KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > caucho > doc > javadoc > Store


1 /*
2  * Copyright (c) 1998-2003 Caucho Technology -- all rights reserved
3  *
4  * Caucho Technology permits redistribution, modification and use
5  * of this file in source and binary form ("the Software") under the
6  * Caucho Developer Source License ("the License"). The following
7  * conditions must be met:
8  *
9  * 1. Each copy or derived work of the Software must preserve the copyright
10  * notice and this notice unmodified.
11  *
12  * 2. Redistributions of the Software in source or binary form must include
13  * an unmodified copy of the License, normally in a plain ASCII text
14  *
15  * 3. The names "Resin" or "Caucho" are trademarks of Caucho Technology and
16  * may not be used to endorse products derived from this software.
17  * "Resin" or "Caucho" may not appear in the names of products derived
18  * from this software.
19  *
20  * This Software is provided "AS IS," without a warranty of any kind.
21  * ALL EXPRESS OR IMPLIED REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
22  * IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
23  * OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED.
24  *
25  * CAUCHO TECHNOLOGY AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES
26  * SUFFERED BY LICENSEE OR ANY THIRD PARTY AS A RESULT OF USING OR
27  * DISTRIBUTING SOFTWARE. IN NO EVENT WILL CAUCHO OR ITS LICENSORS BE LIABLE
28  * FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
29  * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
30  * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
31  * INABILITY TO USE SOFTWARE, EVEN IF HE HAS BEEN ADVISED OF THE POSSIBILITY
32  * OF SUCH DAMAGES.
33  *
34  * @author Sam
35  */

36
37 package com.caucho.doc.javadoc;
38
39 import com.caucho.config.ConfigException;
40 import com.caucho.config.types.Period;
41 import com.caucho.loader.Environment;
42 import com.caucho.log.Log;
43 import com.caucho.server.webapp.Application;
44 import com.caucho.server.webapp.PathMapping;
45 import com.caucho.util.CharBuffer;
46 import com.caucho.util.Crc64;
47 import com.caucho.util.L10N;
48 import com.caucho.vfs.Path;
49 import com.caucho.vfs.ReadStream;
50 import com.caucho.vfs.Vfs;
51 import com.caucho.vfs.WriteStream;
52
53 import java.io.IOException JavaDoc;
54
55 import java.sql.Connection JavaDoc;
56 import java.sql.ResultSet JavaDoc;
57 import java.sql.SQLException JavaDoc;
58 import java.sql.Statement JavaDoc;
59
60 import java.util.Collection JavaDoc;
61 import java.util.Iterator JavaDoc;
62 import java.util.LinkedHashMap JavaDoc;
63 import java.util.LinkedList JavaDoc;
64 import java.util.logging.Level JavaDoc;
65 import java.util.logging.Logger JavaDoc;
66
67 import javax.naming.Context JavaDoc;
68 import javax.naming.InitialContext JavaDoc;
69 import javax.naming.NamingException JavaDoc;
70
71 import javax.sql.DataSource JavaDoc;
72
73 /**
74  * A store for javadoc information.
75  */

76 public class Store {
77   static protected final Logger JavaDoc log = Log.open(Store.class);
78   static final L10N L = new L10N(Store.class);
79
80   private final static String JavaDoc STORE_JNDINAME = "resin-javadoc/store";
81
82   private String JavaDoc _dataSource;
83   private LinkedHashMap JavaDoc<String JavaDoc,Api> _api = new LinkedHashMap JavaDoc<String JavaDoc,Api>();
84
85   private Path _timestampFile;
86   private String JavaDoc _tableNameFile = "javadoc_file";
87   private String JavaDoc _tableNameItem = "javadoc_item";
88   private long _httpCachePeriod;
89
90   private String JavaDoc _startPage = "overview.jsp";
91   private boolean _showAddHelp;
92
93   private long _crc64 = 0;
94   private DataSource JavaDoc _pool;
95
96   private Exception JavaDoc _initError;
97
98   /**
99    * A convenience method to do the JNDI lookup
100    */

101   static public Store getInstance()
102     throws NamingException JavaDoc
103   {
104     Context JavaDoc env = (Context JavaDoc) new InitialContext JavaDoc().lookup("java:comp/env");
105
106     Store store = (Store) env.lookup(STORE_JNDINAME);
107
108     if (store == null)
109       throw new NamingException JavaDoc(L.l("`{0}' is an unknown Store",STORE_JNDINAME));
110     return store;
111   }
112
113   /**
114    * The data-source indicates the database to use.
115    */

116   public void setDataSource(String JavaDoc dataSource)
117   {
118     _dataSource = dataSource;
119   }
120
121   /**
122    * A timestamp file is used to determine if the database needs to be
123    * recreated from the javadoc, default "WEB-INF/timestamp".
124    */

125   public void setTimestampFile(Path timestampFile)
126   {
127     _timestampFile = timestampFile;
128   }
129
130   /**
131    * An api is a javadoc generated set of html files.
132    */

133   public void addApi(Api api)
134     throws ConfigException
135   {
136     if (_api.get(api.getId()) != null)
137       throw new ConfigException(L.l("id `{0}' already used, id must be unique",api.getId()));
138     _api.put(api.getId(),api);
139   }
140
141   /**
142    * An api is a javadoc generated set of html files.
143    */

144   public Api getApi(String JavaDoc id)
145   {
146     return _api.get(id);
147   }
148
149   /**
150    * An api is a javadoc generated set of html files.
151    */

152   public Collection JavaDoc<Api> getAllApi()
153   {
154     return _api.values();
155   }
156
157   /**
158    * The table name for storing information about javadoc items, the default is
159    * "javadoc_item".
160    */

161   public void setTableNameItem(String JavaDoc tableNameItem)
162   {
163     _tableNameItem = tableNameItem;
164   }
165   
166   /**
167    * The table name for storing information about javadoc items.
168    */

169   public String JavaDoc getTableNameItem()
170   {
171     return _tableNameItem;
172   }
173   
174   /**
175    * The table name for storing information about javadoc files, the default is
176    * "javadoc_file".
177    */

178   public void setTableNameFile(String JavaDoc tableNameFile)
179   {
180     _tableNameFile = tableNameFile;
181   }
182   
183   /**
184    * The table name for storing information about javadoc files.
185    */

186   public String JavaDoc getTableNameFile()
187   {
188     return _tableNameFile;
189   }
190
191   /**
192    * The time period to indicate to Resin's cache and to browsers
193    * that the responses (including search results) should be cached.
194    * If Resin's http cache is enabled, then the cached
195    * result will be used for subsequent searches for the same thing, even
196    * by a different user. This is an effective way to avoid hitting the
197    * database for every search.
198    *
199    * examples: 10m, 10h, 10D,
200    * -1 disables (not a good idea, but useful during development).
201    */

202   public void setHttpCachePeriod(Period httpCachePeriod)
203   {
204     _httpCachePeriod = httpCachePeriod.getPeriod();
205   }
206   
207   /**
208    * The time period for cache expiry, in ms.
209    */

210   public long getHttpCachePeriod()
211   {
212     return _httpCachePeriod;
213   }
214   
215   /**
216    * The page to show in the class window for the first request, default
217    * "overview.jsp".
218    */

219   public void setStartPage(String JavaDoc startPage)
220   {
221     _startPage = startPage;
222   }
223   
224   /**
225    * The page to show in the class window for the first request.
226    */

227   public String JavaDoc getStartPage()
228   {
229     return _startPage;
230   }
231   
232   /**
233    * True/false show a help message about adding more api's.
234    */

235   public void setShowAddHelp(boolean showAddHelp)
236   {
237     _showAddHelp = showAddHelp;
238   }
239   
240   /**
241    * True/false show a help message about adding more api's.
242    */

243   public boolean getShowAddHelp()
244   {
245     return _showAddHelp;
246   }
247   
248   public void init()
249   {
250     try {
251       if (_timestampFile == null)
252         _timestampFile = Vfs.lookup("WEB-INF/timestamp");
253
254       try {
255         Context JavaDoc env = (Context JavaDoc) new InitialContext JavaDoc().lookup("java:comp/env");
256
257         _pool = (DataSource JavaDoc) env.lookup(_dataSource);
258
259         if (_pool == null)
260           throw new ConfigException(L.l("`{0}' is an unknown DataSource, database has not been configured or is not configured properly.",_dataSource));
261       } catch (NamingException JavaDoc e) {
262         throw new ConfigException(e);
263       }
264
265       // update database if needed
266

267       for (Iterator JavaDoc<Api> i = _api.values().iterator(); i.hasNext(); ) {
268         Api api = i.next();
269         _crc64 = api.generateCrc64(_crc64);
270       }
271       _crc64 = Crc64.generate(_crc64,_dataSource);
272       _crc64 = Crc64.generate(_crc64,_timestampFile.toString());
273       _crc64 = Crc64.generate(_crc64,_tableNameFile);
274       _crc64 = Crc64.generate(_crc64,_tableNameItem);
275
276       try {
277         if (isNeedUpdate())
278           createFromIndex();
279       } catch (Exception JavaDoc ex) {
280         throw new ConfigException(ex);
281       }
282
283       // add path-mappings to map local files with absolute paths
284

285       Application app = Application.getLocal();
286
287       CharBuffer cb = CharBuffer.allocate();
288
289       for (Iterator JavaDoc<Api> i = _api.values().iterator(); i.hasNext(); ) {
290         Api api = i.next();
291
292         if (api.isLocalAbsolute()) {
293           cb.clear();
294           cb.append("/");
295           cb.append(api.getId());
296           cb.append("/*");
297           String JavaDoc urlPattern = cb.toString();
298
299           PathMapping pm = new PathMapping();
300           pm.setUrlPattern(urlPattern);
301           pm.setRealPath(api.getLocation());
302           try {
303             app.addPathMapping(pm);
304           } catch (Exception JavaDoc ex) {
305             throw new ConfigException(ex);
306           }
307         }
308       }
309
310       cb.free();
311
312
313       // add dependencies to the Environment so that if a local api
314
// is regenerated the web-app is restarted
315

316       for (Iterator JavaDoc<Api> i = _api.values().iterator(); i.hasNext(); ) {
317         Api api = i.next();
318         if (api.isLocal()) {
319           Path index = api.getLocationPath().lookup("index.html");
320           Environment.addDependency(index);
321         }
322       }
323     } catch (Exception JavaDoc ex) {
324       log.log(Level.FINE,"resin-javadoc init error",ex);
325       _initError = ex;
326     }
327   }
328
329   public Exception JavaDoc getInitError()
330   {
331     return _initError;
332   }
333
334   /**
335    * True if the timestamp file indicates that the database needs updating to
336    * match the index file(s).
337    */

338   public boolean isNeedUpdate()
339   {
340     try {
341       ReadStream rs = _timestampFile.openRead();
342       String JavaDoc lms = rs.readLine();
343       long crc = Long.parseLong(lms);
344       lms = rs.readLine();
345       long tsfiles = Long.parseLong(lms);
346       rs.close();
347
348       if (crc != _crc64) {
349         log.info(L.l("javadoc index needs update - config has changed"));
350         if (log.isLoggable(Level.FINE))
351           log.finer(L.l("timestamp file {0} lastmodified {1}",_timestampFile,new Long JavaDoc(crc)));
352       }
353       else if (tsfiles != getTimestampForLocalFiles()) {
354         log.info(L.l("javadoc index needs update - a local api has changed"));
355       }
356       else {
357         return false;
358       }
359     } catch (Exception JavaDoc ex) {
360       log.info(L.l("javadoc index needs update - timestamp file could not be read"));
361       if (log.isLoggable(Level.FINE))
362         log.finer(L.l("timestamp file {0} {1}",_timestampFile,ex));
363     }
364
365     return true;
366   }
367
368   /**
369    * Clear the database tables (if they exist) and create new
370    * contents based on the passed javadoc index file(s).
371    */

372   public void createFromIndex()
373     throws SQLException JavaDoc, IOException JavaDoc
374   {
375     long st = System.currentTimeMillis();
376     int cnt = 0;
377
378     log.info(L.l("creating javadoc index db entries"));
379     Connection JavaDoc conn = null;
380     try {
381       conn = _pool.getConnection();
382
383       StoreWriter sw = new StoreWriter(this);
384       sw.clear(conn);
385       for (Iterator JavaDoc<Api> i = _api.values().iterator(); i.hasNext(); ) {
386         Api api = i.next();
387         cnt += sw.add(conn,api);
388       }
389     } finally {
390       try {
391         if (conn != null)
392           conn.close();
393       } catch (SQLException JavaDoc e) {
394         log.warning(L.l("conn.close() error",e));
395       }
396     }
397
398     updateTimestampFile();
399
400     long tm = System.currentTimeMillis() - st;
401     log.info(L.l("created {0} javadoc index entries in {1}sec", new Integer JavaDoc(cnt), new Double JavaDoc( (double) tm / 1000.0)));
402   }
403
404   private long getTimestampForLocalFiles()
405   {
406     long ts = 0L;
407     for (Iterator JavaDoc<Api> i = _api.values().iterator(); i.hasNext(); ) {
408       Api api = i.next();
409       if (api.isLocal()) {
410         ts += api.getLocationPath().lookup("index.html").getLastModified();
411       }
412     }
413
414     return ts;
415   }
416
417   private void updateTimestampFile()
418     throws IOException JavaDoc
419   {
420     _timestampFile.getParent().mkdirs();
421     WriteStream ws = _timestampFile.openWrite();
422     ws.println(_crc64);
423     ws.println(getTimestampForLocalFiles());
424     ws.close();
425   }
426
427   /**
428    * Look for a javadoc item. The string can begin with `package', `class',
429    * `method', `var', or `any'. If it begins with none of them, `any' is
430    * assumed. The rest of the string is used as the name to search for.
431    * If the name conatins the `*' character, the `*' will match any
432    * characters.
433    *
434    * @returns a list of the results, a list with size 0 for no results
435    */

436   public LinkedList JavaDoc<JavadocItem> query(String JavaDoc query, int offset, int limit)
437     throws SQLException JavaDoc
438   {
439     LinkedList JavaDoc<JavadocItem> results = new LinkedList JavaDoc<JavadocItem>();
440
441     try {
442       int type = -1;
443
444       int i = query.indexOf(' ');
445       if (i > -1) {
446         String JavaDoc t = query.substring(0,i);
447         if (t.equals("package"))
448           type = JavadocItem.PACKAGE;
449         else if (t.equals("class"))
450           type = JavadocItem.CLASS;
451         else if (t.equals("method"))
452           type = JavadocItem.METHOD;
453         else if (t.equals("var"))
454           type = JavadocItem.VARIABLE;
455         else if (t.equals("any"))
456           type = JavadocItem.ANY;
457
458         if (type > -1) {
459           while (i < query.length() && Character.isWhitespace(query.charAt(i)))
460             i++;
461           if (i >= query.length())
462             return results;
463           else
464             query = query.substring(i);
465         }
466       }
467
468
469       // handle the special case of ClassName.method
470
if (query.length() > 0 && Character.isUpperCase(query.charAt(0))) {
471         int di = query.indexOf('.');
472         if (di > -1 ) {
473           CharBuffer cb = CharBuffer.allocate();
474           cb.append('*');
475           cb.append(query);
476           cb.append('*');
477           query = cb.close();
478         }
479       }
480
481       if (type < 0)
482         type = JavadocItem.ANY;
483
484       Connection JavaDoc conn = null;
485       try {
486         conn = _pool.getConnection();
487
488         Statement JavaDoc stmt = conn.createStatement(ResultSet.TYPE_FORWARD_ONLY,ResultSet.CONCUR_READ_ONLY);
489         // construct the query or queries
490
boolean like = query.indexOf("*") > -1 ? true : false;
491         String JavaDoc nameToUse = (query.indexOf(".") > -1) ? "item.fullname" : "item.name";
492         String JavaDoc safequery = escapeSQL(query);
493
494         CharBuffer cb = CharBuffer.allocate();
495         cb.append("SELECT item.name,item.fullname,item.typ,item.anchor,item.description,file.api,file.path FROM ");
496           cb.append(getTableNameItem());
497           cb.append(" AS item, ");
498           cb.append(getTableNameFile());
499           cb.append(" AS file WHERE file.id = item.file_id");
500
501         if (type != JavadocItem.ANY) {
502           cb.append(" AND item.typ = ");
503           cb.append(type);
504         }
505         String JavaDoc q_select = cb.close();
506
507         if (like) {
508           cb = CharBuffer.allocate();
509           cb.append(q_select);
510           cb.append(" AND ");
511           cb.append(nameToUse);
512           cb.append(" LIKE '");
513           cb.append(safequery);
514           cb.append("'");
515
516           results = doQuery(safequery,stmt,cb,offset,limit,results);
517         }
518         else {
519           cb = CharBuffer.allocate();
520           cb.append(q_select);
521           cb.append(" AND ");
522           cb.append(nameToUse);
523           cb.append(" LIKE '");
524           cb.append(safequery);
525           cb.append("%'");
526           results = doQuery(safequery,stmt,cb,offset,limit,results);
527         }
528
529         // see if the first one is exact
530
if (results.size() > 0) {
531           JavadocItem item = results.getFirst();
532           if (item.getName().equals(query) || item.getFullName().equals(query)) {
533             item.setExact(true);
534           }
535         }
536         stmt.close();
537       } finally {
538         if (conn != null)
539           conn.close();
540       }
541     }
542     catch (SQLException JavaDoc ex) {
543       if (log.isLoggable(Level.CONFIG))
544         log.log(Level.CONFIG,L.l("error with query `{0}': {1}",query,ex.getMessage()));
545       throw ex;
546     }
547
548     return results;
549   }
550
551   private String JavaDoc escapeSQL(String JavaDoc q)
552   {
553     CharBuffer cb = CharBuffer.allocate();
554
555     for (int i = 0; i < q.length(); i++) {
556       char c = q.charAt(i);
557       switch (c) {
558         case '\'':
559           cb.append("\\'");
560           break;
561         case '\"':
562           cb.append("\\\"");
563           break;
564         case '*':
565           cb.append("%");
566           break;
567
568         case '%':
569           cb.append('\\');
570         default:
571           cb.append(c);
572       }
573     }
574
575     return cb.close();
576   }
577
578   private LinkedList JavaDoc<JavadocItem> doQuery(String JavaDoc query, Statement JavaDoc stmt, CharBuffer cb, int offset, int limit, LinkedList JavaDoc<JavadocItem> results)
579     throws SQLException JavaDoc
580   {
581     if (limit <= 0) {
582       return results;
583     }
584
585     cb.append(" ORDER BY if(item.name = '");
586     cb.append(query);
587     cb.append("' OR item.fullname = '");
588     cb.append(query);
589     cb.append("',0,1) ");
590     cb.append(",item.typ");
591     cb.append(", LENGTH(item.name)");
592     cb.append(" LIMIT ");
593     cb.append(limit);
594     if (offset > 0) {
595       cb.append(" OFFSET ");
596       cb.append(offset);
597     }
598
599
600     String JavaDoc q = cb.close();
601     log.finest(L.l("query is [{0}]",q));
602
603     ResultSet JavaDoc rs = stmt.executeQuery(q);
604     try {
605
606       while (rs.next()) {
607         results.add(new JavadocItem(
608               rs.getString(1), // name
609
rs.getString(2), // fullname
610
rs.getInt(3), // type
611
rs.getString(4), // anchor
612
rs.getString(5), // description
613
new JavadocFile(_api.get(rs.getString(6)), // api
614
rs.getString(7)) // path
615
));
616       }
617     } finally {
618       rs.close();
619     }
620
621     return results;
622
623   }
624
625 }
626
627
Popular Tags