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