diff --git a/tycho-baseline-plugin/pom.xml b/tycho-baseline-plugin/pom.xml
index 5acee8e8ba..f76e211228 100644
--- a/tycho-baseline-plugin/pom.xml
+++ b/tycho-baseline-plugin/pom.xml
@@ -53,6 +53,16 @@
asciitable
0.3.2
+
+ org.ow2.asm
+ asm
+ 9.7.1
+
+
+ org.eclipse.emf
+ org.eclipse.emf.ecore
+ 2.38.0
+
@@ -60,6 +70,10 @@
org.codehaus.plexus
plexus-component-metadata
+
+ org.eclipse.sisu
+ sisu-maven-plugin
+
org.apache.maven.plugins
maven-plugin-plugin
diff --git a/tycho-baseline-plugin/src/main/java/org/eclipse/tycho/baseline/DependencyCheckMojo.java b/tycho-baseline-plugin/src/main/java/org/eclipse/tycho/baseline/DependencyCheckMojo.java
new file mode 100644
index 0000000000..0e229c0cad
--- /dev/null
+++ b/tycho-baseline-plugin/src/main/java/org/eclipse/tycho/baseline/DependencyCheckMojo.java
@@ -0,0 +1,299 @@
+/*******************************************************************************
+ * Copyright (c) 2024 Christoph Läubrich and others.
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Christoph Läubrich - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tycho.baseline;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.stream.Collectors;
+
+import org.apache.maven.plugin.AbstractMojo;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugin.MojoFailureException;
+import org.apache.maven.plugins.annotations.Component;
+import org.apache.maven.plugins.annotations.LifecyclePhase;
+import org.apache.maven.plugins.annotations.Mojo;
+import org.apache.maven.plugins.annotations.Parameter;
+import org.apache.maven.project.MavenProject;
+import org.eclipse.equinox.p2.metadata.IInstallableUnit;
+import org.eclipse.osgi.container.ModuleRevisionBuilder;
+import org.eclipse.osgi.container.ModuleRevisionBuilder.GenericInfo;
+import org.eclipse.osgi.container.builders.OSGiManifestBuilderFactory;
+import org.eclipse.osgi.internal.framework.FilterImpl;
+import org.eclipse.tycho.DependencyArtifacts;
+import org.eclipse.tycho.artifacts.ArtifactVersion;
+import org.eclipse.tycho.artifacts.ArtifactVersionProvider;
+import org.eclipse.tycho.core.TychoProjectManager;
+import org.eclipse.tycho.core.osgitools.BundleReader;
+import org.eclipse.tycho.core.osgitools.OsgiManifest;
+import org.eclipse.tycho.core.resolver.target.ArtifactMatcher;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.VersionRange;
+import org.osgi.framework.namespace.PackageNamespace;
+import org.osgi.resource.Namespace;
+
+/**
+ * This mojos performs deep inspections of dependencies to find out if a version
+ * range is actually valid. For this the following steps are performed:
+ *
+ * - The current project artifact is analyzed for method signatures it
+ * calls
+ * - Then it is checked what of these match to a given dependency
+ * - All dependency versions matching the range are fetched and inspected
+ * using {@link ArtifactVersionProvider}s
+ * - Then it checks if there are any missing signatures or inconsistencies and
+ * possibly failing the build
+ *
+ */
+@Mojo(defaultPhase = LifecyclePhase.VERIFY, name = "check-dependencies", threadSafe = true, requiresProject = true)
+public class DependencyCheckMojo extends AbstractMojo {
+
+ private static final String CLASS_SUFFIX = ".class";
+
+ @Parameter(property = "project", readonly = true)
+ private MavenProject project;
+
+ @Component
+ private TychoProjectManager projectManager;
+
+ @Component
+ private List versionProvider;
+
+ @Component
+ private BundleReader bundleReader;
+
+ @Override
+ public void execute() throws MojoExecutionException, MojoFailureException {
+ // TODO check for packaging types...? But maven now also provides profiles on
+ // packaging types!
+ DependencyArtifacts artifacts = projectManager.getDependencyArtifacts(project).orElse(null);
+ File file = project.getArtifact().getFile();
+ if (file == null || !file.isFile()) {
+ throw new MojoFailureException("Project artifact is not a valid file");
+ }
+ ClassUsage usages = analyzeUsage(file);
+ Collection units = artifacts.getInstallableUnits();
+ ModuleRevisionBuilder builder = readOSGiInfo(file);
+ List requirements = builder.getRequirements();
+ List dependencyProblems = new ArrayList<>();
+ for (GenericInfo genericInfo : requirements) {
+ if (PackageNamespace.PACKAGE_NAMESPACE.equals(genericInfo.getNamespace())) {
+ Map pkgInfo = getVersionInfo(genericInfo,
+ PackageNamespace.CAPABILITY_VERSION_ATTRIBUTE);
+ String packageVersion = pkgInfo.getOrDefault(PackageNamespace.CAPABILITY_VERSION_ATTRIBUTE, "0.0.0");
+ String packageName = pkgInfo.get(PackageNamespace.PACKAGE_NAMESPACE);
+ Optional packageProvidingUnit = ArtifactMatcher.findPackage(packageName, units);
+ if (packageProvidingUnit.isEmpty()) {
+ continue;
+ }
+ Set packageMethods = usages.signatures().stream()
+ .filter(ms -> packageName.equals(ms.packageName()))
+ .collect(Collectors.toCollection(LinkedHashSet::new));
+ if (packageMethods.isEmpty()) {
+ // it could be that actually no methods referenced (e.g. interface is only
+ // referencing a type)
+ // TODO we need to check that the types used are present in all versions as
+ // otherwise we will get CNF exception!
+ continue;
+ }
+ IInstallableUnit unit = packageProvidingUnit.get();
+ VersionRange versionRange = VersionRange.valueOf(packageVersion);
+ System.out.println("== " + packageName + " " + packageVersion + " is provided by " + unit
+ + " with version range " + versionRange + ", used method signatures from package are:");
+ for (MethodSignature signature : packageMethods) {
+ System.out.println("\t" + signature.id());
+ }
+ List list = versionProvider.stream()
+ .flatMap(avp -> avp.getPackageVersions(unit, packageName, versionRange, project)).toList();
+ System.out.println("Matching versions:");
+ // now we need to inspect all jars
+ for (ArtifactVersion v : list) {
+ System.out.println("\t" + v);
+ Path artifact = v.getArtifact();
+ if (artifact == null) {
+ // Retrieval of artifacts might be lazy and we can't get this one --> error?
+ continue;
+ }
+ ClassProvides provides = analyzeProvides(artifact.toFile());
+ for (MethodSignature mthd : packageMethods) {
+ if (!provides.signatures().contains(mthd)) {
+ dependencyProblems.add(new DependencyVersionProblem(
+ String.format(
+ "Import-Package '%s %s (compiled against %s %s) includes %s (provided by %s) but this version is missing the method %s",
+ packageName, packageVersion, unit.getId(), unit.getVersion(),
+ v.getVersion(), v.getProvider(), mthd.id()),
+ usages.classRef().get(mthd)));
+ }
+ }
+ }
+ // TODO we should emit a warning if the lower bound is not part of the
+ // discovered versions (or even fail?)
+
+ }
+ }
+ for (DependencyVersionProblem problem : dependencyProblems) {
+ getLog().error(String.format("%s, referenced by:%s%s", problem.message(), System.lineSeparator(),
+ problem.references().stream().collect(Collectors.joining(System.lineSeparator()))));
+ }
+ }
+
+ private Map getVersionInfo(GenericInfo genericInfo, String versionAttribute) {
+ Map directives = new HashMap<>(genericInfo.getDirectives());
+ String filter = directives.remove(Namespace.REQUIREMENT_FILTER_DIRECTIVE);
+ FilterImpl filterImpl;
+ try {
+ filterImpl = FilterImpl.newInstance(filter);
+ } catch (InvalidSyntaxException e) {
+ throw new IllegalArgumentException("Invalid filter directive", e); //$NON-NLS-1$
+ }
+ return filterImpl.getStandardOSGiAttributes(versionAttribute);
+ }
+
+ private ModuleRevisionBuilder readOSGiInfo(File file) throws MojoFailureException {
+ OsgiManifest manifest = bundleReader.loadManifest(file);
+ ModuleRevisionBuilder builder;
+ try {
+ builder = OSGiManifestBuilderFactory.createBuilder(manifest.getHeaders());
+ } catch (BundleException e) {
+ throw new MojoFailureException(e);
+ }
+ return builder;
+ }
+
+ private ClassUsage analyzeUsage(File file) throws MojoFailureException {
+ try {
+ Set usedMethodSignatures = new TreeSet<>();
+ Map> classRef = new HashMap<>();
+ try (JarFile jar = new JarFile(file)) {
+ Enumeration entries = jar.entries();
+ while (entries.hasMoreElements()) {
+ JarEntry jarEntry = entries.nextElement();
+ String name = jarEntry.getName();
+ if (name.endsWith(CLASS_SUFFIX)) {
+ String classname = name.substring(0, name.length() - CLASS_SUFFIX.length()).replace('/', '.');
+ InputStream stream = jar.getInputStream(jarEntry);
+ ClassReader reader = new ClassReader(stream.readAllBytes());
+ reader.accept(new ClassVisitor(Opcodes.ASM9) {
+ @Override
+ public MethodVisitor visitMethod(int access, String name, String descriptor,
+ String signature, String[] exceptions) {
+ return new MethodVisitor(Opcodes.ASM9) {
+ @Override
+ public void visitMethodInsn(int opcode, String owner, String name,
+ String descriptor, boolean isInterface) {
+ if (owner.startsWith("java/")) {
+ // ignore references to java core classes
+ return;
+ }
+ MethodSignature sig = new MethodSignature(owner.replace('/', '.'), name,
+ descriptor);
+ classRef.computeIfAbsent(sig, nil -> new TreeSet<>()).add(classname);
+ usedMethodSignatures.add(sig);
+ }
+ };
+ }
+ }, ClassReader.SKIP_FRAMES);
+ }
+ }
+ }
+ return new ClassUsage(usedMethodSignatures, classRef);
+ } catch (IOException e) {
+ throw new MojoFailureException(e);
+ }
+ }
+
+ private ClassProvides analyzeProvides(File file) throws MojoFailureException {
+ try {
+ Set providedMethodSignatures = new TreeSet<>();
+ try (JarFile jar = new JarFile(file)) {
+ Enumeration entries = jar.entries();
+ while (entries.hasMoreElements()) {
+ JarEntry jarEntry = entries.nextElement();
+ String name = jarEntry.getName();
+ if (name.endsWith(CLASS_SUFFIX)) {
+ String classname = name.substring(0, name.length() - CLASS_SUFFIX.length()).replace('/', '.');
+ InputStream stream = jar.getInputStream(jarEntry);
+ ClassReader reader = new ClassReader(stream.readAllBytes());
+ reader.accept(new ClassVisitor(Opcodes.ASM9) {
+ @Override
+ public MethodVisitor visitMethod(int access, String name, String descriptor,
+ String signature, String[] exceptions) {
+ providedMethodSignatures.add(new MethodSignature(classname, name, descriptor));
+ return null;
+ }
+ }, ClassReader.SKIP_FRAMES);
+ }
+ }
+ }
+ return new ClassProvides(providedMethodSignatures);
+ } catch (IOException e) {
+ // TODO
+ System.err.println(e);
+ return new ClassProvides(List.of());
+ // throw new MojoFailureException(e);
+ }
+ }
+
+ private static record ClassUsage(Collection signatures,
+ Map> classRef) {
+
+ }
+
+ private static record ClassProvides(Collection signatures) {
+
+ }
+
+ private static record MethodSignature(String className, String methodName, String signature)
+ implements Comparable {
+ public String packageName() {
+ String cn = className();
+ int idx = cn.lastIndexOf('.');
+ if (idx > 0) {
+ String substring = cn.substring(0, idx);
+ return substring;
+ }
+ return cn;
+ }
+
+ public String id() {
+ return className() + "#" + methodName() + signature();
+ }
+
+ @Override
+ public int compareTo(MethodSignature o) {
+ return id().compareTo(o.id());
+ }
+ }
+
+ private static record DependencyVersionProblem(String message, Collection references) {
+
+ }
+}
diff --git a/tycho-baseline-plugin/src/main/java/org/eclipse/tycho/baseline/EclipseIndexArtifactVersionProvider.java b/tycho-baseline-plugin/src/main/java/org/eclipse/tycho/baseline/EclipseIndexArtifactVersionProvider.java
new file mode 100644
index 0000000000..df38af6688
--- /dev/null
+++ b/tycho-baseline-plugin/src/main/java/org/eclipse/tycho/baseline/EclipseIndexArtifactVersionProvider.java
@@ -0,0 +1,142 @@
+/*******************************************************************************
+ * Copyright (c) 2024 Christoph Läubrich and others.
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Christoph Läubrich - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tycho.baseline;
+
+import java.io.File;
+import java.io.OutputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Stream;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+import org.apache.maven.project.MavenProject;
+import org.eclipse.equinox.p2.metadata.IInstallableUnit;
+import org.eclipse.equinox.p2.metadata.Version;
+import org.eclipse.equinox.p2.query.QueryUtil;
+import org.eclipse.equinox.p2.repository.metadata.IMetadataRepository;
+import org.eclipse.equinox.spi.p2.publisher.PublisherHelper;
+import org.eclipse.tycho.artifacts.ArtifactVersion;
+import org.eclipse.tycho.artifacts.ArtifactVersionProvider;
+import org.eclipse.tycho.copyfrom.oomph.P2Index;
+import org.eclipse.tycho.copyfrom.oomph.P2Index.Repository;
+import org.eclipse.tycho.copyfrom.oomph.P2IndexImpl;
+import org.eclipse.tycho.core.resolver.target.ArtifactMatcher;
+import org.eclipse.tycho.p2maven.repository.P2RepositoryManager;
+import org.eclipse.tycho.p2maven.transport.TransportCacheConfig;
+import org.osgi.framework.VersionRange;
+
+/**
+ * {@link ArtifactVersionProvider} using eclipse index
+ */
+@Named
+public class EclipseIndexArtifactVersionProvider implements ArtifactVersionProvider {
+
+ private P2Index p2Index;
+ private P2RepositoryManager repositoryManager;
+
+ @Inject
+ public EclipseIndexArtifactVersionProvider(TransportCacheConfig cacheConfig,
+ P2RepositoryManager repositoryManager) {
+ this.repositoryManager = repositoryManager;
+ p2Index = new P2IndexImpl(new File(cacheConfig.getCacheLocation(), "index"));
+ }
+
+ @Override
+ public Stream getPackageVersions(IInstallableUnit unit, String packageName,
+ VersionRange versionRange, MavenProject mavenProject) {
+ Map> map = p2Index.lookupCapabilities(PublisherHelper.CAPABILITY_NS_JAVA_PACKAGE,
+ packageName);
+ Set found = new HashSet<>();
+ String id = unit.getId();
+ return map.entrySet().stream()
+ .flatMap(entry -> entry.getValue().stream().filter(v -> v.isOSGiCompatible()).filter(v -> found.add(v))
+ .map(version -> new EclipseIndexArtifactVersion(entry.getKey(), id, packageName, version)))
+ .filter(eia -> versionRange.includes(eia.getVersion()))
+ .sorted(Comparator.comparing(EclipseIndexArtifactVersion::getVersion).reversed())
+ .map(ArtifactVersion.class::cast);
+ }
+
+ private class EclipseIndexArtifactVersion implements ArtifactVersion {
+
+ private Version version;
+ private org.apache.maven.model.Repository repository;
+ private String packageName;
+ private Path tempFile;
+ private String unitId;
+
+ public EclipseIndexArtifactVersion(Repository repository, String unitId, String packageName, Version version) {
+ this.repository = new org.apache.maven.model.Repository();
+ this.repository.setUrl(repository.getLocation().toString());
+ this.unitId = unitId;
+ this.packageName = packageName;
+ this.version = version;
+ }
+
+ @Override
+ public Path getArtifact() {
+ if (tempFile == null) {
+ try {
+ IInstallableUnit unit = getUnit();
+ if (unit != null) {
+ tempFile = Files.createTempFile(unit.getId(), ".jar");
+ tempFile.toFile().deleteOnExit();
+ try (OutputStream stream = Files.newOutputStream(tempFile)) {
+ repositoryManager.downloadArtifact(unit,
+ repositoryManager.getArtifactRepository(repository), stream);
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ return tempFile;
+ }
+
+ private IInstallableUnit getUnit() {
+ try {
+ IMetadataRepository metadataRepository = repositoryManager.getMetadataRepository(repository);
+ return ArtifactMatcher.findPackage(packageName,
+ metadataRepository.query(QueryUtil.createIUQuery(unitId), null), version).orElse(null);
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+ @Override
+ public org.osgi.framework.Version getVersion() {
+ return org.osgi.framework.Version.parseVersion(version.getOriginal());
+ }
+
+ @Override
+ public String toString() {
+ return getVersion() + " (from repository " + repository + ")";
+ }
+
+ @Override
+ public String getProvider() {
+ IInstallableUnit unit = getUnit();
+ if (unit != null) {
+ return unit.getId() + " " + unit.getVersion();
+ }
+ return null;
+ }
+
+ }
+
+}
diff --git a/tycho-baseline-plugin/src/main/java/org/eclipse/tycho/baseline/MavenArtifactVersionProvider.java b/tycho-baseline-plugin/src/main/java/org/eclipse/tycho/baseline/MavenArtifactVersionProvider.java
new file mode 100644
index 0000000000..c15ff976f4
--- /dev/null
+++ b/tycho-baseline-plugin/src/main/java/org/eclipse/tycho/baseline/MavenArtifactVersionProvider.java
@@ -0,0 +1,178 @@
+/*******************************************************************************
+ * Copyright (c) 2024 Christoph Läubrich and others.
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Christoph Läubrich - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tycho.baseline;
+
+import java.nio.file.Path;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Stream;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+import org.apache.maven.RepositoryUtils;
+import org.apache.maven.SessionScoped;
+import org.apache.maven.execution.MavenSession;
+import org.apache.maven.project.MavenProject;
+import org.eclipse.aether.RepositorySystem;
+import org.eclipse.aether.artifact.Artifact;
+import org.eclipse.aether.artifact.DefaultArtifact;
+import org.eclipse.aether.repository.RemoteRepository;
+import org.eclipse.aether.resolution.ArtifactRequest;
+import org.eclipse.aether.resolution.ArtifactResolutionException;
+import org.eclipse.aether.resolution.ArtifactResult;
+import org.eclipse.aether.resolution.VersionRangeRequest;
+import org.eclipse.aether.resolution.VersionRangeResolutionException;
+import org.eclipse.aether.resolution.VersionRangeResult;
+import org.eclipse.aether.version.Version;
+import org.eclipse.equinox.p2.metadata.IInstallableUnit;
+import org.eclipse.osgi.container.ModuleRevisionBuilder;
+import org.eclipse.osgi.container.ModuleRevisionBuilder.GenericInfo;
+import org.eclipse.osgi.container.builders.OSGiManifestBuilderFactory;
+import org.eclipse.tycho.TychoConstants;
+import org.eclipse.tycho.artifacts.ArtifactVersion;
+import org.eclipse.tycho.artifacts.ArtifactVersionProvider;
+import org.eclipse.tycho.core.osgitools.BundleReader;
+import org.eclipse.tycho.core.osgitools.OsgiManifest;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.VersionRange;
+import org.osgi.framework.namespace.PackageNamespace;
+
+/**
+ * A {@link ArtifactVersionProvider} that checks maven repository for possible
+ * candidates
+ */
+@Named
+@SessionScoped
+public class MavenArtifactVersionProvider implements ArtifactVersionProvider {
+
+ private MavenSession session;
+ private RepositorySystem repoSystem;
+ private BundleReader bundleReader;
+
+ @Inject
+ public MavenArtifactVersionProvider(MavenSession session, RepositorySystem repoSystem, BundleReader bundleReader) {
+ this.session = session;
+ this.repoSystem = repoSystem;
+ this.bundleReader = bundleReader;
+ }
+
+ @Override
+ public Stream getPackageVersions(IInstallableUnit unit, String packageName,
+ VersionRange versionRange, MavenProject mavenProject) {
+ String groupId = unit.getProperty(TychoConstants.PROP_GROUP_ID);
+ String artifactId = unit.getProperty(TychoConstants.PROP_ARTIFACT_ID);
+ String classifier = unit.getProperty(TychoConstants.PROP_CLASSIFIER);
+ if (groupId != null && artifactId != null && !"sources".equals(classifier)) {
+ System.out.println("Checking for " + groupId + "//" + artifactId);
+ List repositories = RepositoryUtils.toRepos(mavenProject.getRemoteArtifactRepositories());
+ DefaultArtifact artifact = new DefaultArtifact(groupId, artifactId, classifier, "jar", "[0,)");
+ // as we have no mean for a package version in maven we can only fetch all
+ // versions an check if the match
+ VersionRangeRequest rangeRequest = new VersionRangeRequest(artifact, repositories, "");
+ try {
+ VersionRangeResult range = repoSystem.resolveVersionRange(session.getRepositorySession(), rangeRequest);
+ // now we sort from highest > lowest version
+ List versions = range.getVersions().stream()
+ .sorted(Comparator.naturalOrder().reversed()).toList();
+ return versions.stream()
+ .map(v -> new MavenPackageArtifactVersion(artifact, v, packageName, repositories))
+ .filter(mav -> mav.getVersion() != null)
+ // and drop all until we find a matching version
+ .dropWhile(mav -> !versionRange.includes(mav.getVersion()))
+ // and stop when we find the first non matching version
+ .takeWhile(mav -> versionRange.includes(mav.getVersion()))
+ // cast to make compiler happy
+ .map(ArtifactVersion.class::cast);
+ } catch (VersionRangeResolutionException e) {
+ // can't provide any useful data then...
+ }
+ }
+ return Stream.empty();
+ }
+
+ private class MavenPackageArtifactVersion implements ArtifactVersion {
+
+ private Artifact artifact;
+ private List repositories;
+ private Path path;
+ private String packageName;
+ private org.osgi.framework.Version packageVersion;
+
+ public MavenPackageArtifactVersion(Artifact artifact, Version version, String packageName,
+ List repositories) {
+ this.artifact = new DefaultArtifact(artifact.getGroupId(), artifact.getArtifactId(),
+ artifact.getExtension(), version.toString());
+ this.packageName = packageName;
+ this.repositories = repositories;
+ }
+
+ @Override
+ public Path getArtifact() {
+ try {
+ ArtifactRequest request = new ArtifactRequest(artifact, repositories, "");
+ ArtifactResult result = repoSystem.resolveArtifact(session.getRepositorySession(), request);
+ path = result.getArtifact().getFile().toPath();
+ } catch (ArtifactResolutionException e) {
+ }
+ return path;
+ }
+
+ @Override
+ public org.osgi.framework.Version getVersion() {
+ if (packageVersion == null) {
+ ModuleRevisionBuilder builder = readOSGiInfo(getArtifact());
+ if (builder != null) {
+ List capabilities = builder.getCapabilities(PackageNamespace.PACKAGE_NAMESPACE);
+ for (GenericInfo info : capabilities) {
+ Map attributes = info.getAttributes();
+ if (packageName.equals(attributes.get(PackageNamespace.PACKAGE_NAMESPACE))) {
+ packageVersion = (org.osgi.framework.Version) attributes.getOrDefault(
+ PackageNamespace.CAPABILITY_VERSION_ATTRIBUTE,
+ org.osgi.framework.Version.emptyVersion);
+ }
+ }
+ }
+ }
+ return packageVersion;
+ }
+
+ @Override
+ public String toString() {
+ return getVersion() + " (maven artifact " + artifact + ")";
+ }
+
+ @Override
+ public String getProvider() {
+ ModuleRevisionBuilder info = readOSGiInfo(getArtifact());
+ if (info != null) {
+ return info.getSymbolicName() + " " + info.getVersion();
+ }
+ return null;
+ }
+
+ }
+
+ private ModuleRevisionBuilder readOSGiInfo(Path path) {
+ if (path != null) {
+ OsgiManifest manifest = bundleReader.loadManifest(path.toFile());
+ try {
+ return OSGiManifestBuilderFactory.createBuilder(manifest.getHeaders());
+ } catch (BundleException e) {
+ }
+ }
+ return null;
+ }
+
+}
diff --git a/tycho-baseline-plugin/src/main/java/org/eclipse/tycho/copyfrom/oomph/P2Index.java b/tycho-baseline-plugin/src/main/java/org/eclipse/tycho/copyfrom/oomph/P2Index.java
index 03cc9203fa..7e1db8dbbb 100644
--- a/tycho-baseline-plugin/src/main/java/org/eclipse/tycho/copyfrom/oomph/P2Index.java
+++ b/tycho-baseline-plugin/src/main/java/org/eclipse/tycho/copyfrom/oomph/P2Index.java
@@ -8,15 +8,14 @@
* Contributors:
* Eike Stepper - initial API and implementation
*/
-package org.eclipse.oomph.p2.internal.core;
-
-import org.eclipse.emf.common.util.URI;
-
-import org.eclipse.equinox.p2.metadata.Version;
+package org.eclipse.tycho.copyfrom.oomph;
import java.util.Map;
import java.util.Set;
+import org.eclipse.emf.common.util.URI;
+import org.eclipse.equinox.p2.metadata.Version;
+
/**
* @author Eike Stepper
*/
@@ -26,8 +25,6 @@ public interface P2Index
public static final int COMPOSED_REPOSITORY = 1;
- public static final P2Index INSTANCE = P2IndexImpl.INSTANCE;
-
public Repository[] getRepositories();
public Map> getCapabilities();
diff --git a/tycho-baseline-plugin/src/main/java/org/eclipse/tycho/copyfrom/oomph/P2IndexImpl.java b/tycho-baseline-plugin/src/main/java/org/eclipse/tycho/copyfrom/oomph/P2IndexImpl.java
index b7df4d3433..a728ea9032 100644
--- a/tycho-baseline-plugin/src/main/java/org/eclipse/tycho/copyfrom/oomph/P2IndexImpl.java
+++ b/tycho-baseline-plugin/src/main/java/org/eclipse/tycho/copyfrom/oomph/P2IndexImpl.java
@@ -8,22 +8,10 @@
* Contributors:
* Eike Stepper - initial API and implementation
*/
-package org.eclipse.oomph.p2.internal.core;
-
-import org.eclipse.oomph.util.CollectionUtil;
-import org.eclipse.oomph.util.IOUtil;
-import org.eclipse.oomph.util.StringUtil;
-
-import org.eclipse.emf.common.util.URI;
-import org.eclipse.emf.ecore.resource.impl.BinaryResourceImpl;
-import org.eclipse.emf.ecore.resource.impl.BinaryResourceImpl.EObjectInputStream;
-
-import org.eclipse.core.runtime.IPath;
-import org.eclipse.core.runtime.IStatus;
-import org.eclipse.core.runtime.Path;
-import org.eclipse.equinox.p2.metadata.Version;
+package org.eclipse.tycho.copyfrom.oomph;
import java.io.BufferedReader;
+import java.io.Closeable;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
@@ -38,555 +26,528 @@
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
+import org.eclipse.emf.common.util.URI;
+import org.eclipse.emf.ecore.resource.impl.BinaryResourceImpl;
+import org.eclipse.emf.ecore.resource.impl.BinaryResourceImpl.EObjectInputStream;
+import org.eclipse.equinox.p2.metadata.Version;
+
/**
* @author Eike Stepper
*/
-public class P2IndexImpl implements P2Index
-{
- public static final P2IndexImpl INSTANCE = new P2IndexImpl();
-
- private static final String INDEX_BASE = "https://download.eclipse.org/oomph/index/"; //$NON-NLS-1$
-
- private long timeStamp;
-
- private Map repositories;
-
- private Repository[] repositoriesArray;
-
- private Map> capabilitiesMap;
-
- private File repositoriesCacheFile;
-
- private File capabilitiesCacheFile;
-
- private int capabilitiesRefreshHours = -1;
-
- private int repositoriesRefreshHours = -1;
-
- private P2IndexImpl()
- {
- }
-
- private synchronized void initCapabilities()
- {
- if (capabilitiesMap == null || capabilitiesCacheFile.lastModified() + capabilitiesRefreshHours * 60 * 60 * 1000 < System.currentTimeMillis())
- {
- capabilitiesMap = new LinkedHashMap<>();
-
- ZipFile zipFile = null;
- InputStream inputStream = null;
-
- try
- {
- initCapabilitiesCacheFile();
-
- zipFile = new ZipFile(capabilitiesCacheFile);
- ZipEntry zipEntry = zipFile.getEntry("capabilities"); //$NON-NLS-1$
-
- inputStream = zipFile.getInputStream(zipEntry);
-
- Map