From 3d577e5eedb200a05423ba0ef14cb7d741b3b2bb Mon Sep 17 00:00:00 2001 From: Ross Nicoll Date: Thu, 14 Sep 2017 17:34:01 +0100 Subject: [PATCH] CORDA-499: Remove further `Kt` classes (#1489) * Change how NetworkHostAndPort is parsed Change from using a global extension function to parse NetworkHostAndPort strings, into using a function on the companion object. This is a lot easier for Java interop and matches the common style used elsewhere both in Corda and in Java libraries. * Move JAR extraction into new utils file * Move path verification function to ArtemisUtils Move path verification function "requireOnDefaultFileSystem()" to new ArtemisUtils.kt file, as this makes more sense from a Java interop perspective. * Add JvmName to AMQPSchemaExtensions * Add JvmName to AMQPSerializationScheme * Revert "Move JAR extraction into new utils file" This reverts commit 1f0f41909b68ff21cc24b5efd6a1a360393a0a14. * Reformat code * Run formatter on ArtemisUtils --- .../core/utilities/NetworkHostAndPort.kt | 45 ++++++++++--------- .../core/utilities/NetworkHostAndPortTest.kt | 20 ++++----- .../net/corda/nodeapi/ArtemisTcpTransport.kt | 10 +---- .../kotlin/net/corda/nodeapi/ArtemisUtils.kt | 14 ++++++ .../corda/nodeapi/config/ConfigUtilities.kt | 5 +-- .../serialization/AMQPSerializationScheme.kt | 2 + .../carpenter/AMQPSchemaExtensions.kt | 4 +- .../kotlin/net/corda/node/internal/Node.kt | 2 +- .../messaging/ArtemisMessagingServer.kt | 2 +- .../kotlin/net/corda/testing/RPCDriver.kt | 3 +- .../kotlin/net/corda/testing/driver/Driver.kt | 2 +- .../corda/demobench/model/InstallFactory.kt | 4 +- 12 files changed, 64 insertions(+), 49 deletions(-) create mode 100644 node-api/src/main/kotlin/net/corda/nodeapi/ArtemisUtils.kt diff --git a/core/src/main/kotlin/net/corda/core/utilities/NetworkHostAndPort.kt b/core/src/main/kotlin/net/corda/core/utilities/NetworkHostAndPort.kt index 3472682639..8866e79e1b 100644 --- a/core/src/main/kotlin/net/corda/core/utilities/NetworkHostAndPort.kt +++ b/core/src/main/kotlin/net/corda/core/utilities/NetworkHostAndPort.kt @@ -2,38 +2,43 @@ package net.corda.core.utilities import net.corda.core.serialization.CordaSerializable import java.net.URI +import java.net.URISyntaxException /** - * Tuple of host and port. Use [parseNetworkHostAndPort] on untrusted data. + * Tuple of host and port. Use [NetworkHostAndPort.parse] on untrusted data. * @param host a hostname or IP address. IPv6 addresses must not be enclosed in square brackets. * @param port a valid port number. */ @CordaSerializable data class NetworkHostAndPort(val host: String, val port: Int) { companion object { - internal val invalidPortFormat = "Invalid port: %s" - internal val unparseableAddressFormat = "Unparseable address: %s" - internal val missingPortFormat = "Missing port: %s" + internal const val INVALID_PORT_FORMAT = "Invalid port: %s" + internal const val UNPARSEABLE_ADDRESS_FORMAT = "Unparseable address: %s" + internal const val MISSING_PORT_FORMAT = "Missing port: %s" + private val bracketedHost = "\\[(.*)]".toRegex() + + /** + * Parses a string of the form host:port into a [NetworkHostAndPort]. + * The host part may be a hostname or IP address. If it's an IPv6 address, it must be enclosed in square brackets. + * Note this does not parse the toString of a resolved [java.net.InetSocketAddress], which is of a host/IP:port form. + * @throws IllegalArgumentException if the port is missing, the string is garbage, or the NetworkHostAndPort constructor rejected the parsed parts. + */ + @JvmStatic + fun parse(str: String): NetworkHostAndPort { + val uri = try { + URI(null, str, null, null, null) + } catch(ex: URISyntaxException) { + throw IllegalArgumentException("Host and port syntax is invalid, expected host:port") + } + require(uri.host != null) { NetworkHostAndPort.UNPARSEABLE_ADDRESS_FORMAT.format(str) } + require(uri.port != -1) { NetworkHostAndPort.MISSING_PORT_FORMAT.format(str) } + return NetworkHostAndPort(bracketedHost.matchEntire(uri.host)?.groupValues?.get(1) ?: uri.host, uri.port) + } } init { - require(port in (0..0xffff)) { invalidPortFormat.format(port) } + require(port in (0..0xffff)) { INVALID_PORT_FORMAT.format(port) } } override fun toString() = if (':' in host) "[$host]:$port" else "$host:$port" } - -/** - * Parses a string of the form host:port into a [NetworkHostAndPort]. - * The host part may be a hostname or IP address. If it's an IPv6 address, it must be enclosed in square brackets. - * Note this does not parse the toString of a resolved [java.net.InetSocketAddress], which is of a host/IP:port form. - * @throws IllegalArgumentException if the port is missing, the string is garbage, or the NetworkHostAndPort constructor rejected the parsed parts. - */ -fun String.parseNetworkHostAndPort() = run { - val uri = URI(null, this, null, null, null) - require(uri.host != null) { NetworkHostAndPort.unparseableAddressFormat.format(this) } - require(uri.port != -1) { NetworkHostAndPort.missingPortFormat.format(this) } - NetworkHostAndPort(bracketedHost.matchEntire(uri.host)?.groupValues?.get(1) ?: uri.host, uri.port) -} - -private val bracketedHost = "\\[(.*)]".toRegex() diff --git a/core/src/test/kotlin/net/corda/core/utilities/NetworkHostAndPortTest.kt b/core/src/test/kotlin/net/corda/core/utilities/NetworkHostAndPortTest.kt index 925773e2b3..b9357530ca 100644 --- a/core/src/test/kotlin/net/corda/core/utilities/NetworkHostAndPortTest.kt +++ b/core/src/test/kotlin/net/corda/core/utilities/NetworkHostAndPortTest.kt @@ -24,7 +24,7 @@ class NetworkHostAndPortTest { listOf(65536, -1).forEach { assertThatThrownBy { NetworkHostAndPort("example.com", it) - }.isInstanceOf(IllegalArgumentException::class.java).hasMessage(NetworkHostAndPort.invalidPortFormat.format(it)) + }.isInstanceOf(IllegalArgumentException::class.java).hasMessage(NetworkHostAndPort.INVALID_PORT_FORMAT.format(it)) } } @@ -41,20 +41,20 @@ class NetworkHostAndPortTest { @Test fun `parseNetworkHostAndPort works`() { - assertEquals(NetworkHostAndPort("example.com", 1234), "example.com:1234".parseNetworkHostAndPort()) - assertEquals(NetworkHostAndPort("example.com", 65535), "example.com:65535".parseNetworkHostAndPort()) - assertEquals(NetworkHostAndPort("1.2.3.4", 1234), "1.2.3.4:1234".parseNetworkHostAndPort()) - assertEquals(NetworkHostAndPort("::1", 1234), "[::1]:1234".parseNetworkHostAndPort()) - assertEquals(NetworkHostAndPort("0:0:0:0:0:0:0:1", 1234), "[0:0:0:0:0:0:0:1]:1234".parseNetworkHostAndPort()) + assertEquals(NetworkHostAndPort("example.com", 1234), NetworkHostAndPort.parse("example.com:1234")) + assertEquals(NetworkHostAndPort("example.com", 65535), NetworkHostAndPort.parse("example.com:65535")) + assertEquals(NetworkHostAndPort("1.2.3.4", 1234), NetworkHostAndPort.parse("1.2.3.4:1234")) + assertEquals(NetworkHostAndPort("::1", 1234), NetworkHostAndPort.parse("[::1]:1234")) + assertEquals(NetworkHostAndPort("0:0:0:0:0:0:0:1", 1234), NetworkHostAndPort.parse("[0:0:0:0:0:0:0:1]:1234")) listOf("0:0:0:0:0:0:0:1:1234", ":1234", "example.com:-1").forEach { assertThatThrownBy { - it.parseNetworkHostAndPort() - }.isInstanceOf(IllegalArgumentException::class.java).hasMessage(NetworkHostAndPort.unparseableAddressFormat.format(it)) + NetworkHostAndPort.parse(it) + }.isInstanceOf(IllegalArgumentException::class.java).hasMessage(NetworkHostAndPort.UNPARSEABLE_ADDRESS_FORMAT.format(it)) } listOf("example.com:", "example.com").forEach { assertThatThrownBy { - it.parseNetworkHostAndPort() - }.isInstanceOf(IllegalArgumentException::class.java).hasMessage(NetworkHostAndPort.missingPortFormat.format(it)) + NetworkHostAndPort.parse(it) + }.isInstanceOf(IllegalArgumentException::class.java).hasMessage(NetworkHostAndPort.MISSING_PORT_FORMAT.format(it)) } } } diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/ArtemisTcpTransport.kt b/node-api/src/main/kotlin/net/corda/nodeapi/ArtemisTcpTransport.kt index 4e2f5aa690..60dfa2f17d 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/ArtemisTcpTransport.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/ArtemisTcpTransport.kt @@ -7,8 +7,6 @@ import org.apache.activemq.artemis.api.core.TransportConfiguration import org.apache.activemq.artemis.core.remoting.impl.netty.NettyConnectorFactory import org.apache.activemq.artemis.core.remoting.impl.netty.TransportConstants import org.bouncycastle.asn1.x500.X500Name -import java.nio.file.FileSystems -import java.nio.file.Path sealed class ConnectionDirection { data class Inbound(val acceptorFactoryClassName: String) : ConnectionDirection() @@ -54,8 +52,8 @@ class ArtemisTcpTransport { ) if (config != null && enableSSL) { - config.sslKeystore.expectedOnDefaultFileSystem() - config.trustStoreFile.expectedOnDefaultFileSystem() + config.sslKeystore.requireOnDefaultFileSystem() + config.trustStoreFile.requireOnDefaultFileSystem() val tlsOptions = mapOf( // Enable TLS transport layer with client certs and restrict to at least SHA256 in handshake // and AES encryption @@ -81,7 +79,3 @@ class ArtemisTcpTransport { } } } - -fun Path.expectedOnDefaultFileSystem() { - require(fileSystem == FileSystems.getDefault()) { "Artemis only uses the default file system" } -} diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/ArtemisUtils.kt b/node-api/src/main/kotlin/net/corda/nodeapi/ArtemisUtils.kt new file mode 100644 index 0000000000..644fdb363a --- /dev/null +++ b/node-api/src/main/kotlin/net/corda/nodeapi/ArtemisUtils.kt @@ -0,0 +1,14 @@ +@file:JvmName("ArtemisUtils") + +package net.corda.nodeapi + +import java.nio.file.FileSystems +import java.nio.file.Path + +/** + * Require that the [Path] is on a default file system, and therefore is one that Artemis is willing to use. + * @throws IllegalArgumentException if the path is not on a default file system. + */ +fun Path.requireOnDefaultFileSystem() { + require(fileSystem == FileSystems.getDefault()) { "Artemis only uses the default file system" } +} diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/config/ConfigUtilities.kt b/node-api/src/main/kotlin/net/corda/nodeapi/config/ConfigUtilities.kt index 4f53babacb..052a361267 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/config/ConfigUtilities.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/config/ConfigUtilities.kt @@ -6,7 +6,6 @@ import com.typesafe.config.ConfigUtil import net.corda.core.identity.CordaX500Name import net.corda.core.internal.noneOrSingle import net.corda.core.utilities.NetworkHostAndPort -import net.corda.core.utilities.parseNetworkHostAndPort import org.slf4j.LoggerFactory import java.net.Proxy import java.net.URL @@ -69,7 +68,7 @@ private fun Config.getSingleValue(path: String, type: KType): Any? { Boolean::class -> getBoolean(path) LocalDate::class -> LocalDate.parse(getString(path)) Instant::class -> Instant.parse(getString(path)) - NetworkHostAndPort::class -> getString(path).parseNetworkHostAndPort() + NetworkHostAndPort::class -> NetworkHostAndPort.parse(getString(path)) Path::class -> Paths.get(getString(path)) URL::class -> URL(getString(path)) Properties::class -> getConfig(path).toProperties() @@ -97,7 +96,7 @@ private fun Config.getCollectionValue(path: String, type: KType): Collection getBooleanList(path) LocalDate::class -> getStringList(path).map(LocalDate::parse) Instant::class -> getStringList(path).map(Instant::parse) - NetworkHostAndPort::class -> getStringList(path).map { it.parseNetworkHostAndPort() } + NetworkHostAndPort::class -> getStringList(path).map(NetworkHostAndPort.Companion::parse) Path::class -> getStringList(path).map { Paths.get(it) } URL::class -> getStringList(path).map(::URL) CordaX500Name::class -> getStringList(path).map(CordaX500Name.Companion::parse) diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/AMQPSerializationScheme.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/AMQPSerializationScheme.kt index 9888dfc6ab..8f09baa998 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/AMQPSerializationScheme.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/AMQPSerializationScheme.kt @@ -1,3 +1,5 @@ +@file:JvmName("AMQPSerializationScheme") + package net.corda.nodeapi.internal.serialization import net.corda.core.node.CordaPluginRegistry diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/carpenter/AMQPSchemaExtensions.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/carpenter/AMQPSchemaExtensions.kt index 613e180a9d..611dbb9788 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/carpenter/AMQPSchemaExtensions.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/carpenter/AMQPSchemaExtensions.kt @@ -1,3 +1,5 @@ +@file:JvmName("AMQPSchemaExtensions") + package net.corda.nodeapi.internal.serialization.carpenter import net.corda.nodeapi.internal.serialization.amqp.CompositeType @@ -5,7 +7,7 @@ import net.corda.nodeapi.internal.serialization.amqp.RestrictedType import net.corda.nodeapi.internal.serialization.amqp.Field as AMQPField import net.corda.nodeapi.internal.serialization.amqp.Schema as AMQPSchema -fun AMQPSchema.carpenterSchema(classloader: ClassLoader) : CarpenterMetaSchema { +fun AMQPSchema.carpenterSchema(classloader: ClassLoader): CarpenterMetaSchema { val rtn = CarpenterMetaSchema.newInstance() types.filterIsInstance().forEach { diff --git a/node/src/main/kotlin/net/corda/node/internal/Node.kt b/node/src/main/kotlin/net/corda/node/internal/Node.kt index bf3fba166e..6acf954f2e 100644 --- a/node/src/main/kotlin/net/corda/node/internal/Node.kt +++ b/node/src/main/kotlin/net/corda/node/internal/Node.kt @@ -242,7 +242,7 @@ open class Node(override val configuration: FullNodeConfiguration, session.deleteQueue(queueName) clientFactory.close() - return publicHostAndPort.removePrefix("/").parseNetworkHostAndPort().host + return NetworkHostAndPort.parse(publicHostAndPort.removePrefix("/")).host } override fun startMessagingService(rpcOps: RPCOps) { diff --git a/node/src/main/kotlin/net/corda/node/services/messaging/ArtemisMessagingServer.kt b/node/src/main/kotlin/net/corda/node/services/messaging/ArtemisMessagingServer.kt index be4163342a..2ad20ce89e 100644 --- a/node/src/main/kotlin/net/corda/node/services/messaging/ArtemisMessagingServer.kt +++ b/node/src/main/kotlin/net/corda/node/services/messaging/ArtemisMessagingServer.kt @@ -119,7 +119,7 @@ class ArtemisMessagingServer(override val config: NodeConfiguration, private val nodeRunsNetworkMapService = config.networkMapService == null init { - config.baseDirectory.expectedOnDefaultFileSystem() + config.baseDirectory.requireOnDefaultFileSystem() } /** diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/RPCDriver.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/RPCDriver.kt index 5e64be47d5..dd5c6c081a 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/RPCDriver.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/RPCDriver.kt @@ -12,7 +12,6 @@ import net.corda.core.internal.concurrent.map import net.corda.core.internal.div import net.corda.core.messaging.RPCOps import net.corda.core.utilities.NetworkHostAndPort -import net.corda.core.utilities.parseNetworkHostAndPort import net.corda.node.services.RPCUserService import net.corda.node.services.messaging.ArtemisMessagingServer import net.corda.node.services.messaging.RPCServer @@ -508,7 +507,7 @@ class RandomRpcUser { require(args.size == 4) @Suppress("UNCHECKED_CAST") val rpcClass = Class.forName(args[0]) as Class - val hostAndPort = args[1].parseNetworkHostAndPort() + val hostAndPort = NetworkHostAndPort.parse(args[1]) val username = args[2] val password = args[3] CordaRPCClient.initialiseSerialization() diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt index a2942e56db..ef6d473d78 100644 --- a/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt +++ b/testing/node-driver/src/main/kotlin/net/corda/testing/driver/Driver.kt @@ -653,7 +653,7 @@ class DriverDSL( is NetworkMapStartStrategy.Nominated -> { serviceConfig(networkMapCandidates.filter { it.name == legalName.toString() - }.single().config.getString("p2pAddress").parseNetworkHostAndPort()).let { + }.single().config.getString("p2pAddress").let(NetworkHostAndPort.Companion::parse)).let { { nodeName: CordaX500Name -> if (nodeName == legalName) null else it } } } diff --git a/tools/demobench/src/main/kotlin/net/corda/demobench/model/InstallFactory.kt b/tools/demobench/src/main/kotlin/net/corda/demobench/model/InstallFactory.kt index f6a2c46f71..ae76a18c06 100644 --- a/tools/demobench/src/main/kotlin/net/corda/demobench/model/InstallFactory.kt +++ b/tools/demobench/src/main/kotlin/net/corda/demobench/model/InstallFactory.kt @@ -4,7 +4,7 @@ import com.typesafe.config.Config import net.corda.core.identity.CordaX500Name import net.corda.core.node.services.ServiceInfo import net.corda.core.node.services.ServiceType -import net.corda.core.utilities.parseNetworkHostAndPort +import net.corda.core.utilities.NetworkHostAndPort import tornadofx.* import java.io.IOException import java.nio.file.Files @@ -48,7 +48,7 @@ class InstallFactory : Controller() { private fun Config.parsePort(path: String): Int { val address = this.getString(path) - val port = address.parseNetworkHostAndPort().port + val port = NetworkHostAndPort.parse(address).port require(nodeController.isPortValid(port), { "Invalid port $port from '$path'." }) return port }