001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: *
015: * See the License for the specific language governing permissions and
016: * limitations under the License.
017: */
018:
019: /**
020: * @author Vasily Zakharov
021: * @version $Revision: 1.1.2.2 $
022: */package org.apache.harmony.rmi.common;
023:
024: import java.io.ByteArrayOutputStream;
025: import java.io.DataOutputStream;
026: import java.io.IOException;
027:
028: import java.lang.reflect.Method;
029:
030: import java.security.MessageDigest;
031: import java.security.NoSuchAlgorithmException;
032:
033: import java.util.Iterator;
034: import java.util.SortedMap;
035: import java.util.TreeMap;
036: import java.util.TreeSet;
037:
038: import org.apache.harmony.rmi.internal.nls.Messages;
039:
040: /**
041: * Generates class and method hash codes necessary for RMI.
042: *
043: * @author Vasily Zakharov
044: * @version $Revision: 1.1.2.2 $
045: */
046: public final class RMIHash {
047:
048: /**
049: * This class cannot be instantiated.
050: */
051: private RMIHash() {
052: }
053:
054: /**
055: * Calculates RMI method hash
056: * as specified in Chapter 8.3 of RMI Specification.
057: *
058: * @param method
059: * Method to calculate RMI hash for.
060: *
061: * @return RMI hash for the specified method.
062: *
063: * @throws RMIHashException
064: * If some error occurs.
065: */
066: public static long getMethodHash(Method method)
067: throws RMIHashException {
068: try {
069: ByteArrayOutputStream buffer = new ByteArrayOutputStream();
070: DataOutputStream dataStream = new DataOutputStream(buffer);
071:
072: dataStream.writeUTF(RMIUtil
073: .getExtendedMethodDescriptor(method));
074: dataStream.close();
075:
076: return getHash(buffer.toByteArray());
077: } catch (IOException e) {
078: // rmi.42=Failed to calculate hash for method {0}
079: throw new RMIHashException(Messages.getString("rmi.42", //$NON-NLS-1$
080: method), e);
081: } catch (NoSuchAlgorithmException e) {
082: // rmi.42=Failed to calculate hash for method {0}
083: throw new RMIHashException(Messages.getString("rmi.42", //$NON-NLS-1$
084: method), e);
085: }
086: }
087:
088: /**
089: * Calculates RMI interface hash
090: * as specified in Chapter 8.3 of RMI Specification.
091: *
092: * @param cls
093: * Class to calculate RMI hash for.
094: *
095: * @return RMI hash for the specified class.
096: *
097: * @throws RMIHashException
098: * If some error occurs.
099: */
100: public static long getInterfaceHash(Class cls)
101: throws RMIHashException {
102: try {
103: return getInterfaceHash(getSortedMethodMap(cls.getMethods()));
104: } catch (RMIHashException e) {
105: // rmi.43=Failed to calculate interface hash for class {0}
106: throw new RMIHashException(Messages.getString("rmi.43", //$NON-NLS-1$
107: cls), e.getCause());
108: }
109: }
110:
111: /**
112: * Calculates RMI interface hash
113: * as specified in Chapter 8.3 of RMI Specification.
114: *
115: * @param methodMap
116: * Map containing methods of the class to calculate hash for.
117: *
118: * @return RMI hash for the specified interface.
119: *
120: * @throws RMIHashException
121: * If some error occurs.
122: */
123: public static long getInterfaceHash(SortedMap methodMap)
124: throws RMIHashException {
125: try {
126: ByteArrayOutputStream buffer = new ByteArrayOutputStream();
127: DataOutputStream dataStream = new DataOutputStream(buffer);
128:
129: dataStream.writeInt(1);
130:
131: for (Iterator i = methodMap.values().iterator(); i
132: .hasNext();) {
133: Method method = (Method) i.next();
134:
135: dataStream.writeUTF(method.getName());
136: dataStream
137: .writeUTF(RMIUtil.getMethodDescriptor(method));
138:
139: Class[] exceptions = method.getExceptionTypes();
140: TreeSet exceptionSet = new TreeSet();
141:
142: for (int j = 0; j < exceptions.length; j++) {
143: exceptionSet.add(exceptions[j].getName());
144: }
145:
146: for (Iterator k = exceptionSet.iterator(); k.hasNext();) {
147: dataStream.writeUTF((String) k.next());
148: }
149: }
150:
151: dataStream.close();
152:
153: return getHash(buffer.toByteArray());
154: } catch (IOException e) {
155: // rmi.44=Failed to calculate interface hash for specified set of methods
156: throw new RMIHashException(Messages.getString("rmi.44"), e); //$NON-NLS-1$
157: } catch (NoSuchAlgorithmException e) {
158: // rmi.44=Failed to calculate interface hash for specified set of methods
159: throw new RMIHashException(Messages.getString("rmi.44"), e); //$NON-NLS-1$
160: }
161: }
162:
163: /**
164: * Moves methods from the specified array to the newly created map
165: * sorting them properly for RMI interface hash calculation.
166: *
167: * @param methods
168: * Methods to sort.
169: *
170: * @return The created method map.
171: */
172: public static SortedMap getSortedMethodMap(Method[] methods) {
173: return getSortedMethodMap(null, methods);
174: }
175:
176: /**
177: * Adds methods from the specified array to the specified map
178: * sorting them properly for RMI interface hash calculation.
179: *
180: * @param methodMap
181: * Map to store sorted methods to.
182: *
183: * @param methods
184: * Methods to sort.
185: *
186: * @return The updated method map.
187: */
188: public static SortedMap getSortedMethodMap(SortedMap methodMap,
189: Method[] methods) {
190: if (methodMap == null) {
191: methodMap = new TreeMap();
192: }
193:
194: for (int i = 0; i < methods.length; i++) {
195: Method method = methods[i];
196: methodMap.put(RMIUtil.getExtendedMethodDescriptor(method),
197: method);
198: }
199:
200: return methodMap;
201: }
202:
203: /**
204: * Calculates RMI hash value for the specified byte array,
205: * as specified in Chapter 8.3 of RMI Specification.
206: *
207: * @param buffer
208: * Byte array to calculate RMI hash for.
209: *
210: * @return RMI hash value for the specified byte array.
211: *
212: * @throws NoSuchAlgorithmException
213: * Should never occur.
214: */
215: private static long getHash(byte[] buffer)
216: throws NoSuchAlgorithmException {
217: byte[] digest = MessageDigest
218: .getInstance("SHA-1").digest(buffer); //$NON-NLS-1$
219:
220: long hash = 0;
221:
222: int length = digest.length;
223:
224: if (length > 8) {
225: length = 8;
226: }
227:
228: for (int i = 0; i < length; i++) {
229: hash += ((long) (digest[i] & 0xff)) << (i * 8);
230: }
231:
232: return hash;
233: }
234: }
|