Merge pull request #842 from corda/chrisr3-os-merge

Merge from OS
This commit is contained in:
Chris Rankin 2018-05-15 15:26:56 +01:00 committed by GitHub
commit a961b06668
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 187 additions and 130 deletions

View File

@ -108,6 +108,7 @@ see changes to this list.
* Lars Stage Thomsen (Danske Bank)
* Lee Braine (Barclays)
* Lucas Salmen (Itau)
* Lulu Ren (S-Labs)
* Maksymilian Pawlak (R3)
* Marek Scocovsky (ABSA)
* marekdapps
@ -138,7 +139,6 @@ see changes to this list.
* Przemyslaw Bak (R3)
* quiark
* RangerOfFire
* renlulu
* Rex Maudsley (Société Générale)
* Rhett Brewer (Goldman Sachs)
* Richard Crook (RBS)

View File

@ -54,8 +54,7 @@ dependencies {
compile "io.reactivex:rxjava:$rxjava_version"
compile("org.apache.activemq:artemis-core-client:${artemis_version}")
compile "org.apache.activemq:artemis-commons:${artemis_version}"
// Netty: All of it! Dn't depend upon ActiveMQ to pull it in correctly
compile "io.netty:netty-all:$netty_version"
compile "io.netty:netty-handler-proxy:$netty_version"
// TypeSafe Config: for simple and human friendly config files.
compile "com.typesafe:config:$typesafe_config_version"
@ -68,7 +67,9 @@ dependencies {
// Seems to be needed?
compile "com.github.ben-manes.caffeine:caffeine:$caffeine_version"
// Pulled in by whitelist
compile "com.esotericsoftware:kryo:4.0.0"
compile ("com.esotericsoftware:kryo:4.0.0") {
transitive = false
}
// Log4J: logging framework (with SLF4J bindings)
compile "org.apache.logging.log4j:log4j-slf4j-impl:$log4j_version"

View File

@ -276,12 +276,23 @@ allprojects {
force "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
force "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
force "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
// Demand that everything uses our given version of Netty.
eachDependency { details ->
if (details.requested.group == 'io.netty' && details.requested.name.startsWith('netty-')) {
details.useVersion netty_version
}
}
}
}
compile {
// We want to use SLF4J's version of these bindings: jcl-over-slf4j
// Remove any transitive dependency on Apache's version.
exclude group: 'commons-logging', module: 'commons-logging'
// Netty-All is an uber-jar which contains every Netty module.
// Exclude it to force us to use the individual Netty modules instead.
exclude group: 'io.netty', module: 'netty-all'
}
runtime {
// We never want isolated.jar on classPath, since we want to test jar being dynamically loaded as an attachment

View File

@ -444,7 +444,8 @@ abstract class FlowLogic<out T> {
*/
var stateMachine: FlowStateMachine<*>
@CordaInternal
get() = _stateMachine ?: throw IllegalStateException("This can only be done after the flow has been started.")
get() = _stateMachine ?: throw IllegalStateException(
"You cannot access the flow's state machine until the flow has been started.")
@CordaInternal
set(value) {
_stateMachine = value

View File

@ -6,16 +6,14 @@ release, see :doc:`upgrade-notes`.
Unreleased
==========
* Set co.paralleluniverse.fibers.verifyInstrumentation=true in devMode.
* Node will now gracefully fail to start if one of the required ports is already in use.
* Node will now gracefully fail to start if ``devMode`` is true and ``compatibilityZoneURL`` is specified.
* Added smart detection logic for the development mode setting and an option to override it from the command line.
* Fixed an error thrown by NodeVaultService upon recording a transaction with a number of inputs greater than the default page size.
* Fixed incorrect computation of ``totalStates`` from ``otherResults`` in ``NodeVaultService``.
* Changes to the JSON/YAML serialisation format from ``JacksonSupport``, which also applies to the node shell:
* ``Instant`` and ``Date`` objects are serialised as ISO-8601 formatted strings rather than timestamps

View File

@ -30,8 +30,7 @@ dependencies {
compile "org.apache.activemq:artemis-core-client:${artemis_version}"
compile "org.apache.activemq:artemis-commons:${artemis_version}"
// Netty: All of it.
compile "io.netty:netty-all:$netty_version"
compile "io.netty:netty-handler-proxy:$netty_version"
// For adding serialisation of file upload streams to RPC
// TODO: Remove this dependency and the code that requires it

View File

@ -102,7 +102,12 @@ internal class AttachmentsClassLoaderBuilder(private val properties: Map<Any, An
}
}
open class SerializationFactoryImpl : SerializationFactory() {
open class SerializationFactoryImpl(
// TODO: This is read-mostly. Probably a faster implementation to be found.
private val schemes: MutableMap<Pair<CordaSerializationMagic, SerializationContext.UseCase>, SerializationScheme>
) : SerializationFactory() {
constructor() : this(ConcurrentHashMap())
companion object {
val magicSize = sequenceOf(kryoMagic, amqpMagic).map { it.size }.distinct().single()
}
@ -113,9 +118,6 @@ open class SerializationFactoryImpl : SerializationFactory() {
private val logger = LoggerFactory.getLogger(javaClass)
// TODO: This is read-mostly. Probably a faster implementation to be found.
private val schemes: ConcurrentHashMap<Pair<CordaSerializationMagic, SerializationContext.UseCase>, SerializationScheme> = ConcurrentHashMap()
private fun schemeFor(byteSequence: ByteSequence, target: SerializationContext.UseCase): Pair<SerializationScheme, CordaSerializationMagic> {
// truncate sequence to at most magicSize, and make sure it's a copy to avoid holding onto large ByteArrays
val magic = CordaSerializationMagic(byteSequence.slice(end = magicSize).copyBytes())

View File

@ -45,10 +45,11 @@ interface SerializerFactoryFactory {
}
abstract class AbstractAMQPSerializationScheme(
private val cordappCustomSerializers: Set<SerializationCustomSerializer<*,*>>,
val sff: SerializerFactoryFactory = createSerializerFactoryFactory()
private val cordappCustomSerializers: Set<SerializationCustomSerializer<*,*>>,
private val serializerFactoriesForContexts: MutableMap<Pair<ClassWhitelist, ClassLoader>, SerializerFactory>,
val sff: SerializerFactoryFactory = createSerializerFactoryFactory()
) : SerializationScheme {
constructor(cordapps: List<Cordapp>) : this(cordapps.customSerializers)
constructor(cordapps: List<Cordapp>) : this(cordapps.customSerializers, ConcurrentHashMap())
// TODO: This method of initialisation for the Whitelist and plugin serializers will have to change
// when we have per-cordapp contexts and dynamic app reloading but for now it's the easiest way
@ -138,8 +139,6 @@ abstract class AbstractAMQPSerializationScheme(
}
}
private val serializerFactoriesForContexts = ConcurrentHashMap<Pair<ClassWhitelist, ClassLoader>, SerializerFactory>()
protected abstract fun rpcClientSerializerFactory(context: SerializationContext): SerializerFactory
protected abstract fun rpcServerSerializerFactory(context: SerializationContext): SerializerFactory
protected open val publicKeySerializer: CustomSerializer.Implements<PublicKey> = net.corda.nodeapi.internal.serialization.amqp.custom.PublicKeySerializer
@ -175,9 +174,13 @@ abstract class AbstractAMQPSerializationScheme(
}
// TODO: This will eventually cover server RPC as well and move to node module, but for now this is not implemented
class AMQPServerSerializationScheme(cordappCustomSerializers: Set<SerializationCustomSerializer<*, *>> = emptySet())
: AbstractAMQPSerializationScheme(cordappCustomSerializers) {
constructor(cordapps: List<Cordapp>) : this(cordapps.customSerializers)
class AMQPServerSerializationScheme(
cordappCustomSerializers: Set<SerializationCustomSerializer<*, *>>,
serializerFactoriesForContexts: MutableMap<Pair<ClassWhitelist, ClassLoader>, SerializerFactory>
) : AbstractAMQPSerializationScheme(cordappCustomSerializers, serializerFactoriesForContexts) {
constructor(cordapps: List<Cordapp>) : this(cordapps.customSerializers, ConcurrentHashMap())
constructor() : this(emptySet(), ConcurrentHashMap())
override fun rpcClientSerializerFactory(context: SerializationContext): SerializerFactory {
throw UnsupportedOperationException()
@ -195,9 +198,13 @@ class AMQPServerSerializationScheme(cordappCustomSerializers: Set<SerializationC
}
// TODO: This will eventually cover client RPC as well and move to client module, but for now this is not implemented
class AMQPClientSerializationScheme(cordappCustomSerializers: Set<SerializationCustomSerializer<*,*>> = emptySet())
: AbstractAMQPSerializationScheme(cordappCustomSerializers) {
constructor(cordapps: List<Cordapp>) : this(cordapps.customSerializers)
class AMQPClientSerializationScheme(
cordappCustomSerializers: Set<SerializationCustomSerializer<*,*>>,
serializerFactoriesForContexts: MutableMap<Pair<ClassWhitelist, ClassLoader>, SerializerFactory>
) : AbstractAMQPSerializationScheme(cordappCustomSerializers, serializerFactoriesForContexts) {
constructor(cordapps: List<Cordapp>) : this(cordapps.customSerializers, ConcurrentHashMap())
constructor() : this(emptySet(), ConcurrentHashMap())
override fun rpcClientSerializerFactory(context: SerializationContext): SerializerFactory {
throw UnsupportedOperationException()

View File

@ -1,5 +1,13 @@
/*
* R3 Proprietary and Confidential
*
* Copyright (c) 2018 R3 Limited. All rights reserved.
*
* The intellectual and technical concepts contained herein are proprietary to R3 and its suppliers and are protected by trade secret law.
*
* Distribution of this file or any portion thereof via any medium without the express permission of R3 is strictly prohibited.
*/
@file:JvmName("AMQPSerializerFactories")
package net.corda.nodeapi.internal.serialization.amqp
import net.corda.core.serialization.SerializationContext

View File

@ -68,7 +68,7 @@ abstract class CustomSerializer<T : Any> : AMQPSerializer<T>, SerializerFor {
* subclass in the schema, so that we can distinguish between subclasses.
*/
// TODO: should this be a custom serializer at all, or should it just be a plain AMQPSerializer?
class SubClass<T : Any>(protected val clazz: Class<*>, protected val superClassSerializer: CustomSerializer<T>) : CustomSerializer<T>() {
class SubClass<T : Any>(private val clazz: Class<*>, private val superClassSerializer: CustomSerializer<T>) : CustomSerializer<T>() {
// TODO: should this be empty or contain the schema of the super?
override val schemaForDocumentation = Schema(emptyList())

View File

@ -249,7 +249,7 @@ class EvolutionSerializerGetter : EvolutionSerializerGetterBase() {
typeNotation: TypeNotation,
newSerializer: AMQPSerializer<Any>,
schemas: SerializationSchemas): AMQPSerializer<Any> {
return factory.getSerializersByDescriptor().computeIfAbsent(typeNotation.descriptor.name!!) {
return factory.serializersByDescriptor.computeIfAbsent(typeNotation.descriptor.name!!) {
when (typeNotation) {
is CompositeType -> EvolutionSerializer.make(typeNotation, newSerializer as ObjectSerializer, factory)
is RestrictedType -> {

View File

@ -50,32 +50,40 @@ open class SerializerFactory(
val whitelist: ClassWhitelist,
val classCarpenter: ClassCarpenter,
private val evolutionSerializerGetter: EvolutionSerializerGetterBase = EvolutionSerializerGetter(),
val fingerPrinter: FingerPrinter = SerializerFingerPrinter()) {
val fingerPrinter: FingerPrinter = SerializerFingerPrinter(),
private val serializersByType: MutableMap<Type, AMQPSerializer<Any>>,
val serializersByDescriptor: MutableMap<Any, AMQPSerializer<Any>>,
private val customSerializers: MutableList<SerializerFor>,
val transformsCache: MutableMap<String, EnumMap<TransformTypes, MutableList<Transform>>>) {
constructor(whitelist: ClassWhitelist,
classCarpenter: ClassCarpenter,
evolutionSerializerGetter: EvolutionSerializerGetterBase = EvolutionSerializerGetter(),
fingerPrinter: FingerPrinter = SerializerFingerPrinter()
) : this(whitelist, classCarpenter, evolutionSerializerGetter, fingerPrinter,
serializersByType = ConcurrentHashMap(),
serializersByDescriptor = ConcurrentHashMap(),
customSerializers = CopyOnWriteArrayList(),
transformsCache = ConcurrentHashMap())
constructor(whitelist: ClassWhitelist,
classLoader: ClassLoader,
evolutionSerializerGetter: EvolutionSerializerGetterBase = EvolutionSerializerGetter(),
fingerPrinter: FingerPrinter = SerializerFingerPrinter()
) : this(whitelist, ClassCarpenterImpl(classLoader, whitelist), evolutionSerializerGetter, fingerPrinter)
) : this(whitelist, ClassCarpenterImpl(classLoader, whitelist), evolutionSerializerGetter, fingerPrinter,
serializersByType = ConcurrentHashMap(),
serializersByDescriptor = ConcurrentHashMap(),
customSerializers = CopyOnWriteArrayList(),
transformsCache = ConcurrentHashMap())
init {
fingerPrinter.setOwner(this)
}
private val serializersByType = ConcurrentHashMap<Type, AMQPSerializer<Any>>()
private val serializersByDescriptor = ConcurrentHashMap<Any, AMQPSerializer<Any>>()
private val customSerializers = CopyOnWriteArrayList<SerializerFor>()
private val transformsCache = ConcurrentHashMap<String, EnumMap<TransformTypes, MutableList<Transform>>>()
val classloader: ClassLoader
get() = classCarpenter.classloader
private fun getEvolutionSerializer(typeNotation: TypeNotation, newSerializer: AMQPSerializer<Any>,
schemas: SerializationSchemas) = evolutionSerializerGetter.getEvolutionSerializer(this, typeNotation, newSerializer, schemas)
fun getSerializersByDescriptor() = serializersByDescriptor
fun getTransformsCache() = transformsCache
/**
* Look up, and manufacture if necessary, a serializer for the given type.
*
@ -229,7 +237,7 @@ open class SerializerFactory(
/**
* Iterate over an AMQP schema, for each type ascertain whether it's on ClassPath of [classloader] and,
* if not, use the [ClassCarpenter] to generate a class to use in it's place.
* if not, use the [ClassCarpenter] to generate a class to use in its place.
*/
private fun processSchema(schemaAndDescriptor: FactorySchemaAndDescriptor, sentinel: Boolean = false) {
val metaSchema = CarpenterMetaSchema.newInstance()
@ -249,24 +257,28 @@ open class SerializerFactory(
}
if (metaSchema.isNotEmpty()) {
val mc = MetaCarpenter(metaSchema, classCarpenter)
try {
mc.build()
} catch (e: MetaCarpenterException) {
// preserve the actual message locally
loggerFor<SerializerFactory>().apply {
error("${e.message} [hint: enable trace debugging for the stack trace]")
trace("", e)
}
// prevent carpenter exceptions escaping into the world, convert things into a nice
// NotSerializableException for when this escapes over the wire
throw NotSerializableException(e.name)
}
processSchema(schemaAndDescriptor, true)
runCarpentry(schemaAndDescriptor, metaSchema)
}
}
private fun runCarpentry(schemaAndDescriptor: FactorySchemaAndDescriptor, metaSchema: CarpenterMetaSchema) {
val mc = MetaCarpenter(metaSchema, classCarpenter)
try {
mc.build()
} catch (e: MetaCarpenterException) {
// preserve the actual message locally
loggerFor<SerializerFactory>().apply {
error("${e.message} [hint: enable trace debugging for the stack trace]")
trace("", e)
}
// prevent carpenter exceptions escaping into the world, convert things into a nice
// NotSerializableException for when this escapes over the wire
throw NotSerializableException(e.name)
}
processSchema(schemaAndDescriptor, true)
}
private fun processSchemaEntry(typeNotation: TypeNotation) = when (typeNotation) {
is CompositeType -> processCompositeType(typeNotation) // java.lang.Class (whether a class or interface)
is RestrictedType -> processRestrictedType(typeNotation) // Collection / Map, possibly with generics

View File

@ -210,7 +210,7 @@ data class TransformsSchema(val types: Map<String, EnumMap<TransformTypes, Mutab
* @param sf the [SerializerFactory] building this transform set. Needed as each can define it's own
* class loader and this dictates which classes we can and cannot see
*/
fun get(name: String, sf: SerializerFactory) = sf.getTransformsCache().computeIfAbsent(name) {
fun get(name: String, sf: SerializerFactory) = sf.transformsCache.computeIfAbsent(name) {
val transforms = EnumMap<TransformTypes, MutableList<Transform>>(TransformTypes::class.java)
try {
val clazz = sf.classloader.loadClass(name)

View File

@ -58,7 +58,7 @@ private val toStringHelper: String = Type.getInternalName(MoreObjects.ToStringHe
// Allow us to create alternative ClassCarpenters.
interface ClassCarpenter {
val whitelist: ClassWhitelist
val classloader: CarpenterClassLoader
val classloader: ClassLoader
fun build(schema: Schema): Class<*>
}

View File

@ -40,8 +40,8 @@ abstract class ClassField(field: Class<out Any?>) : Field(field) {
abstract val nullabilityAnnotation: String
abstract fun nullTest(mv: MethodVisitor, slot: Int)
override var descriptor = Type.getDescriptor(this.field)
override val type: String get() = if (this.field.isPrimitive) this.descriptor else "Ljava/lang/Object;"
override var descriptor: String? = Type.getDescriptor(this.field)
override val type: String get() = if (this.field.isPrimitive) this.descriptor!! else "Ljava/lang/Object;"
fun addNullabilityAnnotation(mv: MethodVisitor) {
mv.visitAnnotation(nullabilityAnnotation, true).visitEnd()

View File

@ -17,7 +17,7 @@ import static org.junit.Assert.*;
import java.io.NotSerializableException;
import java.lang.reflect.Field;
import java.util.concurrent.ConcurrentHashMap;
import java.util.Map;
public class JavaPrivatePropertyTests {
static class C {
@ -126,7 +126,7 @@ public class JavaPrivatePropertyTests {
B3 b2 = des.deserialize(ser.serialize(b, TestSerializationContext.testSerializationContext), B3.class, TestSerializationContext.testSerializationContext);
// since we can't find a getter for b (isb != isB) then we won't serialize that parameter
assertEquals (null, b2.b);
assertNull (b2.b);
}
@Test
@ -164,8 +164,7 @@ public class JavaPrivatePropertyTests {
Field f = SerializerFactory.class.getDeclaredField("serializersByDescriptor");
f.setAccessible(true);
ConcurrentHashMap<Object, AMQPSerializer<Object>> serializersByDescriptor =
(ConcurrentHashMap<Object, AMQPSerializer<Object>>) f.get(factory);
Map<?, AMQPSerializer<?>> serializersByDescriptor = (Map<?, AMQPSerializer<?>>) f.get(factory);
assertEquals(1, serializersByDescriptor.size());
ObjectSerializer cSerializer = ((ObjectSerializer)serializersByDescriptor.values().toArray()[0]);
@ -195,8 +194,7 @@ public class JavaPrivatePropertyTests {
//
Field f = SerializerFactory.class.getDeclaredField("serializersByDescriptor");
f.setAccessible(true);
ConcurrentHashMap<Object, AMQPSerializer<Object>> serializersByDescriptor =
(ConcurrentHashMap<Object, AMQPSerializer<Object>>) f.get(factory);
Map<?, AMQPSerializer<?>> serializersByDescriptor = (Map<?, AMQPSerializer<?>>) f.get(factory);
assertEquals(1, serializersByDescriptor.size());
ObjectSerializer cSerializer = ((ObjectSerializer)serializersByDescriptor.values().toArray()[0]);

View File

@ -20,7 +20,6 @@ import net.corda.core.identity.CordaX500Name
import net.corda.nodeapi.internal.serialization.amqp.testutils.*
import net.corda.testing.core.TestIdentity
import java.util.*
import java.util.concurrent.ConcurrentHashMap
import kotlin.test.assertEquals
data class TestContractState(
@ -46,7 +45,7 @@ class GenericsTests {
private fun <T : Any> BytesAndSchemas<T>.printSchema() = if (VERBOSE) println("${this.schema}\n") else Unit
private fun ConcurrentHashMap<Any, AMQPSerializer<Any>>.printKeyToType() {
private fun MutableMap<Any, AMQPSerializer<Any>>.printKeyToType() {
if (!VERBOSE) return
forEach {
@ -63,11 +62,11 @@ class GenericsTests {
val bytes1 = SerializationOutput(factory).serializeAndReturnSchema(G("hi")).apply { printSchema() }
factory.getSerializersByDescriptor().printKeyToType()
factory.serializersByDescriptor.printKeyToType()
val bytes2 = SerializationOutput(factory).serializeAndReturnSchema(G(121)).apply { printSchema() }
factory.getSerializersByDescriptor().printKeyToType()
factory.serializersByDescriptor.printKeyToType()
listOf(factory, testDefaultFactory()).forEach { f ->
DeserializationInput(f).deserialize(bytes1.obj).apply { assertEquals("hi", this.a) }
@ -100,14 +99,14 @@ class GenericsTests {
val bytes = ser.serializeAndReturnSchema(G("hi")).apply { printSchema() }
factory.getSerializersByDescriptor().printKeyToType()
factory.serializersByDescriptor.printKeyToType()
assertEquals("hi", DeserializationInput(factory).deserialize(bytes.obj).a)
assertEquals("hi", DeserializationInput(altContextFactory).deserialize(bytes.obj).a)
val bytes2 = ser.serializeAndReturnSchema(Wrapper(1, G("hi"))).apply { printSchema() }
factory.getSerializersByDescriptor().printKeyToType()
factory.serializersByDescriptor.printKeyToType()
printSeparator()
@ -159,21 +158,21 @@ class GenericsTests {
ser.serialize(Wrapper(Container(InnerA(1)))).apply {
factories.forEach {
DeserializationInput(it).deserialize(this).apply { assertEquals(1, c.b.a_a) }
it.getSerializersByDescriptor().printKeyToType(); printSeparator()
it.serializersByDescriptor.printKeyToType(); printSeparator()
}
}
ser.serialize(Wrapper(Container(InnerB(1)))).apply {
factories.forEach {
DeserializationInput(it).deserialize(this).apply { assertEquals(1, c.b.a_b) }
it.getSerializersByDescriptor().printKeyToType(); printSeparator()
it.serializersByDescriptor.printKeyToType(); printSeparator()
}
}
ser.serialize(Wrapper(Container(InnerC("Ho ho ho")))).apply {
factories.forEach {
DeserializationInput(it).deserialize(this).apply { assertEquals("Ho ho ho", c.b.a_c) }
it.getSerializersByDescriptor().printKeyToType(); printSeparator()
it.serializersByDescriptor.printKeyToType(); printSeparator()
}
}
}
@ -209,7 +208,7 @@ class GenericsTests {
a: ForceWildcard<*>,
factory: SerializerFactory = SerializerFactory(AllWhitelist, ClassLoader.getSystemClassLoader())): SerializedBytes<*> {
val bytes = SerializationOutput(factory).serializeAndReturnSchema(a)
factory.getSerializersByDescriptor().printKeyToType()
factory.serializersByDescriptor.printKeyToType()
bytes.printSchema()
return bytes.obj
}

View File

@ -4,6 +4,7 @@ import net.corda.core.serialization.*
import net.corda.core.utilities.ByteSequence
import net.corda.nodeapi.internal.serialization.*
import org.junit.Test
import java.util.concurrent.ConcurrentHashMap
import kotlin.test.assertEquals
// Make sure all serialization calls in this test don't get stomped on by anything else
@ -43,7 +44,7 @@ class TestSerializerFactoryFactory : SerializerFactoryFactoryImpl() {
}
}
class AMQPTestSerializationScheme : AbstractAMQPSerializationScheme(emptySet(), TestSerializerFactoryFactory()) {
class AMQPTestSerializationScheme : AbstractAMQPSerializationScheme(emptySet(), ConcurrentHashMap(), TestSerializerFactoryFactory()) {
override fun rpcClientSerializerFactory(context: SerializationContext): SerializerFactory {
throw UnsupportedOperationException()
}

View File

@ -29,13 +29,6 @@ buildscript {
//noinspection GroovyAssignabilityCheck
configurations {
compile {
// We don't need these because we already include netty-all.
exclude group: 'io.netty', module: 'netty-transport'
exclude group: 'io.netty', module: 'netty-handler'
exclude group: 'io.netty', module: 'netty-common'
}
integrationTestCompile.extendsFrom testCompile
integrationTestRuntime.extendsFrom testRuntime
}

View File

@ -135,10 +135,7 @@ import net.corda.node.services.transactions.SimpleNotaryService
import net.corda.node.services.transactions.ValidatingNotaryService
import net.corda.node.services.upgrade.ContractUpgradeServiceImpl
import net.corda.node.services.vault.NodeVaultService
import net.corda.node.utilities.AffinityExecutor
import net.corda.node.utilities.JVMAgentRegistry
import net.corda.node.utilities.NamedThreadFactory
import net.corda.node.utilities.NodeBuildProperties
import net.corda.node.utilities.*
import net.corda.nodeapi.internal.DevIdentityGenerator
import net.corda.nodeapi.internal.NodeInfoAndSigned
import net.corda.nodeapi.internal.SignedNodeInfo
@ -282,6 +279,7 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
open fun start(): StartedNode<AbstractNode> {
check(started == null) { "Node has already been started" }
if (configuration.devMode) {
System.setProperty("co.paralleluniverse.fibers.verifyInstrumentation", "true")
Emoji.renderIfSupported { Node.printWarning("This node is running in developer mode! ${Emoji.developer} This is not safe for production deployment.") }
}
log.info("Node starting up ...")

View File

@ -0,0 +1,33 @@
/*
* R3 Proprietary and Confidential
*
* Copyright (c) 2018 R3 Limited. All rights reserved.
*
* The intellectual and technical concepts contained herein are proprietary to R3 and its suppliers and are protected by trade secret law.
*
* Distribution of this file or any portion thereof via any medium without the express permission of R3 is strictly prohibited.
*/
package net.corda.node.services.identity
import net.corda.core.identity.CordaX500Name
import net.corda.core.identity.Party
fun partiesFromName(query: String, exactMatch: Boolean, x500name: CordaX500Name, results: LinkedHashSet<Party>, party: Party) {
val components = listOfNotNull(x500name.commonName, x500name.organisationUnit, x500name.organisation, x500name.locality, x500name.state, x500name.country)
components.forEach { component ->
if (exactMatch && component == query) {
results += party
} else if (!exactMatch) {
// We can imagine this being a query over a lucene index in future.
//
// Kostas says: We can easily use the Jaro-Winkler distance metric as it is best suited for short
// strings such as entity/company names, and to detect small typos. We can also apply it for city
// or any keyword related search in lists of records (not raw text - for raw text we need indexing)
// and we can return results in hierarchical order (based on normalised String similarity 0.0-1.0).
if (component.contains(query, ignoreCase = true))
results += party
}
}
}

View File

@ -113,22 +113,7 @@ class InMemoryIdentityService(identities: Array<out PartyAndCertificate>,
override fun partiesFromName(query: String, exactMatch: Boolean): Set<Party> {
val results = LinkedHashSet<Party>()
for ((x500name, partyAndCertificate) in principalToParties) {
val party = partyAndCertificate.party
val components = listOfNotNull(x500name.commonName, x500name.organisationUnit, x500name.organisation, x500name.locality, x500name.state, x500name.country)
components.forEach { component ->
if (exactMatch && component == query) {
results += party
} else if (!exactMatch) {
// We can imagine this being a query over a lucene index in future.
//
// Kostas says: We can easily use the Jaro-Winkler distance metric as it is best suited for short
// strings such as entity/company names, and to detect small typos. We can also apply it for city
// or any keyword related search in lists of records (not raw text - for raw text we need indexing)
// and we can return results in hierarchical order (based on normalised String similarity 0.0-1.0).
if (component.contains(query, ignoreCase = true))
results += party
}
}
partiesFromName(query, exactMatch, x500name, results, partyAndCertificate.party)
}
return results
}

View File

@ -190,22 +190,7 @@ class PersistentIdentityService(override val trustRoot: X509Certificate,
override fun partiesFromName(query: String, exactMatch: Boolean): Set<Party> {
val results = LinkedHashSet<Party>()
for ((x500name, partyId) in principalToParties.allPersisted()) {
val party = keyToParties[partyId]!!.party
val components = listOfNotNull(x500name.commonName, x500name.organisationUnit, x500name.organisation, x500name.locality, x500name.state, x500name.country)
components.forEach { component ->
if (exactMatch && component == query) {
results += party
} else if (!exactMatch) {
// We can imagine this being a query over a lucene index in future.
//
// Kostas says: We can easily use the Jaro-Winkler distance metric as it is best suited for short
// strings such as entity/company names, and to detect small typos. We can also apply it for city
// or any keyword related search in lists of records (not raw text - for raw text we need indexing)
// and we can return results in hierarchical order (based on normalised String similarity 0.0-1.0).
if (component.contains(query, ignoreCase = true))
results += party
}
}
partiesFromName(query, exactMatch, x500name, results, keyToParties[partyId]!!.party)
}
return results
}

View File

@ -407,13 +407,18 @@ class NodeVaultService(
@Throws(VaultQueryException::class)
override fun <T : ContractState> _queryBy(criteria: QueryCriteria, paging: PageSpecification, sorting: Sort, contractStateType: Class<out T>): Vault.Page<T> {
return _queryBy(criteria, paging, sorting, contractStateType, false)
}
@Throws(VaultQueryException::class)
private fun <T : ContractState> _queryBy(criteria: QueryCriteria, paging: PageSpecification, sorting: Sort, contractStateType: Class<out T>, skipPagingChecks: Boolean): Vault.Page<T> {
log.debug {"Vault Query for contract type: $contractStateType, criteria: $criteria, pagination: $paging, sorting: $sorting" }
// calculate total results where a page specification has been defined
var totalStates = -1L
if (!paging.isDefault) {
if (!skipPagingChecks && !paging.isDefault) {
val count = builder { VaultSchemaV1.VaultStates::recordedTime.count() }
val countCriteria = QueryCriteria.VaultCustomQueryCriteria(count, Vault.StateStatus.ALL)
val results = queryBy(contractStateType, criteria.and(countCriteria))
val results = _queryBy(criteria.and(countCriteria), PageSpecification(), Sort(emptyList()), contractStateType, true) // only skip pagination checks for total results count query
totalStates = results.otherResults.last() as Long
}
@ -432,7 +437,7 @@ class NodeVaultService(
val query = session.createQuery(criteriaQuery)
// pagination checks
if (!paging.isDefault) {
if (!skipPagingChecks && !paging.isDefault) {
// pagination
if (paging.pageNumber < DEFAULT_PAGE_NUM) throw VaultQueryException("Page specification: invalid page number ${paging.pageNumber} [page numbers start from $DEFAULT_PAGE_NUM]")
if (paging.pageSize < 1) throw VaultQueryException("Page specification: invalid page size ${paging.pageSize} [must be a value between 1 and $MAX_PAGE_SIZE]")
@ -445,7 +450,7 @@ class NodeVaultService(
val results = query.resultList
// final pagination check (fail-fast on too many results when no pagination specified)
if (paging.isDefault && results.size > DEFAULT_PAGE_SIZE)
if (!skipPagingChecks && paging.isDefault && results.size > DEFAULT_PAGE_SIZE)
throw VaultQueryException("Please specify a `PageSpecification` as there are more results [${results.size}] than the default page size [$DEFAULT_PAGE_SIZE]")
val statesAndRefs: MutableList<StateAndRef<T>> = mutableListOf()

View File

@ -38,10 +38,7 @@ import net.corda.nodeapi.internal.persistence.DatabaseTransaction
import net.corda.testing.core.*
import net.corda.testing.internal.TEST_TX_TIME
import net.corda.testing.internal.rigorousMock
import net.corda.testing.internal.vault.DUMMY_LINEAR_CONTRACT_PROGRAM_ID
import net.corda.testing.internal.vault.DummyLinearContract
import net.corda.testing.internal.vault.DummyLinearStateSchemaV1
import net.corda.testing.internal.vault.VaultFiller
import net.corda.testing.internal.vault.*
import net.corda.testing.node.MockServices
import net.corda.testing.node.MockServices.Companion.makeTestDatabaseAndMockServices
import net.corda.testing.node.makeTestIdentityService
@ -1190,6 +1187,30 @@ abstract class VaultQueryTestsBase : VaultQueryParties {
}
}
// test paging with aggregate function and group by clause
@Test
fun `test paging with aggregate function and group by clause`() {
database.transaction {
(0..200).forEach {
vaultFiller.fillWithSomeTestLinearStates(1, linearNumber = it.toLong(), linearString = it.toString())
}
val max = builder { DummyLinearStateSchemaV1.PersistentDummyLinearState::linearTimestamp.max(
groupByColumns = listOf(DummyLinearStateSchemaV1.PersistentDummyLinearState::linearNumber)
)
}
val maxCriteria = VaultCustomQueryCriteria(max)
val pageSpec = PageSpecification(DEFAULT_PAGE_NUM, MAX_PAGE_SIZE)
val results = vaultService.queryBy<DummyLinearContract.State>(maxCriteria, paging = pageSpec)
println("Total states available: ${results.totalStatesAvailable}")
results.otherResults.forEachIndexed { index, any ->
println("$index : $any")
}
assertThat(results.otherResults.size).isEqualTo(402)
assertThat(results.otherResults.last()).isEqualTo(200L)
}
}
// sorting
@Test
fun `sorting - all states sorted by contract type, state status, consumed time`() {

View File

@ -32,7 +32,7 @@ private class EnclaveletSerializationScheme {
private companion object {
init {
nodeSerializationEnv = SerializationEnvironmentImpl(
SerializationFactoryImpl().apply {
SerializationFactoryImpl(HashMap()).apply {
registerScheme(KryoVerifierSerializationScheme)
registerScheme(AMQPVerifierSerializationScheme)
},
@ -59,7 +59,7 @@ private object KryoVerifierSerializationScheme : AbstractKryoSerializationScheme
override fun rpcServerKryoPool(context: SerializationContext) = throw UnsupportedOperationException()
}
private object AMQPVerifierSerializationScheme : AbstractAMQPSerializationScheme(emptyList()) {
private object AMQPVerifierSerializationScheme : AbstractAMQPSerializationScheme(emptySet(), HashMap()) {
override fun canDeserializeVersion(magic: CordaSerializationMagic, target: SerializationContext.UseCase): Boolean {
return magic == amqpMagic && target == SerializationContext.UseCase.P2P
}