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