[jsr294-modularity-eg] Module directive types

Bob Lee crazybob at crazybob.org
Thu May 7 13:24:27 EDT 2009


I can define a grammar and write a Coin-style proposal for these ideas if
you'd like, but Doug told me I should send out the examples sooner rather
than later.

First, we have a typical module declaration containing declarative "module
directives" for Jigsaw, OSGi, and Guice.

Note: I've always thought that a module declaration would be the perfect
place to configure Guice-style dependency injection; we configure type
dependencies here, why not object dependencies? The two go hand-in-hand. I
originally thought I'd have to wait for Java 8 to build this directly into
the language, but if we make the syntax extensible, I could use it for
dependency injection configuration right away. If 294 goes with a less type
safe approach, I probably won't use it for Guice configuration because our
current Java-based approach is more straightforward and type safe.

Example usage:

import com.sun.jigsaw.permits;
import com.sun.jigsaw.provides;
import com.sun.jigsaw.requires;

import org.osgi.jsr294.export;
import org.osgi.jsr294.import;
import org.osgi.jsr294.classpath;
static import org.osgi.jsr294.Granularity.*;

import guice.bind;

module m1 {
  // Module directives

  // Jigsaw
  provides version: "3.4.5";
  provides alias: "acme" version: "3.4.5";
  requires module: "m2" version: "1.0+";
  permits module: "m3";

  // OSGi
  export name: "com.m1" version: "3.4.5";
  import granularity: BUNDLE version: ">1.0";

  // "value:" is assumed if only one operand exists.
  classpath { "m2.jar", "api.jar" };
  classpath "./classes";

  // Guice
  bind type: Foo.class annotatedWith: Red.class to: FooImpl.class;
}

Here are the module directive types (defined with @module) used for the
module directives above:

package com.sun.jigsaw;

/** Expresses a dependency on another module. */
public @module requires {
  /** Module name. */
  String module();
  /** Module version query. */
  String version();
}

/** Versions and optionally renames this module. */
public @module provides {
   /** Overrides the default module name. */
  String alias() default null;
  /** Module version. */
  String version();
}

/** Permits another module access. */
public @module permits {
   /** Module name. */
  String module();
}

 package org.osgi.jsr294;

/** Granularity of import or export. */
public enum Granularity {
  PACKAGE, BUNDLE;
}

/** Exports part of this module. */
public @module export {
   /** Name of package or module. */
  String name();
  /** Specifies the granularity of the export. */
  Granularity granularity() default Granularity.PACKAGE;
  /** Version. */
  String version();
}

/** Imports part or all of another module. */
public @module import {
   /** Name of package or module. */
  String name();
  /** Specifies the granularity of the import. */
  Granularity granularity() default Granularity.PACKAGE;
  /** Version range. */
  String version();
  /** Whether or not this import is required. */
  boolean optional() default false;
}

/** Specifies the classpath of this module. */
public @module classpath {
   /** Array of path elements. */
   String[] value();
}

 package guice;

/** Binds type T to a more specific version of T. */
public @module bind<T> {
  Class<T> type();
   Class<? extends Annotation> annotatedWith() default null;
   Class<? extends T> to() default null;
}

Further ideas:

1. Like annotations, you can already elide value: in the operand if you only
have one operand. Instead, we could always allow you to elide value: for the
*first* operand. With some adjustments to the module directive types, our
example module could look like this:

module m1 {
  // Module directives

  // Jigsaw
  provides "3.4.5";
  provides alias: "acme" version: "3.4.5";
  requires "m2" version: "1.0+";
  permits "m3";

  // OSGi
  export name: "com.m1" version: "3.4.5";
  import BUNDLE version: ">1.0";

  // "value:" is assumed if only one operand exists.
  classpath { "m2.jar", "api.jar" };
  classpath "./classes";

  // Guice
  bind Foo.class annotatedWith: Red.class to: FooImpl.class;
}

Instead of forcing the first attribute name to be value, we could use a
qualifier instead:

/** Binds type T to a more specific version of T. */
public @module bind<T> {
  *default* Class<T> type();
  Class<? extends Annotation> annotatedWith() default null;
  Class<? extends T> to() default null;
}

2. We could elide the operand for boolean-typed attributes.  For example:

import name: "com.m2" version: ">1.0" optional;
import name: "com.m3" version: "2" !optional;

Of course, this could result in ambiguity if used for the first operand, in
which case we should generate an error and force the user to explicitly
specify the name and value.

3. We could support nested directives. The nested directives would be
enclosed in curly braces as the final operand. For example:

bind Foo.class to: FooImpl.class {
  bind Bar.class to: BarThatOnlyFooShouldUse.class;
};

If the directive type has a value attribute with an array type and the
directive has only nested directives as the operand, we wouldn't be able to
tell what the user intended, and we should generate an error.

4. We should support constants. For example:

module m1 {
  String VERSION = "3.4.5";
  provides VERSION;
  export name: "com.m1" version: VERSION;
}

The compiler can tell the difference between a constant and a directive
based on the type.

5. Java desperately needs literals for generic types. Guice has worked
around their absence with
TypeLiteral<http://google-guice.googlecode.com/svn/trunk/latest-javadoc/com/google/inject/TypeLiteral.html>.
I know of two JSRs that have already copied this idea into their own APIs
(299 and JAX-RS). I used Class<?> in the directive type examples above, but
we probably want to define a new type (something like
javax.lang.model.TypeMirror) that can be used easily at compile and run
time.

6. An astute observer might ask how we can reference type literals for types
that come from modules. The answer is that we defer resolution and
validation of these types until after the modules have all been resolved.
Obviously, a directive that imports a module can't directly reference types
from that module. We can generate an error when we encounter these cases.

Thanks,
Bob
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://cs.oswego.edu/pipermail/jsr294-modularity-eg/attachments/20090507/888d7e55/attachment-0001.html>


More information about the jsr294-modularity-eg mailing list