View Javadoc

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.File;
19  import java.net.URL;
20  import java.net.URLClassLoader;
21  import java.util.Enumeration;
22  import java.util.EventListener;
23  import java.util.HashSet;
24  import java.util.Iterator;
25  import java.util.Set;
26  import java.util.jar.JarFile;
27  import java.util.regex.Pattern;
28  import java.util.zip.ZipEntry;
29  
30  import org.mortbay.log.Log;
31  import org.mortbay.resource.Resource;
32  import org.mortbay.util.Loader;
33  import org.mortbay.xml.XmlParser;
34  
35  /* ------------------------------------------------------------ */
36  /** TagLibConfiguration.
37   * 
38   * The class searches for TLD descriptors found in web.xml, in WEB-INF/*.tld files of the web app
39   * or *.tld files withing jars found in WEB-INF/lib of the webapp.   Any listeners defined in these
40   * tld's are added to the context.
41   * 
42   * The "org.mortbay.jetty.webapp.NoTLDJarPattern" context init parameter, if set, is used as a 
43   * regular expression to match commonly known jar files known not to contain TLD files (and 
44   * thus not needed to be scanned).
45   * 
46   * <bile>Scanning for TLDs is total rubbish special case for JSPs! If there was a general use-case for web app
47   * frameworks to register listeners directly, then a generic mechanism could have been added to the servlet
48   * spec.  Instead some special purpose JSP support is required that breaks all sorts of encapsualtion rules as
49   * the servlet container must go searching for and then parsing the descriptors for one particular framework.
50   * It only appears to be used by JSF.
51   * </bile>
52   * 
53   * @author gregw
54   *
55   */
56  public class TagLibConfiguration implements Configuration
57  {
58      WebAppContext _context;
59      
60      /* ------------------------------------------------------------ */
61      public void setWebAppContext(WebAppContext context)
62      {
63          _context=context;
64      }
65  
66      /* ------------------------------------------------------------ */
67      public WebAppContext getWebAppContext()
68      {
69          return _context;
70      }
71  
72      /* ------------------------------------------------------------ */
73      public void configureClassLoader() throws Exception
74      {
75      }
76  
77      /* ------------------------------------------------------------ */
78      /* 
79       * @see org.mortbay.jetty.servlet.WebAppContext.Configuration#configureDefaults()
80       */
81      public void configureDefaults() throws Exception
82      {
83      }
84  
85      
86      /* ------------------------------------------------------------ */
87      /* 
88       * @see org.mortbay.jetty.servlet.WebAppContext.Configuration#configureWebApp()
89       */
90      public void configureWebApp() throws Exception
91      {   
92          Set tlds = new HashSet();
93          Set jars = new HashSet();
94          
95          // Find tld's from web.xml
96          // When the XMLConfigurator (or other configurator) parsed the web.xml,
97          // It should have created aliases for all TLDs.  So search resources aliases
98          // for aliases ending in tld
99          if (_context.getResourceAliases()!=null && 
100             _context.getBaseResource()!=null && 
101             _context.getBaseResource().exists())
102         {
103             Iterator iter=_context.getResourceAliases().values().iterator();
104             while(iter.hasNext())
105             {
106                 String location = (String)iter.next();
107                 if (location!=null && location.toLowerCase().endsWith(".tld"))
108                 {
109                     if (!location.startsWith("/"))
110                         location="/WEB-INF/"+location;
111                     Resource l=_context.getBaseResource().addPath(location);
112                     tlds.add(l);
113                 }
114             }
115         }
116         
117         // Look for any tlds in WEB-INF directly.
118         Resource web_inf = _context.getWebInf();
119         if (web_inf!=null)
120         {
121             String[] contents = web_inf.list();
122             for (int i=0;contents!=null && i<contents.length;i++)
123             {
124                 if (contents[i]!=null && contents[i].toLowerCase().endsWith(".tld"))
125                 {
126                     Resource l=_context.getWebInf().addPath(contents[i]);
127                     tlds.add(l);
128                 }
129                 
130             }
131         }
132         
133         // Get the pattern for noTLDJars
134         String no_TLD_attr = _context.getInitParameter("org.mortbay.jetty.webapp.NoTLDJarPattern");
135         Pattern no_TLD_pattern = no_TLD_attr==null?null:Pattern.compile(no_TLD_attr);
136         
137         // Look for tlds in any jars
138  
139         ClassLoader loader = Thread.currentThread().getContextClassLoader();
140         boolean parent=false;
141         
142         while (loader!=null)
143         {
144             if (loader instanceof URLClassLoader)
145             {
146                 URL[] urls = ((URLClassLoader)loader).getURLs();
147 
148                 if (urls!=null)
149                 {
150                     for (int i=0;i<urls.length;i++)
151                     {   
152                         if (urls[i].toString().toLowerCase().endsWith(".jar"))
153                         {
154 
155                             String jar = urls[i].toString();
156                             int slash=jar.lastIndexOf('/');
157                             jar=jar.substring(slash+1);
158 
159                             if (parent && ( 
160                                     (!_context.isParentLoaderPriority() && jars.contains(jar)) || 
161                                     (no_TLD_pattern!=null && no_TLD_pattern.matcher(jar).matches())))
162                                 continue;
163                             jars.add(jar);
164                             
165                             Log.debug("TLD search of {}",urls[i]);
166                             
167                             File file=Resource.newResource(urls[i]).getFile();
168                             if (file==null || !file.exists() || !file.canRead())
169                                 continue;
170                             
171                             JarFile jarfile = null;
172                             try
173                             {
174                                 jarfile = new JarFile(file);
175                                 Enumeration e = jarfile.entries();
176                                 while (e.hasMoreElements())
177                                 {
178                                     ZipEntry entry = (ZipEntry)e.nextElement();
179                                     String name = entry.getName();
180                                     if (name.startsWith("META-INF/") && name.toLowerCase().endsWith(".tld"))
181                                     {
182                                         Resource tld=Resource.newResource("jar:"+urls[i]+"!/"+name);
183                                         tlds.add(tld);
184                                         Log.debug("TLD found {}",tld);
185                                     }
186                                 }
187                             }
188                             catch (Exception e)
189                             {
190                                 Log.warn("Failed to read file: " + file, e); 
191                             }
192                             finally
193                             {
194                                 if (jarfile != null)
195                                 {
196                                     jarfile.close();
197                                 }
198                             }   
199                         }
200                     }
201                 }
202             }
203 
204             loader=loader.getParent();
205             parent=true;
206             
207         }
208         
209         // Create a TLD parser
210         XmlParser parser = new XmlParser(false);
211         parser.redirectEntity("web-jsptaglib_1_1.dtd",Loader.getResource(TagLibConfiguration.class,"javax/servlet/jsp/resources/web-jsptaglibrary_1_1.dtd", false));
212         parser.redirectEntity("web-jsptaglib_1_2.dtd",Loader.getResource(TagLibConfiguration.class,"javax/servlet/jsp/resources/web-jsptaglibrary_1_2.dtd", false));
213         parser.redirectEntity("web-jsptaglib_2_0.xsd",Loader.getResource(TagLibConfiguration.class,"javax/servlet/jsp/resources/web-jsptaglibrary_2_0.xsd", false));
214         parser.redirectEntity("web-jsptaglibrary_1_1.dtd",Loader.getResource(TagLibConfiguration.class,"javax/servlet/jsp/resources/web-jsptaglibrary_1_1.dtd", false));
215         parser.redirectEntity("web-jsptaglibrary_1_2.dtd",Loader.getResource(TagLibConfiguration.class,"javax/servlet/jsp/resources/web-jsptaglibrary_1_2.dtd", false));
216         parser.redirectEntity("web-jsptaglibrary_2_0.xsd",Loader.getResource(TagLibConfiguration.class,"javax/servlet/jsp/resources/web-jsptaglibrary_2_0.xsd", false));
217         parser.setXpath("/taglib/listener/listener-class");
218         // Parse all the discovered TLDs
219         Iterator iter = tlds.iterator();
220         while (iter.hasNext())
221         {
222             try
223             {
224                 Resource tld = (Resource)iter.next();
225                 if (Log.isDebugEnabled()) Log.debug("TLD="+tld);
226                 
227                 XmlParser.Node root;
228                 
229                 try
230                 {
231                     //xerces on apple appears to sometimes close the zip file instead
232                     //of the inputstream, so try opening the input stream, but if
233                     //that doesn't work, fallback to opening a new url
234                     root = parser.parse(tld.getInputStream());
235                 }
236                 catch (Exception e)
237                 {
238                     root = parser.parse(tld.getURL().toString());
239                 }
240 
241 		if (root==null)
242 		{
243 		    Log.warn("No TLD root in {}",tld);
244 		    continue;
245 		}
246                 
247                 for (int i=0;i<root.size();i++)
248                 {
249                     Object o=root.get(i);
250                     if (o instanceof XmlParser.Node)
251                     {
252                         XmlParser.Node node = (XmlParser.Node)o;
253                         if ("listener".equals(node.getTag()))
254                         {
255                             String className=node.getString("listener-class",false,true);
256                             if (Log.isDebugEnabled()) Log.debug("listener="+className);
257                             
258                             try
259                             {
260                                 Class listenerClass=getWebAppContext().loadClass(className);
261                                 EventListener l=(EventListener)listenerClass.newInstance();
262                                 _context.addEventListener(l);
263                             }
264                             catch(Exception e)
265                             {
266                                 Log.warn("Could not instantiate listener "+className+": "+e);
267                                 Log.debug(e);
268                             }
269                             catch(Error e)
270                             {
271                                 Log.warn("Could not instantiate listener "+className+": "+e);
272                                 Log.debug(e);
273                             }
274                         }
275                     }
276                 }
277             }
278             catch(Exception e)
279             {
280                 Log.warn(e);
281             }
282         }
283     }
284 
285 
286     public void deconfigureWebApp() throws Exception
287     {
288     }
289     
290 
291 }