Index: src/org/netbeans/core/modules/Module.java =================================================================== RCS file: /cvs/core/src/org/netbeans/core/modules/Module.java,v --- src/org/netbeans/core/modules/Module.java 11 Aug 2004 10:07:40 -0000 1.55 +++ src/org/netbeans/core/modules/Module.java 12 Mar 2005 09:01:50 -0000 @@ -110,6 +110,8 @@ public final class Module extends Module private Properties localizedProps; /** public packages, may be null */ private PackageExport[] publicPackages; + /** Set of names of friend modules or null */ + private Set/**/ friendNames; /** Map from extension JARs to sets of JAR that load them via Class-Path. * Used only for debugging purposes, so that a warning is printed if two @@ -380,6 +382,14 @@ public final class Module extends Module return publicPackages; } + /** Checks whether we use friends attribute and if so, then + * whether the name of module is listed there. + */ + public boolean isDeclaredAsFriend (Module module) { + if (friendNames == null) return true; + return friendNames.contains (module.getCodeName()); + } + /** Parse information from the current manifest. * Includes code name, specification version, and dependencies. * If anything is in an invalid format, throws an exception with @@ -481,6 +491,23 @@ public final class Module extends Module Util.err.log(ErrorManager.WARNING, "Warning: module " + codeNameBase + " does not declare OpenIDE-Module-Public-Packages in its manifest, so all packages are considered public by default: http://www.netbeans.org/download/dev/javadoc/OpenAPIs/org/openide/doc-files/upgrade.html#3.4-public-packages"); publicPackages = null; } + + { + // friends + String friends = attr.getValue("OpenIDE-Module-Friends"); // NOI18N + if (friends != null) { + StringTokenizer tok = new StringTokenizer(friends, ", "); // NOI18N + HashSet set = new HashSet (); + while (tok.hasMoreTokens()) { + String piece = tok.nextToken(); + set.add(piece.intern ()); + } + if (set.isEmpty()) throw new IllegalArgumentException("Illegal OpenIDE-Module-Friends: " + friends); // NOI18N + this.friendNames = set; + } + } + + // Dependencies Set dependencies = new HashSet(20); // Set // First convert IDE/1 -> org.openide/1, so we never have to deal with Index: src/org/netbeans/core/modules/ModuleManager.java =================================================================== RCS file: /cvs/core/src/org/netbeans/core/modules/ModuleManager.java,v --- src/org/netbeans/core/modules/ModuleManager.java 14 Aug 2004 00:28:23 -0000 1.64 +++ src/org/netbeans/core/modules/ModuleManager.java 12 Mar 2005 09:01:51 -0000 @@ -555,6 +555,14 @@ public final class ModuleManager { return false; } //Util.err.log("friend"); + } else { + // We have to verify that the caller is friend of this module + if (!parent.isDeclaredAsFriend (m)) { + if (Util.err.isLoggable(ErrorManager.INFORMATIONAL)) { + Util.err.log("Refusing to load public package " + pkg + " for " + m + " from parent module " + parent + " as the caller is not listed as a friend module"); // NOI18N + } + return false; + } } //Util.err.log("exported"); } Index: test/unit/src/org/netbeans/core/modules/ModuleManagerTest.java =================================================================== RCS file: /cvs/core/test/unit/src/org/netbeans/core/modules/ModuleManagerTest.java,v --- test/unit/src/org/netbeans/core/modules/ModuleManagerTest.java 8 Feb 2005 10:12:29 -0000 1.39 +++ test/unit/src/org/netbeans/core/modules/ModuleManagerTest.java 12 Mar 2005 09:01:52 -0000 @@ -1653,6 +1653,50 @@ public class ModuleManagerTest extends S mgr.mutexPrivileged().exitWriteAccess(); } } + + public void testPublicPackagesCanBeExportedToSelectedFriendsOnlyIssue54123 () throws Exception { + ModuleManager mgr = new ModuleManager(new FakeModuleInstaller(), new FakeEvents()); + mgr.mutexPrivileged().enterWriteAccess(); + try { + addOpenide(mgr); + Module m1 = mgr.create(new File(jars, "api-mod-export-friend.jar"), null, false, false, false); + Module m2 = mgr.create(new File(jars, "uses-api-friend.jar"), null, false, false, false); + Module m3 = mgr.create(new File(jars, "uses-and-exports-api.jar"), null, false, false, false); + Module m4 = mgr.create(new File(jars, "uses-api-directly.jar"), null, false, false, false); + assertEquals("api-mod-export-api.jar had no problems", Collections.EMPTY_SET, m1.getProblems()); + assertEquals("uses-api-friend.jar had no problems", Collections.EMPTY_SET, m2.getProblems()); + assertEquals("uses-and-exports-api.jar had no problems", Collections.EMPTY_SET, m3.getProblems()); + assertEquals("uses-api-directly.jar had no problems", Collections.EMPTY_SET, m4.getProblems()); + mgr.enable(new HashSet(Arrays.asList(new Module[] {m1, m2, m3, m4}))); + m2.getClassLoader().loadClass("usesapi.UsesPublicClass").newInstance(); + try { + m2.getClassLoader().loadClass("usesapi.UsesImplClass").newInstance(); + fail ("Even friends modules cannot access implementation classes"); + } catch (NoClassDefFoundError ex) { + // ok + } + + try { + m4.getClassLoader().loadClass("usesapi.UsesPublicClass").newInstance(); + fail ("m4 is not friend and should not be allowed to load the class"); + } catch (NoClassDefFoundError ex) { + // ok + } + try { + m4.getClassLoader().loadClass("usesapi.UsesImplClass").newInstance(); + fail ("m4 is not friend and should not be allowed to load the implementation either"); + } catch (NoClassDefFoundError ex) { + // ok + } + mgr.disable(new HashSet(Arrays.asList(new Module[] {m1, m2, m3, m4}))); + mgr.delete(m4); + mgr.delete(m3); + mgr.delete(m2); + mgr.delete(m1); + } finally { + mgr.mutexPrivileged().exitWriteAccess(); + } + } public void testModuleInterdependencies() throws Exception { FakeModuleInstaller installer = new FakeModuleInstaller(); Index: test/unit/src/org/netbeans/core/modules/build.xml =================================================================== RCS file: /cvs/core/test/unit/src/org/netbeans/core/modules/build.xml,v --- test/unit/src/org/netbeans/core/modules/build.xml 29 Oct 2004 15:18:42 -0000 1.24 +++ test/unit/src/org/netbeans/core/modules/build.xml 12 Mar 2005 09:01:52 -0000 @@ -159,6 +159,10 @@ Microsystems, Inc. All Rights Reserved. + + + + @@ -202,6 +206,10 @@ Microsystems, Inc. All Rights Reserved. + + + + Index: test/unit/src/org/netbeans/core/modules/jars/api-mod-export-friend.mf =================================================================== RCS file: test/unit/src/org/netbeans/core/modules/jars/api-mod-export-friend.mf --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ test/unit/src/org/netbeans/core/modules/jars/api-mod-export-friend.mf 12 Mar 2005 09:01:52 -0000 @@ -0,0 +1,8 @@ +Manifest-Version: 1.0 +OpenIDE-Module: org.netbeans.modules.foo +OpenIDE-Module-Public-Packages: org.netbeans.api.foo.* +OpenIDE-Module-Friends: org.netbeans.core/1, usesapifriend +OpenIDE-Module-Specification-Version: 1.0 +OpenIDE-Module-Implementation-Version: today +OpenIDE-Module-IDE-Dependencies: IDE/1 > 2.2 + Index: test/unit/src/org/netbeans/core/modules/jars/uses-api-friend.mf =================================================================== RCS file: test/unit/src/org/netbeans/core/modules/jars/uses-api-friend.mf --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ test/unit/src/org/netbeans/core/modules/jars/uses-api-friend.mf 12 Mar 2005 09:01:52 -0000 @@ -0,0 +1,5 @@ +Manifest-Version: 1.0 +OpenIDE-Module: usesapifriend +OpenIDE-Module-Module-Dependencies: org.netbeans.modules.foo > 1.0 +OpenIDE-Module-IDE-Dependencies: IDE/1 > 5.0 +