KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > web > jspparser_ext > WebAppParseSupport


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.netbeans.modules.web.jspparser_ext;
21
22 import java.beans.PropertyChangeListener JavaDoc;
23 import java.io.File JavaDoc;
24 import java.io.FileFilter JavaDoc;
25 import java.io.IOException JavaDoc;
26 import java.lang.reflect.Field JavaDoc;
27 import java.net.MalformedURLException JavaDoc;
28 import java.net.URL JavaDoc;
29 import java.net.URLClassLoader JavaDoc;
30 import java.security.CodeSource JavaDoc;
31 import java.security.PermissionCollection JavaDoc;
32 import java.util.ArrayList JavaDoc;
33 import java.util.Date JavaDoc;
34 import java.util.Enumeration JavaDoc;
35 import java.util.HashMap JavaDoc;
36 import java.util.HashSet JavaDoc;
37 import java.util.Hashtable JavaDoc;
38 import java.util.Iterator JavaDoc;
39 import java.util.Map JavaDoc;
40 import java.util.Set JavaDoc;
41 import java.util.regex.Matcher JavaDoc;
42 import java.util.regex.Pattern JavaDoc;
43 import javax.servlet.ServletContext JavaDoc;
44 import org.openide.filesystems.FileObject;
45 import org.openide.filesystems.FileUtil;
46 import org.openide.filesystems.FileStateInvalidException;
47 import org.openide.ErrorManager;
48 import org.openide.util.NbBundle;
49
50 import org.netbeans.modules.web.jsps.parserapi.JspParserAPI;
51 import org.netbeans.modules.web.jsps.parserapi.Node;
52 import org.netbeans.modules.web.jsps.parserapi.PageInfo;
53 import org.netbeans.modules.web.jspparser.*;
54
55 import org.apache.jasper.Options;
56 import org.apache.jasper.JspCompilationContext;
57 import org.apache.jasper.JasperException;
58 import org.apache.jasper.compiler.ExtractPageData;
59 import org.apache.jasper.compiler.GetParseData;
60 import org.apache.jasper.compiler.JspRuntimeContext;
61 import org.apache.jasper.compiler.TldLocationsCache;
62 import org.netbeans.api.java.classpath.ClassPath;
63 import org.netbeans.modules.web.jspparser_ext.OptionsImpl;
64 import org.openide.filesystems.URLMapper;
65
66 /** Class that provides JSP parsing support for one web application. It caches
67  * some useful data on a per-webapp basis.<br>
68  *
69  * Among other things, it does the following to correctly manage the development cycle:
70  * <ul>
71  * <li>Creates the correct classloader for loading JavaBeans, tag hanlders and other classes managed by the application.</li>
72  * <li>Caches the ServletContext (needed by the parser) corresponding to the application.</li>
73  * <li>Listens on changes in the application and releases caches as needed.</li>
74  * </ul>
75  * @author Petr Jiricka
76  */

77 public class WebAppParseSupport implements WebAppParseProxy, PropertyChangeListener JavaDoc {
78     
79     private FileObject wmRoot;
80     
81     private OptionsImpl editorOptions;
82     private OptionsImpl diskOptions;
83     private ServletContext JavaDoc editorContext;
84     private ServletContext JavaDoc diskContext;
85     private JspRuntimeContext rctxt;
86     private URLClassLoader JavaDoc waClassLoader;
87     private URLClassLoader JavaDoc waContextClassLoader;
88     /** Maps File -> Long, holds timestamps for files used during classloading,
89      * namely: all jar files containing classes, and directories+subdirectories containing unpackaged classes. */

90     private HashMap JavaDoc clRootsTimeStamps;
91     private JspParserAPI.WebModule wm;
92     
93     /** The mappings field in the TldLocationsCache class.
94      */

95     private static Field JavaDoc mappingsF;
96     
97     /** The mappings are cashed here.
98      */

99     private Map JavaDoc mappings;
100     
101     /** This is flag, whether the execute and compilation classpath for the web project is actual.
102      * The flag is set to false, when there is event, which notifies about change in the classpath.
103      * The value is obtained before constructing the cache and classloaders.
104      */

105     private boolean isClassPathCurrent;
106     
107     /** This cache contains the lib (all jars), and all tld files. It's used for
108      * checking, whether these files are not changed.
109      */

110     private HashMap JavaDoc mappingFiles;
111    
112     /** This is hashcode of the execution classpath, which is used for building classloader.
113      * In checkClassesAreCurrent is used for fast check, whether the classpath was not changed.
114      * The main reason is the web freeform, because the web freeform doesn't fire the change property.
115      */

116     
117     private int lastCheckedClasspath;
118     
119     /** Set of jars, which are excluded from the parser classpath. Parser takes these jars from the
120      *system. In our case we have to put these jars on the parent classloader.
121      **/

122     private Set JavaDoc parserSystemJars = null;
123     
124     
125     /** ErrorManager shared by whole module (package) for logging */
126     /** Returns the debug level of parser. The higher the number, the more debug messages are printed out.
127      * The debug level is specified by setting system org.netbeans.modules.jspparser.debug to a non-negative int value.
128      * Zero means no debug messages.
129      */

130     
131     private static int parserDebugLevel = Integer.getInteger("org.netbeans.modules.jspparser.debug", 0).intValue(); // NOI18N
132

133     /** Creates a new instance of WebAppParseSupport */
134     public WebAppParseSupport(JspParserAPI.WebModule wm) {
135         this.wm = wm;
136         this.wmRoot = wm.getDocumentBase();
137         wm.addPropertyChangeListener(this);
138         clRootsTimeStamps = new HashMap JavaDoc();
139         reinitOptions();
140         mappings = null;
141         mappingFiles = null;
142     }
143     
144     public JspParserAPI.JspOpenInfo getJspOpenInfo(FileObject jspFile, boolean useEditor/*, URLClassLoader waClassLoader*/) {
145         // PENDING - do caching for individual JSPs
146
JspCompilationContext ctxt = createCompilationContext(jspFile, useEditor);
147         ExtractPageData epd = new ExtractPageData(ctxt);
148         try {
149             return new JspParserAPI.JspOpenInfo(epd.isXMLSyntax(), epd.getEncoding());
150         } catch (Exception JavaDoc e) {
151             if (parserDebugLevel > 0) {
152                 ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, e);
153             }
154             return getDefaultJspOpenInfo(wmRoot, jspFile);
155         }
156     }
157     
158     
159     private JspParserAPI.JspOpenInfo getDefaultJspOpenInfo(FileObject wmRoot, FileObject jspFile) {
160         // PENDING - we could at least look at the file extension etc.
161
return new JspParserAPI.JspOpenInfo(false, "8859_1"); // NOI18N
162
}
163     
164     synchronized void reinitOptions() {
165         if (parserDebugLevel > 0) {
166             System.out.println("[" + new Date JavaDoc() + "] " + //NOI18N
167
"JSP parser reinitialized for WM " + FileUtil.toFile(wmRoot)); // NOI18N
168
ErrorManager.getDefault().log(ErrorManager.INFORMATIONAL, "[" + new Date JavaDoc() + "] " + //NOI18N
169
"JSP parser reinitialized for WM " + FileUtil.toFile(wmRoot)); // NOI18N
170
}
171         editorContext = new ParserServletContext(wmRoot, wm, true);
172         diskContext = new ParserServletContext(wmRoot, wm, false);
173         editorOptions = new OptionsImpl(editorContext);
174         diskOptions = new OptionsImpl(diskContext);
175         rctxt = null;
176         // try null, but test with tag files
177
//new JspRuntimeContext(context, options);
178
isClassPathCurrent = true;
179         createClassLoaders();
180         
181     }
182     
183     private static Pattern JavaDoc rePatternMyFaces = Pattern.compile(".*myfaces-impl.*\\.jar.*"); // NOI18N
184
private static Pattern JavaDoc rePatternCommonsLogging = Pattern.compile(".*commons-logging.*\\.jar.*"); // NOI18N
185
/** Returns whether the jar is excluded by the parser, because should be on the parser
186      * system classpath.
187      **/

