diff options
Diffstat (limited to 'bundle-plugin/src/main/java/com/yahoo/container/plugin/mojo/GenerateOsgiManifestMojo.java')
-rw-r--r-- | bundle-plugin/src/main/java/com/yahoo/container/plugin/mojo/GenerateOsgiManifestMojo.java | 104 |
1 files changed, 88 insertions, 16 deletions
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); + } + } |