/******************************************************************************* * Copyright (c) 2005 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ /* */ package org.eclipse.jem.internal.proxy.remote; import java.util.*; import java.util.Map.Entry; import org.eclipse.jem.internal.proxy.core.*; import org.eclipse.jem.internal.proxy.core.ExpressionProxy.ProxyEvent; /** * MethodProxyConstants is a cache of IMethodProxies to avoid repeated lookup * and thereby avoid the relatively expensive java.lang.reflect calls to repeatedly * lookup method by name * * @since 1.1.0 */ public class REMProxyConstants { private Map methodsCache = new HashMap(80); private Map invokablesCache = new HashMap(80); private Map fieldsCache = new HashMap(80); private REMProxyFactoryRegistry registry; public REMProxyConstants(REMProxyFactoryRegistry registry) { this.registry = registry; } /* * Used as the key to the methodCache and invokablesCache when there are parms. * It allows the parms to be either strings or IBeanTypeProxies without the * overhead of creating complicated strings. * * It will compare method name and each individual parm name without fluffing * up a string and building it up. * * For no parm methods, just the name of the method as a string will be the key. * * @since 1.1.0 */ private abstract static class MethodKey { public String methodName; public MethodKey(String methodName) { this.methodName = methodName; } protected abstract boolean compareParms(IProxyBeanType[] parms); protected abstract boolean compareParms(String[] parms); /* (non-Javadoc) * @see java.lang.Object#hashCode() */ public int hashCode() { return methodName.hashCode(); } } private static class MethodKeyStringParms extends MethodKey { public String[] parmNames; public MethodKeyStringParms(String methodName, String[] parmNames) { super(methodName); this.parmNames = parmNames; } /* (non-Javadoc) * @see java.lang.Object#equals(java.lang.Object) */ public boolean equals(Object obj) { if (this == obj) return true; try { return ((MethodKey) obj).compareParms(parmNames); } catch (ClassCastException e) { return false; } } /* (non-Javadoc) * @see org.eclipse.jem.internal.proxy.remote.REMProxyConstants.MethodKey#hashCode() */ public int hashCode() { int h = super.hashCode(); for (int i = 0; i < parmNames.length; i++) { h += parmNames[i].hashCode(); } return h; } /* (non-Javadoc) * @see org.eclipse.jem.internal.proxy.remote.REMProxyConstants.MethodKey#compareParms(org.eclipse.jem.internal.proxy.core.IBeanTypeProxy[]) */ protected boolean compareParms(IProxyBeanType[] parms) { if (parms.length != parmNames.length) return false; for (int i = 0; i < parms.length; i++) { if (!parms[i].getTypeName().equals(parmNames[i])) return false; } return true; } /* (non-Javadoc) * @see org.eclipse.jem.internal.proxy.remote.REMProxyConstants.MethodKey#compareParms(java.lang.String[]) */ protected boolean compareParms(String[] parms) { return Arrays.equals(parms, parmNames); } } private static class MethodKeyProxyParms extends MethodKey { public IProxyBeanType[] parmTypes; public MethodKeyProxyParms(String methodName, IProxyBeanType[] parmTypes) { super(methodName); this.parmTypes = parmTypes; } public Object toMethodKeyStringParms() { String[] parms = new String[parmTypes.length]; for (int i = 0; i < parmTypes.length; i++) { parms[i] = parmTypes[i].getTypeName(); } return new MethodKeyStringParms(methodName, parms); } /* (non-Javadoc) * @see java.lang.Object#equals(java.lang.Object) */ public boolean equals(Object obj) { if (this == obj) return true; try { return ((MethodKey) obj).compareParms(parmTypes); } catch (ClassCastException e) { return false; } } /* (non-Javadoc) * @see org.eclipse.jem.internal.proxy.remote.REMProxyConstants.MethodKey#hashCode() */ public int hashCode() { int h = super.hashCode(); for (int i = 0; i < parmTypes.length; i++) { h += parmTypes[i].getTypeName().hashCode(); } return h; } /* (non-Javadoc) * @see org.eclipse.jem.internal.proxy.remote.REMProxyConstants.MethodKey#compareParms(org.eclipse.jem.internal.proxy.core.IBeanTypeProxy[]) */ protected boolean compareParms(String[] parms) { if (parms.length != parmTypes.length) return false; for (int i = 0; i < parms.length; i++) { if (!parmTypes[i].getTypeName().equals(parms[i])) return false; } return true; } /* (non-Javadoc) * @see org.eclipse.jem.internal.proxy.remote.REMProxyConstants.MethodKey#compareParms(java.lang.String[]) */ protected boolean compareParms(IProxyBeanType[] parms) { if (parms.length != parmTypes.length) return false; for (int i = 0; i < parms.length; i++) { if (!parmTypes[i].getTypeName().equals(parms[i].getTypeName())) return false; } return true; } } static int REMMETHODCOUNT = 0; static int UNIQUEMETHODCOUNT = 0; static int REMINVOKABLECOUNT = 0; static int UNIQUEINVOKABLECOUNT = 0; static int INVOKEINVOKECOUNT = 0; static int METHODPROXYINVOKECOUNT = 0; static int REMFIELDCOUNT = 0; static int UNIQUEFIELDCOUNT = 0; static int REMCONSTRUCTORCALLED = 0; static HashMap METHODCOUNTMAP; static HashMap FIELDCOUNTMAP; static HashMap FIELDSETCOUNTMAP; static boolean GATHER_COUNTS; /** * Set if counts should be gathered. * * @param gatherCounts * * @since 1.1.0 */ public static void setGatherCounts(boolean gatherCounts) { if (gatherCounts != GATHER_COUNTS) { reset(); if (gatherCounts) { if (METHODCOUNTMAP == null) { METHODCOUNTMAP = new HashMap(); FIELDCOUNTMAP = new HashMap(); FIELDSETCOUNTMAP = new HashMap(); } } GATHER_COUNTS = gatherCounts; } } public static void reset(){ REMMETHODCOUNT = UNIQUEMETHODCOUNT = REMINVOKABLECOUNT = UNIQUEINVOKABLECOUNT = REMCONSTRUCTORCALLED = METHODPROXYINVOKECOUNT = INVOKEINVOKECOUNT = REMFIELDCOUNT = UNIQUEFIELDCOUNT = 0; if (GATHER_COUNTS) { METHODCOUNTMAP.clear(); FIELDCOUNTMAP.clear(); FIELDSETCOUNTMAP.clear(); } } public static void println(){ if (GATHER_COUNTS) { System.out.println("--------------------------------------------------"); //$NON-NLS-1$ System.out.println("Method proxies invokes = " + METHODPROXYINVOKECOUNT); //$NON-NLS-1$ System.out.println("Invoke invokes = " + INVOKEINVOKECOUNT); //$NON-NLS-1$ System.out.println(".................................................."); //$NON-NLS-1$ System.out.println("Methods retrieved = " + REMMETHODCOUNT + "(" + UNIQUEMETHODCOUNT + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ System.out.println("Invokes retrieved = " + REMINVOKABLECOUNT + "(" + UNIQUEINVOKABLECOUNT + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ System.out.println("Fields retrieved = " + REMFIELDCOUNT + "(" + UNIQUEFIELDCOUNT + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ System.out.println("Constructor calls = " + REMCONSTRUCTORCALLED); //$NON-NLS-1$ System.out.println("--------------------------------------------------"); //$NON-NLS-1$ System.out.println("-Count of methods invoked-------------------------"); //$NON-NLS-1$ System.out.println("--------------------------------------------------"); //$NON-NLS-1$ // Collate the methods called Iterator entries = METHODCOUNTMAP.entrySet().iterator(); while (entries.hasNext()) { Map.Entry entry = (Entry) entries.next(); REMMethodProxy methodProxy = (REMMethodProxy) entry.getKey(); System.out.println(methodProxy.getClassType().getTypeName() + "," + methodProxy.getName() + "," + entry.getValue()); //$NON-NLS-1$ //$NON-NLS-2$ } System.out.println("--------------------------------------------------"); //$NON-NLS-1$ System.out.println("-Count of fields get called ----------------------"); //$NON-NLS-1$ System.out.println("--------------------------------------------------"); //$NON-NLS-1$ // Collate the fields accessed entries = FIELDCOUNTMAP.entrySet().iterator(); while (entries.hasNext()) { Map.Entry entry = (Entry) entries.next(); REMFieldProxy fieldProxy = (REMFieldProxy) entry.getKey(); System.out.println(fieldProxy.toBeanString() + "," + entry.getValue()); //$NON-NLS-1$ } System.out.println("--------------------------------------------------"); //$NON-NLS-1$ System.out.println("-Count of fields set called ----------------------"); //$NON-NLS-1$ System.out.println("--------------------------------------------------"); //$NON-NLS-1$ // Collate the fields set entries = FIELDSETCOUNTMAP.entrySet().iterator(); while (entries.hasNext()) { Map.Entry entry = (Entry) entries.next(); REMFieldProxy fieldProxy = (REMFieldProxy) entry.getKey(); System.out.println(fieldProxy.toBeanString() + "," + entry.getValue()); //$NON-NLS-1$ } } } /** * @param aBeanTypeProxy = BeanTypeProxy for the method * @param methodName = methodName to be looked for * @param parmTypes = array of qualified type names for the method arguments, null if no methods */ public IMethodProxy getMethodProxy(IBeanTypeProxy aBeanTypeProxy, String methodName, String[] parmTypes){ if (!registry.isValid()) return null; REMMETHODCOUNT++; Map methods; Object key; synchronized (this) { // The classCache map is keyed by the BeanTypeProxy and holds a further map of cache'd methods methods = getMethods(aBeanTypeProxy); // The syntax of the key is methodName(parmType1,parmType2) if (parmTypes == null || parmTypes.length == 0) { key = methodName; } else { key = new MethodKeyStringParms(methodName, parmTypes); } IMethodProxy result = (IMethodProxy) methods.get(key); if (result != null) return result; } UNIQUEMETHODCOUNT++; // Get the method proxy and cache this before returning it // Get the method proxy and cache this before returning it REMMethodProxyFactory proxyFactory = (REMMethodProxyFactory) registry.getMethodProxyFactory(); IMethodProxy result = proxyFactory.getMethodProxy((IREMBeanTypeProxy)aBeanTypeProxy,methodName,parmTypes); synchronized (this) { // Get it again to make sure it hasn't changed due to a race condition. We don't sync for the getMethodProxy because that goes to the remote vm and could deadlock. IMethodProxy mValue = (IMethodProxy) methods.get(key); if (mValue != null && mValue != result) { registry.releaseProxy(result); // Don't need the result now, got it through a race condition. return mValue; // We have a real value now. } methods.put(key, result); } return result; } /** * @param aBeanTypeProxy * @return Map of cache'd methods */ private Map getMethods(IProxyBeanType aBeanTypeProxy) { Map methods = (Map) methodsCache.get(aBeanTypeProxy.getTypeName()); if(methods == null){ methods = new HashMap(20); methodsCache.put(aBeanTypeProxy.getTypeName(),methods); } return methods; } /** * @param aBeanTypeProxy * @return Map of cache'd invokables */ private Map getInvokables(IBeanTypeProxy aBeanTypeProxy) { Map invokables = (Map) invokablesCache.get(aBeanTypeProxy); if(invokables == null){ invokables = new HashMap(20); invokablesCache.put(aBeanTypeProxy,invokables); } return invokables; } /** * @param aBeanTypeProxy * @return Map of cache'd fields */ private Map getFields(IProxyBeanType aBeanTypeProxy) { Map fields = (Map) fieldsCache.get(aBeanTypeProxy.getTypeName()); if(fields == null){ fields = new HashMap(20); fieldsCache.put(aBeanTypeProxy.getTypeName(),fields); } return fields; } /** * @param aBeanTypeProxy = BeanTypeProxy for the method * @param methodName = methodName to be looked for * @param parmTypes = array of qualified type names for the method arguments, null if no arguments */ public IInvokable getInvokable(IBeanTypeProxy aBeanTypeProxy, String invokableName, String[] parmTypeNames){ REMINVOKABLECOUNT++; // The classCache map is keyed by the BeanTypeProxy and holds a further map of cache'd methods Map invokables = getInvokables(aBeanTypeProxy); Object key = null; if(parmTypeNames == null || parmTypeNames.length == 0){ key = invokableName; } else { key = new MethodKeyStringParms(invokableName, parmTypeNames); } IInvokable result = (IInvokable) invokables.get(key); if(result != null) return result; UNIQUEINVOKABLECOUNT++; // Get the method proxy and cache this before returning it // Get the method proxy and cache this before returning it REMMethodProxyFactory proxyFactory = (REMMethodProxyFactory) aBeanTypeProxy.getProxyFactoryRegistry().getMethodProxyFactory(); result = proxyFactory.getInvokable((IREMBeanTypeProxy)aBeanTypeProxy,invokableName,parmTypeNames); invokables.put(key,result); return result; } /** * @param aBeanTypeProxy = BeanTypeProxy for the method * @param methodName = methodName to be looked for * @param parmTypes = array of IBeanTypeProxy types for the method arguments, null if no arguments */ public IInvokable getInvokable(IBeanTypeProxy aBeanTypeProxy, String invokableName, IBeanTypeProxy[] parmTypes){ REMINVOKABLECOUNT++; // The classCache map is keyed by the BeanTypeProxy and holds a further map of cache'd methods Map invokables = getInvokables(aBeanTypeProxy); Object key = null; if(parmTypes == null || parmTypes.length == 0){ key = invokableName; } else { key = new MethodKeyProxyParms(invokableName, parmTypes); } IInvokable result = (IInvokable) invokables.get(key); if(result != null) return result; UNIQUEINVOKABLECOUNT++; // Get the method proxy and cache this before returning it // Get the method proxy and cache this before returning it REMMethodProxyFactory proxyFactory = (REMMethodProxyFactory) aBeanTypeProxy.getProxyFactoryRegistry().getMethodProxyFactory(); result = proxyFactory.getInvokable((IREMBeanTypeProxy)aBeanTypeProxy,invokableName,parmTypes); invokables.put(key,result); return result; } /** * @param aBeanTypeProxy = BeanTypeProxy for the method * @param methodName = methodName to be looked for * @param parmTypes = array of qualified type names for the method arguments, null if no methods */ public IMethodProxy getMethodProxy(IBeanTypeProxy aBeanTypeProxy, String methodName, IBeanTypeProxy[] parmTypes){ if (!registry.isValid()) return null; REMMETHODCOUNT++; // The classCache map is keyed by the BeanTypeProxy and holds a further map of cache'd methods Map methods; Object key; synchronized (this) { methods = getMethods(aBeanTypeProxy); key = null; if (parmTypes == null || parmTypes.length == 0) { key = methodName; } else { key = new MethodKeyProxyParms(methodName, parmTypes); } IMethodProxy result = (IMethodProxy) methods.get(key); if (result != null) return result; } UNIQUEMETHODCOUNT++; // Get the method proxy and cache this before returning it // Get the method proxy and cache this before returning it REMMethodProxyFactory proxyFactory = (REMMethodProxyFactory) registry.getMethodProxyFactory(); IMethodProxy result = proxyFactory.getMethodProxy((IREMBeanTypeProxy)aBeanTypeProxy,methodName,parmTypes); synchronized (this) { // Get it again to make sure it hasn't changed due to a race condition. We don't sync for the getMethodProxy because that goes to the remote vm and could deadlock. IMethodProxy mValue = (IMethodProxy) methods.get(key); if (mValue != null && mValue != result) { registry.releaseProxy(result); // Don't need result now, got it already through a race condition. return mValue; // We have a real value now. } methods.put(key, result); } return result; } /** * Return the proxy method for the method through the expression. * @param expression * @param aBeanTypeProxy * @param methodName * @param parmTypes * @return either the IMethodProxy if already resolved or an ExpressionProxy if not yet resolved. * * @since 1.1.0 */ public IProxyMethod getMethodProxy(IExpression expression, IProxyBeanType aBeanTypeProxy, String methodName, IProxyBeanType[] parmTypes){ if (!registry.isValid()) return null; REMMETHODCOUNT++; Map methods; Map epMethods; Object key; boolean isKey; synchronized (this) { // The classCache map is keyed by the BeanTypeProxy name and holds a further map of cache'd methods methods = getMethods(aBeanTypeProxy); if (parmTypes == null || parmTypes.length == 0) { key = methodName; isKey = false; } else { key = new MethodKeyProxyParms(methodName, parmTypes); isKey = true; } IProxyMethod result = (IProxyMethod) methods.get(key); if (result != null) return result; // See if stored in the expression. epMethods = ((REMExpression) expression).getMethods(aBeanTypeProxy); result = (IProxyMethod) epMethods.get(key); if (result != null) return result; } UNIQUEMETHODCOUNT++; // Get the method expression proxy and cache this before returning it IProxyMethod result = ((Expression) expression).createMethodExpressionProxy(aBeanTypeProxy,methodName,parmTypes); epMethods.put(key, result); final Object epKey = key; final Map rMethods = methods; final Map fepMethods = epMethods; final boolean isKeyType = isKey; ((ExpressionProxy) result).addProxyListener(new ExpressionProxy.ProxyAdapter() { public void proxyResolved(ProxyEvent event) { synchronized (REMProxyConstants.this) { if (rMethods.containsKey(epKey)) return; // We already have a true method proxy in there. A race condition occurred. // Now put this resolved guy into the methods. // We don't want the key to contain expression proxies in the final map, so if it is a key type // we will turn it into a string type key. Object key; if (isKeyType) { key = ((MethodKeyProxyParms) epKey).toMethodKeyStringParms(); // So that we don't put a ket that contains expression proxy parm types into the main map. } else key = epKey; rMethods.put(key, event.getProxy()); } } public void proxyNotResolved(ExpressionProxy.ProxyEvent event) { synchronized (REMProxyConstants.this) { fepMethods.remove(epKey); } } }); return result; } /** * @param proxy */ static void methodInvoked(REMMethodProxy proxy) { if (GATHER_COUNTS) { Integer count = (Integer) METHODCOUNTMAP.get(proxy); if (count == null) { METHODCOUNTMAP.put(proxy, new Integer(1)); } else { METHODCOUNTMAP.put(proxy, new Integer(count.intValue() + 1)); } } } static void fieldGetInvoked(IBeanProxy proxy) { if (GATHER_COUNTS) { Integer count = (Integer) FIELDCOUNTMAP.get(proxy); if (count == null) { FIELDCOUNTMAP.put(proxy, new Integer(1)); } else { FIELDCOUNTMAP.put(proxy, new Integer(count.intValue() + 1)); } } } static void fieldSetInvoked(IBeanProxy proxy, IBeanProxy value) { if (GATHER_COUNTS) { Integer count = (Integer) FIELDSETCOUNTMAP.get(proxy); if (count == null) { FIELDSETCOUNTMAP.put(proxy, new Integer(1)); } else { FIELDSETCOUNTMAP.put(proxy, new Integer(count.intValue() + 1)); } } } /** * @param proxy for the BeanType of the field * @param fieldName of the field, e.g. (java.awt.Dimension, width) for the "width" field on Dimension * @return The field proxy that is cache'd for performance */ public IFieldProxy getFieldProxy(REMAbstractBeanTypeProxy aBeanTypeProxy, String fieldName) { if (!registry.isValid()) return null; REMFIELDCOUNT++; Map fields; synchronized (this) { // The field map is keyed by the BeanTypeProxy and holds a further map of cache'd fields fields = getFields(aBeanTypeProxy); // Lookup the cache'd Field proxy IFieldProxy result = (IFieldProxy) fields.get(fieldName); if (result != null) return result; } UNIQUEFIELDCOUNT++; IFieldProxy result = (IFieldProxy) REMStandardBeanProxyConstants.getConstants(aBeanTypeProxy.getProxyFactoryRegistry()).getClassGetField().invokeCatchThrowableExceptions( aBeanTypeProxy, registry.getBeanProxyFactory().createBeanProxyWith(fieldName)); synchronized (this) { IFieldProxy fValue = (IFieldProxy) fields.get(fieldName); if (fValue != null) { registry.releaseProxy(result); // Don't need it now. A race had put another one in. return fValue; } fields.put(fieldName,result); } return result; } public IProxyField getFieldProxy(IExpression expression, IProxyBeanType aBeanTypeProxy, final String fieldName){ if (!registry.isValid()) return null; REMFIELDCOUNT++; Map fields; Map epFields; synchronized (this) { // The classCache map is keyed by the BeanTypeProxy name and holds a further map of cache'd methods fields = getFields(aBeanTypeProxy); IProxyField result = (IProxyField) fields.get(fieldName); if (result != null) return result; // See if stored in the expression. epFields = ((REMExpression) expression).getFields(aBeanTypeProxy); result = (IProxyField) epFields.get(fieldName); if (result != null) return result; } UNIQUEFIELDCOUNT++; // Get the field expression proxy and cache this before returning it IProxyField result = ((REMExpression) expression).createFieldExpressionProxy(aBeanTypeProxy, fieldName); epFields.put(fieldName, result); final Map fpFields = fields; final Map fepFields = epFields; ((ExpressionProxy) result).addProxyListener(new ExpressionProxy.ProxyAdapter() { public void proxyResolved(ProxyEvent event) { synchronized (REMProxyConstants.this) { if (fpFields.containsKey(fieldName)) return; // Already set to resolved value by someone else. fpFields.put(fieldName, event.getProxy()); } } public void proxyNotResolved(ExpressionProxy.ProxyEvent event) { synchronized (REMProxyConstants.this) { fepFields.remove(fieldName); } } }); return result; } }