188     private boolean isParserSystemJar(URL JavaDoc path){
189         String JavaDoc name = path.getFile();
190         if (name.endsWith("!/")) name = name.substring(0, name.length()-2);
191         int index = name.lastIndexOf('/')+1;
192         
193         if (index > 0)
194             name = name.substring(index).trim();
195         // fix for issue #77134 - NetBeans5.5 editor cannot resolve some MyFaces taglibs
196
Matcher JavaDoc m = rePatternMyFaces.matcher(path.getFile());
197         return (m.matches() || getParserSystemJar().contains(name));
198     }
199     
200     private boolean isUnexpectedLibrary(URL JavaDoc url){
201         Matcher JavaDoc m = rePatternCommonsLogging.matcher(url.getFile());
202         return m.matches();
203     }
204     
205     private void createClassLoaders() {
206         clRootsTimeStamps.clear();
207         
208         //web.xml
209
FileObject webxml = ContextUtil.findRelativeFileObject(wmRoot, "WEB-INF/web.xml"); //NOI18N
210
if (webxml !=null ){
211             registerTimeStamp(webxml, false);
212         }
213         
214         // libraries
215

216         // WEB-INF/lib
217
// Looking for jars in WEB-INF/lib is mainly for tests. Can a user create a lib dir in the document base
218
// and put here a jar?
219

220         Hashtable JavaDoc tomcatTable = new Hashtable JavaDoc();
221         Hashtable JavaDoc loadingTable = new Hashtable JavaDoc();
222         HashSet JavaDoc systemJars = new HashSet JavaDoc();
223         FileObject libDir = ContextUtil.findRelativeFileObject(wmRoot, "WEB-INF/lib"); //NOI18N
224
URL JavaDoc helpurl;
225         boolean isSystemJar;
226         
227         if (libDir != null) {
228             Enumeration JavaDoc libDirKids = libDir.getChildren(false);
229             while (libDirKids.hasMoreElements()) {
230                 FileObject elem = (FileObject)libDirKids.nextElement();
231                 if (elem.getExt().equals("jar")) { //NOI18N
232
helpurl = findInternalURL(elem);
233                     isSystemJar = isParserSystemJar(helpurl);
234                     if (!isUnexpectedLibrary(helpurl) /*&& !isSystemJar*/){
235                         tomcatTable.put(helpurl, helpurl);
236                         loadingTable.put(helpurl, helpurl);
237                         registerTimeStamp(elem, false);
238                     }
239                     if (isSystemJar) systemJars.add(helpurl);
240                 }
241             }
242         }
243         
244         
245         
246         
247         // issue 54845. On the class loader we must put the java sources as well. It's in the case, when there are a
248
// tag hendler, which is added in a tld, which is used in the jsp file.
249
ClassPath cp = ClassPath.getClassPath(wmRoot, ClassPath.COMPILE);
250         if (cp != null){
251             FileObject[] roots = cp.getRoots();
252             for (int i = 0; i < roots.length; i++){
253                 helpurl = findInternalURL(roots[i]);
254                 isSystemJar = isParserSystemJar(helpurl);
255                 if (loadingTable.get(helpurl) == null && !isUnexpectedLibrary(helpurl) /*&& !isSystemJar*/){
256                     loadingTable.put(helpurl, helpurl);
257                     tomcatTable.put(helpurl, findExternalURL(roots[i]));
258                     registerTimeStamp(roots[i], false);
259                 }
260                 if (isSystemJar && !systemJars.contains(helpurl)) systemJars.add(helpurl);
261             }
262         }
263         // libraries and built classes are on the execution classpath
264
cp = ClassPath.getClassPath(wmRoot, ClassPath.EXECUTE);
265         // remember the hashCode of this classpath
266
lastCheckedClasspath = cp.hashCode();
267         
268         if (cp != null){
269             FileObject [] roots = cp.getRoots();
270             for (int i = 0; i < roots.length; i++){
271                 helpurl = findInternalURL(roots[i]);
272                 isSystemJar = isParserSystemJar(helpurl);
273                 if (loadingTable.get(helpurl) == null && !isUnexpectedLibrary(helpurl) /*&& !isSystemJar*/){
274                     loadingTable.put(helpurl, helpurl);
275                     tomcatTable.put(helpurl, findExternalURL(roots[i]));
276                     registerTimeStamp(roots[i], false);
277                 }
278                 if (isSystemJar && !systemJars.contains(helpurl)) systemJars.add(helpurl);
279             }
280         }
281         FileObject classesDir = ContextUtil.findRelativeFileObject(wmRoot, "WEB-INF/classes"); //NOI18N
282
if (classesDir != null && loadingTable.get(helpurl = findInternalURL(classesDir)) == null){
283             loadingTable.put(helpurl, helpurl);
284             tomcatTable.put(helpurl, helpurl);
285             registerTimeStamp(classesDir, false);
286         }
287         
288         Iterator JavaDoc iter = loadingTable.values().iterator();
289         URL JavaDoc loadingURLs[] = new URL JavaDoc[loadingTable.size()];
290         int index = 0;
291         while (iter.hasNext())
292             loadingURLs[index++] = (URL JavaDoc)iter.next();
293         
294         URL JavaDoc tomcatURLs[] = new URL JavaDoc[tomcatTable.size()];
295         iter = tomcatTable.values().iterator();
296         index = 0;
297         while (iter.hasNext())
298             tomcatURLs[index++] = (URL JavaDoc)iter.next();
299         
300         
301         // Put extra jars on the classpath. Usually these jars are offered by target server
302
File JavaDoc[] files = wm.getExtraClasspathEntries();
303         if (files == null)
304             files = new File JavaDoc[0];
305         
306         URL JavaDoc urls[] = new URL JavaDoc[files.length + systemJars.size()];
307         try {
308             for (int i = 0; i < files.length; i++) {
309                 urls[i] = files[i].toURI().toURL();
310             }
311             iter = systemJars.iterator();
312             for (int i = files.length; i < urls.length; i++)
313                 urls[i] = (URL JavaDoc)iter.next();
314         } catch (MalformedURLException JavaDoc ex) {
315             ErrorManager.getDefault().notify(ErrorManager.EXCEPTION, ex);
316         }
317         
318         waClassLoader = new ParserClassLoader(loadingURLs, tomcatURLs, getClass().getClassLoader());
319         waContextClassLoader = new ParserClassLoader(loadingURLs, tomcatURLs, new JasperSystemClassLoader(urls, Thread.currentThread().getContextClassLoader()));
320         if (parserDebugLevel > 3) {
321             String JavaDoc clString;
322             // print out webapp classloader
323
clString = "wa class loader : " + waClassLoader; // NOI18N
324
System.out.println(clString);
325             ErrorManager.getDefault().log(ErrorManager.INFORMATIONAL, clString); // NOI18N
326
// print out context class loader
327
clString = "ctxt class loader : " + waContextClassLoader; // NOI18N
328
System.out.println(clString);
329             ErrorManager.getDefault().log(ErrorManager.INFORMATIONAL, clString); // NOI18N
330
}
331     }
332     
333     private URL JavaDoc findInternalURL(FileObject fo) {
334         URL JavaDoc url = URLMapper.findURL(fo, URLMapper.INTERNAL);
335         return url;
336     }
337     
338     private URL JavaDoc findExternalURL(FileObject fo) {
339         // PENDING - URLMapper.EXTERNAL does not seem to be working now, so using this workaround
340
File JavaDoc f = FileUtil.toFile(fo);
341         if ((f != null)/* && (f.isDirectory())*/) {
342             try {
343                 return f.toURI().toURL();
344             }
345             catch (MalformedURLException JavaDoc e) {
346                 ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, e);
347             }
348         }
349         // fallback
350
return URLMapper.findURL(fo, URLMapper.EXTERNAL);
351     }
352     
353     private void registerTimeStamp(FileObject fo, boolean recursive) {
354         try {
355             if (fo.getURL().getProtocol().equals("jar")) // NOI18N
356
fo = FileUtil.getArchiveFile(fo);
357         }
358         catch (FileStateInvalidException e){ //Nothing to do.
359
}
360         
361         if (fo != null){
362             File JavaDoc f = FileUtil.toFile(fo);
363             if (f != null) {
364                 registerTimeStamp(f, recursive);
365             }
366         }
367     }
368     
369     private void registerTimeStamp(Map JavaDoc where, File JavaDoc f, boolean recursive) {
370         where.put(f, new Long JavaDoc(f.lastModified()));
371         if (recursive && f.isDirectory()) {
372             File JavaDoc kids[] = f.listFiles(
373                     new FileFilter JavaDoc() {
374                 public boolean accept(File JavaDoc pathname) {
375                     return pathname.isDirectory();
376                 }
377             }
378             );
379             for (int i = 0; i < kids.length; i++) {
380                 registerTimeStamp(where, kids[i], recursive);
381             }
382         }
383     }
384     
385     private void registerTimeStamp(File JavaDoc f, boolean recursive) {
386         clRootsTimeStamps.put(f, new Long JavaDoc(f.lastModified()));
387         if (recursive && f.isDirectory()) {
388             File JavaDoc kids[] = f.listFiles(
389                     new FileFilter JavaDoc() {
390                 public boolean accept(File JavaDoc pathname) {
391                     return pathname.isDirectory();
392                 }
393             }
394             );
395             for (int i = 0; i < kids.length; i++) {
396                 registerTimeStamp(kids[i], recursive);
397             }
398         }
399     }
400     
401     private synchronized JspCompilationContext createCompilationContext(FileObject jspFile, boolean useEditor) {
402         boolean isTagFile = determineIsTagFile(jspFile);
403         String JavaDoc jspUri = getJSPUri(jspFile);
404         Options options = useEditor ? editorOptions : diskOptions;
405         ServletContext JavaDoc context = useEditor ? editorContext : diskContext;
406         JspCompilationContext clctxt = null;
407         try {
408             if (isTagFile) {
409                 clctxt = new JspCompilationContext
410                     (jspUri, null, options, context, null, rctxt, null );
411
412             } else {
413                 clctxt = new JspCompilationContext
414                         (jspUri, false, options, context, null, rctxt );
415             }
416         } catch (JasperException ex) {
417                 ErrorManager.getDefault().annotate(ex, "JSP Parser");
418         }
419         clctxt.setClassLoader(getWAClassLoader());
420         return clctxt;
421     }
422     
423     private boolean determineIsTagFile(FileObject fo) {
424         if (fo.getExt().startsWith("tag")) { // NOI18N - all tag, tagx and even tagf are considered tag files
425
return true;
426         }
427         if (JspParserAPI.TAG_MIME_TYPE.equals(fo.getMIMEType())) {
428             return true;
429         }
430         return false;
431     }
432     
433     private String JavaDoc getJSPUri(FileObject jsp) {
434         return ContextUtil.findRelativeContextPath(wmRoot, jsp);
435     }
436     
437     // from JspCompileUtil
438
public JspParserAPI.ParseResult analyzePage(FileObject jspFile, /*String compilationURI, */
439             int errorReportingMode) {
440         // PENDING - do caching for individual JSPs
441
JspCompilationContext ctxt = createCompilationContext(jspFile, true);
442         
443 /* OptionsImpl options = new OptionsImpl(jspPage);
444  
445         CompilationDescriptor cd = new CompilationDescriptor(
446                                        JspCompileUtil.getContextRoot(jspPage.getPrimaryFile()).getFileSystem(), compilationURI);
447         String jspResource = JspCompileUtil.getContextPath(jspPage.getPrimaryFile());
448  
449         AnalyzerCompilerContext ctxt = new AnalyzerCompilerContext(jspResource, cd, options);*/

450         
451         return callTomcatParser(jspFile, ctxt, waContextClassLoader, errorReportingMode);
452     }
453     
454     /**
455      * Returns the mapping of the 'global' tag library URI to the location (resource
456      * path) of the TLD associated with that tag library. The location is
457      * returned as a String array:
458      * [0] The location
459      * [1] If the location is a jar file, this is the location of the tld.
460      */

