changeset 12720:d82cd22736fd

Initial support for automatic modules
author alanb
date Fri, 15 May 2015 13:02:59 +0100
parents 6404d89dad9e
children 390730357dbf
files src/java.base/share/classes/java/lang/module/Configuration.java src/java.base/share/classes/java/lang/module/Layer.java src/java.base/share/classes/java/lang/module/ModuleDescriptor.java src/java.base/share/classes/java/lang/module/ModuleFinder.java src/java.base/share/classes/java/lang/module/ModulePath.java src/java.base/share/classes/java/lang/module/Resolver.java test/jdk/jigsaw/etc/automaticmodules/basic.sh test/jdk/jigsaw/etc/automaticmodules/src/httpserver/http/HttpServer.java test/jdk/jigsaw/etc/automaticmodules/src/httpserver/module-info.java test/jdk/jigsaw/etc/automaticmodules/src/logging/logging/Logger.java test/jdk/jigsaw/etc/automaticmodules/src/logging/module-info.java test/jdk/jigsaw/etc/automaticmodules/src/test/module-info.java test/jdk/jigsaw/etc/automaticmodules/src/test/test/Main.java
diffstat 13 files changed, 444 insertions(+), 29 deletions(-) [+]
line wrap: on
line diff
--- a/src/java.base/share/classes/java/lang/module/Configuration.java	Thu May 14 12:16:00 2015 -0700
+++ b/src/java.base/share/classes/java/lang/module/Configuration.java	Fri May 15 13:02:59 2015 +0100
@@ -45,7 +45,7 @@
  *         ModuleFinder.of(dir1, dir2, dir3);
  *
  *     Configuration cf =
