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 }