View Javadoc

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