001: /*
002: * Copyright 1999,2004 The Apache Software Foundation.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package org.jboss.web.tomcat.service.jasper;
017:
018: import java.util.Set;
019: import java.util.Iterator;
020: import java.util.HashMap;
021: import java.util.ArrayList;
022: import java.util.jar.JarEntry;
023: import java.util.jar.JarInputStream;
024: import java.io.InputStream;
025: import java.io.FileInputStream;
026: import java.io.FileNotFoundException;
027: import java.io.IOException;
028: import java.net.URL;
029: import java.net.URLConnection;
030: import java.net.MalformedURLException;
031: import javax.servlet.ServletContext;
032: import java.util.Map; // SYNC
033:
034: import org.apache.jasper.compiler.TldLocationsCache;
035: import org.apache.jasper.compiler.Localizer;
036: import org.apache.jasper.JasperException;
037: import org.apache.jasper.Constants;
038: import org.apache.jasper.xmlparser.TreeNode;
039: import org.apache.jasper.xmlparser.ParserUtils;
040: import org.jboss.logging.Logger;
041:
042: /**
043: * A prototype TagLibCache that allows one to obtain shared tlds from the
044: * jbossweb sar conf/tlds directory.
045: * @author Scott.Stark@jboss.org
046: * @version $Revision: 41791 $
047: */
048: public class TagLibCache extends TldLocationsCache {
049: private static final String WEB_XML = "/WEB-INF/web.xml";
050: private static final String JAR_FILE_SUFFIX = ".jar";
051:
052: private static Logger log = Logger.getLogger(TagLibCache.class);
053:
054: private ServletContext ctx;
055: private HashMap mappings;
056: private ArrayList tagLibJars;
057:
058: public TagLibCache(ServletContext ctx, ArrayList tagLibJars) {
059: super (ctx, true);
060: this .ctx = ctx;
061: this .tagLibJars = tagLibJars;
062: }
063:
064: /**
065: * Gets the 'location' of the TLD associated with the given taglib 'uri'.
066: *
067: * Returns null if the uri is not associated with any tag library 'exposed'
068: * in the web application. A tag library is 'exposed' either explicitly in
069: * web.xml or implicitly via the uri tag in the TLD of a taglib deployed in a
070: * jar file (WEB-INF/lib).
071: * @param uri The taglib uri
072: * @return An array of two Strings: The first element denotes the real path
073: * to the TLD. If the path to the TLD points to a jar file, then the
074: * second element denotes the name of the TLD entry in the jar file.
075: * Returns null if the uri is not associated with any tag library
076: * 'exposed' in the web application.
077: */
078: public String[] getLocation(String uri) throws JasperException {
079: if (mappings == null)
080: init();
081: String[] locations = (String[]) mappings.get(uri);
082: return locations;
083: }
084:
085: private synchronized void init() throws JasperException {
086: if (mappings != null) {
087: return;
088: }
089:
090: HashMap tmpMappings = null;
091: try {
092: tmpMappings = new HashMap();
093: processWebDotXml(tmpMappings);
094: loadStandardTlds(tmpMappings);
095: processTldsInFileSystem("/WEB-INF/", tmpMappings);
096: } catch (Exception ex) {
097: String msg = Localizer.getMessage(
098: "jsp.error.internal.tldinit", ex.getMessage());
099: throw new JasperException(msg, ex);
100: } finally {
101: mappings = tmpMappings;
102: }
103: }
104:
105: /*
106: * Populates taglib map described in web.xml.
107: */
108: protected void processWebDotXml(Map tmpMappings) throws Exception {
109:
110: InputStream is = null;
111:
112: try {
113: // Acquire input stream to web application deployment descriptor
114: String altDDName = (String) ctx
115: .getAttribute(Constants.ALT_DD_ATTR);
116: if (altDDName != null) {
117: try {
118: is = new FileInputStream(altDDName);
119: } catch (FileNotFoundException e) {
120: log.warn(Localizer.getMessage(
121: "jsp.error.internal.filenotfound",
122: altDDName));
123: }
124: } else {
125: is = ctx.getResourceAsStream(WEB_XML);
126: if (is == null) {
127: log
128: .warn(Localizer.getMessage(
129: "jsp.error.internal.filenotfound",
130: WEB_XML));
131: }
132: }
133:
134: if (is == null) {
135: return;
136: }
137:
138: // Parse the web application deployment descriptor
139: TreeNode webtld = null;
140: // altDDName is the absolute path of the DD
141: if (altDDName != null) {
142: webtld = new ParserUtils().parseXMLDocument(altDDName,
143: is);
144: } else {
145: webtld = new ParserUtils()
146: .parseXMLDocument(WEB_XML, is);
147: }
148:
149: // Allow taglib to be an element of the root or jsp-config (JSP2.0)
150: TreeNode jspConfig = webtld.findChild("jsp-config");
151: if (jspConfig != null) {
152: webtld = jspConfig;
153: }
154: Iterator taglibs = webtld.findChildren("taglib");
155: while (taglibs.hasNext()) {
156:
157: // Parse the next <taglib> element
158: TreeNode taglib = (TreeNode) taglibs.next();
159: String tagUri = null;
160: String tagLoc = null;
161: TreeNode child = taglib.findChild("taglib-uri");
162: if (child != null)
163: tagUri = child.getBody();
164: child = taglib.findChild("taglib-location");
165: if (child != null)
166: tagLoc = child.getBody();
167:
168: // Save this location if appropriate
169: if (tagLoc == null)
170: continue;
171: if (uriType(tagLoc) == NOROOT_REL_URI)
172: tagLoc = "/WEB-INF/" + tagLoc;
173: String tagLoc2 = null;
174: if (tagLoc.endsWith(JAR_FILE_SUFFIX)) {
175: tagLoc = ctx.getResource(tagLoc).toString();
176: tagLoc2 = "META-INF/taglib.tld";
177: }
178: tmpMappings.put(tagUri,
179: new String[] { tagLoc, tagLoc2 }); // SYNC
180: }
181: } finally {
182: if (is != null) {
183: try {
184: is.close();
185: } catch (Throwable t) {
186: }
187: }
188: }
189: }
190:
191: protected void loadStandardTlds(Map tmpMappings)
192: throws MalformedURLException // SYNC
193: {
194: if (tagLibJars.size() == 0)
195: return;
196:
197: // Locate the conf/web.xml
198: ClassLoader loader = Thread.currentThread()
199: .getContextClassLoader();
200: URL web = loader.getResource("conf/web.xml");
201: URL sarURL = new URL(web, "..");
202: for (int n = 0; n < tagLibJars.size(); n++) {
203: String jarPath = (String) tagLibJars.get(n);
204: try {
205: URL url = new URL(sarURL, jarPath);
206: String resourcePath = url.toString();
207: log.debug("Scanning for tlds in: " + resourcePath);
208: URLConnection conn = url.openConnection();
209: conn.setUseCaches(false);
210: scanJar(conn, resourcePath, true, tmpMappings); // SYNC
211: } catch (Exception e) {
212: log.debug("Failed to scan: " + jarPath, e);
213: }
214: }
215: }
216:
217: /*
218: * Searches the filesystem under /WEB-INF for any TLD files, and adds
219: * an implicit map entry to the taglib map for any TLD that has a <uri>
220: * element.
221: */
222: protected void processTldsInFileSystem(String startPath,
223: Map tmpMappings) // SYNC
224: throws Exception {
225:
226: Set dirList = ctx.getResourcePaths(startPath);
227: if (dirList != null) {
228: Iterator it = dirList.iterator();
229: while (it.hasNext()) {
230: String path = (String) it.next();
231: if (path.endsWith("/")) {
232: processTldsInFileSystem(path, tmpMappings); // SYNC
233: }
234: if (path.endsWith(".jar")) {
235: URL resURL = ctx.getResource(path);
236: URLConnection conn = resURL.openConnection();
237: conn.setUseCaches(false);
238: this .scanJar(conn, resURL.toString(), false,
239: tmpMappings); // SYNC
240: } else if (path.endsWith(".tld") == true) {
241: InputStream stream = ctx.getResourceAsStream(path);
242: String uri = null;
243: try {
244: uri = getUriFromTld(path, stream);
245: } finally {
246: if (stream != null) {
247: try {
248: stream.close();
249: } catch (Throwable t) {
250: // do nothing
251: }
252: }
253: }
254: // Add implicit map entry only if its uri is not already
255: // present in the map
256: if (uri != null && tmpMappings.get(uri) == null) // SYNC
257: {
258: tmpMappings.put(uri,
259: new String[] { path, null }); // SYNC
260: }
261: }
262: }
263: }
264: }
265:
266: /**
267: * Scans the given JarInputStream for TLD files located in META-INF (or a
268: * subdirectory of it), adding an implicit map entry to the taglib map for
269: * any TLD that has a <uri> element.
270: * @param conn - the
271: * @param ignore true if any exceptions raised when processing the given JAR
272: * should be ignored, false otherwise
273: */
274: private void scanJar(URLConnection conn, String resourcePath,
275: boolean ignore, Map tmpMappings) // SYNC
276: throws JasperException, IOException {
277: InputStream connIS = conn.getInputStream();
278: JarInputStream jis = new JarInputStream(connIS);
279: try {
280: JarEntry entry = jis.getNextJarEntry();
281: while (entry != null) {
282: String name = entry.getName();
283: if (name.endsWith(".tld") == false) {
284: entry = jis.getNextJarEntry();
285: continue;
286: }
287:
288: EntryInputStream eis = new EntryInputStream(jis);
289: String uri = getUriFromTld(resourcePath, eis);
290: // Add implicit map entry only if its uri is not already
291: // present in the map
292: if (uri != null && tmpMappings.get(uri) == null) // SYNC
293: {
294: tmpMappings.put(uri, new String[] { resourcePath,
295: name }); // SYNC
296: }
297: entry = jis.getNextJarEntry();
298: }
299: } catch (Exception ex) {
300: if (!ignore) {
301: throw new JasperException(ex);
302: }
303: } finally {
304: if (jis != null) {
305: try {
306: jis.close();
307: } catch (Throwable t) {
308: // ignore
309: }
310: }
311:
312: if (connIS != null) {
313: try {
314: connIS.close();
315: } catch (Throwable t) {
316: // ignore
317: }
318: }
319: }
320: }
321:
322: /*
323: * Returns the value of the uri element of the given TLD, or null if the
324: * given TLD does not contain any such element.
325: */
326: private String getUriFromTld(String resourcePath, InputStream in)
327: throws JasperException {
328: // Parse the tag library descriptor at the specified resource path
329: TreeNode tld = new ParserUtils().parseXMLDocument(resourcePath,
330: in);
331: TreeNode uri = tld.findChild("uri");
332: if (uri != null) {
333: String body = uri.getBody();
334: if (body != null)
335: return body;
336: }
337:
338: return null;
339: }
340:
341: /**
342: * Used to ignore the close on the jar entry input stream since this
343: * closes the jar stream, not just the entry.
344: */
345: static class EntryInputStream extends InputStream {
346: private JarInputStream jis;
347:
348: EntryInputStream(JarInputStream jis) {
349: this .jis = jis;
350: }
351:
352: public int read() throws IOException {
353: return jis.read();
354: }
355:
356: public int available() throws IOException {
357: return jis.available();
358: }
359:
360: public void close() throws IOException {
361:
362: }
363:
364: public void reset() throws IOException {
365: jis.reset();
366: }
367:
368: public boolean markSupported() {
369: return jis.markSupported();
370: }
371:
372: public synchronized void mark(int readlimit) {
373: jis.mark(readlimit);
374: }
375:
376: public long skip(long n) throws IOException {
377: return jis.skip(n);
378: }
379:
380: public int read(byte b[], int off, int len) throws IOException {
381: return jis.read(b, off, len);
382: }
383: }
384: }
|