KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > objectweb > cjdbc > controller > cache > parsing > ParsingCache


1 /**
2  * C-JDBC: Clustered JDBC.
3  * Copyright (C) 2002-2004 French National Institute For Research In Computer
4  * Science And Control (INRIA).
5  * Contact: c-jdbc@objectweb.org
6  *
7  * This library is free software; you can redistribute it and/or modify it
8  * under the terms of the GNU Lesser General Public License as published by the
9  * Free Software Foundation; either version 2.1 of the License, or any later
10  * version.
11  *
12  * This library is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
15  * for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public License
18  * along with this library; if not, write to the Free Software Foundation,
19  * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
20  *
21  * Initial developer(s): Emmanuel Cecchet.
22  * Contributor(s): ______________________________________.
23  */

24
25 package org.objectweb.cjdbc.controller.cache.parsing;
26
27 import java.sql.SQLException JavaDoc;
28 import java.util.Hashtable JavaDoc;
29
30 import org.objectweb.cjdbc.common.i18n.Translate;
31 import org.objectweb.cjdbc.common.log.Trace;
32 import org.objectweb.cjdbc.common.sql.AbstractRequest;
33 import org.objectweb.cjdbc.common.sql.ParsingGranularities;
34 import org.objectweb.cjdbc.common.sql.RequestType;
35 import org.objectweb.cjdbc.common.xml.DatabasesXmlTags;
36 import org.objectweb.cjdbc.controller.requestmanager.ParserThread;
37 import org.objectweb.cjdbc.controller.requestmanager.RequestManager;
38
39 /**
40  * This class implements a request parsing cache.
41  *
42  * @author <a HREF="mailto:Emmanuel.Cecchet@inria.fr">Emmanuel Cecchet </a>
43  * @version 1.0
44  */