461     public synchronized Map JavaDoc getTaglibMap(boolean useEditor) throws IOException JavaDoc {
462         Options options = useEditor ? editorOptions : diskOptions;
463         TldLocationsCache lc = options.getTldLocationsCache();
464         Map JavaDoc mappings = new HashMap JavaDoc();
465         mappings.putAll(getMappingsByReflection(lc));
466         mappings.putAll(getImplicitLocation());
467         return mappings;
468     }
469     
470     /** Returns map with tlds, which doesn't have defined <uri>.
471      */

472     private Map JavaDoc getImplicitLocation(){
473         Map JavaDoc returnMap = new HashMap JavaDoc();
474         // Obtain all tld files under WEB-INF folder
475
FileObject webInf = ContextUtil.findRelativeFileObject(wmRoot, "WEB-INF");
476         FileObject fo;
477         File JavaDoc file;
478         if (webInf != null && webInf.isFolder()){
479             Enumeration JavaDoc en = webInf.getChildren(true);
480             while (en.hasMoreElements()){
481                 fo = (FileObject)en.nextElement();
482                 if (fo.getExt().equals("tld")){
483                     file = FileUtil.toFile(fo);
484                     String JavaDoc path = "/" + ContextUtil.findRelativePath(wmRoot, fo);
485                     returnMap.put(path, new String JavaDoc[] { path, null });
486                 }
487             }
488         }
489         return returnMap;
490     }
491     
492     /** Methed checks whether the files, which contains mappings (jar and tld files)
493      * were not changed. At first obtain all jars and then all tlds. These file are
494      * included into HashMap, which is compared with the cache.
495      */

