Skip to content

Instantly share code, notes, and snippets.

@junkdog
Last active October 5, 2022 20:45
Show Gist options
  • Save junkdog/f860ed85c6b36df06f5d15c53d73ffa2 to your computer and use it in GitHub Desktop.
Save junkdog/f860ed85c6b36df06f5d15c53d73ffa2 to your computer and use it in GitHub Desktop.
package sift.instrumenter.deprecated
import sift.core.entity.Entity
import sift.core.InstrumenterService
import sift.core.api.Action
import sift.core.api.Dsl
import sift.core.api.Dsl.classes
import sift.core.entity.EntityService
import sift.core.tree.EntityNode
import sift.core.tree.Tree
import sift.core.tree.TreeDsl.Companion.tree
typealias JavaDeprecated = java.lang.Deprecated
@Suppress("unused")
object DeprecationInstrumenter : InstrumenterService {
val klazz = Entity.Type("classes")
val referencing = Entity.Type("classes with deprecations")
val method = Entity.Type("methods")
val field = Entity.Type("fields")
override val entityTypes: Iterable<Entity.Type> = listOf(klazz, method, field, referencing)
override fun pipeline(): Action<Unit, Unit> {
fun Dsl.Methods.registerWithParent() {
parentScope("tag class") { entity(referencing) }
referencing["deprecated"] = method
}
fun Dsl.Fields.registerWithParent() {
parentScope("tag class") { entity(referencing) }
referencing["deprecated"] = field
}
return classes {
scope("deprecated java classes") {
annotatedBy<JavaDeprecated>()
entity(klazz)
}
scope("deprecated kotlin classes") {
annotatedBy<Deprecated>()
entity(klazz)
}
fields {
annotatedBy<JavaDeprecated>()
entity(field)
registerWithParent()
}
fields {
annotatedBy<Deprecated>()
entity(field)
registerWithParent()
}
methods {
annotatedBy<JavaDeprecated>()
entity(method)
registerWithParent()
}
methods {
annotatedBy<Deprecated>()
entity(method)
registerWithParent()
}
}
}
override fun toTree(
es: EntityService
): Tree<EntityNode> {
return tree("deprecations") {
if (klazz in es)
add(klazz.id) { es[klazz].values.forEach(::add) }
if (referencing in es)
add(referencing.id) {
es[referencing].values.forEach { e ->
add(e) {
e.children("deprecated").forEach(::add)
}
}
}
}.also { it.sort { o1, o2 -> o1.toString().compareTo(o2.toString()) } }
}
}
package sift.instrumenter.sbacqrs
import org.objectweb.asm.Type
import sift.core.entity.Entity
import sift.core.InstrumenterService
import sift.core.api.Action
import sift.core.api.Dsl
import sift.core.api.Dsl.classes
import sift.core.entity.EntityService
import sift.core.tree.EntityNode
import sift.core.tree.Tree
import sift.core.tree.TreeDsl.Companion.tree
import kotlin.reflect.full.declaredMemberProperties
typealias JavaDeprecated = java.lang.Deprecated
@Suppress("unused")
object SpringBootAxonCqrsInstrumenter : InstrumenterService {
object Annotation {
private val String.type
get() = Type.getType("L${replace('.', '/')};")!!
// axon
val aggregate = "org.axonframework.spring.stereotype.Aggregate".type
val aggregateIdentifier = "org.axonframework.modelling.command.AggregateIdentifier".type
val aggregateMember = "org.axonframework.modelling.command.AggregateMember".type
val restController = "org.springframework.web.bind.annotation.RestController".type
val requestMapping = "org.springframework.web.bind.annotation.RequestMapping".type
val processingGroup = "org.axonframework.config.ProcessingGroup".type
val commandHandler = "org.axonframework.commandhandling.CommandHandler".type
val commandHandlerInterceptor = "org.axonframework.modelling.command.CommandHandlerInterceptor".type
val eventHandler = "org.axonframework.eventhandling.EventHandler".type
val entityId = "org.axonframework.modelling.command.EntityId".type
val queryHandler = "org.axonframework.queryhandling.QueryHandler".type
val eventSourcingHandler = "org.axonframework.eventsourcing.EventSourcingHandler".type
val postMapping = "org.springframework.web.bind.annotation.PostMapping".type
val getMapping = "org.springframework.web.bind.annotation.GetMapping".type
val putMapping = "org.springframework.web.bind.annotation.PutMapping".type
val patchMapping = "org.springframework.web.bind.annotation.PatchMapping".type
val deleteMapping = "org.springframework.web.bind.annotation.DeleteMapping".type
val requestBody = "org.springframework.web.bind.annotation.RequestBody".type
val valid = "javax.validation.Valid".type
}
object EntityType {
// axon
val aggregate = Entity.Type("aggregate")
val command = Entity.Type("command")
val event = Entity.Type("event")
val query = Entity.Type("query")
val queryHandler = Entity.Type("query-handler")
val commandHandler = Entity.Type("command-handler")
val eventSourcingHandler = Entity.Type("event-sourcing-handler")
val eventHandler = Entity.Type("event-handler")
val projection = Entity.Type("projection")
// spring
val controller = Entity.Type("controller")
val endpoint = Entity.Type("endpoint")
}
override val entityTypes: Iterable<Entity.Type> = EntityType::class
.declaredMemberProperties
.map { property -> property(EntityType) as Entity.Type }
override fun pipeline(): Action<Unit, Unit> {
fun Dsl.Methods.registerAxonHandlers(annotation: Type, type: Entity.Type, handler: Entity.Type) {
annotatedBy(annotation)
entity(handler, property("type") {
parameters {
parameter(0) // 1st parameter is command/event
publish(readType()) // register type
// (re-)register command/event entity
explodeType { // class scope of parameter
entity(type)
type["received-by"] = handler
}
}
})
}
fun Dsl.Methods.registerEndpoints(method: String, httpMethod: Type) {
scope(method) {
annotatedBy(httpMethod)
entity(
EntityType.endpoint,
label("$method \${path}"),
property("path", readAnnotation(httpMethod, "value"))
)
parameters {
annotatedBy(Annotation.requestBody)
update(
EntityType.endpoint,
property("request-object", readType())
)
}
}
}
return classes {
scope("register controllers") {
annotatedBy(Annotation.restController)
entity(EntityType.controller)
methods {
// maps to EntityType.endpoint
registerEndpoints("DELETE", Annotation.deleteMapping)
registerEndpoints("GET", Annotation.getMapping)
registerEndpoints("PATCH", Annotation.patchMapping)
registerEndpoints("POST", Annotation.postMapping)
registerEndpoints("PUT", Annotation.putMapping)
}
}
scope("register aggregates") {
annotatedBy(Annotation.aggregate)
entity(EntityType.aggregate)
methods {
scope("register command handlers with aggregate") {
registerAxonHandlers(Annotation.commandHandler, EntityType.command, EntityType.commandHandler)
EntityType.aggregate["commands"] = EntityType.commandHandler
}
scope("register event sourcing handlers with aggregate") {
registerAxonHandlers(
Annotation.eventSourcingHandler,
EntityType.event,
EntityType.eventSourcingHandler
)
EntityType.aggregate["events"] = EntityType.eventSourcingHandler
}
}
}
scope("register projections") {
annotatedBy(Annotation.processingGroup)
entity(EntityType.projection)
methods {
scope("register event handlers with aggregate") {
annotatedBy(Annotation.eventHandler)
registerAxonHandlers(Annotation.eventHandler, EntityType.event, EntityType.eventHandler)
EntityType.projection["events"] = EntityType.eventHandler
}
scope("register event sourcing handlers with aggregate") {
annotatedBy(Annotation.queryHandler)
registerAxonHandlers(Annotation.queryHandler, EntityType.query, EntityType.queryHandler)
EntityType.projection["queries"] = EntityType.queryHandler
}
}
}
scope("scan for sent commands, events and queries") {
fun scanForInstantiated(sought: Entity.Type, source: Entity.Type) {
methodsOf(source) {
instantiationsMatching(sought) {
sought["sent-by"] = source
source["sends"] = sought
}
}
}
scanForInstantiated(EntityType.command, EntityType.aggregate)
scanForInstantiated(EntityType.command, EntityType.endpoint)
scanForInstantiated(EntityType.command, EntityType.projection)
scanForInstantiated(EntityType.event, EntityType.aggregate)
scanForInstantiated(EntityType.query, EntityType.aggregate)
scanForInstantiated(EntityType.query, EntityType.endpoint)
scanForInstantiated(EntityType.query, EntityType.projection)
}
}
}
override fun toTree(
es: EntityService
): Tree<EntityNode> {
TODO()
}
}
package sift.core.tree
import java.lang.StringBuilder
class Tree<T>(val value: T) {
var parent: Tree<T>? = null
private set
private val nodes: MutableList<Tree<T>> = mutableListOf()
val depth: Int
get() = generateSequence { parent }.count()
fun delete() {
parent?.nodes?.remove(this)
parent = null
}
fun add(node: Tree<T>) {
node.delete() // clear any previous association
node.parent = this
nodes += node
}
fun add(value: T) {
add(Tree(value))
}
fun children(): List<Tree<T>> = nodes.toList()
override fun equals(other: Any?) = (other as? Tree<*>)?.value == value
override fun hashCode() = value.hashCode()
fun walk(): TreeWalker<T> = TreeWalker(this)
fun <U> map(f: (T) -> U): Tree<U> {
return Tree(f(value)).also { tree ->
children().map { it.map(f) }.forEach(tree::add)
}
}
override fun toString(): String {
return toString { it.toString() }
}
fun toString(format: (T) -> String): String {
fun print(
node: Tree<T>,
indent: String,
last: Boolean,
out: StringBuilder
): StringBuilder {
val delim = if (last) '└' else '├'
val prefix = "" // todo: line no?
val value = format(node.value)
out.append("$prefix$indent$delim─ ${value}\n")
val nextIndent = indent + (if (last) " " else "│ ")
node.nodes.forEachIndexed { i, n ->
print(n, nextIndent, i == node.nodes.lastIndex, out)
}
return out
}
return print(this, "", true, StringBuilder())
.also { it[it.indexOf('└')] = '─' }
.toString()
}
fun sort(comparator: Comparator<in T>) {
walk()
.filter { it.nodes.isNotEmpty() }
.forEach { it.nodes.sortWith { o1, o2 -> comparator.compare(o1.value, o2.value) } }
}
}
package sift.core.tree
class TreeWalker<T>(val root: Tree<T>) : Sequence<Tree<T>> {
override fun iterator() = object : Iterator<Tree<T>> {
val stack: MutableList<Tree<T>> = mutableListOf(root)
var nextValue: Tree<T>? = null
init {
prepareNext()
}
override fun hasNext() = nextValue != null
override fun next(): Tree<T> {
val value = nextValue ?: error("no more elements")
prepareNext()
return value
}
private fun prepareNext() {
nextValue = stack.removeLastOrNull()
nextValue?.children()?.let(stack::addAll)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment