Repository: mojohaus/versions
Pull request #1222 (2025-03-09 07:50:50)

Removed numeric and mercury comparators, further refactoring
Third stage of the refactoring: removed the Numeric and Mercury comparators, some further refactoring in DefaultVersionsHelper.

Because we're removing comparators, a new version of rules.xsd is necessary and it's a breaking change.

andrzejj0: @slawekjaranowski would that be ok?

```
Versions - XML schemas for ruleSet
==================================

- [rule-3.0.0.xsd](rule-3.0.0.xsd)
- [rule-2.1.0.xsd](https://www.mojohaus.org/versions-maven-plugin/xsd/rule-2.1.0.xsd)
- [rule-2.0.0.xsd](https://www.mojohaus.org/versions-maven-plugin/xsd/rule-2.0.0.xsd)
```
slawekjaranowski: > @slawekjaranowski would that be ok?
> 
> ```
> Versions - XML schemas for ruleSet
> ==================================
> 
> - [rule-3.0.0.xsd](rule-3.0.0.xsd)
> - [rule-2.1.0.xsd](https://www.mojohaus.org/versions-maven-plugin/xsd/rule-2.1.0.xsd)
> - [rule-2.0.0.xsd](https://www.mojohaus.org/versions-maven-plugin/xsd/rule-2.0.0.xsd)
> ```

try to generate site locally ... 

we need an old link to works - https://www.mojohaus.org/versions/versions-model/xsd/rule-2.1.0.xsd
so we need a copy `rule-2.1.0.xsd` to `versions-model/src/site/resources/xsd`

andrzejj0: I will do the proper site in another PR...

Patch:
diff --git a/pom.xml b/pom.xml
index 1e4428f8..e42d5e75 100644
--- a/pom.xml
+++ b/pom.xml
@@ -135,7 +135,7 @@
     <sitePluginVersion>${maven-site-plugin.version}</sitePluginVersion>
     <sisuMavenPluginVersion>${sisu-maven-plugin.version}</sisuMavenPluginVersion>
     <fluidoVersion>${maven-fluido-skin.version}</fluidoVersion>
-    <modelloNamespaceRuleVersion>2.1.0</modelloNamespaceRuleVersion>
+    <modelloNamespaceRuleVersion>3.0.0</modelloNamespaceRuleVersion>
     <modelloNamespaceReportVersion>2.0.0</modelloNamespaceReportVersion>
     <byteBuddyVersion>1.17.2</byteBuddyVersion>
 
diff --git a/rules.xml b/rules.xml
index 0af6c982..12c294d2 100644
--- a/rules.xml
+++ b/rules.xml
@@ -1,1 +1,1 @@
-<ruleset comparisonMethod="maven"/>
\ No newline at end of file
+<ruleset/>
\ No newline at end of file
diff --git a/versions-common/pom.xml b/versions-common/pom.xml
index 0a8f28f2..3e3f02d1 100644
--- a/versions-common/pom.xml
+++ b/versions-common/pom.xml
@@ -85,7 +85,7 @@
     <dependency>
       <groupId>org.apache.maven.resolver</groupId>
       <artifactId>maven-resolver-util</artifactId>
-      <scope>test</scope>
+      <scope>provided</scope>
     </dependency>
     <dependency>
       <groupId>org.junit.jupiter</groupId>
diff --git a/versions-common/src/main/java/org/codehaus/mojo/versions/api/AbstractVersionDetails.java b/versions-common/src/main/java/org/codehaus/mojo/versions/api/AbstractVersionDetails.java
index fbbd3262..3b53b823 100644
--- a/versions-common/src/main/java/org/codehaus/mojo/versions/api/AbstractVersionDetails.java
+++ b/versions-common/src/main/java/org/codehaus/mojo/versions/api/AbstractVersionDetails.java
@@ -21,6 +21,7 @@ package org.codehaus.mojo.versions.api;
 
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.Comparator;
 import java.util.Iterator;
 import java.util.Objects;
 import java.util.Optional;
@@ -34,13 +35,14 @@ import org.apache.maven.artifact.versioning.ArtifactVersion;
 import org.apache.maven.artifact.versioning.Restriction;
 import org.apache.maven.artifact.versioning.VersionRange;
 import org.codehaus.mojo.versions.ordering.BoundArtifactVersion;
+import org.codehaus.mojo.versions.ordering.DefaultSegmentCounter;
 import org.codehaus.mojo.versions.ordering.InvalidSegmentException;
-import org.codehaus.mojo.versions.ordering.VersionComparator;
 import org.codehaus.mojo.versions.utils.ArtifactVersionService;
 
 import static java.util.Collections.reverseOrder;
 import static java.util.Optional.empty;
 import static java.util.Optional.of;
+import static java.util.Optional.ofNullable;
 import static org.codehaus.mojo.versions.api.Segment.MAJOR;
 import static org.codehaus.mojo.versions.api.Segment.SUBINCREMENTAL;
 
@@ -85,7 +87,7 @@ public abstract class AbstractVersionDetails implements VersionDetails {
         return getCurrentVersionRange().getRestrictions().stream()
                 .map(Restriction::getLowerBound)
                 .filter(Objects::nonNull)
-                .max(getVersionComparator())
+                .max(Comparator.naturalOrder())
                 .orElse(lowerBoundVersion);
     }
 
@@ -269,7 +271,7 @@ public abstract class AbstractVersionDetails implements VersionDetails {
         return Arrays.stream(getVersions(includeSnapshots))
                 .filter(candidate -> isVersionInRestriction(lookupRestriction, candidate))
                 .filter(candidate -> includeSnapshots || !ArtifactUtils.isSnapshot(candidate.toString()))
-                .max(getVersionComparator());
+                .max(Comparator.naturalOrder());
     }
 
     @Override
@@ -284,7 +286,7 @@ public abstract class AbstractVersionDetails implements VersionDetails {
                 .filter(candidate -> versionRange == null || ArtifactVersions.isVersionInRange(candidate, versionRange))
                 .filter(candidate -> restriction == null || isVersionInRestriction(restriction, candidate))
                 .filter(candidate -> includeSnapshots || !ArtifactUtils.isSnapshot(candidate.toString()))
-                .sorted(getVersionComparator())
+                .sorted(Comparator.naturalOrder())
                 .distinct()
                 .toArray(ArtifactVersion[]::new);
     }
@@ -345,7 +347,7 @@ public abstract class AbstractVersionDetails implements VersionDetails {
             return empty();
         }
 
-        int segmentCount = getVersionComparator().getSegmentCount(version);
+        int segmentCount = DefaultSegmentCounter.INSTANCE.getSegmentCount(version);
         if (unchangedSegment.get().value() > segmentCount) {
             throw new InvalidSegmentException(unchangedSegment.get(), segmentCount, version);
         }
