diff --git a/README.md b/README.md
index 4ffb21f..22ce58a 100644
--- a/README.md
+++ b/README.md
@@ -103,6 +103,28 @@ However, if you would like to get the benefit of the community maintained defini
```
+In some cases your definition file might be shared across many projects.
+As an alternative to copying and pasting this file into each build, you can package your definitions into a JAR and reference it like so:
+```xml
+
+ biz.lermitage.oga
+ oga-maven-plugin
+
+
+ classpath-og-definitions.json
+
+
+
+
+
+ com.example.oga
+ build-config
+ 1.0.0
+
+
+
+```
+
#### Ignoring definitions
You can also provide a JSON ignore-list in order to exclude some *groupIds* or *groupId + artifactIds*:
diff --git a/pom.xml b/pom.xml
index 4be3077..83255d5 100644
--- a/pom.xml
+++ b/pom.xml
@@ -104,6 +104,12 @@
${gson.version}
+
+ org.codehaus.plexus
+ plexus-resources
+ 1.3.0
+
+
diff --git a/src/main/java/biz/lermitage/oga/CheckMojo.kt b/src/main/java/biz/lermitage/oga/CheckMojo.kt
index 54c45d0..f4282be 100644
--- a/src/main/java/biz/lermitage/oga/CheckMojo.kt
+++ b/src/main/java/biz/lermitage/oga/CheckMojo.kt
@@ -5,15 +5,17 @@ import biz.lermitage.oga.cfg.IgnoreList
import biz.lermitage.oga.util.DefinitionsTools
import biz.lermitage.oga.util.IOTools
import biz.lermitage.oga.util.IgnoreListTools
+import org.apache.maven.execution.MavenSession
import org.apache.maven.plugin.AbstractMojo
import org.apache.maven.plugin.MojoExecutionException
+import org.apache.maven.plugins.annotations.Component
import org.apache.maven.plugins.annotations.Mojo
import org.apache.maven.plugins.annotations.Parameter
import org.apache.maven.project.MavenProject
+import org.codehaus.plexus.resource.ResourceManager
+import org.codehaus.plexus.resource.loader.FileResourceLoader
import org.codehaus.plexus.util.xml.pull.XmlPullParserException
-import java.io.File
import java.io.IOException
-import java.net.URL
import java.util.Optional
/**
@@ -60,6 +62,12 @@ class CheckMojo : AbstractMojo() {
@Parameter(property = "project", readonly = true)
var project: MavenProject? = null
+ @Parameter(defaultValue = "\${session}", required = true, readonly = true)
+ private var session: MavenSession? = null
+
+ @Component
+ private val locator: ResourceManager? = null
+
/**
* Execute goal.
*/
@@ -72,22 +80,25 @@ class CheckMojo : AbstractMojo() {
return
}
+ setUpLocator(locator!!)
+
try {
- val allDefinitions = mutableListOf(DefinitionsTools.loadDefinitionsFromUrl(ogDefinitionsUrl ?: DEFINITIONS_URL, log))
+ val allDefinitions = mutableListOf(DefinitionsTools.loadDefinitionsFromUrl(ogDefinitionsUrl ?: DEFINITIONS_URL, log, locator))
if (!ignoreUnofficialMigrations) {
- allDefinitions += DefinitionsTools.loadDefinitionsFromUrl(ogUnofficialDefinitionsUrl ?: UNOFFICIAL_DEFINITIONS_URL, log)
+ allDefinitions += DefinitionsTools.loadDefinitionsFromUrl(ogUnofficialDefinitionsUrl ?: UNOFFICIAL_DEFINITIONS_URL, log, locator)
}
// Load additional definitions if defined
- additionalDefinitionFiles!!.forEach { allDefinitions += DefinitionsTools.loadDefinitionsFromUrl(it, log) }
+ additionalDefinitionFiles!!.forEach { allDefinitions += DefinitionsTools.loadDefinitionsFromUrl(it, log, locator) }
var ignoreList = Optional.empty()
if (!ignoreListFile.isNullOrEmpty()) {
log.info("Loading ignore list from file $ignoreListFile")
- ignoreList = Optional.of(IOTools.readIgnoreList(File(ignoreListFile)))
+ // TODO given that we now support loading a file from classpath, file and URL we could consolidate configuration to definitions and suppressions (like PMD/Checkstyle)
+ ignoreList = Optional.of(IOTools.readIgnoreList(ignoreListFile, locator, log))
} else if (!ignoreListUrl.isNullOrEmpty()) {
log.info("Loading ignore list from url $ignoreListUrl")
- ignoreList = Optional.of(IOTools.readIgnoreList(URL(ignoreListUrl)))
+ ignoreList = Optional.of(IOTools.readIgnoreList(ignoreListUrl, locator, log))
}
val dependencies = DefinitionsTools.mapDependenciesToOgaDependencies(project!!.dependencies!!)
@@ -162,6 +173,19 @@ class CheckMojo : AbstractMojo() {
}
}
+ private fun setUpLocator(locator: ResourceManager) {
+ val searchPaths = listOf(
+ // 0. The locator will read from classpath and URL locations
+ // 1. in the directory of the current project's pom file - note: extensions might replace the pom file on the fly
+ project!!.file.parentFile.absolutePath,
+ // 2. in the current project's directory
+ project!!.basedir.absolutePath,
+ // 3. in the base directory - that's the directory of the initial pom requested to build, e.g. the root of a multi-module build
+ session!!.request!!.baseDirectory
+ )
+ searchPaths.forEach { searchPath -> locator.addSearchPath(FileResourceLoader.ID, searchPath) }
+ }
+
companion object {
private const val GITHUB_PRJ_RAW_URL = "https://raw.githubusercontent.com/jonathanlermitage/oga-maven-plugin/master/"
private const val DEFINITIONS_URL = GITHUB_PRJ_RAW_URL + "uc/og-definitions.json"
diff --git a/src/main/java/biz/lermitage/oga/util/DefinitionsTools.kt b/src/main/java/biz/lermitage/oga/util/DefinitionsTools.kt
index 9918ef5..1399827 100644
--- a/src/main/java/biz/lermitage/oga/util/DefinitionsTools.kt
+++ b/src/main/java/biz/lermitage/oga/util/DefinitionsTools.kt
@@ -4,6 +4,7 @@ import biz.lermitage.oga.Dependency
import biz.lermitage.oga.DependencyType
import biz.lermitage.oga.cfg.Definitions
import org.apache.maven.plugin.logging.Log
+import org.codehaus.plexus.resource.ResourceManager
/**
* Definitions tools.
@@ -12,9 +13,9 @@ import org.apache.maven.plugin.logging.Log
*/
object DefinitionsTools {
- fun loadDefinitionsFromUrl(url: String, log: Log): Definitions {
+ fun loadDefinitionsFromUrl(url: String, log: Log, locator: ResourceManager): Definitions {
log.info("Loading definitions from $url")
- val definitions = IOTools.readDefinitions(url)
+ val definitions = IOTools.readDefinitions(url, locator, log)
val nbDefinitions = definitions.migration?.size
var welcomeMsg = "Loaded $nbDefinitions definitions from '$url'"
diff --git a/src/main/java/biz/lermitage/oga/util/IOTools.kt b/src/main/java/biz/lermitage/oga/util/IOTools.kt
index 4f838ea..aa0e909 100644
--- a/src/main/java/biz/lermitage/oga/util/IOTools.kt
+++ b/src/main/java/biz/lermitage/oga/util/IOTools.kt
@@ -4,13 +4,15 @@ import biz.lermitage.oga.cfg.Definitions
import biz.lermitage.oga.cfg.IgnoreList
import com.google.gson.GsonBuilder
import org.apache.commons.io.FileUtils
-import java.io.BufferedReader
+import org.apache.maven.plugin.MojoExecutionException
+import org.apache.maven.plugin.logging.Log
+import org.codehaus.plexus.resource.ResourceManager
+import org.codehaus.plexus.resource.loader.FileResourceCreationException
+import org.codehaus.plexus.resource.loader.ResourceNotFoundException
import java.io.File
import java.io.IOException
-import java.io.InputStreamReader
-import java.net.HttpURLConnection
-import java.net.URL
-import java.util.regex.Pattern
+import java.nio.file.Files
+import kotlin.io.path.pathString
/**
* IO tools.
@@ -22,45 +24,33 @@ object IOTools {
private val GSON = GsonBuilder().create()
@Throws(IOException::class)
- fun readDefinitions(url: String): Definitions {
- return readFromLocation(url, Definitions::class.java)
+ fun readDefinitions(location: String, locator: ResourceManager, log: Log): Definitions {
+ return readFromLocation(location, locator, log, Definitions::class.java)
}
@Throws(IOException::class)
- fun readIgnoreList(url: URL): IgnoreList {
- val ignoreListAsString = readContent(url)
- return GSON.fromJson(ignoreListAsString, IgnoreList::class.java)
+ fun readIgnoreList(location: String, locator: ResourceManager, log: Log): IgnoreList {
+ return readFromLocation(location, locator, log, IgnoreList::class.java)
}
@Throws(IOException::class)
- fun readIgnoreList(file: File): IgnoreList {
- val ignoreListAsString = FileUtils.readFileToString(file, Charsets.UTF_8)
- return GSON.fromJson(ignoreListAsString, IgnoreList::class.java)
- }
-
- @Throws(IOException::class)
- private fun readFromLocation(location: String, clazz: Class): T {
- val uriRx: Pattern = Pattern.compile("^https?:.*", Pattern.CASE_INSENSITIVE)
- val asString = if (uriRx.matcher(location).matches()) {
- readContent(URL(location))
- } else {
- FileUtils.readFileToString(File(location), Charsets.UTF_8)
- }
+ private fun readFromLocation(location: String, locator: ResourceManager, log: Log, clazz: Class): T {
+ val file = locationToFile(location, locator, log)
+ val asString = FileUtils.readFileToString(file, Charsets.UTF_8)
return GSON.fromJson(asString, clazz)
}
- @Throws(IOException::class)
- private fun readContent(url: URL): String {
- val content = StringBuilder()
- val conn = url.openConnection() as HttpURLConnection
- conn.requestMethod = "GET"
- BufferedReader(InputStreamReader(conn.inputStream)).use { rd ->
- var line: String? = rd.readLine()
- while (line != null) {
- content.append(line)
- line = rd.readLine()
- }
+ @Throws(MojoExecutionException::class)
+ private fun locationToFile(location: String, locator: ResourceManager, log: Log): File {
+ try {
+ val resolvedLocation = Files.createTempFile("oga-", ".json")
+ log.debug("Resolved file from '$location' to '$resolvedLocation'")
+ return locator.getResourceAsFile(location, resolvedLocation.pathString)
+ ?: throw MojoExecutionException("Could not resolve $location")
+ } catch (e: ResourceNotFoundException) {
+ throw MojoExecutionException(e.message, e)
+ } catch (e: FileResourceCreationException) {
+ throw MojoExecutionException(e.message, e)
}
- return content.toString()
}
}
diff --git a/src/test/java/biz/lermitage/oga/CheckMojoITest.kt b/src/test/java/biz/lermitage/oga/CheckMojoITest.kt
index 8b0c0f5..1adddb6 100644
--- a/src/test/java/biz/lermitage/oga/CheckMojoITest.kt
+++ b/src/test/java/biz/lermitage/oga/CheckMojoITest.kt
@@ -16,6 +16,28 @@ class CheckMojoITest {
@JvmField
var mockServerRule: MockServerRule = MockServerRule(this)
+ @Test
+ fun testProjectWithClasspathDefinitionFiles() {
+ // GIVEN a project that contains our definition file
+ val classpathResourcesDir = ResourceExtractor.simpleExtractResources(javaClass, "/biz/lermitage/oga/classpath_build_config")
+ val classpathVerifier = Verifier(classpathResourcesDir.absolutePath)
+ classpathVerifier.deleteArtifact("biz.lermitage.oga", "classpath-build-config", "1.0.0-SNAPSHOT", "jar")
+ classpathVerifier.executeGoal("install")
+
+ // AND a project which uses the definition file as a classpath resource
+ val testDir = ResourceExtractor.simpleExtractResources(javaClass, "/biz/lermitage/oga/ko_classpath_definitions")
+ val verifier = Verifier(testDir.absolutePath)
+
+ verifier.deleteArtifact("biz.lermitage.oga", "project-to-test", "1.0.0-SNAPSHOT", "pom")
+
+ // WHEN checking the projects dependencies
+ // THEN the build fails
+ assertThrows(VerificationException::class.java) { verifier.executeGoal("biz.lermitage.oga:oga-maven-plugin:check") }
+
+ // AND the build contains a failure message from the classpath definition file
+ verifier.verifyTextInLog("[ERROR] (dependency) 'org.mock-server' groupId should be replaced by 'com.example.classpath.dependency'")
+ }
+
@Throws(Exception::class)
@Test
fun testProjectWithAdditionalDefinitionFiles() {
diff --git a/src/test/resources/biz/lermitage/oga/classpath_build_config/pom.xml b/src/test/resources/biz/lermitage/oga/classpath_build_config/pom.xml
new file mode 100644
index 0000000..0f43d1c
--- /dev/null
+++ b/src/test/resources/biz/lermitage/oga/classpath_build_config/pom.xml
@@ -0,0 +1,11 @@
+
+ 4.0.0
+
+
+
+ biz.lermitage.oga
+ classpath-build-config
+ 1.0.0-SNAPSHOT
+ Build Configuration
+
diff --git a/src/test/resources/biz/lermitage/oga/classpath_build_config/src/main/resources/classpath-og-definitions.json b/src/test/resources/biz/lermitage/oga/classpath_build_config/src/main/resources/classpath-og-definitions.json
new file mode 100644
index 0000000..2e5959a
--- /dev/null
+++ b/src/test/resources/biz/lermitage/oga/classpath_build_config/src/main/resources/classpath-og-definitions.json
@@ -0,0 +1,10 @@
+{
+ "version": "2",
+ "date": "2021/02/03",
+ "migration": [
+ {
+ "old": "org.mock-server",
+ "new": "com.example.classpath.dependency"
+ }
+ ]
+}
diff --git a/src/test/resources/biz/lermitage/oga/ko_classpath_definitions/pom.xml b/src/test/resources/biz/lermitage/oga/ko_classpath_definitions/pom.xml
new file mode 100644
index 0000000..bc3bae1
--- /dev/null
+++ b/src/test/resources/biz/lermitage/oga/ko_classpath_definitions/pom.xml
@@ -0,0 +1,48 @@
+
+ 4.0.0
+
+ project-to-test
+ 1.0.0-SNAPSHOT
+ pom
+ Test CheckMojo
+
+
+ biz.lermitage.oga
+ parent-pom
+ 1.0.0-SNAPSHOT
+ ../pom.xml
+
+
+
+
+
+ org.mock-server
+ mockserver-junit-rule-no-dependencies
+ 5.13.2
+ test
+
+
+
+
+
+
+ biz.lermitage.oga
+ oga-maven-plugin
+
+
+
+ classpath-og-definitions.json
+
+
+
+
+ biz.lermitage.oga
+ classpath-build-config
+ 1.0.0-SNAPSHOT
+
+
+
+
+
+