Commits

Anonymous committed 82632ab

Implemented ability to register a special class cache listener class to OgnlRuntime which makes it possible to prevent reflection caching of specific class types.

Comments (0)

Files changed (5)

       <module fileurl="file://$PROJECT_DIR$/OGNL.iml" filepath="$PROJECT_DIR$/OGNL.iml" />
     </modules>
   </component>
-  <component name="ProjectRootManager" version="2" assert-keyword="true" jdk-15="true" project-jdk-name="1.6" project-jdk-type="JavaSDK" />
+  <component name="ProjectRootManager" version="2" assert-keyword="true" jdk-15="true" project-jdk-name="1.5" project-jdk-type="JavaSDK" />
   <component name="ResourceManagerContainer">
     <option name="myResourceBundles">
       <value>
   </component>
   <component name="ChangeListManager">
     <list default="true" name="Default" comment="">
-      <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/java/ognl/ASTBitAnd.java" afterPath="$PROJECT_DIR$/src/java/ognl/ASTBitAnd.java" />
-      <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/java/ognl/ComparisonExpression.java" afterPath="$PROJECT_DIR$/src/java/ognl/ComparisonExpression.java" />
+      <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/test/java/ognl/TestOgnlRuntime.java" afterPath="$PROJECT_DIR$/src/test/java/ognl/TestOgnlRuntime.java" />
+      <change type="MODIFICATION" beforePath="$PROJECT_DIR$/OGNL.iws" afterPath="$PROJECT_DIR$/OGNL.iws" />
+      <change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/src/java/ognl/ClassCacheInspector.java" />
       <change type="MODIFICATION" beforePath="$PROJECT_DIR$/OGNL.ipr" afterPath="$PROJECT_DIR$/OGNL.ipr" />
       <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/java/ognl/OgnlRuntime.java" afterPath="$PROJECT_DIR$/src/java/ognl/OgnlRuntime.java" />
-      <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/java/ognl/NumericExpression.java" afterPath="$PROJECT_DIR$/src/java/ognl/NumericExpression.java" />
-      <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/test/java/org/ognl/test/PropertyTest.java" afterPath="$PROJECT_DIR$/src/test/java/org/ognl/test/PropertyTest.java" />
-      <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/test/java/org/ognl/test/ArithmeticAndLogicalOperatorsTest.java" afterPath="$PROJECT_DIR$/src/test/java/org/ognl/test/ArithmeticAndLogicalOperatorsTest.java" />
-      <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/java/ognl/ASTUnsignedShiftRight.java" afterPath="$PROJECT_DIR$/src/java/ognl/ASTUnsignedShiftRight.java" />
-      <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/java/ognl/ASTAdd.java" afterPath="$PROJECT_DIR$/src/java/ognl/ASTAdd.java" />
-      <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/java/ognl/ASTBitNegate.java" afterPath="$PROJECT_DIR$/src/java/ognl/ASTBitNegate.java" />
-      <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/java/ognl/enhance/ExpressionCompiler.java" afterPath="$PROJECT_DIR$/src/java/ognl/enhance/ExpressionCompiler.java" />
-      <change type="MODIFICATION" beforePath="$PROJECT_DIR$/src/java/ognl/ASTNegate.java" afterPath="$PROJECT_DIR$/src/java/ognl/ASTNegate.java" />
-      <change type="MODIFICATION" beforePath="$PROJECT_DIR$/OGNL.iws" afterPath="$PROJECT_DIR$/OGNL.iws" />
     </list>
   </component>
   <component name="ChangesViewManager" flattened_view="true" show_ignored="false" />
         <option name="proportions">
           <collection>
             <option value="0.16666667" />
-            <option value="0.5" />
           </collection>
         </option>
       </SplitterProportionsDataImpl>
     </option>
-    <option name="lastEditedConfigurable" value="OGNL" />
+    <option name="lastEditedConfigurable" value="Project 'OGNL'" />
   </component>
   <component name="ProjectView">
     <navigator currentView="ProjectPane" proportions="0.16666667" version="1" splitterProportion="0.5">
       <showLibraryContents />
       <hideEmptyPackages />
       <abbreviatePackageNames />
-      <showStructure Favorites="false" Scope="false" PackagesPane="false" ProjectPane="false" />
+      <showStructure PackagesPane="false" Scope="false" Favorites="false" ProjectPane="false" />
       <autoscrollToSource />
       <autoscrollFromSource />
       <sortByType />
     <option name="referencePos" value="0" />
     <option name="showLabels" value="true" />
   </component>
-  <component name="RunManager" selected="JUnit.IndexAccessTest">
-    <tempConfiguration default="false" name="IndexAccessTest" type="JUnit" factoryName="JUnit" enabled="false" merge="false">
+  <component name="RunManager" selected="JUnit.PropertyTest">
+    <tempConfiguration default="false" name="PropertyTest" type="JUnit" factoryName="JUnit" enabled="false" merge="false">
       <pattern value="org.ognl.test.*" />
       <module name="OGNL" />
       <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="true" />
       <option name="ALTERNATIVE_JRE_PATH" value="/usr/local/jdk1.6.0_02" />
       <option name="PACKAGE_NAME" value="org.ognl.test" />
