<p dir="auto"><strong><a href="https://jira.spring.io/secure/ViewProfile.jspa?name=kaborka20" rel="nofollow">Moshe Moshe</a></strong> opened <strong><a href="https://jira.spring.io/browse/SPR-7980?redirect=false" rel="nofollow">SPR-7980</a></strong> and commented</p>
<p dir="auto">Hi,</p>
<p dir="auto">It seems that the <code class="notranslate">AdvisedSupport.MethodCacheKey.equals()</code> compares methods by reference and not using <code class="notranslate">Method.equals()</code>.<br>
This means if 2 Methods instances are <code class="notranslate">equals()</code> the <code class="notranslate">AdvisedSupport</code> will add both of them to the <code class="notranslate">methodCache</code> Map.</p>
<p dir="auto">This can be easily recreated if you get the Method instance using reflection <code class="notranslate">Class.getMethod()</code> because it duplicates the method before returning it, meaning each call to <code class="notranslate">Class.getMethod()</code> returns a new instance of <code class="notranslate">Method</code>.</p>
<p dir="auto">please see sample code:</p>
<div class="highlight highlight-source-java notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="static public Object invokeMethod(Object obj, String methodName,
            Object[] args) {

        if (AopUtils.isJdkDynamicProxy(obj)) {
            if (args == null) {
                args = new Object[0];
            }
            int arguments = args.length;
            Class parameterTypes[] = new Class[arguments];
            for (int i = 0; i &lt; arguments; i++) {
                parameterTypes[i] = args[i].getClass();
            }

            Class _targetClass = AopUtils.getTargetClass(obj);
            Class[] _targetInterfaces = _targetClass.getInterfaces();
            Method method = null;
            for (Class _targetInterface : _targetInterfaces) {
                method = org.apache.commons.beanutils.MethodUtils.getMatchingAccessibleMethod(_targetInterface, methodName,
                                parameterTypes);
                if (method != null) {
                    break;
                }
            }
           
            try {
                return java.lang.reflect.Proxy.getInvocationHandler(obj)
                        .invoke(obj, method, args);
            } catch (Throwable e) {
                throw new RuntimeException(e);
            }
        } 
        return null;
    }"><pre class="notranslate"><span class="pl-k">static</span> <span class="pl-k">public</span> <span class="pl-smi">Object</span> <span class="pl-s1">invokeMethod</span>(<span class="pl-smi">Object</span> <span class="pl-s1">obj</span>, <span class="pl-smi">String</span> <span class="pl-s1">methodName</span>,
            <span class="pl-smi">Object</span>[] <span class="pl-s1">args</span>) {

        <span class="pl-k">if</span> (<span class="pl-smi">AopUtils</span>.<span class="pl-en">isJdkDynamicProxy</span>(<span class="pl-s1">obj</span>)) {
            <span class="pl-k">if</span> (<span class="pl-s1">args</span> == <span class="pl-c1">null</span>) {
                <span class="pl-s1">args</span> = <span class="pl-k">new</span> <span class="pl-smi">Object</span>[<span class="pl-c1">0</span>];
            }
            <span class="pl-smi">int</span> <span class="pl-s1">arguments</span> = <span class="pl-s1">args</span>.<span class="pl-s1">length</span>;
            <span class="pl-smi">Class</span> <span class="pl-s1">parameterTypes</span>[] = <span class="pl-k">new</span> <span class="pl-smi">Class</span>[<span class="pl-s1">arguments</span>];
            <span class="pl-k">for</span> (<span class="pl-smi">int</span> <span class="pl-s1">i</span> = <span class="pl-c1">0</span>; <span class="pl-s1">i</span> &lt; <span class="pl-s1">arguments</span>; <span class="pl-s1">i</span>++) {
                <span class="pl-s1">parameterTypes</span>[<span class="pl-s1">i</span>] = <span class="pl-s1">args</span>[<span class="pl-s1">i</span>].<span class="pl-en">getClass</span>();
            }

            <span class="pl-smi">Class</span> <span class="pl-s1">_targetClass</span> = <span class="pl-smi">AopUtils</span>.<span class="pl-en">getTargetClass</span>(<span class="pl-s1">obj</span>);
            <span class="pl-smi">Class</span>[] <span class="pl-s1">_targetInterfaces</span> = <span class="pl-s1">_targetClass</span>.<span class="pl-en">getInterfaces</span>();
            <span class="pl-smi">Method</span> <span class="pl-s1">method</span> = <span class="pl-c1">null</span>;
            <span class="pl-k">for</span> (<span class="pl-smi">Class</span> <span class="pl-s1">_targetInterface</span> : <span class="pl-s1">_targetInterfaces</span>) {
                <span class="pl-s1">method</span> = <span class="pl-s1">org</span>.<span class="pl-s1">apache</span>.<span class="pl-s1">commons</span>.<span class="pl-s1">beanutils</span>.<span class="pl-s1">MethodUtils</span>.<span class="pl-en">getMatchingAccessibleMethod</span>(<span class="pl-s1">_targetInterface</span>, <span class="pl-s1">methodName</span>,
                                <span class="pl-s1">parameterTypes</span>);
                <span class="pl-k">if</span> (<span class="pl-s1">method</span> != <span class="pl-c1">null</span>) {
                    <span class="pl-k">break</span>;
                }
            }
           
            <span class="pl-k">try</span> {
                <span class="pl-k">return</span> <span class="pl-s1">java</span>.<span class="pl-s1">lang</span>.<span class="pl-s1">reflect</span>.<span class="pl-s1">Proxy</span>.<span class="pl-en">getInvocationHandler</span>(<span class="pl-s1">obj</span>)
                        .<span class="pl-en">invoke</span>(<span class="pl-s1">obj</span>, <span class="pl-s1">method</span>, <span class="pl-s1">args</span>);
            } <span class="pl-k">catch</span> (<span class="pl-smi">Throwable</span> <span class="pl-s1">e</span>) {
                <span class="pl-k">throw</span> <span class="pl-k">new</span> <span class="pl-smi">RuntimeException</span>(<span class="pl-s1">e</span>);
            }
        } 
        <span class="pl-k">return</span> <span class="pl-c1">null</span>;
    }</pre></div>
