Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8350547

ClassLoader.definePackage() throws IllegalArgumentException if package already defined

XMLWordPrintable

    • Icon: CSR CSR
    • Resolution: Unresolved
    • Icon: P4 P4
    • None
    • core-libs
    • None

      Summary

      ClassLoader.definePackage to be changed to avoid custom class loaders having to deal with IllegalArgumentExceptions due to concurrent calls to definePackage() (loading two classes in the same package concurrently).

      Problem

      Concurrent calls to definePackage() can yield IllegalArgumentExceptions if the package is already defined. Some built-in class loaders, like URLClassLoader, already handle this case, but custom class loaders (would) all have to implement the handling of concurrent class loading.

      The CDI reference implementation's default behavior (Weld) [1] and Quarkus [2] are known to run into IAEs due to concurrent calls to definePackage().

      Solution

      Change the implementation of CL.definePackage() to only throw an IllegalArgumentException if the given package properties are incompatible with the already existing one.

      Properties to consider for the "compatible check" are those defined in Package and its super class NamedPackage:

      • specTitle, specVersion, specVendor, implTitle, implVersion, implVendor (via .equals())
      • module (object identity comparison)
      • sealBase via Package.isSealed() and Package.isSealed(URL)

      The core implementation change in java.lang.ClassLoader is to functionally replace the check around packages.putIfAbsent() from

          protected Package definePackage(String name, String specTitle,
                                       implTitle, implVersion, implVendor,
                                       sealBase, this);
      ...
              Package p = new Package(name, specTitle, specVersion, specVendor,
                                      implTitle, implVersion, implVendor,
                                      sealBase, this);
      
             if (packages.putIfAbsent(name, p) != null)
                 throw new IllegalArgumentException(name);
             return p;

      to

      ...
             NamedPackage ex = packages.putIfAbsent(name, p);
             if (ex == null)
                 return p;
             // '.equals' here for simplicity for the CSR.
             // Implementation to check the individual properties
             // and use 'isSealed(URL)'.
             if (ex.equals(p))
                 return (Package) ex;
             throw new IllegalArgumentException(
                 "Incompatible redefinition of package " + name);
           }

      Additionally, the thrown IAE's message shall indicate the package name and a description.

      The existing special handling in BuiltinClassLoader and URLClassLoader can be removed.

      Specification

      --- a/src/java.base/share/classes/java/lang/ClassLoader.java
      +++ b/src/java.base/share/classes/java/lang/ClassLoader.java
      @@ -2036,14 +2036,19 @@ private Package toPackage(String name, NamedPackage p, Module m) {
            *         respect to the given code source {@link java.net.URL URL}
            *         object.  Otherwise, the package is not sealed.
            *
      -     * @return  The newly defined {@code Package} object
      +     * @return  The {@code Package} object for the given implementation
      +     *          and specification title/version/vendor and seal-base
      +     *          properties.
            *
            * @throws  NullPointerException
            *          if {@code name} is {@code null}.
            *
            * @throws  IllegalArgumentException
            *          if a package of the given {@code name} is already
      -     *          defined by this class loader
      +     *          defined by this class loader with non-equal values for
      +     *          the package properties (implementation and specification
      +     *          version, vendor, title) or a different {@code sealBase}
      +     *          or module.
            *
            *
            * @since  1.2

      References

      [1] https://github.com/weld/core/blob/d55d91bec804b0c42fd8db38f74a93b99b6c541c/impl/src/main/java/org/jboss/weld/bootstrap/ConcurrentBeanDeployer.java

      [2] https://github.com/quarkusio/quarkus/issues/37363

            rstupp Robert Stupp
            rstupp Robert Stupp
            Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

              Created:
              Updated: