Skip to content

Commit

Permalink
Merge pull request #4 from tigrulya-exe/processor-json
Browse files Browse the repository at this point in the history
Added KCacheableJpa annotation and friends
  • Loading branch information
tigrulya-exe authored Apr 6, 2021
2 parents 98028fe + 1fbc4f8 commit 2ab018a
Show file tree
Hide file tree
Showing 28 changed files with 312 additions and 259 deletions.
4 changes: 4 additions & 0 deletions library/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ dependencies {
implementation("org.redisson:redisson:3.15.1")

implementation("com.hazelcast:hazelcast:4.1.1")
implementation("org.reflections:reflections:0.9.11")
implementation("io.github.classgraph:classgraph:4.8.102")
// TODO: make optional with nebula plugin
implementation("org.hibernate:hibernate-core:5.4.30.Final")

testImplementation("org.springframework.boot:spring-boot-starter-test")
annotationProcessor("org.springframework.boot:spring-boot-configuration-processor")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package ru.nsu.manasyan.kcache.aspect
import org.aspectj.lang.JoinPoint
import org.aspectj.lang.annotation.After
import org.aspectj.lang.annotation.Aspect
import org.aspectj.lang.reflect.MethodSignature
import ru.nsu.manasyan.kcache.core.annotations.KCacheEvict
import ru.nsu.manasyan.kcache.core.state.holdermanager.StateHolderManager
import ru.nsu.manasyan.kcache.core.state.provider.NewStateProvider
Expand All @@ -20,17 +19,16 @@ class KCacheEvictAspect(
* Processes section of code, which changes the state of the DB.
* Updates state of each DB table, listed in the tables field of [KCacheEvict]
*/
@After("@annotation(ru.nsu.manasyan.kcache.core.annotations.KCacheEvict)")
fun wrapKCacheEvictMethod(joinPoint: JoinPoint) {
val method = (joinPoint.signature as MethodSignature).method
// we know, that method has KCacheEvict annotation
// TODO: mb get tables from generated container as well as in KCacheable
val annotation = getAnnotationInstance<KCacheEvict>(method)!!
annotation.tables.forEach { tableName ->
@After("@annotation(kCacheEvict)")
fun wrapKCacheEvictMethod(
joinPoint: JoinPoint,
kCacheEvict: KCacheEvict
) {
kCacheEvict.tables.forEach { tableName ->
val newState = newStateProvider.provide(tableName)
stateHolderManager
.getOrCreateStateHolder(tableName)
.setState(annotation.key, newState)
.setState(kCacheEvict.key, newState)
logger.debug("Table $tableName was updated: $newState")
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,26 @@ import org.springframework.expression.spel.support.StandardEvaluationContext
import org.springframework.http.HttpHeaders
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.RequestHeader
import ru.nsu.manasyan.kcache.aspect.strategy.KCacheableAspectStrategy
import ru.nsu.manasyan.kcache.core.annotations.KCacheable
import ru.nsu.manasyan.kcache.core.annotations.KCacheableJpa
import ru.nsu.manasyan.kcache.core.etag.builder.ETagBuilder
import ru.nsu.manasyan.kcache.core.etag.extractor.IfNoneMatchHeaderExtractor
import ru.nsu.manasyan.kcache.core.resultbuilder.ResultBuilderFactory
import ru.nsu.manasyan.kcache.util.LoggerProperty
import ru.nsu.manasyan.kcache.util.ifDebug
import kotlin.reflect.full.createInstance

@Aspect
class KCacheableAspect(
open class KCacheableAspect(
private val eTagBuilder: ETagBuilder,
private val headerExtractor: IfNoneMatchHeaderExtractor,
private val strategy: KCacheableAspectStrategy,
private val expressionParser: ExpressionParser
) {
private companion object {
private const val SPEL_PREFIX = "#"
private const val SPEL_CONTEXT_ARGS_KEY = "args"
}

private val logger by LoggerProperty()

/**
Expand All @@ -38,49 +44,78 @@ class KCacheableAspect(
* then the wrapped method is called and its result ([ResponseEntity]) is returned
* with the ETag header set to the current ETag value.
*/
@Around("@annotation(ru.nsu.manasyan.kcache.core.annotations.KCacheable)")
fun wrapKCacheableControllerMethod(joinPoint: ProceedingJoinPoint): Any? {
@Around("@annotation(kCacheable)")
open fun wrapKCacheableMethod(
joinPoint: ProceedingJoinPoint,
kCacheable: KCacheable,
): Any? = handleKCacheable(
joinPoint,
kCacheable.tables.toList(),
kCacheable.key,
kCacheable.resultBuilderFactory.createInstance()
)

@Around("@annotation(kCacheableJpa)")
open fun wrapKCacheableJpaMethod(
joinPoint: ProceedingJoinPoint,
kCacheableJpa: KCacheableJpa,
): Any? = handleKCacheable(
joinPoint,
kCacheableJpa.entities.map { it.qualifiedName!! },
"",
kCacheableJpa.resultBuilderFactory.createInstance()
)

private fun handleKCacheable(
joinPoint: ProceedingJoinPoint,
tables: List<String>,
key: String,
resultBuilderFactory: ResultBuilderFactory
): Any? {
val methodSignature = joinPoint.signature as MethodSignature
strategy.methodSignature = methodSignature
val methodArgs = joinPoint.args

val currentETag = eTagBuilder.buildETag(
strategy.getTableStates(),
getKey(strategy.getKeyExpression(), joinPoint.args)
tables,
getKey(key, methodArgs)
)

val previousETag = headerExtractor.extract(
methodSignature.method,
joinPoint.args
)

val factory = strategy.getResultBuilderFactory()
if (currentETag == previousETag) {
logger.ifDebug(
"Equal ETags for method ${methodSignature.getMethodName()}: " +
"$currentETag, returning 304"
)
return factory.getOnHitResultBuilder()
.build(currentETag)
return resultBuilderFactory.getOnHitResultBuilder().build(currentETag)
}

logger.ifDebug(
"Different ETags for method ${methodSignature.getMethodName()}: " +
"Current [$currentETag] Previous[$previousETag]. Invoking method."
)

return factory.getOnMissResultBuilder()
.build(joinPoint.proceed(), currentETag)
return resultBuilderFactory.getOnMissResultBuilder().build(
joinPoint.proceed(),
currentETag
)
}

private fun getKey(keyExpression: String, parameters: Array<Any>): String {
if (!keyExpression.startsWith("#")) {
private fun getKey(keyExpression: String, args: Array<Any>): String {
if (!keyExpression.startsWith(SPEL_PREFIX)) {
return keyExpression
}
val expression = expressionParser.parseExpression(keyExpression)
val context: EvaluationContext = StandardEvaluationContext().apply {
setVariable("params", parameters)
setVariable(SPEL_CONTEXT_ARGS_KEY, args)
}
return expression.getValue(context)?.toString()
return expressionParser
.parseExpression(keyExpression)
.getValue(context)
?.toString()
?: throw IllegalArgumentException("Key should not be null")
}
}
}

This file was deleted.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@ import org.springframework.expression.ExpressionParser
import org.springframework.expression.spel.standard.SpelExpressionParser
import ru.nsu.manasyan.kcache.aspect.KCacheEvictAspect
import ru.nsu.manasyan.kcache.aspect.KCacheableAspect
import ru.nsu.manasyan.kcache.aspect.strategy.KCacheableAspectStrategy
import ru.nsu.manasyan.kcache.config.aspectstrategy.AspectStrategyConfiguration
import ru.nsu.manasyan.kcache.config.jpa.HibernateListenerConfiguration
import ru.nsu.manasyan.kcache.config.stateholdermanager.StateHolderConfiguration
import ru.nsu.manasyan.kcache.core.etag.builder.ConcatenateETagBuilder
import ru.nsu.manasyan.kcache.core.etag.builder.ETagBuilder
Expand All @@ -30,7 +29,7 @@ import ru.nsu.manasyan.kcache.util.LoggerProperty
StateHolderConfiguration::class,
ETagExtractorConfiguration::class,
StateProviderConfiguration::class,
AspectStrategyConfiguration::class
HibernateListenerConfiguration::class
]
)
@EnableConfigurationProperties(KCacheProperties::class)
Expand Down Expand Up @@ -60,14 +59,12 @@ class KCacheAutoConfiguration {
fun kCacheAspect(
eTagBuilder: ETagBuilder,
extractor: IfNoneMatchHeaderExtractor,
strategy: KCacheableAspectStrategy,
expressionParser: ExpressionParser
): KCacheableAspect {
logger.debug("Building KCacheAspect")
return KCacheableAspect(
eTagBuilder,
extractor,
strategy,
expressionParser
)
}
Expand Down

This file was deleted.

This file was deleted.

Loading

0 comments on commit 2ab018a

Please sign in to comment.