- *         Configuration.resolve(ModuleFinder.nullFinder(),
+ *         Configuration.resolve(ModuleFinder.empty(),
  *                               Layer.bootLayer(),
  *                               finder,
  *                               "myapp")
--- a/src/java.base/share/classes/java/lang/module/Layer.java	Thu May 14 12:16:00 2015 -0700
+++ b/src/java.base/share/classes/java/lang/module/Layer.java	Fri May 15 13:02:59 2015 +0100
@@ -26,6 +26,7 @@
 package java.lang.module;
 
 import java.lang.reflect.Module;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.Map;
@@ -34,6 +35,7 @@
 
 import sun.misc.JavaLangModuleAccess;
 import sun.misc.JavaLangReflectAccess;
+import sun.misc.Modules;
 import sun.misc.SharedSecrets;
 
 /**
@@ -48,7 +50,7 @@
  *         ModuleFinder.of(dir1, dir2, dir3);
  *
  *     Configuration cf =
- *         Configuration.resolve(ModuleFinder.nullFinder(),
+ *         Configuration.resolve(ModuleFinder.empty(),
  *                               Layer.bootLayer(),
  *                               finder,
  *                               "myapp");
@@ -114,7 +116,38 @@
     public static Layer create(Configuration cf, ClassLoaderFinder clf) {
         Objects.requireNonNull(cf);
         Objects.requireNonNull(clf);
-        return new Layer(cf, reflectAccess.defineModules(cf, clf));
+        Layer layer = new Layer(cf, reflectAccess.defineModules(cf, clf));
+
+        // Update the readability graph so that every automatic module in the
+        // newly created Layer reads every other module.
+        // We do this here for now but it may move.
+        cf.descriptors().stream()
+                .filter(ModuleDescriptor::isAutomatic)
+                .map(ModuleDescriptor::name)
+                .map(layer::findModule)
+                .forEach(m -> layer.fixupAutomaticModule(m));
+
+        return layer;
+    }
+
+    /**
+     * Changes an automatic module to be a "loose" module, and adds a
+     * read edge to every module in the Layer (and all parent Layers).
+     */
+    private void fixupAutomaticModule(Module autoModule) {
+        assert autoModule.getDescriptor().isAutomatic();
+
+        // automatic modules read all unnamed modules
+        // (use Modules.addReads to skip permission check)
+        Modules.addReads(autoModule, null);
+
+        // automatic modules read all modules in this, and parent, layers
+        Layer l = this;
+        do {
+            Collection<Module> modules = l.nameToModule.values();
+            modules.forEach(m -> Modules.addReads(autoModule, m));
+            l = l.parent();
+        } while (l != Layer.emptyLayer());
     }
 
     /**
--- a/src/java.base/share/classes/java/lang/module/ModuleDescriptor.java	Thu May 14 12:16:00 2015 -0700
+++ b/src/java.base/share/classes/java/lang/module/ModuleDescriptor.java	Fri May 15 13:02:59 2015 +0100
@@ -264,6 +264,9 @@
     private final Set<String> uses;
     private final Map<String, Provides> provides;
 
+    // Indicates if synthesised for a JAR file found on the module path
+    private final boolean automatic;
+
     // "Extended" information, added post-compilation
     private final Optional<Version> version;
     private final Optional<String> mainClass;
@@ -272,6 +275,7 @@
     private final Optional<DependencyHashes> hashes;
 
     private ModuleDescriptor(String name,
+                             boolean automatic,
                              Map<String, Requires> requires,
                              Set<String> uses,
                              Map<String, Exports> exports,
@@ -283,6 +287,7 @@
     {
 
         this.name = requireModuleName(name);
+        this.automatic = automatic;
 
         Set<Requires> rqs = new HashSet<>(requires.values());
         assert (rqs.stream().map(Requires::name).sorted().distinct().count()
@@ -324,6 +329,13 @@
     }
 
     /**
+     * <p> Indicates if this is an automatic module. </p>
+     */
+    /* package */ boolean isAutomatic() {
+        return automatic;
+    }
+
+    /**
      * <p> The dependences of this module </p>
      *
      * @return  A possibly-empty unmodifiable set of {@link Requires} objects
@@ -404,10 +416,13 @@
 
     /**
      * A builder used for building {@link ModuleDescriptor} objects.
+     *
+     * @apiNote Should Builder be final?
      */
     public static class Builder {
 
-        String name;
+        final String name;
+        final boolean automatic;
         final Map<String, Requires> requires = new HashMap<>();
         final Set<String> uses = new HashSet<>();
         final Map<String, Exports> exports = new HashMap<>();
@@ -421,7 +436,12 @@
          * Initializes a new builder.
          */
         public Builder(String name) {
+            this(name, false);
+        }
+
+        /* package */ Builder(String name, boolean automatic) {
             this.name = name;
+            this.automatic = automatic;
         }
 
         /**
@@ -539,6 +559,7 @@
         public ModuleDescriptor build() {
             assert name != null;
             return new ModuleDescriptor(name,
+                                        automatic,
                                         requires,
                                         uses,
                                         exports,
@@ -571,6 +592,7 @@
             return false;
         ModuleDescriptor that = (ModuleDescriptor)ob;
         return (name.equals(that.name)
+                && automatic == that.automatic
                 && requires.equals(that.requires)
                 && uses.equals(that.uses)
                 && exports.equals(that.exports)
@@ -588,6 +610,7 @@
         int hc = hash;
         if (hc == 0) {
             hc = name.hashCode();
+            hc = hc * 43 + Boolean.hashCode(automatic);
             hc = hc * 43 + requires.hashCode();
             hc = hc * 43 + uses.hashCode();
             hc = hc * 43 + exports.hashCode();
--- a/src/java.base/share/classes/java/lang/module/ModuleFinder.java	Thu May 14 12:16:00 2015 -0700
+++ b/src/java.base/share/classes/java/lang/module/ModuleFinder.java	Fri May 15 13:02:59 2015 +0100
@@ -39,7 +39,7 @@
  *
  * <p> An important property is that a {@code ModuleFinder} admits to
  * at most one module with a given name. A {@code ModuleFinder} that
- * finds modules in sequence of directories for example, will locate the first
+ * finds modules in a sequence of directories for example, will locate the first
  * occurrence of a module and ignores other modules of that name that appear in
  * directories later in the sequence. </p>
  *
--- a/src/java.base/share/classes/java/lang/module/ModulePath.java	Thu May 14 12:16:00 2015 -0700
+++ b/src/java.base/share/classes/java/lang/module/ModulePath.java	Fri May 15 13:02:59 2015 +0100
@@ -195,12 +195,12 @@
             if (ze == null) {
                 throw new IOException(MODULE_INFO + " is missing: " + file);
             }
-            // jmod URI - syntax not defined yet
-            URI location = URI.create("jmod:" + file.toUri() + "!/");
             ModuleDescriptor md;
             try (InputStream in = zf.getInputStream(ze)) {
                 md = ModuleDescriptor.read(in, () -> jmodPackages(zf));
             }
+            // jmod URI - syntax not defined yet
+            URI location = URI.create("jmod:" + file.toUri() + "!/");
             HashSupplier hasher = (algorithm) -> Hasher.generate(file, algorithm);
             return ModuleReferences.newModuleReference(md, location, hasher);
         }
@@ -216,21 +216,61 @@
     }
 
     /**
+     * Derive a module name from the given JAR file name.
+     *
+     * @apiNote This needs to move to somewhere where it can be used by tools,
+     * maybe even a standard API if automatic modules are a Java SE feature.
+     */
+    private String deriveModuleNameFromJarName(String name) {
+        // drop .jar
+        name = name.substring(0, name.length()-4);
+
+        // drop -${VERSION}
+        int index = name.lastIndexOf('-');
+        if (index > 0) {
+            String tail = name.substring(index+1);
+            if (tail.matches("^(\\d+\\.)?(\\d+\\.)?(\\*|\\d+)$")) {
+                name = name.substring(0, index);
+            }
+        }
+
+        // drop all non-alphanumeric chars
+        return name.replaceAll("[^A-Za-z0-9]", "");
+    }
+
+    /**
      * Returns a {@code ModuleReference} to represent a module jar on the
      * file system.
      */
     private ModuleReference readJar(Path file) throws IOException {
         try (JarFile jf = new JarFile(file.toString())) {
+
+            ModuleDescriptor md;
             JarEntry entry = jf.getJarEntry(MODULE_INFO);
             if (entry == null) {
-                // not a modular jar
-                return null;
+
+                // no module-info.class so treat as automatic module
+                // Builder throws IAE if module name is empty
+                String fn = file.getFileName().toString();
+                String mn = deriveModuleNameFromJarName(fn);
+                ModuleDescriptor.Builder builder
+                    = new ModuleDescriptor.Builder(mn, true).requires("java.base");
+
+                // all packages are exported
+                jarPackages(jf).stream().forEach(p -> builder.exports(p));
+
+                // TBD: Need to scan META-INF/services for any configuration files
+
+                md = builder.build();
+
+            } else {
+                md = ModuleDescriptor.read(jf.getInputStream(entry),
+                                           () -> jarPackages(jf));
             }
-            // jar URI
+
             URI location = URI.create("jar:" + file.toUri() + "!/");
-            ModuleDescriptor md = ModuleDescriptor.read(jf.getInputStream(entry),
-                                                        () -> jarPackages(jf));
             HashSupplier hasher = (algorithm) -> Hasher.generate(file, algorithm);
+
             return ModuleReferences.newModuleReference(md, location, hasher);
         }
     }
@@ -254,14 +294,14 @@
      * on the file system.
      */
     private ModuleReference readExploded(Path dir) throws IOException {
-        Path file = dir.resolve(MODULE_INFO);
-        if (Files.notExists((file))) {
+        Path mi = dir.resolve(MODULE_INFO);
+        if (Files.notExists(mi)) {
             // no module-info in directory
             return null;
         }
         URI location = dir.toUri();
         ModuleDescriptor md;
-        try (InputStream in = Files.newInputStream(file)) {
+        try (InputStream in = Files.newInputStream(mi)) {
             md = ModuleDescriptor.read(new BufferedInputStream(in),
                                        () -> explodedPackages(dir));
         }
--- a/src/java.base/share/classes/java/lang/module/Resolver.java	Thu May 14 12:16:00 2015 -0700
+++ b/src/java.base/share/classes/java/lang/module/Resolver.java	Fri May 15 13:02:59 2015 +0100
@@ -40,6 +40,7 @@
 import java.util.Optional;
 import java.util.Set;
 import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 import jdk.internal.module.Hasher.DependencyHashes;
 
@@ -204,13 +205,16 @@
     private Set<ModuleDescriptor> resolve(Deque<ModuleDescriptor> q) {
         Set<ModuleDescriptor> newlySelected = new HashSet<>();
 
+        // true if all modules are selected
+        boolean allModulesSelected = false;
+
         while (!q.isEmpty()) {
             ModuleDescriptor descriptor = q.poll();
             assert nameToReference.containsKey(descriptor.name());
             selected.add(descriptor);
 
             // process dependences
-            for (ModuleDescriptor.Requires requires: descriptor.requires()) {
+            for (ModuleDescriptor.Requires requires : descriptor.requires()) {
                 String dn = requires.name();
 
                 // before finder
@@ -243,6 +247,28 @@
                     q.offer(other);
                 }
             }
+
+            // If an automatic module is encountered then its dependences are
+            // not known so this requires resolving all modules. We do this at
+            // most once.
+            if (descriptor.isAutomatic() && !allModulesSelected) {
+
+                findAll().forEach(mref -> {
+                    ModuleDescriptor other = mref.descriptor();
+                    if (!selected.contains(other) && !newlySelected.contains(other)) {
+
+                        trace("Module %s located (%s), implicitly required by %s",
+                                other.name(), mref.location(), descriptor.name());
+
+                        newlySelected.add(other);
+                        nameToReference.put(other.name(), mref);
+                        q.offer(other);
+                    }
+                });
+
+                allModulesSelected = true;
+            }
+
         }
 
         return newlySelected;
@@ -257,22 +283,13 @@
         // Scan the finders for all available service provider modules. As java.base
         // uses services then all finders will need to be scanned anyway.
         Map<String, Set<ModuleReference>> availableProviders = new HashMap<>();
-        for (ModuleReference mref : findAll(beforeFinder)) {
+        findAll().forEach(mref -> {
             ModuleDescriptor descriptor = mref.descriptor();
             if (!descriptor.provides().isEmpty()) {
                 descriptor.provides().keySet().forEach(s ->
                     availableProviders.computeIfAbsent(s, k -> new HashSet<>()).add(mref));
             }
-        }
-        for (ModuleReference mref : findAll(afterFinder)) {
-            ModuleDescriptor descriptor = mref.descriptor();
-            // the parent layer may hide service providers from afterFinder
-            if (!descriptor.provides().isEmpty() && layer.findModule(descriptor.name()) == null) {
-                descriptor.provides().keySet().forEach(s ->
-                    availableProviders.computeIfAbsent(s, k -> new HashSet<>()).add(mref));
-            }
-        }
-
+        });
 
         // create the visit stack
         Deque<ModuleDescriptor> q = new ArrayDeque<>();
@@ -531,6 +548,13 @@
 
 
     /**
+     * Returns true if a module of the given module's name is in a parent Layer
+     */
+    private boolean inParentLayer(ModuleReference mref) {
+        return layer.findModule(mref.descriptor().name()) != null;
+    }
+
+    /**
      * Invokes the finder's find method to find the given module.
      */
     private static ModuleReference find(ModuleFinder finder, String mn) {
@@ -546,9 +570,13 @@
     /**
      * Invokes the finder's allModules to find all modules.
      */
-    private static Set<ModuleReference> findAll(ModuleFinder finder) {
+    private Stream<ModuleReference> findAll() {
         try {
-            return finder.findAll();
+            Stream<ModuleReference> s1
+                = beforeFinder.findAll().stream();
+            Stream<ModuleReference> s2
+                = afterFinder.findAll().stream().filter(m -> !inParentLayer(m));
+            return Stream.concat(s1, s2);
         } catch (UncheckedIOException e) {
             throw new ResolutionException(e.getCause());
         } catch (RuntimeException | Error e) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jigsaw/etc/automaticmodules/basic.sh	Fri May 15 13:02:59 2015 +0100
@@ -0,0 +1,70 @@
+#
+# Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# This code is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License version 2 only, as
+# published by the Free Software Foundation.
+#
+# This code is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+# version 2 for more details (a copy is included in the LICENSE file that
+# accompanied this code).
+#
+# You should have received a copy of the GNU General Public License version
+# 2 along with this work; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+# or visit www.oracle.com if you need additional information or have any
+# questions.
+#
+
+# @test
+# @summary Basic test for automatic modules
+
+
+set -e
+
+if [ -z "$TESTJAVA" ]; then
+  if [ $# -lt 1 ]; then exit 1; fi
+  TESTJAVA="$1"; shift
+  COMPILEJAVA="${TESTJAVA}"
+  TESTSRC="`pwd`"
+  TESTCLASSES="`pwd`"
+fi
+
+JAVAC="$COMPILEJAVA/bin/javac"
+JAVA="$TESTJAVA/bin/java ${TESTVMOPTS}"
+JAR="$TESTJAVA/bin/jar ${TESTTOOLVMOPTS}"
+
+rm -rf mods
+
+# Compile logging and http server libraries as modules as javac doesn't
+# support automatic modules yet
+
+mkdir -p mods/logging
+$JAVAC -d mods/logging `find $TESTSRC/src/logging -name "*.java"`
+
+mkdir -p mods/httpserver
+$JAVAC -d mods/httpserver -mp mods `find $TESTSRC/src/httpserver -name "*.java"`
+
+mkdir -p mods/test
+$JAVAC -d mods/test -mp mods `find $TESTSRC/src/test -name "*.java"`
+
+
+# Create regular (non-modular JAR files)
+
+$JAR cf mods/logging-1.0.jar -C mods/logging logging
+rm -rf mods/logging
+
+$JAR cf mods/http-server-9.0.0.jar -C mods/httpserver http
+rm -rf mods/httpserver
+
+
+# Run the test
+$JAVA -mp mods -m test/test.Main
+
+
+exit 0
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jigsaw/etc/automaticmodules/src/httpserver/http/HttpServer.java	Fri May 15 13:02:59 2015 +0100
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package http;
+
+import logging.Logger;
+
+/**
+ * A do-nothing HTTP server
+ */
+
+public class HttpServer {
+    private final int port;
+    private final Logger logger;
+
+    public HttpServer(int port) {
+        this.port = port;
+        this.logger = new Logger();
+    }
+
+    public void start() {
+        logger.log("Start HTTP server on port " + port);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jigsaw/etc/automaticmodules/src/httpserver/module-info.java	Fri May 15 13:02:59 2015 +0100
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+module httpserver {
+    requires logging;
+    exports http;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jigsaw/etc/automaticmodules/src/logging/logging/Logger.java	Fri May 15 13:02:59 2015 +0100
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package logging;
+
+/**
+ * A simple logger.
+ */
+
+public class Logger {
+    public Logger() { }
+
+    public void log(String msg) {
+        System.out.println(msg);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jigsaw/etc/automaticmodules/src/logging/module-info.java	Fri May 15 13:02:59 2015 +0100
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+module logging {
+    exports logging;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jigsaw/etc/automaticmodules/src/test/module-info.java	Fri May 15 13:02:59 2015 +0100
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+module test {
+    requires httpserver;  // automatic module
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jigsaw/etc/automaticmodules/src/test/test/Main.java	Fri May 15 13:02:59 2015 +0100
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package test;
+
+import java.lang.module.*;
+import java.lang.reflect.Module;
+
+import http.HttpServer;
+
+/**
+ * Basic test using automatic modules.
+ */
+
+public class Main {
+    public static void main(String[] args) throws Exception {
+
+        Module httpModule = HttpServer.class.getModule();
+
+        // automatic modules are named
+        assertTrue(httpModule.isNamed());
+
+        // and loose
+        assertTrue(httpModule.canRead(null));
+
+        // and read all modules in the boot Layer
+        Layer layer = Layer.bootLayer();
+        layer.configuration().descriptors().stream()
+                .map(ModuleDescriptor::name)
+                .map(layer::findModule)
+                .forEach(m -> assertTrue(httpModule.canRead(m)));
+
+        // run code in the automatic modue, ensures access is allowed
+        HttpServer http = new HttpServer(80);
+        http.start();
+    }
+
+    static void assertTrue(boolean e) {
+        if (!e)
+            throw new RuntimeException();
+    }
+}