Add asfMavenTlpPlgnBuild to run plugins with multiple Maven versions
diff --git a/vars/asfMavenTlpPlgnBuild.groovy b/vars/asfMavenTlpPlgnBuild.groovy
new file mode 100644
index 0000000..ac85231
--- /dev/null
+++ b/vars/asfMavenTlpPlgnBuild.groovy
@@ -0,0 +1,167 @@
+#!/usr/bin/env groovy
+
+/*
+ * 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.
+ */
+
+def call(Map params = [:]) {
+  def failingFast = null
+  try {
+    // set build retention time first
+    def buildRetention
+    if (env.BRANCH_NAME == 'master') {
+      buildRetention = buildDiscarder(logRotator(artifactDaysToKeepStr: '', artifactNumToKeepStr: '3', daysToKeepStr: '21', numToKeepStr: '25'))
+    } else {
+      buildRetention = buildDiscarder(logRotator(artifactDaysToKeepStr: '', artifactNumToKeepStr: '1', daysToKeepStr: '7', numToKeepStr: '5'))
+    }
+    properties([buildRetention])
+
+    // now determine the matrix of parallel builds
+    def oses = params.containsKey('os') ? params.os : ['linux', 'windows']
+    def jdks = params.containsKey('jdks') ? params.jdks : params.containsKey('jdk') ? params.jdk : ['7','8','9','10']
+    def mavens = params.containsKey('maven') ? params.maven : ['3.0.x','3.1.x','3.2.x','3.3.x','3.5.x']
+    def failFast = params.containsKey('failFast') ? params.failFast : true
+    Map tasks = [failFast: failFast]
+    boolean first = true
+    for (String os in oses) {
+      for (def mvn in mavens) {
+	    def jdk = jenkinsEnv.jdkForMaven(mvn)
+		jdks.remove(jdk)
+	    doCreateTask( os, jdk, mvn, tasks )
+      }
+      for (def jdk in jdks) {
+	    def mvn = jenkinsEnv.mavenForJdk(jdk)
+	    doCreateTask( os, jdk, mvn, tasks )
+      }
+    }
+    // run the parallel builds
+    parallel(tasks)
+
+    // JENKINS-34376 seems to make it hard to detect the aborted builds
+  } catch (org.jenkinsci.plugins.workflow.steps.FlowInterruptedException e) {
+    // this ambiguous condition means a user probably aborted
+    if (e.causes.size() == 0) {
+      currentBuild.result = "ABORTED"
+    } else {
+      currentBuild.result = "FAILURE"
+    }
+    throw e
+  } catch (hudson.AbortException e) {
+    // this ambiguous condition means during a shell step, user probably aborted
+    if (e.getMessage().contains('script returned exit code 143')) {
+      currentBuild.result = "ABORTED"
+    } else {
+      currentBuild.result = "FAILURE"
+    }
+    throw e
+  } catch (InterruptedException e) {
+    currentBuild.result = "ABORTED"
+    throw e
+  } catch (Throwable e) {
+    currentBuild.result = "FAILURE"
+    throw e
+  } finally {
+    // notify completion
+    if (failingFast != null) {
+      echo "***** FAST FAILURE *****\n\nFast failure triggered by ${failingFast}\n\n***** FAST FAILURE *****"
+    }
+    stage("Notifications") {
+      jenkinsNotify()
+    }
+  }
+}
+
+def doCreateTask( os, jdk, maven, tasks )
+{
+	String label = jenkinsEnv.labelForOS(os);
+	String jdkName = jenkinsEnv.jdkFromVersion(os, "${jdk}")
+	String mvnName = jenkinsEnv.mvnFromVersion(os, "${maven}")
+	echo "OS: ${os} JDK: ${jdk} Maven: ${maven} => Label: ${label} JDK: ${jdkName} Maven: ${mvnName}"
+	if (label == null || jdkName == null || mvnName == null) {
+	  echo "Skipping ${os}-jdk${jdk} as unsupported by Jenkins Environment"
+	  return;
+	}
+	def cmd = [
+	  'mvn',
+	  '-P+run-its',
+	  '-Dmaven.test.failure.ignore=true',
+	  '-Dfindbugs.failOnError=false',
+	]
+	if (!first) {
+	  cmd += '-Dfindbugs.skip=true'
+	}
+	cmd += 'clean'
+	cmd += 'verify'
+	def disablePublishers = !first
+	first = false
+	String stageId = "${os}-jdk${jdk}"
+	tasks[stageId] = {
+	  node(label) {
+		stage("Checkout ${stageId}") {
+		  try {
+			dir('m') {
+			  checkout scm
+			}
+		  } catch (Throwable e) {
+			if (!failFast) {
+			  throw e
+			} else if (failingFast == null) {
+			  failingFast = stageId
+			  echo "[FAIL FAST] This is the first failure and likely root cause"
+			  throw e
+			} else {
+			  echo "[FAIL FAST] ${failingFast} had first failure, ignoring ${e.message}"
+			}
+		  }
+		}
+		stage("Build ${stageId}") {
+		  if (failingFast != null) {
+			echo "[FAIL FAST] ${failingFast} has failed. Skipping ${stageId}."
+		  } else try {
+			withMaven(jdk:jdkName, maven:mvnName, mavenLocalRepo:'.repository', options: [
+			  artifactsPublisher(disabled: disablePublishers),
+			  junitPublisher(ignoreAttachments: false),
+			  findbugsPublisher(disabled: disablePublishers),
+			  openTasksPublisher(disabled: disablePublishers),
+			  dependenciesFingerprintPublisher(),
+			  invokerPublisher(),
+			  pipelineGraphPublisher()
+			]) {
+			dir ('m') {
+				if (isUnix()) {
+				  sh cmd.join(' ')
+				} else {
+				  bat cmd.join(' ')
+				}
+			  }
+			}
+		  } catch (Throwable e) {
+			if (!failFast) {
+			  throw e
+			} else if (failingFast == null) {
+			  failingFast = stageId
+			  echo "[FAIL FAST] This is the first failure and likely root cause"
+			  throw e
+			} else {
+			  echo "[FAIL FAST] ${failingFast} had first failure, ignoring ${e.message}"
+			}
+		  }
+		}
+	  }
+	}
+}
\ No newline at end of file