Proposal (revised^2): #ReflectiveAccessToNonExportedTypes&#AwkwardStrongEncapsulation: Open modules & open packages

Thomas Watson tjwatson at us.ibm.com
Thu Oct 27 17:49:41 UTC 2016


I like this proposal also.  It nicely solves the usecases I have in mind. 
Thanks.

Tom




> From: Remi Forax <forax at univ-mlv.fr>
> To: mark.reinhold at oracle.com, jpms-spec-experts at openjdk.java.net
> Date: 10/27/2016 12:05 PM
> Subject: Re: Proposal (revised^2): 
> #ReflectiveAccessToNonExportedTypes & #AwkwardStrongEncapsulation: 
> Open modules & open packages
> Sent by: "jpms-spec-observers" 
<jpms-spec-observers-bounces at openjdk.java.net>
> 
> I really like this proposal.
> 
> Remi 
> 
> On October 27, 2016 5:53:36 PM GMT+02:00, mark.reinhold at oracle.com 
wrote:
> >Issue summary
> >-------------
> >
> >  #ReflectiveAccessToNonExportedTypes --- Some kinds of framework
> >  libraries require reflective access to members of the non-exported
> > types of other modules; examples include dependency injection (Guice),
> >  persistence (JPA), debugging tools, code-automation tools, and
> >  serialization (XStream).  In some cases the particular library to be
> > used is not known until run time (e.g., Hibernate and EclipseLink both
> > implement JPA).  This capability is also sometimes used to work around
> >  bugs in unchangeable code.  Access to non-exported packages can, at
> >  present, only be done via command-line flags, which is extremely
> >  awkward.  Provide an easier way for reflective code to access such
> >  non-exported types. [1]
> >
> >  #AwkwardStrongEncapsulation --- A non-public element of an exported
> >package can still be accessed via the `AccessibleObject::setAccessible`
> >  method of the core reflection API.  The only way to strongly
> >  encapsulate such an element is to move it to a non-exported package.
> >  This makes it awkward, at best, to encapsulate the internals of a
> >  package that defines a public API. [2]
> >
> >Proposal
> >--------
> >
> >(This is the third proposal for #ReflectiveAccessToNonExportedTypes.
> > The note that follows [3] summarizes the background and relates the
> > alternatives presented thus far.)
> >
> >Extend the language of module declarations with the concept of _open_
> >modules.  Open modules make it easy to define modules whose internals
> >will be accessed at run time by reflection-based frameworks.  Every
> >package in an open module has the following properties, by default:
> >
> >  (1) At compile time it is not exported.
> >
> >  (2) At run time it is exported without regard to split-package
> >      conflicts or other inconsistencies, i.e., it does not affect
> >      the operation of the resolver, nor the construction of layer
> >      configurations, but is simply exported as if by invoking the
> >      `Module::addExports` method during layer instantiation.
> >
> >  (3) At run time it is available for _deep reflection_, i.e., all
> >      of its elements are accessible via the core reflection API
> >      including non-public elements, which can be accessed via the
> >      `AccessibleObject::setAccessible` method.
> >
> >A package in an open module can be exported explicitly, for use at
> >compile time, via the familiar `exports` directive, so property (1)
> >will
> >not hold.  In that case it will also partake in run-time resolution, so
> >property (2) will not hold either.
> >
> >Suppose we have a module `foo.bar` that contains an API package
> >`com.foo.bar`, whose public types are intended for use by other
> >modules,
> >and a non-API package `com.foo.bar.model`, that contains entity classes
> >to be manipulated by Hibernate via core reflection.  Then the module
> >declaration
> >
> >    open module foo.bar {
> >        exports com.foo.bar;
> >        requires hibernate.core;
> >        requires hibernate.entitymanager;
> >    }
> >
> >makes all elements, public and otherwise, of all packages available for
> >deep reflection at run time, but makes only the public and protected
> >types in `com.foo.bar` accessible at compile time.  Thus Hibernate can
> >access non-public elements of the `com.foo.bar.model` package via the
> >`setAccessible` method, and some other module that `requires foo.bar`
> >can be compiled against the API in the `com.foo.bar` package but not
> >against the types in any other package.
> >
> >Open modules can be considered an intermediate step on the migration
> >path, between automatic modules and normal modules:
> >
> >  - An automatic module offers the traditional level of encapsulation:
> >    All packages are both open for deep reflective access and exported
> >   for ordinary compile-time and run-time access to their public types.
> >
> > - An open module offers a modest degree of stronger encapsulation: All
> >  packages are open for deep reflective access, but the module's author
> >    must specify which packages, if any, are exported for ordinary
> >    compile-time and run-time access.
> >
> >  - A normal module offers the strongest encapsulation: The module's
> >    author must specify which packages, if any, are open, or exported,
> >    or both.
> >
> >An open module is a good starting point for application code that's
> >being
> >modularized by its author.  It affords a separation of concerns, so
> >that
> >the author can focus on defining the module's domain-specific API
> >without
> >having to worry about how the module will be inspected or manipulated
> >at
> >run time by reflective frameworks.
> >
> >                                  * * *
> >
> >To open specific packages in normal module declarations we introduce a
> >new per-package directive, `opens`.  A package in a normal module can
> >be
> >opened, or exported, or both, as follows:
> >
> >  - If a package is only opened (`opens p;`) then properties (1), (2),
> >    and (3) hold, i.e., its types are not accessible at compile time,
> >   it does not affect resolution, and all of its elements are available
> >    for deep reflection at run time.
> >
> >  - If a package is only exported (`exports p;`) then those properties
> >    do not hold, and it is exported exactly as it is today.
> >
> >  - If a package is both exported and opened (`exports p; opens p;`)
> >    then it is exported as today and is, additionally, available for
> >    deep reflection at run time (3).
> >
> >We can use the `opens` directive to refine the previous example so that
> >only the `com.foo.bar` package is accessible at compile time, only the
> >`com.foo.bar.model` package is available for deep reflection at run
> >time,
> >and the non-public elements of all other packages are strongly
> >encapsulated:
> >
> >    module foo.bar {
> >        exports com.foo.bar;
> >        opens com.foo.bar.model;
> >        requires hibernate.core;
> >        requires hibernate.entitymanager;
> >    }
> >
> >It's possible to both open and export a package in a normal module, but
> >it's often inadvisable.  This is especially so for API packages, since
> >normally an API's internal implementation details should be strongly
> >encapsulated.  This combination of directives may, however, be useful
> >for
> >legacy APIs whose internals are known to be accessed by existing code.
> >
> >To ensure the integrity of the platform we expect that all the modules
> >of
> >the JDK itself will be normal modules and that very few, if any,
> >packages
> >will be opened.
> >
> >The `opens` directive cannot be used in an `open` module, since all
> >packages in such modules are implicitly open.
> >
> >                                  * * *
> >
> >Both the `exports` and `opens` directives can be qualified, so that a
> >package is exported or opened only to certain other named modules.  As
> >before, duplicate directives are not permitted in order to ensure easy
> >readability.  At most one `exports` directive is relevant to any given
> >package, and at most one `opens` directive is relevant to any given
> >package.
> >
> >There is no syntax for wildcards.  If the defaults are not sufficient
> >for
> >a package then the package must be named explicitly.
> >
> >                                  * * *
> >
> >The existing syntax of `requires public` has long been confusing, so we
> >here take the opportunity to fix that problem by renaming the `public`
> >modifier in `requires` directives to `transitive`.  Thus the
> >declaration
> >
> >    module foo.bar {
> >        exports com.foo.bar;
> >        requires public java.sql;
> >    }
> >
> >is now written
> >
> >    module foo.bar {
> >        exports com.foo.bar;
> >        requires transitive java.sql;
> >    }
> >
> >This is potentially confusing in a different way, since in mathematics
> >the term "transitive" is usually applied to an entire relation rather
> >than to three specific elements of a set.  Its use here does not, in
> >particular, mean that the resolver does not interpret plain `requires`
> >directives when computing the transitive closure of a set of root
> >modules.  "Transitive" as used here is in the more abstract sense,
> >expressing the notion of conveying a property -- in this case, the
> >readability of the required module -- from one thing to another.  On
> >balance, however, `transitive` does appear superior to `public`.
> >
> >Notes
> >-----
> >
> >  - This proposal is significantly different from both the first and
> >    second proposals [4][5].  It recognizes that, in practice, it will
> >    be common to have open modules, all of whose content is available
> >  for deep reflection but otherwise inaccessible at compile time unless
> >  explicitly exported.  If finer-grained control over whether a package
> >    is opened, exported, or both is required then a normal module
> >    declaration can be used.
> >
> >  - This proposal is similar to one made by Stephen Colebourne [6],
> >    in which he uses the term `exposes`, and to various unpublished
> >    proposals (of which there have been many!).  We here adopt the term
> >    `opens` rather than `exposes` in order to avoid confusion between
> >    the phonetically-similar `exports` and `exposes`, especially for
> >    non-native speakers of English.  We also use `open` to modify the
> >    `module` keyword rather than introduce wildcards for the `opens`
> >   directive, since the former is easier to explain.  The latter would,
> >  further, lead people to expect wildcards for the `exports` directive,
> >    but we'd prefer to avoid that since it could encourage the careless
> >    exportation of all of a module's packages.  Exporting a package for
> >    use as an API should always be an explicit, affirmative choice.
> >
> >  - A separate `opens` directive, and the avoidance of wildcards,
> >    simplifies the interactions amongst qualified and unqualified
> >    package-level directives.  We no longer need complex rules such as
> >   those shown for the `private` modifier in the previous proposal [5].
> >
> >- This proposal implies corresponding changes to the `ModuleDescriptor`
> >    and `Module` APIs, but these are reasonably straightforward.
> >
> >  - This proposal amends the proposal for #ResourceEncapsulation [6]
> >    to replace the use of private exports with open modules and open
> >    packages.  Wherever that proposal indicates that a resource can be
> >    located when its effective package name identifies a package that's
> >    exported privately, assume instead that the resource can be located
> >    when that package is either an element of an open module or an open
> >    package in a normal module.
> >
> >  - If a container is to ensure that a package in an application module
> >    is available for deep reflection only by a trusted framework module
> >    then it can arrange for that by rewriting that module's descriptor,
> >    as suggested previously [8], to insert the appropriate qualified
> >  `opens` directive.  This works even in the case, pointed out by Jason
> >    Greene [9], of two modules that open a package of the same name to
> >    the same framework module, since opened and unexported packages are
> >    not subject to the usual package-consistency checks.  There is no
> >    need any longer for the resolution algorithm to take this type of
> >    scenario into account [a].  A more flexible way to arrange for such
> >  precisely-constrained access is a separate issue and may be addressed
> >    by a future proposal.
> >
> >- This proposal primarily addresses "friendly" uses of reflection, such
> >    as dependency injection and persistence, in which the author of a
> >    module knows in advance that one or more, or possibly all, packages
> >   must be opened at run time for deep reflective access by frameworks.
> >  Intrusive access to arbitrary packages of arbitrary modules by, e.g.,
> >    serialization frameworks or debugging tools, will still require the
> >    use of sharp knives such as `--add-exports` or `--add-opens`
> >    command-line options, the legacy unsupported `sun.misc.Unsafe` API
> >    and related APIs, or JVM TI.
> >
> >- Using `--add-exports` or `--add-opens` command-line options, or their
> >  equivalents, remains awkward, but sometimes they're the only way out.
> >   To ease migration it's worth considering some way for an application
> >    packaged as a JAR file to include such options in its `MANIFEST.MF`
> >    file, as suggested by Simon Nash [b].  This is addressed in the
> >    proposal for #AddExportsInManifest [c], which is hereby amended to
> >    rename the `Add-Exports-Private` attribute to `Add-Opens`.
> >
> >
> >[1]
> >http://openjdk.java.net/projects/jigsaw/spec/issues/
> #ReflectiveAccessToNonExportedTypes
> >[2]
> >http://openjdk.java.net/projects/jigsaw/spec/issues/
> #AwkwardStrongEncapsulation
> >[3]
> >http://mail.openjdk.java.net/pipermail/jpms-spec-experts/2016-
> October/000431.html
> >[4]
> >http://mail.openjdk.java.net/pipermail/jpms-spec-experts/2016-June/
> 000307.html
> >[5]
> >http://mail.openjdk.java.net/pipermail/jpms-spec-experts/2016-
> September/000390.html
> >[6]
> >http://mail.openjdk.java.net/pipermail/jpms-spec-experts/2016-
> September/000392.html
> >[7]
> >
http://mail.openjdk.java.net/pipermail/jigsaw-dev/2016-September/009370.html

> >[8]
> >http://mail.openjdk.java.net/pipermail/jigsaw-dev/2016-July/008637.html
> >[9]
> >http://mail.openjdk.java.net/pipermail/jigsaw-dev/2016-July/008641.html
> >[a]
> >http://mail.openjdk.java.net/pipermail/jigsaw-dev/2016-July/008727.html
> >[b]
> >
http://mail.openjdk.java.net/pipermail/jigsaw-dev/2015-December/005745.html

> >[c]
> >http://mail.openjdk.java.net/pipermail/jpms-spec-experts/2016-
> September/000391.html
> 
> -- 
> Sent from my Android device with K-9 Mail. Please excuse my brevity.
> 




More information about the jpms-spec-experts mailing list