@@ -157,7 +157,7 @@ public class TldScanner implements ServletContainerInitializer {
157157 // A Cache is used for system jar files.
158158 // The key is the name of the jar file, the value is an array of
159159 // TldInfo, one for each of the TLD in the jar file
160- private static ConcurrentHashMap <String , TldInfo []> jarTldCache =
160+ private static Map <String , TldInfo []> jarTldCache =
161161 new ConcurrentHashMap <String , TldInfo []>();
162162
163163 private static final String EAR_LIB_CLASSLOADER =
@@ -167,18 +167,25 @@ public class TldScanner implements ServletContainerInitializer {
167167 "org.glassfish.jsp.isStandaloneWebapp" ;
168168
169169 /**
170- * The mapping of the 'global' tag library URI to the location (resource
171- * path) of the TLD associated with that tag library. The location is
172- * returned as a String array:
173- * [0] The location
170+ * The mapping of the 'global' tag library URI (as defined in the tld) to
171+ * the location (resource path) of the TLD associated with that tag library.
172+ * The location is returned as a String array:
173+ * [0] The location of the tld file or the jar file that contains the tld
174174 * [1] If the location is a jar file, this is the location of the tld.
175175 */
176176 private HashMap <String , String []> mappings ;
177177
178+ /**
179+ * A local cache for keeping track which jars have been scanned.
180+ */
181+ private Map <String , TldInfo []> jarTldCacheLocal =
182+ new HashMap <String , TldInfo []>();
183+
178184 private ServletContext ctxt ;
179185 private boolean isValidationEnabled ;
180186 private boolean useMyFaces = false ;
181- private boolean scanListeners ; // true if need to scan listeners in tld
187+ private boolean scanListeners ; // true if scan tlds for listeners
188+ private boolean doneScanning ; // true if all tld scanning done
182189
183190
184191 //*********************************************************************
@@ -249,17 +256,36 @@ public void onStartup(java.util.Set<java.lang.Class<?>> c,
249256 * second element denotes the name of the TLD entry in the jar file.
250257 * Returns null if the uri is not associated with any tag library 'exposed'
251258 * in the web application.
259+ *
260+ * This method may be called when the scanning is in one of states:
261+ * 1. Called from jspc script, then a full tld scan is required.
262+ * 2. The is the first call after servlet initialization, then system jars
263+ * that are knwon to have tlds but not listeners need to be scanned.
264+ * 3. Sebsequent calls, no need to scans.
252265 */
253266
254267 @ SuppressWarnings ("unchecked" )
255268 public String [] getLocation (String uri ) throws JasperException {
269+
256270 if (mappings == null ) {
271+ // Recovering the map done in onStart.
257272 mappings = (HashMap <String , String []>) ctxt .getAttribute (
258273 Constants .JSP_TLD_URI_TO_LOCATION_MAP );
259- if (mappings == null ) {
260- scanListeners = false ;
261- scanTlds ();
262- }
274+ }
275+
276+ if (mappings != null && mappings .get (uri ) != null ) {
277+ // if the uri is in, return that, and dont bother to do full scan
278+ return mappings .get (uri );
279+ }
280+
281+ if (! doneScanning ) {
282+ scanListeners = false ;
283+ scanTlds ();
284+ doneScanning = true ;
285+ }
286+ if (mappings == null ) {
287+ // Should never happend
288+ return null ;
263289 }
264290 return mappings .get (uri );
265291 }
@@ -269,11 +295,25 @@ Map<URI, List<String>> getTldMap() {
269295 /*
270296 * System jars with tlds may be passed as a special
271297 * ServletContext attribute
298+ * Map key: a JarURI
299+ * Map value: list of tlds in the jar file
272300 */
273301 return (Map <URI , List <String >>)
274302 ctxt .getAttribute ("com.sun.appserv.tld.map" );
275303 }
276304
305+ @ SuppressWarnings ("unchecked" )
306+ Map <URI , List <String >> getTldListenerMap () {
307+ /*
308+ * System jars with tlds that are known to contain a listener, and
309+ * may be passed as a special ServletContext attribute
310+ * Map key: a JarURI
311+ * Map value: list of tlds in the jar file
312+ */
313+ return (Map <URI , List <String >>)
314+ ctxt .getAttribute ("com.sun.appserv.tldlistener.map" );
315+ }
316+
277317 /**
278318 * Returns the type of a URI:
279319 * ABS_URI
@@ -290,9 +330,21 @@ public static int uriType(String uri) {
290330 }
291331 }
292332
333+ /**
334+ * Scan the all the tlds accessible in the web app.
335+ * For performance reasons, this is done in two stages. At servlet
336+ * initialization time, we only scan the jar files for listeners. The
337+ * container passes a list of system jar files that are known to contain
338+ * tlds with listeners. The rest of the jar files will be scanned when
339+ * a JSP page with a tld referenced is compiled.
340+ */
293341 private void scanTlds () throws JasperException {
294342
295343 mappings = new HashMap <String , String []>();
344+
345+ // Make a local copy of the system jar cache
346+ jarTldCacheLocal .putAll (jarTldCache );
347+
296348 try {
297349 processWebDotXml ();
298350 scanJars ();
@@ -311,6 +363,12 @@ private void scanTlds() throws JasperException {
311363 */
312364 private void processWebDotXml () throws Exception {
313365
366+
367+ // Skip if we are only looking for listeners
368+ if (scanListeners ) {
369+ return ;
370+ }
371+
314372 JspConfigDescriptor jspConfig = ctxt .getJspConfigDescriptor ();
315373 if (jspConfig == null ) {
316374 return ;
@@ -364,17 +422,17 @@ private void scanJar(JarURLConnection conn, List<String> tldNames,
364422 throws JasperException {
365423
366424 String resourcePath = conn .getJarFileURL ().toString ();
367- TldInfo [] cachedTldInfos = jarTldCache .get (resourcePath );
425+ TldInfo [] tldInfos = jarTldCacheLocal .get (resourcePath );
368426
369- // Optimize for most common cases
370- if (cachedTldInfos != null && cachedTldInfos .length == 0 ) {
427+ // Optimize for most common cases: jars known to NOT have tlds
428+ if (tldInfos != null && tldInfos .length == 0 ) {
371429 return ;
372430 }
373431
374- // scan the tld if the jar is local, or if it has not been cached.
375- ArrayList <TldInfo > tldInfoA = new ArrayList <TldInfo >();
376- if (isLocal || cachedTldInfos == null ) {
432+ // scan the tld if the jar has not been cached.
433+ if (tldInfos == null ) {
377434 JarFile jarFile = null ;
435+ ArrayList <TldInfo > tldInfoA = new ArrayList <TldInfo >();
378436 try {
379437 conn .setUseCaches (false );
380438 jarFile = conn .getJarFile ();
@@ -418,15 +476,12 @@ private void scanJar(JarURLConnection conn, List<String> tldNames,
418476 }
419477 }
420478 }
421- }
422-
423- // Update the jar TLD cache
424- TldInfo [] tldInfos = tldInfoA .toArray (new TldInfo [tldInfoA .size ()]);
425- if (! isLocal ) {
426- if (cachedTldInfos == null ) {
479+ // Update the jar TLD cache
480+ tldInfos = tldInfoA .toArray (new TldInfo [tldInfoA .size ()]);
481+ jarTldCacheLocal .put (resourcePath , tldInfos );
482+ if (!isLocal ) {
483+ // Also update the global cache;
427484 jarTldCache .put (resourcePath , tldInfos );
428- } else {
429- tldInfos = cachedTldInfos ;
430485 }
431486 }
432487
@@ -525,8 +580,7 @@ private void processTldsInFileSystem(String startPath)
525580 }
526581
527582 /**
528- * Scan the given TLD for uri and listeners elements. Update the tld map
529- * and register any listeners found.
583+ * Scan the given TLD for uri and listeners elements.
530584 *
531585 * @param resourcePath the resource path for the jar file or the tld file.
532586 * @param entryName If the resource path is a jar file, then the name of
@@ -597,7 +651,13 @@ private void scanJars() throws Exception {
597651 Thread .currentThread ().getContextClassLoader ();
598652 ClassLoader loader = webappLoader ;
599653
600- Map <URI , List <String >> tldMap = getTldMap ();
654+ Map <URI , List <String >> tldMap ;
655+ if (scanListeners ) {
656+ tldMap = getTldListenerMap ();
657+ } else {
658+ tldMap = getTldMap ();
659+ }
660+
601661 Boolean isStandalone = (Boolean )
602662 ctxt .getAttribute (IS_STANDALONE_ATTRIBUTE_NAME );
603663
0 commit comments