blob: 6d558c2384989b3e4c3d308a7cca43e89ad5c3ef [file] [log] [blame]
/*
* 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(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('/', '.');
}
}
}
}