+ 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
retrieving revision 1.14
diff -u -r1.14 Main.java
--- 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 13 Aug 2002 23:37:52 -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
diff -N 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 13 Aug 2002 23:37:52 -0000
@@ -0,0 +1,805 @@
+/*
+ * 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.*;
+import java.util.HashMap;
+
+/** Class that can enhance bytecode with information about alternative
+ * superclass and access modifiers. It can also extract this information
+ * later when the class is about to be loaded into the VM.
+ *
+ * The additional information is added to attributes of the classfile (global attributes
+ * and also member attributes) and as such the class remain compatible and
+ * understandable for any VM. But if loaded by classloader that before defining
+ * the class invokes:
+ *
+ * byte[] arr = ...;
+ * arr = PatchByteCode.patch (arr);
+ *
+ * The class is altered in its superclass and/or access modifiers.
+ *
+ * The patching mechanism uses two attributes. ATTR_SUPERCLASS can be just
+ * in global attributes pool (and only once), is of length 2 and contains index
+ * into constant pool that contains definition of a Class that should become
+ * the alternate superclass. Attribute ATTR_MEMBER can appear in global
+ * attribute set and also in set of each member (field or method). It is of
+ * length 2 and contains alternate value for access flags of the class or of
+ * the field.
+ *
+ * For purposes for speed, each patched class file has to end with bytes "nb".
+ * This is achieved by finishing the patching process by adding third attribute
+ * "org.netbeans.enhanced" with value "nb". As such the PatchByteCode.patch
+ * can quickly check the byte array and process just those that need processing.
+ *
+ * @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 static final String ATTR_MEMBER = "org.netbeans.member"; // NOI18N
+ private static final byte[] BYTE_MEMBER;
+ static {
+ try {
+ BYTE_MEMBER = ATTR_MEMBER.getBytes("utf-8"); // NOI18N
+ } catch (java.io.UnsupportedEncodingException ex) {
+ throw new IllegalStateException (ex.getMessage());
+ }
+ }
+
+ private static final String ATTR_INIT = ""; // NOI18N
+ private static final byte[] BYTE_INIT;
+ static {
+ try {
+ BYTE_INIT = ATTR_INIT.getBytes("utf-8"); // NOI18N
+ } catch (java.io.UnsupportedEncodingException ex) {
+ throw new IllegalStateException (ex.getMessage());
+ }
+ }
+
+ private static final String ATTR_INIT_TYPE = "()V"; // NOI18N
+ private static final byte[] BYTE_INIT_TYPE;
+ static {
+ try {
+ BYTE_INIT_TYPE = ATTR_INIT_TYPE.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;
+ /** the index of a string to patch members of a field */
+ private int memberNameIndex = -1;
+ /** position of attribute the change the access rights of the class */
+ private int memberClassAttr = -1;
+ /** index of UTF8 in constant pool*/
+ private int initIndex = -1;
+ /** index of ()V UTF8 in constant pool */
+ private int initIndexType = -1;
+ /** index of CONSTANT_NameAndType index for and ()V in pool */
+ private int initNameTypeIndex = -1;
+ /** position of the method */
+ private int initAttr = -1;
+
+ /** map that maps names of fields to their position in constant pool (String, int[1]) */
+ private HashMap nameIndexes;
+
+ /** Creates a new instance of PatchByteCode
+ *
+ * @param nameIndexes hashmap from (String -> int[1])
+ */
+ private PatchByteCode(byte[] arr, HashMap nameIndexes) {
+ this.arr = arr;
+ this.nameIndexes = nameIndexes;
+
+ // scan twice because of back references
+ scan ();
+ 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) {
+ return enhance (arr, superClass, null);
+ }
+
+ /** 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
+ * @param methods array of names of methods to make public
+ * @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, String[] methods) {
+ if (isPatched (arr)) {
+ // already patched
+ return null;
+ }
+
+ HashMap m;
+ if (methods != null) {
+ m = new HashMap ();
+ for (int i = 0; i < methods.length; i++) {
+ m.put (methods[i], new int[1]);
+ }
+ } else {
+ m = null;
+ }
+
+
+ PatchByteCode pc = new PatchByteCode (arr, m);
+ boolean patched = false;
+
+ if (superClass != null) {
+ int x = pc.addClass (superClass);
+
+ byte[] sup = new byte[2];
+ writeU2 (sup, 0, x);
+ pc.addAttribute (ATTR_SUPERCLASS, sup);
+
+ patched = true;
+ }
+
+ if (!pc.isPublic ()) {
+ // will need patching
+ pc.markPublic ();
+ patched = true;
+ }
+
+ if (methods != null) {
+ for (int i = 0; i < methods.length; i++) {
+ patched |= pc.markMemberPublic (methods[i]);
+ }
+ }
+
+ if (patched) {
+ byte[] patch = {
+ 'n', 'b' // identification at the end of class file
+ };
+
+ pc.addAttribute ("org.netbeans.enhanced", patch);
+ } else {
+ 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, null);
+ if (pc.superClassNameAttr > 0) {
+ // let's patch
+ int classindex = pc.readU2 (pc.superClassNameAttr + 6);
+
+ writeU2 (pc.getClassFile(), pc.cpEnd + 4, classindex);
+
+ if (pc.initAttr != -1) {
+ // patch also CONSTANT_Methodref to superclass's
+ writeU2 (pc.getClassFile (), pc.initAttr + 1, classindex);
+ }
+ }
+
+ if (pc.memberClassAttr > 0) {
+ // change the access rights of the class itself
+ if (pc.readU4 (pc.memberClassAttr + 2) != 2) {
+ throw new IllegalArgumentException ("Size of a attribute " + ATTR_MEMBER + " should be 2"); // NOI18N
+ }
+
+ // alternate access rights
+ int access = pc.readU2 (pc.memberClassAttr + 6);
+
+ int now = pc.readU2 (pc.cpEnd);
+
+ writeU2 (pc.getClassFile (), pc.cpEnd, access);
+
+ }
+
+ if (pc.memberNameIndex > 0) {
+ // change access rights of fields
+ pc.applyMemberAccessChanges ();
+ }
+
+ 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;
+ }
+
+ /** Checks whether the code is public.
+ */
+ private boolean isPublic () {
+ int x = readU2 (cpEnd);
+
+ if ((x & 0x0001) != 0) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /** Ensures that the class is public
+ * @return true if really patched, false if not
+ */
+ private boolean markPublic () {
+ if (isPublic ()) {
+ return false;
+ }
+
+ // make sure ATTR_MEMBER is in constant pool
+ if (memberNameIndex == -1) {
+ memberNameIndex = addConstant (ATTR_MEMBER);
+ }
+
+ int x = readU2 (cpEnd) | 0x0001; // make it public
+
+ byte[] sup = new byte[2];
+ writeU2 (sup, 0, x);
+ addAttribute (ATTR_MEMBER, sup);
+
+ return true;
+ }
+
+ /** Makes method of field public and non final.
+ * @param name name of the method to make public
+ * @return true if really changed, false if it already was public
+ */
+ private boolean markMemberPublic (String name) {
+ int constantPoolIndex = ((int[])nameIndexes.get (name))[0];
+ int patchCount = 0;
+ boolean modified = false;
+
+ // make sure ATTR_MEMBER is in constant pool
+ if (memberNameIndex == -1) {
+ memberNameIndex = addConstant (ATTR_MEMBER);
+ }
+
+ int pos = cpEnd;
+
+ pos += 6;
+ // now add interfaces
+ pos += 2 * readU2 (pos);
+ // to add also the integer with interfaces
+ pos += 2;
+
+ for (int fieldsAndMethods = 0; fieldsAndMethods < 2; fieldsAndMethods++) {
+ // fields and then methods
+ int fieldsOrMethods = readU2 (pos);
+ pos += 2;
+
+ while (fieldsOrMethods-- > 0) {
+ // check the name
+ int nameIndex = readU2 (pos + 2);
+ if (nameIndex == constantPoolIndex) {
+ // let's patch
+ int access = readU2 (pos);
+ if ((access & 0x0001) == 0 || (access & 0x0010) != 0) {
+ // is not public or is final
+ access = (access | 0x0001) & ~(0x0010 | 0x0002 | 0x0004);
+
+
+ // increment the attributes count
+ int cnt = readU2 (pos + 6) + 1;
+
+ //
+ byte[] res = new byte[arr.length + 2 + 6];
+
+ // copy the array before
+ System.arraycopy(arr, 0, res, 0, pos + 6);
+ // write the new count of attributes
+ writeU2 (res, pos + 6, cnt);
+
+ // write the attribute itself
+ writeU2 (res, pos + 8, memberNameIndex); // name of attribute
+ writeU4 (res, pos + 10, 2); // length
+ writeU2 (res, pos + 14, access); // data - the "NetBeans" member modifier
+
+ // copy the rest
+ System.arraycopy(arr, pos + 8, res, pos + 8 + 6 + 2, arr.length - pos - 8);
+
+ atEnd += 2 + 6;
+ atCount += 2 + 6;
+
+
+ arr = res;
+
+ modified = true;
+ }
+
+ patchCount++;
+ }
+
+ pos += memberSize (pos, null);
+ }
+ }
+
+ if (patchCount == 0) {
+ throw new IllegalArgumentException ("Member " + name + " not found!");
+ }
+
+ return modified;
+ }
+
+
+ /** Checks all members of the class to find out whether they need patching
+ * of access rights. If so, patches them.
+ */
+ private void applyMemberAccessChanges () {
+ int[] result = new int[1];
+
+ int pos = cpEnd;
+
+ pos += 6;
+ // now add interfaces
+ pos += 2 * readU2 (pos);
+ // to add also the integer with interfaces
+ pos += 2;
+
+ for (int fieldsAndMethods = 0; fieldsAndMethods < 2; fieldsAndMethods++) {
+ // fields and then methods
+ int fieldsOrMethods = readU2 (pos);
+ pos += 2;
+
+ while (fieldsOrMethods-- > 0) {
+ result[0] = -1;
+ int size = memberSize(pos, result);
+ if (result[0] != -1) {
+ // we will do patching
+
+ if (readU4 (result[0] + 2) != 2) {
+ throw new IllegalArgumentException ("Size of a attribute " + ATTR_MEMBER + " should be 2"); // NOI18N
+ }
+
+ // alternate access rights
+ int access = readU2 (result[0] + 6);
+ writeU2 (arr, pos, access);
+ }
+
+ pos += size;
+ }
+ }
+ }
+
+ /** 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 = -1;
+ if (ATTR_SUPERCLASS.equals (name) && superClassNameIndex > 0) {
+ index = superClassNameIndex;
+ }
+
+ if (ATTR_MEMBER.equals (name) && memberNameIndex > 0) {
+ index = memberNameIndex;
+ }
+
+ if (index == -1) {
+ // 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 += memberSize (pos, null);
+ }
+
+ int methods = readU2 (pos);
+ pos += 2;
+ while (methods-- > 0) {
+ pos += memberSize (pos, null);
+ }
+
+ // count of items in Attributes is here
+
+ int[] memberAccess = { -1 };
+
+ atCount = pos;
+ int attrs = readU2 (pos);
+ pos += 2;
+ while (attrs-- > 0) {
+ pos += attributeSize (pos, memberAccess);
+ }
+
+ if (memberAccess[0] != -1) {
+ // we need to update the name of class
+ memberClassAttr = memberAccess[0];
+ }
+
+ // 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 12: // CONSTANT_NameAndType
+ // check for and ()V invocation
+ int nameIndex = readU2 (pos + 1);
+ if (nameIndex == initIndex) {
+ int descriptorIndex = readU2 (pos + 3);
+ if (descriptorIndex == initIndexType) {
+ if (initNameTypeIndex > 0 && initNameTypeIndex != cnt[0]) {
+ throw new IllegalArgumentException ("Second initialization of name type index"); // NOI18N
+ }
+ initNameTypeIndex = cnt[0];
+ }
+ }
+ return 5;
+
+ case 10: // CONSTANT_Methodref
+ // special check for invocation
+ int classname = readU2 (pos + 1);
+ int nameAndType = readU2 (pos + 3);
+ if (nameAndType == initNameTypeIndex) {
+ // found call to
+ int superclass = readU2 (cpEnd + 4);
+
+ if (superclass == classname) {
+ // it is call to our superclass
+ if (initAttr > 0 && initAttr != pos) {
+ throw new IllegalStateException ("Second initialization of position of invocation"); // NOI18N
+ }
+ initAttr = pos;
+ }
+ }
+ return 5;
+
+ case 9: // CONSTANT_Fieldref
+ case 11: // CONSTANT_InterfaceMethodref
+ case 3: // CONSTANT_Integer
+ case 4: // CONSTANT_Float
+ 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 (compareUtfEntry (BYTE_INIT, pos)) {
+ if (initIndex > 0 && initIndex != cnt[0]) {
+ throw new IllegalArgumentException ("Second initialization of " + ATTR_INIT); // NOI18N
+ }
+ initIndex = cnt[0];
+ }
+
+ if (compareUtfEntry (BYTE_INIT_TYPE, pos)) {
+ if (initIndexType > 0 && initIndexType != cnt[0]) {
+ throw new IllegalArgumentException ("Second initialization of " + ATTR_INIT_TYPE); // NOI18N
+ }
+ initIndexType = cnt[0];
+ }
+
+ if (compareUtfEntry (BYTE_SUPERCLASS, pos)) {
+ // we have found the attribute
+ if (superClassNameIndex > 0 && superClassNameIndex != cnt[0]) {
+ throw new IllegalStateException (ATTR_SUPERCLASS + " registered for the second time!"); // NOI18N
+ }
+
+ superClassNameIndex = cnt[0];
+ }
+
+ if (compareUtfEntry (BYTE_MEMBER, pos)) {
+ // we have found the attribute
+ if (memberNameIndex > 0 && memberNameIndex != cnt[0]) {
+ throw new IllegalStateException (ATTR_MEMBER + " registered for the second time!"); // NOI18N
+ }
+
+ memberNameIndex = cnt[0];
+ }
+
+ if (nameIndexes != null) {
+ // check the name in the table
+ String s;
+ try {
+ s = new String (arr, pos + 3, len, "utf-8"); // NOI18N
+ } catch (UnsupportedEncodingException ex) {
+ throw new IllegalStateException ("utf-8 is always supported"); // NOI18N
+ }
+
+ int[] index = (int[])nameIndexes.get (s);
+ if (index != null) {
+ index[0] = cnt[0];
+ }
+ }
+
+ // ok, exit
+ return len + 3;
+ default:
+ throw new IllegalStateException ("Unknown pool type: " + get (pos)); // NOI18N
+ }
+ }
+
+ private int memberSize (int pos, int[] containsPatchAttribute) {
+ int s = 8;
+ int name = readU2 (pos + 2);
+
+ int cnt = readU2 (pos + 6);
+
+ while (cnt-- > 0) {
+ s += attributeSize (pos + s, containsPatchAttribute);
+ }
+ return s;
+ }
+
+ /** Into the containsPatchAttribute (if not null) it adds the
+ * index of structure of attribute ATTR_MEMBER.
+ */
+ private int attributeSize (int pos, int[] containsPatchAttribute) {
+ // index to the name attr
+ int name = readU2 (pos);
+
+ if (name == superClassNameIndex) {
+ if (superClassNameAttr > 0 && superClassNameAttr != pos) {
+ throw new IllegalStateException ("Two attributes with name " + ATTR_SUPERCLASS); // NOI18N
+ }
+
+ // we found the attribute
+ superClassNameAttr = pos;
+ }
+
+ if (name == memberNameIndex && containsPatchAttribute != null) {
+ if (containsPatchAttribute[0] != -1) {
+ throw new IllegalStateException ("Second attribute " + ATTR_MEMBER); // NOI18N
+ }
+ containsPatchAttribute[0] = pos;
+ }
+
+ int len = readU4 (pos + 2);
+ return len + 6;
+ }
+
+
+ /** Compares arrays.
+ */
+ private boolean compareUtfEntry (byte[] pattern, int pos) {
+ int len = readU2 (pos + 1);
+
+ if (pattern.length != len) {
+ return false;
+ }
+
+ int base = pos + 3;
+ // we are searching for an attribute with given name
+ for (int i = 0; i < len; i++) {
+ if (pattern[i] != arr[base + i]) {
+ // regular exit
+ return false;
+ }
+ }
+
+ return true;
+ }
+}
Index: nb_all/core/src/org/netbeans/ProxyClassLoader.java
===================================================================
RCS file: nb_all/core/src/org/netbeans/ProxyClassLoader.java
diff -N 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 13 Aug 2002 23:37:52 -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:
+ *
+ * - Calls {@link #findLoadedClass(String)} to check if the class has
+ * already been loaded.
+ *
- Checks the caches whether another class from the same package
+ * was already loaded and uses the same classloader
+ *
- Tries to find the class using parent loaders in their order.
+ *
- 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:
+ *
+ * - Checks the caches whether another resource or class from the same
+ * package was already loaded and uses the same classloader.
+ *
- Tries to find the resources using parent loaders in their order.
+ *
- 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
retrieving revision 1.147
diff -u -r1.147 Main.java
--- 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 13 Aug 2002 23:37:52 -0000
@@ -121,8 +121,9 @@
/** 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
+ StartLog.logStart ("Forwarding to topThreadGroup"); // NOI18N
tg.start ();
}
@@ -356,7 +357,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
retrieving revision 1.16
diff -u -r1.16 TopThreadGroup.java
--- 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 13 Aug 2002 23:37:52 -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
diff -N 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
retrieving revision 1.30
diff -u -r1.30 Module.java
--- 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 13 Aug 2002 23:37:52 -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
retrieving revision 1.35
diff -u -r1.35 ModuleManager.java
--- nb_all/core/src/org/netbeans/core/modules/ModuleManager.java 5 Aug 2002 15:29:16 -0000 1.35
+++ nb_all/core/src/org/netbeans/core/modules/ModuleManager.java 13 Aug 2002 23:37:52 -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], Collections.EMPTY_SET);
+ private SystemClassLoader classLoader = new SystemClassLoader(new ClassLoader[] {ModuleManager.class.getClassLoader()}, Collections.EMPTY_SET);
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, modules);
} catch (IllegalArgumentException iae) {
Util.err.notify(iae);
- nue = new SystemClassLoader(new ClassLoader[0], Collections.EMPTY_SET);
+ nue = new SystemClassLoader(new ClassLoader[] {ModuleManager.class.getClassLoader()}, Collections.EMPTY_SET);
}
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
diff -N 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:
- *
- * - Calls {@link #findLoadedClass(String)} to check if the class has
- * already been loaded.
- *
- Checks the caches whether another class from the same package
- * was already loaded and uses the same classloader
- *
- Tries to find the class using parent loaders in their order.
- *
- 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:
- *
- * - Checks the caches whether another resource or class from the same
- * package was already loaded and uses the same classloader.
- *
- Tries to find the resources using parent loaders in their order.
- *
- 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/cfg-unit.xml
===================================================================
RCS file: /cvs/core/test/cfg-unit.xml,v
retrieving revision 1.22
diff -u -r1.22 cfg-unit.xml
--- nb_all/core/test/cfg-unit.xml 21 Jun 2002 15:16:47 -0000 1.22
+++ nb_all/core/test/cfg-unit.xml 13 Aug 2002 23:37:52 -0000
@@ -57,6 +57,7 @@
+
Index: nb_all/core/test/unit/src/org/netbeans/PatchByteCodeTest.java
===================================================================
RCS file: nb_all/core/test/unit/src/org/netbeans/PatchByteCodeTest.java
diff -N 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 13 Aug 2002 23:37:53 -0000
@@ -0,0 +1,194 @@
+/*
+ * 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 junit.framework.AssertionFailedError;
+import junit.textui.TestRunner;
+import org.netbeans.junit.*;
+import java.io.InputStream;
+import java.lang.reflect.*;
+
+/** Test patching of openide.jar byte code for compatibility.
+ * @author Jaroslav Tulach
+ */
+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",
+ "data/BeanTreeView.clazz",
+ null
+ );
+ }
+
+ public void testCompilerGroupLoad () throws Exception {
+ checkPatching (
+ "org.openide.compiler.CompilerGroup",
+ "data/CompilerGroup.clazz",
+ null
+ );
+
+ InputStream is = getClass ().getResourceAsStream ("data/CompilerGroup.clazz");
+ assertNotNull ("Class has not been found", is);
+
+ byte[] arr = new byte[is.available ()];
+ int l = is.read (arr);
+ assertEquals ("Read exactly as much as expected", l, arr.length);
+
+
+ byte[] res = PatchByteCode.enhance(arr, null, new String[] { "addCompilerListener", "removeCompilerListener" } );
+ PatchClassLoader loader = new PatchClassLoader ("org.openide.compiler.CompilerGroup", res, ClassLoader.getSystemClassLoader());
+
+ Class c = loader.loadClass ("org.openide.compiler.CompilerGroup");
+
+ Method m = c.getDeclaredMethod("addCompilerListener", new Class[] { org.openide.compiler.CompilerListener.class });
+ assertTrue ("Is not final", !Modifier.isFinal (m.getModifiers ()));
+
+ m = c.getDeclaredMethod("removeCompilerListener", new Class[] { org.openide.compiler.CompilerListener.class });
+ assertTrue ("Is not final", !Modifier.isFinal (m.getModifiers ()));
+ }
+
+ public void testClassCanBeAlsoInstantiated () throws Exception {
+ Class c = checkPatching (
+ Sample.class.getName (),
+ "Sample.class",
+ "java.lang.Throwable"
+ );
+
+ c.newInstance ();
+ }
+
+ public void testPatchingOfFieldsAndMethodsToPublicAndNonFinal () throws Exception {
+ InputStream is = getClass ().getResourceAsStream ("Sample.class");
+ assertNotNull ("Class has not been found", is);
+
+ byte[] arr = new byte[is.available ()];
+ int l = is.read (arr);
+ assertEquals ("Read exactly as much as expected", l, arr.length);
+
+
+
+ byte[] res = PatchByteCode.enhance(arr, null, new String[] { "member", "field", "method", "staticmethod" } );
+ PatchClassLoader loader = new PatchClassLoader (Sample.class.getName (), res, ClassLoader.getSystemClassLoader());
+
+ Class c = loader.loadClass (Sample.class.getName ());
+
+ assertTrue ("Class should be public", Modifier.isPublic (c.getModifiers()));
+
+ Method m = c.getDeclaredMethod("method", new Class[0]);
+ assertNotNull ("Mehtod method is there", m);
+ assertTrue ("And is public", Modifier.isPublic (m.getModifiers()));
+ assertTrue ("And is not final", !Modifier.isFinal(m.getModifiers ()));
+ assertTrue ("And is not static", !Modifier.isStatic(m.getModifiers()));
+ assertTrue ("And is not synchronzied", !Modifier.isSynchronized(m.getModifiers()));
+
+ m = c.getDeclaredMethod("member", new Class[] { Object.class });
+ assertNotNull ("Member method is there", m);
+ assertTrue ("And is public", Modifier.isPublic (m.getModifiers()));
+ assertTrue ("And is not final", !Modifier.isFinal(m.getModifiers ()));
+ assertTrue ("And is not static", !Modifier.isStatic(m.getModifiers()));
+ assertTrue ("And is synchronzied", Modifier.isSynchronized(m.getModifiers()));
+
+ m = c.getDeclaredMethod("staticmethod", new Class[] { });
+ assertNotNull ("Member method is there", m);
+ assertTrue ("And is public", Modifier.isPublic (m.getModifiers()));
+ assertTrue ("And is not final", !Modifier.isFinal(m.getModifiers ()));
+ assertTrue ("And is not static", Modifier.isStatic(m.getModifiers()));
+ assertTrue ("And is not synchronzied", !Modifier.isSynchronized(m.getModifiers()));
+
+ java.lang.reflect.Field f;
+
+ f = c.getDeclaredField("member");
+ assertNotNull ("Really exists", f);
+ assertTrue ("Is public", Modifier.isPublic (f.getModifiers ()));
+ assertTrue ("Is not final", !Modifier.isFinal (f.getModifiers ()));
+ assertTrue ("Is static", Modifier.isStatic (f.getModifiers ()));
+
+ f = c.getDeclaredField("field");
+ assertNotNull ("Really exists", f);
+ assertTrue ("Is public", Modifier.isPublic (f.getModifiers ()));
+ assertTrue ("Is not final", !Modifier.isFinal (f.getModifiers ()));
+ assertTrue ("Is static", !Modifier.isStatic (f.getModifiers ()));
+
+ }
+
+
+ private Class checkPatching (
+ String className, String resource, String superclass
+ ) throws Exception {
+ if (superclass == null) {
+ superclass = PatchByteCodeTest.class.getName ();
+ }
+
+ 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);
+
+ byte[] res = PatchByteCode.enhance(arr, superclass.replace ('.', '/'));
+ PatchClassLoader loader = new PatchClassLoader (className, res);
+
+ Class c = loader.loadClass (className);
+
+ assertEquals (
+ "Superclass changed appropriately",
+ superclass,
+ c.getSuperclass().getName ()
+ );
+
+ return c;
+ }
+
+
+ private static final class PatchClassLoader extends ClassLoader {
+ private String res;
+ private byte[] arr;
+
+ public PatchClassLoader (String res, byte[] arr) {
+ this (res, arr, PatchClassLoader.class.getClassLoader ());
+ }
+
+ public PatchClassLoader (String res, byte[] arr, ClassLoader c) {
+ super (c);
+
+ 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/core/test/unit/src/org/netbeans/Sample.java
===================================================================
RCS file: nb_all/core/test/unit/src/org/netbeans/Sample.java
diff -N nb_all/core/test/unit/src/org/netbeans/Sample.java
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ nb_all/core/test/unit/src/org/netbeans/Sample.java 13 Aug 2002 23:37:53 -0000
@@ -0,0 +1,40 @@
+/*
+ * 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;
+import java.lang.reflect.*;
+
+/** Sample class to pass to PatchByteCodeTest to see what changes can be done.
+ */
+class Sample extends Object {
+ private static Object member;
+ private final Object field = null;
+
+ public Sample () {
+ }
+
+ protected synchronized void member (Object x) {
+ }
+
+ private final Object method () {
+ return null;
+ }
+
+ protected static void staticmethod () {
+ }
+}
Index: nb_all/core/test/unit/src/org/netbeans/data/BeanTreeView.clazz
===================================================================
RCS file: nb_all/core/test/unit/src/org/netbeans/data/BeanTreeView.clazz
diff -N nb_all/core/test/unit/src/org/netbeans/data/BeanTreeView.clazz
Binary files /dev/null and BeanTreeView.clazz differ
Index: nb_all/core/test/unit/src/org/netbeans/data/CompilerGroup.clazz
===================================================================
RCS file: nb_all/core/test/unit/src/org/netbeans/data/CompilerGroup.clazz
diff -N nb_all/core/test/unit/src/org/netbeans/data/CompilerGroup.clazz
Binary files /dev/null and CompilerGroup.clazz differ
Index: nb_all/nbbuild/antsrc/org/netbeans/nbbuild/NbEnhanceClass.java
===================================================================
RCS file: nb_all/nbbuild/antsrc/org/netbeans/nbbuild/NbEnhanceClass.java
diff -N nb_all/nbbuild/antsrc/org/netbeans/nbbuild/NbEnhanceClass.java
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ nb_all/nbbuild/antsrc/org/netbeans/nbbuild/NbEnhanceClass.java 13 Aug 2002 23:38:19 -0000
@@ -0,0 +1,217 @@
+/*
+ * 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.nbbuild;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.ArrayList;
+import java.util.Iterator;
+// IMPORTANT! You may need to mount ant.jar before this class will
+// compile. So mount the JAR modules/ext/ant-1.4.1.jar (NOT modules/ant.jar)
+// from your IDE installation directory in your Filesystems before
+// continuing to ensure that it is in your classpath.
+
+import org.apache.tools.ant.*;
+import org.apache.tools.ant.types.*;
+
+/**
+ * @author Jaroslav Tulach
+ */
+public class NbEnhanceClass extends Task {
+ /* Path to library containing the patch method */
+ private File patchLibrary;
+ public void setLibrary (File f) {
+ patchLibrary = f;
+ }
+
+ /* Name of class with patch method */
+ private String patchClass = "org.netbeans.PatchByteCode";
+ public void setPatchClass (String f) {
+ patchClass = f;
+ }
+
+ /** Name of the method to call. Must have byte[] array argument and return the same
+ */
+ private String enhanceMethod = "enhance";
+ public void setEnhanceMethod (String f) {
+ enhanceMethod = f;
+ }
+
+ /* Base dir to find classes relative to */
+ private File basedir;
+ public void setBasedir (File f) {
+ basedir = f;
+ }
+
+ /* The class to change its super class and the value of the super class.
+ */
+ public static class Patch {
+ String clazz;
+ String nbSuperClass;
+ ArrayList members;
+
+
+ /** Class in form of java/lang/Object */
+ public void setClass (String s) {
+ clazz = s;
+ }
+ /** Class in form of java/lang/Object */
+ public void setSuper (String s) {
+ nbSuperClass = s;
+ }
+
+ public Object createMember () {
+ Member m = new Member();
+ if (members == null) {
+ members = new ArrayList ();
+ }
+ members.add (m);
+ return m;
+ }
+
+ public static final class Member extends Object {
+ String name;
+
+ public void setName (String s) {
+ name = s;
+ }
+ }
+ }
+ private ArrayList patches = new ArrayList (); // List
+ public Patch createPatch () {
+ Patch n = new Patch ();
+ patches.add(n);
+ return n;
+ }
+
+
+ public void execute() throws BuildException {
+ if (basedir == null) {
+ throw new BuildException ("Attribute basedir must be specified");
+ }
+
+ if (patches.isEmpty()) {
+ // no work
+ return;
+ }
+
+ //
+ // Initialize the method
+ //
+
+ ClassLoader cl;
+ if (patchLibrary == null) {
+ log ("Loading patch class from task loader");
+ cl = getClass ().getClassLoader();
+ } else {
+ try {
+ cl = new URLClassLoader (new URL[] { patchLibrary.toURL() });
+ } catch (java.net.MalformedURLException ex) {
+ throw new BuildException (ex);
+ }
+ }
+
+ java.lang.reflect.Method m;
+ try {
+ Class c = cl.loadClass (patchClass);
+ m = c.getMethod(enhanceMethod, new Class[] { byte[].class, String.class, String[].class });
+ if (m.getReturnType() != byte[].class) {
+ throw new BuildException ("Method does not return byte[]: " + m);
+ }
+ } catch (Exception ex) {
+ throw new BuildException ("Cannot initialize class " + patchClass + " and method " + enhanceMethod, ex);
+ }
+
+ /*
+ try {
+ log ("Testing method " + m);
+ byte[] res = (byte[])m.invoke (null, new Object[] { new byte[0], "someString" });
+ } catch (Exception ex) {
+ throw new BuildException ("Exception during test invocation of the method", ex);
+ }
+ */
+
+ //
+ // Ok we have the method and we can do the patching
+ //
+
+ Iterator it = patches.iterator();
+ while (it.hasNext()) {
+ Patch p = (Patch)it.next ();
+
+ if (p.clazz == null) {
+ throw new BuildException ("Attribute class must be specified");
+ }
+
+ File f = new File (basedir, p.clazz + ".class");
+ if (!f.exists ()) {
+ throw new BuildException ("File " + f + " for class " + p.clazz + " does not exists");
+ }
+
+ byte[] arr = new byte[(int)f.length()];
+ try {
+ FileInputStream is = new FileInputStream (f);
+ if (arr.length != is.read (arr)) {
+ throw new BuildException ("Not all bytes read");
+ }
+ is.close ();
+ } catch (IOException ex) {
+ throw new BuildException ("Cannot read file " + f, ex);
+ }
+
+ String[] members;
+ if (p.members != null) {
+ members = new String[p.members.size ()];
+ Iterator myIt = p.members.iterator();
+ int i = 0;
+ while (myIt.hasNext ()) {
+ members[i++] = ((Patch.Member)myIt.next ()).name;
+ }
+ } else {
+ members = null;
+ }
+
+
+ byte[] out;
+ try {
+ out = (byte[])m.invoke (null, new Object[] { arr, p.nbSuperClass, members });
+ if (out == null) {
+ // no patching needed
+ continue;
+ }
+ } catch (Exception ex) {
+ throw new BuildException (ex);
+ }
+
+ if (p.nbSuperClass != null) {
+ log ("Enhanced " + f + " to have alternate superclass " + p.nbSuperClass + " and be public");
+ } else {
+ log ("Enhanced " + f + " to be public");
+ }
+
+ try {
+ FileOutputStream os = new FileOutputStream (f);
+ os.write (out);
+ os.close ();
+ } catch (IOException ex) {
+ throw new BuildException ("Cannot overwrite file " + f, ex);
+ }
+ }
+ }
+
+}
Index: nb_all/nbbuild/antsrc/org/netbeans/nbbuild/NbPatchClass.java
===================================================================
RCS file: nb_all/nbbuild/antsrc/org/netbeans/nbbuild/NbPatchClass.java
diff -N nb_all/nbbuild/antsrc/org/netbeans/nbbuild/NbPatchClass.java
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ nb_all/nbbuild/antsrc/org/netbeans/nbbuild/NbPatchClass.java 13 Aug 2002 23:38:19 -0000
@@ -0,0 +1,191 @@
+/*
+ * 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.nbbuild;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.jar.JarFile;
+// IMPORTANT! You may need to mount ant.jar before this class will
+// compile. So mount the JAR modules/ext/ant-1.4.1.jar (NOT modules/ant.jar)
+// from your IDE installation directory in your Filesystems before
+// continuing to ensure that it is in your classpath.
+
+import org.apache.tools.ant.*;
+import org.apache.tools.ant.types.*;
+
+/**
+ * @author Jaroslav Tulach
+ */
+public class NbPatchClass extends Task {
+ /* Path to library containing the patch method */
+ private File patchLibrary;
+ public void setLibrary (File f) {
+ patchLibrary = f;
+ }
+
+ /* Name of class with patch method */
+ private String patchClass = "org.netbeans.PatchByteCode";
+ public void setPatchClass (String f) {
+ patchClass = f;
+ }
+
+ /** Name of the method to call. Must have byte[] array argument and return the same
+ */
+ private String patchMethod = "patch";
+ public void setPatchMethod (String f) {
+ patchMethod = f;
+ }
+
+ /** Source JAR to extract.
+ */
+ private File sourceJar;
+ public void setSource (File f) {
+ sourceJar = f;
+ }
+
+ /* Base dir to find classes relative to */
+ private File targetdir;
+ public void setTargetdir (File f) {
+ targetdir = f;
+ }
+
+ public void execute() throws BuildException {
+ if (targetdir == null) {
+ throw new BuildException ("Attribute targetdir must be specified");
+ }
+
+ if (sourceJar == null) {
+ throw new BuildException ("Attribute source must be specified");
+ }
+
+
+ JarFile jar;
+
+ try {
+ jar = new JarFile (sourceJar);
+ } catch (IOException ex) {
+ throw new BuildException ("Problem initializing file " + sourceJar, ex);
+ }
+
+ //
+ // Initialize the method
+ //
+
+ log ("Initilalizing patching " + patchClass + '.' + patchMethod);
+
+ ClassLoader cl;
+ if (patchLibrary == null) {
+ log ("Loading patch class from task loader");
+ cl = getClass ().getClassLoader();
+ } else {
+ try {
+ cl = new URLClassLoader (new URL[] { patchLibrary.toURL() });
+ } catch (java.net.MalformedURLException ex) {
+ throw new BuildException (ex);
+ }
+ }
+
+ java.lang.reflect.Method m;
+ try {
+ Class c = cl.loadClass (patchClass);
+ m = c.getMethod(patchMethod, new Class[] { byte[].class });
+ if (m.getReturnType() != byte[].class) {
+ throw new BuildException ("Method does not return byte[]: " + m);
+ }
+ } catch (Exception ex) {
+ throw new BuildException ("Cannot initialize class " + patchClass + " and method " + patchMethod, ex);
+ }
+
+ /*
+ try {
+ log ("Testing method " + m);
+ byte[] res = (byte[])m.invoke (null, new Object[] { new byte[0], "someString" });
+ } catch (Exception ex) {
+ throw new BuildException ("Exception during test invocation of the method", ex);
+ }
+ */
+
+ //
+ // Ok we have the method and we can do the patching
+ //
+
+ java.util.Enumeration it = jar.entries();
+ while (it.hasMoreElements()) {
+ java.util.jar.JarEntry e = (java.util.jar.JarEntry)it.nextElement ();
+
+ int size = (int)e.getSize();
+ if (size <= 4) {
+ // not interesting entry
+ continue;
+ }
+
+ byte[] arr = new byte[size];
+
+ try {
+ java.io.InputStream is = jar.getInputStream(e);
+
+ int indx = 0;
+ while (indx < arr.length) {
+ int read = is.read (arr, indx, arr.length - indx);
+ if (read == -1) {
+ throw new BuildException("Entry: " + e.getName () + " size should be: " + size + " but was read just: " + indx);
+ }
+ indx += read;
+ }
+ } catch (IOException ex) {
+ throw new BuildException (ex);
+ }
+
+ byte[] original = (byte[])arr.clone ();
+ byte[] out;
+ try {
+ out = (byte[])m.invoke (null, new Object[] { arr });
+ } catch (java.lang.reflect.InvocationTargetException ex) {
+ throw new BuildException (ex.getTargetException());
+ } catch (Exception ex) {
+ throw new BuildException (ex);
+ }
+
+ if (java.util.Arrays.equals (original, out)) {
+ // no patching, go on
+ continue;
+ }
+
+ File f = new File (targetdir, e.getName ().replace ('/', File.separatorChar));
+ if (f.exists () && f.lastModified() > sourceJar.lastModified ()) {
+ // if the file is newer
+ continue;
+ }
+
+ f.getParentFile().mkdirs();
+
+ log ("Writing patched file " + f);
+
+ try {
+ FileOutputStream os = new FileOutputStream (f);
+ os.write (out);
+ os.close ();
+ } catch (IOException ex) {
+ throw new BuildException ("Cannot write file " + f, ex);
+ }
+ }
+ }
+
+}
Index: nb_all/nbbuild/antsrc/org/netbeans/nbbuild/NbPatchSuperClass.java
===================================================================
RCS file: nb_all/nbbuild/antsrc/org/netbeans/nbbuild/NbPatchSuperClass.java
diff -N nb_all/nbbuild/antsrc/org/netbeans/nbbuild/NbPatchSuperClass.java
--- nb_all/nbbuild/antsrc/org/netbeans/nbbuild/NbPatchSuperClass.java 19 Jul 2002 07:27:45 -0000 1.2
+++ /dev/null 1 Jan 1970 00:00:00 -0000
@@ -1,186 +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.nbbuild;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.net.URL;
-import java.net.URLClassLoader;
-import java.util.ArrayList;
-import java.util.Iterator;
-// IMPORTANT! You may need to mount ant.jar before this class will
-// compile. So mount the JAR modules/ext/ant-1.4.1.jar (NOT modules/ant.jar)
-// from your IDE installation directory in your Filesystems before
-// continuing to ensure that it is in your classpath.
-
-import org.apache.tools.ant.*;
-import org.apache.tools.ant.types.*;
-
-/**
- * @author Jaroslav Tulach
- */
-public class NbPatchSuperClass extends Task {
- /* Path to library containing the patch method */
- private File patchLibrary;
- public void setPatchLibrary (File f) {
- patchLibrary = f;
- }
-
- /* Name of class with patch method */
- private String patchClass = "org.netbeans.PatchByteCode";
- public void setPatchClass (String f) {
- patchClass = f;
- }
-
- /** Name of the method to call. Must have byte[] array argument and return the same
- */
- private String patchMethod = "enhance";
- public void setPatchMethod (String f) {
- patchMethod = f;
- }
-
- /* Base dir to find classes relative to */
- private File basedir;
- public void setBasedir (File f) {
- basedir = f;
- }
-
- /* The class to change its super class and the value of the super class.
- */
- public static class Patch {
- String clazz;
- String nbSuperClass;
- /** Class in form of java/lang/Object */
- public void setClass (String s) {
- clazz = s;
- }
- /** Class in form of java/lang/Object */
- public void setSuper (String s) {
- nbSuperClass = s;
- }
- }
- private ArrayList patches = new ArrayList (); // List
- public Patch createPatch () {
- Patch n = new Patch ();
- patches.add(n);
- return n;
- }
-
-
- public void execute() throws BuildException {
- if (basedir == null) {
- throw new BuildException ("Attribute basedir must be specified");
- }
-
- if (patches.isEmpty()) {
- // no work
- return;
- }
-
- //
- // Initialize the method
- //
-
- log ("Initilalizing patching " + patchClass + '.' + patchMethod);
-
- ClassLoader cl;
- if (patchLibrary == null) {
- log ("Loading patch class from task loader");
- cl = getClass ().getClassLoader();
- } else {
- try {
- cl = new URLClassLoader (new URL[] { patchLibrary.toURL() });
- } catch (java.net.MalformedURLException ex) {
- throw new BuildException (ex);
- }
- }
-
- java.lang.reflect.Method m;
- try {
- Class c = cl.loadClass (patchClass);
- m = c.getMethod(patchMethod, new Class[] { byte[].class, String.class });
- if (m.getReturnType() != byte[].class) {
- throw new BuildException ("Method does not return byte[]: " + m);
- }
- } catch (Exception ex) {
- throw new BuildException ("Cannot initialize class " + patchClass + " and method " + patchMethod, ex);
- }
-
- /*
- try {
- log ("Testing method " + m);
- byte[] res = (byte[])m.invoke (null, new Object[] { new byte[0], "someString" });
- } catch (Exception ex) {
- throw new BuildException ("Exception during test invocation of the method", ex);
- }
- */
-
- //
- // Ok we have the method and we can do the patching
- //
-
- Iterator it = patches.iterator();
- while (it.hasNext()) {
- Patch p = (Patch)it.next ();
-
- if (p.clazz == null) {
- throw new BuildException ("Attribute class must be specified");
- }
-
- File f = new File (basedir, p.clazz + ".class");
- if (!f.exists ()) {
- throw new BuildException ("File " + f + " for class " + p.clazz + " does not exists");
- }
-
- byte[] arr = new byte[(int)f.length()];
- try {
- FileInputStream is = new FileInputStream (f);
- if (arr.length != is.read (arr)) {
- throw new BuildException ("Not all bytes read");
- }
- is.close ();
- } catch (IOException ex) {
- throw new BuildException ("Cannot read file " + f, ex);
- }
-
- byte[] out;
- try {
- out = (byte[])m.invoke (null, new Object[] { arr, p.nbSuperClass });
- if (out == null) {
- // no patching needed
- continue;
- }
- } catch (Exception ex) {
- throw new BuildException (ex);
- }
-
- if (p.nbSuperClass != null) {
- log ("Patch applied to " + f + " to have alternate superclass " + p.nbSuperClass + " and be public");
- } else {
- log ("Patch applied to " + f + " to be public");
- }
-
- try {
- FileOutputStream os = new FileOutputStream (f);
- os.write (out);
- os.close ();
- } catch (IOException ex) {
- throw new BuildException ("Cannot overwrite file " + f, ex);
- }
- }
- }
-
-}
Index: nb_all/nbbuild/antsrc/org/netbeans/nbbuild/Postprocess.java
===================================================================
RCS file: /cvs/nbbuild/antsrc/org/netbeans/nbbuild/Postprocess.java,v
retrieving revision 1.3
diff -u -r1.3 Postprocess.java
--- 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 13 Aug 2002 23:38:19 -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
retrieving revision 1.4
diff -u -r1.4 Preprocess.java
--- 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 13 Aug 2002 23:38:19 -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
retrieving revision 1.18
diff -u -r1.18 .cvsignore
--- nb_all/openide/.cvsignore 24 Jun 2002 18:48:31 -0000 1.18
+++ nb_all/openide/.cvsignore 13 Aug 2002 23:38:19 -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
retrieving revision 1.107
diff -u -r1.107 build.xml
--- nb_all/openide/build.xml 6 Aug 2002 17:15:16 -0000 1.107
+++ nb_all/openide/build.xml 13 Aug 2002 23:38:19 -0000
@@ -27,8 +27,7 @@
-
-
+
@@ -84,52 +83,124 @@
-->