Glyn Normington wrote:

*Bryan Atsatt <[EMAIL PROTECTED]>* wrote on 31/05/2007 23:52:08:

 > I'd like to continue this discussion separately from the
 > 291/import-by-package thread :^).
 >
 > To recap, the spec (sections 1.2, 6.1, 6.4) talks about using separate
 > Repository instances as a means to provide isolation for environments
 > like EE and Applets.
 >
 > I believe this is an oversight--actually, a holdover from the ideas I
 > put forth in the prototype, where Repository cached Module instances.
 >
 > But in the current design, that is no longer the case. Repositories hold
 > ModuleDefinitions, not Modules, so they are not a suitable isolation
 > mechanism. It is *Module* instances with their associated loaders that
 > may require isolation.

Yes, I think you're right.

 >
 > The current design also assumes that ModuleSystems cache Module
 > instances. This certainly makes more sense than Repository. However, it
 > means that systems that require isolation must arrange for separate
 > ModuleSystem instances. This seems quite awkward, and a poor abstraction.
 >
 > So I think we need to introduce an explicit notion of an isolation
 > context that:
 >
 > 1. Acts as a cache for Module instances.
 > 2. Enables multiple instances, each forming a separate Module namespace.
 > 3. Provides out-of-the box instances for bootstrap and "system" modules.
 > 4. Provides a delegation model to link instances.
 > 5. Provides a lookup mechanism identical to that of Repository.
 >
 > Any given resolution would take place within a single context. That is,
 > an initial context is selected, and while existing instances can be
 > retrieved from delegated contexts, *new* instances must be created in
 > the initial context.

This seems like a module analogue of a class loader except that new
module instances are created in the initial context, which seems a bit
peculiar. Wouldn't this mean that Java platform module instances could
get created in an application context?


Argh. You're right, and this isn't appropriate for normal sharing. I was
thinking about this from the other perspective: isolation, where new
Module instances should end up in the new context.

But even there, we don't want to allow bootstrap Modules to be
re-instantiated (although this would be useful in some cases, if it
could be made to work).

Hmm. In my original model, binding a Module to a "context" (Repository,
in that case) was natural because, like Class.getClassLoader(),
ModuleDefinition.getRepository() allowed you to get the "defining"
context easily, so recursive resolution could always use the correct
context.

Given the current design, my premise was that it doesn't seem to make
any sense to bind a Module cache directly to a Repository when
Repository.find() returns ModuleDefinition. And ModuleSystem.getModule()
requires caching behavior, so I think this might have thrown me off track.

I'm guessing now that I've just not thinking about this the right way
(and the spec is misleading?). I had forgotten a conversation Stanley
and I had on this topic, which may be lurking somewhere between the
lines of the spec.

Stanley's idea was that a Repository implementation could return a
*copy* of a ModuleDefinition created by a different Repository. Such a
Repository would in fact form an isolation context. And caching Module
instances becomes trivial: each definition can just have one as a field.

While there are definite lifecycle issues here between such repository
instances, they are probably solvable.

And then we really *do* have an exact analogue of the loader delegation
model, in which:

   Repository       ~= ClassLoader
   ModuleDefinition ~= Class
   Module           ~= Object

Isolation of classes requires different Class instances. So isolation of
 modules requires different ModuleDefinition instances.

But this puts us back awfully close to my original design.


 >
 > To enforce this model, and to simplify the api, I believe the context
 > should directly expose a "resolve" convenience method (which simply
 > delegates the actual implementation).
 >
 > Here is a strawman API, with a simple parent-first delegation model:
 >
 > public abstract class ModuleContext {
 >
 >      // Get the context used to resolve JRE modules.
 >      public static ModuleContext getBootstrapContext(){...};
 >
 >      // Get the context used to resolve 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() {...}
 >
 >      // Add a new Module instance to the cache. Throw an error
 >      // if an existing module contains the same definition.
 >      public abstract void addModule(Module module);
 >
 >      // Find cached Module instances. Must check parent first.
 >      public abstract List<Module> findModules(Query query);
 >
 >      // Remove a Module.
 >      public abstract boolean remove(Module module);
 >
 >      // Remove all Modules created from the specified definition.
 >      public static void removeAll(ModuleDefinition def) {...}
 >
 >      // Resolve a definition.
 >      public Module resolve(ModuleDefinition def) {
 >          ModuleSystem ms = def.getModuleSystem();
 >          return ms.resolve(def, this);
 >      }

Using module context as a resolution context seems to imply that
concurrent resolutions need to be serialised to avoid interference and
so some synchronisation needs adding. Or maybe the idea is to use a
module context instance per resolution, but that wouldn't fit with a
module context being a cache spanning multiple resolutions.

Well, regardless of the resolution mechanism, there must be some
serialization in the process, else how will sharing occur? If two
modules both depend on X, and X hasn't been instantiated, we must ensure
that only 1 thread does the instantiation, and any others see the result.


 >
 >      // 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 would create an appropriate subtype and call
 > setBootstrapContext(). JRE module instances would be cached here.
 >
 > The launcher would create a child context and call setSystemContext().
 > The main module and any of its dependencies would be resolved here.
 >
 > An EE/Applet (or similar) environment can create/remove/use new contexts
 > as needed for application isolation. Note that the these new contexts
 > need not have the system context in their parent chain. But they do need
 > the bootstrap context so the code should enforce that.
 >
 > The resolve() method here assumes that ModuleSystem.getModule() is
 > replaced by:
 >
 > ModuleSystem {
 >     ...
 >     public abstract Module resolve(ModuleDefinition def,
 >                                    ModuleContext ctx);
 > }
 >
 > The ModuleSystem is no longer required to directly cache Modules, but to
 > implement the resolution algorithm using the supplied context.
 >
 > ModuleSystem releaseModule() and disableModuleDefinition() methods just
 > use remove() and removeAll(), respectively.
 >
 > This class could easily be made concrete, as the implementations are
 > obviously simple. Regardless, for extensibility the actual types used at
 > runtime should be under the control of the JVM/JRE and those systems
 > that need to create new contexts.
 >
 > And, we might want to tie other existing ideas in here. For example,
 > ModuleContext could be the home for "default" policies:
 >
 >     public abstract ImportPolicy getImportPolicy();
 >     public abstract ImportOverridePolicy getImportOverridePolicy();
 >
 > Thoughts?

As I've pointed out on this list before, custom import policies have
many disadvantages including making it very hard to write management
systems that can infer dependencies before modules are installed in the
module system. Moving custom import policies out of individual modules
into the repository (which came up in discussion with Stanley and Michal
at JavaOne) or module context, leaving purely declarative metadata
behind in individual modules, would solve this problem. Then the default
policy would simply be the one returned by default. (I guess the same is
true for custom import override policies, which I'm also not fond of,
particularly at the module level.)

Yep, I have also suggested moving this kind of logic to Repository. I
don't much like bundled ImportPolicies, even though Stanley has defended
most of the issues well, simply because of the extra complexity.

 >
 > // 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/






Reply via email to