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 }