-      <option name="MAIN_CLASS_NAME" value="org.ognl.test.IndexAccessTest" />
+      <option name="MAIN_CLASS_NAME" value="org.ognl.test.PropertyTest" />
       <option name="METHOD_NAME" value="" />
       <option name="TEST_OBJECT" value="class" />
       <option name="VM_PARAMETERS" value="" />
         <option name="Make" value="true" />
       </method>
     </tempConfiguration>
-    <configuration default="true" type="TestNG" factoryName="TestNG" enabled="false" merge="false">
-      <module name="" />
-      <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
-      <option name="ALTERNATIVE_JRE_PATH" value="" />
-      <option name="SUITE_NAME" value="" />
-      <option name="PACKAGE_NAME" />
-      <option name="MAIN_CLASS_NAME" value="" />
-      <option name="METHOD_NAME" />
-      <option name="GROUP_NAME" value="" />
-      <option name="TEST_OBJECT" value="CLASS" />
-      <option name="VM_PARAMETERS" value="" />
-      <option name="PARAMETERS" value="" />
-      <option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$/build" />
-      <option name="OUTPUT_DIRECTORY" value="" />
-      <option name="ENV_VARIABLES" />
-      <option name="ADDITIONAL_CLASS_PATH" />
-      <option name="TEST_SEARCH_SCOPE">
-        <value defaultName="singleModule" />
-      </option>
-      <option name="PROPERTIES_FILE" value="" />
-      <properties />
-      <method>
-        <option name="Make" value="true" />
-      </method>
-    </configuration>
     <configuration default="true" type="JUnit" factoryName="JUnit" enabled="false" merge="false">
       <module name="" />
       <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="true" />
       <option name="ENV_VARIABLES" />
       <module name="" />
     </configuration>
+    <configuration default="true" type="Remote" factoryName="Remote">
+      <option name="USE_SOCKET_TRANSPORT" value="true" />
+      <option name="SERVER_MODE" value="false" />
+      <option name="SHMEM_ADDRESS" value="javadebug" />
+      <option name="HOST" value="localhost" />
+      <option name="PORT" value="5005" />
+    </configuration>
+    <configuration default="true" type="TestNG" factoryName="TestNG" enabled="false" merge="false">
+      <module name="" />
+      <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
+      <option name="ALTERNATIVE_JRE_PATH" value="" />
+      <option name="SUITE_NAME" value="" />
+      <option name="PACKAGE_NAME" />
+      <option name="MAIN_CLASS_NAME" value="" />
+      <option name="METHOD_NAME" />
+      <option name="GROUP_NAME" value="" />
+      <option name="TEST_OBJECT" value="CLASS" />
+      <option name="VM_PARAMETERS" value="" />
+      <option name="PARAMETERS" value="" />
+      <option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$/build" />
+      <option name="OUTPUT_DIRECTORY" value="" />
+      <option name="ENV_VARIABLES" />
+      <option name="ADDITIONAL_CLASS_PATH" />
+      <option name="TEST_SEARCH_SCOPE">
+        <value defaultName="singleModule" />
+      </option>
+      <option name="PROPERTIES_FILE" value="" />
+      <properties />
+      <method>
+        <option name="Make" value="true" />
+      </method>
+    </configuration>
     <configuration default="true" type="Applet" factoryName="Applet">
       <module name="" />
       <option name="MAIN_CLASS_NAME" />
       <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
       <option name="ALTERNATIVE_JRE_PATH" />
     </configuration>
-    <configuration default="true" type="Remote" factoryName="Remote">
-      <option name="USE_SOCKET_TRANSPORT" value="true" />
-      <option name="SERVER_MODE" value="false" />
-      <option name="SHMEM_ADDRESS" value="javadebug" />
-      <option name="HOST" value="localhost" />
-      <option name="PORT" value="5005" />
-    </configuration>
     <configuration default="true" type="#com.intellij.j2ee.web.tomcat.TomcatRunConfigurationFactory" factoryName="Local">
       <option name="WORKING_DIRECTORY" />
       <option name="HOST" value="localhost" />
     <option name="PERFORM_EDIT_IN_BACKGROUND" value="true" />
     <option name="PERFORM_ADD_REMOVE_IN_BACKGROUND" value="true" />
     <option name="FORCE_NON_EMPTY_COMMENT" value="false" />
-    <option name="LAST_COMMIT_MESSAGE" value="Fixes OGNL-91. God that bug sucked ass.... Finally did numeric coercion refactoring whether I liked it or not." />
+    <option name="LAST_COMMIT_MESSAGE" value="Implemented ability to register a special class cache listener class to OgnlRuntime which makes it possible to prevent reflection caching of specific class types." />
     <option name="OPTIMIZE_IMPORTS_BEFORE_PROJECT_COMMIT" value="true" />
     <option name="REFORMAT_BEFORE_PROJECT_COMMIT" value="false" />
     <option name="REFORMAT_BEFORE_FILE_COMMIT" value="false" />
     <option name="UPDATE_GROUP_BY_CHANGELIST" value="false" />
     <option name="SHOW_FILE_HISTORY_AS_TREE" value="false" />
     <option name="FILE_HISTORY_SPLITTER_PROPORTION" value="0.6" />
