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 = new JarFile(file);
172                             try
173                             {
174                                 Enumeration e = jarfile.entries();
175                                 while (e.hasMoreElements())
176                                 {
177                                     ZipEntry entry = (ZipEntry)e.nextElement();
178                                     String name = entry.getName();
179                                     if (name.startsWith("META-INF/") && name.toLowerCase().endsWith(".tld"))
180                                     {
181                                         Resource tld=Resource.newResource("jar:"+urls[i]+"!/"+name);
182                                         tlds.add(tld);
183                                         Log.debug("TLD found {}",tld);
184                                     }
185                                 }
186                             }
187                             finally
188                             {
189                                 jarfile.close();
190                             }   
191                         }
192                     }
193                 }
194             }
195 
196             loader=loader.getParent();
197             parent=true;
198             
199         }
200         
201         // Create a TLD parser
202         XmlParser parser = new XmlParser(false);
203         parser.redirectEntity("web-jsptaglib_1_1.dtd",Loader.getResource(TagLibConfiguration.class,"javax/servlet/jsp/resources/web-jsptaglibrary_1_1.dtd", false));
204         parser.redirectEntity("web-jsptaglib_1_2.dtd",Loader.getResource(TagLibConfiguration.class,"javax/servlet/jsp/resources/web-jsptaglibrary_1_2.dtd", false));
205         parser.redirectEntity("web-jsptaglib_2_0.xsd",Loader.getResource(TagLibConfiguration.class,"javax/servlet/jsp/resources/web-jsptaglibrary_2_0.xsd", false));
206         parser.redirectEntity("web-jsptaglibrary_1_1.dtd",Loader.getResource(TagLibConfiguration.class,"javax/servlet/jsp/resources/web-jsptaglibrary_1_1.dtd", false));
207         parser.redirectEntity("web-jsptaglibrary_1_2.dtd",Loader.getResource(TagLibConfiguration.class,"javax/servlet/jsp/resources/web-jsptaglibrary_1_2.dtd", false));
208         parser.redirectEntity("web-jsptaglibrary_2_0.xsd",Loader.getResource(TagLibConfiguration.class,"javax/servlet/jsp/resources/web-jsptaglibrary_2_0.xsd", false));
209         parser.setXpath("/taglib/listener/listener-class");
210         // Parse all the discovered TLDs
211         Iterator iter = tlds.iterator();
212         while (iter.hasNext())
213         {
214             try
215             {
216                 Resource tld = (Resource)iter.next();
217                 if (Log.isDebugEnabled()) Log.debug("TLD="+tld);
218                 
219                 XmlParser.Node root;
220                 
221                 try
222                 {
223                     //xerces on apple appears to sometimes close the zip file instead
224                     //of the inputstream, so try opening the input stream, but if
225                     //that doesn't work, fallback to opening a new url
226                     root = parser.parse(tld.getInputStream());
227                 }
228                 catch (Exception e)
229                 {
230                     root = parser.parse(tld.getURL().toString());
231                 }
232 
233 		if (root==null)
234 		{
235 		    Log.warn("No TLD root in {}",tld);
236 		    continue;
237 		}
238                 
239                 for (int i=0;i<root.size();i++)
240                 {
241                     Object o=root.get(i);
242                     if (o instanceof XmlParser.Node)
243                     {
244                         XmlParser.Node node = (XmlParser.Node)o;
245                         if ("listener".equals(node.getTag()))
246                         {
247                             String className=node.getString("listener-class",false,true);
248                             if (Log.isDebugEnabled()) Log.debug("listener="+className);
249                             
250                             try
251                             {
252                                 Class listenerClass=getWebAppContext().loadClass(className);
253                                 EventListener l=(EventListener)listenerClass.newInstance();
254                                 _context.addEventListener(l);
255                             }
256                             catch(Exception e)
257                             {
258                                 Log.warn("Could not instantiate listener "+className+": "+e);
259                                 Log.debug(e);
260                             }
261                             catch(Error e)
262                             {
263                                 Log.warn("Could not instantiate listener "+className+": "+e);
264                                 Log.debug(e);
265                             }
266                         }
267                     }
268                 }
269             }
270             catch(Exception e)
271             {
272                 Log.warn(e);
273             }
274         }
275     }
276 
277 
278     public void deconfigureWebApp() throws Exception
279     {
280     }
281     
282 
283 }