Relationship to JSR 291 [was: Re: Bryan's comments]

Bryan Atsatt bryan.atsatt at oracle.com
Wed May 30 11:11:02 PDT 2007


Responses inline, and a few clarifications here (I was a bit tired when
I finished this last night :^)...

The main point I was trying to make is that resolution must occur within
a specific context, but I don't think my example APIs showed that well.
I was assuming that ImportResolver had a ModuleContext as a field, but
we can make this much cleaner and easier to understand by passing it as
an argument:

public abstract class ImportResolver {
     public abstract Module resolve(ModuleDefinition def,
                                    ModuleContext ctx);
}

And we can really tie this together by adding a convenience method to
ModuleContext:

public abstract class ModuleContext {
     ...
     public Module resolve(ModuleDefinition def) {
         return getImportResolver().resolve(def, this);
     }
}

Now resolution becomes simply:

     context.resolve(definition);


(I also left out any use of the ImportPolicy, as it isn't yet clear to
me that it should remain a separate type in this model.)

// Bryan

Glyn Normington wrote:
>
> *Bryan Atsatt <bryan.atsatt at ORACLE.COM>* wrote on 30/05/2007 07:57:59:
>
>  > Andy Piper wrote:
>  >  > At 23:19 25/05/2007, Stanley M. Ho wrote:
>  >  >> Anyway, it seems the EG consensus so far is to not add import package
>  >  >> support. If any EG member disagrees, please speak up.
>  >  >
>  >  > Well, it depends on what the solution for enabling interoperation
>  >  > with JSR 291 is.
>  >  > Our requirement is that there must be a solution, if that requires
>  >  > import package, so be it. If not then not.
>  >
>  > Exactly.
>  >
>  > I think we can all agree that, at minimum, interoperation means that
>  > classes and resources are sharable *across* ModuleSystems at runtime.
>  >
>  > Which implies that *import dependencies must be resolvable across
>  > multiple ModuleSystem instances*. (BTW, I think we should change the
>  > state name "PREPARING" to "RESOLVING" in 7.2.1.)
>
> Agreed. We must avoid the trap of thinking that module system interop.
> can be achieved by exposing class loaders (as loadClass will happily
> load unexported classes).
>
>  >
>  > So the open issue is the richness of the import "language": must we
>  > support only lowest-common-denominator, or can we do better without
>  > over-complicating the design?
>  >
>  > I for one would like to be able to have a single module express
>  > dependencies on modules from both the same and different ModuleSystems,
>  > *using the standard semantics of each*. This may be reaching too far,
>  > but we should at least explore it seriously while we figure out what
>  > interop means here...
>
> At this point, I feel that is likely to be reaching too far, but I'm
> happy to play along and see what we can learn along the way.
>
>  >
>  >
>  > BASICS
>  >
>  > So far, we only know of two different import semantics: module-name, and
>  > package-name. For discussion, let's call these:
>  >
>  > a. import-module
>  > b. import-package
>  >
>  > So, to start, we could:
>  >
>  > 1. Support declaration of both import types. If 294 supports imports at
>  > all, it should be relatively easy to support both, since a superpackage
>  >   name is a module name, and it contains member package names. (Compiler
>  > support is clearly the critical issue here, but it will obviously
>  > require use of the 277 runtime, so the import *type* should be
>  > transparent to it.) At worst, we'd need two new annotation types.
>
> A superpackage name is a deployment module name in the JSR 277 model of
> one superpackage per deployment module, but I don't see any reason why a
> JSR 291 deployment module should not contain more than one superpackage.
> So if 294 were to support import, then its import-module would really be
> a superpackage import rather than a development module import.

If we end up with nested superpackages, might it make sense to model
multiple superpackages by enclosing them in a single top-level one?

>
>  >
>  > 2. Provide API for both import types (e.g. ImportDependency has
>  > getModuleName() and getPackageName() methods, one of which will return
>  > null on a given instance).
>  >
>  > However, we know these are necessary but not sufficient. Leaving aside
>  > the resolution issue for a moment, support for import-package also
>  > suggests that we:
>  >
>  > 3. Enable a single module to declare different versions for each of its
>  > member packages.
>  >
>  > 4. Enable efficient Repository lookup by package name.
>  >
>  > I think these are relatively easy (but *could* be considered optional).
>  >
>  > We also need:
>  >
>  > 5. Standard Query types for lookup by module and package name.
>  >
>  >
>  > EXISTING DEPENDENCY RESOLUTION MODEL
>  >
>  > The more interesting issue is dependency resolution. But this hasn't
>  > been discussed in any real detail, so lets do so before talking further
>  > about import-package. To simplify this discussion, I'm ignoring
>  > bundled/custom import policies for now...
>  >
>  > Resolution in the current spec is delegated to the associated
>  > ModuleSystem instance (7.2.2 #8). While the details are not spelled out,
>  > the expectation appears to be that
>  > ModuleSystem.getModule(ModuleDefinition) must:
>  >
>  > - Select an initial repository. Call getRepository() on the input
> parameter.
>  >
>  > Then, for each ImportDependency of the definition:
>  >
>  > - Select a matching definition. Construct a Query from the
>  > ImportDependency and use Repository.find() to lookup a matching
>  > ModuleDefinition.
>  >
>  > - Get an instance. Use def.getModuleSystem().getModule(def). The
>  > ModuleSystem is expected to return a cached instance if available, or
>  > create/cache/return one if not.
>
> I think there also needs to be some 'resolution context' object which
> explicitly denotes a particular resolution so that each module system
> can keep track of the state of a resolution. This is required when two
> or more imports of a given module from another module system need to
> resolve to the same module instance. A resolution context may also be
> needed for back-tracking when a set of module instances created earlier
> in resolution turn out not to satisfy all the necessary constraints.

Yes. I had (briefly :^) thought that we need only a List<Module> as
field in the ImportResolver to hold the resolved modules as we go. But
probably we need to keep some additional state along with each Module,
so a new type may be required. Let's call it ResolutionContext, and
create one at the start of each resolution:

public abstract class ImportResolver {

     public Module resolve(ModuleDefinition def, ModuleContext modCtx) {
         ResolutionContext resCtx = createResolutionContext();
         return resolve(def, modCtx, resCtx);
     }

     protected abstract ResolutionContext createResolutionContext();

     protected abstract Module resolve(ModuleDefinition def,
                                       ModuleContext modCtx,
                                       ResolutionContext resCtx);
}

Does this approach make sense? Any thoughts on details for
ResolutionContext?

>
>  >
>  >
>  > (TBD: The PlatformBinding must be taken into account somehow during
>  > selection. ModuleDefinition must include an accessor for it, and either
>  > Repository.find() should implicitly filter them, or the caller must
>  > construct a Query which will do so. I think we should add a
>  > CURRENT_PLATFORM constant to Query, which will evaluate to true if no
>  > binding is present in a definition.)
>  >
>  > (The spec also talks about Repository as the mechanism of isolation
>  > (6.4). This was the case in the prototype, where the repository itself
>  > provided caching. It doesn't appear to work with the current design.
>  > There is no need that I can see to isolate ModuleDefinition
>  > instances--it is Module instances with their associated loaders that may
>  > require isolation.)
>  >
>  > (Also note that if ImportDependency was itself a Query subclass, there
>  > would be no need to do any mapping. And since the ModuleDefinition
>  > subclass must produce ImportDependency instances, it can even produce
>  > more specialized Query instances if desired.)
>  >
>  >
>  > REFINEMENT
>  >
>  > I think we can improve on the existing model in several ways:
>  >
>  > A. Provide a model for Module isolation (e.g. for EE, Applets, etc).
>  >
>  > B. Encapsulate all selection logic in a single mechanism.
>  >
>  > C. Eliminate the overhead of the repository lookup when a cached
>  > instance exists.
>  >
>  > Let me propose a new class that encapsulates the caching logic, enables
>  > lookup using Query, and supports multiple instances for isolation:
>  >
>  > public abstract class ModuleContext {
>  >
>  >      // Get the context used to define JRE modules.
>  >      public static ModuleContext getBootstrapContext(){...};
>  >
>  >      // Get the context used to define the main module.
>  >      public static ModuleContext getSystemContext(){...};
>  >
>  >      // Get all contexts.
>  >      public static List<ModuleContext> getContexts() {...};
>  >
>  >      // Add a new context.
>  >      public static void addContext(ModuleContext ctx) {...}
>  >
>  >      // Remove a context (error if == default).
>  >      public static boolean removeContext(ModuleContext ctx) {...}
>  >
>  >      // Get the parent context (null if bootstrap).
>  >      public ModuleContext getParentContext(){...}
>  >
>  >      // Get the name of this context.
>  >      public String getContextName() {...}
>  >
>  >      // Create a new Module instance and store it in the cache.
>  >      public abstract Module createModule(ModuleDefinition def);
>  >
>  >      // Find cached Module instances. Must check parent first.
>  >      public abstract List<Module> findModules(Query query);
>  >
>  >      // Set the context used for JRE modules.
>  >      static void setBootstrapContext(ModuleContext ctx){...}
>  >
>  >      // Set the context used to define the main module.
>  >      static void setSystemContext(ModuleContext ctx){...}
>  > }
>  >
>  > The JVM will create an appropriate subtype and call
>  > setBootstrapContext(). The launcher will create a child context and call
>  > setSystemContext(). An EE (or similar) environment can create/remove new
>  > contexts as needed for application isolation.
>  >
>  > And the resolution algorithm can now check the cache *first*, before
>  > doing a repository lookup, using the same mechanism in both. Query
>  > should be used to express *all* selection criteria (including
>  > attributes, which we have not yet directly supported).
>  >
>  > Caches are no longer tied to ModuleSystem instances.
>  > ModuleSystem.getModule() can become simply createModule(). The normal
>  > implementation of ModuleContext.createModule() just calls
>  > ModuleSystem.createModule() and caches/returns the result.
>  >
>  >
>  > This class could easily be made concrete, but it may be useful to
>  > support subtypes for specialization (e.g. event generation, lifecycle
>  > management, specialized diagnostics, etc).
>  >
>  >
>  > RESOLUTION MODELS
>  >
>  > The current design requires that each ModuleSystem provide its own
>  > resolution logic, and that each definition will be resolved by its
>  > owning ModuleSystem. This model appears to provide flexibility for
>  > significant differences in implementation, but we really don't know
>  > enough at this point. Perhaps only an actual second implementation will
>  > tell us if this provides useful flexibility.
>  >
>  > It wouldn't surprise me if we have to keep tightening the spec as we go,
>  > in order to remove inconsistencies that arise from the separate
>  > algorithms. And this may eliminate flexibility to the point where it is
>  > no longer useful. Much worse, we may not even discover this until after
>  > the spec and RI are released, if that second implementation (e.g. OSGi)
>  > is not completed beforehand.
>  >
>  > We should at least consider the obvious alternative: one algorithm (to
>  > rule them all :^). And I don't mean one hard-coded algorithm, I mean one
>  >   replaceable, extensible class, such as:
>  >
>  > public abstract class ImportResolver {
>  >      public abstract Module resolve(ModuleDefinition def);
>  > }
>  >
>  > And we add a method to ModuleContext to retrieve an instance:
>  >
>  > public abstract class ModuleContext {
>  >      ...
>  >      public abstract ImportResolver getImportResolver();
>  > }
>  >
>  > (Note that ImportResolver is now in a position to subsume the
>  > functionality of both VisibilityPolicy and ImportOverridePolicy.)
>  >
>  > Repository usage is encapsulated within the implementation of the
>  > resolve() method. The full resolution algorithm becomes:
>  >
>  >     context.getImportResolver().resolve(definition);
>  >
>  > The launcher uses the "system" context for this. EE, Applets, etc. make
>  > and use their own distinct, isolated instances.
>  >
>  >
>  > RESOLUTION WITH IMPORT-NAME AND IMPORT-PACKAGE
>  >
>  > With this scaffolding in place we can easily take a phased approach to
>  > supporting import-package: the initial implementation simply does not
>  > support it at runtime.
>
> A phased approach would be particularly beneficial if the initial phase
> could be delivered as part of Java 7 and subsequent phases implemented
> strictly on top of Java 7. But getting the API right up front might be
> tricky unless we can spot some really good abstractions or prototype the
> later phases sufficiently well. Is that the kind of phasing you had in
> mind?

Yes. It would be *extremely* useful to have an OSGi
implementation/prototype well under way before Java 7 is completed, so
that we can fine tune the model as we learn.

>
>  >
>  > A subsequent implementation may support import-package, but only within
>  > the boundaries of the same ModuleSystem.
>  >
>  > And a full blown implementation may support import-package across
>  > ModuleSystems.
>  >
>  > We can build in support for selecting/configuring the ImportResolver as
>  > a service, just as we plan to do with ModuleSystem (and Repository, I
>  > presume).
>  >
>  >
>  > And maybe, just maybe, we can find a way to abstract and re-use the
>  > mature resolution logic from the OSGi reference implementation *as* the
>  > one implementation to rule them all.
>  >
>  > // Bryan
>
> Glyn
>
>
> ------------------------------------------------------------------------
>
> /
> /
>
> /Unless stated otherwise above:
> IBM United Kingdom Limited - Registered in England and Wales with number
> 741598.
> Registered office: PO Box 41, North Harbour, Portsmouth, Hampshire PO6 3AU/
>
>
>
>
>
>



More information about the jsr277-eg-observer mailing list