KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > jasper > compiler > TldLocationsCache


1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements. See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License. You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */

17
18 package org.apache.jasper.compiler;
19
20 import java.io.InputStream JavaDoc;
21 import java.io.IOException JavaDoc;
22 import java.net.JarURLConnection JavaDoc;
23 import java.net.MalformedURLException JavaDoc;
24 import java.net.URL JavaDoc;
25 import java.net.URLClassLoader JavaDoc;
26 import java.net.URLConnection JavaDoc;
27 import java.util.Enumeration JavaDoc;
28 import java.util.Hashtable JavaDoc;
29 import java.util.HashSet JavaDoc;
30 import java.util.Iterator JavaDoc;
31 import java.util.Set JavaDoc;
32 import java.util.StringTokenizer JavaDoc;
33 import java.util.jar.JarEntry JavaDoc;
34 import java.util.jar.JarFile JavaDoc;
35 import org.xml.sax.InputSource JavaDoc;
36
37 import javax.servlet.ServletContext JavaDoc;
38
39 import org.apache.commons.logging.Log;
40 import org.apache.commons.logging.LogFactory;
41 import org.apache.jasper.Constants;
42 import org.apache.jasper.JasperException;
43 import org.apache.jasper.xmlparser.ParserUtils;
44 import org.apache.jasper.xmlparser.TreeNode;
45
46 /**
47  * A container for all tag libraries that are defined "globally"
48  * for the web application.
49  *
50  * Tag Libraries can be defined globally in one of two ways:
51  * 1. Via <taglib> elements in web.xml:
52  * the uri and location of the tag-library are specified in
53  * the <taglib> element.
54  * 2. Via packaged jar files that contain .tld files
55  * within the META-INF directory, or some subdirectory
56  * of it. The taglib is 'global' if it has the <uri>
57  * element defined.
58  *
59  * A mapping between the taglib URI and its associated TaglibraryInfoImpl
60  * is maintained in this container.
61  * Actually, that's what we'd like to do. However, because of the
62  * way the classes TagLibraryInfo and TagInfo have been defined,
63  * it is not currently possible to share an instance of TagLibraryInfo
64  * across page invocations. A bug has been submitted to the spec lead.
65  * In the mean time, all we do is save the 'location' where the
66  * TLD associated with a taglib URI can be found.
67  *
68  * When a JSP page has a taglib directive, the mappings in this container
69  * are first searched (see method getLocation()).
70  * If a mapping is found, then the location of the TLD is returned.
71  * If no mapping is found, then the uri specified
72  * in the taglib directive is to be interpreted as the location for
73  * the TLD of this tag library.
74  *
75  * @author Pierre Delisle
76  * @author Jan Luehe
77  */

