Index: openide/modules/src/org/openide/modules/Dependency.java =================================================================== RCS file: /shared/data/ccvs/repository/openide/modules/src/org/openide/modules/Dependency.java,v retrieving revision 1.5 diff -u -r1.5 Dependency.java --- openide/modules/src/org/openide/modules/Dependency.java 22 Mar 2006 06:37:52 -0000 1.5 +++ openide/modules/src/org/openide/modules/Dependency.java 28 Jun 2006 21:00:36 -0000 @@ -47,6 +47,12 @@ */ public final static int TYPE_REQUIRES = 5; + /** Dependency on a token, but without need to have token provider be initialised sooner. + * @see ModuleInfo#getProvides + * @since JST-PENDING + */ + public final static int TYPE_NEEDS = 6; + /** Comparison by specification version. */ public final static int COMPARE_SPEC = 1; @@ -310,6 +316,12 @@ } else if (type == Dependency.TYPE_REQUIRES) { if (comparison != Dependency.COMPARE_ANY) { throw new IllegalArgumentException("Cannot give a comparison for a token requires dep: " + body); // NOI18N + } + + checkCodeName(name, false); + } else if (type == Dependency.TYPE_NEEDS) { + if (comparison != Dependency.COMPARE_ANY) { + throw new IllegalArgumentException("Cannot give a comparison for a token needs dep: " + body); // NOI18N } checkCodeName(name, false); Index: core/bootstrap/src/org/netbeans/Module.java =================================================================== RCS file: /shared/data/ccvs/repository/core/bootstrap/src/org/netbeans/Module.java,v retrieving revision 1.15 diff -u -r1.15 Module.java --- core/bootstrap/src/org/netbeans/Module.java 22 Jun 2006 07:22:09 -0000 1.15 +++ core/bootstrap/src/org/netbeans/Module.java 28 Jun 2006 21:00:36 -0000 @@ -393,6 +393,7 @@ dependencies.addAll(Dependency.create(Dependency.TYPE_PACKAGE, pkgdeps)); // NOI18N } dependencies.addAll(Dependency.create(Dependency.TYPE_REQUIRES, attr.getValue("OpenIDE-Module-Requires"))); // NOI18N + dependencies.addAll(Dependency.create(Dependency.TYPE_NEEDS, attr.getValue("OpenIDE-Module-Needs"))); // NOI18N // Permit the concrete installer to make some changes: mgr.refineDependencies(this, dependencies); dependenciesA = dependencies.toArray(new Dependency[dependencies.size()]); Index: core/bootstrap/src/org/netbeans/ModuleManager.java =================================================================== RCS file: /shared/data/ccvs/repository/core/bootstrap/src/org/netbeans/ModuleManager.java,v retrieving revision 1.16 diff -u -r1.16 ModuleManager.java --- core/bootstrap/src/org/netbeans/ModuleManager.java 6 Jun 2006 14:41:18 -0000 1.16 +++ core/bootstrap/src/org/netbeans/ModuleManager.java 28 Jun 2006 21:00:37 -0000 @@ -1065,7 +1065,7 @@ if (! other.isEnabled()) { maybeAddToEnableList(willEnable, mightEnable, other, false); } - } else if (dep.getType() == Dependency.TYPE_REQUIRES) { + } else if (dep.getType() == Dependency.TYPE_REQUIRES || dep.getType() == Dependency.TYPE_NEEDS) { String token = dep.getName(); Set providers = providersOf.get(token); if (providers == null) throw new IllegalStateException("Should have found a provider of: " + token); // NOI18N @@ -1315,6 +1315,7 @@ private Set> _missingDependencies(Module probed) { Set> probs = moduleProblems.get(probed); if (probs == null) { + List> enableAtLeastOne = new ArrayList>(); probs = new HashSet>(8); probs.add(PROBING_IN_PROCESS); moduleProblems.put(probed, probs); @@ -1428,6 +1429,27 @@ probs.add(Union2.createFirst(dep)); } } + } else if (dep.getType() == Dependency.TYPE_NEEDS) { + String token = dep.getName(); + Set providers = providersOf.get(token); + if (providers == null) { + // Nobody provides it. This dep failed. + probs.add(Union2.createFirst(dep)); + } else { + // We have some possible providers. Check that at least one is good. + boolean foundOne = false; + Set possibleModules = new HashSet(); + for (Module other : providers) { + if (other.isEnabled()) { + foundOne = true; + } else { + possibleModules.add(other); + } + } + if (!foundOne) { + enableAtLeastOne.add(possibleModules); + } + } } else { assert dep.getType() == Dependency.TYPE_JAVA; // Java dependency. Fixed for whole VM session, safe to check once and keep. @@ -1438,6 +1460,20 @@ } } probs.remove(PROBING_IN_PROCESS); + if (probs.isEmpty()) { + // verify that at least one of the needed modules can be enabled + GROUPS: for (Set group : enableAtLeastOne) { + Set> err = null; + for (Module m : group) { + err = _missingDependencies(m); + if (err.isEmpty()) { + continue GROUPS; + } + } + assert err != null; + probs.addAll(err); + } + } } return probs; } Index: core/startup/test/unit/src/org/netbeans/core/startup/ModuleManagerTest.java =================================================================== RCS file: /shared/data/ccvs/repository/core/startup/test/unit/src/org/netbeans/core/startup/ModuleManagerTest.java,v retrieving revision 1.6 diff -u -r1.6 ModuleManagerTest.java --- core/startup/test/unit/src/org/netbeans/core/startup/ModuleManagerTest.java 1 Jun 2006 15:03:58 -0000 1.6 +++ core/startup/test/unit/src/org/netbeans/core/startup/ModuleManagerTest.java 28 Jun 2006 21:00:42 -0000 @@ -49,6 +49,7 @@ import org.openide.util.Lookup; import org.openide.util.LookupEvent; import org.openide.util.LookupListener; +import org.openide.util.TopologicalSortException; import org.openide.util.Utilities; /** Test the module manager as well as the Module class. @@ -1018,6 +1019,88 @@ assertEquals(4, toEnable.size()); assertTrue(toEnable.get(0) != m2); assertTrue(toEnable.get(0) != m3); + } finally { + mgr.mutexPrivileged().exitWriteAccess(); + } + } + + public void testSimpleProvNeeds() throws Exception { + FakeModuleInstaller installer = new FakeModuleInstaller(); + FakeEvents ev = new FakeEvents(); + ModuleManager mgr = new ModuleManager(installer, ev); + mgr.mutexPrivileged().enterWriteAccess(); + try { + Module m1 = mgr.create(new File(jars, "prov-foo-depends-needs_foo.jar"), null, false, false, false); + Module m2 = mgr.create(new File(jars, "needs-foo.jar"), null, false, false, false); + assertEquals(Collections.singletonList("foo"), Arrays.asList(m1.getProvides())); + assertEquals(Collections.EMPTY_LIST, Arrays.asList(m2.getProvides())); + assertEquals(1, m1.getDependencies().size()); + assertEquals(Dependency.create(Dependency.TYPE_NEEDS, "foo"), m2.getDependencies()); + Map modulesByName = new HashMap(); + modulesByName.put(m1.getCodeNameBase(), m1); + modulesByName.put(m2.getCodeNameBase(), m2); + Map> providersOf = new HashMap>(); + providersOf.put("foo", Collections.singleton(m1)); + List m1m2 = Arrays.asList(new Module[] {m1, m2}); + List m2m1 = Arrays.asList(new Module[] {m2, m1}); + Map> deps = Util.moduleDependencies(m1m2, modulesByName, providersOf); + assertEquals(Collections.singletonList(m2), deps.get(m1)); +/* assertEquals(Collections.singletonList(m1), deps.get(m2)); + + try { + Utilities.topologicalSort(m1m2, deps); + } catch (TopologicalSortException ex) { + Set[] arr = ex.unsortableSets(); + assertEquals("One unsortable set", 1, arr.length); + assertEquals("It contains two elements", 2, arr[0].size()); + assertTrue("m1 is there", arr[0].contains(m1)); + assertTrue("m2 is there", arr[0].contains(m2)); + }*/ + Set m1PlusM2 = new HashSet(); + m1PlusM2.add(m1); + m1PlusM2.add(m2); + List toEnable = mgr.simulateEnable(m1PlusM2); + assertEquals("correct result of simulateEnable", Arrays.asList(new Module[] {m2, m1}), toEnable); + toEnable = mgr.simulateEnable(Collections.singleton(m1)); + assertEquals("correct result of simulateEnable #2", Arrays.asList(new Module[] {m2, m1}), toEnable); + toEnable = mgr.simulateEnable(Collections.singleton(m2)); + assertEquals("correct result of simulateEnable #3", Arrays.asList(new Module[] {m2, m1}), toEnable); + mgr.enable(m1PlusM2); + assertEquals(Arrays.asList(new String[] { + "prepare", + "prepare", + "load" + }), installer.actions); + assertEquals(Arrays.asList(new Object[] { + m2, + m1, + Arrays.asList(new Module[] {m2, m1}) + }), installer.args); + Class testclazz = Class.forName("org.prov_foo.Clazz", true, m1.getClassLoader()); + try { + Class.forName("org.prov_foo.Clazz", true, m2.getClassLoader()); + fail("Should not be able to access classes due to prov-req deps only"); + } catch (ClassNotFoundException cnfe) { + // OK, good. + } + installer.clear(); + List toDisable = mgr.simulateDisable(Collections.singleton(m1)); + assertEquals("correct result of simulateDisable", Arrays.asList(new Module[] {m2, m1}), toDisable); + toDisable = mgr.simulateDisable(m1PlusM2); + assertEquals("correct result of simulateDisable #2", Arrays.asList(new Module[] {m2, m1}), toDisable); + mgr.disable(m1PlusM2); + assertFalse(m1.isEnabled()); + assertFalse(m2.isEnabled()); + assertEquals(Arrays.asList(new String[] { + "unload", + "dispose", + "dispose" + }), installer.actions); + assertEquals(Arrays.asList(new Object[] { + Arrays.asList(new Module[] {m2, m1}), + m2, + m1 + }), installer.args); } finally { mgr.mutexPrivileged().exitWriteAccess(); } Index: core/startup/test/unit/src/org/netbeans/core/startup/build.xml =================================================================== RCS file: /shared/data/ccvs/repository/core/startup/test/unit/src/org/netbeans/core/startup/build.xml,v retrieving revision 1.3 diff -u -r1.3 build.xml --- core/startup/test/unit/src/org/netbeans/core/startup/build.xml 16 Sep 2005 13:08:42 -0000 1.3 +++ core/startup/test/unit/src/org/netbeans/core/startup/build.xml 28 Jun 2006 21:00:42 -0000 @@ -152,6 +152,11 @@ + + + + + Index: core/startup/test/unit/src/org/netbeans/core/startup/jars/needs-foo.mf =================================================================== RCS file: core/startup/test/unit/src/org/netbeans/core/startup/jars/needs-foo.mf diff -N core/startup/test/unit/src/org/netbeans/core/startup/jars/needs-foo.mf --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ core/startup/test/unit/src/org/netbeans/core/startup/jars/needs-foo.mf 28 Jun 2006 21:00:42 -0000 @@ -0,0 +1,4 @@ +Manifest-Version: 1.0 +OpenIDE-Module: needs_foo +OpenIDE-Module-Name: Needs foo +OpenIDE-Module-Needs: foo Index: core/startup/test/unit/src/org/netbeans/core/startup/jars/prov-foo-depends-needs_foo.mf =================================================================== RCS file: core/startup/test/unit/src/org/netbeans/core/startup/jars/prov-foo-depends-needs_foo.mf diff -N core/startup/test/unit/src/org/netbeans/core/startup/jars/prov-foo-depends-needs_foo.mf --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ core/startup/test/unit/src/org/netbeans/core/startup/jars/prov-foo-depends-needs_foo.mf 28 Jun 2006 21:00:42 -0000 @@ -0,0 +1,5 @@ +Manifest-Version: 1.0 +OpenIDE-Module: prov_foo +OpenIDE-Module-Name: Provides foo a Depends on Needs_Foo +OpenIDE-Module-Provides: foo +OpenIDE-Module-Module-Dependencies: needs_foo