<p dir="auto">If you call this in a loop you can see the <code class="notranslate">methodCache</code> map size growing and growing up to OutOfMemory.</p>
<p dir="auto">I believe that the correct code should be:</p>
<div class="highlight highlight-source-java notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="private static class MethodCacheKey {

	private final Method method;

	private final int hashCode;

	public MethodCacheKey(Method method) {
		this.method = method;
		this.hashCode = method.hashCode();
	}

	public boolean equals(Object other) {
		if (other == this) {
			return true;
		}
	   MethodCacheKey otherKey = (MethodCacheKey) other;
            return this.method == otherKey.method || this.method.equals(otherKey.method);
        }

	public int hashCode() {
		return this.hashCode;
	}
}"><pre class="notranslate"><span class="pl-k">private</span> <span class="pl-k">static</span> <span class="pl-k">class</span> <span class="pl-smi">MethodCacheKey</span> {

	<span class="pl-k">private</span> <span class="pl-k">final</span> <span class="pl-smi">Method</span> <span class="pl-s1">method</span>;

	<span class="pl-k">private</span> <span class="pl-k">final</span> <span class="pl-smi">int</span> <span class="pl-s1">hashCode</span>;

	<span class="pl-k">public</span> <span class="pl-smi">MethodCacheKey</span>(<span class="pl-smi">Method</span> <span class="pl-s1">method</span>) {
		<span class="pl-smi">this</span>.<span class="pl-s1">method</span> = <span class="pl-s1">method</span>;
		<span class="pl-smi">this</span>.<span class="pl-s1">hashCode</span> = <span class="pl-s1">method</span>.<span class="pl-en">hashCode</span>();
	}

	<span class="pl-k">public</span> <span class="pl-smi">boolean</span> <span class="pl-en">equals</span>(<span class="pl-smi">Object</span> <span class="pl-s1">other</span>) {
		<span class="pl-k">if</span> (<span class="pl-s1">other</span> == <span class="pl-smi">this</span>) {
			<span class="pl-k">return</span> <span class="pl-c1">true</span>;
		}
	   <span class="pl-smi">MethodCacheKey</span> <span class="pl-s1">otherKey</span> = (<span class="pl-smi">MethodCacheKey</span>) <span class="pl-s1">other</span>;
            <span class="pl-k">return</span> <span class="pl-smi">this</span>.<span class="pl-s1">method</span> == <span class="pl-s1">otherKey</span>.<span class="pl-s1">method</span> || <span class="pl-smi">this</span>.<span class="pl-s1">method</span>.<span class="pl-en">equals</span>(<span class="pl-s1">otherKey</span>.<span class="pl-s1">method</span>);
        }

	<span class="pl-k">public</span> <span class="pl-smi">int</span> <span class="pl-en">hashCode</span>() {
		<span class="pl-k">return</span> <span class="pl-smi">this</span>.<span class="pl-s1">hashCode</span>;
	}
}</pre></div>
<p dir="auto">Thank you.</p>
<hr>
<p dir="auto"><strong>Affects:</strong> 3.0.5</p>
<p dir="auto"><strong><a href="https://jira.spring.io/secure/ViewProfile.jspa?name=gaplo" rel="nofollow">Gary Ip</a></strong> opened <strong><a href="https://jira.spring.io/browse/SPR-2598?redirect=false" rel="nofollow">SPR-2598</a></strong> and commented</p>
<p dir="auto">Features request for dealing with Dealing with Single PersistenceUnit in Multiple persistence.xml files as stated in Spring forum.<br>
<a href="http://forum.springframework.org/showthread.php?t=29230" rel="nofollow">http://forum.springframework.org/showthread.php?t=29230</a></p>
<p dir="auto">suggestion:</p>
<ol dir="auto">
<li>allow the method preparePersistenceUnitInfos () in org.springframework.orm.jpa.persistenceunit.DefaultPersistenceUnitManager to handle duplicated by adding ManagedClasses to the original one.</li>
<li>or make this method to be easily overrided by opening the "readPersistenceUnitInfos()", etc.</li>
</ol>
<hr>
<p dir="auto"><strong>Affects:</strong> 2.0 RC4</p>
<p dir="auto">2 votes, 4 watchers</p>