KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > activemq > filter > MultiExpressionEvaluator


1 /**
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one or more
4  * contributor license agreements. See the NOTICE file distributed with
5  * this work for additional information regarding copyright ownership.
6  * The ASF licenses this file to You under the Apache License, Version 2.0
7  * (the "License"); you may not use this file except in compliance with
8  * the License. You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */

18 package org.apache.activemq.filter;
19
20 import java.util.ArrayList JavaDoc;
21 import java.util.Collection JavaDoc;
22 import java.util.HashMap JavaDoc;
23 import java.util.Iterator JavaDoc;
24
25 import javax.jms.JMSException JavaDoc;
26
27 /**
28  * A MultiExpressionEvaluator is used to evaluate multiple expressions in
29  * single method call.
30  * <p/>
31  * Multiple Expression/ExpressionListener pairs can be added to a MultiExpressionEvaluator object. When
32  * the MultiExpressionEvaluator object is evaluated, all the registed Expressions are evaluated and then the
33  * associated ExpressionListener is invoked to inform it of the evaluation result.
34  * <p/>
35  * By evaluating multiple expressions at one time, some optimizations can be made
36  * to reduce the number of computations normally required to evaluate all the expressions.
37  * <p/>
38  * When this class adds an Expression it wrapps each node in the Expression's AST with a
39  * CacheExpression object. Then each CacheExpression object (one for each node) is placed
40  * in the cachedExpressions map. The cachedExpressions map allows us to find the sub expressions
41  * that are common across two different expressions. When adding an Expression in, if a sub
42  * Expression of the Expression is allready in the cachedExpressions map, then instead of
43  * wrapping the sub expression in a new CacheExpression object, we reuse the CacheExpression allready
44  * int the map.
45  * <p/>
46  * To help illustrate what going on, lets try to give an exmample:
47  * If we denote the AST of a Expression as follows: [AST-Node-Type,Left-Node,Right-Node], then
48  * A expression like: "3*5+6" would result in "[*,3,[+,5,6]]"
49  * <p/>
50  * If the [*,3,[+,5,6]] expression is added to the MultiExpressionEvaluator, it would really
51  * be converted to: [c0,[*,3,[c1,[+,5,6]]]] where c0 and c1 represent the CacheExpression expression
52  * objects that cache the results of the * and the + operation. Constants and Property nodes are not
53  * cached.
54  * <p/>
55  * If later on we add the following expression [=,11,[+,5,6]] ("11=5+6") to the MultiExpressionEvaluator
56  * it would be converted to: [c2,[=,11,[c1,[+,5,6]]]], where c2 is a new CacheExpression object
57  * but c1 is the same CacheExpression used in the previous expression.
58  * <p/>
59  * When the expressions are evaluated, the c1 CacheExpression object will only evaluate the
60  * [+,5,6] expression once and cache the resulting value. Hence evauating the second expression
61  * costs less because that [+,5,6] is not done 2 times.
62  * <p/>
63  * Problems:
64  * - cacheing the values introduces overhead. It may be possible to be smarter about WHICH
65  * nodes in the AST are cached and which are not.
66  * - Current implementation is not thread safe. This is because you need a way to invalidate
67  * all the cached values so that the next evaluation re-evaluates the nodes. By going single
68  * threaded, chache invalidation is done quickly by incrementing a 'view' counter.
69  * When a CacheExpressionnotices it's last cached value was generated in an old 'view',
70  * it invalidates its cached value.
71  *
72  * @version $Revision: 1.2 $ $Date: 2005/08/27 03:52:36 $
73  */