496     private synchronized boolean checkMappingsAreCurrent(){
497         if (mappingFiles == null) {
498             return false;
499         }
500         
501         HashMap JavaDoc checkedFiles = new HashMap JavaDoc();
502         // Obtain all libraries (jars).
503
FileObject[] roots = ClassPath.getClassPath(wm.getDocumentBase(), ClassPath.EXECUTE).getRoots();
504         FileObject fo;
505         File JavaDoc file;
506         try{
507             for (int i = 0; i < roots.length; i++){
508                 if (roots[i].getURL().getProtocol().equals("jar")) { //NOI18N
509
fo = FileUtil.getArchiveFile(roots[i]);
510                     if (fo != null){
511                         file = FileUtil.toFile(fo);
512                         checkedFiles.put(file, new Long JavaDoc(file.lastModified()));
513                     }
514                 }
515             }
516         }
517         catch(org.openide.filesystems.FileStateInvalidException e){
518             ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, e);
519         }
520         
521         // Obtain all tld files under WEB-INF folder
522
FileObject webInf = ContextUtil.findRelativeFileObject(wmRoot, "WEB-INF"); //NOI18N
523
if (webInf != null && webInf.isFolder()){
524             Enumeration JavaDoc en = webInf.getChildren(true);
525             while (en.hasMoreElements()){
526                 fo = (FileObject)en.nextElement();
527                 if (fo.getExt().equals("tld")){ //NOI18N
528
file = FileUtil.toFile(fo);
529                     checkedFiles.put (file, new Long JavaDoc(file.lastModified()));
530                 }
531             }
532         }
533         
534         // all file under WEB-INF
535
fo = ContextUtil.findRelativeFileObject(wmRoot, "WEB-INF"); //NOI18N
536
if (fo != null){
537             file = FileUtil.toFile(fo);
538             registerTimeStamp(checkedFiles, file, true);
539         }
540         // Compare the maps
541
if (!checkedFiles.equals(mappingFiles)){
542             return false;
543         }
544         return true;
545     }
546     
547     /**
548      * Parser system jars, are jars, which are excluded from the parsing.
549      * These jar has to be putted in different "system parser" classloader.
550      */