@@ -382,9 +384,8 @@ public abstract class AbstractVersionDetails implements VersionDetails {
         ArtifactVersion upperBound = restriction.getUpperBound();
         boolean includeLower = restriction.isLowerBoundInclusive();
         boolean includeUpper = restriction.isUpperBoundInclusive();
-        final VersionComparator versionComparator = getVersionComparator();
-        int lower = lowerBound == null ? -1 : versionComparator.compare(lowerBound, candidate);
-        int upper = upperBound == null ? +1 : versionComparator.compare(upperBound, candidate);
+        int lower = ofNullable(lowerBound).map(b -> b.compareTo(candidate)).orElse(-1);
+        int upper = ofNullable(upperBound).map(b -> b.compareTo(candidate)).orElse(1);
         if (lower > 0 || upper < 0) {
             return false;
         }
@@ -401,7 +402,7 @@ public abstract class AbstractVersionDetails implements VersionDetails {
      */
     public final ArtifactVersion getReportNewestUpdate(Optional<Segment> updateScope, boolean includeSnapshots) {
         return getArtifactVersionStream(updateScope, includeSnapshots)
-                .min(Collections.reverseOrder(getVersionComparator()))
+                .min(Collections.reverseOrder())
                 .orElse(null);
     }
 
@@ -412,8 +413,8 @@ public abstract class AbstractVersionDetails implements VersionDetails {
      * @return all versions after currentVersion within the specified update scope.
      */
     public final ArtifactVersion[] getReportUpdates(Optional<Segment> updateScope, boolean includeSnapshots) {
-        TreeSet<ArtifactVersion> versions = getArtifactVersionStream(updateScope, includeSnapshots)
-                .collect(Collectors.toCollection(() -> new TreeSet<>(getVersionComparator())));
+        TreeSet<ArtifactVersion> versions =
+                getArtifactVersionStream(updateScope, includeSnapshots).collect(Collectors.toCollection(TreeSet::new));
         // filter out intermediate minor versions.
         if (!verboseDetail) {
             int major = 0;
diff --git a/versions-common/src/main/java/org/codehaus/mojo/versions/api/ArtifactVersions.java b/versions-common/src/main/java/org/codehaus/mojo/versions/api/ArtifactVersions.java
index cbc6d200..434d18f1 100644
--- a/versions-common/src/main/java/org/codehaus/mojo/versions/api/ArtifactVersions.java
+++ b/versions-common/src/main/java/org/codehaus/mojo/versions/api/ArtifactVersions.java
@@ -32,7 +32,6 @@ import org.apache.maven.artifact.ArtifactUtils;
 import org.apache.maven.artifact.versioning.ArtifactVersion;
 import org.apache.maven.artifact.versioning.Restriction;
 import org.apache.maven.artifact.versioning.VersionRange;
-import org.codehaus.mojo.versions.ordering.VersionComparator;
 
 /**
  * Holds the results of a search for versions of an artifact.
@@ -55,25 +54,16 @@ public class ArtifactVersions extends AbstractVersionDetails implements Comparab
      */
     private final SortedSet<ArtifactVersion> versions;
 
-    /**
-     * The version comparison rule that is used for this artifact.
-     *
-     * @since 1.0-alpha-3
-     */
-    private final VersionComparator versionComparator;
-
     /**
      * Creates a new {@link ArtifactVersions} instance.
      *
      * @param artifact          The artifact.
      * @param versions          The versions.
-     * @param versionComparator The version comparison rule.
      * @since 1.0-alpha-3
      */
-    public ArtifactVersions(Artifact artifact, List<ArtifactVersion> versions, VersionComparator versionComparator) {
+    public ArtifactVersions(Artifact artifact, List<ArtifactVersion> versions) {
         this.artifact = artifact;
-        this.versionComparator = versionComparator;
-        this.versions = new TreeSet<>(versionComparator);
+        this.versions = new TreeSet<>();
         this.versions.addAll(versions);
         setCurrentVersion(artifact.getVersion());
         setCurrentVersionRange(artifact.getVersionRange());
@@ -87,7 +77,6 @@ public class ArtifactVersions extends AbstractVersionDetails implements Comparab
      */
     public ArtifactVersions(ArtifactVersions other) {
         artifact = other.artifact;
-        versionComparator = other.versionComparator;
         versions = other.versions;
         setCurrentVersion(other.getCurrentVersion());
         setCurrentVersionRange(other.getCurrentVersionRange());
@@ -121,13 +110,12 @@ public class ArtifactVersions extends AbstractVersionDetails implements Comparab
                 .append(getArtifact().getVersion(), that.getArtifact().getVersion())
                 .append(getArtifact().getScope(), that.getArtifact().getScope())
                 .append(getVersions(true), that.getVersions(true))
-                .append(getVersionComparator(), that.getVersionComparator())
                 .isEquals();
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(getArtifact(), Arrays.hashCode(getVersions(true)), getVersionComparator());
+        return Objects.hash(getArtifact(), Arrays.hashCode(getVersions(true)));
     }
 
     /**
@@ -230,15 +218,10 @@ public class ArtifactVersions extends AbstractVersionDetails implements Comparab
                 : versions.stream().map(Object::toString).allMatch(ArtifactUtils::isSnapshot);
     }
 
-    public VersionComparator getVersionComparator() {
-        return versionComparator;
-    }
-
     /**
      * {@inheritDoc}
      */
     public String toString() {
-        return "ArtifactVersions" + "{artifact=" + artifact + ", versions=" + versions + ", versionComparator="
-                + versionComparator + '}';
+        return "ArtifactVersions" + "{artifact=" + artifact + ", versions=" + versions + '}';
     }
 }
diff --git a/versions-common/src/main/java/org/codehaus/mojo/versions/api/DefaultVersionsHelper.java b/versions-common/src/main/java/org/codehaus/mojo/versions/api/DefaultVersionsHelper.java
index 48d0f0ac..5d18238a 100644
--- a/versions-common/src/main/java/org/codehaus/mojo/versions/api/DefaultVersionsHelper.java
+++ b/versions-common/src/main/java/org/codehaus/mojo/versions/api/DefaultVersionsHelper.java
@@ -19,21 +19,11 @@ package org.codehaus.mojo.versions.api;
  * under the License.
  */
 
-import javax.xml.stream.XMLStreamException;
-
-import java.io.BufferedInputStream;
 import java.io.IOException;
-import java.io.InputStream;
-import java.net.URISyntaxException;
-import java.net.URL;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
-import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
@@ -41,12 +31,10 @@ import java.util.Optional;
 import java.util.Set;
 import java.util.TreeMap;
 import java.util.TreeSet;
-import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.Future;
-import java.util.regex.Pattern;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
@@ -62,30 +50,17 @@ import org.apache.maven.artifact.versioning.VersionRange;
 import org.apache.maven.execution.MavenSession;
 import org.apache.maven.model.Dependency;
 import org.apache.maven.model.Plugin;
-import org.apache.maven.plugin.MojoExecution;
 import org.apache.maven.plugin.MojoExecutionException;
 import org.apache.maven.plugin.logging.Log;
 import org.apache.maven.project.MavenProject;
-import org.apache.maven.wagon.Wagon;
-import org.apache.maven.wagon.authentication.AuthenticationInfo;
-import org.apache.maven.wagon.observers.Debug;
-import org.apache.maven.wagon.proxy.ProxyInfo;
 import org.codehaus.mojo.versions.model.IgnoreVersion;
-import org.codehaus.mojo.versions.model.Rule;
-import org.codehaus.mojo.versions.model.RuleSet;
-import org.codehaus.mojo.versions.model.io.stax.RuleStaxReader;
-import org.codehaus.mojo.versions.ordering.VersionComparator;
-import org.codehaus.mojo.versions.ordering.VersionComparators;
+import org.codehaus.mojo.versions.rule.RuleService;
 import org.codehaus.mojo.versions.utils.ArtifactFactory;
 import org.codehaus.mojo.versions.utils.ArtifactVersionService;
 import org.codehaus.mojo.versions.utils.DependencyComparator;
 import org.codehaus.mojo.versions.utils.PluginComparator;
-import org.codehaus.mojo.versions.utils.RegexUtils;
-import org.codehaus.mojo.versions.utils.VersionsExpressionEvaluator;
 import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluationException;
-import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluator;
 import org.eclipse.aether.RepositorySystem;
-import org.eclipse.aether.repository.AuthenticationContext;
 import org.eclipse.aether.repository.RemoteRepository;
 import org.eclipse.aether.repository.RepositoryPolicy;
 import org.eclipse.aether.resolution.ArtifactRequest;
@@ -93,10 +68,7 @@ import org.eclipse.aether.resolution.ArtifactResult;
 import org.eclipse.aether.resolution.VersionRangeRequest;
 import org.eclipse.aether.resolution.VersionRangeResolutionException;
 
-import static java.util.Collections.emptyList;
-import static java.util.Collections.singletonList;
 import static java.util.Objects.requireNonNull;
-import static java.util.Optional.empty;
 import static java.util.Optional.of;
 import static java.util.Optional.ofNullable;
 import static org.apache.maven.RepositoryUtils.toArtifact;
@@ -109,21 +81,10 @@ import static org.apache.maven.RepositoryUtils.toArtifact;
  * @since 1.0-alpha-3
  */
 public class DefaultVersionsHelper implements VersionsHelper {
-    private static final String CLASSPATH_PROTOCOL = "classpath";
 
     private static final int LOOKUP_PARALLEL_THREADS = 5;
 
-    // for testing purpose
-    RuleSet getRuleSet() {
-        return ruleSet;
-    }
-
-    /**
-     * The artifact comparison rules to use.
-     *
-     * @since 1.0-alpha-3
-     */
-    private RuleSet ruleSet;
+    private final RuleService ruleService;
 
     private final ArtifactFactory artifactFactory;
 
@@ -143,21 +104,9 @@ public class DefaultVersionsHelper implements VersionsHelper {
      */
     private final MavenSession mavenSession;
 
-    private final MojoExecution mojoExecution;
-
-    /**
-     * A cache mapping artifacts to their best fitting rule, since looking up
-     * this information can be quite costly.
-     *
-     * @since 2.12
-     */
-    private final Map<String, Rule> artifactBestFitRule = new ConcurrentHashMap<>();
-
-    private final List<RemoteRepository> remoteProjectRepositories;
-
     private final List<RemoteRepository> remotePluginRepositories;
 
-    private final List<RemoteRepository> remoteRepositories;
+    private final List<RemoteRepository> remoteProjectRepositories;
 
     private final PomHelper pomHelper;
 
@@ -169,82 +118,50 @@ public class DefaultVersionsHelper implements VersionsHelper {
             ArtifactFactory artifactFactory,
             RepositorySystem repositorySystem,
             MavenSession mavenSession,
-            MojoExecution mojoExecution,
+            RuleService ruleService,
             Log log) {
         this.pomHelper = requireNonNull(pomHelper);
         this.artifactFactory = requireNonNull(artifactFactory);
-        this.repositorySystem = repositorySystem;
-        this.mavenSession = mavenSession;
-        this.mojoExecution = mojoExecution;
+        this.repositorySystem = requireNonNull(repositorySystem);
+        this.mavenSession = requireNonNull(mavenSession);
+        this.ruleService = requireNonNull(ruleService);
         this.log = requireNonNull(log);
 
-        this.remoteProjectRepositories = Optional.ofNullable(mavenSession)
+        this.remoteProjectRepositories = of(mavenSession)
                 .map(MavenSession::getCurrentProject)
                 .map(MavenProject::getRemoteProjectRepositories)
-                .map(DefaultVersionsHelper::adjustRemoteRepositoriesRefreshPolicy)
+                .map(DefaultVersionsHelper::forceDailyRemoteRepositoriesRefreshPolicy)
                 .orElseGet(Collections::emptyList);
 
-        this.remotePluginRepositories = Optional.ofNullable(mavenSession)
+        this.remotePluginRepositories = of(mavenSession)
                 .map(MavenSession::getCurrentProject)
                 .map(MavenProject::getRemotePluginRepositories)
-                .map(DefaultVersionsHelper::adjustRemoteRepositoriesRefreshPolicy)
+                .map(DefaultVersionsHelper::forceDailyRemoteRepositoriesRefreshPolicy)
                 .orElseGet(Collections::emptyList);
-
-        this.remoteRepositories = Stream.concat(remoteProjectRepositories.stream(), remotePluginRepositories.stream())
-                .distinct()
-                .collect(Collectors.toList());
     }
 
-    static List<RemoteRepository> adjustRemoteRepositoriesRefreshPolicy(List<RemoteRepository> remoteRepositories) {
+    static List<RemoteRepository> forceDailyRemoteRepositoriesRefreshPolicy(List<RemoteRepository> remoteRepositories) {
         return remoteRepositories.stream()
-                .map(DefaultVersionsHelper::adjustRemoteRepositoryRefreshPolicy)
+                .map(remoteRepository -> {
+                    RepositoryPolicy snapshotPolicy = forceDailyUpdatePolicy(remoteRepository.getPolicy(true));
+                    RepositoryPolicy releasePolicy = forceDailyUpdatePolicy(remoteRepository.getPolicy(false));
+                    if (snapshotPolicy != null || releasePolicy != null) {
+                        RemoteRepository.Builder builder = new RemoteRepository.Builder(remoteRepository);
+                        Optional.ofNullable(snapshotPolicy).ifPresent(builder::setSnapshotPolicy);
+                        Optional.ofNullable(releasePolicy).ifPresent(builder::setReleasePolicy);
+                        return builder.build();
+                    } else {
+                        return remoteRepository;
+                    }
+                })
                 .collect(Collectors.toList());
     }
 
-    static RemoteRepository adjustRemoteRepositoryRefreshPolicy(RemoteRepository remoteRepository) {
-        RepositoryPolicy snapshotPolicy = remoteRepository.getPolicy(true);
-        RepositoryPolicy releasePolicy = remoteRepository.getPolicy(false);
-
-        RepositoryPolicy newSnapshotPolicy = null;
-        RepositoryPolicy newReleasePolicy = null;
-
-        if (snapshotPolicy.isEnabled()
-                && RepositoryPolicy.UPDATE_POLICY_NEVER.equals(snapshotPolicy.getUpdatePolicy())) {
-            newSnapshotPolicy = new RepositoryPolicy(
-                    true, RepositoryPolicy.UPDATE_POLICY_DAILY, snapshotPolicy.getChecksumPolicy());
-        }
-
-        if (releasePolicy.isEnabled() && RepositoryPolicy.UPDATE_POLICY_NEVER.equals(releasePolicy.getUpdatePolicy())) {
-            newReleasePolicy =
-                    new RepositoryPolicy(true, RepositoryPolicy.UPDATE_POLICY_DAILY, releasePolicy.getChecksumPolicy());
-        }
-
-        if (newSnapshotPolicy != null || newReleasePolicy != null) {
-            RemoteRepository.Builder builder = new RemoteRepository.Builder(remoteRepository);
-            if (newSnapshotPolicy != null) {
-                builder.setSnapshotPolicy(newSnapshotPolicy);
-            }
-            if (newReleasePolicy != null) {
-                builder.setReleasePolicy(newReleasePolicy);
-            }
-            return builder.build();
-        } else {
-            return remoteRepository;
+    private static RepositoryPolicy forceDailyUpdatePolicy(RepositoryPolicy policy) {
+        if (policy.isEnabled() && RepositoryPolicy.UPDATE_POLICY_NEVER.equals(policy.getUpdatePolicy())) {
+            return new RepositoryPolicy(true, RepositoryPolicy.UPDATE_POLICY_DAILY, policy.getChecksumPolicy());
         }
-    }
-
-    static boolean exactMatch(String wildcardRule, String value) {
-        Pattern p = Pattern.compile(RegexUtils.convertWildcardsToRegex(wildcardRule, true));
-        return p.matcher(value).matches();
-    }
-
-    static boolean match(String wildcardRule, String value) {
-        Pattern p = Pattern.compile(RegexUtils.convertWildcardsToRegex(wildcardRule, false));
-        return p.matcher(value).matches();
-    }
-
-    static boolean isClasspathUri(String uri) {
-        return (uri != null && uri.startsWith(CLASSPATH_PROTOCOL + ":"));
+        return null;
     }
 
     @Override
@@ -259,38 +176,28 @@ public class DefaultVersionsHelper implements VersionsHelper {
             Artifact artifact, VersionRange versionRange, boolean usePluginRepositories, boolean useProjectRepositories)
             throws VersionRetrievalException {
         try {
-            Collection<IgnoreVersion> ignoredVersions = getIgnoredVersions(artifact);
+            Collection<IgnoreVersion> ignoredVersions = ruleService.getIgnoredVersions(artifact);
             if (!ignoredVersions.isEmpty() && log.isDebugEnabled()) {
                 log.debug("Found ignored versions: " + ignoredVersions + " for artifact" + artifact);
             }
 
-            final List<RemoteRepository> repositories;
-            if (usePluginRepositories && !useProjectRepositories) {
-                repositories = remotePluginRepositories;
-            } else if (!usePluginRepositories && useProjectRepositories) {
-                repositories = remoteProjectRepositories;
-            } else if (usePluginRepositories) {
-                repositories = remoteRepositories;
-            } else {
-                // testing?
-                repositories = emptyList();
-            }
+            VersionRangeRequest versionRangeRequest = new VersionRangeRequest(
+                    toArtifact(artifact)
+                            .setVersion(ofNullable(versionRange)
+                                    .map(VersionRange::getRestrictions)
+                                    .flatMap(list -> list.stream().findFirst().map(Restriction::toString))
+                                    .orElse("(,)")),
+                    Stream.concat(
+                                    usePluginRepositories ? remotePluginRepositories.stream() : Stream.empty(),
+                                    useProjectRepositories ? remoteProjectRepositories.stream() : Stream.empty())
+                            .distinct()
+                            .collect(Collectors.toList()),
+                    "lookupArtifactVersions");
 
             return new ArtifactVersions(
                     artifact,
                     repositorySystem
-                            .resolveVersionRange(
-                                    mavenSession.getRepositorySession(),
-                                    new VersionRangeRequest(
-                                            toArtifact(artifact)
-                                                    .setVersion(ofNullable(versionRange)
-                                                            .map(VersionRange::getRestrictions)
-                                                            .flatMap(list -> list.stream()
-                                                                    .findFirst()
-                                                                    .map(Restriction::toString))
-                                                            .orElse("(,)")),
-                                            repositories,
-                                            "lookupArtifactVersions"))
+                            .resolveVersionRange(mavenSession.getRepositorySession(), versionRangeRequest)
                             .getVersions()
                             .stream()
                             .filter(v -> ignoredVersions.stream().noneMatch(i -> {
@@ -307,8 +214,7 @@ public class DefaultVersionsHelper implements VersionsHelper {
                                 return false;
                             }))
                             .map(v -> ArtifactVersionService.getArtifactVersion(v.toString()))
-                            .collect(Collectors.toList()),
-                    getVersionComparator(artifact));
+                            .collect(Collectors.toList()));
         } catch (VersionRangeResolutionException e) {
             throw new VersionRetrievalException(e.getMessage(), e);
         }
@@ -320,41 +226,6 @@ public class DefaultVersionsHelper implements VersionsHelper {
         return lookupArtifactVersions(artifact, null, usePluginRepositories);
     }
 
-    /**
-     * Returns a list of versions which should not be considered when looking for updates.
-     *
-     * @param artifact The artifact
-     * @return List of ignored version
-     */
-    private List<IgnoreVersion> getIgnoredVersions(Artifact artifact) {
-        final List<IgnoreVersion> ret = new ArrayList<>();
-
-        for (final IgnoreVersion ignoreVersion : ruleSet.getIgnoreVersions()) {
-            if (IgnoreVersionHelper.isValidType(ignoreVersion)) {
-                ret.add(ignoreVersion);
-            } else {
-                log.warn("The type attribute '" + ignoreVersion.getType() + "' for global ignoreVersion["
-                        + ignoreVersion + "] is not valid. Please use one of '" + IgnoreVersionHelper.VALID_TYPES
-                        + "'.");
-            }
-        }
-
-        final Rule rule = getBestFitRule(artifact.getGroupId(), artifact.getArtifactId());
-
-        if (rule != null) {
-            for (IgnoreVersion ignoreVersion : rule.getIgnoreVersions()) {
-                if (IgnoreVersionHelper.isValidType(ignoreVersion)) {
-                    ret.add(ignoreVersion);
-                } else {
-                    log.warn("The type attribute '" + ignoreVersion.getType() + "' for " + rule + " is not valid."
-                            + " Please use one of '" + IgnoreVersionHelper.VALID_TYPES + "'.");
-                }
-            }
-        }
-
-        return ret;
-    }
-
     @Override
     public void resolveArtifact(Artifact artifact, boolean usePluginRepositories) throws ArtifactResolutionException {
         try {
@@ -374,79 +245,6 @@ public class DefaultVersionsHelper implements VersionsHelper {
         }
     }
 
-    @Override
-    public VersionComparator getVersionComparator(Artifact artifact) {
-        return getVersionComparator(artifact.getGroupId(), artifact.getArtifactId());
-    }
-
-    @Override
-    public VersionComparator getVersionComparator(String groupId, String artifactId) {
-        Rule rule = getBestFitRule(groupId, artifactId);
-        final String comparisonMethod = rule == null ? ruleSet.getComparisonMethod() : rule.getComparisonMethod();
-        return VersionComparators.getVersionComparator(comparisonMethod);
-    }
-
-    /**
-     * Find the rule, if any, which best fits the artifact details given.
-     *
-     * @param groupId    Group id of the artifact
-     * @param artifactId Artifact id of the artifact
-     * @return Rule which best describes the given artifact
-     */
-    protected Rule getBestFitRule(String groupId, String artifactId) {
-        String groupArtifactId = groupId + ':' + artifactId;
-        if (artifactBestFitRule.containsKey(groupArtifactId)) {
-            return artifactBestFitRule.get(groupArtifactId);
-        }
-
-        Rule bestFit = null;
-        final List<Rule> rules = ruleSet.getRules();
-        int bestGroupIdScore = Integer.MAX_VALUE;
-        int bestArtifactIdScore = Integer.MAX_VALUE;
-        boolean exactGroupId = false;
-        boolean exactArtifactId = false;
-        for (Rule rule : rules) {
-            int groupIdScore = RegexUtils.getWildcardScore(rule.getGroupId());
-            if (groupIdScore > bestGroupIdScore) {
-                continue;
-            }
-            boolean exactMatch = exactMatch(rule.getGroupId(), groupId);
-            boolean match = exactMatch || match(rule.getGroupId(), groupId);
-            if (!match || (exactGroupId && !exactMatch)) {
-                continue;
-            }
-            if (bestGroupIdScore > groupIdScore) {
-                bestArtifactIdScore = Integer.MAX_VALUE;
-                exactArtifactId = false;
-            }
-            bestGroupIdScore = groupIdScore;
-            if (exactMatch && !exactGroupId) {
-                exactGroupId = true;
-                bestArtifactIdScore = Integer.MAX_VALUE;
-                exactArtifactId = false;
-            }
-            int artifactIdScore = RegexUtils.getWildcardScore(rule.getArtifactId());
-            if (artifactIdScore > bestArtifactIdScore) {
-                continue;
-            }
-            exactMatch = exactMatch(rule.getArtifactId(), artifactId);
-            match = exactMatch || match(rule.getArtifactId(), artifactId);
-            if (!match || (exactArtifactId && !exactMatch)) {
-                continue;
-            }
-            bestArtifactIdScore = artifactIdScore;
-            if (exactMatch && !exactArtifactId) {
-                exactArtifactId = true;
-            }
-            bestFit = rule;
-        }
-
-        if (bestFit != null) {
-            artifactBestFitRule.put(groupArtifactId, bestFit);
-        }
-        return bestFit;
-    }
-
     public Map<Dependency, ArtifactVersions> lookupDependenciesUpdates(
             Stream<Dependency> dependencies,
             boolean usePluginRepositories,
@@ -494,8 +292,7 @@ public class DefaultVersionsHelper implements VersionsHelper {
                 artifactFactory.createArtifact(dependency), null, usePluginRepositories, useProjectRepositories);
         return new ArtifactVersions(
                 allVersions.getArtifact(),
-                Arrays.stream(allVersions.getAllUpdates(allowSnapshots)).collect(Collectors.toList()),
-                allVersions.getVersionComparator());
+                Arrays.stream(allVersions.getAllUpdates(allowSnapshots)).collect(Collectors.toList()));
     }
 
     @Override
@@ -537,16 +334,10 @@ public class DefaultVersionsHelper implements VersionsHelper {
                 artifactFactory.createMavenPluginArtifact(plugin.getGroupId(), plugin.getArtifactId(), version), true);
         ArtifactVersions updatedVersions = new ArtifactVersions(
                 allVersions.getArtifact(),
-                Arrays.stream(allVersions.getAllUpdates(allowSnapshots)).collect(Collectors.toList()),
-                allVersions.getVersionComparator());
+                Arrays.stream(allVersions.getAllUpdates(allowSnapshots)).collect(Collectors.toList()));
         return new PluginUpdatesDetails(updatedVersions, pluginDependencyDetails, allowSnapshots);
     }
 
-    @Override
-    public ExpressionEvaluator getExpressionEvaluator(MavenProject project) {
-        return new VersionsExpressionEvaluator(mavenSession, mojoExecution);
-    }
-
     @Override
     public Map<Property, PropertyVersions> getVersionPropertiesMap(VersionPropertiesMapRequest request)
             throws MojoExecutionException {
@@ -582,76 +373,85 @@ public class DefaultVersionsHelper implements VersionsHelper {
             }
         }
 
-        List<String> includePropertiesList = request.getIncludeProperties() != null
-                ? Arrays.asList(request.getIncludeProperties().split("\\s*,\\s*"))
-                : Collections.emptyList();
-        List<String> excludePropertiesList = request.getExcludeProperties() != null
-                ? Arrays.asList(request.getExcludeProperties().split("\\s*,\\s*"))
-                : Collections.emptyList();
-
-        log.debug("Searching for properties associated with builders");
-        Iterator<Property> i = properties.values().iterator();
-        while (i.hasNext()) {
-            Property property = i.next();
-
-            log.debug("includePropertiesList:" + includePropertiesList + " property: " + property.getName());
-            log.debug("excludePropertiesList:" + excludePropertiesList + " property: " + property.getName());
-            if (!includePropertiesList.isEmpty() && !includePropertiesList.contains(property.getName())) {
-                log.debug("Skipping property ${" + property.getName() + "}");
-                i.remove();
-            } else if (!excludePropertiesList.isEmpty() && excludePropertiesList.contains(property.getName())) {
-                log.debug("Ignoring property ${" + property.getName() + "}");
-                i.remove();
+        // Create include and exclude properties lists
+        List<String> includePropertiesList = Optional.ofNullable(request.getIncludeProperties())
+                .map(s -> Arrays.asList(s.split("\\s*,\\s*")))
+                .orElse(Collections.emptyList());
+
+        List<String> excludePropertiesList = Optional.ofNullable(request.getExcludeProperties())
+                .map(s -> Arrays.asList(s.split("\\s*,\\s*")))
+                .orElse(Collections.emptyList());
+
+        // Filter properties based on include/exclude lists
+        properties.values().removeIf(property -> {
+            String name = property.getName();
+            if (!includePropertiesList.isEmpty() && !includePropertiesList.contains(name)) {
+                log.debug("Skipping property ${" + name + "}");
+                return true;
             }
-        }
-        i = properties.values().iterator();
+            if (excludePropertiesList.contains(name)) {
+                log.debug("Ignoring property ${" + name + "}");
+                return true;
+            }
+            return false;
+        });
+
+        log.debug("Processing properties to build PropertyVersions");
         Map<Property, PropertyVersions> propertyVersions = new LinkedHashMap<>(properties.size());
-        while (i.hasNext()) {
-            Property property = i.next();
-            log.debug("Property ${" + property.getName() + "}");
-            PropertyVersionsBuilder builder = builders.get(property.getName());
+
+        for (Property property : properties.values()) {
+            String propertyName = property.getName();
+            log.debug("Property ${" + propertyName + "}");
+
+            PropertyVersionsBuilder builder = builders.get(propertyName);
             if (builder == null || !builder.isAssociated()) {
-                log.debug("Property ${" + property.getName() + "}: Looks like this property is not "
-                        + "associated with any dependency...");
-                builder = new PropertyVersionsBuilder(this, null, property.getName(), log);
+                log.debug(String.format("Property ${%s}: Not associated with any dependency.", propertyName));
+                builder = new PropertyVersionsBuilder(this, null, propertyName, log);
             }
+
             if (!property.isAutoLinkDependencies()) {
-                log.debug("Property ${" + property.getName() + "}: Removing any autoLinkDependencies");
+                log.debug(String.format("Property ${%s}: Clearing auto-link dependencies", propertyName));
                 builder.clearAssociations();
             }
+
+            // Add specified dependencies to the builder
             Dependency[] dependencies = property.getDependencies();
             if (dependencies != null) {
                 for (Dependency dependency : dependencies) {
-                    log.debug("Property ${" + property.getName() + "}: Adding association to " + dependency);
+                    log.debug(String.format("Property ${%s}: Adding association to %s", propertyName, dependency));
                     builder.withAssociation(artifactFactory.createArtifact(dependency), false);
                 }
             }
+
             try {
+                String currentVersion =
+                        request.getMavenProject().getProperties().getProperty(propertyName);
+                property.setValue(currentVersion);
+
                 if (property.isAutoLinkDependencies()
                         && StringUtils.isEmpty(property.getVersion())
                         && !StringUtils.isEmpty(builder.getVersionRange())) {
-                    log.debug("Property ${" + property.getName() + "}: Adding inferred version range of "
-                            + builder.getVersionRange());
+                    log.debug(String.format(
+                            "Property ${%s}: Adding inferred version range of %s",
+                            propertyName, builder.getVersionRange()));
                     property.setVersion(builder.getVersionRange());
                 }
-                final String currentVersion =
-                        request.getMavenProject().getProperties().getProperty(property.getName());
-                property.setValue(currentVersion);
-                final PropertyVersions versions;
-                try {
-                    if (currentVersion != null) {
-                        builder.withCurrentVersion(ArtifactVersionService.getArtifactVersion(currentVersion))
-                                .withCurrentVersionRange(VersionRange.createFromVersionSpec(currentVersion));
-                    }
-                } catch (InvalidVersionSpecificationException e) {
-                    throw new RuntimeException(e);
+
+                if (currentVersion != null) {
+                    builder.withCurrentVersion(ArtifactVersionService.getArtifactVersion(currentVersion))
+                            .withCurrentVersionRange(VersionRange.createFromVersionSpec(currentVersion));
                 }
-                versions = builder.build();
+
+                PropertyVersions versions = builder.build();
                 propertyVersions.put(property, versions);
+
             } catch (VersionRetrievalException e) {
                 throw new MojoExecutionException(e.getMessage(), e);
+            } catch (InvalidVersionSpecificationException e) {
+                throw new RuntimeException(e);
             }
         }
+
         return propertyVersions;
     }
 
@@ -659,245 +459,20 @@ public class DefaultVersionsHelper implements VersionsHelper {
      * Builder class for {@linkplain DefaultVersionsHelper}
      */
     public static class Builder {
-        private Collection<String> ignoredVersions;
-
-        private RuleSet ruleSet;
-
-        private String serverId;
-
-        private String rulesUri;
-
         private Log log;
 
         private MavenSession mavenSession;
 
-        private MojoExecution mojoExecution;
-
         private RepositorySystem repositorySystem;
 
+        private RuleService ruleService;
+
         private ArtifactFactory artifactFactory;
 
         private PomHelper pomHelper;
 
-        private Map<String, Wagon> wagonMap;
-
         public Builder() {}
 
-        private static RuleSet getRulesFromClasspath(String uri, Log logger) throws MojoExecutionException {
-            logger.debug("Going to load rules from \"" + uri + "\"");
-            String choppedUrl = uri.substring(CLASSPATH_PROTOCOL.length() + 3);
-            URL url = DefaultVersionsHelper.class.getResource(choppedUrl);
-            if (url == null) {
-                throw new MojoExecutionException("Resource \"" + uri + "\" not found in classpath.");
-            }
-
-            try (BufferedInputStream bis = new BufferedInputStream(url.openStream())) {
-                RuleSet result = new RuleStaxReader().read(bis);
-                logger.debug("Loaded rules from \"" + uri + "\" successfully");
-                return result;
-            } catch (IOException | XMLStreamException e) {
-                throw new MojoExecutionException("Could not load specified rules from " + uri, e);
-            }
-        }
-
-        /**
-         * <p>Creates the enriched version of the ruleSet given as parameter; the ruleSet will contain the
-         * set of ignored versions passed on top of its own (if defined).</p>
-         *
-         * <p>If the {@code originalRuleSet} is {@code null}, a new {@linkplain RuleSet} will be created as
-         * a result.</p>
-         *
-         * <p><em>The method does not change the {@code originalRuleSet} object.</em></p>
-         *
-         * @param ignoredVersions collection of ignored version to enrich the clone of the original rule set
-         * @param originalRuleSet original rule set
-         * @return new RuleSet object containing the (if passed) cloned version of the rule set, enriched with
-         *         the given set of ignored versions
-         */
-        @SuppressWarnings("checkstyle:AvoidNestedBlocks")
-        private static RuleSet enrichRuleSet(Collection<String> ignoredVersions, RuleSet originalRuleSet) {
-            RuleSet ruleSet = new RuleSet();
-            if (originalRuleSet != null) {
-                ruleSet.setComparisonMethod(originalRuleSet.getComparisonMethod());
-                if (originalRuleSet.getRules() != null) {
-                    ruleSet.setRules(new ArrayList<>(originalRuleSet.getRules()));
-                }
-                if (originalRuleSet.getIgnoreVersions() != null) {
-                    ruleSet.setIgnoreVersions(new ArrayList<>(originalRuleSet.getIgnoreVersions()));
-                }
-            }
-
-            if (ruleSet.getIgnoreVersions() == null) {
-                ruleSet.setIgnoreVersions(new ArrayList<>());
-            }
-            ruleSet.getIgnoreVersions()
-                    .addAll(ignoredVersions.stream()
-                            .map(v -> {
-                                IgnoreVersion ignoreVersion = new IgnoreVersion();
-                                ignoreVersion.setType(IgnoreVersion.TYPE_REGEX);
-                                ignoreVersion.setVersion(v);
-                                return ignoreVersion;
-                            })
-                            .collect(Collectors.toList()));
-
-            return ruleSet;
-        }
-
-        private static class RulesUri {
-            String basePath;
-            String resource;
-
-            private RulesUri(String basePath, String resource) {
-                this.basePath = basePath;
-                this.resource = resource;
-            }
-
-            static RulesUri build(String rulesUri) throws URISyntaxException {
-                int split = rulesUri.lastIndexOf('/');
-                return split == -1
-                        ? new RulesUri(rulesUri, "")
-                        : new RulesUri(
-                                rulesUri.substring(0, split) + '/',
-                                split + 1 < rulesUri.length() ? rulesUri.substring(split + 1) : "");
-            }
-        }
-
-        private RemoteRepository remoteRepository(RulesUri uri) {
-            RemoteRepository prototype = new RemoteRepository.Builder(serverId, null, uri.basePath).build();
-            RemoteRepository.Builder builder = new RemoteRepository.Builder(prototype);
-            ofNullable(mavenSession.getRepositorySession().getProxySelector().getProxy(prototype))
-                    .ifPresent(builder::setProxy);
-            ofNullable(mavenSession
-                            .getRepositorySession()
-                            .getAuthenticationSelector()
-                            .getAuthentication(prototype))
-                    .ifPresent(builder::setAuthentication);
-            ofNullable(mavenSession.getRepositorySession().getMirrorSelector().getMirror(prototype))
-                    .ifPresent(mirror -> builder.setMirroredRepositories(singletonList(mirror)));
-            return builder.build();
-        }
-
-        private Optional<ProxyInfo> getProxyInfo(RemoteRepository repository) {
-            return ofNullable(repository.getProxy()).map(proxy -> new ProxyInfo() {
-                {
-                    setHost(proxy.getHost());
-                    setPort(proxy.getPort());
-                    setType(proxy.getType());
-                    ofNullable(proxy.getAuthentication()).ifPresent(auth -> {
-                        try (AuthenticationContext authCtx =
-                                AuthenticationContext.forProxy(mavenSession.getRepositorySession(), repository)) {
-                            ofNullable(authCtx.get(AuthenticationContext.USERNAME))
-                                    .ifPresent(this::setUserName);
-                            ofNullable(authCtx.get(AuthenticationContext.PASSWORD))
-                                    .ifPresent(this::setPassword);
-                            ofNullable(authCtx.get(AuthenticationContext.NTLM_DOMAIN))
-                                    .ifPresent(this::setNtlmDomain);
-                            ofNullable(authCtx.get(AuthenticationContext.NTLM_WORKSTATION))
-                                    .ifPresent(this::setNtlmHost);
-                        }
-                    });
-                }
-            });
-        }
-
-        private Optional<AuthenticationInfo> getAuthenticationInfo(RemoteRepository repository) {
-            return ofNullable(repository.getAuthentication()).map(authentication -> new AuthenticationInfo() {
-                {
-                    try (AuthenticationContext authCtx =
-                            AuthenticationContext.forRepository(mavenSession.getRepositorySession(), repository)) {
-                        ofNullable(authCtx.get(AuthenticationContext.USERNAME)).ifPresent(this::setUserName);
-                        ofNullable(authCtx.get(AuthenticationContext.PASSWORD)).ifPresent(this::setPassword);
-                        ofNullable(authCtx.get(AuthenticationContext.PRIVATE_KEY_PASSPHRASE))
-                                .ifPresent(this::setPassphrase);
-                        ofNullable(authCtx.get(AuthenticationContext.PRIVATE_KEY_PATH))
-                                .ifPresent(this::setPrivateKey);
-                    }
-                }
-            });
-        }
-
-        private org.apache.maven.wagon.repository.Repository wagonRepository(RemoteRepository repository) {
-            return new org.apache.maven.wagon.repository.Repository(repository.getId(), repository.getUrl());
-        }
-
-        private RuleSet getRulesUsingWagon() throws MojoExecutionException {
-            RulesUri uri;
-            try {
-                uri = RulesUri.build(rulesUri);
-            } catch (URISyntaxException e) {
-                log.warn("Invalid rulesUri protocol: " + e.getMessage());
-                return null;
-            }
-
-            RemoteRepository repository = remoteRepository(uri);
-            return ofNullable(wagonMap.get(repository.getProtocol()))
-                    .map(wagon -> {
-                        if (log.isDebugEnabled()) {
-                            Debug debug = new Debug();
-                            wagon.addSessionListener(debug);
-                            wagon.addTransferListener(debug);
-                        }
-
-                        try {
-                            Optional<ProxyInfo> proxyInfo = getProxyInfo(repository);
-                            Optional<AuthenticationInfo> authenticationInfo = getAuthenticationInfo(repository);
-                            if (log.isDebugEnabled()) {
-                                log.debug("Connecting to remote repository \"" + repository.getId() + "\""
-                                        + proxyInfo
-                                                .map(pi -> " using proxy " + pi.getHost() + ":" + pi.getPort())
-                                                .orElse("")
-                                        + authenticationInfo
-                                                .map(ai -> " as " + ai.getUserName())
-                                                .orElse(""));
-                            }
-                            wagon.connect(
-                                    wagonRepository(repository),
-                                    getAuthenticationInfo(repository).orElse(null),
-                                    getProxyInfo(repository).orElse(null));
-                            try {
-                                Path tempFile = Files.createTempFile("rules-", ".xml");
-                                wagon.get(uri.resource, tempFile.toFile());
-                                try (InputStream is = Files.newInputStream(tempFile)) {
-                                    return new RuleStaxReader().read(is);
-                                } finally {
-                                    Files.deleteIfExists(tempFile);
-                                }
-
-                            } finally {
-                                wagon.disconnect();
-                            }
-                        } catch (Exception e) {
-                            throw new RuntimeException(e);
-                        }
-                    })
-                    .orElseThrow(() -> new MojoExecutionException("Could not load specified rules from " + rulesUri));
-        }
-
-        public static Optional<String> protocol(final String url) {
-            int pos = url.indexOf(":");
-            return pos == -1 ? empty() : of(url.substring(0, pos).trim());
-        }
-
-        public Builder withIgnoredVersions(Collection<String> ignoredVersions) {
-            this.ignoredVersions = ignoredVersions;
-            return this;
-        }
-
-        public Builder withRuleSet(RuleSet ruleSet) {
-            this.ruleSet = ruleSet;
-            return this;
-        }
-
-        public Builder withServerId(String serverId) {
-            this.serverId = serverId;
-            return this;
-        }
-
-        public Builder withRulesUri(String rulesUri) {
-            this.rulesUri = rulesUri;
-            return this;
-        }
-
         public Builder withLog(Log log) {
             this.log = log;
             return this;
@@ -908,18 +483,13 @@ public class DefaultVersionsHelper implements VersionsHelper {
             return this;
         }
 
-        public Builder withMojoExecution(MojoExecution mojoExecution) {
-            this.mojoExecution = mojoExecution;
-            return this;
-        }
-
         public Builder withRepositorySystem(RepositorySystem repositorySystem) {
             this.repositorySystem = repositorySystem;
             return this;
         }
 
-        public Builder withWagonMap(Map<String, Wagon> wagonMap) {
-            this.wagonMap = wagonMap;
+        public Builder withRuleService(RuleService ruleService) {
+            this.ruleService = ruleService;
             return this;
         }
 
@@ -940,26 +510,8 @@ public class DefaultVersionsHelper implements VersionsHelper {
          * @throws MojoExecutionException should the constructor with the RuleSet retrieval doesn't succeed
          */
         public DefaultVersionsHelper build() throws MojoExecutionException {
-            DefaultVersionsHelper instance = new DefaultVersionsHelper(
-                    pomHelper, artifactFactory, repositorySystem, mavenSession, mojoExecution, log);
-            if (ruleSet != null) {
-                if (!isBlank(rulesUri)) {
-                    log.warn("rulesUri is ignored if rules are specified in pom or as parameters");
-                }
-                instance.ruleSet = ruleSet;
-            } else {
-                instance.ruleSet = isBlank(rulesUri)
-                        ? new RuleSet()
-                        : isClasspathUri(rulesUri) ? getRulesFromClasspath(rulesUri, log) : getRulesUsingWagon();
-            }
-            if (ignoredVersions != null && !ignoredVersions.isEmpty()) {
-                instance.ruleSet = enrichRuleSet(ignoredVersions, instance.ruleSet);
-            }
-            return instance;
-        }
-
-        private static boolean isBlank(String s) {
-            return s == null || s.trim().isEmpty();
+            return new DefaultVersionsHelper(
+                    pomHelper, artifactFactory, repositorySystem, mavenSession, ruleService, log);
         }
     }
 }
diff --git a/versions-common/src/main/java/org/codehaus/mojo/versions/api/PluginUpdatesDetails.java b/versions-common/src/main/java/org/codehaus/mojo/versions/api/PluginUpdatesDetails.java
index e4c3a596..fefd30b5 100644
--- a/versions-common/src/main/java/org/codehaus/mojo/versions/api/PluginUpdatesDetails.java
+++ b/versions-common/src/main/java/org/codehaus/mojo/versions/api/PluginUpdatesDetails.java
@@ -108,12 +108,7 @@ public class PluginUpdatesDetails extends ArtifactVersions {
     }
 
     public int hashCode() {
-        return Objects.hash(
-                getArtifact(),
-                Arrays.hashCode(getVersions(true)),
-                getVersionComparator(),
-                includeSnapshots,
-                dependencyVersions);
+        return Objects.hash(getArtifact(), Arrays.hashCode(getVersions(true)), includeSnapshots, dependencyVersions);
     }
 
     // added an arbitrary comparison just to be able to differentiate objects having different includeSnapshots
diff --git a/versions-common/src/main/java/org/codehaus/mojo/versions/api/PropertyVersions.java b/versions-common/src/main/java/org/codehaus/mojo/versions/api/PropertyVersions.java
index dac48b27..bad5d0d8 100644
--- a/versions-common/src/main/java/org/codehaus/mojo/versions/api/PropertyVersions.java
+++ b/versions-common/src/main/java/org/codehaus/mojo/versions/api/PropertyVersions.java
@@ -38,7 +38,6 @@ import org.apache.maven.plugin.logging.Log;
 import org.apache.maven.project.MavenProject;
 import org.codehaus.mojo.versions.ordering.BoundArtifactVersion;
 import org.codehaus.mojo.versions.ordering.InvalidSegmentException;
-import org.codehaus.mojo.versions.ordering.VersionComparator;
 import org.codehaus.mojo.versions.utils.ArtifactVersionService;
 
 import static org.codehaus.mojo.versions.api.Segment.SUBINCREMENTAL;
@@ -65,47 +64,23 @@ public class PropertyVersions extends AbstractVersionDetails {
 
     private final Log log;
 
-    private VersionsHelper helper;
-
-    private final PropertyVersions.PropertyVersionComparator comparator;
-
     PropertyVersions(
             String profileId,
             String name,
             Log log,
-            VersionsHelper helper,
             Set<ArtifactAssociation> associations,
             SortedSet<ArtifactVersion> resolvedVersions) {
         this.profileId = profileId;
         this.name = name;
         this.log = log;
-        this.helper = helper;
         this.associations = new TreeSet<>(associations);
-        this.comparator = new PropertyVersionComparator();
         this.resolvedVersions = resolvedVersions;
     }
 
-    /**
-     * Gets the rule for version comparison of this artifact.
-     *
-     * @return the rule for version comparison of this artifact.
-     * @since 1.0-beta-1
-     */
-    public VersionComparator getVersionComparator() {
-        return comparator;
-    }
-
     public ArtifactAssociation[] getAssociations() {
         return associations.toArray(new ArtifactAssociation[0]);
     }
 
-    private VersionComparator[] lookupComparators() {
-        return associations.stream()
-                .map(association -> helper.getVersionComparator(association.getArtifact()))
-                .distinct()
-                .toArray(VersionComparator[]::new);
-    }
-
     /**
      * Uses the supplied {@link Collection} of {@link Artifact} instances to see if an ArtifactVersion can be provided.
      *
@@ -280,54 +255,4 @@ public class PropertyVersions extends AbstractVersionDetails {
 
         return result;
     }
-
-    private final class PropertyVersionComparator implements VersionComparator {
-        public int compare(ArtifactVersion v1, ArtifactVersion v2) {
-            return innerCompare(v1, v2);
-        }
-
-        private int innerCompare(ArtifactVersion v1, ArtifactVersion v2) {
-            if (!isAssociated()) {
-                throw new IllegalStateException("Cannot compare versions for a property with no associations");
-            }
-            VersionComparator[] comparators = lookupComparators();
-            assert comparators.length >= 1 : "we have at least one association => at least one comparator";
-            int result = comparators[0].compare(v1, v2);
-            for (int i = 1; i < comparators.length; i++) {
-                int alt = comparators[i].compare(v1, v2);
-                if (result != alt && (result >= 0 && alt < 0) || (result <= 0 && alt > 0)) {
-                    throw new IllegalStateException("Property " + name + " is associated with multiple artifacts"
-                            + " and these artifacts use different version sorting rules and these rules are effectively"
-                            + " incompatible for the two of versions being compared.\nFirst rule says compare(\""
-                            + v1
-                            + "\", \"" + v2 + "\") = " + result
-                            + "\nSecond rule says compare(\"" + v1 + "\", \"" + v2
-                            + "\") = " + alt);
-                }
-            }
-            return result;
-        }
-
-        public int getSegmentCount(ArtifactVersion v) {
-            if (!isAssociated()) {
-                throw new IllegalStateException("Cannot compare versions for a property with no associations");
-            }
-            VersionComparator[] comparators = lookupComparators();
-            assert comparators.length >= 1 : "we have at least one association => at least one comparator";
-            int result = comparators[0].getSegmentCount(v);
-            for (int i = 1; i < comparators.length; i++) {
-                int alt = comparators[i].getSegmentCount(v);
-                if (result != alt) {
-                    throw new IllegalStateException("Property " + name + " is associated with multiple artifacts"
-                            + " and these artifacts use different version sorting rules and these rules are effectively"
-                            + " incompatible for the two of versions being compared.\n"
-                            + "First rule says getSegmentCount(\""
-                            + v + "\") = " + result
-                            + "\nSecond rule says getSegmentCount(\"" + v + "\") = "
-                            + alt);
-                }
-            }
-            return result;
-        }
-    }
 }
diff --git a/versions-common/src/main/java/org/codehaus/mojo/versions/api/PropertyVersionsBuilder.java b/versions-common/src/main/java/org/codehaus/mojo/versions/api/PropertyVersionsBuilder.java
index 78bd645c..c579c186 100644
--- a/versions-common/src/main/java/org/codehaus/mojo/versions/api/PropertyVersionsBuilder.java
+++ b/versions-common/src/main/java/org/codehaus/mojo/versions/api/PropertyVersionsBuilder.java
@@ -92,7 +92,7 @@ public class PropertyVersionsBuilder {
 
     public PropertyVersions build() throws VersionRetrievalException {
         SortedSet<ArtifactVersion> resolvedVersions = resolveAssociatedVersions(helper, associations);
-        PropertyVersions instance = new PropertyVersions(profileId, name, log, helper, associations, resolvedVersions);
+        PropertyVersions instance = new PropertyVersions(profileId, name, log, associations, resolvedVersions);
         instance.setCurrentVersion(currentVersion);
         instance.setCurrentVersionRange(currentVersionRange);
         return instance;
diff --git a/versions-common/src/main/java/org/codehaus/mojo/versions/api/VersionDetails.java b/versions-common/src/main/java/org/codehaus/mojo/versions/api/VersionDetails.java
index f12699e5..23308ae0 100644
--- a/versions-common/src/main/java/org/codehaus/mojo/versions/api/VersionDetails.java
+++ b/versions-common/src/main/java/org/codehaus/mojo/versions/api/VersionDetails.java
@@ -25,7 +25,6 @@ import org.apache.maven.artifact.versioning.ArtifactVersion;
 import org.apache.maven.artifact.versioning.Restriction;
 import org.apache.maven.artifact.versioning.VersionRange;
 import org.codehaus.mojo.versions.ordering.InvalidSegmentException;
-import org.codehaus.mojo.versions.ordering.VersionComparator;
 
 /**
  * Created by IntelliJ IDEA.
@@ -81,14 +80,6 @@ public interface VersionDetails {
      */
     void setCurrentVersionRange(VersionRange versionRange);
 
-    /**
-     * Gets the rule for version comparison of this artifact.
-     *
-     * @return the rule for version comparison of this artifact.
-     * @since 1.0-beta-1
-     */
-    VersionComparator getVersionComparator();
-
     /**
      * Returns all available versions in increasing order.
      *
diff --git a/versions-common/src/main/java/org/codehaus/mojo/versions/api/VersionsHelper.java b/versions-common/src/main/java/org/codehaus/mojo/versions/api/VersionsHelper.java
index ec3555a8..0642caa2 100644
--- a/versions-common/src/main/java/org/codehaus/mojo/versions/api/VersionsHelper.java
+++ b/versions-common/src/main/java/org/codehaus/mojo/versions/api/VersionsHelper.java
@@ -29,8 +29,6 @@ import org.apache.maven.model.Dependency;
 import org.apache.maven.model.Plugin;
 import org.apache.maven.plugin.MojoExecutionException;
 import org.apache.maven.project.MavenProject;
-import org.codehaus.mojo.versions.ordering.VersionComparator;
-import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluator;
 
 /**
  * Helper class that provides common functionality required by both the mojos and the reports.
@@ -40,25 +38,6 @@ import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluator
  */
 public interface VersionsHelper {
 
-    /**
-     * Returns the version comparator to use for the specified artifact.
-     *
-     * @param artifact the artifact.
-     * @return the version comparator to use.
-     * @since 1.0-alpha-3
-     */
-    VersionComparator getVersionComparator(Artifact artifact);
-
-    /**
-     * Returns the version comparator to use for the specified groupId and artifactId.
-     *
-     * @param groupId the groupId.
-     * @param artifactId the artifactId.
-     * @return the version comparator to use.
-     * @since 1.0-alpha-3
-     */
-    VersionComparator getVersionComparator(String groupId, String artifactId);
-
     /**
      * Looks up the versions of the specified artifact that are available in either the local repository, or the
      * appropriate remote repositories.
@@ -180,15 +159,6 @@ public interface VersionsHelper {
      */
     PluginUpdatesDetails lookupPluginUpdates(Plugin plugin, boolean allowSnapshots) throws VersionRetrievalException;
 
-    /**
-     * Returns an {@link ExpressionEvaluator} for the specified project.
-     *
-     * @param project The project.
-     * @return an {@link ExpressionEvaluator} for the specified project.
-     * @since 1.0-beta-1
-     */
-    ExpressionEvaluator getExpressionEvaluator(MavenProject project);
-
     /**
      * Returns a map of {@link org.codehaus.mojo.versions.api.PropertyVersions} values keyed by
      * {@link Property} instances consisting of the properties defined in the project which
diff --git a/versions-common/src/main/java/org/codehaus/mojo/versions/ordering/AbstractVersionComparator.java b/versions-common/src/main/java/org/codehaus/mojo/versions/ordering/AbstractVersionComparator.java
deleted file mode 100644
index 6f05bfda..00000000
--- a/versions-common/src/main/java/org/codehaus/mojo/versions/ordering/AbstractVersionComparator.java
+++ /dev/null
@@ -1,69 +0,0 @@
-package org.codehaus.mojo.versions.ordering;
-
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you 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.
- */
-
-import org.apache.maven.artifact.ArtifactUtils;
-import org.apache.maven.artifact.versioning.ArtifactVersion;
-
-/**
- * Base class for version comparators.
- *
- * @since 1.0-beta-1
- */
-public abstract class AbstractVersionComparator implements VersionComparator {
-    @Override
-    public abstract int compare(ArtifactVersion o1, ArtifactVersion o2);
-
-    @Override
-    public final int getSegmentCount(ArtifactVersion v) {
-        if (v == null) {
-            return 0;
-        }
-        if (ArtifactUtils.isSnapshot(v.toString())) {
-            return innerGetSegmentCount(VersionComparators.stripSnapshot(v));
-        }
-        return innerGetSegmentCount(v);
-    }
-
-    protected abstract int innerGetSegmentCount(ArtifactVersion v);
-
-    /**
-     * Returns a hash code value for the comparator class.
-     *
-     * @return the hash code.
-     */
-    @Override
-    public int hashCode() {
-        return getClass().hashCode();
-    }
-
-    /**
-     * Returns true if this object is the same type of comparator as the parameter.
-     *
-     * @param obj the reference object with which to compare.
-     * @return <code>true</code> if this object is the same as the obj argument; <code>false</code> otherwise.
-     * @see #hashCode()
-     * @see java.util.Hashtable
-     */
-    @Override
-    public boolean equals(Object obj) {
-        return obj == this || (obj != null && getClass().equals(obj.getClass()));
-    }
-}
diff --git a/versions-common/src/main/java/org/codehaus/mojo/versions/ordering/BoundArtifactVersion.java b/versions-common/src/main/java/org/codehaus/mojo/versions/ordering/BoundArtifactVersion.java
index cc555eb1..54ad06bb 100644
--- a/versions-common/src/main/java/org/codehaus/mojo/versions/ordering/BoundArtifactVersion.java
+++ b/versions-common/src/main/java/org/codehaus/mojo/versions/ordering/BoundArtifactVersion.java
@@ -15,6 +15,8 @@ package org.codehaus.mojo.versions.ordering;
  * limitations under the License.
  */
 
+import javax.annotation.Nullable;
+
 import java.util.ArrayList;
 import java.util.List;
 
@@ -128,7 +130,7 @@ public class BoundArtifactVersion implements ArtifactVersion {
     }
 
     @Override
-    public int compareTo(ArtifactVersion other) {
+    public int compareTo(@Nullable ArtifactVersion other) {
         if (other == null) {
             return -1;
         }
@@ -201,9 +203,6 @@ public class BoundArtifactVersion implements ArtifactVersion {
 
     /**
      * {@inheritDoc}
-     *
-     * Quasi-contract: {@link #toString()} must produce the textual representation of the version, without any
-     * additional items, this is required by the implementation of {@link NumericVersionComparator}.
      */
     @Override
     public String toString() {
diff --git a/versions-common/src/main/java/org/codehaus/mojo/versions/ordering/DefaultSegmentCounter.java b/versions-common/src/main/java/org/codehaus/mojo/versions/ordering/DefaultSegmentCounter.java
new file mode 100644
index 00000000..0e2eee53
--- /dev/null
+++ b/versions-common/src/main/java/org/codehaus/mojo/versions/ordering/DefaultSegmentCounter.java
@@ -0,0 +1,99 @@
+package org.codehaus.mojo.versions.ordering;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+import javax.annotation.Nullable;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.maven.artifact.ArtifactUtils;
+import org.apache.maven.artifact.versioning.ArtifactVersion;
+import org.codehaus.mojo.versions.utils.ArtifactVersionService;
+
+/**
+ * Default implementation of the {@link SegmentCounter} interface, using Maven's version rules, i.e. 1.3.34 &gt; 1.3.9
+ * but 1.3.4.3.2.34 &lt; 1.3.4.3.2.9.
+ */
+public class DefaultSegmentCounter implements SegmentCounter {
+    public static final SegmentCounter INSTANCE = new DefaultSegmentCounter();
+
+    private static final Pattern SNAPSHOT_PATTERN = Pattern.compile("(-((\\d{8}\\.\\d{6})-(\\d+))|(SNAPSHOT))$");
+
+    private static ArtifactVersion stripSnapshot(ArtifactVersion v) {
+        final String version = v.toString();
+        final Matcher matcher = SNAPSHOT_PATTERN.matcher(version);
+        if (matcher.find()) {
+            return ArtifactVersionService.getArtifactVersion(version.substring(0, matcher.start(1) - 1));
+        }
+        return v;
+    }
+
+    private int innerGetSegmentCount(ArtifactVersion v) {
+        // if the version does not match the maven rules, then we have only one segment
+        // i.e. the qualifier
+        if (v.getBuildNumber() != 0) {
+            // the version was successfully parsed, and we have a build number
+            // have to have four segments
+            return 4;
+        }
+        if ((v.getMajorVersion() != 0 || v.getMinorVersion() != 0 || v.getIncrementalVersion() != 0)
+                && v.getQualifier() != null) {
+            // the version was successfully parsed, and we have a qualifier
+            // have to have four segments
+            return 4;
+        }
+        final String version = v.toString();
+        if (version.indexOf('-') != -1) {
+            // the version has parts and was not parsed successfully
+            // have to have one segment
+            return version.equals(v.getQualifier()) ? 1 : 4;
+        }
+        if (version.indexOf('.') != -1) {
+            // the version has parts and was not parsed successfully
+            // have to have one segment
+            return version.equals(v.getQualifier()) ? 1 : 3;
+        }
+        if (StringUtils.isEmpty(version)) {
+            return 3;
+        }
+        try {
+            Integer.parseInt(version);
+            return 3;
+        } catch (NumberFormatException e) {
+            return 1;
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public final int getSegmentCount(@Nullable ArtifactVersion v) {
+        if (v == null) {
+            return 0;
+        }
+        if (ArtifactUtils.isSnapshot(v.toString())) {
+            return innerGetSegmentCount(stripSnapshot(v));
+        }
+        return innerGetSegmentCount(v);
+    }
+}
diff --git a/versions-common/src/main/java/org/codehaus/mojo/versions/ordering/MavenVersionComparator.java b/versions-common/src/main/java/org/codehaus/mojo/versions/ordering/MavenVersionComparator.java
deleted file mode 100644
index c01c1b67..00000000
--- a/versions-common/src/main/java/org/codehaus/mojo/versions/ordering/MavenVersionComparator.java
+++ /dev/null
@@ -1,180 +0,0 @@
-package org.codehaus.mojo.versions.ordering;
-
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you 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.
- */
-
-import org.apache.commons.lang3.StringUtils;
-import org.apache.maven.artifact.versioning.ArtifactVersion;
-import org.apache.maven.artifact.versioning.ComparableVersion;
-import org.codehaus.mojo.versions.api.Segment;
-
-import static org.codehaus.mojo.versions.utils.ArtifactVersionService.getArtifactVersion;
-
-/**
- * A comparator which uses Maven's version rules, i.e. 1.3.34 &gt; 1.3.9 but 1.3.4.3.2.34 &lt; 1.3.4.3.2.9.
- *
- * @author Stephen Connolly
- * @since 1.0-alpha-3
- */
-public class MavenVersionComparator extends AbstractVersionComparator {
-
-    /**
-     * {@inheritDoc}
-     */
-    public int compare(ArtifactVersion o1, ArtifactVersion o2) {
-        if (o1 instanceof BoundArtifactVersion) {
-            return o1.compareTo(o2);
-        }
-        return new ComparableVersion(o1.toString()).compareTo(new ComparableVersion(o2.toString()));
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    protected int innerGetSegmentCount(ArtifactVersion v) {
-        // if the version does not match the maven rules, then we have only one segment
-        // i.e. the qualifier
-        if (v.getBuildNumber() != 0) {
-            // the version was successfully parsed, and we have a build number
-            // have to have four segments
-            return 4;
-        }
-        if ((v.getMajorVersion() != 0 || v.getMinorVersion() != 0 || v.getIncrementalVersion() != 0)
-                && v.getQualifier() != null) {
-            // the version was successfully parsed, and we have a qualifier
-            // have to have four segments
-            return 4;
-        }
-        final String version = v.toString();
-        if (version.indexOf('-') != -1) {
-            // the version has parts and was not parsed successfully
-            // have to have one segment
-            return version.equals(v.getQualifier()) ? 1 : 4;
-        }
-        if (version.indexOf('.') != -1) {
-            // the version has parts and was not parsed successfully
-            // have to have one segment
-            return version.equals(v.getQualifier()) ? 1 : 3;
-        }
-        if (StringUtils.isEmpty(version)) {
-            return 3;
-        }
-        try {
-            Integer.parseInt(version);
-            return 3;
-        } catch (NumberFormatException e) {
-            return 1;
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    protected ArtifactVersion innerIncrementSegment(ArtifactVersion v, Segment segment) throws InvalidSegmentException {
-        String version = v.toString();
-        if (innerGetSegmentCount(v) == 1) {
-            // only the qualifier
-            version = VersionComparators.alphaNumIncrement(version);
-            return getArtifactVersion(version);
-        } else {
-            int major = v.getMajorVersion();
-            int minor = v.getMinorVersion();
-            int incremental = v.getIncrementalVersion();
-            int build = v.getBuildNumber();
-            String qualifier = v.getQualifier();
-
-            int minorIndex = version.indexOf('.');
-            boolean haveMinor = minorIndex != -1;
-            int incrementalIndex = haveMinor ? version.indexOf('.', minorIndex + 1) : -1;
-            boolean haveIncremental = incrementalIndex != -1;
-            int buildIndex = version.indexOf('-');
-            boolean haveBuild = buildIndex != -1 && qualifier == null;
-            boolean haveQualifier = buildIndex != -1 && qualifier != null;
-
-            switch (segment) {
-                case MAJOR:
-                    major++;
-                    minor = 0;
-                    incremental = 0;
-                    build = 0;
-                    qualifier = null;
-                    break;
-                case MINOR:
-                    minor++;
-                    incremental = 0;
-                    build = 0;
-                    if (haveQualifier && qualifier.endsWith("SNAPSHOT")) {
-                        qualifier = "SNAPSHOT";
-                    }
-                    break;
-                case INCREMENTAL:
-                    incremental++;
-                    build = 0;
-                    qualifier = null;
-                    break;
-                case SUBINCREMENTAL:
-                    if (haveQualifier) {
-                        qualifier = qualifierIncrement(qualifier);
-                    } else {
-                        build++;
-                    }
-                    break;
-                default:
-                    // no action
-                    break;
-            }
-            StringBuilder result = new StringBuilder();
-            result.append(major);
-            if (haveMinor || minor > 0 || incremental > 0) {
-                result.append('.');
-                result.append(minor);
-            }
-            if (haveIncremental || incremental > 0) {
-                result.append('.');
-                result.append(incremental);
-            }
-            if (haveQualifier && qualifier != null) {
-                result.append('-');
-                result.append(qualifier);
-            } else if (haveBuild || build > 0) {
-                result.append('-');
-                result.append(build);
-            }
-            return getArtifactVersion(result.toString());
-        }
-    }
-
-    private String qualifierIncrement(String qualifier) {
-        if (qualifier.toLowerCase().startsWith("alpha")) {
-            return qualifier.substring(0, 5) + VersionComparators.alphaNumIncrement(qualifier.substring(5));
-        }
-        if (qualifier.toLowerCase().startsWith("beta")) {
-            return qualifier.substring(0, 4) + VersionComparators.alphaNumIncrement(qualifier.substring(4));
-        }
-        if (qualifier.toLowerCase().startsWith("milestone")) {
-            return qualifier.substring(0, 8) + VersionComparators.alphaNumIncrement(qualifier.substring(8));
-        }
-        if (qualifier.toLowerCase().startsWith("cr")
-                || qualifier.toLowerCase().startsWith("rc")
-                || qualifier.toLowerCase().startsWith("sp")) {
-            return qualifier.substring(0, 2) + VersionComparators.alphaNumIncrement(qualifier.substring(2));
-        }
-        return VersionComparators.alphaNumIncrement(qualifier);
-    }
-}
diff --git a/versions-common/src/main/java/org/codehaus/mojo/versions/ordering/MercuryVersionComparator.java b/versions-common/src/main/java/org/codehaus/mojo/versions/ordering/MercuryVersionComparator.java
deleted file mode 100644
index a32d185d..00000000
--- a/versions-common/src/main/java/org/codehaus/mojo/versions/ordering/MercuryVersionComparator.java
+++ /dev/null
@@ -1,121 +0,0 @@
-package org.codehaus.mojo.versions.ordering;
-
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you 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.
- */
-
-import java.math.BigInteger;
-import java.util.StringTokenizer;
-
-import org.apache.maven.artifact.versioning.ArtifactVersion;
-import org.apache.maven.artifact.versioning.ComparableVersion;
-import org.codehaus.mojo.versions.api.Segment;
-
-import static org.codehaus.mojo.versions.utils.ArtifactVersionService.getArtifactVersion;
-
-/**
- * A comparator which uses Mercury's version rules.
- *
- * @author Stephen Connolly
- * @since 1.0-alpha-3
- * @deprecated
- */
-public class MercuryVersionComparator extends AbstractVersionComparator {
-    private static final BigInteger BIG_INTEGER_ONE = new BigInteger("1");
-
-    /**
-     * {@inheritDoc}
-     */
-    public int compare(ArtifactVersion o1, ArtifactVersion o2) {
-        return new ComparableVersion(o1.toString()).compareTo(new ComparableVersion(o2.toString()));
-    }
-
-    protected int innerGetSegmentCount(ArtifactVersion v) {
-        final String version = v.toString();
-        StringTokenizer tok = new StringTokenizer(version, ".-");
-        return tok.countTokens();
-    }
-
-    protected ArtifactVersion innerIncrementSegment(ArtifactVersion v, Segment segment) throws InvalidSegmentException {
-        final String version = v.toString();
-        StringBuilder result = new StringBuilder(version.length() + 10);
-        StringTokenizer tok = new StringTokenizer(version, ".-");
-        int index = 0;
-        while (tok.hasMoreTokens() && segment.value() > 0) {
-            String token = tok.nextToken();
-            result.append(token);
-            index += token.length();
-            if (tok.hasMoreTokens()) {
-                // grab the token separator
-                result.append(version.charAt(index));
-                index++;
-            }
-            segment = Segment.of(segment.value() - 1);
-        }
-        if (segment.value() == 0) {
-            if (tok.hasMoreTokens()) {
-                String token = tok.nextToken();
-                String newToken;
-                try {
-                    BigInteger n = new BigInteger(token);
-                    newToken = n.add(BIG_INTEGER_ONE).toString();
-                } catch (NumberFormatException e) {
-                    // ok, let's try some common tricks
-                    if ("alpha".equalsIgnoreCase(token)) {
-                        newToken = "beta";
-                    } else if ("beta".equalsIgnoreCase(token)) {
-                        newToken = "milestone";
-                    } else if ("milestone".equalsIgnoreCase(token)) {
-                        newToken = "rc";
-                    } else if ("rc".equalsIgnoreCase(token) || "cr".equalsIgnoreCase(token)) {
-                        newToken = "ga";
-                    } else if ("final".equalsIgnoreCase(token)
-                            || "ga".equalsIgnoreCase(token)
-                            || "".equalsIgnoreCase(token)) {
-                        newToken = "sp";
-                    } else {
-                        newToken = VersionComparators.alphaNumIncrement(token);
-                    }
-                }
-
-                result.append(newToken);
-                index += token.length();
-                if (tok.hasMoreTokens()) {
-                    // grab the token separator
-                    result.append(version.charAt(index));
-                    index++;
-                }
-
-            } else {
-                // an empty part is equivalent to 0 for mercury version comparator
-                result.append("1");
-            }
-        }
-        while (tok.hasMoreTokens()) {
-            String token = tok.nextToken();
-            result.append("0");
-            index += token.length();
-            if (tok.hasMoreTokens()) {
-                // grab the token separator
-                result.append(version.charAt(index));
-                index++;
-            }
-        }
-        return getArtifactVersion(result.toString());
-    }
-}
diff --git a/versions-common/src/main/java/org/codehaus/mojo/versions/ordering/NumericVersionComparator.java b/versions-common/src/main/java/org/codehaus/mojo/versions/ordering/NumericVersionComparator.java
deleted file mode 100644
index f9cd607f..00000000
--- a/versions-common/src/main/java/org/codehaus/mojo/versions/ordering/NumericVersionComparator.java
+++ /dev/null
@@ -1,249 +0,0 @@
-package org.codehaus.mojo.versions.ordering;
-
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you 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.
- */
-
-import java.math.BigInteger;
-import java.util.StringTokenizer;
-
-import org.apache.maven.artifact.versioning.ArtifactVersion;
-import org.codehaus.mojo.versions.api.Segment;
-
-import static org.codehaus.mojo.versions.utils.ArtifactVersionService.getArtifactVersion;
-
-/**
- * A comparator which will compare all segments of a dot separated version string as numbers if possible, i.e. 1.3.34
- * &gt; 1.3.9 and 1.3.4.3.2.34 &gt; 1.3.4.3.2.9 and 1.3.4.3.2.34 &gt; 1.3.4.3.2.34-SNAPSHOT
- *
- * @author Stephen Connolly
- * @since 1.0-alpha-3
- * @deprecated
- */
-public class NumericVersionComparator extends AbstractVersionComparator {
-    private static final BigInteger BIG_INTEGER_ZERO = new BigInteger("0");
-
-    private static final BigInteger BIG_INTEGER_ONE = new BigInteger("1");
-
-    /**
-     * {@inheritDoc}
-     */
-    public int compare(ArtifactVersion o1, ArtifactVersion o2) {
-        String v1 = o1.toString();
-        String v2 = o2.toString();
-        StringTokenizer tok1 = new StringTokenizer(v1, ".");
-        StringTokenizer tok2 = new StringTokenizer(v2, ".");
-        while (tok1.hasMoreTokens() && tok2.hasMoreTokens()) {
-            String p1 = tok1.nextToken();
-            String p2 = tok2.nextToken();
-            String q1 = null;
-            String q2 = null;
-            if (p1.indexOf('-') >= 0) {
-                int index = p1.indexOf('-');
-                q1 = p1.substring(index);
-                p1 = p1.substring(0, index);
-            }
-            if (p2.indexOf('-') >= 0) {
-                int index = p2.indexOf('-');
-                q2 = p2.substring(index);
-                p2 = p2.substring(0, index);
-            }
-            try {
-                BigInteger n1 = new BigInteger(p1);
-                BigInteger n2 = new BigInteger(p2);
-                int result = n1.compareTo(n2);
-                if (result != 0) {
-                    return result;
-                }
-            } catch (NumberFormatException e) {
-                int result = p1.compareTo(p2);
-                if (result != 0) {
-                    return result;
-                }
-            }
-            if (q1 != null && q2 != null) {
-                final int result = q1.compareTo(q2);
-                if (result != 0) {
-                    return result;
-                }
-            }
-            if (q1 != null) {
-                return -1;
-            }
-            if (q2 != null) {
-                return +1;
-            }
-        }
-        if (tok1.hasMoreTokens()) {
-            BigInteger n2 = BIG_INTEGER_ZERO;
-            while (tok1.hasMoreTokens()) {
-                try {
-                    BigInteger n1 = new BigInteger(tok1.nextToken());
-                    int result = n1.compareTo(n2);
-                    if (result != 0) {
-                        return result;
-                    }
-                } catch (NumberFormatException e) {
-                    // any token is better than zero
-                    return +1;
-                }
-            }
-            return -1;
-        }
-        if (tok2.hasMoreTokens()) {
-            BigInteger n1 = BIG_INTEGER_ZERO;
-            while (tok2.hasMoreTokens()) {
-                try {
-                    BigInteger n2 = new BigInteger(tok2.nextToken());
-                    int result = n1.compareTo(n2);
-                    if (result != 0) {
-                        return result;
-                    }
-                } catch (NumberFormatException e) {
-                    // any token is better than zero
-                    return -1;
-                }
-            }
-            return +1;
-        }
-        return 0;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    protected int innerGetSegmentCount(ArtifactVersion v) {
-        final String version = v.toString();
-        StringTokenizer tok = new StringTokenizer(version, ".");
-        return tok.countTokens();
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @SuppressWarnings("checkstyle:MethodLength")
-    protected ArtifactVersion innerIncrementSegment(ArtifactVersion v, Segment segment) throws InvalidSegmentException {
-        final String version = v.toString();
-        StringBuilder buf = new StringBuilder();
-        StringTokenizer tok = new StringTokenizer(version, ".");
-        boolean first = true;
-        for (int segmentIdx = segment.value(); segmentIdx >= 0 && tok.hasMoreTokens(); --segmentIdx) {
-            if (first) {
-                first = false;
-            } else {
-                buf.append('.');
-            }
-            String p = tok.nextToken();
-            String q = null;
-            if (p.indexOf('-') >= 0) {
-                int index = p.indexOf('-');
-                q = p.substring(index + 1);
-                p = p.substring(0, index);
-            }
-
-            if (segmentIdx == 0) {
-                try {
-                    BigInteger n = new BigInteger(p);
-                    p = n.add(BIG_INTEGER_ONE).toString();
-                    q = null;
-                } catch (NumberFormatException e) {
-                    // ok, let's try some common tricks
-                    if ("alpha".equalsIgnoreCase(p)) {
-                        if (q == null) {
-                            p = "beta";
-                        } else {
-                            try {
-                                BigInteger n = new BigInteger(q);
-                                q = n.add(BIG_INTEGER_ONE).toString();
-                            } catch (NumberFormatException e1) {
-                                p = "beta";
-                                q = null;
-                            }
-                        }
-                    } else if ("beta".equalsIgnoreCase(p)) {
-                        if (q == null) {
-                            p = "milestone";
-                        } else {
-                            try {
-                                BigInteger n = new BigInteger(q);
-                                q = n.add(BIG_INTEGER_ONE).toString();
-                            } catch (NumberFormatException e1) {
-                                p = "milestone";
-                                q = null;
-                            }
-                        }
-                    } else if ("milestone".equalsIgnoreCase(p)) {
-                        if (q == null) {
-                            p = "rc";
-                        } else {
-                            try {
-                                BigInteger n = new BigInteger(q);
-                                q = n.add(BIG_INTEGER_ONE).toString();
-                            } catch (NumberFormatException e1) {
-                                p = "rc";
-                                q = null;
-                            }
-                        }
-                    } else if ("cr".equalsIgnoreCase(p) || "rc".equalsIgnoreCase(p)) {
-                        if (q == null) {
-                            p = "ga";
-                        } else {
-                            try {
-                                BigInteger n = new BigInteger(q);
-                                q = n.add(BIG_INTEGER_ONE).toString();
-                            } catch (NumberFormatException e1) {
-                                p = "ga";
-                                q = null;
-                            }
-                        }
-                    } else if ("ga".equalsIgnoreCase(p) || "final".equalsIgnoreCase(p)) {
-                        if (q == null) {
-                            p = "sp";
-                            q = "1";
-                        } else {
-                            try {
-                                BigInteger n = new BigInteger(q);
-                                q = n.add(BIG_INTEGER_ONE).toString();
-                            } catch (NumberFormatException e1) {
-                                p = "sp";
-                                q = "1";
-                            }
-                        }
-                    } else {
-                        p = VersionComparators.alphaNumIncrement(p);
-                    }
-                }
-            }
-            buf.append(p);
-            if (q != null) {
-                buf.append('-');
-                buf.append(q);
-            }
-        }
-        while (tok.hasMoreTokens()) {
-            if (first) {
-                first = false;
-            } else {
-                buf.append('.');
-            }
-            tok.nextToken();
-            buf.append("0");
-        }
-        return getArtifactVersion(buf.toString());
-    }
-}
diff --git a/versions-common/src/main/java/org/codehaus/mojo/versions/ordering/VersionComparator.java b/versions-common/src/main/java/org/codehaus/mojo/versions/ordering/SegmentCounter.java
similarity index 89%
rename from versions-common/src/main/java/org/codehaus/mojo/versions/ordering/VersionComparator.java
rename to versions-common/src/main/java/org/codehaus/mojo/versions/ordering/SegmentCounter.java
index 2b7fa449..4492893e 100644
--- a/versions-common/src/main/java/org/codehaus/mojo/versions/ordering/VersionComparator.java
+++ b/versions-common/src/main/java/org/codehaus/mojo/versions/ordering/SegmentCounter.java
@@ -19,14 +19,12 @@ package org.codehaus.mojo.versions.ordering;
  * under the License.
  */
 
-import java.util.Comparator;
-
 import org.apache.maven.artifact.versioning.ArtifactVersion;
 
 /**
- * A rule for comparing and manipulating versions.
+ * Counts the number of segments
  */
-public interface VersionComparator extends Comparator<ArtifactVersion> {
+public interface SegmentCounter {
     /**
      * Returns the number of segments specified or specifiable in the supplied artifact version.
      *
diff --git a/versions-common/src/main/java/org/codehaus/mojo/versions/ordering/VersionComparators.java b/versions-common/src/main/java/org/codehaus/mojo/versions/ordering/VersionComparators.java
deleted file mode 100644
index 0f6cc1ad..00000000
--- a/versions-common/src/main/java/org/codehaus/mojo/versions/ordering/VersionComparators.java
+++ /dev/null
@@ -1,124 +0,0 @@
-package org.codehaus.mojo.versions.ordering;
-
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you 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.
- */
-
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import org.apache.maven.artifact.versioning.ArtifactVersion;
-
-import static org.codehaus.mojo.versions.utils.ArtifactVersionService.getArtifactVersion;
-
-/**
- * Utility.
- *
- * @author Stephen Connolly
- * @since 1.0-alpha-3
- * @deprecated
- * Note: This comparator should be replaced by using the one of Maven core instead.
- */
-public final class VersionComparators {
-    private static final Pattern SNAPSHOT_PATTERN = Pattern.compile("(-((\\d{8}\\.\\d{6})-(\\d+))|(SNAPSHOT))$");
-
-    private VersionComparators() {
-        throw new IllegalAccessError("Utility classes should never be instantiated");
-    }
-
-    /**
-     * Returns the version comparator to use.
-     *
-     * @param comparisonMethod the comparison method.
-     * @return the version comparator to use.
-     * @since 1.0-alpha-1
-     */
-    public static VersionComparator getVersionComparator(String comparisonMethod) {
-        if ("numeric".equalsIgnoreCase(comparisonMethod)) {
-            return new NumericVersionComparator();
-        } else if ("mercury".equalsIgnoreCase(comparisonMethod)) {
-            return new MercuryVersionComparator();
-        }
-        return new MavenVersionComparator();
-    }
-
-    public static String alphaNumIncrement(String token) {
-        String newToken;
-        int i = token.length();
-        boolean done = false;
-        newToken = token;
-        while (!done && i > 0) {
-            i--;
-            char c = token.charAt(i);
-            if ('0' <= c && c < '9') {
-                c++;
-                newToken = newToken.substring(0, i) + c + (i + 1 < newToken.length() ? newToken.substring(i + 1) : "");
-                done = true;
-            } else if (c == '9') {
-                c = '0';
-                newToken = newToken.substring(0, i) + c + (i + 1 < newToken.length() ? newToken.substring(i + 1) : "");
-            } else if ('A' <= c && c < 'Z') {
-                c++;
-                newToken = newToken.substring(0, i) + c + (i + 1 < newToken.length() ? newToken.substring(i + 1) : "");
-                done = true;
-            } else if (c == 'Z') {
-                c = 'A';
-                newToken = newToken.substring(0, i) + c + (i + 1 < newToken.length() ? newToken.substring(i + 1) : "");
-            } else if ('a' <= c && c < 'z') {
-                c++;
-                newToken = newToken.substring(0, i) + c + (i + 1 < newToken.length() ? newToken.substring(i + 1) : "");
-                done = true;
-            } else if (c == 'z') {
-                c = 'a';
-                newToken = newToken.substring(0, i) + c + (i + 1 < newToken.length() ? newToken.substring(i + 1) : "");
-            }
-        }
-        if (done) {
-            return newToken;
-        } else {
-            // ok this is roll-over time
-            boolean lastNumeric = false;
-            boolean lastAlpha = false;
-            boolean lastUpper = false;
-            i = token.length();
-            while (!lastAlpha && !lastNumeric && i > 0) {
-                i--;
-                char c = token.charAt(i);
-                lastAlpha = Character.isLetter(c);
-                lastUpper = c == Character.toUpperCase(c);
-                lastNumeric = Character.isDigit(c);
-            }
-            if (lastAlpha) {
-                if (lastUpper) {
-                    return token + 'A';
-                }
-                return token + 'a';
-            }
-            return token + '0';
-        }
-    }
-
-    static ArtifactVersion stripSnapshot(ArtifactVersion v) {
-        final String version = v.toString();
-        final Matcher matcher = SNAPSHOT_PATTERN.matcher(version);
-        if (matcher.find()) {
-            return getArtifactVersion(version.substring(0, matcher.start(1) - 1));
-        }
-        return v;
-    }
-}
diff --git a/versions-common/src/main/java/org/codehaus/mojo/versions/rule/RuleService.java b/versions-common/src/main/java/org/codehaus/mojo/versions/rule/RuleService.java
new file mode 100644
index 00000000..c5e7da38
--- /dev/null
+++ b/versions-common/src/main/java/org/codehaus/mojo/versions/rule/RuleService.java
@@ -0,0 +1,135 @@
+package org.codehaus.mojo.versions.rule;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.apache.maven.artifact.Artifact;
+import org.apache.maven.plugin.logging.Log;
+import org.codehaus.mojo.versions.api.IgnoreVersionHelper;
+import org.codehaus.mojo.versions.model.IgnoreVersion;
+import org.codehaus.mojo.versions.model.Rule;
+import org.codehaus.mojo.versions.model.RuleSet;
+import org.codehaus.mojo.versions.utils.RegexUtils;
+
+public class RuleService {
+    Log log;
+
+    private final Map<String, Rule> bestFitRuleCache = new ConcurrentHashMap<>();
+
+    private final RuleSet ruleSet;
+
+    public RuleService(Log log, RuleSet ruleSet) {
+        this.log = log;
+        this.ruleSet = ruleSet;
+    }
+
+    /**
+     * Find the rule, if any, which best fits the artifact details given.
+     *
+     * @param groupId    Group id of the artifact
+     * @param artifactId Artifact id of the artifact
+     * @return Rule which best describes the given artifact
+     */
+    public Rule getBestFitRule(String groupId, String artifactId) {
+        String groupArtifactId = groupId + ':' + artifactId;
+        if (bestFitRuleCache.containsKey(groupArtifactId)) {
+            return bestFitRuleCache.get(groupArtifactId);
+        }
+
+        Rule bestFit = null;
+        final List<Rule> rules = getRuleSet().getRules();
+        int bestGroupIdScore = Integer.MAX_VALUE;
+        int bestArtifactIdScore = Integer.MAX_VALUE;
+        boolean exactGroupId = false;
+        boolean exactArtifactId = false;
+        for (Rule rule : rules) {
+            int groupIdScore = RegexUtils.getWildcardScore(rule.getGroupId());
+            if (groupIdScore > bestGroupIdScore) {
+                continue;
+            }
+            boolean exactMatch = exactMatch(rule.getGroupId(), groupId);
+            boolean match = exactMatch || match(rule.getGroupId(), groupId);
+            if (!match || (exactGroupId && !exactMatch)) {
+                continue;
+            }
+            if (bestGroupIdScore > groupIdScore) {
+                bestArtifactIdScore = Integer.MAX_VALUE;
+                exactArtifactId = false;
+            }
+            bestGroupIdScore = groupIdScore;
+            if (exactMatch && !exactGroupId) {
+                exactGroupId = true;
+                bestArtifactIdScore = Integer.MAX_VALUE;
+                exactArtifactId = false;
+            }
+            int artifactIdScore = RegexUtils.getWildcardScore(rule.getArtifactId());
+            if (artifactIdScore > bestArtifactIdScore) {
+                continue;
+            }
+            exactMatch = exactMatch(rule.getArtifactId(), artifactId);
+            match = exactMatch || match(rule.getArtifactId(), artifactId);
+            if (!match || (exactArtifactId && !exactMatch)) {
+                continue;
+            }
+            bestArtifactIdScore = artifactIdScore;
+            if (exactMatch && !exactArtifactId) {
+                exactArtifactId = true;
+            }
+            bestFit = rule;
+        }
+
+        if (bestFit != null) {
+            bestFitRuleCache.put(groupArtifactId, bestFit);
+        }
+        return bestFit;
+    }
+
+    /**
+     * Returns a list of versions which should not be considered when looking for updates.
+     *
+     * @param artifact The artifact
+     * @return List of ignored version
+     */
+    public List<IgnoreVersion> getIgnoredVersions(Artifact artifact) {
+        Rule bestFitRule = getBestFitRule(artifact.getGroupId(), artifact.getArtifactId());
+        return Stream.concat(
+                        ruleSet.getIgnoreVersions().stream(),
+                        Optional.ofNullable(bestFitRule)
+                                .map(Rule::getIgnoreVersions)
+                                .map(Collection::stream)
+                                .orElse(Stream.empty()))
+                .filter(v -> {
+                    if (!IgnoreVersionHelper.isValidType(v)) {
+                        log.warn("The type attribute '" + v.getType() + "' for global ignoreVersion["
+                                + v + "] is not valid. Please use one of '" + IgnoreVersionHelper.VALID_TYPES
+                                + "'.");
+                        return false;
+                    }
+                    return true;
+                })
+                .collect(Collectors.toList());
+    }
+
+    /**
+     * @return rule set
+     */
+    public RuleSet getRuleSet() {
+        return ruleSet;
+    }
+
+    static boolean exactMatch(String wildcardRule, String value) {
+        Pattern p = Pattern.compile(RegexUtils.convertWildcardsToRegex(wildcardRule, true));
+        return p.matcher(value).matches();
+    }
+
+    static boolean match(String wildcardRule, String value) {
+        Pattern p = Pattern.compile(RegexUtils.convertWildcardsToRegex(wildcardRule, false));
+        return p.matcher(value).matches();
+    }
+}
diff --git a/versions-common/src/main/java/org/codehaus/mojo/versions/rule/RulesServiceBuilder.java b/versions-common/src/main/java/org/codehaus/mojo/versions/rule/RulesServiceBuilder.java
new file mode 100644
index 00000000..5586a7d1
--- /dev/null
+++ b/versions-common/src/main/java/org/codehaus/mojo/versions/rule/RulesServiceBuilder.java
@@ -0,0 +1,292 @@
+package org.codehaus.mojo.versions.rule;
+
+import javax.xml.stream.XMLStreamException;
+
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+import org.apache.maven.execution.MavenSession;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugin.logging.Log;
+import org.apache.maven.wagon.Wagon;
+import org.apache.maven.wagon.authentication.AuthenticationInfo;
+import org.apache.maven.wagon.observers.Debug;
+import org.apache.maven.wagon.proxy.ProxyInfo;
+import org.codehaus.mojo.versions.api.DefaultVersionsHelper;
+import org.codehaus.mojo.versions.model.IgnoreVersion;
+import org.codehaus.mojo.versions.model.RuleSet;
+import org.codehaus.mojo.versions.model.io.stax.RuleStaxReader;
+import org.eclipse.aether.repository.AuthenticationContext;
+import org.eclipse.aether.repository.RemoteRepository;
+
+import static java.util.Collections.singletonList;
+import static java.util.Optional.ofNullable;
+import static org.apache.commons.lang3.StringUtils.isBlank;
+
+public class RulesServiceBuilder {
+    private static final String CLASSPATH_PROTOCOL = "classpath";
+
+    private Collection<String> ignoredVersions;
+    private RuleSet ruleSet;
+    private String serverId;
+    private String rulesUri;
+    private Log log;
+    private MavenSession mavenSession;
+
+    private Map<String, Wagon> wagonMap;
+
+    public RulesServiceBuilder withIgnoredVersions(Collection<String> ignoredVersions) {
+        this.ignoredVersions = ignoredVersions;
+        return this;
+    }
+
+    public RulesServiceBuilder withRuleSet(RuleSet ruleSet) {
+        this.ruleSet = ruleSet;
+        return this;
+    }
+
+    public RulesServiceBuilder withServerId(String serverId) {
+        this.serverId = serverId;
+        return this;
+    }
+
+    public RulesServiceBuilder withRulesUri(String rulesUri) {
+        this.rulesUri = rulesUri;
+        return this;
+    }
+
+    public RulesServiceBuilder withLog(Log log) {
+        this.log = log;
+        return this;
+    }
+
+    public RulesServiceBuilder withMavenSession(MavenSession mavenSession) {
+        this.mavenSession = mavenSession;
+        return this;
+    }
+
+    public RulesServiceBuilder withWagonMap(Map<String, Wagon> wagonMap) {
+        this.wagonMap = wagonMap;
+        return this;
+    }
+
+    public RuleService build() throws MojoExecutionException {
+        assert mavenSession != null;
+        assert log != null;
+
+        RuleSet ruleSet;
+        if (this.ruleSet != null) {
+            if (!isBlank(rulesUri)) {
+                log.warn("rulesUri is ignored if rules are specified in pom or as parameters");
+            }
+            ruleSet = this.ruleSet;
+        } else {
+            ruleSet = isBlank(rulesUri)
+                    ? new RuleSet()
+                    : rulesUri.startsWith(CLASSPATH_PROTOCOL + ":")
+                            ? getRulesFromClasspath(rulesUri, log)
+                            : getRulesUsingWagon(rulesUri);
+        }
+        if (ignoredVersions != null && !ignoredVersions.isEmpty()) {
+            ruleSet = enrichRuleSet(ignoredVersions, ruleSet);
+        }
+        return new RuleService(log, ruleSet);
+    }
+
+    private static class RulesUri {
+        String basePath;
+        String resource;
+
+        private RulesUri(String basePath, String resource) {
+            this.basePath = basePath;
+            this.resource = resource;
+        }
+
+        static RulesUri build(String rulesUri) throws URISyntaxException {
+            int split = rulesUri.lastIndexOf('/');
+            return split == -1
+                    ? new RulesUri(rulesUri, "")
+                    : new RulesUri(
+                            rulesUri.substring(0, split) + '/',
+                            split + 1 < rulesUri.length() ? rulesUri.substring(split + 1) : "");
+        }
+    }
+
+    private static RuleSet getRulesFromClasspath(String uri, Log logger) throws MojoExecutionException {
+        logger.debug("Going to load rules from \"" + uri + "\"");
+        String choppedUrl = uri.substring(CLASSPATH_PROTOCOL.length() + 3);
+        URL url = DefaultVersionsHelper.class.getResource(choppedUrl);
+        if (url == null) {
+            throw new MojoExecutionException("Resource \"" + uri + "\" not found in classpath.");
+        }
+
+        try (BufferedInputStream bis = new BufferedInputStream(url.openStream())) {
+            RuleSet result = new RuleStaxReader().read(bis);
+            logger.debug("Loaded rules from \"" + uri + "\" successfully");
+            return result;
+        } catch (IOException | XMLStreamException e) {
+            throw new MojoExecutionException("Could not load specified rules from " + uri, e);
+        }
+    }
+
+    private RuleSet getRulesUsingWagon(String rulesUri) throws MojoExecutionException {
+        RulesUri uri;
+        try {
+            uri = RulesUri.build(rulesUri);
+        } catch (URISyntaxException e) {
+            log.warn("Invalid rulesUri protocol: " + e.getMessage());
+            return null;
+        }
+
+        RemoteRepository repository = remoteRepository(uri);
+        return ofNullable(wagonMap.get(repository.getProtocol()))
+                .map(wagon -> {
+                    if (log.isDebugEnabled()) {
+                        Debug debug = new Debug();
+                        wagon.addSessionListener(debug);
+                        wagon.addTransferListener(debug);
+                    }
+
+                    try {
+                        Optional<ProxyInfo> proxyInfo = getProxyInfo(repository);
+                        Optional<AuthenticationInfo> authenticationInfo = getAuthenticationInfo(repository);
+                        if (log.isDebugEnabled()) {
+                            log.debug("Connecting to remote repository \"" + repository.getId() + "\""
+                                    + proxyInfo
+                                            .map(pi -> " using proxy " + pi.getHost() + ":" + pi.getPort())
+                                            .orElse("")
+                                    + authenticationInfo
+                                            .map(ai -> " as " + ai.getUserName())
+                                            .orElse(""));
+                        }
+                        wagon.connect(
+                                wagonRepository(repository),
+                                getAuthenticationInfo(repository).orElse(null),
+                                getProxyInfo(repository).orElse(null));
+                        try {
+                            Path tempFile = Files.createTempFile("rules-", ".xml");
+                            wagon.get(uri.resource, tempFile.toFile());
+                            try (InputStream is = Files.newInputStream(tempFile)) {
+                                return new RuleStaxReader().read(is);
+                            } finally {
+                                Files.deleteIfExists(tempFile);
+                            }
+
+                        } finally {
+                            wagon.disconnect();
+                        }
+                    } catch (Exception e) {
+                        throw new RuntimeException(e);
+                    }
+                })
+                .orElseThrow(() -> new MojoExecutionException("Could not load specified rules from " + rulesUri));
+    }
+
+    private Optional<AuthenticationInfo> getAuthenticationInfo(RemoteRepository repository) {
+        return ofNullable(repository.getAuthentication()).map(authentication -> new AuthenticationInfo() {
+            {
+                try (AuthenticationContext authCtx =
+                        AuthenticationContext.forRepository(mavenSession.getRepositorySession(), repository)) {
+                    ofNullable(authCtx.get(AuthenticationContext.USERNAME)).ifPresent(this::setUserName);
+                    ofNullable(authCtx.get(AuthenticationContext.PASSWORD)).ifPresent(this::setPassword);
+                    ofNullable(authCtx.get(AuthenticationContext.PRIVATE_KEY_PASSPHRASE))
+                            .ifPresent(this::setPassphrase);
+                    ofNullable(authCtx.get(AuthenticationContext.PRIVATE_KEY_PATH))
+                            .ifPresent(this::setPrivateKey);
+                }
+            }
+        });
+    }
+
+    private org.apache.maven.wagon.repository.Repository wagonRepository(RemoteRepository repository) {
+        return new org.apache.maven.wagon.repository.Repository(repository.getId(), repository.getUrl());
+    }
+
+    private Optional<ProxyInfo> getProxyInfo(RemoteRepository repository) {
+        return ofNullable(repository.getProxy()).map(proxy -> new ProxyInfo() {
+            {
+                setHost(proxy.getHost());
+                setPort(proxy.getPort());
+                setType(proxy.getType());
+                ofNullable(proxy.getAuthentication()).ifPresent(auth -> {
+                    try (AuthenticationContext authCtx =
+                            AuthenticationContext.forProxy(mavenSession.getRepositorySession(), repository)) {
+                        ofNullable(authCtx.get(AuthenticationContext.USERNAME)).ifPresent(this::setUserName);
+                        ofNullable(authCtx.get(AuthenticationContext.PASSWORD)).ifPresent(this::setPassword);
+                        ofNullable(authCtx.get(AuthenticationContext.NTLM_DOMAIN))
+                                .ifPresent(this::setNtlmDomain);
+                        ofNullable(authCtx.get(AuthenticationContext.NTLM_WORKSTATION))
+                                .ifPresent(this::setNtlmHost);
+                    }
+                });
+            }
+        });
+    }
+
+    private RemoteRepository remoteRepository(RulesUri uri) {
+        RemoteRepository prototype = new RemoteRepository.Builder(serverId, null, uri.basePath).build();
+        RemoteRepository.Builder builder = new RemoteRepository.Builder(prototype);
+        ofNullable(mavenSession.getRepositorySession().getProxySelector().getProxy(prototype))
+                .ifPresent(builder::setProxy);
+        ofNullable(mavenSession
+                        .getRepositorySession()
+                        .getAuthenticationSelector()
+                        .getAuthentication(prototype))
+                .ifPresent(builder::setAuthentication);
+        ofNullable(mavenSession.getRepositorySession().getMirrorSelector().getMirror(prototype))
+                .ifPresent(mirror -> builder.setMirroredRepositories(singletonList(mirror)));
+        return builder.build();
+    }
+
+    /**
+     * <p>Creates the enriched version of the ruleSet given as parameter; the ruleSet will contain the
+     * set of ignored versions passed on top of its own (if defined).</p>
+     *
+     * <p>If the {@code originalRuleSet} is {@code null}, a new {@linkplain RuleSet} will be created as
+     * a result.</p>
+     *
+     * <p><em>The method does not change the {@code originalRuleSet} object.</em></p>
+     *
+     * @param ignoredVersions collection of ignored version to enrich the clone of the original rule set
+     * @param originalRuleSet original rule set
+     * @return new RuleSet object containing the (if passed) cloned version of the rule set, enriched with
+     *         the given set of ignored versions
+     */
+    @SuppressWarnings("checkstyle:AvoidNestedBlocks")
+    private static RuleSet enrichRuleSet(Collection<String> ignoredVersions, RuleSet originalRuleSet) {
+        RuleSet ruleSet = new RuleSet();
+        if (originalRuleSet != null) {
+            if (originalRuleSet.getRules() != null) {
+                ruleSet.setRules(new ArrayList<>(originalRuleSet.getRules()));
+            }
+            if (originalRuleSet.getIgnoreVersions() != null) {
+                ruleSet.setIgnoreVersions(new ArrayList<>(originalRuleSet.getIgnoreVersions()));
+            }
+        }
+
+        if (ruleSet.getIgnoreVersions() == null) {
+            ruleSet.setIgnoreVersions(new ArrayList<>());
+        }
+        ruleSet.getIgnoreVersions()
+                .addAll(ignoredVersions.stream()
+                        .map(v -> {
+                            IgnoreVersion ignoreVersion = new IgnoreVersion();
+                            ignoreVersion.setType(IgnoreVersion.TYPE_REGEX);
+                            ignoreVersion.setVersion(v);
+                            return ignoreVersion;
+                        })
+                        .collect(Collectors.toList()));
+
+        return ruleSet;
+    }
+}
diff --git a/versions-common/src/main/java/org/codehaus/mojo/versions/utils/PluginComparator.java b/versions-common/src/main/java/org/codehaus/mojo/versions/utils/PluginComparator.java
index 221ccaf2..01219cb5 100644
--- a/versions-common/src/main/java/org/codehaus/mojo/versions/utils/PluginComparator.java
+++ b/versions-common/src/main/java/org/codehaus/mojo/versions/utils/PluginComparator.java
@@ -32,6 +32,10 @@ import org.apache.maven.model.ReportPlugin;
 public enum PluginComparator implements Comparator<Object> {
     INSTANCE;
 
+    private static boolean isPluginOrReportPlugin(Object o) {
+        return o instanceof Plugin || o instanceof ReportPlugin;
+    }
+
     /**
      * Compares to {@link Plugin} or {@link ReportPlugin} instances.
      *
@@ -42,14 +46,11 @@ public enum PluginComparator implements Comparator<Object> {
      * @since 1.0-beta-1
      */
     public int compare(Object o1, Object o2) {
-        if (!(o1 instanceof Plugin || o1 instanceof ReportPlugin)) {
-            throw new IllegalArgumentException(
-                    "This comparator can only be used to compare Plugin and ReportPlugin instances");
-        }
-        if (!(o2 instanceof Plugin || o2 instanceof ReportPlugin)) {
+        if (!isPluginOrReportPlugin(o1) || !isPluginOrReportPlugin(o2)) {
             throw new IllegalArgumentException(
                     "This comparator can only be used to compare Plugin and ReportPlugin instances");
         }
+
         String g1 = o1 instanceof Plugin ? ((Plugin) o1).getGroupId() : ((ReportPlugin) o1).getGroupId();
         String g2 = o2 instanceof Plugin ? ((Plugin) o2).getGroupId() : ((ReportPlugin) o2).getGroupId();
 
diff --git a/versions-enforcer/src/main/java/org/codehaus/mojo/versions/enforcer/MaxDependencyUpdates.java b/versions-enforcer/src/main/java/org/codehaus/mojo/versions/enforcer/MaxDependencyUpdates.java
index a3266791..3f5ca276 100644
--- a/versions-enforcer/src/main/java/org/codehaus/mojo/versions/enforcer/MaxDependencyUpdates.java
+++ b/versions-enforcer/src/main/java/org/codehaus/mojo/versions/enforcer/MaxDependencyUpdates.java
@@ -38,6 +38,7 @@ import org.apache.maven.execution.MavenSession;
 import org.apache.maven.model.Dependency;
 import org.apache.maven.plugin.MojoExecution;
 import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugin.logging.Log;
 import org.apache.maven.project.MavenProject;
 import org.apache.maven.wagon.Wagon;
 import org.codehaus.mojo.versions.api.ArtifactVersions;
@@ -47,6 +48,8 @@ import org.codehaus.mojo.versions.api.Segment;
 import org.codehaus.mojo.versions.api.VersionRetrievalException;
 import org.codehaus.mojo.versions.api.VersionsHelper;
 import org.codehaus.mojo.versions.model.RuleSet;
+import org.codehaus.mojo.versions.rule.RuleService;
+import org.codehaus.mojo.versions.rule.RulesServiceBuilder;
 import org.codehaus.mojo.versions.utils.ArtifactFactory;
 import org.codehaus.mojo.versions.utils.DependencyComparator;
 import org.codehaus.mojo.versions.utils.VersionsExpressionEvaluator;
@@ -297,20 +300,25 @@ public class MaxDependencyUpdates extends AbstractEnforcerRule {
     private VersionsHelper createVersionsHelper(String serverId, String rulesUri, RuleSet ruleSet)
             throws EnforcerRuleError {
         try {
-            PomHelper pomHelper =
-                    new PomHelper(artifactFactory, new VersionsExpressionEvaluator(mavenSession, mojoExecution));
-            return new DefaultVersionsHelper.Builder()
-                    .withArtifactFactory(artifactFactory)
-                    .withRepositorySystem(repositorySystem)
+            Log log = new PluginLogWrapper(getLog());
+            RuleService ruleService = new RulesServiceBuilder()
                     .withWagonMap(wagonMap)
                     .withServerId(serverId)
                     .withRulesUri(rulesUri)
                     .withRuleSet(ruleSet)
                     .withIgnoredVersions(null)
-                    .withLog(new PluginLogWrapper(getLog()))
+                    .withLog(log)
+                    .withMavenSession(mavenSession)
+                    .build();
+            PomHelper pomHelper =
+                    new PomHelper(artifactFactory, new VersionsExpressionEvaluator(mavenSession, mojoExecution));
+            return new DefaultVersionsHelper.Builder()
+                    .withArtifactFactory(artifactFactory)
+                    .withRepositorySystem(repositorySystem)
+                    .withLog(log)
                     .withMavenSession(mavenSession)
                     .withPomHelper(pomHelper)
-                    .withMojoExecution(mojoExecution)
+                    .withRuleService(ruleService)
                     .build();
         } catch (MojoExecutionException e) {
             throw new EnforcerRuleError("Cannot resolve dependency", e);
diff --git a/versions-maven-plugin/src/it/setup-provide-rules-in-jar/src/main/resources/package/foo/bar/rules.xml b/versions-maven-plugin/src/it/setup-provide-rules-in-jar/src/main/resources/package/foo/bar/rules.xml
index 3e7699c7..bf1cebc6 100644
--- a/versions-maven-plugin/src/it/setup-provide-rules-in-jar/src/main/resources/package/foo/bar/rules.xml
+++ b/versions-maven-plugin/src/it/setup-provide-rules-in-jar/src/main/resources/package/foo/bar/rules.xml
@@ -1,5 +1,1 @@
-<ruleset comparisonMethod="maven">
-  <rules>
-    <rule groupId="localhost" artifactId="version-rules" comparisonMethod="mercury"/>
-  </rules>
-</ruleset>
+<ruleset/>
diff --git a/versions-maven-plugin/src/main/java/org/codehaus/mojo/versions/AbstractVersionsReport.java b/versions-maven-plugin/src/main/java/org/codehaus/mojo/versions/AbstractVersionsReport.java
index 97b98c62..d9416920 100644
--- a/versions-maven-plugin/src/main/java/org/codehaus/mojo/versions/AbstractVersionsReport.java
+++ b/versions-maven-plugin/src/main/java/org/codehaus/mojo/versions/AbstractVersionsReport.java
@@ -35,6 +35,8 @@ import org.codehaus.mojo.versions.api.PomHelper;
 import org.codehaus.mojo.versions.api.VersionsHelper;
 import org.codehaus.mojo.versions.model.RuleSet;
 import org.codehaus.mojo.versions.reporting.ReportRendererFactory;
+import org.codehaus.mojo.versions.rule.RuleService;
+import org.codehaus.mojo.versions.rule.RulesServiceBuilder;
 import org.codehaus.mojo.versions.utils.ArtifactFactory;
 import org.codehaus.mojo.versions.utils.VersionsExpressionEvaluator;
 import org.codehaus.plexus.i18n.I18N;
@@ -86,16 +88,6 @@ public abstract class AbstractVersionsReport<T> extends AbstractMavenReport {
     @Parameter(property = "maven.version.rules")
     private String rulesUri;
 
-    /**
-     * The versioning rule to use when comparing versions. Valid values are <code>maven</code>, <code>numeric</code>
-     * which will handle long version numbers provided all components are numeric, or <code>mercury</code> which will
-     * use the mercury version number comparison rules.
-     *
-     * @since 1.0-alpha-1
-     */
-    @Parameter(property = "comparisonMethod")
-    protected String comparisonMethod;
-
     /**
      * Whether to allow snapshots when searching for the latest version of an artifact.
      *
@@ -178,11 +170,7 @@ public abstract class AbstractVersionsReport<T> extends AbstractMavenReport {
     public VersionsHelper getHelper() throws MavenReportException {
         if (helper == null) {
             try {
-                PomHelper pomHelper =
-                        new PomHelper(artifactFactory, new VersionsExpressionEvaluator(session, mojoExecution));
-                helper = new DefaultVersionsHelper.Builder()
-                        .withArtifactFactory(artifactFactory)
-                        .withRepositorySystem(repositorySystem)
+                RuleService ruleService = new RulesServiceBuilder()
                         .withWagonMap(wagonMap)
                         .withServerId(serverId)
                         .withRulesUri(rulesUri)
@@ -190,8 +178,16 @@ public abstract class AbstractVersionsReport<T> extends AbstractMavenReport {
                         .withIgnoredVersions(ignoredVersions)
                         .withLog(getLog())
                         .withMavenSession(session)
+                        .build();
+                PomHelper pomHelper =
+                        new PomHelper(artifactFactory, new VersionsExpressionEvaluator(session, mojoExecution));
+                helper = new DefaultVersionsHelper.Builder()
+                        .withArtifactFactory(artifactFactory)
+                        .withRepositorySystem(repositorySystem)
+                        .withLog(getLog())
+                        .withMavenSession(session)
                         .withPomHelper(pomHelper)
-                        .withMojoExecution(mojoExecution)
+                        .withRuleService(ruleService)
                         .build();
             } catch (MojoExecutionException e) {
                 throw new MavenReportException(e.getMessage(), e);
diff --git a/versions-maven-plugin/src/main/java/org/codehaus/mojo/versions/AbstractVersionsUpdaterMojo.java b/versions-maven-plugin/src/main/java/org/codehaus/mojo/versions/AbstractVersionsUpdaterMojo.java
index 8f9911eb..2322d051 100644
--- a/versions-maven-plugin/src/main/java/org/codehaus/mojo/versions/AbstractVersionsUpdaterMojo.java
+++ b/versions-maven-plugin/src/main/java/org/codehaus/mojo/versions/AbstractVersionsUpdaterMojo.java
@@ -58,6 +58,8 @@ import org.codehaus.mojo.versions.api.recording.ChangeRecorder;
 import org.codehaus.mojo.versions.model.RuleSet;
 import org.codehaus.mojo.versions.ordering.InvalidSegmentException;
 import org.codehaus.mojo.versions.rewriting.MutableXMLStreamReader;
+import org.codehaus.mojo.versions.rule.RuleService;
+import org.codehaus.mojo.versions.rule.RulesServiceBuilder;
 import org.codehaus.mojo.versions.utils.ArtifactFactory;
 import org.codehaus.mojo.versions.utils.VersionsExpressionEvaluator;
 import org.eclipse.aether.RepositorySystem;
@@ -207,20 +209,24 @@ public abstract class AbstractVersionsUpdaterMojo extends AbstractMojo {
 
     public VersionsHelper getHelper() throws MojoExecutionException {
         if (helper == null) {
-            PomHelper pomHelper =
-                    new PomHelper(artifactFactory, new VersionsExpressionEvaluator(session, mojoExecution));
-            helper = new DefaultVersionsHelper.Builder()
-                    .withArtifactFactory(artifactFactory)
-                    .withRepositorySystem(repositorySystem)
+            RuleService ruleService = new RulesServiceBuilder()
+                    .withMavenSession(session)
                     .withWagonMap(wagonMap)
                     .withServerId(serverId)
                     .withRulesUri(rulesUri)
                     .withRuleSet(ruleSet)
                     .withIgnoredVersions(ignoredVersions)
                     .withLog(getLog())
+                    .build();
+            PomHelper pomHelper =
+                    new PomHelper(artifactFactory, new VersionsExpressionEvaluator(session, mojoExecution));
+            helper = new DefaultVersionsHelper.Builder()
+                    .withArtifactFactory(artifactFactory)
+                    .withRepositorySystem(repositorySystem)
+                    .withLog(getLog())
                     .withMavenSession(session)
                     .withPomHelper(pomHelper)
-                    .withMojoExecution(mojoExecution)
+                    .withRuleService(ruleService)
                     .build();
         }
         return helper;
diff --git a/versions-maven-plugin/src/main/java/org/codehaus/mojo/versions/DisplayPluginUpdatesMojo.java b/versions-maven-plugin/src/main/java/org/codehaus/mojo/versions/DisplayPluginUpdatesMojo.java
index 7b0625a4..a20ec4c6 100644
--- a/versions-maven-plugin/src/main/java/org/codehaus/mojo/versions/DisplayPluginUpdatesMojo.java
+++ b/versions-maven-plugin/src/main/java/org/codehaus/mojo/versions/DisplayPluginUpdatesMojo.java
@@ -85,7 +85,6 @@ import org.apache.maven.wagon.Wagon;
 import org.codehaus.mojo.versions.api.PomHelper;
 import org.codehaus.mojo.versions.api.VersionRetrievalException;
 import org.codehaus.mojo.versions.api.recording.ChangeRecorder;
-import org.codehaus.mojo.versions.ordering.MavenVersionComparator;
 import org.codehaus.mojo.versions.rewriting.MutableXMLStreamReader;
 import org.codehaus.mojo.versions.utils.ArtifactFactory;
 import org.codehaus.mojo.versions.utils.ArtifactVersionService;
@@ -371,7 +370,7 @@ public class DisplayPluginUpdatesMojo extends AbstractVersionsDisplayMojo {
         ArtifactVersion minMavenVersion = null;
         boolean superPomDrivingMinVersion = false;
         // if Maven prerequisite upgraded to a version, Map<plugin compact key, latest compatible plugin vesion>
-        Map<ArtifactVersion, Map<String, String>> mavenUpgrades = new TreeMap<>(new MavenVersionComparator());
+        Map<ArtifactVersion, Map<String, String>> mavenUpgrades = new TreeMap<>();
 
         for (Plugin plugin : plugins) {
             String coords = ArtifactUtils.versionlessKey(plugin.getGroupId(), plugin.getArtifactId());
diff --git a/versions-maven-plugin/src/main/java/org/codehaus/mojo/versions/LockSnapshotsMojo.java b/versions-maven-plugin/src/main/java/org/codehaus/mojo/versions/LockSnapshotsMojo.java
index fa69d503..9ab4d1a1 100644
--- a/versions-maven-plugin/src/main/java/org/codehaus/mojo/versions/LockSnapshotsMojo.java
+++ b/versions-maven-plugin/src/main/java/org/codehaus/mojo/versions/LockSnapshotsMojo.java
@@ -40,6 +40,7 @@ import org.apache.maven.plugins.annotations.Parameter;
 import org.apache.maven.project.MavenProject;
 import org.apache.maven.wagon.Wagon;
 import org.codehaus.mojo.versions.api.PomHelper;
+import org.codehaus.mojo.versions.api.VersionsHelper;
 import org.codehaus.mojo.versions.api.recording.ChangeRecorder;
 import org.codehaus.mojo.versions.rewriting.MutableXMLStreamReader;
 import org.codehaus.mojo.versions.utils.ArtifactFactory;
@@ -243,9 +244,11 @@ public class LockSnapshotsMojo extends AbstractVersionsDependencyUpdaterMojo {
      *
      * @param dep dependency for which to retrieve the locked version
      * @return The timestamp version if exists, otherwise {@link Optional#empty()}
+     * @throws MojoExecutionException thrown if retrieval of {@link VersionsHelper} fails
      * @throws VersionResolutionException thrown if version resolution fails
      */
-    private Optional<String> resolveSnapshotVersion(Dependency dep) throws VersionResolutionException {
+    private Optional<String> resolveSnapshotVersion(Dependency dep)
+            throws MojoExecutionException, VersionResolutionException {
         return resolveSnapshotVersion(artifactFactory.createArtifact(dep));
     }
 }
diff --git a/versions-maven-plugin/src/main/java/org/codehaus/mojo/versions/reporting/ReportRendererFactoryImpl.java b/versions-maven-plugin/src/main/java/org/codehaus/mojo/versions/reporting/ReportRendererFactoryImpl.java
index 2e6c0c21..fd9afa4f 100644
--- a/versions-maven-plugin/src/main/java/org/codehaus/mojo/versions/reporting/ReportRendererFactoryImpl.java
+++ b/versions-maven-plugin/src/main/java/org/codehaus/mojo/versions/reporting/ReportRendererFactoryImpl.java
@@ -61,22 +61,24 @@ public class ReportRendererFactoryImpl implements ReportRendererFactory {
     public <T extends ReportRenderer, U> T createReportRenderer(
             String reportName, Sink sink, Locale locale, U model, boolean allowSnapshots)
             throws IllegalArgumentException {
-        if (DEPENDENCY_UPDATES_REPORT.equals(reportName) || DEPENDENCY_UPDATES_AGGREGATE_REPORT.equals(reportName)) {
-            return (T) new DependencyUpdatesReportRenderer<>(
-                    i18N, sink, locale, reportName, (DependencyUpdatesModel) model, allowSnapshots);
+        switch (reportName) {
+            case DEPENDENCY_UPDATES_REPORT:
+            case DEPENDENCY_UPDATES_AGGREGATE_REPORT:
+                return (T) new DependencyUpdatesReportRenderer<>(
+                        i18N, sink, locale, reportName, (DependencyUpdatesModel) model, allowSnapshots);
+            case PLUGIN_UPDATES_REPORT:
+            case PLUGIN_UPDATES_AGGREGATE_REPORT:
+                return (T) new PluginUpdatesReportRenderer(
+                        i18N, sink, locale, reportName, (PluginUpdatesModel) model, allowSnapshots);
+            case PROPERTY_UPDATES_REPORT:
+            case PROPERTY_UPDATES_AGGREGATE_REPORT:
+                return (T) new PropertyUpdatesReportRenderer(
+                        i18N, sink, locale, reportName, (PropertyUpdatesModel) model, allowSnapshots);
+            case PARENT_UPDATES_REPORT:
+                return (T) new ParentUpdatesReportRenderer(
+                        i18N, sink, locale, reportName, (ParentUpdatesModel) model, allowSnapshots);
+            default:
+                throw new IllegalArgumentException("Invalid report name: " + reportName);
         }
-        if (PLUGIN_UPDATES_REPORT.equals(reportName) || PLUGIN_UPDATES_AGGREGATE_REPORT.equals(reportName)) {
-            return (T) new PluginUpdatesReportRenderer(
-                    i18N, sink, locale, reportName, (PluginUpdatesModel) model, allowSnapshots);
-        }
-        if (PROPERTY_UPDATES_REPORT.equals(reportName) || PROPERTY_UPDATES_AGGREGATE_REPORT.equals(reportName)) {
-            return (T) new PropertyUpdatesReportRenderer(
-                    i18N, sink, locale, reportName, (PropertyUpdatesModel) model, allowSnapshots);
-        }
-        if (PARENT_UPDATES_REPORT.equals(reportName)) {
-            return (T) new ParentUpdatesReportRenderer(
-                    i18N, sink, locale, reportName, (ParentUpdatesModel) model, allowSnapshots);
-        }
-        throw new IllegalArgumentException("Invalid report name: " + reportName);
     }
 }
diff --git a/versions-maven-plugin/src/site/markdown/version-rules.md.vm b/versions-maven-plugin/src/site/markdown/version-rules.md.vm
index e17dcdd6..dd4fa3f8 100644
--- a/versions-maven-plugin/src/site/markdown/version-rules.md.vm
+++ b/versions-maven-plugin/src/site/markdown/version-rules.md.vm
@@ -47,10 +47,8 @@ If your version numbers do not match this scheme, you may face issues with
 - goals in this plugin that sort versions, for example update-properties (you can do things to help with these
   kinds of problems)
 
-The `versions-maven-plugin` knows three rules for comparing version numbers:
-- *maven*, The standard Maven 2.x and 3.x version numbering scheme.
-- *numeric*, An alternative version numbering scheme with no "special" qualifiers.
-- *mercury*, An alternative version numbering scheme, now defunct.
+Since version 2.19.0, `versions-maven-plugin` only uses the Maven version numbering scheme, explained above.
+*numeric* and *mercury* version schemes have been abandoned.
 
 The `versions-maven-plugin` will assume that all version numbers follow the `maven` scheme unless you tell
 it otherwise.
@@ -58,7 +56,7 @@ it otherwise.
 rules.xml
 ---------
 
-To specify the version schemes to use, you may define a [rule-set xml file](../versions-model/rule.html), use the `ruleSet`
+To specify ignored versions, you may define a [rule-set xml file](../versions-model/rule.html), use the `ruleSet`
 element in the `versions-maven-plugin` plugin configuration, or specify ignored versions via
 the `maven.version.ignore` property.
 
@@ -67,15 +65,22 @@ $h3 Using the `rules.xml` file
 The `rules.xml` file should conform to the xsd specified in the example below.
 
 ```xml
-<ruleset comparisonMethod="maven"
-         xmlns="https://www.mojohaus.org/VERSIONS/RULE/2.1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-         xsi:schemaLocation="https://www.mojohaus.org/VERSIONS/RULE/2.1.0 https://www.mojohaus.org/versions/versions-model/xsd/rule-2.1.0.xsd">
+<ruleset xmlns="https://www.mojohaus.org/VERSIONS/RULE/3.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="https://www.mojohaus.org/VERSIONS/RULE/3.0.0 https://www.mojohaus.org/versions/versions-model/xsd/rule-3.0.0.xsd">
   <rules>
-    <rule groupId="*.maven" comparisonMethod="mercury"/>
-    <rule groupId="com.mycompany" comparisonMethod="numeric"/>
-    <rule groupId="com.mycompany.maven" comparisonMethod="maven"/>
-    <rule groupId="com.mycompany.maven" artifactId="old-maven-plugin" comparisonMethod="mercury"/>
+    <groupId>org.codehaus.mojo</groupId>
+    <artifactId>versions-maven-plugin</artifactId>
+    <ignoreVersions>
+      <type>range</type>
+      <version>(,2.16.0]</version>
+    </ignoreVersions>
   </rules>
+  <ignoreVersions>
+    <ignoreVersion>
+      <type>regex</type>
+      <version>(.+-SNAPSHOT|.+-M\d)</version>
+    </ignoreVersion>
+  </ignoreVersions>
 </ruleset>
 ```
 
@@ -141,9 +146,6 @@ the `rules.xml` file:
               <ignoreVersions>
                 ...
               </ignoreVersions>
-
-              <!-- optional, comparison method for the rule, one of: maven (default), numeric, mercury -->
-              <comparisonMethod>maven</comparisonMethod>
             </rule>
           </rules>
       </ruleSet>
@@ -167,14 +169,13 @@ The other methods (via the `<ruleSet>` element and via the `maven.version.ignore
 explained above. The described
 
 ```xml
-<ruleset comparisonMethod="maven"
-         xmlns="https://www.mojohaus.org/VERSIONS/RULE/2.1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-         xsi:schemaLocation="https://www.mojohaus.org/VERSIONS/RULE/2.1.0 https://www.mojohaus.org/versions/versions-model/xsd/rule-2.1.0.xsd">
+<ruleset xmlns="https://www.mojohaus.org/VERSIONS/RULE/3.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="https://www.mojohaus.org/VERSIONS/RULE/3.0.0 https://www.mojohaus.org/versions/versions-model/xsd/rule-3.0.0.xsd">
   <ignoreVersions>
     <ignoreVersion type="regex">.*-beta</ignoreVersion>
   </ignoreVersions>
   <rules>
-    <rule groupId="com.mycompany.maven" comparisonMethod="maven">
+    <rule groupId="com.mycompany.maven">
       <ignoreVersions>
         <ignoreVersion type="regex">.*-RELEASE</ignoreVersion>
         <ignoreVersion type="range">[2.0.0,)</ignoreVersion>
diff --git a/versions-model/src/main/mdo/rule.mdo b/versions-model/src/main/mdo/rule.mdo
index 93912c77..292c0502 100644
--- a/versions-model/src/main/mdo/rule.mdo
+++ b/versions-model/src/main/mdo/rule.mdo
@@ -76,13 +76,6 @@ under the License.
             A rule with ? wildcards will override a rule with * wildcards.
           </description>
         </field>
-        <field xml.attribute="true">
-          <name>comparisonMethod</name>
-          <version>1.0+</version>
-          <required>true</required>
-          <type>String</type>
-          <description>The comparison method that this rule specifies.</description>
-        </field>
       </fields>
       <codeSegments>
         <codeSegment>
@@ -95,7 +88,6 @@ under the License.
     {
         // enables no-arg construction
         artifactId = "*";
-        comparisonMethod = "maven";
     }
 
           ]]></code>
@@ -110,8 +102,6 @@ under the License.
         buf.append( groupId );
         buf.append( "\", artifactId = \"" );
         buf.append( artifactId );
-        buf.append( "\", comparisonMethod = \"" );
-        buf.append( comparisonMethod );
         buf.append( "\", ignoreVersions = \"" );
         buf.append( ignoreVersions );
         buf.append( "\"]" );
@@ -148,40 +138,17 @@ under the License.
             <multiplicity>*</multiplicity>
           </association>
         </field>
-        <field xml.attribute="true">
-          <name>comparisonMethod</name>
-          <version>1.0+</version>
-          <required>true</required>
-          <type>String</type>
-          <description>The comparison method to use when no rule matches.</description>
-        </field>
       </fields>
       <codeSegments>
         <codeSegment>
           <version>1.0+</version>
           <code><![CDATA[
-    /**
-     * Creates a new empty rule set.
-     */
-    public RuleSet()
-    {
-        // enables no-arg construction
-        comparisonMethod = "maven";
-    }
-
-          ]]></code>
-        </codeSegment>
-        <codeSegment>
-          <version>1.0+</version>
-          <code><![CDATA[
     public String toString()
     {
         StringBuilder buf = new StringBuilder( 128 );
         buf.append( "RuleSet[rules = " );
         buf.append( rules );
-        buf.append( ", comparisonMethod = \"" );
-        buf.append( comparisonMethod );
-        buf.append( "\"]" );
+        buf.append( "]" );
         return buf.toString();
     }
           ]]></code>
diff --git a/versions-model/src/site/markdown/xsd/index.md b/versions-model/src/site/markdown/xsd/index.md
index e8269808..2fe238a0 100644
--- a/versions-model/src/site/markdown/xsd/index.md
+++ b/versions-model/src/site/markdown/xsd/index.md
@@ -7,6 +7,7 @@ date: 2022-10-17
 Versions - XML schemas for ruleSet
 ==================================
 
-- [rule-2.1.0.xsd](rule-2.1.0.xsd)
+- [rule-3.0.0.xsd](rule-3.0.0.xsd)
+- [rule-2.1.0.xsd](https://www.mojohaus.org/versions-maven-plugin/xsd/rule-2.1.0.xsd)
 - [rule-2.0.0.xsd](https://www.mojohaus.org/versions-maven-plugin/xsd/rule-2.0.0.xsd)
 


Test patch:
diff --git a/versions-common/src/test/java/org/codehaus/mojo/versions/api/AbstractVersionDetailsTest.java b/versions-common/src/test/java/org/codehaus/mojo/versions/api/AbstractVersionDetailsTest.java
index 618f575f..3bc5d73c 100644
--- a/versions-common/src/test/java/org/codehaus/mojo/versions/api/AbstractVersionDetailsTest.java
+++ b/versions-common/src/test/java/org/codehaus/mojo/versions/api/AbstractVersionDetailsTest.java
@@ -19,8 +19,6 @@ import org.apache.maven.artifact.versioning.ArtifactVersion;
 import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
 import org.apache.maven.artifact.versioning.VersionRange;
 import org.codehaus.mojo.versions.ordering.InvalidSegmentException;
-import org.codehaus.mojo.versions.ordering.MavenVersionComparator;
-import org.codehaus.mojo.versions.ordering.VersionComparator;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
@@ -42,11 +40,6 @@ class AbstractVersionDetailsTest {
     @BeforeEach
     void setUp() {
         instance = new AbstractVersionDetails() {
-            @Override
-            public VersionComparator getVersionComparator() {
-                return new MavenVersionComparator();
-            }
-
             @Override
             public ArtifactVersion[] getVersions(boolean includeSnapshots) {
                 return new ArtifactVersion[0];
diff --git a/versions-common/src/test/java/org/codehaus/mojo/versions/api/ArtifactVersionsTest.java b/versions-common/src/test/java/org/codehaus/mojo/versions/api/ArtifactVersionsTest.java
index 8dd5eaeb..00deb3d7 100644
--- a/versions-common/src/test/java/org/codehaus/mojo/versions/api/ArtifactVersionsTest.java
+++ b/versions-common/src/test/java/org/codehaus/mojo/versions/api/ArtifactVersionsTest.java
@@ -25,13 +25,9 @@ import java.util.Optional;
 import org.apache.maven.artifact.DefaultArtifact;
 import org.apache.maven.artifact.handler.DefaultArtifactHandler;
 import org.apache.maven.artifact.versioning.ArtifactVersion;
-import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
 import org.apache.maven.artifact.versioning.Restriction;
 import org.apache.maven.artifact.versioning.VersionRange;
 import org.codehaus.mojo.versions.ordering.InvalidSegmentException;
-import org.codehaus.mojo.versions.ordering.MavenVersionComparator;
-import org.codehaus.mojo.versions.ordering.MercuryVersionComparator;
-import org.codehaus.mojo.versions.ordering.VersionComparator;
 import org.junit.jupiter.api.Test;
 
 import static java.util.Optional.of;
@@ -53,7 +49,8 @@ import static org.junit.jupiter.api.Assertions.assertNull;
 
 class ArtifactVersionsTest {
 
-    private void test4DigitVersion(VersionComparator comparator) throws InvalidVersionSpecificationException {
+    @Test
+    void test4DigitVersionsMaven() throws Exception {
         ArtifactVersions instance = new ArtifactVersions(
                 new DefaultArtifact(
                         "group",
@@ -63,9 +60,8 @@ class ArtifactVersionsTest {
                         "bar",
                         "jar",
                         new DefaultArtifactHandler()),
-                Arrays.asList(
-                        versions("1.0.0.1", "1.0.0.2", "2.121.2.1", "2.100.0.1", "3.1.0.1", "1.1.1", "2.0.0-SNAPSHOT")),
-                comparator);
+                Arrays.asList(versions(
+                        "1.0.0.1", "1.0.0.2", "2.121.2.1", "2.100.0.1", "3.1.0.1", "1.1.1", "2.0.0-SNAPSHOT")));
         assertEquals("artifact", instance.getArtifactId());
         assertEquals("group", instance.getGroupId());
         assertThat(
@@ -100,16 +96,6 @@ class ArtifactVersionsTest {
                 instance.getNewestVersion(new Restriction(version("1.1.1"), false, version("2.0"), false), true));
     }
 
-    @Test
-    void test4DigitVersionsMercury() throws Exception {
-        test4DigitVersion(new MercuryVersionComparator());
-    }
-
-    @Test
-    void test4DigitVersionsMaven() throws Exception {
-        test4DigitVersion(new MavenVersionComparator());
-    }
-
     @Test
     void testIsEmpty() throws Exception {
         ArtifactVersions instance = new ArtifactVersions(
@@ -121,8 +107,7 @@ class ArtifactVersionsTest {
                         "bar",
                         "jar",
                         new DefaultArtifactHandler()),
-                Arrays.asList(versions("1.0.1-SNAPSHOT", "1.0.2-SNAPSHOT")),
-                new MavenVersionComparator());
+                Arrays.asList(versions("1.0.1-SNAPSHOT", "1.0.2-SNAPSHOT")));
         assertThat(instance.isEmpty(false), is(true));
         assertThat(instance.isEmpty(true), is(false));
     }
@@ -138,8 +123,7 @@ class ArtifactVersionsTest {
                         "bar",
                         "jar",
                         new DefaultArtifactHandler()),
-                Arrays.asList(versions("1.0", "3.0", "1.1", "1.0", "1.0.1")),
-                new MavenVersionComparator());
+                Arrays.asList(versions("1.0", "3.0", "1.1", "1.0", "1.0.1")));
         assertEquals("artifact", instance.getArtifactId());
         assertEquals("group", instance.getGroupId());
         assertArrayEquals(versions("1.0", "1.0.1", "1.1", "3.0"), instance.getVersions(true));
@@ -180,8 +164,7 @@ class ArtifactVersionsTest {
                         "3.0",
                         "3.1.1-SNAPSHOT",
                         "3.1.5-SNAPSHOT",
-                        "3.4.0-SNAPSHOT")),
-                new MavenVersionComparator());
+                        "3.4.0-SNAPSHOT")));
 
         assertThat(instance.getNewestUpdateWithinSegment(of(SUBINCREMENTAL), false), hasToString("1.1.0-2"));
         assertThat(instance.getNewestUpdateWithinSegment(of(INCREMENTAL), false), hasToString("1.1.3"));
@@ -191,8 +174,7 @@ class ArtifactVersionsTest {
     void testGetNewerVersionsWithSnapshot() throws InvalidSegmentException {
         ArtifactVersions instance = new ArtifactVersions(
                 new DefaultArtifact("default-group", "dummy-api", "1.0.0-SNAPSHOT", "foo", "bar", "jar", null),
-                Arrays.asList(versions("1.0.0-SNAPSHOT", "1.0.0")),
-                new MavenVersionComparator());
+                Arrays.asList(versions("1.0.0-SNAPSHOT", "1.0.0")));
 
         assertThat(
                 instance.getNewerVersions("1.0.0-SNAPSHOT", of(SUBINCREMENTAL), false, false),
@@ -202,8 +184,7 @@ class ArtifactVersionsTest {
     private static ArtifactVersions createInstance(ArtifactVersion[] versions) {
         return new ArtifactVersions(
                 new DefaultArtifact("default-group", "dummy-api", "1.0.0", "foo", "bar", "jar", null),
-                Arrays.asList(versions),
-                new MavenVersionComparator());
+                Arrays.asList(versions));
     }
 
     @Test
diff --git a/versions-common/src/test/java/org/codehaus/mojo/versions/api/DefaultVersionsHelperTest.java b/versions-common/src/test/java/org/codehaus/mojo/versions/api/DefaultVersionsHelperTest.java
index 87ea4ee7..cd406076 100644
--- a/versions-common/src/test/java/org/codehaus/mojo/versions/api/DefaultVersionsHelperTest.java
+++ b/versions-common/src/test/java/org/codehaus/mojo/versions/api/DefaultVersionsHelperTest.java
@@ -34,10 +34,7 @@ import org.apache.maven.artifact.Artifact;
 import org.apache.maven.artifact.handler.DefaultArtifactHandler;
 import org.apache.maven.artifact.versioning.ArtifactVersion;
 import org.apache.maven.execution.MavenSession;
-import org.apache.maven.plugin.MojoExecution;
-import org.apache.maven.plugin.MojoExecutionException;
 import org.apache.maven.plugin.logging.Log;
-import org.apache.maven.plugin.logging.SystemStreamLog;
 import org.apache.maven.project.MavenProject;
 import org.apache.maven.wagon.ConnectionException;
 import org.apache.maven.wagon.ResourceDoesNotExistException;
@@ -47,10 +44,7 @@ import org.apache.maven.wagon.authentication.AuthenticationException;
 import org.apache.maven.wagon.authentication.AuthenticationInfo;
 import org.apache.maven.wagon.authorization.AuthorizationException;
 import org.apache.maven.wagon.proxy.ProxyInfo;
-import org.codehaus.mojo.versions.model.IgnoreVersion;
-import org.codehaus.mojo.versions.model.Rule;
-import org.codehaus.mojo.versions.model.RuleSet;
-import org.codehaus.mojo.versions.ordering.VersionComparators;
+import org.codehaus.mojo.versions.rule.RulesServiceBuilder;
 import org.codehaus.mojo.versions.utils.ArtifactFactory;
 import org.eclipse.aether.DefaultRepositorySystemSession;
 import org.eclipse.aether.RepositorySystem;
@@ -61,7 +55,6 @@ import org.eclipse.aether.resolution.VersionRangeResult;
 import org.eclipse.aether.util.version.GenericVersionScheme;
 import org.eclipse.aether.version.InvalidVersionSpecificationException;
 import org.eclipse.aether.version.Version;
-import org.hamcrest.CoreMatchers;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.mockito.Mock;
@@ -69,17 +62,14 @@ import org.mockito.MockitoAnnotations;
 
 import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
 import static java.util.Collections.emptyList;
-import static java.util.Collections.singletonList;
 import static java.util.Collections.singletonMap;
 import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.containsInAnyOrder;
 import static org.hamcrest.Matchers.equalTo;
 import static org.hamcrest.Matchers.hasSize;
 import static org.hamcrest.Matchers.is;
 import static org.hamcrest.Matchers.not;
 import static org.hamcrest.core.IsIterableContaining.hasItems;
 import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.any;
@@ -99,18 +89,29 @@ class DefaultVersionsHelperTest {
     @Mock
     PomHelper pomHelper;
 
+    @Mock
+    RepositorySystem repositorySystem;
+
+    @Mock
+    Artifact artifact;
+
+    @Mock
+    MavenSession mavenSession;
+
     @Mock
     Log log;
 
     @BeforeEach
     public void beforeEach() {
         MockitoAnnotations.openMocks(this);
+        when(mavenSession.getCurrentProject()).thenReturn(mock(MavenProject.class));
+        when(mavenSession.getCurrentProject().getRemotePluginRepositories()).thenReturn(emptyList());
+        when(mavenSession.getCurrentProject().getRemotePluginRepositories()).thenReturn(emptyList());
+        when(mavenSession.getRepositorySession()).thenReturn(new DefaultRepositorySystemSession());
     }
 
     @Test
     public void testPerRuleVersionsIgnored() throws Exception {
-        final RepositorySystem repositorySystem = mock(org.eclipse.aether.RepositorySystem.class);
-        final Artifact artifact = mock(Artifact.class);
         when(artifact.getGroupId()).thenReturn("com.mycompany.maven");
         when(artifact.getArtifactId()).thenReturn("artifact-one");
         when(artifact.getType()).thenReturn("jar");
@@ -138,7 +139,6 @@ class DefaultVersionsHelperTest {
 
     @Test
     public void testGlobalRuleVersionsIgnored() throws Exception {
-        final RepositorySystem repositorySystem = mock(org.eclipse.aether.RepositorySystem.class);
         final Artifact artifact = mock(Artifact.class);
         when(artifact.getGroupId()).thenReturn("other.company");
         when(artifact.getArtifactId()).thenReturn("artifact-two");
@@ -174,43 +174,6 @@ class DefaultVersionsHelperTest {
         assertThat(actual, hasItems(one, two, three, illegal));
     }
 
-    @Test
-    public void testWildcardMatching() throws Exception {
-        assertTrue(DefaultVersionsHelper.exactMatch("*", "com.foo.bar"));
-        assertFalse(DefaultVersionsHelper.exactMatch("com.bar*", "com-bar"));
-        assertTrue(DefaultVersionsHelper.exactMatch("com?foo.bar", "com.foo.bar"));
-        assertTrue(DefaultVersionsHelper.exactMatch("co*.foo.b?r", "com.foo.bar"));
-        assertTrue(DefaultVersionsHelper.exactMatch("c*oo*r", "com.foo.bar"));
-    }
-
-    @Test
-    public void testRuleSets() throws Exception {
-        VersionsHelper helper = createHelper();
-
-        assertEquals(
-                VersionComparators.getVersionComparator("maven"),
-                helper.getVersionComparator("net.foo", "bar"),
-                "no match gives default");
-        assertEquals(
-                VersionComparators.getVersionComparator("mercury"),
-                helper.getVersionComparator("org.apache.maven", "plugins"),
-                "matches wildcard");
-        assertEquals(
-                VersionComparators.getVersionComparator("mercury"),
-                helper.getVersionComparator("com.mycompany.custom.maven", "plugins"),
-                "exact match wins over initial match");
-        assertEquals(
-                VersionComparators.getVersionComparator("maven"),
-                helper.getVersionComparator("com.mycompany.maven.plugins", "plugins"),
-                "non-wildcard prefix wins over wildcard prefix match");
-        assertEquals(
-                VersionComparators.getVersionComparator("maven"),
-                helper.getVersionComparator("com.mycompany.maven", "new-maven-plugin"));
-        assertEquals(
-                VersionComparators.getVersionComparator("mercury"),
-                helper.getVersionComparator("com.mycompany.maven", "old-maven-plugin"));
-    }
-
     @Test
     void testMVERSIONS159ExcludedAndNotIncluded() throws Exception {
         VersionsHelper helper = createHelper();
@@ -230,24 +193,11 @@ class DefaultVersionsHelperTest {
         assertTrue(result.isEmpty());
     }
 
-    @Test
-    void testIsClasspathUriDetectsClassPathProtocol() throws Exception {
-        String uri = "classpath:/p/a/c/k/a/g/e/resource.res";
-
-        assertThat(DefaultVersionsHelper.isClasspathUri(uri), CoreMatchers.is(true));
-    }
-
-    @Test
-    void testIsClasspathUriDetectsThatItIsDifferentProtocol() throws Exception {
-        String uri = "http://10.10.10.10/p/a/c/k/a/g/e/resource.res";
-
-        assertThat(DefaultVersionsHelper.isClasspathUri(uri), CoreMatchers.is(false));
-    }
-
     private DefaultVersionsHelper createHelper() throws Exception {
-        return createHelper(null);
+        return createHelper(mock(RepositorySystem.class));
     }
 
+    @Deprecated
     private static Wagon mockFileWagon(URI rulesUri)
             throws AuthenticationException, ConnectionException, AuthorizationException, TransferFailedException,
                     ResourceDoesNotExistException {
@@ -272,90 +222,22 @@ class DefaultVersionsHelperTest {
         final String resourcePath = "/" + getClass().getPackage().getName().replace('.', '/') + "/rules.xml";
         final String rulesUri =
                 Objects.requireNonNull(getClass().getResource(resourcePath)).toExternalForm();
-        MavenSession mavenSession = mock(MavenSession.class);
-        when(mavenSession.getCurrentProject()).thenReturn(mock(MavenProject.class));
-        when(mavenSession.getCurrentProject().getRemotePluginRepositories()).thenReturn(emptyList());
-        when(mavenSession.getCurrentProject().getRemotePluginRepositories()).thenReturn(emptyList());
-        when(mavenSession.getRepositorySession()).thenReturn(new DefaultRepositorySystemSession());
 
         return new DefaultVersionsHelper.Builder()
                 .withArtifactFactory(artifactFactory)
                 .withPomHelper(pomHelper)
                 .withRepositorySystem(repositorySystem)
                 .withLog(log)
-                .withWagonMap(singletonMap("file", mockFileWagon(new URI(rulesUri))))
-                .withServerId("")
-                .withRulesUri(rulesUri)
                 .withMavenSession(mavenSession)
-                .withMojoExecution(mock(MojoExecution.class))
-                .build();
-    }
-
-    @Test
-    void testIgnoredVersionsShouldBeTheOnlyPresentInAnEmptyRuleSet() throws MojoExecutionException {
-        DefaultVersionsHelper versionsHelper = new DefaultVersionsHelper.Builder()
-                .withArtifactFactory(artifactFactory)
-                .withPomHelper(pomHelper)
-                .withLog(new SystemStreamLog())
-                .withIgnoredVersions(Arrays.asList(".*-M.", ".*-SNAPSHOT"))
-                .build();
-        RuleSet ruleSet = versionsHelper.getRuleSet();
-        assertThat(ruleSet.getIgnoreVersions(), hasSize(2));
-        assertThat(
-                ruleSet.getIgnoreVersions().stream()
-                        .map(IgnoreVersion::getVersion)
-                        .collect(Collectors.toList()),
-                containsInAnyOrder(".*-M.", ".*-SNAPSHOT"));
-    }
-
-    @Test
-    void testDefaultsShouldBePresentInAnEmptyRuleSet() throws MojoExecutionException, IllegalAccessException {
-        DefaultVersionsHelper versionsHelper = new DefaultVersionsHelper.Builder()
-                .withArtifactFactory(artifactFactory)
-                .withPomHelper(pomHelper)
-                .withLog(new SystemStreamLog())
-                .withIgnoredVersions(singletonList(".*-M."))
+                .withRuleService(new RulesServiceBuilder()
+                        .withMavenSession(mavenSession)
+                        .withWagonMap(singletonMap("file", mockFileWagon(new URI(rulesUri))))
+                        .withServerId("")
+                        .withRulesUri(rulesUri)
+                        .withMavenSession(mavenSession)
+                        .withLog(log)
+                        .build())
                 .build();
-        RuleSet ruleSet = versionsHelper.getRuleSet();
-        assertThat(ruleSet.getComparisonMethod(), is("maven"));
-    }
-
-    @Test
-    void testIgnoredVersionsShouldExtendTheRuleSet() throws MojoExecutionException, IllegalAccessException {
-        DefaultVersionsHelper versionsHelper = new DefaultVersionsHelper.Builder()
-                .withArtifactFactory(artifactFactory)
-                .withPomHelper(pomHelper)
-                .withLog(new SystemStreamLog())
-                .withRuleSet(new RuleSet() {
-                    {
-                        setIgnoreVersions(new ArrayList<>(singletonList(new IgnoreVersion() {
-                            {
-                                setVersion("1.0.0");
-                            }
-                        })));
-                        setRules(singletonList(new Rule() {
-                            {
-                                setGroupId("org.slf4j");
-                                setArtifactId("slf4j-api");
-                                setIgnoreVersions(singletonList(new IgnoreVersion() {
-                                    {
-                                        setType("regex");
-                                        setVersion("^[^1]\\.*");
-                                    }
-                                }));
-                            }
-                        }));
-                    }
-                })
-                .withIgnoredVersions(Arrays.asList(".*-M.", ".*-SNAPSHOT"))
-                .build();
-        RuleSet ruleSet = versionsHelper.getRuleSet();
-        assertThat(ruleSet.getIgnoreVersions(), hasSize(3));
-        assertThat(
-                ruleSet.getIgnoreVersions().stream()
-                        .map(IgnoreVersion::getVersion)
-                        .collect(Collectors.toList()),
-                containsInAnyOrder(".*-M.", ".*-SNAPSHOT", "1.0.0"));
     }
 
     @Test
@@ -383,7 +265,7 @@ class DefaultVersionsHelperTest {
                 .build();
 
         List<RemoteRepository> remoteRepositories =
-                DefaultVersionsHelper.adjustRemoteRepositoriesRefreshPolicy(Arrays.asList(repo1, repo2, repo3));
+                DefaultVersionsHelper.forceDailyRemoteRepositoriesRefreshPolicy(Arrays.asList(repo1, repo2, repo3));
 
         assertThat(remoteRepositories, hasSize(3));
         assertThat(remoteRepositories.get(0), not(is(repo1)));
diff --git a/versions-common/src/test/java/org/codehaus/mojo/versions/ordering/MavenVersionComparatorTest.java b/versions-common/src/test/java/org/codehaus/mojo/versions/ordering/DefaultSegmentCounterTest.java
similarity index 55%
rename from versions-common/src/test/java/org/codehaus/mojo/versions/ordering/MavenVersionComparatorTest.java
rename to versions-common/src/test/java/org/codehaus/mojo/versions/ordering/DefaultSegmentCounterTest.java
index 53e8046d..464d0eb4 100644
--- a/versions-common/src/test/java/org/codehaus/mojo/versions/ordering/MavenVersionComparatorTest.java
+++ b/versions-common/src/test/java/org/codehaus/mojo/versions/ordering/DefaultSegmentCounterTest.java
@@ -20,49 +20,21 @@ package org.codehaus.mojo.versions.ordering;
  */
 
 import org.apache.maven.artifact.versioning.ArtifactVersion;
+import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
-import static org.codehaus.mojo.versions.api.Segment.INCREMENTAL;
 import static org.codehaus.mojo.versions.api.Segment.SUBINCREMENTAL;
+import static org.codehaus.mojo.versions.utils.ArtifactVersionUtils.version;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.greaterThan;
 import static org.hamcrest.Matchers.lessThan;
-import static org.hamcrest.core.Is.is;
 
-class MavenVersionComparatorTest extends VersionComparatorTestBase {
-    MavenVersionComparatorTest() {
-        super(new MavenVersionComparator());
-    }
-
-    @Test
-    void testSegmentCounting() {
-        assertThat(3, is(instance.getSegmentCount(version("5"))));
-        assertThat(3, is(instance.getSegmentCount(version("5.0"))));
-        assertThat(4, is(instance.getSegmentCount(version("5-0"))));
-        assertThat(1, is(instance.getSegmentCount(version("5.3.a"))));
-        assertThat(1, is(instance.getSegmentCount(version("5.0.a.1.4.5"))));
-        assertThat(3, is(instance.getSegmentCount(version(""))));
-    }
+class DefaultSegmentCounterTest {
+    protected SegmentCounter instance;
 
-    @Test
-    void testUpperBoundaryCustom() {
-        assertThat(
-                instance.compare(new BoundArtifactVersion(version("1.2.3"), INCREMENTAL), version("1.2.3-ANDRZEJ")),
-                greaterThan(0));
-    }
-
-    @Test
-    void testUpperBoundaryRelease() {
-        assertThat(
-                instance.compare(new BoundArtifactVersion(version("1.1.0"), INCREMENTAL), version("1.1.0")),
-                greaterThan(0));
-    }
-
-    @Test
-    void testUpperBoundarySnapshot() {
-        assertThat(
-                instance.compare(new BoundArtifactVersion(version("1.1.0"), INCREMENTAL), version("1.1.0-SNAPSHOT")),
-                greaterThan(0));
+    @BeforeEach
+    void beforeEach() {
+        this.instance = new DefaultSegmentCounter();
     }
 
     @Test
diff --git a/versions-common/src/test/java/org/codehaus/mojo/versions/ordering/MercuryVersionComparatorTest.java b/versions-common/src/test/java/org/codehaus/mojo/versions/ordering/MercuryVersionComparatorTest.java
deleted file mode 100644
index 1ef5163b..00000000
--- a/versions-common/src/test/java/org/codehaus/mojo/versions/ordering/MercuryVersionComparatorTest.java
+++ /dev/null
@@ -1,41 +0,0 @@
-package org.codehaus.mojo.versions.ordering;
-
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you 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.
- */
-
-import org.junit.jupiter.api.Test;
-
-import static org.codehaus.mojo.versions.utils.ArtifactVersionService.getArtifactVersion;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
-class MercuryVersionComparatorTest extends VersionComparatorTestBase {
-    MercuryVersionComparatorTest() {
-        super(new MercuryVersionComparator());
-    }
-
-    @Test
-    void testSegmentCounting() {
-        assertEquals(1, instance.getSegmentCount(getArtifactVersion("5")));
-        assertEquals(2, instance.getSegmentCount(getArtifactVersion("5.0")));
-        assertEquals(2, instance.getSegmentCount(getArtifactVersion("5-0")));
-        assertEquals(3, instance.getSegmentCount(getArtifactVersion("5.3.a")));
-        assertEquals(6, instance.getSegmentCount(getArtifactVersion("5.0.a.1.4.5")));
-        assertEquals(0, instance.getSegmentCount(getArtifactVersion("")));
-    }
-}
diff --git a/versions-common/src/test/java/org/codehaus/mojo/versions/ordering/NumericVersionComparatorTest.java b/versions-common/src/test/java/org/codehaus/mojo/versions/ordering/NumericVersionComparatorTest.java
deleted file mode 100644
index c064f8f4..00000000
--- a/versions-common/src/test/java/org/codehaus/mojo/versions/ordering/NumericVersionComparatorTest.java
+++ /dev/null
@@ -1,101 +0,0 @@
-package org.codehaus.mojo.versions.ordering;
-
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you 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.
- */
-
-import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
-import org.junit.jupiter.api.Test;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
-class NumericVersionComparatorTest extends VersionComparatorTestBase {
-    NumericVersionComparatorTest() {
-        super(new NumericVersionComparator());
-    }
-
-    private int instanceCompare(String v1, String v2) {
-        return instance.compare(new DefaultArtifactVersion(v1), new DefaultArtifactVersion(v2));
-    }
-
-    @Test
-    void testSmokes() {
-        assertTrue(instanceCompare("1.0.0.0.0", "1.0.0.0.1") < 0);
-        assertTrue(instanceCompare("1.0.0.0.0", "2.0.0.0.1") < 0);
-        assertTrue(instanceCompare("1.0.0.0.0", "1.0.0.0") < 0);
-        assertTrue(instanceCompare("1.0.0.0.0", "1.0.0.0.0") == 0);
-        assertTrue(instanceCompare("1.0.0.0", "1.0.0.0.0") > 0);
-    }
-
-    @Test
-    void testBigValues() {
-        assertTrue(instanceCompare("1.92.0", "1.100000000000000000000000.0") < 0);
-        assertTrue(instanceCompare("1.100000000000000000000000.0", "1.92.0") > 0);
-        assertTrue(instanceCompare("1.100000000000000000000000.0", "1.100000000000000000000000.0") == 0);
-    }
-
-    @Test
-    void testStringValues() {
-        assertTrue(instanceCompare("1.a20.0", "1.a3.0") < 0);
-        assertTrue(instanceCompare("1.a20.0", "1.b10.0") < 0);
-        assertTrue(instanceCompare("1.a.0.b.0", "1.a.0.b.1") < 0);
-        assertTrue(instanceCompare("1.162%a.0.b.0", "1.162%a.0.b.1") < 0);
-        assertTrue(instanceCompare("1.162%a.0.b.0", "1.2.0.b.1") < 0);
-        assertTrue(instanceCompare("1.0a.0.b.0", "1.162%.0.b.1") < 0);
-        assertTrue(instanceCompare("1.a.0.b.0", "2.a.0.b.1") < 0);
-        assertTrue(instanceCompare("1.a.0.b.0", "1.a.0.b") < 0);
-        assertTrue(instanceCompare("1.a.0.b.0", "1.a.0.b.0") == 0);
-        assertTrue(instanceCompare("1.a.0.b", "1.a.0.b.0") > 0);
-        assertTrue(instanceCompare("1.a.0.0", "1.a.0") < 0);
-        assertTrue(instanceCompare("1.a.0", "1.a.0.0") > 0);
-        assertTrue(instanceCompare("1.a.0.1", "1.a.0") > 0);
-        assertTrue(instanceCompare("1.a.0", "1.a.0.1") < 0);
-        assertTrue(instanceCompare("1.a.0.b", "1.a.0") > 0);
-        assertTrue(instanceCompare("1.a.0", "1.a.0.b") < 0);
-    }
-
-    @Test
-    void testQualifiers() {
-        assertTrue(instanceCompare("1.0-alpha.10", "1.0-alpha.20") < 0);
-        assertTrue(instanceCompare("1.0-alpha.10", "1.0-beta.1") < 0);
-        assertTrue(instanceCompare("1.0", "1.0-alpha.2") > 0);
-        assertTrue(instanceCompare("1.0-alpha.10", "1.0") < 0);
-        assertTrue(instanceCompare("1.0.10", "1.0-alpha.10") > 0);
-    }
-
-    @Test
-    void testSegmentCounting() {
-        assertEquals(1, instance.getSegmentCount(new DefaultArtifactVersion("5")));
-        assertEquals(2, instance.getSegmentCount(new DefaultArtifactVersion("5.0")));
-        assertEquals(1, instance.getSegmentCount(new DefaultArtifactVersion("5-0")));
-        assertEquals(3, instance.getSegmentCount(new DefaultArtifactVersion("5.3.a")));
-        assertEquals(6, instance.getSegmentCount(new DefaultArtifactVersion("5.0.a.1.4.5")));
-        assertEquals(0, instance.getSegmentCount(new DefaultArtifactVersion("")));
-    }
-
-    @Override
-    public void testVersionComparatorRow5() {
-        // non-numeric -- does not apply
-    }
-
-    @Override
-    public void testVersionComparatorRow6() {
-        // non-numeric -- does not apply
-    }
-}
diff --git a/versions-common/src/test/java/org/codehaus/mojo/versions/ordering/VersionComparatorTestBase.java b/versions-common/src/test/java/org/codehaus/mojo/versions/ordering/VersionComparatorTestBase.java
deleted file mode 100644
index f997e0f4..00000000
--- a/versions-common/src/test/java/org/codehaus/mojo/versions/ordering/VersionComparatorTestBase.java
+++ /dev/null
@@ -1,98 +0,0 @@
-package org.codehaus.mojo.versions.ordering;
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you 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.
- */
-
-import org.apache.maven.artifact.versioning.ArtifactVersion;
-import org.codehaus.mojo.versions.api.Segment;
-import org.junit.jupiter.api.Test;
-
-import static org.codehaus.mojo.versions.utils.ArtifactVersionService.getArtifactVersion;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.greaterThan;
-import static org.hamcrest.Matchers.lessThan;
-
-/**
- * Abstract base class for {@link MavenVersionComparatorTest}, {@link MercuryVersionComparatorTest},
- * and {@link NumericVersionComparatorTest}
- */
-abstract class VersionComparatorTestBase {
-    protected final VersionComparator instance;
-
-    VersionComparatorTestBase(VersionComparator instance) {
-        this.instance = instance;
-    }
-
-    protected static ArtifactVersion version(String version) {
-        return getArtifactVersion(version);
-    }
-
-    @Test
-    void testVersionComparatorRow1() {
-        assertThat(instance.compare(version("1"), version("2")), lessThan(0));
-        assertThat(instance.compare(version("1.0"), version("2.0")), lessThan(0));
-        assertThat(instance.compare(version("1.0"), version("1.1")), lessThan(0));
-        assertThat(instance.compare(version("1.0"), version("1.1")), lessThan(0));
-    }
-
-    @Test
-    void testVersionComparatorRow2() {
-        assertThat(instance.compare(version("1.0"), version("2.0")), lessThan(0));
-        assertThat(instance.compare(version("1.0"), version("1.1")), lessThan(0));
-    }
-
-    @Test
-    void testVersionComparatorRow3() {
-        assertThat(instance.compare(version("1.0.0"), version("2.0.0")), lessThan(0));
-        assertThat(instance.compare(version("1.0.0"), version("1.1.0")), lessThan(0));
-        assertThat(instance.compare(version("1.0.0"), version("1.0.1")), lessThan(0));
-    }
-
-    @Test
-    void testVersionComparatorRow4() {
-        assertThat(instance.compare(version("1.0.0-1"), version("2.0.0-1")), lessThan(0));
-        assertThat(instance.compare(version("1.0.0-1"), version("1.1.0-1")), lessThan(0));
-        assertThat(instance.compare(version("1.0.0-1"), version("1.0.1-1")), lessThan(0));
-        assertThat(instance.compare(version("1.0.0-1"), version("1.0.0-2")), lessThan(0));
-    }
-
-    @Test
-    void testVersionComparatorRow5() {
-        assertThat(instance.compare(version("1.0.0-sp1"), version("2.0.0-sp1")), lessThan(0));
-        assertThat(instance.compare(version("1.0.0-sp1"), version("1.1.0-sp1")), lessThan(0));
-        assertThat(instance.compare(version("1.0.0-sp1"), version("1.0.1-sp1")), lessThan(0));
-        assertThat(instance.compare(version("1.0.0-sp1"), version("1.0.0-1-sp1")), lessThan(0));
-    }
-
-    @Test
-    void testVersionComparatorRow6() {
-        assertThat(instance.compare(version("foobar"), version("foobar-1")), lessThan(0));
-    }
-
-    @Test
-    void testVersionComparatorRow7() {
-        assertThat(instance.compare(version("1-alpha-1"), version("2-alpha-1")), lessThan(0));
-        assertThat(instance.compare(version("1-alpha-1"), version("1-alpha-2")), lessThan(0));
-    }
-
-    @Test
-    void testBoundArtifactVersion() {
-        assertThat(
-                instance.compare(new BoundArtifactVersion("1.0.0", Segment.MAJOR), version("1.9.9")), greaterThan(0));
-    }
-}
diff --git a/versions-common/src/test/java/org/codehaus/mojo/versions/rule/RuleServiceTest.java b/versions-common/src/test/java/org/codehaus/mojo/versions/rule/RuleServiceTest.java
new file mode 100644
index 00000000..2c73fb71
--- /dev/null
+++ b/versions-common/src/test/java/org/codehaus/mojo/versions/rule/RuleServiceTest.java
@@ -0,0 +1,125 @@
+package org.codehaus.mojo.versions.rule;
+
+import java.io.File;
+import java.net.URI;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.stream.Collectors;
+
+import org.apache.maven.execution.MavenSession;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugin.logging.Log;
+import org.apache.maven.wagon.ConnectionException;
+import org.apache.maven.wagon.ResourceDoesNotExistException;
+import org.apache.maven.wagon.TransferFailedException;
+import org.apache.maven.wagon.Wagon;
+import org.apache.maven.wagon.authentication.AuthenticationException;
+import org.apache.maven.wagon.authentication.AuthenticationInfo;
+import org.apache.maven.wagon.authorization.AuthorizationException;
+import org.apache.maven.wagon.proxy.ProxyInfo;
+import org.codehaus.mojo.versions.model.IgnoreVersion;
+import org.codehaus.mojo.versions.model.Rule;
+import org.codehaus.mojo.versions.model.RuleSet;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
+import static java.util.Collections.singletonList;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.containsInAnyOrder;
+import static org.hamcrest.Matchers.hasSize;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.mock;
+
+public class RuleServiceTest {
+    @Mock
+    private Log log;
+
+    @Mock
+    private MavenSession mavenSession;
+
+    @BeforeEach
+    public void beforeEach() {
+        MockitoAnnotations.openMocks(this);
+    }
+
+    @Test
+    void testIgnoredVersionsShouldExtendTheRuleSet() throws MojoExecutionException, IllegalAccessException {
+        RuleService service = new RulesServiceBuilder()
+                .withMavenSession(mavenSession)
+                .withLog(log)
+                .withRuleSet(new RuleSet() {
+                    {
+                        setIgnoreVersions(new ArrayList<>(singletonList(new IgnoreVersion() {
+                            {
+                                setVersion("1.0.0");
+                            }
+                        })));
+                        setRules(singletonList(new Rule() {
+                            {
+                                setGroupId("org.slf4j");
+                                setArtifactId("slf4j-api");
+                                setIgnoreVersions(singletonList(new IgnoreVersion() {
+                                    {
+                                        setType("regex");
+                                        setVersion("^[^1]\\.*");
+                                    }
+                                }));
+                            }
+                        }));
+                    }
+                })
+                .withIgnoredVersions(Arrays.asList(".*-M.", ".*-SNAPSHOT"))
+                .build();
+        RuleSet ruleSet = service.getRuleSet();
+        assertThat(ruleSet.getIgnoreVersions(), hasSize(3));
+        assertThat(
+                ruleSet.getIgnoreVersions().stream()
+                        .map(IgnoreVersion::getVersion)
+                        .collect(Collectors.toList()),
+                containsInAnyOrder(".*-M.", ".*-SNAPSHOT", "1.0.0"));
+    }
+
+    @Test
+    void testIgnoredVersionsShouldBeTheOnlyPresentInAnEmptyRuleSet() throws MojoExecutionException {
+        RuleService service = new RulesServiceBuilder()
+                .withMavenSession(mavenSession)
+                .withLog(log)
+                .withIgnoredVersions(Arrays.asList(".*-M.", ".*-SNAPSHOT"))
+                .build();
+        RuleSet ruleSet = service.getRuleSet();
+        assertThat(ruleSet.getIgnoreVersions(), hasSize(2));
+        assertThat(
+                ruleSet.getIgnoreVersions().stream()
+                        .map(IgnoreVersion::getVersion)
+                        .collect(Collectors.toList()),
+                containsInAnyOrder(".*-M.", ".*-SNAPSHOT"));
+    }
+
+    private static Wagon mockFileWagon(URI rulesUri)
+            throws AuthenticationException, ConnectionException, AuthorizationException, TransferFailedException,
+                    ResourceDoesNotExistException {
+        Wagon wagon = mock(Wagon.class);
+        doNothing()
+                .when(wagon)
+                .connect(
+                        any(org.apache.maven.wagon.repository.Repository.class),
+                        any(AuthenticationInfo.class),
+                        any(ProxyInfo.class));
+        doAnswer(i -> {
+                    File tempFile = i.getArgument(1);
+                    Files.copy(Paths.get(rulesUri), tempFile.toPath(), REPLACE_EXISTING);
+                    return null;
+                })
+                .when(wagon)
+                .get(anyString(), any(File.class));
+        return wagon;
+    }
+}
diff --git a/versions-common/src/test/resources/org/codehaus/mojo/versions/api/rules.xml b/versions-common/src/test/resources/org/codehaus/mojo/versions/api/rules.xml
index 24ed244f..983af110 100644
--- a/versions-common/src/test/resources/org/codehaus/mojo/versions/api/rules.xml
+++ b/versions-common/src/test/resources/org/codehaus/mojo/versions/api/rules.xml
@@ -1,13 +1,11 @@
-<ruleset comparisonMethod="maven">
+<ruleset>
   <ignoreVersions>
     <ignoreVersion type="regex">.*-alpha</ignoreVersion>
     <ignoreVersion type="regex">.*-beta</ignoreVersion>
     <ignoreVersion type="illegalType">illegalVersion</ignoreVersion>
   </ignoreVersions>
   <rules>
-    <rule groupId="*.maven" comparisonMethod="mercury"/>
-    <rule groupId="com.mycompany" comparisonMethod="numeric"/>
-    <rule groupId="com.mycompany.maven" comparisonMethod="maven">
+    <rule groupId="com.mycompany.maven">
       <ignoreVersions>
         <ignoreVersion>one</ignoreVersion>
         <ignoreVersion>two</ignoreVersion>
@@ -15,6 +13,5 @@
         <ignoreVersion type="illegalType">illegalVersion</ignoreVersion>
       </ignoreVersions>
     </rule>
-    <rule groupId="com.mycompany.maven" artifactId="old-maven-plugin" comparisonMethod="mercury"/>
   </rules>
 </ruleset>
\ No newline at end of file
diff --git a/versions-common/src/test/resources/org/codehaus/mojo/versions/rule/rules.xml b/versions-common/src/test/resources/org/codehaus/mojo/versions/rule/rules.xml
new file mode 100644
index 00000000..983af110
--- /dev/null
+++ b/versions-common/src/test/resources/org/codehaus/mojo/versions/rule/rules.xml
@@ -0,0 +1,17 @@
+<ruleset>
+  <ignoreVersions>
+    <ignoreVersion type="regex">.*-alpha</ignoreVersion>
+    <ignoreVersion type="regex">.*-beta</ignoreVersion>
+    <ignoreVersion type="illegalType">illegalVersion</ignoreVersion>
+  </ignoreVersions>
+  <rules>
+    <rule groupId="com.mycompany.maven">
+      <ignoreVersions>
+        <ignoreVersion>one</ignoreVersion>
+        <ignoreVersion>two</ignoreVersion>
+        <ignoreVersion>1.2.0</ignoreVersion>
+        <ignoreVersion type="illegalType">illegalVersion</ignoreVersion>
+      </ignoreVersions>
+    </rule>
+  </rules>
+</ruleset>
\ No newline at end of file
diff --git a/versions-maven-plugin/src/it/it-use-latest-releases-003/rule-set.xml b/versions-maven-plugin/src/it/it-use-latest-releases-003/rule-set.xml
index c7add764..bf1cebc6 100644
--- a/versions-maven-plugin/src/it/it-use-latest-releases-003/rule-set.xml
+++ b/versions-maven-plugin/src/it/it-use-latest-releases-003/rule-set.xml
@@ -1,5 +1,1 @@
-<ruleset comparisonMethod="maven">
-<rules>
-<rule groupId="localhost" artifactId="version-rules" comparisonMethod="mercury"/>
-</rules>
-</ruleset>
\ No newline at end of file
+<ruleset/>
diff --git a/versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/AbstractVersionsUpdaterMojoTest.java b/versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/AbstractVersionsUpdaterMojoTest.java
deleted file mode 100644
index 4f58abad..00000000
--- a/versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/AbstractVersionsUpdaterMojoTest.java
+++ /dev/null
@@ -1,58 +0,0 @@
-package org.codehaus.mojo.versions;
-
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you 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.
- */
-
-import org.codehaus.mojo.versions.ordering.NumericVersionComparator;
-import org.codehaus.mojo.versions.utils.ArtifactVersionService;
-import org.junit.Test;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
-/**
- * Basic tests for {@linkplain org.codehaus.mojo.versions.AbstractVersionsUpdaterMojo}.
- *
- * @author Stephen Connolly
- */
-public class AbstractVersionsUpdaterMojoTest {
-
-    private NumericVersionComparator instance = new NumericVersionComparator();
-
-    private int instanceCompare(String v1, String v2) {
-        return instance.compare(
-                ArtifactVersionService.getArtifactVersion(v1), ArtifactVersionService.getArtifactVersion(v2));
-    }
-
-    /**
-     * Basic test.
-     *
-     */
-    @Test
-    public void testBasic() {
-        assertEquals(0, instanceCompare("1", "1"));
-        assertTrue(instanceCompare("1", "2") < 0);
-        assertTrue(instanceCompare("2", "1") > 0);
-        assertTrue(instanceCompare("1", "1-SNAPSHOT") > 0);
-        assertTrue(instanceCompare("1", "1.0") > 0);
-        assertTrue(instanceCompare("1.1", "1") > 0);
-        assertTrue(instanceCompare("5.1.0.0.24", "5.1.0.0.9") > 0);
-        assertTrue(instanceCompare("5.1.0.0.2a4", "5.1.0.0.9") < 0);
-    }
-}
diff --git a/versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/DependencyUpdatesReportTest.java b/versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/DependencyUpdatesReportTest.java
index 52586eaa..64ca69d5 100644
--- a/versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/DependencyUpdatesReportTest.java
+++ b/versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/DependencyUpdatesReportTest.java
@@ -33,6 +33,7 @@ import org.apache.maven.doxia.sink.SinkFactory;
 import org.apache.maven.model.Dependency;
 import org.apache.maven.model.DependencyManagement;
 import org.apache.maven.model.Model;
+import org.apache.maven.plugin.MojoExecution;
 import org.apache.maven.project.MavenProject;
 import org.apache.maven.reporting.MavenReportException;
 import org.codehaus.mojo.versions.model.RuleSet;
@@ -56,6 +57,7 @@ import static org.hamcrest.CoreMatchers.not;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.anyOf;
 import static org.hamcrest.Matchers.matchesPattern;
+import static org.mockito.Mockito.mock;
 
 /**
  * Basic tests for {@linkplain DependencyUpdatesReport}.
@@ -81,6 +83,7 @@ public class DependencyUpdatesReportTest {
             project.getModel().setDependencyManagement(new DependencyManagement());
 
             session = mockMavenSession();
+            mojoExecution = mock(MojoExecution.class);
         }
 
         public TestDependencyUpdatesReport withDependencies(Dependency... dependencies) {
diff --git a/versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/DependencyUpdatesXmlRendererTest.java b/versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/DependencyUpdatesXmlRendererTest.java
index 27d67712..85a19e4c 100644
--- a/versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/DependencyUpdatesXmlRendererTest.java
+++ b/versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/DependencyUpdatesXmlRendererTest.java
@@ -26,7 +26,6 @@ import java.util.Arrays;
 
 import org.apache.maven.artifact.DefaultArtifact;
 import org.codehaus.mojo.versions.api.ArtifactVersions;
-import org.codehaus.mojo.versions.ordering.MavenVersionComparator;
 import org.codehaus.mojo.versions.reporting.model.DependencyUpdatesModel;
 import org.codehaus.mojo.versions.utils.ArtifactVersionService;
 import org.codehaus.mojo.versions.utils.DependencyBuilder;
@@ -84,8 +83,7 @@ public class DependencyUpdatesXmlRendererTest {
                                                         ArtifactVersionService.getArtifactVersion("1.0.0"),
                                                         ArtifactVersionService.getArtifactVersion("1.0.1"),
                                                         ArtifactVersionService.getArtifactVersion("1.1.0"),
-                                                        ArtifactVersionService.getArtifactVersion("2.0.0")),
-                                                new MavenVersionComparator())),
+                                                        ArtifactVersionService.getArtifactVersion("2.0.0")))),
                                 emptyMap()),
                         tempFile,
                         false)
diff --git a/versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/DisplayDependencyUpdatesMojoTest.java b/versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/DisplayDependencyUpdatesMojoTest.java
index 6c3ae95b..4dd83122 100644
--- a/versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/DisplayDependencyUpdatesMojoTest.java
+++ b/versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/DisplayDependencyUpdatesMojoTest.java
@@ -26,7 +26,9 @@ import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager;
 import org.apache.maven.model.InputLocation;
 import org.apache.maven.model.InputSource;
 import org.apache.maven.model.Model;
+import org.apache.maven.plugin.MojoExecution;
 import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugin.logging.Log;
 import org.apache.maven.plugin.testing.AbstractMojoTestCase;
 import org.apache.maven.plugin.testing.MojoRule;
 import org.apache.maven.project.MavenProject;
@@ -35,10 +37,12 @@ import org.codehaus.mojo.versions.model.TestIgnoreVersions;
 import org.codehaus.mojo.versions.utils.ArtifactFactory;
 import org.codehaus.mojo.versions.utils.CloseableTempFile;
 import org.codehaus.mojo.versions.utils.DependencyBuilder;
+import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluator;
 import org.hamcrest.Matchers;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
+import org.mockito.Mock;
 
 import static java.nio.charset.StandardCharsets.UTF_8;
 import static java.util.Collections.emptyList;
@@ -56,6 +60,7 @@ import static org.hamcrest.Matchers.hasItem;
 import static org.hamcrest.Matchers.is;
 import static org.hamcrest.Matchers.not;
 import static org.hamcrest.Matchers.notNullValue;
+import static org.mockito.Mockito.mock;
 import static org.mockito.MockitoAnnotations.openMocks;
 
 /**
@@ -65,8 +70,14 @@ public class DisplayDependencyUpdatesMojoTest extends AbstractMojoTestCase {
     @Rule
     public final MojoRule mojoRule = new MojoRule(this);
 
+    @Mock
+    private Log log;
+
     private ArtifactFactory artifactFactory;
 
+    @Mock
+    private ExpressionEvaluator expressionEvaluator;
+
     @Before
     public void setUp() throws Exception {
         super.setUp();
@@ -178,6 +189,7 @@ public class DisplayDependencyUpdatesMojoTest extends AbstractMojoTestCase {
                     setPluginContext(new HashMap<>());
 
                     session = mockMavenSession();
+                    mojoExecution = mock(MojoExecution.class);
                 }
             }.execute();
 
@@ -213,6 +225,7 @@ public class DisplayDependencyUpdatesMojoTest extends AbstractMojoTestCase {
                     setPluginContext(new HashMap<>());
 
                     session = mockMavenSession();
+                    mojoExecution = mock(MojoExecution.class);
                 }
             }.execute();
 
@@ -246,6 +259,7 @@ public class DisplayDependencyUpdatesMojoTest extends AbstractMojoTestCase {
                     setPluginContext(new HashMap<>());
 
                     session = mockMavenSession();
+                    mojoExecution = mock(MojoExecution.class);
                 }
             }.execute();
 
@@ -280,6 +294,7 @@ public class DisplayDependencyUpdatesMojoTest extends AbstractMojoTestCase {
                     setPluginContext(new HashMap<>());
 
                     session = mockMavenSession();
+                    mojoExecution = mock(MojoExecution.class);
                 }
             }.execute();
 
@@ -318,6 +333,7 @@ public class DisplayDependencyUpdatesMojoTest extends AbstractMojoTestCase {
                     setPluginContext(new HashMap<>());
 
                     session = mockMavenSession();
+                    mojoExecution = mock(MojoExecution.class);
                 }
             }.execute();
 
@@ -414,6 +430,7 @@ public class DisplayDependencyUpdatesMojoTest extends AbstractMojoTestCase {
                     setPluginContext(new HashMap<>());
 
                     session = mockMavenSession();
+                    mojoExecution = mock(MojoExecution.class);
                 }
             }.execute();
 
@@ -445,6 +462,7 @@ public class DisplayDependencyUpdatesMojoTest extends AbstractMojoTestCase {
                     setPluginContext(new HashMap<>());
 
                     session = mockMavenSession();
+                    mojoExecution = mock(MojoExecution.class);
                 }
             }.execute();
 
diff --git a/versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/DisplayExtensionUpdatesMojoTest.java b/versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/DisplayExtensionUpdatesMojoTest.java
index f59ea87e..4f019cc4 100644
--- a/versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/DisplayExtensionUpdatesMojoTest.java
+++ b/versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/DisplayExtensionUpdatesMojoTest.java
@@ -25,16 +25,20 @@ import java.util.HashMap;
 import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager;
 import org.apache.maven.model.Build;
 import org.apache.maven.model.Model;
+import org.apache.maven.plugin.MojoExecution;
 import org.apache.maven.plugin.MojoExecutionException;
 import org.apache.maven.plugin.MojoFailureException;
+import org.apache.maven.plugin.logging.Log;
 import org.apache.maven.project.MavenProject;
 import org.codehaus.mojo.versions.api.PomHelper;
 import org.codehaus.mojo.versions.utils.ArtifactFactory;
 import org.codehaus.mojo.versions.utils.ExtensionBuilder;
+import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluator;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.ArgumentMatchers;
+import org.mockito.Mock;
 import org.mockito.MockedStatic;
 
 import static java.util.Collections.emptyList;
@@ -48,6 +52,7 @@ import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.containsString;
 import static org.hamcrest.Matchers.empty;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.mockStatic;
 import static org.mockito.MockitoAnnotations.openMocks;
 
@@ -60,8 +65,16 @@ public class DisplayExtensionUpdatesMojoTest {
     private DisplayExtensionUpdatesMojo mojo;
     private Path tempPath;
 
+    @Mock
+    private Log log;
+
+    private PomHelper pomHelper;
+
     private ArtifactFactory artifactFactory;
 
+    @Mock
+    private ExpressionEvaluator expressionEvaluator;
+
     @Before
     public void setUp() throws IllegalAccessException, IOException, MojoExecutionException {
         openMocks(this);
@@ -88,6 +101,7 @@ public class DisplayExtensionUpdatesMojoTest {
         setVariableValueToObject(mojo, "processCoreExtensions", false);
         // turning interpolateExtensions off so that we don't need to bother with the model tree
         mojo.interpolateProperties = false;
+        mojo.mojoExecution = mock(MojoExecution.class);
 
         mojo.setPluginContext(new HashMap<String, Object>() {
             {
diff --git a/versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/DisplayParentUpdatesMojoTest.java b/versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/DisplayParentUpdatesMojoTest.java
index 29647312..90d133a0 100644
--- a/versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/DisplayParentUpdatesMojoTest.java
+++ b/versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/DisplayParentUpdatesMojoTest.java
@@ -30,19 +30,23 @@ import org.apache.maven.artifact.versioning.ArtifactVersion;
 import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
 import org.apache.maven.artifact.versioning.VersionRange;
 import org.apache.maven.model.Model;
+import org.apache.maven.plugin.MojoExecution;
 import org.apache.maven.plugin.MojoExecutionException;
 import org.apache.maven.plugin.MojoFailureException;
+import org.apache.maven.plugin.logging.Log;
 import org.apache.maven.plugin.testing.stubs.DefaultArtifactHandlerStub;
 import org.apache.maven.project.MavenProject;
 import org.codehaus.mojo.versions.api.VersionRetrievalException;
 import org.codehaus.mojo.versions.ordering.InvalidSegmentException;
 import org.codehaus.mojo.versions.utils.ArtifactFactory;
 import org.codehaus.mojo.versions.utils.TestUtils;
+import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluator;
 import org.eclipse.aether.RepositorySystem;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.BeforeClass;
 import org.junit.Test;
+import org.mockito.Mock;
 
 import static java.util.Collections.singleton;
 import static org.apache.maven.artifact.Artifact.SCOPE_COMPILE;
@@ -55,6 +59,7 @@ import static org.hamcrest.Matchers.is;
 import static org.hamcrest.Matchers.notNullValue;
 import static org.hamcrest.Matchers.nullValue;
 import static org.hamcrest.Matchers.stringContainsInOrder;
+import static org.mockito.Mockito.mock;
 import static org.mockito.MockitoAnnotations.openMocks;
 
 public class DisplayParentUpdatesMojoTest {
@@ -66,12 +71,18 @@ public class DisplayParentUpdatesMojoTest {
 
     private static ArtifactFactory artifactFactory;
 
+    @Mock
+    private static ExpressionEvaluator expressionEvaluator;
+
+    @Mock
+    private static Log log;
+
     private Path tempDir;
 
     private Path tempFile;
 
     @BeforeClass
-    public static void setUpStatic() {
+    public static void setUpStatic() throws MojoExecutionException {
         artifactHandlerManager = mockArtifactHandlerManager();
         artifactFactory = new ArtifactFactory(artifactHandlerManager);
         repositorySystem = mockAetherRepositorySystem(new HashMap<String, String[]>() {
@@ -94,6 +105,7 @@ public class DisplayParentUpdatesMojoTest {
                 setProject(createProject());
                 reactorProjects = Collections.emptyList();
                 session = mockMavenSession();
+                mojoExecution = mock(MojoExecution.class);
             }
         };
         mojo.outputFile = tempFile.toFile();
diff --git a/versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/ForceReleasesMojoTest.java b/versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/ForceReleasesMojoTest.java
index 3b9bd3b0..3e3cf5f6 100644
--- a/versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/ForceReleasesMojoTest.java
+++ b/versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/ForceReleasesMojoTest.java
@@ -4,9 +4,12 @@ import javax.xml.stream.XMLStreamException;
 
 import org.apache.maven.artifact.DefaultArtifact;
 import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager;
+import org.apache.maven.execution.MavenSession;
 import org.apache.maven.model.Model;
+import org.apache.maven.plugin.MojoExecution;
 import org.apache.maven.plugin.MojoExecutionException;
 import org.apache.maven.plugin.MojoFailureException;
+import org.apache.maven.plugin.logging.Log;
 import org.apache.maven.plugin.testing.AbstractMojoTestCase;
 import org.apache.maven.project.MavenProject;
 import org.codehaus.mojo.versions.api.PomHelper;
@@ -15,8 +18,10 @@ import org.codehaus.mojo.versions.change.DefaultDependencyVersionChange;
 import org.codehaus.mojo.versions.utils.ArtifactFactory;
 import org.codehaus.mojo.versions.utils.DependencyBuilder;
 import org.codehaus.mojo.versions.utils.TestChangeRecorder;
+import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluator;
 import org.junit.Before;
 import org.junit.Test;
+import org.mockito.Mock;
 import org.mockito.MockedStatic;
 
 import static java.util.Collections.emptyList;
@@ -31,6 +36,7 @@ import static org.hamcrest.Matchers.hasItem;
 import static org.hamcrest.Matchers.startsWith;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.mockStatic;
 import static org.mockito.MockitoAnnotations.openMocks;
 
@@ -56,14 +62,24 @@ public class ForceReleasesMojoTest extends AbstractMojoTestCase {
     private TestChangeRecorder changeRecorder;
     private ForceReleasesMojo mojo;
 
+    @Mock
+    private Log log;
+
+    private PomHelper pomHelper;
+
     private ArtifactFactory artifactFactory;
 
+    @Mock
+    private ExpressionEvaluator expressionEvaluator;
+
     @Before
     public void setUp() throws Exception {
         super.setUp();
         openMocks(this);
         ArtifactHandlerManager artifactHandlerManager = mockArtifactHandlerManager();
         artifactFactory = new ArtifactFactory(artifactHandlerManager);
+        MavenSession session = mockMavenSession();
+
         changeRecorder = new TestChangeRecorder();
         mojo = new ForceReleasesMojo(artifactFactory, mockAetherRepositorySystem(), null, changeRecorder.asTestMap());
         setVariableValueToObject(mojo, "reactorProjects", emptyList());
@@ -78,7 +94,8 @@ public class ForceReleasesMojoTest extends AbstractMojoTestCase {
                 });
             }
         };
-        mojo.session = mockMavenSession();
+        mojo.session = session;
+        mojo.mojoExecution = mock(MojoExecution.class);
     }
 
     @Test
diff --git a/versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/LockSnapshotsMojoTest.java b/versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/LockSnapshotsMojoTest.java
index 4cb7f79b..1a3fc018 100644
--- a/versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/LockSnapshotsMojoTest.java
+++ b/versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/LockSnapshotsMojoTest.java
@@ -22,43 +22,61 @@ import java.util.function.UnaryOperator;
 
 import org.apache.maven.artifact.DefaultArtifact;
 import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager;
+import org.apache.maven.execution.MavenSession;
 import org.apache.maven.model.Dependency;
 import org.apache.maven.model.Model;
 import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugin.logging.Log;
 import org.apache.maven.plugin.testing.stubs.DefaultArtifactHandlerStub;
 import org.apache.maven.project.MavenProject;
 import org.codehaus.mojo.versions.api.PomHelper;
 import org.codehaus.mojo.versions.rewriting.MutableXMLStreamReader;
 import org.codehaus.mojo.versions.utils.ArtifactFactory;
 import org.codehaus.mojo.versions.utils.DependencyBuilder;
-import org.codehaus.mojo.versions.utils.MockUtils;
+import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluator;
 import org.eclipse.aether.RepositorySystem;
 import org.eclipse.aether.resolution.VersionRequest;
 import org.eclipse.aether.resolution.VersionResolutionException;
 import org.eclipse.aether.resolution.VersionResult;
 import org.junit.Before;
 import org.junit.Test;
+import org.mockito.Mock;
 import org.mockito.MockedStatic;
 
 import static java.util.Collections.emptyList;
 import static java.util.Collections.singletonList;
 import static org.codehaus.mojo.versions.utils.MockUtils.mockArtifactHandlerManager;
+import static org.codehaus.mojo.versions.utils.MockUtils.mockMavenSession;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.mockStatic;
 import static org.mockito.Mockito.when;
+import static org.mockito.MockitoAnnotations.openMocks;
 
 /**
  * Unit tests for {@link LockSnapshotsMojo}
  */
 public class LockSnapshotsMojoTest {
 
+    @Mock
+    private Log log;
+
+    private PomHelper pomHelper;
+
     private ArtifactFactory artifactFactory;
 
+    @Mock
+    private ExpressionEvaluator expressionEvaluator;
+
+    private MavenSession mavenSession;
+
     @Before
     public void setUp() throws MojoExecutionException {
+        openMocks(this);
+        mavenSession = mockMavenSession();
         ArtifactHandlerManager artifactHandlerManager = mockArtifactHandlerManager();
         artifactFactory = new ArtifactFactory(artifactHandlerManager);
+        pomHelper = new PomHelper(artifactFactory, expressionEvaluator);
     }
 
     private LockSnapshotsMojo createMojo(RepositorySystem repositorySystem) throws MojoExecutionException {
@@ -81,7 +99,7 @@ public class LockSnapshotsMojoTest {
                                 .build()));
                     }
                 });
-                session = MockUtils.mockMavenSession();
+                session = mavenSession;
             }
         };
     }
diff --git a/versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/ParentUpdatesReportTest.java b/versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/ParentUpdatesReportTest.java
index 5d66919a..d7318e6c 100644
--- a/versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/ParentUpdatesReportTest.java
+++ b/versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/ParentUpdatesReportTest.java
@@ -31,6 +31,7 @@ import org.apache.maven.artifact.DefaultArtifact;
 import org.apache.maven.doxia.module.xhtml5.Xhtml5SinkFactory;
 import org.apache.maven.doxia.sink.SinkFactory;
 import org.apache.maven.model.Model;
+import org.apache.maven.plugin.MojoExecution;
 import org.apache.maven.plugin.testing.stubs.DefaultArtifactHandlerStub;
 import org.apache.maven.project.MavenProject;
 import org.apache.maven.reporting.MavenReportException;
@@ -96,6 +97,7 @@ public class ParentUpdatesReportTest {
                         new DefaultArtifactHandlerStub("default")));
 
                 session = mockMavenSession();
+                mojoExecution = mock(MojoExecution.class);
             }
         }.generate(sinkFactory.createSink(os), sinkFactory, Locale.getDefault());
 
diff --git a/versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/PluginUpdatesReportTest.java b/versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/PluginUpdatesReportTest.java
index 943ae768..be9e2718 100644
--- a/versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/PluginUpdatesReportTest.java
+++ b/versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/PluginUpdatesReportTest.java
@@ -33,6 +33,7 @@ import org.apache.maven.doxia.sink.SinkFactory;
 import org.apache.maven.model.Build;
 import org.apache.maven.model.Plugin;
 import org.apache.maven.model.PluginManagement;
+import org.apache.maven.plugin.MojoExecution;
 import org.apache.maven.project.MavenProject;
 import org.apache.maven.reporting.MavenReportException;
 import org.codehaus.mojo.versions.model.RuleSet;
@@ -52,6 +53,7 @@ import static org.hamcrest.CoreMatchers.not;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.anyOf;
 import static org.hamcrest.Matchers.matchesPattern;
+import static org.mockito.Mockito.mock;
 
 /**
  * Basic tests for {@linkplain PluginUpdatesReport}.
@@ -76,6 +78,7 @@ public class PluginUpdatesReportTest {
             project.getBuild().setPluginManagement(new PluginManagement());
 
             session = mockMavenSession();
+            mojoExecution = mock(MojoExecution.class);
         }
 
         public TestPluginUpdatesReport withPlugins(Plugin... plugins) {
diff --git a/versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/PluginUpdatesXmlRendererTest.java b/versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/PluginUpdatesXmlRendererTest.java
index 9ada3928..0eb681ac 100644
--- a/versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/PluginUpdatesXmlRendererTest.java
+++ b/versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/PluginUpdatesXmlRendererTest.java
@@ -29,7 +29,6 @@ import org.apache.maven.artifact.DefaultArtifact;
 import org.apache.maven.model.Plugin;
 import org.codehaus.mojo.versions.api.ArtifactVersions;
 import org.codehaus.mojo.versions.api.PluginUpdatesDetails;
-import org.codehaus.mojo.versions.ordering.MavenVersionComparator;
 import org.codehaus.mojo.versions.reporting.model.PluginUpdatesModel;
 import org.codehaus.mojo.versions.utils.ArtifactVersionService;
 import org.codehaus.mojo.versions.utils.DependencyBuilder;
@@ -75,8 +74,7 @@ public class PluginUpdatesXmlRendererTest {
                                         artifactOf("default-group", "artifactA", "1.0.0"),
                                         Stream.of("1.0.0", "1.0.1", "1.1.0", "2.0.0")
                                                 .map(ArtifactVersionService::getArtifactVersion)
-                                                .collect(Collectors.toList()),
-                                        new MavenVersionComparator()),
+                                                .collect(Collectors.toList())),
                                 singletonMap(
                                         DependencyBuilder.newBuilder()
                                                 .withGroupId("default-group")
@@ -87,8 +85,7 @@ public class PluginUpdatesXmlRendererTest {
                                                 artifactOf("default-group", "artifactB", "1.0.0"),
                                                 Stream.of("1.0.0", "1.0.1-SNAPSHOT", "1.1.0-rc1", "2.0.0")
                                                         .map(ArtifactVersionService::getArtifactVersion)
-                                                        .collect(Collectors.toList()),
-                                                new MavenVersionComparator())),
+                                                        .collect(Collectors.toList()))),
                                 false)),
                 emptyMap());
         new PluginUpdatesXmlReportRenderer(pluginUpdates, tempFile, false).render();
diff --git a/versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/SeparatePatternsForIncludesAnExcludesTest.java b/versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/SeparatePatternsForIncludesAnExcludesTest.java
index eea66c7a..9b1b7dd8 100644
--- a/versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/SeparatePatternsForIncludesAnExcludesTest.java
+++ b/versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/SeparatePatternsForIncludesAnExcludesTest.java
@@ -2,19 +2,42 @@ package org.codehaus.mojo.versions;
 
 import java.util.List;
 
+import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager;
+import org.apache.maven.plugin.logging.Log;
+import org.codehaus.mojo.versions.api.PomHelper;
 import org.codehaus.mojo.versions.rewriting.MutableXMLStreamReader;
+import org.codehaus.mojo.versions.utils.ArtifactFactory;
+import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluator;
 import org.junit.Before;
 import org.junit.Test;
+import org.mockito.Mock;
 
+import static org.codehaus.mojo.versions.utils.MockUtils.mockArtifactHandlerManager;
 import static org.junit.Assert.assertEquals;
+import static org.mockito.MockitoAnnotations.openMocks;
 
 public class SeparatePatternsForIncludesAnExcludesTest {
 
     AbstractVersionsDependencyUpdaterMojo mojo;
 
+    @Mock
+    private Log log;
+
+    private PomHelper pomHelper;
+
+    private ArtifactFactory artifactFactory;
+
+    @Mock
+    private ExpressionEvaluator expressionEvaluator;
+
     @Before
     public void setUp() throws Exception {
-        mojo = new AbstractVersionsDependencyUpdaterMojo(null, null, null, null) {
+        openMocks(this);
+        ArtifactHandlerManager artifactHandlerManager = mockArtifactHandlerManager();
+        artifactFactory = new ArtifactFactory(artifactHandlerManager);
+        pomHelper = new PomHelper(artifactFactory, expressionEvaluator);
+
+        mojo = new AbstractVersionsDependencyUpdaterMojo(artifactFactory, null, null, null) {
             @Override
             protected boolean getProcessDependencies() {
                 return true;
diff --git a/versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/SetMojoTest.java b/versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/SetMojoTest.java
index 301279f1..c2e21aba 100644
--- a/versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/SetMojoTest.java
+++ b/versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/SetMojoTest.java
@@ -14,9 +14,11 @@ import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager;
 import org.apache.maven.model.Model;
 import org.apache.maven.plugin.MojoExecutionException;
 import org.apache.maven.plugin.MojoFailureException;
+import org.apache.maven.plugin.logging.Log;
 import org.apache.maven.plugin.testing.AbstractMojoTestCase;
 import org.apache.maven.plugin.testing.MojoRule;
 import org.apache.maven.project.MavenProject;
+import org.codehaus.mojo.versions.api.PomHelper;
 import org.codehaus.mojo.versions.utils.ArtifactFactory;
 import org.codehaus.mojo.versions.utils.TestLog;
 import org.codehaus.mojo.versions.utils.TestUtils;
@@ -24,6 +26,7 @@ import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
+import org.mockito.Mock;
 
 import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
 import static org.codehaus.mojo.versions.utils.MockUtils.mockArtifactHandlerManager;
@@ -41,6 +44,11 @@ public class SetMojoTest extends AbstractMojoTestCase {
 
     private Path tempDir;
 
+    @Mock
+    protected Log log;
+
+    protected PomHelper pomHelper;
+
     protected ArtifactFactory artifactFactory;
 
     @Before
@@ -59,7 +67,7 @@ public class SetMojoTest extends AbstractMojoTestCase {
 
     @Test
     public void testGetIncrementedVersion() throws MojoExecutionException {
-        new SetMojo(null, null, null, null, null, null) {
+        new SetMojo(artifactFactory, null, null, null, null, null) {
             {
                 assertThat(getIncrementedVersion("1", null), is("2-SNAPSHOT"));
                 assertThat(getIncrementedVersion("1.0", null), is("1.1-SNAPSHOT"));
diff --git a/versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/UpdatePropertiesMojoTest.java b/versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/UpdatePropertiesMojoTest.java
index 467cd837..1ad8ec54 100644
--- a/versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/UpdatePropertiesMojoTest.java
+++ b/versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/UpdatePropertiesMojoTest.java
@@ -20,7 +20,6 @@ import java.nio.file.Paths;
 import java.util.HashMap;
 
 import org.codehaus.mojo.versions.change.DefaultDependencyVersionChange;
-import org.codehaus.mojo.versions.model.RuleSet;
 import org.codehaus.mojo.versions.utils.TestChangeRecorder;
 import org.codehaus.mojo.versions.utils.TestUtils;
 import org.junit.Test;
@@ -102,11 +101,6 @@ public class UpdatePropertiesMojoTest extends UpdatePropertiesMojoTestBase {
                 put("artifactA", new String[] {"6.2.5.Final", "8.0.0.Final"});
             }
         });
-        mojo.ruleSet = new RuleSet() {
-            {
-                setComparisonMethod("numeric");
-            }
-        };
 
         mojo.execute();
         assertThat(changeRecorder.getChanges(), is(empty()));
diff --git a/versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/UseLatestVersionsMojoTestBase.java b/versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/UseLatestVersionsMojoTestBase.java
index 98fe354b..70977f96 100644
--- a/versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/UseLatestVersionsMojoTestBase.java
+++ b/versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/UseLatestVersionsMojoTestBase.java
@@ -7,8 +7,10 @@ import java.util.HashMap;
 import java.util.HashSet;
 
 import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager;
+import org.apache.maven.plugin.MojoExecution;
 import org.apache.maven.plugin.MojoExecutionException;
 import org.apache.maven.plugin.MojoFailureException;
+import org.apache.maven.plugin.logging.Log;
 import org.apache.maven.project.MavenProject;
 import org.codehaus.mojo.versions.api.PomHelper;
 import org.codehaus.mojo.versions.api.VersionRetrievalException;
@@ -16,11 +18,14 @@ import org.codehaus.mojo.versions.change.DefaultDependencyVersionChange;
 import org.codehaus.mojo.versions.utils.ArtifactFactory;
 import org.codehaus.mojo.versions.utils.DependencyBuilder;
 import org.codehaus.mojo.versions.utils.TestChangeRecorder;
+import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluator;
 import org.eclipse.aether.RepositorySystem;
 import org.hamcrest.core.Is;
 import org.junit.Before;
 import org.junit.Test;
+import org.mockito.Mock;
 import org.mockito.MockedStatic;
+import org.mockito.Mockito;
 
 import static org.apache.maven.artifact.Artifact.SCOPE_COMPILE;
 import static org.apache.maven.plugin.testing.ArtifactStubFactory.setVariableValueToObject;
@@ -34,22 +39,34 @@ import static org.hamcrest.Matchers.not;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.mockStatic;
+import static org.mockito.MockitoAnnotations.openMocks;
 
 public abstract class UseLatestVersionsMojoTestBase {
     protected UseLatestVersionsMojoBase mojo;
 
     protected TestChangeRecorder changeRecorder;
 
+    @Mock
+    protected Log log;
+
+    protected PomHelper pomHelper;
+
     protected ArtifactFactory artifactFactory;
 
+    @Mock
+    protected ExpressionEvaluator expressionEvaluator;
+
     protected abstract UseLatestVersionsMojoBase createMojo() throws IllegalAccessException, MojoExecutionException;
 
     @Before
     public void setUp() throws Exception {
+        openMocks(this);
         changeRecorder = new TestChangeRecorder();
         ArtifactHandlerManager artifactHandlerManager = mockArtifactHandlerManager();
         artifactFactory = new ArtifactFactory(artifactHandlerManager);
+        pomHelper = new PomHelper(artifactFactory, expressionEvaluator);
         mojo = createMojo();
+        mojo.mojoExecution = Mockito.mock(MojoExecution.class);
     }
 
     protected RepositorySystem createRepositorySystem() {
diff --git a/versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/UseReleasesMojoTest.java b/versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/UseReleasesMojoTest.java
index 2b706191..e8a07a31 100644
--- a/versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/UseReleasesMojoTest.java
+++ b/versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/UseReleasesMojoTest.java
@@ -4,19 +4,25 @@ import javax.xml.stream.XMLStreamException;
 
 import org.apache.maven.artifact.DefaultArtifact;
 import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager;
+import org.apache.maven.execution.MavenSession;
 import org.apache.maven.model.Model;
+import org.apache.maven.plugin.MojoExecution;
 import org.apache.maven.plugin.MojoExecutionException;
 import org.apache.maven.plugin.MojoFailureException;
+import org.apache.maven.plugin.logging.Log;
 import org.apache.maven.plugin.testing.AbstractMojoTestCase;
 import org.apache.maven.project.MavenProject;
 import org.codehaus.mojo.versions.api.PomHelper;
 import org.codehaus.mojo.versions.api.VersionRetrievalException;
 import org.codehaus.mojo.versions.change.DefaultDependencyVersionChange;
+import org.codehaus.mojo.versions.rule.RuleService;
 import org.codehaus.mojo.versions.utils.ArtifactFactory;
 import org.codehaus.mojo.versions.utils.DependencyBuilder;
 import org.codehaus.mojo.versions.utils.TestChangeRecorder;
+import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluator;
 import org.junit.Before;
 import org.junit.Test;
+import org.mockito.Mock;
 import org.mockito.MockedStatic;
 
 import static java.util.Collections.emptyList;
@@ -31,7 +37,9 @@ import static org.hamcrest.Matchers.hasItem;
 import static org.hamcrest.Matchers.startsWith;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.mockStatic;
+import static org.mockito.MockitoAnnotations.openMocks;
 
 /*
  * Copyright MojoHaus and Contributors
@@ -55,16 +63,29 @@ public class UseReleasesMojoTest extends AbstractMojoTestCase {
     private TestChangeRecorder changeRecorder;
     private UseReleasesMojo mojo;
 
+    @Mock
+    private Log log;
+
+    private RuleService ruleService;
+
+    private PomHelper pomHelper;
+
     private ArtifactFactory artifactFactory;
 
+    @Mock
+    private ExpressionEvaluator expressionEvaluator;
+
     @Before
     public void setUp() throws Exception {
         super.setUp();
+        openMocks(this);
         ArtifactHandlerManager artifactHandlerManager = mockArtifactHandlerManager();
         artifactFactory = new ArtifactFactory(artifactHandlerManager);
+        MavenSession mavenSession = mockMavenSession();
         changeRecorder = new TestChangeRecorder();
         mojo = new UseReleasesMojo(artifactFactory, mockAetherRepositorySystem(), null, changeRecorder.asTestMap());
         setVariableValueToObject(mojo, "reactorProjects", emptyList());
+        mojo.mojoExecution = mock(MojoExecution.class);
         mojo.project = new MavenProject() {
             {
                 setModel(new Model() {
@@ -76,7 +97,7 @@ public class UseReleasesMojoTest extends AbstractMojoTestCase {
                 });
             }
         };
-        mojo.session = mockMavenSession();
+        mojo.session = mavenSession;
     }
 
     @Test