You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@netbeans.apache.org by GitBox <gi...@apache.org> on 2018/02/20 08:34:49 UTC

[GitHub] geertjanw closed pull request #430: Adding utilities to construct boot classpath and module boot classpat?

geertjanw closed pull request #430: Adding utilities to construct boot classpath and module boot classpat?
URL: https://github.com/apache/incubator-netbeans/pull/430
 
 
   

This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:

As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):

diff --git a/java.completion/test/unit/src/org/netbeans/modules/java/completion/CompletionTestBase.java b/java.completion/test/unit/src/org/netbeans/modules/java/completion/CompletionTestBase.java
index 7e77cfe08..217f8a999 100644
--- a/java.completion/test/unit/src/org/netbeans/modules/java/completion/CompletionTestBase.java
+++ b/java.completion/test/unit/src/org/netbeans/modules/java/completion/CompletionTestBase.java
@@ -55,6 +55,7 @@
 import org.netbeans.core.startup.Main;
 import org.netbeans.junit.NbTestCase;
 import org.netbeans.modules.java.JavaDataLoader;
+import org.netbeans.modules.java.source.BootClassPathUtil;
 import org.netbeans.modules.java.source.TestUtil;
 import org.netbeans.modules.java.source.indexing.TransactionContext;
 import org.netbeans.modules.java.source.usages.BinaryAnalyser;