551     private Set JavaDoc getParserSystemJar(){
552         if (parserSystemJars == null){
553             TldLocationsCache lc = diskOptions.getTldLocationsCache();
554             try {
555                 Field JavaDoc systemsJar = TldLocationsCache.class.getDeclaredField("systemJars"); //NOI18N
556
systemsJar.setAccessible(true);
557                 parserSystemJars = (HashSet JavaDoc)systemsJar.get(lc);
558             } catch (IllegalArgumentException JavaDoc e) {
559                 ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, e);
560             } catch (SecurityException JavaDoc e) {
561                 ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, e);
562             } catch (NoSuchFieldException JavaDoc e) {
563                 ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, e);
564             } catch (IllegalAccessException JavaDoc e) {
565                 ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, e);
566             }
567         }
568         return parserSystemJars;
569     }
570     
571     private Map JavaDoc getMappingsByReflection(TldLocationsCache lc) throws IOException JavaDoc {
572         try {
573             if (!isClassPathCurrent || !checkMappingsAreCurrent()) {
574                 // if the classpath was changed, create new classloaders
575
if (!isClassPathCurrent)
576                     reinitOptions();
577                 mappingsF = TldLocationsCache.class.getDeclaredField("mappings"); //NOI18N
578
mappingsF.setAccessible(true);
579                 // Before new parsing, the old mappings in the TldLocationCache has to be cleared. Else there are
580
// stored the old mappings.
581
((Hashtable JavaDoc)mappingsF.get(lc)).clear();
582                 
583                 Thread JavaDoc compThread = new WebAppParseSupport.InitTldLocationCacheThread(lc);
584                 compThread.setContextClassLoader(waContextClassLoader);
585                 compThread.start();
586                 
587                 try {
588                     compThread.join();
589                 } catch (java.lang.InterruptedException JavaDoc e){
590                     ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, e);
591                 }
592                 
593                 mappings = (Map JavaDoc)mappingsF.get(lc);
594                 //------------------------- construct the cache -----------------------------
595
// Obtain all files, which were parsed and store the lastchange time to the cache.
596
if (mappingFiles == null)
597                     mappingFiles = new HashMap JavaDoc();
598                 else
599                     // clear the old cache
600
mappingFiles.clear();
601                 
602                 HashMap JavaDoc usedFile = new HashMap JavaDoc();
603                 
604                 // Obtain all files which has tlds. There can be more mappings in one tld.
605
// The value of the mapping is String[file][relative tld path]
606
Iterator JavaDoc iter = mappings.values().iterator();
607                 while (iter.hasNext()){
608                     usedFile.put(((String JavaDoc[])iter.next())[0], null);
609                 }
610                 
611                 // Store the files into the cache
612
iter = usedFile.keySet().iterator();
613                 File JavaDoc file;
614                 while (iter.hasNext()){
615                     String JavaDoc uri = (String JavaDoc)iter.next();
616                     // usualy if the uri starts with the file, then it's a jar
617
if (!uri.startsWith("file:")){ // NoI18N
618
FileObject fo = ContextUtil.findRelativeFileObject(wmRoot, uri);
619                         if (fo != null)
620                             file = FileUtil.toFile(fo);
621                         else
622                             file = null;
623                     } else {
624                         uri = uri.substring(5); // remove the file:
625
file = new File JavaDoc(uri);
626                     }
627                     if (file != null)
628                         mappingFiles.put(file, new Long JavaDoc(file.lastModified()));
629                 }
630                 
631                 // Add to the cache all jars, which are on the classpath. It's because
632
// not every jar has tld, but every jar is parsed during parsing mappings.
633
FileObject[] roots = ClassPath.getClassPath(wm.getDocumentBase(), ClassPath.EXECUTE).getRoots();
634                 FileObject fo;
635                 try{
636                     for (int i = 0; i < roots.length; i++){
637                         if (roots[i].getURL().getProtocol().equals("jar")) { //NOI18N
638
fo = FileUtil.getArchiveFile(roots[i]);
639                             if (fo != null){
640                                 file = FileUtil.toFile(fo);
641                                 mappingFiles.put(file, new Long JavaDoc(file.lastModified()));
642                             }
643                         }
644                     }
645                 } catch(org.openide.filesystems.FileStateInvalidException e){
646                     ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, e);
647                 }
648                 
649                 // Add all files under WEB-INF. The most interesting files are web.xml and tag files.
650
fo = ContextUtil.findRelativeFileObject(wmRoot, "WEB-INF");
651                 if (fo != null){
652                     file = FileUtil.toFile(fo);
653                     registerTimeStamp(mappingFiles, file, true);
654                 }
655                 //------------------------- end of constructing the cache -----------------------------
656
}
657             return mappings;
658         }
659         
660         catch (NoSuchFieldException JavaDoc e) {
661             ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, e);
662             IOException JavaDoc e2 = new IOException JavaDoc();
663             e2.initCause(e);
664             throw e2;
665         } catch (IllegalAccessException JavaDoc e) {
666             ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, e);
667             IOException JavaDoc e2 = new IOException JavaDoc();
668             e2.initCause(e);
669             throw e2;
670         }
671     }
672     
673     
674     /** Returns the classloader to be used by the JSP parser.
675      * This classloader loads the classes belonging to the application
676      * from both expanded directory structures and jar files.
677      */

