001: /**
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */package org.apache.openejb.loader;
017:
018: import java.io.File;
019: import java.io.IOException;
020: import java.lang.reflect.Method;
021: import java.net.URL;
022: import java.net.URLClassLoader;
023: import java.net.JarURLConnection;
024: import java.net.URLConnection;
025: import java.net.HttpURLConnection;
026: import java.net.MalformedURLException;
027: import java.net.URLDecoder;
028: import static java.net.URLDecoder.*;
029: import java.security.AccessController;
030: import java.security.PrivilegedAction;
031: import java.util.jar.JarFile;
032:
033: /*-------------------------------------------------------*/
034: /* Tomcat ClassLoader Support */
035: /*-------------------------------------------------------*/
036:
037: public class TomcatClassPath extends BasicURLClassPath {
038:
039: private final ClassLoader commonLoader;
040: private final ClassLoader serverLoader;
041:
042: private Method addRepositoryMethod;
043: private Method addURLMethod;
044:
045: public TomcatClassPath() {
046: this (getCommonLoader(getContextClassLoader()));
047: }
048:
049: public TomcatClassPath(ClassLoader classLoader) {
050: this .commonLoader = classLoader;
051: try {
052: addRepositoryMethod = getAddRepositoryMethod();
053: } catch (Exception tomcat4Exception) {
054:
055: try {
056: addURLMethod = getAddURLMethod();
057: } catch (Exception tomcat5Exception) {
058: throw new RuntimeException(
059: "Failed accessing classloader for Tomcat 5 or 6",
060: tomcat5Exception);
061: }
062: }
063:
064: ClassLoader serverLoader = getServerLoader(getContextClassLoader());
065: if (serverLoader != null && serverLoader != commonLoader) {
066: this .serverLoader = serverLoader;
067: } else
068: this .serverLoader = null;
069:
070: }
071:
072: private static ClassLoader getCommonLoader(ClassLoader loader) {
073: ClassLoader bootstrapCL;
074: try {
075: bootstrapCL = loader.loadClass(
076: "org.apache.catalina.startup.Bootstrap")
077: .getClassLoader();
078: } catch (ClassNotFoundException e) {
079: bootstrapCL = ClassLoader.getSystemClassLoader();
080: }
081:
082: if (loader == bootstrapCL) {
083: // this shouldn't happen...
084: // means all the tomcat classes are on the system classpath
085: // maybe we are in a junit test case?
086: return loader;
087: }
088:
089: while (loader.getParent() != bootstrapCL
090: && loader.getParent() != null) {
091: loader = loader.getParent();
092: }
093: return loader;
094: }
095:
096: private static ClassLoader getServerLoader(ClassLoader loader) {
097: try {
098: return loader.loadClass("org.apache.catalina.Container")
099: .getClassLoader();
100: } catch (ClassNotFoundException e) {
101: return null;
102: }
103: }
104:
105: public ClassLoader getClassLoader() {
106: return (serverLoader != null) ? serverLoader : commonLoader;
107: // return commonLoader;
108: }
109:
110: public ClassLoader getCommonLoader() {
111: return commonLoader;
112: }
113:
114: public void addJarsToPath(File dir) throws Exception {
115: String[] jarNames = dir.list(new java.io.FilenameFilter() {
116: public boolean accept(File dir, String name) {
117: return (name.endsWith(".jar") || name.endsWith(".zip"));
118: }
119: });
120:
121: if (jarNames == null) {
122: return;
123: }
124:
125: for (String jarName : jarNames) {
126: this .addJarToPath(new File(dir, jarName).toURL());
127: }
128: rebuild();
129: }
130:
131: public void addJarToPath(URL jar) throws Exception {
132: this ._addJarToPath(jar);
133: rebuild();
134: }
135:
136: public void _addJarToPath(URL jar) throws Exception {
137: ClassLoader classLoader = commonLoader;
138:
139: if (serverLoader != null && useServerClassLoader(jar)) {
140: classLoader = serverLoader;
141: }
142:
143: if (addRepositoryMethod != null) {
144: String path = jar.toExternalForm();
145: addRepositoryMethod.invoke(classLoader, path);
146: } else {
147: addURLMethod.invoke(classLoader, jar);
148: }
149: }
150:
151: private boolean useServerClassLoader(URL jar) {
152: try {
153: URL url = findResource(
154: "META-INF/org.apache.openejb.tomcat/ServerClassLoader",
155: jar);
156: return url != null;
157: } catch (Exception e) {
158: return false;
159: }
160: }
161:
162: protected void rebuild() {
163: try {
164: sun.misc.URLClassPath cp = getURLClassPath((URLClassLoader) getCommonLoader());
165: URL[] urls = cp.getURLs();
166:
167: if (urls.length < 1)
168: return;
169:
170: StringBuffer path = new StringBuffer(urls.length * 32);
171:
172: File s = new File(URLDecoder.decode(urls[0].getFile()));
173: path.append(s.getPath());
174:
175: for (int i = 1; i < urls.length; i++) {
176: path.append(File.pathSeparator);
177:
178: s = new File(URLDecoder.decode(urls[i].getFile()));
179:
180: path.append(s.getPath());
181: }
182: System.setProperty("java.class.path", path.toString());
183: } catch (Exception e) {
184: }
185:
186: }
187:
188: private Method getAddURLMethod() throws Exception {
189: return AccessController
190: .doPrivileged(new PrivilegedAction<Method>() {
191: public Method run() {
192: Method method = null;
193: try {
194: Class clazz = URLClassLoader.class;
195: method = clazz.getDeclaredMethod("addURL",
196: URL.class);
197: method.setAccessible(true);
198: return method;
199: } catch (Exception e2) {
200: e2.printStackTrace();
201: }
202: return method;
203: }
204: });
205: }
206:
207: private Method getAddRepositoryMethod() throws Exception {
208: return AccessController
209: .doPrivileged(new PrivilegedAction<Method>() {
210: public Method run() {
211: Method method;
212: try {
213: Class clazz = getClassLoader().getClass();
214: method = clazz.getDeclaredMethod(
215: "addRepository", String.class);
216: method.setAccessible(true);
217: return method;
218: } catch (Exception e2) {
219: throw (IllegalStateException) new IllegalStateException(
220: "Unable to find or access the addRepository method in StandardClassLoader")
221: .initCause(e2);
222: }
223: }
224: });
225: }
226:
227: private static boolean isDirectory(URL url) {
228: String file = url.getFile();
229: return (file.length() > 0 && file.charAt(file.length() - 1) == '/');
230: }
231:
232: private static URL findResource(String resourceName, URL... search) {
233: for (int i = 0; i < search.length; i++) {
234: URL currentUrl = search[i];
235: if (currentUrl == null) {
236: continue;
237: }
238:
239: if (currentUrl == null || isDirectory(currentUrl)
240: || currentUrl.getProtocol().equals("jar")) {
241: continue;
242: }
243: try {
244: currentUrl = new URL("jar", "", -1, currentUrl
245: .toString()
246: + "!/");
247: } catch (MalformedURLException e) {
248: }
249:
250: JarFile jarFile;
251: try {
252: String protocol = currentUrl.getProtocol();
253: if (protocol.equals("jar")) {
254: /*
255: * If the connection for currentUrl or resURL is
256: * used, getJarFile() will throw an exception if the
257: * entry doesn't exist.
258: */
259: URL jarURL = ((JarURLConnection) currentUrl
260: .openConnection()).getJarFileURL();
261: try {
262: JarURLConnection juc = (JarURLConnection) new URL(
263: "jar", "", jarURL.toExternalForm()
264: + "!/").openConnection();
265: jarFile = juc.getJarFile();
266: } catch (IOException e) {
267: // Don't look for this jar file again
268: search[i] = null;
269: throw e;
270: }
271:
272: String entryName;
273: if (currentUrl.getFile().endsWith("!/")) {
274: entryName = resourceName;
275: } else {
276: String file = currentUrl.getFile();
277: int sepIdx = file.lastIndexOf("!/");
278: if (sepIdx == -1) {
279: // Invalid URL, don't look here again
280: search[i] = null;
281: continue;
282: }
283: sepIdx += 2;
284: StringBuffer sb = new StringBuffer(file
285: .length()
286: - sepIdx + resourceName.length());
287: sb.append(file.substring(sepIdx));
288: sb.append(resourceName);
289: entryName = sb.toString();
290: }
291: if (entryName.equals("META-INF/")
292: && jarFile.getEntry("META-INF/MANIFEST.MF") != null) {
293: return targetURL(currentUrl,
294: "META-INF/MANIFEST.MF");
295: }
296: if (jarFile.getEntry(entryName) != null) {
297: return targetURL(currentUrl, resourceName);
298: }
299: } else if (protocol.equals("file")) {
300: String baseFile = currentUrl.getFile();
301: String host = currentUrl.getHost();
302: int hostLength = 0;
303: if (host != null) {
304: hostLength = host.length();
305: }
306: StringBuffer buf = new StringBuffer(2 + hostLength
307: + baseFile.length() + resourceName.length());
308:
309: if (hostLength > 0) {
310: buf.append("//").append(host);
311: }
312: // baseFile always ends with '/'
313: buf.append(baseFile);
314: String fixedResName = resourceName;
315: // Do not create a UNC path, i.e. \\host
316: while (fixedResName.startsWith("/")
317: || fixedResName.startsWith("\\")) {
318: fixedResName = fixedResName.substring(1);
319: }
320: buf.append(fixedResName);
321: String filename = buf.toString();
322: File file = new File(filename);
323: File file2 = new File(decode(filename));
324: if (file.exists() || file2.exists()) {
325: return targetURL(currentUrl, fixedResName);
326: }
327: } else {
328: URL resourceURL = targetURL(currentUrl,
329: resourceName);
330: URLConnection urlConnection = resourceURL
331: .openConnection();
332:
333: try {
334: urlConnection.getInputStream().close();
335: } catch (SecurityException e) {
336: return null;
337: }
338: // HTTP can return a stream on a non-existent file
339: // So check for the return code;
340: if (!resourceURL.getProtocol().equals("http")) {
341: return resourceURL;
342: }
343:
344: int code = ((HttpURLConnection) urlConnection)
345: .getResponseCode();
346: if (code >= 200 && code < 300) {
347: return resourceURL;
348: }
349: }
350: } catch (MalformedURLException e) {
351: // Keep iterating through the URL list
352: } catch (IOException e) {
353: } catch (SecurityException e) {
354: }
355: }
356: return null;
357: }
358:
359: private static URL targetURL(URL base, String name)
360: throws MalformedURLException {
361: StringBuffer sb = new StringBuffer(base.getFile().length()
362: + name.length());
363: sb.append(base.getFile());
364: sb.append(name);
365: String file = sb.toString();
366: return new URL(base.getProtocol(), base.getHost(), base
367: .getPort(), file, null);
368: }
369:
370: }
|