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:
igor nitto
2018-02-15 17:10:07 +00:00
committed by GitHub
parent 2864ce1384
commit ed0cf91946
17 changed files with 125 additions and 17 deletions

View File

@ -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()

View File

@ -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()

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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
}

View File

@ -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 }
}
}
}

View File

@ -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)
}

View 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

View File

@ -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
}