678     public URLClassLoader JavaDoc getWAClassLoader() {
679         if (!checkClassesAreCurrent()) {
680             reinitOptions();
681         }
682         return waClassLoader;
683     }
684     
685     /** Checks whether the classes used by this web module have not changed since
686      * the last time the classloader was initialized.
687      * @return true if the classes are still the same (have not changed).
688      */

689     private boolean checkClassesAreCurrent() {
690         if (!isClassPathCurrent)
691             return false;
692         long timeStamp = 0;
693         if (parserDebugLevel > 0) {
694             System.out.println("[" + new Date JavaDoc() + "] " + //NOI18N
695
"JSP parser classloader check started for WM " + FileUtil.toFile(wmRoot)); // NOI18N
696
ErrorManager.getDefault().log(ErrorManager.INFORMATIONAL, "[" + new Date JavaDoc() + "] " + //NOI18N
697
"JSP parser classloader check started for WM " + FileUtil.toFile(wmRoot)); // NOI18N
698
timeStamp = System.currentTimeMillis();
699         }
700         if (clRootsTimeStamps == null) {
701             return false;
702         }
703         Iterator JavaDoc it = clRootsTimeStamps.entrySet().iterator();
704         while (it.hasNext()) {
705             Map.Entry JavaDoc e = (Map.Entry JavaDoc)it.next();
706             File JavaDoc f = (File JavaDoc)e.getKey();
707             if (parserDebugLevel > 9) {
708                 System.out.println(" -> checking file " + f); // NOI18N
709
ErrorManager.getDefault().log(ErrorManager.INFORMATIONAL, " -> checking file " + f);
710             }
711             if (!f.exists()) {
712                 return false;
713             }
714             if (f.lastModified() != ((Long JavaDoc)e.getValue()).longValue()) {
715                 return false;
716             }
717         }
718         ClassPath cp = ClassPath.getClassPath(wmRoot, ClassPath.EXECUTE);
719         // check whether the execution classpath was not changed
720
if (lastCheckedClasspath != cp.hashCode()){
721             return false;
722         }
723         // check whether the files on the execution classpath were not changed.
724
FileObject[] roots = cp.getRoots();
725         FileObject fo;
726         File JavaDoc file;
727         for (int i = 0 ; i < roots.length; i++){
728             URL JavaDoc url = findInternalURL(roots[i]);
729             if (!isParserSystemJar(url) && !isUnexpectedLibrary(url)){
730                 fo = roots[i];
731                 file = null;
732                 try {
733                     if (roots[i].getURL().getProtocol().equals("jar"))
734                         fo = FileUtil.getArchiveFile(roots[i]);
735                 } catch (FileStateInvalidException ex) {
736                     ErrorManager.getDefault().notify(ex);
737                 }
738                 if (fo != null)
739                     file = FileUtil.toFile(fo);
740                 if (!clRootsTimeStamps.containsKey(file)){
741                     return false;
742                 }
743             }
744         }
745         if (parserDebugLevel > 0) {
746             long timeStamp2 = System.currentTimeMillis();
747             System.out.println("[" + new Date JavaDoc() + "] " + //NOI18N
748
"check completed with result 'true', time " + (timeStamp2 - timeStamp));
749         }
750         return true;
751     }
752     
753     public class RRef {
754         JspParserAPI.ParseResult result;
755     }
756     
757     private JspParserAPI.ParseResult callTomcatParser(final FileObject jspFile,
758             final JspCompilationContext ctxt, final ClassLoader JavaDoc contextClassLoader, final int errorReportingMode) {
759         
760         final RRef resultRef = new RRef();
761         
762         // calling the parser in a new thread, as per the spec, we need to set the context classloader
763
// to contain the web application classes. Jasper really relies on this
764
Thread JavaDoc compThread = new Thread JavaDoc("JSP Parsing") { // NOI18N
765

766             private void setResult(GetParseData gpd){
767                 PageInfo nbPageInfo = gpd.getNbPageInfo();
768                 Node.Nodes nbNodes = gpd.getNbNodes();
769                 Throwable JavaDoc e = gpd.getParseException();
770                 if (e == null) {
771                     resultRef.result = new JspParserAPI.ParseResult(nbPageInfo, nbNodes);
772                 } else {
773                     // the exceptions we may see here:
774
// JasperException - usual
775
// ArrayIndexOutOfBoundsException - see issue 20919
776
// Throwable - see issue 21169, related to Tomcat bug 7124
777
ErrorManager.getDefault().annotate(e, NbBundle.getMessage(WebAppParseSupport.class, "MSG_errorDuringJspParsing"));
778                     if (parserDebugLevel > 0) {
779                         ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, e);
780                     }
781                     JspParserAPI.ErrorDescriptor error = constructErrorDescriptor(e, wmRoot, jspFile);
782                     resultRef.result = new JspParserAPI.ParseResult(nbPageInfo, nbNodes, new JspParserAPI.ErrorDescriptor[] {error});
783                 }
784             }
785             
786             public void run() {
787                 GetParseData gpd = null;
788                 try {
789                     gpd = new GetParseData(ctxt, errorReportingMode);
790                     gpd.parse();
791                     setResult(gpd);
792                     
793                 } catch (ThreadDeath JavaDoc td) {
794                     ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, td);
795                     throw td;
796                 } catch (Throwable JavaDoc t) {
797                     if (gpd != null) {
798                         setResult(gpd);
799                     }
800                     else {
801                         ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, t);
802                     }
803                 }
804             }
805         };
806         compThread.setContextClassLoader(contextClassLoader);
807         compThread.start();
808         try {
809             compThread.join();
810             return resultRef.result;
811         } catch (InterruptedException JavaDoc e) {
812             JspParserAPI.ErrorDescriptor error = constructErrorDescriptor(e, wmRoot, jspFile);
813             return new JspParserAPI.ParseResult(new JspParserAPI.ErrorDescriptor[] {error});
814         }
815         
816 /* GetParseData gpd = new GetParseData(ctxt, errorReportingMode);
817         gpd.parse();
818         PageInfo nbPageInfo = gpd.getNbPageInfo();
819         Node.Nodes nbNodes = gpd.getNbNodes();
820         Throwable e = gpd.getParseException();
821         if (e == null) {
822             return new JspParserAPI.ParseResult(nbPageInfo, nbNodes);
823         }
824         else {
825             // the exceptions we may see here:
826             // JasperException - usual
827             // ArrayIndexOutOfBoundsException - see issue 20919
828             // Throwable - see issue 21169, related to Tomcat bug 7124
829             err.annotate(e, NbBundle.getMessage(WebAppParseSupport.class, "MSG_errorDuringJspParsing"));
830             if (getParserDebugLevel() > 0) {
831                 err.notify (ErrorManager.INFORMATIONAL, e);
832             }
833             JspParserAPI.ErrorDescriptor error = constructErrorDescriptor(e, wmRoot, jspFile);
834             return new JspParserAPI.ParseResult(nbPageInfo, nbNodes, new JspParserAPI.ErrorDescriptor[] {error});
835         }
836  */

