0001: /*
0002: * Licensed to the Apache Software Foundation (ASF) under one or more
0003: * contributor license agreements. See the NOTICE file distributed with
0004: * this work for additional information regarding copyright ownership.
0005: * The ASF licenses this file to You under the Apache License, Version 2.0
0006: * (the "License"); you may not use this file except in compliance with
0007: * the License. You may obtain a copy of the License at
0008: *
0009: * http://www.apache.org/licenses/LICENSE-2.0
0010: *
0011: * Unless required by applicable law or agreed to in writing, software
0012: * distributed under the License is distributed on an "AS IS" BASIS,
0013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0014: * See the License for the specific language governing permissions and
0015: * limitations under the License.
0016: */
0017:
0018: package java.net;
0019:
0020: import java.io.BufferedReader;
0021: import java.io.ByteArrayOutputStream;
0022: import java.io.File;
0023: import java.io.FileInputStream;
0024: import java.io.FileNotFoundException;
0025: import java.io.FilePermission;
0026: import java.io.IOException;
0027: import java.io.InputStream;
0028: import java.io.InputStreamReader;
0029: import java.io.UnsupportedEncodingException;
0030: import java.security.AccessControlContext;
0031: import java.security.AccessController;
0032: import java.security.CodeSource;
0033: import java.security.PermissionCollection;
0034: import java.security.PrivilegedAction;
0035: import java.security.SecureClassLoader;
0036: import java.security.cert.Certificate;
0037: import java.util.ArrayList;
0038: import java.util.Collections;
0039: import java.util.Enumeration;
0040: import java.util.HashMap;
0041: import java.util.List;
0042: import java.util.Map;
0043: import java.util.StringTokenizer;
0044: import java.util.jar.Attributes;
0045: import java.util.jar.JarEntry;
0046: import java.util.jar.JarFile;
0047: import java.util.jar.Manifest;
0048:
0049: import org.apache.harmony.luni.util.Msg;
0050:
0051: /**
0052: * This class loader is responsible for loading classes and resources from a
0053: * list of URLs which can refer to either directories or JAR files. Classes
0054: * loaded by this <code>URLClassLoader</code> are granted permission to access
0055: * the URLs contained in the URL search list.
0056: */
0057: public class URLClassLoader extends SecureClassLoader {
0058:
0059: ArrayList<URL> originalUrls;
0060:
0061: List<URL> searchList;
0062: ArrayList<URLHandler> handlerList;
0063: Map<URL, URLHandler> handlerMap = new HashMap<URL, URLHandler>();
0064:
0065: private URLStreamHandlerFactory factory;
0066:
0067: private AccessControlContext currentContext;
0068:
0069: static class SubURLClassLoader extends URLClassLoader {
0070: // The subclass that overwrites the loadClass() method
0071: private boolean checkingPackageAccess = false;
0072:
0073: SubURLClassLoader(URL[] urls) {
0074: super (urls, ClassLoader.getSystemClassLoader());
0075: }
0076:
0077: SubURLClassLoader(URL[] urls, ClassLoader parent) {
0078: super (urls, parent);
0079: }
0080:
0081: /**
0082: * Overrides the loadClass() of <code>ClassLoader</code>. It calls
0083: * the security manager's <code>checkPackageAccess()</code> before
0084: * attempting to load the class.
0085: *
0086: * @param className String the name of the class to search for.
0087: * @param resolveClass boolean indicates if class should be resolved after
0088: * loading.
0089: * @return the Class object.
0090: * @throws ClassNotFoundException If the class could not be found.
0091: */
0092: @Override
0093: protected synchronized Class<?> loadClass(String className,
0094: boolean resolveClass) throws ClassNotFoundException {
0095: SecurityManager sm = System.getSecurityManager();
0096: if (sm != null && !checkingPackageAccess) {
0097: int index = className.lastIndexOf('.');
0098: if (index > 0) { // skip if class is from a default package
0099: try {
0100: checkingPackageAccess = true;
0101: sm.checkPackageAccess(className.substring(0,
0102: index));
0103: } finally {
0104: checkingPackageAccess = false;
0105: }
0106: }
0107: }
0108: return super .loadClass(className, resolveClass);
0109: }
0110: }
0111:
0112: static class IndexFile {
0113:
0114: private HashMap<String, ArrayList<URL>> map;
0115:
0116: //private URLClassLoader host;
0117:
0118: static IndexFile readIndexFile(JarFile jf, JarEntry indexEntry,
0119: URL url) {
0120: BufferedReader in = null;
0121: InputStream is = null;
0122: try {
0123: // Add mappings from resource to jar file
0124: String parentURLString = getParentURL(url)
0125: .toExternalForm();
0126: String prefix = "jar:" //$NON-NLS-1$
0127: + parentURLString + "/"; //$NON-NLS-1$
0128: is = jf.getInputStream(indexEntry);
0129: in = new BufferedReader(new InputStreamReader(is,
0130: "UTF8"));
0131: HashMap<String, ArrayList<URL>> pre_map = new HashMap<String, ArrayList<URL>>();
0132: // Ignore the 2 first lines (index version)
0133: if (in.readLine() == null)
0134: return null;
0135: if (in.readLine() == null)
0136: return null;
0137: TOP_CYCLE: while (true) {
0138: String line = in.readLine();
0139: if (line == null) {
0140: break;
0141: }
0142: URL jar = new URL(prefix + line + "!/"); //$NON-NLS-1$
0143: while (true) {
0144: line = in.readLine();
0145: if (line == null) {
0146: break TOP_CYCLE;
0147: }
0148: if ("".equals(line)) {
0149: break;
0150: }
0151: ArrayList<URL> list;
0152: if (pre_map.containsKey(line)) {
0153: list = pre_map.get(line);
0154: } else {
0155: list = new ArrayList<URL>();
0156: pre_map.put(line, list);
0157: }
0158: list.add(jar);
0159: }
0160: }
0161: if (!pre_map.isEmpty()) {
0162: return new IndexFile(pre_map);
0163: }
0164: } catch (MalformedURLException e) {
0165: // Ignore this jar's index
0166: } catch (IOException e) {
0167: // Ignore this jar's index
0168: } finally {
0169: if (in != null) {
0170: try {
0171: in.close();
0172: } catch (IOException e) {
0173: }
0174: }
0175: if (is != null) {
0176: try {
0177: is.close();
0178: } catch (IOException e) {
0179: }
0180: }
0181: }
0182: return null;
0183: }
0184:
0185: private static URL getParentURL(URL url) throws IOException {
0186: URL fileURL = ((JarURLConnection) url.openConnection())
0187: .getJarFileURL();
0188: String file = fileURL.getFile();
0189: String parentFile = new File(file).getParent();
0190: parentFile = parentFile.replace(File.separatorChar, '/');
0191: if (parentFile.charAt(0) != '/') {
0192: parentFile = "/" + parentFile; //$NON-NLS-1$
0193: }
0194: URL parentURL = new URL(fileURL.getProtocol(), fileURL
0195: .getHost(), fileURL.getPort(), parentFile);
0196: return parentURL;
0197: }
0198:
0199: public IndexFile(HashMap<String, ArrayList<URL>> map) {
0200: this .map = map;
0201: }
0202:
0203: ArrayList<URL> get(String name) {
0204: return map.get(name);
0205: }
0206: }
0207:
0208: class URLHandler {
0209: URL url;
0210: URL codeSourceUrl;
0211:
0212: public URLHandler(URL url) {
0213: this .url = url;
0214: this .codeSourceUrl = url;
0215: }
0216:
0217: void findResources(String name, ArrayList<URL> resources) {
0218: URL res = findResource(name);
0219: if (res != null && !resources.contains(res)) {
0220: resources.add(res);
0221: }
0222: }
0223:
0224: Class<?> findClass(String packageName, String name,
0225: String origName) {
0226: URL resURL = targetURL(url, name);
0227: if (resURL != null) {
0228: try {
0229: InputStream is = resURL.openStream();
0230: return createClass(is, packageName, origName);
0231: } catch (IOException e) {
0232: }
0233: }
0234: return null;
0235: }
0236:
0237: Class<?> createClass(InputStream is, String packageName,
0238: String origName) {
0239: if (is == null) {
0240: return null;
0241: }
0242: byte[] clBuf = null;
0243: try {
0244: clBuf = getBytes(is);
0245: } catch (IOException e) {
0246: return null;
0247: } finally {
0248: try {
0249: is.close();
0250: } catch (IOException e) {
0251: }
0252: }
0253: if (packageName != null) {
0254: String packageDotName = packageName.replace('/', '.');
0255: Package packageObj = getPackage(packageDotName);
0256: if (packageObj == null) {
0257: definePackage(packageDotName, null, null, null,
0258: null, null, null, null);
0259: } else {
0260: if (packageObj.isSealed()) {
0261: throw new SecurityException(Msg
0262: .getString("K004c")); //$NON-NLS-1$
0263: }
0264: }
0265: }
0266: return defineClass(origName, clBuf, 0, clBuf.length,
0267: new CodeSource(codeSourceUrl, (Certificate[]) null));
0268: }
0269:
0270: URL findResource(String name) {
0271: URL resURL = targetURL(url, name);
0272: if (resURL != null) {
0273: try {
0274: URLConnection uc = resURL.openConnection();
0275: uc.getInputStream().close();
0276: // HTTP can return a stream on a non-existent file
0277: // So check for the return code;
0278: if (!resURL.getProtocol().equals("http")) { //$NON-NLS-1$
0279: return resURL;
0280: }
0281: int code;
0282: if ((code = ((HttpURLConnection) uc)
0283: .getResponseCode()) >= 200
0284: && code < 300) {
0285: return resURL;
0286: }
0287: } catch (SecurityException e) {
0288: return null;
0289: } catch (IOException e) {
0290: return null;
0291: }
0292: }
0293: return null;
0294: }
0295:
0296: URL targetURL(URL base, String name) {
0297: try {
0298: String file = base.getFile()
0299: + URIEncoderDecoder.quoteIllegal(name, "/@"
0300: + URI.someLegal);
0301:
0302: return new URL(base.getProtocol(), base.getHost(), base
0303: .getPort(), file, null);
0304: } catch (UnsupportedEncodingException e) {
0305: return null;
0306: } catch (MalformedURLException e) {
0307: return null;
0308: }
0309: }
0310:
0311: }
0312:
0313: class URLJarHandler extends URLHandler {
0314: JarFile jf;
0315: String prefixName;
0316: IndexFile index = null;
0317: Map<URL, URLHandler> subHandlers = new HashMap<URL, URLHandler>();
0318:
0319: public URLJarHandler(URL url, URL jarURL, JarFile jf,
0320: String prefixName) {
0321: super (url);
0322: this .jf = jf;
0323: this .prefixName = prefixName;
0324: this .codeSourceUrl = jarURL;
0325: JarEntry je = jf.getJarEntry("META-INF/INDEX.LIST"); //$NON-NLS-1$
0326: if (je != null) {
0327: index = IndexFile.readIndexFile(jf, je, url);
0328: }
0329: }
0330:
0331: public URLJarHandler(URL url, URL jarURL, JarFile jf,
0332: String prefixName, IndexFile index) {
0333: super (url);
0334: this .jf = jf;
0335: this .prefixName = prefixName;
0336: this .index = index;
0337: this .codeSourceUrl = jarURL;
0338: }
0339:
0340: IndexFile getIndex() {
0341: return index;
0342: }
0343:
0344: void findResources(String name, ArrayList<URL> resources) {
0345: URL res = findResourceInOwn(name);
0346: if (res != null && !resources.contains(res)) {
0347: resources.add(res);
0348: }
0349: if (index != null) {
0350: int pos = name.lastIndexOf("/"); //$NON-NLS-1$
0351: // only keep the directory part of the resource
0352: // as index.list only keeps track of directories and root files
0353: String indexedName = (pos > 0) ? name.substring(0, pos)
0354: : name;
0355: ArrayList<URL> urls = index.get(indexedName);
0356: if (urls != null) {
0357: for (URL url : urls) {
0358: URLHandler h = getSubHandler(url);
0359: if (h != null) {
0360: h.findResources(name, resources);
0361: }
0362: }
0363: }
0364: }
0365:
0366: }
0367:
0368: Class<?> findClass(String packageName, String name,
0369: String origName) {
0370: String entryName = prefixName + name;
0371: JarEntry entry = jf.getJarEntry(entryName);
0372: if (entry != null) {
0373: /**
0374: * Avoid recursive load class, especially the class
0375: * is an implementation class of security provider
0376: * and the jar is signed.
0377: */
0378: try {
0379: Manifest manifest = jf.getManifest();
0380: return createClass(entry, manifest, packageName,
0381: origName);
0382: } catch (IOException e) {
0383: }
0384: }
0385: if (index != null) {
0386: ArrayList<URL> urls;
0387: if (packageName == null) {
0388: urls = index.get(name);
0389: } else {
0390: urls = index.get(packageName);
0391: }
0392: if (urls != null) {
0393: for (URL url : urls) {
0394: URLHandler h = getSubHandler(url);
0395: if (h != null) {
0396: Class<?> res = h.findClass(packageName,
0397: name, origName);
0398: if (res != null) {
0399: return res;
0400: }
0401: }
0402: }
0403: }
0404: }
0405: return null;
0406: }
0407:
0408: private Class<?> createClass(JarEntry entry, Manifest manifest,
0409: String packageName, String origName) {
0410: InputStream is = null;
0411: byte[] clBuf = null;
0412: try {
0413: is = jf.getInputStream(entry);
0414: clBuf = getBytes(is);
0415: } catch (IOException e) {
0416: return null;
0417: } finally {
0418: if (is != null) {
0419: try {
0420: is.close();
0421: } catch (IOException e) {
0422: }
0423: }
0424: }
0425: if (packageName != null) {
0426: String packageDotName = packageName.replace('/', '.');
0427: Package packageObj = getPackage(packageDotName);
0428: if (packageObj == null) {
0429: if (manifest != null) {
0430: definePackage(packageDotName, manifest,
0431: codeSourceUrl);
0432: } else {
0433: definePackage(packageDotName, null, null, null,
0434: null, null, null, null);
0435: }
0436: } else {
0437: boolean exception = false;
0438: if (manifest != null) {
0439: if (isSealed(manifest, packageName + "/")) {
0440: exception = !packageObj
0441: .isSealed(codeSourceUrl);
0442: }
0443: } else {
0444: exception = packageObj.isSealed();
0445: }
0446: if (exception) {
0447: throw new SecurityException(Msg
0448: .getString("K004c")); //$NON-NLS-1$
0449: }
0450: }
0451: }
0452: CodeSource codeS = new CodeSource(codeSourceUrl, entry
0453: .getCertificates());
0454: return defineClass(origName, clBuf, 0, clBuf.length, codeS);
0455: }
0456:
0457: URL findResourceInOwn(String name) {
0458: String entryName = prefixName + name;
0459: if (jf.getEntry(entryName) != null) {
0460: return targetURL(url, name);
0461: }
0462: return null;
0463: }
0464:
0465: URL findResource(String name) {
0466: URL res = findResourceInOwn(name);
0467: if (res != null) {
0468: return res;
0469: }
0470: if (index != null) {
0471: int pos = name.lastIndexOf("/"); //$NON-NLS-1$
0472: // only keep the directory part of the resource
0473: // as index.list only keeps track of directories and root files
0474: String indexedName = (pos > 0) ? name.substring(0, pos)
0475: : name;
0476: ArrayList<URL> urls = index.get(indexedName);
0477: if (urls != null) {
0478: for (URL url : urls) {
0479: URLHandler h = getSubHandler(url);
0480: if (h != null) {
0481: res = h.findResource(name);
0482: if (res != null) {
0483: return res;
0484: }
0485: }
0486: }
0487: }
0488: }
0489: return null;
0490: }
0491:
0492: private synchronized URLHandler getSubHandler(URL url) {
0493: URLHandler sub = subHandlers.get(url);
0494: if (url != null) {
0495: return sub;
0496: }
0497: String protocol = url.getProtocol();
0498: if (protocol.equals("jar")) { //$NON-NLS-1$
0499: sub = createURLJarHandler(url);
0500: } else if (protocol.equals("file")) { //$NON-NLS-1$
0501: sub = createURLSubJarHandler(url);
0502: } else {
0503: sub = createURLHandler(url);
0504: }
0505: if (sub != null) {
0506: subHandlers.put(url, sub);
0507: }
0508: return sub;
0509: }
0510:
0511: private URLHandler createURLSubJarHandler(URL url) {
0512: String prefixName;
0513: String file = url.getFile();
0514: if (url.getFile().endsWith("!/")) { //$NON-NLS-1$
0515: prefixName = "";
0516: } else {
0517: int sepIdx = file.lastIndexOf("!/"); //$NON-NLS-1$
0518: if (sepIdx == -1) {
0519: // Invalid URL, don't look here again
0520: return null;
0521: }
0522: sepIdx += 2;
0523: prefixName = file.substring(sepIdx);
0524: }
0525: try {
0526: URL jarURL = ((JarURLConnection) url.openConnection())
0527: .getJarFileURL();
0528: JarURLConnection juc = (JarURLConnection) new URL(
0529: "jar", "", //$NON-NLS-1$ //$NON-NLS-2$
0530: jarURL.toExternalForm() + "!/").openConnection(); //$NON-NLS-1$
0531: JarFile jf = juc.getJarFile();
0532: URLJarHandler jarH = new URLJarHandler(url, jarURL, jf,
0533: prefixName, null);
0534: // TODO : to think what we should do with indexes & manifest.class file here
0535: return jarH;
0536: } catch (IOException e) {
0537: }
0538: return null;
0539: }
0540:
0541: }
0542:
0543: class URLFileHandler extends URLHandler {
0544: private String prefix;
0545:
0546: public URLFileHandler(URL url) {
0547: super (url);
0548: String baseFile = url.getFile();
0549: String host = url.getHost();
0550: int hostLength = 0;
0551: if (host != null) {
0552: hostLength = host.length();
0553: }
0554: StringBuilder buf = new StringBuilder(2 + hostLength
0555: + baseFile.length());
0556: if (hostLength > 0) {
0557: buf.append("//").append(host); //$NON-NLS-1$
0558: }
0559: // baseFile always ends with '/'
0560: buf.append(baseFile);
0561: prefix = buf.toString();
0562: }
0563:
0564: Class<?> findClass(String packageName, String name,
0565: String origName) {
0566: String filename = prefix + name;
0567: try {
0568: filename = URLDecoder.decode(filename, "UTF-8"); //$NON-NLS-1$
0569: } catch (IllegalArgumentException e) {
0570: return null;
0571: } catch (UnsupportedEncodingException e) {
0572: return null;
0573: }
0574:
0575: File file = new File(filename);
0576: if (file.exists()) {
0577: try {
0578: InputStream is = new FileInputStream(file);
0579: return createClass(is, packageName, origName);
0580: } catch (FileNotFoundException e) {
0581: }
0582: }
0583: return null;
0584: }
0585:
0586: URL findResource(String name) {
0587: int idx = 0;
0588: String filename;
0589:
0590: // Do not create a UNC path, i.e. \\host
0591: while (idx < name.length() && ((name.charAt(idx) == '/') //$NON-NLS-1$
0592: || (name.charAt(idx) == '\\'))) { //$NON-NLS-1$
0593: idx++;
0594: }
0595:
0596: if (idx > 0) {
0597: name = name.substring(idx);
0598: }
0599:
0600: try {
0601: filename = URLDecoder.decode(prefix, "UTF-8") + name; //$NON-NLS-1$
0602:
0603: if (new File(filename).exists()) {
0604: return targetURL(url, name);
0605: } else {
0606: return null;
0607: }
0608: } catch (IllegalArgumentException e) {
0609: return null;
0610: } catch (UnsupportedEncodingException e) {
0611: return null;
0612: }
0613: }
0614:
0615: }
0616:
0617: /**
0618: * Constructs a new instance of this class. The newly created instance will
0619: * have the system ClassLoader as its parent. URLs that end with "/" are
0620: * assumed to be directories, otherwise they are assumed to be Jar files.
0621: *
0622: * @param urls
0623: * the URLs to search
0624: *
0625: * @throws SecurityException
0626: * if a security manager exists and its checkCreateClassLoader
0627: * method doesn't allow creation of new ClassLoaders
0628: */
0629: public URLClassLoader(URL[] urls) {
0630: this (urls, ClassLoader.getSystemClassLoader(), null);
0631: }
0632:
0633: /**
0634: * Constructs a new instance of this class. The newly created instance will
0635: * have the specified ClassLoader as its parent. URLs that end with "/" are
0636: * assumed to be directories, otherwise they are assumed to be Jar files.
0637: *
0638: * @param urls
0639: * the URLs to search
0640: *
0641: * @param parent
0642: * the ClassLoader to assign as this loader's parent.
0643: *
0644: * @throws SecurityException
0645: * if a security manager exists and its checkCreateClassLoader
0646: * method doesn't allow creation of new ClassLoaders
0647: */
0648: public URLClassLoader(URL[] urls, ClassLoader parent) {
0649: this (urls, parent, null);
0650: }
0651:
0652: /**
0653: * Adds the specified URL to the search list.
0654: *
0655: * @param url java.net.URL the new URL
0656: */
0657: protected void addURL(URL url) {
0658: try {
0659: originalUrls.add(url);
0660: searchList.add(createSearchURL(url));
0661: } catch (MalformedURLException e) {
0662: }
0663: }
0664:
0665: /**
0666: * Answers an enumeration of URLs that contain the specified resource.
0667: *
0668: * @param name java.lang.String the name of the requested resource
0669: * @return Enumeration the enumeration of URLs that contain the specified
0670: * resource.
0671: * @throws IOException
0672: * thrown if an IO Exception occurs while attempting to connect
0673: */
0674: @Override
0675: public Enumeration<URL> findResources(final String name)
0676: throws IOException {
0677: if (name == null) {
0678: return null;
0679: }
0680: ArrayList<URL> result = AccessController.doPrivileged(
0681: new PrivilegedAction<ArrayList<URL>>() {
0682: public ArrayList<URL> run() {
0683: ArrayList<URL> results = new ArrayList<URL>();
0684: findResourcesImpl(name, results);
0685: return results;
0686: }
0687: }, currentContext);
0688: SecurityManager sm;
0689: int length = result.size();
0690: if (length > 0 && (sm = System.getSecurityManager()) != null) {
0691: ArrayList<URL> reduced = new ArrayList<URL>(length);
0692: for (int i = 0; i < length; i++) {
0693: URL url = result.get(i);
0694: try {
0695: sm.checkPermission(url.openConnection()
0696: .getPermission());
0697: reduced.add(url);
0698: } catch (IOException e) {
0699: } catch (SecurityException e) {
0700: }
0701: }
0702: result = reduced;
0703: }
0704: return Collections.enumeration(result);
0705: }
0706:
0707: void findResourcesImpl(String name, ArrayList<URL> result) {
0708: int n = 0;
0709: while (true) {
0710: URLHandler handler = getHandler(n++);
0711: if (handler == null) {
0712: break;
0713: }
0714: handler.findResources(name, result);
0715: }
0716: }
0717:
0718: /**
0719: * Converts an input stream into a byte array.
0720: *
0721: * @param is the input stream
0722: * @return byte[] the byte array
0723: */
0724: private static byte[] getBytes(InputStream is) throws IOException {
0725: byte[] buf = new byte[4096];
0726: ByteArrayOutputStream bos = new ByteArrayOutputStream(4096);
0727: int count;
0728: while ((count = is.read(buf)) > 0) {
0729: bos.write(buf, 0, count);
0730: }
0731: return bos.toByteArray();
0732: }
0733:
0734: /**
0735: * Answers the permissions for the given code source. First this method
0736: * retrieves the permissions from the system policy. If the protocol is
0737: * "file:/" then a new permission, <code>FilePermission</code>, granting
0738: * the read permission to the file is added to the permission collection.
0739: * Otherwise, connecting to and accepting connections from the URL is
0740: * granted.
0741: *
0742: * @param codesource CodeSource
0743: * @return PermissionCollection
0744: */
0745: @Override
0746: protected PermissionCollection getPermissions(
0747: final CodeSource codesource) {
0748: PermissionCollection pc = super .getPermissions(codesource);
0749: URL u = codesource.getLocation();
0750: if (u.getProtocol().equals("jar")) { //$NON-NLS-1$
0751: try {
0752: // Create a URL for the resource the jar refers to
0753: u = ((JarURLConnection) u.openConnection())
0754: .getJarFileURL();
0755: } catch (IOException e) {
0756: // This should never occur. If it does continue using the jar
0757: // URL
0758: }
0759: }
0760: if (u.getProtocol().equals("file")) { //$NON-NLS-1$
0761: String path = u.getFile();
0762: String host = u.getHost();
0763: if (host != null && host.length() > 0) {
0764: path = "//" + host + path; //$NON-NLS-1$
0765: }
0766:
0767: if (File.separatorChar != '/') {
0768: path = path.replace('/', File.separatorChar);
0769: }
0770: if (isDirectory(u)) {
0771: pc.add(new FilePermission(path + "-", "read")); //$NON-NLS-1$ //$NON-NLS-2$
0772: } else {
0773: pc.add(new FilePermission(path, "read")); //$NON-NLS-1$
0774: }
0775: } else {
0776: String host = u.getHost();
0777: if (host.length() == 0) {
0778: host = "localhost"; //$NON-NLS-1$
0779: }
0780: pc.add(new SocketPermission(host, "connect, accept")); //$NON-NLS-1$
0781: }
0782: return pc;
0783: }
0784:
0785: /**
0786: * Answers the search list of this URLClassLoader
0787: *
0788: * @return java.net.URL[]
0789: */
0790: public URL[] getURLs() {
0791: return originalUrls.toArray(new URL[originalUrls.size()]);
0792: }
0793:
0794: /**
0795: * Determines if the URL is pointing to a directory.
0796: *
0797: * @param url java.net.URL
0798: * @return boolean
0799: */
0800: private static boolean isDirectory(URL url) {
0801: String file = url.getFile();
0802: return (file.length() > 0 && file.charAt(file.length() - 1) == '/');
0803: }
0804:
0805: /**
0806: * Answers an instance of <code>URLClassLoader</code>.
0807: * <code>loadClass()</code> of the new instance will call the
0808: * SecurityManager's <code>checkPackageAccess()</code> before loading a
0809: * class.
0810: *
0811: * @param urls java.net.URL[] a list of URLs that is passed to the new
0812: * URLClassloader
0813: * @return java.net.URLClassLoader the new instance of
0814: * <code>URLClassLoader</code>
0815: */
0816: public static URLClassLoader newInstance(final URL[] urls) {
0817: URLClassLoader sub = AccessController
0818: .doPrivileged(new PrivilegedAction<URLClassLoader>() {
0819: public URLClassLoader run() {
0820: return new SubURLClassLoader(urls);
0821: }
0822: });
0823: sub.currentContext = AccessController.getContext();
0824: return sub;
0825: }
0826:
0827: /**
0828: * Answers an instance of <code>URLClassLoader</code>.
0829: * <code>loadClass()</code> of the new instance will call security
0830: * manager's <code>checkPackageAccess()</code> before loading a class.
0831: *
0832: * @param urls URL[] the list of URLs that is passed to the new
0833: * <code>URLClassloader</code>
0834: * @param parentCl ClassLoader the parent class loader that is passed to
0835: the new <code>URLClassloader</code>
0836: * @return URLClassLoader the new instance of <code>URLClassLoader</code>
0837: */
0838: public static URLClassLoader newInstance(final URL[] urls,
0839: final ClassLoader parentCl) {
0840: URLClassLoader sub = AccessController
0841: .doPrivileged(new PrivilegedAction<URLClassLoader>() {
0842: public URLClassLoader run() {
0843: return new SubURLClassLoader(urls, parentCl);
0844: }
0845: });
0846: sub.currentContext = AccessController.getContext();
0847: return sub;
0848: }
0849:
0850: /**
0851: * Constructs a new instance of this class. The newly created instance will
0852: * have the specified ClassLoader as its parent and use the specified
0853: * factory to create stream handlers. URLs that end with "/" are assumed to
0854: * be directories, otherwise they are assumed to be Jar files.
0855: *
0856: * @param searchUrls
0857: * java.net.URL[] the URLs that will be searched in the order
0858: * they were specified for resource
0859: *
0860: * @param parent
0861: * ClassLoader the ClassLoader name of the resource to find.
0862: *
0863: * @param factory
0864: * java.net.URLStreamHandlerFactory the factory that will used to
0865: * create stream (protocol) handlers
0866: * @throws SecurityException
0867: * if a security manager exists and its checkCreateClassLoader
0868: * method doesn't allow creation of new ClassLoaders
0869: */
0870: public URLClassLoader(URL[] searchUrls, ClassLoader parent,
0871: URLStreamHandlerFactory factory) {
0872: super (parent);
0873: // Required for pre-v1.2 security managers to work
0874: SecurityManager security = System.getSecurityManager();
0875: if (security != null) {
0876: security.checkCreateClassLoader();
0877: }
0878: this .factory = factory;
0879: // capture the context of the thread that creates this URLClassLoader
0880: currentContext = AccessController.getContext();
0881: int nbUrls = searchUrls.length;
0882: originalUrls = new ArrayList<URL>(nbUrls);
0883: handlerList = new ArrayList<URLHandler>(nbUrls);
0884: searchList = Collections.synchronizedList(new ArrayList<URL>(
0885: nbUrls));
0886: for (int i = 0; i < nbUrls; i++) {
0887: originalUrls.add(searchUrls[i]);
0888: try {
0889: searchList.add(createSearchURL(searchUrls[i]));
0890: } catch (MalformedURLException e) {
0891: }
0892: }
0893: }
0894:
0895: /**
0896: * Locates and loads the specified class, searching this URLClassLoader's
0897: * list of URLS.
0898: *
0899: * @param clsName String the name of the class.
0900: * @return Class the class that has been loaded.
0901: * @throws java.lang.ClassNotFoundException
0902: * if the class cannot be loaded
0903: */
0904: @Override
0905: protected Class<?> findClass(final String clsName)
0906: throws ClassNotFoundException {
0907: Class<?> cls = AccessController.doPrivileged(
0908: new PrivilegedAction<Class<?>>() {
0909: public Class<?> run() {
0910: return findClassImpl(clsName);
0911: }
0912: }, currentContext);
0913: if (cls != null) {
0914: return cls;
0915: }
0916: throw new ClassNotFoundException(clsName);
0917: }
0918:
0919: /**
0920: * Answers an URL that will be checked if it contains the class or resource.
0921: * If the file component of the URL is not a directory, a Jar URL will be
0922: * created.
0923: *
0924: * @return java.net.URL a test URL
0925: */
0926: private URL createSearchURL(URL url) throws MalformedURLException {
0927: if (url == null) {
0928: return url;
0929: }
0930:
0931: String protocol = url.getProtocol();
0932:
0933: if (isDirectory(url) || protocol.equals("jar")) { //$NON-NLS-1$
0934: return url;
0935: }
0936: if (factory == null) {
0937: return new URL("jar", "", //$NON-NLS-1$ //$NON-NLS-2$
0938: -1, url.toString() + "!/"); //$NON-NLS-1$
0939: }
0940: return new URL("jar", "", //$NON-NLS-1$ //$NON-NLS-2$
0941: -1, url.toString() + "!/", //$NON-NLS-1$
0942: factory.createURLStreamHandler(protocol));
0943: }
0944:
0945: /**
0946: * Answers a URL referencing the specified resource or null if no resource
0947: * could be found.
0948: *
0949: * @param name java.lang.String the name of the requested resource
0950: * @return URL URL for the resource.
0951: */
0952: @Override
0953: public URL findResource(final String name) {
0954: if (name == null) {
0955: return null;
0956: }
0957: URL result = AccessController.doPrivileged(
0958: new PrivilegedAction<URL>() {
0959: public URL run() {
0960: return findResourceImpl(name);
0961: }
0962: }, currentContext);
0963: SecurityManager sm;
0964: if (result != null
0965: && (sm = System.getSecurityManager()) != null) {
0966: try {
0967: sm.checkPermission(result.openConnection()
0968: .getPermission());
0969: } catch (IOException e) {
0970: return null;
0971: } catch (SecurityException e) {
0972: return null;
0973: }
0974: }
0975: return result;
0976: }
0977:
0978: /**
0979: * Answers a URL among the given ones referencing the specified resource or
0980: * null if no resource could be found.
0981: *
0982: * @param resName java.lang.String the name of the requested resource
0983: * @return URL URL for the resource.
0984: */
0985: URL findResourceImpl(String resName) {
0986: int n = 0;
0987:
0988: while (true) {
0989: URLHandler handler = getHandler(n++);
0990: if (handler == null) {
0991: break;
0992: }
0993: URL res = handler.findResource(resName);
0994: if (res != null) {
0995: return res;
0996: }
0997: }
0998: return null;
0999: }
1000:
1001: URLHandler getHandler(int num) {
1002: if (num < handlerList.size()) {
1003: return handlerList.get(num);
1004: }
1005: makeNewHandler();
1006: if (num < handlerList.size()) {
1007: return handlerList.get(num);
1008: }
1009: return null;
1010: }
1011:
1012: private synchronized void makeNewHandler() {
1013: while (!searchList.isEmpty()) {
1014: URL nextCandidate = searchList.remove(0);
1015: if (nextCandidate == null) { // KA024=One of urls is null
1016: throw new NullPointerException(Msg.getString("KA024")); //$NON-NLS-1$
1017: }
1018: if (!handlerMap.containsKey(nextCandidate)) {
1019: URLHandler result;
1020: String protocol = nextCandidate.getProtocol();
1021: if (protocol.equals("jar")) { //$NON-NLS-1$
1022: result = createURLJarHandler(nextCandidate);
1023: } else if (protocol.equals("file")) { //$NON-NLS-1$
1024: result = createURLFileHandler(nextCandidate);
1025: } else {
1026: result = createURLHandler(nextCandidate);
1027: }
1028: if (result != null) {
1029: handlerMap.put(nextCandidate, result);
1030: handlerList.add(result);
1031: return;
1032: }
1033: }
1034: }
1035: }
1036:
1037: private URLHandler createURLHandler(URL url) {
1038: return new URLHandler(url);
1039: }
1040:
1041: private URLHandler createURLFileHandler(URL url) {
1042: return new URLFileHandler(url);
1043: }
1044:
1045: private URLHandler createURLJarHandler(URL url) {
1046: String prefixName;
1047: String file = url.getFile();
1048: if (url.getFile().endsWith("!/")) { //$NON-NLS-1$
1049: prefixName = "";
1050: } else {
1051: int sepIdx = file.lastIndexOf("!/"); //$NON-NLS-1$
1052: if (sepIdx == -1) {
1053: // Invalid URL, don't look here again
1054: return null;
1055: }
1056: sepIdx += 2;
1057: prefixName = file.substring(sepIdx);
1058: }
1059: try {
1060: URL jarURL = ((JarURLConnection) url.openConnection())
1061: .getJarFileURL();
1062: JarURLConnection juc = (JarURLConnection) new URL(
1063: "jar", "", //$NON-NLS-1$ //$NON-NLS-2$
1064: jarURL.toExternalForm() + "!/").openConnection(); //$NON-NLS-1$
1065: JarFile jf = juc.getJarFile();
1066: URLJarHandler jarH = new URLJarHandler(url, jarURL, jf,
1067: prefixName);
1068: if (jarH.getIndex() == null) {
1069: try {
1070: Manifest manifest = jf.getManifest();
1071: if (manifest != null) {
1072: String classpath = manifest.getMainAttributes()
1073: .getValue(Attributes.Name.CLASS_PATH);
1074: if (classpath != null) {
1075: searchList.addAll(0, getInternalURLs(url,
1076: classpath));
1077: }
1078: }
1079: } catch (IOException e) {
1080: }
1081: }
1082: return jarH;
1083: } catch (IOException e) {
1084: }
1085: return null;
1086: }
1087:
1088: /**
1089: * Define a new Package using information extracted from the specified
1090: * Manifest.
1091: *
1092: * @param packageName The name of the package
1093: * @param manifest The Manifest for the Package
1094: * @param url The code source for the Package
1095: * @return The Package created
1096: * @throws IllegalArgumentException if the Package already exists
1097: */
1098: protected Package definePackage(String packageName,
1099: Manifest manifest, URL url) throws IllegalArgumentException {
1100: Attributes mainAttributes = manifest.getMainAttributes();
1101: String dirName = packageName.replace('.', '/') + "/"; //$NON-NLS-1$
1102: Attributes packageAttributes = manifest.getAttributes(dirName);
1103: boolean noEntry = false;
1104: if (packageAttributes == null) {
1105: noEntry = true;
1106: packageAttributes = mainAttributes;
1107: }
1108: String specificationTitle = packageAttributes
1109: .getValue(Attributes.Name.SPECIFICATION_TITLE);
1110: if (specificationTitle == null && !noEntry) {
1111: specificationTitle = mainAttributes
1112: .getValue(Attributes.Name.SPECIFICATION_TITLE);
1113: }
1114: String specificationVersion = packageAttributes
1115: .getValue(Attributes.Name.SPECIFICATION_VERSION);
1116: if (specificationVersion == null && !noEntry) {
1117: specificationVersion = mainAttributes
1118: .getValue(Attributes.Name.SPECIFICATION_VERSION);
1119: }
1120: String specificationVendor = packageAttributes
1121: .getValue(Attributes.Name.SPECIFICATION_VENDOR);
1122: if (specificationVendor == null && !noEntry) {
1123: specificationVendor = mainAttributes
1124: .getValue(Attributes.Name.SPECIFICATION_VENDOR);
1125: }
1126: String implementationTitle = packageAttributes
1127: .getValue(Attributes.Name.IMPLEMENTATION_TITLE);
1128: if (implementationTitle == null && !noEntry) {
1129: implementationTitle = mainAttributes
1130: .getValue(Attributes.Name.IMPLEMENTATION_TITLE);
1131: }
1132: String implementationVersion = packageAttributes
1133: .getValue(Attributes.Name.IMPLEMENTATION_VERSION);
1134: if (implementationVersion == null && !noEntry) {
1135: implementationVersion = mainAttributes
1136: .getValue(Attributes.Name.IMPLEMENTATION_VERSION);
1137: }
1138: String implementationVendor = packageAttributes
1139: .getValue(Attributes.Name.IMPLEMENTATION_VENDOR);
1140: if (implementationVendor == null && !noEntry) {
1141: implementationVendor = mainAttributes
1142: .getValue(Attributes.Name.IMPLEMENTATION_VENDOR);
1143: }
1144:
1145: return definePackage(packageName, specificationTitle,
1146: specificationVersion, specificationVendor,
1147: implementationTitle, implementationVersion,
1148: implementationVendor, isSealed(manifest, dirName) ? url
1149: : null);
1150: }
1151:
1152: private boolean isSealed(Manifest manifest, String dirName) {
1153: Attributes mainAttributes = manifest.getMainAttributes();
1154: String value = mainAttributes.getValue(Attributes.Name.SEALED);
1155: boolean sealed = value != null
1156: && value.toLowerCase().equals("true"); //$NON-NLS-1$
1157: Attributes attributes = manifest.getAttributes(dirName);
1158: if (attributes != null) {
1159: value = attributes.getValue(Attributes.Name.SEALED);
1160: if (value != null) {
1161: sealed = value.toLowerCase().equals("true"); //$NON-NLS-1$
1162: }
1163: }
1164: return sealed;
1165: }
1166:
1167: /**
1168: * returns URLs referenced in the string classpath.
1169: *
1170: * @param root the jar URL that classpath is related to
1171: * @param classpath the relative URLs separated by spaces
1172: * @return URL[] the URLs contained in the string classpath.
1173: */
1174: private ArrayList<URL> getInternalURLs(URL root, String classpath) {
1175: // Class-path attribute is composed of space-separated values.
1176: StringTokenizer tokenizer = new java.util.StringTokenizer(
1177: classpath);
1178: ArrayList<URL> addedURLs = new ArrayList<URL>();
1179: String file = root.getFile();
1180: int jarIndex = file.lastIndexOf("!/") - 1; //$NON-NLS-1$
1181: int index = file.lastIndexOf("/", jarIndex) + 1; //$NON-NLS-1$
1182: if (index == 0) {
1183: index = file.lastIndexOf(System
1184: .getProperty("file.separator"), jarIndex) + 1; //$NON-NLS-1$
1185: }
1186: file = file.substring(0, index);
1187: String protocol = root.getProtocol();
1188: String host = root.getHost();
1189: int port = root.getPort();
1190: while (tokenizer.hasMoreElements()) {
1191: String element = tokenizer.nextToken();
1192: if (!element.equals("")) { //$NON-NLS-1$
1193: try {
1194: URL newURL = new URL(protocol, host, port, file
1195: + element + "!/"); //$NON-NLS-1$
1196: addedURLs.add(newURL);
1197: } catch (MalformedURLException e) {
1198: // Nothing is added
1199: }
1200: }
1201: }
1202: return addedURLs;
1203: }
1204:
1205: Class<?> findClassImpl(String className) {
1206: Class<?> loadedClass = findLoadedClass(className);
1207: if (null != loadedClass) {
1208: return loadedClass;
1209: }
1210: String partialName = className.replace('.', '/');
1211: final String classFileName = new StringBuilder(partialName)
1212: .append(".class").toString(); //$NON-NLS-1$
1213: String packageName = null;
1214: int position = partialName.lastIndexOf('/');
1215: if ((position = partialName.lastIndexOf('/')) != -1) {
1216: packageName = partialName.substring(0, position);
1217: }
1218: int n = 0;
1219: while (true) {
1220: URLHandler handler = getHandler(n++);
1221: if (handler == null) {
1222: break;
1223: }
1224: Class<?> res = handler.findClass(packageName,
1225: classFileName, className);
1226: if (res != null) {
1227: return res;
1228: }
1229: }
1230: return null;
1231:
1232: }
1233:
1234: }
|