Merge from O/S

This commit is contained in:
Anthony Keenan 2018-07-05 19:09:25 +01:00
commit 3203bdae97
64 changed files with 1008 additions and 242 deletions

View File

@ -4359,7 +4359,7 @@ public interface net.corda.core.schemas.QueryableState extends net.corda.core.co
@NotNull @NotNull
public abstract Iterable<net.corda.core.schemas.MappedSchema> supportedSchemas() public abstract Iterable<net.corda.core.schemas.MappedSchema> supportedSchemas()
## ##
public interface net.corda.core.schemas.StatePersistable extends java.io.Serializable public interface net.corda.core.schemas.StatePersistable
## ##
public interface net.corda.core.serialization.ClassWhitelist public interface net.corda.core.serialization.ClassWhitelist
public abstract boolean hasListed(Class<?>) public abstract boolean hasListed(Class<?>)

View File

@ -157,10 +157,6 @@ allprojects {
apply plugin: 'org.owasp.dependencycheck' apply plugin: 'org.owasp.dependencycheck'
apply plugin: 'kotlin-allopen' apply plugin: 'kotlin-allopen'
// This line works around a serious performance regression in the Kotlin 1.2.50 gradle plugin, it's fixed in 1.2.51
// TODO: Remove when we upgrade past Kotlin 1.2.51
inspectClassesForKotlinIC.enabled = false
allOpen { allOpen {
annotations( annotations(
"javax.persistence.Entity", "javax.persistence.Entity",

View File

@ -32,9 +32,6 @@ buildscript {
} }
} }
apply plugin: 'maven'
apply plugin: 'java'
repositories { repositories {
mavenLocal() mavenLocal()
mavenCentral() mavenCentral()
@ -61,12 +58,13 @@ allprojects {
} }
} }
dependencies { configurations {
// Add the top-level projects ONLY to the host project. runtime
runtime project.childProjects.values().collect {
project(it.path)
}
} }
// Don't create an empty jar. The plugins are now in child projects. dependencies {
jar.enabled = false // Add the top-level projects ONLY to the host project.
runtime project.childProjects.collect { n, p ->
project(p.path)
}
}

View File

