View Javadoc

1   /*
2    *
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *     http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   */
19  package org.apache.hadoop.hbase.ipc;
20  
21  import java.io.DataInput;
22  import java.io.DataOutput;
23  import java.io.IOException;
24  import java.lang.reflect.Field;
25  import java.lang.reflect.Method;
26  import java.util.HashMap;
27  import java.util.HashSet;
28  import java.util.Map;
29  import java.util.Set;
30  
31  import org.apache.hadoop.classification.InterfaceAudience;
32  import org.apache.hadoop.conf.Configurable;
33  import org.apache.hadoop.conf.Configuration;
34  import org.apache.hadoop.hbase.client.AdminProtocol;
35  import org.apache.hadoop.hbase.client.ClientProtocol;
36  import org.apache.hadoop.hbase.MasterMonitorProtocol;
37  import org.apache.hadoop.hbase.MasterAdminProtocol;
38  import org.apache.hadoop.hbase.io.HbaseObjectWritable;
39  import org.apache.hadoop.hbase.protobuf.generated.AdminProtos.AdminService;
40  import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.ClientService;
41  import org.apache.hadoop.hbase.protobuf.generated.RegionServerStatusProtos.RegionServerStatusService;
42  import org.apache.hadoop.hbase.RegionServerStatusProtocol;
43  import org.apache.hadoop.io.VersionMismatchException;
44  import org.apache.hadoop.io.VersionedWritable;
45  
46  /** A method invocation, including the method name and its parameters.*/
47  @InterfaceAudience.Private
48  public class Invocation extends VersionedWritable implements Configurable {
49    protected String methodName;
50    @SuppressWarnings("rawtypes")
51    protected Class[] parameterClasses;
52    protected Object[] parameters;
53    protected Configuration conf;
54    private long clientVersion;
55    private int clientMethodsHash;
56  
57    // For generated protocol classes which don't have VERSION field,
58    // such as protobuf interfaces.
59    static final Map<Class<?>, Long>
60      PROTOCOL_VERSION = new HashMap<Class<?>, Long>();
61  
62    static {
63      PROTOCOL_VERSION.put(ClientService.BlockingInterface.class,
64        Long.valueOf(ClientProtocol.VERSION));
65      PROTOCOL_VERSION.put(AdminService.BlockingInterface.class,
66        Long.valueOf(AdminProtocol.VERSION));
67      PROTOCOL_VERSION.put(RegionServerStatusService.BlockingInterface.class,
68        Long.valueOf(RegionServerStatusProtocol.VERSION));
69      PROTOCOL_VERSION.put(MasterMonitorProtocol.class,Long.valueOf(MasterMonitorProtocol.VERSION));
70      PROTOCOL_VERSION.put(MasterAdminProtocol.class,Long.valueOf(MasterAdminProtocol.VERSION));
71    }
72  
73    // For protobuf protocols, which use ServiceException, instead of IOException
74    protected static final Set<Class<?>>
75      PROTOBUF_PROTOCOLS = new HashSet<Class<?>>();
76  
77    static {
78      PROTOBUF_PROTOCOLS.add(ClientProtocol.class);
79      PROTOBUF_PROTOCOLS.add(AdminProtocol.class);
80      PROTOBUF_PROTOCOLS.add(RegionServerStatusProtocol.class);
81      PROTOBUF_PROTOCOLS.add(MasterMonitorProtocol.class);
82      PROTOBUF_PROTOCOLS.add(MasterAdminProtocol.class);
83    }
84  
85    private static byte RPC_VERSION = 1;
86  
87    public Invocation() {}
88  
89    public Invocation(Method method, Object[] parameters) {
90      this.methodName = method.getName();
91      this.parameterClasses = method.getParameterTypes();
92      this.parameters = parameters;
93      Class<?> declaringClass = method.getDeclaringClass();
94      if (declaringClass.equals(VersionedProtocol.class)) {
95        //VersionedProtocol is exempted from version check.
96        clientVersion = 0;
97        clientMethodsHash = 0;
98      } else {
99        try {
100         Long version = PROTOCOL_VERSION.get(declaringClass);
101         if (version != null) {
102           this.clientVersion = version.longValue();
103         } else {
104           Field versionField = declaringClass.getField("VERSION");
105           versionField.setAccessible(true);
106           this.clientVersion = versionField.getLong(declaringClass);
107         }
108       } catch (NoSuchFieldException ex) {
109         throw new RuntimeException("The " + declaringClass, ex);
110       } catch (IllegalAccessException ex) {
111         throw new RuntimeException(ex);
112       }
113       this.clientMethodsHash = ProtocolSignature.getFingerprint(
114         declaringClass.getMethods());
115     }
116   }
117 
118   /** @return The name of the method invoked. */
119   public String getMethodName() { return methodName; }
120 
121   /** @return The parameter classes. */
122   @SuppressWarnings({ "rawtypes" })
123   public Class[] getParameterClasses() { return parameterClasses; }
124 
125   /** @return The parameter instances. */
126   public Object[] getParameters() { return parameters; }
127 
128   long getProtocolVersion() {
129     return clientVersion;
130   }
131 
132   protected int getClientMethodsHash() {
133     return clientMethodsHash;
134   }
135 
136   /**
137    * Returns the rpc version used by the client.
138    * @return rpcVersion
139    */
140   public long getRpcVersion() {
141     return RPC_VERSION;
142   }
143 
144   public void readFields(DataInput in) throws IOException {
145     try {
146       super.readFields(in);
147       methodName = in.readUTF();
148       clientVersion = in.readLong();
149       clientMethodsHash = in.readInt();
150     } catch (VersionMismatchException e) {
151       // VersionMismatchException doesn't provide an API to access
152       // expectedVersion and foundVersion.  This is really sad.
153       if (e.toString().endsWith("found v0")) {
154         // Try to be a bit backwards compatible.  In previous versions of
155         // HBase (before HBASE-3939 in 0.92) Invocation wasn't a
156         // VersionedWritable and thus the first thing on the wire was always
157         // the 2-byte length of the method name.  Because no method name is
158         // longer than 255 characters, and all method names are in ASCII,
159         // The following code is equivalent to `in.readUTF()', which we can't
160         // call again here, because `super.readFields(in)' already consumed
161         // the first byte of input, which can't be "unread" back into `in'.
162         final short len = (short) (in.readByte() & 0xFF);  // Unsigned byte.
163         final byte[] buf = new byte[len];
164         in.readFully(buf, 0, len);
165         methodName = new String(buf);
166       }
167     }
168     parameters = new Object[in.readInt()];
169     parameterClasses = new Class[parameters.length];
170     HbaseObjectWritable objectWritable = new HbaseObjectWritable();
171     for (int i = 0; i < parameters.length; i++) {
172       parameters[i] = HbaseObjectWritable.readObject(in, objectWritable,
173         this.conf);
174       parameterClasses[i] = objectWritable.getDeclaredClass();
175     }
176   }
177 
178   public void write(DataOutput out) throws IOException {
179     super.write(out);
180     out.writeUTF(this.methodName);
181     out.writeLong(clientVersion);
182     out.writeInt(clientMethodsHash);
183     out.writeInt(parameterClasses.length);
184     for (int i = 0; i < parameterClasses.length; i++) {
185       HbaseObjectWritable.writeObject(out, parameters[i], parameterClasses[i],
186                                  conf);
187     }
188   }
189 
190   @Override
191   public String toString() {
192     StringBuilder buffer = new StringBuilder(256);
193     buffer.append(methodName);
194     buffer.append("(");
195     for (int i = 0; i < parameters.length; i++) {
196       if (i != 0)
197         buffer.append(", ");
198       buffer.append(parameters[i]);
199     }
200     buffer.append(")");
201     buffer.append(", rpc version="+RPC_VERSION);
202     buffer.append(", client version="+clientVersion);
203     buffer.append(", methodsFingerPrint="+clientMethodsHash);
204     return buffer.toString();
205   }
206 
207   public void setConf(Configuration conf) {
208     this.conf = conf;
209   }
210 
211   public Configuration getConf() {
212     return this.conf;
213   }
214 
215   @Override
216   public byte getVersion() {
217     return RPC_VERSION;
218   }
219 }