Proposal: #ResourceEncapsulation and #ClassFilesAsResources

Mark Reinhold mark.reinhold at
Tue Jun 28 21:20:15 UTC 2016

Issue summaries

  #ClassFilesAsResources --- If a type is visible and was loaded from a
  class file then it should be possible to read that file by invoking the
  `getResourceAsStream` method of the type's class loader, as it is in
  earlier releases. [1]

  #ResourceEncapsulation --- The `Module::getResourceAsStream` method can
  be used to read the resources of any named module, without restriction,
  which violates the resource-encapsulation requirement [2].  This method
  should be restricted somehow so that only "suitably-privileged" code
  (for some definition of that term) can access resources in a named
  module other than its own.  An alternative is to drop this
  requirement. [3]


Drop the agreed resource-encapsulation requirement, which reads:

  Resource encapsulation --- The run-time system must ensure that the
  static resource files within a module are directly accessible only by
  code within that module.  The existing resource-access APIs should
  continue to work as they do today when used to access a module's own
  resource files. [2]

Make the following changes to the various `getResource*` methods:

  - The `ClassLoader::getResource*` methods will first delegate to the
    loader's parent, if any, and will then search for resources in the
    named modules defined to the loader, and will then search in the
    loader's unnamed module, typically defined by a class path.  In the
    case of the `getResources` method the order of the resources in the
    returned enumeration will be unspecified, except that the values of
    `cl.getResources(name).nextElement()` and `cl.getResource(name)`
    will be equal.

  - The `Class::getResource*` methods will only search for resources in
    the module that defines the class.  In the case of a class in an
    unnamed module this will typically result in searching the class
    path of the corresponding class loader.

The `java.lang.reflect.Module::getResourceAsStream` method will remain,
so that it's possible to look up a resource in a module without having a
reference to a class defined in that module.  It will always be the case
that, for a given a class `c`, `c.getResourceAsStream(name)` will be
equivalent to `c.getModule().getResourceAsStream(name)`.


The encapsulation of resources was intended to be part of the overall
encapsulation story, on the view that a module's resources are just
internal implementation details that need not, and should not, be
available to code in other modules.  Allowing external code to depend
upon a module's internal resources is just as problematic as allowing
such code to depend upon internal APIs.

The reality of existing code today, however, is rather different, with
two common use cases:

  - Resources are often used intentionally to convey information to
    external code, e.g., as a way to publish configuration files such as
    `persistence.xml`.  This sort of thing should, ideally, be done via
    services [4][5], but that is not the current practice and it will
    take years to migrate to it.

  - Various popular tools and byte-code manipulation frameworks expect to
    be able to load class files as resources, i.e., to access the class
    file that defines a class `p.q.C` by invoking `getResource("p.q.C")`
    on that class's loader.

With the API as it stands today these use cases can still be supported,
but the code that looks up the resource must be converted to use the
`Module::getResourceAsStream` method.  This is a barrier to migration,
since it means that fewer existing frameworks will work out-of-the-box
as automatic modules.

We should not drop agreed requirements lightly, but now that we have
some practical experience with an implementation of this one I think
it's clear that the pain outweighs the gain.


More information about the jpms-spec-experts mailing list