Release test: check the staged artifacts are valid
Check JDK version for the build
Check Multi-Release jars
Check versions in jar
Check OsgiHeaders

Signed-off-by: jansupol <jan.supol@oracle.com>
diff --git a/tests/release-test/README.MD b/tests/release-test/README.MD
new file mode 100644
index 0000000..395ec2a
--- /dev/null
+++ b/tests/release-test/README.MD
@@ -0,0 +1,20 @@
+[//]: # " Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved. "
+[//]: # "  "
+[//]: # " This program and the accompanying materials are made available under the "
+[//]: # " terms of the Eclipse Public License v. 2.0, which is available at "
+[//]: # " http://www.eclipse.org/legal/epl-2.0. "
+[//]: # "  "
+[//]: # " This Source Code may also be made available under the following Secondary "
+[//]: # " Licenses when the conditions for such availability set forth in the "
+[//]: # " Eclipse Public License v. 2.0 are satisfied: GNU General Public License, "
+[//]: # " version 2 with the GNU Classpath Exception, which is available at "
+[//]: # " https://www.gnu.org/software/classpath/license.html. "
+[//]: # "  "
+[//]: # " SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 "
+
+This module is to check the built artifacts before release.
+
+The module can check Jersey 2.x, 3.0.x, and 3.1.x versions.
+
+Usage:
+ * mvn test -Djersey.version=<version> [-Pstaging]
diff --git a/tests/release-test/pom.xml b/tests/release-test/pom.xml
new file mode 100644
index 0000000..7199c0b
--- /dev/null
+++ b/tests/release-test/pom.xml
@@ -0,0 +1,122 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Public License v. 2.0, which is available at
+    http://www.eclipse.org/legal/epl-2.0.
+
+    This Source Code may also be made available under the following Secondary
+    Licenses when the conditions for such availability set forth in the
+    Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+    version 2 with the GNU Classpath Exception, which is available at
+    https://www.gnu.org/software/classpath/license.html.
+
+    SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.eclipse.ee4j</groupId>
+        <artifactId>project</artifactId>
+        <version>1.0.7</version>
+        <relativePath>../../../pom.xml</relativePath>
+    </parent>
+
+    <groupId>org.glassfish.jersey.tests</groupId>
+    <artifactId>release-test</artifactId>
+    <version>2.37-SNAPSHOT</version>
+    <packaging>jar</packaging>
+    <name>jersey-release-test</name>
+
+    <description>Jersey post-release validation tests</description>
+
+    <properties>
+        <jersey.version>${jersey.version}</jersey.version> <!-- Must pass using -Djersey.version= -->
+        <maven.compiler.source>11</maven.compiler.source>
+        <maven.compiler.target>11</maven.compiler.target>
+        <maven.version>3.8.6</maven.version>
+        <maven.resolver.version>1.7.3</maven.resolver.version> <!-- 1.8.2 does not work-->
+    </properties>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <version>3.0.0-M7</version>
+                <configuration>
+                    <forkCount>1</forkCount>
+                    <reuseForks>false</reuseForks>
+                    <enableAssertions>false</enableAssertions>
+                    <includes>
+                        <include>**/DownloadBomPomDependencies.java</include>
+                        <include>**/*Test.class</include>
+                    </includes>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>org.glassfish.jersey</groupId>
+                <artifactId>jersey-bom</artifactId>
+                <version>${jersey.version}</version>
+                <scope>import</scope>
+                <type>pom</type>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <dependencies>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>4.13.2</version>
+            <scope>test</scope>
+            <!-- maven-plugin-testing-harness does not work with junit 5 -->
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.maven</groupId>
+            <artifactId>maven-core</artifactId>
+            <version>${maven.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.maven</groupId>
+            <artifactId>maven-model</artifactId>
+            <version>${maven.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.maven</groupId>
+            <artifactId>maven-compat</artifactId>
+            <version>${maven.version}</version>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.maven.plugin-testing</groupId>
+            <artifactId>maven-plugin-testing-harness</artifactId>
+            <version>3.3.0</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.maven.resolver</groupId>
+            <artifactId>maven-resolver-connector-basic</artifactId>
+            <version>${maven.resolver.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.maven.resolver</groupId>
+            <artifactId>maven-resolver-transport-http</artifactId>
+            <version>${maven.resolver.version}</version>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/tests/release-test/src/main/java/org/glassfish/jersey/test/artifacts/ClassVersionChecker.java b/tests/release-test/src/main/java/org/glassfish/jersey/test/artifacts/ClassVersionChecker.java
new file mode 100644
index 0000000..43e5327
--- /dev/null
+++ b/tests/release-test/src/main/java/org/glassfish/jersey/test/artifacts/ClassVersionChecker.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.glassfish.jersey.test.artifacts;
+
+import java.io.DataInputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Properties;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+
+class ClassVersionChecker {
+    static TestResult checkClassVersion(JarFile jar, JarEntry entry, Properties properties) throws IOException {
+        final String jerseyVersion = MavenUtil.getJerseyVersion(properties);
+        final int minVersion = jerseyVersion.startsWith("3.1") ? 11 : 8;
+        return checkClassVersion(jar.getInputStream(entry), jar.getName() + File.separator + entry.getName(), minVersion);
+    }
+
+    private static TestResult checkClassVersion(InputStream inputStream, String filename, int version) throws IOException {
+        TestResult result = new TestResult();
+        DataInputStream in = new DataInputStream(inputStream);
+
+        int magic = in.readInt();
+        if (magic != -889275714) {
+            result.exception().append(filename).println(" is not a valid class!");
+        }
+
+        int minor = in.readUnsignedShort();
+        int major = in.readUnsignedShort();
+        int classVersion = convertMajorMinorToSE(major, minor);
+        TestResult.MessageBuilder builder =  classVersion <= version ? result.ok() : result.exception();
+        builder.append(filename).append(": ").append(major).append(".").append(minor).append(" = JDK ")
+                .println(String.valueOf(classVersion));
+        in.close();
+        return result;
+    }
+
+    private static int convertMajorMinorToSE(int major, int minor) {
+        int comp = (major - 44 + minor);
+        return comp;
+    }
+}
diff --git a/tests/release-test/src/main/java/org/glassfish/jersey/test/artifacts/DependencyPair.java b/tests/release-test/src/main/java/org/glassfish/jersey/test/artifacts/DependencyPair.java
new file mode 100644
index 0000000..dd31588
--- /dev/null
+++ b/tests/release-test/src/main/java/org/glassfish/jersey/test/artifacts/DependencyPair.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.glassfish.jersey.test.artifacts;
+
+import java.util.Arrays;
+
+class DependencyPair {
+    private final String groupId;
+    private final String artifactId;
+
+    DependencyPair(String groupId, String artifactId) {
+        this.groupId = groupId;
+        this.artifactId = artifactId;
+    }
+
+    String artifactId() {
+        return artifactId;
+    }
+
+    String groupId() {
+        return groupId;
+    }
+
+    static DependencyPair[] concat(DependencyPair[] first, DependencyPair[] second) {
+        DependencyPair[] result = Arrays.copyOf(first, first.length + second.length);
+        System.arraycopy(second, 0, result, first.length, second.length);
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        return groupId + ':' + artifactId;
+    }
+}
diff --git a/tests/release-test/src/main/java/org/glassfish/jersey/test/artifacts/DependencyResolver.java b/tests/release-test/src/main/java/org/glassfish/jersey/test/artifacts/DependencyResolver.java
new file mode 100644
index 0000000..5df3bc1
--- /dev/null
+++ b/tests/release-test/src/main/java/org/glassfish/jersey/test/artifacts/DependencyResolver.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.glassfish.jersey.test.artifacts;
+
+import org.eclipse.aether.RepositorySystem;
+import org.eclipse.aether.RepositorySystemSession;
+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 java.util.List;
+
+/**
+ *
+ * @author lukas
+ */
+final class DependencyResolver {
+    static Artifact resolveArtifact(org.apache.maven.model.Dependency d, List<RemoteRepository> remoteRepos,
+                                           RepositorySystem repoSystem, RepositorySystemSession repoSession)
+            throws ArtifactResolutionException {
+        DefaultArtifact artifact = new DefaultArtifact(
+                d.getGroupId(), d.getArtifactId(), d.getClassifier(), d.getType(), d.getVersion()
+        );
+        ArtifactRequest request = new ArtifactRequest();
+        request.setArtifact(artifact);
+        request.setRepositories(remoteRepos);
+        return repoSystem.resolveArtifact(repoSession, request).getArtifact();
+    }
+}
\ No newline at end of file
diff --git a/tests/release-test/src/main/java/org/glassfish/jersey/test/artifacts/MavenUtil.java b/tests/release-test/src/main/java/org/glassfish/jersey/test/artifacts/MavenUtil.java
new file mode 100644
index 0000000..2c5b3a2
--- /dev/null
+++ b/tests/release-test/src/main/java/org/glassfish/jersey/test/artifacts/MavenUtil.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+package org.glassfish.jersey.test.artifacts;
+
+import org.apache.maven.model.Dependency;
+import org.apache.maven.model.Model;
+import org.apache.maven.model.Profile;
+import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
+import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.List;
+import java.util.Properties;
+import java.util.stream.Stream;
+
+public final class MavenUtil {
+
+    static final String JERSEY_VERSION = "jersey.version";
+    private static final String PROJECT_VERSION = "project.version";
+
+    static File getArtifactJar(File repositoryRoot, Dependency dependency, Properties properties) {
+        return getArtifactFile(repositoryRoot, dependency, properties, "jar");
+    }
+
+    private static File getArtifactFile(File repositoryRoot, Dependency dependency, Properties properties, String extension) {
+        StringBuilder fileSuffix = new StringBuilder();
+        String groupIdParts[] = dependency.getGroupId().split("\\.");
+        for (String groupIdPart : groupIdParts) {
+            fileSuffix.append(groupIdPart).append(File.separator);
+        }
+        String artifactIdParts[] = dependency.getArtifactId().split("\\.");
+        for (String artifactIdPart : artifactIdParts) {
+            fileSuffix.append(artifactIdPart).append(File.separator);
+        }
+        String version = MavenUtil.getDependencyVersion(dependency, properties);
+        fileSuffix.append(version).append(File.separator);
+        fileSuffix.append(dependency.getArtifactId()).append('-').append(version).append(".").append(extension);
+        return new File(repositoryRoot, fileSuffix.toString());
+    }
+
+    static String getDependencyVersion(Dependency dependency, Properties properties) {
+        String version = dependency.getVersion();
+        if (version.startsWith("${") && version.endsWith("}")) {
+            String property = version.substring(2, version.length() - 1);
+            final String value;
+            switch (property) {
+                case JERSEY_VERSION: // in pom.xml
+                case PROJECT_VERSION: // in bom.pom
+                    value = getJerseyVersion(properties);
+                    break;
+                default:
+                    value = properties.getProperty(property);
+                    break;
+            }
+            version = value == null ? version : value;
+        }
+        return version;
+    }
+
+    static File getLocalMavenRepository() {
+        String folder = System.getProperty("localRepository");
+        return new File(folder);
+    }
+
+    static Properties getMavenProperties() {
+        try {
+            Model model = getModelFromFile("pom.xml");
+            return model.getProperties();
+        } catch (XmlPullParserException | IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    static Stream<Dependency> keepJerseyJars(Stream<Dependency> stream, DependencyPair... keep) {
+        return stream.filter(dependency -> {
+            for (DependencyPair pair : keep) {
+                if (dependency.getGroupId().equals(pair.groupId()) && dependency.getArtifactId().equals(pair.artifactId())) {
+                    return true;
+                }
+            }
+            return false;
+        });
+    }
+
+    static Stream<Dependency> streamJerseyJars() throws IOException, XmlPullParserException {
+        Model model = getModelFromFile("pom.xml");
+        List<Dependency> deps = getBomPomDependencies(model);
+
+        return deps.stream()
+                .filter(dep -> dep.getGroupId().startsWith("org.glassfish.jersey"))
+                .filter(dep -> dep.getType().equals("jar"));
+    }
+
+    private static Model getModelFromFile(String fileName) throws IOException, XmlPullParserException {
+        File pomFile = new File(fileName);
+        return getModelFromFile(pomFile);
+    }
+
+    private static Model getModelFromFile(File file) throws IOException, XmlPullParserException {
+        MavenXpp3Reader mavenReader = new MavenXpp3Reader();
+        try (FileReader fileReader = new FileReader(file)) {
+            Model model = mavenReader.read(fileReader);
+            return model;
+        }
+    }
+
+    private static List<Dependency> getBomPomDependencies(Model model) throws IOException, XmlPullParserException {
+        Dependency bomPom = null;
+        List<Dependency> dependencies = model.getDependencyManagement().getDependencies();
+        for (Dependency dependency : dependencies) {
+            if (dependency.getGroupId().equals("org.glassfish.jersey") && dependency.getArtifactId().equals("jersey-bom")) {
+                bomPom = dependency;
+                break;
+            }
+        }
+        if (bomPom == null) {
+            throw new IllegalStateException("Bom pom not found");
+        }
+        File pom = getArtifactFile(getLocalMavenRepository(), bomPom, model.getProperties(), "pom");
+        Model bomPomModel = getModelFromFile(pom);
+        return bomPomModel.getDependencyManagement().getDependencies();
+    }
+
+    static String getJerseyVersion(Properties properties) {
+        String property = properties.getProperty(JERSEY_VERSION); // when it is in the pom.file
+        if (property == null || property.startsWith("${")) {
+            property = System.getProperty(JERSEY_VERSION);        // not in pom, but -Djersey.version
+        }
+        if (property == null || property.startsWith("${")) {
+            throw new IllegalStateException("Property " + JERSEY_VERSION + " not set (-Djersey.version=)");
+        }
+        return property;
+    }
+
+    /* Unused at the moment, but could be useful in the future in the case of profiles are needed */
+    private static List<Dependency> getProfileDependency(Model model) {
+        List<Dependency> profileDependencies = Collections.EMPTY_LIST;
+        List<Profile> profiles = model.getProfiles();
+        String activeProfile = getActiveProfile();
+        for (Profile profile : profiles) {
+            if (activeProfile.equals(profile.getId())) {
+                profileDependencies = profile.getDependencies();
+                break;
+            }
+        }
+        return profileDependencies;
+    }
+
+    private static String getActiveProfile() {
+        String profileId = System.getProperty("profileId"); // set this to the surefire plugin
+        return profileId;
+    }
+}
diff --git a/tests/release-test/src/main/java/org/glassfish/jersey/test/artifacts/TestResult.java b/tests/release-test/src/main/java/org/glassfish/jersey/test/artifacts/TestResult.java
new file mode 100644
index 0000000..172c596
--- /dev/null
+++ b/tests/release-test/src/main/java/org/glassfish/jersey/test/artifacts/TestResult.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+package org.glassfish.jersey.test.artifacts;
+
+import java.io.IOException;
+import java.util.LinkedList;
+import java.util.List;
+
+class TestResult {
+    private List<String> oks = new LinkedList<>();
+    private List<String> exceptions = new LinkedList<>();
+
+    MessageBuilder ok() {
+        return new MessageBuilder(oks);
+    }
+
+    MessageBuilder exception() {
+        return new MessageBuilder(exceptions);
+    }
+    boolean result() {
+        for (String ok : oks) {
+            System.out.append("(pass) ").print(ok);
+        }
+
+        for (String exception : exceptions) {
+            System.out.append("\u001b[31;1m(FAIL) ").append(exception).print("\u001b[0m");
+        }
+
+        return exceptions.isEmpty();
+    }
+
+    public TestResult append(TestResult result) throws IOException {
+        oks.addAll(result.oks);
+        exceptions.addAll(result.exceptions);
+        return this;
+    }
+
+    class MessageBuilder implements Appendable {
+        final List<String> list;
+        final StringBuilder builder = new StringBuilder();
+
+        MessageBuilder(List<String> list) {
+            this.list = list;
+        }
+
+        @Override
+        public MessageBuilder append(CharSequence csq) throws IOException {
+            builder.append(csq);
+            return this;
+        }
+
+        public MessageBuilder append(int i) throws IOException {
+            builder.append(i);
+            return this;
+        }
+
+        @Override
+        public MessageBuilder append(CharSequence csq, int start, int end) throws IOException {
+            builder.append(csq, start, end);
+            return this;
+        }
+
+        @Override
+        public MessageBuilder append(char c) throws IOException {
+            builder.append(c);
+            return this;
+        }
+
+        public TestResult println(String message) {
+            builder.append(message).append('\n');
+            list.add(builder.toString());
+            return TestResult.this;
+        }
+    }
+}
diff --git a/tests/release-test/src/test/java/org/glassfish/jersey/test/artifacts/DownloadBomPomDependencies.java b/tests/release-test/src/test/java/org/glassfish/jersey/test/artifacts/DownloadBomPomDependencies.java
new file mode 100644
index 0000000..ef31d4e
--- /dev/null
+++ b/tests/release-test/src/test/java/org/glassfish/jersey/test/artifacts/DownloadBomPomDependencies.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+package org.glassfish.jersey.test.artifacts;
+
+import org.eclipse.aether.DefaultRepositorySystemSession;
+import org.eclipse.aether.RepositorySystemSession;
+import org.eclipse.aether.artifact.Artifact;
+import org.eclipse.aether.impl.DefaultServiceLocator;
+import org.eclipse.aether.repository.LocalRepository;
+import org.eclipse.aether.spi.connector.RepositoryConnectorFactory;
+import org.eclipse.aether.spi.connector.transport.TransporterFactory;
+import org.eclipse.aether.transport.http.HttpTransporterFactory;
+
+import org.apache.maven.execution.DefaultMavenExecutionRequest;
+import org.apache.maven.execution.MavenExecutionRequest;
+import org.apache.maven.model.Dependency;
+import org.apache.maven.plugin.testing.AbstractMojoTestCase;
+import org.apache.maven.project.MavenProject;
+import org.apache.maven.project.ProjectBuilder;
+import org.apache.maven.project.ProjectBuildingRequest;
+import org.apache.maven.project.ProjectBuildingResult;
+import org.apache.maven.repository.internal.MavenRepositorySystemUtils;
+
+import org.eclipse.aether.RepositorySystem;
+import org.eclipse.aether.repository.RemoteRepository;
+import org.eclipse.aether.connector.basic.BasicRepositoryConnectorFactory;
+import org.junit.Test;
+
+import java.io.File;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.util.List;
+import java.util.Properties;
+import java.util.stream.Collectors;
+
+public class DownloadBomPomDependencies extends AbstractMojoTestCase {
+
+    @Test
+    public void testDownloadBomPomDependencies() throws Exception {
+//        RepositorySystem repositorySystem = (RepositorySystem) lookup(RepositorySystem.class.getName());
+        DefaultServiceLocator locator = MavenRepositorySystemUtils.newServiceLocator();
+        locator.addService(RepositoryConnectorFactory.class, BasicRepositoryConnectorFactory.class);
+        locator.addService(TransporterFactory.class, HttpTransporterFactory.class);
+        RepositorySystem repositorySystem = locator.getService(RepositorySystem.class);
+
+        RepositorySystemSession repoSession = getRepoSession(repositorySystem);
+        List<RemoteRepository> remoteRepos = getRemoteRepositories(repoSession);
+
+        Properties properties = MavenUtil.getMavenProperties();
+        String jerseyVersion = MavenUtil.getJerseyVersion(properties);
+        List<Dependency> memberDeps = MavenUtil.streamJerseyJars().collect(Collectors.toList());
+        for (Dependency member : memberDeps) {
+            member.setVersion(jerseyVersion);
+            Artifact m = DependencyResolver.resolveArtifact(member, remoteRepos, repositorySystem, repoSession);
+            System.out.append("Resolved ").append(member.getGroupId()).append(":").append(member.getArtifactId()).append(":")
+                    .append(member.getVersion()).append(" to ").println(m.getFile().getName());
+        }
+    }
+
+    private List<RemoteRepository> getRemoteRepositories(RepositorySystemSession session) throws Exception {
+        File pom = lookupResourcesPom("/release-test-pom.xml");
+        MavenExecutionRequest request = new DefaultMavenExecutionRequest();
+        request.setPom(pom);
+        request.addActiveProfile("staging");
+        ProjectBuildingRequest buildingRequest = request
+                .getProjectBuildingRequest()
+                .setRepositorySession(session)
+                .setResolveDependencies(true);
+
+        ProjectBuilder projectBuilder = lookup(ProjectBuilder.class);
+        ProjectBuildingResult projectBuildingResult = projectBuilder.build(pom, buildingRequest);
+        MavenProject project = projectBuildingResult.getProject();
+
+        List<RemoteRepository> remoteArtifactRepositories = project.getRemoteProjectRepositories();
+        return remoteArtifactRepositories;
+    }
+
+    private static RepositorySystemSession getRepoSession(RepositorySystem repositorySystem) {
+        DefaultRepositorySystemSession session = MavenRepositorySystemUtils.newSession();
+        LocalRepository localRepo = new LocalRepository(MavenUtil.getLocalMavenRepository().getAbsolutePath());
+        session.setLocalRepositoryManager(repositorySystem.newLocalRepositoryManager(session, localRepo));
+        return session;
+    }
+
+    private static File lookupResourcesPom(String pomFile) throws URISyntaxException {
+        URL resource = DownloadBomPomDependencies.class.getResource(pomFile);
+        if (resource == null) {
+            throw new IllegalStateException("Pom file " + pomFile + " was not located on classpath!");
+        }
+        File file = new File(resource.toURI());
+        if (!file.exists()) {
+            throw new IllegalStateException("Cannot locate test pom xml file!");
+        }
+        return file;
+    }
+}
diff --git a/tests/release-test/src/test/java/org/glassfish/jersey/test/artifacts/ManifestTest.java b/tests/release-test/src/test/java/org/glassfish/jersey/test/artifacts/ManifestTest.java
new file mode 100644
index 0000000..8da0835
--- /dev/null
+++ b/tests/release-test/src/test/java/org/glassfish/jersey/test/artifacts/ManifestTest.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+package org.glassfish.jersey.test.artifacts;
+
+import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+import java.util.Properties;
+import java.util.jar.JarFile;
+import java.util.stream.Collectors;
+
+public class ManifestTest {
+    private static final File localRepository = MavenUtil.getLocalMavenRepository();
+    private static final Properties properties = MavenUtil.getMavenProperties();
+
+    private static final String BUNDLE_NAME_ATTRIBUTE = "Bundle-Name";
+    private static final String BUNDLE_VERSION_ATTRIBUTE = "Bundle-Version";
+    private static final String [] EXCLUDED_JARS = {"test", "rx-client", "oauth", "weld2-se", "spring",
+            "servlet-portability", /* obsolete */
+            "helidon-connector", /* Helidon does not contain OSGi headers */
+            "grizzly-connector", /* Limited maintenance */
+    };
+
+    @Test
+    public void testHasOsgiManifest() throws IOException, XmlPullParserException {
+        TestResult testResult = new TestResult();
+        List<File> jars = MavenUtil.streamJerseyJars()
+                .filter(dependency -> {
+                    for (String excluded : EXCLUDED_JARS) {
+                        if (dependency.getArtifactId().contains(excluded)) {
+                            return false;
+                        }
+                    }
+                    return true;
+                })
+                .map(dependency -> MavenUtil.getArtifactJar(localRepository, dependency, properties))
+                .collect(Collectors.toList());
+
+        for (String ATTRIBUTE : new String[]{BUNDLE_NAME_ATTRIBUTE, BUNDLE_VERSION_ATTRIBUTE}) {
+            for (File jar : jars) {
+                JarFile jarFile = new JarFile(jar);
+                String value = jarFile.getManifest().getMainAttributes().getValue(ATTRIBUTE);
+                TestResult.MessageBuilder builder = value != null ? testResult.ok() : testResult.exception();
+                builder.append(jar.getName()).append(value == null ? " DOES NOT CONTAIN " : " CONTAINS ")
+                        .append(ATTRIBUTE).println(" attribute");
+            }
+        }
+
+        //Assertions.assertTrue(testResult.result(), "Some error occurred, see previous messages");
+        Assert.assertTrue("Some error occurred, see previous messages", testResult.result());
+    }
+
+}
diff --git a/tests/release-test/src/test/java/org/glassfish/jersey/test/artifacts/MultiReleaseTest.java b/tests/release-test/src/test/java/org/glassfish/jersey/test/artifacts/MultiReleaseTest.java
new file mode 100644
index 0000000..deef29a
--- /dev/null
+++ b/tests/release-test/src/test/java/org/glassfish/jersey/test/artifacts/MultiReleaseTest.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0,
+ * or the Eclipse Distribution License v. 1.0 which is available at
+ * http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+ */
+
+package org.glassfish.jersey.test.artifacts;
+
+import org.apache.maven.model.Dependency;
+import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+import java.util.Optional;
+import java.util.Properties;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import java.util.zip.ZipEntry;
+
+public class MultiReleaseTest {
+    private static final String s = "";
+    private static final File localRepository = MavenUtil.getLocalMavenRepository();
+    private static final Properties properties = MavenUtil.getMavenProperties();
+
+    private static final DependencyPair[] jdk11multiRelease = jdk11multiRelease(properties);
+    private static final DependencyPair[] jdk12multiRelease = jdk12multiRelease(properties);
+    private static final DependencyPair[] jdk17multiRelease = jdk17multiRelease(properties);
+
+    @Test
+    public void testIsJdkMultiRelease() throws IOException, XmlPullParserException {
+        TestResult result = testJdkVersions("11", jdk11multiRelease);
+        result.append(testJdkVersions("12", jdk12multiRelease));
+        result.append(testJdkVersions("17", jdk17multiRelease));
+        //Assertions.assertTrue(result.result(), "Some error occurred, see previous messages");
+        Assert.assertTrue("Some error occurred, see previous messages", result.result());
+    }
+
+    private static TestResult testJdkVersions(String version, DependencyPair... dependencies)
+            throws XmlPullParserException, IOException {
+        final TestResult result = new TestResult();
+        if (dependencies == null || dependencies.length == 0) {
+            return result;
+        }
+
+        Stream<Dependency> deps = MavenUtil.streamJerseyJars();
+        List<File> files = MavenUtil.keepJerseyJars(deps, dependencies)
+                .map(dependency -> MavenUtil.getArtifactJar(localRepository, dependency, properties))
+                .collect(Collectors.toList());
+
+        //Assertions.assertEquals(dependencies.length, files.size(), "Some jdk " + version + " dependencies not found");
+        if (dependencies.length != files.size()) {
+            System.out.println("Expected:");
+            for (DependencyPair pair : dependencies) {
+                System.out.println(pair);
+            }
+            System.out.println("Resolved:");
+            for (File file : files) {
+                System.out.println(file.getName());
+            }
+            Assert.assertEquals("Some jdk " + version + " dependencies not found", dependencies.length, files.size());
+        }
+
+        for (File jar : files) {
+            JarFile jarFile = new JarFile(jar);
+            if (!jarFile.isMultiRelease()) {
+                result.exception().append("Not a multirelease jar ").append(jar.getName()).println("!");
+            }
+            ZipEntry versions = jarFile.getEntry("META-INF/versions/" + version);
+            if (versions == null) {
+                result.exception().append("No classes for JDK ").append(version).append(" for ").println(jar.getName());
+            }
+            result.ok().append("Classes for JDK ").append(version).append(" found for ").println(jar.getName());
+
+            Optional<JarEntry> file = jarFile.stream()
+                    .filter(entry -> !entry.isDirectory())
+                    .filter(entry -> !entry.getName().contains("versions"))
+                    .filter(entry -> entry.getName().endsWith(".class"))
+                    .findAny();
+            JarEntry jarEntry = file.get();
+            result.append(ClassVersionChecker.checkClassVersion(jarFile, jarEntry, properties));
+        }
+
+        return result;
+    }
+
+    private static DependencyPair[] jdk11multiRelease(Properties properties) {
+        String jerseyVersion = MavenUtil.getJerseyVersion(properties);
+        if (jerseyVersion.startsWith("3.0")) {
+            return jdk11multiRelease30();
+        } else if (jerseyVersion.startsWith("3")) {
+            return jdk11multiRelease31();
+        }
+        return jdk11multiRelease2();
+    }
+
+    private static DependencyPair[] jdk11multiRelease2() {
+        return new DependencyPair[] {
+                new DependencyPair("org.glassfish.jersey.bundles", "jaxrs-ri"),
+                new DependencyPair("org.glassfish.jersey.core", "jersey-common")
+        };
+    }
+
+    private static DependencyPair[] jdk11multiRelease30() {
+        DependencyPair[] pairs30 = {
+                new DependencyPair("org.glassfish.jersey.containers", "jersey-container-jetty-http"),
+                new DependencyPair("org.glassfish.jersey.containers", "jersey-container-jetty-servlet"),
+                new DependencyPair("org.glassfish.jersey.test-framework.providers", "jersey-test-framework-provider-jetty")
+        };
+        return DependencyPair.concat(jdk11multiRelease2(), pairs30);
+    }
+
+    private static DependencyPair[] jdk11multiRelease31() {
+        return new DependencyPair[]{};
+    }
+
+    private static DependencyPair[] jdk12multiRelease(Properties properties) {
+        return new DependencyPair[]{
+                new DependencyPair("org.glassfish.jersey.ext", "jersey-wadl-doclet")
+        };
+    }
+
+    private static DependencyPair[] jdk17multiRelease(Properties properties) {
+        String jerseyVersion = MavenUtil.getJerseyVersion(properties);
+        if (jerseyVersion.startsWith("3")) {
+            return new DependencyPair[] {
+                    new DependencyPair("org.glassfish.jersey.connectors", "jersey-helidon-connector"),
+                    new DependencyPair("org.glassfish.jersey.ext", "jersey-spring6")
+            };
+        }
+        return new DependencyPair[]{};
+    }
+}
diff --git a/tests/release-test/src/test/resources/release-test-pom.xml b/tests/release-test/src/test/resources/release-test-pom.xml
new file mode 100644
index 0000000..d8aa486
--- /dev/null
+++ b/tests/release-test/src/test/resources/release-test-pom.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Public License v. 2.0, which is available at
+    http://www.eclipse.org/legal/epl-2.0.
+
+    This Source Code may also be made available under the following Secondary
+    Licenses when the conditions for such availability set forth in the
+    Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+    version 2 with the GNU Classpath Exception, which is available at
+    https://www.gnu.org/software/classpath/license.html.
+
+    SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.eclipse.ee4j</groupId>
+        <artifactId>project</artifactId>
+        <version>1.0.7</version>
+    </parent>
+
+    <groupId>org.glassfish.jersey.tests</groupId>
+    <artifactId>release-test-test</artifactId>
+    <version>0.0.1-SNAPSHOT</version>
+
+</project>
\ No newline at end of file