View Javadoc

1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package org.apache.hadoop.hbase.ipc;
19  
20  import java.io.IOException;
21  
22  import org.apache.hadoop.hbase.CellScanner;
23  import org.apache.hadoop.hbase.classification.InterfaceAudience;
24  import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
25  import org.apache.hadoop.hbase.protobuf.generated.RPCProtos;
26  import org.apache.hadoop.ipc.RemoteException;
27  
28  import com.google.protobuf.Message;
29  
30  import io.netty.buffer.ByteBuf;
31  import io.netty.buffer.ByteBufInputStream;
32  import io.netty.channel.ChannelHandlerContext;
33  import io.netty.channel.SimpleChannelInboundHandler;
34  
35  /**
36   * Handles Hbase responses
37   */
38  @InterfaceAudience.Private
39  public class AsyncServerResponseHandler extends SimpleChannelInboundHandler<ByteBuf> {
40    private final AsyncRpcChannel channel;
41  
42    /**
43     * Constructor
44     * @param channel on which this response handler operates
45     */
46    public AsyncServerResponseHandler(AsyncRpcChannel channel) {
47      this.channel = channel;
48    }
49  
50    @Override
51    protected void channelRead0(ChannelHandlerContext ctx, ByteBuf inBuffer) throws Exception {
52      ByteBufInputStream in = new ByteBufInputStream(inBuffer);
53      int totalSize = inBuffer.readableBytes();
54      // Read the header
55      RPCProtos.ResponseHeader responseHeader = RPCProtos.ResponseHeader.parseDelimitedFrom(in);
56      int id = responseHeader.getCallId();
57      AsyncCall call = channel.removePendingCall(id);
58      if (call == null) {
59        // So we got a response for which we have no corresponding 'call' here on the client-side.
60        // We probably timed out waiting, cleaned up all references, and now the server decides
61        // to return a response. There is nothing we can do w/ the response at this stage. Clean
62        // out the wire of the response so its out of the way and we can get other responses on
63        // this connection.
64        int readSoFar = IPCUtil.getTotalSizeWhenWrittenDelimited(responseHeader);
65        int whatIsLeftToRead = totalSize - readSoFar;
66  
67        // This is done through a Netty ByteBuf which has different behavior than InputStream.
68        // It does not return number of bytes read but will update pointer internally and throws an
69        // exception when too many bytes are to be skipped.
70        inBuffer.skipBytes(whatIsLeftToRead);
71        return;
72      }
73  
74      if (responseHeader.hasException()) {
75        RPCProtos.ExceptionResponse exceptionResponse = responseHeader.getException();
76        RemoteException re = createRemoteException(exceptionResponse);
77        if (exceptionResponse.getExceptionClassName()
78            .equals(FatalConnectionException.class.getName())) {
79          channel.close(re);
80        } else {
81          call.setFailed(re);
82        }
83      } else {
84        Message value = null;
85        // Call may be null because it may have timedout and been cleaned up on this side already
86        if (call.responseDefaultType != null) {
87          Message.Builder builder = call.responseDefaultType.newBuilderForType();
88          ProtobufUtil.mergeDelimitedFrom(builder, in);
89          value = builder.build();
90        }
91        CellScanner cellBlockScanner = null;
92        if (responseHeader.hasCellBlockMeta()) {
93          int size = responseHeader.getCellBlockMeta().getLength();
94          byte[] cellBlock = new byte[size];
95          inBuffer.readBytes(cellBlock, 0, cellBlock.length);
96          cellBlockScanner = channel.client.createCellScanner(cellBlock);
97        }
98        call.setSuccess(value, cellBlockScanner);
99        call.callStats.setResponseSizeBytes(totalSize);
100     }
101   }
102 
103   @Override
104   public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
105     channel.close(cause);
106   }
107 
108   @Override
109   public void channelInactive(ChannelHandlerContext ctx) throws Exception {
110     channel.close(new IOException("connection closed"));
111   }
112 
113   /**
114    * @param e Proto exception
115    * @return RemoteException made from passed <code>e</code>
116    */
117   private RemoteException createRemoteException(final RPCProtos.ExceptionResponse e) {
118     String innerExceptionClassName = e.getExceptionClassName();
119     boolean doNotRetry = e.getDoNotRetry();
120     return e.hasHostname() ?
121         // If a hostname then add it to the RemoteWithExtrasException
122         new RemoteWithExtrasException(innerExceptionClassName, e.getStackTrace(), e.getHostname(),
123             e.getPort(), doNotRetry)
124         : new RemoteWithExtrasException(innerExceptionClassName, e.getStackTrace(), doNotRetry);
125   }
126 }