mirror of
https://github.com/corda/corda.git
synced 2025-02-07 03:29:19 +00:00
Merge from O/S
This commit is contained in:
commit
3203bdae97
@ -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<?>)
|
||||||
|
@ -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",
|
||||||
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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,
|
||||||
|
@ -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
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -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()
|
||||||
|
}
|
||||||
|
@ -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,
|
||||||
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
@ -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() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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.
|
||||||
|
@ -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.
|
||||||
*/
|
*/
|
||||||
|
@ -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"]
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
||||||
|
}
|
@ -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) {
|
||||||
|
@ -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.
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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.")
|
||||||
|
}
|
||||||
|
}
|
@ -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() })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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 {
|
||||||
|
@ -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.
|
||||||
|
@ -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 ->
|
||||||
|
@ -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.
|
||||||
|
@ -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
|
||||||
|
Before Width: | Height: | Size: 74 KiB After Width: | Height: | Size: 74 KiB |
@ -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)
|
@ -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
|
||||||
|
@ -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
|
@ -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`.
|
||||||
|
|
||||||
|
@ -2,7 +2,6 @@ Serialization
|
|||||||
=============
|
=============
|
||||||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
|
|
||||||
:caption: Other docs
|
:caption: Other docs
|
||||||
:maxdepth: 1
|
:maxdepth: 1
|
||||||
|
|
||||||
|
@ -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:
|
||||||
|
@ -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
|
||||||
|
@ -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"))
|
||||||
|
@ -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(),
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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?)
|
||||||
|
|
||||||
|
@ -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 {
|
||||||
|
@ -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> {
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
@ -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 {
|
||||||
|
@ -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")
|
||||||
|
@ -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
|
||||||
|
@ -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> {
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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,
|
||||||
|
@ -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 },
|
||||||
|
@ -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)
|
||||||
|
@ -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(
|
||||||
|
@ -104,6 +104,9 @@ task jarFilter(type: JarFilterTask) {
|
|||||||
forRemove = [
|
forRemove = [
|
||||||
"co.paralleluniverse.fibers.Suspendable"
|
"co.paralleluniverse.fibers.Suspendable"
|
||||||
]
|
]
|
||||||
|
forSanitise = [
|
||||||
|
"net.corda.core.DeleteForDJVM"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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,
|
||||||
|
@ -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,
|
||||||
|
@ -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 },
|
||||||
|
@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user