blob: b54553053980241517ec7e398fbc3634d8a79542 [file] [log] [blame]
Jake Slack03928ae2014-05-13 18:41:56 -07001//
2// ========================================================================
3// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
4// ------------------------------------------------------------------------
5// All rights reserved. This program and the accompanying materials
6// are made available under the terms of the Eclipse Public License v1.0
7// and Apache License v2.0 which accompanies this distribution.
8//
9// The Eclipse Public License is available at
10// http://www.eclipse.org/legal/epl-v10.html
11//
12// The Apache License v2.0 is available at
13// http://www.opensource.org/licenses/apache2.0.php
14//
15// You may elect to redistribute this code under either of these licenses.
16// ========================================================================
17//
18
19package org.eclipse.jetty.webapp;
20
21import java.io.IOException;
22import java.net.URI;
23import java.net.URL;
24import java.util.ArrayList;
25import java.util.Collection;
26import java.util.EventListener;
27import java.util.HashMap;
28import java.util.HashSet;
29import java.util.Iterator;
30import java.util.List;
31import java.util.Locale;
32import java.util.Map;
33import java.util.Set;
34
35import javax.servlet.Servlet;
36import javax.servlet.ServletContextEvent;
37import javax.servlet.ServletContextListener;
38
39import org.eclipse.jetty.util.Loader;
40import org.eclipse.jetty.util.log.Log;
41import org.eclipse.jetty.util.log.Logger;
42import org.eclipse.jetty.util.resource.Resource;
43import org.eclipse.jetty.xml.XmlParser;
44
45/* ------------------------------------------------------------ */
46/** TagLibConfiguration.
47 *
48 * The class searches for TLD descriptors found in web.xml, in WEB-INF/*.tld files of the web app
49 * or *.tld files within jars found in WEB-INF/lib of the webapp. Any listeners defined in these
50 * tld's are added to the context.
51 *
52 * <bile>This is total rubbish special case for JSPs! If there was a general use-case for web app
53 * frameworks to register listeners directly, then a generic mechanism could have been added to the servlet
54 * spec. Instead some special purpose JSP support is required that breaks all sorts of encapsulation rules as
55 * the servlet container must go searching for and then parsing the descriptors for one particular framework.
56 * It only appears to be used by JSF, which is being developed by the same developer who implemented this
57 * feature in the first place!
58 * </bile>
59 *
60 *
61 * Note- this has been superceded by the new TldScanner in jasper which uses ServletContainerInitializer to
62 * find all the listeners in tag libs and register them.
63 */
64public class TagLibConfiguration extends AbstractConfiguration
65{
66 private static final Logger LOG = Log.getLogger(TagLibConfiguration.class);
67
68 public static final String TLD_RESOURCES = "org.eclipse.jetty.tlds";
69
70
71 /**
72 * TagLibListener
73 *
74 * A listener that does the job of finding .tld files that contain
75 * (other) listeners that need to be called by the servlet container.
76 *
77 * This implementation is necessitated by the fact that it is only
78 * after all the Configuration classes have run that we will
79 * parse web.xml/fragments etc and thus find tlds mentioned therein.
80 *
81 * Note: TagLibConfiguration is not used in jetty-8 as jasper (JSP engine)
82 * uses the new TldScanner class - a ServletContainerInitializer from
83 * Servlet Spec 3 - to find all listeners in taglibs and register them
84 * with the servlet container.
85 */
86 public class TagLibListener implements ServletContextListener {
87 private List<EventListener> _tldListeners;
88 private WebAppContext _context;
89
90 public TagLibListener (WebAppContext context) {
91 _context = context;
92 }
93
94 public void contextDestroyed(ServletContextEvent sce)
95 {
96 if (_tldListeners == null)
97 return;
98
99 for (int i=_tldListeners.size()-1; i>=0; i--) {
100 EventListener l = _tldListeners.get(i);
101 if (l instanceof ServletContextListener) {
102 ((ServletContextListener)l).contextDestroyed(sce);
103 }
104 }
105 }
106
107 public void contextInitialized(ServletContextEvent sce)
108 {
109 try
110 {
111 //For jasper 2.1:
112 //Get the system classpath tlds and tell jasper about them, if jasper is on the classpath
113 try
114 {
115
116 ClassLoader loader = _context.getClassLoader();
117 if (loader == null || loader.getParent() == null)
118 loader = getClass().getClassLoader();
119 else
120 loader = loader.getParent();
121 Class<?> clazz = loader.loadClass("org.apache.jasper.compiler.TldLocationsCache");
122 assert clazz!=null;
123 Collection<Resource> tld_resources = (Collection<Resource>)_context.getAttribute(TLD_RESOURCES);
124
125 Map<URI, List<String>> tldMap = new HashMap<URI, List<String>>();
126
127 if (tld_resources != null)
128 {
129 //get the jar file names of the files
130 for (Resource r:tld_resources)
131 {
132 Resource jarResource = extractJarResource(r);
133 //jasper is happy with an empty list of tlds
134 if (!tldMap.containsKey(jarResource.getURI()))
135 tldMap.put(jarResource.getURI(), null);
136
137 }
138 //set the magic context attribute that tells jasper about the system tlds
139 sce.getServletContext().setAttribute("com.sun.appserv.tld.map", tldMap);
140 }
141 }
142 catch (ClassNotFoundException e)
143 {
144 LOG.ignore(e);
145 }
146
147 //find the tld files and parse them to get out their
148 //listeners
149 Set<Resource> tlds = findTldResources();
150 List<TldDescriptor> descriptors = parseTlds(tlds);
151 processTlds(descriptors);
152
153 if (_tldListeners == null)
154 return;
155
156 //call the listeners that are ServletContextListeners, put the
157 //rest into the context's list of listeners to call at the appropriate
158 //moment
159 for (EventListener l:_tldListeners) {
160 if (l instanceof ServletContextListener) {
161 ((ServletContextListener)l).contextInitialized(sce);
162 } else {
163 _context.addEventListener(l);
164 }
165 }
166
167 }
168 catch (Exception e) {
169 LOG.warn(e);
170 }
171 }
172
173
174
175
176 private Resource extractJarResource (Resource r)
177 {
178 if (r == null)
179 return null;
180
181 try
182 {
183 String url = r.getURI().toURL().toString();
184 int idx = url.lastIndexOf("!/");
185 if (idx >= 0)
186 url = url.substring(0, idx);
187 if (url.startsWith("jar:"))
188 url = url.substring(4);
189 return Resource.newResource(url);
190 }
191 catch (IOException e)
192 {
193 LOG.warn(e);
194 return null;
195 }
196 }
197
198 /**
199 * Find all the locations that can harbour tld files that may contain
200 * a listener which the web container is supposed to instantiate and
201 * call.
202 *
203 * @return
204 * @throws IOException
205 */
206 private Set<Resource> findTldResources () throws IOException {
207
208 Set<Resource> tlds = new HashSet<Resource>();
209
210 // Find tld's from web.xml
211 // When web.xml was processed, it should have created aliases for all TLDs. So search resources aliases
212 // for aliases ending in tld
213 if (_context.getResourceAliases()!=null &&
214 _context.getBaseResource()!=null &&
215 _context.getBaseResource().exists())
216 {
217 Iterator<String> iter=_context.getResourceAliases().values().iterator();
218 while(iter.hasNext())
219 {
220 String location = iter.next();
221 if (location!=null && location.toLowerCase(Locale.ENGLISH).endsWith(".tld"))
222 {
223 if (!location.startsWith("/"))
224 location="/WEB-INF/"+location;
225 Resource l=_context.getBaseResource().addPath(location);
226 tlds.add(l);
227 }
228 }
229 }
230
231 // Look for any tlds in WEB-INF directly.
232 Resource web_inf = _context.getWebInf();
233 if (web_inf!=null)
234 {
235 String[] contents = web_inf.list();
236 for (int i=0;contents!=null && i<contents.length;i++)
237 {
238 if (contents[i]!=null && contents[i].toLowerCase(Locale.ENGLISH).endsWith(".tld"))
239 {
240 Resource l=web_inf.addPath(contents[i]);
241 tlds.add(l);
242 }
243 }
244 }
245
246 //Look for tlds in common location of WEB-INF/tlds
247 if (web_inf != null) {
248 Resource web_inf_tlds = _context.getWebInf().addPath("/tlds/");
249 if (web_inf_tlds.exists() && web_inf_tlds.isDirectory()) {
250 String[] contents = web_inf_tlds.list();
251 for (int i=0;contents!=null && i<contents.length;i++)
252 {
253 if (contents[i]!=null && contents[i].toLowerCase(Locale.ENGLISH).endsWith(".tld"))
254 {
255 Resource l=web_inf_tlds.addPath(contents[i]);
256 tlds.add(l);
257 }
258 }
259 }
260 }
261
262 // Add in tlds found in META-INF of jars. The jars that will be scanned are controlled by
263 // the patterns defined in the context attributes: org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern,
264 // and org.eclipse.jetty.server.webapp.WebInfIncludeJarPattern
265 @SuppressWarnings("unchecked")
266 Collection<Resource> tld_resources=(Collection<Resource>)_context.getAttribute(TLD_RESOURCES);
267 if (tld_resources!=null)
268 tlds.addAll(tld_resources);
269
270 return tlds;
271 }
272
273
274 /**
275 * Parse xml into in-memory tree
276 * @param tlds
277 * @return
278 */
279 private List<TldDescriptor> parseTlds (Set<Resource> tlds) {
280 List<TldDescriptor> descriptors = new ArrayList<TldDescriptor>();
281
282 Resource tld = null;
283 Iterator<Resource> iter = tlds.iterator();
284 while (iter.hasNext())
285 {
286 try
287 {
288 tld = iter.next();
289 if (LOG.isDebugEnabled()) LOG.debug("TLD="+tld);
290
291 TldDescriptor d = new TldDescriptor(tld);
292 d.parse();
293 descriptors.add(d);
294 }
295 catch(Exception e)
296 {
297 LOG.warn("Unable to parse TLD: " + tld,e);
298 }
299 }
300 return descriptors;
301 }
302
303
304 /**
305 * Create listeners from the parsed tld trees
306 * @param descriptors
307 * @throws Exception
308 */
309 private void processTlds (List<TldDescriptor> descriptors) throws Exception {
310
311 TldProcessor processor = new TldProcessor();
312 for (TldDescriptor d:descriptors)
313 processor.process(_context, d);
314
315 _tldListeners = new ArrayList<EventListener>(processor.getListeners());
316 }
317 }
318
319
320
321
322 /**
323 * TldDescriptor
324 *
325 *
326 */
327 public static class TldDescriptor extends Descriptor
328 {
329 protected static XmlParser __parserSingleton;
330
331 public TldDescriptor(Resource xml)
332 {
333 super(xml);
334 }
335
336 @Override
337 public void ensureParser() throws ClassNotFoundException
338 {
339 if (__parserSingleton == null)
340 __parserSingleton = newParser();
341 _parser = __parserSingleton;
342 }
343
344 @Override
345 public XmlParser newParser() throws ClassNotFoundException
346 {
347 // Create a TLD parser
348 XmlParser parser = new XmlParser(false);
349
350 URL taglib11=null;
351 URL taglib12=null;
352 URL taglib20=null;
353 URL taglib21=null;
354
355 try
356 {
357 Class<?> jsp_page = Loader.loadClass(WebXmlConfiguration.class,"javax.servlet.jsp.JspPage");
358 taglib11=jsp_page.getResource("javax/servlet/jsp/resources/web-jsptaglibrary_1_1.dtd");
359 taglib12=jsp_page.getResource("javax/servlet/jsp/resources/web-jsptaglibrary_1_2.dtd");
360 taglib20=jsp_page.getResource("javax/servlet/jsp/resources/web-jsptaglibrary_2_0.xsd");
361 taglib21=jsp_page.getResource("javax/servlet/jsp/resources/web-jsptaglibrary_2_1.xsd");
362 }
363 catch(Exception e)
364 {
365 LOG.ignore(e);
366 }
367 finally
368 {
369 if(taglib11==null)
370 taglib11=Loader.getResource(Servlet.class,"javax/servlet/jsp/resources/web-jsptaglibrary_1_1.dtd",true);
371 if(taglib12==null)
372 taglib12=Loader.getResource(Servlet.class,"javax/servlet/jsp/resources/web-jsptaglibrary_1_2.dtd",true);
373 if(taglib20==null)
374 taglib20=Loader.getResource(Servlet.class,"javax/servlet/jsp/resources/web-jsptaglibrary_2_0.xsd",true);
375 if(taglib21==null)
376 taglib21=Loader.getResource(Servlet.class,"javax/servlet/jsp/resources/web-jsptaglibrary_2_1.xsd",true);
377 }
378
379
380 if(taglib11!=null)
381 {
382 redirect(parser, "web-jsptaglib_1_1.dtd",taglib11);
383 redirect(parser, "web-jsptaglibrary_1_1.dtd",taglib11);
384 }
385 if(taglib12!=null)
386 {
387 redirect(parser, "web-jsptaglib_1_2.dtd",taglib12);
388 redirect(parser, "web-jsptaglibrary_1_2.dtd",taglib12);
389 }
390 if(taglib20!=null)
391 {
392 redirect(parser, "web-jsptaglib_2_0.xsd",taglib20);
393 redirect(parser, "web-jsptaglibrary_2_0.xsd",taglib20);
394 }
395 if(taglib21!=null)
396 {
397 redirect(parser, "web-jsptaglib_2_1.xsd",taglib21);
398 redirect(parser, "web-jsptaglibrary_2_1.xsd",taglib21);
399 }
400
401 parser.setXpath("/taglib/listener/listener-class");
402 return parser;
403 }
404
405 public void parse ()
406 throws Exception
407 {
408 ensureParser();
409 try
410 {
411 //xerces on apple appears to sometimes close the zip file instead
412 //of the inputstream, so try opening the input stream, but if
413 //that doesn't work, fallback to opening a new url
414 _root = _parser.parse(_xml.getInputStream());
415 }
416 catch (Exception e)
417 {
418 _root = _parser.parse(_xml.getURL().toString());
419 }
420
421 if (_root==null)
422 {
423 LOG.warn("No TLD root in {}",_xml);
424 }
425 }
426 }
427
428
429 /**
430 * TldProcessor
431 *
432 * Process TldDescriptors representing tag libs to find listeners.
433 */
434 public class TldProcessor extends IterativeDescriptorProcessor
435 {
436 public static final String TAGLIB_PROCESSOR = "org.eclipse.jetty.tagLibProcessor";
437 XmlParser _parser;
438 List<XmlParser.Node> _roots = new ArrayList<XmlParser.Node>();
439 List<EventListener> _listeners;
440
441
442 public TldProcessor ()
443 throws Exception
444 {
445 _listeners = new ArrayList<EventListener>();
446 registerVisitor("listener", this.getClass().getDeclaredMethod("visitListener", __signature));
447 }
448
449
450 public void visitListener (WebAppContext context, Descriptor descriptor, XmlParser.Node node)
451 {
452 String className=node.getString("listener-class",false,true);
453 if (LOG.isDebugEnabled())
454 LOG.debug("listener="+className);
455
456 try
457 {
458 Class<?> listenerClass = context.loadClass(className);
459 EventListener l = (EventListener)listenerClass.newInstance();
460 _listeners.add(l);
461 }
462 catch(Exception e)
463 {
464 LOG.warn("Could not instantiate listener "+className+": "+e);
465 LOG.debug(e);
466 }
467 catch(Error e)
468 {
469 LOG.warn("Could not instantiate listener "+className+": "+e);
470 LOG.debug(e);
471 }
472
473 }
474
475 @Override
476 public void end(WebAppContext context, Descriptor descriptor)
477 {
478 }
479
480 @Override
481 public void start(WebAppContext context, Descriptor descriptor)
482 {
483 }
484
485 public List<EventListener> getListeners() {
486 return _listeners;
487 }
488 }
489
490
491 @Override
492 public void preConfigure(WebAppContext context) throws Exception
493 {
494 try
495 {
496 Class<?> jsp_page = Loader.loadClass(WebXmlConfiguration.class,"javax.servlet.jsp.JspPage");
497 }
498 catch (Exception e)
499 {
500 //no jsp available, don't parse TLDs
501 return;
502 }
503
504 TagLibListener tagLibListener = new TagLibListener(context);
505 context.addEventListener(tagLibListener);
506 }
507
508
509 @Override
510 public void configure (WebAppContext context) throws Exception
511 {
512 }
513
514 @Override
515 public void postConfigure(WebAppContext context) throws Exception
516 {
517 }
518
519
520 @Override
521 public void cloneConfigure(WebAppContext template, WebAppContext context) throws Exception
522 {
523 }
524
525
526 @Override
527 public void deconfigure(WebAppContext context) throws Exception
528 {
529 }
530}