blob: 72f62c5ec1c1f4dc74940cc33dbea6d1d623c5d7 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.openide.execution;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLClassLoader;
import java.security.AllPermission;
import java.security.CodeSource;
import java.security.Permission;
import java.security.PermissionCollection;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.jar.Manifest;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileSystem;
import org.openide.filesystems.FileUtil;
import org.openide.filesystems.URLMapper;
import org.openide.util.Exceptions;
import org.openide.util.Lookup;
import org.openide.windows.InputOutput;
/** A class loader which is capable of loading classes from the Repository.
* XXX the only useful thing this class does is effectively make
* ExecutionEngine.createPermissions public! Consider deprecating this class...
* @see <a href="@org-netbeans-api-java-classpath@/org/netbeans/api/java/classpath/ClassPath.html#getClassLoader(boolean)"><code>ClassPath.getClassLoader(...)</code></a>
* @author Ales Novak, Petr Hamernik, Jaroslav Tulach, Ian Formanek
*/
public class NbClassLoader extends URLClassLoader {
/** I/O for classes defined by this classloader. May be <code>null</code>. */
protected InputOutput inout;
/** Cached PermissionCollections returned from ExecutionEngine. */
private HashMap permissionCollections;
/** Default permissions */
private PermissionCollection defaultPermissions;
/** Works onthe top of file (jar:file) - directly delegates to URLClassLoader*/
private final boolean fast;
private static ClassLoader systemClassLoader() {
return (ClassLoader)Lookup.getDefault().lookup(ClassLoader.class);
}
/** Create a new class loader retrieving classes from the core IDE as well as the Repository.
* @see FileSystemCapability#EXECUTE
* @see FileSystemCapability#fileSystems
* @deprecated Misuses classpath.
*/
public NbClassLoader () {
super(new URL[0], systemClassLoader());
fast = false;
}
/** Create a new class loader retrieving classes from the core IDE as well as the Repository,
* and redirecting system I/O.
* @param io an I/O tab in the Output Window
* @see org.openide.filesystems.Repository#getFileSystems
* @deprecated Misuses classpath.
*/
public NbClassLoader(InputOutput io) {
super(new URL[0], systemClassLoader());
fast = false;
inout = io;
}
/**
* Create a new class loader retrieving classes from a set of package roots.
* @param roots a set of package roots
* @param parent a parent loader
* @param io an I/O tab in the Output Window, or null
* @since XXX
*/
static ThreadLocal<Boolean> f = new ThreadLocal<Boolean>();
public NbClassLoader(FileObject[] roots, ClassLoader parent, InputOutput io) {
super(createRootURLs(roots), parent);
fast = canOptimize(getURLs());
inout = io;
}
/** Create a new class loader retrieving classes from the core IDE as well as specified file systems.
* @param fileSystems file systems to load classes from
* @deprecated Misuses classpath.
*/
public NbClassLoader (FileSystem[] fileSystems) {
super(new URL[0], systemClassLoader(), null);
fast = false;
Thread.dumpStack();
}
/** Create a new class loader.
* @param fileSystems file systems to load classes from
* @param parent fallback class loader
* @deprecated Misuses classpath.
*/
public NbClassLoader (FileSystem[] fileSystems, ClassLoader parent) {
super(new URL[0], parent);
fast = false;
Thread.dumpStack();
}
/** Create a URL to a resource specified by name.
* Same behavior as in the super method, but handles names beginning with a slash.
* @param name resource name
* @return URL to that resource or <code>null</code>
*/
public URL getResource (String name) {
return super.getResource (name.startsWith ("/") ? name.substring (1) : name); // NOI18N
}
/* Needs to be overridden so that packages are correctly defined
based on manifest for e.g. JarFileSystem's in the repository.
Otherwise URLClassLoader, not understanding nbfs:/..../foo.jar,
would simply define packages loaded from such a URL with no
particular info. We want it to have specification version and
all that good stuff. */
protected Class findClass (final String name) throws ClassNotFoundException {
if (!fast && name.indexOf ('.') != -1) {
Logger.getLogger(NbClassLoader.class.getName()).log(Level.FINE, "NBFS used!");
String pkg = name.substring (0, name.lastIndexOf ('.'));
if (getPackage (pkg) == null) {
String resource = name.replace ('.', '/') + ".class"; // NOI18N
URL[] urls = getURLs ();
for (int i = 0; i < urls.length; i++) {
// System.err.println (urls[i].toString ());
FileObject root = URLMapper.findFileObject(urls[i]);
if (root == null) {
continue;
}
try {
FileObject fo = root.getFileObject(resource);
if (fo != null) {
// Got it. If there is an associated manifest, load it.
FileObject manifo = root.getFileObject("META-INF/MANIFEST.MF");
if (manifo == null)
manifo = root.getFileObject("meta-inf/manifest.mf");
if (manifo != null) {
// System.err.println (manifo.toString () + " " + manifo.getClass ().getName () + " " + manifo.isValid ());
Manifest mani = new Manifest();
InputStream is = manifo.getInputStream();
try {
mani.read(is);
}
finally {
is.close();
}
definePackage(pkg, mani, urls[i]);
}
break;
}
}
catch (IOException ioe) {
Exceptions.attachLocalizedMessage(ioe,
urls[i].toString());
Exceptions.printStackTrace(ioe);
continue;
}
}
}
}
return super.findClass (name);
}
/** Sets a PermissionsCollectio which will be used
* for ProtectionDomain of newly created classes.
*
* @param defaultPerms
*/
public void setDefaultPermissions(PermissionCollection defaultPerms) {
if (defaultPerms != null && !defaultPerms.isReadOnly()) {
defaultPerms.setReadOnly();
}
this.defaultPermissions = defaultPerms;
}
/* @return a PermissionCollection for given CodeSource. */
protected final synchronized PermissionCollection getPermissions(CodeSource cs) {
if (permissionCollections != null) {
PermissionCollection pc = (PermissionCollection) permissionCollections.get(cs);
if (pc != null) {
return pc;
}
}
return createPermissions(cs, inout);
}
/**
* @param cs CodeSource
* @param inout InputOutput passed to @seeExecutionEngine#createPermissions(java.security.CodeSource, org.openide.windows.InpuOutput).
* @return a PermissionCollection for given CodeSource.
*/
private PermissionCollection createPermissions(CodeSource cs, InputOutput inout) {
PermissionCollection pc;
if (inout == null) {
if (defaultPermissions != null) {
pc = defaultPermissions;
} else {
pc = super.getPermissions(cs);
}
} else {
ExecutionEngine engine = ExecutionEngine.getDefault();
pc = engine.createPermissions(cs, inout);
if (defaultPermissions != null) {
addAllPermissions(pc, defaultPermissions);
} else {
pc.add(new AllPermission());
}
}
if (permissionCollections == null) {
permissionCollections = new HashMap(7);
}
permissionCollections.put(cs, pc);
return pc;
}
/**
* Copies all permissions from <tt>src</tt> into <tt>target</tt>
*
* @param target To where put permissions
* @param src From where take paermissions
*/
private static void addAllPermissions(PermissionCollection target, PermissionCollection src) {
Enumeration e = src.elements();
while (e.hasMoreElements()) {
target.add((Permission) e.nextElement());
}
}
/**
* Creates URLs for file objects.
* @param roots file roots
* @return array of URLs
*/
private static URL[] createRootURLs(FileObject[] roots) {
URL[] urls = new URL[roots.length];
for (int i = 0; i < roots.length; i++) {
urls[i] = roots[i].toURL();
}
return urls;
}
private static boolean canOptimize (URL[] urls) {
assert urls != null;
for (int i=0; i<urls.length; i++) {
URL url = urls[i];
URL au = FileUtil.getArchiveFile(url);
if (au != null) {
if (!url.toExternalForm().endsWith("!/")) { //NOI18N
//Nested path - not supported fast mode
return false;
}
url = au;
}
if (!"file".equals(url.getProtocol())) { //NOI18N
//Not file - not supported fast mode
return false;
}
}
return true;
}
}