@@ -21,9 +21,8 @@ import org.apache.commons.configuration.Configuration
21
21
import org .apache .commons .lang .ClassUtils .{getAllInterfaces , getAllSuperclasses }
22
22
import org .apache .spark .internal .Logging
23
23
import za .co .absa .spline .commons .lang .ARM
24
- import za .co .absa .spline .harvester .plugin .Plugin
25
24
import za .co .absa .spline .harvester .plugin .Plugin .Precedence
26
- import za .co .absa .spline .harvester .plugin .registry . AutoDiscoveryPluginRegistry .{ EnabledByDefault , EnabledConfProperty , PluginClasses , getOnlyOrThrow }
25
+ import za .co .absa .spline .harvester .plugin .{ Plugin , PluginsConfiguration }
27
26
28
27
import javax .annotation .Priority
29
28
import scala .collection .JavaConverters ._
@@ -32,11 +31,13 @@ import scala.util.Try
32
31
import scala .util .control .NonFatal
33
32
34
33
class AutoDiscoveryPluginRegistry (
35
- conf : Configuration ,
34
+ pluginsConf : PluginsConfiguration ,
36
35
injectables : AnyRef *
37
36
) extends PluginRegistry
38
37
with Logging {
39
38
39
+ import za .co .absa .spline .harvester .plugin .registry .AutoDiscoveryPluginRegistry ._
40
+
40
41
private val injectablesByType : Map [Class [_], Seq [_ <: AnyRef ]] = {
41
42
val typedInjectables =
42
43
for {
@@ -47,13 +48,30 @@ class AutoDiscoveryPluginRegistry(
47
48
typedInjectables.groupBy(_._1).mapValues(_.map(_._2))
48
49
}
49
50
50
- private val allPlugins : Seq [Plugin ] =
51
- for (pc <- PluginClasses if isPluginEnabled(pc)) yield {
52
- logInfo(s " Loading plugin: $pc" )
53
- instantiatePlugin(pc)
54
- .recover({ case NonFatal (e) => throw new RuntimeException (s " Plugin instantiation failure: $pc" , e) })
51
+ private val allPlugins : Seq [Plugin ] = {
52
+ val discoveredClasses : Seq [Class [Plugin ]] =
53
+ if (pluginsConf.classpathScanEnabled) scanForPluginClasses()
54
+ else {
55
+ logInfo(s " Classpath scanning is DISABLED. Only explicitly configured plugins will be loaded. " )
56
+ Seq .empty
57
+ }
58
+
59
+ val configuredClasses : Seq [Class [Plugin ]] = getRegisteredPluginClasses(pluginsConf.config)
60
+
61
+ val allFoundPluginClasses : Seq [Class [Plugin ]] = (discoveredClasses ++ configuredClasses).distinct
62
+
63
+ val allSortedPluginClasses = allFoundPluginClasses
64
+ .map(c => c -> priorityOf(c))
65
+ .sortBy({ case (_, p) => p })
66
+ .map({ case (c, _) => c })
67
+
68
+ for (cls <- allSortedPluginClasses if isPluginEnabled(cls)) yield {
69
+ logInfo(s " Loading plugin: $cls" )
70
+ instantiatePlugin(cls)
71
+ .recover({ case NonFatal (e) => throw new RuntimeException (s " Plugin instantiation failure: $cls" , e) })
55
72
.get
56
73
}
74
+ }
57
75
58
76
override def plugins [A : ClassTag ]: Seq [Plugin with A ] = {
59
77
val ct = implicitly[ClassTag [A ]]
@@ -65,7 +83,7 @@ class AutoDiscoveryPluginRegistry(
65
83
val constr = getOnlyOrThrow(constrs, s " Plugin class must have a single public constructor: ${constrs.mkString(" , " )}" )
66
84
val args = constr.getParameterTypes.map {
67
85
case ct if classOf [Configuration ].isAssignableFrom(ct) =>
68
- conf .subset(pluginClass.getName)
86
+ pluginsConf.config .subset(pluginClass.getName)
69
87
case pt =>
70
88
val candidates = injectablesByType.getOrElse(pt, sys.error(s " Cannot bind $pt. No value found " ))
71
89
getOnlyOrThrow(candidates, s " Ambiguous constructor parameter binding. Multiple values found for $pt: ${candidates.length}" )
@@ -74,7 +92,7 @@ class AutoDiscoveryPluginRegistry(
74
92
}
75
93
76
94
private def isPluginEnabled (pc : Class [Plugin ]): Boolean = {
77
- val pluginConf = conf .subset(pc.getName)
95
+ val pluginConf = pluginsConf.config .subset(pc.getName)
78
96
val isEnabled = pluginConf.getBoolean(EnabledConfProperty , EnabledByDefault )
79
97
if (! isEnabled) {
80
98
logWarning(s " Plugin ${pc.getName} is disabled in the configuration. " )
@@ -89,22 +107,33 @@ object AutoDiscoveryPluginRegistry extends Logging {
89
107
private val EnabledConfProperty = " enabled"
90
108
private val EnabledByDefault = true
91
109
92
- private val PluginClasses : Seq [Class [Plugin ]] = {
110
+ private def scanForPluginClasses () : Seq [Class [Plugin ]] = {
93
111
logDebug(" Scanning for plugins" )
94
112
val classGraph = new ClassGraph ().enableClassInfo
95
113
for {
96
114
scanResult <- ARM .managed(classGraph.scan)
97
- ( cls, prt) <- scanResult
115
+ cls <- scanResult
98
116
.getClassesImplementing(classOf [Plugin ].getName)
99
117
.loadClasses.asScala.asInstanceOf [Seq [Class [Plugin ]]]
100
- .map(c => c -> priorityOf(c))
101
- .sortBy(_._2)
102
118
} yield {
103
- logDebug(s " Found plugin [priority= $prt ] \t : $cls" )
119
+ logDebug(s " Discovered plugin: $cls" )
104
120
cls
105
121
}
106
122
}
107
123
124
+ private def getRegisteredPluginClasses (conf : Configuration ): Seq [Class [Plugin ]] = {
125
+ for {
126
+ key <- conf.getKeys.asScala.toSeq
127
+ if key.endsWith(s " . $EnabledConfProperty" ) // Looking for keys ending with ".enabled", since plugins must be explicitly enabled
128
+ className = key.dropRight(EnabledConfProperty .length + 1 ) // Dropping ".enabled" to get plugin class name
129
+ cls = Class .forName(className)
130
+ if classOf [Plugin ].isAssignableFrom(cls)
131
+ } yield {
132
+ logDebug(s " Found registered plugin: $cls" )
133
+ cls.asInstanceOf [Class [Plugin ]]
134
+ }
135
+ }
136
+
108
137
private def priorityOf (c : Class [Plugin ]): Int =
109
138
Option (c.getAnnotation(classOf [Priority ]))
110
139
.map(_.value)
0 commit comments