@ -59,6 +59,72 @@ task jarFilter(type: JarFilterTask) {
You can specify as many annotations for each role as you like. The only constraint is that a given You can specify as many annotations for each role as you like. The only constraint is that a given
annotation cannot be assigned to more than one role. annotation cannot be assigned to more than one role.
#### Removing unwanted default parameter values
It is possible to assign non-deterministic expressions as default values for Kotlin constructors and functions. For
example:
```kotlin
data class UniqueIdentifier(val externalId: String? = null, val id: UUID = UUID.randomUUID())
```
The Kotlin compiler will generate _two_ constructors in this case:
```
UniqueIdentifier(String?, UUID)
UniqueIdentifier(String?, UUID, Int, DefaultConstructorMarker)
```
The first constructor is the primary constructor that we would expect (and which we'd like to keep), whereas the
second is a public synthetic constructor that Kotlin applications invoke to handle the different combinations of
default parameter values. Unfortunately, this synthetic constructor is therefore also part of the Kotlin ABI and
so we _cannot_ rewrite the class like this to remove the default values:
```kotlin
// THIS REFACTOR WOULD BREAK THE KOTLIN ABI!
data class UniqueIdentifier(val externalId: String?, val id: UUID) {
constructor(externalId: String?) : this(externalId, UUID.randomUUID())
constructor() : this(null)
}
```
The refactored class would have the following constructors, and would require client applications to be recompiled:
```
UniqueIdentifier(String?, UUID)
UniqueIdentifier(String?)
UniqueIdentifier()
```
We therefore need to keep the default constructor parameters in order to preserve the ABI for the unfiltered code,
which in turn means that `JarFilter` will need to delete only the synthetic constructor and leave the primary
constructor intact. However, Kotlin does not currently allow us to annotate _specific_ constructors - see
[KT-22524](https://youtrack.jetbrains.com/issue/KT-22524). Until it does, `JarFilter` will perform an initial
"sanitising" pass over the JAR file to remove any unwanted annotations from the primary constructors. These unwanted
annotations are configured in the `JarFilter` task definition:
```gradle
task jarFilter(type: JarFilterTask) {
...
annotations {
...
forSanitise = [
"org.testing.DeleteMe"
]
}
}
```
This allows us to annotate the `UniqueIdentifier` class like this:
```kotlin
data class UniqueIdentifier @DeleteMe constructor(val externalId: String? = null, val id: UUID = UUID.randomUUID())
```
to generate these constructors:
```
UniqueIdentifier(String?, UUID)
@DeleteMe UniqueIdentifier(String?, UUID, Int, DefaultConstructorMarker)
```
We currently **do not** sanitise annotations from functions with default parameter values, although (in theory) these
may also be non-deterministic. We will need to extend the sanitation pass to include such functions if/when the need
arises. At the moment, deleting such functions _entirely_ is enough, whereas also deleting a primary constructor means
that we can no longer create instances of that class either.
### The `MetaFixer` task ### The `MetaFixer` task
The `MetaFixer` task updates the `@kotlin.Metadata` annotations by removing references to any functions, The `MetaFixer` task updates the `@kotlin.Metadata` annotations by removing references to any functions,
constructors, properties or nested classes that no longer exist in the byte-code. This is primarily to constructors, properties or nested classes that no longer exist in the byte-code. This is primarily to

View File

@ -26,7 +26,7 @@ class FilterTransformer private constructor (
private val unwantedFields: MutableSet<FieldElement>, private val unwantedFields: MutableSet<FieldElement>,
private val deletedMethods: MutableSet<MethodElement>, private val deletedMethods: MutableSet<MethodElement>,
private val stubbedMethods: MutableSet<MethodElement> private val stubbedMethods: MutableSet<MethodElement>
) : KotlinAwareVisitor(ASM6, visitor, logger, kotlinMetadata), Repeatable<FilterTransformer> { ) : KotlinAfterProcessor(ASM6, visitor, logger, kotlinMetadata), Repeatable<FilterTransformer> {
constructor( constructor(
visitor: ClassVisitor, visitor: ClassVisitor,
logger: Logger, logger: Logger,
@ -47,8 +47,8 @@ class FilterTransformer private constructor (
stubbedMethods = mutableSetOf() stubbedMethods = mutableSetOf()
) )
private var _className: String = "(unknown)" var className: String = "(unknown)"
val className: String get() = _className private set
val isUnwantedClass: Boolean get() = isUnwantedClass(className) val isUnwantedClass: Boolean get() = isUnwantedClass(className)
override val hasUnwantedElements: Boolean override val hasUnwantedElements: Boolean
@ -76,7 +76,7 @@ class FilterTransformer private constructor (
) )
override fun visit(version: Int, access: Int, clsName: String, signature: String?, superName: String?, interfaces: Array<String>?) { override fun visit(version: Int, access: Int, clsName: String, signature: String?, superName: String?, interfaces: Array<String>?) {
_className = clsName className = clsName
logger.info("Class {}", clsName) logger.info("Class {}", clsName)
super.visit(version, access, clsName, signature, superName, interfaces) super.visit(version, access, clsName, signature, superName, interfaces)
} }
@ -172,7 +172,7 @@ class FilterTransformer private constructor (
/** /**
* Removes the deleted methods and fields from the Kotlin Class metadata. * Removes the deleted methods and fields from the Kotlin Class metadata.
*/ */
override fun transformClassMetadata(d1: List<String>, d2: List<String>): List<String> { override fun processClassMetadata(d1: List<String>, d2: List<String>): List<String> {
val partitioned = deletedMethods.groupBy(MethodElement::isConstructor) val partitioned = deletedMethods.groupBy(MethodElement::isConstructor)
val prefix = "$className$" val prefix = "$className$"
return ClassMetadataTransformer( return ClassMetadataTransformer(
@ -191,7 +191,7 @@ class FilterTransformer private constructor (
/** /**
* Removes the deleted methods and fields from the Kotlin Package metadata. * Removes the deleted methods and fields from the Kotlin Package metadata.
*/ */
override fun transformPackageMetadata(d1: List<String>, d2: List<String>): List<String> { override fun processPackageMetadata(d1: List<String>, d2: List<String>): List<String> {
return PackageMetadataTransformer( return PackageMetadataTransformer(
logger = logger, logger = logger,
deletedFields = unwantedFields, deletedFields = unwantedFields,

View File

@ -46,6 +46,9 @@ open class JarFilterTask : DefaultTask() {
@get:Input @get:Input
protected var forRemove: Set<String> = emptySet() protected var forRemove: Set<String> = emptySet()
@get:Input
protected var forSanitise: Set<String> = emptySet()
fun annotations(assign: Closure<List<String>>) { fun annotations(assign: Closure<List<String>>) {
assign.call() assign.call()
} }
@ -90,6 +93,9 @@ open class JarFilterTask : DefaultTask() {
if (forRemove.isNotEmpty()) { if (forRemove.isNotEmpty()) {
logger.info("- Annotations '{}' will be removed entirely", forRemove.joinToString()) logger.info("- Annotations '{}' will be removed entirely", forRemove.joinToString())
} }
if (forSanitise.isNotEmpty()) {
logger.info("- Annotations '{}' will be removed from primary constructors", forSanitise.joinToString())
}
checkDistinctAnnotations() checkDistinctAnnotations()
try { try {
jars.forEach { jar -> jars.forEach { jar ->
@ -136,6 +142,11 @@ open class JarFilterTask : DefaultTask() {
private val source: Path = inFile.toPath() private val source: Path = inFile.toPath()
private val target: Path = toFiltered(inFile).toPath() private val target: Path = toFiltered(inFile).toPath()
private val descriptorsForRemove = toDescriptors(forRemove)
private val descriptorsForDelete = toDescriptors(forDelete)
private val descriptorsForStub = toDescriptors(forStub)
private val descriptorsForSanitising = toDescriptors(forSanitise)
init { init {
Files.deleteIfExists(target) Files.deleteIfExists(target)
} }
@ -145,10 +156,14 @@ open class JarFilterTask : DefaultTask() {
var input = source var input = source
try { try {
if (descriptorsForSanitising.isNotEmpty() && SanitisingPass(input).use { it.run() }) {
input = target.moveToInput()
}
var passes = 1 var passes = 1
while (true) { while (true) {
verbose("Pass {}", passes) verbose("Pass {}", passes)
val isModified = Pass(input).use { it.run() } val isModified = FilterPass(input).use { it.run() }
if (!isModified) { if (!isModified) {
logger.info("No changes after latest pass - exiting.") logger.info("No changes after latest pass - exiting.")
@ -157,9 +172,7 @@ open class JarFilterTask : DefaultTask() {
break break
} }
input = Files.move( input = target.moveToInput()
target, Files.createTempFile(target.parent, "filter-", ".tmp"), REPLACE_EXISTING)
verbose("New input JAR: {}", input)
} }
} catch (e: Exception) { } catch (e: Exception) {
logger.error("Error filtering '{}' elements from {}", ArrayList(forRemove).apply { addAll(forDelete); addAll(forStub) }, input) logger.error("Error filtering '{}' elements from {}", ArrayList(forRemove).apply { addAll(forDelete); addAll(forStub) }, input)
@ -167,14 +180,20 @@ open class JarFilterTask : DefaultTask() {
} }
} }
private inner class Pass(input: Path): Closeable { private fun Path.moveToInput(): Path {
return Files.move(this, Files.createTempFile(parent, "filter-", ".tmp"), REPLACE_EXISTING).also {
verbose("New input JAR: {}", it)
}
}
private abstract inner class Pass(input: Path): Closeable {
/** /**
* Use [ZipFile] instead of [java.util.jar.JarInputStream] because * Use [ZipFile] instead of [java.util.jar.JarInputStream] because
* JarInputStream consumes MANIFEST.MF when it's the first or second entry. * JarInputStream consumes MANIFEST.MF when it's the first or second entry.
*/ */
private val inJar = ZipFile(input.toFile()) protected val inJar = ZipFile(input.toFile())
private val outJar = ZipOutputStream(Files.newOutputStream(target)) protected val outJar = ZipOutputStream(Files.newOutputStream(target))
private var isModified = false protected var isModified = false
@Throws(IOException::class) @Throws(IOException::class)
override fun close() { override fun close() {
@ -183,6 +202,8 @@ open class JarFilterTask : DefaultTask() {
} }
} }
abstract fun transform(inBytes: ByteArray): ByteArray
fun run(): Boolean { fun run(): Boolean {
outJar.setLevel(BEST_COMPRESSION) outJar.setLevel(BEST_COMPRESSION)
outJar.setComment(inJar.comment) outJar.setComment(inJar.comment)
@ -207,16 +228,29 @@ open class JarFilterTask : DefaultTask() {
} }
return isModified return isModified
} }
}
private fun transform(inBytes: ByteArray): ByteArray { private inner class SanitisingPass(input: Path) : Pass(input) {
override fun transform(inBytes: ByteArray): ByteArray {
return ClassWriter(0).let { writer ->
val transformer = SanitisingTransformer(writer, logger, descriptorsForSanitising)
ClassReader(inBytes).accept(transformer, 0)
isModified = isModified or transformer.isModified
writer.toByteArray()
}
}
}
private inner class FilterPass(input: Path) : Pass(input) {
override fun transform(inBytes: ByteArray): ByteArray {
var reader = ClassReader(inBytes) var reader = ClassReader(inBytes)
var writer = ClassWriter(COMPUTE_MAXS) var writer = ClassWriter(COMPUTE_MAXS)
var transformer = FilterTransformer( var transformer = FilterTransformer(
visitor = writer, visitor = writer,
logger = logger, logger = logger,
removeAnnotations = toDescriptors(forRemove), removeAnnotations = descriptorsForRemove,
deleteAnnotations = toDescriptors(forDelete), deleteAnnotations = descriptorsForDelete,
stubAnnotations = toDescriptors(forStub), stubAnnotations = descriptorsForStub,
unwantedClasses = unwantedClasses unwantedClasses = unwantedClasses
) )

View File

@ -1,13 +1,13 @@
package net.corda.gradle.jarfilter package net.corda.gradle.jarfilter
import org.gradle.api.logging.LogLevel
import org.gradle.api.logging.Logger import org.gradle.api.logging.Logger
import org.jetbrains.kotlin.load.java.JvmAnnotationNames.* import org.jetbrains.kotlin.load.java.JvmAnnotationNames.*
import org.objectweb.asm.AnnotationVisitor import org.objectweb.asm.AnnotationVisitor
import org.objectweb.asm.ClassVisitor import org.objectweb.asm.ClassVisitor
/** /**
* Kotlin support: Loads the ProtoBuf data from the [kotlin.Metadata] annotation, * Kotlin support: Loads the ProtoBuf data from the [kotlin.Metadata] annotation.
* or writes new ProtoBuf data that was created during a previous pass.
*/ */
abstract class KotlinAwareVisitor( abstract class KotlinAwareVisitor(
api: Int, api: Int,
@ -27,23 +27,24 @@ abstract class KotlinAwareVisitor(
private var classKind: Int = 0 private var classKind: Int = 0
open val hasUnwantedElements: Boolean get() = kotlinMetadata.isNotEmpty() open val hasUnwantedElements: Boolean get() = kotlinMetadata.isNotEmpty()
protected open val level: LogLevel = LogLevel.INFO
protected abstract fun transformClassMetadata(d1: List<String>, d2: List<String>): List<String> protected abstract fun processClassMetadata(d1: List<String>, d2: List<String>): List<String>
protected abstract fun transformPackageMetadata(d1: List<String>, d2: List<String>): List<String> protected abstract fun processPackageMetadata(d1: List<String>, d2: List<String>): List<String>
protected abstract fun processKotlinAnnotation()
override fun visitAnnotation(descriptor: String, visible: Boolean): AnnotationVisitor? { override fun visitAnnotation(descriptor: String, visible: Boolean): AnnotationVisitor? {
val av = super.visitAnnotation(descriptor, visible) ?: return null val av = super.visitAnnotation(descriptor, visible) ?: return null
return if (descriptor == METADATA_DESC) KotlinMetadataAdaptor(av) else av return if (descriptor == METADATA_DESC) KotlinMetadataAdaptor(av) else av
} }
override fun visitEnd() { protected fun processMetadata() {
super.visitEnd()
if (kotlinMetadata.isNotEmpty()) { if (kotlinMetadata.isNotEmpty()) {
logger.info("- Examining Kotlin @Metadata[k={}]", classKind) logger.log(level, "- Examining Kotlin @Metadata[k={}]", classKind)
val d1 = kotlinMetadata.remove(METADATA_DATA_FIELD_NAME) val d1 = kotlinMetadata.remove(METADATA_DATA_FIELD_NAME)
val d2 = kotlinMetadata.remove(METADATA_STRINGS_FIELD_NAME) val d2 = kotlinMetadata.remove(METADATA_STRINGS_FIELD_NAME)
if (d1 != null && d1.isNotEmpty() && d2 != null) { if (d1 != null && d1.isNotEmpty() && d2 != null) {
transformMetadata(d1, d2).apply { processMetadata(d1, d2).apply {
if (isNotEmpty()) { if (isNotEmpty()) {
kotlinMetadata[METADATA_DATA_FIELD_NAME] = this kotlinMetadata[METADATA_DATA_FIELD_NAME] = this
kotlinMetadata[METADATA_STRINGS_FIELD_NAME] = d2 kotlinMetadata[METADATA_STRINGS_FIELD_NAME] = d2
@ -53,12 +54,12 @@ abstract class KotlinAwareVisitor(
} }
} }
private fun transformMetadata(d1: List<String>, d2: List<String>): List<String> { private fun processMetadata(d1: List<String>, d2: List<String>): List<String> {
return when (classKind) { return when (classKind) {
KOTLIN_CLASS -> transformClassMetadata(d1, d2) KOTLIN_CLASS -> processClassMetadata(d1, d2)
KOTLIN_FILE, KOTLIN_MULTIFILE_PART -> transformPackageMetadata(d1, d2) KOTLIN_FILE, KOTLIN_MULTIFILE_PART -> processPackageMetadata(d1, d2)
KOTLIN_SYNTHETIC -> { KOTLIN_SYNTHETIC -> {
logger.info("-- synthetic class ignored") logger.log(level,"-- synthetic class ignored")
emptyList() emptyList()
} }
else -> { else -> {
@ -66,7 +67,7 @@ abstract class KotlinAwareVisitor(
* For class-kind=4 (i.e. "multi-file"), we currently * For class-kind=4 (i.e. "multi-file"), we currently
* expect d1=[list of multi-file-part classes], d2=null. * expect d1=[list of multi-file-part classes], d2=null.
*/ */
logger.info("-- unsupported class-kind {}", classKind) logger.log(level,"-- unsupported class-kind {}", classKind)
emptyList() emptyList()
} }
} }
@ -91,19 +92,68 @@ abstract class KotlinAwareVisitor(
return null return null
} }
private inner class ArrayAccumulator(av: AnnotationVisitor, private val name: String) : AnnotationVisitor(api, av) { override fun visitEnd() {
private val data: MutableList<String> = mutableListOf() super.visitEnd()
processKotlinAnnotation()
override fun visit(name: String?, value: Any?) {
super.visit(name, value)
data.add(value as String)
}
override fun visitEnd() {
super.visitEnd()
kotlinMetadata[name] = data
logger.debug("-- read @Metadata.{}[{}]", name, data.size)
}
} }
} }
}
private inner class ArrayAccumulator(av: AnnotationVisitor, private val name: String) : AnnotationVisitor(api, av) {
private val data: MutableList<String> = mutableListOf()
override fun visit(name: String?, value: Any?) {
super.visit(name, value)
data.add(value as String)
}
override fun visitEnd() {
super.visitEnd()
kotlinMetadata[name] = data
logger.debug("-- read @Metadata.{}[{}]", name, data.size)
}
}
}
/**
* Loads the ProtoBuf data from the [kotlin.Metadata] annotation, or
* writes new ProtoBuf data that was created during a previous pass.
*/
abstract class KotlinAfterProcessor(
api: Int,
visitor: ClassVisitor,
logger: Logger,
kotlinMetadata: MutableMap<String, List<String>>
) : KotlinAwareVisitor(api, visitor, logger, kotlinMetadata) {
/**
* Process the metadata once we have finished visiting the class.
* This will allow us to rewrite the [kotlin.Metadata] annotation
* in the next visit.
*/
override fun visitEnd() {
super.visitEnd()
processMetadata()
}
/**
* Do nothing immediately after we have parsed [kotlin.Metadata].
*/
final override fun processKotlinAnnotation() {}
}
/**
* Loads the ProtoBuf data from the [kotlin.Metadata] annotation
* and then processes it before visiting the rest of the class.
*/
abstract class KotlinBeforeProcessor(
api: Int,
visitor: ClassVisitor,
logger: Logger,
kotlinMetadata: MutableMap<String, List<String>>
) : KotlinAwareVisitor(api, visitor, logger, kotlinMetadata) {
/**
* Process the ProtoBuf data as soon as we have parsed [kotlin.Metadata].
*/
final override fun processKotlinAnnotation() = processMetadata()
}

View File

@ -17,7 +17,7 @@ class MetaFixerVisitor private constructor(
private val fields: MutableSet<FieldElement>, private val fields: MutableSet<FieldElement>,
private val methods: MutableSet<String>, private val methods: MutableSet<String>,
private val nestedClasses: MutableSet<String> private val nestedClasses: MutableSet<String>
) : KotlinAwareVisitor(ASM6, visitor, logger, kotlinMetadata), Repeatable<MetaFixerVisitor> { ) : KotlinAfterProcessor(ASM6, visitor, logger, kotlinMetadata), Repeatable<MetaFixerVisitor> {
constructor(visitor: ClassVisitor, logger: Logger, classNames: Set<String>) constructor(visitor: ClassVisitor, logger: Logger, classNames: Set<String>)
: this(visitor, logger, mutableMapOf(), classNames, mutableSetOf(), mutableSetOf(), mutableSetOf()) : this(visitor, logger, mutableMapOf(), classNames, mutableSetOf(), mutableSetOf(), mutableSetOf())
@ -52,7 +52,7 @@ class MetaFixerVisitor private constructor(
return super.visitInnerClass(clsName, outerName, innerName, access) return super.visitInnerClass(clsName, outerName, innerName, access)
} }
override fun transformClassMetadata(d1: List<String>, d2: List<String>): List<String> { override fun processClassMetadata(d1: List<String>, d2: List<String>): List<String> {
return ClassMetaFixerTransformer( return ClassMetaFixerTransformer(
logger = logger, logger = logger,
actualFields = fields, actualFields = fields,
@ -64,7 +64,7 @@ class MetaFixerVisitor private constructor(
.transform() .transform()
} }
override fun transformPackageMetadata(d1: List<String>, d2: List<String>): List<String> { override fun processPackageMetadata(d1: List<String>, d2: List<String>): List<String> {
return PackageMetaFixerTransformer( return PackageMetaFixerTransformer(
logger = logger, logger = logger,
actualFields = fields, actualFields = fields,

View File

@ -0,0 +1,81 @@
package net.corda.gradle.jarfilter
import org.gradle.api.logging.LogLevel
import org.gradle.api.logging.Logger
import org.jetbrains.kotlin.metadata.ProtoBuf
import org.jetbrains.kotlin.metadata.deserialization.Flags.*
import org.jetbrains.kotlin.metadata.deserialization.TypeTable
import org.jetbrains.kotlin.metadata.jvm.JvmProtoBuf.*
import org.jetbrains.kotlin.metadata.jvm.deserialization.BitEncoding
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmNameResolver
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmProtoBufUtil.EXTENSION_REGISTRY
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmProtoBufUtil.getJvmConstructorSignature
import org.objectweb.asm.AnnotationVisitor
import org.objectweb.asm.ClassVisitor
import org.objectweb.asm.MethodVisitor
import org.objectweb.asm.Opcodes.*
import java.io.ByteArrayInputStream
/**
* This is (hopefully?!) a temporary solution for classes with [JvmOverloads] constructors.
* We need to be able to annotate ONLY the secondary constructors for such classes, but Kotlin
* will apply any annotation to all constructors equally. Nor can we replace the overloaded
* constructor with individual constructors because this will break ABI compatibility. (Kotlin
* generates a synthetic public constructor to handle default parameter values.)
*
* This transformer identifies a class's primary constructor and removes all of its unwanted annotations.
* It will become superfluous when Kotlin allows us to target only the secondary constructors with our
* filtering annotations in the first place.
*/
class SanitisingTransformer(visitor: ClassVisitor, logger: Logger, private val unwantedAnnotations: Set<String>)
: KotlinBeforeProcessor(ASM6, visitor, logger, mutableMapOf()) {
var isModified: Boolean = false
private set
override val level: LogLevel = LogLevel.DEBUG
private var className: String = "(unknown)"
private var primaryConstructor: MethodElement? = null
override fun processPackageMetadata(d1: List<String>, d2: List<String>): List<String> = emptyList()
override fun processClassMetadata(d1: List<String>, d2: List<String>): List<String> {
val input = ByteArrayInputStream(BitEncoding.decodeBytes(d1.toTypedArray()))
val stringTableTypes = StringTableTypes.parseDelimitedFrom(input, EXTENSION_REGISTRY)
val nameResolver = JvmNameResolver(stringTableTypes, d2.toTypedArray())
val message = ProtoBuf.Class.parseFrom(input, EXTENSION_REGISTRY)
val typeTable = TypeTable(message.typeTable)
for (constructor in message.constructorList) {
if (!IS_SECONDARY.get(constructor.flags)) {
val signature = getJvmConstructorSignature(constructor, nameResolver, typeTable) ?: break
primaryConstructor = MethodElement("<init>", signature.drop("<init>".length))
logger.log(level, "Class {} has primary constructor {}", className, signature)
break
}
}
return emptyList()
}
override fun visit(version: Int, access: Int, clsName: String, signature: String?, superName: String?, interfaces: Array<String>?) {
className = clsName
super.visit(version, access, clsName, signature, superName, interfaces)
}
override fun visitMethod(access: Int, methodName: String, descriptor: String, signature: String?, exceptions: Array<String>?): MethodVisitor? {
val method = MethodElement(methodName, descriptor, access)
val mv = super.visitMethod(access, methodName, descriptor, signature, exceptions) ?: return null
return if (method == primaryConstructor) SanitisingMethodAdapter(mv, method) else mv
}
private inner class SanitisingMethodAdapter(mv: MethodVisitor, private val method: MethodElement) : MethodVisitor(api, mv) {
override fun visitAnnotation(descriptor: String, visible: Boolean): AnnotationVisitor? {
if (unwantedAnnotations.contains(descriptor)) {
logger.info("Sanitising annotation {} from method {}.{}{}", descriptor, className, method.name, method.descriptor)
isModified = true
return null
}
return super.visitAnnotation(descriptor, visible)
}
}
}

View File

@ -73,17 +73,20 @@ internal val String.descriptor: String get() = "L$toPathFormat;"
internal fun <T> ByteArray.execute(visitor: (ClassVisitor) -> T, flags: Int = 0, passes: Int = 2): ByteArray internal fun <T> ByteArray.execute(visitor: (ClassVisitor) -> T, flags: Int = 0, passes: Int = 2): ByteArray
where T : ClassVisitor, where T : ClassVisitor,
T : Repeatable<T> { T : Repeatable<T> {
var reader = ClassReader(this) var bytecode = this
var writer = ClassWriter(flags) var writer = ClassWriter(flags)
val transformer = visitor(writer) var transformer = visitor(writer)
var count = max(passes, 1) var count = max(passes, 1)
reader.accept(transformer, 0) while (--count >= 0) {
while (transformer.hasUnwantedElements && --count > 0) { ClassReader(bytecode).accept(transformer, 0)
reader = ClassReader(writer.toByteArray()) bytecode = writer.toByteArray()
if (!transformer.hasUnwantedElements) break
writer = ClassWriter(flags) writer = ClassWriter(flags)
reader.accept(transformer.recreate(writer), 0) transformer = transformer.recreate(writer)
} }
return writer.toByteArray() return bytecode
} }

View File

@ -36,7 +36,7 @@ class DeleteConstructorTest {
@Test @Test
fun deleteConstructorWithLongParameter() { fun deleteConstructorWithLongParameter() {
val longConstructor = isConstructor(SECONDARY_CONSTRUCTOR_CLASS, hasParam(Long::class)) val longConstructor = isConstructor(SECONDARY_CONSTRUCTOR_CLASS, Long::class)
classLoaderFor(testProject.sourceJar).use { cl -> classLoaderFor(testProject.sourceJar).use { cl ->
cl.load<HasLong>(SECONDARY_CONSTRUCTOR_CLASS).apply { cl.load<HasLong>(SECONDARY_CONSTRUCTOR_CLASS).apply {
@ -58,7 +58,7 @@ class DeleteConstructorTest {
@Test @Test
fun deleteConstructorWithStringParameter() { fun deleteConstructorWithStringParameter() {
val stringConstructor = isConstructor(SECONDARY_CONSTRUCTOR_CLASS, hasParam(String::class)) val stringConstructor = isConstructor(SECONDARY_CONSTRUCTOR_CLASS, String::class)
classLoaderFor(testProject.sourceJar).use { cl -> classLoaderFor(testProject.sourceJar).use { cl ->
cl.load<HasString>(SECONDARY_CONSTRUCTOR_CLASS).apply { cl.load<HasString>(SECONDARY_CONSTRUCTOR_CLASS).apply {
@ -80,7 +80,7 @@ class DeleteConstructorTest {
@Test @Test
fun showUnannotatedConstructorIsUnaffected() { fun showUnannotatedConstructorIsUnaffected() {
val intConstructor = isConstructor(SECONDARY_CONSTRUCTOR_CLASS, hasParam(Int::class)) val intConstructor = isConstructor(SECONDARY_CONSTRUCTOR_CLASS, Int::class)
classLoaderFor(testProject.filteredJar).use { cl -> classLoaderFor(testProject.filteredJar).use { cl ->
cl.load<HasAll>(SECONDARY_CONSTRUCTOR_CLASS).apply { cl.load<HasAll>(SECONDARY_CONSTRUCTOR_CLASS).apply {
getDeclaredConstructor(Int::class.java).newInstance(NUMBER).also { getDeclaredConstructor(Int::class.java).newInstance(NUMBER).also {
@ -96,7 +96,7 @@ class DeleteConstructorTest {
@Test @Test
fun deletePrimaryConstructorWithStringParameter() { fun deletePrimaryConstructorWithStringParameter() {
val stringConstructor = isConstructor(STRING_PRIMARY_CONSTRUCTOR_CLASS, hasParam(String::class)) val stringConstructor = isConstructor(STRING_PRIMARY_CONSTRUCTOR_CLASS, String::class)
classLoaderFor(testProject.sourceJar).use { cl -> classLoaderFor(testProject.sourceJar).use { cl ->
cl.load<HasString>(STRING_PRIMARY_CONSTRUCTOR_CLASS).apply { cl.load<HasString>(STRING_PRIMARY_CONSTRUCTOR_CLASS).apply {
@ -119,7 +119,7 @@ class DeleteConstructorTest {
@Test @Test
fun deletePrimaryConstructorWithLongParameter() { fun deletePrimaryConstructorWithLongParameter() {
val longConstructor = isConstructor(LONG_PRIMARY_CONSTRUCTOR_CLASS, hasParam(Long::class)) val longConstructor = isConstructor(LONG_PRIMARY_CONSTRUCTOR_CLASS, Long::class)
classLoaderFor(testProject.sourceJar).use { cl -> classLoaderFor(testProject.sourceJar).use { cl ->
cl.load<HasLong>(LONG_PRIMARY_CONSTRUCTOR_CLASS).apply { cl.load<HasLong>(LONG_PRIMARY_CONSTRUCTOR_CLASS).apply {
@ -142,7 +142,7 @@ class DeleteConstructorTest {
@Test @Test
fun deletePrimaryConstructorWithIntParameter() { fun deletePrimaryConstructorWithIntParameter() {
val intConstructor = isConstructor(INT_PRIMARY_CONSTRUCTOR_CLASS, hasParam(Int::class)) val intConstructor = isConstructor(INT_PRIMARY_CONSTRUCTOR_CLASS, Int::class)
classLoaderFor(testProject.sourceJar).use { cl -> classLoaderFor(testProject.sourceJar).use { cl ->
cl.load<HasInt>(INT_PRIMARY_CONSTRUCTOR_CLASS).apply { cl.load<HasInt>(INT_PRIMARY_CONSTRUCTOR_CLASS).apply {

View File

@ -0,0 +1,183 @@
package net.corda.gradle.jarfilter
import net.corda.gradle.jarfilter.matcher.*
import net.corda.gradle.unwanted.HasInt
import net.corda.gradle.unwanted.HasLong
import net.corda.gradle.unwanted.HasString
import org.assertj.core.api.Assertions.*
import org.hamcrest.core.IsCollectionContaining.hasItem
import org.junit.Assert.*
import org.junit.ClassRule
import org.junit.Test
import org.junit.rules.RuleChain
import org.junit.rules.TemporaryFolder
import org.junit.rules.TestRule
import kotlin.jvm.kotlin
import kotlin.reflect.full.primaryConstructor
import kotlin.test.assertFailsWith
class SanitiseConstructorTest {
companion object {
private const val COUNT_INITIAL_OVERLOADED = 1
private const val COUNT_INITIAL_MULTIPLE = 2
private val testProjectDir = TemporaryFolder()
private val testProject = JarFilterProject(testProjectDir, "sanitise-constructor")
@ClassRule
@JvmField
val rules: TestRule = RuleChain
.outerRule(testProjectDir)
.around(testProject)
}
@Test
fun deleteOverloadedLongConstructor() = checkClassWithLongParameter(
"net.corda.gradle.HasOverloadedLongConstructor",
COUNT_INITIAL_OVERLOADED
)
@Test
fun deleteMultipleLongConstructor() = checkClassWithLongParameter(
"net.corda.gradle.HasMultipleLongConstructors",
COUNT_INITIAL_MULTIPLE
)
private fun checkClassWithLongParameter(longClass: String, initialCount: Int) {
val longConstructor = isConstructor(longClass, Long::class)
classLoaderFor(testProject.sourceJar).use { cl ->
cl.load<HasLong>(longClass).apply {
getDeclaredConstructor(Long::class.java).newInstance(BIG_NUMBER).also {
assertEquals(BIG_NUMBER, it.longData())
}
kotlin.constructors.apply {
assertThat("<init>(J) not found", this, hasItem(longConstructor))
assertEquals(initialCount, this.size)
}
val primary = kotlin.primaryConstructor ?: throw AssertionError("primary constructor missing")
assertThat(primary.call(BIG_NUMBER).longData()).isEqualTo(BIG_NUMBER)
newInstance().also {
assertEquals(0, it.longData())
}
}
}
classLoaderFor(testProject.filteredJar).use { cl ->
cl.load<HasLong>(longClass).apply {
getDeclaredConstructor(Long::class.java).newInstance(BIG_NUMBER).also {
assertEquals(BIG_NUMBER, it.longData())
}
kotlin.constructors.apply {
assertThat("<init>(J) not found", this, hasItem(longConstructor))
assertEquals(1, this.size)
}
val primary = kotlin.primaryConstructor ?: throw AssertionError("primary constructor missing")
assertThat(primary.call(BIG_NUMBER).longData()).isEqualTo(BIG_NUMBER)
assertFailsWith<NoSuchMethodException> { getDeclaredConstructor() }
}
}
}
@Test
fun deleteOverloadedIntConstructor() = checkClassWithIntParameter(
"net.corda.gradle.HasOverloadedIntConstructor",
COUNT_INITIAL_OVERLOADED
)
@Test
fun deleteMultipleIntConstructor() = checkClassWithIntParameter(
"net.corda.gradle.HasMultipleIntConstructors",
COUNT_INITIAL_MULTIPLE
)
private fun checkClassWithIntParameter(intClass: String, initialCount: Int) {
val intConstructor = isConstructor(intClass, Int::class)
classLoaderFor(testProject.sourceJar).use { cl ->
cl.load<HasInt>(intClass).apply {
getDeclaredConstructor(Int::class.java).newInstance(NUMBER).also {
assertEquals(NUMBER, it.intData())
}
kotlin.constructors.apply {
assertThat("<init>(I) not found", this, hasItem(intConstructor))
assertEquals(initialCount, this.size)
}
val primary = kotlin.primaryConstructor ?: throw AssertionError("primary constructor missing")
assertThat(primary.call(NUMBER).intData()).isEqualTo(NUMBER)
//assertThat("", constructors, hasItem(isConstructor(""))
newInstance().also {
assertEquals(0, it.intData())
}
}
}
classLoaderFor(testProject.filteredJar).use { cl ->
cl.load<HasInt>(intClass).apply {
getDeclaredConstructor(Int::class.java).newInstance(NUMBER).also {
assertEquals(NUMBER, it.intData())
}
kotlin.constructors.apply {
assertThat("<init>(I) not found", this, hasItem(intConstructor))
assertEquals(1, this.size)
}
val primary = kotlin.primaryConstructor ?: throw AssertionError("primary constructor missing")
assertThat(primary.call(NUMBER).intData()).isEqualTo(NUMBER)
assertFailsWith<NoSuchMethodException> { getDeclaredConstructor() }
}
}
}
@Test
fun deleteOverloadedStringConstructor() = checkClassWithStringParameter(
"net.corda.gradle.HasOverloadedStringConstructor",
COUNT_INITIAL_OVERLOADED
)
@Test
fun deleteMultipleStringConstructor() = checkClassWithStringParameter(
"net.corda.gradle.HasMultipleStringConstructors",
COUNT_INITIAL_MULTIPLE
)
private fun checkClassWithStringParameter(stringClass: String, initialCount: Int) {
val stringConstructor = isConstructor(stringClass, String::class)
classLoaderFor(testProject.sourceJar).use { cl ->
cl.load<HasString>(stringClass).apply {
getDeclaredConstructor(String::class.java).newInstance(MESSAGE).also {
assertEquals(MESSAGE, it.stringData())
}
kotlin.constructors.apply {
assertThat("<init>(String) not found", this, hasItem(stringConstructor))
assertEquals(initialCount, this.size)
}
val primary = kotlin.primaryConstructor ?: throw AssertionError("primary constructor missing")
assertThat(primary.call(MESSAGE).stringData()).isEqualTo(MESSAGE)
newInstance().also {
assertEquals(DEFAULT_MESSAGE, it.stringData())
}
}
}
classLoaderFor(testProject.filteredJar).use { cl ->
cl.load<HasString>(stringClass).apply {
getDeclaredConstructor(String::class.java).newInstance(MESSAGE).also {
assertEquals(MESSAGE, it.stringData())
}
kotlin.constructors.apply {
assertThat("<init>(String) not found", this, hasItem(stringConstructor))
assertEquals(1, this.size)
}
val primary = kotlin.primaryConstructor ?: throw AssertionError("primary constructor missing")
assertThat(primary.call(MESSAGE).stringData()).isEqualTo(MESSAGE)
assertFailsWith<NoSuchMethodException> { getDeclaredConstructor() }
}
}
}
}

View File

@ -13,10 +13,12 @@ fun isMethod(name: Matcher<in String>, returnType: Matcher<in Class<*>>, vararg
} }
fun isMethod(name: String, returnType: Class<*>, vararg parameters: Class<*>): Matcher<Method> { fun isMethod(name: String, returnType: Class<*>, vararg parameters: Class<*>): Matcher<Method> {
return isMethod(equalTo(name), equalTo(returnType), *parameters.map(::equalTo).toTypedArray()) return isMethod(equalTo(name), equalTo(returnType), *parameters.toMatchers())
} }
val <T: Any> KClass<T>.javaDeclaredMethods: List<Method> get() = java.declaredMethods.toList() private fun Array<out Class<*>>.toMatchers() = map(::equalTo).toTypedArray()
val KClass<*>.javaDeclaredMethods: List<Method> get() = java.declaredMethods.toList()
/** /**
* Matcher logic for a Java [Method] object. Also applicable to constructors. * Matcher logic for a Java [Method] object. Also applicable to constructors.

View File

@ -17,7 +17,7 @@ fun isFunction(name: Matcher<in String>, returnType: Matcher<in String>, vararg
} }
fun isFunction(name: String, returnType: KClass<*>, vararg parameters: KClass<*>): Matcher<KFunction<*>> { fun isFunction(name: String, returnType: KClass<*>, vararg parameters: KClass<*>): Matcher<KFunction<*>> {
return isFunction(equalTo(name), matches(returnType), *parameters.map(::hasParam).toTypedArray()) return isFunction(equalTo(name), matches(returnType), *parameters.toMatchers())
} }
fun isConstructor(returnType: Matcher<in String>, vararg parameters: Matcher<in KParameter>): Matcher<KFunction<*>> { fun isConstructor(returnType: Matcher<in String>, vararg parameters: Matcher<in KParameter>): Matcher<KFunction<*>> {
@ -25,11 +25,11 @@ fun isConstructor(returnType: Matcher<in String>, vararg parameters: Matcher<in
} }
fun isConstructor(returnType: KClass<*>, vararg parameters: KClass<*>): Matcher<KFunction<*>> { fun isConstructor(returnType: KClass<*>, vararg parameters: KClass<*>): Matcher<KFunction<*>> {
return isConstructor(matches(returnType), *parameters.map(::hasParam).toTypedArray()) return isConstructor(matches(returnType), *parameters.toMatchers())
} }
fun isConstructor(returnType: String, vararg parameters: Matcher<in KParameter>): Matcher<KFunction<*>> { fun isConstructor(returnType: String, vararg parameters: KClass<*>): Matcher<KFunction<*>> {
return isConstructor(equalTo(returnType), *parameters) return isConstructor(equalTo(returnType), *parameters.toMatchers())
} }
fun hasParam(type: Matcher<in String>): Matcher<KParameter> = KParameterMatcher(type) fun hasParam(type: Matcher<in String>): Matcher<KParameter> = KParameterMatcher(type)
@ -44,6 +44,8 @@ fun isClass(name: String): Matcher<KClass<*>> = KClassMatcher(equalTo(name))
fun matches(type: KClass<*>): Matcher<in String> = equalTo(type.qualifiedName) fun matches(type: KClass<*>): Matcher<in String> = equalTo(type.qualifiedName)
private fun Array<out KClass<*>>.toMatchers() = map(::hasParam).toTypedArray()
/** /**
* Matcher logic for a Kotlin [KFunction] object. Also applicable to constructors. * Matcher logic for a Kotlin [KFunction] object. Also applicable to constructors.
*/ */

View File

@ -0,0 +1,34 @@
plugins {
id 'org.jetbrains.kotlin.jvm' version '$kotlin_version'
id 'net.corda.plugins.jar-filter'
}
apply from: 'repositories.gradle'
sourceSets {
main {
kotlin {
srcDir files(
'../resources/test/sanitise-constructor/kotlin',
'../resources/test/annotations/kotlin'
)
}
}
}
dependencies {
compile 'org.jetbrains.kotlin:kotlin-stdlib-jdk8'
compileOnly files('../../unwanteds/build/libs/unwanteds.jar')
}
jar {
baseName = 'sanitise-constructor'
}
import net.corda.gradle.jarfilter.JarFilterTask
task jarFilter(type: JarFilterTask) {
jars jar
annotations {
forDelete = ["net.corda.gradle.jarfilter.DeleteMe"]
forSanitise = ["net.corda.gradle.jarfilter.DeleteMe"]
}
}

View File

@ -0,0 +1,34 @@
@file:Suppress("UNUSED")
package net.corda.gradle
import net.corda.gradle.jarfilter.DeleteMe
import net.corda.gradle.unwanted.*
private const val DEFAULT_MESSAGE = "<default-value>"
class HasOverloadedStringConstructor @JvmOverloads @DeleteMe constructor(private val message: String = DEFAULT_MESSAGE) : HasString {
override fun stringData(): String = message
}
class HasOverloadedLongConstructor @JvmOverloads @DeleteMe constructor(private val data: Long = 0) : HasLong {
override fun longData(): Long = data
}
class HasOverloadedIntConstructor @JvmOverloads @DeleteMe constructor(private val data: Int = 0) : HasInt {
override fun intData(): Int = data
}
class HasMultipleStringConstructors(private val message: String) : HasString {
@DeleteMe constructor() : this(DEFAULT_MESSAGE)
override fun stringData(): String = message
}
class HasMultipleLongConstructors(private val data: Long) : HasLong {
@DeleteMe constructor() : this(0)
override fun longData(): Long = data
}
class HasMultipleIntConstructors(private val data: Int) : HasInt {
@DeleteMe constructor() : this(0)
override fun intData(): Int = data
}

View File

@ -107,7 +107,7 @@ open class StringToMethodCallParser<in T : Any> @JvmOverloads constructor(
protected val methodMap: Multimap<String, Method> = methodsFromType(targetType) protected val methodMap: Multimap<String, Method> = methodsFromType(targetType)
/** A map of method name to parameter names for the target type. */ /** A map of method name to parameter names for the target type. */
val methodParamNames: Map<String, List<String>> = targetType.declaredMethods.mapNotNull { val methodParamNames: Map<String, List<String>> = targetType.declaredMethods.filterNot(Method::isSynthetic).mapNotNull {
try { try {
it.name to paramNamesFromMethod(it) it.name to paramNamesFromMethod(it)
} catch (e: KotlinReflectionInternalError) { } catch (e: KotlinReflectionInternalError) {

View File

@ -27,7 +27,7 @@ import net.corda.finance.flows.CashPaymentFlow.PaymentRequest
open class EventGenerator(val parties: List<Party>, val currencies: List<Currency>, val notary: Party) { open class EventGenerator(val parties: List<Party>, val currencies: List<Currency>, val notary: Party) {
protected val partyGenerator = Generator.pickOne(parties) protected val partyGenerator = Generator.pickOne(parties)
protected val issueRefGenerator = Generator.intRange(0, 1).map { number -> OpaqueBytes(ByteArray(1, { number.toByte() })) } protected val issueRefGenerator = Generator.intRange(0, 1).map { number -> OpaqueBytes.of(number.toByte()) }
protected val amountGenerator = Generator.longRange(10000, 1000000) protected val amountGenerator = Generator.longRange(10000, 1000000)
protected val currencyGenerator = Generator.pickOne(currencies) protected val currencyGenerator = Generator.pickOne(currencies)
protected val currencyMap: MutableMap<Currency, Long> = mutableMapOf(USD to 0L, GBP to 0L) // Used for estimation of how much money we have in general. protected val currencyMap: MutableMap<Currency, Long> = mutableMapOf(USD to 0L, GBP to 0L) // Used for estimation of how much money we have in general.

View File

@ -54,7 +54,7 @@ class FlowsExecutionModeRpcTest : IntegrationTest() {
assumeFalse(System.getProperty("os.name").toLowerCase().startsWith("win")) assumeFalse(System.getProperty("os.name").toLowerCase().startsWith("win"))
val user = User("mark", "dadada", setOf(invokeRpc("setFlowsDrainingModeEnabled"), invokeRpc("isFlowsDrainingModeEnabled"))) val user = User("mark", "dadada", setOf(invokeRpc("setFlowsDrainingModeEnabled"), invokeRpc("isFlowsDrainingModeEnabled")))
driver(DriverParameters(isDebug = true, startNodesInProcess = true)) { driver(DriverParameters(isDebug = true, inMemoryDB = false, startNodesInProcess = true)) {
val nodeName = { val nodeName = {
val nodeHandle = startNode(rpcUsers = listOf(user)).getOrThrow() val nodeHandle = startNode(rpcUsers = listOf(user)).getOrThrow()
val nodeName = nodeHandle.nodeInfo.chooseIdentity().name val nodeName = nodeHandle.nodeInfo.chooseIdentity().name

View File

@ -9,7 +9,7 @@
# #
gradlePluginsVersion=4.0.25 gradlePluginsVersion=4.0.25
kotlinVersion=1.2.50 kotlinVersion=1.2.51
platformVersion=4 platformVersion=4
guavaVersion=21.0 guavaVersion=21.0
proguardVersion=6.0.3 proguardVersion=6.0.3

View File

@ -86,7 +86,7 @@ task predeterminise(type: ProGuardTask) {
keepattributes '*' keepattributes '*'
keepdirectories keepdirectories
dontwarn '**$1$1' dontwarn '**$1$1,org.hibernate.annotations.*'
dontpreverify dontpreverify
dontobfuscate dontobfuscate
dontoptimize dontoptimize
@ -112,7 +112,11 @@ task jarFilter(type: JarFilterTask) {
"net.corda.core.StubOutForDJVM" "net.corda.core.StubOutForDJVM"
] ]
forRemove = [ forRemove = [
"co.paralleluniverse.fibers.Suspendable" "co.paralleluniverse.fibers.Suspendable",
"org.hibernate.annotations.Immutable"
]
forSanitise = [
"net.corda.core.DeleteForDJVM"
] ]
} }
} }

View File

@ -23,3 +23,6 @@ dependencies {
testCompile "org.assertj:assertj-core:$assertj_version" testCompile "org.assertj:assertj-core:$assertj_version"
testCompile "junit:junit:$junit_version" testCompile "junit:junit:$junit_version"
} }
// This module has no artifact and only contains tests.
jar.enabled = false

View File

@ -5,7 +5,6 @@ import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter
import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder
import java.io.OutputStream import java.io.OutputStream
import java.math.BigInteger
import java.math.BigInteger.TEN import java.math.BigInteger.TEN
import java.security.KeyPairGenerator import java.security.KeyPairGenerator
import java.security.KeyStore import java.security.KeyStore

View File

@ -11,7 +11,7 @@ class PrivacySaltTest {
@Test @Test
fun testValidSalt() { fun testValidSalt() {
PrivacySalt(ByteArray(SALT_SIZE, { 0x14 })) PrivacySalt(ByteArray(SALT_SIZE) { 0x14 })
} }
@Test @Test
@ -22,13 +22,13 @@ class PrivacySaltTest {
@Test @Test
fun testTooShortPrivacySalt() { fun testTooShortPrivacySalt() {
val ex = assertFailsWith<IllegalArgumentException> { PrivacySalt(ByteArray(SALT_SIZE - 1, { 0x7f })) } val ex = assertFailsWith<IllegalArgumentException> { PrivacySalt(ByteArray(SALT_SIZE - 1) { 0x7f }) }
assertEquals("Privacy salt should be 32 bytes.", ex.message) assertEquals("Privacy salt should be 32 bytes.", ex.message)
} }
@Test @Test
fun testTooLongPrivacySalt() { fun testTooLongPrivacySalt() {
val ex = assertFailsWith<IllegalArgumentException> { PrivacySalt(ByteArray(SALT_SIZE + 1, { 0x7f })) } val ex = assertFailsWith<IllegalArgumentException> { PrivacySalt(ByteArray(SALT_SIZE + 1) { 0x7f }) }
assertEquals("Privacy salt should be 32 bytes.", ex.message) assertEquals("Privacy salt should be 32 bytes.", ex.message)
} }
} }

View File

@ -0,0 +1,37 @@
package net.corda.deterministic.contracts
import net.corda.core.contracts.UniqueIdentifier
import org.assertj.core.api.Assertions.assertThat
import org.junit.Assert.*
import org.junit.Test
import java.util.*
import kotlin.reflect.full.primaryConstructor
import kotlin.test.assertFailsWith
class UniqueIdentifierTest {
private companion object {
private const val NAME = "MyName"
private val TEST_UUID: UUID = UUID.fromString("00000000-1111-2222-3333-444444444444")
}
@Test
fun testNewInstance() {
val id = UniqueIdentifier(NAME, TEST_UUID)
assertEquals("${NAME}_$TEST_UUID", id.toString())
assertEquals(NAME, id.externalId)
assertEquals(TEST_UUID, id.id)
}
@Test
fun testPrimaryConstructor() {
val primary = UniqueIdentifier::class.primaryConstructor ?: throw AssertionError("primary constructor missing")
assertThat(primary.call(NAME, TEST_UUID)).isEqualTo(UniqueIdentifier(NAME, TEST_UUID))
}
@Test
fun testConstructors() {
assertEquals(1, UniqueIdentifier::class.constructors.size)
val ex = assertFailsWith<IllegalArgumentException> { UniqueIdentifier::class.constructors.first().call() }
assertThat(ex).hasMessage("Callable expects 2 arguments, but 0 were provided.")
}
}

View File

@ -33,7 +33,7 @@ class SecureHashTest {
@Test @Test
fun testConstants() { fun testConstants() {
assertArrayEquals(SecureHash.zeroHash.bytes, ByteArray(32)) assertArrayEquals(SecureHash.zeroHash.bytes, ByteArray(32))
assertArrayEquals(SecureHash.allOnesHash.bytes, ByteArray(32, { 0xFF.toByte() })) assertArrayEquals(SecureHash.allOnesHash.bytes, ByteArray(32) { 0xFF.toByte() })
} }
} }

View File

@ -30,10 +30,7 @@ import java.util.*
*/ */
@CordaSerializable @CordaSerializable
@KeepForDJVM @KeepForDJVM
data class UniqueIdentifier(val externalId: String?, val id: UUID) : Comparable<UniqueIdentifier> { data class UniqueIdentifier @JvmOverloads @DeleteForDJVM constructor(val externalId: String? = null, val id: UUID = UUID.randomUUID()) : Comparable<UniqueIdentifier> {
@DeleteForDJVM constructor(externalId: String?) : this(externalId, UUID.randomUUID())
@DeleteForDJVM constructor() : this(null)
override fun toString(): String = if (externalId != null) "${externalId}_$id" else id.toString() override fun toString(): String = if (externalId != null) "${externalId}_$id" else id.toString()
companion object { companion object {

View File

@ -104,7 +104,7 @@ sealed class SecureHash(bytes: ByteArray) : OpaqueBytes(bytes) {
* This field provides more intuitive access from Java. * This field provides more intuitive access from Java.
*/ */
@JvmField @JvmField
val zeroHash: SHA256 = SecureHash.SHA256(ByteArray(32, { 0.toByte() })) val zeroHash: SHA256 = SecureHash.SHA256(ByteArray(32) { 0.toByte() })
/** /**
* A SHA-256 hash value consisting of 32 0x00 bytes. * A SHA-256 hash value consisting of 32 0x00 bytes.
@ -118,7 +118,7 @@ sealed class SecureHash(bytes: ByteArray) : OpaqueBytes(bytes) {
* This field provides more intuitive access from Java. * This field provides more intuitive access from Java.
*/ */
@JvmField @JvmField
val allOnesHash: SHA256 = SecureHash.SHA256(ByteArray(32, { 255.toByte() })) val allOnesHash: SHA256 = SecureHash.SHA256(ByteArray(32) { 255.toByte() })
/** /**
* A SHA-256 hash value consisting of 32 0xFF bytes. * A SHA-256 hash value consisting of 32 0xFF bytes.

View File

@ -15,6 +15,7 @@ import net.corda.core.contracts.ContractState
import net.corda.core.contracts.StateRef import net.corda.core.contracts.StateRef
import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.CordaSerializable
import net.corda.core.utilities.toHexString import net.corda.core.utilities.toHexString
import org.hibernate.annotations.Immutable
import java.io.Serializable import java.io.Serializable
import javax.persistence.Column import javax.persistence.Column
import javax.persistence.Embeddable import javax.persistence.Embeddable
@ -100,6 +101,7 @@ class PersistentState(@EmbeddedId var stateRef: PersistentStateRef? = null) : St
*/ */
@KeepForDJVM @KeepForDJVM
@Embeddable @Embeddable
@Immutable
data class PersistentStateRef( data class PersistentStateRef(
@Column(name = "transaction_id", length = 64, nullable = false) @Column(name = "transaction_id", length = 64, nullable = false)
var txId: String, var txId: String,
@ -114,7 +116,8 @@ data class PersistentStateRef(
* Marker interface to denote a persistable Corda state entity that will always have a transaction id and index * Marker interface to denote a persistable Corda state entity that will always have a transaction id and index
*/ */
@KeepForDJVM @KeepForDJVM
interface StatePersistable : Serializable interface StatePersistable
object MappedSchemaValidator { object MappedSchemaValidator {
fun fieldsFromOtherMappedSchema(schema: MappedSchema) : List<SchemaCrossReferenceReport> = fun fieldsFromOtherMappedSchema(schema: MappedSchema) : List<SchemaCrossReferenceReport> =
schema.mappedTypes.map { entity -> schema.mappedTypes.map { entity ->

View File

@ -12,6 +12,9 @@ Unreleased
and within that file the ``bridgeMode`` propety has been modified to ``firewallMode`` for overall consistency. and within that file the ``bridgeMode`` propety has been modified to ``firewallMode`` for overall consistency.
This will be a breaking change for early adopters and their deployments, but hopefully will be more future proof. This will be a breaking change for early adopters and their deployments, but hopefully will be more future proof.
* The Corda JPA entities no longer implement java.io.Serializable, as this was causing persistence errors in obscure cases.
Java serialization is disabled globally in the node, but in the unlikely event you were relying on these types being Java serializable please contact us.
* Remove all references to the out-of-process transaction verification. * Remove all references to the out-of-process transaction verification.
* Introduced a hierarchy of ``DatabaseMigrationException``s, allowing ``NodeStartup`` to gracefully inform users of problems related to database migrations before exiting with a non-zero code. * Introduced a hierarchy of ``DatabaseMigrationException``s, allowing ``NodeStartup`` to gracefully inform users of problems related to database migrations before exiting with a non-zero code.

View File

@ -12,6 +12,7 @@ Nodes
clientrpc clientrpc
shell shell
node-database node-database
node-database-access-h2
node-administration node-administration
node-operations-upgrading node-operations-upgrading
node-operations-upgrade-cordapps node-operations-upgrade-cordapps

View File

Before

Width:  |  Height:  |  Size: 74 KiB

After

Width:  |  Height:  |  Size: 74 KiB

View File

@ -10,34 +10,34 @@ Kubernetes for parts specific to that.
The main idea behind the infrastructure is to provide a highly available cluster of enclave services (hosts) which can The main idea behind the infrastructure is to provide a highly available cluster of enclave services (hosts) which can
serve enclaves on demand. It provides an interface for enclave business logic that's agnostic with regards to the serve enclaves on demand. It provides an interface for enclave business logic that's agnostic with regards to the
infrastructure, similar to [serverless architectures](details/serverless.md). The enclaves will use an opaque reference infrastructure, similar to serverless architectures. The enclaves will use an opaque reference
to other enclaves or services in the form of [enclave channels](details/channels.md). Channels hides attestation details to other enclaves or services in the form of enclave channels. Channels hides attestation details
and provide a loose coupling between enclave/non-enclave functionality and specific enclave images/services implementing and provide a loose coupling between enclave/non-enclave functionality and specific enclave images/services implementing
it. This loose coupling allows easier upgrade of enclaves, relaxed trust (whitelisting), dynamic deployment, and it. This loose coupling allows easier upgrade of enclaves, relaxed trust (whitelisting), dynamic deployment, and
horizontal scaling as we can spin up enclaves dynamically on demand when a channel is requested. horizontal scaling as we can spin up enclaves dynamically on demand when a channel is requested.
For more information see:
.. toctree::
:maxdepth: 1
details/serverless.md
details/channels.md
## Infrastructure components ## Infrastructure components
Here are the major components of the infrastructure. Note that this doesn't include business logic specific Here are the major components of the infrastructure. Note that this doesn't include business logic specific
infrastructure pieces (like ORAM blob storage for Corda privacy model integration). infrastructure pieces (like ORAM blob storage for Corda privacy model integration).
* [**Distributed key-value store**](details/kv-store.md): .. toctree::
Responsible for maintaining metadata about enclaves, hosts, sealed secrets and CPU locality. :maxdepth: 1
* [**Discovery service**](details/discovery.md) details/kv-store.md
Responsible for resolving an enclave channel to a specific enclave image and a host that can serve it using the details/discovery.md
metadata in the key-value store. details/host.md
details/enclave-storage.md
* [**Enclave host**](details/host.md): details/ias-proxy.md
This is a service capable of serving enclaves and driving the underlying traffic. Third party components like Intel's
SGX driver and aesmd also belong here.
* [**Enclave storage**](details/enclave-storage.md):
Responsible for serving enclave images to hosts. This is a simple static content server.
* [**IAS proxy**](details/ias-proxy.md):
This is an unfortunate necessity because of Intel's requirement to do mutual TLS with their services.
## Infrastructure interactions ## Infrastructure interactions
* **Enclave deployment**: * **Enclave deployment**:
@ -54,17 +54,23 @@ infrastructure pieces (like ORAM blob storage for Corda privacy model integratio
## Decisions to be made ## Decisions to be made
* [**Strategic roadmap**](decisions/roadmap.md) .. toctree::
* [**CPU certification method**](decisions/certification.md) :maxdepth: 1
* [**Enclave language of choice**](decisions/enclave-language.md)
* [**Key-value store**](decisions/kv-store.md) decisions/roadmap.md
decisions/certification.md
decisions/enclave-language.md
decisions/kv-store.md
## Further details ## Further details
* [**Attestation**](details/attestation.md) .. toctree::
* [**Calendar time for data at rest**](details/time.md) :maxdepth: 1
* [**Enclave deployment**](details/enclave-deployment.md)
details/attestation.md
details/time.md
details/enclave-deployment.md
## Example deployment ## Example deployment
This is an example of how two Corda parties may use the above infrastructure. In this example R3 is hosting the IAS This is an example of how two Corda parties may use the above infrastructure. In this example R3 is hosting the IAS
@ -75,4 +81,4 @@ the enclave image store (although R3 will need to have a repository of the signe
We may also decide to go the other way and have R3 host the enclave hosts and the discovery service, shared between We may also decide to go the other way and have R3 host the enclave hosts and the discovery service, shared between
parties (if e.g. they don't have access to/want to maintain SGX capable boxes). parties (if e.g. they don't have access to/want to maintain SGX capable boxes).
![Example SGX deployment](Example%20SGX%20deployment.png) ![Example SGX deployment](ExampleSGXdeployment.png)

View File

@ -62,15 +62,16 @@ The build generates each of Corda's deterministic JARs in six steps:
@CordaSerializable @CordaSerializable
@KeepForDJVM @KeepForDJVM
data class UniqueIdentifier(val externalId: String?, val id: UUID) : Comparable<UniqueIdentifier> { data class UniqueIdentifier @JvmOverloads @DeleteForDJVM constructor(
@DeleteForDJVM constructor(externalId: String?) : this(externalId, UUID.randomUUID()) val externalId: String? = null,
@DeleteForDJVM constructor() : this(null) val id: UUID = UUID.randomUUID()
) : Comparable<UniqueIdentifier> {
... ...
} }
.. ..
While CorDapps will definitely need to handle ``UniqueIdentifier`` objects, both of the secondary constructors While CorDapps will definitely need to handle ``UniqueIdentifier`` objects, all of the secondary constructors
generate a new random ``UUID`` and so are non-deterministic. Hence the next "determinising" step is to pass the generate a new random ``UUID`` and so are non-deterministic. Hence the next "determinising" step is to pass the
classes to the ``JarFilter`` tool, which strips out all of the elements which have been annotated as classes to the ``JarFilter`` tool, which strips out all of the elements which have been annotated as
``@DeleteForDJVM`` and stubs out any functions annotated with ``@StubOutForDJVM``. (Stub functions that ``@DeleteForDJVM`` and stubs out any functions annotated with ``@StubOutForDJVM``. (Stub functions that
@ -270,11 +271,34 @@ Non-Deterministic Elements
.. ..
You must also ensure that a deterministic class's primary constructor does not reference any classes that are You must also ensure that a deterministic class's primary constructor does not reference any classes that are
not available in the deterministic ``rt.jar``, nor have any non-deterministic default parameter values such as not available in the deterministic ``rt.jar``. The biggest risk here would be that ``JarFilter`` would delete the
``UUID.randomUUID()``. The biggest risk here would be that ``JarFilter`` would delete the primary constructor primary constructor and that the class could no longer be instantiated, although ``JarFilter`` will print a warning
and that the class could no longer be instantiated, although ``JarFilter`` will print a warning in this case. in this case. However, it is also likely that the "determinised" class would have a different serialisation
However, it is also likely that the "determinised" class would have a different serialisation signature than signature than its non-deterministic version and so become unserialisable on the deterministic JVM.
its non-deterministic version and so become unserialisable on the deterministic JVM.
Primary constructors that have non-deterministic default parameter values must still be annotated as
``@DeleteForDJVM`` because they cannot be refactored without breaking Corda's binary interface. The Kotlin compiler
will automatically apply this ``@DeleteForDJVM`` annotation - along with any others - to all of the class's
secondary constructors too. The ``JarFilter`` plugin can then remove the ``@DeleteForDJVM`` annotation from the
primary constructor so that it can subsequently delete only the secondary constructors.
The annotations that ``JarFilter`` will "sanitise" from primary constructors in this way are listed in the plugin's
configuration block, e.g.
.. sourcecode:: groovy
task jarFilter(type: JarFilterTask) {
...
annotations {
...
forSanitise = [
"net.corda.core.DeleteForDJVM"
]
}
}
..
Be aware that package-scoped Kotlin properties are all initialised within a common ``<clinit>`` block inside Be aware that package-scoped Kotlin properties are all initialised within a common ``<clinit>`` block inside
their host ``.class`` file. This means that when ``JarFilter`` deletes these properties, it cannot also remove their host ``.class`` file. This means that when ``JarFilter`` deletes these properties, it cannot also remove

View File

@ -60,6 +60,7 @@ application development please continue to refer to `the main project documentat
azure-vm.rst azure-vm.rst
aws-vm.rst aws-vm.rst
certificate-revocation certificate-revocation
loadtesting.rst
.. Documentation is not included in the pdf unless it is included in a toctree somewhere .. Documentation is not included in the pdf unless it is included in a toctree somewhere
.. only:: pdfmode .. only:: pdfmode
@ -68,4 +69,4 @@ application development please continue to refer to `the main project documentat
:caption: Other documentation :caption: Other documentation
deterministic-modules.rst deterministic-modules.rst
changelog.rst changelog.rst

View File

@ -82,8 +82,10 @@ This command line will start the node with JMX metrics accessible via HTTP on po
See :ref:`Monitoring your node <jolokia_ref>` for further details. See :ref:`Monitoring your node <jolokia_ref>` for further details.
Starting all nodes at once from the command line Starting all nodes at once on a local machine from the command line
------------------------------------------------ -------------------------------------------------------------------
.. _starting-all-nodes-at-once:
Native Native
~~~~~~ ~~~~~~
@ -115,7 +117,35 @@ After the nodes are started up, you can use ``docker ps`` command to see how the
and `Docker Compose documentation <https://docs.docker.com/compose/install/>`_ for installation instructions for all and `Docker Compose documentation <https://docs.docker.com/compose/install/>`_ for installation instructions for all
major operating systems. major operating systems.
Starting all nodes at once on a remote machine from the command line
--------------------------------------------------------------------
By default, ``Cordform`` expects the nodes it generates to be run on the same machine where they were generated.
In order to run the nodes remotely, the nodes can be deployed locally and then copied to a remote server.
If after copying the nodes to the remote machine you encounter errors related to ``localhost`` resolution, you will additionally need to follow the steps below.
To create nodes locally and run on a remote machine perform the following steps:
1. Configure Cordform task and deploy the nodes locally as described in :doc:`generating-a-node`.
2. Copy the generated directory structure to a remote machine using e.g. Secure Copy.
3. Optionally, bootstrap the network on the remote machine.
This is optional step when a remote machine doesn't accept ``localhost`` addresses, or the generated nodes are configured to run on another host's IP address.
If required change host addresses in top level configuration files ``[NODE NAME]_node.conf`` for entries ``p2pAddress`` , ``rpcSettings.address`` and ``rpcSettings.adminAddress``.
Run the network bootstrapper tool to regenerate the nodes network map (see for more explanation :doc:`network-bootstrapper`):
``java -jar corda-tools-network-bootstrapper-Master.jar --dir <nodes-root-dir>``
4. Run nodes on the remote machine using :ref:`runnodes command <starting-all-nodes-at-once>`.
The above steps create a test deployment as ``deployNodes`` Gradle task would do on a local machine.
Database migrations Database migrations
------------------- -------------------
Depending on the versions of Corda and of the CorDapps used, database migration scripts might need to run before a node is able to start. Depending on the versions of Corda and of the CorDapps used, database migration scripts might need to run before a node is able to start.
For more information refer to :doc:`database-management`. For more information refer to :doc:`database-management`.

View File

@ -2,7 +2,6 @@ Serialization
============= =============
.. toctree:: .. toctree::
:caption: Other docs :caption: Other docs
:maxdepth: 1 :maxdepth: 1

View File

@ -33,8 +33,10 @@ with the node using RPC calls.
The shell via the local terminal The shell via the local terminal
-------------------------------- --------------------------------
In development mode, the shell will display in the node's terminal window. .. note:: Local terminal shell works only in development mode!
The shell connects to the node as 'shell' user with password 'shell' which is only available in dev mode.
The shell will display in the node's terminal window. It connects to the node as 'shell' user with password 'shell'
(which is only available in dev mode).
It may be disabled by passing the ``--no-local-shell`` flag when running the node. It may be disabled by passing the ``--no-local-shell`` flag when running the node.
.. _ssh_server: .. _ssh_server:

View File

@ -57,7 +57,7 @@ class NodeStatePersistenceTests : IntegrationTest() {
fun `persistent state survives node restart`() { fun `persistent state survives node restart`() {
val user = User("mark", "dadada", setOf(startFlow<SendMessageFlow>(), invokeRpc("vaultQuery"))) val user = User("mark", "dadada", setOf(startFlow<SendMessageFlow>(), invokeRpc("vaultQuery")))
val message = Message("Hello world!") val message = Message("Hello world!")
val stateAndRef: StateAndRef<MessageState>? = driver(DriverParameters(isDebug = true, startNodesInProcess = isQuasarAgentSpecified(), val stateAndRef: StateAndRef<MessageState>? = driver(DriverParameters(isDebug = true, inMemoryDB = false, startNodesInProcess = isQuasarAgentSpecified(),
portAllocation = RandomFree, extraCordappPackagesToScan = listOf(MessageState::class.packageName))) { portAllocation = RandomFree, extraCordappPackagesToScan = listOf(MessageState::class.packageName))) {
val nodeName = { val nodeName = {
val nodeHandle = startNode(rpcUsers = listOf(user)).getOrThrow() val nodeHandle = startNode(rpcUsers = listOf(user)).getOrThrow()
@ -90,7 +90,7 @@ class NodeStatePersistenceTests : IntegrationTest() {
val user = User("mark", "dadada", setOf(startFlow<SendMessageFlow>(), invokeRpc("vaultQuery"))) val user = User("mark", "dadada", setOf(startFlow<SendMessageFlow>(), invokeRpc("vaultQuery")))
val message = Message("Hello world!") val message = Message("Hello world!")
val stateAndRef: StateAndRef<MessageState>? = driver(DriverParameters(isDebug = true, startNodesInProcess = isQuasarAgentSpecified(), portAllocation = RandomFree, extraCordappPackagesToScan = listOf(MessageState::class.packageName))) { val stateAndRef: StateAndRef<MessageState>? = driver(DriverParameters(isDebug = true, inMemoryDB = false, startNodesInProcess = isQuasarAgentSpecified(), portAllocation = RandomFree, extraCordappPackagesToScan = listOf(MessageState::class.packageName))) {
val nodeName = { val nodeName = {
val nodeHandle = startNode(rpcUsers = listOf(user)).getOrThrow() val nodeHandle = startNode(rpcUsers = listOf(user)).getOrThrow()
val nodeName = nodeHandle.nodeInfo.singleIdentity().name val nodeName = nodeHandle.nodeInfo.singleIdentity().name

View File

@ -70,7 +70,7 @@ class HardRestartTest : IntegrationTest() {
@Test @Test
fun restartShortPingPongFlowRandomly() { fun restartShortPingPongFlowRandomly() {
val demoUser = User("demo", "demo", setOf(Permissions.startFlow<Ping>(), Permissions.all())) val demoUser = User("demo", "demo", setOf(Permissions.startFlow<Ping>(), Permissions.all()))
driver(DriverParameters(isDebug = true, startNodesInProcess = false, systemProperties = mapOf("log4j.configurationFile" to logConfigFile.toString()))) { driver(DriverParameters(isDebug = true, startNodesInProcess = false, inMemoryDB = false, systemProperties = mapOf("log4j.configurationFile" to logConfigFile.toString()))) {
val (a, b) = listOf( val (a, b) = listOf(
startNode(providedName = DUMMY_BANK_A_NAME, rpcUsers = listOf(demoUser), customOverrides = mapOf("p2pAddress" to "localhost:30000")), startNode(providedName = DUMMY_BANK_A_NAME, rpcUsers = listOf(demoUser), customOverrides = mapOf("p2pAddress" to "localhost:30000")),
startNode(providedName = DUMMY_BANK_B_NAME, rpcUsers = listOf(demoUser), customOverrides = mapOf("p2pAddress" to "localhost:40000")) startNode(providedName = DUMMY_BANK_B_NAME, rpcUsers = listOf(demoUser), customOverrides = mapOf("p2pAddress" to "localhost:40000"))
@ -102,7 +102,7 @@ class HardRestartTest : IntegrationTest() {
@Test @Test
fun restartLongPingPongFlowRandomly() { fun restartLongPingPongFlowRandomly() {
val demoUser = User("demo", "demo", setOf(Permissions.startFlow<Ping>(), Permissions.all())) val demoUser = User("demo", "demo", setOf(Permissions.startFlow<Ping>(), Permissions.all()))
driver(DriverParameters(isDebug = true, startNodesInProcess = false, systemProperties = mapOf("log4j.configurationFile" to logConfigFile.toString()))) { driver(DriverParameters(isDebug = true, startNodesInProcess = false, inMemoryDB = false, systemProperties = mapOf("log4j.configurationFile" to logConfigFile.toString()))) {
val (a, b) = listOf( val (a, b) = listOf(
startNode(providedName = DUMMY_BANK_A_NAME, rpcUsers = listOf(demoUser), customOverrides = mapOf("p2pAddress" to "localhost:30000")), startNode(providedName = DUMMY_BANK_A_NAME, rpcUsers = listOf(demoUser), customOverrides = mapOf("p2pAddress" to "localhost:30000")),
startNode(providedName = DUMMY_BANK_B_NAME, rpcUsers = listOf(demoUser), customOverrides = mapOf("p2pAddress" to "localhost:40000")) startNode(providedName = DUMMY_BANK_B_NAME, rpcUsers = listOf(demoUser), customOverrides = mapOf("p2pAddress" to "localhost:40000"))
@ -134,7 +134,7 @@ class HardRestartTest : IntegrationTest() {
@Test @Test
fun softRestartLongPingPongFlowRandomly() { fun softRestartLongPingPongFlowRandomly() {
val demoUser = User("demo", "demo", setOf(Permissions.startFlow<Ping>(), Permissions.all())) val demoUser = User("demo", "demo", setOf(Permissions.startFlow<Ping>(), Permissions.all()))
driver(DriverParameters(isDebug = true, startNodesInProcess = false, systemProperties = mapOf("log4j.configurationFile" to logConfigFile.toString()))) { driver(DriverParameters(isDebug = true, startNodesInProcess = false, inMemoryDB = false, systemProperties = mapOf("log4j.configurationFile" to logConfigFile.toString()))) {
val (a, b) = listOf( val (a, b) = listOf(
startNode(providedName = DUMMY_BANK_A_NAME, rpcUsers = listOf(demoUser), customOverrides = mapOf("p2pAddress" to "localhost:30000")), startNode(providedName = DUMMY_BANK_A_NAME, rpcUsers = listOf(demoUser), customOverrides = mapOf("p2pAddress" to "localhost:30000")),
startNode(providedName = DUMMY_BANK_B_NAME, rpcUsers = listOf(demoUser), customOverrides = mapOf("p2pAddress" to "localhost:40000")) startNode(providedName = DUMMY_BANK_B_NAME, rpcUsers = listOf(demoUser), customOverrides = mapOf("p2pAddress" to "localhost:40000"))
@ -210,7 +210,7 @@ class HardRestartTest : IntegrationTest() {
@Test @Test
fun restartRecursiveFlowRandomly() { fun restartRecursiveFlowRandomly() {
val demoUser = User("demo", "demo", setOf(Permissions.startFlow<RecursiveA>(), Permissions.all())) val demoUser = User("demo", "demo", setOf(Permissions.startFlow<RecursiveA>(), Permissions.all()))
driver(DriverParameters(isDebug = true, startNodesInProcess = false, systemProperties = mapOf("log4j.configurationFile" to logConfigFile.toString()))) { driver(DriverParameters(isDebug = true, startNodesInProcess = false, inMemoryDB = false, systemProperties = mapOf("log4j.configurationFile" to logConfigFile.toString()))) {
val (a, b) = listOf( val (a, b) = listOf(
startNode(providedName = DUMMY_BANK_A_NAME, rpcUsers = listOf(demoUser), customOverrides = mapOf("p2pAddress" to "localhost:30000")), startNode(providedName = DUMMY_BANK_A_NAME, rpcUsers = listOf(demoUser), customOverrides = mapOf("p2pAddress" to "localhost:30000")),
startNode(providedName = DUMMY_BANK_B_NAME, rpcUsers = listOf(demoUser), customOverrides = mapOf("p2pAddress" to "localhost:40000")) startNode(providedName = DUMMY_BANK_B_NAME, rpcUsers = listOf(demoUser), customOverrides = mapOf("p2pAddress" to "localhost:40000"))

View File

@ -20,7 +20,6 @@ import net.corda.core.serialization.serialize
import net.corda.core.utilities.MAX_HASH_HEX_SIZE import net.corda.core.utilities.MAX_HASH_HEX_SIZE
import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.NetworkHostAndPort
import net.corda.node.services.persistence.NodePropertiesPersistentStore import net.corda.node.services.persistence.NodePropertiesPersistentStore
import java.io.Serializable
import javax.persistence.* import javax.persistence.*
object NodeInfoSchema object NodeInfoSchema
@ -66,7 +65,7 @@ object NodeInfoSchemaV1 : MappedSchema(
*/ */
@Column(name = "serial", nullable = false) @Column(name = "serial", nullable = false)
val serial: Long val serial: Long
) : Serializable { ) {
fun toNodeInfo(): NodeInfo { fun toNodeInfo(): NodeInfo {
return NodeInfo( return NodeInfo(
this.addresses.map { it.toHostAndPort() }, this.addresses.map { it.toHostAndPort() },
@ -86,7 +85,7 @@ object NodeInfoSchemaV1 : MappedSchema(
var id: Int, var id: Int,
val host: String? = null, val host: String? = null,
val port: Int? = null val port: Int? = null
) : Serializable { ) {
companion object { companion object {
fun fromHostAndPort(hostAndPort: NetworkHostAndPort) = DBHostAndPort( fun fromHostAndPort(hostAndPort: NetworkHostAndPort) = DBHostAndPort(
0, hostAndPort.host, hostAndPort.port 0, hostAndPort.host, hostAndPort.port
@ -119,7 +118,7 @@ object NodeInfoSchemaV1 : MappedSchema(
@ManyToMany(mappedBy = "legalIdentitiesAndCerts", cascade = [(CascadeType.ALL)]) // ManyToMany because of distributed services. @ManyToMany(mappedBy = "legalIdentitiesAndCerts", cascade = [(CascadeType.ALL)]) // ManyToMany because of distributed services.
private val persistentNodeInfos: Set<PersistentNodeInfo> = emptySet() private val persistentNodeInfos: Set<PersistentNodeInfo> = emptySet()
) : Serializable { ) {
constructor(partyAndCert: PartyAndCertificate, isMain: Boolean = false) constructor(partyAndCert: PartyAndCertificate, isMain: Boolean = false)
: this(partyAndCert.name.toString(), : this(partyAndCert.name.toString(),
partyAndCert.party.owningKey.toStringShort(), partyAndCert.party.owningKey.toStringShort(),

View File

@ -21,13 +21,9 @@ import net.corda.core.contracts.ScheduledStateRef
import net.corda.core.contracts.StateRef import net.corda.core.contracts.StateRef
import net.corda.core.flows.FlowLogic import net.corda.core.flows.FlowLogic
import net.corda.core.flows.FlowLogicRefFactory import net.corda.core.flows.FlowLogicRefFactory
import net.corda.core.internal.FlowStateMachine import net.corda.core.internal.*
import net.corda.core.internal.ThreadBox
import net.corda.core.internal.VisibleForTesting
import net.corda.core.internal.concurrent.flatMap import net.corda.core.internal.concurrent.flatMap
import net.corda.core.internal.concurrent.openFuture import net.corda.core.internal.concurrent.openFuture
import net.corda.core.internal.join
import net.corda.core.internal.until
import net.corda.core.node.ServicesForResolution import net.corda.core.node.ServicesForResolution
import net.corda.core.schemas.PersistentStateRef import net.corda.core.schemas.PersistentStateRef
import net.corda.core.serialization.SingletonSerializeAsToken import net.corda.core.serialization.SingletonSerializeAsToken
@ -47,18 +43,9 @@ import net.corda.nodeapi.internal.persistence.contextTransaction
import org.apache.activemq.artemis.utils.ReusableLatch import org.apache.activemq.artemis.utils.ReusableLatch
import org.apache.mina.util.ConcurrentHashSet import org.apache.mina.util.ConcurrentHashSet
import org.slf4j.Logger import org.slf4j.Logger
import java.io.Serializable
import java.time.Duration import java.time.Duration
import java.time.Instant import java.time.Instant
import java.util.concurrent.CancellationException import java.util.concurrent.*
import java.util.concurrent.CompletableFuture
import java.util.concurrent.CompletionStage
import java.util.concurrent.ExecutionException
import java.util.concurrent.Executor
import java.util.concurrent.Executors
import java.util.concurrent.Future
import java.util.concurrent.TimeUnit
import java.util.concurrent.TimeoutException
import javax.annotation.concurrent.ThreadSafe import javax.annotation.concurrent.ThreadSafe
import javax.persistence.Column import javax.persistence.Column
import javax.persistence.EmbeddedId import javax.persistence.EmbeddedId
@ -159,7 +146,7 @@ class NodeSchedulerService(private val clock: CordaClock,
@Column(name = "scheduled_at", nullable = false) @Column(name = "scheduled_at", nullable = false)
var scheduledAt: Instant = Instant.now() var scheduledAt: Instant = Instant.now()
) : Serializable )
private class InnerState { private class InnerState {
var rescheduled: GuavaSettableFuture<Boolean>? = null var rescheduled: GuavaSettableFuture<Boolean>? = null

View File

@ -29,7 +29,6 @@ import net.corda.nodeapi.internal.crypto.x509Certificates
import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.nodeapi.internal.persistence.CordaPersistence
import net.corda.nodeapi.internal.persistence.NODE_DATABASE_PREFIX import net.corda.nodeapi.internal.persistence.NODE_DATABASE_PREFIX
import org.apache.commons.lang.ArrayUtils.EMPTY_BYTE_ARRAY import org.apache.commons.lang.ArrayUtils.EMPTY_BYTE_ARRAY
import java.io.Serializable
import java.security.InvalidAlgorithmParameterException import java.security.InvalidAlgorithmParameterException
import java.security.PublicKey import java.security.PublicKey
import java.security.cert.* import java.security.cert.*
@ -96,7 +95,7 @@ class PersistentIdentityService(override val trustRoot: X509Certificate,
@Lob @Lob
@Column(name = "identity_value", nullable = false) @Column(name = "identity_value", nullable = false)
var identity: ByteArray = EMPTY_BYTE_ARRAY var identity: ByteArray = EMPTY_BYTE_ARRAY
) : Serializable )
@Entity @Entity
@javax.persistence.Table(name = "${NODE_DATABASE_PREFIX}named_identities") @javax.persistence.Table(name = "${NODE_DATABASE_PREFIX}named_identities")
@ -107,7 +106,7 @@ class PersistentIdentityService(override val trustRoot: X509Certificate,
@Column(name = "pk_hash", length = MAX_HASH_HEX_SIZE, nullable = true) @Column(name = "pk_hash", length = MAX_HASH_HEX_SIZE, nullable = true)
var publicKeyHash: String? = "" var publicKeyHash: String? = ""
) : Serializable )
override val caCertStore: CertStore override val caCertStore: CertStore
override val trustAnchor: TrustAnchor = TrustAnchor(trustRoot, null) override val trustAnchor: TrustAnchor = TrustAnchor(trustRoot, null)

View File

@ -21,7 +21,6 @@ import net.corda.nodeapi.internal.persistence.CordaPersistence
import net.corda.nodeapi.internal.persistence.NODE_DATABASE_PREFIX import net.corda.nodeapi.internal.persistence.NODE_DATABASE_PREFIX
import org.apache.commons.lang.ArrayUtils.EMPTY_BYTE_ARRAY import org.apache.commons.lang.ArrayUtils.EMPTY_BYTE_ARRAY
import org.bouncycastle.operator.ContentSigner import org.bouncycastle.operator.ContentSigner
import java.io.Serializable
import java.security.KeyPair import java.security.KeyPair
import java.security.PrivateKey import java.security.PrivateKey
import java.security.PublicKey import java.security.PublicKey
@ -54,7 +53,7 @@ class PersistentKeyManagementService(val identityService: IdentityService,
@Lob @Lob
@Column(name = "private_key", nullable = false) @Column(name = "private_key", nullable = false)
var privateKey: ByteArray = EMPTY_BYTE_ARRAY var privateKey: ByteArray = EMPTY_BYTE_ARRAY
) : Serializable { ) {
constructor(publicKey: PublicKey, privateKey: PrivateKey) constructor(publicKey: PublicKey, privateKey: PrivateKey)
: this(publicKey.toStringShort(), publicKey.encoded, privateKey.encoded) : this(publicKey.toStringShort(), publicKey.encoded, privateKey.encoded)
} }

View File

@ -17,7 +17,6 @@ import net.corda.node.services.statemachine.DeduplicationId
import net.corda.node.utilities.AppendOnlyPersistentMap import net.corda.node.utilities.AppendOnlyPersistentMap
import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.nodeapi.internal.persistence.CordaPersistence
import net.corda.nodeapi.internal.persistence.NODE_DATABASE_PREFIX import net.corda.nodeapi.internal.persistence.NODE_DATABASE_PREFIX
import java.io.Serializable
import java.time.Instant import java.time.Instant
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
@ -161,7 +160,7 @@ class P2PMessageDeduplicator(private val database: CordaPersistence) {
@Column(name = "sequence_number", nullable = true) @Column(name = "sequence_number", nullable = true)
var seqNo: Long? = null var seqNo: Long? = null
) : Serializable )
private data class MessageMeta(val insertionTime: Instant, val senderHash: String?, val senderSeqNo: Long?) private data class MessageMeta(val insertionTime: Instant, val senderHash: String?, val senderSeqNo: Long?)

View File

@ -46,7 +46,7 @@ class DBCheckpointStorage : CheckpointStorage {
@Lob @Lob
@Column(name = "checkpoint_value", nullable = false) @Column(name = "checkpoint_value", nullable = false)
var checkpoint: ByteArray = EMPTY_BYTE_ARRAY var checkpoint: ByteArray = EMPTY_BYTE_ARRAY
) : Serializable )
override fun addCheckpoint(id: StateMachineRunId, checkpoint: SerializedBytes<Checkpoint>) { override fun addCheckpoint(id: StateMachineRunId, checkpoint: SerializedBytes<Checkpoint>) {
currentDBSession().saveOrUpdate(DBCheckpoint().apply { currentDBSession().saveOrUpdate(DBCheckpoint().apply {

View File

@ -23,7 +23,6 @@ import net.corda.nodeapi.internal.persistence.NODE_DATABASE_PREFIX
import net.corda.nodeapi.internal.persistence.bufferUntilDatabaseCommit import net.corda.nodeapi.internal.persistence.bufferUntilDatabaseCommit
import net.corda.nodeapi.internal.persistence.wrapWithDatabaseTransaction import net.corda.nodeapi.internal.persistence.wrapWithDatabaseTransaction
import rx.subjects.PublishSubject import rx.subjects.PublishSubject
import java.io.Serializable
import java.util.* import java.util.*
import javax.annotation.concurrent.ThreadSafe import javax.annotation.concurrent.ThreadSafe
import javax.persistence.Column import javax.persistence.Column
@ -48,7 +47,7 @@ class DBTransactionMappingStorage(private val database: CordaPersistence) : Stat
@Column(name = "state_machine_run_id", length = 36, nullable = true) @Column(name = "state_machine_run_id", length = 36, nullable = true)
var stateMachineRunId: String? = "" var stateMachineRunId: String? = ""
) : Serializable )
private companion object { private companion object {
fun createMap(): AppendOnlyPersistentMap<SecureHash, StateMachineRunId, DBTransactionMapping, String> { fun createMap(): AppendOnlyPersistentMap<SecureHash, StateMachineRunId, DBTransactionMapping, String> {

View File

@ -18,11 +18,7 @@ import net.corda.core.internal.VisibleForTesting
import net.corda.core.internal.bufferUntilSubscribed import net.corda.core.internal.bufferUntilSubscribed
import net.corda.core.internal.concurrent.doneFuture import net.corda.core.internal.concurrent.doneFuture
import net.corda.core.messaging.DataFeed import net.corda.core.messaging.DataFeed
import net.corda.core.serialization.SerializationDefaults import net.corda.core.serialization.*
import net.corda.core.serialization.SerializedBytes
import net.corda.core.serialization.SingletonSerializeAsToken
import net.corda.core.serialization.deserialize
import net.corda.core.serialization.serialize
import net.corda.core.toFuture import net.corda.core.toFuture
import net.corda.core.transactions.CoreTransaction import net.corda.core.transactions.CoreTransaction
import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.SignedTransaction
@ -37,12 +33,7 @@ import net.corda.serialization.internal.CordaSerializationEncoding.SNAPPY
import org.apache.commons.lang.ArrayUtils.EMPTY_BYTE_ARRAY import org.apache.commons.lang.ArrayUtils.EMPTY_BYTE_ARRAY
import rx.Observable import rx.Observable
import rx.subjects.PublishSubject import rx.subjects.PublishSubject
import java.io.Serializable import javax.persistence.*
import javax.persistence.Column
import javax.persistence.Entity
import javax.persistence.Id
import javax.persistence.Lob
import javax.persistence.Table
// cache value type to just store the immutable bits of a signed transaction plus conversion helpers // cache value type to just store the immutable bits of a signed transaction plus conversion helpers
typealias TxCacheValue = Pair<SerializedBytes<CoreTransaction>, List<TransactionSignature>> typealias TxCacheValue = Pair<SerializedBytes<CoreTransaction>, List<TransactionSignature>>
@ -62,7 +53,7 @@ class DBTransactionStorage(cacheSizeBytes: Long, private val database: CordaPers
@Lob @Lob
@Column(name = "transaction_value", nullable = false) @Column(name = "transaction_value", nullable = false)
var transaction: ByteArray = EMPTY_BYTE_ARRAY var transaction: ByteArray = EMPTY_BYTE_ARRAY
) : Serializable )
private companion object { private companion object {
fun createTransactionsMap(maxSizeInBytes: Long) fun createTransactionsMap(maxSizeInBytes: Long)

View File

@ -30,11 +30,7 @@ import net.corda.core.node.services.AttachmentId
import net.corda.core.node.services.AttachmentStorage import net.corda.core.node.services.AttachmentStorage
import net.corda.core.node.services.vault.AttachmentQueryCriteria import net.corda.core.node.services.vault.AttachmentQueryCriteria
import net.corda.core.node.services.vault.AttachmentSort import net.corda.core.node.services.vault.AttachmentSort
import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.*
import net.corda.core.serialization.SerializationToken
import net.corda.core.serialization.SerializeAsToken
import net.corda.core.serialization.SerializeAsTokenContext
import net.corda.core.serialization.SingletonSerializeAsToken
import net.corda.core.utilities.contextLogger import net.corda.core.utilities.contextLogger
import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.config.NodeConfiguration
import net.corda.node.services.vault.HibernateAttachmentQueryCriteriaParser import net.corda.node.services.vault.HibernateAttachmentQueryCriteriaParser
@ -48,22 +44,12 @@ import net.corda.nodeapi.internal.withContractsInJar
import java.io.FilterInputStream import java.io.FilterInputStream
import java.io.IOException import java.io.IOException
import java.io.InputStream import java.io.InputStream
import java.io.Serializable
import java.nio.file.Paths import java.nio.file.Paths
import java.time.Instant import java.time.Instant
import java.util.* import java.util.*
import java.util.jar.JarInputStream import java.util.jar.JarInputStream
import javax.annotation.concurrent.ThreadSafe import javax.annotation.concurrent.ThreadSafe
import javax.persistence.CollectionTable import javax.persistence.*
import javax.persistence.Column
import javax.persistence.ElementCollection
import javax.persistence.Entity
import javax.persistence.ForeignKey
import javax.persistence.Id
import javax.persistence.Index
import javax.persistence.JoinColumn
import javax.persistence.Lob
import javax.persistence.Table
/** /**
* Stores attachments using Hibernate to database. * Stores attachments using Hibernate to database.
@ -125,7 +111,7 @@ class NodeAttachmentService(
@CollectionTable(name = "${NODE_DATABASE_PREFIX}attachments_contracts", joinColumns = [(JoinColumn(name = "att_id", referencedColumnName = "att_id"))], @CollectionTable(name = "${NODE_DATABASE_PREFIX}attachments_contracts", joinColumns = [(JoinColumn(name = "att_id", referencedColumnName = "att_id"))],
foreignKey = ForeignKey(name = "FK__ctr_class__attachments")) foreignKey = ForeignKey(name = "FK__ctr_class__attachments"))
var contractClassNames: List<ContractClassName>? = null var contractClassNames: List<ContractClassName>? = null
) : Serializable )
@VisibleForTesting @VisibleForTesting
var checkAttachmentsOnLoad = true var checkAttachmentsOnLoad = true

View File

@ -19,7 +19,6 @@ import net.corda.nodeapi.internal.persistence.CordaPersistence
import net.corda.nodeapi.internal.persistence.NODE_DATABASE_PREFIX import net.corda.nodeapi.internal.persistence.NODE_DATABASE_PREFIX
import org.slf4j.Logger import org.slf4j.Logger
import rx.subjects.PublishSubject import rx.subjects.PublishSubject
import java.io.Serializable
import javax.persistence.Column import javax.persistence.Column
import javax.persistence.Entity import javax.persistence.Entity
import javax.persistence.Id import javax.persistence.Id
@ -45,7 +44,7 @@ class NodePropertiesPersistentStore(readPhysicalNodeId: () -> String, persistenc
@Column(name = "property_value", nullable = true) @Column(name = "property_value", nullable = true)
var value: String? = "" var value: String? = ""
) : Serializable )
} }
private class FlowsDrainingModeOperationsImpl(readPhysicalNodeId: () -> String, private val persistence: CordaPersistence, logger: Logger) : FlowsDrainingModeOperations { private class FlowsDrainingModeOperationsImpl(readPhysicalNodeId: () -> String, private val persistence: CordaPersistence, logger: Logger) : FlowsDrainingModeOperations {

View File

@ -32,7 +32,6 @@ import net.corda.core.utilities.debug
import net.corda.node.utilities.AppendOnlyPersistentMap import net.corda.node.utilities.AppendOnlyPersistentMap
import net.corda.nodeapi.internal.persistence.NODE_DATABASE_PREFIX import net.corda.nodeapi.internal.persistence.NODE_DATABASE_PREFIX
import net.corda.nodeapi.internal.persistence.currentDBSession import net.corda.nodeapi.internal.persistence.currentDBSession
import java.io.Serializable
import java.time.Clock import java.time.Clock
import java.time.Instant import java.time.Instant
import java.util.* import java.util.*
@ -49,7 +48,7 @@ class PersistentUniquenessProvider(val clock: Clock) : UniquenessProvider, Singl
@Column(name = "consuming_transaction_id", nullable = true) @Column(name = "consuming_transaction_id", nullable = true)
val consumingTxHash: String? val consumingTxHash: String?
) : Serializable )
@Entity @Entity
@javax.persistence.Table(name = "${NODE_DATABASE_PREFIX}notary_request_log") @javax.persistence.Table(name = "${NODE_DATABASE_PREFIX}notary_request_log")
@ -72,7 +71,7 @@ class PersistentUniquenessProvider(val clock: Clock) : UniquenessProvider, Singl
@Column(name = "request_timestamp", nullable = false) @Column(name = "request_timestamp", nullable = false)
var requestDate: Instant var requestDate: Instant
) : Serializable )
@Entity @Entity
@javax.persistence.Table(name = "${NODE_DATABASE_PREFIX}notary_committed_states") @javax.persistence.Table(name = "${NODE_DATABASE_PREFIX}notary_committed_states")

View File

@ -42,7 +42,6 @@ import net.corda.nodeapi.internal.config.NodeSSLConfiguration
import net.corda.nodeapi.internal.config.SSLConfiguration import net.corda.nodeapi.internal.config.SSLConfiguration
import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.nodeapi.internal.persistence.CordaPersistence
import net.corda.nodeapi.internal.persistence.NODE_DATABASE_PREFIX import net.corda.nodeapi.internal.persistence.NODE_DATABASE_PREFIX
import java.io.Serializable
import java.nio.file.Path import java.nio.file.Path
import java.time.Clock import java.time.Clock
import java.util.concurrent.CompletableFuture import java.util.concurrent.CompletableFuture
@ -104,7 +103,7 @@ class RaftUniquenessProvider(
var value: String? = "", var value: String? = "",
@Column(name = "raft_log_index", nullable = false) @Column(name = "raft_log_index", nullable = false)
var index: Long = 0 var index: Long = 0
) : Serializable )
/** Directory storing the Raft log and state machine snapshots */ /** Directory storing the Raft log and state machine snapshots */
private val storagePath: Path = transportConfiguration.baseDirectory private val storagePath: Path = transportConfiguration.baseDirectory

View File

@ -14,9 +14,8 @@ import net.corda.core.contracts.StateRef
import net.corda.core.contracts.UpgradedContract import net.corda.core.contracts.UpgradedContract
import net.corda.core.node.services.ContractUpgradeService import net.corda.core.node.services.ContractUpgradeService
import net.corda.core.serialization.SingletonSerializeAsToken import net.corda.core.serialization.SingletonSerializeAsToken
import net.corda.nodeapi.internal.persistence.NODE_DATABASE_PREFIX
import net.corda.node.utilities.PersistentMap import net.corda.node.utilities.PersistentMap
import java.io.Serializable import net.corda.nodeapi.internal.persistence.NODE_DATABASE_PREFIX
import javax.persistence.Column import javax.persistence.Column
import javax.persistence.Entity import javax.persistence.Entity
import javax.persistence.Id import javax.persistence.Id
@ -34,7 +33,7 @@ class ContractUpgradeServiceImpl : ContractUpgradeService, SingletonSerializeAsT
/** refers to the UpgradedContract class name*/ /** refers to the UpgradedContract class name*/
@Column(name = "contract_class_name", nullable = true) @Column(name = "contract_class_name", nullable = true)
var upgradedContractClassName: String? = "" var upgradedContractClassName: String? = ""
) : Serializable )
private companion object { private companion object {
fun createContractUpgradesMap(): PersistentMap<String, String, DBContractUpgrade, String> { fun createContractUpgradesMap(): PersistentMap<String, String, DBContractUpgrade, String> {

View File

@ -21,7 +21,6 @@ import net.corda.core.schemas.PersistentState
import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.CordaSerializable
import net.corda.core.utilities.OpaqueBytes import net.corda.core.utilities.OpaqueBytes
import org.hibernate.annotations.Type import org.hibernate.annotations.Type
import java.io.Serializable
import java.time.Instant import java.time.Instant
import java.util.* import java.util.*
import javax.persistence.* import javax.persistence.*
@ -167,7 +166,7 @@ object VaultSchemaV1 : MappedSchema(schemaFamily = VaultSchema.javaClass, versio
@Column(name = "note", nullable = true) @Column(name = "note", nullable = true)
var note: String? var note: String?
) : Serializable { ) {
constructor(txId: String, note: String) : this(0, txId, note) constructor(txId: String, note: String) : this(0, txId, note)
} }
} }

View File

@ -180,7 +180,7 @@ class KryoTests(private val compression: CordaSerializationEncoding?) {
@Test @Test
fun `InputStream serialisation`() { fun `InputStream serialisation`() {
val rubbish = ByteArray(12345, { (it * it * 0.12345).toByte() }) val rubbish = ByteArray(12345) { (it * it * 0.12345).toByte() }
val readRubbishStream: InputStream = rubbish.inputStream().serialize(factory, context).deserialize(factory, context) val readRubbishStream: InputStream = rubbish.inputStream().serialize(factory, context).deserialize(factory, context)
for (i in 0..12344) { for (i in 0..12344) {
assertEquals(rubbish[i], readRubbishStream.read().toByte()) assertEquals(rubbish[i], readRubbishStream.read().toByte())
@ -218,7 +218,7 @@ class KryoTests(private val compression: CordaSerializationEncoding?) {
@Test @Test
fun `HashCheckingStream (de)serialize`() { fun `HashCheckingStream (de)serialize`() {
val rubbish = ByteArray(12345, { (it * it * 0.12345).toByte() }) val rubbish = ByteArray(12345) { (it * it * 0.12345).toByte() }
val readRubbishStream: InputStream = NodeAttachmentService.HashCheckingStream( val readRubbishStream: InputStream = NodeAttachmentService.HashCheckingStream(
SecureHash.sha256(rubbish), SecureHash.sha256(rubbish),
rubbish.size, rubbish.size,

View File

@ -6,14 +6,12 @@ import net.corda.node.internal.configureDatabase
import net.corda.node.services.schema.NodeSchemaService import net.corda.node.services.schema.NodeSchemaService
import net.corda.node.utilities.AppendOnlyPersistentMap import net.corda.node.utilities.AppendOnlyPersistentMap
import net.corda.nodeapi.internal.persistence.DatabaseConfig import net.corda.nodeapi.internal.persistence.DatabaseConfig
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
import org.junit.After import org.junit.After
import org.junit.Assert.* import org.junit.Assert.*
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import org.junit.runners.Parameterized import org.junit.runners.Parameterized
import java.io.Serializable
import java.util.concurrent.CountDownLatch import java.util.concurrent.CountDownLatch
import javax.persistence.Column import javax.persistence.Column
import javax.persistence.Entity import javax.persistence.Entity
@ -270,7 +268,7 @@ class AppendOnlyPersistentMapTest(var scenario: Scenario) {
@Column(name = "value", length = 16) @Column(name = "value", length = 16)
var value: String = "" var value: String = ""
) : Serializable )
class TestMap : AppendOnlyPersistentMap<Long, String, PersistentMapEntry, Long>( class TestMap : AppendOnlyPersistentMap<Long, String, PersistentMapEntry, Long>(
toPersistentEntityKey = { it }, toPersistentEntityKey = { it },

View File

@ -30,7 +30,6 @@ import org.hibernate.annotations.Cascade
import org.hibernate.annotations.CascadeType import org.hibernate.annotations.CascadeType
import org.junit.Ignore import org.junit.Ignore
import org.junit.Test import org.junit.Test
import java.io.Serializable
import javax.persistence.* import javax.persistence.*
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.assertFalse import kotlin.test.assertFalse
@ -155,7 +154,7 @@ object TestSchema : MappedSchema(SchemaFamily::class.java, 1, setOf(Parent::clas
@Suppress("unused") @Suppress("unused")
@Entity @Entity
@Table(name = "children") @Table(name = "children")
class Child : Serializable { class Child {
@Id @Id
@GeneratedValue @GeneratedValue
@Column(name = "child_id", unique = true, nullable = false) @Column(name = "child_id", unique = true, nullable = false)

View File

@ -54,7 +54,7 @@ class TraderDemoTest : IntegrationTest() {
startFlow<CashPaymentFlow>(), startFlow<CashPaymentFlow>(),
startFlow<CommercialPaperIssueFlow>(), startFlow<CommercialPaperIssueFlow>(),
all())) all()))
driver(DriverParameters(startNodesInProcess = true, extraCordappPackagesToScan = listOf("net.corda.finance"))) { driver(DriverParameters(startNodesInProcess = true, inMemoryDB = false, extraCordappPackagesToScan = listOf("net.corda.finance"))) {
val (nodeA, nodeB, bankNode) = listOf( val (nodeA, nodeB, bankNode) = listOf(
startNode(providedName = DUMMY_BANK_A_NAME, rpcUsers = listOf(demoUser)), startNode(providedName = DUMMY_BANK_A_NAME, rpcUsers = listOf(demoUser)),
startNode(providedName = DUMMY_BANK_B_NAME, rpcUsers = listOf(demoUser)), startNode(providedName = DUMMY_BANK_B_NAME, rpcUsers = listOf(demoUser)),
@ -96,7 +96,7 @@ class TraderDemoTest : IntegrationTest() {
@Test @Test
fun `Tudor test`() { fun `Tudor test`() {
driver(DriverParameters(isDebug = true, startNodesInProcess = false, extraCordappPackagesToScan = listOf("net.corda.finance"))) { driver(DriverParameters(isDebug = true, startNodesInProcess = false, inMemoryDB = false, extraCordappPackagesToScan = listOf("net.corda.finance"))) {
val demoUser = User("demo", "demo", setOf(startFlow<SellerFlow>(), all())) val demoUser = User("demo", "demo", setOf(startFlow<SellerFlow>(), all()))
val bankUser = User("user1", "test", permissions = setOf(all())) val bankUser = User("user1", "test", permissions = setOf(all()))
val (nodeA, nodeB, bankNode) = listOf( val (nodeA, nodeB, bankNode) = listOf(

View File

@ -104,6 +104,9 @@ task jarFilter(type: JarFilterTask) {
forRemove = [ forRemove = [
"co.paralleluniverse.fibers.Suspendable" "co.paralleluniverse.fibers.Suspendable"
] ]
forSanitise = [
"net.corda.core.DeleteForDJVM"
]
} }
} }

View File

@ -244,7 +244,8 @@ fun <A> driver(defaultParameters: DriverParameters = DriverParameters(), dsl: Dr
jmxPolicy = defaultParameters.jmxPolicy, jmxPolicy = defaultParameters.jmxPolicy,
compatibilityZone = null, compatibilityZone = null,
networkParameters = defaultParameters.networkParameters, networkParameters = defaultParameters.networkParameters,
notaryCustomOverrides = defaultParameters.notaryCustomOverrides notaryCustomOverrides = defaultParameters.notaryCustomOverrides,
inMemoryDB = defaultParameters.inMemoryDB
), ),
coerce = { it }, coerce = { it },
dsl = dsl, dsl = dsl,
@ -277,6 +278,10 @@ fun <A> driver(defaultParameters: DriverParameters = DriverParameters(), dsl: Dr
* @property networkParameters The network parameters to be used by all the nodes. [NetworkParameters.notaries] must be * @property networkParameters The network parameters to be used by all the nodes. [NetworkParameters.notaries] must be
* empty as notaries are defined by [notarySpecs]. * empty as notaries are defined by [notarySpecs].
* @property notaryCustomOverrides Extra settings that need to be passed to the notary. * @property notaryCustomOverrides Extra settings that need to be passed to the notary.
* @property initialiseSerialization Indicates whether to initialized the serialization subsystem.
* @property inMemoryDB Whether to use in-memory H2 for new nodes rather then on-disk (the node starts quicker, however
* the data is not persisted between node restarts). Has no effect if node is configured
* in any way to use database other than H2.
*/ */
@Suppress("unused") @Suppress("unused")
data class DriverParameters( data class DriverParameters(
@ -293,7 +298,8 @@ data class DriverParameters(
val jmxPolicy: JmxPolicy = JmxPolicy(), val jmxPolicy: JmxPolicy = JmxPolicy(),
val networkParameters: NetworkParameters = testNetworkParameters(notaries = emptyList()), val networkParameters: NetworkParameters = testNetworkParameters(notaries = emptyList()),
val notaryCustomOverrides: Map<String, Any?> = emptyMap(), val notaryCustomOverrides: Map<String, Any?> = emptyMap(),
val initialiseSerialization: Boolean = true val initialiseSerialization: Boolean = true,
val inMemoryDB: Boolean = true
) { ) {
constructor( constructor(
isDebug: Boolean, isDebug: Boolean,
@ -322,6 +328,7 @@ data class DriverParameters(
jmxPolicy, jmxPolicy,
networkParameters, networkParameters,
emptyMap(), emptyMap(),
true,
true true
) )
@ -338,7 +345,8 @@ data class DriverParameters(
extraCordappPackagesToScan: List<String>, extraCordappPackagesToScan: List<String>,
jmxPolicy: JmxPolicy, jmxPolicy: JmxPolicy,
networkParameters: NetworkParameters, networkParameters: NetworkParameters,
initialiseSerialization: Boolean initialiseSerialization: Boolean,
inMemoryDB: Boolean
) : this( ) : this(
isDebug, isDebug,
driverDirectory, driverDirectory,
@ -353,7 +361,8 @@ data class DriverParameters(
jmxPolicy, jmxPolicy,
networkParameters, networkParameters,
emptyMap(), emptyMap(),
initialiseSerialization initialiseSerialization,
inMemoryDB
) )
fun withIsDebug(isDebug: Boolean): DriverParameters = copy(isDebug = isDebug) fun withIsDebug(isDebug: Boolean): DriverParameters = copy(isDebug = isDebug)
@ -370,6 +379,7 @@ data class DriverParameters(
fun withJmxPolicy(jmxPolicy: JmxPolicy): DriverParameters = copy(jmxPolicy = jmxPolicy) fun withJmxPolicy(jmxPolicy: JmxPolicy): DriverParameters = copy(jmxPolicy = jmxPolicy)
fun withNetworkParameters(networkParameters: NetworkParameters): DriverParameters = copy(networkParameters = networkParameters) fun withNetworkParameters(networkParameters: NetworkParameters): DriverParameters = copy(networkParameters = networkParameters)
fun withNotaryCustomOverrides(notaryCustomOverrides: Map<String, Any?>): DriverParameters = copy(notaryCustomOverrides = notaryCustomOverrides) fun withNotaryCustomOverrides(notaryCustomOverrides: Map<String, Any?>): DriverParameters = copy(notaryCustomOverrides = notaryCustomOverrides)
fun withInMemoryDB(inMemoryDB: Boolean): DriverParameters = copy(inMemoryDB = inMemoryDB)
fun copy( fun copy(
isDebug: Boolean, isDebug: Boolean,

View File

@ -84,6 +84,7 @@ import java.util.*
import java.util.concurrent.Executors import java.util.concurrent.Executors
import java.util.concurrent.ScheduledExecutorService import java.util.concurrent.ScheduledExecutorService
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import java.util.concurrent.atomic.AtomicInteger
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
import kotlin.collections.HashMap import kotlin.collections.HashMap
import kotlin.concurrent.thread import kotlin.concurrent.thread
@ -103,7 +104,8 @@ class DriverDSLImpl(
val notarySpecs: List<NotarySpec>, val notarySpecs: List<NotarySpec>,
val compatibilityZone: CompatibilityZoneParams?, val compatibilityZone: CompatibilityZoneParams?,
val networkParameters: NetworkParameters, val networkParameters: NetworkParameters,
val notaryCustomOverrides: Map<String, Any?> val notaryCustomOverrides: Map<String, Any?>,
val inMemoryDB: Boolean
) : InternalDriverDSL { ) : InternalDriverDSL {
private var _executorService: ScheduledExecutorService? = null private var _executorService: ScheduledExecutorService? = null
@ -122,6 +124,9 @@ class DriverDSLImpl(
private lateinit var _notaries: CordaFuture<List<NotaryHandle>> private lateinit var _notaries: CordaFuture<List<NotaryHandle>>
override val notaryHandles: List<NotaryHandle> get() = _notaries.getOrThrow() override val notaryHandles: List<NotaryHandle> get() = _notaries.getOrThrow()
// While starting with inProcess mode, we need to have different names to avoid clashes
private val inMemoryCounter = AtomicInteger()
interface Waitable { interface Waitable {
@Throws(InterruptedException::class) @Throws(InterruptedException::class)
fun waitFor() fun waitFor()
@ -138,6 +143,16 @@ class DriverDSLImpl(
private val jolokiaJarPath: String by lazy { resolveJar(".*jolokia-jvm-.*-agent\\.jar$") } private val jolokiaJarPath: String by lazy { resolveJar(".*jolokia-jvm-.*-agent\\.jar$") }
private fun NodeConfig.checkAndOverrideForInMemoryDB(): NodeConfig = this.run {
if (inMemoryDB && corda.dataSourceProperties.getProperty("dataSource.url").startsWith("jdbc:h2:")) {
val jdbcUrl = "jdbc:h2:mem:persistence${inMemoryCounter.getAndIncrement()};DB_CLOSE_ON_EXIT=FALSE;LOCK_TIMEOUT=10000;WRITE_DELAY=100"
corda.dataSourceProperties.setProperty("dataSource.url", jdbcUrl)
NodeConfig(typesafe = typesafe + mapOf("dataSourceProperties" to mapOf("dataSource.url" to jdbcUrl)), corda = corda)
} else {
this
}
}
private fun resolveJar(jarNamePattern: String): String { private fun resolveJar(jarNamePattern: String): String {
return try { return try {
val cl = ClassLoader.getSystemClassLoader() val cl = ClassLoader.getSystemClassLoader()
@ -246,7 +261,7 @@ class DriverDSLImpl(
baseDirectory = baseDirectory(name), baseDirectory = baseDirectory(name),
allowMissingConfig = true, allowMissingConfig = true,
configOverrides = if (overrides.hasPath("devMode")) overrides else overrides + mapOf("devMode" to true) configOverrides = if (overrides.hasPath("devMode")) overrides else overrides + mapOf("devMode" to true)
)) )).checkAndOverrideForInMemoryDB()
return startNodeInternal(config, webAddress, startInSameProcess, maximumHeapSize, localNetworkMap) return startNodeInternal(config, webAddress, startInSameProcess, maximumHeapSize, localNetworkMap)
} }
@ -264,7 +279,7 @@ class DriverDSLImpl(
"adminAddress" to portAllocation.nextHostAndPort().toString() "adminAddress" to portAllocation.nextHostAndPort().toString()
), ),
"devMode" to false) "devMode" to false)
)) )).checkAndOverrideForInMemoryDB()
val versionInfo = VersionInfo(1, "1", "1", "1") val versionInfo = VersionInfo(1, "1", "1", "1")
config.corda.certificatesDirectory.createDirectories() config.corda.certificatesDirectory.createDirectories()
@ -387,7 +402,7 @@ class DriverDSLImpl(
configOverrides = rawConfig.toNodeOnly() configOverrides = rawConfig.toNodeOnly()
) )
val cordaConfig = typesafe.parseAsNodeConfiguration() val cordaConfig = typesafe.parseAsNodeConfiguration()
val config = NodeConfig(rawConfig, cordaConfig) val config = NodeConfig(rawConfig, cordaConfig).checkAndOverrideForInMemoryDB()
return startNodeInternal(config, webAddress, null, "512m", localNetworkMap) return startNodeInternal(config, webAddress, null, "512m", localNetworkMap)
} }
@ -1064,7 +1079,8 @@ fun <DI : DriverDSL, D : InternalDriverDSL, A> genericDriver(
notarySpecs = defaultParameters.notarySpecs, notarySpecs = defaultParameters.notarySpecs,
compatibilityZone = null, compatibilityZone = null,
networkParameters = defaultParameters.networkParameters, networkParameters = defaultParameters.networkParameters,
notaryCustomOverrides = defaultParameters.notaryCustomOverrides notaryCustomOverrides = defaultParameters.notaryCustomOverrides,
inMemoryDB = defaultParameters.inMemoryDB
) )
) )
val shutdownHook = addShutdownHook(driverDsl::shutdown) val shutdownHook = addShutdownHook(driverDsl::shutdown)
@ -1143,6 +1159,7 @@ fun <A> internalDriver(
networkParameters: NetworkParameters = DriverParameters().networkParameters, networkParameters: NetworkParameters = DriverParameters().networkParameters,
compatibilityZone: CompatibilityZoneParams? = null, compatibilityZone: CompatibilityZoneParams? = null,
notaryCustomOverrides: Map<String, Any?> = DriverParameters().notaryCustomOverrides, notaryCustomOverrides: Map<String, Any?> = DriverParameters().notaryCustomOverrides,
inMemoryDB: Boolean = DriverParameters().inMemoryDB,
dsl: DriverDSLImpl.() -> A dsl: DriverDSLImpl.() -> A
): A { ): A {
return genericDriver( return genericDriver(
@ -1160,7 +1177,8 @@ fun <A> internalDriver(
jmxPolicy = jmxPolicy, jmxPolicy = jmxPolicy,
compatibilityZone = compatibilityZone, compatibilityZone = compatibilityZone,
networkParameters = networkParameters, networkParameters = networkParameters,
notaryCustomOverrides = notaryCustomOverrides notaryCustomOverrides = notaryCustomOverrides,
inMemoryDB = inMemoryDB
), ),
coerce = { it }, coerce = { it },
dsl = dsl, dsl = dsl,

View File

@ -125,6 +125,7 @@ fun <A> rpcDriver(
jmxPolicy: JmxPolicy = JmxPolicy(), jmxPolicy: JmxPolicy = JmxPolicy(),
networkParameters: NetworkParameters = testNetworkParameters(notaries = emptyList()), networkParameters: NetworkParameters = testNetworkParameters(notaries = emptyList()),
notaryCustomOverrides: Map<String, Any?> = emptyMap(), notaryCustomOverrides: Map<String, Any?> = emptyMap(),
inMemoryDB: Boolean = true,
dsl: RPCDriverDSL.() -> A dsl: RPCDriverDSL.() -> A
): A { ): A {
return genericDriver( return genericDriver(
@ -143,7 +144,8 @@ fun <A> rpcDriver(
jmxPolicy = jmxPolicy, jmxPolicy = jmxPolicy,
compatibilityZone = null, compatibilityZone = null,
networkParameters = networkParameters, networkParameters = networkParameters,
notaryCustomOverrides = notaryCustomOverrides notaryCustomOverrides = notaryCustomOverrides,
inMemoryDB = inMemoryDB
), externalTrace ), externalTrace
), ),
coerce = { it }, coerce = { it },

View File

@ -194,7 +194,7 @@ class ExplorerSimulation(private val options: OptionSet) {
for (ref in 0..1) { for (ref in 0..1) {
for ((currency, issuer) in issuers) { for ((currency, issuer) in issuers) {
val amount = Amount(1_000_000, currency) val amount = Amount(1_000_000, currency)
issuer.startFlow(::CashIssueAndPaymentFlow, amount, OpaqueBytes(ByteArray(1, { ref.toByte() })), issuer.startFlow(::CashIssueAndPaymentFlow, amount, OpaqueBytes.of( ref.toByte() ),
it, anonymous, notary).returnValue.getOrThrow() it, anonymous, notary).returnValue.getOrThrow()
} }
} }

View File

@ -0,0 +1,190 @@
package net.corda.tools.shell;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import com.google.common.collect.Lists;
import kotlin.Pair;
import net.corda.client.jackson.JacksonSupport;
import net.corda.core.contracts.Amount;
import net.corda.core.crypto.SecureHash;
import net.corda.core.flows.FlowException;
import net.corda.core.flows.FlowLogic;
import net.corda.core.flows.FlowSession;
import net.corda.core.flows.StateMachineRunId;
import net.corda.core.identity.CordaX500Name;
import net.corda.core.identity.Party;
import net.corda.core.internal.concurrent.CordaFutureImplKt;
import net.corda.core.internal.concurrent.OpenFuture;
import net.corda.core.messaging.FlowProgressHandleImpl;
import net.corda.core.utilities.ProgressTracker;
import net.corda.node.services.identity.InMemoryIdentityService;
import net.corda.testing.core.TestIdentity;
import net.corda.testing.internal.InternalTestConstantsKt;
import org.jetbrains.annotations.Nullable;
import org.junit.Test;
import rx.Observable;
import java.util.*;
import static org.junit.Assert.assertEquals;
public class InteractiveShellJavaTest {
private static TestIdentity megaCorp = new TestIdentity(new CordaX500Name("MegaCorp", "London", "GB"));
// should guarantee that FlowA will have synthetic method to access this field
private static String synthetic = "synth";
abstract static class StringFlow extends FlowLogic<String> {
abstract String getA();
}
public static class FlowA extends StringFlow {
private String a;
public FlowA(String a) {
if (!synthetic.isEmpty()) {
this.a = a;
}
}
public FlowA(Integer b) {
this(b.toString());
}
public FlowA(Integer b, String c) {
this(b.toString() + c);
}
public FlowA(Amount<Currency> amount) {
this(amount.toString());
}
public FlowA(Pair<Amount<Currency>, SecureHash.SHA256> pair) {
this(pair.toString());
}
public FlowA(Party party) {
this(party.getName().toString());
}
@Nullable
@Override
public ProgressTracker getProgressTracker() {
return new ProgressTracker();
}
@Override
public String call() throws FlowException {
return a;
}
@Override
String getA() {
return a;
}
}
public static class FlowB extends StringFlow {
private Party party;
private String a;
public FlowB(Party party, String a) {
this.party = party;
this.a = a;
}
@Nullable
@Override
public ProgressTracker getProgressTracker() {
return new ProgressTracker();
}
@Override
public String call() throws FlowException {
FlowSession session = initiateFlow(party);
Integer integer = session.receive(Integer.class).unwrap((i) -> {
return i;
});
return integer.toString();
}
@Override
String getA() {
return a;
}
}
private InMemoryIdentityService ids = new InMemoryIdentityService(Lists.newArrayList(megaCorp.getIdentity()), InternalTestConstantsKt.getDEV_ROOT_CA().getCertificate());
private ObjectMapper om = JacksonSupport.createInMemoryMapper(ids, new YAMLFactory());
private String output;
private void check(String input, String expected, Class<? extends StringFlow> flowClass) throws InteractiveShell.NoApplicableConstructor {
InteractiveShell.INSTANCE.runFlowFromString((clazz, args) -> {
StringFlow instance = null;
try {
instance = (StringFlow)clazz.getConstructor(Arrays.stream(args).map(Object::getClass).toArray(Class[]::new)).newInstance(args);
} catch (Exception e) {
System.out.println(e);
}
output = instance.getA();
OpenFuture<String> future = CordaFutureImplKt.openFuture();
future.set("ABC");
return new FlowProgressHandleImpl(StateMachineRunId.Companion.createRandom(), future, Observable.just("Some string"));
}, input, flowClass, om);
assertEquals(input, expected, output);
}
@Test
public void flowStartSimple() throws InteractiveShell.NoApplicableConstructor {
check("a: Hi there", "Hi there", FlowA.class);
check("b: 12", "12", FlowA.class);
check("b: 12, c: Yo", "12Yo", FlowA.class);
}
@Test
public void flowStartWithComplexTypes() throws InteractiveShell.NoApplicableConstructor {
check("amount: £10", "10.00 GBP", FlowA.class);
}
@Test
public void flowStartWithNestedTypes() throws InteractiveShell.NoApplicableConstructor {
check(
"pair: { first: $100.12, second: df489807f81c8c8829e509e1bcb92e6692b9dd9d624b7456435cb2f51dc82587 }",
"($100.12, df489807f81c8c8829e509e1bcb92e6692b9dd9d624b7456435cb2f51dc82587)",
FlowA.class);
}
@Test(expected = InteractiveShell.NoApplicableConstructor.class)
public void flowStartNoArgs() throws InteractiveShell.NoApplicableConstructor {
check("", "", FlowA.class);
}
@Test(expected = InteractiveShell.NoApplicableConstructor.class)
public void flowMissingParam() throws InteractiveShell.NoApplicableConstructor {
check("c: Yo", "", FlowA.class);
}
@Test(expected = InteractiveShell.NoApplicableConstructor.class)
public void flowTooManyParams() throws InteractiveShell.NoApplicableConstructor {
check("b: 12, c: Yo, d: Bar", "", FlowA.class);
}
@Test
public void party() throws InteractiveShell.NoApplicableConstructor {
check("party: \"" + megaCorp.getName() + "\"", megaCorp.getName().toString(), FlowA.class);
}
@Test
public void unwrapLambda() throws InteractiveShell.NoApplicableConstructor {
check("party: \"" + megaCorp.getName() + "\", a: Bambam", "Bambam", FlowB.class);
}
}