Merge branch 'release/os/4.3' into vkolomeyko/4.3-merge

# Conflicts:
#	constants.properties
This commit is contained in:
Viktor Kolomeyko 2019-09-26 16:23:58 +01:00
commit 39828326a9
53 changed files with 317 additions and 130 deletions

View File

@ -384,7 +384,7 @@ dependencies {
// Set to corda compile to ensure it exists now deploy nodes no longer relies on build
compile project(path: ":node:capsule", configuration: 'runtimeArtifacts')
compile project(path: ":webserver:webcapsule", configuration: 'runtimeArtifacts')
compile project(path: ":testing:testserver:testcapsule:", configuration: 'runtimeArtifacts')
// For the buildCordappDependenciesJar task
runtime project(':client:jfx')
@ -394,7 +394,7 @@ dependencies {
runtime project(':confidential-identities')
runtime project(':finance:workflows')
runtime project(':finance:contracts')
runtime project(':webserver')
runtime project(':testing:testserver')
testCompile project(':test-utils')
detekt 'io.gitlab.arturbosch.detekt:detekt-cli:1.0.1'
}
@ -481,8 +481,8 @@ bintrayConfig {
'corda-test-utils',
'corda-test-db',
'corda-jackson',
'corda-webserver-impl',
'corda-webserver',
'corda-testserver-impl',
'corda-testserver',
'corda-node-driver',
'corda-confidential-identities',
'corda-shell',

View File

@ -0,0 +1,91 @@
package net.corda.client.rpc
import com.nhaarman.mockito_kotlin.mock
import net.corda.client.rpc.RPCMultipleInterfacesTests.StringRPCOpsImpl.testPhrase
import net.corda.core.crypto.SecureHash
import net.corda.core.messaging.CordaRPCOps
import net.corda.core.messaging.RPCOps
import net.corda.testing.core.SerializationEnvironmentRule
import net.corda.testing.node.internal.rpcDriver
import net.corda.testing.node.internal.startRpcClient
import org.assertj.core.api.Assertions
import org.junit.Assert.*
import org.junit.Rule
import org.junit.Test
import rx.Observable
class RPCMultipleInterfacesTests {
@Rule
@JvmField
val testSerialization = SerializationEnvironmentRule(true)
companion object {
const val sampleSize = 30
}
interface IntRPCOps : RPCOps {
fun stream(size: Int): Observable<Int>
fun intTestMethod(): Int
}
interface StringRPCOps : RPCOps {
fun stream(size: Int): Observable<String>
fun stringTestMethod() : String
}
private class IntRPCOpsImpl : IntRPCOps {
override val protocolVersion = 1000
override fun stream(size: Int): Observable<Int> {
return Observable.range(0, size)
}
override fun intTestMethod(): Int = protocolVersion
}
private object StringRPCOpsImpl : StringRPCOps {
const val testPhrase = "I work with Strings."
override val protocolVersion = 1000
override fun stream(size: Int): Observable<String> {
return Observable.range(0, size).map { it.toString(8) }
}
override fun stringTestMethod(): String = testPhrase
}
private object MyCordaRpcOpsImpl : CordaRPCOps by mock() {
override val protocolVersion = 1000
}
interface ImaginaryFriend : RPCOps
@Test
fun `can talk multiple interfaces`() {
rpcDriver {
val server = startRpcServer(listOps = listOf(IntRPCOpsImpl(), StringRPCOpsImpl, MyCordaRpcOpsImpl)).get()
val clientInt = startRpcClient<IntRPCOps>(server.broker.hostAndPort!!).get()
val intList = clientInt.stream(sampleSize).toList().toBlocking().single()
assertEquals(sampleSize, intList.size)
val clientString = startRpcClient<StringRPCOps>(server.broker.hostAndPort!!).get()
val stringList = clientString.stream(sampleSize).toList().toBlocking().single()
assertEquals(sampleSize, stringList.size)
assertTrue(stringList.toString(), stringList.all { it.matches("[0-7]*".toRegex()) })
assertEquals(testPhrase, clientString.stringTestMethod())
val rpcOpsClient = startRpcClient<CordaRPCOps>(server.broker.hostAndPort!!).get()
assertFalse(rpcOpsClient.attachmentExists(SecureHash.zeroHash))
Assertions.assertThatThrownBy { startRpcClient<ImaginaryFriend>(server.broker.hostAndPort!!).get() }
.hasCauseInstanceOf(RPCException::class.java).hasMessageContaining("possible client/server version skew")
server.rpcServer.close()
}
}
}

View File

@ -17,6 +17,7 @@ import net.corda.core.context.Trace
import net.corda.core.context.Trace.InvocationId
import net.corda.core.identity.CordaX500Name
import net.corda.core.internal.*
import net.corda.core.messaging.CordaRPCOps
import net.corda.core.messaging.RPCOps
import net.corda.core.serialization.SerializationContext
import net.corda.core.serialization.serialize
@ -25,6 +26,7 @@ import net.corda.core.utilities.contextLogger
import net.corda.core.utilities.debug
import net.corda.core.utilities.getOrThrow
import net.corda.nodeapi.RPCApi
import net.corda.nodeapi.RPCApi.CLASS_METHOD_DIVIDER
import net.corda.nodeapi.internal.DeduplicationChecker
import org.apache.activemq.artemis.api.core.ActiveMQException
import org.apache.activemq.artemis.api.core.ActiveMQNotConnectedException
@ -252,12 +254,13 @@ class RPCClientProxyHandler(
throw RPCException("RPC server is not available.")
val replyId = InvocationId.newInstance()
callSiteMap?.set(replyId, CallSite(method.name))
val methodFqn = produceMethodFullyQualifiedName(method)
callSiteMap?.set(replyId, CallSite(methodFqn))
try {
val serialisedArguments = (arguments?.toList() ?: emptyList()).serialize(context = serializationContextWithObservableContext)
val request = RPCApi.ClientToServer.RpcRequest(
clientAddress,
method.name,
methodFqn,
serialisedArguments,
replyId,
sessionId,
@ -282,6 +285,15 @@ class RPCClientProxyHandler(
}
}
private fun produceMethodFullyQualifiedName(method: Method) : String {
// For CordaRPCOps send method only - for backwards compatibility
return if (CordaRPCOps::class.java == rpcOpsClass) {
method.name
} else {
rpcOpsClass.name + CLASS_METHOD_DIVIDER + method.name
}
}
private fun sendMessage(message: RPCApi.ClientToServer) {
val artemisMessage = producerSession!!.createMessage(false)
message.writeToClientMessage(artemisMessage)

View File

@ -3,7 +3,7 @@
# their own projects. So don't get fancy with syntax!
cordaVersion=4.4-SNAPSHOT
gradlePluginsVersion=5.0.2
gradlePluginsVersion=5.0.3
kotlinVersion=1.2.71
java8MinUpdateVersion=171
# ***************************************************************#

View File

@ -872,8 +872,10 @@
<ID>LongParameterList:QueryCriteria.kt$QueryCriteria.VaultQueryCriteria$( status: Vault.StateStatus = this.status, contractStateTypes: Set&lt;Class&lt;out ContractState&gt;&gt;? = this.contractStateTypes, stateRefs: List&lt;StateRef&gt;? = this.stateRefs, notary: List&lt;AbstractParty&gt;? = this.notary, softLockingCondition: SoftLockingCondition? = this.softLockingCondition, timeCondition: TimeCondition? = this.timeCondition )</ID>
<ID>LongParameterList:RPCClient.kt$RPCClient$( rpcOpsClass: Class&lt;I&gt;, username: String, password: String, externalTrace: Trace? = null, impersonatedActor: Actor? = null, targetLegalIdentity: CordaX500Name? = null )</ID>
<ID>LongParameterList:RPCDriver.kt$( isDebug: Boolean = false, driverDirectory: Path = Paths.get("build") / "rpc-driver" / getTimestampAsDirectoryName(), portAllocation: PortAllocation = globalPortAllocation, debugPortAllocation: PortAllocation = globalDebugPortAllocation, systemProperties: Map&lt;String, String&gt; = emptyMap(), useTestClock: Boolean = false, startNodesInProcess: Boolean = false, waitForNodesToFinish: Boolean = false, extraCordappPackagesToScan: List&lt;String&gt; = emptyList(), notarySpecs: List&lt;NotarySpec&gt; = emptyList(), externalTrace: Trace? = null, @Suppress("DEPRECATION") jmxPolicy: JmxPolicy = JmxPolicy(), networkParameters: NetworkParameters = testNetworkParameters(), notaryCustomOverrides: Map&lt;String, Any?&gt; = emptyMap(), inMemoryDB: Boolean = true, cordappsForAllNodes: Collection&lt;TestCordappInternal&gt;? = null, dsl: RPCDriverDSL.() -&gt; A )</ID>
<ID>LongParameterList:RPCDriver.kt$RPCDriverDSL$( rpcUser: User = rpcTestUser, nodeLegalName: CordaX500Name = fakeNodeLegalName, configuration: RPCServerConfiguration = RPCServerConfiguration.DEFAULT, listOps: List&lt;I&gt;, brokerHandle: RpcBrokerHandle, queueDrainTimeout: Duration = 5.seconds )</ID>
<ID>LongParameterList:RPCDriver.kt$RPCDriverDSL$( rpcUser: User = rpcTestUser, nodeLegalName: CordaX500Name = fakeNodeLegalName, configuration: RPCServerConfiguration = RPCServerConfiguration.DEFAULT, ops: I, brokerHandle: RpcBrokerHandle, queueDrainTimeout: Duration = 5.seconds )</ID>
<ID>LongParameterList:RPCDriver.kt$RPCDriverDSL$( rpcUser: User = rpcTestUser, nodeLegalName: CordaX500Name = fakeNodeLegalName, maxFileSize: Int = MAX_MESSAGE_SIZE, maxBufferedBytesPerClient: Long = 10L * MAX_MESSAGE_SIZE, configuration: RPCServerConfiguration = RPCServerConfiguration.DEFAULT, ops: I, queueDrainTimeout: Duration = 5.seconds )</ID>
<ID>LongParameterList:RPCDriver.kt$RPCDriverDSL$( serverName: String = "driver-rpc-server-${random63BitValue()}", rpcUser: User = rpcTestUser, nodeLegalName: CordaX500Name = fakeNodeLegalName, maxFileSize: Int = MAX_MESSAGE_SIZE, maxBufferedBytesPerClient: Long = 5L * MAX_MESSAGE_SIZE, configuration: RPCServerConfiguration = RPCServerConfiguration.DEFAULT, customPort: NetworkHostAndPort? = null, listOps: List&lt;I&gt; )</ID>
<ID>LongParameterList:RPCDriver.kt$RPCDriverDSL$( serverName: String = "driver-rpc-server-${random63BitValue()}", rpcUser: User = rpcTestUser, nodeLegalName: CordaX500Name = fakeNodeLegalName, maxFileSize: Int = MAX_MESSAGE_SIZE, maxBufferedBytesPerClient: Long = 5L * MAX_MESSAGE_SIZE, configuration: RPCServerConfiguration = RPCServerConfiguration.DEFAULT, customPort: NetworkHostAndPort? = null, ops: I )</ID>
<ID>LongParameterList:RpcBrokerConfiguration.kt$RpcBrokerConfiguration$(name: String, send: Boolean = false, consume: Boolean = false, createDurableQueue: Boolean = false, deleteDurableQueue: Boolean = false, createNonDurableQueue: Boolean = false, deleteNonDurableQueue: Boolean = false, manage: Boolean = false, browse: Boolean = false)</ID>
<ID>LongParameterList:SerializationEnvironment.kt$SerializationEnvironment.Companion$( serializationFactory: SerializationFactory, p2pContext: SerializationContext, rpcServerContext: SerializationContext? = null, rpcClientContext: SerializationContext? = null, storageContext: SerializationContext? = null, checkpointContext: CheckpointSerializationContext? = null, checkpointSerializer: CheckpointSerializer? = null )</ID>
@ -2135,6 +2137,10 @@
<ID>MagicNumber:RPCHighThroughputObservableTests.kt$RPCHighThroughputObservableTests$3</ID>
<ID>MagicNumber:RPCHighThroughputObservableTests.kt$RPCHighThroughputObservableTests$4</ID>
<ID>MagicNumber:RPCHighThroughputObservableTests.kt$RPCHighThroughputObservableTests.TestOpsImpl$1000</ID>
<ID>MagicNumber:RPCMultipleInterfacesTests.kt$RPCMultipleInterfacesTests.IntRPCOpsImpl$1000</ID>
<ID>MagicNumber:RPCMultipleInterfacesTests.kt$RPCMultipleInterfacesTests.MyCordaRpcOpsImpl$1000</ID>
<ID>MagicNumber:RPCMultipleInterfacesTests.kt$RPCMultipleInterfacesTests.StringRPCOpsImpl$1000</ID>
<ID>MagicNumber:RPCMultipleInterfacesTests.kt$RPCMultipleInterfacesTests.StringRPCOpsImpl$8</ID>
<ID>MagicNumber:RPCPerformanceTests.kt$RPCPerformanceTests$100</ID>
<ID>MagicNumber:RPCPerformanceTests.kt$RPCPerformanceTests$1000</ID>
<ID>MagicNumber:RPCPerformanceTests.kt$RPCPerformanceTests$1000.0</ID>
@ -3609,11 +3615,8 @@
<ID>MaxLineLength:HibernateQueryCriteriaParser.kt$HibernateQueryCriteriaParser$val existingTypes = (commonPredicates[predicateID]!!.expressions[0] as InPredicate&lt;*&gt;).values.map { (it as LiteralExpression).literal }.toSet()</ID>
<ID>MaxLineLength:HibernateQueryCriteriaParser.kt$HibernateQueryCriteriaParser$val externalIdJoin = criteriaBuilder.equal(vaultStates.get&lt;VaultSchemaV1.VaultStates&gt;("stateRef"), entityRoot.get&lt;VaultSchemaV1.StateToExternalId&gt;("compositeKey").get&lt;PersistentStateRef&gt;("stateRef"))</ID>
<ID>MaxLineLength:HibernateQueryCriteriaParser.kt$HibernateQueryCriteriaParser$val joinPredicate = criteriaBuilder.equal(vaultStates.get&lt;PersistentStateRef&gt;("stateRef"), entityRoot.get&lt;PersistentStateRef&gt;("stateRef"))</ID>
<ID>MaxLineLength:HibernateQueryCriteriaParser.kt$HibernateQueryCriteriaParser$val joinPredicate = criteriaBuilder.equal(vaultStates.get&lt;PersistentStateRef&gt;("stateRef"), vaultFungibleStates.get&lt;PersistentStateRef&gt;("stateRef"))</ID>
<ID>MaxLineLength:HibernateQueryCriteriaParser.kt$HibernateQueryCriteriaParser$val joinPredicate = criteriaBuilder.equal(vaultStates.get&lt;PersistentStateRef&gt;("stateRef"), vaultLinearStates.get&lt;PersistentStateRef&gt;("stateRef"))</ID>
<ID>MaxLineLength:HibernateQueryCriteriaParser.kt$HibernateQueryCriteriaParser$val predicateConstraintData = criteriaBuilder.equal(vaultStates.get&lt;Vault.ConstraintInfo&gt;(VaultSchemaV1.VaultStates::constraintData.name), constraint.data())</ID>
<ID>MaxLineLength:HibernateQueryCriteriaParser.kt$HibernateQueryCriteriaParser$val predicateConstraintType = criteriaBuilder.equal(vaultStates.get&lt;Vault.ConstraintInfo&gt;(VaultSchemaV1.VaultStates::constraintType.name), constraint.type())</ID>
<ID>MaxLineLength:HibernateQueryCriteriaParser.kt$HibernateQueryCriteriaParser$val statePartyJoin = criteriaBuilder.equal(vaultStates.get&lt;VaultSchemaV1.VaultStates&gt;("stateRef"), entityRoot.get&lt;VaultSchemaV1.PersistentParty&gt;("compositeKey").get&lt;PersistentStateRef&gt;("stateRef"))</ID>
<ID>MaxLineLength:HibernateQueryCriteriaParser.kt$HibernateQueryCriteriaParser$val vaultStates: Root&lt;VaultSchemaV1.VaultStates&gt;</ID>
<ID>MaxLineLength:HibernateQueryCriteriaParser.kt$HibernateQueryCriteriaParser${ @Suppress("UNCHECKED_CAST") column as Path&lt;Long?&gt;? val aggregateExpression = when (columnPredicate.type) { AggregateFunctionType.SUM -&gt; criteriaBuilder.sum(column) AggregateFunctionType.AVG -&gt; criteriaBuilder.avg(column) AggregateFunctionType.COUNT -&gt; criteriaBuilder.count(column) AggregateFunctionType.MAX -&gt; criteriaBuilder.max(column) AggregateFunctionType.MIN -&gt; criteriaBuilder.min(column) } //TODO investigate possibility to avoid producing redundant joins in SQL for multiple aggregate functions against the same table aggregateExpressions.add(aggregateExpression) // Some databases may not support aggregate expression in 'group by' clause e.g. 'group by sum(col)', // Hibernate Criteria Builder can't produce alias 'group by col_alias', and the only solution is to use a positional parameter 'group by 1' val orderByColumnPosition = aggregateExpressions.size var shiftLeft = 0 // add optional group by clauses expression.groupByColumns?.let { columns -&gt; val groupByExpressions = columns.map { _column -&gt; val path = root.get&lt;Any?&gt;(getColumnName(_column)) val columnNumberBeforeRemoval = aggregateExpressions.size if (path is SingularAttributePath) //remove the same columns from different joins to match the single column in 'group by' only (from the last join) aggregateExpressions.removeAll { elem -&gt; if (elem is SingularAttributePath) elem.attribute.javaMember == path.attribute.javaMember else false } shiftLeft += columnNumberBeforeRemoval - aggregateExpressions.size //record how many times a duplicated column was removed (from the previous 'parseAggregateFunction' run) aggregateExpressions.add(path) path } criteriaQuery.groupBy(groupByExpressions) } // optionally order by this aggregate function expression.orderBy?.let { val orderCriteria = when (expression.orderBy!!) { // when adding column position of 'group by' shift in case columns were removed Sort.Direction.ASC -&gt; criteriaBuilder.asc(criteriaBuilder.literal&lt;Int&gt;(orderByColumnPosition - shiftLeft)) Sort.Direction.DESC -&gt; criteriaBuilder.desc(criteriaBuilder.literal&lt;Int&gt;(orderByColumnPosition - shiftLeft)) } criteriaQuery.orderBy(orderCriteria) } return aggregateExpression }</ID>
<ID>MaxLineLength:HttpApi.kt$HttpApi.Companion$fun fromHostAndPort(hostAndPort: NetworkHostAndPort, base: String, protocol: String = "http", mapper: ObjectMapper = defaultMapper): HttpApi</ID>
@ -4513,7 +4516,6 @@
<ID>MaxLineLength:RPCClientProxyHandler.kt$RPCClientProxyHandler$return cacheFactory.buildNamed(Caffeine.newBuilder().weakValues().removalListener(onObservableRemove).executor(SameThreadExecutor.getExecutor()), "RpcClientProxyHandler_rpcObservable")</ID>
<ID>MaxLineLength:RPCClientProxyHandler.kt$RPCClientProxyHandler$throw UnsupportedOperationException("Method $calledMethod was added in RPC protocol version $sinceVersion but the server is running $serverProtocolVersion")</ID>
<ID>MaxLineLength:RPCDriver.kt$RPCDriverDSL$val artemisConfig = createRpcServerArtemisConfig(maxFileSize, maxBufferedBytesPerClient, driverDSL.driverDirectory / serverName, hostAndPort)</ID>
<ID>MaxLineLength:RPCDriver.kt$RPCDriverDSL$val rpcSecurityManager = RPCSecurityManagerImpl.fromUserList(users = listOf(InternalUser(rpcUser.username, rpcUser.password, rpcUser.permissions)), id = AuthServiceId("TEST_SECURITY_MANAGER"))</ID>
<ID>MaxLineLength:RPCDriver.kt$RPCDriverDSL.Companion$fun createRpcServerArtemisConfig(maxFileSize: Int, maxBufferedBytesPerClient: Long, baseDirectory: Path, hostAndPort: NetworkHostAndPort): Configuration</ID>
<ID>MaxLineLength:RPCDriver.kt$RandomRpcUser.Companion$private inline fun &lt;reified T&gt; HashMap&lt;Class&lt;*&gt;, Generator&lt;*&gt;&gt;.add(generator: Generator&lt;T&gt;)</ID>
<ID>MaxLineLength:RPCDriver.kt$RandomRpcUser.Companion$val handle = RPCClient&lt;RPCOps&gt;(hostAndPort, null, serializationContext = AMQP_RPC_CLIENT_CONTEXT).start(rpcClass, username, password)</ID>
@ -4523,6 +4525,9 @@
<ID>MaxLineLength:RPCOpsWithContext.kt$fun makeRPCOps(getCordaRPCOps: (username: String, credential: String) -&gt; InternalCordaRPCOps, username: String, credential: String): InternalCordaRPCOps</ID>
<ID>MaxLineLength:RPCOpsWithContext.kt$return Proxy.newProxyInstance(InternalCordaRPCOps::class.java.classLoader, arrayOf(InternalCordaRPCOps::class.java)) { _, method, args -&gt; try { method.invoke(cordaRPCOps, *(args ?: arrayOf())) } catch (e: InvocationTargetException) { // Unpack exception. throw e.targetException } } as InternalCordaRPCOps</ID>
<ID>MaxLineLength:RPCSecurityManagerWithAdditionalUser.kt$RPCSecurityManagerWithAdditionalUser : RPCSecurityManager</ID>
<ID>MaxLineLength:RPCServer.kt$RPCServer</ID>
<ID>MaxLineLength:RPCServer.kt$RPCServer$( ops: RPCOps, rpcServerUsername: String, rpcServerPassword: String, serverLocator: ServerLocator, securityManager: RPCSecurityManager, nodeLegalName: CordaX500Name, rpcConfiguration: RPCServerConfiguration, cacheFactory: NamedCacheFactory )</ID>
<ID>MaxLineLength:RPCServer.kt$RPCServer$/** * The method name -&gt; InvocationTarget used for servicing the actual call. * NB: The key in this map can either be: * - FQN of the method including interface name for all the interfaces except `CordaRPCOps`; * - For `CordaRPCOps` interface this will be just plain method name. This is done to maintain wire compatibility with previous versions. */ private val methodTable: Map&lt;String, InvocationTarget&gt;</ID>
<ID>MaxLineLength:RPCServer.kt$RPCServer$consumerSession = sessionFactory!!.createSession(rpcServerUsername, rpcServerPassword, false, true, true, false, DEFAULT_ACK_BATCH_SIZE)</ID>
<ID>MaxLineLength:RPCServer.kt$RPCServer$private</ID>
<ID>MaxLineLength:RPCServer.kt$RPCServer$producerSession = sessionFactory!!.createSession(rpcServerUsername, rpcServerPassword, false, true, true, false, DEFAULT_ACK_BATCH_SIZE)</ID>
@ -5432,7 +5437,7 @@
<ID>SpreadOperator:RPCDriver.kt$RandomRpcUser.Companion$(handle.proxy, *arguments.toTypedArray())</ID>
<ID>SpreadOperator:RPCOpsWithContext.kt$(cordaRPCOps, *(args ?: arrayOf()))</ID>
<ID>SpreadOperator:RPCSecurityManagerTest.kt$RPCSecurityManagerTest$(request.first(), *args)</ID>
<ID>SpreadOperator:RPCServer.kt$RPCServer$(ops, *arguments.toTypedArray())</ID>
<ID>SpreadOperator:RPCServer.kt$RPCServer$(invocationTarget.instance, *arguments.toTypedArray())</ID>
<ID>SpreadOperator:ReactiveArtemisConsumer.kt$ReactiveArtemisConsumer.Companion$(queueName, *queueNames)</ID>
<ID>SpreadOperator:ReconnectingCordaRPCOps.kt$ReconnectingCordaRPCOps.ErrorInterceptingHandler$(reconnectingRPCConnection.proxy, *(args ?: emptyArray()))</ID>
<ID>SpreadOperator:ServiceHub.kt$ServiceHub$(first, *remaining)</ID>
@ -5480,7 +5485,7 @@
<ID>ThrowsCount:NonValidatingNotaryFlow.kt$NonValidatingNotaryFlow$ private fun checkNotaryWhitelisted(notary: Party, attachedParameterHash: SecureHash?)</ID>
<ID>ThrowsCount:PropertyDescriptor.kt$PropertyDescriptor$ fun validate()</ID>
<ID>ThrowsCount:RPCApi.kt$RPCApi.ServerToClient.Companion$fun fromClientMessage(context: SerializationContext, message: ClientMessage): ServerToClient</ID>
<ID>ThrowsCount:RPCServer.kt$RPCServer$private fun invokeRpc(context: RpcAuthContext, methodName: String, arguments: List&lt;Any?&gt;): Try&lt;Any&gt;</ID>
<ID>ThrowsCount:RPCServer.kt$RPCServer$private fun invokeRpc(context: RpcAuthContext, inMethodName: String, arguments: List&lt;Any?&gt;): Try&lt;Any&gt;</ID>
<ID>ThrowsCount:SchemaMigration.kt$SchemaMigration$private fun doRunMigration(run: Boolean, check: Boolean, existingCheckpoints: Boolean? = null)</ID>
<ID>ThrowsCount:ServicesForResolutionImpl.kt$ServicesForResolutionImpl$// We may need to recursively chase transactions if there are notary changes. fun inner(stateRef: StateRef, forContractClassName: String?): Attachment</ID>
<ID>ThrowsCount:SignedNodeInfo.kt$SignedNodeInfo$// TODO Add root cert param (or TrustAnchor) to make sure all the identities belong to the same root fun verified(): NodeInfo</ID>
@ -6659,6 +6664,7 @@
<ID>WildcardImport:QueryCriteriaUtils.kt$import net.corda.core.node.services.vault.LikenessOperator.*</ID>
<ID>WildcardImport:RPCClientProxyHandler.kt$import net.corda.core.internal.*</ID>
<ID>WildcardImport:RPCClientProxyHandler.kt$import org.apache.activemq.artemis.api.core.client.*</ID>
<ID>WildcardImport:RPCMultipleInterfacesTests.kt$import org.junit.Assert.*</ID>
<ID>WildcardImport:RPCSecurityManagerImpl.kt$import org.apache.shiro.authc.*</ID>
<ID>WildcardImport:RPCServer.kt$import net.corda.core.utilities.*</ID>
<ID>WildcardImport:RPCServer.kt$import org.apache.activemq.artemis.api.core.client.*</ID>

View File

@ -53,11 +53,11 @@ For example: with cordapp-example (IOU app) the following commands would be run:
./gradlew deployNodes
./kotlin-source/build/nodes/runnodes
Then start the Corda webserver
Then start the Corda test webserver
.. sourcecode:: shell
find ~/dev/cordapp-example/kotlin-source/ -name corda-webserver.jar -execdir sh -c 'java -jar {} &' \;
find ~/dev/cordapp-example/kotlin-source/ -name corda-testserver.jar -execdir sh -c 'java -jar {} &' \;
You can now interact with your running CorDapp. See the instructions `here <https://docs.corda.net/tutorial-cordapp.html#via-http>`__.

View File

@ -103,7 +103,7 @@ Connect to one of your Corda nodes (make sure this is not the Notary node) using
Build the yo cordapp sample which you can find here: |os_samples_branch|/yo-cordapp and install it in the cordapp directory.
Now restart Corda and the Corda webserver using the following commands or restart your Corda VM from the Azure portal:
Now restart Corda and the Corda test webserver using the following commands or restart your Corda VM from the Azure portal:
.. sourcecode:: shell

View File

@ -6,6 +6,8 @@ release, see :doc:`app-upgrade-notes`.
Unreleased
----------
* Moved and renamed the testing web server to the ``testing`` subproject. Also renamed the published artifact to ``corda-testserver.jar``.
* Support for Java 11 (compatibility mode). Please read https://github.com/corda/corda/pull/5356.
* Updating FinalityFlow with functionality to indicate the appropriate StatesToRecord. This allows the initiating party to record states

View File

@ -16,7 +16,7 @@ This class allows you to connect to your node via a message queue protocol and p
interacting with the node. You make calls on a JVM object as normal, and the marshalling back-and-forth is handled for
you.
.. warning:: The built-in Corda webserver is deprecated and unsuitable for production use. If you want to interact with
.. warning:: The built-in Corda test webserver is deprecated and unsuitable for production use. If you want to interact with
your node via HTTP, you will need to stand up your own webserver that connects to your node using the
`CordaRPCClient`_ class. You can find an example of how to do this using the popular Spring Boot server
`here <https://github.com/corda/spring-webserver>`_.

View File

@ -126,8 +126,6 @@ Here is an overview of the various Corda dependencies:
* ``corda-tools-explorer`` - The Node Explorer tool. Do not depend on
* ``corda-tools-network-bootstrapper`` - The Network Builder tool. Useful in build scripts
* ``corda-tools-shell-cli`` - The Shell CLI tool. Useful in build scripts
* ``corda-webserver-impl`` - The Corda webserver fat JAR. Deprecated. Usually only used by build scripts
* ``corda-websever`` - The Corda webserver library. Deprecated. Use a standard webserver library such as Spring instead
Dependencies on other CorDapps
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -308,7 +306,7 @@ Below is a sample CorDapp Gradle dependencies block. When building your own CorD
cordaCompile "net.corda:corda-node-api:$corda_release_version"
cordaCompile "net.corda:corda-webserver-impl:$corda_release_version"
cordaRuntime "net.corda:corda:$corda_release_version"
cordaRuntime "net.corda:corda-webserver:$corda_release_version"
cordaRuntime "net.corda:corda-testserver:$corda_release_version"
testCompile "net.corda:corda-test-utils:$corda_release_version"
// Corda Plugins: dependent flows and services

View File

@ -126,7 +126,7 @@ current working directory of the JVM):
corda/
corda.jar
corda-webserver.jar
corda-testserver.jar
explorer/
node-explorer.jar
cordapps/

View File

@ -9,7 +9,7 @@ Deploying a node to a server
Linux: Installing and running Corda as a system service
-------------------------------------------------------
We recommend creating system services to run a node and the optional webserver. This provides logging and service
We recommend creating system services to run a node and the optional test webserver. This provides logging and service
handling, and ensures the Corda service is run at boot.
**Prerequisites**:
@ -123,7 +123,7 @@ handling, and ensures the Corda service is run at boot.
* ``sudo chown root:root /etc/init/corda.conf``
* ``sudo chmod 644 /etc/init/corda.conf``
.. note:: The Corda webserver provides a simple interface for interacting with your installed CorDapps in a browser.
.. note:: The Corda test webserver provides a simple interface for interacting with your installed CorDapps in a browser.
Running the webserver is optional.
10. **SystemD**: Create a ``corda-webserver.service`` file based on the example below and save it in the ``/etc/systemd/system/``

View File

@ -53,10 +53,10 @@ human readable and machine readable.
In addition, in-house Corda networks at R3 use the following tools:
* Standard [DataDog](https://docs.datadoghq.com/guides/overview/) probes are currently used to provide e-mail based
alerting for running Corda nodes. [Telegraf](https://github.com/influxdata/telegraf) is used in conjunction with a
* Standard [DataDog](https://docs.datadoghq.com/guides/overview/) probes are currently used to provide e-mail based
alerting for running Corda nodes. [Telegraf](https://github.com/influxdata/telegraf) is used in conjunction with a
[Jolokia agent](https://jolokia.org/agent.html) as a collector to parse emitted metric data and push these to DataDog.
* Investigation is underway to evaluate [ELK](https://logz.io/learn/complete-guide-elk-stack/) as a mechanism for parsing,
* Investigation is underway to evaluate [ELK](https://logz.io/learn/complete-guide-elk-stack/) as a mechanism for parsing,
indexing, storing, searching, and visualising log file data.
## Scope
@ -121,15 +121,15 @@ design, either directly or through an integrated enterprise-wide systems managem
- Validate liveness and correctness of Corda nodes and deployed CorDapps, and the physical machine or VM they are hosted on.
* Use logging to troubleshoot operational failures (in conjunction with other supporting failure information: eg. GC logs, stack traces)
* Use reported metrics to fine-tune and tweak operational systems parameters (including dynamic setting of logging
* Use reported metrics to fine-tune and tweak operational systems parameters (including dynamic setting of logging
modules and severity levels to enable detailed logging).
## Design Decisions
The following design decisions are to be confirmed:
1. JMX for metric eventing and SLF4J for logging
Both above are widely adopted mechanisms that enable pluggability and seamless interoperability with other 3rd party
1. JMX for metric eventing and SLF4J for logging
Both above are widely adopted mechanisms that enable pluggability and seamless interoperability with other 3rd party
enterprise-wide system management solutions.
2. Continue or discontinue usage of Jolokia? (TBC - most likely yes, subject to read-only security lock-down)
3. Separation of Corda Node and CorDapp log outputs (TBC)
@ -138,54 +138,54 @@ The following design decisions are to be confirmed:
There are a number of activities and parts to the solution proposal:
1. Extend JMX metric reporting through the Corda Monitoring Service and associated jolokia conversion to REST/JSON)
coverage (see implementation details) to include all Corda services (vault, key management, transaction storage,
1. Extend JMX metric reporting through the Corda Monitoring Service and associated jolokia conversion to REST/JSON)
coverage (see implementation details) to include all Corda services (vault, key management, transaction storage,
network map, attachment storage, identity, cordapp provision) & sub-sytems components (state machine)
2. Review and extend Corda log4j2 coverage (see implementation details) to ensure
- consistent use of severities according to situation
- consistent coverage across all modules and libraries
- consistent output format with all relevant contextual information (node identity, user/execution identity, flow
- consistent output format with all relevant contextual information (node identity, user/execution identity, flow
session identity, version information)
- separation of Corda Node and CorDapp log outputs (TBC)
For consistent interleaving reasons, it may be desirable to continue using combined log output.
Publication of a *code style guide* to define when to use different severity levels.
3. Implement a CorDapp to perform sanity checking of flow framework, fundamental corda services (vault, identity), and
3. Implement a CorDapp to perform sanity checking of flow framework, fundamental corda services (vault, identity), and
dependent middleware infrastructure (message broker, database).
4. Revisit and enhance as necessary the [Audit service API]( https://github.com/corda/corda/pull/620 ), and provide a
4. Revisit and enhance as necessary the [Audit service API]( https://github.com/corda/corda/pull/620 ), and provide a
persistent backed implementation, to include:
- specification of Business Event Categories (eg. User authentication and authorisation, Flow-based triggering, Corda
- specification of Business Event Categories (eg. User authentication and authorisation, Flow-based triggering, Corda
Service invocations, Oracle invocations, Flow-based send/receive calls, RPC invocations)
- auto-enabled with Progress Tracker as Business Event generator
- RDBMS backed persistent store (independent of Corda database), with adequate security controls (authenticated access
- RDBMS backed persistent store (independent of Corda database), with adequate security controls (authenticated access
and read-only permissioning). Captured information should be consistent with standard logging, and it may be desirable
to define auditable loggers within log4j2 to automatically redirect certain types of log events to the audit service.
to define auditable loggers within log4j2 to automatically redirect certain types of log events to the audit service.
5. Ensure 3rd party middleware drivers (JDBC for database, MQ for messaging) and the JVM are correctly configured to export
JMX metrics. Ensure the [JVM Hotspot VM command-line parameters](https://docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/clopts001.html)
are tuned correctly to enable detailed troubleshooting upon failure. Many of these metrics are already automatically
exposed to 3rd party profiling tools such as Yourkit.
5. Ensure 3rd party middleware drivers (JDBC for database, MQ for messaging) and the JVM are correctly configured to export
JMX metrics. Ensure the [JVM Hotspot VM command-line parameters](https://docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/clopts001.html)
are tuned correctly to enable detailed troubleshooting upon failure. Many of these metrics are already automatically
exposed to 3rd party profiling tools such as Yourkit.
Apache Artemis has a comprehensive [management API](https://activemq.apache.org/artemis/docs/latest/management.html)
that allows a user to modify a server configuration, create new resources (e.g. addresses and queues), inspect these
resources (e.g. how many messages are currently held in a queue) and interact with it (e.g. to remove messages from a
queue), and exposes key metrics using JMX (using role-based authentication using Artemis's JAAS plug-in support to
Apache Artemis has a comprehensive [management API](https://activemq.apache.org/artemis/docs/latest/management.html)
that allows a user to modify a server configuration, create new resources (e.g. addresses and queues), inspect these
resources (e.g. how many messages are currently held in a queue) and interact with it (e.g. to remove messages from a
queue), and exposes key metrics using JMX (using role-based authentication using Artemis's JAAS plug-in support to
ensure Artemis cannot be controlled via JMX)..
### Restrictions
As of Corda M11, Java serialisation in the Corda node has been restricted, meaning MBeans access via the JMX port will no longer work.
Usage of Jolokia requires bundling an associated *jolokia-agent-war* file on the classpath, and associated configuration
to export JMX monitoring statistics and data over the Jolokia REST/JSON interface. An associated *jolokia-access.xml*
Usage of Jolokia requires bundling an associated *jolokia-agent-war* file on the classpath, and associated configuration
to export JMX monitoring statistics and data over the Jolokia REST/JSON interface. An associated *jolokia-access.xml*
configuration file defines role based permissioning to HTTP operations.
## Complementary solutions
## Complementary solutions
A number of 3rd party libraries and frameworks have been proposed which solve different parts of the end to end
solution, albeit with most focusing on the Agent Collector (eg. collect metrics from systems then output them to some
@ -207,7 +207,7 @@ include:
Most of the above solutions are not within the scope of this design proposal, but should be capable of ingesting the outputs (logging and metrics) defined by this design.
## Technical design
## Technical design
In general, the requirements outlined in this design are cross-cutting concerns which affect the Corda codebase holistically, both for logging and capture/export of JMX metrics.
@ -238,7 +238,7 @@ In general, the requirements outlined in this design are cross-cutting concerns
#### Health Checker
The Health checker is a CorDapp which verifies the health and liveliness of the Corda node it is deployed and running within by performing the following activities:
The Health checker is a CorDapp which verifies the health and liveliness of the Corda node it is deployed and running within by performing the following activities:
1. Corda network and middleware infrastructure connectivity checking:
@ -263,7 +263,7 @@ The Health checker is a CorDapp which verifies the health and liveliness of the
4. RPC triggering
Autotriggering of above flow using RPC to exercise the following:
- messaging subsystem verification (RPC queuing)
- messaging subsystem verification (RPC queuing)
- authenticaton and permissions checking (against underlying configuration)
@ -272,12 +272,12 @@ The Health checker may be deployed as part of a Corda distribution and automatic
Please note that the Health checker application is not responsible for determining the healthiness of a Corda Network. This is the responsibility of the network operator, and may include verification checks such as:
- correct functioning of Network Map Service (registration, discovery)
- correct functioning of configured Notary
- correct functioning of configured Notary
- remote messaging sub-sytem (including bridge creation)
#### Metrics augmentation within Corda Subsystems and Components
*Codahale* provides the following types of reportable metrics:
*Codahale* provides the following types of reportable metrics:
- Gauge: is an instantaneous measurement of a value.
- Counter: is a gauge for a numeric value (specifically of type `AtomicLong`) which can be incremented or decremented.
@ -288,7 +288,7 @@ Please note that the Health checker application is not responsible for determini
See Appendix B for summary of current JMX Metrics exported by the Corda codebase.
The following table identifies additional metrics to report for a Corda node:
The following table identifies additional metrics to report for a Corda node:
| Component / Subsystem | Proposed Metric(s) |
| ---------------------------------------- | ---------------------------------------- |
@ -333,7 +333,7 @@ A *logging style guide* will be published to answer questions such as what sever
- A connection to a remote peer is unexpectedly terminated.
- A database connection timed out but was successfully re-established.
- A message was sent to a peer.
- A message was sent to a peer.
It is also important that we capture the correct amount of contextual information to enable rapid identification and resolution of issues using log file output. Specifically, within Corda we should include the following information in logged messages:
@ -342,7 +342,7 @@ It is also important that we capture the correct amount of contextual informatio
- Flow id (runId, also referred to as `StateMachineRunId`), if logging within a flow
- Other contextual Flow information (eg. counterparty), if logging within a flow
- `FlowStackSnapshot` information for catastrophic flow failures.
Note: this information is not currently supposed to be used in production (???).
Note: this information is not currently supposed to be used in production (???).
- Session id information for RPC calls
- CorDapp name, if logging from within a CorDapp
@ -406,10 +406,6 @@ The following metrics are exposed directly by a Corda Node at run-time:
| Module | Metric | Desccription |
| ------------------------ | ---------------------------- | ---------------------------------------- |
| Attachment Service | Attachments | Counts number of attachments persisted in database. |
| Verification Service | VerificationsInFlight | Gauge of number of in flight verifications handled by the out of process verification service. |
| Verification Service | Verification.Duration | Timer |
| Verification Service | Verification.Success | Count |
| Verification Service | Verification.Failure | Count |
| RAFT Uniqueness Provider | RaftCluster.ThisServerStatus | Gauge |
| RAFT Uniqueness Provider | RaftCluster.MembersCount | Count |
| RAFT Uniqueness Provider | RaftCluster.Members | Gauge, containing a list of members (by server address) |
@ -417,7 +413,6 @@ The following metrics are exposed directly by a Corda Node at run-time:
| State Machine Manager | Flows.CheckpointingRate | Meter |
| State Machine Manager | Flows.Started | Count |
| State Machine Manager | Flows.Finished | Count |
| Flow State Machine | FlowDuration | Timer |
Additionally, JMX metrics are also generated within the Corda *node-driver* performance testing utilities. Specifically, the `startPublishingFixedRateInjector` defines and exposes `QueueSize` and `WorkDuration` metrics.
@ -536,4 +531,3 @@ The following table summarised the types of metrics associated with Message Queu
| messageCountDelta | *overall* number of messages added/removed from the queue *since the last message counter update*. Positive value indicated more messages were added, negative vice versa. |
| lastAddTimestamp | timestamp of the last time a message was added to the queue |
| updateTimestamp | timestamp of the last message counter update |

View File

@ -40,7 +40,7 @@ dependencies {
compile project(':core')
compile project(':client:jfx')
compile project(':node-driver')
compile project(':webserver')
compile project(':testing:testserver')
testCompile project(':test-utils')
@ -51,7 +51,7 @@ dependencies {
}
cordaRuntime project(path: ":node:capsule", configuration: 'runtimeArtifacts')
cordaRuntime project(path: ":webserver:webcapsule", configuration: 'runtimeArtifacts')
cordaRuntime project(path: ":testing:testserver:testcapsule:", configuration: 'runtimeArtifacts')
// CorDapps: dependent flows and services
compile project(':finance:contracts')

View File

@ -55,7 +55,7 @@ Since the CorDapp models a car dealership network, a state must be created to re
It's important to specify what classes are required in each state, contract, and flow. This process must be repeated with each file as it is created.
5. Update ``@BelongsToContract(TemplateContract:class)`` to specify ``CarContract::class``.
5. Update ``@BelongsToContract(TemplateContract::class)`` to specify ``CarContract::class``.
6. Add the following fields to the state:
* ``owningBank`` of type ``Party``

View File

@ -75,6 +75,8 @@ object RPCApi {
const val DEDUPLICATION_SEQUENCE_NUMBER_FIELD_NAME = "deduplication-sequence-number"
const val CLASS_METHOD_DIVIDER = "#"
val RPC_CLIENT_BINDING_REMOVAL_FILTER_EXPRESSION =
"${ManagementHelper.HDR_NOTIFICATION_TYPE} = '${CoreNotificationType.BINDING_REMOVED.name}' AND " +
"${ManagementHelper.HDR_ROUTING_NAME} LIKE '$RPC_CLIENT_QUEUE_NAME_PREFIX.%'"
@ -103,7 +105,7 @@ object RPCApi {
* Request to a server to trigger the specified method with the provided arguments.
*
* @param clientAddress return address to contact the client at.
* @param id a unique ID for the request, which the server will use to identify its response with.
* @param replyId a unique ID for the request, which the server will use to identify its response with.
* @param methodName name of the method (procedure) to be called.
* @param serialisedArguments Serialised arguments to pass to the method, if any.
*/

View File

@ -375,7 +375,7 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
signedNodeInfo,
netParams,
keyManagementService,
configuration.networkParameterAcceptanceSettings)
configuration.networkParameterAcceptanceSettings!!)
try {
startMessagingService(rpcOps, nodeInfo, myNotaryIdentity, netParams)
} catch (e: Exception) {

View File

@ -81,7 +81,7 @@ interface NodeConfiguration {
val cordappSignerKeyFingerprintBlacklist: List<String>
val networkParameterAcceptanceSettings: NetworkParameterAcceptanceSettings
val networkParameterAcceptanceSettings: NetworkParameterAcceptanceSettings?
val blacklistedAttachmentSigningKeys: List<String>

View File

@ -75,7 +75,8 @@ data class NodeConfigurationImpl(
override val jmxReporterType: JmxReporterType? = Defaults.jmxReporterType,
override val flowOverrides: FlowOverrideConfig?,
override val cordappSignerKeyFingerprintBlacklist: List<String> = Defaults.cordappSignerKeyFingerprintBlacklist,
override val networkParameterAcceptanceSettings: NetworkParameterAcceptanceSettings = Defaults.networkParameterAcceptanceSettings,
override val networkParameterAcceptanceSettings: NetworkParameterAcceptanceSettings? =
Defaults.networkParameterAcceptanceSettings,
override val blacklistedAttachmentSigningKeys: List<String> = Defaults.blacklistedAttachmentSigningKeys
) : NodeConfiguration {
internal object Defaults {

View File

@ -21,6 +21,7 @@ import net.corda.node.services.config.DevModeOptions
import net.corda.node.services.config.FlowOverride
import net.corda.node.services.config.FlowOverrideConfig
import net.corda.node.services.config.FlowTimeoutConfiguration
import net.corda.node.services.config.NetworkParameterAcceptanceSettings
import net.corda.node.services.config.NetworkServicesConfig
import net.corda.node.services.config.NodeH2Settings
import net.corda.node.services.config.NodeRpcSettings
@ -143,6 +144,18 @@ internal object NetworkServicesConfigSpec : Configuration.Specification<NetworkS
}
}
internal object NetworkParameterAcceptanceSettingsSpec :
Configuration.Specification<NetworkParameterAcceptanceSettings>("NetworkParameterAcceptanceSettings") {
private val autoAcceptEnabled by boolean().optional().withDefaultValue(true)
private val excludedAutoAcceptableParameters by string().listOrEmpty()
override fun parseValid(configuration: Config): Valid<NetworkParameterAcceptanceSettings> {
return valid(NetworkParameterAcceptanceSettings(configuration[autoAcceptEnabled],
configuration[excludedAutoAcceptableParameters].toSet())
)
}
}
@Suppress("DEPRECATION")
internal object CertChainPolicyConfigSpec : Configuration.Specification<CertChainPolicyConfig>("CertChainPolicyConfig") {
private val role by string()

View File

@ -58,6 +58,9 @@ internal object V1NodeConfigurationSpec : Configuration.Specification<NodeConfig
private val cordappDirectories by string().mapValid(::toPath).list().optional()
private val cordappSignerKeyFingerprintBlacklist by string().list().optional().withDefaultValue(Defaults.cordappSignerKeyFingerprintBlacklist)
private val blacklistedAttachmentSigningKeys by string().list().optional().withDefaultValue(Defaults.blacklistedAttachmentSigningKeys)
private val networkParameterAcceptanceSettings by nested(NetworkParameterAcceptanceSettingsSpec)
.optional()
.withDefaultValue(Defaults.networkParameterAcceptanceSettings)
@Suppress("unused")
private val custom by nestedObject().optional()
@Suppress("unused")
@ -117,7 +120,8 @@ internal object V1NodeConfigurationSpec : Configuration.Specification<NodeConfig
jarDirs = configuration[jarDirs],
cordappDirectories = cordappDirectories.map { baseDirectoryPath.resolve(it) },
cordappSignerKeyFingerprintBlacklist = configuration[cordappSignerKeyFingerprintBlacklist],
blacklistedAttachmentSigningKeys = configuration[blacklistedAttachmentSigningKeys]
blacklistedAttachmentSigningKeys = configuration[blacklistedAttachmentSigningKeys],
networkParameterAcceptanceSettings = configuration[networkParameterAcceptanceSettings]
))
} catch (e: Exception) {
return when (e) {

View File

@ -14,6 +14,7 @@ import net.corda.core.context.Trace.InvocationId
import net.corda.core.identity.CordaX500Name
import net.corda.core.internal.LifeCycle
import net.corda.core.internal.NamedCacheFactory
import net.corda.core.messaging.CordaRPCOps
import net.corda.core.messaging.RPCOps
import net.corda.core.serialization.SerializationContext
import net.corda.core.serialization.SerializationDefaults
@ -25,6 +26,7 @@ import net.corda.node.internal.security.RPCSecurityManager
import net.corda.node.serialization.amqp.RpcServerObservableSerializer
import net.corda.node.services.logging.pushToLoggingContext
import net.corda.nodeapi.RPCApi
import net.corda.nodeapi.RPCApi.CLASS_METHOD_DIVIDER
import net.corda.nodeapi.externalTrace
import net.corda.nodeapi.impersonatedActor
import net.corda.nodeapi.internal.DeduplicationChecker
@ -67,15 +69,18 @@ data class RPCServerConfiguration(
}
/**
* The [RPCServer] implements the complement of [RPCClient]. When an RPC request arrives it dispatches to the
* corresponding function in [ops]. During serialisation of the reply (and later observations) the server subscribes to
* The [RPCServer] implements the complement of [net.corda.client.rpc.internal.RPCClient]. When an RPC request arrives it dispatches to the
* corresponding function in [opsList]. During serialisation of the reply (and later observations) the server subscribes to
* each Observable it encounters and captures the client address to associate with these Observables. Later it uses this
* address to forward observations arriving on the Observables.
*
* The way this is done is similar to that in [RPCClient], we use Kryo and add a context to stores the subscription map.
* The way this is done is similar to that in [net.corda.client.rpc.internal.RPCClient], we use AMQP and add a context to stores the subscription map.
*
* NB: The order of elements in [opsList] matters in case of legacy RPC clients who do not specify class name of the RPC Ops they are after.
* For Legacy RPC clients who supply method name alone, the calls are being targeted at first element in [opsList].
*/
class RPCServer(
private val ops: RPCOps,
private val opsList: List<RPCOps>,
private val rpcServerUsername: String,
private val rpcServerPassword: String,
private val serverLocator: ServerLocator,
@ -86,6 +91,8 @@ class RPCServer(
) {
private companion object {
private val log = contextLogger()
private data class InvocationTarget(val method: Method, val instance: RPCOps)
}
private enum class State {
@ -102,8 +109,13 @@ class RPCServer(
private data class MessageAndContext(val message: RPCApi.ServerToClient.RpcReply, val context: ObservableContext)
private val lifeCycle = LifeCycle(State.UNSTARTED)
/** The methodname->Method map to use for dispatching. */
private val methodTable: Map<String, Method>
/**
* The method name -> InvocationTarget used for servicing the actual call.
* NB: The key in this map can either be:
* - FQN of the method including interface name for all the interfaces except `CordaRPCOps`;
* - For `CordaRPCOps` interface this will be just plain method name. This is done to maintain wire compatibility with previous versions.
*/
private val methodTable: Map<String, InvocationTarget>
/** The observable subscription mapping. */
private val observableMap = createObservableSubscriptionMap()
/** A mapping from client addresses to IDs of associated Observables */
@ -130,16 +142,47 @@ class RPCServer(
private val deduplicationChecker = DeduplicationChecker(rpcConfiguration.deduplicationCacheExpiry, cacheFactory = cacheFactory)
private var deduplicationIdentity: String? = null
constructor (
ops: RPCOps,
rpcServerUsername: String,
rpcServerPassword: String,
serverLocator: ServerLocator,
securityManager: RPCSecurityManager,
nodeLegalName: CordaX500Name,
rpcConfiguration: RPCServerConfiguration,
cacheFactory: NamedCacheFactory
) : this(listOf(ops), rpcServerUsername, rpcServerPassword, serverLocator, securityManager, nodeLegalName, rpcConfiguration, cacheFactory)
init {
val groupedMethods = ops.javaClass.declaredMethods.groupBy { it.name }
groupedMethods.forEach { name, methods ->
if (methods.size > 1) {
throw IllegalArgumentException("Encountered more than one method called $name on ${ops.javaClass.name}")
val mutableMethodTable = mutableMapOf<String, InvocationTarget>()
opsList.forEach { ops ->
listOfApplicableInterfacesRec(ops.javaClass).toSet().forEach { interfaceClass ->
val groupedMethods = with(interfaceClass) {
if(interfaceClass == CordaRPCOps::class.java) {
methods.groupBy { it.name }
} else {
methods.groupBy { interfaceClass.name + CLASS_METHOD_DIVIDER + it.name }
}
}
groupedMethods.forEach { name, methods ->
if (methods.size > 1) {
throw IllegalArgumentException("Encountered more than one method called $name on ${interfaceClass.name}")
}
}
val interimMap = groupedMethods.mapValues { InvocationTarget(it.value.single(), ops) }
mutableMethodTable.putAll(interimMap)
}
}
methodTable = groupedMethods.mapValues { it.value.single() }
// Going forward it is should be treated as immutable construct.
methodTable = mutableMethodTable
}
private fun listOfApplicableInterfacesRec(clazz: Class<*>): List<Class<*>> =
clazz.interfaces.filter { RPCOps::class.java.isAssignableFrom(it) }.flatMap {
listOf(it) + listOfApplicableInterfacesRec(it)
}
private fun createObservableSubscriptionMap(): ObservableSubscriptionMap {
val onObservableRemove = RemovalListener<InvocationId, ObservableSubscription> { key, value, cause ->
log.debug { "Unsubscribing from Observable with id $key because of $cause" }
@ -349,18 +392,18 @@ class RPCServer(
}
}
private fun invokeRpc(context: RpcAuthContext, methodName: String, arguments: List<Any?>): Try<Any> {
private fun invokeRpc(context: RpcAuthContext, inMethodName: String, arguments: List<Any?>): Try<Any> {
return Try.on {
try {
CURRENT_RPC_CONTEXT.set(context)
log.trace { "Calling $methodName" }
val method = methodTable[methodName] ?:
throw RPCException("Received RPC for unknown method $methodName - possible client/server version skew?")
method.invoke(ops, *arguments.toTypedArray())
log.trace { "Calling $inMethodName" }
val invocationTarget = methodTable[inMethodName] ?:
throw RPCException("Received RPC for unknown method $inMethodName - possible client/server version skew?")
invocationTarget.method.invoke(invocationTarget.instance, *arguments.toTypedArray())
} catch (e: InvocationTargetException) {
throw e.cause ?: RPCException("Caught InvocationTargetException without cause")
} catch (e: Exception) {
log.warn("Caught exception attempting to invoke RPC $methodName", e)
log.warn("Caught exception attempting to invoke RPC $inMethodName", e)
throw e
} finally {
CURRENT_RPC_CONTEXT.remove()
@ -393,7 +436,7 @@ class RPCServer(
* we receive a notification that the client queue bindings were added.
*/
private fun bufferIfQueueNotBound(clientAddress: SimpleString, message: RPCApi.ServerToClient.RpcReply, context: ObservableContext): Boolean {
val clientBuffer = responseMessageBuffer.compute(clientAddress, { _, value ->
val clientBuffer = responseMessageBuffer.compute(clientAddress) { _, value ->
when (value) {
null -> BufferOrNone.Buffer(ArrayList()).apply {
container.add(MessageAndContext(message, context))
@ -403,7 +446,7 @@ class RPCServer(
}
is BufferOrNone.None -> value
}
})
}
return clientBuffer is BufferOrNone.Buffer
}

View File

@ -3,7 +3,7 @@ package net.corda.node.services.statemachine
import co.paralleluniverse.fibers.Fiber
import co.paralleluniverse.fibers.FiberExecutorScheduler
import co.paralleluniverse.fibers.Suspendable
import co.paralleluniverse.fibers.instrument.SuspendableHelper
import co.paralleluniverse.fibers.instrument.JavaAgent
import co.paralleluniverse.strands.channels.Channels
import com.codahale.metrics.Gauge
import net.corda.core.concurrent.CordaFuture
@ -307,7 +307,7 @@ class SingleThreadedStateMachineManager(
}
private fun checkQuasarJavaAgentPresence() {
check(SuspendableHelper.isJavaAgentActive()) {
check(JavaAgent.isActive()) {
"""Missing the '-javaagent' JVM argument. Make sure you run the tests with the Quasar java agent attached to your JVM.
#See https://docs.corda.net/head/testing.html#running-tests-in-intellij - 'Fiber classes not instrumented' for more details.""".trimMargin("#")
}

View File

@ -44,7 +44,7 @@ dependencies {
// Corda integration dependencies
cordaRuntime project(path: ":node:capsule", configuration: 'runtimeArtifacts')
cordaRuntime project(path: ":webserver:webcapsule", configuration: 'runtimeArtifacts')
cordaRuntime project(path: ":testing:testserver:testcapsule:", configuration: 'runtimeArtifacts')
cordapp project(':samples:attachment-demo:contracts')
cordapp project(':samples:attachment-demo:workflows')
@ -64,7 +64,7 @@ dependencies {
testCompile "org.assertj:assertj-core:$assertj_version"
integrationTestCompile project(':webserver')
integrationTestCompile project(':testing:testserver')
}
task integrationTest(type: Test, dependsOn: []) {
@ -73,7 +73,7 @@ task integrationTest(type: Test, dependsOn: []) {
}
def nodeTask = tasks.getByPath(':node:capsule:assemble')
def webTask = tasks.getByPath(':webserver:webcapsule:assemble')
def webTask = tasks.getByPath(':testing:testserver:testcapsule::assemble')
task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar', nodeTask, webTask]) {
ext.rpcUsers = [['username': "demo", 'password': "demo", 'permissions': ["StartFlow.net.corda.attachmentdemo.AttachmentDemoFlow",
"InvokeRpc.partiesFromName",

View File

@ -19,11 +19,11 @@ dependencies {
// Corda integration dependencies
cordaRuntime project(path: ":node:capsule", configuration: 'runtimeArtifacts')
cordaRuntime project(path: ":webserver:webcapsule", configuration: 'runtimeArtifacts')
cordaRuntime project(path: ":testing:testserver:testcapsule:", configuration: 'runtimeArtifacts')
cordaCompile project(':core')
cordaCompile project(':client:jfx')
cordaCompile project(':client:rpc')
cordaCompile (project(':webserver')) {
cordaCompile(project(':testing:testserver')) {
exclude group: "org.apache.logging.log4j"
}
cordaCompile (project(':node-driver')) {
@ -43,7 +43,7 @@ dependencies {
}
def nodeTask = tasks.getByPath(':node:capsule:assemble')
def webTask = tasks.getByPath(':webserver:webcapsule:assemble')
def webTask = tasks.getByPath(':testing:testserver:testcapsule::assemble')
task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar', nodeTask, webTask]) {
nodeDefaults {
cordapp project(':finance:workflows')

View File

@ -16,7 +16,7 @@ dependencies {
}
def nodeTask = tasks.getByPath(':node:capsule:assemble')
def webTask = tasks.getByPath(':webserver:webcapsule:assemble')
def webTask = tasks.getByPath(':testing:testserver:testcapsule::assemble')
task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar', nodeTask, webTask]) {
directory file("$buildDir/nodes")
nodeDefaults {

View File

@ -31,7 +31,7 @@ dependencies {
}
def nodeTask = tasks.getByPath(':node:capsule:assemble')
def webTask = tasks.getByPath(':webserver:webcapsule:assemble')
def webTask = tasks.getByPath(':testing:testserver:testcapsule::assemble')
task deployNodes(dependsOn: ['deployNodesSingle', 'deployNodesRaft', 'deployNodesBFT', 'deployNodesCustom'])

View File

@ -41,9 +41,9 @@ dependencies {
// Corda integration dependencies
cordaRuntime project(path: ":node:capsule", configuration: 'runtimeArtifacts')
cordaRuntime project(path: ":webserver:webcapsule", configuration: 'runtimeArtifacts')
cordaRuntime project(path: ":testing:testserver:testcapsule:", configuration: 'runtimeArtifacts')
cordaCompile project(':core')
cordaCompile (project(':webserver')) {
cordaCompile(project(':testing:testserver')) {
exclude group: "org.apache.logging.log4j"
}
@ -82,7 +82,7 @@ jar {
}
def nodeTask = tasks.getByPath(':node:capsule:assemble')
def webTask = tasks.getByPath(':webserver:webcapsule:assemble')
def webTask = tasks.getByPath(':testing:testserver:testcapsule::assemble')
task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar', nodeTask, webTask]) {
directory file("$buildDir/nodes")
nodeDefaults {

View File

@ -25,8 +25,8 @@ include 'client:jfx'
include 'client:mock'
include 'client:rpc'
include 'docker'
include 'webserver'
include 'webserver:webcapsule'
include 'testing:testserver'
include 'testing:testserver:testcapsule:'
include 'experimental'
include 'experimental:avalanche'
include 'experimental:blobwriter'

View File

@ -1,5 +1,6 @@
package net.corda.testing.node.internal
import co.paralleluniverse.fibers.instrument.JavaAgent
import com.google.common.util.concurrent.ThreadFactoryBuilder
import com.typesafe.config.Config
import com.typesafe.config.ConfigFactory
@ -54,7 +55,6 @@ import okhttp3.Request
import rx.Subscription
import rx.schedulers.Schedulers
import java.io.File
import java.lang.management.ManagementFactory
import java.net.ConnectException
import java.net.URL
import java.nio.file.Path
@ -708,7 +708,7 @@ class DriverDSLImpl(
val effectiveP2PAddress = config.corda.messagingServerAddress ?: config.corda.p2pAddress
return executorService.fork {
log.info("Starting in-process Node ${config.corda.myLegalName.organisation}")
if (!(ManagementFactory.getRuntimeMXBean().inputArguments.any { it.contains("quasar") })) {
if (!JavaAgent.isActive()) {
throw IllegalStateException("No quasar agent: -javaagent:lib/quasar.jar and working directory project root might fix")
}
// Write node.conf

View File

@ -303,15 +303,6 @@ data class RPCDriverDSL(
return session
}
/**
* Starts a Netty RPC server.
*
* @param serverName The name of the server, to be used for the folder created for Artemis files.
* @param rpcUser The single user who can access the server through RPC, and their permissions.
* @param nodeLegalName The legal name of the node to check against to authenticate a super user.
* @param configuration The RPC server configuration.
* @param ops The server-side implementation of the RPC interface.
*/
fun <I : RPCOps> startRpcServer(
serverName: String = "driver-rpc-server-${random63BitValue()}",
rpcUser: User = rpcTestUser,
@ -321,9 +312,29 @@ data class RPCDriverDSL(
configuration: RPCServerConfiguration = RPCServerConfiguration.DEFAULT,
customPort: NetworkHostAndPort? = null,
ops: I
) = startRpcServer(serverName, rpcUser, nodeLegalName, maxFileSize, maxBufferedBytesPerClient, configuration, customPort, listOf(ops))
/**
* Starts a Netty RPC server.
*
* @param serverName The name of the server, to be used for the folder created for Artemis files.
* @param rpcUser The single user who can access the server through RPC, and their permissions.
* @param nodeLegalName The legal name of the node to check against to authenticate a super user.
* @param configuration The RPC server configuration.
* @param listOps The server-side implementation of the RPC interfaces.
*/
fun <I : RPCOps> startRpcServer(
serverName: String = "driver-rpc-server-${random63BitValue()}",
rpcUser: User = rpcTestUser,
nodeLegalName: CordaX500Name = fakeNodeLegalName,
maxFileSize: Int = MAX_MESSAGE_SIZE,
maxBufferedBytesPerClient: Long = 5L * MAX_MESSAGE_SIZE,
configuration: RPCServerConfiguration = RPCServerConfiguration.DEFAULT,
customPort: NetworkHostAndPort? = null,
listOps: List<I>
): CordaFuture<RpcServerHandle> {
return startRpcBroker(serverName, rpcUser, maxFileSize, maxBufferedBytesPerClient, customPort).map { broker ->
startRpcServerWithBrokerRunning(rpcUser, nodeLegalName, configuration, ops, broker)
startRpcServerWithBrokerRunning(rpcUser, nodeLegalName, configuration, listOps, broker)
}
}
@ -477,14 +488,24 @@ data class RPCDriverDSL(
ops: I,
brokerHandle: RpcBrokerHandle,
queueDrainTimeout: Duration = 5.seconds
) = startRpcServerWithBrokerRunning(rpcUser, nodeLegalName, configuration, listOf(ops), brokerHandle, queueDrainTimeout)
private fun <I : RPCOps> startRpcServerWithBrokerRunning(
rpcUser: User = rpcTestUser,
nodeLegalName: CordaX500Name = fakeNodeLegalName,
configuration: RPCServerConfiguration = RPCServerConfiguration.DEFAULT,
listOps: List<I>,
brokerHandle: RpcBrokerHandle,
queueDrainTimeout: Duration = 5.seconds
): RpcServerHandle {
val locator = ActiveMQClient.createServerLocatorWithoutHA(brokerHandle.clientTransportConfiguration).apply {
minLargeMessageSize = MAX_MESSAGE_SIZE
isUseGlobalPools = false
}
val rpcSecurityManager = RPCSecurityManagerImpl.fromUserList(users = listOf(InternalUser(rpcUser.username, rpcUser.password, rpcUser.permissions)), id = AuthServiceId("TEST_SECURITY_MANAGER"))
val rpcSecurityManager = RPCSecurityManagerImpl.fromUserList(users = listOf(InternalUser(rpcUser.username,
rpcUser.password, rpcUser.permissions)), id = AuthServiceId("TEST_SECURITY_MANAGER"))
val rpcServer = RPCServer(
ops,
listOps,
rpcUser.username,
rpcUser.password,
locator,

View File

@ -76,7 +76,7 @@ task integrationTest(type: Test) {
}
jar {
baseName 'corda-webserver-impl'
baseName 'corda-testserver-impl'
}
publish {

View File

@ -26,12 +26,12 @@ capsule {
task buildWebserverJar(type: FatCapsule, dependsOn: project(':node').tasks.jar) {
applicationClass 'net.corda.webserver.WebServer'
archiveName "corda-webserver-${corda_release_version}.jar"
archiveName "corda-testserver-${corda_release_version}.jar"
applicationSource = files(
project(':webserver').configurations.runtimeClasspath,
project(':webserver').tasks.jar,
project(':webserver').sourceSets.main.java.outputDir.toString() + '/CordaWebserverCaplet.class',
project(':webserver').sourceSets.main.java.outputDir.toString() + '/CordaWebserverCaplet$1.class',
project(':testing:testserver').configurations.runtimeClasspath,
project(':testing:testserver').tasks.jar,
project(':testing:testserver').sourceSets.main.java.outputDir.toString() + '/CordaWebserverCaplet.class',
project(':testing:testserver').sourceSets.main.java.outputDir.toString() + '/CordaWebserverCaplet$1.class',
project(':node').buildDir.toString() + '/resources/main/reference.conf',
"$rootDir/config/dev/log4j2.xml",
project(':node:capsule').projectDir.toString() + '/NOTICE' // Copy CDDL notice
@ -66,5 +66,5 @@ artifacts {
publish {
disableDefaultJar = true
name 'corda-webserver'
name 'corda-testserver'
}

View File

@ -31,7 +31,7 @@ apply plugin: 'kotlin'
apply plugin: 'application'
evaluationDependsOn(':tools:explorer:capsule')
evaluationDependsOn(':webserver:webcapsule')
evaluationDependsOn(':testing:testserver:testcapsule:')
mainClassName = 'net.corda.demobench.DemoBench'
applicationDefaultJvmArgs = [
@ -85,7 +85,7 @@ dependencies {
compile ':purejavacomm-0.0.18'
testCompile project(':test-utils')
testCompile project(':webserver')
testCompile project(':testing:testserver')
testRuntimeOnly "org.junit.vintage:junit-vintage-engine:${junit_vintage_version}"
testRuntimeOnly "org.junit.platform:junit-platform-launcher:${junit_platform_version}"
@ -139,8 +139,8 @@ distributions {
into 'corda'
fileMode = 0444
}
from(project(':webserver:webcapsule').tasks.buildWebserverJar) {
rename 'corda-webserver-(.*)', 'corda-webserver.jar'
from(project(':testing:testserver:testcapsule:').tasks.buildWebserverJar) {
rename 'corda-testserver-(.*)', 'corda-testserver.jar'
into 'corda'
fileMode = 0444
}

View File

@ -5,7 +5,7 @@ import tornadofx.*
class WebServerController : Controller() {
private val jvm by inject<JVMConfig>()
private val webserverPath = jvm.applicationDir.resolve("corda").resolve("corda-webserver.jar")
private val webserverPath = jvm.applicationDir.resolve("corda").resolve("corda-testserver.jar")
init {
log.info("Web Server JAR: $webserverPath")