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    }