diff options
author | Igor Fedorenko | 2017-11-19 23:26:34 +0000 |
---|---|---|
committer | Igor Fedorenko | 2018-05-21 13:17:14 +0000 |
commit | b68b8b26d23c41b94abd1b02a1864ab52038f6c1 (patch) | |
tree | 0a61500c5afc9f8c56a4e65b308d5f1b81bcb3b0 /org.eclipse.m2e.sourcelookup/src | |
parent | 4c27b30f5335de0e98805a42ff83622a854daedd (diff) | |
download | m2e-core-b68b8b26d23c41b94abd1b02a1864ab52038f6c1.tar.gz m2e-core-b68b8b26d23c41b94abd1b02a1864ab52038f6c1.tar.xz m2e-core-b68b8b26d23c41b94abd1b02a1864ab52038f6c1.zip |
Bug 384065: advanced sourcelookup support
Change-Id: Ib145445c60c686bbbc187f252b32591535ac740d
Signed-off-by: Igor Fedorenko <igor@ifedorenko.com>
Diffstat (limited to 'org.eclipse.m2e.sourcelookup/src')
4 files changed, 376 insertions, 0 deletions
diff --git a/org.eclipse.m2e.sourcelookup/src/org/eclipse/m2e/sourcelookup/internal/launch/MavenArtifactIdentifier.java b/org.eclipse.m2e.sourcelookup/src/org/eclipse/m2e/sourcelookup/internal/launch/MavenArtifactIdentifier.java new file mode 100644 index 00000000..af625b72 --- /dev/null +++ b/org.eclipse.m2e.sourcelookup/src/org/eclipse/m2e/sourcelookup/internal/launch/MavenArtifactIdentifier.java @@ -0,0 +1,160 @@ +/******************************************************************************* + * Copyright (c) 2011-2016 Igor Fedorenko + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Igor Fedorenko - initial API and implementation + *******************************************************************************/ +package org.eclipse.m2e.sourcelookup.internal.launch; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.URL; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Properties; +import java.util.Set; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.m2e.core.MavenPlugin; +import org.eclipse.m2e.core.embedder.ArtifactKey; +import org.eclipse.m2e.core.internal.index.IIndex; +import org.eclipse.m2e.core.internal.index.IndexedArtifactFile; +import org.eclipse.m2e.core.internal.index.nexus.CompositeIndex; + +import com.google.common.base.Charsets; +import com.google.common.collect.ImmutableSet; +import com.google.common.hash.Hashing; +import com.google.common.io.Files; +import com.google.gson.Gson; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; + +@SuppressWarnings("restriction") +public class MavenArtifactIdentifier { + + // reads META-INF/maven/**/pom.properties + private static final MetaInfMavenScanner<Properties> scanner = new MetaInfMavenScanner<Properties>() { + @Override + protected Properties visitFile(File file) throws IOException { + // TODO validate properties and path match + try (InputStream is = new BufferedInputStream(new FileInputStream(file))) { + return loadProperties(is); + } + } + + @Override + protected Properties visitJarEntry(JarFile jar, JarEntry entry) throws IOException { + // TODO validate properties and path match + try (InputStream is = jar.getInputStream(entry)) { + return loadProperties(is); + } + } + + private Properties loadProperties(InputStream is) throws IOException { + Properties properties = new Properties(); + properties.load(is); + return properties; + } + }; + + public Collection<ArtifactKey> identify(File classesLocation, IProgressMonitor monitor) { + // checksum-based lookup in nexus index + // checksum-based lookup in central + // GAV extracted from pom.properties + + Collection<ArtifactKey> classesArtifacts = identifyNexusIndexer(classesLocation); + if (classesArtifacts == null) { + classesArtifacts = identifyCentralSearch(classesLocation); + } + if (classesArtifacts == null) { + classesArtifacts = scanPomProperties(classesLocation); + } + + return classesArtifacts; + } + + protected Collection<ArtifactKey> identifyNexusIndexer(File file) { + if (!file.isFile()) { + return null; + } + + try { + IIndex index = MavenPlugin.getIndexManager().getAllIndexes(); + + List<IndexedArtifactFile> identified; + if (index instanceof CompositeIndex) { + identified = ((CompositeIndex) index).identifyAll(file); + } else { + IndexedArtifactFile indexed = index.identify(file); + if (indexed != null) { + identified = Collections.singletonList(indexed); + } else { + identified = Collections.emptyList(); + } + } + + for (IndexedArtifactFile indexed : identified) { + if (indexed.sourcesExists == IIndex.PRESENT) { + return Collections.singleton(indexed.getArtifactKey()); + } + } + } catch (CoreException e) { + // TODO maybe log, but ignore otherwise + } + + return null; + } + + protected Collection<ArtifactKey> identifyCentralSearch(File file) { + if (!file.isFile()) { + return null; + } + + try { + String sha1 = Files.hash(file, Hashing.sha1()).toString(); // TODO use Locations for caching + URL url = new URL("https://search.maven.org/solrsearch/select?q=1:" + sha1); + try (InputStreamReader reader = new InputStreamReader(url.openStream(), Charsets.UTF_8)) { + Set<ArtifactKey> result = new LinkedHashSet<>(); + JsonObject container = new Gson().fromJson(reader, JsonObject.class); + JsonArray docs = container.get("response").getAsJsonObject().get("docs").getAsJsonArray(); + for (int i = 0; i < docs.size(); i++) { + JsonObject doc = docs.get(i).getAsJsonObject(); + String g = doc.get("g").getAsString(); + String a = doc.get("a").getAsString(); + String v = doc.get("v").getAsString(); + result.add(new ArtifactKey(g, a, v, null)); + } + return !result.isEmpty() ? ImmutableSet.copyOf(result) : null; + } + } catch (IOException e) { + // TODO maybe log, ignore otherwise + } + return null; + } + + public Collection<ArtifactKey> scanPomProperties(File classesLocation) { + Set<ArtifactKey> artifacts = new LinkedHashSet<>(); + for (Properties pomProperties : scanner.scan(classesLocation, "pom.properties")) { + String groupId = pomProperties.getProperty("groupId"); + String artifactId = pomProperties.getProperty("artifactId"); + String version = pomProperties.getProperty("version"); + if (groupId != null && artifactId != null && version != null) { + artifacts.add(new ArtifactKey(groupId, artifactId, version, /* classifier= */null)); + } + } + return ImmutableSet.copyOf(artifacts); + } +} diff --git a/org.eclipse.m2e.sourcelookup/src/org/eclipse/m2e/sourcelookup/internal/launch/MavenSourceContainerResolver.java b/org.eclipse.m2e.sourcelookup/src/org/eclipse/m2e/sourcelookup/internal/launch/MavenSourceContainerResolver.java new file mode 100644 index 00000000..6b4a6b26 --- /dev/null +++ b/org.eclipse.m2e.sourcelookup/src/org/eclipse/m2e/sourcelookup/internal/launch/MavenSourceContainerResolver.java @@ -0,0 +1,85 @@ +/******************************************************************************* + * Copyright (c) 2011-2016 Igor Fedorenko + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Igor Fedorenko - initial API and implementation + *******************************************************************************/ +package org.eclipse.m2e.sourcelookup.internal.launch; + +import java.io.File; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.repository.ArtifactRepository; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.debug.core.sourcelookup.ISourceContainer; +import org.eclipse.debug.core.sourcelookup.containers.ExternalArchiveSourceContainer; +import org.eclipse.jdt.core.JavaCore; +import org.eclipse.jdt.launching.sourcelookup.advanced.ISourceContainerResolver; +import org.eclipse.jdt.launching.sourcelookup.containers.JavaProjectSourceContainer; +import org.eclipse.m2e.core.MavenPlugin; +import org.eclipse.m2e.core.embedder.ArtifactKey; +import org.eclipse.m2e.core.embedder.IMaven; +import org.eclipse.m2e.core.project.IMavenProjectFacade; +import org.eclipse.m2e.core.project.IMavenProjectRegistry; + + +public class MavenSourceContainerResolver implements ISourceContainerResolver { + + private static final MavenArtifactIdentifier INDENTIFIER = new MavenArtifactIdentifier(); + + @Override + public Collection<ISourceContainer> resolveSourceContainers(File classesLocation, IProgressMonitor monitor) { + Collection<ArtifactKey> classesArtifacts = INDENTIFIER.identify(classesLocation, monitor); + + if (classesArtifacts == null) { + return null; + } + + List<ISourceContainer> result = new ArrayList<>(); + for (ArtifactKey classesArtifact : classesArtifacts) { + ISourceContainer container = resolveSourceContainer(classesArtifact, monitor); + if (container != null) { + result.add(container); + } + } + return result; + } + + protected ISourceContainer resolveSourceContainer(ArtifactKey artifact, IProgressMonitor monitor) { + String groupId = artifact.getGroupId(); + String artifactId = artifact.getArtifactId(); + String version = artifact.getVersion(); + + IMaven maven = MavenPlugin.getMaven(); + IMavenProjectRegistry projectRegistry = MavenPlugin.getMavenProjectRegistry(); + + IMavenProjectFacade mavenProject = projectRegistry.getMavenProject(groupId, artifactId, version); + if (mavenProject != null) { + return new JavaProjectSourceContainer(JavaCore.create(mavenProject.getProject())); + } + + try { + List<ArtifactRepository> repositories = new ArrayList<ArtifactRepository>(); + repositories.addAll(maven.getArtifactRepositories()); + repositories.addAll(maven.getPluginArtifactRepositories()); + + if (!maven.isUnavailable(groupId, artifactId, version, "jar", "sources", repositories)) { + Artifact resolve = maven.resolve(groupId, artifactId, version, "jar", "sources", null, monitor); + + return new ExternalArchiveSourceContainer(resolve.getFile().getAbsolutePath(), true); + } + } catch (CoreException e) { + // TODO maybe log, ignore otherwise + } + + return null; + } +} diff --git a/org.eclipse.m2e.sourcelookup/src/org/eclipse/m2e/sourcelookup/internal/launch/MavenSourceLookupParticipant.java b/org.eclipse.m2e.sourcelookup/src/org/eclipse/m2e/sourcelookup/internal/launch/MavenSourceLookupParticipant.java new file mode 100644 index 00000000..16954c74 --- /dev/null +++ b/org.eclipse.m2e.sourcelookup/src/org/eclipse/m2e/sourcelookup/internal/launch/MavenSourceLookupParticipant.java @@ -0,0 +1,41 @@ +/******************************************************************************* + * Copyright (c) 2011-2016 Igor Fedorenko + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Igor Fedorenko - initial API and implementation + *******************************************************************************/ +package org.eclipse.m2e.sourcelookup.internal.launch; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.debug.core.sourcelookup.ISourceLookupDirector; +import org.eclipse.jdt.launching.sourcelookup.advanced.AdvancedSourceLookupParticipant; +import org.eclipse.m2e.core.MavenPlugin; +import org.eclipse.m2e.core.project.IMavenProjectChangedListener; +import org.eclipse.m2e.core.project.MavenProjectChangedEvent; + + +public class MavenSourceLookupParticipant extends AdvancedSourceLookupParticipant + implements + IMavenProjectChangedListener { + + @Override + public void init(ISourceLookupDirector director) { + super.init(director); + MavenPlugin.getMavenProjectRegistry().addMavenProjectChangedListener(this); + } + + @Override + public void dispose() { + MavenPlugin.getMavenProjectRegistry().removeMavenProjectChangedListener(this); + super.dispose(); + } + + @Override + public void mavenProjectChanged(MavenProjectChangedEvent[] events, IProgressMonitor monitor) { + disposeContainers(); + } +} diff --git a/org.eclipse.m2e.sourcelookup/src/org/eclipse/m2e/sourcelookup/internal/launch/MetaInfMavenScanner.java b/org.eclipse.m2e.sourcelookup/src/org/eclipse/m2e/sourcelookup/internal/launch/MetaInfMavenScanner.java new file mode 100644 index 00000000..77ddb23b --- /dev/null +++ b/org.eclipse.m2e.sourcelookup/src/org/eclipse/m2e/sourcelookup/internal/launch/MetaInfMavenScanner.java @@ -0,0 +1,90 @@ +/******************************************************************************* + * Copyright (c) 2011-2012 Igor Fedorenko + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Igor Fedorenko - initial API and implementation + *******************************************************************************/ +package org.eclipse.m2e.sourcelookup.internal.launch; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.List; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; + +/** + * Helper to find and extract information from META-INF/maven pom.properties files. + */ +public abstract class MetaInfMavenScanner<T> { + + private static final String META_INF_MAVEN = "META-INF/maven"; + + public List<T> scan(File file, String filename) { + List<T> result = new ArrayList<T>(); + if (file != null) { + if (file.isDirectory()) { + scanFilesystem(new File(file, META_INF_MAVEN), filename, result); + } else if (file.isFile()) { + try { + try (JarFile jar = new JarFile(file)) { + scanJar(jar, filename, result); + } + } catch (IOException e) { + // fall through + } + } + } + return result; + } + + private void scanJar(JarFile jar, String filename, List<T> result) throws IOException { + Enumeration<JarEntry> entries = jar.entries(); + while (entries.hasMoreElements()) { + JarEntry entry = entries.nextElement(); + if (!entry.isDirectory()) { + String name = entry.getName(); + if (name.startsWith(META_INF_MAVEN) && name.endsWith(filename)) { + try { + T t = visitJarEntry(jar, entry); + if (t != null) { + result.add(t); + } + } catch (IOException e) { + // ignore + } + } + } + } + } + + private void scanFilesystem(File dir, String filename, List<T> result) { + File[] files = dir.listFiles(); + if (files == null) { + return; + } + for (File file : files) { + if (file.isDirectory()) { + scanFilesystem(file, filename, result); + } else if (file.isFile() && filename.equals(file.getName())) { + try { + T t = visitFile(file); + if (t != null) { + result.add(t); + } + } catch (IOException e) { + // ignore + } + } + } + } + + protected abstract T visitFile(File file) throws IOException; + + protected abstract T visitJarEntry(JarFile jar, JarEntry entry) throws IOException; +} |