6767import java .net .URLConnection ;
6868import java .util .Enumeration ;
6969import java .util .HashMap ;
70+ import java .util .ArrayList ;
71+ import java .util .concurrent .ConcurrentHashMap ;
7072import java .util .HashSet ;
7173import java .util .Iterator ;
7274import java .util .List ;
124126 *
125127 * @author Pierre Delisle
126128 * @author Jan Luehe
127- * @author Kin-man Chung servlet 3.0 JSP plugin
129+ * @author Kin-man Chung servlet 3.0 JSP plugin, tld cache etc
128130 */
129131
130132public class TldScanner implements ServletContainerInitializer {
@@ -148,6 +150,12 @@ public class TldScanner implements ServletContainerInitializer {
148150 private static HashSet <String > systemUris = new HashSet <String >();
149151 private static HashSet <String > systemUrisJsf = new HashSet <String >();
150152
153+ // A Cache is used for system jar files.
154+ // The key is the name of the jar file, the value is an array of
155+ // TldInfo, one for each of the TLD in the jar file
156+ private static ConcurrentHashMap <String , TldInfo []> jarTldCache =
157+ new ConcurrentHashMap <String , TldInfo []>();
158+
151159 /**
152160 * The mapping of the 'global' tag library URI to the location (resource
153161 * path) of the TLD associated with that tag library. The location is
@@ -318,66 +326,146 @@ private void processWebDotXml() throws Exception {
318326
319327 /**
320328 * Scans the given JarURLConnection for TLD files located in META-INF
321- * (or a subdirectory of it), adding an implicit map entry to the taglib
322- * map for any TLD that has a <uri> element.
329+ * (or a subdirectory of it). If the scanning in is done as part of the
330+ * ServletContextInitializer, the listeners in the tlds in this jar file
331+ * are added to the servlet context, and for any TLD that has a <uri>
332+ * element, an implicit map entry is added to the taglib map.
323333 *
324334 * @param conn The JarURLConnection to the JAR file to scan
325335 * @param tldNames the list of tld element to scan. The null value
326336 * indicates all the tlds in this case.
327- * @param isLocal True is the jar file is under WEB-INF
328- * JAR should be ignored, false otherwise
337+ * @param isLocal True if the jar file is under WEB-INF
338+ * false otherwise
329339 */
330340 private void scanJar (JarURLConnection conn , List <String > tldNames ,
331341 boolean isLocal )
332342 throws JasperException {
333343
334- JarFile jarFile = null ;
335344 String resourcePath = conn .getJarFileURL ().toString ();
336- try {
337- conn .setUseCaches (false );
338- jarFile = conn .getJarFile ();
339- if (tldNames != null ) {
340- for (String tldName : tldNames ) {
341- JarEntry entry = jarFile .getJarEntry (tldName );
342- InputStream stream = jarFile .getInputStream (entry );
343- scanTld (resourcePath , tldName , stream , isLocal );
344- }
345- } else {
346- Enumeration <JarEntry > entries = jarFile .entries ();
347- while (entries .hasMoreElements ()) {
348- JarEntry entry = entries .nextElement ();
349- String name = entry .getName ();
350- if (!name .startsWith ("META-INF/" )) continue ;
351- if (!name .endsWith (".tld" )) continue ;
352- InputStream stream = jarFile .getInputStream (entry );
353- scanTld (resourcePath , name , stream , isLocal );
345+ TldInfo [] cachedTldInfos = jarTldCache .get (resourcePath );
346+
347+ // Optimize for most common cases
348+ if (cachedTldInfos != null && cachedTldInfos .length == 0 ) {
349+ return ;
350+ }
351+
352+ // scan the tld if the jar is local, or if it has not been cached.
353+ ArrayList <TldInfo > tldInfoA = new ArrayList <TldInfo >();
354+ if (isLocal || cachedTldInfos == null ) {
355+ JarFile jarFile = null ;
356+ try {
357+ conn .setUseCaches (false );
358+ jarFile = conn .getJarFile ();
359+ if (tldNames != null ) {
360+ for (String tldName : tldNames ) {
361+ JarEntry entry = jarFile .getJarEntry (tldName );
362+ InputStream stream = jarFile .getInputStream (entry );
363+ tldInfoA .add (scanTld (resourcePath , tldName , stream ));
364+ }
365+ } else {
366+ Enumeration <JarEntry > entries = jarFile .entries ();
367+ while (entries .hasMoreElements ()) {
368+ JarEntry entry = entries .nextElement ();
369+ String name = entry .getName ();
370+ if (!name .startsWith ("META-INF/" )) continue ;
371+ if (!name .endsWith (".tld" )) continue ;
372+ InputStream stream = jarFile .getInputStream (entry );
373+ tldInfoA .add (scanTld (resourcePath , name , stream ));
374+ }
354375 }
355- }
356- } catch (IOException ex ) {
357- if (resourcePath .startsWith (FILE_PROTOCOL ) &&
358- !((new File (resourcePath )).exists ())) {
359- if (log .isLoggable (Level .WARNING )) {
360- log .log (Level .WARNING ,
376+ } catch (IOException ex ) {
377+ if (resourcePath .startsWith (FILE_PROTOCOL ) &&
378+ !((new File (resourcePath )).exists ())) {
379+ if (log .isLoggable (Level .WARNING )) {
380+ log .log (Level .WARNING ,
361381 Localizer .getMessage ("jsp.warn.nojar" ,
362382 resourcePath ),
363383 ex );
384+ }
385+ } else {
386+ throw new JasperException (
387+ Localizer .getMessage ("jsp.error.jar.io" , resourcePath ),
388+ ex );
389+ }
390+ } finally {
391+ if (jarFile != null ) {
392+ try {
393+ jarFile .close ();
394+ } catch (Throwable t ) {
395+ // ignore
396+ }
364397 }
398+ }
399+ }
400+
401+ // Update the jar TLD cache
402+ TldInfo [] tldInfos = tldInfoA .toArray (new TldInfo [tldInfoA .size ()]);
403+ if (! isLocal ) {
404+ if (cachedTldInfos == null ) {
405+ jarTldCache .put (resourcePath , tldInfos );
365406 } else {
366- throw new JasperException (
367- Localizer .getMessage ("jsp.error.jar.io" , resourcePath ),
368- ex );
407+ tldInfos = cachedTldInfos ;
369408 }
370- } finally {
371- if (jarFile != null ) {
372- try {
373- jarFile .close ();
374- } catch (Throwable t ) {
375- // ignore
409+ }
410+
411+ // Iterate over tldinfos to add listeners or to map tldlocations
412+ for (TldInfo tldInfo : tldInfos ) {
413+ if (scanListeners ) {
414+ addListener (tldInfo , isLocal );
415+ }
416+ mapTldLocation (resourcePath , tldInfo , isLocal );
417+ }
418+ }
419+
420+ private void addListener (TldInfo tldInfo , boolean isLocal ) {
421+ String uri = tldInfo .getUri ();
422+ if (!systemUrisJsf .contains (uri )
423+ || (isLocal && useMyFaces )
424+ || (!isLocal && !useMyFaces )) {
425+ for (String listenerClassName : tldInfo .getListeners ()) {
426+ if (log .isLoggable (Level .FINE )) {
427+ log .fine ( "Add tld listener " + listenerClassName );
376428 }
429+ ctxt .addListener (listenerClassName );
377430 }
378431 }
379432 }
380433
434+ private void mapTldLocation (String resourcePath , TldInfo tldInfo ,
435+ boolean isLocal ) {
436+
437+ String uri = tldInfo .getUri ();
438+ if (uri == null ) {
439+ return ;
440+ }
441+
442+ if ((isLocal
443+ // Local tld files override the tlds in the jar files,
444+ // unless it is in a system jar (except when using myfaces)
445+ && mappings .get (uri ) == null
446+ && !systemUris .contains (uri )
447+ && (!systemUrisJsf .contains (uri ) || useMyFaces )
448+ ) ||
449+ (!isLocal
450+ // Jars are scanned bottom up, so jars in WEB-INF override
451+ // thos in the system (except when using myfaces)
452+ && (mappings .get (uri ) == null
453+ || systemUris .contains (uri )
454+ || (systemUrisJsf .contains (uri ) && !useMyFaces )
455+ )
456+ )
457+ ) {
458+ String entryName = tldInfo .getEntryName ();
459+ if (log .isLoggable (Level .FINE )) {
460+ log .fine ("Add tld map from tld in " +
461+ (isLocal ? "WEB-INF" : "jar: " ) + uri + "=>" +
462+ resourcePath + "," + entryName );
463+ }
464+ mappings .put (uri , new String [] {resourcePath , entryName });
465+ }
466+ }
467+
468+
381469 /*
382470 * Searches the filesystem under /WEB-INF for any TLD files, and scans
383471 * them for <uri> and <listener> elements.
@@ -404,7 +492,12 @@ private void processTldsInFileSystem(String startPath)
404492 path ));
405493 }
406494 InputStream stream = ctxt .getResourceAsStream (path );
407- scanTld (path , null , stream , true );
495+ TldInfo tldInfo = scanTld (path , null , stream );
496+ // Add listeners or to map tldlocations for this TLD
497+ if (scanListeners ) {
498+ addListener (tldInfo , true );
499+ }
500+ mapTldLocation (path , tldInfo , true );
408501 }
409502 }
410503 }
@@ -414,13 +507,13 @@ private void processTldsInFileSystem(String startPath)
414507 * and register any listeners found.
415508 *
416509 * @param resourcePath the resource path for the jar file or the tld file.
417- * @entryName If the resource path is a jar file, then the name of the tld
418- * file in the jar, else should be null.
419- * @stream The input stream for the tld
420- * @isLocal True if the tld is a WEB-INF file, false if it is in a jar file.
510+ * @param entryName If the resource path is a jar file, then the name of
511+ * the tld file in the jar, else should be null.
512+ * @param stream The input stream for the tld
513+ * @return The TldInfo for this tld
421514 */
422- private void scanTld (String resourcePath , String entryName ,
423- InputStream stream , boolean isLocal )
515+ private TldInfo scanTld (String resourcePath , String entryName ,
516+ InputStream stream )
424517 throws JasperException {
425518 try {
426519 // Parse the tag library descriptor at the specified resource path
@@ -433,55 +526,23 @@ private void scanTld(String resourcePath, String entryName,
433526 uri = uriNode .getBody ();
434527 }
435528
436- if (scanListeners ) {
437- Iterator <TreeNode >listeners = tld .findChildren ("listener" );
438- while (listeners .hasNext ()) {
439- TreeNode listener = listeners .next ();
440- TreeNode listenerClass = listener .findChild ("listener-class" );
441- if (listenerClass != null ) {
442- String listenerClassName = listenerClass .getBody ();
443- if (listenerClassName != null ) {
444- if (!systemUrisJsf .contains (uri )
445- || (isLocal && useMyFaces )
446- || (!isLocal && !useMyFaces )) {
447- if (log .isLoggable (Level .FINE )) {
448- log .fine ( "Add tld listener " +
449- listenerClassName );
450- }
451- ctxt .addListener (listenerClassName );
452- }
453- }
529+ ArrayList <String > listeners = new ArrayList <String >();
530+
531+ Iterator <TreeNode >listenerNodes = tld .findChildren ("listener" );
532+ while (listenerNodes .hasNext ()) {
533+ TreeNode listener = listenerNodes .next ();
534+ TreeNode listenerClass = listener .findChild ("listener-class" );
535+ if (listenerClass != null ) {
536+ String listenerClassName = listenerClass .getBody ();
537+ if (listenerClassName != null ) {
538+ listeners .add (listenerClassName );
454539 }
455540 }
456541 }
457542
458- if (uri == null ) {
459- return ;
460- }
543+ return new TldInfo (uri , entryName ,
544+ listeners .toArray (new String [listeners .size ()]));
461545
462- if ((isLocal
463- // Local tld files override the tlds in the jar files,
464- // unless it is in a system jar (except when using myfaces)
465- && mappings .get (uri ) == null
466- && !systemUris .contains (uri )
467- && (!systemUrisJsf .contains (uri ) || useMyFaces )
468- ) ||
469- (!isLocal
470- // Jars are scanned bottom up, so jars in WEB-INF override
471- // thos in the system (except when using myfaces)
472- && (mappings .get (uri ) == null
473- || systemUris .contains (uri )
474- || (systemUrisJsf .contains (uri ) && !useMyFaces )
475- )
476- )
477- ) {
478- if (log .isLoggable (Level .FINE )) {
479- log .fine ("Add tld map from tld in " +
480- (isLocal ? "WEB-INF" : "jar: " ) + uri + "=>" +
481- resourcePath + "," + entryName );
482- }
483- mappings .put (uri , new String [] {resourcePath , entryName });
484- }
485546 } finally {
486547 if (stream != null ) {
487548 try {
@@ -549,4 +610,28 @@ private void scanJars() throws Exception {
549610 }
550611 }
551612 }
613+
614+ static class TldInfo {
615+ private String entryName ; // The name of the tld file
616+ private String uri ; // The uri name for the tld
617+ private String [] listeners ; // The listeners in the tld
618+
619+ public TldInfo (String uri , String entryName , String [] listeners ) {
620+ this .uri = uri ;
621+ this .entryName = entryName ;
622+ this .listeners = listeners ;
623+ }
624+
625+ public String getEntryName () {
626+ return entryName ;
627+ }
628+
629+ public String getUri () {
630+ return uri ;
631+ }
632+
633+ public String [] getListeners () {
634+ return listeners ;
635+ }
636+ }
552637}
0 commit comments