@@ -118,6 +119,7 @@ public CompletionTestBase(String testName) {
     protected void setUp() throws Exception {
         ClassPathProvider cpp = new ClassPathProvider() {
             volatile ClassPath bootCache;
+            volatile ClassPath moduleBootCache;
             @Override
             public ClassPath findClassPath(FileObject file, String type) {
                 try {
@@ -127,10 +129,17 @@ public ClassPath findClassPath(FileObject file, String type) {
                     if (type.equals(ClassPath.COMPILE)) {
                         return ClassPathSupport.createClassPath(new FileObject[0]);
                     }
-                    if (type.equals(ClassPath.BOOT) || type.equals(JavaClassPathConstants.MODULE_BOOT_PATH)) {
+                    if (type.equals(ClassPath.BOOT)) {
                         ClassPath cp = bootCache;
                         if (cp == null) {
-                            cp = TestUtil.getBootClassPath();
+                            bootCache = cp = BootClassPathUtil.getBootClassPath();
+                        }
+                        return cp;
+                    }
+                    if (type.equals(JavaClassPathConstants.MODULE_BOOT_PATH)) {
+                        ClassPath cp = moduleBootCache;
+                        if (cp == null) {
+                            moduleBootCache = cp = BootClassPathUtil.getModuleBootPath();
                         }
                         return cp;
                     }
diff --git a/java.hints.test/nbproject/project.properties b/java.hints.test/nbproject/project.properties
index 0bc79b376..f211965cd 100644
--- a/java.hints.test/nbproject/project.properties
+++ b/java.hints.test/nbproject/project.properties
@@ -15,7 +15,7 @@
 # specific language governing permissions and limitations
 # under the License.
 is.autoload=true
-javac.source=1.7
+javac.source=1.8
 javac.compilerargs=-Xlint -Xlint:-serial
 spec.version.base=1.19.0
 javadoc.arch=${basedir}/arch.xml
diff --git a/java.hints.test/nbproject/project.xml b/java.hints.test/nbproject/project.xml
index 0a5da9d64..fc7a21aad 100644
--- a/java.hints.test/nbproject/project.xml
+++ b/java.hints.test/nbproject/project.xml
@@ -86,6 +86,13 @@
                         <specification-version>1.25</specification-version>
                     </run-dependency>
                 </dependency>
+                <dependency>
+                    <code-name-base>org.netbeans.modules.java.j2seplatform</code-name-base>
+                    <run-dependency>
+                        <release-version>1</release-version>
+                        <specification-version>1.42</specification-version>
+                    </run-dependency>
+                </dependency>
                 <dependency>
                     <code-name-base>org.netbeans.modules.java.lexer</code-name-base>
                     <build-prerequisite/>
@@ -146,6 +153,12 @@
                         <implementation-version/>
                     </run-dependency>
                 </dependency>
+                <dependency>
+                    <code-name-base>org.netbeans.modules.parsing.nb</code-name-base>
+                    <run-dependency>
+                        <specification-version>1.4</specification-version>
+                    </run-dependency>
+                </dependency>
                 <dependency>
                     <code-name-base>org.netbeans.modules.projectapi</code-name-base>
                     <build-prerequisite/>
@@ -155,6 +168,12 @@
                         <specification-version>1.41</specification-version>
                     </run-dependency>
                 </dependency>
+                <dependency>
+                    <code-name-base>org.netbeans.modules.projectapi.nb</code-name-base>
+                    <run-dependency>
+                        <specification-version>1.4</specification-version>
+                    </run-dependency>
+                </dependency>
                 <dependency>
                     <code-name-base>org.netbeans.modules.projectuiapi</code-name-base>
                     <build-prerequisite/>
@@ -231,7 +250,7 @@
                     </run-dependency>
                 </dependency>
                 <dependency>
-                    <code-name-base>org.openide.util.ui</code-name-base>
+                    <code-name-base>org.openide.util</code-name-base>
                     <build-prerequisite/>
                     <compile-dependency/>
                     <run-dependency>
@@ -239,19 +258,19 @@
                     </run-dependency>
                 </dependency>
                 <dependency>
-                    <code-name-base>org.openide.util</code-name-base>
+                    <code-name-base>org.openide.util.lookup</code-name-base>
                     <build-prerequisite/>
                     <compile-dependency/>
                     <run-dependency>
-                        <specification-version>9.3</specification-version>
+                        <specification-version>8.12</specification-version>
                     </run-dependency>
                 </dependency>
                 <dependency>
-                    <code-name-base>org.openide.util.lookup</code-name-base>
+                    <code-name-base>org.openide.util.ui</code-name-base>
                     <build-prerequisite/>
                     <compile-dependency/>
                     <run-dependency>
-                        <specification-version>8.12</specification-version>
+                        <specification-version>9.8</specification-version>
                     </run-dependency>
                 </dependency>
             </module-dependencies>
diff --git a/java.hints.test/src/org/netbeans/modules/java/hints/test/BootClassPathUtil.java b/java.hints.test/src/org/netbeans/modules/java/hints/test/BootClassPathUtil.java
new file mode 100644
index 000000000..4e441145d
--- /dev/null
+++ b/java.hints.test/src/org/netbeans/modules/java/hints/test/BootClassPathUtil.java
@@ -0,0 +1,307 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.java.hints.test;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.lang.reflect.InvocationTargetException;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.regex.Pattern;
+import javax.tools.ForwardingJavaFileManager;
+import javax.tools.JavaCompiler;
+import javax.tools.JavaFileManager;
+import javax.tools.JavaFileObject;
+import javax.tools.SimpleJavaFileObject;
+import javax.tools.StandardLocation;
+import javax.tools.ToolProvider;
+import org.netbeans.api.annotations.common.NonNull;
+import org.netbeans.api.java.classpath.ClassPath;
+import org.netbeans.spi.java.classpath.PathResourceImplementation;
+import org.netbeans.spi.java.classpath.support.ClassPathSupport;
+import org.openide.filesystems.FileObject;
+import org.openide.filesystems.FileSystem;
+import org.openide.filesystems.FileUtil;
+import org.openide.filesystems.URLMapper;
+import org.openide.util.BaseUtilities;
+
+import static junit.framework.TestCase.assertFalse;
+import org.openide.filesystems.MultiFileSystem;
+import org.openide.filesystems.Repository;
+
+/** Note this is duplicated in:
+ * java.source.base/test/unit/src/org/netbeans/modules/java/source/BootClassPathUtil.java
+ *
+ * @author lahvac
+ */
+public class BootClassPathUtil {
+
+    public static ClassPath getBootClassPath() {
+        String cp = System.getProperty("sun.boot.class.path");
+        if (cp != null) {
+            List<URL> urls = new ArrayList<>();
+            String[] paths = cp.split(Pattern.quote(System.getProperty("path.separator")));
+            for (String path : paths) {
+                File f = new File(path);
+
+                if (!f.canRead())
+                    continue;
+
+                FileObject fo = FileUtil.toFileObject(f);
+                if (FileUtil.isArchiveFile(fo)) {
+                    fo = FileUtil.getArchiveRoot(fo);
+                }
+                if (fo != null) {
+                    urls.add(fo.toURL());
+                }
+            }
+            return ClassPathSupport.createClassPath((URL[])urls.toArray(new URL[0]));
+        } else {
+            try {
+                Class.forName("org.netbeans.ProxyURLStreamHandlerFactory").getMethod("register")
+                                                                          .invoke(null);
+            } catch (ClassNotFoundException | NoSuchMethodException |
+                     SecurityException | IllegalAccessException |
+                    IllegalArgumentException | InvocationTargetException ex) {
+                throw new IllegalStateException(ex);
+            }
+            final List<PathResourceImplementation> modules = new ArrayList<>();
+            final File installDir = new File(System.getProperty("java.home"));
+            final URI imageURI = getImageURI(installDir);
+            try {
+                final FileObject jrtRoot = URLMapper.findFileObject(imageURI.toURL());
+                final FileObject root = getModulesRoot(jrtRoot);
+                for (FileObject module : root.getChildren()) {
+                    modules.add(ClassPathSupport.createResource(module.toURL()));
+                }
+            } catch (MalformedURLException e) {
+                throw new IllegalStateException(e);
+            }
+            assertFalse(modules.isEmpty());
+            return ClassPathSupport.createClassPath(modules);
+        }
+    }
+
+    public static ClassPath getModuleBootPath() {
+        if (System.getProperty("sun.boot.class.path") != null) {
+            try {
+                //JDK 8:
+                return getModuleBootOnJDK8();
+            } catch (Exception ex) {
+                throw new IllegalStateException(ex);
+            }
+        } else {
+            return getBootClassPath();
+        }
+    }
+
+    private static final String PROTOCOL = "nbjrt"; //NOI18N
+
+    private static URI getImageURI(@NonNull final File jdkHome) {
+        try {
+            return new URI(String.format(
+                "%s:%s!/%s",  //NOI18N
+                PROTOCOL,
+                BaseUtilities.toURI(jdkHome).toString(),
+                ""));
+        } catch (URISyntaxException e) {
+            throw new IllegalStateException();
+        }
+    }
+
+    @NonNull
+    private static FileObject getModulesRoot(@NonNull final FileObject jrtRoot) {
+        final FileObject modules = jrtRoot.getFileObject("modules");    //NOI18N
+        //jimage v1 - modules are located in the root
+        //jimage v2 - modules are located in "modules" folder
+        return modules == null ?
+            jrtRoot :
+            modules;
+    }
+
+    //create fake "system module path" while running on JDK 8
+    //java.base contains rt.jar and exports all java.* packages:
+    private static ClassPath moduleBootOnJDK8;
+
+    private static ClassPath getModuleBootOnJDK8() throws Exception {
+        if (moduleBootOnJDK8 == null) {
+            List<FileSystem> roots = new ArrayList<>();
+
+            FileSystem output = FileUtil.createMemoryFileSystem();
+            FileObject sink = output.getRoot();
+
+            roots.add(output);
+
+            Set<String> packages = new HashSet<>();
+            for (FileObject r : getBootClassPath().getRoots()) {
+                FileObject javaDir = r.getFileObject("java");
+
+                if (javaDir == null)
+                    continue;
+
+                roots.add(r.getFileSystem());
+
+                Enumeration<? extends FileObject> c = javaDir.getChildren(true);
+
+                while (c.hasMoreElements()) {
+                    FileObject current = c.nextElement();
+
+                    if (!current.isData() || !current.hasExt("class")) continue;
+
+                    String rel = FileUtil.getRelativePath(r, current.getParent());
+
+                    packages.add(rel.replace('/', '.'));
+                }
+            }
+
+            FileSystem outS = new MultiFileSystem(roots.toArray(new FileSystem[0])) {
+                {
+                    setSystemName("module-boot");
+                }
+            };
+
+            Repository.getDefault().addFileSystem(outS);
+
+            StringBuilder moduleInfo = new StringBuilder();
+
+            moduleInfo.append("module java.base {\n");
+
+            for (String pack : packages) {
+                moduleInfo.append("    exports " + pack + ";\n");
+            }
+
+            moduleInfo.append("}\n");
+
+            JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
+            FileObject javaBase = outS.getRoot();
+
+            try (JavaFileManager fm = compiler.getStandardFileManager(null, null, null);
+                 JFMImpl fmImpl = new JFMImpl(fm, javaBase, sink)) {
+                compiler.getTask(null, fmImpl, null, Arrays.asList("-proc:none"), null,
+                                 Arrays.asList(new SimpleJavaFileObject(new URI("mem:///module-info.java"), javax.tools.JavaFileObject.Kind.SOURCE) {
+                    @Override
+                    public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
+                        return moduleInfo.toString();
+                    }
+                })).call();
+            }
+
+            javaBase.refresh();
+            moduleBootOnJDK8 = ClassPathSupport.createClassPath(javaBase);
+        }
+
+        return moduleBootOnJDK8;
+    }
+
+    private static class JFMImpl extends ForwardingJavaFileManager<JavaFileManager> {
+        private final FileObject output;
+        private final FileObject sink;
+
+        public JFMImpl(JavaFileManager fileManager, FileObject output, FileObject sink) {
+            super(fileManager);
+            this.output = output;
+            this.sink = sink;
+        }
+
+        @Override
+        public Iterable<Set<Location>> listLocationsForModules(Location location) throws IOException {
+            return Collections.emptyList();
+        }
+
+        @Override
+        public Iterable<JavaFileObject> list(Location location, String packageName, Set<JavaFileObject.Kind> kinds, boolean recurse) throws IOException {
+            if (location == StandardLocation.CLASS_OUTPUT) {
+                List<JavaFileObject> result = new ArrayList<>();
+                FileObject pack = output.getFileObject(packageName.replace('.', '/'));
+
+                if (pack != null) {
+                    Enumeration<? extends FileObject> c = pack.getChildren(recurse);
+
+                    while (c.hasMoreElements()) {
+                        FileObject file = c.nextElement();
+                        if (!file.hasExt("class"))
+                            continue;
+                        result.add(new InferableJavaFileObject(file, JavaFileObject.Kind.CLASS));
+                    }
+                }
+
+                return result;
+            }
+            return super.list(location, packageName, kinds, recurse);
+        }
+
+        @Override
+        public JavaFileObject getJavaFileForOutput(Location location, String className, JavaFileObject.Kind kind, javax.tools.FileObject sibling) throws IOException {
+            if (location == StandardLocation.CLASS_OUTPUT) {
+                String relPath = className.replace('.', '/') + ".class";
+                try {
+                    return new SimpleJavaFileObject(new URI("mem://" + relPath), kind) {
+                        @Override
+                        public OutputStream openOutputStream() throws IOException {
+                            return new ByteArrayOutputStream() {
+                                @Override
+                                public void close() throws IOException {
+                                    super.close();
+                                    FileObject target = FileUtil.createData(sink, relPath);
+                                    try (OutputStream out = target.getOutputStream()) {
+                                        out.write(toByteArray());
+                                    }
+                                }
+                            };
+                        }
+                    };
+                } catch (URISyntaxException ex) {
+                    throw new IOException(ex);
+                }
+            }
+            return super.getJavaFileForOutput(location, className, kind, sibling);
+        }
+
+        @Override
+        public String inferBinaryName(Location location, JavaFileObject file) {
+            if (file instanceof InferableJavaFileObject) {
+                return ((InferableJavaFileObject) file).className;
+            }
+            return super.inferBinaryName(location, file);
+        }
+
+        private class InferableJavaFileObject extends SimpleJavaFileObject {
+
+            private final String className;
+            public InferableJavaFileObject(FileObject file, Kind kind) {
+                super(file.toURI(), kind);
+                String relPath = FileUtil.getRelativePath(output, file);
+                className = relPath.substring(0, relPath.length() - ".class".length()).replace('/', '.');
+            }
+        }
+
+    }
+
+}
diff --git a/java.hints.test/src/org/netbeans/modules/java/hints/test/api/HintTest.java b/java.hints.test/src/org/netbeans/modules/java/hints/test/api/HintTest.java
index 202f5489a..98c80cea7 100644
--- a/java.hints.test/src/org/netbeans/modules/java/hints/test/api/HintTest.java
+++ b/java.hints.test/src/org/netbeans/modules/java/hints/test/api/HintTest.java
@@ -56,13 +56,14 @@
 import javax.tools.Diagnostic;
 import junit.framework.Assert;
 import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertNotNull;
 import static junit.framework.Assert.assertNotSame;
 import static junit.framework.Assert.assertTrue;
+import static junit.framework.TestCase.assertFalse;
 
 import org.netbeans.api.editor.mimelookup.MimeLookup;
 import org.netbeans.api.java.classpath.ClassPath;
+import org.netbeans.api.java.classpath.JavaClassPathConstants;
 import org.netbeans.api.java.lexer.JavaTokenId;
 import org.netbeans.api.java.queries.SourceForBinaryQuery;
 import org.netbeans.api.java.queries.SourceForBinaryQuery.Result;
@@ -97,8 +98,10 @@
 import org.netbeans.modules.java.hints.spiimpl.batch.BatchUtilities;
 import org.netbeans.modules.java.hints.spiimpl.hints.HintsInvoker;
 import org.netbeans.modules.java.hints.spiimpl.options.HintsSettings;
+import org.netbeans.modules.java.hints.test.BootClassPathUtil;
 import org.netbeans.modules.java.hints.test.Utilities.TestLookup;
 import org.netbeans.modules.java.source.JavaSourceAccessor;
+import org.netbeans.modules.java.source.parsing.JavacParser;
 import org.netbeans.modules.parsing.api.indexing.IndexingManager;
 import org.netbeans.modules.parsing.impl.indexing.CacheFolder;
 import org.netbeans.modules.parsing.impl.indexing.MimeTypes;
@@ -261,6 +264,7 @@ private HintTest() throws Exception {
         sourcePath = ClassPathSupport.createClassPath(sourceRoot);
         
         Main.initializeURLFactory();
+        JavacParser.DISABLE_SOURCE_LEVEL_DOWNGRADE = true;
     }
 
     /**Bootstraps the test framework.
@@ -664,51 +668,18 @@ public void removeChangeListener(ChangeListener l) {
 
     }
 
-    private static List<URL> bootClassPath;
-
     private static Logger log = Logger.getLogger(HintTest.class.getName());
 
-    private static synchronized List<URL> getBootClassPath() {
-        if (bootClassPath == null) {
-            try {
-                String cp = System.getProperty("sun.boot.class.path");
-                List<URL> urls = new ArrayList<URL>();
-                String[] paths = cp.split(Pattern.quote(System.getProperty("path.separator")));
-
-                for (String path : paths) {
-                    File f = new File(path);
-
-                    if (!f.canRead())
-                        continue;
-
-                    FileObject fo = FileUtil.toFileObject(f);
-
-                    if (FileUtil.isArchiveFile(fo)) {
-                        fo = FileUtil.getArchiveRoot(fo);
-                    }
-
-                    if (fo != null) {
-                        urls.add(fo.getURL());
-                    }
-                }
-
-                bootClassPath = urls;
-            } catch (FileStateInvalidException e) {
-                if (log.isLoggable(Level.SEVERE))
-                    log.log(Level.SEVERE, e.getMessage(), e);
-            }
-        }
-
-        return bootClassPath;
-    }
-    
     private class TestProxyClassPathProvider implements ClassPathProvider {
 
         public ClassPath findClassPath(FileObject file, String type) {
             try {
             if (ClassPath.BOOT == type) {
-                // XXX simpler to use JavaPlatformManager.getDefault().getDefaultPlatform().getBootstrapLibraries()
-                return ClassPathSupport.createClassPath(getBootClassPath().toArray(new URL[0]));
+                return BootClassPathUtil.getBootClassPath();
+            }
+
+            if (JavaClassPathConstants.MODULE_BOOT_PATH == type) {
+                return BootClassPathUtil.getModuleBootPath();
             }
 
             if (ClassPath.SOURCE == type) {
diff --git a/java.hints.test/test/unit/src/org/netbeans/modules/java/hints/test/api/HintTestTest.java b/java.hints.test/test/unit/src/org/netbeans/modules/java/hints/test/api/HintTestTest.java
index a910d703b..22a9970a5 100644
--- a/java.hints.test/test/unit/src/org/netbeans/modules/java/hints/test/api/HintTestTest.java
+++ b/java.hints.test/test/unit/src/org/netbeans/modules/java/hints/test/api/HintTestTest.java
@@ -302,4 +302,22 @@ public void testNonJavaChangesOpenedInEditor214197() throws Exception {
         Assert.assertEquals("6\n7\n", doc.getText(0, doc.getLength()));
     }
 
+    @Test
+    public void testModuleBootPath() throws Exception {
+        HintTest.create()
+                .sourceLevel("9")
+                .input("module-info.java",
+                       "module m1 {}\n")
+                .run(ModuleBootPath.class)
+                .assertWarnings("0:0-0:12:verifier:Test");
+    }
+
+    @Hint(displayName="testModuleBootPath", description="testModuleBootPath", category="test")
+    public static final class ModuleBootPath {
+        @TriggerTreeKind(Kind.MODULE)
+        public static ErrorDescription hint(HintContext ctx) {
+            return ErrorDescriptionFactory.forTree(ctx, ctx.getPath(), "Test");
+        }
+    }
+
 }
diff --git a/java.hints/nbproject/project.xml b/java.hints/nbproject/project.xml
index 04e5500b4..a328b255f 100644
--- a/java.hints/nbproject/project.xml
+++ b/java.hints/nbproject/project.xml
@@ -543,6 +543,9 @@
                         <code-name-base>org.netbeans.modules.java.hints.test</code-name-base>
                         <compile-dependency/>
                     </test-dependency>
+                    <test-dependency>
+                        <code-name-base>org.netbeans.modules.java.j2seplatform</code-name-base>
+                    </test-dependency>
                     <test-dependency>
                         <code-name-base>org.netbeans.modules.java.source.base</code-name-base>
                         <compile-dependency/>
diff --git a/java.source.base/test/unit/src/org/netbeans/modules/java/source/BootClassPathUtil.java b/java.source.base/test/unit/src/org/netbeans/modules/java/source/BootClassPathUtil.java
new file mode 100644
index 000000000..7ea0fde4a
--- /dev/null
+++ b/java.source.base/test/unit/src/org/netbeans/modules/java/source/BootClassPathUtil.java
@@ -0,0 +1,307 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.java.source;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.lang.reflect.InvocationTargetException;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.regex.Pattern;
+import javax.tools.ForwardingJavaFileManager;
+import javax.tools.JavaCompiler;
+import javax.tools.JavaFileManager;
+import javax.tools.JavaFileObject;
+import javax.tools.SimpleJavaFileObject;
+import javax.tools.StandardLocation;
+import javax.tools.ToolProvider;
+import org.netbeans.api.annotations.common.NonNull;
+import org.netbeans.api.java.classpath.ClassPath;
+import org.netbeans.spi.java.classpath.PathResourceImplementation;
+import org.netbeans.spi.java.classpath.support.ClassPathSupport;
+import org.openide.filesystems.FileObject;
+import org.openide.filesystems.FileSystem;
+import org.openide.filesystems.FileUtil;
+import org.openide.filesystems.URLMapper;
+import org.openide.util.BaseUtilities;
+
+import static junit.framework.TestCase.assertFalse;
+import org.openide.filesystems.MultiFileSystem;
+import org.openide.filesystems.Repository;
+
+/** Note this is duplicated in:
+ *  java.hints.test/src/org/netbeans/modules/java/hints/test/BootClassPathUtil.java
+ *
+ * @author lahvac
+ */
+public class BootClassPathUtil {
+
+    public static ClassPath getBootClassPath() {
+        String cp = System.getProperty("sun.boot.class.path");
+        if (cp != null) {
+            List<URL> urls = new ArrayList<>();
+            String[] paths = cp.split(Pattern.quote(System.getProperty("path.separator")));
+            for (String path : paths) {
+                File f = new File(path);
+
+                if (!f.canRead())
+                    continue;
+
+                FileObject fo = FileUtil.toFileObject(f);
+                if (FileUtil.isArchiveFile(fo)) {
+                    fo = FileUtil.getArchiveRoot(fo);
+                }
+                if (fo != null) {
+                    urls.add(fo.toURL());
+                }
+            }
+            return ClassPathSupport.createClassPath((URL[])urls.toArray(new URL[0]));
+        } else {
+            try {
+                Class.forName("org.netbeans.ProxyURLStreamHandlerFactory").getMethod("register")
+                                                                          .invoke(null);
+            } catch (ClassNotFoundException | NoSuchMethodException |
+                     SecurityException | IllegalAccessException |
+                    IllegalArgumentException | InvocationTargetException ex) {
+                throw new IllegalStateException(ex);
+            }
+            final List<PathResourceImplementation> modules = new ArrayList<>();
+            final File installDir = new File(System.getProperty("java.home"));
+            final URI imageURI = getImageURI(installDir);
+            try {
+                final FileObject jrtRoot = URLMapper.findFileObject(imageURI.toURL());
+                final FileObject root = getModulesRoot(jrtRoot);
+                for (FileObject module : root.getChildren()) {
+                    modules.add(ClassPathSupport.createResource(module.toURL()));
+                }
+            } catch (MalformedURLException e) {
+                throw new IllegalStateException(e);
+            }
+            assertFalse(modules.isEmpty());
+            return ClassPathSupport.createClassPath(modules);
+        }
+    }
+
+    public static ClassPath getModuleBootPath() {
+        if (System.getProperty("sun.boot.class.path") != null) {
+            try {
+                //JDK 8:
+                return getModuleBootOnJDK8();
+            } catch (Exception ex) {
+                throw new IllegalStateException(ex);
+            }
+        } else {
+            return getBootClassPath();
+        }
+    }
+
+    private static final String PROTOCOL = "nbjrt"; //NOI18N
+
+    private static URI getImageURI(@NonNull final File jdkHome) {
+        try {
+            return new URI(String.format(
+                "%s:%s!/%s",  //NOI18N
+                PROTOCOL,
+                BaseUtilities.toURI(jdkHome).toString(),
+                ""));
+        } catch (URISyntaxException e) {
+            throw new IllegalStateException();
+        }
+    }
+
+    @NonNull
+    private static FileObject getModulesRoot(@NonNull final FileObject jrtRoot) {
+        final FileObject modules = jrtRoot.getFileObject("modules");    //NOI18N
+        //jimage v1 - modules are located in the root
+        //jimage v2 - modules are located in "modules" folder
+        return modules == null ?
+            jrtRoot :
+            modules;
+    }
+
+    //create fake "system module path" while running on JDK 8
+    //java.base contains rt.jar and exports all java.* packages:
+    private static ClassPath moduleBootOnJDK8;
+
+    private static ClassPath getModuleBootOnJDK8() throws Exception {
+        if (moduleBootOnJDK8 == null) {
+            List<FileSystem> roots = new ArrayList<>();
+
+            FileSystem output = FileUtil.createMemoryFileSystem();
+            FileObject sink = output.getRoot();
+
+            roots.add(output);
+
+            Set<String> packages = new HashSet<>();
+            for (FileObject r : getBootClassPath().getRoots()) {
+                FileObject javaDir = r.getFileObject("java");
+
+                if (javaDir == null)
+                    continue;
+
+                roots.add(r.getFileSystem());
+
+                Enumeration<? extends FileObject> c = javaDir.getChildren(true);
+
+                while (c.hasMoreElements()) {
+                    FileObject current = c.nextElement();
+
+                    if (!current.isData() || !current.hasExt("class")) continue;
+
+                    String rel = FileUtil.getRelativePath(r, current.getParent());
+
+                    packages.add(rel.replace('/', '.'));
+                }
+            }
+
+            FileSystem outS = new MultiFileSystem(roots.toArray(new FileSystem[0])) {
+                {
+                    setSystemName("module-boot");
+                }
+            };
+
+            Repository.getDefault().addFileSystem(outS);
+
+            StringBuilder moduleInfo = new StringBuilder();
+
+            moduleInfo.append("module java.base {\n");
+
+            for (String pack : packages) {
+                moduleInfo.append("    exports " + pack + ";\n");
+            }
+
+            moduleInfo.append("}\n");
+
+            JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
+            FileObject javaBase = outS.getRoot();
+
+            try (JavaFileManager fm = compiler.getStandardFileManager(null, null, null);
+                 JFMImpl fmImpl = new JFMImpl(fm, javaBase, sink)) {
+                compiler.getTask(null, fmImpl, null, Arrays.asList("-proc:none"), null,
+                                 Arrays.asList(new SimpleJavaFileObject(new URI("mem:///module-info.java"), javax.tools.JavaFileObject.Kind.SOURCE) {
+                    @Override
+                    public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
+                        return moduleInfo.toString();
+                    }
+                })).call();
+            }
+
+            javaBase.refresh();
+            moduleBootOnJDK8 = ClassPathSupport.createClassPath(javaBase);
+        }
+
+        return moduleBootOnJDK8;
+    }
+
+    private static class JFMImpl extends ForwardingJavaFileManager<JavaFileManager> {
+        private final FileObject output;
+        private final FileObject sink;
+
+        public JFMImpl(JavaFileManager fileManager, FileObject output, FileObject sink) {
+            super(fileManager);
+            this.output = output;
+            this.sink = sink;
+        }
+
+        @Override
+        public Iterable<Set<Location>> listLocationsForModules(Location location) throws IOException {
+            return Collections.emptyList();
+        }
+
+        @Override
+        public Iterable<JavaFileObject> list(Location location, String packageName, Set<JavaFileObject.Kind> kinds, boolean recurse) throws IOException {
+            if (location == StandardLocation.CLASS_OUTPUT) {
+                List<JavaFileObject> result = new ArrayList<>();
+                FileObject pack = output.getFileObject(packageName.replace('.', '/'));
+
+                if (pack != null) {
+                    Enumeration<? extends FileObject> c = pack.getChildren(recurse);
+
+                    while (c.hasMoreElements()) {
+                        FileObject file = c.nextElement();
+                        if (!file.hasExt("class"))
+                            continue;
+                        result.add(new InferableJavaFileObject(file, JavaFileObject.Kind.CLASS));
+                    }
+                }
+
+                return result;
+            }
+            return super.list(location, packageName, kinds, recurse);
+        }
+
+        @Override
+        public JavaFileObject getJavaFileForOutput(Location location, String className, JavaFileObject.Kind kind, javax.tools.FileObject sibling) throws IOException {
+            if (location == StandardLocation.CLASS_OUTPUT) {
+                String relPath = className.replace('.', '/') + ".class";
+                try {
+                    return new SimpleJavaFileObject(new URI("mem://" + relPath), kind) {
+                        @Override
+                        public OutputStream openOutputStream() throws IOException {
+                            return new ByteArrayOutputStream() {
+                                @Override
+                                public void close() throws IOException {
+                                    super.close();
+                                    FileObject target = FileUtil.createData(sink, relPath);
+                                    try (OutputStream out = target.getOutputStream()) {
+                                        out.write(toByteArray());
+                                    }
+                                }
+                            };
+                        }
+                    };
+                } catch (URISyntaxException ex) {
+                    throw new IOException(ex);
+                }
+            }
+            return super.getJavaFileForOutput(location, className, kind, sibling);
+        }
+
+        @Override
+        public String inferBinaryName(Location location, JavaFileObject file) {
+            if (file instanceof InferableJavaFileObject) {
+                return ((InferableJavaFileObject) file).className;
+            }
+            return super.inferBinaryName(location, file);
+        }
+
+        private class InferableJavaFileObject extends SimpleJavaFileObject {
+
+            private final String className;
+            public InferableJavaFileObject(FileObject file, Kind kind) {
+                super(file.toURI(), kind);
+                String relPath = FileUtil.getRelativePath(output, file);
+                className = relPath.substring(0, relPath.length() - ".class".length()).replace('/', '.');
+            }
+        }
+
+    }
+
+}
diff --git a/java.source.base/test/unit/src/org/netbeans/modules/java/source/TestUtil.java b/java.source.base/test/unit/src/org/netbeans/modules/java/source/TestUtil.java
index cf658e7f0..824562769 100644
--- a/java.source.base/test/unit/src/org/netbeans/modules/java/source/TestUtil.java
+++ b/java.source.base/test/unit/src/org/netbeans/modules/java/source/TestUtil.java
@@ -405,65 +405,7 @@ public Document createDocument(String mimeType) {
       }
 
     public static ClassPath getBootClassPath() {
-        String cp = System.getProperty("sun.boot.class.path");
-        if (cp != null) {
-            List<URL> urls = new ArrayList<>();
-            String[] paths = cp.split(Pattern.quote(System.getProperty("path.separator")));
-            for (String path : paths) {
-                File f = new File(path);
-
-                if (!f.canRead())
-                    continue;
-
-                FileObject fo = FileUtil.toFileObject(f);
-                if (FileUtil.isArchiveFile(fo)) {
-                    fo = FileUtil.getArchiveRoot(fo);
-                }
-                if (fo != null) {
-                    urls.add(fo.toURL());
-                }
-            }
-            return ClassPathSupport.createClassPath((URL[])urls.toArray(new URL[0]));
-        } else {
-            ProxyURLStreamHandlerFactory.register();
-            final List<PathResourceImplementation> modules = new ArrayList<>();
-            final File installDir = new File(System.getProperty("java.home"));
-            final URI imageURI = getImageURI(installDir);
-            try {
-                final FileObject jrtRoot = URLMapper.findFileObject(imageURI.toURL());
-                final FileObject root = getModulesRoot(jrtRoot);
-                for (FileObject module : root.getChildren()) {
-                    modules.add(ClassPathSupport.createResource(module.toURL()));
-                }
-            } catch (MalformedURLException e) {
-                throw new IllegalStateException(e);
-            }
-            assertFalse(modules.isEmpty());
-            return ClassPathSupport.createClassPath(modules);
-        }
+        return BootClassPathUtil.getBootClassPath();
     }
 
-    private static final String PROTOCOL = "nbjrt"; //NOI18N
-
-    private static URI getImageURI(@NonNull final File jdkHome) {
-        try {
-            return new URI(String.format(
-                "%s:%s!/%s",  //NOI18N
-                PROTOCOL,
-                BaseUtilities.toURI(jdkHome).toString(),
-                ""));
-        } catch (URISyntaxException e) {
-            throw new IllegalStateException();
-        }
-    }
-
-    @NonNull
-    private static FileObject getModulesRoot(@NonNull final FileObject jrtRoot) {
-        final FileObject modules = jrtRoot.getFileObject("modules");    //NOI18N
-        //jimage v1 - modules are located in the root
-        //jimage v2 - modules are located in "modules" folder
-        return modules == null ?
-            jrtRoot :
-            modules;
-    }
 }


 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
users@infra.apache.org


With regards,
Apache Git Services

---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@netbeans.apache.org
For additional commands, e-mail: notifications-help@netbeans.apache.org

For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists