aboutsummaryrefslogtreecommitdiffstats
path: root/config-model
diff options
context:
space:
mode:
authorBjørn Christian Seime <bjorncs@verizonmedia.com>2022-01-11 16:06:46 +0100
committerBjørn Christian Seime <bjorncs@verizonmedia.com>2022-01-11 16:06:46 +0100
commiteae7143b83a1ff38178e4c744fff04dada17b5c2 (patch)
tree53b7126507db9dd4b04253ba3b70881cc416fa29 /config-model
parent70b6dbe247aa06befea059466a1195ea2ec69f42 (diff)
Add validation of imported Java packages
Diffstat (limited to 'config-model')
-rw-r--r--config-model/pom.xml16
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/application/validation/BundleValidator.java86
-rw-r--r--config-model/src/test/cfg/application/validation/testjars/manifest-producing-import-warnings.MF10
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/application/validation/BundleValidatorTest.java36
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');
+ }
}