FileDocCategorySizeDatePackage
MethodMap.javaAPI DocGlassfish v2 API9439Fri May 04 22:33:00 BST 2007com.sun.ejb.containers.util

MethodMap

public final class MethodMap extends HashMap
This is an optimized map for resolving java.lang.reflect.Method objects. Doing a method lookup, even on an unsynchronized Map, can be an expensive operation, in many cases taking multiple microseconds. In most situations this overhead is negligible, but it can be noticeable when performed in the common path of a local ejb invocation, where our goal is to be as fast as a raw java method call. A MethodMap must be created with an existing Map and is immutable after construction(except for clear()). It does not support the optional Map operations put, putAll, and remove. NOTE that these operations could be implemented but are not necessary at this point since the main use is for the container's method info, which is invariant after initialization. As this is a map for Method objects, null keys are not supported. This map is unsynchronized.

Fields Summary
private static final int
DEFAULT_BUCKET_MULTIPLIER
private int
numBuckets_
private MethodInfo[]
methodInfo_
Constructors Summary
public MethodMap(Map methodMap)


       
        super(methodMap);

        numBuckets_ = methodMap.size() * DEFAULT_BUCKET_MULTIPLIER;

        buildLookupTable(methodMap);
    
public MethodMap(Map methodMap, int numBuckets)

        
        super(methodMap);

        if( numBuckets <= 0 ) {
            throw new IllegalArgumentException
                ("Invalid value of numBuckets = " + numBuckets);
        }

        numBuckets_ = numBuckets;
        buildLookupTable(methodMap);
    
Methods Summary
private voidbuildLookupTable(java.util.Map methodMap)

        
        methodInfo_ = new MethodInfo[numBuckets_];

        Set methods  = methodMap.keySet();
        Set occupied = new HashSet();

        for(Iterator iter = methods.iterator(); iter.hasNext();) {
            Object nextObj = iter.next();           
            Method next = null;

            if( nextObj == null ) {
                throw new IllegalStateException("null keys not supported");
            } else if( nextObj instanceof Method ) {
                next = (Method) nextObj;
            } else {
                throw new IllegalStateException
                    ("invalid key type = " + nextObj.getClass() +  
                     " key must be of type java.lang.reflect.Method");
            }

            int bucket = getBucket(next);
            
            if( !occupied.contains(new Integer(bucket)) ) {

                MethodInfo methodInfo = new MethodInfo();
                methodInfo.key = next;
                methodInfo.value = methodMap.get(next);

                // cache declaring class so we can avoid the method call
                // during lookup operation.
                methodInfo.declaringClass = next.getDeclaringClass();

                methodInfo_[bucket] = methodInfo;
              
                occupied.add(new Integer(bucket));

            } else {                
                // there's a clash for this bucket, so null it out and
                // defer to backing HashMap for results.  
                methodInfo_[bucket] = null;
            }            
        }
    
public voidclear()


        if( methodInfo_ != null ) {
            methodInfo_ = null;
            super.clear();            
        }

    
public java.lang.Objectget(java.lang.Object key)

       
        if( key instanceof Method ) {
            Method m = (Method) key;            
            Class[] paramTypes = m.getParameterTypes();
            return get(m, paramTypes.length);
        } 

        return null;
    
public java.lang.Objectget(java.lang.reflect.Method m, int numParams)


        if( methodInfo_ == null ) {
            return null;
        } else if( numParams < 0 ) {
            throw new IllegalStateException
                ("invalid numParams = " + numParams);
        } 

        Object value = null;

        MethodInfo methodInfo = methodInfo_[getBucket(m, numParams)];
        
        if( methodInfo != null) {
            // Declaring classes must be the same for methods to be equal.
            if(methodInfo.declaringClass == m.getDeclaringClass()) { 
                value = methodInfo.value;                                 
            }                
        }

        return (value != null) ? value : super.get(m);

    
private final intgetBucket(java.lang.reflect.Method m)

        
        // note : getParameterTypes is guaranteed to be 0-length array 
        // (as opposed to null) for a method with no arguments.
        Class[] paramTypes = m.getParameterTypes();        

        return getBucket(m, paramTypes.length);
    
private final intgetBucket(java.lang.reflect.Method m, int numParams)


        String methodName = m.getName();     

        // The normal Method.hashCode() method makes 5 method calls
        // and does not cache the result.  Here, we use the method name's
        // hashCode since String.hashCode() makes 0 method calls *and* caches 
        // the result.   The tradeoff is that using only method name will 
        // not account for overloaded methods, so we also add the number of
        // parameters to the calculation.  In many cases, the caller
        // already knows the number of parameters, so it can be passed in
        // to the lookup.  This gives up some encapsulation for 
        // speed.   It will result in better performance because
        // we can skip the call to m.getClass().getParameterTypes(),
        // which results in multiple method calls and can involve some
        // expensive copying depending of the types themselves.
        // Of course, this still won't account for the case where methods
        // are overloaded with the same number of parameters but different
        // types.  However, the cache miss penalty should be small enough
        // in this case that it's a fair tradeoff.  Adding anything else
        // to the hashcode calculation will have too large an impact on the
        // common case.

        int hashCode = methodName.hashCode();
       
        // account for negative hashcodes
        hashCode = (hashCode >= 0) ? hashCode : (hashCode * -1);
        hashCode = (hashCode > numParams) ? 
            (hashCode - numParams) : (hashCode + numParams);
        return (hashCode % numBuckets_);
    
public java.lang.Objectput(java.lang.Object key, java.lang.Object value)

        throw new UnsupportedOperationException();
    
public voidputAll(java.util.Map t)

        throw new UnsupportedOperationException();
    
public java.lang.Objectremove(java.lang.Object key)

        throw new UnsupportedOperationException();