837     }
838     
839     
840     private static JspParserAPI.ErrorDescriptor constructErrorDescriptor(Throwable JavaDoc e, FileObject wmRoot, FileObject jspPage) {
841         JspParserAPI.ErrorDescriptor error = null;
842         try {
843             error = constructJakartaErrorDescriptor(wmRoot, jspPage, e);
844         } catch (FileStateInvalidException e2) {
845             ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, e2);
846             // do nothing, error will just remain to be null
847
} catch (IOException JavaDoc e2) {
848             ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, e2);
849             // do nothing, error will just remain to be null
850
}
851         if (error == null) {
852             error = new JspParserAPI.ErrorDescriptor(wmRoot, jspPage, -1, -1,
853                     ContextUtil.getThrowableMessage(e, !(e instanceof JasperException)), "");
854         }
855         return error;
856     }
857     
858     /** Returns an ErrorDescriptor for a compilation error if the throwable was thrown by Jakarta,
859      * otherwise returns null. */

860     private static JspParserAPI.ErrorDescriptor constructJakartaErrorDescriptor(
861             FileObject wmRoot, FileObject jspPage, Throwable JavaDoc ex) throws IOException JavaDoc {
862         
863         // PENDING: maybe we should check all nested exceptions
864
StringBuffer JavaDoc allStack = new StringBuffer JavaDoc();
865         Throwable JavaDoc last = ex;
866         allStack.append(ContextUtil.getThrowableMessage(ex, true));
867         while (ex instanceof JasperException) {
868             last = ex;
869             ex = ((JasperException)ex).getRootCause();
870             if (ex != null) {
871                 ErrorManager.getDefault().annotate(last, ex);
872                 allStack.append(ContextUtil.getThrowableMessage(ex, true));
873             }
874         }
875         
876         if (ex == null)
877             ex = last;
878         
879 /*System.out.println("--------STACK------");
880 System.out.println(allStack.toString());
881 System.out.println("--------ENDSTACK------"); */

882         // now it can be JasperException, which starts with error location description
883
String JavaDoc m1 = ex.getMessage();
884         if (m1 == null) return null;
885         int lpar = m1.indexOf('(');
886         if (lpar == -1) return null;
887         int comma = m1.indexOf(',', lpar);
888         if (comma == -1) return null;
889         int rpar = m1.indexOf(')', comma);
890         if (rpar == -1) return null;
891         String JavaDoc line = m1.substring(lpar + 1, comma).trim();
892         String JavaDoc col = m1.substring(comma + 1, rpar).trim();
893         String JavaDoc fileName = m1.substring(0, lpar);
894         
895         // now cnstruct the FileObject using this file name and the web module root
896
File JavaDoc file = FileUtil.toFile(wmRoot);
897         FileObject errorFile = jspPage; // a sensible default
898

899         fileName = new File JavaDoc(fileName).getCanonicalPath();
900         String JavaDoc wmFileName = file.getCanonicalPath();
901         if (fileName.startsWith(wmFileName)) {
902             String JavaDoc errorRes = fileName.substring(wmFileName.length());
903             errorRes = errorRes.replace(File.separatorChar, '/');
904             if (errorRes.startsWith("/")) // NOI18N
905
errorRes = errorRes.substring(1);
906             FileObject errorTemp = ContextUtil.findRelativeFileObject(wmRoot, errorRes);
907             if (errorTemp != null)
908                 errorFile = errorTemp;
909         }
910         
911         // now construct the ErrorDescriptor
912
try {
913             String JavaDoc errContextPath = ContextUtil.findRelativeContextPath(wmRoot, errorFile);
914             String JavaDoc errorMessage = errContextPath + " [" + line + ";" + col + "] " + m1.substring(rpar + 1).trim();
915             return new JspParserAPI.ErrorDescriptor(
916                     wmRoot, errorFile, Integer.parseInt(line), Integer.parseInt(col),
917                     errorMessage, ""); // NOI18N
918
// pending - should also include a line of code
919
} catch (NumberFormatException JavaDoc e) {
920             return null;
921         }
922     }
923     
924     // IMPLEMENTATION OF PropertyChangeListener
925

