Index: core/src/org/netbeans/core/NbTopManager.java
===================================================================
RCS file: /cvs/core/src/org/netbeans/core/NbTopManager.java,v
--- core/src/org/netbeans/core/NbTopManager.java 29 Jan 2002 15:45:07 -0000 1.132
+++ core/src/org/netbeans/core/NbTopManager.java 30 Jan 2002 10:01:38 -0000
@@ -18,6 +18,7 @@
import java.awt.event.*;
import java.beans.*;
import java.io.*;
+import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.Enumeration;
@@ -40,6 +41,8 @@
import org.openide.filesystems.*;
import org.openide.filesystems.FileSystem;
import org.openide.filesystems.JarFileSystem;
+import org.openide.modules.Dependency;
+import org.openide.modules.SpecificationVersion;
import org.openide.options.ControlPanel;
import org.openide.windows.WindowManager;
import org.openide.windows.OutputWriter;
@@ -59,13 +62,12 @@
import org.netbeans.core.windows.WindowManagerImpl;
import org.netbeans.core.compiler.CompilationEngineImpl;
import org.netbeans.core.perftool.StartLog;
+import org.netbeans.core.modules.ModuleManager;
import org.netbeans.core.modules.ModuleSystem;
-import org.openide.modules.Dependency;
-import org.openide.modules.SpecificationVersion;
/** This class is a TopManager for Corona environment.
*
-* @author Ales Novak, Jaroslav Tulach, Ian Formanek, Petr Hamernik, Jan Jancura
+* @author Ales Novak, Jaroslav Tulach, Ian Formanek, Petr Hamernik, Jan Jancura, Jesse Glick
*/
public abstract class NbTopManager extends TopManager {
/* masks to define the interactivity level */
@@ -150,7 +152,7 @@
// Set up module-versioning properties, which logger prints.
Package p = Package.getPackage ("org.openide"); // NOI18N
- putSystemProperty ("org.openide.specification.version", p.getSpecificationVersion (), "1.1.6"); // NOI18N
+ putSystemProperty ("org.openide.specification.version", p.getSpecificationVersion (), "2.3"); // NOI18N
putSystemProperty ("org.openide.version", p.getImplementationVersion (), "OwnBuild"); // NOI18N
putSystemProperty ("org.openide.major.version", p.getSpecificationTitle (), "IDE/1"); // NOI18N
putSystemProperty ("netbeans.buildnumber", p.getImplementationVersion (), "OwnBuild"); // NOI18N
@@ -938,18 +940,40 @@
public Lkp () {
super (new Lookup[] {
new org.netbeans.core.lookup.TMLookup(),
+ // #14722: pay attention also to META-INF/services/class.Name resources:
+ createMetaInfServicesLookup(false),
createInitialErrorManagerLookup(),
});
//System.err.println("creating default lookup");
}
+ /** @param modules if true, use module classloader, else not */
+ private static Lookup createMetaInfServicesLookup(boolean modules) {
+ try {
+ Class clazz = Class.forName("org.openide.util.MetaInfServicesLookup"); // NOI18N
+ Constructor c = clazz.getDeclaredConstructor(new Class[] {ClassLoader.class});
+ c.setAccessible(true);
+ ClassLoader loader;
+ if (modules) {
+ loader = get().getModuleSystem().getManager().getClassLoader();
+ } else {
+ loader = Lkp.class.getClassLoader();
+ }
+ return (Lookup)c.newInstance(new Object[] {loader});
+ } catch (Exception e) {
+ e.printStackTrace();
+ return Lookup.EMPTY;
+ }
+ }
+
private static Lookup createInitialErrorManagerLookup() {
InstanceContent c = new InstanceContent();
- c.add(Boolean.TRUE, new InitialErrorManagerConvertor());
+ c.add(Boolean.TRUE, new ConvertorListener());
return new AbstractLookup(c);
}
- private static final class InitialErrorManagerConvertor implements InstanceContent.Convertor, TaskListener {
+ private static final class ConvertorListener
+ implements InstanceContent.Convertor, TaskListener, PropertyChangeListener {
public Object convert(Object obj) {
//System.err.println("IEMC.convert");
return getDefaultErrorManager();
@@ -972,19 +996,34 @@
if (lookup instanceof Lkp) {
Lkp lkp = (Lkp)lookup;
Lookup[] old = lkp.getLookups();
- if (old.length != 5) throw new IllegalStateException();
+ if (old.length != 6) throw new IllegalStateException();
Lookup[] nue = new Lookup[] {
old[0], // TMLookup
+ old[1], // metaInfServicesLookup
// do NOT include initialErrorManagerLookup; this is now replaced by the layer entry
// Services/Hidden/org-netbeans-core-default-error-manager.instance
- old[2], // NbTM.instanceLookup
- old[3], // FolderLookup
- old[4], // moduleLookup
+ old[3], // NbTM.instanceLookup
+ old[4], // FolderLookup
+ old[5], // moduleLookup
};
lkp.setLookups(nue);
}
}
-
+ public void propertyChange(PropertyChangeEvent evt) {
+ //System.err.println("modules changed; changing metaInfServicesLookup");
+ if (ModuleManager.PROP_ENABLED_MODULES.equals(evt.getPropertyName())) {
+ // Time to refresh META-INF/services/ lookup; modules turned on or off.
+ Lookup lookup = Lookup.getDefault();
+ if (lookup instanceof Lkp) {
+ Lkp lkp = (Lkp)lookup;
+ Lookup[] old = lkp.getLookups();
+ Lookup[] nue = (Lookup[])old.clone();
+ nue[1] = createMetaInfServicesLookup(true);
+ lkp.setLookups(nue);
+ //System.err.println("lookups: " + java.util.Arrays.asList(arr));
+ }
+ }
+ }
}
/** When all module classes are accessible thru systemClassLoader, this
@@ -1011,15 +1050,17 @@
StartLog.logProgress ("Got Services folder"); // NOI18N
FolderLookup folder = new FolderLookup (df, "SL["); // NOI18N
- folder.addTaskListener(new InitialErrorManagerConvertor());
+ folder.addTaskListener(new ConvertorListener());
StartLog.logProgress ("created FolderLookup"); // NOI18N
// extend the lookup
Lookup[] arr = new Lookup[] {
- lkp.getLookups ()[0],
+ lkp.getLookups ()[0], // TMLookup
+ // replace metaInfServicesLookup with a new one from modules
+ createMetaInfServicesLookup(true),
// Include initialErrorManagerLookup provisionally, until the folder lookup
// is actually ready and usable
- lkp.getLookups()[1],
+ lkp.getLookups()[2], // initialErrorManagerLookup
NbTopManager.get ().getInstanceLookup (),
folder.getLookup (),
NbTopManager.get().getModuleSystem().getManager().getModuleLookup(),
@@ -1028,6 +1069,9 @@
lkp.setLookups (arr);
StartLog.logProgress ("Lookups set"); // NOI18N
+
+ // Also listen for changes in modules, as META-INF/services/ would change:
+ get().getModuleSystem().getManager().addPropertyChangeListener(new ConvertorListener());
} catch (java.io.IOException ex) {
ex.printStackTrace();
throw new IllegalStateException ("Cannot initialize folder Services"); // NOI18N
Index: openide/src/org/openide/util/Lookup.java
===================================================================
RCS file: /cvs/openide/src/org/openide/util/Lookup.java,v
--- openide/src/org/openide/util/Lookup.java 29 Jan 2002 11:20:23 -0000 1.12
+++ openide/src/org/openide/util/Lookup.java 30 Jan 2002 10:01:39 -0000
@@ -47,7 +47,7 @@
);
if (className == null) {
- defaultLookup = EMPTY;
+ defaultLookup = new MetaInfServicesLookup();
return defaultLookup;
}
@@ -55,7 +55,7 @@
Class c = Class.forName(className);
defaultLookup = (Lookup)c.newInstance();
} catch (Exception ex) {
- defaultLookup = EMPTY;
+ defaultLookup = new MetaInfServicesLookup();
ex.printStackTrace();
throw new InternalError ();
}
Index: openide/src/org/openide/util/MetaInfServicesLookup.java
===================================================================
RCS file: openide/src/org/openide/util/MetaInfServicesLookup.java
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ openide/src/org/openide/util/MetaInfServicesLookup.java 30 Jan 2002 10:01:39 -0000
@@ -0,0 +1,244 @@
+/*
+ * 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.openide.util;
+
+import java.net.URL;
+import java.util.*;
+import java.io.*;
+
+import org.openide.ErrorManager;
+import org.openide.util.lookup.*;
+
+/** A lookup that implements the JDK1.3 JAR services mechanism and delegates
+ * to META-INF/services/name.of.class files.
+ *
It is not dynamic - so if you need to change the classloader or JARs,
+ * wrap it in a ProxyLookup and change the delegate when necessary.
+ * Existing instances will be kept if the implementation classes are unchanged,
+ * so there is "stability" in doing this provided some parent loaders are the same
+ * as the previous ones.
+ *
If this is to be made public, please move it to the org.openide.util.lookup
+ * package; currently used by the core via reflection, until it is needed some
+ * other way.
+ * @author Jaroslav Tulach, Jesse Glick
+ * @see #14722
+ */
+final class MetaInfServicesLookup extends AbstractLookup {
+
+ private static final Map knownInstances = new WeakHashMap(); // Map
+
+ /** A set of all requested classes.
+ * Note that classes that we actually succeeded on can never be removed
+ * from here because we hold a strong reference to the loader.
+ * However we also hold classes which are definitely not loadable by
+ * our loader.
+ */
+ private final Set classes = new WeakSet(); // Set
+ /** class loader to use */
+ private final ClassLoader loader;
+
+ /** Create a lookup reading from the classpath.
+ * That is, the same classloader as this class itself.
+ */
+ public MetaInfServicesLookup() {
+ this(MetaInfServicesLookup.class.getClassLoader());
+ }
+
+ /** Create a lookup reading from a specified classloader.
+ */
+ public MetaInfServicesLookup(ClassLoader loader) {
+ this.loader = loader;
+ }
+
+
+ /* Tries to load appropriate resources from manifest files.
+ */
+ protected synchronized final void beforeLookup(Lookup.Template t) {
+ Class c = t.getType();
+ if (classes.add(c)) {
+ // Added new class, search for it.
+ List arr = new ArrayList();
+ search(c, arr);
+ Iterator it = arr.iterator();
+ while (it.hasNext()) {
+ Pair p = (Pair)it.next();
+ addPair(p);
+ }
+ }
+ }
+
+ /** Finds all pairs and adds them to the collection.
+ *
+ * @param clazz class to find
+ * @param result collection to add Pair to
+ */
+ private void search(Class clazz, Collection result) {
+ String res = "META-INF/services/" + clazz.getName(); // NOI18N
+ Enumeration en;
+ try {
+ en = loader.getResources(res);
+ } catch (IOException ioe) {
+ ErrorManager.getDefault().notify(ioe);
+ return;
+ }
+
+ // Do not create multiple instances in case more than one JAR
+ // has the same entry in it (and they load to the same class).
+ // Probably would not happen, assuming JARs only list classes
+ // they own, but just in case...
+ Set foundClasses = new HashSet(); // Set
+
+ boolean foundOne = false;
+ while (en.hasMoreElements()) {
+
+ if (!foundOne) {
+ foundOne = true;
+ // Double-check that in fact we can load the *interface* class.
+ // For example, say class I is defined in two JARs, J1 and J2.
+ // There is also an implementation M1 defined in J1, and another
+ // implementation M2 defined in J2.
+ // Classloaders C1 and C2 are made from J1 and J2.
+ // A MetaInfServicesLookup is made from C1. Then the user asks to
+ // lookup I as loaded from C2. J1 has the services line and lists
+ // M1, and we can in fact make it. However it is not of the desired
+ // type to be looked up. Don't do this check, which could be expensive,
+ // unless we expect to be getting some results, however.
+ Class realMcCoy = null;
+ try {
+ realMcCoy = loader.loadClass(clazz.getName());
+ } catch (ClassNotFoundException cnfe) {
+ // our loader does not know about it, OK
+ }
+ if (realMcCoy != clazz) {
+ // Either the interface class is not available at all in our loader,
+ // or it is not the same version as we expected. Don't provide results.
+ return;
+ }
+ }
+
+ URL url = (URL)en.nextElement();
+ try {
+ InputStream is = url.openStream();
+ try {
+ BufferedReader reader = new BufferedReader(new InputStreamReader(is, "UTF-8")); // NOI18N
+ while (true) {
+ String line = reader.readLine();
+ if (line == null) break;
+
+ // Ignore blank lines and comments.
+ line = line.trim();
+ if (line.length() == 0) continue;
+ if (line.charAt(0) == '#') continue; // NOI18N
+
+ // Most lines are fully-qualified class names.
+ Class inst = Class.forName(line, false, loader);
+ if (!clazz.isAssignableFrom(inst)) {
+ throw new ClassNotFoundException(inst.getName() + " not a subclass of " + clazz.getName()); // NOI18N
+ }
+ if (foundClasses.add(inst)) {
+ result.add(new P(inst));
+ }
+ }
+ } finally {
+ is.close();
+ }
+ } catch (ClassNotFoundException ex) {
+ ErrorManager.getDefault().notify(ex);
+ } catch (IOException ex) {
+ ErrorManager.getDefault().notify(ex);
+ }
+ }
+ }
+
+
+ /** Pair that holds name of a class and maybe the instance.
+ */
+ private static final class P extends Pair {
+ /** May be one of three things:
+ * 1. The implementation class which was named in the services file.
+ * 2. An instance of it.
+ * 3. Null, if creation of the instance resulted in an error.
+ */
+ private Object object;
+
+ public P(Class clazz) {
+ this.object = clazz;
+ }
+
+ /** Finds the class.
+ */
+ private Class clazz() {
+ Object o = object;
+ if (o instanceof Class) {
+ return (Class)o;
+ } else if (o != null) {
+ return o.getClass();
+ } else {
+ // Broken.
+ return Object.class;
+ }
+ }
+
+ public boolean equals(Object o) {
+ if (o instanceof P) {
+ return ((P)o).clazz().equals(clazz());
+ }
+ return false;
+ }
+
+ public int hashCode() {
+ return clazz().hashCode();
+ }
+
+ protected boolean instanceOf(Class c) {
+ return c.isAssignableFrom(clazz());
+ }
+
+ public Class getType() {
+ return clazz();
+ }
+
+ public Object getInstance() {
+ synchronized (knownInstances) {
+ if (object instanceof Class) {
+ try {
+ Class c = ((Class)object);
+ object = knownInstances.get(c);
+ if (object == null) {
+ object = c.newInstance();
+ knownInstances.put(c, object);
+ }
+ } catch (Exception ex) {
+ ErrorManager.getDefault().notify(ex);
+ object = null;
+ }
+ }
+ return object;
+ }
+ }
+
+ public String getDisplayName() {
+ return clazz().getName();
+ }
+
+ public String getId() {
+ return clazz().getName();
+ }
+
+ protected boolean creatorOf(Object obj) {
+ return obj == object;
+ }
+
+ }
+
+}
Index: core/test/unit/src/org/netbeans/core/lookup/MetaInfServicesTest.java
===================================================================
RCS file: core/test/unit/src/org/netbeans/core/lookup/MetaInfServicesTest.java
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ core/test/unit/src/org/netbeans/core/lookup/MetaInfServicesTest.java 30 Jan 2002 10:01:40 -0000
@@ -0,0 +1,180 @@
+/*
+ * 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.lookup;
+
+import org.netbeans.junit.*;
+import junit.textui.TestRunner;
+import org.netbeans.core.NbTopManager;
+import org.netbeans.core.modules.*;
+import org.openide.TopManager;
+import org.openide.util.*;
+import java.io.File;
+import java.util.*;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.SAXParserFactory;
+
+/** Test whether modules can really register things in their META-INF/services/class.Name
+ * files, and whether this behaves correctly when the modules are disabled/enabled.
+ * Note that Plain loads its classpath modules as soon as you ask for it, so these
+ * tests do not check what happens on the NetBeans startup classpath.
+ * @author Jesse Glick
+ */
+public class MetaInfServicesTest extends NbTestCase {
+
+ public MetaInfServicesTest(String name) {
+ super(name);
+ }
+
+ public static void main(String[] args) throws Exception {
+ File temp = File.createTempFile("MetaInfServicesTest", ".dummy");
+ System.setProperty("nbjunit.workdir", new File(temp.getParentFile(), temp.getName().substring(0, temp.getName().length() - 6)).getAbsolutePath());
+ TestRunner.run(new NbTestSuite(MetaInfServicesTest.class));
+ }
+
+ private ModuleManager mgr;
+ private Module m1, m2;
+ protected void setUp() throws Exception {
+ //System.err.println("setUp");
+ //Thread.dumpStack();
+ clearWorkDir();
+ // Load Plain.
+ TopManager.getDefault();
+ // Make a couple of modules.
+ mgr = NbTopManager.get().getModuleSystem().getManager();
+ try {
+ mgr.mutex().writeAccess(new Mutex.ExceptionAction() {
+ public Object run() throws Exception {
+ File jar1 = new File(MetaInfServicesTest.class.getResource("data/services-jar-1.jar").getPath());
+ File jar2 = new File(MetaInfServicesTest.class.getResource("data/services-jar-2.jar").getPath());
+ m1 = mgr.create(jar1, new ModuleHistory(), false, false, false);
+ m2 = mgr.create(jar2, new ModuleHistory(), false, false, false);
+ return null;
+ }
+ });
+ } catch (MutexException me) {
+ throw me.getException();
+ }
+ assertEquals(Collections.EMPTY_SET, m1.getProblems());
+ }
+ protected void tearDown() throws Exception {
+ try {
+ mgr.mutex().writeAccess(new Mutex.ExceptionAction() {
+ public Object run() throws Exception {
+ if (m2.isEnabled()) mgr.disable(m2);
+ mgr.delete(m2);
+ if (m1.isEnabled()) mgr.disable(m1);
+ mgr.delete(m1);
+ return null;
+ }
+ });
+ } catch (MutexException me) {
+ throw me.getException();
+ }
+ m1 = null;
+ m2 = null;
+ mgr = null;
+ }
+ protected static final int TWIDDLE_ENABLE = 0;
+ protected static final int TWIDDLE_DISABLE = 1;
+ protected void twiddle(final Module m, final int action) throws Exception {
+ try {
+ mgr.mutex().writeAccess(new Mutex.ExceptionAction() {
+ public Object run() throws Exception {
+ switch (action) {
+ case TWIDDLE_ENABLE:
+ mgr.enable(m);
+ break;
+ case TWIDDLE_DISABLE:
+ mgr.disable(m);
+ break;
+ default:
+ throw new IllegalArgumentException("bad action: " + action);
+ }
+ return null;
+ }
+ });
+ } catch (MutexException me) {
+ throw me.getException();
+ }
+ }
+
+ /** Fails to work if you have >1 method per class, because setUp gets run more
+ * than once (XTest bug I suppose).
+ */
+ public void testEverything() throws Exception {
+ twiddle(m1, TWIDDLE_ENABLE);
+ Class xface = TopManager.getDefault().systemClassLoader().loadClass("org.foo.Interface");
+ Lookup.Result r = Lookup.getDefault().lookup(new Lookup.Template(xface));
+ List instances = new ArrayList(r.allInstances());
+ // Expect to get Impl1 from first JAR.
+ assertEquals(1, instances.size());
+ Object instance1 = instances.get(0);
+ assertTrue(xface.isInstance(instance1));
+ assertEquals("org.foo.impl.Implementation1", instance1.getClass().getName());
+ // Expect to have (same) Impl1 + Impl2.
+ LookupL l = new LookupL();
+ r.addLookupListener(l);
+ twiddle(m2, TWIDDLE_ENABLE);
+ assert(l.gotSomething());
+ instances = new ArrayList(r.allInstances());
+ assertEquals(2, instances.size());
+ assertEquals(instance1, instances.get(0));
+ assertEquals("org.bar.Implementation2", instances.get(1).getClass().getName());
+ // Expect to lose Impl2.
+ l.count = 0;
+ twiddle(m2, TWIDDLE_DISABLE);
+ assert(l.gotSomething());
+ instances = new ArrayList(r.allInstances());
+ assertEquals(1, instances.size());
+ assertEquals(instance1, instances.get(0));
+ // Expect to lose Impl1 too.
+ l.count = 0;
+ twiddle(m1, TWIDDLE_DISABLE);
+ assert(l.gotSomething());
+ instances = new ArrayList(r.allInstances());
+ assertEquals(0, instances.size());
+ // Expect to not get anything: wrong xface version
+ l.count = 0;
+ twiddle(m1, TWIDDLE_ENABLE);
+ // not really important: assert(!l.gotSomething());
+ instances = new ArrayList(r.allInstances());
+ assertEquals(0, instances.size());
+ Class xface2 = TopManager.getDefault().systemClassLoader().loadClass("org.foo.Interface");
+ assertTrue(xface != xface2);
+ Lookup.Result r2 = Lookup.getDefault().lookup(new Lookup.Template(xface2));
+ instances = new ArrayList(r2.allInstances());
+ assertEquals(1, instances.size());
+ // Let's also check up on XML providers, which ought to be in the classpath.
+ DocumentBuilderFactory dbf = (DocumentBuilderFactory)Lookup.getDefault().lookup(DocumentBuilderFactory.class);
+ assertNotNull(dbf);
+ SAXParserFactory spf = (SAXParserFactory)Lookup.getDefault().lookup(SAXParserFactory.class);
+ assertNotNull(spf);
+ //System.err.println("dbf=" + dbf + " spf=" + spf);
+ //System.err.println("all DBFs=" + Lookup.getDefault().lookup(new Lookup.Template(DocumentBuilderFactory.class)).allInstances());
+ }
+
+ protected static final class LookupL implements LookupListener {
+ public int count = 0;
+ public synchronized void resultChanged(LookupEvent ev) {
+ count++;
+ notifyAll();
+ }
+ public synchronized boolean gotSomething() throws InterruptedException {
+ if (count > 0) return true;
+ wait(9999);
+ return count > 0;
+ }
+ }
+
+}
Index: core/test/unit/src/org/netbeans/core/lookup/data/build.xml
===================================================================
RCS file: /cvs/core/test/unit/src/org/netbeans/core/lookup/data/build.xml,v
--- core/test/unit/src/org/netbeans/core/lookup/data/build.xml 22 Oct 2001 19:47:17 -0000 1.2
+++ core/test/unit/src/org/netbeans/core/lookup/data/build.xml 30 Jan 2002 10:01:40 -0000
@@ -8,12 +8,12 @@
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
+Code is Sun Microsystems, Inc. Portions Copyright 1997-2002 Sun
Microsystems, Inc. All Rights Reserved.
-->
-
+
@@ -28,11 +28,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
Index: core/test/unit/src/org/netbeans/core/lookup/data/services-jar-1.mf
===================================================================
RCS file: core/test/unit/src/org/netbeans/core/lookup/data/services-jar-1.mf
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ core/test/unit/src/org/netbeans/core/lookup/data/services-jar-1.mf 30 Jan 2002 10:01:40 -0000
@@ -0,0 +1,5 @@
+Manifest-Version: 1.0
+OpenIDE-Module: org.foo
+OpenIDE-Module-Name: Interface & Impl #1
+OpenIDE-Module-IDE-Dependencies: IDE/1 > 2.2
+
Index: core/test/unit/src/org/netbeans/core/lookup/data/services-jar-2.mf
===================================================================
RCS file: core/test/unit/src/org/netbeans/core/lookup/data/services-jar-2.mf
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ core/test/unit/src/org/netbeans/core/lookup/data/services-jar-2.mf 30 Jan 2002 10:01:40 -0000
@@ -0,0 +1,6 @@
+Manifest-Version: 1.0
+OpenIDE-Module: org.bar
+OpenIDE-Module-Name: Impl #2
+OpenIDE-Module-Module-Dependencies: org.foo
+OpenIDE-Module-IDE-Dependencies: IDE/1 > 2.2
+
Index: core/test/unit/src/org/netbeans/core/lookup/data/services-jar-1/META-INF/services/org.foo.Interface
===================================================================
RCS file: core/test/unit/src/org/netbeans/core/lookup/data/services-jar-1/META-INF/services/org.foo.Interface
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ core/test/unit/src/org/netbeans/core/lookup/data/services-jar-1/META-INF/services/org.foo.Interface 30 Jan 2002 10:01:40 -0000
@@ -0,0 +1,4 @@
+# Some header info, maybe.
+
+# Our first impl here:
+org.foo.impl.Implementation1
Index: core/test/unit/src/org/netbeans/core/lookup/data/services-jar-1/org/foo/Interface.java
===================================================================
RCS file: core/test/unit/src/org/netbeans/core/lookup/data/services-jar-1/org/foo/Interface.java
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ core/test/unit/src/org/netbeans/core/lookup/data/services-jar-1/org/foo/Interface.java 30 Jan 2002 10:01:40 -0000
@@ -0,0 +1,2 @@
+package org.foo;
+public interface Interface {}
Index: core/test/unit/src/org/netbeans/core/lookup/data/services-jar-1/org/foo/impl/Implementation1.java
===================================================================
RCS file: core/test/unit/src/org/netbeans/core/lookup/data/services-jar-1/org/foo/impl/Implementation1.java
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ core/test/unit/src/org/netbeans/core/lookup/data/services-jar-1/org/foo/impl/Implementation1.java 30 Jan 2002 10:01:40 -0000
@@ -0,0 +1,3 @@
+package org.foo.impl;
+import org.foo.Interface;
+public class Implementation1 implements Interface {}
Index: core/test/unit/src/org/netbeans/core/lookup/data/services-jar-2/META-INF/services/org.foo.Interface
===================================================================
RCS file: core/test/unit/src/org/netbeans/core/lookup/data/services-jar-2/META-INF/services/org.foo.Interface
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ core/test/unit/src/org/netbeans/core/lookup/data/services-jar-2/META-INF/services/org.foo.Interface 30 Jan 2002 10:01:40 -0000
@@ -0,0 +1, @@
+org.bar.Implementation2
Index: core/test/unit/src/org/netbeans/core/lookup/data/services-jar-2/org/bar/Implementation2.java
===================================================================
RCS file: core/test/unit/src/org/netbeans/core/lookup/data/services-jar-2/org/bar/Implementation2.java
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ core/test/unit/src/org/netbeans/core/lookup/data/services-jar-2/org/bar/Implementation2.java 30 Jan 2002 10:01:40 -0000
@@ -0,0 +1,3 @@
+package org.bar;
+import org.foo.Interface;
+public class Implementation2 implements Interface {}
Index: openide/test/build.xml
===================================================================
RCS file: /cvs/openide/test/build.xml,v
--- openide/test/build.xml 24 Jan 2002 17:37:44 -0000 1.33
+++ openide/test/build.xml 30 Jan 2002 10:01:40 -0000
@@ -55,6 +55,7 @@
+
@@ -77,6 +78,8 @@
+
+
Index: openide/test/unit/src/org/openide/util/MetaInfServicesLookupTest.java
===================================================================
RCS file: openide/test/unit/src/org/openide/util/MetaInfServicesLookupTest.java
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ openide/test/unit/src/org/openide/util/MetaInfServicesLookupTest.java 30 Jan 2002 10:01:41 -0000
@@ -0,0 +1,92 @@
+/*
+ * 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.openide.util;
+
+import org.netbeans.junit.*;
+import junit.textui.TestRunner;
+import java.net.*;
+import java.util.*;
+
+/** Test finding services from manifest.
+ * @author Jesse Glick
+ */
+public class MetaInfServicesLookupTest extends NbTestCase {
+
+ public MetaInfServicesLookupTest(String name) {
+ super(name);
+ }
+
+ public static void main(String[] args) {
+ TestRunner.run(new NbTestSuite(MetaInfServicesLookupTest.class));
+ }
+
+ ClassLoader c1, c2, c2a, c3, c4;
+
+ protected void setUp() throws Exception {
+ c1 = new URLClassLoader(new URL[] {
+ MetaInfServicesLookupTest.class.getResource("data/services-jar-1.jar"),
+ });
+ c2 = new URLClassLoader(new URL[] {
+ MetaInfServicesLookupTest.class.getResource("data/services-jar-2.jar"),
+ }, c1);
+ c2a = new URLClassLoader(new URL[] {
+ MetaInfServicesLookupTest.class.getResource("data/services-jar-2.jar"),
+ }, c1);
+ c3 = new URLClassLoader(new URL[] {
+ MetaInfServicesLookupTest.class.getResource("data/services-jar-2.jar"),
+ });
+ c4 = new URLClassLoader(new URL[] {
+ MetaInfServicesLookupTest.class.getResource("data/services-jar-1.jar"),
+ MetaInfServicesLookupTest.class.getResource("data/services-jar-2.jar"),
+ });
+ }
+
+ public void testBasicUsage() throws Exception {
+ Lookup l = new MetaInfServicesLookup(c2);
+ Class xface = c1.loadClass("org.foo.Interface");
+ List results = new ArrayList(l.lookup(new Lookup.Template(xface)).allInstances());
+ assertEquals(2, results.size());
+ // Note that they have to be in order:
+ assertEquals("org.foo.impl.Implementation1", results.get(0).getClass().getName());
+ assertEquals("org.bar.Implementation2", results.get(1).getClass().getName());
+ // Make sure it does not gratuitously replace items:
+ List results2 = new ArrayList(l.lookup(new Lookup.Template(xface)).allInstances());
+ assertEquals(results, results2);
+ }
+
+ public void testLoaderSkew() throws Exception {
+ Class xface1 = c1.loadClass("org.foo.Interface");
+ Lookup l3 = new MetaInfServicesLookup(c3);
+ // If we cannot load Interface, there should be no impls of course... quietly!
+ assertEquals(Collections.EMPTY_LIST,
+ new ArrayList(l3.lookup(new Lookup.Template(xface1)).allInstances()));
+ Lookup l4 = new MetaInfServicesLookup(c4);
+ // If we can load Interface but it is the wrong one, ignore it.
+ assertEquals(Collections.EMPTY_LIST,
+ new ArrayList(l4.lookup(new Lookup.Template(xface1)).allInstances()));
+ // Make sure l4 is really OK - it can load from its own JARs.
+ Class xface4 = c4.loadClass("org.foo.Interface");
+ assertEquals(2, l4.lookup(new Lookup.Template(xface4)).allInstances().size());
+ }
+
+ public void testStability() throws Exception {
+ Lookup l = new MetaInfServicesLookup(c2);
+ Class xface = c1.loadClass("org.foo.Interface");
+ Object first = l.lookup(new Lookup.Template(xface)).allInstances().iterator().next();
+ l = new MetaInfServicesLookup(c2a);
+ Object second = l.lookup(new Lookup.Template(xface)).allInstances().iterator().next();
+ assertEquals(first, second);
+ }
+
+}
Index: openide/test/unit/src/org/openide/util/data/.cvsignore
===================================================================
RCS file: openide/test/unit/src/org/openide/util/data/.cvsignore
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ openide/test/unit/src/org/openide/util/data/.cvsignore 30 Jan 2002 10:01:41 -0000
@@ -0,0 +1, @@
+*.jar
Index: openide/test/unit/src/org/openide/util/data/build.xml
===================================================================
RCS file: openide/test/unit/src/org/openide/util/data/build.xml
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ openide/test/unit/src/org/openide/util/data/build.xml 30 Jan 2002 10:01:41 -0000
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Index: openide/test/unit/src/org/openide/util/data/services-jar-1/META-INF/services/org.foo.Interface
===================================================================
RCS file: openide/test/unit/src/org/openide/util/data/services-jar-1/META-INF/services/org.foo.Interface
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ openide/test/unit/src/org/openide/util/data/services-jar-1/META-INF/services/org.foo.Interface 30 Jan 2002 10:01:41 -0000
@@ -0,0 +1,4 @@
+# Some header info, maybe.
+
+# Our first impl here:
+org.foo.impl.Implementation1
Index: openide/test/unit/src/org/openide/util/data/services-jar-1/org/foo/Interface.java
===================================================================
RCS file: openide/test/unit/src/org/openide/util/data/services-jar-1/org/foo/Interface.java
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ openide/test/unit/src/org/openide/util/data/services-jar-1/org/foo/Interface.java 30 Jan 2002 10:01:41 -0000
@@ -0,0 +1,2 @@
+package org.foo;
+public interface Interface {}
Index: openide/test/unit/src/org/openide/util/data/services-jar-1/org/foo/impl/Implementation1.java
===================================================================
RCS file: openide/test/unit/src/org/openide/util/data/services-jar-1/org/foo/impl/Implementation1.java
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ openide/test/unit/src/org/openide/util/data/services-jar-1/org/foo/impl/Implementation1.java 30 Jan 2002 10:01:41 -0000
@@ -0,0 +1,3 @@
+package org.foo.impl;
+import org.foo.Interface;
+public class Implementation1 implements Interface {}
Index: openide/test/unit/src/org/openide/util/data/services-jar-2/META-INF/services/org.foo.Interface
===================================================================
RCS file: openide/test/unit/src/org/openide/util/data/services-jar-2/META-INF/services/org.foo.Interface
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ openide/test/unit/src/org/openide/util/data/services-jar-2/META-INF/services/org.foo.Interface 30 Jan 2002 10:01:41 -0000
@@ -0,0 +1, @@
+org.bar.Implementation2
Index: openide/test/unit/src/org/openide/util/data/services-jar-2/org/bar/Implementation2.java
===================================================================
RCS file: openide/test/unit/src/org/openide/util/data/services-jar-2/org/bar/Implementation2.java
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ openide/test/unit/src/org/openide/util/data/services-jar-2/org/bar/Implementation2.java 30 Jan 2002 10:01:41 -0000
@@ -0,0 +1,3 @@
+package org.bar;
+import org.foo.Interface;
+public class Implementation2 implements Interface {}