Backported some of the tap4 OGNL related work,  such as ExpressionEvaluator service / pooling of OgnlContext objects / etc.

git-svn-id: https://svn.apache.org/repos/asf/tapestry/tapestry3/trunk@674683 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/tapestry-examples/tapestry-workbench/pom.xml b/tapestry-examples/tapestry-workbench/pom.xml
index 114ec38..ccee15f 100644
--- a/tapestry-examples/tapestry-workbench/pom.xml
+++ b/tapestry-examples/tapestry-workbench/pom.xml
@@ -80,7 +80,7 @@
                     <systemProperties>
                         <systemProperty>
                             <name>org.apache.tapestry.disable-caching</name>
-                            <value>false</value>
+                            <value>true</value>
                         </systemProperty>
                     </systemProperties>
                 </configuration>
diff --git a/tapestry-examples/tapestry-workbench/src/org/apache/tapestry/workbench/components/Border.java b/tapestry-examples/tapestry-workbench/src/org/apache/tapestry/workbench/components/Border.java
index b5ba5e0..7e18648 100644
--- a/tapestry-examples/tapestry-workbench/src/org/apache/tapestry/workbench/components/Border.java
+++ b/tapestry-examples/tapestry-workbench/src/org/apache/tapestry/workbench/components/Border.java
@@ -79,7 +79,7 @@
 	
 	public boolean isActivePage()
 	{
-		return getPageName().equals(getActivePageName());
+        return getPageName().equals(getActivePageName());
 	}
 
     public String getPageTitle()
diff --git a/tapestry-framework/pom.xml b/tapestry-framework/pom.xml
index 9131848..6c37468 100644
--- a/tapestry-framework/pom.xml
+++ b/tapestry-framework/pom.xml
@@ -56,6 +56,10 @@
             <artifactId>commons-lang</artifactId>
         </dependency>
         <dependency>
+            <groupId>commons-pool</groupId>
+            <artifactId>commons-pool</artifactId>
+        </dependency>
+        <dependency>
             <groupId>commons-beanutils</groupId>
             <artifactId>commons-beanutils</artifactId>
         </dependency>
diff --git a/tapestry-framework/src/org/apache/tapestry/AbstractComponent.java b/tapestry-framework/src/org/apache/tapestry/AbstractComponent.java
index bfdb63e..3dc696a 100644
--- a/tapestry-framework/src/org/apache/tapestry/AbstractComponent.java
+++ b/tapestry-framework/src/org/apache/tapestry/AbstractComponent.java
@@ -17,13 +17,13 @@
 import ognl.OgnlRuntime;
 import org.apache.tapestry.bean.BeanProvider;
 import org.apache.tapestry.bean.BeanProviderPropertyAccessor;
+import org.apache.tapestry.engine.ExpressionEvaluator;
 import org.apache.tapestry.engine.IPageLoader;
 import org.apache.tapestry.event.*;
 import org.apache.tapestry.listener.ListenerMap;
 import org.apache.tapestry.param.ParameterManager;
 import org.apache.tapestry.spec.BaseLocatable;
 import org.apache.tapestry.spec.IComponentSpecification;
-import org.apache.tapestry.util.prop.OgnlUtils;
 import org.apache.tapestry.util.prop.PropertyFinder;
 import org.apache.tapestry.util.prop.PropertyInfo;
 
@@ -189,6 +189,8 @@
 
     private IMessages _strings;
 
+    private ExpressionEvaluator _evaluator;
+
     public void addAsset(String name, IAsset asset)
     {
         if (_assets == null)
@@ -470,9 +472,10 @@
 
 		if (info != null && info.isReadWrite() && info.getType().equals(IBinding.class))
 		{
-			IResourceResolver resolver = getPage().getEngine().getResourceResolver();
+            return (IBinding) getEvaluator().read(this, bindingPropertyName);
+            //IResourceResolver resolver = getPage().getEngine().getResourceResolver();
 
-			return (IBinding) OgnlUtils.get(bindingPropertyName, resolver, this);
+			//return (IBinding) OgnlUtils.get(bindingPropertyName, resolver, this);
 		}
 
 		if (_bindings == null)
@@ -639,8 +642,9 @@
 
 		if (info != null && info.isReadWrite() && info.getType().equals(IBinding.class))
 		{
-			IResourceResolver resolver = getPage().getEngine().getResourceResolver();
-			OgnlUtils.set(bindingPropertyName, resolver, this, binding);
+            getEvaluator().write(this, bindingPropertyName, binding);
+            // IResourceResolver resolver = getPage().getEngine().getResourceResolver();
+			// OgnlUtils.set(bindingPropertyName, resolver, this, binding);
 			return;
 		}
 
@@ -1111,6 +1115,16 @@
     {
     }
 
+    ExpressionEvaluator getEvaluator()
+    {
+        if (_evaluator == null)
+        {
+            _evaluator = _page.getEngine().getExpressionEvaluator();
+        }
+
+        return _evaluator;
+    }
+
     /**
      *  Sets a property of a component.
      *  @see IComponent 
@@ -1118,8 +1132,10 @@
      */
     public void setProperty(String propertyName, Object value)
     {
-        IResourceResolver resolver = getResourceResolver();
-        OgnlUtils.set(propertyName, resolver, this, value);
+        getEvaluator().write(this, propertyName, value);
+
+        // IResourceResolver resolver = getResourceResolver();
+        // OgnlUtils.set(propertyName, resolver, this, value);
     }
     /**
      *  Gets a property of a component.
@@ -1128,7 +1144,9 @@
      */
     public Object getProperty(String propertyName)
     {
-        IResourceResolver resolver = getResourceResolver();
-        return OgnlUtils.get(propertyName, resolver, this);
+        return getEvaluator().read(this, propertyName);
+        
+        //IResourceResolver resolver = getResourceResolver();
+        //return OgnlUtils.get(propertyName, resolver, this);
     }
 }
\ No newline at end of file
diff --git a/tapestry-framework/src/org/apache/tapestry/BaseComponentTemplateLoader.java b/tapestry-framework/src/org/apache/tapestry/BaseComponentTemplateLoader.java
index 8150771..12628d8 100644
--- a/tapestry-framework/src/org/apache/tapestry/BaseComponentTemplateLoader.java
+++ b/tapestry-framework/src/org/apache/tapestry/BaseComponentTemplateLoader.java
@@ -14,32 +14,25 @@
 
 package org.apache.tapestry;
 
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Set;
-
 import org.apache.commons.lang.builder.ToStringBuilder;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.apache.tapestry.binding.ExpressionBinding;
 import org.apache.tapestry.binding.StaticBinding;
 import org.apache.tapestry.binding.StringBinding;
+import org.apache.tapestry.engine.ExpressionEvaluator;
 import org.apache.tapestry.engine.IPageLoader;
 import org.apache.tapestry.engine.IPageSource;
 import org.apache.tapestry.engine.ITemplateSource;
-import org.apache.tapestry.parse.AttributeType;
-import org.apache.tapestry.parse.CloseToken;
-import org.apache.tapestry.parse.ComponentTemplate;
-import org.apache.tapestry.parse.LocalizationToken;
-import org.apache.tapestry.parse.OpenToken;
-import org.apache.tapestry.parse.TemplateAttribute;
-import org.apache.tapestry.parse.TemplateToken;
-import org.apache.tapestry.parse.TextToken;
-import org.apache.tapestry.parse.TokenType;
+import org.apache.tapestry.parse.*;
 import org.apache.tapestry.spec.IComponentSpecification;
 import org.apache.tapestry.spec.IContainedComponent;
 
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
 /**
  *  Utility class instantiated by {@link org.apache.tapestry.BaseComponent} to
  *  process the component's {@link org.apache.tapestry.parse.ComponentTemplate template},
@@ -65,6 +58,7 @@
     private IComponent[] _stack;
     private int _stackx = 0;
     private IComponent _activeComponent = null;
+    private ExpressionEvaluator _evaluator;
     private Set _seenIds = new HashSet();
 
     /**
diff --git a/tapestry-framework/src/org/apache/tapestry/IEngine.java b/tapestry-framework/src/org/apache/tapestry/IEngine.java
index c340012..ff6938e 100644
--- a/tapestry-framework/src/org/apache/tapestry/IEngine.java
+++ b/tapestry-framework/src/org/apache/tapestry/IEngine.java
@@ -14,26 +14,17 @@
 
 package org.apache.tapestry;
 
-import java.io.IOException;
-import java.util.Locale;
-
-import javax.servlet.ServletException;
-
 import org.apache.tapestry.asset.ResourceChecksumSource;
-import org.apache.tapestry.engine.IComponentClassEnhancer;
-import org.apache.tapestry.engine.IComponentMessagesSource;
-import org.apache.tapestry.engine.IEngineService;
-import org.apache.tapestry.engine.IPageRecorder;
-import org.apache.tapestry.engine.IPageSource;
-import org.apache.tapestry.engine.IPropertySource;
-import org.apache.tapestry.engine.IScriptSource;
-import org.apache.tapestry.engine.ISpecificationSource;
-import org.apache.tapestry.engine.ITemplateSource;
+import org.apache.tapestry.engine.*;
 import org.apache.tapestry.request.RequestContext;
 import org.apache.tapestry.spec.IApplicationSpecification;
 import org.apache.tapestry.util.io.DataSqueezer;
 import org.apache.tapestry.util.pool.Pool;
 
+import javax.servlet.ServletException;
+import java.io.IOException;
+import java.util.Locale;
+
 /**
  * Defines the core, session-persistant object used to run a Tapestry
  * application for a single client (each client will have its own instance of the engine).
@@ -230,6 +221,13 @@
     public IResourceResolver getResourceResolver();
 
     /**
+     * Gets the OGNL expression evaluator.
+     *
+     * @return The expression evaluator.
+     */
+    public ExpressionEvaluator getExpressionEvaluator();
+
+    /**
      *  Returns the visit object, an object that represents the client's visit
      *  to the application.  This is where most server-side state is stored (with
      *  the exception of persistent page properties).
diff --git a/tapestry-framework/src/org/apache/tapestry/TapestryStrings.properties b/tapestry-framework/src/org/apache/tapestry/TapestryStrings.properties
index cac3ec4..329c453 100644
--- a/tapestry-framework/src/org/apache/tapestry/TapestryStrings.properties
+++ b/tapestry-framework/src/org/apache/tapestry/TapestryStrings.properties
@@ -42,6 +42,13 @@
 must-be-contained-by-body={0} components must be contained by a Body component.
 illegal-encoding=The encoding ''{0}'' is not recognized.
 
+unable-to-parse-expression=Unable to parse OGNL expression ''{0}'': {1}
+unable-to-read-expression=Unable to read OGNL expression ''{0}'' of {1}: {2}
+unable-to-write-expression=Unable to update OGNL expression ''{0}'' of {1} to {2}: {3}
+is-constant-expression-error=Error evaluating OGNL expression ''{0}'': {1}
+
+unable-to-lookup=Unable to lookup {0}: {1}
+
 # org.apache.tapestry
 
 AbstractComponent.attempt-to-change-container=Attempt to change existing container.
diff --git a/tapestry-framework/src/org/apache/tapestry/binding/ExpressionBinding.java b/tapestry-framework/src/org/apache/tapestry/binding/ExpressionBinding.java
index 15419e5..34db1a4 100644
--- a/tapestry-framework/src/org/apache/tapestry/binding/ExpressionBinding.java
+++ b/tapestry-framework/src/org/apache/tapestry/binding/ExpressionBinding.java
@@ -14,22 +14,19 @@
 
 package org.apache.tapestry.binding;
 
-import java.util.Map;
-
+import ognl.Node;
 import ognl.Ognl;
 import ognl.OgnlException;
 import ognl.TypeConverter;
-
-import org.apache.tapestry.BindingException;
-import org.apache.tapestry.IComponent;
-import org.apache.tapestry.ILocation;
-import org.apache.tapestry.IResourceResolver;
-import org.apache.tapestry.Tapestry;
+import ognl.enhance.ExpressionAccessor;
+import org.apache.tapestry.*;
+import org.apache.tapestry.engine.ExpressionEvaluator;
 import org.apache.tapestry.spec.BeanLifecycle;
-import org.apache.tapestry.spec.IBeanSpecification;
 import org.apache.tapestry.spec.IApplicationSpecification;
+import org.apache.tapestry.spec.IBeanSpecification;
 import org.apache.tapestry.util.StringSplitter;
-import org.apache.tapestry.util.prop.OgnlUtils;
+
+import java.util.Map;
 
 /**
  *  Implements a dynamic binding, based on getting and fetching
@@ -37,7 +34,7 @@
  *  upon the <a href="http://www.ognl.org">OGNL</a> library.
  *
  *  <p><b>Optimization of the Expression</b>
- * 
+ *
  *  <p>There's a lot of room for optimization here because we can
  *  count on some portions of the expression to be
  *  effectively static.  Note that we type the root object as
@@ -56,13 +53,13 @@
  *  <p>This means that once an ExpressionBinding has been triggered, 
  *  the {@link #toString()} method may return different values for the root
  *  component and the expression than was originally set.
- * 
+ *
  *  <p><b>Identifying Invariants</b>
- * 
+ *
  *  <p>Most expressions are fully dynamic; they must be
  *  resolved each time they are accessed.  This can be somewhat inefficient.
  *  Tapestry can identify certain paths as invariant:
- * 
+ *
  *  <ul>
  *  <li>A component within the page hierarchy 
  *  <li>An {@link org.apache.tapestry.IAsset} from then assets map (property <code>assets</code>)
@@ -72,7 +69,7 @@
  *  lifecycle (property <code>beans</code>)
  *  <li>A binding (property <code>bindings</code>)
  *  </ul>
- * 
+ *
  *  <p>
  *  These optimizations have some inherent dangers; they assume that
  *  the components have not overidden the specified properties;
@@ -80,11 +77,11 @@
  *  component does inherit from {@link org.apache.tapestry.AbstractComponent}.
  *  If this becomes a problem in the future, it may be necessary to
  *  have the component itself involved in these determinations.
- *  
+ *
  *  @author Howard Lewis Ship
  *  @version $Id$
  *  @since 2.2
- * 
+ *
  **/
 
 public class ExpressionBinding extends AbstractBinding
