Last active
October 5, 2022 20:45
-
-
Save junkdog/f860ed85c6b36df06f5d15c53d73ffa2 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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()) } } | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) } } | |
} | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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