Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 8 additions & 7 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
plugins {
allprojects {
group = "de.timolia.lactea"
version = "1.0-SNAPSHOT"
}
subprojects {
repositories {
mavenCentral()
}
}
group = "de.timolia"
version = "1.0-SNAPSHOT"

repositories {
mavenCentral()
}
14 changes: 14 additions & 0 deletions config/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
plugins {
`java-library`
kotlin("jvm") version "1.9.10"
}

dependencies {
api(project(":core"))
implementation(kotlin("stdlib-jdk8"))
annotationProcessor("com.google.auto.factory:auto-factory:1.0.1")
api("com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.13.3")
}
kotlin {
jvmToolchain(11)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package de.timolia.lactea.config

import java.io.File
import java.io.IOException
import javax.inject.Singleton

@Singleton
class ConfigController {
private val pathPlaceholders: MutableMap<String, String> = HashMap()
private val extensionToFormat: MutableMap<String, ConfigFormat> = HashMap()
private val defaultFormat: ConfigFormat = ConfigFormat.YAML

init {
registerFormat(ConfigFormat.YAML)
registerFormat(ConfigFormat.JSON)
registerPlaceholder("lactea:", "lactea/config/")
}

private fun registerPlaceholder(name: String, replacement: String) {
pathPlaceholders[name] = replacement
}

private fun registerFormat(format: ConfigFormat) {
extensionToFormat[format.extension] = format
}

private fun acceptPlaceholders(name: String): String {
var name = name
for ((key, value) in pathPlaceholders) {
name = name.replace(key, value)
}
return name
}

fun formatByExtensionOrDefault(extension: String) = extensionToFormat.getOrDefault(extension, defaultFormat)

@Throws(IOException::class)
fun <T> loadConfig(name: String, clazz: Class<T>): T {
val file = File(acceptPlaceholders(name))
val parent = file.absoluteFile.parentFile
parent.mkdirs()
val candidates = parent.listFiles()
check(candidates != null) {"$file is not a Directory"}
for (candidate in candidates) {
if (candidate.isDirectory) {
continue
}
if (candidate.nameWithoutExtension == file.name) {
return formatByExtensionOrDefault(candidate.extension).loadConfig(candidate, clazz)
}
}
return createDefault(parent, file.name, clazz)
}

@Throws(IOException::class)
private fun <T> createDefault(parent: File, name: String, clazz: Class<T>): T {
val format = defaultFormat
val file = File(parent, name + '.' + format.extension)
format.objectMapper.writeValue(file, clazz.newInstance())
file.createNewFile()
return format.loadConfig(file, clazz)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package de.timolia.lactea.config

@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
annotation class ConfigDefinition(val value: String)
37 changes: 37 additions & 0 deletions config/src/main/kotlin/de/timolia/lactea/config/ConfigFormat.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package de.timolia.lactea.config

import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory
import java.io.File
import java.io.IOException

abstract class ConfigFormat (
val extension: String,
val name: String,
) {
val objectMapper: ObjectMapper by lazy(::createObjectMapper)

protected abstract fun createObjectMapper(): ObjectMapper

@Throws(IOException::class)
fun <T> loadConfig(file: File, clazz: Class<T>): T {
return objectMapper.readValue(file, clazz)
}

companion object {
var YAML: ConfigFormat = object : ConfigFormat("yml", "yaml") {
public override fun createObjectMapper(): ObjectMapper {
val mapper = ObjectMapper(YAMLFactory())
mapper.findAndRegisterModules()
return mapper
}
}
var JSON: ConfigFormat = object : ConfigFormat("json", "json") {
public override fun createObjectMapper(): ObjectMapper {
val mapper = ObjectMapper()
mapper.findAndRegisterModules()
return mapper
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package de.timolia.lactea.config

import com.google.inject.AbstractModule
import java.io.IOException
import java.util.logging.Level

class LocalConfigModule(
) : AbstractModule() {

/*private fun <T> createConfigObject(name: String, clazz: Class<T>): T? {
return try {
controller.loadConfig<T>(name, clazz)
} catch (e: IOException) {
logger.log(Level.WARNING, "Failed to load config with name $name as $clazz", e)
try {
clazz.getConstructor().newInstance()
} catch (ex: Exception) {
logger.log(Level.WARNING, "Failed to create dummy config object", ex)
null
}
}
}

private fun <T> bindConfig(name: String, clazz: Class<T>) {
bind<T>(clazz).toInstance(createConfigObject<T>(name, clazz))
}*/
}
14 changes: 14 additions & 0 deletions core/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
plugins {
`java-library`
kotlin("jvm") version "1.9.10"
}

dependencies {
api(project(":module-api"))
compileOnlyApi(project(":initialize"))
implementation("org.javassist:javassist:3.29.0-GA")
implementation(kotlin("stdlib-jdk8"))
}
kotlin {
jvmToolchain(11)
}
32 changes: 32 additions & 0 deletions core/src/main/kotlin/de/timolia/lactea/core/Lactea.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package de.timolia.lactea.core

import com.google.inject.Guice
import com.google.inject.Injector
import com.google.inject.Module
import de.timolia.lactea.Bootstrap
import de.timolia.lactea.core.lifecycle.startup.LoadEnableSplitStartup
import de.timolia.lactea.core.lifecycle.startup.SingleEntrypointStartup
import de.timolia.lactea.core.lifecycle.startup.StartUp
import de.timolia.lactea.path.PathResolver
import de.timolia.lactea.source.ClassPathInjector

class Lactea (
bootstrap: Bootstrap,
vararg modules: Module,
) {
val injector: Injector

init {
injector = Guice.createInjector(
*modules,
Module {
it.bind(ClassPathInjector::class.java) to bootstrap.classPathInjector()
it.bind(PathResolver::class.java) to bootstrap.pathResolver()
}
)
}

fun singleEntrypoint(): SingleEntrypointStartup = injector.getInstance(StartUp::class.java)

fun loadEnableSplit(): LoadEnableSplitStartup = injector.getInstance(StartUp::class.java)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package de.timolia.lactea.core.discovery

import javassist.bytecode.annotation.Annotation as ByteCodeAnnotation

class DiscoveryClass(
val name: String,
val annotations: Array<ByteCodeAnnotation>
) {
private val byType: MutableMap<String, ByteCodeAnnotation> = HashMap()

init {
buildTypeIndex()
}

private fun buildTypeIndex() {
for (annotation in annotations) {
check(byType.put(annotation.getTypeName(), annotation) == null) { "Illegal discovery name=$name annotation=$annotation" }
}
}

fun byType(search: String): ByteCodeAnnotation? {
return byType[search]
}

fun byType(search: Class<out Annotation>): ByteCodeAnnotation? {
return byType(search.name)
}

@Throws(ClassNotFoundException::class)
fun loadClass(): Class<*> {
return Class.forName(name)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package de.timolia.lactea.core.discovery

import com.google.common.collect.HashMultimap
import com.google.common.collect.Multimap
import javassist.bytecode.AnnotationsAttribute
import javassist.bytecode.ClassFile
import java.io.DataInputStream
import java.io.IOException
import java.util.*
import java.util.jar.JarEntry
import java.util.jar.JarFile

class DiscoveryIndex {
private val index: Multimap<String, DiscoveryClass> = HashMultimap.create()

fun addToIndex(discoveryClass: DiscoveryClass) {
for (annotation in discoveryClass.annotations) {
index.put(annotation.typeName, discoveryClass)
}
}

@Throws(IOException::class)
fun indexJarFile(jar: JarFile) {
val entries: Enumeration<JarEntry> = jar.entries()
while (entries.hasMoreElements()) {
val jarEntry: JarEntry = entries.nextElement()
if (jarEntry.name.endsWith(".class")) {
val classFile = ClassFile(DataInputStream(jar.getInputStream(jarEntry)))
val visible: AnnotationsAttribute = classFile.getAttribute("RuntimeVisibleAnnotations") as AnnotationsAttribute
if (visible != null) {
addToIndex(DiscoveryClass(classFile.name, visible.annotations))
}
}
}
}

fun runDiscovery(search: String) = DiscoveryResult(index[search])

fun runDiscovery(search: Class<out Annotation>) = runDiscovery(search.name)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package de.timolia.lactea.core.discovery

data class DiscoveryResult(val all: Collection<DiscoveryClass>) {
fun requireExactlyOne(name: String): DiscoveryClass {
check(all.size == 1) {
("Require exactly one $name Candidates are: $all")
}
return all.iterator().next()
}
}
Loading