Skip to content

Commit

Permalink
feat: Add basic support to custom plugins
Browse files Browse the repository at this point in the history
To be compatible with zpa-cli, the plugins should have a META-INF/extensions.idx file with the fully-qualified name of a CustomPlSqlRulesDefinition subclass
  • Loading branch information
felipebz committed Apr 24, 2023
1 parent cb2125f commit 52b4772
Show file tree
Hide file tree
Showing 6 changed files with 100 additions and 3 deletions.
2 changes: 2 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ dependencies {
implementation("com.squareup.okhttp3:okhttp:4.10.0")
implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.14.0")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.14.0")
implementation("org.pf4j:pf4j:3.9.0")
implementation("org.slf4j:slf4j-jdk14:2.0.7")
testImplementation(kotlin("test"))
}

Expand Down
16 changes: 15 additions & 1 deletion src/main/kotlin/br/com/felipezorzo/zpa/cli/Main.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package br.com.felipezorzo.zpa.cli

import br.com.felipezorzo.zpa.cli.plugin.PluginManager
import br.com.felipezorzo.zpa.cli.sonarqube.SonarQubeLoader
import br.com.felipezorzo.zpa.cli.sqissue.GenericIssueData
import br.com.felipezorzo.zpa.cli.sqissue.PrimaryLocation
Expand All @@ -24,6 +25,7 @@ import org.sonar.plsqlopen.squid.AstScanner
import org.sonar.plsqlopen.squid.ProgressReport
import org.sonar.plsqlopen.squid.ZpaIssue
import org.sonar.plsqlopen.utils.log.Loggers
import org.sonar.plugins.plsqlopen.api.CustomPlSqlRulesDefinition
import org.sonar.plugins.plsqlopen.api.PlSqlFile
import org.sonar.plugins.plsqlopen.api.checks.PlSqlVisitor
import java.io.File
Expand Down Expand Up @@ -57,6 +59,15 @@ class Main : CliktCommand(name = "zpa-cli") {
LogManager.getLogManager().readConfiguration(it)
}

val pluginManager = PluginManager()
pluginManager.loadPlugins()
pluginManager.startPlugins()

// print loaded plugins
for (plugin in pluginManager.startedPlugins) {
LOG.info("Plugin '${plugin.descriptor.pluginId}@${plugin.descriptor.version}' loaded")
}

val extensions = extensions.split(',')

val stopwatch = Stopwatch.createStarted()
Expand All @@ -78,7 +89,8 @@ class Main : CliktCommand(name = "zpa-cli") {

val checkList = mutableListOf<PlSqlVisitor>()

val rulesDefinitions= listOf(DefaultRulesDefinition())
val rulesDefinitions= listOf(DefaultRulesDefinition(),
*pluginManager.getExtensions(CustomPlSqlRulesDefinition::class.java).toTypedArray())

for (rulesDefinition in rulesDefinitions) {
val repository = Repository(rulesDefinition.repositoryKey())
Expand Down Expand Up @@ -140,6 +152,8 @@ class Main : CliktCommand(name = "zpa-cli") {
}

LOG.info("Time elapsed: ${stopwatch.elapsed().toMillis()} ms")
pluginManager.stopPlugins()
pluginManager.unloadPlugins()
}

private fun printIssues(issues: List<ZpaIssue>) {
Expand Down
12 changes: 12 additions & 0 deletions src/main/kotlin/br/com/felipezorzo/zpa/cli/plugin/PluginManager.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package br.com.felipezorzo.zpa.cli.plugin

import org.pf4j.DefaultPluginManager
import org.pf4j.PluginDescriptorFinder

class PluginManager: DefaultPluginManager() {

override fun createPluginDescriptorFinder(): PluginDescriptorFinder {
return ZpaPluginDescriptorFinder()
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package br.com.felipezorzo.zpa.cli.plugin

import org.pf4j.DefaultPluginDescriptor

class ZpaPluginDescriptor : DefaultPluginDescriptor() {

public override fun setPluginId(pluginId: String): DefaultPluginDescriptor {
return super.setPluginId(pluginId)
}

fun setVersion(version: String): DefaultPluginDescriptor {
return super.setPluginVersion(version)
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package br.com.felipezorzo.zpa.cli.plugin

import org.pf4j.PluginDescriptor
import org.pf4j.PluginDescriptorFinder
import org.pf4j.PluginRuntimeException
import org.pf4j.util.FileUtils
import java.io.IOException
import java.nio.file.Files
import java.nio.file.Path
import java.util.jar.JarFile
import java.util.jar.Manifest

class ZpaPluginDescriptorFinder : PluginDescriptorFinder {

override fun isApplicable(pluginPath: Path): Boolean {
return Files.exists(pluginPath) && FileUtils.isJarFile(pluginPath)
}

override fun find(pluginPath: Path): PluginDescriptor {
val manifest = readManifest(pluginPath)
return createPluginDescriptor(manifest)
}

private fun readManifest(pluginPath: Path): Manifest {
try {
return JarFile(pluginPath.toFile()).use { it.manifest }
} catch (e: IOException) {
throw PluginRuntimeException(e, "Cannot read manifest from {}", pluginPath)
}
}

private fun createPluginDescriptor(manifest: Manifest): PluginDescriptor {
val pluginDescriptor = ZpaPluginDescriptor()
val attributes = manifest.mainAttributes

val id = attributes.getValue(PLUGIN_ID)
pluginDescriptor.pluginId = id

val version = attributes.getValue(PLUGIN_VERSION)
if (version.isNotEmpty()) {
pluginDescriptor.version = version
}

return pluginDescriptor
}

companion object {
const val PLUGIN_ID = "Plugin-Key"
const val PLUGIN_VERSION = "Plugin-Version"
}

}
6 changes: 4 additions & 2 deletions src/main/resources/logging.properties
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,7 @@ handlers=java.util.logging.ConsoleHandler
java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter
java.util.logging.SimpleFormatter.format=%1$tH:%1$tM:%1$tS %4$s: %5$s %6$s%n

java.util.logging.ConsoleHandler.level=INFO
app.level=INFO
.level=INFO

# PF4J log
org.pf4j.level=WARNING

0 comments on commit 52b4772

Please sign in to comment.