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 178 expression.set(context, target, value); 179 } 180 catch (Exception ex) 181 { 182 throw new ApplicationRuntimeException(ImplMessages.unableToWriteExpression(ImplMessages 183 .parsedExpression(), target, value, ex), target, null, ex); 184 } finally { 185 try { if (context != null) _contextPool.returnObject(context); } catch (Exception e) {} 186 } 187 } 188 189 public void writeCompiled(Object target, Object expression, Object value) 190 { 191 OgnlContext context = null; 192 try 193 { 194 context = (OgnlContext)_contextPool.borrowObject(); 195 196 Ognl.setValue(expression, context, target, value); 197 } 198 catch (Exception ex) 199 { 200 throw new ApplicationRuntimeException(ImplMessages.unableToWriteExpression(ImplMessages 201 .parsedExpression(), target, value, ex), target, null, ex); 202 } finally { 203 try { if (context != null) _contextPool.returnObject(context); } catch (Exception e) {} 204 } 205 } 206 207 public boolean isConstant(Object target, String expression) 208 { 209 Object compiled = _expressionCache.getCompiledExpression(target, expression); 210 211 try 212 { 213 return Ognl.isConstant(compiled, _defaultContext); 214 } 215 catch (Exception ex) 216 { 217 throw new ApplicationRuntimeException(ImplMessages.isConstantExpressionError( 218 expression, 219 ex), ex); 220 } 221 } 222 223 public boolean isConstant(String expression) 224 { 225 Object compiled = _expressionCache.getCompiledExpression(expression); 226 227 try 228 { 229 return Ognl.isConstant(compiled, _defaultContext); 230 } 231 catch (Exception ex) 232 { 233 throw new ApplicationRuntimeException(ImplMessages.isConstantExpressionError( 234 expression, 235 ex), ex); 236 } 237 } 238 239 public void registryDidShutdown() 240 { 241 try 242 { 243 _contextPool.clear(); 244 _contextPool.close(); 245 246 OgnlRuntime.clearCache(); 247 Introspector.flushCaches(); 248 249 } catch (Exception et) { 250 // ignore 251 } 252 } 253 254 public void setExpressionCache(ExpressionCache expressionCache) 255 { 256 _expressionCache = expressionCache; 257 } 258 259 public void setContributions(List contributions) 260 { 261 _contributions = contributions; 262 } 263 264 public void setNullHandlerContributions(List nullHandlerContributions) 265 { 266 _nullHandlerContributions = nullHandlerContributions; 267 } 268 269 public void setClassFactory(ClassFactory classFactory) 270 { 271 _classFactory = classFactory; 272 } 273 }