diff options
author | Bjørn Christian Seime <bjorncs@verizonmedia.com> | 2022-01-11 16:06:46 +0100 |
---|---|---|
committer | Bjørn Christian Seime <bjorncs@verizonmedia.com> | 2022-01-11 16:06:46 +0100 |
commit | eae7143b83a1ff38178e4c744fff04dada17b5c2 (patch) | |
tree | 53b7126507db9dd4b04253ba3b70881cc416fa29 /config-model | |
parent | 70b6dbe247aa06befea059466a1195ea2ec69f42 (diff) |
Add validation of imported Java packages
Diffstat (limited to 'config-model')
4 files changed, 127 insertions, 21 deletions
diff --git a/config-model/pom.xml b/config-model/pom.xml index d42d5af8975..dc7bec27a3b 100644 --- a/config-model/pom.xml +++ b/config-model/pom.xml @@ -288,6 +288,22 @@ <version>${project.version}</version> <scope>provided</scope> </dependency> + <dependency> + <groupId>biz.aQute.bnd</groupId> + <artifactId>biz.aQute.bndlib</artifactId> + <version>6.1.0</version> + <exclusions> + <exclusion> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-api</artifactId> + </exclusion> + <exclusion> + <!-- These are not needed for our use of bndlib --> + <groupId>org.osgi</groupId> + <artifactId>*</artifactId> + </exclusion> + </exclusions> + </dependency> </dependencies> <build> diff --git a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/BundleValidator.java b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/BundleValidator.java index eb5e25e61cd..b6b9190fedf 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/BundleValidator.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/BundleValidator.java @@ -1,6 +1,9 @@ // Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.vespa.model.application.validation; +import aQute.bnd.header.Parameters; +import aQute.bnd.osgi.Domain; +import aQute.bnd.version.VersionRange; import com.yahoo.config.application.api.ApplicationPackage; import com.yahoo.config.application.api.ComponentInfo; import com.yahoo.config.application.api.DeployLogger; @@ -9,17 +12,22 @@ import com.yahoo.path.Path; import com.yahoo.vespa.model.VespaModel; import java.io.IOException; +import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; +import java.util.function.Predicate; import java.util.jar.Attributes; import java.util.jar.JarFile; import java.util.jar.Manifest; import java.util.logging.Level; /** - * A validator for bundles. Uses BND library for some of the validation (not active yet) + * A validator for bundles. Uses BND library for some of the validation. * * @author hmusum * @author bjorncs @@ -46,15 +54,15 @@ public class BundleValidator extends Validator { } void validateJarFile(DeployLogger deployLogger, JarFile jarFile) throws IOException { - validateOSGIHeaders(deployLogger, jarFile); - } - - public void validateOSGIHeaders(DeployLogger deployLogger, JarFile jarFile) throws IOException { - Manifest mf = jarFile.getManifest(); - if (mf == null) { - throw new IllegalArgumentException("Non-existing or invalid manifest in " + jarFile.getName()); + Manifest manifest = jarFile.getManifest(); + String jarPath = jarFile.getName(); + if (manifest == null) { + throw new IllegalArgumentException("Non-existing or invalid manifest in " + jarPath); } + validateManifest(deployLogger, jarPath, manifest); + } + void validateManifest(DeployLogger deployLogger, String jarPath, Manifest mf) { // Check for required OSGI headers Attributes attributes = mf.getMainAttributes(); HashSet<String> mfAttributes = new HashSet<>(); @@ -66,13 +74,71 @@ public class BundleValidator extends Validator { for (String header : requiredOSGIHeaders) { if (!mfAttributes.contains(header)) { throw new IllegalArgumentException("Required OSGI header '" + header + - "' was not found in manifest in '" + jarFile.getName() + "'"); + "' was not found in manifest in '" + jarPath + "'"); } } if (attributes.getValue("Bundle-Version").endsWith(".SNAPSHOT")) { - deployLogger.logApplicationPackage(Level.WARNING, "Deploying snapshot bundle " + jarFile.getName() + + deployLogger.logApplicationPackage(Level.WARNING, "Deploying snapshot bundle " + jarPath + ".\nTo use this bundle, you must include the qualifier 'SNAPSHOT' in the version specification in services.xml."); } + + if (attributes.getValue("Import-Package") != null) { + validateImportedPackages(deployLogger, jarPath, mf); + } + } + + private static void validateImportedPackages(DeployLogger deployLogger, String jarPath, Manifest manifest) { + Domain osgiHeaders = Domain.domain(manifest); + Parameters importPackage = osgiHeaders.getImportPackage(); + Map<DeprecatedArtifact, List<String>> deprecatedPackagesInUse = new HashMap<>(); + + importPackage.forEach((packageName, attrs) -> { + VersionRange versionRange = attrs.getVersion() != null + ? VersionRange.parseOSGiVersionRange(attrs.getVersion()) + : null; + + for (DeprecatedArtifact deprecatedArtifact : DeprecatedArtifact.values()) { + if (deprecatedArtifact.javaPackages.contains(packageName) + && (versionRange == null || deprecatedArtifact.versionDiscriminator.test(versionRange))) { + deprecatedPackagesInUse.computeIfAbsent(deprecatedArtifact, __ -> new ArrayList<>()) + .add(packageName); + } + } + }); + + deprecatedPackagesInUse.forEach((artifact, packagesInUse) -> { + deployLogger.logApplicationPackage(Level.WARNING, + String.format("For JAR file '%s': \n" + + "Manifest imports the following Java packages from '%s': %s. \n" + + "%s", + jarPath, artifact.name, packagesInUse, artifact.description)); + }); + } + + private enum DeprecatedArtifact { + ORG_JSON("org.json:json", + "The org.json library will no longer provided by jdisc runtime on Vespa 8. " + + "See https://docs.vespa.ai/en/vespa8-release-notes.html#container-runtime.", + Set.of("org.json")); + + final String name; + final Collection<String> javaPackages; + final Predicate<VersionRange> versionDiscriminator; + final String description; + + DeprecatedArtifact(String name, String description, Collection<String> javaPackages) { + this(name, description, __ -> true, javaPackages); + } + + DeprecatedArtifact(String name, + String description, + Predicate<VersionRange> versionDiscriminator, + Collection<String> javaPackages) { + this.name = name; + this.javaPackages = javaPackages; + this.versionDiscriminator = versionDiscriminator; + this.description = description; + } } } diff --git a/config-model/src/test/cfg/application/validation/testjars/manifest-producing-import-warnings.MF b/config-model/src/test/cfg/application/validation/testjars/manifest-producing-import-warnings.MF new file mode 100644 index 00000000000..760a9ecf00f --- /dev/null +++ b/config-model/src/test/cfg/application/validation/testjars/manifest-producing-import-warnings.MF @@ -0,0 +1,10 @@ +Manifest-Version: 1.0 +Export-Package: com.yahoo.vespa.test.myapp;version=1.0.0 +Bundle-ManifestVersion: 2 +Bundle-SymbolicName: my-bundle +Bundle-Version: 7.0.0 +Created-By: vespa container maven plugin +Bundle-Name: my-bundle +Bundle-Vendor: Yahoo! +Import-Package: org.json;version="[0.0.0,1)" + diff --git a/config-model/src/test/java/com/yahoo/vespa/model/application/validation/BundleValidatorTest.java b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/BundleValidatorTest.java index 76ca8ad15dd..e2eae30d78d 100644 --- a/config-model/src/test/java/com/yahoo/vespa/model/application/validation/BundleValidatorTest.java +++ b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/BundleValidatorTest.java @@ -1,16 +1,18 @@ // Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.vespa.model.application.validation; -import org.junit.Test; - import com.yahoo.config.application.api.DeployLogger; import com.yahoo.config.model.application.provider.BaseDeployLogger; +import org.junit.Test; import java.io.File; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; import java.util.jar.JarFile; -import java.util.logging.Level; +import java.util.jar.Manifest; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; @@ -42,15 +44,27 @@ public class BundleValidatorTest { @Test public void require_that_deploying_snapshot_bundle_gives_warning() throws IOException { final StringBuffer buffer = new StringBuffer(); - - DeployLogger logger = new DeployLogger() { - @Override - public void log(Level level, String message) { - buffer.append(message).append('\n'); - } - }; - + + DeployLogger logger = createDeployLogger(buffer); + new BundleValidator().validateJarFile(logger, new JarFile(JARS_DIR + "snapshot_bundle.jar")); assertTrue(buffer.toString().contains("Deploying snapshot bundle")); } + + @Test + public void outputs_deploy_warning_on_import_of_packages_from_deprecated_artifact() throws IOException { + final StringBuffer buffer = new StringBuffer(); + DeployLogger logger = createDeployLogger(buffer); + BundleValidator validator = new BundleValidator(); + Manifest manifest = new Manifest(Files.newInputStream(Paths.get(JARS_DIR + "/manifest-producing-import-warnings.MF"))); + validator.validateManifest(logger, "my-app-bundle.jar", manifest); + assertThat(buffer.toString()) + .contains("For JAR file 'my-app-bundle.jar': \n" + + "Manifest imports the following Java packages from 'org.json:json': [org.json]. \n" + + "The org.json library will no longer provided by jdisc runtime on Vespa 8. See https://docs.vespa.ai/en/vespa8-release-notes.html#container-runtime."); + } + + private DeployLogger createDeployLogger(StringBuffer buffer) { + return (__, message) -> buffer.append(message).append('\n'); + } } |