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 java.io.DataInput;
22  import java.io.DataOutput;
23  import java.io.IOException;
24  import java.lang.reflect.Method;
25  import java.util.Arrays;
26  import java.util.HashMap;
27  
28  import org.apache.hadoop.classification.InterfaceAudience;
29  import org.apache.hadoop.io.Writable;
30  import org.apache.hadoop.io.WritableFactories;
31  import org.apache.hadoop.io.WritableFactory;
32  
33  @InterfaceAudience.Private
34  public class ProtocolSignature implements Writable {
35    static {               // register a ctor
36      WritableFactories.setFactory
37        (ProtocolSignature.class,
38         new WritableFactory() {
39           public Writable newInstance() { return new ProtocolSignature(); }
40         });
41    }
42  
43    private long version;
44    private int[] methods = null; // an array of method hash codes
45  
46    /**
47     * default constructor
48     */
49    public ProtocolSignature() {
50    }
51  
52    /**
53     * Constructor
54     *
55     * @param version server version
56     * @param methodHashcodes hash codes of the methods supported by server
57     */
58    public ProtocolSignature(long version, int[] methodHashcodes) {
59      this.version = version;
60      this.methods = methodHashcodes;
61    }
62  
63    public long getVersion() {
64      return version;
65    }
66  
67    public int[] getMethods() {
68      return methods;
69    }
70  
71    @Override
72    public void readFields(DataInput in) throws IOException {
73      version = in.readLong();
74      boolean hasMethods = in.readBoolean();
75      if (hasMethods) {
76        int numMethods = in.readInt();
77        methods = new int[numMethods];
78        for (int i=0; i<numMethods; i++) {
79          methods[i] = in.readInt();
80        }
81      }
82    }
83  
84    @Override
85    public void write(DataOutput out) throws IOException {
86      out.writeLong(version);
87      if (methods == null) {
88        out.writeBoolean(false);
89      } else {
90        out.writeBoolean(true);
91        out.writeInt(methods.length);
92        for (int method : methods) {
93          out.writeInt(method);
94        }
95      }
96    }
97  
98    /**
99     * Calculate a method's hash code considering its method
100    * name, returning type, and its parameter types
101    *
102    * @param method a method
103    * @return its hash code
104    */
105   static int getFingerprint(Method method) {
106     int hashcode = method.getName().hashCode();
107     hashcode =  hashcode + 31*method.getReturnType().getName().hashCode();
108     for (Class<?> type : method.getParameterTypes()) {
109       hashcode = 31*hashcode ^ type.getName().hashCode();
110     }
111     return hashcode;
112   }
113 
114   /**
115    * Convert an array of Method into an array of hash codes
116    *
117    * @param methods
118    * @return array of hash codes
119    */
120   private static int[] getFingerprints(Method[] methods) {
121     if (methods == null) {
122       return null;
123     }
124     int[] hashCodes = new int[methods.length];
125     for (int i = 0; i<methods.length; i++) {
126       hashCodes[i] = getFingerprint(methods[i]);
127     }
128     return hashCodes;
129   }
130 
131   /**
132    * Get the hash code of an array of methods
133    * Methods are sorted before hashcode is calculated.
134    * So the returned value is irrelevant of the method order in the array.
135    *
136    * @param methods an array of methods
137    * @return the hash code
138    */
139   static int getFingerprint(Method[] methods) {
140     return getFingerprint(getFingerprints(methods));
141   }
142 
143   /**
144    * Get the hash code of an array of hashcodes
145    * Hashcodes are sorted before hashcode is calculated.
146    * So the returned value is irrelevant of the hashcode order in the array.
147    *
148    * @param methods an array of methods
149    * @return the hash code
150    */
151   static int getFingerprint(int[] hashcodes) {
152     Arrays.sort(hashcodes);
153     return Arrays.hashCode(hashcodes);
154 
155   }
156   private static class ProtocolSigFingerprint {
157     private ProtocolSignature signature;
158     private int fingerprint;
159 
160     ProtocolSigFingerprint(ProtocolSignature sig, int fingerprint) {
161       this.signature = sig;
162       this.fingerprint = fingerprint;
163     }
164   }
165 
166   /**
167    * A cache that maps a protocol's name to its signature & finger print
168    */
169   final private static HashMap<String, ProtocolSigFingerprint> 
170      PROTOCOL_FINGERPRINT_CACHE =
171        new HashMap<String, ProtocolSigFingerprint>();
172 
173   /**
174    * Return a protocol's signature and finger print from cache
175    *
176    * @param protocol a protocol class
177    * @param serverVersion protocol version
178    * @return its signature and finger print
179    */
180   private static ProtocolSigFingerprint getSigFingerprint(
181       Class <? extends VersionedProtocol> protocol, long serverVersion) {
182     String protocolName = protocol.getName();
183     synchronized (PROTOCOL_FINGERPRINT_CACHE) {
184       ProtocolSigFingerprint sig = PROTOCOL_FINGERPRINT_CACHE.get(protocolName);
185       if (sig == null) {
186         int[] serverMethodHashcodes = getFingerprints(protocol.getMethods());
187         sig = new ProtocolSigFingerprint(
188             new ProtocolSignature(serverVersion, serverMethodHashcodes),
189             getFingerprint(serverMethodHashcodes));
190         PROTOCOL_FINGERPRINT_CACHE.put(protocolName, sig);
191       }
192       return sig;
193     }
194   }
195 
196   /**
197    * Get a server protocol's signature
198    *
199    * @param clientMethodsHashCode client protocol methods hashcode
200    * @param serverVersion server protocol version
201    * @param protocol protocol
202    * @return the server's protocol signature
203    */
204   static ProtocolSignature getProtocolSignature(
205       int clientMethodsHashCode,
206       long serverVersion,
207       Class<? extends VersionedProtocol> protocol) {
208     // try to get the finger print & signature from the cache
209     ProtocolSigFingerprint sig = getSigFingerprint(protocol, serverVersion);
210 
211     // check if the client side protocol matches the one on the server side
212     if (clientMethodsHashCode == sig.fingerprint) {
213       return new ProtocolSignature(serverVersion, null);  // null indicates a match
214     }
215 
216     return sig.signature;
217   }
218 
219   /**
220    * Get a server protocol's signature
221    *
222    * @param server server implementation
223    * @param protocol server protocol
224    * @param clientVersion client's version
225    * @param clientMethodsHash client's protocol's hash code
226    * @return the server protocol's signature
227    * @throws IOException if any error occurs
228    */
229   @SuppressWarnings("unchecked")
230   public static ProtocolSignature getProtocolSignature(VersionedProtocol server,
231       String protocol,
232       long clientVersion, int clientMethodsHash) throws IOException {
233     Class<? extends VersionedProtocol> inter;
234     try {
235       inter = (Class<? extends VersionedProtocol>)Class.forName(protocol);
236     } catch (Exception e) {
237       throw new IOException(e);
238     }
239     long serverVersion = server.getProtocolVersion(protocol, clientVersion);
240     return ProtocolSignature.getProtocolSignature(
241         clientMethodsHash, serverVersion, inter);
242   }
243 }