KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > icesoft > jasper > compiler > TldLocationsCache


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

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

68
69 public class TldLocationsCache {
70
71     // Logger
72
private static Log log = LogFactory.getLog(TldLocationsCache.class);
73
74     /**
75      * The types of URI one may specify for a tag library
76      */

77     public static final int ABS_URI = 0;
78     public static final int ROOT_REL_URI = 1;
79     public static final int NOROOT_REL_URI = 2;
80
81     private static final String JavaDoc WEB_XML = "/WEB-INF/web.xml";
82     private static final String JavaDoc FILE_PROTOCOL = "file:";
83     private static final String JavaDoc JAR_FILE_SUFFIX = ".jar";
84
85     // Names of JARs that are known not to contain any TLDs
86
private static HashSet JavaDoc noTldJars;
87
88     /**
89      * The mapping of the 'global' tag library URI to the location (resource
90      * path) of the TLD associated with that tag library. The location is
91      * returned as a String array: [0] The location [1] If the location is a jar
92      * file, this is the location of the tld.
93      */

94     private Hashtable JavaDoc mappings;
95
96     private boolean initialized;
97     private ExternalContext ctxt;
98     private boolean redeployMode;
99
100     //*********************************************************************
101
// Constructor and Initilizations
102

103     /*
104      * Initializes the set of JARs that are known not to contain any TLDs
105      */

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

175     public TldLocationsCache(ExternalContext ctxt, boolean redeployMode) {
176         this.ctxt = ctxt;
177         this.redeployMode = redeployMode;
178         mappings = new Hashtable JavaDoc();
179         initialized = false;
180     }
181
182     /**
183      * Sets the list of JARs that are known not to contain any TLDs.
184      *
185      * @param jarNames List of comma-separated names of JAR files that are known
186      * not to contain any TLDs
187      */

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

213     public String JavaDoc[] getLocation(String JavaDoc uri) throws JasperException {
214         if (!initialized) {
215             init();
216         }
217         return (String JavaDoc[]) mappings.get(uri);
218     }
219
220     /**
221      * Returns the type of a URI: ABS_URI ROOT_REL_URI NOROOT_REL_URI
222      */

223     public static int uriType(String JavaDoc uri) {
224         if (uri.indexOf(':') != -1) {
225             return ABS_URI;
226         } else if (uri.startsWith("/")) {
227             return ROOT_REL_URI;
228         } else {
229             return NOROOT_REL_URI;
230         }
231     }
232
233     private void init() throws JasperException {
234         if (initialized) return;
235         try {
236             if (log.isDebugEnabled()) {
237                 log.debug("Initializing ICEfaces TldLocationsCache");
238             }
239             processWebDotXml();
240             scanJars();
241             processTldsInFileSystem("/WEB-INF/");
242             initialized = true;
243         } catch (Exception JavaDoc ex) {
244 // if (log.isDebugEnabled()) {
245
// log.debug(ex.getMessage());
246
// }
247
// ex.printStackTrace();
248
throw new JasperException(Localizer.getMessage(
249                     "jsp.error.internal.tldinit", ex.getMessage()));
250         }
251     }
252
253     /*
254      * Populates taglib map described in web.xml.
255      */

256     private void processWebDotXml() throws Exception JavaDoc {
257
258         InputStream JavaDoc is = null;
259
260         try {
261             // Acquire input stream to web application deployment descriptor
262
is = ctxt.getResourceAsStream(WEB_XML);
263             if (is == null) {
264                 if (log.isWarnEnabled()) {
265                     log.warn(Localizer.getMessage(
266                             "jsp.error.internal.filenotfound",
267                             WEB_XML));
268                 }
269                 return;
270             }
271
272             // Parse the web application deployment descriptor
273
TreeNode webtld = new ParserUtils().parseXMLDocument(WEB_XML, is);
274
275             // Allow taglib to be an element of the root or jsp-config (JSP2.0)
276
TreeNode jspConfig = webtld.findChild("jsp-config");
277             if (jspConfig != null) {
278                 webtld = jspConfig;
279             }
280             Iterator JavaDoc taglibs = webtld.findChildren("taglib");
281             while (taglibs.hasNext()) {
282
283                 // Parse the next <taglib> element
284
TreeNode taglib = (TreeNode) taglibs.next();
285                 String JavaDoc tagUri = null;
286                 String JavaDoc tagLoc = null;
287                 TreeNode child = taglib.findChild("taglib-uri");
288                 if (child != null)
289                     tagUri = child.getBody();
290                 child = taglib.findChild("taglib-location");
291                 if (child != null)
292                     tagLoc = child.getBody();
293
294                 // Save this location if appropriate
295
if (tagLoc == null)
296                     continue;
297                 if (uriType(tagLoc) == NOROOT_REL_URI)
298                     tagLoc = "/WEB-INF/" + tagLoc;
299                 String JavaDoc tagLoc2 = null;
300                 if (tagLoc.endsWith(JAR_FILE_SUFFIX)) {
301                     tagLoc = ctxt.getResource(tagLoc).toString();
302                     tagLoc2 = "META-INF/taglib.tld";
303                 }
304                 mappings.put(tagUri, new String JavaDoc[]{tagLoc, tagLoc2});
305             }
306         } finally {
307             if (is != null) {
308                 try {
309                     is.close();
310                 } catch (IOException JavaDoc e) {
311                     if (log.isDebugEnabled()) {
312                         log.debug(e.getLocalizedMessage(), e);
313                     }
314                 }
315             }
316         }
317     }
318
319     /**
320      * Scans the given JarURLConnection for TLD files located in META-INF (or a
321      * subdirectory of it), adding an implicit map entry to the taglib map for
322      * any TLD that has a <uri> element.
323      *
324      * @param conn The JarURLConnection to the JAR file to scan
325      * @param ignore true if any exceptions raised when processing the given JAR
326      * should be ignored, false otherwise
327      */

328     private void scanJar(JarURLConnection JavaDoc conn, boolean ignore)
329             throws JasperException {
330
331         JarFile JavaDoc jarFile = null;
332         String JavaDoc resourcePath = conn.getJarFileURL().toString();
333         try {
334             if (redeployMode) {
335                 conn.setUseCaches(false);
336             }
337             jarFile = conn.getJarFile();
338             Enumeration JavaDoc entries = jarFile.entries();
339             while (entries.hasMoreElements()) {
340                 JarEntry JavaDoc entry = (JarEntry JavaDoc) entries.nextElement();
341                 String JavaDoc name = entry.getName();
342                 if (!name.startsWith("META-INF/")) continue;
343                 if (!name.endsWith(".tld")) continue;
344                 InputStream JavaDoc stream = jarFile.getInputStream(entry);
345                 try {
346                     String JavaDoc uri = getUriFromTld(resourcePath, stream);
347                     // Add implicit map entry only if its uri is not already
348
// present in the map
349
if (uri != null && mappings.get(uri) == null) {
350                         mappings.put(uri, new String JavaDoc[]{resourcePath, name});
351                     }
352                 } finally {
353                     if (stream != null) {
354                         try {
355                             stream.close();
356                         } catch (IOException JavaDoc e) {
357                             if (log.isDebugEnabled()) {
358                                 log.debug(e.getLocalizedMessage(), e);
359                             }
360                         }
361                     }
362                 }
363             }
364         } catch (IOException JavaDoc ex) {
365             if (log.isDebugEnabled()) {
366                 log.debug(ex.getMessage(), ex);
367             }
368             if (!redeployMode) {
369                 // if not in redeploy mode, close the jar in case of an error
370
if (jarFile != null) {
371                     try {
372                         jarFile.close();
373                     } catch (IOException JavaDoc e) {
374                         if (log.isDebugEnabled()) {
375                             log.debug(e.getLocalizedMessage(), e);
376                         }
377                     }
378                 }
379             }
380             if (!ignore) {
381                 throw new JasperException(ex);
382             }
383         } finally {
384             if (redeployMode) {
385                 // if in redeploy mode, always close the jar
386
if (jarFile != null) {
387                     try {
388                         jarFile.close();
389                     } catch (IOException JavaDoc e) {
390                         if (log.isDebugEnabled()) {
391                             log.debug(e.getLocalizedMessage(), e);
392                         }
393                     }
394                 }
395             }
396         }
397     }
398
399     /*
400      * Searches the filesystem under /WEB-INF for any TLD files, and adds
401      * an implicit map entry to the taglib map for any TLD that has a <uri>
402      * element.
403      */

404     private void processTldsInFileSystem(String JavaDoc startPath)
405             throws Exception JavaDoc {
406
407         Set JavaDoc dirList = ctxt.getResourcePaths(startPath);
408         if (dirList != null) {
409             Iterator JavaDoc it = dirList.iterator();
410             while (it.hasNext()) {
411                 String JavaDoc path = (String JavaDoc) it.next();
412                 if (path.endsWith("/")) {
413                     processTldsInFileSystem(path);
414                 }
415                 if (!path.endsWith(".tld")) {
416                     continue;
417                 }
418                 InputStream JavaDoc stream = ctxt.getResourceAsStream(path);
419                 String JavaDoc uri = null;
420                 try {
421                     uri = getUriFromTld(path, stream);
422                 } finally {
423                     if (stream != null) {
424                         try {
425                             stream.close();
426                         } catch (Throwable JavaDoc t) {
427                             // do nothing
428
}
429                     }
430                 }
431                 // Add implicit map entry only if its uri is not already
432
// present in the map
433
if (uri != null && mappings.get(uri) == null) {
434                     mappings.put(uri, new String JavaDoc[]{path, null});
435                 }
436             }
437         }
438     }
439
440     /*
441      * Returns the value of the uri element of the given TLD, or null if the
442      * given TLD does not contain any such element.
443      */

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

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

528     private boolean needScanJar(ClassLoader JavaDoc loader, ClassLoader JavaDoc webappLoader,
529                                 String JavaDoc jarPath) {
530         if (loader == webappLoader) {
531             // JARs under WEB-INF/lib must be scanned unconditionally according
532
// to the spec.
533
return true;
534         } else {
535             String JavaDoc jarName = jarPath;
536             int slash = jarPath.lastIndexOf('/');
537             if (slash >= 0) {
538                 jarName = jarPath.substring(slash + 1);
539             }
540             return (!noTldJars.contains(jarName));
541         }
542     }
543 }
544
Popular Tags