45 public class ParsingCache
46 {
47   private static Trace logger = Trace.getLogger(ParsingCache.class.getName());
48   private Hashtable JavaDoc cache; // SQL -> parsed request
49
private Hashtable JavaDoc currentlyParsing; // SQL -> CurrentlyParsingEntry
50
private RequestManager requestManager;
51   private int granularity;
52   private int maxNbOfEntries;
53   private boolean backgroundParsing; // Default is parse when needed
54
private boolean caseSensitiveParsing; // Default is case insensitive
55

56   /**
57    * CurrentlyParsingEntry contains a (Request,ParserThread) which is an element
58    * of the currentlyParsing Hashtable.
59    *
60    * @author <a HREF="mailto:Emmanuel.Cecchet@inria.fr">Emmanuel Cecchet </a>
61    * @version 1.0
62    */

63   private class CurrentlyParsingEntry
64   {
65     private ParserThread parserThread;
66     private AbstractRequest request;
67
68     /**
69      * Constructor for CurrentlyParsingEntry.
70      *
71      * @param parserThread creating parser thread
72      * @param request request to parse
73      */

74     public CurrentlyParsingEntry(ParserThread parserThread,
75         AbstractRequest request)
76     {
77       this.parserThread = parserThread;
78       this.request = request;
79     }
80
81     /**
82      * Returns the parserThread.
83      *
84      * @return ParserThread
85      */

86     public ParserThread getParserThread()
87     {
88       return parserThread;
89     }
90
91     /**
92      * Returns the request.
93      *
94      * @return AbstractRequest
95      */

96     public AbstractRequest getRequest()
97     {
98       return request;
99     }
100
101   }
102
103   /**
104    * Constructor for ParsingCache.
105    *
106    * @param size maximum cache size in nb of entries
107    * @param backgroundParsing true if the parsing should be done in background
108    * by a ParserThread
109    */

110   public ParsingCache(int size, boolean backgroundParsing)
111   {
112     cache = new Hashtable JavaDoc(size == 0 ? 10000 : size);
113     currentlyParsing = new Hashtable JavaDoc();
114     if (size < 0)
115       throw new RuntimeException JavaDoc(Translate.get("cache.parsing.invalid.size",
116           size));
117     if (size == 0)
118       this.maxNbOfEntries = Integer.MAX_VALUE;
119     else
120       this.maxNbOfEntries = size;
121     this.backgroundParsing = backgroundParsing;
122     caseSensitiveParsing = false;
123   }
124
125   /**
126    * Returns the granularity value.
127    *
128    * @return Returns the granularity.
129    */

130   public int getGranularity()
131   {
132     return granularity;
133   }
134
135   /**
136    * Sets the granularity value.
137    *
138    * @param granularity The granularity to set.
139    */

140   public void setGranularity(int granularity)
141   {
142     this.granularity = granularity;
143   }
144
145   /**
146    * Returns the requestManager value.
147    *
148    * @return Returns the requestManager.
149    */

150   public RequestManager getRequestManager()
151   {
152     return requestManager;
153   }
154
155   /**
156    * Sets the requestManager value.
157    *
158    * @param requestManager The requestManager to set.
159    */

160   public void setRequestManager(RequestManager requestManager)
161   {
162     this.requestManager = requestManager;
163   }
164
165   /**
166    * If the same SQL query is found in the cache, the parsing is cloned into the
167    * given request. If backgroundParsing is set to true, then a ParserThread
168    * starts parsing the request in background else nothing is done on a cache
169    * miss.
170    *
171    * @param request the request you look for
172    */

173   public void getParsingFromCache(AbstractRequest request)
174   {
175     if (request.isParsed())
176       return;
177
178     String JavaDoc sql = request.getSqlSkeleton();
179     if (sql == null)
180       sql = request.getSQL();
181     AbstractRequest parsedRequest = (AbstractRequest) cache.get(sql);
182
183     if (parsedRequest != null)
184     { // Cache hit, clone the parsing
185
request.cloneParsing(parsedRequest);
186       return;
187     }
188     else if (backgroundParsing)
189     { // Cache miss, start parsing the request in background
190
synchronized (currentlyParsing)
191       {
192         if (!currentlyParsing.contains(sql))
193         { // Nobody else is trying to parse the same SQL query
194
ParserThread pt = new ParserThread(request, requestManager
195               .getDatabaseSchema(), granularity, caseSensitiveParsing);
196           currentlyParsing.put(sql, new CurrentlyParsingEntry(pt, request));
197         }
198       }
199     }
200   }
201
202   /**
203    * Method getParsingFromCacheAndParseIfMissing.
204    *
205    * @param request the request we look for
206    * @exception SQLException if an error occurs
207    */

208   public void getParsingFromCacheAndParseIfMissing(AbstractRequest request)
209       throws SQLException JavaDoc
210   {
211     if (request.isParsed())
212       return;
213
214     // Check cache
215
String JavaDoc instanciatedSQL = request.getSQL();
216     AbstractRequest parsedRequest = (AbstractRequest) cache
217         .get(instanciatedSQL);
218
219
220     try
221     {
222     
223     if (parsedRequest == null)
224     { // Cache miss
225
String JavaDoc sqlSkeleton = request.getSqlSkeleton();
226       String JavaDoc sql;
227       if (sqlSkeleton != null)
228       { // Missed with instanciated query, try with skeleton
229
sql = sqlSkeleton;
230         parsedRequest = (AbstractRequest) cache.get(sql);
231         if (parsedRequest != null)
232         { // Cache hit with skeleton
233
request.cloneParsing(parsedRequest);
234           return;
235         }
236       }
237       else
238         sql = instanciatedSQL;
239
240       // Full cache miss. Note that the underlying cache Hashtable is
241
// synchronized and we usually do not need to synchronize on it.
242
// As we will have to add a cache entry, check if the cache size is ok
243
// else remove the first entry of the hashtable.
244
while (cache.size() > maxNbOfEntries)
245       { // Remove first entry from Hashtable. We need to synchronize here to be
246
// sure that we are not trying to concurrently remove the first cache
247
// entry.
248
synchronized (cache)
249         {
250           try
251           {
252             cache.remove(cache.keys().nextElement());
253           }
254           catch (Exception JavaDoc ignore)
255           {
256             break;
257           }
258         }
259       }
260
261       
262       // Both skeleton and instanciated missed
263
if (backgroundParsing)
264       {
265         // Find the parsing thread and request (note that Hasthtable is
266
// synchronized)
267
CurrentlyParsingEntry cpe = (CurrentlyParsingEntry) currentlyParsing
268             .get(sql);
269         if (cpe != null)
270         {
271           ParserThread pt = cpe.getParserThread();
272           try
273           {
274             if (pt != null)
275             {
276               // Wait for completion
277
pt.join();
278               synchronized (currentlyParsing)
279               {
280                 currentlyParsing.remove(sql);
281               }
282
283               // Update cache
284
if ((granularity != ParsingGranularities.COLUMN_UNIQUE)
285                   || (sqlSkeleton == null))
286                 // No skeleton or no uniqueness criteria, add the query
287
cache.put(instanciatedSQL, cpe.getRequest());
288               else
289               { // We have a skeleton and COLUMN_UNIQUE parsing
290
if (request.getCacheAbility() != RequestType.UNIQUE_CACHEABLE)
291                   // It is NOT UNIQUE, add the skeleton
292
cache.put(sqlSkeleton, cpe.getRequest());
293                 else
294                   // It is UNIQUE, add the instanciated query
295
cache.put(instanciatedSQL, cpe.getRequest());
296               }
297             }
298           }
299           catch (InterruptedException JavaDoc failed)
300           {
301             throw new SQLException JavaDoc(Translate.get(
302                 "cache.parsing.failed.join.parser.thread", new String JavaDoc[]{
303                     "" + request.getId(), failed.getMessage()}));
304           }
305         }
306       }
307       // Parse it now because we didn't parse in background or
308
// backgroundParsing has failed for any obscure reason.
309
request.parse(requestManager.getDatabaseSchema(), granularity,
310           caseSensitiveParsing);
311
312       // Update cache
313
if ((sqlSkeleton != null)
314           && (granularity == ParsingGranularities.COLUMN_UNIQUE)
315           && (request.getCacheAbility() == RequestType.UNIQUE_CACHEABLE))
316         // If this is a unique request, we must put the instanciated query in
317
// the cache to retrieve the exact pk value.
318
cache.put(instanciatedSQL, request);
319       else
320         cache.put(sql, request);
321     }
322     else
323       // Cache hit
324
request.cloneParsing(parsedRequest);
325     
326     }
327     catch (OutOfMemoryError JavaDoc oome)
328     {
329       synchronized (cache)
330       {
331         cache.clear();
332       }
333       System.gc();
334       logger.warn(Translate.get("cache.memory.error.cache.flushed", this
335           .getClass()));
336     }
337   }
338
339   /**
340    * Returns the backgroundParsing.
341    *
342    * @return boolean
343    */

344   public boolean isBackgroundParsing()
345   {
346     return backgroundParsing;
347   }
348
349   /**
350    * Sets the background parsing. If true the request are parsed in background
351    * by a separate thread that is created for this purpose.
352    *
353    * @param backgroundParsing The backgroundParsing to set
354    */

355   public void setBackgroundParsing(boolean backgroundParsing)
356   {
357     this.backgroundParsing = backgroundParsing;
358   }
359
360   /**
361    * Sets the parsing case sensitivity
362    *
363    * @param isCaseSensitiveParsing true if parsing is case sensitive
364    */

365   public void setCaseSensitiveParsing(boolean isCaseSensitiveParsing)
366   {
367     this.caseSensitiveParsing = isCaseSensitiveParsing;
368   }
369
370   /**
371    * Returns the caseSensitiveParsin.
372    *
373    * @return boolean
374    */

375   public boolean isCaseSensitiveParsing()
376   {
377     return caseSensitiveParsing;
378   }
379
380   /**
381    * Get xml information about this ParsingCache
382    *
383    * @return <code>String</code> in xml formatted text
384    */

385   public String JavaDoc getXml()
386   {
387     return "<" + DatabasesXmlTags.ELT_ParsingCache + " "
388         + DatabasesXmlTags.ATT_backgroundParsing + "=\"" + backgroundParsing
389         + "\" " + DatabasesXmlTags.ATT_maxNbOfEntries + "=\"" + maxNbOfEntries
390         + "\"/>";
391   }
392
393 }
Popular Tags