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.net.URL;
17  import java.util.ArrayList;
18  import java.util.Collection;
19  import java.util.EventListener;
20  import java.util.HashSet;
21  import java.util.Iterator;
22  import java.util.List;
23  import java.util.Set;
24  import javax.servlet.Servlet;
25  
26  import org.eclipse.jetty.util.Loader;
27  import org.eclipse.jetty.util.log.Log;
28  import org.eclipse.jetty.util.resource.Resource;
29  import org.eclipse.jetty.xml.XmlParser;
30  
31  /* ------------------------------------------------------------ */
32  /** TagLibConfiguration.
33   * 
34   * The class searches for TLD descriptors found in web.xml, in WEB-INF/*.tld files of the web app
35   * or *.tld files within jars found in WEB-INF/lib of the webapp.   Any listeners defined in these
36   * tld's are added to the context.
37   * 
38   * <bile>This is total rubbish special case for JSPs! If there was a general use-case for web app
39   * frameworks to register listeners directly, then a generic mechanism could have been added to the servlet
40   * spec.  Instead some special purpose JSP support is required that breaks all sorts of encapsulation rules as
41   * the servlet container must go searching for and then parsing the descriptors for one particular framework.
42   * It only appears to be used by JSF, which is being developed by the same developer who implemented this
43   * feature in the first place!
44   * </bile>
45   * 
46   * 
47   *
48   */
49  public class TagLibConfiguration implements Configuration
50  {
51      public static final String TLD_RESOURCES = "org.eclipse.jetty.tlds";
52      
53      
54      public class TldProcessor
55      {
56          public static final String TAGLIB_PROCESSOR = "org.eclipse.jetty.tagLibProcessor";
57          XmlParser _parser;
58          WebAppContext _context;
59          List<XmlParser.Node> _roots = new ArrayList<XmlParser.Node>();
60          
61          
62          public TldProcessor (WebAppContext context)
63          throws Exception
64          {
65              _context = context;
66              createParser();
67          }
68          
69          private void createParser ()
70          throws Exception
71          {
72              // Create a TLD parser
73              _parser = new XmlParser(false);
74              
75              URL taglib11=null;
76              URL taglib12=null;
77              URL taglib20=null;
78              URL taglib21=null;
79  
80              try
81              {
82                  Class jsp_page = Loader.loadClass(WebXmlConfiguration.class,"javax.servlet.jsp.JspPage");
83                  taglib11=jsp_page.getResource("javax/servlet/jsp/resources/web-jsptaglibrary_1_1.dtd");
84                  taglib12=jsp_page.getResource("javax/servlet/jsp/resources/web-jsptaglibrary_1_2.dtd");
85                  taglib20=jsp_page.getResource("javax/servlet/jsp/resources/web-jsptaglibrary_2_0.xsd");
86                  taglib21=jsp_page.getResource("javax/servlet/jsp/resources/web-jsptaglibrary_2_1.xsd");
87              }
88              catch(Exception e)
89              {
90                  Log.ignore(e);
91              }
92              finally
93              {
94                  if(taglib11==null)
95                      taglib11=Loader.getResource(Servlet.class,"javax/servlet/jsp/resources/web-jsptaglibrary_1_1.dtd",true);
96                  if(taglib12==null)
97                      taglib12=Loader.getResource(Servlet.class,"javax/servlet/jsp/resources/web-jsptaglibrary_1_2.dtd",true);
98                  if(taglib20==null)
99                      taglib20=Loader.getResource(Servlet.class,"javax/servlet/jsp/resources/web-jsptaglibrary_2_0.xsd",true);
100                 if(taglib21==null)
101                     taglib21=Loader.getResource(Servlet.class,"javax/servlet/jsp/resources/web-jsptaglibrary_2_1.xsd",true);
102             }
103             
104 
105             if(taglib11!=null)
106             {
107                 _parser.redirectEntity("web-jsptaglib_1_1.dtd",taglib11);
108                 _parser.redirectEntity("web-jsptaglibrary_1_1.dtd",taglib11);
109             }
110             if(taglib12!=null)
111             {
112                 _parser.redirectEntity("web-jsptaglib_1_2.dtd",taglib12);
113                 _parser.redirectEntity("web-jsptaglibrary_1_2.dtd",taglib12);
114             }
115             if(taglib20!=null)
116             {
117                 _parser.redirectEntity("web-jsptaglib_2_0.xsd",taglib20);
118                 _parser.redirectEntity("web-jsptaglibrary_2_0.xsd",taglib20);
119             }
120             if(taglib21!=null)
121             {
122                 _parser.redirectEntity("web-jsptaglib_2_1.xsd",taglib21);
123                 _parser.redirectEntity("web-jsptaglibrary_2_1.xsd",taglib21);
124             }
125             
126             _parser.setXpath("/taglib/listener/listener-class");
127         }
128         
129         
130         public XmlParser.Node parse (Resource tld)
131         throws Exception
132         {
133             XmlParser.Node root;
134             try
135             {
136                 //xerces on apple appears to sometimes close the zip file instead
137                 //of the inputstream, so try opening the input stream, but if
138                 //that doesn't work, fallback to opening a new url
139                 root = _parser.parse(tld.getInputStream());
140             }
141             catch (Exception e)
142             {
143                 root = _parser.parse(tld.getURL().toString());
144             }
145 
146             if (root==null)
147             {
148                 Log.warn("No TLD root in {}",tld);
149             }
150             else
151                 _roots.add(root);
152             
153             return root;
154         }
155         
156         public void processRoots ()
157         {
158             for (XmlParser.Node root: _roots)
159                 process(root);
160         }
161         
162         public void process (XmlParser.Node root)
163         {     
164             for (int i=0;i<root.size();i++)
165             {
166                 Object o=root.get(i);
167                 if (o instanceof XmlParser.Node)
168                 {
169                     XmlParser.Node node = (XmlParser.Node)o;
170                     if ("listener".equals(node.getTag()))
171                     {
172                         String className=node.getString("listener-class",false,true);
173                         if (Log.isDebugEnabled()) Log.debug("listener="+className);
174                         
175                         try
176                         {
177                             Class listenerClass = _context.loadClass(className);
178                             EventListener l = (EventListener)listenerClass.newInstance();
179                             _context.addEventListener(l);
180                         }
181                         catch(Exception e)
182                         {
183                             Log.warn("Could not instantiate listener "+className+": "+e);
184                             Log.debug(e);
185                         }
186                         catch(Error e)
187                         {
188                             Log.warn("Could not instantiate listener "+className+": "+e);
189                             Log.debug(e);
190                         }
191                     }
192                 }
193             }
194         }
195         
196     }
197 
198 
199     public void preConfigure(WebAppContext context) throws Exception
200     {
201         Set tlds = new HashSet();
202 
203         // Find tld's from web.xml
204         // When web.xml was processed, it should have created aliases for all TLDs.  So search resources aliases
205         // for aliases ending in tld
206         if (context.getResourceAliases()!=null && 
207                 context.getBaseResource()!=null && 
208                 context.getBaseResource().exists())
209         {
210             Iterator iter=context.getResourceAliases().values().iterator();
211             while(iter.hasNext())
212             {
213                 String location = (String)iter.next();
214                 if (location!=null && location.toLowerCase().endsWith(".tld"))
215                 {
216                     if (!location.startsWith("/"))
217                         location="/WEB-INF/"+location;
218                     Resource l=context.getBaseResource().addPath(location);
219                     tlds.add(l);
220                 }
221             }
222         }
223         
224         // Look for any tlds in WEB-INF directly.
225         Resource web_inf = context.getWebInf();
226         if (web_inf!=null)
227         {
228             String[] contents = web_inf.list();
229             for (int i=0;contents!=null && i<contents.length;i++)
230             {
231                 if (contents[i]!=null && contents[i].toLowerCase().endsWith(".tld"))
232                 {
233                     Resource l=web_inf.addPath(contents[i]);
234                     tlds.add(l);
235                 }
236             }
237         }
238         
239     
240         // Add in tlds found in META-INF of jars. The jars that will be scanned are controlled by
241         // the patterns defined in the context attributes: org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern,
242         // and org.eclipse.jetty.server.webapp.WebInfIncludeJarPattern
243         Collection<Resource> tld_resources=(Collection<Resource>)context.getAttribute(TLD_RESOURCES);
244         if (tld_resources!=null)
245             tlds.addAll(tld_resources);
246         
247         // Create a processor for the tlds and save it
248         TldProcessor processor = new TldProcessor (context);
249         context.setAttribute(TldProcessor.TAGLIB_PROCESSOR, processor);
250         
251         // Parse the tlds into memory
252         Resource tld = null;
253         Iterator iter = tlds.iterator();
254         while (iter.hasNext())
255         {
256             try
257             {
258                 tld = (Resource)iter.next();
259                 if (Log.isDebugEnabled()) Log.debug("TLD="+tld);
260                 processor.parse(tld);
261             }
262             catch(Exception e)
263             {
264                 Log.warn("Unable to parse TLD: " + tld,e);
265             }
266         }
267     }
268     
269 
270     public void configure (WebAppContext context) throws Exception
271     {         
272         TldProcessor processor = (TldProcessor)context.getAttribute(TldProcessor.TAGLIB_PROCESSOR); 
273         if (processor == null)
274         {
275             Log.warn("No TldProcessor configured, skipping tld processing");
276             return;
277         }
278 
279         //Create listeners from the parsed tld trees
280         processor.processRoots();
281     }
282 
283     public void postConfigure(WebAppContext context) throws Exception
284     {
285         context.setAttribute(TldProcessor.TAGLIB_PROCESSOR, null);
286     }
287 
288     public void deconfigure(WebAppContext context) throws Exception
289     {
290         
291     }
292 
293 }