diff --git a/build.gradle b/build.gradle index 8f6404af26..94ce6dbe9b 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile import static org.gradle.api.JavaVersion.VERSION_17 import static org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17 -import static org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_1_8 +import static org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_1_9 buildscript { // For sharing constants between builds @@ -284,8 +284,8 @@ allprojects { tasks.withType(KotlinCompile).configureEach { compilerOptions { - languageVersion = KOTLIN_1_8 - apiVersion = KOTLIN_1_8 + languageVersion = KOTLIN_1_9 + apiVersion = KOTLIN_1_9 jvmTarget = JVM_17 javaParameters = true // Useful for reflection. freeCompilerArgs = ['-Xjvm-default=all-compatibility'] diff --git a/gradle.properties b/gradle.properties index b41380bb02..2a11c69c5c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,6 +5,6 @@ owasp.failOnError=false owasp.failBuildOnCVSS=11.0 compilation.allWarningsAsErrors=false test.parallel=false -kotlin_version=1.9.0 +kotlin_version=1.9.20 commons_lang3_version=3.12.0 json_api_version=1.1.4 diff --git a/node/src/main/kotlin/net/corda/node/internal/security/RPCSecurityManagerImpl.kt b/node/src/main/kotlin/net/corda/node/internal/security/RPCSecurityManagerImpl.kt index f246c02330..8b7951710c 100644 --- a/node/src/main/kotlin/net/corda/node/internal/security/RPCSecurityManagerImpl.kt +++ b/node/src/main/kotlin/net/corda/node/internal/security/RPCSecurityManagerImpl.kt @@ -10,7 +10,11 @@ import net.corda.node.services.config.AuthDataSourceType import net.corda.node.services.config.PasswordEncryption import net.corda.node.services.config.SecurityConfiguration import net.corda.nodeapi.internal.config.User -import org.apache.shiro.authc.* +import org.apache.shiro.authc.AuthenticationException +import org.apache.shiro.authc.AuthenticationInfo +import org.apache.shiro.authc.AuthenticationToken +import org.apache.shiro.authc.SimpleAuthenticationInfo +import org.apache.shiro.authc.UsernamePasswordToken import org.apache.shiro.authc.credential.PasswordMatcher import org.apache.shiro.authc.credential.SimpleCredentialsMatcher import org.apache.shiro.authz.AuthorizationInfo @@ -34,13 +38,8 @@ private typealias AuthServiceConfig = SecurityConfiguration.AuthService class RPCSecurityManagerImpl(config: AuthServiceConfig, cacheFactory: NamedCacheFactory) : RPCSecurityManager { override val id = config.id - private val manager: DefaultSecurityManager + private val manager: DefaultSecurityManager = buildImpl(config, cacheFactory) - init { - manager = buildImpl(config, cacheFactory) - } - - @Throws(FailedLoginException::class) override fun authenticate(principal: String, password: Password): AuthorizingSubject { password.use { val authToken = UsernamePasswordToken(principal, it.value) @@ -83,11 +82,12 @@ class RPCSecurityManagerImpl(config: AuthServiceConfig, cacheFactory: NamedCache } return DefaultSecurityManager(realm).also { // Setup optional cache layer if configured - it.cacheManager = config.options?.cache?.let { + it.cacheManager = config.options?.cache?.let { options -> CaffeineCacheManager( - timeToLiveSeconds = it.expireAfterSecs, - maxSize = it.maxEntries, - cacheFactory = cacheFactory) + timeToLiveSeconds = options.expireAfterSecs, + maxSize = options.maxEntries, + cacheFactory = cacheFactory + ) } } } @@ -193,8 +193,7 @@ private typealias ShiroCache = org.apache.shiro.cache.Cache /* * Adapts a [com.github.benmanes.caffeine.cache.Cache] to a [org.apache.shiro.cache.Cache] implementation. */ -private fun Cache.toShiroCache() = object : ShiroCache { - +private fun Cache.toShiroCache() = object : ShiroCache { private val impl = this@toShiroCache override operator fun get(key: K) = impl.getIfPresent(key) @@ -231,13 +230,13 @@ private class CaffeineCacheManager(val maxSize: Long, private val instances = ConcurrentHashMap>() - override fun getCache(name: String): ShiroCache { + override fun getCache(name: String): ShiroCache { val result = instances[name] ?: buildCache(name) instances.putIfAbsent(name, result) return uncheckedCast(result) } - private fun buildCache(name: String): ShiroCache { + private fun buildCache(name: String): ShiroCache { logger.info("Constructing cache '$name' with maximumSize=$maxSize, TTL=${timeToLiveSeconds}s") return cacheFactory.buildNamed("RPCSecurityManagerShiroCache_$name").toShiroCache() } @@ -245,4 +244,4 @@ private class CaffeineCacheManager(val maxSize: Long, companion object { private val logger = loggerFor() } -} \ No newline at end of file +} diff --git a/node/src/main/kotlin/net/corda/node/services/network/PersistentPartyInfoCache.kt b/node/src/main/kotlin/net/corda/node/services/network/PersistentPartyInfoCache.kt index 6cee01c45a..9958d8ee27 100644 --- a/node/src/main/kotlin/net/corda/node/services/network/PersistentPartyInfoCache.kt +++ b/node/src/main/kotlin/net/corda/node/services/network/PersistentPartyInfoCache.kt @@ -15,17 +15,17 @@ class PersistentPartyInfoCache(private val networkMapCache: PersistentNetworkMap private val database: CordaPersistence) { // probably better off using a BiMap here: https://www.baeldung.com/guava-bimap - private val cordaX500NameToPartyIdCache = NonInvalidatingCache( - cacheFactory = cacheFactory, - name = "RecoveryPartyInfoCache_byCordaX500Name") { key -> - database.transaction { queryByCordaX500Name(session, key) } - } + private val cordaX500NameToPartyIdCache = NonInvalidatingCache( + cacheFactory = cacheFactory, + name = "RecoveryPartyInfoCache_byCordaX500Name" + ) { key -> database.transaction { queryByCordaX500Name(session, key) } } - private val partyIdToCordaX500NameCache = NonInvalidatingCache( - cacheFactory = cacheFactory, - name = "RecoveryPartyInfoCache_byPartyId") { key -> - database.transaction { queryByPartyId(session, key) } - } + private val partyIdToCordaX500NameCache = NonInvalidatingCache( + cacheFactory = cacheFactory, + name = "RecoveryPartyInfoCache_byPartyId" + ) { key -> + database.transaction { queryByPartyId(session, key) } + } private lateinit var trackNetworkMapUpdates: Observable diff --git a/node/src/main/kotlin/net/corda/node/utilities/AppendOnlyPersistentMap.kt b/node/src/main/kotlin/net/corda/node/utilities/AppendOnlyPersistentMap.kt index 98de78ac0c..8b9898e5a5 100644 --- a/node/src/main/kotlin/net/corda/node/utilities/AppendOnlyPersistentMap.kt +++ b/node/src/main/kotlin/net/corda/node/utilities/AppendOnlyPersistentMap.kt @@ -1,7 +1,6 @@ package net.corda.node.utilities import com.github.benmanes.caffeine.cache.LoadingCache -import com.github.benmanes.caffeine.cache.Weigher import net.corda.core.crypto.SecureHash import net.corda.core.internal.NamedCacheFactory import net.corda.core.utilities.contextLogger @@ -10,7 +9,6 @@ import net.corda.nodeapi.internal.persistence.contextTransaction import net.corda.nodeapi.internal.persistence.currentDBSession import org.hibernate.Session import org.hibernate.internal.SessionImpl -import java.util.* import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.atomic.AtomicBoolean import java.util.concurrent.atomic.AtomicReference @@ -22,7 +20,8 @@ import java.util.stream.Stream * * This class relies heavily on the fact that compute operations in the cache are atomic for a particular key. */ -abstract class AppendOnlyPersistentMapBase( +@Suppress("TooManyFunctions") +abstract class AppendOnlyPersistentMapBase( val toPersistentEntityKey: (K) -> EK, val fromPersistentEntity: (E) -> Pair, val toPersistentEntity: (key: K, value: V) -> E, @@ -326,9 +325,9 @@ abstract class AppendOnlyPersistentMapBase( } // No one is writing, but we haven't looked in the database yet. This can only be when there are no writers. - class Unknown(private val map: AppendOnlyPersistentMapBase, - private val key: K, - private val _valueLoader: () -> T?) : Transactional() { + class Unknown(private val map: AppendOnlyPersistentMapBase, + private val key: K, + private val _valueLoader: () -> T?) : Transactional() { override val value: T get() = valueWithoutIsolationDelegate.value ?: throw NoSuchElementException("Not present") override val isPresent: Boolean @@ -351,11 +350,11 @@ abstract class AppendOnlyPersistentMapBase( // Written in a transaction (uncommitted) somewhere, but there's a small window when this might be seen after commit, // hence the committed flag. - class InFlight(private val map: AppendOnlyPersistentMapBase, - private val key: K, - val weight: Int, - private val _readerValueLoader: () -> T?, - private val _writerValueLoader: () -> T = { throw IllegalAccessException("No value loader provided") }) : Transactional() { + class InFlight(private val map: AppendOnlyPersistentMapBase, + private val key: K, + val weight: Int, + private val _readerValueLoader: () -> T?, + private val _writerValueLoader: () -> T = { throw IllegalAccessException("No value loader provided") }) : Transactional() { // A flag to indicate this has now been committed, but hasn't yet been replaced with Committed. This also // de-duplicates writes of the Committed value to the cache. @@ -363,10 +362,10 @@ abstract class AppendOnlyPersistentMapBase( // What to do if a non-writer needs to see the value and it hasn't yet been committed to the database. // Can be updated into a no-op once evaluated. - private val readerValueLoader = AtomicReference<() -> T?>(_readerValueLoader) + private val readerValueLoader = AtomicReference(_readerValueLoader) // What to do if a writer needs to see the value and it hasn't yet been committed to the database. // Can be updated into a no-op once evaluated. - private val writerValueLoader = AtomicReference<() -> T>(_writerValueLoader) + private val writerValueLoader = AtomicReference(_writerValueLoader) fun alsoWrite(_value: T) { // Make the lazy loader the writers see actually just return the value that has been set. @@ -382,11 +381,11 @@ abstract class AppendOnlyPersistentMapBase( // and then stop saying the transaction is writing the key. tx.onCommit { strongMap.cache.asMap().computeIfPresent(strongKey) { _, transactional: Transactional -> - if (transactional is Transactional.InFlight<*, T>) { + if (transactional is InFlight<*, T>) { transactional.committed.set(true) val value = transactional.peekableValue if (value != null) { - Transactional.Committed(value) + Committed(value) } else { transactional } @@ -447,7 +446,7 @@ abstract class AppendOnlyPersistentMapBase( } // Open for tests to override -open class AppendOnlyPersistentMap( +open class AppendOnlyPersistentMap( cacheFactory: NamedCacheFactory, name: String, toPersistentEntityKey: (K) -> EK, @@ -466,7 +465,7 @@ open class AppendOnlyPersistentMap( } // Same as above, but with weighted values (e.g. memory footprint sensitive). -class WeightBasedAppendOnlyPersistentMap( +class WeightBasedAppendOnlyPersistentMap( cacheFactory: NamedCacheFactory, name: String, toPersistentEntityKey: (K) -> EK, @@ -485,7 +484,7 @@ class WeightBasedAppendOnlyPersistentMap( override val cache = NonInvalidatingWeightBasedCache( cacheFactory = cacheFactory, name = name, - weigher = Weigher { key, value: Transactional -> + weigher = { key, value: Transactional -> value.shallowSize + if (value is Transactional.InFlight<*, *>) { value.weight * 2 } else { diff --git a/node/src/main/kotlin/net/corda/node/utilities/NonInvalidatingCache.kt b/node/src/main/kotlin/net/corda/node/utilities/NonInvalidatingCache.kt index 42b7738a1d..b8b02af6f0 100644 --- a/node/src/main/kotlin/net/corda/node/utilities/NonInvalidatingCache.kt +++ b/node/src/main/kotlin/net/corda/node/utilities/NonInvalidatingCache.kt @@ -6,21 +6,17 @@ import com.github.benmanes.caffeine.cache.LoadingCache import com.github.benmanes.caffeine.cache.Weigher import net.corda.core.internal.NamedCacheFactory -class NonInvalidatingCache private constructor( - val cache: LoadingCache -) : LoadingCache by cache { - - constructor(cacheFactory: NamedCacheFactory, name: String, loadFunction: (K) -> V) : - this(buildCache(cacheFactory, name, loadFunction)) +class NonInvalidatingCache private constructor(val cache: LoadingCache) : LoadingCache by cache { + constructor(cacheFactory: NamedCacheFactory, name: String, loadFunction: (K) -> V?) : this(buildCache(cacheFactory, name, loadFunction)) private companion object { - private fun buildCache(cacheFactory: NamedCacheFactory, name: String, loadFunction: (K) -> V): LoadingCache { + private fun buildCache(cacheFactory: NamedCacheFactory, name: String, loadFunction: (K) -> V?): LoadingCache { return cacheFactory.buildNamed(name, NonInvalidatingCacheLoader(loadFunction)) } } // TODO look into overriding loadAll() if we ever use it - class NonInvalidatingCacheLoader(val loadFunction: (K) -> V) : CacheLoader { + class NonInvalidatingCacheLoader(val loadFunction: (K) -> V?) : CacheLoader { override fun reload(key: K, oldValue: V): V { throw IllegalStateException("Non invalidating cache refreshed") } @@ -29,16 +25,17 @@ class NonInvalidatingCache private constructor( } } -class NonInvalidatingWeightBasedCache private constructor( - val cache: LoadingCache -) : LoadingCache by cache { - constructor (cacheFactory: NamedCacheFactory, name: String, weigher: Weigher, loadFunction: (K) -> V) : +class NonInvalidatingWeightBasedCache private constructor(val cache: LoadingCache) : LoadingCache by cache { + constructor(cacheFactory: NamedCacheFactory, name: String, weigher: Weigher, loadFunction: (K) -> V?) : this(buildCache(cacheFactory, name, weigher, loadFunction)) private companion object { - private fun buildCache(cacheFactory: NamedCacheFactory, name: String, weigher: Weigher, loadFunction: (K) -> V): LoadingCache { + private fun buildCache(cacheFactory: NamedCacheFactory, + name: String, + weigher: Weigher, + loadFunction: (K) -> V?): LoadingCache { val builder = Caffeine.newBuilder().weigher(weigher) return cacheFactory.buildNamed(builder, name, NonInvalidatingCache.NonInvalidatingCacheLoader(loadFunction)) } } -} \ No newline at end of file +} diff --git a/node/src/main/kotlin/net/corda/node/utilities/NonInvalidatingUnboundCache.kt b/node/src/main/kotlin/net/corda/node/utilities/NonInvalidatingUnboundCache.kt index 634189a7b7..6c97ca4df4 100644 --- a/node/src/main/kotlin/net/corda/node/utilities/NonInvalidatingUnboundCache.kt +++ b/node/src/main/kotlin/net/corda/node/utilities/NonInvalidatingUnboundCache.kt @@ -7,17 +7,20 @@ import com.github.benmanes.caffeine.cache.LoadingCache import com.github.benmanes.caffeine.cache.RemovalListener import net.corda.core.internal.NamedCacheFactory -class NonInvalidatingUnboundCache private constructor( - val cache: LoadingCache -) : LoadingCache by cache { - - constructor(name: String, cacheFactory: NamedCacheFactory, loadFunction: (K) -> V, removalListener: RemovalListener = RemovalListener { _, _, _ -> }, +class NonInvalidatingUnboundCache private constructor(val cache: LoadingCache) : LoadingCache by cache { + constructor(name: String, + cacheFactory: NamedCacheFactory, + loadFunction: (K) -> V?, + removalListener: RemovalListener = RemovalListener { _, _, _ -> }, keysToPreload: () -> Iterable = { emptyList() }) : this(buildCache(name, cacheFactory, loadFunction, removalListener, keysToPreload)) private companion object { - private fun buildCache(name: String, cacheFactory: NamedCacheFactory, loadFunction: (K) -> V, removalListener: RemovalListener, - keysToPreload: () -> Iterable): LoadingCache { + private fun buildCache(name: String, + cacheFactory: NamedCacheFactory, + loadFunction: (K) -> V?, + removalListener: RemovalListener, + keysToPreload: () -> Iterable): LoadingCache { val builder = Caffeine.newBuilder().removalListener(removalListener).executor(SameThreadExecutor.getExecutor()) return cacheFactory.buildNamed(builder, name, NonInvalidatingCacheLoader(loadFunction)).apply { getAll(keysToPreload()) @@ -26,11 +29,11 @@ class NonInvalidatingUnboundCache private constructor( } // TODO look into overriding loadAll() if we ever use it - private class NonInvalidatingCacheLoader(val loadFunction: (K) -> V) : CacheLoader { + private class NonInvalidatingCacheLoader(val loadFunction: (K) -> V?) : CacheLoader { override fun reload(key: K, oldValue: V): V { throw IllegalStateException("Non invalidating cache refreshed") } override fun load(key: K) = loadFunction(key) } -} \ No newline at end of file +} diff --git a/node/src/main/kotlin/net/corda/node/utilities/PersistentMap.kt b/node/src/main/kotlin/net/corda/node/utilities/PersistentMap.kt index a006ee9883..3fcc754ff5 100644 --- a/node/src/main/kotlin/net/corda/node/utilities/PersistentMap.kt +++ b/node/src/main/kotlin/net/corda/node/utilities/PersistentMap.kt @@ -10,7 +10,7 @@ import java.util.* /** * Implements an unbound caching layer on top of a table accessed via Hibernate mapping. */ -class PersistentMap( +class PersistentMap( name: String, val toPersistentEntityKey: (K) -> EK, val fromPersistentEntity: (E) -> Pair, @@ -126,7 +126,7 @@ class PersistentMap( return result } - private class NotReallyMutableEntry(key: K, value: V) : AbstractMap.SimpleImmutableEntry(key, value), MutableMap.MutableEntry { + private class NotReallyMutableEntry(key: K, value: V) : SimpleImmutableEntry(key, value), MutableMap.MutableEntry { override fun setValue(newValue: V): V { throw UnsupportedOperationException("Not really mutable. Implement if really required.") } diff --git a/node/src/test/kotlin/net/corda/node/services/statemachine/FlowFrameworkTests.kt b/node/src/test/kotlin/net/corda/node/services/statemachine/FlowFrameworkTests.kt index 1bfc441987..a808be5466 100644 --- a/node/src/test/kotlin/net/corda/node/services/statemachine/FlowFrameworkTests.kt +++ b/node/src/test/kotlin/net/corda/node/services/statemachine/FlowFrameworkTests.kt @@ -820,8 +820,9 @@ class FlowFrameworkTests { } // When ported to ENT use the existing API there to properly retry the flow + @Suppress("FunctionNaming") // For some reason this test produces invalid class names if the function name contains spaces @Test(timeout=300_000) - fun `Hospitalized flow, resets to 'RUNNABLE' and clears exception when retried`() { + fun Hospitalized_flow_resets_to_RUNNABLE_and_clears_exception_when_retried() { var firstRun = true var counter = 0 val waitUntilHospitalizedTwice = Semaphore(-1)