mirror of
https://github.com/corda/corda.git
synced 2025-02-21 09:51:57 +00:00
Custom ClassLoader created from collection of attachments
This commit is contained in:
parent
2a72f38076
commit
3675675277
2
.gitignore
vendored
2
.gitignore
vendored
@ -13,7 +13,7 @@ tags
|
||||
.gradle
|
||||
/build/
|
||||
/contracts/build
|
||||
/sandbox/build
|
||||
/standalone.test/build
|
||||
/core/build
|
||||
/docs/build/doctrees
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
rootProject.name = 'r3prototyping'
|
||||
include 'contracts'
|
||||
include 'core'
|
||||
include 'sandbox'
|
||||
include 'standalone.test'
|
64
src/main/kotlin/core/node/ClassLoader.kt
Normal file
64
src/main/kotlin/core/node/ClassLoader.kt
Normal file
@ -0,0 +1,64 @@
|
||||
package core.node
|
||||
|
||||
import core.Attachment
|
||||
import java.io.Closeable
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.net.URL
|
||||
import java.net.URLClassLoader
|
||||
import java.util.*
|
||||
import java.util.jar.JarEntry
|
||||
|
||||
class OverlappingAttachments : Exception()
|
||||
|
||||
/**
|
||||
* A custom ClassLoader for creating contracts distributed as attachments and for contracts to
|
||||
* access attachments.
|
||||
*
|
||||
*
|
||||
*/
|
||||
class ClassLoader private constructor(val tmpFiles: List<File> )
|
||||
: URLClassLoader(tmpFiles.map { URL("file", "", it.toString()) }.toTypedArray()), Closeable {
|
||||
|
||||
override fun close() {
|
||||
super.close()
|
||||
|
||||
for (file in tmpFiles) {
|
||||
file.delete()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun create(streams: List<Attachment>) : ClassLoader {
|
||||
|
||||
validate(streams)
|
||||
|
||||
var tmpFiles = streams.map {
|
||||
var filename = File.createTempFile("jar", "")
|
||||
it.open().use {
|
||||
str -> FileOutputStream(filename).use { str.copyTo(it) }
|
||||
}
|
||||
filename
|
||||
}
|
||||
|
||||
return ClassLoader(tmpFiles)
|
||||
}
|
||||
|
||||
private fun validate(streams: List<Attachment>) {
|
||||
val set = HashSet<String>()
|
||||
|
||||
val jars = streams.map { it.openAsJAR() }
|
||||
|
||||
for (jar in jars) {
|
||||
|
||||
var entry: JarEntry = jar.nextJarEntry ?: continue
|
||||
if (set.add(entry.name) == false) {
|
||||
throw OverlappingAttachments()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -92,7 +92,7 @@ class MockAttachmentStorage : AttachmentStorage {
|
||||
override fun openAttachment(id: SecureHash): Attachment? {
|
||||
val f = files[id] ?: return null
|
||||
return object : Attachment {
|
||||
override fun open(): JarInputStream = JarInputStream(ByteArrayInputStream(f))
|
||||
override fun open(): InputStream = ByteArrayInputStream(f)
|
||||
override val id: SecureHash = id
|
||||
}
|
||||
}
|
||||
@ -104,6 +104,7 @@ class MockAttachmentStorage : AttachmentStorage {
|
||||
val bytes = run {
|
||||
val s = ByteArrayOutputStream()
|
||||
jar.copyTo(s)
|
||||
s.close()
|
||||
s.toByteArray()
|
||||
}
|
||||
val sha256 = bytes.sha256()
|
||||
|
@ -46,7 +46,7 @@ class AttachmentTests {
|
||||
val bs = ByteArrayOutputStream()
|
||||
val js = JarOutputStream(bs)
|
||||
js.putNextEntry(ZipEntry("file1.txt"))
|
||||
js.writer().append("Some useful content")
|
||||
js.writer().apply { append("Some useful content"); flush() }
|
||||
js.closeEntry()
|
||||
js.close()
|
||||
return bs.toByteArray()
|
||||
|
94
src/test/kotlin/core/node/ClassLoaderTests.kt
Normal file
94
src/test/kotlin/core/node/ClassLoaderTests.kt
Normal file
@ -0,0 +1,94 @@
|
||||
package core.node
|
||||
|
||||
import core.Contract
|
||||
import core.MockAttachmentStorage
|
||||
import core.crypto.SecureHash
|
||||
import org.apache.commons.io.IOUtils
|
||||
import org.junit.Ignore
|
||||
import org.junit.Test
|
||||
import java.io.ByteArrayInputStream
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.FileInputStream
|
||||
import java.util.jar.JarOutputStream
|
||||
import java.util.zip.ZipEntry
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertFailsWith
|
||||
|
||||
class ClassLoaderTests {
|
||||
|
||||
fun fakeAttachment(filepath: String, content: String): ByteArray {
|
||||
val bs = ByteArrayOutputStream()
|
||||
val js = JarOutputStream(bs)
|
||||
js.putNextEntry(ZipEntry(filepath))
|
||||
js.writer().apply { append(content); flush() }
|
||||
js.closeEntry()
|
||||
js.close()
|
||||
return bs.toByteArray()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test MockAttachmentStorage open as jar`() {
|
||||
val storage = MockAttachmentStorage()
|
||||
val key = storage.importAttachment( FileInputStream("contracts/build/libs/contracts.jar") )
|
||||
val attachment = storage.openAttachment(key)!!
|
||||
|
||||
val jar = attachment.openAsJAR()
|
||||
|
||||
assert( jar.nextEntry != null )
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test overlapping file exception`() {
|
||||
|
||||
var storage = MockAttachmentStorage()
|
||||
|
||||
var att0 = storage.importAttachment( FileInputStream("contracts/build/libs/contracts.jar") )
|
||||
var att1 = storage.importAttachment( ByteArrayInputStream(fakeAttachment("file.txt", "some data")) )
|
||||
var att2 = storage.importAttachment( ByteArrayInputStream(fakeAttachment("file.txt", "some other data")) )
|
||||
|
||||
assertFailsWith( OverlappingAttachments::class ) {
|
||||
|
||||
var cl = ClassLoader.create(arrayOf(att0, att1, att2).map { storage.openAttachment(it)!! })
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `basic`() {
|
||||
|
||||
var storage = MockAttachmentStorage()
|
||||
|
||||
var att0 = storage.importAttachment( FileInputStream("contracts/build/libs/contracts.jar") )
|
||||
var att1 = storage.importAttachment( ByteArrayInputStream(fakeAttachment("file1.txt", "some data")) )
|
||||
var att2 = storage.importAttachment( ByteArrayInputStream(fakeAttachment("file2.txt", "some other data")) )
|
||||
|
||||
ClassLoader.create( arrayOf( att0, att1, att2 ).map { storage.openAttachment(it)!! } ).use {
|
||||
var txt = IOUtils.toString(it.getResourceAsStream("file1.txt"))
|
||||
assertEquals( "some data", txt )
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `loading class Cash`() {
|
||||
var storage = MockAttachmentStorage()
|
||||
|
||||
var att0 = storage.importAttachment( FileInputStream("contracts/build/libs/contracts.jar") )
|
||||
var att1 = storage.importAttachment( ByteArrayInputStream(fakeAttachment("file1.txt", "some data")) )
|
||||
var att2 = storage.importAttachment( ByteArrayInputStream(fakeAttachment("file2.txt", "some other data")) )
|
||||
|
||||
ClassLoader.create( arrayOf( att0, att1, att2 ).map { storage.openAttachment(it)!! } ).use {
|
||||
|
||||
var contractClass = Class.forName("contracts.Cash", true, it)
|
||||
var contract = contractClass.newInstance() as Contract
|
||||
|
||||
assertEquals(SecureHash.sha256("https://www.big-book-of-banking-law.gov/cash-claims.html"), contract.legalContractReference)
|
||||
}
|
||||
}
|
||||
|
||||
@Ignore
|
||||
@Test
|
||||
fun `testing Kryo with ClassLoader`() {
|
||||
assert(false) // todo
|
||||
}
|
||||
|
||||
}
|
@ -3,12 +3,13 @@ import core.crypto.SecureHash
|
||||
import org.junit.Test
|
||||
import java.net.URL
|
||||
import java.net.URLClassLoader
|
||||
import java.util.jar.JarInputStream
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class LoaderTests {
|
||||
|
||||
@Test
|
||||
fun loadContracts() {
|
||||
fun `dynamically load Cash class from contracts jar`() {
|
||||
var child = URLClassLoader(arrayOf(URL("file", "", "../contracts/build/libs/contracts.jar")))
|
||||
|
||||
var contractClass = Class.forName("contracts.Cash", true, child)
|
Loading…
x
Reference in New Issue
Block a user