mirror of
https://github.com/corda/corda.git
synced 2025-06-13 04:38:19 +00:00
Merge remote-tracking branch 'remotes/open/master' into merges/may-14-15-21
# Conflicts: # .idea/compiler.xml # constants.properties # experimental/behave/src/main/kotlin/net/corda/behave/network/Network.kt # experimental/behave/src/main/kotlin/net/corda/behave/node/Distribution.kt # experimental/behave/src/test/kotlin/net/corda/behave/service/PostreSQLServiceTests.kt # node-api/src/main/kotlin/net/corda/nodeapi/internal/bridging/AMQPBridgeManager.kt # node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/CordaPersistence.kt # node-api/src/main/kotlin/net/corda/nodeapi/internal/persistence/HibernateConfiguration.kt # node-api/src/main/kotlin/net/corda/nodeapi/internal/protonwrapper/netty/AMQPChannelHandler.kt # node-api/src/main/kotlin/net/corda/nodeapi/internal/protonwrapper/netty/AMQPClient.kt # node/src/integration-test/kotlin/net/corda/node/AuthDBTests.kt # node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt # node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt # node/src/main/kotlin/net/corda/node/services/config/ConfigUtilities.kt # node/src/main/kotlin/net/corda/node/services/messaging/MessagingExecutor.kt # node/src/main/kotlin/net/corda/node/services/persistence/AbstractPartyToX500NameAsStringConverter.kt # node/src/main/kotlin/net/corda/node/services/persistence/DBTransactionMappingStorage.kt # node/src/main/kotlin/net/corda/node/utilities/AddOrRemove.kt # node/src/test/kotlin/net/corda/node/services/config/NodeConfigurationImplTest.kt # samples/attachment-demo/src/integration-test/kotlin/net/corda/attachmentdemo/AttachmentDemoTest.kt # samples/irs-demo/web/src/test/kotlin/net/corda/irs/web/IrsDemoWebApplicationTests.kt # samples/simm-valuation-demo/src/integration-test/kotlin/net/corda/vega/SimmValuationTest.kt # testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt # testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/InternalTestUtils.kt # testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/RPCDriver.kt # testing/smoke-test-utils/src/main/kotlin/net/corda/smoketesting/NodeConfig.kt # webserver/src/integration-test/kotlin/net/corda/webserver/WebserverDriverTests.kt
This commit is contained in:
@ -113,7 +113,7 @@ class ArtemisTcpTransport {
|
||||
direction: ConnectionDirection,
|
||||
hostAndPortList: List<NetworkHostAndPort>,
|
||||
config: SSLConfiguration?,
|
||||
enableSSL: Boolean = true): List<TransportConfiguration>{
|
||||
enableSSL: Boolean = true): List<TransportConfiguration> {
|
||||
val tcpTransports = ArrayList<TransportConfiguration>(hostAndPortList.size)
|
||||
hostAndPortList.forEach {
|
||||
tcpTransports.add(tcpTransport(direction, it, config, enableSSL))
|
||||
|
@ -258,22 +258,22 @@ object RPCApi {
|
||||
}
|
||||
}
|
||||
|
||||
private val TAG_FIELD_NAME = "tag"
|
||||
private val RPC_ID_FIELD_NAME = "rpc-id"
|
||||
private val RPC_ID_TIMESTAMP_FIELD_NAME = "rpc-id-timestamp"
|
||||
private val RPC_SESSION_ID_FIELD_NAME = "rpc-session-id"
|
||||
private val RPC_SESSION_ID_TIMESTAMP_FIELD_NAME = "rpc-session-id-timestamp"
|
||||
private val RPC_EXTERNAL_ID_FIELD_NAME = "rpc-external-id"
|
||||
private val RPC_EXTERNAL_ID_TIMESTAMP_FIELD_NAME = "rpc-external-id-timestamp"
|
||||
private val RPC_EXTERNAL_SESSION_ID_FIELD_NAME = "rpc-external-session-id"
|
||||
private val RPC_EXTERNAL_SESSION_ID_TIMESTAMP_FIELD_NAME = "rpc-external-session-id-timestamp"
|
||||
private val RPC_IMPERSONATED_ACTOR_ID = "rpc-impersonated-actor-id"
|
||||
private val RPC_IMPERSONATED_ACTOR_STORE_ID = "rpc-impersonated-actor-store-id"
|
||||
private val RPC_IMPERSONATED_ACTOR_OWNING_LEGAL_IDENTITY = "rpc-impersonated-actor-owningLegalIdentity"
|
||||
private val DEDUPLICATION_IDENTITY_FIELD_NAME = "deduplication-identity"
|
||||
private val OBSERVABLE_ID_FIELD_NAME = "observable-id"
|
||||
private val OBSERVABLE_ID_TIMESTAMP_FIELD_NAME = "observable-id-timestamp"
|
||||
private val METHOD_NAME_FIELD_NAME = "method-name"
|
||||
private const val TAG_FIELD_NAME = "tag"
|
||||
private const val RPC_ID_FIELD_NAME = "rpc-id"
|
||||
private const val RPC_ID_TIMESTAMP_FIELD_NAME = "rpc-id-timestamp"
|
||||
private const val RPC_SESSION_ID_FIELD_NAME = "rpc-session-id"
|
||||
private const val RPC_SESSION_ID_TIMESTAMP_FIELD_NAME = "rpc-session-id-timestamp"
|
||||
private const val RPC_EXTERNAL_ID_FIELD_NAME = "rpc-external-id"
|
||||
private const val RPC_EXTERNAL_ID_TIMESTAMP_FIELD_NAME = "rpc-external-id-timestamp"
|
||||
private const val RPC_EXTERNAL_SESSION_ID_FIELD_NAME = "rpc-external-session-id"
|
||||
private const val RPC_EXTERNAL_SESSION_ID_TIMESTAMP_FIELD_NAME = "rpc-external-session-id-timestamp"
|
||||
private const val RPC_IMPERSONATED_ACTOR_ID = "rpc-impersonated-actor-id"
|
||||
private const val RPC_IMPERSONATED_ACTOR_STORE_ID = "rpc-impersonated-actor-store-id"
|
||||
private const val RPC_IMPERSONATED_ACTOR_OWNING_LEGAL_IDENTITY = "rpc-impersonated-actor-owningLegalIdentity"
|
||||
private const val DEDUPLICATION_IDENTITY_FIELD_NAME = "deduplication-identity"
|
||||
private const val OBSERVABLE_ID_FIELD_NAME = "observable-id"
|
||||
private const val OBSERVABLE_ID_TIMESTAMP_FIELD_NAME = "observable-id-timestamp"
|
||||
private const val METHOD_NAME_FIELD_NAME = "method-name"
|
||||
|
||||
fun ClientMessage.replyId(): InvocationId {
|
||||
|
||||
|
@ -59,7 +59,6 @@ class ArtemisMessagingComponent {
|
||||
*/
|
||||
val bridgedCertificateSubject = SimpleString("sender-subject-name")
|
||||
|
||||
|
||||
object Type {
|
||||
const val KEY = "corda_p2p_message_type"
|
||||
const val SESSION_INIT_VALUE = "session_init"
|
||||
@ -138,5 +137,4 @@ class ArtemisMessagingComponent {
|
||||
|
||||
override val queueName: String = "$P2P_PREFIX${identity.toStringShort()}"
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -70,6 +70,7 @@ inline fun NodeInfo.sign(signer: (PublicKey, SerializedBytes<NodeInfo>) -> Digit
|
||||
class NodeInfoAndSigned private constructor(val nodeInfo: NodeInfo, val signed: SignedNodeInfo) {
|
||||
constructor(nodeInfo: NodeInfo, signer: (PublicKey, SerializedBytes<NodeInfo>) -> DigitalSignature) : this(nodeInfo, nodeInfo.sign(signer))
|
||||
constructor(signedNodeInfo: SignedNodeInfo) : this(signedNodeInfo.verified(), signedNodeInfo)
|
||||
|
||||
operator fun component1(): NodeInfo = nodeInfo
|
||||
operator fun component2(): SignedNodeInfo = signed
|
||||
}
|
||||
|
@ -48,7 +48,7 @@ import kotlin.concurrent.withLock
|
||||
* The Netty thread pool used by the AMQPBridges is also shared and managed by the AMQPBridgeManager.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
class AMQPBridgeManager(config: NodeSSLConfiguration, private val socksProxyConfig: SocksProxyConfig? = null, val artemisMessageClientFactory: () -> ArtemisSessionProvider) : BridgeManager {
|
||||
class AMQPBridgeManager(config: NodeSSLConfiguration, private val socksProxyConfig: SocksProxyConfig? = null, private val artemisMessageClientFactory: () -> ArtemisSessionProvider) : BridgeManager {
|
||||
|
||||
private val lock = ReentrantLock()
|
||||
private val bridgeNameToBridgeMap = mutableMapOf<String, AMQPBridge>()
|
||||
|
@ -16,6 +16,6 @@ data class User(
|
||||
val password: String,
|
||||
val permissions: Set<String>) {
|
||||
override fun toString(): String = "${javaClass.simpleName}($username, permissions=$permissions)"
|
||||
@Deprecated("Use toConfig().root().unwrapped() instead")
|
||||
@Deprecated("Use toConfig().root().unwrapped() instead", ReplaceWith("toConfig().root().unwrapped()"))
|
||||
fun toMap(): Map<String, Any> = toConfig().root().unwrapped()
|
||||
}
|
||||
|
@ -23,7 +23,6 @@ import net.corda.nodeapi.internal.crypto.X509Utilities
|
||||
import java.security.cert.X509Certificate
|
||||
import java.time.Instant
|
||||
|
||||
|
||||
const val NETWORK_PARAMS_FILE_NAME = "network-parameters"
|
||||
const val NETWORK_PARAMS_UPDATE_FILE_NAME = "network-parameters-update"
|
||||
|
||||
@ -81,6 +80,7 @@ fun <T : Any> SignedDataWithCert<T>.verifiedNetworkMapCert(rootCert: X509Certifi
|
||||
class NetworkMapAndSigned private constructor(val networkMap: NetworkMap, val signed: SignedNetworkMap) {
|
||||
constructor(networkMap: NetworkMap, signer: (SerializedBytes<NetworkMap>) -> DigitalSignatureWithCert) : this(networkMap, networkMap.signWithCert(signer))
|
||||
constructor(signed: SignedNetworkMap) : this(signed.verified(), signed)
|
||||
|
||||
operator fun component1(): NetworkMap = networkMap
|
||||
operator fun component2(): SignedNetworkMap = signed
|
||||
}
|
||||
|
@ -290,10 +290,8 @@ fun isH2Database(jdbcUrl: String) = jdbcUrl.startsWith("jdbc:h2:")
|
||||
|
||||
/** Check if any nested cause is of [SQLException] type. */
|
||||
private fun Throwable.hasSQLExceptionCause(): Boolean =
|
||||
if (cause == null)
|
||||
false
|
||||
else if (cause is SQLException)
|
||||
true
|
||||
else
|
||||
cause?.hasSQLExceptionCause() ?: false
|
||||
|
||||
when (cause) {
|
||||
null -> false
|
||||
is SQLException -> true
|
||||
else -> cause?.hasSQLExceptionCause() ?: false
|
||||
}
|
@ -176,8 +176,8 @@ class HibernateConfiguration(
|
||||
}
|
||||
|
||||
// A tweaked version of `org.hibernate.type.descriptor.java.PrimitiveByteArrayTypeDescriptor` that truncates logged messages.
|
||||
object CordaPrimitiveByteArrayTypeDescriptor : PrimitiveByteArrayTypeDescriptor() {
|
||||
private val LOG_SIZE_LIMIT = 1024
|
||||
private object CordaPrimitiveByteArrayTypeDescriptor : PrimitiveByteArrayTypeDescriptor() {
|
||||
private const val LOG_SIZE_LIMIT = 1024
|
||||
|
||||
override fun extractLoggableRepresentation(value: ByteArray?): String {
|
||||
return if (value == null) {
|
||||
|
@ -72,6 +72,7 @@ internal class ConnectionStateMachine(serverMode: Boolean,
|
||||
transport = Engine.transport()
|
||||
transport.idleTimeout = IDLE_TIMEOUT
|
||||
transport.context = connection
|
||||
@Suppress("UsePropertyAccessSyntax")
|
||||
transport.setEmitFlowEventOnSend(true)
|
||||
connection.collect(collector)
|
||||
val sasl = transport.sasl()
|
||||
|
@ -15,7 +15,6 @@ import io.netty.channel.ChannelDuplexHandler
|
||||
import io.netty.channel.ChannelHandlerContext
|
||||
import io.netty.channel.ChannelPromise
|
||||
import io.netty.channel.socket.SocketChannel
|
||||
import io.netty.handler.proxy.ProxyConnectException
|
||||
import io.netty.handler.proxy.ProxyConnectionEvent
|
||||
import io.netty.handler.ssl.SslHandler
|
||||
import io.netty.handler.ssl.SslHandshakeCompletionEvent
|
||||
@ -101,31 +100,38 @@ internal class AMQPChannelHandler(private val serverMode: Boolean,
|
||||
val sslHandler = ctx.pipeline().get(SslHandler::class.java)
|
||||
localCert = sslHandler.engine().session.localCertificates[0].x509
|
||||
remoteCert = sslHandler.engine().session.peerCertificates[0].x509
|
||||
try {
|
||||
val remoteX500Name = CordaX500Name.build(remoteCert!!.subjectX500Principal)
|
||||
require(allowedRemoteLegalNames == null || remoteX500Name in allowedRemoteLegalNames)
|
||||
log.info("handshake completed subject: $remoteX500Name")
|
||||
val remoteX500Name = try {
|
||||
CordaX500Name.build(remoteCert!!.subjectX500Principal)
|
||||
} catch (ex: IllegalArgumentException) {
|
||||
log.error("Invalid certificate subject", ex)
|
||||
log.error("Certificate subject not a valid CordaX500Name", ex)
|
||||
ctx.close()
|
||||
return
|
||||
}
|
||||
if (allowedRemoteLegalNames != null && remoteX500Name !in allowedRemoteLegalNames) {
|
||||
log.error("Provided certificate subject $remoteX500Name not in expected set $allowedRemoteLegalNames")
|
||||
ctx.close()
|
||||
return
|
||||
}
|
||||
log.info("Handshake completed with subject: $remoteX500Name")
|
||||
createAMQPEngine(ctx)
|
||||
onOpen(Pair(ctx.channel() as SocketChannel, ConnectionChange(remoteAddress, remoteCert, true)))
|
||||
} else {
|
||||
log.error("Handshake failure $evt")
|
||||
log.error("Handshake failure ${evt.cause().message}")
|
||||
if (log.isTraceEnabled) {
|
||||
log.trace("Handshake failure", evt.cause())
|
||||
}
|
||||
ctx.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Suppress("OverridingDeprecatedMember")
|
||||
override fun exceptionCaught(ctx: ChannelHandlerContext, cause: Throwable) {
|
||||
if (cause is ProxyConnectException) {
|
||||
log.warn("Proxy connection failed ${cause.message}")
|
||||
suppressClose = true // The pipeline gets marked as active on connection to the proxy rather than to the target, which causes excess close events
|
||||
log.warn("Closing channel due to nonrecoverable exception ${cause.message}")
|
||||
if (log.isTraceEnabled) {
|
||||
log.trace("Pipeline uncaught exception", cause)
|
||||
}
|
||||
ctx.close()
|
||||
}
|
||||
|
||||
override fun channelRead(ctx: ChannelHandlerContext, msg: Any) {
|
||||
|
@ -30,6 +30,7 @@ import net.corda.nodeapi.internal.protonwrapper.messages.impl.SendableMessageImp
|
||||
import rx.Observable
|
||||
import rx.subjects.PublishSubject
|
||||
import java.net.InetSocketAddress
|
||||
import java.lang.Long.min
|
||||
import java.security.KeyStore
|
||||
import java.util.concurrent.TimeUnit
|
||||
import java.util.concurrent.locks.ReentrantLock
|
||||
@ -74,7 +75,9 @@ class AMQPClient(val targets: List<NetworkHostAndPort>,
|
||||
}
|
||||
|
||||
val log = contextLogger()
|
||||
const val RETRY_INTERVAL = 1000L
|
||||
const val MIN_RETRY_INTERVAL = 1000L
|
||||
const val MAX_RETRY_INTERVAL = 60000L
|
||||
const val BACKOFF_MULTIPLIER = 2L
|
||||
const val NUM_CLIENT_THREADS = 2
|
||||
}
|
||||
|
||||
@ -87,6 +90,13 @@ class AMQPClient(val targets: List<NetworkHostAndPort>,
|
||||
// Offset into the list of targets, so that we can implement round-robin reconnect logic.
|
||||
private var targetIndex = 0
|
||||
private var currentTarget: NetworkHostAndPort = targets.first()
|
||||
private var retryInterval = MIN_RETRY_INTERVAL
|
||||
|
||||
private fun nextTarget() {
|
||||
targetIndex = (targetIndex + 1).rem(targets.size)
|
||||
log.info("Retry connect to ${targets[targetIndex]}")
|
||||
retryInterval = min(MAX_RETRY_INTERVAL, retryInterval * BACKOFF_MULTIPLIER)
|
||||
}
|
||||
|
||||
private val connectListener = object : ChannelFutureListener {
|
||||
override fun operationComplete(future: ChannelFuture) {
|
||||
@ -95,10 +105,9 @@ class AMQPClient(val targets: List<NetworkHostAndPort>,
|
||||
|
||||
if (!stopping) {
|
||||
workerGroup?.schedule({
|
||||
log.info("Retry connect to $currentTarget")
|
||||
targetIndex = (targetIndex + 1).rem(targets.size)
|
||||
nextTarget()
|
||||
restart()
|
||||
}, RETRY_INTERVAL, TimeUnit.MILLISECONDS)
|
||||
}, retryInterval, TimeUnit.MILLISECONDS)
|
||||
}
|
||||
} else {
|
||||
log.info("Connected to $currentTarget")
|
||||
@ -116,10 +125,9 @@ class AMQPClient(val targets: List<NetworkHostAndPort>,
|
||||
clientChannel = null
|
||||
if (!stopping) {
|
||||
workerGroup?.schedule({
|
||||
log.info("Retry connect")
|
||||
targetIndex = (targetIndex + 1).rem(targets.size)
|
||||
nextTarget()
|
||||
restart()
|
||||
}, RETRY_INTERVAL, TimeUnit.MILLISECONDS)
|
||||
}, retryInterval, TimeUnit.MILLISECONDS)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -162,7 +170,10 @@ class AMQPClient(val targets: List<NetworkHostAndPort>,
|
||||
parent.userName,
|
||||
parent.password,
|
||||
parent.trace,
|
||||
{ parent._onConnection.onNext(it.second) },
|
||||
{
|
||||
parent.retryInterval = MIN_RETRY_INTERVAL // reset to fast reconnect if we connect properly
|
||||
parent._onConnection.onNext(it.second)
|
||||
},
|
||||
{ parent._onConnection.onNext(it.second) },
|
||||
{ rcv -> parent._onReceive.onNext(rcv) }))
|
||||
}
|
||||
|
@ -100,7 +100,7 @@ class AMQPServer(val hostName: String,
|
||||
parent.password,
|
||||
parent.trace,
|
||||
{
|
||||
parent.clientChannels.put(it.first.remoteAddress(), it.first)
|
||||
parent.clientChannels[it.first.remoteAddress()] = it.first
|
||||
parent._onConnection.onNext(it.second)
|
||||
},
|
||||
{
|
||||
@ -204,5 +204,4 @@ class AMQPServer(val hostName: String,
|
||||
private val _onConnection = PublishSubject.create<ConnectionChange>().toSerialized()
|
||||
val onConnection: Observable<ConnectionChange>
|
||||
get() = _onConnection
|
||||
|
||||
}
|
@ -12,22 +12,111 @@ package net.corda.nodeapi.internal.protonwrapper.netty
|
||||
|
||||
import io.netty.handler.ssl.SslHandler
|
||||
import net.corda.core.utilities.NetworkHostAndPort
|
||||
import net.corda.core.utilities.contextLogger
|
||||
import net.corda.core.utilities.toHex
|
||||
import net.corda.nodeapi.ArtemisTcpTransport
|
||||
import net.corda.nodeapi.internal.crypto.toBc
|
||||
import org.bouncycastle.asn1.x509.AuthorityKeyIdentifier
|
||||
import org.bouncycastle.asn1.x509.Extension
|
||||
import org.bouncycastle.asn1.x509.SubjectKeyIdentifier
|
||||
import java.net.Socket
|
||||
import java.security.KeyStore
|
||||
import java.security.SecureRandom
|
||||
import java.security.cert.CertPathBuilder
|
||||
import java.security.cert.PKIXBuilderParameters
|
||||
import java.security.cert.PKIXRevocationChecker
|
||||
import java.security.cert.X509CertSelector
|
||||
import java.security.cert.*
|
||||
import java.util.*
|
||||
import javax.net.ssl.*
|
||||
|
||||
internal class LoggingTrustManagerWrapper(val wrapped: X509ExtendedTrustManager) : X509ExtendedTrustManager() {
|
||||
companion object {
|
||||
val log = contextLogger()
|
||||
}
|
||||
|
||||
private fun certPathToString(certPath: Array<out X509Certificate>?): String {
|
||||
if (certPath == null) {
|
||||
return "<empty certpath>"
|
||||
}
|
||||
val certs = certPath.map {
|
||||
val bcCert = it.toBc()
|
||||
val subject = bcCert.subject.toString()
|
||||
val issuer = bcCert.issuer.toString()
|
||||
val keyIdentifier = try {
|
||||
SubjectKeyIdentifier.getInstance(bcCert.getExtension(Extension.subjectKeyIdentifier).parsedValue).keyIdentifier.toHex()
|
||||
} catch (ex: Exception) {
|
||||
"null"
|
||||
}
|
||||
val authorityKeyIdentifier = try {
|
||||
AuthorityKeyIdentifier.getInstance(bcCert.getExtension(Extension.authorityKeyIdentifier).parsedValue).keyIdentifier.toHex()
|
||||
} catch (ex: Exception) {
|
||||
"null"
|
||||
}
|
||||
" $subject[$keyIdentifier] issued by $issuer[$authorityKeyIdentifier]"
|
||||
}
|
||||
return certs.joinToString("\r\n")
|
||||
}
|
||||
|
||||
|
||||
private fun certPathToStringFull(chain: Array<out X509Certificate>?): String {
|
||||
if (chain == null) {
|
||||
return "<empty certpath>"
|
||||
}
|
||||
return chain.map { it.toString() }.joinToString(", ")
|
||||
}
|
||||
|
||||
private fun logErrors(chain: Array<out X509Certificate>?, block: () -> Unit) {
|
||||
try {
|
||||
block()
|
||||
} catch (ex: CertificateException) {
|
||||
log.error("Bad certificate path ${ex.message}:\r\n${certPathToStringFull(chain)}")
|
||||
throw ex
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(CertificateException::class)
|
||||
override fun checkClientTrusted(chain: Array<out X509Certificate>?, authType: String?, socket: Socket?) {
|
||||
log.info("Check Client Certpath:\r\n${certPathToString(chain)}")
|
||||
logErrors(chain) { wrapped.checkClientTrusted(chain, authType, socket) }
|
||||
}
|
||||
|
||||
@Throws(CertificateException::class)
|
||||
override fun checkClientTrusted(chain: Array<out X509Certificate>?, authType: String?, engine: SSLEngine?) {
|
||||
log.info("Check Client Certpath:\r\n${certPathToString(chain)}")
|
||||
logErrors(chain) { wrapped.checkClientTrusted(chain, authType, engine) }
|
||||
}
|
||||
|
||||
@Throws(CertificateException::class)
|
||||
override fun checkClientTrusted(chain: Array<out X509Certificate>?, authType: String?) {
|
||||
log.info("Check Client Certpath:\r\n${certPathToString(chain)}")
|
||||
logErrors(chain) { wrapped.checkClientTrusted(chain, authType) }
|
||||
}
|
||||
|
||||
@Throws(CertificateException::class)
|
||||
override fun checkServerTrusted(chain: Array<out X509Certificate>?, authType: String?, socket: Socket?) {
|
||||
log.info("Check Server Certpath:\r\n${certPathToString(chain)}")
|
||||
logErrors(chain) { wrapped.checkServerTrusted(chain, authType, socket) }
|
||||
}
|
||||
|
||||
@Throws(CertificateException::class)
|
||||
override fun checkServerTrusted(chain: Array<out X509Certificate>?, authType: String?, engine: SSLEngine?) {
|
||||
log.info("Check Server Certpath:\r\n${certPathToString(chain)}")
|
||||
logErrors(chain) { wrapped.checkServerTrusted(chain, authType, engine) }
|
||||
}
|
||||
|
||||
@Throws(CertificateException::class)
|
||||
override fun checkServerTrusted(chain: Array<out X509Certificate>?, authType: String?) {
|
||||
log.info("Check Server Certpath:\r\n${certPathToString(chain)}")
|
||||
logErrors(chain) { wrapped.checkServerTrusted(chain, authType) }
|
||||
}
|
||||
|
||||
override fun getAcceptedIssuers(): Array<X509Certificate> = wrapped.acceptedIssuers
|
||||
|
||||
}
|
||||
|
||||
internal fun createClientSslHelper(target: NetworkHostAndPort,
|
||||
keyManagerFactory: KeyManagerFactory,
|
||||
trustManagerFactory: TrustManagerFactory): SslHandler {
|
||||
val sslContext = SSLContext.getInstance("TLS")
|
||||
val keyManagers = keyManagerFactory.keyManagers
|
||||
val trustManagers = trustManagerFactory.trustManagers
|
||||
val trustManagers = trustManagerFactory.trustManagers.filterIsInstance(X509ExtendedTrustManager::class.java).map { LoggingTrustManagerWrapper(it) }.toTypedArray()
|
||||
sslContext.init(keyManagers, trustManagers, SecureRandom())
|
||||
val sslEngine = sslContext.createSSLEngine(target.host, target.port)
|
||||
sslEngine.useClientMode = true
|
||||
@ -41,7 +130,7 @@ internal fun createServerSslHelper(keyManagerFactory: KeyManagerFactory,
|
||||
trustManagerFactory: TrustManagerFactory): SslHandler {
|
||||
val sslContext = SSLContext.getInstance("TLS")
|
||||
val keyManagers = keyManagerFactory.keyManagers
|
||||
val trustManagers = trustManagerFactory.trustManagers
|
||||
val trustManagers = trustManagerFactory.trustManagers.filterIsInstance(X509ExtendedTrustManager::class.java).map { LoggingTrustManagerWrapper(it) }.toTypedArray()
|
||||
sslContext.init(keyManagers, trustManagers, SecureRandom())
|
||||
val sslEngine = sslContext.createSSLEngine()
|
||||
sslEngine.useClientMode = false
|
||||
|
@ -1,4 +1,5 @@
|
||||
@file:JvmName("ByteBufferStreams")
|
||||
|
||||
package net.corda.nodeapi.internal.serialization
|
||||
|
||||
import net.corda.core.internal.LazyPool
|
||||
@ -10,9 +11,9 @@ import java.nio.ByteBuffer
|
||||
import kotlin.math.min
|
||||
|
||||
internal val serializeOutputStreamPool = LazyPool(
|
||||
clear = ByteBufferOutputStream::reset,
|
||||
shouldReturnToPool = { it.size() < 256 * 1024 }, // Discard if it grew too large
|
||||
newInstance = { ByteBufferOutputStream(64 * 1024) })
|
||||
clear = ByteBufferOutputStream::reset,
|
||||
shouldReturnToPool = { it.size() < 256 * 1024 }, // Discard if it grew too large
|
||||
newInstance = { ByteBufferOutputStream(64 * 1024) })
|
||||
|
||||
internal fun <T> byteArrayOutput(task: (ByteBufferOutputStream) -> T): ByteArray {
|
||||
return serializeOutputStreamPool.run { underlying ->
|
||||
|
@ -56,7 +56,7 @@ class SerializeAsTokenContextImpl(override val serviceHub: ServiceHub, init: Ser
|
||||
if (className !in classNameToSingleton) {
|
||||
// Only allowable if we are in SerializeAsTokenContext init (readOnly == false)
|
||||
if (readOnly) {
|
||||
throw UnsupportedOperationException("Attempt to write token for lazy registered ${className}. All tokens should be registered during context construction.")
|
||||
throw UnsupportedOperationException("Attempt to write token for lazy registered $className. All tokens should be registered during context construction.")
|
||||
}
|
||||
classNameToSingleton[className] = toBeTokenized
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ import java.lang.reflect.Type
|
||||
* [ByteArray] is automatically marshalled to/from the Proton-J wrapper, [Binary].
|
||||
*/
|
||||
class AMQPPrimitiveSerializer(clazz: Class<*>) : AMQPSerializer<Any> {
|
||||
override val typeDescriptor = Symbol.valueOf(SerializerFactory.primitiveTypeName(clazz)!!)
|
||||
override val typeDescriptor = Symbol.valueOf(SerializerFactory.primitiveTypeName(clazz)!!)!!
|
||||
override val type: Type = clazz
|
||||
|
||||
// NOOP since this is a primitive type.
|
||||
|
@ -78,6 +78,7 @@ abstract class AbstractAMQPSerializationScheme(
|
||||
val List<Cordapp>.customSerializers get() = flatMap { it.serializationCustomSerializers }.toSet()
|
||||
}
|
||||
|
||||
// Parameter "context" is unused directy but passed in by reflection. Removing it will cause failures.
|
||||
private fun registerCustomSerializers(context: SerializationContext, factory: SerializerFactory) {
|
||||
with(factory) {
|
||||
register(publicKeySerializer)
|
||||
|
@ -1,4 +1,5 @@
|
||||
@file:JvmName("AMQPSerializerFactories")
|
||||
|
||||
package net.corda.nodeapi.internal.serialization.amqp
|
||||
|
||||
import net.corda.core.serialization.SerializationContext
|
||||
|
@ -9,6 +9,7 @@
|
||||
*/
|
||||
|
||||
@file:JvmName("AMQPStreams")
|
||||
|
||||
package net.corda.nodeapi.internal.serialization.amqp
|
||||
|
||||
import net.corda.nodeapi.internal.serialization.ByteBufferInputStream
|
||||
|
@ -42,7 +42,7 @@ open class ArraySerializer(override val type: Type, factory: SerializerFactory)
|
||||
"${type.componentType().typeName}$arrayType"
|
||||
}
|
||||
|
||||
override val typeDescriptor by lazy {
|
||||
override val typeDescriptor: Symbol by lazy {
|
||||
Symbol.valueOf("$DESCRIPTOR_DOMAIN:${factory.fingerPrinter.fingerprint(type)}")
|
||||
}
|
||||
internal val elementType: Type by lazy { type.componentType() }
|
||||
@ -105,7 +105,7 @@ abstract class PrimArraySerializer(type: Type, factory: SerializerFactory) : Arr
|
||||
companion object {
|
||||
// We don't need to handle the unboxed byte type as that is coercible to a byte array, but
|
||||
// the other 7 primitive types we do
|
||||
val primTypes: Map<Type, (SerializerFactory) -> PrimArraySerializer> = mapOf(
|
||||
private val primTypes: Map<Type, (SerializerFactory) -> PrimArraySerializer> = mapOf(
|
||||
IntArray::class.java to { f -> PrimIntArraySerializer(f) },
|
||||
CharArray::class.java to { f -> PrimCharArraySerializer(f) },
|
||||
BooleanArray::class.java to { f -> PrimBooleanArraySerializer(f) },
|
||||
|
@ -24,10 +24,10 @@ import kotlin.collections.LinkedHashSet
|
||||
/**
|
||||
* Serialization / deserialization of predefined set of supported [Collection] types covering mostly [List]s and [Set]s.
|
||||
*/
|
||||
class CollectionSerializer(val declaredType: ParameterizedType, factory: SerializerFactory) : AMQPSerializer<Any> {
|
||||
class CollectionSerializer(private val declaredType: ParameterizedType, factory: SerializerFactory) : AMQPSerializer<Any> {
|
||||
override val type: Type = declaredType as? DeserializedParameterizedType
|
||||
?: DeserializedParameterizedType.make(SerializerFactory.nameForType(declaredType))
|
||||
override val typeDescriptor by lazy {
|
||||
override val typeDescriptor: Symbol by lazy {
|
||||
Symbol.valueOf("$DESCRIPTOR_DOMAIN:${factory.fingerPrinter.fingerprint(type)}")
|
||||
}
|
||||
|
||||
@ -63,10 +63,8 @@ class CollectionSerializer(val declaredType: ParameterizedType, factory: Seriali
|
||||
(declaredType as? ParameterizedType)
|
||||
?: DeserializedParameterizedType(collectionClass, arrayOf(SerializerFactory.AnyType))
|
||||
|
||||
|
||||
private fun findMostSuitableCollectionType(actualClass: Class<*>): Class<out Collection<*>> =
|
||||
supportedTypes.keys.findLast { it.isAssignableFrom(actualClass) }!!
|
||||
|
||||
}
|
||||
|
||||
private val concreteBuilder: (List<*>) -> Collection<*> = findConcreteType(declaredType.rawType as Class<*>)
|
||||
|
@ -69,7 +69,7 @@ class CorDappCustomSerializer(
|
||||
|
||||
override val type = types[CORDAPP_TYPE]
|
||||
val proxyType = types[PROXY_TYPE]
|
||||
override val typeDescriptor = Symbol.valueOf("$DESCRIPTOR_DOMAIN:${nameForType(type)}")
|
||||
override val typeDescriptor: Symbol = Symbol.valueOf("$DESCRIPTOR_DOMAIN:${nameForType(type)}")
|
||||
val descriptor: Descriptor = Descriptor(typeDescriptor)
|
||||
private val proxySerializer: ObjectSerializer by lazy { ObjectSerializer(proxyType, factory) }
|
||||
|
||||
|
@ -74,7 +74,7 @@ abstract class CustomSerializer<T : Any> : AMQPSerializer<T>, SerializerFor {
|
||||
|
||||
override fun isSerializerFor(clazz: Class<*>): Boolean = clazz == this.clazz
|
||||
override val type: Type get() = clazz
|
||||
override val typeDescriptor by lazy {
|
||||
override val typeDescriptor: Symbol by lazy {
|
||||
Symbol.valueOf("$DESCRIPTOR_DOMAIN:${SerializerFingerPrinter().fingerprintForDescriptors(superClassSerializer.typeDescriptor.toString(), nameForType(clazz))}")
|
||||
}
|
||||
private val typeNotation: TypeNotation = RestrictedType(
|
||||
@ -110,7 +110,7 @@ abstract class CustomSerializer<T : Any> : AMQPSerializer<T>, SerializerFor {
|
||||
*/
|
||||
abstract class CustomSerializerImp<T : Any>(protected val clazz: Class<T>, protected val withInheritance: Boolean) : CustomSerializer<T>() {
|
||||
override val type: Type get() = clazz
|
||||
override val typeDescriptor = Symbol.valueOf("$DESCRIPTOR_DOMAIN:${nameForType(clazz)}")
|
||||
override val typeDescriptor: Symbol = Symbol.valueOf("$DESCRIPTOR_DOMAIN:${nameForType(clazz)}")
|
||||
override fun writeClassInfo(output: SerializationOutput) {}
|
||||
override val descriptor: Descriptor = Descriptor(typeDescriptor)
|
||||
override fun isSerializerFor(clazz: Class<*>): Boolean = if (withInheritance) this.clazz.isAssignableFrom(clazz) else this.clazz == clazz
|
||||
|
@ -18,7 +18,6 @@ import net.corda.core.utilities.ByteSequence
|
||||
import net.corda.nodeapi.internal.serialization.*
|
||||
import org.apache.qpid.proton.amqp.Binary
|
||||
import org.apache.qpid.proton.amqp.DescribedType
|
||||
import org.apache.qpid.proton.amqp.UnsignedByte
|
||||
import org.apache.qpid.proton.amqp.UnsignedInteger
|
||||
import org.apache.qpid.proton.codec.Data
|
||||
import java.io.InputStream
|
||||
@ -42,28 +41,6 @@ class DeserializationInput @JvmOverloads constructor(private val serializerFacto
|
||||
private val objectHistory: MutableList<Any> = mutableListOf()
|
||||
|
||||
companion object {
|
||||
private const val BYTES_NEEDED_TO_PEEK: Int = 23
|
||||
|
||||
fun peekSize(bytes: ByteArray): Int {
|
||||
// There's an 8 byte header, and then a 0 byte plus descriptor followed by constructor
|
||||
val eighth = bytes[8].toInt()
|
||||
check(eighth == 0x0) { "Expected to find a descriptor in the AMQP stream" }
|
||||
// We should always have an Envelope, so the descriptor should be a 64-bit long (0x80)
|
||||
val ninth = UnsignedByte.valueOf(bytes[9]).toInt()
|
||||
check(ninth == 0x80) { "Expected to find a ulong in the AMQP stream" }
|
||||
// Skip 8 bytes
|
||||
val eighteenth = UnsignedByte.valueOf(bytes[18]).toInt()
|
||||
check(eighteenth == 0xd0 || eighteenth == 0xc0) { "Expected to find a list8 or list32 in the AMQP stream" }
|
||||
val size = if (eighteenth == 0xc0) {
|
||||
// Next byte is size
|
||||
UnsignedByte.valueOf(bytes[19]).toInt() - 3 // Minus three as PEEK_SIZE assumes 4 byte unsigned integer.
|
||||
} else {
|
||||
// Next 4 bytes is size
|
||||
UnsignedByte.valueOf(bytes[19]).toInt().shl(24) + UnsignedByte.valueOf(bytes[20]).toInt().shl(16) + UnsignedByte.valueOf(bytes[21]).toInt().shl(8) + UnsignedByte.valueOf(bytes[22]).toInt()
|
||||
}
|
||||
return size + BYTES_NEEDED_TO_PEEK
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@Throws(NotSerializableException::class)
|
||||
fun <T> withDataBytes(byteSequence: ByteSequence, encodingWhitelist: EncodingWhitelist, task: (ByteBuffer) -> T): T {
|
||||
@ -150,13 +127,12 @@ class DeserializationInput @JvmOverloads constructor(private val serializerFacto
|
||||
envelope)
|
||||
}
|
||||
|
||||
internal fun readObjectOrNull(obj: Any?, schema: SerializationSchemas, type: Type, context: SerializationContext,
|
||||
offset: Int = 0
|
||||
internal fun readObjectOrNull(obj: Any?, schema: SerializationSchemas, type: Type, context: SerializationContext
|
||||
): Any? {
|
||||
return if (obj == null) null else readObject(obj, schema, type, context, offset)
|
||||
return if (obj == null) null else readObject(obj, schema, type, context)
|
||||
}
|
||||
|
||||
internal fun readObject(obj: Any, schemas: SerializationSchemas, type: Type, context: SerializationContext, debugIndent: Int = 0): Any =
|
||||
internal fun readObject(obj: Any, schemas: SerializationSchemas, type: Type, context: SerializationContext): Any =
|
||||
if (obj is DescribedType && ReferencedObject.DESCRIPTOR == obj.descriptor) {
|
||||
// It must be a reference to an instance that has already been read, cheaply and quickly returning it by reference.
|
||||
val objectIndex = (obj.described as UnsignedInteger).toInt()
|
||||
|
@ -38,7 +38,7 @@ class DeserializedParameterizedType(private val rawType: Class<*>, private val p
|
||||
private val _typeName: String = makeTypeName()
|
||||
|
||||
private fun makeTypeName(): String {
|
||||
val paramsJoined = params.map { it.typeName }.joinToString(", ")
|
||||
val paramsJoined = params.joinToString(", ") { it.typeName }
|
||||
return "${rawType.name}<$paramsJoined>"
|
||||
}
|
||||
|
||||
@ -155,14 +155,14 @@ class DeserializedParameterizedType(private val rawType: Class<*>, private val p
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other is ParameterizedType) {
|
||||
return if (other is ParameterizedType) {
|
||||
if (this === other) {
|
||||
return true
|
||||
true
|
||||
} else {
|
||||
return this.ownerType == other.ownerType && this.rawType == other.rawType && Arrays.equals(this.actualTypeArguments, other.actualTypeArguments)
|
||||
this.ownerType == other.ownerType && this.rawType == other.rawType && Arrays.equals(this.actualTypeArguments, other.actualTypeArguments)
|
||||
}
|
||||
} else {
|
||||
return false
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
@ -101,7 +101,7 @@ abstract class EvolutionSerializer(
|
||||
val constructorArgs = arrayOfNulls<Any?>(constructor.parameters.size)
|
||||
|
||||
constructor.parameters.withIndex().forEach {
|
||||
readersAsSerialized.get(it.value.name!!)?.apply {
|
||||
readersAsSerialized[it.value.name!!]?.apply {
|
||||
this.resultsIndex = it.index
|
||||
} ?: if (!it.value.type.isMarkedNullable) {
|
||||
throw NotSerializableException(
|
||||
|
@ -104,7 +104,7 @@ class SerializerFingerPrinter : FingerPrinter {
|
||||
// serialise the object in the first place (and thus the cache lookup fails). This is also
|
||||
// true of Any, where we need Example<A, B> and Example<?, ?> to have the same fingerprint
|
||||
return if ((type in alreadySeen)
|
||||
&& (type !is SerializerFactory.AnyType)
|
||||
&& (type !== SerializerFactory.AnyType)
|
||||
&& (type !is TypeVariable<*>)
|
||||
&& (type !is WildcardType)) {
|
||||
hasher.putUnencodedChars(ALREADY_SEEN_HASH)
|
||||
|
@ -33,7 +33,7 @@ open class ObjectSerializer(val clazz: Type, factory: SerializerFactory) : AMQPS
|
||||
private val logger = contextLogger()
|
||||
}
|
||||
|
||||
open internal val propertySerializers: PropertySerializers by lazy {
|
||||
internal open val propertySerializers: PropertySerializers by lazy {
|
||||
propertiesForSerialization(kotlinConstructor, clazz, factory)
|
||||
}
|
||||
|
||||
@ -42,12 +42,12 @@ open class ObjectSerializer(val clazz: Type, factory: SerializerFactory) : AMQPS
|
||||
private val typeName = nameForType(clazz)
|
||||
|
||||
override val typeDescriptor = Symbol.valueOf(
|
||||
"$DESCRIPTOR_DOMAIN:${factory.fingerPrinter.fingerprint(type)}")
|
||||
"$DESCRIPTOR_DOMAIN:${factory.fingerPrinter.fingerprint(type)}")!!
|
||||
|
||||
// We restrict to only those annotated or whitelisted
|
||||
private val interfaces = interfacesForSerialization(clazz, factory)
|
||||
|
||||
open internal val typeNotation: TypeNotation by lazy {
|
||||
internal open val typeNotation: TypeNotation by lazy {
|
||||
CompositeType(typeName, null, generateProvides(), Descriptor(typeDescriptor), generateFields())
|
||||
}
|
||||
|
||||
|
@ -89,7 +89,7 @@ class PrivatePropertyReader(val field: Field, parentType: Type) : PropertyReader
|
||||
// So this used to report as an error, but given we serialise exceptions all the time it
|
||||
// provides for very scary log files so move this to trace level
|
||||
loggerFor<PropertySerializer>().let { logger ->
|
||||
logger.trace("Using kotlin introspection on internal type ${field}")
|
||||
logger.trace("Using kotlin introspection on internal type $field")
|
||||
logger.trace("Unexpected internal Kotlin error", e)
|
||||
}
|
||||
true
|
||||
|
@ -134,18 +134,18 @@ open class SerializationOutput @JvmOverloads constructor(
|
||||
// assigned to them first as they will be first read from the stream on receiving end.
|
||||
// Skip for primitive types as they are too small and overhead of referencing them will be much higher than their content
|
||||
if (suitableForObjectReference(obj.javaClass)) {
|
||||
objectHistory.put(obj, objectHistory.size)
|
||||
objectHistory[obj] = objectHistory.size
|
||||
}
|
||||
} else {
|
||||
data.writeReferencedObject(ReferencedObject(retrievedRefCount))
|
||||
}
|
||||
}
|
||||
|
||||
open internal fun writeTypeNotations(vararg typeNotation: TypeNotation): Boolean {
|
||||
internal open fun writeTypeNotations(vararg typeNotation: TypeNotation): Boolean {
|
||||
return schemaHistory.addAll(typeNotation)
|
||||
}
|
||||
|
||||
open internal fun requireSerializer(type: Type) {
|
||||
internal open fun requireSerializer(type: Type) {
|
||||
if (type != SerializerFactory.AnyType && type != Object::class.java) {
|
||||
val serializer = serializerFactory.get(null, type)
|
||||
if (serializer !in serializerHistory) {
|
||||
|
@ -154,7 +154,7 @@ open class SerializerFactory(
|
||||
return if (actualClass.typeParameters.isNotEmpty()) {
|
||||
// The actual class can never have type variables resolved, due to the JVM's use of type erasure, so let's try and resolve them
|
||||
// Search for declared type in the inheritance hierarchy and then see if that fills in all the variables
|
||||
val implementationChain: List<Type>? = findPathToDeclared(actualClass, declaredType, mutableListOf<Type>())
|
||||
val implementationChain: List<Type>? = findPathToDeclared(actualClass, declaredType, mutableListOf())
|
||||
if (implementationChain != null) {
|
||||
val start = implementationChain.last()
|
||||
val rest = implementationChain.dropLast(1).drop(1)
|
||||
@ -315,6 +315,7 @@ open class SerializerFactory(
|
||||
return if (declaredSuperClass == null
|
||||
|| !customSerializer.isSerializerFor(declaredSuperClass)
|
||||
|| !customSerializer.revealSubclassesInSchema) {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
customSerializer as? AMQPSerializer<Any>
|
||||
} else {
|
||||
// Make a subclass serializer for the subclass and return that...
|
||||
|
@ -22,7 +22,7 @@ import java.lang.reflect.Type
|
||||
*/
|
||||
class SingletonSerializer(override val type: Class<*>, val singleton: Any, factory: SerializerFactory) : AMQPSerializer<Any> {
|
||||
override val typeDescriptor = Symbol.valueOf(
|
||||
"$DESCRIPTOR_DOMAIN:${factory.fingerPrinter.fingerprint(type)}")
|
||||
"$DESCRIPTOR_DOMAIN:${factory.fingerPrinter.fingerprint(type)}")!!
|
||||
|
||||
private val interfaces = interfacesForSerialization(type, factory)
|
||||
|
||||
|
@ -82,7 +82,7 @@ abstract class Transform : DescribedType {
|
||||
*/
|
||||
class UnknownTransform : Transform() {
|
||||
companion object : DescribedTypeConstructor<UnknownTransform> {
|
||||
val typeName = "UnknownTransform"
|
||||
const val typeName = "UnknownTransform"
|
||||
|
||||
override fun newInstance(obj: Any?) = UnknownTransform()
|
||||
|
||||
@ -100,7 +100,7 @@ class UnknownTransform : Transform() {
|
||||
*/
|
||||
class UnknownTestTransform(val a: Int, val b: Int, val c: Int) : Transform() {
|
||||
companion object : DescribedTypeConstructor<UnknownTestTransform> {
|
||||
val typeName = "UnknownTest"
|
||||
const val typeName = "UnknownTest"
|
||||
|
||||
override fun newInstance(obj: Any?): UnknownTestTransform {
|
||||
val described = obj as List<*>
|
||||
@ -127,7 +127,7 @@ class EnumDefaultSchemaTransform(val old: String, val new: String) : Transform()
|
||||
/**
|
||||
* Value encoded into the schema that identifies a transform as this type
|
||||
*/
|
||||
val typeName = "EnumDefault"
|
||||
const val typeName = "EnumDefault"
|
||||
|
||||
override fun newInstance(obj: Any?): EnumDefaultSchemaTransform {
|
||||
val described = obj as List<*>
|
||||
@ -164,7 +164,7 @@ class RenameSchemaTransform(val from: String, val to: String) : Transform() {
|
||||
/**
|
||||
* Value encoded into the schema that identifies a transform as this type
|
||||
*/
|
||||
val typeName = "Rename"
|
||||
const val typeName = "Rename"
|
||||
|
||||
override fun newInstance(obj: Any?): RenameSchemaTransform {
|
||||
val described = obj as List<*>
|
||||
@ -223,9 +223,7 @@ data class TransformsSchema(val types: Map<String, EnumMap<TransformTypes, Mutab
|
||||
// we're explicitly rejecting repeated annotations, whilst it's fine and we'd just
|
||||
// ignore them it feels like a good thing to alert the user to since this is
|
||||
// more than likely a typo in their code so best make it an actual error
|
||||
if (transforms.computeIfAbsent(transform.enum) { mutableListOf() }
|
||||
.filter { t == it }
|
||||
.isNotEmpty()) {
|
||||
if (transforms.computeIfAbsent(transform.enum) { mutableListOf() }.any { t == it }) {
|
||||
throw NotSerializableException(
|
||||
"Repeated unique transformation annotation of type ${t.name}")
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ package net.corda.nodeapi.internal.serialization.amqp.custom
|
||||
|
||||
import net.corda.nodeapi.internal.serialization.amqp.CustomSerializer
|
||||
import net.corda.nodeapi.internal.serialization.amqp.SerializerFactory
|
||||
import java.lang.reflect.Method
|
||||
import java.time.LocalDateTime
|
||||
import java.time.ZoneId
|
||||
import java.time.ZoneOffset
|
||||
@ -24,7 +25,7 @@ class ZonedDateTimeSerializer(factory: SerializerFactory) : CustomSerializer.Pro
|
||||
// Java deserialization of `ZonedDateTime` uses a private method. We will resolve this somewhat statically
|
||||
// so that any change to internals of `ZonedDateTime` is detected early.
|
||||
companion object {
|
||||
val ofLenient = ZonedDateTime::class.java.getDeclaredMethod("ofLenient", LocalDateTime::class.java, ZoneOffset::class.java, ZoneId::class.java)
|
||||
val ofLenient: Method = ZonedDateTime::class.java.getDeclaredMethod("ofLenient", LocalDateTime::class.java, ZoneOffset::class.java, ZoneId::class.java)
|
||||
|
||||
init {
|
||||
ofLenient.isAccessible = true
|
||||
|
@ -134,14 +134,14 @@ fun String.stripGenerics(): String = if (this.endsWith('>')) {
|
||||
this.substring(0, this.indexOf('<'))
|
||||
} else this
|
||||
|
||||
fun AMQPField.getTypeAsClass(classloader: ClassLoader) = typeStrToType[Pair(type, mandatory)] ?: when (type) {
|
||||
fun AMQPField.getTypeAsClass(classloader: ClassLoader) = (typeStrToType[Pair(type, mandatory)] ?: when (type) {
|
||||
"string" -> String::class.java
|
||||
"binary" -> ByteArray::class.java
|
||||
"*" -> if (requires.isEmpty()) Any::class.java else {
|
||||
classloader.loadClass(requires[0].stripGenerics())
|
||||
}
|
||||
else -> classloader.loadClass(type.stripGenerics())
|
||||
}
|
||||
})!!
|
||||
|
||||
fun AMQPField.validateType(classloader: ClassLoader) =
|
||||
when (type) {
|
||||
|
@ -16,7 +16,7 @@ import com.esotericsoftware.kryo.serializers.ClosureSerializer
|
||||
import java.io.Serializable
|
||||
|
||||
object CordaClosureSerializer : ClosureSerializer() {
|
||||
val ERROR_MESSAGE = "Unable to serialize Java Lambda expression, unless explicitly declared e.g., Runnable r = (Runnable & Serializable) () -> System.out.println(\"Hello world!\");"
|
||||
const val ERROR_MESSAGE = "Unable to serialize Java Lambda expression, unless explicitly declared e.g., Runnable r = (Runnable & Serializable) () -> System.out.println(\"Hello world!\");"
|
||||
|
||||
override fun write(kryo: Kryo, output: Output, target: Any) {
|
||||
if (!isSerializable(target)) {
|
||||
@ -31,7 +31,7 @@ object CordaClosureSerializer : ClosureSerializer() {
|
||||
}
|
||||
|
||||
object CordaClosureBlacklistSerializer : ClosureSerializer() {
|
||||
val ERROR_MESSAGE = "Java 8 Lambda expressions are not supported for serialization."
|
||||
const val ERROR_MESSAGE = "Java 8 Lambda expressions are not supported for serialization."
|
||||
|
||||
override fun write(kryo: Kryo, output: Output, target: Any) {
|
||||
throw IllegalArgumentException(ERROR_MESSAGE)
|
||||
|
@ -226,6 +226,7 @@ object DefaultKryoCustomizer {
|
||||
if (kryo.serializationContext() != null) {
|
||||
val attachmentHash = SecureHash.SHA256(input.readBytes(32))
|
||||
val contract = input.readString()
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val additionalContracts = kryo.readClassAndObject(input) as Set<ContractClassName>
|
||||
val uploader = input.readString()
|
||||
val context = kryo.serializationContext()!!
|
||||
@ -243,6 +244,7 @@ object DefaultKryoCustomizer {
|
||||
} else {
|
||||
val attachment = GeneratedAttachment(input.readBytesWithLength())
|
||||
val contract = input.readString()
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val additionalContracts = kryo.readClassAndObject(input) as Set<ContractClassName>
|
||||
val uploader = input.readString()
|
||||
return ContractAttachment(attachment, contract, additionalContracts, uploader)
|
||||
|
@ -9,6 +9,7 @@
|
||||
*/
|
||||
|
||||
@file:JvmName("KryoStreams")
|
||||
|
||||
package net.corda.nodeapi.internal.serialization.kryo
|
||||
|
||||
import com.esotericsoftware.kryo.io.Input
|
||||
|
@ -53,7 +53,7 @@ public final class ForbiddenLambdaSerializationTests {
|
||||
assertThat(throwable).isNotNull();
|
||||
assertThat(throwable).isInstanceOf(IllegalArgumentException.class);
|
||||
if (ctx != SerializationContext.UseCase.RPCServer && ctx != SerializationContext.UseCase.Storage) {
|
||||
assertThat(throwable).hasMessage(CordaClosureBlacklistSerializer.INSTANCE.getERROR_MESSAGE());
|
||||
assertThat(throwable).hasMessage(CordaClosureBlacklistSerializer.ERROR_MESSAGE);
|
||||
} else {
|
||||
assertThat(throwable).hasMessageContaining("RPC not allowed to deserialise internal classes");
|
||||
}
|
||||
@ -75,7 +75,7 @@ public final class ForbiddenLambdaSerializationTests {
|
||||
assertThat(throwable).isInstanceOf(IllegalArgumentException.class);
|
||||
assertThat(throwable).isInstanceOf(IllegalArgumentException.class);
|
||||
if (ctx != SerializationContext.UseCase.RPCServer && ctx != SerializationContext.UseCase.Storage) {
|
||||
assertThat(throwable).hasMessage(CordaClosureBlacklistSerializer.INSTANCE.getERROR_MESSAGE());
|
||||
assertThat(throwable).hasMessage(CordaClosureBlacklistSerializer.ERROR_MESSAGE);
|
||||
} else {
|
||||
assertThat(throwable).hasMessageContaining("RPC not allowed to deserialise internal classes");
|
||||
}
|
||||
|
@ -61,7 +61,7 @@ public final class LambdaCheckpointSerializationTest {
|
||||
|
||||
assertThat(throwable).isNotNull();
|
||||
assertThat(throwable).isInstanceOf(IllegalArgumentException.class);
|
||||
assertThat(throwable).hasMessage(CordaClosureSerializer.INSTANCE.getERROR_MESSAGE());
|
||||
assertThat(throwable).hasMessage(CordaClosureSerializer.ERROR_MESSAGE);
|
||||
}
|
||||
|
||||
private <T> SerializedBytes<T> serialize(final T target) {
|
||||
|
@ -12,7 +12,6 @@ package net.corda.nodeapi.internal.serialization.amqp;
|
||||
|
||||
import net.corda.nodeapi.internal.serialization.AllWhitelist;
|
||||
import net.corda.nodeapi.internal.serialization.amqp.testutils.TestSerializationContext;
|
||||
import net.corda.nodeapi.internal.serialization.amqp.testutils.TestSerializationContextKt;
|
||||
import org.assertj.core.api.Assertions;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
@ -1,7 +1,6 @@
|
||||
package net.corda.nodeapi.internal.serialization.amqp;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import kotlin.Suppress;
|
||||
import net.corda.core.contracts.ContractState;
|
||||
import net.corda.core.identity.AbstractParty;
|
||||
import net.corda.core.serialization.SerializedBytes;
|
||||
|
@ -10,22 +10,17 @@
|
||||
|
||||
package net.corda.nodeapi.internal.serialization.amqp;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import net.corda.core.contracts.ContractState;
|
||||
import net.corda.core.identity.AbstractParty;
|
||||
import net.corda.core.serialization.ConstructorForDeserialization;
|
||||
import net.corda.nodeapi.internal.serialization.AllWhitelist;
|
||||
import net.corda.core.serialization.SerializedBytes;
|
||||
import net.corda.nodeapi.internal.serialization.amqp.testutils.TestSerializationContext;
|
||||
import org.apache.qpid.proton.codec.DecoderImpl;
|
||||
import org.apache.qpid.proton.codec.EncoderImpl;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.NotSerializableException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
@ -11,7 +11,7 @@ import java.util.Map;
|
||||
|
||||
public class TestSerializationContext {
|
||||
|
||||
static private Map<Object, Object> serializationProperties = new HashMap<Object, Object>();
|
||||
private static Map<Object, Object> serializationProperties = new HashMap<>();
|
||||
|
||||
public static SerializationContext testSerializationContext = new SerializationContextImpl(
|
||||
ByteSequence.of(new byte[] { 'c', 'o', 'r', 'd', 'a', (byte)0, (byte)0, (byte)1}),
|
||||
|
@ -46,7 +46,7 @@ class AttachmentsClassLoaderStaticContractTests {
|
||||
|
||||
class AttachmentDummyContract : Contract {
|
||||
companion object {
|
||||
private val ATTACHMENT_PROGRAM_ID = "net.corda.nodeapi.internal.AttachmentsClassLoaderStaticContractTests\$AttachmentDummyContract"
|
||||
private const val ATTACHMENT_PROGRAM_ID = "net.corda.nodeapi.internal.AttachmentsClassLoaderStaticContractTests\$AttachmentDummyContract"
|
||||
}
|
||||
|
||||
data class State(val magicNumber: Int = 0) : ContractState {
|
||||
|
@ -20,7 +20,12 @@ import net.corda.core.internal.declaredField
|
||||
import net.corda.core.internal.toWireTransaction
|
||||
import net.corda.core.node.ServiceHub
|
||||
import net.corda.core.node.services.AttachmentStorage
|
||||
import net.corda.core.serialization.*
|
||||
import net.corda.core.serialization.CordaSerializable
|
||||
import net.corda.core.serialization.MissingAttachmentsException
|
||||
import net.corda.core.serialization.SerializationContext
|
||||
import net.corda.core.serialization.SerializationFactory
|
||||
import net.corda.core.serialization.deserialize
|
||||
import net.corda.core.serialization.serialize
|
||||
import net.corda.core.utilities.ByteSequence
|
||||
import net.corda.core.utilities.OpaqueBytes
|
||||
import net.corda.node.internal.cordapp.CordappLoader
|
||||
@ -38,10 +43,12 @@ import net.corda.testing.internal.kryoSpecific
|
||||
import net.corda.testing.internal.rigorousMock
|
||||
import net.corda.testing.services.MockAttachmentStorage
|
||||
import org.apache.commons.io.IOUtils
|
||||
import org.junit.Assert.*
|
||||
import org.junit.Assert.assertArrayEquals
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertNotNull
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import java.io.ByteArrayInputStream
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.net.URL
|
||||
import java.net.URLClassLoader
|
||||
@ -135,6 +142,7 @@ class AttachmentsClassLoaderTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
@Suppress("DEPRECATION")
|
||||
fun `test overlapping file exception`() {
|
||||
val storage = attachments
|
||||
val att0 = attachmentId
|
||||
@ -147,7 +155,8 @@ class AttachmentsClassLoaderTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `basic`() {
|
||||
@Suppress("DEPRECATION")
|
||||
fun basic() {
|
||||
val storage = attachments
|
||||
val att0 = attachmentId
|
||||
val att1 = storage.importAttachment(fakeAttachment("file1.txt", "some data").inputStream())
|
||||
@ -159,6 +168,7 @@ class AttachmentsClassLoaderTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
@Suppress("DEPRECATION")
|
||||
fun `Check platform independent path handling in attachment jars`() {
|
||||
val storage = MockAttachmentStorage()
|
||||
|
||||
@ -179,6 +189,7 @@ class AttachmentsClassLoaderTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
@Suppress("DEPRECATION")
|
||||
fun `loading class AnotherDummyContract`() {
|
||||
val storage = attachments
|
||||
val att0 = attachmentId
|
||||
@ -200,6 +211,7 @@ class AttachmentsClassLoaderTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
@Suppress("DEPRECATION")
|
||||
fun `testing Kryo with ClassLoader (with top level class name)`() {
|
||||
val contract = createContract2Cash()
|
||||
|
||||
@ -222,6 +234,7 @@ class AttachmentsClassLoaderTests {
|
||||
class Data(val contract: Contract)
|
||||
|
||||
@Test
|
||||
@Suppress("DEPRECATION")
|
||||
fun `testing Kryo with ClassLoader (without top level class name)`() {
|
||||
val data = Data(createContract2Cash())
|
||||
|
||||
|
@ -28,7 +28,6 @@ import org.assertj.core.api.Assertions.assertThatThrownBy
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import java.security.KeyPair
|
||||
import java.security.PrivateKey
|
||||
import java.security.PublicKey
|
||||
import java.security.SignatureException
|
||||
|
||||
|
@ -28,32 +28,32 @@ import kotlin.reflect.full.primaryConstructor
|
||||
|
||||
class ConfigParsingTest {
|
||||
@Test
|
||||
fun `String`() {
|
||||
fun String() {
|
||||
testPropertyType<StringData, StringListData, String>("hello world!", "bye")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Int`() {
|
||||
fun Int() {
|
||||
testPropertyType<IntData, IntListData, Int>(1, 2)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Long`() {
|
||||
fun Long() {
|
||||
testPropertyType<LongData, LongListData, Long>(Long.MAX_VALUE, Long.MIN_VALUE)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Double`() {
|
||||
fun Double() {
|
||||
testPropertyType<DoubleData, DoubleListData, Double>(1.2, 3.4)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Boolean`() {
|
||||
fun Boolean() {
|
||||
testPropertyType<BooleanData, BooleanListData, Boolean>(true, false)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Enum`() {
|
||||
fun Enum() {
|
||||
testPropertyType<EnumData, EnumListData, TestEnum>(TestEnum.Value2, TestEnum.Value1, valuesToString = true)
|
||||
}
|
||||
|
||||
@ -66,17 +66,17 @@ class ConfigParsingTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `LocalDate`() {
|
||||
fun LocalDate() {
|
||||
testPropertyType<LocalDateData, LocalDateListData, LocalDate>(LocalDate.now(), LocalDate.now().plusDays(1), valuesToString = true)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Instant`() {
|
||||
fun Instant() {
|
||||
testPropertyType<InstantData, InstantListData, Instant>(Instant.now(), Instant.now().plusMillis(100), valuesToString = true)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `NetworkHostAndPort`() {
|
||||
fun NetworkHostAndPort() {
|
||||
testPropertyType<NetworkHostAndPortData, NetworkHostAndPortListData, NetworkHostAndPort>(
|
||||
NetworkHostAndPort("localhost", 2223),
|
||||
NetworkHostAndPort("localhost", 2225),
|
||||
@ -84,18 +84,18 @@ class ConfigParsingTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Path`() {
|
||||
fun Path() {
|
||||
val path = "tmp" / "test"
|
||||
testPropertyType<PathData, PathListData, Path>(path, path / "file", valuesToString = true)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `URL`() {
|
||||
fun URL() {
|
||||
testPropertyType<URLData, URLListData, URL>(URL("http://localhost:1234"), URL("http://localhost:1235"), valuesToString = true)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `UUID`() {
|
||||
fun UUID() {
|
||||
testPropertyType<UUIDData, UUIDListData, UUID>(UUID.randomUUID(), UUID.randomUUID(), valuesToString = true)
|
||||
}
|
||||
|
||||
@ -146,7 +146,7 @@ class ConfigParsingTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Set`() {
|
||||
fun Set() {
|
||||
val data = StringSetData(setOf("a", "b"))
|
||||
assertThat(config("values" to listOf("a", "a", "b")).parseAs<StringSetData>()).isEqualTo(data)
|
||||
assertThat(data.toConfig()).isEqualTo(config("values" to listOf("a", "b")))
|
||||
|
@ -24,7 +24,6 @@ import rx.schedulers.TestScheduler
|
||||
import java.nio.file.Path
|
||||
import java.time.Duration
|
||||
import java.util.concurrent.TimeUnit
|
||||
import kotlin.streams.toList
|
||||
|
||||
class NodeInfoFilesCopierTest {
|
||||
companion object {
|
||||
@ -123,7 +122,7 @@ class NodeInfoFilesCopierTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `clear`() {
|
||||
fun clear() {
|
||||
// Configure 2 nodes.
|
||||
nodeInfoFilesCopier.addConfig(node1RootPath)
|
||||
nodeInfoFilesCopier.addConfig(node2RootPath)
|
||||
|
@ -65,7 +65,7 @@ class MapsSerializationTest {
|
||||
val wrongPayloadType = WrongPayloadType(payload)
|
||||
assertThatThrownBy { wrongPayloadType.serialize() }
|
||||
.isInstanceOf(IllegalArgumentException::class.java).hasMessageContaining(
|
||||
"Map type class java.util.HashMap is unstable under iteration. Suggested fix: use java.util.LinkedHashMap instead.")
|
||||
"Map type class java.util.HashMap is unstable under iteration. Suggested fix: use java.util.LinkedHashMap instead.")
|
||||
}
|
||||
|
||||
@CordaSerializable
|
||||
|
@ -50,7 +50,6 @@ class PrivateKeySerializationTest(private val privateKey: PrivateKey, private va
|
||||
fun `failed with wrong UseCase`() {
|
||||
assertThatThrownBy { privateKey.serialize(context = SerializationDefaults.P2P_CONTEXT) }
|
||||
.isInstanceOf(IllegalStateException::class.java)
|
||||
.hasMessageContaining("UseCase '${P2P}' is not within")
|
||||
|
||||
.hasMessageContaining("UseCase '$P2P' is not within")
|
||||
}
|
||||
}
|
@ -407,6 +407,7 @@ class EnumEvolvabilityTests {
|
||||
val f = sf.javaClass.getDeclaredField("transformsCache")
|
||||
f.isAccessible = true
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val transformsCache = f.get(sf) as ConcurrentHashMap<String, EnumMap<TransformTypes, MutableList<Transform>>>
|
||||
|
||||
assertEquals(0, transformsCache.size)
|
||||
|
@ -25,7 +25,7 @@ class ErrorMessagesTests {
|
||||
val VERBOSE get() = false
|
||||
}
|
||||
|
||||
private fun errMsg(property:String, testname: String) =
|
||||
private fun errMsg(property: String, testname: String) =
|
||||
"Property '$property' or its getter is non public, this renders class 'class $testname\$C' unserializable -> class $testname\$C"
|
||||
|
||||
// Java allows this to be set at the class level yet Kotlin doesn't for some reason
|
||||
|
@ -17,7 +17,7 @@ import net.corda.nodeapi.internal.serialization.AllWhitelist
|
||||
import net.corda.nodeapi.internal.serialization.amqp.testutils.TestSerializationOutput
|
||||
import net.corda.nodeapi.internal.serialization.amqp.testutils.serializeAndReturnSchema
|
||||
|
||||
class FingerPrinterTesting : FingerPrinter {
|
||||
class FingerPrinterTesting : FingerPrinter {
|
||||
private var index = 0
|
||||
private val cache = mutableMapOf<Type, String>()
|
||||
|
||||
@ -39,18 +39,19 @@ class FingerPrinterTestingTests {
|
||||
companion object {
|
||||
const val VERBOSE = true
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testingTest() {
|
||||
val fpt = FingerPrinterTesting()
|
||||
assertEquals ("0", fpt.fingerprint(Integer::class.java))
|
||||
assertEquals ("1", fpt.fingerprint(String::class.java))
|
||||
assertEquals ("0", fpt.fingerprint(Integer::class.java))
|
||||
assertEquals ("1", fpt.fingerprint(String::class.java))
|
||||
assertEquals("0", fpt.fingerprint(Integer::class.java))
|
||||
assertEquals("1", fpt.fingerprint(String::class.java))
|
||||
assertEquals("0", fpt.fingerprint(Integer::class.java))
|
||||
assertEquals("1", fpt.fingerprint(String::class.java))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun worksAsReplacement() {
|
||||
data class C (val a: Int, val b: Long)
|
||||
data class C(val a: Int, val b: Long)
|
||||
|
||||
val factory = SerializerFactory(
|
||||
AllWhitelist,
|
||||
@ -60,8 +61,7 @@ class FingerPrinterTestingTests {
|
||||
|
||||
val blob = TestSerializationOutput(VERBOSE, factory).serializeAndReturnSchema(C(1, 2L))
|
||||
|
||||
assertEquals (1, blob.schema.types.size)
|
||||
assertEquals ("<descriptor name=\"net.corda:0\"/>", blob.schema.types[0].descriptor.toString())
|
||||
assertEquals(1, blob.schema.types.size)
|
||||
assertEquals("<descriptor name=\"net.corda:0\"/>", blob.schema.types[0].descriptor.toString())
|
||||
}
|
||||
|
||||
}
|
@ -31,7 +31,7 @@ class OverridePKSerializerTest {
|
||||
|
||||
override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput,
|
||||
context: SerializationContext
|
||||
) : PublicKey {
|
||||
): PublicKey {
|
||||
throw SerializerTestException("Custom read call")
|
||||
}
|
||||
|
||||
|
@ -10,9 +10,10 @@ import org.junit.Test
|
||||
class RoundTripTests {
|
||||
@Test
|
||||
fun mutableBecomesImmutable() {
|
||||
data class C(val l : MutableList<String>)
|
||||
data class C(val l: MutableList<String>)
|
||||
|
||||
val factory = testDefaultFactoryNoEvolution()
|
||||
val bytes = SerializationOutput(factory).serialize(C(mutableListOf ("a", "b", "c")))
|
||||
val bytes = SerializationOutput(factory).serialize(C(mutableListOf("a", "b", "c")))
|
||||
val newC = DeserializationInput(factory).deserialize(bytes)
|
||||
|
||||
Assertions.assertThatThrownBy {
|
||||
@ -23,15 +24,16 @@ class RoundTripTests {
|
||||
@Test
|
||||
fun mutableStillMutable() {
|
||||
class C {
|
||||
val l : MutableList<String>
|
||||
val l: MutableList<String>
|
||||
|
||||
@Suppress("Unused")
|
||||
constructor (l : MutableList<String>) {
|
||||
constructor (l: MutableList<String>) {
|
||||
this.l = l.toMutableList()
|
||||
}
|
||||
}
|
||||
|
||||
val factory = testDefaultFactoryNoEvolution()
|
||||
val bytes = SerializationOutput(factory).serialize(C(mutableListOf ("a", "b", "c")))
|
||||
val bytes = SerializationOutput(factory).serialize(C(mutableListOf("a", "b", "c")))
|
||||
val newC = DeserializationInput(factory).deserialize(bytes)
|
||||
|
||||
newC.l.add("d")
|
||||
@ -39,14 +41,14 @@ class RoundTripTests {
|
||||
|
||||
@Test
|
||||
fun mutableStillMutable2() {
|
||||
data class C (val l : MutableList<String>){
|
||||
data class C(val l: MutableList<String>) {
|
||||
@ConstructorForDeserialization
|
||||
@Suppress("Unused")
|
||||
constructor (l : Collection<String>) : this (l.toMutableList())
|
||||
constructor (l: Collection<String>) : this(l.toMutableList())
|
||||
}
|
||||
|
||||
val factory = testDefaultFactoryNoEvolution()
|
||||
val bytes = SerializationOutput(factory).serialize(C(mutableListOf ("a", "b", "c")))
|
||||
val bytes = SerializationOutput(factory).serialize(C(mutableListOf("a", "b", "c")))
|
||||
val newC = DeserializationInput(factory).deserialize(bytes)
|
||||
|
||||
newC.l.add("d")
|
||||
@ -54,10 +56,11 @@ class RoundTripTests {
|
||||
|
||||
@Test
|
||||
fun mutableBecomesImmutable4() {
|
||||
data class C(val l : List<String>)
|
||||
data class C(val l: List<String>)
|
||||
|
||||
val factory = testDefaultFactoryNoEvolution()
|
||||
val bytes = SerializationOutput(factory).serialize(C(listOf ("a", "b", "c")))
|
||||
val bytes = SerializationOutput(factory).serialize(C(listOf("a", "b", "c")))
|
||||
val newC = DeserializationInput(factory).deserialize(bytes)
|
||||
val newC2 = newC.copy (l = (newC.l + "d"))
|
||||
val newC2 = newC.copy(l = (newC.l + "d"))
|
||||
}
|
||||
}
|
@ -17,7 +17,13 @@ import com.nhaarman.mockito_kotlin.whenever
|
||||
import net.corda.client.rpc.RPCException
|
||||
import net.corda.core.CordaException
|
||||
import net.corda.core.CordaRuntimeException
|
||||
import net.corda.core.contracts.*
|
||||
import net.corda.core.contracts.Amount
|
||||
import net.corda.core.contracts.Contract
|
||||
import net.corda.core.contracts.ContractAttachment
|
||||
import net.corda.core.contracts.ContractState
|
||||
import net.corda.core.contracts.PrivacySalt
|
||||
import net.corda.core.contracts.StateRef
|
||||
import net.corda.core.contracts.TransactionState
|
||||
import net.corda.core.crypto.Crypto
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.crypto.secureRandomBytes
|
||||
@ -26,25 +32,47 @@ import net.corda.core.identity.AbstractParty
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.internal.AbstractAttachment
|
||||
import net.corda.core.internal.x500Name
|
||||
import net.corda.core.serialization.*
|
||||
import net.corda.core.serialization.ConstructorForDeserialization
|
||||
import net.corda.core.serialization.CordaSerializable
|
||||
import net.corda.core.serialization.EncodingWhitelist
|
||||
import net.corda.core.serialization.MissingAttachmentsException
|
||||
import net.corda.core.serialization.SerializationContext
|
||||
import net.corda.core.serialization.SerializationFactory
|
||||
import net.corda.core.transactions.LedgerTransaction
|
||||
import net.corda.core.utilities.OpaqueBytes
|
||||
import net.corda.nodeapi.internal.DEV_INTERMEDIATE_CA
|
||||
import net.corda.nodeapi.internal.crypto.ContentSignerBuilder
|
||||
import net.corda.nodeapi.internal.serialization.*
|
||||
import net.corda.nodeapi.internal.serialization.AllWhitelist
|
||||
import net.corda.nodeapi.internal.serialization.CordaSerializationEncoding
|
||||
import net.corda.nodeapi.internal.serialization.EmptyWhitelist
|
||||
import net.corda.nodeapi.internal.serialization.GeneratedAttachment
|
||||
import net.corda.nodeapi.internal.serialization.amqp.SerializerFactory.Companion.isPrimitive
|
||||
import net.corda.nodeapi.internal.serialization.amqp.testutils.*
|
||||
import net.corda.nodeapi.internal.serialization.carpenter.ClassCarpenterImpl
|
||||
import net.corda.nodeapi.internal.serialization.amqp.testutils.deserialize
|
||||
import net.corda.nodeapi.internal.serialization.amqp.testutils.serialize
|
||||
import net.corda.nodeapi.internal.serialization.amqp.testutils.testDefaultFactory
|
||||
import net.corda.nodeapi.internal.serialization.amqp.testutils.testDefaultFactoryNoEvolution
|
||||
import net.corda.nodeapi.internal.serialization.amqp.testutils.testSerializationContext
|
||||
import net.corda.nodeapi.internal.serialization.encodingNotPermittedFormat
|
||||
import net.corda.testing.contracts.DummyContract
|
||||
import net.corda.testing.core.BOB_NAME
|
||||
import net.corda.testing.core.SerializationEnvironmentRule
|
||||
import net.corda.testing.core.TestIdentity
|
||||
import net.corda.testing.internal.rigorousMock
|
||||
import org.apache.activemq.artemis.api.core.SimpleString
|
||||
import org.apache.qpid.proton.amqp.*
|
||||
import org.apache.qpid.proton.amqp.Decimal128
|
||||
import org.apache.qpid.proton.amqp.Decimal32
|
||||
import org.apache.qpid.proton.amqp.Decimal64
|
||||
import org.apache.qpid.proton.amqp.Symbol
|
||||
import org.apache.qpid.proton.amqp.UnsignedByte
|
||||
import org.apache.qpid.proton.amqp.UnsignedInteger
|
||||
import org.apache.qpid.proton.amqp.UnsignedLong
|
||||
import org.apache.qpid.proton.amqp.UnsignedShort
|
||||
import org.apache.qpid.proton.codec.DecoderImpl
|
||||
import org.apache.qpid.proton.codec.EncoderImpl
|
||||
import org.assertj.core.api.Assertions.*
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.assertj.core.api.Assertions.assertThatExceptionOfType
|
||||
import org.assertj.core.api.Assertions.assertThatThrownBy
|
||||
import org.assertj.core.api.Assertions.catchThrowable
|
||||
import org.bouncycastle.cert.X509v2CRLBuilder
|
||||
import org.bouncycastle.cert.jcajce.JcaX509CRLConverter
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider
|
||||
@ -60,7 +88,20 @@ import java.io.NotSerializableException
|
||||
import java.math.BigDecimal
|
||||
import java.math.BigInteger
|
||||
import java.security.cert.X509CRL
|
||||
import java.time.*
|
||||
import java.time.DayOfWeek
|
||||
import java.time.Duration
|
||||
import java.time.Instant
|
||||
import java.time.LocalDate
|
||||
import java.time.LocalDateTime
|
||||
import java.time.LocalTime
|
||||
import java.time.Month
|
||||
import java.time.MonthDay
|
||||
import java.time.OffsetDateTime
|
||||
import java.time.OffsetTime
|
||||
import java.time.Period
|
||||
import java.time.Year
|
||||
import java.time.YearMonth
|
||||
import java.time.ZonedDateTime
|
||||
import java.time.temporal.ChronoUnit
|
||||
import java.util.*
|
||||
import kotlin.reflect.full.superclasses
|
||||
@ -243,7 +284,7 @@ class SerializationOutputTests(private val compression: CordaSerializationEncodi
|
||||
}
|
||||
EncoderImpl(decoder)
|
||||
DeserializationInput.withDataBytes(bytes, encodingWhitelist) {
|
||||
decoder.setByteBuffer(it)
|
||||
decoder.byteBuffer = it
|
||||
// Check that a vanilla AMQP decoder can deserialize without schema.
|
||||
val result = decoder.readObject() as Envelope
|
||||
assertNotNull(result)
|
||||
@ -899,7 +940,7 @@ class SerializationOutputTests(private val compression: CordaSerializationEncodi
|
||||
|
||||
@Test
|
||||
fun `test generic in constructor serialize`() {
|
||||
val obj = GenericSubclass(OtherGeneric<String>())
|
||||
val obj = GenericSubclass(OtherGeneric())
|
||||
serdes(obj)
|
||||
}
|
||||
|
||||
@ -1196,7 +1237,7 @@ class SerializationOutputTests(private val compression: CordaSerializationEncodi
|
||||
factory.register(net.corda.nodeapi.internal.serialization.amqp.custom.BigDecimalSerializer)
|
||||
factory.register(net.corda.nodeapi.internal.serialization.amqp.custom.CurrencySerializer)
|
||||
|
||||
val c = C(Amount<Currency>(100, BigDecimal("1.5"), Currency.getInstance("USD")))
|
||||
val c = C(Amount(100, BigDecimal("1.5"), Currency.getInstance("USD")))
|
||||
|
||||
// were the issue not fixed we'd blow up here
|
||||
SerializationOutput(factory, compression).serialize(c)
|
||||
|
@ -17,8 +17,8 @@ val TESTING_CONTEXT = SerializationContextImpl(amqpMagic,
|
||||
|
||||
// Test factory that lets us count the number of serializer registration attempts
|
||||
class TestSerializerFactory(
|
||||
wl : ClassWhitelist,
|
||||
cl : ClassLoader
|
||||
wl: ClassWhitelist,
|
||||
cl: ClassLoader
|
||||
) : SerializerFactory(wl, cl) {
|
||||
var registerCount = 0
|
||||
|
||||
@ -83,16 +83,17 @@ class TestSerializationFactory : SerializationFactory() {
|
||||
class SerializationSchemaTests {
|
||||
@Test
|
||||
fun onlyRegisterCustomSerializersOnce() {
|
||||
@CordaSerializable data class C(val a: Int)
|
||||
@CordaSerializable
|
||||
data class C(val a: Int)
|
||||
|
||||
val c = C(1)
|
||||
val testSerializationFactory = TestSerializationFactory()
|
||||
val expectedCustomSerializerCount = 40
|
||||
|
||||
assertEquals (0, testFactory.registerCount)
|
||||
c.serialize (testSerializationFactory, TESTING_CONTEXT)
|
||||
assertEquals (expectedCustomSerializerCount, testFactory.registerCount)
|
||||
c.serialize (testSerializationFactory, TESTING_CONTEXT)
|
||||
assertEquals (expectedCustomSerializerCount, testFactory.registerCount)
|
||||
assertEquals(0, testFactory.registerCount)
|
||||
c.serialize(testSerializationFactory, TESTING_CONTEXT)
|
||||
assertEquals(expectedCustomSerializerCount, testFactory.registerCount)
|
||||
c.serialize(testSerializationFactory, TESTING_CONTEXT)
|
||||
assertEquals(expectedCustomSerializerCount, testFactory.registerCount)
|
||||
}
|
||||
}
|
@ -78,7 +78,6 @@ class StaticInitialisationOfSerializedObjectTest {
|
||||
assertEquals(1, serialisersByType.size)
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun deserializeTest() {
|
||||
data class D(val c: C2)
|
||||
@ -109,8 +108,7 @@ class StaticInitialisationOfSerializedObjectTest {
|
||||
// Version of a serializer factory that will allow the class carpenter living on the
|
||||
// factory to have a different whitelist applied to it than the factory
|
||||
class TestSerializerFactory(wl1: ClassWhitelist, wl2: ClassWhitelist) :
|
||||
SerializerFactory(wl1, ClassCarpenterImpl(ClassLoader.getSystemClassLoader(), wl2)) {
|
||||
}
|
||||
SerializerFactory(wl1, ClassCarpenterImpl(ClassLoader.getSystemClassLoader(), wl2))
|
||||
|
||||
// This time have the serialization factory and the carpenter use different whitelists
|
||||
@Test
|
||||
|
@ -27,7 +27,7 @@ class TestClassLoader(private var exclude: List<String>) : ClassLoader() {
|
||||
}
|
||||
|
||||
interface TestInterface {
|
||||
fun runThing() : Int
|
||||
fun runThing(): Int
|
||||
}
|
||||
|
||||
// Create a custom serialization factory where we need to be able to both specify a carpenter
|
||||
@ -63,10 +63,10 @@ class CarpenterExceptionTests {
|
||||
val a1 = CLA().loadClass(A::class.java.name)
|
||||
val a2 = CLB().loadClass(A::class.java.name)
|
||||
|
||||
assertTrue (TypeToken.of(a1).isSubtypeOf(a2))
|
||||
assertTrue (a1 is Type)
|
||||
assertTrue (a2 is Type)
|
||||
assertTrue (a3 is Type)
|
||||
assertTrue(TypeToken.of(a1).isSubtypeOf(a2))
|
||||
assertTrue(a1 is Type)
|
||||
assertTrue(a2 is Type)
|
||||
assertTrue(a3 is Type)
|
||||
assertEquals(a1, a2)
|
||||
assertEquals(a1, a3)
|
||||
assertEquals(a2, a3)
|
||||
@ -74,11 +74,11 @@ class CarpenterExceptionTests {
|
||||
|
||||
@Test
|
||||
fun carpenterExceptionRethrownAsNotSerializableException() {
|
||||
data class C2 (val i: Int) : TestInterface {
|
||||
data class C2(val i: Int) : TestInterface {
|
||||
override fun runThing() = 1
|
||||
}
|
||||
|
||||
data class C1 (val i: Int, val c: C2)
|
||||
data class C1(val i: Int, val c: C2)
|
||||
|
||||
// We need two factories to ensure when we deserialize the blob we don't just use the serializer
|
||||
// we built to serialise things
|
||||
|
@ -50,7 +50,7 @@ fun Schema.mangleNames(names: List<String>): Schema {
|
||||
* rather than have it create its own
|
||||
*/
|
||||
class SerializerFactoryExternalCarpenter(classCarpenter: ClassCarpenter)
|
||||
: SerializerFactory (classCarpenter.whitelist, classCarpenter)
|
||||
: SerializerFactory(classCarpenter.whitelist, classCarpenter)
|
||||
|
||||
open class AmqpCarpenterBase(whitelist: ClassWhitelist) {
|
||||
var cc = ClassCarpenterImpl(whitelist = whitelist)
|
||||
|
@ -63,7 +63,8 @@ class ClassCarpenterWhitelistTest {
|
||||
// it's marked as CordaSerializable
|
||||
@Test
|
||||
fun notWhitelistedButAnnotated() {
|
||||
@CordaSerializable data class A(val a: Int)
|
||||
@CordaSerializable
|
||||
data class A(val a: Int)
|
||||
|
||||
class WL : ClassWhitelist {
|
||||
override fun hasListed(type: Class<*>) = false
|
||||
|
@ -108,6 +108,5 @@ class MultiMemberCompositeSchemaToClassCarpenterTests : AmqpCarpenterBase(AllWhi
|
||||
assertEquals(pinochio.getMethod("getA").invoke(p), amqpObj.a)
|
||||
assertEquals(pinochio.getMethod("getB").invoke(p), amqpObj.b)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user