--- a/core.startup/src/org/netbeans/core/startup/ConsistencyVerifier.java +++ a/core.startup/src/org/netbeans/core/startup/ConsistencyVerifier.java @@ -99,7 +99,9 @@ public class ConsistencyVerifier { mgr.mutexPrivileged().enterWriteAccess(); Manifest dummy = new Manifest(); dummy.getMainAttributes().putValue("OpenIDE-Module", "__dummy__"); // NOI18N - dummy.getMainAttributes().putValue("OpenIDE-Module-Provides", "org.openide.modules.ModuleFormat1, " + // NOI18N + dummy.getMainAttributes().putValue("OpenIDE-Module-Provides", + "org.openide.modules.ModuleFormat1, " + // NOI18N + "org.openide.modules.ModuleFormat2, " + // NOI18N "org.openide.modules.os.Unix, " + // NOI18N "org.openide.modules.os.PlainUnix, " + // NOI18N "org.openide.modules.os.Windows, " + // NOI18N --- a/core.startup/src/org/netbeans/core/startup/NbInstaller.java +++ a/core.startup/src/org/netbeans/core/startup/NbInstaller.java @@ -52,6 +52,7 @@ import java.util.Date; import java.util.Date; import java.util.HashMap; import java.util.HashSet; +import java.util.LinkedList; import java.util.List; import java.util.Locale; import java.util.Map; @@ -93,7 +94,9 @@ import org.xml.sax.SAXException; * @author Jesse Glick, Jan Pokorsky, Jaroslav Tulach, et al. */ final class NbInstaller extends ModuleInstaller { - + + private static final Logger LOG = Logger.getLogger(NbInstaller.class.getName()); + /** set of manifest sections for each module */ private final Map> sections = new HashMap>(100); /** ModuleInstall classes for each module that declares one */ @@ -110,8 +113,8 @@ final class NbInstaller extends ModuleIn private ModuleManager mgr; /** set of permitted core or package dependencies from a module */ private final Map> kosherPackages = new HashMap>(100); - /** Package prefixes passed as special system property. */ - private static String[] specialResourcePrefixes = null; + /** classpath ~ JRE packages to be hidden from a module */ + private final Map> hiddenClasspathPackages = new HashMap>(); /** Create an NbInstaller. * You should also call {@link #registerManager} and if applicable @@ -135,6 +138,7 @@ final class NbInstaller extends ModuleIn // @SuppressWarnings("unchecked") public void prepare(Module m) throws InvalidException { ev.log(Events.PREPARE, m); + checkForHiddenPackages(m); Set mysections = null; Class clazz = null; { @@ -224,6 +228,50 @@ final class NbInstaller extends ModuleIn layers.put(m, layerResource); } } + + private void checkForHiddenPackages(Module m) throws InvalidException { + List hiddenPackages = new ArrayList(); + List mWithDeps = new LinkedList(); + mWithDeps.add(m); + for (Dependency d : m.getDependencies()) { + if (d.getType() == Dependency.TYPE_MODULE) { + Module _m = mgr.get((String) Util.parseCodeName(d.getName())[0]); + assert _m != null : d; + mWithDeps.add(_m); + } + } + for (Module _m : mWithDeps) { + String hidden = (String) _m.getAttribute("OpenIDE-Module-Hide-Classpath-Packages"); // NOI18N + if (hidden != null) { + for (String piece : hidden.trim().split("[ ,]+")) { // NOI18N + try { + if (piece.endsWith(".*")) { // NOI18N + String pkg = piece.substring(0, piece.length() - 2); + Dependency.create(Dependency.TYPE_MODULE, pkg); + if (pkg.lastIndexOf('/') != -1) { + throw new IllegalArgumentException("Illegal OpenIDE-Module-Hide-Classpath-Packages: " + hidden); // NOI18N + } + hiddenPackages.add(new Module.PackageExport(pkg.replace('.', '/') + '/', false)); + } else if (piece.endsWith(".**")) { // NOI18N + String pkg = piece.substring(0, piece.length() - 3); + Dependency.create(Dependency.TYPE_MODULE, pkg); + if (pkg.lastIndexOf('/') != -1) { + throw new IllegalArgumentException("Illegal OpenIDE-Module-Hide-Classpath-Packages: " + hidden); // NOI18N + } + hiddenPackages.add(new Module.PackageExport(pkg.replace('.', '/') + '/', true)); + } else { + throw new IllegalArgumentException("Illegal OpenIDE-Module-Hide-Classpath-Packages: " + hidden); // NOI18N + } + } catch (IllegalArgumentException x) { + throw new InvalidException(_m, x.getMessage()); + } + } + } + } + if (!hiddenPackages.isEmpty()) { + hiddenClasspathPackages.put(m, hiddenPackages); + } + } public void dispose(Module m) { Util.err.fine("dispose: " + m); @@ -237,6 +285,7 @@ final class NbInstaller extends ModuleIn installs.remove(m); layers.remove(m); kosherPackages.remove(m); + hiddenClasspathPackages.remove(m); } public void load(List modules) { @@ -727,8 +776,9 @@ final class NbInstaller extends ModuleIn arr.add("org.openide.modules.os.Solaris"); // NOI18N } - // module format is now 1 - arr.add ("org.openide.modules.ModuleFormat1"); // NOI18N + // module format is now 2 + arr.add("org.openide.modules.ModuleFormat1"); // NOI18N + arr.add("org.openide.modules.ModuleFormat2"); // NOI18N return arr.toArray (new String[0]); } @@ -743,33 +793,34 @@ final class NbInstaller extends ModuleIn for (String cppkg : CLASSPATH_PACKAGES) { if (pkg.startsWith(cppkg) && !findKosher(m).contains(cppkg)) { // Undeclared use of a classpath package. Refuse it. - if (Util.err.isLoggable(Level.FINE)) { - Util.err.fine("Refusing to load classpath package " + pkg + " for " + m.getCodeNameBase() + " without a proper dependency"); // NOI18N + if (LOG.isLoggable(Level.FINE)) { + LOG.fine("Refusing to load classpath package " + pkg + " for " + m.getCodeNameBase() + " without a proper dependency"); // NOI18N } return false; } } + List hiddenPackages = hiddenClasspathPackages.get(m); + if (hiddenPackages != null) { + for (Module.PackageExport hidden : hiddenPackages) { + if (hidden.recursive ? pkg.startsWith(hidden.pkg) : pkg.equals(hidden.pkg)) { + if (LOG.isLoggable(Level.FINE)) { + LOG.fine("Refusing to load classpath package " + pkg + " for " + m.getCodeNameBase()); + } + return false; + } + } + } + } + if (LOG.isLoggable(Level.FINER)) { + LOG.finer("Delegating resource " + pkg + " from " + parent + " for " + m.getCodeNameBase()); } return true; } - private static final String[] CLASSPATH_PACKAGES = new String[] { + private static final String[] CLASSPATH_PACKAGES = { // core.jar shall be inaccessible "org/netbeans/core/startup/", - // Java language infrastructure bundled with IDE; do not want clashes with JDK 6: - "com/sun/tools/javac/", - "com/sun/tools/javadoc/", - "com/sun/javadoc/", - "com/sun/source/", - "javax/annotation/", - "javax/lang/model/", - "javax/tools/", - // do not want JAX-WS 2.0 classes from JDK 6; - "javax/xml/bind/", // NOI18N - "javax/xml/ws/", // NOI18N - "javax/xml/stream/", // NOI18N - "javax/jws/", // NOI18N - "javax/xml/soap/" // NOI18N + // Do not add JRE packages here! See issue #96711 for the alternative. }; private Set findKosher(Module m) { --- a/core.startup/test/unit/src/org/netbeans/core/startup/ConsistencyVerifierTest.java +++ a/core.startup/test/unit/src/org/netbeans/core/startup/ConsistencyVerifierTest.java @@ -100,8 +100,10 @@ public class ConsistencyVerifierTest ext public void testStandardProvides() throws Exception { assertProblems("{}", "=foo; Requires=org.openide.modules.ModuleFormat1"); - assertProblems("{foo=[requires org.openide.modules.ModuleFormat2]}", + assertProblems("{}", "=foo; Requires=org.openide.modules.ModuleFormat2"); + assertProblems("{foo=[requires org.openide.modules.ModuleFormat99]}", + "=foo; Requires=org.openide.modules.ModuleFormat99"); assertProblems("{}", "=foo; Requires=org.openide.modules.os.Unix"); assertProblems("{}", --- a/core.startup/test/unit/src/org/netbeans/core/startup/ModuleFormatSatisfiedTest.java +++ a/core.startup/test/unit/src/org/netbeans/core/startup/ModuleFormatSatisfiedTest.java @@ -75,7 +75,7 @@ public class ModuleFormatSatisfiedTest e man.getMainAttributes ().putValue ("OpenIDE-Module", "org.test.FormatDependency/1"); - String req = "org.openide.modules.ModuleFormat1"; + String req = "org.openide.modules.ModuleFormat1, org.openide.modules.ModuleFormat2"; man.getMainAttributes ().putValue ("OpenIDE-Module-Requires", req); --- a/core.startup/test/unit/src/org/netbeans/core/startup/NbInstallerHideClasspathPackagesTest.java +++ a/core.startup/test/unit/src/org/netbeans/core/startup/NbInstallerHideClasspathPackagesTest.java @@ -0,0 +1,119 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2008 Sun Microsystems, Inc. + */ + +package org.netbeans.core.startup; + +import java.io.File; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.logging.Level; +import org.netbeans.Events; +import org.netbeans.Module; +import org.netbeans.ModuleManager; + +/** + * Checks that OpenIDE-Module-Hide-Classpath-Packages works. + */ +public class NbInstallerHideClasspathPackagesTest extends SetupHid { + + public NbInstallerHideClasspathPackagesTest(String n) { + super(n); + } + + public void testHideClasspathPackages() throws Exception { + File m1j = new File(getWorkDir(), "m1.jar"); + Map contents = new HashMap(); + contents.put("javax/net/SocketFactory.class", "ignored"); + contents.put("javax/swing/JPanel.class", "overrides"); + contents.put("javax/swing/text/Document.class", "overrides"); + contents.put("javax/naming/Context.class", "overrides"); + contents.put("javax/naming/spi/Resolver.class", "ignored"); + Map mani = new HashMap(); + mani.put("OpenIDE-Module", "m1"); + mani.put("OpenIDE-Module-Hide-Classpath-Packages", "javax.swing.**, javax.naming.*"); + createJar(m1j, contents, mani); + File m2j = new File(getWorkDir(), "m2.jar"); + mani = new HashMap(); + mani.put("OpenIDE-Module", "m2"); + mani.put("OpenIDE-Module-Module-Dependencies", "m1"); + // Just to check early attempts to load packages: + mani.put("OpenIDE-Module-Layer", "m2/layer.xml"); + mani.put("OpenIDE-Module-Package-Dependencies", "javax.management[Descriptor]"); + createJar(m2j, Collections.singletonMap("m2/layer.xml", ""), mani); + File m3j = new File(getWorkDir(), "m3.jar"); + createJar(m3j, Collections.emptyMap(), Collections.singletonMap("OpenIDE-Module", "m3")); + Events ev = new FakeEvents(); + NbInstaller inst = new NbInstaller(ev); + ModuleManager mgr = new ModuleManager(inst, ev); + inst.registerManager(mgr); + mgr.mutexPrivileged().enterWriteAccess(); + try { + Module m1 = mgr.create(m1j, null, false, false, false); + Module m2 = mgr.create(m2j, null, false, false, false); + Module m3 = mgr.create(m3j, null, false, false, false); + mgr.enable(new HashSet(Arrays.asList(m1, m2, m3))); + ModuleManagerTest.assertDoesNotOverride(m1, "javax.net.SocketFactory"); + ModuleManagerTest.assertOverrides(m1, "javax.swing.JPanel"); + ModuleManagerTest.assertOverrides(m1, "javax.swing.text.Document"); + ModuleManagerTest.assertOverrides(m1, "javax.naming.Context"); + ModuleManagerTest.assertDoesNotOverride(m1, "javax.naming.spi.Resolver"); + ModuleManagerTest.assertDoesNotOverride(m2, "javax.net.SocketFactory"); + ModuleManagerTest.assertOverrides(m2, "javax.swing.JPanel"); + ModuleManagerTest.assertOverrides(m2, "javax.swing.text.Document"); + ModuleManagerTest.assertOverrides(m2, "javax.naming.Context"); + ModuleManagerTest.assertDoesNotOverride(m2, "javax.naming.spi.Resolver"); + ModuleManagerTest.assertDoesNotOverride(m3, "javax.net.SocketFactory"); + ModuleManagerTest.assertDoesNotOverride(m3, "javax.swing.JPanel"); + ModuleManagerTest.assertDoesNotOverride(m3, "javax.swing.text.Document"); + ModuleManagerTest.assertDoesNotOverride(m3, "javax.naming.Context"); + ModuleManagerTest.assertDoesNotOverride(m3, "javax.naming.spi.Resolver"); + } finally { + mgr.mutexPrivileged().exitWriteAccess(); + } + } + + @Override + protected Level logLevel() { + return Level.FINER; + } + +} --- a/etl.editor/manifest.mf +++ a/etl.editor/manifest.mf @@ -2,7 +2,8 @@ OpenIDE-Module: org.netbeans.modules.etl OpenIDE-Module: org.netbeans.modules.etl.editor OpenIDE-Module-Layer: org/netbeans/modules/etl/layer.xml OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/etl/Bundle.properties -OpenIDE-Module-Requires: org.openide.windows.IOProvider +OpenIDE-Module-Requires: org.openide.windows.IOProvider, org.openide.modules.ModuleFormat2 +OpenIDE-Module-Hide-Classpath-Packages: javax.xml.stream.** OpenIDE-Module-Specification-Version: 1.0 Name: org/netbeans/modules/etl/ui/ETLDataLoader.class --- a/libs.javacapi/manifest.mf +++ a/libs.javacapi/manifest.mf @@ -3,3 +3,5 @@ OpenIDE-Module-Implementation-Version: 1 OpenIDE-Module-Implementation-Version: 1 OpenIDE-Module-Localizing-Bundle: org/netbeans/libs/javacapi/Bundle.properties OpenIDE-Module-Layer: org/netbeans/libs/javacapi/layer.xml +OpenIDE-Module-Requires: org.openide.modules.ModuleFormat2 +OpenIDE-Module-Hide-Classpath-Packages: com.sun.javadoc.**, com.sun.source.**, javax.annotation.**, javax.lang.model.**, javax.tools.** --- a/libs.javacimpl/manifest.mf +++ a/libs.javacimpl/manifest.mf @@ -2,4 +2,6 @@ OpenIDE-Module: org.netbeans.libs.javaci OpenIDE-Module: org.netbeans.libs.javacimpl/1 OpenIDE-Module-Implementation-Version: 5 OpenIDE-Module-Localizing-Bundle: org/netbeans/libs/javacimpl/Bundle.properties +OpenIDE-Module-Requires: org.openide.modules.ModuleFormat2 +OpenIDE-Module-Hide-Classpath-Packages: com.sun.tools.javac.**, com.sun.tools.javadoc.** --- a/o.n.soa.libs.xmlbeans/manifest.mf +++ a/o.n.soa.libs.xmlbeans/manifest.mf @@ -3,3 +3,5 @@ OpenIDE-Module-Localizing-Bundle: org/ne OpenIDE-Module-Localizing-Bundle: org/netbeans/soa/libs/xmlbeans/Bundle.properties OpenIDE-Module-Specification-Version: 1.0 OpenIDE-Module-Implementation-Version: 2.1.0 +OpenIDE-Module-Requires: org.openide.modules.ModuleFormat2 +OpenIDE-Module-Hide-Classpath-Packages: javax.xml.stream.** --- a/openide.modules/apichanges.xml +++ a/openide.modules/apichanges.xml @@ -47,6 +47,57 @@ made subject to such option by the copyr Modules API + + + + + + + +

+ To guaranteed that the new tag will be honored you should also: +

+
OpenIDE-Module-Requires: org.openide.modules.ModuleFormat2
+

+ Several JRE packages used to be hidden automatically but are not any more; + while these were never documented, modules which relied on being able to override + these packages could be broken by having the JRE classes exposed instead. Specifically, + the following packages (or package prefixes) were in NetBeans 6.0 not loaded from the + classpath, but now will be unless otherwise requested: +

+
com.sun.javadoc
+com.sun.source
+com.sun.tools.javac
+com.sun.tools.javadoc
+javax.annotation
+javax.jws
+javax.lang.model
+javax.tools
+javax.xml.bind
+javax.xml.soap
+javax.xml.stream
+javax.xml.ws
+
+ +

+ Packages supplied in a module which overlap those in the JRE or its extensions will normally be ignored + (as usual, the JRE takes precedence). Modules which wish to specifically suppress loading of some packages + from the classpath can now request the class loader to do so by specifying: +

+
OpenIDE-Module-Hide-Classpath-Packages: javax.lang.model.*, com.sun.source.**
+

+ (The syntax is analogous to that of OpenIDE-Module-Public-Packages.) +

+

+ Such a declaration affects not just this module, but any other modules declaring a direct + dependency on it (OpenIDE-Module-Module-Dependencies). The module is now free to bundle + its own versions of these classes and be sure they will be used by it and its clients. + Be aware that as with all changes to the normal class loading scheme, + careless usage could result in LinkageErrors. +

+
+ +
No longer possible to obtain a module JAR entry as a FileObject --- a/openide.modules/manifest.mf +++ a/openide.modules/manifest.mf @@ -1,5 +1,5 @@ Manifest-Version: 1.0 Manifest-Version: 1.0 OpenIDE-Module: org.openide.modules OpenIDE-Module-Localizing-Bundle: org/openide/modules/Bundle.properties -OpenIDE-Module-Specification-Version: 7.5 +OpenIDE-Module-Specification-Version: 7.6 --- a/websvc.jaxws21api/manifest.mf +++ a/websvc.jaxws21api/manifest.mf @@ -3,3 +3,5 @@ OpenIDE-Module-Localizing-Bundle: org/ne OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/websvc/jaxws21api/Bundle.properties OpenIDE-Module-Specification-Version: 1.2 AutoUpdate-Show-In-Client: false +OpenIDE-Module-Requires: org.openide.modules.ModuleFormat2 +OpenIDE-Module-Hide-Classpath-Packages: javax.jws.**, javax.xml.bind.**, javax.xml.stream.**, javax.xml.ws.**, javax.xml.soap.**, javax.annotation.**