mirror of
https://github.com/corda/corda.git
synced 2025-06-17 14:48:16 +00:00
Start Jolokia agents if configured without modifying JVM options [CORDA-1042] (#2541)
* Jolokia agents are loaded dynamically if configured * Renamed exportJmxTo (never used) to jmxMonitoringHttpPort and take it from config * Updated documentation and tests
This commit is contained in:
@ -138,7 +138,7 @@ class AMQPBridgeTest {
|
||||
doReturn("trustpass").whenever(it).trustStorePassword
|
||||
doReturn("cordacadevpass").whenever(it).keyStorePassword
|
||||
doReturn(artemisAddress).whenever(it).p2pAddress
|
||||
doReturn("").whenever(it).exportJMXto
|
||||
doReturn(null).whenever(it).jmxMonitoringHttpPort
|
||||
doReturn(emptyList<CertChainPolicyConfig>()).whenever(it).certificateChainCheckPolicies
|
||||
}
|
||||
artemisConfig.configureWithDevSSLCertificate()
|
||||
|
@ -222,7 +222,7 @@ class ProtonWrapperTests {
|
||||
doReturn("trustpass").whenever(it).trustStorePassword
|
||||
doReturn("cordacadevpass").whenever(it).keyStorePassword
|
||||
doReturn(NetworkHostAndPort("0.0.0.0", artemisPort)).whenever(it).p2pAddress
|
||||
doReturn("").whenever(it).exportJMXto
|
||||
doReturn(null).whenever(it).jmxMonitoringHttpPort
|
||||
doReturn(emptyList<CertChainPolicyConfig>()).whenever(it).certificateChainCheckPolicies
|
||||
}
|
||||
artemisConfig.configureWithDevSSLCertificate()
|
||||
|
@ -59,6 +59,8 @@ import net.corda.node.services.vault.NodeVaultService
|
||||
import net.corda.node.services.vault.VaultSoftLockManager
|
||||
import net.corda.node.shell.InteractiveShell
|
||||
import net.corda.node.utilities.AffinityExecutor
|
||||
import net.corda.node.utilities.NodeBuildProperties
|
||||
import net.corda.node.utilities.JVMAgentRegistry
|
||||
import net.corda.nodeapi.internal.DevIdentityGenerator
|
||||
import net.corda.nodeapi.internal.crypto.X509Utilities
|
||||
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
||||
@ -73,6 +75,7 @@ import rx.Observable
|
||||
import rx.Scheduler
|
||||
import java.io.IOException
|
||||
import java.lang.reflect.InvocationTargetException
|
||||
import java.nio.file.Paths
|
||||
import java.security.KeyPair
|
||||
import java.security.KeyStoreException
|
||||
import java.security.PublicKey
|
||||
@ -192,6 +195,7 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
|
||||
check(started == null) { "Node has already been started" }
|
||||
log.info("Node starting up ...")
|
||||
initCertificate()
|
||||
initialiseJVMAgents()
|
||||
val schemaService = NodeSchemaService(cordappLoader.cordappSchemas, configuration.notary != null)
|
||||
val (identity, identityKeyPair) = obtainIdentity(notaryConfig = null)
|
||||
val identityService = makeIdentityService(identity.certificate)
|
||||
@ -749,6 +753,22 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
|
||||
return NodeVaultService(platformClock, keyManagementService, stateLoader, hibernateConfig)
|
||||
}
|
||||
|
||||
/** Load configured JVM agents */
|
||||
private fun initialiseJVMAgents() {
|
||||
configuration.jmxMonitoringHttpPort?.let { port ->
|
||||
requireNotNull(NodeBuildProperties.JOLOKIA_AGENT_VERSION) {
|
||||
"'jolokiaAgentVersion' missing from build properties"
|
||||
}
|
||||
log.info("Starting Jolokia agent on HTTP port: $port")
|
||||
val libDir = Paths.get(configuration.baseDirectory.toString(), "drivers")
|
||||
val jarFilePath = JVMAgentRegistry.resolveAgentJar(
|
||||
"jolokia-jvm-${NodeBuildProperties.JOLOKIA_AGENT_VERSION}-agent.jar", libDir) ?:
|
||||
throw Error("Unable to locate agent jar file")
|
||||
log.info("Agent jar file: $jarFilePath")
|
||||
JVMAgentRegistry.attach("jolokia", "port=$port", jarFilePath)
|
||||
}
|
||||
}
|
||||
|
||||
private inner class ServiceHubInternalImpl(
|
||||
override val identityService: IdentityService,
|
||||
// Place the long term identity key in the KMS. Eventually, this is likely going to be separated again because
|
||||
|
@ -192,9 +192,9 @@ open class Node(configuration: NodeConfiguration,
|
||||
val rpcBrokerDirectory: Path = baseDirectory / "brokers" / "rpc"
|
||||
with(rpcOptions) {
|
||||
rpcBroker = if (useSsl) {
|
||||
ArtemisRpcBroker.withSsl(this.address!!, sslConfig, securityManager, certificateChainCheckPolicies, networkParameters.maxMessageSize, exportJMXto.isNotEmpty(), rpcBrokerDirectory)
|
||||
ArtemisRpcBroker.withSsl(this.address!!, sslConfig, securityManager, certificateChainCheckPolicies, networkParameters.maxMessageSize, jmxMonitoringHttpPort != null, rpcBrokerDirectory)
|
||||
} else {
|
||||
ArtemisRpcBroker.withoutSsl(this.address!!, adminAddress!!, sslConfig, securityManager, certificateChainCheckPolicies, networkParameters.maxMessageSize, exportJMXto.isNotEmpty(), rpcBrokerDirectory)
|
||||
ArtemisRpcBroker.withoutSsl(this.address!!, adminAddress!!, sslConfig, securityManager, certificateChainCheckPolicies, networkParameters.maxMessageSize, jmxMonitoringHttpPort != null, rpcBrokerDirectory)
|
||||
}
|
||||
}
|
||||
return rpcBroker!!.addresses
|
||||
|
@ -25,7 +25,7 @@ val Int.MB: Long get() = this * 1024L * 1024L
|
||||
interface NodeConfiguration : NodeSSLConfiguration {
|
||||
val myLegalName: CordaX500Name
|
||||
val emailAddress: String
|
||||
val exportJMXto: String
|
||||
val jmxMonitoringHttpPort: Int?
|
||||
val dataSourceProperties: Properties
|
||||
val rpcUsers: List<User>
|
||||
val security: SecurityConfiguration?
|
||||
@ -118,6 +118,7 @@ data class NodeConfigurationImpl(
|
||||
/** This is not retrieved from the config file but rather from a command line argument. */
|
||||
override val baseDirectory: Path,
|
||||
override val myLegalName: CordaX500Name,
|
||||
override val jmxMonitoringHttpPort: Int? = null,
|
||||
override val emailAddress: String,
|
||||
override val keyStorePassword: String,
|
||||
override val trustStorePassword: String,
|
||||
@ -184,7 +185,6 @@ data class NodeConfigurationImpl(
|
||||
return errors
|
||||
}
|
||||
|
||||
override val exportJMXto: String get() = "http"
|
||||
override val transactionCacheSizeBytes: Long
|
||||
get() = transactionCacheSizeMegaBytes?.MB ?: super.transactionCacheSizeBytes
|
||||
override val attachmentContentCacheSizeBytes: Long
|
||||
|
@ -144,7 +144,7 @@ class ArtemisMessagingServer(private val config: NodeConfiguration,
|
||||
managementNotificationAddress = SimpleString(NOTIFICATIONS_ADDRESS)
|
||||
|
||||
// JMX enablement
|
||||
if (config.exportJMXto.isNotEmpty()) {
|
||||
if (config.jmxMonitoringHttpPort != null) {
|
||||
isJMXManagementEnabled = true
|
||||
isJMXUseBrokerName = true
|
||||
}
|
||||
|
@ -0,0 +1,50 @@
|
||||
package net.corda.node.utilities
|
||||
|
||||
import com.ea.agentloader.AgentLoader
|
||||
import net.corda.core.internal.exists
|
||||
import net.corda.core.internal.isRegularFile
|
||||
import java.net.URLClassLoader
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
/**
|
||||
* Helper class for loading JVM agents dynamically
|
||||
*/
|
||||
object JVMAgentRegistry {
|
||||
|
||||
/**
|
||||
* Names and options of loaded agents
|
||||
*/
|
||||
val loadedAgents = ConcurrentHashMap<String, String>()
|
||||
|
||||
/**
|
||||
* Load and attach agent located at given [jar], unless [loadedAgents]
|
||||
* indicate that one of its instance has been already loaded.
|
||||
*/
|
||||
fun attach(agentName: String, options: String, jar: Path) {
|
||||
loadedAgents.computeIfAbsent(agentName.toLowerCase()) {
|
||||
AgentLoader.loadAgent(jar.toString(), options)
|
||||
options
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt finding location of jar for given agent by first searching into
|
||||
* "drivers" directory of [nodeBaseDirectory] and then falling back to
|
||||
* classpath. Returns null if no match is found.
|
||||
*/
|
||||
fun resolveAgentJar(jarFileName: String, driversDir: Path): Path? {
|
||||
require(jarFileName.endsWith(".jar")) { "jarFileName does not have .jar suffix" }
|
||||
|
||||
val path = Paths.get(driversDir.toString(), jarFileName)
|
||||
return if (path.exists() && path.isRegularFile()) {
|
||||
path
|
||||
} else {
|
||||
(this::class.java.classLoader as? URLClassLoader)
|
||||
?.urLs
|
||||
?.map { Paths.get(it.path) }
|
||||
?.firstOrNull { it.fileName.toString() == jarFileName }
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
package net.corda.node.utilities
|
||||
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* Expose properties defined in top-level 'constants.properties' file.
|
||||
*/
|
||||
object NodeBuildProperties {
|
||||
|
||||
// Note: initialization order is important
|
||||
private val data by lazy {
|
||||
Properties().apply {
|
||||
NodeBuildProperties::class.java.getResourceAsStream("/build.properties")
|
||||
?.let { load(it) }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Jolokia dependency version
|
||||
*/
|
||||
val JOLOKIA_AGENT_VERSION = get("jolokiaAgentVersion")
|
||||
|
||||
/**
|
||||
* Get property value by name
|
||||
*/
|
||||
fun get(key: String): String? = data.getProperty(key)
|
||||
}
|
5
node/src/main/resources/build.properties
Normal file
5
node/src/main/resources/build.properties
Normal file
@ -0,0 +1,5 @@
|
||||
# Build constants exported as resource file to make them visible in Node program
|
||||
# Note: sadly, due to present limitation of IntelliJ-IDEA in processing resource files, these constants cannot be
|
||||
# imported from top-level 'constants.properties' file
|
||||
|
||||
jolokiaAgentVersion=1.3.7
|
@ -67,7 +67,7 @@ class ArtemisMessagingTest {
|
||||
doReturn("trustpass").whenever(it).trustStorePassword
|
||||
doReturn("cordacadevpass").whenever(it).keyStorePassword
|
||||
doReturn(NetworkHostAndPort("0.0.0.0", serverPort)).whenever(it).p2pAddress
|
||||
doReturn("").whenever(it).exportJMXto
|
||||
doReturn(null).whenever(it).jmxMonitoringHttpPort
|
||||
doReturn(emptyList<CertChainPolicyConfig>()).whenever(it).certificateChainCheckPolicies
|
||||
doReturn(5).whenever(it).messageRedeliveryDelaySeconds
|
||||
}
|
||||
|
Reference in New Issue
Block a user