From de71c564b48d07ad8c73d560a4e04073cb5e3560 Mon Sep 17 00:00:00 2001 From: Jeff Trent Date: Wed, 13 Sep 2023 12:21:37 -0400 Subject: [PATCH] backport of maven enforcer rules (#987) --- maven-enforcer-rules/README.md | 51 ++++ maven-enforcer-rules/etc/spotbugs/exclude.xml | 37 +++ maven-enforcer-rules/pom.xml | 85 +++++++ .../rules/DependencyIsValidCheck.java | 219 ++++++++++++++++++ .../rules/HelidonDependenciesRule.java | 103 ++++++++ .../enforcer/rules/ViolationException.java | 51 ++++ .../maven/enforcer/rules/package-info.java | 20 ++ .../main/resources/jakarta-groups.properties | 24 ++ .../main/resources/jakarta-renamed.properties | 177 ++++++++++++++ .../resources/jakarta-versions.properties | 167 +++++++++++++ .../rules/DependencyIsValidCheckTest.java | 101 ++++++++ .../rules/HelidonDependencyRuleTest.java | 43 ++++ pom.xml | 9 +- 13 files changed, 1086 insertions(+), 1 deletion(-) create mode 100644 maven-enforcer-rules/README.md create mode 100644 maven-enforcer-rules/etc/spotbugs/exclude.xml create mode 100644 maven-enforcer-rules/pom.xml create mode 100644 maven-enforcer-rules/src/main/java/io/helidon/build/maven/enforcer/rules/DependencyIsValidCheck.java create mode 100644 maven-enforcer-rules/src/main/java/io/helidon/build/maven/enforcer/rules/HelidonDependenciesRule.java create mode 100644 maven-enforcer-rules/src/main/java/io/helidon/build/maven/enforcer/rules/ViolationException.java create mode 100644 maven-enforcer-rules/src/main/java/io/helidon/build/maven/enforcer/rules/package-info.java create mode 100644 maven-enforcer-rules/src/main/resources/jakarta-groups.properties create mode 100644 maven-enforcer-rules/src/main/resources/jakarta-renamed.properties create mode 100644 maven-enforcer-rules/src/main/resources/jakarta-versions.properties create mode 100644 maven-enforcer-rules/src/test/java/io/helidon/build/maven/enforcer/rules/DependencyIsValidCheckTest.java create mode 100644 maven-enforcer-rules/src/test/java/io/helidon/build/maven/enforcer/rules/HelidonDependencyRuleTest.java diff --git a/maven-enforcer-rules/README.md b/maven-enforcer-rules/README.md new file mode 100644 index 000000000..979349cf3 --- /dev/null +++ b/maven-enforcer-rules/README.md @@ -0,0 +1,51 @@ +# Helidon Build Common Maven Enforcer Rules + +This module provides common Helidon enforcer rules intended to integrate with the [Maven Enforcer Plugin](https://maven.apache.org/enforcer/maven-enforcer-plugin/). + +## Rules +* [HelidonDependenciesRule](src/main/java/io/helidon/build/maven/enforcer/rules/HelidonDependenciesRule.java) - Verifies compile and runtime maven dependencies (including transitive dependencies) for compatibility with Helidon. + +### General usage + +Here is an example pom.xml: + +```xml + + + + org.apache.maven.plugins + maven-enforcer-plugin + + + io.helidon.build-tools + helidon-build-maven-enforcer-rules + ${helidon.build-tools.version} + + + + + enforce-helidon-dependencies + + enforce + + + + + JAKARTA | JAVAX + + + + javax.servlet.* + + + + + + + + ... +``` + +```bash +mvn package +``` diff --git a/maven-enforcer-rules/etc/spotbugs/exclude.xml b/maven-enforcer-rules/etc/spotbugs/exclude.xml new file mode 100644 index 000000000..3e11f1c35 --- /dev/null +++ b/maven-enforcer-rules/etc/spotbugs/exclude.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + diff --git a/maven-enforcer-rules/pom.xml b/maven-enforcer-rules/pom.xml new file mode 100644 index 000000000..3308c0d37 --- /dev/null +++ b/maven-enforcer-rules/pom.xml @@ -0,0 +1,85 @@ + + + + 4.0.0 + + io.helidon.build-tools + helidon-build-tools-project + 3.0.0-SNAPSHOT + + helidon-build-maven-enforcer-rules + Helidon Build Tools Maven Enforcer Rules + + + + 11 + 11 + + etc/spotbugs/exclude.xml + + + + + org.apache.maven + maven-artifact + + + org.apache.maven.enforcer + enforcer-api + true + provided + + + + org.apache.maven + maven-core + true + provided + + + + org.junit.jupiter + junit-jupiter-api + test + + + org.hamcrest + hamcrest-all + test + + + + + + + org.codehaus.plexus + plexus-component-metadata + + + + generate-metadata + + + + + + + diff --git a/maven-enforcer-rules/src/main/java/io/helidon/build/maven/enforcer/rules/DependencyIsValidCheck.java b/maven-enforcer-rules/src/main/java/io/helidon/build/maven/enforcer/rules/DependencyIsValidCheck.java new file mode 100644 index 000000000..2e530e7f9 --- /dev/null +++ b/maven-enforcer-rules/src/main/java/io/helidon/build/maven/enforcer/rules/DependencyIsValidCheck.java @@ -0,0 +1,219 @@ +/* + * Copyright (c) 2023 Oracle and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.helidon.build.maven.enforcer.rules; + +import java.io.IOException; +import java.io.InputStream; +import java.io.UncheckedIOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.function.Function; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.artifact.DefaultArtifact; +import org.apache.maven.artifact.versioning.ArtifactVersion; +import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException; +import org.apache.maven.artifact.versioning.OverConstrainedVersionException; +import org.apache.maven.artifact.versioning.VersionRange; + +/** + * A function that will return {@code true} if the given maven coordinate is valid. + */ +class DependencyIsValidCheck implements Function { + static final String JAKARTA_RENAMED = "jakarta-renamed.properties"; + static final String JAKARTA_VERSIONS = "jakarta-versions.properties"; + static final String JAKARTA_GROUPS = "jakarta-groups.properties"; + + private static final Map PACKAGE_TO_VERSIONS = loadVersions(); + private static final Map PACKAGE_TO_RENAMED = loadRenamed(); + private static final Map GROUP_TO_PACKAGE = loadGroups(); + + private final String namespace; + private final List excludedGavRegExs; + + DependencyIsValidCheck(String namespace, + List excludedGavRegExs) { + this.namespace = namespace; + this.excludedGavRegExs = excludedGavRegExs; + } + + @Override + public Boolean apply(Artifact gav) { + String groupPackageName = toPackage(gav.getGroupId()); + if (isExcluded(groupPackageName)) { + return true; + } + + if (groupPackageName.equals("javax.servlet") + || groupPackageName.equals("jakarta.servlet")) { + return false; + } + + try { + if (HelidonDependenciesRule.JAKARTA.equalsIgnoreCase(namespace)) { + return applyJakartaRule(groupPackageName, gav.getSelectedVersion()); + } else if (HelidonDependenciesRule.JAVAX.equalsIgnoreCase(namespace)) { + return applyJavaxRule(groupPackageName, gav.getSelectedVersion()); + } else { + throw new IllegalStateException("Invalid namespace: " + namespace); + } + } catch (OverConstrainedVersionException e) { + throw new IllegalStateException(e); + } + } + + /** + * Checks the given maven GAV. + * + * @param gav the maven GAV + * @return true if the GAV is not in violation + */ + public boolean apply(String gav) { + return apply(toArtifact(gav)); + } + + /** + * Validates the provided maven GAVs. If any are invalid an exception is thrown. + * + * @param gavs the array of maven GAVs + * @throws ViolationException if a passed GAV is in violation of Helidon's usage policy + */ + public void validate(String... gavs) throws ViolationException { + validate(Arrays.stream(gavs).map(DependencyIsValidCheck::toArtifact).collect(Collectors.toList())); + } + + /** + * Validates the provided maven GAVs. If any are invalid an exception is thrown. + * + * @param gavs the collection of maven GAVs + * @throws ViolationException if a passed GAV is in violation of Helidon's usage policy + */ + public void validate(Collection gavs) throws ViolationException { + List violations = new ArrayList<>(); + for (Artifact gav : gavs) { + if (!apply(gav)) { + violations.add(gav.getGroupId() + ":" + gav.getArtifactId() + ":" + gav.getVersion()); + } + } + + if (!violations.isEmpty()) { + throw new ViolationException("Bad dependencies spotted (review with mvn dependency:tree): " + violations, violations); + } + } + + boolean applyJakartaRule(String groupPackageName, + ArtifactVersion version) { + if (groupPackageName.startsWith("javax.")) { + String renamedPackage = PACKAGE_TO_RENAMED.get(groupPackageName); + return (renamedPackage == null); + } else if (groupPackageName.startsWith("jakarta.")) { + VersionRange versionRange = PACKAGE_TO_VERSIONS.get(groupPackageName); + if (versionRange != null) { + return versionRange.containsVersion(version); + } + } + + return true; + } + + boolean applyJavaxRule(String groupPackageName, + ArtifactVersion version) { + if (groupPackageName.startsWith("jakarta.")) { + VersionRange versionRange = PACKAGE_TO_VERSIONS.get(groupPackageName); + if (versionRange != null) { + return !versionRange.containsVersion(version); + } + } + return true; + } + + /** + * Converts a group name to a package name. + * + * @param group the group name + * @return the associated package name + */ + String toPackage(String group) { + String packageName = GROUP_TO_PACKAGE.get(group); + return (packageName == null) ? group : packageName; + + } + + boolean isExcluded(String groupPackageName) { + for (Pattern pattern : excludedGavRegExs) { + if (pattern.matcher(groupPackageName).matches()) { + return true; + } + } + return false; + } + + static DefaultArtifact toArtifact(String gav) { + String[] split = gav.split(":"); + DefaultArtifact artifact = new DefaultArtifact(split[0], + split.length > 1 ? split[1] : "", // artifact + split.length > 2 ? split[2] : null, // version + split.length > 3 ? split[3] : "compile", // scope + split.length > 4 ? split[4] : "jar", // type + split.length > 5 ? split[5] : "", // classifier + null); // handler + return artifact; + } + + static Map loadVersions() { + Map result = new LinkedHashMap<>(); + load(JAKARTA_VERSIONS).forEach((k, v) -> { + try { + result.put(k.toString(), VersionRange.createFromVersionSpec(v.toString())); + } catch (InvalidVersionSpecificationException e) { + throw new IllegalStateException(e); + } + }); + return result; + } + + static Map loadRenamed() { + Map result = new LinkedHashMap<>(); + load(JAKARTA_RENAMED).forEach((k, v) -> result.put(k.toString(), v.toString())); + return result; + } + + static Map loadGroups() { + Map result = new LinkedHashMap<>(); + load(JAKARTA_GROUPS).forEach((k, v) -> result.put(k.toString(), v.toString())); + return result; + } + + static Properties load(String name) { + Properties props = new Properties(); + try (InputStream is = DependencyIsValidCheck.class.getClassLoader() + .getResourceAsStream(name)) { + props.load(is); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + return props; + } + +} diff --git a/maven-enforcer-rules/src/main/java/io/helidon/build/maven/enforcer/rules/HelidonDependenciesRule.java b/maven-enforcer-rules/src/main/java/io/helidon/build/maven/enforcer/rules/HelidonDependenciesRule.java new file mode 100644 index 000000000..713841ed3 --- /dev/null +++ b/maven-enforcer-rules/src/main/java/io/helidon/build/maven/enforcer/rules/HelidonDependenciesRule.java @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2023 Oracle and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.helidon.build.maven.enforcer.rules; + +import java.util.List; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +import javax.inject.Inject; +import javax.inject.Named; + +import org.apache.maven.artifact.Artifact; +import org.apache.maven.enforcer.rule.api.AbstractEnforcerRule; +import org.apache.maven.execution.MavenSession; +import org.apache.maven.project.MavenProject; + +/** + * This rule will apply the same basic "eclipse transformer" checks, but applied as an enforcer rule. + */ +// see https://maven.apache.org/enforcer/enforcer-api/writing-a-custom-rule.html +// see https://projects.eclipse.org/projects/technology.transformer +@Named("helidonDependenciesRule") // rule name - must start from lowercase character +@SuppressWarnings("unused") +public class HelidonDependenciesRule extends AbstractEnforcerRule { + /** + * An appropriate value for {@link #namespace} (value={@value}). + */ + static final String JAVAX = "javax"; + /** + * An appropriate value for {@link #namespace} (value={@value}). + */ + static final String JAKARTA = "jakarta"; + + @Inject + private MavenProject project; + + @Inject + private MavenSession session; + + /** + * Rule parameter as list of gav regular expressions. Reflectively injected from pom.xml + */ + // @Inject // injected by maven + private List excludedGavRegExs; + + /** + * Rule parameter string. The enforcement policy can either be {@link #JAVAX} or {@link #JAKARTA}. + * Reflectively injected from pom.xml. The default is {@link #JAKARTA}. + * When set to {@link #JAKARTA}, only "post javax->jakarta" module names are allowed. When set to {@link #JAVAX}, only + * "pre javax->jakarta" module names are allowed. + */ + // @Inject // injected by maven + private String namespace; + + @Override + public void execute() throws ViolationException { + String namespace = checkNamespace(this.namespace); + List excludedGavRegExs = this.excludedGavRegExs.stream() + .map(Pattern::compile) + .collect(Collectors.toList()); + List artifacts = project.getArtifacts().stream() + .filter(a -> a.getGroupId().startsWith("javax.") || a.getGroupId().startsWith("jakarta.")) + .filter(a -> a.getScope().equalsIgnoreCase("compile") || a.getScope().equalsIgnoreCase("runtime")) + .filter(a -> !a.isOptional()) + .collect(Collectors.toList()); + DependencyIsValidCheck check = new DependencyIsValidCheck(namespace, excludedGavRegExs); + check.validate(artifacts); + } + + @Override + public String toString() { + return String.format(getClass().getSimpleName() + "[namespace=%s, excludedGavRegExs=%s]", namespace, excludedGavRegExs); + } + + static String checkNamespace(String namespace) { + if (namespace == null || namespace.isBlank()) { + return JAKARTA; + } + + namespace = namespace.toLowerCase().trim(); + if (namespace.equals(JAVAX) || namespace.equals(JAKARTA)) { + return namespace; + } + + throw new IllegalArgumentException("The namespace '" + namespace + "' is invalid. Only valid namespace names are: '" + + JAKARTA + "' and '" + JAVAX + "'."); + } + +} diff --git a/maven-enforcer-rules/src/main/java/io/helidon/build/maven/enforcer/rules/ViolationException.java b/maven-enforcer-rules/src/main/java/io/helidon/build/maven/enforcer/rules/ViolationException.java new file mode 100644 index 000000000..f5b7e9780 --- /dev/null +++ b/maven-enforcer-rules/src/main/java/io/helidon/build/maven/enforcer/rules/ViolationException.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2023 Oracle and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.helidon.build.maven.enforcer.rules; + +import java.util.List; + +import org.apache.maven.enforcer.rule.api.EnforcerRuleException; + +/** + * Thrown when an unsupported type is observed. + */ +public class ViolationException extends EnforcerRuleException { + + private final List violations; + + /** + * Constructor. + * + * @param message the message + * @param violations the gavs in violation + */ + public ViolationException(String message, + List violations) { + super(message); + this.violations = List.copyOf(violations); + } + + /** + * The GAV violations. + * + * @return gav violations + */ + public List violations() { + return violations; + } + +} diff --git a/maven-enforcer-rules/src/main/java/io/helidon/build/maven/enforcer/rules/package-info.java b/maven-enforcer-rules/src/main/java/io/helidon/build/maven/enforcer/rules/package-info.java new file mode 100644 index 000000000..ee215b15e --- /dev/null +++ b/maven-enforcer-rules/src/main/java/io/helidon/build/maven/enforcer/rules/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2023 Oracle and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Helidon Build Maven Enforcer Rules. + */ +package io.helidon.build.maven.enforcer.rules; diff --git a/maven-enforcer-rules/src/main/resources/jakarta-groups.properties b/maven-enforcer-rules/src/main/resources/jakarta-groups.properties new file mode 100644 index 000000000..7739e5630 --- /dev/null +++ b/maven-enforcer-rules/src/main/resources/jakarta-groups.properties @@ -0,0 +1,24 @@ +# +# Copyright (c) 2023 Oracle and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Format: +# mavenGroupId:artifactId=jakarta-packagename +# +# If the maven G:A is not found to be present in these properties, then it can be assumed that that jakarta-packagename is the +# same as the maven group id. +# +# javax.servlet:javax.servlet=jakarta.servlet +# diff --git a/maven-enforcer-rules/src/main/resources/jakarta-renamed.properties b/maven-enforcer-rules/src/main/resources/jakarta-renamed.properties new file mode 100644 index 000000000..e7a54f584 --- /dev/null +++ b/maven-enforcer-rules/src/main/resources/jakarta-renamed.properties @@ -0,0 +1,177 @@ +# +# Copyright (c) 2023 Oracle and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# sourced from https://github.com/eclipse/transformer/blob/main/org.eclipse.transformer.jakarta/src/main/resources/org/eclipse/transformer/jakarta/jakarta-renames.properties + +# +# Default jakarta configuration for package renames. +# +# Format: +# currentPackageName=newPackageName +# +# The current package name may contain a ".*" suffix. The suffix indicates +# that sub-packages are to be matched. For example: +# +# javax.servlet.*=jakartax.servlet +# +# This will cause sub-packages like javax.servlet.annotation +# to be replaced with jakartax.servlet.annotation +# +# By default, sub-packages are not updated. +# +# Package renames apply in several ways: +# Class references in .class files are updated. +# Qualified class names which appear in java string constants in .class files are updated. +# Qualified resource references which appear in java string constants in .class files are updated. +# Package references in manifest (MANIFEST.MF) and feature manifest (featureName.mf) files are updated. +# + +javax.activation=jakarta.activation +javax.annotation.security=jakarta.annotation.security +javax.annotation.sql=jakarta.annotation.sql +javax.annotation=jakarta.annotation +javax.batch.api.chunk.listener=jakarta.batch.api.chunk.listener +javax.batch.api.chunk=jakarta.batch.api.chunk +javax.batch.api.listener=jakarta.batch.api.listener +javax.batch.api.partition=jakarta.batch.api.partition +javax.batch.api=jakarta.batch.api +javax.batch.operations=jakarta.batch.operations +javax.batch.runtime.context=jakarta.batch.runtime.context +javax.batch.runtime=jakarta.batch.runtime +javax.decorator=jakarta.decorator +javax.ejb.embeddable=jakarta.ejb.embeddable +javax.ejb.spi=jakarta.ejb.spi +javax.ejb=jakarta.ejb +javax.el=jakarta.el +javax.enterprise.concurrent=jakarta.enterprise.concurrent +javax.enterprise.context.control=jakarta.enterprise.context.control +javax.enterprise.context.spi=jakarta.enterprise.context.spi +javax.enterprise.context=jakarta.enterprise.context +javax.enterprise.event=jakarta.enterprise.event +javax.enterprise.inject.literal=jakarta.enterprise.inject.literal +javax.enterprise.inject.se=jakarta.enterprise.inject.se +javax.enterprise.inject.spi.configurator=jakarta.enterprise.inject.spi.configurator +javax.enterprise.inject.spi=jakarta.enterprise.inject.spi +javax.enterprise.inject=jakarta.enterprise.inject +javax.enterprise.util=jakarta.enterprise.util +javax.faces.annotation=jakarta.faces.annotation +javax.faces.application=jakarta.faces.application +javax.faces.bean=jakarta.faces.bean +javax.faces.component.behavior=jakarta.faces.component.behavior +javax.faces.component.html=jakarta.faces.component.html +javax.faces.component.search=jakarta.faces.component.search +javax.faces.component.visit=jakarta.faces.component.visit +javax.faces.component=jakarta.faces.component +javax.faces.context=jakarta.faces.context +javax.faces.convert=jakarta.faces.convert +javax.faces.el=jakarta.faces.el +javax.faces.event=jakarta.faces.event +javax.faces.flow.builder=jakarta.faces.flow.builder +javax.faces.flow=jakarta.faces.flow +javax.faces.lifecycle=jakarta.faces.lifecycle +javax.faces.model=jakarta.faces.model +javax.faces.push=jakarta.faces.push +javax.faces.render=jakarta.faces.render +javax.faces.validator=jakarta.faces.validator +javax.faces.view.facelets=jakarta.faces.view.facelets +javax.faces.view=jakarta.faces.view +javax.faces.webapp=jakarta.faces.webapp +javax.faces=jakarta.faces +javax.inject=jakarta.inject +javax.interceptor=jakarta.interceptor +javax.jms=jakarta.jms +javax.json.bind.adapter=jakarta.json.bind.adapter +javax.json.bind.annotation=jakarta.json.bind.annotation +javax.json.bind.config=jakarta.json.bind.config +javax.json.bind.serializer=jakarta.json.bind.serializer +javax.json.bind.spi=jakarta.json.bind.spi +javax.json.bind=jakarta.json.bind +javax.json.spi=jakarta.json.spi +javax.json.stream=jakarta.json.stream +javax.json=jakarta.json +javax.jws.soap=jakarta.jws.soap +javax.jws=jakarta.jws +javax.mail.event=jakarta.mail.event +javax.mail.internet=jakarta.mail.internet +javax.mail.search=jakarta.mail.search +javax.mail.util=jakarta.mail.util +javax.mail=jakarta.mail +javax.persistence.criteria=jakarta.persistence.criteria +javax.persistence.metamodel=jakarta.persistence.metamodel +javax.persistence.spi=jakarta.persistence.spi +javax.persistence=jakarta.persistence +javax.resource.cci=jakarta.resource.cci +javax.resource.spi.endpoint=jakarta.resource.spi.endpoint +javax.resource.spi.security=jakarta.resource.spi.security +javax.resource.spi.work=jakarta.resource.spi.work +javax.resource.spi=jakarta.resource.spi +javax.resource=jakarta.resource +javax.security.auth.message.callback=jakarta.security.auth.message.callback +javax.security.auth.message.config=jakarta.security.auth.message.config +javax.security.auth.message.module=jakarta.security.auth.message.module +javax.security.auth.message=jakarta.security.auth.message +javax.security.enterprise.authentication.mechanism.http=jakarta.security.enterprise.authentication.mechanism.http +javax.security.enterprise.credential=jakarta.security.enterprise.credential +javax.security.enterprise.identitystore=jakarta.security.enterprise.identitystore +javax.security.enterprise=jakarta.security.enterprise +javax.security.jacc=jakarta.security.jacc +javax.servlet.annotation=jakarta.servlet.annotation +javax.servlet.descriptor=jakarta.servlet.descriptor +javax.servlet.http=jakarta.servlet.http +javax.servlet.jsp.el=jakarta.servlet.jsp.el +javax.servlet.jsp.jstl.core=jakarta.servlet.jsp.jstl.core +javax.servlet.jsp.jstl.fmt=jakarta.servlet.jsp.jstl.fmt +javax.servlet.jsp.jstl.sql=jakarta.servlet.jsp.jstl.sql +javax.servlet.jsp.jstl.tlv=jakarta.servlet.jsp.jstl.tlv +javax.servlet.jsp.jstl=jakarta.servlet.jsp.jstl +javax.servlet.jsp.resources=jakarta.servlet.jsp.resources +javax.servlet.jsp.tagext=jakarta.servlet.jsp.tagext +javax.servlet.jsp=jakarta.servlet.jsp +javax.servlet.resources=jakarta.servlet.resources +javax.servlet=jakarta.servlet +javax.transaction=jakarta.transaction +javax.validation.bootstrap=jakarta.validation.bootstrap +javax.validation.constraints=jakarta.validation.constraints +javax.validation.constraintvalidation=jakarta.validation.constraintvalidation +javax.validation.executable=jakarta.validation.executable +javax.validation.groups=jakarta.validation.groups +javax.validation.metadata=jakarta.validation.metadata +javax.validation.spi=jakarta.validation.spi +javax.validation.valueextraction=jakarta.validation.valueextraction +javax.validation=jakarta.validation +javax.websocket.server=jakarta.websocket.server +javax.websocket=jakarta.websocket +javax.ws.rs.client=jakarta.ws.rs.client +javax.ws.rs.container=jakarta.ws.rs.container +javax.ws.rs.core=jakarta.ws.rs.core +javax.ws.rs.ext=jakarta.ws.rs.ext +javax.ws.rs.sse=jakarta.ws.rs.sse +javax.ws.rs=jakarta.ws.rs +javax.xml.bind.annotation.adapters=jakarta.xml.bind.annotation.adapters +javax.xml.bind.annotation=jakarta.xml.bind.annotation +javax.xml.bind.attachment=jakarta.xml.bind.attachment +javax.xml.bind.helpers=jakarta.xml.bind.helpers +javax.xml.bind.util=jakarta.xml.bind.util +javax.xml.bind=jakarta.xml.bind +javax.xml.soap=jakarta.xml.soap +javax.xml.ws.handler.soap=jakarta.xml.ws.handler.soap +javax.xml.ws.handler=jakarta.xml.ws.handler +javax.xml.ws.http=jakarta.xml.ws.http +javax.xml.ws.soap=jakarta.xml.ws.soap +javax.xml.ws.spi.http=jakarta.xml.ws.spi.http +javax.xml.ws.spi=jakarta.xml.ws.spi +javax.xml.ws.wsaddressing=jakarta.xml.ws.wsaddressing +javax.xml.ws=jakarta.xml.ws diff --git a/maven-enforcer-rules/src/main/resources/jakarta-versions.properties b/maven-enforcer-rules/src/main/resources/jakarta-versions.properties new file mode 100644 index 000000000..c0a29dc40 --- /dev/null +++ b/maven-enforcer-rules/src/main/resources/jakarta-versions.properties @@ -0,0 +1,167 @@ +# +# Copyright (c) 2023 Oracle and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# sourced from https://github.com/eclipse/transformer/blob/main/org.eclipse.transformer.jakarta/src/main/resources/org/eclipse/transformer/jakarta/jakarta-versions.properties + +# +# Default jakarta configuration for bundle metadata version updates. +# +# These supplemental properties are used to perform version range updates +# when performing package renames on bundle metadata. +# +# Format: +# newPackageName=newVersionText +# +# The new package name must match a package name specified in the package rename rules. +# +# The new version text is a new version range which is to substituted into bundle metadata +# whenever a substitution generates the new package name. +# +# Updates are made to manifest (MANIFEST.MF) and feature manifest (feature-name.mf) files. +# +# For example, starting with a package reference: +# javax.servlet; version="[2.6,3)" +# +# In the absence of a version update rule, jakarta package renaming would update this to: +# jakarta.servlet; version="[2.6,3)" +# +# Together with the version update rule: +# jakarta.servlet=[5.0,6) +# +# The final text of the update is: +# jakarta.servlet; version="[5.0,6)" +# + +jakarta.activation=[2.0,3) +jakarta.annotation.security=[2.0,3) +jakarta.annotation.sql=[2.0,3) +jakarta.annotation=[2.0,3) +jakarta.batch.api.chunk.listener=[2.0,3) +jakarta.batch.api.chunk=[2.0,3) +jakarta.batch.api.listener=[2.0,3) +jakarta.batch.api.partition=[2.0,3) +jakarta.batch.api=[2.0,3) +jakarta.batch.operations=[2.0,3) +jakarta.batch.runtime.context=[2.0,3) +jakarta.batch.runtime=[2.0,3) +jakarta.decorator=[3.0,4.0) +jakarta.ejb.embeddable=[4.0,5) +jakarta.ejb.spi=[4.0,5) +jakarta.ejb=[4.0,5) +jakarta.el=[4.0,5) +jakarta.enterprise.concurrent=[2.0,4) +jakarta.enterprise.context.control=[3.0,4.0) +jakarta.enterprise.context.spi=[3.0,4.0) +jakarta.enterprise.context=[3.0,4.0) +jakarta.enterprise.event=[3.0,4.0) +jakarta.enterprise.inject.literal=[3.0,4.0) +jakarta.enterprise.inject.spi.configurator=[3.0,4.0) +jakarta.enterprise.inject.spi=[3.0,4.0) +jakarta.enterprise.inject=[3.0,4.0) +jakarta.enterprise.util=[3.0,4.0) +jakarta.faces.application=[3.0,4.0) +jakarta.faces.bean=[3.0,4.0) +jakarta.faces.component.behavior=[3.0,4.0) +jakarta.faces.component=[3.0,4.0) +jakarta.faces.context=[3.0,4.0) +jakarta.faces.convert=[3.0,4.0) +jakarta.faces.event=[3.0,4.0) +jakarta.faces.model=[3.0,4.0) +jakarta.faces.render=[3.0,4.0) +jakarta.faces.validator=[3.0,4.0) +jakarta.faces.view.facelets=[3.0,4.0) +jakarta.faces.webapp=[3.0,4.0) +jakarta.faces=[3.0,4.0) +jakarta.inject=[2.0,3.0) +jakarta.interceptor=[2.0,3.0) +jakarta.jms=[3.0,4) +jakarta.json.bind.adapter=[2.0,3.0) +jakarta.json.bind.annotation=[2.0,3.0) +jakarta.json.bind.config=[2.0,3.0) +jakarta.json.bind.serializer=[2.0,3.0) +jakarta.json.bind.spi=[2.0,3.0) +jakarta.json.bind=[2.0,3.0) +jakarta.json.spi=[2.0,3.0) +jakarta.json.stream=[2.0,3.0) +jakarta.json=[2.0,3.0) +jakarta.jws.soap=[3.0,4) +jakarta.jws=[3.0,4) +jakarta.persistence.criteria=[3.0,4) +jakarta.persistence.metamodel=[3.0,4) +jakarta.persistence.spi=[3.0,4) +jakarta.persistence=[3.0,4) +jakarta.resource.cci=[2.0,3) +jakarta.resource.spi.endpoint=[2.0,3) +jakarta.resource.spi.security=[2.0,3) +jakarta.resource.spi.work=[2.0,3) +jakarta.resource.spi=[2.0,3) +jakarta.resource=[2.0,3) +jakarta.security.auth.message.callback=[2.0,3) +jakarta.security.auth.message.config=[2.0,3) +jakarta.security.auth.message.module=[2.0,3) +jakarta.security.auth.message=[2.0,3) +jakarta.security.enterprise.authentication.mechanism.http=[2.0,3) +jakarta.security.enterprise.credential=[2.0,3) +jakarta.security.enterprise.identitystore=[2.0,3) +jakarta.security.enterprise=[2.0,3) +jakarta.security.jacc=[2.0,3) +jakarta.servlet.annotation=[5.0,6) +jakarta.servlet.descriptor=[5.0,6) +jakarta.servlet.http=[5.0,6) +jakarta.servlet.jsp.el=[3.0,4) +jakarta.servlet.jsp.jstl.core=[2.0,3) +jakarta.servlet.jsp.jstl.fmt=[2.0,3) +jakarta.servlet.jsp.jstl.sql=[2.0,3) +jakarta.servlet.jsp.jstl.tlv=[2.0,3) +jakarta.servlet.jsp.jstl=[2.0,3) +jakarta.servlet.jsp.resources=[3.0,4) +jakarta.servlet.jsp.tagext=[3.0,4) +jakarta.servlet.jsp=[3.0,4) +jakarta.servlet.resources=[5.0,6) +jakarta.servlet=[5.0,6) +jakarta.transaction=[2.0,3) +jakarta.validation.bootstrap=[3.0,4) +jakarta.validation.constraints=[3.0,4) +jakarta.validation.constraintvalidation=[3.0,4) +jakarta.validation.executable=[3.0,4) +jakarta.validation.groups=[3.0,4) +jakarta.validation.metadata=[3.0,4) +jakarta.validation.spi=[3.0,4) +jakarta.validation.valueextraction=[3.0,4) +jakarta.validation=[3.0,4) +jakarta.websocket.server=[2.0,3) +jakarta.websocket=[2.0,3) +jakarta.ws.rs.client=[3.0,4.0) +jakarta.ws.rs.container=[3.0,4.0) +jakarta.ws.rs.core=[3.0,4.0) +jakarta.ws.rs.ext=[3.0,4.0) +jakarta.ws.rs.sse=[3.0,4.0) +jakarta.ws.rs=[3.0,4.0) +jakarta.xml.bind.annotation.adapters=[3.0,4) +jakarta.xml.bind.annotation=[3.0,4) +jakarta.xml.bind.attachment=[3.0,4) +jakarta.xml.bind.helpers=[3.0,4) +jakarta.xml.bind.util=[3.0,4) +jakarta.xml.bind=[3.0,4) +jakarta.xml.soap=[3.0,4) +jakarta.xml.ws.handler.soap=[3.0,4) +jakarta.xml.ws.handler=[3.0,4) +jakarta.xml.ws.http=[3.0,4) +jakarta.xml.ws.soap=[3.0,4) +jakarta.xml.ws.spi.http=[3.0,4) +jakarta.xml.ws.spi=[3.0,4) +jakarta.xml.ws.wsaddressing=[3.0,4) +jakarta.xml.ws=[3.0,4) diff --git a/maven-enforcer-rules/src/test/java/io/helidon/build/maven/enforcer/rules/DependencyIsValidCheckTest.java b/maven-enforcer-rules/src/test/java/io/helidon/build/maven/enforcer/rules/DependencyIsValidCheckTest.java new file mode 100644 index 000000000..b9e397cc3 --- /dev/null +++ b/maven-enforcer-rules/src/test/java/io/helidon/build/maven/enforcer/rules/DependencyIsValidCheckTest.java @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2023 Oracle and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.helidon.build.maven.enforcer.rules; + +import java.util.List; +import java.util.regex.Pattern; + +import org.junit.jupiter.api.Test; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.startsWith; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.contains; +import static org.junit.jupiter.api.Assertions.assertThrows; + +class DependencyIsValidCheckTest { + DependencyIsValidCheck dependencyIsValidCheckForJakarta = + new DependencyIsValidCheck(HelidonDependenciesRule.JAKARTA, List.of()); + DependencyIsValidCheck dependencyIsValidCheckForJavax = + new DependencyIsValidCheck(HelidonDependenciesRule.JAVAX, List.of()); + + @Test + void testServletIsNotValid() { + assertThat(dependencyIsValidCheckForJakarta.apply("javax.servlet:javax.servlet-api:2.1.0"), + is(false)); + assertThat(dependencyIsValidCheckForJavax.apply("javax.servlet:javax.servlet-api:2.1.0"), + is(false)); + } + + @Test + void testServletIsValidWithExplicitExclusion() { + dependencyIsValidCheckForJakarta = + new DependencyIsValidCheck(HelidonDependenciesRule.JAKARTA, List.of(Pattern.compile("javax.servlet.*"))); + assertThat(dependencyIsValidCheckForJakarta.apply("javax.servlet:javax.servlet-api:2.1.0"), + is(true)); + assertThat(dependencyIsValidCheckForJakarta.apply("jakarta.servlet:jakarta.servlet-api:2.1.0"), + is(false)); + + dependencyIsValidCheckForJavax = + new DependencyIsValidCheck(HelidonDependenciesRule.JAVAX, List.of(Pattern.compile("javax.servlet.*"))); + assertThat(dependencyIsValidCheckForJakarta.apply("javax.servlet:javax.servlet-api:2.1.0"), + is(true)); + assertThat(dependencyIsValidCheckForJakarta.apply("jakarta.servlet:jakarta.servlet-api:2.1.0"), + is(false)); + } + + @Test + void testAnnotations() { + assertThat(dependencyIsValidCheckForJakarta.apply("jakarta.annotation:jakarta.annotation-api:2.1.1"), + is(true)); + assertThat(dependencyIsValidCheckForJakarta.apply("jakarta.annotation:jakarta.annotation-api:1.0.0"), + is(false)); + + assertThat(dependencyIsValidCheckForJavax.apply("jakarta.annotation:jakarta.annotation-api:2.1.1"), + is(false)); + assertThat(dependencyIsValidCheckForJavax.apply("jakarta.annotation:jakarta.annotation-api:1.0.0"), + is(true)); + } + + @Test + void testJavaxCryptoIsValid() { + assertThat(dependencyIsValidCheckForJakarta.apply("javax.crypto:javax.crypto:1"), + is(true)); + } + + @Test + void testJavaxInject() { + assertThat(dependencyIsValidCheckForJakarta.apply("javax.inject:javax.inject:1"), + is(false)); + assertThat(dependencyIsValidCheckForJavax.apply("javax.inject:javax.inject:1"), + is(true)); + } + + @Test + void testValidate() { + ViolationException ve = assertThrows(ViolationException.class, () -> + dependencyIsValidCheckForJakarta.validate("javax.inject:javax.inject:1", + "javax.crypto:javax.crypto:1", + "javax.servlet:javax.servlet-api:2.1.0", + "other.servlet:other.servlet:1")); + assertThat(ve.getMessage(), + startsWith("Bad dependencies spotted (review with mvn dependency:tree): ")); + assertThat(ve.violations(), + contains("javax.inject:javax.inject:1", "javax.servlet:javax.servlet-api:2.1.0")); + } + +} diff --git a/maven-enforcer-rules/src/test/java/io/helidon/build/maven/enforcer/rules/HelidonDependencyRuleTest.java b/maven-enforcer-rules/src/test/java/io/helidon/build/maven/enforcer/rules/HelidonDependencyRuleTest.java new file mode 100644 index 000000000..c8bcb13c9 --- /dev/null +++ b/maven-enforcer-rules/src/test/java/io/helidon/build/maven/enforcer/rules/HelidonDependencyRuleTest.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2023 Oracle and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.helidon.build.maven.enforcer.rules; + +import org.junit.jupiter.api.Test; + +import static io.helidon.build.maven.enforcer.rules.HelidonDependenciesRule.checkNamespace; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; + +class HelidonDependencyRuleTest { + + @Test + void testCheckNamespace() { + assertThat(checkNamespace(null), + equalTo(HelidonDependenciesRule.JAKARTA)); + assertThat(checkNamespace(""), + equalTo(HelidonDependenciesRule.JAKARTA)); + assertThat(checkNamespace("jakarta"), + equalTo(HelidonDependenciesRule.JAKARTA)); + assertThat(checkNamespace("javax"), + equalTo(HelidonDependenciesRule.JAVAX)); + IllegalArgumentException e = assertThrows(IllegalArgumentException.class, () -> checkNamespace("java")); + assertThat(e.getMessage(), + equalTo("The namespace 'java' is invalid. Only valid namespace names are: 'jakarta' and 'javax'.")); + } + +} diff --git a/pom.xml b/pom.xml index 5412a3f00..6a204b242 100644 --- a/pom.xml +++ b/pom.xml @@ -150,6 +150,7 @@ 3.5.0 0.13.1 3.6.2 + 3.4.0 3.1.1 3.3.0 1.4.1 @@ -183,7 +184,7 @@ 3.8.1 3.3.0 2.8.2 - 3.0.0-M1 + 3.4.0 3.0.0-M5 1.11.0 1.11.3 @@ -209,6 +210,7 @@ common + maven-enforcer-rules maven-plugins cli archetype @@ -615,6 +617,11 @@ maven-artifact ${version.lib.maven} + + org.apache.maven.enforcer + enforcer-api + ${version.lib.maven-enforcer} + org.apache.maven.plugin-tools maven-plugin-annotations