926     /** Handes the event of property change of libraries used by the web module.
927      * Reinitializes the classloader and other stuff.
928      */

929     public void propertyChange(java.beans.PropertyChangeEvent JavaDoc evt) {
930         String JavaDoc propName = evt.getPropertyName();
931         if (JspParserAPI.WebModule.PROP_LIBRARIES.equals(propName) ||
932                 JspParserAPI.WebModule.PROP_PACKAGE_ROOTS.equals(propName)) {
933             // the classpath was changed, need to be done reinitOptions()
934
isClassPathCurrent = false;
935         }
936     }
937     
938 /* class WAFileChangeListener extends FileChangeAdapter {
939         PENDING - listening
940         WAFileChangeListener() {
941         }
942  
943  
944     }*/

945     
946     public static class JasperSystemClassLoader extends URLClassLoader JavaDoc{
947         private static final java.security.AllPermission JavaDoc ALL_PERM = new java.security.AllPermission JavaDoc();
948         
949         public JasperSystemClassLoader(URL JavaDoc[] urls, ClassLoader JavaDoc parent) {
950             super(urls, parent);
951         }
952         
953         protected PermissionCollection JavaDoc getPermissions(CodeSource JavaDoc codesource) {
954             PermissionCollection JavaDoc perms = super.getPermissions(codesource);
955             perms.add(ALL_PERM);
956             return perms;
957         }
958     }
959     
960     /** This classloader does some security stuff, but equally importantly, it does one horrible hack,
961      * explained in the constructor Javadoc.
962      */

963     public static class ParserClassLoader extends URLClassLoader JavaDoc {
964         
965         private static final java.security.AllPermission JavaDoc ALL_PERM = new java.security.AllPermission JavaDoc();
966         
967         private URL JavaDoc[] tomcatURLs;
968         
969         /** This constructor and the getURLs() method is one horrible hack. On the one hand, we want to give
970          * Tomcat a JarURLConnection when it attempts to load classes from jars, on the other hand
971          * we don't want this classloader to load classes using JarURLConnection, as that's buggy.
972          * We want it to load classes using internal nb: protocols. So the getURLs() method
973          * returns an "external" list of URLs for Tomcat, while internally, the classloader uses
974          * an "internal" list of URLs.
975          */

976         public ParserClassLoader(URL JavaDoc[] classLoadingURLs, URL JavaDoc[] tomcatURLs, ClassLoader JavaDoc parent) {
977             super(classLoadingURLs, parent);
978             this.tomcatURLs = tomcatURLs;
979         }
980         
981         /** See the constructor for explanation of what thie method does.
982          */

983         public URL JavaDoc[] getURLs() {
984             return tomcatURLs;
985         }
986         
987         protected PermissionCollection JavaDoc getPermissions(CodeSource JavaDoc codesource) {
988             PermissionCollection JavaDoc perms = super.getPermissions(codesource);
989             perms.add(ALL_PERM);
990             return perms;
991         }
992         
993         public String JavaDoc toString() {
994             StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
995             sb.append(super.toString());
996             sb.append(", parent : "); // NOI18N
997
sb.append(getParent().toString());
998             return sb.toString();
999         }
1000        
1001    }
1002    
1003    private static class InitTldLocationCacheThread extends Thread JavaDoc{
1004        
1005        private TldLocationsCache cache;
1006        
1007        InitTldLocationCacheThread(TldLocationsCache lc){
1008            super("Init TldLocationCache"); // NOI18N
1009
cache = lc;
1010        }
1011        
1012        public void run() {
1013            try {
1014                Field JavaDoc initialized= TldLocationsCache.class.getDeclaredField("initialized"); // NOI18N
1015
initialized.setAccessible(true);
1016                initialized.setBoolean(cache, false);
1017                cache.getLocation("");
1018            } catch (JasperException e) {
1019                ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, e);
1020            } catch (NoSuchFieldException JavaDoc e) {
1021                ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, e);
1022            } catch (IllegalAccessException JavaDoc e) {
1023                ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, e);
1024            }
1025        }
1026        
1027    }
1028}
1029
Popular Tags