@@ -106,7 +103,7 @@
     /**
      *  If true, then the binding is invariant, and cachedValue
      *  is the ultimate value.
-     * 
+     *
      **/
 
     private boolean _invariant = false;
@@ -114,25 +111,25 @@
     /**
      *  Stores the cached value for the binding, if invariant
      *  is true.
-     * 
+     *
      **/
 
     private Object _cachedValue;
 
     /**
      *   Parsed OGNL expression.
-     * 
+     *
      **/
 
-    private Object _parsedExpression;
+    private Node _parsedExpression;
 
     /**
      *  Flag set once the binding has initialized.
      *  _cachedValue, _invariant and _final value
      *  for _expression
      *  are not valid until after initialization.
-     * 
-     * 
+     *
+     *
      **/
 
     private boolean _initialized;
@@ -142,28 +139,47 @@
     /**
      *  The OGNL context for this binding.  It is retained
      *  for the lifespan of the binding once created.
-     * 
+     *
      **/
 
     private Map _context;
 
     /**
+     * Central OGNL expression manager.
+     */
+    private ExpressionEvaluator _evaluator;
+
+    /**
+     * Compiled OGNL expression.
+     */
+
+    private ExpressionAccessor _accessor;
+
+    /**
+     * Used to detect previous failed attempts at writing values when compiling expressions so
+     * that as many expressions as possible can be fully compiled into their java byte form when
+     * all objects in the expression are available.
+     */
+    private boolean _writeFailed;
+
+    /**
      *  Creates a {@link ExpressionBinding} from the root object
      *  and an OGNL expression.
-     * 
+     *
      **/
 
     public ExpressionBinding(
-        IResourceResolver resolver,
-        IComponent root,
-        String expression,
-        ILocation location)
+            IResourceResolver resolver,
+            IComponent root,
+            String expression,
+            ILocation location)
     {
         super(location);
 
         _resolver = resolver;
         _root = root;
         _expression = expression;
+        _evaluator = root.getPage().getEngine().getExpressionEvaluator();
     }
 
     public String getExpression()
@@ -198,17 +214,26 @@
     {
         try
         {
-            return Ognl.getValue(_parsedExpression, getOgnlContext(), _root);
+            if (false && _evaluator.isCompileEnabled() && _accessor == null && !_writeFailed)
+            {
+                _evaluator.compileExpression(_root, _parsedExpression, _expression);
+                _accessor = _parsedExpression.getAccessor();
+            }
+
+            if (_accessor != null)
+                return _evaluator.read(_root, _accessor);
+
+            return _evaluator.readCompiled(_root, _parsedExpression);
         }
-        catch (OgnlException t)
+        catch (Throwable t)
         {
             throw new BindingException(
-                Tapestry.format(
-                    "ExpressionBinding.unable-to-resolve-expression",
-                    _expression,
-                    _root),
-                this,
-                t);
+                    Tapestry.format(
+                            "ExpressionBinding.unable-to-resolve-expression",
+                            _expression,
+                            _root),
+                    this,
+                    t);
         }
     }
 
@@ -219,7 +244,7 @@
      *  An optional type converter will be added to the OGNL context
      *  if it is specified as an application extension with the name
      *  {@link Tapestry#OGNL_TYPE_CONVERTER}.
-     * 
+     *
      **/
 
     private Map getOgnlContext()
@@ -235,8 +260,7 @@
 
                 if (appSpec != null && appSpec.checkExtension(Tapestry.OGNL_TYPE_CONVERTER))
                 {
-                    TypeConverter typeConverter =
-                        (TypeConverter) appSpec.getExtension(
+                    TypeConverter typeConverter = (TypeConverter) appSpec.getExtension(
                             Tapestry.OGNL_TYPE_CONVERTER,
                             TypeConverter.class);
 
@@ -251,8 +275,8 @@
     /**
      *  Returns true if the binding is expected to always 
      *  return the same value.
-     * 
-     * 
+     *
+     *
      **/
 
     public boolean isInvariant()
@@ -297,7 +321,7 @@
 
         try
         {
-            _parsedExpression = OgnlUtils.getParsedExpression(_expression);
+            _parsedExpression = _evaluator.parse(_root, _expression);
         }
         catch (Exception ex)
         {
@@ -317,6 +341,7 @@
             throw new BindingException(ex.getMessage(), this, ex);
         }
 
+
         // Split the expression into individual property names.
         // We then optimize what we can from the expression.  This will
         // shorten the expression and, in some cases, eliminate
@@ -327,7 +352,7 @@
         int count = optimizeRootObject(split);
 
         // We'ver removed some or all of the initial elements of split
-        // but have to account for anthing left over.
+        // but have to account for anything left over.
 
         if (count == split.length)
         {
@@ -344,7 +369,7 @@
         }
 
         _expression = reassemble(count, split);
-        _parsedExpression = OgnlUtils.getParsedExpression(_expression);
+        _parsedExpression = _evaluator.parse(_root, _expression);
 
         checkForInvariant(count, split);
     }
@@ -352,10 +377,10 @@
     /**
      *  Looks for common prefixes on the expression (provided pre-split) that
      *  are recognized as references to other components.
-     * 
+     *
      *  @return the number of leading elements of the split expression that
      *  have been removed.
-     * 
+     *
      **/
 
     private int optimizeRootObject(String[] split)
@@ -398,26 +423,14 @@
 
     private boolean checkForConstant()
     {
-        try
-        {
-            if (Ognl.isConstant(_parsedExpression, getOgnlContext()))
-            {
-                _invariant = true;
 
-                _cachedValue = resolveProperty();
-
-                return true;
-            }
-        }
-        catch (OgnlException ex)
+        if (_evaluator.isConstant(_root, _expression))
         {
-            throw new BindingException(
-                Tapestry.format(
-                    "ExpressionBinding.unable-to-resolve-expression",
-                    _expression,
-                    _root),
-                this,
-                ex);
+            _invariant = true;
+
+            _cachedValue = resolveProperty();
+
+            return true;
         }
 
         return false;
@@ -426,7 +439,7 @@
     /**
      *  Reassembles the remainder of the split property path
      *  from the start point.
-     * 
+     *
      **/
 
     private String reassemble(int start, String[] split)
@@ -454,7 +467,7 @@
 
     /**
      *  Checks to see if the binding can be converted to an invariant.
-     * 
+     *
      **/
 
     private void checkForInvariant(int start, String[] split)
@@ -473,12 +486,12 @@
         catch (OgnlException ex)
         {
             throw new BindingException(
-                Tapestry.format(
-                    "ExpressionBinding.unable-to-resolve-expression",
-                    _expression,
-                    _root),
-                this,
-                ex);
+                    Tapestry.format(
+                            "ExpressionBinding.unable-to-resolve-expression",
+                            _expression,
+                            _root),
+                    this,
+                    ex);
         }
 
         String first = split[start];
@@ -550,18 +563,47 @@
 
         try
         {
-            Ognl.setValue(_parsedExpression, getOgnlContext(), _root, value);
+            if (_accessor != null)
+            {
+                _evaluator.write(_root, _accessor, value);
+            } else if (false && _evaluator.isCompileEnabled() && _accessor == null)
+            {
+                //_evaluator.compileExpression(_root, _parsedExpression, _expression);
+                //_accessor = _parsedExpression.getAccessor();
+
+                if (!_writeFailed)
+                {
+                    // re-parse expression as compilation may be possible now that it potentially has a value
+                    try
+                    {
+                        _evaluator.compileExpression(_root, _parsedExpression, _expression);
+                        _accessor = _parsedExpression.getAccessor();
+                    } catch (Throwable t)
+                    {
+                        // ignore re-read failures as they aren't supposed to be happening now anyways
+                        // and a more user friendly version will be available if someone actually calls
+                        // getObject
+
+                        // if writing fails then we're probably screwed...so don't do it again
+                        if (value != null)
+                            _writeFailed = true;
+                    }
+                }
+            } else
+            {
+                _evaluator.writeCompiled(_root, _parsedExpression, value);
+            }
         }
-        catch (OgnlException ex)
+        catch (Throwable ex)
         {
             throw new BindingException(
-                Tapestry.format(
-                    "ExpressionBinding.unable-to-update-expression",
-                    _expression,
-                    _root,
-                    value),
-                this,
-                ex);
+                    Tapestry.format(
+                            "ExpressionBinding.unable-to-update-expression",
+                            _expression,
+                            _root,
+                            value),
+                    this,
+                    ex);
         }
     }
 
diff --git a/tapestry-framework/src/org/apache/tapestry/engine/AbstractEngine.java b/tapestry-framework/src/org/apache/tapestry/engine/AbstractEngine.java
index c630a4c..96e0c68 100644
--- a/tapestry-framework/src/org/apache/tapestry/engine/AbstractEngine.java
+++ b/tapestry-framework/src/org/apache/tapestry/engine/AbstractEngine.java
@@ -23,6 +23,8 @@
 import org.apache.tapestry.asset.ResourceChecksumSource;
 import org.apache.tapestry.asset.ResourceChecksumSourceImpl;
 import org.apache.tapestry.enhance.DefaultComponentClassEnhancer;
