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.

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.

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);
    }

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

// Bryan

Reply via email to