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    }