-    <MESSAGE value="Added some more unit tests referenced in jira but could not get them to fail." />
     <MESSAGE value="Fixes OGNL-60 . OgnlOps wasn't converting simple object values to object arrays &amp;&amp; ASTMethod wasn't checking for void return types to parameter values." />
     <MESSAGE value="Fixes OGNL-62. OgnlRuntime.getReadMethod() wasn't checking for a valid return type in the second pass method matcher." />
     <MESSAGE value="Added some more tests but unable to re-produce bug." />
     <MESSAGE value="Got docbook working finally.  Has to be run with 1.4 jre." />
     <MESSAGE value="Fixes OGNL-89.  ASTCtor was calling ExpressionCompiler.getRootExpression() on child expression of ASTRootVarRef - which was also doing the same exact thing on itself." />
     <MESSAGE value="Fixes OGNL-91. God that bug sucked ass.... Finally did numeric coercion refactoring whether I liked it or not." />
+    <MESSAGE value="Implemented ability to register a special class cache listener class to OgnlRuntime which makes it possible to prevent reflection caching of specific class types." />
   </component>
   <component name="VssConfiguration">
     <option name="CLIENT_PATH" value="" />
     <option name="myLastEditedConfigurable" />
   </component>
   <component name="editorHistoryManager">
-    <entry file="file://$PROJECT_DIR$/src/java/ognl/ASTAdd.java">
-      <provider selected="true" editor-type-id="text-editor">
-        <state line="282" column="19" selection-start="12284" selection-end="12284" vertical-scroll-proportion="1.0857815">
-          <folding />
-        </state>
-      </provider>
-    </entry>
-    <entry file="file://$PROJECT_DIR$/src/java/ognl/ASTBitNegate.java">
-      <provider selected="true" editor-type-id="text-editor">
-        <state line="69" column="0" selection-start="2693" selection-end="2693" vertical-scroll-proportion="0.70505285">
-          <folding />
-        </state>
-      </provider>
-    </entry>
-    <entry file="file://$PROJECT_DIR$/src/java/ognl/ASTNegate.java">
-      <provider selected="true" editor-type-id="text-editor">
-        <state line="63" column="0" selection-start="2542" selection-end="2542" vertical-scroll-proportion="0.59929496">
-          <folding />
-        </state>
-      </provider>
-    </entry>
     <entry file="file://$PROJECT_DIR$/src/java/ognl/OgnlOps.java">
       <provider selected="true" editor-type-id="text-editor">
         <state line="685" column="36" selection-start="25262" selection-end="25262" vertical-scroll-proportion="0.42068157">
         </state>
       </provider>
     </entry>
-    <entry file="file://$PROJECT_DIR$/src/test/java/org/ognl/test/PropertyTest.java">
-      <provider selected="true" editor-type-id="text-editor">
-        <state line="86" column="81" selection-start="5207" selection-end="5207" vertical-scroll-proportion="0.67839193">
-          <folding>
-            <element signature="imports" expanded="true" />
-          </folding>
-        </state>
-      </provider>
-    </entry>
-    <entry file="file://$PROJECT_DIR$/src/java/ognl/OgnlRuntime.java">
-      <provider selected="true" editor-type-id="text-editor">
-        <state line="2182" column="8" selection-start="82304" selection-end="82304" vertical-scroll-proportion="0.40305522">
-          <folding />
-        </state>
-      </provider>
-    </entry>
     <entry file="file://$PROJECT_DIR$/src/java/ognl/ComparisonExpression.java">
       <provider selected="true" editor-type-id="text-editor">
         <state line="35" column="34" selection-start="894" selection-end="894" vertical-scroll-proportion="0.5746181">
         </state>
       </provider>
     </entry>
+    <entry file="file://$PROJECT_DIR$/src/test/java/org/ognl/test/PropertyTest.java">
+      <provider selected="true" editor-type-id="text-editor">
+        <state line="90" column="58" selection-start="5466" selection-end="5466" vertical-scroll-proportion="0.6345476">
+          <folding />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/src/test/java/org/ognl/test/objects/Root.java">
+      <provider selected="true" editor-type-id="text-editor">
+        <state line="30" column="29" selection-start="1750" selection-end="1771" vertical-scroll-proportion="0.012562814">
+          <folding />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/src/java/ognl/OgnlRuntime.java">
+      <provider selected="true" editor-type-id="text-editor">
+        <state line="2010" column="0" selection-start="75842" selection-end="75842" vertical-scroll-proportion="0.27470687">
+          <folding>
+            <element signature="imports" expanded="true" />
+          </folding>
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/src/test/java/ognl/TestOgnlRuntime.java">
+      <provider selected="true" editor-type-id="text-editor">
+        <state line="81" column="0" selection-start="2533" selection-end="2533" vertical-scroll-proportion="0.42211056">
+          <folding />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/src/java/ognl/ClassCacheInspector.java">
+      <provider selected="true" editor-type-id="text-editor">
+        <state line="18" column="0" selection-start="510" selection-end="510" vertical-scroll-proportion="0.22613065">
+          <folding />
+        </state>
+      </provider>
+    </entry>
   </component>
   <component name="testng.defaultConfiguration">
     <outputDirectory />

