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