mirror of
https://github.com/corda/corda.git
synced 2025-06-17 14:48:16 +00:00
Simple Attachment Storage implementation using Requery/H2 database
This commit is contained in:
@ -204,7 +204,7 @@ inline fun elapsedTime(block: () -> Unit): Duration {
|
||||
val start = System.nanoTime()
|
||||
block()
|
||||
val end = System.nanoTime()
|
||||
return Duration.ofNanos(end-start)
|
||||
return Duration.ofNanos(end - start)
|
||||
}
|
||||
|
||||
// TODO: Add inline back when a new Kotlin version is released and check if the java.lang.VerifyError
|
||||
@ -280,13 +280,16 @@ class TransientProperty<out T>(private val initializer: () -> T) {
|
||||
/**
|
||||
* Given a path to a zip file, extracts it to the given directory.
|
||||
*/
|
||||
fun extractZipFile(zipFile: Path, toDirectory: Path) {
|
||||
val normalisedDirectory = toDirectory.normalize().createDirectories()
|
||||
fun extractZipFile(zipFile: Path, toDirectory: Path) = extractZipFile(Files.newInputStream(zipFile), toDirectory)
|
||||
|
||||
zipFile.read {
|
||||
val zip = ZipInputStream(BufferedInputStream(it))
|
||||
/**
|
||||
* Given a zip file input stream, extracts it to the given directory.
|
||||
*/
|
||||
fun extractZipFile(inputStream: InputStream, toDirectory: Path) {
|
||||
val normalisedDirectory = toDirectory.normalize().createDirectories()
|
||||
ZipInputStream(BufferedInputStream(inputStream)).use {
|
||||
while (true) {
|
||||
val e = zip.nextEntry ?: break
|
||||
val e = it.nextEntry ?: break
|
||||
val outPath = (normalisedDirectory / e.name).normalize()
|
||||
|
||||
// Security checks: we should reject a zip that contains tricksy paths that try to escape toDirectory.
|
||||
@ -297,9 +300,9 @@ fun extractZipFile(zipFile: Path, toDirectory: Path) {
|
||||
continue
|
||||
}
|
||||
outPath.write { out ->
|
||||
ByteStreams.copy(zip, out)
|
||||
ByteStreams.copy(it, out)
|
||||
}
|
||||
zip.closeEntry()
|
||||
it.closeEntry()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -394,13 +397,16 @@ private class ObservableToFuture<T>(observable: Observable<T>) : AbstractFuture<
|
||||
override fun onNext(value: T) {
|
||||
set(value)
|
||||
}
|
||||
|
||||
override fun onError(e: Throwable) {
|
||||
setException(e)
|
||||
}
|
||||
|
||||
override fun cancel(mayInterruptIfRunning: Boolean): Boolean {
|
||||
subscription.unsubscribe()
|
||||
return super.cancel(mayInterruptIfRunning)
|
||||
}
|
||||
|
||||
override fun onCompleted() {}
|
||||
}
|
||||
|
||||
|
@ -3,11 +3,20 @@ package net.corda.core.node.services
|
||||
import net.corda.core.contracts.Attachment
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import java.io.InputStream
|
||||
import java.nio.file.Path
|
||||
|
||||
/**
|
||||
* An attachment store records potentially large binary objects, identified by their hash.
|
||||
*/
|
||||
interface AttachmentStorage {
|
||||
/**
|
||||
* If true, newly inserted attachments will be unzipped to a subdirectory of the [storePath]. This is intended for
|
||||
* human browsing convenience: the attachment itself will still be the file (that is, edits to the extracted directory
|
||||
* will not have any effect).
|
||||
*/
|
||||
var automaticallyExtractAttachments : Boolean
|
||||
var storePath : Path
|
||||
|
||||
/**
|
||||
* Returns a handle to a locally stored attachment, or null if it's not known. The handle can be used to open
|
||||
* a stream for the data, which will be a zip/jar file.
|
||||
|
@ -0,0 +1,28 @@
|
||||
package net.corda.core.schemas.requery.converters
|
||||
|
||||
import io.requery.Converter
|
||||
import java.sql.Blob
|
||||
import javax.sql.rowset.serial.SerialBlob
|
||||
|
||||
/**
|
||||
* Converts from a [ByteArray] to a [Blob].
|
||||
*/
|
||||
class BlobConverter : Converter<ByteArray, Blob> {
|
||||
|
||||
override fun getMappedType(): Class<ByteArray> = ByteArray::class.java
|
||||
|
||||
override fun getPersistedType(): Class<Blob> = Blob::class.java
|
||||
|
||||
/**
|
||||
* creates BLOB(INT.MAX) = 2 GB
|
||||
*/
|
||||
override fun getPersistedSize(): Int? = null
|
||||
|
||||
override fun convertToPersisted(value: ByteArray?): Blob? {
|
||||
return value?.let { SerialBlob(value) }
|
||||
}
|
||||
|
||||
override fun convertToMapped(type: Class<out ByteArray>?, value: Blob?): ByteArray? {
|
||||
return value?.getBytes(1, value.length().toInt())
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
package net.corda.core.schemas.requery.converters
|
||||
|
||||
import io.requery.Converter
|
||||
import net.corda.core.crypto.SecureHash
|
||||
|
||||
/**
|
||||
* Convert from a [SecureHash] to a [String]
|
||||
*/
|
||||
class SecureHashConverter : Converter<SecureHash, String> {
|
||||
|
||||
override fun getMappedType(): Class<SecureHash> = SecureHash::class.java
|
||||
|
||||
override fun getPersistedType(): Class<String> = String::class.java
|
||||
|
||||
/**
|
||||
* SecureHash consists of 32 bytes which need VARCHAR(64) in hex
|
||||
* TODO: think about other hash widths
|
||||
*/
|
||||
override fun getPersistedSize(): Int? = 64
|
||||
|
||||
override fun convertToPersisted(value: SecureHash?): String? {
|
||||
return value?.toString()
|
||||
}
|
||||
|
||||
override fun convertToMapped(type: Class<out SecureHash>, value: String?): SecureHash? {
|
||||
return value?.let { SecureHash.parse(value) }
|
||||
}
|
||||
}
|
@ -134,13 +134,20 @@ class ResolveTransactionsFlowTest {
|
||||
|
||||
@Test
|
||||
fun attachment() {
|
||||
val id = a.services.storageService.attachments.importAttachment("Some test file".toByteArray().opaque().open())
|
||||
// TODO: this operation should not require an explicit transaction
|
||||
val id = databaseTransaction(a.database) {
|
||||
a.services.storageService.attachments.importAttachment("Some test file".toByteArray().opaque().open())
|
||||
}
|
||||
val stx2 = makeTransactions(withAttachment = id).second
|
||||
val p = ResolveTransactionsFlow(stx2, a.info.legalIdentity)
|
||||
val future = b.services.startFlow(p).resultFuture
|
||||
net.runNetwork()
|
||||
future.getOrThrow()
|
||||
assertNotNull(b.services.storageService.attachments.openAttachment(id))
|
||||
|
||||
// TODO: this operation should not require an explicit transaction
|
||||
databaseTransaction(b.database) {
|
||||
assertNotNull(b.services.storageService.attachments.openAttachment(id))
|
||||
}
|
||||
}
|
||||
|
||||
// DOCSTART 2
|
||||
|
Reference in New Issue
Block a user