dependencies = mavenProject.getDependencies();
+ if ( hasDependencies( dependencies ) )
+ {
+ for ( Dependency dependency : dependencies )
+ {
+ logger.debug( " -> Dep:" + dependency.getGroupId() + ":" + dependency.getArtifactId() + ":"
+ + dependency.getVersion() );
+ if ( isDependencyPartOfTheReactor( dependency, sortedProjects ) )
+ {
+ if ( !dependency.getVersion().equals( reactorVersion ) )
+ {
+ addDep( result, mavenProject, dependency );
+ }
+ }
+ }
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * This method will check the following situation within a multi-module build.
+ *
+ *
+ * <parent>
+ * <groupId>...</groupId>
+ * <artifactId>...</artifactId>
+ * <version>1.0-SNAPSHOT</version>
+ * </parent>
+ *
+ * <version>1.1-SNAPSHOT</version>
+ *
+ *
+ * @param projectList The sorted list of the reactor modules.
+ * @return The resulting list will contain the modules in the reactor which do the thing in the example above. The
+ * list will never null. If the list is empty no violation have happened.
+ */
+ private List isReactorVersionConsistent( List projectList )
+ {
+ List result = new ArrayList<>();
+
+ if ( projectList != null && !projectList.isEmpty() )
+ {
+ String version = projectList.get( 0 ).getVersion();
+ logger.debug( "First version:" + version );
+ for ( MavenProject mavenProject : projectList )
+ {
+ logger.debug( " -> checking " + mavenProject.getId() );
+ if ( !version.equals( mavenProject.getVersion() ) )
+ {
+ result.add( mavenProject );
+ }
+ }
+ }
+ return result;
+ }
+
+ private boolean hasDependencies( List dependencies )
+ {
+ return dependencies != null && !dependencies.isEmpty();
+ }
+
+ private boolean hasParent( MavenProject mavenProject )
+ {
+ return mavenProject.getParent() != null;
+ }
+
+ public boolean isIgnoreModuleDependencies()
+ {
+ return ignoreModuleDependencies;
+ }
+
+ public void setIgnoreModuleDependencies( boolean ignoreModuleDependencies )
+ {
+ this.ignoreModuleDependencies = ignoreModuleDependencies;
+ }
+
+ /**
+ * This will add the given user message to the output.
+ *
+ * @param sb The already initialized exception message part.
+ */
+ private void addMessageIfExist( StringBuilder sb )
+ {
+ if ( !StringUtils.isEmpty( getMessage() ) )
+ {
+ sb.append( getMessage() );
+ sb.append( System.lineSeparator() );
+ }
+ }
+
+}
diff --git a/Java-base/maven-enforcer/src/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireActiveProfile.java b/Java-base/maven-enforcer/src/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireActiveProfile.java
new file mode 100644
index 000000000..31fde9cd9
--- /dev/null
+++ b/Java-base/maven-enforcer/src/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireActiveProfile.java
@@ -0,0 +1,153 @@
+package org.apache.maven.plugins.enforcer;
+
+/*
+ * 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.ArrayList;
+import java.util.List;
+
+import org.apache.maven.enforcer.rule.api.EnforcerRuleException;
+import org.apache.maven.enforcer.rule.api.EnforcerRuleHelper;
+import org.apache.maven.model.Profile;
+import org.apache.maven.project.MavenProject;
+import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluationException;
+import org.codehaus.plexus.util.StringUtils;
+
+/**
+ * This rule checks that some profiles are active.
+ *
+ * @author Brian Fox
+ */
+public class RequireActiveProfile
+ extends AbstractNonCacheableEnforcerRule
+{
+
+ /** Comma separated list of profiles to check.
+ *
+ * @see {@link #setProfiles(String)}
+ * @see {@link #getProfiles()}
+ */
+ private String profiles = null;
+
+ /** If all profiles must be active. If false, only one must be active
+ *
+ * @see {@link #setAll(boolean)}
+ * @see {@link #isAll()}
+ */
+ private boolean all = true;
+
+ public final String getProfiles()
+ {
+ return profiles;
+ }
+
+ public final void setProfiles( String profiles )
+ {
+ this.profiles = profiles;
+ }
+
+ public final boolean isAll()
+ {
+ return all;
+ }
+
+ public final void setAll( boolean all )
+ {
+ this.all = all;
+ }
+
+ @Override
+ public void execute( EnforcerRuleHelper theHelper )
+ throws EnforcerRuleException
+ {
+ List missingProfiles = new ArrayList<>();
+ try
+ {
+ MavenProject project = (MavenProject) theHelper.evaluate( "${project}" );
+ if ( StringUtils.isNotEmpty( profiles ) )
+ {
+ String[] profs = profiles.split( "," );
+ for ( String profile : profs )
+ {
+ if ( !isProfileActive( project, profile ) )
+ {
+ missingProfiles.add( profile );
+ }
+ }
+
+ boolean fail = false;
+ if ( !missingProfiles.isEmpty() )
+ {
+ if ( all || missingProfiles.size() == profs.length )
+ {
+ fail = true;
+ }
+ }
+
+ if ( fail )
+ {
+ String message = getMessage();
+ StringBuilder buf = new StringBuilder();
+ if ( message != null )
+ {
+ buf.append( message + System.lineSeparator() );
+ }
+
+ for ( String profile : missingProfiles )
+ {
+ buf.append( "Profile \"" + profile + "\" is not activated." + System.lineSeparator() );
+ }
+
+ throw new EnforcerRuleException( buf.toString() );
+ }
+
+ }
+
+ }
+ catch ( ExpressionEvaluationException e )
+ {
+ throw new EnforcerRuleException( "Unable to retrieve the project.", e );
+ }
+
+ }
+
+ /**
+ * Checks if profile is active.
+ *
+ * @param project the project
+ * @param profileName the profile name
+ * @return true
if profile is active, otherwise false
+ */
+ protected boolean isProfileActive( MavenProject project, String profileName )
+ {
+ List activeProfiles = project.getActiveProfiles();
+ if ( activeProfiles != null && !activeProfiles.isEmpty() )
+ {
+ for ( Profile profile : activeProfiles )
+ {
+ if ( profile.getId().equals( profileName ) )
+ {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+}
diff --git a/Java-base/maven-enforcer/src/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireEnvironmentVariable.java b/Java-base/maven-enforcer/src/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireEnvironmentVariable.java
new file mode 100755
index 000000000..d325f98c1
--- /dev/null
+++ b/Java-base/maven-enforcer/src/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireEnvironmentVariable.java
@@ -0,0 +1,94 @@
+package org.apache.maven.plugins.enforcer;
+
+/*
+ * 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.enforcer.rule.api.EnforcerRule;
+import org.apache.maven.enforcer.rule.api.EnforcerRuleHelper;
+
+/**
+ * This rule checks that certain environment variable is set.
+ *
+ * @author Marvin Froeder
+ */
+public class RequireEnvironmentVariable
+ extends AbstractPropertyEnforcerRule
+{
+
+ /**
+ * Specify the required variable.
+ */
+ private String variableName = null;
+
+ /**
+ * @param variableName the variable name
+ *
+ * @see #setVariableName(String)
+ * @see #getVariableName()
+ */
+ public final void setVariableName( String variableName )
+ {
+ this.variableName = variableName;
+ }
+
+ public final String getVariableName()
+ {
+ return variableName;
+ }
+
+ @Override
+ public String resolveValue( EnforcerRuleHelper helper )
+ {
+ String envValue = System.getenv( variableName );
+ return envValue;
+ }
+
+ @Override
+ public boolean isCacheable()
+ {
+ // environment variables won't change while maven is on the run
+ return true;
+ }
+
+ @Override
+ public boolean isResultValid( EnforcerRule cachedRule )
+ {
+ // this rule shall always have the same result, since environment
+ // variables are set before maven is launched
+ return true;
+ }
+
+ @Override
+ public String getCacheId()
+ {
+ return variableName;
+ }
+
+ @Override
+ public String getPropertyName()
+ {
+ return variableName;
+ }
+
+ @Override
+ public String getName()
+ {
+ return "Environment variable";
+ }
+}
diff --git a/Java-base/maven-enforcer/src/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireFileChecksum.java b/Java-base/maven-enforcer/src/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireFileChecksum.java
new file mode 100644
index 000000000..bbed9e7ea
--- /dev/null
+++ b/Java-base/maven-enforcer/src/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireFileChecksum.java
@@ -0,0 +1,180 @@
+package org.apache.maven.plugins.enforcer;
+
+/*
+ * 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.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.commons.codec.digest.DigestUtils;
+import org.apache.maven.enforcer.rule.api.EnforcerRuleException;
+import org.apache.maven.enforcer.rule.api.EnforcerRuleHelper;
+
+/**
+ * Rule to validate a file to match the specified checksum.
+ *
+ * @author Edward Samson
+ * @author Lyubomyr Shaydariv
+ */
+public class RequireFileChecksum
+ extends AbstractNonCacheableEnforcerRule
+{
+
+ private File file;
+
+ private String checksum;
+
+ private String type;
+
+ private String nonexistentFileMessage;
+
+ @Override
+ public void execute( EnforcerRuleHelper helper )
+ throws EnforcerRuleException
+ {
+ if ( this.file == null )
+ {
+ throw new EnforcerRuleException( "Input file unspecified" );
+ }
+
+ if ( this.type == null )
+ {
+ throw new EnforcerRuleException( "Hash type unspecified" );
+ }
+
+ if ( this.checksum == null )
+ {
+ throw new EnforcerRuleException( "Checksum unspecified" );
+ }
+
+ if ( !this.file.exists() )
+ {
+ String message = nonexistentFileMessage;
+ if ( message == null )
+ {
+ message = "File does not exist: " + this.file.getAbsolutePath();
+ }
+ throw new EnforcerRuleException( message );
+ }
+
+ if ( this.file.isDirectory() )
+ {
+ throw new EnforcerRuleException( "Cannot calculate the checksum of directory: "
+ + this.file.getAbsolutePath() );
+ }
+
+ if ( !this.file.canRead() )
+ {
+ throw new EnforcerRuleException( "Cannot read file: " + this.file.getAbsolutePath() );
+ }
+
+ String checksum = calculateChecksum();
+
+ if ( !checksum.equalsIgnoreCase( this.checksum ) )
+ {
+ String exceptionMessage = getMessage();
+ if ( exceptionMessage == null )
+ {
+ exceptionMessage = this.type + " hash of " + this.file + " was " + checksum
+ + " but expected " + this.checksum;
+ }
+ throw new EnforcerRuleException( exceptionMessage );
+ }
+ }
+
+ /**
+ * The file to check.
+ *
+ * @param file file
+ */
+ public void setFile( File file )
+ {
+ this.file = file;
+ }
+
+ /**
+ * The expected checksum value.
+ *
+ * @param checksum checksum
+ */
+ public void setChecksum( String checksum )
+ {
+ this.checksum = checksum;
+ }
+
+ /**
+ * The checksum algorithm to use. Possible values: "md5", "sha1", "sha256", "sha384", "sha512".
+ *
+ * @param type algorithm
+ */
+ public void setType( String type )
+ {
+ this.type = type;
+ }
+
+ /**
+ * The friendly message to use when the file does not exist.
+ *
+ * @param nonexistentFileMessage message
+ */
+ public void setNonexistentFileMessage( String nonexistentFileMessage )
+ {
+ this.nonexistentFileMessage = nonexistentFileMessage;
+ }
+
+ private String calculateChecksum()
+ throws EnforcerRuleException
+ {
+ try ( InputStream inputStream = new FileInputStream( this.file ) )
+ {
+ String checksum;
+ if ( "md5".equals( this.type ) )
+ {
+ checksum = DigestUtils.md5Hex( inputStream );
+ }
+ else if ( "sha1".equals( this.type ) )
+ {
+ checksum = DigestUtils.shaHex( inputStream );
+ }
+ else if ( "sha256".equals( this.type ) )
+ {
+ checksum = DigestUtils.sha256Hex( inputStream );
+ }
+ else if ( "sha384".equals( this.type ) )
+ {
+ checksum = DigestUtils.sha384Hex( inputStream );
+ }
+ else if ( "sha512".equals( this.type ) )
+ {
+ checksum = DigestUtils.sha512Hex( inputStream );
+ }
+ else
+ {
+ throw new EnforcerRuleException( "Unsupported hash type: " + this.type );
+ }
+ return checksum;
+ }
+ catch ( IOException e )
+ {
+ throw new EnforcerRuleException( "Unable to calculate checksum", e );
+ }
+ }
+}
diff --git a/Java-base/maven-enforcer/src/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireFilesDontExist.java b/Java-base/maven-enforcer/src/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireFilesDontExist.java
new file mode 100644
index 000000000..7ad08f568
--- /dev/null
+++ b/Java-base/maven-enforcer/src/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireFilesDontExist.java
@@ -0,0 +1,43 @@
+package org.apache.maven.plugins.enforcer;
+
+/*
+ * 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.io.File;
+
+/**
+ * The Class RequireFilesDontExist.
+ */
+public class RequireFilesDontExist
+ extends AbstractRequireFiles
+{
+ @Override
+ boolean checkFile( File file )
+ {
+ // if we get here and the handle is null, treat it as a success
+ return file == null ? true : !file.exists();
+ }
+
+ @Override
+ String getErrorMsg()
+ {
+ return "Some files should not exist:" + System.lineSeparator();
+ }
+
+}
diff --git a/Java-base/maven-enforcer/src/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireFilesExist.java b/Java-base/maven-enforcer/src/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireFilesExist.java
new file mode 100644
index 000000000..5727bd811
--- /dev/null
+++ b/Java-base/maven-enforcer/src/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireFilesExist.java
@@ -0,0 +1,43 @@
+package org.apache.maven.plugins.enforcer;
+
+/*
+ * 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.io.File;
+
+/**
+ * The Class RequireFilesExist.
+ */
+public class RequireFilesExist
+ extends AbstractRequireFiles
+{
+ @Override
+ boolean checkFile( File file )
+ {
+ // if we get here and the handle is null, treat it as a success
+ return file == null ? true : file.exists();
+ }
+
+ @Override
+ String getErrorMsg()
+ {
+ return "Some required files are missing:" + System.lineSeparator();
+ }
+
+}
diff --git a/Java-base/maven-enforcer/src/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireFilesSize.java b/Java-base/maven-enforcer/src/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireFilesSize.java
new file mode 100644
index 000000000..6ad928539
--- /dev/null
+++ b/Java-base/maven-enforcer/src/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireFilesSize.java
@@ -0,0 +1,164 @@
+package org.apache.maven.plugins.enforcer;
+
+/*
+ * 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.io.File;
+
+import org.apache.maven.enforcer.rule.api.EnforcerRule;
+import org.apache.maven.enforcer.rule.api.EnforcerRuleException;
+import org.apache.maven.enforcer.rule.api.EnforcerRuleHelper;
+import org.apache.maven.plugin.logging.Log;
+import org.apache.maven.project.MavenProject;
+import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluationException;
+
+/**
+ * Rule to validate the main artifact is within certain size constraints.
+ *
+ * @author brianf
+ * @author Roman Stumm
+ */
+public class RequireFilesSize
+ extends AbstractRequireFiles
+{
+
+ private static final long MAXSIZE = 10000;
+
+ /** the max size allowed. */
+ private long maxsize = MAXSIZE;
+
+ /** the min size allowed. */
+ private long minsize = 0;
+
+ /** The error msg. */
+ private String errorMsg;
+
+ /** The log. */
+ private Log log;
+
+ @Override
+ public void execute( EnforcerRuleHelper helper )
+ throws EnforcerRuleException
+ {
+ this.log = helper.getLog();
+
+ // if the file is already defined, use that. Otherwise get the main artifact.
+ if ( getFiles().length == 0 )
+ {
+ try
+ {
+ MavenProject project = (MavenProject) helper.evaluate( "${project}" );
+ setFiles( new File[1] );
+ getFiles()[0] = project.getArtifact().getFile();
+
+ super.execute( helper );
+ }
+ catch ( ExpressionEvaluationException e )
+ {
+ throw new EnforcerRuleException( "Unable to retrieve the project.", e );
+ }
+ }
+ else
+ {
+ super.execute( helper );
+ }
+
+ }
+
+ @Override
+ public boolean isCacheable()
+ {
+ return false;
+ }
+
+ @Override
+ public boolean isResultValid( EnforcerRule cachedRule )
+ {
+ return false;
+ }
+
+ @Override
+ boolean checkFile( File file )
+ {
+ if ( file == null )
+ {
+ // if we get here and it's null, treat it as a success.
+ return true;
+ }
+
+ // check the file now
+ if ( file.exists() )
+ {
+ long length = file.length();
+ if ( length < minsize )
+ {
+ this.errorMsg = ( file + " size (" + length + ") too small. Min. is " + minsize );
+ return false;
+ }
+ else if ( length > maxsize )
+ {
+ this.errorMsg = ( file + " size (" + length + ") too large. Max. is " + maxsize );
+ return false;
+ }
+ else
+ {
+
+ this.log.debug( file
+ + " size ("
+ + length
+ + ") is OK ("
+ + ( minsize == maxsize || minsize == 0 ? ( "max. " + maxsize )
+ : ( "between " + minsize + " and " + maxsize ) ) + " byte)." );
+
+ return true;
+ }
+ }
+ else
+ {
+ this.errorMsg = ( file + " does not exist!" );
+ return false;
+ }
+ }
+
+ @Override
+ String getErrorMsg()
+ {
+ return this.errorMsg;
+ }
+
+ public long getMaxsize()
+ {
+ return maxsize;
+ }
+
+ public void setMaxsize( long maxsize )
+ {
+ this.maxsize = maxsize;
+ }
+
+ public long getMinsize()
+ {
+ return minsize;
+ }
+
+ public void setMinsize( long minsize )
+ {
+ this.minsize = minsize;
+ }
+}
diff --git a/Java-base/maven-enforcer/src/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireJavaVersion.java b/Java-base/maven-enforcer/src/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireJavaVersion.java
new file mode 100644
index 000000000..4076ac8f8
--- /dev/null
+++ b/Java-base/maven-enforcer/src/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireJavaVersion.java
@@ -0,0 +1,101 @@
+package org.apache.maven.plugins.enforcer;
+
+/*
+ * 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.Arrays;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.commons.lang3.SystemUtils;
+import org.apache.maven.artifact.versioning.ArtifactVersion;
+import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
+import org.apache.maven.enforcer.rule.api.EnforcerRuleException;
+import org.apache.maven.enforcer.rule.api.EnforcerRuleHelper;
+import org.apache.maven.plugin.logging.Log;
+import org.codehaus.plexus.util.StringUtils;
+
+/**
+ * This rule checks that the Java version is allowed.
+ *
+ * @author Brian Fox
+ */
+public class RequireJavaVersion
+ extends AbstractVersionEnforcer
+{
+ @Override
+ public void execute( EnforcerRuleHelper helper )
+ throws EnforcerRuleException
+ {
+ String javaVersion = SystemUtils.JAVA_VERSION;
+ Log log = helper.getLog();
+
+ log.debug( "Detected Java String: '" + javaVersion + "'" );
+ javaVersion = normalizeJDKVersion( javaVersion );
+ log.debug( "Normalized Java String: '" + javaVersion + "'" );
+
+ ArtifactVersion detectedJdkVersion = new DefaultArtifactVersion( javaVersion );
+
+ log.debug( "Parsed Version: Major: " + detectedJdkVersion.getMajorVersion() + " Minor: "
+ + detectedJdkVersion.getMinorVersion() + " Incremental: " + detectedJdkVersion.getIncrementalVersion()
+ + " Build: " + detectedJdkVersion.getBuildNumber() + " Qualifier: " + detectedJdkVersion.getQualifier() );
+
+ enforceVersion( helper.getLog(), "JDK", getVersion(), detectedJdkVersion );
+ }
+
+ /**
+ * Converts a jdk string from 1.5.0-11b12 to a single 3 digit version like 1.5.0-11
+ *
+ * @param theJdkVersion to be converted.
+ * @return the converted string.
+ */
+ public static String normalizeJDKVersion( String theJdkVersion )
+ {
+
+ theJdkVersion = theJdkVersion.replaceAll( "_|-", "." );
+ String tokenArray[] = StringUtils.split( theJdkVersion, "." );
+ List tokens = Arrays.asList( tokenArray );
+ StringBuilder buffer = new StringBuilder( theJdkVersion.length() );
+
+ Iterator iter = tokens.iterator();
+ for ( int i = 0; i < tokens.size() && i < 4; i++ )
+ {
+ String section = iter.next();
+ section = section.replaceAll( "[^0-9]", "" );
+
+ if ( StringUtils.isNotEmpty( section ) )
+ {
+ buffer.append( Integer.parseInt( section ) );
+
+ if ( i != 2 )
+ {
+ buffer.append( '.' );
+ }
+ else
+ {
+ buffer.append( '-' );
+ }
+ }
+ }
+
+ String version = buffer.toString();
+ version = StringUtils.stripEnd( version, "-" );
+ return StringUtils.stripEnd( version, "." );
+ }
+}
diff --git a/Java-base/maven-enforcer/src/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireMavenVersion.java b/Java-base/maven-enforcer/src/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireMavenVersion.java
new file mode 100644
index 000000000..50ba8697a
--- /dev/null
+++ b/Java-base/maven-enforcer/src/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireMavenVersion.java
@@ -0,0 +1,55 @@
+package org.apache.maven.plugins.enforcer;
+
+/*
+ * 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.apache.maven.enforcer.rule.api.EnforcerRuleException;
+import org.apache.maven.enforcer.rule.api.EnforcerRuleHelper;
+import org.apache.maven.execution.MavenSession;
+import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluationException;
+
+/**
+ * This rule checks that the Maven version is allowed.
+ *
+ * @author Brian Fox
+ */
+public class RequireMavenVersion
+ extends AbstractVersionEnforcer
+{
+ @Override
+ public void execute( EnforcerRuleHelper helper )
+ throws EnforcerRuleException
+ {
+ try
+ {
+ MavenSession mavenSession = (MavenSession) helper.evaluate( "${session}" );
+ String mavenVersion = mavenSession.getSystemProperties().getProperty( "maven.version" );
+ helper.getLog().debug( "Detected Maven Version: " + mavenVersion );
+ DefaultArtifactVersion detectedVersion = new DefaultArtifactVersion( mavenVersion );
+ enforceVersion( helper.getLog(), "Maven", getVersion(), detectedVersion );
+ }
+ catch ( ExpressionEvaluationException e )
+ {
+ throw new EnforcerRuleException( "Unable to retrieve the session.", e );
+ }
+
+ }
+
+}
diff --git a/Java-base/maven-enforcer/src/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireNoRepositories.java b/Java-base/maven-enforcer/src/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireNoRepositories.java
new file mode 100644
index 000000000..23a21dde0
--- /dev/null
+++ b/Java-base/maven-enforcer/src/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireNoRepositories.java
@@ -0,0 +1,229 @@
+package org.apache.maven.plugins.enforcer;
+
+/*
+ * 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.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.maven.enforcer.rule.api.EnforcerRuleException;
+import org.apache.maven.enforcer.rule.api.EnforcerRuleHelper;
+import org.apache.maven.execution.MavenSession;
+import org.apache.maven.model.Model;
+import org.apache.maven.model.Repository;
+import org.apache.maven.plugin.logging.Log;
+import org.apache.maven.project.MavenProject;
+import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluationException;
+import org.codehaus.plexus.util.StringUtils;
+
+/**
+ * This rule checks that this pom or its parents don't define a repository.
+ *
+ * @author Brian Fox
+ */
+public class RequireNoRepositories
+ extends AbstractNonCacheableEnforcerRule
+{
+ private static final String VERSION = " version:";
+
+ /**
+ * Whether to ban non-plugin repositories. By default they are banned.
+ *
+ * @see #setBanRepositories(boolean)
+ */
+ private boolean banRepositories = true;
+
+ /**
+ * Whether to ban plugin repositories. By default they are banned.
+ *
+ * @see #setBanPluginRepositories(boolean)
+ */
+ private boolean banPluginRepositories = true;
+
+ /**
+ * Specify explicitly allowed non-plugin repositories. This is a list of ids.
+ *
+ * @see #setAllowedRepositories(List)
+ */
+ private List allowedRepositories = Collections.emptyList();
+
+ /**
+ * Specify explicitly allowed plugin repositories. This is a list of ids.
+ *
+ * @see #setAllowedPluginRepositories(List)
+ */
+ private List allowedPluginRepositories = Collections.emptyList();
+
+ /**
+ * Whether to allow repositories which only resolve snapshots. By default they are banned.
+ *
+ * @see #setAllowSnapshotRepositories(boolean)
+ */
+ private boolean allowSnapshotRepositories = false;
+
+ /**
+ * Whether to allow plugin repositories which only resolve snapshots. By default they are banned.
+ *
+ * @see {@link #setAllowSnapshotPluginRepositories(boolean)}
+ */
+ private boolean allowSnapshotPluginRepositories = false;
+
+ public final void setBanRepositories( boolean banRepositories )
+ {
+ this.banRepositories = banRepositories;
+ }
+
+ public final void setBanPluginRepositories( boolean banPluginRepositories )
+ {
+ this.banPluginRepositories = banPluginRepositories;
+ }
+
+ public final void setAllowedRepositories( List allowedRepositories )
+ {
+ this.allowedRepositories = allowedRepositories;
+ }
+
+ public final void setAllowedPluginRepositories( List allowedPluginRepositories )
+ {
+ this.allowedPluginRepositories = allowedPluginRepositories;
+ }
+
+ public final void setAllowSnapshotRepositories( boolean allowSnapshotRepositories )
+ {
+ this.allowSnapshotRepositories = allowSnapshotRepositories;
+ }
+
+ public final void setAllowSnapshotPluginRepositories( boolean allowSnapshotPluginRepositories )
+ {
+ this.allowSnapshotPluginRepositories = allowSnapshotPluginRepositories;
+ }
+
+ private Log logger;
+
+ @Override
+ public void execute( EnforcerRuleHelper helper )
+ throws EnforcerRuleException
+ {
+ logger = helper.getLog();
+
+ MavenSession session;
+ try
+ {
+ session = (MavenSession) helper.evaluate( "${session}" );
+
+ List sortedProjects = session.getProjectDependencyGraph().getSortedProjects();
+
+ List models = new ArrayList<>();
+ for ( MavenProject mavenProject : sortedProjects )
+ {
+ logger.debug( "Scanning project: " + mavenProject.getGroupId() + ":" + mavenProject.getArtifactId()
+ + VERSION + mavenProject.getVersion() );
+ models.add( mavenProject.getOriginalModel() );
+ }
+
+ List badModels = new ArrayList<>();
+
+ StringBuilder newMsg = new StringBuilder();
+ newMsg.append( "Some poms have repositories defined:" + System.lineSeparator() );
+
+ for ( Model model : models )
+ {
+ if ( banRepositories )
+ {
+ List repos = model.getRepositories();
+ if ( repos != null && !repos.isEmpty() )
+ {
+ List bannedRepos =
+ findBannedRepositories( repos, allowedRepositories, allowSnapshotRepositories );
+ if ( !bannedRepos.isEmpty() )
+ {
+ badModels.add( model );
+ newMsg.append(
+ model.getGroupId() + ":" + model.getArtifactId() + VERSION + model.getVersion()
+ + " has repositories " + bannedRepos );
+ }
+ }
+ }
+ if ( banPluginRepositories )
+ {
+ List repos = model.getPluginRepositories();
+ if ( repos != null && !repos.isEmpty() )
+ {
+ List bannedRepos =
+ findBannedRepositories( repos, allowedPluginRepositories, allowSnapshotPluginRepositories );
+ if ( !bannedRepos.isEmpty() )
+ {
+ badModels.add( model );
+ newMsg.append(
+ model.getGroupId() + ":" + model.getArtifactId() + VERSION + model.getVersion()
+ + " has plugin repositories " + bannedRepos );
+ }
+ }
+ }
+ }
+
+ // if anything was found, log it then append the
+ // optional message.
+ if ( !badModels.isEmpty() )
+ {
+ String message = getMessage();
+ if ( StringUtils.isNotEmpty( message ) )
+ {
+ newMsg.append( message );
+ }
+
+ throw new EnforcerRuleException( newMsg.toString() );
+ }
+
+ }
+ catch ( ExpressionEvaluationException e )
+ {
+ throw new EnforcerRuleException( e.getLocalizedMessage() );
+ }
+ }
+
+ /**
+ *
+ * @param repos all repositories, never {@code null}
+ * @param allowedRepos allowed repositories, never {@code null}
+ * @param allowSnapshots
+ * @return List of banned repositoreis.
+ */
+ private static List findBannedRepositories( List repos, List allowedRepos,
+ boolean allowSnapshots )
+ {
+ List bannedRepos = new ArrayList<>( allowedRepos.size() );
+ for ( Repository r : repos )
+ {
+ if ( !allowedRepos.contains( r.getId() ) )
+ {
+ if ( !allowSnapshots || r.getReleases() == null || r.getReleases().isEnabled() )
+ {
+ // if we are not allowing snapshots and this repo is enabled for releases
+ // it is banned. We don't care whether it is enabled for snapshots
+ // if you define a repo and don't enable it for anything, then we have nothing
+ // to worry about
+ bannedRepos.add( r.getId() );
+ }
+ }
+ }
+ return bannedRepos;
+ }
+}
diff --git a/Java-base/maven-enforcer/src/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireOS.java b/Java-base/maven-enforcer/src/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireOS.java
new file mode 100644
index 000000000..abc4c3678
--- /dev/null
+++ b/Java-base/maven-enforcer/src/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireOS.java
@@ -0,0 +1,421 @@
+package org.apache.maven.plugins.enforcer;
+
+/*
+ * 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.Iterator;
+
+import org.apache.maven.enforcer.rule.api.EnforcerRule;
+import org.apache.maven.enforcer.rule.api.EnforcerRuleException;
+import org.apache.maven.enforcer.rule.api.EnforcerRuleHelper;
+import org.apache.maven.model.Activation;
+import org.apache.maven.model.ActivationOS;
+import org.apache.maven.model.Profile;
+import org.apache.maven.plugin.logging.Log;
+import org.apache.maven.model.profile.activation.ProfileActivator;
+import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
+import org.codehaus.plexus.util.Os;
+import org.codehaus.plexus.util.StringUtils;
+
+/**
+ * This rule checks that the OS is allowed by combinations of family, name, version and cpu architecture. The behavior
+ * is exactly the same as the Maven Os profile activation so the same values are allowed here.
+ *
+ * @author Brian Fox
+ */
+public class RequireOS
+ extends AbstractStandardEnforcerRule
+{
+ private ProfileActivator activator;
+
+ /**
+ * The OS family type desired
+ * Possible values:
+ *
+ * - dos
+ * - mac
+ * - netware
+ * - os/2
+ * - tandem
+ * - unix
+ * - windows
+ * - win9x
+ * - z/os
+ * - os/400
+ *
+ *
+ * @see {@link #setFamily(String)}
+ * @see {@link #getFamily()}
+ */
+ private String family = null;
+
+ /**
+ * The OS name desired.
+ *
+ * @see {@link #setName(String)}
+ * @see {@link #getName()}
+ */
+ private String name = null;
+
+ /**
+ * The OS version desired.
+ *
+ * @see {@link #setVersion(String)}
+ * @see {@link #getVersion()}
+ */
+ private String version = null;
+
+ /**
+ * The OS architecture desired.
+ *
+ * @see {@link #setArch(String)}
+ * @see {@link #getArch()}
+ */
+ private String arch = null;
+
+ /**
+ * Display detected OS information.
+ *
+ * @see {@link #setDisplay(boolean)}
+ * @see {@link #isDisplay()}
+ */
+ private boolean display = false;
+
+ /**
+ * Instantiates a new RequireOS.
+ */
+ public RequireOS()
+ {
+
+ }
+
+ // For testing
+ RequireOS( ProfileActivator activator )
+ {
+ this.activator = activator;
+ }
+
+
+ @Override
+ public void execute( EnforcerRuleHelper helper )
+ throws EnforcerRuleException
+ {
+
+ displayOSInfo( helper.getLog(), display );
+
+ if ( allParamsEmpty() )
+ {
+ throw new EnforcerRuleException( "All parameters can not be empty. "
+ + "You must pick at least one of (family, name, version, arch) "
+ + "or use -Denforcer.os.display=true to see the current OS information." );
+ }
+
+ try
+ {
+ activator = helper.getComponent( ProfileActivator.class, "os" );
+ }
+ catch ( ComponentLookupException e )
+ {
+ throw new EnforcerRuleException( e.getMessage() );
+ }
+
+ if ( isValidFamily( this.family ) )
+ {
+ if ( !isAllowed() )
+ {
+ String message = getMessage();
+ if ( StringUtils.isEmpty( message ) )
+ {
+ //@formatter:off
+ message =
+ ( "OS Arch: "
+ + Os.OS_ARCH + " Family: "
+ + Os.OS_FAMILY + " Name: "
+ + Os.OS_NAME + " Version: "
+ + Os.OS_VERSION + " is not allowed by" + ( arch != null ? " Arch=" + arch : "" )
+ + ( family != null ? " Family=" + family : "" )
+ + ( name != null ? " Name=" + name : "" )
+ + ( version != null ? " Version=" + version : "" ) );
+ //@formatter:on
+ }
+ throw new EnforcerRuleException( message );
+ }
+ }
+ else
+ {
+ final int minimumBufferSize = 50;
+ StringBuilder buffer = new StringBuilder( minimumBufferSize );
+ Iterator> iter = Os.getValidFamilies().iterator();
+ while ( iter.hasNext() )
+ {
+ buffer.append( iter.next() );
+ buffer.append( ", " );
+ }
+ String help = StringUtils.stripEnd( buffer.toString().trim(), "." );
+ throw new EnforcerRuleException( "Invalid Family type used. Valid family types are: " + help );
+ }
+ }
+
+ /**
+ * Log the current OS information.
+ *
+ * @param log the log
+ * @param info the info
+ */
+ public void displayOSInfo( Log log, boolean info )
+ {
+ String string =
+ "OS Info: Arch: " + Os.OS_ARCH + " Family: " + Os.OS_FAMILY + " Name: " + Os.OS_NAME + " Version: "
+ + Os.OS_VERSION;
+
+ if ( !info )
+ {
+ log.debug( string );
+ }
+ else
+ {
+ log.info( string );
+ }
+ }
+
+ /**
+ * Helper method to determine if the current OS is allowed based on the injected values for family, name, version
+ * and arch.
+ *
+ * @return true if the version is allowed.
+ */
+ public boolean isAllowed()
+ {
+ return activator.isActive( createProfile(), null, null );
+ }
+
+ /**
+ * Helper method to check that at least one of family, name, version or arch is set.
+ *
+ * @return true if all parameters are empty.
+ */
+ public boolean allParamsEmpty()
+ {
+ // CHECKSTYLE_OFF: LineLength
+ return ( StringUtils.isEmpty( family ) && StringUtils.isEmpty( arch ) && StringUtils.isEmpty( name ) && StringUtils.isEmpty( version ) );
+ // CHECKSTYLE_ON: LineLength
+ }
+
+ /**
+ * Creates a Profile object that contains the activation information.
+ *
+ * @return a properly populated profile to be used for OS validation.
+ */
+ private Profile createProfile()
+ {
+ Profile profile = new Profile();
+ profile.setActivation( createActivation() );
+ return profile;
+ }
+
+ /**
+ * Creates an Activation object that contains the ActivationOS information.
+ *
+ * @return a properly populated Activation object.
+ */
+ private Activation createActivation()
+ {
+ Activation activation = new Activation();
+ activation.setActiveByDefault( false );
+ activation.setOs( createOsBean() );
+ return activation;
+ }
+
+ /**
+ * Creates an ActivationOS object containing family, name, version and arch.
+ *
+ * @return a properly populated ActivationOS object.
+ */
+ private ActivationOS createOsBean()
+ {
+ ActivationOS os = new ActivationOS();
+
+ os.setArch( arch );
+ os.setFamily( family );
+ os.setName( name );
+ os.setVersion( version );
+
+ return os;
+ }
+
+ /**
+ * Helper method to check if the given family is in the following list:
+ *
+ * - dos
+ * - mac
+ * - netware
+ * - os/2
+ * - tandem
+ * - unix
+ * - windows
+ * - win9x
+ * - z/os
+ * - os/400
+ *
+ * Note: '!' is allowed at the beginning of the string and still considered valid.
+ *
+ * @param theFamily the family to check.
+ * @return true if one of the valid families.
+ */
+ public boolean isValidFamily( String theFamily )
+ {
+
+ // in case they are checking !family
+ theFamily = StringUtils.stripStart( theFamily, "!" );
+
+ return ( StringUtils.isEmpty( theFamily ) || Os.getValidFamilies().contains( theFamily ) );
+ }
+
+ /**
+ * Gets the arch.
+ *
+ * @return the arch
+ */
+ public String getArch()
+ {
+ return this.arch;
+ }
+
+ /**
+ * Sets the arch.
+ *
+ * @param theArch the arch to set
+ */
+ public void setArch( String theArch )
+ {
+ this.arch = theArch;
+ }
+
+ /**
+ * Gets the family.
+ *
+ * @return the family
+ */
+ public String getFamily()
+ {
+ return this.family;
+ }
+
+ /**
+ * Sets the family.
+ *
+ * @param theFamily the family to set
+ */
+ public void setFamily( String theFamily )
+ {
+ this.family = theFamily;
+ }
+
+ /**
+ * Gets the name.
+ *
+ * @return the name
+ */
+ public String getName()
+ {
+ return this.name;
+ }
+
+ /**
+ * Sets the name.
+ *
+ * @param theName the name to set
+ */
+ public void setName( String theName )
+ {
+ this.name = theName;
+ }
+
+ /**
+ * Gets the version.
+ *
+ * @return the version
+ */
+ public String getVersion()
+ {
+ return this.version;
+ }
+
+ /**
+ * Sets the version.
+ *
+ * @param theVersion the version to set
+ */
+ public void setVersion( String theVersion )
+ {
+ this.version = theVersion;
+ }
+
+ /**
+ * @param display The value for the display.
+ */
+ public final void setDisplay( boolean display )
+ {
+ this.display = display;
+ }
+
+ public final boolean isDisplay()
+ {
+ return display;
+ }
+
+ @Override
+ public String getCacheId()
+ {
+ // return the hashcodes of all the parameters
+ StringBuilder b = new StringBuilder();
+ if ( StringUtils.isNotEmpty( version ) )
+ {
+ b.append( version.hashCode() );
+ }
+ if ( StringUtils.isNotEmpty( name ) )
+ {
+ b.append( name.hashCode() );
+ }
+ if ( StringUtils.isNotEmpty( arch ) )
+ {
+ b.append( arch.hashCode() );
+ }
+ if ( StringUtils.isNotEmpty( family ) )
+ {
+ b.append( family.hashCode() );
+ }
+ return b.toString();
+ }
+
+ @Override
+ public boolean isCacheable()
+ {
+ // the os is not going to change between projects in the same build.
+ return true;
+ }
+
+ @Override
+ public boolean isResultValid( EnforcerRule theCachedRule )
+ {
+ // i will always return the hash of the parameters as my id. If my parameters are the same, this
+ // rule must always have the same result.
+ return true;
+ }
+}
diff --git a/Java-base/maven-enforcer/src/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequirePluginVersions.java b/Java-base/maven-enforcer/src/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequirePluginVersions.java
new file mode 100644
index 000000000..c96793b48
--- /dev/null
+++ b/Java-base/maven-enforcer/src/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequirePluginVersions.java
@@ -0,0 +1,1272 @@
+package org.apache.maven.plugins.enforcer;
+
+/*
+ * 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.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.maven.BuildFailureException;
+import org.apache.maven.artifact.Artifact;
+import org.apache.maven.artifact.factory.ArtifactFactory;
+import org.apache.maven.artifact.repository.ArtifactRepository;
+import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
+import org.apache.maven.artifact.resolver.ArtifactResolutionException;
+import org.apache.maven.artifact.resolver.ArtifactResolver;
+import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
+import org.apache.maven.artifact.versioning.VersionRange;
+import org.apache.maven.enforcer.rule.api.EnforcerRuleException;
+import org.apache.maven.enforcer.rule.api.EnforcerRuleHelper;
+import org.apache.maven.execution.MavenSession;
+import org.apache.maven.lifecycle.Lifecycle;
+import org.apache.maven.lifecycle.LifecycleExecutionException;
+import org.apache.maven.lifecycle.LifecycleExecutor;
+import org.apache.maven.lifecycle.mapping.LifecycleMapping;
+import org.apache.maven.model.BuildBase;
+import org.apache.maven.model.Model;
+import org.apache.maven.model.Plugin;
+import org.apache.maven.model.Profile;
+import org.apache.maven.model.ReportPlugin;
+import org.apache.maven.plugin.InvalidPluginException;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugin.PluginManager;
+import org.apache.maven.plugin.PluginManagerException;
+import org.apache.maven.plugin.PluginNotFoundException;
+import org.apache.maven.plugin.descriptor.PluginDescriptor;
+import org.apache.maven.plugin.logging.Log;
+import org.apache.maven.plugin.version.PluginVersionNotFoundException;
+import org.apache.maven.plugin.version.PluginVersionResolutionException;
+import org.apache.maven.plugins.enforcer.utils.EnforcerRuleUtils;
+import org.apache.maven.plugins.enforcer.utils.PluginWrapper;
+import org.apache.maven.project.MavenProject;
+import org.apache.maven.settings.Settings;
+import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluationException;
+import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
+import org.codehaus.plexus.util.ReflectionUtils;
+import org.codehaus.plexus.util.StringUtils;
+import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
+
+/**
+ * This rule will enforce that all plugins specified in the poms have a version declared.
+ *
+ * @author Brian Fox
+ */
+public class RequirePluginVersions
+ extends AbstractNonCacheableEnforcerRule
+{
+
+ private EnforcerRuleHelper helper;
+
+ /**
+ * Don't allow the LATEST identifier.
+ *
+ * @see {@link #setBanLatest(boolean)}
+ * @see {@link #isBanLatest()}
+ */
+ private boolean banLatest = true;
+
+ /**
+ * Don't allow the RELEASE identifier.
+ *
+ * @see {@link #setBanRelease(boolean)}
+ * @see {@link #isBanRelease()}
+ */
+ private boolean banRelease = true;
+
+ /**
+ * Don't allow snapshot plugins.
+ *
+ * @see {@link #setBanSnapshots(boolean)}
+ * @see {@link #isBanSnapshots()}
+ */
+ private boolean banSnapshots = true;
+
+ /**
+ * Don't allow timestamp snapshot plugins.
+ *
+ * @see {@link #setBanTimestamps(boolean)}
+ * @see {@link #isBanTimestamps()}
+ */
+ private boolean banTimestamps = true;
+
+ /**
+ * The comma separated list of phases that should be used to find lifecycle plugin bindings. The default value is
+ * "clean,deploy,site".
+ *
+ * @see {@link #setPhases(String)}
+ * @see {@link #getPhases()}
+ */
+ private String phases = "clean,deploy,site";
+
+ /**
+ * Additional plugins to enforce have versions. These are plugins that may not be in the poms but are used anyway,
+ * like help, eclipse etc.
+ * The plugins should be specified in the form: group:artifactId
.
+ *
+ * @see {@link #setAdditionalPlugins(List)}
+ * @see {@link #getAdditionalPlugins()}
+ */
+ private List additionalPlugins;
+
+ /**
+ * Plugins to skip for version enforcement. The plugins should be specified in the form:
+ * group:artifactId
. NOTE: This is deprecated, use unCheckedPluginList instead.
+ *
+ * @see {@link #setUnCheckedPlugins(List)}
+ * @see {@link #getUnCheckedPlugins()}
+ */
+ private List unCheckedPlugins;
+
+ /**
+ * Same as unCheckedPlugins but as a comma list to better support properties. Sample form:
+ * group:artifactId,group2:artifactId2
+ *
+ * @since 1.0-beta-1
+ * @see {@link #setUnCheckedPlugins(List)}
+ * @see {@link #getUnCheckedPlugins()}
+ */
+ private String unCheckedPluginList;
+
+ /** The plugin manager. */
+ private PluginManager pluginManager;
+
+ /** The phase to lifecycle map. */
+ private Map phaseToLifecycleMap;
+
+ /** The lifecycles. */
+ private Collection lifecycles;
+
+ /** The factory. */
+ private ArtifactFactory factory;
+
+ /** The resolver. */
+ private ArtifactResolver resolver;
+
+ /** The local. */
+ private ArtifactRepository local;
+
+ /** The remote repositories. */
+ private List remoteRepositories;
+
+ /** The log. */
+ private Log log;
+
+ /** The session. */
+ private MavenSession session;
+
+ /** The utils. */
+ private EnforcerRuleUtils utils;
+
+ @Override
+ public void execute( EnforcerRuleHelper helper )
+ throws EnforcerRuleException
+ {
+ this.log = helper.getLog();
+ this.helper = helper;
+
+ MavenProject project;
+ try
+ {
+ // get the various expressions out of the helper.
+
+ project = (MavenProject) helper.evaluate( "${project}" );
+ LifecycleExecutor life;
+ life = helper.getComponent( LifecycleExecutor.class );
+
+ Object defaultLifeCycles = ReflectionUtils.getValueIncludingSuperclasses( "defaultLifeCycles", life );
+ Map lifecyclesMap = (Map) ReflectionUtils.getValueIncludingSuperclasses( "lifecycles", defaultLifeCycles );
+ lifecycles = lifecyclesMap.values();
+
+ session = (MavenSession) helper.evaluate( "${session}" );
+ pluginManager = helper.getComponent( PluginManager.class );
+ factory = helper.getComponent( ArtifactFactory.class );
+ resolver = helper.getComponent( ArtifactResolver.class );
+ local = (ArtifactRepository) helper.evaluate( "${localRepository}" );
+ remoteRepositories = project.getRemoteArtifactRepositories();
+
+ utils = new EnforcerRuleUtils( helper );
+
+ // get all the plugins that are bound to the specified lifecycles
+ Set allPlugins = getBoundPlugins( life, project, phases );
+
+ // insert any additional plugins specified by the user.
+ allPlugins = addAdditionalPlugins( allPlugins, additionalPlugins );
+ allPlugins.addAll( getProfilePlugins( project ) );
+
+ // pull out any we should skip
+ allPlugins =
+ removeUncheckedPlugins( combineUncheckedPlugins( unCheckedPlugins, unCheckedPluginList ), allPlugins );
+
+ // there's nothing to do here
+ if ( allPlugins.isEmpty() )
+ {
+ log.info( "No plugin bindings found." );
+ return;
+ }
+ else
+ {
+ log.debug( "All Plugins in use: " + allPlugins );
+ }
+
+ // get all the plugins that are mentioned in the pom (and parents)
+ List pluginWrappers = getAllPluginEntries( project );
+
+ for ( PluginWrapper pluginWrapper : pluginWrappers )
+ {
+ log.debug( "pluginWrappers: " + pluginWrapper.getGroupId() + ":" + pluginWrapper.getArtifactId() + ":"
+ + pluginWrapper.getVersion() + " source:" + pluginWrapper.getSource() );
+ }
+ // now look for the versions that aren't valid and add to a list.
+ List failures = new ArrayList();
+
+ for ( Plugin plugin : allPlugins )
+ {
+ if ( !hasValidVersionSpecified( helper, plugin, pluginWrappers ) )
+ {
+ failures.add( plugin );
+ }
+ }
+
+ // if anything was found, log it then append the optional message.
+ if ( !failures.isEmpty() )
+ {
+ handleMessagesToTheUser( project, failures );
+ }
+ }
+ catch ( ExpressionEvaluationException e )
+ {
+ throw new EnforcerRuleException( "Unable to Evaluate an Expression:" + e.getLocalizedMessage() );
+ }
+ catch ( ComponentLookupException e )
+ {
+ throw new EnforcerRuleException( "Unable to lookup a component:" + e.getLocalizedMessage() );
+ }
+ catch ( Exception e )
+ {
+ throw new EnforcerRuleException( e.getLocalizedMessage() );
+ }
+ }
+
+ private void handleMessagesToTheUser( MavenProject project, List failures )
+ throws EnforcerRuleException
+ {
+ StringBuilder newMsg = new StringBuilder();
+ newMsg.append( "Some plugins are missing valid versions:" );
+ handleBanMessages( newMsg );
+ newMsg.append( "\n" );
+ for ( Plugin plugin : failures )
+ {
+ newMsg.append( plugin.getGroupId() );
+ newMsg.append( ":" );
+ newMsg.append( plugin.getArtifactId() );
+
+ try
+ {
+ newMsg.append( ". \tThe version currently in use is " );
+
+ Plugin currentPlugin = findCurrentPlugin( plugin, project );
+
+ if ( currentPlugin != null )
+ {
+ newMsg.append( currentPlugin.getVersion() );
+ }
+ else
+ {
+ newMsg.append( "unknown" );
+ }
+ }
+ catch ( Exception e )
+ {
+ // lots can go wrong here. Don't allow any issues trying to
+ // determine the issue stop me
+ log.debug( "Exception while determining plugin Version.", e );
+ newMsg.append( ". Unable to determine the plugin version." );
+ }
+ newMsg.append( "\n" );
+ }
+ String message = getMessage();
+ if ( StringUtils.isNotEmpty( message ) )
+ {
+ newMsg.append( message );
+ }
+
+ throw new EnforcerRuleException( newMsg.toString() );
+ }
+
+ private void handleBanMessages( StringBuilder newMsg )
+ {
+ if ( banLatest || banRelease || banSnapshots || banTimestamps )
+ {
+ newMsg.append( " (" );
+ if ( banLatest )
+ {
+ newMsg.append( "LATEST " );
+ }
+ if ( banRelease )
+ {
+ newMsg.append( "RELEASE " );
+ }
+ if ( banSnapshots || banTimestamps )
+ {
+ newMsg.append( "SNAPSHOT " );
+ }
+ newMsg.append( "are not allowed)" );
+ }
+ }
+
+ /**
+ * Remove the plugins that the user doesn't want to check.
+ *
+ * @param uncheckedPlugins
+ * @param plugins
+ * @throws MojoExecutionException
+ * @return The plugins which have been removed.
+ */
+ public Set removeUncheckedPlugins( Collection uncheckedPlugins, Set plugins )
+ throws MojoExecutionException
+ {
+ if ( uncheckedPlugins != null && !uncheckedPlugins.isEmpty() )
+ {
+ for ( String pluginKey : uncheckedPlugins )
+ {
+ Plugin plugin = parsePluginString( pluginKey, "UncheckedPlugins" );
+ plugins.remove( plugin );
+ }
+ }
+ return plugins;
+ }
+
+ /**
+ * Combines the old Collection with the new comma separated list.
+ *
+ * @param uncheckedPlugins
+ * @param uncheckedPluginsList
+ * @return List of unchecked plugins.
+ */
+ // CHECKSTYLE_OFF: LineLength
+ public Collection combineUncheckedPlugins( Collection uncheckedPlugins,
+ String uncheckedPluginsList )
+ // CHECKSTYLE_ON: LineLength
+ {
+ // if the comma list is empty, then there's nothing to do here.
+ if ( StringUtils.isNotEmpty( uncheckedPluginsList ) )
+ {
+ // make sure there is a collection to add to.
+ if ( uncheckedPlugins == null )
+ {
+ uncheckedPlugins = new HashSet();
+ }
+ else if ( !uncheckedPlugins.isEmpty() && log != null )
+ {
+ log.warn( "The parameter 'unCheckedPlugins' is deprecated. Use 'unCheckedPluginList' instead" );
+ }
+
+ uncheckedPlugins.addAll( Arrays.asList( uncheckedPluginsList.split( "," ) ) );
+ }
+ return uncheckedPlugins;
+ }
+
+ /**
+ * Add the additional plugins if they don't exist yet.
+ *
+ * @param existing the existing
+ * @param additional the additional
+ * @return the sets the
+ * @throws MojoExecutionException the mojo execution exception
+ */
+ public Set addAdditionalPlugins( Set existing, List additional )
+ throws MojoExecutionException
+ {
+ if ( additional != null )
+ {
+ for ( String pluginString : additional )
+ {
+ Plugin plugin = parsePluginString( pluginString, "AdditionalPlugins" );
+
+ if ( existing == null )
+ {
+ existing = new HashSet<>();
+ existing.add( plugin );
+ }
+ else if ( !existing.contains( plugin ) )
+ {
+ existing.add( plugin );
+ }
+ }
+ }
+ return existing;
+ }
+
+ /**
+ * Helper method to parse and inject a Plugin.
+ *
+ * @param pluginString
+ * @param field
+ * @throws MojoExecutionException
+ * @return the plugin
+ */
+ protected Plugin parsePluginString( String pluginString, String field )
+ throws MojoExecutionException
+ {
+ if ( pluginString != null )
+ {
+ String[] pluginStrings = pluginString.split( ":" );
+ if ( pluginStrings.length == 2 )
+ {
+ Plugin plugin = new Plugin();
+ plugin.setGroupId( StringUtils.strip( pluginStrings[0] ) );
+ plugin.setArtifactId( StringUtils.strip( pluginStrings[1] ) );
+
+ return plugin;
+ }
+ else
+ {
+ throw new MojoExecutionException( "Invalid " + field + " string: " + pluginString );
+ }
+ }
+ else
+ {
+ throw new MojoExecutionException( "Invalid " + field + " string: " + pluginString );
+ }
+
+ }
+
+ /**
+ * Finds the plugins that are listed in active profiles.
+ *
+ * @param project the project
+ * @return the profile plugins
+ */
+ public Set getProfilePlugins( MavenProject project )
+ {
+ Set result = new HashSet<>();
+ List profiles = project.getActiveProfiles();
+ if ( profiles != null && !profiles.isEmpty() )
+ {
+ for ( Profile p : profiles )
+ {
+ BuildBase b = p.getBuild();
+ if ( b != null )
+ {
+ List plugins = b.getPlugins();
+ if ( plugins != null )
+ {
+ result.addAll( plugins );
+ }
+ }
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Given a plugin, this will retrieve the matching plugin artifact from the model.
+ *
+ * @param plugin plugin to lookup
+ * @param project project to search
+ * @return matching plugin, null
if not found.
+ */
+ protected Plugin findCurrentPlugin( Plugin plugin, MavenProject project )
+ {
+ Plugin found = null;
+ try
+ {
+ Model model = project.getModel();
+ Map plugins = model.getBuild().getPluginsAsMap();
+ found = plugins.get( plugin.getKey() );
+ }
+ catch ( NullPointerException e )
+ {
+ // nothing to do here
+ }
+
+ if ( found == null )
+ {
+ found = resolvePlugin( plugin, project );
+ }
+
+ return found;
+ }
+
+ /**
+ * Resolve plugin.
+ *
+ * @param plugin the plugin
+ * @param project the project
+ * @return the plugin
+ */
+ protected Plugin resolvePlugin( Plugin plugin, MavenProject project )
+ {
+
+ List pluginRepositories = project.getPluginArtifactRepositories();
+ Artifact artifact = factory.createPluginArtifact( plugin.getGroupId(), plugin.getArtifactId(),
+ VersionRange.createFromVersion( "LATEST" ) );
+
+ try
+ {
+ this.resolver.resolve( artifact, pluginRepositories, this.local );
+ plugin.setVersion( artifact.getVersion() );
+ }
+ catch ( ArtifactResolutionException | ArtifactNotFoundException e )
+ {
+ // What does this mean?
+ }
+
+ return plugin;
+ }
+
+ /**
+ * Gets the plugins that are bound to the defined phases. This does not find plugins bound in the pom to a phase
+ * later than the plugin is executing.
+ *
+ * @param life the life
+ * @param project the project
+ * @param thePhases the the phases
+ * @return the bound plugins
+ * @throws PluginNotFoundException the plugin not found exception
+ * @throws LifecycleExecutionException the lifecycle execution exception
+ * @throws IllegalAccessException the illegal access exception
+ */
+ protected Set getBoundPlugins( LifecycleExecutor life, MavenProject project, String thePhases )
+ throws PluginNotFoundException, LifecycleExecutionException, IllegalAccessException
+ {
+
+ Set allPlugins = new HashSet<>();
+
+ // lookup the bindings for all the passed in phases
+ String[] lifecyclePhases = thePhases.split( "," );
+ for ( int i = 0; i < lifecyclePhases.length; i++ )
+ {
+ String lifecyclePhase = lifecyclePhases[i];
+ if ( StringUtils.isNotEmpty( lifecyclePhase ) )
+ {
+ try
+ {
+ Lifecycle lifecycle = getLifecycleForPhase( lifecyclePhase );
+ log.debug( "getBoundPlugins(): " + project.getId() + " " + lifecyclePhase + " "
+ + lifecycle.getId() );
+ allPlugins.addAll( getAllPlugins( project, lifecycle ) );
+ }
+ catch ( BuildFailureException e )
+ {
+ // i'm going to swallow this because the
+ // user may have declared a phase that
+ // doesn't exist for every module.
+ }
+ }
+ }
+ return allPlugins;
+ }
+
+ /**
+ * Checks for valid version specified. Checks to see if the version is specified for the plugin. Can optionally ban
+ * "RELEASE" or "LATEST" even if specified.
+ *
+ * @param helper the helper
+ * @param source the source
+ * @param pluginWrappers the plugins
+ * @return true, if successful
+ */
+ protected boolean hasValidVersionSpecified( EnforcerRuleHelper helper, Plugin source,
+ List pluginWrappers )
+ {
+ boolean found = false;
+ boolean status = false;
+ for ( PluginWrapper plugin : pluginWrappers )
+ {
+ // find the matching plugin entry
+ if ( isMatchingPlugin( source, plugin ) )
+ {
+ found = true;
+ // found the entry. now see if the version is specified
+ String version = plugin.getVersion();
+ try
+ {
+ version = (String) helper.evaluate( version );
+ }
+ catch ( ExpressionEvaluationException e )
+ {
+ return false;
+ }
+
+ if ( isValidVersion( version ) )
+ {
+ helper.getLog().debug( "checking for notEmpty and notIsWhitespace(): " + version );
+ if ( banRelease && version.equals( "RELEASE" ) )
+ {
+ return false;
+ }
+
+ if ( banLatest && version.equals( "LATEST" ) )
+ {
+ return false;
+ }
+
+ if ( banSnapshots && isSnapshot( version ) )
+ {
+ return false;
+ }
+ // the version was specified and not
+ // banned. It's ok. Keep looking through the list to make
+ // sure it's not using a banned version somewhere else.
+
+ status = true;
+
+ if ( !banRelease && !banLatest && !banSnapshots )
+ {
+ // no need to keep looking
+ break;
+ }
+ }
+ }
+ }
+ if ( !found )
+ {
+ helper.getLog().debug( "plugin " + source.getGroupId() + ":" + source.getArtifactId() + " not found" );
+ }
+ return status;
+ }
+
+ private boolean isValidVersion( String version )
+ {
+ return StringUtils.isNotEmpty( version ) && !StringUtils.isWhitespace( version );
+ }
+
+ private boolean isMatchingPlugin( Plugin source, PluginWrapper plugin )
+ {
+ return source.getArtifactId().equals( plugin.getArtifactId() )
+ && source.getGroupId().equals( plugin.getGroupId() );
+ }
+
+ /**
+ * Checks if is snapshot.
+ *
+ * @param baseVersion the base version
+ * @return true, if is snapshot
+ */
+ protected boolean isSnapshot( String baseVersion )
+ {
+ if ( banTimestamps )
+ {
+ return Artifact.VERSION_FILE_PATTERN.matcher( baseVersion ).matches()
+ || baseVersion.endsWith( Artifact.SNAPSHOT_VERSION );
+ }
+ else
+ {
+ return baseVersion.endsWith( Artifact.SNAPSHOT_VERSION );
+ }
+ }
+
+ /*
+ * Uses borrowed lifecycle code to get a list of all plugins bound to the lifecycle.
+ */
+ /**
+ * Gets the all plugins.
+ *
+ * @param project the project
+ * @param lifecycle the lifecycle
+ * @return the all plugins
+ * @throws PluginNotFoundException the plugin not found exception
+ * @throws LifecycleExecutionException the lifecycle execution exception
+ */
+ private Set getAllPlugins( MavenProject project, Lifecycle lifecycle )
+ throws PluginNotFoundException, LifecycleExecutionException
+
+ {
+ log.debug( "RequirePluginVersions.getAllPlugins:" );
+
+ Set plugins = new HashSet<>();
+ // first, bind those associated with the packaging
+ Map mappings = findMappingsForLifecycle( project, lifecycle );
+
+ for ( Map.Entry entry : mappings.entrySet() )
+ {
+ log.debug( " lifecycleMapping = " + entry.getKey() );
+ String pluginsForLifecycle = (String) entry.getValue();
+ log.debug( " plugins = " + pluginsForLifecycle );
+ if ( StringUtils.isNotEmpty( pluginsForLifecycle ) )
+ {
+ String pluginList[] = pluginsForLifecycle.split( "," );
+ for ( String plugin : pluginList )
+ {
+ plugin = StringUtils.strip( plugin );
+ log.debug( " plugin = " + plugin );
+ String tokens[] = plugin.split( ":" );
+ log.debug( " GAV = " + Arrays.asList( tokens ) );
+
+ Plugin p = new Plugin();
+ p.setGroupId( tokens[0] );
+ p.setArtifactId( tokens[1] );
+ plugins.add( p );
+ }
+ }
+ }
+
+ List mojos = findOptionalMojosForLifecycle( project, lifecycle );
+ for ( String value : mojos )
+ {
+ String tokens[] = value.split( ":" );
+
+ Plugin plugin = new Plugin();
+ plugin.setGroupId( tokens[0] );
+ plugin.setArtifactId( tokens[1] );
+ plugins.add( plugin );
+ }
+
+ plugins.addAll( project.getBuildPlugins() );
+
+ return plugins;
+ }
+
+ /*
+ * NOTE: All the code following this point was scooped from the DefaultLifecycleExecutor. There must be a better way
+ * but for now it should work.
+ */
+ /**
+ * Gets the phase to lifecycle map.
+ *
+ * @return the phase to lifecycle map
+ * @throws LifecycleExecutionException the lifecycle execution exception
+ */
+ public Map getPhaseToLifecycleMap()
+ throws LifecycleExecutionException
+ {
+ if ( phaseToLifecycleMap == null )
+ {
+ phaseToLifecycleMap = new HashMap<>();
+
+ for ( Lifecycle lifecycle : lifecycles )
+ {
+ List phases = lifecycle.getPhases();
+ for ( String phase : phases )
+ {
+ log.debug( "getPhaseToLifecycleMap(): phase: " + phase );
+ if ( phaseToLifecycleMap.containsKey( phase ) )
+ {
+ Lifecycle prevLifecycle = (Lifecycle) phaseToLifecycleMap.get( phase );
+ throw new LifecycleExecutionException( "Phase '" + phase
+ + "' is defined in more than one lifecycle: '" + lifecycle.getId() + "' and '"
+ + prevLifecycle.getId() + "'" );
+ }
+ else
+ {
+ phaseToLifecycleMap.put( phase, lifecycle );
+ }
+ }
+ }
+ }
+ return phaseToLifecycleMap;
+ }
+
+ /**
+ * Gets the lifecycle for phase.
+ *
+ * @param phase the phase
+ * @return the lifecycle for phase
+ * @throws BuildFailureException the build failure exception
+ * @throws LifecycleExecutionException the lifecycle execution exception
+ */
+ private Lifecycle getLifecycleForPhase( String phase )
+ throws BuildFailureException, LifecycleExecutionException
+ {
+ Lifecycle lifecycle = getPhaseToLifecycleMap().get( phase );
+
+ if ( lifecycle == null )
+ {
+ throw new BuildFailureException( "Unable to find lifecycle for phase '" + phase + "'" );
+ }
+ return lifecycle;
+ }
+
+ /**
+ * Find mappings for lifecycle.
+ *
+ * @param project the project
+ * @param lifecycle the lifecycle
+ * @return the map
+ * @throws LifecycleExecutionException the lifecycle execution exception
+ * @throws PluginNotFoundException the plugin not found exception
+ */
+ private Map findMappingsForLifecycle( MavenProject project, Lifecycle lifecycle )
+ throws LifecycleExecutionException, PluginNotFoundException
+ {
+ String packaging = project.getPackaging();
+ Map mappings = null;
+
+ LifecycleMapping m = (LifecycleMapping) findExtension( project, LifecycleMapping.ROLE, packaging,
+ session.getSettings(), session.getLocalRepository() );
+ if ( m != null )
+ {
+ mappings = m.getPhases( lifecycle.getId() );
+ }
+
+ Map defaultMappings = lifecycle.getDefaultPhases();
+
+ if ( mappings == null )
+ {
+ try
+ {
+ m = helper.getComponent( LifecycleMapping.class, packaging );
+ mappings = m.getPhases( lifecycle.getId() );
+ }
+ catch ( ComponentLookupException e )
+ {
+ if ( defaultMappings == null )
+ {
+ throw new LifecycleExecutionException( "Cannot find lifecycle mapping for packaging: \'" + packaging
+ + "\'.", e );
+ }
+ }
+ }
+
+ if ( mappings == null )
+ {
+ if ( defaultMappings == null )
+ {
+ throw new LifecycleExecutionException( "Cannot find lifecycle mapping for packaging: \'" + packaging
+ + "\', and there is no default" );
+ }
+ else
+ {
+ mappings = defaultMappings;
+ }
+ }
+
+ return mappings;
+ }
+
+ /**
+ * Find optional mojos for lifecycle.
+ *
+ * @param project the project
+ * @param lifecycle the lifecycle
+ * @return the list
+ * @throws LifecycleExecutionException the lifecycle execution exception
+ * @throws PluginNotFoundException the plugin not found exception
+ */
+ private List findOptionalMojosForLifecycle( MavenProject project, Lifecycle lifecycle )
+ throws LifecycleExecutionException, PluginNotFoundException
+ {
+ String packaging = project.getPackaging();
+ List optionalMojos = null;
+
+ LifecycleMapping m = (LifecycleMapping) findExtension( project, LifecycleMapping.ROLE, packaging,
+ session.getSettings(), session.getLocalRepository() );
+
+ if ( m != null )
+ {
+ optionalMojos = m.getOptionalMojos( lifecycle.getId() );
+ }
+
+ if ( optionalMojos == null )
+ {
+ try
+ {
+ m = helper.getComponent( LifecycleMapping.class, packaging );
+ optionalMojos = m.getOptionalMojos( lifecycle.getId() );
+ }
+ catch ( ComponentLookupException e )
+ {
+ log.debug( "Error looking up lifecycle mapping to retrieve optional mojos. Lifecycle ID: "
+ + lifecycle.getId() + ". Error: " + e.getMessage(), e );
+ }
+ }
+
+ if ( optionalMojos == null )
+ {
+ optionalMojos = Collections.emptyList();
+ }
+
+ return optionalMojos;
+ }
+
+ /**
+ * Find extension.
+ *
+ * @param project the project
+ * @param role the role
+ * @param roleHint the role hint
+ * @param settings the settings
+ * @param localRepository the local repository
+ * @return the object
+ * @throws LifecycleExecutionException the lifecycle execution exception
+ * @throws PluginNotFoundException the plugin not found exception
+ */
+ private Object findExtension( MavenProject project, String role, String roleHint, Settings settings,
+ ArtifactRepository localRepository )
+ throws LifecycleExecutionException, PluginNotFoundException
+ {
+ Object pluginComponent = null;
+
+ List buildPlugins = project.getBuildPlugins();
+ for ( Plugin plugin : buildPlugins )
+ {
+ if ( plugin.isExtensions() )
+ {
+ verifyPlugin( plugin, project, settings, localRepository );
+
+ // TODO: if moved to the plugin manager we
+ // already have the descriptor from above
+ // and so do can lookup the container
+ // directly
+ try
+ {
+ pluginComponent = pluginManager.getPluginComponent( plugin, role, roleHint );
+
+ if ( pluginComponent != null )
+ {
+ break;
+ }
+ }
+ catch ( ComponentLookupException e )
+ {
+ log.debug( "Unable to find the lifecycle component in the extension", e );
+ }
+ catch ( PluginManagerException e )
+ {
+ throw new LifecycleExecutionException( "Error getting extensions from the plugin '"
+ + plugin.getKey() + "': " + e.getMessage(), e );
+ }
+ }
+ }
+ return pluginComponent;
+ }
+
+ /**
+ * Verify plugin.
+ *
+ * @param plugin the plugin
+ * @param project the project
+ * @param settings the settings
+ * @param localRepository the local repository
+ * @return the plugin descriptor
+ * @throws LifecycleExecutionException the lifecycle execution exception
+ * @throws PluginNotFoundException the plugin not found exception
+ */
+ private PluginDescriptor verifyPlugin( Plugin plugin, MavenProject project, Settings settings,
+ ArtifactRepository localRepository )
+ throws LifecycleExecutionException, PluginNotFoundException
+ {
+ PluginDescriptor pluginDescriptor;
+ try
+ {
+ pluginDescriptor = pluginManager.verifyPlugin( plugin, project, settings, localRepository );
+ }
+ catch ( PluginManagerException e )
+ {
+ throw new LifecycleExecutionException( "Internal error in the plugin manager getting plugin '"
+ + plugin.getKey() + "': " + e.getMessage(), e );
+ }
+ catch ( PluginVersionResolutionException | InvalidVersionSpecificationException | InvalidPluginException
+ | ArtifactNotFoundException | ArtifactResolutionException | PluginVersionNotFoundException e )
+ {
+ throw new LifecycleExecutionException( e.getMessage(), e );
+ }
+ return pluginDescriptor;
+ }
+
+ /**
+ * Gets all plugin entries in build.plugins, build.pluginManagement.plugins, profile.build.plugins, reporting and
+ * profile.reporting in this project and all parents
+ *
+ * @param project the project
+ * @return the all plugin entries wrapped in a PluginWrapper Object
+ * @throws ArtifactResolutionException the artifact resolution exception
+ * @throws ArtifactNotFoundException the artifact not found exception
+ * @throws IOException Signals that an I/O exception has occurred.
+ * @throws XmlPullParserException the xml pull parser exception
+ */
+ protected List getAllPluginEntries( MavenProject project )
+ throws ArtifactResolutionException, ArtifactNotFoundException, IOException, XmlPullParserException
+ {
+ List models = new ArrayList<>();
+
+ List sortedProjects = session.getProjectDependencyGraph().getSortedProjects();
+
+ if ( !sortedProjects.isEmpty() && sortedProjects.get( 0 ).getParent() != null )
+ {
+ getOriginalModelFromAllParents( models, sortedProjects );
+ }
+
+ for ( MavenProject mavenProject : sortedProjects )
+ {
+ models.add( mavenProject.getOriginalModel() );
+ }
+
+ List plugins = new ArrayList<>();
+ // now find all the plugin entries, either in
+ // build.plugins or build.pluginManagement.plugins, profiles.plugins and reporting
+ for ( Model model : models )
+ {
+ getPlugins( plugins, model );
+ getReportingPlugins( plugins, model );
+ getPluginManagementPlugins( plugins, model );
+
+ addPluginsInProfiles( plugins, model );
+ }
+
+ return plugins;
+ }
+
+ private void getOriginalModelFromAllParents( List models, List sortedProjects )
+ {
+ MavenProject parent = sortedProjects.get( 0 ).getParent();
+ do
+ {
+ models.add( parent.getOriginalModel() );
+ parent = parent.getParent();
+ }
+ while ( parent != null );
+ }
+
+ private void addPluginsInProfiles( List plugins, Model model )
+ {
+ List profiles = model.getProfiles();
+ for ( Profile profile : profiles )
+ {
+ getProfilePlugins( plugins, model, profile );
+ getProfileReportingPlugins( plugins, model, profile );
+ getProfilePluginManagementPlugins( plugins, model, profile );
+ }
+ }
+
+ private void getProfilePluginManagementPlugins( List plugins, Model model, Profile profile )
+ {
+ try
+ {
+ List modelPlugins = profile.getBuild().getPluginManagement().getPlugins();
+ plugins.addAll( PluginWrapper.addAll( utils.resolvePlugins( modelPlugins ), model.getId() + "profile["
+ + profile.getId() + "].build.pluginManagement.plugins" ) );
+ }
+ catch ( NullPointerException e )
+ {
+ // guess there are no plugins here.
+ }
+ }
+
+ private void getProfileReportingPlugins( List plugins, Model model, Profile profile )
+ {
+ try
+ {
+ List modelReportPlugins = profile.getReporting().getPlugins();
+ // add the reporting plugins
+ plugins.addAll( PluginWrapper.addAll( utils.resolveReportPlugins( modelReportPlugins ), model.getId()
+ + "profile[" + profile.getId() + "].reporting.plugins" ) );
+ }
+ catch ( NullPointerException e )
+ {
+ // guess there are no plugins here.
+ }
+ }
+
+ private void getProfilePlugins( List plugins, Model model, Profile profile )
+ {
+ try
+ {
+ List modelPlugins = profile.getBuild().getPlugins();
+ plugins.addAll( PluginWrapper.addAll( utils.resolvePlugins( modelPlugins ), model.getId()
+ + ".profiles.profile[" + profile.getId() + "].build.plugins" ) );
+ }
+ catch ( NullPointerException e )
+ {
+ // guess there are no plugins here.
+ }
+ }
+
+ private void getPlugins( List plugins, Model model )
+ {
+ try
+ {
+ List modelPlugins = model.getBuild().getPlugins();
+ plugins.addAll( PluginWrapper.addAll( utils.resolvePlugins( modelPlugins ),
+ model.getId() + ".build.plugins" ) );
+ }
+ catch ( NullPointerException e )
+ {
+ // guess there are no plugins here.
+ }
+ }
+
+ private void getPluginManagementPlugins( List plugins, Model model )
+ {
+ try
+ {
+ List modelPlugins = model.getBuild().getPluginManagement().getPlugins();
+ plugins.addAll( PluginWrapper.addAll( utils.resolvePlugins( modelPlugins ),
+ model.getId() + ".build.pluginManagement.plugins" ) );
+ }
+ catch ( NullPointerException e )
+ {
+ // guess there are no plugins here.
+ }
+ }
+
+ private void getReportingPlugins( List plugins, Model model )
+ {
+ try
+ {
+ List modelReportPlugins = model.getReporting().getPlugins();
+ // add the reporting plugins
+ plugins.addAll( PluginWrapper.addAll( utils.resolveReportPlugins( modelReportPlugins ),
+ model.getId() + ".reporting" ) );
+ }
+ catch ( NullPointerException e )
+ {
+ // guess there are no plugins here.
+ }
+ }
+
+ /**
+ * Checks if is ban latest.
+ *
+ * @return the banLatest
+ */
+ protected boolean isBanLatest()
+ {
+ return this.banLatest;
+ }
+
+ /**
+ * Sets the ban latest.
+ *
+ * @param theBanLatest the banLatest to set
+ */
+ protected void setBanLatest( boolean theBanLatest )
+ {
+ this.banLatest = theBanLatest;
+ }
+
+ /**
+ * Checks if is ban release.
+ *
+ * @return the banRelease
+ */
+ protected boolean isBanRelease()
+ {
+ return this.banRelease;
+ }
+
+ /**
+ * Sets the ban release.
+ *
+ * @param theBanRelease the banRelease to set
+ */
+ protected void setBanRelease( boolean theBanRelease )
+ {
+ this.banRelease = theBanRelease;
+ }
+
+ /**
+ * Gets the utils.
+ *
+ * @return the utils
+ */
+ protected EnforcerRuleUtils getUtils()
+ {
+ return this.utils;
+ }
+
+ /**
+ * Sets the utils.
+ *
+ * @param theUtils the utils to set
+ */
+ protected void setUtils( EnforcerRuleUtils theUtils )
+ {
+ this.utils = theUtils;
+ }
+
+ /**
+ * Checks if is ban snapshots.
+ *
+ * @return the banSnapshots
+ */
+ public boolean isBanSnapshots()
+ {
+ return this.banSnapshots;
+ }
+
+ /**
+ * Sets the ban snapshots.
+ *
+ * @param theBanSnapshots the banSnapshots to set
+ */
+ public void setBanSnapshots( boolean theBanSnapshots )
+ {
+ this.banSnapshots = theBanSnapshots;
+ }
+
+ /**
+ * Checks if is ban timestamps.
+ *
+ * @return the banTimestamps
+ */
+ public boolean isBanTimestamps()
+ {
+ return this.banTimestamps;
+ }
+
+ /**
+ * Sets the ban timestamps.
+ *
+ * @param theBanTimestamps the banTimestamps to set
+ */
+ public void setBanTimestamps( boolean theBanTimestamps )
+ {
+ this.banTimestamps = theBanTimestamps;
+ }
+
+ public List getUnCheckedPlugins()
+ {
+ return unCheckedPlugins;
+ }
+
+ public void setUnCheckedPlugins( List unCheckedPlugins )
+ {
+ this.unCheckedPlugins = unCheckedPlugins;
+ }
+
+ public final void setPhases( String phases )
+ {
+ this.phases = phases;
+ }
+
+ public final String getPhases()
+ {
+ return phases;
+ }
+
+ public final void setAdditionalPlugins( List additionalPlugins )
+ {
+ this.additionalPlugins = additionalPlugins;
+ }
+
+ public final List getAdditionalPlugins()
+ {
+ return additionalPlugins;
+ }
+}
diff --git a/Java-base/maven-enforcer/src/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequirePrerequisite.java b/Java-base/maven-enforcer/src/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequirePrerequisite.java
new file mode 100644
index 000000000..725f2aab3
--- /dev/null
+++ b/Java-base/maven-enforcer/src/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequirePrerequisite.java
@@ -0,0 +1,132 @@
+package org.apache.maven.plugins.enforcer;
+
+/*
+ * 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.List;
+
+import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
+import org.apache.maven.artifact.versioning.VersionRange;
+import org.apache.maven.enforcer.rule.api.EnforcerRuleException;
+import org.apache.maven.enforcer.rule.api.EnforcerRuleHelper;
+import org.apache.maven.model.Prerequisites;
+import org.apache.maven.project.MavenProject;
+import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluationException;
+
+/**
+ * @author Robert Scholte
+ * @since 1.3
+ */
+public class RequirePrerequisite
+ extends AbstractNonCacheableEnforcerRule
+{
+ /**
+ * Only the projects with one of these packagings will be enforced to have the correct prerequisite.
+ *
+ * @since 1.4
+ */
+ private List packagings;
+
+ /**
+ * Can either be version or a range, e.g. {@code 2.2.1} or {@code [2.2.1,)}
+ */
+ private String mavenVersion;
+
+ /**
+ * Set the mavenVersion Can either be version or a range, e.g. {@code 2.2.1} or {@code [2.2.1,)}
+ *
+ * @param mavenVersion the version or {@code null}
+ */
+ public void setMavenVersion( String mavenVersion )
+ {
+ this.mavenVersion = mavenVersion;
+ }
+
+ /**
+ * Only the projects with one of these packagings will be enforced to have the correct prerequisite.
+ *
+ * @since 1.4
+ * @param packagings the list of packagings
+ */
+ public void setPackagings( List packagings )
+ {
+ this.packagings = packagings;
+ }
+
+ @Override
+ public void execute( EnforcerRuleHelper helper )
+ throws EnforcerRuleException
+ {
+ try
+ {
+ MavenProject project = (MavenProject) helper.evaluate( "${project}" );
+
+ if ( "pom".equals( project.getPackaging() ) )
+ {
+ helper.getLog().debug( "Packaging is pom, skipping requirePrerequisite rule" );
+ return;
+ }
+
+ if ( packagings != null && !packagings.contains( project.getPackaging() ) )
+ {
+ // CHECKSTYLE_OFF: LineLength
+ helper.getLog().debug( "Packaging is " + project.getPackaging() + ", skipping requirePrerequisite rule" );
+ return;
+ // CHECKSTYLE_ON: LineLength
+ }
+
+ Prerequisites prerequisites = project.getPrerequisites();
+
+ if ( prerequisites == null )
+ {
+ throw new EnforcerRuleException( "Requires prerequisite not set" );
+ }
+
+ if ( mavenVersion != null )
+ {
+
+ VersionRange requiredVersionRange = VersionRange.createFromVersionSpec( mavenVersion );
+
+ if ( !requiredVersionRange.hasRestrictions() )
+ {
+ requiredVersionRange = VersionRange.createFromVersionSpec( "[" + mavenVersion + ",)" );
+ }
+
+ VersionRange specifiedVersion = VersionRange.createFromVersionSpec( prerequisites.getMaven() );
+
+ VersionRange restrictedVersionRange = requiredVersionRange.restrict( specifiedVersion );
+
+ if ( restrictedVersionRange.getRecommendedVersion() == null )
+ {
+ throw new EnforcerRuleException( "The specified Maven prerequisite( " + specifiedVersion
+ + " ) doesn't match the required version: " + mavenVersion );
+ }
+ }
+ }
+ catch ( ExpressionEvaluationException e )
+ {
+ throw new EnforcerRuleException( e.getMessage(), e );
+ }
+ catch ( InvalidVersionSpecificationException e )
+ {
+ throw new EnforcerRuleException( e.getMessage(), e );
+ }
+ }
+
+}
diff --git a/Java-base/maven-enforcer/src/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireProfileIdsExist.java b/Java-base/maven-enforcer/src/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireProfileIdsExist.java
new file mode 100644
index 000000000..e213b08b0
--- /dev/null
+++ b/Java-base/maven-enforcer/src/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireProfileIdsExist.java
@@ -0,0 +1,101 @@
+package org.apache.maven.plugins.enforcer;
+
+/*
+ * 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.ArrayList;
+import java.util.List;
+
+import org.apache.maven.enforcer.rule.api.EnforcerRuleException;
+import org.apache.maven.enforcer.rule.api.EnforcerRuleHelper;
+import org.apache.maven.execution.MavenSession;
+import org.apache.maven.project.MavenProject;
+import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluationException;
+import org.codehaus.plexus.util.StringUtils;
+
+/**
+ * Ensure that all profiles mentioned on the commandline do exist.
+ *
+ * @author Robert Scholte
+ * @author Gabriel Belingueres
+ */
+public class RequireProfileIdsExist extends AbstractNonCacheableEnforcerRule
+{
+ @Override
+ public void execute( EnforcerRuleHelper helper )
+ throws EnforcerRuleException
+ {
+ try
+ {
+ MavenSession session = (MavenSession) helper.evaluate( "${session}" );
+
+ List profileIds = new ArrayList<>();
+ profileIds.addAll( session.getProjectBuildingRequest().getActiveProfileIds() );
+ profileIds.addAll( session.getProjectBuildingRequest().getInactiveProfileIds() );
+
+ for ( MavenProject project : session.getProjects() )
+ {
+ // iterate over all parents
+ MavenProject currentProject = project;
+ do
+ {
+ for ( org.apache.maven.model.Profile profile : currentProject.getModel().getProfiles() )
+ {
+ profileIds.remove( profile.getId() );
+
+ if ( profileIds.isEmpty() )
+ {
+ return;
+ }
+ }
+
+ currentProject = currentProject.getParent();
+ }
+ while ( currentProject != null );
+ }
+
+ for ( org.apache.maven.settings.Profile profile : session.getSettings().getProfiles() )
+ {
+ profileIds.remove( profile.getId() );
+ }
+
+ if ( profileIds.isEmpty() )
+ {
+ return;
+ }
+
+ StringBuilder sb = new StringBuilder();
+ if ( profileIds.size() > 1 )
+ {
+ sb.append( "The requested profiles don't exist: " );
+ }
+ else
+ {
+ sb.append( "The requested profile doesn't exist: " );
+ }
+ sb.append( StringUtils.join( profileIds.iterator(), ", " ) );
+
+ throw new EnforcerRuleException( sb.toString() );
+ }
+ catch ( ExpressionEvaluationException e )
+ {
+ throw new EnforcerRuleException( e.getMessage() );
+ }
+ }
+}
diff --git a/Java-base/maven-enforcer/src/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireProperty.java b/Java-base/maven-enforcer/src/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireProperty.java
new file mode 100644
index 000000000..b915475e9
--- /dev/null
+++ b/Java-base/maven-enforcer/src/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireProperty.java
@@ -0,0 +1,80 @@
+package org.apache.maven.plugins.enforcer;
+
+/*
+ * 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.enforcer.rule.api.EnforcerRuleException;
+import org.apache.maven.enforcer.rule.api.EnforcerRuleHelper;
+import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluationException;
+
+/**
+ * This rule checks that certain properties are set.
+ *
+ * @author Paul Gier
+ */
+public class RequireProperty
+ extends AbstractPropertyEnforcerRule
+{
+
+ /**
+ * Specify the required property.
+ *
+ * @see {@link #setProperty(String)}
+ * @see {@link #getPropertyName()}
+ */
+ private String property = null;
+
+ public final void setProperty( String property )
+ {
+ this.property = property;
+ }
+
+ @Override
+ public Object resolveValue( EnforcerRuleHelper helper )
+ throws EnforcerRuleException
+ {
+ Object propValue = null;
+ try
+ {
+ propValue = helper.evaluate( "${" + property + "}" );
+ }
+ catch ( ExpressionEvaluationException eee )
+ {
+ throw new EnforcerRuleException( "Unable to evaluate property: " + property, eee );
+ }
+ return propValue;
+ }
+
+ protected String resolveValue()
+ {
+ return null;
+ }
+
+ @Override
+ public String getPropertyName()
+ {
+ return property;
+ }
+
+ @Override
+ public String getName()
+ {
+ return "Property";
+ }
+}
diff --git a/Java-base/maven-enforcer/src/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireReleaseDeps.java b/Java-base/maven-enforcer/src/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireReleaseDeps.java
new file mode 100644
index 000000000..888cfd822
--- /dev/null
+++ b/Java-base/maven-enforcer/src/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireReleaseDeps.java
@@ -0,0 +1,232 @@
+package org.apache.maven.plugins.enforcer;
+
+/*
+ * 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.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.maven.artifact.Artifact;
+import org.apache.maven.artifact.resolver.filter.AndArtifactFilter;
+import org.apache.maven.enforcer.rule.api.EnforcerRuleException;
+import org.apache.maven.enforcer.rule.api.EnforcerRuleHelper;
+import org.apache.maven.plugin.logging.Log;
+import org.apache.maven.project.MavenProject;
+import org.apache.maven.shared.artifact.filter.StrictPatternExcludesArtifactFilter;
+import org.apache.maven.shared.artifact.filter.StrictPatternIncludesArtifactFilter;
+import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluationException;
+
+/**
+ * This rule checks that no snapshots are included.
+ *
+ * @author Brian Fox
+ */
+public class RequireReleaseDeps
+ extends AbstractBanDependencies
+{
+
+ /**
+ * Allows this rule to execute only when this project is a release.
+ *
+ * @parameter
+ *
+ * @see {@link #setOnlyWhenRelease(boolean)}
+ * @see {@link #isOnlyWhenRelease()}
+
+ */
+ private boolean onlyWhenRelease = false;
+
+ /**
+ * Allows this rule to fail when the parent is defined as a snapshot.
+ *
+ * @parameter
+ *
+ * @see {@link #setFailWhenParentIsSnapshot(boolean)}
+ * @see {@link #isFailWhenParentIsSnapshot()}
+ */
+ private boolean failWhenParentIsSnapshot = true;
+
+ /**
+ * Dependencies to ignore when checking for release versions. For example, inter-module dependencies
+ * can be excluded from the check and therefore allowed to contain snapshot versions.
+ *
+ * @see {@link #setExcludes(List)}
+ * @see {@link #getExcludes()}
+ */
+ private List excludes = null;
+
+ /**
+ * Dependencies to include when checking for release versions. If any of the included dependencies
+ * have snapshot versions, the rule will fail.
+ *
+ * @see {@link #setIncludes(List)}
+ * @see {@link #getIncludes()}
+ */
+ private List includes = null;
+
+ // Override parent to allow optional ignore of this rule.
+ @Override
+ public void execute( EnforcerRuleHelper helper )
+ throws EnforcerRuleException
+ {
+ boolean callSuper;
+ MavenProject project = null;
+ if ( onlyWhenRelease )
+ {
+ // get the project
+ project = getProject( helper );
+
+ // only call super if this project is a release
+ callSuper = !project.getArtifact().isSnapshot();
+ }
+ else
+ {
+ callSuper = true;
+ }
+ if ( callSuper )
+ {
+ super.execute( helper );
+ if ( failWhenParentIsSnapshot )
+ {
+ if ( project == null )
+ {
+ project = getProject( helper );
+ }
+ Artifact parentArtifact = project.getParentArtifact();
+ if ( parentArtifact != null && parentArtifact.isSnapshot() )
+ {
+ throw new EnforcerRuleException( "Parent Cannot be a snapshot: " + parentArtifact.getId() );
+ }
+ }
+ }
+ }
+
+ /**
+ * @param helper
+ * @return The evaluated {@link MavenProject}.
+ * @throws EnforcerRuleException
+ */
+ private MavenProject getProject( EnforcerRuleHelper helper )
+ throws EnforcerRuleException
+ {
+ try
+ {
+ return (MavenProject) helper.evaluate( "${project}" );
+ }
+ catch ( ExpressionEvaluationException eee )
+ {
+ throw new EnforcerRuleException( "Unable to retrieve the MavenProject: ", eee );
+ }
+ }
+
+ @Override
+ protected Set checkDependencies( Set dependencies, Log log )
+ throws EnforcerRuleException
+ {
+ Set foundSnapshots = new HashSet<>();
+
+ Set filteredDependencies = filterArtifacts( dependencies );
+
+ for ( Artifact artifact : filteredDependencies )
+ {
+ if ( artifact.isSnapshot() )
+ {
+ foundSnapshots.add( artifact );
+ }
+ }
+
+ return foundSnapshots;
+ }
+
+ /*
+ * Filter the dependency artifacts according to the includes and excludes
+ * If includes and excludes are both null, the original set is returned.
+ *
+ * @param dependencies the list of dependencies to filter
+ * @return the resulting set of dependencies
+ */
+ public Set filterArtifacts( Set dependencies )
+ {
+ if ( includes == null && excludes == null )
+ {
+ return dependencies;
+ }
+
+ AndArtifactFilter filter = new AndArtifactFilter( );
+ if ( includes != null )
+ {
+ filter.add( new StrictPatternIncludesArtifactFilter( includes ) );
+ }
+ if ( excludes != null )
+ {
+ filter.add( new StrictPatternExcludesArtifactFilter( excludes ) );
+ }
+
+ Set result = new HashSet<>();
+ for ( Artifact artifact : dependencies )
+ {
+ if ( filter.include( artifact ) )
+ {
+ result.add( artifact );
+ }
+ }
+ return result;
+ }
+
+ public final boolean isOnlyWhenRelease()
+ {
+ return onlyWhenRelease;
+ }
+
+ public final void setOnlyWhenRelease( boolean onlyWhenRelease )
+ {
+ this.onlyWhenRelease = onlyWhenRelease;
+ }
+
+ public final boolean isFailWhenParentIsSnapshot()
+ {
+ return failWhenParentIsSnapshot;
+ }
+
+ public final void setFailWhenParentIsSnapshot( boolean failWhenParentIsSnapshot )
+ {
+ this.failWhenParentIsSnapshot = failWhenParentIsSnapshot;
+ }
+
+ public final void setExcludes( List excludes )
+ {
+ this.excludes = excludes;
+ }
+
+ public final List getExcludes()
+ {
+ return excludes;
+ }
+
+ public void setIncludes( List includes )
+ {
+ this.includes = includes;
+ }
+
+ public List getIncludes()
+ {
+ return includes;
+ }
+}
diff --git a/Java-base/maven-enforcer/src/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireReleaseVersion.java b/Java-base/maven-enforcer/src/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireReleaseVersion.java
new file mode 100644
index 000000000..fd6c6fd98
--- /dev/null
+++ b/Java-base/maven-enforcer/src/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireReleaseVersion.java
@@ -0,0 +1,103 @@
+package org.apache.maven.plugins.enforcer;
+
+/*
+ * 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.Artifact;
+import org.apache.maven.enforcer.rule.api.EnforcerRuleException;
+import org.apache.maven.enforcer.rule.api.EnforcerRuleHelper;
+import org.apache.maven.project.MavenProject;
+import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluationException;
+
+/**
+ * This rule checks that the current project is not a snapshot.
+ *
+ * @author Brian Fox
+ */
+public class RequireReleaseVersion
+ extends AbstractNonCacheableEnforcerRule
+{
+
+ /**
+ * Allows this rule to fail when the parent is defined as a snapshot.
+ *
+ * @parameter
+ *
+ * @see {@link #setFailWhenParentIsSnapshot(boolean)}
+ * @see {@link #isFailWhenParentIsSnapshot()}
+ */
+ private boolean failWhenParentIsSnapshot = true;
+
+ @Override
+ public void execute( EnforcerRuleHelper theHelper )
+ throws EnforcerRuleException
+ {
+
+ MavenProject project = getProject( theHelper );
+
+ if ( project.getArtifact().isSnapshot() )
+ {
+ String message = getMessage();
+ StringBuilder buf = new StringBuilder();
+ if ( message != null )
+ {
+ buf.append( message ).append( System.lineSeparator() );
+ }
+ buf.append( "This project cannot be a snapshot:" ).append( project.getArtifact().getId() );
+ throw new EnforcerRuleException( buf.toString() );
+ }
+ if ( failWhenParentIsSnapshot )
+ {
+ Artifact parentArtifact = project.getParentArtifact();
+ if ( parentArtifact != null && parentArtifact.isSnapshot() )
+ {
+ throw new EnforcerRuleException( "Parent Cannot be a snapshot: " + parentArtifact.getId() );
+ }
+ }
+
+ }
+
+ /**
+ * @param helper
+ * @return The evaluated {@link MavenProject}.
+ * @throws EnforcerRuleException
+ */
+ private MavenProject getProject( EnforcerRuleHelper helper )
+ throws EnforcerRuleException
+ {
+ try
+ {
+ return (MavenProject) helper.evaluate( "${project}" );
+ }
+ catch ( ExpressionEvaluationException eee )
+ {
+ throw new EnforcerRuleException( "Unable to retrieve the MavenProject: ", eee );
+ }
+ }
+
+ public final boolean isFailWhenParentIsSnapshot()
+ {
+ return failWhenParentIsSnapshot;
+ }
+
+ public final void setFailWhenParentIsSnapshot( boolean failWhenParentIsSnapshot )
+ {
+ this.failWhenParentIsSnapshot = failWhenParentIsSnapshot;
+ }
+}
diff --git a/Java-base/maven-enforcer/src/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireSameVersions.java b/Java-base/maven-enforcer/src/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireSameVersions.java
new file mode 100644
index 000000000..d50cabab8
--- /dev/null
+++ b/Java-base/maven-enforcer/src/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireSameVersions.java
@@ -0,0 +1,131 @@
+package org.apache.maven.plugins.enforcer;
+
+/*
+ * 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.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+import org.apache.maven.artifact.Artifact;
+import org.apache.maven.enforcer.rule.api.EnforcerRuleException;
+import org.apache.maven.enforcer.rule.api.EnforcerRuleHelper;
+import org.apache.maven.project.MavenProject;
+import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluationException;
+
+/**
+ * @author Robert Scholte
+ * @since 1.3
+ */
+public class RequireSameVersions
+ extends AbstractNonCacheableEnforcerRule
+{
+ private boolean uniqueVersions;
+
+ private Set dependencies = new HashSet<>();
+
+ private Set plugins = new HashSet<>();
+
+ private Set buildPlugins = new HashSet<>();
+
+ private Set reportPlugins = new HashSet<>();
+
+ @Override
+ public void execute( EnforcerRuleHelper helper )
+ throws EnforcerRuleException
+ {
+ // get the project
+ MavenProject project;
+ try
+ {
+ project = (MavenProject) helper.evaluate( "${project}" );
+ }
+ catch ( ExpressionEvaluationException eee )
+ {
+ throw new EnforcerRuleException( "Unable to retrieve the MavenProject: ", eee );
+ }
+
+ // consider including profile based artifacts
+ Map> versionMembers = new LinkedHashMap<>();
+
+ Set buildPluginSet = new HashSet<>( buildPlugins );
+ buildPluginSet.addAll( plugins );
+ Set reportPluginSet = new HashSet<>( reportPlugins );
+ reportPluginSet.addAll( plugins );
+
+ // CHECKSTYLE_OFF: LineLength
+ versionMembers.putAll( collectVersionMembers( project.getArtifacts(), dependencies, " (dependency)" ) );
+ versionMembers.putAll( collectVersionMembers( project.getPluginArtifacts(), buildPlugins, " (buildPlugin)" ) );
+ versionMembers.putAll( collectVersionMembers( project.getReportArtifacts(), reportPlugins, " (reportPlugin)" ) );
+ // CHECKSTYLE_ON: LineLength
+
+ if ( versionMembers.size() > 1 )
+ {
+ StringBuilder builder = new StringBuilder( "Found entries with different versions"
+ + System.lineSeparator() );
+ for ( Map.Entry> entry : versionMembers.entrySet() )
+ {
+ builder.append( "Entries with version " ).append( entry.getKey() ).append( System.lineSeparator() );
+ for ( String conflictId : entry.getValue() )
+ {
+ builder.append( "- " ).append( conflictId ).append( System.lineSeparator() );
+ }
+ }
+ throw new EnforcerRuleException( builder.toString() );
+ }
+ }
+
+ private Map> collectVersionMembers( Set artifacts, Collection patterns,
+ String source )
+ {
+ Map> versionMembers = new LinkedHashMap<>();
+
+ List regExs = new ArrayList<>();
+ for ( String pattern : patterns )
+ {
+ String regex = pattern.replace( ".", "\\." ).replace( "*", ".*" ).replace( ":", "\\:" ).replace( '?', '.' );
+
+ // pattern is groupId[:artifactId[:type[:classifier]]]
+ regExs.add( Pattern.compile( regex + "(\\:.+)?" ) );
+ }
+
+ for ( Artifact artifact : artifacts )
+ {
+ for ( Pattern regEx : regExs )
+ {
+ if ( regEx.matcher( artifact.getDependencyConflictId() ).matches() )
+ {
+ String version = uniqueVersions ? artifact.getVersion() : artifact.getBaseVersion();
+ if ( !versionMembers.containsKey( version ) )
+ {
+ versionMembers.put( version, new ArrayList() );
+ }
+ versionMembers.get( version ).add( artifact.getDependencyConflictId() + source );
+ }
+ }
+ }
+ return versionMembers;
+ }
+
+}
diff --git a/Java-base/maven-enforcer/src/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireSnapshotVersion.java b/Java-base/maven-enforcer/src/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireSnapshotVersion.java
new file mode 100644
index 000000000..a631d2a1f
--- /dev/null
+++ b/Java-base/maven-enforcer/src/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireSnapshotVersion.java
@@ -0,0 +1,95 @@
+package org.apache.maven.plugins.enforcer;
+
+/*
+ * 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.Artifact;
+import org.apache.maven.enforcer.rule.api.EnforcerRuleException;
+import org.apache.maven.enforcer.rule.api.EnforcerRuleHelper;
+import org.apache.maven.project.MavenProject;
+import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluationException;
+
+/**
+ * This rule checks that the current project is not a release.
+ */
+public class RequireSnapshotVersion
+ extends AbstractNonCacheableEnforcerRule
+{
+
+ /**
+ * Allows this rule to fail when the parent is defined as a release.
+ */
+ private boolean failWhenParentIsRelease = true;
+
+ @Override
+ public void execute( EnforcerRuleHelper helper )
+ throws EnforcerRuleException
+ {
+
+ MavenProject project = getProject( false, helper );
+ Artifact artifact = project.getArtifact();
+
+ if ( !artifact.isSnapshot() )
+ {
+ String message = getMessage();
+ StringBuilder sb = new StringBuilder();
+ if ( message != null )
+ {
+ sb.append( message ).append( System.lineSeparator() );
+ }
+ sb.append( "This project cannot be a release:" ).append( artifact.getId() );
+ throw new EnforcerRuleException( sb.toString() );
+ }
+ if ( failWhenParentIsRelease && project.hasParent() )
+ {
+ // project.getParentArtifact() does not work here if a "CI Friendly Version" is used (e.g. ${revision})
+ Artifact parentArtifact = getProject( true, helper ).getArtifact();
+ if ( parentArtifact != null && !parentArtifact.isSnapshot() )
+ {
+ throw new EnforcerRuleException( "Parent cannot be a release: " + parentArtifact.getId() );
+ }
+ }
+
+ }
+
+ private MavenProject getProject( boolean parent, EnforcerRuleHelper helper )
+ throws EnforcerRuleException
+ {
+ String expression = parent ? "${project.parent}" : "${project}";
+ try
+ {
+ return (MavenProject) helper.evaluate( expression );
+ }
+ catch ( ExpressionEvaluationException eee )
+ {
+ throw new EnforcerRuleException( "Unable to retrieve the MavenProject: ", eee );
+ }
+ }
+
+ public boolean isFailWhenParentIsRelease()
+ {
+ return failWhenParentIsRelease;
+ }
+
+ public void setFailWhenParentIsRelease( boolean failWhenParentIsRelease )
+ {
+ this.failWhenParentIsRelease = failWhenParentIsRelease;
+ }
+
+}
diff --git a/Java-base/maven-enforcer/src/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireUpperBoundDeps.java b/Java-base/maven-enforcer/src/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireUpperBoundDeps.java
new file mode 100644
index 000000000..348d056a6
--- /dev/null
+++ b/Java-base/maven-enforcer/src/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireUpperBoundDeps.java
@@ -0,0 +1,391 @@
+package org.apache.maven.plugins.enforcer;
+
+/*
+ * 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.ArrayList;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.maven.artifact.Artifact;
+import org.apache.maven.artifact.factory.ArtifactFactory;
+import org.apache.maven.artifact.metadata.ArtifactMetadataSource;
+import org.apache.maven.artifact.repository.ArtifactRepository;
+import org.apache.maven.artifact.resolver.ArtifactCollector;
+import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
+import org.apache.maven.artifact.versioning.ArtifactVersion;
+import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
+import org.apache.maven.artifact.versioning.OverConstrainedVersionException;
+import org.apache.maven.enforcer.rule.api.EnforcerRuleException;
+import org.apache.maven.enforcer.rule.api.EnforcerRuleHelper;
+import org.apache.maven.plugin.logging.Log;
+import org.apache.maven.project.MavenProject;
+import org.apache.maven.shared.dependency.tree.DependencyNode;
+import org.apache.maven.shared.dependency.tree.DependencyTreeBuilder;
+import org.apache.maven.shared.dependency.tree.DependencyTreeBuilderException;
+import org.apache.maven.shared.dependency.tree.traversal.DependencyNodeVisitor;
+import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluationException;
+import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
+
+/**
+ * Rule to enforce that the resolved dependency is also the most recent one of all transitive dependencies.
+ *
+ * @author Geoffrey De Smet
+ * @since 1.1
+ */
+public class RequireUpperBoundDeps
+ extends AbstractNonCacheableEnforcerRule
+{
+ private static Log log;
+
+ /**
+ * @since 1.3
+ */
+ private boolean uniqueVersions;
+
+ /**
+ * Dependencies to ignore.
+ *
+ * @since TBD
+ */
+ private List excludes = null;
+
+ /**
+ * Set to {@code true} if timestamped snapshots should be used.
+ *
+ * @param uniqueVersions
+ * @since 1.3
+ */
+ public void setUniqueVersions( boolean uniqueVersions )
+ {
+ this.uniqueVersions = uniqueVersions;
+ }
+
+ /**
+ * Sets dependencies to exclude.
+ * @param excludes a list of {@code groupId:artifactId} names
+ */
+ public void setExcludes( List excludes )
+ {
+ this.excludes = excludes;
+ }
+
+ // CHECKSTYLE_OFF: LineLength
+ /**
+ * Uses the {@link EnforcerRuleHelper} to populate the values of the
+ * {@link DependencyTreeBuilder#buildDependencyTree(MavenProject, ArtifactRepository, ArtifactFactory, ArtifactMetadataSource, ArtifactFilter, ArtifactCollector)}
+ * factory method.
+ * This method simply exists to hide all the ugly lookup that the {@link EnforcerRuleHelper} has to do.
+ *
+ * @param helper
+ * @return a Dependency Node which is the root of the project's dependency tree
+ * @throws EnforcerRuleException when the build should fail
+ */
+ // CHECKSTYLE_ON: LineLength
+ private DependencyNode getNode( EnforcerRuleHelper helper )
+ throws EnforcerRuleException
+ {
+ try
+ {
+ MavenProject project = (MavenProject) helper.evaluate( "${project}" );
+ DependencyTreeBuilder dependencyTreeBuilder = helper.getComponent( DependencyTreeBuilder.class );
+ ArtifactRepository repository = (ArtifactRepository) helper.evaluate( "${localRepository}" );
+ ArtifactFactory factory = helper.getComponent( ArtifactFactory.class );
+ ArtifactMetadataSource metadataSource = helper.getComponent( ArtifactMetadataSource.class );
+ ArtifactCollector collector = helper.getComponent( ArtifactCollector.class );
+ ArtifactFilter filter = null; // we need to evaluate all scopes
+ DependencyNode node =
+ dependencyTreeBuilder.buildDependencyTree( project, repository, factory, metadataSource, filter,
+ collector );
+ return node;
+ }
+ catch ( ExpressionEvaluationException e )
+ {
+ throw new EnforcerRuleException( "Unable to lookup an expression " + e.getLocalizedMessage(), e );
+ }
+ catch ( ComponentLookupException e )
+ {
+ throw new EnforcerRuleException( "Unable to lookup a component " + e.getLocalizedMessage(), e );
+ }
+ catch ( DependencyTreeBuilderException e )
+ {
+ throw new EnforcerRuleException( "Could not build dependency tree " + e.getLocalizedMessage(), e );
+ }
+ }
+
+ @Override
+ public void execute( EnforcerRuleHelper helper )
+ throws EnforcerRuleException
+ {
+ if ( log == null )
+ {
+ log = helper.getLog();
+ }
+ try
+ {
+ DependencyNode node = getNode( helper );
+ RequireUpperBoundDepsVisitor visitor = new RequireUpperBoundDepsVisitor();
+ visitor.setUniqueVersions( uniqueVersions );
+ node.accept( visitor );
+ List errorMessages = buildErrorMessages( visitor.getConflicts() );
+ if ( errorMessages.size() > 0 )
+ {
+ throw new EnforcerRuleException( "Failed while enforcing RequireUpperBoundDeps. The error(s) are "
+ + errorMessages );
+ }
+ }
+ catch ( Exception e )
+ {
+ throw new EnforcerRuleException( e.getLocalizedMessage(), e );
+ }
+ }
+
+ private List buildErrorMessages( List> conflicts )
+ {
+ List errorMessages = new ArrayList<>( conflicts.size() );
+ for ( List conflict : conflicts )
+ {
+ Artifact artifact = conflict.get( 0 ).getArtifact();
+ String groupArt = artifact.getGroupId() + ":" + artifact.getArtifactId();
+ if ( excludes != null && excludes.contains( groupArt ) )
+ {
+ log.info( "Ignoring requireUpperBoundDeps in " + groupArt );
+ }
+ else
+ {
+ errorMessages.add( buildErrorMessage( conflict ) );
+ }
+ }
+ return errorMessages;
+ }
+
+ private String buildErrorMessage( List conflict )
+ {
+ StringBuilder errorMessage = new StringBuilder();
+ errorMessage.append( System.lineSeparator() + "Require upper bound dependencies error for "
+ + getFullArtifactName( conflict.get( 0 ), false ) + " paths to dependency are:" + System.lineSeparator() );
+ if ( conflict.size() > 0 )
+ {
+ errorMessage.append( buildTreeString( conflict.get( 0 ) ) );
+ }
+ for ( DependencyNode node : conflict.subList( 1, conflict.size() ) )
+ {
+ errorMessage.append( "and" + System.lineSeparator() );
+ errorMessage.append( buildTreeString( node ) );
+ }
+ return errorMessage.toString();
+ }
+
+ private StringBuilder buildTreeString( DependencyNode node )
+ {
+ List loc = new ArrayList<>();
+ DependencyNode currentNode = node;
+ while ( currentNode != null )
+ {
+ StringBuilder line = new StringBuilder( getFullArtifactName( currentNode, false ) );
+
+ if ( currentNode.getPremanagedVersion() != null )
+ {
+ line.append( " (managed) <-- " );
+ line.append( getFullArtifactName( currentNode, true ) );
+ }
+
+ loc.add( line.toString() );
+ currentNode = currentNode.getParent();
+ }
+ Collections.reverse( loc );
+ StringBuilder builder = new StringBuilder();
+ for ( int i = 0; i < loc.size(); i++ )
+ {
+ for ( int j = 0; j < i; j++ )
+ {
+ builder.append( " " );
+ }
+ builder.append( "+-" ).append( loc.get( i ) );
+ builder.append( System.lineSeparator() );
+ }
+ return builder;
+ }
+
+ private String getFullArtifactName( DependencyNode node, boolean usePremanaged )
+ {
+ Artifact artifact = node.getArtifact();
+
+ String version = node.getPremanagedVersion();
+ if ( !usePremanaged || version == null )
+ {
+ version = uniqueVersions ? artifact.getVersion() : artifact.getBaseVersion();
+ }
+ return artifact.getGroupId() + ":" + artifact.getArtifactId() + ":" + version;
+ }
+
+ private static class RequireUpperBoundDepsVisitor
+ implements DependencyNodeVisitor
+ {
+
+ private boolean uniqueVersions;
+
+ public void setUniqueVersions( boolean uniqueVersions )
+ {
+ this.uniqueVersions = uniqueVersions;
+ }
+
+ private Map> keyToPairsMap =
+ new LinkedHashMap<>();
+
+ public boolean visit( DependencyNode node )
+ {
+ DependencyNodeHopCountPair pair = new DependencyNodeHopCountPair( node );
+ String key = pair.constructKey();
+ List pairs = keyToPairsMap.get( key );
+ if ( pairs == null )
+ {
+ pairs = new ArrayList<>();
+ keyToPairsMap.put( key, pairs );
+ }
+ pairs.add( pair );
+ Collections.sort( pairs );
+ return true;
+ }
+
+ public boolean endVisit( DependencyNode node )
+ {
+ return true;
+ }
+
+ public List> getConflicts()
+ {
+ List> output = new ArrayList<>();
+ for ( List pairs : keyToPairsMap.values() )
+ {
+ if ( containsConflicts( pairs ) )
+ {
+ List outputSubList = new ArrayList<>( pairs.size() );
+ for ( DependencyNodeHopCountPair pair : pairs )
+ {
+ outputSubList.add( pair.getNode() );
+ }
+ output.add( outputSubList );
+ }
+ }
+ return output;
+ }
+
+ @SuppressWarnings( "unchecked" )
+ private boolean containsConflicts( List pairs )
+ {
+ DependencyNodeHopCountPair resolvedPair = pairs.get( 0 );
+
+ // search for artifact with lowest hopCount
+ for ( DependencyNodeHopCountPair hopPair : pairs.subList( 1, pairs.size() ) )
+ {
+ if ( hopPair.getHopCount() < resolvedPair.getHopCount() )
+ {
+ resolvedPair = hopPair;
+ }
+ }
+
+ ArtifactVersion resolvedVersion = resolvedPair.extractArtifactVersion( uniqueVersions, false );
+
+ for ( DependencyNodeHopCountPair pair : pairs )
+ {
+ ArtifactVersion version = pair.extractArtifactVersion( uniqueVersions, true );
+ if ( resolvedVersion.compareTo( version ) < 0 )
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ }
+
+ private static class DependencyNodeHopCountPair
+ implements Comparable
+ {
+
+ private DependencyNode node;
+
+ private int hopCount;
+
+ private DependencyNodeHopCountPair( DependencyNode node )
+ {
+ this.node = node;
+ countHops();
+ }
+
+ private void countHops()
+ {
+ hopCount = 0;
+ DependencyNode parent = node.getParent();
+ while ( parent != null )
+ {
+ hopCount++;
+ parent = parent.getParent();
+ }
+ }
+
+ private String constructKey()
+ {
+ Artifact artifact = node.getArtifact();
+ return artifact.getGroupId() + ":" + artifact.getArtifactId();
+ }
+
+ public DependencyNode getNode()
+ {
+ return node;
+ }
+
+ private ArtifactVersion extractArtifactVersion( boolean uniqueVersions, boolean usePremanagedVersion )
+ {
+ if ( usePremanagedVersion && node.getPremanagedVersion() != null )
+ {
+ return new DefaultArtifactVersion( node.getPremanagedVersion() );
+ }
+
+ Artifact artifact = node.getArtifact();
+ String version = uniqueVersions ? artifact.getVersion() : artifact.getBaseVersion();
+ if ( version != null )
+ {
+ return new DefaultArtifactVersion( version );
+ }
+ try
+ {
+ return artifact.getSelectedVersion();
+ }
+ catch ( OverConstrainedVersionException e )
+ {
+ throw new RuntimeException( "Version ranges problem with " + node.getArtifact(), e );
+ }
+ }
+
+ public int getHopCount()
+ {
+ return hopCount;
+ }
+
+ public int compareTo( DependencyNodeHopCountPair other )
+ {
+ return Integer.valueOf( hopCount ).compareTo( Integer.valueOf( other.getHopCount() ) );
+ }
+ }
+
+}
diff --git a/Java-base/maven-enforcer/src/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/utils/ArtifactMatcher.java b/Java-base/maven-enforcer/src/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/utils/ArtifactMatcher.java
new file mode 100644
index 000000000..ff5803b78
--- /dev/null
+++ b/Java-base/maven-enforcer/src/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/utils/ArtifactMatcher.java
@@ -0,0 +1,219 @@
+package org.apache.maven.plugins.enforcer.utils;
+
+/*
+ * 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.Artifact;
+import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
+import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
+import org.apache.maven.artifact.versioning.VersionRange;
+import org.apache.maven.plugins.enforcer.AbstractVersionEnforcer;
+import java.util.Collection;
+import java.util.LinkedList;
+
+/**
+ * This class is used for matching Artifacts against a list of patterns.
+ *
+ * @author Jakub Senko
+ * @see org.apache.maven.plugins.enforcer.BanTransitiveDependencies
+ */
+public final class ArtifactMatcher
+{
+
+ /**
+ * @author I don't know
+ */
+ public static class Pattern
+ {
+ private String pattern;
+
+ private String[] parts;
+
+ public Pattern( String pattern )
+ {
+ if ( pattern == null )
+ {
+ throw new NullPointerException( "pattern" );
+ }
+
+ this.pattern = pattern;
+
+ parts = pattern.split( ":", 7 );
+
+ if ( parts.length == 7 )
+ {
+ throw new IllegalArgumentException( "Pattern contains too many delimiters." );
+ }
+
+ for ( String part : parts )
+ {
+ if ( "".equals( part ) )
+ {
+ throw new IllegalArgumentException( "Pattern or its part is empty." );
+ }
+ }
+ }
+
+ public boolean match( Artifact artifact )
+ throws InvalidVersionSpecificationException
+ {
+ if ( artifact == null )
+ {
+ throw new NullPointerException( "artifact" );
+ }
+
+ switch ( parts.length )
+ {
+ case 6:
+ String classifier = artifact.getClassifier();
+ if ( !matches( parts[5], classifier ) )
+ {
+ return false;
+ }
+ case 5:
+ String scope = artifact.getScope();
+ if ( scope == null || scope.equals( "" ) )
+ {
+ scope = Artifact.SCOPE_COMPILE;
+ }
+
+ if ( !matches( parts[4], scope ) )
+ {
+ return false;
+ }
+ case 4:
+ String type = artifact.getType();
+ if ( type == null || type.equals( "" ) )
+ {
+ type = "jar";
+ }
+
+ if ( !matches( parts[3], type ) )
+ {
+ return false;
+ }
+
+ case 3:
+ if ( !matches( parts[2], artifact.getVersion() ) )
+ {
+ // CHECKSTYLE_OFF: LineLength
+ if ( !AbstractVersionEnforcer.containsVersion( VersionRange.createFromVersionSpec( parts[2] ),
+ new DefaultArtifactVersion(
+ artifact.getVersion() ) ) )
+ // CHECKSTYLE_ON: LineLength
+ {
+ return false;
+ }
+ }
+
+ case 2:
+ if ( !matches( parts[1], artifact.getArtifactId() ) )
+ {
+ return false;
+ }
+ case 1:
+ return matches( parts[0], artifact.getGroupId() );
+ default:
+ throw new AssertionError();
+ }
+ }
+
+ private boolean matches( String expression, String input )
+ {
+ String regex =
+ expression.replace( ".", "\\." ).replace( "*", ".*" ).replace( ":", "\\:" ).replace( '?', '.' )
+ .replace( "[", "\\[" ).replace( "]", "\\]" ).replace( "(", "\\(" ).replace( ")", "\\)" );
+
+ // TODO: Check if this can be done better or prevented earlier.
+ if ( input == null )
+ {
+ input = "";
+ }
+
+ return java.util.regex.Pattern.matches( regex, input );
+ }
+
+ @Override
+ public String toString()
+ {
+ return pattern;
+ }
+ }
+
+ private Collection patterns = new LinkedList<>();
+
+ private Collection ignorePatterns = new LinkedList<>();
+
+ /**
+ * Construct class by providing patterns as strings. Empty strings are ignored.
+ *
+ * @throws NullPointerException if any of the arguments is null
+ */
+ public ArtifactMatcher( final Collection patterns, final Collection ignorePatterns )
+ {
+ if ( patterns == null )
+ {
+ throw new NullPointerException( "patterns" );
+ }
+ if ( ignorePatterns == null )
+ {
+ throw new NullPointerException( "ignorePatterns" );
+ }
+ for ( String pattern : patterns )
+ {
+ if ( pattern != null && !"".equals( pattern ) )
+ {
+ this.patterns.add( new Pattern( pattern ) );
+ }
+ }
+
+ for ( String ignorePattern : ignorePatterns )
+ {
+ if ( ignorePattern != null && !"".equals( ignorePattern ) )
+ {
+ this.ignorePatterns.add( new Pattern( ignorePattern ) );
+ }
+ }
+ }
+
+ /**
+ * Check if artifact matches patterns.
+ *
+ * @throws InvalidVersionSpecificationException
+ */
+ public boolean match( Artifact artifact )
+ throws InvalidVersionSpecificationException
+ {
+ for ( Pattern pattern : patterns )
+ {
+ if ( pattern.match( artifact ) )
+ {
+ for ( Pattern ignorePattern : ignorePatterns )
+ {
+ if ( ignorePattern.match( artifact ) )
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/Java-base/maven-enforcer/src/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/utils/ArtifactUtils.java b/Java-base/maven-enforcer/src/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/utils/ArtifactUtils.java
new file mode 100644
index 000000000..b9a04afd0
--- /dev/null
+++ b/Java-base/maven-enforcer/src/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/utils/ArtifactUtils.java
@@ -0,0 +1,129 @@
+package org.apache.maven.plugins.enforcer.utils;
+
+/*
+ * 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.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.maven.artifact.Artifact;
+import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
+import org.apache.maven.enforcer.rule.api.EnforcerRuleException;
+import org.apache.maven.plugins.enforcer.utils.ArtifactMatcher.Pattern;
+import org.apache.maven.shared.dependency.graph.DependencyNode;
+
+/**
+ *
+ * @author Robert Scholte
+ * @since 3.0.0
+ */
+public final class ArtifactUtils
+{
+ private ArtifactUtils()
+ {
+ }
+
+ public static Set