+import org.apache.tapestry.enhance.IEnhancedClassFactory;
+import org.apache.tapestry.enhance.javassist.EnhancedClassFactory;
 import org.apache.tapestry.listener.ListenerMap;
 import org.apache.tapestry.pageload.PageSource;
 import org.apache.tapestry.request.RequestContext;
@@ -390,6 +392,10 @@
 
     private transient IComponentClassEnhancer _enhancer;
 
+    protected static final String CLASS_FACTORY_NAME = "org.apache.tapestry.enhance.javassist.EnhancedClassFactory";
+
+    private transient IEnhancedClassFactory _classFactory;
+
     /**
      *  Set to true when there is a (potential)
      *  change to the internal state of the engine, set
@@ -417,6 +423,18 @@
     private transient ResourceChecksumSource _resourceChecksumSource;
 
     /**
+     *  The name of the context attribute for the {@link ExpressionEvaluator} instance.
+     *  The application's name is appended.
+     **/
+    protected static final String EXPRESSION_EVALUATOR_NAME = "org.apache.tapestry.engine.ExpressionEvaluator";
+
+    private transient ExpressionEvaluator _expressionEvaluator;
+
+    private transient ExpressionCache _expressionCache;
+
+    private transient String _outputEncoding;
+
+    /**
      *  Sets the Exception page's exception property, then renders the Exception page.
      *
      *  <p>If the render throws an exception, then copious output is sent to
@@ -1109,6 +1127,9 @@
         _stringsSource.reset();
         _enhancer.reset();
         _resourceChecksumSource.reset();
+        _expressionCache.reset();
+        _expressionEvaluator.reset();
+        _outputEncoding = null;
     }
 
     /**
@@ -1249,6 +1270,33 @@
             }
         }
 
+        if (_classFactory == null)
+        {
+            String name = CLASS_FACTORY_NAME + ":" + servletName;
+
+            _classFactory = (IEnhancedClassFactory) servletContext.getAttribute(name);
+
+            if (_classFactory == null)
+            {
+                try
+                {
+                    context.getServlet().getLock().lock();
+
+                    _classFactory = (IEnhancedClassFactory) servletContext.getAttribute(name);
+
+                    if (_classFactory == null)
+                    {
+                        _classFactory = createClassFactory();
+
+                        servletContext.setAttribute(name, _classFactory);
+                    }
+                } finally
+                {
+                    context.getServlet().getLock().unlock();
+                }
+            }
+        }
+
         if (_enhancer == null)
         {
             String name = ENHANCER_NAME + ":" + servletName;
@@ -1546,6 +1594,33 @@
             }
         }
 
+        if (_expressionEvaluator == null)
+        {
+            String name = EXPRESSION_EVALUATOR_NAME + ":" + servletName;
+
+            _expressionEvaluator = (ExpressionEvaluator) servletContext.getAttribute(name);
+
+            if (_expressionEvaluator == null)
+            {
+                try
+                {
+                    context.getServlet().getLock().lock();
+
+                    _expressionEvaluator = (ExpressionEvaluator) servletContext.getAttribute(name);
+
+                    if (_expressionEvaluator == null)
+                    {
+                        _expressionEvaluator = createExpressionEvaluator();
+
+                        servletContext.setAttribute(name, _expressionEvaluator);
+                    }
+                } finally
+                {
+                    context.getServlet().getLock().unlock();
+                }
+            }
+        }
+
         String encoding = request.getCharacterEncoding();
         if (encoding == null)
         {
@@ -1662,6 +1737,18 @@
     {
         return new ResourceChecksumSourceImpl("MD5", new Hex());
     }
+    
+    protected ExpressionEvaluator createExpressionEvaluator()
+    {
+        _expressionCache = (ExpressionCacheImpl) createExpressionCache();
+
+        return new ExpressionEvaluatorImpl(_resolver, _classFactory, _expressionCache, _specification);
+    }
+
+    protected ExpressionCache createExpressionCache()
+    {
+        return new ExpressionCacheImpl();
+    }
 
     /**
      *  Returns an object which can find resources and classes.
@@ -1673,6 +1760,11 @@
         return _resolver;
     }
 
+    public ExpressionEvaluator getExpressionEvaluator()
+    {
+        return _expressionEvaluator;
+    }
+
     /**
      *  Generates a description of the instance.
      *  Invokes {@link #extendDescription(ToStringBuilder)}
@@ -2378,10 +2470,15 @@
                 "true".equals(
                         _propertySource.getPropertyValue(
                                 "org.apache.tapestry.enhance.disable-abstract-method-validation"));
-
-        return new DefaultComponentClassEnhancer(_resolver, disableValidation);
+        
+        return new DefaultComponentClassEnhancer(_resolver, disableValidation, _classFactory);
     }
 
+    protected IEnhancedClassFactory createClassFactory()
+    {
+        return new EnhancedClassFactory(_resolver);
+    }
+    
     /** @since 3.0 **/
 
     public IComponentClassEnhancer getComponentClassEnhancer()
@@ -2478,13 +2575,18 @@
      **/
     public String getOutputEncoding()
     {
+        if (_outputEncoding != null)
+            return _outputEncoding;
+        
         IPropertySource source = getPropertySource();
 
         String encoding = source.getPropertyValue(OUTPUT_ENCODING_PROPERTY_NAME);
         if (encoding == null)
             encoding = getDefaultOutputEncoding();
 
-        return encoding;
+        _outputEncoding = encoding;
+
+        return _outputEncoding;
     }
 
 }
