KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sun > ejb > containers > util > MethodMap


1 /*
2  * The contents of this file are subject to the terms
3  * of the Common Development and Distribution License
4  * (the License). You may not use this file except in
5  * compliance with the License.
6  *
7  * You can obtain a copy of the license at
8  * https://glassfish.dev.java.net/public/CDDLv1.0.html or
9  * glassfish/bootstrap/legal/CDDLv1.0.txt.
10  * See the License for the specific language governing
11  * permissions and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL
14  * Header Notice in each file and include the License file
15  * at glassfish/bootstrap/legal/CDDLv1.0.txt.
16  * If applicable, add the following below the CDDL Header,
17  * with the fields enclosed by brackets [] replaced by
18  * you own identifying information:
19  * "Portions Copyrighted [year] [name of copyright owner]"
20  *
21  * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
22  */

23
24 package com.sun.ejb.containers.util;
25
26 import java.util.Map JavaDoc;
27 import java.util.HashMap JavaDoc;
28 import java.util.Set JavaDoc;
29 import java.util.HashSet JavaDoc;
30 import java.util.Iterator JavaDoc;
31 import java.lang.reflect.Method JavaDoc;
32
33 /**
34  * This is an optimized map for resolving java.lang.reflect.Method objects.
35  * Doing a method lookup, even on an unsynchronized Map, can be an
36  * expensive operation, in many cases taking multiple microseconds.
37  * In most situations this overhead is negligible, but it can be noticeable
38  * when performed in the common path of a local ejb invocation, where our
39  * goal is to be as fast as a raw java method call.
40  *
41  * A MethodMap must be created with an existing Map and is immutable after
42  * construction(except for clear()).
43  * It does not support the optional Map operations
44  * put, putAll, and remove. NOTE that these operations could
45  * be implemented but are not necessary at this point since the main use
46  * is for the container's method info, which is invariant after initialization.
47  *
48  * As this is a map for Method objects, null keys are not supported.
49  * This map is unsynchronized.
50  */

