com.github.bjoernpetersen.jmusicbot.PlaybackFactoryManager.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of musicbot Show documentation
Show all versions of musicbot Show documentation
Core library of MusicBot, which plays music from various providers.
package com.github.bjoernpetersen.jmusicbot
import com.github.bjoernpetersen.jmusicbot.config.Config
import com.github.bjoernpetersen.jmusicbot.platform.Platform
import com.github.bjoernpetersen.jmusicbot.playback.PlaybackFactory
import com.github.bjoernpetersen.jmusicbot.playback.PlaybackFactoryLoader
import java.io.Closeable
import java.io.File
import java.io.IOException
import java.util.*
typealias PlaybackFactoryWrapperFactory = (PlaybackFactory) -> PlaybackFactoryWrapper
/**
* Manages all PlaybackFactories for a MusicBot session.
* @param config a config to use
* @param wrapperFactory a factory supplying a [PlaybackFactoryWrapper] for a [PlaybackFactory]
*/
class PlaybackFactoryManager(config: Config, private val wrapperFactory: PlaybackFactoryWrapperFactory) :
Loggable, Closeable {
private val factories: MutableSet = LinkedHashSet()
private val factoryByType: MutableMap, PlaybackFactoryWrapper> = LinkedHashMap()
/**
* Returns an immutable Set of all PlaybackFactories.
*/
val playbackFactories: Set
get() = Collections.unmodifiableSet(factories)
init {
val pluginFolderName = config.defaults.pluginFolder.value
val pluginFolder = File(pluginFolderName)
loadFactories(pluginFolder)
}
/**
* Gets a PlaybackFactory that implements the given marker interface.
*
* @param factoryType a PlaybackFactory marker interface
* @param the type of PlaybackFactory
* @return an instance of F
* @throws IllegalArgumentException if no implementation of the given interface is active.
*/
fun getFactory(factoryType: Class): F {
val result = factoryByType[factoryType]
return if (result == null || !result.isActive) {
throw IllegalArgumentException("No factory for: " + factoryType.simpleName)
} else {
try {
factoryType.cast(result.wrapped)
} catch (e: ClassCastException) {
throw IllegalArgumentException("Wrong type for factory: " + factoryType.name, e)
}
}
}
/**
* Checks whether there is an active factory implementing the specified type.
*
* @param factoryType a PlaybackFactory marker interface
* @return whether an implementing factory is active
*/
fun hasFactory(factoryType: Class): Boolean = factoryByType[factoryType]?.isActive == true
/**
* Completely closes all registered playback factories.
*/
@Throws(IOException::class)
override fun close() {
for (playbackFactory in playbackFactories) {
playbackFactory.close()
playbackFactory.destructConfigEntries()
}
}
private fun loadFactories(pluginFolder: File) {
for (factory in PluginLoader(pluginFolder, PlaybackFactory::class.java).load()) {
try {
storeFactoryForValidBases(factory)
} catch (e: InvalidFactoryException) {
logInfo(e, "Could not load factory " + factory.readableName)
} catch (e: InitializationException) {
logInfo(e, "Could not load factory " + factory.readableName)
}
}
val platform = Platform.get()
for (loader in PluginLoader(pluginFolder, PlaybackFactoryLoader::class.java).load()) {
val factory = loader.load(platform)
if (factory == null) {
logInfo("Platform not supported by PlaybackFactory %s", loader.readableName)
} else {
try {
storeFactoryForValidBases(factory)
} catch (e: InvalidFactoryException) {
logInfo(e, "Could not load factory %s", factory.readableName)
} catch (e: InitializationException) {
logInfo(e, "Could not load factory %s", factory.readableName)
}
}
}
}
@Throws(InvalidFactoryException::class, InitializationException::class)
private fun storeFactoryForValidBases(factory: PlaybackFactory) {
val validBases = LinkedList>()
for (base in factory.bases) {
if (!base.isAssignableFrom(factory.javaClass)) {
logWarning("Bad base '%s' for PlaybackFactory: %s", base, factory)
continue
}
validBases.add(base)
}
if (validBases.isEmpty()) {
throw InvalidFactoryException()
}
val wrapper = wrapperFactory(factory)
factories.add(wrapper)
for (base in validBases) {
factoryByType.put(base, wrapper)
}
}
/**
* Ensures all playback factories in the [Plugin.State.CONFIG] state are fully configured.
* @param configurator a configurator to ask the user to fix missing config entries
*/
@Throws(CancelException::class)
fun ensureConfigured(configurator: Configurator) {
val unconfigured = LinkedList()
for (factory in playbackFactories.filter { it.state == Plugin.State.CONFIG }) {
var missing: List = factory.missingConfigEntries
wh@ while (!missing.isEmpty()) {
val result = configurator.configure(factory.readableName, missing)
when (result) {
Configurator.Result.CANCEL -> throw CancelException("User cancelled configuration")
Configurator.Result.DISABLE -> {
logInfo("Deactivating unconfigured plugin " + factory.readableName)
unconfigured.add(factory)
break@wh // I feel dirty
}
Configurator.Result.OK -> {
// just continue
}
}
missing = factory.missingConfigEntries
}
}
for (factory in unconfigured) {
removeFactory(factory)
}
}
/**
* Initializes all playback factories in the [Plugin.State.CONFIG] state.
*/
@Throws(InterruptedException::class)
fun initializeFactories(initStateWriter: InitStateWriter) {
val defective = LinkedList()
for (factory in playbackFactories.filter { it.state == Plugin.State.CONFIG }) {
try {
if (Thread.currentThread().isInterrupted) {
throw InterruptedException()
}
initStateWriter.begin(factory)
factory.initialize(initStateWriter)
} catch (e: InitializationException) {
logWarning(e, "Could not initialize PlaybackFactory '%s'", factory.readableName)
defective.add(factory)
} catch (e: InterruptedException) {
initStateWriter.state("Interrupted during PlaybackFactory initialization, closing...")
close()
throw e
}
}
for (factory in defective) {
removeFactory(factory)
}
}
private fun removeFactory(factory: PlaybackFactory) {
for (base in factory.bases) {
val registered = getFactory(base)
if (registered === factory) {
factoryByType.remove(base)
}
}
factories.remove(factory)
factory.destructConfigEntries()
}
private class InvalidFactoryException : Exception()
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy