KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > python > core > CachedJarsPackageManager


1 // Copyright (c) Corporation for National Research Initiatives
2
// Copyright 2000 Samuele Pedroni
3

4 package org.python.core;
5
6 import java.util.Hashtable JavaDoc;
7 import java.util.Vector JavaDoc;
8 import java.util.Enumeration JavaDoc;
9 import java.util.zip.ZipInputStream JavaDoc;
10 import java.util.zip.ZipEntry JavaDoc;
11 import java.io.*;
12 import java.net.URL JavaDoc;
13 import java.net.URLConnection JavaDoc;
14 //import java.net.URLDecoder;
15
import java.lang.reflect.Modifier JavaDoc;
16
17 /** Abstract package manager that gathers info about statically known classes
18  * from a set of jars. This info can be eventually cached.
19  * Off-the-shelf this class offers a local file-system based cache impl.
20  */

21 public abstract class CachedJarsPackageManager extends PackageManager {
22
23     /** Message log method - hook. This default impl does nothing.
24      * @param msg message text
25      */

26     protected void message(String JavaDoc msg) {
27     }
28     /** Warning log method - hook. This default impl does nothing.
29      * @param warn warning text
30      */

31     protected void warning(String JavaDoc warn) {
32     }
33     /** Comment log method - hook. This default impl does nothing.
34      * @param msg message text
35      */

36     protected void comment(String JavaDoc msg) {
37     }
38     /** Debug log method - hook. This default impl does nothing.
39      * @param msg message text
40      */

41     protected void debug(String JavaDoc msg) {
42     }
43
44     /** Filter class/pkg by name helper method - hook.
45      * The default impl. is used by {@link #addJarToPackages} in order
46      * to filter out classes whose name contains '$' (e.g. inner classes,...).
47      * Should be used or overriden by derived classes too.
48      * Also to be used in {@link #doDir}.
49      * @param name class/pkg name
50      * @param pkg if true, name refers to a pkg
51      * @return true if name must be filtered out
52      */

53     protected boolean filterByName(String JavaDoc name,boolean pkg) {
54         return name.indexOf('$') != -1;
55     }
56
57     /** Filter class by access perms helper method - hook.
58      * The default impl. is used by {@link #addJarToPackages} in order
59      * to filter out non-public classes.
60      * Should be used or overriden by derived classes too.
61      * Also to be used in {@link #doDir}.
62      * Access perms can be read with {@link #checkAccess}.
63      * @param name class name
64      * @param acc class access permissions as int
65      * @return true if name must be filtered out
66      */

67     protected boolean filterByAccess(String JavaDoc name,int acc) {
68         return (acc & Modifier.PUBLIC) != Modifier.PUBLIC;
69     }
70
71     private boolean indexModified;
72     private Hashtable JavaDoc jarfiles;
73
74     private static String JavaDoc vectorToString(Vector JavaDoc vec) {
75         int n = vec.size();
76         StringBuffer JavaDoc ret = new StringBuffer JavaDoc();
77         for(int i=0; i<n; i++) {
78             ret.append((String JavaDoc)vec.elementAt(i));
79             if (i<n-1) ret.append(",");
80         }
81         return ret.toString();
82     }
83
84     // Add a single class from zipFile to zipPackages
85
// Only add valid, public classes
86
private void addZipEntry(Hashtable JavaDoc zipPackages,
87                              ZipEntry JavaDoc entry,
88                              ZipInputStream JavaDoc zip) throws IOException
89     {
90         String JavaDoc name = entry.getName();
91         //System.err.println("entry: "+name);
92
if (!name.endsWith(".class")) return;
93
94         char sep = '/';
95         int breakPoint = name.lastIndexOf(sep);
96         if (breakPoint == -1) {
97             breakPoint = name.lastIndexOf('\\');
98             sep = '\\';
99         }
100
101         String JavaDoc packageName;
102         if (breakPoint == -1) {
103             packageName = "";
104         } else {
105             packageName = name.substring(0,breakPoint).replace(sep, '.');
106         }
107
108         String JavaDoc className = name.substring(breakPoint+1, name.length()-6);
109
110         if (filterByName(className,false)) return;
111
112         Vector JavaDoc[] vec = (Vector JavaDoc[])zipPackages.get(packageName);
113         if (vec == null) {
114             vec = new Vector JavaDoc[] { new Vector JavaDoc(), new Vector JavaDoc() };
115             zipPackages.put(packageName, vec);
116         }
117         int access = checkAccess(zip);
118         if ((access != -1) && !filterByAccess(name,access)) {
119             vec[0].addElement(className);
120         } else {
121             vec[1].addElement(className);
122         }
123     }
124
125     // Extract all of the packages in a single jarfile
126
private Hashtable JavaDoc getZipPackages(InputStream JavaDoc jarin) throws IOException {
127         Hashtable JavaDoc zipPackages = new Hashtable JavaDoc();
128
129         ZipInputStream JavaDoc zip=new ZipInputStream JavaDoc(jarin);
130
131         ZipEntry JavaDoc entry;
132         while ((entry = zip.getNextEntry()) != null) {
133             addZipEntry(zipPackages, entry, zip);
134             zip.closeEntry();
135         }
136
137         // Turn each vector into a comma-separated String
138
for (Enumeration JavaDoc e = zipPackages.keys() ; e.hasMoreElements() ;) {
139             Object JavaDoc key = e.nextElement();
140             Vector JavaDoc[] vec = (Vector JavaDoc[])zipPackages.get(key);
141             String JavaDoc classes = vectorToString(vec[0]);
142             if (vec[1].size() > 0) {
143                 classes += '@' + vectorToString(vec[1]);
144             }
145             zipPackages.put(key, classes);
146         }
147
148         return zipPackages;
149     }
150
151     /** Gathers classes info from jar specified by jarurl URL.
152      * Eventually just using previously cached info.
153      * Eventually updated info is not cached.
154      * Persistent cache storage access goes through
155      * inOpenCacheFile() and outCreateCacheFile().
156      */

157     public void addJarToPackages(java.net.URL JavaDoc jarurl) {
158         addJarToPackages(jarurl,null,false);
159     }
160
161     /** Gathers classes info from jar specified by jarurl URL.
162      * Eventually just using previously cached info.
163      * Eventually updated info is (re-)cached if param cache is true.
164      * Persistent cache storage access goes through
165      * inOpenCacheFile() and outCreateCacheFile().
166      */

167     public void addJarToPackages(URL JavaDoc jarurl,boolean cache) {
168         addJarToPackages(jarurl,null,cache);
169     }
170
171     /** Gathers classes info from jar specified by File jarfile.
172      * Eventually just using previously cached info.
173      * Eventually updated info is not cached.
174      * Persistent cache storage access goes through
175      * inOpenCacheFile() and outCreateCacheFile().
176      */

177     public void addJarToPackages(File jarfile) {
178         addJarToPackages(null,jarfile,false);
179     }
180
181     /** Gathers classes info from jar specified by File jarfile.
182      * Eventually just using previously cached info.
183      * Eventually updated info is (re-)cached if param cache is true.
184      * Persistent cache storage access goes through
185      * inOpenCacheFile() and outCreateCacheFile().
186      */

187     public void addJarToPackages(File jarfile,boolean cache) {
188         addJarToPackages(null,jarfile,cache);
189     }
190
191     private void addJarToPackages(URL JavaDoc jarurl,File jarfile,boolean cache) {
192         try {
193             boolean caching = jarfiles!=null;
194
195             URLConnection JavaDoc jarconn = null;
196             boolean localfile = true;
197
198             if (jarfile == null) {
199                 jarconn = jarurl.openConnection();
200                 // This is necessary because 'file:' url-connections
201
// return always 0 through getLastModified (bug?).
202
// And in order to handle localfiles (from urls too)
203
// uniformly.
204
if(jarconn.getURL().getProtocol().equals("file")) {
205                     // ??pending: need to use java2 URLDecoder.decode?
206
// but under 1.1 this is absent and should be simulated.
207
String JavaDoc jarfilename = jarurl.getFile();
208                     jarfilename = jarfilename.replace('/',File.separatorChar);
209                     jarfile = new File(jarfilename);
210                 }
211                 else localfile = false;
212             }
213
214             if (localfile && !jarfile.exists()) return;
215
216             Hashtable JavaDoc zipPackages = null;
217
218             long mtime = 0;
219             String JavaDoc jarcanon = null;
220             JarXEntry entry = null;
221             boolean brandNew = false;
222
223             if(caching) {
224
225                 if(localfile) {
226                     mtime = jarfile.lastModified();
227                     jarcanon = jarfile.getCanonicalPath();
228                 } else {
229                     mtime = jarconn.getLastModified();
230                     jarcanon = jarurl.toString();
231                 }
232
233                 entry = (JarXEntry)jarfiles.get(jarcanon);
234
235                 if ((entry == null || !(new File(entry.cachefile).exists())) && cache) {
236                     message("processing new jar, '"+
237                     jarcanon+"'");
238
239                     String JavaDoc jarname;
240                     if(localfile) {
241                         jarname = jarfile.getName();
242                     } else {
243                         jarname = jarurl.getFile();
244                         int slash = jarname.lastIndexOf('/');
245                         if (slash != -1)
246                         jarname=jarname.substring(slash+1);
247                     }
248                     jarname=jarname.substring(0,jarname.length()-4);
249
250                     entry = new JarXEntry(jarname);
251                     jarfiles.put(jarcanon, entry);
252
253                     brandNew = true;
254                 }
255
256
257                 if (mtime != 0 && entry != null && entry.mtime == mtime) {
258                     zipPackages = readCacheFile(entry, jarcanon);
259                 }
260
261             }
262
263             if (zipPackages == null) {
264                 caching = caching && cache;
265
266                 if(caching) {
267                     indexModified = true;
268                     if (entry.mtime != 0) {
269                         message("processing modified jar, '"+
270                         jarcanon+"'");
271                     }
272                     entry.mtime = mtime;
273                 }
274
275                 InputStream JavaDoc jarin;
276                 if (jarconn == null)
277                     jarin = new BufferedInputStream(
278                             new FileInputStream(jarfile));
279                 else
280                     jarin = jarconn.getInputStream();
281
282                 zipPackages = getZipPackages(jarin);
283
284                 if (caching)
285                     writeCacheFile(entry, jarcanon, zipPackages, brandNew);
286             }
287
288             addPackages(zipPackages, jarcanon);
289         } catch (IOException ioe) {
290             // silently skip any bad directories
291
warning("skipping bad jar, '" +
292                     (jarfile != null ?
293                           jarfile.toString() :
294                           jarurl.toString()) +
295                     "'");
296         }
297
298     }
299
300     private void addPackages(Hashtable JavaDoc zipPackages, String JavaDoc jarfile) {
301         for (Enumeration JavaDoc e = zipPackages.keys() ; e.hasMoreElements() ;) {
302             String JavaDoc pkg = (String JavaDoc)e.nextElement();
303             String JavaDoc classes = (String JavaDoc)zipPackages.get(pkg);
304
305             int idx = classes.indexOf('@');
306             if (idx >= 0 && Options.respectJavaAccessibility) {
307                 classes = classes.substring(0, idx);
308             }
309
310             makeJavaPackage(pkg, classes, jarfile);
311         }
312     }
313
314     // Read in cache file storing package info for a single .jar
315
// Return null and delete this cachefile if it is invalid
316
private Hashtable JavaDoc readCacheFile(JarXEntry entry,String JavaDoc jarcanon)
317     {
318         String JavaDoc cachefile = entry.cachefile;
319         long mtime = entry.mtime;
320
321         debug("reading cache, '"+jarcanon+"'");
322
323         try {
324             DataInputStream istream = inOpenCacheFile(cachefile);
325             String JavaDoc old_jarcanon = istream.readUTF();
326             long old_mtime = istream.readLong();
327             if ((!old_jarcanon.equals(jarcanon)) ||
328             (old_mtime != mtime))
329             {
330                 comment("invalid cache file: "+
331                 cachefile+", "+jarcanon+":"+
332                 old_jarcanon+", "+mtime+":"+old_mtime);
333                 deleteCacheFile(cachefile);
334                 return null;
335             }
336             Hashtable JavaDoc packs = new Hashtable JavaDoc();
337             try {
338                 while (true) {
339                     String JavaDoc packageName = istream.readUTF();
340                     String JavaDoc classes = istream.readUTF();
341                     packs.put(packageName, classes);
342                 }
343             } catch (EOFException eof) {
344                 ;
345             }
346             istream.close();
347
348             return packs;
349         } catch (IOException ioe) {
350             // if (cachefile.exists()) cachefile.delete();
351
return null;
352         }
353     }
354
355     // Write a cache file storing package info for a single .jar
356
private void writeCacheFile(JarXEntry entry,String JavaDoc jarcanon,
357     Hashtable JavaDoc zipPackages,boolean brandNew)
358     {
359         try {
360             DataOutputStream ostream = outCreateCacheFile(entry, brandNew);
361             ostream.writeUTF(jarcanon);
362             ostream.writeLong(entry.mtime);
363             comment("rewriting cachefile for '"+jarcanon+"'");
364
365             for (Enumeration JavaDoc e = zipPackages.keys() ; e.hasMoreElements() ;) {
366                 String JavaDoc packageName = (String JavaDoc)e.nextElement();
367                 String JavaDoc classes = (String JavaDoc)zipPackages.get(packageName);
368                 ostream.writeUTF(packageName);
369                 ostream.writeUTF(classes);
370             }
371             ostream.close();
372         } catch (IOException ioe) {
373             warning("can't write cache file for '"+jarcanon+"'");
374         }
375     }
376
377     /** Initializes cache. Eventually reads back cache index.
378      * Index persistent storage is accessed through inOpenIndex().
379      */

380     protected void initCache() {
381         indexModified = false;
382         jarfiles = new Hashtable JavaDoc();
383
384         try {
385             DataInputStream istream = inOpenIndex();
386             if (istream == null) return;
387
388             try {
389                 while (true) {
390                     String JavaDoc jarcanon = istream.readUTF();
391                     String JavaDoc cachefile = istream.readUTF();
392                     long mtime = istream.readLong();
393                     jarfiles.put(jarcanon, new JarXEntry(cachefile,mtime));
394                 }
395             } catch (EOFException eof) {
396                 ;
397             }
398             istream.close();
399         } catch (IOException ioe) {
400             warning("invalid index file");
401         }
402
403     }
404
405     /** Write back cache index.
406      * Index persistent storage is accessed through outOpenIndex().
407      */

408     public void saveCache() {
409         if(jarfiles == null || !indexModified ) return;
410
411         indexModified = false;
412
413         comment("writing modified index file");
414
415         try {
416             DataOutputStream ostream = outOpenIndex();
417             for (Enumeration JavaDoc e = jarfiles.keys(); e.hasMoreElements();) {
418                 String JavaDoc jarcanon = (String JavaDoc)e.nextElement();
419                 JarXEntry entry = (JarXEntry)jarfiles.get(jarcanon);
420                 ostream.writeUTF(jarcanon);
421                 ostream.writeUTF(entry.cachefile);
422                 ostream.writeLong(entry.mtime);
423             }
424             ostream.close();
425         } catch (IOException ioe) {
426             warning("can't write index file");
427         }
428     }
429
430     // hooks for changing cache storage
431

432     /** To pass a cachefile id by ref. And for internal use.
433      * See outCreateCacheFile
434      */

435     public static class JarXEntry extends Object JavaDoc {
436         /** cachefile id */
437         public String JavaDoc cachefile;
438
439         public long mtime;
440
441         public JarXEntry(String JavaDoc cachefile) {
442             this.cachefile = cachefile;
443         }
444
445         public JarXEntry(String JavaDoc cachefile,long mtime) {
446             this.cachefile = cachefile;
447             this.mtime = mtime;
448         }
449
450
451     }
452
453     /** Open cache index for reading from persistent storage - hook.
454      * Must Return null if this is absent.
455      * This default impl is part of the off-the-shelf local
456      * file-system cache impl.
457      * Can be overriden.
458      */

459     protected DataInputStream inOpenIndex() throws IOException {
460         File indexFile = new File(cachedir, "packages.idx");
461
462         if (!indexFile.exists()) return null;
463
464         DataInputStream istream = new DataInputStream(
465         new BufferedInputStream(new FileInputStream(indexFile)));
466
467         return istream;
468     }
469
470     /** Open cache index for writing back to persistent storage - hook.
471      * This default impl is part of the off-the-shelf local
472      * file-system cache impl.
473      * Can be overriden.
474      */

475     protected DataOutputStream outOpenIndex() throws IOException {
476         File indexFile = new File(cachedir, "packages.idx");
477
478         return new DataOutputStream(
479         new BufferedOutputStream(new FileOutputStream(indexFile)));
480     }
481
482     /** Open cache file for reading from persistent storage - hook.
483      * This default impl is part of the off-the-shelf local
484      * file-system cache impl.
485      * Can be overriden.
486      */

487     protected DataInputStream inOpenCacheFile(String JavaDoc cachefile)
488         throws IOException
489     {
490         return new DataInputStream(
491                new BufferedInputStream(
492                new FileInputStream(cachefile)));
493     }
494
495     /** Delete (invalidated) cache file from persistent storage - hook.
496      * This default impl is part of the off-the-shelf local
497      * file-system cache impl.
498      * Can be overriden.
499      */

500     protected void deleteCacheFile(String JavaDoc cachefile) {
501         new File(cachefile).delete();
502     }
503
504     /**
505      * Create/open cache file for rewriting back to persistent storage - hook.
506      * If create is false, cache file is supposed to exist and must be opened
507      * for rewriting, entry.cachefile is a valid cachefile id.
508      * If create is true, cache file must be created. entry.cachefile is a
509      * flat jarname to be used to produce a valid cachefile id (to be put
510      * back in entry.cachefile on exit).
511      * This default impl is part of the off-the-shelf local file-system
512      * cache impl.
513      * Can be overriden.
514      */

515     protected DataOutputStream outCreateCacheFile(JarXEntry entry,
516                                                   boolean create)
517         throws IOException
518     {
519         File cachefile = null;
520
521         if(create) {
522             int index = 1;
523             String JavaDoc suffix = "";
524             String JavaDoc jarname = entry.cachefile;
525             while (true) {
526                 cachefile = new File(cachedir, jarname+suffix+".pkc");
527                 //System.err.println("try cachefile: "+cachefile);
528
if (!cachefile.exists()) break;
529                 suffix = "$"+index;
530                 index += 1;
531             }
532             entry.cachefile = cachefile.getCanonicalPath();
533         } else cachefile = new File(entry.cachefile);
534
535         return new DataOutputStream(
536                new BufferedOutputStream(
537                new FileOutputStream(cachefile)));
538     }
539
540     // for default cache (local fs based) impl
541

542     private File cachedir;
543
544     /** Initialize off-the-shelf (default) local file-system cache impl.
545      * Must be called before {@link #initCache}.
546      * cachedir is the cache repository directory, this is eventually created.
547      * Returns true if dir works.
548      */

549     protected boolean useCacheDir(File cachedir) {
550         if(cachedir == null) return false;
551         if (!cachedir.isDirectory() && cachedir.mkdirs() == false) {
552             warning("can't create package cache dir, '"+cachedir+"'");
553             return false;
554         }
555
556         this.cachedir = cachedir;
557
558         return true;
559     }
560
561 }
562
Popular Tags