blob: e43625898a8a021111b90a6368253794d17b1971 [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.j2seplatform.platformdefinition;
import org.netbeans.modules.java.j2seplatform.queries.DefaultClassPathProvider;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.DataOutputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import java.util.concurrent.RunnableFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import javax.swing.event.ChangeListener;
import junit.framework.TestSuite;
import org.netbeans.api.annotations.common.NonNull;
import org.netbeans.api.java.classpath.ClassPath;
import org.netbeans.api.java.classpath.GlobalPathRegistry;
import org.netbeans.api.java.classpath.JavaClassPathConstants;
import org.netbeans.api.java.platform.JavaPlatform;
import org.netbeans.api.java.platform.JavaPlatformManager;
import org.netbeans.api.java.platform.Specification;
import org.netbeans.api.java.queries.SourceForBinaryQuery;
import org.netbeans.api.java.queries.SourceLevelQuery;
import org.netbeans.api.project.Project;
import org.netbeans.api.project.ui.ProjectGroup;
import org.netbeans.api.project.ui.ProjectGroupChangeListener;
import org.netbeans.core.startup.layers.ArchiveURLMapper;
import org.netbeans.junit.MockServices;
import org.netbeans.junit.NbTestCase;
import org.netbeans.junit.NbTestSuite;
import org.netbeans.modules.masterfs.MasterURLMapper;
import org.netbeans.modules.project.uiapi.OpenProjectsTrampoline;
import org.netbeans.spi.java.classpath.ClassPathProvider;
import org.netbeans.spi.java.classpath.support.ClassPathSupport;
import org.netbeans.spi.java.queries.SourceForBinaryQueryImplementation;
import org.netbeans.spi.java.queries.SourceLevelQueryImplementation2;
import org.openide.filesystems.FileLock;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileUtil;
import org.openide.modules.SpecificationVersion;
import org.openide.util.Lookup;
import org.openide.util.Utilities;
/**
*
* @author tom
*/
public class DefaultClassPathProviderTest extends NbTestCase {
private static final int FILE_IN_PACKAGE = 0;
private static final int FILE_IN_BAD_PACKAGE = 1;
private static final int FILE_IN_DEFAULT_PACKAGE = 2;
private static final int DELAY = 2500;
private static final byte[] CLASS_FILE_DATA = {
(byte)0xca, (byte)0xfe, (byte)0xba, (byte)0xbe, 0x00, 0x00, 0x00, 0x2e, 0x00, 0x0d, 0x0a, 0x00, 0x03, 0x00, 0x0a, 0x07, 0x00, 0x0b, 0x07, 0x00,
0x0c, 0x01, 0x00, 0x06, 0x3c, 0x69, 0x6e, 0x69, 0x74, 0x3e, 0x01, 0x00, 0x03, 0x28, 0x29, 0x56, 0x01, 0x00, 0x04, 0x43,
0x6f, 0x64, 0x65, 0x01, 0x00, 0x0f, 0x4c, 0x69, 0x6e, 0x65, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x54, 0x61, 0x62, 0x6c,
0x65, 0x01, 0x00, 0x0a, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x01, 0x00, 0x09, 0x54, 0x65, 0x73,
0x74, 0x2e, 0x6a, 0x61, 0x76, 0x61, 0x0c, 0x00, 0x04, 0x00, 0x05, 0x01, 0x00, 0x09, 0x74, 0x65, 0x73, 0x74, 0x2f, 0x54,
0x65, 0x73, 0x74, 0x01, 0x00, 0x10, 0x6a, 0x61, 0x76, 0x61, 0x2f, 0x6c, 0x61, 0x6e, 0x67, 0x2f, 0x4f, 0x62, 0x6a, 0x65,
0x63, 0x74, 0x00, 0x21, 0x00, 0x02, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x04, 0x00, 0x05,
0x00, 0x01, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x2a, (byte)0xb7, 0x00, 0x01,
(byte)0xb1, 0x00, 0x00, 0x00, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x01, 0x00,
0x08, 0x00, 0x00, 0x00, 0x02, 0x00, 0x09
};
private FileObject srcRoot;
private FileObject[] srcFile = new FileObject[3];
private FileObject[] compileRoots;
private static FileObject[] execRoots;
private static FileObject[] libSourceRoots;
private FileObject execTestDir;
private JavaPlatform j9;
/** Creates a new instance of DefaultClassPathProviderTest */
public DefaultClassPathProviderTest (String testName) {
super (testName);
MockServices.setServices(
ArchiveURLMapper.class,
MasterURLMapper.class,
JavaPlatformProviderImpl.class,
SFBQI.class,
OpenProject.class,
MockSLQ.class);
}
public static TestSuite suite() {
final NbTestSuite suite = new NbTestSuite();
suite.addTest(new DefaultClassPathProviderTest("testFindClassPath")); //NOI18N
suite.addTest(new DefaultClassPathProviderTest("testCycle")); //NOI18N
suite.addTest(new DefaultClassPathProviderTest("testEvents")); //NOI18N
suite.addTest(new DefaultClassPathProviderTest("testModularSources_SystemModules")); //NOI18N
suite.addTest(new DefaultClassPathProviderTest("testJavaPlatformCaching")); //NOI18N
suite.addTest(new DefaultClassPathProviderTest("testLRUCaching")); //NOI18N
suite.addTest(new DefaultClassPathProviderTest("testJPMChanges")); //NOI18N
suite.addTest(new DefaultClassPathProviderTest("testModularSources_UserModules")); //NOI18N
return suite;
}
@Override
protected void tearDown () throws Exception {
this.srcRoot = null;
this.compileRoots = null;
if (j9 != null) {
Lookup.getDefault().lookup(JavaPlatformProviderImpl.class).removePlatform(j9);
}
super.tearDown();
}
@Override
protected void setUp() throws Exception {
super.setUp();
this.clearWorkDir();
final RunnableFuture<Project[]> dummyFuture = new FutureTask<>(new Callable<Project[]>() {
@Override
public Project[] call() throws Exception {
return new Project[0];
}
});
dummyFuture.run();
assertTrue(dummyFuture.isDone());
OpenProject.future = dummyFuture;
FileObject workDir = FileUtil.toFileObject(this.getWorkDir());
assertNotNull("MasterFS is not configured.", workDir);
this.srcRoot = workDir.createFolder("src");
this.compileRoots = new FileObject[3];
for (int i=0; i< this.compileRoots.length; i++) {
this.compileRoots[i] = workDir.createFolder("lib_"+Integer.toString(i));
}
ClassPath cp = ClassPathSupport.createClassPath(this.compileRoots);
GlobalPathRegistry.getDefault().register(ClassPath.COMPILE, new ClassPath[] {cp});
this.execRoots = new FileObject[2];
this.execRoots[0] = this.compileRoots[2];
this.execRoots[1] = workDir.createFolder("lib_OnlyExec");
cp = ClassPathSupport.createClassPath(this.execRoots);
GlobalPathRegistry.getDefault().register (ClassPath.EXECUTE, new ClassPath[]{cp});
this.libSourceRoots = new FileObject[2];
for (int i=0; i< libSourceRoots.length; i++) {
this.libSourceRoots[i] = workDir.createFolder ("libSrc_"+Integer.toString(i));
}
cp = ClassPathSupport.createClassPath (this.libSourceRoots);
GlobalPathRegistry.getDefault().register (ClassPath.SOURCE, new ClassPath[]{cp});
execTestDir = workDir.createFolder("exec");
FileObject java9 = FileUtil.createFolder(workDir, "java9"); //NOI18N
FileObject javaBase = FileUtil.createFolder(java9, "modules/java.base"); //NOI18N
j9 = new J9(java9, javaBase);
Lookup.getDefault().lookup(JavaPlatformProviderImpl.class).addPlatform(j9);
Lookup.getDefault().lookup(MockSLQ.class).reset();
}
public void testFindClassPath () throws IOException {
FileObject artefact = getSourceFile (FILE_IN_PACKAGE);
ClassPathProvider cpp = new DefaultClassPathProvider ();
ClassPath cp = cpp.findClassPath(artefact, ClassPath.SOURCE);
assertNull ("DefaultClassPathProvider returned not null for SOURCES",cp);
// assertNotNull ("DefaultClassPathProvider returned null for SOURCES",cp);
// assertEquals("Invalid length of classpath for SOURCE",1,cp.getRoots().length);
// assertRootsEquals ("Invalid classpath roots for SOURCE", cp, new FileObject[] {this.srcRoot});
cp = cpp.findClassPath(artefact, ClassPath.COMPILE);
assertNotNull ("DefaultClassPathProvider returned null for COMPILE",cp);
assertEquals("Invalid length of classpath for COMPILE",this.compileRoots.length + 1, cp.getRoots().length);
FileObject[] resRoots = new FileObject[this.compileRoots.length + 1];
System.arraycopy(this.compileRoots,0,resRoots,0,this.compileRoots.length);
resRoots[this.compileRoots.length] = this.execRoots[1];
assertRootsEquals ("Invalid classpath roots for COMPILE", cp, resRoots);
cp = cpp.findClassPath(artefact, ClassPath.BOOT);
assertNotNull ("DefaultClassPathProvider returned null for BOOT",cp);
JavaPlatform dp = JavaPlatformManager.getDefault().getDefaultPlatform();
assertEquals("Invalid length of classpath for BOOT",dp.getBootstrapLibraries().getRoots().length, cp.getRoots().length);
assertRootsEquals ("Invalid classpath roots for BOOT", cp, dp.getBootstrapLibraries().getRoots());
artefact = getSourceFile (FILE_IN_DEFAULT_PACKAGE);
cp = cpp.findClassPath(artefact, ClassPath.SOURCE);
assertNull ("DefaultClassPathProvider returned not null for SOURCES",cp);
// assertNotNull ("DefaultClassPathProvider returned null for SOURCES",cp);
// assertEquals("Invalid length of classpath for SOURCE",1,cp.getRoots().length);
// assertRootsEquals ("Invalid classpath roots for SOURCE", cp, new FileObject[] {this.srcRoot});
artefact = getSourceFile (FILE_IN_BAD_PACKAGE);
cp = cpp.findClassPath(artefact, ClassPath.SOURCE);
assertNull ("DefaultClassPathProvider returned not null for SOURCES",cp);
// assertNotNull ("DefaultClassPathProvider returned null for SOURCES",cp);
// assertEquals("Invalid length of classpath for SOURCE",1,cp.getRoots().length);
// FileObject badRoot = this.srcRoot.getFileObject ("test");
// assertRootsEquals ("Invalid classpath roots for SOURCE", cp, new FileObject[] {badRoot}); //ERROR
FileObject classFile = getClassFile();
cp = cpp.findClassPath(classFile, ClassPath.EXECUTE);
assertNotNull ("DefaultClassPathProvider returned null for EXECUTE",cp);
assertEquals("Invalid length of classpath for EXECUTE",1,cp.getRoots().length);
assertEquals("Illegal classpath for EXECUTE: ",cp.getRoots()[0],this.execTestDir);
}
public void testCycle () throws Exception {
GlobalPathRegistry regs = GlobalPathRegistry.getDefault();
Set<ClassPath> toCleanUp = regs.getPaths(ClassPath.COMPILE);
regs.unregister(ClassPath.COMPILE, toCleanUp.toArray(new ClassPath[toCleanUp.size()]));
toCleanUp = regs.getPaths(ClassPath.EXECUTE);
regs.unregister(ClassPath.EXECUTE, toCleanUp.toArray(new ClassPath[toCleanUp.size()]));
File wdf = getWorkDir();
FileObject wd = FileUtil.toFileObject(wdf);
FileObject root1 = wd.createFolder("root1");
FileObject root2 = wd.createFolder("root2");
ClassPathProvider cpp = new DefaultClassPathProvider ();
ClassPath dcp = cpp.findClassPath(root2, ClassPath.COMPILE);
ClassPath cp = ClassPathSupport.createClassPath(new FileObject[] {root1});
regs.register(ClassPath.COMPILE, new ClassPath[] {cp});
assertNotNull(dcp);
FileObject[] roots = dcp.getRoots();
assertEquals(1, roots.length);
assertEquals(root1, roots[0]);
regs.register(ClassPath.COMPILE, new ClassPath[] {dcp});
roots = dcp.getRoots();
assertEquals(1, roots.length);
}
/**
* Checks that single event is fired during opening (closing) of projects
*/
public void testEvents() throws Exception {
final File wd = getWorkDir();
final File root1 = FileUtil.normalizeFile(new File (wd,"src1")); //NOI18N
final File root2 = FileUtil.normalizeFile(new File (wd,"src2")); //NOI18N
root1.mkdir();
root2.mkdir();
final ClassPathProvider cpp = new DefaultClassPathProvider ();
final ClassPath defaultCompile = cpp.findClassPath(FileUtil.toFileObject(wd), ClassPath.COMPILE);
assertNotNull(defaultCompile);
assertFalse(contains(defaultCompile, Utilities.toURI(root1).toURL()));
assertFalse(contains(defaultCompile, Utilities.toURI(root2).toURL()));
final Listener listener = new Listener();
defaultCompile.addPropertyChangeListener(listener);
final GlobalPathRegistry regs = GlobalPathRegistry.getDefault();
final ClassPath compileCpProject1 = ClassPathSupport.createClassPath(Utilities.toURI(root1).toURL());
final ClassPath compileCpProject2 = ClassPathSupport.createClassPath(Utilities.toURI(root2).toURL());
//Simulate projects open
RunnableFuture<Project[]> barrier = new FutureTask<Project[]>(new Callable<Project[]>() {
@Override
public Project[] call() throws Exception {
return new Project[0];
}
});
OpenProject.future = barrier;
regs.register(ClassPath.COMPILE, new ClassPath[]{compileCpProject1});
assertFalse(contains(defaultCompile, Utilities.toURI(root1).toURL()));
assertFalse(listener.awaitEvent());
assertEquals(0, listener.get());
regs.register(ClassPath.COMPILE, new ClassPath[]{compileCpProject2});
assertFalse(contains(defaultCompile, Utilities.toURI(root2).toURL()));
assertFalse(listener.awaitEvent());
assertEquals(0, listener.get());
barrier.run();
assertTrue(listener.awaitEvent());
assertEquals(1, listener.get());
assertTrue(contains(defaultCompile, Utilities.toURI(root1).toURL()));
assertTrue(contains(defaultCompile, Utilities.toURI(root2).toURL()));
//Simulate projects close
barrier = new FutureTask<Project[]>(new Callable<Project[]>() {
@Override
public Project[] call() throws Exception {
return new Project[0];
}
});
OpenProject.future = barrier;
listener.reset();
regs.unregister(ClassPath.COMPILE, new ClassPath[]{compileCpProject1});
assertTrue(contains(defaultCompile, Utilities.toURI(root1).toURL()));
assertFalse(listener.awaitEvent());
assertEquals(0, listener.get());
regs.unregister(ClassPath.COMPILE, new ClassPath[]{compileCpProject2});
assertTrue(contains(defaultCompile, Utilities.toURI(root2).toURL()));
assertFalse(listener.awaitEvent());
assertEquals(0, listener.get());
barrier.run();
assertTrue(listener.awaitEvent());
assertEquals(1, listener.get());
assertFalse(contains(defaultCompile, Utilities.toURI(root1).toURL()));
assertFalse(contains(defaultCompile, Utilities.toURI(root2).toURL()));
}
public void testModularSources_SystemModules() throws Exception {
final FileObject artefact = getSourceFile (FILE_IN_PACKAGE);
Lookup.getDefault().lookup(MockSLQ.class).register(this.srcRoot, new SpecificationVersion("1.8")); //NOI18N
assertEquals("1.8", SourceLevelQuery.getSourceLevel(artefact)); //NOI18N
ClassPathProvider cpp = new DefaultClassPathProvider ();
ClassPath cp = cpp.findClassPath(artefact, JavaClassPathConstants.MODULE_BOOT_PATH);
assertNull ("DefaultClassPathProvider returned not null for MODULE_BOOT_PATH with source level 8",cp);
cp = cpp.findClassPath(artefact, ClassPath.BOOT);
assertEquals("DefaultClassPathProvider returned invalid classpath for BOOT with source level 8",
JavaPlatform.getDefault().getBootstrapLibraries(),
cp);
Lookup.getDefault().lookup(MockSLQ.class).register(this.srcRoot, new SpecificationVersion("9")); //NOI18N
assertEquals("9", SourceLevelQuery.getSourceLevel(artefact)); //NOI18N
cpp = new DefaultClassPathProvider ();
cp = cpp.findClassPath(artefact, JavaClassPathConstants.MODULE_BOOT_PATH);
assertEquals ("DefaultClassPathProvider returned invalid classpath for MODULE_BOOT_PATH with source level 9",
j9.getBootstrapLibraries(),
cp);
cp = cpp.findClassPath(artefact, ClassPath.BOOT);
assertEquals("DefaultClassPathProvider returned invalid classpath for BOOT with source level 9",
j9.getBootstrapLibraries(),
cp);
}
public void testJavaPlatformCaching() throws IOException {
FileObject artefact = getSourceFile (FILE_IN_PACKAGE);
Lookup.getDefault().lookup(MockSLQ.class).register(this.srcRoot, new SpecificationVersion("9")); //NOI18N
assertEquals("9", SourceLevelQuery.getSourceLevel(artefact)); //NOI18N
ClassPathProvider cpp = new DefaultClassPathProvider ();
final Logger log = Logger.getLogger(DefaultClassPathProvider.class.getName());
final H h = new H();
final Level origLevel = log.getLevel();
log.setLevel(Level.FINE);
log.addHandler(h);
try {
ClassPath cp = cpp.findClassPath(artefact, JavaClassPathConstants.MODULE_BOOT_PATH);
assertEquals ("DefaultClassPathProvider returned invalid classpath for MODULE_BOOT_PATH with source level 9",
j9.getBootstrapLibraries(),
cp);
List<Optional<JavaPlatform>> plts = h.getCachedPlatforms();
assertEquals(1, plts.size());
assertEquals(j9, plts.get(0).get());
h.reset();
artefact = getSourceFile (FILE_IN_PACKAGE);
cp = cpp.findClassPath(artefact, JavaClassPathConstants.MODULE_BOOT_PATH);
assertEquals ("DefaultClassPathProvider returned invalid classpath for MODULE_BOOT_PATH with source level 9",
j9.getBootstrapLibraries(),
cp);
plts = h.getCachedPlatforms();
assertEquals(0, plts.size());
} finally {
log.removeHandler(h);
log.setLevel(origLevel);
}
}
public void testLRUCaching() throws IOException {
FileObject artefact = getSourceFile (FILE_IN_PACKAGE);
Lookup.getDefault().lookup(MockSLQ.class).register(this.srcRoot, new SpecificationVersion("9")); //NOI18N
assertEquals("9", SourceLevelQuery.getSourceLevel(artefact)); //NOI18N
ClassPathProvider cpp = new DefaultClassPathProvider ();
final Logger log = Logger.getLogger(DefaultClassPathProvider.class.getName());
final H h = new H();
final Level origLevel = log.getLevel();
log.setLevel(Level.FINE);
log.addHandler(h);
try {
ClassPath cp = cpp.findClassPath(artefact, JavaClassPathConstants.MODULE_BOOT_PATH);
assertEquals ("DefaultClassPathProvider returned invalid classpath for MODULE_BOOT_PATH with source level 9",
j9.getBootstrapLibraries(),
cp);
List<JavaPlatform> plts = h.getLRU();
assertEquals(1, plts.size());
assertEquals(j9, plts.get(0));
h.reset();
artefact = getSourceFile (FILE_IN_PACKAGE);
cp = cpp.findClassPath(artefact, JavaClassPathConstants.MODULE_BOOT_PATH);
assertEquals ("DefaultClassPathProvider returned invalid classpath for MODULE_BOOT_PATH with source level 9",
j9.getBootstrapLibraries(),
cp);
plts = h.getLRU();
assertEquals(0, plts.size());
} finally {
log.removeHandler(h);
log.setLevel(origLevel);
}
}
public void testJPMChanges() throws IOException {
final FileObject artefact = getSourceFile (FILE_IN_PACKAGE);
Lookup.getDefault().lookup(MockSLQ.class).register(this.srcRoot, new SpecificationVersion("9")); //NOI18N
assertEquals("9", SourceLevelQuery.getSourceLevel(artefact)); //NOI18N
final JavaPlatform j9Orig = Arrays.stream(JavaPlatformManager.getDefault().getInstalledPlatforms())
.filter((jp) -> "j2se".equals(jp.getSpecification().getName()) //NOI18N
&& new SpecificationVersion("9").equals(jp.getSpecification().getVersion()))
.findFirst()
.get();
ClassPathProvider cpp = new DefaultClassPathProvider ();
final Logger log = Logger.getLogger(DefaultClassPathProvider.class.getName());
final H h = new H();
final Level origLevel = log.getLevel();
log.setLevel(Level.FINE);
log.addHandler(h);
try {
ClassPath cp = cpp.findClassPath(artefact, JavaClassPathConstants.MODULE_BOOT_PATH);
assertEquals ("DefaultClassPathProvider returned invalid classpath for MODULE_BOOT_PATH with source level 9",
j9Orig.getBootstrapLibraries(),
cp);
List<Optional<JavaPlatform>> plts = h.getCachedPlatforms();
assertEquals(1, plts.size());
assertEquals(j9Orig, plts.get(0).get());
List<JavaPlatform> lru = h.getLRU();
assertEquals(1, lru.size());
assertEquals(j9Orig, lru.get(0));
h.reset();
Lookup.getDefault().lookup(JavaPlatformProviderImpl.class).removePlatform(j9Orig);
cp = cpp.findClassPath(artefact, JavaClassPathConstants.MODULE_BOOT_PATH);
assertEquals ("DefaultClassPathProvider returned invalid classpath for MODULE_BOOT_PATH with source level 9",
null,
cp);
plts = h.getCachedPlatforms();
assertEquals(1, plts.size());
assertFalse(plts.get(0).isPresent());
lru = h.getLRU();
assertEquals(1, lru.size());
assertNull(lru.get(0));
h.reset();
Lookup.getDefault().lookup(JavaPlatformProviderImpl.class).addPlatform(j9Orig);
cp = cpp.findClassPath(artefact, JavaClassPathConstants.MODULE_BOOT_PATH);
assertEquals ("DefaultClassPathProvider returned invalid classpath for MODULE_BOOT_PATH with source level 9",
j9Orig.getBootstrapLibraries(),
cp);
plts = h.getCachedPlatforms();
assertEquals(1, plts.size());
assertEquals(j9Orig, plts.get(0).get());
lru = h.getLRU();
assertEquals(1, lru.size());
assertEquals(j9Orig, lru.get(0));
} finally {
log.removeHandler(h);
log.setLevel(origLevel);
}
}
public void testModularSources_UserModules() throws Exception {
final FileObject workDir = FileUtil.toFileObject(FileUtil.normalizeFile(this.getWorkDir()));
final FileObject modulesFolder = FileUtil.createFolder(workDir, "modules");
final FileObject[] modules = new FileObject[3];
for (int i = 0; i < modules.length; i++) {
modules[i] = FileUtil.createFolder(modulesFolder, "module" + (char) ('a'+i));
}
final ClassPath mp = ClassPathSupport.createClassPath(modules);
GlobalPathRegistry.getDefault().register(JavaClassPathConstants.MODULE_COMPILE_PATH, new ClassPath[]{mp});
try {
final FileObject artefact = libSourceRoots[0];
Lookup.getDefault().lookup(MockSLQ.class).register(artefact, new SpecificationVersion("1.8")); //NOI18N
assertEquals("1.8", SourceLevelQuery.getSourceLevel(artefact)); //NOI18N
ClassPathProvider cpp = new DefaultClassPathProvider ();
ClassPath cp = cpp.findClassPath(artefact, JavaClassPathConstants.MODULE_COMPILE_PATH);
assertNull ("DefaultClassPathProvider returned not null for MODULE_COMPILE_PATH with source level 8",cp);
Lookup.getDefault().lookup(MockSLQ.class).register(artefact, new SpecificationVersion("9")); //NOI18N
assertEquals("9", SourceLevelQuery.getSourceLevel(artefact)); //NOI18N
cpp = new DefaultClassPathProvider ();
cp = cpp.findClassPath(artefact, JavaClassPathConstants.MODULE_COMPILE_PATH);
assertEquals(
mp.entries().stream()
.map((e) -> e.getRoot())
.sorted((a,b) -> a.getPath().compareTo(b.getPath()))
.collect(Collectors.toList()),
cp.entries().stream()
.map((e) -> e.getRoot())
.sorted((a,b) -> a.getPath().compareTo(b.getPath()))
.collect(Collectors.toList()));
} finally {
GlobalPathRegistry.getDefault().unregister(JavaClassPathConstants.MODULE_COMPILE_PATH, new ClassPath[]{mp});
}
}
private static boolean contains (final ClassPath cp, final URL url) {
for (ClassPath.Entry entry : cp.entries()) {
if (entry.getURL().equals(url)) {
return true;
}
}
return false;
}
private static void assertRootsEquals (String message, ClassPath cp, FileObject[] roots) {
Set<FileObject> cpRoots = new HashSet<FileObject>(Arrays.asList(cp.getRoots()));
assertEquals(message, cpRoots.size(), roots.length);
for (FileObject root : roots) {
if (!cpRoots.contains(root)) {
assertTrue(message, false);
}
}
}
private synchronized FileObject getSourceFile (int type) throws IOException {
if (this.srcFile[type]==null) {
assertNotNull (this.srcRoot);
switch (type) {
case FILE_IN_PACKAGE:
this.srcFile[type] = createFile (this.srcRoot,"test","Test","package test;\npublic class Test {}");
break;
case FILE_IN_DEFAULT_PACKAGE:
this.srcFile[type] = createFile (this.srcRoot,null,"DefaultTest","public class DefaultTest {}");
break;
case FILE_IN_BAD_PACKAGE:
this.srcFile[type] = createFile (this.srcRoot,"test","BadTest","package bad;\npublic class BadTest {}");
break;
default:
throw new IllegalArgumentException ();
}
}
return this.srcFile[type];
}
private synchronized FileObject getClassFile () throws IOException {
FileObject fo = this.execTestDir.getFileObject("test/Test.class");
if (fo == null) {
fo = execTestDir.createFolder("test");
fo = fo.createData("Test","class");
FileLock lock = fo.lock();
try {
DataOutputStream out = new DataOutputStream (fo.getOutputStream(lock));
try {
out.write(CLASS_FILE_DATA);
out.flush();
} finally {
out.close();
}
} finally {
lock.releaseLock();
}
}
return fo;
}
private static FileObject createFile (FileObject root, String folderName, String name, String body) throws IOException {
if (folderName != null) {
FileObject tmp = root.getFileObject(folderName,null);
if (tmp == null) {
tmp = root.createFolder (folderName);
}
root = tmp;
}
FileObject file = root.createData (name,"java");
FileLock lock = file.lock();
try {
PrintWriter out = new PrintWriter ( new OutputStreamWriter (file.getOutputStream(lock)));
try {
out.println (body);
} finally {
out.close ();
}
} finally {
lock.releaseLock();
}
return file;
}
public static class SFBQI implements SourceForBinaryQueryImplementation {
public SFBQI () {
}
public SourceForBinaryQuery.Result findSourceRoots(URL binaryRoot) {
for (int i = 0; i < execRoots.length; i++) {
final URL url = execRoots[i].toURL ();
if (url.equals (binaryRoot)) {
return new SourceForBinaryQuery.Result () {
public FileObject[] getRoots () {
return libSourceRoots;
}
public void addChangeListener (ChangeListener l) {
}
public void removeChangeListener (ChangeListener l) {
}
};
}
}
return null;
}
}
public static class OpenProject implements OpenProjectsTrampoline {
static Future<Project[]> future;
@Override
public Project[] getOpenProjectsAPI() {
return new Project[0];
}
@Override
public void openAPI(Project[] projects, boolean openRequiredProjects, boolean showProgress) {
}
@Override
public void closeAPI(Project[] projects) {
}
@Override
public Project getMainProject() {
return null;
}
@Override
public void setMainProject(Project project) {
}
@Override
public void addPropertyChangeListenerAPI(PropertyChangeListener listener, Object source) {
}
@Override
public void removePropertyChangeListenerAPI(PropertyChangeListener listener) {
}
@Override
public Future<Project[]> openProjectsAPI() {
return future;
}
@Override
public ProjectGroup getActiveProjectGroupAPI() {
return null;
}
@Override
public void addProjectGroupChangeListenerAPI(ProjectGroupChangeListener listener) {
}
@Override
public void removeProjectGroupChangeListenerAPI(ProjectGroupChangeListener listener) {
}
}
private static class Listener implements PropertyChangeListener {
private volatile int count;
private final Lock lock = new ReentrantLock();
private final Condition cnd = lock.newCondition();
public void reset() {
lock.lock();
try {
count = 0;
} finally {
lock.unlock();
}
}
public int get() {
return count;
}
public boolean awaitEvent() throws InterruptedException {
lock.lock();
try {
while (count == 0) {
if (!cnd.await(DELAY, TimeUnit.MILLISECONDS)) {
return false;
}
}
} finally {
lock.unlock();
}
return true;
}
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (ClassPath.PROP_ENTRIES.equals(evt.getPropertyName())) {
lock.lock();
try {
count = count + 1;
cnd.signal();
} finally {
lock.unlock();
}
}
}
}
private static final class J9 extends JavaPlatform {
private final FileObject installFolder;
private final ClassPath boot;
public J9(
@NonNull final FileObject installFolder,
@NonNull final FileObject javaBase) {
assert installFolder != null;
assert javaBase != null;
this.installFolder = installFolder;
this.boot = ClassPathSupport.createClassPath(javaBase);
}
@Override
public String getDisplayName() {
return installFolder.getNameExt();
}
@Override
public ClassPath getBootstrapLibraries() {
return boot;
}
@Override
public ClassPath getStandardLibraries() {
return ClassPath.EMPTY;
}
@Override
public ClassPath getSourceFolders() {
return ClassPath.EMPTY;
}
@Override
public List<URL> getJavadocFolders() {
return Collections.emptyList();
}
@Override
public String getVendor() {
return "Oracle"; //NOI18N
}
@Override
public Collection<FileObject> getInstallFolders() {
return Collections.singleton(installFolder);
}
@Override
public FileObject findTool(String toolName) {
return null;
}
@Override
public Map<String, String> getProperties() {
return Collections.emptyMap();
}
@Override
public Specification getSpecification() {
return new Specification("j2se", new SpecificationVersion("9")); //NOI18N
}
}
public static final class MockSLQ implements SourceLevelQueryImplementation2 {
private Map<FileObject,R> levels;
public MockSLQ() {
this.levels = new HashMap<>();
}
void register(
@NonNull final FileObject root,
@NonNull final SpecificationVersion sl) {
assert root != null;
levels.put(root, new R(sl));
}
void reset() {
levels.clear();
}
@Override
public Result getSourceLevel(FileObject javaFile) {
for (Map.Entry<FileObject,R> e : this.levels.entrySet()) {
final FileObject root = e.getKey();
if (root.equals(javaFile) || FileUtil.isParentOf(root, javaFile)) {
return e.getValue();
}
}
return null;
}
private static final class R implements Result {
private final SpecificationVersion sl;
R(@NonNull final SpecificationVersion sl) {
assert sl != null;
this.sl = sl;
}
@Override
public String getSourceLevel() {
return sl.toString();
}
@Override
public void addChangeListener(ChangeListener listener) {
}
@Override
public void removeChangeListener(ChangeListener listener) {
}
}
}
private static final class H extends Handler {
private final List<Optional<JavaPlatform>> cachedPlatforms = new ArrayList<>();
private final List<JavaPlatform> lru = new ArrayList<>();
H() {
}
void reset() {
cachedPlatforms.clear();
lru.clear();
}
@NonNull
List<Optional<JavaPlatform>> getCachedPlatforms() {
return new ArrayList<>(cachedPlatforms);
}
@NonNull
List<JavaPlatform> getLRU() {
return new ArrayList<>(lru);
}
@Override
public void publish(LogRecord record) {
final String msg = record.getMessage();
if (msg != null) {
switch (msg) {
case "platformCache updated: {0}":
cachedPlatforms.add((Optional<JavaPlatform>)record.getParameters()[0]);
break;
case "lru updated: {0}":
lru.add((JavaPlatform)record.getParameters()[0]);
break;
}
}
}
@Override
public void flush() {
}
@Override
public void close() throws SecurityException {
}
}
}