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  
19  package org.apache.hadoop.hbase.ipc;
20  
21  import static org.apache.hadoop.fs.CommonConfigurationKeysPublic.HADOOP_SECURITY_AUTHORIZATION;
22  
23  import com.google.common.annotations.VisibleForTesting;
24  import com.google.common.util.concurrent.ThreadFactoryBuilder;
25  import com.google.protobuf.BlockingService;
26  import com.google.protobuf.CodedInputStream;
27  import com.google.protobuf.CodedOutputStream;
28  import com.google.protobuf.Descriptors.MethodDescriptor;
29  import com.google.protobuf.Message;
30  import com.google.protobuf.ServiceException;
31  import com.google.protobuf.TextFormat;
32  
33  import java.io.ByteArrayInputStream;
34  import java.io.ByteArrayOutputStream;
35  import java.io.DataOutputStream;
36  import java.io.IOException;
37  import java.io.InputStream;
38  import java.net.BindException;
39  import java.net.InetAddress;
40  import java.net.InetSocketAddress;
41  import java.net.ServerSocket;
42  import java.net.Socket;
43  import java.net.SocketException;
44  import java.net.UnknownHostException;
45  import java.nio.ByteBuffer;
46  import java.nio.channels.CancelledKeyException;
47  import java.nio.channels.Channels;
48  import java.nio.channels.ClosedChannelException;
49  import java.nio.channels.GatheringByteChannel;
50  import java.nio.channels.ReadableByteChannel;
51  import java.nio.channels.SelectionKey;
52  import java.nio.channels.Selector;
53  import java.nio.channels.ServerSocketChannel;
54  import java.nio.channels.SocketChannel;
55  import java.nio.channels.WritableByteChannel;
56  import java.security.PrivilegedExceptionAction;
57  import java.util.ArrayList;
58  import java.util.Collections;
59  import java.util.HashMap;
60  import java.util.Iterator;
61  import java.util.LinkedList;
62  import java.util.List;
63  import java.util.Map;
64  import java.util.Random;
65  import java.util.Set;
66  import java.util.concurrent.ConcurrentHashMap;
67  import java.util.concurrent.ConcurrentLinkedDeque;
68  import java.util.concurrent.ExecutorService;
69  import java.util.concurrent.Executors;
70  import java.util.concurrent.locks.Lock;
71  import java.util.concurrent.locks.ReentrantLock;
72  
73  import javax.security.sasl.Sasl;
74  import javax.security.sasl.SaslException;
75  import javax.security.sasl.SaslServer;
76  
77  import org.apache.commons.logging.Log;
78  import org.apache.commons.logging.LogFactory;
79  import org.apache.hadoop.conf.Configuration;
80  import org.apache.hadoop.hbase.CallQueueTooBigException;
81  import org.apache.hadoop.hbase.CellScanner;
82  import org.apache.hadoop.hbase.DoNotRetryIOException;
83  import org.apache.hadoop.hbase.HBaseIOException;
84  import org.apache.hadoop.hbase.HBaseInterfaceAudience;
85  import org.apache.hadoop.hbase.HConstants;
86  import org.apache.hadoop.hbase.Server;
87  import org.apache.hadoop.hbase.classification.InterfaceAudience;
88  import org.apache.hadoop.hbase.classification.InterfaceStability;
89  import org.apache.hadoop.hbase.client.NeedUnmanagedConnectionException;
90  import org.apache.hadoop.hbase.client.VersionInfoUtil;
91  import org.apache.hadoop.hbase.codec.Codec;
92  import org.apache.hadoop.hbase.conf.ConfigurationObserver;
93  import org.apache.hadoop.hbase.exceptions.RegionMovedException;
94  import org.apache.hadoop.hbase.exceptions.RequestTooBigException;
95  import org.apache.hadoop.hbase.io.BoundedByteBufferPool;
96  import org.apache.hadoop.hbase.io.ByteBufferInputStream;
97  import org.apache.hadoop.hbase.io.ByteBufferOutputStream;
98  import org.apache.hadoop.hbase.monitoring.MonitoredRPCHandler;
99  import org.apache.hadoop.hbase.monitoring.TaskMonitor;
100 import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
101 import org.apache.hadoop.hbase.protobuf.generated.ClientProtos;
102 import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.VersionInfo;
103 import org.apache.hadoop.hbase.protobuf.generated.RPCProtos.CellBlockMeta;
104 import org.apache.hadoop.hbase.protobuf.generated.RPCProtos.ConnectionHeader;
105 import org.apache.hadoop.hbase.protobuf.generated.RPCProtos.ExceptionResponse;
106 import org.apache.hadoop.hbase.protobuf.generated.RPCProtos.RequestHeader;
107 import org.apache.hadoop.hbase.protobuf.generated.RPCProtos.ResponseHeader;
108 import org.apache.hadoop.hbase.protobuf.generated.RPCProtos.UserInformation;
109 import org.apache.hadoop.hbase.regionserver.RSRpcServices;
110 import org.apache.hadoop.hbase.security.AccessDeniedException;
111 import org.apache.hadoop.hbase.security.AuthMethod;
112 import org.apache.hadoop.hbase.security.HBasePolicyProvider;
113 import org.apache.hadoop.hbase.security.HBaseSaslRpcServer;
114 import org.apache.hadoop.hbase.security.HBaseSaslRpcServer.SaslDigestCallbackHandler;
115 import org.apache.hadoop.hbase.security.HBaseSaslRpcServer.SaslGssCallbackHandler;
116 import org.apache.hadoop.hbase.security.SaslStatus;
117 import org.apache.hadoop.hbase.security.SaslUtil;
118 import org.apache.hadoop.hbase.security.User;
119 import org.apache.hadoop.hbase.security.UserProvider;
120 import org.apache.hadoop.hbase.security.token.AuthenticationTokenSecretManager;
121 import org.apache.hadoop.hbase.util.Bytes;
122 import org.apache.hadoop.hbase.util.Counter;
123 import org.apache.hadoop.hbase.util.Pair;
124 import org.apache.hadoop.hbase.util.Threads;
125 import org.apache.hadoop.io.BytesWritable;
126 import org.apache.hadoop.io.IntWritable;
127 import org.apache.hadoop.io.Writable;
128 import org.apache.hadoop.io.WritableUtils;
129 import org.apache.hadoop.io.compress.CompressionCodec;
130 import org.apache.hadoop.security.UserGroupInformation;
131 import org.apache.hadoop.security.UserGroupInformation.AuthenticationMethod;
132 import org.apache.hadoop.security.authorize.AuthorizationException;
133 import org.apache.hadoop.security.authorize.PolicyProvider;
134 import org.apache.hadoop.security.authorize.ProxyUsers;
135 import org.apache.hadoop.security.authorize.ServiceAuthorizationManager;
136 import org.apache.hadoop.security.token.SecretManager;
137 import org.apache.hadoop.security.token.SecretManager.InvalidToken;
138 import org.apache.hadoop.security.token.TokenIdentifier;
139 import org.apache.hadoop.util.StringUtils;
140 import org.apache.htrace.TraceInfo;
141 import org.codehaus.jackson.map.ObjectMapper;
142 
143 /**
144  * An RPC server that hosts protobuf described Services.
145  *
146  * An RpcServer instance has a Listener that hosts the socket.  Listener has fixed number
147  * of Readers in an ExecutorPool, 10 by default.  The Listener does an accept and then
148  * round robin a Reader is chosen to do the read.  The reader is registered on Selector.  Read does
149  * total read off the channel and the parse from which it makes a Call.  The call is wrapped in a
150  * CallRunner and passed to the scheduler to be run.  Reader goes back to see if more to be done
151  * and loops till done.
152  *
153  * <p>Scheduler can be variously implemented but default simple scheduler has handlers to which it
154  * has given the queues into which calls (i.e. CallRunner instances) are inserted.  Handlers run
155  * taking from the queue.  They run the CallRunner#run method on each item gotten from queue
156  * and keep taking while the server is up.
157  *
158  * CallRunner#run executes the call.  When done, asks the included Call to put itself on new
159  * queue for Responder to pull from and return result to client.
160  */
161 @InterfaceAudience.LimitedPrivate({HBaseInterfaceAudience.COPROC, HBaseInterfaceAudience.PHOENIX})
162 @InterfaceStability.Evolving
163 public class RpcServer implements RpcServerInterface, ConfigurationObserver {
164   // LOG is being used in CallRunner and the log level is being changed in tests
165   public static final Log LOG = LogFactory.getLog(RpcServer.class);
166   private static final CallQueueTooBigException CALL_QUEUE_TOO_BIG_EXCEPTION
167       = new CallQueueTooBigException();
168 
169   private final boolean authorize;
170   private boolean isSecurityEnabled;
171 
172   public static final byte CURRENT_VERSION = 0;
173 
174   /**
175    * Whether we allow a fallback to SIMPLE auth for insecure clients when security is enabled.
176    */
177   public static final String FALLBACK_TO_INSECURE_CLIENT_AUTH =
178           "hbase.ipc.server.fallback-to-simple-auth-allowed";
179 
180   /**
181    * How many calls/handler are allowed in the queue.
182    */
183   static final int DEFAULT_MAX_CALLQUEUE_LENGTH_PER_HANDLER = 10;
184 
185   /**
186    * The maximum size that we can hold in the RPC queue
187    */
188   private static final int DEFAULT_MAX_CALLQUEUE_SIZE = 1024 * 1024 * 1024;
189 
190   private final CellBlockBuilder cellBlockBuilder;
191 
192   private static final String AUTH_FAILED_FOR = "Auth failed for ";
193   private static final String AUTH_SUCCESSFUL_FOR = "Auth successful for ";
194   private static final Log AUDITLOG = LogFactory.getLog("SecurityLogger." +
195     Server.class.getName());
196   protected SecretManager<TokenIdentifier> secretManager;
197   protected ServiceAuthorizationManager authManager;
198 
199   /** This is set to Call object before Handler invokes an RPC and ybdie
200    * after the call returns.
201    */
202   protected static final ThreadLocal<Call> CurCall = new ThreadLocal<Call>();
203 
204   /** Keeps MonitoredRPCHandler per handler thread. */
205   static final ThreadLocal<MonitoredRPCHandler> MONITORED_RPC
206       = new ThreadLocal<MonitoredRPCHandler>();
207 
208   protected final InetSocketAddress bindAddress;
209   protected int port;                             // port we listen on
210   protected InetSocketAddress address;            // inet address we listen on
211   private int readThreads;                        // number of read threads
212   protected int maxIdleTime;                      // the maximum idle time after
213                                                   // which a client may be
214                                                   // disconnected
215   protected int thresholdIdleConnections;         // the number of idle
216                                                   // connections after which we
217                                                   // will start cleaning up idle
218                                                   // connections
219   int maxConnectionsToNuke;                       // the max number of
220                                                   // connections to nuke
221                                                   // during a cleanup
222 
223   protected MetricsHBaseServer metrics;
224 
225   protected final Configuration conf;
226 
227   private int maxQueueSize;
228   protected int socketSendBufferSize;
229   protected final boolean tcpNoDelay;   // if T then disable Nagle's Algorithm
230   protected final boolean tcpKeepAlive; // if T then use keepalives
231   protected final long purgeTimeout;    // in milliseconds
232 
233   /**
234    * This flag is used to indicate to sub threads when they should go down.  When we call
235    * {@link #start()}, all threads started will consult this flag on whether they should
236    * keep going.  It is set to false when {@link #stop()} is called.
237    */
238   volatile boolean running = true;
239 
240   /**
241    * This flag is set to true after all threads are up and 'running' and the server is then opened
242    * for business by the call to {@link #start()}.
243    */
244   volatile boolean started = false;
245 
246   /**
247    * This is a running count of the size of all outstanding calls by size.
248    */
249   protected final Counter callQueueSize = new Counter();
250 
251   protected final List<Connection> connectionList =
252     Collections.synchronizedList(new LinkedList<Connection>());
253   //maintain a list
254   //of client connections
255   private Listener listener = null;
256   protected Responder responder = null;
257   protected AuthenticationTokenSecretManager authTokenSecretMgr = null;
258   protected int numConnections = 0;
259 
260   protected HBaseRPCErrorHandler errorHandler = null;
261 
262   public static final String MAX_REQUEST_SIZE = "hbase.ipc.max.request.size";
263   private static final RequestTooBigException REQUEST_TOO_BIG_EXCEPTION =
264       new RequestTooBigException();
265 
266   private static final String WARN_RESPONSE_TIME = "hbase.ipc.warn.response.time";
267   private static final String WARN_RESPONSE_SIZE = "hbase.ipc.warn.response.size";
268 
269 
270   /**
271    * Minimum allowable timeout (in milliseconds) in rpc request's header. This
272    * configuration exists to prevent the rpc service regarding this request as timeout immediately.
273    */
274   private static final String MIN_CLIENT_REQUEST_TIMEOUT = "hbase.ipc.min.client.request.timeout";
275   private static final int DEFAULT_MIN_CLIENT_REQUEST_TIMEOUT = 20;
276 
277   /** Default value for above params */
278   public static final int DEFAULT_MAX_REQUEST_SIZE = DEFAULT_MAX_CALLQUEUE_SIZE / 4; // 256M
279   private static final int DEFAULT_WARN_RESPONSE_TIME = 10000; // milliseconds
280   private static final int DEFAULT_WARN_RESPONSE_SIZE = 100 * 1024 * 1024;
281 
282   private static final ObjectMapper MAPPER = new ObjectMapper();
283 
284   protected static final int DEFAULT_TRACE_LOG_MAX_LENGTH = 1000;
285   protected static final String TRACE_LOG_MAX_LENGTH = "hbase.ipc.trace.log.max.length";
286   protected static final String KEY_WORD_TRUNCATED = " <TRUNCATED>";
287 
288   private final int maxRequestSize;
289   private final int warnResponseTime;
290   private final int warnResponseSize;
291 
292   private final int minClientRequestTimeout;
293 
294   private final Server server;
295   private final List<BlockingServiceAndInterface> services;
296 
297   private final RpcScheduler scheduler;
298 
299   private UserProvider userProvider;
300 
301   private final BoundedByteBufferPool reservoir;
302 
303   private volatile boolean allowFallbackToSimpleAuth;
304 
305   /**
306    * Used to get details for scan with a scanner_id<br/>
307    * TODO try to figure out a better way and remove reference from regionserver package later.
308    */
309   private RSRpcServices rsRpcServices;
310 
311   /**
312    * Datastructure that holds all necessary to a method invocation and then afterward, carries
313    * the result.
314    */
315   @InterfaceAudience.LimitedPrivate({HBaseInterfaceAudience.COPROC, HBaseInterfaceAudience.PHOENIX})
316   @InterfaceStability.Evolving
317   public class Call implements RpcCallContext {
318     protected int id;                             // the client's call id
319     protected BlockingService service;
320     protected MethodDescriptor md;
321     protected RequestHeader header;
322     protected Message param;                      // the parameter passed
323     // Optional cell data passed outside of protobufs.
324     protected CellScanner cellScanner;
325     protected Connection connection;              // connection to client
326     protected long timestamp;      // the time received when response is null
327                                    // the time served when response is not null
328     protected int timeout;
329     protected long startTime;
330     protected long deadline;// the deadline to handle this call, if exceed we can drop it.
331 
332     /**
333      * Chain of buffers to send as response.
334      */
335     protected BufferChain response;
336     protected Responder responder;
337 
338     protected long size;                          // size of current call
339     protected boolean isError;
340     protected TraceInfo tinfo;
341     private ByteBuffer cellBlock = null;
342 
343     private User user;
344     private InetAddress remoteAddress;
345 
346     private long responseCellSize = 0;
347     private long responseBlockSize = 0;
348     // cumulative size of serialized exceptions
349     private long exceptionSize = 0;
350     private boolean retryImmediatelySupported;
351 
352     @edu.umd.cs.findbugs.annotations.SuppressWarnings(value="NP_NULL_ON_SOME_PATH",
353         justification="Can't figure why this complaint is happening... see below")
354     Call(int id, final BlockingService service, final MethodDescriptor md, RequestHeader header,
355          Message param, CellScanner cellScanner, Connection connection, Responder responder,
356          long size, TraceInfo tinfo, final InetAddress remoteAddress, int timeout) {
357       this.id = id;
358       this.service = service;
359       this.md = md;
360       this.header = header;
361       this.param = param;
362       this.cellScanner = cellScanner;
363       this.connection = connection;
364       this.timestamp = System.currentTimeMillis();
365       this.response = null;
366       this.responder = responder;
367       this.isError = false;
368       this.size = size;
369       this.tinfo = tinfo;
370       this.user = connection == null? null: connection.user; // FindBugs: NP_NULL_ON_SOME_PATH
371       this.remoteAddress = remoteAddress;
372       this.retryImmediatelySupported =
373           connection == null? null: connection.retryImmediatelySupported;
374       this.timeout = timeout;
375       this.deadline = this.timeout > 0 ? this.timestamp + this.timeout : Long.MAX_VALUE;
376     }
377 
378     /**
379      * Call is done. Execution happened and we returned results to client. It is now safe to
380      * cleanup.
381      */
382     @edu.umd.cs.findbugs.annotations.SuppressWarnings(value="IS2_INCONSISTENT_SYNC",
383         justification="Presume the lock on processing request held by caller is protection enough")
384     void done() {
385       if (this.cellBlock != null && reservoir != null) {
386         // Return buffer to reservoir now we are done with it.
387         reservoir.putBuffer(this.cellBlock);
388         this.cellBlock = null;
389       }
390       this.connection.decRpcCount();  // Say that we're done with this call.
391     }
392 
393     @Override
394     public String toString() {
395       return toShortString() + " param: " +
396         (this.param != null? ProtobufUtil.getShortTextFormat(this.param): "") +
397         " connection: " + connection.toString();
398     }
399 
400     protected RequestHeader getHeader() {
401       return this.header;
402     }
403 
404     public boolean hasPriority() {
405       return this.header.hasPriority();
406     }
407 
408     public int getPriority() {
409       return this.header.getPriority();
410     }
411 
412     /*
413      * Short string representation without param info because param itself could be huge depends on
414      * the payload of a command
415      */
416     String toShortString() {
417       String serviceName = this.connection.service != null ?
418           this.connection.service.getDescriptorForType().getName() : "null";
419       return "callId: " + this.id + " service: " + serviceName +
420           " methodName: " + ((this.md != null) ? this.md.getName() : "n/a") +
421           " size: " + StringUtils.TraditionalBinaryPrefix.long2String(this.size, "", 1) +
422           " connection: " + connection.toString() +
423           " deadline: " + deadline;
424     }
425 
426     String toTraceString() {
427       String serviceName = this.connection.service != null ?
428                            this.connection.service.getDescriptorForType().getName() : "";
429       String methodName = (this.md != null) ? this.md.getName() : "";
430       return serviceName + "." + methodName;
431     }
432 
433     protected synchronized void setSaslTokenResponse(ByteBuffer response) {
434       this.response = new BufferChain(response);
435     }
436 
437     protected synchronized void setResponse(Object m, final CellScanner cells,
438         Throwable t, String errorMsg) {
439       if (this.isError) return;
440       if (t != null) this.isError = true;
441       BufferChain bc = null;
442       try {
443         ResponseHeader.Builder headerBuilder = ResponseHeader.newBuilder();
444         // Presume it a pb Message.  Could be null.
445         Message result = (Message)m;
446         // Call id.
447         headerBuilder.setCallId(this.id);
448         if (t != null) {
449           ExceptionResponse.Builder exceptionBuilder = ExceptionResponse.newBuilder();
450           exceptionBuilder.setExceptionClassName(t.getClass().getName());
451           exceptionBuilder.setStackTrace(errorMsg);
452           exceptionBuilder.setDoNotRetry(t instanceof DoNotRetryIOException ||
453             t instanceof NeedUnmanagedConnectionException);
454           if (t instanceof RegionMovedException) {
455             // Special casing for this exception.  This is only one carrying a payload.
456             // Do this instead of build a generic system for allowing exceptions carry
457             // any kind of payload.
458             RegionMovedException rme = (RegionMovedException)t;
459             exceptionBuilder.setHostname(rme.getHostname());
460             exceptionBuilder.setPort(rme.getPort());
461           }
462           // Set the exception as the result of the method invocation.
463           headerBuilder.setException(exceptionBuilder.build());
464         }
465         // Pass reservoir to buildCellBlock. Keep reference to returne so can add it back to the
466         // reservoir when finished. This is hacky and the hack is not contained but benefits are
467         // high when we can avoid a big buffer allocation on each rpc.
468         this.cellBlock = cellBlockBuilder.buildCellBlock(this.connection.codec,
469           this.connection.compressionCodec, cells, reservoir);
470         if (this.cellBlock != null) {
471           CellBlockMeta.Builder cellBlockBuilder = CellBlockMeta.newBuilder();
472           // Presumes the cellBlock bytebuffer has been flipped so limit has total size in it.
473           cellBlockBuilder.setLength(this.cellBlock.limit());
474           headerBuilder.setCellBlockMeta(cellBlockBuilder.build());
475         }
476         Message header = headerBuilder.build();
477 
478         byte[] b = createHeaderAndMessageBytes(result, header);
479 
480         bc = new BufferChain(ByteBuffer.wrap(b), this.cellBlock);
481 
482         if (connection.useWrap) {
483           bc = wrapWithSasl(bc);
484         }
485       } catch (IOException e) {
486         LOG.warn("Exception while creating response " + e);
487       }
488       this.response = bc;
489     }
490 
491     private byte[] createHeaderAndMessageBytes(Message result, Message header)
492         throws IOException {
493       // Organize the response as a set of bytebuffers rather than collect it all together inside
494       // one big byte array; save on allocations.
495       int headerSerializedSize = 0, resultSerializedSize = 0, headerVintSize = 0,
496           resultVintSize = 0;
497       if (header != null) {
498         headerSerializedSize = header.getSerializedSize();
499         headerVintSize = CodedOutputStream.computeRawVarint32Size(headerSerializedSize);
500       }
501       if (result != null) {
502         resultSerializedSize = result.getSerializedSize();
503         resultVintSize = CodedOutputStream.computeRawVarint32Size(resultSerializedSize);
504       }
505       // calculate the total size
506       int totalSize = headerSerializedSize + headerVintSize
507           + (resultSerializedSize + resultVintSize)
508           + (this.cellBlock == null ? 0 : this.cellBlock.limit());
509       // The byte[] should also hold the totalSize of the header, message and the cellblock
510       byte[] b = new byte[headerSerializedSize + headerVintSize + resultSerializedSize
511           + resultVintSize + Bytes.SIZEOF_INT];
512       // The RpcClient expects the int to be in a format that code be decoded by
513       // the DataInputStream#readInt(). Hence going with the Bytes.toBytes(int)
514       // form of writing int.
515       Bytes.putInt(b, 0, totalSize);
516       CodedOutputStream cos = CodedOutputStream.newInstance(b, Bytes.SIZEOF_INT,
517           b.length - Bytes.SIZEOF_INT);
518       if (header != null) {
519         cos.writeMessageNoTag(header);
520       }
521       if (result != null) {
522         cos.writeMessageNoTag(result);
523       }
524       cos.flush();
525       cos.checkNoSpaceLeft();
526       return b;
527     }
528 
529     private BufferChain wrapWithSasl(BufferChain bc)
530         throws IOException {
531       if (!this.connection.useSasl) return bc;
532       // Looks like no way around this; saslserver wants a byte array.  I have to make it one.
533       // THIS IS A BIG UGLY COPY.
534       byte [] responseBytes = bc.getBytes();
535       byte [] token;
536       // synchronization may be needed since there can be multiple Handler
537       // threads using saslServer to wrap responses.
538       synchronized (connection.saslServer) {
539         token = connection.saslServer.wrap(responseBytes, 0, responseBytes.length);
540       }
541       if (LOG.isTraceEnabled()) {
542         LOG.trace("Adding saslServer wrapped token of size " + token.length
543             + " as call response.");
544       }
545 
546       ByteBuffer bbTokenLength = ByteBuffer.wrap(Bytes.toBytes(token.length));
547       ByteBuffer bbTokenBytes = ByteBuffer.wrap(token);
548       return new BufferChain(bbTokenLength, bbTokenBytes);
549     }
550 
551     @Override
552     public boolean isClientCellBlockSupported() {
553       return this.connection != null && this.connection.codec != null;
554     }
555 
556     @Override
557     public long disconnectSince() {
558       if (!connection.channel.isOpen()) {
559         return System.currentTimeMillis() - timestamp;
560       } else {
561         return -1L;
562       }
563     }
564 
565     @Override
566     public long getResponseExceptionSize() {
567       return exceptionSize;
568     }
569     @Override
570     public void incrementResponseExceptionSize(long exSize) {
571       exceptionSize += exSize;
572     }
573 
574     public long getSize() {
575       return this.size;
576     }
577 
578     @Override
579     public long getResponseCellSize() {
580       return responseCellSize;
581     }
582 
583     @Override
584     public void incrementResponseCellSize(long cellSize) {
585       responseCellSize += cellSize;
586     }
587 
588     @Override
589     public long getResponseBlockSize() {
590       return responseBlockSize;
591     }
592 
593     @Override
594     public void incrementResponseBlockSize(long blockSize) {
595       responseBlockSize += blockSize;
596     }
597 
598     @Override
599     public long getDeadline() {
600       return deadline;
601     }
602 
603     public synchronized void sendResponseIfReady() throws IOException {
604       // set param null to reduce memory pressure
605       this.param = null;
606       this.responder.doRespond(this);
607     }
608 
609     public UserGroupInformation getRemoteUser() {
610       return connection.ugi;
611     }
612 
613     @Override
614     public User getRequestUser() {
615       return user;
616     }
617 
618     @Override
619     public String getRequestUserName() {
620       User user = getRequestUser();
621       return user == null? null: user.getShortName();
622     }
623 
624     @Override
625     public InetAddress getRemoteAddress() {
626       return remoteAddress;
627     }
628 
629     @Override
630     public VersionInfo getClientVersionInfo() {
631       return connection.getVersionInfo();
632     }
633 
634     @Override
635     public boolean isRetryImmediatelySupported() {
636       return retryImmediatelySupported;
637     }
638   }
639 
640   /** Listens on the socket. Creates jobs for the handler threads*/
641   private class Listener extends Thread {
642 
643     private ServerSocketChannel acceptChannel = null; //the accept channel
644     private Selector selector = null; //the selector that we use for the server
645     private Reader[] readers = null;
646     private int currentReader = 0;
647     private Random rand = new Random();
648     private long lastCleanupRunTime = 0; //the last time when a cleanup connec-
649                                          //-tion (for idle connections) ran
650     private long cleanupInterval = 10000; //the minimum interval between
651                                           //two cleanup runs
652     private int backlogLength;
653 
654     private ExecutorService readPool;
655 
656     public Listener(final String name) throws IOException {
657       super(name);
658       backlogLength = conf.getInt("hbase.ipc.server.listen.queue.size", 128);
659       // Create a new server socket and set to non blocking mode
660       acceptChannel = ServerSocketChannel.open();
661       acceptChannel.configureBlocking(false);
662 
663       // Bind the server socket to the binding addrees (can be different from the default interface)
664       bind(acceptChannel.socket(), bindAddress, backlogLength);
665       port = acceptChannel.socket().getLocalPort(); //Could be an ephemeral port
666       address = (InetSocketAddress)acceptChannel.socket().getLocalSocketAddress();
667 
668       // create a selector;
669       selector= Selector.open();
670 
671       readers = new Reader[readThreads];
672       readPool = Executors.newFixedThreadPool(readThreads,
673         new ThreadFactoryBuilder().setNameFormat(
674           "RpcServer.reader=%d,bindAddress=" + bindAddress.getHostName() +
675           ",port=" + port).setDaemon(true)
676         .setUncaughtExceptionHandler(Threads.LOGGING_EXCEPTION_HANDLER).build());
677       for (int i = 0; i < readThreads; ++i) {
678         Reader reader = new Reader();
679         readers[i] = reader;
680         readPool.execute(reader);
681       }
682       LOG.info(getName() + ": started " + readThreads + " reader(s) listening on port=" + port);
683 
684       // Register accepts on the server socket with the selector.
685       acceptChannel.register(selector, SelectionKey.OP_ACCEPT);
686       this.setName("RpcServer.listener,port=" + port);
687       this.setDaemon(true);
688     }
689 
690 
691     private class Reader implements Runnable {
692       private volatile boolean adding = false;
693       private final Selector readSelector;
694 
695       Reader() throws IOException {
696         this.readSelector = Selector.open();
697       }
698       @Override
699       public void run() {
700         try {
701           doRunLoop();
702         } finally {
703           try {
704             readSelector.close();
705           } catch (IOException ioe) {
706             LOG.error(getName() + ": error closing read selector in " + getName(), ioe);
707           }
708         }
709       }
710 
711       private synchronized void doRunLoop() {
712         while (running) {
713           try {
714             readSelector.select();
715             while (adding) {
716               this.wait(1000);
717             }
718 
719             Iterator<SelectionKey> iter = readSelector.selectedKeys().iterator();
720             while (iter.hasNext()) {
721               SelectionKey key = iter.next();
722               iter.remove();
723               if (key.isValid()) {
724                 if (key.isReadable()) {
725                   doRead(key);
726                 }
727               }
728             }
729           } catch (InterruptedException e) {
730             LOG.debug("Interrupted while sleeping");
731           } catch (CancelledKeyException e) {
732             LOG.error(getName() + ": CancelledKeyException in Reader", e);
733           } catch (IOException ex) {
734             LOG.info(getName() + ": IOException in Reader", ex);
735           }
736         }
737       }
738 
739       /**
740        * This gets reader into the state that waits for the new channel
741        * to be registered with readSelector. If it was waiting in select()
742        * the thread will be woken up, otherwise whenever select() is called
743        * it will return even if there is nothing to read and wait
744        * in while(adding) for finishAdd call
745        */
746       public void startAdd() {
747         adding = true;
748         readSelector.wakeup();
749       }
750 
751       public synchronized SelectionKey registerChannel(SocketChannel channel)
752         throws IOException {
753         return channel.register(readSelector, SelectionKey.OP_READ);
754       }
755 
756       public synchronized void finishAdd() {
757         adding = false;
758         this.notify();
759       }
760     }
761 
762     /** cleanup connections from connectionList. Choose a random range
763      * to scan and also have a limit on the number of the connections
764      * that will be cleanedup per run. The criteria for cleanup is the time
765      * for which the connection was idle. If 'force' is true then all
766      * connections will be looked at for the cleanup.
767      * @param force all connections will be looked at for cleanup
768      */
769     private void cleanupConnections(boolean force) {
770       if (force || numConnections > thresholdIdleConnections) {
771         long currentTime = System.currentTimeMillis();
772         if (!force && (currentTime - lastCleanupRunTime) < cleanupInterval) {
773           return;
774         }
775         int start = 0;
776         int end = numConnections - 1;
777         if (!force) {
778           start = rand.nextInt(numConnections);
779           end = rand.nextInt(numConnections);
780           int temp;
781           if (end < start) {
782             temp = start;
783             start = end;
784             end = temp;
785           }
786         }
787         int i = start;
788         int numNuked = 0;
789         while (i <= end) {
790           Connection c;
791           synchronized (connectionList) {
792             try {
793               c = connectionList.get(i);
794             } catch (Exception e) {return;}
795           }
796           if (c.timedOut(currentTime)) {
797             if (LOG.isDebugEnabled())
798               LOG.debug(getName() + ": disconnecting client " + c.getHostAddress());
799             closeConnection(c);
800             numNuked++;
801             end--;
802             //noinspection UnusedAssignment
803             c = null;
804             if (!force && numNuked == maxConnectionsToNuke) break;
805           }
806           else i++;
807         }
808         lastCleanupRunTime = System.currentTimeMillis();
809       }
810     }
811 
812     @Override
813     @edu.umd.cs.findbugs.annotations.SuppressWarnings(value="IS2_INCONSISTENT_SYNC",
814       justification="selector access is not synchronized; seems fine but concerned changing " +
815         "it will have per impact")
816     public void run() {
817       LOG.info(getName() + ": starting");
818       while (running) {
819         SelectionKey key = null;
820         try {
821           selector.select(); // FindBugs IS2_INCONSISTENT_SYNC
822           Iterator<SelectionKey> iter = selector.selectedKeys().iterator();
823           while (iter.hasNext()) {
824             key = iter.next();
825             iter.remove();
826             try {
827               if (key.isValid()) {
828                 if (key.isAcceptable())
829                   doAccept(key);
830               }
831             } catch (IOException ignored) {
832               if (LOG.isTraceEnabled()) LOG.trace("ignored", ignored);
833             }
834             key = null;
835           }
836         } catch (OutOfMemoryError e) {
837           if (errorHandler != null) {
838             if (errorHandler.checkOOME(e)) {
839               LOG.info(getName() + ": exiting on OutOfMemoryError");
840               closeCurrentConnection(key, e);
841               cleanupConnections(true);
842               return;
843             }
844           } else {
845             // we can run out of memory if we have too many threads
846             // log the event and sleep for a minute and give
847             // some thread(s) a chance to finish
848             LOG.warn(getName() + ": OutOfMemoryError in server select", e);
849             closeCurrentConnection(key, e);
850             cleanupConnections(true);
851             try {
852               Thread.sleep(60000);
853             } catch (InterruptedException ex) {
854               LOG.debug("Interrupted while sleeping");
855               return;
856             }
857           }
858         } catch (Exception e) {
859           closeCurrentConnection(key, e);
860         }
861         cleanupConnections(false);
862       }
863 
864       LOG.info(getName() + ": stopping");
865 
866       synchronized (this) {
867         try {
868           acceptChannel.close();
869           selector.close();
870         } catch (IOException ignored) {
871           if (LOG.isTraceEnabled()) LOG.trace("ignored", ignored);
872         }
873 
874         selector= null;
875         acceptChannel= null;
876 
877         // clean up all connections
878         while (!connectionList.isEmpty()) {
879           closeConnection(connectionList.remove(0));
880         }
881       }
882     }
883 
884     private void closeCurrentConnection(SelectionKey key, Throwable e) {
885       if (key != null) {
886         Connection c = (Connection)key.attachment();
887         if (c != null) {
888           if (LOG.isDebugEnabled()) {
889             LOG.debug(getName() + ": disconnecting client " + c.getHostAddress() +
890                 (e != null ? " on error " + e.getMessage() : ""));
891           }
892           closeConnection(c);
893           key.attach(null);
894         }
895       }
896     }
897 
898     InetSocketAddress getAddress() {
899       return address;
900     }
901 
902     void doAccept(SelectionKey key) throws IOException, OutOfMemoryError {
903       Connection c;
904       ServerSocketChannel server = (ServerSocketChannel) key.channel();
905 
906       SocketChannel channel;
907       while ((channel = server.accept()) != null) {
908         try {
909           channel.configureBlocking(false);
910           channel.socket().setTcpNoDelay(tcpNoDelay);
911           channel.socket().setKeepAlive(tcpKeepAlive);
912         } catch (IOException ioe) {
913           channel.close();
914           throw ioe;
915         }
916 
917         Reader reader = getReader();
918         try {
919           reader.startAdd();
920           SelectionKey readKey = reader.registerChannel(channel);
921           c = getConnection(channel, System.currentTimeMillis());
922           readKey.attach(c);
923           synchronized (connectionList) {
924             connectionList.add(numConnections, c);
925             numConnections++;
926           }
927           if (LOG.isDebugEnabled())
928             LOG.debug(getName() + ": connection from " + c.toString() +
929                 "; # active connections: " + numConnections);
930         } finally {
931           reader.finishAdd();
932         }
933       }
934     }
935 
936     void doRead(SelectionKey key) throws InterruptedException {
937       int count;
938       Connection c = (Connection) key.attachment();
939       if (c == null) {
940         return;
941       }
942       c.setLastContact(System.currentTimeMillis());
943       try {
944         count = c.readAndProcess();
945 
946         if (count > 0) {
947           c.setLastContact(System.currentTimeMillis());
948         }
949 
950       } catch (InterruptedException ieo) {
951         throw ieo;
952       } catch (Exception e) {
953         if (LOG.isDebugEnabled()) {
954           LOG.debug(getName() + ": Caught exception while reading:", e);
955         }
956         count = -1; //so that the (count < 0) block is executed
957       }
958       if (count < 0) {
959         if (LOG.isDebugEnabled()) {
960           LOG.debug(getName() + ": DISCONNECTING client " + c.toString() +
961               " because read count=" + count +
962               ". Number of active connections: " + numConnections);
963         }
964         closeConnection(c);
965       }
966     }
967 
968     synchronized void doStop() {
969       if (selector != null) {
970         selector.wakeup();
971         Thread.yield();
972       }
973       if (acceptChannel != null) {
974         try {
975           acceptChannel.socket().close();
976         } catch (IOException e) {
977           LOG.info(getName() + ": exception in closing listener socket. " + e);
978         }
979       }
980       readPool.shutdownNow();
981     }
982 
983     // The method that will return the next reader to work with
984     // Simplistic implementation of round robin for now
985     Reader getReader() {
986       currentReader = (currentReader + 1) % readers.length;
987       return readers[currentReader];
988     }
989   }
990 
991   // Sends responses of RPC back to clients.
992   protected class Responder extends Thread {
993     private final Selector writeSelector;
994     private final Set<Connection> writingCons =
995         Collections.newSetFromMap(new ConcurrentHashMap<Connection, Boolean>());
996 
997     Responder() throws IOException {
998       this.setName("RpcServer.responder");
999       this.setDaemon(true);
1000       this.setUncaughtExceptionHandler(Threads.LOGGING_EXCEPTION_HANDLER);
1001       writeSelector = Selector.open(); // create a selector
1002     }
1003 
1004     @Override
1005     public void run() {
1006       LOG.info(getName() + ": starting");
1007       try {
1008         doRunLoop();
1009       } finally {
1010         LOG.info(getName() + ": stopping");
1011         try {
1012           writeSelector.close();
1013         } catch (IOException ioe) {
1014           LOG.error(getName() + ": couldn't close write selector", ioe);
1015         }
1016       }
1017     }
1018 
1019     /**
1020      * Take the list of the connections that want to write, and register them
1021      * in the selector.
1022      */
1023     private void registerWrites() {
1024       Iterator<Connection> it = writingCons.iterator();
1025       while (it.hasNext()) {
1026         Connection c = it.next();
1027         it.remove();
1028         SelectionKey sk = c.channel.keyFor(writeSelector);
1029         try {
1030           if (sk == null) {
1031             try {
1032               c.channel.register(writeSelector, SelectionKey.OP_WRITE, c);
1033             } catch (ClosedChannelException e) {
1034               // ignore: the client went away.
1035               if (LOG.isTraceEnabled()) LOG.trace("ignored", e);
1036             }
1037           } else {
1038             sk.interestOps(SelectionKey.OP_WRITE);
1039           }
1040         } catch (CancelledKeyException e) {
1041           // ignore: the client went away.
1042           if (LOG.isTraceEnabled()) LOG.trace("ignored", e);
1043         }
1044       }
1045     }
1046 
1047     /**
1048      * Add a connection to the list that want to write,
1049      */
1050     public void registerForWrite(Connection c) {
1051       if (writingCons.add(c)) {
1052         writeSelector.wakeup();
1053       }
1054     }
1055 
1056     private void doRunLoop() {
1057       long lastPurgeTime = 0;   // last check for old calls.
1058       while (running) {
1059         try {
1060           registerWrites();
1061           int keyCt = writeSelector.select(purgeTimeout);
1062           if (keyCt == 0) {
1063             continue;
1064           }
1065 
1066           Set<SelectionKey> keys = writeSelector.selectedKeys();
1067           Iterator<SelectionKey> iter = keys.iterator();
1068           while (iter.hasNext()) {
1069             SelectionKey key = iter.next();
1070             iter.remove();
1071             try {
1072               if (key.isValid() && key.isWritable()) {
1073                 doAsyncWrite(key);
1074               }
1075             } catch (IOException e) {
1076               LOG.debug(getName() + ": asyncWrite", e);
1077             }
1078           }
1079 
1080           lastPurgeTime = purge(lastPurgeTime);
1081 
1082         } catch (OutOfMemoryError e) {
1083           if (errorHandler != null) {
1084             if (errorHandler.checkOOME(e)) {
1085               LOG.info(getName() + ": exiting on OutOfMemoryError");
1086               return;
1087             }
1088           } else {
1089             //
1090             // we can run out of memory if we have too many threads
1091             // log the event and sleep for a minute and give
1092             // some thread(s) a chance to finish
1093             //
1094             LOG.warn(getName() + ": OutOfMemoryError in server select", e);
1095             try {
1096               Thread.sleep(60000);
1097             } catch (InterruptedException ex) {
1098               LOG.debug("Interrupted while sleeping");
1099               return;
1100             }
1101           }
1102         } catch (Exception e) {
1103           LOG.warn(getName() + ": exception in Responder " +
1104               StringUtils.stringifyException(e), e);
1105         }
1106       }
1107       LOG.info(getName() + ": stopped");
1108     }
1109 
1110     /**
1111      * If there were some calls that have not been sent out for a
1112      * long time, we close the connection.
1113      * @return the time of the purge.
1114      */
1115     private long purge(long lastPurgeTime) {
1116       long now = System.currentTimeMillis();
1117       if (now < lastPurgeTime + purgeTimeout) {
1118         return lastPurgeTime;
1119       }
1120 
1121       ArrayList<Connection> conWithOldCalls = new ArrayList<Connection>();
1122       // get the list of channels from list of keys.
1123       synchronized (writeSelector.keys()) {
1124         for (SelectionKey key : writeSelector.keys()) {
1125           Connection connection = (Connection) key.attachment();
1126           if (connection == null) {
1127             throw new IllegalStateException("Coding error: SelectionKey key without attachment.");
1128           }
1129           Call call = connection.responseQueue.peekFirst();
1130           if (call != null && now > call.timestamp + purgeTimeout) {
1131             conWithOldCalls.add(call.connection);
1132           }
1133         }
1134       }
1135 
1136       // Seems safer to close the connection outside of the synchronized loop...
1137       for (Connection connection : conWithOldCalls) {
1138         closeConnection(connection);
1139       }
1140 
1141       return now;
1142     }
1143 
1144     private void doAsyncWrite(SelectionKey key) throws IOException {
1145       Connection connection = (Connection) key.attachment();
1146       if (connection == null) {
1147         throw new IOException("doAsyncWrite: no connection");
1148       }
1149       if (key.channel() != connection.channel) {
1150         throw new IOException("doAsyncWrite: bad channel");
1151       }
1152 
1153       if (processAllResponses(connection)) {
1154         try {
1155           // We wrote everything, so we don't need to be told when the socket is ready for
1156           //  write anymore.
1157          key.interestOps(0);
1158         } catch (CancelledKeyException e) {
1159           /* The Listener/reader might have closed the socket.
1160            * We don't explicitly cancel the key, so not sure if this will
1161            * ever fire.
1162            * This warning could be removed.
1163            */
1164           LOG.warn("Exception while changing ops : " + e);
1165         }
1166       }
1167     }
1168 
1169     /**
1170      * Process the response for this call. You need to have the lock on
1171      * {@link org.apache.hadoop.hbase.ipc.RpcServer.Connection#responseWriteLock}
1172      *
1173      * @param call the call
1174      * @return true if we proceed the call fully, false otherwise.
1175      * @throws IOException
1176      */
1177     private boolean processResponse(final Call call) throws IOException {
1178       boolean error = true;
1179       try {
1180         // Send as much data as we can in the non-blocking fashion
1181         long numBytes = channelWrite(call.connection.channel, call.response);
1182         if (numBytes < 0) {
1183           throw new HBaseIOException("Error writing on the socket " +
1184             "for the call:" + call.toShortString());
1185         }
1186         error = false;
1187       } finally {
1188         if (error) {
1189           LOG.debug(getName() + call.toShortString() + ": output error -- closing");
1190           closeConnection(call.connection);
1191         }
1192       }
1193 
1194       if (!call.response.hasRemaining()) {
1195         call.done();
1196         return true;
1197       } else {
1198         return false; // Socket can't take more, we will have to come back.
1199       }
1200     }
1201 
1202     /**
1203      * Process all the responses for this connection
1204      *
1205      * @return true if all the calls were processed or that someone else is doing it.
1206      * false if there * is still some work to do. In this case, we expect the caller to
1207      * delay us.
1208      * @throws IOException
1209      */
1210     private boolean processAllResponses(final Connection connection) throws IOException {
1211       // We want only one writer on the channel for a connection at a time.
1212       connection.responseWriteLock.lock();
1213       try {
1214         for (int i = 0; i < 20; i++) {
1215           // protection if some handlers manage to need all the responder
1216           Call call = connection.responseQueue.pollFirst();
1217           if (call == null) {
1218             return true;
1219           }
1220           if (!processResponse(call)) {
1221             connection.responseQueue.addFirst(call);
1222             return false;
1223           }
1224         }
1225       } finally {
1226         connection.responseWriteLock.unlock();
1227       }
1228 
1229       return connection.responseQueue.isEmpty();
1230     }
1231 
1232     //
1233     // Enqueue a response from the application.
1234     //
1235     void doRespond(Call call) throws IOException {
1236       boolean added = false;
1237 
1238       // If there is already a write in progress, we don't wait. This allows to free the handlers
1239       //  immediately for other tasks.
1240       if (call.connection.responseQueue.isEmpty() && call.connection.responseWriteLock.tryLock()) {
1241         try {
1242           if (call.connection.responseQueue.isEmpty()) {
1243             // If we're alone, we can try to do a direct call to the socket. It's
1244             //  an optimisation to save on context switches and data transfer between cores..
1245             if (processResponse(call)) {
1246               return; // we're done.
1247             }
1248             // Too big to fit, putting ahead.
1249             call.connection.responseQueue.addFirst(call);
1250             added = true; // We will register to the selector later, outside of the lock.
1251           }
1252         } finally {
1253           call.connection.responseWriteLock.unlock();
1254         }
1255       }
1256 
1257       if (!added) {
1258         call.connection.responseQueue.addLast(call);
1259       }
1260       call.responder.registerForWrite(call.connection);
1261 
1262       // set the serve time when the response has to be sent later
1263       call.timestamp = System.currentTimeMillis();
1264     }
1265   }
1266 
1267   /** Reads calls from a connection and queues them for handling. */
1268   @edu.umd.cs.findbugs.annotations.SuppressWarnings(
1269       value="VO_VOLATILE_INCREMENT",
1270       justification="False positive according to http://sourceforge.net/p/findbugs/bugs/1032/")
1271   public class Connection {
1272     // If initial preamble with version and magic has been read or not.
1273     private boolean connectionPreambleRead = false;
1274     // If the connection header has been read or not.
1275     private boolean connectionHeaderRead = false;
1276     protected SocketChannel channel;
1277     private ByteBuffer data;
1278     private ByteBuffer dataLengthBuffer;
1279     private ByteBuffer preambleBuffer;
1280     protected final ConcurrentLinkedDeque<Call> responseQueue = new ConcurrentLinkedDeque<Call>();
1281     private final Lock responseWriteLock = new ReentrantLock();
1282     private Counter rpcCount = new Counter(); // number of outstanding rpcs
1283     private long lastContact;
1284     private InetAddress addr;
1285     protected Socket socket;
1286     // Cache the remote host & port info so that even if the socket is
1287     // disconnected, we can say where it used to connect to.
1288     protected String hostAddress;
1289     protected int remotePort;
1290     ConnectionHeader connectionHeader;
1291 
1292     /**
1293      * Codec the client asked use.
1294      */
1295     private Codec codec;
1296     /**
1297      * Compression codec the client asked us use.
1298      */
1299     private CompressionCodec compressionCodec;
1300     BlockingService service;
1301 
1302     private AuthMethod authMethod;
1303     private boolean saslContextEstablished;
1304     private boolean skipInitialSaslHandshake;
1305     private ByteBuffer unwrappedData;
1306     // When is this set?  FindBugs wants to know!  Says NP
1307     private ByteBuffer unwrappedDataLengthBuffer = ByteBuffer.allocate(4);
1308     boolean useSasl;
1309     SaslServer saslServer;
1310     private boolean useWrap = false;
1311     // Fake 'call' for failed authorization response
1312     private static final int AUTHORIZATION_FAILED_CALLID = -1;
1313     private final Call authFailedCall = new Call(AUTHORIZATION_FAILED_CALLID, null, null, null,
1314         null, null, this, null, 0, null, null, 0);
1315     private ByteArrayOutputStream authFailedResponse =
1316         new ByteArrayOutputStream();
1317     // Fake 'call' for SASL context setup
1318     private static final int SASL_CALLID = -33;
1319     private final Call saslCall = new Call(SASL_CALLID, null, null, null, null, null, this, null,
1320         0, null, null, 0);
1321 
1322     // was authentication allowed with a fallback to simple auth
1323     private boolean authenticatedWithFallback;
1324 
1325     private boolean retryImmediatelySupported = false;
1326 
1327     public UserGroupInformation attemptingUser = null; // user name before auth
1328     protected User user = null;
1329     protected UserGroupInformation ugi = null;
1330 
1331     public Connection(SocketChannel channel, long lastContact) {
1332       this.channel = channel;
1333       this.lastContact = lastContact;
1334       this.data = null;
1335       this.dataLengthBuffer = ByteBuffer.allocate(4);
1336       this.socket = channel.socket();
1337       this.addr = socket.getInetAddress();
1338       if (addr == null) {
1339         this.hostAddress = "*Unknown*";
1340       } else {
1341         this.hostAddress = addr.getHostAddress();
1342       }
1343       this.remotePort = socket.getPort();
1344       if (socketSendBufferSize != 0) {
1345         try {
1346           socket.setSendBufferSize(socketSendBufferSize);
1347         } catch (IOException e) {
1348           LOG.warn("Connection: unable to set socket send buffer size to " +
1349                    socketSendBufferSize);
1350         }
1351       }
1352     }
1353 
1354       @Override
1355     public String toString() {
1356       return getHostAddress() + ":" + remotePort;
1357     }
1358 
1359     public String getHostAddress() {
1360       return hostAddress;
1361     }
1362 
1363     public InetAddress getHostInetAddress() {
1364       return addr;
1365     }
1366 
1367     public int getRemotePort() {
1368       return remotePort;
1369     }
1370 
1371     public void setLastContact(long lastContact) {
1372       this.lastContact = lastContact;
1373     }
1374 
1375     public VersionInfo getVersionInfo() {
1376       if (connectionHeader.hasVersionInfo()) {
1377         return connectionHeader.getVersionInfo();
1378       }
1379       return null;
1380     }
1381 
1382     /* Return true if the connection has no outstanding rpc */
1383     private boolean isIdle() {
1384       return rpcCount.get() == 0;
1385     }
1386 
1387     /* Decrement the outstanding RPC count */
1388     protected void decRpcCount() {
1389       rpcCount.decrement();
1390     }
1391 
1392     /* Increment the outstanding RPC count */
1393     protected void incRpcCount() {
1394       rpcCount.increment();
1395     }
1396 
1397     protected boolean timedOut(long currentTime) {
1398       return isIdle() && currentTime - lastContact > maxIdleTime;
1399     }
1400 
1401     private UserGroupInformation getAuthorizedUgi(String authorizedId)
1402         throws IOException {
1403       UserGroupInformation authorizedUgi;
1404       if (authMethod == AuthMethod.DIGEST) {
1405         TokenIdentifier tokenId = HBaseSaslRpcServer.getIdentifier(authorizedId,
1406             secretManager);
1407         authorizedUgi = tokenId.getUser();
1408         if (authorizedUgi == null) {
1409           throw new AccessDeniedException(
1410               "Can't retrieve username from tokenIdentifier.");
1411         }
1412         authorizedUgi.addTokenIdentifier(tokenId);
1413       } else {
1414         authorizedUgi = UserGroupInformation.createRemoteUser(authorizedId);
1415       }
1416       authorizedUgi.setAuthenticationMethod(authMethod.authenticationMethod.getAuthMethod());
1417       return authorizedUgi;
1418     }
1419 
1420     private void saslReadAndProcess(ByteBuffer saslToken) throws IOException,
1421         InterruptedException {
1422       if (saslContextEstablished) {
1423         if (LOG.isTraceEnabled())
1424           LOG.trace("Have read input token of size " + saslToken.limit()
1425               + " for processing by saslServer.unwrap()");
1426 
1427         if (!useWrap) {
1428           processOneRpc(saslToken);
1429         } else {
1430           byte[] b = saslToken.array();
1431           byte [] plaintextData = saslServer.unwrap(b, saslToken.position(), saslToken.limit());
1432           processUnwrappedData(plaintextData);
1433         }
1434       } else {
1435         byte[] replyToken;
1436         try {
1437           if (saslServer == null) {
1438             switch (authMethod) {
1439             case DIGEST:
1440               if (secretManager == null) {
1441                 throw new AccessDeniedException(
1442                     "Server is not configured to do DIGEST authentication.");
1443               }
1444               saslServer = Sasl.createSaslServer(AuthMethod.DIGEST
1445                   .getMechanismName(), null, SaslUtil.SASL_DEFAULT_REALM,
1446                   HBaseSaslRpcServer.getSaslProps(), new SaslDigestCallbackHandler(
1447                       secretManager, this));
1448               break;
1449             default:
1450               UserGroupInformation current = UserGroupInformation.getCurrentUser();
1451               String fullName = current.getUserName();
1452               if (LOG.isDebugEnabled()) {
1453                 LOG.debug("Kerberos principal name is " + fullName);
1454               }
1455               final String names[] = SaslUtil.splitKerberosName(fullName);
1456               if (names.length != 3) {
1457                 throw new AccessDeniedException(
1458                     "Kerberos principal name does NOT have the expected "
1459                         + "hostname part: " + fullName);
1460               }
1461               current.doAs(new PrivilegedExceptionAction<Object>() {
1462                 @Override
1463                 public Object run() throws SaslException {
1464                   saslServer = Sasl.createSaslServer(AuthMethod.KERBEROS
1465                       .getMechanismName(), names[0], names[1],
1466                       HBaseSaslRpcServer.getSaslProps(), new SaslGssCallbackHandler());
1467                   return null;
1468                 }
1469               });
1470             }
1471             if (saslServer == null)
1472               throw new AccessDeniedException(
1473                   "Unable to find SASL server implementation for "
1474                       + authMethod.getMechanismName());
1475             if (LOG.isDebugEnabled()) {
1476               LOG.debug("Created SASL server with mechanism = " + authMethod.getMechanismName());
1477             }
1478           }
1479           if (LOG.isDebugEnabled()) {
1480             LOG.debug("Have read input token of size " + saslToken.limit()
1481                 + " for processing by saslServer.evaluateResponse()");
1482           }
1483           replyToken = saslServer.evaluateResponse(saslToken.array());
1484         } catch (IOException e) {
1485           IOException sendToClient = e;
1486           Throwable cause = e;
1487           while (cause != null) {
1488             if (cause instanceof InvalidToken) {
1489               sendToClient = (InvalidToken) cause;
1490               break;
1491             }
1492             cause = cause.getCause();
1493           }
1494           doRawSaslReply(SaslStatus.ERROR, null, sendToClient.getClass().getName(),
1495             sendToClient.getLocalizedMessage());
1496           metrics.authenticationFailure();
1497           String clientIP = this.toString();
1498           // attempting user could be null
1499           AUDITLOG.warn(AUTH_FAILED_FOR + clientIP + ":" + attemptingUser);
1500           throw e;
1501         }
1502         if (replyToken != null) {
1503           if (LOG.isDebugEnabled()) {
1504             LOG.debug("Will send token of size " + replyToken.length
1505                 + " from saslServer.");
1506           }
1507           doRawSaslReply(SaslStatus.SUCCESS, new BytesWritable(replyToken), null,
1508               null);
1509         }
1510         if (saslServer.isComplete()) {
1511           String qop = (String) saslServer.getNegotiatedProperty(Sasl.QOP);
1512           useWrap = qop != null && !"auth".equalsIgnoreCase(qop);
1513           ugi = getAuthorizedUgi(saslServer.getAuthorizationID());
1514           if (LOG.isDebugEnabled()) {
1515             LOG.debug("SASL server context established. Authenticated client: "
1516               + ugi + ". Negotiated QoP is "
1517               + saslServer.getNegotiatedProperty(Sasl.QOP));
1518           }
1519           metrics.authenticationSuccess();
1520           AUDITLOG.info(AUTH_SUCCESSFUL_FOR + ugi);
1521           saslContextEstablished = true;
1522         }
1523       }
1524     }
1525 
1526     /**
1527      * No protobuf encoding of raw sasl messages
1528      */
1529     private void doRawSaslReply(SaslStatus status, Writable rv,
1530         String errorClass, String error) throws IOException {
1531       ByteBufferOutputStream saslResponse = null;
1532       DataOutputStream out = null;
1533       try {
1534         // In my testing, have noticed that sasl messages are usually
1535         // in the ballpark of 100-200. That's why the initial capacity is 256.
1536         saslResponse = new ByteBufferOutputStream(256);
1537         out = new DataOutputStream(saslResponse);
1538         out.writeInt(status.state); // write status
1539         if (status == SaslStatus.SUCCESS) {
1540           rv.write(out);
1541         } else {
1542           WritableUtils.writeString(out, errorClass);
1543           WritableUtils.writeString(out, error);
1544         }
1545         saslCall.setSaslTokenResponse(saslResponse.getByteBuffer());
1546         saslCall.responder = responder;
1547         saslCall.sendResponseIfReady();
1548       } finally {
1549         if (saslResponse != null) {
1550           saslResponse.close();
1551         }
1552         if (out != null) {
1553           out.close();
1554         }
1555       }
1556     }
1557 
1558     private void disposeSasl() {
1559       if (saslServer != null) {
1560         try {
1561           saslServer.dispose();
1562           saslServer = null;
1563         } catch (SaslException ignored) {
1564           // Ignored. This is being disposed of anyway.
1565         }
1566       }
1567     }
1568 
1569     private int readPreamble() throws IOException {
1570       if (preambleBuffer == null) {
1571         preambleBuffer = ByteBuffer.allocate(6);
1572       }
1573       int count = channelRead(channel, preambleBuffer);
1574       if (count < 0 || preambleBuffer.remaining() > 0) {
1575         return count;
1576       }
1577       // Check for 'HBas' magic.
1578       preambleBuffer.flip();
1579       for (int i = 0; i < HConstants.RPC_HEADER.length; i++) {
1580         if (HConstants.RPC_HEADER[i] != preambleBuffer.get(i)) {
1581           return doBadPreambleHandling("Expected HEADER=" +
1582               Bytes.toStringBinary(HConstants.RPC_HEADER) + " but received HEADER=" +
1583               Bytes.toStringBinary(preambleBuffer.array(), 0, HConstants.RPC_HEADER.length) +
1584               " from " + toString());
1585         }
1586       }
1587       int version = preambleBuffer.get(HConstants.RPC_HEADER.length);
1588       byte authbyte = preambleBuffer.get(HConstants.RPC_HEADER.length + 1);
1589       this.authMethod = AuthMethod.valueOf(authbyte);
1590       if (version != CURRENT_VERSION) {
1591         String msg = getFatalConnectionString(version, authbyte);
1592         return doBadPreambleHandling(msg, new WrongVersionException(msg));
1593       }
1594       if (authMethod == null) {
1595         String msg = getFatalConnectionString(version, authbyte);
1596         return doBadPreambleHandling(msg, new BadAuthException(msg));
1597       }
1598       if (isSecurityEnabled && authMethod == AuthMethod.SIMPLE) {
1599         if (allowFallbackToSimpleAuth) {
1600           metrics.authenticationFallback();
1601           authenticatedWithFallback = true;
1602         } else {
1603           AccessDeniedException ae = new AccessDeniedException("Authentication is required");
1604           setupResponse(authFailedResponse, authFailedCall, ae, ae.getMessage());
1605           responder.doRespond(authFailedCall);
1606           throw ae;
1607         }
1608       }
1609       if (!isSecurityEnabled && authMethod != AuthMethod.SIMPLE) {
1610         doRawSaslReply(SaslStatus.SUCCESS, new IntWritable(
1611             SaslUtil.SWITCH_TO_SIMPLE_AUTH), null, null);
1612         authMethod = AuthMethod.SIMPLE;
1613         // client has already sent the initial Sasl message and we
1614         // should ignore it. Both client and server should fall back
1615         // to simple auth from now on.
1616         skipInitialSaslHandshake = true;
1617       }
1618       if (authMethod != AuthMethod.SIMPLE) {
1619         useSasl = true;
1620       }
1621 
1622       preambleBuffer = null; // do not need it anymore
1623       connectionPreambleRead = true;
1624       return count;
1625     }
1626 
1627     private int read4Bytes() throws IOException {
1628       if (this.dataLengthBuffer.remaining() > 0) {
1629         return channelRead(channel, this.dataLengthBuffer);
1630       } else {
1631         return 0;
1632       }
1633     }
1634 
1635 
1636     /**
1637      * Read off the wire. If there is not enough data to read, update the connection state with
1638      *  what we have and returns.
1639      * @return Returns -1 if failure (and caller will close connection), else zero or more.
1640      * @throws IOException
1641      * @throws InterruptedException
1642      */
1643     public int readAndProcess() throws IOException, InterruptedException {
1644       // If we have not read the connection setup preamble, look to see if that is on the wire.
1645       if (!connectionPreambleRead) {
1646         int count = readPreamble();
1647         if (!connectionPreambleRead) {
1648           return count;
1649         }
1650       }
1651       // Try and read in an int. It will be length of the data to read (or -1 if a ping). We catch
1652       // the integer length into the 4-byte this.dataLengthBuffer.
1653       int count = read4Bytes();
1654       if (count < 0 || dataLengthBuffer.remaining() > 0) {
1655         return count;
1656       }
1657 
1658       // If we have not read the connection setup preamble, look to see if that is on the wire.
1659       if (!connectionPreambleRead) {
1660         count = readPreamble();
1661         if (!connectionPreambleRead) {
1662           return count;
1663         }
1664 
1665         count = read4Bytes();
1666         if (count < 0 || dataLengthBuffer.remaining() > 0) {
1667           return count;
1668         }
1669       }
1670 
1671       final boolean useWrap = this.useWrap;
1672       final BlockingService service = this.service;
1673       final boolean headerAndPreambleRead = connectionHeaderRead && connectionPreambleRead;
1674       final boolean canUseRequestTooBig = headerAndPreambleRead &&
1675           VersionInfoUtil.hasMinimumVersion(connectionHeader.getVersionInfo(),
1676               RequestTooBigException.MAJOR_VERSION, RequestTooBigException.MINOR_VERSION);
1677 
1678       // we're guarding against data being modified concurrently
1679       // while trying to keep other instance members out of the block
1680       synchronized(this) {
1681         // We have read a length and we have read the preamble.  It is either the connection header
1682         // or it is a request.
1683         if (data == null) {
1684           dataLengthBuffer.flip();
1685           int dataLength = dataLengthBuffer.getInt();
1686           if (dataLength == RpcClient.PING_CALL_ID) {
1687             if (!useWrap) { //covers the !useSasl too
1688               dataLengthBuffer.clear();
1689               return 0;  //ping message
1690             }
1691           }
1692           if (dataLength < 0) { // A data length of zero is legal.
1693             throw new DoNotRetryIOException("Unexpected data length "
1694                 + dataLength + "!! from " + getHostAddress());
1695           }
1696 
1697           if (dataLength > maxRequestSize) {
1698             String msg = "RPC data length of " + dataLength + " received from "
1699                 + getHostAddress() + " is greater than max allowed "
1700                 + maxRequestSize + ". Set \"" + MAX_REQUEST_SIZE
1701                 + "\" on server to override this limit (not recommended)";
1702             LOG.warn(msg);
1703 
1704             if (headerAndPreambleRead) {
1705               incRpcCount();
1706               // Construct InputStream for the non-blocking SocketChannel
1707               // We need the InputStream because we want to read only the request header
1708               // instead of the whole rpc.
1709               final ByteBuffer buf = ByteBuffer.allocate(1);
1710               InputStream is = new InputStream() {
1711                 @Override
1712                 public int read() throws IOException {
1713                   channelRead(channel, buf);
1714                   buf.flip();
1715                   int x = buf.get();
1716                   buf.flip();
1717                   return x;
1718                 }
1719               };
1720               CodedInputStream cis = CodedInputStream.newInstance(is);
1721               int headerSize = cis.readRawVarint32();
1722               Message.Builder builder = RequestHeader.newBuilder();
1723               ProtobufUtil.mergeFrom(builder, cis, headerSize);
1724               RequestHeader header = (RequestHeader) builder.build();
1725 
1726               // Notify the client about the offending request
1727               Call reqTooBig = new Call(header.getCallId(), service, null, null, null,
1728                   null, this, responder, 0, null, this.addr,0);
1729               metrics.exception(REQUEST_TOO_BIG_EXCEPTION);
1730               // Make sure the client recognizes the underlying exception
1731               // Otherwise, throw a DoNotRetryIOException.
1732               if (canUseRequestTooBig) {
1733                 setupResponse(null, reqTooBig, REQUEST_TOO_BIG_EXCEPTION, msg);
1734               } else {
1735                 setupResponse(null, reqTooBig, new DoNotRetryIOException(), msg);
1736               }
1737               // We are going to close the connection, make sure we process the response
1738               // before that. In rare case when this fails, we still close the connection.
1739               responseWriteLock.lock();
1740               responder.processResponse(reqTooBig);
1741               responseWriteLock.unlock();
1742             }
1743             // Close the connection
1744             return -1;
1745           }
1746 
1747           data = ByteBuffer.allocate(dataLength);
1748 
1749           // Increment the rpc count. This counter will be decreased when we write
1750           //  the response.  If we want the connection to be detected as idle properly, we
1751           //  need to keep the inc / dec correct.
1752           incRpcCount();
1753         }
1754 
1755         count = channelRead(channel, data);
1756 
1757         if (count >= 0 && data.remaining() == 0) { // count==0 if dataLength == 0
1758           process();
1759         }
1760       }
1761 
1762       return count;
1763     }
1764 
1765     /**
1766      * Process the data buffer and clean the connection state for the next call.
1767      */
1768     private void process() throws IOException, InterruptedException {
1769       data.flip();
1770       try {
1771         if (skipInitialSaslHandshake) {
1772           skipInitialSaslHandshake = false;
1773           return;
1774         }
1775 
1776         if (useSasl) {
1777           saslReadAndProcess(data);
1778         } else {
1779           processOneRpc(data);
1780         }
1781 
1782       } finally {
1783         dataLengthBuffer.clear(); // Clean for the next call
1784         data = null; // For the GC
1785       }
1786     }
1787 
1788     private String getFatalConnectionString(final int version, final byte authByte) {
1789       return "serverVersion=" + CURRENT_VERSION +
1790       ", clientVersion=" + version + ", authMethod=" + authByte +
1791       ", authSupported=" + (authMethod != null) + " from " + toString();
1792     }
1793 
1794     private int doBadPreambleHandling(final String msg) throws IOException {
1795       return doBadPreambleHandling(msg, new FatalConnectionException(msg));
1796     }
1797 
1798     private int doBadPreambleHandling(final String msg, final Exception e) throws IOException {
1799       LOG.warn(msg);
1800       Call fakeCall = new Call(-1, null, null, null, null, null, this, responder, -1, null, null,0);
1801       setupResponse(null, fakeCall, e, msg);
1802       responder.doRespond(fakeCall);
1803       // Returning -1 closes out the connection.
1804       return -1;
1805     }
1806 
1807     // Reads the connection header following version
1808     private void processConnectionHeader(ByteBuffer buf) throws IOException {
1809       this.connectionHeader = ConnectionHeader.parseFrom(
1810         new ByteBufferInputStream(buf));
1811       String serviceName = connectionHeader.getServiceName();
1812       if (serviceName == null) throw new EmptyServiceNameException();
1813       this.service = getService(services, serviceName);
1814       if (this.service == null) throw new UnknownServiceException(serviceName);
1815       setupCellBlockCodecs(this.connectionHeader);
1816       UserGroupInformation protocolUser = createUser(connectionHeader);
1817       if (!useSasl) {
1818         ugi = protocolUser;
1819         if (ugi != null) {
1820           ugi.setAuthenticationMethod(AuthMethod.SIMPLE.authenticationMethod);
1821         }
1822         // audit logging for SASL authenticated users happens in saslReadAndProcess()
1823         if (authenticatedWithFallback) {
1824           LOG.warn("Allowed fallback to SIMPLE auth for " + ugi
1825               + " connecting from " + getHostAddress());
1826         }
1827         AUDITLOG.info(AUTH_SUCCESSFUL_FOR + ugi);
1828       } else {
1829         // user is authenticated
1830         ugi.setAuthenticationMethod(authMethod.authenticationMethod);
1831         //Now we check if this is a proxy user case. If the protocol user is
1832         //different from the 'user', it is a proxy user scenario. However,
1833         //this is not allowed if user authenticated with DIGEST.
1834         if ((protocolUser != null)
1835             && (!protocolUser.getUserName().equals(ugi.getUserName()))) {
1836           if (authMethod == AuthMethod.DIGEST) {
1837             // Not allowed to doAs if token authentication is used
1838             throw new AccessDeniedException("Authenticated user (" + ugi
1839                 + ") doesn't match what the client claims to be ("
1840                 + protocolUser + ")");
1841           } else {
1842             // Effective user can be different from authenticated user
1843             // for simple auth or kerberos auth
1844             // The user is the real user. Now we create a proxy user
1845             UserGroupInformation realUser = ugi;
1846             ugi = UserGroupInformation.createProxyUser(protocolUser
1847                 .getUserName(), realUser);
1848             // Now the user is a proxy user, set Authentication method Proxy.
1849             ugi.setAuthenticationMethod(AuthenticationMethod.PROXY);
1850           }
1851         }
1852       }
1853       if (connectionHeader.hasVersionInfo()) {
1854         // see if this connection will support RetryImmediatelyException
1855         retryImmediatelySupported = VersionInfoUtil.hasMinimumVersion(getVersionInfo(), 1, 2);
1856 
1857         AUDITLOG.info("Connection from " + this.hostAddress + " port: " + this.remotePort
1858             + " with version info: "
1859             + TextFormat.shortDebugString(connectionHeader.getVersionInfo()));
1860       } else {
1861         AUDITLOG.info("Connection from " + this.hostAddress + " port: " + this.remotePort
1862             + " with unknown version info");
1863       }
1864 
1865 
1866     }
1867 
1868     /**
1869      * Set up cell block codecs
1870      * @throws FatalConnectionException
1871      */
1872     private void setupCellBlockCodecs(final ConnectionHeader header)
1873     throws FatalConnectionException {
1874       // TODO: Plug in other supported decoders.
1875       if (!header.hasCellBlockCodecClass()) return;
1876       String className = header.getCellBlockCodecClass();
1877       if (className == null || className.length() == 0) return;
1878       try {
1879         this.codec = (Codec)Class.forName(className).getDeclaredConstructor().newInstance();
1880       } catch (Exception e) {
1881         throw new UnsupportedCellCodecException(className, e);
1882       }
1883       if (!header.hasCellBlockCompressorClass()) return;
1884       className = header.getCellBlockCompressorClass();
1885       try {
1886         this.compressionCodec = (CompressionCodec)
1887           Class.forName(className).getDeclaredConstructor().newInstance();
1888       } catch (Exception e) {
1889         throw new UnsupportedCompressionCodecException(className, e);
1890       }
1891     }
1892 
1893     private void processUnwrappedData(byte[] inBuf) throws IOException,
1894     InterruptedException {
1895       ReadableByteChannel ch = Channels.newChannel(new ByteArrayInputStream(inBuf));
1896       // Read all RPCs contained in the inBuf, even partial ones
1897       while (true) {
1898         int count;
1899         if (unwrappedDataLengthBuffer.remaining() > 0) {
1900           count = channelRead(ch, unwrappedDataLengthBuffer);
1901           if (count <= 0 || unwrappedDataLengthBuffer.remaining() > 0)
1902             return;
1903         }
1904 
1905         if (unwrappedData == null) {
1906           unwrappedDataLengthBuffer.flip();
1907           int unwrappedDataLength = unwrappedDataLengthBuffer.getInt();
1908 
1909           if (unwrappedDataLength == RpcClient.PING_CALL_ID) {
1910             if (LOG.isDebugEnabled())
1911               LOG.debug("Received ping message");
1912             unwrappedDataLengthBuffer.clear();
1913             continue; // ping message
1914           }
1915           unwrappedData = ByteBuffer.allocate(unwrappedDataLength);
1916         }
1917 
1918         count = channelRead(ch, unwrappedData);
1919         if (count <= 0 || unwrappedData.remaining() > 0)
1920           return;
1921 
1922         if (unwrappedData.remaining() == 0) {
1923           unwrappedDataLengthBuffer.clear();
1924           unwrappedData.flip();
1925           processOneRpc(unwrappedData);
1926           unwrappedData = null;
1927         }
1928       }
1929     }
1930 
1931 
1932     private void processOneRpc(ByteBuffer buf) throws IOException, InterruptedException {
1933       if (connectionHeaderRead) {
1934         processRequest(buf);
1935       } else {
1936         processConnectionHeader(buf);
1937         this.connectionHeaderRead = true;
1938         if (!authorizeConnection()) {
1939           // Throw FatalConnectionException wrapping ACE so client does right thing and closes
1940           // down the connection instead of trying to read non-existent retun.
1941           throw new AccessDeniedException("Connection from " + this + " for service " +
1942             connectionHeader.getServiceName() + " is unauthorized for user: " + ugi);
1943         }
1944         this.user = userProvider.create(this.ugi);
1945       }
1946     }
1947 
1948     /**
1949      * @param buf Has the request header and the request param and optionally encoded data buffer
1950      * all in this one array.
1951      * @throws IOException
1952      * @throws InterruptedException
1953      */
1954     protected void processRequest(ByteBuffer buf) throws IOException, InterruptedException {
1955       long totalRequestSize = buf.limit();
1956       int offset = 0;
1957       // Here we read in the header.  We avoid having pb
1958       // do its default 4k allocation for CodedInputStream.  We force it to use backing array.
1959       CodedInputStream cis = CodedInputStream.newInstance(buf.array(), offset, buf.limit());
1960       int headerSize = cis.readRawVarint32();
1961       offset = cis.getTotalBytesRead();
1962       Message.Builder builder = RequestHeader.newBuilder();
1963       ProtobufUtil.mergeFrom(builder, cis, headerSize);
1964       RequestHeader header = (RequestHeader) builder.build();
1965       offset += headerSize;
1966       int id = header.getCallId();
1967       if (LOG.isTraceEnabled()) {
1968         LOG.trace("RequestHeader " + TextFormat.shortDebugString(header) +
1969           " totalRequestSize: " + totalRequestSize + " bytes");
1970       }
1971       // Enforcing the call queue size, this triggers a retry in the client
1972       // This is a bit late to be doing this check - we have already read in the total request.
1973       if ((totalRequestSize + callQueueSize.get()) > maxQueueSize) {
1974         final Call callTooBig =
1975           new Call(id, this.service, null, null, null, null, this,
1976             responder, totalRequestSize, null, null, 0);
1977         ByteArrayOutputStream responseBuffer = new ByteArrayOutputStream();
1978         metrics.exception(CALL_QUEUE_TOO_BIG_EXCEPTION);
1979         setupResponse(responseBuffer, callTooBig, CALL_QUEUE_TOO_BIG_EXCEPTION,
1980             "Call queue is full on " + server.getServerName() +
1981                 ", is hbase.ipc.server.max.callqueue.size too small?");
1982         responder.doRespond(callTooBig);
1983         return;
1984       }
1985       MethodDescriptor md = null;
1986       Message param = null;
1987       CellScanner cellScanner = null;
1988       try {
1989         if (header.hasRequestParam() && header.getRequestParam()) {
1990           md = this.service.getDescriptorForType().findMethodByName(header.getMethodName());
1991           if (md == null) throw new UnsupportedOperationException(header.getMethodName());
1992           builder = this.service.getRequestPrototype(md).newBuilderForType();
1993           cis.resetSizeCounter();
1994           int paramSize = cis.readRawVarint32();
1995           offset += cis.getTotalBytesRead();
1996           if (builder != null) {
1997             ProtobufUtil.mergeFrom(builder, cis, paramSize);
1998             param = builder.build();
1999           }
2000           offset += paramSize;
2001         } else {
2002           // currently header must have request param, so we directly throw exception here
2003           String msg = "Invalid request header: " + TextFormat.shortDebugString(header)
2004               + ", should have param set in it";
2005           LOG.warn(msg);
2006           throw new DoNotRetryIOException(msg);
2007         }
2008         if (header.hasCellBlockMeta()) {
2009           buf.position(offset);
2010           cellScanner = cellBlockBuilder.createCellScanner(this.codec, this.compressionCodec, buf);
2011         }
2012       } catch (Throwable t) {
2013         InetSocketAddress address = getListenerAddress();
2014         String msg = (address != null ? address : "(channel closed)") +
2015             " is unable to read call parameter from client " + getHostAddress();
2016         LOG.warn(msg, t);
2017 
2018         metrics.exception(t);
2019 
2020         // probably the hbase hadoop version does not match the running hadoop version
2021         if (t instanceof LinkageError) {
2022           t = new DoNotRetryIOException(t);
2023         }
2024         // If the method is not present on the server, do not retry.
2025         if (t instanceof UnsupportedOperationException) {
2026           t = new DoNotRetryIOException(t);
2027         }
2028 
2029         final Call readParamsFailedCall =
2030           new Call(id, this.service, null, null, null, null, this,
2031             responder, totalRequestSize, null, null, 0);
2032         ByteArrayOutputStream responseBuffer = new ByteArrayOutputStream();
2033         setupResponse(responseBuffer, readParamsFailedCall, t,
2034           msg + "; " + t.getMessage());
2035         responder.doRespond(readParamsFailedCall);
2036         return;
2037       }
2038 
2039       TraceInfo traceInfo = header.hasTraceInfo()
2040           ? new TraceInfo(header.getTraceInfo().getTraceId(), header.getTraceInfo().getParentId())
2041           : null;
2042       int timeout = 0;
2043       if (header.hasTimeout() && header.getTimeout() > 0){
2044         timeout = Math.max(minClientRequestTimeout, header.getTimeout());
2045       }
2046       Call call = new Call(id, this.service, md, header, param, cellScanner, this, responder,
2047               totalRequestSize, traceInfo, this.addr, timeout);
2048 
2049       if (!scheduler.dispatch(new CallRunner(RpcServer.this, call))) {
2050         callQueueSize.add(-1 * call.getSize());
2051 
2052         ByteArrayOutputStream responseBuffer = new ByteArrayOutputStream();
2053         metrics.exception(CALL_QUEUE_TOO_BIG_EXCEPTION);
2054         setupResponse(responseBuffer, call, CALL_QUEUE_TOO_BIG_EXCEPTION,
2055             "Call queue is full on " + server.getServerName() +
2056                 ", too many items queued ?");
2057         responder.doRespond(call);
2058       }
2059     }
2060 
2061     private boolean authorizeConnection() throws IOException {
2062       try {
2063         // If auth method is DIGEST, the token was obtained by the
2064         // real user for the effective user, therefore not required to
2065         // authorize real user. doAs is allowed only for simple or kerberos
2066         // authentication
2067         if (ugi != null && ugi.getRealUser() != null
2068             && (authMethod != AuthMethod.DIGEST)) {
2069           ProxyUsers.authorize(ugi, this.getHostAddress(), conf);
2070         }
2071         authorize(ugi, connectionHeader, getHostInetAddress());
2072         metrics.authorizationSuccess();
2073       } catch (AuthorizationException ae) {
2074         if (LOG.isDebugEnabled()) {
2075           LOG.debug("Connection authorization failed: " + ae.getMessage(), ae);
2076         }
2077         metrics.authorizationFailure();
2078         setupResponse(authFailedResponse, authFailedCall,
2079           new AccessDeniedException(ae), ae.getMessage());
2080         responder.doRespond(authFailedCall);
2081         return false;
2082       }
2083       return true;
2084     }
2085 
2086     protected synchronized void close() {
2087       disposeSasl();
2088       data = null;
2089       if (!channel.isOpen())
2090         return;
2091       try {socket.shutdownOutput();} catch(Exception ignored) {
2092         if (LOG.isTraceEnabled()) {
2093           LOG.trace(ignored);
2094         }
2095       }
2096       if (channel.isOpen()) {
2097         try {channel.close();} catch(Exception ignored) {
2098           if (LOG.isTraceEnabled()) {
2099             LOG.trace(ignored);
2100           }
2101         }
2102       }
2103       try {socket.close();} catch(Exception ignored) {
2104         if (LOG.isTraceEnabled()) {
2105           LOG.trace(ignored);
2106         }
2107       }
2108     }
2109 
2110     private UserGroupInformation createUser(ConnectionHeader head) {
2111       UserGroupInformation ugi = null;
2112 
2113       if (!head.hasUserInfo()) {
2114         return null;
2115       }
2116       UserInformation userInfoProto = head.getUserInfo();
2117       String effectiveUser = null;
2118       if (userInfoProto.hasEffectiveUser()) {
2119         effectiveUser = userInfoProto.getEffectiveUser();
2120       }
2121       String realUser = null;
2122       if (userInfoProto.hasRealUser()) {
2123         realUser = userInfoProto.getRealUser();
2124       }
2125       if (effectiveUser != null) {
2126         if (realUser != null) {
2127           UserGroupInformation realUserUgi =
2128               UserGroupInformation.createRemoteUser(realUser);
2129           ugi = UserGroupInformation.createProxyUser(effectiveUser, realUserUgi);
2130         } else {
2131           ugi = UserGroupInformation.createRemoteUser(effectiveUser);
2132         }
2133       }
2134       return ugi;
2135     }
2136   }
2137 
2138   /**
2139    * Datastructure for passing a {@link BlockingService} and its associated class of
2140    * protobuf service interface.  For example, a server that fielded what is defined
2141    * in the client protobuf service would pass in an implementation of the client blocking service
2142    * and then its ClientService.BlockingInterface.class.  Used checking connection setup.
2143    */
2144   public static class BlockingServiceAndInterface {
2145     private final BlockingService service;
2146     private final Class<?> serviceInterface;
2147     public BlockingServiceAndInterface(final BlockingService service,
2148         final Class<?> serviceInterface) {
2149       this.service = service;
2150       this.serviceInterface = serviceInterface;
2151     }
2152     public Class<?> getServiceInterface() {
2153       return this.serviceInterface;
2154     }
2155     public BlockingService getBlockingService() {
2156       return this.service;
2157     }
2158   }
2159 
2160   /**
2161    * Constructs a server listening on the named port and address.
2162    * @param server hosting instance of {@link Server}. We will do authentications if an
2163    * instance else pass null for no authentication check.
2164    * @param name Used keying this rpc servers' metrics and for naming the Listener thread.
2165    * @param services A list of services.
2166    * @param bindAddress Where to listen
2167    * @param conf
2168    * @param scheduler
2169    */
2170   public RpcServer(final Server server, final String name,
2171       final List<BlockingServiceAndInterface> services,
2172       final InetSocketAddress bindAddress, Configuration conf,
2173       RpcScheduler scheduler)
2174       throws IOException {
2175     
2176     if (conf.getBoolean("hbase.ipc.server.reservoir.enabled", true)) {
2177       this.reservoir = new BoundedByteBufferPool(
2178           conf.getInt("hbase.ipc.server.reservoir.max.buffer.size", 1024 * 1024),
2179           conf.getInt("hbase.ipc.server.reservoir.initial.buffer.size", 16 * 1024),
2180           // Make the max twice the number of handlers to be safe.
2181           conf.getInt("hbase.ipc.server.reservoir.initial.max",
2182               conf.getInt(HConstants.REGION_SERVER_HANDLER_COUNT,
2183                   HConstants.DEFAULT_REGION_SERVER_HANDLER_COUNT) * 2),
2184           // By default make direct byte buffers from the buffer pool.
2185           conf.getBoolean("hbase.ipc.server.reservoir.direct.buffer", false));
2186     } else {
2187       reservoir = null;
2188     }
2189     this.server = server;
2190     this.services = services;
2191     this.bindAddress = bindAddress;
2192     this.conf = conf;
2193     this.socketSendBufferSize = 0;
2194     this.maxQueueSize =
2195       this.conf.getInt("hbase.ipc.server.max.callqueue.size", DEFAULT_MAX_CALLQUEUE_SIZE);
2196     this.readThreads = conf.getInt("hbase.ipc.server.read.threadpool.size", 10);
2197     this.maxIdleTime = 2 * conf.getInt("hbase.ipc.client.connection.maxidletime", 1000);
2198     this.maxConnectionsToNuke = conf.getInt("hbase.ipc.client.kill.max", 10);
2199     this.thresholdIdleConnections = conf.getInt("hbase.ipc.client.idlethreshold", 4000);
2200     this.purgeTimeout = conf.getLong("hbase.ipc.client.call.purge.timeout",
2201       2 * HConstants.DEFAULT_HBASE_RPC_TIMEOUT);
2202     this.warnResponseTime = conf.getInt(WARN_RESPONSE_TIME, DEFAULT_WARN_RESPONSE_TIME);
2203     this.warnResponseSize = conf.getInt(WARN_RESPONSE_SIZE, DEFAULT_WARN_RESPONSE_SIZE);
2204     this.minClientRequestTimeout = conf.getInt(MIN_CLIENT_REQUEST_TIMEOUT,
2205         DEFAULT_MIN_CLIENT_REQUEST_TIMEOUT);
2206     this.maxRequestSize = conf.getInt(MAX_REQUEST_SIZE, DEFAULT_MAX_REQUEST_SIZE);
2207 
2208     // Start the listener here and let it bind to the port
2209     listener = new Listener(name);
2210     this.port = listener.getAddress().getPort();
2211 
2212     this.metrics = new MetricsHBaseServer(name, new MetricsHBaseServerWrapperImpl(this));
2213     this.tcpNoDelay = conf.getBoolean("hbase.ipc.server.tcpnodelay", true);
2214     this.tcpKeepAlive = conf.getBoolean("hbase.ipc.server.tcpkeepalive", true);
2215 
2216     this.cellBlockBuilder = new CellBlockBuilder(conf);
2217 
2218     // Create the responder here
2219     responder = new Responder();
2220     this.authorize = conf.getBoolean(HADOOP_SECURITY_AUTHORIZATION, false);
2221     this.userProvider = UserProvider.instantiate(conf);
2222     this.isSecurityEnabled = userProvider.isHBaseSecurityEnabled();
2223     if (isSecurityEnabled) {
2224       HBaseSaslRpcServer.init(conf);
2225     }
2226     initReconfigurable(conf);
2227 
2228     this.scheduler = scheduler;
2229     this.scheduler.init(new RpcSchedulerContext(this));
2230   }
2231 
2232   @Override
2233   public void onConfigurationChange(Configuration newConf) {
2234     initReconfigurable(newConf);
2235     if (scheduler instanceof ConfigurationObserver) {
2236       ((ConfigurationObserver)scheduler).onConfigurationChange(newConf);
2237     }
2238   }
2239 
2240   private void initReconfigurable(Configuration confToLoad) {
2241     this.allowFallbackToSimpleAuth = confToLoad.getBoolean(FALLBACK_TO_INSECURE_CLIENT_AUTH, false);
2242     if (isSecurityEnabled && allowFallbackToSimpleAuth) {
2243       LOG.warn("********* WARNING! *********");
2244       LOG.warn("This server is configured to allow connections from INSECURE clients");
2245       LOG.warn("(" + FALLBACK_TO_INSECURE_CLIENT_AUTH + " = true).");
2246       LOG.warn("While this option is enabled, client identities cannot be secured, and user");
2247       LOG.warn("impersonation is possible!");
2248       LOG.warn("For secure operation, please disable SIMPLE authentication as soon as possible,");
2249       LOG.warn("by setting " + FALLBACK_TO_INSECURE_CLIENT_AUTH + " = false in hbase-site.xml");
2250       LOG.warn("****************************");
2251     }
2252   }
2253 
2254   /**
2255    * Subclasses of HBaseServer can override this to provide their own
2256    * Connection implementations.
2257    */
2258   protected Connection getConnection(SocketChannel channel, long time) {
2259     return new Connection(channel, time);
2260   }
2261 
2262   /**
2263    * Setup response for the RPC Call.
2264    *
2265    * @param response buffer to serialize the response into
2266    * @param call {@link Call} to which we are setting up the response
2267    * @param error error message, if the call failed
2268    * @throws IOException
2269    */
2270   private void setupResponse(ByteArrayOutputStream response, Call call, Throwable t, String error)
2271   throws IOException {
2272     if (response != null) response.reset();
2273     call.setResponse(null, null, t, error);
2274   }
2275 
2276   protected void closeConnection(Connection connection) {
2277     synchronized (connectionList) {
2278       if (connectionList.remove(connection)) {
2279         numConnections--;
2280       }
2281     }
2282     connection.close();
2283   }
2284 
2285   Configuration getConf() {
2286     return conf;
2287   }
2288 
2289   /** Sets the socket buffer size used for responding to RPCs.
2290    * @param size send size
2291    */
2292   @Override
2293   public void setSocketSendBufSize(int size) { this.socketSendBufferSize = size; }
2294 
2295   @Override
2296   public boolean isStarted() {
2297     return this.started;
2298   }
2299 
2300   /** Starts the service.  Must be called before any calls will be handled. */
2301   @Override
2302   public synchronized void start() {
2303     if (started) return;
2304     authTokenSecretMgr = createSecretManager();
2305     if (authTokenSecretMgr != null) {
2306       setSecretManager(authTokenSecretMgr);
2307       authTokenSecretMgr.start();
2308     }
2309     this.authManager = new ServiceAuthorizationManager();
2310     HBasePolicyProvider.init(conf, authManager);
2311     responder.start();
2312     listener.start();
2313     scheduler.start();
2314     started = true;
2315   }
2316 
2317   @Override
2318   public synchronized void refreshAuthManager(PolicyProvider pp) {
2319     // Ignore warnings that this should be accessed in a static way instead of via an instance;
2320     // it'll break if you go via static route.
2321     this.authManager.refresh(this.conf, pp);
2322   }
2323 
2324   private AuthenticationTokenSecretManager createSecretManager() {
2325     if (!isSecurityEnabled) return null;
2326     if (server == null) return null;
2327     Configuration conf = server.getConfiguration();
2328     long keyUpdateInterval =
2329         conf.getLong("hbase.auth.key.update.interval", 24*60*60*1000);
2330     long maxAge =
2331         conf.getLong("hbase.auth.token.max.lifetime", 7*24*60*60*1000);
2332     return new AuthenticationTokenSecretManager(conf, server.getZooKeeper(),
2333         server.getServerName().toString(), keyUpdateInterval, maxAge);
2334   }
2335 
2336   public SecretManager<? extends TokenIdentifier> getSecretManager() {
2337     return this.secretManager;
2338   }
2339 
2340   @SuppressWarnings("unchecked")
2341   public void setSecretManager(SecretManager<? extends TokenIdentifier> secretManager) {
2342     this.secretManager = (SecretManager<TokenIdentifier>) secretManager;
2343   }
2344 
2345   public Pair<Message, CellScanner> call(BlockingService service, MethodDescriptor md,
2346       Message param, CellScanner cellScanner, long receiveTime, MonitoredRPCHandler status)
2347       throws IOException {
2348     return call(service, md, param, cellScanner, receiveTime, status, System.currentTimeMillis(),0);
2349   }
2350 
2351   /**
2352    * This is a server side method, which is invoked over RPC. On success
2353    * the return response has protobuf response payload. On failure, the
2354    * exception name and the stack trace are returned in the protobuf response.
2355    */
2356   @Override
2357   public Pair<Message, CellScanner> call(BlockingService service, MethodDescriptor md,
2358       Message param, CellScanner cellScanner, long receiveTime, MonitoredRPCHandler status,
2359       long startTime, int timeout)
2360   throws IOException {
2361     try {
2362       status.setRPC(md.getName(), new Object[]{param}, receiveTime);
2363       // TODO: Review after we add in encoded data blocks.
2364       status.setRPCPacket(param);
2365       status.resume("Servicing call");
2366       //get an instance of the method arg type
2367       HBaseRpcController controller = new HBaseRpcControllerImpl(cellScanner);
2368       controller.setCallTimeout(timeout);
2369       Message result = service.callBlockingMethod(md, controller, param);
2370       long endTime = System.currentTimeMillis();
2371       int processingTime = (int) (endTime - startTime);
2372       int qTime = (int) (startTime - receiveTime);
2373       int totalTime = (int) (endTime - receiveTime);
2374       Call call = CurCall.get();
2375       if (LOG.isTraceEnabled()) {
2376         LOG.trace(CurCall.get().toString() +
2377             ", response " + TextFormat.shortDebugString(result) +
2378             " queueTime: " + qTime +
2379             " processingTime: " + processingTime +
2380             " totalTime: " + totalTime);
2381       }
2382       // Use the raw request call size for now.
2383       long requestSize = call.getSize();
2384       long responseSize = result.getSerializedSize();
2385       if (call.isClientCellBlockSupported()) {
2386         // Include the payload size in HBaseRpcController
2387         responseSize += call.getResponseCellSize();
2388       }
2389 
2390       metrics.dequeuedCall(qTime);
2391       metrics.processedCall(processingTime);
2392       metrics.totalCall(totalTime);
2393       metrics.receivedRequest(requestSize);
2394       metrics.sentResponse(responseSize);
2395       // log any RPC responses that are slower than the configured warn
2396       // response time or larger than configured warning size
2397       boolean tooSlow = (processingTime > warnResponseTime && warnResponseTime > -1);
2398       boolean tooLarge = (responseSize > warnResponseSize && warnResponseSize > -1);
2399       if (tooSlow || tooLarge) {
2400         // when tagging, we let TooLarge trump TooSmall to keep output simple
2401         // note that large responses will often also be slow.
2402         logResponse(param,
2403             md.getName(), md.getName() + "(" + param.getClass().getName() + ")",
2404             (tooLarge ? "TooLarge" : "TooSlow"),
2405             status.getClient(), startTime, processingTime, qTime,
2406             responseSize);
2407       }
2408       return new Pair<Message, CellScanner>(result, controller.cellScanner());
2409     } catch (Throwable e) {
2410       // The above callBlockingMethod will always return a SE.  Strip the SE wrapper before
2411       // putting it on the wire.  Its needed to adhere to the pb Service Interface but we don't
2412       // need to pass it over the wire.
2413       if (e instanceof ServiceException) {
2414         if (e.getCause() == null) {
2415           LOG.debug("Caught a ServiceException with null cause", e);
2416         } else {
2417           e = e.getCause();
2418         }
2419       }
2420 
2421       // increment the number of requests that were exceptions.
2422       metrics.exception(e);
2423 
2424       if (e instanceof LinkageError) throw new DoNotRetryIOException(e);
2425       if (e instanceof IOException) throw (IOException)e;
2426       LOG.error("Unexpected throwable object ", e);
2427       throw new IOException(e.getMessage(), e);
2428     }
2429   }
2430 
2431   /**
2432    * Logs an RPC response to the LOG file, producing valid JSON objects for
2433    * client Operations.
2434    * @param param The parameters received in the call.
2435    * @param methodName The name of the method invoked
2436    * @param call The string representation of the call
2437    * @param tag  The tag that will be used to indicate this event in the log.
2438    * @param clientAddress   The address of the client who made this call.
2439    * @param startTime       The time that the call was initiated, in ms.
2440    * @param processingTime  The duration that the call took to run, in ms.
2441    * @param qTime           The duration that the call spent on the queue
2442    *                        prior to being initiated, in ms.
2443    * @param responseSize    The size in bytes of the response buffer.
2444    */
2445   void logResponse(Message param, String methodName, String call, String tag,
2446       String clientAddress, long startTime, int processingTime, int qTime,
2447       long responseSize)
2448           throws IOException {
2449     // base information that is reported regardless of type of call
2450     Map<String, Object> responseInfo = new HashMap<String, Object>();
2451     responseInfo.put("starttimems", startTime);
2452     responseInfo.put("processingtimems", processingTime);
2453     responseInfo.put("queuetimems", qTime);
2454     responseInfo.put("responsesize", responseSize);
2455     responseInfo.put("client", clientAddress);
2456     responseInfo.put("class", server == null? "": server.getClass().getSimpleName());
2457     responseInfo.put("method", methodName);
2458     responseInfo.put("call", call);
2459     // The params could be really big, make sure they don't kill us at WARN
2460     String stringifiedParam = ProtobufUtil.getShortTextFormat(param);
2461     if (stringifiedParam.length() > 150) {
2462       // Truncate to 1000 chars if TRACE is on, else to 150 chars
2463       stringifiedParam = truncateTraceLog(stringifiedParam);
2464     }
2465     responseInfo.put("param", stringifiedParam);
2466     if (param instanceof ClientProtos.ScanRequest && rsRpcServices != null) {
2467       ClientProtos.ScanRequest request = ((ClientProtos.ScanRequest) param);
2468       if (request.hasScannerId()) {
2469         long scannerId = request.getScannerId();
2470         String scanDetails = rsRpcServices.getScanDetailsWithId(scannerId);
2471         if (scanDetails != null) {
2472           responseInfo.put("scandetails", scanDetails);
2473         }
2474       }
2475     }
2476     LOG.warn("(response" + tag + "): " + MAPPER.writeValueAsString(responseInfo));
2477   }
2478 
2479   /** Stops the service.  No new calls will be handled after this is called. */
2480   @Override
2481   public synchronized void stop() {
2482     LOG.info("Stopping server on " + port);
2483     running = false;
2484     if (authTokenSecretMgr != null) {
2485       authTokenSecretMgr.stop();
2486       authTokenSecretMgr = null;
2487     }
2488     listener.interrupt();
2489     listener.doStop();
2490     responder.interrupt();
2491     scheduler.stop();
2492     notifyAll();
2493   }
2494 
2495   /** Wait for the server to be stopped.
2496    * Does not wait for all subthreads to finish.
2497    *  See {@link #stop()}.
2498    * @throws InterruptedException e
2499    */
2500   @Override
2501   public synchronized void join() throws InterruptedException {
2502     while (running) {
2503       wait();
2504     }
2505   }
2506 
2507   /**
2508    * Return the socket (ip+port) on which the RPC server is listening to. May return null if
2509    * the listener channel is closed.
2510    * @return the socket (ip+port) on which the RPC server is listening to, or null if this
2511    * information cannot be determined
2512    */
2513   @Override
2514   public synchronized InetSocketAddress getListenerAddress() {
2515     if (listener == null) {
2516       return null;
2517     }
2518     return listener.getAddress();
2519   }
2520 
2521   /**
2522    * Truncate to number of chars decided by conf hbase.ipc.trace.log.max.length
2523    * if TRACE is on else to 150 chars Refer to Jira HBASE-20826 and HBASE-20942
2524    * @param strParam stringifiedParam to be truncated
2525    * @return truncated trace log string
2526    */
2527   @VisibleForTesting
2528   String truncateTraceLog(String strParam) {
2529     if (LOG.isTraceEnabled()) {
2530       int traceLogMaxLength = getConf().getInt(TRACE_LOG_MAX_LENGTH, DEFAULT_TRACE_LOG_MAX_LENGTH);
2531       int truncatedLength =
2532           strParam.length() < traceLogMaxLength ? strParam.length() : traceLogMaxLength;
2533       String truncatedFlag = truncatedLength == strParam.length() ? "" : KEY_WORD_TRUNCATED;
2534       return strParam.subSequence(0, truncatedLength) + truncatedFlag;
2535     }
2536     return strParam.subSequence(0, 150) + KEY_WORD_TRUNCATED;
2537   }
2538 
2539   /**
2540    * Set the handler for calling out of RPC for error conditions.
2541    * @param handler the handler implementation
2542    */
2543   @Override
2544   public void setErrorHandler(HBaseRPCErrorHandler handler) {
2545     this.errorHandler = handler;
2546   }
2547 
2548   @Override
2549   public HBaseRPCErrorHandler getErrorHandler() {
2550     return this.errorHandler;
2551   }
2552 
2553   /**
2554    * Returns the metrics instance for reporting RPC call statistics
2555    */
2556   @Override
2557   public MetricsHBaseServer getMetrics() {
2558     return metrics;
2559   }
2560 
2561   @Override
2562   public void addCallSize(final long diff) {
2563     this.callQueueSize.add(diff);
2564   }
2565 
2566   /**
2567    * Authorize the incoming client connection.
2568    *
2569    * @param user client user
2570    * @param connection incoming connection
2571    * @param addr InetAddress of incoming connection
2572    * @throws org.apache.hadoop.security.authorize.AuthorizationException
2573    *         when the client isn't authorized to talk the protocol
2574    */
2575   public synchronized void authorize(UserGroupInformation user, ConnectionHeader connection,
2576       InetAddress addr)
2577   throws AuthorizationException {
2578     if (authorize) {
2579       Class<?> c = getServiceInterface(services, connection.getServiceName());
2580       this.authManager.authorize(user != null ? user : null, c, getConf(), addr);
2581     }
2582   }
2583 
2584   /**
2585    * When the read or write buffer size is larger than this limit, i/o will be
2586    * done in chunks of this size. Most RPC requests and responses would be
2587    * be smaller.
2588    */
2589   private static int NIO_BUFFER_LIMIT = 64 * 1024; //should not be more than 64KB.
2590 
2591   /**
2592    * This is a wrapper around {@link java.nio.channels.WritableByteChannel#write(java.nio.ByteBuffer)}.
2593    * If the amount of data is large, it writes to channel in smaller chunks.
2594    * This is to avoid jdk from creating many direct buffers as the size of
2595    * buffer increases. This also minimizes extra copies in NIO layer
2596    * as a result of multiple write operations required to write a large
2597    * buffer.
2598    *
2599    * @param channel writable byte channel to write to
2600    * @param bufferChain Chain of buffers to write
2601    * @return number of bytes written
2602    * @throws java.io.IOException e
2603    * @see java.nio.channels.WritableByteChannel#write(java.nio.ByteBuffer)
2604    */
2605   protected long channelWrite(GatheringByteChannel channel, BufferChain bufferChain)
2606   throws IOException {
2607     long count =  bufferChain.write(channel, NIO_BUFFER_LIMIT);
2608     if (count > 0) this.metrics.sentBytes(count);
2609     return count;
2610   }
2611 
2612   /**
2613    * This is a wrapper around {@link java.nio.channels.ReadableByteChannel#read(java.nio.ByteBuffer)}.
2614    * If the amount of data is large, it writes to channel in smaller chunks.
2615    * This is to avoid jdk from creating many direct buffers as the size of
2616    * ByteBuffer increases. There should not be any performance degredation.
2617    *
2618    * @param channel writable byte channel to write on
2619    * @param buffer buffer to write
2620    * @return number of bytes written
2621    * @throws java.io.IOException e
2622    * @see java.nio.channels.ReadableByteChannel#read(java.nio.ByteBuffer)
2623    */
2624   protected int channelRead(ReadableByteChannel channel,
2625                                    ByteBuffer buffer) throws IOException {
2626 
2627     int count = (buffer.remaining() <= NIO_BUFFER_LIMIT) ?
2628            channel.read(buffer) : channelIO(channel, null, buffer);
2629     if (count > 0) {
2630       metrics.receivedBytes(count);
2631     }
2632     return count;
2633   }
2634 
2635   /**
2636    * Helper for {@link #channelRead(java.nio.channels.ReadableByteChannel, java.nio.ByteBuffer)}
2637    * and {@link #channelWrite(GatheringByteChannel, BufferChain)}. Only
2638    * one of readCh or writeCh should be non-null.
2639    *
2640    * @param readCh read channel
2641    * @param writeCh write channel
2642    * @param buf buffer to read or write into/out of
2643    * @return bytes written
2644    * @throws java.io.IOException e
2645    * @see #channelRead(java.nio.channels.ReadableByteChannel, java.nio.ByteBuffer)
2646    * @see #channelWrite(GatheringByteChannel, BufferChain)
2647    */
2648   private static int channelIO(ReadableByteChannel readCh,
2649                                WritableByteChannel writeCh,
2650                                ByteBuffer buf) throws IOException {
2651 
2652     int originalLimit = buf.limit();
2653     int initialRemaining = buf.remaining();
2654     int ret = 0;
2655 
2656     while (buf.remaining() > 0) {
2657       try {
2658         int ioSize = Math.min(buf.remaining(), NIO_BUFFER_LIMIT);
2659         buf.limit(buf.position() + ioSize);
2660 
2661         ret = (readCh == null) ? writeCh.write(buf) : readCh.read(buf);
2662 
2663         if (ret < ioSize) {
2664           break;
2665         }
2666 
2667       } finally {
2668         buf.limit(originalLimit);
2669       }
2670     }
2671 
2672     int nBytes = initialRemaining - buf.remaining();
2673     return (nBytes > 0) ? nBytes : ret;
2674   }
2675 
2676   /**
2677    * Needed for features such as delayed calls.  We need to be able to store the current call
2678    * so that we can complete it later or ask questions of what is supported by the current ongoing
2679    * call.
2680    * @return An RpcCallContext backed by the currently ongoing call (gotten from a thread local)
2681    */
2682   public static RpcCallContext getCurrentCall() {
2683     return CurCall.get();
2684   }
2685 
2686   public static boolean isInRpcCallContext() {
2687     return CurCall.get() != null;
2688   }
2689 
2690   /**
2691    * Returns the user credentials associated with the current RPC request or
2692    * <code>null</code> if no credentials were provided.
2693    * @return A User
2694    */
2695   public static User getRequestUser() {
2696     RpcCallContext ctx = getCurrentCall();
2697     return ctx == null? null: ctx.getRequestUser();
2698   }
2699 
2700   /**
2701    * Returns the username for any user associated with the current RPC
2702    * request or <code>null</code> if no user is set.
2703    */
2704   public static String getRequestUserName() {
2705     User user = getRequestUser();
2706     return user == null? null: user.getShortName();
2707   }
2708 
2709   /**
2710    * @return Address of remote client if a request is ongoing, else null
2711    */
2712   public static InetAddress getRemoteAddress() {
2713     RpcCallContext ctx = getCurrentCall();
2714     return ctx == null? null: ctx.getRemoteAddress();
2715   }
2716 
2717   /**
2718    * @param serviceName Some arbitrary string that represents a 'service'.
2719    * @param services Available service instances
2720    * @return Matching BlockingServiceAndInterface pair
2721    */
2722   static BlockingServiceAndInterface getServiceAndInterface(
2723       final List<BlockingServiceAndInterface> services, final String serviceName) {
2724     for (BlockingServiceAndInterface bs : services) {
2725       if (bs.getBlockingService().getDescriptorForType().getName().equals(serviceName)) {
2726         return bs;
2727       }
2728     }
2729     return null;
2730   }
2731 
2732   /**
2733    * @param serviceName Some arbitrary string that represents a 'service'.
2734    * @param services Available services and their service interfaces.
2735    * @return Service interface class for <code>serviceName</code>
2736    */
2737   static Class<?> getServiceInterface(
2738       final List<BlockingServiceAndInterface> services,
2739       final String serviceName) {
2740     BlockingServiceAndInterface bsasi =
2741         getServiceAndInterface(services, serviceName);
2742     return bsasi == null? null: bsasi.getServiceInterface();
2743   }
2744 
2745   /**
2746    * @param serviceName Some arbitrary string that represents a 'service'.
2747    * @param services Available services and their service interfaces.
2748    * @return BlockingService that goes with the passed <code>serviceName</code>
2749    */
2750   static BlockingService getService(
2751       final List<BlockingServiceAndInterface> services,
2752       final String serviceName) {
2753     BlockingServiceAndInterface bsasi =
2754         getServiceAndInterface(services, serviceName);
2755     return bsasi == null? null: bsasi.getBlockingService();
2756   }
2757 
2758   static MonitoredRPCHandler getStatus() {
2759     // It is ugly the way we park status up in RpcServer.  Let it be for now.  TODO.
2760     MonitoredRPCHandler status = RpcServer.MONITORED_RPC.get();
2761     if (status != null) {
2762       return status;
2763     }
2764     status = TaskMonitor.get().createRPCStatus(Thread.currentThread().getName());
2765     status.pause("Waiting for a call");
2766     RpcServer.MONITORED_RPC.set(status);
2767     return status;
2768   }
2769 
2770   /** Returns the remote side ip address when invoked inside an RPC
2771    *  Returns null incase of an error.
2772    *  @return InetAddress
2773    */
2774   public static InetAddress getRemoteIp() {
2775     Call call = CurCall.get();
2776     if (call != null && call.connection != null && call.connection.socket != null) {
2777       return call.connection.socket.getInetAddress();
2778     }
2779     return null;
2780   }
2781 
2782 
2783   /**
2784    * A convenience method to bind to a given address and report
2785    * better exceptions if the address is not a valid host.
2786    * @param socket the socket to bind
2787    * @param address the address to bind to
2788    * @param backlog the number of connections allowed in the queue
2789    * @throws BindException if the address can't be bound
2790    * @throws UnknownHostException if the address isn't a valid host name
2791    * @throws IOException other random errors from bind
2792    */
2793   public static void bind(ServerSocket socket, InetSocketAddress address,
2794                           int backlog) throws IOException {
2795     try {
2796       socket.bind(address, backlog);
2797     } catch (BindException e) {
2798       BindException bindException =
2799         new BindException("Problem binding to " + address + " : " +
2800             e.getMessage());
2801       bindException.initCause(e);
2802       throw bindException;
2803     } catch (SocketException e) {
2804       // If they try to bind to a different host's address, give a better
2805       // error message.
2806       if ("Unresolved address".equals(e.getMessage())) {
2807         throw new UnknownHostException("Invalid hostname for server: " +
2808                                        address.getHostName());
2809       }
2810       throw e;
2811     }
2812   }
2813 
2814   @Override
2815   public RpcScheduler getScheduler() {
2816     return scheduler;
2817   }
2818 
2819   @Override
2820   public void setRsRpcServices(RSRpcServices rsRpcServices) {
2821     this.rsRpcServices = rsRpcServices;
2822   }
2823 }