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 io.netty.channel.ChannelDuplexHandler;
21  import io.netty.channel.ChannelHandlerContext;
22  import io.netty.channel.ChannelPromise;
23  
24  import java.io.IOException;
25  import java.util.HashMap;
26  import java.util.Map;
27  
28  import org.apache.hadoop.hbase.classification.InterfaceAudience;
29  
30  /**
31   * We will expose the connection to upper layer before initialized, so we need to buffer the calls
32   * passed in and write them out once the connection is established.
33   */
34  @InterfaceAudience.Private
35  class BufferCallBeforeInitHandler extends ChannelDuplexHandler {
36  
37    private enum BufferCallAction {
38      FLUSH, FAIL
39    }
40  
41    public static final class BufferCallEvent {
42  
43      public final BufferCallAction action;
44  
45      public final IOException error;
46  
47      private BufferCallEvent(BufferCallBeforeInitHandler.BufferCallAction action,
48          IOException error) {
49        this.action = action;
50        this.error = error;
51      }
52  
53      public static BufferCallBeforeInitHandler.BufferCallEvent success() {
54        return SUCCESS_EVENT;
55      }
56  
57      public static BufferCallBeforeInitHandler.BufferCallEvent fail(IOException error) {
58        return new BufferCallEvent(BufferCallAction.FAIL, error);
59      }
60    }
61  
62    private static final BufferCallEvent SUCCESS_EVENT = new BufferCallEvent(BufferCallAction.FLUSH,
63        null);
64  
65    private final Map<Integer, Call> id2Call = new HashMap<>();
66  
67    @Override
68    public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) {
69      if (msg instanceof Call) {
70        Call call = (Call) msg;
71        id2Call.put(call.id, call);
72        // The call is already in track so here we set the write operation as success.
73        // We will fail the call directly if we can not write it out.
74        promise.trySuccess();
75      } else {
76        ctx.write(msg, promise);
77      }
78    }
79  
80    @Override
81    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
82      if (evt instanceof BufferCallEvent) {
83        BufferCallEvent bcEvt = (BufferCallBeforeInitHandler.BufferCallEvent) evt;
84        switch (bcEvt.action) {
85          case FLUSH:
86            for (Call call : id2Call.values()) {
87              ctx.write(call);
88            }
89            break;
90          case FAIL:
91            for (Call call : id2Call.values()) {
92              call.setException(bcEvt.error);
93            }
94            break;
95        }
96        ctx.flush();
97        ctx.pipeline().remove(this);
98      } else if (evt instanceof CallEvent) {
99        // just remove the call for now until we add other call event other than timeout and cancel.
100       id2Call.remove(((CallEvent) evt).call.id);
101     } else {
102       ctx.fireUserEventTriggered(evt);
103     }
104   }
105 }