Imported Upstream version 8.0.14
Emmanuel Bourg
9 years ago
192 | 192 | if not exist "%CATALINA_BASE%\conf\logging.properties" goto noJuliConfig |
193 | 193 | set LOGGING_CONFIG=-Djava.util.logging.config.file="%CATALINA_BASE%\conf\logging.properties" |
194 | 194 | :noJuliConfig |
195 | set JAVA_OPTS=%JAVA_OPTS% %LOGGING_CONFIG% | |
195 | set "JAVA_OPTS=%JAVA_OPTS% %LOGGING_CONFIG%" | |
196 | 196 | |
197 | 197 | if not "%LOGGING_MANAGER%" == "" goto noJuliManager |
198 | 198 | set LOGGING_MANAGER=-Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager |
199 | 199 | :noJuliManager |
200 | set JAVA_OPTS=%JAVA_OPTS% %LOGGING_MANAGER% | |
200 | set "JAVA_OPTS=%JAVA_OPTS% %LOGGING_MANAGER%" | |
201 | 201 | |
202 | 202 | rem ----- Execute The Requested Command --------------------------------------- |
203 | 203 |
24 | 24 | # ----- Version Control Flags ----- |
25 | 25 | version.major=8 |
26 | 26 | version.minor=0 |
27 | version.build=12 | |
27 | version.build=14 | |
28 | 28 | version.patch=0 |
29 | 29 | version.suffix= |
30 | 30 | |
52 | 52 | codesigning.user=request-via-pmc |
53 | 53 | codesigning.pwd=request-via-pmc |
54 | 54 | codesigning.partnercode=request-via-pmc |
55 | codesigning.service=Microsoft Signing | |
55 | 56 | |
56 | 57 | # ----- Settings to use when downloading files ----- |
57 | 58 | trydownload.httpusecaches=true |
174 | 175 | commons-daemon.native.win.home=${commons-daemon.home}/windows |
175 | 176 | commons-daemon.native.win.mgr.exe=${commons-daemon.native.win.home}/prunmgr.exe |
176 | 177 | commons-daemon.native.src.tgz=${commons-daemon.home}/commons-daemon-${commons-daemon.version}-native-src.tar.gz |
177 | commons-daemon.native.win.zip=${commons-daemon.home}/commons-daemon-${commons-daemon.version}-bin-windows.zip | |
178 | commons-daemon.native.win.zip=${commons-daemon.home}/commons-daemon-${commons-daemon.version}-bin-windows-signed.zip | |
178 | 179 | commons-daemon.bin.loc.1=${base-commons.loc.1}/daemon/binaries/commons-daemon-${commons-daemon.version}-bin.tar.gz |
179 | 180 | commons-daemon.bin.loc.2=${base-commons.loc.2}/daemon/binaries/commons-daemon-${commons-daemon.version}-bin.tar.gz |
180 | 181 | commons-daemon.native.src.loc.1=${base-commons.loc.1}/daemon/source/commons-daemon-${commons-daemon.version}-native-src.tar.gz |
181 | 182 | commons-daemon.native.src.loc.2=${base-commons.loc.2}/daemon/source/commons-daemon-${commons-daemon.version}-native-src.tar.gz |
182 | commons-daemon.native.win.loc.1=${base-commons.loc.1}/daemon/binaries/windows/commons-daemon-${commons-daemon.version}-bin-windows.zip | |
183 | commons-daemon.native.win.loc.2=${base-commons.loc.2}/daemon/binaries/windows/commons-daemon-${commons-daemon.version}-bin-windows.zip | |
183 | commons-daemon.native.win.loc.1=${base-commons.loc.1}/daemon/binaries/windows/commons-daemon-${commons-daemon.version}-bin-windows-signed.zip | |
184 | commons-daemon.native.win.loc.2=${base-commons.loc.2}/daemon/binaries/windows/commons-daemon-${commons-daemon.version}-bin-windows-signed.zip | |
184 | 185 | |
185 | 186 | # ----- JUnit Unit Test Suite, version 4.11 or later ----- |
186 | 187 | junit.version=4.11 |
337 | 337 | <patternset id="files.tomcat-api"> |
338 | 338 | <include name="org/apache/tomcat/*" /> |
339 | 339 | <exclude name="org/apache/tomcat/buildutil" /> |
340 | <exclude name="org/apache/tomcat/dbcp" /> | |
340 | 341 | <exclude name="org/apache/tomcat/jni" /> |
341 | 342 | <exclude name="org/apache/tomcat/spdy" /> |
342 | 343 | <exclude name="org/apache/tomcat/util" /> |
1093 | 1094 | <!-- build the jdbc-pool jar and source jar--> |
1094 | 1095 | <echo message="Building Tomcat JDBC pool libraries"/> |
1095 | 1096 | <ant antfile="${tomcat.jdbc.dir}/build.xml" dir="${tomcat.jdbc.dir}" |
1096 | inheritall="false" target="build"> | |
1097 | inheritAll="false" target="build"> | |
1097 | 1098 | <property name="tomcat.pool" value="${tomcat.pool}" /> |
1098 | 1099 | <property name="tomcat.juli.jar" value="${tomcat-juli.jar}" /> |
1099 | 1100 | <property name="skip.download" value="set"/> |
1105 | 1106 | <!-- build the jdbc-pool source jar--> |
1106 | 1107 | <echo message="Building Tomcat JDBC pool src JAR"/> |
1107 | 1108 | <ant antfile="${tomcat.jdbc.dir}/build.xml" dir="${tomcat.jdbc.dir}" |
1108 | inheritall="false" target="build-src"> | |
1109 | inheritAll="false" target="build-src"> | |
1109 | 1110 | <property name="tomcat.pool" value="${tomcat.pool}" /> |
1110 | 1111 | <property name="tomcat.juli.jar" value="${tomcat-juli.jar}" /> |
1111 | 1112 | <property name="skip.download" value="set"/> |
1585 | 1586 | |
1586 | 1587 | <ant antfile="${tomcat.extras}/logging/commons-logging-${commons-logging.version}-src/build2.xml" |
1587 | 1588 | dir="${tomcat.extras}/logging/commons-logging-${commons-logging.version}-src" |
1588 | target="compile" /> | |
1589 | inheritAll="false" target="compile" /> | |
1589 | 1590 | |
1590 | 1591 | <jar jarfile="${tomcat-juli-extras.jar}" |
1591 | 1592 | manifest="${tomcat.manifests}/default.manifest"> |
2128 | 2129 | partnerCode="${codesigning.partnercode}" |
2129 | 2130 | applicationName="Apache Tomcat ${version.major.minor}" |
2130 | 2131 | applicationversion="${version}" |
2131 | signingService="Microsoft Signing"> | |
2132 | signingService="${codesigning.service}"> | |
2132 | 2133 | <fileset dir="${tomcat.release}"> |
2133 | 2134 | <filename name="v${version}/bin/${final.name}.exe"/> |
2134 | 2135 | </fileset> |
2135 | 2136 | </signcode> |
2137 | ||
2138 | <!-- .exe has changed so need to redo MD5 and OpenPGP signature --> | |
2139 | <delete file="${tomcat.release}/v${version}/bin/${final.name}.exe.md5" /> | |
2140 | <delete file="${tomcat.release}/v${version}/bin/${final.name}.exe.asc" /> | |
2141 | <antcall target="md5sum"> | |
2142 | <param name="file" value="${tomcat.release}/v${version}/bin/${final.name}.exe" /> | |
2143 | </antcall> | |
2136 | 2144 | |
2137 | 2145 | </target> |
2138 | 2146 | |
2642 | 2650 | <delete dir="${tomcat.output}" /> |
2643 | 2651 | <!-- Remove the copied catalina.properties --> |
2644 | 2652 | <delete file="java/org/apache/catalina/startup/catalina.properties" /> |
2645 | <ant antfile="${tomcat.jdbc.dir}/build.xml" dir="${tomcat.jdbc.dir}" inheritall="false" target="clean"> | |
2653 | <ant antfile="${tomcat.jdbc.dir}/build.xml" dir="${tomcat.jdbc.dir}" inheritAll="false" target="clean"> | |
2646 | 2654 | <property name="tomcat.pool" value="${tomcat.pool}" /> |
2647 | 2655 | </ant> |
2648 | 2656 | <!-- remove jdbc-pool documentation --> |
2886 | 2894 | |
2887 | 2895 | <!-- ============================ Eclipse ================================ --> |
2888 | 2896 | |
2889 | <target name="ide-eclipse" depends="deploy, extras-webservices-prepare" | |
2897 | <target name="ide-eclipse" | |
2898 | depends="download-compile, extras-webservices-prepare, download-test-compile" | |
2890 | 2899 | description="Prepares the source tree to be built in Eclipse"> |
2891 | 2900 | |
2892 | 2901 | <!-- Copy the sample project files into the root directory --> |
19 | 19 | Documentation at /docs/config/server.html |
20 | 20 | --> |
21 | 21 | <Server port="8005" shutdown="SHUTDOWN"> |
22 | <Listener className="org.apache.catalina.startup.VersionLoggerListener" /> | |
22 | 23 | <!-- Security listener. Documentation at /docs/config/listeners.html |
23 | 24 | <Listener className="org.apache.catalina.security.SecurityListener" /> |
24 | 25 | --> |
184 | 184 | } |
185 | 185 | |
186 | 186 | context.setPropertyResolved(base, property); |
187 | return this.readOnly | |
188 | || this.property(context, base, property).isReadOnly(); | |
187 | return this.readOnly || this.property(context, base, property).isReadOnly(); | |
189 | 188 | } |
190 | 189 | |
191 | 190 | @Override |
280 | 279 | } |
281 | 280 | |
282 | 281 | public boolean isReadOnly() { |
283 | return this.write == null | |
284 | && (null == (this.write = Util.getMethod(this.owner, descriptor.getWriteMethod()))); | |
282 | return this.write == null && | |
283 | (null == (this.write = Util.getMethod(this.owner, descriptor.getWriteMethod()))); | |
285 | 284 | } |
286 | 285 | |
287 | 286 | public Method getWriteMethod() { |
153 | 153 | Class<?> commonType = null, type = null; |
154 | 154 | for (int i = 0; i < sz; i++) { |
155 | 155 | type = this.resolvers[i].getCommonPropertyType(context, base); |
156 | if (type != null | |
157 | && (commonType == null || commonType.isAssignableFrom(type))) { | |
156 | if (type != null && | |
157 | (commonType == null || commonType.isAssignableFrom(type))) { | |
158 | 158 | commonType = type; |
159 | 159 | } |
160 | 160 | } |
73 | 73 | } |
74 | 74 | return template; |
75 | 75 | } catch (MissingResourceException e) { |
76 | return "Missing Resource: '" + name + "' for Locale " | |
77 | + locale.getDisplayName(); | |
76 | return "Missing Resource: '" + name + "' for Locale " + locale.getDisplayName(); | |
78 | 77 | } |
79 | 78 | } |
80 | 79 |
266 | 266 | url.append (scheme); // http, https |
267 | 267 | url.append ("://"); |
268 | 268 | url.append (req.getServerName ()); |
269 | if ((scheme.equals ("http") && port != 80) | |
270 | || (scheme.equals ("https") && port != 443)) { | |
269 | if ((scheme.equals ("http") && port != 80) || (scheme.equals ("https") && port != 443)) { | |
271 | 270 | url.append (':'); |
272 | 271 | url.append (req.getServerPort ()); |
273 | 272 | } |
183 | 183 | { |
184 | 184 | boolean isInterface = false; |
185 | 185 | |
186 | if (from == null || klass == null | |
187 | || (!JspTag.class.isAssignableFrom(klass) | |
188 | && !(isInterface = klass.isInterface()))) { | |
186 | if (from == null || klass == null || (!JspTag.class.isAssignableFrom(klass) && | |
187 | !(isInterface = klass.isInterface()))) { | |
189 | 188 | return null; |
190 | 189 | } |
191 | 190 | |
205 | 204 | parent = ((TagAdapter) parent).getAdaptee(); |
206 | 205 | } |
207 | 206 | |
208 | if ((isInterface && klass.isInstance(parent)) | |
209 | || klass.isAssignableFrom(parent.getClass())) { | |
207 | if ((isInterface && klass.isInstance(parent)) || | |
208 | klass.isAssignableFrom(parent.getClass())) { | |
210 | 209 | return parent; |
211 | 210 | } |
212 | 211 |
30 | 30 | */ |
31 | 31 | WebSocketContainer getContainer(); |
32 | 32 | |
33 | void addMessageHandler(MessageHandler listener) | |
34 | throws IllegalStateException; | |
33 | /** | |
34 | * Registers a {@link MessageHandler} for incoming messages. Only one | |
35 | * {@link MessageHandler} may be registered for each message type (text, | |
36 | * binary, pong). The message type will be derived at runtime from the | |
37 | * provided {@link MessageHandler} instance. It is not always possible to do | |
38 | * this so it is better to use | |
39 | * {@link #addMessageHandler(Class, javax.websocket.MessageHandler.Partial)} | |
40 | * or | |
41 | * {@link #addMessageHandler(Class, javax.websocket.MessageHandler.Whole)}. | |
42 | * | |
43 | * @param listener The message handler for a incoming message | |
44 | * | |
45 | * @throws IllegalStateException If a message handler has already been | |
46 | * registered for the associated message type | |
47 | */ | |
48 | void addMessageHandler(MessageHandler listener) throws IllegalStateException; | |
35 | 49 | |
36 | 50 | Set<MessageHandler> getMessageHandlers(); |
37 | 51 | |
125 | 139 | * this session is associated with. |
126 | 140 | */ |
127 | 141 | Set<Session> getOpenSessions(); |
142 | ||
143 | /** | |
144 | * Registers a {@link MessageHandler} for partial incoming messages. Only | |
145 | * one {@link MessageHandler} may be registered for each message type (text | |
146 | * or binary, pong messages are never presented as partial messages). | |
147 | * | |
148 | * @param clazz The type of message that the given handler is intended | |
149 | * for | |
150 | * @param listener The message handler for a incoming message | |
151 | * | |
152 | * @throws IllegalStateException If a message handler has already been | |
153 | * registered for the associated message type | |
154 | */ | |
155 | <T> void addMessageHandler(Class<T> clazz, MessageHandler.Partial<T> handler) | |
156 | throws IllegalStateException; | |
157 | ||
158 | /** | |
159 | * Registers a {@link MessageHandler} for whole incoming messages. Only | |
160 | * one {@link MessageHandler} may be registered for each message type (text, | |
161 | * binary, pong). | |
162 | * | |
163 | * @param clazz The type of message that the given handler is intended | |
164 | * for | |
165 | * @param listener The message handler for a incoming message | |
166 | * | |
167 | * @throws IllegalStateException If a message handler has already been | |
168 | * registered for the associated message type | |
169 | */ | |
170 | <T> void addMessageHandler(Class<T> clazz, MessageHandler.Whole<T> handler) | |
171 | throws IllegalStateException; | |
128 | 172 | } |
16 | 16 | package org.apache.catalina; |
17 | 17 | |
18 | 18 | import java.net.URL; |
19 | import java.nio.charset.Charset; | |
19 | 20 | import java.util.Locale; |
20 | 21 | import java.util.Map; |
21 | 22 | import java.util.Set; |
1636 | 1637 | * context. |
1637 | 1638 | */ |
1638 | 1639 | public Object getNamingToken(); |
1640 | ||
1641 | /** | |
1642 | * Should this context use the new RFC6265 based cookie parser for | |
1643 | * processing HTTP cookies? The default value is currently false but that | |
1644 | * may change in a future point release. | |
1645 | */ | |
1646 | public void setUseRfc6265(boolean useRfc6265); | |
1647 | ||
1648 | /** | |
1649 | * Does this context use the new RFC6265 based cookie parser for | |
1650 | * processing HTTP cookies? The default value is currently false but that | |
1651 | * may change in a future point release. | |
1652 | */ | |
1653 | public boolean getUseRfc6265(); | |
1654 | ||
1655 | /** | |
1656 | * Specifies the name of the character encoding to use to convert bytes into | |
1657 | * characters when processing cookies using the RFC6265 based cookie parser. | |
1658 | * It has no effect if the RFC6265 parser is not used. | |
1659 | * If an unrecognised character encoding is specified, a warning will be | |
1660 | * logged and the default value of UTF-8 will be used. | |
1661 | */ | |
1662 | public void setCookieEncoding(String encoding); | |
1663 | ||
1664 | /** | |
1665 | * Returns the name of the character encoding used to convert bytes into | |
1666 | * characters when processing cookies using the RFC6265 based cookie parser. | |
1667 | * The default value is UTF-8. | |
1668 | */ | |
1669 | public String getCookieEncoding(); | |
1670 | ||
1671 | /** | |
1672 | * Returns the character set used to convert bytes into characters when | |
1673 | * processing cookies using the RFC6265 based cookie parser. | |
1674 | */ | |
1675 | public Charset getCookieEncodingCharset(); | |
1639 | 1676 | } |
13 | 13 | * See the License for the specific language governing permissions and |
14 | 14 | * limitations under the License. |
15 | 15 | */ |
16 | ||
17 | ||
18 | 16 | package org.apache.catalina; |
19 | 17 | |
20 | 18 | import java.beans.PropertyChangeListener; |
26 | 24 | import org.apache.catalina.connector.Response; |
27 | 25 | import org.apache.tomcat.util.descriptor.web.SecurityConstraint; |
28 | 26 | import org.ietf.jgss.GSSContext; |
27 | ||
29 | 28 | /** |
30 | 29 | * A <b>Realm</b> is a read-only facade for an underlying security realm |
31 | 30 | * used to authenticate individual users, and identify the security roles |
39 | 38 | |
40 | 39 | |
41 | 40 | // ------------------------------------------------------------- Properties |
42 | ||
43 | 41 | |
44 | 42 | /** |
45 | 43 | * Return the Container with which this Realm has been associated. |
56 | 54 | |
57 | 55 | |
58 | 56 | // --------------------------------------------------------- Public Methods |
57 | ||
59 | 58 | /** |
60 | 59 | * Add a property change listener to this component. |
61 | 60 | * |
163 | 162 | */ |
164 | 163 | public boolean hasRole(Wrapper wrapper, Principal principal, String role); |
165 | 164 | |
166 | /** | |
165 | ||
166 | /** | |
167 | 167 | * Enforce any user data constraint required by the security constraint |
168 | 168 | * guarding this request URI. Return <code>true</code> if this constraint |
169 | 169 | * was not violated and processing should continue, or <code>false</code> |
180 | 180 | SecurityConstraint []constraint) |
181 | 181 | throws IOException; |
182 | 182 | |
183 | ||
183 | 184 | /** |
184 | 185 | * Remove a property change listener from this component. |
185 | 186 | * |
186 | 187 | * @param listener The listener to remove |
187 | 188 | */ |
188 | 189 | public void removePropertyChangeListener(PropertyChangeListener listener); |
189 | ||
190 | ||
191 | 190 | } |
13 | 13 | * See the License for the specific language governing permissions and |
14 | 14 | * limitations under the License. |
15 | 15 | */ |
16 | ||
17 | 16 | package org.apache.catalina.ant.jmx; |
18 | 17 | |
19 | import java.io.IOException; | |
20 | import java.net.MalformedURLException; | |
21 | ||
22 | import javax.management.MBeanServerConnection; | |
23 | import javax.management.ObjectName; | |
24 | ||
25 | 18 | import org.apache.tools.ant.BuildException; |
26 | import org.apache.tools.ant.ProjectComponent; | |
27 | import org.apache.tools.ant.taskdefs.condition.Condition; | |
28 | 19 | |
29 | 20 | /** |
30 | 21 | * |
86 | 77 | * @author Peter Rossbach |
87 | 78 | * @since 5.5.10 |
88 | 79 | */ |
89 | public class JMXAccessorCondition extends ProjectComponent implements Condition { | |
80 | public class JMXAccessorCondition extends JMXAccessorConditionBase { | |
90 | 81 | |
91 | 82 | // ----------------------------------------------------- Instance Variables |
92 | 83 | |
93 | private String url = null; | |
94 | private String host = "localhost"; | |
95 | private String port = "8050"; | |
96 | private String password = null; | |
97 | private String username = null; | |
98 | private String name = null; | |
99 | private String attribute; | |
100 | private String value; | |
101 | 84 | private String operation = "==" ; |
102 | 85 | private String type = "long" ; |
103 | private String ref = "jmx.server"; | |
104 | 86 | private String unlessCondition; |
105 | 87 | private String ifCondition; |
106 | 88 | |
89 | ||
107 | 90 | // ----------------------------------------------------- Properties |
108 | 91 | |
109 | 92 | /** |
131 | 114 | public void setType(String type) { |
132 | 115 | this.type = type; |
133 | 116 | } |
134 | /** | |
135 | * @return Returns the attribute. | |
136 | */ | |
137 | public String getAttribute() { | |
138 | return attribute; | |
139 | } | |
140 | /** | |
141 | * @param attribute The attribute to set. | |
142 | */ | |
143 | public void setAttribute(String attribute) { | |
144 | this.attribute = attribute; | |
145 | } | |
146 | /** | |
147 | * @return Returns the host. | |
148 | */ | |
149 | public String getHost() { | |
150 | return host; | |
151 | } | |
152 | /** | |
153 | * @param host The host to set. | |
154 | */ | |
155 | public void setHost(String host) { | |
156 | this.host = host; | |
157 | } | |
158 | /** | |
159 | * @return Returns the name. | |
160 | */ | |
161 | public String getName() { | |
162 | return name; | |
163 | } | |
164 | /** | |
165 | * @param objectName The name to set. | |
166 | */ | |
167 | public void setName(String objectName) { | |
168 | this.name = objectName; | |
169 | } | |
170 | /** | |
171 | * @return Returns the password. | |
172 | */ | |
173 | public String getPassword() { | |
174 | return password; | |
175 | } | |
176 | /** | |
177 | * @param password The password to set. | |
178 | */ | |
179 | public void setPassword(String password) { | |
180 | this.password = password; | |
181 | } | |
182 | /** | |
183 | * @return Returns the port. | |
184 | */ | |
185 | public String getPort() { | |
186 | return port; | |
187 | } | |
188 | /** | |
189 | * @param port The port to set. | |
190 | */ | |
191 | public void setPort(String port) { | |
192 | this.port = port; | |
193 | } | |
194 | /** | |
195 | * @return Returns the url. | |
196 | */ | |
197 | public String getUrl() { | |
198 | return url; | |
199 | } | |
200 | /** | |
201 | * @param url The url to set. | |
202 | */ | |
203 | public void setUrl(String url) { | |
204 | this.url = url; | |
205 | } | |
206 | /** | |
207 | * @return Returns the username. | |
208 | */ | |
209 | public String getUsername() { | |
210 | return username; | |
211 | } | |
212 | /** | |
213 | * @param username The username to set. | |
214 | */ | |
215 | public void setUsername(String username) { | |
216 | this.username = username; | |
217 | } | |
218 | /** | |
219 | * @return Returns the value. | |
220 | */ | |
221 | public String getValue() { | |
222 | return value; | |
223 | } | |
224 | // The setter for the "value" attribute | |
225 | public void setValue(String value) { | |
226 | this.value = value; | |
227 | } | |
228 | ||
229 | /** | |
230 | * @return Returns the ref. | |
231 | */ | |
232 | public String getRef() { | |
233 | return ref; | |
234 | } | |
235 | /** | |
236 | * @param refId The ref to set. | |
237 | */ | |
238 | public void setRef(String refId) { | |
239 | this.ref = refId; | |
240 | } | |
117 | ||
241 | 118 | /** |
242 | 119 | * @return Returns the ifCondition. |
243 | 120 | */ |
251 | 128 | public void setIf(String c) { |
252 | 129 | ifCondition = c; |
253 | 130 | } |
131 | ||
254 | 132 | /** |
255 | 133 | * @return Returns the unlessCondition. |
256 | 134 | */ |
257 | 135 | public String getUnless() { |
258 | 136 | return unlessCondition; |
259 | 137 | } |
260 | ||
261 | 138 | /** |
262 | 139 | * Only execute if a property of the given name does not |
263 | 140 | * exist in the current project. |
268 | 145 | } |
269 | 146 | |
270 | 147 | /** |
271 | * Get JMXConnection (default look at <em>jmx.server</em> project reference from jmxOpen Task) | |
272 | * @return active JMXConnection | |
273 | * @throws MalformedURLException | |
274 | * @throws IOException | |
275 | */ | |
276 | protected MBeanServerConnection getJMXConnection() | |
277 | throws MalformedURLException, IOException { | |
278 | return JMXAccessorTask.accessJMXConnection( | |
279 | getProject(), | |
280 | getUrl(), getHost(), | |
281 | getPort(), getUsername(), getPassword(), ref); | |
282 | } | |
283 | ||
284 | /** | |
285 | * Get value from MBeans attribute | |
286 | * @return The value | |
287 | */ | |
288 | protected String accessJMXValue() { | |
289 | try { | |
290 | Object result = getJMXConnection().getAttribute( | |
291 | new ObjectName(name), attribute); | |
292 | if(result != null) | |
293 | return result.toString(); | |
294 | } catch (Exception e) { | |
295 | // ignore access or connection open errors | |
296 | } | |
297 | return null; | |
298 | } | |
299 | ||
300 | /** | |
301 | 148 | * test the if condition |
302 | 149 | * @return true if there is no if condition, or the named property exists |
303 | 150 | */ |
327 | 174 | */ |
328 | 175 | @Override |
329 | 176 | public boolean eval() { |
177 | String value = getValue(); | |
330 | 178 | if (operation == null) { |
331 | 179 | throw new BuildException("operation attribute is not set"); |
332 | 180 | } |
333 | 181 | if (value == null) { |
334 | 182 | throw new BuildException("value attribute is not set"); |
335 | 183 | } |
336 | if ((name == null || attribute == null)) { | |
184 | if ((getName() == null || getAttribute() == null)) { | |
337 | 185 | throw new BuildException( |
338 | "Must specify a 'attribute', name for equals condition"); | |
186 | "Must specify an MBean name and attribute for condition"); | |
339 | 187 | } |
340 | 188 | if (testIfCondition() && testUnlessCondition()) { |
341 | 189 | String jmxValue = accessJMXValue(); |
0 | /* | |
1 | * Licensed to the Apache Software Foundation (ASF) under one or more | |
2 | * contributor license agreements. See the NOTICE file distributed with | |
3 | * this work for additional information regarding copyright ownership. | |
4 | * The ASF licenses this file to You under the Apache License, Version 2.0 | |
5 | * (the "License"); you may not use this file except in compliance with | |
6 | * the License. You may obtain a copy of the License at | |
7 | * | |
8 | * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
15 | */ | |
16 | package org.apache.catalina.ant.jmx; | |
17 | ||
18 | import java.io.IOException; | |
19 | import java.net.MalformedURLException; | |
20 | ||
21 | import javax.management.MBeanServerConnection; | |
22 | import javax.management.ObjectName; | |
23 | ||
24 | import org.apache.tools.ant.ProjectComponent; | |
25 | import org.apache.tools.ant.taskdefs.condition.Condition; | |
26 | ||
27 | public abstract class JMXAccessorConditionBase extends ProjectComponent implements Condition { | |
28 | ||
29 | private String url = null; | |
30 | private String host = "localhost"; | |
31 | private String port = "8050"; | |
32 | private String password = null; | |
33 | private String username = null; | |
34 | private String name = null; | |
35 | private String attribute; | |
36 | private String value; | |
37 | private String ref = "jmx.server" ; | |
38 | ||
39 | /** | |
40 | * @return Returns the attribute. | |
41 | */ | |
42 | public String getAttribute() { | |
43 | return attribute; | |
44 | } | |
45 | /** | |
46 | * @param attribute The attribute to set. | |
47 | */ | |
48 | public void setAttribute(String attribute) { | |
49 | this.attribute = attribute; | |
50 | } | |
51 | /** | |
52 | * @return Returns the host. | |
53 | */ | |
54 | public String getHost() { | |
55 | return host; | |
56 | } | |
57 | /** | |
58 | * @param host The host to set. | |
59 | */ | |
60 | public void setHost(String host) { | |
61 | this.host = host; | |
62 | } | |
63 | /** | |
64 | * @return Returns the name. | |
65 | */ | |
66 | public String getName() { | |
67 | return name; | |
68 | } | |
69 | /** | |
70 | * @param objectName The name to set. | |
71 | */ | |
72 | public void setName(String objectName) { | |
73 | this.name = objectName; | |
74 | } | |
75 | /** | |
76 | * @return Returns the password. | |
77 | */ | |
78 | public String getPassword() { | |
79 | return password; | |
80 | } | |
81 | /** | |
82 | * @param password The password to set. | |
83 | */ | |
84 | public void setPassword(String password) { | |
85 | this.password = password; | |
86 | } | |
87 | /** | |
88 | * @return Returns the port. | |
89 | */ | |
90 | public String getPort() { | |
91 | return port; | |
92 | } | |
93 | /** | |
94 | * @param port The port to set. | |
95 | */ | |
96 | public void setPort(String port) { | |
97 | this.port = port; | |
98 | } | |
99 | /** | |
100 | * @return Returns the url. | |
101 | */ | |
102 | public String getUrl() { | |
103 | return url; | |
104 | } | |
105 | /** | |
106 | * @param url The url to set. | |
107 | */ | |
108 | public void setUrl(String url) { | |
109 | this.url = url; | |
110 | } | |
111 | /** | |
112 | * @return Returns the username. | |
113 | */ | |
114 | public String getUsername() { | |
115 | return username; | |
116 | } | |
117 | /** | |
118 | * @param username The username to set. | |
119 | */ | |
120 | public void setUsername(String username) { | |
121 | this.username = username; | |
122 | } | |
123 | /** | |
124 | * @return Returns the value. | |
125 | */ | |
126 | public String getValue() { | |
127 | return value; | |
128 | } | |
129 | // The setter for the "value" attribute | |
130 | public void setValue(String value) { | |
131 | this.value = value; | |
132 | } | |
133 | ||
134 | /** | |
135 | * @return Returns the ref. | |
136 | */ | |
137 | public String getRef() { | |
138 | return ref; | |
139 | } | |
140 | /** | |
141 | * @param refId The ref to set. | |
142 | */ | |
143 | public void setRef(String refId) { | |
144 | this.ref = refId; | |
145 | } | |
146 | ||
147 | /** | |
148 | * Get JMXConnection (default look at <em>jmx.server</em> project reference | |
149 | * from jmxOpen Task). | |
150 | * | |
151 | * @return active JMXConnection | |
152 | * @throws MalformedURLException | |
153 | * @throws IOException | |
154 | */ | |
155 | protected MBeanServerConnection getJMXConnection() | |
156 | throws MalformedURLException, IOException { | |
157 | return JMXAccessorTask.accessJMXConnection( | |
158 | getProject(), | |
159 | getUrl(), getHost(), | |
160 | getPort(), getUsername(), getPassword(), ref); | |
161 | } | |
162 | ||
163 | /** | |
164 | * Get value from MBeans attribute. | |
165 | * | |
166 | * @return The value | |
167 | */ | |
168 | protected String accessJMXValue() { | |
169 | try { | |
170 | Object result = getJMXConnection().getAttribute( | |
171 | new ObjectName(name), attribute); | |
172 | if(result != null) | |
173 | return result.toString(); | |
174 | } catch (Exception e) { | |
175 | // ignore access or connection open errors | |
176 | } | |
177 | return null; | |
178 | } | |
179 | } | |
180 |
13 | 13 | * See the License for the specific language governing permissions and |
14 | 14 | * limitations under the License. |
15 | 15 | */ |
16 | ||
17 | 16 | package org.apache.catalina.ant.jmx; |
18 | 17 | |
19 | import java.io.IOException; | |
20 | import java.net.MalformedURLException; | |
21 | ||
22 | import javax.management.MBeanServerConnection; | |
23 | import javax.management.ObjectName; | |
24 | ||
25 | 18 | import org.apache.tools.ant.BuildException; |
26 | import org.apache.tools.ant.ProjectComponent; | |
27 | import org.apache.tools.ant.taskdefs.condition.Condition; | |
28 | 19 | |
29 | 20 | /** |
30 | 21 | * |
65 | 56 | * @author Peter Rossbach |
66 | 57 | * @since 5.5.10 |
67 | 58 | */ |
68 | public class JMXAccessorEqualsCondition extends ProjectComponent implements Condition { | |
59 | public class JMXAccessorEqualsCondition extends JMXAccessorConditionBase { | |
69 | 60 | |
70 | // ----------------------------------------------------- Instance Variables | |
71 | ||
72 | private String url = null; | |
73 | private String host = "localhost"; | |
74 | private String port = "8050"; | |
75 | private String password = null; | |
76 | private String username = null; | |
77 | private String name = null; | |
78 | private String attribute; | |
79 | private String value; | |
80 | private String ref = "jmx.server" ; | |
81 | ||
82 | // ----------------------------------------------------- Properties | |
83 | ||
84 | /** | |
85 | * @return Returns the attribute. | |
86 | */ | |
87 | public String getAttribute() { | |
88 | return attribute; | |
89 | } | |
90 | /** | |
91 | * @param attribute The attribute to set. | |
92 | */ | |
93 | public void setAttribute(String attribute) { | |
94 | this.attribute = attribute; | |
95 | } | |
96 | /** | |
97 | * @return Returns the host. | |
98 | */ | |
99 | public String getHost() { | |
100 | return host; | |
101 | } | |
102 | /** | |
103 | * @param host The host to set. | |
104 | */ | |
105 | public void setHost(String host) { | |
106 | this.host = host; | |
107 | } | |
108 | /** | |
109 | * @return Returns the name. | |
110 | */ | |
111 | public String getName() { | |
112 | return name; | |
113 | } | |
114 | /** | |
115 | * @param objectName The name to set. | |
116 | */ | |
117 | public void setName(String objectName) { | |
118 | this.name = objectName; | |
119 | } | |
120 | /** | |
121 | * @return Returns the password. | |
122 | */ | |
123 | public String getPassword() { | |
124 | return password; | |
125 | } | |
126 | /** | |
127 | * @param password The password to set. | |
128 | */ | |
129 | public void setPassword(String password) { | |
130 | this.password = password; | |
131 | } | |
132 | /** | |
133 | * @return Returns the port. | |
134 | */ | |
135 | public String getPort() { | |
136 | return port; | |
137 | } | |
138 | /** | |
139 | * @param port The port to set. | |
140 | */ | |
141 | public void setPort(String port) { | |
142 | this.port = port; | |
143 | } | |
144 | /** | |
145 | * @return Returns the url. | |
146 | */ | |
147 | public String getUrl() { | |
148 | return url; | |
149 | } | |
150 | /** | |
151 | * @param url The url to set. | |
152 | */ | |
153 | public void setUrl(String url) { | |
154 | this.url = url; | |
155 | } | |
156 | /** | |
157 | * @return Returns the username. | |
158 | */ | |
159 | public String getUsername() { | |
160 | return username; | |
161 | } | |
162 | /** | |
163 | * @param username The username to set. | |
164 | */ | |
165 | public void setUsername(String username) { | |
166 | this.username = username; | |
167 | } | |
168 | /** | |
169 | * @return Returns the value. | |
170 | */ | |
171 | public String getValue() { | |
172 | return value; | |
173 | } | |
174 | // The setter for the "value" attribute | |
175 | public void setValue(String value) { | |
176 | this.value = value; | |
177 | } | |
178 | ||
179 | /** | |
180 | * @return Returns the ref. | |
181 | */ | |
182 | public String getRef() { | |
183 | return ref; | |
184 | } | |
185 | /** | |
186 | * @param refId The ref to set. | |
187 | */ | |
188 | public void setRef(String refId) { | |
189 | this.ref = refId; | |
190 | } | |
191 | ||
192 | protected MBeanServerConnection getJMXConnection() | |
193 | throws MalformedURLException, IOException { | |
194 | return JMXAccessorTask.accessJMXConnection( | |
195 | getProject(), | |
196 | getUrl(), getHost(), | |
197 | getPort(), getUsername(), getPassword(), ref); | |
198 | } | |
199 | ||
200 | /** | |
201 | * @return The value | |
202 | */ | |
203 | protected String accessJMXValue() { | |
204 | try { | |
205 | Object result = getJMXConnection().getAttribute( | |
206 | new ObjectName(name), attribute); | |
207 | if(result != null) | |
208 | return result.toString(); | |
209 | } catch (Exception e) { | |
210 | // ignore access or connection open errors | |
211 | } | |
212 | return null; | |
213 | } | |
214 | ||
215 | // This method evaluates the condition | |
216 | 61 | @Override |
217 | 62 | public boolean eval() { |
63 | String value = getValue(); | |
64 | ||
218 | 65 | if (value == null) { |
219 | 66 | throw new BuildException("value attribute is not set"); |
220 | 67 | } |
221 | if ((name == null || attribute == null)) { | |
68 | if (getName() == null || getAttribute() == null) { | |
222 | 69 | throw new BuildException( |
223 | "Must specify a 'attribute', name for equals condition"); | |
70 | "Must specify an MBean name and attribute for equals condition"); | |
224 | 71 | } |
225 | 72 | //FIXME check url or host/parameter |
226 | 73 | String jmxValue = accessJMXValue(); |
227 | if(jmxValue != null) | |
74 | if (jmxValue != null) { | |
228 | 75 | return jmxValue.equals(value); |
76 | } | |
229 | 77 | return false; |
230 | 78 | } |
231 | 79 | } |
24 | 24 | |
25 | 25 | import javax.servlet.ServletException; |
26 | 26 | import javax.servlet.http.Cookie; |
27 | import javax.servlet.http.HttpServletRequest; | |
27 | 28 | import javax.servlet.http.HttpServletResponse; |
28 | 29 | |
29 | 30 | import org.apache.catalina.Authenticator; |
566 | 567 | "authorization") != null; |
567 | 568 | } |
568 | 569 | |
569 | if (!authRequired && context.getPreemptiveAuthentication()) { | |
570 | X509Certificate[] certs = getRequestCertificates(request, false); | |
570 | if (!authRequired && context.getPreemptiveAuthentication() && | |
571 | HttpServletRequest.CLIENT_CERT_AUTH.equals(getAuthMethod())) { | |
572 | X509Certificate[] certs = getRequestCertificates(request); | |
571 | 573 | authRequired = certs != null && certs.length > 0; |
572 | 574 | } |
573 | 575 | |
625 | 627 | * extracting the certificate chain from the Coyote request. |
626 | 628 | * |
627 | 629 | * @param request Request to be processed |
628 | * @param force Should a renegotiation be forced to request certificates | |
629 | * from the user agent if none have been provided | |
630 | 630 | * |
631 | 631 | * @return The X509 certificate chain if found, <code>null</code> |
632 | 632 | * otherwise. |
633 | 633 | */ |
634 | protected X509Certificate[] getRequestCertificates(final Request request, boolean force) | |
634 | protected X509Certificate[] getRequestCertificates(final Request request) | |
635 | 635 | throws IllegalStateException { |
636 | 636 | |
637 | 637 | X509Certificate certs[] = |
639 | 639 | |
640 | 640 | if ((certs == null) || (certs.length < 1)) { |
641 | 641 | try { |
642 | request.getCoyoteRequest().action(ActionCode.REQ_SSL_CERTIFICATE, Boolean.valueOf(force)); | |
642 | request.getCoyoteRequest().action(ActionCode.REQ_SSL_CERTIFICATE, null); | |
643 | 643 | certs = (X509Certificate[]) request.getAttribute(Globals.CERTIFICATES_ATTR); |
644 | 644 | } catch (IllegalStateException ise) { |
645 | 645 | // Request body was too large for save buffer |
18 | 18 | import java.io.IOException; |
19 | 19 | import java.io.StringReader; |
20 | 20 | import java.nio.charset.StandardCharsets; |
21 | import java.security.MessageDigest; | |
22 | import java.security.NoSuchAlgorithmException; | |
23 | 21 | import java.security.Principal; |
24 | 22 | import java.util.LinkedHashMap; |
25 | 23 | import java.util.Map; |
30 | 28 | import org.apache.catalina.LifecycleException; |
31 | 29 | import org.apache.catalina.Realm; |
32 | 30 | import org.apache.catalina.connector.Request; |
33 | import org.apache.catalina.util.ConcurrentMessageDigest; | |
34 | import org.apache.catalina.util.MD5Encoder; | |
35 | 31 | import org.apache.juli.logging.Log; |
36 | 32 | import org.apache.juli.logging.LogFactory; |
37 | 33 | import org.apache.tomcat.util.http.parser.Authorization; |
34 | import org.apache.tomcat.util.security.ConcurrentMessageDigest; | |
35 | import org.apache.tomcat.util.security.MD5Encoder; | |
38 | 36 | |
39 | 37 | |
40 | 38 | /** |
56 | 54 | */ |
57 | 55 | protected static final String QOP = "auth"; |
58 | 56 | |
57 | ||
59 | 58 | // ----------------------------------------------------------- Constructors |
60 | ||
61 | 59 | |
62 | 60 | public DigestAuthenticator() { |
63 | 61 | super(); |
64 | 62 | setCache(false); |
65 | try { | |
66 | if (md5Helper == null) { | |
67 | md5Helper = MessageDigest.getInstance("MD5"); | |
68 | } | |
69 | } catch (NoSuchAlgorithmException e) { | |
70 | throw new IllegalStateException(e); | |
71 | } | |
72 | 63 | } |
73 | 64 | |
74 | 65 | |
75 | 66 | // ----------------------------------------------------- Instance Variables |
76 | ||
77 | ||
78 | /** | |
79 | * MD5 message digest provider. | |
80 | * @deprecated Unused - will be removed in Tomcat 8.0.x onwards | |
81 | */ | |
82 | @Deprecated | |
83 | protected static volatile MessageDigest md5Helper; | |
84 | ||
85 | 67 | |
86 | 68 | /** |
87 | 69 | * List of server nonce values currently being tracked |
94 | 94 | containerLog.debug(" Looking up certificates"); |
95 | 95 | } |
96 | 96 | |
97 | X509Certificate certs[] = getRequestCertificates(request, true); | |
97 | X509Certificate certs[] = getRequestCertificates(request); | |
98 | 98 | |
99 | 99 | if ((certs == null) || (certs.length < 1)) { |
100 | 100 | if (containerLog.isDebugEnabled()) { |
305 | 305 | } |
306 | 306 | |
307 | 307 | if (status==SocketStatus.TIMEOUT) { |
308 | success = true; | |
309 | 308 | if (!asyncConImpl.timeout()) { |
310 | 309 | asyncConImpl.setErrorState(null, false); |
311 | 310 | } |
372 | 371 | } finally { |
373 | 372 | request.getContext().unbind(false, oldCL); |
374 | 373 | } |
375 | success = true; | |
376 | 374 | } else if (readListener != null && status == SocketStatus.OPEN_READ) { |
377 | 375 | ClassLoader oldCL = null; |
378 | 376 | try { |
395 | 393 | } finally { |
396 | 394 | request.getContext().unbind(false, oldCL); |
397 | 395 | } |
398 | success = true; | |
399 | 396 | } |
400 | 397 | } |
401 | 398 | |
408 | 405 | } |
409 | 406 | |
410 | 407 | if (request.isAsyncDispatching()) { |
411 | success = true; | |
412 | 408 | connector.getService().getContainer().getPipeline().getFirst().invoke(request, response); |
413 | 409 | Throwable t = (Throwable) request.getAttribute( |
414 | 410 | RequestDispatcher.ERROR_EXCEPTION); |
449 | 445 | } |
450 | 446 | } |
451 | 447 | |
448 | // Check to see if the processor is in an error state. If it is, | |
449 | // bail out now. | |
450 | AtomicBoolean error = new AtomicBoolean(false); | |
451 | res.action(ActionCode.IS_ERROR, error); | |
452 | if (error.get()) { | |
453 | success = false; | |
454 | } | |
452 | 455 | } catch (IOException e) { |
453 | 456 | success = false; |
454 | 457 | // Ignore |
898 | 901 | request.setRequestedSessionURL(true); |
899 | 902 | } |
900 | 903 | } |
904 | ||
905 | if (request.getContext().getUseRfc6265()) { | |
906 | req.getCookies().setUseRfc6265(true); | |
907 | } else { | |
908 | req.getCookies().setUseRfc6265(false); | |
909 | } | |
910 | ||
901 | 911 | |
902 | 912 | // Look for session ID in cookies and SSL session |
903 | 913 | parseSessionCookiesId(req, request); |
930 | 940 | // Reset mapping |
931 | 941 | request.getMappingData().recycle(); |
932 | 942 | mapRequired = true; |
943 | // Recycle cookies in case correct context is | |
944 | // configured with different settings | |
945 | req.getCookies().recycle(); | |
933 | 946 | } |
934 | 947 | break; |
935 | 948 | } |
2864 | 2864 | Cookie cookie = new Cookie(scookie.getName().toString(),null); |
2865 | 2865 | int version = scookie.getVersion(); |
2866 | 2866 | cookie.setVersion(version); |
2867 | if (getContext().getUseRfc6265()) { | |
2868 | scookie.getValue().getByteChunk().setCharset( | |
2869 | getContext().getCookieEncodingCharset()); | |
2870 | } | |
2867 | 2871 | cookie.setValue(unescape(scookie.getValue().toString())); |
2868 | 2872 | cookie.setPath(unescape(scookie.getPath().toString())); |
2869 | 2873 | String domain = scookie.getDomain().toString(); |
48 | 48 | import org.apache.catalina.Globals; |
49 | 49 | import org.apache.catalina.security.SecurityUtil; |
50 | 50 | import org.apache.catalina.util.Introspection; |
51 | import org.apache.juli.logging.Log; | |
51 | 52 | import org.apache.tomcat.InstanceManager; |
52 | 53 | import org.apache.tomcat.util.ExceptionUtils; |
53 | 54 | import org.apache.tomcat.util.res.StringManager; |
70 | 71 | protected final ClassLoader containerClassLoader; |
71 | 72 | protected final boolean privileged; |
72 | 73 | protected final boolean ignoreAnnotations; |
73 | private final Properties restrictedFilters = new Properties(); | |
74 | private final Properties restrictedListeners = new Properties(); | |
75 | private final Properties restrictedServlets = new Properties(); | |
74 | private final Properties restrictedFilters; | |
75 | private final Properties restrictedListeners; | |
76 | private final Properties restrictedServlets; | |
76 | 77 | private final Map<Class<?>, AnnotationCacheEntry[]> annotationCache = |
77 | 78 | new WeakHashMap<>(); |
78 | 79 | private final Map<String, String> postConstructMethods; |
87 | 88 | this.containerClassLoader = containerClassLoader; |
88 | 89 | ignoreAnnotations = catalinaContext.getIgnoreAnnotations(); |
89 | 90 | StringManager sm = StringManager.getManager(Constants.Package); |
90 | try { | |
91 | InputStream is = | |
92 | this.getClass().getClassLoader().getResourceAsStream | |
93 | ("org/apache/catalina/core/RestrictedServlets.properties"); | |
94 | if (is != null) { | |
95 | restrictedServlets.load(is); | |
96 | } else { | |
97 | catalinaContext.getLogger().error(sm.getString( | |
98 | "defaultInstanceManager.restrictedServletsResource")); | |
99 | } | |
100 | } catch (IOException e) { | |
101 | catalinaContext.getLogger().error(sm.getString( | |
102 | "defaultInstanceManager.restrictedServletsResource"), e); | |
103 | } | |
104 | ||
105 | try { | |
106 | InputStream is = | |
107 | this.getClass().getClassLoader().getResourceAsStream | |
108 | ("org/apache/catalina/core/RestrictedListeners.properties"); | |
109 | if (is != null) { | |
110 | restrictedListeners.load(is); | |
111 | } else { | |
112 | catalinaContext.getLogger().error(sm.getString( | |
113 | "defaultInstanceManager.restrictedListenersResources")); | |
114 | } | |
115 | } catch (IOException e) { | |
116 | catalinaContext.getLogger().error(sm.getString( | |
117 | "defaultInstanceManager.restrictedListenersResources"), e); | |
118 | } | |
119 | try { | |
120 | InputStream is = | |
121 | this.getClass().getClassLoader().getResourceAsStream | |
122 | ("org/apache/catalina/core/RestrictedFilters.properties"); | |
123 | if (is != null) { | |
124 | restrictedFilters.load(is); | |
125 | } else { | |
126 | catalinaContext.getLogger().error(sm.getString( | |
127 | "defaultInstanceManager.restrictedFiltersResource")); | |
128 | } | |
129 | } catch (IOException e) { | |
130 | catalinaContext.getLogger().error(sm.getString( | |
131 | "defaultInstanceManager.restrictedServletsResources"), e); | |
132 | } | |
91 | restrictedServlets = loadProperties( | |
92 | "org/apache/catalina/core/RestrictedServlets.properties", | |
93 | sm.getString("defaultInstanceManager.restrictedServletsResource"), | |
94 | catalinaContext.getLogger()); | |
95 | restrictedListeners = loadProperties( | |
96 | "org/apache/catalina/core/RestrictedListeners.properties", | |
97 | "defaultInstanceManager.restrictedListenersResources", | |
98 | catalinaContext.getLogger()); | |
99 | restrictedFilters = loadProperties( | |
100 | "org/apache/catalina/core/RestrictedFilters.properties", | |
101 | "defaultInstanceManager.restrictedFiltersResource", | |
102 | catalinaContext.getLogger()); | |
133 | 103 | this.context = context; |
134 | 104 | this.injectionMap = injectionMap; |
135 | 105 | this.postConstructMethods = catalinaContext.findPostConstructMethods(); |
653 | 623 | } |
654 | 624 | } |
655 | 625 | |
626 | private static Properties loadProperties(String resourceName, String errorString, Log log) { | |
627 | Properties result = new Properties(); | |
628 | ClassLoader cl = DefaultInstanceManager.class.getClassLoader(); | |
629 | try (InputStream is = cl.getResourceAsStream(resourceName)) { | |
630 | if (is ==null) { | |
631 | log.error(errorString); | |
632 | } else { | |
633 | result.load(is); | |
634 | } | |
635 | } catch (IOException ioe) { | |
636 | log.error(errorString, ioe); | |
637 | } | |
638 | return result; | |
639 | } | |
640 | ||
656 | 641 | private static String normalize(String jndiName){ |
657 | 642 | if(jndiName != null && jndiName.startsWith("java:comp/env/")){ |
658 | 643 | return jndiName.substring(14); |
147 | 147 | standardContext.startingContext=Exception starting Context with name [{0}] |
148 | 148 | standardContext.stoppingContext=Exception stopping Context with name [{0}] |
149 | 149 | standardContext.threadBindingListenerError=An error occurred in the thread binding listener configured for Context [{0}] |
150 | standardContext.unknownCookieEncoding=The unknown encoding [{0}] was specified for setCookieEncoding(String) so the default of UTF-8 will be used instead | |
150 | 151 | standardContext.urlPattern.patternWarning=WARNING: URL pattern {0} must start with a ''/'' in Servlet 2.4 |
151 | 152 | standardContext.webappClassLoader.missingProperty=Unable to set the web application class loader property [{0}] to [{1}] as the property does not exist. |
152 | 153 | standardContext.workPath=Exception obtaining work path for context [{0}] |
20 | 20 | import java.io.IOException; |
21 | 21 | import java.io.InputStream; |
22 | 22 | import java.io.InputStreamReader; |
23 | import java.io.UnsupportedEncodingException; | |
23 | 24 | import java.net.MalformedURLException; |
24 | 25 | import java.net.URL; |
26 | import java.nio.charset.Charset; | |
27 | import java.nio.charset.StandardCharsets; | |
25 | 28 | import java.security.AccessController; |
26 | 29 | import java.security.PrivilegedAction; |
27 | 30 | import java.util.ArrayList; |
114 | 117 | import org.apache.tomcat.JarScanner; |
115 | 118 | import org.apache.tomcat.util.ExceptionUtils; |
116 | 119 | import org.apache.tomcat.util.IntrospectionUtils; |
120 | import org.apache.tomcat.util.buf.B2CConverter; | |
117 | 121 | import org.apache.tomcat.util.buf.UDecoder; |
118 | 122 | import org.apache.tomcat.util.descriptor.XmlIdentifiers; |
119 | 123 | import org.apache.tomcat.util.descriptor.web.ApplicationParameter; |
757 | 761 | /** |
758 | 762 | * If an HttpClient keep-alive timer thread has been started by this web |
759 | 763 | * application and is still running, should Tomcat change the context class |
760 | * loader from the current {@link WebappClassLoader} to | |
761 | * {@link WebappClassLoader#parent} to prevent a memory leak? Note that the | |
764 | * loader from the current {@link ClassLoader} to | |
765 | * {@link ClassLoader#getParent()} to prevent a memory leak? Note that the | |
762 | 766 | * keep-alive timer thread will stop on its own once the keep-alives all |
763 | 767 | * expire however, on a busy system that might not happen for some time. |
764 | 768 | */ |
820 | 824 | |
821 | 825 | private final Object namingToken = new Object(); |
822 | 826 | |
827 | private boolean useRfc6265 = false; | |
828 | private Charset cookieEncoding = StandardCharsets.UTF_8; | |
829 | ||
823 | 830 | |
824 | 831 | // ----------------------------------------------------- Context Properties |
832 | ||
833 | ||
834 | @Override | |
835 | public void setUseRfc6265(boolean useRfc6265) { | |
836 | this.useRfc6265 = useRfc6265; | |
837 | } | |
838 | ||
839 | ||
840 | @Override | |
841 | public boolean getUseRfc6265() { | |
842 | return useRfc6265; | |
843 | } | |
844 | ||
845 | ||
846 | @Override | |
847 | public void setCookieEncoding(String encoding) { | |
848 | try { | |
849 | Charset charset = B2CConverter.getCharset(encoding); | |
850 | cookieEncoding = charset; | |
851 | } catch (UnsupportedEncodingException uee) { | |
852 | cookieEncoding = StandardCharsets.UTF_8; | |
853 | log.warn(sm.getString("standardContext.unknownCookieEncoding"), uee); | |
854 | } | |
855 | } | |
856 | ||
857 | ||
858 | @Override | |
859 | public String getCookieEncoding() { | |
860 | return cookieEncoding.name(); | |
861 | } | |
862 | ||
863 | ||
864 | @Override | |
865 | public Charset getCookieEncodingCharset() { | |
866 | return cookieEncoding; | |
867 | } | |
868 | ||
825 | 869 | |
826 | 870 | @Override |
827 | 871 | public Object getNamingToken() { |
917 | 961 | StringBuilder result = new StringBuilder(); |
918 | 962 | boolean first = true; |
919 | 963 | for (String servletName : resourceOnlyServlets) { |
920 | if (!first) { | |
964 | if (first) { | |
965 | first = false; | |
966 | } else { | |
921 | 967 | result.append(','); |
922 | 968 | } |
923 | 969 | result.append(servletName); |
38 | 38 | import org.apache.catalina.LifecycleException; |
39 | 39 | import org.apache.catalina.LifecycleListener; |
40 | 40 | import org.apache.catalina.Valve; |
41 | import org.apache.catalina.loader.WebappClassLoader; | |
41 | import org.apache.catalina.loader.WebappClassLoaderBase; | |
42 | 42 | import org.apache.tomcat.util.ExceptionUtils; |
43 | 43 | |
44 | 44 | /** |
748 | 748 | for (Map.Entry<ClassLoader, String> entry : |
749 | 749 | childClassLoaders.entrySet()) { |
750 | 750 | ClassLoader cl = entry.getKey(); |
751 | if (cl instanceof WebappClassLoader) { | |
752 | if (!((WebappClassLoader) cl).getState().isAvailable()) { | |
751 | if (cl instanceof WebappClassLoaderBase) { | |
752 | if (!((WebappClassLoaderBase) cl).getState().isAvailable()) { | |
753 | 753 | result.add(entry.getValue()); |
754 | 754 | } |
755 | 755 | } |
102 | 102 | type="boolean" |
103 | 103 | writeable="false" /> |
104 | 104 | |
105 | <attribute name="cookieEncoding" | |
106 | description="If the new cookie parser is used, which encoding should be used to decode the cookie values?" | |
107 | type="java.lang.String"/> | |
108 | ||
105 | 109 | <attribute name="cookies" |
106 | 110 | description="Should we attempt to use cookies for session id communication?" |
107 | 111 | type="boolean"/> |
328 | 332 | <attribute name="useNaming" |
329 | 333 | description="Create a JNDI naming context for this application?" |
330 | 334 | is="true" |
335 | type="boolean"/> | |
336 | ||
337 | <attribute name="useNewCookieParser" | |
338 | description="Use the new RFC6265 based cookie parser" | |
339 | is="false" | |
331 | 340 | type="boolean"/> |
332 | 341 | |
333 | 342 | <attribute name="webappVersion" |
26 | 26 | import java.util.List; |
27 | 27 | |
28 | 28 | /** |
29 | * This class is loaded by the {@link WebappClassLoader} to enable it to | |
29 | * This class is loaded by {@link WebappClassLoaderBase} to enable it to | |
30 | 30 | * deregister JDBC drivers forgotten by the web application. There are some |
31 | * classloading hacks involved - see {@link WebappClassLoader#clearReferences()} | |
32 | * for details - but the short version is do not just create a new instance of | |
33 | * this class with the new keyword. | |
31 | * classloading hacks involved - see | |
32 | * {@link WebappClassLoaderBase#clearReferences()} for details - but the short | |
33 | * version is do not just create a new instance of this class with the new | |
34 | * keyword. | |
34 | 35 | * |
35 | * Since this class is loaded by {@link WebappClassLoader}, it can not refer to | |
36 | * any internal Tomcat classes as that will cause the security manager to | |
36 | * Since this class is loaded by {@link WebappClassLoaderBase}, it can not refer | |
37 | * to any internal Tomcat classes as that will cause the security manager to | |
37 | 38 | * complain. |
38 | 39 | */ |
39 | 40 | public class JdbcLeakPrevention { |
50 | 50 | webappClassLoader.addTransformer=Added class file transformer [{0}] to web application [{1}]. |
51 | 51 | webappClassLoader.removeTransformer=Removed class file transformer [{0}] from web application [{1}]. |
52 | 52 | webappClassLoader.transformError=Instrumentation error: could not transform class [{0}] because its class file format is not legal. |
53 | ||
54 | webappClassLoaderParallel.registrationFailed=Registration of org.apache.catalina.loader.ParallelWebappClassLoader as capable of loading classes in parallel failed | |
55 | ||
53 | 56 | webappLoader.addRepository=Adding repository {0} |
54 | 57 | webappLoader.deploy=Deploying class repositories to work directory {0} |
55 | 58 | webappLoader.jarDeploy=Deploy JAR {0} to {1} |
0 | /* | |
1 | * Licensed to the Apache Software Foundation (ASF) under one or more | |
2 | * contributor license agreements. See the NOTICE file distributed with | |
3 | * this work for additional information regarding copyright ownership. | |
4 | * The ASF licenses this file to You under the Apache License, Version 2.0 | |
5 | * (the "License"); you may not use this file except in compliance with | |
6 | * the License. You may obtain a copy of the License at | |
7 | * | |
8 | * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
15 | */ | |
16 | package org.apache.catalina.loader; | |
17 | ||
18 | import org.apache.catalina.LifecycleException; | |
19 | ||
20 | public class ParallelWebappClassLoader extends WebappClassLoaderBase { | |
21 | ||
22 | private static final org.apache.juli.logging.Log log = | |
23 | org.apache.juli.logging.LogFactory.getLog(ParallelWebappClassLoader.class); | |
24 | ||
25 | static { | |
26 | boolean result = ClassLoader.registerAsParallelCapable(); | |
27 | if (!result) { | |
28 | log.warn(sm.getString("webappClassLoaderParallel.registrationFailed")); | |
29 | } | |
30 | } | |
31 | ||
32 | public ParallelWebappClassLoader() { | |
33 | super(); | |
34 | } | |
35 | ||
36 | ||
37 | public ParallelWebappClassLoader(ClassLoader parent) { | |
38 | super(parent); | |
39 | } | |
40 | ||
41 | ||
42 | /** | |
43 | * Returns a copy of this class loader without any class file | |
44 | * transformers. This is a tool often used by Java Persistence API | |
45 | * providers to inspect entity classes in the absence of any | |
46 | * instrumentation, something that can't be guaranteed within the | |
47 | * context of a {@link java.lang.instrument.ClassFileTransformer}'s | |
48 | * {@link java.lang.instrument.ClassFileTransformer#transform(ClassLoader, | |
49 | * String, Class, java.security.ProtectionDomain, byte[]) transform} method. | |
50 | * <p> | |
51 | * The returned class loader's resource cache will have been cleared | |
52 | * so that classes already instrumented will not be retained or | |
53 | * returned. | |
54 | * | |
55 | * @return the transformer-free copy of this class loader. | |
56 | */ | |
57 | @Override | |
58 | public ParallelWebappClassLoader copyWithoutTransformers() { | |
59 | ||
60 | ParallelWebappClassLoader result = new ParallelWebappClassLoader(getParent()); | |
61 | ||
62 | super.copyStateWithoutTransformers(result); | |
63 | ||
64 | try { | |
65 | result.start(); | |
66 | } catch (LifecycleException e) { | |
67 | throw new IllegalStateException(e); | |
68 | } | |
69 | ||
70 | return result; | |
71 | } | |
72 | } |
15 | 15 | */ |
16 | 16 | package org.apache.catalina.loader; |
17 | 17 | |
18 | import java.io.ByteArrayInputStream; | |
19 | import java.io.File; | |
20 | import java.io.FilePermission; | |
21 | import java.io.IOException; | |
22 | import java.io.InputStream; | |
23 | import java.lang.instrument.ClassFileTransformer; | |
24 | import java.lang.instrument.IllegalClassFormatException; | |
25 | import java.lang.ref.Reference; | |
26 | import java.lang.ref.WeakReference; | |
27 | import java.lang.reflect.Field; | |
28 | import java.lang.reflect.Method; | |
29 | import java.lang.reflect.Modifier; | |
30 | import java.net.URI; | |
31 | import java.net.URISyntaxException; | |
32 | import java.net.URL; | |
33 | import java.net.URLClassLoader; | |
34 | import java.nio.charset.StandardCharsets; | |
35 | import java.security.AccessControlException; | |
36 | import java.security.AccessController; | |
37 | import java.security.CodeSource; | |
38 | import java.security.Permission; | |
39 | import java.security.PermissionCollection; | |
40 | import java.security.Policy; | |
41 | import java.security.PrivilegedAction; | |
42 | import java.security.ProtectionDomain; | |
43 | import java.util.ArrayList; | |
44 | import java.util.Arrays; | |
45 | import java.util.Collection; | |
46 | import java.util.Collections; | |
47 | import java.util.ConcurrentModificationException; | |
48 | import java.util.Date; | |
49 | import java.util.Enumeration; | |
50 | import java.util.HashMap; | |
51 | import java.util.Iterator; | |
52 | import java.util.LinkedHashSet; | |
53 | import java.util.List; | |
54 | import java.util.Map; | |
55 | import java.util.Map.Entry; | |
56 | import java.util.ResourceBundle; | |
57 | import java.util.Set; | |
58 | import java.util.concurrent.ConcurrentHashMap; | |
59 | import java.util.concurrent.CopyOnWriteArrayList; | |
60 | import java.util.concurrent.ThreadPoolExecutor; | |
61 | import java.util.jar.Attributes; | |
62 | import java.util.jar.Attributes.Name; | |
63 | import java.util.jar.Manifest; | |
64 | import java.util.regex.Matcher; | |
65 | import java.util.regex.Pattern; | |
18 | import org.apache.catalina.LifecycleException; | |
66 | 19 | |
67 | import org.apache.catalina.Globals; | |
68 | import org.apache.catalina.Lifecycle; | |
69 | import org.apache.catalina.LifecycleException; | |
70 | import org.apache.catalina.LifecycleListener; | |
71 | import org.apache.catalina.LifecycleState; | |
72 | import org.apache.catalina.WebResource; | |
73 | import org.apache.catalina.WebResourceRoot; | |
74 | import org.apache.catalina.webresources.TomcatURLStreamHandlerFactory; | |
75 | import org.apache.tomcat.InstrumentableClassLoader; | |
76 | import org.apache.tomcat.util.ExceptionUtils; | |
77 | import org.apache.tomcat.util.IntrospectionUtils; | |
78 | import org.apache.tomcat.util.res.StringManager; | |
20 | public class WebappClassLoader extends WebappClassLoaderBase { | |
79 | 21 | |
80 | /** | |
81 | * Specialized web application class loader. | |
82 | * <p> | |
83 | * This class loader is a full reimplementation of the | |
84 | * <code>URLClassLoader</code> from the JDK. It is designed to be fully | |
85 | * compatible with a normal <code>URLClassLoader</code>, although its internal | |
86 | * behavior may be completely different. | |
87 | * <p> | |
88 | * <strong>IMPLEMENTATION NOTE</strong> - By default, this class loader follows | |
89 | * the delegation model required by the specification. The system class | |
90 | * loader will be queried first, then the local repositories, and only then | |
91 | * delegation to the parent class loader will occur. This allows the web | |
92 | * application to override any shared class except the classes from J2SE. | |
93 | * Special handling is provided from the JAXP XML parser interfaces, the JNDI | |
94 | * interfaces, and the classes from the servlet API, which are never loaded | |
95 | * from the webapp repositories. The <code>delegate</code> property | |
96 | * allows an application to modify this behavior to move the parent class loader | |
97 | * ahead of the local repositories. | |
98 | * <p> | |
99 | * <strong>IMPLEMENTATION NOTE</strong> - Due to limitations in Jasper | |
100 | * compilation technology, any repository which contains classes from | |
101 | * the servlet API will be ignored by the class loader. | |
102 | * <p> | |
103 | * <strong>IMPLEMENTATION NOTE</strong> - The class loader generates source | |
104 | * URLs which include the full JAR URL when a class is loaded from a JAR file, | |
105 | * which allows setting security permission at the class level, even when a | |
106 | * class is contained inside a JAR. | |
107 | * <p> | |
108 | * <strong>IMPLEMENTATION NOTE</strong> - Local repositories are searched in | |
109 | * the order they are added via the initial constructor and/or any subsequent | |
110 | * calls to <code>addRepository()</code> or <code>addJar()</code>. | |
111 | * <p> | |
112 | * <strong>IMPLEMENTATION NOTE</strong> - No check for sealing violations or | |
113 | * security is made unless a security manager is present. | |
114 | * <p> | |
115 | * <strong>IMPLEMENTATION NOTE</strong> - As of 8.0, this class | |
116 | * loader implements {@link InstrumentableClassLoader}, permitting web | |
117 | * application classes to instrument other classes in the same web | |
118 | * application. It does not permit instrumentation of system or container | |
119 | * classes or classes in other web apps. | |
120 | * | |
121 | * @author Remy Maucherat | |
122 | * @author Craig R. McClanahan | |
123 | */ | |
124 | public class WebappClassLoader extends URLClassLoader | |
125 | implements Lifecycle, InstrumentableClassLoader { | |
126 | ||
127 | private static final org.apache.juli.logging.Log log= | |
128 | org.apache.juli.logging.LogFactory.getLog( WebappClassLoader.class ); | |
129 | ||
130 | /** | |
131 | * List of ThreadGroup names to ignore when scanning for web application | |
132 | * started threads that need to be shut down. | |
133 | */ | |
134 | private static final List<String> JVM_THREAD_GROUP_NAMES = new ArrayList<>(); | |
135 | ||
136 | private static final String JVM_THREAD_GROUP_SYSTEM = "system"; | |
137 | ||
138 | private static final String CLASS_FILE_SUFFIX = ".class"; | |
139 | private static final String SERVICES_PREFIX = "/META-INF/services/"; | |
140 | ||
141 | static { | |
142 | JVM_THREAD_GROUP_NAMES.add(JVM_THREAD_GROUP_SYSTEM); | |
143 | JVM_THREAD_GROUP_NAMES.add("RMI Runtime"); | |
144 | } | |
145 | ||
146 | protected class PrivilegedFindResourceByName | |
147 | implements PrivilegedAction<ResourceEntry> { | |
148 | ||
149 | protected final String name; | |
150 | protected final String path; | |
151 | ||
152 | PrivilegedFindResourceByName(String name, String path) { | |
153 | this.name = name; | |
154 | this.path = path; | |
155 | } | |
156 | ||
157 | @Override | |
158 | public ResourceEntry run() { | |
159 | return findResourceInternal(name, path); | |
160 | } | |
161 | ||
22 | public WebappClassLoader() { | |
23 | super(); | |
162 | 24 | } |
163 | 25 | |
164 | 26 | |
165 | protected static final class PrivilegedGetClassLoader | |
166 | implements PrivilegedAction<ClassLoader> { | |
167 | ||
168 | public final Class<?> clazz; | |
169 | ||
170 | public PrivilegedGetClassLoader(Class<?> clazz){ | |
171 | this.clazz = clazz; | |
172 | } | |
173 | ||
174 | @Override | |
175 | public ClassLoader run() { | |
176 | return clazz.getClassLoader(); | |
177 | } | |
27 | public WebappClassLoader(ClassLoader parent) { | |
28 | super(parent); | |
178 | 29 | } |
179 | 30 | |
180 | ||
181 | // ------------------------------------------------------- Static Variables | |
182 | ||
183 | /** | |
184 | * Regular expression of package names which are not allowed to be loaded | |
185 | * from a webapp class loader without delegating first. | |
186 | */ | |
187 | protected final Matcher packageTriggersDeny = Pattern.compile( | |
188 | "^javax\\.el\\.|" + | |
189 | "^javax\\.servlet\\.|" + | |
190 | "^org\\.apache\\.(catalina|coyote|el|jasper|juli|naming|tomcat)\\." | |
191 | ).matcher(""); | |
192 | ||
193 | ||
194 | /** | |
195 | * Regular expression of package names which are allowed to be loaded from a | |
196 | * webapp class loader without delegating first and override any set by | |
197 | * {@link #packageTriggersDeny}. | |
198 | */ | |
199 | protected final Matcher packageTriggersPermit = | |
200 | Pattern.compile("^javax\\.servlet\\.jsp\\.jstl\\.|" + | |
201 | "^org\\.apache\\.tomcat\\.jdbc\\.").matcher(""); | |
202 | ||
203 | ||
204 | /** | |
205 | * The string manager for this package. | |
206 | */ | |
207 | protected static final StringManager sm = | |
208 | StringManager.getManager(Constants.Package); | |
209 | ||
210 | ||
211 | // ----------------------------------------------------------- Constructors | |
212 | ||
213 | /** | |
214 | * Construct a new ClassLoader with no defined repositories and no | |
215 | * parent ClassLoader. | |
216 | */ | |
217 | public WebappClassLoader() { | |
218 | ||
219 | super(new URL[0]); | |
220 | ||
221 | ClassLoader p = getParent(); | |
222 | if (p == null) { | |
223 | p = getSystemClassLoader(); | |
224 | } | |
225 | this.parent = p; | |
226 | ||
227 | ClassLoader j = String.class.getClassLoader(); | |
228 | if (j == null) { | |
229 | j = getSystemClassLoader(); | |
230 | while (j.getParent() != null) { | |
231 | j = j.getParent(); | |
232 | } | |
233 | } | |
234 | this.javaseClassLoader = j; | |
235 | ||
236 | securityManager = System.getSecurityManager(); | |
237 | if (securityManager != null) { | |
238 | refreshPolicy(); | |
239 | } | |
240 | } | |
241 | ||
242 | ||
243 | /** | |
244 | * Construct a new ClassLoader with no defined repositories and the given | |
245 | * parent ClassLoader. | |
246 | * <p> | |
247 | * Method is used via reflection - | |
248 | * see {@link WebappLoader#createClassLoader()} | |
249 | * | |
250 | * @param parent Our parent class loader | |
251 | */ | |
252 | public WebappClassLoader(ClassLoader parent) { | |
253 | ||
254 | super(new URL[0], parent); | |
255 | ||
256 | ClassLoader p = getParent(); | |
257 | if (p == null) { | |
258 | p = getSystemClassLoader(); | |
259 | } | |
260 | this.parent = p; | |
261 | ||
262 | ClassLoader j = String.class.getClassLoader(); | |
263 | if (j == null) { | |
264 | j = getSystemClassLoader(); | |
265 | while (j.getParent() != null) { | |
266 | j = j.getParent(); | |
267 | } | |
268 | } | |
269 | this.javaseClassLoader = j; | |
270 | ||
271 | securityManager = System.getSecurityManager(); | |
272 | if (securityManager != null) { | |
273 | refreshPolicy(); | |
274 | } | |
275 | } | |
276 | ||
277 | ||
278 | // ----------------------------------------------------- Instance Variables | |
279 | ||
280 | /** | |
281 | * Associated web resources for this webapp. | |
282 | */ | |
283 | protected WebResourceRoot resources = null; | |
284 | ||
285 | ||
286 | /** | |
287 | * The cache of ResourceEntry for classes and resources we have loaded, | |
288 | * keyed by resource path, not binary name. Path is used as the key since | |
289 | * resources may be requested by binary name (classes) or path (other | |
290 | * resources such as property files) and the mapping from binary name to | |
291 | * path is unambiguous but the reverse mapping is ambiguous. | |
292 | */ | |
293 | protected final Map<String, ResourceEntry> resourceEntries = | |
294 | new ConcurrentHashMap<>(); | |
295 | ||
296 | ||
297 | /** | |
298 | * Should this class loader delegate to the parent class loader | |
299 | * <strong>before</strong> searching its own repositories (i.e. the | |
300 | * usual Java2 delegation model)? If set to <code>false</code>, | |
301 | * this class loader will search its own repositories first, and | |
302 | * delegate to the parent only if the class or resource is not | |
303 | * found locally. Note that the default, <code>false</code>, is | |
304 | * the behavior called for by the servlet specification. | |
305 | */ | |
306 | protected boolean delegate = false; | |
307 | ||
308 | ||
309 | private final HashMap<String,Long> jarModificationTimes = new HashMap<>(); | |
310 | ||
311 | ||
312 | /** | |
313 | * A list of read File and Jndi Permission's required if this loader | |
314 | * is for a web application context. | |
315 | */ | |
316 | protected final ArrayList<Permission> permissionList = new ArrayList<>(); | |
317 | ||
318 | ||
319 | /** | |
320 | * The PermissionCollection for each CodeSource for a web | |
321 | * application context. | |
322 | */ | |
323 | protected final HashMap<String, PermissionCollection> loaderPC = new HashMap<>(); | |
324 | ||
325 | ||
326 | /** | |
327 | * Instance of the SecurityManager installed. | |
328 | */ | |
329 | protected final SecurityManager securityManager; | |
330 | ||
331 | ||
332 | /** | |
333 | * The parent class loader. | |
334 | */ | |
335 | protected final ClassLoader parent; | |
336 | ||
337 | ||
338 | /** | |
339 | * The bootstrap class loader used to load the JavaSE classes. In some | |
340 | * implementations this class loader is always <code>null</null> and in | |
341 | * those cases {@link ClassLoader#getParent()} will be called recursively on | |
342 | * the system class loader and the last non-null result used. | |
343 | */ | |
344 | private ClassLoader javaseClassLoader; | |
345 | ||
346 | ||
347 | /** | |
348 | * need conversion for properties files | |
349 | */ | |
350 | protected boolean needConvert = false; | |
351 | ||
352 | ||
353 | /** | |
354 | * All permission. | |
355 | */ | |
356 | protected final Permission allPermission = new java.security.AllPermission(); | |
357 | ||
358 | ||
359 | /** | |
360 | * Should Tomcat attempt to null out any static or final fields from loaded | |
361 | * classes when a web application is stopped as a work around for apparent | |
362 | * garbage collection bugs and application coding errors? There have been | |
363 | * some issues reported with log4j when this option is true. Applications | |
364 | * without memory leaks using recent JVMs should operate correctly with this | |
365 | * option set to <code>false</code>. If not specified, the default value of | |
366 | * <code>false</code> will be used. | |
367 | */ | |
368 | private boolean clearReferencesStatic = false; | |
369 | ||
370 | /** | |
371 | * Should Tomcat attempt to terminate threads that have been started by the | |
372 | * web application? Stopping threads is performed via the deprecated (for | |
373 | * good reason) <code>Thread.stop()</code> method and is likely to result in | |
374 | * instability. As such, enabling this should be viewed as an option of last | |
375 | * resort in a development environment and is not recommended in a | |
376 | * production environment. If not specified, the default value of | |
377 | * <code>false</code> will be used. | |
378 | */ | |
379 | private boolean clearReferencesStopThreads = false; | |
380 | ||
381 | /** | |
382 | * Should Tomcat attempt to terminate any {@link java.util.TimerThread}s | |
383 | * that have been started by the web application? If not specified, the | |
384 | * default value of <code>false</code> will be used. | |
385 | */ | |
386 | private boolean clearReferencesStopTimerThreads = false; | |
387 | ||
388 | /** | |
389 | * Should Tomcat call {@link org.apache.juli.logging.LogFactory#release()} | |
390 | * when the class loader is stopped? If not specified, the default value | |
391 | * of <code>true</code> is used. Changing the default setting is likely to | |
392 | * lead to memory leaks and other issues. | |
393 | */ | |
394 | private boolean clearReferencesLogFactoryRelease = true; | |
395 | ||
396 | /** | |
397 | * If an HttpClient keep-alive timer thread has been started by this web | |
398 | * application and is still running, should Tomcat change the context class | |
399 | * loader from the current {@link WebappClassLoader} to | |
400 | * {@link WebappClassLoader#parent} to prevent a memory leak? Note that the | |
401 | * keep-alive timer thread will stop on its own once the keep-alives all | |
402 | * expire however, on a busy system that might not happen for some time. | |
403 | */ | |
404 | private boolean clearReferencesHttpClientKeepAliveThread = true; | |
405 | ||
406 | /** | |
407 | * Holds the class file transformers decorating this class loader. The | |
408 | * CopyOnWriteArrayList is thread safe. It is expensive on writes, but | |
409 | * those should be rare. It is very fast on reads, since synchronization | |
410 | * is not actually used. Importantly, the ClassLoader will never block | |
411 | * iterating over the transformers while loading a class. | |
412 | */ | |
413 | private final List<ClassFileTransformer> transformers = new CopyOnWriteArrayList<>(); | |
414 | ||
415 | ||
416 | /** | |
417 | * Flag that indicates that {@link #addURL(URL)} has been called which | |
418 | * creates a requirement to check the super class when searching for | |
419 | * resources. | |
420 | */ | |
421 | private boolean hasExternalRepositories = false; | |
422 | ||
423 | ||
424 | /** | |
425 | * Repositories managed by this class rather than the super class. | |
426 | */ | |
427 | private List<URL> localRepositories = new ArrayList<>(); | |
428 | ||
429 | ||
430 | private volatile LifecycleState state = LifecycleState.NEW; | |
431 | ||
432 | ||
433 | // ------------------------------------------------------------- Properties | |
434 | ||
435 | /** | |
436 | * Get associated resources. | |
437 | */ | |
438 | public WebResourceRoot getResources() { | |
439 | return this.resources; | |
440 | } | |
441 | ||
442 | ||
443 | /** | |
444 | * Set associated resources. | |
445 | */ | |
446 | public void setResources(WebResourceRoot resources) { | |
447 | this.resources = resources; | |
448 | } | |
449 | ||
450 | ||
451 | /** | |
452 | * Return the context name for this class loader. | |
453 | */ | |
454 | public String getContextName() { | |
455 | if (resources == null) { | |
456 | return "Unknown"; | |
457 | } else { | |
458 | return resources.getContext().getName(); | |
459 | } | |
460 | } | |
461 | ||
462 | ||
463 | /** | |
464 | * Return the "delegate first" flag for this class loader. | |
465 | */ | |
466 | public boolean getDelegate() { | |
467 | ||
468 | return (this.delegate); | |
469 | ||
470 | } | |
471 | ||
472 | ||
473 | /** | |
474 | * Set the "delegate first" flag for this class loader. | |
475 | * If this flag is true, this class loader delegates | |
476 | * to the parent class loader | |
477 | * <strong>before</strong> searching its own repositories, as | |
478 | * in an ordinary (non-servlet) chain of Java class loaders. | |
479 | * If set to <code>false</code> (the default), | |
480 | * this class loader will search its own repositories first, and | |
481 | * delegate to the parent only if the class or resource is not | |
482 | * found locally, as per the servlet specification. | |
483 | * | |
484 | * @param delegate The new "delegate first" flag | |
485 | */ | |
486 | public void setDelegate(boolean delegate) { | |
487 | this.delegate = delegate; | |
488 | } | |
489 | ||
490 | ||
491 | /** | |
492 | * If there is a Java SecurityManager create a read permission for the | |
493 | * target of the given URL as appropriate. | |
494 | * | |
495 | * @param url URL for a file or directory on local system | |
496 | */ | |
497 | void addPermission(URL url) { | |
498 | if (url == null) { | |
499 | return; | |
500 | } | |
501 | if (securityManager != null) { | |
502 | String protocol = url.getProtocol(); | |
503 | if ("file".equalsIgnoreCase(protocol)) { | |
504 | URI uri; | |
505 | File f; | |
506 | String path; | |
507 | try { | |
508 | uri = url.toURI(); | |
509 | f = new File(uri); | |
510 | path = f.getCanonicalPath(); | |
511 | } catch (IOException | URISyntaxException e) { | |
512 | log.warn(sm.getString( | |
513 | "webappClassLoader.addPermisionNoCanonicalFile", | |
514 | url.toExternalForm())); | |
515 | return; | |
516 | } | |
517 | if (f.isFile()) { | |
518 | // Allow the file to be read | |
519 | addPermission(new FilePermission(path, "read")); | |
520 | } else if (f.isDirectory()) { | |
521 | addPermission(new FilePermission(path, "read")); | |
522 | addPermission(new FilePermission( | |
523 | path + File.separator + "-", "read")); | |
524 | } else { | |
525 | // File does not exist - ignore (shouldn't happen) | |
526 | } | |
527 | } else { | |
528 | // Unsupported URL protocol | |
529 | log.warn(sm.getString( | |
530 | "webappClassLoader.addPermisionNoProtocol", | |
531 | protocol, url.toExternalForm())); | |
532 | } | |
533 | } | |
534 | } | |
535 | ||
536 | ||
537 | /** | |
538 | * If there is a Java SecurityManager create a Permission. | |
539 | * | |
540 | * @param permission The permission | |
541 | */ | |
542 | void addPermission(Permission permission) { | |
543 | if ((securityManager != null) && (permission != null)) { | |
544 | permissionList.add(permission); | |
545 | } | |
546 | } | |
547 | ||
548 | ||
549 | /** | |
550 | * Return the clearReferencesStatic flag for this Context. | |
551 | */ | |
552 | public boolean getClearReferencesStatic() { | |
553 | return (this.clearReferencesStatic); | |
554 | } | |
555 | ||
556 | ||
557 | /** | |
558 | * Set the clearReferencesStatic feature for this Context. | |
559 | * | |
560 | * @param clearReferencesStatic The new flag value | |
561 | */ | |
562 | public void setClearReferencesStatic(boolean clearReferencesStatic) { | |
563 | this.clearReferencesStatic = clearReferencesStatic; | |
564 | } | |
565 | ||
566 | ||
567 | /** | |
568 | * Return the clearReferencesStopThreads flag for this Context. | |
569 | */ | |
570 | public boolean getClearReferencesStopThreads() { | |
571 | return (this.clearReferencesStopThreads); | |
572 | } | |
573 | ||
574 | ||
575 | /** | |
576 | * Set the clearReferencesStopThreads feature for this Context. | |
577 | * | |
578 | * @param clearReferencesStopThreads The new flag value | |
579 | */ | |
580 | public void setClearReferencesStopThreads( | |
581 | boolean clearReferencesStopThreads) { | |
582 | this.clearReferencesStopThreads = clearReferencesStopThreads; | |
583 | } | |
584 | ||
585 | ||
586 | /** | |
587 | * Return the clearReferencesStopTimerThreads flag for this Context. | |
588 | */ | |
589 | public boolean getClearReferencesStopTimerThreads() { | |
590 | return (this.clearReferencesStopTimerThreads); | |
591 | } | |
592 | ||
593 | ||
594 | /** | |
595 | * Set the clearReferencesStopTimerThreads feature for this Context. | |
596 | * | |
597 | * @param clearReferencesStopTimerThreads The new flag value | |
598 | */ | |
599 | public void setClearReferencesStopTimerThreads( | |
600 | boolean clearReferencesStopTimerThreads) { | |
601 | this.clearReferencesStopTimerThreads = clearReferencesStopTimerThreads; | |
602 | } | |
603 | ||
604 | ||
605 | /** | |
606 | * Return the clearReferencesLogFactoryRelease flag for this Context. | |
607 | */ | |
608 | public boolean getClearReferencesLogFactoryRelease() { | |
609 | return (this.clearReferencesLogFactoryRelease); | |
610 | } | |
611 | ||
612 | ||
613 | /** | |
614 | * Set the clearReferencesLogFactoryRelease feature for this Context. | |
615 | * | |
616 | * @param clearReferencesLogFactoryRelease The new flag value | |
617 | */ | |
618 | public void setClearReferencesLogFactoryRelease( | |
619 | boolean clearReferencesLogFactoryRelease) { | |
620 | this.clearReferencesLogFactoryRelease = | |
621 | clearReferencesLogFactoryRelease; | |
622 | } | |
623 | ||
624 | ||
625 | /** | |
626 | * Return the clearReferencesHttpClientKeepAliveThread flag for this | |
627 | * Context. | |
628 | */ | |
629 | public boolean getClearReferencesHttpClientKeepAliveThread() { | |
630 | return (this.clearReferencesHttpClientKeepAliveThread); | |
631 | } | |
632 | ||
633 | ||
634 | /** | |
635 | * Set the clearReferencesHttpClientKeepAliveThread feature for this | |
636 | * Context. | |
637 | * | |
638 | * @param clearReferencesHttpClientKeepAliveThread The new flag value | |
639 | */ | |
640 | public void setClearReferencesHttpClientKeepAliveThread( | |
641 | boolean clearReferencesHttpClientKeepAliveThread) { | |
642 | this.clearReferencesHttpClientKeepAliveThread = | |
643 | clearReferencesHttpClientKeepAliveThread; | |
644 | } | |
645 | ||
646 | ||
647 | // ------------------------------------------------------- Reloader Methods | |
648 | ||
649 | /** | |
650 | * Adds the specified class file transformer to this class loader. The | |
651 | * transformer will then be able to modify the bytecode of any classes | |
652 | * loaded by this class loader after the invocation of this method. | |
653 | * | |
654 | * @param transformer The transformer to add to the class loader | |
655 | */ | |
656 | @Override | |
657 | public void addTransformer(ClassFileTransformer transformer) { | |
658 | ||
659 | if (transformer == null) { | |
660 | throw new IllegalArgumentException(sm.getString( | |
661 | "webappClassLoader.addTransformer.illegalArgument", getContextName())); | |
662 | } | |
663 | ||
664 | if (this.transformers.contains(transformer)) { | |
665 | // if the same instance of this transformer was already added, bail out | |
666 | log.warn(sm.getString("webappClassLoader.addTransformer.duplicate", | |
667 | transformer, getContextName())); | |
668 | return; | |
669 | } | |
670 | this.transformers.add(transformer); | |
671 | ||
672 | log.info(sm.getString("webappClassLoader.addTransformer", transformer, getContextName())); | |
673 | } | |
674 | ||
675 | /** | |
676 | * Removes the specified class file transformer from this class loader. | |
677 | * It will no longer be able to modify the byte code of any classes | |
678 | * loaded by the class loader after the invocation of this method. | |
679 | * However, any classes already modified by this transformer will | |
680 | * remain transformed. | |
681 | * | |
682 | * @param transformer The transformer to remove | |
683 | */ | |
684 | @Override | |
685 | public void removeTransformer(ClassFileTransformer transformer) { | |
686 | ||
687 | if (transformer == null) { | |
688 | return; | |
689 | } | |
690 | ||
691 | if (this.transformers.remove(transformer)) { | |
692 | log.info(sm.getString("webappClassLoader.removeTransformer", | |
693 | transformer, getContextName())); | |
694 | return; | |
695 | } | |
696 | ||
697 | } | |
698 | 31 | |
699 | 32 | /** |
700 | 33 | * Returns a copy of this class loader without any class file |
701 | 34 | * transformers. This is a tool often used by Java Persistence API |
702 | 35 | * providers to inspect entity classes in the absence of any |
703 | 36 | * instrumentation, something that can't be guaranteed within the |
704 | * context of a {@link ClassFileTransformer}'s | |
705 | * {@link ClassFileTransformer#transform(ClassLoader, String, Class, | |
706 | * ProtectionDomain, byte[]) transform} method. | |
37 | * context of a {@link java.lang.instrument.ClassFileTransformer}'s | |
38 | * {@link java.lang.instrument.ClassFileTransformer#transform(ClassLoader, | |
39 | * String, Class, java.security.ProtectionDomain, byte[]) transform} method. | |
707 | 40 | * <p> |
708 | 41 | * The returned class loader's resource cache will have been cleared |
709 | 42 | * so that classes already instrumented will not be retained or |
716 | 49 | |
717 | 50 | WebappClassLoader result = new WebappClassLoader(getParent()); |
718 | 51 | |
719 | result.resources = this.resources; | |
720 | result.delegate = this.delegate; | |
721 | result.state = this.state; | |
722 | result.needConvert = this.needConvert; | |
723 | result.clearReferencesStatic = this.clearReferencesStatic; | |
724 | result.clearReferencesStopThreads = this.clearReferencesStopThreads; | |
725 | result.clearReferencesStopTimerThreads = this.clearReferencesStopTimerThreads; | |
726 | result.clearReferencesLogFactoryRelease = this.clearReferencesLogFactoryRelease; | |
727 | result.clearReferencesHttpClientKeepAliveThread = this.clearReferencesHttpClientKeepAliveThread; | |
728 | result.jarModificationTimes.putAll(this.jarModificationTimes); | |
729 | result.permissionList.addAll(this.permissionList); | |
730 | result.loaderPC.putAll(this.loaderPC); | |
52 | super.copyStateWithoutTransformers(result); | |
731 | 53 | |
732 | 54 | try { |
733 | 55 | result.start(); |
738 | 60 | return result; |
739 | 61 | } |
740 | 62 | |
741 | /** | |
742 | * Have one or more classes or resources been modified so that a reload | |
743 | * is appropriate? | |
744 | */ | |
745 | public boolean modified() { | |
746 | ||
747 | if (log.isDebugEnabled()) | |
748 | log.debug("modified()"); | |
749 | ||
750 | for (Entry<String,ResourceEntry> entry : resourceEntries.entrySet()) { | |
751 | long cachedLastModified = entry.getValue().lastModified; | |
752 | long lastModified = resources.getClassLoaderResource( | |
753 | entry.getKey()).getLastModified(); | |
754 | if (lastModified != cachedLastModified) { | |
755 | if( log.isDebugEnabled() ) | |
756 | log.debug(sm.getString("webappClassLoader.resourceModified", | |
757 | entry.getKey(), | |
758 | new Date(cachedLastModified), | |
759 | new Date(lastModified))); | |
760 | return true; | |
761 | } | |
762 | } | |
763 | ||
764 | // Check if JARs have been added or removed | |
765 | WebResource[] jars = resources.listResources("/WEB-INF/lib"); | |
766 | // Filter out non-JAR resources | |
767 | ||
768 | int jarCount = 0; | |
769 | for (WebResource jar : jars) { | |
770 | if (jar.getName().endsWith(".jar") && jar.isFile() && jar.canRead()) { | |
771 | jarCount++; | |
772 | Long recordedLastModified = jarModificationTimes.get(jar.getName()); | |
773 | if (recordedLastModified == null) { | |
774 | // Jar has been added | |
775 | log.info(sm.getString("webappClassLoader.jarsAdded", | |
776 | resources.getContext().getName())); | |
777 | return true; | |
778 | } | |
779 | if (recordedLastModified.longValue() != jar.getLastModified()) { | |
780 | // Jar has been changed | |
781 | log.info(sm.getString("webappClassLoader.jarsModified", | |
782 | resources.getContext().getName())); | |
783 | return true; | |
784 | } | |
785 | } | |
786 | } | |
787 | ||
788 | if (jarCount < jarModificationTimes.size()){ | |
789 | log.info(sm.getString("webappClassLoader.jarsRemoved", | |
790 | resources.getContext().getName())); | |
791 | return true; | |
792 | } | |
793 | ||
794 | ||
795 | // No classes have been modified | |
796 | return false; | |
797 | } | |
798 | ||
799 | 63 | |
800 | 64 | /** |
801 | * Render a String representation of this object. | |
65 | * This class loader is not parallel capable so lock on the class loader | |
66 | * rather than a per-class lock. | |
802 | 67 | */ |
803 | 68 | @Override |
804 | public String toString() { | |
805 | ||
806 | StringBuilder sb = new StringBuilder("WebappClassLoader\r\n"); | |
807 | sb.append(" context: "); | |
808 | sb.append(getContextName()); | |
809 | sb.append("\r\n"); | |
810 | sb.append(" delegate: "); | |
811 | sb.append(delegate); | |
812 | sb.append("\r\n"); | |
813 | if (this.parent != null) { | |
814 | sb.append("----------> Parent Classloader:\r\n"); | |
815 | sb.append(this.parent.toString()); | |
816 | sb.append("\r\n"); | |
817 | } | |
818 | if (this.transformers.size() > 0) { | |
819 | sb.append("----------> Class file transformers:\r\n"); | |
820 | for (ClassFileTransformer transformer : this.transformers) { | |
821 | sb.append(transformer).append("\r\n"); | |
822 | } | |
823 | } | |
824 | return (sb.toString()); | |
825 | ||
826 | } | |
827 | ||
828 | ||
829 | // ---------------------------------------------------- ClassLoader Methods | |
830 | ||
831 | ||
832 | /** | |
833 | * Expose this method for use by the unit tests. | |
834 | */ | |
835 | protected final Class<?> doDefineClass(String name, byte[] b, int off, int len, | |
836 | ProtectionDomain protectionDomain) { | |
837 | return super.defineClass(name, b, off, len, protectionDomain); | |
838 | } | |
839 | ||
840 | /** | |
841 | * Find the specified class in our local repositories, if possible. If | |
842 | * not found, throw <code>ClassNotFoundException</code>. | |
843 | * | |
844 | * @param name The binary name of the class to be loaded | |
845 | * | |
846 | * @exception ClassNotFoundException if the class was not found | |
847 | */ | |
848 | @Override | |
849 | public Class<?> findClass(String name) throws ClassNotFoundException { | |
850 | ||
851 | if (log.isDebugEnabled()) | |
852 | log.debug(" findClass(" + name + ")"); | |
853 | ||
854 | checkStateForClassLoading(name); | |
855 | ||
856 | // (1) Permission to define this class when using a SecurityManager | |
857 | if (securityManager != null) { | |
858 | int i = name.lastIndexOf('.'); | |
859 | if (i >= 0) { | |
860 | try { | |
861 | if (log.isTraceEnabled()) | |
862 | log.trace(" securityManager.checkPackageDefinition"); | |
863 | securityManager.checkPackageDefinition(name.substring(0,i)); | |
864 | } catch (Exception se) { | |
865 | if (log.isTraceEnabled()) | |
866 | log.trace(" -->Exception-->ClassNotFoundException", se); | |
867 | throw new ClassNotFoundException(name, se); | |
868 | } | |
869 | } | |
870 | } | |
871 | ||
872 | // Ask our superclass to locate this class, if possible | |
873 | // (throws ClassNotFoundException if it is not found) | |
874 | Class<?> clazz = null; | |
875 | try { | |
876 | if (log.isTraceEnabled()) | |
877 | log.trace(" findClassInternal(" + name + ")"); | |
878 | try { | |
879 | clazz = findClassInternal(name); | |
880 | } catch(AccessControlException ace) { | |
881 | log.warn("WebappClassLoader.findClassInternal(" + name | |
882 | + ") security exception: " + ace.getMessage(), ace); | |
883 | throw new ClassNotFoundException(name, ace); | |
884 | } catch (RuntimeException e) { | |
885 | if (log.isTraceEnabled()) | |
886 | log.trace(" -->RuntimeException Rethrown", e); | |
887 | throw e; | |
888 | } | |
889 | if ((clazz == null) && hasExternalRepositories) { | |
890 | try { | |
891 | clazz = super.findClass(name); | |
892 | } catch(AccessControlException ace) { | |
893 | log.warn("WebappClassLoader.findClassInternal(" + name | |
894 | + ") security exception: " + ace.getMessage(), ace); | |
895 | throw new ClassNotFoundException(name, ace); | |
896 | } catch (RuntimeException e) { | |
897 | if (log.isTraceEnabled()) | |
898 | log.trace(" -->RuntimeException Rethrown", e); | |
899 | throw e; | |
900 | } | |
901 | } | |
902 | if (clazz == null) { | |
903 | if (log.isDebugEnabled()) | |
904 | log.debug(" --> Returning ClassNotFoundException"); | |
905 | throw new ClassNotFoundException(name); | |
906 | } | |
907 | } catch (ClassNotFoundException e) { | |
908 | if (log.isTraceEnabled()) | |
909 | log.trace(" --> Passing on ClassNotFoundException"); | |
910 | throw e; | |
911 | } | |
912 | ||
913 | // Return the class we have located | |
914 | if (log.isTraceEnabled()) | |
915 | log.debug(" Returning class " + clazz); | |
916 | ||
917 | if (log.isTraceEnabled()) { | |
918 | ClassLoader cl; | |
919 | if (Globals.IS_SECURITY_ENABLED){ | |
920 | cl = AccessController.doPrivileged( | |
921 | new PrivilegedGetClassLoader(clazz)); | |
922 | } else { | |
923 | cl = clazz.getClassLoader(); | |
924 | } | |
925 | log.debug(" Loaded by " + cl.toString()); | |
926 | } | |
927 | return (clazz); | |
928 | ||
929 | } | |
930 | ||
931 | ||
932 | /** | |
933 | * Find the specified resource in our local repository, and return a | |
934 | * <code>URL</code> referring to it, or <code>null</code> if this resource | |
935 | * cannot be found. | |
936 | * | |
937 | * @param name Name of the resource to be found | |
938 | */ | |
939 | @Override | |
940 | public URL findResource(final String name) { | |
941 | ||
942 | if (log.isDebugEnabled()) | |
943 | log.debug(" findResource(" + name + ")"); | |
944 | ||
945 | URL url = null; | |
946 | ||
947 | String path = nameToPath(name); | |
948 | ||
949 | ResourceEntry entry = resourceEntries.get(path); | |
950 | if (entry == null) { | |
951 | if (securityManager != null) { | |
952 | PrivilegedAction<ResourceEntry> dp = | |
953 | new PrivilegedFindResourceByName(name, path); | |
954 | entry = AccessController.doPrivileged(dp); | |
955 | } else { | |
956 | entry = findResourceInternal(name, path); | |
957 | } | |
958 | } | |
959 | if (entry != null) { | |
960 | url = entry.source; | |
961 | } | |
962 | ||
963 | if ((url == null) && hasExternalRepositories) { | |
964 | url = super.findResource(name); | |
965 | } | |
966 | ||
967 | if (log.isDebugEnabled()) { | |
968 | if (url != null) | |
969 | log.debug(" --> Returning '" + url.toString() + "'"); | |
970 | else | |
971 | log.debug(" --> Resource not found, returning null"); | |
972 | } | |
973 | return (url); | |
974 | ||
975 | } | |
976 | ||
977 | ||
978 | /** | |
979 | * Return an enumeration of <code>URLs</code> representing all of the | |
980 | * resources with the given name. If no resources with this name are | |
981 | * found, return an empty enumeration. | |
982 | * | |
983 | * @param name Name of the resources to be found | |
984 | * | |
985 | * @exception IOException if an input/output error occurs | |
986 | */ | |
987 | @Override | |
988 | public Enumeration<URL> findResources(String name) throws IOException { | |
989 | ||
990 | if (log.isDebugEnabled()) | |
991 | log.debug(" findResources(" + name + ")"); | |
992 | ||
993 | LinkedHashSet<URL> result = new LinkedHashSet<>(); | |
994 | ||
995 | String path = nameToPath(name); | |
996 | ||
997 | WebResource[] webResources = resources.getClassLoaderResources(path); | |
998 | for (WebResource webResource : webResources) { | |
999 | if (webResource.exists()) { | |
1000 | result.add(webResource.getURL()); | |
1001 | } | |
1002 | } | |
1003 | ||
1004 | // Adding the results of a call to the superclass | |
1005 | if (hasExternalRepositories) { | |
1006 | Enumeration<URL> otherResourcePaths = super.findResources(name); | |
1007 | while (otherResourcePaths.hasMoreElements()) { | |
1008 | result.add(otherResourcePaths.nextElement()); | |
1009 | } | |
1010 | } | |
1011 | ||
1012 | return Collections.enumeration(result); | |
1013 | } | |
1014 | ||
1015 | ||
1016 | /** | |
1017 | * Find the resource with the given name. A resource is some data | |
1018 | * (images, audio, text, etc.) that can be accessed by class code in a | |
1019 | * way that is independent of the location of the code. The name of a | |
1020 | * resource is a "/"-separated path name that identifies the resource. | |
1021 | * If the resource cannot be found, return <code>null</code>. | |
1022 | * <p> | |
1023 | * This method searches according to the following algorithm, returning | |
1024 | * as soon as it finds the appropriate URL. If the resource cannot be | |
1025 | * found, returns <code>null</code>. | |
1026 | * <ul> | |
1027 | * <li>If the <code>delegate</code> property is set to <code>true</code>, | |
1028 | * call the <code>getResource()</code> method of the parent class | |
1029 | * loader, if any.</li> | |
1030 | * <li>Call <code>findResource()</code> to find this resource in our | |
1031 | * locally defined repositories.</li> | |
1032 | * <li>Call the <code>getResource()</code> method of the parent class | |
1033 | * loader, if any.</li> | |
1034 | * </ul> | |
1035 | * | |
1036 | * @param name Name of the resource to return a URL for | |
1037 | */ | |
1038 | @Override | |
1039 | public URL getResource(String name) { | |
1040 | ||
1041 | if (log.isDebugEnabled()) | |
1042 | log.debug("getResource(" + name + ")"); | |
1043 | URL url = null; | |
1044 | ||
1045 | // (1) Delegate to parent if requested | |
1046 | if (delegate) { | |
1047 | if (log.isDebugEnabled()) | |
1048 | log.debug(" Delegating to parent classloader " + parent); | |
1049 | url = parent.getResource(name); | |
1050 | if (url != null) { | |
1051 | if (log.isDebugEnabled()) | |
1052 | log.debug(" --> Returning '" + url.toString() + "'"); | |
1053 | return (url); | |
1054 | } | |
1055 | } | |
1056 | ||
1057 | // (2) Search local repositories | |
1058 | url = findResource(name); | |
1059 | if (url != null) { | |
1060 | if (log.isDebugEnabled()) | |
1061 | log.debug(" --> Returning '" + url.toString() + "'"); | |
1062 | return (url); | |
1063 | } | |
1064 | ||
1065 | // (3) Delegate to parent unconditionally if not already attempted | |
1066 | if( !delegate ) { | |
1067 | url = parent.getResource(name); | |
1068 | if (url != null) { | |
1069 | if (log.isDebugEnabled()) | |
1070 | log.debug(" --> Returning '" + url.toString() + "'"); | |
1071 | return (url); | |
1072 | } | |
1073 | } | |
1074 | ||
1075 | // (4) Resource was not found | |
1076 | if (log.isDebugEnabled()) | |
1077 | log.debug(" --> Resource not found, returning null"); | |
1078 | return (null); | |
1079 | ||
1080 | } | |
1081 | ||
1082 | ||
1083 | /** | |
1084 | * Find the resource with the given name, and return an input stream | |
1085 | * that can be used for reading it. The search order is as described | |
1086 | * for <code>getResource()</code>, after checking to see if the resource | |
1087 | * data has been previously cached. If the resource cannot be found, | |
1088 | * return <code>null</code>. | |
1089 | * | |
1090 | * @param name Name of the resource to return an input stream for | |
1091 | */ | |
1092 | @Override | |
1093 | public InputStream getResourceAsStream(String name) { | |
1094 | ||
1095 | if (log.isDebugEnabled()) | |
1096 | log.debug("getResourceAsStream(" + name + ")"); | |
1097 | InputStream stream = null; | |
1098 | ||
1099 | // (0) Check for a cached copy of this resource | |
1100 | stream = findLoadedResource(name); | |
1101 | if (stream != null) { | |
1102 | if (log.isDebugEnabled()) | |
1103 | log.debug(" --> Returning stream from cache"); | |
1104 | return (stream); | |
1105 | } | |
1106 | ||
1107 | // (1) Delegate to parent if requested | |
1108 | if (delegate) { | |
1109 | if (log.isDebugEnabled()) | |
1110 | log.debug(" Delegating to parent classloader " + parent); | |
1111 | stream = parent.getResourceAsStream(name); | |
1112 | if (stream != null) { | |
1113 | // FIXME - cache??? | |
1114 | if (log.isDebugEnabled()) | |
1115 | log.debug(" --> Returning stream from parent"); | |
1116 | return (stream); | |
1117 | } | |
1118 | } | |
1119 | ||
1120 | // (2) Search local repositories | |
1121 | if (log.isDebugEnabled()) | |
1122 | log.debug(" Searching local repositories"); | |
1123 | URL url = findResource(name); | |
1124 | if (url != null) { | |
1125 | // FIXME - cache??? | |
1126 | if (log.isDebugEnabled()) | |
1127 | log.debug(" --> Returning stream from local"); | |
1128 | stream = findLoadedResource(name); | |
1129 | try { | |
1130 | if (hasExternalRepositories && (stream == null)) | |
1131 | stream = url.openStream(); | |
1132 | } catch (IOException e) { | |
1133 | // Ignore | |
1134 | } | |
1135 | if (stream != null) | |
1136 | return (stream); | |
1137 | } | |
1138 | ||
1139 | // (3) Delegate to parent unconditionally | |
1140 | if (!delegate) { | |
1141 | if (log.isDebugEnabled()) | |
1142 | log.debug(" Delegating to parent classloader unconditionally " + parent); | |
1143 | stream = parent.getResourceAsStream(name); | |
1144 | if (stream != null) { | |
1145 | // FIXME - cache??? | |
1146 | if (log.isDebugEnabled()) | |
1147 | log.debug(" --> Returning stream from parent"); | |
1148 | return (stream); | |
1149 | } | |
1150 | } | |
1151 | ||
1152 | // (4) Resource was not found | |
1153 | if (log.isDebugEnabled()) | |
1154 | log.debug(" --> Resource not found, returning null"); | |
1155 | return (null); | |
1156 | ||
1157 | } | |
1158 | ||
1159 | ||
1160 | /** | |
1161 | * Load the class with the specified name. This method searches for | |
1162 | * classes in the same manner as <code>loadClass(String, boolean)</code> | |
1163 | * with <code>false</code> as the second argument. | |
1164 | * | |
1165 | * @param name The binary name of the class to be loaded | |
1166 | * | |
1167 | * @exception ClassNotFoundException if the class was not found | |
1168 | */ | |
1169 | @Override | |
1170 | public Class<?> loadClass(String name) throws ClassNotFoundException { | |
1171 | ||
1172 | return (loadClass(name, false)); | |
1173 | ||
1174 | } | |
1175 | ||
1176 | ||
1177 | /** | |
1178 | * Load the class with the specified name, searching using the following | |
1179 | * algorithm until it finds and returns the class. If the class cannot | |
1180 | * be found, returns <code>ClassNotFoundException</code>. | |
1181 | * <ul> | |
1182 | * <li>Call <code>findLoadedClass(String)</code> to check if the | |
1183 | * class has already been loaded. If it has, the same | |
1184 | * <code>Class</code> object is returned.</li> | |
1185 | * <li>If the <code>delegate</code> property is set to <code>true</code>, | |
1186 | * call the <code>loadClass()</code> method of the parent class | |
1187 | * loader, if any.</li> | |
1188 | * <li>Call <code>findClass()</code> to find this class in our locally | |
1189 | * defined repositories.</li> | |
1190 | * <li>Call the <code>loadClass()</code> method of our parent | |
1191 | * class loader, if any.</li> | |
1192 | * </ul> | |
1193 | * If the class was found using the above steps, and the | |
1194 | * <code>resolve</code> flag is <code>true</code>, this method will then | |
1195 | * call <code>resolveClass(Class)</code> on the resulting Class object. | |
1196 | * | |
1197 | * @param name The binary name of the class to be loaded | |
1198 | * @param resolve If <code>true</code> then resolve the class | |
1199 | * | |
1200 | * @exception ClassNotFoundException if the class was not found | |
1201 | */ | |
1202 | @Override | |
1203 | public synchronized Class<?> loadClass(String name, boolean resolve) | |
1204 | throws ClassNotFoundException { | |
1205 | ||
1206 | if (log.isDebugEnabled()) | |
1207 | log.debug("loadClass(" + name + ", " + resolve + ")"); | |
1208 | Class<?> clazz = null; | |
1209 | ||
1210 | // Log access to stopped class loader | |
1211 | checkStateForClassLoading(name); | |
1212 | ||
1213 | // (0) Check our previously loaded local class cache | |
1214 | clazz = findLoadedClass0(name); | |
1215 | if (clazz != null) { | |
1216 | if (log.isDebugEnabled()) | |
1217 | log.debug(" Returning class from cache"); | |
1218 | if (resolve) | |
1219 | resolveClass(clazz); | |
1220 | return (clazz); | |
1221 | } | |
1222 | ||
1223 | // (0.1) Check our previously loaded class cache | |
1224 | clazz = findLoadedClass(name); | |
1225 | if (clazz != null) { | |
1226 | if (log.isDebugEnabled()) | |
1227 | log.debug(" Returning class from cache"); | |
1228 | if (resolve) | |
1229 | resolveClass(clazz); | |
1230 | return (clazz); | |
1231 | } | |
1232 | ||
1233 | // (0.2) Try loading the class with the system class loader, to prevent | |
1234 | // the webapp from overriding J2SE classes | |
1235 | String resourceName = binaryNameToPath(name, false); | |
1236 | ClassLoader javaseLoader = getJavaseClassLoader(); | |
1237 | if (javaseLoader.getResource(resourceName) != null) { | |
1238 | try { | |
1239 | clazz = javaseLoader.loadClass(name); | |
1240 | if (clazz != null) { | |
1241 | if (resolve) | |
1242 | resolveClass(clazz); | |
1243 | return (clazz); | |
1244 | } | |
1245 | } catch (ClassNotFoundException e) { | |
1246 | // Ignore | |
1247 | } | |
1248 | } | |
1249 | ||
1250 | // (0.5) Permission to access this class when using a SecurityManager | |
1251 | if (securityManager != null) { | |
1252 | int i = name.lastIndexOf('.'); | |
1253 | if (i >= 0) { | |
1254 | try { | |
1255 | securityManager.checkPackageAccess(name.substring(0,i)); | |
1256 | } catch (SecurityException se) { | |
1257 | String error = "Security Violation, attempt to use " + | |
1258 | "Restricted Class: " + name; | |
1259 | log.info(error, se); | |
1260 | throw new ClassNotFoundException(error, se); | |
1261 | } | |
1262 | } | |
1263 | } | |
1264 | ||
1265 | boolean delegateLoad = delegate || filter(name); | |
1266 | ||
1267 | // (1) Delegate to our parent if requested | |
1268 | if (delegateLoad) { | |
1269 | if (log.isDebugEnabled()) | |
1270 | log.debug(" Delegating to parent classloader1 " + parent); | |
1271 | try { | |
1272 | clazz = Class.forName(name, false, parent); | |
1273 | if (clazz != null) { | |
1274 | if (log.isDebugEnabled()) | |
1275 | log.debug(" Loading class from parent"); | |
1276 | if (resolve) | |
1277 | resolveClass(clazz); | |
1278 | return (clazz); | |
1279 | } | |
1280 | } catch (ClassNotFoundException e) { | |
1281 | // Ignore | |
1282 | } | |
1283 | } | |
1284 | ||
1285 | // (2) Search local repositories | |
1286 | if (log.isDebugEnabled()) | |
1287 | log.debug(" Searching local repositories"); | |
1288 | try { | |
1289 | clazz = findClass(name); | |
1290 | if (clazz != null) { | |
1291 | if (log.isDebugEnabled()) | |
1292 | log.debug(" Loading class from local repository"); | |
1293 | if (resolve) | |
1294 | resolveClass(clazz); | |
1295 | return (clazz); | |
1296 | } | |
1297 | } catch (ClassNotFoundException e) { | |
1298 | // Ignore | |
1299 | } | |
1300 | ||
1301 | // (3) Delegate to parent unconditionally | |
1302 | if (!delegateLoad) { | |
1303 | if (log.isDebugEnabled()) | |
1304 | log.debug(" Delegating to parent classloader at end: " + parent); | |
1305 | try { | |
1306 | clazz = Class.forName(name, false, parent); | |
1307 | if (clazz != null) { | |
1308 | if (log.isDebugEnabled()) | |
1309 | log.debug(" Loading class from parent"); | |
1310 | if (resolve) | |
1311 | resolveClass(clazz); | |
1312 | return (clazz); | |
1313 | } | |
1314 | } catch (ClassNotFoundException e) { | |
1315 | // Ignore | |
1316 | } | |
1317 | } | |
1318 | ||
1319 | throw new ClassNotFoundException(name); | |
1320 | } | |
1321 | ||
1322 | ||
1323 | protected void checkStateForClassLoading(String className) throws ClassNotFoundException { | |
1324 | // It is not permitted to load new classes once the web application has | |
1325 | // been stopped. | |
1326 | if (!state.isAvailable()) { | |
1327 | String msg = sm.getString("webappClassLoader.stopped", className); | |
1328 | IllegalStateException cause = new IllegalStateException(msg); | |
1329 | ClassNotFoundException cnfe = new ClassNotFoundException(); | |
1330 | cnfe.initCause(cause); | |
1331 | log.info(msg, cnfe); | |
1332 | throw cnfe; | |
1333 | } | |
1334 | } | |
1335 | ||
1336 | ||
1337 | /** | |
1338 | * Get the Permissions for a CodeSource. If this instance | |
1339 | * of WebappClassLoader is for a web application context, | |
1340 | * add read FilePermission or JndiPermissions for the base | |
1341 | * directory (if unpacked), | |
1342 | * the context URL, and jar file resources. | |
1343 | * | |
1344 | * @param codeSource where the code was loaded from | |
1345 | * @return PermissionCollection for CodeSource | |
1346 | */ | |
1347 | @Override | |
1348 | protected PermissionCollection getPermissions(CodeSource codeSource) { | |
1349 | ||
1350 | String codeUrl = codeSource.getLocation().toString(); | |
1351 | PermissionCollection pc; | |
1352 | if ((pc = loaderPC.get(codeUrl)) == null) { | |
1353 | pc = super.getPermissions(codeSource); | |
1354 | if (pc != null) { | |
1355 | Iterator<Permission> perms = permissionList.iterator(); | |
1356 | while (perms.hasNext()) { | |
1357 | Permission p = perms.next(); | |
1358 | pc.add(p); | |
1359 | } | |
1360 | loaderPC.put(codeUrl,pc); | |
1361 | } | |
1362 | } | |
1363 | return (pc); | |
1364 | ||
1365 | } | |
1366 | ||
1367 | ||
1368 | /** | |
1369 | * {@inheritDoc} | |
1370 | * <p> | |
1371 | * Note that list of URLs returned by this method may not be complete. The | |
1372 | * web application class loader accesses class loader resources via the | |
1373 | * {@link WebResourceRoot} which supports the arbitrary mapping of | |
1374 | * additional files, directories and contents of JAR files under | |
1375 | * WEB-INF/classes. Any such resources will not be included in the URLs | |
1376 | * returned here. | |
1377 | */ | |
1378 | @Override | |
1379 | public URL[] getURLs() { | |
1380 | ArrayList<URL> result = new ArrayList<>(); | |
1381 | result.addAll(localRepositories); | |
1382 | result.addAll(Arrays.asList(super.getURLs())); | |
1383 | return result.toArray(new URL[result.size()]); | |
1384 | } | |
1385 | ||
1386 | ||
1387 | // ------------------------------------------------------ Lifecycle Methods | |
1388 | ||
1389 | ||
1390 | /** | |
1391 | * Add a lifecycle event listener to this component. | |
1392 | * | |
1393 | * @param listener The listener to add | |
1394 | */ | |
1395 | @Override | |
1396 | public void addLifecycleListener(LifecycleListener listener) { | |
1397 | // NOOP | |
1398 | } | |
1399 | ||
1400 | ||
1401 | /** | |
1402 | * Get the lifecycle listeners associated with this lifecycle. If this | |
1403 | * Lifecycle has no listeners registered, a zero-length array is returned. | |
1404 | */ | |
1405 | @Override | |
1406 | public LifecycleListener[] findLifecycleListeners() { | |
1407 | return new LifecycleListener[0]; | |
1408 | } | |
1409 | ||
1410 | ||
1411 | /** | |
1412 | * Remove a lifecycle event listener from this component. | |
1413 | * | |
1414 | * @param listener The listener to remove | |
1415 | */ | |
1416 | @Override | |
1417 | public void removeLifecycleListener(LifecycleListener listener) { | |
1418 | // NOOP | |
1419 | } | |
1420 | ||
1421 | ||
1422 | /** | |
1423 | * Obtain the current state of the source component. | |
1424 | * | |
1425 | * @return The current state of the source component. | |
1426 | */ | |
1427 | @Override | |
1428 | public LifecycleState getState() { | |
1429 | return state; | |
1430 | } | |
1431 | ||
1432 | ||
1433 | /** | |
1434 | * {@inheritDoc} | |
1435 | */ | |
1436 | @Override | |
1437 | public String getStateName() { | |
1438 | return getState().toString(); | |
1439 | } | |
1440 | ||
1441 | ||
1442 | @Override | |
1443 | public void init() { | |
1444 | state = LifecycleState.INITIALIZED; | |
1445 | } | |
1446 | ||
1447 | ||
1448 | /** | |
1449 | * Start the class loader. | |
1450 | * | |
1451 | * @exception LifecycleException if a lifecycle error occurs | |
1452 | */ | |
1453 | @Override | |
1454 | public void start() throws LifecycleException { | |
1455 | ||
1456 | state = LifecycleState.STARTING_PREP; | |
1457 | ||
1458 | WebResource classes = resources.getResource("/WEB-INF/classes"); | |
1459 | if (classes.isDirectory() && classes.canRead()) { | |
1460 | localRepositories.add(classes.getURL()); | |
1461 | } | |
1462 | WebResource[] jars = resources.listResources("/WEB-INF/lib"); | |
1463 | for (WebResource jar : jars) { | |
1464 | if (jar.getName().endsWith(".jar") && jar.isFile() && jar.canRead()) { | |
1465 | localRepositories.add(jar.getURL()); | |
1466 | jarModificationTimes.put( | |
1467 | jar.getName(), Long.valueOf(jar.getLastModified())); | |
1468 | } | |
1469 | } | |
1470 | ||
1471 | state = LifecycleState.STARTING; | |
1472 | ||
1473 | String encoding = null; | |
1474 | try { | |
1475 | encoding = System.getProperty("file.encoding"); | |
1476 | } catch (SecurityException e) { | |
1477 | return; | |
1478 | } | |
1479 | if (encoding.indexOf("EBCDIC")!=-1) { | |
1480 | needConvert = true; | |
1481 | } | |
1482 | ||
1483 | state = LifecycleState.STARTED; | |
1484 | } | |
1485 | ||
1486 | ||
1487 | /** | |
1488 | * Stop the class loader. | |
1489 | * | |
1490 | * @exception LifecycleException if a lifecycle error occurs | |
1491 | */ | |
1492 | @Override | |
1493 | public void stop() throws LifecycleException { | |
1494 | ||
1495 | state = LifecycleState.STOPPING_PREP; | |
1496 | ||
1497 | // Clearing references should be done before setting started to | |
1498 | // false, due to possible side effects | |
1499 | clearReferences(); | |
1500 | ||
1501 | state = LifecycleState.STOPPING; | |
1502 | ||
1503 | resourceEntries.clear(); | |
1504 | jarModificationTimes.clear(); | |
1505 | resources = null; | |
1506 | ||
1507 | permissionList.clear(); | |
1508 | loaderPC.clear(); | |
1509 | ||
1510 | state = LifecycleState.STOPPED; | |
1511 | } | |
1512 | ||
1513 | ||
1514 | @Override | |
1515 | public void destroy() { | |
1516 | state = LifecycleState.DESTROYING; | |
1517 | ||
1518 | try { | |
1519 | super.close(); | |
1520 | } catch (IOException ioe) { | |
1521 | log.warn(sm.getString("webappClassLoader.superCloseFail"), ioe); | |
1522 | } | |
1523 | state = LifecycleState.DESTROYED; | |
1524 | } | |
1525 | ||
1526 | ||
1527 | // ------------------------------------------------------ Protected Methods | |
1528 | ||
1529 | protected ClassLoader getJavaseClassLoader() { | |
1530 | return javaseClassLoader; | |
1531 | } | |
1532 | ||
1533 | protected void setJavaseClassLoader(ClassLoader classLoader) { | |
1534 | if (classLoader == null) { | |
1535 | throw new IllegalArgumentException( | |
1536 | sm.getString("webappClassLoader.javaseClassLoaderNull")); | |
1537 | } | |
1538 | javaseClassLoader = classLoader; | |
1539 | } | |
1540 | ||
1541 | /** | |
1542 | * Clear references. | |
1543 | */ | |
1544 | protected void clearReferences() { | |
1545 | ||
1546 | // De-register any remaining JDBC drivers | |
1547 | clearReferencesJdbc(); | |
1548 | ||
1549 | // Stop any threads the web application started | |
1550 | clearReferencesThreads(); | |
1551 | ||
1552 | // Check for leaks triggered by ThreadLocals loaded by this class loader | |
1553 | checkThreadLocalsForLeaks(); | |
1554 | ||
1555 | // Clear RMI Targets loaded by this class loader | |
1556 | clearReferencesRmiTargets(); | |
1557 | ||
1558 | // Null out any static or final fields from loaded classes, | |
1559 | // as a workaround for apparent garbage collection bugs | |
1560 | if (clearReferencesStatic) { | |
1561 | clearReferencesStaticFinal(); | |
1562 | } | |
1563 | ||
1564 | // Clear the IntrospectionUtils cache. | |
1565 | IntrospectionUtils.clear(); | |
1566 | ||
1567 | // Clear the classloader reference in common-logging | |
1568 | if (clearReferencesLogFactoryRelease) { | |
1569 | org.apache.juli.logging.LogFactory.release(this); | |
1570 | } | |
1571 | ||
1572 | // Clear the resource bundle cache | |
1573 | // This shouldn't be necessary, the cache uses weak references but | |
1574 | // it has caused leaks. Oddly, using the leak detection code in | |
1575 | // standard host allows the class loader to be GC'd. This has been seen | |
1576 | // on Sun but not IBM JREs. Maybe a bug in Sun's GC impl? | |
1577 | clearReferencesResourceBundles(); | |
1578 | ||
1579 | // Clear the classloader reference in the VM's bean introspector | |
1580 | java.beans.Introspector.flushCaches(); | |
1581 | ||
1582 | // Clear any custom URLStreamHandlers | |
1583 | TomcatURLStreamHandlerFactory.release(this); | |
1584 | } | |
1585 | ||
1586 | ||
1587 | /** | |
1588 | * Deregister any JDBC drivers registered by the webapp that the webapp | |
1589 | * forgot. This is made unnecessary complex because a) DriverManager | |
1590 | * checks the class loader of the calling class (it would be much easier | |
1591 | * if it checked the context class loader) b) using reflection would | |
1592 | * create a dependency on the DriverManager implementation which can, | |
1593 | * and has, changed. | |
1594 | * | |
1595 | * We can't just create an instance of JdbcLeakPrevention as it will be | |
1596 | * loaded by the common class loader (since it's .class file is in the | |
1597 | * $CATALINA_HOME/lib directory). This would fail DriverManager's check | |
1598 | * on the class loader of the calling class. So, we load the bytes via | |
1599 | * our parent class loader but define the class with this class loader | |
1600 | * so the JdbcLeakPrevention looks like a webapp class to the | |
1601 | * DriverManager. | |
1602 | * | |
1603 | * If only apps cleaned up after themselves... | |
1604 | */ | |
1605 | private final void clearReferencesJdbc() { | |
1606 | // We know roughly how big the class will be (~ 1K) so allow 2k as a | |
1607 | // starting point | |
1608 | byte[] classBytes = new byte[2048]; | |
1609 | int offset = 0; | |
1610 | try (InputStream is = getResourceAsStream( | |
1611 | "org/apache/catalina/loader/JdbcLeakPrevention.class")) { | |
1612 | int read = is.read(classBytes, offset, classBytes.length-offset); | |
1613 | while (read > -1) { | |
1614 | offset += read; | |
1615 | if (offset == classBytes.length) { | |
1616 | // Buffer full - double size | |
1617 | byte[] tmp = new byte[classBytes.length * 2]; | |
1618 | System.arraycopy(classBytes, 0, tmp, 0, classBytes.length); | |
1619 | classBytes = tmp; | |
1620 | } | |
1621 | read = is.read(classBytes, offset, classBytes.length-offset); | |
1622 | } | |
1623 | Class<?> lpClass = | |
1624 | defineClass("org.apache.catalina.loader.JdbcLeakPrevention", | |
1625 | classBytes, 0, offset, this.getClass().getProtectionDomain()); | |
1626 | Object obj = lpClass.newInstance(); | |
1627 | @SuppressWarnings("unchecked") | |
1628 | List<String> driverNames = (List<String>) obj.getClass().getMethod( | |
1629 | "clearJdbcDriverRegistrations").invoke(obj); | |
1630 | for (String name : driverNames) { | |
1631 | log.warn(sm.getString("webappClassLoader.clearJdbc", | |
1632 | getContextName(), name)); | |
1633 | } | |
1634 | } catch (Exception e) { | |
1635 | // So many things to go wrong above... | |
1636 | Throwable t = ExceptionUtils.unwrapInvocationTargetException(e); | |
1637 | ExceptionUtils.handleThrowable(t); | |
1638 | log.warn(sm.getString( | |
1639 | "webappClassLoader.jdbcRemoveFailed", getContextName()), t); | |
1640 | } | |
1641 | } | |
1642 | ||
1643 | ||
1644 | private final void clearReferencesStaticFinal() { | |
1645 | ||
1646 | Collection<ResourceEntry> values = resourceEntries.values(); | |
1647 | Iterator<ResourceEntry> loadedClasses = values.iterator(); | |
1648 | // | |
1649 | // walk through all loaded class to trigger initialization for | |
1650 | // any uninitialized classes, otherwise initialization of | |
1651 | // one class may call a previously cleared class. | |
1652 | while(loadedClasses.hasNext()) { | |
1653 | ResourceEntry entry = loadedClasses.next(); | |
1654 | if (entry.loadedClass != null) { | |
1655 | Class<?> clazz = entry.loadedClass; | |
1656 | try { | |
1657 | Field[] fields = clazz.getDeclaredFields(); | |
1658 | for (int i = 0; i < fields.length; i++) { | |
1659 | if(Modifier.isStatic(fields[i].getModifiers())) { | |
1660 | fields[i].get(null); | |
1661 | break; | |
1662 | } | |
1663 | } | |
1664 | } catch(Throwable t) { | |
1665 | // Ignore | |
1666 | } | |
1667 | } | |
1668 | } | |
1669 | loadedClasses = values.iterator(); | |
1670 | while (loadedClasses.hasNext()) { | |
1671 | ResourceEntry entry = loadedClasses.next(); | |
1672 | if (entry.loadedClass != null) { | |
1673 | Class<?> clazz = entry.loadedClass; | |
1674 | try { | |
1675 | Field[] fields = clazz.getDeclaredFields(); | |
1676 | for (int i = 0; i < fields.length; i++) { | |
1677 | Field field = fields[i]; | |
1678 | int mods = field.getModifiers(); | |
1679 | if (field.getType().isPrimitive() | |
1680 | || (field.getName().indexOf("$") != -1)) { | |
1681 | continue; | |
1682 | } | |
1683 | if (Modifier.isStatic(mods)) { | |
1684 | try { | |
1685 | field.setAccessible(true); | |
1686 | if (Modifier.isFinal(mods)) { | |
1687 | if (!((field.getType().getName().startsWith("java.")) | |
1688 | || (field.getType().getName().startsWith("javax.")))) { | |
1689 | nullInstance(field.get(null)); | |
1690 | } | |
1691 | } else { | |
1692 | field.set(null, null); | |
1693 | if (log.isDebugEnabled()) { | |
1694 | log.debug("Set field " + field.getName() | |
1695 | + " to null in class " + clazz.getName()); | |
1696 | } | |
1697 | } | |
1698 | } catch (Throwable t) { | |
1699 | ExceptionUtils.handleThrowable(t); | |
1700 | if (log.isDebugEnabled()) { | |
1701 | log.debug("Could not set field " + field.getName() | |
1702 | + " to null in class " + clazz.getName(), t); | |
1703 | } | |
1704 | } | |
1705 | } | |
1706 | } | |
1707 | } catch (Throwable t) { | |
1708 | ExceptionUtils.handleThrowable(t); | |
1709 | if (log.isDebugEnabled()) { | |
1710 | log.debug("Could not clean fields for class " + clazz.getName(), t); | |
1711 | } | |
1712 | } | |
1713 | } | |
1714 | } | |
1715 | ||
1716 | } | |
1717 | ||
1718 | ||
1719 | private void nullInstance(Object instance) { | |
1720 | if (instance == null) { | |
1721 | return; | |
1722 | } | |
1723 | Field[] fields = instance.getClass().getDeclaredFields(); | |
1724 | for (int i = 0; i < fields.length; i++) { | |
1725 | Field field = fields[i]; | |
1726 | int mods = field.getModifiers(); | |
1727 | if (field.getType().isPrimitive() | |
1728 | || (field.getName().indexOf("$") != -1)) { | |
1729 | continue; | |
1730 | } | |
1731 | try { | |
1732 | field.setAccessible(true); | |
1733 | if (Modifier.isStatic(mods) && Modifier.isFinal(mods)) { | |
1734 | // Doing something recursively is too risky | |
1735 | continue; | |
1736 | } | |
1737 | Object value = field.get(instance); | |
1738 | if (null != value) { | |
1739 | Class<? extends Object> valueClass = value.getClass(); | |
1740 | if (!loadedByThisOrChild(valueClass)) { | |
1741 | if (log.isDebugEnabled()) { | |
1742 | log.debug("Not setting field " + field.getName() + | |
1743 | " to null in object of class " + | |
1744 | instance.getClass().getName() + | |
1745 | " because the referenced object was of type " + | |
1746 | valueClass.getName() + | |
1747 | " which was not loaded by this WebappClassLoader."); | |
1748 | } | |
1749 | } else { | |
1750 | field.set(instance, null); | |
1751 | if (log.isDebugEnabled()) { | |
1752 | log.debug("Set field " + field.getName() | |
1753 | + " to null in class " + instance.getClass().getName()); | |
1754 | } | |
1755 | } | |
1756 | } | |
1757 | } catch (Throwable t) { | |
1758 | ExceptionUtils.handleThrowable(t); | |
1759 | if (log.isDebugEnabled()) { | |
1760 | log.debug("Could not set field " + field.getName() | |
1761 | + " to null in object instance of class " | |
1762 | + instance.getClass().getName(), t); | |
1763 | } | |
1764 | } | |
1765 | } | |
1766 | } | |
1767 | ||
1768 | ||
1769 | @SuppressWarnings("deprecation") // thread.stop() | |
1770 | private void clearReferencesThreads() { | |
1771 | Thread[] threads = getThreads(); | |
1772 | List<Thread> executorThreadsToStop = new ArrayList<>(); | |
1773 | ||
1774 | // Iterate over the set of threads | |
1775 | for (Thread thread : threads) { | |
1776 | if (thread != null) { | |
1777 | ClassLoader ccl = thread.getContextClassLoader(); | |
1778 | if (ccl == this) { | |
1779 | // Don't warn about this thread | |
1780 | if (thread == Thread.currentThread()) { | |
1781 | continue; | |
1782 | } | |
1783 | ||
1784 | final String threadName = thread.getName(); | |
1785 | ||
1786 | // JVM controlled threads | |
1787 | ThreadGroup tg = thread.getThreadGroup(); | |
1788 | if (tg != null && | |
1789 | JVM_THREAD_GROUP_NAMES.contains(tg.getName())) { | |
1790 | ||
1791 | // HttpClient keep-alive threads | |
1792 | if (clearReferencesHttpClientKeepAliveThread && | |
1793 | threadName.equals("Keep-Alive-Timer")) { | |
1794 | thread.setContextClassLoader(parent); | |
1795 | log.debug(sm.getString( | |
1796 | "webappClassLoader.checkThreadsHttpClient")); | |
1797 | } | |
1798 | ||
1799 | // Don't warn about remaining JVM controlled threads | |
1800 | continue; | |
1801 | } | |
1802 | ||
1803 | // Skip threads that have already died | |
1804 | if (!thread.isAlive()) { | |
1805 | continue; | |
1806 | } | |
1807 | ||
1808 | // TimerThread can be stopped safely so treat separately | |
1809 | // "java.util.TimerThread" in Sun/Oracle JDK | |
1810 | // "java.util.Timer$TimerImpl" in Apache Harmony and in IBM JDK | |
1811 | if (thread.getClass().getName().startsWith("java.util.Timer") && | |
1812 | clearReferencesStopTimerThreads) { | |
1813 | clearReferencesStopTimerThread(thread); | |
1814 | continue; | |
1815 | } | |
1816 | ||
1817 | if (isRequestThread(thread)) { | |
1818 | log.warn(sm.getString("webappClassLoader.stackTraceRequestThread", | |
1819 | getContextName(), threadName, getStackTrace(thread))); | |
1820 | } else { | |
1821 | log.warn(sm.getString("webappClassLoader.stackTrace", | |
1822 | getContextName(), threadName, getStackTrace(thread))); | |
1823 | } | |
1824 | ||
1825 | // Don't try an stop the threads unless explicitly | |
1826 | // configured to do so | |
1827 | if (!clearReferencesStopThreads) { | |
1828 | continue; | |
1829 | } | |
1830 | ||
1831 | // If the thread has been started via an executor, try | |
1832 | // shutting down the executor | |
1833 | boolean usingExecutor = false; | |
1834 | try { | |
1835 | ||
1836 | // Runnable wrapped by Thread | |
1837 | // "target" in Sun/Oracle JDK | |
1838 | // "runnable" in IBM JDK | |
1839 | // "action" in Apache Harmony | |
1840 | Object target = null; | |
1841 | for (String fieldName : new String[] { "target", | |
1842 | "runnable", "action" }) { | |
1843 | try { | |
1844 | Field targetField = thread.getClass() | |
1845 | .getDeclaredField(fieldName); | |
1846 | targetField.setAccessible(true); | |
1847 | target = targetField.get(thread); | |
1848 | break; | |
1849 | } catch (NoSuchFieldException nfe) { | |
1850 | continue; | |
1851 | } | |
1852 | } | |
1853 | ||
1854 | // "java.util.concurrent" code is in public domain, | |
1855 | // so all implementations are similar | |
1856 | if (target != null && | |
1857 | target.getClass().getCanonicalName() != null | |
1858 | && target.getClass().getCanonicalName().equals( | |
1859 | "java.util.concurrent.ThreadPoolExecutor.Worker")) { | |
1860 | Field executorField = | |
1861 | target.getClass().getDeclaredField("this$0"); | |
1862 | executorField.setAccessible(true); | |
1863 | Object executor = executorField.get(target); | |
1864 | if (executor instanceof ThreadPoolExecutor) { | |
1865 | ((ThreadPoolExecutor) executor).shutdownNow(); | |
1866 | usingExecutor = true; | |
1867 | } | |
1868 | } | |
1869 | } catch (SecurityException e) { | |
1870 | log.warn(sm.getString( | |
1871 | "webappClassLoader.stopThreadFail", | |
1872 | thread.getName(), getContextName()), e); | |
1873 | } catch (NoSuchFieldException e) { | |
1874 | log.warn(sm.getString( | |
1875 | "webappClassLoader.stopThreadFail", | |
1876 | thread.getName(), getContextName()), e); | |
1877 | } catch (IllegalArgumentException e) { | |
1878 | log.warn(sm.getString( | |
1879 | "webappClassLoader.stopThreadFail", | |
1880 | thread.getName(), getContextName()), e); | |
1881 | } catch (IllegalAccessException e) { | |
1882 | log.warn(sm.getString( | |
1883 | "webappClassLoader.stopThreadFail", | |
1884 | thread.getName(), getContextName()), e); | |
1885 | } | |
1886 | ||
1887 | if (usingExecutor) { | |
1888 | // Executor may take a short time to stop all the | |
1889 | // threads. Make a note of threads that should be | |
1890 | // stopped and check them at the end of the method. | |
1891 | executorThreadsToStop.add(thread); | |
1892 | } else { | |
1893 | // This method is deprecated and for good reason. This | |
1894 | // is very risky code but is the only option at this | |
1895 | // point. A *very* good reason for apps to do this | |
1896 | // clean-up themselves. | |
1897 | thread.stop(); | |
1898 | } | |
1899 | } | |
1900 | } | |
1901 | } | |
1902 | ||
1903 | // If thread stopping is enabled, executor threads should have been | |
1904 | // stopped above when the executor was shut down but that depends on the | |
1905 | // thread correctly handling the interrupt. Give all the executor | |
1906 | // threads a few seconds shutdown and if they are still running | |
1907 | // Give threads up to 2 seconds to shutdown | |
1908 | int count = 0; | |
1909 | for (Thread t : executorThreadsToStop) { | |
1910 | while (t.isAlive() && count < 100) { | |
1911 | try { | |
1912 | Thread.sleep(20); | |
1913 | } catch (InterruptedException e) { | |
1914 | // Quit the while loop | |
1915 | break; | |
1916 | } | |
1917 | count++; | |
1918 | } | |
1919 | if (t.isAlive()) { | |
1920 | // This method is deprecated and for good reason. This is | |
1921 | // very risky code but is the only option at this point. | |
1922 | // A *very* good reason for apps to do this clean-up | |
1923 | // themselves. | |
1924 | t.stop(); | |
1925 | } | |
1926 | } | |
1927 | } | |
1928 | ||
1929 | ||
1930 | /* | |
1931 | * Look at a threads stack trace to see if it is a request thread or not. It | |
1932 | * isn't perfect, but it should be good-enough for most cases. | |
1933 | */ | |
1934 | private boolean isRequestThread(Thread thread) { | |
1935 | ||
1936 | StackTraceElement[] elements = thread.getStackTrace(); | |
1937 | ||
1938 | if (elements == null || elements.length == 0) { | |
1939 | // Must have stopped already. Too late to ignore it. Assume not a | |
1940 | // request processing thread. | |
1941 | return false; | |
1942 | } | |
1943 | ||
1944 | // Step through the methods in reverse order looking for calls to any | |
1945 | // CoyoteAdapter method. All request threads will have this unless | |
1946 | // Tomcat has been heavily modified - in which case there isn't much we | |
1947 | // can do. | |
1948 | for (int i = 0; i < elements.length; i++) { | |
1949 | StackTraceElement element = elements[elements.length - (i+1)]; | |
1950 | if ("org.apache.catalina.connector.CoyoteAdapter".equals( | |
1951 | element.getClassName())) { | |
1952 | return true; | |
1953 | } | |
1954 | } | |
1955 | return false; | |
1956 | } | |
1957 | ||
1958 | ||
1959 | private void clearReferencesStopTimerThread(Thread thread) { | |
1960 | ||
1961 | // Need to get references to: | |
1962 | // in Sun/Oracle JDK: | |
1963 | // - newTasksMayBeScheduled field (in java.util.TimerThread) | |
1964 | // - queue field | |
1965 | // - queue.clear() | |
1966 | // in IBM JDK, Apache Harmony: | |
1967 | // - cancel() method (in java.util.Timer$TimerImpl) | |
1968 | ||
1969 | try { | |
1970 | ||
1971 | try { | |
1972 | Field newTasksMayBeScheduledField = | |
1973 | thread.getClass().getDeclaredField("newTasksMayBeScheduled"); | |
1974 | newTasksMayBeScheduledField.setAccessible(true); | |
1975 | Field queueField = thread.getClass().getDeclaredField("queue"); | |
1976 | queueField.setAccessible(true); | |
1977 | ||
1978 | Object queue = queueField.get(thread); | |
1979 | ||
1980 | Method clearMethod = queue.getClass().getDeclaredMethod("clear"); | |
1981 | clearMethod.setAccessible(true); | |
1982 | ||
1983 | synchronized(queue) { | |
1984 | newTasksMayBeScheduledField.setBoolean(thread, false); | |
1985 | clearMethod.invoke(queue); | |
1986 | queue.notify(); // In case queue was already empty. | |
1987 | } | |
1988 | ||
1989 | }catch (NoSuchFieldException nfe){ | |
1990 | Method cancelMethod = thread.getClass().getDeclaredMethod("cancel"); | |
1991 | synchronized(thread) { | |
1992 | cancelMethod.setAccessible(true); | |
1993 | cancelMethod.invoke(thread); | |
1994 | } | |
1995 | } | |
1996 | ||
1997 | log.warn(sm.getString("webappClassLoader.warnTimerThread", | |
1998 | getContextName(), thread.getName())); | |
1999 | ||
2000 | } catch (Exception e) { | |
2001 | // So many things to go wrong above... | |
2002 | Throwable t = ExceptionUtils.unwrapInvocationTargetException(e); | |
2003 | ExceptionUtils.handleThrowable(t); | |
2004 | log.warn(sm.getString( | |
2005 | "webappClassLoader.stopTimerThreadFail", | |
2006 | thread.getName(), getContextName()), t); | |
2007 | } | |
2008 | } | |
2009 | ||
2010 | private void checkThreadLocalsForLeaks() { | |
2011 | Thread[] threads = getThreads(); | |
2012 | ||
2013 | try { | |
2014 | // Make the fields in the Thread class that store ThreadLocals | |
2015 | // accessible | |
2016 | Field threadLocalsField = | |
2017 | Thread.class.getDeclaredField("threadLocals"); | |
2018 | threadLocalsField.setAccessible(true); | |
2019 | Field inheritableThreadLocalsField = | |
2020 | Thread.class.getDeclaredField("inheritableThreadLocals"); | |
2021 | inheritableThreadLocalsField.setAccessible(true); | |
2022 | // Make the underlying array of ThreadLoad.ThreadLocalMap.Entry objects | |
2023 | // accessible | |
2024 | Class<?> tlmClass = Class.forName("java.lang.ThreadLocal$ThreadLocalMap"); | |
2025 | Field tableField = tlmClass.getDeclaredField("table"); | |
2026 | tableField.setAccessible(true); | |
2027 | Method expungeStaleEntriesMethod = tlmClass.getDeclaredMethod("expungeStaleEntries"); | |
2028 | expungeStaleEntriesMethod.setAccessible(true); | |
2029 | ||
2030 | for (int i = 0; i < threads.length; i++) { | |
2031 | Object threadLocalMap; | |
2032 | if (threads[i] != null) { | |
2033 | ||
2034 | // Clear the first map | |
2035 | threadLocalMap = threadLocalsField.get(threads[i]); | |
2036 | if (null != threadLocalMap){ | |
2037 | expungeStaleEntriesMethod.invoke(threadLocalMap); | |
2038 | checkThreadLocalMapForLeaks(threadLocalMap, tableField); | |
2039 | } | |
2040 | ||
2041 | // Clear the second map | |
2042 | threadLocalMap =inheritableThreadLocalsField.get(threads[i]); | |
2043 | if (null != threadLocalMap){ | |
2044 | expungeStaleEntriesMethod.invoke(threadLocalMap); | |
2045 | checkThreadLocalMapForLeaks(threadLocalMap, tableField); | |
2046 | } | |
2047 | } | |
2048 | } | |
2049 | } catch (Throwable t) { | |
2050 | ExceptionUtils.handleThrowable(t); | |
2051 | log.warn(sm.getString( | |
2052 | "webappClassLoader.checkThreadLocalsForLeaksFail", | |
2053 | getContextName()), t); | |
2054 | } | |
2055 | } | |
2056 | ||
2057 | ||
2058 | /** | |
2059 | * Analyzes the given thread local map object. Also pass in the field that | |
2060 | * points to the internal table to save re-calculating it on every | |
2061 | * call to this method. | |
2062 | */ | |
2063 | private void checkThreadLocalMapForLeaks(Object map, | |
2064 | Field internalTableField) throws IllegalAccessException, | |
2065 | NoSuchFieldException { | |
2066 | if (map != null) { | |
2067 | Object[] table = (Object[]) internalTableField.get(map); | |
2068 | if (table != null) { | |
2069 | for (int j =0; j < table.length; j++) { | |
2070 | Object obj = table[j]; | |
2071 | if (obj != null) { | |
2072 | boolean potentialLeak = false; | |
2073 | // Check the key | |
2074 | Object key = ((Reference<?>) obj).get(); | |
2075 | if (this.equals(key) || loadedByThisOrChild(key)) { | |
2076 | potentialLeak = true; | |
2077 | } | |
2078 | // Check the value | |
2079 | Field valueField = | |
2080 | obj.getClass().getDeclaredField("value"); | |
2081 | valueField.setAccessible(true); | |
2082 | Object value = valueField.get(obj); | |
2083 | if (this.equals(value) || loadedByThisOrChild(value)) { | |
2084 | potentialLeak = true; | |
2085 | } | |
2086 | if (potentialLeak) { | |
2087 | Object[] args = new Object[5]; | |
2088 | args[0] = getContextName(); | |
2089 | if (key != null) { | |
2090 | args[1] = getPrettyClassName(key.getClass()); | |
2091 | try { | |
2092 | args[2] = key.toString(); | |
2093 | } catch (Exception e) { | |
2094 | log.warn(sm.getString( | |
2095 | "webappClassLoader.checkThreadLocalsForLeaks.badKey", | |
2096 | args[1]), e); | |
2097 | args[2] = sm.getString( | |
2098 | "webappClassLoader.checkThreadLocalsForLeaks.unknown"); | |
2099 | } | |
2100 | } | |
2101 | if (value != null) { | |
2102 | args[3] = getPrettyClassName(value.getClass()); | |
2103 | try { | |
2104 | args[4] = value.toString(); | |
2105 | } catch (Exception e) { | |
2106 | log.warn(sm.getString( | |
2107 | "webappClassLoader.checkThreadLocalsForLeaks.badValue", | |
2108 | args[3]), e); | |
2109 | args[4] = sm.getString( | |
2110 | "webappClassLoader.checkThreadLocalsForLeaks.unknown"); | |
2111 | } | |
2112 | } | |
2113 | if (value == null) { | |
2114 | if (log.isDebugEnabled()) { | |
2115 | log.debug(sm.getString( | |
2116 | "webappClassLoader.checkThreadLocalsForLeaksDebug", | |
2117 | args)); | |
2118 | } | |
2119 | } else { | |
2120 | log.error(sm.getString( | |
2121 | "webappClassLoader.checkThreadLocalsForLeaks", | |
2122 | args)); | |
2123 | } | |
2124 | } | |
2125 | } | |
2126 | } | |
2127 | } | |
2128 | } | |
2129 | } | |
2130 | ||
2131 | private String getPrettyClassName(Class<?> clazz) { | |
2132 | String name = clazz.getCanonicalName(); | |
2133 | if (name==null){ | |
2134 | name = clazz.getName(); | |
2135 | } | |
2136 | return name; | |
2137 | } | |
2138 | ||
2139 | private String getStackTrace(Thread thread) { | |
2140 | StringBuilder builder = new StringBuilder(); | |
2141 | for (StackTraceElement ste : thread.getStackTrace()) { | |
2142 | builder.append("\n ").append(ste); | |
2143 | } | |
2144 | return builder.toString(); | |
2145 | } | |
2146 | ||
2147 | /** | |
2148 | * @param o object to test, may be null | |
2149 | * @return <code>true</code> if o has been loaded by the current classloader | |
2150 | * or one of its descendants. | |
2151 | */ | |
2152 | private boolean loadedByThisOrChild(Object o) { | |
2153 | if (o == null) { | |
2154 | return false; | |
2155 | } | |
2156 | ||
2157 | Class<?> clazz; | |
2158 | if (o instanceof Class) { | |
2159 | clazz = (Class<?>) o; | |
2160 | } else { | |
2161 | clazz = o.getClass(); | |
2162 | } | |
2163 | ||
2164 | ClassLoader cl = clazz.getClassLoader(); | |
2165 | while (cl != null) { | |
2166 | if (cl == this) { | |
2167 | return true; | |
2168 | } | |
2169 | cl = cl.getParent(); | |
2170 | } | |
2171 | ||
2172 | if (o instanceof Collection<?>) { | |
2173 | Iterator<?> iter = ((Collection<?>) o).iterator(); | |
2174 | try { | |
2175 | while (iter.hasNext()) { | |
2176 | Object entry = iter.next(); | |
2177 | if (loadedByThisOrChild(entry)) { | |
2178 | return true; | |
2179 | } | |
2180 | } | |
2181 | } catch (ConcurrentModificationException e) { | |
2182 | log.warn(sm.getString( | |
2183 | "webappClassLoader", clazz.getName(), getContextName()), | |
2184 | e); | |
2185 | } | |
2186 | } | |
2187 | return false; | |
2188 | } | |
2189 | ||
2190 | /* | |
2191 | * Get the set of current threads as an array. | |
2192 | */ | |
2193 | private Thread[] getThreads() { | |
2194 | // Get the current thread group | |
2195 | ThreadGroup tg = Thread.currentThread().getThreadGroup(); | |
2196 | // Find the root thread group | |
2197 | try { | |
2198 | while (tg.getParent() != null) { | |
2199 | tg = tg.getParent(); | |
2200 | } | |
2201 | } catch (SecurityException se) { | |
2202 | String msg = sm.getString( | |
2203 | "webappClassLoader.getThreadGroupError", tg.getName()); | |
2204 | if (log.isDebugEnabled()) { | |
2205 | log.debug(msg, se); | |
2206 | } else { | |
2207 | log.warn(msg); | |
2208 | } | |
2209 | } | |
2210 | ||
2211 | int threadCountGuess = tg.activeCount() + 50; | |
2212 | Thread[] threads = new Thread[threadCountGuess]; | |
2213 | int threadCountActual = tg.enumerate(threads); | |
2214 | // Make sure we don't miss any threads | |
2215 | while (threadCountActual == threadCountGuess) { | |
2216 | threadCountGuess *=2; | |
2217 | threads = new Thread[threadCountGuess]; | |
2218 | // Note tg.enumerate(Thread[]) silently ignores any threads that | |
2219 | // can't fit into the array | |
2220 | threadCountActual = tg.enumerate(threads); | |
2221 | } | |
2222 | ||
2223 | return threads; | |
2224 | } | |
2225 | ||
2226 | ||
2227 | /** | |
2228 | * This depends on the internals of the Sun JVM so it does everything by | |
2229 | * reflection. | |
2230 | */ | |
2231 | private void clearReferencesRmiTargets() { | |
2232 | try { | |
2233 | // Need access to the ccl field of sun.rmi.transport.Target | |
2234 | Class<?> objectTargetClass = | |
2235 | Class.forName("sun.rmi.transport.Target"); | |
2236 | Field cclField = objectTargetClass.getDeclaredField("ccl"); | |
2237 | cclField.setAccessible(true); | |
2238 | ||
2239 | // Clear the objTable map | |
2240 | Class<?> objectTableClass = | |
2241 | Class.forName("sun.rmi.transport.ObjectTable"); | |
2242 | Field objTableField = objectTableClass.getDeclaredField("objTable"); | |
2243 | objTableField.setAccessible(true); | |
2244 | Object objTable = objTableField.get(null); | |
2245 | if (objTable == null) { | |
2246 | return; | |
2247 | } | |
2248 | ||
2249 | // Iterate over the values in the table | |
2250 | if (objTable instanceof Map<?,?>) { | |
2251 | Iterator<?> iter = ((Map<?,?>) objTable).values().iterator(); | |
2252 | while (iter.hasNext()) { | |
2253 | Object obj = iter.next(); | |
2254 | Object cclObject = cclField.get(obj); | |
2255 | if (this == cclObject) { | |
2256 | iter.remove(); | |
2257 | } | |
2258 | } | |
2259 | } | |
2260 | ||
2261 | // Clear the implTable map | |
2262 | Field implTableField = objectTableClass.getDeclaredField("implTable"); | |
2263 | implTableField.setAccessible(true); | |
2264 | Object implTable = implTableField.get(null); | |
2265 | if (implTable == null) { | |
2266 | return; | |
2267 | } | |
2268 | ||
2269 | // Iterate over the values in the table | |
2270 | if (implTable instanceof Map<?,?>) { | |
2271 | Iterator<?> iter = ((Map<?,?>) implTable).values().iterator(); | |
2272 | while (iter.hasNext()) { | |
2273 | Object obj = iter.next(); | |
2274 | Object cclObject = cclField.get(obj); | |
2275 | if (this == cclObject) { | |
2276 | iter.remove(); | |
2277 | } | |
2278 | } | |
2279 | } | |
2280 | } catch (ClassNotFoundException e) { | |
2281 | log.info(sm.getString("webappClassLoader.clearRmiInfo", | |
2282 | getContextName()), e); | |
2283 | } catch (SecurityException e) { | |
2284 | log.warn(sm.getString("webappClassLoader.clearRmiFail", | |
2285 | getContextName()), e); | |
2286 | } catch (NoSuchFieldException e) { | |
2287 | log.warn(sm.getString("webappClassLoader.clearRmiFail", | |
2288 | getContextName()), e); | |
2289 | } catch (IllegalArgumentException e) { | |
2290 | log.warn(sm.getString("webappClassLoader.clearRmiFail", | |
2291 | getContextName()), e); | |
2292 | } catch (IllegalAccessException e) { | |
2293 | log.warn(sm.getString("webappClassLoader.clearRmiFail", | |
2294 | getContextName()), e); | |
2295 | } | |
2296 | } | |
2297 | ||
2298 | ||
2299 | /** | |
2300 | * Clear the {@link ResourceBundle} cache of any bundles loaded by this | |
2301 | * class loader or any class loader where this loader is a parent class | |
2302 | * loader. Whilst {@link ResourceBundle#clearCache()} could be used there | |
2303 | * are complications around the | |
2304 | * {@link org.apache.jasper.servlet.JasperLoader} that mean a reflection | |
2305 | * based approach is more likely to be complete. | |
2306 | * | |
2307 | * The ResourceBundle is using WeakReferences so it shouldn't be pinning the | |
2308 | * class loader in memory. However, it is. Therefore clear ou the | |
2309 | * references. | |
2310 | */ | |
2311 | private void clearReferencesResourceBundles() { | |
2312 | // Get a reference to the cache | |
2313 | try { | |
2314 | Field cacheListField = | |
2315 | ResourceBundle.class.getDeclaredField("cacheList"); | |
2316 | cacheListField.setAccessible(true); | |
2317 | ||
2318 | // Java 6 uses ConcurrentMap | |
2319 | // Java 5 uses SoftCache extends Abstract Map | |
2320 | // So use Map and it *should* work with both | |
2321 | Map<?,?> cacheList = (Map<?,?>) cacheListField.get(null); | |
2322 | ||
2323 | // Get the keys (loader references are in the key) | |
2324 | Set<?> keys = cacheList.keySet(); | |
2325 | ||
2326 | Field loaderRefField = null; | |
2327 | ||
2328 | // Iterate over the keys looking at the loader instances | |
2329 | Iterator<?> keysIter = keys.iterator(); | |
2330 | ||
2331 | int countRemoved = 0; | |
2332 | ||
2333 | while (keysIter.hasNext()) { | |
2334 | Object key = keysIter.next(); | |
2335 | ||
2336 | if (loaderRefField == null) { | |
2337 | loaderRefField = | |
2338 | key.getClass().getDeclaredField("loaderRef"); | |
2339 | loaderRefField.setAccessible(true); | |
2340 | } | |
2341 | WeakReference<?> loaderRef = | |
2342 | (WeakReference<?>) loaderRefField.get(key); | |
2343 | ||
2344 | ClassLoader loader = (ClassLoader) loaderRef.get(); | |
2345 | ||
2346 | while (loader != null && loader != this) { | |
2347 | loader = loader.getParent(); | |
2348 | } | |
2349 | ||
2350 | if (loader != null) { | |
2351 | keysIter.remove(); | |
2352 | countRemoved++; | |
2353 | } | |
2354 | } | |
2355 | ||
2356 | if (countRemoved > 0 && log.isDebugEnabled()) { | |
2357 | log.debug(sm.getString( | |
2358 | "webappClassLoader.clearReferencesResourceBundlesCount", | |
2359 | Integer.valueOf(countRemoved), getContextName())); | |
2360 | } | |
2361 | } catch (SecurityException e) { | |
2362 | log.warn(sm.getString( | |
2363 | "webappClassLoader.clearReferencesResourceBundlesFail", | |
2364 | getContextName()), e); | |
2365 | } catch (NoSuchFieldException e) { | |
2366 | if (Globals.IS_ORACLE_JVM) { | |
2367 | log.warn(sm.getString( | |
2368 | "webappClassLoader.clearReferencesResourceBundlesFail", | |
2369 | getContextName()), e); | |
2370 | } else { | |
2371 | log.debug(sm.getString( | |
2372 | "webappClassLoader.clearReferencesResourceBundlesFail", | |
2373 | getContextName()), e); | |
2374 | } | |
2375 | } catch (IllegalArgumentException e) { | |
2376 | log.warn(sm.getString( | |
2377 | "webappClassLoader.clearReferencesResourceBundlesFail", | |
2378 | getContextName()), e); | |
2379 | } catch (IllegalAccessException e) { | |
2380 | log.warn(sm.getString( | |
2381 | "webappClassLoader.clearReferencesResourceBundlesFail", | |
2382 | getContextName()), e); | |
2383 | } | |
2384 | } | |
2385 | ||
2386 | ||
2387 | /** | |
2388 | * Find specified class in local repositories. | |
2389 | * | |
2390 | * @param name The binary name of the class to be loaded | |
2391 | * | |
2392 | * @return the loaded class, or null if the class isn't found | |
2393 | */ | |
2394 | protected Class<?> findClassInternal(String name) { | |
2395 | ||
2396 | if (!validate(name)) { | |
2397 | return null; | |
2398 | } | |
2399 | ||
2400 | String path = binaryNameToPath(name, true); | |
2401 | ||
2402 | ResourceEntry entry = null; | |
2403 | ||
2404 | if (securityManager != null) { | |
2405 | PrivilegedAction<ResourceEntry> dp = | |
2406 | new PrivilegedFindResourceByName(name, path); | |
2407 | entry = AccessController.doPrivileged(dp); | |
2408 | } else { | |
2409 | entry = findResourceInternal(name, path); | |
2410 | } | |
2411 | ||
2412 | if (entry == null) { | |
2413 | return null; | |
2414 | } | |
2415 | ||
2416 | Class<?> clazz = entry.loadedClass; | |
2417 | if (clazz != null) | |
2418 | return clazz; | |
2419 | ||
2420 | synchronized (this) { | |
2421 | clazz = entry.loadedClass; | |
2422 | if (clazz != null) | |
2423 | return clazz; | |
2424 | ||
2425 | if (entry.binaryContent == null) { | |
2426 | return null; | |
2427 | } | |
2428 | ||
2429 | // Looking up the package | |
2430 | String packageName = null; | |
2431 | int pos = name.lastIndexOf('.'); | |
2432 | if (pos != -1) | |
2433 | packageName = name.substring(0, pos); | |
2434 | ||
2435 | Package pkg = null; | |
2436 | ||
2437 | if (packageName != null) { | |
2438 | pkg = getPackage(packageName); | |
2439 | // Define the package (if null) | |
2440 | if (pkg == null) { | |
2441 | try { | |
2442 | if (entry.manifest == null) { | |
2443 | definePackage(packageName, null, null, null, null, | |
2444 | null, null, null); | |
2445 | } else { | |
2446 | definePackage(packageName, entry.manifest, | |
2447 | entry.codeBase); | |
2448 | } | |
2449 | } catch (IllegalArgumentException e) { | |
2450 | // Ignore: normal error due to dual definition of package | |
2451 | } | |
2452 | pkg = getPackage(packageName); | |
2453 | } | |
2454 | } | |
2455 | ||
2456 | if (securityManager != null) { | |
2457 | ||
2458 | // Checking sealing | |
2459 | if (pkg != null) { | |
2460 | boolean sealCheck = true; | |
2461 | if (pkg.isSealed()) { | |
2462 | sealCheck = pkg.isSealed(entry.codeBase); | |
2463 | } else { | |
2464 | sealCheck = (entry.manifest == null) | |
2465 | || !isPackageSealed(packageName, entry.manifest); | |
2466 | } | |
2467 | if (!sealCheck) | |
2468 | throw new SecurityException | |
2469 | ("Sealing violation loading " + name + " : Package " | |
2470 | + packageName + " is sealed."); | |
2471 | } | |
2472 | ||
2473 | } | |
2474 | ||
2475 | try { | |
2476 | clazz = defineClass(name, entry.binaryContent, 0, | |
2477 | entry.binaryContent.length, | |
2478 | new CodeSource(entry.codeBase, entry.certificates)); | |
2479 | } catch (UnsupportedClassVersionError ucve) { | |
2480 | throw new UnsupportedClassVersionError( | |
2481 | ucve.getLocalizedMessage() + " " + | |
2482 | sm.getString("webappClassLoader.wrongVersion", | |
2483 | name)); | |
2484 | } | |
2485 | // Now the class has been defined, clear the elements of the local | |
2486 | // resource cache that are no longer required. | |
2487 | entry.loadedClass = clazz; | |
2488 | entry.binaryContent = null; | |
2489 | entry.codeBase = null; | |
2490 | entry.manifest = null; | |
2491 | entry.certificates = null; | |
2492 | // Retain entry.source in case of a getResourceAsStream() call on | |
2493 | // the class file after the class has been defined. | |
2494 | } | |
2495 | ||
2496 | return clazz; | |
2497 | } | |
2498 | ||
2499 | ||
2500 | private String binaryNameToPath(String binaryName, boolean withLeadingSlash) { | |
2501 | // 1 for leading '/', 6 for ".class" | |
2502 | StringBuilder path = new StringBuilder(7 + binaryName.length()); | |
2503 | if (withLeadingSlash) { | |
2504 | path.append('/'); | |
2505 | } | |
2506 | path.append(binaryName.replace('.', '/')); | |
2507 | path.append(CLASS_FILE_SUFFIX); | |
2508 | return path.toString(); | |
2509 | } | |
2510 | ||
2511 | ||
2512 | private String nameToPath(String name) { | |
2513 | if (name.startsWith("/")) { | |
2514 | return name; | |
2515 | } | |
2516 | StringBuilder path = new StringBuilder( | |
2517 | 1 + name.length()); | |
2518 | path.append('/'); | |
2519 | path.append(name); | |
2520 | return path.toString(); | |
2521 | } | |
2522 | ||
2523 | ||
2524 | /** | |
2525 | * Find specified resource in local repositories. | |
2526 | * | |
2527 | * @return the loaded resource, or null if the resource isn't found | |
2528 | */ | |
2529 | protected ResourceEntry findResourceInternal(final String name, final String path) { | |
2530 | ||
2531 | if (!state.isAvailable()) { | |
2532 | log.info(sm.getString("webappClassLoader.stopped", name)); | |
2533 | return null; | |
2534 | } | |
2535 | ||
2536 | if (name == null || path == null) { | |
2537 | return null; | |
2538 | } | |
2539 | ||
2540 | ResourceEntry entry = resourceEntries.get(path); | |
2541 | if (entry != null) { | |
2542 | return entry; | |
2543 | } | |
2544 | ||
2545 | boolean isClassResource = path.endsWith(CLASS_FILE_SUFFIX); | |
2546 | boolean isCacheable = isClassResource; | |
2547 | if (!isCacheable) { | |
2548 | isCacheable = path.startsWith(SERVICES_PREFIX); | |
2549 | } | |
2550 | ||
2551 | WebResource resource = null; | |
2552 | ||
2553 | boolean fileNeedConvert = false; | |
2554 | ||
2555 | resource = resources.getClassLoaderResource(path); | |
2556 | ||
2557 | if (!resource.exists()) { | |
2558 | return null; | |
2559 | } | |
2560 | ||
2561 | entry = new ResourceEntry(); | |
2562 | entry.source = resource.getURL(); | |
2563 | entry.codeBase = entry.source; | |
2564 | entry.lastModified = resource.getLastModified(); | |
2565 | ||
2566 | if (needConvert && path.endsWith(".properties")) { | |
2567 | fileNeedConvert = true; | |
2568 | } | |
2569 | ||
2570 | /* Only cache the binary content if there is some content | |
2571 | * available one of the following is true: | |
2572 | * a) It is a class file since the binary content is only cached | |
2573 | * until the class has been loaded | |
2574 | * or | |
2575 | * b) The file needs conversion to address encoding issues (see | |
2576 | * below) | |
2577 | * or | |
2578 | * c) The resource is a service provider configuration file located | |
2579 | * under META=INF/services | |
2580 | * | |
2581 | * In all other cases do not cache the content to prevent | |
2582 | * excessive memory usage if large resources are present (see | |
2583 | * https://issues.apache.org/bugzilla/show_bug.cgi?id=53081). | |
2584 | */ | |
2585 | if (isCacheable || fileNeedConvert) { | |
2586 | byte[] binaryContent = resource.getContent(); | |
2587 | if (binaryContent != null) { | |
2588 | if (fileNeedConvert) { | |
2589 | // Workaround for certain files on platforms that use | |
2590 | // EBCDIC encoding, when they are read through FileInputStream. | |
2591 | // See commit message of rev.303915 for details | |
2592 | // http://svn.apache.org/viewvc?view=revision&revision=303915 | |
2593 | String str = new String(binaryContent); | |
2594 | try { | |
2595 | binaryContent = str.getBytes(StandardCharsets.UTF_8); | |
2596 | } catch (Exception e) { | |
2597 | return null; | |
2598 | } | |
2599 | } | |
2600 | entry.binaryContent = binaryContent; | |
2601 | // The certificates and manifest are made available as a side | |
2602 | // effect of reading the binary content | |
2603 | entry.certificates = resource.getCertificates(); | |
2604 | } | |
2605 | } | |
2606 | entry.manifest = resource.getManifest(); | |
2607 | ||
2608 | if (isClassResource && entry.binaryContent != null && | |
2609 | this.transformers.size() > 0) { | |
2610 | // If the resource is a class just being loaded, decorate it | |
2611 | // with any attached transformers | |
2612 | String className = name.endsWith(CLASS_FILE_SUFFIX) ? | |
2613 | name.substring(0, name.length() - CLASS_FILE_SUFFIX.length()) : name; | |
2614 | String internalName = className.replace(".", "/"); | |
2615 | ||
2616 | for (ClassFileTransformer transformer : this.transformers) { | |
2617 | try { | |
2618 | byte[] transformed = transformer.transform( | |
2619 | this, internalName, null, null, entry.binaryContent | |
2620 | ); | |
2621 | if (transformed != null) { | |
2622 | entry.binaryContent = transformed; | |
2623 | } | |
2624 | } catch (IllegalClassFormatException e) { | |
2625 | log.error(sm.getString("webappClassLoader.transformError", name), e); | |
2626 | return null; | |
2627 | } | |
2628 | } | |
2629 | } | |
2630 | ||
2631 | // Add the entry in the local resource repository | |
2632 | synchronized (resourceEntries) { | |
2633 | // Ensures that all the threads which may be in a race to load | |
2634 | // a particular class all end up with the same ResourceEntry | |
2635 | // instance | |
2636 | ResourceEntry entry2 = resourceEntries.get(path); | |
2637 | if (entry2 == null) { | |
2638 | resourceEntries.put(path, entry); | |
2639 | } else { | |
2640 | entry = entry2; | |
2641 | } | |
2642 | } | |
2643 | ||
2644 | return entry; | |
2645 | } | |
2646 | ||
2647 | ||
2648 | /** | |
2649 | * Returns true if the specified package name is sealed according to the | |
2650 | * given manifest. | |
2651 | */ | |
2652 | protected boolean isPackageSealed(String name, Manifest man) { | |
2653 | ||
2654 | String path = name.replace('.', '/') + '/'; | |
2655 | Attributes attr = man.getAttributes(path); | |
2656 | String sealed = null; | |
2657 | if (attr != null) { | |
2658 | sealed = attr.getValue(Name.SEALED); | |
2659 | } | |
2660 | if (sealed == null) { | |
2661 | if ((attr = man.getMainAttributes()) != null) { | |
2662 | sealed = attr.getValue(Name.SEALED); | |
2663 | } | |
2664 | } | |
2665 | return "true".equalsIgnoreCase(sealed); | |
2666 | ||
2667 | } | |
2668 | ||
2669 | ||
2670 | /** | |
2671 | * Finds the resource with the given name if it has previously been | |
2672 | * loaded and cached by this class loader, and return an input stream | |
2673 | * to the resource data. If this resource has not been cached, return | |
2674 | * <code>null</code>. | |
2675 | * | |
2676 | * @param name Name of the resource to return | |
2677 | */ | |
2678 | protected InputStream findLoadedResource(String name) { | |
2679 | ||
2680 | String path = nameToPath(name); | |
2681 | ||
2682 | ResourceEntry entry = resourceEntries.get(path); | |
2683 | if (entry != null) { | |
2684 | if (entry.binaryContent != null) | |
2685 | return new ByteArrayInputStream(entry.binaryContent); | |
2686 | else { | |
2687 | try { | |
2688 | return entry.source.openStream(); | |
2689 | } catch (IOException ioe) { | |
2690 | // Ignore | |
2691 | } | |
2692 | } | |
2693 | } | |
2694 | return null; | |
2695 | } | |
2696 | ||
2697 | ||
2698 | /** | |
2699 | * Finds the class with the given name if it has previously been | |
2700 | * loaded and cached by this class loader, and return the Class object. | |
2701 | * If this class has not been cached, return <code>null</code>. | |
2702 | * | |
2703 | * @param name The binary name of the resource to return | |
2704 | */ | |
2705 | protected Class<?> findLoadedClass0(String name) { | |
2706 | ||
2707 | String path = binaryNameToPath(name, true); | |
2708 | ||
2709 | ResourceEntry entry = resourceEntries.get(path); | |
2710 | if (entry != null) { | |
2711 | return entry.loadedClass; | |
2712 | } | |
2713 | return null; | |
2714 | } | |
2715 | ||
2716 | ||
2717 | /** | |
2718 | * Refresh the system policy file, to pick up eventual changes. | |
2719 | */ | |
2720 | protected void refreshPolicy() { | |
2721 | ||
2722 | try { | |
2723 | // The policy file may have been modified to adjust | |
2724 | // permissions, so we're reloading it when loading or | |
2725 | // reloading a Context | |
2726 | Policy policy = Policy.getPolicy(); | |
2727 | policy.refresh(); | |
2728 | } catch (AccessControlException e) { | |
2729 | // Some policy files may restrict this, even for the core, | |
2730 | // so this exception is ignored | |
2731 | } | |
2732 | ||
2733 | } | |
2734 | ||
2735 | ||
2736 | /** | |
2737 | * Filter classes. | |
2738 | * | |
2739 | * @param name class name | |
2740 | * @return true if the class should be filtered | |
2741 | */ | |
2742 | protected synchronized boolean filter(String name) { | |
2743 | ||
2744 | if (name == null) | |
2745 | return false; | |
2746 | ||
2747 | // Looking up the package | |
2748 | String packageName = null; | |
2749 | int pos = name.lastIndexOf('.'); | |
2750 | if (pos != -1) | |
2751 | packageName = name.substring(0, pos); | |
2752 | else | |
2753 | return false; | |
2754 | ||
2755 | packageTriggersPermit.reset(packageName); | |
2756 | if (packageTriggersPermit.lookingAt()) { | |
2757 | return false; | |
2758 | } | |
2759 | ||
2760 | packageTriggersDeny.reset(packageName); | |
2761 | if (packageTriggersDeny.lookingAt()) { | |
2762 | return true; | |
2763 | } | |
2764 | ||
2765 | return false; | |
2766 | } | |
2767 | ||
2768 | ||
2769 | /** | |
2770 | * Validate a classname. As per SRV.9.7.2, we must restrict loading of | |
2771 | * classes from J2SE (java.*) and most classes of the servlet API | |
2772 | * (javax.servlet.*). That should enhance robustness and prevent a number | |
2773 | * of user error (where an older version of servlet.jar would be present | |
2774 | * in /WEB-INF/lib). | |
2775 | * | |
2776 | * @param name class name | |
2777 | * @return true if the name is valid | |
2778 | */ | |
2779 | protected boolean validate(String name) { | |
2780 | ||
2781 | // Need to be careful with order here | |
2782 | if (name == null) { | |
2783 | // Can't load a class without a name | |
2784 | return false; | |
2785 | } | |
2786 | if (name.startsWith("java.")) { | |
2787 | // Must never load java.* classes | |
2788 | return false; | |
2789 | } | |
2790 | if (name.startsWith("javax.servlet.jsp.jstl")) { | |
2791 | // OK for web apps to package JSTL | |
2792 | return true; | |
2793 | } | |
2794 | if (name.startsWith("javax.servlet.")) { | |
2795 | // Web apps should never package any other Servlet or JSP classes | |
2796 | return false; | |
2797 | } | |
2798 | if (name.startsWith("javax.el")) { | |
2799 | // Must never load javax.el.* classes | |
2800 | return false; | |
2801 | } | |
2802 | ||
2803 | // Assume everything else is OK | |
2804 | return true; | |
2805 | ||
2806 | } | |
2807 | ||
2808 | ||
2809 | @Override | |
2810 | protected void addURL(URL url) { | |
2811 | super.addURL(url); | |
2812 | hasExternalRepositories = true; | |
69 | protected Object getClassLoadingLock(String className) { | |
70 | return this; | |
2813 | 71 | } |
2814 | 72 | } |
0 | /* | |
1 | * Licensed to the Apache Software Foundation (ASF) under one or more | |
2 | * contributor license agreements. See the NOTICE file distributed with | |
3 | * this work for additional information regarding copyright ownership. | |
4 | * The ASF licenses this file to You under the Apache License, Version 2.0 | |
5 | * (the "License"); you may not use this file except in compliance with | |
6 | * the License. You may obtain a copy of the License at | |
7 | * | |
8 | * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
15 | */ | |
16 | package org.apache.catalina.loader; | |
17 | ||
18 | import java.io.ByteArrayInputStream; | |
19 | import java.io.File; | |
20 | import java.io.FilePermission; | |
21 | import java.io.IOException; | |
22 | import java.io.InputStream; | |
23 | import java.lang.instrument.ClassFileTransformer; | |
24 | import java.lang.instrument.IllegalClassFormatException; | |
25 | import java.lang.ref.Reference; | |
26 | import java.lang.ref.WeakReference; | |
27 | import java.lang.reflect.Field; | |
28 | import java.lang.reflect.Method; | |
29 | import java.lang.reflect.Modifier; | |
30 | import java.net.URI; | |
31 | import java.net.URISyntaxException; | |
32 | import java.net.URL; | |
33 | import java.net.URLClassLoader; | |
34 | import java.nio.charset.StandardCharsets; | |
35 | import java.security.AccessControlException; | |
36 | import java.security.AccessController; | |
37 | import java.security.CodeSource; | |
38 | import java.security.Permission; | |
39 | import java.security.PermissionCollection; | |
40 | import java.security.Policy; | |
41 | import java.security.PrivilegedAction; | |
42 | import java.security.ProtectionDomain; | |
43 | import java.util.ArrayList; | |
44 | import java.util.Arrays; | |
45 | import java.util.Collection; | |
46 | import java.util.Collections; | |
47 | import java.util.ConcurrentModificationException; | |
48 | import java.util.Date; | |
49 | import java.util.Enumeration; | |
50 | import java.util.HashMap; | |
51 | import java.util.Iterator; | |
52 | import java.util.LinkedHashSet; | |
53 | import java.util.List; | |
54 | import java.util.Map; | |
55 | import java.util.Map.Entry; | |
56 | import java.util.ResourceBundle; | |
57 | import java.util.Set; | |
58 | import java.util.concurrent.ConcurrentHashMap; | |
59 | import java.util.concurrent.CopyOnWriteArrayList; | |
60 | import java.util.concurrent.ThreadPoolExecutor; | |
61 | import java.util.jar.Attributes; | |
62 | import java.util.jar.Attributes.Name; | |
63 | import java.util.jar.Manifest; | |
64 | import java.util.regex.Matcher; | |
65 | import java.util.regex.Pattern; | |
66 | ||
67 | import org.apache.catalina.Globals; | |
68 | import org.apache.catalina.Lifecycle; | |
69 | import org.apache.catalina.LifecycleException; | |
70 | import org.apache.catalina.LifecycleListener; | |
71 | import org.apache.catalina.LifecycleState; | |
72 | import org.apache.catalina.WebResource; | |
73 | import org.apache.catalina.WebResourceRoot; | |
74 | import org.apache.catalina.webresources.TomcatURLStreamHandlerFactory; | |
75 | import org.apache.tomcat.InstrumentableClassLoader; | |
76 | import org.apache.tomcat.util.ExceptionUtils; | |
77 | import org.apache.tomcat.util.IntrospectionUtils; | |
78 | import org.apache.tomcat.util.res.StringManager; | |
79 | ||
80 | /** | |
81 | * Specialized web application class loader. | |
82 | * <p> | |
83 | * This class loader is a full reimplementation of the | |
84 | * <code>URLClassLoader</code> from the JDK. It is designed to be fully | |
85 | * compatible with a normal <code>URLClassLoader</code>, although its internal | |
86 | * behavior may be completely different. | |
87 | * <p> | |
88 | * <strong>IMPLEMENTATION NOTE</strong> - By default, this class loader follows | |
89 | * the delegation model required by the specification. The system class | |
90 | * loader will be queried first, then the local repositories, and only then | |
91 | * delegation to the parent class loader will occur. This allows the web | |
92 | * application to override any shared class except the classes from J2SE. | |
93 | * Special handling is provided from the JAXP XML parser interfaces, the JNDI | |
94 | * interfaces, and the classes from the servlet API, which are never loaded | |
95 | * from the webapp repositories. The <code>delegate</code> property | |
96 | * allows an application to modify this behavior to move the parent class loader | |
97 | * ahead of the local repositories. | |
98 | * <p> | |
99 | * <strong>IMPLEMENTATION NOTE</strong> - Due to limitations in Jasper | |
100 | * compilation technology, any repository which contains classes from | |
101 | * the servlet API will be ignored by the class loader. | |
102 | * <p> | |
103 | * <strong>IMPLEMENTATION NOTE</strong> - The class loader generates source | |
104 | * URLs which include the full JAR URL when a class is loaded from a JAR file, | |
105 | * which allows setting security permission at the class level, even when a | |
106 | * class is contained inside a JAR. | |
107 | * <p> | |
108 | * <strong>IMPLEMENTATION NOTE</strong> - Local repositories are searched in | |
109 | * the order they are added via the initial constructor and/or any subsequent | |
110 | * calls to <code>addRepository()</code> or <code>addJar()</code>. | |
111 | * <p> | |
112 | * <strong>IMPLEMENTATION NOTE</strong> - No check for sealing violations or | |
113 | * security is made unless a security manager is present. | |
114 | * <p> | |
115 | * <strong>IMPLEMENTATION NOTE</strong> - As of 8.0, this class | |
116 | * loader implements {@link InstrumentableClassLoader}, permitting web | |
117 | * application classes to instrument other classes in the same web | |
118 | * application. It does not permit instrumentation of system or container | |
119 | * classes or classes in other web apps. | |
120 | * | |
121 | * @author Remy Maucherat | |
122 | * @author Craig R. McClanahan | |
123 | */ | |
124 | public abstract class WebappClassLoaderBase extends URLClassLoader | |
125 | implements Lifecycle, InstrumentableClassLoader { | |
126 | ||
127 | private static final org.apache.juli.logging.Log log = | |
128 | org.apache.juli.logging.LogFactory.getLog(WebappClassLoaderBase.class); | |
129 | ||
130 | /** | |
131 | * List of ThreadGroup names to ignore when scanning for web application | |
132 | * started threads that need to be shut down. | |
133 | */ | |
134 | private static final List<String> JVM_THREAD_GROUP_NAMES = new ArrayList<>(); | |
135 | ||
136 | private static final String JVM_THREAD_GROUP_SYSTEM = "system"; | |
137 | ||
138 | private static final String CLASS_FILE_SUFFIX = ".class"; | |
139 | private static final String SERVICES_PREFIX = "/META-INF/services/"; | |
140 | ||
141 | static { | |
142 | ClassLoader.registerAsParallelCapable(); | |
143 | JVM_THREAD_GROUP_NAMES.add(JVM_THREAD_GROUP_SYSTEM); | |
144 | JVM_THREAD_GROUP_NAMES.add("RMI Runtime"); | |
145 | } | |
146 | ||
147 | protected class PrivilegedFindResourceByName | |
148 | implements PrivilegedAction<ResourceEntry> { | |
149 | ||
150 | protected final String name; | |
151 | protected final String path; | |
152 | ||
153 | PrivilegedFindResourceByName(String name, String path) { | |
154 | this.name = name; | |
155 | this.path = path; | |
156 | } | |
157 | ||
158 | @Override | |
159 | public ResourceEntry run() { | |
160 | return findResourceInternal(name, path); | |
161 | } | |
162 | ||
163 | } | |
164 | ||
165 | ||
166 | protected static final class PrivilegedGetClassLoader | |
167 | implements PrivilegedAction<ClassLoader> { | |
168 | ||
169 | public final Class<?> clazz; | |
170 | ||
171 | public PrivilegedGetClassLoader(Class<?> clazz){ | |
172 | this.clazz = clazz; | |
173 | } | |
174 | ||
175 | @Override | |
176 | public ClassLoader run() { | |
177 | return clazz.getClassLoader(); | |
178 | } | |
179 | } | |
180 | ||
181 | ||
182 | // ------------------------------------------------------- Static Variables | |
183 | ||
184 | /** | |
185 | * Regular expression of package names which are not allowed to be loaded | |
186 | * from a webapp class loader without delegating first. | |
187 | */ | |
188 | protected final Matcher packageTriggersDeny = Pattern.compile( | |
189 | "^javax\\.el\\.|" + | |
190 | "^javax\\.servlet\\.|" + | |
191 | "^org\\.apache\\.(catalina|coyote|el|jasper|juli|naming|tomcat)\\." | |
192 | ).matcher(""); | |
193 | ||
194 | ||
195 | /** | |
196 | * Regular expression of package names which are allowed to be loaded from a | |
197 | * webapp class loader without delegating first and override any set by | |
198 | * {@link #packageTriggersDeny}. | |
199 | */ | |
200 | protected final Matcher packageTriggersPermit = | |
201 | Pattern.compile("^javax\\.servlet\\.jsp\\.jstl\\.|" + | |
202 | "^org\\.apache\\.tomcat\\.jdbc\\.").matcher(""); | |
203 | ||
204 | ||
205 | /** | |
206 | * The string manager for this package. | |
207 | */ | |
208 | protected static final StringManager sm = | |
209 | StringManager.getManager(Constants.Package); | |
210 | ||
211 | ||
212 | // ----------------------------------------------------------- Constructors | |
213 | ||
214 | /** | |
215 | * Construct a new ClassLoader with no defined repositories and no | |
216 | * parent ClassLoader. | |
217 | */ | |
218 | protected WebappClassLoaderBase() { | |
219 | ||
220 | super(new URL[0]); | |
221 | ||
222 | ClassLoader p = getParent(); | |
223 | if (p == null) { | |
224 | p = getSystemClassLoader(); | |
225 | } | |
226 | this.parent = p; | |
227 | ||
228 | ClassLoader j = String.class.getClassLoader(); | |
229 | if (j == null) { | |
230 | j = getSystemClassLoader(); | |
231 | while (j.getParent() != null) { | |
232 | j = j.getParent(); | |
233 | } | |
234 | } | |
235 | this.javaseClassLoader = j; | |
236 | ||
237 | securityManager = System.getSecurityManager(); | |
238 | if (securityManager != null) { | |
239 | refreshPolicy(); | |
240 | } | |
241 | } | |
242 | ||
243 | ||
244 | /** | |
245 | * Construct a new ClassLoader with no defined repositories and the given | |
246 | * parent ClassLoader. | |
247 | * <p> | |
248 | * Method is used via reflection - | |
249 | * see {@link WebappLoader#createClassLoader()} | |
250 | * | |
251 | * @param parent Our parent class loader | |
252 | */ | |
253 | protected WebappClassLoaderBase(ClassLoader parent) { | |
254 | ||
255 | super(new URL[0], parent); | |
256 | ||
257 | ClassLoader p = getParent(); | |
258 | if (p == null) { | |
259 | p = getSystemClassLoader(); | |
260 | } | |
261 | this.parent = p; | |
262 | ||
263 | ClassLoader j = String.class.getClassLoader(); | |
264 | if (j == null) { | |
265 | j = getSystemClassLoader(); | |
266 | while (j.getParent() != null) { | |
267 | j = j.getParent(); | |
268 | } | |
269 | } | |
270 | this.javaseClassLoader = j; | |
271 | ||
272 | securityManager = System.getSecurityManager(); | |
273 | if (securityManager != null) { | |
274 | refreshPolicy(); | |
275 | } | |
276 | } | |
277 | ||
278 | ||
279 | // ----------------------------------------------------- Instance Variables | |
280 | ||
281 | /** | |
282 | * Associated web resources for this webapp. | |
283 | */ | |
284 | protected WebResourceRoot resources = null; | |
285 | ||
286 | ||
287 | /** | |
288 | * The cache of ResourceEntry for classes and resources we have loaded, | |
289 | * keyed by resource path, not binary name. Path is used as the key since | |
290 | * resources may be requested by binary name (classes) or path (other | |
291 | * resources such as property files) and the mapping from binary name to | |
292 | * path is unambiguous but the reverse mapping is ambiguous. | |
293 | */ | |
294 | protected final Map<String, ResourceEntry> resourceEntries = | |
295 | new ConcurrentHashMap<>(); | |
296 | ||
297 | ||
298 | /** | |
299 | * Should this class loader delegate to the parent class loader | |
300 | * <strong>before</strong> searching its own repositories (i.e. the | |
301 | * usual Java2 delegation model)? If set to <code>false</code>, | |
302 | * this class loader will search its own repositories first, and | |
303 | * delegate to the parent only if the class or resource is not | |
304 | * found locally. Note that the default, <code>false</code>, is | |
305 | * the behavior called for by the servlet specification. | |
306 | */ | |
307 | protected boolean delegate = false; | |
308 | ||
309 | ||
310 | private final HashMap<String,Long> jarModificationTimes = new HashMap<>(); | |
311 | ||
312 | ||
313 | /** | |
314 | * A list of read File and Jndi Permission's required if this loader | |
315 | * is for a web application context. | |
316 | */ | |
317 | protected final ArrayList<Permission> permissionList = new ArrayList<>(); | |
318 | ||
319 | ||
320 | /** | |
321 | * The PermissionCollection for each CodeSource for a web | |
322 | * application context. | |
323 | */ | |
324 | protected final HashMap<String, PermissionCollection> loaderPC = new HashMap<>(); | |
325 | ||
326 | ||
327 | /** | |
328 | * Instance of the SecurityManager installed. | |
329 | */ | |
330 | protected final SecurityManager securityManager; | |
331 | ||
332 | ||
333 | /** | |
334 | * The parent class loader. | |
335 | */ | |
336 | protected final ClassLoader parent; | |
337 | ||
338 | ||
339 | /** | |
340 | * The bootstrap class loader used to load the JavaSE classes. In some | |
341 | * implementations this class loader is always <code>null</null> and in | |
342 | * those cases {@link ClassLoader#getParent()} will be called recursively on | |
343 | * the system class loader and the last non-null result used. | |
344 | */ | |
345 | private ClassLoader javaseClassLoader; | |
346 | ||
347 | ||
348 | /** | |
349 | * need conversion for properties files | |
350 | */ | |
351 | protected boolean needConvert = false; | |
352 | ||
353 | ||
354 | /** | |
355 | * All permission. | |
356 | */ | |
357 | protected final Permission allPermission = new java.security.AllPermission(); | |
358 | ||
359 | ||
360 | /** | |
361 | * Should Tomcat attempt to null out any static or final fields from loaded | |
362 | * classes when a web application is stopped as a work around for apparent | |
363 | * garbage collection bugs and application coding errors? There have been | |
364 | * some issues reported with log4j when this option is true. Applications | |
365 | * without memory leaks using recent JVMs should operate correctly with this | |
366 | * option set to <code>false</code>. If not specified, the default value of | |
367 | * <code>false</code> will be used. | |
368 | */ | |
369 | private boolean clearReferencesStatic = false; | |
370 | ||
371 | /** | |
372 | * Should Tomcat attempt to terminate threads that have been started by the | |
373 | * web application? Stopping threads is performed via the deprecated (for | |
374 | * good reason) <code>Thread.stop()</code> method and is likely to result in | |
375 | * instability. As such, enabling this should be viewed as an option of last | |
376 | * resort in a development environment and is not recommended in a | |
377 | * production environment. If not specified, the default value of | |
378 | * <code>false</code> will be used. | |
379 | */ | |
380 | private boolean clearReferencesStopThreads = false; | |
381 | ||
382 | /** | |
383 | * Should Tomcat attempt to terminate any {@link java.util.TimerThread}s | |
384 | * that have been started by the web application? If not specified, the | |
385 | * default value of <code>false</code> will be used. | |
386 | */ | |
387 | private boolean clearReferencesStopTimerThreads = false; | |
388 | ||
389 | /** | |
390 | * Should Tomcat call {@link org.apache.juli.logging.LogFactory#release()} | |
391 | * when the class loader is stopped? If not specified, the default value | |
392 | * of <code>true</code> is used. Changing the default setting is likely to | |
393 | * lead to memory leaks and other issues. | |
394 | */ | |
395 | private boolean clearReferencesLogFactoryRelease = true; | |
396 | ||
397 | /** | |
398 | * If an HttpClient keep-alive timer thread has been started by this web | |
399 | * application and is still running, should Tomcat change the context class | |
400 | * loader from the current {@link ClassLoader} to | |
401 | * {@link ClassLoader#getParent()} to prevent a memory leak? Note that the | |
402 | * keep-alive timer thread will stop on its own once the keep-alives all | |
403 | * expire however, on a busy system that might not happen for some time. | |
404 | */ | |
405 | private boolean clearReferencesHttpClientKeepAliveThread = true; | |
406 | ||
407 | /** | |
408 | * Holds the class file transformers decorating this class loader. The | |
409 | * CopyOnWriteArrayList is thread safe. It is expensive on writes, but | |
410 | * those should be rare. It is very fast on reads, since synchronization | |
411 | * is not actually used. Importantly, the ClassLoader will never block | |
412 | * iterating over the transformers while loading a class. | |
413 | */ | |
414 | private final List<ClassFileTransformer> transformers = new CopyOnWriteArrayList<>(); | |
415 | ||
416 | ||
417 | /** | |
418 | * Flag that indicates that {@link #addURL(URL)} has been called which | |
419 | * creates a requirement to check the super class when searching for | |
420 | * resources. | |
421 | */ | |
422 | private boolean hasExternalRepositories = false; | |
423 | ||
424 | ||
425 | /** | |
426 | * Repositories managed by this class rather than the super class. | |
427 | */ | |
428 | private List<URL> localRepositories = new ArrayList<>(); | |
429 | ||
430 | ||
431 | private volatile LifecycleState state = LifecycleState.NEW; | |
432 | ||
433 | ||
434 | // ------------------------------------------------------------- Properties | |
435 | ||
436 | /** | |
437 | * Get associated resources. | |
438 | */ | |
439 | public WebResourceRoot getResources() { | |
440 | return this.resources; | |
441 | } | |
442 | ||
443 | ||
444 | /** | |
445 | * Set associated resources. | |
446 | */ | |
447 | public void setResources(WebResourceRoot resources) { | |
448 | this.resources = resources; | |
449 | } | |
450 | ||
451 | ||
452 | /** | |
453 | * Return the context name for this class loader. | |
454 | */ | |
455 | public String getContextName() { | |
456 | if (resources == null) { | |
457 | return "Unknown"; | |
458 | } else { | |
459 | return resources.getContext().getName(); | |
460 | } | |
461 | } | |
462 | ||
463 | ||
464 | /** | |
465 | * Return the "delegate first" flag for this class loader. | |
466 | */ | |
467 | public boolean getDelegate() { | |
468 | ||
469 | return (this.delegate); | |
470 | ||
471 | } | |
472 | ||
473 | ||
474 | /** | |
475 | * Set the "delegate first" flag for this class loader. | |
476 | * If this flag is true, this class loader delegates | |
477 | * to the parent class loader | |
478 | * <strong>before</strong> searching its own repositories, as | |
479 | * in an ordinary (non-servlet) chain of Java class loaders. | |
480 | * If set to <code>false</code> (the default), | |
481 | * this class loader will search its own repositories first, and | |
482 | * delegate to the parent only if the class or resource is not | |
483 | * found locally, as per the servlet specification. | |
484 | * | |
485 | * @param delegate The new "delegate first" flag | |
486 | */ | |
487 | public void setDelegate(boolean delegate) { | |
488 | this.delegate = delegate; | |
489 | } | |
490 | ||
491 | ||
492 | /** | |
493 | * If there is a Java SecurityManager create a read permission for the | |
494 | * target of the given URL as appropriate. | |
495 | * | |
496 | * @param url URL for a file or directory on local system | |
497 | */ | |
498 | void addPermission(URL url) { | |
499 | if (url == null) { | |
500 | return; | |
501 | } | |
502 | if (securityManager != null) { | |
503 | String protocol = url.getProtocol(); | |
504 | if ("file".equalsIgnoreCase(protocol)) { | |
505 | URI uri; | |
506 | File f; | |
507 | String path; | |
508 | try { | |
509 | uri = url.toURI(); | |
510 | f = new File(uri); | |
511 | path = f.getCanonicalPath(); | |
512 | } catch (IOException | URISyntaxException e) { | |
513 | log.warn(sm.getString( | |
514 | "webappClassLoader.addPermisionNoCanonicalFile", | |
515 | url.toExternalForm())); | |
516 | return; | |
517 | } | |
518 | if (f.isFile()) { | |
519 | // Allow the file to be read | |
520 | addPermission(new FilePermission(path, "read")); | |
521 | } else if (f.isDirectory()) { | |
522 | addPermission(new FilePermission(path, "read")); | |
523 | addPermission(new FilePermission( | |
524 | path + File.separator + "-", "read")); | |
525 | } else { | |
526 | // File does not exist - ignore (shouldn't happen) | |
527 | } | |
528 | } else { | |
529 | // Unsupported URL protocol | |
530 | log.warn(sm.getString( | |
531 | "webappClassLoader.addPermisionNoProtocol", | |
532 | protocol, url.toExternalForm())); | |
533 | } | |
534 | } | |
535 | } | |
536 | ||
537 | ||
538 | /** | |
539 | * If there is a Java SecurityManager create a Permission. | |
540 | * | |
541 | * @param permission The permission | |
542 | */ | |
543 | void addPermission(Permission permission) { | |
544 | if ((securityManager != null) && (permission != null)) { | |
545 | permissionList.add(permission); | |
546 | } | |
547 | } | |
548 | ||
549 | ||
550 | /** | |
551 | * Return the clearReferencesStatic flag for this Context. | |
552 | */ | |
553 | public boolean getClearReferencesStatic() { | |
554 | return (this.clearReferencesStatic); | |
555 | } | |
556 | ||
557 | ||
558 | /** | |
559 | * Set the clearReferencesStatic feature for this Context. | |
560 | * | |
561 | * @param clearReferencesStatic The new flag value | |
562 | */ | |
563 | public void setClearReferencesStatic(boolean clearReferencesStatic) { | |
564 | this.clearReferencesStatic = clearReferencesStatic; | |
565 | } | |
566 | ||
567 | ||
568 | /** | |
569 | * Return the clearReferencesStopThreads flag for this Context. | |
570 | */ | |
571 | public boolean getClearReferencesStopThreads() { | |
572 | return (this.clearReferencesStopThreads); | |
573 | } | |
574 | ||
575 | ||
576 | /** | |
577 | * Set the clearReferencesStopThreads feature for this Context. | |
578 | * | |
579 | * @param clearReferencesStopThreads The new flag value | |
580 | */ | |
581 | public void setClearReferencesStopThreads( | |
582 | boolean clearReferencesStopThreads) { | |
583 | this.clearReferencesStopThreads = clearReferencesStopThreads; | |
584 | } | |
585 | ||
586 | ||
587 | /** | |
588 | * Return the clearReferencesStopTimerThreads flag for this Context. | |
589 | */ | |
590 | public boolean getClearReferencesStopTimerThreads() { | |
591 | return (this.clearReferencesStopTimerThreads); | |
592 | } | |
593 | ||
594 | ||
595 | /** | |
596 | * Set the clearReferencesStopTimerThreads feature for this Context. | |
597 | * | |
598 | * @param clearReferencesStopTimerThreads The new flag value | |
599 | */ | |
600 | public void setClearReferencesStopTimerThreads( | |
601 | boolean clearReferencesStopTimerThreads) { | |
602 | this.clearReferencesStopTimerThreads = clearReferencesStopTimerThreads; | |
603 | } | |
604 | ||
605 | ||
606 | /** | |
607 | * Return the clearReferencesLogFactoryRelease flag for this Context. | |
608 | */ | |
609 | public boolean getClearReferencesLogFactoryRelease() { | |
610 | return (this.clearReferencesLogFactoryRelease); | |
611 | } | |
612 | ||
613 | ||
614 | /** | |
615 | * Set the clearReferencesLogFactoryRelease feature for this Context. | |
616 | * | |
617 | * @param clearReferencesLogFactoryRelease The new flag value | |
618 | */ | |
619 | public void setClearReferencesLogFactoryRelease( | |
620 | boolean clearReferencesLogFactoryRelease) { | |
621 | this.clearReferencesLogFactoryRelease = | |
622 | clearReferencesLogFactoryRelease; | |
623 | } | |
624 | ||
625 | ||
626 | /** | |
627 | * Return the clearReferencesHttpClientKeepAliveThread flag for this | |
628 | * Context. | |
629 | */ | |
630 | public boolean getClearReferencesHttpClientKeepAliveThread() { | |
631 | return (this.clearReferencesHttpClientKeepAliveThread); | |
632 | } | |
633 | ||
634 | ||
635 | /** | |
636 | * Set the clearReferencesHttpClientKeepAliveThread feature for this | |
637 | * Context. | |
638 | * | |
639 | * @param clearReferencesHttpClientKeepAliveThread The new flag value | |
640 | */ | |
641 | public void setClearReferencesHttpClientKeepAliveThread( | |
642 | boolean clearReferencesHttpClientKeepAliveThread) { | |
643 | this.clearReferencesHttpClientKeepAliveThread = | |
644 | clearReferencesHttpClientKeepAliveThread; | |
645 | } | |
646 | ||
647 | ||
648 | // ------------------------------------------------------- Reloader Methods | |
649 | ||
650 | /** | |
651 | * Adds the specified class file transformer to this class loader. The | |
652 | * transformer will then be able to modify the bytecode of any classes | |
653 | * loaded by this class loader after the invocation of this method. | |
654 | * | |
655 | * @param transformer The transformer to add to the class loader | |
656 | */ | |
657 | @Override | |
658 | public void addTransformer(ClassFileTransformer transformer) { | |
659 | ||
660 | if (transformer == null) { | |
661 | throw new IllegalArgumentException(sm.getString( | |
662 | "webappClassLoader.addTransformer.illegalArgument", getContextName())); | |
663 | } | |
664 | ||
665 | if (this.transformers.contains(transformer)) { | |
666 | // if the same instance of this transformer was already added, bail out | |
667 | log.warn(sm.getString("webappClassLoader.addTransformer.duplicate", | |
668 | transformer, getContextName())); | |
669 | return; | |
670 | } | |
671 | this.transformers.add(transformer); | |
672 | ||
673 | log.info(sm.getString("webappClassLoader.addTransformer", transformer, getContextName())); | |
674 | } | |
675 | ||
676 | /** | |
677 | * Removes the specified class file transformer from this class loader. | |
678 | * It will no longer be able to modify the byte code of any classes | |
679 | * loaded by the class loader after the invocation of this method. | |
680 | * However, any classes already modified by this transformer will | |
681 | * remain transformed. | |
682 | * | |
683 | * @param transformer The transformer to remove | |
684 | */ | |
685 | @Override | |
686 | public void removeTransformer(ClassFileTransformer transformer) { | |
687 | ||
688 | if (transformer == null) { | |
689 | return; | |
690 | } | |
691 | ||
692 | if (this.transformers.remove(transformer)) { | |
693 | log.info(sm.getString("webappClassLoader.removeTransformer", | |
694 | transformer, getContextName())); | |
695 | return; | |
696 | } | |
697 | ||
698 | } | |
699 | ||
700 | protected void copyStateWithoutTransformers(WebappClassLoaderBase base) { | |
701 | base.resources = this.resources; | |
702 | base.delegate = this.delegate; | |
703 | base.state = LifecycleState.NEW; | |
704 | base.needConvert = this.needConvert; | |
705 | base.clearReferencesStatic = this.clearReferencesStatic; | |
706 | base.clearReferencesStopThreads = this.clearReferencesStopThreads; | |
707 | base.clearReferencesStopTimerThreads = this.clearReferencesStopTimerThreads; | |
708 | base.clearReferencesLogFactoryRelease = this.clearReferencesLogFactoryRelease; | |
709 | base.clearReferencesHttpClientKeepAliveThread = this.clearReferencesHttpClientKeepAliveThread; | |
710 | base.jarModificationTimes.putAll(this.jarModificationTimes); | |
711 | base.permissionList.addAll(this.permissionList); | |
712 | base.loaderPC.putAll(this.loaderPC); | |
713 | } | |
714 | ||
715 | /** | |
716 | * Have one or more classes or resources been modified so that a reload | |
717 | * is appropriate? | |
718 | */ | |
719 | public boolean modified() { | |
720 | ||
721 | if (log.isDebugEnabled()) | |
722 | log.debug("modified()"); | |
723 | ||
724 | for (Entry<String,ResourceEntry> entry : resourceEntries.entrySet()) { | |
725 | long cachedLastModified = entry.getValue().lastModified; | |
726 | long lastModified = resources.getClassLoaderResource( | |
727 | entry.getKey()).getLastModified(); | |
728 | if (lastModified != cachedLastModified) { | |
729 | if( log.isDebugEnabled() ) | |
730 | log.debug(sm.getString("webappClassLoader.resourceModified", | |
731 | entry.getKey(), | |
732 | new Date(cachedLastModified), | |
733 | new Date(lastModified))); | |
734 | return true; | |
735 | } | |
736 | } | |
737 | ||
738 | // Check if JARs have been added or removed | |
739 | WebResource[] jars = resources.listResources("/WEB-INF/lib"); | |
740 | // Filter out non-JAR resources | |
741 | ||
742 | int jarCount = 0; | |
743 | for (WebResource jar : jars) { | |
744 | if (jar.getName().endsWith(".jar") && jar.isFile() && jar.canRead()) { | |
745 | jarCount++; | |
746 | Long recordedLastModified = jarModificationTimes.get(jar.getName()); | |
747 | if (recordedLastModified == null) { | |
748 | // Jar has been added | |
749 | log.info(sm.getString("webappClassLoader.jarsAdded", | |
750 | resources.getContext().getName())); | |
751 | return true; | |
752 | } | |
753 | if (recordedLastModified.longValue() != jar.getLastModified()) { | |
754 | // Jar has been changed | |
755 | log.info(sm.getString("webappClassLoader.jarsModified", | |
756 | resources.getContext().getName())); | |
757 | return true; | |
758 | } | |
759 | } | |
760 | } | |
761 | ||
762 | if (jarCount < jarModificationTimes.size()){ | |
763 | log.info(sm.getString("webappClassLoader.jarsRemoved", | |
764 | resources.getContext().getName())); | |
765 | return true; | |
766 | } | |
767 | ||
768 | ||
769 | // No classes have been modified | |
770 | return false; | |
771 | } | |
772 | ||
773 | ||
774 | /** | |
775 | * Render a String representation of this object. | |
776 | */ | |
777 | @Override | |
778 | public String toString() { | |
779 | ||
780 | StringBuilder sb = new StringBuilder(this.getClass().getSimpleName()); | |
781 | sb.append("\r\n context: "); | |
782 | sb.append(getContextName()); | |
783 | sb.append("\r\n delegate: "); | |
784 | sb.append(delegate); | |
785 | sb.append("\r\n"); | |
786 | if (this.parent != null) { | |
787 | sb.append("----------> Parent Classloader:\r\n"); | |
788 | sb.append(this.parent.toString()); | |
789 | sb.append("\r\n"); | |
790 | } | |
791 | if (this.transformers.size() > 0) { | |
792 | sb.append("----------> Class file transformers:\r\n"); | |
793 | for (ClassFileTransformer transformer : this.transformers) { | |
794 | sb.append(transformer).append("\r\n"); | |
795 | } | |
796 | } | |
797 | return (sb.toString()); | |
798 | } | |
799 | ||
800 | ||
801 | // ---------------------------------------------------- ClassLoader Methods | |
802 | ||
803 | ||
804 | /** | |
805 | * Expose this method for use by the unit tests. | |
806 | */ | |
807 | protected final Class<?> doDefineClass(String name, byte[] b, int off, int len, | |
808 | ProtectionDomain protectionDomain) { | |
809 | return super.defineClass(name, b, off, len, protectionDomain); | |
810 | } | |
811 | ||
812 | /** | |
813 | * Find the specified class in our local repositories, if possible. If | |
814 | * not found, throw <code>ClassNotFoundException</code>. | |
815 | * | |
816 | * @param name The binary name of the class to be loaded | |
817 | * | |
818 | * @exception ClassNotFoundException if the class was not found | |
819 | */ | |
820 | @Override | |
821 | public Class<?> findClass(String name) throws ClassNotFoundException { | |
822 | ||
823 | if (log.isDebugEnabled()) | |
824 | log.debug(" findClass(" + name + ")"); | |
825 | ||
826 | checkStateForClassLoading(name); | |
827 | ||
828 | // (1) Permission to define this class when using a SecurityManager | |
829 | if (securityManager != null) { | |
830 | int i = name.lastIndexOf('.'); | |
831 | if (i >= 0) { | |
832 | try { | |
833 | if (log.isTraceEnabled()) | |
834 | log.trace(" securityManager.checkPackageDefinition"); | |
835 | securityManager.checkPackageDefinition(name.substring(0,i)); | |
836 | } catch (Exception se) { | |
837 | if (log.isTraceEnabled()) | |
838 | log.trace(" -->Exception-->ClassNotFoundException", se); | |
839 | throw new ClassNotFoundException(name, se); | |
840 | } | |
841 | } | |
842 | } | |
843 | ||
844 | // Ask our superclass to locate this class, if possible | |
845 | // (throws ClassNotFoundException if it is not found) | |
846 | Class<?> clazz = null; | |
847 | try { | |
848 | if (log.isTraceEnabled()) | |
849 | log.trace(" findClassInternal(" + name + ")"); | |
850 | try { | |
851 | clazz = findClassInternal(name); | |
852 | } catch(AccessControlException ace) { | |
853 | log.warn("WebappClassLoader.findClassInternal(" + name | |
854 | + ") security exception: " + ace.getMessage(), ace); | |
855 | throw new ClassNotFoundException(name, ace); | |
856 | } catch (RuntimeException e) { | |
857 | if (log.isTraceEnabled()) | |
858 | log.trace(" -->RuntimeException Rethrown", e); | |
859 | throw e; | |
860 | } | |
861 | if ((clazz == null) && hasExternalRepositories) { | |
862 | try { | |
863 | clazz = super.findClass(name); | |
864 | } catch(AccessControlException ace) { | |
865 | log.warn("WebappClassLoader.findClassInternal(" + name | |
866 | + ") security exception: " + ace.getMessage(), ace); | |
867 | throw new ClassNotFoundException(name, ace); | |
868 | } catch (RuntimeException e) { | |
869 | if (log.isTraceEnabled()) | |
870 | log.trace(" -->RuntimeException Rethrown", e); | |
871 | throw e; | |
872 | } | |
873 | } | |
874 | if (clazz == null) { | |
875 | if (log.isDebugEnabled()) | |
876 | log.debug(" --> Returning ClassNotFoundException"); | |
877 | throw new ClassNotFoundException(name); | |
878 | } | |
879 | } catch (ClassNotFoundException e) { | |
880 | if (log.isTraceEnabled()) | |
881 | log.trace(" --> Passing on ClassNotFoundException"); | |
882 | throw e; | |
883 | } | |
884 | ||
885 | // Return the class we have located | |
886 | if (log.isTraceEnabled()) | |
887 | log.debug(" Returning class " + clazz); | |
888 | ||
889 | if (log.isTraceEnabled()) { | |
890 | ClassLoader cl; | |
891 | if (Globals.IS_SECURITY_ENABLED){ | |
892 | cl = AccessController.doPrivileged( | |
893 | new PrivilegedGetClassLoader(clazz)); | |
894 | } else { | |
895 | cl = clazz.getClassLoader(); | |
896 | } | |
897 | log.debug(" Loaded by " + cl.toString()); | |
898 | } | |
899 | return (clazz); | |
900 | ||
901 | } | |
902 | ||
903 | ||
904 | /** | |
905 | * Find the specified resource in our local repository, and return a | |
906 | * <code>URL</code> referring to it, or <code>null</code> if this resource | |
907 | * cannot be found. | |
908 | * | |
909 | * @param name Name of the resource to be found | |
910 | */ | |
911 | @Override | |
912 | public URL findResource(final String name) { | |
913 | ||
914 | if (log.isDebugEnabled()) | |
915 | log.debug(" findResource(" + name + ")"); | |
916 | ||
917 | URL url = null; | |
918 | ||
919 | String path = nameToPath(name); | |
920 | ||
921 | ResourceEntry entry = resourceEntries.get(path); | |
922 | if (entry == null) { | |
923 | if (securityManager != null) { | |
924 | PrivilegedAction<ResourceEntry> dp = | |
925 | new PrivilegedFindResourceByName(name, path); | |
926 | entry = AccessController.doPrivileged(dp); | |
927 | } else { | |
928 | entry = findResourceInternal(name, path); | |
929 | } | |
930 | } | |
931 | if (entry != null) { | |
932 | url = entry.source; | |
933 | } | |
934 | ||
935 | if ((url == null) && hasExternalRepositories) { | |
936 | url = super.findResource(name); | |
937 | } | |
938 | ||
939 | if (log.isDebugEnabled()) { | |
940 | if (url != null) | |
941 | log.debug(" --> Returning '" + url.toString() + "'"); | |
942 | else | |
943 | log.debug(" --> Resource not found, returning null"); | |
944 | } | |
945 | return (url); | |
946 | ||
947 | } | |
948 | ||
949 | ||
950 | /** | |
951 | * Return an enumeration of <code>URLs</code> representing all of the | |
952 | * resources with the given name. If no resources with this name are | |
953 | * found, return an empty enumeration. | |
954 | * | |
955 | * @param name Name of the resources to be found | |
956 | * | |
957 | * @exception IOException if an input/output error occurs | |
958 | */ | |
959 | @Override | |
960 | public Enumeration<URL> findResources(String name) throws IOException { | |
961 | ||
962 | if (log.isDebugEnabled()) | |
963 | log.debug(" findResources(" + name + ")"); | |
964 | ||
965 | LinkedHashSet<URL> result = new LinkedHashSet<>(); | |
966 | ||
967 | String path = nameToPath(name); | |
968 | ||
969 | WebResource[] webResources = resources.getClassLoaderResources(path); | |
970 | for (WebResource webResource : webResources) { | |
971 | if (webResource.exists()) { | |
972 | result.add(webResource.getURL()); | |
973 | } | |
974 | } | |
975 | ||
976 | // Adding the results of a call to the superclass | |
977 | if (hasExternalRepositories) { | |
978 | Enumeration<URL> otherResourcePaths = super.findResources(name); | |
979 | while (otherResourcePaths.hasMoreElements()) { | |
980 | result.add(otherResourcePaths.nextElement()); | |
981 | } | |
982 | } | |
983 | ||
984 | return Collections.enumeration(result); | |
985 | } | |
986 | ||
987 | ||
988 | /** | |
989 | * Find the resource with the given name. A resource is some data | |
990 | * (images, audio, text, etc.) that can be accessed by class code in a | |
991 | * way that is independent of the location of the code. The name of a | |
992 | * resource is a "/"-separated path name that identifies the resource. | |
993 | * If the resource cannot be found, return <code>null</code>. | |
994 | * <p> | |
995 | * This method searches according to the following algorithm, returning | |
996 | * as soon as it finds the appropriate URL. If the resource cannot be | |
997 | * found, returns <code>null</code>. | |
998 | * <ul> | |
999 | * <li>If the <code>delegate</code> property is set to <code>true</code>, | |
1000 | * call the <code>getResource()</code> method of the parent class | |
1001 | * loader, if any.</li> | |
1002 | * <li>Call <code>findResource()</code> to find this resource in our | |
1003 | * locally defined repositories.</li> | |
1004 | * <li>Call the <code>getResource()</code> method of the parent class | |
1005 | * loader, if any.</li> | |
1006 | * </ul> | |
1007 | * | |
1008 | * @param name Name of the resource to return a URL for | |
1009 | */ | |
1010 | @Override | |
1011 | public URL getResource(String name) { | |
1012 | ||
1013 | if (log.isDebugEnabled()) | |
1014 | log.debug("getResource(" + name + ")"); | |
1015 | URL url = null; | |
1016 | ||
1017 | // (1) Delegate to parent if requested | |
1018 | if (delegate) { | |
1019 | if (log.isDebugEnabled()) | |
1020 | log.debug(" Delegating to parent classloader " + parent); | |
1021 | url = parent.getResource(name); | |
1022 | if (url != null) { | |
1023 | if (log.isDebugEnabled()) | |
1024 | log.debug(" --> Returning '" + url.toString() + "'"); | |
1025 | return (url); | |
1026 | } | |
1027 | } | |
1028 | ||
1029 | // (2) Search local repositories | |
1030 | url = findResource(name); | |
1031 | if (url != null) { | |
1032 | if (log.isDebugEnabled()) | |
1033 | log.debug(" --> Returning '" + url.toString() + "'"); | |
1034 | return (url); | |
1035 | } | |
1036 | ||
1037 | // (3) Delegate to parent unconditionally if not already attempted | |
1038 | if( !delegate ) { | |
1039 | url = parent.getResource(name); | |
1040 | if (url != null) { | |
1041 | if (log.isDebugEnabled()) | |
1042 | log.debug(" --> Returning '" + url.toString() + "'"); | |
1043 | return (url); | |
1044 | } | |
1045 | } | |
1046 | ||
1047 | // (4) Resource was not found | |
1048 | if (log.isDebugEnabled()) | |
1049 | log.debug(" --> Resource not found, returning null"); | |
1050 | return (null); | |
1051 | ||
1052 | } | |
1053 | ||
1054 | ||
1055 | /** | |
1056 | * Find the resource with the given name, and return an input stream | |
1057 | * that can be used for reading it. The search order is as described | |
1058 | * for <code>getResource()</code>, after checking to see if the resource | |
1059 | * data has been previously cached. If the resource cannot be found, | |
1060 | * return <code>null</code>. | |
1061 | * | |
1062 | * @param name Name of the resource to return an input stream for | |
1063 | */ | |
1064 | @Override | |
1065 | public InputStream getResourceAsStream(String name) { | |
1066 | ||
1067 | if (log.isDebugEnabled()) | |
1068 | log.debug("getResourceAsStream(" + name + ")"); | |
1069 | InputStream stream = null; | |
1070 | ||
1071 | // (0) Check for a cached copy of this resource | |
1072 | stream = findLoadedResource(name); | |
1073 | if (stream != null) { | |
1074 | if (log.isDebugEnabled()) | |
1075 | log.debug(" --> Returning stream from cache"); | |
1076 | return (stream); | |
1077 | } | |
1078 | ||
1079 | // (1) Delegate to parent if requested | |
1080 | if (delegate) { | |
1081 | if (log.isDebugEnabled()) | |
1082 | log.debug(" Delegating to parent classloader " + parent); | |
1083 | stream = parent.getResourceAsStream(name); | |
1084 | if (stream != null) { | |
1085 | // FIXME - cache??? | |
1086 | if (log.isDebugEnabled()) | |
1087 | log.debug(" --> Returning stream from parent"); | |
1088 | return (stream); | |
1089 | } | |
1090 | } | |
1091 | ||
1092 | // (2) Search local repositories | |
1093 | if (log.isDebugEnabled()) | |
1094 | log.debug(" Searching local repositories"); | |
1095 | URL url = findResource(name); | |
1096 | if (url != null) { | |
1097 | // FIXME - cache??? | |
1098 | if (log.isDebugEnabled()) | |
1099 | log.debug(" --> Returning stream from local"); | |
1100 | stream = findLoadedResource(name); | |
1101 | try { | |
1102 | if (hasExternalRepositories && (stream == null)) | |
1103 | stream = url.openStream(); | |
1104 | } catch (IOException e) { | |
1105 | // Ignore | |
1106 | } | |
1107 | if (stream != null) | |
1108 | return (stream); | |
1109 | } | |
1110 | ||
1111 | // (3) Delegate to parent unconditionally | |
1112 | if (!delegate) { | |
1113 | if (log.isDebugEnabled()) | |
1114 | log.debug(" Delegating to parent classloader unconditionally " + parent); | |
1115 | stream = parent.getResourceAsStream(name); | |
1116 | if (stream != null) { | |
1117 | // FIXME - cache??? | |
1118 | if (log.isDebugEnabled()) | |
1119 | log.debug(" --> Returning stream from parent"); | |
1120 | return (stream); | |
1121 | } | |
1122 | } | |
1123 | ||
1124 | // (4) Resource was not found | |
1125 | if (log.isDebugEnabled()) | |
1126 | log.debug(" --> Resource not found, returning null"); | |
1127 | return (null); | |
1128 | ||
1129 | } | |
1130 | ||
1131 | ||
1132 | /** | |
1133 | * Load the class with the specified name. This method searches for | |
1134 | * classes in the same manner as <code>loadClass(String, boolean)</code> | |
1135 | * with <code>false</code> as the second argument. | |
1136 | * | |
1137 | * @param name The binary name of the class to be loaded | |
1138 | * | |
1139 | * @exception ClassNotFoundException if the class was not found | |
1140 | */ | |
1141 | @Override | |
1142 | public Class<?> loadClass(String name) throws ClassNotFoundException { | |
1143 | ||
1144 | return (loadClass(name, false)); | |
1145 | ||
1146 | } | |
1147 | ||
1148 | ||
1149 | /** | |
1150 | * Load the class with the specified name, searching using the following | |
1151 | * algorithm until it finds and returns the class. If the class cannot | |
1152 | * be found, returns <code>ClassNotFoundException</code>. | |
1153 | * <ul> | |
1154 | * <li>Call <code>findLoadedClass(String)</code> to check if the | |
1155 | * class has already been loaded. If it has, the same | |
1156 | * <code>Class</code> object is returned.</li> | |
1157 | * <li>If the <code>delegate</code> property is set to <code>true</code>, | |
1158 | * call the <code>loadClass()</code> method of the parent class | |
1159 | * loader, if any.</li> | |
1160 | * <li>Call <code>findClass()</code> to find this class in our locally | |
1161 | * defined repositories.</li> | |
1162 | * <li>Call the <code>loadClass()</code> method of our parent | |
1163 | * class loader, if any.</li> | |
1164 | * </ul> | |
1165 | * If the class was found using the above steps, and the | |
1166 | * <code>resolve</code> flag is <code>true</code>, this method will then | |
1167 | * call <code>resolveClass(Class)</code> on the resulting Class object. | |
1168 | * | |
1169 | * @param name The binary name of the class to be loaded | |
1170 | * @param resolve If <code>true</code> then resolve the class | |
1171 | * | |
1172 | * @exception ClassNotFoundException if the class was not found | |
1173 | */ | |
1174 | @Override | |
1175 | public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { | |
1176 | ||
1177 | synchronized (getClassLoadingLock(name)) { | |
1178 | if (log.isDebugEnabled()) | |
1179 | log.debug("loadClass(" + name + ", " + resolve + ")"); | |
1180 | Class<?> clazz = null; | |
1181 | ||
1182 | // Log access to stopped class loader | |
1183 | checkStateForClassLoading(name); | |
1184 | ||
1185 | // (0) Check our previously loaded local class cache | |
1186 | clazz = findLoadedClass0(name); | |
1187 | if (clazz != null) { | |
1188 | if (log.isDebugEnabled()) | |
1189 | log.debug(" Returning class from cache"); | |
1190 | if (resolve) | |
1191 | resolveClass(clazz); | |
1192 | return (clazz); | |
1193 | } | |
1194 | ||
1195 | // (0.1) Check our previously loaded class cache | |
1196 | clazz = findLoadedClass(name); | |
1197 | if (clazz != null) { | |
1198 | if (log.isDebugEnabled()) | |
1199 | log.debug(" Returning class from cache"); | |
1200 | if (resolve) | |
1201 | resolveClass(clazz); | |
1202 | return (clazz); | |
1203 | } | |
1204 | ||
1205 | // (0.2) Try loading the class with the system class loader, to prevent | |
1206 | // the webapp from overriding J2SE classes | |
1207 | String resourceName = binaryNameToPath(name, false); | |
1208 | ClassLoader javaseLoader = getJavaseClassLoader(); | |
1209 | if (javaseLoader.getResource(resourceName) != null) { | |
1210 | try { | |
1211 | clazz = javaseLoader.loadClass(name); | |
1212 | if (clazz != null) { | |
1213 | if (resolve) | |
1214 | resolveClass(clazz); | |
1215 | return (clazz); | |
1216 | } | |
1217 | } catch (ClassNotFoundException e) { | |
1218 | // Ignore | |
1219 | } | |
1220 | } | |
1221 | ||
1222 | // (0.5) Permission to access this class when using a SecurityManager | |
1223 | if (securityManager != null) { | |
1224 | int i = name.lastIndexOf('.'); | |
1225 | if (i >= 0) { | |
1226 | try { | |
1227 | securityManager.checkPackageAccess(name.substring(0,i)); | |
1228 | } catch (SecurityException se) { | |
1229 | String error = "Security Violation, attempt to use " + | |
1230 | "Restricted Class: " + name; | |
1231 | log.info(error, se); | |
1232 | throw new ClassNotFoundException(error, se); | |
1233 | } | |
1234 | } | |
1235 | } | |
1236 | ||
1237 | boolean delegateLoad = delegate || filter(name); | |
1238 | ||
1239 | // (1) Delegate to our parent if requested | |
1240 | if (delegateLoad) { | |
1241 | if (log.isDebugEnabled()) | |
1242 | log.debug(" Delegating to parent classloader1 " + parent); | |
1243 | try { | |
1244 | clazz = Class.forName(name, false, parent); | |
1245 | if (clazz != null) { | |
1246 | if (log.isDebugEnabled()) | |
1247 | log.debug(" Loading class from parent"); | |
1248 | if (resolve) | |
1249 | resolveClass(clazz); | |
1250 | return (clazz); | |
1251 | } | |
1252 | } catch (ClassNotFoundException e) { | |
1253 | // Ignore | |
1254 | } | |
1255 | } | |
1256 | ||
1257 | // (2) Search local repositories | |
1258 | if (log.isDebugEnabled()) | |
1259 | log.debug(" Searching local repositories"); | |
1260 | try { | |
1261 | clazz = findClass(name); | |
1262 | if (clazz != null) { | |
1263 | if (log.isDebugEnabled()) | |
1264 | log.debug(" Loading class from local repository"); | |
1265 | if (resolve) | |
1266 | resolveClass(clazz); | |
1267 | return (clazz); | |
1268 | } | |
1269 | } catch (ClassNotFoundException e) { | |
1270 | // Ignore | |
1271 | } | |
1272 | ||
1273 | // (3) Delegate to parent unconditionally | |
1274 | if (!delegateLoad) { | |
1275 | if (log.isDebugEnabled()) | |
1276 | log.debug(" Delegating to parent classloader at end: " + parent); | |
1277 | try { | |
1278 | clazz = Class.forName(name, false, parent); | |
1279 | if (clazz != null) { | |
1280 | if (log.isDebugEnabled()) | |
1281 | log.debug(" Loading class from parent"); | |
1282 | if (resolve) | |
1283 | resolveClass(clazz); | |
1284 | return (clazz); | |
1285 | } | |
1286 | } catch (ClassNotFoundException e) { | |
1287 | // Ignore | |
1288 | } | |
1289 | } | |
1290 | } | |
1291 | ||
1292 | throw new ClassNotFoundException(name); | |
1293 | } | |
1294 | ||
1295 | ||
1296 | protected void checkStateForClassLoading(String className) throws ClassNotFoundException { | |
1297 | // It is not permitted to load new classes once the web application has | |
1298 | // been stopped. | |
1299 | if (!state.isAvailable()) { | |
1300 | String msg = sm.getString("webappClassLoader.stopped", className); | |
1301 | IllegalStateException cause = new IllegalStateException(msg); | |
1302 | ClassNotFoundException cnfe = new ClassNotFoundException(); | |
1303 | cnfe.initCause(cause); | |
1304 | log.info(msg, cnfe); | |
1305 | throw cnfe; | |
1306 | } | |
1307 | } | |
1308 | ||
1309 | ||
1310 | /** | |
1311 | * Get the Permissions for a CodeSource. If this instance | |
1312 | * of WebappClassLoaderBase is for a web application context, | |
1313 | * add read FilePermission or JndiPermissions for the base | |
1314 | * directory (if unpacked), | |
1315 | * the context URL, and jar file resources. | |
1316 | * | |
1317 | * @param codeSource where the code was loaded from | |
1318 | * @return PermissionCollection for CodeSource | |
1319 | */ | |
1320 | @Override | |
1321 | protected PermissionCollection getPermissions(CodeSource codeSource) { | |
1322 | ||
1323 | String codeUrl = codeSource.getLocation().toString(); | |
1324 | PermissionCollection pc; | |
1325 | if ((pc = loaderPC.get(codeUrl)) == null) { | |
1326 | pc = super.getPermissions(codeSource); | |
1327 | if (pc != null) { | |
1328 | Iterator<Permission> perms = permissionList.iterator(); | |
1329 | while (perms.hasNext()) { | |
1330 | Permission p = perms.next(); | |
1331 | pc.add(p); | |
1332 | } | |
1333 | loaderPC.put(codeUrl,pc); | |
1334 | } | |
1335 | } | |
1336 | return (pc); | |
1337 | ||
1338 | } | |
1339 | ||
1340 | ||
1341 | /** | |
1342 | * {@inheritDoc} | |
1343 | * <p> | |
1344 | * Note that list of URLs returned by this method may not be complete. The | |
1345 | * web application class loader accesses class loader resources via the | |
1346 | * {@link WebResourceRoot} which supports the arbitrary mapping of | |
1347 | * additional files, directories and contents of JAR files under | |
1348 | * WEB-INF/classes. Any such resources will not be included in the URLs | |
1349 | * returned here. | |
1350 | */ | |
1351 | @Override | |
1352 | public URL[] getURLs() { | |
1353 | ArrayList<URL> result = new ArrayList<>(); | |
1354 | result.addAll(localRepositories); | |
1355 | result.addAll(Arrays.asList(super.getURLs())); | |
1356 | return result.toArray(new URL[result.size()]); | |
1357 | } | |
1358 | ||
1359 | ||
1360 | // ------------------------------------------------------ Lifecycle Methods | |
1361 | ||
1362 | ||
1363 | /** | |
1364 | * Add a lifecycle event listener to this component. | |
1365 | * | |
1366 | * @param listener The listener to add | |
1367 | */ | |
1368 | @Override | |
1369 | public void addLifecycleListener(LifecycleListener listener) { | |
1370 | // NOOP | |
1371 | } | |
1372 | ||
1373 | ||
1374 | /** | |
1375 | * Get the lifecycle listeners associated with this lifecycle. If this | |
1376 | * Lifecycle has no listeners registered, a zero-length array is returned. | |
1377 | */ | |
1378 | @Override | |
1379 | public LifecycleListener[] findLifecycleListeners() { | |
1380 | return new LifecycleListener[0]; | |
1381 | } | |
1382 | ||
1383 | ||
1384 | /** | |
1385 | * Remove a lifecycle event listener from this component. | |
1386 | * | |
1387 | * @param listener The listener to remove | |
1388 | */ | |
1389 | @Override | |
1390 | public void removeLifecycleListener(LifecycleListener listener) { | |
1391 | // NOOP | |
1392 | } | |
1393 | ||
1394 | ||
1395 | /** | |
1396 | * Obtain the current state of the source component. | |
1397 | * | |
1398 | * @return The current state of the source component. | |
1399 | */ | |
1400 | @Override | |
1401 | public LifecycleState getState() { | |
1402 | return state; | |
1403 | } | |
1404 | ||
1405 | ||
1406 | /** | |
1407 | * {@inheritDoc} | |
1408 | */ | |
1409 | @Override | |
1410 | public String getStateName() { | |
1411 | return getState().toString(); | |
1412 | } | |
1413 | ||
1414 | ||
1415 | @Override | |
1416 | public void init() { | |
1417 | state = LifecycleState.INITIALIZED; | |
1418 | } | |
1419 | ||
1420 | ||
1421 | /** | |
1422 | * Start the class loader. | |
1423 | * | |
1424 | * @exception LifecycleException if a lifecycle error occurs | |
1425 | */ | |
1426 | @Override | |
1427 | public void start() throws LifecycleException { | |
1428 | ||
1429 | state = LifecycleState.STARTING_PREP; | |
1430 | ||
1431 | WebResource classes = resources.getResource("/WEB-INF/classes"); | |
1432 | if (classes.isDirectory() && classes.canRead()) { | |
1433 | localRepositories.add(classes.getURL()); | |
1434 | } | |
1435 | WebResource[] jars = resources.listResources("/WEB-INF/lib"); | |
1436 | for (WebResource jar : jars) { | |
1437 | if (jar.getName().endsWith(".jar") && jar.isFile() && jar.canRead()) { | |
1438 | localRepositories.add(jar.getURL()); | |
1439 | jarModificationTimes.put( | |
1440 | jar.getName(), Long.valueOf(jar.getLastModified())); | |
1441 | } | |
1442 | } | |
1443 | ||
1444 | state = LifecycleState.STARTING; | |
1445 | ||
1446 | String encoding = null; | |
1447 | try { | |
1448 | encoding = System.getProperty("file.encoding"); | |
1449 | } catch (SecurityException e) { | |
1450 | return; | |
1451 | } | |
1452 | if (encoding.indexOf("EBCDIC")!=-1) { | |
1453 | needConvert = true; | |
1454 | } | |
1455 | ||
1456 | state = LifecycleState.STARTED; | |
1457 | } | |
1458 | ||
1459 | ||
1460 | /** | |
1461 | * Stop the class loader. | |
1462 | * | |
1463 | * @exception LifecycleException if a lifecycle error occurs | |
1464 | */ | |
1465 | @Override | |
1466 | public void stop() throws LifecycleException { | |
1467 | ||
1468 | state = LifecycleState.STOPPING_PREP; | |
1469 | ||
1470 | // Clearing references should be done before setting started to | |
1471 | // false, due to possible side effects | |
1472 | clearReferences(); | |
1473 | ||
1474 | state = LifecycleState.STOPPING; | |
1475 | ||
1476 | resourceEntries.clear(); | |
1477 | jarModificationTimes.clear(); | |
1478 | resources = null; | |
1479 | ||
1480 | permissionList.clear(); | |
1481 | loaderPC.clear(); | |
1482 | ||
1483 | state = LifecycleState.STOPPED; | |
1484 | } | |
1485 | ||
1486 | ||
1487 | @Override | |
1488 | public void destroy() { | |
1489 | state = LifecycleState.DESTROYING; | |
1490 | ||
1491 | try { | |
1492 | super.close(); | |
1493 | } catch (IOException ioe) { | |
1494 | log.warn(sm.getString("webappClassLoader.superCloseFail"), ioe); | |
1495 | } | |
1496 | state = LifecycleState.DESTROYED; | |
1497 | } | |
1498 | ||
1499 | ||
1500 | // ------------------------------------------------------ Protected Methods | |
1501 | ||
1502 | protected ClassLoader getJavaseClassLoader() { | |
1503 | return javaseClassLoader; | |
1504 | } | |
1505 | ||
1506 | protected void setJavaseClassLoader(ClassLoader classLoader) { | |
1507 | if (classLoader == null) { | |
1508 | throw new IllegalArgumentException( | |
1509 | sm.getString("webappClassLoader.javaseClassLoaderNull")); | |
1510 | } | |
1511 | javaseClassLoader = classLoader; | |
1512 | } | |
1513 | ||
1514 | /** | |
1515 | * Clear references. | |
1516 | */ | |
1517 | protected void clearReferences() { | |
1518 | ||
1519 | // De-register any remaining JDBC drivers | |
1520 | clearReferencesJdbc(); | |
1521 | ||
1522 | // Stop any threads the web application started | |
1523 | clearReferencesThreads(); | |
1524 | ||
1525 | // Check for leaks triggered by ThreadLocals loaded by this class loader | |
1526 | checkThreadLocalsForLeaks(); | |
1527 | ||
1528 | // Clear RMI Targets loaded by this class loader | |
1529 | clearReferencesRmiTargets(); | |
1530 | ||
1531 | // Null out any static or final fields from loaded classes, | |
1532 | // as a workaround for apparent garbage collection bugs | |
1533 | if (clearReferencesStatic) { | |
1534 | clearReferencesStaticFinal(); | |
1535 | } | |
1536 | ||
1537 | // Clear the IntrospectionUtils cache. | |
1538 | IntrospectionUtils.clear(); | |
1539 | ||
1540 | // Clear the classloader reference in common-logging | |
1541 | if (clearReferencesLogFactoryRelease) { | |
1542 | org.apache.juli.logging.LogFactory.release(this); | |
1543 | } | |
1544 | ||
1545 | // Clear the resource bundle cache | |
1546 | // This shouldn't be necessary, the cache uses weak references but | |
1547 | // it has caused leaks. Oddly, using the leak detection code in | |
1548 | // standard host allows the class loader to be GC'd. This has been seen | |
1549 | // on Sun but not IBM JREs. Maybe a bug in Sun's GC impl? | |
1550 | clearReferencesResourceBundles(); | |
1551 | ||
1552 | // Clear the classloader reference in the VM's bean introspector | |
1553 | java.beans.Introspector.flushCaches(); | |
1554 | ||
1555 | // Clear any custom URLStreamHandlers | |
1556 | TomcatURLStreamHandlerFactory.release(this); | |
1557 | } | |
1558 | ||
1559 | ||
1560 | /** | |
1561 | * Deregister any JDBC drivers registered by the webapp that the webapp | |
1562 | * forgot. This is made unnecessary complex because a) DriverManager | |
1563 | * checks the class loader of the calling class (it would be much easier | |
1564 | * if it checked the context class loader) b) using reflection would | |
1565 | * create a dependency on the DriverManager implementation which can, | |
1566 | * and has, changed. | |
1567 | * | |
1568 | * We can't just create an instance of JdbcLeakPrevention as it will be | |
1569 | * loaded by the common class loader (since it's .class file is in the | |
1570 | * $CATALINA_HOME/lib directory). This would fail DriverManager's check | |
1571 | * on the class loader of the calling class. So, we load the bytes via | |
1572 | * our parent class loader but define the class with this class loader | |
1573 | * so the JdbcLeakPrevention looks like a webapp class to the | |
1574 | * DriverManager. | |
1575 | * | |
1576 | * If only apps cleaned up after themselves... | |
1577 | */ | |
1578 | private final void clearReferencesJdbc() { | |
1579 | // We know roughly how big the class will be (~ 1K) so allow 2k as a | |
1580 | // starting point | |
1581 | byte[] classBytes = new byte[2048]; | |
1582 | int offset = 0; | |
1583 | try (InputStream is = getResourceAsStream( | |
1584 | "org/apache/catalina/loader/JdbcLeakPrevention.class")) { | |
1585 | int read = is.read(classBytes, offset, classBytes.length-offset); | |
1586 | while (read > -1) { | |
1587 | offset += read; | |
1588 | if (offset == classBytes.length) { | |
1589 | // Buffer full - double size | |
1590 | byte[] tmp = new byte[classBytes.length * 2]; | |
1591 | System.arraycopy(classBytes, 0, tmp, 0, classBytes.length); | |
1592 | classBytes = tmp; | |
1593 | } | |
1594 | read = is.read(classBytes, offset, classBytes.length-offset); | |
1595 | } | |
1596 | Class<?> lpClass = | |
1597 | defineClass("org.apache.catalina.loader.JdbcLeakPrevention", | |
1598 | classBytes, 0, offset, this.getClass().getProtectionDomain()); | |
1599 | Object obj = lpClass.newInstance(); | |
1600 | @SuppressWarnings("unchecked") | |
1601 | List<String> driverNames = (List<String>) obj.getClass().getMethod( | |
1602 | "clearJdbcDriverRegistrations").invoke(obj); | |
1603 | for (String name : driverNames) { | |
1604 | log.warn(sm.getString("webappClassLoader.clearJdbc", | |
1605 | getContextName(), name)); | |
1606 | } | |
1607 | } catch (Exception e) { | |
1608 | // So many things to go wrong above... | |
1609 | Throwable t = ExceptionUtils.unwrapInvocationTargetException(e); | |
1610 | ExceptionUtils.handleThrowable(t); | |
1611 | log.warn(sm.getString( | |
1612 | "webappClassLoader.jdbcRemoveFailed", getContextName()), t); | |
1613 | } | |
1614 | } | |
1615 | ||
1616 | ||
1617 | private final void clearReferencesStaticFinal() { | |
1618 | ||
1619 | Collection<ResourceEntry> values = resourceEntries.values(); | |
1620 | Iterator<ResourceEntry> loadedClasses = values.iterator(); | |
1621 | // | |
1622 | // walk through all loaded class to trigger initialization for | |
1623 | // any uninitialized classes, otherwise initialization of | |
1624 | // one class may call a previously cleared class. | |
1625 | while(loadedClasses.hasNext()) { | |
1626 | ResourceEntry entry = loadedClasses.next(); | |
1627 | if (entry.loadedClass != null) { | |
1628 | Class<?> clazz = entry.loadedClass; | |
1629 | try { | |
1630 | Field[] fields = clazz.getDeclaredFields(); | |
1631 | for (int i = 0; i < fields.length; i++) { | |
1632 | if(Modifier.isStatic(fields[i].getModifiers())) { | |
1633 | fields[i].get(null); | |
1634 | break; | |
1635 | } | |
1636 | } | |
1637 | } catch(Throwable t) { | |
1638 | // Ignore | |
1639 | } | |
1640 | } | |
1641 | } | |
1642 | loadedClasses = values.iterator(); | |
1643 | while (loadedClasses.hasNext()) { | |
1644 | ResourceEntry entry = loadedClasses.next(); | |
1645 | if (entry.loadedClass != null) { | |
1646 | Class<?> clazz = entry.loadedClass; | |
1647 | try { | |
1648 | Field[] fields = clazz.getDeclaredFields(); | |
1649 | for (int i = 0; i < fields.length; i++) { | |
1650 | Field field = fields[i]; | |
1651 | int mods = field.getModifiers(); | |
1652 | if (field.getType().isPrimitive() | |
1653 | || (field.getName().indexOf("$") != -1)) { | |
1654 | continue; | |
1655 | } | |
1656 | if (Modifier.isStatic(mods)) { | |
1657 | try { | |
1658 | field.setAccessible(true); | |
1659 | if (Modifier.isFinal(mods)) { | |
1660 | if (!((field.getType().getName().startsWith("java.")) | |
1661 | || (field.getType().getName().startsWith("javax.")))) { | |
1662 | nullInstance(field.get(null)); | |
1663 | } | |
1664 | } else { | |
1665 | field.set(null, null); | |
1666 | if (log.isDebugEnabled()) { | |
1667 | log.debug("Set field " + field.getName() | |
1668 | + " to null in class " + clazz.getName()); | |
1669 | } | |
1670 | } | |
1671 | } catch (Throwable t) { | |
1672 | ExceptionUtils.handleThrowable(t); | |
1673 | if (log.isDebugEnabled()) { | |
1674 | log.debug("Could not set field " + field.getName() | |
1675 | + " to null in class " + clazz.getName(), t); | |
1676 | } | |
1677 | } | |
1678 | } | |
1679 | } | |
1680 | } catch (Throwable t) { | |
1681 | ExceptionUtils.handleThrowable(t); | |
1682 | if (log.isDebugEnabled()) { | |
1683 | log.debug("Could not clean fields for class " + clazz.getName(), t); | |
1684 | } | |
1685 | } | |
1686 | } | |
1687 | } | |
1688 | ||
1689 | } | |
1690 | ||
1691 | ||
1692 | private void nullInstance(Object instance) { | |
1693 | if (instance == null) { | |
1694 | return; | |
1695 | } | |
1696 | Field[] fields = instance.getClass().getDeclaredFields(); | |
1697 | for (int i = 0; i < fields.length; i++) { | |
1698 | Field field = fields[i]; | |
1699 | int mods = field.getModifiers(); | |
1700 | if (field.getType().isPrimitive() | |
1701 | || (field.getName().indexOf("$") != -1)) { | |
1702 | continue; | |
1703 | } | |
1704 | try { | |
1705 | field.setAccessible(true); | |
1706 | if (Modifier.isStatic(mods) && Modifier.isFinal(mods)) { | |
1707 | // Doing something recursively is too risky | |
1708 | continue; | |
1709 | } | |
1710 | Object value = field.get(instance); | |
1711 | if (null != value) { | |
1712 | Class<? extends Object> valueClass = value.getClass(); | |
1713 | if (!loadedByThisOrChild(valueClass)) { | |
1714 | if (log.isDebugEnabled()) { | |
1715 | log.debug("Not setting field " + field.getName() + | |
1716 | " to null in object of class " + | |
1717 | instance.getClass().getName() + | |
1718 | " because the referenced object was of type " + | |
1719 | valueClass.getName() + | |
1720 | " which was not loaded by this web application class loader."); | |
1721 | } | |
1722 | } else { | |
1723 | field.set(instance, null); | |
1724 | if (log.isDebugEnabled()) { | |
1725 | log.debug("Set field " + field.getName() | |
1726 | + " to null in class " + instance.getClass().getName()); | |
1727 | } | |
1728 | } | |
1729 | } | |
1730 | } catch (Throwable t) { | |
1731 | ExceptionUtils.handleThrowable(t); | |
1732 | if (log.isDebugEnabled()) { | |
1733 | log.debug("Could not set field " + field.getName() | |
1734 | + " to null in object instance of class " | |
1735 | + instance.getClass().getName(), t); | |
1736 | } | |
1737 | } | |
1738 | } | |
1739 | } | |
1740 | ||
1741 | ||
1742 | @SuppressWarnings("deprecation") // thread.stop() | |
1743 | private void clearReferencesThreads() { | |
1744 | Thread[] threads = getThreads(); | |
1745 | List<Thread> executorThreadsToStop = new ArrayList<>(); | |
1746 | ||
1747 | // Iterate over the set of threads | |
1748 | for (Thread thread : threads) { | |
1749 | if (thread != null) { | |
1750 | ClassLoader ccl = thread.getContextClassLoader(); | |
1751 | if (ccl == this) { | |
1752 | // Don't warn about this thread | |
1753 | if (thread == Thread.currentThread()) { | |
1754 | continue; | |
1755 | } | |
1756 | ||
1757 | final String threadName = thread.getName(); | |
1758 | ||
1759 | // JVM controlled threads | |
1760 | ThreadGroup tg = thread.getThreadGroup(); | |
1761 | if (tg != null && | |
1762 | JVM_THREAD_GROUP_NAMES.contains(tg.getName())) { | |
1763 | ||
1764 | // HttpClient keep-alive threads | |
1765 | if (clearReferencesHttpClientKeepAliveThread && | |
1766 | threadName.equals("Keep-Alive-Timer")) { | |
1767 | thread.setContextClassLoader(parent); | |
1768 | log.debug(sm.getString( | |
1769 | "webappClassLoader.checkThreadsHttpClient")); | |
1770 | } | |
1771 | ||
1772 | // Don't warn about remaining JVM controlled threads | |
1773 | continue; | |
1774 | } | |
1775 | ||
1776 | // Skip threads that have already died | |
1777 | if (!thread.isAlive()) { | |
1778 | continue; | |
1779 | } | |
1780 | ||
1781 | // TimerThread can be stopped safely so treat separately | |
1782 | // "java.util.TimerThread" in Sun/Oracle JDK | |
1783 | // "java.util.Timer$TimerImpl" in Apache Harmony and in IBM JDK | |
1784 | if (thread.getClass().getName().startsWith("java.util.Timer") && | |
1785 | clearReferencesStopTimerThreads) { | |
1786 | clearReferencesStopTimerThread(thread); | |
1787 | continue; | |
1788 | } | |
1789 | ||
1790 | if (isRequestThread(thread)) { | |
1791 | log.warn(sm.getString("webappClassLoader.stackTraceRequestThread", | |
1792 | getContextName(), threadName, getStackTrace(thread))); | |
1793 | } else { | |
1794 | log.warn(sm.getString("webappClassLoader.stackTrace", | |
1795 | getContextName(), threadName, getStackTrace(thread))); | |
1796 | } | |
1797 | ||
1798 | // Don't try an stop the threads unless explicitly | |
1799 | // configured to do so | |
1800 | if (!clearReferencesStopThreads) { | |
1801 | continue; | |
1802 | } | |
1803 | ||
1804 | // If the thread has been started via an executor, try | |
1805 | // shutting down the executor | |
1806 | boolean usingExecutor = false; | |
1807 | try { | |
1808 | ||
1809 | // Runnable wrapped by Thread | |
1810 | // "target" in Sun/Oracle JDK | |
1811 | // "runnable" in IBM JDK | |
1812 | // "action" in Apache Harmony | |
1813 | Object target = null; | |
1814 | for (String fieldName : new String[] { "target", | |
1815 | "runnable", "action" }) { | |
1816 | try { | |
1817 | Field targetField = thread.getClass() | |
1818 | .getDeclaredField(fieldName); | |
1819 | targetField.setAccessible(true); | |
1820 | target = targetField.get(thread); | |
1821 | break; | |
1822 | } catch (NoSuchFieldException nfe) { | |
1823 | continue; | |
1824 | } | |
1825 | } | |
1826 | ||
1827 | // "java.util.concurrent" code is in public domain, | |
1828 | // so all implementations are similar | |
1829 | if (target != null && | |
1830 | target.getClass().getCanonicalName() != null | |
1831 | && target.getClass().getCanonicalName().equals( | |
1832 | "java.util.concurrent.ThreadPoolExecutor.Worker")) { | |
1833 | Field executorField = | |
1834 | target.getClass().getDeclaredField("this$0"); | |
1835 | executorField.setAccessible(true); | |
1836 | Object executor = executorField.get(target); | |
1837 | if (executor instanceof ThreadPoolExecutor) { | |
1838 | ((ThreadPoolExecutor) executor).shutdownNow(); | |
1839 | usingExecutor = true; | |
1840 | } | |
1841 | } | |
1842 | } catch (SecurityException e) { | |
1843 | log.warn(sm.getString( | |
1844 | "webappClassLoader.stopThreadFail", | |
1845 | thread.getName(), getContextName()), e); | |
1846 | } catch (NoSuchFieldException e) { | |
1847 | log.warn(sm.getString( | |
1848 | "webappClassLoader.stopThreadFail", | |
1849 | thread.getName(), getContextName()), e); | |
1850 | } catch (IllegalArgumentException e) { | |
1851 | log.warn(sm.getString( | |
1852 | "webappClassLoader.stopThreadFail", | |
1853 | thread.getName(), getContextName()), e); | |
1854 | } catch (IllegalAccessException e) { | |
1855 | log.warn(sm.getString( | |
1856 | "webappClassLoader.stopThreadFail", | |
1857 | thread.getName(), getContextName()), e); | |
1858 | } | |
1859 | ||
1860 | if (usingExecutor) { | |
1861 | // Executor may take a short time to stop all the | |
1862 | // threads. Make a note of threads that should be | |
1863 | // stopped and check them at the end of the method. | |
1864 | executorThreadsToStop.add(thread); | |
1865 | } else { | |
1866 | // This method is deprecated and for good reason. This | |
1867 | // is very risky code but is the only option at this | |
1868 | // point. A *very* good reason for apps to do this | |
1869 | // clean-up themselves. | |
1870 | thread.stop(); | |
1871 | } | |
1872 | } | |
1873 | } | |
1874 | } | |
1875 | ||
1876 | // If thread stopping is enabled, executor threads should have been | |
1877 | // stopped above when the executor was shut down but that depends on the | |
1878 | // thread correctly handling the interrupt. Give all the executor | |
1879 | // threads a few seconds shutdown and if they are still running | |
1880 | // Give threads up to 2 seconds to shutdown | |
1881 | int count = 0; | |
1882 | for (Thread t : executorThreadsToStop) { | |
1883 | while (t.isAlive() && count < 100) { | |
1884 | try { | |
1885 | Thread.sleep(20); | |
1886 | } catch (InterruptedException e) { | |
1887 | // Quit the while loop | |
1888 | break; | |
1889 | } | |
1890 | count++; | |
1891 | } | |
1892 | if (t.isAlive()) { | |
1893 | // This method is deprecated and for good reason. This is | |
1894 | // very risky code but is the only option at this point. | |
1895 | // A *very* good reason for apps to do this clean-up | |
1896 | // themselves. | |
1897 | t.stop(); | |
1898 | } | |
1899 | } | |
1900 | } | |
1901 | ||
1902 | ||
1903 | /* | |
1904 | * Look at a threads stack trace to see if it is a request thread or not. It | |
1905 | * isn't perfect, but it should be good-enough for most cases. | |
1906 | */ | |
1907 | private boolean isRequestThread(Thread thread) { | |
1908 | ||
1909 | StackTraceElement[] elements = thread.getStackTrace(); | |
1910 | ||
1911 | if (elements == null || elements.length == 0) { | |
1912 | // Must have stopped already. Too late to ignore it. Assume not a | |
1913 | // request processing thread. | |
1914 | return false; | |
1915 | } | |
1916 | ||
1917 | // Step through the methods in reverse order looking for calls to any | |
1918 | // CoyoteAdapter method. All request threads will have this unless | |
1919 | // Tomcat has been heavily modified - in which case there isn't much we | |
1920 | // can do. | |
1921 | for (int i = 0; i < elements.length; i++) { | |
1922 | StackTraceElement element = elements[elements.length - (i+1)]; | |
1923 | if ("org.apache.catalina.connector.CoyoteAdapter".equals( | |
1924 | element.getClassName())) { | |
1925 | return true; | |
1926 | } | |
1927 | } | |
1928 | return false; | |
1929 | } | |
1930 | ||
1931 | ||
1932 | private void clearReferencesStopTimerThread(Thread thread) { | |
1933 | ||
1934 | // Need to get references to: | |
1935 | // in Sun/Oracle JDK: | |
1936 | // - newTasksMayBeScheduled field (in java.util.TimerThread) | |
1937 | // - queue field | |
1938 | // - queue.clear() | |
1939 | // in IBM JDK, Apache Harmony: | |
1940 | // - cancel() method (in java.util.Timer$TimerImpl) | |
1941 | ||
1942 | try { | |
1943 | ||
1944 | try { | |
1945 | Field newTasksMayBeScheduledField = | |
1946 | thread.getClass().getDeclaredField("newTasksMayBeScheduled"); | |
1947 | newTasksMayBeScheduledField.setAccessible(true); | |
1948 | Field queueField = thread.getClass().getDeclaredField("queue"); | |
1949 | queueField.setAccessible(true); | |
1950 | ||
1951 | Object queue = queueField.get(thread); | |
1952 | ||
1953 | Method clearMethod = queue.getClass().getDeclaredMethod("clear"); | |
1954 | clearMethod.setAccessible(true); | |
1955 | ||
1956 | synchronized(queue) { | |
1957 | newTasksMayBeScheduledField.setBoolean(thread, false); | |
1958 | clearMethod.invoke(queue); | |
1959 | queue.notify(); // In case queue was already empty. | |
1960 | } | |
1961 | ||
1962 | }catch (NoSuchFieldException nfe){ | |
1963 | Method cancelMethod = thread.getClass().getDeclaredMethod("cancel"); | |
1964 | synchronized(thread) { | |
1965 | cancelMethod.setAccessible(true); | |
1966 | cancelMethod.invoke(thread); | |
1967 | } | |
1968 | } | |
1969 | ||
1970 | log.warn(sm.getString("webappClassLoader.warnTimerThread", | |
1971 | getContextName(), thread.getName())); | |
1972 | ||
1973 | } catch (Exception e) { | |
1974 | // So many things to go wrong above... | |
1975 | Throwable t = ExceptionUtils.unwrapInvocationTargetException(e); | |
1976 | ExceptionUtils.handleThrowable(t); | |
1977 | log.warn(sm.getString( | |
1978 | "webappClassLoader.stopTimerThreadFail", | |
1979 | thread.getName(), getContextName()), t); | |
1980 | } | |
1981 | } | |
1982 | ||
1983 | private void checkThreadLocalsForLeaks() { | |
1984 | Thread[] threads = getThreads(); | |
1985 | ||
1986 | try { | |
1987 | // Make the fields in the Thread class that store ThreadLocals | |
1988 | // accessible | |
1989 | Field threadLocalsField = | |
1990 | Thread.class.getDeclaredField("threadLocals"); | |
1991 | threadLocalsField.setAccessible(true); | |
1992 | Field inheritableThreadLocalsField = | |
1993 | Thread.class.getDeclaredField("inheritableThreadLocals"); | |
1994 | inheritableThreadLocalsField.setAccessible(true); | |
1995 | // Make the underlying array of ThreadLoad.ThreadLocalMap.Entry objects | |
1996 | // accessible | |
1997 | Class<?> tlmClass = Class.forName("java.lang.ThreadLocal$ThreadLocalMap"); | |
1998 | Field tableField = tlmClass.getDeclaredField("table"); | |
1999 | tableField.setAccessible(true); | |
2000 | Method expungeStaleEntriesMethod = tlmClass.getDeclaredMethod("expungeStaleEntries"); | |
2001 | expungeStaleEntriesMethod.setAccessible(true); | |
2002 | ||
2003 | for (int i = 0; i < threads.length; i++) { | |
2004 | Object threadLocalMap; | |
2005 | if (threads[i] != null) { | |
2006 | ||
2007 | // Clear the first map | |
2008 | threadLocalMap = threadLocalsField.get(threads[i]); | |
2009 | if (null != threadLocalMap){ | |
2010 | expungeStaleEntriesMethod.invoke(threadLocalMap); | |
2011 | checkThreadLocalMapForLeaks(threadLocalMap, tableField); | |
2012 | } | |
2013 | ||
2014 | // Clear the second map | |
2015 | threadLocalMap =inheritableThreadLocalsField.get(threads[i]); | |
2016 | if (null != threadLocalMap){ | |
2017 | expungeStaleEntriesMethod.invoke(threadLocalMap); | |
2018 | checkThreadLocalMapForLeaks(threadLocalMap, tableField); | |
2019 | } | |
2020 | } | |
2021 | } | |
2022 | } catch (Throwable t) { | |
2023 | ExceptionUtils.handleThrowable(t); | |
2024 | log.warn(sm.getString( | |
2025 | "webappClassLoader.checkThreadLocalsForLeaksFail", | |
2026 | getContextName()), t); | |
2027 | } | |
2028 | } | |
2029 | ||
2030 | ||
2031 | /** | |
2032 | * Analyzes the given thread local map object. Also pass in the field that | |
2033 | * points to the internal table to save re-calculating it on every | |
2034 | * call to this method. | |
2035 | */ | |
2036 | private void checkThreadLocalMapForLeaks(Object map, | |
2037 | Field internalTableField) throws IllegalAccessException, | |
2038 | NoSuchFieldException { | |
2039 | if (map != null) { | |
2040 | Object[] table = (Object[]) internalTableField.get(map); | |
2041 | if (table != null) { | |
2042 | for (int j =0; j < table.length; j++) { | |
2043 | Object obj = table[j]; | |
2044 | if (obj != null) { | |
2045 | boolean potentialLeak = false; | |
2046 | // Check the key | |
2047 | Object key = ((Reference<?>) obj).get(); | |
2048 | if (this.equals(key) || loadedByThisOrChild(key)) { | |
2049 | potentialLeak = true; | |
2050 | } | |
2051 | // Check the value | |
2052 | Field valueField = | |
2053 | obj.getClass().getDeclaredField("value"); | |
2054 | valueField.setAccessible(true); | |
2055 | Object value = valueField.get(obj); | |
2056 | if (this.equals(value) || loadedByThisOrChild(value)) { | |
2057 | potentialLeak = true; | |
2058 | } | |
2059 | if (potentialLeak) { | |
2060 | Object[] args = new Object[5]; | |
2061 | args[0] = getContextName(); | |
2062 | if (key != null) { | |
2063 | args[1] = getPrettyClassName(key.getClass()); | |
2064 | try { | |
2065 | args[2] = key.toString(); | |
2066 | } catch (Exception e) { | |
2067 | log.warn(sm.getString( | |
2068 | "webappClassLoader.checkThreadLocalsForLeaks.badKey", | |
2069 | args[1]), e); | |
2070 | args[2] = sm.getString( | |
2071 | "webappClassLoader.checkThreadLocalsForLeaks.unknown"); | |
2072 | } | |
2073 | } | |
2074 | if (value != null) { | |
2075 | args[3] = getPrettyClassName(value.getClass()); | |
2076 | try { | |
2077 | args[4] = value.toString(); | |
2078 | } catch (Exception e) { | |
2079 | log.warn(sm.getString( | |
2080 | "webappClassLoader.checkThreadLocalsForLeaks.badValue", | |
2081 | args[3]), e); | |
2082 | args[4] = sm.getString( | |
2083 | "webappClassLoader.checkThreadLocalsForLeaks.unknown"); | |
2084 | } | |
2085 | } | |
2086 | if (value == null) { | |
2087 | if (log.isDebugEnabled()) { | |
2088 | log.debug(sm.getString( | |
2089 | "webappClassLoader.checkThreadLocalsForLeaksDebug", | |
2090 | args)); | |
2091 | } | |
2092 | } else { | |
2093 | log.error(sm.getString( | |
2094 | "webappClassLoader.checkThreadLocalsForLeaks", | |
2095 | args)); | |
2096 | } | |
2097 | } | |
2098 | } | |
2099 | } | |
2100 | } | |
2101 | } | |
2102 | } | |
2103 | ||
2104 | private String getPrettyClassName(Class<?> clazz) { | |
2105 | String name = clazz.getCanonicalName(); | |
2106 | if (name==null){ | |
2107 | name = clazz.getName(); | |
2108 | } | |
2109 | return name; | |
2110 | } | |
2111 | ||
2112 | private String getStackTrace(Thread thread) { | |
2113 | StringBuilder builder = new StringBuilder(); | |
2114 | for (StackTraceElement ste : thread.getStackTrace()) { | |
2115 | builder.append("\n ").append(ste); | |
2116 | } | |
2117 | return builder.toString(); | |
2118 | } | |
2119 | ||
2120 | /** | |
2121 | * @param o object to test, may be null | |
2122 | * @return <code>true</code> if o has been loaded by the current classloader | |
2123 | * or one of its descendants. | |
2124 | */ | |
2125 | private boolean loadedByThisOrChild(Object o) { | |
2126 | if (o == null) { | |
2127 | return false; | |
2128 | } | |
2129 | ||
2130 | Class<?> clazz; | |
2131 | if (o instanceof Class) { | |
2132 | clazz = (Class<?>) o; | |
2133 | } else { | |
2134 | clazz = o.getClass(); | |
2135 | } | |
2136 | ||
2137 | ClassLoader cl = clazz.getClassLoader(); | |
2138 | while (cl != null) { | |
2139 | if (cl == this) { | |
2140 | return true; | |
2141 | } | |
2142 | cl = cl.getParent(); | |
2143 | } | |
2144 | ||
2145 | if (o instanceof Collection<?>) { | |
2146 | Iterator<?> iter = ((Collection<?>) o).iterator(); | |
2147 | try { | |
2148 | while (iter.hasNext()) { | |
2149 | Object entry = iter.next(); | |
2150 | if (loadedByThisOrChild(entry)) { | |
2151 | return true; | |
2152 | } | |
2153 | } | |
2154 | } catch (ConcurrentModificationException e) { | |
2155 | log.warn(sm.getString( | |
2156 | "webappClassLoader.loadedByThisOrChildFail", clazz.getName(), getContextName()), | |
2157 | e); | |
2158 | } | |
2159 | } | |
2160 | return false; | |
2161 | } | |
2162 | ||
2163 | /* | |
2164 | * Get the set of current threads as an array. | |
2165 | */ | |
2166 | private Thread[] getThreads() { | |
2167 | // Get the current thread group | |
2168 | ThreadGroup tg = Thread.currentThread().getThreadGroup(); | |
2169 | // Find the root thread group | |
2170 | try { | |
2171 | while (tg.getParent() != null) { | |
2172 | tg = tg.getParent(); | |
2173 | } | |
2174 | } catch (SecurityException se) { | |
2175 | String msg = sm.getString( | |
2176 | "webappClassLoader.getThreadGroupError", tg.getName()); | |
2177 | if (log.isDebugEnabled()) { | |
2178 | log.debug(msg, se); | |
2179 | } else { | |
2180 | log.warn(msg); | |
2181 | } | |
2182 | } | |
2183 | ||
2184 | int threadCountGuess = tg.activeCount() + 50; | |
2185 | Thread[] threads = new Thread[threadCountGuess]; | |
2186 | int threadCountActual = tg.enumerate(threads); | |
2187 | // Make sure we don't miss any threads | |
2188 | while (threadCountActual == threadCountGuess) { | |
2189 | threadCountGuess *=2; | |
2190 | threads = new Thread[threadCountGuess]; | |
2191 | // Note tg.enumerate(Thread[]) silently ignores any threads that | |
2192 | // can't fit into the array | |
2193 | threadCountActual = tg.enumerate(threads); | |
2194 | } | |
2195 | ||
2196 | return threads; | |
2197 | } | |
2198 | ||
2199 | ||
2200 | /** | |
2201 | * This depends on the internals of the Sun JVM so it does everything by | |
2202 | * reflection. | |
2203 | */ | |
2204 | private void clearReferencesRmiTargets() { | |
2205 | try { | |
2206 | // Need access to the ccl field of sun.rmi.transport.Target | |
2207 | Class<?> objectTargetClass = | |
2208 | Class.forName("sun.rmi.transport.Target"); | |
2209 | Field cclField = objectTargetClass.getDeclaredField("ccl"); | |
2210 | cclField.setAccessible(true); | |
2211 | ||
2212 | // Clear the objTable map | |
2213 | Class<?> objectTableClass = | |
2214 | Class.forName("sun.rmi.transport.ObjectTable"); | |
2215 | Field objTableField = objectTableClass.getDeclaredField("objTable"); | |
2216 | objTableField.setAccessible(true); | |
2217 | Object objTable = objTableField.get(null); | |
2218 | if (objTable == null) { | |
2219 | return; | |
2220 | } | |
2221 | ||
2222 | // Iterate over the values in the table | |
2223 | if (objTable instanceof Map<?,?>) { | |
2224 | Iterator<?> iter = ((Map<?,?>) objTable).values().iterator(); | |
2225 | while (iter.hasNext()) { | |
2226 | Object obj = iter.next(); | |
2227 | Object cclObject = cclField.get(obj); | |
2228 | if (this == cclObject) { | |
2229 | iter.remove(); | |
2230 | } | |
2231 | } | |
2232 | } | |
2233 | ||
2234 | // Clear the implTable map | |
2235 | Field implTableField = objectTableClass.getDeclaredField("implTable"); | |
2236 | implTableField.setAccessible(true); | |
2237 | Object implTable = implTableField.get(null); | |
2238 | if (implTable == null) { | |
2239 | return; | |
2240 | } | |
2241 | ||
2242 | // Iterate over the values in the table | |
2243 | if (implTable instanceof Map<?,?>) { | |
2244 | Iterator<?> iter = ((Map<?,?>) implTable).values().iterator(); | |
2245 | while (iter.hasNext()) { | |
2246 | Object obj = iter.next(); | |
2247 | Object cclObject = cclField.get(obj); | |
2248 | if (this == cclObject) { | |
2249 | iter.remove(); | |
2250 | } | |
2251 | } | |
2252 | } | |
2253 | } catch (ClassNotFoundException e) { | |
2254 | log.info(sm.getString("webappClassLoader.clearRmiInfo", | |
2255 | getContextName()), e); | |
2256 | } catch (SecurityException e) { | |
2257 | log.warn(sm.getString("webappClassLoader.clearRmiFail", | |
2258 | getContextName()), e); | |
2259 | } catch (NoSuchFieldException e) { | |
2260 | log.warn(sm.getString("webappClassLoader.clearRmiFail", | |
2261 | getContextName()), e); | |
2262 | } catch (IllegalArgumentException e) { | |
2263 | log.warn(sm.getString("webappClassLoader.clearRmiFail", | |
2264 | getContextName()), e); | |
2265 | } catch (IllegalAccessException e) { | |
2266 | log.warn(sm.getString("webappClassLoader.clearRmiFail", | |
2267 | getContextName()), e); | |
2268 | } | |
2269 | } | |
2270 | ||
2271 | ||
2272 | /** | |
2273 | * Clear the {@link ResourceBundle} cache of any bundles loaded by this | |
2274 | * class loader or any class loader where this loader is a parent class | |
2275 | * loader. Whilst {@link ResourceBundle#clearCache()} could be used there | |
2276 | * are complications around the | |
2277 | * {@link org.apache.jasper.servlet.JasperLoader} that mean a reflection | |
2278 | * based approach is more likely to be complete. | |
2279 | * | |
2280 | * The ResourceBundle is using WeakReferences so it shouldn't be pinning the | |
2281 | * class loader in memory. However, it is. Therefore clear ou the | |
2282 | * references. | |
2283 | */ | |
2284 | private void clearReferencesResourceBundles() { | |
2285 | // Get a reference to the cache | |
2286 | try { | |
2287 | Field cacheListField = | |
2288 | ResourceBundle.class.getDeclaredField("cacheList"); | |
2289 | cacheListField.setAccessible(true); | |
2290 | ||
2291 | // Java 6 uses ConcurrentMap | |
2292 | // Java 5 uses SoftCache extends Abstract Map | |
2293 | // So use Map and it *should* work with both | |
2294 | Map<?,?> cacheList = (Map<?,?>) cacheListField.get(null); | |
2295 | ||
2296 | // Get the keys (loader references are in the key) | |
2297 | Set<?> keys = cacheList.keySet(); | |
2298 | ||
2299 | Field loaderRefField = null; | |
2300 | ||
2301 | // Iterate over the keys looking at the loader instances | |
2302 | Iterator<?> keysIter = keys.iterator(); | |
2303 | ||
2304 | int countRemoved = 0; | |
2305 | ||
2306 | while (keysIter.hasNext()) { | |
2307 | Object key = keysIter.next(); | |
2308 | ||
2309 | if (loaderRefField == null) { | |
2310 | loaderRefField = | |
2311 | key.getClass().getDeclaredField("loaderRef"); | |
2312 | loaderRefField.setAccessible(true); | |
2313 | } | |
2314 | WeakReference<?> loaderRef = | |
2315 | (WeakReference<?>) loaderRefField.get(key); | |
2316 | ||
2317 | ClassLoader loader = (ClassLoader) loaderRef.get(); | |
2318 | ||
2319 | while (loader != null && loader != this) { | |
2320 | loader = loader.getParent(); | |
2321 | } | |
2322 | ||
2323 | if (loader != null) { | |
2324 | keysIter.remove(); | |
2325 | countRemoved++; | |
2326 | } | |
2327 | } | |
2328 | ||
2329 | if (countRemoved > 0 && log.isDebugEnabled()) { | |
2330 | log.debug(sm.getString( | |
2331 | "webappClassLoader.clearReferencesResourceBundlesCount", | |
2332 | Integer.valueOf(countRemoved), getContextName())); | |
2333 | } | |
2334 | } catch (SecurityException e) { | |
2335 | log.warn(sm.getString( | |
2336 | "webappClassLoader.clearReferencesResourceBundlesFail", | |
2337 | getContextName()), e); | |
2338 | } catch (NoSuchFieldException e) { | |
2339 | if (Globals.IS_ORACLE_JVM) { | |
2340 | log.warn(sm.getString( | |
2341 | "webappClassLoader.clearReferencesResourceBundlesFail", | |
2342 | getContextName()), e); | |
2343 | } else { | |
2344 | log.debug(sm.getString( | |
2345 | "webappClassLoader.clearReferencesResourceBundlesFail", | |
2346 | getContextName()), e); | |
2347 | } | |
2348 | } catch (IllegalArgumentException e) { | |
2349 | log.warn(sm.getString( | |
2350 | "webappClassLoader.clearReferencesResourceBundlesFail", | |
2351 | getContextName()), e); | |
2352 | } catch (IllegalAccessException e) { | |
2353 | log.warn(sm.getString( | |
2354 | "webappClassLoader.clearReferencesResourceBundlesFail", | |
2355 | getContextName()), e); | |
2356 | } | |
2357 | } | |
2358 | ||
2359 | ||
2360 | /** | |
2361 | * Find specified class in local repositories. | |
2362 | * | |
2363 | * @param name The binary name of the class to be loaded | |
2364 | * | |
2365 | * @return the loaded class, or null if the class isn't found | |
2366 | */ | |
2367 | protected Class<?> findClassInternal(String name) { | |
2368 | ||
2369 | if (!validate(name)) { | |
2370 | return null; | |
2371 | } | |
2372 | ||
2373 | String path = binaryNameToPath(name, true); | |
2374 | ||
2375 | ResourceEntry entry = null; | |
2376 | ||
2377 | if (securityManager != null) { | |
2378 | PrivilegedAction<ResourceEntry> dp = | |
2379 | new PrivilegedFindResourceByName(name, path); | |
2380 | entry = AccessController.doPrivileged(dp); | |
2381 | } else { | |
2382 | entry = findResourceInternal(name, path); | |
2383 | } | |
2384 | ||
2385 | if (entry == null) { | |
2386 | return null; | |
2387 | } | |
2388 | ||
2389 | Class<?> clazz = entry.loadedClass; | |
2390 | if (clazz != null) | |
2391 | return clazz; | |
2392 | ||
2393 | synchronized (getClassLoadingLock(name)) { | |
2394 | clazz = entry.loadedClass; | |
2395 | if (clazz != null) | |
2396 | return clazz; | |
2397 | ||
2398 | if (entry.binaryContent == null) { | |
2399 | return null; | |
2400 | } | |
2401 | ||
2402 | // Looking up the package | |
2403 | String packageName = null; | |
2404 | int pos = name.lastIndexOf('.'); | |
2405 | if (pos != -1) | |
2406 | packageName = name.substring(0, pos); | |
2407 | ||
2408 | Package pkg = null; | |
2409 | ||
2410 | if (packageName != null) { | |
2411 | pkg = getPackage(packageName); | |
2412 | // Define the package (if null) | |
2413 | if (pkg == null) { | |
2414 | try { | |
2415 | if (entry.manifest == null) { | |
2416 | definePackage(packageName, null, null, null, null, | |
2417 | null, null, null); | |
2418 | } else { | |
2419 | definePackage(packageName, entry.manifest, | |
2420 | entry.codeBase); | |
2421 | } | |
2422 | } catch (IllegalArgumentException e) { | |
2423 | // Ignore: normal error due to dual definition of package | |
2424 | } | |
2425 | pkg = getPackage(packageName); | |
2426 | } | |
2427 | } | |
2428 | ||
2429 | if (securityManager != null) { | |
2430 | ||
2431 | // Checking sealing | |
2432 | if (pkg != null) { | |
2433 | boolean sealCheck = true; | |
2434 | if (pkg.isSealed()) { | |
2435 | sealCheck = pkg.isSealed(entry.codeBase); | |
2436 | } else { | |
2437 | sealCheck = (entry.manifest == null) | |
2438 | || !isPackageSealed(packageName, entry.manifest); | |
2439 | } | |
2440 | if (!sealCheck) | |
2441 | throw new SecurityException | |
2442 | ("Sealing violation loading " + name + " : Package " | |
2443 | + packageName + " is sealed."); | |
2444 | } | |
2445 | ||
2446 | } | |
2447 | ||
2448 | try { | |
2449 | clazz = defineClass(name, entry.binaryContent, 0, | |
2450 | entry.binaryContent.length, | |
2451 | new CodeSource(entry.codeBase, entry.certificates)); | |
2452 | } catch (UnsupportedClassVersionError ucve) { | |
2453 | throw new UnsupportedClassVersionError( | |
2454 | ucve.getLocalizedMessage() + " " + | |
2455 | sm.getString("webappClassLoader.wrongVersion", | |
2456 | name)); | |
2457 | } | |
2458 | // Now the class has been defined, clear the elements of the local | |
2459 | // resource cache that are no longer required. | |
2460 | entry.loadedClass = clazz; | |
2461 | entry.binaryContent = null; | |
2462 | entry.codeBase = null; | |
2463 | entry.manifest = null; | |
2464 | entry.certificates = null; | |
2465 | // Retain entry.source in case of a getResourceAsStream() call on | |
2466 | // the class file after the class has been defined. | |
2467 | } | |
2468 | ||
2469 | return clazz; | |
2470 | } | |
2471 | ||
2472 | ||
2473 | private String binaryNameToPath(String binaryName, boolean withLeadingSlash) { | |
2474 | // 1 for leading '/', 6 for ".class" | |
2475 | StringBuilder path = new StringBuilder(7 + binaryName.length()); | |
2476 | if (withLeadingSlash) { | |
2477 | path.append('/'); | |
2478 | } | |
2479 | path.append(binaryName.replace('.', '/')); | |
2480 | path.append(CLASS_FILE_SUFFIX); | |
2481 | return path.toString(); | |
2482 | } | |
2483 | ||
2484 | ||
2485 | private String nameToPath(String name) { | |
2486 | if (name.startsWith("/")) { | |
2487 | return name; | |
2488 | } | |
2489 | StringBuilder path = new StringBuilder( | |
2490 | 1 + name.length()); | |
2491 | path.append('/'); | |
2492 | path.append(name); | |
2493 | return path.toString(); | |
2494 | } | |
2495 | ||
2496 | ||
2497 | /** | |
2498 | * Find specified resource in local repositories. | |
2499 | * | |
2500 | * @return the loaded resource, or null if the resource isn't found | |
2501 | */ | |
2502 | protected ResourceEntry findResourceInternal(final String name, final String path) { | |
2503 | ||
2504 | if (!state.isAvailable()) { | |
2505 | log.info(sm.getString("webappClassLoader.stopped", name)); | |
2506 | return null; | |
2507 | } | |
2508 | ||
2509 | if (name == null || path == null) { | |
2510 | return null; | |
2511 | } | |
2512 | ||
2513 | ResourceEntry entry = resourceEntries.get(path); | |
2514 | if (entry != null) { | |
2515 | return entry; | |
2516 | } | |
2517 | ||
2518 | boolean isClassResource = path.endsWith(CLASS_FILE_SUFFIX); | |
2519 | boolean isCacheable = isClassResource; | |
2520 | if (!isCacheable) { | |
2521 | isCacheable = path.startsWith(SERVICES_PREFIX); | |
2522 | } | |
2523 | ||
2524 | WebResource resource = null; | |
2525 | ||
2526 | boolean fileNeedConvert = false; | |
2527 | ||
2528 | resource = resources.getClassLoaderResource(path); | |
2529 | ||
2530 | if (!resource.exists()) { | |
2531 | return null; | |
2532 | } | |
2533 | ||
2534 | entry = new ResourceEntry(); | |
2535 | entry.source = resource.getURL(); | |
2536 | entry.codeBase = entry.source; | |
2537 | entry.lastModified = resource.getLastModified(); | |
2538 | ||
2539 | if (needConvert && path.endsWith(".properties")) { | |
2540 | fileNeedConvert = true; | |
2541 | } | |
2542 | ||
2543 | /* Only cache the binary content if there is some content | |
2544 | * available one of the following is true: | |
2545 | * a) It is a class file since the binary content is only cached | |
2546 | * until the class has been loaded | |
2547 | * or | |
2548 | * b) The file needs conversion to address encoding issues (see | |
2549 | * below) | |
2550 | * or | |
2551 | * c) The resource is a service provider configuration file located | |
2552 | * under META=INF/services | |
2553 | * | |
2554 | * In all other cases do not cache the content to prevent | |
2555 | * excessive memory usage if large resources are present (see | |
2556 | * https://issues.apache.org/bugzilla/show_bug.cgi?id=53081). | |
2557 | */ | |
2558 | if (isCacheable || fileNeedConvert) { | |
2559 | byte[] binaryContent = resource.getContent(); | |
2560 | if (binaryContent != null) { | |
2561 | if (fileNeedConvert) { | |
2562 | // Workaround for certain files on platforms that use | |
2563 | // EBCDIC encoding, when they are read through FileInputStream. | |
2564 | // See commit message of rev.303915 for details | |
2565 | // http://svn.apache.org/viewvc?view=revision&revision=303915 | |
2566 | String str = new String(binaryContent); | |
2567 | try { | |
2568 | binaryContent = str.getBytes(StandardCharsets.UTF_8); | |
2569 | } catch (Exception e) { | |
2570 | return null; | |
2571 | } | |
2572 | } | |
2573 | entry.binaryContent = binaryContent; | |
2574 | // The certificates and manifest are made available as a side | |
2575 | // effect of reading the binary content | |
2576 | entry.certificates = resource.getCertificates(); | |
2577 | } | |
2578 | } | |
2579 | entry.manifest = resource.getManifest(); | |
2580 | ||
2581 | if (isClassResource && entry.binaryContent != null && | |
2582 | this.transformers.size() > 0) { | |
2583 | // If the resource is a class just being loaded, decorate it | |
2584 | // with any attached transformers | |
2585 | String className = name.endsWith(CLASS_FILE_SUFFIX) ? | |
2586 | name.substring(0, name.length() - CLASS_FILE_SUFFIX.length()) : name; | |
2587 | String internalName = className.replace(".", "/"); | |
2588 | ||
2589 | for (ClassFileTransformer transformer : this.transformers) { | |
2590 | try { | |
2591 | byte[] transformed = transformer.transform( | |
2592 | this, internalName, null, null, entry.binaryContent | |
2593 | ); | |
2594 | if (transformed != null) { | |
2595 | entry.binaryContent = transformed; | |
2596 | } | |
2597 | } catch (IllegalClassFormatException e) { | |
2598 | log.error(sm.getString("webappClassLoader.transformError", name), e); | |
2599 | return null; | |
2600 | } | |
2601 | } | |
2602 | } | |
2603 | ||
2604 | // Add the entry in the local resource repository | |
2605 | synchronized (resourceEntries) { | |
2606 | // Ensures that all the threads which may be in a race to load | |
2607 | // a particular class all end up with the same ResourceEntry | |
2608 | // instance | |
2609 | ResourceEntry entry2 = resourceEntries.get(path); | |
2610 | if (entry2 == null) { | |
2611 | resourceEntries.put(path, entry); | |
2612 | } else { | |
2613 | entry = entry2; | |
2614 | } | |
2615 | } | |
2616 | ||
2617 | return entry; | |
2618 | } | |
2619 | ||
2620 | ||
2621 | /** | |
2622 | * Returns true if the specified package name is sealed according to the | |
2623 | * given manifest. | |
2624 | */ | |
2625 | protected boolean isPackageSealed(String name, Manifest man) { | |
2626 | ||
2627 | String path = name.replace('.', '/') + '/'; | |
2628 | Attributes attr = man.getAttributes(path); | |
2629 | String sealed = null; | |
2630 | if (attr != null) { | |
2631 | sealed = attr.getValue(Name.SEALED); | |
2632 | } | |
2633 | if (sealed == null) { | |
2634 | if ((attr = man.getMainAttributes()) != null) { | |
2635 | sealed = attr.getValue(Name.SEALED); | |
2636 | } | |
2637 | } | |
2638 | return "true".equalsIgnoreCase(sealed); | |
2639 | ||
2640 | } | |
2641 | ||
2642 | ||
2643 | /** | |
2644 | * Finds the resource with the given name if it has previously been | |
2645 | * loaded and cached by this class loader, and return an input stream | |
2646 | * to the resource data. If this resource has not been cached, return | |
2647 | * <code>null</code>. | |
2648 | * | |
2649 | * @param name Name of the resource to return | |
2650 | */ | |
2651 | protected InputStream findLoadedResource(String name) { | |
2652 | ||
2653 | String path = nameToPath(name); | |
2654 | ||
2655 | ResourceEntry entry = resourceEntries.get(path); | |
2656 | if (entry != null) { | |
2657 | if (entry.binaryContent != null) | |
2658 | return new ByteArrayInputStream(entry.binaryContent); | |
2659 | else { | |
2660 | try { | |
2661 | return entry.source.openStream(); | |
2662 | } catch (IOException ioe) { | |
2663 | // Ignore | |
2664 | } | |
2665 | } | |
2666 | } | |
2667 | return null; | |
2668 | } | |
2669 | ||
2670 | ||
2671 | /** | |
2672 | * Finds the class with the given name if it has previously been | |
2673 | * loaded and cached by this class loader, and return the Class object. | |
2674 | * If this class has not been cached, return <code>null</code>. | |
2675 | * | |
2676 | * @param name The binary name of the resource to return | |
2677 | */ | |
2678 | protected Class<?> findLoadedClass0(String name) { | |
2679 | ||
2680 | String path = binaryNameToPath(name, true); | |
2681 | ||
2682 | ResourceEntry entry = resourceEntries.get(path); | |
2683 | if (entry != null) { | |
2684 | return entry.loadedClass; | |
2685 | } | |
2686 | return null; | |
2687 | } | |
2688 | ||
2689 | ||
2690 | /** | |
2691 | * Refresh the system policy file, to pick up eventual changes. | |
2692 | */ | |
2693 | protected void refreshPolicy() { | |
2694 | ||
2695 | try { | |
2696 | // The policy file may have been modified to adjust | |
2697 | // permissions, so we're reloading it when loading or | |
2698 | // reloading a Context | |
2699 | Policy policy = Policy.getPolicy(); | |
2700 | policy.refresh(); | |
2701 | } catch (AccessControlException e) { | |
2702 | // Some policy files may restrict this, even for the core, | |
2703 | // so this exception is ignored | |
2704 | } | |
2705 | ||
2706 | } | |
2707 | ||
2708 | ||
2709 | /** | |
2710 | * Filter classes. | |
2711 | * | |
2712 | * @param name class name | |
2713 | * @return true if the class should be filtered | |
2714 | */ | |
2715 | protected synchronized boolean filter(String name) { | |
2716 | ||
2717 | if (name == null) | |
2718 | return false; | |
2719 | ||
2720 | // Looking up the package | |
2721 | String packageName = null; | |
2722 | int pos = name.lastIndexOf('.'); | |
2723 | if (pos != -1) | |
2724 | packageName = name.substring(0, pos); | |
2725 | else | |
2726 | return false; | |
2727 | ||
2728 | packageTriggersPermit.reset(packageName); | |
2729 | if (packageTriggersPermit.lookingAt()) { | |
2730 | return false; | |
2731 | } | |
2732 | ||
2733 | packageTriggersDeny.reset(packageName); | |
2734 | if (packageTriggersDeny.lookingAt()) { | |
2735 | return true; | |
2736 | } | |
2737 | ||
2738 | return false; | |
2739 | } | |
2740 | ||
2741 | ||
2742 | /** | |
2743 | * Validate a classname. As per SRV.9.7.2, we must restrict loading of | |
2744 | * classes from J2SE (java.*) and most classes of the servlet API | |
2745 | * (javax.servlet.*). That should enhance robustness and prevent a number | |
2746 | * of user error (where an older version of servlet.jar would be present | |
2747 | * in /WEB-INF/lib). | |
2748 | * | |
2749 | * @param name class name | |
2750 | * @return true if the name is valid | |
2751 | */ | |
2752 | protected boolean validate(String name) { | |
2753 | ||
2754 | // Need to be careful with order here | |
2755 | if (name == null) { | |
2756 | // Can't load a class without a name | |
2757 | return false; | |
2758 | } | |
2759 | if (name.startsWith("java.")) { | |
2760 | // Must never load java.* classes | |
2761 | return false; | |
2762 | } | |
2763 | if (name.startsWith("javax.servlet.jsp.jstl")) { | |
2764 | // OK for web apps to package JSTL | |
2765 | return true; | |
2766 | } | |
2767 | if (name.startsWith("javax.servlet.")) { | |
2768 | // Web apps should never package any other Servlet or JSP classes | |
2769 | return false; | |
2770 | } | |
2771 | if (name.startsWith("javax.el")) { | |
2772 | // Must never load javax.el.* classes | |
2773 | return false; | |
2774 | } | |
2775 | ||
2776 | // Assume everything else is OK | |
2777 | return true; | |
2778 | ||
2779 | } | |
2780 | ||
2781 | ||
2782 | @Override | |
2783 | protected void addURL(URL url) { | |
2784 | super.addURL(url); | |
2785 | hasExternalRepositories = true; | |
2786 | } | |
2787 | } |
91 | 91 | /** |
92 | 92 | * The class loader being managed by this Loader component. |
93 | 93 | */ |
94 | private WebappClassLoader classLoader = null; | |
94 | private WebappClassLoaderBase classLoader = null; | |
95 | 95 | |
96 | 96 | |
97 | 97 | /** |
109 | 109 | |
110 | 110 | /** |
111 | 111 | * The Java class name of the ClassLoader implementation to be used. |
112 | * This class should extend WebappClassLoader, otherwise, a different | |
112 | * This class should extend WebappClassLoaderBase, otherwise, a different | |
113 | 113 | * loader implementation must be used. |
114 | 114 | */ |
115 | private String loaderClass = | |
116 | "org.apache.catalina.loader.WebappClassLoader"; | |
115 | private String loaderClass = WebappClassLoader.class.getName(); | |
117 | 116 | |
118 | 117 | |
119 | 118 | /** |
405 | 404 | if (!contextName.startsWith("/")) { |
406 | 405 | contextName = "/" + contextName; |
407 | 406 | } |
408 | ObjectName cloname = new ObjectName(context.getDomain() + | |
409 | ":type=WebappClassLoader,host=" + context.getParent().getName() + | |
410 | ",context=" + contextName); | |
407 | ObjectName cloname = new ObjectName(context.getDomain() + ":type=" + | |
408 | classLoader.getClass().getSimpleName() + ",host=" + | |
409 | context.getParent().getName() + ",context=" + contextName); | |
411 | 410 | Registry.getRegistry(null, null) |
412 | 411 | .registerComponent(classLoader, cloname, null); |
413 | 412 | |
455 | 454 | if (!contextName.startsWith("/")) { |
456 | 455 | contextName = "/" + contextName; |
457 | 456 | } |
458 | ObjectName cloname = new ObjectName(context.getDomain() + | |
459 | ":type=WebappClassLoader,host=" + context.getParent().getName() + | |
460 | ",context=" + contextName); | |
457 | ObjectName cloname = new ObjectName(context.getDomain() + ":type=" + | |
458 | classLoader.getClass().getSimpleName() + ",host=" + | |
459 | context.getParent().getName() + ",context=" + contextName); | |
461 | 460 | Registry.getRegistry(null, null).unregisterComponent(cloname); |
462 | 461 | } catch (Exception e) { |
463 | 462 | log.error("LifecycleException ", e); |
500 | 499 | /** |
501 | 500 | * Create associated classLoader. |
502 | 501 | */ |
503 | private WebappClassLoader createClassLoader() | |
502 | private WebappClassLoaderBase createClassLoader() | |
504 | 503 | throws Exception { |
505 | 504 | |
506 | 505 | Class<?> clazz = Class.forName(loaderClass); |
507 | WebappClassLoader classLoader = null; | |
506 | WebappClassLoaderBase classLoader = null; | |
508 | 507 | |
509 | 508 | if (parentClassLoader == null) { |
510 | 509 | parentClassLoader = context.getParentClassLoader(); |
512 | 511 | Class<?>[] argTypes = { ClassLoader.class }; |
513 | 512 | Object[] args = { parentClassLoader }; |
514 | 513 | Constructor<?> constr = clazz.getConstructor(argTypes); |
515 | classLoader = (WebappClassLoader) constr.newInstance(args); | |
514 | classLoader = (WebappClassLoaderBase) constr.newInstance(args); | |
516 | 515 | |
517 | 516 | return classLoader; |
518 | 517 | } |
89 | 89 | |
90 | 90 | </mbean> |
91 | 91 | |
92 | ||
93 | <mbean name="ParallelWebappClassLoader" | |
94 | description="Classloader implementation which is specialized for handling web applications and is capable of loading classes in parallel" | |
95 | domain="Catalina" | |
96 | group="Loader" | |
97 | type="org.apache.catalina.loader.ParallelWebappClassLoader"> | |
98 | ||
99 | <attribute name="className" | |
100 | description="Fully qualified class name of the managed object" | |
101 | type="java.lang.String" | |
102 | writeable="false"/> | |
103 | ||
104 | <attribute name="contextName" | |
105 | description="Name of the webapp context" | |
106 | type="java.lang.String" | |
107 | writeable="false"/> | |
108 | ||
109 | <attribute name="delegate" | |
110 | description="The 'follow standard delegation model' flag that will be used to configure our ClassLoader" | |
111 | type="boolean"/> | |
112 | ||
113 | <attribute name="stateName" | |
114 | description="The name of the LifecycleState that this component is currently in" | |
115 | type="java.lang.String" | |
116 | writeable="false"/> | |
117 | ||
118 | <attribute name="URLs" | |
119 | description="The URLs of this loader" | |
120 | type="[Ljava.net.URL;"/> | |
121 | ||
122 | </mbean> | |
92 | 123 | </mbeans-descriptors> |
45 | 45 | import org.apache.catalina.connector.Request; |
46 | 46 | import org.apache.catalina.connector.Response; |
47 | 47 | import org.apache.catalina.util.LifecycleMBeanBase; |
48 | import org.apache.catalina.util.MD5Encoder; | |
49 | 48 | import org.apache.catalina.util.SessionConfig; |
50 | 49 | import org.apache.juli.logging.Log; |
51 | 50 | import org.apache.juli.logging.LogFactory; |
55 | 54 | import org.apache.tomcat.util.descriptor.web.SecurityCollection; |
56 | 55 | import org.apache.tomcat.util.descriptor.web.SecurityConstraint; |
57 | 56 | import org.apache.tomcat.util.res.StringManager; |
57 | import org.apache.tomcat.util.security.ConcurrentMessageDigest; | |
58 | import org.apache.tomcat.util.security.MD5Encoder; | |
58 | 59 | import org.ietf.jgss.GSSContext; |
59 | 60 | import org.ietf.jgss.GSSCredential; |
60 | 61 | import org.ietf.jgss.GSSException; |
102 | 103 | |
103 | 104 | /** |
104 | 105 | * The MessageDigest object for digesting user credentials (passwords). |
105 | */ | |
106 | * | |
107 | * @deprecated Unused. Will be removed in Tomcat 9.0.x onwards. | |
108 | */ | |
109 | @Deprecated | |
106 | 110 | protected volatile MessageDigest md = null; |
107 | 111 | |
108 | 112 | |
109 | 113 | /** |
110 | 114 | * MD5 message digest provider. |
111 | */ | |
115 | * | |
116 | * @deprecated Unused. Will be removed in Tomcat 9.0.x onwards. | |
117 | */ | |
118 | @Deprecated | |
112 | 119 | protected static volatile MessageDigest md5Helper; |
113 | 120 | |
114 | 121 | |
389 | 396 | throw new IllegalArgumentException(uee.getMessage()); |
390 | 397 | } |
391 | 398 | |
392 | String serverDigest = null; | |
393 | // Bugzilla 32137 | |
394 | synchronized(md5Helper) { | |
395 | serverDigest = MD5Encoder.encode(md5Helper.digest(valueBytes)); | |
396 | } | |
399 | String serverDigest = MD5Encoder.encode(ConcurrentMessageDigest.digestMD5(valueBytes)); | |
397 | 400 | |
398 | 401 | if (log.isDebugEnabled()) { |
399 | 402 | log.debug("Digest : " + clientDigest + " Username:" + username |
508 | 511 | // Server is storing digested passwords with a prefix indicating |
509 | 512 | // the digest type |
510 | 513 | String serverDigest = serverCredentials.substring(5); |
511 | String userDigest; | |
512 | synchronized (this) { | |
513 | md.reset(); | |
514 | md.update(userCredentials.getBytes(StandardCharsets.ISO_8859_1)); | |
515 | userDigest = Base64.encodeBase64String(md.digest()); | |
516 | } | |
514 | String userDigest = Base64.encodeBase64String(ConcurrentMessageDigest.digest( | |
515 | getDigest(), userCredentials.getBytes(StandardCharsets.ISO_8859_1))); | |
517 | 516 | return userDigest.equals(serverDigest); |
518 | 517 | |
519 | 518 | } else if (serverCredentials.startsWith("{SSHA}")) { |
530 | 529 | byte[] serverDigestBytes = new byte[saltPos]; |
531 | 530 | System.arraycopy(serverDigestPlusSaltBytes, 0, |
532 | 531 | serverDigestBytes, 0, saltPos); |
532 | final int saltLength = serverDigestPlusSaltBytes.length - saltPos; | |
533 | byte[] serverSaltBytes = new byte[saltLength]; | |
534 | System.arraycopy(serverDigestPlusSaltBytes, saltPos, | |
535 | serverSaltBytes, 0, saltLength); | |
533 | 536 | |
534 | 537 | // Generate the digested form of the user provided password |
535 | 538 | // using the salt |
536 | byte[] userDigestBytes; | |
537 | synchronized (this) { | |
538 | md.reset(); | |
539 | // User provided password | |
540 | md.update(userCredentials.getBytes(StandardCharsets.ISO_8859_1)); | |
541 | // Add the salt | |
542 | md.update(serverDigestPlusSaltBytes, saltPos, | |
543 | serverDigestPlusSaltBytes.length - saltPos); | |
544 | userDigestBytes = md.digest(); | |
545 | } | |
539 | byte[] userDigestBytes = ConcurrentMessageDigest.digest(getDigest(), | |
540 | userCredentials.getBytes(StandardCharsets.ISO_8859_1), | |
541 | serverSaltBytes); | |
546 | 542 | |
547 | 543 | return Arrays.equals(userDigestBytes, serverDigestBytes); |
548 | 544 | |
1119 | 1115 | protected void startInternal() throws LifecycleException { |
1120 | 1116 | |
1121 | 1117 | // Create a MessageDigest instance for credentials, if desired |
1122 | if (digest != null) { | |
1118 | ||
1119 | if (getDigest() != null) { | |
1123 | 1120 | try { |
1124 | md = MessageDigest.getInstance(digest); | |
1121 | md = MessageDigest.getInstance(getDigest()); | |
1122 | ConcurrentMessageDigest.init(getDigest()); | |
1125 | 1123 | } catch (NoSuchAlgorithmException e) { |
1126 | 1124 | throw new LifecycleException |
1127 | (sm.getString("realmBase.algorithm", digest), e); | |
1128 | } | |
1125 | (sm.getString("realmBase.algorithm", getDigest()), e); | |
1126 | } | |
1127 | ||
1129 | 1128 | } |
1130 | 1129 | |
1131 | 1130 | setState(LifecycleState.STARTING); |
1182 | 1181 | // Digest the user credentials and return as hexadecimal |
1183 | 1182 | synchronized (this) { |
1184 | 1183 | try { |
1185 | md.reset(); | |
1186 | ||
1187 | 1184 | byte[] bytes = null; |
1188 | 1185 | try { |
1189 | 1186 | bytes = credentials.getBytes(getDigestCharset()); |
1191 | 1188 | log.error("Illegal digestEncoding: " + getDigestEncoding(), uee); |
1192 | 1189 | throw new IllegalArgumentException(uee.getMessage()); |
1193 | 1190 | } |
1194 | md.update(bytes); | |
1195 | ||
1196 | return (HexUtils.toHexString(md.digest())); | |
1191 | ||
1192 | return (HexUtils.toHexString(ConcurrentMessageDigest.digest(getDigest(), bytes))); | |
1197 | 1193 | } catch (Exception e) { |
1198 | 1194 | log.error(sm.getString("realmBase.digest"), e); |
1199 | 1195 | return (credentials); |
1203 | 1199 | } |
1204 | 1200 | |
1205 | 1201 | protected boolean hasMessageDigest() { |
1206 | return !(md == null); | |
1202 | return getDigest() != null; | |
1207 | 1203 | } |
1208 | 1204 | |
1209 | 1205 | /** |
1210 | 1206 | * Return the digest associated with given principal's user name. |
1211 | 1207 | */ |
1212 | 1208 | protected String getDigest(String username, String realmName) { |
1213 | if (md5Helper == null) { | |
1214 | try { | |
1215 | md5Helper = MessageDigest.getInstance("MD5"); | |
1216 | } catch (NoSuchAlgorithmException e) { | |
1217 | log.error("Couldn't get MD5 digest: ", e); | |
1218 | throw new IllegalStateException(e.getMessage()); | |
1219 | } | |
1220 | } | |
1221 | ||
1222 | 1209 | if (hasMessageDigest()) { |
1223 | 1210 | // Use pre-generated digest |
1224 | 1211 | return getPassword(username); |
1235 | 1222 | throw new IllegalArgumentException(uee.getMessage()); |
1236 | 1223 | } |
1237 | 1224 | |
1238 | byte[] digest; | |
1239 | // Bugzilla 32137 | |
1240 | synchronized(md5Helper) { | |
1241 | digest = md5Helper.digest(valueBytes); | |
1242 | } | |
1243 | ||
1244 | return MD5Encoder.encode(digest); | |
1225 | return MD5Encoder.encode(ConcurrentMessageDigest.digestMD5(valueBytes)); | |
1245 | 1226 | } |
1246 | 1227 | |
1247 | 1228 |
108 | 108 | final String basePackage = "org.apache.catalina.loader."; |
109 | 109 | loader.loadClass |
110 | 110 | (basePackage + |
111 | "WebappClassLoader$PrivilegedFindResourceByName"); | |
111 | "WebappClassLoaderBase$PrivilegedFindResourceByName"); | |
112 | 112 | } |
113 | 113 | |
114 | 114 | |
275 | 275 | clazz.newInstance(); |
276 | 276 | loader.loadClass(basePackage + "util.http.HttpMessages"); |
277 | 277 | loader.loadClass(basePackage + "util.http.parser.HttpParser"); |
278 | loader.loadClass(basePackage + "util.http.parser.HttpParser$SkipResult"); | |
279 | 278 | loader.loadClass(basePackage + "util.http.parser.MediaType"); |
280 | 279 | loader.loadClass(basePackage + "util.http.parser.MediaTypeCache"); |
280 | loader.loadClass(basePackage + "util.http.parser.SkipResult"); | |
281 | 281 | // net |
282 | 282 | loader.loadClass(basePackage + "util.net.Constants"); |
283 | 283 | loader.loadClass(basePackage + "util.net.DispatchType"); |
2031 | 2031 | while ( (exception == null) && (ranges.hasNext()) ) { |
2032 | 2032 | |
2033 | 2033 | InputStream resourceInputStream = resource.getInputStream(); |
2034 | InputStream istream = | |
2035 | new BufferedInputStream(resourceInputStream, input); | |
2036 | ||
2037 | Range currentRange = ranges.next(); | |
2038 | ||
2039 | // Writing MIME header. | |
2040 | ostream.println(); | |
2041 | ostream.println("--" + mimeSeparation); | |
2042 | if (contentType != null) | |
2043 | ostream.println("Content-Type: " + contentType); | |
2044 | ostream.println("Content-Range: bytes " + currentRange.start | |
2045 | + "-" + currentRange.end + "/" | |
2046 | + currentRange.length); | |
2047 | ostream.println(); | |
2048 | ||
2049 | // Printing content | |
2050 | exception = copyRange(istream, ostream, currentRange.start, | |
2051 | currentRange.end); | |
2052 | ||
2053 | istream.close(); | |
2054 | ||
2034 | try (InputStream istream = new BufferedInputStream(resourceInputStream, input)) { | |
2035 | ||
2036 | Range currentRange = ranges.next(); | |
2037 | ||
2038 | // Writing MIME header. | |
2039 | ostream.println(); | |
2040 | ostream.println("--" + mimeSeparation); | |
2041 | if (contentType != null) | |
2042 | ostream.println("Content-Type: " + contentType); | |
2043 | ostream.println("Content-Range: bytes " + currentRange.start | |
2044 | + "-" + currentRange.end + "/" | |
2045 | + currentRange.length); | |
2046 | ostream.println(); | |
2047 | ||
2048 | // Printing content | |
2049 | exception = copyRange(istream, ostream, currentRange.start, | |
2050 | currentRange.end); | |
2051 | } | |
2055 | 2052 | } |
2056 | 2053 | |
2057 | 2054 | ostream.println(); |
20 | 20 | import java.io.StringWriter; |
21 | 21 | import java.io.Writer; |
22 | 22 | import java.nio.charset.StandardCharsets; |
23 | import java.security.MessageDigest; | |
24 | import java.security.NoSuchAlgorithmException; | |
25 | 23 | import java.util.Date; |
26 | 24 | import java.util.Enumeration; |
27 | 25 | import java.util.Hashtable; |
33 | 31 | import javax.servlet.RequestDispatcher; |
34 | 32 | import javax.servlet.ServletContext; |
35 | 33 | import javax.servlet.ServletException; |
36 | import javax.servlet.UnavailableException; | |
37 | 34 | import javax.servlet.http.HttpServletRequest; |
38 | 35 | import javax.servlet.http.HttpServletResponse; |
39 | 36 | import javax.xml.parsers.DocumentBuilder; |
43 | 40 | import org.apache.catalina.WebResource; |
44 | 41 | import org.apache.catalina.util.ConcurrentDateFormat; |
45 | 42 | import org.apache.catalina.util.DOMWriter; |
46 | import org.apache.catalina.util.MD5Encoder; | |
47 | 43 | import org.apache.catalina.util.XMLWriter; |
48 | 44 | import org.apache.tomcat.util.buf.UDecoder; |
49 | 45 | import org.apache.tomcat.util.http.FastHttpDateFormat; |
50 | 46 | import org.apache.tomcat.util.http.RequestUtil; |
47 | import org.apache.tomcat.util.security.ConcurrentMessageDigest; | |
48 | import org.apache.tomcat.util.security.MD5Encoder; | |
51 | 49 | import org.w3c.dom.Document; |
52 | 50 | import org.w3c.dom.Element; |
53 | 51 | import org.w3c.dom.Node; |
190 | 188 | TimeZone.getTimeZone("GMT")); |
191 | 189 | |
192 | 190 | |
193 | /** | |
194 | * MD5 message digest provider. | |
195 | */ | |
196 | protected static MessageDigest md5Helper; | |
197 | ||
198 | ||
199 | 191 | // ----------------------------------------------------- Instance Variables |
200 | 192 | |
201 | 193 | /** |
270 | 262 | if (getServletConfig().getInitParameter("allowSpecialPaths") != null) |
271 | 263 | allowSpecialPaths = Boolean.parseBoolean( |
272 | 264 | getServletConfig().getInitParameter("allowSpecialPaths")); |
273 | ||
274 | // Load the MD5 helper used to calculate signatures. | |
275 | try { | |
276 | md5Helper = MessageDigest.getInstance("MD5"); | |
277 | } catch (NoSuchAlgorithmException e) { | |
278 | throw new UnavailableException("No MD5"); | |
279 | } | |
280 | ||
281 | 265 | } |
282 | 266 | |
283 | 267 | |
1075 | 1059 | + lock.depth + "-" + lock.owner + "-" + lock.tokens + "-" |
1076 | 1060 | + lock.expiresAt + "-" + System.currentTimeMillis() + "-" |
1077 | 1061 | + secret; |
1078 | String lockToken = MD5Encoder.encode(md5Helper.digest( | |
1062 | String lockToken = MD5Encoder.encode(ConcurrentMessageDigest.digestMD5( | |
1079 | 1063 | lockTokenStr.getBytes(StandardCharsets.ISO_8859_1))); |
1080 | 1064 | |
1081 | 1065 | if (resource.isDirectory() && lock.depth == maxDepth) { |
137 | 137 | |
138 | 138 | static { |
139 | 139 | // Load our mapping properties for the standard authenticators |
140 | InputStream is = | |
141 | ContextConfig.class.getClassLoader().getResourceAsStream( | |
142 | "org/apache/catalina/startup/Authenticators.properties"); | |
143 | Properties props = null; | |
144 | props = new Properties(); | |
145 | if (is != null) { | |
146 | try { | |
140 | Properties props = new Properties(); | |
141 | try (InputStream is = ContextConfig.class.getClassLoader().getResourceAsStream( | |
142 | "org/apache/catalina/startup/Authenticators.properties");) { | |
143 | if (is != null) { | |
147 | 144 | props.load(is); |
148 | } catch (IOException e) { | |
149 | props = null; | |
150 | } | |
145 | } | |
146 | } catch (IOException ioe) { | |
147 | props = null; | |
151 | 148 | } |
152 | 149 | authenticators = props; |
153 | ||
154 | 150 | } |
155 | 151 | |
156 | 152 | /** |
164 | 160 | */ |
165 | 161 | protected static final Map<Host,DefaultWebXmlCacheEntry> hostWebXmlCache = |
166 | 162 | new ConcurrentHashMap<>(); |
163 | ||
164 | ||
165 | /** | |
166 | * Set used as the value for {@code JavaClassCacheEntry.sciSet} when there | |
167 | * are no SCIs associated with a class. | |
168 | */ | |
169 | private static final Set<ServletContainerInitializer> EMPTY_SCI_SET = Collections.emptySet(); | |
167 | 170 | |
168 | 171 | |
169 | 172 | // ----------------------------------------------------- Instance Variables |
608 | 611 | } |
609 | 612 | } |
610 | 613 | |
611 | if (docBase.toLowerCase(Locale.ENGLISH).endsWith(".war") && !file.isDirectory() && unpackWARs) { | |
612 | URL war = new URL("jar:" + (new File(docBase)).toURI().toURL() + "!/"); | |
613 | docBase = ExpandWar.expand(host, war, pathName); | |
614 | file = new File(docBase); | |
615 | docBase = file.getCanonicalPath(); | |
616 | if (context instanceof StandardContext) { | |
617 | ((StandardContext) context).setOriginalDocBase(origDocBase); | |
618 | } | |
619 | } else if (docBase.toLowerCase(Locale.ENGLISH).endsWith(".war") && | |
620 | !file.isDirectory() && !unpackWARs) { | |
621 | URL war = | |
622 | new URL("jar:" + (new File (docBase)).toURI().toURL() + "!/"); | |
623 | ExpandWar.validate(host, war, pathName); | |
614 | if (docBase.toLowerCase(Locale.ENGLISH).endsWith(".war") && !file.isDirectory()) { | |
615 | if (unpackWARs) { | |
616 | URL war = new URL("jar:" + (new File(docBase)).toURI().toURL() + "!/"); | |
617 | docBase = ExpandWar.expand(host, war, pathName); | |
618 | file = new File(docBase); | |
619 | docBase = file.getCanonicalPath(); | |
620 | if (context instanceof StandardContext) { | |
621 | ((StandardContext) context).setOriginalDocBase(origDocBase); | |
622 | } | |
623 | } else { | |
624 | URL war = | |
625 | new URL("jar:" + (new File (docBase)).toURI().toURL() + "!/"); | |
626 | ExpandWar.validate(host, war, pathName); | |
627 | } | |
624 | 628 | } else { |
625 | 629 | File docDir = new File(docBase); |
626 | 630 | if (!docDir.exists()) { |
1985 | 1989 | boolean handlesTypesOnly) |
1986 | 1990 | throws ClassFormatException, IOException { |
1987 | 1991 | |
1988 | ClassParser parser = new ClassParser(is, null); | |
1992 | ClassParser parser = new ClassParser(is); | |
1989 | 1993 | JavaClass clazz = parser.parse(); |
1990 | 1994 | checkHandlesTypes(clazz); |
1991 | 1995 | |
1993 | 1997 | return; |
1994 | 1998 | } |
1995 | 1999 | |
1996 | String className = clazz.getClassName(); | |
1997 | ||
1998 | 2000 | AnnotationEntry[] annotationsEntries = clazz.getAnnotationEntries(); |
1999 | ||
2000 | for (AnnotationEntry ae : annotationsEntries) { | |
2001 | String type = ae.getAnnotationType(); | |
2002 | if ("Ljavax/servlet/annotation/WebServlet;".equals(type)) { | |
2003 | processAnnotationWebServlet(className, ae, fragment); | |
2004 | }else if ("Ljavax/servlet/annotation/WebFilter;".equals(type)) { | |
2005 | processAnnotationWebFilter(className, ae, fragment); | |
2006 | }else if ("Ljavax/servlet/annotation/WebListener;".equals(type)) { | |
2007 | fragment.addListener(className); | |
2008 | } else { | |
2009 | // Unknown annotation - ignore | |
2001 | if (annotationsEntries != null) { | |
2002 | String className = clazz.getClassName(); | |
2003 | for (AnnotationEntry ae : annotationsEntries) { | |
2004 | String type = ae.getAnnotationType(); | |
2005 | if ("Ljavax/servlet/annotation/WebServlet;".equals(type)) { | |
2006 | processAnnotationWebServlet(className, ae, fragment); | |
2007 | }else if ("Ljavax/servlet/annotation/WebFilter;".equals(type)) { | |
2008 | processAnnotationWebFilter(className, ae, fragment); | |
2009 | }else if ("Ljavax/servlet/annotation/WebListener;".equals(type)) { | |
2010 | fragment.addListener(className); | |
2011 | } else { | |
2012 | // Unknown annotation - ignore | |
2013 | } | |
2010 | 2014 | } |
2011 | 2015 | } |
2012 | 2016 | } |
2047 | 2051 | classHierarchyToString(className, entry))); |
2048 | 2052 | } |
2049 | 2053 | } |
2050 | if (entry.getSciSet().size() > 0) { | |
2054 | if (!entry.getSciSet().isEmpty()) { | |
2051 | 2055 | // Need to try and load the class |
2052 | 2056 | clazz = Introspection.loadClass(context, className); |
2053 | 2057 | if (clazz == null) { |
2055 | 2059 | return; |
2056 | 2060 | } |
2057 | 2061 | |
2058 | for (ServletContainerInitializer sci : | |
2059 | entry.getSciSet()) { | |
2062 | for (ServletContainerInitializer sci : entry.getSciSet()) { | |
2060 | 2063 | Set<Class<?>> classes = initializerClassMap.get(sci); |
2061 | 2064 | if (classes == null) { |
2062 | 2065 | classes = new HashSet<>(); |
2068 | 2071 | } |
2069 | 2072 | |
2070 | 2073 | if (handlesTypesAnnotations) { |
2071 | for (Map.Entry<Class<?>, Set<ServletContainerInitializer>> entry : | |
2072 | typeInitializerMap.entrySet()) { | |
2073 | if (entry.getKey().isAnnotation()) { | |
2074 | AnnotationEntry[] annotationEntries = | |
2075 | javaClass.getAnnotationEntries(); | |
2076 | for (AnnotationEntry annotationEntry : annotationEntries) { | |
2077 | if (entry.getKey().getName().equals( | |
2078 | getClassName(annotationEntry.getAnnotationType()))) { | |
2079 | if (clazz == null) { | |
2080 | clazz = Introspection.loadClass( | |
2081 | context, className); | |
2074 | AnnotationEntry[] annotationEntries = javaClass.getAnnotationEntries(); | |
2075 | if (annotationEntries != null) { | |
2076 | for (Map.Entry<Class<?>, Set<ServletContainerInitializer>> entry : | |
2077 | typeInitializerMap.entrySet()) { | |
2078 | if (entry.getKey().isAnnotation()) { | |
2079 | String entryClassName = entry.getKey().getName(); | |
2080 | for (AnnotationEntry annotationEntry : annotationEntries) { | |
2081 | if (entryClassName.equals( | |
2082 | getClassName(annotationEntry.getAnnotationType()))) { | |
2082 | 2083 | if (clazz == null) { |
2083 | // Can't load the class so no point | |
2084 | // continuing | |
2085 | return; | |
2084 | clazz = Introspection.loadClass( | |
2085 | context, className); | |
2086 | if (clazz == null) { | |
2087 | // Can't load the class so no point | |
2088 | // continuing | |
2089 | return; | |
2090 | } | |
2086 | 2091 | } |
2092 | for (ServletContainerInitializer sci : entry.getValue()) { | |
2093 | initializerClassMap.get(sci).add(clazz); | |
2094 | } | |
2095 | break; | |
2087 | 2096 | } |
2088 | for (ServletContainerInitializer sci : entry.getValue()) { | |
2089 | initializerClassMap.get(sci).add(clazz); | |
2090 | } | |
2091 | break; | |
2092 | 2097 | } |
2093 | 2098 | } |
2094 | 2099 | } |
2143 | 2148 | if (is == null) { |
2144 | 2149 | return; |
2145 | 2150 | } |
2146 | ClassParser parser = new ClassParser(is, null); | |
2151 | ClassParser parser = new ClassParser(is); | |
2147 | 2152 | JavaClass clazz = parser.parse(); |
2148 | 2153 | populateJavaClassCache(clazz.getClassName(), clazz); |
2149 | 2154 | } catch (ClassFormatException e) { |
2166 | 2171 | |
2167 | 2172 | // Avoid an infinite loop with java.lang.Object |
2168 | 2173 | if (cacheEntry.equals(superClassCacheEntry)) { |
2169 | cacheEntry.setSciSet(new HashSet<ServletContainerInitializer>()); | |
2174 | cacheEntry.setSciSet(EMPTY_SCI_SET); | |
2170 | 2175 | return; |
2171 | 2176 | } |
2172 | 2177 | |
2195 | 2200 | result.addAll(getSCIsForClass(interfaceName)); |
2196 | 2201 | } |
2197 | 2202 | |
2198 | cacheEntry.setSciSet(result); | |
2203 | cacheEntry.setSciSet(result.isEmpty() ? EMPTY_SCI_SET : result); | |
2199 | 2204 | } |
2200 | 2205 | |
2201 | 2206 | private Set<ServletContainerInitializer> getSCIsForClass(String className) { |
2208 | 2213 | } |
2209 | 2214 | } |
2210 | 2215 | } |
2211 | return Collections.emptySet(); | |
2216 | return EMPTY_SCI_SET; | |
2212 | 2217 | } |
2213 | 2218 | |
2214 | 2219 | private static final String getClassName(String internalForm) { |
2225 | 2230 | AnnotationEntry ae, WebXml fragment) { |
2226 | 2231 | String servletName = null; |
2227 | 2232 | // must search for name s. Spec Servlet API 3.0 - 8.2.3.3.n.ii page 81 |
2228 | ElementValuePair[] evps = ae.getElementValuePairs(); | |
2233 | List<ElementValuePair> evps = ae.getElementValuePairs(); | |
2229 | 2234 | for (ElementValuePair evp : evps) { |
2230 | 2235 | String name = evp.getNameString(); |
2231 | 2236 | if ("name".equals(name)) { |
2252 | 2257 | boolean urlPatternsSet = false; |
2253 | 2258 | String[] urlPatterns = null; |
2254 | 2259 | |
2255 | // ElementValuePair[] evps = ae.getElementValuePairs(); | |
2260 | // List<ElementValuePair> evps = ae.getElementValuePairs(); | |
2256 | 2261 | for (ElementValuePair evp : evps) { |
2257 | 2262 | String name = evp.getNameString(); |
2258 | 2263 | if ("value".equals(name) || "urlPatterns".equals(name)) { |
2335 | 2340 | AnnotationEntry ae, WebXml fragment) { |
2336 | 2341 | String filterName = null; |
2337 | 2342 | // must search for name s. Spec Servlet API 3.0 - 8.2.3.3.n.ii page 81 |
2338 | ElementValuePair[] evps = ae.getElementValuePairs(); | |
2343 | List<ElementValuePair> evps = ae.getElementValuePairs(); | |
2339 | 2344 | for (ElementValuePair evp : evps) { |
2340 | 2345 | String name = evp.getNameString(); |
2341 | 2346 | if ("filterName".equals(name)) { |
2494 | 2499 | ((ArrayElementValue) ev).getElementValuesArray(); |
2495 | 2500 | for (ElementValue value : arrayValues) { |
2496 | 2501 | if (value instanceof AnnotationElementValue) { |
2497 | ElementValuePair[] evps = ((AnnotationElementValue) | |
2498 | value).getAnnotationEntry().getElementValuePairs(); | |
2502 | List<ElementValuePair> evps = ((AnnotationElementValue) value) | |
2503 | .getAnnotationEntry().getElementValuePairs(); | |
2499 | 2504 | String initParamName = null; |
2500 | 2505 | String initParamValue = null; |
2501 | 2506 | for (ElementValuePair evp : evps) { |
18 | 18 | import java.beans.PropertyChangeListener; |
19 | 19 | import java.io.File; |
20 | 20 | import java.net.URL; |
21 | import java.nio.charset.Charset; | |
22 | import java.nio.charset.StandardCharsets; | |
21 | 23 | import java.util.Locale; |
22 | 24 | import java.util.Map; |
23 | 25 | import java.util.Set; |
752 | 754 | } |
753 | 755 | |
754 | 756 | @Override |
755 | public Object getNamingToken() { | |
756 | return null; | |
757 | } | |
757 | public Object getNamingToken() { return null; } | |
758 | ||
759 | @Override | |
760 | public void setUseRfc6265(boolean useRfc6265) { /* NO-OP */ } | |
761 | ||
762 | @Override | |
763 | public boolean getUseRfc6265() {return false; } | |
764 | ||
765 | @Override | |
766 | public void setCookieEncoding(String encoding) { /* NO-OP */ } | |
767 | ||
768 | @Override | |
769 | public String getCookieEncoding() { return "UTF-8"; } | |
770 | ||
771 | @Override | |
772 | public Charset getCookieEncodingCharset() { return StandardCharsets.UTF_8; } | |
758 | 773 | }⏎ |
898 | 898 | if (deployXML && xml.exists() && copyThisXml) { |
899 | 899 | deployedApp.redeployResources.put(xml.getAbsolutePath(), |
900 | 900 | Long.valueOf(xml.lastModified())); |
901 | } else if (!copyThisXml ) { | |
901 | } else { | |
902 | 902 | // In case an XML file is added to the config base later |
903 | 903 | deployedApp.redeployResources.put( |
904 | 904 | (new File(host.getConfigBaseFile(), |
1222 | 1222 | // expanded WAR (if any) |
1223 | 1223 | Context context = (Context) host.findChild(app.name); |
1224 | 1224 | String docBase = context.getDocBase(); |
1225 | docBase = docBase.toLowerCase(Locale.ENGLISH); | |
1226 | if (!docBase.endsWith(".war")) { | |
1225 | if (!docBase.toLowerCase(Locale.ENGLISH).endsWith(".war")) { | |
1227 | 1226 | // This is an expanded directory |
1228 | 1227 | File docBaseFile = new File(docBase); |
1229 | 1228 | if (!docBaseFile.isAbsolute()) { |
1519 | 1518 | Context previousContext = |
1520 | 1519 | (Context) host.findChild(previous.getName()); |
1521 | 1520 | Context currentContext = |
1522 | (Context) host.findChild(previous.getName()); | |
1521 | (Context) host.findChild(current.getName()); | |
1523 | 1522 | if (previousContext != null && currentContext != null && |
1524 | 1523 | currentContext.getState().isAvailable() && |
1525 | 1524 | !isServiced(previous.getName())) { |
120 | 120 | userConfig.start=UserConfig: Processing START |
121 | 121 | userConfig.stop=UserConfig: Processing STOP |
122 | 122 | userConfig.deploy.threaded.error=Error waiting for multi-thread deployment of user directories to complete |
123 | versionLoggerListener.serverInfo.server.version=Server version: {0} | |
124 | versionLoggerListener.serverInfo.server.built =Server built: {0} | |
125 | versionLoggerListener.serverInfo.server.number =Server number: {0} | |
126 | versionLoggerListener.serverInfo.os.name =OS Name: {0} | |
127 | versionLoggerListener.serverInfo.os.version =OS Version: {0} | |
128 | versionLoggerListener.serverInfo.os.arch =Architecture: {0} | |
129 | versionLoggerListener.serverInfo.vm.version =JVM Version: {0} | |
130 | versionLoggerListener.serverInfo.vm.vendor =JVM Vendor: {0} | |
123 | 131 | webAnnotationSet.invalidInjection=Invalid method resource injection annotation. |
0 | /* | |
1 | * Licensed to the Apache Software Foundation (ASF) under one or more | |
2 | * contributor license agreements. See the NOTICE file distributed with | |
3 | * this work for additional information regarding copyright ownership. | |
4 | * The ASF licenses this file to You under the Apache License, Version 2.0 | |
5 | * (the "License"); you may not use this file except in compliance with | |
6 | * the License. You may obtain a copy of the License at | |
7 | * | |
8 | * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
15 | */ | |
16 | package org.apache.catalina.startup; | |
17 | ||
18 | import org.apache.catalina.LifecycleEvent; | |
19 | import org.apache.catalina.LifecycleListener; | |
20 | import org.apache.catalina.util.ServerInfo; | |
21 | import org.apache.juli.logging.Log; | |
22 | import org.apache.juli.logging.LogFactory; | |
23 | import org.apache.tomcat.util.res.StringManager; | |
24 | ||
25 | /** | |
26 | * Logs version information on startup. | |
27 | */ | |
28 | public class VersionLoggerListener implements LifecycleListener { | |
29 | ||
30 | private static final Log log = LogFactory.getLog(VersionLoggerListener.class); | |
31 | ||
32 | /** | |
33 | * The string manager for this package. | |
34 | */ | |
35 | protected static final StringManager sm = StringManager.getManager(Constants.Package); | |
36 | ||
37 | ||
38 | public VersionLoggerListener() { | |
39 | // The log message is generated here to ensure that it appears before | |
40 | // any log messages from the APRLifecycleListener. This won't be logged | |
41 | // on shutdown because only the Server element in server.xml is | |
42 | // processed on shutdown. | |
43 | log(); | |
44 | } | |
45 | ||
46 | ||
47 | @Override | |
48 | public void lifecycleEvent(LifecycleEvent event) { | |
49 | // NO-OP | |
50 | } | |
51 | ||
52 | ||
53 | private void log() { | |
54 | log.info(sm.getString("versionLoggerListener.serverInfo.server.version", | |
55 | ServerInfo.getServerInfo())); | |
56 | log.info(sm.getString("versionLoggerListener.serverInfo.server.built", | |
57 | ServerInfo.getServerBuilt())); | |
58 | log.info(sm.getString("versionLoggerListener.serverInfo.server.number", | |
59 | ServerInfo.getServerNumber())); | |
60 | log.info(sm.getString("versionLoggerListener.serverInfo.os.name", | |
61 | System.getProperty("os.name"))); | |
62 | log.info(sm.getString("versionLoggerListener.serverInfo.os.version", | |
63 | System.getProperty("os.version"))); | |
64 | log.info(sm.getString("versionLoggerListener.serverInfo.os.arch", | |
65 | System.getProperty("os.arch"))); | |
66 | log.info(sm.getString("versionLoggerListener.serverInfo.vm.version", | |
67 | System.getProperty("java.runtime.version"))); | |
68 | log.info(sm.getString("versionLoggerListener.serverInfo.vm.vendor", | |
69 | System.getProperty("java.vm.vendor"))); | |
70 | } | |
71 | } |
153 | 153 | /* Process Resource annotation. |
154 | 154 | * Ref JSR 250 |
155 | 155 | */ |
156 | { | |
157 | Resource annotation = classClass.getAnnotation(Resource.class); | |
158 | if (annotation != null) { | |
159 | addResource(context, annotation); | |
160 | } | |
156 | Resource resourceAnnotation = classClass.getAnnotation(Resource.class); | |
157 | if (resourceAnnotation != null) { | |
158 | addResource(context, resourceAnnotation); | |
161 | 159 | } |
162 | 160 | /* Process Resources annotation. |
163 | 161 | * Ref JSR 250 |
164 | 162 | */ |
165 | { | |
166 | Resources annotation = classClass.getAnnotation(Resources.class); | |
167 | if (annotation != null && annotation.value() != null) { | |
168 | for (Resource resource : annotation.value()) { | |
169 | addResource(context, resource); | |
170 | } | |
163 | Resources resourcesAnnotation = classClass.getAnnotation(Resources.class); | |
164 | if (resourcesAnnotation != null && resourcesAnnotation.value() != null) { | |
165 | for (Resource resource : resourcesAnnotation.value()) { | |
166 | addResource(context, resource); | |
171 | 167 | } |
172 | 168 | } |
173 | 169 | /* Process EJB annotation. |
243 | 239 | * Ref JSR 250, equivalent to the security-role element in |
244 | 240 | * the deployment descriptor |
245 | 241 | */ |
246 | { | |
247 | DeclareRoles annotation = classClass | |
248 | .getAnnotation(DeclareRoles.class); | |
249 | if (annotation != null && annotation.value() != null) { | |
250 | for (String role : annotation.value()) { | |
251 | context.addSecurityRole(role); | |
252 | } | |
242 | DeclareRoles declareRolesAnnotation = classClass | |
243 | .getAnnotation(DeclareRoles.class); | |
244 | if (declareRolesAnnotation != null && declareRolesAnnotation.value() != null) { | |
245 | for (String role : declareRolesAnnotation.value()) { | |
246 | context.addSecurityRole(role); | |
253 | 247 | } |
254 | 248 | } |
255 | 249 | } |
68 | 68 | * resource could not be loaded for any reason. |
69 | 69 | */ |
70 | 70 | public CharsetMapper(String name) { |
71 | try { | |
72 | InputStream stream = | |
73 | this.getClass().getResourceAsStream(name); | |
71 | try (InputStream stream = this.getClass().getResourceAsStream(name)) { | |
74 | 72 | map.load(stream); |
75 | stream.close(); | |
76 | 73 | } catch (Throwable t) { |
77 | 74 | ExceptionUtils.handleThrowable(t); |
78 | 75 | throw new IllegalArgumentException(t.toString()); |
0 | /* | |
1 | * Licensed to the Apache Software Foundation (ASF) under one or more | |
2 | * contributor license agreements. See the NOTICE file distributed with | |
3 | * this work for additional information regarding copyright ownership. | |
4 | * The ASF licenses this file to You under the Apache License, Version 2.0 | |
5 | * (the "License"); you may not use this file except in compliance with | |
6 | * the License. You may obtain a copy of the License at | |
7 | * | |
8 | * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
15 | */ | |
16 | package org.apache.catalina.util; | |
17 | ||
18 | import java.security.MessageDigest; | |
19 | import java.security.NoSuchAlgorithmException; | |
20 | import java.util.HashMap; | |
21 | import java.util.Map; | |
22 | import java.util.Queue; | |
23 | import java.util.concurrent.ConcurrentLinkedQueue; | |
24 | ||
25 | /** | |
26 | * A thread safe wrapper around {@link MessageDigest} that does not make use | |
27 | * of ThreadLocal and - broadly - only creates enough MessageDigest objects | |
28 | * to satisfy the concurrency requirements. | |
29 | */ | |
30 | public class ConcurrentMessageDigest { | |
31 | ||
32 | private static final String MD5 = "MD5"; | |
33 | ||
34 | private static final Map<String,Queue<MessageDigest>> queues = | |
35 | new HashMap<>(); | |
36 | ||
37 | ||
38 | private ConcurrentMessageDigest() { | |
39 | // Hide default constructor for this utility class | |
40 | } | |
41 | ||
42 | static { | |
43 | try { | |
44 | // Init commonly used algorithms | |
45 | init(MD5); | |
46 | } catch (NoSuchAlgorithmException e) { | |
47 | throw new IllegalArgumentException(e); | |
48 | } | |
49 | } | |
50 | ||
51 | public static byte[] digestMD5(byte[] input) { | |
52 | return digest(MD5, input); | |
53 | } | |
54 | ||
55 | public static byte[] digest(String algorithm, byte[] input) { | |
56 | ||
57 | Queue<MessageDigest> queue = queues.get(algorithm); | |
58 | if (queue == null) { | |
59 | throw new IllegalStateException("Must call init() first"); | |
60 | } | |
61 | ||
62 | MessageDigest md = queue.poll(); | |
63 | if (md == null) { | |
64 | try { | |
65 | md = MessageDigest.getInstance(algorithm); | |
66 | } catch (NoSuchAlgorithmException e) { | |
67 | // Ignore. Impossible if init() has been successfully called | |
68 | // first. | |
69 | throw new IllegalStateException("Must call init() first"); | |
70 | } | |
71 | } | |
72 | ||
73 | byte[] result = md.digest(input); | |
74 | ||
75 | queue.add(md); | |
76 | ||
77 | return result; | |
78 | } | |
79 | ||
80 | ||
81 | /** | |
82 | * Ensures that {@link #digest(String, byte[])} will support the specified | |
83 | * algorithm. This method <b>must</b> be called and return successfully | |
84 | * before using {@link #digest(String, byte[])}. | |
85 | * | |
86 | * @param algorithm The message digest algorithm to be supported | |
87 | * | |
88 | * @throws NoSuchAlgorithmException If the algorithm is not supported by the | |
89 | * JVM | |
90 | */ | |
91 | public static void init(String algorithm) throws NoSuchAlgorithmException { | |
92 | synchronized (queues) { | |
93 | if (!queues.containsKey(algorithm)) { | |
94 | MessageDigest md = MessageDigest.getInstance(algorithm); | |
95 | Queue<MessageDigest> queue = new ConcurrentLinkedQueue<>(); | |
96 | queue.add(md); | |
97 | queues.put(algorithm, queue); | |
98 | } | |
99 | } | |
100 | } | |
101 | } |
168 | 168 | * @param jarFile The system JAR whose manifest to add |
169 | 169 | */ |
170 | 170 | public static void addSystemResource(File jarFile) throws IOException { |
171 | Manifest manifest = getManifest(new FileInputStream(jarFile)); | |
172 | if (manifest != null) { | |
173 | ManifestResource mre | |
174 | = new ManifestResource(jarFile.getAbsolutePath(), | |
175 | manifest, | |
176 | ManifestResource.SYSTEM); | |
177 | containerManifestResources.add(mre); | |
171 | try (InputStream is = new FileInputStream(jarFile)) { | |
172 | Manifest manifest = getManifest(is); | |
173 | if (manifest != null) { | |
174 | ManifestResource mre = new ManifestResource(jarFile.getAbsolutePath(), manifest, | |
175 | ManifestResource.SYSTEM); | |
176 | containerManifestResources.add(mre); | |
177 | } | |
178 | 178 | } |
179 | 179 | } |
180 | 180 |
0 | /* | |
1 | * Licensed to the Apache Software Foundation (ASF) under one or more | |
2 | * contributor license agreements. See the NOTICE file distributed with | |
3 | * this work for additional information regarding copyright ownership. | |
4 | * The ASF licenses this file to You under the Apache License, Version 2.0 | |
5 | * (the "License"); you may not use this file except in compliance with | |
6 | * the License. You may obtain a copy of the License at | |
7 | * | |
8 | * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
15 | */ | |
16 | package org.apache.catalina.util; | |
17 | ||
18 | /** | |
19 | * Encode an MD5 digest into a String. | |
20 | * <p> | |
21 | * The 128 bit MD5 hash is converted into a 32 character long String. | |
22 | * Each character of the String is the hexadecimal representation of 4 bits | |
23 | * of the digest. | |
24 | * | |
25 | * @author Remy Maucherat | |
26 | */ | |
27 | public final class MD5Encoder { | |
28 | ||
29 | ||
30 | private MD5Encoder() { | |
31 | // Hide default constructor for utility class | |
32 | } | |
33 | ||
34 | ||
35 | private static final char[] hexadecimal = {'0', '1', '2', '3', '4', '5', | |
36 | '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; | |
37 | ||
38 | ||
39 | /** | |
40 | * Encodes the 128 bit (16 bytes) MD5 into a 32 character String. | |
41 | * | |
42 | * @param binaryData Array containing the digest | |
43 | * | |
44 | * @return Encoded MD5, or null if encoding failed | |
45 | */ | |
46 | public static String encode(byte[] binaryData) { | |
47 | ||
48 | if (binaryData.length != 16) | |
49 | return null; | |
50 | ||
51 | char[] buffer = new char[32]; | |
52 | ||
53 | for (int i=0; i<16; i++) { | |
54 | int low = binaryData[i] & 0x0f; | |
55 | int high = (binaryData[i] & 0xf0) >> 4; | |
56 | buffer[i*2] = hexadecimal[high]; | |
57 | buffer[i*2 + 1] = hexadecimal[low]; | |
58 | } | |
59 | ||
60 | return new String(buffer); | |
61 | } | |
62 | } | |
63 |
57 | 57 | String built = null; |
58 | 58 | String number = null; |
59 | 59 | |
60 | try { | |
61 | InputStream is = ServerInfo.class.getResourceAsStream | |
62 | ("/org/apache/catalina/util/ServerInfo.properties"); | |
63 | Properties props = new Properties(); | |
60 | Properties props = new Properties(); | |
61 | try (InputStream is = ServerInfo.class.getResourceAsStream | |
62 | ("/org/apache/catalina/util/ServerInfo.properties")) { | |
64 | 63 | props.load(is); |
65 | is.close(); | |
66 | 64 | info = props.getProperty("server.info"); |
67 | 65 | built = props.getProperty("server.built"); |
68 | 66 | number = props.getProperty("server.number"); |
338 | 338 | StringBuilder buffer = new StringBuilder(); |
339 | 339 | boolean first = true; |
340 | 340 | while (iter.hasNext()) { |
341 | if (!first) { | |
341 | if (first) { | |
342 | first = false; | |
343 | } else { | |
342 | 344 | buffer.append(","); |
343 | 345 | } |
344 | 346 | buffer.append(iter.next()); |
244 | 244 | int numStuckThreads = stuckCount.decrementAndGet(); |
245 | 245 | notifyStuckThreadCompleted(completedStuckThread, numStuckThreads); |
246 | 246 | } |
247 | } | |
248 | ||
249 | public int getStuckThreadCount() { | |
250 | return stuckCount.get(); | |
247 | 251 | } |
248 | 252 | |
249 | 253 | public long[] getStuckThreadIds() { |
516 | 516 | type="java.lang.String" |
517 | 517 | writeable="false"/> |
518 | 518 | |
519 | <attribute name="stuckThreadCount" | |
520 | description="Count of the threads currently considered stuck" | |
521 | type="int" | |
522 | writeable="false"/> | |
523 | ||
519 | 524 | <attribute name="stuckThreadIds" |
520 | 525 | description="IDs of the threads currently considered stuck. Each ID can then be used with the Threading MBean to retrieve data about it." |
521 | 526 | type="long[]" |
74 | 74 | } |
75 | 75 | } |
76 | 76 | |
77 | protected void configureProcessor(AbstractAjpProcessor<S> processor) { | |
78 | processor.setAdapter(getAdapter()); | |
79 | processor.setTomcatAuthentication(getTomcatAuthentication()); | |
80 | processor.setRequiredSecret(requiredSecret); | |
81 | processor.setKeepAliveTimeout(getKeepAliveTimeout()); | |
82 | processor.setClientCertProvider(getClientCertProvider()); | |
83 | } | |
84 | ||
77 | 85 | protected abstract static class AbstractAjpConnectionHandler<S,P extends AbstractAjpProcessor<S>> |
78 | 86 | extends AbstractConnectionHandler<S, P> { |
79 | 87 |
142 | 142 | @Override |
143 | 143 | protected AjpAprProcessor createProcessor() { |
144 | 144 | AjpAprProcessor processor = new AjpAprProcessor(proto.packetSize, (AprEndpoint)proto.endpoint); |
145 | processor.setAdapter(proto.getAdapter()); | |
146 | processor.setTomcatAuthentication(proto.tomcatAuthentication); | |
147 | processor.setRequiredSecret(proto.requiredSecret); | |
148 | processor.setClientCertProvider(proto.getClientCertProvider()); | |
145 | proto.configureProcessor(processor); | |
149 | 146 | register(processor); |
150 | 147 | return processor; |
151 | 148 | } |
143 | 143 | @Override |
144 | 144 | protected AjpNio2Processor createProcessor() { |
145 | 145 | AjpNio2Processor processor = new AjpNio2Processor(proto.packetSize, (Nio2Endpoint) proto.endpoint); |
146 | processor.setAdapter(proto.getAdapter()); | |
147 | processor.setTomcatAuthentication(proto.tomcatAuthentication); | |
148 | processor.setRequiredSecret(proto.requiredSecret); | |
149 | processor.setClientCertProvider(proto.getClientCertProvider()); | |
146 | proto.configureProcessor(processor); | |
150 | 147 | register(processor); |
151 | 148 | return processor; |
152 | 149 | } |
172 | 172 | @Override |
173 | 173 | protected AjpNioProcessor createProcessor() { |
174 | 174 | AjpNioProcessor processor = new AjpNioProcessor(proto.packetSize, (NioEndpoint)proto.endpoint); |
175 | processor.setAdapter(proto.getAdapter()); | |
176 | processor.setTomcatAuthentication(proto.tomcatAuthentication); | |
177 | processor.setRequiredSecret(proto.requiredSecret); | |
178 | processor.setClientCertProvider(proto.getClientCertProvider()); | |
175 | proto.configureProcessor(processor); | |
179 | 176 | register(processor); |
180 | 177 | return processor; |
181 | 178 | } |
131 | 131 | @Override |
132 | 132 | protected AjpProcessor createProcessor() { |
133 | 133 | AjpProcessor processor = new AjpProcessor(proto.packetSize, (JIoEndpoint)proto.endpoint); |
134 | processor.setAdapter(proto.getAdapter()); | |
135 | processor.setTomcatAuthentication(proto.tomcatAuthentication); | |
136 | processor.setRequiredSecret(proto.requiredSecret); | |
137 | processor.setKeepAliveTimeout(proto.getKeepAliveTimeout()); | |
138 | processor.setClientCertProvider(proto.getClientCertProvider()); | |
134 | proto.configureProcessor(processor); | |
139 | 135 | register(processor); |
140 | 136 | return processor; |
141 | 137 | } |
217 | 217 | } |
218 | 218 | } |
219 | 219 | |
220 | ||
221 | // ------------------------------------------------------------- Common code | |
222 | ||
223 | // Common configuration required for all new HTTP11 processors | |
224 | protected void configureProcessor(AbstractHttp11Processor<S> processor) { | |
225 | processor.setAdapter(getAdapter()); | |
226 | processor.setMaxKeepAliveRequests(getMaxKeepAliveRequests()); | |
227 | processor.setKeepAliveTimeout(getKeepAliveTimeout()); | |
228 | processor.setConnectionUploadTimeout(getConnectionUploadTimeout()); | |
229 | processor.setDisableUploadTimeout(getDisableUploadTimeout()); | |
230 | processor.setCompressionMinSize(getCompressionMinSize()); | |
231 | processor.setCompression(getCompression()); | |
232 | processor.setNoCompressionUserAgents(getNoCompressionUserAgents()); | |
233 | processor.setCompressableMimeTypes(getCompressableMimeTypes()); | |
234 | processor.setRestrictedUserAgents(getRestrictedUserAgents()); | |
235 | processor.setSocketBuffer(getSocketBuffer()); | |
236 | processor.setMaxSavePostSize(getMaxSavePostSize()); | |
237 | processor.setServer(getServer()); | |
238 | } | |
220 | 239 | } |
418 | 418 | } |
419 | 419 | case REQ_SSL_CERTIFICATE: { |
420 | 420 | if (endpoint.isSSLEnabled() && (socketRef != 0)) { |
421 | boolean force = ((Boolean) param).booleanValue(); | |
422 | if (force) { | |
423 | /* Forced triggers a handshake so consume and buffer the | |
424 | * request body, so that it does not interfere with the | |
425 | * client's handshake messages | |
426 | */ | |
427 | InputFilter[] inputFilters = inputBuffer.getFilters(); | |
428 | ((BufferedInputFilter) inputFilters[Constants.BUFFERED_FILTER]) | |
429 | .setLimit(maxSavePostSize); | |
430 | inputBuffer.addActiveFilter(inputFilters[Constants.BUFFERED_FILTER]); | |
431 | } | |
421 | // Consume and buffer the request body, so that it does not | |
422 | // interfere with the client's handshake messages | |
423 | InputFilter[] inputFilters = inputBuffer.getFilters(); | |
424 | ((BufferedInputFilter) inputFilters[Constants.BUFFERED_FILTER]).setLimit(maxSavePostSize); | |
425 | inputBuffer.addActiveFilter(inputFilters[Constants.BUFFERED_FILTER]); | |
432 | 426 | try { |
433 | if (force) { | |
434 | // Configure connection to require a certificate | |
435 | SSLSocket.setVerify(socketRef, SSL.SSL_CVERIFY_REQUIRE, | |
436 | ((AprEndpoint)endpoint).getSSLVerifyDepth()); | |
437 | } | |
438 | if (!force || SSLSocket.renegotiate(socketRef) == 0) { | |
439 | // Only look for certs if not forcing a renegotiation or | |
440 | // if we know renegotiation worked. | |
427 | // Configure connection to require a certificate | |
428 | SSLSocket.setVerify(socketRef, SSL.SSL_CVERIFY_REQUIRE, | |
429 | ((AprEndpoint)endpoint).getSSLVerifyDepth()); | |
430 | // Renegotiate certificates | |
431 | if (SSLSocket.renegotiate(socketRef) == 0) { | |
432 | // Don't look for certs unless we know renegotiation worked. | |
441 | 433 | // Get client certificate and the certificate chain if present |
442 | 434 | // certLength == -1 indicates an error |
443 | 435 | int certLength = SSLSocket.getInfoI(socketRef,SSL.SSL_INFO_CLIENT_CERT_CHAIN); |
320 | 320 | proto.getMaxHttpHeaderSize(), (AprEndpoint)proto.endpoint, |
321 | 321 | proto.getMaxTrailerSize(), proto.getMaxExtensionSize(), |
322 | 322 | proto.getMaxSwallowSize()); |
323 | processor.setAdapter(proto.getAdapter()); | |
324 | processor.setMaxKeepAliveRequests(proto.getMaxKeepAliveRequests()); | |
325 | processor.setKeepAliveTimeout(proto.getKeepAliveTimeout()); | |
326 | processor.setConnectionUploadTimeout( | |
327 | proto.getConnectionUploadTimeout()); | |
328 | processor.setDisableUploadTimeout(proto.getDisableUploadTimeout()); | |
329 | processor.setCompressionMinSize(proto.getCompressionMinSize()); | |
330 | processor.setCompression(proto.getCompression()); | |
331 | processor.setNoCompressionUserAgents(proto.getNoCompressionUserAgents()); | |
332 | processor.setCompressableMimeTypes(proto.getCompressableMimeTypes()); | |
333 | processor.setRestrictedUserAgents(proto.getRestrictedUserAgents()); | |
334 | processor.setSocketBuffer(proto.getSocketBuffer()); | |
335 | processor.setMaxSavePostSize(proto.getMaxSavePostSize()); | |
336 | processor.setServer(proto.getServer()); | |
323 | proto.configureProcessor(processor); | |
324 | // APR specific configuration | |
337 | 325 | processor.setClientCertProvider(proto.getClientCertProvider()); |
338 | 326 | register(processor); |
339 | 327 | return processor; |
464 | 464 | } |
465 | 465 | case REQ_SSL_CERTIFICATE: { |
466 | 466 | if (sslSupport != null && socketWrapper.getSocket() != null) { |
467 | boolean force = ((Boolean) param).booleanValue(); | |
468 | if (force) { | |
469 | /* Forced triggers a handshake so consume and buffer the | |
470 | * request body, so that it does not interfere with the | |
471 | * client's handshake messages | |
472 | */ | |
473 | InputFilter[] inputFilters = inputBuffer.getFilters(); | |
474 | ((BufferedInputFilter) inputFilters[Constants.BUFFERED_FILTER]) | |
475 | .setLimit(maxSavePostSize); | |
476 | inputBuffer.addActiveFilter(inputFilters[Constants.BUFFERED_FILTER]); | |
477 | } | |
467 | /* | |
468 | * Consume and buffer the request body, so that it does not | |
469 | * interfere with the client's handshake messages | |
470 | */ | |
471 | InputFilter[] inputFilters = inputBuffer.getFilters(); | |
472 | ((BufferedInputFilter) inputFilters[Constants.BUFFERED_FILTER]) | |
473 | .setLimit(maxSavePostSize); | |
474 | inputBuffer.addActiveFilter | |
475 | (inputFilters[Constants.BUFFERED_FILTER]); | |
478 | 476 | SecureNio2Channel sslChannel = (SecureNio2Channel) socketWrapper.getSocket(); |
479 | 477 | SSLEngine engine = sslChannel.getSslEngine(); |
480 | if (!engine.getNeedClientAuth() && force) { | |
478 | if (!engine.getNeedClientAuth()) { | |
481 | 479 | // Need to re-negotiate SSL connection |
482 | 480 | engine.setNeedClientAuth(true); |
483 | 481 | try { |
494 | 492 | // use force=false since re-negotiation is handled above |
495 | 493 | // (and it is a NO-OP for NIO anyway) |
496 | 494 | Object sslO = sslSupport.getPeerCertificateChain(false); |
497 | if (sslO != null) { | |
498 | request.setAttribute(SSLSupport.CERTIFICATE_KEY, sslO); | |
495 | if( sslO != null) { | |
496 | request.setAttribute | |
497 | (SSLSupport.CERTIFICATE_KEY, sslO); | |
499 | 498 | } |
500 | 499 | } catch (Exception e) { |
501 | 500 | log.warn(sm.getString("http11processor.socket.ssl"), e); |
249 | 249 | proto.getMaxHttpHeaderSize(), (Nio2Endpoint) proto.endpoint, |
250 | 250 | proto.getMaxTrailerSize(), proto.getMaxExtensionSize(), |
251 | 251 | proto.getMaxSwallowSize()); |
252 | processor.setAdapter(proto.getAdapter()); | |
253 | processor.setMaxKeepAliveRequests(proto.getMaxKeepAliveRequests()); | |
254 | processor.setKeepAliveTimeout(proto.getKeepAliveTimeout()); | |
255 | processor.setConnectionUploadTimeout( | |
256 | proto.getConnectionUploadTimeout()); | |
257 | processor.setDisableUploadTimeout(proto.getDisableUploadTimeout()); | |
258 | processor.setCompressionMinSize(proto.getCompressionMinSize()); | |
259 | processor.setCompression(proto.getCompression()); | |
260 | processor.setNoCompressionUserAgents(proto.getNoCompressionUserAgents()); | |
261 | processor.setCompressableMimeTypes(proto.getCompressableMimeTypes()); | |
262 | processor.setRestrictedUserAgents(proto.getRestrictedUserAgents()); | |
263 | processor.setSocketBuffer(proto.getSocketBuffer()); | |
264 | processor.setMaxSavePostSize(proto.getMaxSavePostSize()); | |
265 | processor.setServer(proto.getServer()); | |
252 | proto.configureProcessor(processor); | |
266 | 253 | register(processor); |
267 | 254 | return processor; |
268 | 255 | } |
433 | 433 | } |
434 | 434 | case REQ_SSL_CERTIFICATE: { |
435 | 435 | if (sslSupport != null) { |
436 | boolean force = ((Boolean) param).booleanValue(); | |
437 | if (force) { | |
438 | /* Forced triggers a handshake so consume and buffer the | |
439 | * request body, so that it does not interfere with the | |
440 | * client's handshake messages | |
441 | */ | |
442 | InputFilter[] inputFilters = inputBuffer.getFilters(); | |
443 | ((BufferedInputFilter) inputFilters[Constants.BUFFERED_FILTER]) | |
444 | .setLimit(maxSavePostSize); | |
445 | inputBuffer.addActiveFilter(inputFilters[Constants.BUFFERED_FILTER]); | |
446 | } | |
436 | /* | |
437 | * Consume and buffer the request body, so that it does not | |
438 | * interfere with the client's handshake messages | |
439 | */ | |
440 | InputFilter[] inputFilters = inputBuffer.getFilters(); | |
441 | ((BufferedInputFilter) inputFilters[Constants.BUFFERED_FILTER]) | |
442 | .setLimit(maxSavePostSize); | |
443 | inputBuffer.addActiveFilter | |
444 | (inputFilters[Constants.BUFFERED_FILTER]); | |
447 | 445 | SecureNioChannel sslChannel = (SecureNioChannel) socketWrapper.getSocket(); |
448 | 446 | SSLEngine engine = sslChannel.getSslEngine(); |
449 | if (!engine.getNeedClientAuth() && force) { | |
447 | if (!engine.getNeedClientAuth()) { | |
450 | 448 | // Need to re-negotiate SSL connection |
451 | 449 | engine.setNeedClientAuth(true); |
452 | 450 | try { |
463 | 461 | // use force=false since re-negotiation is handled above |
464 | 462 | // (and it is a NO-OP for NIO anyway) |
465 | 463 | Object sslO = sslSupport.getPeerCertificateChain(false); |
466 | if (sslO != null) { | |
467 | request.setAttribute(SSLSupport.CERTIFICATE_KEY, sslO); | |
464 | if( sslO != null) { | |
465 | request.setAttribute | |
466 | (SSLSupport.CERTIFICATE_KEY, sslO); | |
468 | 467 | } |
469 | 468 | } catch (Exception e) { |
470 | 469 | log.warn(sm.getString("http11processor.socket.ssl"), e); |
281 | 281 | proto.getMaxHttpHeaderSize(), (NioEndpoint)proto.endpoint, |
282 | 282 | proto.getMaxTrailerSize(), proto.getMaxExtensionSize(), |
283 | 283 | proto.getMaxSwallowSize()); |
284 | processor.setAdapter(proto.getAdapter()); | |
285 | processor.setMaxKeepAliveRequests(proto.getMaxKeepAliveRequests()); | |
286 | processor.setKeepAliveTimeout(proto.getKeepAliveTimeout()); | |
287 | processor.setConnectionUploadTimeout( | |
288 | proto.getConnectionUploadTimeout()); | |
289 | processor.setDisableUploadTimeout(proto.getDisableUploadTimeout()); | |
290 | processor.setCompressionMinSize(proto.getCompressionMinSize()); | |
291 | processor.setCompression(proto.getCompression()); | |
292 | processor.setNoCompressionUserAgents(proto.getNoCompressionUserAgents()); | |
293 | processor.setCompressableMimeTypes(proto.getCompressableMimeTypes()); | |
294 | processor.setRestrictedUserAgents(proto.getRestrictedUserAgents()); | |
295 | processor.setSocketBuffer(proto.getSocketBuffer()); | |
296 | processor.setMaxSavePostSize(proto.getMaxSavePostSize()); | |
297 | processor.setServer(proto.getServer()); | |
284 | proto.configureProcessor(processor); | |
298 | 285 | register(processor); |
299 | 286 | return processor; |
300 | 287 | } |
349 | 349 | } |
350 | 350 | case REQ_SSL_CERTIFICATE: { |
351 | 351 | if (sslSupport != null) { |
352 | boolean force = ((Boolean) param).booleanValue(); | |
353 | if (force) { | |
354 | /* Forced triggers a handshake so consume and buffer the | |
355 | * request body, so that it does not interfere with the | |
356 | * client's handshake messages | |
357 | */ | |
358 | InputFilter[] inputFilters = inputBuffer.getFilters(); | |
359 | ((BufferedInputFilter) inputFilters[Constants.BUFFERED_FILTER]) | |
360 | .setLimit(maxSavePostSize); | |
361 | inputBuffer.addActiveFilter(inputFilters[Constants.BUFFERED_FILTER]); | |
362 | } | |
352 | /* | |
353 | * Consume and buffer the request body, so that it does not | |
354 | * interfere with the client's handshake messages | |
355 | */ | |
356 | InputFilter[] inputFilters = inputBuffer.getFilters(); | |
357 | ((BufferedInputFilter) inputFilters[Constants.BUFFERED_FILTER]) | |
358 | .setLimit(maxSavePostSize); | |
359 | inputBuffer.addActiveFilter | |
360 | (inputFilters[Constants.BUFFERED_FILTER]); | |
363 | 361 | try { |
364 | Object sslO = sslSupport.getPeerCertificateChain(force); | |
362 | Object sslO = sslSupport.getPeerCertificateChain(true); | |
365 | 363 | if( sslO != null) { |
366 | 364 | request.setAttribute |
367 | 365 | (SSLSupport.CERTIFICATE_KEY, sslO); |
187 | 187 | proto.getMaxHttpHeaderSize(), (JIoEndpoint)proto.endpoint, |
188 | 188 | proto.getMaxTrailerSize(),proto.getMaxExtensionSize(), |
189 | 189 | proto.getMaxSwallowSize()); |
190 | processor.setAdapter(proto.getAdapter()); | |
191 | processor.setMaxKeepAliveRequests(proto.getMaxKeepAliveRequests()); | |
192 | processor.setKeepAliveTimeout(proto.getKeepAliveTimeout()); | |
193 | processor.setConnectionUploadTimeout( | |
194 | proto.getConnectionUploadTimeout()); | |
195 | processor.setDisableUploadTimeout(proto.getDisableUploadTimeout()); | |
196 | processor.setCompressionMinSize(proto.getCompressionMinSize()); | |
197 | processor.setCompression(proto.getCompression()); | |
198 | processor.setNoCompressionUserAgents(proto.getNoCompressionUserAgents()); | |
199 | processor.setCompressableMimeTypes(proto.getCompressableMimeTypes()); | |
200 | processor.setRestrictedUserAgents(proto.getRestrictedUserAgents()); | |
201 | processor.setSocketBuffer(proto.getSocketBuffer()); | |
202 | processor.setMaxSavePostSize(proto.getMaxSavePostSize()); | |
203 | processor.setServer(proto.getServer()); | |
204 | processor.setDisableKeepAlivePercentage( | |
205 | proto.getDisableKeepAlivePercentage()); | |
190 | proto.configureProcessor(processor); | |
191 | // BIO specific configuration | |
192 | processor.setDisableKeepAlivePercentage(proto.getDisableKeepAlivePercentage()); | |
206 | 193 | register(processor); |
207 | 194 | return processor; |
208 | 195 | } |
248 | 248 | private static final Long ZERO = Long.valueOf(0); |
249 | 249 | |
250 | 250 | public static final Number add(final Object obj0, final Object obj1) { |
251 | if (obj0 == null && obj1 == null) { | |
251 | final ELArithmetic delegate = findDelegate(obj0, obj1); | |
252 | if (delegate == null) { | |
252 | 253 | return Long.valueOf(0); |
253 | 254 | } |
254 | ||
255 | final ELArithmetic delegate; | |
256 | if (BIGDECIMAL.matches(obj0, obj1)) | |
257 | delegate = BIGDECIMAL; | |
258 | else if (DOUBLE.matches(obj0, obj1)) { | |
259 | if (BIGINTEGER.matches(obj0, obj1)) | |
260 | delegate = BIGDECIMAL; | |
261 | else | |
262 | delegate = DOUBLE; | |
263 | } else if (BIGINTEGER.matches(obj0, obj1)) | |
264 | delegate = BIGINTEGER; | |
265 | else | |
266 | delegate = LONG; | |
267 | 255 | |
268 | 256 | Number num0 = delegate.coerce(obj0); |
269 | 257 | Number num1 = delegate.coerce(obj1); |
293 | 281 | } |
294 | 282 | |
295 | 283 | public static final Number subtract(final Object obj0, final Object obj1) { |
296 | if (obj0 == null && obj1 == null) { | |
284 | final ELArithmetic delegate = findDelegate(obj0, obj1); | |
285 | if (delegate == null) { | |
297 | 286 | return Long.valueOf(0); |
298 | 287 | } |
299 | ||
300 | final ELArithmetic delegate; | |
301 | if (BIGDECIMAL.matches(obj0, obj1)) | |
302 | delegate = BIGDECIMAL; | |
303 | else if (DOUBLE.matches(obj0, obj1)) { | |
304 | if (BIGINTEGER.matches(obj0, obj1)) | |
305 | delegate = BIGDECIMAL; | |
306 | else | |
307 | delegate = DOUBLE; | |
308 | } else if (BIGINTEGER.matches(obj0, obj1)) | |
309 | delegate = BIGINTEGER; | |
310 | else | |
311 | delegate = LONG; | |
312 | 288 | |
313 | 289 | Number num0 = delegate.coerce(obj0); |
314 | 290 | Number num1 = delegate.coerce(obj1); |
336 | 312 | } |
337 | 313 | |
338 | 314 | public static final Number multiply(final Object obj0, final Object obj1) { |
339 | if (obj0 == null && obj1 == null) { | |
315 | final ELArithmetic delegate = findDelegate(obj0, obj1); | |
316 | if (delegate == null) { | |
340 | 317 | return Long.valueOf(0); |
341 | 318 | } |
342 | ||
343 | final ELArithmetic delegate; | |
344 | if (BIGDECIMAL.matches(obj0, obj1)) | |
345 | delegate = BIGDECIMAL; | |
346 | else if (DOUBLE.matches(obj0, obj1)) { | |
347 | if (BIGINTEGER.matches(obj0, obj1)) | |
348 | delegate = BIGDECIMAL; | |
349 | else | |
350 | delegate = DOUBLE; | |
351 | } else if (BIGINTEGER.matches(obj0, obj1)) | |
352 | delegate = BIGINTEGER; | |
353 | else | |
354 | delegate = LONG; | |
355 | 319 | |
356 | 320 | Number num0 = delegate.coerce(obj0); |
357 | 321 | Number num1 = delegate.coerce(obj1); |
358 | 322 | |
359 | 323 | return delegate.multiply(num0, num1); |
324 | } | |
325 | ||
326 | private static ELArithmetic findDelegate(final Object obj0, final Object obj1) { | |
327 | if (obj0 == null && obj1 == null) { | |
328 | return null; | |
329 | } | |
330 | ||
331 | if (BIGDECIMAL.matches(obj0, obj1)) { | |
332 | return BIGDECIMAL; | |
333 | } else if (DOUBLE.matches(obj0, obj1)) { | |
334 | if (BIGINTEGER.matches(obj0, obj1)) { | |
335 | return BIGDECIMAL; | |
336 | } else { | |
337 | return DOUBLE; | |
338 | } | |
339 | } else if (BIGINTEGER.matches(obj0, obj1)) { | |
340 | return BIGINTEGER; | |
341 | } else { | |
342 | return LONG; | |
343 | } | |
360 | 344 | } |
361 | 345 | |
362 | 346 | public static final boolean isNumber(final Object obj) { |
36 | 36 | private final Iterator<Object> iterator; |
37 | 37 | |
38 | 38 | |
39 | public Stream(Iterator<Object > iterator) { | |
39 | public Stream(Iterator<Object> iterator) { | |
40 | 40 | this.iterator = iterator; |
41 | 41 | } |
42 | 42 | |
467 | 467 | |
468 | 468 | |
469 | 469 | private static class LambdaExpressionComparator |
470 | implements Comparator<Object>{ | |
470 | implements Comparator<Object> { | |
471 | 471 | |
472 | 472 | private final LambdaExpression le; |
473 | 473 |
68 | 68 | * servlet definition. If present on a request, this overrides the |
69 | 69 | * value returned by <code>request.getServletPath()</code> to select |
70 | 70 | * the JSP page to be executed. |
71 | * @deprecated This will be removed in Tomcat 9.0.x onwards. It is replaced | |
72 | * by the use of the jspFile servlet initialisation parameter | |
71 | 73 | */ |
74 | @Deprecated | |
72 | 75 | public static final String JSP_FILE = |
73 | 76 | System.getProperty("org.apache.jasper.Constants.JSP_FILE", "org.apache.catalina.jsp_file"); |
74 | 77 |
502 | 502 | @Override |
503 | 503 | public boolean getMappedFile() { |
504 | 504 | return mappedFile; |
505 | } | |
506 | ||
507 | public void setMappedFile(boolean b) { | |
508 | mappedFile = b; | |
505 | 509 | } |
506 | 510 | |
507 | 511 | /** |
483 | 483 | // Assume we constructed this correctly |
484 | 484 | int entryStart = key.lastIndexOf("!/"); |
485 | 485 | String entry = key.substring(entryStart + 2); |
486 | Jar jar = JarFactory.newInstance(new URL(key.substring(4, entryStart))); | |
487 | includeLastModified = jar.getLastModified(entry); | |
486 | try (Jar jar = JarFactory.newInstance(new URL(key.substring(4, entryStart)))) { | |
487 | includeLastModified = jar.getLastModified(entry); | |
488 | } | |
488 | 489 | } else { |
489 | 490 | if (key.startsWith("jar:") || key.startsWith("file:")) { |
490 | 491 | includeUrl = new URL(key); |
225 | 225 | return false; |
226 | 226 | } |
227 | 227 | String resourceName = result.replace('.', '/') + ".class"; |
228 | InputStream is = | |
229 | classLoader.getResourceAsStream(resourceName); | |
230 | return is == null; | |
228 | try (InputStream is = | |
229 | classLoader.getResourceAsStream(resourceName)) { | |
230 | return is == null; | |
231 | } catch (IOException e) { | |
232 | // we are here, since close on is failed. That means it was not null | |
233 | return false; | |
234 | } | |
231 | 235 | } |
232 | 236 | |
233 | 237 | @Override |
275 | 275 | } |
276 | 276 | |
277 | 277 | |
278 | @SuppressWarnings("deprecation") // Use of JSP_FILE to be removed in 9.0.x | |
278 | 279 | @Override |
279 | 280 | public void service (HttpServletRequest request, |
280 | 281 | HttpServletResponse response) |
284 | 285 | String jspUri = jspFile; |
285 | 286 | |
286 | 287 | if (jspUri == null) { |
287 | // JSP specified via <jsp-file> in <servlet> declaration and supplied through | |
288 | //custom servlet container code | |
289 | jspUri = (String) request.getAttribute(Constants.JSP_FILE); | |
288 | // JSP specified via <jsp-file> in <servlet> declaration and | |
289 | // supplied through custom servlet container code | |
290 | String jspFile = (String) request.getAttribute(Constants.JSP_FILE); | |
291 | if (jspFile != null) { | |
292 | jspUri = jspFile; | |
293 | request.removeAttribute(Constants.JSP_FILE); | |
294 | } | |
290 | 295 | } |
291 | 296 | if (jspUri == null) { |
292 | 297 | /* |
455 | 455 | } |
456 | 456 | throw ex; |
457 | 457 | } catch (IOException ex) { |
458 | if(options.getDevelopment()) { | |
459 | throw handleJspException(ex); | |
458 | if (options.getDevelopment()) { | |
459 | throw new IOException(handleJspException(ex).getMessage(), ex); | |
460 | 460 | } |
461 | 461 | throw ex; |
462 | 462 | } catch (IllegalStateException ex) { |
22 | 22 | * {@link ClassFileTransformer}s. These transformers can instrument |
23 | 23 | * (or weave) the byte code of classes loaded through this class loader |
24 | 24 | * to alter their behavior. Currently only |
25 | * {@link org.apache.catalina.loader.WebappClassLoader} implements this | |
25 | * {@link org.apache.catalina.loader.WebappClassLoaderBase} implements this | |
26 | 26 | * interface. This allows web application frameworks or JPA providers |
27 | 27 | * bundled with a web application to instrument web application classes |
28 | 28 | * as necessary. |
29 | 29 | * <p> |
30 | 30 | * You should always program against the methods of this interface |
31 | 31 | * (whether using reflection or otherwise). The methods in |
32 | * {@code WebappClassLoader} are protected by the default security | |
32 | * {@code WebappClassLoaderBase} are protected by the default security | |
33 | 33 | * manager if one is in use. |
34 | 34 | * |
35 | 35 | * @since 8.0, 7.0.44 |
21 | 21 | import java.io.FileInputStream; |
22 | 22 | import java.io.FileOutputStream; |
23 | 23 | import java.io.IOException; |
24 | import java.net.MalformedURLException; | |
24 | 25 | import java.net.URL; |
25 | 26 | import java.util.ArrayList; |
26 | 27 | import java.util.List; |
52 | 53 | */ |
53 | 54 | public class SignCode extends Task { |
54 | 55 | |
56 | private static final URL SIGNING_SERVICE_URL; | |
57 | ||
55 | 58 | private static final String NS = "cod"; |
56 | 59 | |
57 | 60 | private static final MessageFactory SOAP_MSG_FACTORY; |
58 | 61 | |
59 | 62 | static { |
63 | try { | |
64 | SIGNING_SERVICE_URL = new URL("https://api.ws.symantec.com/webtrust/SigningService"); | |
65 | } catch (MalformedURLException e) { | |
66 | throw new IllegalArgumentException(e); | |
67 | } | |
60 | 68 | try { |
61 | 69 | SOAP_MSG_FACTORY = MessageFactory.newInstance(SOAPConstants.SOAP_1_1_PROTOCOL); |
62 | 70 | } catch (SOAPException e) { |
159 | 167 | requestSigningRequest.addChildElement("signingServiceName", NS); |
160 | 168 | signingServiceName.addTextNode(this.signingService); |
161 | 169 | |
170 | List<String> fileNames = getFileNames(filesToSign); | |
171 | ||
162 | 172 | SOAPElement commaDelimitedFileNames = |
163 | 173 | requestSigningRequest.addChildElement("commaDelimitedFileNames", NS); |
164 | commaDelimitedFileNames.addTextNode(getFileNames(filesToSign.size())); | |
174 | commaDelimitedFileNames.addTextNode(listToString(fileNames)); | |
165 | 175 | |
166 | 176 | SOAPElement application = |
167 | 177 | requestSigningRequest.addChildElement("application", NS); |
168 | application.addTextNode(getApplicationString(filesToSign)); | |
178 | application.addTextNode(getApplicationString(fileNames, filesToSign)); | |
169 | 179 | |
170 | 180 | // Send the message |
171 | 181 | SOAPConnectionFactory soapConnectionFactory = SOAPConnectionFactory.newInstance(); |
172 | 182 | SOAPConnection connection = soapConnectionFactory.createConnection(); |
173 | URL endpoint = new URL("https://test-api.ws.symantec.com:443/webtrust/SigningService"); | |
174 | 183 | |
175 | 184 | log("Sending siging request to server and waiting for response"); |
176 | SOAPMessage response = connection.call(message, endpoint); | |
185 | SOAPMessage response = connection.call(message, SIGNING_SERVICE_URL); | |
177 | 186 | |
178 | 187 | log("Processing response"); |
179 | 188 | SOAPElement responseBody = response.getSOAPBody(); |
203 | 212 | } |
204 | 213 | |
205 | 214 | |
215 | private String listToString(List<String> list) { | |
216 | StringBuilder sb = new StringBuilder(list.size() * 6); | |
217 | boolean doneFirst = false; | |
218 | for (String s : list) { | |
219 | if (doneFirst) { | |
220 | sb.append(','); | |
221 | } else { | |
222 | doneFirst = true; | |
223 | } | |
224 | sb.append(s); | |
225 | } | |
226 | return sb.toString(); | |
227 | } | |
228 | ||
229 | ||
206 | 230 | private void downloadSignedFiles(List<File> filesToSign, String id) |
207 | 231 | throws SOAPException, IOException { |
208 | 232 | |
228 | 252 | // Send the message |
229 | 253 | SOAPConnectionFactory soapConnectionFactory = SOAPConnectionFactory.newInstance(); |
230 | 254 | SOAPConnection connection = soapConnectionFactory.createConnection(); |
231 | URL endpoint = new URL("https://test-api.ws.symantec.com:443/webtrust/SigningService"); | |
232 | 255 | |
233 | 256 | log("Requesting signed files from server and waiting for response"); |
234 | SOAPMessage response = connection.call(message, endpoint); | |
257 | SOAPMessage response = connection.call(message, SIGNING_SERVICE_URL); | |
235 | 258 | |
236 | 259 | log("Processing response"); |
237 | 260 | SOAPElement responseBody = response.getSOAPBody(); |
289 | 312 | |
290 | 313 | /** |
291 | 314 | * Signing service requires unique files names. Since files will be returned |
292 | * in order, use dummy names that we know are unique. | |
315 | * in order, use dummy names that we know are unique but retain the file | |
316 | * extension since the signing service appears to use it to figure out what | |
317 | * to sign and how to sign it. | |
293 | 318 | */ |
294 | private static String getFileNames(int fileCount) { | |
295 | StringBuilder sb = new StringBuilder(); | |
296 | ||
297 | boolean first = true; | |
298 | ||
299 | for (int i = 0; i < fileCount; i++) { | |
300 | if (first) { | |
301 | first = false; | |
319 | private static List<String> getFileNames(List<File> filesToSign) { | |
320 | List<String> result = new ArrayList<>(filesToSign.size()); | |
321 | ||
322 | for (int i = 0; i < filesToSign.size(); i++) { | |
323 | File f = filesToSign.get(i); | |
324 | String fileName = f.getName(); | |
325 | int extIndex = fileName.lastIndexOf('.'); | |
326 | String newName; | |
327 | if (extIndex < 0) { | |
328 | newName = Integer.toString(i); | |
302 | 329 | } else { |
303 | sb.append(','); | |
304 | } | |
305 | sb.append(Integer.toString(i)); | |
306 | } | |
307 | return sb.toString(); | |
308 | } | |
330 | newName = Integer.toString(i) + fileName.substring(extIndex); | |
331 | } | |
332 | result.add(newName); | |
333 | } | |
334 | return result; | |
335 | } | |
336 | ||
309 | 337 | |
310 | 338 | /** |
311 | 339 | * Zips the files, base 64 encodes the resulting zip and then returns the |
313 | 341 | * signing server but the files that need to be signed are relatively small |
314 | 342 | * and this simpler to write. |
315 | 343 | * |
316 | * @param files Files to be signed | |
344 | * @param fileNames Modified names of files | |
345 | * @param files Files to be signed | |
317 | 346 | */ |
318 | private static String getApplicationString(List<File> files) throws IOException { | |
319 | // 10 MB should be more than enough for Tomcat | |
320 | ByteArrayOutputStream baos = new ByteArrayOutputStream(10 * 1024 * 1024); | |
347 | private static String getApplicationString(List<String> fileNames, List<File> files) | |
348 | throws IOException { | |
349 | // 16 MB should be more than enough for Tomcat | |
350 | ByteArrayOutputStream baos = new ByteArrayOutputStream(16 * 1024 * 1024); | |
321 | 351 | try (ZipOutputStream zos = new ZipOutputStream(baos)) { |
322 | 352 | byte[] buf = new byte[32 * 1024]; |
323 | 353 | for (int i = 0; i < files.size(); i++) { |
324 | 354 | try (FileInputStream fis = new FileInputStream(files.get(i))) { |
325 | ZipEntry zipEntry = new ZipEntry(Integer.toString(i)); | |
355 | ZipEntry zipEntry = new ZipEntry(fileNames.get(i)); | |
326 | 356 | zos.putNextEntry(zipEntry); |
327 | 357 | int numRead; |
328 | 358 | while ( (numRead = fis.read(buf)) >= 0) { |
1307 | 1307 | * Returns the value of the flag that controls whether or not connections |
1308 | 1308 | * being returned to the pool will checked and configured with |
1309 | 1309 | * {@link Connection#setAutoCommit(boolean) Connection.setAutoCommit(true)} |
1310 | * if the auto commit setting is <code>false</false> when the connection | |
1310 | * if the auto commit setting is {@code false} when the connection | |
1311 | 1311 | * is returned. It is <code>true</code> by default. |
1312 | 1312 | */ |
1313 | 1313 | public boolean getEnableAutoCommitOnReturn() { |
1318 | 1318 | * Sets the value of the flag that controls whether or not connections |
1319 | 1319 | * being returned to the pool will checked and configured with |
1320 | 1320 | * {@link Connection#setAutoCommit(boolean) Connection.setAutoCommit(true)} |
1321 | * if the auto commit setting is <code>false</false> when the connection | |
1321 | * if the auto commit setting is {@code false} when the connection | |
1322 | 1322 | * is returned. It is <code>true</code> by default. |
1323 | 1323 | */ |
1324 | 1324 | public void setEnableAutoCommitOnReturn(boolean enableAutoCommitOnReturn) { |
2077 | 2077 | gop.setTestWhileIdle(testWhileIdle); |
2078 | 2078 | gop.setLifo(lifo); |
2079 | 2079 | gop.setSwallowedExceptionListener(new SwallowedExceptionLogger(log)); |
2080 | gop.setEvictionPolicyClassName(evictionPolicyClassName); | |
2080 | 2081 | factory.setPool(gop); |
2081 | 2082 | connectionPool = gop; |
2082 | 2083 | } |
161 | 161 | |
162 | 162 | /** |
163 | 163 | * If my underlying {@link Connection} is not a |
164 | * <tt>DelegatingConnection</tt>, returns it, | |
164 | * {@code DelegatingConnection}, returns it, | |
165 | 165 | * otherwise recursively invokes this method on |
166 | 166 | * my delegate. |
167 | 167 | * <p> |
168 | 168 | * Hence this method will return the first |
169 | * delegate that is not a <tt>DelegatingConnection</tt>, | |
170 | * or <tt>null</tt> when no non-<tt>DelegatingConnection</tt> | |
169 | * delegate that is not a {@code DelegatingConnection}, | |
170 | * or {@code null} when no non-{@code DelegatingConnection} | |
171 | 171 | * delegate can be found by traversing this chain. |
172 | 172 | * <p> |
173 | 173 | * This method is useful when you may have nested |
174 | * <tt>DelegatingConnection</tt>s, and you want to make | |
174 | * {@code DelegatingConnection}s, and you want to make | |
175 | 175 | * sure to obtain a "genuine" {@link Connection}. |
176 | 176 | */ |
177 | 177 | public Connection getInnermostDelegate() { |
51 | 51 | |
52 | 52 | /** |
53 | 53 | * If my underlying {@link ResultSet} is not a |
54 | * <tt>DelegatingResultSet</tt>, returns it, | |
54 | * {@code DelegatingResultSet}, returns it, | |
55 | 55 | * otherwise recursively invokes this method on |
56 | 56 | * my delegate. |
57 | 57 | * <p> |
58 | 58 | * Hence this method will return the first |
59 | * delegate that is not a <tt>DelegatingResultSet</tt>, | |
60 | * or <tt>null</tt> when no non-<tt>DelegatingResultSet</tt> | |
59 | * delegate that is not a {@code DelegatingResultSet}, | |
60 | * or {@code null} when no non-{@code DelegatingResultSet} | |
61 | 61 | * delegate can be found by transversing this chain. |
62 | 62 | * <p> |
63 | 63 | * This method is useful when you may have nested |
64 | * <tt>DelegatingResultSet</tt>s, and you want to make | |
64 | * {@code DelegatingResultSet}s, and you want to make | |
65 | 65 | * sure to obtain a "genuine" {@link ResultSet}. |
66 | 66 | */ |
67 | 67 | public DatabaseMetaData getInnermostDelegate() { |
86 | 86 | |
87 | 87 | @Override |
88 | 88 | public boolean allProceduresAreCallable() throws SQLException { |
89 | { try { return _meta.allProceduresAreCallable(); } | |
90 | catch (SQLException e) { handleException(e); return false; } } | |
89 | try { return _meta.allProceduresAreCallable(); } | |
90 | catch (SQLException e) { handleException(e); return false; } | |
91 | 91 | } |
92 | 92 | |
93 | 93 | @Override |
94 | 94 | public boolean allTablesAreSelectable() throws SQLException { |
95 | { try { return _meta.allTablesAreSelectable(); } | |
96 | catch (SQLException e) { handleException(e); return false; } } | |
95 | try { return _meta.allTablesAreSelectable(); } | |
96 | catch (SQLException e) { handleException(e); return false; } | |
97 | 97 | } |
98 | 98 | |
99 | 99 | @Override |
100 | 100 | public boolean dataDefinitionCausesTransactionCommit() throws SQLException { |
101 | { try { return _meta.dataDefinitionCausesTransactionCommit(); } | |
102 | catch (SQLException e) { handleException(e); return false; } } | |
101 | try { return _meta.dataDefinitionCausesTransactionCommit(); } | |
102 | catch (SQLException e) { handleException(e); return false; } | |
103 | 103 | } |
104 | 104 | |
105 | 105 | @Override |
106 | 106 | public boolean dataDefinitionIgnoredInTransactions() throws SQLException { |
107 | { try { return _meta.dataDefinitionIgnoredInTransactions(); } | |
108 | catch (SQLException e) { handleException(e); return false; } } | |
107 | try { return _meta.dataDefinitionIgnoredInTransactions(); } | |
108 | catch (SQLException e) { handleException(e); return false; } | |
109 | 109 | } |
110 | 110 | |
111 | 111 | @Override |
112 | 112 | public boolean deletesAreDetected(int type) throws SQLException { |
113 | { try { return _meta.deletesAreDetected(type); } | |
114 | catch (SQLException e) { handleException(e); return false; } } | |
113 | try { return _meta.deletesAreDetected(type); } | |
114 | catch (SQLException e) { handleException(e); return false; } | |
115 | 115 | } |
116 | 116 | |
117 | 117 | @Override |
118 | 118 | public boolean doesMaxRowSizeIncludeBlobs() throws SQLException { |
119 | { try { return _meta.doesMaxRowSizeIncludeBlobs(); } | |
120 | catch (SQLException e) { handleException(e); return false; } } | |
119 | try { return _meta.doesMaxRowSizeIncludeBlobs(); } | |
120 | catch (SQLException e) { handleException(e); return false; } | |
121 | 121 | } |
122 | 122 | |
123 | 123 | @Override |
153 | 153 | |
154 | 154 | @Override |
155 | 155 | public String getCatalogSeparator() throws SQLException { |
156 | { try { return _meta.getCatalogSeparator(); } | |
157 | catch (SQLException e) { handleException(e); throw new AssertionError(); } } | |
156 | try { return _meta.getCatalogSeparator(); } | |
157 | catch (SQLException e) { handleException(e); throw new AssertionError(); } | |
158 | 158 | } |
159 | 159 | |
160 | 160 | @Override |
161 | 161 | public String getCatalogTerm() throws SQLException { |
162 | { try { return _meta.getCatalogTerm(); } | |
163 | catch (SQLException e) { handleException(e); throw new AssertionError(); } } | |
162 | try { return _meta.getCatalogTerm(); } | |
163 | catch (SQLException e) { handleException(e); throw new AssertionError(); } | |
164 | 164 | } |
165 | 165 | |
166 | 166 | @Override |
231 | 231 | |
232 | 232 | @Override |
233 | 233 | public int getDatabaseMajorVersion() throws SQLException { |
234 | { try { return _meta.getDatabaseMajorVersion(); } | |
235 | catch (SQLException e) { handleException(e); return 0; } } | |
234 | try { return _meta.getDatabaseMajorVersion(); } | |
235 | catch (SQLException e) { handleException(e); return 0; } | |
236 | 236 | } |
237 | 237 | |
238 | 238 | @Override |
239 | 239 | public int getDatabaseMinorVersion() throws SQLException { |
240 | { try { return _meta.getDatabaseMinorVersion(); } | |
241 | catch (SQLException e) { handleException(e); return 0; } } | |
240 | try { return _meta.getDatabaseMinorVersion(); } | |
241 | catch (SQLException e) { handleException(e); return 0; } | |
242 | 242 | } |
243 | 243 | |
244 | 244 | @Override |
245 | 245 | public String getDatabaseProductName() throws SQLException { |
246 | { try { return _meta.getDatabaseProductName(); } | |
247 | catch (SQLException e) { handleException(e); throw new AssertionError(); } } | |
246 | try { return _meta.getDatabaseProductName(); } | |
247 | catch (SQLException e) { handleException(e); throw new AssertionError(); } | |
248 | 248 | } |
249 | 249 | |
250 | 250 | @Override |
251 | 251 | public String getDatabaseProductVersion() throws SQLException { |
252 | { try { return _meta.getDatabaseProductVersion(); } | |
253 | catch (SQLException e) { handleException(e); throw new AssertionError(); } } | |
252 | try { return _meta.getDatabaseProductVersion(); } | |
253 | catch (SQLException e) { handleException(e); throw new AssertionError(); } | |
254 | 254 | } |
255 | 255 | |
256 | 256 | @Override |
257 | 257 | public int getDefaultTransactionIsolation() throws SQLException { |
258 | { try { return _meta.getDefaultTransactionIsolation(); } | |
259 | catch (SQLException e) { handleException(e); return 0; } } | |
258 | try { return _meta.getDefaultTransactionIsolation(); } | |
259 | catch (SQLException e) { handleException(e); return 0; } | |
260 | 260 | } |
261 | 261 | |
262 | 262 | @Override |
267 | 267 | |
268 | 268 | @Override |
269 | 269 | public String getDriverName() throws SQLException { |
270 | { try { return _meta.getDriverName(); } | |
271 | catch (SQLException e) { handleException(e); throw new AssertionError(); } } | |
270 | try { return _meta.getDriverName(); } | |
271 | catch (SQLException e) { handleException(e); throw new AssertionError(); } | |
272 | 272 | } |
273 | 273 | |
274 | 274 | @Override |
275 | 275 | public String getDriverVersion() throws SQLException { |
276 | { try { return _meta.getDriverVersion(); } | |
277 | catch (SQLException e) { handleException(e); throw new AssertionError(); } } | |
276 | try { return _meta.getDriverVersion(); } | |
277 | catch (SQLException e) { handleException(e); throw new AssertionError(); } | |
278 | 278 | } |
279 | 279 | |
280 | 280 | @Override |
293 | 293 | |
294 | 294 | @Override |
295 | 295 | public String getExtraNameCharacters() throws SQLException { |
296 | { try { return _meta.getExtraNameCharacters(); } | |
297 | catch (SQLException e) { handleException(e); throw new AssertionError(); } } | |
296 | try { return _meta.getExtraNameCharacters(); } | |
297 | catch (SQLException e) { handleException(e); throw new AssertionError(); } | |
298 | 298 | } |
299 | 299 | |
300 | 300 | @Override |
301 | 301 | public String getIdentifierQuoteString() throws SQLException { |
302 | { try { return _meta.getIdentifierQuoteString(); } | |
303 | catch (SQLException e) { handleException(e); throw new AssertionError(); } } | |
302 | try { return _meta.getIdentifierQuoteString(); } | |
303 | catch (SQLException e) { handleException(e); throw new AssertionError(); } | |
304 | 304 | } |
305 | 305 | |
306 | 306 | @Override |
334 | 334 | |
335 | 335 | @Override |
336 | 336 | public int getJDBCMajorVersion() throws SQLException { |
337 | { try { return _meta.getJDBCMajorVersion(); } | |
338 | catch (SQLException e) { handleException(e); return 0; } } | |
337 | try { return _meta.getJDBCMajorVersion(); } | |
338 | catch (SQLException e) { handleException(e); return 0; } | |
339 | 339 | } |
340 | 340 | |
341 | 341 | @Override |
342 | 342 | public int getJDBCMinorVersion() throws SQLException { |
343 | { try { return _meta.getJDBCMinorVersion(); } | |
344 | catch (SQLException e) { handleException(e); return 0; } } | |
343 | try { return _meta.getJDBCMinorVersion(); } | |
344 | catch (SQLException e) { handleException(e); return 0; } | |
345 | 345 | } |
346 | 346 | |
347 | 347 | @Override |
348 | 348 | public int getMaxBinaryLiteralLength() throws SQLException { |
349 | { try { return _meta.getMaxBinaryLiteralLength(); } | |
350 | catch (SQLException e) { handleException(e); return 0; } } | |
349 | try { return _meta.getMaxBinaryLiteralLength(); } | |
350 | catch (SQLException e) { handleException(e); return 0; } | |
351 | 351 | } |
352 | 352 | |
353 | 353 | @Override |
354 | 354 | public int getMaxCatalogNameLength() throws SQLException { |
355 | { try { return _meta.getMaxCatalogNameLength(); } | |
356 | catch (SQLException e) { handleException(e); return 0; } } | |
355 | try { return _meta.getMaxCatalogNameLength(); } | |
356 | catch (SQLException e) { handleException(e); return 0; } | |
357 | 357 | } |
358 | 358 | |
359 | 359 | @Override |
360 | 360 | public int getMaxCharLiteralLength() throws SQLException { |
361 | { try { return _meta.getMaxCharLiteralLength(); } | |
362 | catch (SQLException e) { handleException(e); return 0; } } | |
361 | try { return _meta.getMaxCharLiteralLength(); } | |
362 | catch (SQLException e) { handleException(e); return 0; } | |
363 | 363 | } |
364 | 364 | |
365 | 365 | @Override |
366 | 366 | public int getMaxColumnNameLength() throws SQLException { |
367 | { try { return _meta.getMaxColumnNameLength(); } | |
368 | catch (SQLException e) { handleException(e); return 0; } } | |
367 | try { return _meta.getMaxColumnNameLength(); } | |
368 | catch (SQLException e) { handleException(e); return 0; } | |
369 | 369 | } |
370 | 370 | |
371 | 371 | @Override |
372 | 372 | public int getMaxColumnsInGroupBy() throws SQLException { |
373 | { try { return _meta.getMaxColumnsInGroupBy(); } | |
374 | catch (SQLException e) { handleException(e); return 0; } } | |
373 | try { return _meta.getMaxColumnsInGroupBy(); } | |
374 | catch (SQLException e) { handleException(e); return 0; } | |
375 | 375 | } |
376 | 376 | |
377 | 377 | @Override |
378 | 378 | public int getMaxColumnsInIndex() throws SQLException { |
379 | { try { return _meta.getMaxColumnsInIndex(); } | |
380 | catch (SQLException e) { handleException(e); return 0; } } | |
379 | try { return _meta.getMaxColumnsInIndex(); } | |
380 | catch (SQLException e) { handleException(e); return 0; } | |
381 | 381 | } |
382 | 382 | |
383 | 383 | @Override |
384 | 384 | public int getMaxColumnsInOrderBy() throws SQLException { |
385 | { try { return _meta.getMaxColumnsInOrderBy(); } | |
386 | catch (SQLException e) { handleException(e); return 0; } } | |
385 | try { return _meta.getMaxColumnsInOrderBy(); } | |
386 | catch (SQLException e) { handleException(e); return 0; } | |
387 | 387 | } |
388 | 388 | |
389 | 389 | @Override |
390 | 390 | public int getMaxColumnsInSelect() throws SQLException { |
391 | { try { return _meta.getMaxColumnsInSelect(); } | |
392 | catch (SQLException e) { handleException(e); return 0; } } | |
391 | try { return _meta.getMaxColumnsInSelect(); } | |
392 | catch (SQLException e) { handleException(e); return 0; } | |
393 | 393 | } |
394 | 394 | |
395 | 395 | @Override |
396 | 396 | public int getMaxColumnsInTable() throws SQLException { |
397 | { try { return _meta.getMaxColumnsInTable(); } | |
398 | catch (SQLException e) { handleException(e); return 0; } } | |
397 | try { return _meta.getMaxColumnsInTable(); } | |
398 | catch (SQLException e) { handleException(e); return 0; } | |
399 | 399 | } |
400 | 400 | |
401 | 401 | @Override |
402 | 402 | public int getMaxConnections() throws SQLException { |
403 | { try { return _meta.getMaxConnections(); } | |
404 | catch (SQLException e) { handleException(e); return 0; } } | |
403 | try { return _meta.getMaxConnections(); } | |
404 | catch (SQLException e) { handleException(e); return 0; } | |
405 | 405 | } |
406 | 406 | |
407 | 407 | @Override |
408 | 408 | public int getMaxCursorNameLength() throws SQLException { |
409 | { try { return _meta.getMaxCursorNameLength(); } | |
410 | catch (SQLException e) { handleException(e); return 0; } } | |
409 | try { return _meta.getMaxCursorNameLength(); } | |
410 | catch (SQLException e) { handleException(e); return 0; } | |
411 | 411 | } |
412 | 412 | |
413 | 413 | @Override |
414 | 414 | public int getMaxIndexLength() throws SQLException { |
415 | { try { return _meta.getMaxIndexLength(); } | |
416 | catch (SQLException e) { handleException(e); return 0; } } | |
415 | try { return _meta.getMaxIndexLength(); } | |
416 | catch (SQLException e) { handleException(e); return 0; } | |
417 | 417 | } |
418 | 418 | |
419 | 419 | @Override |
420 | 420 | public int getMaxProcedureNameLength() throws SQLException { |
421 | { try { return _meta.getMaxProcedureNameLength(); } | |
422 | catch (SQLException e) { handleException(e); return 0; } } | |
421 | try { return _meta.getMaxProcedureNameLength(); } | |
422 | catch (SQLException e) { handleException(e); return 0; } | |
423 | 423 | } |
424 | 424 | |
425 | 425 | @Override |
426 | 426 | public int getMaxRowSize() throws SQLException { |
427 | { try { return _meta.getMaxRowSize(); } | |
428 | catch (SQLException e) { handleException(e); return 0; } } | |
427 | try { return _meta.getMaxRowSize(); } | |
428 | catch (SQLException e) { handleException(e); return 0; } | |
429 | 429 | } |
430 | 430 | |
431 | 431 | @Override |
432 | 432 | public int getMaxSchemaNameLength() throws SQLException { |
433 | { try { return _meta.getMaxSchemaNameLength(); } | |
434 | catch (SQLException e) { handleException(e); return 0; } } | |
433 | try { return _meta.getMaxSchemaNameLength(); } | |
434 | catch (SQLException e) { handleException(e); return 0; } | |
435 | 435 | } |
436 | 436 | |
437 | 437 | @Override |
438 | 438 | public int getMaxStatementLength() throws SQLException { |
439 | { try { return _meta.getMaxStatementLength(); } | |
440 | catch (SQLException e) { handleException(e); return 0; } } | |
439 | try { return _meta.getMaxStatementLength(); } | |
440 | catch (SQLException e) { handleException(e); return 0; } | |
441 | 441 | } |
442 | 442 | |
443 | 443 | @Override |
444 | 444 | public int getMaxStatements() throws SQLException { |
445 | { try { return _meta.getMaxStatements(); } | |
446 | catch (SQLException e) { handleException(e); return 0; } } | |
445 | try { return _meta.getMaxStatements(); } | |
446 | catch (SQLException e) { handleException(e); return 0; } | |
447 | 447 | } |
448 | 448 | |
449 | 449 | @Override |
450 | 450 | public int getMaxTableNameLength() throws SQLException { |
451 | { try { return _meta.getMaxTableNameLength(); } | |
452 | catch (SQLException e) { handleException(e); return 0; } } | |
451 | try { return _meta.getMaxTableNameLength(); } | |
452 | catch (SQLException e) { handleException(e); return 0; } | |
453 | 453 | } |
454 | 454 | |
455 | 455 | @Override |
456 | 456 | public int getMaxTablesInSelect() throws SQLException { |
457 | { try { return _meta.getMaxTablesInSelect(); } | |
458 | catch (SQLException e) { handleException(e); return 0; } } | |
457 | try { return _meta.getMaxTablesInSelect(); } | |
458 | catch (SQLException e) { handleException(e); return 0; } | |
459 | 459 | } |
460 | 460 | |
461 | 461 | @Override |
462 | 462 | public int getMaxUserNameLength() throws SQLException { |
463 | { try { return _meta.getMaxUserNameLength(); } | |
464 | catch (SQLException e) { handleException(e); return 0; } } | |
463 | try { return _meta.getMaxUserNameLength(); } | |
464 | catch (SQLException e) { handleException(e); return 0; } | |
465 | 465 | } |
466 | 466 | |
467 | 467 | @Override |
468 | 468 | public String getNumericFunctions() throws SQLException { |
469 | { try { return _meta.getNumericFunctions(); } | |
470 | catch (SQLException e) { handleException(e); throw new AssertionError(); } } | |
469 | try { return _meta.getNumericFunctions(); } | |
470 | catch (SQLException e) { handleException(e); throw new AssertionError(); } | |
471 | 471 | } |
472 | 472 | |
473 | 473 | @Override |
502 | 502 | |
503 | 503 | @Override |
504 | 504 | public String getProcedureTerm() throws SQLException { |
505 | { try { return _meta.getProcedureTerm(); } | |
506 | catch (SQLException e) { handleException(e); throw new AssertionError(); } } | |
505 | try { return _meta.getProcedureTerm(); } | |
506 | catch (SQLException e) { handleException(e); throw new AssertionError(); } | |
507 | 507 | } |
508 | 508 | |
509 | 509 | @Override |
523 | 523 | |
524 | 524 | @Override |
525 | 525 | public int getResultSetHoldability() throws SQLException { |
526 | { try { return _meta.getResultSetHoldability(); } | |
527 | catch (SQLException e) { handleException(e); return 0; } } | |
526 | try { return _meta.getResultSetHoldability(); } | |
527 | catch (SQLException e) { handleException(e); return 0; } | |
528 | 528 | } |
529 | 529 | |
530 | 530 | @Override |
531 | 531 | public String getSQLKeywords() throws SQLException { |
532 | { try { return _meta.getSQLKeywords(); } | |
533 | catch (SQLException e) { handleException(e); throw new AssertionError(); } } | |
532 | try { return _meta.getSQLKeywords(); } | |
533 | catch (SQLException e) { handleException(e); throw new AssertionError(); } | |
534 | 534 | } |
535 | 535 | |
536 | 536 | @Override |
537 | 537 | public int getSQLStateType() throws SQLException { |
538 | { try { return _meta.getSQLStateType(); } | |
539 | catch (SQLException e) { handleException(e); return 0; } } | |
538 | try { return _meta.getSQLStateType(); } | |
539 | catch (SQLException e) { handleException(e); return 0; } | |
540 | 540 | } |
541 | 541 | |
542 | 542 | @Override |
543 | 543 | public String getSchemaTerm() throws SQLException { |
544 | { try { return _meta.getSchemaTerm(); } | |
545 | catch (SQLException e) { handleException(e); throw new AssertionError(); } } | |
544 | try { return _meta.getSchemaTerm(); } | |
545 | catch (SQLException e) { handleException(e); throw new AssertionError(); } | |
546 | 546 | } |
547 | 547 | |
548 | 548 | @Override |
560 | 560 | |
561 | 561 | @Override |
562 | 562 | public String getSearchStringEscape() throws SQLException { |
563 | { try { return _meta.getSearchStringEscape(); } | |
564 | catch (SQLException e) { handleException(e); throw new AssertionError(); } } | |
563 | try { return _meta.getSearchStringEscape(); } | |
564 | catch (SQLException e) { handleException(e); throw new AssertionError(); } | |
565 | 565 | } |
566 | 566 | |
567 | 567 | @Override |
568 | 568 | public String getStringFunctions() throws SQLException { |
569 | { try { return _meta.getStringFunctions(); } | |
570 | catch (SQLException e) { handleException(e); throw new AssertionError(); } } | |
569 | try { return _meta.getStringFunctions(); } | |
570 | catch (SQLException e) { handleException(e); throw new AssertionError(); } | |
571 | 571 | } |
572 | 572 | |
573 | 573 | @Override |
602 | 602 | |
603 | 603 | @Override |
604 | 604 | public String getSystemFunctions() throws SQLException { |
605 | { try { return _meta.getSystemFunctions(); } | |
606 | catch (SQLException e) { handleException(e); throw new AssertionError(); } } | |
605 | try { return _meta.getSystemFunctions(); } | |
606 | catch (SQLException e) { handleException(e); throw new AssertionError(); } | |
607 | 607 | } |
608 | 608 | |
609 | 609 | @Override |
651 | 651 | |
652 | 652 | @Override |
653 | 653 | public String getTimeDateFunctions() throws SQLException { |
654 | { try { return _meta.getTimeDateFunctions(); } | |
655 | catch (SQLException e) { handleException(e); throw new AssertionError(); } } | |
654 | try { return _meta.getTimeDateFunctions(); } | |
655 | catch (SQLException e) { handleException(e); throw new AssertionError(); } | |
656 | 656 | } |
657 | 657 | |
658 | 658 | @Override |
685 | 685 | |
686 | 686 | @Override |
687 | 687 | public String getURL() throws SQLException { |
688 | { try { return _meta.getURL(); } | |
689 | catch (SQLException e) { handleException(e); throw new AssertionError(); } } | |
688 | try { return _meta.getURL(); } | |
689 | catch (SQLException e) { handleException(e); throw new AssertionError(); } | |
690 | 690 | } |
691 | 691 | |
692 | 692 | @Override |
693 | 693 | public String getUserName() throws SQLException { |
694 | { try { return _meta.getUserName(); } | |
695 | catch (SQLException e) { handleException(e); throw new AssertionError(); } } | |
694 | try { return _meta.getUserName(); } | |
695 | catch (SQLException e) { handleException(e); throw new AssertionError(); } | |
696 | 696 | } |
697 | 697 | |
698 | 698 | @Override |
711 | 711 | |
712 | 712 | @Override |
713 | 713 | public boolean insertsAreDetected(int type) throws SQLException { |
714 | { try { return _meta.insertsAreDetected(type); } | |
715 | catch (SQLException e) { handleException(e); return false; } } | |
714 | try { return _meta.insertsAreDetected(type); } | |
715 | catch (SQLException e) { handleException(e); return false; } | |
716 | 716 | } |
717 | 717 | |
718 | 718 | @Override |
719 | 719 | public boolean isCatalogAtStart() throws SQLException { |
720 | { try { return _meta.isCatalogAtStart(); } | |
721 | catch (SQLException e) { handleException(e); return false; } } | |
720 | try { return _meta.isCatalogAtStart(); } | |
721 | catch (SQLException e) { handleException(e); return false; } | |
722 | 722 | } |
723 | 723 | |
724 | 724 | @Override |
725 | 725 | public boolean isReadOnly() throws SQLException { |
726 | { try { return _meta.isReadOnly(); } | |
727 | catch (SQLException e) { handleException(e); return false; } } | |
726 | try { return _meta.isReadOnly(); } | |
727 | catch (SQLException e) { handleException(e); return false; } | |
728 | 728 | } |
729 | 729 | |
730 | 730 | @Override |
731 | 731 | public boolean locatorsUpdateCopy() throws SQLException { |
732 | { try { return _meta.locatorsUpdateCopy(); } | |
733 | catch (SQLException e) { handleException(e); return false; } } | |
732 | try { return _meta.locatorsUpdateCopy(); } | |
733 | catch (SQLException e) { handleException(e); return false; } | |
734 | 734 | } |
735 | 735 | |
736 | 736 | @Override |
737 | 737 | public boolean nullPlusNonNullIsNull() throws SQLException { |
738 | { try { return _meta.nullPlusNonNullIsNull(); } | |
739 | catch (SQLException e) { handleException(e); return false; } } | |
738 | try { return _meta.nullPlusNonNullIsNull(); } | |
739 | catch (SQLException e) { handleException(e); return false; } | |
740 | 740 | } |
741 | 741 | |
742 | 742 | @Override |
743 | 743 | public boolean nullsAreSortedAtEnd() throws SQLException { |
744 | { try { return _meta.nullsAreSortedAtEnd(); } | |
745 | catch (SQLException e) { handleException(e); return false; } } | |
744 | try { return _meta.nullsAreSortedAtEnd(); } | |
745 | catch (SQLException e) { handleException(e); return false; } | |
746 | 746 | } |
747 | 747 | |
748 | 748 | @Override |
749 | 749 | public boolean nullsAreSortedAtStart() throws SQLException { |
750 | { try { return _meta.nullsAreSortedAtStart(); } | |
751 | catch (SQLException e) { handleException(e); return false; } } | |
750 | try { return _meta.nullsAreSortedAtStart(); } | |
751 | catch (SQLException e) { handleException(e); return false; } | |
752 | 752 | } |
753 | 753 | |
754 | 754 | @Override |
755 | 755 | public boolean nullsAreSortedHigh() throws SQLException { |
756 | { try { return _meta.nullsAreSortedHigh(); } | |
757 | catch (SQLException e) { handleException(e); return false; } } | |
756 | try { return _meta.nullsAreSortedHigh(); } | |
757 | catch (SQLException e) { handleException(e); return false; } | |
758 | 758 | } |
759 | 759 | |
760 | 760 | @Override |
761 | 761 | public boolean nullsAreSortedLow() throws SQLException { |
762 | { try { return _meta.nullsAreSortedLow(); } | |
763 | catch (SQLException e) { handleException(e); return false; } } | |
762 | try { return _meta.nullsAreSortedLow(); } | |
763 | catch (SQLException e) { handleException(e); return false; } | |
764 | 764 | } |
765 | 765 | |
766 | 766 | @Override |
767 | 767 | public boolean othersDeletesAreVisible(int type) throws SQLException { |
768 | { try { return _meta.othersDeletesAreVisible(type); } | |
769 | catch (SQLException e) { handleException(e); return false; } } | |
768 | try { return _meta.othersDeletesAreVisible(type); } | |
769 | catch (SQLException e) { handleException(e); return false; } | |
770 | 770 | } |
771 | 771 | |
772 | 772 | @Override |
773 | 773 | public boolean othersInsertsAreVisible(int type) throws SQLException { |
774 | { try { return _meta.othersInsertsAreVisible(type); } | |
775 | catch (SQLException e) { handleException(e); return false; } } | |
774 | try { return _meta.othersInsertsAreVisible(type); } | |
775 | catch (SQLException e) { handleException(e); return false; } | |
776 | 776 | } |
777 | 777 | |
778 | 778 | @Override |
779 | 779 | public boolean othersUpdatesAreVisible(int type) throws SQLException { |
780 | { try { return _meta.othersUpdatesAreVisible(type); } | |
781 | catch (SQLException e) { handleException(e); return false; } } | |
780 | try { return _meta.othersUpdatesAreVisible(type); } | |
781 | catch (SQLException e) { handleException(e); return false; } | |
782 | 782 | } |
783 | 783 | |
784 | 784 | @Override |
785 | 785 | public boolean ownDeletesAreVisible(int type) throws SQLException { |
786 | { try { return _meta.ownDeletesAreVisible(type); } | |
787 | catch (SQLException e) { handleException(e); return false; } } | |
786 | try { return _meta.ownDeletesAreVisible(type); } | |
787 | catch (SQLException e) { handleException(e); return false; } | |
788 | 788 | } |
789 | 789 | |
790 | 790 | @Override |
791 | 791 | public boolean ownInsertsAreVisible(int type) throws SQLException { |
792 | { try { return _meta.ownInsertsAreVisible(type); } | |
793 | catch (SQLException e) { handleException(e); return false; } } | |
792 | try { return _meta.ownInsertsAreVisible(type); } | |
793 | catch (SQLException e) { handleException(e); return false; } | |
794 | 794 | } |
795 | 795 | |
796 | 796 | @Override |
797 | 797 | public boolean ownUpdatesAreVisible(int type) throws SQLException { |
798 | { try { return _meta.ownUpdatesAreVisible(type); } | |
799 | catch (SQLException e) { handleException(e); return false; } } | |
798 | try { return _meta.ownUpdatesAreVisible(type); } | |
799 | catch (SQLException e) { handleException(e); return false; } | |
800 | 800 | } |
801 | 801 | |
802 | 802 | @Override |
803 | 803 | public boolean storesLowerCaseIdentifiers() throws SQLException { |
804 | { try { return _meta.storesLowerCaseIdentifiers(); } | |
805 | catch (SQLException e) { handleException(e); return false; } } | |
804 | try { return _meta.storesLowerCaseIdentifiers(); } | |
805 | catch (SQLException e) { handleException(e); return false; } | |
806 | 806 | } |
807 | 807 | |
808 | 808 | @Override |
809 | 809 | public boolean storesLowerCaseQuotedIdentifiers() throws SQLException { |
810 | { try { return _meta.storesLowerCaseQuotedIdentifiers(); } | |
811 | catch (SQLException e) { handleException(e); return false; } } | |
810 | try { return _meta.storesLowerCaseQuotedIdentifiers(); } | |
811 | catch (SQLException e) { handleException(e); return false; } | |
812 | 812 | } |
813 | 813 | |
814 | 814 | @Override |
815 | 815 | public boolean storesMixedCaseIdentifiers() throws SQLException { |
816 | { try { return _meta.storesMixedCaseIdentifiers(); } | |
817 | catch (SQLException e) { handleException(e); return false; } } | |
816 | try { return _meta.storesMixedCaseIdentifiers(); } | |
817 | catch (SQLException e) { handleException(e); return false; } | |
818 | 818 | } |
819 | 819 | |
820 | 820 | @Override |
821 | 821 | public boolean storesMixedCaseQuotedIdentifiers() throws SQLException { |
822 | { try { return _meta.storesMixedCaseQuotedIdentifiers(); } | |
823 | catch (SQLException e) { handleException(e); return false; } } | |
822 | try { return _meta.storesMixedCaseQuotedIdentifiers(); } | |
823 | catch (SQLException e) { handleException(e); return false; } | |
824 | 824 | } |
825 | 825 | |
826 | 826 | @Override |
827 | 827 | public boolean storesUpperCaseIdentifiers() throws SQLException { |
828 | { try { return _meta.storesUpperCaseIdentifiers(); } | |
829 | catch (SQLException e) { handleException(e); return false; } } | |
828 | try { return _meta.storesUpperCaseIdentifiers(); } | |
829 | catch (SQLException e) { handleException(e); return false; } | |
830 | 830 | } |
831 | 831 | |
832 | 832 | @Override |
833 | 833 | public boolean storesUpperCaseQuotedIdentifiers() throws SQLException { |
834 | { try { return _meta.storesUpperCaseQuotedIdentifiers(); } | |
835 | catch (SQLException e) { handleException(e); return false; } } | |
834 | try { return _meta.storesUpperCaseQuotedIdentifiers(); } | |
835 | catch (SQLException e) { handleException(e); return false; } | |
836 | 836 | } |
837 | 837 | |
838 | 838 | @Override |
839 | 839 | public boolean supportsANSI92EntryLevelSQL() throws SQLException { |
840 | { try { return _meta.supportsANSI92EntryLevelSQL(); } | |
841 | catch (SQLException e) { handleException(e); return false; } } | |
840 | try { return _meta.supportsANSI92EntryLevelSQL(); } | |
841 | catch (SQLException e) { handleException(e); return false; } | |
842 | 842 | } |
843 | 843 | |
844 | 844 | @Override |
845 | 845 | public boolean supportsANSI92FullSQL() throws SQLException { |
846 | { try { return _meta.supportsANSI92FullSQL(); } | |
847 | catch (SQLException e) { handleException(e); return false; } } | |
846 | try { return _meta.supportsANSI92FullSQL(); } | |
847 | catch (SQLException e) { handleException(e); return false; } | |
848 | 848 | } |
849 | 849 | |
850 | 850 | @Override |
851 | 851 | public boolean supportsANSI92IntermediateSQL() throws SQLException { |
852 | { try { return _meta.supportsANSI92IntermediateSQL(); } | |
853 | catch (SQLException e) { handleException(e); return false; } } | |
852 | try { return _meta.supportsANSI92IntermediateSQL(); } | |
853 | catch (SQLException e) { handleException(e); return false; } | |
854 | 854 | } |
855 | 855 | |
856 | 856 | @Override |
857 | 857 | public boolean supportsAlterTableWithAddColumn() throws SQLException { |
858 | { try { return _meta.supportsAlterTableWithAddColumn(); } | |
859 | catch (SQLException e) { handleException(e); return false; } } | |
858 | try { return _meta.supportsAlterTableWithAddColumn(); } | |
859 | catch (SQLException e) { handleException(e); return false; } | |
860 | 860 | } |
861 | 861 | |
862 | 862 | @Override |
863 | 863 | public boolean supportsAlterTableWithDropColumn() throws SQLException { |
864 | { try { return _meta.supportsAlterTableWithDropColumn(); } | |
865 | catch (SQLException e) { handleException(e); return false; } } | |
864 | try { return _meta.supportsAlterTableWithDropColumn(); } | |
865 | catch (SQLException e) { handleException(e); return false; } | |
866 | 866 | } |
867 | 867 | |
868 | 868 | @Override |
869 | 869 | public boolean supportsBatchUpdates() throws SQLException { |
870 | { try { return _meta.supportsBatchUpdates(); } | |
871 | catch (SQLException e) { handleException(e); return false; } } | |
870 | try { return _meta.supportsBatchUpdates(); } | |
871 | catch (SQLException e) { handleException(e); return false; } | |
872 | 872 | } |
873 | 873 | |
874 | 874 | @Override |
875 | 875 | public boolean supportsCatalogsInDataManipulation() throws SQLException { |
876 | { try { return _meta.supportsCatalogsInDataManipulation(); } | |
877 | catch (SQLException e) { handleException(e); return false; } } | |
876 | try { return _meta.supportsCatalogsInDataManipulation(); } | |
877 | catch (SQLException e) { handleException(e); return false; } | |
878 | 878 | } |
879 | 879 | |
880 | 880 | @Override |
881 | 881 | public boolean supportsCatalogsInIndexDefinitions() throws SQLException { |
882 | { try { return _meta.supportsCatalogsInIndexDefinitions(); } | |
883 | catch (SQLException e) { handleException(e); return false; } } | |
882 | try { return _meta.supportsCatalogsInIndexDefinitions(); } | |
883 | catch (SQLException e) { handleException(e); return false; } | |
884 | 884 | } |
885 | 885 | |
886 | 886 | @Override |
887 | 887 | public boolean supportsCatalogsInPrivilegeDefinitions() throws SQLException { |
888 | { try { return _meta.supportsCatalogsInPrivilegeDefinitions(); } | |
889 | catch (SQLException e) { handleException(e); return false; } } | |
888 | try { return _meta.supportsCatalogsInPrivilegeDefinitions(); } | |
889 | catch (SQLException e) { handleException(e); return false; } | |
890 | 890 | } |
891 | 891 | |
892 | 892 | @Override |
893 | 893 | public boolean supportsCatalogsInProcedureCalls() throws SQLException { |
894 | { try { return _meta.supportsCatalogsInProcedureCalls(); } | |
895 | catch (SQLException e) { handleException(e); return false; } } | |
894 | try { return _meta.supportsCatalogsInProcedureCalls(); } | |
895 | catch (SQLException e) { handleException(e); return false; } | |
896 | 896 | } |
897 | 897 | |
898 | 898 | @Override |
899 | 899 | public boolean supportsCatalogsInTableDefinitions() throws SQLException { |
900 | { try { return _meta.supportsCatalogsInTableDefinitions(); } | |
901 | catch (SQLException e) { handleException(e); return false; } } | |
900 | try { return _meta.supportsCatalogsInTableDefinitions(); } | |
901 | catch (SQLException e) { handleException(e); return false; } | |
902 | 902 | } |
903 | 903 | |
904 | 904 | @Override |
905 | 905 | public boolean supportsColumnAliasing() throws SQLException { |
906 | { try { return _meta.supportsColumnAliasing(); } | |
907 | catch (SQLException e) { handleException(e); return false; } } | |
906 | try { return _meta.supportsColumnAliasing(); } | |
907 | catch (SQLException e) { handleException(e); return false; } | |
908 | 908 | } |
909 | 909 | |
910 | 910 | @Override |
911 | 911 | public boolean supportsConvert() throws SQLException { |
912 | { try { return _meta.supportsConvert(); } | |
913 | catch (SQLException e) { handleException(e); return false; } } | |
912 | try { return _meta.supportsConvert(); } | |
913 | catch (SQLException e) { handleException(e); return false; } | |
914 | 914 | } |
915 | 915 | |
916 | 916 | @Override |
917 | 917 | public boolean supportsConvert(int fromType, int toType) |
918 | 918 | throws SQLException { |
919 | { try { return _meta.supportsConvert(fromType, toType); } | |
920 | catch (SQLException e) { handleException(e); return false; } } | |
919 | try { return _meta.supportsConvert(fromType, toType); } | |
920 | catch (SQLException e) { handleException(e); return false; } | |
921 | 921 | } |
922 | 922 | |
923 | 923 | @Override |
924 | 924 | public boolean supportsCoreSQLGrammar() throws SQLException { |
925 | { try { return _meta.supportsCoreSQLGrammar(); } | |
926 | catch (SQLException e) { handleException(e); return false; } } | |
925 | try { return _meta.supportsCoreSQLGrammar(); } | |
926 | catch (SQLException e) { handleException(e); return false; } | |
927 | 927 | } |
928 | 928 | |
929 | 929 | @Override |
930 | 930 | public boolean supportsCorrelatedSubqueries() throws SQLException { |
931 | { try { return _meta.supportsCorrelatedSubqueries(); } | |
932 | catch (SQLException e) { handleException(e); return false; } } | |
931 | try { return _meta.supportsCorrelatedSubqueries(); } | |
932 | catch (SQLException e) { handleException(e); return false; } | |
933 | 933 | } |
934 | 934 | |
935 | 935 | @Override |
936 | 936 | public boolean supportsDataDefinitionAndDataManipulationTransactions() |
937 | 937 | throws SQLException { |
938 | { try { return _meta.supportsDataDefinitionAndDataManipulationTransactions(); } | |
939 | catch (SQLException e) { handleException(e); return false; } } | |
938 | try { return _meta.supportsDataDefinitionAndDataManipulationTransactions(); } | |
939 | catch (SQLException e) { handleException(e); return false; } | |
940 | 940 | } |
941 | 941 | |
942 | 942 | @Override |
943 | 943 | public boolean supportsDataManipulationTransactionsOnly() |
944 | 944 | throws SQLException { |
945 | { try { return _meta.supportsDataManipulationTransactionsOnly(); } | |
946 | catch (SQLException e) { handleException(e); return false; } } | |
945 | try { return _meta.supportsDataManipulationTransactionsOnly(); } | |
946 | catch (SQLException e) { handleException(e); return false; } | |
947 | 947 | } |
948 | 948 | |
949 | 949 | @Override |
950 | 950 | public boolean supportsDifferentTableCorrelationNames() throws SQLException { |
951 | { try { return _meta.supportsDifferentTableCorrelationNames(); } | |
952 | catch (SQLException e) { handleException(e); return false; } } | |
951 | try { return _meta.supportsDifferentTableCorrelationNames(); } | |
952 | catch (SQLException e) { handleException(e); return false; } | |
953 | 953 | } |
954 | 954 | |
955 | 955 | @Override |
956 | 956 | public boolean supportsExpressionsInOrderBy() throws SQLException { |
957 | { try { return _meta.supportsExpressionsInOrderBy(); } | |
958 | catch (SQLException e) { handleException(e); return false; } } | |
957 | try { return _meta.supportsExpressionsInOrderBy(); } | |
958 | catch (SQLException e) { handleException(e); return false; } | |
959 | 959 | } |
960 | 960 | |
961 | 961 | @Override |
962 | 962 | public boolean supportsExtendedSQLGrammar() throws SQLException { |
963 | { try { return _meta.supportsExtendedSQLGrammar(); } | |
964 | catch (SQLException e) { handleException(e); return false; } } | |
963 | try { return _meta.supportsExtendedSQLGrammar(); } | |
964 | catch (SQLException e) { handleException(e); return false; } | |
965 | 965 | } |
966 | 966 | |
967 | 967 | @Override |
968 | 968 | public boolean supportsFullOuterJoins() throws SQLException { |
969 | { try { return _meta.supportsFullOuterJoins(); } | |
970 | catch (SQLException e) { handleException(e); return false; } } | |
969 | try { return _meta.supportsFullOuterJoins(); } | |
970 | catch (SQLException e) { handleException(e); return false; } | |
971 | 971 | } |
972 | 972 | |
973 | 973 | @Override |
974 | 974 | public boolean supportsGetGeneratedKeys() throws SQLException { |
975 | { try { return _meta.supportsGetGeneratedKeys(); } | |
976 | catch (SQLException e) { handleException(e); return false; } } | |
975 | try { return _meta.supportsGetGeneratedKeys(); } | |
976 | catch (SQLException e) { handleException(e); return false; } | |
977 | 977 | } |
978 | 978 | |
979 | 979 | @Override |
980 | 980 | public boolean supportsGroupBy() throws SQLException { |
981 | { try { return _meta.supportsGroupBy(); } | |
982 | catch (SQLException e) { handleException(e); return false; } } | |
981 | try { return _meta.supportsGroupBy(); } | |
982 | catch (SQLException e) { handleException(e); return false; } | |
983 | 983 | } |
984 | 984 | |
985 | 985 | @Override |
986 | 986 | public boolean supportsGroupByBeyondSelect() throws SQLException { |
987 | { try { return _meta.supportsGroupByBeyondSelect(); } | |
988 | catch (SQLException e) { handleException(e); return false; } } | |
987 | try { return _meta.supportsGroupByBeyondSelect(); } | |
988 | catch (SQLException e) { handleException(e); return false; } | |
989 | 989 | } |
990 | 990 | |
991 | 991 | @Override |
992 | 992 | public boolean supportsGroupByUnrelated() throws SQLException { |
993 | { try { return _meta.supportsGroupByUnrelated(); } | |
994 | catch (SQLException e) { handleException(e); return false; } } | |
993 | try { return _meta.supportsGroupByUnrelated(); } | |
994 | catch (SQLException e) { handleException(e); return false; } | |
995 | 995 | } |
996 | 996 | |
997 | 997 | @Override |
998 | 998 | public boolean supportsIntegrityEnhancementFacility() throws SQLException { |
999 | { try { return _meta.supportsIntegrityEnhancementFacility(); } | |
1000 | catch (SQLException e) { handleException(e); return false; } } | |
999 | try { return _meta.supportsIntegrityEnhancementFacility(); } | |
1000 | catch (SQLException e) { handleException(e); return false; } | |
1001 | 1001 | } |
1002 | 1002 | |
1003 | 1003 | @Override |
1004 | 1004 | public boolean supportsLikeEscapeClause() throws SQLException { |
1005 | { try { return _meta.supportsLikeEscapeClause(); } | |
1006 | catch (SQLException e) { handleException(e); return false; } } | |
1005 | try { return _meta.supportsLikeEscapeClause(); } | |
1006 | catch (SQLException e) { handleException(e); return false; } | |
1007 | 1007 | } |
1008 | 1008 | |
1009 | 1009 | @Override |
1010 | 1010 | public boolean supportsLimitedOuterJoins() throws SQLException { |
1011 | { try { return _meta.supportsLimitedOuterJoins(); } | |
1012 | catch (SQLException e) { handleException(e); return false; } } | |
1011 | try { return _meta.supportsLimitedOuterJoins(); } | |
1012 | catch (SQLException e) { handleException(e); return false; } | |
1013 | 1013 | } |
1014 | 1014 | |
1015 | 1015 | @Override |
1016 | 1016 | public boolean supportsMinimumSQLGrammar() throws SQLException { |
1017 | { try { return _meta.supportsMinimumSQLGrammar(); } | |
1018 | catch (SQLException e) { handleException(e); return false; } } | |
1017 | try { return _meta.supportsMinimumSQLGrammar(); } | |
1018 | catch (SQLException e) { handleException(e); return false; } | |
1019 | 1019 | } |
1020 | 1020 | |
1021 | 1021 | @Override |
1022 | 1022 | public boolean supportsMixedCaseIdentifiers() throws SQLException { |
1023 | { try { return _meta.supportsMixedCaseIdentifiers(); } | |
1024 | catch (SQLException e) { handleException(e); return false; } } | |
1023 | try { return _meta.supportsMixedCaseIdentifiers(); } | |
1024 | catch (SQLException e) { handleException(e); return false; } | |
1025 | 1025 | } |
1026 | 1026 | |
1027 | 1027 | @Override |
1028 | 1028 | public boolean supportsMixedCaseQuotedIdentifiers() throws SQLException { |
1029 | { try { return _meta.supportsMixedCaseQuotedIdentifiers(); } | |
1030 | catch (SQLException e) { handleException(e); return false; } } | |
1029 | try { return _meta.supportsMixedCaseQuotedIdentifiers(); } | |
1030 | catch (SQLException e) { handleException(e); return false; } | |
1031 | 1031 | } |
1032 | 1032 | |
1033 | 1033 | @Override |
1034 | 1034 | public boolean supportsMultipleOpenResults() throws SQLException { |
1035 | { try { return _meta.supportsMultipleOpenResults(); } | |
1036 | catch (SQLException e) { handleException(e); return false; } } | |
1035 | try { return _meta.supportsMultipleOpenResults(); } | |
1036 | catch (SQLException e) { handleException(e); return false; } | |
1037 | 1037 | } |
1038 | 1038 | |
1039 | 1039 | @Override |
1040 | 1040 | public boolean supportsMultipleResultSets() throws SQLException { |
1041 | { try { return _meta.supportsMultipleResultSets(); } | |
1042 | catch (SQLException e) { handleException(e); return false; } } | |
1041 | try { return _meta.supportsMultipleResultSets(); } | |
1042 | catch (SQLException e) { handleException(e); return false; } | |
1043 | 1043 | } |
1044 | 1044 | |
1045 | 1045 | @Override |
1046 | 1046 | public boolean supportsMultipleTransactions() throws SQLException { |
1047 | { try { return _meta.supportsMultipleTransactions(); } | |
1048 | catch (SQLException e) { handleException(e); return false; } } | |
1047 | try { return _meta.supportsMultipleTransactions(); } | |
1048 | catch (SQLException e) { handleException(e); return false; } | |
1049 | 1049 | } |
1050 | 1050 | |
1051 | 1051 | @Override |
1052 | 1052 | public boolean supportsNamedParameters() throws SQLException { |
1053 | { try { return _meta.supportsNamedParameters(); } | |
1054 | catch (SQLException e) { handleException(e); return false; } } | |
1053 | try { return _meta.supportsNamedParameters(); } | |
1054 | catch (SQLException e) { handleException(e); return false; } | |
1055 | 1055 | } |
1056 | 1056 | |
1057 | 1057 | @Override |
1058 | 1058 | public boolean supportsNonNullableColumns() throws SQLException { |
1059 | { try { return _meta.supportsNonNullableColumns(); } | |
1060 | catch (SQLException e) { handleException(e); return false; } } | |
1059 | try { return _meta.supportsNonNullableColumns(); } | |
1060 | catch (SQLException e) { handleException(e); return false; } | |
1061 | 1061 | } |
1062 | 1062 | |
1063 | 1063 | @Override |
1064 | 1064 | public boolean supportsOpenCursorsAcrossCommit() throws SQLException { |
1065 | { try { return _meta.supportsOpenCursorsAcrossCommit(); } | |
1066 | catch (SQLException e) { handleException(e); return false; } } | |
1065 | try { return _meta.supportsOpenCursorsAcrossCommit(); } | |
1066 | catch (SQLException e) { handleException(e); return false; } | |
1067 | 1067 | } |
1068 | 1068 | |
1069 | 1069 | @Override |
1070 | 1070 | public boolean supportsOpenCursorsAcrossRollback() throws SQLException { |
1071 | { try { return _meta.supportsOpenCursorsAcrossRollback(); } | |
1072 | catch (SQLException e) { handleException(e); return false; } } | |
1071 | try { return _meta.supportsOpenCursorsAcrossRollback(); } | |
1072 | catch (SQLException e) { handleException(e); return false; } | |
1073 | 1073 | } |
1074 | 1074 | |
1075 | 1075 | @Override |
1076 | 1076 | public boolean supportsOpenStatementsAcrossCommit() throws SQLException { |
1077 | { try { return _meta.supportsOpenStatementsAcrossCommit(); } | |
1078 | catch (SQLException e) { handleException(e); return false; } } | |
1077 | try { return _meta.supportsOpenStatementsAcrossCommit(); } | |
1078 | catch (SQLException e) { handleException(e); return false; } | |
1079 | 1079 | } |
1080 | 1080 | |
1081 | 1081 | @Override |
1082 | 1082 | public boolean supportsOpenStatementsAcrossRollback() throws SQLException { |
1083 | { try { return _meta.supportsOpenStatementsAcrossRollback(); } | |
1084 | catch (SQLException e) { handleException(e); return false; } } | |
1083 | try { return _meta.supportsOpenStatementsAcrossRollback(); } | |
1084 | catch (SQLException e) { handleException(e); return false; } | |
1085 | 1085 | } |
1086 | 1086 | |
1087 | 1087 | @Override |
1088 | 1088 | public boolean supportsOrderByUnrelated() throws SQLException { |
1089 | { try { return _meta.supportsOrderByUnrelated(); } | |
1090 | catch (SQLException e) { handleException(e); return false; } } | |
1089 | try { return _meta.supportsOrderByUnrelated(); } | |
1090 | catch (SQLException e) { handleException(e); return false; } | |
1091 | 1091 | } |
1092 | 1092 | |
1093 | 1093 | @Override |
1094 | 1094 | public boolean supportsOuterJoins() throws SQLException { |
1095 | { try { return _meta.supportsOuterJoins(); } | |
1096 | catch (SQLException e) { handleException(e); return false; } } | |
1095 | try { return _meta.supportsOuterJoins(); } | |
1096 | catch (SQLException e) { handleException(e); return false; } | |
1097 | 1097 | } |
1098 | 1098 | |
1099 | 1099 | @Override |
1100 | 1100 | public boolean supportsPositionedDelete() throws SQLException { |
1101 | { try { return _meta.supportsPositionedDelete(); } | |
1102 | catch (SQLException e) { handleException(e); return false; } } | |
1101 | try { return _meta.supportsPositionedDelete(); } | |
1102 | catch (SQLException e) { handleException(e); return false; } | |
1103 | 1103 | } |
1104 | 1104 | |
1105 | 1105 | @Override |
1106 | 1106 | public boolean supportsPositionedUpdate() throws SQLException { |
1107 | { try { return _meta.supportsPositionedUpdate(); } | |
1108 | catch (SQLException e) { handleException(e); return false; } } | |
1107 | try { return _meta.supportsPositionedUpdate(); } | |
1108 | catch (SQLException e) { handleException(e); return false; } | |
1109 | 1109 | } |
1110 | 1110 | |
1111 | 1111 | @Override |
1112 | 1112 | public boolean supportsResultSetConcurrency(int type, int concurrency) |
1113 | 1113 | throws SQLException { |
1114 | { try { return _meta.supportsResultSetConcurrency(type, concurrency); } | |
1115 | catch (SQLException e) { handleException(e); return false; } } | |
1114 | try { return _meta.supportsResultSetConcurrency(type, concurrency); } | |
1115 | catch (SQLException e) { handleException(e); return false; } | |
1116 | 1116 | } |
1117 | 1117 | |
1118 | 1118 | @Override |
1119 | 1119 | public boolean supportsResultSetHoldability(int holdability) |
1120 | 1120 | throws SQLException { |
1121 | { try { return _meta.supportsResultSetHoldability(holdability); } | |
1122 | catch (SQLException e) { handleException(e); return false; } } | |
1121 | try { return _meta.supportsResultSetHoldability(holdability); } | |
1122 | catch (SQLException e) { handleException(e); return false; } | |
1123 | 1123 | } |
1124 | 1124 | |
1125 | 1125 | @Override |
1126 | 1126 | public boolean supportsResultSetType(int type) throws SQLException { |
1127 | { try { return _meta.supportsResultSetType(type); } | |
1128 | catch (SQLException e) { handleException(e); return false; } } | |
1127 | try { return _meta.supportsResultSetType(type); } | |
1128 | catch (SQLException e) { handleException(e); return false; } | |
1129 | 1129 | } |
1130 | 1130 | |
1131 | 1131 | @Override |
1132 | 1132 | public boolean supportsSavepoints() throws SQLException { |
1133 | { try { return _meta.supportsSavepoints(); } | |
1134 | catch (SQLException e) { handleException(e); return false; } } | |
1133 | try { return _meta.supportsSavepoints(); } | |
1134 | catch (SQLException e) { handleException(e); return false; } | |
1135 | 1135 | } |
1136 | 1136 | |
1137 | 1137 | @Override |
1138 | 1138 | public boolean supportsSchemasInDataManipulation() throws SQLException { |
1139 | { try { return _meta.supportsSchemasInDataManipulation(); } | |
1140 | catch (SQLException e) { handleException(e); return false; } } | |
1139 | try { return _meta.supportsSchemasInDataManipulation(); } | |
1140 | catch (SQLException e) { handleException(e); return false; } | |
1141 | 1141 | } |
1142 | 1142 | |
1143 | 1143 | @Override |
1144 | 1144 | public boolean supportsSchemasInIndexDefinitions() throws SQLException { |
1145 | { try { return _meta.supportsSchemasInIndexDefinitions(); } | |
1146 | catch (SQLException e) { handleException(e); return false; } } | |
1145 | try { return _meta.supportsSchemasInIndexDefinitions(); } | |
1146 | catch (SQLException e) { handleException(e); return false; } | |
1147 | 1147 | } |
1148 | 1148 | |
1149 | 1149 | @Override |
1150 | 1150 | public boolean supportsSchemasInPrivilegeDefinitions() throws SQLException { |
1151 | { try { return _meta.supportsSchemasInPrivilegeDefinitions(); } | |
1152 | catch (SQLException e) { handleException(e); return false; } } | |
1151 | try { return _meta.supportsSchemasInPrivilegeDefinitions(); } | |
1152 | catch (SQLException e) { handleException(e); return false; } | |
1153 | 1153 | } |
1154 | 1154 | |
1155 | 1155 | @Override |
1156 | 1156 | public boolean supportsSchemasInProcedureCalls() throws SQLException { |
1157 | { try { return _meta.supportsSchemasInProcedureCalls(); } | |
1158 | catch (SQLException e) { handleException(e); return false; } } | |
1157 | try { return _meta.supportsSchemasInProcedureCalls(); } | |
1158 | catch (SQLException e) { handleException(e); return false; } | |
1159 | 1159 | } |
1160 | 1160 | |
1161 | 1161 | @Override |
1162 | 1162 | public boolean supportsSchemasInTableDefinitions() throws SQLException { |
1163 | { try { return _meta.supportsSchemasInTableDefinitions(); } | |
1164 | catch (SQLException e) { handleException(e); return false; } } | |
1163 | try { return _meta.supportsSchemasInTableDefinitions(); } | |
1164 | catch (SQLException e) { handleException(e); return false; } | |
1165 | 1165 | } |
1166 | 1166 | |
1167 | 1167 | @Override |
1168 | 1168 | public boolean supportsSelectForUpdate() throws SQLException { |
1169 | { try { return _meta.supportsSelectForUpdate(); } | |
1170 | catch (SQLException e) { handleException(e); return false; } } | |
1169 | try { return _meta.supportsSelectForUpdate(); } | |
1170 | catch (SQLException e) { handleException(e); return false; } | |
1171 | 1171 | } |
1172 | 1172 | |
1173 | 1173 | @Override |
1174 | 1174 | public boolean supportsStatementPooling() throws SQLException { |
1175 | { try { return _meta.supportsStatementPooling(); } | |
1176 | catch (SQLException e) { handleException(e); return false; } } | |
1175 | try { return _meta.supportsStatementPooling(); } | |
1176 | catch (SQLException e) { handleException(e); return false; } | |
1177 | 1177 | } |
1178 | 1178 | |
1179 | 1179 | @Override |
1180 | 1180 | public boolean supportsStoredProcedures() throws SQLException { |
1181 | { try { return _meta.supportsStoredProcedures(); } | |
1182 | catch (SQLException e) { handleException(e); return false; } } | |
1181 | try { return _meta.supportsStoredProcedures(); } | |
1182 | catch (SQLException e) { handleException(e); return false; } | |
1183 | 1183 | } |
1184 | 1184 | |
1185 | 1185 | @Override |
1186 | 1186 | public boolean supportsSubqueriesInComparisons() throws SQLException { |
1187 | { try { return _meta.supportsSubqueriesInComparisons(); } | |
1188 | catch (SQLException e) { handleException(e); return false; } } | |
1187 | try { return _meta.supportsSubqueriesInComparisons(); } | |
1188 | catch (SQLException e) { handleException(e); return false; } | |
1189 | 1189 | } |
1190 | 1190 | |
1191 | 1191 | @Override |
1192 | 1192 | public boolean supportsSubqueriesInExists() throws SQLException { |
1193 | { try { return _meta.supportsSubqueriesInExists(); } | |
1194 | catch (SQLException e) { handleException(e); return false; } } | |
1193 | try { return _meta.supportsSubqueriesInExists(); } | |
1194 | catch (SQLException e) { handleException(e); return false; } | |
1195 | 1195 | } |
1196 | 1196 | |
1197 | 1197 | @Override |
1198 | 1198 | public boolean supportsSubqueriesInIns() throws SQLException { |
1199 | { try { return _meta.supportsSubqueriesInIns(); } | |
1200 | catch (SQLException e) { handleException(e); return false; } } | |
1199 | try { return _meta.supportsSubqueriesInIns(); } | |
1200 | catch (SQLException e) { handleException(e); return false; } | |
1201 | 1201 | } |
1202 | 1202 | |
1203 | 1203 | @Override |
1204 | 1204 | public boolean supportsSubqueriesInQuantifieds() throws SQLException { |
1205 | { try { return _meta.supportsSubqueriesInQuantifieds(); } | |
1206 | catch (SQLException e) { handleException(e); return false; } } | |
1205 | try { return _meta.supportsSubqueriesInQuantifieds(); } | |
1206 | catch (SQLException e) { handleException(e); return false; } | |
1207 | 1207 | } |
1208 | 1208 | |
1209 | 1209 | @Override |
1210 | 1210 | public boolean supportsTableCorrelationNames() throws SQLException { |
1211 | { try { return _meta.supportsTableCorrelationNames(); } | |
1212 | catch (SQLException e) { handleException(e); return false; } } | |
1211 | try { return _meta.supportsTableCorrelationNames(); } | |
1212 | catch (SQLException e) { handleException(e); return false; } | |
1213 | 1213 | } |
1214 | 1214 | |
1215 | 1215 | @Override |
1216 | 1216 | public boolean supportsTransactionIsolationLevel(int level) |
1217 | 1217 | throws SQLException { |
1218 | { try { return _meta.supportsTransactionIsolationLevel(level); } | |
1219 | catch (SQLException e) { handleException(e); return false; } } | |
1218 | try { return _meta.supportsTransactionIsolationLevel(level); } | |
1219 | catch (SQLException e) { handleException(e); return false; } | |
1220 | 1220 | } |
1221 | 1221 | |
1222 | 1222 | @Override |
1223 | 1223 | public boolean supportsTransactions() throws SQLException { |
1224 | { try { return _meta.supportsTransactions(); } | |
1225 | catch (SQLException e) { handleException(e); return false; } } | |
1224 | try { return _meta.supportsTransactions(); } | |
1225 | catch (SQLException e) { handleException(e); return false; } | |
1226 | 1226 | } |
1227 | 1227 | |
1228 | 1228 | @Override |
1229 | 1229 | public boolean supportsUnion() throws SQLException { |
1230 | { try { return _meta.supportsUnion(); } | |
1231 | catch (SQLException e) { handleException(e); return false; } } | |
1230 | try { return _meta.supportsUnion(); } | |
1231 | catch (SQLException e) { handleException(e); return false; } | |
1232 | 1232 | } |
1233 | 1233 | |
1234 | 1234 | @Override |
1235 | 1235 | public boolean supportsUnionAll() throws SQLException { |
1236 | { try { return _meta.supportsUnionAll(); } | |
1237 | catch (SQLException e) { handleException(e); return false; } } | |
1236 | try { return _meta.supportsUnionAll(); } | |
1237 | catch (SQLException e) { handleException(e); return false; } | |
1238 | 1238 | } |
1239 | 1239 | |
1240 | 1240 | @Override |
1241 | 1241 | public boolean updatesAreDetected(int type) throws SQLException { |
1242 | { try { return _meta.updatesAreDetected(type); } | |
1243 | catch (SQLException e) { handleException(e); return false; } } | |
1242 | try { return _meta.updatesAreDetected(type); } | |
1243 | catch (SQLException e) { handleException(e); return false; } | |
1244 | 1244 | } |
1245 | 1245 | |
1246 | 1246 | @Override |
1247 | 1247 | public boolean usesLocalFilePerTable() throws SQLException { |
1248 | { try { return _meta.usesLocalFilePerTable(); } | |
1249 | catch (SQLException e) { handleException(e); return false; } } | |
1248 | try { return _meta.usesLocalFilePerTable(); } | |
1249 | catch (SQLException e) { handleException(e); return false; } | |
1250 | 1250 | } |
1251 | 1251 | |
1252 | 1252 | @Override |
1253 | 1253 | public boolean usesLocalFiles() throws SQLException { |
1254 | { try { return _meta.usesLocalFiles(); } | |
1255 | catch (SQLException e) { handleException(e); return false; } } | |
1254 | try { return _meta.usesLocalFiles(); } | |
1255 | catch (SQLException e) { handleException(e); return false; } | |
1256 | 1256 | } |
1257 | 1257 | |
1258 | 1258 | /* JDBC_4_ANT_KEY_BEGIN */ |
1281 | 1281 | |
1282 | 1282 | @Override |
1283 | 1283 | public RowIdLifetime getRowIdLifetime() throws SQLException { |
1284 | { try { return _meta.getRowIdLifetime(); } | |
1285 | catch (SQLException e) { handleException(e); throw new AssertionError(); } } | |
1284 | try { return _meta.getRowIdLifetime(); } | |
1285 | catch (SQLException e) { handleException(e); throw new AssertionError(); } | |
1286 | 1286 | } |
1287 | 1287 | |
1288 | 1288 | @Override |
1301 | 1301 | |
1302 | 1302 | @Override |
1303 | 1303 | public boolean autoCommitFailureClosesAllResultSets() throws SQLException { |
1304 | { try { return _meta.autoCommitFailureClosesAllResultSets(); } | |
1305 | catch (SQLException e) { handleException(e); return false; } } | |
1304 | try { return _meta.autoCommitFailureClosesAllResultSets(); } | |
1305 | catch (SQLException e) { handleException(e); return false; } | |
1306 | 1306 | } |
1307 | 1307 | |
1308 | 1308 | @Override |
1309 | 1309 | public boolean supportsStoredFunctionsUsingCallSyntax() throws SQLException { |
1310 | { try { return _meta.supportsStoredFunctionsUsingCallSyntax(); } | |
1311 | catch (SQLException e) { handleException(e); return false; } } | |
1310 | try { return _meta.supportsStoredFunctionsUsingCallSyntax(); } | |
1311 | catch (SQLException e) { handleException(e); return false; } | |
1312 | 1312 | } |
1313 | 1313 | |
1314 | 1314 | @Override |
120 | 120 | |
121 | 121 | /** |
122 | 122 | * If my underlying {@link ResultSet} is not a |
123 | * <tt>DelegatingResultSet</tt>, returns it, | |
123 | * {@code DelegatingResultSet}, returns it, | |
124 | 124 | * otherwise recursively invokes this method on |
125 | 125 | * my delegate. |
126 | 126 | * <p> |
127 | 127 | * Hence this method will return the first |
128 | * delegate that is not a <tt>DelegatingResultSet</tt>, | |
129 | * or <tt>null</tt> when no non-<tt>DelegatingResultSet</tt> | |
128 | * delegate that is not a {@code DelegatingResultSet}, | |
129 | * or {@code null} when no non-{@code DelegatingResultSet} | |
130 | 130 | * delegate can be found by transversing this chain. |
131 | 131 | * <p> |
132 | 132 | * This method is useful when you may have nested |
133 | * <tt>DelegatingResultSet</tt>s, and you want to make | |
133 | * {@code DelegatingResultSet}s, and you want to make | |
134 | 134 | * sure to obtain a "genuine" {@link ResultSet}. |
135 | 135 | */ |
136 | 136 | public ResultSet getInnermostDelegate() { |
73 | 73 | |
74 | 74 | /** |
75 | 75 | * If my underlying {@link Statement} is not a |
76 | * <tt>DelegatingStatement</tt>, returns it, | |
76 | * {@code DelegatingStatement}, returns it, | |
77 | 77 | * otherwise recursively invokes this method on |
78 | 78 | * my delegate. |
79 | 79 | * <p> |
80 | 80 | * Hence this method will return the first |
81 | * delegate that is not a <tt>DelegatingStatement</tt> | |
82 | * or <tt>null</tt> when no non-<tt>DelegatingStatement</tt> | |
81 | * delegate that is not a {@code DelegatingStatement} | |
82 | * or {@code null} when no non-{@code DelegatingStatement} | |
83 | 83 | * delegate can be found by transversing this chain. |
84 | 84 | * <p> |
85 | 85 | * This method is useful when you may have nested |
86 | * <tt>DelegatingStatement</tt>s, and you want to make | |
86 | * {@code DelegatingStatement}s, and you want to make | |
87 | 87 | * sure to obtain a "genuine" {@link Statement}. |
88 | 88 | * @see #getDelegate |
89 | 89 | */ |
51 | 51 | LogFactory.getLog(PoolableConnectionFactory.class); |
52 | 52 | |
53 | 53 | /** |
54 | * Create a new <tt>PoolableConnectionFactory</tt>. | |
54 | * Create a new {@code PoolableConnectionFactory}. | |
55 | 55 | * @param connFactory the {@link ConnectionFactory} from which to obtain |
56 | 56 | * base {@link Connection}s |
57 | 57 | */ |
86 | 86 | |
87 | 87 | /** |
88 | 88 | * Sets the SQL statements I use to initialize newly created {@link Connection}s. |
89 | * Using <tt>null</tt> turns off connection initialization. | |
89 | * Using {@code null} turns off connection initialization. | |
90 | 90 | * @param connectionInitSqls SQL statement to initialize {@link Connection}s. |
91 | 91 | */ |
92 | 92 | public void setConnectionInitSql(Collection<String> connectionInitSqls) { |
363 | 363 | * |
364 | 364 | * @param key ignored |
365 | 365 | * @param p ignored |
366 | * @return <tt>true</tt> | |
366 | * @return {@code true} | |
367 | 367 | */ |
368 | 368 | @Override |
369 | 369 | public boolean validateObject(PStmtKey key, |
77 | 77 | */ |
78 | 78 | private boolean isClosed; |
79 | 79 | |
80 | /** My pool of {*link PreparedStatement}s. */ | |
80 | /** My pool of {@link PreparedStatement}s. */ | |
81 | 81 | private KeyedObjectPool<PStmtKeyCPDS, PoolablePreparedStatement<PStmtKeyCPDS>> pstmtPool = null; |
82 | 82 | |
83 | 83 | /** |
359 | 359 | } |
360 | 360 | |
361 | 361 | /** |
362 | * Create a {*link PooledConnectionImpl.PStmtKey} for the given arguments. | |
362 | * Create a {@link PooledConnectionImpl.PStmtKey} for the given arguments. | |
363 | 363 | */ |
364 | 364 | protected PStmtKeyCPDS createKey(String sql, int autoGeneratedKeys) { |
365 | 365 | return new PStmtKeyCPDS(normalizeSQL(sql), autoGeneratedKeys); |
366 | 366 | } |
367 | 367 | |
368 | 368 | /** |
369 | * Create a {*link PooledConnectionImpl.PStmtKey} for the given arguments. | |
369 | * Create a {@link PooledConnectionImpl.PStmtKey} for the given arguments. | |
370 | 370 | */ |
371 | 371 | protected PStmtKeyCPDS createKey(String sql, int resultSetType, |
372 | 372 | int resultSetConcurrency, int resultSetHoldability) { |
375 | 375 | } |
376 | 376 | |
377 | 377 | /** |
378 | * Create a {*link PooledConnectionImpl.PStmtKey} for the given arguments. | |
378 | * Create a {@link PooledConnectionImpl.PStmtKey} for the given arguments. | |
379 | 379 | */ |
380 | 380 | protected PStmtKeyCPDS createKey(String sql, int columnIndexes[]) { |
381 | 381 | return new PStmtKeyCPDS(normalizeSQL(sql), columnIndexes); |
382 | 382 | } |
383 | 383 | |
384 | 384 | /** |
385 | * Create a {*link PooledConnectionImpl.PStmtKey} for the given arguments. | |
385 | * Create a {@link PooledConnectionImpl.PStmtKey} for the given arguments. | |
386 | 386 | */ |
387 | 387 | protected PStmtKeyCPDS createKey(String sql, String columnNames[]) { |
388 | 388 | return new PStmtKeyCPDS(normalizeSQL(sql), columnNames); |
389 | 389 | } |
390 | 390 | |
391 | 391 | /** |
392 | * Create a {*link PooledConnectionImpl.PStmtKey} for the given arguments. | |
392 | * Create a {@link PooledConnectionImpl.PStmtKey} for the given arguments. | |
393 | 393 | */ |
394 | 394 | protected PStmtKeyCPDS createKey(String sql, int resultSetType, |
395 | 395 | int resultSetConcurrency) { |
398 | 398 | } |
399 | 399 | |
400 | 400 | /** |
401 | * Create a {*link PooledConnectionImpl.PStmtKey} for the given arguments. | |
401 | * Create a {@link PooledConnectionImpl.PStmtKey} for the given arguments. | |
402 | 402 | */ |
403 | 403 | protected PStmtKeyCPDS createKey(String sql) { |
404 | 404 | return new PStmtKeyCPDS(normalizeSQL(sql)); |
413 | 413 | } |
414 | 414 | |
415 | 415 | /** |
416 | * My {*link KeyedPoolableObjectFactory} method for creating | |
417 | * {*link PreparedStatement}s. | |
418 | * @param key the key for the {*link PreparedStatement} to be created | |
416 | * My {@link KeyedPooledObjectFactory} method for creating | |
417 | * {@link PreparedStatement}s. | |
418 | * @param key the key for the {@link PreparedStatement} to be created | |
419 | 419 | */ |
420 | 420 | @Override |
421 | 421 | public PooledObject<PoolablePreparedStatement<PStmtKeyCPDS>> makeObject(PStmtKeyCPDS key) throws Exception { |
443 | 443 | } |
444 | 444 | |
445 | 445 | /** |
446 | * My {*link KeyedPoolableObjectFactory} method for destroying | |
447 | * {*link PreparedStatement}s. | |
446 | * My {@link KeyedPooledObjectFactory} method for destroying | |
447 | * {@link PreparedStatement}s. | |
448 | 448 | * @param key ignored |
449 | * @param p the wrapped {*link PreparedStatement} to be destroyed. | |
449 | * @param p the wrapped {@link PreparedStatement} to be destroyed. | |
450 | 450 | */ |
451 | 451 | @Override |
452 | 452 | public void destroyObject(PStmtKeyCPDS key, |
456 | 456 | } |
457 | 457 | |
458 | 458 | /** |
459 | * My {*link KeyedPoolableObjectFactory} method for validating | |
460 | * {*link PreparedStatement}s. | |
459 | * My {@link KeyedPooledObjectFactory} method for validating | |
460 | * {@link PreparedStatement}s. | |
461 | 461 | * @param key ignored |
462 | 462 | * @param p ignored |
463 | * @return <tt>true</tt> | |
463 | * @return {@code true} | |
464 | 464 | */ |
465 | 465 | @Override |
466 | 466 | public boolean validateObject(PStmtKeyCPDS key, |
469 | 469 | } |
470 | 470 | |
471 | 471 | /** |
472 | * My {*link KeyedPoolableObjectFactory} method for activating | |
473 | * {*link PreparedStatement}s. | |
472 | * My {@link KeyedPooledObjectFactory} method for activating | |
473 | * {@link PreparedStatement}s. | |
474 | 474 | * @param key ignored |
475 | 475 | * @param p ignored |
476 | 476 | */ |
482 | 482 | } |
483 | 483 | |
484 | 484 | /** |
485 | * My {*link KeyedPoolableObjectFactory} method for passivating | |
486 | * {*link PreparedStatement}s. Currently invokes {*link PreparedStatement#clearParameters}. | |
485 | * My {@link KeyedPooledObjectFactory} method for passivating | |
486 | * {@link PreparedStatement}s. Currently invokes {@link PreparedStatement#clearParameters}. | |
487 | 487 | * @param key ignored |
488 | * @param p a wrapped {*link PreparedStatement} | |
488 | * @param p a wrapped {@link PreparedStatement} | |
489 | 489 | */ |
490 | 490 | @Override |
491 | 491 | public void passivateObject(PStmtKeyCPDS key, |
74 | 74 | new ConcurrentHashMap<>(); |
75 | 75 | |
76 | 76 | /** |
77 | * Create a new <tt>PoolableConnectionFactory</tt>. | |
77 | * Create a new {@code PoolableConnectionFactory}. | |
78 | 78 | * |
79 | 79 | * @param cpds the ConnectionPoolDataSource from which to obtain |
80 | 80 | * PooledConnection's |
81 | 81 | * @param validationQuery a query to use to {@link #validateObject |
82 | 82 | * validate} {@link Connection}s. Should return at least one row. |
83 | * May be <tt>null</tt> in which case {@link Connection#isValid(int)} will | |
83 | * May be {@code null} in which case {@link Connection#isValid(int)} will | |
84 | 84 | * be used to validate connections. |
85 | 85 | * @param validationQueryTimeout Timeout in seconds before validation fails |
86 | 86 | * @param rollbackAfterValidation whether a rollback should be issued |
37 | 37 | import org.apache.tomcat.dbcp.pool2.impl.DefaultPooledObject; |
38 | 38 | |
39 | 39 | /** |
40 | * A {*link PoolableObjectFactory} that creates | |
41 | * {*link PoolableConnection}s. | |
40 | * A {@link KeyedPooledObjectFactory} that creates | |
41 | * {@link PoolableConnection}s. | |
42 | 42 | * |
43 | 43 | * @author John D. McNally |
44 | 44 | * @since 2.0 |
73 | 73 | |
74 | 74 | |
75 | 75 | /** |
76 | * Create a new <tt>KeyedPoolableConnectionFactory</tt>. | |
76 | * Create a new {@code KeyedPoolableConnectionFactory}. | |
77 | 77 | * @param cpds the ConnectionPoolDataSource from which to obtain |
78 | 78 | * PooledConnections |
79 | 79 | * @param validationQuery a query to use to {@link #validateObject validate} |
80 | 80 | * {@link Connection}s. Should return at least one row. May be |
81 | * <tt>null</tt> in which case3 {@link Connection#isValid(int)} will be used | |
81 | * {@code null} in which case3 {@link Connection#isValid(int)} will be used | |
82 | 82 | * to validate connections. |
83 | 83 | * @param rollbackAfterValidation whether a rollback should be issued after |
84 | 84 | * {@link #validateObject validating} {@link Connection}s. |
112 | 112 | * To create a {@link org.apache.tomcat.dbcp.dbcp2.PoolingDriver}, we do the same thing, |
113 | 113 | * except that instead of creating a {@link javax.sql.DataSource} on the last line, |
114 | 114 | * we create a {@link org.apache.tomcat.dbcp.dbcp2.PoolingDriver}, and register the |
115 | * <tt>connectionPool</tt> with it. E.g.,: | |
115 | * {@code connectionPool} with it. E.g.,: | |
116 | 116 | * <pre>GenericObjectPool connectionPool = new GenericObjectPool(null); |
117 | 117 | * ConnectionFactory connectionFactory = new DriverManagerConnectionFactory("jdbc:some:connect:string", "username", "password"); |
118 | 118 | * PoolableConnectionFactory poolableConnectionFactory = new PoolableConnectionFactory(connectionFactory,connectionPool,null,null,false,true); |
75 | 75 | /** |
76 | 76 | * Ensures that the instance is safe to be returned by the pool. |
77 | 77 | * <p> |
78 | * The default implementation always returns <tt>true</tt>. | |
78 | * The default implementation always returns {@code true}. | |
79 | 79 | * |
80 | 80 | * @param key the key used when selecting the object |
81 | 81 | * @param p a {@code PooledObject} wrapping the the instance to be validated |
43 | 43 | * @return true if abandoned objects are to be removed by borrowObject |
44 | 44 | */ |
45 | 45 | public boolean getRemoveAbandonedOnBorrow() { |
46 | return (this.removeAbandonedOnBorrow); | |
46 | return this.removeAbandonedOnBorrow; | |
47 | 47 | } |
48 | 48 | |
49 | 49 | /** |
80 | 80 | * @return true if abandoned objects are to be removed by the evictor |
81 | 81 | */ |
82 | 82 | public boolean getRemoveAbandonedOnMaintenance() { |
83 | return (this.removeAbandonedOnMaintenance); | |
83 | return this.removeAbandonedOnMaintenance; | |
84 | 84 | } |
85 | 85 | |
86 | 86 | /** |
113 | 113 | * @return the abandoned object timeout in seconds |
114 | 114 | */ |
115 | 115 | public int getRemoveAbandonedTimeout() { |
116 | return (this.removeAbandonedTimeout); | |
116 | return this.removeAbandonedTimeout; | |
117 | 117 | } |
118 | 118 | |
119 | 119 | /** |
151 | 151 | * |
152 | 152 | */ |
153 | 153 | public boolean getLogAbandoned() { |
154 | return (this.logAbandoned); | |
154 | return this.logAbandoned; | |
155 | 155 | } |
156 | 156 | |
157 | 157 | /** |
19 | 19 | import java.io.StringWriter; |
20 | 20 | import java.io.Writer; |
21 | 21 | import java.lang.management.ManagementFactory; |
22 | import java.lang.ref.WeakReference; | |
22 | 23 | import java.util.Iterator; |
23 | 24 | import java.util.TimerTask; |
24 | 25 | import java.util.concurrent.atomic.AtomicLong; |
89 | 90 | private Evictor evictor = null; // @GuardedBy("evictionLock") |
90 | 91 | Iterator<PooledObject<T>> evictionIterator = null; // @GuardedBy("evictionLock") |
91 | 92 | /* |
92 | * Class loader for evictor thread to use since in a J2EE or similar | |
93 | * environment the context class loader for the evictor thread may have | |
94 | * visibility of the correct factory. See POOL-161. | |
95 | */ | |
96 | private final ClassLoader factoryClassLoader; | |
93 | * Class loader for evictor thread to use since, in a JavaEE or similar | |
94 | * environment, the context class loader for the evictor thread may not have | |
95 | * visibility of the correct factory. See POOL-161. Uses a weak reference to | |
96 | * avoid potential memory leaks if the Pool is discarded rather than closed. | |
97 | */ | |
98 | private final WeakReference<ClassLoader> factoryClassLoader; | |
97 | 99 | |
98 | 100 | |
99 | 101 | // Monitoring (primarily JMX) attributes |
110 | 112 | private final StatsStore waitTimes = new StatsStore(MEAN_TIMING_STATS_CACHE_SIZE); |
111 | 113 | private final Object maxBorrowWaitTimeMillisLock = new Object(); |
112 | 114 | private volatile long maxBorrowWaitTimeMillis = 0; // @GuardedBy("maxBorrowWaitTimeMillisLock") |
113 | private SwallowedExceptionListener swallowedExceptionListener = null; | |
115 | private volatile SwallowedExceptionListener swallowedExceptionListener = null; | |
114 | 116 | |
115 | 117 | |
116 | 118 | /** |
133 | 135 | // Populate the creation stack trace |
134 | 136 | this.creationStackTrace = getStackTrace(new Exception()); |
135 | 137 | |
136 | // save the current CCL to be used later by the evictor Thread | |
137 | factoryClassLoader = Thread.currentThread().getContextClassLoader(); | |
138 | // save the current TCCL (if any) to be used later by the evictor Thread | |
139 | ClassLoader cl = Thread.currentThread().getContextClassLoader(); | |
140 | if (cl == null) { | |
141 | factoryClassLoader = null; | |
142 | } else { | |
143 | factoryClassLoader = new WeakReference<>(cl); | |
144 | } | |
145 | ||
138 | 146 | fairness = config.getFairness(); |
139 | 147 | } |
140 | 148 | |
585 | 593 | public final void setEvictionPolicyClassName( |
586 | 594 | String evictionPolicyClassName) { |
587 | 595 | try { |
588 | Class<?> clazz = Class.forName(evictionPolicyClassName); | |
596 | Class<?> clazz = Class.forName(evictionPolicyClassName, true, | |
597 | Thread.currentThread().getContextClassLoader()); | |
589 | 598 | Object policy = clazz.newInstance(); |
590 | 599 | if (policy instanceof EvictionPolicy<?>) { |
591 | 600 | @SuppressWarnings("unchecked") // safe, because we just checked the class |
993 | 1002 | ClassLoader savedClassLoader = |
994 | 1003 | Thread.currentThread().getContextClassLoader(); |
995 | 1004 | try { |
996 | // Set the class loader for the factory | |
997 | Thread.currentThread().setContextClassLoader( | |
998 | factoryClassLoader); | |
1005 | if (factoryClassLoader != null) { | |
1006 | // Set the class loader for the factory | |
1007 | ClassLoader cl = factoryClassLoader.get(); | |
1008 | if (cl == null) { | |
1009 | // The pool has been dereferenced and the class loader | |
1010 | // GC'd. Cancel this timer so the pool can be GC'd as | |
1011 | // well. | |
1012 | cancel(); | |
1013 | return; | |
1014 | } | |
1015 | Thread.currentThread().setContextClassLoader(cl); | |
1016 | } | |
999 | 1017 | |
1000 | 1018 | // Evict from the pool |
1001 | 1019 | try { |
38 | 38 | class EvictionTimer { |
39 | 39 | |
40 | 40 | /** Timer instance */ |
41 | private static Timer _timer; //@GuardedBy("this") | |
41 | private static Timer _timer; //@GuardedBy("EvictionTimer.class") | |
42 | 42 | |
43 | 43 | /** Static usage count tracker */ |
44 | private static int _usageCount; //@GuardedBy("this") | |
44 | private static int _usageCount; //@GuardedBy("EvictionTimer.class") | |
45 | 45 | |
46 | 46 | /** Prevent instantiation */ |
47 | 47 | private EvictionTimer() { |
66 | 66 | try { |
67 | 67 | AccessController.doPrivileged(new PrivilegedSetTccl( |
68 | 68 | EvictionTimer.class.getClassLoader())); |
69 | _timer = new Timer("commons-pool-EvictionTimer", true); | |
69 | _timer = AccessController.doPrivileged(new PrivilegedNewEvictionTimer()); | |
70 | 70 | } finally { |
71 | 71 | AccessController.doPrivileged(new PrivilegedSetTccl(ccl)); |
72 | 72 | } |
128 | 128 | } |
129 | 129 | } |
130 | 130 | |
131 | /** | |
132 | * {@link PrivilegedAction} used to create a new Timer. Creating the timer | |
133 | * with a privileged action means the associated Thread does not inherit the | |
134 | * current access control context. In a container environment, inheriting | |
135 | * the current access control context is likely to result in retaining a | |
136 | * reference to the thread context class loader which would be a memory | |
137 | * leak. | |
138 | */ | |
139 | private static class PrivilegedNewEvictionTimer implements PrivilegedAction<Timer> { | |
140 | ||
141 | /** | |
142 | * {@inheritDoc} | |
143 | */ | |
144 | @Override | |
145 | public Timer run() { | |
146 | return new Timer("commons-pool-EvictionTimer", true); | |
147 | } | |
148 | } | |
131 | 149 | } |
106 | 106 | |
107 | 107 | setConfig(config); |
108 | 108 | |
109 | startEvictor(getMinEvictableIdleTimeMillis()); | |
109 | startEvictor(getTimeBetweenEvictionRunsMillis()); | |
110 | 110 | } |
111 | 111 | |
112 | 112 | /** |
349 | 349 | if (blockWhenExhausted) { |
350 | 350 | p = objectDeque.getIdleObjects().pollFirst(); |
351 | 351 | if (p == null) { |
352 | create = true; | |
353 | 352 | p = create(key); |
353 | if (p != null) { | |
354 | create = true; | |
355 | } | |
354 | 356 | } |
355 | 357 | if (p == null) { |
356 | 358 | if (borrowMaxWaitMillis < 0) { |
370 | 372 | } else { |
371 | 373 | p = objectDeque.getIdleObjects().pollFirst(); |
372 | 374 | if (p == null) { |
373 | create = true; | |
374 | 375 | p = create(key); |
376 | if (p != null) { | |
377 | create = true; | |
378 | } | |
375 | 379 | } |
376 | 380 | if (p == null) { |
377 | 381 | throw new NoSuchElementException("Pool exhausted"); |
929 | 933 | continue; |
930 | 934 | } |
931 | 935 | |
932 | if (evictionPolicy.evict(evictionConfig, underTest, | |
933 | poolMap.get(evictionKey).getIdleObjects().size())) { | |
936 | // User provided eviction policy could throw all sorts of crazy | |
937 | // exceptions. Protect against such an exception killing the | |
938 | // eviction thread. | |
939 | boolean evict; | |
940 | try { | |
941 | evict = evictionPolicy.evict(evictionConfig, underTest, | |
942 | poolMap.get(evictionKey).getIdleObjects().size()); | |
943 | } catch (Throwable t) { | |
944 | // Slightly convoluted as SwallowedExceptionListener uses | |
945 | // Exception rather than Throwable | |
946 | PoolUtils.checkRethrow(t); | |
947 | swallowException(new Exception(t)); | |
948 | // Don't evict on error conditions | |
949 | evict = false; | |
950 | } | |
951 | ||
952 | if (evict) { | |
934 | 953 | destroy(evictionKey, underTest, true); |
935 | 954 | destroyedByEvictorCount.incrementAndGet(); |
936 | 955 | } else { |
428 | 428 | if (blockWhenExhausted) { |
429 | 429 | p = idleObjects.pollFirst(); |
430 | 430 | if (p == null) { |
431 | create = true; | |
432 | 431 | p = create(); |
432 | if (p != null) { | |
433 | create = true; | |
434 | } | |
433 | 435 | } |
434 | 436 | if (p == null) { |
435 | 437 | if (borrowMaxWaitMillis < 0) { |
449 | 451 | } else { |
450 | 452 | p = idleObjects.pollFirst(); |
451 | 453 | if (p == null) { |
452 | create = true; | |
453 | 454 | p = create(); |
455 | if (p != null) { | |
456 | create = true; | |
457 | } | |
454 | 458 | } |
455 | 459 | if (p == null) { |
456 | 460 | throw new NoSuchElementException("Pool exhausted"); |
774 | 778 | continue; |
775 | 779 | } |
776 | 780 | |
777 | if (evictionPolicy.evict(evictionConfig, underTest, | |
778 | idleObjects.size())) { | |
781 | // User provided eviction policy could throw all sorts of crazy | |
782 | // exceptions. Protect against such an exception killing the | |
783 | // eviction thread. | |
784 | boolean evict; | |
785 | try { | |
786 | evict = evictionPolicy.evict(evictionConfig, underTest, | |
787 | idleObjects.size()); | |
788 | } catch (Throwable t) { | |
789 | // Slightly convoluted as SwallowedExceptionListener uses | |
790 | // Exception rather than Throwable | |
791 | PoolUtils.checkRethrow(t); | |
792 | swallowException(new Exception(t)); | |
793 | // Don't evict on error conditions | |
794 | evict = false; | |
795 | } | |
796 | ||
797 | if (evict) { | |
779 | 798 | destroy(underTest); |
780 | 799 | destroyedByEvictorCount.incrementAndGet(); |
781 | 800 | } else { |
129 | 129 | * Invariant: (first == null && last == null) || |
130 | 130 | * (first.prev == null && first.item != null) |
131 | 131 | */ |
132 | private transient Node<E> first; | |
132 | private transient Node<E> first; // @GuardedBy("lock") | |
133 | 133 | |
134 | 134 | /** |
135 | 135 | * Pointer to last node. |
136 | 136 | * Invariant: (first == null && last == null) || |
137 | 137 | * (last.next == null && last.item != null) |
138 | 138 | */ |
139 | private transient Node<E> last; | |
139 | private transient Node<E> last; // @GuardedBy("lock") | |
140 | 140 | |
141 | 141 | /** Number of items in the deque */ |
142 | private transient int count; | |
142 | private transient int count; // @GuardedBy("lock") | |
143 | 143 | |
144 | 144 | /** Maximum number of items in the deque */ |
145 | 145 | private final int capacity; |
191 | 191 | * @throws IllegalArgumentException if {@code capacity} is less than 1 |
192 | 192 | */ |
193 | 193 | public LinkedBlockingDeque(int capacity, boolean fairness) { |
194 | if (capacity <= 0) throw new IllegalArgumentException(); | |
194 | if (capacity <= 0) { | |
195 | throw new IllegalArgumentException(); | |
196 | } | |
195 | 197 | this.capacity = capacity; |
196 | 198 | lock = new InterruptibleReentrantLock(fairness); |
197 | 199 | notEmpty = lock.newCondition(); |
213 | 215 | lock.lock(); // Never contended, but necessary for visibility |
214 | 216 | try { |
215 | 217 | for (E e : c) { |
216 | if (e == null) | |
218 | if (e == null) { | |
217 | 219 | throw new NullPointerException(); |
218 | if (!linkLast(e)) | |
220 | } | |
221 | if (!linkLast(e)) { | |
219 | 222 | throw new IllegalStateException("Deque full"); |
223 | } | |
220 | 224 | } |
221 | 225 | } finally { |
222 | 226 | lock.unlock(); |
235 | 239 | */ |
236 | 240 | private boolean linkFirst(E e) { |
237 | 241 | // assert lock.isHeldByCurrentThread(); |
238 | if (count >= capacity) | |
242 | if (count >= capacity) { | |
239 | 243 | return false; |
244 | } | |
240 | 245 | Node<E> f = first; |
241 | 246 | Node<E> x = new Node<>(e, null, f); |
242 | 247 | first = x; |
243 | if (last == null) | |
248 | if (last == null) { | |
244 | 249 | last = x; |
245 | else | |
250 | } else { | |
246 | 251 | f.prev = x; |
252 | } | |
247 | 253 | ++count; |
248 | 254 | notEmpty.signal(); |
249 | 255 | return true; |
258 | 264 | */ |
259 | 265 | private boolean linkLast(E e) { |
260 | 266 | // assert lock.isHeldByCurrentThread(); |
261 | if (count >= capacity) | |
267 | if (count >= capacity) { | |
262 | 268 | return false; |
269 | } | |
263 | 270 | Node<E> l = last; |
264 | 271 | Node<E> x = new Node<>(e, l, null); |
265 | 272 | last = x; |
266 | if (first == null) | |
273 | if (first == null) { | |
267 | 274 | first = x; |
268 | else | |
275 | } else { | |
269 | 276 | l.next = x; |
277 | } | |
270 | 278 | ++count; |
271 | 279 | notEmpty.signal(); |
272 | 280 | return true; |
280 | 288 | private E unlinkFirst() { |
281 | 289 | // assert lock.isHeldByCurrentThread(); |
282 | 290 | Node<E> f = first; |
283 | if (f == null) | |
291 | if (f == null) { | |
284 | 292 | return null; |
293 | } | |
285 | 294 | Node<E> n = f.next; |
286 | 295 | E item = f.item; |
287 | 296 | f.item = null; |
288 | 297 | f.next = f; // help GC |
289 | 298 | first = n; |
290 | if (n == null) | |
299 | if (n == null) { | |
291 | 300 | last = null; |
292 | else | |
301 | } else { | |
293 | 302 | n.prev = null; |
303 | } | |
294 | 304 | --count; |
295 | 305 | notFull.signal(); |
296 | 306 | return item; |
304 | 314 | private E unlinkLast() { |
305 | 315 | // assert lock.isHeldByCurrentThread(); |
306 | 316 | Node<E> l = last; |
307 | if (l == null) | |
317 | if (l == null) { | |
308 | 318 | return null; |
319 | } | |
309 | 320 | Node<E> p = l.prev; |
310 | 321 | E item = l.item; |
311 | 322 | l.item = null; |
312 | 323 | l.prev = l; // help GC |
313 | 324 | last = p; |
314 | if (p == null) | |
325 | if (p == null) { | |
315 | 326 | first = null; |
316 | else | |
327 | } else { | |
317 | 328 | p.next = null; |
329 | } | |
318 | 330 | --count; |
319 | 331 | notFull.signal(); |
320 | 332 | return item; |
351 | 363 | */ |
352 | 364 | @Override |
353 | 365 | public void addFirst(E e) { |
354 | if (!offerFirst(e)) | |
366 | if (!offerFirst(e)) { | |
355 | 367 | throw new IllegalStateException("Deque full"); |
368 | } | |
356 | 369 | } |
357 | 370 | |
358 | 371 | /** |
360 | 373 | */ |
361 | 374 | @Override |
362 | 375 | public void addLast(E e) { |
363 | if (!offerLast(e)) | |
376 | if (!offerLast(e)) { | |
364 | 377 | throw new IllegalStateException("Deque full"); |
378 | } | |
365 | 379 | } |
366 | 380 | |
367 | 381 | /** |
369 | 383 | */ |
370 | 384 | @Override |
371 | 385 | public boolean offerFirst(E e) { |
372 | if (e == null) throw new NullPointerException(); | |
386 | if (e == null) { | |
387 | throw new NullPointerException(); | |
388 | } | |
373 | 389 | lock.lock(); |
374 | 390 | try { |
375 | 391 | return linkFirst(e); |
383 | 399 | */ |
384 | 400 | @Override |
385 | 401 | public boolean offerLast(E e) { |
386 | if (e == null) throw new NullPointerException(); | |
402 | if (e == null) { | |
403 | throw new NullPointerException(); | |
404 | } | |
387 | 405 | lock.lock(); |
388 | 406 | try { |
389 | 407 | return linkLast(e); |
402 | 420 | * @throws InterruptedException |
403 | 421 | */ |
404 | 422 | public void putFirst(E e) throws InterruptedException { |
405 | if (e == null) throw new NullPointerException(); | |
406 | lock.lock(); | |
407 | try { | |
408 | while (!linkFirst(e)) | |
423 | if (e == null) { | |
424 | throw new NullPointerException(); | |
425 | } | |
426 | lock.lock(); | |
427 | try { | |
428 | while (!linkFirst(e)) { | |
409 | 429 | notFull.await(); |
430 | } | |
410 | 431 | } finally { |
411 | 432 | lock.unlock(); |
412 | 433 | } |
422 | 443 | * @throws InterruptedException |
423 | 444 | */ |
424 | 445 | public void putLast(E e) throws InterruptedException { |
425 | if (e == null) throw new NullPointerException(); | |
426 | lock.lock(); | |
427 | try { | |
428 | while (!linkLast(e)) | |
446 | if (e == null) { | |
447 | throw new NullPointerException(); | |
448 | } | |
449 | lock.lock(); | |
450 | try { | |
451 | while (!linkLast(e)) { | |
429 | 452 | notFull.await(); |
453 | } | |
430 | 454 | } finally { |
431 | 455 | lock.unlock(); |
432 | 456 | } |
447 | 471 | */ |
448 | 472 | public boolean offerFirst(E e, long timeout, TimeUnit unit) |
449 | 473 | throws InterruptedException { |
450 | if (e == null) throw new NullPointerException(); | |
474 | if (e == null) { | |
475 | throw new NullPointerException(); | |
476 | } | |
451 | 477 | long nanos = unit.toNanos(timeout); |
452 | 478 | lock.lockInterruptibly(); |
453 | 479 | try { |
454 | 480 | while (!linkFirst(e)) { |
455 | if (nanos <= 0) | |
481 | if (nanos <= 0) { | |
456 | 482 | return false; |
483 | } | |
457 | 484 | nanos = notFull.awaitNanos(nanos); |
458 | 485 | } |
459 | 486 | return true; |
477 | 504 | */ |
478 | 505 | public boolean offerLast(E e, long timeout, TimeUnit unit) |
479 | 506 | throws InterruptedException { |
480 | if (e == null) throw new NullPointerException(); | |
507 | if (e == null) { | |
508 | throw new NullPointerException(); | |
509 | } | |
481 | 510 | long nanos = unit.toNanos(timeout); |
482 | 511 | lock.lockInterruptibly(); |
483 | 512 | try { |
484 | 513 | while (!linkLast(e)) { |
485 | if (nanos <= 0) | |
514 | if (nanos <= 0) { | |
486 | 515 | return false; |
516 | } | |
487 | 517 | nanos = notFull.awaitNanos(nanos); |
488 | 518 | } |
489 | 519 | return true; |
498 | 528 | @Override |
499 | 529 | public E removeFirst() { |
500 | 530 | E x = pollFirst(); |
501 | if (x == null) throw new NoSuchElementException(); | |
531 | if (x == null) { | |
532 | throw new NoSuchElementException(); | |
533 | } | |
502 | 534 | return x; |
503 | 535 | } |
504 | 536 | |
508 | 540 | @Override |
509 | 541 | public E removeLast() { |
510 | 542 | E x = pollLast(); |
511 | if (x == null) throw new NoSuchElementException(); | |
543 | if (x == null) { | |
544 | throw new NoSuchElementException(); | |
545 | } | |
512 | 546 | return x; |
513 | 547 | } |
514 | 548 | |
543 | 577 | lock.lock(); |
544 | 578 | try { |
545 | 579 | E x; |
546 | while ( (x = unlinkFirst()) == null) | |
580 | while ( (x = unlinkFirst()) == null) { | |
547 | 581 | notEmpty.await(); |
582 | } | |
548 | 583 | return x; |
549 | 584 | } finally { |
550 | 585 | lock.unlock(); |
562 | 597 | lock.lock(); |
563 | 598 | try { |
564 | 599 | E x; |
565 | while ( (x = unlinkLast()) == null) | |
600 | while ( (x = unlinkLast()) == null) { | |
566 | 601 | notEmpty.await(); |
602 | } | |
567 | 603 | return x; |
568 | 604 | } finally { |
569 | 605 | lock.unlock(); |
587 | 623 | try { |
588 | 624 | E x; |
589 | 625 | while ( (x = unlinkFirst()) == null) { |
590 | if (nanos <= 0) | |
626 | if (nanos <= 0) { | |
591 | 627 | return null; |
628 | } | |
592 | 629 | nanos = notEmpty.awaitNanos(nanos); |
593 | 630 | } |
594 | 631 | return x; |
614 | 651 | try { |
615 | 652 | E x; |
616 | 653 | while ( (x = unlinkLast()) == null) { |
617 | if (nanos <= 0) | |
654 | if (nanos <= 0) { | |
618 | 655 | return null; |
656 | } | |
619 | 657 | nanos = notEmpty.awaitNanos(nanos); |
620 | 658 | } |
621 | 659 | return x; |
630 | 668 | @Override |
631 | 669 | public E getFirst() { |
632 | 670 | E x = peekFirst(); |
633 | if (x == null) throw new NoSuchElementException(); | |
671 | if (x == null) { | |
672 | throw new NoSuchElementException(); | |
673 | } | |
634 | 674 | return x; |
635 | 675 | } |
636 | 676 | |
640 | 680 | @Override |
641 | 681 | public E getLast() { |
642 | 682 | E x = peekLast(); |
643 | if (x == null) throw new NoSuchElementException(); | |
683 | if (x == null) { | |
684 | throw new NoSuchElementException(); | |
685 | } | |
644 | 686 | return x; |
645 | 687 | } |
646 | 688 | |
648 | 690 | public E peekFirst() { |
649 | 691 | lock.lock(); |
650 | 692 | try { |
651 | return (first == null) ? null : first.item; | |
693 | return first == null ? null : first.item; | |
652 | 694 | } finally { |
653 | 695 | lock.unlock(); |
654 | 696 | } |
658 | 700 | public E peekLast() { |
659 | 701 | lock.lock(); |
660 | 702 | try { |
661 | return (last == null) ? null : last.item; | |
703 | return last == null ? null : last.item; | |
662 | 704 | } finally { |
663 | 705 | lock.unlock(); |
664 | 706 | } |
666 | 708 | |
667 | 709 | @Override |
668 | 710 | public boolean removeFirstOccurrence(Object o) { |
669 | if (o == null) return false; | |
711 | if (o == null) { | |
712 | return false; | |
713 | } | |
670 | 714 | lock.lock(); |
671 | 715 | try { |
672 | 716 | for (Node<E> p = first; p != null; p = p.next) { |
683 | 727 | |
684 | 728 | @Override |
685 | 729 | public boolean removeLastOccurrence(Object o) { |
686 | if (o == null) return false; | |
730 | if (o == null) { | |
731 | return false; | |
732 | } | |
687 | 733 | lock.lock(); |
688 | 734 | try { |
689 | 735 | for (Node<E> p = last; p != null; p = p.prev) { |
873 | 919 | * @throws IllegalArgumentException |
874 | 920 | */ |
875 | 921 | public int drainTo(Collection<? super E> c, int maxElements) { |
876 | if (c == null) | |
922 | if (c == null) { | |
877 | 923 | throw new NullPointerException(); |
878 | if (c == this) | |
924 | } | |
925 | if (c == this) { | |
879 | 926 | throw new IllegalArgumentException(); |
927 | } | |
880 | 928 | lock.lock(); |
881 | 929 | try { |
882 | 930 | int n = Math.min(maxElements, count); |
954 | 1002 | */ |
955 | 1003 | @Override |
956 | 1004 | public boolean contains(Object o) { |
957 | if (o == null) return false; | |
958 | lock.lock(); | |
959 | try { | |
960 | for (Node<E> p = first; p != null; p = p.next) | |
961 | if (o.equals(p.item)) | |
1005 | if (o == null) { | |
1006 | return false; | |
1007 | } | |
1008 | lock.lock(); | |
1009 | try { | |
1010 | for (Node<E> p = first; p != null; p = p.next) { | |
1011 | if (o.equals(p.item)) { | |
962 | 1012 | return true; |
1013 | } | |
1014 | } | |
963 | 1015 | return false; |
964 | 1016 | } finally { |
965 | 1017 | lock.unlock(); |
1026 | 1078 | try { |
1027 | 1079 | Object[] a = new Object[count]; |
1028 | 1080 | int k = 0; |
1029 | for (Node<E> p = first; p != null; p = p.next) | |
1081 | for (Node<E> p = first; p != null; p = p.next) { | |
1030 | 1082 | a[k++] = p.item; |
1083 | } | |
1031 | 1084 | return a; |
1032 | 1085 | } finally { |
1033 | 1086 | lock.unlock(); |
1047 | 1100 | (a.getClass().getComponentType(), count); |
1048 | 1101 | } |
1049 | 1102 | int k = 0; |
1050 | for (Node<E> p = first; p != null; p = p.next) | |
1103 | for (Node<E> p = first; p != null; p = p.next) { | |
1051 | 1104 | a[k++] = (T)p.item; |
1105 | } | |
1052 | 1106 | if (a.length > k) { |
1053 | 1107 | a[k] = null; |
1054 | 1108 | } |
1164 | 1218 | lock.lock(); |
1165 | 1219 | try { |
1166 | 1220 | next = firstNode(); |
1167 | nextItem = (next == null) ? null : next.item; | |
1221 | nextItem = next == null ? null : next.item; | |
1168 | 1222 | } finally { |
1169 | 1223 | lock.unlock(); |
1170 | 1224 | } |
1183 | 1237 | } else { |
1184 | 1238 | // Skip over removed nodes. |
1185 | 1239 | // May be necessary if multiple interior Nodes are removed. |
1186 | while (s != null && s.item == null) | |
1240 | while (s != null && s.item == null) { | |
1187 | 1241 | s = nextNode(s); |
1242 | } | |
1188 | 1243 | next = s; |
1189 | 1244 | } |
1190 | nextItem = (next == null) ? null : next.item; | |
1245 | nextItem = next == null ? null : next.item; | |
1191 | 1246 | } finally { |
1192 | 1247 | lock.unlock(); |
1193 | 1248 | } |
1200 | 1255 | |
1201 | 1256 | @Override |
1202 | 1257 | public E next() { |
1203 | if (next == null) | |
1258 | if (next == null) { | |
1204 | 1259 | throw new NoSuchElementException(); |
1260 | } | |
1205 | 1261 | lastRet = next; |
1206 | 1262 | E x = nextItem; |
1207 | 1263 | advance(); |
1211 | 1267 | @Override |
1212 | 1268 | public void remove() { |
1213 | 1269 | Node<E> n = lastRet; |
1214 | if (n == null) | |
1270 | if (n == null) { | |
1215 | 1271 | throw new IllegalStateException(); |
1272 | } | |
1216 | 1273 | lastRet = null; |
1217 | 1274 | lock.lock(); |
1218 | 1275 | try { |
1219 | if (n.item != null) | |
1276 | if (n.item != null) { | |
1220 | 1277 | unlink(n); |
1278 | } | |
1221 | 1279 | } finally { |
1222 | 1280 | lock.unlock(); |
1223 | 1281 | } |
1254 | 1312 | // Write out capacity and any hidden stuff |
1255 | 1313 | s.defaultWriteObject(); |
1256 | 1314 | // Write out all elements in the proper order. |
1257 | for (Node<E> p = first; p != null; p = p.next) | |
1315 | for (Node<E> p = first; p != null; p = p.next) { | |
1258 | 1316 | s.writeObject(p.item); |
1317 | } | |
1259 | 1318 | // Use trailing null as sentinel |
1260 | 1319 | s.writeObject(null); |
1261 | 1320 | } finally { |
1278 | 1337 | for (;;) { |
1279 | 1338 | @SuppressWarnings("unchecked") |
1280 | 1339 | E item = (E)s.readObject(); |
1281 | if (item == null) | |
1340 | if (item == null) { | |
1282 | 1341 | break; |
1342 | } | |
1283 | 1343 | add(item); |
1284 | 1344 | } |
1285 | 1345 | } |
52 | 52 | private int numActive = 0; // @GuardedBy("this") |
53 | 53 | |
54 | 54 | /** Total number of instances that have been destroyed */ |
55 | private long destroyCount = 0; | |
55 | private long destroyCount = 0; // @GuardedBy("this") | |
56 | ||
56 | 57 | |
57 | 58 | /** Total number of instances that have been created */ |
58 | private long createCount = 0; | |
59 | private long createCount = 0; // @GuardedBy("this") | |
59 | 60 | |
60 | 61 | /** Idle references - waiting to be borrowed */ |
61 | 62 | private final LinkedBlockingDeque<PooledSoftReference<T>> idleReferences = |
90 | 90 | "CONSTANT_NameAndType", "", "", "CONSTANT_MethodHandle", |
91 | 91 | "CONSTANT_MethodType", "", "CONSTANT_InvokeDynamic" }; |
92 | 92 | |
93 | ||
94 | /** Attributes and their corresponding names. | |
95 | */ | |
96 | public static final byte ATTR_UNKNOWN = -1; | |
97 | public static final byte ATTR_SOURCE_FILE = 0; | |
98 | public static final byte ATTR_CONSTANT_VALUE = 1; | |
99 | public static final byte ATTR_CODE = 2; | |
100 | public static final byte ATTR_EXCEPTIONS = 3; | |
101 | public static final byte ATTR_LINE_NUMBER_TABLE = 4; | |
102 | public static final byte ATTR_LOCAL_VARIABLE_TABLE = 5; | |
103 | public static final byte ATTR_INNER_CLASSES = 6; | |
104 | public static final byte ATTR_SYNTHETIC = 7; | |
105 | public static final byte ATTR_DEPRECATED = 8; | |
106 | public static final byte ATTR_PMG = 9; | |
107 | public static final byte ATTR_SIGNATURE = 10; | |
108 | public static final byte ATTR_STACK_MAP = 11; | |
109 | public static final byte ATTR_RUNTIME_VISIBLE_ANNOTATIONS = 12; | |
110 | public static final byte ATTR_RUNTIME_INVISIBLE_ANNOTATIONS = 13; | |
111 | public static final byte ATTR_RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS = 14; | |
112 | public static final byte ATTR_RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS = 15; | |
113 | public static final byte ATTR_ANNOTATION_DEFAULT = 16; | |
114 | public static final byte ATTR_LOCAL_VARIABLE_TYPE_TABLE = 17; | |
115 | public static final byte ATTR_ENCLOSING_METHOD = 18; | |
116 | public static final byte ATTR_STACK_MAP_TABLE = 19; | |
117 | public static final byte ATTR_BOOTSTRAP_METHODS = 20; | |
118 | public static final byte ATTR_METHOD_PARAMETERS = 21; | |
119 | ||
120 | public static final short KNOWN_ATTRIBUTES = 22; | |
121 | ||
122 | // TOFO: FIXXXXX | |
123 | public static final String[] ATTRIBUTE_NAMES = { | |
124 | "SourceFile", "ConstantValue", "Code", "Exceptions", | |
125 | "LineNumberTable", "LocalVariableTable", | |
126 | "InnerClasses", "Synthetic", "Deprecated", | |
127 | "PMGClass", "Signature", "StackMap", | |
128 | "RuntimeVisibleAnnotations", "RuntimeInvisibleAnnotations", | |
129 | "RuntimeVisibleParameterAnnotations", "RuntimeInvisibleParameterAnnotations", | |
130 | "AnnotationDefault", "LocalVariableTypeTable", "EnclosingMethod", "StackMapTable", | |
131 | "BootstrapMethods", "MethodParameters" | |
132 | }; | |
133 | ||
134 | /** Constants used in the StackMap attribute. | |
135 | */ | |
136 | public static final byte ITEM_Bogus = 0; | |
137 | public static final byte ITEM_Object = 7; | |
138 | public static final byte ITEM_NewObject = 8; | |
139 | ||
140 | /** Constants used to identify StackMapEntry types. | |
141 | * | |
142 | * For those types which can specify a range, the | |
143 | * constant names the lowest value. | |
144 | */ | |
145 | public static final int SAME_FRAME = 0; | |
146 | public static final int SAME_LOCALS_1_STACK_ITEM_FRAME = 64; | |
147 | public static final int SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED = 247; | |
148 | public static final int CHOP_FRAME = 248; | |
149 | public static final int SAME_FRAME_EXTENDED = 251; | |
150 | public static final int APPEND_FRAME = 252; | |
151 | public static final int FULL_FRAME = 255; | |
152 | ||
153 | /** Constants that define the maximum value of | |
154 | * those constants which store ranges. */ | |
155 | ||
156 | public static final int SAME_FRAME_MAX = 63; | |
157 | public static final int SAME_LOCALS_1_STACK_ITEM_FRAME_MAX = 127; | |
158 | public static final int CHOP_FRAME_MAX = 250; | |
159 | public static final int APPEND_FRAME_MAX = 254; | |
160 | 93 | } |
0 | /* | |
1 | * Licensed to the Apache Software Foundation (ASF) under one or more | |
2 | * contributor license agreements. See the NOTICE file distributed with | |
3 | * this work for additional information regarding copyright ownership. | |
4 | * The ASF licenses this file to You under the Apache License, Version 2.0 | |
5 | * (the "License"); you may not use this file except in compliance with | |
6 | * the License. You may obtain a copy of the License at | |
7 | * | |
8 | * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
15 | * | |
16 | */ | |
17 | package org.apache.tomcat.util.bcel.classfile; | |
18 | ||
19 | /** | |
20 | * Super class for all objects that have modifiers like private, final, ... | |
21 | * I.e. classes, fields, and methods. | |
22 | * | |
23 | * @author <A HREF="mailto:m.dahm@gmx.de">M. Dahm</A> | |
24 | */ | |
25 | public abstract class AccessFlags implements java.io.Serializable { | |
26 | ||
27 | private static final long serialVersionUID = 2548932939969293935L; | |
28 | protected int access_flags; | |
29 | ||
30 | ||
31 | public AccessFlags() { | |
32 | } | |
33 | ||
34 | /** | |
35 | * @return Access flags of the object aka. "modifiers". | |
36 | */ | |
37 | public final int getAccessFlags() { | |
38 | return access_flags; | |
39 | } | |
40 | } |
0 | /* | |
1 | * Licensed to the Apache Software Foundation (ASF) under one or more | |
2 | * contributor license agreements. See the NOTICE file distributed with | |
3 | * this work for additional information regarding copyright ownership. | |
4 | * The ASF licenses this file to You under the Apache License, Version 2.0 | |
5 | * (the "License"); you may not use this file except in compliance with | |
6 | * the License. You may obtain a copy of the License at | |
7 | * | |
8 | * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
15 | * | |
16 | */ | |
17 | package org.apache.tomcat.util.bcel.classfile; | |
18 | ||
19 | import java.io.DataInputStream; | |
20 | import java.io.IOException; | |
21 | ||
22 | /** | |
23 | * represents the default value of a annotation for a method info | |
24 | * | |
25 | * @author <A HREF="mailto:dbrosius@qis.net">D. Brosius</A> | |
26 | * @since 6.0 | |
27 | */ | |
28 | public class AnnotationDefault extends Attribute | |
29 | { | |
30 | private static final long serialVersionUID = 6715933396664171543L; | |
31 | ||
32 | /** | |
33 | * @param name_index | |
34 | * Index pointing to the name <em>Code</em> | |
35 | * @param length | |
36 | * Content length in bytes | |
37 | * @param file | |
38 | * Input stream | |
39 | * @param constant_pool | |
40 | * Array of constants | |
41 | */ | |
42 | public AnnotationDefault(int name_index, int length, | |
43 | DataInputStream file, ConstantPool constant_pool) | |
44 | throws IOException | |
45 | { | |
46 | this(name_index, length, (ElementValue) null, | |
47 | constant_pool); | |
48 | // Default value | |
49 | ElementValue.readElementValue(file, constant_pool); | |
50 | } | |
51 | ||
52 | /** | |
53 | * @param name_index | |
54 | * Index pointing to the name <em>Code</em> | |
55 | * @param length | |
56 | * Content length in bytes | |
57 | * @param defaultValue | |
58 | * the annotation's default value | |
59 | * @param constant_pool | |
60 | * Array of constants | |
61 | */ | |
62 | public AnnotationDefault(int name_index, int length, | |
63 | ElementValue defaultValue, ConstantPool constant_pool) | |
64 | { | |
65 | super(name_index, length, constant_pool); | |
66 | } | |
67 | } |
19 | 19 | public class AnnotationElementValue extends ElementValue |
20 | 20 | { |
21 | 21 | // For annotation element values, this is the annotation |
22 | private AnnotationEntry annotationEntry; | |
22 | private final AnnotationEntry annotationEntry; | |
23 | 23 | |
24 | public AnnotationElementValue(int type, AnnotationEntry annotationEntry, | |
24 | AnnotationElementValue(int type, AnnotationEntry annotationEntry, | |
25 | 25 | ConstantPool cpool) |
26 | 26 | { |
27 | 27 | super(type, cpool); |
16 | 16 | */ |
17 | 17 | package org.apache.tomcat.util.bcel.classfile; |
18 | 18 | |
19 | import java.io.DataInputStream; | |
19 | import java.io.DataInput; | |
20 | 20 | import java.io.IOException; |
21 | 21 | import java.util.ArrayList; |
22 | 22 | import java.util.List; |
34 | 34 | private final int type_index; |
35 | 35 | private final ConstantPool constant_pool; |
36 | 36 | |
37 | private List<ElementValuePair> element_value_pairs; | |
37 | private final List<ElementValuePair> element_value_pairs; | |
38 | 38 | |
39 | 39 | /** |
40 | * Factory method to create an AnnotionEntry from a DataInputStream | |
40 | * Creates an AnnotationEntry from a DataInputStream | |
41 | 41 | * |
42 | 42 | * @param file |
43 | 43 | * @param constant_pool |
44 | * @return the entry | |
45 | 44 | * @throws IOException |
46 | 45 | */ |
47 | public static AnnotationEntry read(DataInputStream file, ConstantPool constant_pool) throws IOException { | |
46 | AnnotationEntry(DataInput file, ConstantPool constant_pool) throws IOException { | |
48 | 47 | |
49 | final AnnotationEntry annotationEntry = new AnnotationEntry(file.readUnsignedShort(), constant_pool); | |
50 | final int num_element_value_pairs = (file.readUnsignedShort()); | |
51 | annotationEntry.element_value_pairs = new ArrayList<>(); | |
48 | this.constant_pool = constant_pool; | |
49 | ||
50 | type_index = file.readUnsignedShort(); | |
51 | int num_element_value_pairs = file.readUnsignedShort(); | |
52 | ||
53 | element_value_pairs = new ArrayList<>(num_element_value_pairs); | |
52 | 54 | for (int i = 0; i < num_element_value_pairs; i++) { |
53 | annotationEntry.element_value_pairs.add(new ElementValuePair(file.readUnsignedShort(), ElementValue.readElementValue(file, constant_pool), | |
54 | constant_pool)); | |
55 | element_value_pairs.add(new ElementValuePair(file, constant_pool)); | |
55 | 56 | } |
56 | return annotationEntry; | |
57 | } | |
58 | ||
59 | public AnnotationEntry(int type_index, ConstantPool constant_pool) { | |
60 | this.type_index = type_index; | |
61 | this.constant_pool = constant_pool; | |
62 | 57 | } |
63 | 58 | |
64 | 59 | /** |
72 | 67 | /** |
73 | 68 | * @return the element value pairs in this annotation entry |
74 | 69 | */ |
75 | public ElementValuePair[] getElementValuePairs() { | |
76 | // TODO return List | |
77 | return element_value_pairs.toArray(new ElementValuePair[element_value_pairs.size()]); | |
70 | public List<ElementValuePair> getElementValuePairs() { | |
71 | return element_value_pairs; | |
78 | 72 | } |
79 | 73 | } |
16 | 16 | */ |
17 | 17 | package org.apache.tomcat.util.bcel.classfile; |
18 | 18 | |
19 | import java.io.DataInputStream; | |
19 | import java.io.DataInput; | |
20 | 20 | import java.io.IOException; |
21 | 21 | |
22 | 22 | /** |
25 | 25 | * @author <A HREF="mailto:dbrosius@qis.net">D. Brosius</A> |
26 | 26 | * @since 6.0 |
27 | 27 | */ |
28 | public abstract class Annotations extends Attribute { | |
28 | public class Annotations { | |
29 | 29 | |
30 | private static final long serialVersionUID = 1L; | |
31 | ||
32 | private AnnotationEntry[] annotation_table; | |
30 | private final AnnotationEntry[] annotation_table; | |
33 | 31 | |
34 | 32 | /** |
35 | * @param name_index Index pointing to the name <em>Code</em> | |
36 | * @param length Content length in bytes | |
37 | 33 | * @param file Input stream |
38 | 34 | * @param constant_pool Array of constants |
39 | 35 | */ |
40 | public Annotations(int name_index, int length, DataInputStream file, ConstantPool constant_pool) throws IOException { | |
41 | this(name_index, length, (AnnotationEntry[]) null, constant_pool); | |
36 | Annotations(DataInput file, ConstantPool constant_pool) | |
37 | throws IOException { | |
42 | 38 | final int annotation_table_length = (file.readUnsignedShort()); |
43 | 39 | annotation_table = new AnnotationEntry[annotation_table_length]; |
44 | 40 | for (int i = 0; i < annotation_table_length; i++) { |
45 | annotation_table[i] = AnnotationEntry.read(file, constant_pool); | |
41 | annotation_table[i] = new AnnotationEntry(file, constant_pool); | |
46 | 42 | } |
47 | 43 | } |
48 | 44 | |
49 | ||
50 | /** | |
51 | * @param name_index Index pointing to the name <em>Code</em> | |
52 | * @param length Content length in bytes | |
53 | * @param annotation_table the actual annotations | |
54 | * @param constant_pool Array of constants | |
55 | */ | |
56 | public Annotations(int name_index, int length, AnnotationEntry[] annotation_table, ConstantPool constant_pool) { | |
57 | super(name_index, length, constant_pool); | |
58 | setAnnotationTable(annotation_table); | |
59 | } | |
60 | ||
61 | /** | |
62 | * @param annotation_table the entries to set in this annotation | |
63 | */ | |
64 | public final void setAnnotationTable( AnnotationEntry[] annotation_table ) { | |
65 | this.annotation_table = annotation_table; | |
66 | } | |
67 | 45 | |
68 | 46 | /** |
69 | 47 | * returns the array of annotation entries in this annotation |
19 | 19 | public class ArrayElementValue extends ElementValue |
20 | 20 | { |
21 | 21 | // For array types, this is the array |
22 | private ElementValue[] evalues; | |
22 | private final ElementValue[] evalues; | |
23 | 23 | |
24 | public ArrayElementValue(int type, ElementValue[] datums, ConstantPool cpool) | |
24 | ArrayElementValue(int type, ElementValue[] datums, ConstantPool cpool) | |
25 | 25 | { |
26 | 26 | super(type, cpool); |
27 | 27 | if (type != ARRAY) { |
0 | /* | |
1 | * Licensed to the Apache Software Foundation (ASF) under one or more | |
2 | * contributor license agreements. See the NOTICE file distributed with | |
3 | * this work for additional information regarding copyright ownership. | |
4 | * The ASF licenses this file to You under the Apache License, Version 2.0 | |
5 | * (the "License"); you may not use this file except in compliance with | |
6 | * the License. You may obtain a copy of the License at | |
7 | * | |
8 | * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
15 | * | |
16 | */ | |
17 | package org.apache.tomcat.util.bcel.classfile; | |
18 | ||
19 | import java.io.DataInputStream; | |
20 | import java.io.IOException; | |
21 | import java.io.Serializable; | |
22 | ||
23 | import org.apache.tomcat.util.bcel.Constants; | |
24 | ||
25 | /** | |
26 | * Abstract super class for <em>Attribute</em> objects. Currently the | |
27 | * <em>ConstantValue</em>, <em>SourceFile</em>, <em>Code</em>, | |
28 | * <em>ExceptionTable</em>, <em>LineNumberTable</em>, | |
29 | * <em>LocalVariableTable</em>, <em>InnerClasses</em> and | |
30 | * <em>Synthetic</em> attributes are supported. The <em>Unknown</em> | |
31 | * attribute stands for non-standard-attributes. | |
32 | * | |
33 | * @author <A HREF="mailto:m.dahm@gmx.de">M. Dahm</A> | |
34 | * @see ConstantValue | |
35 | * @see SourceFile | |
36 | * @see Code | |
37 | * @see ExceptionTable | |
38 | * @see LineNumberTable | |
39 | * @see LocalVariableTable | |
40 | * @see InnerClasses | |
41 | * @see Deprecated | |
42 | */ | |
43 | public abstract class Attribute implements Cloneable, Serializable | |
44 | { | |
45 | private static final long serialVersionUID = 1514136303496688899L; | |
46 | ||
47 | protected int name_index; // Points to attribute name in constant pool | |
48 | ||
49 | protected int length; // Content length of attribute field | |
50 | ||
51 | protected ConstantPool constant_pool; | |
52 | ||
53 | protected Attribute(int name_index, int length, | |
54 | ConstantPool constant_pool) | |
55 | { | |
56 | this.name_index = name_index; | |
57 | this.length = length; | |
58 | this.constant_pool = constant_pool; | |
59 | } | |
60 | ||
61 | /* | |
62 | * Class method reads one attribute from the input data stream. This method | |
63 | * must not be accessible from the outside. It is called by the Field and | |
64 | * Method constructor methods. | |
65 | * | |
66 | * @see Field | |
67 | * @see Method @param file Input stream @param constant_pool Array of | |
68 | * constants @return Attribute @throws IOException @throws | |
69 | * ClassFormatException | |
70 | */ | |
71 | public static final Attribute readAttribute(DataInputStream file, | |
72 | ConstantPool constant_pool) throws IOException, | |
73 | ClassFormatException | |
74 | { | |
75 | ConstantUtf8 c; | |
76 | String name; | |
77 | int name_index; | |
78 | int length; | |
79 | byte tag = Constants.ATTR_UNKNOWN; // Unknown attribute | |
80 | // Get class name from constant pool via `name_index' indirection | |
81 | name_index = file.readUnsignedShort(); | |
82 | c = (ConstantUtf8) constant_pool.getConstant(name_index, | |
83 | Constants.CONSTANT_Utf8); | |
84 | name = c.getBytes(); | |
85 | // Length of data in bytes | |
86 | length = file.readInt(); | |
87 | // Compare strings to find known attribute | |
88 | // System.out.println(name); | |
89 | for (byte i = 0; i < Constants.KNOWN_ATTRIBUTES; i++) | |
90 | { | |
91 | if (name.equals(Constants.ATTRIBUTE_NAMES[i])) | |
92 | { | |
93 | tag = i; // found! | |
94 | break; | |
95 | } | |
96 | } | |
97 | // Call proper constructor, depending on `tag' | |
98 | switch (tag) | |
99 | { | |
100 | case Constants.ATTR_UNKNOWN: | |
101 | Utility.swallowUnknownAttribute(file, length); | |
102 | return null; | |
103 | case Constants.ATTR_CONSTANT_VALUE: | |
104 | return new ConstantValue(name_index, length, file, constant_pool); | |
105 | case Constants.ATTR_SOURCE_FILE: | |
106 | return new SourceFile(name_index, length, file, constant_pool); | |
107 | case Constants.ATTR_CODE: | |
108 | return new Code(name_index, length, file, constant_pool); | |
109 | case Constants.ATTR_EXCEPTIONS: | |
110 | return new ExceptionTable(name_index, length, file, constant_pool); | |
111 | case Constants.ATTR_LINE_NUMBER_TABLE: | |
112 | return new LineNumberTable(name_index, length, file, constant_pool); | |
113 | case Constants.ATTR_LOCAL_VARIABLE_TABLE: | |
114 | return new LocalVariableTable(name_index, length, file, | |
115 | constant_pool); | |
116 | case Constants.ATTR_INNER_CLASSES: | |
117 | return new InnerClasses(name_index, length, file, constant_pool); | |
118 | case Constants.ATTR_SYNTHETIC: | |
119 | Utility.swallowSynthetic(file, length); | |
120 | return null; | |
121 | case Constants.ATTR_DEPRECATED: | |
122 | return new Deprecated(name_index, length, file, constant_pool); | |
123 | case Constants.ATTR_PMG: | |
124 | return new PMGClass(name_index, length, file, constant_pool); | |
125 | case Constants.ATTR_SIGNATURE: | |
126 | Utility.swallowSignature(file); | |
127 | return null; | |
128 | case Constants.ATTR_STACK_MAP: | |
129 | Utility.swallowStackMap(file); | |
130 | return null; | |
131 | case Constants.ATTR_RUNTIME_VISIBLE_ANNOTATIONS: | |
132 | return new RuntimeVisibleAnnotations(name_index, length, file, | |
133 | constant_pool); | |
134 | case Constants.ATTR_RUNTIME_INVISIBLE_ANNOTATIONS: | |
135 | return new RuntimeInvisibleAnnotations(name_index, length, file, | |
136 | constant_pool); | |
137 | case Constants.ATTR_RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS: | |
138 | return new RuntimeVisibleParameterAnnotations(name_index, length, | |
139 | file, constant_pool); | |
140 | case Constants.ATTR_RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS: | |
141 | return new RuntimeInvisibleParameterAnnotations(name_index, length, | |
142 | file, constant_pool); | |
143 | case Constants.ATTR_ANNOTATION_DEFAULT: | |
144 | return new AnnotationDefault(name_index, length, file, | |
145 | constant_pool); | |
146 | case Constants.ATTR_LOCAL_VARIABLE_TYPE_TABLE: | |
147 | return new LocalVariableTypeTable(name_index, length, file, | |
148 | constant_pool); | |
149 | case Constants.ATTR_ENCLOSING_METHOD: | |
150 | return new EnclosingMethod(name_index, length, file, constant_pool); | |
151 | case Constants.ATTR_STACK_MAP_TABLE: | |
152 | Utility.swallowStackMapTable(file); | |
153 | return null; | |
154 | case Constants.ATTR_BOOTSTRAP_METHODS: | |
155 | Utility.swallowBootstrapMethods(file); | |
156 | return null; | |
157 | case Constants.ATTR_METHOD_PARAMETERS: | |
158 | Utility.swallowMethodParameters(file); | |
159 | return null; | |
160 | default: // Never reached | |
161 | throw new IllegalStateException("Unrecognized attribute type tag parsed: " + tag); | |
162 | } | |
163 | } | |
164 | ||
165 | /** | |
166 | * @return Name of attribute | |
167 | */ | |
168 | public String getName() | |
169 | { | |
170 | ConstantUtf8 c = (ConstantUtf8) constant_pool.getConstant(name_index, | |
171 | Constants.CONSTANT_Utf8); | |
172 | return c.getBytes(); | |
173 | } | |
174 | ||
175 | ||
176 | /** | |
177 | * Use copy() if you want to have a deep copy(), i.e., with all references | |
178 | * copied correctly. | |
179 | * | |
180 | * @return shallow copy of this attribute | |
181 | */ | |
182 | @Override | |
183 | public Attribute clone() | |
184 | { | |
185 | Attribute attr = null; | |
186 | try | |
187 | { | |
188 | attr = (Attribute) super.clone(); | |
189 | } | |
190 | catch (CloneNotSupportedException e) | |
191 | { | |
192 | throw new Error("Clone Not Supported"); // never happens | |
193 | } | |
194 | return attr; | |
195 | } | |
196 | ||
197 | } |
23 | 23 | // For primitive types and string type, this points to the value entry in |
24 | 24 | // the cpool |
25 | 25 | // For 'class' this points to the class entry in the cpool |
26 | private int idx; | |
26 | private final int idx; | |
27 | 27 | |
28 | public ClassElementValue(int type, int idx, ConstantPool cpool) | |
29 | { | |
28 | ClassElementValue(int type, int idx, ConstantPool cpool) { | |
30 | 29 | super(type, cpool); |
31 | 30 | this.idx = idx; |
32 | 31 | } |
16 | 16 | */ |
17 | 17 | package org.apache.tomcat.util.bcel.classfile; |
18 | 18 | |
19 | import java.io.BufferedInputStream; | |
20 | import java.io.DataInputStream; | |
19 | import java.io.DataInput; | |
21 | 20 | import java.io.IOException; |
22 | 21 | import java.io.InputStream; |
23 | 22 | |
41 | 40 | |
42 | 41 | private static final int MAGIC = 0xCAFEBABE; |
43 | 42 | |
44 | private DataInputStream file; | |
45 | private String file_name; | |
46 | private int class_name_index, superclass_name_index; | |
43 | private final DataInput file; | |
44 | private String class_name, superclass_name; | |
47 | 45 | private int access_flags; // Access rights of parsed class |
48 | private int[] interfaces; // Names of implemented interfaces | |
46 | private String[] interface_names; // Names of implemented interfaces | |
49 | 47 | private ConstantPool constant_pool; // collection of constants |
50 | private FieldOrMethod[] fields; // class fields, i.e., its variables | |
51 | private FieldOrMethod[] methods; // methods defined in the class | |
52 | private Attribute[] attributes; // attributes defined in the class | |
48 | private Annotations runtimeVisibleAnnotations; // "RuntimeVisibleAnnotations" attribute defined in the class | |
53 | 49 | private static final int BUFSIZE = 8192; |
54 | 50 | |
51 | private static final String[] INTERFACES_EMPTY_ARRAY = new String[0]; | |
55 | 52 | |
56 | 53 | /** |
57 | 54 | * Parse class from the given stream. |
58 | 55 | * |
59 | 56 | * @param file Input stream |
60 | * @param file_name File name | |
61 | */ | |
62 | public ClassParser(InputStream file, String file_name) { | |
63 | this.file_name = file_name; | |
64 | if (file instanceof DataInputStream) { | |
65 | this.file = (DataInputStream) file; | |
66 | } else { | |
67 | this.file = new DataInputStream(new BufferedInputStream(file, BUFSIZE)); | |
68 | } | |
57 | */ | |
58 | public ClassParser(InputStream file) { | |
59 | this.file = new FastDataInputStream(file, BUFSIZE); | |
69 | 60 | } |
70 | 61 | |
71 | 62 | |
100 | 91 | readMethods(); |
101 | 92 | // Read class attributes |
102 | 93 | readAttributes(); |
103 | // Check for unknown variables | |
104 | //Unknown[] u = Unknown.getUnknownAttributes(); | |
105 | //for(int i=0; i < u.length; i++) | |
106 | // System.err.println("WARNING: " + u[i]); | |
107 | // Everything should have been read now | |
108 | // if(file.available() > 0) { | |
109 | // int bytes = file.available(); | |
110 | // byte[] buf = new byte[bytes]; | |
111 | // file.read(buf); | |
112 | // if(!(is_zip && (buf.length == 1))) { | |
113 | // System.err.println("WARNING: Trailing garbage at end of " + file_name); | |
114 | // System.err.println(bytes + " extra bytes: " + Utility.toHexString(buf)); | |
115 | // } | |
116 | // } | |
117 | 94 | |
118 | 95 | // Return the information we have gathered in a new object |
119 | return new JavaClass(class_name_index, superclass_name_index, | |
120 | access_flags, constant_pool, interfaces, attributes); | |
96 | return new JavaClass(class_name, superclass_name, | |
97 | access_flags, constant_pool, interface_names, | |
98 | runtimeVisibleAnnotations); | |
121 | 99 | } |
122 | 100 | |
123 | 101 | |
129 | 107 | private void readAttributes() throws IOException, ClassFormatException { |
130 | 108 | int attributes_count; |
131 | 109 | attributes_count = file.readUnsignedShort(); |
132 | attributes = new Attribute[attributes_count]; | |
133 | 110 | for (int i = 0; i < attributes_count; i++) { |
134 | attributes[i] = Attribute.readAttribute(file, constant_pool); | |
111 | ConstantUtf8 c; | |
112 | String name; | |
113 | int name_index; | |
114 | int length; | |
115 | // Get class name from constant pool via `name_index' indirection | |
116 | name_index = file.readUnsignedShort(); | |
117 | c = (ConstantUtf8) constant_pool.getConstant(name_index, | |
118 | Constants.CONSTANT_Utf8); | |
119 | name = c.getBytes(); | |
120 | // Length of data in bytes | |
121 | length = file.readInt(); | |
122 | ||
123 | if (name.equals("RuntimeVisibleAnnotations")) { | |
124 | if (runtimeVisibleAnnotations != null) { | |
125 | throw new ClassFormatException( | |
126 | "RuntimeVisibleAnnotations attribute is not allowed more than once in a class file"); | |
127 | } | |
128 | runtimeVisibleAnnotations = new Annotations(file, constant_pool); | |
129 | } else { | |
130 | // All other attributes are skipped | |
131 | Utility.skipFully(file, length); | |
132 | } | |
135 | 133 | } |
136 | 134 | } |
137 | 135 | |
151 | 149 | } |
152 | 150 | if (((access_flags & Constants.ACC_ABSTRACT) != 0) |
153 | 151 | && ((access_flags & Constants.ACC_FINAL) != 0)) { |
154 | throw new ClassFormatException("Class " + file_name + " can't be both final and abstract"); | |
155 | } | |
156 | class_name_index = file.readUnsignedShort(); | |
157 | superclass_name_index = file.readUnsignedShort(); | |
152 | throw new ClassFormatException("Class can't be both final and abstract"); | |
153 | } | |
154 | ||
155 | int class_name_index = file.readUnsignedShort(); | |
156 | class_name = Utility.getClassName(constant_pool, class_name_index); | |
157 | ||
158 | int superclass_name_index = file.readUnsignedShort(); | |
159 | if (superclass_name_index > 0) { | |
160 | // May be zero -> class is java.lang.Object | |
161 | superclass_name = Utility.getClassName(constant_pool, superclass_name_index); | |
162 | } else { | |
163 | superclass_name = "java.lang.Object"; | |
164 | } | |
158 | 165 | } |
159 | 166 | |
160 | 167 | |
174 | 181 | * @throws ClassFormatException |
175 | 182 | */ |
176 | 183 | private void readFields() throws IOException, ClassFormatException { |
177 | int fields_count; | |
178 | fields_count = file.readUnsignedShort(); | |
179 | fields = new FieldOrMethod[fields_count]; | |
184 | int fields_count = file.readUnsignedShort(); | |
180 | 185 | for (int i = 0; i < fields_count; i++) { |
181 | fields[i] = new FieldOrMethod(file, constant_pool); | |
186 | Utility.swallowFieldOrMethod(file); | |
182 | 187 | } |
183 | 188 | } |
184 | 189 | |
192 | 197 | */ |
193 | 198 | private void readID() throws IOException, ClassFormatException { |
194 | 199 | if (file.readInt() != MAGIC) { |
195 | throw new ClassFormatException(file_name + " is not a Java .class file"); | |
200 | throw new ClassFormatException("It is not a Java .class file"); | |
196 | 201 | } |
197 | 202 | } |
198 | 203 | |
205 | 210 | private void readInterfaces() throws IOException, ClassFormatException { |
206 | 211 | int interfaces_count; |
207 | 212 | interfaces_count = file.readUnsignedShort(); |
208 | interfaces = new int[interfaces_count]; | |
209 | for (int i = 0; i < interfaces_count; i++) { | |
210 | interfaces[i] = file.readUnsignedShort(); | |
213 | if (interfaces_count > 0) { | |
214 | interface_names = new String[interfaces_count]; | |
215 | for (int i = 0; i < interfaces_count; i++) { | |
216 | int index = file.readUnsignedShort(); | |
217 | interface_names[i] = Utility.getClassName(constant_pool, index); | |
218 | } | |
219 | } else { | |
220 | interface_names = INTERFACES_EMPTY_ARRAY; | |
211 | 221 | } |
212 | 222 | } |
213 | 223 | |
220 | 230 | private void readMethods() throws IOException, ClassFormatException { |
221 | 231 | int methods_count; |
222 | 232 | methods_count = file.readUnsignedShort(); |
223 | methods = new FieldOrMethod[methods_count]; | |
224 | 233 | for (int i = 0; i < methods_count; i++) { |
225 | methods[i] = new FieldOrMethod(file, constant_pool); | |
234 | Utility.swallowFieldOrMethod(file); | |
226 | 235 | } |
227 | 236 | } |
228 | 237 | |
233 | 242 | * @throws ClassFormatException |
234 | 243 | */ |
235 | 244 | private void readVersion() throws IOException, ClassFormatException { |
236 | file.readUnsignedShort(); // Unused minor | |
237 | file.readUnsignedShort(); // Unused major | |
245 | // file.readUnsignedShort(); // Unused minor | |
246 | // file.readUnsignedShort(); // Unused major | |
247 | Utility.skipFully(file, 4); | |
238 | 248 | } |
239 | 249 | } |
0 | /* | |
1 | * Licensed to the Apache Software Foundation (ASF) under one or more | |
2 | * contributor license agreements. See the NOTICE file distributed with | |
3 | * this work for additional information regarding copyright ownership. | |
4 | * The ASF licenses this file to You under the Apache License, Version 2.0 | |
5 | * (the "License"); you may not use this file except in compliance with | |
6 | * the License. You may obtain a copy of the License at | |
7 | * | |
8 | * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
15 | * | |
16 | */ | |
17 | package org.apache.tomcat.util.bcel.classfile; | |
18 | ||
19 | import java.io.DataInputStream; | |
20 | import java.io.IOException; | |
21 | ||
22 | /** | |
23 | * This class represents a chunk of Java byte code contained in a | |
24 | * method. It is instantiated by the | |
25 | * <em>Attribute.readAttribute()</em> method. A <em>Code</em> | |
26 | * attribute contains informations about operand stack, local | |
27 | * variables, byte code and the exceptions handled within this | |
28 | * method. | |
29 | * | |
30 | * This attribute has attributes itself, namely <em>LineNumberTable</em> which | |
31 | * is used for debugging purposes and <em>LocalVariableTable</em> which | |
32 | * contains information about the local variables. | |
33 | * | |
34 | * @author <A HREF="mailto:m.dahm@gmx.de">M. Dahm</A> | |
35 | * @see Attribute | |
36 | * @see LineNumberTable | |
37 | * @see LocalVariableTable | |
38 | */ | |
39 | public final class Code extends Attribute { | |
40 | ||
41 | private static final long serialVersionUID = 8936843273318969602L; | |
42 | private int code_length; // Length of code in bytes | |
43 | private byte[] code; // Actual byte code | |
44 | private int exception_table_length; | |
45 | private int attributes_count; // Attributes of code: LineNumber | |
46 | private Attribute[] attributes; // or LocalVariable | |
47 | ||
48 | ||
49 | /** | |
50 | * @param name_index Index pointing to the name <em>Code</em> | |
51 | * @param length Content length in bytes | |
52 | * @param file Input stream | |
53 | * @param constant_pool Array of constants | |
54 | */ | |
55 | Code(int name_index, int length, DataInputStream file, ConstantPool constant_pool) | |
56 | throws IOException { | |
57 | // Initialize with some default values which will be overwritten later | |
58 | this(name_index, length, (byte[]) null, (Attribute[]) null, | |
59 | constant_pool); | |
60 | file.readUnsignedShort(); // Unused max_stack | |
61 | file.readUnsignedShort(); // Unused max_locals | |
62 | code_length = file.readInt(); | |
63 | code = new byte[code_length]; // Read byte code | |
64 | file.readFully(code); | |
65 | /* Read exception table that contains all regions where an exception | |
66 | * handler is active, i.e., a try { ... } catch() block. | |
67 | */ | |
68 | exception_table_length = file.readUnsignedShort(); | |
69 | for (int i = 0; i < exception_table_length; i++) { | |
70 | Utility.swallowCodeException(file); | |
71 | } | |
72 | /* Read all attributes, currently `LineNumberTable' and | |
73 | * `LocalVariableTable' | |
74 | */ | |
75 | attributes_count = file.readUnsignedShort(); | |
76 | attributes = new Attribute[attributes_count]; | |
77 | for (int i = 0; i < attributes_count; i++) { | |
78 | attributes[i] = Attribute.readAttribute(file, constant_pool); | |
79 | } | |
80 | /* Adjust length, because of setAttributes in this(), s.b. length | |
81 | * is incorrect, because it didn't take the internal attributes | |
82 | * into account yet! Very subtle bug, fixed in 3.1.1. | |
83 | */ | |
84 | this.length = length; | |
85 | } | |
86 | ||
87 | ||
88 | /** | |
89 | * @param name_index Index pointing to the name <em>Code</em> | |
90 | * @param length Content length in bytes | |
91 | * @param code Actual byte code | |
92 | * @param attributes Attributes of code: LineNumber or LocalVariable | |
93 | * @param constant_pool Array of constants | |
94 | */ | |
95 | public Code(int name_index, int length, byte[] code, | |
96 | Attribute[] attributes, ConstantPool constant_pool) { | |
97 | super(name_index, length, constant_pool); | |
98 | setCode(code); | |
99 | setAttributes(attributes); // Overwrites length! | |
100 | } | |
101 | ||
102 | ||
103 | /** | |
104 | * @return the internal length of this code attribute (minus the first 6 bytes) | |
105 | * and excluding all its attributes | |
106 | */ | |
107 | private int getInternalLength() { | |
108 | return 2 /*max_stack*/+ 2 /*max_locals*/+ 4 /*code length*/ | |
109 | + code_length /*byte-code*/ | |
110 | + 2 /*exception-table length*/ | |
111 | + 8 * exception_table_length /* exception table */ | |
112 | + 2 /* attributes count */; | |
113 | } | |
114 | ||
115 | ||
116 | /** | |
117 | * @return the full size of this code attribute, minus its first 6 bytes, | |
118 | * including the size of all its contained attributes | |
119 | */ | |
120 | private int calculateLength() { | |
121 | int len = 0; | |
122 | for (int i = 0; i < attributes_count; i++) { | |
123 | len += attributes[i].length + 6 /*attribute header size*/; | |
124 | } | |
125 | return len + getInternalLength(); | |
126 | } | |
127 | ||
128 | ||
129 | /** | |
130 | * @param attributes the attributes to set for this Code | |
131 | */ | |
132 | public final void setAttributes( Attribute[] attributes ) { | |
133 | this.attributes = attributes; | |
134 | attributes_count = (attributes == null) ? 0 : attributes.length; | |
135 | length = calculateLength(); // Adjust length | |
136 | } | |
137 | ||
138 | ||
139 | /** | |
140 | * @param code byte code | |
141 | */ | |
142 | public final void setCode( byte[] code ) { | |
143 | this.code = code; | |
144 | code_length = (code == null) ? 0 : code.length; | |
145 | length = calculateLength(); // Adjust length | |
146 | } | |
147 | } |
16 | 16 | */ |
17 | 17 | package org.apache.tomcat.util.bcel.classfile; |
18 | 18 | |
19 | import java.io.DataInputStream; | |
19 | import java.io.DataInput; | |
20 | 20 | import java.io.IOException; |
21 | import java.io.Serializable; | |
22 | 21 | |
23 | 22 | import org.apache.tomcat.util.bcel.Constants; |
24 | import org.apache.tomcat.util.bcel.util.BCELComparator; | |
25 | 23 | |
26 | 24 | /** |
27 | 25 | * Abstract superclass for classes to represent the different constant types |
30 | 28 | * |
31 | 29 | * @author <A HREF="mailto:m.dahm@gmx.de">M. Dahm</A> |
32 | 30 | */ |
33 | public abstract class Constant implements Cloneable, Serializable { | |
31 | public abstract class Constant { | |
34 | 32 | |
35 | private static final long serialVersionUID = 2827409182154809454L; | |
36 | private static BCELComparator _cmp = new BCELComparator() { | |
37 | ||
38 | @Override | |
39 | public boolean equals( Object o1, Object o2 ) { | |
40 | Constant THIS = (Constant) o1; | |
41 | Constant THAT = (Constant) o2; | |
42 | return THIS.toString().equals(THAT.toString()); | |
43 | } | |
44 | ||
45 | ||
46 | @Override | |
47 | public int hashCode( Object o ) { | |
48 | Constant THIS = (Constant) o; | |
49 | return THIS.toString().hashCode(); | |
50 | } | |
51 | }; | |
52 | 33 | /* In fact this tag is redundant since we can distinguish different |
53 | 34 | * `Constant' objects by their type, i.e., via `instanceof'. In some |
54 | 35 | * places we will use the tag for switch()es anyway. |
57 | 38 | * need the tag as an index to select the corresponding class name from the |
58 | 39 | * `CONSTANT_NAMES' array. |
59 | 40 | */ |
60 | protected byte tag; | |
41 | protected final byte tag; | |
61 | 42 | |
62 | 43 | |
63 | 44 | Constant(byte tag) { |
74 | 55 | } |
75 | 56 | |
76 | 57 | |
77 | @Override | |
78 | public Object clone() { | |
79 | try { | |
80 | return super.clone(); | |
81 | } catch (CloneNotSupportedException e) { | |
82 | throw new Error("Clone Not Supported"); // never happens | |
83 | } | |
84 | } | |
85 | ||
86 | ||
87 | 58 | /** |
88 | 59 | * Read one constant from the given file, the type depends on a tag byte. |
89 | 60 | * |
90 | 61 | * @param file Input stream |
91 | 62 | * @return Constant object |
92 | 63 | */ |
93 | static Constant readConstant( DataInputStream file ) throws IOException, | |
64 | static Constant readConstant(DataInput file ) throws IOException, | |
94 | 65 | ClassFormatException { |
95 | 66 | byte b = file.readByte(); // Read tag byte |
67 | int skipSize; | |
96 | 68 | switch (b) { |
97 | 69 | case Constants.CONSTANT_Class: |
98 | 70 | return new ConstantClass(file); |
99 | case Constants.CONSTANT_Fieldref: | |
100 | return new ConstantFieldref(file); | |
101 | case Constants.CONSTANT_Methodref: | |
102 | return new ConstantMethodref(file); | |
103 | case Constants.CONSTANT_InterfaceMethodref: | |
104 | return new ConstantInterfaceMethodref(file); | |
105 | case Constants.CONSTANT_String: | |
106 | return new ConstantString(file); | |
107 | 71 | case Constants.CONSTANT_Integer: |
108 | 72 | return new ConstantInteger(file); |
109 | 73 | case Constants.CONSTANT_Float: |
112 | 76 | return new ConstantLong(file); |
113 | 77 | case Constants.CONSTANT_Double: |
114 | 78 | return new ConstantDouble(file); |
115 | case Constants.CONSTANT_NameAndType: | |
116 | return new ConstantNameAndType(file); | |
117 | 79 | case Constants.CONSTANT_Utf8: |
118 | 80 | return ConstantUtf8.getInstance(file); |
81 | case Constants.CONSTANT_String: | |
82 | case Constants.CONSTANT_MethodType: | |
83 | skipSize = 2; // unsigned short | |
84 | break; | |
119 | 85 | case Constants.CONSTANT_MethodHandle: |
120 | return new ConstantMethodHandle(file); | |
121 | case Constants.CONSTANT_MethodType: | |
122 | return new ConstantMethodType(file); | |
86 | skipSize = 3; // unsigned byte, unsigned short | |
87 | break; | |
88 | case Constants.CONSTANT_Fieldref: | |
89 | case Constants.CONSTANT_Methodref: | |
90 | case Constants.CONSTANT_InterfaceMethodref: | |
91 | case Constants.CONSTANT_NameAndType: | |
123 | 92 | case Constants.CONSTANT_InvokeDynamic: |
124 | return new ConstantInvokeDynamic(file); | |
93 | skipSize = 4; // unsigned short, unsigned short | |
94 | break; | |
125 | 95 | default: |
126 | 96 | throw new ClassFormatException("Invalid byte tag in constant pool: " + b); |
127 | 97 | } |
98 | Utility.skipFully(file, skipSize); | |
99 | return null; | |
128 | 100 | } |
129 | 101 | |
130 | 102 | |
133 | 105 | public String toString() { |
134 | 106 | return "[" + tag + "]"; |
135 | 107 | } |
136 | ||
137 | ||
138 | /** | |
139 | * Return value as defined by given BCELComparator strategy. | |
140 | * By default two Constant objects are said to be equal when | |
141 | * the result of toString() is equal. | |
142 | * | |
143 | * @see java.lang.Object#equals(java.lang.Object) | |
144 | */ | |
145 | @Override | |
146 | public boolean equals( Object obj ) { | |
147 | return _cmp.equals(this, obj); | |
148 | } | |
149 | ||
150 | ||
151 | /** | |
152 | * Return value as defined by given BCELComparator strategy. | |
153 | * By default return the hashcode of the result of toString(). | |
154 | * | |
155 | * @see java.lang.Object#hashCode() | |
156 | */ | |
157 | @Override | |
158 | public int hashCode() { | |
159 | return _cmp.hashCode(this); | |
160 | } | |
161 | 108 | } |
0 | /* | |
1 | * Licensed to the Apache Software Foundation (ASF) under one or more | |
2 | * contributor license agreements. See the NOTICE file distributed with | |
3 | * this work for additional information regarding copyright ownership. | |
4 | * The ASF licenses this file to You under the Apache License, Version 2.0 | |
5 | * (the "License"); you may not use this file except in compliance with | |
6 | * the License. You may obtain a copy of the License at | |
7 | * | |
8 | * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
15 | * | |
16 | */ | |
17 | package org.apache.tomcat.util.bcel.classfile; | |
18 | ||
19 | import java.io.DataInput; | |
20 | import java.io.IOException; | |
21 | ||
22 | /** | |
23 | * Abstract super class for Fieldref and Methodref constants. | |
24 | * | |
25 | * @author <A HREF="mailto:m.dahm@gmx.de">M. Dahm</A> | |
26 | * @see ConstantFieldref | |
27 | * @see ConstantMethodref | |
28 | * @see ConstantInterfaceMethodref | |
29 | */ | |
30 | public abstract class ConstantCP extends Constant { | |
31 | ||
32 | private static final long serialVersionUID = 7282382456501145526L; | |
33 | /** References to the constants containing the class and the field signature | |
34 | */ | |
35 | protected int class_index, name_and_type_index; | |
36 | ||
37 | ||
38 | /** | |
39 | * Initialize instance from file data. | |
40 | * | |
41 | * @param tag Constant type tag | |
42 | * @param file Input stream | |
43 | * @throws IOException | |
44 | */ | |
45 | ConstantCP(byte tag, DataInput file) throws IOException { | |
46 | this(tag, file.readUnsignedShort(), file.readUnsignedShort()); | |
47 | } | |
48 | ||
49 | ||
50 | /** | |
51 | * @param class_index Reference to the class containing the field | |
52 | * @param name_and_type_index and the field signature | |
53 | */ | |
54 | protected ConstantCP(byte tag, int class_index, int name_and_type_index) { | |
55 | super(tag); | |
56 | this.class_index = class_index; | |
57 | this.name_and_type_index = name_and_type_index; | |
58 | } | |
59 | ||
60 | ||
61 | /** | |
62 | * @return Reference (index) to class this field or method belongs to. | |
63 | */ | |
64 | public final int getClassIndex() { | |
65 | return class_index; | |
66 | } | |
67 | ||
68 | ||
69 | /** | |
70 | * @return Reference (index) to signature of the field. | |
71 | */ | |
72 | public final int getNameAndTypeIndex() { | |
73 | return name_and_type_index; | |
74 | } | |
75 | } |
31 | 31 | */ |
32 | 32 | public final class ConstantClass extends Constant { |
33 | 33 | |
34 | private static final long serialVersionUID = -6603658849582876642L; | |
35 | private int name_index; // Identical to ConstantString except for the name | |
34 | private final int name_index; // Identical to ConstantString except for the name | |
36 | 35 | |
37 | 36 | |
38 | 37 | /** |
42 | 41 | * @throws IOException |
43 | 42 | */ |
44 | 43 | ConstantClass(DataInput file) throws IOException { |
45 | this(file.readUnsignedShort()); | |
46 | } | |
47 | ||
48 | ||
49 | /** | |
50 | * @param name_index Name index in constant pool. Should refer to a | |
51 | * ConstantUtf8. | |
52 | */ | |
53 | public ConstantClass(int name_index) { | |
54 | 44 | super(Constants.CONSTANT_Class); |
55 | this.name_index = name_index; | |
45 | this.name_index = file.readUnsignedShort(); | |
56 | 46 | } |
57 | 47 | |
58 | 48 |
31 | 31 | */ |
32 | 32 | public final class ConstantDouble extends Constant { |
33 | 33 | |
34 | private static final long serialVersionUID = 3450743772468544760L; | |
35 | private double bytes; | |
36 | ||
37 | ||
38 | /** | |
39 | * @param bytes Data | |
40 | */ | |
41 | public ConstantDouble(double bytes) { | |
42 | super(Constants.CONSTANT_Double); | |
43 | this.bytes = bytes; | |
44 | } | |
34 | private final double bytes; | |
45 | 35 | |
46 | 36 | |
47 | 37 | /** |
51 | 41 | * @throws IOException |
52 | 42 | */ |
53 | 43 | ConstantDouble(DataInput file) throws IOException { |
54 | this(file.readDouble()); | |
44 | super(Constants.CONSTANT_Double); | |
45 | this.bytes = file.readDouble(); | |
55 | 46 | } |
56 | 47 | |
57 | 48 |
0 | /* | |
1 | * Licensed to the Apache Software Foundation (ASF) under one or more | |
2 | * contributor license agreements. See the NOTICE file distributed with | |
3 | * this work for additional information regarding copyright ownership. | |
4 | * The ASF licenses this file to You under the Apache License, Version 2.0 | |
5 | * (the "License"); you may not use this file except in compliance with | |
6 | * the License. You may obtain a copy of the License at | |
7 | * | |
8 | * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
15 | * | |
16 | */ | |
17 | package org.apache.tomcat.util.bcel.classfile; | |
18 | ||
19 | import java.io.DataInputStream; | |
20 | import java.io.IOException; | |
21 | ||
22 | import org.apache.tomcat.util.bcel.Constants; | |
23 | ||
24 | /** | |
25 | * This class represents a constant pool reference to a field. | |
26 | * | |
27 | * @author <A HREF="mailto:m.dahm@gmx.de">M. Dahm</A> | |
28 | */ | |
29 | public final class ConstantFieldref extends ConstantCP { | |
30 | ||
31 | ||
32 | private static final long serialVersionUID = -8062332095934294437L; | |
33 | ||
34 | /** | |
35 | * Initialize instance from file data. | |
36 | * | |
37 | * @param file input stream | |
38 | * @throws IOException | |
39 | */ | |
40 | ConstantFieldref(DataInputStream file) throws IOException { | |
41 | super(Constants.CONSTANT_Fieldref, file); | |
42 | } | |
43 | } |
31 | 31 | */ |
32 | 32 | public final class ConstantFloat extends Constant { |
33 | 33 | |
34 | private static final long serialVersionUID = 8301269629885378651L; | |
35 | private float bytes; | |
36 | ||
37 | ||
38 | /** | |
39 | * @param bytes Data | |
40 | */ | |
41 | public ConstantFloat(float bytes) { | |
42 | super(Constants.CONSTANT_Float); | |
43 | this.bytes = bytes; | |
44 | } | |
34 | private final float bytes; | |
45 | 35 | |
46 | 36 | |
47 | 37 | /** |
51 | 41 | * @throws IOException |
52 | 42 | */ |
53 | 43 | ConstantFloat(DataInput file) throws IOException { |
54 | this(file.readFloat()); | |
44 | super(Constants.CONSTANT_Float); | |
45 | this.bytes = file.readFloat(); | |
55 | 46 | } |
56 | 47 | |
57 | 48 |
31 | 31 | */ |
32 | 32 | public final class ConstantInteger extends Constant { |
33 | 33 | |
34 | private static final long serialVersionUID = -6415476571232528966L; | |
35 | private int bytes; | |
36 | ||
37 | ||
38 | /** | |
39 | * @param bytes Data | |
40 | */ | |
41 | public ConstantInteger(int bytes) { | |
42 | super(Constants.CONSTANT_Integer); | |
43 | this.bytes = bytes; | |
44 | } | |
34 | private final int bytes; | |
45 | 35 | |
46 | 36 | |
47 | 37 | /** |
51 | 41 | * @throws IOException |
52 | 42 | */ |
53 | 43 | ConstantInteger(DataInput file) throws IOException { |
54 | this(file.readInt()); | |
44 | super(Constants.CONSTANT_Integer); | |
45 | this.bytes = file.readInt(); | |
55 | 46 | } |
56 | 47 | |
57 | 48 |
0 | /* | |
1 | * Licensed to the Apache Software Foundation (ASF) under one or more | |
2 | * contributor license agreements. See the NOTICE file distributed with | |
3 | * this work for additional information regarding copyright ownership. | |
4 | * The ASF licenses this file to You under the Apache License, Version 2.0 | |
5 | * (the "License"); you may not use this file except in compliance with | |
6 | * the License. You may obtain a copy of the License at | |
7 | * | |
8 | * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
15 | * | |
16 | */ | |
17 | package org.apache.tomcat.util.bcel.classfile; | |
18 | ||
19 | import java.io.DataInputStream; | |
20 | import java.io.IOException; | |
21 | ||
22 | import org.apache.tomcat.util.bcel.Constants; | |
23 | ||
24 | /** | |
25 | * This class represents a constant pool reference to an interface method. | |
26 | * | |
27 | * @author <A HREF="mailto:m.dahm@gmx.de">M. Dahm</A> | |
28 | */ | |
29 | public final class ConstantInterfaceMethodref extends ConstantCP { | |
30 | ||
31 | ||
32 | private static final long serialVersionUID = -8587605570227841891L; | |
33 | ||
34 | /** | |
35 | * Initialize instance from file data. | |
36 | * | |
37 | * @param file input stream | |
38 | * @throws IOException | |
39 | */ | |
40 | ConstantInterfaceMethodref(DataInputStream file) throws IOException { | |
41 | super(Constants.CONSTANT_InterfaceMethodref, file); | |
42 | } | |
43 | } |
0 | /* | |
1 | * Licensed to the Apache Software Foundation (ASF) under one or more | |
2 | * contributor license agreements. See the NOTICE file distributed with | |
3 | * this work for additional information regarding copyright ownership. | |
4 | * The ASF licenses this file to You under the Apache License, Version 2.0 | |
5 | * (the "License"); you may not use this file except in compliance with | |
6 | * the License. You may obtain a copy of the License at | |
7 | * | |
8 | * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
15 | * | |
16 | */ | |
17 | package org.apache.tomcat.util.bcel.classfile; | |
18 | ||
19 | import java.io.DataInput; | |
20 | import java.io.IOException; | |
21 | ||
22 | import org.apache.tomcat.util.bcel.Constants; | |
23 | ||
24 | /** | |
25 | * This class is derived from the abstract | |
26 | * <A HREF="org.apache.bcel.classfile.Constant.html">Constant</A> class | |
27 | * and represents a reference to a invoke dynamic. | |
28 | * | |
29 | * @see Constant | |
30 | */ | |
31 | public final class ConstantInvokeDynamic extends Constant { | |
32 | ||
33 | private static final long serialVersionUID = 4310367359017396174L; | |
34 | ||
35 | ||
36 | /** | |
37 | * Initialize instance from file data. | |
38 | * | |
39 | * @param file Input stream | |
40 | * @throws IOException | |
41 | */ | |
42 | ConstantInvokeDynamic(DataInput file) throws IOException { | |
43 | super(Constants.CONSTANT_InvokeDynamic); | |
44 | file.readUnsignedShort(); // Unused bootstrap_method_attr_index | |
45 | file.readUnsignedShort(); // Unused name_and_type_index | |
46 | } | |
47 | } |
31 | 31 | */ |
32 | 32 | public final class ConstantLong extends Constant { |
33 | 33 | |
34 | private static final long serialVersionUID = -1893131676489003562L; | |
35 | private long bytes; | |
36 | ||
37 | ||
38 | /** | |
39 | * @param bytes Data | |
40 | */ | |
41 | public ConstantLong(long bytes) { | |
42 | super(Constants.CONSTANT_Long); | |
43 | this.bytes = bytes; | |
44 | } | |
34 | private final long bytes; | |
45 | 35 | |
46 | 36 | |
47 | 37 | /** |
51 | 41 | * @throws IOException |
52 | 42 | */ |
53 | 43 | ConstantLong(DataInput file) throws IOException { |
54 | this(file.readLong()); | |
44 | super(Constants.CONSTANT_Long); | |
45 | this.bytes = file.readLong(); | |
55 | 46 | } |
56 | 47 | |
57 | 48 |
0 | /* | |
1 | * Licensed to the Apache Software Foundation (ASF) under one or more | |
2 | * contributor license agreements. See the NOTICE file distributed with | |
3 | * this work for additional information regarding copyright ownership. | |
4 | * The ASF licenses this file to You under the Apache License, Version 2.0 | |
5 | * (the "License"); you may not use this file except in compliance with | |
6 | * the License. You may obtain a copy of the License at | |
7 | * | |
8 | * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
15 | * | |
16 | */ | |
17 | package org.apache.tomcat.util.bcel.classfile; | |
18 | ||
19 | import java.io.DataInput; | |
20 | import java.io.IOException; | |
21 | ||
22 | import org.apache.tomcat.util.bcel.Constants; | |
23 | ||
24 | /** | |
25 | * This class is derived from the abstract | |
26 | * <A HREF="org.apache.bcel.classfile.Constant.html">Constant</A> class | |
27 | * and represents a reference to a method handle. | |
28 | * | |
29 | * @see Constant | |
30 | */ | |
31 | public final class ConstantMethodHandle extends Constant { | |
32 | ||
33 | private static final long serialVersionUID = -7875124116920198044L; | |
34 | ||
35 | ||
36 | /** | |
37 | * Initialize instance from file data. | |
38 | * | |
39 | * @param file Input stream | |
40 | * @throws IOException | |
41 | */ | |
42 | ConstantMethodHandle(DataInput file) throws IOException { | |
43 | super(Constants.CONSTANT_MethodHandle); | |
44 | file.readUnsignedByte(); // Unused reference_kind | |
45 | file.readUnsignedShort(); // Unused reference_index | |
46 | } | |
47 | } |
0 | /* | |
1 | * Licensed to the Apache Software Foundation (ASF) under one or more | |
2 | * contributor license agreements. See the NOTICE file distributed with | |
3 | * this work for additional information regarding copyright ownership. | |
4 | * The ASF licenses this file to You under the Apache License, Version 2.0 | |
5 | * (the "License"); you may not use this file except in compliance with | |
6 | * the License. You may obtain a copy of the License at | |
7 | * | |
8 | * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
15 | * | |
16 | */ | |
17 | package org.apache.tomcat.util.bcel.classfile; | |
18 | ||
19 | import java.io.DataInput; | |
20 | import java.io.IOException; | |
21 | ||
22 | import org.apache.tomcat.util.bcel.Constants; | |
23 | ||
24 | /** | |
25 | * This class is derived from the abstract | |
26 | * <A HREF="org.apache.bcel.classfile.Constant.html">Constant</A> class | |
27 | * and represents a reference to a method type. | |
28 | * | |
29 | * @see Constant | |
30 | */ | |
31 | public final class ConstantMethodType extends Constant { | |
32 | ||
33 | private static final long serialVersionUID = 6750768220616618881L; | |
34 | ||
35 | ||
36 | /** | |
37 | * Initialize instance from file data. | |
38 | * | |
39 | * @param file Input stream | |
40 | * @throws IOException | |
41 | */ | |
42 | ConstantMethodType(DataInput file) throws IOException { | |
43 | super(Constants.CONSTANT_MethodType); | |
44 | file.readUnsignedShort(); // Unused descriptor_index | |
45 | } | |
46 | } |
0 | /* | |
1 | * Licensed to the Apache Software Foundation (ASF) under one or more | |
2 | * contributor license agreements. See the NOTICE file distributed with | |
3 | * this work for additional information regarding copyright ownership. | |
4 | * The ASF licenses this file to You under the Apache License, Version 2.0 | |
5 | * (the "License"); you may not use this file except in compliance with | |
6 | * the License. You may obtain a copy of the License at | |
7 | * | |
8 | * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
15 | * | |
16 | */ | |
17 | package org.apache.tomcat.util.bcel.classfile; | |
18 | ||
19 | import java.io.DataInputStream; | |
20 | import java.io.IOException; | |
21 | ||
22 | import org.apache.tomcat.util.bcel.Constants; | |
23 | ||
24 | /** | |
25 | * This class represents a constant pool reference to a method. | |
26 | * | |
27 | * @author <A HREF="mailto:m.dahm@gmx.de">M. Dahm</A> | |
28 | */ | |
29 | public final class ConstantMethodref extends ConstantCP { | |
30 | ||
31 | ||
32 | private static final long serialVersionUID = -7857009620954576086L; | |
33 | ||
34 | /** | |
35 | * Initialize instance from file data. | |
36 | * | |
37 | * @param file input stream | |
38 | * @throws IOException | |
39 | */ | |
40 | ConstantMethodref(DataInputStream file) throws IOException { | |
41 | super(Constants.CONSTANT_Methodref, file); | |
42 | } | |
43 | } |
0 | /* | |
1 | * Licensed to the Apache Software Foundation (ASF) under one or more | |
2 | * contributor license agreements. See the NOTICE file distributed with | |
3 | * this work for additional information regarding copyright ownership. | |
4 | * The ASF licenses this file to You under the Apache License, Version 2.0 | |
5 | * (the "License"); you may not use this file except in compliance with | |
6 | * the License. You may obtain a copy of the License at | |
7 | * | |
8 | * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
15 | * | |
16 | */ | |
17 | package org.apache.tomcat.util.bcel.classfile; | |
18 | ||
19 | import java.io.DataInput; | |
20 | import java.io.IOException; | |
21 | ||
22 | import org.apache.tomcat.util.bcel.Constants; | |
23 | ||
24 | /** | |
25 | * This class is derived from the abstract | |
26 | * <A HREF="org.apache.tomcat.util.bcel.classfile.Constant.html">Constant</A> class | |
27 | * and represents a reference to the name and signature | |
28 | * of a field or method. | |
29 | * | |
30 | * @author <A HREF="mailto:m.dahm@gmx.de">M. Dahm</A> | |
31 | * @see Constant | |
32 | */ | |
33 | public final class ConstantNameAndType extends Constant { | |
34 | ||
35 | private static final long serialVersionUID = 1010506730811368756L; | |
36 | private int name_index; // Name of field/method | |
37 | private int signature_index; // and its signature. | |
38 | ||
39 | ||
40 | /** | |
41 | * Initialize instance from file data. | |
42 | * | |
43 | * @param file Input stream | |
44 | * @throws IOException | |
45 | */ | |
46 | ConstantNameAndType(DataInput file) throws IOException { | |
47 | this(file.readUnsignedShort(), file.readUnsignedShort()); | |
48 | } | |
49 | ||
50 | ||
51 | /** | |
52 | * @param name_index Name of field/method | |
53 | * @param signature_index and its signature | |
54 | */ | |
55 | public ConstantNameAndType(int name_index, int signature_index) { | |
56 | super(Constants.CONSTANT_NameAndType); | |
57 | this.name_index = name_index; | |
58 | this.signature_index = signature_index; | |
59 | } | |
60 | ||
61 | ||
62 | /** | |
63 | * @return Name index in constant pool of field/method name. | |
64 | */ | |
65 | public final int getNameIndex() { | |
66 | return name_index; | |
67 | } | |
68 | ||
69 | ||
70 | /** | |
71 | * @return Index in constant pool of field/method signature. | |
72 | */ | |
73 | public final int getSignatureIndex() { | |
74 | return signature_index; | |
75 | } | |
76 | } |
16 | 16 | */ |
17 | 17 | package org.apache.tomcat.util.bcel.classfile; |
18 | 18 | |
19 | import java.io.DataInputStream; | |
19 | import java.io.DataInput; | |
20 | 20 | import java.io.IOException; |
21 | import java.io.Serializable; | |
22 | 21 | |
23 | 22 | import org.apache.tomcat.util.bcel.Constants; |
24 | 23 | |
33 | 32 | * @see Constant |
34 | 33 | * @author <A HREF="mailto:m.dahm@gmx.de">M. Dahm</A> |
35 | 34 | */ |
36 | public class ConstantPool implements Cloneable, Serializable { | |
35 | public class ConstantPool { | |
37 | 36 | |
38 | private static final long serialVersionUID = -6765503791185687014L; | |
39 | private int constant_pool_count; | |
40 | private Constant[] constant_pool; | |
37 | private final Constant[] constant_pool; | |
41 | 38 | |
42 | 39 | |
43 | 40 | /** |
47 | 44 | * @throws IOException |
48 | 45 | * @throws ClassFormatException |
49 | 46 | */ |
50 | ConstantPool(DataInputStream file) throws IOException, ClassFormatException { | |
51 | byte tag; | |
52 | constant_pool_count = file.readUnsignedShort(); | |
47 | ConstantPool(DataInput file) throws IOException, ClassFormatException { | |
48 | int constant_pool_count = file.readUnsignedShort(); | |
53 | 49 | constant_pool = new Constant[constant_pool_count]; |
54 | 50 | /* constant_pool[0] is unused by the compiler and may be used freely |
55 | 51 | * by the implementation. |
63 | 59 | * |
64 | 60 | * Thus we have to increment the index counter. |
65 | 61 | */ |
66 | tag = constant_pool[i].getTag(); | |
67 | if ((tag == Constants.CONSTANT_Double) || (tag == Constants.CONSTANT_Long)) { | |
68 | i++; | |
62 | if (constant_pool[i] != null) { | |
63 | byte tag = constant_pool[i].getTag(); | |
64 | if ((tag == Constants.CONSTANT_Double) || (tag == Constants.CONSTANT_Long)) { | |
65 | i++; | |
66 | } | |
69 | 67 | } |
70 | 68 | } |
71 | } | |
72 | ||
73 | ||
74 | /** | |
75 | * Resolve constant to a string representation. | |
76 | * | |
77 | * @param c Constant to be printed | |
78 | * @return String representation | |
79 | */ | |
80 | public String constantToString( Constant c ) throws ClassFormatException { | |
81 | String str; | |
82 | int i; | |
83 | byte tag = c.getTag(); | |
84 | switch (tag) { | |
85 | case Constants.CONSTANT_Class: | |
86 | i = ((ConstantClass) c).getNameIndex(); | |
87 | c = getConstant(i, Constants.CONSTANT_Utf8); | |
88 | str = Utility.compactClassName(((ConstantUtf8) c).getBytes()); | |
89 | break; | |
90 | case Constants.CONSTANT_String: | |
91 | i = ((ConstantString) c).getStringIndex(); | |
92 | c = getConstant(i, Constants.CONSTANT_Utf8); | |
93 | str = "\"" + escape(((ConstantUtf8) c).getBytes()) + "\""; | |
94 | break; | |
95 | case Constants.CONSTANT_Utf8: | |
96 | str = ((ConstantUtf8) c).getBytes(); | |
97 | break; | |
98 | case Constants.CONSTANT_Double: | |
99 | str = String.valueOf(((ConstantDouble) c).getBytes()); | |
100 | break; | |
101 | case Constants.CONSTANT_Float: | |
102 | str = String.valueOf(((ConstantFloat) c).getBytes()); | |
103 | break; | |
104 | case Constants.CONSTANT_Long: | |
105 | str = String.valueOf(((ConstantLong) c).getBytes()); | |
106 | break; | |
107 | case Constants.CONSTANT_Integer: | |
108 | str = String.valueOf(((ConstantInteger) c).getBytes()); | |
109 | break; | |
110 | case Constants.CONSTANT_NameAndType: | |
111 | str = (constantToString(((ConstantNameAndType) c).getNameIndex(), | |
112 | Constants.CONSTANT_Utf8) | |
113 | + " " + constantToString(((ConstantNameAndType) c).getSignatureIndex(), | |
114 | Constants.CONSTANT_Utf8)); | |
115 | break; | |
116 | case Constants.CONSTANT_InterfaceMethodref: | |
117 | case Constants.CONSTANT_Methodref: | |
118 | case Constants.CONSTANT_Fieldref: | |
119 | str = (constantToString(((ConstantCP) c).getClassIndex(), Constants.CONSTANT_Class) | |
120 | + "." + constantToString(((ConstantCP) c).getNameAndTypeIndex(), | |
121 | Constants.CONSTANT_NameAndType)); | |
122 | break; | |
123 | default: // Never reached | |
124 | throw new RuntimeException("Unknown constant type " + tag); | |
125 | } | |
126 | return str; | |
127 | } | |
128 | ||
129 | ||
130 | private static String escape( String str ) { | |
131 | int len = str.length(); | |
132 | StringBuilder buf = new StringBuilder(len + 5); | |
133 | char[] ch = str.toCharArray(); | |
134 | for (int i = 0; i < len; i++) { | |
135 | switch (ch[i]) { | |
136 | case '\n': | |
137 | buf.append("\\n"); | |
138 | break; | |
139 | case '\r': | |
140 | buf.append("\\r"); | |
141 | break; | |
142 | case '\t': | |
143 | buf.append("\\t"); | |
144 | break; | |
145 | case '\b': | |
146 | buf.append("\\b"); | |
147 | break; | |
148 | case '"': | |
149 | buf.append("\\\""); | |
150 | break; | |
151 | default: | |
152 | buf.append(ch[i]); | |
153 | } | |
154 | } | |
155 | return buf.toString(); | |
156 | } | |
157 | ||
158 | ||
159 | /** | |
160 | * Retrieve constant at `index' from constant pool and resolve it to | |
161 | * a string representation. | |
162 | * | |
163 | * @param index of constant in constant pool | |
164 | * @param tag expected type | |
165 | * @return String representation | |
166 | */ | |
167 | public String constantToString( int index, byte tag ) throws ClassFormatException { | |
168 | Constant c = getConstant(index, tag); | |
169 | return constantToString(c); | |
170 | 69 | } |
171 | 70 | |
172 | 71 | |
208 | 107 | } |
209 | 108 | return c; |
210 | 109 | } |
211 | ||
212 | ||
213 | /** | |
214 | * Get string from constant pool and bypass the indirection of | |
215 | * `ConstantClass' and `ConstantString' objects. I.e. these classes have | |
216 | * an index field that points to another entry of the constant pool of | |
217 | * type `ConstantUtf8' which contains the real data. | |
218 | * | |
219 | * @param index Index in constant pool | |
220 | * @param tag Tag of expected constant, either ConstantClass or ConstantString | |
221 | * @return Contents of string reference | |
222 | * @see ConstantClass | |
223 | * @see ConstantString | |
224 | * @throws ClassFormatException | |
225 | */ | |
226 | public String getConstantString( int index, byte tag ) throws ClassFormatException { | |
227 | Constant c; | |
228 | int i; | |
229 | c = getConstant(index, tag); | |
230 | /* This switch() is not that elegant, since the two classes have the | |
231 | * same contents, they just differ in the name of the index | |
232 | * field variable. | |
233 | * But we want to stick to the JVM naming conventions closely though | |
234 | * we could have solved these more elegantly by using the same | |
235 | * variable name or by subclassing. | |
236 | */ | |
237 | switch (tag) { | |
238 | case Constants.CONSTANT_Class: | |
239 | i = ((ConstantClass) c).getNameIndex(); | |
240 | break; | |
241 | case Constants.CONSTANT_String: | |
242 | i = ((ConstantString) c).getStringIndex(); | |
243 | break; | |
244 | default: | |
245 | throw new RuntimeException("getConstantString called with illegal tag " + tag); | |
246 | } | |
247 | // Finally get the string from the constant pool | |
248 | c = getConstant(i, Constants.CONSTANT_Utf8); | |
249 | return ((ConstantUtf8) c).getBytes(); | |
250 | } | |
251 | 110 | } |
0 | /* | |
1 | * Licensed to the Apache Software Foundation (ASF) under one or more | |
2 | * contributor license agreements. See the NOTICE file distributed with | |
3 | * this work for additional information regarding copyright ownership. | |
4 | * The ASF licenses this file to You under the Apache License, Version 2.0 | |
5 | * (the "License"); you may not use this file except in compliance with | |
6 | * the License. You may obtain a copy of the License at | |
7 | * | |
8 | * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
15 | * | |
16 | */ | |
17 | package org.apache.tomcat.util.bcel.classfile; | |
18 | ||
19 | import java.io.DataInput; | |
20 | import java.io.IOException; | |
21 | ||
22 | import org.apache.tomcat.util.bcel.Constants; | |
23 | ||
24 | /** | |
25 | * This class is derived from the abstract | |
26 | * <A HREF="org.apache.tomcat.util.bcel.classfile.Constant.html">Constant</A> class | |
27 | * and represents a reference to a String object. | |
28 | * | |
29 | * @author <A HREF="mailto:m.dahm@gmx.de">M. Dahm</A> | |
30 | * @see Constant | |
31 | */ | |
32 | public final class ConstantString extends Constant { | |
33 | ||
34 | private static final long serialVersionUID = 2809338612858801341L; | |
35 | private int string_index; // Identical to ConstantClass except for this name | |
36 | ||
37 | ||
38 | /** | |
39 | * Initialize instance from file data. | |
40 | * | |
41 | * @param file Input stream | |
42 | * @throws IOException | |
43 | */ | |
44 | ConstantString(DataInput file) throws IOException { | |
45 | this(file.readUnsignedShort()); | |
46 | } | |
47 | ||
48 | ||
49 | /** | |
50 | * @param string_index Index of Constant_Utf8 in constant pool | |
51 | */ | |
52 | public ConstantString(int string_index) { | |
53 | super(Constants.CONSTANT_String); | |
54 | this.string_index = string_index; | |
55 | } | |
56 | ||
57 | ||
58 | /** | |
59 | * @return Index in constant pool of the string (ConstantUtf8). | |
60 | */ | |
61 | public final int getStringIndex() { | |
62 | return string_index; | |
63 | } | |
64 | } |
16 | 16 | package org.apache.tomcat.util.bcel.classfile; |
17 | 17 | |
18 | 18 | import java.io.DataInput; |
19 | import java.io.DataInputStream; | |
20 | 19 | import java.io.IOException; |
21 | import java.util.HashMap; | |
22 | import java.util.LinkedHashMap; | |
23 | import java.util.Map; | |
24 | 20 | |
25 | 21 | import org.apache.tomcat.util.bcel.Constants; |
26 | 22 | |
34 | 30 | */ |
35 | 31 | public final class ConstantUtf8 extends Constant { |
36 | 32 | |
37 | private static final long serialVersionUID = 8119001312020421976L; | |
38 | 33 | private final String bytes; |
39 | 34 | |
40 | private static final int MAX_CACHE_ENTRIES = 20000; | |
41 | private static final int INITIAL_CACHE_CAPACITY = (int)(MAX_CACHE_ENTRIES/0.75); | |
42 | private static HashMap<String, ConstantUtf8> cache; | |
43 | 35 | |
44 | private static synchronized ConstantUtf8 getCachedInstance(String s) { | |
45 | if (s.length() > 200) { | |
46 | return new ConstantUtf8(s); | |
47 | } | |
48 | if (cache == null) { | |
49 | cache = new LinkedHashMap<String, ConstantUtf8>(INITIAL_CACHE_CAPACITY, 0.75f, true) { | |
50 | private static final long serialVersionUID = 1L; | |
51 | ||
52 | @Override | |
53 | protected boolean removeEldestEntry(Map.Entry<String, ConstantUtf8> eldest) { | |
54 | return size() > MAX_CACHE_ENTRIES; | |
55 | } | |
56 | }; | |
57 | } | |
58 | ConstantUtf8 result = cache.get(s); | |
59 | if (result != null) { | |
60 | return result; | |
61 | } | |
62 | result = new ConstantUtf8(s); | |
63 | cache.put(s, result); | |
64 | return result; | |
65 | } | |
66 | ||
67 | private static ConstantUtf8 getInstance(String s) { | |
68 | return getCachedInstance(s); | |
69 | } | |
70 | ||
71 | static ConstantUtf8 getInstance(DataInputStream file) throws IOException { | |
72 | return getInstance(file.readUTF()); | |
73 | } | |
74 | ||
75 | /** | |
76 | * Initialize instance from file data. | |
77 | * | |
78 | * @param file Input stream | |
79 | * @throws IOException | |
80 | */ | |
81 | ConstantUtf8(DataInput file) throws IOException { | |
82 | super(Constants.CONSTANT_Utf8); | |
83 | bytes = file.readUTF(); | |
36 | static ConstantUtf8 getInstance(DataInput file) throws IOException { | |
37 | return new ConstantUtf8(file.readUTF()); | |
84 | 38 | } |
85 | 39 | |
86 | 40 |
0 | /* | |
1 | * Licensed to the Apache Software Foundation (ASF) under one or more | |
2 | * contributor license agreements. See the NOTICE file distributed with | |
3 | * this work for additional information regarding copyright ownership. | |
4 | * The ASF licenses this file to You under the Apache License, Version 2.0 | |
5 | * (the "License"); you may not use this file except in compliance with | |
6 | * the License. You may obtain a copy of the License at | |
7 | * | |
8 | * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
15 | */ | |
16 | package org.apache.tomcat.util.bcel.classfile; | |
17 | ||
18 | import java.io.DataInput; | |
19 | import java.io.IOException; | |
20 | ||
21 | /** | |
22 | * This class is derived from <em>Attribute</em> and represents a constant | |
23 | * value, i.e., a default value for initializing a class field. | |
24 | * This class is instantiated by the <em>Attribute.readAttribute()</em> method. | |
25 | * | |
26 | * @author <A HREF="mailto:m.dahm@gmx.de">M. Dahm</A> | |
27 | * @see Attribute | |
28 | */ | |
29 | public final class ConstantValue extends Attribute { | |
30 | ||
31 | private static final long serialVersionUID = -388222612752527969L; | |
32 | ||
33 | ||
34 | /** | |
35 | * Construct object from file stream. | |
36 | * @param name_index Name index in constant pool | |
37 | * @param length Content length in bytes | |
38 | * @param file Input stream | |
39 | * @param constant_pool Array of constants | |
40 | * @throws IOException | |
41 | */ | |
42 | ConstantValue(int name_index, int length, DataInput file, ConstantPool constant_pool) | |
43 | throws IOException { | |
44 | super(name_index, length, constant_pool); | |
45 | file.readUnsignedShort(); // Unused constantvalue_index | |
46 | } | |
47 | } |
0 | /* | |
1 | * Licensed to the Apache Software Foundation (ASF) under one or more | |
2 | * contributor license agreements. See the NOTICE file distributed with | |
3 | * this work for additional information regarding copyright ownership. | |
4 | * The ASF licenses this file to You under the Apache License, Version 2.0 | |
5 | * (the "License"); you may not use this file except in compliance with | |
6 | * the License. You may obtain a copy of the License at | |
7 | * | |
8 | * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
15 | * | |
16 | */ | |
17 | package org.apache.tomcat.util.bcel.classfile; | |
18 | ||
19 | import java.io.DataInputStream; | |
20 | import java.io.IOException; | |
21 | ||
22 | /** | |
23 | * This class is derived from <em>Attribute</em> and denotes that this is a | |
24 | * deprecated method. | |
25 | * It is instantiated from the <em>Attribute.readAttribute()</em> method. | |
26 | * | |
27 | * @author <A HREF="mailto:m.dahm@gmx.de">M. Dahm</A> | |
28 | * @see Attribute | |
29 | */ | |
30 | public final class Deprecated extends Attribute { | |
31 | ||
32 | private static final long serialVersionUID = 8499975360019639912L; | |
33 | private byte[] bytes; | |
34 | ||
35 | ||
36 | /** | |
37 | * @param name_index Index in constant pool to CONSTANT_Utf8 | |
38 | * @param length Content length in bytes | |
39 | * @param bytes Attribute contents | |
40 | * @param constant_pool Array of constants | |
41 | */ | |
42 | public Deprecated(int name_index, int length, byte[] bytes, ConstantPool constant_pool) { | |
43 | super(name_index, length, constant_pool); | |
44 | this.bytes = bytes; | |
45 | } | |
46 | ||
47 | ||
48 | /** | |
49 | * Construct object from file stream. | |
50 | * @param name_index Index in constant pool to CONSTANT_Utf8 | |
51 | * @param length Content length in bytes | |
52 | * @param file Input stream | |
53 | * @param constant_pool Array of constants | |
54 | * @throws IOException | |
55 | */ | |
56 | Deprecated(int name_index, int length, DataInputStream file, ConstantPool constant_pool) | |
57 | throws IOException { | |
58 | this(name_index, length, (byte[]) null, constant_pool); | |
59 | if (length > 0) { | |
60 | bytes = new byte[length]; | |
61 | file.readFully(bytes); | |
62 | System.err.println("Deprecated attribute with length > 0"); | |
63 | } | |
64 | } | |
65 | } |
16 | 16 | */ |
17 | 17 | package org.apache.tomcat.util.bcel.classfile; |
18 | 18 | |
19 | import java.io.DataInputStream; | |
19 | import java.io.DataInput; | |
20 | 20 | import java.io.IOException; |
21 | 21 | |
22 | 22 | /** |
25 | 25 | */ |
26 | 26 | public abstract class ElementValue |
27 | 27 | { |
28 | protected int type; | |
28 | protected final int type; | |
29 | 29 | |
30 | protected ConstantPool cpool; | |
30 | protected final ConstantPool cpool; | |
31 | 31 | |
32 | 32 | |
33 | protected ElementValue(int type, ConstantPool cpool) | |
34 | { | |
33 | ElementValue(int type, ConstantPool cpool) { | |
35 | 34 | this.type = type; |
36 | 35 | this.cpool = cpool; |
37 | 36 | } |
64 | 63 | |
65 | 64 | public static final int PRIMITIVE_BOOLEAN = 'Z'; |
66 | 65 | |
67 | public static ElementValue readElementValue(DataInputStream dis, | |
66 | public static ElementValue readElementValue(DataInput dis, | |
68 | 67 | ConstantPool cpool) throws IOException |
69 | 68 | { |
70 | 69 | byte type = dis.readByte(); |
105 | 104 | return new ClassElementValue(CLASS, dis.readUnsignedShort(), cpool); |
106 | 105 | case '@': // Annotation |
107 | 106 | // TODO isRuntimeVisible |
108 | return new AnnotationElementValue(ANNOTATION, AnnotationEntry.read( | |
107 | return new AnnotationElementValue(ANNOTATION, new AnnotationEntry( | |
109 | 108 | dis, cpool), cpool); |
110 | 109 | case '[': // Array |
111 | 110 | int numArrayVals = dis.readUnsignedShort(); |
116 | 115 | } |
117 | 116 | return new ArrayElementValue(ARRAY, evalues, cpool); |
118 | 117 | default: |
119 | throw new RuntimeException( | |
118 | throw new ClassFormatException( | |
120 | 119 | "Unexpected element value kind in annotation: " + type); |
121 | 120 | } |
122 | 121 | } |
16 | 16 | */ |
17 | 17 | package org.apache.tomcat.util.bcel.classfile; |
18 | 18 | |
19 | import java.io.DataInput; | |
20 | import java.io.IOException; | |
21 | ||
19 | 22 | import org.apache.tomcat.util.bcel.Constants; |
20 | 23 | |
21 | 24 | /** |
26 | 29 | */ |
27 | 30 | public class ElementValuePair |
28 | 31 | { |
29 | private ElementValue elementValue; | |
32 | private final ElementValue elementValue; | |
30 | 33 | |
31 | private ConstantPool constantPool; | |
34 | private final ConstantPool constantPool; | |
32 | 35 | |
33 | private int elementNameIndex; | |
36 | private final int elementNameIndex; | |
34 | 37 | |
35 | public ElementValuePair(int elementNameIndex, ElementValue elementValue, | |
36 | ConstantPool constantPool) | |
37 | { | |
38 | this.elementValue = elementValue; | |
39 | this.elementNameIndex = elementNameIndex; | |
38 | ElementValuePair(DataInput file, ConstantPool constantPool) throws IOException { | |
40 | 39 | this.constantPool = constantPool; |
40 | this.elementNameIndex = file.readUnsignedShort(); | |
41 | this.elementValue = ElementValue.readElementValue(file, constantPool); | |
41 | 42 | } |
42 | 43 | |
43 | 44 | public String getNameString() |
0 | /** | |
1 | * Licensed to the Apache Software Foundation (ASF) under one or more | |
2 | * contributor license agreements. See the NOTICE file distributed with | |
3 | * this work for additional information regarding copyright ownership. | |
4 | * The ASF licenses this file to You under the Apache License, Version 2.0 | |
5 | * (the "License"); you may not use this file except in compliance with | |
6 | * the License. You may obtain a copy of the License at | |
7 | * | |
8 | * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
15 | */ | |
16 | package org.apache.tomcat.util.bcel.classfile; | |
17 | ||
18 | import java.io.DataInputStream; | |
19 | import java.io.IOException; | |
20 | ||
21 | /** | |
22 | * This attribute exists for local or | |
23 | * anonymous classes and ... there can be only one. | |
24 | */ | |
25 | public class EnclosingMethod extends Attribute { | |
26 | ||
27 | private static final long serialVersionUID = 6755214228300933233L; | |
28 | ||
29 | // Ctors - and code to read an attribute in. | |
30 | public EnclosingMethod(int nameIndex, int len, DataInputStream dis, | |
31 | ConstantPool cpool) throws IOException { | |
32 | super(nameIndex, len, cpool); | |
33 | // Unused class index | |
34 | dis.readUnsignedShort(); | |
35 | // Unused method index | |
36 | dis.readUnsignedShort(); | |
37 | } | |
38 | } |
20 | 20 | |
21 | 21 | public class EnumElementValue extends ElementValue |
22 | 22 | { |
23 | private int valueIdx; | |
23 | private final int valueIdx; | |
24 | 24 | |
25 | public EnumElementValue(int type, int valueIdx, ConstantPool cpool) | |
26 | { | |
25 | EnumElementValue(int type, int valueIdx, ConstantPool cpool) { | |
27 | 26 | super(type, cpool); |
28 | 27 | if (type != ENUM_CONSTANT) |
29 | 28 | throw new RuntimeException( |
0 | /* | |
1 | * Licensed to the Apache Software Foundation (ASF) under one or more | |
2 | * contributor license agreements. See the NOTICE file distributed with | |
3 | * this work for additional information regarding copyright ownership. | |
4 | * The ASF licenses this file to You under the Apache License, Version 2.0 | |
5 | * (the "License"); you may not use this file except in compliance with | |
6 | * the License. You may obtain a copy of the License at | |
7 | * | |
8 | * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
15 | * | |
16 | */ | |
17 | package org.apache.tomcat.util.bcel.classfile; | |
18 | ||
19 | import java.io.DataInputStream; | |
20 | import java.io.IOException; | |
21 | ||
22 | /** | |
23 | * This class represents the table of exceptions that are thrown by a | |
24 | * method. This attribute may be used once per method. The name of | |
25 | * this class is <em>ExceptionTable</em> for historical reasons; The | |
26 | * Java Virtual Machine Specification, Second Edition defines this | |
27 | * attribute using the name <em>Exceptions</em> (which is inconsistent | |
28 | * with the other classes). | |
29 | * | |
30 | * @author <A HREF="mailto:m.dahm@gmx.de">M. Dahm</A> | |
31 | * @see Code | |
32 | */ | |
33 | public final class ExceptionTable extends Attribute { | |
34 | ||
35 | private static final long serialVersionUID = -5109672682663772900L; | |
36 | private int number_of_exceptions; // Table of indices into | |
37 | private int[] exception_index_table; // constant pool | |
38 | ||
39 | ||
40 | /** | |
41 | * @param name_index Index in constant pool | |
42 | * @param length Content length in bytes | |
43 | * @param exception_index_table Table of indices in constant pool | |
44 | * @param constant_pool Array of constants | |
45 | */ | |
46 | public ExceptionTable(int name_index, int length, int[] exception_index_table, | |
47 | ConstantPool constant_pool) { | |
48 | super(name_index, length, constant_pool); | |
49 | setExceptionIndexTable(exception_index_table); | |
50 | } | |
51 | ||
52 | ||
53 | /** | |
54 | * Construct object from file stream. | |
55 | * @param name_index Index in constant pool | |
56 | * @param length Content length in bytes | |
57 | * @param file Input stream | |
58 | * @param constant_pool Array of constants | |
59 | * @throws IOException | |
60 | */ | |
61 | ExceptionTable(int name_index, int length, DataInputStream file, ConstantPool constant_pool) | |
62 | throws IOException { | |
63 | this(name_index, length, (int[]) null, constant_pool); | |
64 | number_of_exceptions = file.readUnsignedShort(); | |
65 | exception_index_table = new int[number_of_exceptions]; | |
66 | for (int i = 0; i < number_of_exceptions; i++) { | |
67 | exception_index_table[i] = file.readUnsignedShort(); | |
68 | } | |
69 | } | |
70 | ||
71 | ||
72 | /** | |
73 | * @param exception_index_table the list of exception indexes | |
74 | * Also redefines number_of_exceptions according to table length. | |
75 | */ | |
76 | public final void setExceptionIndexTable( int[] exception_index_table ) { | |
77 | this.exception_index_table = exception_index_table; | |
78 | number_of_exceptions = (exception_index_table == null) ? 0 : exception_index_table.length; | |
79 | } | |
80 | } |
0 | /* | |
1 | * Licensed to the Apache Software Foundation (ASF) under one or more | |
2 | * contributor license agreements. See the NOTICE file distributed with | |
3 | * this work for additional information regarding copyright ownership. | |
4 | * The ASF licenses this file to You under the Apache License, Version 2.0 | |
5 | * (the "License"); you may not use this file except in compliance with | |
6 | * the License. You may obtain a copy of the License at | |
7 | * | |
8 | * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
15 | */ | |
16 | package org.apache.tomcat.util.bcel.classfile; | |
17 | ||
18 | import java.io.BufferedInputStream; | |
19 | import java.io.DataInput; | |
20 | import java.io.DataInputStream; | |
21 | import java.io.EOFException; | |
22 | import java.io.IOException; | |
23 | import java.io.InputStream; | |
24 | ||
25 | /** | |
26 | * A "FastDataInputStream" that get the numbers from buffer and from the target | |
27 | * directly instead of "DataInputStream". | |
28 | */ | |
29 | class FastDataInputStream extends BufferedInputStream implements DataInput { | |
30 | ||
31 | private final byte readBuffer[] = new byte[8]; | |
32 | ||
33 | ||
34 | public FastDataInputStream(InputStream in, int size) { | |
35 | super(in, size); | |
36 | } | |
37 | ||
38 | ||
39 | @Override | |
40 | public final int read(byte b[]) throws IOException { | |
41 | return this.read(b, 0, b.length); | |
42 | } | |
43 | ||
44 | ||
45 | @Override | |
46 | public final void readFully(byte b[]) throws IOException { | |
47 | readFully(b, 0, b.length); | |
48 | } | |
49 | ||
50 | ||
51 | @Override | |
52 | public final void readFully(byte b[], int off, int len) throws IOException { | |
53 | if (len < 0) | |
54 | throw new IndexOutOfBoundsException(); | |
55 | // Total read | |
56 | int sum = 0; | |
57 | // Current read | |
58 | int cur = 0; | |
59 | for(; sum < len; sum += cur){ | |
60 | cur = read(b, off + sum, len - sum); | |
61 | if(cur < 0) | |
62 | throw new EOFException(); | |
63 | sum += cur; | |
64 | } | |
65 | } | |
66 | ||
67 | ||
68 | @Override | |
69 | public boolean readBoolean() throws IOException { | |
70 | if (pos >= count) { | |
71 | fillNew(); | |
72 | if (pos >= count) | |
73 | throw new EOFException(); | |
74 | } | |
75 | int ch = this.buf[pos++] & 0xff; | |
76 | return (ch != 0); | |
77 | } | |
78 | ||
79 | ||
80 | @Override | |
81 | public final byte readByte() throws IOException { | |
82 | if (pos >= count) { | |
83 | fillNew(); | |
84 | if (pos >= count) | |
85 | throw new EOFException(); | |
86 | } | |
87 | return this.buf[pos++]; | |
88 | } | |
89 | ||
90 | ||
91 | @Override | |
92 | public int readUnsignedByte() throws IOException { | |
93 | if (pos >= count) { | |
94 | fillNew(); | |
95 | if (pos >= count) | |
96 | throw new EOFException(); | |
97 | } | |
98 | int ch = this.buf[pos++] & 0xff; | |
99 | return ch; | |
100 | } | |
101 | ||
102 | ||
103 | @Override | |
104 | public final short readShort() throws IOException { | |
105 | if(pos + 1 >= count){ | |
106 | fillNew(); | |
107 | if(pos + 1 >= count) throw new EOFException(); | |
108 | } | |
109 | int ch1 = this.buf[pos++] & 0xff; | |
110 | int ch2 = this.buf[pos++] & 0xff; | |
111 | return (short)((ch1 << 8) + ch2); | |
112 | } | |
113 | ||
114 | ||
115 | @Override | |
116 | public int readUnsignedShort() throws IOException{ | |
117 | if(pos + 1 >= count) { | |
118 | fillNew(); | |
119 | if(pos + 1 >= count) throw new EOFException(); | |
120 | } | |
121 | ||
122 | int ch1 = this.buf[pos++] & 0xff; | |
123 | int ch2 = this.buf[pos++] & 0xff; | |
124 | return (ch1 << 8) + ch2; | |
125 | } | |
126 | ||
127 | ||
128 | @Override | |
129 | public final char readChar() throws IOException { | |
130 | if(pos + 1 >= count) { | |
131 | fillNew(); | |
132 | if(pos + 1 >= count) throw new EOFException(); | |
133 | } | |
134 | int ch1 = this.buf[pos++] & 0xff; | |
135 | int ch2 = this.buf[pos++] & 0xff; | |
136 | return (char)((ch1 << 8) + ch2); | |
137 | } | |
138 | ||
139 | ||
140 | @Override | |
141 | public final int readInt() throws IOException { | |
142 | if(pos + 3 >= count){ | |
143 | fillNew(); | |
144 | if(pos + 3 >= count) throw new EOFException(); | |
145 | } | |
146 | int ch1 = this.buf[pos++] & 0xff; | |
147 | int ch2 = this.buf[pos++] & 0xff; | |
148 | int ch3 = this.buf[pos++] & 0xff; | |
149 | int ch4 = this.buf[pos++] & 0xff; | |
150 | return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + ch4); | |
151 | } | |
152 | ||
153 | ||
154 | @Override | |
155 | public final long readLong() throws IOException { | |
156 | readFully(readBuffer, 0, 8); | |
157 | return (((long)readBuffer[0] << 56) + | |
158 | ((long)(readBuffer[1] & 255) << 48) + | |
159 | ((long)(readBuffer[2] & 255) << 40) + | |
160 | ((long)(readBuffer[3] & 255) << 32) + | |
161 | ((long)(readBuffer[4] & 255) << 24) + | |
162 | ((readBuffer[5] & 255) << 16) + | |
163 | ((readBuffer[6] & 255) << 8) + | |
164 | (readBuffer[7] & 255)); | |
165 | } | |
166 | ||
167 | ||
168 | @Override | |
169 | public final float readFloat() throws IOException { | |
170 | return Float.intBitsToFloat(readInt()); | |
171 | } | |
172 | ||
173 | ||
174 | @Override | |
175 | public double readDouble() throws IOException { | |
176 | return Double.longBitsToDouble(readLong()); | |
177 | } | |
178 | ||
179 | ||
180 | @Override | |
181 | public final String readUTF() throws IOException { | |
182 | return DataInputStream.readUTF(this); | |
183 | } | |
184 | ||
185 | ||
186 | private void fillNew() throws IOException { | |
187 | int remain = 0; | |
188 | if(pos < count){ | |
189 | remain = count - pos; | |
190 | System.arraycopy(buf, pos, buf, 0, remain); | |
191 | } | |
192 | pos = 0; | |
193 | int n = this.in.read(buf, remain, buf.length - remain); | |
194 | count = pos + n + remain; | |
195 | } | |
196 | ||
197 | ||
198 | @Override | |
199 | public int skipBytes(int n) throws IOException { | |
200 | int avail = count - pos; | |
201 | // Total Skipped | |
202 | int sum = 0; | |
203 | // Current skipped | |
204 | int cur = 0; | |
205 | if (avail <= 0) { | |
206 | // buffer is exhausted, read via stream directly | |
207 | while (sum < n && (cur = (int) in.skip(n - sum)) > 0) { | |
208 | sum += cur; | |
209 | } | |
210 | return sum; | |
211 | } | |
212 | // Data in the buffer is not enough | |
213 | if(n > avail){ | |
214 | // Skip the data in buffer | |
215 | pos += avail; | |
216 | sum += avail; | |
217 | // Read via stream | |
218 | while (sum < n && (cur = (int) in.skip(n - sum)) > 0) { | |
219 | sum += cur; | |
220 | } | |
221 | return sum; | |
222 | } | |
223 | pos += n; | |
224 | return n; | |
225 | } | |
226 | ||
227 | ||
228 | @Override | |
229 | public String readLine() throws IOException { | |
230 | // Unimplemented | |
231 | throw new UnsupportedOperationException(); | |
232 | } | |
233 | }⏎ |
0 | /* | |
1 | * Licensed to the Apache Software Foundation (ASF) under one or more | |
2 | * contributor license agreements. See the NOTICE file distributed with | |
3 | * this work for additional information regarding copyright ownership. | |
4 | * The ASF licenses this file to You under the Apache License, Version 2.0 | |
5 | * (the "License"); you may not use this file except in compliance with | |
6 | * the License. You may obtain a copy of the License at | |
7 | * | |
8 | * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
15 | * | |
16 | */ | |
17 | package org.apache.tomcat.util.bcel.classfile; | |
18 | ||
19 | import java.io.DataInputStream; | |
20 | import java.io.IOException; | |
21 | ||
22 | import org.apache.tomcat.util.bcel.Constants; | |
23 | import org.apache.tomcat.util.bcel.util.BCELComparator; | |
24 | ||
25 | /** | |
26 | * Class for fields and methods. | |
27 | * | |
28 | * @author <A HREF="mailto:m.dahm@gmx.de">M. Dahm</A> | |
29 | */ | |
30 | public class FieldOrMethod extends AccessFlags implements Cloneable { | |
31 | ||
32 | private static final long serialVersionUID = -3383525930205542157L; | |
33 | private static BCELComparator _cmp = new BCELComparator() { | |
34 | ||
35 | @Override | |
36 | public boolean equals( Object o1, Object o2 ) { | |
37 | FieldOrMethod THIS = (FieldOrMethod) o1; | |
38 | FieldOrMethod THAT = (FieldOrMethod) o2; | |
39 | return THIS.getName().equals(THAT.getName()) | |
40 | && THIS.getSignature().equals(THAT.getSignature()); | |
41 | } | |
42 | ||
43 | ||
44 | @Override | |
45 | public int hashCode( Object o ) { | |
46 | FieldOrMethod THIS = (FieldOrMethod) o; | |
47 | return THIS.getSignature().hashCode() ^ THIS.getName().hashCode(); | |
48 | } | |
49 | }; | |
50 | ||
51 | protected int name_index; // Points to field name in constant pool | |
52 | protected int signature_index; // Points to encoded signature | |
53 | protected int attributes_count; // No. of attributes | |
54 | protected Attribute[] attributes; // Collection of attributes | |
55 | ||
56 | protected ConstantPool constant_pool; | |
57 | ||
58 | FieldOrMethod() { | |
59 | } | |
60 | ||
61 | ||
62 | /** | |
63 | * Construct object from file stream. | |
64 | * @param file Input stream | |
65 | * @throws IOException | |
66 | * @throws ClassFormatException | |
67 | */ | |
68 | protected FieldOrMethod(DataInputStream file, ConstantPool constant_pool) throws IOException, | |
69 | ClassFormatException { | |
70 | this(file.readUnsignedShort(), file.readUnsignedShort(), file.readUnsignedShort(), null, | |
71 | constant_pool); | |
72 | attributes_count = file.readUnsignedShort(); | |
73 | attributes = new Attribute[attributes_count]; | |
74 | for (int i = 0; i < attributes_count; i++) { | |
75 | attributes[i] = Attribute.readAttribute(file, constant_pool); | |
76 | } | |
77 | } | |
78 | ||
79 | ||
80 | /** | |
81 | * @param access_flags Access rights of method | |
82 | * @param name_index Points to field name in constant pool | |
83 | * @param signature_index Points to encoded signature | |
84 | * @param attributes Collection of attributes | |
85 | * @param constant_pool Array of constants | |
86 | */ | |
87 | protected FieldOrMethod(int access_flags, int name_index, int signature_index, | |
88 | Attribute[] attributes, ConstantPool constant_pool) { | |
89 | this.access_flags = access_flags; | |
90 | this.name_index = name_index; | |
91 | this.signature_index = signature_index; | |
92 | this.constant_pool = constant_pool; | |
93 | setAttributes(attributes); | |
94 | } | |
95 | ||
96 | ||
97 | /** | |
98 | * @param attributes Collection of object attributes. | |
99 | */ | |
100 | public final void setAttributes( Attribute[] attributes ) { | |
101 | this.attributes = attributes; | |
102 | attributes_count = (attributes == null) ? 0 : attributes.length; | |
103 | } | |
104 | ||
105 | ||
106 | /** | |
107 | * @return Name of object, i.e., method name or field name | |
108 | */ | |
109 | public final String getName() { | |
110 | ConstantUtf8 c; | |
111 | c = (ConstantUtf8) constant_pool.getConstant(name_index, Constants.CONSTANT_Utf8); | |
112 | return c.getBytes(); | |
113 | } | |
114 | ||
115 | ||
116 | /** | |
117 | * @return String representation of object's type signature (java style) | |
118 | */ | |
119 | public final String getSignature() { | |
120 | ConstantUtf8 c; | |
121 | c = (ConstantUtf8) constant_pool.getConstant(signature_index, Constants.CONSTANT_Utf8); | |
122 | return c.getBytes(); | |
123 | } | |
124 | ||
125 | /** | |
126 | * Return value as defined by given BCELComparator strategy. | |
127 | * By default two FieldOrMethod objects are said to be equal when | |
128 | * their names and signatures are equal. | |
129 | * | |
130 | * @see java.lang.Object#equals(java.lang.Object) | |
131 | */ | |
132 | @Override | |
133 | public boolean equals( Object obj ) { | |
134 | return _cmp.equals(this, obj); | |
135 | } | |
136 | ||
137 | ||
138 | /** | |
139 | * Return value as defined by given BCELComparator strategy. | |
140 | * By default return the hashcode of the FieldOrMethod's name XOR signature. | |
141 | * | |
142 | * @see java.lang.Object#hashCode() | |
143 | */ | |
144 | @Override | |
145 | public int hashCode() { | |
146 | return _cmp.hashCode(this); | |
147 | } | |
148 | } |
0 | /* | |
1 | * Licensed to the Apache Software Foundation (ASF) under one or more | |
2 | * contributor license agreements. See the NOTICE file distributed with | |
3 | * this work for additional information regarding copyright ownership. | |
4 | * The ASF licenses this file to You under the Apache License, Version 2.0 | |
5 | * (the "License"); you may not use this file except in compliance with | |
6 | * the License. You may obtain a copy of the License at | |
7 | * | |
8 | * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
15 | * | |
16 | */ | |
17 | package org.apache.tomcat.util.bcel.classfile; | |
18 | ||
19 | import java.io.DataInputStream; | |
20 | import java.io.IOException; | |
21 | ||
22 | /** | |
23 | * This class is derived from <em>Attribute</em> and denotes that this class | |
24 | * is an Inner class of another. | |
25 | * to the source file of this class. | |
26 | * It is instantiated from the <em>Attribute.readAttribute()</em> method. | |
27 | * | |
28 | * @author <A HREF="mailto:m.dahm@gmx.de">M. Dahm</A> | |
29 | * @see Attribute | |
30 | */ | |
31 | public final class InnerClasses extends Attribute { | |
32 | ||
33 | private static final long serialVersionUID = 54179484605570305L; | |
34 | private int number_of_classes; | |
35 | ||
36 | ||
37 | /** | |
38 | * Construct object from file stream. | |
39 | * | |
40 | * @param name_index Index in constant pool to CONSTANT_Utf8 | |
41 | * @param length Content length in bytes | |
42 | * @param file Input stream | |
43 | * @param constant_pool Array of constants | |
44 | * @throws IOException | |
45 | */ | |
46 | InnerClasses(int name_index, int length, DataInputStream file, ConstantPool constant_pool) | |
47 | throws IOException { | |
48 | super(name_index, length, constant_pool); | |
49 | number_of_classes = file.readUnsignedShort(); | |
50 | for (int i = 0; i < number_of_classes; i++) { | |
51 | Utility.swallowInnerClass(file); | |
52 | } | |
53 | } | |
54 | } |
16 | 16 | */ |
17 | 17 | package org.apache.tomcat.util.bcel.classfile; |
18 | 18 | |
19 | import java.util.ArrayList; | |
20 | import java.util.List; | |
21 | ||
22 | import org.apache.tomcat.util.bcel.Constants; | |
23 | import org.apache.tomcat.util.bcel.util.BCELComparator; | |
24 | ||
25 | 19 | /** |
26 | 20 | * Represents a Java class, i.e., the data structures, constant pool, |
27 | 21 | * fields, methods and commands contained in a Java .class file. |
32 | 26 | |
33 | 27 | * @author <A HREF="mailto:m.dahm@gmx.de">M. Dahm</A> |
34 | 28 | */ |
35 | public class JavaClass extends AccessFlags | |
36 | implements Cloneable, Comparable<JavaClass> { | |
29 | public class JavaClass { | |
37 | 30 | |
38 | private static final long serialVersionUID = 7029227708237523236L; | |
39 | private String class_name; | |
40 | private String superclass_name; | |
41 | private String[] interface_names; | |
42 | private Attribute[] attributes; // attributes defined in the class | |
43 | private AnnotationEntry[] annotations; // annotations defined on the class | |
44 | ||
45 | ||
46 | // Annotations are collected from certain attributes, don't do it more than necessary! | |
47 | private boolean annotationsOutOfDate = true; | |
48 | ||
49 | private static BCELComparator _cmp = new BCELComparator() { | |
50 | ||
51 | @Override | |
52 | public boolean equals( Object o1, Object o2 ) { | |
53 | JavaClass THIS = (JavaClass) o1; | |
54 | JavaClass THAT = (JavaClass) o2; | |
55 | return THIS.getClassName().equals(THAT.getClassName()); | |
56 | } | |
57 | ||
58 | ||
59 | @Override | |
60 | public int hashCode( Object o ) { | |
61 | JavaClass THIS = (JavaClass) o; | |
62 | return THIS.getClassName().hashCode(); | |
63 | } | |
64 | }; | |
65 | ||
31 | private final int access_flags; | |
32 | private final String class_name; | |
33 | private final String superclass_name; | |
34 | private final String[] interface_names; | |
35 | private final Annotations runtimeVisibleAnnotations; // "RuntimeVisibleAnnotations" attribute defined in the class | |
66 | 36 | |
67 | 37 | /** |
68 | 38 | * Constructor gets all contents as arguments. |
69 | 39 | * |
70 | * @param class_name_index Index into constant pool referencing a | |
71 | * ConstantClass that represents this class. | |
72 | * @param superclass_name_index Index into constant pool referencing a | |
73 | * ConstantClass that represents this class's superclass. | |
40 | * @param class_name Name of this class. | |
41 | * @param superclass_name Name of this class's superclass. | |
74 | 42 | * @param access_flags Access rights defined by bit flags |
75 | 43 | * @param constant_pool Array of constants |
76 | 44 | * @param interfaces Implemented interfaces |
77 | * @param attributes Class attributes | |
45 | * @param runtimeVisibleAnnotations "RuntimeVisibleAnnotations" attribute defined on the Class, or null | |
78 | 46 | */ |
79 | public JavaClass(int class_name_index, int superclass_name_index, | |
80 | int access_flags, ConstantPool constant_pool, int[] interfaces, | |
81 | Attribute[] attributes) { | |
82 | if (interfaces == null) { | |
83 | interfaces = new int[0]; | |
84 | } | |
85 | if (attributes == null) { | |
86 | attributes = new Attribute[0]; | |
87 | } | |
47 | JavaClass(String class_name, String superclass_name, | |
48 | int access_flags, ConstantPool constant_pool, String[] interface_names, | |
49 | Annotations runtimeVisibleAnnotations) { | |
88 | 50 | this.access_flags = access_flags; |
89 | this.attributes = attributes; | |
90 | annotationsOutOfDate = true; | |
91 | ||
92 | /* According to the specification the following entries must be of type | |
93 | * `ConstantClass' but we check that anyway via the | |
94 | * `ConstPool.getConstant' method. | |
95 | */ | |
96 | class_name = constant_pool.getConstantString(class_name_index, Constants.CONSTANT_Class); | |
97 | class_name = Utility.compactClassName(class_name); | |
98 | if (superclass_name_index > 0) { | |
99 | // May be zero -> class is java.lang.Object | |
100 | superclass_name = constant_pool.getConstantString(superclass_name_index, | |
101 | Constants.CONSTANT_Class); | |
102 | superclass_name = Utility.compactClassName(superclass_name); | |
103 | } else { | |
104 | superclass_name = "java.lang.Object"; | |
105 | } | |
106 | interface_names = new String[interfaces.length]; | |
107 | for (int i = 0; i < interfaces.length; i++) { | |
108 | String str = constant_pool.getConstantString(interfaces[i], Constants.CONSTANT_Class); | |
109 | interface_names[i] = Utility.compactClassName(str); | |
110 | } | |
51 | this.runtimeVisibleAnnotations = runtimeVisibleAnnotations; | |
52 | this.class_name = class_name; | |
53 | this.superclass_name = superclass_name; | |
54 | this.interface_names = interface_names; | |
111 | 55 | } |
112 | 56 | |
57 | /** | |
58 | * @return Access flags of the object aka. "modifiers". | |
59 | */ | |
60 | public final int getAccessFlags() { | |
61 | return access_flags; | |
62 | } | |
113 | 63 | |
64 | /** | |
65 | * Return annotations entries from "RuntimeVisibleAnnotations" attribute on | |
66 | * the class, if there is any. | |
67 | * | |
68 | * @return An array of entries or {@code null} | |
69 | */ | |
114 | 70 | public AnnotationEntry[] getAnnotationEntries() { |
115 | if (annotationsOutOfDate) { | |
116 | // Find attributes that contain annotation data | |
117 | List<AnnotationEntry> accumulatedAnnotations = new ArrayList<>(); | |
118 | for (Attribute attribute : attributes) { | |
119 | if (attribute instanceof Annotations) { | |
120 | Annotations runtimeAnnotations = (Annotations)attribute; | |
121 | for(int j = 0; j < runtimeAnnotations.getAnnotationEntries().length; j++) | |
122 | accumulatedAnnotations.add(runtimeAnnotations.getAnnotationEntries()[j]); | |
123 | } | |
124 | } | |
125 | annotations = accumulatedAnnotations.toArray(new AnnotationEntry[accumulatedAnnotations.size()]); | |
126 | annotationsOutOfDate = false; | |
71 | if (runtimeVisibleAnnotations != null) { | |
72 | return runtimeVisibleAnnotations.getAnnotationEntries(); | |
127 | 73 | } |
128 | return annotations; | |
74 | return null; | |
129 | 75 | } |
130 | 76 | |
131 | 77 | /** |
154 | 100 | public String getSuperclassName() { |
155 | 101 | return superclass_name; |
156 | 102 | } |
157 | ||
158 | ||
159 | /** | |
160 | * Return value as defined by given BCELComparator strategy. | |
161 | * By default two JavaClass objects are said to be equal when | |
162 | * their class names are equal. | |
163 | * | |
164 | * @see java.lang.Object#equals(java.lang.Object) | |
165 | */ | |
166 | @Override | |
167 | public boolean equals( Object obj ) { | |
168 | return _cmp.equals(this, obj); | |
169 | } | |
170 | ||
171 | ||
172 | /** | |
173 | * Return the natural ordering of two JavaClasses. | |
174 | * This ordering is based on the class name | |
175 | */ | |
176 | @Override | |
177 | public int compareTo(JavaClass obj) { | |
178 | return getClassName().compareTo(obj.getClassName()); | |
179 | } | |
180 | ||
181 | ||
182 | /** | |
183 | * Return value as defined by given BCELComparator strategy. | |
184 | * By default return the hashcode of the class name. | |
185 | * | |
186 | * @see java.lang.Object#hashCode() | |
187 | */ | |
188 | @Override | |
189 | public int hashCode() { | |
190 | return _cmp.hashCode(this); | |
191 | } | |
192 | 103 | } |
0 | /* | |
1 | * Licensed to the Apache Software Foundation (ASF) under one or more | |
2 | * contributor license agreements. See the NOTICE file distributed with | |
3 | * this work for additional information regarding copyright ownership. | |
4 | * The ASF licenses this file to You under the Apache License, Version 2.0 | |
5 | * (the "License"); you may not use this file except in compliance with | |
6 | * the License. You may obtain a copy of the License at | |
7 | * | |
8 | * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
15 | * | |
16 | */ | |
17 | package org.apache.tomcat.util.bcel.classfile; | |
18 | ||
19 | import java.io.DataInputStream; | |
20 | import java.io.IOException; | |
21 | ||
22 | /** | |
23 | * This class represents a table of line numbers for debugging | |
24 | * purposes. This attribute is used by the <em>Code</em> attribute. It | |
25 | * contains pairs of PCs and line numbers. | |
26 | * | |
27 | * @author <A HREF="mailto:m.dahm@gmx.de">M. Dahm</A> | |
28 | * @see Code | |
29 | */ | |
30 | public final class LineNumberTable extends Attribute { | |
31 | ||
32 | private static final long serialVersionUID = 6585122636118666124L; | |
33 | ||
34 | ||
35 | /** | |
36 | * Construct object from file stream. | |
37 | * @param name_index Index of name | |
38 | * @param length Content length in bytes | |
39 | * @param file Input stream | |
40 | * @param constant_pool Array of constants | |
41 | * @throws IOException | |
42 | */ | |
43 | LineNumberTable(int name_index, int length, DataInputStream file, ConstantPool constant_pool) | |
44 | throws IOException { | |
45 | super(name_index, length, constant_pool); | |
46 | int line_number_table_length = (file.readUnsignedShort()); | |
47 | for (int i = 0; i < line_number_table_length; i++) { | |
48 | Utility.swallowLineNumber(file); | |
49 | } | |
50 | } | |
51 | } |
0 | /* | |
1 | * Licensed to the Apache Software Foundation (ASF) under one or more | |
2 | * contributor license agreements. See the NOTICE file distributed with | |
3 | * this work for additional information regarding copyright ownership. | |
4 | * The ASF licenses this file to You under the Apache License, Version 2.0 | |
5 | * (the "License"); you may not use this file except in compliance with | |
6 | * the License. You may obtain a copy of the License at | |
7 | * | |
8 | * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
15 | * | |
16 | */ | |
17 | package org.apache.tomcat.util.bcel.classfile; | |
18 | ||
19 | import java.io.DataInputStream; | |
20 | import java.io.IOException; | |
21 | ||
22 | /** | |
23 | * This class represents colection of local variables in a | |
24 | * method. This attribute is contained in the <em>Code</em> attribute. | |
25 | * | |
26 | * @author <A HREF="mailto:m.dahm@gmx.de">M. Dahm</A> | |
27 | * @see Code | |
28 | */ | |
29 | public class LocalVariableTable extends Attribute { | |
30 | ||
31 | private static final long serialVersionUID = -3904314258294133920L; | |
32 | ||
33 | ||
34 | /** | |
35 | * Construct object from file stream. | |
36 | * @param name_index Index in constant pool | |
37 | * @param length Content length in bytes | |
38 | * @param file Input stream | |
39 | * @param constant_pool Array of constants | |
40 | * @throws IOException | |
41 | */ | |
42 | LocalVariableTable(int name_index, int length, DataInputStream file, ConstantPool constant_pool) | |
43 | throws IOException { | |
44 | super(name_index, length, constant_pool); | |
45 | int local_variable_table_length = (file.readUnsignedShort()); | |
46 | for (int i = 0; i < local_variable_table_length; i++) { | |
47 | Utility.swallowLocalVariable(file); | |
48 | } | |
49 | } | |
50 | } |
0 | /** | |
1 | * Licensed to the Apache Software Foundation (ASF) under one or more | |
2 | * contributor license agreements. See the NOTICE file distributed with | |
3 | * this work for additional information regarding copyright ownership. | |
4 | * The ASF licenses this file to You under the Apache License, Version 2.0 | |
5 | * (the "License"); you may not use this file except in compliance with | |
6 | * the License. You may obtain a copy of the License at | |
7 | * | |
8 | * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
15 | */ | |
16 | package org.apache.tomcat.util.bcel.classfile; | |
17 | ||
18 | import java.io.DataInputStream; | |
19 | import java.io.IOException; | |
20 | ||
21 | // The new table is used when generic types are about... | |
22 | ||
23 | //LocalVariableTable_attribute { | |
24 | // u2 attribute_name_index; | |
25 | // u4 attribute_length; | |
26 | // u2 local_variable_table_length; | |
27 | // { u2 start_pc; | |
28 | // u2 length; | |
29 | // u2 name_index; | |
30 | // u2 descriptor_index; | |
31 | // u2 index; | |
32 | // } local_variable_table[local_variable_table_length]; | |
33 | // } | |
34 | ||
35 | //LocalVariableTypeTable_attribute { | |
36 | // u2 attribute_name_index; | |
37 | // u4 attribute_length; | |
38 | // u2 local_variable_type_table_length; | |
39 | // { | |
40 | // u2 start_pc; | |
41 | // u2 length; | |
42 | // u2 name_index; | |
43 | // u2 signature_index; | |
44 | // u2 index; | |
45 | // } local_variable_type_table[local_variable_type_table_length]; | |
46 | // } | |
47 | // J5TODO: Needs some testing ! | |
48 | public class LocalVariableTypeTable extends Attribute { | |
49 | private static final long serialVersionUID = -5466082154076451597L; | |
50 | ||
51 | LocalVariableTypeTable(int name_index, int length, | |
52 | DataInputStream dis, ConstantPool constant_pool) | |
53 | throws IOException { | |
54 | super(name_index, length, constant_pool); | |
55 | ||
56 | int local_variable_type_table_length = (dis.readUnsignedShort()); | |
57 | ||
58 | for(int i=0; i < local_variable_type_table_length; i++) { | |
59 | Utility.swallowLocalVariable(dis); | |
60 | } | |
61 | } | |
62 | } |
0 | /* | |
1 | * Licensed to the Apache Software Foundation (ASF) under one or more | |
2 | * contributor license agreements. See the NOTICE file distributed with | |
3 | * this work for additional information regarding copyright ownership. | |
4 | * The ASF licenses this file to You under the Apache License, Version 2.0 | |
5 | * (the "License"); you may not use this file except in compliance with | |
6 | * the License. You may obtain a copy of the License at | |
7 | * | |
8 | * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
15 | * | |
16 | */ | |
17 | package org.apache.tomcat.util.bcel.classfile; | |
18 | ||
19 | import java.io.DataInput; | |
20 | import java.io.IOException; | |
21 | ||
22 | /** | |
23 | * This class is derived from <em>Attribute</em> and represents a reference | |
24 | * to a PMG attribute. | |
25 | * | |
26 | * @author <A HREF="mailto:m.dahm@gmx.de">M. Dahm</A> | |
27 | * @see Attribute | |
28 | */ | |
29 | public final class PMGClass extends Attribute { | |
30 | ||
31 | private static final long serialVersionUID = -1876065562391587509L; | |
32 | ||
33 | ||
34 | /** | |
35 | * Construct object from file stream. | |
36 | * @param name_index Index in constant pool to CONSTANT_Utf8 | |
37 | * @param length Content length in bytes | |
38 | * @param file Input stream | |
39 | * @param constant_pool Array of constants | |
40 | * @throws IOException | |
41 | */ | |
42 | PMGClass(int name_index, int length, DataInput file, ConstantPool constant_pool) | |
43 | throws IOException { | |
44 | super(name_index, length, constant_pool); | |
45 | file.readUnsignedShort(); // Unused pmg_index | |
46 | file.readUnsignedShort(); // Unused pmg_class_index | |
47 | } | |
48 | } |
0 | /* | |
1 | * Licensed to the Apache Software Foundation (ASF) under one or more | |
2 | * contributor license agreements. See the NOTICE file distributed with | |
3 | * this work for additional information regarding copyright ownership. | |
4 | * The ASF licenses this file to You under the Apache License, Version 2.0 | |
5 | * (the "License"); you may not use this file except in compliance with | |
6 | * the License. You may obtain a copy of the License at | |
7 | * | |
8 | * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
15 | * | |
16 | */ | |
17 | package org.apache.tomcat.util.bcel.classfile; | |
18 | ||
19 | import java.io.DataInputStream; | |
20 | import java.io.IOException; | |
21 | ||
22 | import org.apache.tomcat.util.bcel.Constants; | |
23 | ||
24 | /** | |
25 | * represents one parameter annotation in the parameter annotation table | |
26 | * | |
27 | * @author <A HREF="mailto:dbrosius@qis.net">D. Brosius</A> | |
28 | * @since 6.0 | |
29 | */ | |
30 | public class ParameterAnnotationEntry implements Constants { | |
31 | ||
32 | private int annotation_table_length; | |
33 | private AnnotationEntry[] annotation_table; | |
34 | ||
35 | ||
36 | /** | |
37 | * Construct object from file stream. | |
38 | * @param file Input stream | |
39 | * @throws IOException | |
40 | */ | |
41 | ParameterAnnotationEntry(DataInputStream file, ConstantPool constant_pool) throws IOException { | |
42 | annotation_table_length = (file.readUnsignedShort()); | |
43 | annotation_table = new AnnotationEntry[annotation_table_length]; | |
44 | for (int i = 0; i < annotation_table_length; i++) { | |
45 | annotation_table[i] = AnnotationEntry.read(file, constant_pool); | |
46 | } | |
47 | } | |
48 | ||
49 | } |
0 | /* | |
1 | * Licensed to the Apache Software Foundation (ASF) under one or more | |
2 | * contributor license agreements. See the NOTICE file distributed with | |
3 | * this work for additional information regarding copyright ownership. | |
4 | * The ASF licenses this file to You under the Apache License, Version 2.0 | |
5 | * (the "License"); you may not use this file except in compliance with | |
6 | * the License. You may obtain a copy of the License at | |
7 | * | |
8 | * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
15 | * | |
16 | */ | |
17 | package org.apache.tomcat.util.bcel.classfile; | |
18 | ||
19 | import java.io.DataInputStream; | |
20 | import java.io.IOException; | |
21 | ||
22 | /** | |
23 | * base class for parameter annotations | |
24 | * | |
25 | * @author <A HREF="mailto:dbrosius@qis.net">D. Brosius</A> | |
26 | * @since 6.0 | |
27 | */ | |
28 | public abstract class ParameterAnnotations extends Attribute { | |
29 | ||
30 | private static final long serialVersionUID = -8831779739803248091L; | |
31 | private int num_parameters; | |
32 | private ParameterAnnotationEntry[] parameter_annotation_table; // Table of parameter annotations | |
33 | ||
34 | ||
35 | /** | |
36 | * @param name_index Index pointing to the name <em>Code</em> | |
37 | * @param length Content length in bytes | |
38 | * @param file Input stream | |
39 | * @param constant_pool Array of constants | |
40 | */ | |
41 | ParameterAnnotations(int name_index, int length, | |
42 | DataInputStream file, ConstantPool constant_pool) throws IOException { | |
43 | this(name_index, length, (ParameterAnnotationEntry[]) null, | |
44 | constant_pool); | |
45 | num_parameters = (file.readUnsignedByte()); | |
46 | parameter_annotation_table = new ParameterAnnotationEntry[num_parameters]; | |
47 | for (int i = 0; i < num_parameters; i++) { | |
48 | parameter_annotation_table[i] = new ParameterAnnotationEntry(file, constant_pool); | |
49 | } | |
50 | } | |
51 | ||
52 | ||
53 | /** | |
54 | * @param name_index Index pointing to the name <em>Code</em> | |
55 | * @param length Content length in bytes | |
56 | * @param parameter_annotation_table the actual parameter annotations | |
57 | * @param constant_pool Array of constants | |
58 | */ | |
59 | public ParameterAnnotations(int name_index, int length, | |
60 | ParameterAnnotationEntry[] parameter_annotation_table, ConstantPool constant_pool) { | |
61 | super(name_index, length, constant_pool); | |
62 | setParameterAnnotationTable(parameter_annotation_table); | |
63 | } | |
64 | ||
65 | ||
66 | /** | |
67 | * @param parameter_annotation_table the entries to set in this parameter annotation | |
68 | */ | |
69 | public final void setParameterAnnotationTable( | |
70 | ParameterAnnotationEntry[] parameter_annotation_table ) { | |
71 | this.parameter_annotation_table = parameter_annotation_table; | |
72 | num_parameters = (parameter_annotation_table == null) | |
73 | ? 0 | |
74 | : parameter_annotation_table.length; | |
75 | } | |
76 | } |
0 | /* | |
1 | * Licensed to the Apache Software Foundation (ASF) under one or more | |
2 | * contributor license agreements. See the NOTICE file distributed with | |
3 | * this work for additional information regarding copyright ownership. | |
4 | * The ASF licenses this file to You under the Apache License, Version 2.0 | |
5 | * (the "License"); you may not use this file except in compliance with | |
6 | * the License. You may obtain a copy of the License at | |
7 | * | |
8 | * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
15 | * | |
16 | */ | |
17 | package org.apache.tomcat.util.bcel.classfile; | |
18 | ||
19 | import java.io.DataInputStream; | |
20 | import java.io.IOException; | |
21 | ||
22 | /** | |
23 | * represents an annotation that is represented in the class file but is not | |
24 | * provided to the JVM. | |
25 | * | |
26 | * @author <A HREF="mailto:dbrosius@qis.net">D. Brosius</A> | |
27 | * @since 6.0 | |
28 | */ | |
29 | public class RuntimeInvisibleAnnotations extends Annotations | |
30 | { | |
31 | private static final long serialVersionUID = -7962627688723310248L; | |
32 | ||
33 | /** | |
34 | * @param name_index | |
35 | * Index pointing to the name <em>Code</em> | |
36 | * @param length | |
37 | * Content length in bytes | |
38 | * @param file | |
39 | * Input stream | |
40 | * @param constant_pool | |
41 | * Array of constants | |
42 | */ | |
43 | RuntimeInvisibleAnnotations(int name_index, int length, | |
44 | DataInputStream file, ConstantPool constant_pool) | |
45 | throws IOException | |
46 | { | |
47 | super(name_index, length, file, constant_pool); | |
48 | } | |
49 | } |
+0
-45
0 | /* | |
1 | * Licensed to the Apache Software Foundation (ASF) under one or more | |
2 | * contributor license agreements. See the NOTICE file distributed with | |
3 | * this work for additional information regarding copyright ownership. | |
4 | * The ASF licenses this file to You under the Apache License, Version 2.0 | |
5 | * (the "License"); you may not use this file except in compliance with | |
6 | * the License. You may obtain a copy of the License at | |
7 | * | |
8 | * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
15 | * | |
16 | */ | |
17 | package org.apache.tomcat.util.bcel.classfile; | |
18 | ||
19 | import java.io.DataInputStream; | |
20 | import java.io.IOException; | |
21 | ||
22 | /** | |
23 | * represents a parameter annotation that is represented in the class file | |
24 | * but is not provided to the JVM. | |
25 | * | |
26 | * @author <A HREF="mailto:dbrosius@qis.net">D. Brosius</A> | |
27 | * @since 6.0 | |
28 | */ | |
29 | public class RuntimeInvisibleParameterAnnotations extends ParameterAnnotations { | |
30 | ||
31 | private static final long serialVersionUID = -6819370369102352536L; | |
32 | ||
33 | ||
34 | /** | |
35 | * @param name_index Index pointing to the name <em>Code</em> | |
36 | * @param length Content length in bytes | |
37 | * @param file Input stream | |
38 | * @param constant_pool Array of constants | |
39 | */ | |
40 | RuntimeInvisibleParameterAnnotations(int name_index, int length, DataInputStream file, | |
41 | ConstantPool constant_pool) throws IOException { | |
42 | super(name_index, length, file, constant_pool); | |
43 | } | |
44 | } |
0 | /* | |
1 | * Licensed to the Apache Software Foundation (ASF) under one or more | |
2 | * contributor license agreements. See the NOTICE file distributed with | |
3 | * this work for additional information regarding copyright ownership. | |
4 | * The ASF licenses this file to You under the Apache License, Version 2.0 | |
5 | * (the "License"); you may not use this file except in compliance with | |
6 | * the License. You may obtain a copy of the License at | |
7 | * | |
8 | * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
15 | * | |
16 | */ | |
17 | package org.apache.tomcat.util.bcel.classfile; | |
18 | ||
19 | import java.io.DataInputStream; | |
20 | import java.io.IOException; | |
21 | ||
22 | /** | |
23 | * represents an annotation that is represented in the class file and is | |
24 | * provided to the JVM. | |
25 | * | |
26 | * @author <A HREF="mailto:dbrosius@qis.net">D. Brosius</A> | |
27 | * @since 6.0 | |
28 | */ | |
29 | public class RuntimeVisibleAnnotations extends Annotations | |
30 | { | |
31 | private static final long serialVersionUID = 2912284875689024413L; | |
32 | ||
33 | /** | |
34 | * @param name_index | |
35 | * Index pointing to the name <em>Code</em> | |
36 | * @param length | |
37 | * Content length in bytes | |
38 | * @param file | |
39 | * Input stream | |
40 | * @param constant_pool | |
41 | * Array of constants | |
42 | */ | |
43 | public RuntimeVisibleAnnotations(int name_index, int length, | |
44 | DataInputStream file, ConstantPool constant_pool) | |
45 | throws IOException | |
46 | { | |
47 | super(name_index, length, file, constant_pool); | |
48 | } | |
49 | } |
0 | /* | |
1 | * Licensed to the Apache Software Foundation (ASF) under one or more | |
2 | * contributor license agreements. See the NOTICE file distributed with | |
3 | * this work for additional information regarding copyright ownership. | |
4 | * The ASF licenses this file to You under the Apache License, Version 2.0 | |
5 | * (the "License"); you may not use this file except in compliance with | |
6 | * the License. You may obtain a copy of the License at | |
7 | * | |
8 | * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
15 | * | |
16 | */ | |
17 | package org.apache.tomcat.util.bcel.classfile; | |
18 | ||
19 | import java.io.DataInputStream; | |
20 | import java.io.IOException; | |
21 | ||
22 | /** | |
23 | * represents a parameter annotation that is represented in the class file | |
24 | * and is provided to the JVM. | |
25 | * | |
26 | * @author <A HREF="mailto:dbrosius@qis.net">D. Brosius</A> | |
27 | * @since 6.0 | |
28 | */ | |
29 | public class RuntimeVisibleParameterAnnotations extends ParameterAnnotations { | |
30 | ||
31 | private static final long serialVersionUID = 7633756460868573992L; | |
32 | ||
33 | ||
34 | /** | |
35 | * @param name_index Index pointing to the name <em>Code</em> | |
36 | * @param length Content length in bytes | |
37 | * @param file Input stream | |
38 | * @param constant_pool Array of constants | |
39 | */ | |
40 | RuntimeVisibleParameterAnnotations(int name_index, int length, DataInputStream file, | |
41 | ConstantPool constant_pool) throws IOException { | |
42 | super(name_index, length, file, constant_pool); | |
43 | } | |
44 | } |
20 | 20 | |
21 | 21 | public class SimpleElementValue extends ElementValue |
22 | 22 | { |
23 | private int index; | |
23 | private final int index; | |
24 | 24 | |
25 | public SimpleElementValue(int type, int index, ConstantPool cpool) | |
26 | { | |
25 | SimpleElementValue(int type, int index, ConstantPool cpool) { | |
27 | 26 | super(type, cpool); |
28 | 27 | this.index = index; |
29 | 28 | } |
0 | /* | |
1 | * Licensed to the Apache Software Foundation (ASF) under one or more | |
2 | * contributor license agreements. See the NOTICE file distributed with | |
3 | * this work for additional information regarding copyright ownership. | |
4 | * The ASF licenses this file to You under the Apache License, Version 2.0 | |
5 | * (the "License"); you may not use this file except in compliance with | |
6 | * the License. You may obtain a copy of the License at | |
7 | * | |
8 | * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
15 | * | |
16 | */ | |
17 | package org.apache.tomcat.util.bcel.classfile; | |
18 | ||
19 | import java.io.DataInput; | |
20 | import java.io.IOException; | |
21 | ||
22 | /** | |
23 | * This class is derived from <em>Attribute</em> and represents a reference | |
24 | * to the source file of this class. At most one SourceFile attribute | |
25 | * should appear per classfile. The intention of this class is that it is | |
26 | * instantiated from the <em>Attribute.readAttribute()</em> method. | |
27 | * | |
28 | * @author <A HREF="mailto:m.dahm@gmx.de">M. Dahm</A> | |
29 | * @see Attribute | |
30 | */ | |
31 | public final class SourceFile extends Attribute { | |
32 | ||
33 | private static final long serialVersionUID = 332346699609443704L; | |
34 | ||
35 | ||
36 | /** | |
37 | * Construct object from file stream. | |
38 | * @param name_index Index in constant pool to CONSTANT_Utf8 | |
39 | * @param length Content length in bytes | |
40 | * @param file Input stream | |
41 | * @param constant_pool Array of constants | |
42 | * @throws IOException | |
43 | */ | |
44 | SourceFile(int name_index, int length, DataInput file, ConstantPool constant_pool) | |
45 | throws IOException { | |
46 | super(name_index, length, constant_pool); | |
47 | file.readUnsignedShort(); // Unused sourcefile_index | |
48 | } | |
49 | } |
17 | 17 | package org.apache.tomcat.util.bcel.classfile; |
18 | 18 | |
19 | 19 | import java.io.DataInput; |
20 | import java.io.DataInputStream; | |
20 | import java.io.EOFException; | |
21 | 21 | import java.io.IOException; |
22 | 22 | |
23 | 23 | import org.apache.tomcat.util.bcel.Constants; |
46 | 46 | return str.replace('/', '.'); // Is `/' on all systems, even DOS |
47 | 47 | } |
48 | 48 | |
49 | static void swallowCodeException(DataInput file) throws IOException { | |
50 | file.readUnsignedShort(); // Unused start_pc | |
51 | file.readUnsignedShort(); // Unused end_pc | |
52 | file.readUnsignedShort(); // Unused handler_pc | |
53 | file.readUnsignedShort(); // Unused catch_type | |
49 | static String getClassName(ConstantPool constant_pool, int index) { | |
50 | Constant c = constant_pool.getConstant(index, Constants.CONSTANT_Class); | |
51 | int i = ((ConstantClass) c).getNameIndex(); | |
52 | ||
53 | // Finally get the string from the constant pool | |
54 | c = constant_pool.getConstant(i, Constants.CONSTANT_Utf8); | |
55 | String name = ((ConstantUtf8) c).getBytes(); | |
56 | ||
57 | return compactClassName(name); | |
54 | 58 | } |
55 | 59 | |
56 | static void swallowInnerClass(DataInput file) throws IOException { | |
57 | file.readUnsignedShort(); // Unused inner_class_index | |
58 | file.readUnsignedShort(); // Unused outer_class_index | |
59 | file.readUnsignedShort(); // Unused inner_name_index | |
60 | file.readUnsignedShort(); // Unused inner_access_flags | |
61 | } | |
62 | ||
63 | static void swallowLineNumber(DataInput file) throws IOException { | |
64 | file.readUnsignedShort(); // Unused start_pc | |
65 | file.readUnsignedShort(); // Unused line_number | |
66 | } | |
67 | ||
68 | static void swallowLocalVariable(DataInput file) throws IOException { | |
69 | file.readUnsignedShort(); // Unused start_pc | |
70 | file.readUnsignedShort(); // Unused length | |
71 | file.readUnsignedShort(); // Unused name_index | |
72 | file.readUnsignedShort(); // Unused signature_index | |
73 | file.readUnsignedShort(); // Unused index | |
74 | } | |
75 | ||
76 | static void swallowStackMap(DataInput file) throws IOException { | |
77 | int map_length = file.readUnsignedShort(); | |
78 | for (int i = 0; i < map_length; i++) { | |
79 | Utility.swallowStackMapEntry(file); | |
60 | static void skipFully(DataInput file, int length) throws IOException { | |
61 | int total = file.skipBytes(length); | |
62 | if (total != length) { | |
63 | throw new EOFException(); | |
80 | 64 | } |
81 | 65 | } |
82 | 66 | |
83 | static void swallowStackMapTable(DataInputStream file) throws IOException { | |
84 | int map_length = file.readUnsignedShort(); | |
85 | for (int i = 0; i < map_length; i++) { | |
86 | Utility.swallowStackMapTableEntry(file); | |
67 | static void swallowFieldOrMethod(DataInput file) | |
68 | throws IOException { | |
69 | // file.readUnsignedShort(); // Unused access flags | |
70 | // file.readUnsignedShort(); // name index | |
71 | // file.readUnsignedShort(); // signature index | |
72 | skipFully(file, 6); | |
73 | ||
74 | int attributes_count = file.readUnsignedShort(); | |
75 | for (int i = 0; i < attributes_count; i++) { | |
76 | swallowAttribute(file); | |
87 | 77 | } |
88 | 78 | } |
89 | 79 | |
90 | static void swallowStackMapType(DataInput file) throws IOException { | |
91 | byte type = file.readByte(); | |
92 | if ((type < Constants.ITEM_Bogus) || (type > Constants.ITEM_NewObject)) { | |
93 | throw new ClassFormatException("Illegal type for StackMapType: " + type); | |
94 | } | |
95 | // Check to see if type has an index | |
96 | if ((type == Constants.ITEM_Object) || (type == Constants.ITEM_NewObject)) { | |
97 | file.readShort(); // Unused index | |
98 | } | |
80 | static void swallowAttribute(DataInput file) | |
81 | throws IOException { | |
82 | //file.readUnsignedShort(); // Unused name index | |
83 | skipFully(file, 2); | |
84 | // Length of data in bytes | |
85 | int length = file.readInt(); | |
86 | skipFully(file, length); | |
99 | 87 | } |
100 | 88 | |
101 | static void swallowStackMapEntry(DataInput file) throws IOException { | |
102 | file.readShort(); // Unused byte_code_offset | |
103 | int number_of_locals = file.readShort(); | |
104 | for (int i = 0; i < number_of_locals; i++) { | |
105 | Utility.swallowStackMapType(file); | |
106 | } | |
107 | int number_of_stack_items = file.readShort(); | |
108 | for (int i = 0; i < number_of_stack_items; i++) { | |
109 | Utility.swallowStackMapType(file); | |
110 | } | |
111 | } | |
112 | ||
113 | static void swallowStackMapTableEntry(DataInputStream file) throws IOException { | |
114 | int frame_type = file.read(); | |
115 | ||
116 | if (frame_type >= Constants.SAME_FRAME && frame_type <= Constants.SAME_FRAME_MAX) { | |
117 | // NO-OP | |
118 | } else if (frame_type >= Constants.SAME_LOCALS_1_STACK_ITEM_FRAME && | |
119 | frame_type <= Constants.SAME_LOCALS_1_STACK_ITEM_FRAME_MAX) { | |
120 | Utility.swallowStackMapType(file); // Unused single stack item | |
121 | } else if (frame_type == Constants.SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED) { | |
122 | file.readShort(); // Unused byte_code_offset_delta | |
123 | Utility.swallowStackMapType(file); // Unused single stack item | |
124 | } else if (frame_type >= Constants.CHOP_FRAME && | |
125 | frame_type <= Constants.CHOP_FRAME_MAX) { | |
126 | file.readShort(); // Unused byte_code_offset_delta | |
127 | } else if (frame_type == Constants.SAME_FRAME_EXTENDED) { | |
128 | file.readShort(); // Unused byte_code_offset_delta | |
129 | } else if (frame_type >= Constants.APPEND_FRAME && | |
130 | frame_type <= Constants.APPEND_FRAME_MAX) { | |
131 | file.readShort(); // Unused byte_code_offset_delta | |
132 | int number_of_locals = frame_type - 251; | |
133 | for (int i = 0; i < number_of_locals; i++) { | |
134 | Utility.swallowStackMapType(file); | |
135 | } | |
136 | } else if (frame_type == Constants.FULL_FRAME) { | |
137 | file.readShort(); // Unused byte_code_offset_delta | |
138 | int number_of_locals = file.readShort(); | |
139 | for (int i = 0; i < number_of_locals; i++) { | |
140 | Utility.swallowStackMapType(file); | |
141 | } | |
142 | int number_of_stack_items = file.readShort(); | |
143 | for (int i = 0; i < number_of_stack_items; i++) { | |
144 | Utility.swallowStackMapType(file); | |
145 | } | |
146 | } else { | |
147 | /* Can't happen */ | |
148 | throw new ClassFormatException ( | |
149 | "Invalid frame type found while parsing stack map table: " + frame_type); | |
150 | } | |
151 | } | |
152 | ||
153 | static void swallowUnknownAttribute(DataInput file, int length) throws IOException { | |
154 | if (length > 0) { | |
155 | byte[] bytes = new byte[length]; | |
156 | file.readFully(bytes); | |
157 | } | |
158 | } | |
159 | ||
160 | static void swallowSignature(DataInput file) throws IOException { | |
161 | file.readUnsignedShort(); // Unused signature_index | |
162 | } | |
163 | ||
164 | static void swallowSynthetic(DataInput file, int length) throws IOException { | |
165 | if (length > 0) { | |
166 | byte[] bytes = new byte[length]; | |
167 | file.readFully(bytes); | |
168 | throw new ClassFormatException("Synthetic attribute with length > 0"); | |
169 | } | |
170 | } | |
171 | ||
172 | static void swallowBootstrapMethods(DataInput file) throws IOException { | |
173 | int num_bootstrap_methods = file.readUnsignedShort(); | |
174 | for (int i = 0; i < num_bootstrap_methods; i++) { | |
175 | file.readUnsignedShort(); // Unused bootstrap_method_ref | |
176 | int num_bootstrap_args = file.readUnsignedShort(); | |
177 | for (int j = 0; j < num_bootstrap_args; j++) { | |
178 | file.readUnsignedShort(); // Unused bootstrap method argument | |
179 | } | |
180 | } | |
181 | } | |
182 | ||
183 | static void swallowMethodParameters(DataInput file) throws IOException { | |
184 | int parameters_count = file.readUnsignedByte(); | |
185 | for (int i = 0; i < parameters_count; i++) { | |
186 | file.readUnsignedShort(); // Unused name_index | |
187 | file.readUnsignedShort(); // Unused access_flags | |
188 | } | |
189 | } | |
190 | 89 | } |
0 | /* | |
1 | * Licensed to the Apache Software Foundation (ASF) under one or more | |
2 | * contributor license agreements. See the NOTICE file distributed with | |
3 | * this work for additional information regarding copyright ownership. | |
4 | * The ASF licenses this file to You under the Apache License, Version 2.0 | |
5 | * (the "License"); you may not use this file except in compliance with | |
6 | * the License. You may obtain a copy of the License at | |
7 | * | |
8 | * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
15 | * | |
16 | */ | |
17 | package org.apache.tomcat.util.bcel.util; | |
18 | ||
19 | /** | |
20 | * Used for BCEL comparison strategy | |
21 | * | |
22 | * @author <A HREF="mailto:m.dahm@gmx.de">M. Dahm</A> | |
23 | * @since 5.2 | |
24 | */ | |
25 | public interface BCELComparator { | |
26 | ||
27 | /** | |
28 | * Compare two objects and return what THIS.equals(THAT) should return | |
29 | * | |
30 | * @param THIS | |
31 | * @param THAT | |
32 | * @return true if and only if THIS equals THAT | |
33 | */ | |
34 | boolean equals( Object THIS, Object THAT ); | |
35 | ||
36 | ||
37 | /** | |
38 | * Return hashcode for THIS.hashCode() | |
39 | * | |
40 | * @param THIS | |
41 | * @return hashcode for THIS.hashCode() | |
42 | */ | |
43 | int hashCode( Object THIS ); | |
44 | } |
0 | <!-- | |
1 | Licensed to the Apache Software Foundation (ASF) under one or more | |
2 | contributor license agreements. See the NOTICE file distributed with | |
3 | this work for additional information regarding copyright ownership. | |
4 | The ASF licenses this file to You under the Apache License, Version 2.0 | |
5 | (the "License"); you may not use this file except in compliance with | |
6 | the License. You may obtain a copy of the License at | |
7 | ||
8 | http://www.apache.org/licenses/LICENSE-2.0 | |
9 | ||
10 | Unless required by applicable law or agreed to in writing, software | |
11 | distributed under the License is distributed on an "AS IS" BASIS, | |
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | See the License for the specific language governing permissions and | |
14 | limitations under the License. | |
15 | --> | |
16 | <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> | |
17 | <html> | |
18 | <head> | |
19 | </head> | |
20 | <body bgcolor="white"> | |
21 | <p> | |
22 | This package contains utility classes for the | |
23 | <a href="http://commons.apache.org/bcel/">Byte Code Engineering | |
24 | Library</a>, namely: | |
25 | </p> | |
26 | <p> | |
27 | <ul> | |
28 | <li>Collection classes for JavaClass objects</li> | |
29 | <li>A converter for class files to HTML</li> | |
30 | <li>A tool to find instructions patterns via regular expressions</li> | |
31 | <li>A class to find classes as defined in the CLASSPATH</li> | |
32 | <li>A class loader that allows to create classes at run time</li> | |
33 | </ul> | |
34 | ||
35 | </p> | |
36 | </body> | |
37 | </html> |
22 | 22 | * |
23 | 23 | * @author Craig R. McClanahan |
24 | 24 | */ |
25 | ||
26 | 25 | public final class HexUtils { |
27 | 26 | |
28 | 27 | |
29 | 28 | // -------------------------------------------------------------- Constants |
30 | ||
31 | 29 | |
32 | 30 | /** |
33 | 31 | * Table for HEX to DEC byte translation. |
54 | 52 | */ |
55 | 53 | private static final char[] hex = "0123456789abcdef".toCharArray(); |
56 | 54 | |
55 | ||
57 | 56 | // --------------------------------------------------------- Static Methods |
58 | 57 | |
59 | ||
60 | public static int getDec(int index){ | |
58 | public static int getDec(int index) { | |
61 | 59 | // Fast for correct values, slower for incorrect ones |
62 | 60 | try { |
63 | 61 | return DEC[index - '0']; |
66 | 64 | } |
67 | 65 | } |
68 | 66 | |
69 | public static byte getHex(int index){ | |
67 | ||
68 | public static byte getHex(int index) { | |
70 | 69 | return HEX[index]; |
71 | 70 | } |
72 | 71 | |
73 | public static String toHexString(byte[] bytes) | |
74 | { | |
75 | if(null == bytes) { | |
72 | ||
73 | public static String toHexString(byte[] bytes) { | |
74 | if (null == bytes) { | |
76 | 75 | return null; |
77 | 76 | } |
78 | 77 | |
79 | 78 | StringBuilder sb = new StringBuilder(bytes.length << 1); |
80 | 79 | |
81 | for(int i=0; i<bytes.length; ++i) { | |
80 | for(int i = 0; i < bytes.length; ++i) { | |
82 | 81 | sb.append(hex[(bytes[i] & 0xf0) >> 4]) |
83 | 82 | .append(hex[(bytes[i] & 0x0f)]) |
84 | 83 | ; |
86 | 85 | |
87 | 86 | return sb.toString(); |
88 | 87 | } |
88 | ||
89 | ||
90 | public static byte[] fromHexString(String input) { | |
91 | if (input == null) { | |
92 | return null; | |
93 | } | |
94 | ||
95 | char[] inputChars = input.toCharArray(); | |
96 | byte[] result = new byte[input.length() >> 1]; | |
97 | for (int i = 0; i < result.length; i++) { | |
98 | result[i] = (byte) ((getDec(inputChars[2*i]) << 4) + getDec(inputChars[2*i + 1])); | |
99 | } | |
100 | return result; | |
101 | } | |
89 | 102 | } |
110 | 110 | @Override |
111 | 111 | public void scan(File file, String webappPath, boolean isWebapp) throws IOException { |
112 | 112 | |
113 | InputStream stream = null; | |
114 | 113 | WebXml fragment = new WebXml(); |
115 | 114 | fragment.setWebappJar(isWebapp); |
116 | 115 | fragment.setDelegate(delegate); |
117 | 116 | |
117 | File fragmentFile = new File(file, FRAGMENT_LOCATION); | |
118 | 118 | try { |
119 | File fragmentFile = new File(file, FRAGMENT_LOCATION); | |
120 | 119 | if (fragmentFile.isFile()) { |
121 | stream = new FileInputStream(fragmentFile); | |
122 | InputSource source = | |
123 | new InputSource(fragmentFile.toURI().toURL().toString()); | |
124 | source.setByteStream(stream); | |
125 | if (!webXmlParser.parseWebXml(source, fragment, true)) { | |
126 | ok = false; | |
120 | try (InputStream stream = new FileInputStream(fragmentFile)) { | |
121 | InputSource source = | |
122 | new InputSource(fragmentFile.toURI().toURL().toString()); | |
123 | source.setByteStream(stream); | |
124 | if (!webXmlParser.parseWebXml(source, fragment, true)) { | |
125 | ok = false; | |
126 | } | |
127 | 127 | } |
128 | 128 | } else { |
129 | 129 | // If there is no web.xml, normal folder no impact on |
2081 | 2081 | |
2082 | 2082 | if (dest.getMaxFileSize() == null) { |
2083 | 2083 | dest.setMaxFileSize(src.getMaxFileSize()); |
2084 | } else if (src.getLocation() != null) { | |
2084 | } else if (src.getMaxFileSize() != null) { | |
2085 | 2085 | if (failOnConflict && |
2086 | 2086 | !src.getMaxFileSize().equals(dest.getMaxFileSize())) { |
2087 | 2087 | return false; |
22 | 22 | import org.apache.juli.logging.LogFactory; |
23 | 23 | import org.apache.tomcat.util.buf.ByteChunk; |
24 | 24 | import org.apache.tomcat.util.buf.MessageBytes; |
25 | import org.apache.tomcat.util.http.parser.Cookie; | |
25 | 26 | import org.apache.tomcat.util.log.UserDataHelper; |
26 | 27 | import org.apache.tomcat.util.res.StringManager; |
27 | 28 | |
28 | 29 | /** |
29 | 30 | * A collection of cookies - reusable and tuned for server side performance. |
30 | * Based on RFC2965 (and 2109). | |
31 | * Based on RFC6265 and RFC2109. | |
31 | 32 | * |
32 | 33 | * This class is not thread-safe. |
33 | 34 | * |
45 | 46 | |
46 | 47 | // expected average number of cookies per request |
47 | 48 | public static final int INITIAL_SIZE = 4; |
48 | private ServerCookie scookies[] = new ServerCookie[INITIAL_SIZE]; | |
49 | private int cookieCount = 0; | |
49 | private ServerCookies scookies = new ServerCookies(INITIAL_SIZE); | |
50 | 50 | private boolean unprocessed = true; |
51 | private boolean useRfc6265 = false; | |
51 | 52 | |
52 | 53 | private final MimeHeaders headers; |
53 | 54 | |
65 | 66 | |
66 | 67 | |
67 | 68 | public void recycle() { |
68 | for (int i = 0; i < cookieCount; i++) { | |
69 | if (scookies[i] != null) { | |
70 | scookies[i].recycle(); | |
71 | } | |
72 | } | |
73 | cookieCount = 0; | |
69 | scookies.recycle(); | |
74 | 70 | unprocessed = true; |
71 | useRfc6265 = false; | |
75 | 72 | } |
76 | 73 | |
77 | 74 | |
99 | 96 | // This will trigger cookie processing |
100 | 97 | getCookieCount(); |
101 | 98 | } |
102 | return scookies[idx]; | |
99 | return scookies.getCookie(idx); | |
103 | 100 | } |
104 | 101 | |
105 | 102 | |
108 | 105 | unprocessed = false; |
109 | 106 | processCookies(headers); |
110 | 107 | } |
111 | return cookieCount; | |
112 | } | |
113 | ||
114 | ||
115 | /** | |
116 | * Register a new, initialized cookie. Cookies are recycled, and most of the | |
117 | * time an existing ServerCookie object is returned. The caller can set the | |
118 | * name/value and attributes for the cookie. | |
119 | */ | |
120 | private ServerCookie addCookie() { | |
121 | if (cookieCount >= scookies.length) { | |
122 | ServerCookie scookiesTmp[] = new ServerCookie[2*cookieCount]; | |
123 | System.arraycopy(scookies, 0, scookiesTmp, 0, cookieCount); | |
124 | scookies = scookiesTmp; | |
125 | } | |
126 | ||
127 | ServerCookie c = scookies[cookieCount]; | |
128 | if (c == null) { | |
129 | c = new ServerCookie(); | |
130 | scookies[cookieCount] = c; | |
131 | } | |
132 | cookieCount++; | |
133 | return c; | |
108 | return scookies.getCookieCount(); | |
134 | 109 | } |
135 | 110 | |
136 | 111 | |
169 | 144 | // search from the next position |
170 | 145 | pos = headers.findHeader("Cookie", ++pos); |
171 | 146 | } |
147 | } | |
148 | ||
149 | ||
150 | public void setUseRfc6265(boolean useRfc6265) { | |
151 | this.useRfc6265 = useRfc6265; | |
172 | 152 | } |
173 | 153 | |
174 | 154 | |
245 | 225 | } |
246 | 226 | |
247 | 227 | |
228 | final void processCookieHeader(byte bytes[], int off, int len) { | |
229 | if (useRfc6265) { | |
230 | Cookie.parseCookie(bytes, off, len, scookies); | |
231 | } else { | |
232 | doProcessCookieHeaderOriginal(bytes, off, len); | |
233 | } | |
234 | } | |
235 | ||
236 | ||
248 | 237 | /** |
249 | 238 | * Parses a cookie header after the initial "Cookie:" |
250 | 239 | * [WS][$]token[WS]=[WS](token|QV)[;|,] |
251 | * RFC 2965 | |
240 | * RFC 2965 / RFC 2109 | |
252 | 241 | * JVK |
253 | 242 | */ |
254 | final void processCookieHeader(byte bytes[], int off, int len){ | |
243 | private void doProcessCookieHeaderOriginal(byte bytes[], int off, int len){ | |
255 | 244 | if (len <= 0 || bytes == null) { |
256 | 245 | return; |
257 | 246 | } |
470 | 459 | continue; |
471 | 460 | } |
472 | 461 | |
473 | sc = addCookie(); | |
462 | sc = scookies.addCookie(); | |
474 | 463 | sc.setVersion( version ); |
475 | 464 | sc.getName().setBytes( bytes, nameStart, |
476 | 465 | nameEnd-nameStart); |
0 | /* | |
1 | * Licensed to the Apache Software Foundation (ASF) under one or more | |
2 | * contributor license agreements. See the NOTICE file distributed with | |
3 | * this work for additional information regarding copyright ownership. | |
4 | * The ASF licenses this file to You under the Apache License, Version 2.0 | |
5 | * (the "License"); you may not use this file except in compliance with | |
6 | * the License. You may obtain a copy of the License at | |
7 | * | |
8 | * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
15 | */ | |
16 | package org.apache.tomcat.util.http; | |
17 | ||
18 | /** | |
19 | * This class is not thread-safe. | |
20 | */ | |
21 | public class ServerCookies { | |
22 | ||
23 | private ServerCookie[] serverCookies; | |
24 | ||
25 | private int cookieCount = 0; | |
26 | ||
27 | ||
28 | public ServerCookies(int initialSize) { | |
29 | serverCookies = new ServerCookie[initialSize]; | |
30 | } | |
31 | ||
32 | ||
33 | /** | |
34 | * Register a new, initialized cookie. Cookies are recycled, and most of the | |
35 | * time an existing ServerCookie object is returned. The caller can set the | |
36 | * name/value and attributes for the cookie. | |
37 | */ | |
38 | public ServerCookie addCookie() { | |
39 | if (cookieCount >= serverCookies.length) { | |
40 | ServerCookie scookiesTmp[] = new ServerCookie[2*cookieCount]; | |
41 | System.arraycopy(serverCookies, 0, scookiesTmp, 0, cookieCount); | |
42 | serverCookies = scookiesTmp; | |
43 | } | |
44 | ||
45 | ServerCookie c = serverCookies[cookieCount]; | |
46 | if (c == null) { | |
47 | c = new ServerCookie(); | |
48 | serverCookies[cookieCount] = c; | |
49 | } | |
50 | cookieCount++; | |
51 | return c; | |
52 | } | |
53 | ||
54 | ||
55 | public ServerCookie getCookie(int idx) { | |
56 | return serverCookies[idx]; | |
57 | } | |
58 | ||
59 | ||
60 | public int getCookieCount() { | |
61 | return cookieCount; | |
62 | } | |
63 | ||
64 | ||
65 | public void recycle() { | |
66 | for (int i = 0; i < cookieCount; i++) { | |
67 | serverCookies[i].recycle(); | |
68 | } | |
69 | cookieCount = 0; | |
70 | } | |
71 | } |
62 | 62 | |
63 | 63 | // See if a quality has been provided |
64 | 64 | double quality = 1; |
65 | HttpParser.SkipResult lookForSemiColon = HttpParser.skipConstant(input, ";"); | |
66 | if (lookForSemiColon == HttpParser.SkipResult.FOUND) { | |
65 | SkipResult lookForSemiColon = HttpParser.skipConstant(input, ";"); | |
66 | if (lookForSemiColon == SkipResult.FOUND) { | |
67 | 67 | quality = HttpParser.readWeight(input, ','); |
68 | 68 | } |
69 | 69 |
77 | 77 | |
78 | 78 | Map<String,String> result = new HashMap<>(); |
79 | 79 | |
80 | if (HttpParser.skipConstant(input, "Digest") != HttpParser.SkipResult.FOUND) { | |
80 | if (HttpParser.skipConstant(input, "Digest") != SkipResult.FOUND) { | |
81 | 81 | return null; |
82 | 82 | } |
83 | 83 | // All field names are valid tokens |
86 | 86 | return null; |
87 | 87 | } |
88 | 88 | while (!field.equals("")) { |
89 | if (HttpParser.skipConstant(input, "=") != HttpParser.SkipResult.FOUND) { | |
89 | if (HttpParser.skipConstant(input, "=") != SkipResult.FOUND) { | |
90 | 90 | return null; |
91 | 91 | } |
92 | 92 | String value; |
126 | 126 | } |
127 | 127 | result.put(field, value); |
128 | 128 | |
129 | if (HttpParser.skipConstant(input, ",") == HttpParser.SkipResult.NOT_FOUND) { | |
129 | if (HttpParser.skipConstant(input, ",") == SkipResult.NOT_FOUND) { | |
130 | 130 | return null; |
131 | 131 | } |
132 | 132 | field = HttpParser.readToken(input); |
0 | /* | |
1 | * Licensed to the Apache Software Foundation (ASF) under one or more | |
2 | * contributor license agreements. See the NOTICE file distributed with | |
3 | * this work for additional information regarding copyright ownership. | |
4 | * The ASF licenses this file to You under the Apache License, Version 2.0 | |
5 | * (the "License"); you may not use this file except in compliance with | |
6 | * the License. You may obtain a copy of the License at | |
7 | * | |
8 | * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
15 | */ | |
16 | package org.apache.tomcat.util.http.parser; | |
17 | ||
18 | import java.nio.charset.StandardCharsets; | |
19 | ||
20 | import org.apache.juli.logging.Log; | |
21 | import org.apache.juli.logging.LogFactory; | |
22 | import org.apache.tomcat.util.http.ServerCookie; | |
23 | import org.apache.tomcat.util.http.ServerCookies; | |
24 | import org.apache.tomcat.util.log.UserDataHelper; | |
25 | import org.apache.tomcat.util.res.StringManager; | |
26 | ||
27 | ||
28 | /** | |
29 | * <p>Cookie header parser based on RFC6265 and RFC2109.</p> | |
30 | * <p>The parsing of cookies using RFC6265 is more relaxed that the | |
31 | * specification in the following ways:</p> | |
32 | * <ul> | |
33 | * <li>Values 0x80 to 0xFF are permitted in cookie-octet to support the use of | |
34 | * UTF-8 in cookie values as used by HTML 5.</li> | |
35 | * <li>For cookies without a value, the '=' is not required after the name as | |
36 | * some browsers do not sent it.</li> | |
37 | * </ul> | |
38 | * <p>The parsing of cookies using RFC2109 is more relaxed that the | |
39 | * specification in the following ways:</p> | |
40 | * <ul> | |
41 | * <li>Values for the path attribute that contain a / character do not have to | |
42 | * be quoted even though / is not permitted in a token.</li> | |
43 | * </ul> | |
44 | * | |
45 | * <p>Implementation note:<br/> | |
46 | * This class has been carefully tuned to ensure that it has equal or better | |
47 | * performance than the original Netscape/RFC2109 cookie parser. Before | |
48 | * committing and changes, ensure that the TesterCookiePerformance unit test | |
49 | * continues to give results within 1% for the old and new parsers.</p> | |
50 | */ | |
51 | public class Cookie { | |
52 | ||
53 | private static final Log log = LogFactory.getLog(Cookie.class); | |
54 | private static final UserDataHelper invalidCookieVersionLog = new UserDataHelper(log); | |
55 | private static final UserDataHelper invalidCookieLog = new UserDataHelper(log); | |
56 | private static final StringManager sm = | |
57 | StringManager.getManager("org.apache.tomcat.util.http.parser"); | |
58 | ||
59 | private static final boolean isCookieOctet[] = new boolean[256]; | |
60 | private static final boolean isText[] = new boolean[256]; | |
61 | private static final byte[] VERSION_BYTES = "$Version".getBytes(StandardCharsets.ISO_8859_1); | |
62 | private static final byte[] PATH_BYTES = "$Path".getBytes(StandardCharsets.ISO_8859_1); | |
63 | private static final byte[] DOMAIN_BYTES = "$Domain".getBytes(StandardCharsets.ISO_8859_1); | |
64 | private static final byte[] EMPTY_BYTES = new byte[0]; | |
65 | private static final byte TAB_BYTE = (byte) 0x09; | |
66 | private static final byte SPACE_BYTE = (byte) 0x20; | |
67 | private static final byte QUOTE_BYTE = (byte) 0x22; | |
68 | private static final byte COMMA_BYTE = (byte) 0x2C; | |
69 | private static final byte FORWARDSLASH_BYTE = (byte) 0x2F; | |
70 | private static final byte SEMICOLON_BYTE = (byte) 0x3B; | |
71 | private static final byte EQUALS_BYTE = (byte) 0x3D; | |
72 | private static final byte SLASH_BYTE = (byte) 0x5C; | |
73 | private static final byte DEL_BYTE = (byte) 0x7F; | |
74 | ||
75 | ||
76 | static { | |
77 | // %x21 / %x23-2B / %x2D-3A / %x3C-5B / %x5D-7E (RFC6265) | |
78 | // %x80 to %xFF (UTF-8) | |
79 | for (int i = 0; i < 256; i++) { | |
80 | if (i < 0x21 || i == QUOTE_BYTE || i == COMMA_BYTE || | |
81 | i == SEMICOLON_BYTE || i == SLASH_BYTE || i == DEL_BYTE) { | |
82 | isCookieOctet[i] = false; | |
83 | } else { | |
84 | isCookieOctet[i] = true; | |
85 | } | |
86 | } | |
87 | for (int i = 0; i < 256; i++) { | |
88 | if (i < 0x21 || i == DEL_BYTE) { | |
89 | isText[i] = false; | |
90 | } else { | |
91 | isText[i] = true; | |
92 | } | |
93 | } | |
94 | } | |
95 | ||
96 | ||
97 | private Cookie() { | |
98 | // Hide default constructor | |
99 | } | |
100 | ||
101 | ||
102 | public static void parseCookie(byte[] bytes, int offset, int len, | |
103 | ServerCookies serverCookies) { | |
104 | ||
105 | // ByteBuffer is used throughout this parser as it allows the byte[] | |
106 | // and position information to be easily passed between parsing methods | |
107 | ByteBuffer bb = new ByteBuffer(bytes, offset, len); | |
108 | ||
109 | // Using RFC6265 parsing rules, check to see if the header starts with a | |
110 | // version marker. An RFC2109 version marker may be read using RFC6265 | |
111 | // parsing rules. If version 1, use RFC2109. Else use RFC6265. | |
112 | ||
113 | skipLWS(bb); | |
114 | ||
115 | // Record position in case we need to return. | |
116 | int mark = bb.position(); | |
117 | ||
118 | SkipResult skipResult = skipBytes(bb, VERSION_BYTES); | |
119 | if (skipResult != SkipResult.FOUND) { | |
120 | // No need to reset position since skipConstant() will have done it | |
121 | parseCookieRfc6265(bb, serverCookies); | |
122 | return; | |
123 | } | |
124 | ||
125 | skipLWS(bb); | |
126 | ||
127 | skipResult = skipByte(bb, EQUALS_BYTE); | |
128 | if (skipResult != SkipResult.FOUND) { | |
129 | // Need to reset position as skipConstant() will only have reset to | |
130 | // position before it was called | |
131 | bb.position(mark); | |
132 | parseCookieRfc6265(bb, serverCookies); | |
133 | return; | |
134 | } | |
135 | ||
136 | skipLWS(bb); | |
137 | ||
138 | ByteBuffer value = readCookieValue(bb); | |
139 | if (value != null && value.remaining() == 1) { | |
140 | if (value.get() == (byte) 49) { | |
141 | // $Version=1 -> RFC2109 | |
142 | skipLWS(bb); | |
143 | byte b = bb.get(); | |
144 | if (b == SEMICOLON_BYTE || b == COMMA_BYTE) { | |
145 | parseCookieRfc2109(bb, serverCookies); | |
146 | } | |
147 | return; | |
148 | } else { | |
149 | // Unrecognised version. | |
150 | // Ignore this header. | |
151 | value.rewind(); | |
152 | logInvalidVersion(value); | |
153 | } | |
154 | } else { | |
155 | // Unrecognised version. | |
156 | // Ignore this header. | |
157 | logInvalidVersion(value); | |
158 | } | |
159 | } | |
160 | ||
161 | ||
162 | public static String unescapeCookieValueRfc2109(String input) { | |
163 | if (input == null || input.length() < 2) { | |
164 | return input; | |
165 | } | |
166 | if (input.charAt(0) != '"' && input.charAt(input.length() - 1) != '"') { | |
167 | return input; | |
168 | } | |
169 | ||
170 | StringBuilder sb = new StringBuilder(input.length()); | |
171 | char[] chars = input.toCharArray(); | |
172 | boolean escaped = false; | |
173 | ||
174 | for (int i = 1; i < input.length() - 1; i++) { | |
175 | if (chars[i] == '\\') { | |
176 | escaped = true; | |
177 | } else if (escaped) { | |
178 | escaped = false; | |
179 | if (chars[i] < 128) { | |
180 | sb.append(chars[i]); | |
181 | } else { | |
182 | sb.append('\\'); | |
183 | sb.append(chars[i]); | |
184 | } | |
185 | } else { | |
186 | sb.append(chars[i]); | |
187 | } | |
188 | } | |
189 | return sb.toString(); | |
190 | } | |
191 | ||
192 | ||
193 | private static void parseCookieRfc6265(ByteBuffer bb, ServerCookies serverCookies) { | |
194 | ||
195 | boolean moreToProcess = true; | |
196 | ||
197 | while (moreToProcess) { | |
198 | skipLWS(bb); | |
199 | ||
200 | ByteBuffer name = readToken(bb); | |
201 | ByteBuffer value = null; | |
202 | ||
203 | skipLWS(bb); | |
204 | ||
205 | SkipResult skipResult = skipByte(bb, EQUALS_BYTE); | |
206 | if (skipResult == SkipResult.FOUND) { | |
207 | skipLWS(bb); | |
208 | value = readCookieValueRfc6265(bb); | |
209 | if (value == null) { | |
210 | logInvalidHeader(bb); | |
211 | // Invalid cookie value. Skip to the next semi-colon | |
212 | skipUntilSemiColon(bb); | |
213 | continue; | |
214 | } | |
215 | skipLWS(bb); | |
216 | } | |
217 | ||
218 | skipResult = skipByte(bb, SEMICOLON_BYTE); | |
219 | if (skipResult == SkipResult.FOUND) { | |
220 | // NO-OP | |
221 | } else if (skipResult == SkipResult.NOT_FOUND) { | |
222 | logInvalidHeader(bb); | |
223 | // Invalid cookie. Ignore it and skip to the next semi-colon | |
224 | skipUntilSemiColon(bb); | |
225 | continue; | |
226 | } else { | |
227 | // SkipResult.EOF | |
228 | moreToProcess = false; | |
229 | } | |
230 | ||
231 | if (name.hasRemaining()) { | |
232 | ServerCookie sc = serverCookies.addCookie(); | |
233 | sc.getName().setBytes(name.array(), name.position(), name.remaining()); | |
234 | if (value == null) { | |
235 | sc.getValue().setBytes(EMPTY_BYTES, 0, EMPTY_BYTES.length); | |
236 | } else { | |
237 | sc.getValue().setBytes(value.array(), value.position(), value.remaining()); | |
238 | } | |
239 | } | |
240 | } | |
241 | } | |
242 | ||
243 | ||
244 | private static void parseCookieRfc2109(ByteBuffer bb, ServerCookies serverCookies) { | |
245 | ||
246 | boolean moreToProcess = true; | |
247 | ||
248 | while (moreToProcess) { | |
249 | skipLWS(bb); | |
250 | ||
251 | boolean parseAttributes = true; | |
252 | ||
253 | ByteBuffer name = readToken(bb); | |
254 | ByteBuffer value = null; | |
255 | ByteBuffer path = null; | |
256 | ByteBuffer domain = null; | |
257 | ||
258 | skipLWS(bb); | |
259 | ||
260 | SkipResult skipResult = skipByte(bb, EQUALS_BYTE); | |
261 | if (skipResult == SkipResult.FOUND) { | |
262 | skipLWS(bb); | |
263 | value = readCookieValueRfc2109(bb, false); | |
264 | if (value == null) { | |
265 | skipInvalidCookie(bb); | |
266 | continue; | |
267 | } | |
268 | skipLWS(bb); | |
269 | } | |
270 | ||
271 | skipResult = skipByte(bb, COMMA_BYTE); | |
272 | if (skipResult == SkipResult.FOUND) { | |
273 | parseAttributes = false; | |
274 | } | |
275 | skipResult = skipByte(bb, SEMICOLON_BYTE); | |
276 | if (skipResult == SkipResult.EOF) { | |
277 | parseAttributes = false; | |
278 | moreToProcess = false; | |
279 | } else if (skipResult == SkipResult.NOT_FOUND) { | |
280 | skipInvalidCookie(bb); | |
281 | continue; | |
282 | } | |
283 | ||
284 | if (parseAttributes) { | |
285 | skipResult = skipBytes(bb, PATH_BYTES); | |
286 | if (skipResult == SkipResult.FOUND) { | |
287 | skipLWS(bb); | |
288 | skipResult = skipByte(bb, EQUALS_BYTE); | |
289 | if (skipResult != SkipResult.FOUND) { | |
290 | skipInvalidCookie(bb); | |
291 | continue; | |
292 | } | |
293 | path = readCookieValueRfc2109(bb, true); | |
294 | if (path == null) { | |
295 | skipInvalidCookie(bb); | |
296 | continue; | |
297 | } | |
298 | skipLWS(bb); | |
299 | ||
300 | skipResult = skipByte(bb, COMMA_BYTE); | |
301 | if (skipResult == SkipResult.FOUND) { | |
302 | parseAttributes = false; | |
303 | } | |
304 | skipResult = skipByte(bb, SEMICOLON_BYTE); | |
305 | if (skipResult == SkipResult.EOF) { | |
306 | parseAttributes = false; | |
307 | moreToProcess = false; | |
308 | } else if (skipResult == SkipResult.NOT_FOUND) { | |
309 | skipInvalidCookie(bb); | |
310 | continue; | |
311 | } | |
312 | } | |
313 | } | |
314 | ||
315 | if (parseAttributes) { | |
316 | skipResult = skipBytes(bb, DOMAIN_BYTES); | |
317 | if (skipResult == SkipResult.FOUND) { | |
318 | skipLWS(bb); | |
319 | skipResult = skipByte(bb, EQUALS_BYTE); | |
320 | if (skipResult != SkipResult.FOUND) { | |
321 | skipInvalidCookie(bb); | |
322 | continue; | |
323 | } | |
324 | domain = readCookieValueRfc2109(bb, false); | |
325 | if (domain == null) { | |
326 | skipInvalidCookie(bb); | |
327 | continue; | |
328 | } | |
329 | ||
330 | skipResult = skipByte(bb, COMMA_BYTE); | |
331 | if (skipResult == SkipResult.FOUND) { | |
332 | parseAttributes = false; | |
333 | } | |
334 | skipResult = skipByte(bb, SEMICOLON_BYTE); | |
335 | if (skipResult == SkipResult.EOF) { | |
336 | parseAttributes = false; | |
337 | moreToProcess = false; | |
338 | } else if (skipResult == SkipResult.NOT_FOUND) { | |
339 | skipInvalidCookie(bb); | |
340 | continue; | |
341 | } | |
342 | } | |
343 | } | |
344 | ||
345 | if (name.hasRemaining() && value != null && value.hasRemaining()) { | |
346 | ServerCookie sc = serverCookies.addCookie(); | |
347 | sc.setVersion(1); | |
348 | sc.getName().setBytes(name.array(), name.position(), name.remaining()); | |
349 | sc.getValue().setBytes(value.array(), value.position(), value.remaining()); | |
350 | if (domain != null) { | |
351 | sc.getDomain().setBytes(domain.array(), domain.position(), domain.remaining()); | |
352 | } | |
353 | if (path != null) { | |
354 | sc.getPath().setBytes(path.array(), path.position(), path.remaining()); | |
355 | } | |
356 | } | |
357 | } | |
358 | } | |
359 | ||
360 | ||
361 | private static void skipInvalidCookie(ByteBuffer bb) { | |
362 | logInvalidHeader(bb); | |
363 | // Invalid cookie value. Skip to the next semi-colon | |
364 | skipUntilSemiColonOrComma(bb); | |
365 | } | |
366 | ||
367 | ||
368 | private static void skipLWS(ByteBuffer bb) { | |
369 | while(bb.hasRemaining()) { | |
370 | byte b = bb.get(); | |
371 | if (b != TAB_BYTE && b != SPACE_BYTE) { | |
372 | bb.rewind(); | |
373 | break; | |
374 | } | |
375 | } | |
376 | } | |
377 | ||
378 | ||
379 | private static void skipUntilSemiColon(ByteBuffer bb) { | |
380 | while(bb.hasRemaining()) { | |
381 | if (bb.get() == SEMICOLON_BYTE) { | |
382 | break; | |
383 | } | |
384 | } | |
385 | } | |
386 | ||
387 | ||
388 | private static void skipUntilSemiColonOrComma(ByteBuffer bb) { | |
389 | while(bb.hasRemaining()) { | |
390 | byte b = bb.get(); | |
391 | if (b == SEMICOLON_BYTE || b == COMMA_BYTE) { | |
392 | break; | |
393 | } | |
394 | } | |
395 | } | |
396 | ||
397 | ||
398 | private static SkipResult skipByte(ByteBuffer bb, byte target) { | |
399 | ||
400 | if (!bb.hasRemaining()) { | |
401 | return SkipResult.EOF; | |
402 | } | |
403 | if (bb.get() == target) { | |
404 | return SkipResult.FOUND; | |
405 | } | |
406 | ||
407 | bb.rewind(); | |
408 | return SkipResult.NOT_FOUND; | |
409 | } | |
410 | ||
411 | ||
412 | private static SkipResult skipBytes(ByteBuffer bb, byte[] target) { | |
413 | int mark = bb.position(); | |
414 | ||
415 | for (int i = 0; i < target.length; i++) { | |
416 | if (!bb.hasRemaining()) { | |
417 | bb.position(mark); | |
418 | return SkipResult.EOF; | |
419 | } | |
420 | if (bb.get() != target[i]) { | |
421 | bb.position(mark); | |
422 | return SkipResult.NOT_FOUND; | |
423 | } | |
424 | } | |
425 | return SkipResult.FOUND; | |
426 | } | |
427 | ||
428 | ||
429 | /** | |
430 | * Similar to readCookieValueRfc6265() but also allows a comma to terminate | |
431 | * the value (as permitted by RFC2109). | |
432 | */ | |
433 | private static ByteBuffer readCookieValue(ByteBuffer bb) { | |
434 | boolean quoted = false; | |
435 | if (bb.hasRemaining()) { | |
436 | if (bb.get() == QUOTE_BYTE) { | |
437 | quoted = true; | |
438 | } else { | |
439 | bb.rewind(); | |
440 | } | |
441 | } | |
442 | int start = bb.position(); | |
443 | int end = bb.limit(); | |
444 | while (bb.hasRemaining()) { | |
445 | byte b = bb.get(); | |
446 | if (isCookieOctet[(b & 0xFF)]) { | |
447 | // NO-OP | |
448 | } else if (b == SEMICOLON_BYTE || b == COMMA_BYTE || b == SPACE_BYTE || b == TAB_BYTE) { | |
449 | end = bb.position() - 1; | |
450 | bb.position(end); | |
451 | break; | |
452 | } else if (quoted && b == QUOTE_BYTE) { | |
453 | end = bb.position() - 1; | |
454 | break; | |
455 | } else { | |
456 | // Invalid cookie | |
457 | return null; | |
458 | } | |
459 | } | |
460 | ||
461 | return new ByteBuffer(bb.bytes, start, end - start); | |
462 | } | |
463 | ||
464 | ||
465 | /** | |
466 | * Similar to readCookieValue() but treats a comma as part of an invalid | |
467 | * value. | |
468 | */ | |
469 | private static ByteBuffer readCookieValueRfc6265(ByteBuffer bb) { | |
470 | boolean quoted = false; | |
471 | if (bb.hasRemaining()) { | |
472 | if (bb.get() == QUOTE_BYTE) { | |
473 | quoted = true; | |
474 | } else { | |
475 | bb.rewind(); | |
476 | } | |
477 | } | |
478 | int start = bb.position(); | |
479 | int end = bb.limit(); | |
480 | while (bb.hasRemaining()) { | |
481 | byte b = bb.get(); | |
482 | if (isCookieOctet[(b & 0xFF)]) { | |
483 | // NO-OP | |
484 | } else if (b == SEMICOLON_BYTE || b == SPACE_BYTE || b == TAB_BYTE) { | |
485 | end = bb.position() - 1; | |
486 | bb.position(end); | |
487 | break; | |
488 | } else if (quoted && b == QUOTE_BYTE) { | |
489 | end = bb.position() - 1; | |
490 | break; | |
491 | } else { | |
492 | // Invalid cookie | |
493 | return null; | |
494 | } | |
495 | } | |
496 | ||
497 | return new ByteBuffer(bb.bytes, start, end - start); | |
498 | } | |
499 | ||
500 | ||
501 | private static ByteBuffer readCookieValueRfc2109(ByteBuffer bb, boolean allowForwardSlash) { | |
502 | if (!bb.hasRemaining()) { | |
503 | return null; | |
504 | } | |
505 | ||
506 | if (bb.peek() == QUOTE_BYTE) { | |
507 | return readQuotedString(bb); | |
508 | } else { | |
509 | if (allowForwardSlash) { | |
510 | return readTokenAllowForwardSlash(bb); | |
511 | } else { | |
512 | return readToken(bb); | |
513 | } | |
514 | } | |
515 | } | |
516 | ||
517 | ||
518 | private static ByteBuffer readToken(ByteBuffer bb) { | |
519 | final int start = bb.position(); | |
520 | int end = bb.limit(); | |
521 | while (bb.hasRemaining()) { | |
522 | if (!HttpParser.isToken(bb.get())) { | |
523 | end = bb.position() - 1; | |
524 | bb.position(end); | |
525 | break; | |
526 | } | |
527 | } | |
528 | ||
529 | return new ByteBuffer(bb.bytes, start, end - start); | |
530 | } | |
531 | ||
532 | ||
533 | private static ByteBuffer readTokenAllowForwardSlash(ByteBuffer bb) { | |
534 | final int start = bb.position(); | |
535 | int end = bb.limit(); | |
536 | while (bb.hasRemaining()) { | |
537 | byte b = bb.get(); | |
538 | if (b != FORWARDSLASH_BYTE && !HttpParser.isToken(b)) { | |
539 | end = bb.position() - 1; | |
540 | bb.position(end); | |
541 | break; | |
542 | } | |
543 | } | |
544 | ||
545 | return new ByteBuffer(bb.bytes, start, end - start); | |
546 | } | |
547 | ||
548 | ||
549 | private static ByteBuffer readQuotedString(ByteBuffer bb) { | |
550 | int start = bb.position(); | |
551 | ||
552 | // Read the opening quote | |
553 | bb.get(); | |
554 | boolean escaped = false; | |
555 | while (bb.hasRemaining()) { | |
556 | byte b = bb.get(); | |
557 | if (b == SLASH_BYTE) { | |
558 | // Escaping another character | |
559 | escaped = true; | |
560 | } else if (escaped && b > (byte) -1) { | |
561 | escaped = false; | |
562 | } else if (b == QUOTE_BYTE) { | |
563 | return new ByteBuffer(bb.bytes, start, bb.position() - start); | |
564 | } else if (isText[b & 0xFF]) { | |
565 | escaped = false; | |
566 | } else { | |
567 | return null; | |
568 | } | |
569 | } | |
570 | ||
571 | return null; | |
572 | } | |
573 | ||
574 | ||
575 | private static void logInvalidHeader(ByteBuffer bb) { | |
576 | UserDataHelper.Mode logMode = invalidCookieLog.getNextMode(); | |
577 | if (logMode != null) { | |
578 | String headerValue = new String(bb.array(), bb.position(), bb.limit() - bb.position(), | |
579 | StandardCharsets.UTF_8); | |
580 | String message = sm.getString("cookie.invalidCookieValue", headerValue); | |
581 | switch (logMode) { | |
582 | case INFO_THEN_DEBUG: | |
583 | message += sm.getString("cookie.fallToDebug"); | |
584 | //$FALL-THROUGH$ | |
585 | case INFO: | |
586 | log.info(message); | |
587 | break; | |
588 | case DEBUG: | |
589 | log.debug(message); | |
590 | } | |
591 | } | |
592 | } | |
593 | ||
594 | ||
595 | private static void logInvalidVersion(ByteBuffer value) { | |
596 | UserDataHelper.Mode logMode = invalidCookieVersionLog.getNextMode(); | |
597 | if (logMode != null) { | |
598 | String version; | |
599 | if (value == null) { | |
600 | version = sm.getString("cookie.valueNotPresent"); | |
601 | } else { | |
602 | version = new String(value.bytes, value.position(), | |
603 | value.limit() - value.position(), StandardCharsets.UTF_8); | |
604 | } | |
605 | String message = sm.getString("cookie.invalidCookieVersion", version); | |
606 | switch (logMode) { | |
607 | case INFO_THEN_DEBUG: | |
608 | message += sm.getString("cookie.fallToDebug"); | |
609 | //$FALL-THROUGH$ | |
610 | case INFO: | |
611 | log.info(message); | |
612 | break; | |
613 | case DEBUG: | |
614 | log.debug(message); | |
615 | } | |
616 | } | |
617 | } | |
618 | ||
619 | ||
620 | /** | |
621 | * Custom implementation that skips many of the safety checks in | |
622 | * {@link javax.nio.ByteBuffer}. | |
623 | */ | |
624 | private static class ByteBuffer { | |
625 | ||
626 | private final byte[] bytes; | |
627 | private int limit; | |
628 | private int position = 0; | |
629 | ||
630 | public ByteBuffer(byte[] bytes, int offset, int len) { | |
631 | this.bytes = bytes; | |
632 | this.position = offset; | |
633 | this.limit = offset + len; | |
634 | } | |
635 | ||
636 | public int position() { | |
637 | return position; | |
638 | } | |
639 | ||
640 | public void position(int position) { | |
641 | this.position = position; | |
642 | } | |
643 | ||
644 | public int limit() { | |
645 | return limit; | |
646 | } | |
647 | ||
648 | public int remaining() { | |
649 | return limit - position; | |
650 | } | |
651 | ||
652 | public boolean hasRemaining() { | |
653 | return position < limit; | |
654 | } | |
655 | ||
656 | public byte get() { | |
657 | return bytes[position++]; | |
658 | } | |
659 | ||
660 | public byte peek() { | |
661 | return bytes[position]; | |
662 | } | |
663 | ||
664 | public void rewind() { | |
665 | position--; | |
666 | } | |
667 | ||
668 | public byte[] array() { | |
669 | return bytes; | |
670 | } | |
671 | ||
672 | // For debug purposes | |
673 | @Override | |
674 | public String toString() { | |
675 | return "position [" + position + "], limit [" + limit + "]"; | |
676 | } | |
677 | } | |
678 | } |
397 | 397 | return SkipResult.FOUND; |
398 | 398 | } |
399 | 399 | } |
400 | ||
401 | ||
402 | static enum SkipResult { | |
403 | FOUND, | |
404 | NOT_FOUND, | |
405 | EOF | |
406 | } | |
407 | 400 | } |
0 | # Licensed to the Apache Software Foundation (ASF) under one or more | |
1 | # contributor license agreements. See the NOTICE file distributed with | |
2 | # this work for additional information regarding copyright ownership. | |
3 | # The ASF licenses this file to You under the Apache License, Version 2.0 | |
4 | # (the "License"); you may not use this file except in compliance with | |
5 | # the License. You may obtain a copy of the License at | |
6 | # | |
7 | # http://www.apache.org/licenses/LICENSE-2.0 | |
8 | # | |
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 | cookie.fallToDebug=Note: further occurrences of this error will be logged at DEBUG level. | |
16 | cookie.invalidCookieValue=A cookie header was received [{0}] that contained an invalid cookie. That cookie will be ignored. | |
17 | cookie.invalidCookieVersion=A cookie header was received using an unrecognised cookie version of [{0}]. The header and the cookies it contains will be ignored. | |
18 | cookie.valueNotPresent=<not present>⏎ |
137 | 137 | return null; |
138 | 138 | } |
139 | 139 | |
140 | if (HttpParser.skipConstant(input, "/") == HttpParser.SkipResult.NOT_FOUND) { | |
140 | if (HttpParser.skipConstant(input, "/") == SkipResult.NOT_FOUND) { | |
141 | 141 | return null; |
142 | 142 | } |
143 | 143 | |
149 | 149 | |
150 | 150 | LinkedHashMap<String,String> parameters = new LinkedHashMap<>(); |
151 | 151 | |
152 | HttpParser.SkipResult lookForSemiColon = HttpParser.skipConstant(input, ";"); | |
153 | if (lookForSemiColon == HttpParser.SkipResult.NOT_FOUND) { | |
152 | SkipResult lookForSemiColon = HttpParser.skipConstant(input, ";"); | |
153 | if (lookForSemiColon == SkipResult.NOT_FOUND) { | |
154 | 154 | return null; |
155 | 155 | } |
156 | while (lookForSemiColon == HttpParser.SkipResult.FOUND) { | |
156 | while (lookForSemiColon == SkipResult.FOUND) { | |
157 | 157 | String attribute = HttpParser.readToken(input); |
158 | 158 | |
159 | 159 | String value = ""; |
160 | if (HttpParser.skipConstant(input, "=") == HttpParser.SkipResult.FOUND) { | |
160 | if (HttpParser.skipConstant(input, "=") == SkipResult.FOUND) { | |
161 | 161 | value = HttpParser.readTokenOrQuotedString(input, true); |
162 | 162 | } |
163 | 163 | |
166 | 166 | } |
167 | 167 | |
168 | 168 | lookForSemiColon = HttpParser.skipConstant(input, ";"); |
169 | if (lookForSemiColon == HttpParser.SkipResult.NOT_FOUND) { | |
169 | if (lookForSemiColon == SkipResult.NOT_FOUND) { | |
170 | 170 | return null; |
171 | 171 | } |
172 | 172 | } |
0 | /* | |
1 | * Licensed to the Apache Software Foundation (ASF) under one or more | |
2 | * contributor license agreements. See the NOTICE file distributed with | |
3 | * this work for additional information regarding copyright ownership. | |
4 | * The ASF licenses this file to You under the Apache License, Version 2.0 | |
5 | * (the "License"); you may not use this file except in compliance with | |
6 | * the License. You may obtain a copy of the License at | |
7 | * | |
8 | * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
15 | */ | |
16 | package org.apache.tomcat.util.http.parser; | |
17 | ||
18 | enum SkipResult { | |
19 | FOUND, | |
20 | NOT_FOUND, | |
21 | EOF | |
22 | }⏎ |
351 | 351 | // TODO : Investigate if it is possible to extract the current list of |
352 | 352 | // available ciphers. Native code changes will be required. |
353 | 353 | return new String[] { getSSLCipherSuite() }; |
354 | } | |
355 | ||
356 | ||
357 | /** | |
358 | * This endpoint does not support <code>-1</code> for unlimited connections, | |
359 | * nor does it support setting this attribute while the endpoint is running. | |
360 | * | |
361 | * {@inheritDoc} | |
362 | */ | |
363 | @Override | |
364 | public void setMaxConnections(int maxConnections) { | |
365 | if (maxConnections == -1) { | |
366 | log.warn(sm.getString("endpoint.apr.maxConnections.unlimited", | |
367 | Integer.valueOf(getMaxConnections()))); | |
368 | return; | |
369 | } | |
370 | if (running) { | |
371 | log.warn(sm.getString("endpoint.apr.maxConnections.running", | |
372 | Integer.valueOf(getMaxConnections()))); | |
373 | return; | |
374 | } | |
375 | super.setMaxConnections(maxConnections); | |
354 | 376 | } |
355 | 377 | |
356 | 378 |
51 | 51 | endpoint.timeout.err=Error processing socket timeout |
52 | 52 | endpoint.apr.failSslContextMake=Unable to create SSLContext. Check that SSLEngine is enabled in the AprLifecycleListener, the AprLifecycleListener has initialised correctly and that a valid SSLProtocol has been specified |
53 | 53 | endpoint.apr.invalidSslProtocol=An invalid value [{0}] was provided for the SSLProtocol attribute |
54 | endpoint.apr.maxConnections.running=The APR endpoint does not support the setting of maxConnections while it is running. The existing value of [{0}] will continue to be used. | |
55 | endpoint.apr.maxConnections.unlimited=The APR endpoint does not support unlimited connections. The existing value of [{0}] will continue to be used. | |
54 | 56 | endpoint.apr.noSendfileWithSSL=Sendfile is not supported for the APR/native connector when SSL is enabled |
55 | 57 | endpoint.apr.noSslCertFile=Connector attribute SSLCertificateFile must be defined when using SSL with APR |
56 | 58 | endpoint.apr.pollAddInvalid=Invalid attempted to add a socket [{0}] to the poller |
0 | /* | |
1 | * Licensed to the Apache Software Foundation (ASF) under one or more | |
2 | * contributor license agreements. See the NOTICE file distributed with | |
3 | * this work for additional information regarding copyright ownership. | |
4 | * The ASF licenses this file to You under the Apache License, Version 2.0 | |
5 | * (the "License"); you may not use this file except in compliance with | |
6 | * the License. You may obtain a copy of the License at | |
7 | * | |
8 | * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
15 | */ | |
16 | package org.apache.tomcat.util.security; | |
17 | ||
18 | import java.security.MessageDigest; | |
19 | import java.security.NoSuchAlgorithmException; | |
20 | import java.util.HashMap; | |
21 | import java.util.Map; | |
22 | import java.util.Queue; | |
23 | import java.util.concurrent.ConcurrentLinkedQueue; | |
24 | ||
25 | /** | |
26 | * A thread safe wrapper around {@link MessageDigest} that does not make use | |
27 | * of ThreadLocal and - broadly - only creates enough MessageDigest objects | |
28 | * to satisfy the concurrency requirements. | |
29 | */ | |
30 | public class ConcurrentMessageDigest { | |
31 | ||
32 | private static final String MD5 = "MD5"; | |
33 | private static final String SHA1 = "SHA-1"; | |
34 | ||
35 | private static final Map<String,Queue<MessageDigest>> queues = | |
36 | new HashMap<>(); | |
37 | ||
38 | ||
39 | private ConcurrentMessageDigest() { | |
40 | // Hide default constructor for this utility class | |
41 | } | |
42 | ||
43 | static { | |
44 | try { | |
45 | // Init commonly used algorithms | |
46 | init(MD5); | |
47 | init(SHA1); | |
48 | } catch (NoSuchAlgorithmException e) { | |
49 | throw new IllegalArgumentException(e); | |
50 | } | |
51 | } | |
52 | ||
53 | public static byte[] digestMD5(byte[]... input) { | |
54 | return digest(MD5, input); | |
55 | } | |
56 | ||
57 | public static byte[] digestSHA1(byte[]... input) { | |
58 | return digest(SHA1, input); | |
59 | } | |
60 | ||
61 | public static byte[] digest(String algorithm, byte[]... input) { | |
62 | ||
63 | Queue<MessageDigest> queue = queues.get(algorithm); | |
64 | if (queue == null) { | |
65 | throw new IllegalStateException("Must call init() first"); | |
66 | } | |
67 | ||
68 | MessageDigest md = queue.poll(); | |
69 | if (md == null) { | |
70 | try { | |
71 | md = MessageDigest.getInstance(algorithm); | |
72 | } catch (NoSuchAlgorithmException e) { | |
73 | // Ignore. Impossible if init() has been successfully called | |
74 | // first. | |
75 | throw new IllegalStateException("Must call init() first"); | |
76 | } | |
77 | } | |
78 | ||
79 | for (byte[] bytes : input) { | |
80 | md.update(bytes); | |
81 | } | |
82 | byte[] result = md.digest(); | |
83 | ||
84 | queue.add(md); | |
85 | ||
86 | return result; | |
87 | } | |
88 | ||
89 | ||
90 | /** | |
91 | * Ensures that {@link #digest(String, byte[])} will support the specified | |
92 | * algorithm. This method <b>must</b> be called and return successfully | |
93 | * before using {@link #digest(String, byte[])}. | |
94 | * | |
95 | * @param algorithm The message digest algorithm to be supported | |
96 | * | |
97 | * @throws NoSuchAlgorithmException If the algorithm is not supported by the | |
98 | * JVM | |
99 | */ | |
100 | public static void init(String algorithm) throws NoSuchAlgorithmException { | |
101 | synchronized (queues) { | |
102 | if (!queues.containsKey(algorithm)) { | |
103 | MessageDigest md = MessageDigest.getInstance(algorithm); | |
104 | Queue<MessageDigest> queue = new ConcurrentLinkedQueue<>(); | |
105 | queue.add(md); | |
106 | queues.put(algorithm, queue); | |
107 | } | |
108 | } | |
109 | } | |
110 | } |
0 | /* | |
1 | * Licensed to the Apache Software Foundation (ASF) under one or more | |
2 | * contributor license agreements. See the NOTICE file distributed with | |
3 | * this work for additional information regarding copyright ownership. | |
4 | * The ASF licenses this file to You under the Apache License, Version 2.0 | |
5 | * (the "License"); you may not use this file except in compliance with | |
6 | * the License. You may obtain a copy of the License at | |
7 | * | |
8 | * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
15 | */ | |
16 | package org.apache.tomcat.util.security; | |
17 | ||
18 | /** | |
19 | * Encode an MD5 digest into a String. | |
20 | * <p> | |
21 | * The 128 bit MD5 hash is converted into a 32 character long String. | |
22 | * Each character of the String is the hexadecimal representation of 4 bits | |
23 | * of the digest. | |
24 | * | |
25 | * @author Remy Maucherat | |
26 | */ | |
27 | public final class MD5Encoder { | |
28 | ||
29 | ||
30 | private MD5Encoder() { | |
31 | // Hide default constructor for utility class | |
32 | } | |
33 | ||
34 | ||
35 | private static final char[] hexadecimal = {'0', '1', '2', '3', '4', '5', | |
36 | '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; | |
37 | ||
38 | ||
39 | /** | |
40 | * Encodes the 128 bit (16 bytes) MD5 into a 32 character String. | |
41 | * | |
42 | * @param binaryData Array containing the digest | |
43 | * | |
44 | * @return Encoded MD5, or null if encoding failed | |
45 | */ | |
46 | public static String encode(byte[] binaryData) { | |
47 | ||
48 | if (binaryData.length != 16) | |
49 | return null; | |
50 | ||
51 | char[] buffer = new char[32]; | |
52 | ||
53 | for (int i=0; i<16; i++) { | |
54 | int low = binaryData[i] & 0x0f; | |
55 | int high = (binaryData[i] & 0xf0) >> 4; | |
56 | buffer[i*2] = hexadecimal[high]; | |
57 | buffer[i*2 + 1] = hexadecimal[low]; | |
58 | } | |
59 | ||
60 | return new String(buffer); | |
61 | } | |
62 | } | |
63 |
23 | 23 | public static final String Package = "org.apache.tomcat.util.threads"; |
24 | 24 | |
25 | 25 | public static final long DEFAULT_THREAD_RENEWAL_DELAY = 1000L; |
26 | ||
27 | /** | |
28 | * Has security been turned on? | |
29 | */ | |
30 | public static final boolean IS_SECURITY_ENABLED = (System.getSecurityManager() != null); | |
26 | 31 | } |
20 | 20 | import java.util.concurrent.ThreadFactory; |
21 | 21 | import java.util.concurrent.atomic.AtomicInteger; |
22 | 22 | |
23 | import org.apache.tomcat.util.net.Constants; | |
24 | 23 | import org.apache.tomcat.util.security.PrivilegedSetTccl; |
24 | ||
25 | 25 | /** |
26 | 26 | * Simple task thread factory to use to create threads for an executor |
27 | 27 | * implementation. |
50 | 50 | private final Inflater inflater = new Inflater(true); |
51 | 51 | private final ByteBuffer readBuffer = ByteBuffer.allocate(Constants.DEFAULT_BUFFER_SIZE); |
52 | 52 | private final Deflater deflater = new Deflater(Deflater.DEFAULT_COMPRESSION, true); |
53 | private final byte[] EOM_BUFFER = new byte[EOM_BYTES.length + 1]; | |
53 | 54 | |
54 | 55 | private volatile Transformation next; |
55 | 56 | private volatile boolean skipDecompression = false; |
56 | 57 | private volatile ByteBuffer writeBuffer = ByteBuffer.allocate(Constants.DEFAULT_BUFFER_SIZE); |
57 | 58 | private volatile boolean firstCompressedFrameWritten = false; |
58 | private volatile byte[] EOM_BUFFER = new byte[EOM_BYTES.length + 1]; | |
59 | 59 | |
60 | 60 | static PerMessageDeflate negotiate(List<List<Parameter>> preferences) { |
61 | 61 | // Accept the first preference that the server is able to support |
171 | 171 | } |
172 | 172 | |
173 | 173 | |
174 | public static Class<?> getDecoderType(Class<? extends Decoder> decoder) { | |
174 | private static Class<?> getDecoderType(Class<? extends Decoder> decoder) { | |
175 | 175 | return Util.getGenericType(Decoder.class, decoder).getClazz(); |
176 | 176 | } |
177 | 177 | |
352 | 352 | } |
353 | 353 | |
354 | 354 | |
355 | public static Set<MessageHandlerResult> getMessageHandlers( | |
355 | static Set<MessageHandlerResult> getMessageHandlers(Class<?> target, | |
356 | 356 | MessageHandler listener, EndpointConfig endpointConfig, |
357 | 357 | Session session) { |
358 | ||
359 | Class<?> target = Util.getMessageType(listener); | |
360 | 358 | |
361 | 359 | // Will never be more than 2 types |
362 | 360 | Set<MessageHandlerResult> results = new HashSet<>(2); |
36 | 36 | import javax.websocket.EndpointConfig; |
37 | 37 | import javax.websocket.Extension; |
38 | 38 | import javax.websocket.MessageHandler; |
39 | import javax.websocket.MessageHandler.Partial; | |
40 | import javax.websocket.MessageHandler.Whole; | |
39 | 41 | import javax.websocket.PongMessage; |
40 | 42 | import javax.websocket.RemoteEndpoint; |
41 | 43 | import javax.websocket.SendResult; |
74 | 76 | private final Principal userPrincipal; |
75 | 77 | private final EndpointConfig endpointConfig; |
76 | 78 | |
79 | private final List<Extension> negotiatedExtensions; | |
77 | 80 | private final String subProtocol; |
78 | 81 | private final Map<String,String> pathParameters; |
79 | 82 | private final boolean secure; |
104 | 107 | * |
105 | 108 | * @param localEndpoint |
106 | 109 | * @param wsRemoteEndpoint |
110 | * @param negotiatedExtensions | |
107 | 111 | * @throws DeploymentException |
108 | 112 | */ |
109 | 113 | public WsSession(Endpoint localEndpoint, |
111 | 115 | WsWebSocketContainer wsWebSocketContainer, |
112 | 116 | URI requestUri, Map<String,List<String>> requestParameterMap, |
113 | 117 | String queryString, Principal userPrincipal, String httpSessionId, |
114 | String subProtocol, Map<String,String> pathParameters, | |
118 | List<Extension> negotiatedExtensions, String subProtocol, Map<String,String> pathParameters, | |
115 | 119 | boolean secure, EndpointConfig endpointConfig) throws DeploymentException { |
116 | 120 | this.localEndpoint = localEndpoint; |
117 | 121 | this.wsRemoteEndpoint = wsRemoteEndpoint; |
137 | 141 | this.queryString = queryString; |
138 | 142 | this.userPrincipal = userPrincipal; |
139 | 143 | this.httpSessionId = httpSessionId; |
144 | this.negotiatedExtensions = negotiatedExtensions; | |
140 | 145 | if (subProtocol == null) { |
141 | 146 | this.subProtocol = ""; |
142 | 147 | } else { |
159 | 164 | } |
160 | 165 | |
161 | 166 | |
167 | @Override | |
168 | public void addMessageHandler(MessageHandler listener) { | |
169 | Class<?> target = Util.getMessageType(listener); | |
170 | doAddMessageHandler(target, listener); | |
171 | } | |
172 | ||
173 | ||
174 | @Override | |
175 | public <T> void addMessageHandler(Class<T> clazz, Partial<T> handler) | |
176 | throws IllegalStateException { | |
177 | doAddMessageHandler(clazz, handler); | |
178 | } | |
179 | ||
180 | ||
181 | @Override | |
182 | public <T> void addMessageHandler(Class<T> clazz, Whole<T> handler) | |
183 | throws IllegalStateException { | |
184 | doAddMessageHandler(clazz, handler); | |
185 | } | |
186 | ||
187 | ||
162 | 188 | @SuppressWarnings("unchecked") |
163 | @Override | |
164 | public void addMessageHandler(MessageHandler listener) { | |
165 | ||
189 | private void doAddMessageHandler(Class<?> target, MessageHandler listener) { | |
166 | 190 | checkState(); |
167 | 191 | |
168 | 192 | // Message handlers that require decoders may map to text messages, |
176 | 200 | // just as easily. |
177 | 201 | |
178 | 202 | Set<MessageHandlerResult> mhResults = |
179 | Util.getMessageHandlers(listener, endpointConfig, this); | |
203 | Util.getMessageHandlers(target, listener, endpointConfig, this); | |
180 | 204 | |
181 | 205 | for (MessageHandlerResult mhResult : mhResults) { |
182 | 206 | switch (mhResult.getType()) { |
301 | 325 | @Override |
302 | 326 | public List<Extension> getNegotiatedExtensions() { |
303 | 327 | checkState(); |
304 | return Collections.emptyList(); | |
328 | return negotiatedExtensions; | |
305 | 329 | } |
306 | 330 | |
307 | 331 |
272 | 272 | |
273 | 273 | ByteBuffer response; |
274 | 274 | String subProtocol; |
275 | boolean success = false; | |
275 | 276 | try { |
276 | 277 | fConnect.get(timeout, TimeUnit.MILLISECONDS); |
277 | 278 | |
309 | 310 | throw new DeploymentException( |
310 | 311 | sm.getString("Sec-WebSocket-Protocol")); |
311 | 312 | } |
313 | success = true; | |
312 | 314 | } catch (ExecutionException | InterruptedException | SSLException | |
313 | 315 | EOFException | TimeoutException e) { |
314 | 316 | throw new DeploymentException( |
315 | 317 | sm.getString("wsWebSocketContainer.httpRequestFailed"), e); |
318 | } finally { | |
319 | if (!success) { | |
320 | channel.close(); | |
321 | } | |
316 | 322 | } |
317 | 323 | |
318 | 324 | // Switch to WebSocket |
319 | 325 | WsRemoteEndpointImplClient wsRemoteEndpointClient = new WsRemoteEndpointImplClient(channel); |
320 | 326 | |
321 | 327 | WsSession wsSession = new WsSession(endpoint, wsRemoteEndpointClient, |
322 | this, null, null, null, null, null, subProtocol, | |
323 | Collections.<String, String> emptyMap(), secure, | |
328 | this, null, null, null, null, null, Collections.<Extension>emptyList(), | |
329 | subProtocol, Collections.<String,String>emptyMap(), secure, | |
324 | 330 | clientEndpointConfiguration); |
325 | 331 | |
326 | 332 | WsFrameClient wsFrameClient = new WsFrameClient(response, channel, |
35 | 35 | setPojo(pojo); |
36 | 36 | setMethodMapping( |
37 | 37 | new PojoMethodMapping(pojo.getClass(), decoders, null)); |
38 | setPathParameters(Collections.<String, String> emptyMap()); | |
38 | setPathParameters(Collections.<String,String>emptyMap()); | |
39 | 39 | } |
40 | 40 | |
41 | 41 | @Override |
24 | 24 | * ByteBuffer specific concrete implementation for handling partial messages. |
25 | 25 | */ |
26 | 26 | public class PojoMessageHandlerPartialBinary |
27 | extends PojoMessageHandlerPartialBase<ByteBuffer>{ | |
27 | extends PojoMessageHandlerPartialBase<ByteBuffer> { | |
28 | 28 | |
29 | 29 | public PojoMessageHandlerPartialBinary(Object pojo, Method method, |
30 | 30 | Session session, Object[] params, int indexPayload, boolean convert, |
23 | 23 | * Text specific concrete implementation for handling partial messages. |
24 | 24 | */ |
25 | 25 | public class PojoMessageHandlerPartialText |
26 | extends PojoMessageHandlerPartialBase<String>{ | |
26 | extends PojoMessageHandlerPartialBase<String> { | |
27 | 27 | |
28 | 28 | public PojoMessageHandlerPartialText(Object pojo, Method method, |
29 | 29 | Session session, Object[] params, int indexPayload, boolean convert, |
21 | 21 | serverContainer.pojoDeploy=POJO class [{0}] deploying to path [{1}] in ServletContext [{2}] |
22 | 22 | serverContainer.servletContextMismatch=Attempted to register a POJO annotated for WebSocket at path [{0}] in the ServletContext with context path [{1}] when the WebSocket ServerContainer is allocated to the ServletContext with context path [{2}] |
23 | 23 | serverContainer.servletContextMissing=No ServletContext was specified |
24 | serverContainer.threadGroupNotDestroyed=Unable to destroy WebSocket thread group [{0}] as some threads were still running when the web application was stopped | |
24 | serverContainer.threadGroupNotDestroyed=Unable to destroy WebSocket thread group [{0}] as [{1}] threads were still running when the web application was stopped. The thread group will be destroyed once the threads terminate. | |
25 | 25 | |
26 | 26 | uriTemplate.duplicateParameter=The parameter [{0}] appears more than once in the path which is not permitted |
27 | 27 | uriTemplate.emptySegment=The path [{0}] contains one or more empty segments which are is not permitted |
17 | 17 | |
18 | 18 | import java.io.IOException; |
19 | 19 | import java.nio.charset.StandardCharsets; |
20 | import java.security.MessageDigest; | |
21 | import java.security.NoSuchAlgorithmException; | |
22 | 20 | import java.util.ArrayList; |
21 | import java.util.Collections; | |
23 | 22 | import java.util.Enumeration; |
24 | 23 | import java.util.LinkedHashMap; |
25 | 24 | import java.util.List; |
26 | 25 | import java.util.Map; |
27 | 26 | import java.util.Map.Entry; |
28 | import java.util.Queue; | |
29 | import java.util.concurrent.ConcurrentLinkedQueue; | |
30 | 27 | |
31 | 28 | import javax.servlet.ServletException; |
32 | 29 | import javax.servlet.ServletRequest; |
39 | 36 | import javax.websocket.server.ServerEndpointConfig; |
40 | 37 | |
41 | 38 | import org.apache.tomcat.util.codec.binary.Base64; |
39 | import org.apache.tomcat.util.security.ConcurrentMessageDigest; | |
42 | 40 | import org.apache.tomcat.websocket.Constants; |
43 | 41 | import org.apache.tomcat.websocket.Transformation; |
44 | 42 | import org.apache.tomcat.websocket.TransformationFactory; |
51 | 49 | private static final byte[] WS_ACCEPT = |
52 | 50 | "258EAFA5-E914-47DA-95CA-C5AB0DC85B11".getBytes( |
53 | 51 | StandardCharsets.ISO_8859_1); |
54 | private static final Queue<MessageDigest> sha1Helpers = | |
55 | new ConcurrentLinkedQueue<>(); | |
56 | 52 | |
57 | 53 | private UpgradeUtil() { |
58 | 54 | // Utility class. Hide default constructor. |
126 | 122 | while (extHeaders.hasMoreElements()) { |
127 | 123 | Util.parseExtensionHeader(extensionsRequested, extHeaders.nextElement()); |
128 | 124 | } |
129 | List<Extension> negotiatedExtensions = sec.getConfigurator().getNegotiatedExtensions( | |
125 | // Negotiation phase 1. By default this simply filters out the | |
126 | // extensions that the server does not support but applications could | |
127 | // use a custom configurator to do more than this. | |
128 | List<Extension> negotiatedExtensionsPhase1 = sec.getConfigurator().getNegotiatedExtensions( | |
130 | 129 | Constants.INSTALLED_EXTENSIONS, extensionsRequested); |
131 | 130 | |
132 | // Create the Transformations that will be applied to this connection | |
133 | List<Transformation> transformations = createTransformations(negotiatedExtensions); | |
131 | // Negotiation phase 2. Create the Transformations that will be applied | |
132 | // to this connection. Note than an extension may be dropped at this | |
133 | // point if the client has requested a configuration that the server is | |
134 | // unable to support. | |
135 | List<Transformation> transformations = createTransformations(negotiatedExtensionsPhase1); | |
136 | ||
137 | List<Extension> negotiatedExtensionsPhase2; | |
138 | if (transformations.isEmpty()) { | |
139 | negotiatedExtensionsPhase2 = Collections.emptyList(); | |
140 | } else { | |
141 | negotiatedExtensionsPhase2 = new ArrayList<>(transformations.size()); | |
142 | for (Transformation t : transformations) { | |
143 | negotiatedExtensionsPhase2.add(t.getExtensionResponse()); | |
144 | } | |
145 | } | |
134 | 146 | |
135 | 147 | // Build the transformation pipeline |
136 | 148 | Transformation transformation = null; |
203 | 215 | WsHttpUpgradeHandler wsHandler = |
204 | 216 | req.upgrade(WsHttpUpgradeHandler.class); |
205 | 217 | wsHandler.preInit(ep, perSessionServerEndpointConfig, sc, wsRequest, |
206 | subProtocol, transformation, pathParams, req.isSecure()); | |
218 | negotiatedExtensionsPhase2, subProtocol, transformation, pathParams, | |
219 | req.isSecure()); | |
207 | 220 | |
208 | 221 | } |
209 | 222 | |
299 | 312 | } |
300 | 313 | |
301 | 314 | |
302 | private static String getWebSocketAccept(String key) throws ServletException { | |
303 | MessageDigest sha1Helper = sha1Helpers.poll(); | |
304 | if (sha1Helper == null) { | |
305 | try { | |
306 | sha1Helper = MessageDigest.getInstance("SHA1"); | |
307 | } catch (NoSuchAlgorithmException e) { | |
308 | throw new ServletException(e); | |
309 | } | |
310 | } | |
311 | sha1Helper.reset(); | |
312 | sha1Helper.update(key.getBytes(StandardCharsets.ISO_8859_1)); | |
313 | String result = Base64.encodeBase64String(sha1Helper.digest(WS_ACCEPT)); | |
314 | sha1Helpers.add(sha1Helper); | |
315 | return result; | |
315 | private static String getWebSocketAccept(String key) { | |
316 | byte[] digest = ConcurrentMessageDigest.digestSHA1( | |
317 | key.getBytes(StandardCharsets.ISO_8859_1), WS_ACCEPT); | |
318 | return Base64.encodeBase64String(digest); | |
316 | 319 | } |
317 | 320 | } |
17 | 17 | |
18 | 18 | import java.io.EOFException; |
19 | 19 | import java.io.IOException; |
20 | import java.util.List; | |
20 | 21 | import java.util.Map; |
21 | 22 | |
22 | 23 | import javax.servlet.ReadListener; |
31 | 32 | import javax.websocket.DeploymentException; |
32 | 33 | import javax.websocket.Endpoint; |
33 | 34 | import javax.websocket.EndpointConfig; |
35 | import javax.websocket.Extension; | |
34 | 36 | |
35 | 37 | import org.apache.juli.logging.Log; |
36 | 38 | import org.apache.juli.logging.LogFactory; |
55 | 57 | private EndpointConfig endpointConfig; |
56 | 58 | private WsServerContainer webSocketContainer; |
57 | 59 | private WsHandshakeRequest handshakeRequest; |
60 | private List<Extension> negotiatedExtensions; | |
58 | 61 | private String subProtocol; |
59 | 62 | private Transformation transformation; |
60 | 63 | private Map<String,String> pathParameters; |
71 | 74 | |
72 | 75 | public void preInit(Endpoint ep, EndpointConfig endpointConfig, |
73 | 76 | WsServerContainer wsc, WsHandshakeRequest handshakeRequest, |
74 | String subProtocol, Transformation transformation, | |
75 | Map<String,String> pathParameters, boolean secure) { | |
77 | List<Extension> negotiatedExtensionsPhase2, String subProtocol, | |
78 | Transformation transformation, Map<String,String> pathParameters, | |
79 | boolean secure) { | |
76 | 80 | this.ep = ep; |
77 | 81 | this.endpointConfig = endpointConfig; |
78 | 82 | this.webSocketContainer = wsc; |
79 | 83 | this.handshakeRequest = handshakeRequest; |
84 | this.negotiatedExtensions = negotiatedExtensionsPhase2; | |
80 | 85 | this.subProtocol = subProtocol; |
81 | 86 | this.transformation = transformation; |
82 | 87 | this.pathParameters = pathParameters; |
122 | 127 | handshakeRequest.getParameterMap(), |
123 | 128 | handshakeRequest.getQueryString(), |
124 | 129 | handshakeRequest.getUserPrincipal(), httpSessionId, |
125 | subProtocol, pathParameters, secure, endpointConfig); | |
130 | negotiatedExtensions, subProtocol, pathParameters, secure, | |
131 | endpointConfig); | |
126 | 132 | WsFrameServer wsFrame = new WsFrameServer(sis, wsSession, transformation); |
127 | 133 | sos.setWriteListener(new WsWriteListener(this, wsRemoteEndpointServer)); |
128 | 134 | // WsFrame adds the necessary final transformations. Copy the |
139 | 139 | |
140 | 140 | fr.addMappingForUrlPatterns(types, true, "/*"); |
141 | 141 | |
142 | // Use a per web application executor for any threads the the WebSocket | |
142 | // Use a per web application executor for any threads that the WebSocket | |
143 | 143 | // server code needs to create. Group all of the threads under a single |
144 | 144 | // ThreadGroup. |
145 | 145 | StringBuffer threadGroupName = new StringBuffer("WebSocketServer-"); |
272 | 272 | public void destroy() { |
273 | 273 | shutdownExecutor(); |
274 | 274 | super.destroy(); |
275 | // If the executor hasn't fully shutdown it won't be possible to | |
276 | // destroy this thread group as there will still be threads running. | |
277 | // Mark the thread group as daemon one, so that it destroys itself | |
278 | // when thread count reaches zero. | |
279 | // Synchronization on threadGroup is needed, as there is a race between | |
280 | // destroy() call from termination of the last thread in thread group | |
281 | // marked as daemon versus the explicit destroy() call. | |
282 | int threadCount = threadGroup.activeCount(); | |
283 | boolean success = false; | |
275 | 284 | try { |
276 | threadGroup.destroy(); | |
277 | } catch (IllegalThreadStateException itse) { | |
278 | // If the executor hasn't fully shutdown it won't be possible to | |
279 | // destroy this thread group as there will still be threads running | |
285 | while (true) { | |
286 | int oldThreadCount = threadCount; | |
287 | synchronized (threadGroup) { | |
288 | if (threadCount > 0) { | |
289 | Thread.yield(); | |
290 | threadCount = threadGroup.activeCount(); | |
291 | } | |
292 | if (threadCount > 0 && threadCount != oldThreadCount) { | |
293 | // Value not stabilized. Retry. | |
294 | continue; | |
295 | } | |
296 | if (threadCount > 0) { | |
297 | threadGroup.setDaemon(true); | |
298 | } else { | |
299 | threadGroup.destroy(); | |
300 | success = true; | |
301 | } | |
302 | break; | |
303 | } | |
304 | } | |
305 | } catch (IllegalThreadStateException exception) { | |
306 | // Fall-through | |
307 | } | |
308 | if (!success) { | |
280 | 309 | log.warn(sm.getString("serverContainer.threadGroupNotDestroyed", |
281 | threadGroup.getName())); | |
282 | } | |
310 | threadGroup.getName(), Integer.valueOf(threadCount))); } | |
283 | 311 | } |
284 | 312 | |
285 | 313 | |
307 | 335 | // Check an exact match. Simple case as there are no templates. |
308 | 336 | ServerEndpointConfig sec = configExactMatchMap.get(path); |
309 | 337 | if (sec != null) { |
310 | return new WsMappingResult(sec, | |
311 | Collections.<String, String> emptyMap()); | |
338 | return new WsMappingResult(sec, Collections.<String, String>emptyMap()); | |
312 | 339 | } |
313 | 340 | |
314 | 341 | // No exact match. Need to look for template matches. |
1 | 1 | X-Compile-Source-JDK: @source.jdk@ |
2 | 2 | X-Compile-Target-JDK: @target.jdk@ |
3 | 3 | |
4 | Name: javax/el/ | |
4 | Name: javax/websocket/ | |
5 | 5 | Specification-Title: WebSocket |
6 | Specification-Version: 1.0 | |
6 | Specification-Version: 1.1 | |
7 | 7 | Specification-Vendor: Oracle, Inc. |
8 | Implementation-Title: javax.net.websocket | |
9 | Implementation-Version: 1.0.@websocket.revision@ | |
8 | Implementation-Title: javax.websocket | |
9 | Implementation-Version: 1.1.@websocket.revision@ | |
10 | 10 | Implementation-Vendor: Apache Software Foundation |
42 | 42 | value="${tomcat.output}/res/checkstyle/cachefile-checkstyle.xml"/> |
43 | 43 | |
44 | 44 | <!-- Block Checks --> |
45 | <!-- ~60 errors | |
46 | <module name="AvoidNestedBlocks"/> | |
47 | --> | |
45 | <module name="AvoidNestedBlocks"> | |
46 | <property name="allowInSwitchCase" value="true"/> | |
47 | </module> | |
48 | 48 | |
49 | 49 | <!-- Coding --> |
50 | 50 | <module name="IllegalInstantiation"/> |
60 | 60 | <property name="illegalPkgs" value="sun,junit.framework"/> |
61 | 61 | </module> |
62 | 62 | <module name="ImportOrder"> |
63 | <property name="groups" value="java,javax,async,jsp2,junit,org.junit,org,util"/> | |
63 | <property name="groups" value="java,javax,org.hamcrest,org.junit,org,async,jsp2,util"/> | |
64 | 64 | <property name="ordered" value="true"/> |
65 | 65 | <property name="separated" value="true"/> |
66 | 66 | <property name="option" value="above"/> |
81 | 81 | --> |
82 | 82 | <module name="EmptyForInitializerPad"/> |
83 | 83 | <module name="EmptyForIteratorPad"/> |
84 | <!-- ~ 1000 errors | |
84 | <!-- ~ 1200 errors | |
85 | 85 | <module name="OperatorWrap"> |
86 | <property name="option" value="oel"/> | |
86 | <property name="option" value="eol"/> | |
87 | 87 | </module> |
88 | 88 | --> |
89 | 89 | </module> |
34 | 34 | maven.asf.release.repo.repositoryId=apache.releases |
35 | 35 | |
36 | 36 | # Release version info |
37 | maven.asf.release.deploy.version=8.0.12 | |
37 | maven.asf.release.deploy.version=8.0.14 | |
38 | 38 | |
39 | 39 | #Where do we load the libraries from |
40 | 40 | tomcat.lib.path=../../output/build/lib |
36 | 36 | import org.apache.catalina.startup.TesterServlet; |
37 | 37 | import org.apache.catalina.startup.Tomcat; |
38 | 38 | import org.apache.catalina.startup.TomcatBaseTest; |
39 | import org.apache.catalina.util.ConcurrentMessageDigest; | |
40 | import org.apache.catalina.util.MD5Encoder; | |
41 | 39 | import org.apache.tomcat.util.buf.ByteChunk; |
42 | 40 | import org.apache.tomcat.util.descriptor.web.LoginConfig; |
43 | 41 | import org.apache.tomcat.util.descriptor.web.SecurityCollection; |
44 | 42 | import org.apache.tomcat.util.descriptor.web.SecurityConstraint; |
43 | import org.apache.tomcat.util.security.ConcurrentMessageDigest; | |
44 | import org.apache.tomcat.util.security.MD5Encoder; | |
45 | 45 | |
46 | 46 | public class TestDigestAuthenticator extends TomcatBaseTest { |
47 | 47 |
29 | 29 | import org.apache.catalina.startup.TesterServlet; |
30 | 30 | import org.apache.catalina.startup.Tomcat; |
31 | 31 | import org.apache.catalina.startup.TomcatBaseTest; |
32 | import org.apache.catalina.util.ConcurrentMessageDigest; | |
33 | import org.apache.catalina.util.MD5Encoder; | |
34 | 32 | import org.apache.tomcat.util.buf.ByteChunk; |
35 | 33 | import org.apache.tomcat.util.descriptor.web.LoginConfig; |
36 | 34 | import org.apache.tomcat.util.descriptor.web.SecurityCollection; |
37 | 35 | import org.apache.tomcat.util.descriptor.web.SecurityConstraint; |
36 | import org.apache.tomcat.util.security.ConcurrentMessageDigest; | |
37 | import org.apache.tomcat.util.security.MD5Encoder; | |
38 | 38 | |
39 | 39 | /** |
40 | 40 | * Test DigestAuthenticator and NonLoginAuthenticator when a |
30 | 30 | import org.apache.catalina.core.StandardContext; |
31 | 31 | import org.apache.catalina.filters.TesterHttpServletResponse; |
32 | 32 | import org.apache.catalina.startup.TesterMapRealm; |
33 | import org.apache.catalina.util.ConcurrentMessageDigest; | |
34 | import org.apache.catalina.util.MD5Encoder; | |
35 | 33 | import org.apache.tomcat.util.descriptor.web.LoginConfig; |
34 | import org.apache.tomcat.util.security.ConcurrentMessageDigest; | |
35 | import org.apache.tomcat.util.security.MD5Encoder; | |
36 | 36 | |
37 | 37 | public class TesterDigestAuthenticatorPerformance { |
38 | 38 |
660 | 660 | writer.append("Content-Disposition: form-data; name=\"part\"\r\n"); |
661 | 661 | writer.append("Content-Type: text/plain; charset=UTF-8\r\n"); |
662 | 662 | writer.append("\r\n"); |
663 | writer.append("��").append("\r\n"); | |
663 | writer.append("äö").append("\r\n"); | |
664 | 664 | writer.flush(); |
665 | 665 | |
666 | 666 | writer.append("\r\n"); |
681 | 681 | while ((line = reader.readLine()) != null) { |
682 | 682 | response.add(line); |
683 | 683 | } |
684 | assertTrue(response.contains("Part ��")); | |
684 | assertTrue(response.contains("Part äö")); | |
685 | 685 | } |
686 | 686 | } else { |
687 | 687 | fail("OK status was expected: " + status); |
18 | 18 | import java.io.File; |
19 | 19 | import java.io.IOException; |
20 | 20 | import java.io.PrintWriter; |
21 | import java.util.Arrays; | |
21 | 22 | import java.util.HashSet; |
22 | 23 | import java.util.Set; |
23 | 24 | |
41 | 42 | import javax.servlet.http.HttpServletRequest; |
42 | 43 | import javax.servlet.http.HttpServletResponse; |
43 | 44 | |
45 | import org.hamcrest.CoreMatchers; | |
46 | ||
44 | 47 | import static org.junit.Assert.assertEquals; |
45 | 48 | import static org.junit.Assert.assertFalse; |
46 | 49 | import static org.junit.Assert.assertNotSame; |
69 | 72 | import org.apache.tomcat.util.descriptor.web.FilterDef; |
70 | 73 | import org.apache.tomcat.util.descriptor.web.FilterMap; |
71 | 74 | import org.apache.tomcat.util.descriptor.web.LoginConfig; |
75 | ||
72 | 76 | |
73 | 77 | public class TestStandardContext extends TomcatBaseTest { |
74 | 78 | |
944 | 948 | |
945 | 949 | Assert.assertNull(realPath); |
946 | 950 | } |
951 | ||
952 | @Test | |
953 | public void testBug56903() { | |
954 | Context context = new StandardContext(); | |
955 | ||
956 | context.setResourceOnlyServlets("a,b,c"); | |
957 | Assert.assertThat(Arrays.asList(context.getResourceOnlyServlets().split(",")), | |
958 | CoreMatchers.hasItems("a", "b", "c")); | |
959 | } | |
947 | 960 | } |
18 | 18 | import java.beans.PropertyChangeListener; |
19 | 19 | import java.io.File; |
20 | 20 | import java.net.URL; |
21 | import java.nio.charset.Charset; | |
22 | import java.nio.charset.StandardCharsets; | |
21 | 23 | import java.util.ArrayList; |
22 | 24 | import java.util.List; |
23 | 25 | import java.util.Locale; |
1220 | 1222 | public Object getNamingToken() { |
1221 | 1223 | return null; |
1222 | 1224 | } |
1225 | ||
1226 | @Override | |
1227 | public void setUseRfc6265(boolean useRfc6265) { /* NO-OP */ } | |
1228 | ||
1229 | @Override | |
1230 | public boolean getUseRfc6265() {return false; } | |
1231 | ||
1232 | @Override | |
1233 | public void setCookieEncoding(String encoding) { /* NO-OP */ } | |
1234 | ||
1235 | @Override | |
1236 | public String getCookieEncoding() { return "UTF-8"; } | |
1237 | ||
1238 | @Override | |
1239 | public Charset getCookieEncodingCharset() { return StandardCharsets.UTF_8; } | |
1223 | 1240 | } |
79 | 79 | tomcat.start(); |
80 | 80 | try { |
81 | 81 | // VERIFY EXCLUDED RESPONSE STATUS CODES |
82 | { | |
83 | int[] excludedResponseStatusCodes = expiresFilter.getExcludedResponseStatusCodesAsInts(); | |
84 | Assert.assertEquals(2, excludedResponseStatusCodes.length); | |
85 | Assert.assertEquals(304, excludedResponseStatusCodes[0]); | |
86 | Assert.assertEquals(503, excludedResponseStatusCodes[1]); | |
87 | } | |
82 | int[] excludedResponseStatusCodes = expiresFilter.getExcludedResponseStatusCodesAsInts(); | |
83 | Assert.assertEquals(2, excludedResponseStatusCodes.length); | |
84 | Assert.assertEquals(304, excludedResponseStatusCodes[0]); | |
85 | Assert.assertEquals(503, excludedResponseStatusCodes[1]); | |
88 | 86 | |
89 | 87 | // VERIFY DEFAULT CONFIGURATION |
90 | { | |
91 | ExpiresConfiguration expiresConfiguration = expiresFilter.getDefaultExpiresConfiguration(); | |
92 | Assert.assertEquals(StartingPoint.ACCESS_TIME, | |
93 | expiresConfiguration.getStartingPoint()); | |
94 | Assert.assertEquals(1, | |
95 | expiresConfiguration.getDurations().size()); | |
96 | Assert.assertEquals(DurationUnit.MONTH, | |
97 | expiresConfiguration.getDurations().get(0).getUnit()); | |
98 | Assert.assertEquals(1, expiresConfiguration.getDurations().get( | |
99 | 0).getAmount()); | |
100 | } | |
88 | ExpiresConfiguration expiresConfigurationDefault = | |
89 | expiresFilter.getDefaultExpiresConfiguration(); | |
90 | Assert.assertEquals(StartingPoint.ACCESS_TIME, | |
91 | expiresConfigurationDefault.getStartingPoint()); | |
92 | Assert.assertEquals(1, expiresConfigurationDefault.getDurations().size()); | |
93 | Assert.assertEquals(DurationUnit.MONTH, | |
94 | expiresConfigurationDefault.getDurations().get(0).getUnit()); | |
95 | Assert.assertEquals(1, expiresConfigurationDefault.getDurations().get(0).getAmount()); | |
101 | 96 | |
102 | 97 | // VERIFY TEXT/HTML |
103 | { | |
104 | ExpiresConfiguration expiresConfiguration = expiresFilter.getExpiresConfigurationByContentType().get( | |
105 | "text/html"); | |
106 | Assert.assertEquals(StartingPoint.ACCESS_TIME, | |
107 | expiresConfiguration.getStartingPoint()); | |
108 | ||
109 | Assert.assertEquals(3, | |
110 | expiresConfiguration.getDurations().size()); | |
111 | ||
112 | Duration oneMonth = expiresConfiguration.getDurations().get(0); | |
113 | Assert.assertEquals(DurationUnit.MONTH, oneMonth.getUnit()); | |
114 | Assert.assertEquals(1, oneMonth.getAmount()); | |
115 | ||
116 | Duration fifteenDays = expiresConfiguration.getDurations().get( | |
117 | 1); | |
118 | Assert.assertEquals(DurationUnit.DAY, fifteenDays.getUnit()); | |
119 | Assert.assertEquals(15, fifteenDays.getAmount()); | |
120 | ||
121 | Duration twoHours = expiresConfiguration.getDurations().get(2); | |
122 | Assert.assertEquals(DurationUnit.HOUR, twoHours.getUnit()); | |
123 | Assert.assertEquals(2, twoHours.getAmount()); | |
124 | } | |
98 | ExpiresConfiguration expiresConfigurationTextHtml = | |
99 | expiresFilter.getExpiresConfigurationByContentType().get("text/html"); | |
100 | Assert.assertEquals(StartingPoint.ACCESS_TIME, | |
101 | expiresConfigurationTextHtml.getStartingPoint()); | |
102 | ||
103 | Assert.assertEquals(3, expiresConfigurationTextHtml.getDurations().size()); | |
104 | ||
105 | Duration oneMonth = expiresConfigurationTextHtml.getDurations().get(0); | |
106 | Assert.assertEquals(DurationUnit.MONTH, oneMonth.getUnit()); | |
107 | Assert.assertEquals(1, oneMonth.getAmount()); | |
108 | ||
109 | Duration fifteenDays = expiresConfigurationTextHtml.getDurations().get(1); | |
110 | Assert.assertEquals(DurationUnit.DAY, fifteenDays.getUnit()); | |
111 | Assert.assertEquals(15, fifteenDays.getAmount()); | |
112 | ||
113 | Duration twoHours = expiresConfigurationTextHtml.getDurations().get(2); | |
114 | Assert.assertEquals(DurationUnit.HOUR, twoHours.getUnit()); | |
115 | Assert.assertEquals(2, twoHours.getAmount()); | |
116 | ||
125 | 117 | // VERIFY IMAGE/GIF |
126 | { | |
127 | ExpiresConfiguration expiresConfiguration = expiresFilter.getExpiresConfigurationByContentType().get( | |
128 | "image/gif"); | |
129 | Assert.assertEquals(StartingPoint.LAST_MODIFICATION_TIME, | |
130 | expiresConfiguration.getStartingPoint()); | |
131 | ||
132 | Assert.assertEquals(2, | |
133 | expiresConfiguration.getDurations().size()); | |
134 | ||
135 | Duration fiveHours = expiresConfiguration.getDurations().get(0); | |
136 | Assert.assertEquals(DurationUnit.HOUR, fiveHours.getUnit()); | |
137 | Assert.assertEquals(5, fiveHours.getAmount()); | |
138 | ||
139 | Duration threeMinutes = expiresConfiguration.getDurations().get( | |
140 | 1); | |
141 | Assert.assertEquals(DurationUnit.MINUTE, threeMinutes.getUnit()); | |
142 | Assert.assertEquals(3, threeMinutes.getAmount()); | |
143 | ||
144 | } | |
118 | ExpiresConfiguration expiresConfigurationImageGif = | |
119 | expiresFilter.getExpiresConfigurationByContentType().get("image/gif"); | |
120 | Assert.assertEquals(StartingPoint.LAST_MODIFICATION_TIME, | |
121 | expiresConfigurationImageGif.getStartingPoint()); | |
122 | ||
123 | Assert.assertEquals(2, expiresConfigurationImageGif.getDurations().size()); | |
124 | ||
125 | Duration fiveHours = expiresConfigurationImageGif.getDurations().get(0); | |
126 | Assert.assertEquals(DurationUnit.HOUR, fiveHours.getUnit()); | |
127 | Assert.assertEquals(5, fiveHours.getAmount()); | |
128 | ||
129 | Duration threeMinutes = expiresConfigurationImageGif.getDurations().get(1); | |
130 | Assert.assertEquals(DurationUnit.MINUTE, threeMinutes.getUnit()); | |
131 | Assert.assertEquals(3, threeMinutes.getAmount()); | |
132 | ||
145 | 133 | // VERIFY IMAGE/JPG |
146 | { | |
147 | ExpiresConfiguration expiresConfiguration = expiresFilter.getExpiresConfigurationByContentType().get( | |
148 | "image/jpg"); | |
149 | Assert.assertEquals(StartingPoint.ACCESS_TIME, | |
150 | expiresConfiguration.getStartingPoint()); | |
151 | ||
152 | Assert.assertEquals(1, | |
153 | expiresConfiguration.getDurations().size()); | |
154 | ||
155 | Duration tenThousandSeconds = expiresConfiguration.getDurations().get( | |
156 | 0); | |
157 | Assert.assertEquals(DurationUnit.SECOND, | |
158 | tenThousandSeconds.getUnit()); | |
159 | Assert.assertEquals(10000, tenThousandSeconds.getAmount()); | |
160 | ||
161 | } | |
134 | ExpiresConfiguration expiresConfigurationImageJpg = | |
135 | expiresFilter.getExpiresConfigurationByContentType().get("image/jpg"); | |
136 | Assert.assertEquals(StartingPoint.ACCESS_TIME, | |
137 | expiresConfigurationImageJpg.getStartingPoint()); | |
138 | ||
139 | Assert.assertEquals(1, expiresConfigurationImageJpg.getDurations().size()); | |
140 | ||
141 | Duration tenThousandSeconds = expiresConfigurationImageJpg.getDurations().get(0); | |
142 | Assert.assertEquals(DurationUnit.SECOND, tenThousandSeconds.getUnit()); | |
143 | Assert.assertEquals(10000, tenThousandSeconds.getAmount()); | |
144 | ||
162 | 145 | // VERIFY VIDEO/MPEG |
163 | { | |
164 | ExpiresConfiguration expiresConfiguration = expiresFilter.getExpiresConfigurationByContentType().get( | |
165 | "video/mpeg"); | |
166 | Assert.assertEquals(StartingPoint.LAST_MODIFICATION_TIME, | |
167 | expiresConfiguration.getStartingPoint()); | |
168 | ||
169 | Assert.assertEquals(1, | |
170 | expiresConfiguration.getDurations().size()); | |
171 | ||
172 | Duration twentyThousandSeconds = expiresConfiguration.getDurations().get( | |
173 | 0); | |
174 | Assert.assertEquals(DurationUnit.SECOND, | |
175 | twentyThousandSeconds.getUnit()); | |
176 | Assert.assertEquals(20000, twentyThousandSeconds.getAmount()); | |
177 | } | |
146 | ExpiresConfiguration expiresConfiguration = | |
147 | expiresFilter.getExpiresConfigurationByContentType().get("video/mpeg"); | |
148 | Assert.assertEquals(StartingPoint.LAST_MODIFICATION_TIME, | |
149 | expiresConfiguration.getStartingPoint()); | |
150 | ||
151 | Assert.assertEquals(1, expiresConfiguration.getDurations().size()); | |
152 | ||
153 | Duration twentyThousandSeconds = expiresConfiguration.getDurations().get(0); | |
154 | Assert.assertEquals(DurationUnit.SECOND, twentyThousandSeconds.getUnit()); | |
155 | Assert.assertEquals(20000, twentyThousandSeconds.getAmount()); | |
178 | 156 | } finally { |
179 | 157 | tomcat.stop(); |
180 | 158 | } |
76 | 76 | |
77 | 77 | /* |
78 | 78 | * Get the set of current threads as an array. |
79 | * Copied from WebappClassLoader | |
79 | * Copied from WebappClassLoaderBase | |
80 | 80 | */ |
81 | 81 | private Thread[] getThreads() { |
82 | 82 | // Get the current thread group |
59 | 59 | LogValidationFilter f = new LogValidationFilter( |
60 | 60 | "The web application [] created a ThreadLocal with key of"); |
61 | 61 | LogManager.getLogManager().getLogger( |
62 | "org.apache.catalina.loader.WebappClassLoader").setFilter(f); | |
62 | "org.apache.catalina.loader.WebappClassLoaderBase").setFilter(f); | |
63 | 63 | |
64 | 64 | // Need to force loading of all web application classes via the web |
65 | 65 | // application class loader |
113 | 113 | LogValidationFilter f = new LogValidationFilter( |
114 | 114 | "The web application [] created a ThreadLocal with key of"); |
115 | 115 | LogManager.getLogManager().getLogger( |
116 | "org.apache.catalina.loader.WebappClassLoader").setFilter(f); | |
116 | "org.apache.catalina.loader.WebappClassLoaderBase").setFilter(f); | |
117 | 117 | |
118 | 118 | // Need to force loading of all web application classes via the web |
119 | 119 | // application class loader |
79 | 79 | Assert.assertTrue(result.contains("OK")); |
80 | 80 | } |
81 | 81 | |
82 | @Test | |
83 | public void testIncludeThrowsIOException() throws Exception { | |
84 | Tomcat tomcat = getTomcatInstance(); | |
85 | ||
86 | File appDir = new File("test/webapp"); | |
87 | tomcat.addWebapp(null, "/test", appDir.getAbsolutePath()); | |
88 | ||
89 | tomcat.start(); | |
90 | ||
91 | ByteChunk res = new ByteChunk(); | |
92 | ||
93 | int rc = getUrl("http://localhost:" + getPort() + "/test/jsp/pageContext1.jsp", res, null); | |
94 | ||
95 | Assert.assertEquals(HttpServletResponse.SC_OK, rc); | |
96 | ||
97 | String body = res.toString(); | |
98 | Assert.assertTrue(body.contains("OK")); | |
99 | Assert.assertFalse(body.contains("FAILED")); | |
100 | ||
101 | res = new ByteChunk(); | |
102 | ||
103 | rc = getUrl("http://localhost:" + getPort() + "/test/jsp/pageContext1.jsp?flush=true", res, | |
104 | null); | |
105 | ||
106 | Assert.assertEquals(HttpServletResponse.SC_OK, rc); | |
107 | ||
108 | body = res.toString(); | |
109 | Assert.assertTrue(body.contains("Flush")); | |
110 | Assert.assertTrue(body.contains("OK")); | |
111 | Assert.assertFalse(body.contains("FAILED")); | |
112 | } | |
113 | ||
82 | 114 | public static class Bug56010 extends HttpServlet { |
83 | 115 | |
84 | 116 | private static final long serialVersionUID = 1L; |
42 | 42 | // Get dfc.cache.cache field |
43 | 43 | Object dfcCache; |
44 | 44 | Field dfcCacheArray; |
45 | { | |
46 | Field dfcCacheField = dfc.getClass().getDeclaredField("cache"); | |
47 | dfcCacheField.setAccessible(true); | |
48 | dfcCache = dfcCacheField.get(dfc); | |
49 | dfcCacheArray = dfcCache.getClass().getDeclaredField("cache"); | |
50 | dfcCacheArray.setAccessible(true); | |
51 | } | |
45 | Field dfcCacheField = dfc.getClass().getDeclaredField("cache"); | |
46 | dfcCacheField.setAccessible(true); | |
47 | dfcCache = dfcCacheField.get(dfc); | |
48 | dfcCacheArray = dfcCache.getClass().getDeclaredField("cache"); | |
49 | dfcCacheArray.setAccessible(true); | |
52 | 50 | |
53 | 51 | // Create an array to hold the expected values |
54 | 52 | String[] expected = new String[cacheSize]; |
0 | /* | |
1 | * Licensed to the Apache Software Foundation (ASF) under one or more | |
2 | * contributor license agreements. See the NOTICE file distributed with | |
3 | * this work for additional information regarding copyright ownership. | |
4 | * The ASF licenses this file to You under the Apache License, Version 2.0 | |
5 | * (the "License"); you may not use this file except in compliance with | |
6 | * the License. You may obtain a copy of the License at | |
7 | * | |
8 | * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
15 | */ | |
16 | package org.apache.tomcat.util.bcel; | |
17 | ||
18 | import java.io.File; | |
19 | import java.io.IOException; | |
20 | import java.io.InputStream; | |
21 | import java.net.URL; | |
22 | import java.util.HashSet; | |
23 | import java.util.Locale; | |
24 | import java.util.Set; | |
25 | ||
26 | import org.junit.Test; | |
27 | ||
28 | import org.apache.tomcat.util.bcel.classfile.ClassParser; | |
29 | import org.apache.tomcat.util.scan.Jar; | |
30 | import org.apache.tomcat.util.scan.JarFactory; | |
31 | ||
32 | public class TesterPerformance { | |
33 | ||
34 | private static final String JAR_LOCATION = "/tmp/jira-libs"; | |
35 | ||
36 | @Test | |
37 | public void testClassParserPerformance() throws IOException { | |
38 | File libDir = new File(JAR_LOCATION); | |
39 | String[] libs = libDir.list(); | |
40 | ||
41 | Set<URL> jarURLs = new HashSet<>(); | |
42 | ||
43 | for (String lib : libs) { | |
44 | if (!lib.toLowerCase(Locale.ENGLISH).endsWith(".jar")) { | |
45 | continue; | |
46 | } | |
47 | jarURLs.add(new URL("jar:" + new File (libDir, lib).toURI().toURL().toExternalForm() + "!/")); | |
48 | } | |
49 | ||
50 | long duration = 0; | |
51 | ||
52 | for (URL jarURL : jarURLs) { | |
53 | Jar jar = JarFactory.newInstance(jarURL); | |
54 | jar.nextEntry(); | |
55 | String jarEntryName = jar.getEntryName(); | |
56 | while (jarEntryName != null) { | |
57 | if (jarEntryName.endsWith(".class")) { | |
58 | InputStream is = jar.getEntryInputStream(); | |
59 | long start = System.nanoTime(); | |
60 | ClassParser cp = new ClassParser(is); | |
61 | cp.parse(); | |
62 | duration += System.nanoTime() - start; | |
63 | } | |
64 | jar.nextEntry(); | |
65 | jarEntryName = jar.getEntryName(); | |
66 | } | |
67 | } | |
68 | ||
69 | System.out.println("ClassParser performance test took: " + duration + "ns"); | |
70 | } | |
71 | } |
16 | 16 | |
17 | 17 | package org.apache.tomcat.util.buf; |
18 | 18 | |
19 | import static org.junit.Assert.assertEquals; | |
19 | import java.nio.charset.StandardCharsets; | |
20 | 20 | |
21 | import org.junit.Assert; | |
21 | 22 | import org.junit.Test; |
22 | 23 | |
23 | 24 | /** |
25 | 26 | */ |
26 | 27 | public class TestHexUtils { |
27 | 28 | |
29 | private static final String TEST01_STRING = "Hello World"; | |
30 | private static final byte[] TEST01_BYTES = TEST01_STRING.getBytes(StandardCharsets.UTF_8); | |
31 | private static final String TEST02_STRING = "foo"; | |
32 | private static final byte[] TEST02_BYTES = TEST02_STRING.getBytes(StandardCharsets.UTF_8); | |
33 | ||
28 | 34 | @Test |
29 | 35 | public void testGetDec() { |
30 | assertEquals(0, HexUtils.getDec('0')); | |
31 | assertEquals(9, HexUtils.getDec('9')); | |
32 | assertEquals(10, HexUtils.getDec('a')); | |
33 | assertEquals(15, HexUtils.getDec('f')); | |
34 | assertEquals(10, HexUtils.getDec('A')); | |
35 | assertEquals(15, HexUtils.getDec('F')); | |
36 | assertEquals(-1, HexUtils.getDec(0)); | |
37 | assertEquals(-1, HexUtils.getDec('Z')); | |
38 | assertEquals(-1, HexUtils.getDec(255)); | |
39 | assertEquals(-1, HexUtils.getDec(-60)); | |
36 | Assert.assertEquals(0, HexUtils.getDec('0')); | |
37 | Assert.assertEquals(9, HexUtils.getDec('9')); | |
38 | Assert.assertEquals(10, HexUtils.getDec('a')); | |
39 | Assert.assertEquals(15, HexUtils.getDec('f')); | |
40 | Assert.assertEquals(10, HexUtils.getDec('A')); | |
41 | Assert.assertEquals(15, HexUtils.getDec('F')); | |
42 | Assert.assertEquals(-1, HexUtils.getDec(0)); | |
43 | Assert.assertEquals(-1, HexUtils.getDec('Z')); | |
44 | Assert.assertEquals(-1, HexUtils.getDec(255)); | |
45 | Assert.assertEquals(-1, HexUtils.getDec(-60)); | |
40 | 46 | } |
41 | } | |
47 | ||
48 | @Test | |
49 | public void testRoundTrip01() { | |
50 | Assert.assertArrayEquals(TEST01_STRING, TEST01_BYTES, | |
51 | HexUtils.fromHexString(HexUtils.toHexString(TEST01_BYTES))); | |
52 | } | |
53 | ||
54 | @Test | |
55 | public void testRoundTrip02() { | |
56 | Assert.assertArrayEquals(TEST02_STRING, TEST02_BYTES, | |
57 | HexUtils.fromHexString(HexUtils.toHexString(TEST02_BYTES))); | |
58 | } | |
59 | }⏎ |
23 | 23 | import org.junit.Assert; |
24 | 24 | import org.junit.Test; |
25 | 25 | |
26 | import org.apache.tomcat.util.buf.MessageBytes; | |
27 | ||
26 | 28 | public class TestCookies { |
27 | private Cookie FOO = new Cookie("foo", "bar"); | |
28 | private Cookie BAR = new Cookie("bar", "rab"); | |
29 | private Cookie A = new Cookie("a", "b"); | |
30 | ||
31 | @Test | |
32 | public void testBasicCookie() { | |
33 | test("foo=bar; a=b", FOO, A); | |
34 | test("foo=bar;a=b", FOO, A); | |
35 | test("foo=bar;a=b;", FOO, A); | |
36 | test("foo=bar;a=b; ", FOO, A); | |
37 | test("foo=bar;a=b; ;", FOO, A); | |
38 | } | |
39 | ||
40 | @Test | |
41 | public void testNameOnlyAreDropped() { | |
42 | test("foo=;a=b; ;", A); | |
43 | test("foo;a=b; ;", A); | |
44 | test("foo;a=b;bar", A); | |
45 | test("foo;a=b;bar;", A); | |
46 | test("foo;a=b;bar ", A); | |
47 | test("foo;a=b;bar ;", A); | |
29 | private final Cookie FOO = new Cookie("foo", "bar"); | |
30 | private final Cookie FOO_EMPTY = new Cookie("foo", ""); | |
31 | private final Cookie FOO_CONTROL = new Cookie("foo", "b\u00e1r"); | |
32 | private final Cookie BAR = new Cookie("bar", "rab"); | |
33 | private final Cookie BAR_EMPTY = new Cookie("bar", ""); | |
34 | private final Cookie A = new Cookie("a", "b"); | |
35 | private final Cookie HASH_EMPTY = new Cookie("#", ""); | |
36 | private final Cookie $PORT = new Cookie("$Port", "8080"); | |
37 | ||
38 | @Test | |
39 | public void testBasicCookieOld() { | |
40 | doTestBasicCookie(false); | |
41 | } | |
42 | ||
43 | @Test | |
44 | public void testBasicCookieRfc6265() { | |
45 | doTestBasicCookie(true); | |
46 | } | |
47 | ||
48 | private void doTestBasicCookie(boolean useRfc6265) { | |
49 | test(useRfc6265, "foo=bar; a=b", FOO, A); | |
50 | test(useRfc6265, "foo=bar;a=b", FOO, A); | |
51 | test(useRfc6265, "foo=bar;a=b;", FOO, A); | |
52 | test(useRfc6265, "foo=bar;a=b; ", FOO, A); | |
53 | test(useRfc6265, "foo=bar;a=b; ;", FOO, A); | |
54 | } | |
55 | ||
56 | @Test | |
57 | public void testNameOnlyAreDroppedOld() { | |
58 | test(false, "foo=;a=b; ;", A); | |
59 | test(false, "foo;a=b; ;", A); | |
60 | test(false, "foo;a=b;bar", A); | |
61 | test(false, "foo;a=b;bar;", A); | |
62 | test(false, "foo;a=b;bar ", A); | |
63 | test(false, "foo;a=b;bar ;", A); | |
48 | 64 | |
49 | 65 | // Bug 49000 |
50 | 66 | Cookie fred = new Cookie("fred", "1"); |
51 | 67 | Cookie jim = new Cookie("jim", "2"); |
52 | 68 | Cookie george = new Cookie("george", "3"); |
53 | test("fred=1; jim=2; bob", fred, jim); | |
54 | test("fred=1; jim=2; bob; george=3", fred, jim, george); | |
55 | test("fred=1; jim=2; bob=; george=3", fred, jim, george); | |
56 | test("fred=1; jim=2; bob=", fred, jim); | |
57 | } | |
58 | ||
59 | @Test | |
60 | public void testQuotedValue() { | |
61 | test("foo=bar;a=\"b\"", FOO, A); | |
62 | test("foo=bar;a=\"b\";", FOO, A); | |
63 | } | |
64 | ||
65 | @Test | |
66 | public void testEmptyPairs() { | |
67 | test("foo;a=b; ;bar", A); | |
68 | test("foo;a=b;;bar", A); | |
69 | test("foo;a=b; ;;bar=rab", A, BAR); | |
70 | test("foo;a=b;; ;bar=rab", A, BAR); | |
71 | test("foo;a=b;;#;bar=rab", A, BAR); | |
72 | test("foo;a=b;;\\;bar=rab", A, BAR); | |
73 | } | |
74 | ||
75 | @Test | |
76 | public void testSeparatorsInValue() { | |
77 | test("a=()<>@:\\\"/[]?={}\t; foo=bar", FOO); | |
78 | } | |
79 | ||
80 | ||
81 | @Test | |
82 | public void v1TokenValue() { | |
83 | FOO.setVersion(1); | |
84 | A.setVersion(1); | |
85 | test("$Version=1; foo=bar;a=b", FOO, A); | |
86 | test("$Version=1;foo=bar;a=b; ; ", FOO, A); | |
87 | } | |
88 | ||
89 | @Test | |
90 | public void v1NameOnlyIsDropped() { | |
91 | A.setVersion(1); | |
92 | test("$Version=1;foo=;a=b; ; ", A); | |
93 | test("$Version=1;foo= ;a=b; ; ", A); | |
94 | test("$Version=1;foo;a=b; ; ", A); | |
95 | } | |
96 | ||
97 | @Test | |
98 | public void v1QuotedValue() { | |
69 | test(false, "fred=1; jim=2; bob", fred, jim); | |
70 | test(false, "fred=1; jim=2; bob; george=3", fred, jim, george); | |
71 | test(false, "fred=1; jim=2; bob=; george=3", fred, jim, george); | |
72 | test(false, "fred=1; jim=2; bob=", fred, jim); | |
73 | } | |
74 | ||
75 | @Test | |
76 | public void testNameOnlyAreDroppedRfc6265() { | |
77 | // Name only cookies are not dropped in RFC6265 | |
78 | test(true, "foo=;a=b; ;", FOO_EMPTY, A); | |
79 | test(true, "foo;a=b; ;", FOO_EMPTY, A); | |
80 | test(true, "foo;a=b;bar", FOO_EMPTY, A, BAR_EMPTY); | |
81 | test(true, "foo;a=b;bar;", FOO_EMPTY, A, BAR_EMPTY); | |
82 | test(true, "foo;a=b;bar ", FOO_EMPTY, A, BAR_EMPTY); | |
83 | test(true, "foo;a=b;bar ;", FOO_EMPTY, A, BAR_EMPTY); | |
84 | ||
85 | // Bug 49000 | |
86 | Cookie fred = new Cookie("fred", "1"); | |
87 | Cookie jim = new Cookie("jim", "2"); | |
88 | Cookie bobEmpty = new Cookie("bob", ""); | |
89 | Cookie george = new Cookie("george", "3"); | |
90 | test(true, "fred=1; jim=2; bob", fred, jim, bobEmpty); | |
91 | test(true, "fred=1; jim=2; bob; george=3", fred, jim, bobEmpty, george); | |
92 | test(true, "fred=1; jim=2; bob=; george=3", fred, jim, bobEmpty, george); | |
93 | test(true, "fred=1; jim=2; bob=", fred, jim, bobEmpty); | |
94 | } | |
95 | ||
96 | @Test | |
97 | public void testQuotedValueOld() { | |
98 | doTestQuotedValue(false); | |
99 | } | |
100 | ||
101 | @Test | |
102 | public void testQuotedValueRfc6265() { | |
103 | doTestQuotedValue(true); | |
104 | } | |
105 | ||
106 | private void doTestQuotedValue(boolean useRfc6265) { | |
107 | test(useRfc6265, "foo=bar;a=\"b\"", FOO, A); | |
108 | test(useRfc6265, "foo=bar;a=\"b\";", FOO, A); | |
109 | } | |
110 | ||
111 | @Test | |
112 | public void testEmptyPairsOld() { | |
113 | test(false, "foo;a=b; ;bar", A); | |
114 | test(false, "foo;a=b;;bar", A); | |
115 | test(false, "foo;a=b; ;;bar=rab", A, BAR); | |
116 | test(false, "foo;a=b;; ;bar=rab", A, BAR); | |
117 | test(false, "foo;a=b;;#;bar=rab", A, BAR); | |
118 | test(false, "foo;a=b;;\\;bar=rab", A, BAR); | |
119 | } | |
120 | ||
121 | @Test | |
122 | public void testEmptyPairsRfc6265() { | |
123 | test(true, "foo;a=b; ;bar", FOO_EMPTY, A, BAR_EMPTY); | |
124 | test(true, "foo;a=b;;bar", FOO_EMPTY, A, BAR_EMPTY); | |
125 | test(true, "foo;a=b; ;;bar=rab", FOO_EMPTY, A, BAR); | |
126 | test(true, "foo;a=b;; ;bar=rab", FOO_EMPTY, A, BAR); | |
127 | test(true, "foo;a=b;;#;bar=rab", FOO_EMPTY, A, HASH_EMPTY, BAR); | |
128 | test(true, "foo;a=b;;\\;bar=rab", FOO_EMPTY, A, BAR); | |
129 | } | |
130 | ||
131 | @Test | |
132 | public void testSeparatorsInValueOld() { | |
133 | doTestSeparatorsInValue(false); | |
134 | } | |
135 | ||
136 | @Test | |
137 | public void testSeparatorsInValueRfc6265() { | |
138 | doTestSeparatorsInValue(true); | |
139 | } | |
140 | ||
141 | private void doTestSeparatorsInValue(boolean useRfc6265) { | |
142 | test(useRfc6265, "a=()<>@:\\\"/[]?={}\t; foo=bar", FOO); | |
143 | } | |
144 | ||
145 | ||
146 | @Test | |
147 | public void v1TokenValueOld() { | |
148 | doV1TokenValue(false); | |
149 | } | |
150 | ||
151 | @Test | |
152 | public void v1TokenValueRfc6265() { | |
153 | doV1TokenValue(true); | |
154 | } | |
155 | ||
156 | private void doV1TokenValue(boolean useRfc6265) { | |
157 | FOO.setVersion(1); | |
158 | A.setVersion(1); | |
159 | test(useRfc6265, "$Version=1; foo=bar;a=b", FOO, A); | |
160 | test(useRfc6265, "$Version=1;foo=bar;a=b; ; ", FOO, A); | |
161 | } | |
162 | ||
163 | @Test | |
164 | public void v1NameOnlyIsDroppedOld() { | |
165 | doV1NameOnlyIsDropped(false); | |
166 | } | |
167 | ||
168 | @Test | |
169 | public void v1NameOnlyIsDroppedRfc6265() { | |
170 | doV1NameOnlyIsDropped(true); | |
171 | } | |
172 | ||
173 | private void doV1NameOnlyIsDropped(boolean useRfc6265) { | |
174 | A.setVersion(1); | |
175 | test(useRfc6265, "$Version=1;foo=;a=b; ; ", A); | |
176 | test(useRfc6265, "$Version=1;foo= ;a=b; ; ", A); | |
177 | test(useRfc6265, "$Version=1;foo;a=b; ; ", A); | |
178 | } | |
179 | ||
180 | @Test | |
181 | public void v1QuotedValueOld() { | |
182 | doV1QuotedValue(false); | |
183 | } | |
184 | ||
185 | @Test | |
186 | public void v1QuotedValueRfc6265() { | |
187 | doV1QuotedValue(true); | |
188 | } | |
189 | ||
190 | private void doV1QuotedValue(boolean useRfc6265) { | |
99 | 191 | FOO.setVersion(1); |
100 | 192 | A.setVersion(1); |
101 | 193 | // presumes quotes are removed |
102 | test("$Version=1;foo=\"bar\";a=b; ; ", FOO, A); | |
103 | } | |
104 | ||
105 | @Test | |
106 | public void v1DQuoteInValue() { | |
194 | test(useRfc6265, "$Version=1;foo=\"bar\";a=b; ; ", FOO, A); | |
195 | } | |
196 | ||
197 | @Test | |
198 | public void v1DQuoteInValueOld() { | |
107 | 199 | FOO.setValue("b"); |
108 | 200 | FOO.setVersion(1); |
109 | 201 | A.setVersion(1); |
110 | test("$Version=1;foo=\"b\"ar\";a=b", FOO, A); // Incorrectly escaped. | |
111 | } | |
112 | ||
113 | @Test | |
114 | public void v1QuoteInValue() { | |
202 | test(false, "$Version=1;foo=\"b\"ar\";a=b", FOO, A); // Incorrectly escaped. | |
203 | } | |
204 | ||
205 | @Test | |
206 | public void v1DQuoteInValueRfc6265() { | |
207 | A.setVersion(1); | |
208 | test(true, "$Version=1;foo=\"b\"ar\";a=b", A); // Incorrectly escaped. | |
209 | } | |
210 | ||
211 | @Test | |
212 | public void v1QuoteInValueOld() { | |
213 | doV1QuoteInValue(false); | |
214 | } | |
215 | ||
216 | @Test | |
217 | public void v1QuoteInValueRfc6265() { | |
218 | doV1QuoteInValue(true); | |
219 | } | |
220 | ||
221 | private void doV1QuoteInValue(boolean useRfc6265) { | |
115 | 222 | FOO.setValue("b'ar"); |
116 | 223 | FOO.setVersion(1); |
117 | 224 | A.setVersion(1); |
118 | test("$Version=1;foo=b'ar;a=b", FOO, A); | |
119 | } | |
120 | ||
121 | ||
122 | @Test | |
123 | public void v1QuoteInQuotedValue() { | |
225 | test(useRfc6265, "$Version=1;foo=b'ar;a=b", FOO, A); | |
226 | } | |
227 | ||
228 | ||
229 | @Test | |
230 | public void v1QuoteInQuotedValueOld() { | |
231 | doV1QuoteInQuotedValue(false); | |
232 | } | |
233 | ||
234 | @Test | |
235 | public void v1QuoteInQuotedValueRfc6265() { | |
236 | doV1QuoteInQuotedValue(true); | |
237 | } | |
238 | ||
239 | private void doV1QuoteInQuotedValue(boolean useRfc6265) { | |
124 | 240 | FOO.setValue("b'ar"); |
125 | 241 | FOO.setVersion(1); |
126 | 242 | A.setVersion(1); |
127 | test("$Version=1;foo=\"b'ar\";a=b", FOO, A); | |
128 | } | |
129 | ||
130 | @Test | |
131 | public void v1EscapedDQuoteInValue() { | |
243 | test(useRfc6265, "$Version=1;foo=\"b'ar\";a=b", FOO, A); | |
244 | } | |
245 | ||
246 | @Test | |
247 | public void v1EscapedDQuoteInValueOld() { | |
248 | doV1EscapedDQuoteInValue(false); | |
249 | } | |
250 | ||
251 | @Test | |
252 | public void v1EscapedDQuoteInValueRfc6265() { | |
253 | doV1EscapedDQuoteInValue(true); | |
254 | } | |
255 | ||
256 | private void doV1EscapedDQuoteInValue(boolean useRfc6265) { | |
132 | 257 | FOO.setValue("b\"ar"); |
133 | 258 | FOO.setVersion(1); |
134 | 259 | A.setVersion(1); |
135 | test("$Version=1;foo=\"b\\\"ar\";a=b", FOO, A); // correctly escaped. | |
136 | } | |
137 | ||
138 | @Test | |
139 | public void v1QuotedValueEndsInBackslash() { | |
140 | FOO.setVersion(1); | |
141 | test("$Version=1;foo=bar;a=\"b\\\"", FOO); | |
142 | } | |
143 | ||
144 | @Test | |
145 | public void v1MismatchedQuotes() { | |
146 | FOO.setVersion(1); | |
147 | test("$Version=1;foo=bar;a=\"b\\", FOO); | |
148 | } | |
149 | ||
150 | @Test | |
151 | public void v1SingleQuotesAreValidTokenCharacters() { | |
260 | test(useRfc6265, "$Version=1;foo=\"b\\\"ar\";a=b", FOO, A); // correctly escaped. | |
261 | } | |
262 | ||
263 | @Test | |
264 | public void v1QuotedValueEndsInBackslashOld() { | |
265 | doV1QuotedValueEndsInBackslash(false); | |
266 | } | |
267 | ||
268 | @Test | |
269 | public void v1QuotedValueEndsInBackslashRfc6265() { | |
270 | doV1QuotedValueEndsInBackslash(true); | |
271 | } | |
272 | ||
273 | private void doV1QuotedValueEndsInBackslash(boolean useRfc6265) { | |
274 | FOO.setVersion(1); | |
275 | test(useRfc6265, "$Version=1;foo=bar;a=\"b\\\"", FOO); | |
276 | } | |
277 | ||
278 | @Test | |
279 | public void v1MismatchedQuotesOld() { | |
280 | doV1MismatchedQuotes(false); | |
281 | } | |
282 | ||
283 | @Test | |
284 | public void v1MismatchedQuotesRfc6265() { | |
285 | doV1MismatchedQuotes(true); | |
286 | } | |
287 | ||
288 | private void doV1MismatchedQuotes(boolean useRfc6265) { | |
289 | FOO.setVersion(1); | |
290 | test(useRfc6265, "$Version=1;foo=bar;a=\"b\\", FOO); | |
291 | } | |
292 | ||
293 | @Test | |
294 | public void v1SingleQuotesAreValidTokenCharactersOld() { | |
295 | doV1SingleQuotesAreValidTokenCharacters(false); | |
296 | } | |
297 | ||
298 | @Test | |
299 | public void v1SingleQuotesAreValidTokenCharactersRfc6265() { | |
300 | doV1SingleQuotesAreValidTokenCharacters(true); | |
301 | } | |
302 | ||
303 | private void doV1SingleQuotesAreValidTokenCharacters(boolean useRfc6265) { | |
152 | 304 | FOO.setVersion(1); |
153 | 305 | FOO.setValue("'bar'"); |
154 | test("$Version=1; foo='bar'", FOO); | |
155 | } | |
156 | ||
157 | @Test | |
158 | public void v1DomainIsParsed() { | |
306 | test(useRfc6265, "$Version=1; foo='bar'", FOO); | |
307 | } | |
308 | ||
309 | @Test | |
310 | public void v1DomainIsParsedOld() { | |
311 | doV1DomainIsParsed(false); | |
312 | } | |
313 | ||
314 | @Test | |
315 | public void v1DomainIsParsedRfc6265() { | |
316 | doV1DomainIsParsed(true); | |
317 | } | |
318 | ||
319 | private void doV1DomainIsParsed(boolean useRfc6265) { | |
159 | 320 | FOO.setVersion(1); |
160 | 321 | FOO.setDomain("apache.org"); |
161 | 322 | A.setVersion(1); |
162 | 323 | A.setDomain("yahoo.com"); |
163 | test("$Version=1;foo=\"bar\";$Domain=apache.org;a=b;$Domain=yahoo.com", FOO, A); | |
164 | } | |
165 | ||
166 | @Test | |
167 | public void v1DomainOnlyAffectsPrecedingCookie() { | |
324 | test(useRfc6265, "$Version=1;foo=\"bar\";$Domain=apache.org;a=b;$Domain=yahoo.com", FOO, A); | |
325 | } | |
326 | ||
327 | @Test | |
328 | public void v1DomainOnlyAffectsPrecedingCookieOld() { | |
329 | doV1DomainOnlyAffectsPrecedingCookie(false); | |
330 | } | |
331 | ||
332 | @Test | |
333 | public void v1DomainOnlyAffectsPrecedingCookieRfc6265() { | |
334 | doV1DomainOnlyAffectsPrecedingCookie(true); | |
335 | } | |
336 | ||
337 | private void doV1DomainOnlyAffectsPrecedingCookie(boolean useRfc6265) { | |
168 | 338 | FOO.setVersion(1); |
169 | 339 | FOO.setDomain("apache.org"); |
170 | 340 | A.setVersion(1); |
171 | test("$Version=1;foo=\"bar\";$Domain=apache.org;a=b", FOO, A); | |
172 | } | |
173 | ||
174 | @Test | |
175 | public void v1PortIsIgnored() { | |
341 | test(useRfc6265, "$Version=1;foo=\"bar\";$Domain=apache.org;a=b", FOO, A); | |
342 | } | |
343 | ||
344 | @Test | |
345 | public void v1PortIsIgnoredOld() { | |
176 | 346 | FOO.setVersion(1); |
177 | 347 | FOO.setDomain("apache.org"); |
178 | 348 | A.setVersion(1); |
179 | test("$Version=1;foo=\"bar\";$Domain=apache.org;$Port=8080;a=b", FOO, A); | |
180 | } | |
181 | ||
182 | @Test | |
183 | public void v1PathAffectsPrecedingCookie() { | |
349 | test(false, "$Version=1;foo=\"bar\";$Domain=apache.org;$Port=8080;a=b", FOO, A); | |
350 | } | |
351 | ||
352 | @Test | |
353 | public void v1PortIsIgnoredRfc6265() { | |
354 | FOO.setVersion(1); | |
355 | FOO.setDomain("apache.org"); | |
356 | $PORT.setVersion(1); | |
357 | A.setVersion(1); | |
358 | test(true, "$Version=1;foo=\"bar\";$Domain=apache.org;$Port=8080;a=b", FOO, $PORT, A); | |
359 | } | |
360 | ||
361 | @Test | |
362 | public void v1PathAffectsPrecedingCookieOld() { | |
363 | doV1PathAffectsPrecedingCookie(false); | |
364 | } | |
365 | ||
366 | @Test | |
367 | public void v1PathAffectsPrecedingCookieRfc6265() { | |
368 | doV1PathAffectsPrecedingCookie(true); | |
369 | } | |
370 | ||
371 | private void doV1PathAffectsPrecedingCookie(boolean useRfc6265) { | |
184 | 372 | FOO.setVersion(1); |
185 | 373 | FOO.setPath("/examples"); |
186 | 374 | A.setVersion(1); |
187 | test("$Version=1;foo=\"bar\";$Path=/examples;a=b; ; ", FOO, A); | |
188 | } | |
189 | ||
190 | @Test | |
191 | public void rfc2109Version0() { | |
375 | test(useRfc6265, "$Version=1;foo=\"bar\";$Path=/examples;a=b; ; ", FOO, A); | |
376 | } | |
377 | ||
378 | @Test | |
379 | public void rfc2109Version0Old() { | |
192 | 380 | // rfc2109 semantically does not allow $Version to be 0 but it is valid syntax |
193 | test("$Version=0;foo=bar", FOO); | |
194 | } | |
195 | ||
196 | private void test(String header, Cookie... expected) { | |
197 | Cookies cookies = new Cookies(null); | |
381 | test(false, "$Version=0;foo=bar", FOO); | |
382 | } | |
383 | ||
384 | @Test | |
385 | public void rfc2109Version0Rfc6265() { | |
386 | // Neither RFC2109 nor RFc6265 allow version 0 | |
387 | test(true, "$Version=0;foo=bar"); | |
388 | } | |
389 | ||
390 | @Test | |
391 | public void disallow8bitInName() { | |
392 | // Bug 55917 | |
393 | test(true, "f\u00f6o=bar"); | |
394 | } | |
395 | ||
396 | @Test | |
397 | public void disallowControlInName() { | |
398 | // Bug 55917 | |
399 | test(true, "f\010o=bar"); | |
400 | } | |
401 | ||
402 | @Test | |
403 | public void disallow8BitControlInName() { | |
404 | // Bug 55917 | |
405 | test(true, "f\210o=bar"); | |
406 | } | |
407 | ||
408 | @Test | |
409 | public void allow8BitInV0Value() { | |
410 | // Bug 55917 | |
411 | test(true, "foo=b\u00e1r", FOO_CONTROL); | |
412 | } | |
413 | ||
414 | @Test | |
415 | public void disallow8bitInV1UnquotedValue() { | |
416 | // Bug 55917 | |
417 | test(true, "$Version=1; foo=b\u00e1r"); | |
418 | } | |
419 | ||
420 | @Test | |
421 | public void allow8bitInV1QuotedValue() { | |
422 | // Bug 55917 | |
423 | FOO_CONTROL.setVersion(1); | |
424 | test(true, "$Version=1; foo=\"b\u00e1r\"", FOO_CONTROL); | |
425 | } | |
426 | ||
427 | @Test | |
428 | public void disallowControlInV0Value() { | |
429 | // Bug 55917 | |
430 | test(true, "foo=b\010r"); | |
431 | } | |
432 | ||
433 | @Test | |
434 | public void disallowControlInV1UnquotedValue() { | |
435 | // Bug 55917 | |
436 | test(true, "$Version=1; foo=b\010r"); | |
437 | } | |
438 | ||
439 | @Test | |
440 | public void disallowControlInV1QuotedValue() { | |
441 | // Bug 55917 / Bug 55918 | |
442 | test(true, "$Version=1; foo=\"b\010r\""); | |
443 | } | |
444 | ||
445 | @Test | |
446 | public void disallow8BitControlInV1UnquotedValue() { | |
447 | // Bug 55917 | |
448 | test(true, "$Version=1; foo=b\210r"); | |
449 | } | |
450 | ||
451 | @Test | |
452 | public void testJsonInV0() { | |
453 | // Bug 55921 | |
454 | test(true, "{\"a\":true, \"b\":false};a=b", A); | |
455 | } | |
456 | ||
457 | @Test | |
458 | public void testJsonInV1() { | |
459 | // Bug 55921 | |
460 | A.setVersion(1); | |
461 | test(true, "$Version=1;{\"a\":true, \"b\":false};a=b", A); | |
462 | } | |
463 | ||
464 | @Test | |
465 | public void testSkipSemicolonOrComma() { | |
466 | // V1 cookies can also use commas to separate cookies | |
467 | FOO.setVersion(1); | |
468 | A.setVersion(1); | |
469 | test(true, "$Version=1;x\tx=yyy,foo=bar;a=b", FOO, A); | |
470 | } | |
471 | ||
472 | private void test(boolean useRfc6265, String header, Cookie... expected) { | |
473 | MimeHeaders mimeHeaders = new MimeHeaders(); | |
474 | Cookies cookies = new Cookies(mimeHeaders); | |
475 | cookies.setUseRfc6265(useRfc6265); | |
476 | MessageBytes cookieHeaderValue = mimeHeaders.addValue("Cookie"); | |
198 | 477 | byte[] bytes = header.getBytes(StandardCharsets.UTF_8); |
199 | cookies.processCookieHeader(bytes, 0, bytes.length); | |
478 | cookieHeaderValue.setBytes(bytes, 0, bytes.length); | |
479 | // Calling getCookieCount() triggers parsing | |
200 | 480 | Assert.assertEquals(expected.length, cookies.getCookieCount()); |
201 | 481 | for (int i = 0; i < expected.length; i++) { |
202 | 482 | Cookie cookie = expected[i]; |
203 | 483 | ServerCookie actual = cookies.getCookie(i); |
204 | 484 | Assert.assertEquals(cookie.getVersion(), actual.getVersion()); |
205 | 485 | Assert.assertEquals(cookie.getName(), actual.getName().toString()); |
206 | Assert.assertEquals(cookie.getValue(), actual.getValue().toString()); | |
486 | actual.getValue().getByteChunk().setCharset(StandardCharsets.UTF_8); | |
487 | Assert.assertEquals(cookie.getValue(), | |
488 | org.apache.tomcat.util.http.parser.Cookie.unescapeCookieValueRfc2109( | |
489 | actual.getValue().toString())); | |
207 | 490 | if (cookie.getVersion() == 1) { |
208 | 491 | Assert.assertEquals(cookie.getDomain(), actual.getDomain().toString()); |
209 | 492 | Assert.assertEquals(cookie.getPath(), actual.getPath().toString()); |
0 | /* | |
1 | * Licensed to the Apache Software Foundation (ASF) under one or more | |
2 | * contributor license agreements. See the NOTICE file distributed with | |
3 | * this work for additional information regarding copyright ownership. | |
4 | * The ASF licenses this file to You under the Apache License, Version 2.0 | |
5 | * (the "License"); you may not use this file except in compliance with | |
6 | * the License. You may obtain a copy of the License at | |
7 | * | |
8 | * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
15 | */ | |
16 | package org.apache.tomcat.util.http; | |
17 | ||
18 | import org.junit.Assert; | |
19 | import org.junit.Test; | |
20 | ||
21 | import org.apache.tomcat.util.buf.MessageBytes; | |
22 | ||
23 | public class TesterCookiesPerformance { | |
24 | ||
25 | @Test | |
26 | public void testPerformance01() throws Exception { | |
27 | final int cookieCount = 100; | |
28 | final int parsingLoops = 200000; | |
29 | ||
30 | MimeHeaders mimeHeaders = new MimeHeaders(); | |
31 | ||
32 | StringBuilder cookieHeader = new StringBuilder(); | |
33 | // Create cookies | |
34 | for (int i = 0; i < cookieCount; i++) { | |
35 | cookieHeader.append("name"); | |
36 | cookieHeader.append(i); | |
37 | cookieHeader.append('='); | |
38 | cookieHeader.append("value"); | |
39 | cookieHeader.append(i); | |
40 | cookieHeader.append(';'); | |
41 | } | |
42 | ||
43 | byte[] cookieHeaderBytes = cookieHeader.toString().getBytes("UTF-8"); | |
44 | ||
45 | MessageBytes headerValue = mimeHeaders.addValue("Cookie"); | |
46 | headerValue.setBytes(cookieHeaderBytes, 0, cookieHeaderBytes.length); | |
47 | ||
48 | Cookies cookies = new Cookies(mimeHeaders); | |
49 | // warm up | |
50 | for (int i = 0; i < parsingLoops; i++) { | |
51 | Assert.assertEquals(cookieCount, cookies.getCookieCount()); | |
52 | cookies.recycle(); | |
53 | } | |
54 | ||
55 | long oldStart = System.nanoTime(); | |
56 | for (int i = 0; i < parsingLoops; i++) { | |
57 | cookies.setUseRfc6265(false); | |
58 | Assert.assertEquals(cookieCount, cookies.getCookieCount()); | |
59 | cookies.recycle(); | |
60 | } | |
61 | long oldDuration = System.nanoTime() - oldStart; | |
62 | ||
63 | long newStart = System.nanoTime(); | |
64 | for (int i = 0; i < parsingLoops; i++) { | |
65 | cookies.setUseRfc6265(true); | |
66 | Assert.assertEquals(cookieCount, cookies.getCookieCount()); | |
67 | cookies.recycle(); | |
68 | } | |
69 | long newDuration = System.nanoTime() - newStart; | |
70 | ||
71 | System.out.println("Old duration: " + oldDuration); | |
72 | System.out.println("New duration: " + newDuration); | |
73 | } | |
74 | } |
54 | 54 | Context c = (Context) tomcat.getHost().findChildren()[0]; |
55 | 55 | // Enable pre-emptive auth |
56 | 56 | c.setPreemptiveAuthentication(true); |
57 | ||
58 | // Connector needs to advertise is accepts client certs for | |
59 | // pre-emptive to work | |
60 | tomcat.getConnector().setAttribute("clientAuth", "want"); | |
61 | 57 | } |
62 | 58 | |
63 | 59 | getTomcatInstance().start(); |
139 | 139 | } |
140 | 140 | |
141 | 141 | |
142 | private abstract static class GenericMessageHandler<T> | |
143 | implements MessageHandler.Whole<T> { | |
144 | } | |
145 | ||
146 | ||
147 | private static class GenericSubMessageHandler | |
148 | extends GenericMessageHandler<String>{ | |
149 | ||
142 | private abstract static class GenericMessageHandler<T> implements MessageHandler.Whole<T> { | |
143 | } | |
144 | ||
145 | ||
146 | private static class GenericSubMessageHandler extends GenericMessageHandler<String> { | |
150 | 147 | @Override |
151 | 148 | public void onMessage(String message) { |
152 | 149 | // NO-OP |
241 | 238 | } |
242 | 239 | |
243 | 240 | |
244 | private static class GenericSubEncoder | |
245 | extends GenericEncoder<String>{ | |
241 | private static class GenericSubEncoder extends GenericEncoder<String> { | |
246 | 242 | |
247 | 243 | @Override |
248 | 244 | public String encode(String object) throws EncodeException { |
68 | 68 | public void testProgrammaticEndPoints() throws Exception{ |
69 | 69 | Tomcat tomcat = getTomcatInstance(); |
70 | 70 | // Must have a real docBase - just use temp |
71 | Context ctx = | |
72 | tomcat.addContext("", System.getProperty("java.io.tmpdir")); | |
71 | Context ctx = tomcat.addContext("", System.getProperty("java.io.tmpdir")); | |
73 | 72 | ctx.addApplicationListener(ProgramaticServerEndpointConfig.class.getName()); |
74 | 73 | Tomcat.addServlet(ctx, "default", new DefaultServlet()); |
75 | 74 | ctx.addServletMapping("/", "default"); |
76 | 75 | |
77 | WebSocketContainer wsContainer = | |
78 | ContainerProvider.getWebSocketContainer(); | |
76 | WebSocketContainer wsContainer = ContainerProvider.getWebSocketContainer(); | |
79 | 77 | |
80 | 78 | tomcat.start(); |
81 | 79 | |
340 | 338 | } |
341 | 339 | |
342 | 340 | |
343 | public static class MsgStringMessageHandler | |
344 | implements MessageHandler.Whole<MsgString>{ | |
341 | public static class MsgStringMessageHandler implements MessageHandler.Whole<MsgString> { | |
345 | 342 | |
346 | 343 | public static Queue<Object> received = new ConcurrentLinkedQueue<>(); |
347 | 344 | private final Session session; |
0 | <%-- | |
1 | Licensed to the Apache Software Foundation (ASF) under one or more | |
2 | contributor license agreements. See the NOTICE file distributed with | |
3 | this work for additional information regarding copyright ownership. | |
4 | The ASF licenses this file to You under the Apache License, Version 2.0 | |
5 | (the "License"); you may not use this file except in compliance with | |
6 | the License. You may obtain a copy of the License at | |
7 | ||
8 | http://www.apache.org/licenses/LICENSE-2.0 | |
9 | ||
10 | Unless required by applicable law or agreed to in writing, software | |
11 | distributed under the License is distributed on an "AS IS" BASIS, | |
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | See the License for the specific language governing permissions and | |
14 | limitations under the License. | |
15 | --%> | |
16 | <%@ page import="java.io.IOException" contentType="text/plain"%> | |
17 | <% | |
18 | boolean flush = Boolean.valueOf(request.getParameter("flush")); | |
19 | if (pageContext != null) { | |
20 | try { | |
21 | if (flush) { | |
22 | out.println("Flush"); | |
23 | pageContext.include("/jsp/pageContext2.jsp", true); | |
24 | } else { | |
25 | pageContext.include("/jsp/pageContext2.jsp"); | |
26 | } | |
27 | } catch (IOException e) { | |
28 | out.println("OK"); | |
29 | return; | |
30 | } catch (Throwable t) { | |
31 | out.println("FAILED. Expected IOException, received: " + t.getClass().getName()); | |
32 | return; | |
33 | } | |
34 | out.println("FAILED. Expected IOException."); | |
35 | } else { | |
36 | out.println("FAILED. Expected IOException."); | |
37 | } | |
38 | %>⏎ |
0 | <%-- | |
1 | Licensed to the Apache Software Foundation (ASF) under one or more | |
2 | contributor license agreements. See the NOTICE file distributed with | |
3 | this work for additional information regarding copyright ownership. | |
4 | The ASF licenses this file to You under the Apache License, Version 2.0 | |
5 | (the "License"); you may not use this file except in compliance with | |
6 | the License. You may obtain a copy of the License at | |
7 | ||
8 | http://www.apache.org/licenses/LICENSE-2.0 | |
9 | ||
10 | Unless required by applicable law or agreed to in writing, software | |
11 | distributed under the License is distributed on an "AS IS" BASIS, | |
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | See the License for the specific language governing permissions and | |
14 | limitations under the License. | |
15 | --%> | |
16 | <%@ page import="java.io.IOException" contentType="text/plain"%> | |
17 | <% throw new IOException("Throws IOException."); %>⏎ |
43 | 43 | They eventually become mixed with the numbered issues. (I.e., numbered |
44 | 44 | issues to not "pop up" wrt. others). |
45 | 45 | --> |
46 | <section name="Tomcat 8.0.12 (markt)"> | |
46 | <section name="Tomcat 8.0.14 (markt)"> | |
47 | <subsection name="Other"> | |
48 | <changelog> | |
49 | <fix> | |
50 | <bug>56079</bug>: The Apache Tomcat Windows installer, the Apache Tomcat | |
51 | Windows service and the Apache Tomcat Windows service monitor | |
52 | application are now digitally signed. (markt) | |
53 | </fix> | |
54 | </changelog> | |
55 | </subsection> | |
56 | </section> | |
57 | <section name="Tomcat 8.0.13 (markt)" rtext="not released"> | |
58 | <subsection name="Catalina"> | |
59 | <changelog> | |
60 | <fix> | |
61 | <bug>55917</bug>: Allow bytes in the range 0x80 to 0xFF to appear in | |
62 | cookie values if the cookie is a V1 (RFC2109) cookie and the value is | |
63 | correctly quoted. The new RFC6265 based cookie parser must be enabled to | |
64 | correctly handle these cookies. (markt) | |
65 | </fix> | |
66 | <fix> | |
67 | <bug>55918</bug>: Do not permit control characters to appear in quoted | |
68 | V1 (RFC2109) cookie values. The new RFC6265 based cookie parser must be | |
69 | enabled to correctly handle these cookies. (markt) | |
70 | </fix> | |
71 | <fix> | |
72 | <bug>55921</bug>: Correctly handle (ignore the cookie) unescaped JSON in | |
73 | a cookie value. The new RFC6265 based cookie parser must be enabled to | |
74 | correctly handle these cookies. (markt) | |
75 | </fix> | |
76 | <add> | |
77 | <bug>56401</bug>: Log version information when Tomcat starts. | |
78 | (markt/kkolinko) | |
79 | </add> | |
80 | <add> | |
81 | <bug>56530</bug>: Add a web application class loader implementation that | |
82 | supports the parallel loading of web application classes. (markt) | |
83 | </add> | |
84 | <fix> | |
85 | <bug>56900</bug>: Fix some potential resource leaks when reading | |
86 | property files reported by Coverity Scan. Based on patches provided by | |
87 | Felix Schumacher. (markt) | |
88 | </fix> | |
89 | <fix> | |
90 | <bug>56902</bug>: Fix a potential resource leak in the Default Servlet | |
91 | reported by Coverity Scan. Based on a patch provided by Felix | |
92 | Schumacher. (markt) | |
93 | </fix> | |
94 | <fix> | |
95 | <bug>56903</bug>: Correct the return value for | |
96 | <code>StandardContext.getResourceOnlyServlets()</code> so that multiple | |
97 | names are separated by commas. Identified by Coverity Scan and fixed | |
98 | based on a patch by Felix Schumacher. (markt) | |
99 | </fix> | |
100 | <add> | |
101 | Add an additional implementation of a RFC6265 based cookie parser along | |
102 | with new Context options to select and configure it. This parser is | |
103 | currently considered experiemental and is not used by default. (markt) | |
104 | </add> | |
105 | <fix> | |
106 | Fixed the multipart elements merge operation performed during web | |
107 | application deployment. Identified by Coverity Scan. (violetagg) | |
108 | </fix> | |
109 | <fix> | |
110 | Correct the information written by | |
111 | <code>ExtendedAccessLogValve</code> when a format token x-O(XXX) is | |
112 | used so that multiple values for a header XXX are separated by commas. | |
113 | Identified by Coverity Scan. (violetagg) | |
114 | </fix> | |
115 | <fix> | |
116 | Fix a potential resource leak when reading MANIFEST.MF file for | |
117 | extension dependencies reported by Coverity Scan. (violetagg) | |
118 | </fix> | |
119 | <fix> | |
120 | Fix some potential resource leaks when reading properties, files and | |
121 | other resources. Reported by Coverity Scan. (violetagg) | |
122 | </fix> | |
123 | <fix> | |
124 | Correct the previous fix for <bug>56825</bug> that enabled pre-emptive | |
125 | authentication to work with the SSL authenticator. (markt) | |
126 | </fix> | |
127 | <scode> | |
128 | Refactor to reduce code duplication identified by Simian. (markt) | |
129 | </scode> | |
130 | <fix> | |
131 | When using parallel deployment and <code>undeployOldVersions</code> | |
132 | feature is enabled on a Host, correctly undeploy context of old | |
133 | version. Make sure that Tomcat does not undeploy older Context if | |
134 | current context is not running. (kfujino) | |
135 | </fix> | |
136 | <fix> | |
137 | Fix a rare threading issue when locking resources via WebDAV. | |
138 | (markt) | |
139 | </fix> | |
140 | <fix> | |
141 | Fix a rare threading issue when using HTTP digest authentication. | |
142 | (markt) | |
143 | </fix> | |
144 | <fix> | |
145 | When deploying war, add XML file in the config base to the redeploy | |
146 | resources if war does not have META-INF/context.xml or | |
147 | <code>deployXML</code> is false. If XML file is created in the config | |
148 | base, redeploy will occur. (kfujino) | |
149 | </fix> | |
150 | <scode> | |
151 | Various changes to reduce unnecessary code in Tomcat's copy of | |
152 | Apache Commons BCEL to reduce the time taken for annotation scanning | |
153 | when web applications start. Includes contributions from kkolinko and | |
154 | hzhang9. (markt) | |
155 | </scode> | |
156 | <fix> | |
157 | <bug>56938</bug>: Ensure web applications that have mixed case context | |
158 | paths and are deployed as directories are correctly removed on undeploy | |
159 | when running on a case sensitive file system. (markt) | |
160 | </fix> | |
161 | <add> | |
162 | <bug>57004</bug>: Add <code>stuckThreadCount</code> property to | |
163 | <code>StuckThreadDetectionValve</code>'s JMX bean. Patch provided by | |
164 | Jiří Pejchal. (schultz) | |
165 | </add> | |
166 | <fix> | |
167 | <bug>57011</bug>: Ensure that the request and response are correctly | |
168 | recycled when processing errors during async processing. (markt) | |
169 | </fix> | |
170 | </changelog> | |
171 | </subsection> | |
172 | <subsection name="Coyote"> | |
173 | <changelog> | |
174 | <fix> | |
175 | <bug>56910</bug>: Prevent the invalid value of <code>-1</code> being | |
176 | used for <code>maxConnections</code> with APR connectors. (markt) | |
177 | </fix> | |
178 | <fix> | |
179 | Ensure that AJP connectors enable the <code>KeepAliveTimeout</code>. | |
180 | (kfujino) | |
181 | </fix> | |
182 | <fix> | |
183 | Reduce duplicated code. All AJP connectors use common method to | |
184 | configuration of processor. (kfujino) | |
185 | </fix> | |
186 | </changelog> | |
187 | </subsection> | |
188 | <subsection name="Jasper"> | |
189 | <changelog> | |
190 | <fix> | |
191 | <bug>43001</bug>: Enable the JspC Ant task to set the JspC option | |
192 | <code>mappedFile</code>. (markt) | |
193 | </fix> | |
194 | <fix> | |
195 | Ensure that the implementation of | |
196 | <code>javax.servlet.jsp.PageContext.include(String)</code> | |
197 | and | |
198 | <code>javax.servlet.jsp.PageContext.include(String, boolean)</code> | |
199 | will throw <code>IOException</code> when an I/O error occur during | |
200 | the operation. (violetagg) | |
201 | </fix> | |
202 | <fix> | |
203 | <bug>56908</bug>: Fix some potential resource leaks when reading | |
204 | jar files. Reported by Coverity Scan. Patch provided by Felix | |
205 | Schumacher. (violetagg) | |
206 | </fix> | |
207 | <fix> | |
208 | Fix a potential resource leak in JDTCompiler when checking wether | |
209 | a resource is a package. Reported by Coverity Scan. (fschumacher) | |
210 | </fix> | |
211 | <fix> | |
212 | <bug>56991</bug>: Deprecate the use of a request attribute to pass a | |
213 | <jsp-file> declaration to Jasper and prevent an infinite loop | |
214 | if this technique is used in conjunction with an include. (markt) | |
215 | </fix> | |
216 | </changelog> | |
217 | </subsection> | |
218 | <subsection name="WebSocket"> | |
219 | <changelog> | |
220 | <fix> | |
221 | <bug>56905</bug>: Make destruction on web application stop of thread | |
222 | group used for WebSocket connections more robust. (kkolinko/markt) | |
223 | </fix> | |
224 | <fix> | |
225 | <bug>56907</bug>: Ensure that client IO threads are stopped if a secure | |
226 | WebSocket client connection fails. (markt) | |
227 | </fix> | |
228 | <fix> | |
229 | <bug>56982</bug>: Return the actual negotiated extensions rather than an | |
230 | empty list for <code>Session.getNegotiatedExtensions()</code>. (markt) | |
231 | </fix> | |
232 | <update> | |
233 | Update the WebSocket implementation to support the Java WebSocket | |
234 | specification version 1.1. (markt) | |
235 | </update> | |
236 | </changelog> | |
237 | </subsection> | |
238 | <subsection name="Web applications"> | |
239 | <changelog> | |
240 | <add> | |
241 | Add <code>JarScanner</code> to the nested components listed for a | |
242 | Context. (markt) | |
243 | </add> | |
244 | <update> | |
245 | Update the Windows authentication documentation after some additional | |
246 | testing to answer the remaining questions. (markt) | |
247 | </update> | |
248 | </changelog> | |
249 | </subsection> | |
250 | <subsection name="Other"> | |
251 | <changelog> | |
252 | <fix> | |
253 | <bug>56895</bug>: Correctly compose <code>JAVA_OPTS</code> in | |
254 | <code>catalina.bat</code> so that escape sequences are preserved. Patch | |
255 | by Lucas Theisen. (markt) | |
256 | </fix> | |
257 | <update> | |
258 | <bug>56988</bug>: Allow to use relative path in <code>base.path</code> | |
259 | setting when building Tomcat. (kkolinko) | |
260 | </update> | |
261 | <fix> | |
262 | <bug>56990</bug>: Ensure that the <code>ide-eclipse</code> build target | |
263 | downloads all the libraries required by the default Eclipse | |
264 | configuration files. (markt) | |
265 | </fix> | |
266 | <fix> | |
267 | Update the package renamed copy of Apache Commons DBCP 2 to revision | |
268 | 1626988 to pick up the fixes since the 2.0.1 release including support | |
269 | for custom eviction policies. (markt) | |
270 | </fix> | |
271 | <fix> | |
272 | Update the package renamed copy of Apache Commons Pool 2 to revision | |
273 | 1627271 to pick up the fixes since the 2.2 release including some memory | |
274 | leak fixes and support for application provided eviction policies. | |
275 | (markt) | |
276 | </fix> | |
277 | </changelog> | |
278 | </subsection> | |
279 | </section> | |
280 | <section name="Tomcat 8.0.12 (markt)" rtext="2014-09-03"> | |
47 | 281 | <subsection name="Catalina"> |
48 | 282 | <changelog> |
49 | 283 | <add> |
685 | 919 | </fix> |
686 | 920 | <add> |
687 | 921 | Add a new limit, defaulting to 2MB, for the amount of data Tomcat will |
688 | swallow for an aborted upload. (markt) | |
922 | swallow for an aborted upload. The limit is configurable by | |
923 | <code>maxSwallowSize</code> attribute of an HTTP connector. (markt) | |
689 | 924 | </add> |
690 | 925 | </changelog> |
691 | 926 | </subsection> |
188 | 188 | loading looks in the following repositories, in this order:</p> |
189 | 189 | <ul> |
190 | 190 | <li>Bootstrap classes of your JVM</li> |
191 | <li>System class loader classes (described above)</li> | |
192 | 191 | <li><em>/WEB-INF/classes</em> of your web application</li> |
193 | 192 | <li><em>/WEB-INF/lib/*.jar</em> of your web application</li> |
193 | <li>System class loader classes (described above)</li> | |
194 | 194 | <li>Common class loader classes (described above)</li> |
195 | </ul> | |
196 | ||
197 | <p>If the web application class loader is configuered with | |
198 | <code>delegate="true"</code> then the order becomes:</p> | |
199 | <ul> | |
200 | <li>Bootstrap classes of your JVM</li> | |
201 | <li>System class loader classes (described above)</li> | |
202 | <li>Common class loader classes (described above)</li> | |
203 | <li><em>/WEB-INF/classes</em> of your web application</li> | |
204 | <li><em>/WEB-INF/lib/*.jar</em> of your web application</li> | |
195 | 205 | </ul> |
196 | 206 | |
197 | 207 | </section> |
164 | 164 | removeSuspects will be automatically removed after removeSuspectsTimeout. |
165 | 165 | If a negative value specified, the removeSuspects members never be |
166 | 166 | removed until disappeared really. If the attribute is not provided, |
167 | a default of 300 milliseconds (5 minutes) is used. | |
167 | a default of 300 seconds (5 minutes) is used. | |
168 | 168 | </attribute> |
169 | 169 | </attributes> |
170 | 170 | </subsection> |
299 | 299 | no filtering will be applied.</p> |
300 | 300 | </attribute> |
301 | 301 | |
302 | <attribute name="cookieEncoding" required="false"> | |
303 | <p>If the RFC6265 based cookie parser is used this attribute determines | |
304 | the encoding to be used to convert the byte values in the HTTP header to | |
305 | strings. If not specified, the default of <code>UTF-8</code> will be | |
306 | used.</p> | |
307 | </attribute> | |
308 | ||
302 | 309 | <attribute name="cookies" required="false"> |
303 | 310 | <p>Set to <code>true</code> if you want cookies to be used for |
304 | 311 | session identifier communication if supported by the client (this |
528 | 535 | penalty.</p> |
529 | 536 | </attribute> |
530 | 537 | |
538 | <attribute name="useRfc6265" required="false"> | |
539 | <p>Determines if the RFC6265 based cookie parser is used. The default | |
540 | value is <code>false</code> in which case the original parser based on | |
541 | RFC2109 and the Netscape cookie specification will be used.</p> | |
542 | </attribute> | |
543 | ||
531 | 544 | <attribute name="useHttpOnly" required="false"> |
532 | 545 | <p>Should the HttpOnly flag be set on session cookies to prevent client |
533 | 546 | side script from accessing the session ID? Defaults to |
627 | 640 | <p>If <code>true</code> and an <code>sun.net.www.http.HttpClient</code> |
628 | 641 | keep-alive timer thread has been started by this web application and is |
629 | 642 | still running, Tomcat will change the context class loader for that |
630 | thread from the current <code>WebappClassLoader</code> to | |
631 | <code>WebappClassLoader#parent</code> to prevent a memory leak. Note | |
632 | that the keep-alive timer thread will stop on its own once the | |
633 | keep-alives all expire however, on a busy system that might not happen | |
634 | for some time. If not specified, the default value of | |
635 | <code>true</code> will be used.</p> | |
643 | thread from the web application class loader to the the parent of the web | |
644 | application class loader to prevent a memory leak. Note that the | |
645 | keep-alive timer thread will stop on its own once the keep-alives all | |
646 | expire however, on a busy system that might not happen for some time. If | |
647 | not specified, the default value of <code>true</code> will be used.</p> | |
636 | 648 | </attribute> |
637 | 649 | |
638 | 650 | <attribute name="clearReferencesStatic" required = "false"> |
694 | 706 | and methods that return values will return <code>null</code>. If not |
695 | 707 | specified, the specification compliant default of <code>true</code> will |
696 | 708 | be used.</p> |
697 | </attribute> | |
698 | ||
699 | <attribute name="processTlds" required="false"> | |
700 | <p>Whether the context should process TLDs on startup. The default | |
701 | is true. The false setting is intended for special cases | |
702 | that know in advance TLDs are not part of the webapp.</p> | |
703 | 709 | </attribute> |
704 | 710 | |
705 | 711 | <attribute name="renewThreadsWhenStoppingContext" required="false"> |
782 | 788 | specified static resource of the web application for updates, and will |
783 | 789 | reload the web application if it is updated. The content of this element |
784 | 790 | must be a string.</li> |
791 | <li><a href="jar-scanner.html"><strong>JarScanner</strong></a> - | |
792 | Configure the Jar Scanner that will be used to scan the web application | |
793 | for JAR files and directories of class files. It is typically used during | |
794 | web application start to identify configuration files such as TLDs o | |
795 | web-fragment.xml files that must be processed as part of the web | |
796 | application initialisation.. Normally, the default Jar Scanner | |
797 | configuration will be sufficient.</li> | |
785 | 798 | </ul> |
786 | 799 | |
787 | 800 | </section> |
383 | 383 | </attribute> |
384 | 384 | |
385 | 385 | </attributes> |
386 | ||
387 | </subsection> | |
388 | ||
389 | <subsection name="Version Logging Lifecycle Listener - org.apache.catalina.startup.VersionLoggerListener"> | |
390 | ||
391 | <p>The <strong>Version Logging Lifecycle Listener</strong> logs Tomcat, Java | |
392 | and operating system information when Tomcat starts.</p> | |
393 | ||
394 | <p>This listener must only be nested within <a href="server.html">Server</a> | |
395 | elements and should be the first listener defined.</p> | |
396 | ||
397 | <p>No additional attributes are supported by the <strong>Version Logging | |
398 | Lifecycle Listener</strong>.</p> | |
386 | 399 | |
387 | 400 | </subsection> |
388 | 401 |
121 | 121 | |
122 | 122 | <attribute name="loaderClass" required="false"> |
123 | 123 | <p>Java class name of the <code>java.lang.ClassLoader</code> |
124 | implementation class to use. If not specified, the default value is | |
125 | <code>org.apache.catalina.loader.WebappClassLoader</code>. Custom | |
126 | <strong>loaderClass</strong> implementations must extend | |
127 | <code>org.apache.catalina.loader.WebappClassLoader</code>.</p> | |
124 | implementation class to use. Custom implementations must extend | |
125 | <code>org.apache.catalina.loader.WebappClassLoaderBase</code>. | |
126 | </p> | |
127 | ||
128 | <p>If not specified, the default value is | |
129 | <code>org.apache.catalina.loader.WebappClassLoader</code>. The | |
130 | default <strong>loaderClass</strong> is not parallel capable, which | |
131 | means that loading a class from this classloader is performed by one | |
132 | thread at a time. A parallel capable <strong>loaderClass</strong> is | |
133 | available and can be used by specifying | |
134 | <code>org.apache.catalina.loader.ParallelWebappClassLoader</code>.</p> | |
128 | 135 | </attribute> |
129 | 136 | |
130 | 137 | <attribute name="searchExternalFirst" required="false"> |
197 | 197 | JSP page to be executed.</p> |
198 | 198 | <p>If not specified, the default value of |
199 | 199 | <code>org.apache.catalina.jsp_file</code> will be used.</p> |
200 | <p><strong>Deprecated:</strong> This will be removed in Tomcat 9.0.x | |
201 | onwards. It is replaced by the use of the jspFile servlet initialisation | |
202 | parameter</p> | |
200 | 203 | </property> |
201 | 204 | |
202 | 205 | <property name="org.apache.jasper.Constants. PRECOMPILE"> |
52 | 52 | <section name="Built-in Tomcat support"> |
53 | 53 | <p>Kerberos (the basis for integrated Windows authentication) requires careful |
54 | 54 | configuration. If the steps in this guide are followed exactly, then a working |
55 | configuration will result. There may be some flexibility in some of the steps | |
56 | below but further testing is required to explore this. From the testing to date | |
57 | it is known that:</p> | |
55 | configuration will result. It is important that the steps below are followed | |
56 | exactly. There is very little scope for flexibility in the configuration. From | |
57 | the testing to date it is known that:</p> | |
58 | 58 | <ul> |
59 | <li>The host name of the Tomcat server must match the host name in the SPN | |
60 | exactly else authentication will fail. A checksum error may be reported in the | |
61 | debug logs in this case.</li> | |
59 | <li>The host name used to access the Tomcat server must match the host name in | |
60 | the SPN exactly else authentication will fail. A checksum error may be reported | |
61 | in the debug logs in this case.</li> | |
62 | 62 | <li>The client must be of the view that the server is part of the local trusted |
63 | 63 | intranet.</li> |
64 | </ul> | |
65 | <p>The areas where further testing is required include:</p> | |
66 | <ul> | |
67 | <li>Does the domain name have to be in upper case?</li> | |
68 | <li>Does the SPN have to start with HTTP/...?</li> | |
69 | <li>Can a port number be appended to the end of the host in the SPN?</li> | |
70 | <li>Can the domain be left off the user in the ktpass command?</li> | |
71 | <li>What are the limitations on the account that Tomcat can run as? SPN | |
72 | associated account works, domain admin works, local admin doesn't | |
73 | work</li> | |
64 | <li>The SPN must be HTTP/<hostname> and it must be exactly the same in all | |
65 | the places it is used.</li> | |
66 | <li>The port number must not be included in the SPN.</li> | |
67 | <li>No more than one SPN may be mapped to a domain user.</li> | |
68 | <li>Tomcat must run as the domain account with which the SPN has been associated | |
69 | or as domain admin. It is <strong>NOT</strong> recommended to run Tomcat under a | |
70 | domain admin user.</li> | |
71 | <li>The domain name (<code>DEV.LOCAL</code>) is not case sensitive when used in | |
72 | the ktpass command, nor when used in jaas.conf</li> | |
73 | <li>The domain must be specified when using the ktpass command</li> | |
74 | 74 | </ul> |
75 | 75 | <p>There are four components to the configuration of the built-in Tomcat |
76 | 76 | support for Windows authentication. The domain controller, the server hosting |
145 | 145 | dev.local= DEV.LOCAL |
146 | 146 | .dev.local= DEV.LOCAL</source> |
147 | 147 | The location of this file can be changed by setting the |
148 | <code>java.security.krb5.conf</code> systm property.</li> | |
148 | <code>java.security.krb5.conf</code> system property.</li> | |
149 | 149 | <li>Create the JAAS login configuration file |
150 | 150 | <code>$CATALINA_BASE/conf/jaas.conf</code>. The file used in this how-to |
151 | 151 | contained:<source>com.sun.security.jgss.krb5.initiate { |