51 public final class MethodMap extends HashMap JavaDoc {
52
53     // If bucket size is not specified by caller, this is the number
54
// of buckets per method that will be created.
55
private static final int DEFAULT_BUCKET_MULTIPLIER = 20;
56
57     private int numBuckets_;
58
59     // Sparse array of method info. Each element represents one method
60
// or is null. Array is hashed by a combination of the
61
// method name's hashcode and its parameter length. See
62
// getBucket() below for more details.
63
//
64
// Note that reference equality is not very useful on Method since
65
// it defines the equals() method and each call to Class.getMethods()
66
// returns new Method instances.
67
private MethodInfo[] methodInfo_;
68
69     public MethodMap(Map JavaDoc methodMap) {
70         super(methodMap);
71
72         numBuckets_ = methodMap.size() * DEFAULT_BUCKET_MULTIPLIER;
73
74         buildLookupTable(methodMap);
75     }
76
77     public MethodMap(Map JavaDoc methodMap, int numBuckets) {
78         super(methodMap);
79
80         if( numBuckets <= 0 ) {
81             throw new IllegalArgumentException JavaDoc
82                 ("Invalid value of numBuckets = " + numBuckets);
83         }
84
85         numBuckets_ = numBuckets;
86         buildLookupTable(methodMap);
87     }
88
89     public Object JavaDoc put(Object JavaDoc key, Object JavaDoc value) {
90         throw new UnsupportedOperationException JavaDoc();
91     }
92     public void putAll(Map JavaDoc t) {
93         throw new UnsupportedOperationException JavaDoc();
94     }
95     public Object JavaDoc remove(Object JavaDoc key) {
96         throw new UnsupportedOperationException JavaDoc();
97     }
98
99     public Object JavaDoc get(Object JavaDoc key) {
100        
101         if( key instanceof Method JavaDoc ) {
102             Method JavaDoc m = (Method JavaDoc) key;
103             Class JavaDoc[] paramTypes = m.getParameterTypes();
104             return get(m, paramTypes.length);
105         }
106
107         return null;
108     }
109
110     public Object JavaDoc get(Method JavaDoc m, int numParams) {
111
112         if( methodInfo_ == null ) {
113             return null;
114         } else if( numParams < 0 ) {
115             throw new IllegalStateException JavaDoc
116                 ("invalid numParams = " + numParams);
117         }
118
119         Object JavaDoc value = null;
120
121         MethodInfo methodInfo = methodInfo_[getBucket(m, numParams)];
122         
123         if( methodInfo != null) {
124             // Declaring classes must be the same for methods to be equal.
125
if(methodInfo.declaringClass == m.getDeclaringClass()) {
126                 value = methodInfo.value;
127             }
128         }
129
130         return (value != null) ? value : super.get(m);
131
132     }
133
134     public void clear() {
135
136         if( methodInfo_ != null ) {
137             methodInfo_ = null;
138             super.clear();
139         }
140
141     }
142
143     private void buildLookupTable(Map JavaDoc methodMap) {
144         
145         methodInfo_ = new MethodInfo[numBuckets_];
146
147         Set JavaDoc methods = methodMap.keySet();
148         Set JavaDoc occupied = new HashSet JavaDoc();
149
150         for(Iterator JavaDoc iter = methods.iterator(); iter.hasNext();) {
151             Object JavaDoc nextObj = iter.next();
152             Method JavaDoc next = null;
153
154             if( nextObj == null ) {
155                 throw new IllegalStateException JavaDoc("null keys not supported");
156             } else if( nextObj instanceof Method JavaDoc ) {
157                 next = (Method JavaDoc) nextObj;
158             } else {
159                 throw new IllegalStateException JavaDoc
160                     ("invalid key type = " + nextObj.getClass() +
161                      " key must be of type java.lang.reflect.Method");
162             }
163
164             int bucket = getBucket(next);
165             
166             if( !occupied.contains(new Integer JavaDoc(bucket)) ) {
167
168                 MethodInfo methodInfo = new MethodInfo();
169                 methodInfo.key = next;
170                 methodInfo.value = methodMap.get(next);
171
172                 // cache declaring class so we can avoid the method call
173
// during lookup operation.
174
methodInfo.declaringClass = next.getDeclaringClass();
175
176                 methodInfo_[bucket] = methodInfo;
177               
178                 occupied.add(new Integer JavaDoc(bucket));
179
180             } else {
181                 // there's a clash for this bucket, so null it out and
182
// defer to backing HashMap for results.
183
methodInfo_[bucket] = null;
184             }
185         }
186     }
187     
188     private final int getBucket(Method JavaDoc m) {
189         
190         // note : getParameterTypes is guaranteed to be 0-length array
191
// (as opposed to null) for a method with no arguments.
192
Class JavaDoc[] paramTypes = m.getParameterTypes();
193
194         return getBucket(m, paramTypes.length);
195     }
196
197     private final int getBucket(Method JavaDoc m, int numParams) {
198
199         String JavaDoc methodName = m.getName();
200
201         // The normal Method.hashCode() method makes 5 method calls
202
// and does not cache the result. Here, we use the method name's
203
// hashCode since String.hashCode() makes 0 method calls *and* caches
204
// the result. The tradeoff is that using only method name will
205
// not account for overloaded methods, so we also add the number of
206
// parameters to the calculation. In many cases, the caller
207
// already knows the number of parameters, so it can be passed in
208
// to the lookup. This gives up some encapsulation for
209
// speed. It will result in better performance because
210
// we can skip the call to m.getClass().getParameterTypes(),
211
// which results in multiple method calls and can involve some
212
// expensive copying depending of the types themselves.
213
// Of course, this still won't account for the case where methods
214
// are overloaded with the same number of parameters but different
215
// types. However, the cache miss penalty should be small enough
216
// in this case that it's a fair tradeoff. Adding anything else
217
// to the hashcode calculation will have too large an impact on the
218
// common case.
219

220         int hashCode = methodName.hashCode();
221        
222         // account for negative hashcodes
223
hashCode = (hashCode >= 0) ? hashCode : (hashCode * -1);
224         hashCode = (hashCode > numParams) ?
225             (hashCode - numParams) : (hashCode + numParams);
226         return (hashCode % numBuckets_);
227     }
228
229
230     private class MethodInfo {
231         public Class JavaDoc declaringClass;
232         public Method JavaDoc key;
233         public Object JavaDoc value;
234     }
235 }
236
Popular Tags