Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: com/presumo/jms/selector/Parser.java


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;
28  import javax.jms.InvalidSelectorException;
29  import javax.jms.Message;
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 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 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("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 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(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 filter)
145     throws InvalidSelectorException
146   {
147   
148     JmsOperand retval = null;
149     obtainLock();
150     try {
151       retval = SqlJmsParser.getFilter(filter);
152     } catch (ParseException e) {
153       throw new InvalidSelectorException(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 unparse(JmsOperand root) 
167   {  
168     obtainLock();
169  
170     String 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();
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();
219 
220     LinkedList noRepeats = new LinkedList();
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 }