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 com.google.common.base.Preconditions;
21  import com.google.protobuf.CodedOutputStream;
22  import com.google.protobuf.Message;
23  
24  import java.io.IOException;
25  import java.io.OutputStream;
26  import java.net.ConnectException;
27  import java.net.SocketTimeoutException;
28  import java.nio.ByteBuffer;
29  
30  import org.apache.hadoop.hbase.DoNotRetryIOException;
31  import org.apache.hadoop.hbase.HConstants;
32  import org.apache.hadoop.hbase.classification.InterfaceAudience;
33  import org.apache.hadoop.hbase.exceptions.ConnectionClosingException;
34  import org.apache.hadoop.hbase.net.Address;
35  import org.apache.hadoop.hbase.protobuf.generated.RPCProtos.CellBlockMeta;
36  import org.apache.hadoop.hbase.protobuf.generated.RPCProtos.ExceptionResponse;
37  import org.apache.hadoop.hbase.protobuf.generated.RPCProtos.RequestHeader;
38  import org.apache.hadoop.hbase.protobuf.generated.TracingProtos.RPCTInfo;
39  import org.apache.hadoop.hbase.util.Bytes;
40  import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
41  import org.apache.hadoop.ipc.RemoteException;
42  
43  /**
44   * Utility to help ipc'ing.
45   */
46  @InterfaceAudience.Private
47  class IPCUtil {
48  
49    /**
50     * Write out header, param, and cell block if there is one.
51     * @param dos Stream to write into
52     * @param header to write
53     * @param param to write
54     * @param cellBlock to write
55     * @return Total number of bytes written.
56     * @throws IOException if write action fails
57     */
58    public static int write(final OutputStream dos, final Message header, final Message param,
59        final ByteBuffer cellBlock) throws IOException {
60      // Must calculate total size and write that first so other side can read it all in in one
61      // swoop. This is dictated by how the server is currently written. Server needs to change
62      // if we are to be able to write without the length prefixing.
63      int totalSize = IPCUtil.getTotalSizeWhenWrittenDelimited(header, param);
64      if (cellBlock != null) {
65        totalSize += cellBlock.remaining();
66      }
67      return write(dos, header, param, cellBlock, totalSize);
68    }
69  
70    private static int write(final OutputStream dos, final Message header, final Message param,
71        final ByteBuffer cellBlock, final int totalSize) throws IOException {
72      // I confirmed toBytes does same as DataOutputStream#writeInt.
73      dos.write(Bytes.toBytes(totalSize));
74      // This allocates a buffer that is the size of the message internally.
75      header.writeDelimitedTo(dos);
76      if (param != null) {
77        param.writeDelimitedTo(dos);
78      }
79      if (cellBlock != null) {
80        dos.write(cellBlock.array(), 0, cellBlock.remaining());
81      }
82      dos.flush();
83      return totalSize;
84    }
85  
86    /**
87     * @return Size on the wire when the two messages are written with writeDelimitedTo
88     */
89    public static int getTotalSizeWhenWrittenDelimited(Message... messages) {
90      int totalSize = 0;
91      for (Message m : messages) {
92        if (m == null) {
93          continue;
94        }
95        totalSize += m.getSerializedSize();
96        totalSize += CodedOutputStream.computeRawVarint32Size(m.getSerializedSize());
97      }
98      Preconditions.checkArgument(totalSize < Integer.MAX_VALUE);
99      return totalSize;
100   }
101 
102   static RequestHeader buildRequestHeader(Call call, CellBlockMeta cellBlockMeta) {
103     RequestHeader.Builder builder = RequestHeader.newBuilder();
104     builder.setCallId(call.id);
105     if (call.span != null) {
106       builder.setTraceInfo(RPCTInfo.newBuilder().setParentId(call.span.getSpanId())
107           .setTraceId(call.span.getTraceId()));
108     }
109     builder.setMethodName(call.md.getName());
110     builder.setRequestParam(call.param != null);
111     if (cellBlockMeta != null) {
112       builder.setCellBlockMeta(cellBlockMeta);
113     }
114     // Only pass priority if there is one set.
115     if (call.priority != HConstants.PRIORITY_UNSET) {
116       builder.setPriority(call.priority);
117     }
118     builder.setTimeout(call.timeout);
119 
120     return builder.build();
121   }
122 
123   /**
124    * @param e exception to be wrapped
125    * @return RemoteException made from passed <code>e</code>
126    */
127   static RemoteException createRemoteException(final ExceptionResponse e) {
128     String innerExceptionClassName = e.getExceptionClassName();
129     boolean doNotRetry = e.getDoNotRetry();
130     return e.hasHostname() ?
131     // If a hostname then add it to the RemoteWithExtrasException
132         new RemoteWithExtrasException(innerExceptionClassName, e.getStackTrace(), e.getHostname(),
133             e.getPort(), doNotRetry)
134         : new RemoteWithExtrasException(innerExceptionClassName, e.getStackTrace(), doNotRetry);
135   }
136 
137   /**
138    * @return True if the exception is a fatal connection exception.
139    */
140   static boolean isFatalConnectionException(final ExceptionResponse e) {
141     return e.getExceptionClassName().equals(FatalConnectionException.class.getName());
142   }
143 
144   static IOException toIOE(Throwable t) {
145     if (t instanceof IOException) {
146       return (IOException) t;
147     } else {
148       return new IOException(t);
149     }
150   }
151 
152   /**
153    * Takes an Exception and the address we were trying to connect to and return an IOException with
154    * the input exception as the cause. The new exception provides the stack trace of the place where
155    * the exception is thrown and some extra diagnostics information. If the exception is
156    * ConnectException or SocketTimeoutException, return a new one of the same type; Otherwise return
157    * an IOException.
158    * @param addr target address
159    * @param exception the relevant exception
160    * @return an exception to throw
161    */
162   static IOException wrapException(Address addr, Exception exception) {
163     if (exception instanceof ConnectException) {
164       // connection refused; include the host:port in the error
165       return (ConnectException) new ConnectException(
166           "Call to " + addr + " failed on connection exception: " + exception).initCause(exception);
167     } else if (exception instanceof SocketTimeoutException) {
168       return (SocketTimeoutException) new SocketTimeoutException(
169           "Call to " + addr + " failed because " + exception).initCause(exception);
170     } else if (exception instanceof ConnectionClosingException) {
171       return new ConnectionClosingException("Call to " + addr + " failed on local exception: "
172           + exception, exception);
173     } else if (exception instanceof ServerTooBusyException) {
174       // we already have address in the exception message
175       return (IOException) exception;
176     } else if (exception instanceof DoNotRetryIOException) {
177       return new DoNotRetryIOException(
178           "Call to " + addr + " failed on local exception: " + exception, exception);
179     } else {
180       return new IOException(
181           "Call to " + addr + " failed on local exception: " + exception, exception);
182     }
183   }
184 
185   static void setCancelled(Call call) {
186     call.setException(new CallCancelledException("Call id=" + call.id + ", waitTime=" +
187         (EnvironmentEdgeManager.currentTime() - call.getStartTime()) + ", rpcTimeout=" +
188         call.timeout));
189   }
190 }