diff --git a/tapestry-framework/src/org/apache/tapestry/engine/ExpressionCache.java b/tapestry-framework/src/org/apache/tapestry/engine/ExpressionCache.java
new file mode 100644
index 0000000..108b617
--- /dev/null
+++ b/tapestry-framework/src/org/apache/tapestry/engine/ExpressionCache.java
@@ -0,0 +1,42 @@
+package org.apache.tapestry.engine;
+
+import ognl.Node;
+
+/**
+ * Cache of compiled OGNL expressions.
+ *
+ * @author Howard M. Lewis Ship
+ * @since 4.0
+ */
+public interface ExpressionCache
+{
+    /**
+     * Returns the compiled ognl expression for the given target object class / expression
+     * combination.
+     *
+     * @param target
+     *          The object this expression is to be used for.
+     * @param expression
+     *          The expression.
+     * @return
+     *      The compiled (or new if neccessary) ognl statement.
+     */
+    Object get(Object target, String expression);
+
+    /**
+     * Stores a parsed ognl expression for the given object type.
+     *
+     * @param target
+     *          The target object expression compiled for.
+     * @param expression
+     *          The ognl string expression node represents.
+     * @param node
+     *          The parsed OGNL expression to cache.
+     */
+    void cache(Object target, String expression, Node node);
+
+    /**
+     * Invoked by {@link AbstractEngine} to clear all cache data.
+     */
+    void reset();
+}
\ No newline at end of file
diff --git a/tapestry-framework/src/org/apache/tapestry/engine/ExpressionCacheImpl.java b/tapestry-framework/src/org/apache/tapestry/engine/ExpressionCacheImpl.java
new file mode 100644
index 0000000..c36c189
--- /dev/null
+++ b/tapestry-framework/src/org/apache/tapestry/engine/ExpressionCacheImpl.java
@@ -0,0 +1,112 @@
+package org.apache.tapestry.engine;
+
+import edu.emory.mathcs.backport.java.util.concurrent.locks.ReentrantLock;
+import ognl.ClassCacheInspector;
+import ognl.Node;
+import ognl.OgnlRuntime;
+import org.apache.tapestry.AbstractComponent;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author Howard M. Lewis Ship
+ * @since 4.0
+ */
+public class ExpressionCacheImpl implements ExpressionCache, ClassCacheInspector {
+
+    private final ReentrantLock _lock = new ReentrantLock();
+
+    private final Map _compiledExpressionCache = new HashMap();
+
+    private final Map _expressionCache = new HashMap();
+
+    private final boolean _cachingDisabled = Boolean.getBoolean("org.apache.tapestry.disable-caching");
+
+    public ExpressionCacheImpl()
+    {
+        initializeService();
+    }
+
+    public void initializeService()
+    {
+        if (_cachingDisabled)
+        {
+            OgnlRuntime.setClassCacheInspector(this);
+        }
+    }
+
+    public void reset()
+    {
+        try
+        {
+            _lock.lock();
+
+            _compiledExpressionCache.clear();
+            _expressionCache.clear();
+        } finally
+        {
+            _lock.unlock();
+        }
+    }
+
+    public boolean shouldCache(Class type)
+    {
+        if (!_cachingDisabled || type == null
+            || AbstractComponent.class.isAssignableFrom(type))
+            return false;
+
+        return true;
+    }
+
+    public Object get(Object target, String expression)
+    {
+        try
+        {
+            _lock.lock();
+
+            Map cached = (Map)_compiledExpressionCache.get(target.getClass());
+            if (cached == null)
+            {
+                return _expressionCache.get(expression);
+            } else
+            {
+                return cached.get(expression);
+            }
+
+        } finally
+        {
+            _lock.unlock();
+        }
+    }
+
+    public void cache(Object target, String expression, Node node)
+    {
+        try
+        {
+            _lock.lock();
+
+            if (node.getAccessor() != null)
+            {
+                Map cached = (Map)_compiledExpressionCache.get(target.getClass());
+
+                if (cached == null)
+                {
+                    cached = new HashMap();
+                    _compiledExpressionCache.put(target.getClass(), cached);
+                }
+
+                cached.put(expression, node);
+
+                _expressionCache.remove(target.getClass());
+            } else
+            {
+                _expressionCache.put(expression, node);
+            }
+
+        } finally
+        {
+            _lock.unlock();
+        }
+    }
+}
diff --git a/tapestry-framework/src/org/apache/tapestry/engine/ExpressionEvaluator.java b/tapestry-framework/src/org/apache/tapestry/engine/ExpressionEvaluator.java
new file mode 100644
index 0000000..ee6a952
--- /dev/null
+++ b/tapestry-framework/src/org/apache/tapestry/engine/ExpressionEvaluator.java
@@ -0,0 +1,148 @@
+package org.apache.tapestry.engine;
+
+import ognl.Node;
+import ognl.enhance.ExpressionAccessor;
+
+/**
+ * Wrapper around the OGNL library.
+ *
+ * @author Howard M. Lewis Ship
+ * @since 4.0
+ */
+public interface ExpressionEvaluator
+{
+
+    /**
+     * Parses the given ognl expression.
+     *
+     * @param target
+     *          The object the expression is to be executed against.
+     * @param expression
+     *          The expression.
+     *
+     * @return The parsed OGNL AST representation of the expression.
+     */
+    Node parse(Object target, String expression);
+
+    /**
+     * Reads a property of the target, defined by the expression.
+     *
+     * @param target
+     *          The object to evaluate the expression against.
+     * @param expression
+     *          The expression.
+     *
+     * @return The value returned from the expression statement.
+     *
+     * @throws org.apache.tapestry.ApplicationRuntimeException
+     *             if the expression can not be parsed, or if some other error
+     *             occurs during evaluation of the expression.
+     */
+    Object read(Object target, String expression);
+
+    /**
+     * Reads a property of the target, defined by the (previously compiled)
+     * expression.
+     *
+     * @param target
+     *          The object to evaluate the expression against.
+     * @param expression
+     *          The expression.
+     *
+     * @return The value returned from the expression statement.
+     *
+     * @throws org.apache.tapestry.ApplicationRuntimeException
+     *             if some other error occurs during evaluation of the
+     *             expression.
+     */
+    Object readCompiled(Object target, Object expression);
+
+    /**
+     * Reads a property of the target, defined by the (previously compiled)
+     * expression.
+     *
+     * @param target
+     *          The object to resolve the expression against.
+     * @param expression
+     *          The compiled expression.
+     * @return
+     *          The result of reading on the expression.
+     */
+    Object read(Object target, ExpressionAccessor expression);
+
+    /**
+     * Updates a property of the target, defined by the expression.
+     *
+     * @param target
+     *          The object to evaluate the expression against.
+     * @param expression
+     *          The expression.
+     * @param value
+     *          The value to set on the target object.
+     *
+     * @throws org.apache.tapestry.ApplicationRuntimeException
+     *             if the expression can not be parsed, or if some other error
+     *             occurs during evaluation of the expression.
+     */
+    void write(Object target, String expression, Object value);
+
+    /**
+     * Updates a property of the target, defined by the (previously compiled)
+     * expression.
+     *
+     * @param target
+     *          The object to evaluate the expression against.
+     * @param expression
+     *          The expression.
+     * @param value
+     *          The value to set on the target object.
+     * 
+     * @throws org.apache.tapestry.ApplicationRuntimeException
+     *             if some other error occurs during evaluation of the
+     *             expression.
+     */
+    void writeCompiled(Object target, Object expression, Object value);
+
+    /**
+     * Updates a property of the target, defined by the (previously compiled)
+     * expression.
+     *
+     * @param target
+     *          The target object to set a value on.
+     * @param expression
+     *          The pre-compiled expression.
+     * @param value
+     *          The value to set.
+     */
+    void write(Object target, ExpressionAccessor expression, Object value);
+
+    /**
+     * Returns true if the expression evaluates to a constant or other literal
+     * value.
+     *
+     * @param target
+     *          The object to evaluate the expression against.
+     * @param expression
+     *          The expression.
+     *
+     * @return True if expression represents a constant statement, false otherwise.
+     * @throws org.apache.tapestry.ApplicationRuntimeException
+     *             if the expression is not valid
+     */
+    boolean isConstant(Object target, String expression);
+
+    /**
+     * Returns true only if both system properties of <code>org.apache.tapestry.disable-caching</code>
+     * and <code>org.apache.tapestry.disable-expression-compile</code> are false.
+     *
+     * @return True if ognl expressions are eligable for JIT compilation.
+     */
+    boolean isCompileEnabled();
+
+    void compileExpression(Object target, Node node, String expression);
+
+    /**
+     * Used to reset any internal state.
+     */
+    void reset();
+}
diff --git a/tapestry-framework/src/org/apache/tapestry/engine/ExpressionEvaluatorImpl.java b/tapestry-framework/src/org/apache/tapestry/engine/ExpressionEvaluatorImpl.java
new file mode 100644
index 0000000..8e56bd9
--- /dev/null
+++ b/tapestry-framework/src/org/apache/tapestry/engine/ExpressionEvaluatorImpl.java
@@ -0,0 +1,239 @@
+package org.apache.tapestry.engine;
+
+import ognl.*;
+import ognl.enhance.ExpressionAccessor;
+import org.apache.commons.pool.impl.GenericObjectPool;
+import org.apache.tapestry.ApplicationRuntimeException;
+import org.apache.tapestry.Tapestry;
+import org.apache.tapestry.enhance.IEnhancedClassFactory;
+import org.apache.tapestry.enhance.javassist.OGNLExpressionCompiler;
+import org.apache.tapestry.spec.IApplicationSpecification;
+
+import java.beans.Introspector;
+import java.util.Map;
+
+/**
+ * @since 4.0
+ */
+public class ExpressionEvaluatorImpl implements ExpressionEvaluator {
+
+    private static final long POOL_MIN_IDLE_TIME = 1000 * 60 * 50;
+
+    private static final long POOL_SLEEP_TIME = 1000 * 60 * 4;
+
+    // Uses Thread's context class loader
+
+    private final ClassResolver _ognlResolver;
+
+    private ExpressionCache _expressionCache;
+
+    private IApplicationSpecification _applicationSpecification;
+
+    private TypeConverter _typeConverter;
+
+    // Context, with a root of null, used when evaluating an expression
+    // to see if it is a constant.
+
+    private Map _defaultContext;
+
+    private IEnhancedClassFactory _classFactory;
+
+    private GenericObjectPool _contextPool;
+
+    private final boolean _cachingDisabled = Boolean.getBoolean("org.apache.tapestry.disable-caching");
+
+    private final boolean _compileDisabled = Boolean.getBoolean("org.apache.tapestry.disable-expression-compile");
+
+    public ExpressionEvaluatorImpl(ClassResolver resolver, IEnhancedClassFactory classFactory,
+                                   ExpressionCache expressionCache, IApplicationSpecification spec)
+    {
+        _ognlResolver = resolver;
+        _classFactory = classFactory;
+        _expressionCache = expressionCache;
+        _applicationSpecification = spec;
+
+        initializeService();
+    }
+
+    void initializeService()
+    {
+        if (_applicationSpecification.checkExtension(Tapestry.OGNL_TYPE_CONVERTER))
+            _typeConverter = (TypeConverter) _applicationSpecification.getExtension(Tapestry.OGNL_TYPE_CONVERTER, TypeConverter.class);
+
+        _defaultContext = Ognl.createDefaultContext(null, _ognlResolver, _typeConverter);
+
+        OgnlRuntime.setCompiler(new OGNLExpressionCompiler(_classFactory));
+
+        _contextPool = new GenericObjectPool(new PoolableOgnlContextFactory(_ognlResolver, _typeConverter));
+
+        _contextPool.setMaxActive(-1);
+        _contextPool.setMaxIdle(-1);
+        _contextPool.setMinEvictableIdleTimeMillis(POOL_MIN_IDLE_TIME);
+        _contextPool.setTimeBetweenEvictionRunsMillis(POOL_SLEEP_TIME);
+    }
+
+    public Node parse(Object target, String expression)
+    {
+        Node node = (Node)_expressionCache.get(target, expression);
+
+        if (node == null)
+        {
+            try
+            {
+                node = (Node)Ognl.parseExpression(expression);
+                _expressionCache.cache(target, expression, node);
+            } catch (OgnlException ex)
+            {
+                throw new ApplicationRuntimeException(Tapestry.format("unable-to-read-expression", expression, target, ex), target, null, ex);
+            }
+        }
+
+        return node;
+    }
+
+    public Object read(Object target, String expression)
+    {
+        Node node = parse(target, expression);
+
+        if (node.getAccessor() != null)
+            return read(target, node.getAccessor());
+
+        return readCompiled(target, node);
+    }
+
+    public Object readCompiled(Object target, Object expression)
+    {
+        OgnlContext context = null;
+        try
+        {
+            context = (OgnlContext)_contextPool.borrowObject();
+            context.setRoot(target);
+            
+            return Ognl.getValue(expression, context, target);
+        }
+        catch (Exception ex)
+        {
+            throw new ApplicationRuntimeException(Tapestry.format("unable-to-read-expression", expression, target, ex), target, null, ex);
+        } finally {
+            try { if (context != null) _contextPool.returnObject(context); } catch (Exception e) {}
+        }
+    }
+
+    public Object read(Object target, ExpressionAccessor expression)
+    {
+        OgnlContext context = null;
+        try
+        {
+            context = (OgnlContext)_contextPool.borrowObject();
+
+            return expression.get(context, target);
+        }
+        catch (Exception ex)
+        {
+            throw new ApplicationRuntimeException(Tapestry.format("unable-to-read-expression", expression, target, ex), target, null, ex);
+        } finally {
+            try { if (context != null) _contextPool.returnObject(context); } catch (Exception e) {}
+        }
+    }
+
+    public void write(Object target, String expression, Object value)
+    {
+        writeCompiled(target, parse(target, expression), value);
+    }
+
+    public void write(Object target, ExpressionAccessor expression, Object value)
+    {
+        OgnlContext context = null;
+        try
+        {
+            context = (OgnlContext)_contextPool.borrowObject();
+
+            // set up context
+            context.setRoot(target);
+
+            expression.set(context, target, value);
+        }
+        catch (Exception ex)
+        {
+            throw new ApplicationRuntimeException(Tapestry.format("unable-to-write-expression",
+                                                                  new Object[] {expression, target, value, ex}),
+                                                  target, null, ex);
+        } finally {
+            try { if (context != null) _contextPool.returnObject(context); } catch (Exception e) {}
+        }
+    }
+
+    public void writeCompiled(Object target, Object expression, Object value)
+    {
+        OgnlContext context = null;
+        try
+        {
+            context = (OgnlContext)_contextPool.borrowObject();
+
+            Ognl.setValue(expression, context, target, value);
+        }
+        catch (Exception ex)
+        {
+            throw new ApplicationRuntimeException(Tapestry.format("unable-to-write-expression",
+                                                                  new Object[] {expression, target, value, ex}),
+                                                  target, null, ex);
+        } finally {
+            try { if (context != null) _contextPool.returnObject(context); } catch (Exception e) {}
+        }
+    }
+
+    public boolean isConstant(Object target, String expression)
+    {
+        Node node = parse(target, expression);
+
+        try
+        {
+            return Ognl.isConstant(node, _defaultContext);
+        }
+        catch (Exception ex)
+        {
+            throw new ApplicationRuntimeException(Tapestry.format("is-constant-expression-error", expression, ex), ex);
+        }
+    }
+
+    public boolean isCompileEnabled()
+    {
+        return !_cachingDisabled && !_compileDisabled;
+    }
+
+    public void compileExpression(Object target, Node node, String expression)
+    {
+        OgnlContext context = null;
+        
+        try
+        {
+            context = (OgnlContext)_contextPool.borrowObject();
+
+            // set up context
+            context.setRoot(target);
+
+            OgnlRuntime.compileExpression(context, node, target);
+
+            _expressionCache.cache(target, expression, node);
+
+        } catch (Exception ex)
+        {
+            throw new ApplicationRuntimeException(Tapestry.format("unable-to-read-expression", expression, target, ex), target, null, ex);
+        } finally {
+            try { if (context != null) _contextPool.returnObject(context); } catch (Exception e) {}
+        }
+    }
+
+    public void reset()
+    {
+        try
+        {
+            _contextPool.clear();
+
+            OgnlRuntime.clearCache();
+            Introspector.flushCaches();
+        } catch (Exception et) {
+            // ignore
+        }
+    }
+}
\ No newline at end of file
diff --git a/tapestry-framework/src/org/apache/tapestry/engine/PoolableOgnlContextFactory.java b/tapestry-framework/src/org/apache/tapestry/engine/PoolableOgnlContextFactory.java
new file mode 100644
index 0000000..449b5ae
--- /dev/null
+++ b/tapestry-framework/src/org/apache/tapestry/engine/PoolableOgnlContextFactory.java
@@ -0,0 +1,40 @@
+package org.apache.tapestry.engine;
+
+import ognl.ClassResolver;
+import ognl.Ognl;
+import ognl.OgnlContext;
+import ognl.TypeConverter;
+import org.apache.commons.pool.BasePoolableObjectFactory;
+
+/**
+ * Used by {@link ExpressionEvaluatorImpl} to pool {@link ognl.OgnlContext} objects.
+ */
+public class PoolableOgnlContextFactory extends BasePoolableObjectFactory {
+
+    private final ClassResolver _resolver;
+
+    private TypeConverter _typeConverter;
+
+    public PoolableOgnlContextFactory(ClassResolver resolver, TypeConverter typeConverter)
+    {
+        _resolver = resolver;
+        _typeConverter = typeConverter;
+    }
+
+    public Object makeObject()
+        throws Exception
+    {
+        return Ognl.createDefaultContext(null, _resolver, _typeConverter);
+    }
+
+    public void activateObject(Object obj)
+    throws Exception
+    {
+        OgnlContext context = (OgnlContext)obj;
+
+        if (context.getRoot() != null || context.getValues().size() > 0)
+        {
+            context.clear();
+        }
+    }
+}
diff --git a/tapestry-framework/src/org/apache/tapestry/enhance/DefaultComponentClassEnhancer.java b/tapestry-framework/src/org/apache/tapestry/enhance/DefaultComponentClassEnhancer.java
index 4cb9fcc..9f9e594 100644
--- a/tapestry-framework/src/org/apache/tapestry/enhance/DefaultComponentClassEnhancer.java
+++ b/tapestry-framework/src/org/apache/tapestry/enhance/DefaultComponentClassEnhancer.java
@@ -21,7 +21,6 @@
 import org.apache.tapestry.IResourceResolver;
 import org.apache.tapestry.Tapestry;
 import org.apache.tapestry.engine.IComponentClassEnhancer;
