summaryrefslogtreecommitdiffstats
path: root/bundle-plugin/src
diff options
context:
space:
mode:
Diffstat (limited to 'bundle-plugin/src')
-rw-r--r--bundle-plugin/src/main/java/com/yahoo/container/plugin/bundle/AnalyzeBundle.java40
-rw-r--r--bundle-plugin/src/main/java/com/yahoo/container/plugin/classanalysis/AnalyzeClassVisitor.java8
-rw-r--r--bundle-plugin/src/main/java/com/yahoo/container/plugin/classanalysis/ClassFileMetaData.java16
-rw-r--r--bundle-plugin/src/main/java/com/yahoo/container/plugin/classanalysis/ExportPackageAnnotation.java2
-rw-r--r--bundle-plugin/src/main/java/com/yahoo/container/plugin/classanalysis/PackageInfo.java16
-rw-r--r--bundle-plugin/src/main/java/com/yahoo/container/plugin/classanalysis/PackageTally.java51
-rw-r--r--bundle-plugin/src/main/java/com/yahoo/container/plugin/classanalysis/Packages.java21
-rw-r--r--bundle-plugin/src/main/java/com/yahoo/container/plugin/mojo/AssembleContainerPluginMojo.java2
-rw-r--r--bundle-plugin/src/main/java/com/yahoo/container/plugin/mojo/GenerateOsgiManifestMojo.java104
-rw-r--r--bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/AnalyzeClassTest.java18
-rw-r--r--bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/sampleclasses/package-info.java2
11 files changed, 221 insertions, 59 deletions
diff --git a/bundle-plugin/src/main/java/com/yahoo/container/plugin/bundle/AnalyzeBundle.java b/bundle-plugin/src/main/java/com/yahoo/container/plugin/bundle/AnalyzeBundle.java
index 2b5941cc5aa..af6c82023ab 100644
--- a/bundle-plugin/src/main/java/com/yahoo/container/plugin/bundle/AnalyzeBundle.java
+++ b/bundle-plugin/src/main/java/com/yahoo/container/plugin/bundle/AnalyzeBundle.java
@@ -7,12 +7,14 @@ import com.yahoo.container.plugin.util.JarFiles;
import java.io.File;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.jar.Manifest;
+import java.util.stream.Collectors;
/**
* Static utilities for analyzing jar files.
@@ -34,20 +36,42 @@ public class AnalyzeBundle {
}
static List<Export> exportedPackages(File jarFile) {
+ var manifest = getOsgiManifest(jarFile);
+ if (manifest == null) return Collections.emptyList();
try {
- Optional<Manifest> jarManifest = JarFiles.getManifest(jarFile);
- if (jarManifest.isPresent()) {
- Manifest manifest = jarManifest.get();
- if (isOsgiManifest(manifest)) {
- return parseExports(manifest);
- }
- }
- return Collections.emptyList();
+ return parseExports(manifest);
} catch (Exception e) {
throw new RuntimeException(String.format("Invalid manifest in bundle '%s'", jarFile.getPath()), e);
}
}
+ public static List<String> publicApiPackagesAggregated(Collection<File> jarFiles) {
+ return jarFiles.stream()
+ .map(AnalyzeBundle::publicApiPackages)
+ .flatMap(List::stream)
+ .distinct()
+ .toList();
+ }
+
+ static List<String> publicApiPackages(File jarFile) {
+ var manifest = getOsgiManifest(jarFile);
+ if (manifest == null) return Collections.emptyList();
+ return getMainAttributeValue(manifest, "X-JDisc-PublicApi-Package")
+ .map(s -> Arrays.asList(s.split(",")))
+ .orElseGet(ArrayList::new);
+ }
+
+ private static Manifest getOsgiManifest(File jarFile) {
+ Optional<Manifest> jarManifest = JarFiles.getManifest(jarFile);
+ if (jarManifest.isPresent()) {
+ Manifest manifest = jarManifest.get();
+ if (isOsgiManifest(manifest)) {
+ return manifest;
+ }
+ }
+ return null;
+ }
+
public static Optional<String> bundleSymbolicName(File jarFile) {
return JarFiles.getManifest(jarFile).flatMap(AnalyzeBundle::getBundleSymbolicName);
}
diff --git a/bundle-plugin/src/main/java/com/yahoo/container/plugin/classanalysis/AnalyzeClassVisitor.java b/bundle-plugin/src/main/java/com/yahoo/container/plugin/classanalysis/AnalyzeClassVisitor.java
index 46a35b07ea7..e57af606b3a 100644
--- a/bundle-plugin/src/main/java/com/yahoo/container/plugin/classanalysis/AnalyzeClassVisitor.java
+++ b/bundle-plugin/src/main/java/com/yahoo/container/plugin/classanalysis/AnalyzeClassVisitor.java
@@ -1,6 +1,7 @@
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.container.plugin.classanalysis;
+import com.yahoo.api.annotations.PublicApi;
import com.yahoo.osgi.annotation.ExportPackage;
import com.yahoo.osgi.annotation.Version;
import org.apache.maven.artifact.versioning.ArtifactVersion;
@@ -28,6 +29,7 @@ class AnalyzeClassVisitor extends ClassVisitor implements ImportCollector {
private String name = null;
private final Set<String> imports = new HashSet<>();
private Optional<ExportPackageAnnotation> exportPackageAnnotation = Optional.empty();
+ private boolean isPublicApi = false;
private final Optional<ArtifactVersion> defaultExportPackageVersion;
@@ -159,6 +161,9 @@ class AnalyzeClassVisitor extends ClassVisitor implements ImportCollector {
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
if (ExportPackage.class.getName().equals(Type.getType(desc).getClassName())) {
return visitExportPackage();
+ } if (PublicApi.class.getName().equals(Type.getType(desc).getClassName())) {
+ isPublicApi = true;
+ return null;
} else {
if (visible) {
addImportWithTypeDesc(desc);
@@ -169,7 +174,8 @@ class AnalyzeClassVisitor extends ClassVisitor implements ImportCollector {
ClassFileMetaData result() {
assert (!imports.contains("int"));
- return new ClassFileMetaData(name, imports, exportPackageAnnotation);
+ var packageInfo = new PackageInfo(Packages.packageName(name), exportPackageAnnotation, isPublicApi);
+ return new ClassFileMetaData(name, imports, packageInfo);
}
}
diff --git a/bundle-plugin/src/main/java/com/yahoo/container/plugin/classanalysis/ClassFileMetaData.java b/bundle-plugin/src/main/java/com/yahoo/container/plugin/classanalysis/ClassFileMetaData.java
index 5601430a27f..7e2f59c1e4d 100644
--- a/bundle-plugin/src/main/java/com/yahoo/container/plugin/classanalysis/ClassFileMetaData.java
+++ b/bundle-plugin/src/main/java/com/yahoo/container/plugin/classanalysis/ClassFileMetaData.java
@@ -14,12 +14,12 @@ public class ClassFileMetaData {
private final String name;
private final Set<String> referencedClasses;
- private final Optional<ExportPackageAnnotation> exportPackage;
+ private final PackageInfo packageInfo;
- public ClassFileMetaData(String name, Set<String> referencedClasses, Optional<ExportPackageAnnotation> exportPackage) {
+ public ClassFileMetaData(String name, Set<String> referencedClasses, PackageInfo packageInfo) {
this.name = name;
this.referencedClasses = referencedClasses;
- this.exportPackage = exportPackage;
+ this.packageInfo = packageInfo;
}
public String getName() {
@@ -30,8 +30,16 @@ public class ClassFileMetaData {
return referencedClasses;
}
+ public PackageInfo packageInfo() {
+ return packageInfo;
+ }
+
public Optional<ExportPackageAnnotation> getExportPackage() {
- return exportPackage;
+ return packageInfo.exportPackage();
+ }
+
+ public boolean isPublicApi() {
+ return packageInfo.isPublicApi();
}
}
diff --git a/bundle-plugin/src/main/java/com/yahoo/container/plugin/classanalysis/ExportPackageAnnotation.java b/bundle-plugin/src/main/java/com/yahoo/container/plugin/classanalysis/ExportPackageAnnotation.java
index 7f3fb9522f7..517a59a5a06 100644
--- a/bundle-plugin/src/main/java/com/yahoo/container/plugin/classanalysis/ExportPackageAnnotation.java
+++ b/bundle-plugin/src/main/java/com/yahoo/container/plugin/classanalysis/ExportPackageAnnotation.java
@@ -26,7 +26,7 @@ public class ExportPackageAnnotation {
requireNonNegative(major, "major");
requireNonNegative(minor, "minor");
requireNonNegative(micro, "micro");
- if (QUALIFIER_PATTERN.matcher(qualifier).matches() == false) {
+ if (! QUALIFIER_PATTERN.matcher(qualifier).matches()) {
throw new IllegalArgumentException(
exportPackageError(String.format("qualifier must follow the format (alpha|digit|'_'|'-')* but was '%s'.", qualifier)));
}
diff --git a/bundle-plugin/src/main/java/com/yahoo/container/plugin/classanalysis/PackageInfo.java b/bundle-plugin/src/main/java/com/yahoo/container/plugin/classanalysis/PackageInfo.java
new file mode 100644
index 00000000000..c19320b8e98
--- /dev/null
+++ b/bundle-plugin/src/main/java/com/yahoo/container/plugin/classanalysis/PackageInfo.java
@@ -0,0 +1,16 @@
+package com.yahoo.container.plugin.classanalysis;
+
+import java.util.Optional;
+
+/**
+ * The package
+ *
+ * @author gjoranv
+ */
+record PackageInfo(String name, Optional<ExportPackageAnnotation> exportPackage, boolean isPublicApi) {
+
+ PackageInfo hasExportPackageOrElse(PackageInfo other) {
+ return exportPackage().isPresent() ? this : other;
+ }
+
+}
diff --git a/bundle-plugin/src/main/java/com/yahoo/container/plugin/classanalysis/PackageTally.java b/bundle-plugin/src/main/java/com/yahoo/container/plugin/classanalysis/PackageTally.java
index e2de90a6463..51fba228b41 100644
--- a/bundle-plugin/src/main/java/com/yahoo/container/plugin/classanalysis/PackageTally.java
+++ b/bundle-plugin/src/main/java/com/yahoo/container/plugin/classanalysis/PackageTally.java
@@ -17,16 +17,17 @@ import java.util.stream.Collectors;
* @author ollivir
*/
public class PackageTally {
- private final Map<String, Optional<ExportPackageAnnotation>> definedPackagesMap;
+
+ private final Map<String, PackageInfo> definedPackages;
private final Set<String> referencedPackagesUnfiltered;
- PackageTally(Map<String, Optional<ExportPackageAnnotation>> definedPackagesMap, Set<String> referencedPackagesUnfiltered) {
- this.definedPackagesMap = definedPackagesMap;
+ PackageTally(Map<String, PackageInfo> definedPackages, Set<String> referencedPackagesUnfiltered) {
+ this.definedPackages = definedPackages;
this.referencedPackagesUnfiltered = referencedPackagesUnfiltered;
}
public Set<String> definedPackages() {
- return definedPackagesMap.keySet();
+ return definedPackages.keySet();
}
public Set<String> referencedPackages() {
@@ -35,12 +36,19 @@ public class PackageTally {
public Map<String, ExportPackageAnnotation> exportedPackages() {
Map<String, ExportPackageAnnotation> ret = new HashMap<>();
- definedPackagesMap.forEach((k, v) -> {
- v.ifPresent(annotation -> ret.put(k, annotation));
+ definedPackages.forEach((pkg, pkgInfo) -> {
+ pkgInfo.exportPackage().ifPresent(a -> ret.put(pkg, a));
});
return ret;
}
+ public Set<String> publicApiPackages() {
+ return definedPackages.values().stream()
+ .filter(PackageInfo::isPublicApi)
+ .map(PackageInfo::name)
+ .collect(Collectors.toSet());
+ }
+
/**
* Returns the set of packages that is referenced from this tally, but not included in the given set of available packages.
*
@@ -58,36 +66,37 @@ public class PackageTally {
* Represents the classes for two package tallies that are deployed as a single unit.
* <p>
* ExportPackageAnnotations from this has precedence over the other.
+ * TODO: Add unit test and try using Map.merge (as in the functions below). Can't see how Maps.combine is any different.
*/
public PackageTally combine(PackageTally other) {
- Map<String, Optional<ExportPackageAnnotation>> map = Maps.combine(this.definedPackagesMap, other.definedPackagesMap,
- (l, r) -> l.isPresent() ? l : r);
+ var definedPkgs = Maps.combine(this.definedPackages, other.definedPackages, PackageInfo::hasExportPackageOrElse);
Set<String> referencedPkgs = new HashSet<>(this.referencedPackagesUnfiltered);
referencedPkgs.addAll(other.referencedPackagesUnfiltered);
- return new PackageTally(map, referencedPkgs);
+ return new PackageTally(definedPkgs, referencedPkgs);
}
public static PackageTally combine(Collection<PackageTally> packageTallies) {
- Map<String, Optional<ExportPackageAnnotation>> map = new HashMap<>();
+ var definedPkgs = new HashMap<String, PackageInfo>();
Set<String> referencedPkgs = new HashSet<>();
- for (PackageTally pt : packageTallies) {
- pt.definedPackagesMap.forEach((k, v) -> map.merge(k, v, (l, r) -> l.isPresent() ? l : r));
- referencedPkgs.addAll(pt.referencedPackagesUnfiltered);
+ for (PackageTally tally : packageTallies) {
+ tally.definedPackages.forEach((pkg, info) -> definedPkgs.merge(pkg, info, PackageInfo::hasExportPackageOrElse));
+ referencedPkgs.addAll(tally.referencedPackagesUnfiltered);
}
- return new PackageTally(map, referencedPkgs);
+ return new PackageTally(definedPkgs, referencedPkgs);
}
public static PackageTally fromAnalyzedClassFiles(Collection<ClassFileMetaData> analyzedClassFiles) {
- Map<String, Optional<ExportPackageAnnotation>> map = new HashMap<>();
- Set<String> referencedPkgs = new HashSet<>();
+ var definedPkgs = new HashMap<String, PackageInfo>();
+ var referencedPkgs = new HashSet<String>();
- for (ClassFileMetaData metaData : analyzedClassFiles) {
- String packageName = Packages.packageName(metaData.getName());
- map.merge(packageName, metaData.getExportPackage(), (l, r) -> l.isPresent() ? l : r);
- metaData.getReferencedClasses().forEach(className -> referencedPkgs.add(Packages.packageName(className)));
+ for (ClassFileMetaData classData : analyzedClassFiles) {
+ var pkgName = classData.packageInfo().name();
+ definedPkgs.merge(pkgName, classData.packageInfo(), PackageInfo::hasExportPackageOrElse);
+ classData.getReferencedClasses().forEach(className -> referencedPkgs.add(Packages.packageName(className)));
}
- return new PackageTally(map, referencedPkgs);
+ return new PackageTally(definedPkgs, referencedPkgs);
}
+
}
diff --git a/bundle-plugin/src/main/java/com/yahoo/container/plugin/classanalysis/Packages.java b/bundle-plugin/src/main/java/com/yahoo/container/plugin/classanalysis/Packages.java
index 9eef8a55c01..48a128c2f0d 100644
--- a/bundle-plugin/src/main/java/com/yahoo/container/plugin/classanalysis/Packages.java
+++ b/bundle-plugin/src/main/java/com/yahoo/container/plugin/classanalysis/Packages.java
@@ -1,8 +1,13 @@
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.container.plugin.classanalysis;
+import com.yahoo.container.plugin.osgi.ImportPackages;
+
import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
import java.util.Set;
+import java.util.stream.Collectors;
/**
* Utility methods related to packages.
@@ -31,6 +36,22 @@ public class Packages {
}
}
+ /**
+ * Returns the imported Vespa packages that don't exist in the given set of allowed packages.
+ */
+ public static List<String> disallowedVespaImports(Map<String, ImportPackages.Import> imports, List<String> allowed) {
+ if (imports == null || imports.isEmpty()) return List.of();
+
+ var publicApi = allowed == null ? Set.of() : new HashSet<>(allowed);
+
+ Set<String> yahooImports = imports.keySet().stream()
+ .filter(pkg -> pkg.startsWith("com.yahoo") || pkg.startsWith("ai.vespa."))
+ .collect(Collectors.toSet());
+
+ List<String> disallowedImports = yahooImports.stream().collect(Collectors.groupingBy(publicApi::contains)).get(false);
+ return disallowedImports == null ? List.of() : disallowedImports;
+ }
+
public static PackageMetaData analyzePackages(Set<ClassFileMetaData> allClasses) {
Set<String> definedPackages = new HashSet<>();
Set<String> referencedPackages = new HashSet<>();
diff --git a/bundle-plugin/src/main/java/com/yahoo/container/plugin/mojo/AssembleContainerPluginMojo.java b/bundle-plugin/src/main/java/com/yahoo/container/plugin/mojo/AssembleContainerPluginMojo.java
index a1d3cd13b3a..bb2d61932f3 100644
--- a/bundle-plugin/src/main/java/com/yahoo/container/plugin/mojo/AssembleContainerPluginMojo.java
+++ b/bundle-plugin/src/main/java/com/yahoo/container/plugin/mojo/AssembleContainerPluginMojo.java
@@ -25,7 +25,7 @@ import java.util.jar.JarFile;
*/
@Mojo(name = "assemble-container-plugin", requiresDependencyResolution = ResolutionScope.COMPILE, threadSafe = true)
public class AssembleContainerPluginMojo extends AbstractAssembleBundleMojo {
- private static enum Dependencies {
+ private enum Dependencies {
WITH, WITHOUT
}
diff --git a/bundle-plugin/src/main/java/com/yahoo/container/plugin/mojo/GenerateOsgiManifestMojo.java b/bundle-plugin/src/main/java/com/yahoo/container/plugin/mojo/GenerateOsgiManifestMojo.java
index f5d3259c537..d217273e42b 100644
--- a/bundle-plugin/src/main/java/com/yahoo/container/plugin/mojo/GenerateOsgiManifestMojo.java
+++ b/bundle-plugin/src/main/java/com/yahoo/container/plugin/mojo/GenerateOsgiManifestMojo.java
@@ -26,6 +26,8 @@ import java.util.stream.Collectors;
import java.util.stream.Stream;
import static com.yahoo.container.plugin.bundle.AnalyzeBundle.exportedPackagesAggregated;
+import static com.yahoo.container.plugin.bundle.AnalyzeBundle.publicApiPackagesAggregated;
+import static com.yahoo.container.plugin.classanalysis.Packages.disallowedVespaImports;
import static com.yahoo.container.plugin.osgi.ExportPackages.exportsByPackageName;
import static com.yahoo.container.plugin.osgi.ImportPackages.calculateImports;
import static com.yahoo.container.plugin.util.Files.allDescendantFiles;
@@ -38,6 +40,14 @@ import static com.yahoo.container.plugin.util.Files.allDescendantFiles;
@Mojo(name = "generate-osgi-manifest", requiresDependencyResolution = ResolutionScope.TEST, threadSafe = true)
public class GenerateOsgiManifestMojo extends AbstractGenerateOsgiManifestMojo {
+ private enum BundleType {
+ CORE, // up to container-dev
+ INTERNAL, // other vespa bundles (need not be set for groupId 'com.yahoo.vespa')
+ USER
+ }
+
+ private static final String VESPA_GROUP_ID = "com.yahoo.vespa";
+
@Parameter
private String discApplicationClass = null;
@@ -56,6 +66,19 @@ public class GenerateOsgiManifestMojo extends AbstractGenerateOsgiManifestMojo {
@Parameter(alias = "Main-Class")
private String mainClass = null;
+ @Parameter(alias = "Bundle-Type")
+ private BundleType bundleType = BundleType.USER;
+
+ @Parameter(defaultValue = "false")
+ private boolean suppressWarningMissingImportPackages;
+ @Parameter(defaultValue = "false")
+ private boolean suppressWarningPublicApi;
+ @Parameter(defaultValue = "false")
+ private boolean suppressWarningOverlappingPackages;
+
+ @Parameter(defaultValue = "false")
+ private boolean failOnWarnings;
+
@Parameter(defaultValue = "false")
private boolean buildLegacyVespaPlatformBundle;
@@ -69,10 +92,12 @@ public class GenerateOsgiManifestMojo extends AbstractGenerateOsgiManifestMojo {
if (! isContainerDiscArtifact(project.getArtifact()))
throwIfInternalContainerArtifactsAreIncluded(artifactSet.getJarArtifactsToInclude());
- List<Export> exportedPackagesFromProvidedJars = exportedPackagesAggregated(
- artifactSet.getJarArtifactsProvided().stream().map(Artifact::getFile).toList());
+ List<Artifact> providedJarArtifacts = artifactSet.getJarArtifactsProvided();
+ List<File> providedJarFiles = providedJarArtifacts.stream().map(Artifact::getFile).toList();
+ List<Export> exportedPackagesFromProvidedJars = exportedPackagesAggregated(providedJarFiles);
+ List<String> publicApiPackagesFromProvidedJars = publicApiPackagesAggregated(providedJarFiles);
- // Packages from Export-Package headers in provided scoped jars
+ // Packages from Export-Package/PublicApi headers in provided scoped jars
Set<String> exportedPackagesFromProvidedDeps = ExportPackages.packageNames(exportedPackagesFromProvidedJars);
// Packaged defined in this project's code
@@ -86,12 +111,12 @@ public class GenerateOsgiManifestMojo extends AbstractGenerateOsgiManifestMojo {
logDebugPackageSets(exportedPackagesFromProvidedJars, includedPackages);
- if (hasJdiscCoreProvided(artifactSet.getJarArtifactsProvided())) {
+ if (hasJdiscCoreProvided(providedJarArtifacts)) {
// jdisc_core being provided guarantees that log output does not contain its exported packages
logMissingPackages(exportedPackagesFromProvidedDeps, projectPackages, compileJarsPackages, includedPackages);
- } else {
- getLog().warn("This project does not have jdisc_core as provided dependency, so the " +
- "generated 'Import-Package' OSGi header may be missing important packages.");
+ } else if (! suppressWarningMissingImportPackages) {
+ warnOrThrow(("This project does not have '%s' as provided dependency, so the generated 'Import-Package' " +
+ "OSGi header may be missing important packages.").formatted(wantedProvidedDependency()));
}
logOverlappingPackages(projectPackages, exportedPackagesFromProvidedDeps);
logUnnecessaryPackages(compileJarsPackages, exportedPackagesFromProvidedDeps);
@@ -100,9 +125,12 @@ public class GenerateOsgiManifestMojo extends AbstractGenerateOsgiManifestMojo {
includedPackages.definedPackages(),
exportsByPackageName(exportedPackagesFromProvidedJars));
+ List<String> nonPublicApiUsed = disallowedVespaImports(calculatedImports, publicApiPackagesFromProvidedJars);
+ logNonPublicApiUsage(nonPublicApiUsed);
Map<String, String> manifestContent = generateManifestContent(artifactSet.getJarArtifactsToInclude(), calculatedImports, includedPackages);
- addAdditionalManifestProperties(manifestContent);
+ addAdditionalManifestProperties(manifestContent, includedPackages);
+ addManifestPropertiesForUserBundles(manifestContent, nonPublicApiUsed);
createManifestFile(Paths.get(project.getBuild().getOutputDirectory()), manifestContent);
} catch (Exception e) {
@@ -110,7 +138,21 @@ public class GenerateOsgiManifestMojo extends AbstractGenerateOsgiManifestMojo {
}
}
- private void addAdditionalManifestProperties(Map<String, String> manifestContent) {
+ private String wantedProvidedDependency() {
+ return switch (effectiveBundleType()) {
+ case CORE -> "jdisc_core";
+ case INTERNAL -> "container-dev";
+ case USER -> "container";
+ };
+ }
+
+ private BundleType effectiveBundleType() {
+ if (bundleType != BundleType.USER) return bundleType;
+ return isVespaInternalGroupId(project.getGroupId()) ? BundleType.INTERNAL : BundleType.USER;
+ }
+
+ private void addAdditionalManifestProperties(Map<String, String> manifestContent, PackageTally includedPackages) {
+ addIfNotEmpty(manifestContent, "X-JDisc-PublicApi-Package", publicApi(includedPackages));
addIfNotEmpty(manifestContent, "Bundle-Activator", bundleActivator);
addIfNotEmpty(manifestContent, "X-JDisc-Privileged-Activator", jdiscPrivilegedActivator);
addIfNotEmpty(manifestContent, "Main-Class", mainClass);
@@ -119,6 +161,20 @@ public class GenerateOsgiManifestMojo extends AbstractGenerateOsgiManifestMojo {
addIfNotEmpty(manifestContent, "WebInfUrl", webInfUrl);
}
+ private void addManifestPropertiesForUserBundles(Map<String, String> manifestContent, List<String> nonPublicApiUsed) {
+ if (effectiveBundleType() != BundleType.USER) return;
+ addIfNotEmpty(manifestContent, "X-JDisc-Non-PublicApi-Import-Package", String.join(",", nonPublicApiUsed));
+ }
+
+ private void logNonPublicApiUsage(List<String> nonPublicApiUsed) {
+ if (suppressWarningPublicApi || effectiveBundleType() != BundleType.USER || nonPublicApiUsed.isEmpty()) return;
+ warnOrThrow("This project uses packages that are not part of Vespa's public api: %s".formatted(nonPublicApiUsed));
+ }
+
+ private static String publicApi(PackageTally tally) {
+ return tally.publicApiPackages().stream().sorted().collect(Collectors.joining(","));
+ }
+
private void logDebugPackageSets(List<Export> exportedPackagesFromProvidedJars, PackageTally includedPackages) {
if (getLog().isDebugEnabled()) {
getLog().debug("Referenced packages = " + includedPackages.referencedPackages());
@@ -154,10 +210,12 @@ public class GenerateOsgiManifestMojo extends AbstractGenerateOsgiManifestMojo {
private void logOverlappingPackages(PackageTally projectPackages,
Set<String> exportedPackagesFromProvidedDeps) {
+ if (suppressWarningOverlappingPackages) return;
+
Set<String> overlappingProjectPackages = Sets.intersection(projectPackages.definedPackages(), exportedPackagesFromProvidedDeps);
if (! overlappingProjectPackages.isEmpty()) {
- getLog().warn("This project defines packages that are also defined in provided scoped dependencies " +
- "(overlapping packages are strongly discouraged): " + overlappingProjectPackages);
+ warnOrThrow("This project defines packages that are also defined in provided scoped dependencies " +
+ "(overlapping packages are strongly discouraged): " + overlappingProjectPackages);
}
}
@@ -184,9 +242,8 @@ public class GenerateOsgiManifestMojo extends AbstractGenerateOsgiManifestMojo {
List<Artifact> unsupportedArtifacts = nonJarArtifacts.stream().filter(a -> ! a.getType().equals("pom"))
.toList();
- unsupportedArtifacts.forEach(artifact -> getLog()
- .warn(String.format("Unsupported artifact '%s': Type '%s' is not supported. Please file a feature request.",
- artifact.getId(), artifact.getType())));
+ unsupportedArtifacts.forEach(artifact -> warnOrThrow(String.format("Unsupported artifact '%s': Type '%s' is not supported. Please file a feature request.",
+ artifact.getId(), artifact.getType())));
}
private void throwIfInternalContainerArtifactsAreIncluded(Collection<Artifact> includedArtifacts) throws MojoExecutionException {
@@ -201,12 +258,18 @@ public class GenerateOsgiManifestMojo extends AbstractGenerateOsgiManifestMojo {
}
}
+ private boolean isVespaInternalGroupId(String groupId) {
+ return groupId.equals(VESPA_GROUP_ID)
+ || groupId.equals(VESPA_GROUP_ID + ".hosted")
+ || groupId.equals(VESPA_GROUP_ID + ".hosted.controller");
+ }
+
private boolean isJdiscComponentArtifact(Artifact a) {
- return a.getArtifactId().equals("component") && a.getGroupId().equals("com.yahoo.vespa");
+ return a.getArtifactId().equals("component") && a.getGroupId().equals(VESPA_GROUP_ID);
}
private boolean isContainerDiscArtifact(Artifact a) {
- return a.getArtifactId().equals("container-disc") && a.getGroupId().equals("com.yahoo.vespa");
+ return a.getArtifactId().equals("container-disc") && a.getGroupId().equals(VESPA_GROUP_ID);
}
private PackageTally getProjectClassesTally() {
@@ -219,4 +282,13 @@ public class GenerateOsgiManifestMojo extends AbstractGenerateOsgiManifestMojo {
return PackageTally.fromAnalyzedClassFiles(analyzedClasses);
}
+
+ private void warnOrThrow(String... messages){
+ String message = String.join("\n", messages);
+ if (failOnWarnings) {
+ throw new RuntimeException(message);
+ }
+ getLog().warn(message);
+ }
+
}
diff --git a/bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/AnalyzeClassTest.java b/bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/AnalyzeClassTest.java
index 9a7aade7ffb..11fe4a14d74 100644
--- a/bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/AnalyzeClassTest.java
+++ b/bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/AnalyzeClassTest.java
@@ -3,28 +3,27 @@ package com.yahoo.container.plugin.classanalysis;
import com.yahoo.container.plugin.classanalysis.sampleclasses.Base;
import com.yahoo.container.plugin.classanalysis.sampleclasses.ClassAnnotation;
-import com.yahoo.container.plugin.classanalysis.sampleclasses.InvisibleAnnotation;
import com.yahoo.container.plugin.classanalysis.sampleclasses.Derived;
import com.yahoo.container.plugin.classanalysis.sampleclasses.DummyAnnotation;
-import com.yahoo.container.plugin.classanalysis.sampleclasses.InvisibleDummyAnnotation;
import com.yahoo.container.plugin.classanalysis.sampleclasses.Fields;
import com.yahoo.container.plugin.classanalysis.sampleclasses.Interface1;
import com.yahoo.container.plugin.classanalysis.sampleclasses.Interface2;
-import com.yahoo.container.plugin.classanalysis.sampleclasses.RecordWithOverride;
-import com.yahoo.container.plugin.classanalysis.sampleclasses.SwitchStatement;
+import com.yahoo.container.plugin.classanalysis.sampleclasses.InvisibleAnnotation;
+import com.yahoo.container.plugin.classanalysis.sampleclasses.InvisibleDummyAnnotation;
import com.yahoo.container.plugin.classanalysis.sampleclasses.MethodAnnotation;
import com.yahoo.container.plugin.classanalysis.sampleclasses.MethodInvocation;
+import com.yahoo.container.plugin.classanalysis.sampleclasses.RecordWithOverride;
+import com.yahoo.container.plugin.classanalysis.sampleclasses.SwitchStatement;
import com.yahoo.osgi.annotation.ExportPackage;
import com.yahoo.osgi.annotation.Version;
import org.junit.jupiter.api.Test;
import javax.security.auth.login.LoginException;
-import java.awt.Image;
+import java.awt.*;
import java.awt.image.ImagingOpException;
import java.awt.image.Kernel;
import java.util.List;
import java.util.Optional;
-import java.util.Set;
import static com.yahoo.container.plugin.classanalysis.TestUtilities.analyzeClass;
import static com.yahoo.container.plugin.classanalysis.TestUtilities.classFile;
@@ -133,7 +132,12 @@ public class AnalyzeClassTest {
@Test
void export_annotations_are_processed() {
assertEquals(Optional.of(new ExportPackageAnnotation(3, 1, 4, "TEST_QUALIFIER-2")),
- Analyze.analyzeClass(classFile("com.yahoo.container.plugin.classanalysis.sampleclasses.package-info")).getExportPackage());
+ Analyze.analyzeClass(classFile("com.yahoo.container.plugin.classanalysis.sampleclasses.package-info")).getExportPackage());
+ }
+
+ @Test
+ void publicApi_annotations_are_processed() {
+ assertTrue(Analyze.analyzeClass(classFile("com.yahoo.container.plugin.classanalysis.sampleclasses.package-info")).isPublicApi());
}
@Test
diff --git a/bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/sampleclasses/package-info.java b/bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/sampleclasses/package-info.java
index d0d1db97c26..5f69032db17 100644
--- a/bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/sampleclasses/package-info.java
+++ b/bundle-plugin/src/test/java/com/yahoo/container/plugin/classanalysis/sampleclasses/package-info.java
@@ -1,6 +1,8 @@
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
@ExportPackage(version = @Version(major = 3, minor = 1, micro = 4, qualifier = "TEST_QUALIFIER-2"))
+@PublicApi
package com.yahoo.container.plugin.classanalysis.sampleclasses;
+import com.yahoo.api.annotations.PublicApi;
import com.yahoo.osgi.annotation.ExportPackage;
import com.yahoo.osgi.annotation.Version;