? startup/test/unit/src/org/netbeans/core/startup/jars/cyclic-1/org/bar Index: arch/arch-core-launcher.xml =================================================================== RCS file: /cvs/core/arch/arch-core-launcher.xml,v retrieving revision 1.31 diff -u -r1.31 arch-core-launcher.xml --- arch/arch-core-launcher.xml 22 Aug 2005 14:17:08 -0000 1.31 +++ arch/arch-core-launcher.xml 22 Aug 2005 19:59:06 -0000 @@ -738,6 +738,15 @@

+ +

+ Instances of org.netbeans.ModuleFactory are found using + services lookup in the "dynamic classpath loader" (i.e. startup + classpath plus ${netbeans.home}/core/*.jar). ModuleFactory + can be used to create alternative module implementations. +

+
+

The communication between core.jar and rest of the platform code Index: bootstrap/src/org/netbeans/FixedModule.java =================================================================== RCS file: bootstrap/src/org/netbeans/FixedModule.java diff -N bootstrap/src/org/netbeans/FixedModule.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ bootstrap/src/org/netbeans/FixedModule.java 22 Aug 2005 19:59:07 -0000 @@ -0,0 +1,263 @@ +/* + * Sun Public License Notice + * + * The contents of this file are subject to the Sun Public License + * Version 1.0 (the "License"). You may not use this file except in + * compliance with the License. A copy of the License is available at + * http://www.sun.com/ + * + * The Original Code is NetBeans. The Initial Developer of the Original + * Code is Sun Microsystems, Inc. Portions Copyright 1997-2004 Sun + * Microsystems, Inc. All Rights Reserved. + */ + +package org.netbeans; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.MissingResourceException; +import java.util.Properties; +import java.util.ResourceBundle; +import java.util.Set; +import java.util.jar.Attributes; +import java.util.jar.Manifest; +import org.openide.ErrorManager; +import org.openide.util.NbBundle; + + +/** Object representing one module, possibly installed. + * Responsible for opening of module JAR file; reading + * manifest; parsing basic information such as dependencies; + * and creating a classloader for use by the installer. + * Methods not defined in ModuleInfo must be called from within + * the module manager's read mutex as a rule. + * @author Jesse Glick + */ +public final class FixedModule extends Module { + + /** localized properties, only non-null if requested from disabled module */ + private Properties localizedProps; + + /** Map from extension JARs to sets of JAR that load them via Class-Path. + * Used only for debugging purposes, so that a warning is printed if two + * different modules try to load the same extension (which would cause them + * to both load their own private copy, which may not be intended). + */ + private static final Map extensionOwners = new HashMap(); // Map> + /** Simple registry of JAR files used as modules. + * Used only for debugging purposes, so that we can be sure + * that no one is using Class-Path to refer to other modules. + */ + private static final Set moduleJARs = new HashSet(); // Set + + /** Set of locale-variants JARs for this module (or null). + * Added explicitly to classloader, and can be used by execution engine. + */ + private Set localeVariants = null; // Set + /** Set of extension JARs that this module loads via Class-Path (or null). + * Can be used e.g. by execution engine. (#9617) + */ + private Set plainExtensions = null; // Set + /** Set of localized extension JARs derived from plainExtensions (or null). + * Used to add these to the classloader. (#9348) + * Can be used e.g. by execution engine. + */ + private Set localeExtensions = null; // Set + /** Patches added at the front of the classloader (or null). + * Files are assumed to be JARs; directories are themselves. + */ + private Set patches = null; // Set + + /** Create a special-purpose "fixed" JAR. */ + public FixedModule(ModuleManager mgr, Events ev, Manifest manifest, Object history, ClassLoader classloader) throws InvalidException { + super(mgr, ev, manifest, history, classloader); + loadLocalizedPropsClasspath(); + parseManifest(); + } + + /** Get a localized attribute. + * First, if OpenIDE-Module-Localizing-Bundle was given, the specified + * bundle file (in all locale JARs as well as base JAR) is searched for + * a key of the specified name. + * Otherwise, the manifest's main attributes are searched for an attribute + * with the specified name, possibly with a locale suffix. + * If the attribute name contains a slash, and there is a manifest section + * named according to the part before the last slash, then this section's attributes + * are searched instead of the main attributes, and for the attribute listed + * after the slash. Currently this would only be useful for localized filesystem + * names. E.g. you may request the attribute org/foo/MyFileSystem.class/Display-Name. + * In the future certain attributes known to be dangerous could be + * explicitly suppressed from this list; should only be used for + * documented localizable attributes such as OpenIDE-Module-Name etc. + */ + public Object getLocalizedAttribute(String attr) { + String locb = getManifest().getMainAttributes().getValue("OpenIDE-Module-Localizing-Bundle"); // NOI18N + boolean usingLoader = false; + if (locb != null) { + if (classloader != null) { + if (locb.endsWith(".properties")) { // NOI18N + usingLoader = true; + String basename = locb.substring(0, locb.length() - 11).replace('/', '.'); + try { + ResourceBundle bundle = NbBundle.getBundle(basename, Locale.getDefault(), classloader); + try { + return bundle.getString(attr); + } catch (MissingResourceException mre) { + // Fine, ignore. + } + } catch (MissingResourceException mre) { + Util.err.notify(mre); + } + } else { + Util.err.log(ErrorManager.WARNING, "WARNING - cannot efficiently load non-*.properties OpenIDE-Module-Localizing-Bundle: " + locb); + } + } + if (!usingLoader) { + if (localizedProps != null) { + String val = localizedProps.getProperty(attr); + if (val != null) { + return val; + } + } + } + } + // Try in the manifest now. + int idx = attr.lastIndexOf('/'); // NOI18N + if (idx == -1) { + // Simple main attribute. + return NbBundle.getLocalizedValue(getManifest().getMainAttributes(), new Attributes.Name(attr)); + } else { + // Attribute of a manifest section. + String section = attr.substring(0, idx); + String realAttr = attr.substring(idx + 1); + Attributes attrs = getManifest().getAttributes(section); + if (attrs != null) { + return NbBundle.getLocalizedValue(attrs, new Attributes.Name(realAttr)); + } else { + return null; + } + } + } + + public boolean isFixed() { + return true; + } + + /** Similar, but for fixed modules only. + * Should be very rarely used: only for classpath modules with a strangely + * named OpenIDE-Module-Localizing-Bundle (not *.properties). + */ + private void loadLocalizedPropsClasspath() throws InvalidException { + Attributes attr = manifest.getMainAttributes(); + String locbundle = attr.getValue("OpenIDE-Module-Localizing-Bundle"); // NOI18N + if (locbundle != null) { + Util.err.log("Localized props in " + locbundle + " for " + attr.getValue("OpenIDE-Module")); + try { + int idx = locbundle.lastIndexOf('.'); // NOI18N + String name, ext; + if (idx == -1) { + name = locbundle; + ext = ""; // NOI18N + } else { + name = locbundle.substring(0, idx); + ext = locbundle.substring(idx); + } + List suffixes = new ArrayList(10); + Iterator it = NbBundle.getLocalizingSuffixes(); + while (it.hasNext()) { + suffixes.add(it.next()); + } + Collections.reverse(suffixes); + it = suffixes.iterator(); + while (it.hasNext()) { + String suffix = (String)it.next(); + String resource = name + suffix + ext; + InputStream is = classloader.getResourceAsStream(resource); + if (is != null) { + Util.err.log("Found " + resource); + if (localizedProps == null) { + localizedProps = new Properties(); + } + localizedProps.load(is); + } + } + if (localizedProps == null) { + throw new IOException("Could not find localizing bundle: " + locbundle); // NOI18N + } + } catch (IOException ioe) { + InvalidException e = new InvalidException(ioe.toString()); + Util.err.annotate(e, ioe); + throw e; + } + } + } + + /** Get all JARs loaded by this module. + * Includes the module itself, any locale variants of the module, + * any extensions specified with Class-Path, any locale variants + * of those extensions. + * The list will be in classpath order (patches first). + * Currently the temp JAR is provided in the case of test modules, to prevent + * sporadic ZIP file exceptions when background threads (like Java parsing) tries + * to open libraries found in the library path. + * JARs already present in the classpath are not listed. + * @return a List<File> of JARs + */ + public List getAllJars() { + return Collections.EMPTY_LIST; + } + + /** Set whether this module is supposed to be reloadable. + * Has no immediate effect, only impacts what happens the + * next time it is enabled (after having been disabled if + * necessary). + * Must be called from within a write mutex. + * @param r whether the module should be considered reloadable + */ + public void setReloadable(boolean r) { + throw new IllegalStateException(); + } + + /** Reload this module. Access from ModuleManager. + * If an exception is thrown, the module is considered + * to be in an invalid state. + */ + public void reload() throws IOException { + throw new IllegalStateException(); + } + + // Access from ModuleManager: + /** Turn on the classloader. Passed a list of parent modules to use. + * The parents should already have had their classloaders initialized. + */ + public void classLoaderUp(Set parents) throws IOException { + return; // no need + } + + /** Turn off the classloader and release all resources. */ + public void classLoaderDown() { + return; // don't touch it + } + /** Should be called after turning off the classloader of one or more modules & GC'ing. */ + public void cleanup() { + return; // don't touch it + } + + /** Notify the module that it is being deleted. */ + public void destroy() { + } + + /** String representation for debugging. */ + public String toString() { + String s = "FixedModule:" + getCodeNameBase(); // NOI18N + if (!isValid()) s += "[invalid]"; // NOI18N + return s; + } +} Index: bootstrap/src/org/netbeans/Module.java =================================================================== RCS file: /cvs/core/bootstrap/src/org/netbeans/Module.java,v retrieving revision 1.5 diff -u -r1.5 Module.java --- bootstrap/src/org/netbeans/Module.java 11 Aug 2005 18:33:57 -0000 1.5 +++ bootstrap/src/org/netbeans/Module.java 22 Aug 2005 19:59:09 -0000 @@ -15,31 +15,19 @@ import java.io.File; import java.io.IOException; -import java.io.InputStream; import java.security.AllPermission; -import java.security.CodeSource; import java.security.PermissionCollection; import java.security.Permissions; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; -import java.util.HashMap; import java.util.HashSet; -import java.util.Iterator; import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.MissingResourceException; import java.util.Properties; -import java.util.ResourceBundle; import java.util.Set; import java.util.StringTokenizer; import java.util.jar.Attributes; -import java.util.jar.JarFile; import java.util.jar.Manifest; -import java.util.zip.ZipEntry; -import org.netbeans.JarClassLoader; -import org.netbeans.ProxyClassLoader; import org.openide.ErrorManager; import org.openide.modules.Dependency; import org.openide.modules.ModuleInfo; @@ -55,7 +43,7 @@ * the module manager's read mutex as a rule. * @author Jesse Glick */ -public final class Module extends ModuleInfo { +public abstract class Module extends ModuleInfo { public static final String PROP_RELOADABLE = "reloadable"; // NOI18N public static final String PROP_CLASS_LOADER = "classLoader"; // NOI18N @@ -63,28 +51,24 @@ public static final String PROP_VALID = "valid"; // NOI18N public static final String PROP_PROBLEMS = "problems"; // NOI18N + /** module manifest */ + protected Manifest manifest; /** manager which owns this module */ - private final ModuleManager mgr; + protected final ModuleManager mgr; /** event logging (should not be much here) */ - private final Events ev; + protected final Events events; /** associated history object * @see ModuleHistory */ private final Object history; - /** JAR file holding the module */ - private final File jar; - /** if reloadable, temporary JAR file actually loaded from */ - private File physicalJar = null; /** true if currently enabled; manipulated by ModuleManager */ private boolean enabled; - /** whether it is supposed to be easily reloadable */ - private boolean reloadable; /** whether it is supposed to be automatically loaded when required */ private final boolean autoload; + /** */ + protected boolean reloadable; /** if true, this module is eagerly turned on whenever it can be */ private final boolean eager; - /** module manifest */ - private Manifest manifest; /** code name base (no slash) */ private String codeNameBase; /** code name release, or -1 if undefined */ @@ -98,7 +82,7 @@ /** specification version parsed from manifest, or null */ private SpecificationVersion specVers; /** currently active module classloader */ - private ClassLoader classloader = null; + protected ClassLoader classloader = null; /** localized properties, only non-null if requested from disabled module */ private Properties localizedProps; /** public packages, may be null */ @@ -106,73 +90,29 @@ /** Set of CNBs of friend modules or null */ private Set/**/ friendNames; - /** Map from extension JARs to sets of JAR that load them via Class-Path. - * Used only for debugging purposes, so that a warning is printed if two - * different modules try to load the same extension (which would cause them - * to both load their own private copy, which may not be intended). - */ - private static final Map extensionOwners = new HashMap(); // Map> - /** Simple registry of JAR files used as modules. - * Used only for debugging purposes, so that we can be sure - * that no one is using Class-Path to refer to other modules. - */ - private static final Set moduleJARs = new HashSet(); // Set - - /** Set of locale-variants JARs for this module (or null). - * Added explicitly to classloader, and can be used by execution engine. - */ - private Set localeVariants = null; // Set - /** Set of extension JARs that this module loads via Class-Path (or null). - * Can be used e.g. by execution engine. (#9617) - */ - private Set plainExtensions = null; // Set - /** Set of localized extension JARs derived from plainExtensions (or null). - * Used to add these to the classloader. (#9348) - * Can be used e.g. by execution engine. - */ - private Set localeExtensions = null; // Set - /** Patches added at the front of the classloader (or null). - * Files are assumed to be JARs; directories are themselves. - */ - private Set patches = null; // Set - /** Use ModuleManager.create as a factory. */ - Module(ModuleManager mgr, Events ev, File jar, Object history, boolean reloadable, boolean autoload, boolean eager) throws IOException { + public Module(ModuleManager mgr, Events ev, Object history, boolean reloadable, boolean autoload, boolean eager) throws IOException { if (autoload && eager) throw new IllegalArgumentException("A module may not be both autoload and eager"); // NOI18N this.mgr = mgr; - this.ev = ev; - this.jar = jar; + this.events = ev; this.history = history; this.reloadable = reloadable; this.autoload = autoload; this.eager = eager; enabled = false; - loadManifest(); - parseManifest(); - findExtensionsAndVariants(manifest); - // Check if some other module already listed this one in Class-Path. - // For the chronologically reverse case, see findExtensionsAndVariants(). - Set bogoOwners = (Set)extensionOwners.get(jar); - if (bogoOwners != null) { - Util.err.log(ErrorManager.WARNING, "WARNING - module " + jar + " was incorrectly placed in the Class-Path of other JARs " + bogoOwners + "; please use OpenIDE-Module-Module-Dependencies instead"); - } - moduleJARs.add(jar); } /** Create a special-purpose "fixed" JAR. */ - Module(ModuleManager mgr, Events ev, Manifest manifest, Object history, ClassLoader classloader) throws InvalidException { + public Module(ModuleManager mgr, Events ev, Manifest manifest, Object history, ClassLoader classloader) throws InvalidException { this.mgr = mgr; - this.ev = ev; + this.events = ev; this.manifest = manifest; this.history = history; this.classloader = classloader; - jar = null; reloadable = false; autoload = false; eager = false; enabled = false; - //loadLocalizedPropsClasspath(); - parseManifest(); } /** Get the associated module manager. */ @@ -186,9 +126,8 @@ // Access from ModuleManager: void setEnabled(boolean enabled) { - /* #13647: actually can happen if loading of bootstrap modules is rolled back: + /* #13647: actually can happen if loading of bootstrap modules is rolled back: */ if (isFixed() && ! enabled) throw new IllegalStateException("Cannot disable a fixed module: " + this); // NOI18N - */ this.enabled = enabled; } @@ -250,75 +189,7 @@ * explicitly suppressed from this list; should only be used for * documented localizable attributes such as OpenIDE-Module-Name etc. */ - public Object getLocalizedAttribute(String attr) { - String locb = manifest.getMainAttributes().getValue("OpenIDE-Module-Localizing-Bundle"); // NOI18N - boolean usingLoader = false; - if (locb != null) { - if (classloader != null) { - if (locb.endsWith(".properties")) { // NOI18N - usingLoader = true; - String basename = locb.substring(0, locb.length() - 11).replace('/', '.'); - try { - ResourceBundle bundle = NbBundle.getBundle(basename, Locale.getDefault(), classloader); - try { - return bundle.getString(attr); - } catch (MissingResourceException mre) { - // Fine, ignore. - } - } catch (MissingResourceException mre) { - Util.err.notify(mre); - } - } else { - Util.err.log(ErrorManager.WARNING, "WARNING - cannot efficiently load non-*.properties OpenIDE-Module-Localizing-Bundle: " + locb); - } - } - if (!usingLoader) { - if (localizedProps == null) { - Util.err.log("Trying to get localized attr " + attr + " from disabled module " + getCodeNameBase()); - try { - if (jar != null) { - JarFile jarFile = new JarFile(jar, false); - try { - loadLocalizedProps(jarFile, manifest); - } finally { - jarFile.close(); - } - } else if (classloader != null) { - loadLocalizedPropsClasspath(); - } else { - throw new IllegalStateException(); - } - } catch (IOException ioe) { - Util.err.annotate(ioe, ErrorManager.INFORMATIONAL, jar.getAbsolutePath(), null, null, null); - Util.err.notify(ErrorManager.INFORMATIONAL, ioe); - if (localizedProps == null) { - localizedProps = new Properties(); - } - } - } - String val = localizedProps.getProperty(attr); - if (val != null) { - return val; - } - } - } - // Try in the manifest now. - int idx = attr.lastIndexOf('/'); // NOI18N - if (idx == -1) { - // Simple main attribute. - return NbBundle.getLocalizedValue(getManifest().getMainAttributes(), new Attributes.Name(attr)); - } else { - // Attribute of a manifest section. - String section = attr.substring(0, idx); - String realAttr = attr.substring(idx + 1); - Attributes attrs = getManifest().getAttributes(section); - if (attrs != null) { - return NbBundle.getLocalizedValue(attrs, new Attributes.Name(realAttr)); - } else { - return null; - } - } - } + public abstract Object getLocalizedAttribute(String attr); public String getCodeName() { return codeName; @@ -393,14 +264,14 @@ * If anything is in an invalid format, throws an exception with * some kind of description of the problem. */ - private void parseManifest() throws InvalidException { + public void parseManifest() throws InvalidException { Attributes attr = manifest.getMainAttributes(); // Code name codeName = attr.getValue("OpenIDE-Module"); // NOI18N if (codeName == null) { - InvalidException e = new InvalidException("Not a module: no OpenIDE-Module tag in manifest of " + /* #17629: important! */jar); // NOI18N + InvalidException e = new InvalidException("Not a module: no OpenIDE-Module tag in manifest of " + /* #17629: important! */this); // NOI18N // #29393: plausible user mistake, deal with it politely. - Util.err.annotate(e, NbBundle.getMessage(Module.class, "EXC_not_a_module", jar.getAbsolutePath())); + Util.err.annotate(e, NbBundle.getMessage(Module.class, "EXC_not_a_module", this.toString())); throw e; } try { @@ -550,311 +421,6 @@ } } - /** Get the JAR this module is packaged in. - * May be null for modules installed specially, e.g. - * automatically from the classpath. - * @see #isFixed - */ - public File getJarFile() { - return jar; - } - - /** Check if this is a "fixed" module. - * Fixed modules are installed automatically (e.g. based on classpath) - * and cannot be uninstalled or manipulated in any way. - */ - public boolean isFixed() { - return jar == null; - } - - /** Create a temporary test JAR if necessary. - * This is primarily necessary to work around a Java bug, - * #4405789, which might be fixed in 1.4--check up on this. - */ - private void ensurePhysicalJar() throws IOException { - if (reloadable && physicalJar == null) { - physicalJar = Util.makeTempJar(jar); - } - } - private void destroyPhysicalJar() { - if (physicalJar != null) { - if (physicalJar.isFile()) { - if (! physicalJar.delete()) { - Util.err.log(ErrorManager.WARNING, "Warning: temporary JAR " + physicalJar + " not currently deletable."); - } else { - Util.err.log("deleted: " + physicalJar); - } - } - physicalJar = null; - } else { - Util.err.log("no physicalJar to delete for " + this); - } - } - - /** Open the JAR, load its manifest, and do related things. */ - private void loadManifest() throws IOException { - Util.err.log("loading manifest of " + jar); - File jarBeingOpened = null; // for annotation purposes - try { - if (reloadable) { - // Never try to cache reloadable JARs. - jarBeingOpened = physicalJar; // might be null - ensurePhysicalJar(); - jarBeingOpened = physicalJar; // might have changed - JarFile jarFile = new JarFile(physicalJar, false); - try { - Manifest m = jarFile.getManifest(); - if (m == null) throw new IOException("No manifest found in " + physicalJar); // NOI18N - manifest = m; - } finally { - jarFile.close(); - } - } else { - jarBeingOpened = jar; - manifest = mgr.loadManifest(jar); - } - } catch (IOException e) { - if (jarBeingOpened != null) { - Util.err.annotate(e, ErrorManager.UNKNOWN, "While loading manifest from: " + jarBeingOpened, null, null, null); // NOI18N - } - throw e; - } - } - - /** Find any extensions loaded by the module, as well as any localized - * variants of the module or its extensions. - */ - private void findExtensionsAndVariants(Manifest m) { - assert jar != null : "Cannot load extensions from classpath module " + codeNameBase; - localeVariants = null; - List l = Util.findLocaleVariantsOf(jar, false); - if (!l.isEmpty()) localeVariants = new HashSet(l); - plainExtensions = null; - localeExtensions = null; - String classPath = m.getMainAttributes().getValue(Attributes.Name.CLASS_PATH); - if (classPath != null) { - StringTokenizer tok = new StringTokenizer(classPath); - while (tok.hasMoreTokens()) { - String ext = tok.nextToken(); - if (new File(ext).isAbsolute() || ext.indexOf("../") != -1) { // NOI18N - if (ext.equals("../lib/updater.jar")) { // NOI18N - // Special case, see #24703. - // JAR is special to the launcher, so it makes sense in lib/ rather - // than modules/ext/. However updater.jar is not in startup classpath, - // so autoupdate module explicitly declares it this way. - } else { - Util.err.log(ErrorManager.WARNING, "WARNING: Class-Path value " + ext + " from " + jar + " is illegal according to the Java Extension Mechanism: must be relative and not move up directories"); - } - } - File extfile = new File(jar.getParentFile(), ext.replace('/', File.separatorChar)); - if (! extfile.exists()) { - // Ignore unloadable extensions. - Util.err.log(ErrorManager.WARNING, "Warning: Class-Path value " + ext + " from " + jar + " cannot be found at " + extfile); - continue; - } - //No need to sync on extensionOwners - we are in write mutex - Set owners = (Set)extensionOwners.get(extfile); - if (owners == null) { - owners = new HashSet(2); - owners.add(jar); - extensionOwners.put(extfile, owners); - } else if (! owners.contains(jar)) { - owners.add(jar); - ev.log(Events.EXTENSION_MULTIPLY_LOADED, extfile, owners); - } // else already know about it (OK or warned) - // Also check to make sure it is not a module JAR! See constructor for the reverse case. - if (moduleJARs.contains(extfile)) { - Util.err.log(ErrorManager.WARNING, "WARNING: Class-Path value " + ext + " from " + jar + " illegally refers to another module; use OpenIDE-Module-Module-Dependencies instead"); - } - if (plainExtensions == null) plainExtensions = new HashSet(); - plainExtensions.add(extfile); - l = Util.findLocaleVariantsOf(extfile, false); - if (!l.isEmpty()) { - if (localeExtensions == null) localeExtensions = new HashSet(); - localeExtensions.addAll(l); - } - } - } - // #9273: load any modules/patches/this-code-name/*.jar files first: - File patchdir = new File(new File(jar.getParentFile(), "patches"), // NOI18N - getCodeNameBase().replace('.', '-')); // NOI18N - scanForPatches(patchdir); - // Use of the following system property is not supported, but is used - // by e.g. XTest to influence installed modules without changing the build. - // Format is -Dnetbeans.patches.org.nb.mods.foo=/path/to.file.jar:/path/to/dir - String patchesClassPath = System.getProperty("netbeans.patches." + getCodeNameBase()); // NOI18N - if (patchesClassPath != null) { - StringTokenizer tokenizer = new StringTokenizer(patchesClassPath, File.pathSeparator); - while (tokenizer.hasMoreElements()) { - String element = (String) tokenizer.nextElement(); - File fileElement = new File(element); - if (fileElement.exists()) { - if (patches == null) { - patches = new HashSet(15); - } - patches.add(fileElement); - } - } - } - Util.err.log("localeVariants of " + jar + ": " + localeVariants); - Util.err.log("plainExtensions of " + jar + ": " + plainExtensions); - Util.err.log("localeExtensions of " + jar + ": " + localeExtensions); - Util.err.log("patches of " + jar + ": " + patches); - if (patches != null) { - Iterator it = patches.iterator(); - while (it.hasNext()) { - ev.log(Events.PATCH, it.next()); - } - } - } - - /** Scans a directory for possible patch JARs. */ - private void scanForPatches(File patchdir) { - if (!patchdir.isDirectory()) { - return; - } - File[] jars = patchdir.listFiles(Util.jarFilter()); - if (jars != null) { - for (int j = 0; j < jars.length; j++) { - if (patches == null) { - patches = new HashSet(5); - } - patches.add(jars[j]); - } - } else { - Util.err.log(ErrorManager.WARNING, "Could not search for patches in " + patchdir); - } - } - - /** Check if there is any need to load localized properties. - * If so, try to load them. Throw an exception if they cannot - * be loaded for some reason. Uses an open JAR file for the - * base module at least, though may also open locale variants - * as needed. - * Note: due to #19698, this cache is not usually used; only if you - * specifically go to look at the display properties of a disabled module. - * @see #12549 - */ - private void loadLocalizedProps(JarFile jarFile, Manifest m) throws IOException { - String locbundle = m.getMainAttributes().getValue("OpenIDE-Module-Localizing-Bundle"); // NOI18N - if (locbundle != null) { - // Something requested, read it in. - // locbundle is a resource path. - { - ZipEntry bundleFile = jarFile.getEntry(locbundle); - // May not be present in base JAR: might only be in e.g. default locale variant. - if (bundleFile != null) { - localizedProps = new Properties(); - InputStream is = jarFile.getInputStream(bundleFile); - try { - localizedProps.load(is); - } finally { - is.close(); - } - } - } - { - // Check also for localized variant JARs and load in anything from them as needed. - // Note we need to go in the reverse of the usual search order, so as to - // overwrite less specific bundles with more specific. - int idx = locbundle.lastIndexOf('.'); // NOI18N - String name, ext; - if (idx == -1) { - name = locbundle; - ext = ""; // NOI18N - } else { - name = locbundle.substring(0, idx); - ext = locbundle.substring(idx); - } - List pairs = Util.findLocaleVariantsOf(jar, true); - Collections.reverse(pairs); - Iterator it = pairs.iterator(); - while (it.hasNext()) { - Object[] pair = (Object[])it.next(); - File localeJar = (File)pair[0]; - String suffix = (String)pair[1]; - String rsrc = name + suffix + ext; - JarFile localeJarFile = new JarFile(localeJar, false); - try { - ZipEntry bundleFile = localeJarFile.getEntry(rsrc); - // Need not exist in all locale variants. - if (bundleFile != null) { - if (localizedProps == null) { - localizedProps = new Properties(); - } // else append and overwrite base-locale values - InputStream is = localeJarFile.getInputStream(bundleFile); - try { - localizedProps.load(is); - } finally { - is.close(); - } - } - } finally { - localeJarFile.close(); - } - } - } - if (localizedProps == null) { - // We should have loaded from at least some bundle in there... - throw new IOException("Could not find localizing bundle: " + locbundle); // NOI18N - } - /* Don't log; too large and annoying: - if (Util.err.isLoggable(ErrorManager.INFORMATIONAL)) { - Util.err.log("localizedProps=" + localizedProps); - } - */ - } - } - - /** Similar, but for fixed modules only. - * Should be very rarely used: only for classpath modules with a strangely - * named OpenIDE-Module-Localizing-Bundle (not *.properties). - */ - private void loadLocalizedPropsClasspath() throws InvalidException { - Attributes attr = manifest.getMainAttributes(); - String locbundle = attr.getValue("OpenIDE-Module-Localizing-Bundle"); // NOI18N - if (locbundle != null) { - Util.err.log("Localized props in " + locbundle + " for " + attr.getValue("OpenIDE-Module")); - try { - int idx = locbundle.lastIndexOf('.'); // NOI18N - String name, ext; - if (idx == -1) { - name = locbundle; - ext = ""; // NOI18N - } else { - name = locbundle.substring(0, idx); - ext = locbundle.substring(idx); - } - List suffixes = new ArrayList(10); - Iterator it = NbBundle.getLocalizingSuffixes(); - while (it.hasNext()) { - suffixes.add(it.next()); - } - Collections.reverse(suffixes); - it = suffixes.iterator(); - while (it.hasNext()) { - String suffix = (String)it.next(); - String resource = name + suffix + ext; - InputStream is = classloader.getResourceAsStream(resource); - if (is != null) { - Util.err.log("Found " + resource); - if (localizedProps == null) { - localizedProps = new Properties(); - } - localizedProps.load(is); - } - } - if (localizedProps == null) { - throw new IOException("Could not find localizing bundle: " + locbundle); // NOI18N - } - } catch (IOException ioe) { - InvalidException e = new InvalidException(ioe.toString()); - Util.err.annotate(e, ioe); - throw e; - } - } - } - /** Get all JARs loaded by this module. * Includes the module itself, any locale variants of the module, * any extensions specified with Class-Path, any locale variants @@ -866,23 +432,7 @@ * JARs already present in the classpath are not listed. * @return a List<File> of JARs */ - public List getAllJars() { - if (jar == null) { - // Classpath module. - return Collections.EMPTY_LIST; - } - List l = new ArrayList (); // List - if (patches != null) l.addAll(patches); - if (physicalJar != null) { - l.add(physicalJar); - } else if (jar != null) { - l.add(jar); - } - if (plainExtensions != null) l.addAll (plainExtensions); - if (localeVariants != null) l.addAll (localeVariants); - if (localeExtensions != null) l.addAll (localeExtensions); - return l; - } + public abstract List getAllJars(); /** Is this module supposed to be easily reloadable? * If so, it is suitable for testing inside the IDE. @@ -901,14 +451,7 @@ * Must be called from within a write mutex. * @param r whether the module should be considered reloadable */ - public void setReloadable(boolean r) { - mgr.assertWritable(); - if (isFixed()) throw new IllegalStateException(); - if (reloadable != r) { - reloadable = r; - mgr.fireReloadable(this); - } - } + public abstract void setReloadable(boolean r); /** Used as a flag to tell if this module was really successfully released. * Currently does not work, so if it cannot be made to work, delete it. @@ -923,20 +466,7 @@ * to be in an invalid state. * @since JST-PENDING: needed from ModuleSystem */ - public final void reload() throws IOException { - if (isFixed()) throw new IllegalStateException(); - // Probably unnecessary but just in case: - destroyPhysicalJar(); - String codeNameBase1 = getCodeNameBase(); - localizedProps = null; - loadManifest(); - parseManifest(); - findExtensionsAndVariants(manifest); - String codeNameBase2 = getCodeNameBase(); - if (! codeNameBase1.equals(codeNameBase2)) { - throw new InvalidException("Code name base changed during reload: " + codeNameBase1 + " -> " + codeNameBase2); // NOI18N - } - } + public abstract void reload() throws IOException; // impl of ModuleInfo method public ClassLoader getClassLoader() throws IllegalArgumentException { @@ -951,124 +481,25 @@ /** Turn on the classloader. Passed a list of parent modules to use. * The parents should already have had their classloaders initialized. */ - void classLoaderUp(Set parents) throws IOException { - if (isFixed()) return; // no need - Util.err.log("classLoaderUp on " + this + " with parents " + parents); - // Find classloaders for dependent modules and parent to them. - List loaders = new ArrayList(parents.size() + 1); // List - // This should really be the base loader created by org.nb.Main for loading openide etc.: - loaders.add(Module.class.getClassLoader()); - Iterator it = parents.iterator(); - while (it.hasNext()) { - Module parent = (Module)it.next(); - PackageExport[] exports = parent.getPublicPackages(); - if (exports != null && exports.length == 0) { - // Check if there is an impl dep here. - Dependency[] deps = getDependenciesArray(); - boolean implDep = false; - for (int i = 0; i < deps.length; i++) { - if (deps[i].getType() == Dependency.TYPE_MODULE && - deps[i].getComparison() == Dependency.COMPARE_IMPL && - deps[i].getName().equals(parent.getCodeName())) { - implDep = true; - break; - } - } - if (!implDep) { - // Nothing exported from here at all, no sense even adding it. - // Shortcut; would not harm anything to add it now, but we would - // never use it anyway. - // Cf. #27853. - continue; - } - } - ClassLoader l = parent.getClassLoader(); - if (parent.isFixed() && loaders.contains(l)) { - Util.err.log("#24996: skipping duplicate classloader from " + parent); - continue; - } - loaders.add(l); - } - List classp = new ArrayList(3); // List - if (patches != null) { - for (it = patches.iterator(); it.hasNext(); ) { - File f = (File)it.next(); - if (f.isDirectory()) { - classp.add(f); - } else { - classp.add(new JarFile(f, false)); - } - } - } - if (reloadable) { - ensurePhysicalJar(); - // Using OPEN_DELETE does not work well with test modules under 1.4. - // Random code (URL handler?) still expects the JAR to be there and - // it is not. - classp.add(new JarFile(physicalJar, false)); - } else { - classp.add(new JarFile(jar, false)); - } - // URLClassLoader would not otherwise find these, so: - if (localeVariants != null) { - for (it = localeVariants.iterator(); it.hasNext(); ) { - classp.add(new JarFile((File)it.next(), false)); - } - } - if (localeExtensions != null) { - for (it = localeExtensions.iterator(); it.hasNext(); ) { - File act = (File)it.next(); - classp.add(act.isDirectory() ? (Object)act : new JarFile(act, false)); - } - } - if (plainExtensions != null) { - for( it = plainExtensions.iterator(); it.hasNext(); ) { - File act = (File)it.next(); - classp.add(act.isDirectory() ? (Object)act : new JarFile(act, false)); - } - } - - // #27853: - mgr.refineClassLoader(this, loaders); - - try { - classloader = new OneModuleClassLoader(classp, (ClassLoader[])loaders.toArray(new ClassLoader[loaders.size()])); - } catch (IllegalArgumentException iae) { - // Should not happen, but just in case. - IOException ioe = new IOException(iae.toString()); - Util.err.annotate(ioe, iae); - throw ioe; - } - } - + public abstract void classLoaderUp(Set parents) throws IOException; + /** Turn off the classloader and release all resources. */ - void classLoaderDown() { - if (isFixed()) return; // don't touch it - if (classloader instanceof ProxyClassLoader) { - ((ProxyClassLoader)classloader).destroy(); - } - classloader = null; - Util.err.log("classLoaderDown on " + this + ": releaseCount=" + releaseCount + " released=" + released); - released = false; - } + public abstract void classLoaderDown(); /** Should be called after turning off the classloader of one or more modules & GC'ing. */ - void cleanup() { - if (isFixed()) return; // don't touch it - if (isEnabled()) throw new IllegalStateException("cleanup on enabled module: " + this); // NOI18N - if (classloader != null) throw new IllegalStateException("cleanup on module with classloader: " + this); // NOI18N - if (! released) { - Util.err.log("Warning: not all resources associated with module " + jar + " were successfully released."); - released = true; - } else { - Util.err.log ("All resources associated with module " + jar + " were successfully released."); - } - // XXX should this rather be done when the classloader is collected? - destroyPhysicalJar(); - } + public abstract void cleanup(); /** Notify the module that it is being deleted. */ - void destroy() { - moduleJARs.remove(jar); + public abstract void destroy(); + + public abstract boolean isFixed(); + + /** Get the JAR this module is packaged in. + * May be null for modules installed specially, e.g. + * automatically from the classpath. + * @see #isFixed + */ + public File getJarFile() { + return null; } /** Get the JAR manifest. @@ -1144,82 +575,6 @@ return modulePermissions; } - /** Class loader to load a single module. - * Auto-localizing, multi-parented, permission-granting, the works. - */ - private class OneModuleClassLoader extends JarClassLoader implements Util.ModuleProvider, Util.PackageAccessibleClassLoader { - private int rc; - /** Create a new loader for a module. - * @param classp the List of all module jars of code directories; - * includes the module itself, its locale variants, - * variants of extensions and Class-Path items from Manifest. - * The items are JarFiles for jars and Files for directories - * @param parents a set of parent classloaders (from other modules) - */ - public OneModuleClassLoader(List classp, ClassLoader[] parents) throws IllegalArgumentException { - super(classp, parents, false); - rc = releaseCount++; - } - - public Module getModule() { - return Module.this; - } - - /** Inherited. - * @param cs is ignored - * @return PermissionCollection with an AllPermission instance - */ - protected PermissionCollection getPermissions(CodeSource cs) { - return getAllPermission(); - } - - /** look for JNI libraries also in modules/bin/ */ - protected String findLibrary(String libname) { - String mapped = System.mapLibraryName(libname); - File lib = new File(new File(jar.getParentFile(), "lib"), mapped); // NOI18N - if (lib.isFile()) { - return lib.getAbsolutePath(); - } else { - return null; - } - } - - protected boolean isSpecialResource(String pkg) { - if (mgr.isSpecialResource(pkg)) { - return true; - } - return super.isSpecialResource(pkg); - } - - protected boolean shouldDelegateResource(String pkg, ClassLoader parent) { - if (!super.shouldDelegateResource(pkg, parent)) { - return false; - } - Module other; - if (parent instanceof Util.ModuleProvider) { - other = ((Util.ModuleProvider)parent).getModule(); - } else { - other = null; - } - return mgr.shouldDelegateResource(Module.this, other, pkg); - } - - public String toString() { - return super.toString() + "[" + getCodeNameBase() + "]"; // NOI18N - } - - protected void finalize() throws Throwable { - super.finalize(); - Util.err.log("Finalize for " + this + ": rc=" + rc + " releaseCount=" + releaseCount + " released=" + released); // NOI18N - if (rc == releaseCount) { - // Hurrah! release() worked. - released = true; - } else { - Util.err.log("Now resources for " + getCodeNameBase() + " have been released."); // NOI18N - } - } - } - /** Struct representing a package exported from a module. * @since org.netbeans.core/1 > 1.4 * @see Module#getPublicPackages Index: bootstrap/src/org/netbeans/ModuleFactory.java =================================================================== RCS file: bootstrap/src/org/netbeans/ModuleFactory.java diff -N bootstrap/src/org/netbeans/ModuleFactory.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ bootstrap/src/org/netbeans/ModuleFactory.java 22 Aug 2005 19:59:09 -0000 @@ -0,0 +1,65 @@ +/* + * Sun Public License Notice + * + * The contents of this file are subject to the Sun Public License + * Version 1.0 (the "License"). You may not use this file except in + * compliance with the License. A copy of the License is available at + * http://www.sun.com/ + * + * The Original Code is NetBeans. The Initial Developer of the Original + * Code is Nokia. Portions Copyright 2005 Nokia. All Rights Reserved. + */ +package org.netbeans; + +import java.io.File; +import java.io.IOException; +import java.util.jar.Manifest; + +/** + * Allows creation of custom modules. The factories are searched in + * the default lookup (org.openide.util.Lookup.getDefault()). If there is one + * it is used - if there are more of them arbitrary one is used (so please make + * sure that there is only one present in the installation). If there is none + * in the default lookup the system will use an instance of this class. + * + * @author David Strupl + */ +public class ModuleFactory { + + /** + * This method creates a "standard" module. Standard modules can be + * disabled, reloaded, autoloaded (loaded only when needed). + * @see StandardModule + */ + public Module create(File jar, Object history, boolean reloadable, + boolean autoload, boolean eager, ModuleManager mgr, Events ev) + throws IOException, DuplicateException { + return new StandardModule(mgr, ev, jar, history, reloadable, autoload, eager); + } + /** + * This method creates a "fixed" module. Fixed modules cannot be + * realoaded, are always enabled and are typically present on the + * classpath. + * @see FixedModule + */ + public Module createFixed(Manifest mani, Object history, + ClassLoader loader, ModuleManager mgr, Events ev) + throws InvalidException, DuplicateException { + return new FixedModule(mgr, ev, mani, history, loader); + } + /** + * Allows specifying different parent classloader of all modules classloaders. + */ + public ClassLoader getClasspathDelegateClassLoader(ModuleManager mgr, ClassLoader del) { + return del; + } + + /** + * If this method returns true the parent the original classpath + * classloader will be removed from the parent classloaders of a module classloader. + */ + public boolean removeBaseClassLoader() { + return false; + } + +} Index: bootstrap/src/org/netbeans/ModuleManager.java =================================================================== RCS file: /cvs/core/bootstrap/src/org/netbeans/ModuleManager.java,v retrieving revision 1.3 diff -u -r1.3 ModuleManager.java --- bootstrap/src/org/netbeans/ModuleManager.java 11 Aug 2005 18:33:57 -0000 1.3 +++ bootstrap/src/org/netbeans/ModuleManager.java 22 Aug 2005 19:59:14 -0000 @@ -56,6 +56,7 @@ private final Map providersOf = new HashMap(25); // Map> private final ModuleInstaller installer; + private ModuleFactory moduleFactory; private SystemClassLoader classLoader; private List classLoaderPatches; // List @@ -96,6 +97,11 @@ } classLoader = new SystemClassLoader(classLoaderPatches, new ClassLoader[] {installer.getClass ().getClassLoader()}, Collections.EMPTY_SET); updateContextClassLoaders(classLoader, true); + + moduleFactory = (ModuleFactory)Lookup.getDefault().lookup(ModuleFactory.class); + if (moduleFactory == null) { + moduleFactory = new ModuleFactory(); + } } /** Access for ManifestSection. @@ -307,6 +313,9 @@ parents.add(m.getClassLoader()); } } + if (moduleFactory.removeBaseClassLoader()) { + parents.remove(base); + } ClassLoader[] parentCLs = (ClassLoader[])parents.toArray(new ClassLoader[parents.size()]); SystemClassLoader nue; try { @@ -416,6 +425,9 @@ } public String toString() { + if (debugme == null) { + return "SystemClassLoader"; + } return debugme.toString(); } @@ -458,7 +470,8 @@ public Module create(File jar, Object history, boolean reloadable, boolean autoload, boolean eager) throws IOException, DuplicateException { assertWritable(); ev.log(Events.START_CREATE_REGULAR_MODULE, jar); - Module m = new Module(this, ev, jar.getAbsoluteFile(), history, reloadable, autoload, eager); + Module m = moduleFactory.create(jar.getAbsoluteFile(), + history, reloadable, autoload, eager, this, ev); ev.log(Events.FINISH_CREATE_REGULAR_MODULE, jar); subCreate(m); if (m.isEager()) { @@ -491,7 +504,7 @@ assertWritable(); if (mani == null || loader == null) throw new IllegalArgumentException("null manifest or loader"); // NOI18N ev.log(Events.START_CREATE_BOOT_MODULE, history); - Module m = new Module(this, ev, mani, history, loader); + Module m = moduleFactory.createFixed(mani, history, loader, this, ev); ev.log(Events.FINISH_CREATE_BOOT_MODULE, history); subCreate(m); return m; @@ -507,12 +520,12 @@ return installer.refineProvides (m); } /** Used by Module to communicate with the ModuleInstaller re. classloader. */ - void refineClassLoader(Module m, List parents) { + public void refineClassLoader(Module m, List parents) { // #27853: installer.refineClassLoader(m, parents); } /** Use by OneModuleClassLoader to communicate with the ModuleInstaller re. masking. */ - boolean shouldDelegateResource(Module m, Module parent, String pkg) { + public boolean shouldDelegateResource(Module m, Module parent, String pkg) { // Cf. #19621: Module.PackageExport[] exports = (parent == null) ? null : parent.getPublicPackages(); if (exports != null) { @@ -566,7 +579,7 @@ // The installer can perform additional checks: return installer.shouldDelegateResource(m, parent, pkg); } - boolean isSpecialResource(String pkg) { + public boolean isSpecialResource(String pkg) { return installer.isSpecialResource(pkg); } // Again, access from Module to ModuleInstaller: @@ -858,8 +871,19 @@ Util.err.log("enable: adding to system classloader"); List nueclassloaders = new ArrayList(toEnable.size()); Iterator teIt = toEnable.iterator(); - while (teIt.hasNext()) { - nueclassloaders.add(((Module)teIt.next()).getClassLoader()); + if (moduleFactory.removeBaseClassLoader()) { + ClassLoader base = ModuleManager.class.getClassLoader(); + nueclassloaders.add(moduleFactory.getClasspathDelegateClassLoader(this, base)); + while (teIt.hasNext()) { + ClassLoader c1 = ((Module)teIt.next()).getClassLoader(); + if (c1 != base) { + nueclassloaders.add(c1); + } + } + } else { + while (teIt.hasNext()) { + nueclassloaders.add(((Module)teIt.next()).getClassLoader()); + } } classLoader.append((ClassLoader[])(nueclassloaders.toArray(new ClassLoader[nueclassloaders.size()])), toEnable); } else { @@ -992,7 +1016,7 @@ if (m.isAutoload()) throw new IllegalArgumentException("Cannot simulate enabling an autoload: " + m); // NOI18N if (m.isEager()) throw new IllegalArgumentException("Cannot simulate enabling an eager module: " + m); // NOI18N if (m.isEnabled()) throw new IllegalArgumentException("Already enabled: " + m); // NOI18N - if (!m.isValid()) throw new IllegalArgumentException("Not managed by me: " + m + " in " + m.getJarFile()); // NOI18N + if (!m.isValid()) throw new IllegalArgumentException("Not managed by me: " + m + " in " + m); // NOI18N maybeAddToEnableList(willEnable, modules, m, true); } // XXX clumsy but should work: @@ -1535,5 +1559,4 @@ installer.close(modules); return true; } - } Index: bootstrap/src/org/netbeans/ProxyClassLoader.java =================================================================== RCS file: /cvs/core/bootstrap/src/org/netbeans/ProxyClassLoader.java,v retrieving revision 1.18 diff -u -r1.18 ProxyClassLoader.java --- bootstrap/src/org/netbeans/ProxyClassLoader.java 11 Aug 2005 18:33:57 -0000 1.18 +++ bootstrap/src/org/netbeans/ProxyClassLoader.java 22 Aug 2005 19:59:16 -0000 @@ -18,6 +18,7 @@ import java.io.IOException; import java.security.AccessController; import java.security.PrivilegedAction; +import org.openide.util.Lookup; /** * A class loader that has multiple parents and uses them for loading @@ -95,11 +96,15 @@ for (int i = 0; i < nueparents.length; i++) { if (nueparents[i] == null) throw new IllegalArgumentException("null parent"); // NOI18N } - - parents = coalesceAppend(parents, nueparents); + ModuleFactory moduleFactory = (ModuleFactory)Lookup.getDefault().lookup(ModuleFactory.class); + if (moduleFactory != null && moduleFactory.removeBaseClassLoader()) { + // this hack is here to prevent having the application classloader + // as parent to all module classloaders. + parents = coalesceAppend(new ClassLoader[0], nueparents); + } else { + parents = coalesceAppend(parents, nueparents); + } } - - /** Try to destroy this classloader. * Subsequent attempts to use it will log an error (at most one though). @@ -536,7 +541,8 @@ for (int i = 0; i < parents.length; i++) { ClassLoader par = parents[i]; if (!shouldDelegateResource(pkg, par)) continue; - if (par instanceof ProxyClassLoader) { + if ((par instanceof ProxyClassLoader) && + ((ProxyClassLoader)par).shouldBeCheckedAsParentProxyClassLoader()) { ProxyClassLoader pcl = (ProxyClassLoader)par; Class c = pcl.fullFindClass(name, fileName, pkg); // pcl might have have c in its already-loaded classes even though @@ -546,16 +552,18 @@ } else { // The following is an optimization, it should not affect semantics: boolean skip = false; - if (name.startsWith("org.netbeans.") || // NOI18N - name.startsWith("org.openide.") || // NOI18N - name.endsWith(".Bundle") || // NOI18N - name.endsWith("BeanInfo") || // NOI18N - name.endsWith("Editor")) { // NOI18N - if (par.getResource(fileName) == null) { - // We would just throw CNFE anyway, don't bother! - // Avg. (over ten runs after primer, w/ netbeans.close): - // before: 13.87s after: 13.40s saved: 1.3% - skip = true; + if (optimizeNBLoading()) { + if (name.startsWith("org.netbeans.") || // NOI18N + name.startsWith("org.openide.") || // NOI18N + name.endsWith(".Bundle") || // NOI18N + name.endsWith("BeanInfo") || // NOI18N + name.endsWith("Editor")) { // NOI18N + if (par.getResource(fileName) == null) { + // We would just throw CNFE anyway, don't bother! + // Avg. (over ten runs after primer, w/ netbeans.close): + // before: 13.87s after: 13.40s saved: 1.3% + skip = true; + } } } if (!skip) { @@ -584,6 +592,21 @@ for (int i = 0; i < pkgs.length; i++) { all.put(pkgs[i].getName(), pkgs[i]); } + } + + /** + * See loadInOrder(...). Can be overriden by special classloaders + * (see project installer/jnlp/modules). + */ + protected boolean shouldBeCheckedAsParentProxyClassLoader() { + return true; + } + + /** + * Allows turning off the optimilazation in loadInOrder(...). + */ + protected boolean optimizeNBLoading() { + return true; } /** Test whether a given resource name is something that any JAR might Index: bootstrap/src/org/netbeans/StandardModule.java =================================================================== RCS file: bootstrap/src/org/netbeans/StandardModule.java diff -N bootstrap/src/org/netbeans/StandardModule.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ bootstrap/src/org/netbeans/StandardModule.java 22 Aug 2005 19:59:18 -0000 @@ -0,0 +1,743 @@ +/* + * Sun Public License Notice + * + * The contents of this file are subject to the Sun Public License + * Version 1.0 (the "License"). You may not use this file except in + * compliance with the License. A copy of the License is available at + * http://www.sun.com/ + * + * The Original Code is NetBeans. The Initial Developer of the Original + * Code is Sun Microsystems, Inc. Portions Copyright 1997-2004 Sun + * Microsystems, Inc. All Rights Reserved. + */ + +package org.netbeans; + +// THIS CLASS OUGHT NOT USE NbBundle NOR org.openide CLASSES +// OUTSIDE OF openide-util.jar! UI AND FILESYSTEM/DATASYSTEM +// INTERACTIONS SHOULD GO ELSEWHERE. +// (NbBundle.getLocalizedValue is OK here.) + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.security.AllPermission; +import java.security.CodeSource; +import java.security.PermissionCollection; +import java.security.Permissions; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.MissingResourceException; +import java.util.Properties; +import java.util.ResourceBundle; +import java.util.Set; +import java.util.StringTokenizer; +import java.util.jar.Attributes; +import java.util.jar.JarFile; +import java.util.jar.Manifest; +import java.util.zip.ZipEntry; +import org.netbeans.JarClassLoader; +import org.netbeans.ProxyClassLoader; +import org.openide.ErrorManager; +import org.openide.modules.Dependency; +import org.openide.util.NbBundle; +import org.openide.util.WeakSet; + +/** Object representing one module, possibly installed. + * Responsible for opening of module JAR file; reading + * manifest; parsing basic information such as dependencies; + * and creating a classloader for use by the installer. + * Methods not defined in ModuleInfo must be called from within + * the module manager's read mutex as a rule. + * @author Jesse Glick + */ +public final class StandardModule extends Module { + + /** JAR file holding the module */ + private final File jar; + /** if reloadable, temporary JAR file actually loaded from */ + private File physicalJar = null; + + /** Map from extension JARs to sets of JAR that load them via Class-Path. + * Used only for debugging purposes, so that a warning is printed if two + * different modules try to load the same extension (which would cause them + * to both load their own private copy, which may not be intended). + */ + private static final Map extensionOwners = new HashMap(); // Map> + /** Simple registry of JAR files used as modules. + * Used only for debugging purposes, so that we can be sure + * that no one is using Class-Path to refer to other modules. + */ + private static final Set moduleJARs = new HashSet(); // Set + + /** Set of locale-variants JARs for this module (or null). + * Added explicitly to classloader, and can be used by execution engine. + */ + private Set localeVariants = null; // Set + /** Set of extension JARs that this module loads via Class-Path (or null). + * Can be used e.g. by execution engine. (#9617) + */ + private Set plainExtensions = null; // Set + /** Set of localized extension JARs derived from plainExtensions (or null). + * Used to add these to the classloader. (#9348) + * Can be used e.g. by execution engine. + */ + private Set localeExtensions = null; // Set + /** Patches added at the front of the classloader (or null). + * Files are assumed to be JARs; directories are themselves. + */ + private Set patches = null; // Set + + /** localized properties, only non-null if requested from disabled module */ + private Properties localizedProps; + + /** Use ModuleManager.create as a factory. */ + public StandardModule(ModuleManager mgr, Events ev, File jar, Object history, boolean reloadable, boolean autoload, boolean eager) throws IOException { + super(mgr, ev, history, reloadable, autoload, eager); + this.jar = jar; + loadManifest(); + parseManifest(); + findExtensionsAndVariants(manifest); + // Check if some other module already listed this one in Class-Path. + // For the chronologically reverse case, see findExtensionsAndVariants(). + Set bogoOwners = (Set)extensionOwners.get(jar); + if (bogoOwners != null) { + Util.err.log(ErrorManager.WARNING, "WARNING - module " + jar + " was incorrectly placed in the Class-Path of other JARs " + bogoOwners + "; please use OpenIDE-Module-Module-Dependencies instead"); + } + moduleJARs.add(jar); + } + + /** Get a localized attribute. + * First, if OpenIDE-Module-Localizing-Bundle was given, the specified + * bundle file (in all locale JARs as well as base JAR) is searched for + * a key of the specified name. + * Otherwise, the manifest's main attributes are searched for an attribute + * with the specified name, possibly with a locale suffix. + * If the attribute name contains a slash, and there is a manifest section + * named according to the part before the last slash, then this section's attributes + * are searched instead of the main attributes, and for the attribute listed + * after the slash. Currently this would only be useful for localized filesystem + * names. E.g. you may request the attribute org/foo/MyFileSystem.class/Display-Name. + * In the future certain attributes known to be dangerous could be + * explicitly suppressed from this list; should only be used for + * documented localizable attributes such as OpenIDE-Module-Name etc. + */ + public Object getLocalizedAttribute(String attr) { + String locb = getManifest().getMainAttributes().getValue("OpenIDE-Module-Localizing-Bundle"); // NOI18N + boolean usingLoader = false; + if (locb != null) { + if (classloader != null) { + if (locb.endsWith(".properties")) { // NOI18N + usingLoader = true; + String basename = locb.substring(0, locb.length() - 11).replace('/', '.'); + try { + ResourceBundle bundle = NbBundle.getBundle(basename, Locale.getDefault(), classloader); + try { + return bundle.getString(attr); + } catch (MissingResourceException mre) { + // Fine, ignore. + } + } catch (MissingResourceException mre) { + Util.err.notify(mre); + } + } else { + Util.err.log(ErrorManager.WARNING, "WARNING - cannot efficiently load non-*.properties OpenIDE-Module-Localizing-Bundle: " + locb); + } + } + if (!usingLoader) { + if (localizedProps == null) { + Util.err.log("Trying to get localized attr " + attr + " from disabled module " + getCodeNameBase()); + try { + if (jar != null) { + JarFile jarFile = new JarFile(jar, false); + try { + loadLocalizedProps(jarFile, manifest); + } finally { + jarFile.close(); + } + } else { + throw new IllegalStateException(); + } + } catch (IOException ioe) { + Util.err.annotate(ioe, ErrorManager.INFORMATIONAL, jar.getAbsolutePath(), null, null, null); + Util.err.notify(ErrorManager.INFORMATIONAL, ioe); + if (localizedProps == null) { + localizedProps = new Properties(); + } + } + } + String val = localizedProps.getProperty(attr); + if (val != null) { + return val; + } + } + } + // Try in the manifest now. + int idx = attr.lastIndexOf('/'); // NOI18N + if (idx == -1) { + // Simple main attribute. + return NbBundle.getLocalizedValue(getManifest().getMainAttributes(), new Attributes.Name(attr)); + } else { + // Attribute of a manifest section. + String section = attr.substring(0, idx); + String realAttr = attr.substring(idx + 1); + Attributes attrs = getManifest().getAttributes(section); + if (attrs != null) { + return NbBundle.getLocalizedValue(attrs, new Attributes.Name(realAttr)); + } else { + return null; + } + } + } + + public boolean owns(Class clazz) { + ClassLoader cl = clazz.getClassLoader(); + if (cl instanceof Util.ModuleProvider) { + return ((Util.ModuleProvider) cl).getModule() == this; + } + return false; + + } + + public boolean isFixed() { + return false; + } + + /** Get the JAR this module is packaged in. + * May be null for modules installed specially, e.g. + * automatically from the classpath. + * @see #isFixed + */ + public File getJarFile() { + return jar; + } + + /** Create a temporary test JAR if necessary. + * This is primarily necessary to work around a Java bug, + * #4405789, which might be fixed in 1.4--check up on this. + */ + private void ensurePhysicalJar() throws IOException { + if (reloadable && physicalJar == null) { + physicalJar = Util.makeTempJar(jar); + } + } + private void destroyPhysicalJar() { + if (physicalJar != null) { + if (physicalJar.isFile()) { + if (! physicalJar.delete()) { + Util.err.log(ErrorManager.WARNING, "Warning: temporary JAR " + physicalJar + " not currently deletable."); + } else { + Util.err.log("deleted: " + physicalJar); + } + } + physicalJar = null; + } else { + Util.err.log("no physicalJar to delete for " + this); + } + } + + /** Open the JAR, load its manifest, and do related things. */ + private void loadManifest() throws IOException { + Util.err.log("loading manifest of " + jar); + File jarBeingOpened = null; // for annotation purposes + try { + if (reloadable) { + // Never try to cache reloadable JARs. + jarBeingOpened = physicalJar; // might be null + ensurePhysicalJar(); + jarBeingOpened = physicalJar; // might have changed + JarFile jarFile = new JarFile(physicalJar, false); + try { + Manifest m = jarFile.getManifest(); + if (m == null) throw new IOException("No manifest found in " + physicalJar); // NOI18N + manifest = m; + } finally { + jarFile.close(); + } + } else { + jarBeingOpened = jar; + manifest = getManager().loadManifest(jar); + } + } catch (IOException e) { + if (jarBeingOpened != null) { + Util.err.annotate(e, ErrorManager.UNKNOWN, "While loading manifest from: " + jarBeingOpened, null, null, null); // NOI18N + } + throw e; + } + } + + /** Find any extensions loaded by the module, as well as any localized + * variants of the module or its extensions. + */ + private void findExtensionsAndVariants(Manifest m) { + assert jar != null : "Cannot load extensions from classpath module " + getCodeNameBase(); + localeVariants = null; + List l = Util.findLocaleVariantsOf(jar, false); + if (!l.isEmpty()) localeVariants = new HashSet(l); + plainExtensions = null; + localeExtensions = null; + String classPath = m.getMainAttributes().getValue(Attributes.Name.CLASS_PATH); + if (classPath != null) { + StringTokenizer tok = new StringTokenizer(classPath); + while (tok.hasMoreTokens()) { + String ext = tok.nextToken(); + if (new File(ext).isAbsolute() || ext.indexOf("../") != -1) { // NOI18N + if (ext.equals("../lib/updater.jar")) { // NOI18N + // Special case, see #24703. + // JAR is special to the launcher, so it makes sense in lib/ rather + // than modules/ext/. However updater.jar is not in startup classpath, + // so autoupdate module explicitly declares it this way. + } else { + Util.err.log(ErrorManager.WARNING, "WARNING: Class-Path value " + ext + " from " + jar + " is illegal according to the Java Extension Mechanism: must be relative and not move up directories"); + } + } + File extfile = new File(jar.getParentFile(), ext.replace('/', File.separatorChar)); + if (! extfile.exists()) { + // Ignore unloadable extensions. + Util.err.log(ErrorManager.WARNING, "Warning: Class-Path value " + ext + " from " + jar + " cannot be found at " + extfile); + continue; + } + //No need to sync on extensionOwners - we are in write mutex + Set owners = (Set)extensionOwners.get(extfile); + if (owners == null) { + owners = new HashSet(2); + owners.add(jar); + extensionOwners.put(extfile, owners); + } else if (! owners.contains(jar)) { + owners.add(jar); + events.log(Events.EXTENSION_MULTIPLY_LOADED, extfile, owners); + } // else already know about it (OK or warned) + // Also check to make sure it is not a module JAR! See constructor for the reverse case. + if (moduleJARs.contains(extfile)) { + Util.err.log(ErrorManager.WARNING, "WARNING: Class-Path value " + ext + " from " + jar + " illegally refers to another module; use OpenIDE-Module-Module-Dependencies instead"); + } + if (plainExtensions == null) plainExtensions = new HashSet(); + plainExtensions.add(extfile); + l = Util.findLocaleVariantsOf(extfile, false); + if (!l.isEmpty()) { + if (localeExtensions == null) localeExtensions = new HashSet(); + localeExtensions.addAll(l); + } + } + } + // #9273: load any modules/patches/this-code-name/*.jar files first: + File patchdir = new File(new File(jar.getParentFile(), "patches"), // NOI18N + getCodeNameBase().replace('.', '-')); // NOI18N + scanForPatches(patchdir); + // Use of the following system property is not supported, but is used + // by e.g. XTest to influence installed modules without changing the build. + // Format is -Dnetbeans.patches.org.nb.mods.foo=/path/to.file.jar:/path/to/dir + String patchesClassPath = System.getProperty("netbeans.patches." + getCodeNameBase()); // NOI18N + if (patchesClassPath != null) { + StringTokenizer tokenizer = new StringTokenizer(patchesClassPath, File.pathSeparator); + while (tokenizer.hasMoreElements()) { + String element = (String) tokenizer.nextElement(); + File fileElement = new File(element); + if (fileElement.exists()) { + if (patches == null) { + patches = new HashSet(15); + } + patches.add(fileElement); + } + } + } + Util.err.log("localeVariants of " + jar + ": " + localeVariants); + Util.err.log("plainExtensions of " + jar + ": " + plainExtensions); + Util.err.log("localeExtensions of " + jar + ": " + localeExtensions); + Util.err.log("patches of " + jar + ": " + patches); + if (patches != null) { + Iterator it = patches.iterator(); + while (it.hasNext()) { + events.log(Events.PATCH, it.next()); + } + } + } + + /** Scans a directory for possible patch JARs. */ + private void scanForPatches(File patchdir) { + if (!patchdir.isDirectory()) { + return; + } + File[] jars = patchdir.listFiles(Util.jarFilter()); + if (jars != null) { + for (int j = 0; j < jars.length; j++) { + if (patches == null) { + patches = new HashSet(5); + } + patches.add(jars[j]); + } + } else { + Util.err.log(ErrorManager.WARNING, "Could not search for patches in " + patchdir); + } + } + + /** Check if there is any need to load localized properties. + * If so, try to load them. Throw an exception if they cannot + * be loaded for some reason. Uses an open JAR file for the + * base module at least, though may also open locale variants + * as needed. + * Note: due to #19698, this cache is not usually used; only if you + * specifically go to look at the display properties of a disabled module. + * @see #12549 + */ + private void loadLocalizedProps(JarFile jarFile, Manifest m) throws IOException { + String locbundle = m.getMainAttributes().getValue("OpenIDE-Module-Localizing-Bundle"); // NOI18N + if (locbundle != null) { + // Something requested, read it in. + // locbundle is a resource path. + { + ZipEntry bundleFile = jarFile.getEntry(locbundle); + // May not be present in base JAR: might only be in e.g. default locale variant. + if (bundleFile != null) { + localizedProps = new Properties(); + InputStream is = jarFile.getInputStream(bundleFile); + try { + localizedProps.load(is); + } finally { + is.close(); + } + } + } + { + // Check also for localized variant JARs and load in anything from them as needed. + // Note we need to go in the reverse of the usual search order, so as to + // overwrite less specific bundles with more specific. + int idx = locbundle.lastIndexOf('.'); // NOI18N + String name, ext; + if (idx == -1) { + name = locbundle; + ext = ""; // NOI18N + } else { + name = locbundle.substring(0, idx); + ext = locbundle.substring(idx); + } + List pairs = Util.findLocaleVariantsOf(jar, true); + Collections.reverse(pairs); + Iterator it = pairs.iterator(); + while (it.hasNext()) { + Object[] pair = (Object[])it.next(); + File localeJar = (File)pair[0]; + String suffix = (String)pair[1]; + String rsrc = name + suffix + ext; + JarFile localeJarFile = new JarFile(localeJar, false); + try { + ZipEntry bundleFile = localeJarFile.getEntry(rsrc); + // Need not exist in all locale variants. + if (bundleFile != null) { + if (localizedProps == null) { + localizedProps = new Properties(); + } // else append and overwrite base-locale values + InputStream is = localeJarFile.getInputStream(bundleFile); + try { + localizedProps.load(is); + } finally { + is.close(); + } + } + } finally { + localeJarFile.close(); + } + } + } + if (localizedProps == null) { + // We should have loaded from at least some bundle in there... + throw new IOException("Could not find localizing bundle: " + locbundle); // NOI18N + } + /* Don't log; too large and annoying: + if (Util.err.isLoggable(ErrorManager.INFORMATIONAL)) { + Util.err.log("localizedProps=" + localizedProps); + } + */ + } + } + + /** Get all JARs loaded by this module. + * Includes the module itself, any locale variants of the module, + * any extensions specified with Class-Path, any locale variants + * of those extensions. + * The list will be in classpath order (patches first). + * Currently the temp JAR is provided in the case of test modules, to prevent + * sporadic ZIP file exceptions when background threads (like Java parsing) tries + * to open libraries found in the library path. + * JARs already present in the classpath are not listed. + * @return a List<File> of JARs + */ + public List getAllJars() { + List l = new ArrayList (); // List + if (patches != null) l.addAll(patches); + if (physicalJar != null) { + l.add(physicalJar); + } else if (jar != null) { + l.add(jar); + } + if (plainExtensions != null) l.addAll (plainExtensions); + if (localeVariants != null) l.addAll (localeVariants); + if (localeExtensions != null) l.addAll (localeExtensions); + return l; + } + + /** Set whether this module is supposed to be reloadable. + * Has no immediate effect, only impacts what happens the + * next time it is enabled (after having been disabled if + * necessary). + * Must be called from within a write mutex. + * @param r whether the module should be considered reloadable + */ + public void setReloadable(boolean r) { + getManager().assertWritable(); + if (reloadable != r) { + reloadable = r; + getManager().fireReloadable(this); + } + } + + /** Used as a flag to tell if this module was really successfully released. + * Currently does not work, so if it cannot be made to work, delete it. + * (Someone seems to be holding a strong reference to the classloader--who?!) + */ + private transient boolean released; + /** Count which release() call is really being checked. */ + private transient int releaseCount = 0; + + /** Reload this module. Access from ModuleManager. + * If an exception is thrown, the module is considered + * to be in an invalid state. + */ + public void reload() throws IOException { + // Probably unnecessary but just in case: + destroyPhysicalJar(); + String codeNameBase1 = getCodeNameBase(); + localizedProps = null; + loadManifest(); + parseManifest(); + findExtensionsAndVariants(manifest); + String codeNameBase2 = getCodeNameBase(); + if (! codeNameBase1.equals(codeNameBase2)) { + throw new InvalidException("Code name base changed during reload: " + codeNameBase1 + " -> " + codeNameBase2); // NOI18N + } + } + + // Access from ModuleManager: + /** Turn on the classloader. Passed a list of parent modules to use. + * The parents should already have had their classloaders initialized. + */ + public void classLoaderUp(Set parents) throws IOException { + Util.err.log("classLoaderUp on " + this + " with parents " + parents); + // Find classloaders for dependent modules and parent to them. + List loaders = new ArrayList(parents.size() + 1); // List + // This should really be the base loader created by org.nb.Main for loading openide etc.: + loaders.add(Module.class.getClassLoader()); + Iterator it = parents.iterator(); + while (it.hasNext()) { + Module parent = (Module)it.next(); + PackageExport[] exports = parent.getPublicPackages(); + if (exports != null && exports.length == 0) { + // Check if there is an impl dep here. + Dependency[] deps = getDependenciesArray(); + boolean implDep = false; + for (int i = 0; i < deps.length; i++) { + if (deps[i].getType() == Dependency.TYPE_MODULE && + deps[i].getComparison() == Dependency.COMPARE_IMPL && + deps[i].getName().equals(parent.getCodeName())) { + implDep = true; + break; + } + } + if (!implDep) { + // Nothing exported from here at all, no sense even adding it. + // Shortcut; would not harm anything to add it now, but we would + // never use it anyway. + // Cf. #27853. + continue; + } + } + ClassLoader l = parent.getClassLoader(); + if (parent.isFixed() && loaders.contains(l)) { + Util.err.log("#24996: skipping duplicate classloader from " + parent); + continue; + } + loaders.add(l); + } + List classp = new ArrayList(3); // List + if (patches != null) { + for (it = patches.iterator(); it.hasNext(); ) { + File f = (File)it.next(); + if (f.isDirectory()) { + classp.add(f); + } else { + classp.add(new JarFile(f, false)); + } + } + } + if (reloadable) { + ensurePhysicalJar(); + // Using OPEN_DELETE does not work well with test modules under 1.4. + // Random code (URL handler?) still expects the JAR to be there and + // it is not. + classp.add(new JarFile(physicalJar, false)); + } else { + classp.add(new JarFile(jar, false)); + } + // URLClassLoader would not otherwise find these, so: + if (localeVariants != null) { + for (it = localeVariants.iterator(); it.hasNext(); ) { + classp.add(new JarFile((File)it.next(), false)); + } + } + if (localeExtensions != null) { + for (it = localeExtensions.iterator(); it.hasNext(); ) { + File act = (File)it.next(); + classp.add(act.isDirectory() ? (Object)act : new JarFile(act, false)); + } + } + if (plainExtensions != null) { + for( it = plainExtensions.iterator(); it.hasNext(); ) { + File act = (File)it.next(); + classp.add(act.isDirectory() ? (Object)act : new JarFile(act, false)); + } + } + + // #27853: + getManager().refineClassLoader(this, loaders); + + try { + classloader = new OneModuleClassLoader(classp, (ClassLoader[])loaders.toArray(new ClassLoader[loaders.size()])); + } catch (IllegalArgumentException iae) { + // Should not happen, but just in case. + IOException ioe = new IOException(iae.toString()); + Util.err.annotate(ioe, iae); + throw ioe; + } + } + + /** Turn off the classloader and release all resources. */ + public void classLoaderDown() { + if (classloader instanceof ProxyClassLoader) { + ((ProxyClassLoader)classloader).destroy(); + } + classloader = null; + Util.err.log("classLoaderDown on " + this + ": releaseCount=" + releaseCount + " released=" + released); + released = false; + } + /** Should be called after turning off the classloader of one or more modules & GC'ing. */ + public void cleanup() { + if (isEnabled()) throw new IllegalStateException("cleanup on enabled module: " + this); // NOI18N + if (classloader != null) throw new IllegalStateException("cleanup on module with classloader: " + this); // NOI18N + if (! released) { + Util.err.log("Warning: not all resources associated with module " + jar + " were successfully released."); + released = true; + } else { + Util.err.log ("All resources associated with module " + jar + " were successfully released."); + } + // XXX should this rather be done when the classloader is collected? + destroyPhysicalJar(); + } + + /** Notify the module that it is being deleted. */ + public void destroy() { + moduleJARs.remove(jar); + } + + /** String representation for debugging. */ + public String toString() { + String s = "StandardModule:" + getCodeNameBase() + " jarFile: " + jar.getAbsolutePath(); // NOI18N + if (!isValid()) s += "[invalid]"; // NOI18N + return s; + } + + /** PermissionCollection with an instance of AllPermission. */ + private static PermissionCollection modulePermissions; + /** @return initialized @see #modulePermission */ + private static synchronized PermissionCollection getAllPermission() { + if (modulePermissions == null) { + modulePermissions = new Permissions(); + modulePermissions.add(new AllPermission()); + modulePermissions.setReadOnly(); + } + return modulePermissions; + } + + /** Class loader to load a single module. + * Auto-localizing, multi-parented, permission-granting, the works. + */ + private class OneModuleClassLoader extends JarClassLoader implements Util.ModuleProvider, Util.PackageAccessibleClassLoader { + private int rc; + /** Create a new loader for a module. + * @param classp the List of all module jars of code directories; + * includes the module itself, its locale variants, + * variants of extensions and Class-Path items from Manifest. + * The items are JarFiles for jars and Files for directories + * @param parents a set of parent classloaders (from other modules) + */ + public OneModuleClassLoader(List classp, ClassLoader[] parents) throws IllegalArgumentException { + super(classp, parents, false); + rc = releaseCount++; + } + + public Module getModule() { + return StandardModule.this; + } + + /** Inherited. + * @param cs is ignored + * @return PermissionCollection with an AllPermission instance + */ + protected PermissionCollection getPermissions(CodeSource cs) { + return getAllPermission(); + } + + /** look for JNI libraries also in modules/bin/ */ + protected String findLibrary(String libname) { + String mapped = System.mapLibraryName(libname); + File lib = new File(new File(jar.getParentFile(), "lib"), mapped); // NOI18N + if (lib.isFile()) { + return lib.getAbsolutePath(); + } else { + return null; + } + } + + protected boolean isSpecialResource(String pkg) { + if (mgr.isSpecialResource(pkg)) { + return true; + } + return super.isSpecialResource(pkg); + } + + + protected boolean shouldDelegateResource(String pkg, ClassLoader parent) { + if (!super.shouldDelegateResource(pkg, parent)) { + return false; + } + Module other; + if (parent instanceof Util.ModuleProvider) { + other = ((Util.ModuleProvider)parent).getModule(); + } else { + other = null; + } + return getManager().shouldDelegateResource(StandardModule.this, other, pkg); + } + + public String toString() { + return super.toString() + "[" + getCodeNameBase() + "]"; // NOI18N + } + + protected void finalize() throws Throwable { + super.finalize(); + Util.err.log("Finalize for " + this + ": rc=" + rc + " releaseCount=" + releaseCount + " released=" + released); // NOI18N + if (rc == releaseCount) { + // Hurrah! release() worked. + released = true; + } else { + Util.err.log("Now resources for " + getCodeNameBase() + " have been released."); // NOI18N + } + } + } + +} Index: bootstrap/src/org/netbeans/Util.java =================================================================== RCS file: /cvs/core/bootstrap/src/org/netbeans/Util.java,v retrieving revision 1.2 diff -u -r1.2 Util.java --- bootstrap/src/org/netbeans/Util.java 14 Jun 2005 12:34:26 -0000 1.2 +++ bootstrap/src/org/netbeans/Util.java 22 Aug 2005 19:59:20 -0000 @@ -303,7 +303,7 @@ } /** Interface to permit a couple of methods in ClassLoader to be made public. */ - interface PackageAccessibleClassLoader { + public interface PackageAccessibleClassLoader { /** @see ClassLoader#getPackage */ Package getPackageAccessibly (String name); /** @see ClassLoader#getPackages */ @@ -311,7 +311,7 @@ } /** Interface for a classloader to declare that it comes from a module. */ - interface ModuleProvider { + public interface ModuleProvider { Module getModule(); } Index: src/org/netbeans/core/LookupCache.java =================================================================== RCS file: /cvs/core/src/org/netbeans/core/LookupCache.java,v retrieving revision 1.6 diff -u -r1.6 LookupCache.java --- src/org/netbeans/core/LookupCache.java 4 Jun 2005 05:11:05 -0000 1.6 +++ src/org/netbeans/core/LookupCache.java 22 Aug 2005 19:59:25 -0000 @@ -37,6 +37,7 @@ import java.util.List; import java.util.Locale; import java.util.StringTokenizer; +import org.netbeans.StandardModule; import org.openide.ErrorManager; import org.openide.filesystems.FileObject; import org.openide.filesystems.Repository; @@ -171,7 +172,7 @@ */ private static File cacheFile() { String ud = System.getProperty("netbeans.user"); - if (ud != null) { + if ((ud != null) && (! ud.equals("memory"))) { File cachedir = new File(new File (ud, "var"), "cache"); // NOI18N cachedir.mkdirs(); return new File(cachedir, "folder-lookup.ser"); // NOI18N @@ -186,7 +187,7 @@ */ private static File stampFile() { String ud = System.getProperty("netbeans.user"); - if (ud != null) { + if ((ud != null) && (! ud.equals("memory"))) { File cachedir = new File(new File (ud, "var"), "cache"); // NOI18N cachedir.mkdirs(); return new File(cachedir, "lookup-stamp.txt"); // NOI18N @@ -217,7 +218,7 @@ Module m = (Module)it.next(); String layer = (String)m.getAttribute("OpenIDE-Module-Layer"); // NOI18N if (layer != null) { - if (!m.isFixed()) { + if (m.getJarFile() != null) { files.add(m.getJarFile()); } else { URL layerURL = m.getClassLoader().getResource(layer); Index: startup/src/org/netbeans/core/startup/ModuleList.java =================================================================== RCS file: /cvs/core/startup/src/org/netbeans/core/startup/ModuleList.java,v retrieving revision 1.3 diff -u -r1.3 ModuleList.java --- startup/src/org/netbeans/core/startup/ModuleList.java 3 Aug 2005 19:09:50 -0000 1.3 +++ startup/src/org/netbeans/core/startup/ModuleList.java 22 Aug 2005 19:59:32 -0000 @@ -300,7 +300,7 @@ // We are going to try to turn it on... status.pendingInstall = false; Module m = status.module; - if (m.isEnabled() || m.isFixed() || m.isAutoload() || m.isEager()) throw new IllegalStateException(); + if (m.isEnabled() || m.isAutoload() || m.isEager()) throw new IllegalStateException(); maybeEnable.add(m); } } @@ -1103,7 +1103,7 @@ Iterator it = mgr.getModules().iterator(); while (it.hasNext()) { Module m = (Module)it.next(); - if (m.isFixed()) { + if (m.isFixed() || m.getAllJars().isEmpty()) { // No way, we don't manage these. continue; } Index: startup/src/org/netbeans/core/startup/ModuleSystem.java =================================================================== RCS file: /cvs/core/startup/src/org/netbeans/core/startup/ModuleSystem.java,v retrieving revision 1.1 diff -u -r1.1 ModuleSystem.java --- startup/src/org/netbeans/core/startup/ModuleSystem.java 4 Jun 2005 05:11:46 -0000 1.1 +++ startup/src/org/netbeans/core/startup/ModuleSystem.java 22 Aug 2005 19:59:33 -0000 @@ -26,6 +26,7 @@ import java.util.jar.Manifest; import java.net.URL; import java.net.MalformedURLException; +import org.openide.util.Lookup; /** Controller of the IDE's whole module system. * Contains higher-level convenience methods to @@ -284,15 +285,17 @@ Iterator it = mgr.getModules().iterator(); while (it.hasNext()) { Module m = (Module)it.next(); - if (jar.equals(m.getJarFile())) { - // Hah, found it. - if (! m.isReloadable()) { - m.setReloadable(true); + if (m.getJarFile() != null) { + if (jar.equals(m.getJarFile())) { + // Hah, found it. + if (! m.isReloadable()) { + m.setReloadable(true); + } + turnOffModule(m, toReenable); + mgr.reload(m); + tm = m; + break; } - turnOffModule(m, toReenable); - mgr.reload(m); - tm = m; - break; } } if (tm == null) { @@ -303,7 +306,7 @@ tm = mgr.create(jar, new ModuleHistory(jar.getAbsolutePath()), true, false, false); } catch (DuplicateException dupe) { Module old = dupe.getOldModule(); - System.err.println("Replacing old module in " + old.getJarFile()); // NOI18N + System.err.println("Replacing old module in " + old); // NOI18N turnOffModule(old, toReenable); mgr.delete(old); try { @@ -317,9 +320,9 @@ } } // Try to turn on the test module. It might throw InvalidExc < IOExc. - System.err.println("Enabling " + tm.getJarFile() + "..."); // NOI18N + System.err.println("Enabling " + tm + "..."); // NOI18N if (!mgr.simulateEnable(Collections.singleton(tm)).contains(tm)) { - throw new IOException("Cannot enable " + tm.getJarFile() + "; problems: " + tm.getProblems()); + throw new IOException("Cannot enable " + tm + "; problems: " + tm.getProblems()); } mgr.enable(tm); // OK, so far so good; also try to turn on any other modules if @@ -367,7 +370,7 @@ } } try { - System.err.println("Disabling " + m.getJarFile() + "..."); // NOI18N + System.err.println("Disabling " + m + "..."); // NOI18N // Don't mention the others, they will be mentioned later anyway. mgr.disable(toReenable); } finally { @@ -401,5 +404,5 @@ QuietEvents() {} protected void logged(String message, Object[] args) {} } - + } Index: startup/src/org/netbeans/core/startup/NbInstaller.java =================================================================== RCS file: /cvs/core/startup/src/org/netbeans/core/startup/NbInstaller.java,v retrieving revision 1.13 diff -u -r1.13 NbInstaller.java --- startup/src/org/netbeans/core/startup/NbInstaller.java 22 Aug 2005 18:40:30 -0000 1.13 +++ startup/src/org/netbeans/core/startup/NbInstaller.java 22 Aug 2005 19:59:37 -0000 @@ -912,7 +912,10 @@ if ("org/openide/util/".equals (pkg)) return true; // NOI18N } - + // IBM's stuff goes here for now: + if (pkg.startsWith("org/omg/CORBA/")) return true; // NOI18N + if (pkg.startsWith("com/ibm/ejs/util/")) return true; // NOI18N + if (pkg.startsWith("javax/rmi/CORBA/")) return true; // NOI18N // Some classes like DOMError are only in xerces.jar, not in JDK: if (pkg.equals("org/w3c/dom/")) return true; // NOI18N @@ -1338,6 +1341,7 @@ while (it.hasNext()) { Module m = (Module)it.next(); if (m.isFixed()) continue; + if (m.getJarFile() == null) continue; File jar = m.getJarFile(); // Note: extension JARs not checked. try { Index: startup/src/org/netbeans/core/startup/TopLogging.java =================================================================== RCS file: /cvs/core/startup/src/org/netbeans/core/startup/TopLogging.java,v retrieving revision 1.1 diff -u -r1.1 TopLogging.java --- startup/src/org/netbeans/core/startup/TopLogging.java 4 Jun 2005 05:11:50 -0000 1.1 +++ startup/src/org/netbeans/core/startup/TopLogging.java 22 Aug 2005 19:59:38 -0000 @@ -33,7 +33,7 @@ private static final boolean disabledConsole = ! Boolean.getBoolean("netbeans.logger.console"); // NOI18N - private final PrintStream logPrintStream; + private PrintStream logPrintStream; private static TopLogging topLogging; @@ -51,6 +51,29 @@ */ TopLogging (String logDir) throws IOException { topLogging = this; + + DateFormat df = DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL); + java.util.Date date = new java.util.Date(); + if (logDir == null) { + // no demultiplexing -- everything goes just to stderr. + logPrintStream = System.err; + logPrintStream.println("-------------------------------------------------------------------------------"); // NOI18N + logPrintStream.println(">Log Session: "+df.format (date)); // NOI18N + logPrintStream.println(">System Info: "); // NOI18N + try { + printSystemInfo(logPrintStream); + } catch (Throwable t) { + // Serious problems. + t.printStackTrace(); + logPrintStream.flush(); + } + logPrintStream.println("-------------------------------------------------------------------------------"); // NOI18N + logPrintStream = new PrintStream(new OutputStream() { + public void write(int b) { /* intentionally empty */ } + }); + return; + } + OutputStream log = null; File logFileDir = new File (logDir); if (! logFileDir.exists () && ! logFileDir.mkdirs ()) { @@ -61,9 +84,7 @@ throw new IOException ("Cannot write to file"); // NOI18N } - OutputStream log = new BufferedOutputStream(new FileOutputStream(logFile.getAbsolutePath(), true)); - DateFormat df = DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL, Locale.ENGLISH); - java.util.Date date = new java.util.Date(); + log = new BufferedOutputStream(new FileOutputStream(logFile.getAbsolutePath(), true)); final PrintStream stderr = System.err; logPrintStream = new PrintStream(new StreamDemultiplexor(stderr, log), false, "UTF-8"); // NOI18N @@ -214,7 +235,7 @@ private static final int FLUSH_DELAY = Integer.getInteger("netbeans.logger.flush.delay", 15000).intValue(); // NOI18N private final OutputStream stderr; - private final OutputStream log; + private OutputStream log; StreamDemultiplexor(PrintStream stderr, OutputStream log) { this.stderr = stderr; Index: startup/test/unit/src/org/netbeans/core/startup/ModuleFactoryTest.java =================================================================== RCS file: startup/test/unit/src/org/netbeans/core/startup/ModuleFactoryTest.java diff -N startup/test/unit/src/org/netbeans/core/startup/ModuleFactoryTest.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ startup/test/unit/src/org/netbeans/core/startup/ModuleFactoryTest.java 22 Aug 2005 19:59:38 -0000 @@ -0,0 +1,113 @@ +/* + * ModuleFactoryTest.java + * + * Created on July 24, 2005, 5:36 PM + * + * To change this template, choose Tools | Options and locate the template under + * the Source Creation and Management node. Right-click the template and choose + * Open. You can then make changes to the template in the Source Editor. + */ + +package org.netbeans.core.startup; + +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.Arrays; +import java.util.HashSet; +import java.util.jar.JarFile; +import java.util.jar.Manifest; +import org.netbeans.DuplicateException; +import org.netbeans.Events; +import org.netbeans.FixedModule; +import org.netbeans.InvalidException; +import org.netbeans.Module; +import org.netbeans.ModuleFactory; +import org.netbeans.ModuleManager; +import org.netbeans.StandardModule; +import org.openide.util.Lookup; +import org.openide.util.lookup.Lookups; +import org.openide.util.lookup.ProxyLookup; + +/** + * These tests verify that the module manager behaves basically the + * same way with ModuleFactory as without it. + * @author david + */ +public class ModuleFactoryTest extends ModuleManagerTest { + + static { + System.setProperty("org.openide.util.Lookup", L.class.getName()); + assertTrue(Lookup.getDefault() instanceof L); + } + + /** Creates a new instance of ModuleFactoryTest */ + public ModuleFactoryTest(String name) { + super(name); + } + + public static int numberOfStandard = 0; + public static int numberOfFixed = 0; + + public void testFactoryCreatesOurModules() throws Exception { + // clear the counters before the test! + numberOfStandard = 0; + numberOfFixed = 0; + + FakeModuleInstaller installer = new FakeModuleInstaller(); + FakeEvents ev = new FakeEvents(); + ModuleManager mgr = new ModuleManager(installer, ev); + mgr.mutexPrivileged().enterWriteAccess(); + try { + File j1 = new File(jars, "simple-module.jar"); + File j2 = new File(jars, "depends-on-simple-module.jar"); + File j3 = new File(jars, "dep-on-two-modules.jar"); + URLClassLoader l = new URLClassLoader(new URL[] {j1.toURL(), j2.toURL()}); + Manifest mani1, mani2; + JarFile j = new JarFile(j1); + try { + mani1 = j.getManifest(); + } finally { + j.close(); + } + j = new JarFile(j2); + try { + mani2 = j.getManifest(); + } finally { + j.close(); + } + Module m1 = mgr.createFixed(mani1, null, l); + Module m2 = mgr.createFixed(mani2, null, l); + Module m3 = mgr.create(j3, null, false, false, false); + mgr.enable(new HashSet(Arrays.asList(new Module[] {m1, m2, m3}))); + } finally { + mgr.mutexPrivileged().exitWriteAccess(); + } + assertEquals("Number of standard modules created by our factory ", 1, numberOfStandard); + assertEquals("Number of fixed modules created by our factory ", 2, numberOfFixed); + } + + + public static final class L extends ProxyLookup { + public L() { + super(new Lookup[] { + Lookups.fixed(new Object[] { + new MyModuleFactory() + }), + }); + } + } + + public static final class MyModuleFactory extends ModuleFactory { + public Module create(File jar, Object history, boolean reloadable, boolean autoload, boolean eager, ModuleManager mgr, Events ev) throws IOException, DuplicateException { + numberOfStandard++; + return new StandardModule(mgr, ev, jar, history, reloadable, autoload, eager); + } + + public Module createFixed(Manifest mani, Object history, ClassLoader loader, ModuleManager mgr, Events ev) throws InvalidException, DuplicateException { + numberOfFixed++; + return new FixedModule(mgr, ev, mani, history, loader); + } + } +}