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    }