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 edu.emory.mathcs.backport.java.util.concurrent.locks.ReentrantLock;
018    import ognl.ClassCacheInspector;
019    import ognl.Node;
020    import ognl.Ognl;
021    import ognl.OgnlRuntime;
022    import org.apache.hivemind.ApplicationRuntimeException;
023    import org.apache.tapestry.AbstractComponent;
024    import org.apache.tapestry.event.ReportStatusEvent;
025    import org.apache.tapestry.event.ReportStatusListener;
026    import org.apache.tapestry.event.ResetEventListener;
027    import org.apache.tapestry.services.ExpressionCache;
028    import org.apache.tapestry.services.ExpressionEvaluator;
029    
030    import java.beans.Introspector;
031    import java.util.HashMap;
032    import java.util.Map;
033    import java.util.WeakHashMap;
034    
035    /**
036     * @author Howard M. Lewis Ship
037     * @since 4.0
038     */
039    public class ExpressionCacheImpl implements ExpressionCache, ResetEventListener, ReportStatusListener, ClassCacheInspector {
040    
041        private final ReentrantLock _lock = new ReentrantLock();
042        
043        private String _serviceId;
044    
045        private Map _cache = new WeakHashMap();
046        
047        private Map _objectCache = new WeakHashMap();
048        
049        private ExpressionEvaluator _evaluator;
050    
051        private final boolean _cachingDisabled = Boolean.getBoolean("org.apache.tapestry.disable-caching");
052    
053        public void initializeService()
054        {
055            if (_cachingDisabled)
056            {
057                OgnlRuntime.setClassCacheInspector(this);
058            }
059        }
060    
061        public void resetEventDidOccur()
062        {
063            try {
064                
065                _lock.lock();
066                
067                _cache.clear();
068                _objectCache.clear();
069    
070                Introspector.flushCaches();
071    
072            } finally {
073                
074                _lock.unlock();
075            }
076        }
077    
078        public boolean shouldCache(Class type)
079        {
080            if (!_cachingDisabled || type == null
081                || AbstractComponent.class.isAssignableFrom(type))
082                return false;
083    
084            return true;
085        }
086    
087        public void reportStatus(ReportStatusEvent event)
088        {
089            event.title(_serviceId);
090    
091            event.property("cached expression count", _cache.size());
092            event.collection("cached expressions", _cache.keySet());
093            
094            event.property("cached object expression count", _objectCache.size());
095        }
096        
097        public Object getCompiledExpression(Object target, String expression)
098        {
099            try {   
100                
101                _lock.lock();
102                
103                Map cached = (Map)_objectCache.get(target.getClass());
104                
105                if (cached == null)
106                {
107                    cached = new HashMap();
108                    _objectCache.put(target.getClass(), cached);
109                }
110                
111                Node result = (Node)cached.get(expression);
112                
113                if (result == null || result.getAccessor() == null)
114                {
115                    result = parse(target, expression);
116                    cached.put(expression, result);
117                }
118                
119                return result;
120                
121            } finally {
122    
123                _lock.unlock();
124            }
125        }
126        
127        public Object getCompiledExpression(String expression)
128        {
129            try {
130                
131                _lock.lock();
132                
133                Object result = _cache.get(expression);
134    
135                if (result == null)
136                {
137                    result = parse(expression);
138                    _cache.put(expression, result);
139                }
140                
141                return result;
142            } finally {
143    
144                _lock.unlock();
145            }
146        }
147    
148        private Node parse(Object target, String expression)
149        {
150            try
151            {
152                return Ognl.compileExpression(_evaluator.createContext(target), target, expression);
153            }
154            catch (Exception ex)
155            {
156                throw new ApplicationRuntimeException(ImplMessages.unableToParseExpression(expression,ex), ex);
157            }
158        }
159        
160        private Object parse(String expression)
161        {
162            try
163            {
164                return Ognl.parseExpression(expression);
165            }
166            catch (Exception ex)
167            {
168                throw new ApplicationRuntimeException(ImplMessages.unableToParseExpression(expression, ex), ex);
169            }
170        }
171    
172        public void setServiceId(String serviceId)
173        {
174            _serviceId = serviceId;
175        }
176        
177        public void setEvaluator(ExpressionEvaluator evaluator)
178        {
179            _evaluator = evaluator;
180        }
181        
182    }