1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements.  See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License.  You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */

17 package org.apache.tomcat.util.descriptor.tld;
18
19 import java.io.IOException;
20 import java.io.InputStream;
21 import java.net.URL;
22 import java.util.Objects;
23
24 import org.apache.tomcat.Jar;
25 import org.apache.tomcat.util.scan.JarFactory;
26 import org.apache.tomcat.util.scan.ReferenceCountedJar;
27
28 /**
29  * A TLD Resource Path as defined in JSP 7.3.2.
30  * <p>
31  * This encapsulates references to Tag Library Descriptors that can be located
32  * in different places:
33  * <ul>
34  * <li>As resources within an application</li>
35  * <li>As entries in JAR files included in the application</li>
36  * <li>As resources provided by the container</li>
37  * </ul>
38  * When configuring a mapping from a well-known URI to a TLD, a user is allowed
39  * to specify just the name of a JAR file that implicitly contains a TLD in
40  * <code>META-INF/taglib.tld</code>. Such a mapping must be explicitly converted
41  * to a URL and entryName when using this implementation.
42  */

43 public class TldResourcePath {
44     private final URL url;
45     private final String webappPath;
46     private final String entryName;
47
48     /**
49      * Constructor identifying a TLD resource directly.
50      *
51      * @param url        the location of the TLD
52      * @param webappPath the web application path, if any, of the TLD
53      */

54     public TldResourcePath(URL url, String webappPath) {
55         this(url, webappPath, null);
56     }
57
58     /**
59      * Constructor identifying a TLD packaged within a JAR file.
60      *
61      * @param url        the location of the JAR
62      * @param webappPath the web application path, if any, of the JAR
63      * @param entryName  the name of the entry in the JAR
64      */

65     public TldResourcePath(URL url, String webappPath, String entryName) {
66         this.url = url;
67         this.webappPath = webappPath;
68         this.entryName = entryName;
69     }
70
71     /**
72      * Returns the URL of the TLD or of the JAR containing the TLD.
73      *
74      * @return the URL of the TLD
75      */

76     public URL getUrl() {
77         return url;
78     }
79
80     /**
81      * Returns the path within the web application, if any, that the resource
82      * returned by {@link #getUrl()} was obtained from.
83      *
84      * @return the web application path or @null if the the resource is not
85      *         located within a web application
86      */

87     public String getWebappPath() {
88         return webappPath;
89     }
90
91     /**
92      * Returns the name of the JAR entry that contains the TLD.
93      * May be null to indicate the URL refers directly to the TLD itself.
94      *
95      * @return the name of the JAR entry that contains the TLD
96      */

97     public String getEntryName() {
98         return entryName;
99     }
100
101     /**
102      * Return the external form of the URL representing this TLD.
103      * This can be used as a canonical location for the TLD itself, for example,
104      * as the systemId to use when parsing its XML.
105      *
106      * @return the external form of the URL representing this TLD
107      */

108     public String toExternalForm() {
109         if (entryName == null) {
110             return url.toExternalForm();
111         } else {
112             return "jar:" + url.toExternalForm() + "!/" + entryName;
113         }
114     }
115
116     /**
117      * Opens a stream to access the TLD.
118      *
119      * @return a stream containing the TLD content
120      * @throws IOException if there was a problem opening the stream
121      */

122     public InputStream openStream() throws IOException {
123         if (entryName == null) {
124             return url.openStream();
125         } else {
126             URL entryUrl = JarFactory.getJarEntryURL(url, entryName);
127             return entryUrl.openStream();
128         }
129     }
130
131     public Jar openJar() throws IOException {
132         if (entryName == null) {
133             return null;
134         } else {
135             // Bug 62976
136             // Jar files containing tags are typically opened during initial
137             // compilation and then closed when compilation is complete. The
138             // reference counting wrapper is used because, when background
139             // compilation is enabled, the Jar will need to be accessed (to
140             // check for modifications) after it has been closed at the end
141             // of the compilation stage.
142             // Using a reference counted Jar enables the Jar to be re-opened,
143             // used and then closed again rather than triggering an ISE.
144             return new ReferenceCountedJar(url);
145         }
146     }
147
148     @Override
149     public boolean equals(Object o) {
150         if (this == o) {
151             return true;
152         }
153         if (o == null || getClass() != o.getClass()) {
154             return false;
155         }
156
157         TldResourcePath other = (TldResourcePath) o;
158
159         return url.equals(other.url) &&
160                 Objects.equals(webappPath, other.webappPath) &&
161                 Objects.equals(entryName, other.entryName);
162     }
163
164     @Override
165     public int hashCode() {
166         int result = url.hashCode();
167         result = result * 31 + Objects.hashCode(webappPath);
168         result = result * 31 + Objects.hashCode(entryName);
169         return result;
170     }
171 }
172