diff --git a/core/src/main/kotlin/net/corda/core/serialization/internal/AttachmentsClassLoader.kt b/core/src/main/kotlin/net/corda/core/serialization/internal/AttachmentsClassLoader.kt index 0a287a7f7d..6c8279a1e8 100644 --- a/core/src/main/kotlin/net/corda/core/serialization/internal/AttachmentsClassLoader.kt +++ b/core/src/main/kotlin/net/corda/core/serialization/internal/AttachmentsClassLoader.kt @@ -32,6 +32,7 @@ import net.corda.core.serialization.internal.AttachmentURLStreamHandlerFactory.t import net.corda.core.serialization.withWhitelist import net.corda.core.utilities.contextLogger import net.corda.core.utilities.debug +import net.corda.core.utilities.loggerFor import java.io.IOException import java.io.InputStream import java.lang.ref.WeakReference @@ -470,7 +471,18 @@ interface AttachmentsClassLoaderCache { @DeleteForDJVM class AttachmentsClassLoaderCacheImpl(cacheFactory: NamedCacheFactory) : SingletonSerializeAsToken(), AttachmentsClassLoaderCache { - private val cache: Cache = cacheFactory.buildNamed(Caffeine.newBuilder(), "AttachmentsClassLoader_cache") + private val cache: Cache = cacheFactory.buildNamed( + // Close deserialization classloaders when we evict them + // to release any resources they may be holding. + @Suppress("TooGenericExceptionCaught") + Caffeine.newBuilder().removalListener { key, context, _ -> + try { + (context?.deserializationClassLoader as? AutoCloseable)?.close() + } catch (e: Exception) { + loggerFor().warn("Error destroying serialization context for $key", e) + } + }, "AttachmentsClassLoader_cache" + ) override fun computeIfAbsent(key: AttachmentsClassLoaderKey, mappingFunction: Function): SerializationContext { return cache.get(key, mappingFunction) ?: throw NullPointerException("null returned from cache mapping function") diff --git a/core/src/main/kotlin/net/corda/core/transactions/TransactionBuilder.kt b/core/src/main/kotlin/net/corda/core/transactions/TransactionBuilder.kt index 71fb6c728b..c05f378223 100644 --- a/core/src/main/kotlin/net/corda/core/transactions/TransactionBuilder.kt +++ b/core/src/main/kotlin/net/corda/core/transactions/TransactionBuilder.kt @@ -741,9 +741,11 @@ open class TransactionBuilder( addReferenceState(resolvedStateAndRef.referenced()) } } else { - log.warn("WARNING: You must pass in a ServiceHub reference to TransactionBuilder to resolve " + - "state pointers outside of flows. If you are writing a unit test then pass in a " + - "MockServices instance.") + if (nextStatePointer.isResolved) { + log.warn("WARNING: You must pass in a ServiceHub reference to TransactionBuilder to resolve " + + "state pointers outside of flows. If you are writing a unit test then pass in a " + + "MockServices instance.") + } return } } diff --git a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt index 2f2479c139..3d83201494 100644 --- a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt +++ b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt @@ -171,7 +171,6 @@ import org.jolokia.jvmagent.JolokiaServerConfig import org.slf4j.Logger import rx.Scheduler import java.lang.reflect.InvocationTargetException -import java.net.URLConnection import java.sql.Connection import java.sql.Savepoint import java.time.Clock @@ -237,7 +236,6 @@ abstract class AbstractNode(val configuration: NodeConfiguration, } quasarExcludePackages(configuration) - disableURLConnectionCache() if (allowHibernateToManageAppSchema && !configuration.devMode) { throw ConfigurationException("Hibernate can only be used to manage app schema in development while using dev mode. " + @@ -424,13 +422,6 @@ abstract class AbstractNode(val configuration: NodeConfiguration, } } - private fun disableURLConnectionCache() { - object : URLConnection(null) { - override fun connect() { - } - }.defaultUseCaches = false - } - private fun quasarExcludePackages(nodeConfiguration: NodeConfiguration) { val quasarInstrumentor = Retransform.getInstrumentor() diff --git a/node/src/main/kotlin/net/corda/node/utilities/NodeNamedCache.kt b/node/src/main/kotlin/net/corda/node/utilities/NodeNamedCache.kt index e43b2ab7ee..9c2e2eaf93 100644 --- a/node/src/main/kotlin/net/corda/node/utilities/NodeNamedCache.kt +++ b/node/src/main/kotlin/net/corda/node/utilities/NodeNamedCache.kt @@ -33,7 +33,8 @@ open class DefaultNamedCacheFactory protected constructor(private val metricRegi override fun bindWithMetrics(metricRegistry: MetricRegistry): BindableNamedCacheFactory = DefaultNamedCacheFactory(metricRegistry, this.nodeConfiguration) override fun bindWithConfig(nodeConfiguration: NodeConfiguration): BindableNamedCacheFactory = DefaultNamedCacheFactory(this.metricRegistry, nodeConfiguration) - open protected fun configuredForNamed(caffeine: Caffeine, name: String): Caffeine { + @Suppress("ComplexMethod") + protected open fun configuredForNamed(caffeine: Caffeine, name: String): Caffeine { return with(nodeConfiguration!!) { when { name.startsWith("RPCSecurityManagerShiroCache_") -> with(security?.authService?.options?.cache!!) { caffeine.maximumSize(maxEntries).expireAfterWrite(expireAfterSecs, TimeUnit.SECONDS) } @@ -84,7 +85,7 @@ open class DefaultNamedCacheFactory protected constructor(private val metricRegi return configuredForNamed(caffeine, name).build(loader) } - open protected val defaultCacheSize = 1024L + protected open val defaultCacheSize = 1024L private val defaultAttachmentsClassLoaderCacheSize = defaultCacheSize / CACHE_SIZE_DENOMINATOR } -private const val CACHE_SIZE_DENOMINATOR = 4L \ No newline at end of file +private const val CACHE_SIZE_DENOMINATOR = 4L