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, 074 TypeConverter.class); 075 076 Iterator i = _contributions.iterator(); 077 078 while (i.hasNext()) 079 { 080 PropertyAccessorContribution c = (PropertyAccessorContribution) i.next(); 081 082 OgnlRuntime.setPropertyAccessor(c.getSubjectClass(), c.getAccessor()); 083 } 084 085 Iterator j = _nullHandlerContributions.iterator(); 086 087 while (j.hasNext()) 088 { 089 NullHandlerContribution h = (NullHandlerContribution) j.next(); 090 091 OgnlRuntime.setNullHandler(h.getSubjectClass(), h.getHandler()); 092 } 093 094 _defaultContext = Ognl.createDefaultContext(null, _ognlResolver, _typeConverter); 095 096 OgnlRuntime.setCompiler(new HiveMindExpressionCompiler(_classFactory)); 097 098 _contextPool = new GenericObjectPool(new PoolableOgnlContextFactory(_ognlResolver, _typeConverter)); 099 100 _contextPool.setMaxActive(-1); 101 _contextPool.setMaxIdle(-1); 102 _contextPool.setMinEvictableIdleTimeMillis(POOL_MIN_IDLE_TIME); 103 _contextPool.setTimeBetweenEvictionRunsMillis(POOL_SLEEP_TIME); 104 } 105 106 public Object read(Object target, String expression) 107 { 108 Node node = (Node)_expressionCache.getCompiledExpression(target, expression); 109 110 if (node.getAccessor() != null) 111 return read(target, node.getAccessor()); 112 113 return readCompiled(target, node); 114 } 115 116 public Object readCompiled(Object target, Object expression) 117 { 118 OgnlContext context = null; 119 try 120 { 121 context = (OgnlContext)_contextPool.borrowObject(); 122 context.setRoot(target); 123 124 return Ognl.getValue(expression, context, target); 125 } 126 catch (Exception ex) 127 { 128 throw new ApplicationRuntimeException(ImplMessages.unableToReadExpression(ImplMessages 129 .parsedExpression(), target, ex), target, null, ex); 130 } finally { 131 try { if (context != null) _contextPool.returnObject(context); } catch (Exception e) {} 132 } 133 } 134 135 public Object read(Object target, ExpressionAccessor expression) 136 { 137 OgnlContext context = null; 138 try 139 { 140 context = (OgnlContext)_contextPool.borrowObject(); 141 142 return expression.get(context, target); 143 } 144 catch (Exception ex) 145 { 146 throw new ApplicationRuntimeException(ImplMessages.unableToReadExpression(ImplMessages 147 .parsedExpression(), target, ex), target, null, ex); 148 } finally { 149 try { if (context != null) _contextPool.returnObject(context); } catch (Exception e) {} 150 } 151 } 152 153 public OgnlContext createContext(Object target) 154 { 155 OgnlContext result = (OgnlContext)Ognl.createDefaultContext(target, _ognlResolver); 156 157 if (_typeConverter != null) 158 Ognl.setTypeConverter(result, _typeConverter); 159 160 return result; 161 } 162 163 public void write(Object target, String expression, Object value) 164 { 165 writeCompiled(target, _expressionCache.getCompiledExpression(target, expression), value); 166 } 167 168 public void write(Object target, ExpressionAccessor expression, Object value) 169 { 170 OgnlContext context = null; 171 try 172 { 173 context = (OgnlContext)_contextPool.borrowObject(); 174 175 expression.set(context, target, value); 176 } 177 catch (Exception ex) 178 { 179 throw new ApplicationRuntimeException(ImplMessages.unableToWriteExpression(ImplMessages 180 .parsedExpression(), target, value, ex), target, null, ex); 181 } finally { 182 try { if (context != null) _contextPool.returnObject(context); } catch (Exception e) {} 183 } 184 } 185 186 public void writeCompiled(Object target, Object expression, Object value) 187 { 188 OgnlContext context = null; 189 try 190 { 191 context = (OgnlContext)_contextPool.borrowObject(); 192 193 Ognl.setValue(expression, context, target, value); 194 } 195 catch (Exception ex) 196 { 197 throw new ApplicationRuntimeException(ImplMessages.unableToWriteExpression(ImplMessages 198 .parsedExpression(), target, value, ex), target, null, ex); 199 } finally { 200 try { if (context != null) _contextPool.returnObject(context); } catch (Exception e) {} 201 } 202 } 203 204 public boolean isConstant(Object target, String expression) 205 { 206 Object compiled = _expressionCache.getCompiledExpression(target, expression); 207 208 try 209 { 210 return Ognl.isConstant(compiled, _defaultContext); 211 } 212 catch (Exception ex) 213 { 214 throw new ApplicationRuntimeException(ImplMessages.isConstantExpressionError( 215 expression, 216 ex), ex); 217 } 218 } 219 220 public boolean isConstant(String expression) 221 { 222 Object compiled = _expressionCache.getCompiledExpression(expression); 223 224 try 225 { 226 return Ognl.isConstant(compiled, _defaultContext); 227 } 228 catch (Exception ex) 229 { 230 throw new ApplicationRuntimeException(ImplMessages.isConstantExpressionError( 231 expression, 232 ex), ex); 233 } 234 } 235 236 public void registryDidShutdown() 237 { 238 try 239 { 240 _contextPool.clear(); 241 _contextPool.close(); 242 243 OgnlRuntime.clearCache(); 244 Introspector.flushCaches(); 245 246 } catch (Exception et) { 247 // ignore 248 } 249 } 250 251 public void setExpressionCache(ExpressionCache expressionCache) 252 { 253 _expressionCache = expressionCache; 254 } 255 256 public void setContributions(List contributions) 257 { 258 _contributions = contributions; 259 } 260 261 public void setNullHandlerContributions(List nullHandlerContributions) 262 { 263 _nullHandlerContributions = nullHandlerContributions; 264 } 265 266 public void setClassFactory(ClassFactory classFactory) 267 { 268 _classFactory = classFactory; 269 } 270 }