From 531e4a85fe6da9e42618b4b96bded0700f6437eb Mon Sep 17 00:00:00 2001 From: Mike Hearn Date: Thu, 14 Apr 2016 18:45:18 +0200 Subject: [PATCH] Tweak the attachment classloader tests so that they don't break if IntelliJ has put the 'contracts:isolated' module on the classpath. --- .../core/node/AttachmentsClassLoader.kt | 2 +- .../core/node/AttachmentClassLoaderTests.kt | 58 ++++++++++--------- 2 files changed, 31 insertions(+), 29 deletions(-) diff --git a/core/src/main/kotlin/core/node/AttachmentsClassLoader.kt b/core/src/main/kotlin/core/node/AttachmentsClassLoader.kt index 311c0dee84..3fb9878109 100644 --- a/core/src/main/kotlin/core/node/AttachmentsClassLoader.kt +++ b/core/src/main/kotlin/core/node/AttachmentsClassLoader.kt @@ -20,7 +20,7 @@ import java.util.* * AttachmentsClassLoader is somewhat expensive, as every attachment is scanned to ensure that there are no overlapping * file paths. */ -class AttachmentsClassLoader(attachments: List) : SecureClassLoader() { +class AttachmentsClassLoader(attachments: List, parent: ClassLoader = ClassLoader.getSystemClassLoader()) : SecureClassLoader(parent) { private val pathsToAttachments = HashMap() private val idsToAttachments = HashMap() diff --git a/src/test/kotlin/core/node/AttachmentClassLoaderTests.kt b/src/test/kotlin/core/node/AttachmentClassLoaderTests.kt index 82e1a6f805..40a49152e5 100644 --- a/src/test/kotlin/core/node/AttachmentClassLoaderTests.kt +++ b/src/test/kotlin/core/node/AttachmentClassLoaderTests.kt @@ -20,18 +20,34 @@ import kotlin.test.assertNotNull interface DummyContractBackdoor { fun generateInitial(owner: PartyReference, magicNumber: Int): TransactionBuilder - fun inspectState(state: ContractState): Int } class AttachmentClassLoaderTests { - val ISOLATED_CONTRACTS_JAR_PATH = AttachmentClassLoaderTests::class.java.getResource("isolated.jar") + companion object { + val ISOLATED_CONTRACTS_JAR_PATH = AttachmentClassLoaderTests::class.java.getResource("isolated.jar") + } fun importJar(storage: AttachmentStorage) = ISOLATED_CONTRACTS_JAR_PATH.openStream().use { storage.importAttachment(it) } + // These ClassLoaders work together to load 'AnotherDummyContract' in a disposable way, such that even though + // the class may be on the unit test class path (due to default IDE settings, etc), it won't be loaded into the + // regular app classloader but rather than ClassLoaderForTests. This helps keep our environment clean and + // ensures we have precise control over where it's loaded. + object FilteringClassLoader : ClassLoader() { + override fun loadClass(name: String, resolve: Boolean): Class<*>? { + if ("AnotherDummyContract" in name) { + return null + } else + return super.loadClass(name, resolve) + } + } + + class ClassLoaderForTests : URLClassLoader(arrayOf(ISOLATED_CONTRACTS_JAR_PATH), FilteringClassLoader) + @Test fun `dynamically load AnotherDummyContract from isolated contracts jar`() { - val child = URLClassLoader(arrayOf(ISOLATED_CONTRACTS_JAR_PATH)) + val child = ClassLoaderForTests() val contractClass = Class.forName("contracts.isolated.AnotherDummyContract", true, child) val contract = contractClass.newInstance() as Contract @@ -94,25 +110,14 @@ class AttachmentClassLoaderTests { val att1 = storage.importAttachment(ByteArrayInputStream(fakeAttachment("file1.txt", "some data"))) val att2 = storage.importAttachment(ByteArrayInputStream(fakeAttachment("file2.txt", "some other data"))) - val cl = AttachmentsClassLoader(arrayOf(att0, att1, att2).map { storage.openAttachment(it)!! }) + val cl = AttachmentsClassLoader(arrayOf(att0, att1, att2).map { storage.openAttachment(it)!! }, FilteringClassLoader) val contractClass = Class.forName("contracts.isolated.AnotherDummyContract", true, cl) val contract = contractClass.newInstance() as Contract + assertEquals(cl, contract.javaClass.classLoader) assertEquals(SecureHash.sha256("https://anotherdummy.org"), contract.legalContractReference) } - /** - * If this test starts failing it is either because you haven't build with gradle first - * or because you have reimported gradle project in IDEA (revert change to .idea/modules.xml) - */ - @Test - fun `verify that contract AnotherDummyContract is not in classPath`() { - assertFailsWith(ClassNotFoundException::class) { - val contractClass = Class.forName("contracts.isolated.AnotherDummyContract") - contractClass.newInstance() as Contract - } - } - @Test fun `verify that contract DummyContract is in classPath`() { val contractClass = Class.forName("contracts.DummyContract") @@ -122,10 +127,9 @@ class AttachmentClassLoaderTests { } fun createContract2Cash(): Contract { - val child = URLClassLoader(arrayOf(ISOLATED_CONTRACTS_JAR_PATH)) - val contractClass = Class.forName("contracts.isolated.AnotherDummyContract", true, child) - val contract = contractClass.newInstance() as Contract - return contract + val cl = ClassLoaderForTests() + val contractClass = Class.forName("contracts.isolated.AnotherDummyContract", true, cl) + return contractClass.newInstance() as Contract } @Test @@ -140,13 +144,13 @@ class AttachmentClassLoaderTests { val att1 = storage.importAttachment(ByteArrayInputStream(fakeAttachment("file1.txt", "some data"))) val att2 = storage.importAttachment(ByteArrayInputStream(fakeAttachment("file2.txt", "some other data"))) - val cl = AttachmentsClassLoader(arrayOf(att0, att1, att2).map { storage.openAttachment(it)!! }) + val cl = AttachmentsClassLoader(arrayOf(att0, att1, att2).map { storage.openAttachment(it)!! }, FilteringClassLoader) val kryo = createKryo() kryo.classLoader = cl val state2 = bytes.deserialize(kryo, true) - + assert(state2.javaClass.classLoader is AttachmentsClassLoader) assertNotNull(state2) } @@ -167,13 +171,13 @@ class AttachmentClassLoaderTests { val att1 = storage.importAttachment(ByteArrayInputStream(fakeAttachment("file1.txt", "some data"))) val att2 = storage.importAttachment(ByteArrayInputStream(fakeAttachment("file2.txt", "some other data"))) - val cl = AttachmentsClassLoader(arrayOf(att0, att1, att2).map { storage.openAttachment(it)!! }) + val cl = AttachmentsClassLoader(arrayOf(att0, att1, att2).map { storage.openAttachment(it)!! }, FilteringClassLoader) val kryo = createKryo() kryo.classLoader = cl val state2 = bytes.deserialize(kryo) - + assertEquals(cl, state2.contract.javaClass.classLoader) assertNotNull(state2) } @@ -190,7 +194,7 @@ class AttachmentClassLoaderTests { @Test fun `test serialization of WireTransaction with dynamically loaded contract`() { - val child = URLClassLoader(arrayOf(ISOLATED_CONTRACTS_JAR_PATH)) + val child = ClassLoaderForTests() val contractClass = Class.forName("contracts.isolated.AnotherDummyContract", true, child) val contract = contractClass.newInstance() as DummyContractBackdoor val tx = contract.generateInitial(MEGA_CORP.ref(0), 42) @@ -209,21 +213,19 @@ class AttachmentClassLoaderTests { val bytes = wireTransaction.serialize(kryo) val kryo2 = createKryo() - // use empty attachmentStorage kryo2.attachmentStorage = storage val copiedWireTransaction = bytes.deserialize(kryo2) assertEquals(1, copiedWireTransaction.outputs.size) - val contract2 = copiedWireTransaction.outputs[0].contract as DummyContractBackdoor assertEquals(42, contract2.inspectState(copiedWireTransaction.outputs[0])) } @Test fun `test deserialize of WireTransaction where contract cannot be found`() { - val child = URLClassLoader(arrayOf(ISOLATED_CONTRACTS_JAR_PATH)) + val child = ClassLoaderForTests() val contractClass = Class.forName("contracts.isolated.AnotherDummyContract", true, child) val contract = contractClass.newInstance() as DummyContractBackdoor val tx = contract.generateInitial(MEGA_CORP.ref(0), 42)