-import org.apache.tapestry.enhance.javassist.EnhancedClassFactory;
 import org.apache.tapestry.spec.IComponentSpecification;
 
 import java.lang.reflect.Method;
@@ -58,19 +57,14 @@
      * @param disableValidation if true, then validation (of unimplemented abstract methods)
      * is skipped
      */
-    public DefaultComponentClassEnhancer(IResourceResolver resolver, boolean disableValidation)
+    public DefaultComponentClassEnhancer(IResourceResolver resolver, boolean disableValidation, IEnhancedClassFactory classFactory)
     {
         _cachedClasses = new ConcurrentHashMap();
         _resolver = resolver;
-        _factory = createEnhancedClassFactory();
+        _factory = classFactory;
         _disableValidation = disableValidation;
     }
 
-    protected IEnhancedClassFactory createEnhancedClassFactory()
-    {
-        return new EnhancedClassFactory(getResourceResolver());
-    }
-
     public synchronized void reset()
     {
         _cachedClasses.clear();
@@ -244,7 +238,7 @@
 
             boolean isAbstract = Modifier.isAbstract(m.getModifiers());
 
-            MethodSignature s = new MethodSignature(m);
+            MethodSignature s = new MethodSignatureImpl(m);
 
             if (isAbstract)
             {
diff --git a/tapestry-framework/src/org/apache/tapestry/enhance/IEnhancedClass.java b/tapestry-framework/src/org/apache/tapestry/enhance/IEnhancedClass.java
index d432990..7ac9566 100644
--- a/tapestry-framework/src/org/apache/tapestry/enhance/IEnhancedClass.java
+++ b/tapestry-framework/src/org/apache/tapestry/enhance/IEnhancedClass.java
@@ -51,4 +51,22 @@
      * Adds an arbitrary enhancer.
      */
     void addEnhancer(IEnhancer enhancer);
+
+    void addInterface(Class type);
+
+    void addMethod(int modifiers, MethodSignature signature, String body);
+
+    void addField(String name, Class type);
+    
+    void addConstructor(Class[] parameterTypes, Class[] exceptions, String body);
+
+    /**
+     * Returns true if the same method was previously added via
+     * {@link #addMethod(int, MethodSignature, String)}.
+     *
+     * @param signature
+     *          The signature to check existance of.
+     * @return True if the same method has been added already, false otherwise.
+     */
+    boolean containsMethod(MethodSignature signature);
 }
diff --git a/tapestry-framework/src/org/apache/tapestry/enhance/MethodSignature.java b/tapestry-framework/src/org/apache/tapestry/enhance/MethodSignature.java
index cabbe9e..8cf69bc 100644
--- a/tapestry-framework/src/org/apache/tapestry/enhance/MethodSignature.java
+++ b/tapestry-framework/src/org/apache/tapestry/enhance/MethodSignature.java
@@ -14,89 +14,67 @@
 
 package org.apache.tapestry.enhance;
 
-import java.lang.reflect.Method;
-
-import org.apache.commons.lang.builder.EqualsBuilder;
-import org.apache.commons.lang.builder.HashCodeBuilder;
-import org.apache.commons.lang.builder.ToStringBuilder;
-
 /**
- *  The signature of a {@link java.lang.reflect.Method}, including
- *  the name, return type, and parameter types.  Used when checking
- *  for unimplemented methods in enhanced subclasses.  
- * 
- *  <p>
- *  The modifiers (i.e., "public", "abstract") and thrown
- *  exceptions are not relevant for these purposes, and
- *  are not part of the signature.
- * 
- *  <p>
- *  Instances of MethodSignature are immutable and
- *  implement equals() and hashCode() properly for use
- *  in Sets or as Map keys.
- *
- *  @author Howard Lewis Ship
- *  @version $Id$
- *  @since 3.0
- *
- **/
-
-public class MethodSignature
+ * A representation of a {@link java.lang.reflect.Method}, identifying the name, return type,
+ * parameter types and exception types. Actual Method objects are tied to a particular class, and
+ * don't compare well with other otherwise identical Methods from other classes or interface;
+ * MethodSignatures are distinct from classes and compare well.
+ * <p>
+ * Because the intended purpose is to compare methods from interfaces (which are always public and
+ * abstract) we don't bother to actually track the modifiers. In addition, at this time,
+ * MethodSignature <em>does not distinguish between instance and static
+ * methods</em>.
+ */
+public interface MethodSignature
 {
-    private String _name;
-    private Class _returnType;
-    private Class[] _parameterTypes;
-    private int _hashCode = 0;
+    /**
+     * Returns the exceptions for this method. Caution: do not modify the returned array. May return
+     * null.
+     */
+    Class[] getExceptionTypes();
 
-    public MethodSignature(Method m)
-    {
-        _name = m.getName();
-        _returnType = m.getReturnType();
+    /**
+     * The name of the method.
+     * @return method name
+     */
+    String getName();
 
-        // getParameterTypes() returns a copy for us to keep.
+    /**
+     * Returns the parameter types for this method. May return null. Caution: do not modify the
+     * returned array.
+     */
+    Class[] getParameterTypes();
 
-        _parameterTypes = m.getParameterTypes();
-    }
+    /**
+     * Method return type.
+     *
+     * @return The return type of the method.
+     */
+    Class getReturnType();
 
-    public boolean equals(Object obj)
-    {
-        if (obj == null || !(obj instanceof MethodSignature))
-            return false;
+    /**
+     * Returns a string consisting of the name of the method and its parameter values. This is
+     * similar to {@link #toString()}, but omits the return type and information about thrown
+     * exceptions. A unique id is used by  MethodIterator to identify overlapping methods
+     * (methods with the same name but different thrown exceptions).
+     *
+     */
+    String getUniqueId();
 
-        MethodSignature other = (MethodSignature) obj;
+    /**
+     * Returns true if this signature has the same return type, name and parameters types as the
+     * method signature passed in, and this signatures exceptions "trump" (are the same as, or
+     * super-implementations of, all exceptions thrown by the other method signature).
+     *
+     */
 
-        EqualsBuilder builder = new EqualsBuilder();
-        builder.append(_name, other._name);
-        builder.append(_returnType, other._returnType);
-        builder.append(_parameterTypes, other._parameterTypes);
+    boolean isOverridingSignatureOf(MethodSignature ms);
 
-        return builder.isEquals();
-    }
-
-    public int hashCode()
-    {
-        if (_hashCode == 0)
-        {
-            HashCodeBuilder builder = new HashCodeBuilder(253, 97);
-
-            builder.append(_name);
-            builder.append(_returnType);
-            builder.append(_parameterTypes);
-
-            _hashCode = builder.toHashCode();
-        }
-
-        return _hashCode;
-    }
-
-    public String toString()
-    {
-        ToStringBuilder builder = new ToStringBuilder(this);
-        builder.append("name", _name);
-        builder.append("returnType", _returnType);
-        builder.append("parameterTypes", _parameterTypes);
-
-        return builder.toString();
-    }
-
+    /**
+     * If the method definition has generic parameters or return types it
+     * is expected that this will return true.
+     *
+     * @return True if this is a generics based method, false otherwise.
+     */
+    boolean isGeneric();
 }
diff --git a/tapestry-framework/src/org/apache/tapestry/enhance/MethodSignatureImpl.java b/tapestry-framework/src/org/apache/tapestry/enhance/MethodSignatureImpl.java
new file mode 100644
index 0000000..5dff974
--- /dev/null
+++ b/tapestry-framework/src/org/apache/tapestry/enhance/MethodSignatureImpl.java
@@ -0,0 +1,241 @@
+package org.apache.tapestry.enhance;
+
+import org.apache.tapestry.enhance.javassist.ClassFabUtils;
+
+import java.lang.reflect.Method;
+
+/**
+ * JDK 1.4 based version of {@link MethodSignature}.
+ */
+public class MethodSignatureImpl implements MethodSignature
+{
+    protected int _hashCode = -1;
+
+    protected Class _returnType;
+
+    protected String _name;
+
+    protected Class[] _parameterTypes;
+
+    protected Class[] _exceptionTypes;
+
+    public MethodSignatureImpl(Class returnType, String name,
+            Class[] parameterTypes, Class[] exceptionTypes)
+    {
+        _returnType = returnType;
+        _name = name;
+        _parameterTypes = parameterTypes;
+        _exceptionTypes = exceptionTypes;
+    }
+
+    public MethodSignatureImpl(Method m)
+    {
+        this(m.getReturnType(), m.getName(), m.getParameterTypes(), m.getExceptionTypes());
+    }
+
+    public Class[] getExceptionTypes()
+    {
+        return _exceptionTypes;
+    }
+
+    public String getName()
+    {
+        return _name;
+    }
+
+    public Class[] getParameterTypes()
+    {
+        return _parameterTypes;
+    }
+
+    public Class getReturnType()
+    {
+        return _returnType;
+    }
+
+    public int hashCode()
+    {
+        if (_hashCode == -1)
+        {
+            _hashCode = _returnType.hashCode();
+
+            _hashCode = 31 * _hashCode + _name.hashCode();
+
+            int count = count(_parameterTypes);
+
+            for (int i = 0; i < count; i++)
+                _hashCode = 31 * _hashCode + _parameterTypes[i].hashCode();
+
+            count = count(_exceptionTypes);
+
+            for (int i = 0; i < count; i++)
+                _hashCode = 31 * _hashCode + _exceptionTypes[i].hashCode();
+        }
+
+        return _hashCode;
+    }
+
+    protected static int count(Object[] array)
+    {
+        return array == null ? 0 : array.length;
+    }
+
+    /**
+     * Returns true if the other object is an instance of MethodSignature with identical values for
+     * return type, name, parameter types and exception types.
+     */
+    public boolean equals(Object o)
+    {
+        if (o == null || !(o instanceof MethodSignatureImpl))
+            return false;
+
+        MethodSignatureImpl ms = (MethodSignatureImpl) o;
+
+        if (_returnType != ms._returnType)
+            return false;
+
+        if (!_name.equals(ms._name))
+            return false;
+
+        if (mismatch(_parameterTypes, ms._parameterTypes))
+            return false;
+
+        return !mismatch(_exceptionTypes, ms._exceptionTypes);
+    }
+
+    protected boolean mismatch(Class[] a1, Class[] a2)
+    {
+        int a1Count = count(a1);
+        int a2Count = count(a2);
+
+        if (a1Count != a2Count)
+            return true;
+
+        // Hm. What if order is important (for exceptions)? We're really saying here that they
+        // were derived from the name Method.
+
+        for (int i = 0; i < a1Count; i++)
+        {
+            if (!a1[i].isAssignableFrom(a2[i]))
+                return true;
+        }
+
+        return false;
+    }
+
+    public String toString()
+    {
+        StringBuffer buffer = new StringBuffer();
+
+        buffer.append(ClassFabUtils.getJavaClassName(_returnType));
+        buffer.append(" ");
+        buffer.append(_name);
+        buffer.append("(");
+
+        for (int i = 0; i < count(_parameterTypes); i++)
+        {
+            if (i > 0)
+                buffer.append(", ");
+
+            buffer.append(ClassFabUtils.getJavaClassName(_parameterTypes[i]));
+        }
+
+        buffer.append(")");
+
+        for (int i = 0; i < count(_exceptionTypes); i++)
+        {
+            if (i == 0)
+                buffer.append(" throws ");
+            else
+                buffer.append(", ");
+
+            buffer.append(_exceptionTypes[i].getName());
+        }
+
+        return buffer.toString();
+    }
+
+    public String getUniqueId()
+    {
+        StringBuffer buffer = new StringBuffer(_name);
+        buffer.append("(");
+
+        for (int i = 0; i < count(_parameterTypes); i++)
+        {
+            if (i > 0)
+                buffer.append(",");
+
+            buffer.append(ClassFabUtils.getJavaClassName(_parameterTypes[i]));
+        }
+
+        buffer.append(")");
+
+        return buffer.toString();
+    }
+
+    public boolean isGeneric()
+    {
+        return false;
+    }
+
+    public boolean isOverridingSignatureOf(MethodSignature ms)
+    {
+        if (!(ms instanceof MethodSignatureImpl))
+            return false;
+
+        MethodSignatureImpl sig = (MethodSignatureImpl)ms;
+
+        if (!sig._returnType.isAssignableFrom(_returnType))
+            return false;
+
+        if (!_name.equals(sig._name))
+            return false;
+
+        if (mismatch(_parameterTypes, sig._parameterTypes))
+            return false;
+
+        return exceptionsEncompass(sig._exceptionTypes);
+    }
+
+    /**
+     * The nuts and bolts of checking that another method signature's exceptions are a subset of
+     * this signature's.
+     */
+
+    protected boolean exceptionsEncompass(Class[] otherExceptions)
+    {
+        int ourCount = count(_exceptionTypes);
+        int otherCount = count(otherExceptions);
+
+        // If we have no exceptions, then ours encompass theirs only if they
+        // have no exceptions, either.
+
+        if (ourCount == 0)
+            return otherCount == 0;
+
+        boolean[] matched = new boolean[otherCount];
+        int unmatched = otherCount;
+
+        for (int i = 0; i < ourCount && unmatched > 0; i++)
+        {
+            for (int j = 0; j < otherCount; j++)
+            {
+                // Ignore exceptions that have already been matched
+
+                if (matched[j])
+                    continue;
+
+                // When one of our exceptions is a super-class of one of their exceptions,
+                // then their exceptions is matched.
+
+                if (_exceptionTypes[i].isAssignableFrom(otherExceptions[j]))
+                {
+                    matched[j] = true;
+                    unmatched--;
+                }
+            }
+        }
+
+        return unmatched == 0;
+    }
+}
diff --git a/tapestry-framework/src/org/apache/tapestry/enhance/javassist/ClassFabUtils.java b/tapestry-framework/src/org/apache/tapestry/enhance/javassist/ClassFabUtils.java
new file mode 100644
index 0000000..0deec4d
--- /dev/null
+++ b/tapestry-framework/src/org/apache/tapestry/enhance/javassist/ClassFabUtils.java
@@ -0,0 +1,74 @@
+package org.apache.tapestry.enhance.javassist;
+
+import java.lang.reflect.Method;
+
+/**
+ * Static class containing utility methods.
+ *
+ * @author Howard Lewis Ship
+ */
+public class ClassFabUtils
+{
+    private static long _uid = System.currentTimeMillis();
+
+    private static final char QUOTE = '"';
+
+    private ClassFabUtils()
+    {
+    }
+
+    /**
+     * Generates a unique class name, which will be in the default package.
+     */
+
+    public static synchronized String generateClassName(String baseName)
+    {
+        return "$" + baseName + "_" + Long.toHexString(_uid++);
+    }
+
+    /**
+     * Returns a class name derived from the provided interfaceClass. The package part of the
+     * interface name is stripped out, and the result passed to {@link #generateClassName(String)}.
+     *
+     * @since 1.1
+     */
+
+    public static synchronized String generateClassName(Class interfaceClass)
+    {
+        String name = interfaceClass.getName();
+
+        int dotx = name.lastIndexOf('.');
+
+        return generateClassName(name.substring(dotx + 1));
+    }
+
+    /**
+     * Javassist needs the class name to be as it appears in source code, even for arrays. Invoking
+     * getName() on a Class instance representing an array returns the internal format (i.e, "[...;"
+     * or something). This returns it as it would appear in Java code.
+     */
+    public static String getJavaClassName(Class inputClass)
+    {
+        if (inputClass.isArray())
+            return getJavaClassName(inputClass.getComponentType()) + "[]";
+
+        return inputClass.getName();
+    }
+
+    /**
+     * Returns true if the method is the standard toString() method. Very few interfaces will ever
+     * include this method as part of the interface, but we have to be sure.
+     */
+    public static boolean isToString(Method method)
+    {
+        if (!method.getName().equals("toString"))
+            return false;
+
+        if (method.getParameterTypes().length > 0)
+            return false;
+
+        return method.getReturnType().equals(String.class);
+    }
+}
+
+
diff --git a/tapestry-framework/src/org/apache/tapestry/enhance/javassist/ClassFabricator.java b/tapestry-framework/src/org/apache/tapestry/enhance/javassist/ClassFabricator.java
index 926ae98..bea97d3 100644
--- a/tapestry-framework/src/org/apache/tapestry/enhance/javassist/ClassFabricator.java
+++ b/tapestry-framework/src/org/apache/tapestry/enhance/javassist/ClassFabricator.java
@@ -17,7 +17,10 @@
 import javassist.*;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import org.apache.tapestry.ApplicationRuntimeException;
+import org.apache.tapestry.Tapestry;
 import org.apache.tapestry.enhance.CodeGenerationException;
+import org.apache.tapestry.enhance.MethodSignature;
 
 import java.io.IOException;
 import java.text.MessageFormat;
@@ -87,7 +90,12 @@
             return null;
         }
     }