78
79 public class TldLocationsCache {
80
81     // Logger
82
private Log log = LogFactory.getLog(TldLocationsCache.class);
83
84     /**
85      * The types of URI one may specify for a tag library
86      */

87     public static final int ABS_URI = 0;
88     public static final int ROOT_REL_URI = 1;
89     public static final int NOROOT_REL_URI = 2;
90
91     private static final String JavaDoc WEB_XML = "/WEB-INF/web.xml";
92     private static final String JavaDoc FILE_PROTOCOL = "file:";
93     private static final String JavaDoc JAR_FILE_SUFFIX = ".jar";
94
95     // Names of JARs that are known not to contain any TLDs
96
private static HashSet JavaDoc noTldJars;
97
98     /**
99      * The mapping of the 'global' tag library URI to the location (resource
100      * path) of the TLD associated with that tag library. The location is
101      * returned as a String array:
102      * [0] The location
103      * [1] If the location is a jar file, this is the location of the tld.
104      */

105     private Hashtable JavaDoc mappings;
106
107     private boolean initialized;
108     private ServletContext JavaDoc ctxt;
109     private boolean redeployMode;
110
111     //*********************************************************************
112
// Constructor and Initilizations
113

114     /*
115      * Initializes the set of JARs that are known not to contain any TLDs
116      */

117     static {
118         noTldJars = new HashSet JavaDoc();
119         noTldJars.add("ant.jar");
120         noTldJars.add("catalina.jar");
121         noTldJars.add("catalina-ant.jar");
122         noTldJars.add("catalina-cluster.jar");
123         noTldJars.add("catalina-optional.jar");
124         noTldJars.add("catalina-i18n-fr.jar");
125         noTldJars.add("catalina-i18n-ja.jar");
126         noTldJars.add("catalina-i18n-es.jar");
127         noTldJars.add("commons-dbcp.jar");
128         noTldJars.add("commons-modeler.jar");
129         noTldJars.add("commons-logging-api.jar");
130         noTldJars.add("commons-beanutils.jar");
131         noTldJars.add("commons-fileupload-1.0.jar");
132         noTldJars.add("commons-pool.jar");
133         noTldJars.add("commons-digester.jar");
134         noTldJars.add("commons-logging.jar");
135         noTldJars.add("commons-collections.jar");
136         noTldJars.add("commons-el.jar");
137         noTldJars.add("jakarta-regexp-1.2.jar");
138         noTldJars.add("jasper-compiler.jar");
139         noTldJars.add("jasper-runtime.jar");
140         noTldJars.add("jmx.jar");
141         noTldJars.add("jmx-tools.jar");
142         noTldJars.add("jsp-api.jar");
143         noTldJars.add("jkshm.jar");
144         noTldJars.add("jkconfig.jar");
145         noTldJars.add("naming-common.jar");
146         noTldJars.add("naming-resources.jar");
147         noTldJars.add("naming-factory.jar");
148         noTldJars.add("naming-java.jar");
149         noTldJars.add("servlet-api.jar");
150         noTldJars.add("servlets-default.jar");
151         noTldJars.add("servlets-invoker.jar");
152         noTldJars.add("servlets-common.jar");
153         noTldJars.add("servlets-webdav.jar");
154         noTldJars.add("tomcat-util.jar");
155         noTldJars.add("tomcat-http11.jar");
156         noTldJars.add("tomcat-jni.jar");
157         noTldJars.add("tomcat-jk.jar");
158         noTldJars.add("tomcat-jk2.jar");
159         noTldJars.add("tomcat-coyote.jar");
160         noTldJars.add("xercesImpl.jar");
161         noTldJars.add("xmlParserAPIs.jar");
162         noTldJars.add("xml-apis.jar");
163         // JARs from J2SE runtime
164
noTldJars.add("sunjce_provider.jar");
165         noTldJars.add("ldapsec.jar");
166         noTldJars.add("localedata.jar");
167         noTldJars.add("dnsns.jar");
168     }
169     
170     public TldLocationsCache(ServletContext JavaDoc ctxt) {
171         this(ctxt, true);
172     }
173
174     /** Constructor.
175      *
176      * @param ctxt the servlet context of the web application in which Jasper
177      * is running
178      * @param redeployMode if true, then the compiler will allow redeploying
179      * a tag library from the same jar, at the expense of slowing down the
180      * server a bit. Note that this may only work on JDK 1.3.1_01a and later,
181      * because of JDK bug 4211817 fixed in this release.
182      * If redeployMode is false, a faster but less capable mode will be used.
183      */

184     public TldLocationsCache(ServletContext JavaDoc ctxt, boolean redeployMode) {
185         this.ctxt = ctxt;
186         this.redeployMode = redeployMode;
187         mappings = new Hashtable JavaDoc();
188         initialized = false;
189     }
190
191     /**
192      * Sets the list of JARs that are known not to contain any TLDs.
193      *
194      * @param jarNames List of comma-separated names of JAR files that are
195      * known not to contain any TLDs
196      */

197     public static void setNoTldJars(String JavaDoc jarNames) {
198         if (jarNames != null) {
199             noTldJars.clear();
200             StringTokenizer JavaDoc tokenizer = new StringTokenizer JavaDoc(jarNames, ",");
201             while (tokenizer.hasMoreElements()) {
202                 noTldJars.add(tokenizer.nextToken());
203             }
204         }
205     }
206
207     /**
208      * Gets the 'location' of the TLD associated with the given taglib 'uri'.
209      *
210      * Returns null if the uri is not associated with any tag library 'exposed'
211      * in the web application. A tag library is 'exposed' either explicitly in
212      * web.xml or implicitly via the uri tag in the TLD of a taglib deployed
213      * in a jar file (WEB-INF/lib).
214      *
215      * @param uri The taglib uri
216      *
217      * @return An array of two Strings: The first element denotes the real
218      * path to the TLD. If the path to the TLD points to a jar file, then the
219      * second element denotes the name of the TLD entry in the jar file.
220      * Returns null if the uri is not associated with any tag library 'exposed'
221      * in the web application.
222      */

223     public String JavaDoc[] getLocation(String JavaDoc uri) throws JasperException {
224         if (!initialized) {
225             init();
226         }
227         return (String JavaDoc[]) mappings.get(uri);
228     }
229
230     /**
231      * Returns the type of a URI:
232      * ABS_URI
233      * ROOT_REL_URI
234      * NOROOT_REL_URI
235      */

236     public static int uriType(String JavaDoc uri) {
237         if (uri.indexOf(':') != -1) {
238             return ABS_URI;
239         } else if (uri.startsWith("/")) {
240             return ROOT_REL_URI;
241         } else {
242             return NOROOT_REL_URI;
243         }
244     }
245
246     private void init() throws JasperException {
247         if (initialized) return;
248         try {
249             processWebDotXml();
250             scanJars();
251             processTldsInFileSystem("/WEB-INF/");
252             initialized = true;
253         } catch (Exception JavaDoc ex) {
254             throw new JasperException(Localizer.getMessage(
255                     "jsp.error.internal.tldinit", ex.getMessage()));
256         }
257     }
258
259     /*
260      * Populates taglib map described in web.xml.
261      */

262     private void processWebDotXml() throws Exception JavaDoc {
263
264         InputStream JavaDoc is = null;
265
266         try {
267             // Acquire input stream to web application deployment descriptor
268
String JavaDoc altDDName = (String JavaDoc)ctxt.getAttribute(
269                                                     Constants.ALT_DD_ATTR);
270             URL JavaDoc uri = null;
271             if (altDDName != null) {
272                 try {
273                     uri = new URL JavaDoc(FILE_PROTOCOL+altDDName.replace('\\', '/'));
274                 } catch (MalformedURLException JavaDoc e) {
275                     if (log.isWarnEnabled()) {
276                         log.warn(Localizer.getMessage(
277                                             "jsp.error.internal.filenotfound",
278                                             altDDName));
279                     }
280                 }
281             } else {
282                 uri = ctxt.getResource(WEB_XML);
283                 if (uri == null && log.isWarnEnabled()) {
284                     log.warn(Localizer.getMessage(
285                                             "jsp.error.internal.filenotfound",
286                                             WEB_XML));
287                 }
288             }
289
290             if (uri == null) {
291                 return;
292             }
293             is = uri.openStream();
294             InputSource JavaDoc ip = new InputSource JavaDoc(is);
295             ip.setSystemId(uri.toExternalForm());
296
297             // Parse the web application deployment descriptor
298
TreeNode webtld = null;
299             // altDDName is the absolute path of the DD
300
if (altDDName != null) {
301                 webtld = new ParserUtils().parseXMLDocument(altDDName, ip);
302             } else {
303                 webtld = new ParserUtils().parseXMLDocument(WEB_XML, ip);
304             }
305
306             // Allow taglib to be an element of the root or jsp-config (JSP2.0)
307
TreeNode jspConfig = webtld.findChild("jsp-config");
308             if (jspConfig != null) {
309                 webtld = jspConfig;
310             }
311             Iterator JavaDoc taglibs = webtld.findChildren("taglib");
312             while (taglibs.hasNext()) {
313
314                 // Parse the next <taglib> element
315
TreeNode taglib = (TreeNode) taglibs.next();
316                 String JavaDoc tagUri = null;
317                 String JavaDoc tagLoc = null;
318                 TreeNode child = taglib.findChild("taglib-uri");
319                 if (child != null)
320                     tagUri = child.getBody();
321                 child = taglib.findChild("taglib-location");
322                 if (child != null)
323                     tagLoc = child.getBody();
324
325                 // Save this location if appropriate
326
if (tagLoc == null)
327                     continue;
328                 if (uriType(tagLoc) == NOROOT_REL_URI)
329                     tagLoc = "/WEB-INF/" + tagLoc;
330                 String JavaDoc tagLoc2 = null;
331                 if (tagLoc.endsWith(JAR_FILE_SUFFIX)) {
332                     tagLoc = ctxt.getResource(tagLoc).toString();
333                     tagLoc2 = "META-INF/taglib.tld";
334                 }
335                 mappings.put(tagUri, new String JavaDoc[] { tagLoc, tagLoc2 });
336             }
337         } finally {
338             if (is != null) {
339                 try {
340                     is.close();
341                 } catch (Throwable JavaDoc t) {}
342             }
343         }
344     }
345
346     /**
347      * Scans the given JarURLConnection for TLD files located in META-INF
348      * (or a subdirectory of it), adding an implicit map entry to the taglib
349      * map for any TLD that has a <uri> element.
350      *
351      * @param conn The JarURLConnection to the JAR file to scan
352      * @param ignore true if any exceptions raised when processing the given
353      * JAR should be ignored, false otherwise
354      */

355     private void scanJar(JarURLConnection JavaDoc conn, boolean ignore)
356                 throws JasperException {
357
358         JarFile JavaDoc jarFile = null;
359         String JavaDoc resourcePath = conn.getJarFileURL().toString();
360         try {
361             if (redeployMode) {
362                 conn.setUseCaches(false);
363             }
364             jarFile = conn.getJarFile();
365             Enumeration JavaDoc entries = jarFile.entries();
366             while (entries.hasMoreElements()) {
367                 JarEntry JavaDoc entry = (JarEntry JavaDoc) entries.nextElement();
368                 String JavaDoc name = entry.getName();
369                 if (!name.startsWith("META-INF/")) continue;
370                 if (!name.endsWith(".tld")) continue;
371                 InputStream JavaDoc stream = jarFile.getInputStream(entry);
372                 try {
373                     String JavaDoc uri = getUriFromTld(resourcePath, stream);
374                     // Add implicit map entry only if its uri is not already
375
// present in the map
376
if (uri != null && mappings.get(uri) == null) {
377                         mappings.put(uri, new String JavaDoc[]{ resourcePath, name });
378                     }
379                 } finally {
380                     if (stream != null) {
381                         try {
382                             stream.close();
383                         } catch (Throwable JavaDoc t) {
384                             // do nothing
385
}
386                     }
387                 }
388             }
389         } catch (Exception JavaDoc ex) {
390             if (!redeployMode) {
391                 // if not in redeploy mode, close the jar in case of an error
392
if (jarFile != null) {
393                     try {
394                         jarFile.close();
395                     } catch (Throwable JavaDoc t) {
396                         // ignore
397
}
398                 }
399             }
400             if (!ignore) {
401                 throw new JasperException(ex);
402             }
403         } finally {
404             if (redeployMode) {
405                 // if in redeploy mode, always close the jar
406
if (jarFile != null) {
407                     try {
408                         jarFile.close();
409                     } catch (Throwable JavaDoc t) {
410                         // ignore
411
}
412                 }
413             }
414         }
415     }
416
417     /*
418      * Searches the filesystem under /WEB-INF for any TLD files, and adds
419      * an implicit map entry to the taglib map for any TLD that has a <uri>
420      * element.
421      */

422     private void processTldsInFileSystem(String JavaDoc startPath)
423             throws Exception JavaDoc {
424
425         Set JavaDoc dirList = ctxt.getResourcePaths(startPath);
426         if (dirList != null) {
427             Iterator JavaDoc it = dirList.iterator();
428             while (it.hasNext()) {
429                 String JavaDoc path = (String JavaDoc) it.next();
430                 if (path.endsWith("/")) {
431                     processTldsInFileSystem(path);
432                 }
433                 if (!path.endsWith(".tld")) {
434                     continue;
435                 }
436                 InputStream JavaDoc stream = ctxt.getResourceAsStream(path);
437                 String JavaDoc uri = null;
438                 try {
439                     uri = getUriFromTld(path, stream);
440                 } finally {
441                     if (stream != null) {
442                         try {
443                             stream.close();
444                         } catch (Throwable JavaDoc t) {
445                             // do nothing
446
}
447                     }
448                 }
449                 // Add implicit map entry only if its uri is not already
450
// present in the map
451
if (uri != null && mappings.get(uri) == null) {
452                     mappings.put(uri, new String JavaDoc[] { path, null });
453                 }
454             }
455         }
456     }
457
458     /*
459      * Returns the value of the uri element of the given TLD, or null if the
460      * given TLD does not contain any such element.
461      */

462     private String JavaDoc getUriFromTld(String JavaDoc resourcePath, InputStream JavaDoc in)
463         throws JasperException
464     {
465         // Parse the tag library descriptor at the specified resource path
466
TreeNode tld = new ParserUtils().parseXMLDocument(resourcePath, in);
467         TreeNode uri = tld.findChild("uri");
468         if (uri != null) {
469             String JavaDoc body = uri.getBody();
470             if (body != null)
471                 return body;
472         }
473
474         return null;
475     }
476
477     /*
478      * Scans all JARs accessible to the webapp's classloader and its
479      * parent classloaders for TLDs.
480      *
481      * The list of JARs always includes the JARs under WEB-INF/lib, as well as
482      * all shared JARs in the classloader delegation chain of the webapp's
483      * classloader.
484      *
485      * Considering JARs in the classloader delegation chain constitutes a
486      * Tomcat-specific extension to the TLD search
487      * order defined in the JSP spec. It allows tag libraries packaged as JAR
488      * files to be shared by web applications by simply dropping them in a
489      * location that all web applications have access to (e.g.,
490      * <CATALINA_HOME>/common/lib).
491      *
492      * The set of shared JARs to be scanned for TLDs is narrowed down by
493      * the <tt>noTldJars</tt> class variable, which contains the names of JARs
494      * that are known not to contain any TLDs.
495      */

496     private void scanJars() throws Exception JavaDoc {
497
498         ClassLoader JavaDoc webappLoader
499             = Thread.currentThread().getContextClassLoader();
500         ClassLoader JavaDoc loader = webappLoader;
501
502         while (loader != null) {
503             if (loader instanceof URLClassLoader JavaDoc) {
504                 URL JavaDoc[] urls = ((URLClassLoader JavaDoc) loader).getURLs();
505                 for (int i=0; i<urls.length; i++) {
506                     URLConnection JavaDoc conn = urls[i].openConnection();
507                     if (conn instanceof JarURLConnection JavaDoc) {
508                         if (needScanJar(loader, webappLoader,
509                                         ((JarURLConnection JavaDoc) conn).getJarFile().getName())) {
510                             scanJar((JarURLConnection JavaDoc) conn, true);
511                         }
512                     } else {
513                         String JavaDoc urlStr = urls[i].toString();
514                         if (urlStr.startsWith(FILE_PROTOCOL)
515                                 && urlStr.endsWith(JAR_FILE_SUFFIX)
516                                 && needScanJar(loader, webappLoader, urlStr)) {
517                             URL JavaDoc jarURL = new URL JavaDoc("jar:" + urlStr + "!/");
518                             scanJar((JarURLConnection JavaDoc) jarURL.openConnection(),
519                                     true);
520                         }
521                     }
522                 }
523             }
524
525             loader = loader.getParent();
526         }
527     }
528
529     /*
530      * Determines if the JAR file with the given <tt>jarPath</tt> needs to be
531      * scanned for TLDs.
532      *
533      * @param loader The current classloader in the parent chain
534      * @param webappLoader The webapp classloader
535      * @param jarPath The JAR file path
536      *
537      * @return TRUE if the JAR file identified by <tt>jarPath</tt> needs to be
538      * scanned for TLDs, FALSE otherwise
539      */

540     private boolean needScanJar(ClassLoader JavaDoc loader, ClassLoader JavaDoc webappLoader,
541                                 String JavaDoc jarPath) {
542         if (loader == webappLoader) {
543             // JARs under WEB-INF/lib must be scanned unconditionally according
544
// to the spec.
545
return true;
546         } else {
547             String JavaDoc jarName = jarPath;
548             int slash = jarPath.lastIndexOf('/');
549             if (slash >= 0) {
550                 jarName = jarPath.substring(slash + 1);
551             }
552             return (!noTldJars.contains(jarName));
553         }
554     }
555 }
556
Popular Tags