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