-    
+
+    public void addInterface(Class interfaceType)
+    {
+        _genClass.addInterface(getCtClass(interfaceType));
+    }
+
     public void createField(CtClass fieldType, String fieldName)
     {
         if (LOG.isDebugEnabled())
@@ -261,11 +269,81 @@
         }
     }
 
-    
+    public CtClass getCtClass(Class searchClass)
+    {
+        String name = ClassFabUtils.getJavaClassName(searchClass);
+        
+        try
+        {
+            return _classPool.get(name);
+        }
+        catch (NotFoundException ex)
+        {
+            throw new ApplicationRuntimeException(Tapestry.format("unable-to-lookup", name, ex), ex);
+        }
+    }
+
+    public void addMethod(int modifiers, MethodSignature ms, String body)
+    {
+        CtClass ctReturnType = getCtClass(ms.getReturnType());
+
+        CtClass[] ctParameters = convertClasses(ms.getParameterTypes());
+        CtClass[] ctExceptions = convertClasses(ms.getExceptionTypes());
+
+        CtMethod method = new CtMethod(ctReturnType, ms.getName(), ctParameters, _genClass);
+
+        try {
+            method.setModifiers(modifiers);
+            method.setBody(body);
+            method.setExceptionTypes(ctExceptions);
+
+            _genClass.addMethod(method);
+        } catch (Exception ex)
+        {
+            throw new CodeGenerationException(ex);
+        }
+    }
+
+    public void addConstructor(Class[] parameterTypes, Class[] exceptions, String body)
+    {
+        CtClass[] ctParameters = convertClasses(parameterTypes);
+        CtClass[] ctExceptions = convertClasses(exceptions);
+
+        try
+        {
+            CtConstructor constructor = new CtConstructor(ctParameters, _genClass);
+            constructor.setExceptionTypes(ctExceptions);
+            constructor.setBody(body);
+
+            _genClass.addConstructor(constructor);
+        } catch (Exception ex) 
+        {
+            throw new CodeGenerationException(ex);
+        }
+    }
+
     public void commit()
     {
     }
 
