001: /**
002: * EasyBeans
003: * Copyright (C) 2006 Bull S.A.S.
004: * Contact: easybeans@ow2.org
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation; either
009: * version 2.1 of the License, or any later version.
010: *
011: * This library is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: *
016: * You should have received a copy of the GNU Lesser General Public
017: * License along with this library; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
019: * USA
020: *
021: * --------------------------------------------------------------------------
022: * $Id: Hash.java 2057 2007-11-21 15:35:32Z benoitf $
023: * --------------------------------------------------------------------------
024: */package org.ow2.easybeans.rpc.util;
025:
026: import java.io.ByteArrayOutputStream;
027: import java.io.DataOutputStream;
028: import java.io.IOException;
029: import java.lang.reflect.Method;
030: import java.security.DigestOutputStream;
031: import java.security.MessageDigest;
032: import java.security.NoSuchAlgorithmException;
033: import java.util.HashMap;
034: import java.util.Map;
035:
036: import org.ow2.easybeans.asm.Type;
037:
038: import org.ow2.util.log.Log;
039: import org.ow2.util.log.LogFactory;
040:
041: /**
042: * Utility class providing some hashing methods on java.lang.reflect.Method or
043: * java.lang.Class.
044: * @author Florent Benoit
045: */
046: public final class Hash {
047:
048: /**
049: * Logger.
050: */
051: private static Log logger = LogFactory.getLog(Hash.class);
052:
053: /**
054: * Length of hash of a method (eight byte sequence).
055: */
056: private static final int BYTES_LENGTH = 8;
057:
058: /**
059: * Mask for hashing algorithm.
060: */
061: private static final int BYTE_MASK = 0xFF;
062:
063: /**
064: * Utility class, no public constructor.
065: */
066: private Hash() {
067:
068: }
069:
070: /**
071: * Computes the hash for a given method.<br>
072: * The method hash to be used for the opnum parameter is a 64-bit (long)
073: * integer computed from the first two 32-bit values of the message digest
074: * of a particular byte stream using the National Institute of Standards and
075: * Technology (NIST) Secure Hash Algorithm (SHA-1). This byte stream
076: * contains a string as if it was written using the
077: * java.io.DataOutput.writeUTF method, consisting of the remote method's
078: * name followed by its method descriptor (see section 4.3.3 of The Java
079: * Virtual Machine Specification (JVMS) for a description of method
080: * descriptors). The 64-bit hash value is the little-endian composition of
081: * an eight byte sequence where the first four bytes are the first 32-bit
082: * value of the message digest in big-endian byte order and the last four
083: * bytes are the second 32-bit value of the message digest in big-endian
084: * byte order.
085: * @param method the given method.
086: * @return the computed hash.
087: * @see <a
088: * href="http://java.sun.com/j2se/1.5.0/docs/guide/rmi/spec/rmi-stubs24.html">Method
089: * hashing of RMI</a>
090: */
091: public static long hashMethod(final Method method) {
092:
093: MessageDigest md;
094: try {
095: md = MessageDigest.getInstance("SHA-1");
096: } catch (NoSuchAlgorithmException e) {
097: throw new IllegalStateException(
098: "Algorithm SHA-1 is not available", e);
099: }
100: ByteArrayOutputStream baos = new ByteArrayOutputStream();
101: DigestOutputStream dos = new DigestOutputStream(baos, md);
102: DataOutputStream das = new DataOutputStream(dos);
103:
104: StringBuilder sb = new StringBuilder();
105: sb.append(method.getName());
106: sb.append(Type.getMethodDescriptor(method));
107: try {
108: das.writeUTF(sb.toString());
109: } catch (IOException e) {
110: throw new IllegalStateException(
111: "Cannot write data for method '" + method.getName()
112: + "'.", e);
113: }
114: try {
115: das.flush();
116: } catch (IOException e) {
117: logger.warn("Cannot flush the stream", e);
118: }
119: try {
120: das.close();
121: } catch (IOException e) {
122: logger.warn("Cannot flush the stream", e);
123: }
124:
125: byte[] digest = md.digest();
126: long hash = 0;
127: int size = Math.min(digest.length, BYTES_LENGTH);
128: for (int i = 0; i < size; i++) {
129: hash += (long) (digest[i] & BYTE_MASK) << (BYTES_LENGTH * i);
130: }
131: return hash;
132: }
133:
134: /**
135: * Gets a map between an hash and its associated method.
136: * @param clz the class to analyze.
137: * @return a map with an hash and its associated method.
138: */
139: public static Map<Long, Method> hashClass(final Class clz) {
140: Map<Long, Method> map = new HashMap<Long, Method>();
141: Method[] methods = clz.getMethods();
142: for (Method m : methods) {
143: map.put(Long.valueOf(hashMethod(m)), m);
144: }
145: return map;
146: }
147:
148: }
|