@@ -10,15 +10,16 @@ import java.io.InputStream
1010import java.net.URL
1111import java.net.URLClassLoader
1212import java.nio.channels.Channels
13- import java.nio.file.Files
1413import java.util.*
1514import java.util.jar.JarFile
16- import java.util.regex.Pattern
17-
1815
1916@SpringBootApplication
2017class PluginManager (val config : PluginsConfig ) {
2118
19+ companion object {
20+ private val log: Logger = LoggerFactory .getLogger(PluginManager ::class .java)
21+ }
22+
2223 final val pluginManifests: MutableList <PluginManifest > = mutableListOf ()
2324 var classLoader: ClassLoader = PluginManager ::class .java.classLoader
2425
@@ -35,14 +36,13 @@ class PluginManager(val config: PluginsConfig) {
3536 val directory = File (config.pluginsDir)
3637 directory.mkdir()
3738
38- data class PluginJar (val name : String , val version : String , val file : File )
39+ data class PluginJar (val manifest : PluginManifest , val file : File )
3940
40- val pattern = Pattern .compile(" (.+)-(.+)\\ .jar$" )
41- val pluginJars = directory.listFiles()!! .mapNotNull { f ->
42- val matcher = pattern.matcher(f.name)
43- if (! matcher.find()) return @mapNotNull null
44- PluginJar (matcher.group(1 ), matcher.group(2 ), f)
45- }
41+ val pluginJars = directory.listFiles()!! .filter { it.extension == " jar" }.map {
42+ JarFile (it).use {jar ->
43+ loadPluginManifests(jar).map { manifest -> PluginJar (manifest, it) }
44+ }
45+ }.flatten()
4646
4747 data class Declaration (val group : String , val name : String , val version : String , val repository : String )
4848
@@ -55,21 +55,22 @@ class PluginManager(val config: PluginsConfig) {
5555 ? : if (declaration.snapshot) config.defaultPluginSnapshotRepository else config.defaultPluginRepository
5656 repository = if (repository.endsWith(" /" )) repository else " $repository /"
5757 Declaration (fragments[0 ], fragments[1 ], fragments[2 ], repository)
58- }
58+ }.distinctBy { " ${it.group} : ${it.name} " }
5959
6060 declarations.forEach declarationLoop@{ declaration ->
61- pluginJars.forEach { jar ->
62- if (declaration.name == jar.name) {
63- if (declaration.version == jar.version) {
64- // We already have this jar so don't redownload it
65- return @declarationLoop
66- }
67-
68- // Delete jar of different versions
69- if (! jar.file.delete()) throw RuntimeException (" Failed to delete ${jar.file.path} " )
70- log.info(" Deleted ${jar.file.path} " )
61+ var hasVersion = false
62+ pluginJars.forEach pluginLoop@{ jar ->
63+ if (declaration.version == jar.manifest.version && ! hasVersion) {
64+ hasVersion = true
65+ // We already have this jar so don't redownload it
66+ return @pluginLoop
7167 }
68+
69+ // Delete jar of different versions
70+ if (! jar.file.delete()) throw RuntimeException (" Failed to delete ${jar.file.path} " )
71+ log.info(" Deleted ${jar.file.path} " )
7272 }
73+ if (hasVersion) return @declarationLoop
7374
7475 val url = declaration.run { " $repository${group.replace(" ." , " /" )} /$name /$version /$name -$version .jar" }
7576 val file = File (directory, declaration.run { " $name -$version .jar" })
@@ -87,16 +88,19 @@ class PluginManager(val config: PluginsConfig) {
8788 private fun readClasspathManifests (): List <PluginManifest > {
8889 return PathMatchingResourcePatternResolver ()
8990 .getResources(" classpath*:lavalink-plugins/*.properties" )
90- .map { r -> parsePluginManifest(r.inputStream) }
91+ .map map@{ r ->
92+ val manifest = parsePluginManifest(r.inputStream)
93+ log.info(" Found plugin '${manifest.name} ' version ${manifest.version} " )
94+ return @map manifest
95+ }
9196 }
9297
9398 private fun loadJars (): List <PluginManifest > {
9499 val directory = File (config.pluginsDir)
95100 if (! directory.isDirectory) return emptyList()
96101 val jarsToLoad = mutableListOf<File >()
97102
98- Files .list(File (config.pluginsDir).toPath()).forEach { path ->
99- val file = path.toFile()
103+ directory.listFiles()?.forEach { file ->
100104 if (! file.isFile) return @forEach
101105 if (file.extension != " jar" ) return @forEach
102106 jarsToLoad.add(file)
@@ -111,7 +115,6 @@ class PluginManager(val config: PluginsConfig) {
111115 classLoader = cl
112116
113117 val manifests = mutableListOf<PluginManifest >()
114-
115118 jarsToLoad.forEach { file ->
116119 try {
117120 manifests.addAll(loadJar(file, cl))
@@ -124,31 +127,43 @@ class PluginManager(val config: PluginsConfig) {
124127 return manifests
125128 }
126129
127- private fun loadJar (file : File , cl : URLClassLoader ): MutableList <PluginManifest > {
130+ private fun loadJar (file : File , cl : URLClassLoader ): List <PluginManifest > {
128131 var classCount = 0
129132 val jar = JarFile (file)
133+ var manifests: List <PluginManifest >
134+
135+ jar.use {
136+ manifests = loadPluginManifests(jar)
137+ if (manifests.isEmpty()) {
138+ throw RuntimeException (" No plugin manifest found in ${file.path} " )
139+ }
140+ val allowedPaths = manifests.map { it.path.replace(" ." , " /" ) }
141+
142+ jar.entries().asIterator().forEach { entry ->
143+ if (entry.isDirectory) return @forEach
144+ if (! entry.name.endsWith(" .class" )) return @forEach
145+ if (! allowedPaths.any { entry.name.startsWith(it) }) return @forEach
146+ cl.loadClass(entry.name.dropLast(6 ).replace(" /" , " ." ))
147+ classCount++
148+ }
149+ }
150+
151+ log.info(" Loaded ${file.name} ($classCount classes)" )
152+ return manifests
153+ }
154+
155+ private fun loadPluginManifests (jar : JarFile ): List <PluginManifest > {
130156 val manifests = mutableListOf<PluginManifest >()
131157
132158 jar.entries().asIterator().forEach { entry ->
133159 if (entry.isDirectory) return @forEach
134160 if (! entry.name.startsWith(" lavalink-plugins/" )) return @forEach
135161 if (! entry.name.endsWith(" .properties" )) return @forEach
136- manifests.add(parsePluginManifest(jar.getInputStream(entry)))
137- }
138162
139- if (manifests.isEmpty()) {
140- throw RuntimeException (" No plugin manifest found in ${file.path} " )
163+ val manifest = parsePluginManifest(jar.getInputStream(entry))
164+ log.info(" Found plugin '${manifest.name} ' version ${manifest.version} " )
165+ manifests.add(manifest)
141166 }
142- val allowedPaths = manifests.map { it.path.replace(" ." , " /" ) }
143-
144- jar.entries().asIterator().forEach { entry ->
145- if (entry.isDirectory) return @forEach
146- if (! entry.name.endsWith(" .class" )) return @forEach
147- if (! allowedPaths.any { entry.name.startsWith(it) }) return @forEach
148- cl.loadClass(entry.name.dropLast(6 ).replace(" /" , " ." ))
149- classCount++
150- }
151- log.info(" Loaded ${file.name} ($classCount classes)" )
152167 return manifests
153168 }
154169
@@ -160,11 +175,6 @@ class PluginManager(val config: PluginsConfig) {
160175 val name = props.getProperty(" name" ) ? : throw RuntimeException (" Manifest is missing 'name'" )
161176 val path = props.getProperty(" path" ) ? : throw RuntimeException (" Manifest is missing 'path'" )
162177 val version = props.getProperty(" version" ) ? : throw RuntimeException (" Manifest is missing 'version'" )
163- log.info(" Found plugin '$name ' version $version " )
164178 return PluginManifest (name, path, version)
165179 }
166-
167- companion object {
168- private val log: Logger = LoggerFactory .getLogger(PluginManager ::class .java)
169- }
170180}
0 commit comments