src/java/ognl/ClassCacheInspector.java

+package ognl;
+
+/**
+ * Optional interface that may be registered with {@link OgnlRuntime#setClassCacheInspector(ClassCacheInspector)} as
+ * a means to disallow caching of specific class types.
+ */
+public interface ClassCacheInspector {
+
+    /**
+     * Invoked just before storing a class type within a cache instance.
+     *
+     * @param type
+     *          The class that is to be stored.
+     *
+     * @return True if the class can be cached, false otherwise.
+     */
+    boolean shouldCache(Class type);
+}

src/java/ognl/OgnlRuntime.java

      */
     private static final String NULL_OBJECT_STRING = "<null>";
 
-    private static final ClassCache methodAccessors = new ClassCache();
-    private static final ClassCache propertyAccessors = new ClassCache();
-    private static final ClassCache elementsAccessors = new ClassCache();
-    private static final ClassCache nullHandlers = new ClassCache();
-    private static final ClassCache propertyDescriptorCache = new ClassCache();
-    private static final ClassCache constructorCache = new ClassCache();
-    private static final ClassCache staticMethodCache = new ClassCache();
-    private static final ClassCache instanceMethodCache = new ClassCache();
-    private static final ClassCache invokePermissionCache = new ClassCache();
-    private static final ClassCache fieldCache = new ClassCache();
-    private static final List superclasses = new ArrayList(); /* Used by fieldCache lookup */
-    private static final ClassCache[] declaredMethods = new ClassCache[]{new ClassCache(), new ClassCache()};
+    static final ClassCache _methodAccessors = new ClassCache();
+    static final ClassCache _propertyAccessors = new ClassCache();
+    static final ClassCache _elementsAccessors = new ClassCache();
+    static final ClassCache _nullHandlers = new ClassCache();
+    
+    static final ClassCache _propertyDescriptorCache = new ClassCache();
+    static final ClassCache _constructorCache = new ClassCache();
+    static final ClassCache _staticMethodCache = new ClassCache();
+    static final ClassCache _instanceMethodCache = new ClassCache();
+    static final ClassCache _invokePermissionCache = new ClassCache();
+    static final ClassCache _fieldCache = new ClassCache();
+    static final List _superclasses = new ArrayList(); /* Used by fieldCache lookup */
+    static final ClassCache[] _declaredMethods = new ClassCache[]{new ClassCache(), new ClassCache()};
 
-    private static final Map primitiveTypes = new HashMap(101);
-    private static final ClassCache primitiveDefaults = new ClassCache();
-    private static final Map methodParameterTypesCache = new HashMap(101);
-    private static final Map ctorParameterTypesCache = new HashMap(101);
-    private static SecurityManager securityManager = System.getSecurityManager();
-    private static final EvaluationPool evaluationPool = new EvaluationPool();
-    private static final ObjectArrayPool objectArrayPool = new ObjectArrayPool();
+    static final Map _primitiveTypes = new HashMap(101);
+    static final ClassCache _primitiveDefaults = new ClassCache();
+    static final Map _methodParameterTypesCache = new HashMap(101);
+    static final Map _ctorParameterTypesCache = new HashMap(101);
+    static SecurityManager _securityManager = System.getSecurityManager();
+    static final EvaluationPool _evaluationPool = new EvaluationPool();
+    static final ObjectArrayPool _objectArrayPool = new ObjectArrayPool();
+
+    static ClassCacheInspector _cacheInspector;
 
     /**
      * Expression compiler used by {@link Ognl#compileExpression(OgnlContext, Object, String)} calls.
     /**
      * This is a highly specialized map for storing values keyed by Class objects.
      */
