Strawman Proposal for JSR 294 Superpackages

Draft 2007-03-16

This is a straw-man proposal for JSR 294 Superpackages written by Andreas Sterbenz and Alex Buckley. It is based on the concepts described by Gilad Bracha in the JSR submission and the JSR 277 whitepaper by Gilad Bracha and Michal Cierniak.

As described in the JSR 294 submission (Improved Modularity Support in the Java Programming Language), we propose to address two problems: information hiding and separate compilation. This document describes a proposal for information hiding using a new entity called superpackage. Separate compilation will be the topic of another document.

Problem Statement

The current Java mechanisms for information hiding work fine for projects that fit into a single package. Types that are implementation details can be declared with default (aka package private) access to hide them from code outside the project. However, if a project is too large to fit into a single package, Java does not provide a suitable encapsulation mechanism. A type has to be declared public to access it from the different packages of the project, but that also makes it available to packages outside the project.
Note:
These concepts are sometimes referred to as a development module. It is a different - although related - problem to that of deployment modules, which are the purview of JSR 277. JSR 294 is intended to be independent of the deployment mechanism and can be used with JAR files and existing ClassLoaders, JSR 277 modules, or other deployment methods.

Goals

The following design goals were considered when addressing the problem of information hiding described above:
  1. The access control model defined by the JLS and JVMS is the appropriate unit to extend in order to accommodate projects larger than a single Java package.
  2. The extensions to the access control model should be simple and consistent with the existing model.
  3. Access control information for an entity larger than a single Java package should be declared in Java source files so it can be processed and enforced by javac.
  4. The new model should to serve as the basis for deployment modules, but it should also be usable on its own without requiring modifications to the deployment mechanisms.
  5. Existing projects should be able to take advantage of the new access control mechanisms by making minimal changes to Java source files.

Superpackages

The solution is a new entity called superpackage. A superpackage can be thought of as a named collection of one or more packages or superpackages and their types. Public types can be declared as exported to make them accessible outside the superpackage. Public types in a superpackage that are not exported are accessible only to other types in the superpackage. A type can be a member of at most one superpackage.

A superpackage is declared in a Java source file. It defines:

A superpackage contained within another superpackage is called a nested superpackage. Please refer to the section below for explanation and rationale.

This source file is processed by a Java compiler similarly to other source files. The result of the compilation is a .superpackage file, analogous to a .class file. The format of the superpackage file is TBD.

Note: in earlier discussions the superpackage file was called a module file. The proposed name was changed to avoid confusion with the Module Archive format defined by JSR 277.

Example

As an illustration, we define a simple superpackage. Note that the syntax is tentative and certain to evolve.

File example/foo/myapp/super-package.java:

// Declare the superpackage
superpackage example.foo.myapp {

    // all types in the listed packages are members of this superpackage
    member package example.foo.myapp, example.foo.myapp.processing;

    // list of exported types
    export example.foo.myapp.Main, example.foo.myapp.Helper;

}
File example/foo/myapp/Main.java
package example.foo.myapp;

public class Main {
    public Main() { ... }
}
File example/foo/myapp/Parser.java
package example.foo.myapp;

public class Parser {
    public Parser() { ... }
}
[Other files omitted]

Explanation

The superpackage source file (example/foo/myapp/super-package.java) defines the members of the superpackage example.foo.myapp as the packages example.foo.myapp and example.foo.myapp.processing.

The exported types are example.foo.myapp.Main and example.foo.myapp.Helper. These are the only types that are accessible outside the superpackage example.foo.myapp. The class example.foo.myapp.Parser is a public non-exported class. That means that it is not accessible by types outside the superpackage example.foo.myapp. The list of exported types may include * wildcards to export an entire package. The wildcard is expanded by the Java compiler to full list of type names.

As with the existing Java access control mechanisms, the superpackage access control is enforced by the Java compiler at compilation time and the Java virtual machine at runtime (JVMS 5.4.4).

Note that this proposal avoids having the source of each Java type declare its superpackage membership. This approach was chosen in order to make it easier to migrate existing Java source code to superpackages. However, Java class files that are part of a superpackage will contain an appropriate attribute that indicates the name of the superpackage. It is assumed that the Java compiler is able to determine superpackage membership from the superpackage source file or in some other way, for example using commandline arguments.

Runtime aspects

A superpackage file is reified at runtime using a reflective API in the style of java.lang.Class. This class java.lang.Superpackage allows retrieval of the information declared in the superpackage source file, such as the names of exported types. A method getSuperpackage() is added to java.lang.Class, which returns the superpackage object associated with a class, or null if the class is not a member of a superpackage.

A superpackage file is loaded via a ClassLoader. A superpackage is always defined by the same ClassLoader as its member classes.

Nested Superpackages

A superpackage can include other superpackages as its members. A superpackage that is a member of another superpackage is called a nested superpackage. The superpackage containing it is called its enclosing superpackage. Nested superpackage are useful in large projects where information hiding between internal components is desired. The nesting scheme is recursive with no practical limit on the level of nesting. Note that it is expected that a superpackage and all its nested superpackages will form one deployment unit.

A nested superpackage is declared by listing the name of the enclosing superpackage in its superpackage source file, and by the enclosing superpackage listing the nested superpackage as a member.

The enclosing superpackage can declare a nested superpackages as exported. If exported, the exported types of the nested superpackage are accessible outside the enclosing superpackage. If not exported, the exported types of the nested superpackage are accessible to other types in the enclosing superpackage but not outside the enclosing superpackage.

Example

We declare a superpackage example.bar.lib which contains the package example.bar.lib and the nested superpackages example.bar.lib.net and example.bar.lib.xml.

The type example.bar.lib.Table is exported by example.bar.lib and therefore accessible by other code. The nested superpackage example.bar.lib.net is also declared as exported. This means that all types it exports - example.bar.lib.net.* - are also accessible to code outside the example.bar.lib superpackage.

The nested superpackage example.bar.lib.xml is not exported by example.bar.lib. This means that the exported types from example.bar.lib.xml are accessible to code in example.bar.lib - including its nested superpackage example.bar.lib.net - but they are not accessible to code outside of example.bar.lib.

File example/bar/lib/super-package.java:

// Declare the superpackage
superpackage example.bar.lib {

    // member packages
    member package example.bar.lib;

    // member superpackages
    member superpackage example.bar.lib.net, example.bar.lib.xml;

    // list of exported types
    export example.bar.lib.Table;

    export superpackage example.bar.lib.net;

}
File example/bar/lib/net/super-package.java:
// First nested superpackage
superpackage example.bar.lib.net member example.bar.lib {

    // member packages
    member package example.bar.lib.net, example.bar.lib.http;

    // list of exported types
    export example.bar.lib.net.*;

}
File example/bar/lib/xml/super-package.java:
// Second nested superpackage
superpackage example.bar.lib.xml member example.bar.lib {

    // member packages
    member package example.bar.lib.xml, example.bar.lib.xml.internal;

    // list of exported types
    export example.bar.lib.xml.*;

}