Author: henrib
Date: Mon Nov 9 22:15:43 2009
New Revision: 834256
URL: http://svn.apache.org/viewvc?rev=834256&view=rev
Log:
Fix for JEXL-61; all caches get wrapped by SoftReference, allowing them to be
GCed under memory pressure.
Modified:
commons/proper/jexl/trunk/pom.xml
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl/Interpreter.java
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl/JexlEngine.java
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl/UnifiedJEXL.java
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl/util/Introspector.java
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl/util/introspection/Introspector.java
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl/util/introspection/IntrospectorBase.java
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl/util/introspection/MethodKey.java
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl/util/introspection/MethodMap.java
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl/util/introspection/UberspectImpl.java
Modified: commons/proper/jexl/trunk/pom.xml
URL:
http://svn.apache.org/viewvc/commons/proper/jexl/trunk/pom.xml?rev=834256&r1=834255&r2=834256&view=diff
==============================================================================
--- commons/proper/jexl/trunk/pom.xml (original)
+++ commons/proper/jexl/trunk/pom.xml Mon Nov 9 22:15:43 2009
@@ -19,240 +19,240 @@
xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/maven-v4_0_0.xsd">
- <parent>
- <groupId>org.apache.commons</groupId>
- <artifactId>commons-parent</artifactId>
- <version>12</version>
- </parent>
- <modelVersion>4.0.0</modelVersion>
- <groupId>commons-jexl</groupId>
- <artifactId>commons-jexl</artifactId>
- <version>2.0-SNAPSHOT</version>
- <name>Commons JEXL</name>
- <inceptionYear>2003</inceptionYear>
- <description>Jexl is an implementation of the JSTL Expression Language with
extensions.</description>
- <url>http://commons.apache.org/jexl/</url>
-
- <issueManagement>
- <system>jira</system>
- <url>http://issues.apache.org/jira/browse/JEXL</url>
- </issueManagement>
-
- <scm>
-
<connection>scm:svn:http://svn.apache.org/repos/asf/commons/proper/jexl/trunk</connection>
-
<developerConnection>scm:svn:https://svn.apache.org/repos/asf/commons/proper/jexl/trunk</developerConnection>
- <url>http://svn.apache.org/viewvc/commons/proper/jexl/trunk</url>
- </scm>
-
- <developers>
- <developer>
- <name>dIon Gillard</name>
- <id>dion</id>
- <email>[email protected]</email>
- <organization>Apache Software Foundation</organization>
- </developer>
- <developer>
- <name>Geir Magnusson Jr.</name>
- <id>geirm</id>
- <email>[email protected]</email>
- <organization>independent</organization>
- </developer>
- <developer>
- <name>Tim O'Brien</name>
- <id>tobrien</id>
- <email>[email protected]</email>
- <organization>independent</organization>
- </developer>
- <developer>
- <name>Peter Royal</name>
- <id>proyal</id>
- <email>[email protected]</email>
- <organization>Apache Software Foundation</organization>
- </developer>
- <developer>
- <name>James Strachan</name>
- <id>jstrachan</id>
- <email>[email protected]</email>
- <organization>SpiritSoft, Inc.</organization>
- </developer>
- <developer>
- <name>Rahul Akolkar</name>
- <id>rahul</id>
- <email>rahul AT apache.org</email>
- <organization>Apache Software Foundation</organization>
- </developer>
- <developer>
- <name>Henri Biestro</name>
- <id>henrib</id>
- <email>henrib AT apache.org</email>
- </developer>
- </developers>
-
- <dependencies>
- <dependency>
- <groupId>commons-logging</groupId>
- <artifactId>commons-logging</artifactId>
- <version>1.1.1</version>
- </dependency>
- <dependency>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
- <version>3.8.2</version>
- <optional>true</optional>
- </dependency>
+ <parent>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-parent</artifactId>
+ <version>12</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>commons-jexl</groupId>
+ <artifactId>commons-jexl</artifactId>
+ <version>2.0-SNAPSHOT</version>
+ <name>Commons JEXL</name>
+ <inceptionYear>2003</inceptionYear>
+ <description>Jexl is an implementation of the JSTL Expression Language
with extensions.</description>
+ <url>http://commons.apache.org/jexl/</url>
+
+ <issueManagement>
+ <system>jira</system>
+ <url>http://issues.apache.org/jira/browse/JEXL</url>
+ </issueManagement>
+
+ <scm>
+
<connection>scm:svn:http://svn.apache.org/repos/asf/commons/proper/jexl/trunk</connection>
+
<developerConnection>scm:svn:https://svn.apache.org/repos/asf/commons/proper/jexl/trunk</developerConnection>
+ <url>http://svn.apache.org/viewvc/commons/proper/jexl/trunk</url>
+ </scm>
+
+ <developers>
+ <developer>
+ <name>dIon Gillard</name>
+ <id>dion</id>
+ <email>[email protected]</email>
+ <organization>Apache Software Foundation</organization>
+ </developer>
+ <developer>
+ <name>Geir Magnusson Jr.</name>
+ <id>geirm</id>
+ <email>[email protected]</email>
+ <organization>independent</organization>
+ </developer>
+ <developer>
+ <name>Tim O'Brien</name>
+ <id>tobrien</id>
+ <email>tobrien AT apache DOT org</email>
+ <organization>independent</organization>
+ </developer>
+ <developer>
+ <name>Peter Royal</name>
+ <id>proyal</id>
+ <email>proyal AT apache DOT org</email>
+ <organization>Apache Software Foundation</organization>
+ </developer>
+ <developer>
+ <name>James Strachan</name>
+ <id>jstrachan</id>
+ <email>jstrachan AT apache DOT org</email>
+ <organization>SpiritSoft, Inc.</organization>
+ </developer>
+ <developer>
+ <name>Rahul Akolkar</name>
+ <id>rahul</id>
+ <email>rahul AT apache DOT org</email>
+ <organization>Apache Software Foundation</organization>
+ </developer>
+ <developer>
+ <name>Henri Biestro</name>
+ <id>henrib</id>
+ <email>henrib AT apache DOT org</email>
+ </developer>
+ </developers>
+
+ <dependencies>
+ <dependency>
+ <groupId>commons-logging</groupId>
+ <artifactId>commons-logging</artifactId>
+ <version>1.1.1</version>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>3.8.2</version>
+ <optional>true</optional>
+ </dependency>
<!-- For JSR-223 API -->
- <dependency>
- <groupId>org.apache.bsf</groupId>
- <artifactId>bsf-api</artifactId>
- <version>3.0-beta3</version>
- <scope>compile</scope>
- </dependency>
- </dependencies>
-
- <properties>
- <maven.compile.source>1.5</maven.compile.source>
- <maven.compile.target>1.5</maven.compile.target>
- <commons.componentid>jexl</commons.componentid>
- <commons.release.version>2.0</commons.release.version>
- <commons.binary.suffix></commons.binary.suffix>
- <commons.jira.id>JEXL</commons.jira.id>
- <commons.jira.pid>12310479</commons.jira.pid>
+ <dependency>
+ <groupId>org.apache.bsf</groupId>
+ <artifactId>bsf-api</artifactId>
+ <version>3.0-beta3</version>
+ <scope>compile</scope>
+ </dependency>
+ </dependencies>
+
+ <properties>
+ <maven.compile.source>1.5</maven.compile.source>
+ <maven.compile.target>1.5</maven.compile.target>
+ <commons.componentid>jexl</commons.componentid>
+ <commons.release.version>2.0</commons.release.version>
+ <commons.binary.suffix></commons.binary.suffix>
+ <commons.jira.id>JEXL</commons.jira.id>
+ <commons.jira.pid>12310479</commons.jira.pid>
<!-- Temp fix until parent POM is updated -->
- <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
- <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
- </properties>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+ </properties>
- <build>
+ <build>
<!-- temporarily override the parent POM (v 11) until that is updated -->
- <resources>
- <resource>
- <directory>${basedir}</directory>
- <targetPath>META-INF</targetPath>
- <includes>
- <include>NOTICE.txt</include>
- <include>LICENSE.txt</include>
- </includes>
- </resource>
+ <resources>
+ <resource>
+ <directory>${basedir}</directory>
+ <targetPath>META-INF</targetPath>
+ <includes>
+ <include>NOTICE.txt</include>
+ <include>LICENSE.txt</include>
+ </includes>
+ </resource>
<!-- This is the default, but is currently missing from the parent POM
(v11) -->
- <resource>
- <directory>src/main/resources</directory>
- </resource>
- </resources>
- <plugins>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-surefire-plugin</artifactId>
- <configuration>
- <includes>
- <include>**/*Test.java</include>
- </includes>
- </configuration>
- </plugin>
- <plugin>
- <artifactId>maven-assembly-plugin</artifactId>
- <configuration>
- <descriptors>
- <descriptor>src/main/assembly/bin.xml</descriptor>
- <descriptor>src/main/assembly/src.xml</descriptor>
- </descriptors>
- <tarLongFileMode>gnu</tarLongFileMode>
- </configuration>
- </plugin>
- <plugin>
- <groupId>org.codehaus.mojo</groupId>
- <artifactId>javacc-maven-plugin</artifactId>
- <version>2.6</version>
- <executions>
- <execution>
- <id>jexl-jjtree</id>
- <configuration>
-
<sourceDirectory>${basedir}/src/main/java/org/apache/commons/jexl/parser</sourceDirectory>
-
<outputDirectory>${project.build.directory}/generated-sources/java</outputDirectory>
-
<timestampDirectory>${project.build.directory}/generated-sources/javacc-timestamp</timestampDirectory>
- <packageName>org.apache.commons.jexl.parser</packageName>
- </configuration>
- <goals>
- <goal>jjtree-javacc</goal>
- </goals>
- </execution>
- </executions>
- </plugin>
- </plugins>
+ <resource>
+ <directory>src/main/resources</directory>
+ </resource>
+ </resources>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <includes>
+ <include>**/*Test.java</include>
+ </includes>
+ </configuration>
+ </plugin>
+ <plugin>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <configuration>
+ <descriptors>
+ <descriptor>src/main/assembly/bin.xml</descriptor>
+ <descriptor>src/main/assembly/src.xml</descriptor>
+ </descriptors>
+ <tarLongFileMode>gnu</tarLongFileMode>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>javacc-maven-plugin</artifactId>
+ <version>2.6</version>
+ <executions>
+ <execution>
+ <id>jexl-jjtree</id>
+ <configuration>
+
<sourceDirectory>${basedir}/src/main/java/org/apache/commons/jexl/parser</sourceDirectory>
+
<outputDirectory>${project.build.directory}/generated-sources/java</outputDirectory>
+
<timestampDirectory>${project.build.directory}/generated-sources/javacc-timestamp</timestampDirectory>
+
<packageName>org.apache.commons.jexl.parser</packageName>
+ </configuration>
+ <goals>
+ <goal>jjtree-javacc</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
</build>
<reporting>
- <plugins>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-changes-plugin</artifactId>
- <version>2.0</version>
- <configuration>
- <xmlPath>${basedir}/xdocs/changes.xml</xmlPath>
- <issueLinkTemplate>%URL%/%ISSUE%</issueLinkTemplate>
- </configuration>
- <reportSets>
- <reportSet>
- <reports>
- <report>changes-report</report>
- </reports>
- </reportSet>
- </reportSets>
- </plugin>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-checkstyle-plugin</artifactId>
- <version>2.3</version>
- <configuration>
-
<configLocation>${basedir}/src/main/config/checkstyle.xml</configLocation>
- <excludes>org/apache/commons/jexl/parser/*.java</excludes>
- <headerFile>${basedir}/src/main/config/header.txt</headerFile>
- <enableRulesSummary>false</enableRulesSummary>
- </configuration>
- </plugin>
- <plugin>
- <groupId>org.codehaus.mojo</groupId>
- <artifactId>cobertura-maven-plugin</artifactId>
- <version>2.3</version>
- <configuration>
- <instrumentation>
- <excludes>
- <exclude>org/apache/commons/jexl/parser/*.class</exclude>
- <exclude>apache/commons/jexl/**/*Test.class</exclude>
- </excludes>
- </instrumentation>
- </configuration>
- </plugin>
- <plugin>
- <groupId>org.codehaus.mojo</groupId>
- <artifactId>findbugs-maven-plugin</artifactId>
- <version>2.1</version>
- <configuration>
- <xmlOutput>true</xmlOutput>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-changes-plugin</artifactId>
+ <version>2.0</version>
+ <configuration>
+ <xmlPath>${basedir}/xdocs/changes.xml</xmlPath>
+ <issueLinkTemplate>%URL%/%ISSUE%</issueLinkTemplate>
+ </configuration>
+ <reportSets>
+ <reportSet>
+ <reports>
+ <report>changes-report</report>
+ </reports>
+ </reportSet>
+ </reportSets>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-checkstyle-plugin</artifactId>
+ <version>2.3</version>
+ <configuration>
+
<configLocation>${basedir}/src/main/config/checkstyle.xml</configLocation>
+ <excludes>org/apache/commons/jexl/parser/*.java</excludes>
+
<headerFile>${basedir}/src/main/config/header.txt</headerFile>
+ <enableRulesSummary>false</enableRulesSummary>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>cobertura-maven-plugin</artifactId>
+ <version>2.3</version>
+ <configuration>
+ <instrumentation>
+ <excludes>
+
<exclude>org/apache/commons/jexl/parser/*.class</exclude>
+
<exclude>apache/commons/jexl/**/*Test.class</exclude>
+ </excludes>
+ </instrumentation>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>findbugs-maven-plugin</artifactId>
+ <version>2.1</version>
+ <configuration>
+ <xmlOutput>true</xmlOutput>
<!-- Optional derectory to put findbugs xdoc xml report -->
- <xmlOutputDirectory>target/site</xmlOutputDirectory>
- </configuration>
- </plugin>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-pmd-plugin</artifactId>
- <version>2.4</version>
- <configuration>
- <targetJdk>1.5</targetJdk>
- <excludes>
- <exclude>org/apache/commons/jexl/parser/*.java</exclude>
- </excludes>
- <rulesets>
- <ruleset>/rulesets/braces.xml</ruleset>
- <ruleset>/rulesets/unusedcode.xml</ruleset>
- <ruleset>/rulesets/imports.xml</ruleset>
- <ruleset>/rulesets/codesize.xml</ruleset>
- <ruleset>/rulesets/coupling.xml</ruleset>
- <ruleset>/rulesets/design.xml</ruleset>
- <ruleset>/rulesets/strings.xml</ruleset>
- </rulesets>
- </configuration>
- </plugin>
- </plugins>
+ <xmlOutputDirectory>target/site</xmlOutputDirectory>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-pmd-plugin</artifactId>
+ <version>2.4</version>
+ <configuration>
+ <targetJdk>1.5</targetJdk>
+ <excludes>
+
<exclude>org/apache/commons/jexl/parser/*.java</exclude>
+ </excludes>
+ <rulesets>
+ <ruleset>/rulesets/braces.xml</ruleset>
+ <ruleset>/rulesets/unusedcode.xml</ruleset>
+ <ruleset>/rulesets/imports.xml</ruleset>
+ <ruleset>/rulesets/codesize.xml</ruleset>
+ <ruleset>/rulesets/coupling.xml</ruleset>
+ <ruleset>/rulesets/design.xml</ruleset>
+ <ruleset>/rulesets/strings.xml</ruleset>
+ </rulesets>
+ </configuration>
+ </plugin>
+ </plugins>
</reporting>
</project>
\ No newline at end of file
Modified:
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl/Interpreter.java
URL:
http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl/Interpreter.java?rev=834256&r1=834255&r2=834256&view=diff
==============================================================================
---
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl/Interpreter.java
(original)
+++
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl/Interpreter.java
Mon Nov 9 22:15:43 2009
@@ -319,7 +319,7 @@
Object entry = node.jjtGetChild(i).jjtAccept(this, data);
array[i] = entry;
}
- return array;
+ return arithmetic.narrowArrayType(array);
}
/** {...@inheritdoc} */
@@ -630,7 +630,7 @@
if (value == null
&& !(node.jjtGetParent() instanceof ASTReference)
&& !context.getVars().containsKey(name)) {
- JexlException xjexl = new JexlException(node, "undefined
variable " + node.image);
+ JexlException xjexl = new JexlException(node, "undefined
variable " + name);
return unknownVariable(xjexl);
}
return value;
Modified:
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl/JexlEngine.java
URL:
http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl/JexlEngine.java?rev=834256&r1=834255&r2=834256&view=diff
==============================================================================
---
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl/JexlEngine.java
(original)
+++
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl/JexlEngine.java
Mon Nov 9 22:15:43 2009
@@ -23,10 +23,13 @@
import java.io.InputStreamReader;
import java.io.StringReader;
import java.io.Reader;
+import java.lang.ref.SoftReference;
import java.util.Map;
import java.util.Collections;
import java.net.URL;
import java.net.URLConnection;
+import java.util.Map.Entry;
+import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -117,7 +120,7 @@
/**
* The expression cache.
*/
- protected Map<String, ASTJexlScript> cache = null;
+ protected SoftCache<String, ASTJexlScript> cache = null;
/**
* An empty/static/non-mutable JexlContext used instead of null context.
*/
@@ -229,6 +232,16 @@
}
/**
+ * Sets the class loader used to discover classes in 'new' expressions.
+ * <p>This method should be called as an optional step of the JexlEngine
+ * initialization code before expression creation & evaluation.</p>
+ * @param loader the class loader to use
+ */
+ public void setClassLoader(ClassLoader loader) {
+ uberspect.getIntrospector().setLoader(loader);
+ }
+
+ /**
* Sets a cache of the defined size for expressions.
* @param size if not strictly positive, no cache is used.
*/
@@ -238,7 +251,7 @@
if (size <= 0) {
cache = null;
} else if (cache == null || cache.size() != size) {
- cache = createCache(size);
+ cache = new SoftCache<String,ASTJexlScript>(size);
}
}
}
@@ -543,17 +556,86 @@
return new Interpreter(this, context);
}
+ /**
+ * A soft reference on cache.
+ * <p>The cache is held through a soft reference, allowing it to be GCed
under
+ * memory pressure.</p>
+ * @param <K> the cache key entry type
+ * @param <V> the cache key value type
+ */
+ protected class SoftCache<K,V> {
+ /**
+ * The cache size.
+ */
+ private final int size;
+ /**
+ * The soft reference to the cache map.
+ */
+ private SoftReference<Map<K, V>> ref = null;
+ /**
+ * Creates a new instance of a soft cache.
+ * @param theSize the cache size
+ */
+ SoftCache(int theSize) {
+ size = theSize;
+ }
+
+ /**
+ * Returns the cache size.
+ * @return the cache size
+ */
+ int size() {
+ return size;
+ }
+
+ /**
+ * Produces the cache entry set.
+ * @return the cache entry set
+ */
+ @SuppressWarnings("unchecked")
+ Set<Entry<K,V>> entrySet() {
+ Map<K, V> map = ref != null? ref.get() : null;
+ return map != null? map.entrySet() : (Set<Entry<K,V>>)
Collections.EMPTY_SET;
+ }
+
+ /**
+ * Gets a value from cache.
+ * @param key the cache entry key
+ * @return the cache entry value
+ */
+ V get(K key) {
+ final Map<K, V> map = ref != null? ref.get() : null;
+ return map != null? map.get(key) : null;
+ }
+
+ /**
+ * Puts a value in cache.
+ * @param key the cache entry key
+ * @param script the cache entry value
+ */
+ void put(K key, V script) {
+ Map<K, V> map = ref != null? ref.get() : null;
+ if (map == null) {
+ map = createCache(size);
+ ref = new SoftReference<Map<K, V>>(map);
+ }
+ map.put(key, script);
+ }
+ }
+
/**
- * Creates a SimpleNode cache.
+ * Creates a cache.
+ * @param <K> the key type
+ * @param <V> the value type
* @param cacheSize the cache size, must be > 0
* @return a Map usable as a cache bounded to the given size
*/
- protected Map<String, ASTJexlScript> createCache(final int cacheSize) {
- return new java.util.LinkedHashMap<String, ASTJexlScript>(cacheSize,
LOAD_FACTOR, true) {
+ protected <K,V> Map<K, V> createCache(final int cacheSize) {
+ return new java.util.LinkedHashMap<K, V>(cacheSize, LOAD_FACTOR, true)
{
/** Serial version UID. */
private static final long serialVersionUID = 3801124242820219131L;
@Override
- protected boolean removeEldestEntry(Map.Entry<String,
ASTJexlScript> eldest) {
+ protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
return size() > cacheSize;
}
};
Modified:
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl/UnifiedJEXL.java
URL:
http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl/UnifiedJEXL.java?rev=834256&r1=834255&r2=834256&view=diff
==============================================================================
---
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl/UnifiedJEXL.java
(original)
+++
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl/UnifiedJEXL.java
Mon Nov 9 22:15:43 2009
@@ -16,8 +16,6 @@
*/
package org.apache.commons.jexl;
-import java.util.Map;
-import java.util.LinkedHashMap;
import java.util.ArrayList;
import org.apache.commons.jexl.parser.JexlNode;
import org.apache.commons.jexl.parser.ParseException;
@@ -80,11 +78,9 @@
/** The JEXL engine instance. */
private final JexlEngine jexl;
/** The expression cache. */
- private final Map<String, Expression> cache;
+ private final JexlEngine.SoftCache<String,Expression> cache;
/** The default cache size. */
private static final int CACHE_SIZE = 512;
- /** The default cache load-factor. */
- private static final float LOAD_FACTOR = 0.75f;
/**
* Creates a new instance of UnifiedJEXL with a default size cache.
* @param aJexl the JexlEngine to use.
@@ -100,23 +96,7 @@
*/
public UnifiedJEXL(JexlEngine aJexl, int cacheSize) {
this.jexl = aJexl;
- this.cache = cacheSize > 0 ? createCache(cacheSize) : null;
- }
-
- /**
- * Creates an expression cache.
- * @param cacheSize the cache size, must be > 0
- * @return a LinkedHashMap
- */
- private static Map<String, Expression> createCache(final int cacheSize) {
- return new LinkedHashMap<String, Expression>(cacheSize, LOAD_FACTOR,
true) {
- /** Serial version UID. */
- private static final long serialVersionUID = -6515503595421899722L;
- @Override
- protected boolean removeEldestEntry(Map.Entry<String, Expression>
eldest) {
- return size() > cacheSize;
- }
- };
+ this.cache = aJexl.new SoftCache<String,Expression>(cacheSize);
}
/**
@@ -732,13 +712,15 @@
try {
if (cache == null) {
return parseExpression(expression);
- } else synchronized (cache) {
- Expression stmt = cache.get(expression);
- if (stmt == null) {
- stmt = parseExpression(expression);
- cache.put(expression, stmt);
+ } else {
+ synchronized (cache) {
+ Expression stmt = cache.get(expression);
+ if (stmt == null) {
+ stmt = parseExpression(expression);
+ cache.put(expression, stmt);
+ }
+ return stmt;
}
- return stmt;
}
} catch (JexlException xjexl) {
Exception xuel = new Exception("failed to parse '" + expression +
"'", xjexl);
Modified:
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl/util/Introspector.java
URL:
http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl/util/Introspector.java?rev=834256&r1=834255&r2=834256&view=diff
==============================================================================
---
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl/util/Introspector.java
(original)
+++
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl/util/Introspector.java
Mon Nov 9 22:15:43 2009
@@ -16,6 +16,7 @@
*/
package org.apache.commons.jexl.util;
+import java.lang.ref.SoftReference;
import java.lang.reflect.Method;
import java.lang.reflect.Constructor;
@@ -31,13 +32,70 @@
* <p>Finding methods as well as property getters & setters.</p>
* @since 1.0
*/
-public class Introspector {
+public class Introspector {
/** The default uberspector that handles all introspection patterns. */
private static volatile Uberspect uberSpect;
/** The logger to use for all warnings & errors. */
protected final Log rlog;
/** The (low level) introspector to use for introspection services. */
- protected final org.apache.commons.jexl.util.introspection.Introspector
introspector;
+ protected final Reference introspector;
+
+ /**
+ * A soft reference to an Introspector.
+ * <p>
+ * If memory pressure becomes critical, this will allow the introspector
to be GCed;
+ * in turn, classes it introspected that are no longer in use may be GCed
as well.
+ * </p>
+ */
+ protected final class Reference {
+ /**
+ * The introspector logger.
+ */
+ private final Log logger;
+ /**
+ * The soft reference to the introspector currently in use.
+ */
+ private volatile
SoftReference<org.apache.commons.jexl.util.introspection.Introspector> ref;
+ /**
+ * Creates a new instance.
+ * @param theLogger logger used by the underlying introspector instance
+ * @param is the underlying introspector instance
+ */
+ protected Reference(Log theLogger,
org.apache.commons.jexl.util.introspection.Introspector is) {
+ logger = theLogger;
+ ref = new
SoftReference<org.apache.commons.jexl.util.introspection.Introspector>(is);
+ }
+
+ /**
+ * Creates a new instance.
+ * @param theLogger logger used by the underlying introspector instance
+ */
+ protected Reference(Log theLogger) {
+ this(theLogger, new
org.apache.commons.jexl.util.introspection.Introspector(theLogger));
+ }
+
+ /**
+ * Gets the current introspector.
+ * <p>If the reference has been collected, this method will recreate
the underlying introspector.</p>
+ * @return the introspector
+ */
+ // CSOFF: DoubleCheckedLocking
+ public org.apache.commons.jexl.util.introspection.Introspector get() {
+ org.apache.commons.jexl.util.introspection.Introspector intro =
ref.get();
+ if (intro == null) {
+ // double checked locking (fixed by Java 5 memory model).
+ synchronized(this) {
+ intro = ref.get();
+ if (intro == null) {
+ intro = new
org.apache.commons.jexl.util.introspection.Introspector(logger);
+ ref = new
SoftReference<org.apache.commons.jexl.util.introspection.Introspector>(intro);
+ }
+ }
+ }
+ return intro;
+ }
+ // CSON: DoubleCheckedLocking
+ }
/**
* Gets the default instance of Uberspect.
@@ -49,7 +107,7 @@
*/
// CSOFF: DoubleCheckedLocking
public static Uberspect getUberspect() {
- // uses a double-locking pattern since java5 memory model allows it
+ // uses a double-locking pattern
if (uberSpect == null) {
synchronized (Uberspect.class) {
if (uberSpect == null) {
@@ -78,7 +136,7 @@
*/
public Introspector(Log log,
org.apache.commons.jexl.util.introspection.Introspector is) {
rlog = log;
- introspector = is;
+ introspector = new Reference(log, is);
}
/**
@@ -95,7 +153,7 @@
}
try {
return Integer.valueOf(arg.toString());
- } catch(NumberFormatException xnumber) {
+ } catch (NumberFormatException xnumber) {
return null;
}
}
@@ -106,7 +164,7 @@
* @return a String if it can be converted, null otherwise
*/
protected String toString(Object arg) {
- return arg == null? null : arg.toString();
+ return arg == null ? null : arg.toString();
}
/**
@@ -123,7 +181,7 @@
* CSOFF: RedundantThrows
*/
protected final Method getMethod(Class<?> c, String name, Object[] params)
throws IllegalArgumentException {
- return introspector.getMethod(c, new MethodKey(name, params));
+ return introspector.get().getMethod(c, new MethodKey(name, params));
}
/**
@@ -137,7 +195,7 @@
* CSOFF: RedundantThrows
*/
protected final Method getMethod(Class<?> c, MethodKey key) throws
IllegalArgumentException {
- return introspector.getMethod(c, key);
+ return introspector.get().getMethod(c, key);
}
/**
@@ -148,14 +206,16 @@
*/
public final Constructor<?> getConstructor(Object ctorHandle, Object[]
args) {
String className = null;
+ Class<?> clazz = null;
if (ctorHandle instanceof Class<?>) {
- className = ((Class<?>) ctorHandle).getName();
+ clazz = (Class<?>) ctorHandle;
+ className = clazz.getName();
} else if (ctorHandle != null) {
className = ctorHandle.toString();
} else {
return null;
}
- return introspector.getConstructor(new MethodKey(className, args));
+ return introspector.get().getConstructor(clazz, new
MethodKey(className, args));
}
/**
@@ -167,7 +227,7 @@
*/
public final AbstractExecutor.Method getMethodExecutor(Object obj, String
name, Object[] args) {
AbstractExecutor.Method me = new MethodExecutor(this, obj, name, args);
- return me.isAlive()? me : null;
+ return me.isAlive() ? me : null;
}
/**
@@ -248,12 +308,11 @@
return executor;
}
}
- // if that didn't work, look for get("foo")
+ // if that didn't work, look for set("foo")
executor = new DuckSetExecutor(this, claz, property, arg);
if (executor.isAlive()) {
return executor;
}
return null;
- }
-
+ }
}
Modified:
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl/util/introspection/Introspector.java
URL:
http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl/util/introspection/Introspector.java?rev=834256&r1=834255&r2=834256&view=diff
==============================================================================
---
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl/util/introspection/Introspector.java
(original)
+++
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl/util/introspection/Introspector.java
Mon Nov 9 22:15:43 2009
@@ -56,13 +56,6 @@
*/
public final class Introspector extends IntrospectorBase {
/**
- * define a public string so that it can be looked for
- * if interested.
- */
- public static final String CACHEDUMP_MSG =
- "Introspector : detected classloader change. Dumping cache.";
-
- /**
* Creates a new instance.
* @param logger a {...@link Log}.
*/
@@ -78,8 +71,8 @@
*
* @return The desired Method object.
* @throws IllegalArgumentException When the parameters passed in can not
be used for introspection.
- * CSOFF: RedundantThrows
*/
+ // CSOFF: RedundantThrows
@Override
public Method getMethod(Class<?> c, MethodKey key) throws
IllegalArgumentException {
// just delegate to the base class
@@ -95,22 +88,25 @@
}
return null;
}
-
// CSON: RedundantThrows
+
/**
- * Gets the method defined by <code>key</code> for the Class
<code>c</code>.
+ * Gets the constructor defined by <code>key</code> for the Class
<code>c</code>.
*
+ * @param c the class to instantiate an object of if known, null otherwise
* @param key MethodKey of the method being searched for
*
* @return The desired Method object.
* @throws IllegalArgumentException When the parameters passed in can not
be used for introspection.
- * CSOFF: RedundantThrows
+ *
+ *
*/
+ // CSOFF: RedundantThrows
@Override
- public Constructor<?> getConstructor(MethodKey key) throws
IllegalArgumentException {
+ public Constructor<?> getConstructor(Class<?> c, MethodKey key) throws
IllegalArgumentException {
// just delegate to the base class
try {
- return super.getConstructor(key);
+ return super.getConstructor(c, key);
} catch (MethodKey.AmbiguousException ae) {
// whoops. Ambiguous. Make a nice log message and return null...
if (rlog != null) {
@@ -119,15 +115,6 @@
}
}
return null;
- } // CSON: RedundantThrows
-
- /**
- * Clears the classmap and classname
- * caches, and logs that we did so.
- */
- @Override
- protected void clearCache() {
- super.clearCache();
- rlog.info(CACHEDUMP_MSG);
}
+ // CSON: RedundantThrows
}
Modified:
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl/util/introspection/IntrospectorBase.java
URL:
http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl/util/introspection/IntrospectorBase.java?rev=834256&r1=834255&r2=834256&view=diff
==============================================================================
---
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl/util/introspection/IntrospectorBase.java
(original)
+++
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl/util/introspection/IntrospectorBase.java
Mon Nov 9 22:15:43 2009
@@ -19,12 +19,8 @@
import java.lang.reflect.Method;
import java.lang.reflect.Constructor;
-import java.lang.ref.SoftReference;
-import java.util.HashSet;
import java.util.Map;
import java.util.HashMap;
-import java.util.WeakHashMap;
-import java.util.Set;
import java.util.List;
import java.util.LinkedList;
@@ -61,17 +57,14 @@
public class IntrospectorBase {
/** the logger. */
protected final Log rlog;
-
/**
* Holds the method maps for the classes we know about, keyed by Class.
- * Implemented as WeakHashMap so we wont prevent an unused class from
being GCed.
*/
- private final Map<Class<?>, ClassMap> classMethodMaps = new
WeakHashMap<Class<?>, ClassMap>();
+ private final Map<Class<?>, ClassMap> classMethodMaps = new
HashMap<Class<?>, ClassMap>();
/**
- * Holds the qualified class names for the classes we hold in the
- * classMethodMaps hash.
+ * The class loader used to solve constructors if needed.
*/
- private Set<String> cachedClassNames = new HashSet<String>();
+ private ClassLoader loader;
/**
* Holds the map of classes ctors we know about as well as unknown ones.
*/
@@ -79,7 +72,7 @@
/**
* Holds the set of classes we have introspected.
*/
- private Map<String, SoftReference<Class<?>>> constructibleClasses = new
HashMap<String, SoftReference<Class<?>>>();
+ private final Map<String, Class<?>> constructibleClasses = new
HashMap<String, Class<?>>();
/**
* Create the introspector.
@@ -87,6 +80,7 @@
*/
public IntrospectorBase(Log log) {
this.rlog = log;
+ loader = getClass().getClassLoader();
}
/**
@@ -98,13 +92,15 @@
* @throws IllegalArgumentException When the parameters passed in can
not be used for introspection.
* @throws MethodKey.AmbiguousException When the method map contains more
than
* one match for the requested signature.
- * CSOFF: RedundantThrows
+ *
*/
+ //CSOFF: RedundantThrows
public Method getMethod(Class<?> c, MethodKey key) {
ClassMap classMap = getMap(c);
return classMap.findMethod(key);
- } // CSON: RedundantThrows
+ }
+ // CSON: RedundantThrows
/**
* Gets the accessible methods names known for a given class.
@@ -131,6 +127,24 @@
private static final Constructor<?> CTOR_MISS =
CacheMiss.class.getConstructors()[0];
/**
+ * Sets the class loader used to solve constructors.
+ * <p>Also cleans the constructors cache.</p>
+ * @param cloader the class loader; if null, use this instance class loader
+ */
+ public void setLoader(ClassLoader cloader) {
+ if (cloader == null) {
+ cloader = getClass().getClassLoader();
+ }
+ if (!cloader.equals(loader)) {
+ synchronized(constructorsMap) {
+ loader = cloader;
+ constructorsMap.clear();
+ constructibleClasses.clear();
+ }
+ }
+ }
+
+ /**
* Gets the constructor defined by the <code>MethodKey</code>.
*
* @param key Key of the constructor being searched for
@@ -138,9 +152,22 @@
* @throws IllegalArgumentException When the parameters passed in can
not be used for introspection.
* @throws MethodKey.AmbiguousException When the method map contains more
than
* one match for the requested signature.
- * CSOFF: RedundantThrows
*/
public Constructor<?> getConstructor(final MethodKey key) {
+ return getConstructor(null, key);
+ }
+
+ /**
+ * Gets the constructor defined by the <code>MethodKey</code>.
+ * @param c the class we want to instantiate
+ * @param key Key of the constructor being searched for
+ * @return The desired Constructor object.
+ * @throws IllegalArgumentException When the parameters passed in can
not be used for introspection.
+ * @throws MethodKey.AmbiguousException When the method map contains more
than
+ * one match for the requested signature.
+ */
+ //CSOFF: RedundantThrows
+ public Constructor<?> getConstructor(final Class<?> c, final MethodKey
key) {
Constructor<?> ctor = null;
synchronized(constructorsMap) {
ctor = constructorsMap.get(key);
@@ -152,22 +179,17 @@
if (ctor == null) {
final String cname = key.getMethod();
// do we know about this class?
- Class<?> clazz = null;
- SoftReference<Class<?>> sc = constructibleClasses.get(cname);
- if (sc != null) {
- clazz = sc.get();
- if (clazz == null) {
- // we knew about this class & it's gone, clear cache
- constructorsMap.clear();
- constructibleClasses = new HashMap<String,
SoftReference<Class<?>>>();
- }
- }
+ Class<?> clazz = constructibleClasses.get(cname);
try {
// do find the most specific ctor
if (clazz == null) {
- clazz = Class.forName(cname);
- // add it to list of know loaded classes
- constructibleClasses.put(cname, new
SoftReference<Class<?>>(clazz));
+ if (c != null && c.getName().equals(key.getMethod())) {
+ clazz = c;
+ } else {
+ clazz = loader.loadClass(cname);
+ }
+ // add it to list of known loaded classes
+ constructibleClasses.put(cname, clazz);
}
List<Constructor<?>> l = new LinkedList<Constructor<?>>();
for(Constructor<?> ictor : clazz.getConstructors()) {
@@ -193,6 +215,7 @@
}
return ctor;
}
+ // CSON: RedundantThrows
/**
* Gets the ClassMap for a given class.
@@ -202,50 +225,11 @@
private ClassMap getMap(Class<?> c) {
synchronized (classMethodMaps) {
ClassMap classMap = classMethodMaps.get(c);
- /*
- * if we don't have this, check to see if we have it by name. if
so,
- * then we have a classloader change so dump our caches.
- */
-
if (classMap == null) {
- if (cachedClassNames.contains(c.getName())) {
- /*
- * we have a map for a class with same name, but not this
- * class we are looking at. This implies a classloader
- * change, so dump
- */
- clearCache();
- }
-
- classMap = createClassMap(c);
+ classMap = new ClassMap(c,rlog);
+ classMethodMaps.put(c, classMap);
}
return classMap;
}
}
-
- /**
- * Creates a class map for specific class and registers it in the cache.
- * Also adds the qualified name to the name->class map for later
Classloader
- * change detection.
- * @param c class.
- * @return a {...@link ClassMap}
- */
- private ClassMap createClassMap(Class<?> c) {
- ClassMap classMap = new ClassMap(c,rlog);
- classMethodMaps.put(c, classMap);
- cachedClassNames.add(c.getName());
-
- return classMap;
- }
-
- /**
- * Clears the classmap and classname caches.
- */
- protected void clearCache() {
- // since we are synchronizing on this object, we have to clear it
rather
- // than just dump it.
- classMethodMaps.clear();
- // for speed, we can just make a new one and let the old one be GC'd
- cachedClassNames = new HashSet<String>();
- }
}
\ No newline at end of file
Modified:
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl/util/introspection/MethodKey.java
URL:
http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl/util/introspection/MethodKey.java?rev=834256&r1=834255&r2=834256&view=diff
==============================================================================
---
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl/util/introspection/MethodKey.java
(original)
+++
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl/util/introspection/MethodKey.java
Mon Nov 9 22:15:43 2009
@@ -145,9 +145,9 @@
/** {...@inheritdoc} */
@Override
- public boolean equals(Object arg) {
- if (arg instanceof MethodKey) {
- MethodKey key = (MethodKey) arg;
+ public boolean equals(Object obj) {
+ if (obj instanceof MethodKey) {
+ MethodKey key = (MethodKey) obj;
return method.equals(key.method) && Arrays.equals(params,
key.params);
}
return false;
Modified:
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl/util/introspection/MethodMap.java
URL:
http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl/util/introspection/MethodMap.java?rev=834256&r1=834255&r2=834256&view=diff
==============================================================================
---
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl/util/introspection/MethodMap.java
(original)
+++
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl/util/introspection/MethodMap.java
Mon Nov 9 22:15:43 2009
@@ -36,9 +36,7 @@
/**
* Keep track of all methods with the same name.
*/
- // CSOFF: VisibilityModifier
- private Map<String, List<Method>> methodByNameMap = new HashMap<String,
List<Method>>();
- // CSON: VisibilityModifier
+ private final Map<String, List<Method>> methodByNameMap = new
HashMap<String, List<Method>>();
/**
* Add a method to a list of methods by name. For a particular class we are
Modified:
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl/util/introspection/UberspectImpl.java
URL:
http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl/util/introspection/UberspectImpl.java?rev=834256&r1=834255&r2=834256&view=diff
==============================================================================
---
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl/util/introspection/UberspectImpl.java
(original)
+++
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl/util/introspection/UberspectImpl.java
Mon Nov 9 22:15:43 2009
@@ -42,7 +42,7 @@
public class UberspectImpl extends org.apache.commons.jexl.util.Introspector
implements Uberspect {
/** {...@inheritdoc} */
public Introspector getIntrospector() {
- return introspector;
+ return introspector.get();
}
/**
@@ -66,7 +66,7 @@
* {...@inheritdoc}
*/
@SuppressWarnings("unchecked")
- public Iterator<?> getIterator(Object obj, DebugInfo i) {
+ public Iterator<?> getIterator(Object obj, DebugInfo info) {
if (obj.getClass().isArray()) {
return new ArrayIterator(obj);
} else if (obj instanceof Collection<?>) {
@@ -74,13 +74,13 @@
} else if (obj instanceof Map<?,?>) {
return ((Map<?,?>) obj).values().iterator();
} else if (obj instanceof Iterator<?>) {
- rlog.warn(i.debugString()
+ rlog.warn(info.debugString()
+ "The iterative is not resetable; if used more than once,
"
+ "this may lead to unexpected results.");
return ((Iterator<?>) obj);
} else if (obj instanceof Enumeration<?>) {
- rlog.warn(i.debugString()
+ rlog.warn(info.debugString()
+ "The iterative is not resetable; if used more than once,
"
+ "this may lead to unexpected results.");
return new EnumerationIterator<Object>((Enumeration<Object>) obj);
@@ -95,7 +95,7 @@
if (Iterator.class.isAssignableFrom(returns)) {
return (Iterator<?>) iter.invoke(obj, (Object[])null);
} else {
- rlog.error(i.debugString()
+ rlog.error(info.debugString()
+ "iterator() method does not return a true
Iterator.");
}
// CSOFF: EmptyBlock
@@ -111,7 +111,7 @@
}
/* we have no clue what this is */
- rlog.warn(i.toString()
+ rlog.warn(info.toString()
+ "Could not determine type of iterator");
return null;
@@ -120,28 +120,28 @@
/**
* {...@inheritdoc}
*/
- public Constructor<?> getConstructor(Object ctorHandle, Object[] args,
DebugInfo i) {
+ public Constructor<?> getConstructor(Object ctorHandle, Object[] args,
DebugInfo info) {
return getConstructor(ctorHandle, args);
}
/**
* {...@inheritdoc}
*/
- public JexlMethod getMethod(Object obj, String methodName, Object[] args,
DebugInfo i) {
- return getMethodExecutor(obj, methodName, args);
+ public JexlMethod getMethod(Object obj, String method, Object[] args,
DebugInfo info) {
+ return getMethodExecutor(obj, method, args);
}
/**
* {...@inheritdoc}
*/
- public JexlPropertyGet getPropertyGet(Object obj, Object identifier,
DebugInfo i) {
+ public JexlPropertyGet getPropertyGet(Object obj, Object identifier,
DebugInfo info) {
return getGetExecutor(obj, identifier);
}
/**
* {...@inheritdoc}
*/
- public JexlPropertySet getPropertySet(final Object obj, final Object
identifier, Object arg, DebugInfo i) {
+ public JexlPropertySet getPropertySet(final Object obj, final Object
identifier, Object arg, DebugInfo info) {
return getSetExecutor(obj, identifier, arg);
}
}
\ No newline at end of file