001 // Copyright 2004, 2005 The Apache Software Foundation 002 // 003 // Licensed under the Apache License, Version 2.0 (the "License"); 004 // you may not use this file except in compliance with the License. 005 // You may obtain a copy of the License at 006 // 007 // http://www.apache.org/licenses/LICENSE-2.0 008 // 009 // Unless required by applicable law or agreed to in writing, software 010 // distributed under the License is distributed on an "AS IS" BASIS, 011 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 012 // See the License for the specific language governing permissions and 013 // limitations under the License. 014 015 package org.apache.tapestry.services.impl; 016 017 import ognl.*; 018 import ognl.enhance.ExpressionAccessor; 019 import org.apache.commons.pool.impl.GenericObjectPool; 020 import org.apache.hivemind.ApplicationRuntimeException; 021 import org.apache.hivemind.events.RegistryShutdownListener; 022 import org.apache.hivemind.service.ClassFactory; 023 import org.apache.tapestry.Tapestry; 024 import org.apache.tapestry.services.ExpressionCache; 025 import org.apache.tapestry.services.ExpressionEvaluator; 026 import org.apache.tapestry.spec.IApplicationSpecification; 027 028 import java.beans.Introspector; 029 import java.util.Iterator; 030 import java.util.List; 031 import java.util.Map; 032 033 /** 034 * @since 4.0 035 */ 036 public class ExpressionEvaluatorImpl implements ExpressionEvaluator, RegistryShutdownListener { 037 038 private static final long POOL_MIN_IDLE_TIME = 1000 * 60 * 50; 039 040 private static final long POOL_SLEEP_TIME = 1000 * 60 * 4; 041 042 // Uses Thread's context class loader 043 044 private final ClassResolver _ognlResolver = new OgnlClassResolver(); 045 046 private ExpressionCache _expressionCache; 047 048 private IApplicationSpecification _applicationSpecification; 049 050 private TypeConverter _typeConverter; 051 052 private List _contributions; 053 054 private List _nullHandlerContributions; 055 056 // Context, with a root of null, used when evaluating an expression 057 // to see if it is a constant. 058 059 private Map _defaultContext; 060 061 private ClassFactory _classFactory; 062 063 private GenericObjectPool _contextPool; 064 065 public void setApplicationSpecification(IApplicationSpecification applicationSpecification) 066 { 067 _applicationSpecification = applicationSpecification; 068 } 069 070 public void initializeService() 071 { 072 if (_applicationSpecification.checkExtension(Tapestry.OGNL_TYPE_CONVERTER)) 073 _typeConverter = (TypeConverter) _applicationSpecification.getExtension(Tapestry.OGNL_TYPE_CONVERTER, TypeConverter.class); 074 075 Iterator i = _contributions.iterator(); 076 077 while (i.hasNext()) 078 { 079 PropertyAccessorContribution c = (PropertyAccessorContribution) i.next(); 080 081 OgnlRuntime.setPropertyAccessor(c.getSubjectClass(), c.getAccessor()); 082 } 083 084 Iterator j = _nullHandlerContributions.iterator(); 085 086 while (j.hasNext()) 087 { 088 NullHandlerContribution h = (NullHandlerContribution) j.next(); 089 090 OgnlRuntime.setNullHandler(h.getSubjectClass(), h.getHandler()); 091 } 092 093 _defaultContext = Ognl.createDefaultContext(null, _ognlResolver, _typeConverter); 094 095 OgnlRuntime.setCompiler(new HiveMindExpressionCompiler(_classFactory)); 096 097 _contextPool = new GenericObjectPool(new PoolableOgnlContextFactory(_ognlResolver, _typeConverter)); 098 099 _contextPool.setMaxActive(-1); 100 _contextPool.setMaxIdle(-1); 101 _contextPool.setMinEvictableIdleTimeMillis(POOL_MIN_IDLE_TIME); 102 _contextPool.setTimeBetweenEvictionRunsMillis(POOL_SLEEP_TIME); 103 } 104 105 public Object read(Object target, String expression) 106 { 107 Node node = (Node)_expressionCache.getCompiledExpression(target, expression); 108 109 if (node.getAccessor() != null) 110 return read(target, node.getAccessor()); 111 112 return readCompiled(target, node); 113 } 114 115 public Object readCompiled(Object target, Object expression) 116 { 117 OgnlContext context = null; 118 try 119 { 120 context = (OgnlContext)_contextPool.borrowObject(); 121 context.setRoot(target); 122 123 return Ognl.getValue(expression, context, target); 124 } 125 catch (Exception ex) 126 { 127 throw new ApplicationRuntimeException(ImplMessages.unableToReadExpression(ImplMessages 128 .parsedExpression(), target, ex), target, null, ex); 129 } finally { 130 try { if (context != null) _contextPool.returnObject(context); } catch (Exception e) {} 131 } 132 } 133 134 public Object read(Object target, ExpressionAccessor expression) 135 { 136 OgnlContext context = null; 137 try 138 { 139 context = (OgnlContext)_contextPool.borrowObject(); 140 141 return expression.get(context, target); 142 } 143 catch (Exception ex) 144 { 145 throw new ApplicationRuntimeException(ImplMessages.unableToReadExpression(ImplMessages.parsedExpression(), 146 target, ex), target, null, ex); 147 } finally { 148 try { if (context != null) _contextPool.returnObject(context); } catch (Exception e) {} 149 } 150 } 151 152 public OgnlContext createContext(Object target) 153 { 154 OgnlContext result = (OgnlContext)Ognl.createDefaultContext(target, _ognlResolver); 155 156 if (_typeConverter != null) 157 Ognl.setTypeConverter(result, _typeConverter); 158 159 return result; 160 } 161 162 public void write(Object target, String expression, Object value) 163 { 164 writeCompiled(target, _expressionCache.getCompiledExpression(target, expression), value); 165 } 166 167 public void write(Object target, ExpressionAccessor expression, Object value) 168 { 169 OgnlContext context = null; 170 try 171 { 172 context = (OgnlContext)_contextPool.borrowObject(); 173 174 // setup context 175 176 context.setRoot(target); 177 context.setCurrentObject(target); 178 179 expression.set(context, target, value); 180 } 181 catch (Exception ex) 182 { 183 throw new ApplicationRuntimeException(ImplMessages.unableToWriteExpression(ImplMessages 184 .parsedExpression(), target, value, ex), target, null, ex); 185 } finally { 186 try { if (context != null) _contextPool.returnObject(context); } catch (Exception e) {} 187 } 188 } 189 190 public void writeCompiled(Object target, Object expression, Object value) 191 { 192 OgnlContext context = null; 193 try 194 { 195 context = (OgnlContext)_contextPool.borrowObject(); 196 197 Ognl.setValue(expression, context, target, value); 198 } 199 catch (Exception ex) 200 { 201 throw new ApplicationRuntimeException(ImplMessages.unableToWriteExpression(ImplMessages 202 .parsedExpression(), target, value, ex), target, null, ex); 203 } finally { 204 try { if (context != null) _contextPool.returnObject(context); } catch (Exception e) {} 205 } 206 } 207 208 public boolean isConstant(Object target, String expression) 209 { 210 Object compiled = _expressionCache.getCompiledExpression(target, expression); 211 212 try 213 { 214 return Ognl.isConstant(compiled, _defaultContext); 215 } 216 catch (Exception ex) 217 { 218 throw new ApplicationRuntimeException(ImplMessages.isConstantExpressionError( 219 expression, 220 ex), ex); 221 } 222 } 223 224 public boolean isConstant(String expression) 225 { 226 Object compiled = _expressionCache.getCompiledExpression(expression); 227 228 try 229 { 230 return Ognl.isConstant(compiled, _defaultContext); 231 } 232 catch (Exception ex) 233 { 234 throw new ApplicationRuntimeException(ImplMessages.isConstantExpressionError( 235 expression, 236 ex), ex); 237 } 238 } 239 240 public void registryDidShutdown() 241 { 242 try 243 { 244 _contextPool.clear(); 245 _contextPool.close(); 246 247 OgnlRuntime.clearCache(); 248 Introspector.flushCaches(); 249 250 } catch (Exception et) { 251 // ignore 252 } 253 } 254 255 public void setExpressionCache(ExpressionCache expressionCache) 256 { 257 _expressionCache = expressionCache; 258 } 259 260 public void setContributions(List contributions) 261 { 262 _contributions = contributions; 263 } 264 265 public void setNullHandlerContributions(List nullHandlerContributions) 266 { 267 _nullHandlerContributions = nullHandlerContributions; 268 } 269 270 public void setClassFactory(ClassFactory classFactory) 271 { 272 _classFactory = classFactory; 273 } 274 }