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