74 public class MultiExpressionEvaluator {
75
76     HashMap JavaDoc rootExpressions = new HashMap JavaDoc();
77     HashMap JavaDoc cachedExpressions = new HashMap JavaDoc();
78
79     int view = 0;
80
81     /**
82      * A UnaryExpression that caches the result of the
83      * nested expression. The cached value is valid
84      * if the CacheExpression.cview==MultiExpressionEvaluator.view
85      */

86     public class CacheExpression extends UnaryExpression {
87         short refCount = 0;
88         int cview = view - 1;
89         Object JavaDoc cachedValue;
90         int cachedHashCode;
91
92         public CacheExpression(Expression realExpression) {
93             super(realExpression);
94             cachedHashCode = realExpression.hashCode();
95         }
96
97         /**
98          * @see org.apache.activemq.filter.Expression#evaluate(MessageEvaluationContext)
99          */

100         public Object JavaDoc evaluate(MessageEvaluationContext message) throws JMSException JavaDoc {
101             if (view == cview) {
102                 return cachedValue;
103             }
104             cachedValue = right.evaluate(message);
105             cview = view;
106             return cachedValue;
107         }
108
109         public int hashCode() {
110             return cachedHashCode;
111         }
112
113         public boolean equals(Object JavaDoc o) {
114             if( o == null )
115                 return false;
116             return ((CacheExpression) o).right.equals(right);
117         }
118
119         public String JavaDoc getExpressionSymbol() {
120             return null;
121         }
122
123         public String JavaDoc toString() {
124             return right.toString();
125         }
126
127     }
128
129     /**
130      * Multiple listeners my be interested in the results
131      * of a single expression.
132      */

133     static class ExpressionListenerSet {
134         Expression expression;
135         ArrayList JavaDoc listeners = new ArrayList JavaDoc();
136     }
137
138     /**
139      * Objects that are interested in the results of an expression
140      * should implement this interface.
141      */

142     static interface ExpressionListener {
143         public void evaluateResultEvent(Expression selector, MessageEvaluationContext message, Object JavaDoc result);
144     }
145
146     /**
147      * Adds an ExpressionListener to a given expression. When evaluate is
148      * called, the ExpressionListener will be provided the results of the
149      * Expression applied to the evaluated message.
150      */

151     public void addExpressionListner(Expression selector, ExpressionListener c) {
152         ExpressionListenerSet data = (ExpressionListenerSet) rootExpressions.get(selector.toString());
153         if (data == null) {
154             data = new ExpressionListenerSet();
155             data.expression = addToCache(selector);
156             rootExpressions.put(selector.toString(), data);
157         }
158         data.listeners.add(c);
159     }
160
161     /**
162      * Removes an ExpressionListener from receiving the results of
163      * a given evaluation.
164      */

165     public boolean removeEventListner(String JavaDoc selector, ExpressionListener c) {
166         String JavaDoc expKey = selector;
167         ExpressionListenerSet d = (ExpressionListenerSet) rootExpressions.get(expKey);
168         if (d == null) // that selector had not been added.
169
{
170             return false;
171         }
172         if (!d.listeners.remove(c)) // that selector did not have that listner..
173
{
174             return false;
175         }
176
177         // If there are no more listners for this expression....
178
if (d.listeners.size() == 0) {
179             // Uncache it...
180
removeFromCache((CacheExpression) d.expression);
181             rootExpressions.remove(expKey);
182         }
183         return true;
184     }
185
186     /**
187      * Finds the CacheExpression that has been associated
188      * with an expression. If it is the first time the
189      * Expression is being added to the Cache, a new
190      * CacheExpression is created and associated with
191      * the expression.
192      * <p/>
193      * This method updates the reference counters on the
194      * CacheExpression to know when it is no longer needed.
195      */

196     private CacheExpression addToCache(Expression expr) {
197
198         CacheExpression n = (CacheExpression) cachedExpressions.get(expr);
199         if (n == null) {
200             n = new CacheExpression(expr);
201             cachedExpressions.put(expr, n);
202             if (expr instanceof UnaryExpression) {
203
204                 // Cache the sub expressions too
205
UnaryExpression un = (UnaryExpression) expr;
206                 un.setRight(addToCache(un.getRight()));
207
208             }
209             else if (expr instanceof BinaryExpression) {
210
211                 // Cache the sub expressions too.
212
BinaryExpression bn = (BinaryExpression) expr;
213                 bn.setRight(addToCache(bn.getRight()));
214                 bn.setLeft(addToCache(bn.getLeft()));
215
216             }
217         }
218         n.refCount++;
219         return n;
220     }
221
222     /**
223      * Removes an expression from the cache. Updates the
224      * reference counters on the CacheExpression object. When
225      * the refernce counter goes to zero, the entry
226      * int the Expression to CacheExpression map is removed.
227      *
228      * @param cn
229      */

230     private void removeFromCache(CacheExpression cn) {
231         cn.refCount--;
232         Expression realExpr = cn.getRight();
233         if (cn.refCount == 0) {
234             cachedExpressions.remove(realExpr);
235         }
236         if (realExpr instanceof UnaryExpression) {
237             UnaryExpression un = (UnaryExpression) realExpr;
238             removeFromCache((CacheExpression) un.getRight());
239         }
240         if (realExpr instanceof BinaryExpression) {
241             BinaryExpression bn = (BinaryExpression) realExpr;
242             removeFromCache((CacheExpression) bn.getRight());
243         }
244     }
245
246     /**
247      * Evaluates the message against all the Expressions added to
248      * this object. The added ExpressionListeners are notified
249      * of the result of the evaluation.
250      *
251      * @param message
252      */

253     public void evaluate(MessageEvaluationContext message) {
254         Collection JavaDoc expressionListeners = rootExpressions.values();
255         for (Iterator JavaDoc iter = expressionListeners.iterator(); iter.hasNext();) {
256             ExpressionListenerSet els = (ExpressionListenerSet) iter.next();
257             try {
258                 Object JavaDoc result = els.expression.evaluate(message);
259                 for (Iterator JavaDoc iterator = els.listeners.iterator(); iterator.hasNext();) {
260                     ExpressionListener l = (ExpressionListener) iterator.next();
261                     l.evaluateResultEvent(els.expression, message, result);
262                 }
263             }
264             catch (Throwable JavaDoc e) {
265                 e.printStackTrace();
266             }
267         }
268     }
269 }
270
Popular Tags