From 35afb62e8fe3601006df4b1f8371f16e7af52c68 Mon Sep 17 00:00:00 2001 From: thomas Date: Mon, 7 Oct 2019 11:09:18 +0200 Subject: [PATCH] [NETBEANS-1396] Fall back to main javadoc and source jars for Maven dependencies with classifiers OpenJFX uses Maven classifiers for platform dependent code, i.e. javafx-graphics-13.jar does not contain any code, but it depends through build profiles on javafx-graphics-13-linux.jar. There is however no javafx-graphics-13-linux-javadoc.jar, only a javafx-graphics-13-javadoc.jar, which NetBeans doesn't find. This patch lets NetBeans fall back to the main javadoc jar if there is none associated with the one with the classifier. The contents of the javadoc jars of OpenJFX are prefixed by module name, which is different from the default output of the maven-javadoc-plugin. This patch does some bytecode reading to still find the correct .html file. Fix RepositoryForBinaryQueryImplTest::testResultChanging which was broken because of a missing test resource. --- .../modules/java/source/JavadocHelper.java | 28 ++++- .../queries/RepositoryForBinaryQueryImpl.java | 30 ++++- .../RepositoryForBinaryQueryImplTest.java | 111 +++++++++++++++++- 3 files changed, 164 insertions(+), 5 deletions(-) diff --git a/java/java.source.base/src/org/netbeans/modules/java/source/JavadocHelper.java b/java/java.source.base/src/org/netbeans/modules/java/source/JavadocHelper.java index 48f36360c4be..c0db82ecd2ef 100644 --- a/java/java.source.base/src/org/netbeans/modules/java/source/JavadocHelper.java +++ b/java/java.source.base/src/org/netbeans/modules/java/source/JavadocHelper.java @@ -77,6 +77,8 @@ import org.netbeans.api.java.queries.SourceForBinaryQuery; import org.netbeans.api.java.source.SourceUtils; import org.netbeans.api.progress.ProgressHandle; +import org.netbeans.modules.classfile.ClassFile; +import org.netbeans.modules.classfile.Module; import org.netbeans.modules.java.source.base.Bundle; import org.netbeans.modules.java.source.indexing.JavaIndex; import org.netbeans.modules.java.source.parsing.CachingArchiveProvider; @@ -768,7 +770,31 @@ private static List findJavadoc( LOG.log(Level.FINE, "assumed valid Javadoc stream at {0}", url); } else if (!speculative || !isRemote) { try { - is = openStream(url, Bundle.LBL_HTTPJavadocDownload()); + try { + is = openStream(url, Bundle.LBL_HTTPJavadocDownload()); + } catch (InterruptedIOException iioe) { + throw iioe; + } catch (IOException x) { + // Some libraries like OpenJFX prefix their + // javadoc by module, similar to the JDK. + // Only search there when the default fails + // to avoid additional I/O. + // NOTE: No multi-release jar support for now. + URL moduleInfo = new URL(binary, "module-info.class"); + try (InputStream classData = moduleInfo.openStream()) { + ClassFile clazz = new ClassFile(classData, false); + Module module = clazz.getModule(); + if (module == null) { + throw x; + } + String moduleName = module.getName(); + if (moduleName == null) { + throw x; + } + url = new URL(root, moduleName + "/" + pkgName + "/" + pageName + ".html"); + is = openStream(url, Bundle.LBL_HTTPJavadocDownload()); + } + } if (useKnownGoodRoots) { knownGoodRoots.add(rootS); LOG.log(Level.FINE, "found valid Javadoc stream at {0}", url); diff --git a/java/maven/src/org/netbeans/modules/maven/queries/RepositoryForBinaryQueryImpl.java b/java/maven/src/org/netbeans/modules/maven/queries/RepositoryForBinaryQueryImpl.java index f2b29543c7ff..8898f270d399 100644 --- a/java/maven/src/org/netbeans/modules/maven/queries/RepositoryForBinaryQueryImpl.java +++ b/java/maven/src/org/netbeans/modules/maven/queries/RepositoryForBinaryQueryImpl.java @@ -263,7 +263,7 @@ public JavadocForBinaryQuery.Result findJavadoc(URL url) { //we have classifier here.. String end = jarFile.getName().substring((start + "-").length()); if (end.indexOf('.') > -1) { - classifier = end.substring(end.indexOf('.')); + classifier = end.substring(0, end.indexOf('.')); } } File javadoc = new File(parent, start + (classifier != null ? ("-" + ("tests".equals(classifier) ? "test" : classifier)) : "") + "-javadoc.jar"); //NOI18N @@ -290,6 +290,7 @@ private static class SrcResult implements SourceForBinaryQueryImplementation2.Re private static final String ATTR_PATH = "lastRootCheckPath"; //NOI18N private static final String ATTR_STAMP = "lastRootCheckStamp"; //NOI18N private final File sourceJarFile; + private final File fallbackSourceJarFile; private final ChangeSupport support; private final ChangeListener mfoListener; private final PropertyChangeListener projectListener; @@ -365,7 +366,17 @@ public void fileDataCreated(FileEvent fe) { if (sourceJarFile != null) { FileUtil.addFileChangeListener(FileUtil.weakFileChangeListener(sourceJarChangeListener, null)); + if (classifier != null) { + // fall back to regular sources if attached sources for classifier are missing + String regularSources = artifactId + "-" + version + "-sources.jar"; //NOI18N + if (!sourceJarFile.getName().equals(regularSources)) { + fallbackSourceJarFile = new File(sourceJarFile.getParentFile(), regularSources); + // already listening for changes through the file change listener above + return; + } + } } + fallbackSourceJarFile = null; } private void checkChanges(boolean fireChanges) { @@ -448,7 +459,9 @@ public FileObject[] getRoots() { // the only way to let user decide would be some sort of stamp file inside maven local repository. if (sourceJarFile != null && sourceJarFile.exists()) { add(fos, getSourceJarRoot(sourceJarFile)); - } + } else if (fallbackSourceJarFile != null && fallbackSourceJarFile.exists()) { + add(fos, getSourceJarRoot(fallbackSourceJarFile)); + } add(fos, getShadedJarSources()); toRet = fos.toArray(new FileObject[0]); } @@ -635,6 +648,7 @@ public static List getJarMetadataCoordinates(File binaryFile) { private static class JavadocResult implements JavadocForBinaryQuery.Result { private final File javadocJarFile; + private final File fallbackJavadocJarFile; private final String groupId; private final String artifactId; private final String version; @@ -689,7 +703,17 @@ public void fileDataCreated(FileEvent fe) { WeakListeners.create(ChangeListener.class, mfoListener, MavenFileOwnerQueryImpl.getInstance())); if (javadocJarFile != null) { FileUtil.addFileChangeListener(javadocJarChangeListener, javadocJarFile); + if (classifier != null) { + // listen for regular javadoc because attached javadoc for classifier might be missing + String regularJavadoc = artifactId + "-" + version + "-javadoc.jar"; //NOI18N + if (!javadocJarFile.getName().equals(regularJavadoc)) { + fallbackJavadocJarFile = new File(javadocJarFile.getParentFile(), regularJavadoc); + FileUtil.addFileChangeListener(javadocJarChangeListener, fallbackJavadocJarFile); + return; + } + } } + fallbackJavadocJarFile = null; } @Override @@ -713,6 +737,8 @@ public synchronized URL[] getRoots() { toRet = accum.toArray(new URL[0]); } else if (javadocJarFile != null && javadocJarFile.exists()) { toRet = getJavadocJarRoot(javadocJarFile); + } else if (fallbackJavadocJarFile != null && fallbackJavadocJarFile.exists()) { + toRet = getJavadocJarRoot(fallbackJavadocJarFile); } else { toRet = checkShadedMultiJars(); } diff --git a/java/maven/test/unit/src/org/netbeans/modules/maven/queries/RepositoryForBinaryQueryImplTest.java b/java/maven/test/unit/src/org/netbeans/modules/maven/queries/RepositoryForBinaryQueryImplTest.java index a9aaa30ffa66..0aee9e38a13a 100644 --- a/java/maven/test/unit/src/org/netbeans/modules/maven/queries/RepositoryForBinaryQueryImplTest.java +++ b/java/maven/test/unit/src/org/netbeans/modules/maven/queries/RepositoryForBinaryQueryImplTest.java @@ -26,6 +26,8 @@ import java.util.Arrays; import org.junit.Test; import static org.junit.Assert.*; +import org.netbeans.api.java.queries.JavadocForBinaryQuery; +import org.netbeans.api.java.queries.SourceForBinaryQuery; import org.netbeans.api.project.ProjectManager; import org.netbeans.junit.NbTestCase; import org.netbeans.modules.maven.NbMavenProjectImpl; @@ -59,6 +61,10 @@ protected void tearDown() throws Exception { super.tearDown(); File prj10 = new File(getWorkDir(), "prj10"); org.codehaus.plexus.util.FileUtils.deleteDirectory(prj10); + + File repo = EmbedderFactory.getProjectEmbedder().getLocalRepositoryFile(); + File nbtest = new File(repo, "nbtest"); + org.codehaus.plexus.util.FileUtils.deleteDirectory(nbtest); } @@ -79,9 +85,8 @@ public void testResultChanging() throws IOException { // now create source jar - File sourceJar = new File(this.getDataDir(), "source.jar"); File repoSourceJar = new File(art10.getParentFile(), "testprj-1.0-sources.jar"); - org.codehaus.plexus.util.FileUtils.copyFile(sourceJar, repoSourceJar); + org.codehaus.plexus.util.FileUtils.copyFile(art10, repoSourceJar); assertEquals(1, result.getRoots().length); assertEquals(FileUtil.getArchiveRoot(FileUtil.toFileObject(repoSourceJar)), result.getRoots()[0]); @@ -128,5 +133,107 @@ public void testResultChanging() throws IOException { } + + @Test + public void testFindJavadoc() throws IOException { + File repo = EmbedderFactory.getProjectEmbedder().getLocalRepositoryFile(); + File parent = new File(repo, "nbtest/testprj/1.0"); + RepositoryForBinaryQueryImpl query = new RepositoryForBinaryQueryImpl(); + + // find nothing + File artifact = new File(parent, "testprj-1.0.jar"); + TestFileUtils.writeZipFile(artifact, "META-INF/MANIFEST.MF:Version:1.0"); + URL artifactRoot = FileUtil.getArchiveRoot(artifact.toURI().toURL()); + JavadocForBinaryQuery.Result result = query.findJavadoc(artifactRoot); + assertNotNull(result); + assertEquals(0, result.getRoots().length); + + // find regular javadoc + File javadoc = new File(parent, "testprj-1.0-javadoc.jar"); + TestFileUtils.writeZipFile(javadoc, "META-INF/MANIFEST.MF:Version:1.0"); + URL javadocRoot = FileUtil.getArchiveRoot(javadoc.toURI().toURL()); + result = query.findJavadoc(artifactRoot); + assertEquals(1, result.getRoots().length); + assertEquals(javadocRoot, result.getRoots()[0]); + + // classifier attachments should fall back to the regular javadoc + File attachment = new File(parent, "testprj-1.0-attachment.jar"); + TestFileUtils.writeZipFile(attachment, "META-INF/MANIFEST.MF:Version:1.0"); + URL attachmentRoot = FileUtil.getArchiveRoot(attachment.toURI().toURL()); + result = query.findJavadoc(attachmentRoot); + assertEquals(1, result.getRoots().length); + assertEquals(javadocRoot, result.getRoots()[0]); + + // classifier attachments should find their own javadoc + File attachmentJavadoc = new File(parent, "testprj-1.0-attachment-javadoc.jar"); + TestFileUtils.writeZipFile(attachmentJavadoc, "META-INF/MANIFEST.MF:Version:1.0"); + URL attachmentJavadocRoot = FileUtil.getArchiveRoot(attachmentJavadoc.toURI().toURL()); + result = query.findJavadoc(attachmentRoot); + assertEquals(1, result.getRoots().length); + assertEquals(attachmentJavadocRoot, result.getRoots()[0]); + + // result reacts to filesystem changes + org.codehaus.plexus.util.FileUtils.forceDelete(attachmentJavadoc); + assertEquals(1, result.getRoots().length); + assertEquals(javadocRoot, result.getRoots()[0]); + + org.codehaus.plexus.util.FileUtils.forceDelete(javadoc); + assertEquals(0, result.getRoots().length); + + TestFileUtils.writeZipFile(javadoc, "META-INF/MANIFEST.MF:Version:1.0"); + assertEquals(1, result.getRoots().length); + assertEquals(javadocRoot, result.getRoots()[0]); + } + + @Test + public void testFindSources() throws IOException { + File repo = EmbedderFactory.getProjectEmbedder().getLocalRepositoryFile(); + File parent = new File(repo, "nbtest/testprj/1.0"); + RepositoryForBinaryQueryImpl query = new RepositoryForBinaryQueryImpl(); + + // find nothing + File artifact = new File(parent, "testprj-1.0.jar"); + TestFileUtils.writeZipFile(artifact, "META-INF/MANIFEST.MF:Version:1.0"); + URL artifactRoot = FileUtil.getArchiveRoot(artifact.toURI().toURL()); + SourceForBinaryQuery.Result result = query.findSourceRoots(artifactRoot); + assertNotNull(result); + assertEquals(0, result.getRoots().length); + + // find regular sources + File sources = new File(parent, "testprj-1.0-sources.jar"); + TestFileUtils.writeZipFile(sources, "META-INF/MANIFEST.MF:Version:1.0"); + URL sourcesRoot = FileUtil.getArchiveRoot(sources.toURI().toURL()); + result = query.findSourceRoots(artifactRoot); + assertEquals(1, result.getRoots().length); + assertEquals(sourcesRoot, result.getRoots()[0].toURL()); + + // classifier attachments should fall back to the regular sources + File attachment = new File(parent, "testprj-1.0-attachment.jar"); + TestFileUtils.writeZipFile(attachment, "META-INF/MANIFEST.MF:Version:1.0"); + URL attachmentRoot = FileUtil.getArchiveRoot(attachment.toURI().toURL()); + result = query.findSourceRoots(attachmentRoot); + assertEquals(1, result.getRoots().length); + assertEquals(sourcesRoot, result.getRoots()[0].toURL()); + + // classifier attachments should find their own sources + File attachmentSources = new File(parent, "testprj-1.0-attachment-sources.jar"); + TestFileUtils.writeZipFile(attachmentSources, "META-INF/MANIFEST.MF:Version:1.0"); + URL attachmentSourcesRoot = FileUtil.getArchiveRoot(attachmentSources.toURI().toURL()); + result = query.findSourceRoots(attachmentRoot); + assertEquals(1, result.getRoots().length); + assertEquals(attachmentSourcesRoot, result.getRoots()[0].toURL()); + + // result reacts to filesystem changes + org.codehaus.plexus.util.FileUtils.forceDelete(attachmentSources); + assertEquals(1, result.getRoots().length); + assertEquals(sourcesRoot, result.getRoots()[0].toURL()); + + org.codehaus.plexus.util.FileUtils.forceDelete(sources); + assertEquals(0, result.getRoots().length); + + TestFileUtils.writeZipFile(sources, "META-INF/MANIFEST.MF:Version:1.0"); + assertEquals(1, result.getRoots().length); + assertEquals(sourcesRoot, result.getRoots()[0].toURL()); + } }