KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > presumo > jms > selector > Parser


1 /**
2  * This file is part of Presumo.
3  *
4  * Presumo is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * Presumo is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with Presumo; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17  *
18  *
19  * Copyright 2001 Dan Greff
20  */

21 package com.presumo.jms.selector;
22
23 import com.presumo.util.log.Logger;
24 import com.presumo.util.log.LoggerFactory;
25 import com.presumo.jms.resources.Resources;
26
27 import java.util.LinkedList JavaDoc;
28 import javax.jms.InvalidSelectorException JavaDoc;
29 import javax.jms.Message JavaDoc;
30
31
32 /**
33  * Access point to the filter functionality for the rest of the JMS
34  * implementation. This class (along with every other class in this package)
35  * is on the critical path for message throughput performance. Thus it is,
36  * and must remain optimized.
37  *
38  * Parser is a singleton instead of a series of static methods because I read
39  * somewhere that instance methods are faster than static methods (never
40  * personally verified this). The class is thread safe, but to prevent the
41  * routing thread from having to obtain a lock for every evaluation, a thread
42  * can obtain the lock once and call several evaluations before releasing
43  * the lock.
44  *
45  * @author Dan Greff
46  */

47 public final class Parser {
48
49   private static Parser onlyInstance;
50   private Thread JavaDoc lockOwner = null;
51   
52     /////////////////////////////////////////////////////////////////////////
53
// Static Methods //
54
/////////////////////////////////////////////////////////////////////////
55
public static synchronized Parser getInstance()
56   {
57     if (onlyInstance == null)
58       onlyInstance = new Parser();
59     return onlyInstance;
60   }
61
62     /////////////////////////////////////////////////////////////////////////
63
// Constructors //
64
/////////////////////////////////////////////////////////////////////////
65
protected Parser()
66   {
67     super();
68   }
69   
70   /**
71    * Called to obtain the mutex lock for the current thread. ReleaseLock()
72    * must be called by the same thread.
73    */

74   public synchronized void obtainLock()
75   {
76     while (lockOwner != null) {
77       try {
78         this.wait();
79       } catch (InterruptedException JavaDoc ie) {}
80     }
81     
82     lockOwner = Thread.currentThread();
83   }
84   
85   /**
86    * Called to release the mutex lock held for this parser instance
87    */

88   public synchronized void releaseLock()
89   {
90     if (! lockOwner.equals(Thread.currentThread())) {
91       throw new IllegalStateException JavaDoc("Thread " + Thread.currentThread() +
92         "attempted to releaseLock held by "+ lockOwner);
93     }
94     lockOwner = null;
95     this.notifyAll();
96   }
97
98   /**
99    * The evaluate() method stores information about its last evaluation. This
100    * is combined with the fact that all logically equivelant JMS filters in
101    * a JVM are represented by the same instance of JmsOperand represents a
102    * signficant optimization. The filtering is used by ONLY the routing thread
103    * for evaluation, and for every message the thread has to deliver this method
104    * is called. Then for every filter the routing thread needs to check the
105    * message against the evaluate() method is called. If any sql expressions
106    * (or subexpressions!!) are duplicated in the filters, they will only be
107    * evaluated once.
108    */

109   public void resetEvaluateOnce()
110   {
111     JmsOperand.resetStoredEvals();
112   }
113  
114  
115   /**
116    * Must be used in conjunction with resetEvaluateOnce().
117    */

118   public boolean evaluate(JmsOperand root, Message JavaDoc msg)
119   {
120     logger.entry("evaluate", root.unParse(), msg);
121     if (root == null || msg == null)
122       return false;
123       
124     boolean retval = false;
125     try {
126       JmsOperand result = root.evaluateOnce(msg);
127       if (result == JmsBooleanLiteral.TRUE)
128         retval = true;
129     } catch (SelectorFalseException e) {
130       e.printStackTrace();
131     }
132
133     logger.exit("evaluate", new Boolean JavaDoc(retval));
134     return retval;
135   }
136   
137   /**
138    * Parse the filter and return a JmsOperand representing the root
139    * node of the filter's expression tree.
140    *
141    * @exception InvalidSelectorException
142    * If a syntax error was detected in the filter.
143    */

144   public JmsOperand parseFilter(String JavaDoc filter)
145     throws InvalidSelectorException JavaDoc
146   {
147   
148     JmsOperand retval = null;
149     obtainLock();
150     try {
151       retval = SqlJmsParser.getFilter(filter);
152     } catch (ParseException e) {
153       throw new InvalidSelectorException JavaDoc(e.toString());
154     } finally {
155       releaseLock();
156     }
157
158     return retval;
159   }
160
161
162   /**
163    * @return A jms-sql query representative of the expression
164    * tree <code>root</code>
165    */

166   public String JavaDoc unparse(JmsOperand root)
167   {
168     obtainLock();
169  
170     String JavaDoc retval = null;
171     try {
172
173       if (root == null)
174         retval = "false";
175       else
176         retval = root.unParse();
177         
178     } finally {
179       releaseLock();
180     }
181
182     return retval;
183   }
184
185   /**
186    * Logically and's together two trees.
187    * The returned expression tree must be deleted.
188    */

189   public JmsOperand andTogether(JmsOperand lv, JmsOperand rv)
190   {
191       
192     if (lv == null || rv == null)
193       throw new IllegalArgumentException JavaDoc();
194     
195     JmsOperand retval = null;
196     obtainLock();
197     try {
198       
199       lv.incrementAllRefCounts();
200       rv.incrementAllRefCounts();
201       retval = JmsBinaryAnd.getInstance(lv, rv);
202       
203     } finally {
204       releaseLock();
205     }
206     
207     return retval;
208   }
209     
210   /**
211    * Logically or's together the trees
212    * The returned expression tree must be deleted
213    */

214   public JmsOperand orTogether(JmsOperand [] roots)
215   {
216     
217     if (roots == null || roots.length == 0)
218       throw new IllegalArgumentException JavaDoc();
219
220     LinkedList JavaDoc noRepeats = new LinkedList JavaDoc();
221     
222     // Construct a list that has no repeats.
223
// Same running time as bubble sort :(
224
int i, j;
225     for (i=0; i < roots.length; i++) {
226       JmsOperand currentSearch = roots[i];
227       if (currentSearch == null) continue;
228       
229       // null out any duplicates
230
for (j= i+1; j < roots.length; j++) {
231         if (roots[j] == currentSearch)
232           roots[j] = null;
233       }
234       noRepeats.add(currentSearch);
235     }
236
237     // Now construct a new binary expression tree.
238
//
239
JmsOperand retval = null;
240     obtainLock();
241     try {
242       
243       if (noRepeats.size() == 0)
244         retval = null;
245       else if (noRepeats.size() == 1) {
246         JmsOperand single = (JmsOperand) noRepeats.removeFirst();
247         single.incrementAllRefCounts();
248         retval = single;
249       } else {
250         JmsOperand loperand = (JmsOperand) noRepeats.removeFirst();
251         JmsOperand roperand = (JmsOperand) noRepeats.removeFirst();
252         loperand.incrementAllRefCounts();
253         roperand.incrementAllRefCounts();
254         loperand = JmsBinaryOr.getInstance(loperand, roperand);
255
256         while (noRepeats.size() > 0) {
257           roperand = (JmsOperand) noRepeats.removeFirst();
258           roperand.incrementAllRefCounts();
259           loperand = JmsBinaryOr.getInstance(loperand, roperand);
260         }
261         retval = loperand;
262       }
263       
264     } finally {
265       releaseLock();
266     }
267     
268     return retval;
269   }
270
271   /**
272    * @return Because of optimizations made during parsing
273    * the several references will be left around unless
274    * this method is called. The internal logic should
275    * eventually be switched to use WeakReferences so
276    * that the parser does not have to rely on this method
277    * being called to prevent a memory leak.
278    */

279   public void delete(JmsOperand root)
280   {
281     if (root != null) {
282       obtainLock();
283       try {
284         
285         root.delete();
286         
287       } finally {
288         releaseLock();
289       }
290     }
291     
292   }
293
294   ////////////////////////////// Misc stuff ////////////////////////////////
295

296   private static Logger logger =
297     LoggerFactory.getLogger(Parser.class, Resources.getBundle());
298
299   ///////////////////////////////////////////////////////////////////////////
300

301 }
302
Popular Tags