001/*****************************************************************************
002 * Copyright (C) PicoContainer Organization. All rights reserved.            *
003 * ------------------------------------------------------------------------- *
004 * The software in this package is published under the terms of the BSD      *
005 * style license a copy of which has been included with this distribution in *
006 * the LICENSE.txt file.                                                     *
007 *                                                                           *
008 *****************************************************************************/
009
010package org.picocontainer.security;
011
012import java.net.URL;
013import java.net.URLClassLoader;
014import java.security.AccessController;
015import java.security.CodeSource;
016import java.security.PermissionCollection;
017import java.security.Permissions;
018import java.security.PrivilegedAction;
019import java.util.Map;
020
021/**
022 * CustomPermissionsURLClassLoader extends URLClassLoader, adding the abilty to programatically add permissions easily.
023 * To be effective for permission management, it should be run in conjunction with a policy that restricts
024 * some of the classloaders, but not all.
025 * It's not ordinarily used by PicoContainer, but is here because PicoContainer is common
026 * to most classloader trees.
027 * 
028 * @author Paul Hammant
029 */
030public class CustomPermissionsURLClassLoader extends URLClassLoader {
031    private final Map<URL, Permissions> permissionsMap;
032
033    public CustomPermissionsURLClassLoader(URL[] urls, Map<URL, Permissions> permissionsMap, ClassLoader parent) {
034        super(urls, parent);
035        this.permissionsMap = permissionsMap;
036    }
037
038    public Class<?> loadClass(String name) throws ClassNotFoundException {
039        try {
040            return super.loadClass(name);
041        } catch (ClassNotFoundException e) {
042            throw decorateException(name, e);
043        }
044    }
045
046    protected Class<?> findClass(String name) throws ClassNotFoundException {
047        try {
048            return super.findClass(name);
049        } catch (ClassNotFoundException e) {
050            throw decorateException(name, e);
051        }
052    }
053
054    private ClassNotFoundException decorateException(String name, ClassNotFoundException e) {
055        if (name.startsWith("class ")) {
056            return new ClassNotFoundException("Class '" + name + "' is not a classInstance.getName(). " +
057                    "It's a classInstance.toString(). The clue is that it starts with 'class ', no classname contains a space.");
058        }
059        ClassLoader classLoader = this;
060        StringBuffer sb = new StringBuffer("'").append(name).append("' classloader stack [");
061        while (classLoader != null) {
062            sb.append(classLoader.toString()).append("\n");
063            final ClassLoader cl = classLoader;
064            classLoader = AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
065                public ClassLoader run() {
066                    return cl.getParent();
067                }
068            });
069
070        }
071        return new ClassNotFoundException(sb.append("]").toString(), e);
072    }
073
074    public String toString() {
075        String result = CustomPermissionsURLClassLoader.class.getName() + " " + System.identityHashCode(this) + ":";
076        URL[] urls = getURLs();
077        for (URL url : urls) {
078            result += "\n\t" + url.toString();
079        }
080
081        return result;
082    }
083
084    public PermissionCollection getPermissions(CodeSource codeSource) {
085        return (Permissions) permissionsMap.get(codeSource.getLocation());
086    }
087
088}
089