+    protected CtClass[] convertClasses(Class[] inputClasses)
+    {
+        if (inputClasses == null || inputClasses.length == 0)
+            return null;
+
+        int count = inputClasses.length;
+        CtClass[] result = new CtClass[count];
+
+        for (int i = 0; i < count; i++)
+        {
+            CtClass ctClass = getCtClass(inputClasses[i]);
+
+            result[i] = ctClass;
+        }
+
+        return result;
+    }
+
     public byte[] getByteCode()
     {
         try
diff --git a/tapestry-framework/src/org/apache/tapestry/enhance/javassist/CompiledExpression.java b/tapestry-framework/src/org/apache/tapestry/enhance/javassist/CompiledExpression.java
new file mode 100644
index 0000000..8c81be5
--- /dev/null
+++ b/tapestry-framework/src/org/apache/tapestry/enhance/javassist/CompiledExpression.java
@@ -0,0 +1,93 @@
+package org.apache.tapestry.enhance.javassist;
+
+import ognl.Node;
+import org.apache.tapestry.enhance.IEnhancedClass;
+import org.apache.tapestry.enhance.MethodSignature;
+
+/**
+ * Simple struct used by {@link OGNLExpressionCompiler} to hold temporary references to
+ * all of the objects involved in compiling / generating a compiled ognl expression.
+ */
+public class CompiledExpression {
+
+    IEnhancedClass _generatedClass;
+    Node _expression;
+    Object _root;
+    MethodSignature _getterMethod;
+    MethodSignature _setterMethod;
+
+    public CompiledExpression(Node expression, Object root,
+                              MethodSignature getter, MethodSignature setter)
+    {
+        _expression = expression;
+        _root = root;
+        _getterMethod = getter;
+        _setterMethod = setter;
+    }
+
+    public IEnhancedClass getGeneratedClass()
+    {
+        return _generatedClass;
+    }
+
+    public void setGeneratedClass(IEnhancedClass generatedClass)
+    {
+        _generatedClass = generatedClass;
+    }
+
+    public Node getExpression()
+    {
+        return _expression;
+    }
+
+    public void setExpression(Node expression)
+    {
+        _expression = expression;
+    }
+
+    public Object getRoot()
+    {
+        return _root;
+    }
+
+    public void setRoot(Object root)
+    {
+        _root = root;
+    }
+
+    public MethodSignature getGetterMethod()
+    {
+        return _getterMethod;
+    }
+
+    public void setGetterMethod(MethodSignature method)
+    {
+        _getterMethod = method;
+    }
+
+    public MethodSignature getSettermethod()
+    {
+        return _setterMethod;
+    }
+
+    public void setSetterMethod(MethodSignature method)
+    {
+        _setterMethod = method;
+    }
+
+    public String toString()
+    {
+        return "CompiledExpression[" +
+               "_generatedClass=" + _generatedClass +
+               '\n' +
+               ", _expression=" + _expression +
+               '\n' +
+               ", _root=" + _root +
+               '\n' +
+               ", _getterMethod=" + _getterMethod +
+               '\n' +
+               ", _setterMethod=" + _setterMethod +
+               '\n' +
+               ']';
+    }
+}
\ No newline at end of file
diff --git a/tapestry-framework/src/org/apache/tapestry/enhance/javassist/EnhancedClass.java b/tapestry-framework/src/org/apache/tapestry/enhance/javassist/EnhancedClass.java
index eef0adc..ebf0351 100644
--- a/tapestry-framework/src/org/apache/tapestry/enhance/javassist/EnhancedClass.java
+++ b/tapestry-framework/src/org/apache/tapestry/enhance/javassist/EnhancedClass.java
@@ -16,16 +16,19 @@
 
 import javassist.ClassPool;
 import javassist.CtClass;
-
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.apache.tapestry.enhance.BaseEnhancedClass;
 import org.apache.tapestry.enhance.EnhancedClassLoader;
 import org.apache.tapestry.enhance.IEnhancer;
+import org.apache.tapestry.enhance.MethodSignature;
+
+import java.util.HashSet;
+import java.util.Set;
 
 /**
  *  Represents a class to be enhanced using Javassist. 
- * 
+ *
  *  @author Mindbridge
  *  @version $Id$
  *  @since 3.0
@@ -40,6 +43,8 @@
 
     private ClassFabricator _classFabricator = null;
 
+    private Set _methods = new HashSet();
+
     public EnhancedClass(String className, Class parentClass, EnhancedClassFactory classFactory)
     {
         _className = className;
@@ -83,17 +88,17 @@
      * @see org.apache.tapestry.enhance.IEnhancedClass#createProperty(java.lang.String, java.lang.String, java.lang.String, boolean)
      */
     public void createProperty(
-        String propertyName,
-        String propertyType,
-        String readMethodName,
-        boolean persistent)
+            String propertyName,
+            String propertyType,
+            String readMethodName,
+            boolean persistent)
     {
         IEnhancer enhancer =
-            new CreatePropertyEnhancer(
-                propertyName,
-                getObjectType(propertyType),
-                readMethodName,
-                persistent);
+                new CreatePropertyEnhancer(
+                        propertyName,
+                        getObjectType(propertyType),
+                        readMethodName,
+                        persistent);
         addEnhancer(enhancer);
     }
 
@@ -101,18 +106,18 @@
      * @see org.apache.tapestry.enhance.IEnhancedClass#createAutoParameter(java.lang.String, java.lang.String, java.lang.String, java.lang.String)
      */
     public void createAutoParameter(
-        String propertyName,
-        String parameterName,
-        String typeClassName,
-        String readMethodName)
+            String propertyName,
+            String parameterName,
+            String typeClassName,
+            String readMethodName)
     {
         IEnhancer enhancer =
-            new CreateAutoParameterEnhancer(
-                this,
-                propertyName,
-                parameterName,
-                getObjectType(typeClassName),
-                readMethodName);
+                new CreateAutoParameterEnhancer(
+                        this,
+                        propertyName,
+                        parameterName,
+                        getObjectType(typeClassName),
+                        readMethodName);
         addEnhancer(enhancer);
     }
 
@@ -131,9 +136,34 @@
 
         EnhancedClassLoader loader = _classFactory.getEnhancedClassLoader();
         return loader.defineClass(
-            enhancedClassName,
-            enhancedClassBytes,
-            _parentClass.getProtectionDomain());
+                enhancedClassName,
+                enhancedClassBytes,
+                _parentClass.getProtectionDomain());
     }
 
+    public void addInterface(Class type)
+    {
+        getClassFabricator().addInterface(type);
+    }
+
+    public void addMethod(int modifiers, MethodSignature signature, String body)
+    {
+        getClassFabricator().addMethod(modifiers, signature, body);
+        _methods.add(signature);
+    }
+
+    public void addField(String name, Class type)
+    {
+        getClassFabricator().createField(getClassFabricator().getCtClass(type), name);
+    }
+
+    public void addConstructor(Class[] parameterTypes, Class[] exceptions, String body)
+    {
+        getClassFabricator().addConstructor(parameterTypes, exceptions, body);
+    }
+
+    public boolean containsMethod(MethodSignature signature)
+    {
+        return _methods.contains(signature);
+    }
 }
