1   //========================================================================
2   //$Id: TagLibConfiguration.java,v 1.4 2005/08/13 00:01:27 gregwilkins Exp $
3   //Copyright 2004 Mort Bay Consulting Pty. Ltd.
4   //------------------------------------------------------------------------
5   //Licensed under the Apache License, Version 2.0 (the "License");
6   //you may not use this file except in compliance with the License.
7   //You may obtain a copy of the License at 
8   //http://www.apache.org/licenses/LICENSE-2.0
9   //Unless required by applicable law or agreed to in writing, software
10  //distributed under the License is distributed on an "AS IS" BASIS,
11  //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  //See the License for the specific language governing permissions and
13  //limitations under the License.
14  //========================================================================
15  
16  package org.mortbay.jetty.webapp;
17  
18  import java.net.URL;
19  import java.util.EventListener;
20  import java.util.HashSet;
21  import java.util.Iterator;
22  import java.util.Set;
23  import java.util.jar.JarEntry;
24  import java.util.regex.Pattern;
25  
26  import javax.servlet.Servlet;
27  
28  import org.mortbay.log.Log;
29  import org.mortbay.resource.Resource;
30  import org.mortbay.util.Loader;
31  import org.mortbay.xml.XmlParser;
32  
33  /* ------------------------------------------------------------ */
34  /** TagLibConfiguration.
35   * 
36   * The class searches for TLD descriptors found in web.xml, in WEB-INF/*.tld files of the web app
37   * or *.tld files withing jars found in WEB-INF/lib of the webapp.   Any listeners defined in these
38   * tld's are added to the context.
39   * 
40   * <bile>This is total rubbish special case for JSPs! If there was a general use-case for web app
41   * frameworks to register listeners directly, then a generic mechanism could have been added to the servlet
42   * spec.  Instead some special purpose JSP support is required that breaks all sorts of encapsualtion rules as
43   * the servlet container must go searching for and then parsing the descriptors for one particular framework.
44   * It only appears to be used by JSF, which is being developed by the same developer who implemented this
45   * feature in the first place!
46   * </bile>
47   * 
48   * @author gregw
49   *
50   */
51  public class TagLibConfiguration implements Configuration
52  {
53      public static final String __web_inf_pattern = "org.mortbay.jetty.webapp.WebInfIncludeTLDJarPattern";
54      public static final String __container_pattern = "org.mortbay.jetty.webapp.ContainerIncludeTLDJarPattern";
55      WebAppContext _context;
56  
57  
58      
59  
60       public class TagLibJarScanner extends JarScanner
61       {
62           Set _tlds;
63           
64           public void setTldSet (Set tlds)
65           {
66               _tlds=tlds;
67           }
68           
69           public Set getTldSet ()
70           {
71               return _tlds;
72           }
73  
74           public void processEntry(URL jarUrl, JarEntry entry)
75           {            
76               try
77               {
78                   String name = entry.getName();
79                   if (name.startsWith("META-INF/") && name.toLowerCase().endsWith(".tld"))
80                   {
81                       Resource tld=_context.newResource("jar:"+jarUrl+"!/"+name);
82                       _tlds.add(tld);
83                       Log.debug("TLD found {}",tld);
84                   }
85               }
86               catch (Exception e)
87               {
88                   Log.warn("Problem processing jar entry "+entry, e);
89               }
90           }
91       }
92  
93       /* ------------------------------------------------------------ */
94       public void setWebAppContext(WebAppContext context)
95       {
96           _context=context;
97       }
98       
99       /* ------------------------------------------------------------ */
100      public WebAppContext getWebAppContext()
101      {
102          return _context;
103      }
104      
105 
106     /* ------------------------------------------------------------ */
107     public void configureClassLoader() throws Exception
108     {
109     }
110 
111     /* ------------------------------------------------------------ */
112     /* 
113      * @see org.mortbay.jetty.servlet.WebAppContext.Configuration#configureDefaults()
114      */
115     public void configureDefaults() throws Exception
116     {
117     }
118  
119     
120     /* ------------------------------------------------------------ */
121     /* 
122      * @see org.mortbay.jetty.servlet.WebAppContext.Configuration#configureWebApp()
123      */
124     public void configureWebApp() throws Exception
125     {   
126         
127         Set tlds = new HashSet();
128         
129         
130         // Find tld's from web.xml
131         // When the XMLConfigurator (or other configurator) parsed the web.xml,
132         // It should have created aliases for all TLDs.  So search resources aliases
133         // for aliases ending in tld
134         if (_context.getResourceAliases()!=null && 
135             _context.getBaseResource()!=null && 
136             _context.getBaseResource().exists())
137         {
138             Iterator iter=_context.getResourceAliases().values().iterator();
139             while(iter.hasNext())
140             {
141                 String location = (String)iter.next();
142                 if (location!=null && location.toLowerCase().endsWith(".tld"))
143                 {
144                     if (!location.startsWith("/"))
145                         location="/WEB-INF/"+location;
146                     Resource l=_context.getBaseResource().addPath(location);
147                     tlds.add(l);
148                 }
149             }
150         }
151         
152         // Look for any tlds in WEB-INF directly.
153         Resource web_inf = _context.getWebInf();
154         if (web_inf!=null)
155         {
156             String[] contents = web_inf.list();
157             for (int i=0;contents!=null && i<contents.length;i++)
158             {
159                 if (contents[i]!=null && contents[i].toLowerCase().endsWith(".tld"))
160                 {
161                     Resource l=_context.getWebInf().addPath(contents[i]);
162                     tlds.add(l);
163                 }
164                 
165             }
166         }
167         
168         // Look for tlds in any jars
169         //Use an opt-in style:
170         //
171         //org.mortbay.jetty.webapp.WebInfIncludeTLDJarPattern and
172         //org.mortbay.jetty.webapp.ContainerIncludeTLDJarPattern
173         //
174         //When examining jars in WEB-INF/lib:
175         //   if WebInfIncludeTLDJarPattern is null
176         //       examine ALL for tlds
177         //   else
178         //       examine only files matching pattern
179         //
180         //When examining jars in parent loaders:
181         //    If IncludeTLDJarPattern is null
182         //       examine none
183         //    else
184         //       examine only files matching pattern
185         //
186         String tmp = _context.getInitParameter(__web_inf_pattern);
187         Pattern webInfPattern = (tmp==null?null:Pattern.compile(tmp));
188         tmp = _context.getInitParameter(__container_pattern);
189         Pattern containerPattern = (tmp==null?null:Pattern.compile(tmp));
190 
191         TagLibJarScanner tldScanner = new TagLibJarScanner();
192         tldScanner.setTldSet(tlds);
193         tldScanner.scan(webInfPattern, Thread.currentThread().getContextClassLoader(), true, false);
194         tldScanner.scan(containerPattern, Thread.currentThread().getContextClassLoader().getParent(), false, true);
195         
196         
197         // Create a TLD parser
198         XmlParser parser = new XmlParser(false);
199         
200         URL taglib11=null;
201         URL taglib12=null;
202         URL taglib20=null;
203         URL taglib21=null;
204 
205         try
206         {
207             Class jsp_page = Loader.loadClass(WebXmlConfiguration.class,"javax.servlet.jsp.JspPage");
208             taglib11=jsp_page.getResource("javax/servlet/jsp/resources/web-jsptaglibrary_1_1.dtd");
209             taglib12=jsp_page.getResource("javax/servlet/jsp/resources/web-jsptaglibrary_1_2.dtd");
210             taglib20=jsp_page.getResource("javax/servlet/jsp/resources/web-jsptaglibrary_2_0.xsd");
211             taglib21=jsp_page.getResource("javax/servlet/jsp/resources/web-jsptaglibrary_2_1.xsd");
212         }
213         catch(Exception e)
214         {
215             Log.ignore(e);
216         }
217         finally
218         {
219             if(taglib11==null)
220                 taglib11=Loader.getResource(Servlet.class,"javax/servlet/jsp/resources/web-jsptaglibrary_1_1.dtd",true);
221             if(taglib12==null)
222                 taglib12=Loader.getResource(Servlet.class,"javax/servlet/jsp/resources/web-jsptaglibrary_1_2.dtd",true);
223             if(taglib20==null)
224                 taglib20=Loader.getResource(Servlet.class,"javax/servlet/jsp/resources/web-jsptaglibrary_2_0.xsd",true);
225             if(taglib21==null)
226                 taglib21=Loader.getResource(Servlet.class,"javax/servlet/jsp/resources/web-jsptaglibrary_2_1.xsd",true);
227         }
228         
229 
230         if(taglib11!=null)
231         {
232             parser.redirectEntity("web-jsptaglib_1_1.dtd",taglib11);
233             parser.redirectEntity("web-jsptaglibrary_1_1.dtd",taglib11);
234         }
235         if(taglib12!=null)
236         {
237             parser.redirectEntity("web-jsptaglib_1_2.dtd",taglib12);
238             parser.redirectEntity("web-jsptaglibrary_1_2.dtd",taglib12);
239         }
240         if(taglib20!=null)
241         {
242             parser.redirectEntity("web-jsptaglib_2_0.xsd",taglib20);
243             parser.redirectEntity("web-jsptaglibrary_2_0.xsd",taglib20);
244         }
245         if(taglib21!=null)
246         {
247             parser.redirectEntity("web-jsptaglib_2_1.xsd",taglib21);
248             parser.redirectEntity("web-jsptaglibrary_2_1.xsd",taglib21);
249         }
250         
251         parser.setXpath("/taglib/listener/listener-class");
252         
253         // Parse all the discovered TLDs
254         Iterator iter = tlds.iterator();
255         while (iter.hasNext())
256         {
257             try
258             {
259                 Resource tld = (Resource)iter.next();
260                 if (Log.isDebugEnabled()) Log.debug("TLD="+tld);
261                 
262                 XmlParser.Node root;
263                 
264                 try
265                 {
266                     //xerces on apple appears to sometimes close the zip file instead
267                     //of the inputstream, so try opening the input stream, but if
268                     //that doesn't work, fallback to opening a new url
269                     root = parser.parse(tld.getInputStream());
270                 }
271                 catch (Exception e)
272                 {
273                     root = parser.parse(tld.getURL().toString());
274                 }
275 
276 		if (root==null)
277 		{
278 		    Log.warn("No TLD root in {}",tld);
279 		    continue;
280 		}
281                 
282                 for (int i=0;i<root.size();i++)
283                 {
284                     Object o=root.get(i);
285                     if (o instanceof XmlParser.Node)
286                     {
287                         XmlParser.Node node = (XmlParser.Node)o;
288                         if ("listener".equals(node.getTag()))
289                         {
290                             String className=node.getString("listener-class",false,true);
291                             if (Log.isDebugEnabled()) Log.debug("listener="+className);
292                             
293                             try
294                             {
295                                 Class listenerClass=getWebAppContext().loadClass(className);
296                                 EventListener l=(EventListener)listenerClass.newInstance();
297                                 _context.addEventListener(l);
298                             }
299                             catch(Exception e)
300                             {
301                                 Log.warn("Could not instantiate listener "+className+": "+e);
302                                 Log.debug(e);
303                             }
304                             catch(Error e)
305                             {
306                                 Log.warn("Could not instantiate listener "+className+": "+e);
307                                 Log.debug(e);
308                             }
309                         }
310                     }
311                 }
312             }
313             catch(Exception e)
314             {
315                 Log.warn(e);
316             }
317         }
318     }
319 
320 
321     public void deconfigureWebApp() throws Exception
322     {
323     }
324 
325 
326 }