-    private static class ClassCache {
+    static class ClassCache {
 
         /* this MUST be a power of 2 */
         private static final int TABLE_SIZE = 512;
 
         private Entry[] table;
 
+        private ClassCacheInspector _classInspector;
+
         private static class Entry {
 
             protected Entry next;
             this.table = new Entry[TABLE_SIZE];
         }
 
+        public void setClassInspector(ClassCacheInspector inspector)
+        {
+            _classInspector = inspector;
+        }
+
+        public void clear()
+        {
+            for (int i=0; i < this.table.length; i++)
+            {
+                this.table[i] = null;
+            }
+        }
+
+        public int getSize()
+        {
+            int counter = 0;
+            for (int i=0; i < table.length; i++)
+            {
+                if (table[i] != null)
+                    counter++;
+            }
+
+            return counter;
+        }
+
         public final Object get(Class key)
         {
             Object result = null;
 
         public final Object put(Class key, Object value)
         {
+            if (_classInspector != null && !_classInspector.shouldCache(key))
+                return value;
+
             Object result = null;
             int i = key.hashCode() & TABLE_SIZE_MASK;
             Entry entry = table[i];
         setMethodAccessor(double[].class, ma);
         setMethodAccessor(Object[].class, ma);
 
-        primitiveTypes.put("boolean", Boolean.TYPE);
-        primitiveTypes.put("byte", Byte.TYPE);
-        primitiveTypes.put("short", Short.TYPE);
-        primitiveTypes.put("char", Character.TYPE);
-        primitiveTypes.put("int", Integer.TYPE);
-        primitiveTypes.put("long", Long.TYPE);
-        primitiveTypes.put("float", Float.TYPE);
-        primitiveTypes.put("double", Double.TYPE);
+        _primitiveTypes.put("boolean", Boolean.TYPE);
+        _primitiveTypes.put("byte", Byte.TYPE);
+        _primitiveTypes.put("short", Short.TYPE);
+        _primitiveTypes.put("char", Character.TYPE);
+        _primitiveTypes.put("int", Integer.TYPE);
+        _primitiveTypes.put("long", Long.TYPE);
+        _primitiveTypes.put("float", Float.TYPE);
+        _primitiveTypes.put("double", Double.TYPE);
 
-        primitiveDefaults.put(Boolean.TYPE, Boolean.FALSE);
-        primitiveDefaults.put(Boolean.class, Boolean.FALSE);
-        primitiveDefaults.put(Byte.TYPE, new Byte((byte) 0));
-        primitiveDefaults.put(Byte.class, new Byte((byte) 0));
-        primitiveDefaults.put(Short.TYPE, new Short((short) 0));
-        primitiveDefaults.put(Short.class, new Short((short) 0));
-        primitiveDefaults.put(Character.TYPE, new Character((char) 0));
-        primitiveDefaults.put(Integer.TYPE, new Integer(0));
-        primitiveDefaults.put(Long.TYPE, new Long(0L));
-        primitiveDefaults.put(Float.TYPE, new Float(0.0f));
-        primitiveDefaults.put(Double.TYPE, new Double(0.0));
+        _primitiveDefaults.put(Boolean.TYPE, Boolean.FALSE);
+        _primitiveDefaults.put(Boolean.class, Boolean.FALSE);
+        _primitiveDefaults.put(Byte.TYPE, new Byte((byte) 0));
+        _primitiveDefaults.put(Byte.class, new Byte((byte) 0));
+        _primitiveDefaults.put(Short.TYPE, new Short((short) 0));
+        _primitiveDefaults.put(Short.class, new Short((short) 0));
+        _primitiveDefaults.put(Character.TYPE, new Character((char) 0));
+        _primitiveDefaults.put(Integer.TYPE, new Integer(0));
+        _primitiveDefaults.put(Long.TYPE, new Long(0L));
+        _primitiveDefaults.put(Float.TYPE, new Float(0.0f));
+        _primitiveDefaults.put(Double.TYPE, new Double(0.0));
 
-        primitiveDefaults.put(BigInteger.class, new BigInteger("0"));
-        primitiveDefaults.put(BigDecimal.class, new BigDecimal(0.0));
+        _primitiveDefaults.put(BigInteger.class, new BigInteger("0"));
+        _primitiveDefaults.put(BigDecimal.class, new BigDecimal(0.0));
+    }
+
+    /**
+     * Clears all of the cached reflection information normally used
+     * to improve the speed of expressions that operate on the same classes
+     * or are executed multiple times.
+     *
+     * <p>
+     * <strong>Warning:</strong> Calling this too often can be a huge performance
+     * drain on your expressions - use with care.
+     * </p>
+     */
+    public static void clearCache()
+    {
+        _methodParameterTypesCache.clear();
+        _ctorParameterTypesCache.clear();
+        _propertyDescriptorCache.clear();
+        _constructorCache.clear();
+        _staticMethodCache.clear();
+        _instanceMethodCache.clear();
+        _invokePermissionCache.clear();
+        _fieldCache.clear();
+        _superclasses.clear();
+        _declaredMethods[0].clear();
+        _declaredMethods[1].clear();
     }
 
     public static String getNumericValueGetter(Class type)
      */
     public static Class[] getParameterTypes(Method m)
     {
-        synchronized (methodParameterTypesCache) {
+        synchronized (_methodParameterTypesCache) {
             Class[] result;
 
-            if ((result = (Class[]) methodParameterTypesCache.get(m)) == null) {
-                methodParameterTypesCache.put(m, result = m.getParameterTypes());
+            if ((result = (Class[]) _methodParameterTypesCache.get(m)) == null) {
+                _methodParameterTypesCache.put(m, result = m.getParameterTypes());
             }
             return result;
         }
      */
     public static Class[] getParameterTypes(Constructor c)
     {
-        synchronized (ctorParameterTypesCache) {
+        synchronized (_ctorParameterTypesCache) {
             Class[] result;
 
-            if ((result = (Class[]) ctorParameterTypesCache.get(c)) == null) {
-                ctorParameterTypesCache.put(c, result = c.getParameterTypes());
+            if ((result = (Class[]) _ctorParameterTypesCache.get(c)) == null) {
+                _ctorParameterTypesCache.put(c, result = c.getParameterTypes());
             }
             return result;
         }
      */
     public static SecurityManager getSecurityManager()
     {
-        return securityManager;
+        return _securityManager;
     }
 
     /**
      */
     public static void setSecurityManager(SecurityManager value)
     {
-        securityManager = value;
+        _securityManager = value;
     }
 
     /**
         Permission result = null;
         Class mc = method.getDeclaringClass();
 
-        synchronized (invokePermissionCache) {
-            Map permissions = (Map) invokePermissionCache.get(mc);
+        synchronized (_invokePermissionCache) {
+            Map permissions = (Map) _invokePermissionCache.get(mc);
 
             if (permissions == null) {
-                invokePermissionCache.put(mc, permissions = new HashMap(101));
+                _invokePermissionCache.put(mc, permissions = new HashMap(101));
             }
             if ((result = (Permission) permissions.get(method.getName())) == null) {
                 result = new OgnlInvokePermission("invoke." + mc.getName() + "." + method.getName());
 
         synchronized(method) {
 
-            if (securityManager != null) {
+            if (_securityManager != null) {
                 try {
-                    securityManager.checkPermission(getPermission(method));
+                    _securityManager.checkPermission(getPermission(method));
                 } catch (SecurityException ex) {
                     throw new IllegalAccessException("Method [" + method + "] cannot be accessed.");
                 }
     public static Class classForName(OgnlContext context, String className)
             throws ClassNotFoundException
     {
-        Class result = (Class) primitiveTypes.get(className);
+        Class result = (Class) _primitiveTypes.get(className);
 
         if (result == null) {
             ClassResolver resolver;
 
     public static Object getPrimitiveDefaultValue(Class forClass)
     {
-        return primitiveDefaults.get(forClass);
+        return _primitiveDefaults.get(forClass);
     }
 
     public static Object getNumericDefaultValue(Class forClass)
             throws MethodFailedException
     {
         Throwable reason = null;
-        Object[] actualArgs = objectArrayPool.create(args.length);
+        Object[] actualArgs = _objectArrayPool.create(args.length);
 
         try {
             Method method = getAppropriateMethod(context, source, target, methodName, propertyName, methods, args,
         } catch (InvocationTargetException e) {
             reason = e.getTargetException();
         } finally {
-            objectArrayPool.recycle(actualArgs);
+            _objectArrayPool.recycle(actualArgs);
         }
         throw new MethodFailedException(source, methodName, reason);
     }
                 }
             }
             if (ctor == null) {
-                actualArgs = objectArrayPool.create(args.length);
+                actualArgs = _objectArrayPool.create(args.length);
                 if ((ctor = getConvertedConstructorAndArgs(context, target, constructors, args, actualArgs)) == null) {
                     throw new NoSuchMethodException();
                 }
             reason = e;
         } finally {
             if (actualArgs != args) {
-                objectArrayPool.recycle(actualArgs);
+                _objectArrayPool.recycle(actualArgs);
             }
         }
 
         }
         if (result) {
             if (m != null) {
-                Object[] args = objectArrayPool.create(value);
+                Object[] args = _objectArrayPool.create(value);
 
                 try {
                     callAppropriateMethod(context, target, target, m.getName(), propertyName,
                                           Collections.nCopies(1, m), args);
                 } finally {
-                    objectArrayPool.recycle(args);
+                    _objectArrayPool.recycle(args);
                 }
             } else {
                 result = false;
     {
         List result;
 
-        synchronized (constructorCache) {
-            if ((result = (List) constructorCache.get(targetClass)) == null) {
-                constructorCache.put(targetClass, result = Arrays.asList(targetClass.getConstructors()));
+        synchronized (_constructorCache) {
+            if ((result = (List) _constructorCache.get(targetClass)) == null) {
+                _constructorCache.put(targetClass, result = Arrays.asList(targetClass.getConstructors()));
             }
         }
         return result;
 
     public static Map getMethods(Class targetClass, boolean staticMethods)
     {
-        ClassCache cache = (staticMethods ? staticMethodCache : instanceMethodCache);
+        ClassCache cache = (staticMethods ? _staticMethodCache : _instanceMethodCache);
         Map result;
 
         synchronized (cache) {
     {
         Map result;
 
-        synchronized (fieldCache) {
-            if ((result = (Map) fieldCache.get(targetClass)) == null) {
+        synchronized (_fieldCache) {
+            if ((result = (Map) _fieldCache.get(targetClass)) == null) {
                 Field fa[];
 
                 result = new HashMap(23);
                 for (int i = 0; i < fa.length; i++) {
                     result.put(fa[i].getName(), fa[i]);
                 }
-                fieldCache.put(targetClass, result);
+                _fieldCache.put(targetClass, result);
             }
         }
         return result;
     {
         Field result = null;
 
-        synchronized (fieldCache) {
+        synchronized (_fieldCache) {
             Object o = getFields(inClass).get(name);
 
             if (o == null) {
-                superclasses.clear();
+                _superclasses.clear();
                 for (Class sc = inClass; (sc != null); sc = sc.getSuperclass()) {
                     if ((o = getFields(sc).get(name)) == NotFound)
                         break;
 
-                    superclasses.add(sc);
+                    _superclasses.add(sc);
 
                     if ((result = (Field) o) != null)
                         break;
                  * Bubble the found value (either cache miss or actual field) to all supeclasses
                  * that we saw for quicker access next time.
                  */
-                for (int i = 0, icount = superclasses.size(); i < icount; i++) {
-                    getFields((Class) superclasses.get(i)).put(name, (result == null) ? NotFound : result);
+                for (int i = 0, icount = _superclasses.size(); i < icount; i++) {
+                    getFields((Class) _superclasses.get(i)).put(name, (result == null) ? NotFound : result);
                 }
             } else {
                 if (o instanceof Field) {
     public static List getDeclaredMethods(Class targetClass, String propertyName, boolean findSets)
     {
         List result = null;
-        ClassCache cache = declaredMethods[findSets ? 0 : 1];
+        ClassCache cache = _declaredMethods[findSets ? 0 : 1];
 
         synchronized (cache) {
             Map propertyCache = (Map) cache.get(targetClass);
     {
         Map result;
 
-        synchronized (propertyDescriptorCache) {
-            if ((result = (Map) propertyDescriptorCache.get(targetClass)) == null) {
+        synchronized (_propertyDescriptorCache) {
+            if ((result = (Map) _propertyDescriptorCache.get(targetClass)) == null) {
                 PropertyDescriptor[] pda = Introspector.getBeanInfo(targetClass).getPropertyDescriptors();
 
                 result = new HashMap(101);
                 }
 
                 findObjectIndexedPropertyDescriptors(targetClass, result);
-                propertyDescriptorCache.put(targetClass, result);
+                _propertyDescriptorCache.put(targetClass, result);
             }
         }
 
         PropertyDescriptor[] result = null;
 
         if (targetClass != null) {
-            synchronized (propertyDescriptorCache) {
-                if ((result = (PropertyDescriptor[]) propertyDescriptorCache.get(targetClass)) == null) {
-                    propertyDescriptorCache.put(targetClass, result = Introspector.getBeanInfo(targetClass)
+            synchronized (_propertyDescriptorCache) {
+                if ((result = (PropertyDescriptor[]) _propertyDescriptorCache.get(targetClass)) == null) {
+                    _propertyDescriptorCache.put(targetClass, result = Introspector.getBeanInfo(targetClass)
                             .getPropertyDescriptors());
                 }
             }
 
     public static void setMethodAccessor(Class cls, MethodAccessor accessor)
     {
-        synchronized (methodAccessors) {
-            methodAccessors.put(cls, accessor);
+        synchronized (_methodAccessors) {
+            _methodAccessors.put(cls, accessor);
         }
     }
 
     public static MethodAccessor getMethodAccessor(Class cls)
             throws OgnlException
     {
-        MethodAccessor answer = (MethodAccessor) getHandler(cls, methodAccessors);
+        MethodAccessor answer = (MethodAccessor) getHandler(cls, _methodAccessors);
         if (answer != null)
             return answer;
         throw new OgnlException("No method accessor for " + cls);
 
     public static void setPropertyAccessor(Class cls, PropertyAccessor accessor)
     {
-        synchronized (propertyAccessors) {
-            propertyAccessors.put(cls, accessor);
+        synchronized (_propertyAccessors) {
+            _propertyAccessors.put(cls, accessor);
         }
     }
 
     public static PropertyAccessor getPropertyAccessor(Class cls)
             throws OgnlException
     {
-        PropertyAccessor answer = (PropertyAccessor) getHandler(cls, propertyAccessors);
+        PropertyAccessor answer = (PropertyAccessor) getHandler(cls, _propertyAccessors);
         if (answer != null)
             return answer;
 
     public static ElementsAccessor getElementsAccessor(Class cls)
             throws OgnlException
     {
-        ElementsAccessor answer = (ElementsAccessor) getHandler(cls, elementsAccessors);
+        ElementsAccessor answer = (ElementsAccessor) getHandler(cls, _elementsAccessors);
         if (answer != null)
             return answer;
         throw new OgnlException("No elements accessor for class " + cls);
 
     public static void setElementsAccessor(Class cls, ElementsAccessor accessor)
     {
-        synchronized (elementsAccessors) {
-            elementsAccessors.put(cls, accessor);
+        synchronized (_elementsAccessors) {
+            _elementsAccessors.put(cls, accessor);
         }
     }
 
     public static NullHandler getNullHandler(Class cls)
             throws OgnlException
     {
-        NullHandler answer = (NullHandler) getHandler(cls, nullHandlers);
+        NullHandler answer = (NullHandler) getHandler(cls, _nullHandlers);
         if (answer != null)
             return answer;
         throw new OgnlException("No null handler for class " + cls);
 
     public static void setNullHandler(Class cls, NullHandler handler)
     {
-        synchronized (nullHandlers) {
-            nullHandlers.put(cls, handler);
+        synchronized (_nullHandlers) {
+            _nullHandlers.put(cls, handler);
         }
     }
 
     public static Object getIndexedProperty(OgnlContext context, Object source, String name, Object index)
             throws OgnlException
     {
-        Object[] args = objectArrayPool.create(index);
+        Object[] args = _objectArrayPool.create(index);
 
         try {
             PropertyDescriptor pd = getPropertyDescriptor((source == null) ? null : source.getClass(), name);
         } catch (Exception ex) {
             throw new OgnlException("getting indexed property descriptor for '" + name + "'", ex);
         } finally {
-            objectArrayPool.recycle(args);
+            _objectArrayPool.recycle(args);
         }
     }
 
                                           Object value)
             throws OgnlException
     {
-        Object[] args = objectArrayPool.create(index, value);
+        Object[] args = _objectArrayPool.create(index, value);
 
         try {
             PropertyDescriptor pd = getPropertyDescriptor((source == null) ? null : source.getClass(), name);
         } catch (Exception ex) {
             throw new OgnlException("getting indexed property descriptor for '" + name + "'", ex);
         } finally {
-            objectArrayPool.recycle(args);
+            _objectArrayPool.recycle(args);
         }
     }
 
     public static EvaluationPool getEvaluationPool()
     {
-        return evaluationPool;
+        return _evaluationPool;
     }
 
     public static ObjectArrayPool getObjectArrayPool()
     {
-        return objectArrayPool;
+        return _objectArrayPool;
+    }
+
+    /**
+     * Registers the specified {@link ClassCacheInspector} with all class reflection based internal
+     * caches.  This may have a significant performance impact so be careful using this in production scenarios.
+     *
+     * @param inspector
+     *          The inspector instance that will be registered with all internal cache instances.
+     */
+    public static void setClassCacheInspector(ClassCacheInspector inspector)
+    {
+        _cacheInspector = inspector;
+
+        _propertyDescriptorCache.setClassInspector(_cacheInspector);
+        _constructorCache.setClassInspector(_cacheInspector);
+        _staticMethodCache.setClassInspector(_cacheInspector);
+        _instanceMethodCache.setClassInspector(_cacheInspector);
+        _invokePermissionCache.setClassInspector(_cacheInspector);
+        _fieldCache.setClassInspector(_cacheInspector);
+        _declaredMethods[0].setClassInspector(_cacheInspector);
+        _declaredMethods[1].setClassInspector(_cacheInspector);
     }
 
     public static Method getMethod(OgnlContext context, Class target, String name, Node[] children, boolean includeStatic)
     {
         if (context.getCurrentType() == context.getPreviousType())
             return false;
-        
+
         return context.getCurrentType() != null && !context.getCurrentType().isArray()
                && context.getPreviousType() != null && !context.getPreviousType().isArray()
                && (!Number.class.isAssignableFrom(context.getCurrentType())

src/test/java/ognl/TestOgnlRuntime.java

 
         Method m = OgnlRuntime.getReadMethod(list.getClass(), "total");
         assertNotNull(m);
-        
+
         assertEquals(ListSource.class, OgnlRuntime.getCompiler().getSuperOrInterfaceClass(m, list.getClass()));
     }
 
     public void test_Get_Private_Class() throws Exception
     {
         List list = Arrays.asList(new String[]{"hello", "world"});
-        
+
         Method m = OgnlRuntime.getReadMethod(list.getClass(), "iterator");
         assertNotNull(m);
-        
+
         assertEquals(Iterable.class, OgnlRuntime.getCompiler().getSuperOrInterfaceClass(m, list.getClass()));
     }
 
     }
 
     public void test_Get_Read_Method()
-     throws Exception
+            throws Exception
     {
         Method m = OgnlRuntime.getReadMethod(Bean2.class, "pageBreakAfter");
         assertNotNull(m);
 
             fail("ClassNotFoundException should have been thrown by previous reference to <made.up.Name> class.");
         } catch (Exception et) {
-            
+
             assertTrue(MethodFailedException.class.isInstance(et));
             assertTrue(et.getMessage().indexOf("made.up.Name") > -1);
         }
     }
 
     public void test_Setter_Returns()
-    throws Exception
+            throws Exception
     {
         OgnlContext context = (OgnlContext) Ognl.createDefaultContext(null);
         SetterReturns root = new SetterReturns();
         Ognl.setValue("value", context, root, "12__");
         assertEquals(Ognl.getValue("value", context, root), "12__");
     }
+
+    public void test_Class_Cache_Inspector()
+        throws Exception
+    {
+        OgnlRuntime.clearCache();
+        assertEquals(0, OgnlRuntime._propertyDescriptorCache.getSize());
+
+        Root root = new Root();
+        OgnlContext context = (OgnlContext) Ognl.createDefaultContext(null);
+        Node expr = Ognl.compileExpression(context, root, "property.bean3.value != null");
+
+        assertTrue((Boolean)expr.getAccessor().get(context, root));
+
+        int size = OgnlRuntime._propertyDescriptorCache.getSize();
+        assertTrue(size > 0);
+
+        OgnlRuntime.clearCache();
+        assertEquals(0, OgnlRuntime._propertyDescriptorCache.getSize());
+
+        // now register class cache prevention
+        
+        OgnlRuntime.setClassCacheInspector(new TestCacheInspector());
+
+        expr = Ognl.compileExpression(context, root, "property.bean3.value != null");
+        assertTrue((Boolean)expr.getAccessor().get(context, root));
+
+        assertEquals((size - 1), OgnlRuntime._propertyDescriptorCache.getSize());
+    }
+
+    class TestCacheInspector implements ClassCacheInspector {
+
+        public boolean shouldCache(Class type)
+        {
+            if (type == null || type == Root.class)
+                return false;
+
+            return true;
+        }
+    }
 }
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.