diff --git a/tapestry-framework/src/org/apache/tapestry/enhance/javassist/OGNLExpressionCompiler.java b/tapestry-framework/src/org/apache/tapestry/enhance/javassist/OGNLExpressionCompiler.java
new file mode 100644
index 0000000..19140cc
--- /dev/null
+++ b/tapestry-framework/src/org/apache/tapestry/enhance/javassist/OGNLExpressionCompiler.java
@@ -0,0 +1,451 @@
+package org.apache.tapestry.enhance.javassist;
+
+import javassist.CannotCompileException;
+import javassist.NotFoundException;
+import ognl.*;
+import ognl.enhance.*;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.tapestry.IRender;
+import org.apache.tapestry.enhance.IEnhancedClass;
+import org.apache.tapestry.enhance.IEnhancedClassFactory;
+import org.apache.tapestry.enhance.MethodSignature;
+import org.apache.tapestry.enhance.MethodSignatureImpl;
+
+import java.lang.reflect.Modifier;
+import java.util.*;
+
+/**
+ * Adds to default ognl compiler class pools.
+ *
+ */
+public class OGNLExpressionCompiler extends ExpressionCompiler implements OgnlExpressionCompiler {
+
+    private static final Log _log = LogFactory.getLog(OGNLExpressionCompiler.class);
+
+    private IEnhancedClassFactory _classFactory;
+
+    public OGNLExpressionCompiler(IEnhancedClassFactory classfactory)
+    {
+        _classFactory = classfactory;
+    }
+
+    public String getClassName(Class clazz)
+    {
+        if (IRender.class.isAssignableFrom(clazz) || Modifier.isPublic(clazz.getModifiers()))
+            return clazz.getName();
+
+        if (clazz.getName().equals("java.util.AbstractList$Itr"))
+            return Iterator.class.getName();
+
+        if (Modifier.isPublic(clazz.getModifiers()) && clazz.isInterface())
+            return clazz.getName();
+
+        Class[] intf = clazz.getInterfaces();
+
+        for (int i = 0; i < intf.length; i++)
+        {
+            if (intf[i].getName().indexOf("util.List") > 0)
+                return intf[i].getName();
+            else if (intf[i].getName().indexOf("Iterator") > 0)
+                return intf[i].getName();
+        }
+
+        if (clazz.getSuperclass() != null && clazz.getSuperclass().getInterfaces().length > 0)
+            return getClassName(clazz.getSuperclass());
+
+        return clazz.getName();
+    }
+
+    public Class getInterfaceClass(Class clazz)
+    {
+        if (IRender.class.isAssignableFrom(clazz) || clazz.isInterface()
+            || Modifier.isPublic(clazz.getModifiers()))
+            return clazz;
+
+        if (clazz.getName().equals("java.util.AbstractList$Itr"))
+            return Iterator.class;
+
+        if (Modifier.isPublic(clazz.getModifiers())
+            && clazz.isInterface() || clazz.isPrimitive())
+        {
+            return clazz;
+        }
+
+        Class[] intf = clazz.getInterfaces();
+
+        for (int i = 0; i < intf.length; i++)
+        {
+            if (List.class.isAssignableFrom(intf[i]))
+                return List.class;
+            else if (Iterator.class.isAssignableFrom(intf[i]))
+                return Iterator.class;
+            else if (Map.class.isAssignableFrom(intf[i]))
+                return Map.class;
+            else if (Set.class.isAssignableFrom(intf[i]))
+                return Set.class;
+            else if (Collection.class.isAssignableFrom(intf[i]))
+                return Collection.class;
+        }
+
+        if (clazz.getSuperclass() != null && clazz.getSuperclass().getInterfaces().length > 0)
+            return getInterfaceClass(clazz.getSuperclass());
+
+        return clazz;
+    }
+
+    public Class getRootExpressionClass(Node rootNode, OgnlContext context)
+    {
+        if (context.getRoot() == null)
+            return null;
+
+        Class ret = context.getRoot().getClass();
+
+        if (!IRender.class.isInstance(context.getRoot())
+            && context.getFirstAccessor() != null
+            && context.getFirstAccessor().isInstance(context.getRoot()))
+        {
+            ret = context.getFirstAccessor();
+        }
+
+        return ret;
+    }
+
+    public void compileExpression(OgnlContext context, Node expression, Object root)
+            throws Exception
+    {
+        if (_log.isDebugEnabled())
+            _log.debug("Compiling expr class " + expression.getClass().getName()
+                       + " and root " + root.getClass().getName() + " with toString:" + expression.toString());
+
+        synchronized (expression)
+        {
+            if (expression.getAccessor() != null)
+                return;
+
+            String getBody = null;
+            String setBody;
+
+            MethodSignature valueGetter = new MethodSignatureImpl(Object.class, "get", new Class[]{OgnlContext.class, Object.class}, null);
+            MethodSignature valueSetter = new MethodSignatureImpl(void.class, "set", new Class[]{OgnlContext.class, Object.class, Object.class}, null);
+
+            CompiledExpression compiled = new CompiledExpression(expression, root, valueGetter, valueSetter);
+
+            MethodSignature expressionSetter = new MethodSignatureImpl(void.class, "setExpression", new Class[]{Node.class}, null);
+
+            try
+            {
+                getBody = generateGetter(context, compiled);
+            } catch (UnsupportedCompilationException uc)
+            {
+                // uc.printStackTrace();
+                // The target object may not fully resolve yet because of a partial tree with a null somewhere, we
+                // don't want to bail out forever because it might be enhancable on another pass eventually
+                return;
+            } catch (javassist.CannotCompileException e)
+            {
+                _log.error("Error generating OGNL getter for expression " + expression + " with root " + root + " and body:\n" + getBody, e);
+
+                e.printStackTrace();
+
+                generateFailSafe(context, expression, root);
+                return;
+            }
+
+            try
+            {
+                generateClassFab(compiled).addMethod(Modifier.PUBLIC, valueGetter, getBody);
+            } catch (Throwable t)
+            {
+                _log.error("Error generating OGNL getter for expression " + expression + " with root " + root + " and body:\n" + getBody, t);
+
+                t.printStackTrace();
+
+                generateFailSafe(context, expression, root);
+                return;
+            }
+
+            try
+            {
+                setBody = generateSetter(context, compiled);
+            } catch (UnsupportedCompilationException uc)
+            {
+                //_log.warn("Unsupported setter compilation caught: " + uc.getMessage() + " for expression: " + expression.toString(), uc);
+
+                setBody = generateOgnlSetter(generateClassFab(compiled), valueSetter);
+
+                if (!generateClassFab(compiled).containsMethod(expressionSetter))
+                {
+                    generateClassFab(compiled).addField("_node", Node.class);
+                    generateClassFab(compiled).addMethod(Modifier.PUBLIC, expressionSetter, "{ _node = $1; }");
+                }
+            }
+
+            try
+            {
+                if (setBody == null)
+                {
+                    setBody = generateOgnlSetter(generateClassFab(compiled), valueSetter);
+
+                    if (!generateClassFab(compiled).containsMethod(expressionSetter))
+                    {
+                        generateClassFab(compiled).addField("_node", Node.class);
+                        generateClassFab(compiled).addMethod(Modifier.PUBLIC, expressionSetter, "{ _node = $1; }");
+                    }
+                }
+
+                if (setBody != null)
+                    generateClassFab(compiled).addMethod(Modifier.PUBLIC, valueSetter, setBody);
+
+                generateClassFab(compiled).addConstructor(new Class[0], new Class[0], "{}");
+
+                Class clazz = generateClassFab(compiled).createEnhancedSubclass();
+
+                expression.setAccessor((ExpressionAccessor) clazz.newInstance());
+
+            }  catch (Throwable t)
+            {
+                _log.error("Error generating OGNL statements for expression " + expression + " with root " + root, t);
+                t.printStackTrace();
+
+                generateFailSafe(context, expression, root);
+                return;
+            }
+
+            // need to set expression on node if the field was just defined.
+
+            if (generateClassFab(compiled).containsMethod(expressionSetter))
+            {
+                expression.getAccessor().setExpression(expression);
+            }
+        }
+    }
+
+    IEnhancedClass generateClassFab(CompiledExpression compiled)
+            throws Exception
+    {
+        if (compiled.getGeneratedClass() != null)
+            return compiled.getGeneratedClass();
+
+        IEnhancedClass classFab = _classFactory.createEnhancedClass(ClassFabUtils.generateClassName(compiled.getExpression().getClass()), Object.class);
+        classFab.addInterface(ExpressionAccessor.class);
+
+        compiled.setGeneratedClass(classFab);
+
+        return classFab;
+    }
+
+    protected void generateFailSafe(OgnlContext context, Node expression, Object root)
+    {
+        if (expression.getAccessor() != null)
+            return;
+
+        try
+        {
+            IEnhancedClass classFab = _classFactory.createEnhancedClass(ClassFabUtils
+                    .generateClassName(
+                    ClassFabUtils.getJavaClassName(expression.getClass()) + "Accessor"),
+                                                                        Object.class); 
+            classFab.addInterface(ExpressionAccessor.class);
+
+            MethodSignature valueGetter = new MethodSignatureImpl(Object.class, "get", new Class[]{OgnlContext.class, Object.class}, null);
+            MethodSignature valueSetter = new MethodSignatureImpl(void.class, "set", new Class[]{OgnlContext.class, Object.class, Object.class}, null);
+
+            MethodSignature expressionSetter = new MethodSignatureImpl(void.class, "setExpression", new Class[]{Node.class}, null);
+
+            if (!classFab.containsMethod(expressionSetter))
+            {
+                classFab.addField("_node", Node.class);
+                classFab.addMethod(Modifier.PUBLIC, expressionSetter, "{ _node = $1; }");
+            }
+
+            classFab.addMethod(Modifier.PUBLIC, valueGetter, generateOgnlGetter(classFab, valueGetter));
+            classFab.addMethod(Modifier.PUBLIC, valueSetter, generateOgnlSetter(classFab, valueSetter));
+
+            classFab.addConstructor(new Class[0], new Class[0], "{}");
+
+            Class clazz = classFab.createEnhancedSubclass();
+
+            expression.setAccessor((ExpressionAccessor) clazz.newInstance());
+
+            // need to set expression on node if the field was just defined.
+
+            if (classFab.containsMethod(expressionSetter))
+            {
+                expression.getAccessor().setExpression(expression);
+            }
+
+        } catch (Throwable t)
+        {
+            t.printStackTrace();
+        }
+    }
+
+    protected String generateGetter(OgnlContext context, CompiledExpression compiled)
+            throws Exception
+    {
+        String pre = "";
+        String post = "";
+        String body;
+        String getterCode;
+
+        context.setRoot(compiled.getRoot());
+        context.setCurrentObject(compiled.getRoot());
+        context.remove(PRE_CAST);
+
+        try
+        {
+            getterCode = compiled.getExpression().toGetSourceString(context, compiled.getRoot());
+        } catch (NullPointerException e)
+        {
+            if (_log.isDebugEnabled())
+                _log.warn("NullPointer caught compiling getter, may be normal ognl method artifact.", e);
+
+            throw new UnsupportedCompilationException("Statement threw nullpointer.");
+        }
+
+        if (getterCode == null || getterCode.trim().length() <= 0
+                                  && !ASTVarRef.class.isAssignableFrom(compiled.getExpression().getClass()))
+        {
+            getterCode = "null";
+        }
+
+        String castExpression = (String) context.get(PRE_CAST);
+
+        if (context.getCurrentType() == null
+            || context.getCurrentType().isPrimitive()
+            || Character.class.isAssignableFrom(context.getCurrentType())
+            || Object.class == context.getCurrentType())
+        {
+            pre = pre + " ($w) (";
+            post = post + ")";
+        }
+
+        String rootExpr = !getterCode.equals("null") ? getRootExpression(compiled.getExpression(), compiled.getRoot(), context) : "";
+
+        String noRoot = (String) context.remove("_noRoot");
+        if (noRoot != null)
+            rootExpr = "";
+
+        createLocalReferences(context, generateClassFab(compiled), compiled.getGetterMethod().getParameterTypes());
+
+        if (OrderedReturn.class.isInstance(compiled.getExpression()) && ((OrderedReturn) compiled.getExpression()).getLastExpression() != null)
+        {
+            body = "{ "
+                   + (ASTMethod.class.isInstance(compiled.getExpression()) || ASTChain.class.isInstance(compiled.getExpression()) ? rootExpr : "")
+                   + (castExpression != null ? castExpression : "")
+                   + ((OrderedReturn) compiled.getExpression()).getCoreExpression()
+                   + " return " + pre + ((OrderedReturn) compiled.getExpression()).getLastExpression()
+                   + post
+                   + ";}";
+
+        } else
+        {
+            body = "{ return " + pre
+                   + (castExpression != null ? castExpression : "")
+                   + rootExpr
+                   + getterCode
+                   + post
+                   + ";}";
+        }
+
+        body = body.replaceAll("\\.\\.", ".");
+
+        if (_log.isDebugEnabled())
+            _log.debug("Getter Body: ===================================\n" + body);
+
+        return body;
+    }
+
+    void createLocalReferences(OgnlContext context, IEnhancedClass classFab, Class[] params)
+            throws CannotCompileException, NotFoundException
+    {
+        Map referenceMap = context.getLocalReferences();
+        if (referenceMap == null || referenceMap.size() < 1)
+            return;
+
+        Iterator it = referenceMap.keySet().iterator();
+
+        while (it.hasNext())
+        {
+            String key = (String) it.next();
+            LocalReference ref = (LocalReference) referenceMap.get(key);
+
+            String widener = ref.getType().isPrimitive() ? " " : " ($w) ";
+
+            String body = "{";
+            body += " return  " + widener + ref.getExpression() + ";";
+            body += "}";
+
+            body = body.replaceAll("\\.\\.", ".");
+
+            if (_log.isDebugEnabled())
+                _log.debug("createLocalReferences() body is:\n" + body);
+
+            MethodSignature method = new MethodSignatureImpl(ref.getType(), ref.getName(), params, null);
+            classFab.addMethod(Modifier.PUBLIC, method, body);
+
+            it.remove();
+        }
+    }
+
+    protected String generateSetter(OgnlContext context, CompiledExpression compiled)
+            throws Exception
+    {
+        if (ExpressionNode.class.isInstance(compiled.getExpression())
+            || ASTConst.class.isInstance(compiled.getExpression()))
+            throw new UnsupportedCompilationException("Can't compile expression/constant setters.");
+
+        context.setRoot(compiled.getRoot());
+        context.setCurrentObject(compiled.getRoot());
+        context.remove(PRE_CAST);
+
+        String body;
+
+        String setterCode = compiled.getExpression().toSetSourceString(context, compiled.getRoot());
+        String castExpression = (String) context.get(PRE_CAST);
+
+        if (setterCode == null || setterCode.trim().length() < 1)
+            throw new UnsupportedCompilationException("Can't compile null setter body.");
+
+        if (compiled.getRoot() == null)
+            throw new UnsupportedCompilationException("Can't compile setters with a null root object.");
+
+        String pre = getRootExpression(compiled.getExpression(), compiled.getRoot(), context);
+
+        String noRoot = (String) context.remove("_noRoot");
+        if (noRoot != null)
+            pre = "";
+
+        String setterValue = (String) context.remove("setterConversion");
+        if (setterValue == null)
+            setterValue = "";
+
+        createLocalReferences(context, generateClassFab(compiled), compiled.getSettermethod().getParameterTypes());
+
+        body = "{"
+               + setterValue
+               + (castExpression != null ? castExpression : "")
+               + pre
+               + setterCode + ";}";
+
+        body = body.replaceAll("\\.\\.", ".");
+
+        if (_log.isDebugEnabled())
+            _log.debug("Setter Body: ===================================\n" + body);
+
+        return body;
+    }
+
+    String generateOgnlGetter(IEnhancedClass newClass, MethodSignature valueGetter)
+            throws Exception
+    {
+        return "{ return _node.getValue($1, $2); }";
+    }
+
+    String generateOgnlSetter(IEnhancedClass newClass, MethodSignature valueSetter)
+            throws Exception
+    {
+        return "{ _node.setValue($1, $2, $3); }";
+    }
+}
\ No newline at end of file
diff --git a/tapestry-framework/src/org/apache/tapestry/param/AbstractParameterConnector.java b/tapestry-framework/src/org/apache/tapestry/param/AbstractParameterConnector.java
index 7aa1918..cac3d60 100644
--- a/tapestry-framework/src/org/apache/tapestry/param/AbstractParameterConnector.java
+++ b/tapestry-framework/src/org/apache/tapestry/param/AbstractParameterConnector.java
@@ -18,12 +18,11 @@
 import org.apache.tapestry.IComponent;
 import org.apache.tapestry.IForm;
 import org.apache.tapestry.IRequestCycle;
-import org.apache.tapestry.IResourceResolver;
+import org.apache.tapestry.engine.ExpressionEvaluator;
 import org.apache.tapestry.form.Form;
 import org.apache.tapestry.form.IFormComponent;
 import org.apache.tapestry.spec.Direction;
 import org.apache.tapestry.spec.IParameterSpecification;
-import org.apache.tapestry.util.prop.OgnlUtils;
 
 /**
  *  Standard implementation of {@link IParameterConnector}.
@@ -44,7 +43,7 @@
     private boolean _required;
     private Object _clearValue;
     private Direction _direction;
-    private IResourceResolver _resolver;
+    private ExpressionEvaluator _evaluator;
 
     /**
      *  Creates a connector.  In addition, obtains the current value
@@ -59,7 +58,7 @@
         _parameterName = parameterName;
         _binding = binding;
 
-        _resolver = component.getPage().getEngine().getResourceResolver();
+        _evaluator = component.getPage().getEngine().getExpressionEvaluator();
 
         IParameterSpecification pspec = _component.getSpecification().getParameter(_parameterName);
         _required = pspec.isRequired();
@@ -73,7 +72,7 @@
 
     private Object readCurrentPropertyValue()
     {
-        return OgnlUtils.get(_propertyName, _resolver, _component);
+        return _evaluator.read(_component, _propertyName);
     }
 
     /**
@@ -83,7 +82,7 @@
 
     protected void setPropertyValue(Object value)
     {
-        OgnlUtils.set(_propertyName, _resolver, _component, value);
+        _evaluator.write(_component, _propertyName, value);
     }
 
     /**