diff --git a/build.gradle b/build.gradle index cc280d8be6..8b3c42d4e4 100644 --- a/build.gradle +++ b/build.gradle @@ -58,6 +58,7 @@ buildscript { ext.log4j_version = '2.9.1' ext.bouncycastle_version = constants.getProperty("bouncycastleVersion") ext.guava_version = constants.getProperty("guavaVersion") + ext.caffeine_version = constants.getProperty("caffeineVersion") ext.okhttp_version = '3.5.0' ext.netty_version = '4.1.9.Final' ext.typesafe_config_version = constants.getProperty("typesafeConfigVersion") diff --git a/client/jfx/src/main/kotlin/net/corda/client/jfx/model/NetworkIdentityModel.kt b/client/jfx/src/main/kotlin/net/corda/client/jfx/model/NetworkIdentityModel.kt index 07a77880a8..85c1c7a97c 100644 --- a/client/jfx/src/main/kotlin/net/corda/client/jfx/model/NetworkIdentityModel.kt +++ b/client/jfx/src/main/kotlin/net/corda/client/jfx/model/NetworkIdentityModel.kt @@ -10,8 +10,7 @@ package net.corda.client.jfx.model -import com.google.common.cache.CacheBuilder -import com.google.common.cache.CacheLoader +import com.github.benmanes.caffeine.cache.Caffeine import javafx.beans.value.ObservableValue import javafx.collections.FXCollections import javafx.collections.ObservableList @@ -42,8 +41,8 @@ class NetworkIdentityModel { private val rpcProxy by observableValue(NodeMonitorModel::proxyObservable) - private val identityCache = CacheBuilder.newBuilder() - .build>(CacheLoader.from { publicKey -> + private val identityCache = Caffeine.newBuilder() + .build>({ publicKey -> publicKey?.let { rpcProxy.map { it?.nodeInfoFromParty(AnonymousParty(publicKey)) } } }) val notaries = ChosenList(rpcProxy.map { FXCollections.observableList(it?.notaryIdentities() ?: emptyList()) }) @@ -52,5 +51,5 @@ class NetworkIdentityModel { .filtered { it.legalIdentities.all { it !in notaries } } val myIdentity = rpcProxy.map { it?.nodeInfo()?.legalIdentitiesAndCerts?.first()?.party } - fun partyFromPublicKey(publicKey: PublicKey): ObservableValue = identityCache[publicKey] + fun partyFromPublicKey(publicKey: PublicKey): ObservableValue = identityCache[publicKey]!! } diff --git a/client/rpc/build.gradle b/client/rpc/build.gradle index 53d2cb9f4e..d7220c3f24 100644 --- a/client/rpc/build.gradle +++ b/client/rpc/build.gradle @@ -81,6 +81,9 @@ dependencies { compile project(':core') compile project(':node-api') + // For caches rather than guava + compile "com.github.ben-manes.caffeine:caffeine:$caffeine_version" + // Unit testing helpers. testCompile "org.jetbrains.kotlin:kotlin-test:$kotlin_version" testCompile "junit:junit:$junit_version" diff --git a/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/RPCClient.kt b/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/RPCClient.kt index 57edb43259..a085144c80 100644 --- a/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/RPCClient.kt +++ b/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/RPCClient.kt @@ -54,12 +54,6 @@ data class RPCClientConfiguration( val reapInterval: Duration, /** The number of threads to use for observations (for executing [Observable.onNext]) */ val observationExecutorPoolSize: Int, - /** - * Determines the concurrency level of the Observable Cache. This is exposed because it implicitly determines - * the limit on the number of leaked observables reaped because of garbage collection per reaping. - * See the implementation of [com.google.common.cache.LocalCache] for details. - */ - val cacheConcurrencyLevel: Int, /** The retry interval of artemis connections in milliseconds */ val connectionRetryInterval: Duration, /** The retry interval multiplier for exponential backoff */ @@ -81,7 +75,6 @@ data class RPCClientConfiguration( trackRpcCallSites = false, reapInterval = 1.seconds, observationExecutorPoolSize = 4, - cacheConcurrencyLevel = 8, connectionRetryInterval = 5.seconds, connectionRetryIntervalMultiplier = 1.5, connectionMaxRetryInterval = 3.minutes, diff --git a/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/RPCClientProxyHandler.kt b/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/RPCClientProxyHandler.kt index 8c15f995a3..a08a94162f 100644 --- a/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/RPCClientProxyHandler.kt +++ b/client/rpc/src/main/kotlin/net/corda/client/rpc/internal/RPCClientProxyHandler.kt @@ -10,14 +10,15 @@ package net.corda.client.rpc.internal +import co.paralleluniverse.common.util.SameThreadExecutor import com.esotericsoftware.kryo.Kryo import com.esotericsoftware.kryo.Serializer import com.esotericsoftware.kryo.io.Input import com.esotericsoftware.kryo.io.Output -import com.google.common.cache.Cache -import com.google.common.cache.CacheBuilder -import com.google.common.cache.RemovalCause -import com.google.common.cache.RemovalListener +import com.github.benmanes.caffeine.cache.Cache +import com.github.benmanes.caffeine.cache.Caffeine +import com.github.benmanes.caffeine.cache.RemovalCause +import com.github.benmanes.caffeine.cache.RemovalListener import com.google.common.util.concurrent.SettableFuture import com.google.common.util.concurrent.ThreadFactoryBuilder import net.corda.client.rpc.RPCException @@ -152,10 +153,10 @@ class RPCClientProxyHandler( private val serializationContextWithObservableContext = RpcClientObservableSerializer.createContext(serializationContext, observableContext) private fun createRpcObservableMap(): RpcObservableMap { - val onObservableRemove = RemovalListener>> { - val observableId = it.key!! + val onObservableRemove = RemovalListener>> { key, value, cause -> + val observableId = key!! val rpcCallSite = callSiteMap?.remove(observableId) - if (it.cause == RemovalCause.COLLECTED) { + if (cause == RemovalCause.COLLECTED) { log.warn(listOf( "A hot observable returned from an RPC was never subscribed to.", "This wastes server-side resources because it was queueing observations for retrieval.", @@ -166,10 +167,9 @@ class RPCClientProxyHandler( } observablesToReap.locked { observables.add(observableId) } } - return CacheBuilder.newBuilder(). + return Caffeine.newBuilder(). weakValues(). - removalListener(onObservableRemove). - concurrencyLevel(rpcConfiguration.cacheConcurrencyLevel). + removalListener(onObservableRemove).executor(SameThreadExecutor.getExecutor()). build() } diff --git a/client/rpc/src/test/kotlin/net/corda/client/rpc/RPCConcurrencyTests.kt b/client/rpc/src/test/kotlin/net/corda/client/rpc/RPCConcurrencyTests.kt index 544343b18c..eb5247b055 100644 --- a/client/rpc/src/test/kotlin/net/corda/client/rpc/RPCConcurrencyTests.kt +++ b/client/rpc/src/test/kotlin/net/corda/client/rpc/RPCConcurrencyTests.kt @@ -101,8 +101,7 @@ class RPCConcurrencyTests : AbstractRPCTest() { return testProxy( TestOpsImpl(pool), clientConfiguration = RPCClientConfiguration.default.copy( - reapInterval = 100.millis, - cacheConcurrencyLevel = 16 + reapInterval = 100.millis ), serverConfiguration = RPCServerConfiguration.default.copy( rpcThreadPoolSize = 4 diff --git a/client/rpc/src/test/kotlin/net/corda/client/rpc/RPCPerformanceTests.kt b/client/rpc/src/test/kotlin/net/corda/client/rpc/RPCPerformanceTests.kt index 11370065b8..3748b6940c 100644 --- a/client/rpc/src/test/kotlin/net/corda/client/rpc/RPCPerformanceTests.kt +++ b/client/rpc/src/test/kotlin/net/corda/client/rpc/RPCPerformanceTests.kt @@ -98,7 +98,6 @@ class RPCPerformanceTests : AbstractRPCTest() { rpcDriver { val proxy = testProxy( RPCClientConfiguration.default.copy( - cacheConcurrencyLevel = 16, observationExecutorPoolSize = 2 ), RPCServerConfiguration.default.copy( @@ -138,8 +137,7 @@ class RPCPerformanceTests : AbstractRPCTest() { val metricRegistry = startReporter(shutdownManager) val proxy = testProxy( RPCClientConfiguration.default.copy( - reapInterval = 1.seconds, - cacheConcurrencyLevel = 16 + reapInterval = 1.seconds ), RPCServerConfiguration.default.copy( rpcThreadPoolSize = 8 diff --git a/constants.properties b/constants.properties index cd17d3be3f..47cfc7da1d 100644 --- a/constants.properties +++ b/constants.properties @@ -17,3 +17,4 @@ typesafeConfigVersion=1.3.1 jsr305Version=3.0.2 artifactoryPluginVersion=4.4.18 snakeYamlVersion=1.19 +caffeineVersion=2.6.2 diff --git a/core/src/main/kotlin/net/corda/core/transactions/LedgerTransaction.kt b/core/src/main/kotlin/net/corda/core/transactions/LedgerTransaction.kt index 00a844f0b8..030b1daae3 100644 --- a/core/src/main/kotlin/net/corda/core/transactions/LedgerTransaction.kt +++ b/core/src/main/kotlin/net/corda/core/transactions/LedgerTransaction.kt @@ -62,13 +62,19 @@ data class LedgerTransaction @JvmOverloads constructor( private companion object { @JvmStatic - private fun createContractFor(className: ContractClassName): Try { - return Try.on { this::class.java.classLoader.loadClass(className).asSubclass(Contract::class.java).getConstructor().newInstance() } + private fun createContractFor(className: ContractClassName, classLoader: ClassLoader?): Try { + return Try.on { + (classLoader ?: this::class.java.classLoader) + .loadClass(className) + .asSubclass(Contract::class.java) + .getConstructor() + .newInstance() + } } } - private val contracts: Map> = (inputs.map { it.state.contract } + outputs.map { it.contract }) - .toSet().map { it to createContractFor(it) }.toMap() + private val contracts: Map> = (inputs.map { it.state } + outputs) + .map { it.contract to createContractFor(it.contract, it.data::class.java.classLoader) }.toMap() val inputStates: List get() = inputs.map { it.state.data } diff --git a/docs/source/getting-set-up.rst b/docs/source/getting-set-up.rst index aef199da90..b8cd50727a 100644 --- a/docs/source/getting-set-up.rst +++ b/docs/source/getting-set-up.rst @@ -107,11 +107,12 @@ Mac Java ^^^^ -1. Open "System Preferences > Java" -2. In the Java Control Panel, if an update is available, click "Update Now" -3. In the "Software Update" window, click "Install Update". If required, enter your password and click "Install Helper" when prompted -4. Wait for a pop-up window indicating that you have successfully installed the update, and click "Close" -5. Open a new terminal and type ``java -version`` to test that Java is installed correctly +1. Visit http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html +2. Scroll down to "Java SE Development Kit 8uXXX" (where "XXX" is the latest minor version number) +3. Toggle "Accept License Agreement" +4. Click the download link for jdk-8uXXX-macosx-x64.dmg (where "XXX" is the latest minor version number) +5. Download and run the executable to install Java (use the default settings) +6. Open a new terminal window and run ``java -version`` to test that Java is installed correctly IntelliJ ^^^^^^^^ @@ -173,4 +174,4 @@ By then, you'll be ready to start writing your own CorDapps. Learn how to do thi :doc:`flow cookbook ` and the `samples `_ along the way. If you encounter any issues, please see the :doc:`troubleshooting` page, or get in touch with us on the -`forums `_ or via `slack `_. \ No newline at end of file +`forums `_ or via `slack `_. diff --git a/finance/src/main/kotlin/net/corda/finance/contracts/asset/cash/selection/AbstractCashSelection.kt b/finance/src/main/kotlin/net/corda/finance/contracts/asset/cash/selection/AbstractCashSelection.kt index 7e3be78c57..255ab6610f 100644 --- a/finance/src/main/kotlin/net/corda/finance/contracts/asset/cash/selection/AbstractCashSelection.kt +++ b/finance/src/main/kotlin/net/corda/finance/contracts/asset/cash/selection/AbstractCashSelection.kt @@ -46,7 +46,7 @@ abstract class AbstractCashSelection(private val maxRetries : Int = 8, private v fun getInstance(metadata: () -> java.sql.DatabaseMetaData): AbstractCashSelection { return instance.get() ?: { val _metadata = metadata() - val cashSelectionAlgos = ServiceLoader.load(AbstractCashSelection::class.java).toList() + val cashSelectionAlgos = ServiceLoader.load(AbstractCashSelection::class.java, this::class.java.classLoader).toList() val cashSelectionAlgo = cashSelectionAlgos.firstOrNull { it.isCompatible(_metadata) } cashSelectionAlgo?.let { instance.set(cashSelectionAlgo) diff --git a/node-api/build.gradle b/node-api/build.gradle index 2309959eb3..02854e236f 100644 --- a/node-api/build.gradle +++ b/node-api/build.gradle @@ -63,6 +63,9 @@ dependencies { compile "com.fasterxml.jackson.core:jackson-databind:$jackson_version" + // For caches rather than guava + compile "com.github.ben-manes.caffeine:caffeine:$caffeine_version" + // Unit testing helpers. testCompile "junit:junit:$junit_version" testCompile "org.assertj:assertj-core:$assertj_version" diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/DeduplicationChecker.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/DeduplicationChecker.kt index de6ff72547..154a50912c 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/DeduplicationChecker.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/DeduplicationChecker.kt @@ -10,8 +10,8 @@ package net.corda.nodeapi.internal -import com.google.common.cache.CacheBuilder -import com.google.common.cache.CacheLoader +import com.github.benmanes.caffeine.cache.CacheLoader +import com.github.benmanes.caffeine.cache.Caffeine import java.time.Duration import java.util.concurrent.TimeUnit import java.util.concurrent.atomic.AtomicLong @@ -21,11 +21,11 @@ import java.util.concurrent.atomic.AtomicLong */ class DeduplicationChecker(cacheExpiry: Duration) { // dedupe identity -> watermark cache - private val watermarkCache = CacheBuilder.newBuilder() + private val watermarkCache = Caffeine.newBuilder() .expireAfterAccess(cacheExpiry.toNanos(), TimeUnit.NANOSECONDS) .build(WatermarkCacheLoader) - private object WatermarkCacheLoader : CacheLoader() { + private object WatermarkCacheLoader : CacheLoader { override fun load(key: Any) = AtomicLong(-1) } @@ -35,6 +35,7 @@ class DeduplicationChecker(cacheExpiry: Duration) { * @return true if the message is unique, false if it's a duplicate. */ fun checkDuplicateMessageId(identity: Any, sequenceNumber: Long): Boolean { - return watermarkCache[identity].getAndUpdate { maxOf(sequenceNumber, it) } >= sequenceNumber + return watermarkCache[identity]!!.getAndUpdate { maxOf(sequenceNumber, it) } >= sequenceNumber } } + diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/CordaPersistence.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/CordaPersistence.kt index d084ba6182..72def0bee9 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/CordaPersistence.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/CordaPersistence.kt @@ -64,7 +64,8 @@ class CordaPersistence( databaseConfig: DatabaseConfig, schemas: Set, val jdbcUrl: String, - attributeConverters: Collection> = emptySet() + attributeConverters: Collection> = emptySet(), + val cordappClassLoader: ClassLoader? = null ) : Closeable { companion object { private val log = contextLogger() @@ -74,7 +75,7 @@ class CordaPersistence( val hibernateConfig: HibernateConfiguration by lazy { transaction { - HibernateConfiguration(schemas, databaseConfig, attributeConverters, jdbcUrl) + HibernateConfiguration(schemas, databaseConfig, attributeConverters, jdbcUrl, cordappClassLoader) } } val entityManagerFactory get() = hibernateConfig.sessionFactoryForRegisteredSchemas diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/HibernateConfiguration.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/HibernateConfiguration.kt index 12ddea09d1..6100ffd487 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/HibernateConfiguration.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/HibernateConfiguration.kt @@ -19,6 +19,8 @@ import org.hibernate.boot.Metadata import org.hibernate.boot.MetadataBuilder import org.hibernate.boot.MetadataSources import org.hibernate.boot.registry.BootstrapServiceRegistryBuilder +import org.hibernate.boot.registry.classloading.internal.ClassLoaderServiceImpl +import org.hibernate.boot.registry.classloading.spi.ClassLoaderService import org.hibernate.cfg.Configuration import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider import org.hibernate.service.UnknownUnwrapTypeException @@ -36,7 +38,8 @@ class HibernateConfiguration( schemas: Set, private val databaseConfig: DatabaseConfig, private val attributeConverters: Collection>, - private val jdbcUrl: String + private val jdbcUrl: String, + val cordappClassLoader: ClassLoader? = null ) { companion object { private val logger = contextLogger() @@ -93,7 +96,7 @@ class HibernateConfiguration( schema.mappedTypes.forEach { config.addAnnotatedClass(it) } } - val sessionFactory = buildSessionFactory(config, metadataSources) + val sessionFactory = buildSessionFactory(config, metadataSources, cordappClassLoader) logger.info("Created session factory for schemas: $schemas") // export Hibernate JMX statistics @@ -119,11 +122,17 @@ class HibernateConfiguration( } } - private fun buildSessionFactory(config: Configuration, metadataSources: MetadataSources): SessionFactory { + private fun buildSessionFactory(config: Configuration, metadataSources: MetadataSources, cordappClassLoader: ClassLoader?): SessionFactory { config.standardServiceRegistryBuilder.applySettings(config.properties) + + if (cordappClassLoader != null) { + config.standardServiceRegistryBuilder.addService( + ClassLoaderService::class.java, + ClassLoaderServiceImpl(cordappClassLoader)) + } + val metadataBuilder = metadataSources.getMetadataBuilder(config.standardServiceRegistryBuilder.build()) val metadata = buildHibernateMetadata(metadataBuilder, jdbcUrl, attributeConverters) - return metadata.sessionFactoryBuilder.run { allowOutOfTransactionUpdateOperations(true) applySecondLevelCacheSupport(false) diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/SerializationScheme.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/SerializationScheme.kt index f102e3933e..1b5d3ed93d 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/SerializationScheme.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/SerializationScheme.kt @@ -10,8 +10,8 @@ package net.corda.nodeapi.internal.serialization -import com.google.common.cache.Cache -import com.google.common.cache.CacheBuilder +import com.github.benmanes.caffeine.cache.Cache +import com.github.benmanes.caffeine.cache.Caffeine import net.corda.core.contracts.Attachment import net.corda.core.crypto.SecureHash import net.corda.core.internal.copyBytes @@ -40,7 +40,7 @@ data class SerializationContextImpl @JvmOverloads constructor(override val prefe override val useCase: SerializationContext.UseCase, override val encoding: SerializationEncoding?, override val encodingWhitelist: EncodingWhitelist = NullEncodingWhitelist) : SerializationContext { - private val cache: Cache, AttachmentsClassLoader> = CacheBuilder.newBuilder().weakValues().maximumSize(1024).build() + private val cache: Cache, AttachmentsClassLoader> = Caffeine.newBuilder().weakValues().maximumSize(1024).build() /** * {@inheritDoc} @@ -59,7 +59,7 @@ data class SerializationContextImpl @JvmOverloads constructor(override val prefe } missing.isNotEmpty() && throw MissingAttachmentsException(missing) AttachmentsClassLoader(attachments, parent = deserializationClassLoader) - }) + }!!) } catch (e: ExecutionException) { // Caught from within the cache get, so unwrap. throw e.cause!! diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/MapSerializer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/MapSerializer.kt index 221dd60e3a..8eb5c4332b 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/MapSerializer.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/amqp/MapSerializer.kt @@ -28,7 +28,8 @@ private typealias MapCreationFunction = (Map<*, *>) -> Map<*, *> * Serialization / deserialization of certain supported [Map] types. */ class MapSerializer(private val declaredType: ParameterizedType, factory: SerializerFactory) : AMQPSerializer { - override val type: Type = declaredType as? DeserializedParameterizedType ?: DeserializedParameterizedType.make(SerializerFactory.nameForType(declaredType)) + override val type: Type = (declaredType as? DeserializedParameterizedType) ?: + DeserializedParameterizedType.make(SerializerFactory.nameForType(declaredType), factory.classloader) override val typeDescriptor: Symbol = Symbol.valueOf( "$DESCRIPTOR_DOMAIN:${factory.fingerPrinter.fingerprint(type)}") diff --git a/node/build.gradle b/node/build.gradle index f8f45931c5..011bfb8d9f 100644 --- a/node/build.gradle +++ b/node/build.gradle @@ -91,6 +91,9 @@ dependencies { compile "com.google.guava:guava:$guava_version" + // For caches rather than guava + compile "com.github.ben-manes.caffeine:caffeine:$caffeine_version" + // JOpt: for command line flags. compile "net.sf.jopt-simple:jopt-simple:$jopt_simple_version" diff --git a/node/src/main/java/CordaCaplet.java b/node/src/main/java/CordaCaplet.java index 3d73ac365c..86dffc2f6a 100644 --- a/node/src/main/java/CordaCaplet.java +++ b/node/src/main/java/CordaCaplet.java @@ -91,9 +91,8 @@ public class CordaCaplet extends Capsule { T cp = super.attribute(attr); (new File(baseDir, "cordapps")).mkdir(); - augmentClasspath((List) cp, new File(baseDir, "cordapps")); - augmentClasspath((List) cp, new File(baseDir, "plugins")); // Add additional directories of JARs to the classpath (at the end). e.g. for JDBC drivers + augmentClasspath((List) cp, new File(baseDir, "drivers")); try { List jarDirs = nodeConfig.getStringList("jarDirs"); log(LOG_VERBOSE, "Configured JAR directories = " + jarDirs); 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 dfecf078e6..81e4015640 100644 --- a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt +++ b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt @@ -657,7 +657,7 @@ abstract class AbstractNode(val configuration: NodeConfiguration, val props = configuration.dataSourceProperties if (props.isEmpty()) throw DatabaseConfigurationException("There must be a database configured.") - val database = configureDatabase(props, configuration.database, identityService, schemaService) + val database = configureDatabase(props, configuration.database, identityService, schemaService, cordappLoader.appClassLoader) // Now log the vendor string as this will also cause a connection to be tested eagerly. logVendorString(database, log) runOnStop += database::close @@ -897,7 +897,8 @@ internal class NetworkMapCacheEmptyException : Exception() fun configureDatabase(hikariProperties: Properties, databaseConfig: DatabaseConfig, identityService: IdentityService, - schemaService: SchemaService = NodeSchemaService()): CordaPersistence { + schemaService: SchemaService = NodeSchemaService(), + cordappClassLoader: ClassLoader? = null): CordaPersistence { // Register the AbstractPartyDescriptor so Hibernate doesn't warn when encountering AbstractParty. Unfortunately // Hibernate warns about not being able to find a descriptor if we don't provide one, but won't use it by default // so we end up providing both descriptor and converter. We should re-examine this in later versions to see if @@ -905,10 +906,12 @@ fun configureDatabase(hikariProperties: Properties, JavaTypeDescriptorRegistry.INSTANCE.addDescriptor(AbstractPartyDescriptor(identityService)) val dataSource = DataSourceFactory.createDataSource(hikariProperties) val attributeConverters = listOf(AbstractPartyToX500NameAsStringConverter(identityService)) - val jdbcUrl = hikariProperties.getProperty("dataSource.url", "") - - SchemaMigration(schemaService.schemaOptions.keys, dataSource, !isH2Database(jdbcUrl), databaseConfig).nodeStartup() - - return CordaPersistence(dataSource, databaseConfig, schemaService.schemaOptions.keys, jdbcUrl, attributeConverters) + SchemaMigration( + schemaService.schemaOptions.keys, + dataSource, + !isH2Database(jdbcUrl), + databaseConfig, + cordappClassLoader ?: Thread.currentThread().contextClassLoader).nodeStartup() + return CordaPersistence(dataSource, databaseConfig, schemaService.schemaOptions.keys, jdbcUrl, attributeConverters, cordappClassLoader) } diff --git a/node/src/main/kotlin/net/corda/node/internal/cordapp/CordappLoader.kt b/node/src/main/kotlin/net/corda/node/internal/cordapp/CordappLoader.kt index 5164ccd642..3eef29d5f6 100644 --- a/node/src/main/kotlin/net/corda/node/internal/cordapp/CordappLoader.kt +++ b/node/src/main/kotlin/net/corda/node/internal/cordapp/CordappLoader.kt @@ -53,7 +53,6 @@ import kotlin.streams.toList */ class CordappLoader private constructor(private val cordappJarPaths: List) { val cordapps: List by lazy { loadCordapps() + coreCordapp } - val appClassLoader: ClassLoader = URLClassLoader(cordappJarPaths.stream().map { it.url }.toTypedArray(), javaClass.classLoader) init { 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 6b0bcba713..b0db5475d7 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,8 +10,9 @@ package net.corda.node.internal.security -import com.google.common.cache.CacheBuilder -import com.google.common.cache.Cache + +import com.github.benmanes.caffeine.cache.Cache +import com.github.benmanes.caffeine.cache.Caffeine import com.google.common.primitives.Ints import net.corda.core.context.AuthServiceId import net.corda.core.utilities.loggerFor @@ -112,7 +113,7 @@ class RPCSecurityManagerImpl(private val config: AuthServiceConfig) : RPCSecurit return DefaultSecurityManager(realm).also { // Setup optional cache layer if configured it.cacheManager = config.options?.cache?.let { - GuavaCacheManager( + CaffeineCacheManager( timeToLiveSeconds = it.expireAfterSecs, maxSize = it.maxEntries) } @@ -275,9 +276,9 @@ private class NodeJdbcRealm(config: SecurityConfiguration.AuthService.DataSource private typealias ShiroCache = org.apache.shiro.cache.Cache /* - * Adapts a [com.google.common.cache.Cache] to a [org.apache.shiro.cache.Cache] implementation. + * Adapts a [com.github.benmanes.caffeine.cache.Cache] to a [org.apache.shiro.cache.Cache] implementation. */ -private fun Cache.toShiroCache(name: String) = object : ShiroCache { +private fun Cache.toShiroCache(name: String) = object : ShiroCache { val name = name private val impl = this@toShiroCache @@ -300,7 +301,7 @@ private fun Cache.toShiroCache(name: String) = object : ShiroCache< impl.invalidateAll() } - override fun size() = Ints.checkedCast(impl.size()) + override fun size() = Ints.checkedCast(impl.estimatedSize()) override fun keys() = impl.asMap().keys override fun values() = impl.asMap().values override fun toString() = "Guava cache adapter [$impl]" @@ -308,22 +309,22 @@ private fun Cache.toShiroCache(name: String) = object : ShiroCache< /* * Implementation of [org.apache.shiro.cache.CacheManager] based on - * cache implementation in [com.google.common.cache] + * cache implementation in [com.github.benmanes.caffeine.cache.Cache] */ -private class GuavaCacheManager(val maxSize: Long, - val timeToLiveSeconds: Long) : CacheManager { +private class CaffeineCacheManager(val maxSize: Long, + val timeToLiveSeconds: Long) : CacheManager { 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 result as ShiroCache } - private fun buildCache(name: String) : ShiroCache { + private fun buildCache(name: String): ShiroCache { logger.info("Constructing cache '$name' with maximumSize=$maxSize, TTL=${timeToLiveSeconds}s") - return CacheBuilder.newBuilder() + return Caffeine.newBuilder() .expireAfterWrite(timeToLiveSeconds, TimeUnit.SECONDS) .maximumSize(maxSize) .build() @@ -331,6 +332,6 @@ private class GuavaCacheManager(val maxSize: Long, } companion object { - private val logger = loggerFor() + private val logger = loggerFor() } } \ No newline at end of file diff --git a/node/src/main/kotlin/net/corda/node/services/messaging/RPCServer.kt b/node/src/main/kotlin/net/corda/node/services/messaging/RPCServer.kt index 5ca6e7da05..de817f0491 100644 --- a/node/src/main/kotlin/net/corda/node/services/messaging/RPCServer.kt +++ b/node/src/main/kotlin/net/corda/node/services/messaging/RPCServer.kt @@ -10,13 +10,14 @@ package net.corda.node.services.messaging +import co.paralleluniverse.common.util.SameThreadExecutor import com.esotericsoftware.kryo.Kryo import com.esotericsoftware.kryo.Serializer import com.esotericsoftware.kryo.io.Input import com.esotericsoftware.kryo.io.Output -import com.google.common.cache.Cache -import com.google.common.cache.CacheBuilder -import com.google.common.cache.RemovalListener +import com.github.benmanes.caffeine.cache.Cache +import com.github.benmanes.caffeine.cache.Caffeine +import com.github.benmanes.caffeine.cache.RemovalListener import com.google.common.collect.HashMultimap import com.google.common.collect.Multimaps import com.google.common.collect.SetMultimap @@ -155,11 +156,11 @@ class RPCServer( } private fun createObservableSubscriptionMap(): ObservableSubscriptionMap { - val onObservableRemove = RemovalListener { - log.debug { "Unsubscribing from Observable with id ${it.key} because of ${it.cause}" } - it.value.subscription.unsubscribe() + val onObservableRemove = RemovalListener { key, value, cause -> + log.debug { "Unsubscribing from Observable with id ${key} because of ${cause}" } + value!!.subscription.unsubscribe() } - return CacheBuilder.newBuilder().removalListener(onObservableRemove).build() + return Caffeine.newBuilder().removalListener(onObservableRemove).executor(SameThreadExecutor.getExecutor()).build() } fun start(activeMqServerControl: ActiveMQServerControl) { diff --git a/node/src/main/kotlin/net/corda/node/services/network/PersistentNetworkMapCache.kt b/node/src/main/kotlin/net/corda/node/services/network/PersistentNetworkMapCache.kt index ffbd678de6..74acaf6ba0 100644 --- a/node/src/main/kotlin/net/corda/node/services/network/PersistentNetworkMapCache.kt +++ b/node/src/main/kotlin/net/corda/node/services/network/PersistentNetworkMapCache.kt @@ -175,9 +175,9 @@ open class PersistentNetworkMapCache( override fun getNodesByLegalName(name: CordaX500Name): List = database.transaction { queryByLegalName(session, name) } - override fun getNodesByLegalIdentityKey(identityKey: PublicKey): List = nodesByKeyCache[identityKey] + override fun getNodesByLegalIdentityKey(identityKey: PublicKey): List = nodesByKeyCache[identityKey]!! - private val nodesByKeyCache = NonInvalidatingCache>(1024, 8, { key -> database.transaction { queryByIdentityKey(session, key) } }) + private val nodesByKeyCache = NonInvalidatingCache>(1024, { key -> database.transaction { queryByIdentityKey(session, key) } }) override fun getNodesByOwningKeyIndex(identityKeyIndex: String): List { return database.transaction { @@ -187,9 +187,9 @@ open class PersistentNetworkMapCache( override fun getNodeByAddress(address: NetworkHostAndPort): NodeInfo? = database.transaction { queryByAddress(session, address) } - override fun getPeerCertificateByLegalName(name: CordaX500Name): PartyAndCertificate? = identityByLegalNameCache.get(name).orElse(null) + override fun getPeerCertificateByLegalName(name: CordaX500Name): PartyAndCertificate? = identityByLegalNameCache.get(name)!!.orElse(null) - private val identityByLegalNameCache = NonInvalidatingCache>(1024, 8, { name -> Optional.ofNullable(database.transaction { queryIdentityByLegalName(session, name) }) }) + private val identityByLegalNameCache = NonInvalidatingCache>(1024, { name -> Optional.ofNullable(database.transaction { queryIdentityByLegalName(session, name) }) }) override fun track(): DataFeed, MapChange> { synchronized(_changed) { diff --git a/node/src/main/kotlin/net/corda/node/services/persistence/NodeAttachmentService.kt b/node/src/main/kotlin/net/corda/node/services/persistence/NodeAttachmentService.kt index 0fa9d30bfa..7655333936 100644 --- a/node/src/main/kotlin/net/corda/node/services/persistence/NodeAttachmentService.kt +++ b/node/src/main/kotlin/net/corda/node/services/persistence/NodeAttachmentService.kt @@ -11,7 +11,7 @@ package net.corda.node.services.persistence import com.codahale.metrics.MetricRegistry -import com.google.common.cache.Weigher +import com.github.benmanes.caffeine.cache.Weigher import com.google.common.hash.HashCode import com.google.common.hash.Hashing import com.google.common.hash.HashingInputStream @@ -34,7 +34,6 @@ import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.vault.HibernateAttachmentQueryCriteriaParser import net.corda.node.utilities.NonInvalidatingCache import net.corda.node.utilities.NonInvalidatingWeightBasedCache -import net.corda.node.utilities.defaultCordaCacheConcurrencyLevel import net.corda.nodeapi.internal.persistence.NODE_DATABASE_PREFIX import net.corda.nodeapi.internal.persistence.currentDBSession import net.corda.nodeapi.internal.withContractsInJar @@ -219,7 +218,6 @@ class NodeAttachmentService( private val attachmentContentCache = NonInvalidatingWeightBasedCache>>( maxWeight = attachmentContentCacheSize, - concurrencyLevel = defaultCordaCacheConcurrencyLevel, weigher = object : Weigher>> { override fun weigh(key: SecureHash, value: Optional>): Int { return key.size + if (value.isPresent) value.get().second.size else 0 @@ -244,12 +242,11 @@ class NodeAttachmentService( private val attachmentCache = NonInvalidatingCache>( attachmentCacheBound, - defaultCordaCacheConcurrencyLevel, { key -> Optional.ofNullable(createAttachment(key)) } ) private fun createAttachment(key: SecureHash): Attachment? { - val content = attachmentContentCache.get(key) + val content = attachmentContentCache.get(key)!! if (content.isPresent) { return content.get().first } @@ -259,7 +256,7 @@ class NodeAttachmentService( } override fun openAttachment(id: SecureHash): Attachment? { - val attachment = attachmentCache.get(id) + val attachment = attachmentCache.get(id)!! if (attachment.isPresent) { return attachment.get() } 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 9870cbe9a4..0a092214dc 100644 --- a/node/src/main/kotlin/net/corda/node/utilities/AppendOnlyPersistentMap.kt +++ b/node/src/main/kotlin/net/corda/node/utilities/AppendOnlyPersistentMap.kt @@ -10,8 +10,8 @@ package net.corda.node.utilities -import com.google.common.cache.LoadingCache -import com.google.common.cache.Weigher +import com.github.benmanes.caffeine.cache.LoadingCache +import com.github.benmanes.caffeine.cache.Weigher import net.corda.core.utilities.contextLogger import net.corda.nodeapi.internal.persistence.currentDBSession import java.util.* @@ -39,7 +39,7 @@ abstract class AppendOnlyPersistentMapBase( * Returns the value associated with the key, first loading that value from the storage if necessary. */ operator fun get(key: K): V? { - return cache.get(key).orElse(null) + return cache.get(key)!!.orElse(null) } val size get() = allPersisted().toList().size @@ -72,7 +72,7 @@ abstract class AppendOnlyPersistentMapBase( } else { Optional.of(value) } - } + }!! if (!insertionAttempt) { if (existingInCache.isPresent) { // Key already exists in cache, do nothing. @@ -81,7 +81,7 @@ abstract class AppendOnlyPersistentMapBase( // This happens when the key was queried before with no value associated. We invalidate the cached null // value and recursively call set again. This is to avoid race conditions where another thread queries after // the invalidate but before the set. - cache.invalidate(key) + cache.invalidate(key!!) return set(key, value, logWarning, store) } } @@ -158,7 +158,6 @@ class AppendOnlyPersistentMap( //TODO determine cacheBound based on entity class later or with node config allowing tuning, or using some heuristic based on heap size override val cache = NonInvalidatingCache>( bound = cacheBound, - concurrencyLevel = 8, loadFunction = { key -> Optional.ofNullable(loadValue(key)) }) } @@ -176,7 +175,6 @@ class WeightBasedAppendOnlyPersistentMap( persistentEntityClass) { override val cache = NonInvalidatingWeightBasedCache>( maxWeight = maxWeight, - concurrencyLevel = 8, weigher = object : Weigher> { override fun weigh(key: K, value: Optional): Int { return weighingFunc(key, value) diff --git a/node/src/main/kotlin/net/corda/node/utilities/JVMAgentRegistry.kt b/node/src/main/kotlin/net/corda/node/utilities/JVMAgentRegistry.kt index 59aacc78e2..84832d6859 100644 --- a/node/src/main/kotlin/net/corda/node/utilities/JVMAgentRegistry.kt +++ b/node/src/main/kotlin/net/corda/node/utilities/JVMAgentRegistry.kt @@ -53,7 +53,7 @@ object JVMAgentRegistry { } else { (this::class.java.classLoader as? URLClassLoader) ?.urLs - ?.map { Paths.get(it.path) } + ?.map { Paths.get(it.toURI()) } ?.firstOrNull { it.fileName.toString() == jarFileName } } } 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 feaeae2dfe..98aab89067 100644 --- a/node/src/main/kotlin/net/corda/node/utilities/NonInvalidatingCache.kt +++ b/node/src/main/kotlin/net/corda/node/utilities/NonInvalidatingCache.kt @@ -10,30 +10,29 @@ package net.corda.node.utilities -import com.google.common.cache.CacheBuilder -import com.google.common.cache.CacheLoader -import com.google.common.cache.LoadingCache -import com.google.common.cache.Weigher -import com.google.common.util.concurrent.ListenableFuture +import com.github.benmanes.caffeine.cache.CacheLoader +import com.github.benmanes.caffeine.cache.Caffeine +import com.github.benmanes.caffeine.cache.LoadingCache +import com.github.benmanes.caffeine.cache.Weigher class NonInvalidatingCache private constructor( val cache: LoadingCache ) : LoadingCache by cache { - constructor(bound: Long, concurrencyLevel: Int, loadFunction: (K) -> V) : - this(buildCache(bound, concurrencyLevel, loadFunction)) + constructor(bound: Long, loadFunction: (K) -> V) : + this(buildCache(bound, loadFunction)) private companion object { - private fun buildCache(bound: Long, concurrencyLevel: Int, loadFunction: (K) -> V): LoadingCache { - val builder = CacheBuilder.newBuilder().maximumSize(bound).concurrencyLevel(concurrencyLevel) + private fun buildCache(bound: Long, loadFunction: (K) -> V): LoadingCache { + val builder = Caffeine.newBuilder().maximumSize(bound) return builder.build(NonInvalidatingCacheLoader(loadFunction)) } } // TODO look into overriding loadAll() if we ever use it - class NonInvalidatingCacheLoader(val loadFunction: (K) -> V) : CacheLoader() { - override fun reload(key: K, oldValue: V): ListenableFuture { + class NonInvalidatingCacheLoader(val loadFunction: (K) -> V) : CacheLoader { + override fun reload(key: K, oldValue: V): V { throw IllegalStateException("Non invalidating cache refreshed") } @@ -44,16 +43,14 @@ class NonInvalidatingCache private constructor( class NonInvalidatingWeightBasedCache private constructor( val cache: LoadingCache ) : LoadingCache by cache { - constructor (maxWeight: Long, concurrencyLevel: Int, weigher: Weigher, loadFunction: (K) -> V) : - this(buildCache(maxWeight, concurrencyLevel, weigher, loadFunction)) + constructor (maxWeight: Long, weigher: Weigher, loadFunction: (K) -> V) : + this(buildCache(maxWeight, weigher, loadFunction)) private companion object { - private fun buildCache(maxWeight: Long, concurrencyLevel: Int, weigher: Weigher, loadFunction: (K) -> V): LoadingCache { - val builder = CacheBuilder.newBuilder().maximumWeight(maxWeight).weigher(weigher).concurrencyLevel(concurrencyLevel) + private fun buildCache(maxWeight: Long, weigher: Weigher, loadFunction: (K) -> V): LoadingCache { + val builder = Caffeine.newBuilder().maximumWeight(maxWeight).weigher(weigher) return builder.build(NonInvalidatingCache.NonInvalidatingCacheLoader(loadFunction)) } } -} - -val defaultCordaCacheConcurrencyLevel: Int = 8 \ No newline at end of file +} \ 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 4ac2147767..b0036dac5b 100644 --- a/node/src/main/kotlin/net/corda/node/utilities/NonInvalidatingUnboundCache.kt +++ b/node/src/main/kotlin/net/corda/node/utilities/NonInvalidatingUnboundCache.kt @@ -10,22 +10,24 @@ package net.corda.node.utilities -import com.google.common.cache.* -import com.google.common.util.concurrent.ListenableFuture - +import co.paralleluniverse.common.util.SameThreadExecutor +import com.github.benmanes.caffeine.cache.CacheLoader +import com.github.benmanes.caffeine.cache.Caffeine +import com.github.benmanes.caffeine.cache.LoadingCache +import com.github.benmanes.caffeine.cache.RemovalListener class NonInvalidatingUnboundCache private constructor( val cache: LoadingCache ) : LoadingCache by cache { - constructor(concurrencyLevel: Int, loadFunction: (K) -> V, removalListener: RemovalListener = RemovalListener {}, + constructor(loadFunction: (K) -> V, removalListener: RemovalListener = RemovalListener { key, value, cause -> }, keysToPreload: () -> Iterable = { emptyList() }) : - this(buildCache(concurrencyLevel, loadFunction, removalListener, keysToPreload)) + this(buildCache(loadFunction, removalListener, keysToPreload)) private companion object { - private fun buildCache(concurrencyLevel: Int, loadFunction: (K) -> V, removalListener: RemovalListener, + private fun buildCache(loadFunction: (K) -> V, removalListener: RemovalListener, keysToPreload: () -> Iterable): LoadingCache { - val builder = CacheBuilder.newBuilder().concurrencyLevel(concurrencyLevel).removalListener(removalListener) + val builder = Caffeine.newBuilder().removalListener(removalListener).executor(SameThreadExecutor.getExecutor()) return builder.build(NonInvalidatingCacheLoader(loadFunction)).apply { getAll(keysToPreload()) } @@ -33,8 +35,8 @@ class NonInvalidatingUnboundCache private constructor( } // TODO look into overriding loadAll() if we ever use it - private class NonInvalidatingCacheLoader(val loadFunction: (K) -> V) : CacheLoader() { - override fun reload(key: K, oldValue: V): ListenableFuture { + private class NonInvalidatingCacheLoader(val loadFunction: (K) -> V) : CacheLoader { + override fun reload(key: K, oldValue: V): V { throw IllegalStateException("Non invalidating cache refreshed") } 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 a00bd8bf8d..c01b03769e 100644 --- a/node/src/main/kotlin/net/corda/node/utilities/PersistentMap.kt +++ b/node/src/main/kotlin/net/corda/node/utilities/PersistentMap.kt @@ -10,9 +10,8 @@ package net.corda.node.utilities -import com.google.common.cache.RemovalCause -import com.google.common.cache.RemovalListener -import com.google.common.cache.RemovalNotification +import com.github.benmanes.caffeine.cache.RemovalCause +import com.github.benmanes.caffeine.cache.RemovalListener import net.corda.core.utilities.contextLogger import net.corda.nodeapi.internal.persistence.currentDBSession import java.util.* @@ -20,7 +19,7 @@ import java.util.* /** * Implements an unbound caching layer on top of a table accessed via Hibernate mapping. */ -class PersistentMap( +class PersistentMap( val toPersistentEntityKey: (K) -> EK, val fromPersistentEntity: (E) -> Pair, val toPersistentEntity: (key: K, value: V) -> E, @@ -32,7 +31,6 @@ class PersistentMap( } private val cache = NonInvalidatingUnboundCache( - concurrencyLevel = 8, loadFunction = { key -> Optional.ofNullable(loadValue(key)) }, removalListener = ExplicitRemoval(toPersistentEntityKey, persistentEntityClass) ).apply { @@ -44,11 +42,11 @@ class PersistentMap( } class ExplicitRemoval(private val toPersistentEntityKey: (K) -> EK, private val persistentEntityClass: Class) : RemovalListener { - override fun onRemoval(notification: RemovalNotification?) { - when (notification?.cause) { + override fun onRemoval(key: K?, value: V?, cause: RemovalCause) { + when (cause) { RemovalCause.EXPLICIT -> { val session = currentDBSession() - val elem = session.find(persistentEntityClass, toPersistentEntityKey(notification.key)) + val elem = session.find(persistentEntityClass, toPersistentEntityKey(key!!)) if (elem != null) { session.remove(elem) } @@ -63,14 +61,14 @@ class PersistentMap( } override operator fun get(key: K): V? { - return cache.get(key).orElse(null) + return cache.get(key)!!.orElse(null) } fun all(): Sequence> { return cache.asMap().asSequence().filter { it.value.isPresent }.map { Pair(it.key, it.value.get()) } } - override val size get() = cache.size().toInt() + override val size get() = cache.estimatedSize().toInt() private tailrec fun set(key: K, value: V, logWarning: Boolean = true, store: (K, V) -> V?, replace: (K, V) -> Unit): Boolean { var insertionAttempt = false @@ -82,7 +80,7 @@ class PersistentMap( // Store the value, depending on store implementation this may replace existing entry in DB. store(key, value) Optional.of(value) - } + }!! if (!insertionAttempt) { if (existingInCache.isPresent) { // Key already exists in cache, store the new value in the DB (depends on tore implementation) and refresh cache. @@ -175,7 +173,7 @@ class PersistentMap( * Removes the mapping for the specified key from this map and underlying storage if present. */ override fun remove(key: K): V? { - val result = cache.get(key).orElse(null) + val result = cache.get(key)!!.orElse(null) cache.invalidate(key) return result } @@ -263,7 +261,7 @@ class PersistentMap( override fun put(key: K, value: V): V? { val old = cache.get(key) addWithDuplicatesReplaced(key, value) - return old.orElse(null) + return old!!.orElse(null) } fun load() { diff --git a/samples/irs-demo/cordapp/src/main/kotlin/net/corda/irs/api/NodeInterestRates.kt b/samples/irs-demo/cordapp/src/main/kotlin/net/corda/irs/api/NodeInterestRates.kt index 825662d909..65e92c6883 100644 --- a/samples/irs-demo/cordapp/src/main/kotlin/net/corda/irs/api/NodeInterestRates.kt +++ b/samples/irs-demo/cordapp/src/main/kotlin/net/corda/irs/api/NodeInterestRates.kt @@ -170,7 +170,7 @@ object NodeInterestRates { } private fun addDefaultFixes() { - knownFixes = parseFile(IOUtils.toString(Thread.currentThread().contextClassLoader.getResourceAsStream("net/corda/irs/simulation/example.rates.txt"), Charsets.UTF_8.name())) + knownFixes = parseFile(IOUtils.toString(this::class.java.classLoader.getResourceAsStream("net/corda/irs/simulation/example.rates.txt"), Charsets.UTF_8.name())) } } diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt index db43d527df..143faae953 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/MockServices.kt @@ -142,7 +142,7 @@ open class MockServices private constructor( val cordappLoader = CordappLoader.createWithTestPackages(cordappPackages) val dataSourceProps = makeTestDataSourceProperties(initialIdentity.name.organisation) val schemaService = NodeSchemaService(cordappLoader.cordappSchemas) - val database = configureDatabase(dataSourceProps, makeTestDatabaseProperties(initialIdentity.name.organisation), identityService, schemaService) + val database = configureDatabase(dataSourceProps, makeTestDatabaseProperties(initialIdentity.name.organisation), identityService, schemaService, cordappLoader.appClassLoader) val mockService = database.transaction { object : MockServices(cordappLoader, identityService, networkParameters, initialIdentity, moreKeys) { override val vaultService: VaultService = makeVaultService(database.hibernateConfig, schemaService) diff --git a/testing/smoke-test-utils/src/main/kotlin/net/corda/smoketesting/NodeProcess.kt b/testing/smoke-test-utils/src/main/kotlin/net/corda/smoketesting/NodeProcess.kt index 64da916290..e4c86ada06 100644 --- a/testing/smoke-test-utils/src/main/kotlin/net/corda/smoketesting/NodeProcess.kt +++ b/testing/smoke-test-utils/src/main/kotlin/net/corda/smoketesting/NodeProcess.kt @@ -112,7 +112,7 @@ class NodeProcess( } fun setupPlugins(config: NodeConfig, jarPaths: List): Factory { - (baseDirectory(config) / "plugins").createDirectories().also { + (baseDirectory(config) / "drivers").createDirectories().also { jarPaths.forEach { jar -> Paths.get(jar).copyToDirectory(it) } } return this diff --git a/tools/explorer/src/main/kotlin/net/corda/explorer/identicon/IdenticonRenderer.kt b/tools/explorer/src/main/kotlin/net/corda/explorer/identicon/IdenticonRenderer.kt index a2d89f4132..8be5984a84 100644 --- a/tools/explorer/src/main/kotlin/net/corda/explorer/identicon/IdenticonRenderer.kt +++ b/tools/explorer/src/main/kotlin/net/corda/explorer/identicon/IdenticonRenderer.kt @@ -10,9 +10,9 @@ package net.corda.explorer.identicon +import com.github.benmanes.caffeine.cache.CacheLoader +import com.github.benmanes.caffeine.cache.Caffeine import com.google.common.base.Splitter -import com.google.common.cache.CacheBuilder -import com.google.common.cache.CacheLoader import javafx.scene.SnapshotParameters import javafx.scene.canvas.Canvas import javafx.scene.canvas.GraphicsContext @@ -85,7 +85,7 @@ object IdenticonRenderer { private val renderingSize = 30.0 - private val cache = CacheBuilder.newBuilder().build(CacheLoader.from { key -> + private val cache = Caffeine.newBuilder().build(CacheLoader { key -> key?.let { render(key.hashCode(), renderingSize) } }) @@ -102,7 +102,7 @@ object IdenticonRenderer { } fun getIdenticon(hash: SecureHash): Image { - return cache.get(hash) + return cache.get(hash)!! } /**