Index: nb_all/core/build.xml =================================================================== RCS file: /cvs/core/build.xml,v --- nb_all/core/build.xml 29 Jul 2002 10:11:08 -0000 1.54 +++ nb_all/core/build.xml 29 Jul 2002 19:26:33 -0000 @@ -48,18 +48,35 @@ + + + + + + + + - - + + + + + + /** Creates new JarClassLoader */ + public JarClassLoader (List files, ClassLoader[] parents) { + super(parents); + + sources = new Source[files.size()]; + try { + int i=0; + for (Iterator it = files.iterator(); it.hasNext(); i++ ) { + Object act = it.next(); + if (act instanceof File) { + sources[i] = new DirSource((File)act); + } else { + sources[i] = new JarSource((JarFile)act); + } + } + } catch (MalformedURLException exc) { + throw new IllegalArgumentException(exc.getMessage()); + } + + } + + /** Allows to specify the right permissions, OneModuleClassLoader does it differently. + */ + protected PermissionCollection getPermissions( CodeSource cs ) { + return Policy.getPolicy().getPermissions(cs); + } + + + protected Package definePackage(String name, Manifest man, URL url) + throws IllegalArgumentException + { + if (man == null ) { + return definePackage(name, null, null, null, null, null, null, null); + } + + String path = name.replace('.', '/').concat("/"); // NOI18N + Attributes spec = man.getAttributes(path); + Attributes main = man.getMainAttributes(); + + String specTitle = getAttr(spec, main, Name.SPECIFICATION_TITLE); + String implTitle = getAttr(spec, main, Name.IMPLEMENTATION_TITLE); + String specVersion = getAttr(spec, main, Name.SPECIFICATION_VERSION); + String implVersion = getAttr(spec, main, Name.IMPLEMENTATION_VERSION); + String specVendor = getAttr(spec, main, Name.SPECIFICATION_VENDOR); + String implVendor = getAttr(spec, main, Name.IMPLEMENTATION_VENDOR); + String sealed = getAttr(spec, main, Name.SEALED); + + URL sealBase = "true".equalsIgnoreCase(sealed) ? url : null; // NOI18N + return definePackage(name, specTitle, specVersion, specVendor, + implTitle, implVersion, implVendor, sealBase); + } + + private static String getAttr(Attributes spec, Attributes main, Name name) { + String val = null; + if (spec != null) val = spec.getValue (name); + if (val == null && main != null) val = main.getValue (name); + return val; + } + + protected Class simpleFindClass(String name, String path) { + // look up the Sources and return a class based on their content + for( int i=0; i + try { + for (int i = 0; i < sources.length; i++) { + if (sources[i] instanceof JarSource) { + JarFile origJar = ((JarSource)sources[i]).getJarFile(); + File orig = new File(origJar.getName()); + if (!orig.isFile()) { + // Can happen when a test module is deleted: + // the physical JAR has already been deleted + // when the module was disabled. In this case it + // is possible that a classloader request for something + // in the JAR could still come in. Does it matter? + // See comment in Module.cleanup. + continue; + } + String name = orig.getName(); + String prefix, suffix; + int idx = name.lastIndexOf('.'); + if (idx == -1) { + prefix = name; + suffix = null; + } else { + prefix = name.substring(0, idx); + suffix = name.substring(idx); + } + while (prefix.length() < 3) prefix += "x"; // NOI18N + File temp = File.createTempFile(prefix, suffix); + temp.deleteOnExit(); + InputStream is = new FileInputStream(orig); + try { + OutputStream os = new FileOutputStream(temp); + try { + byte[] buf = new byte[4096]; + int j; + while ((j = is.read(buf)) != -1) { + os.write(buf, 0, j); + } + } finally { + os.close(); + } + } finally { + is.close(); + } + // Don't use OPEN_DELETE even though it sounds like a good idea. + // Can cause real problems under 1.4; see Module.java. + JarFile tempJar = new JarFile(temp); + origJar.close(); + forceRelease(orig); + deadJars.add(tempJar); + sources[i] = new JarSource(tempJar); + log("#21114: replacing " + orig + " with " + temp); + } + } + } catch (IOException ioe) { + JarClassLoader.notify(0, ioe); + } + } + + /** Release jar: locks when the classloader is shut down. + * Should help reloading modules with changed resources. + */ + public void destroy() { + super.destroy(); + for (int i = 0; i < sources.length; i++) { + if (sources[i] instanceof JarSource) { + JarFile j = ((JarSource)sources[i]).getJarFile(); + File f = new File(j.getName()); + forceRelease(f); + } + } + } + + /** Delete any temporary JARs we were holding on to. + * Also close any other JARs in our list. + */ + protected void finalize() throws Throwable { + super.finalize(); + for (int i = 0; i < sources.length; i++) { + if (sources[i] instanceof JarSource) { + JarFile j = ((JarSource)sources[i]).getJarFile(); + File f = new File(j.getName()); + j.close(); + forceRelease(f); + if (deadJars != null && deadJars.contains(j)) { + log("#21114: closing and deleting temporary JAR " + f); + if (f.isFile() && !f.delete()) { + log("(but failed to delete it)"); + } + } + } + } + } + + /** Make sure the Java runtime's jar: URL cache is not holding + * onto the specified file. + * Workaround for JDK bug #4646668. + */ + private static void forceRelease(File f) { + if (fileCache == null || factory == null) return; + try { + synchronized (factory) { + Iterator it = fileCache.values().iterator(); + while (it.hasNext()) { + JarFile j = (JarFile)it.next(); + if (f.equals(new File(j.getName()))) { + j.close(); + it.remove(); + log("Removing jar: cache for " + f + " as workaround for JDK #4646668"); + } + } + } + } catch (Exception e) { + JarClassLoader.annotate(e, 0, "Could not remove jar: cache for " + f, null, null, null); + JarClassLoader.notify(0, e); + } + } + private static Object factory = null; + private static HashMap fileCache = null; + static { + try { + Class juc = Class.forName("sun.net.www.protocol.jar.JarURLConnection"); // NOI18N + Field factoryF = juc.getDeclaredField("factory"); // NOI18N + factoryF.setAccessible(true); + factory = factoryF.get(null); + Class jff = Class.forName("sun.net.www.protocol.jar.JarFileFactory"); // NOI18N + if (!jff.isInstance(factory)) throw new ClassCastException(factory.getClass().getName()); + Field fileCacheF = jff.getDeclaredField("fileCache"); // NOI18N + fileCacheF.setAccessible(true); + if (Modifier.isStatic(fileCacheF.getModifiers())) { + // JDK 1.3.1 or 1.4 seems to have it static. + fileCache = (HashMap)fileCacheF.get(null); + } else { + // But in 1.3.0 it appears to be an instance var. + fileCache = (HashMap)fileCacheF.get(factory); + } + log("Workaround for JDK #4646668 active as part of IZ #21114"); + } catch (Exception e) { + JarClassLoader.annotate(e, 0, "Workaround for JDK #4646668 as part of IZ #21114 failed", null, null, null); + JarClassLoader.notify(0, e); + } + } + + abstract class Source { + private URL url; + private ProtectionDomain pd; + + public Source(URL url) { + this.url = url; + CodeSource cs = new CodeSource(url, new Certificate[0]); + pd = new ProtectionDomain(cs, getPermissions(cs)); + } + + public final URL getURL() { + return url; + } + + public final ProtectionDomain getProtectionDomain() { + return pd; + } + + public final URL getResource(String name) { + try { + return doGetResource(name); + } catch (MalformedURLException e) { + log(e.toString()); + } + return null; + } + + protected abstract URL doGetResource(String name) throws MalformedURLException; + + public final byte[] getClassData(String name, String path) { + try { + return readClass(name, path); + } catch (IOException e) { + log(e.toString()); + } + return null; + } + + protected abstract byte[] readClass(String name, String path) throws IOException; + + public Manifest getManifest() { + return null; + } + } + + class JarSource extends Source { + JarFile src; + + public JarSource(JarFile file) throws MalformedURLException { + super(new URL("file:" + file.getName())); + src = file; + } + + public Manifest getManifest() { + try { + return src.getManifest(); + } catch (IOException e) { + return null; + } + } + + JarFile getJarFile() { + return src; + } + + protected URL doGetResource(String name) throws MalformedURLException { + ZipEntry ze = src.getEntry(name); + return ze == null ? null : new URL("jar:file:" + src.getName() + "!/" + ze.getName()); // NOI18N + } + + protected byte[] readClass(String name, String path) throws IOException { + ZipEntry ze = src.getEntry(path); + if (ze == null) return null; + + int len = (int)ze.getSize(); + byte[] data = new byte[len]; + InputStream is = src.getInputStream(ze); + int count = 0; + while (count < len) { + count += is.read(data, count, len-count); + } + return data; + } + } + + class DirSource extends Source { + File dir; + + public DirSource(File file) throws MalformedURLException { + super(file.toURL()); + dir = file; + } + + protected URL doGetResource(String name) throws MalformedURLException { + File resFile = new File(dir, name); + return resFile.exists() ? resFile.toURL() : null; + } + + protected byte[] readClass(String name, String path) throws IOException { + File clsFile = new File(dir, path.replace('/', File.separatorChar)); + if (!clsFile.exists()) return null; + + int len = (int)clsFile.length(); + byte[] data = new byte[len]; + InputStream is = new FileInputStream(clsFile); + int count = 0; + while (count < len) { + count += is.read(data, count, len-count); + } + return data; + } + + } + + + // + // ErrorManager's methods + // + + static void log (String msg) { + System.err.println(msg); + } + + static Throwable annotate ( + Throwable t, int x, String s, Object o1, Object o2, Object o3 + ) { + System.err.println("annotated: " + t.getMessage () + " - " + s); + return t; + } + + static void notify (int x, Throwable t) { + t.printStackTrace(); + } +} Index: nb_all/core/src/org/netbeans/Main.java =================================================================== RCS file: /cvs/core/src/org/netbeans/Main.java,v --- nb_all/core/src/org/netbeans/Main.java 29 Jul 2002 10:11:12 -0000 1.14 +++ nb_all/core/src/org/netbeans/Main.java 29 Jul 2002 19:26:34 -0000 @@ -13,6 +13,13 @@ package org.netbeans; +import java.io.File; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.ArrayList; +import java.util.StringTokenizer; +import java.lang.reflect.Method; +import java.util.Collection; import org.netbeans.core.perftool.StartLog; /** The Corona's main class. Starts up the IDE. @@ -23,8 +30,78 @@ /** Starts the IDE. * @param args the command line arguments */ - public static void main (String args[]) { - StartLog.logStart( "Forwarding to topThreadGroup" ); // NOI18N - org.netbeans.core.Main.start(args); + public static void main (String args[]) throws Exception { + ArrayList list = new ArrayList (); + + String user = System.getProperty("netbeans.user"); // NOI18N + if (user != null) { + build_cp (new File (user), list); + } + + String home = System.getProperty ("netbeans.home"); // NOI18N + if (home != null) { + build_cp (new File (home), list); + } + + java.util.ListIterator it = list.listIterator(); + while (it.hasNext()) { + File f = (File)it.next(); + it.set(new java.util.jar.JarFile (f)); + } + + + ClassLoader loader = new JarClassLoader (list, new ClassLoader[] { + Main.class.getClassLoader() + }); + + String className = System.getProperty( + "netbeans.mainclass", "org.netbeans.core.Main" // NOI18N + ); + + Class c = loader.loadClass(className); + Method m = c.getMethod ("main", new Class[] { String[].class }); // NOI18N + + Thread.currentThread().setContextClassLoader (loader); + m.invoke (null, new Object[] { args }); + } + + + private static void append_jars_to_cp (File dir, Collection toAdd) { + /* + for ex in jar zip ; do + if [ "`echo "${dir}"/*.$ex`" != "${dir}/*.$ex" ] ; then + for x in "${dir}"/*.$ex ; do + # XXX also filter out lib/locale/updater*.jar to make Japanese startup faster + # (and put it back in below when building ${updatercp} + if [ "$x" != "$idehome/lib/updater.jar" -a "$x" != "$userdir/lib/updater.jar" ] ; then + if [ ! -z "$cp" ] ; then cp="$cp:" ; fi + cp="$cp$x" + fi + done + fi + done + */ + + if (!dir.isDirectory()) return; + + File[] arr = dir.listFiles(); + for (int i = 0; i < arr.length; i++) { + String n = arr[i].getName (); + if (n.endsWith("jar") || n.endsWith ("zip")) { // NOI18N + toAdd.add (arr[i]); + } + } + } + + + private static void build_cp(File base, Collection toAdd) { + append_jars_to_cp (new File (base, "lib/patches/locale"), toAdd); + append_jars_to_cp (new File (base, "lib/patches"), toAdd); + + append_jars_to_cp (new File (base, "lib/locale"), toAdd); + append_jars_to_cp (new File (base, "lib"), toAdd); + + //append_jars_to_cp (new File (base, "lib/ext/locale"), toAdd); + //append_jars_to_cp (new File (base, "lib/ext"), toAdd); } } Index: nb_all/core/src/org/netbeans/PatchByteCode.java =================================================================== RCS file: nb_all/core/src/org/netbeans/PatchByteCode.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ nb_all/core/src/org/netbeans/PatchByteCode.java 29 Jul 2002 19:26:34 -0000 @@ -0,0 +1,426 @@ +/* + * 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-2002 Sun + * Microsystems, Inc. All Rights Reserved. + */ + +package org.netbeans; + +import java.io.*; + +/** Class that can enhance bytecode with information about alternative + * superclass and also extract this information later when the class + * is about to be loaded into the VM. + * + * @author Jaroslav Tulach + */ +public final class PatchByteCode { + private static final String ATTR_SUPERCLASS = "org.netbeans.superclass"; // NOI18N + private static final byte[] BYTE_SUPERCLASS; + static { + try { + BYTE_SUPERCLASS = ATTR_SUPERCLASS.getBytes("utf-8"); // NOI18N + } catch (java.io.UnsupportedEncodingException ex) { + throw new IllegalStateException (ex.getMessage()); + } + } + + + private byte[] arr; + + private int cpCount; + private int cpEnd; + private int atCount; + private int atEnd; + + /** the index of a string that matches the searched attribute */ + private int superClassNameIndex; + /** the possiton of found attribute */ + private int superClassNameAttr; + + /** Creates a new instance of PatchByteCode */ + private PatchByteCode(byte[] arr) { + this.arr = arr; + scan (); + } + + + /** Generates patch attribute into the classfile to + * allow method patch to modify the superclass of this + * class. + * + * @param arr the bytecode to change + * @param superClass name of the new superclass like java/awt/Button + * @return new version of the bytecode if changed, otherwise null to signal that + * no change has been made + */ + public static byte[] enhance (byte[] arr, String superClass) { + if (isPatched (arr)) { + // already patched + return null; + } + + PatchByteCode pc = new PatchByteCode (arr); + + if (superClass != null) { + int x = pc.addClass (superClass); + + + byte[] sup = new byte[2]; + writeU2 (sup, 0, x); + pc.addAttribute (ATTR_SUPERCLASS, sup); + + byte[] patch = { + 'n', 'b' // identification at the end of class file + }; + + pc.addAttribute ("org.netbeans.enhanced", patch); + } + + // if we patch the class then it is supposed to be public + if (!pc.makePublic () && superClass == null) { + // if it already was public and no superclass change + return null; + } + + + // otherwise do the patching + return pc.getClassFile (); + } + + /** Checks if the class has previously been enhanced by the + * change of superclass attribute and if so, changes the bytecode + * to reflect the change. + * + * @param arr the bytecode + * @return the enhanced bytecode + */ + public static byte[] patch (byte[] arr) { + if (!isPatched (arr)) return arr; + + PatchByteCode pc = new PatchByteCode (arr); + if (pc.superClassNameAttr > 0) { + // let's patch + int classindex = pc.readU2 (pc.superClassNameAttr + 6); + + writeU2 (pc.getClassFile(), pc.cpEnd + 4, classindex); + } + + return pc.getClassFile (); + } + + + /** Check if the byte code is patched. + * @param arr the bytecode + * @return true if patched + */ + private static boolean isPatched (byte[] arr) { + if (arr == null || arr.length < 2) return false; + + int base = arr.length - 2; + if (arr[base + 1] != 'b') return false; + if (arr[base + 0] != 'n') return false; + + // + // ok, looks like enhanced byte code + // + return true; + } + + + + + + + + /** Gets the current byte array of the actual class file. + * @return bytes of the class file + */ + private byte[] getClassFile () { + return arr; + } + + /** Creates new contant pool entry representing given class. + * @param c name of the class + * @return index of the entry + */ + private int addClass (String s) { + int x = addConstant (s); + + byte[] t = { 7, 0, 0 }; + writeU2 (t, 1, x); + + return addPool (t); + } + + /** Adds a new string constant to the constant pool. + * @param s the string to add + * @return index of the constant + */ + private int addConstant (String s) { + byte[] t; + + try { + t = s.getBytes("utf-8"); // NOI18N + } catch (java.io.UnsupportedEncodingException ex) { + throw new IllegalStateException ("UTF-8 shall be always supported"); // NOI18N + } + + byte[] add = new byte[t.length + 3]; + System.arraycopy (t, 0, add, 3, t.length); + add[0] = 1; // UTF8 contant + writeU2 (add, 1, t.length); + + return addPool (add); + } + + /** Adds this array of bytes as another entry into the constant pool */ + private int addPool (byte[] add) { + byte[] res = new byte[arr.length + add.length]; + + System.arraycopy (arr, 0, res, 0, cpEnd); + // increments number of objects in contant pool + int index = readU2 (cpCount); + writeU2 (res, cpCount, index + 1); + + // adds the content + System.arraycopy (add, 0, res, cpEnd, add.length); + + // and now add the rest of the original array + System.arraycopy (arr, cpEnd, res, cpEnd + add.length, arr.length - cpEnd); + + arr = res; + + cpEnd += add.length; + atCount += add.length; + atEnd += add.length; + + // the index + return index; + } + + /** Ensures that the class is public + * @return true if really patched, false if not + */ + private boolean makePublic () { + int x = readU2 (cpEnd); + + if ((x & 0x0001) != 0) { + return false; + } + + x |= 0x0001; // adds public field + writeU2 (arr, cpEnd, x); + + return true; + } + + /** Adds an attribute to the class file. + * @param name name of the attribute to add + * @param b the bytes representing the attribute + */ + private void addAttribute (String name, byte[] b) { + int index; + if (ATTR_SUPERCLASS.equals (name) && superClassNameIndex > 0) { + index = superClassNameIndex; + } else { + // register new attribute + index = addConstant (name); + } + + byte[] res = new byte[arr.length + b.length + 6]; + + System.arraycopy(arr, 0, res, 0, arr.length); + + writeU2 (res, arr.length, index); + writeU4 (res, arr.length + 2, b.length); + + int begin = arr.length + 6; + System.arraycopy(b, 0, res, begin, b.length); + + atEnd += b.length + 6; + + writeU2 (res, atCount, readU2 (atCount) + 1); + + arr = res; + } + + + /** Gets i-th element from the array. + */ + private int get (int pos) { + if (pos >= arr.length) { + throw new ArrayIndexOutOfBoundsException ("Size: " + arr.length + " index: " + pos); + } + + int x = arr[pos]; + return x >= 0 ? x : 256 + x; + } + + /** Scans the file to find out important possitions + * @return size of the bytecode + */ + private void scan () { + if (get (0) != 0xCA && get (1) != 0xFE && get (2) != 0xBA && get (3) != 0xBE) { + throw new IllegalStateException ("Not a class file!"); // NOI18N + } + + int pos = 10; + // count of items in CP is here + cpCount = 8; + + int cp = readU2 (8); + for (int[] i = { 1 }; i[0] < cp; i[0]++) { + // i[0] can be incremented in constantPoolSize + int len = constantPoolSize (pos, i); + pos += len; + } + + // list item in constant pool + cpEnd = pos; + + pos += 6; + // now add interfaces + pos += 2 * readU2 (pos); + // to add also the integer with interfaces + pos += 2; + + // fields + int fields = readU2 (pos); + pos += 2; + while (fields-- > 0) { + pos += fieldSize (pos); + } + + int methods = readU2 (pos); + pos += 2; + while (methods-- > 0) { + pos += methodSize (pos); + } + + // count of items in Attributes is here + atCount = pos; + int attrs = readU2 (pos); + pos += 2; + while (attrs-- > 0) { + pos += attributeSize (pos); + } + + // end of attributes + atEnd = pos; + } + + private int readU2 (int pos) { + int b1 = get (pos); + int b2 = get (pos + 1); + + return b1 * 256 + b2; + } + + private int readU4 (int pos) { + return readU2 (pos) * 256 * 256 + readU2 (pos + 2); + } + + private static void writeU2 (byte[] arr, int pos, int value) { + int v1 = (value & 0xff00) >> 8; + int v2 = value & 0xff; + + if (v1 < 0) v1 += 256; + if (v2 < 0) v2 += 256; + + arr[pos] = (byte)v1; + arr[pos + 1] = (byte)v2; + } + + private static void writeU4 (byte[] arr, int pos, int value) { + writeU2 (arr, pos, (value & 0xff00) >> 16); + writeU2 (arr, pos + 2, value & 0x00ff); + } + + /** @param pos position to read from + * @param cnt[0] index in the pool that we are now reading + */ + private int constantPoolSize (int pos, int[] cnt) { + switch (get (pos)) { + case 7: // CONSTANT_Class + case 8: // CONSTANT_String + return 3; + case 9: // CONSTANT_Fieldref + case 10: // CONSTANT_Methodref + case 11: // CONSTANT_InterfaceMethodref + case 3: // CONSTANT_Integer + case 4: // CONSTANT_Float + case 12: // CONSTANT_NameAndType + return 5; + case 5: // CONSTANT_Long + case 6: // CONSTANT_Double + // after long and double the next entry in CP is unusable + cnt[0]++; + return 9; + case 1: // CONSTANT_Utf8 + int len = readU2 (pos + 1); + + if (BYTE_SUPERCLASS.length == len) { + int base = pos + 3; + // we are searching for an attribute with given name + for (int i = 0; i < BYTE_SUPERCLASS.length; i++) { + if (BYTE_SUPERCLASS[i] != arr[base + i]) { + // regular exit + return len + 3; + } + } + + // we have found the attribute + if (superClassNameIndex != 0) { + throw new IllegalStateException (ATTR_SUPERCLASS + " registered for the second time!"); // NOI18N + } + + superClassNameIndex = cnt[0]; + } + + // ok, exit + return len + 3; + default: + throw new IllegalStateException ("Unknown pool type: " + get (pos)); // NOI18N + } + } + + private int fieldSize (int pos) { + int s = 8; + int cnt = readU2 (pos + 6); + while (cnt-- > 0) { + s += attributeSize (pos + s); + } + return s; + } + + private int methodSize (int pos) { + return fieldSize (pos); + } + + private int attributeSize (int pos) { + if (superClassNameIndex > 0) { + // index to the name attr + int name = readU2 (pos); + + if (name == superClassNameIndex) { + if (superClassNameAttr > 0) { + throw new IllegalStateException ("Two attributes with name " + ATTR_SUPERCLASS); // NOI18N + } + + // we found the attribute + superClassNameAttr = pos; + } + } + + int len = readU4 (pos + 2); + return len + 6; + } +} Index: nb_all/core/src/org/netbeans/ProxyClassLoader.java =================================================================== RCS file: nb_all/core/src/org/netbeans/ProxyClassLoader.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ nb_all/core/src/org/netbeans/ProxyClassLoader.java 29 Jul 2002 19:26:34 -0000 @@ -0,0 +1,608 @@ +/* + * 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-2002 Sun + * Microsystems, Inc. All Rights Reserved. + */ + +package org.netbeans; + +import java.util.*; +import java.net.URL; +import java.io.IOException; + +/** + * A class loader that has multiple parents and uses them for loading + * classes and resources. It can be used in tree hierarchy, where it + * can exploit its capability to not throw ClassNotFoundException when + * communicating with other ProxyClassLoader. + * It itself doesn't load classes or resources, but allows subclasses + * to add such functionality. + * + * @author Petr Nejedly, Jesse Glick + */ +public class ProxyClassLoader extends ClassLoader { + /** empty enumeration */ + private static final Enumeration EMPTY = new ArrayEnumeration (new Object[0]); + + + // Map + // packages are given in format "org/netbeans/modules/foo/" + private final Map domainsByPackage = new HashMap(); + // Map + private HashMap packages = new HashMap(); + + // All parentf of this classloader, including their parents recursively + private ClassLoader[] parents; + + /** if true, we have been destroyed */ + private boolean dead = false; + + /** Create a multi-parented classloader. + * @param parents list of parent classloaders. + * @throws IllegalArgumentException if there are any nulls or duplicate + * parent loaders or cycles. + */ + public ProxyClassLoader( ClassLoader[] parents ) { + if (parents.length == 0) { + throw new IllegalArgumentException ("ProxyClassLoader must have a parent"); // NOI18N + } + + Set check = new HashSet(Arrays.asList(parents)); // Set + if (check.size() < parents.length) throw new IllegalArgumentException("duplicate parents"); // NOI18N + if (check.contains(null)) throw new IllegalArgumentException("null parent"); // NOI18N + + this.parents = coalesceParents(parents); + } + + // this is used only by system classloader, maybe we can redesign it a bit + // to live without this functionality, then destroy may also go away + /** Add new parents dynamically. + * @param parents the new parents to add (append to list) + * @throws IllegalArgumentException in case of a null or cyclic parent (duplicate OK) + */ + public synchronized void append(ClassLoader[] nueparents) throws IllegalArgumentException { + // XXX should this be synchronized? + if (nueparents == null) throw new IllegalArgumentException("null parents array"); // NOI18N + for (int i = 0; i < nueparents.length; i++) { + if (nueparents[i] == null) throw new IllegalArgumentException("null parent"); // NOI18N + } + + parents = coalesceAppend(parents, nueparents); + } + + + + /** Try to destroy this classloader. + * Subsequent attempts to use it will log an error (at most one though). + */ + public void destroy() { + dead = true; + } + + private void zombieCheck(String hint) { + if (dead) { + IllegalStateException ise = new IllegalStateException("WARNING - attempting to use a zombie classloader " + this + " on " + hint + ". This means classes from a disabled module are still active. May or may not be a problem."); // NOI18N + JarClassLoader.notify(0, ise); + // don't warn again for same loader... this was enough + dead = false; + } + } + + /** + * Loads the class with the specified name. The implementation of + * this method searches for classes in the following order:

+ *

    + *
  1. Calls {@link #findLoadedClass(String)} to check if the class has + * already been loaded. + *
  2. Checks the caches whether another class from the same package + * was already loaded and uses the same classloader + *
  3. Tries to find the class using parent loaders in their order. + *
  4. Calls the {@link #simpleFindClass(String,String)} method to find + * the class using this class loader. + *
+ * + * @param name the name of the class + * @param resolve if true then resolve the class + * @return the resulting Class object + * @exception ClassNotFoundException if the class could not be found + */ + protected synchronized final Class loadClass(String name, boolean resolve) + throws ClassNotFoundException { + zombieCheck(name); + // XXX this section is a candidate for local optimization: + String filename = name.replace('.', '/').concat(".class"); // NOI18N + int idx = filename.lastIndexOf('/'); // NOI18N + if (idx == -1) throw new ClassNotFoundException("Will not load classes from default package"); // NOI18N + String pkg = filename.substring(0, idx + 1); // "org/netbeans/modules/foo/" + Class c = smartLoadClass(name, filename, pkg); + if(c == null) throw new ClassNotFoundException(name); + if (resolve) resolveClass(c); + return c; + } + + /** This ClassLoader can't load anything itself. Subclasses + * may override this method to do some class loading themselves. The + * implementation should not throw any exception, just return + * null if it can't load required class. + * + * @param name the name of the class + * @param fileName the expected filename of the classfile, like + * java/lang/Object.class for java.lang.Object + * The ClassLoader implementation may or may not use it, depending + * whether it is usefull to it. + * @return the resulting Class object or null + */ + protected Class simpleFindClass(String name, String fileName) { + return null; + } + + + /** + * Finds the resource with the given name. The implementation of + * this method searches for resources in the following order:

+ *

    + *
  1. Checks the caches whether another resource or class from the same + * package was already loaded and uses the same classloader. + *
  2. Tries to find the resources using parent loaders in their order. + *
  3. Calls the {@link #findResource(String)} method to find + * the resources using this class loader. + *
+ * + * @param name a "/"-separated path name that identifies the resource. + * @return a URL for reading the resource, or null if + * the resource could not be found. + * @see #findResource(String) + */ + public final URL getResource(final String name) { + zombieCheck(name); + + final int slashIdx = name.lastIndexOf('/'); + if (slashIdx == -1) return null; // won't load from the default package + final String pkg = name.substring(0, slashIdx + 1); + + if (isSpecialResource(pkg)) { + // Disable domain cache for this one, do a simple check. + for (int i = 0; i < parents.length; i++) { + if (!shouldDelegateResource(pkg, parents[i])) continue; + URL u; + if (parents[i] instanceof ProxyClassLoader) { + u = ((ProxyClassLoader)parents[i]).findResource(name); + } else { + u = parents[i].getResource(name); + } + if (u != null) return u; + } + return findResource(name); + } + + ClassLoader owner = (ClassLoader)domainsByPackage.get(pkg); + + if (owner != null) { // known package + // Note that shouldDelegateResource should already be true for this! + if (owner instanceof ProxyClassLoader) { + return ((ProxyClassLoader)owner).findResource(name); // we have its parents, skip them + } else { + return owner.getResource(name); // know nothing about this loader and his structure + } + } + + // virgin package + URL retVal = null; + for (int i = 0; i < parents.length; i++) { + owner = parents[i]; + if (!shouldDelegateResource(pkg, owner)) continue; + if (owner instanceof ProxyClassLoader) { + retVal = ((ProxyClassLoader)owner).findResource(name); // skip parents (checked already) + } else { + retVal = owner.getResource(name); // know nothing about this loader and his structure + } + if (retVal != null) { + domainsByPackage.put(pkg, owner); + return retVal; + } + } + + // try it ourself + retVal = findResource(name); + if (retVal != null) { + domainsByPackage.put(pkg, this); + } + return retVal; + } + + /** This ClassLoader can't load anything itself. Subclasses + * may override this method to do some resource loading themselves. + * + * @param name the resource name + * @return a URL for reading the resource, or null + * if the resource could not be found. + */ + protected URL findResource(String name) { + return null; + } + + /** + * Finds all the resource with the given name. The implementation of + * this method uses the {@link #simpleFindResources(String)} method to find + * all the resources available from this classloader and adds all the + * resources from all the parents. + * + * @param name the resource name + * @return an Enumeration of URLs for the resources + * @throws IOException if I/O errors occur + */ + protected final synchronized Enumeration findResources(String name) throws IOException { + zombieCheck(name); + final int slashIdx = name.lastIndexOf('/'); + if (slashIdx == -1) return EMPTY; // won't load from the default package + final String pkg = name.substring(0, slashIdx + 1); + + // Don't bother optimizing this call by domains. + // It is mostly used for resources for which isSpecialResource would be true anyway. + Enumeration[] es = new Enumeration[parents.length + 1]; + for (int i = 0; i < parents.length; i++) { + if (!shouldDelegateResource(pkg, parents[i])) { + es[i] = EMPTY; + continue; + } + if (parents[i] instanceof ProxyClassLoader) { + es[i] = ((ProxyClassLoader)parents[i]).simpleFindResources(name); + } else { + es[i] = parents[i].getResources(name); + } + } + es[parents.length] = simpleFindResources(name); + // Should not be duplicates, assuming the parent loaders are properly distinct + // from one another and do not overlap in JAR usage, which they ought not. + // Anyway MetaInfServicesLookup, the most important client of this method, does + // its own duplicate filtering already. + return new AAEnum (es); + } + + /** This ClassLoader can't load anything itself. Subclasses + * may override this method to do some resource loading themselves, this + * implementation simply delegates to findResources method of the superclass + * that should return empty Enumeration. + * + * @param name the resource name + * @return an Enumeration of URLs for the resources + * @throws IOException if I/O errors occur + */ + protected Enumeration simpleFindResources(String name) throws IOException { + return super.findResources(name); + } + + + /** + * Returns a Package that has been defined by this class loader or any + * of its parents. + * + * @param name the package name + * @return the Package corresponding to the given name, or null if not found + */ + protected Package getPackage(String name) { + zombieCheck(name); + + int idx = name.lastIndexOf('.'); + if (idx == -1) return null; + String spkg = name.substring(0, idx + 1).replace('.', '/'); + + synchronized (packages) { + Package pkg = (Package)packages.get(name); + if (pkg != null) return pkg; + + for (int i = 0; i < parents.length; i++) { + ClassLoader par = parents[i]; + if (par instanceof ProxyClassLoader && shouldDelegateResource(spkg, par)) { + pkg = ((ProxyClassLoader)par).getPackage(name); + if(pkg != null) break; + } + } + // do our own lookup + if (pkg == null) pkg = super.getPackage(name); + // cache results + if (pkg != null) packages.put(name, pkg); + + return pkg; + } + } + + /** This is here just for locking serialization purposes. + * Delegates to super.definePackage with proper locking. + */ + protected Package definePackage(String name, String specTitle, + String specVersion, String specVendor, String implTitle, + String implVersion, String implVendor, URL sealBase ) + throws IllegalArgumentException { + synchronized (packages) { + return super.definePackage (name, specTitle, specVersion, specVendor, implTitle, + implVersion, implVendor, sealBase); + } + } + + /** + * Returns all of the Packages defined by this class loader and its parents. + * + * @return the array of Package objects defined by this + * ClassLoader + */ + protected synchronized Package[] getPackages() { + zombieCheck(null); + Map all = new HashMap(); // Map + addPackages(all, super.getPackages()); + for (int i = 0; i < parents.length; i++) { + ClassLoader par = parents[i]; + if (par instanceof ProxyClassLoader) { + // XXX should ideally use shouldDelegateResource here... + addPackages(all, ((ProxyClassLoader)par).getPackages()); + } + } + synchronized (packages) { + Iterator it = all.entrySet().iterator(); + while (it.hasNext()) { + Map.Entry entry = (Map.Entry)it.next(); + Object name = entry.getKey(); + if (! packages.containsKey(name)) { + packages.put(name, entry.getValue()); + } + } + } + return (Package[])all.values().toArray(new Package[all.size()]); + } + + public Package getPackageAccessibly(String name) { + return getPackage(name); + } + + public Package[] getPackagesAccessibly() { + return getPackages(); + } + + + + /** Coalesce parent classloaders into an optimized set. + * This means that all parents of the specified classloaders + * are also added recursively, removing duplicates along the way. + * Search order should be preserved (parents before children, stable w.r.t. inputs). + * @param loaders list of suggested parents (no nulls or duplicates permitted) + * @return optimized list of parents (no nulls or duplicates) + * @throws IllegalArgumentException if there are cycles + */ + private ClassLoader[] coalesceParents(ClassLoader[] loaders) throws IllegalArgumentException { + int likelySize = loaders.length * 3 + 1; + Set resultingUnique = new HashSet(likelySize); // Set + List resulting = new ArrayList(likelySize); // List + for (int i = 0; i < loaders.length; i++) { + addRec(resultingUnique, resulting, loaders[i]); + } + ClassLoader[] ret = (ClassLoader[])resulting.toArray(new ClassLoader[resulting.size()]); + return ret; + } + + /** Coalesce a new set of loaders into the existing ones. + */ + private ClassLoader[] coalesceAppend(ClassLoader[] existing, ClassLoader[] appended) throws IllegalArgumentException { + int likelySize = existing.length + 3; + Set resultingUnique = new HashSet(likelySize); + List existingL = Arrays.asList(existing); + resultingUnique.addAll(existingL); + if (resultingUnique.containsAll(Arrays.asList(appended))) { + // No change required. + return existing; + } + List resulting = new ArrayList(likelySize); + resulting.addAll(existingL); + int fromIdx = resulting.size(); + for (int i = 0; i < appended.length; i++) { + addRec(resultingUnique, resulting, appended[i]); + } + ClassLoader[] ret = (ClassLoader[])resulting.toArray(new ClassLoader[resulting.size()]); + return ret; + } + + private void addRec(Set resultingUnique, List resulting, ClassLoader loader) throws IllegalArgumentException { + if (loader == this) throw new IllegalArgumentException("cycle in parents"); // NOI18N + if (resultingUnique.contains(loader)) return; + if (loader instanceof ProxyClassLoader) { + ClassLoader[] parents = ((ProxyClassLoader)loader).parents; + for (int i = 0; i < parents.length; i++) { + addRec(resultingUnique, resulting, parents[i]); + } + } + resultingUnique.add(loader); + resulting.add(loader); + } + + /** A method that finds a class either in itself or in parents. + * It uses dual signaling for class not found: it can either return null + * or throw CNFE itself. + * @param name class name, e.g. "org.netbeans.modules.foo.Clazz" + * @param fileName resource name, e.g. "org/netbeans/modules/foo/Clazz.class" + * @param pkg package component, e.g. "org/netbeans/modules/foo/" + * @return a class or null if not found. It can also throw an exception. + * @throws ClassNotFoundException in case it doesn't found a class + * and a parent eglible for loading it thrown it already. + */ + private final Class smartLoadClass(String name, String fileName, String pkg) throws ClassNotFoundException { + // First, check if the class has already been loaded + Class c = findLoadedClass(name); + if(c != null) return c; + + final ClassLoader owner = isSpecialResource(pkg) ? null : (ClassLoader)domainsByPackage.get(pkg); + if (owner == this) { + return simpleFindClass(name,fileName); + } + if (owner != null) { + // Note that shouldDelegateResource should already be true as we hit this pkg before. + if (owner instanceof ProxyClassLoader) { + return ((ProxyClassLoader)owner).fullFindClass(name,fileName); + } else { + return owner.loadClass(name); // May throw CNFE, will be propagated + } + } + + // Virgin package, do the parent scan + c = loadInOrder(name, fileName, pkg); + + if (c != null) { + final ClassLoader owner2 = c.getClassLoader(); // who got it? + domainsByPackage.put(pkg, owner2); + } + return c; + } + + + private final Class loadInOrder( String name, String fileName, String pkg ) throws ClassNotFoundException { + ClassNotFoundException cached = null; + for (int i = 0; i < parents.length; i++) { + ClassLoader par = parents[i]; + if (!shouldDelegateResource(pkg, par)) continue; + if (par instanceof ProxyClassLoader) { + Class c = ((ProxyClassLoader)par).fullFindClass(name,fileName); + if (c != null) return c; + } else { + try { + return par.loadClass(name); + } catch( ClassNotFoundException cnfe ) { + cached = cnfe; + } + } + } + + Class c = simpleFindClass(name,fileName); // Try it ourselves + if (c != null) return c; + if (cached != null) throw cached; + return null; + } + + private synchronized Class fullFindClass(String name, String fileName) { + Class c = findLoadedClass(name); + return (c == null) ? simpleFindClass(name, fileName) : c; + } + + private void addPackages(Map all, Package[] pkgs) { + // Would be easier if Package.equals() was just defined sensibly... + for (int i = 0; i < pkgs.length; i++) { + all.put(pkgs[i].getName(), pkgs[i]); + } + } + + /** Test whether a given resource name is something that any JAR might + * have, and for which the domain cache should be disabled. + * The result must not change from one call to the next with the same argument. + * By default the domain cache is disabled only for META-INF/* JAR information. + * @param pkg the package component of the resource path ending with a slash, + * e.g. "org/netbeans/modules/foo/" + * @return true if it is a special resource, false for normal domain-cached resource + * @since org.netbeans.core/1 1.3 + */ + protected boolean isSpecialResource(String pkg) { + if (pkg.startsWith("META-INF/")) return true; // NOI18N + return false; + } + + /** Test whether a given resource request (for a class or not) should be + * searched for in the specified parent classloader or not. + * The result must not change from one call to the next with the same arguments. + * By default, always true. Subclasses may override to "mask" certain + * packages from view, possibly according to the classloader chain. + * @param pkg the package component of the resource path ending with a slash, + * e.g. "org/netbeans/modules/foo/" + * @param parent a classloader which is a direct or indirect parent of this one + * @return true if the request should be delegated to this parent; false to + * only search elsewhere (other parents, this loader's own namespace) + * @since org.netbeans.core/1 1.3 + */ + protected boolean shouldDelegateResource(String pkg, ClassLoader parent) { + return true; + } + + + private static final class ArrayEnumeration implements Enumeration { + /** The array */ + private Object[] array; + /** Current index in the array */ + private int index = 0; + + /** Constructs a new ArrayEnumeration for specified array */ + public ArrayEnumeration (Object[] array) { + this.array = array; + } + + /** Tests if this enumeration contains more elements. + * @return true if this enumeration contains more elements; + * false otherwise. + */ + public boolean hasMoreElements() { + return (index < array.length); + } + + /** Returns the next element of this enumeration. + * @return the next element of this enumeration. + * @exception NoSuchElementException if no more elements exist. + */ + public Object nextElement() { + try { + return array[index++]; + } catch (ArrayIndexOutOfBoundsException e) { + throw new NoSuchElementException(); + } + } + } + + private static final class AAEnum implements Enumeration { + /** The array */ + private Enumeration[] array; + /** Current index in the array */ + private int index = 0; + + /** Constructs a new ArrayEnumeration for specified array */ + public AAEnum (Enumeration[] array) { + this.array = array; + } + + /** Tests if this enumeration contains more elements. + * @return true if this enumeration contains more elements; + * false otherwise. + */ + public boolean hasMoreElements() { + for (;;) { + if (index == array.length) { + return false; + } + + if (array[index].hasMoreElements ()) { + return true; + } + + index++; + } + } + + /** Returns the next element of this enumeration. + * @return the next element of this enumeration. + * @exception NoSuchElementException if no more elements exist. + */ + public Object nextElement() { + try { + return array[index].nextElement (); + } catch (NoSuchElementException ex) { + if (hasMoreElements ()) { + // try once more + return nextElement (); + } + throw ex; + } catch (ArrayIndexOutOfBoundsException e) { + throw new NoSuchElementException(); + } + } + } + +} Index: nb_all/core/src/org/netbeans/core/Main.java =================================================================== RCS file: /cvs/core/src/org/netbeans/core/Main.java,v --- nb_all/core/src/org/netbeans/core/Main.java 29 Jul 2002 10:11:14 -0000 1.147 +++ nb_all/core/src/org/netbeans/core/Main.java 29 Jul 2002 19:26:35 -0000 @@ -121,7 +121,7 @@ /** Starts TopThreadGroup which properly overrides uncaughtException * Further - new thread in the group execs main */ - public static void start(String[] argv) { + public static void main (String[] argv) { TopThreadGroup tg = new TopThreadGroup ("IDE Main", argv); // NOI18N - programatic name tg.start (); } @@ -356,7 +356,7 @@ /** * @exception SecurityException if it is called multiple times */ - public static void main(String[] args) throws SecurityException { + static void start (String[] args) throws SecurityException { long time = System.currentTimeMillis(); StartLog.logEnd ("Forwarding to topThreadGroup"); // NOI18N Index: nb_all/core/src/org/netbeans/core/TopThreadGroup.java =================================================================== RCS file: /cvs/core/src/org/netbeans/core/TopThreadGroup.java,v --- nb_all/core/src/org/netbeans/core/TopThreadGroup.java 29 Jul 2002 10:11:14 -0000 1.16 +++ nb_all/core/src/org/netbeans/core/TopThreadGroup.java 29 Jul 2002 19:26:35 -0000 @@ -78,7 +78,7 @@ public void run() { try { - Main.main(args); + Main.start (args); } catch (Throwable t) { // XXX is this not handled by uncaughtException? ErrorManager.getDefault().notify(t); Index: nb_all/core/src/org/netbeans/core/modules/JarClassLoader.java =================================================================== RCS file: nb_all/core/src/org/netbeans/core/modules/JarClassLoader.java --- nb_all/core/src/org/netbeans/core/modules/JarClassLoader.java 29 Jul 2002 10:11:15 -0000 1.14 +++ /dev/null 1 Jan 1970 00:00:00 -0000 @@ -1,401 +0,0 @@ -/* - * 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-2001 Sun - * Microsystems, Inc. All Rights Reserved. - */ - -package org.netbeans.core.modules; - -import java.net.URL; -import java.util.jar.JarFile; -import java.util.jar.Manifest; -import java.util.jar.Attributes; -import java.util.jar.Attributes.Name; -import java.util.zip.ZipEntry; -import java.io.*; -import java.net.MalformedURLException; -import java.security.*; -import java.security.cert.Certificate; -import java.util.*; -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; - -import org.openide.ErrorManager; - -/** - * A ProxyClassLoader capable of loading classes from a set of jar files - * and local directories. - * - * @author Petr Nejedly - */ -public class JarClassLoader extends ProxyClassLoader { - private Source[] sources; - /** temp copy JARs which ought to be deleted */ - private Set deadJars = null; // Set - - /** Creates new JarClassLoader */ - public JarClassLoader (List files, ClassLoader[] parents) { - super(parents); - - sources = new Source[files.size()]; - try { - int i=0; - for (Iterator it = files.iterator(); it.hasNext(); i++ ) { - Object act = it.next(); - if (act instanceof File) { - sources[i] = new DirSource((File)act); - } else { - sources[i] = new JarSource((JarFile)act); - } - } - } catch (MalformedURLException exc) { - throw new IllegalArgumentException(exc.getMessage()); - } - - } - - protected PermissionCollection getPermissions( CodeSource cs ) { - return Policy.getPolicy().getPermissions(cs); - } - - protected Package definePackage(String name, Manifest man, URL url) - throws IllegalArgumentException - { - if (man == null ) { - return definePackage(name, null, null, null, null, null, null, null); - } - - String path = name.replace('.', '/').concat("/"); // NOI18N - Attributes spec = man.getAttributes(path); - Attributes main = man.getMainAttributes(); - - String specTitle = getAttr(spec, main, Name.SPECIFICATION_TITLE); - String implTitle = getAttr(spec, main, Name.IMPLEMENTATION_TITLE); - String specVersion = getAttr(spec, main, Name.SPECIFICATION_VERSION); - String implVersion = getAttr(spec, main, Name.IMPLEMENTATION_VERSION); - String specVendor = getAttr(spec, main, Name.SPECIFICATION_VENDOR); - String implVendor = getAttr(spec, main, Name.IMPLEMENTATION_VENDOR); - String sealed = getAttr(spec, main, Name.SEALED); - - URL sealBase = "true".equalsIgnoreCase(sealed) ? url : null; // NOI18N - return definePackage(name, specTitle, specVersion, specVendor, - implTitle, implVersion, implVendor, sealBase); - } - - private static String getAttr(Attributes spec, Attributes main, Name name) { - String val = null; - if (spec != null) val = spec.getValue (name); - if (val == null && main != null) val = main.getValue (name); - return val; - } - - protected Class simpleFindClass(String name, String path) { - // look up the Sources and return a class based on their content - for( int i=0; i - try { - for (int i = 0; i < sources.length; i++) { - if (sources[i] instanceof JarSource) { - JarFile origJar = ((JarSource)sources[i]).getJarFile(); - File orig = new File(origJar.getName()); - if (!orig.isFile()) { - // Can happen when a test module is deleted: - // the physical JAR has already been deleted - // when the module was disabled. In this case it - // is possible that a classloader request for something - // in the JAR could still come in. Does it matter? - // See comment in Module.cleanup. - continue; - } - String name = orig.getName(); - String prefix, suffix; - int idx = name.lastIndexOf('.'); - if (idx == -1) { - prefix = name; - suffix = null; - } else { - prefix = name.substring(0, idx); - suffix = name.substring(idx); - } - while (prefix.length() < 3) prefix += "x"; // NOI18N - File temp = File.createTempFile(prefix, suffix); - temp.deleteOnExit(); - InputStream is = new FileInputStream(orig); - try { - OutputStream os = new FileOutputStream(temp); - try { - byte[] buf = new byte[4096]; - int j; - while ((j = is.read(buf)) != -1) { - os.write(buf, 0, j); - } - } finally { - os.close(); - } - } finally { - is.close(); - } - // Don't use OPEN_DELETE even though it sounds like a good idea. - // Can cause real problems under 1.4; see Module.java. - JarFile tempJar = new JarFile(temp); - origJar.close(); - forceRelease(orig); - deadJars.add(tempJar); - sources[i] = new JarSource(tempJar); - Util.err.log("#21114: replacing " + orig + " with " + temp); - } - } - } catch (IOException ioe) { - Util.err.notify(ErrorManager.INFORMATIONAL, ioe); - } - } - - /** Release jar: locks when the classloader is shut down. - * Should help reloading modules with changed resources. - */ - public void destroy() { - super.destroy(); - for (int i = 0; i < sources.length; i++) { - if (sources[i] instanceof JarSource) { - JarFile j = ((JarSource)sources[i]).getJarFile(); - File f = new File(j.getName()); - forceRelease(f); - } - } - } - - /** Delete any temporary JARs we were holding on to. - * Also close any other JARs in our list. - */ - protected void finalize() throws Throwable { - super.finalize(); - for (int i = 0; i < sources.length; i++) { - if (sources[i] instanceof JarSource) { - JarFile j = ((JarSource)sources[i]).getJarFile(); - File f = new File(j.getName()); - j.close(); - forceRelease(f); - if (deadJars != null && deadJars.contains(j)) { - Util.err.log("#21114: closing and deleting temporary JAR " + f); - if (f.isFile() && !f.delete()) { - Util.err.log("(but failed to delete it)"); - } - } - } - } - } - - /** Make sure the Java runtime's jar: URL cache is not holding - * onto the specified file. - * Workaround for JDK bug #4646668. - */ - private static void forceRelease(File f) { - if (fileCache == null || factory == null) return; - try { - synchronized (factory) { - Iterator it = fileCache.values().iterator(); - while (it.hasNext()) { - JarFile j = (JarFile)it.next(); - if (f.equals(new File(j.getName()))) { - j.close(); - it.remove(); - Util.err.log("Removing jar: cache for " + f + " as workaround for JDK #4646668"); - } - } - } - } catch (Exception e) { - Util.err.annotate(e, ErrorManager.UNKNOWN, "Could not remove jar: cache for " + f, null, null, null); - Util.err.notify(ErrorManager.INFORMATIONAL, e); - } - } - private static Object factory = null; - private static HashMap fileCache = null; - static { - try { - Class juc = Class.forName("sun.net.www.protocol.jar.JarURLConnection"); // NOI18N - Field factoryF = juc.getDeclaredField("factory"); // NOI18N - factoryF.setAccessible(true); - factory = factoryF.get(null); - Class jff = Class.forName("sun.net.www.protocol.jar.JarFileFactory"); // NOI18N - if (!jff.isInstance(factory)) throw new ClassCastException(factory.getClass().getName()); - Field fileCacheF = jff.getDeclaredField("fileCache"); // NOI18N - fileCacheF.setAccessible(true); - if (Modifier.isStatic(fileCacheF.getModifiers())) { - // JDK 1.3.1 or 1.4 seems to have it static. - fileCache = (HashMap)fileCacheF.get(null); - } else { - // But in 1.3.0 it appears to be an instance var. - fileCache = (HashMap)fileCacheF.get(factory); - } - Util.err.log("Workaround for JDK #4646668 active as part of IZ #21114"); - } catch (Exception e) { - Util.err.annotate(e, ErrorManager.UNKNOWN, "Workaround for JDK #4646668 as part of IZ #21114 failed", null, null, null); - Util.err.notify(ErrorManager.INFORMATIONAL, e); - } - } - - abstract class Source { - private URL url; - private ProtectionDomain pd; - - public Source(URL url) { - this.url = url; - CodeSource cs = new CodeSource(url, new Certificate[0]); - pd = new ProtectionDomain(cs, getPermissions(cs)); - } - - public final URL getURL() { - return url; - } - - public final ProtectionDomain getProtectionDomain() { - return pd; - } - - public final URL getResource(String name) { - try { - return doGetResource(name); - } catch (MalformedURLException e) { - Util.err.log(e.toString()); - } - return null; - } - - protected abstract URL doGetResource(String name) throws MalformedURLException; - - public final byte[] getClassData(String name, String path) { - try { - return readClass(name, path); - } catch (IOException e) { - Util.err.log(e.toString()); - } - return null; - } - - protected abstract byte[] readClass(String name, String path) throws IOException; - - public Manifest getManifest() { - return null; - } - } - - class JarSource extends Source { - JarFile src; - - public JarSource(JarFile file) throws MalformedURLException { - super(new URL("file:" + file.getName())); - src = file; - } - - public Manifest getManifest() { - try { - return src.getManifest(); - } catch (IOException e) { - return null; - } - } - - JarFile getJarFile() { - return src; - } - - protected URL doGetResource(String name) throws MalformedURLException { - ZipEntry ze = src.getEntry(name); - return ze == null ? null : new URL("jar:file:" + src.getName() + "!/" + ze.getName()); // NOI18N - } - - protected byte[] readClass(String name, String path) throws IOException { - ZipEntry ze = src.getEntry(path); - if (ze == null) return null; - - int len = (int)ze.getSize(); - byte[] data = new byte[len]; - InputStream is = src.getInputStream(ze); - int count = 0; - while (count < len) { - count += is.read(data, count, len-count); - } - return data; - } - } - - class DirSource extends Source { - File dir; - - public DirSource(File file) throws MalformedURLException { - super(file.toURL()); - dir = file; - } - - protected URL doGetResource(String name) throws MalformedURLException { - File resFile = new File(dir, name); - return resFile.exists() ? resFile.toURL() : null; - } - - protected byte[] readClass(String name, String path) throws IOException { - File clsFile = new File(dir, path.replace('/', File.separatorChar)); - - int len = (int)clsFile.length(); - byte[] data = new byte[len]; - InputStream is = new FileInputStream(clsFile); - int count = 0; - while (count < len) { - count += is.read(data, count, len-count); - } - return data; - } - - } -} Index: nb_all/core/src/org/netbeans/core/modules/Module.java =================================================================== RCS file: /cvs/core/src/org/netbeans/core/modules/Module.java,v --- nb_all/core/src/org/netbeans/core/modules/Module.java 29 Jul 2002 10:11:15 -0000 1.30 +++ nb_all/core/src/org/netbeans/core/modules/Module.java 29 Jul 2002 19:26:35 -0000 @@ -30,6 +30,8 @@ import org.openide.modules.SpecificationVersion; import org.openide.modules.Dependency; import org.openide.util.WeakSet; +import org.netbeans.JarClassLoader; +import org.netbeans.ProxyClassLoader; /** Object representing one module, possibly installed. * Responsible for opening of module JAR file; reading @@ -797,15 +799,31 @@ // #9273: load any modules/patches/this-code-name/*.jar files first: File patchdir = new File(new File(jar.getParentFile(), "patches"), // NOI18N getCodeNameBase().replace('.', '-')); // NOI18N - if (patchdir.isDirectory()) { - File[] jars = patchdir.listFiles(Util.jarFilter()); - if (jars != null) { - for (int j = 0; j < jars.length; j++) { - ev.log(Events.PATCH, jars[j]); - classp.add(new JarFile(jars[j])); + + scanFilesInADir (patchdir, classp); + + String patchesClassPath = System.getProperty ("netbeans.patches." + getCodeNameBase ()); // NOI18N + if (patchesClassPath != null) { + //scanFilesInADir (new File (dir), classp); + StringTokenizer tokenizer = new StringTokenizer(patchesClassPath, File.pathSeparator); + while (tokenizer.hasMoreElements()) { + String element = (String) tokenizer.nextElement(); + File fileElement = new File(element); + if (fileElement.isDirectory()) { + // adding directory + Util.err.log("module patches: adding directory "+fileElement+" for "+getCodeNameBase()); + File[] jars = fileElement.listFiles(Util.jarFilter()); + if (jars != null) { + for (int j=0; j < jars.length; j++) { + // add jar files + System.out.println("adding jar file:"+jars[j]); + classp.add( new JarFile(jars[j])); + } + } + // add also dir + System.out.println("adding directory:"+fileElement); + classp.add((Object) fileElement); } - } else { - Util.err.log(ErrorManager.WARNING, "Could not search for patches in " + patchdir); } } if (reloadable) { @@ -829,6 +847,11 @@ File act = (File)it.next(); classp.add(act.isDirectory() ? (Object)act : new JarFile(act)); } + + if (loaders.isEmpty()) { + loaders.add (Module.class.getClassLoader()); + } + try { classloader = new OneModuleClassLoader(classp, (ClassLoader[])loaders.toArray(new ClassLoader[loaders.size()])); } catch (IllegalArgumentException iae) { @@ -839,6 +862,23 @@ } oldClassLoaders.add(classloader); } + + /** Scans content of a directory */ + private void scanFilesInADir (File patchdir, List classp) throws IOException { + if (!patchdir.isDirectory()) { + return; + } + File[] jars = patchdir.listFiles(Util.jarFilter()); + if (jars != null) { + for (int j = 0; j < jars.length; j++) { + ev.log(Events.PATCH, jars[j]); + classp.add(new JarFile(jars[j])); + } + } else { + Util.err.log(ErrorManager.WARNING, "Could not search for patches in " + patchdir); + } + } + /** Turn off the classloader and release all resources. */ void classLoaderDown() { if (isFixed()) return; // don't touch it Index: nb_all/core/src/org/netbeans/core/modules/ModuleManager.java =================================================================== RCS file: /cvs/core/src/org/netbeans/core/modules/ModuleManager.java,v --- nb_all/core/src/org/netbeans/core/modules/ModuleManager.java 29 Jul 2002 10:11:15 -0000 1.33 +++ nb_all/core/src/org/netbeans/core/modules/ModuleManager.java 29 Jul 2002 19:26:35 -0000 @@ -31,6 +31,7 @@ import org.openide.modules.SpecificationVersion; import org.openide.modules.Dependency; import org.openide.ErrorManager; +import org.netbeans.ProxyClassLoader; /** Manages a collection of modules. * Must use {@link #mutex} to access its important methods. @@ -56,7 +57,7 @@ private final ModuleInstaller installer; - private SystemClassLoader classLoader = new SystemClassLoader(new ClassLoader[0]); + private SystemClassLoader classLoader = new SystemClassLoader(new ClassLoader[] {ModuleManager.class.getClassLoader()}); private final Object classLoaderLock = "ModuleManager.classLoaderLock"; // NOI18N private final Events ev; @@ -233,13 +234,16 @@ } parents.add(m.getClassLoader()); } + if (parents.isEmpty()) { + parents.add (ModuleManager.class.getClassLoader()); + } ClassLoader[] parentCLs = (ClassLoader[])parents.toArray(new ClassLoader[parents.size()]); SystemClassLoader nue; try { nue = new SystemClassLoader(parentCLs); } catch (IllegalArgumentException iae) { Util.err.notify(iae); - nue = new SystemClassLoader(new ClassLoader[0]); + nue = new SystemClassLoader(new ClassLoader[] {ModuleManager.class.getClassLoader()}); } synchronized (classLoaderLock) { classLoader = nue; Index: nb_all/core/src/org/netbeans/core/modules/ProxyClassLoader.java =================================================================== RCS file: nb_all/core/src/org/netbeans/core/modules/ProxyClassLoader.java --- nb_all/core/src/org/netbeans/core/modules/ProxyClassLoader.java 29 Jul 2002 10:11:16 -0000 1.14 +++ /dev/null 1 Jan 1970 00:00:00 -0000 @@ -1,527 +0,0 @@ -/* - * 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-2002 Sun - * Microsystems, Inc. All Rights Reserved. - */ - -package org.netbeans.core.modules; - -import java.util.*; -import java.net.URL; -import java.io.IOException; -import org.openide.ErrorManager; -import org.openide.util.WeakSet; -import org.openide.util.enum.*; - -/** - * A class loader that has multiple parents and uses them for loading - * classes and resources. It can be used in tree hierarchy, where it - * can exploit its capability to not throw ClassNotFoundException when - * communicating with other ProxyClassLoader. - * It itself doesn't load classes or resources, but allows subclasses - * to add such functionality. - * - * @author Petr Nejedly, Jesse Glick - */ -public class ProxyClassLoader extends ClassLoader { - - // Map - // packages are given in format "org/netbeans/modules/foo/" - private final Map domainsByPackage = new HashMap(); - // Map - private HashMap packages = new HashMap(); - - // All parentf of this classloader, including their parents recursively - private ClassLoader[] parents; - - /** if true, we have been destroyed */ - private boolean dead = false; - - /** Create a multi-parented classloader. - * @param parents list of parent classloaders. - * @throws IllegalArgumentException if there are any nulls or duplicate - * parent loaders or cycles. - */ - public ProxyClassLoader( ClassLoader[] parents ) { - if (parents.length == 0) { - parents = new ClassLoader[] { ProxyClassLoader.class.getClassLoader() }; - } - - Set check = new HashSet(Arrays.asList(parents)); // Set - if (check.size() < parents.length) throw new IllegalArgumentException("duplicate parents"); // NOI18N - if (check.contains(null)) throw new IllegalArgumentException("null parent"); // NOI18N - - this.parents = coalesceParents(parents); - } - - // this is used only by system classloader, maybe we can redesign it a bit - // to live without this functionality, then destroy may also go away - /** Add new parents dynamically. - * @param parents the new parents to add (append to list) - * @throws IllegalArgumentException in case of a null or cyclic parent (duplicate OK) - */ - public synchronized void append(ClassLoader[] nueparents) throws IllegalArgumentException { - // XXX should this be synchronized? - if (nueparents == null) throw new IllegalArgumentException("null parents array"); // NOI18N - for (int i = 0; i < nueparents.length; i++) { - if (nueparents[i] == null) throw new IllegalArgumentException("null parent"); // NOI18N - } - - parents = coalesceAppend(parents, nueparents); - } - - - - /** Try to destroy this classloader. - * Subsequent attempts to use it will log an error (at most one though). - */ - public void destroy() { - dead = true; - } - - private void zombieCheck(String hint) { - if (dead) { - IllegalStateException ise = new IllegalStateException("WARNING - attempting to use a zombie classloader " + this + " on " + hint + ". This means classes from a disabled module are still active. May or may not be a problem."); // NOI18N - ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, ise); - // don't warn again for same loader... this was enough - dead = false; - } - } - - /** - * Loads the class with the specified name. The implementation of - * this method searches for classes in the following order:

- *

    - *
  1. Calls {@link #findLoadedClass(String)} to check if the class has - * already been loaded. - *
  2. Checks the caches whether another class from the same package - * was already loaded and uses the same classloader - *
  3. Tries to find the class using parent loaders in their order. - *
  4. Calls the {@link #simpleFindClass(String,String)} method to find - * the class using this class loader. - *
- * - * @param name the name of the class - * @param resolve if true then resolve the class - * @return the resulting Class object - * @exception ClassNotFoundException if the class could not be found - */ - protected synchronized final Class loadClass(String name, boolean resolve) - throws ClassNotFoundException { - zombieCheck(name); - // XXX this section is a candidate for local optimization: - String filename = name.replace('.', '/').concat(".class"); // NOI18N - int idx = filename.lastIndexOf('/'); // NOI18N - if (idx == -1) throw new ClassNotFoundException("Will not load classes from default package"); // NOI18N - String pkg = filename.substring(0, idx + 1); // "org/netbeans/modules/foo/" - Class c = smartLoadClass(name, filename, pkg); - if(c == null) throw new ClassNotFoundException(name); - if (resolve) resolveClass(c); - return c; - } - - /** This ClassLoader can't load anything itself. Subclasses - * may override this method to do some class loading themselves. The - * implementation should not throw any exception, just return - * null if it can't load required class. - * - * @param name the name of the class - * @param fileName the expected filename of the classfile, like - * java/lang/Object.class for java.lang.Object - * The ClassLoader implementation may or may not use it, depending - * whether it is usefull to it. - * @return the resulting Class object or null - */ - protected Class simpleFindClass(String name, String fileName) { - return null; - } - - - /** - * Finds the resource with the given name. The implementation of - * this method searches for resources in the following order:

- *

    - *
  1. Checks the caches whether another resource or class from the same - * package was already loaded and uses the same classloader. - *
  2. Tries to find the resources using parent loaders in their order. - *
  3. Calls the {@link #findResource(String)} method to find - * the resources using this class loader. - *
- * - * @param name a "/"-separated path name that identifies the resource. - * @return a URL for reading the resource, or null if - * the resource could not be found. - * @see #findResource(String) - */ - public final URL getResource(final String name) { - zombieCheck(name); - - final int slashIdx = name.lastIndexOf('/'); - if (slashIdx == -1) return null; // won't load from the default package - final String pkg = name.substring(0, slashIdx + 1); - - if (isSpecialResource(pkg)) { - // Disable domain cache for this one, do a simple check. - for (int i = 0; i < parents.length; i++) { - if (!shouldDelegateResource(pkg, parents[i])) continue; - URL u; - if (parents[i] instanceof ProxyClassLoader) { - u = ((ProxyClassLoader)parents[i]).findResource(name); - } else { - u = parents[i].getResource(name); - } - if (u != null) return u; - } - return findResource(name); - } - - ClassLoader owner = (ClassLoader)domainsByPackage.get(pkg); - - if (owner != null) { // known package - // Note that shouldDelegateResource should already be true for this! - if (owner instanceof ProxyClassLoader) { - return ((ProxyClassLoader)owner).findResource(name); // we have its parents, skip them - } else { - return owner.getResource(name); // know nothing about this loader and his structure - } - } - - // virgin package - URL retVal = null; - for (int i = 0; i < parents.length; i++) { - owner = parents[i]; - if (!shouldDelegateResource(pkg, owner)) continue; - if (owner instanceof ProxyClassLoader) { - retVal = ((ProxyClassLoader)owner).findResource(name); // skip parents (checked already) - } else { - retVal = owner.getResource(name); // know nothing about this loader and his structure - } - if (retVal != null) { - domainsByPackage.put(pkg, owner); - return retVal; - } - } - - // try it ourself - retVal = findResource(name); - if (retVal != null) { - domainsByPackage.put(pkg, this); - } - return retVal; - } - - /** This ClassLoader can't load anything itself. Subclasses - * may override this method to do some resource loading themselves. - * - * @param name the resource name - * @return a URL for reading the resource, or null - * if the resource could not be found. - */ - protected URL findResource(String name) { - return null; - } - - /** - * Finds all the resource with the given name. The implementation of - * this method uses the {@link #simpleFindResources(String)} method to find - * all the resources available from this classloader and adds all the - * resources from all the parents. - * - * @param name the resource name - * @return an Enumeration of URLs for the resources - * @throws IOException if I/O errors occur - */ - protected final synchronized Enumeration findResources(String name) throws IOException { - zombieCheck(name); - final int slashIdx = name.lastIndexOf('/'); - if (slashIdx == -1) return EmptyEnumeration.EMPTY; // won't load from the default package - final String pkg = name.substring(0, slashIdx + 1); - - // Don't bother optimizing this call by domains. - // It is mostly used for resources for which isSpecialResource would be true anyway. - Enumeration[] es = new Enumeration[parents.length + 1]; - for (int i = 0; i < parents.length; i++) { - if (!shouldDelegateResource(pkg, parents[i])) { - es[i] = EmptyEnumeration.EMPTY; - continue; - } - if (parents[i] instanceof ProxyClassLoader) { - es[i] = ((ProxyClassLoader)parents[i]).simpleFindResources(name); - } else { - es[i] = parents[i].getResources(name); - } - } - es[parents.length] = simpleFindResources(name); - // Should not be duplicates, assuming the parent loaders are properly distinct - // from one another and do not overlap in JAR usage, which they ought not. - // Anyway MetaInfServicesLookup, the most important client of this method, does - // its own duplicate filtering already. - return new SequenceEnumeration(new ArrayEnumeration(es)); - } - - /** This ClassLoader can't load anything itself. Subclasses - * may override this method to do some resource loading themselves, this - * implementation simply delegates to findResources method of the superclass - * that should return empty Enumeration. - * - * @param name the resource name - * @return an Enumeration of URLs for the resources - * @throws IOException if I/O errors occur - */ - protected Enumeration simpleFindResources(String name) throws IOException { - return super.findResources(name); - } - - - /** - * Returns a Package that has been defined by this class loader or any - * of its parents. - * - * @param name the package name - * @return the Package corresponding to the given name, or null if not found - */ - protected Package getPackage(String name) { - zombieCheck(name); - - int idx = name.lastIndexOf('.'); - if (idx == -1) return null; - String spkg = name.substring(0, idx + 1).replace('.', '/'); - - synchronized (packages) { - Package pkg = (Package)packages.get(name); - if (pkg != null) return pkg; - - for (int i = 0; i < parents.length; i++) { - ClassLoader par = parents[i]; - if (par instanceof ProxyClassLoader && shouldDelegateResource(spkg, par)) { - pkg = ((ProxyClassLoader)par).getPackage(name); - if(pkg != null) break; - } - } - // do our own lookup - if (pkg == null) pkg = super.getPackage(name); - // cache results - if (pkg != null) packages.put(name, pkg); - - return pkg; - } - } - - /** This is here just for locking serialization purposes. - * Delegates to super.definePackage with proper locking. - */ - protected Package definePackage(String name, String specTitle, - String specVersion, String specVendor, String implTitle, - String implVersion, String implVendor, URL sealBase ) - throws IllegalArgumentException { - synchronized (packages) { - return super.definePackage (name, specTitle, specVersion, specVendor, implTitle, - implVersion, implVendor, sealBase); - } - } - - /** - * Returns all of the Packages defined by this class loader and its parents. - * - * @return the array of Package objects defined by this - * ClassLoader - */ - protected synchronized Package[] getPackages() { - zombieCheck(null); - Map all = new HashMap(); // Map - addPackages(all, super.getPackages()); - for (int i = 0; i < parents.length; i++) { - ClassLoader par = parents[i]; - if (par instanceof ProxyClassLoader) { - // XXX should ideally use shouldDelegateResource here... - addPackages(all, ((ProxyClassLoader)par).getPackages()); - } - } - synchronized (packages) { - Iterator it = all.entrySet().iterator(); - while (it.hasNext()) { - Map.Entry entry = (Map.Entry)it.next(); - Object name = entry.getKey(); - if (! packages.containsKey(name)) { - packages.put(name, entry.getValue()); - } - } - } - return (Package[])all.values().toArray(new Package[all.size()]); - } - - public Package getPackageAccessibly(String name) { - return getPackage(name); - } - - public Package[] getPackagesAccessibly() { - return getPackages(); - } - - - - /** Coalesce parent classloaders into an optimized set. - * This means that all parents of the specified classloaders - * are also added recursively, removing duplicates along the way. - * Search order should be preserved (parents before children, stable w.r.t. inputs). - * @param loaders list of suggested parents (no nulls or duplicates permitted) - * @return optimized list of parents (no nulls or duplicates) - * @throws IllegalArgumentException if there are cycles - */ - private ClassLoader[] coalesceParents(ClassLoader[] loaders) throws IllegalArgumentException { - int likelySize = loaders.length * 3 + 1; - Set resultingUnique = new HashSet(likelySize); // Set - List resulting = new ArrayList(likelySize); // List - for (int i = 0; i < loaders.length; i++) { - addRec(resultingUnique, resulting, loaders[i]); - } - ClassLoader[] ret = (ClassLoader[])resulting.toArray(new ClassLoader[resulting.size()]); - return ret; - } - - /** Coalesce a new set of loaders into the existing ones. - */ - private ClassLoader[] coalesceAppend(ClassLoader[] existing, ClassLoader[] appended) throws IllegalArgumentException { - int likelySize = existing.length + 3; - Set resultingUnique = new HashSet(likelySize); - List existingL = Arrays.asList(existing); - resultingUnique.addAll(existingL); - if (resultingUnique.containsAll(Arrays.asList(appended))) { - // No change required. - return existing; - } - List resulting = new ArrayList(likelySize); - resulting.addAll(existingL); - int fromIdx = resulting.size(); - for (int i = 0; i < appended.length; i++) { - addRec(resultingUnique, resulting, appended[i]); - } - ClassLoader[] ret = (ClassLoader[])resulting.toArray(new ClassLoader[resulting.size()]); - return ret; - } - - private void addRec(Set resultingUnique, List resulting, ClassLoader loader) throws IllegalArgumentException { - if (loader == this) throw new IllegalArgumentException("cycle in parents"); // NOI18N - if (resultingUnique.contains(loader)) return; - if (loader instanceof ProxyClassLoader) { - ClassLoader[] parents = ((ProxyClassLoader)loader).parents; - for (int i = 0; i < parents.length; i++) { - addRec(resultingUnique, resulting, parents[i]); - } - } - resultingUnique.add(loader); - resulting.add(loader); - } - - /** A method that finds a class either in itself or in parents. - * It uses dual signaling for class not found: it can either return null - * or throw CNFE itself. - * @param name class name, e.g. "org.netbeans.modules.foo.Clazz" - * @param fileName resource name, e.g. "org/netbeans/modules/foo/Clazz.class" - * @param pkg package component, e.g. "org/netbeans/modules/foo/" - * @return a class or null if not found. It can also throw an exception. - * @throws ClassNotFoundException in case it doesn't found a class - * and a parent eglible for loading it thrown it already. - */ - private final Class smartLoadClass(String name, String fileName, String pkg) throws ClassNotFoundException { - // First, check if the class has already been loaded - Class c = findLoadedClass(name); - if(c != null) return c; - - final ClassLoader owner = isSpecialResource(pkg) ? null : (ClassLoader)domainsByPackage.get(pkg); - if (owner == this) { - return simpleFindClass(name,fileName); - } - if (owner != null) { - // Note that shouldDelegateResource should already be true as we hit this pkg before. - if (owner instanceof ProxyClassLoader) { - return ((ProxyClassLoader)owner).fullFindClass(name,fileName); - } else { - return owner.loadClass(name); // May throw CNFE, will be propagated - } - } - - // Virgin package, do the parent scan - c = loadInOrder(name, fileName, pkg); - - if (c != null) { - final ClassLoader owner2 = c.getClassLoader(); // who got it? - domainsByPackage.put(pkg, owner2); - } - return c; - } - - - private final Class loadInOrder( String name, String fileName, String pkg ) throws ClassNotFoundException { - ClassNotFoundException cached = null; - for (int i = 0; i < parents.length; i++) { - ClassLoader par = parents[i]; - if (!shouldDelegateResource(pkg, par)) continue; - if (par instanceof ProxyClassLoader) { - Class c = ((ProxyClassLoader)par).fullFindClass(name,fileName); - if (c != null) return c; - } else { - try { - return par.loadClass(name); - } catch( ClassNotFoundException cnfe ) { - cached = cnfe; - } - } - } - - Class c = simpleFindClass(name,fileName); // Try it ourselves - if (c != null) return c; - if (cached != null) throw cached; - return null; - } - - private synchronized Class fullFindClass(String name, String fileName) { - Class c = findLoadedClass(name); - return (c == null) ? simpleFindClass(name, fileName) : c; - } - - private void addPackages(Map all, Package[] pkgs) { - // Would be easier if Package.equals() was just defined sensibly... - for (int i = 0; i < pkgs.length; i++) { - all.put(pkgs[i].getName(), pkgs[i]); - } - } - - /** Test whether a given resource name is something that any JAR might - * have, and for which the domain cache should be disabled. - * The result must not change from one call to the next with the same argument. - * By default the domain cache is disabled only for META-INF/* JAR information. - * @param pkg the package component of the resource path ending with a slash, - * e.g. "org/netbeans/modules/foo/" - * @return true if it is a special resource, false for normal domain-cached resource - * @since org.netbeans.core/1 1.3 - */ - protected boolean isSpecialResource(String pkg) { - if (pkg.startsWith("META-INF/")) return true; // NOI18N - return false; - } - - /** Test whether a given resource request (for a class or not) should be - * searched for in the specified parent classloader or not. - * The result must not change from one call to the next with the same arguments. - * By default, always true. Subclasses may override to "mask" certain - * packages from view, possibly according to the classloader chain. - * @param pkg the package component of the resource path ending with a slash, - * e.g. "org/netbeans/modules/foo/" - * @param parent a classloader which is a direct or indirect parent of this one - * @return true if the request should be delegated to this parent; false to - * only search elsewhere (other parents, this loader's own namespace) - * @since org.netbeans.core/1 1.3 - */ - protected boolean shouldDelegateResource(String pkg, ClassLoader parent) { - return true; - } - -} Index: nb_all/core/test/unit/src/org/netbeans/PatchByteCodeTest.java =================================================================== RCS file: nb_all/core/test/unit/src/org/netbeans/PatchByteCodeTest.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ nb_all/core/test/unit/src/org/netbeans/PatchByteCodeTest.java 29 Jul 2002 19:26:36 -0000 @@ -0,0 +1,91 @@ +/* + * 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-2001 Sun + * Microsystems, Inc. All Rights Reserved. + */ + +package org.netbeans; + +import junit.framework.AssertionFailedError; +import junit.textui.TestRunner; +import org.netbeans.junit.*; +import java.io.InputStream; + +/** Test Plain top manager. Must be run externally. + * @author Jesse Glick + */ +public class PatchByteCodeTest extends NbTestCase { + + public PatchByteCodeTest(String name) { + super(name); + } + + public static void main(String[] args) { + TestRunner.run(new NbTestSuite(PatchByteCodeTest.class)); + } + + protected void setUp() throws Exception { + super.setUp(); + } + + public void testBeanTreeViewLoad () throws Exception { + checkPatching ( + "org.openide.explorer.view.BeanTreeView", + "BeanTreeView.data" + ); + } + + private void checkPatching ( + String className, String resource + ) throws Exception { + InputStream is = getClass ().getResourceAsStream (resource); + assertNotNull ("Resource has been found " + resource, is); + + byte[] arr = new byte[is.available ()]; + int l = is.read (arr); + assertEquals ("Read exactly as much as expected", l, arr.length); + + String replaceName = PatchByteCodeTest.class.getName (); + + byte[] res = PatchByteCode.enhance(arr, replaceName.replace ('.', '/')); + PatchClassLoader loader = new PatchClassLoader (className, res); + + Class c = loader.loadClass ("org.openide.explorer.view.BeanTreeView"); + + assertEquals ( + "Superclass changed appropriatelly", + replaceName, + c.getSuperclass().getName () + ); + } + + + private static final class PatchClassLoader extends ClassLoader { + private String res; + private byte[] arr; + + public PatchClassLoader (String res, byte[] arr) { + super (PatchClassLoader.class.getClassLoader ()); + + this.res = res; + this.arr = arr; + } + + protected synchronized Class loadClass(String name, boolean resolve) + throws ClassNotFoundException { + if (res.equals (name)) { + byte[] patch = PatchByteCode.patch(arr); + return defineClass (name, patch, 0, patch.length); + } else { + return super.loadClass (name, resolve); + } + } + } +} Index: nb_all/nbbuild/antsrc/org/netbeans/nbbuild/Postprocess.java =================================================================== RCS file: /cvs/nbbuild/antsrc/org/netbeans/nbbuild/Postprocess.java,v --- nb_all/nbbuild/antsrc/org/netbeans/nbbuild/Postprocess.java 15 Nov 2001 11:34:38 -0000 1.3 +++ nb_all/nbbuild/antsrc/org/netbeans/nbbuild/Postprocess.java 29 Jul 2002 19:26:45 -0000 @@ -26,6 +26,7 @@ * though a little more customized for binary files. * * @author Jaroslav Tulach +* @deprecated No longer used. */ public class Postprocess extends Task { /** file to post process */ Index: nb_all/nbbuild/antsrc/org/netbeans/nbbuild/Preprocess.java =================================================================== RCS file: /cvs/nbbuild/antsrc/org/netbeans/nbbuild/Preprocess.java,v --- nb_all/nbbuild/antsrc/org/netbeans/nbbuild/Preprocess.java 27 Oct 2000 11:13:24 -0000 1.4 +++ nb_all/nbbuild/antsrc/org/netbeans/nbbuild/Preprocess.java 29 Jul 2002 19:26:45 -0000 @@ -58,6 +58,7 @@ * more conservative changes. It should not be used as a general-purpose Java * preprocessor, we are not C++ programmers here! * @author Jaroslav Tulach, Jesse Glick +* @deprecated No longer used. */ public class Preprocess extends MatchingTask { /** the format of begining of substitution */ Index: nb_all/openide/.cvsignore =================================================================== RCS file: /cvs/openide/.cvsignore,v --- nb_all/openide/.cvsignore 24 Jun 2002 18:48:31 -0000 1.18 +++ nb_all/openide/.cvsignore 29 Jul 2002 19:26:45 -0000 @@ -7,7 +7,6 @@ examplemodules examplemodulereload openide-13javac-workaround.jar -compatkit-work standalone nb-api-tutorial.zip openide.nbm Index: nb_all/openide/build.xml =================================================================== RCS file: /cvs/openide/build.xml,v --- nb_all/openide/build.xml 29 Jul 2002 10:11:19 -0000 1.106 +++ nb_all/openide/build.xml 29 Jul 2002 19:26:45 -0000 @@ -27,8 +27,7 @@ - - + @@ -84,52 +83,50 @@ -->
- - - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -188,7 +185,7 @@ - + @@ -218,13 +215,14 @@ - + - @@ -301,16 +299,6 @@ - @@ -875,7 +863,6 @@ - Index: nb_all/openide/api/doc/changes/apichanges.xml =================================================================== RCS file: /cvs/openide/api/doc/changes/apichanges.xml,v --- nb_all/openide/api/doc/changes/apichanges.xml 29 Jul 2002 15:13:11 -0000 1.84 +++ nb_all/openide/api/doc/changes/apichanges.xml 29 Jul 2002 19:26:45 -0000 @@ -130,6 +130,22 @@ + + Listener methods are again overridable + + + + This change reverts a prior binary-compatible, source-incompatible change, + since the binary compatibility kit is no longer in use. + + + addCompilerListener and removeCompilerListener + made not-final. + + + + + Repository is not final @@ -161,6 +177,24 @@ + + + + New (even obsolete) fields in org.openide.awt.Toolbar + + + + + + Some fields appeared and/or were unfinalized in Toolbar and its innerclasses. + These were there in old 3.0 version, but were remove later when preprocess + task was introduced. The task is now replaced by patchsuperclass mechanism, + which is more clean, but works just for methods, not fields. Thus the fields + in Toolbar were made changed to their previous state. + + + + Enhanced the org.openide.awt.MenuBar @@ -3925,6 +3959,8 @@

Binary Compatibility Kit

+

Binary Compatibility up to NetBeans 3.4

+

In most cases, when it is desirable from an architectural standpoint to change an API in an incompatible way, the standard Java mechanism of @deprecated tags in Javadoc suffices as a compromise: @@ -3987,6 +4023,17 @@ into an IDE installation that has had its lib/patches/openide-compat.jar removed, to ensure that they are not depending on the deprecated APIs.

+ +

Binary Compatibility after NetBeans 3.4

+ +

+ In releases after 3.4 (expected 4.0), the system used is slightly + different, though the effect is similar. + lib/openide-compat.jar is now the compatibility JAR + location. It contains fake superclasses for some classes; the fake + superclasses will be inserted into the inheritance tree at runtime and + include methods and interfaces which are not available in source code. +

Index: nb_all/openide/compat/src/org/openide/awt/ToolbarPatch.java =================================================================== RCS file: nb_all/openide/compat/src/org/openide/awt/ToolbarPatch.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ nb_all/openide/compat/src/org/openide/awt/ToolbarPatch.java 29 Jul 2002 19:26:45 -0000 @@ -0,0 +1,63 @@ +/* + * 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-2001 Sun + * Microsystems, Inc. All Rights Reserved. + */ + +package org.openide.awt; + +import java.awt.event.MouseEvent; +import javax.swing.JToolBar; +import javax.swing.event.MouseInputListener; + +/** A fake class that holds methods that for compatibility reasons + * should be public even in the definition are not. + * + * @author Jaroslav Tulach + */ +public abstract class ToolbarPatch extends JToolBar +implements MouseInputListener { + /** Constructor that delegates. + */ + public ToolbarPatch() { + } + + /** listener to delegate to */ + abstract MouseInputListener mouseDelegate (); + + public void mouseClicked(MouseEvent e) { + mouseDelegate ().mouseClicked (e); + } + + public void mouseDragged(MouseEvent e) { + mouseDelegate ().mouseDragged (e); + } + + public void mouseEntered(MouseEvent e) { + mouseDelegate ().mouseEntered (e); + } + + public void mouseExited(MouseEvent e) { + mouseDelegate ().mouseExited (e); + } + + public void mouseMoved(MouseEvent e) { + mouseDelegate ().mouseMoved (e); + } + + public void mousePressed(MouseEvent e) { + mouseDelegate ().mousePressed (e); + } + + public void mouseReleased(MouseEvent e) { + mouseDelegate ().mouseReleased (e); + } + +} Index: nb_all/openide/compat/src/org/openide/compiler/ExternalCompilerPatch.java =================================================================== RCS file: nb_all/openide/compat/src/org/openide/compiler/ExternalCompilerPatch.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ nb_all/openide/compat/src/org/openide/compiler/ExternalCompilerPatch.java 29 Jul 2002 19:26:45 -0000 @@ -0,0 +1,28 @@ +/* + * 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-2001 Sun + * Microsystems, Inc. All Rights Reserved. + */ + +package org.openide.compiler; + +/** A fake class that holds methods that for compatibility reasons + * should be public even in the definition are not. + * + * @author Jaroslav Tulach + */ +public abstract class ExternalCompilerPatch extends Compiler { + /** Constructor that delegates. + */ + public ExternalCompilerPatch() { + } + + public abstract boolean isUpToDate(); +} Index: nb_all/openide/compat/src/org/openide/explorer/view/BeanTreeViewPatch.java =================================================================== RCS file: nb_all/openide/compat/src/org/openide/explorer/view/BeanTreeViewPatch.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ nb_all/openide/compat/src/org/openide/explorer/view/BeanTreeViewPatch.java 29 Jul 2002 19:26:46 -0000 @@ -0,0 +1,36 @@ +/* + * 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-2001 Sun + * Microsystems, Inc. All Rights Reserved. + */ + +package org.openide.explorer.view; + +import java.beans.PropertyVetoException; + +import org.openide.explorer.ExplorerManager; +import org.openide.nodes.Node; + +/** A fake class that makes some methods public even they were not. + * + * @author Jaroslav Tulach + */ +public abstract class BeanTreeViewPatch extends TreeView { + /** Default constructor. + */ + public BeanTreeViewPatch() { + } + + /** Defines the method to be public, so it will be public + * even if AbstractNode makes it protected. + */ + public abstract void selectionChanged(Node[] nodes, ExplorerManager em) throws PropertyVetoException; +} + Index: nb_all/openide/compat/src/org/openide/filesystems/AbstractFileSystemPatch.java =================================================================== RCS file: nb_all/openide/compat/src/org/openide/filesystems/AbstractFileSystemPatch.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ nb_all/openide/compat/src/org/openide/filesystems/AbstractFileSystemPatch.java 29 Jul 2002 19:26:46 -0000 @@ -0,0 +1,28 @@ +/* + * 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-2001 Sun + * Microsystems, Inc. All Rights Reserved. + */ + +package org.openide.filesystems; + +/** A fake class that holds methods that for compatibility reasons + * should be public even in the definition are not. + * + * @author Jaroslav Tulach + */ +public abstract class AbstractFileSystemPatch extends FileSystem { + // implemented in AbstractFileSystem + abstract AbstractFileObject refreshRootImpl (); + + protected final AbstractFileObject refreshRoot () { + return refreshRootImpl (); + } +} Index: nb_all/openide/compat/src/org/openide/filesystems/LocalJarFSPatch.java =================================================================== RCS file: nb_all/openide/compat/src/org/openide/filesystems/LocalJarFSPatch.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ nb_all/openide/compat/src/org/openide/filesystems/LocalJarFSPatch.java 29 Jul 2002 19:26:46 -0000 @@ -0,0 +1,50 @@ +/* + * 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-2001 Sun + * Microsystems, Inc. All Rights Reserved. + */ + +package org.openide.filesystems; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +/** A fake class that holds methods that for compatibility reasons + * should be public even in the definition are not. + * + * @author Jaroslav Tulach + */ +public abstract class LocalJarFSPatch extends AbstractFileSystem +implements AbstractFileSystem.List, AbstractFileSystem.Info, AbstractFileSystem.Change, AbstractFileSystem.Attr { + public abstract java.util.Enumeration attributes(String name); + public abstract void deleteAttributes(String name); + public abstract Object readAttribute(String name, String attrName); + public abstract void renameAttributes(String oldName, String newName); + public abstract void writeAttribute(String name, String attrName, Object value) throws IOException; + + public abstract String[] children(String f); + + public abstract void createData(String name) throws IOException; + public abstract void createFolder(String name) throws java.io.IOException; + public abstract void delete(String name) throws IOException; + public abstract boolean folder(String name); + + public abstract InputStream inputStream(String name) throws java.io.FileNotFoundException; + public abstract java.util.Date lastModified(String name); + public abstract void lock(String name) throws IOException; + public abstract void markUnimportant(String name); + public abstract String mimeType(String name); + public abstract OutputStream outputStream(String name) throws java.io.IOException; + public abstract boolean readOnly(String name); + public abstract void rename(String oldName, String newName) throws IOException; + public abstract long size(String name); + public abstract void unlock(String name); +} Index: nb_all/openide/compat/src/org/openide/filesystems/RepositoryPatch.java =================================================================== RCS file: nb_all/openide/compat/src/org/openide/filesystems/RepositoryPatch.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ nb_all/openide/compat/src/org/openide/filesystems/RepositoryPatch.java 29 Jul 2002 19:26:46 -0000 @@ -0,0 +1,26 @@ +/* + * 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-2001 Sun + * Microsystems, Inc. All Rights Reserved. + */ + +package org.openide.filesystems; + +/** A fake class that holds methods that for compatibility reasons + * should be public even in the definition are not. + * + *

+ * This class should implement Node.Cookie, so it does. + * + * @author Jaroslav Tulach + */ +public abstract class RepositoryPatch extends Object +implements org.openide.nodes.Node.Cookie { +} Index: nb_all/openide/compat/src/org/openide/loaders/MultiDataObjectPatch.java =================================================================== RCS file: nb_all/openide/compat/src/org/openide/loaders/MultiDataObjectPatch.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ nb_all/openide/compat/src/org/openide/loaders/MultiDataObjectPatch.java 29 Jul 2002 19:26:46 -0000 @@ -0,0 +1,32 @@ +/* + * 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-2001 Sun + * Microsystems, Inc. All Rights Reserved. + */ + +package org.openide.loaders; + +/** A fake class that holds methods that for compatibility reasons + * should be public even in the definition are not. + * + * @author Jaroslav Tulach + */ +public abstract class MultiDataObjectPatch extends DataObject { + /** Constructor that delegates. + */ + public MultiDataObjectPatch( + org.openide.filesystems.FileObject fo, MultiFileLoader loader + ) throws DataObjectExistsException { + super (fo, loader); + } + + public abstract org.openide.nodes.CookieSet getCookieSet (); + public abstract void setCookieSet (org.openide.nodes.CookieSet s); +} Index: nb_all/openide/compat/src/org/openide/nodes/AbstractNodePatch.java =================================================================== RCS file: nb_all/openide/compat/src/org/openide/nodes/AbstractNodePatch.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ nb_all/openide/compat/src/org/openide/nodes/AbstractNodePatch.java 29 Jul 2002 19:26:46 -0000 @@ -0,0 +1,32 @@ +/* + * 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-2001 Sun + * Microsystems, Inc. All Rights Reserved. + */ + +package org.openide.nodes; + +/** A fake class that holds methods that were available in AbstractNode + * and should not be anymore. + * + * @author Jaroslav Tulach + */ +public abstract class AbstractNodePatch extends Node { + /** + */ + public AbstractNodePatch(Children ch) { + super (ch); + } + + /** Defines the method to be public, so it will be public + * even if AbstractNode makes it protected. + */ + public abstract CookieSet getCookieSet (); +} Index: nb_all/openide/compat/src/org/openide/util/actions/SystemActionPatch.java =================================================================== RCS file: nb_all/openide/compat/src/org/openide/util/actions/SystemActionPatch.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ nb_all/openide/compat/src/org/openide/util/actions/SystemActionPatch.java 29 Jul 2002 19:26:46 -0000 @@ -0,0 +1,50 @@ +/* + * 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-2001 Sun + * Microsystems, Inc. All Rights Reserved. + */ + +package org.openide.util.actions; + +import javax.swing.Icon; +import javax.swing.ImageIcon; +import org.openide.util.Utilities; + +/** A fake class that holds methods that for compatibility reasons + * should be public even in the definition are not. + * + * @author Jaroslav Tulach + */ +public abstract class SystemActionPatch extends org.openide.util.SharedClassObject { + /** Constructor that delegates. + */ + public SystemActionPatch() { + } + + // Will be define by SystemAction + public abstract void setIcon (Icon icon); + public abstract Icon getIcon (boolean flag); + + // used to be define + public final void setIcon (ImageIcon icon) { + setIcon ((Icon) icon); + } + public final ImageIcon getIcon () { + Icon i = getIcon (false); + if (i instanceof ImageIcon) { + return ((ImageIcon) i); + } else { + // [PENDING] could try to translate Icon -> ImageIcon somehow, + // but I have no idea how to do this (paint it, take Component + // graphics, load the image data somehow??) + return new ImageIcon(Utilities.loadImage("org/openide/resources/actions/empty.gif")); // NOI18N + } + } +} Index: nb_all/openide/src/org/openide/awt/Toolbar.java =================================================================== RCS file: /cvs/openide/src/org/openide/awt/Toolbar.java,v --- nb_all/openide/src/org/openide/awt/Toolbar.java 29 Jul 2002 10:11:29 -0000 1.69 +++ nb_all/openide/src/org/openide/awt/Toolbar.java 29 Jul 2002 19:26:46 -0000 @@ -39,18 +39,9 @@ * * @author David Peroutka, Libor Kramolis */ -public class Toolbar extends JToolBar -/*nbif compat -implements MouseInputListener -/*nbend*/ -{ - /** Basic toolbar height. */ - public static - /*nbif compat - nbelse*/ - final - /*nbend*/ - int BASIC_HEIGHT = 34; +public class Toolbar extends JToolBar { + /** Basic toolbar height. Do not change. */ + public static int BASIC_HEIGHT = 34; /** 5 pixels is tolerance of toolbar height so toolbar can be high (BASIC_HEIGHT + HEIGHT_TOLERANCE) but it will be set to BASIC_HEIGHT high. */ @@ -295,37 +286,11 @@ } // end of inner class ToolbarMouseListener - /*nbif compat - public void mouseClicked (MouseEvent e) { + synchronized final MouseInputListener mouseDelegate () { if (mouseListener == null) mouseListener = new ToolbarMouseListener (); - mouseListener.mouseClicked (e); + return mouseListener; } - public void mouseEntered (MouseEvent e) { - if (mouseListener == null) mouseListener = new ToolbarMouseListener (); - mouseListener.mouseEntered (e); - } - public void mouseExited (MouseEvent e) { - if (mouseListener == null) mouseListener = new ToolbarMouseListener (); - mouseListener.mouseExited (e); - } - public void mousePressed (MouseEvent e) { - if (mouseListener == null) mouseListener = new ToolbarMouseListener (); - mouseListener.mousePressed (e); - } - public void mouseReleased (MouseEvent e) { - if (mouseListener == null) mouseListener = new ToolbarMouseListener (); - mouseListener.mouseReleased (e); - } - public void mouseDragged (MouseEvent e) { - if (mouseListener == null) mouseListener = new ToolbarMouseListener (); - mouseListener.mouseDragged (e); - } - public void mouseMoved (MouseEvent e) { - if (mouseListener == null) mouseListener = new ToolbarMouseListener (); - mouseListener.mouseMoved (e); - } - /*nbend*/ - + /** * This class can be used to produce a Toolbar instance from * the given DataFolder. @@ -551,33 +516,13 @@ public static final int DND_LINE = 3; /** Name of toolbar where event occured. */ - /*nbif compat - public - nbelse*/ - private - /*nbend*/ - String name; + public String name; /** distance of horizontal dragging */ - /*nbif compat - public - nbelse*/ - private - /*nbend*/ - int dx; + public int dx; /** distance of vertical dragging */ - /*nbif compat - public - nbelse*/ - private - /*nbend*/ - int dy; + public int dy; /** Type of event. */ - /*nbif compat - public - nbelse*/ - private - /*nbend*/ - int type; + public int type; static final long serialVersionUID =4389530973297716699L; public DnDEvent (Toolbar toolbar, String name, int dx, int dy, int type) { Index: nb_all/openide/src/org/openide/compiler/CompilerGroup.java =================================================================== RCS file: /cvs/openide/src/org/openide/compiler/CompilerGroup.java,v --- nb_all/openide/src/org/openide/compiler/CompilerGroup.java 29 Jul 2002 10:11:30 -0000 1.10 +++ nb_all/openide/src/org/openide/compiler/CompilerGroup.java 29 Jul 2002 19:26:46 -0000 @@ -64,12 +64,7 @@ /** Add a listener. * @param l the listener to add */ - public - /*nbif compat - nbelse*/ - final - /*nbend*/ - synchronized void addCompilerListener (CompilerListener l) { + public synchronized void addCompilerListener (CompilerListener l) { if (listeners == null ) { listeners = new javax.swing.event.EventListenerList(); } @@ -79,12 +74,7 @@ /** Remove a listener. * @param l the listener to remove */ - public - /*nbif compat - nbelse*/ - final - /*nbend*/ - synchronized void removeCompilerListener (CompilerListener l) { + public synchronized void removeCompilerListener (CompilerListener l) { if (listeners == null) return; listeners.remove (org.openide.compiler.CompilerListener.class, l); } Index: nb_all/openide/src/org/openide/compiler/ExternalCompiler.java =================================================================== RCS file: /cvs/openide/src/org/openide/compiler/ExternalCompiler.java,v --- nb_all/openide/src/org/openide/compiler/ExternalCompiler.java 29 Jul 2002 10:11:30 -0000 1.39 +++ nb_all/openide/src/org/openide/compiler/ExternalCompiler.java 29 Jul 2002 19:26:46 -0000 @@ -429,12 +429,7 @@ } } - /*nbif compat - public - nbelse*/ - protected - /*nbend*/ - boolean isUpToDate() { + protected boolean isUpToDate() { if (type == BUILD) { return false; } else { Index: nb_all/openide/src/org/openide/explorer/propertysheet/SetDefaultValueAction.java =================================================================== RCS file: /cvs/openide/src/org/openide/explorer/propertysheet/SetDefaultValueAction.java,v --- nb_all/openide/src/org/openide/explorer/propertysheet/SetDefaultValueAction.java 29 Jul 2002 10:11:31 -0000 1.22 +++ nb_all/openide/src/org/openide/explorer/propertysheet/SetDefaultValueAction.java 29 Jul 2002 19:26:46 -0000 @@ -21,9 +21,6 @@ * * @author Jan Jancura, Petr Hamernik, Ian Formanek */ -/*nbif compat -public -/*nbend*/ final class SetDefaultValueAction extends CallbackSystemAction { /** generated Serialized Version UID */ static final long serialVersionUID = -1285705164427519181L; Index: nb_all/openide/src/org/openide/explorer/view/BeanTreeView.java =================================================================== RCS file: /cvs/openide/src/org/openide/explorer/view/BeanTreeView.java,v --- nb_all/openide/src/org/openide/explorer/view/BeanTreeView.java 29 Jul 2002 10:11:31 -0000 1.18 +++ nb_all/openide/src/org/openide/explorer/view/BeanTreeView.java 29 Jul 2002 19:26:46 -0000 @@ -70,13 +70,7 @@ * @param nodes nodes * @param em explorer manager */ - /*nbif compat - public - nbelse*/ - protected - /*nbend*/ - void selectionChanged(Node[] nodes, ExplorerManager em) throws PropertyVetoException { - + protected void selectionChanged(Node[] nodes, ExplorerManager em) throws PropertyVetoException { if (nodes.length > 0) { Node context = nodes[0].getParentNode (); for (int i = 1; i < nodes.length; i++) { Index: nb_all/openide/src/org/openide/filesystems/AbstractFileSystem.java =================================================================== RCS file: /cvs/openide/src/org/openide/filesystems/AbstractFileSystem.java,v --- nb_all/openide/src/org/openide/filesystems/AbstractFileSystem.java 29 Jul 2002 10:11:32 -0000 1.46 +++ nb_all/openide/src/org/openide/filesystems/AbstractFileSystem.java 29 Jul 2002 19:26:46 -0000 @@ -259,12 +259,6 @@ return refreshRootImpl (); } - /*nbif compat - protected final AbstractFileObject r3fr3shR00t () { - return refreshRootImpl (); - } - /*nbend*/ - /** Allows subclasses to fire that a change occured in a * file or folder. The change can be "expected" when it is * a result of an user action and the user knows that such Index: nb_all/openide/src/org/openide/filesystems/JarFileSystem.java =================================================================== RCS file: /cvs/openide/src/org/openide/filesystems/JarFileSystem.java,v --- nb_all/openide/src/org/openide/filesystems/JarFileSystem.java 29 Jul 2002 10:11:32 -0000 1.68 +++ nb_all/openide/src/org/openide/filesystems/JarFileSystem.java 29 Jul 2002 19:26:46 -0000 @@ -37,11 +37,7 @@ * them, or (better) use delegation. * @author Jan Jancura, Jaroslav Tulach, Petr Hamernik, Radek Matous */ -public class JarFileSystem extends AbstractFileSystem - /*nbif compat - implements AbstractFileSystem.List, AbstractFileSystem.Info, AbstractFileSystem.Change, AbstractFileSystem.Attr - /*nbend*/ -{ +public class JarFileSystem extends AbstractFileSystem { /** generated Serialized Version UID */ static final long serialVersionUID = -98124752801761145L; @@ -252,12 +248,7 @@ // List // - /*nbif compat - public - nbelse*/ - protected - /*nbend*/ - String[] children (String name) { + protected String[] children (String name) { EntryCache tmpRootCache = getCacheOfRoot (); if (tmpRootCache == null) { if ((tmpRootCache = parse (false)) == null) @@ -273,39 +264,19 @@ // Change // - /*nbif compat - public - nbelse*/ - protected - /*nbend*/ - void createFolder (String name) throws java.io.IOException { + protected void createFolder (String name) throws java.io.IOException { throw new IOException (); } - /*nbif compat - public - nbelse*/ - protected - /*nbend*/ - void createData (String name) throws IOException { + protected void createData (String name) throws IOException { throw new IOException (); } - /*nbif compat - public - nbelse*/ - protected - /*nbend*/ - void rename(String oldName, String newName) throws IOException { + protected void rename(String oldName, String newName) throws IOException { throw new IOException (); } - /*nbif compat - public - nbelse*/ - protected - /*nbend*/ - void delete (String name) throws IOException { + protected void delete (String name) throws IOException { throw new IOException (); } @@ -313,23 +284,13 @@ // Info // - /*nbif compat - public - nbelse*/ - protected - /*nbend*/ - java.util.Date lastModified(String name) { + protected java.util.Date lastModified(String name) { /** JarEntry.getTime returns wrong value: already reported in bugtraq 4319781 * Fixed in jdk1.4 */ return new java.util.Date (getEntry (name).getTime ()); } - /*nbif compat - public - nbelse*/ - protected - /*nbend*/ - boolean folder (String name) { + protected boolean folder (String name) { if ("".equals (name)) return true; // NOI18N EntryCache tmpRootCache = getCacheOfRoot (); if (tmpRootCache == null) @@ -340,40 +301,20 @@ return tmpCache.isFolder (); } - /*nbif compat - public - nbelse*/ - protected - /*nbend*/ - boolean readOnly (String name) { + protected boolean readOnly (String name) { return true; } - /*nbif compat - public - nbelse*/ - protected - /*nbend*/ - String mimeType (String name) { + protected String mimeType (String name) { return null; } - /*nbif compat - public - nbelse*/ - protected - /*nbend*/ - long size (String name) { + protected long size (String name) { long retVal = getEntry (name).getSize (); return (retVal == -1) ? 0 : retVal; } - /*nbif compat - public - nbelse*/ - protected - /*nbend*/ - InputStream inputStream (String name) throws java.io.FileNotFoundException { + protected InputStream inputStream (String name) throws java.io.FileNotFoundException { InputStream is = null; AbstractFolder fo = null; try { @@ -429,46 +370,21 @@ return in; } - /*nbif compat - public - nbelse*/ - protected - /*nbend*/ - OutputStream outputStream (String name) throws java.io.IOException { + protected OutputStream outputStream (String name) throws java.io.IOException { throw new IOException (); } - /*nbif compat - public - nbelse*/ - protected - /*nbend*/ - void lock (String name) throws IOException { + protected void lock (String name) throws IOException { FSException.io ("EXC_CannotLock", name, getDisplayName (), name); // NOI18N } - /*nbif compat - public - nbelse*/ - protected - /*nbend*/ - void unlock (String name) { - } - - /*nbif compat - public - nbelse*/ - protected - /*nbend*/ - void markUnimportant (String name) { - } - - /*nbif compat - public - nbelse*/ - protected - /*nbend*/ - Object readAttribute(String name, String attrName) { + protected void unlock (String name) { + } + + protected void markUnimportant (String name) { + } + + protected Object readAttribute(String name, String attrName) { Attributes attr = getManifest ().getAttributes (name); try { return attr == null ? null : attr.getValue (attrName); @@ -477,21 +393,11 @@ } } - /*nbif compat - public - nbelse*/ - protected - /*nbend*/ - void writeAttribute(String name, String attrName, Object value) throws IOException { + protected void writeAttribute(String name, String attrName, Object value) throws IOException { throw new IOException (); } - /*nbif compat - public - nbelse*/ - protected - /*nbend*/ - Enumeration attributes(String name) { + protected Enumeration attributes(String name) { Attributes attr = getManifest ().getAttributes (name); if (attr != null) { @@ -506,20 +412,10 @@ } } - /*nbif compat - public - nbelse*/ - protected - /*nbend*/ - void renameAttributes (String oldName, String newName) { + protected void renameAttributes (String oldName, String newName) { } - /*nbif compat - public - nbelse*/ - protected - /*nbend*/ - void deleteAttributes (String name) { + protected void deleteAttributes (String name) { } /** Close the jar file when we go away...*/ Index: nb_all/openide/src/org/openide/filesystems/LocalFileSystem.java =================================================================== RCS file: /cvs/openide/src/org/openide/filesystems/LocalFileSystem.java,v --- nb_all/openide/src/org/openide/filesystems/LocalFileSystem.java 29 Jul 2002 10:11:32 -0000 1.54 +++ nb_all/openide/src/org/openide/filesystems/LocalFileSystem.java 29 Jul 2002 19:26:46 -0000 @@ -34,11 +34,7 @@ * as protected in this class. Do not call them! Subclasses might override * them, or (better) use delegation. */ -public class LocalFileSystem extends AbstractFileSystem - /*nbif compat - implements AbstractFileSystem.List, AbstractFileSystem.Info, AbstractFileSystem.Change - /*nbend*/ -{ +public class LocalFileSystem extends AbstractFileSystem { /** generated Serialized Version UID */ private static final long serialVersionUID = -5355566113542272442L; @@ -155,12 +151,7 @@ // List // - /*nbif compat - public - nbelse*/ - protected - /*nbend*/ - String[] children (String name) { + protected String[] children (String name) { File f = getFile (name); if (f.isDirectory ()) { return f.list (); @@ -173,12 +164,7 @@ // Change // - /*nbif compat - public - nbelse*/ - protected - /*nbend*/ - void createFolder (String name) throws java.io.IOException { + protected void createFolder (String name) throws java.io.IOException { File f = getFile (name); if (name.equals ("")) { // NOI18N @@ -219,12 +205,7 @@ } - /*nbif compat - public - nbelse*/ - protected - /*nbend*/ - void createData (String name) throws IOException { + protected void createData (String name) throws IOException { File f = getFile (name); if (!f.createNewFile ()) { @@ -245,12 +226,7 @@ */ } - /*nbif compat - public - nbelse*/ - protected - /*nbend*/ - void rename(String oldName, String newName) throws IOException { + protected void rename(String oldName, String newName) throws IOException { File of = getFile (oldName); File nf = getFile (newName); @@ -260,12 +236,7 @@ } } - /*nbif compat - public - nbelse*/ - protected - /*nbend*/ - void delete(String name) throws IOException { + protected void delete(String name) throws IOException { File file = getFile(name); if (deleteFile(file) != SUCCESS) { if (file.exists()) @@ -323,49 +294,24 @@ // Info // - /*nbif compat - public - nbelse*/ - protected - /*nbend*/ - java.util.Date lastModified(String name) { + protected java.util.Date lastModified(String name) { return new java.util.Date (getFile (name).lastModified ()); } - /*nbif compat - public - nbelse*/ - protected - /*nbend*/ - boolean folder (String name) { + protected boolean folder (String name) { return getFile (name).isDirectory (); } - /*nbif compat - public - nbelse*/ - protected - /*nbend*/ - boolean readOnly (String name) { + protected boolean readOnly (String name) { File f = getFile (name); return !f.canWrite () && f.exists (); } - /*nbif compat - public - nbelse*/ - protected - /*nbend*/ - String mimeType (String name) { + protected String mimeType (String name) { return null; } - /*nbif compat - public - nbelse*/ - protected - /*nbend*/ - long size (String name) { + protected long size (String name) { return getFile (name).length (); } @@ -399,12 +345,7 @@ // ============================================================================ // Begin of the original part - /*nbif compat - public - nbelse*/ - protected - /*nbend*/ - InputStream inputStream (String name) throws java.io.FileNotFoundException { + protected InputStream inputStream (String name) throws java.io.FileNotFoundException { FileInputStream fis; File file = null; @@ -419,24 +360,14 @@ return fis; } - /*nbif compat - public - nbelse*/ - protected - /*nbend*/ - OutputStream outputStream (String name) throws java.io.IOException { + protected OutputStream outputStream (String name) throws java.io.IOException { return new FileOutputStream (getFile (name)); } // End of the original part // ============================================================================ - /*nbif compat - public - nbelse*/ - protected - /*nbend*/ - void lock (String name) throws IOException { + protected void lock (String name) throws IOException { File file = getFile (name); if ((!file.canWrite () && file.exists ()) || isReadOnly()) { @@ -444,20 +375,10 @@ } } - /*nbif compat - public - nbelse*/ - protected - /*nbend*/ - void unlock (String name) { + protected void unlock (String name) { } - /*nbif compat - public - nbelse*/ - protected - /*nbend*/ - void markUnimportant (String name) { + protected void markUnimportant (String name) { } /** Creates file for given string name. Index: nb_all/openide/src/org/openide/filesystems/Repository.java =================================================================== RCS file: /cvs/openide/src/org/openide/filesystems/Repository.java,v --- nb_all/openide/src/org/openide/filesystems/Repository.java 29 Jul 2002 10:11:32 -0000 1.38 +++ nb_all/openide/src/org/openide/filesystems/Repository.java 29 Jul 2002 19:26:46 -0000 @@ -38,9 +38,6 @@ * @author Jaroslav Tulach, Petr Hamernik */ public class Repository extends Object implements java.io.Serializable -/*nbif compat -, org.openide.nodes.Node.Cookie -/*nbend*/ { /** list of filesystems (FileSystem) */ private ArrayList fileSystems; Index: nb_all/openide/src/org/openide/loaders/DataLoaderPool.java =================================================================== RCS file: /cvs/openide/src/org/openide/loaders/DataLoaderPool.java,v --- nb_all/openide/src/org/openide/loaders/DataLoaderPool.java 29 Jul 2002 10:11:34 -0000 1.87 +++ nb_all/openide/src/org/openide/loaders/DataLoaderPool.java 29 Jul 2002 19:26:46 -0000 @@ -518,10 +518,6 @@ // /** Loader for folders, since 1.13 changed to UniFileLoader. */ - /*nbif compat - public - nbelse*/ - /*nbend*/ static class FolderLoader extends UniFileLoader { static final long serialVersionUID =-8325525104047820255L; @@ -652,11 +648,6 @@ /* Instance loader recognizing .ser and .instance files. It's placed at * the end of loader pool among default loaders. */ - /*nbif compat - public - nbelse*/ - private - /*nbend*/ static class InstanceLoader extends UniFileLoader { static final long serialVersionUID =-3462727693843631328L; @@ -770,11 +761,6 @@ /** Loader for file objects not recognized by any other loader */ - /*nbif compat - public - nbelse*/ - private - /*nbend*/ static class DefaultLoader extends MultiFileLoader { static final long serialVersionUID =-6761887227412396555L; @@ -867,11 +853,6 @@ } /** Loader for shadows, since 1.13 changed to UniFileLoader. */ - /*nbif compat - public - nbelse*/ - private - /*nbend*/ static class ShadowLoader extends UniFileLoader { static final long serialVersionUID =-11013405787959120L; Index: nb_all/openide/src/org/openide/loaders/MultiDataObject.java =================================================================== RCS file: /cvs/openide/src/org/openide/loaders/MultiDataObject.java,v --- nb_all/openide/src/org/openide/loaders/MultiDataObject.java 29 Jul 2002 10:11:34 -0000 1.67 +++ nb_all/openide/src/org/openide/loaders/MultiDataObject.java 29 Jul 2002 19:26:46 -0000 @@ -585,12 +585,7 @@ * @param s the cookie set to use * @deprecated just use getCookieSet().add(...) instead */ - /*nbif compat - public - nbelse*/ - protected final - /*nbend*/ - void setCookieSet (CookieSet s) { + protected final void setCookieSet (CookieSet s) { setCookieSet(s, true); } @@ -625,12 +620,7 @@ * * @return the cookie set (never null) */ - /*nbif compat - public - nbelse*/ - protected final - /*nbend*/ - CookieSet getCookieSet () { + protected final CookieSet getCookieSet () { CookieSet s = cookieSet; if (s != null) return s; synchronized (cookieSetLock) { Index: nb_all/openide/src/org/openide/nodes/AbstractNode.java =================================================================== RCS file: /cvs/openide/src/org/openide/nodes/AbstractNode.java,v --- nb_all/openide/src/org/openide/nodes/AbstractNode.java 29 Jul 2002 10:11:34 -0000 1.51 +++ nb_all/openide/src/org/openide/nodes/AbstractNode.java 29 Jul 2002 19:26:46 -0000 @@ -512,12 +512,7 @@ * * @return the cookie set created by {@link #setCookieSet}, or an empty set (never null) */ - /*nbif compat - public - nbelse*/ - protected - /*nbend*/ - final CookieSet getCookieSet () { + protected final CookieSet getCookieSet () { CookieSet s = cookieSet; if (s != null) return s; synchronized (this) { Index: nb_all/openide/src/org/openide/util/actions/SystemAction.java =================================================================== RCS file: /cvs/openide/src/org/openide/util/actions/SystemAction.java,v --- nb_all/openide/src/org/openide/util/actions/SystemAction.java 29 Jul 2002 10:11:35 -0000 1.58 +++ nb_all/openide/src/org/openide/util/actions/SystemAction.java 29 Jul 2002 19:26:46 -0000 @@ -188,23 +188,6 @@ return getIcon (false); } - /*nbif compat - public final void setIcon (ImageIcon icon) { - setIcon ((Icon) icon); - } - public final ImageIcon g3t1c0n () { - Icon i = getIcon (false); - if (i instanceof ImageIcon) { - return ((ImageIcon) i); - } else { - // [PENDING] could try to translate Icon -> ImageIcon somehow, - // but I have no idea how to do this (paint it, take Component - // graphics, load the image data somehow??) - return getBlankIcon(); - } - } - /*nbend*/ - /** Get the action's display icon, possibly creating a text label. * @param createLabel if true, create a textual icon if otherwise there * would be none; if false, create a blank icon Index: nb_all/xtest/build.xml =================================================================== RCS file: /cvs/xtest/build.xml,v --- nb_all/xtest/build.xml 16 Jul 2002 16:58:11 -0000 1.39 +++ nb_all/xtest/build.xml 29 Jul 2002 19:26:55 -0000 @@ -39,6 +39,7 @@ + Index: nb_all/xtest/src/org/netbeans/core/modules/Module.java =================================================================== RCS file: /cvs/xtest/src/org/netbeans/core/modules/Module.java,v --- nb_all/xtest/src/org/netbeans/core/modules/Module.java 6 Mar 2002 14:15:04 -0000 1.6 +++ nb_all/xtest/src/org/netbeans/core/modules/Module.java 29 Jul 2002 19:26:55 -0000 @@ -29,6 +29,8 @@ import java.util.zip.ZipEntry; import org.openide.modules.SpecificationVersion; import org.openide.modules.Dependency; +import org.netbeans.JarClassLoader; +import org.netbeans.ProxyClassLoader; /** Object representing one module, possibly installed. * Responsible for opening of module JAR file; reading @@ -723,15 +725,31 @@ // #9273: load any modules/patches/this-code-name/*.jar files first: File patchdir = new File(new File(jar.getParentFile(), "patches"), // NOI18N getCodeNameBase().replace('.', '-')); // NOI18N - if (patchdir.isDirectory()) { - File[] jars = patchdir.listFiles(Util.jarFilter()); - if (jars != null) { - for (int j = 0; j < jars.length; j++) { - ev.log(Events.PATCH, jars[j]); - classp.add(new JarFile(jars[j])); + + scanFilesInADir (patchdir, classp); + + String patchesClassPath = System.getProperty ("netbeans.patches." + getCodeNameBase ()); // NOI18N + if (patchesClassPath != null) { + //scanFilesInADir (new File (dir), classp); + StringTokenizer tokenizer = new StringTokenizer(patchesClassPath, File.pathSeparator); + while (tokenizer.hasMoreElements()) { + String element = (String) tokenizer.nextElement(); + File fileElement = new File(element); + if (fileElement.isDirectory()) { + // adding directory + Util.err.log("module patches: adding directory "+fileElement+" for "+getCodeNameBase()); + File[] jars = fileElement.listFiles(Util.jarFilter()); + if (jars != null) { + for (int j=0; j < jars.length; j++) { + // add jar files + System.out.println("adding jar file:"+jars[j]); + classp.add( new JarFile(jars[j])); + } + } + // add also dir + System.out.println("adding directory:"+fileElement); + classp.add((Object) fileElement); } - } else { - Util.err.log(ErrorManager.WARNING, "Could not search for patches in " + patchdir); } } @@ -779,6 +797,11 @@ File act = (File)it.next(); classp.add(act.isDirectory() ? (Object)act : new JarFile(act)); } + + if (loaders.length == 0) { + loaders = new ClassLoader[] {Module.class.getClassLoader()}; + } + try { classloader = new OneModuleClassLoader(classp, loaders); } catch (IllegalArgumentException iae) { @@ -788,6 +811,23 @@ throw ioe; } } + + /** Scans content of a directory */ + private void scanFilesInADir (File patchdir, List classp) throws IOException { + if (!patchdir.isDirectory()) { + return; + } + File[] jars = patchdir.listFiles(Util.jarFilter()); + if (jars != null) { + for (int j = 0; j < jars.length; j++) { + ev.log(Events.PATCH, jars[j]); + classp.add(new JarFile(jars[j])); + } + } else { + Util.err.log(ErrorManager.WARNING, "Could not search for patches in " + patchdir); + } + } + /** Turn off the classloader and release all resources. */ void classLoaderDown() { if (isFixed()) return; // don't touch it Index: nb_all/xtest/src/org/netbeans/xtest/ide/Main.java =================================================================== RCS file: nb_all/xtest/src/org/netbeans/xtest/ide/Main.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ nb_all/xtest/src/org/netbeans/xtest/ide/Main.java 29 Jul 2002 19:26:55 -0000 @@ -0,0 +1,673 @@ +/* + * 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-2002 Sun + * Microsystems, Inc. All Rights Reserved. + */ + +package org.netbeans.xtest.ide; + +import java.util.*; +import java.awt.EventQueue; +import java.awt.Toolkit; +import java.awt.AWTEvent; +import java.awt.event.MouseEvent; +import java.awt.event.*; +import org.openide.*; +import org.openide.filesystems.*; +import org.openide.filesystems.FileSystem; // override java.io.FileSystem +import org.openide.loaders.*; +import org.openide.nodes.*; +import org.openide.execution.*; +import org.openide.util.*; +import org.openide.util.actions.*; + +import org.netbeans.core.output.OutputSettings; +import org.netbeans.core.execution.TopSecurityManager; + +// flag file stuff +import java.io.*; + +// native kill stuff +import org.netbeans.xtest.util.JNIKill; + +/** + * + * @author jchalupa + */ +public class Main extends Object { + + // terminate code :-) + public static final int TERMINATE_CODE = 666; + // terminate command :-) + public static final String TERMINATE_NAME = "kill"; + + private static ErrorManager errMan; + + // ide running flag file + private static File ideRunning = null; + + + + // flag whether IDE was interrupted + private static boolean ideInterrupted = false; + + + // finds terminate process action in the action array + private static SystemAction findTerminateAction(SystemAction[] actions) { + if (actions == null) { + throw new IllegalArgumentException(); + } + // I need to get the string from resource bundle + // bundle = org.netbeans.core.execution.Bundle.properties + // key = terminateProcess + String terminateString = NbBundle.getMessage(org.netbeans.core.execution.ProcessNode.class,"terminateProcess"); + for (int i=0; i 0) { + // better sleep for a sec, so they can be really killed + try { + Thread.sleep(5000); + } catch (InterruptedException ie) { + // nothing + } + } + return notTerminatedProcesses; + } + + + /** Installs XTestErrorManager to NetBeans + * + */ + private static boolean installXTestErrorManager() { + try { + System.out.println("Installing XTestErrorManager"); + FileSystem systemFS = TopManager.getDefault().getRepository().findFileSystem("SystemFileSystem"); + DataFolder emFolder = DataFolder.findFolder(systemFS.getRoot().getFileObject("Services").getFileObject("Hidden")); + InstanceDataObject xem = InstanceDataObject.create(emFolder,"xem","org.netbeans.xtest.ide.XTestErrorManager"); + // sleep for a sec, so ErrorManager gets installed + Thread.sleep(1000); + System.out.println("XTestErrorManager Installed"); + return true; + } catch (NullPointerException npe) { + // cannot install custom ErrorManager + return false; + } catch (IOException ioe) { + return false; + } catch (InterruptedException ie) { + return true; + } + } + + private static void prepareModuleLoaderClassPath() { + String moduleLoaderName = System.getProperty("xtest.useclassloader"); + if (moduleLoaderName != null) { + System.out.println("Using module "+moduleLoaderName+" classloader to load tests"); + String testbagClassPath = System.getProperty("tbag.classpath"); + if (testbagClassPath != null) { + // set appropriate property + String patchesProperty = "netbeans.patches."+moduleLoaderName; + System.out.println("Setting system property "+patchesProperty+" to "+testbagClassPath); + System.setProperty(patchesProperty, testbagClassPath); + } else { + System.out.println("TestBag classpath (tbag.classpath) property not defined - there is nothing to load"); + } + } + } + + + /** Starts the IDE. + * @param args the command line arguments + */ + public static void main(String args[]) { + // need to initialize org.openide.TopManager, not org.netbeans.core.Plain + System.getProperties().put ( + "org.openide.TopManager", // NOI18N + "org.netbeans.core.Main" // NOI18N + ); + + // create the IDE flag file + String workdir = System.getProperty("xtest.workdir"); + if (workdir!=null) { + // create flag file indicating running tests + ideRunning = new File(workdir,"ide.flag"); + File idePID = new File(workdir,"ide.pid"); + PrintWriter writer = null; + try { + ideRunning.createNewFile(); + idePID.createNewFile(); + writer = new PrintWriter(new FileOutputStream(idePID)); + // get my pid + JNIKill kill = new JNIKill(); + long myPID = kill.getMyPID(); + System.out.println("IDE is running under PID:"+myPID); + writer.println(myPID); + } catch (IOException ioe) { + System.out.println("cannot create flag file:"+ideRunning); + ideRunning = null; + } catch (Throwable e) { + System.out.println("There was a problem: "+e); + } finally { + if (writer != null) { + writer.close(); + } + } + } else { + System.out.println("cannot get xtest.workdir property - it has to be set to a valid working directory"); + } + + // prepare module's classpath + prepareModuleLoaderClassPath(); + + // do the expected stuff + try { + org.netbeans.core.Main.main(args); + } + catch (Exception e) { + e.printStackTrace(); + exit(); + } + + // install XTest error manager (only if xtest.error.manager=true) + // and openide version > 2 + + // get openide version - if sys property is not found - assume it is version 0 + float openideVersion = 0; + try { + openideVersion = Float.parseFloat(System.getProperty("org.openide.specification.version","0")); + } catch (NumberFormatException nfe) { + // property is not found - go on with 0 + } + + if (System.getProperty("xtest.error.manager","false").equals("true")) { + if (openideVersion > 2.0) { + boolean result = installXTestErrorManager(); + if (result==false) { + System.out.println("Cannot install XTestErrorManager"); + } else { + System.setProperty("xtest.error.manager","installed"); + } + } else { + System.out.println("OpenIDE version must be over 2.0 to use XTestErrorManager"); + System.out.println("Current version is "+openideVersion); + } + } + + + + + + // get the static ErrorManager instance + errMan = TopManager.getDefault().getErrorManager(); + + // some threads may be still active, wait for the event queue to become quiet + Thread initThread = new Thread(new Runnable() { + public void run() { + try { + new QueueEmpty().waitEventQueueEmpty(2000); + } + catch (Exception ex) { + TopManager.getDefault().getErrorManager().notify(ErrorManager.EXCEPTION, ex); + } + } + }); + // start the init thread + initThread.start(); + try { + initThread.join(60000L); // wait 1 minute at the most + } + catch (InterruptedException iex) { + errMan.notify(ErrorManager.EXCEPTION, iex); + + } + if (initThread.isAlive()) { + // time-out expired, event queue still busy -> interrupt the init thread + errMan.log(ErrorManager.USER, new Date().toString() + ": EventQueue still busy, starting anyway."); + initThread.interrupt(); + } + // ok. The IDE should be up and fully initialized + // let's run the test now + try { + doTestPart(); + } + catch (RuntimeException ex) { + ex.printStackTrace(); + } + } + + + private static final String TEST_CLASS = "test.class"; + private static final String TEST_CLASSPATH = "test.classpath"; + private static final String TEST_ARGS = "test.arguments"; + private static final String TEST_EXECUTOR = "test.executor"; + private static final String TEST_EXIT = "test.exit"; + private static final String TEST_FINISHED = "test.finished"; + private static final String TEST_TIMEOUT = "test.timeout"; + private static final String TEST_REDIRECT = "test.output.redirect"; + private static final String TEST_REUSE_IDE = "test.reuse.ide"; + + private static final long DEFAULT_TIMEOUT = 30; + + private static void doTestPart() { + + long testTimeout; + + // do nothing if no TEST_CLASS specified + if (System.getProperty(TEST_CLASS) == null) + return; + + // Yarda Tulach's line - is this good for anything ? + Object jarda = org.openide.filesystems.Repository.getDefault (); + + if (System.getProperty(TEST_REDIRECT) != null && System.getProperty(TEST_REDIRECT).equals("true")) { + ((OutputSettings) Lookup.getDefault().lookup( OutputSettings.class )).setRedirection(true); + } + + + try { + // default timeout is 30 minutes + testTimeout = Long.parseLong(System.getProperty(TEST_TIMEOUT, "30")); + } + catch (NumberFormatException ex) { + testTimeout = DEFAULT_TIMEOUT; + } + if (testTimeout <= 0) { + testTimeout = DEFAULT_TIMEOUT; + } + // convert to milis + testTimeout *= 60000L; + + StringTokenizer st = new StringTokenizer(System.getProperty(TEST_ARGS, "")); + final String[] params = new String[st.countTokens()]; + int i = 0; + while (st.hasMoreTokens()) { + params[i++] = st.nextToken(); + } + + final long startTime = System.currentTimeMillis(); + final long testTime = testTimeout; + Thread testThread = new Thread( new Runnable() { + public void run() { + try { + + // setup the repository + if (System.getProperty(TEST_CLASSPATH) != null && + System.getProperty(TEST_REUSE_IDE, "false").equals("false")) { + mountFileSystems(); + } + + setNodeProperties(); + + ExecInfo ei = new org.openide.execution.ExecInfo(System.getProperty(TEST_CLASS), params); + Executor exec = (Executor)Class.forName(System.getProperty(TEST_EXECUTOR, "org.openide.execution.ThreadExecutor")).newInstance(); + + if (exec != null) { + System.setProperty(TEST_FINISHED, "false"); + ExecutorTask task = exec.execute(ei); + + while (System.getProperty(TEST_FINISHED).equals("false")) { + try { + Thread.sleep(2000); + } + catch (InterruptedException e) { + long currentTime = System.currentTimeMillis(); + if (startTime + testTime + 10000 > currentTime) { + break; + } + else { + errMan.log(ErrorManager.USER, new Date().toString() + ": False InterruptedException. Continuing running tests"); + } + } + } + } + } + catch (Exception ex) { + TopManager.getDefault().getErrorManager().notify(ErrorManager.EXCEPTION, ex); + } + } + }); + + errMan.log(ErrorManager.USER, new Date().toString() + ": just starting."); + // start the test thread + testThread.start(); + try { + testThread.join(testTimeout); + } + catch (InterruptedException iex) { + errMan.notify(ErrorManager.EXCEPTION, iex); + } + if (testThread.isAlive()) { + // time-out expired, test not finished -> interrupt + errMan.log(ErrorManager.USER, new Date().toString() + ": time-out expired - interrupting! ***"); + ideInterrupted = true; + testThread.interrupt(); + } + + // we're leaving IDE + // delete the flag file (if ide was not interrupted) + if (ideRunning!=null) { + if (!ideInterrupted) { + if (ideRunning.delete()==false) { + System.out.println("Cannot delete the flag file "+ideRunning); + } + } + } + + // close IDE + if (System.getProperty(TEST_EXIT, "false").equals("true")) { + Thread exitThread = new Thread(new Runnable() { + public void run() { + // terminate all running processes + try { + terminateProcesses(); + } catch (Exception e) { + System.out.println("Exception when terminating processes started from IDE"); + e.printStackTrace(); + } + TopManager.getDefault().exit(); + } + }); + // try to exit nicely first + errMan.log(ErrorManager.USER, new Date().toString() + ": soft exit attempt."); + exitThread.start(); + try { + // wait 90 seconds for the IDE to exit + exitThread.join(90000); + } + catch (InterruptedException iex) { + errMan.notify(ErrorManager.EXCEPTION, iex); + } + if (exitThread.isAlive()) { + // IDE refuses to shutdown, terminate unconditionally + errMan.log(ErrorManager.USER, new Date().toString() + ": hard exit attempt!!!."); + exitThread.interrupt(); + exit(); + } + } + } + + private static void exit() { + try { + Class param[] = new Class[1]; + param[0] = int.class; + Class c = org.netbeans.core.execution.TopSecurityManager.class; + java.lang.reflect.Method m = c.getMethod("exit",param); + Integer intparam[] = {new Integer(1)}; + errMan.log(ErrorManager.USER, new Date().toString() + ": using TopSecurityManager.exit(1) to exit IDE."); + // exit + m.invoke(null,intparam); + } + catch (Exception e) { + errMan.log(ErrorManager.USER, new Date().toString() + ": using System.exit(1) to exit IDE."); + // exit + System.exit(1); + } + } + + + private static void mountFileSystems() { + Repository repo = TopManager.getDefault().getRepository(); + + // unmount the currently mounted filesystems + // (could be more sofisticated some day) + Enumeration all = repo.getFileSystems(); + while (all.hasMoreElements()) { + FileSystem fs = (FileSystem)all.nextElement(); + // preserve the hidden and default filesystems + if (!fs.isHidden() && !fs.isDefault()) + repo.removeFileSystem(fs); + } + // mount new filesystems as specified in TEST_CLASSPATH + StringTokenizer stok = new StringTokenizer(System.getProperty(TEST_CLASSPATH), System.getProperty("path.separator")); + while (stok.hasMoreElements()) { + String pathelem = stok.nextToken(); + try { + if (pathelem.endsWith(".jar") || pathelem.endsWith(".zip")) { + JarFileSystem jfs = new JarFileSystem(); + jfs.setJarFile(new java.io.File(pathelem)); + repo.addFileSystem(jfs); + } + else { + LocalFileSystem lfs = new LocalFileSystem(); + lfs.setRootDirectory(new java.io.File(pathelem)); + repo.addFileSystem(lfs); + } + } + catch (Exception ex) { + TopManager.getDefault().getErrorManager().notify(ErrorManager.EXCEPTION, ex); + } + } + } + + + private static void setNodeProperties() { + FileObject fo = TopManager.getDefault().getRepository().findResource(System.getProperty(TEST_CLASS) + ".java"); + if (fo != null) { + try { + DataObject obj = DataObject.find(fo); + Node nod = obj.getNodeDelegate(); + Node.PropertySet[] psets = nod.getPropertySets(); + Node.Property[] props = null; + + // get the Execution property set + for (int i = 0; i < psets.length; i++) { + if (psets[i].getName().equals("Execution")) { + props = psets[i].getProperties(); + break; + } + } + // get the "params" property and try to set it + if (props != null) { + for (int i = 0; i < props.length; i++) { + if (props[i].getName().equals("params")) { + if (System.getProperty(TEST_ARGS) != null) { + props[i].setValue(System.getProperty(TEST_ARGS)); + } + } + } + } + } + catch (java.lang.Exception ex) { + // ok, not able to set the Arguments property + // it's still worth trying to proceed with the test + // the FileObject may just be read-only + } + + } + + } + + + private static class QueueEmpty implements java.awt.event.AWTEventListener { + + private long eventDelayTime = 100; // 100 millis + private long lastEventTime; + + /** Creates a new QueueEmpty instance */ + public QueueEmpty() { + } + + /** method called every time when AWT Event is dispatched + * @param event event dispatched from AWT Event Queue + */ + public void eventDispatched(java.awt.AWTEvent awtEvent) { + lastEventTime = System.currentTimeMillis(); + } + + /** constructor with user defined value + * @param eventdelaytime maximum delay between two events of one redraw action + */ + public synchronized void waitEventQueueEmpty(long eventDelayTime) throws InterruptedException { + this.eventDelayTime = eventDelayTime; + waitEventQueueEmpty(); + } + + /** Waits until the AWTEventQueue is empty for a specified interval + */ + public synchronized void waitEventQueueEmpty() throws InterruptedException { + // store current time as the start time + long startTime = System.currentTimeMillis(); + // register itself as an AWTEventListener + Toolkit.getDefaultToolkit().addAWTEventListener(this, + AWTEvent.ACTION_EVENT_MASK | + AWTEvent.ADJUSTMENT_EVENT_MASK | + AWTEvent.COMPONENT_EVENT_MASK | + AWTEvent.CONTAINER_EVENT_MASK | + AWTEvent.FOCUS_EVENT_MASK | + AWTEvent.HIERARCHY_BOUNDS_EVENT_MASK | + AWTEvent.HIERARCHY_EVENT_MASK | + AWTEvent.INPUT_METHOD_EVENT_MASK | + AWTEvent.INVOCATION_EVENT_MASK | + AWTEvent.ITEM_EVENT_MASK | + AWTEvent.KEY_EVENT_MASK | + AWTEvent.MOUSE_EVENT_MASK | + AWTEvent.MOUSE_MOTION_EVENT_MASK | + AWTEvent.PAINT_EVENT_MASK | + AWTEvent.TEXT_EVENT_MASK | + AWTEvent.WINDOW_EVENT_MASK); + + // set last event time to the current time + lastEventTime=System.currentTimeMillis(); + // get the thread to be put asleep + Thread t = Thread.currentThread(); + // get current AWT Event Queue + EventQueue queue=Toolkit.getDefaultToolkit().getSystemEventQueue(); + + try { + + // while AWT Event Queue is not empty and timedelay from last event is shorter then eventdelaytime + + //wait till the queue is empty + while ( queue.peekEvent() != null ) Thread.currentThread().sleep(100); + //test it - post my own task and wait for it + synchronized(this){ + Runnable r = new Runnable() { + public void run() { + synchronized(QueueEmpty.this){QueueEmpty.this.notifyAll();} + } + }; + queue.invokeLater(r); + wait(); + } + //now 2 sec continuously should be silence + while (System.currentTimeMillis() - lastEventTime < eventDelayTime) { + //sleep for the rest of eventDelay time + t.sleep(eventDelayTime + lastEventTime - System.currentTimeMillis()); + } + + //if (queue.peekEvent()==null) System.out.println("The AWT event queue seems to be empty."); + //else System.out.println("Ops, in the AWT event queue still seems to be some tasks!"); + + } + catch (InterruptedException ex) { + throw ex; + } + finally { + //removing from listeners + Toolkit.getDefaultToolkit().removeAWTEventListener(this); + } + } + + + } +} + Index: nb_all/xtest/src/org/netbeans/xtest/ide/XTestErrorManager.java =================================================================== RCS file: nb_all/xtest/src/org/netbeans/xtest/ide/XTestErrorManager.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ nb_all/xtest/src/org/netbeans/xtest/ide/XTestErrorManager.java 29 Jul 2002 19:26:55 -0000 @@ -0,0 +1,164 @@ +/* + * 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-2002 Sun + * Microsystems, Inc. All Rights Reserved. + */ + +package org.netbeans.xtest.ide; + +import org.openide.ErrorManager; +import org.openide.ErrorManager.Annotation; +import org.openide.TopManager; +import org.netbeans.core.TopLogging; +import java.util.*; +import java.io.*; +import java.lang.ref.*; + +/** + * + * @author mb115822 + */ +public class XTestErrorManager extends ErrorManager { + + + + // here's XTest ErrorManager + private static ErrorManager errorManager = null; + + + // here are stored notifications + private static StringBuffer errorLog = new StringBuffer(); + + // getter for notifications + public static synchronized String getNotifications() { + if (errorLog.length() > 0) { + return errorLog.toString(); + } else { + return null; + } + } + + // clear stuff for notifications + public static synchronized void clearNotifications() { + errorLog = new StringBuffer(); + } + + + // get if there were any notifications and clear the status + public static synchronized boolean anyNotifications() { + if (getNotifications() == null) { + return false; + } else { + clearNotifications(); + return true; + } + } + + + + /** Creates new NbExceptionManager. */ + public XTestErrorManager() { + if (errorManager == null) { + errorManager = this; + } + } + + + + public org.openide.ErrorManager getInstance(String name) { + return getInstance(); + } + + public static org.openide.ErrorManager getInstance() { + if (errorManager == null) { + errorManager = new XTestErrorManager(); + } + return errorManager; + } + + /** Adds these values. All the + * previous annotations are kept and this new is added at + * the top of the annotation stack (index 0 of the annotation + * array). + * + * @param severity integer describing severity (one of const values + * from this class) + * @param date date or null + * @param message message to attach to the exception or null + * @param localizedMessage localized message for the user or null + * @param stackTrace exception representing the stack trace or null + */ + public synchronized Throwable annotate( + Throwable t, + int severity, String message, String localizedMessage, + Throwable stackTrace, java.util.Date date + ) { + return null; + } + + + /** Associates annotations with this thread. + * + * @param arr array of annotations (or null) + */ + public synchronized Throwable attachAnnotations(Throwable t, Annotation[] arr) { + return null; + } + + /** Notifies all the exceptions associated with + * this thread. + * @param clear should the current exception be cleared or not? + */ + public synchronized void notify(int severity, Throwable t) { + // synchronized to ensure that only one exception is + // written to the thread + + + if (severity > ErrorManager.USER ) { + // let's print the stuff to theByteArray .... + + ByteArrayOutputStream bs = new ByteArrayOutputStream(); + + PrintWriter ps = new PrintWriter(bs); + + ps.println("*********** Exception occured ************"); // NOI18N + + // log into the byte array + t.printStackTrace(ps); + + ps.flush(); + + ps.close(); + + // add the notification to the string buffer + errorLog.append(bs.toString()+"\n"); + /* + ErrorManager.getDefault().log(ErrorManager.ERROR,"#\n#\n############# XTestEM notification: "+t+"\n#\n#\n"); + */ + } + } + + + /** Finds annotations associated with given exception. + * @param t the exception + * @return array of annotations or null + */ + public synchronized Annotation[] findAnnotations(Throwable t) { + return null; + } + + // log !!! + public void log(int severity, String s) { + } + + + + +} Index: nbextra/xtest/build.xml =================================================================== RCS file: /nbextra/xtest/build.xml,v --- nbextra/xtest/build.xml 2002/02/20 11:07:53 1.6 +++ nbextra/xtest/build.xml 2002/07/29 19:25:37 @@ -17,11 +17,11 @@ debug="${build.compiler.debug}"> + - - + Index: nbextra/xtest/src/org/netbeans/xtest/junit/.cvsignore =================================================================== RCS file: .cvsignore --- /dev/null Thu Oct 4 12:06:47 2001 1.17 +++ nbextra/xtest/src/org/netbeans/xtest/junit/.cvsignore Mon Jul 29 12:25:37 2002 @@ -0,0 +1, @@ +*.class Index: nbextra/xtest/src/org/netbeans/xtest/pe/junit/.cvsignore =================================================================== RCS file: .cvsignore --- /dev/null Thu Oct 4 12:06:47 2001 +++ nbextra/xtest/src/org/netbeans/xtest/pe/junit/.cvsignore Mon Jul 29 12:25:37 2002 @@ -0,0 +1, @@ +*.class Index: nbextra/xtest/src/org/netbeans/xtest/pe/junit/JUnitResultsReporter.java =================================================================== RCS file: /nbextra/xtest/src/org/netbeans/xtest/pe/junit/JUnitResultsReporter.java,v --- nbextra/xtest/src/org/netbeans/xtest/pe/junit/JUnitResultsReporter.java 2002/07/26 11:40:26 1.10 +++ nbextra/xtest/src/org/netbeans/xtest/pe/junit/JUnitResultsReporter.java 2002/07/29 19:25:37 @@ -19,7 +19,7 @@ package org.netbeans.xtest.pe.junit; -import org.netbeans.xtest.XTestErrorManager; +import org.netbeans.xtest.ide.XTestErrorManager; import org.netbeans.xtest.junit.*; import org.netbeans.xtest.pe.xmlbeans.*; import org.netbeans.xtest.pe.*; Index: nbextra/xtest/src/org/netbeans/xtest/util/.cvsignore =================================================================== RCS file: .cvsignore --- /dev/null Thu Oct 4 12:06:47 2001 +++ nbextra/xtest/src/org/netbeans/xtest/util/.cvsignore Mon Jul 29 12:25:37 2002 @@ -0,0 +1, @@ +*.class