mirror of
https://github.com/corda/corda.git
synced 2024-12-18 20:47:57 +00:00
CORDA-822 - JMX Jolokia instrumentation (#2197)
* JMX Jolokia instrumentation WIP (driverDSL, webserver, cordformation, hibernate statistics, access policy config file hardening) * Cordformation changes to support jolokia agent instrumentation at JVM startup. * Minor updates to reflect usage of Jolokia 1.3.7 (which uses slightly different .war naming) * Use relative path reference in -javaagent to prevent problem with long path names with spaces. * Fixed incorrect regex pattern and added assertion to test. * Enable JMX monitoring. * Reporting of Hibernate JMX statistics is configurable (by default, only switched on in devMode) * Make Artemis JMX enablement configurable. * Re-instate banning of java serialization. * Improve JUnit. * Fixes following rebase from master. * Re-instated correct regex for picking up Jolokia agent jar. * Fixed broken integration test. * Updated documentation * Updated following PR review feedback. * Fixed compilation error caused by change in DriverDSL argument type. * Fixed compilation error caused by change in DriverDSL argument type. * Fail fast if jolokia-agent-jvm.jar is not located. * Applied changes in cordformation following review feedback from CA.
This commit is contained in:
parent
75ea23d193
commit
4762569200
@ -24,7 +24,7 @@ buildscript {
|
|||||||
ext.jackson_version = '2.9.2'
|
ext.jackson_version = '2.9.2'
|
||||||
ext.jetty_version = '9.4.7.v20170914'
|
ext.jetty_version = '9.4.7.v20170914'
|
||||||
ext.jersey_version = '2.25'
|
ext.jersey_version = '2.25'
|
||||||
ext.jolokia_version = '2.0.0-M3'
|
ext.jolokia_version = '1.3.7'
|
||||||
ext.assertj_version = '3.8.0'
|
ext.assertj_version = '3.8.0'
|
||||||
ext.slf4j_version = '1.7.25'
|
ext.slf4j_version = '1.7.25'
|
||||||
ext.log4j_version = '2.9.1'
|
ext.log4j_version = '2.9.1'
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- an all powerful policy file -->
|
||||||
<restrict>
|
<restrict>
|
||||||
<http>
|
<http>
|
||||||
<method>post</method>
|
<method>post</method>
|
||||||
@ -8,23 +8,10 @@
|
|||||||
|
|
||||||
<commands>
|
<commands>
|
||||||
<command>read</command>
|
<command>read</command>
|
||||||
|
<command>write</command>
|
||||||
|
<command>exec</command>
|
||||||
<command>list</command>
|
<command>list</command>
|
||||||
|
<command>search</command>
|
||||||
|
<command>version</command>
|
||||||
</commands>
|
</commands>
|
||||||
|
|
||||||
<!-- allow anyone to force a garbage collection -->
|
|
||||||
<allow>
|
|
||||||
<mbean>
|
|
||||||
<name>java.lang:type=Memory</name>
|
|
||||||
<operation>gc</operation>
|
|
||||||
</mbean>
|
|
||||||
</allow>
|
|
||||||
|
|
||||||
<!-- in case we ever end up using c3pio connection pooling, this example from the docs prevents the password being exported -->
|
|
||||||
<deny>
|
|
||||||
<mbean>
|
|
||||||
<name>com.mchange.v2.c3p0:type=PooledDataSource,*</name>
|
|
||||||
<attribute>properties</attribute>
|
|
||||||
</mbean>
|
|
||||||
</deny>
|
|
||||||
|
|
||||||
</restrict>
|
</restrict>
|
||||||
|
24
config/prod/jolokia-access.xml
Normal file
24
config/prod/jolokia-access.xml
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Jolokia agent and MBean access policy based security -->
|
||||||
|
<!-- TODO: review these settings before production deployment -->
|
||||||
|
<restrict>
|
||||||
|
<!-- IP based restrictions -->
|
||||||
|
<remote>
|
||||||
|
<!-- IP address, a host name, or a netmask given in CIDR format (e.g. "10.0.0.0/16" for all clients coming from the 10.0 network). -->
|
||||||
|
<host>127.0.0.1</host>
|
||||||
|
<host>localhost</host>
|
||||||
|
</remote>
|
||||||
|
<!-- commands for which access is granted: read, write, exec, list, search, version -->
|
||||||
|
<commands>
|
||||||
|
<command>version</command>
|
||||||
|
<command>read</command>
|
||||||
|
</commands>
|
||||||
|
<!-- MBean access and deny restrictions -->
|
||||||
|
<!-- HTTP method restrictions: get, post -->
|
||||||
|
<http>
|
||||||
|
<method>get</method>
|
||||||
|
</http>
|
||||||
|
<!-- Cross-Origin Resource Sharing (CORS) restrictions
|
||||||
|
(by default, allow cross origin access from any host)
|
||||||
|
-->
|
||||||
|
</restrict>
|
@ -6,6 +6,9 @@ from the previous milestone release.
|
|||||||
|
|
||||||
UNRELEASED
|
UNRELEASED
|
||||||
----------
|
----------
|
||||||
|
* Exporting additional JMX metrics (artemis, hibernate statistics) and loading Jolokia agent at JVM startup when using
|
||||||
|
DriverDSL and/or cordformation node runner.
|
||||||
|
|
||||||
* Removed confusing property database.initDatabase, enabling its guarded behaviour with the dev-mode.
|
* Removed confusing property database.initDatabase, enabling its guarded behaviour with the dev-mode.
|
||||||
In devMode Hibernate will try to create or update database schemas, otherwise it will expect relevant schemas to be present
|
In devMode Hibernate will try to create or update database schemas, otherwise it will expect relevant schemas to be present
|
||||||
in the database (pre configured via DDL scripts or equivalent), and validate these are correct.
|
in the database (pre configured via DDL scripts or equivalent), and validate these are correct.
|
||||||
|
@ -73,6 +73,7 @@ path to the node's base directory.
|
|||||||
:serverNameTablePrefix: Prefix string to apply to all the database tables. The default is no prefix.
|
:serverNameTablePrefix: Prefix string to apply to all the database tables. The default is no prefix.
|
||||||
:transactionIsolationLevel: Transaction isolation level as defined by the ``TRANSACTION_`` constants in
|
:transactionIsolationLevel: Transaction isolation level as defined by the ``TRANSACTION_`` constants in
|
||||||
``java.sql.Connection``, but without the "TRANSACTION_" prefix. Defaults to REPEATABLE_READ.
|
``java.sql.Connection``, but without the "TRANSACTION_" prefix. Defaults to REPEATABLE_READ.
|
||||||
|
:exportHibernateJMXStatistics: Whether to export Hibernate JMX statistics (caution: expensive run-time overhead)
|
||||||
|
|
||||||
:dataSourceProperties: This section is used to configure the jdbc connection and database driver used for the nodes persistence.
|
:dataSourceProperties: This section is used to configure the jdbc connection and database driver used for the nodes persistence.
|
||||||
Currently the defaults in ``/node/src/main/resources/reference.conf`` are as shown in the first example. This is currently
|
Currently the defaults in ``/node/src/main/resources/reference.conf`` are as shown in the first example. This is currently
|
||||||
@ -163,7 +164,9 @@ path to the node's base directory.
|
|||||||
Each should be a string. Only the JARs in the directories are added, not the directories themselves. This is useful
|
Each should be a string. Only the JARs in the directories are added, not the directories themselves. This is useful
|
||||||
for including JDBC drivers and the like. e.g. ``jarDirs = [ 'lib' ]``
|
for including JDBC drivers and the like. e.g. ``jarDirs = [ 'lib' ]``
|
||||||
|
|
||||||
:sshd: If provided, node will start internal SSH server which will provide a management shell. It uses the same credentials
|
:sshd: If provided, node will start internal SSH server which will provide a management shell. It uses the same credentials and permissions as RPC subsystem. It has one required parameter.
|
||||||
and permissions as RPC subsystem. It has one required parameter.
|
|
||||||
|
|
||||||
:port: The port to start SSH server on
|
:port: The port to start SSH server on
|
||||||
|
|
||||||
|
:exportJMXTo: If set to ``http``, will enable JMX metrics reporting via the Jolokia HTTP/JSON agent.
|
||||||
|
Default Jolokia access url is http://127.0.0.1:7005/jolokia/
|
@ -93,6 +93,8 @@ formats for accessing MBeans, and provides client libraries to work with that pr
|
|||||||
|
|
||||||
Here are a few ways to build dashboards and extract monitoring data for a node:
|
Here are a few ways to build dashboards and extract monitoring data for a node:
|
||||||
|
|
||||||
|
* `hawtio <https://hawt.io>`_ is a web based console that connects directly to JVM's that have been instrumented with a
|
||||||
|
jolokia agent. This tool provides a nice JMX dashboard very similar to the traditional JVisualVM / JConsole MBbeans original.
|
||||||
* `JMX2Graphite <https://github.com/logzio/jmx2graphite>`_ is a tool that can be pointed to /monitoring/json and will
|
* `JMX2Graphite <https://github.com/logzio/jmx2graphite>`_ is a tool that can be pointed to /monitoring/json and will
|
||||||
scrape the statistics found there, then insert them into the Graphite monitoring tool on a regular basis. It runs
|
scrape the statistics found there, then insert them into the Graphite monitoring tool on a regular basis. It runs
|
||||||
in Docker and can be started with a single command.
|
in Docker and can be started with a single command.
|
||||||
@ -105,6 +107,29 @@ Here are a few ways to build dashboards and extract monitoring data for a node:
|
|||||||
It can bridge any data input to any output using their plugin system, for example, Telegraf can
|
It can bridge any data input to any output using their plugin system, for example, Telegraf can
|
||||||
be configured to collect data from Jolokia and write to DataDog web api.
|
be configured to collect data from Jolokia and write to DataDog web api.
|
||||||
|
|
||||||
|
The Node configuration parameter `exportJMXTo` should be set to ``http`` to ensure a Jolokia agent is instrumented with
|
||||||
|
the JVM run-time.
|
||||||
|
|
||||||
|
The following JMX statistics are exported:
|
||||||
|
|
||||||
|
* Corda specific metrics: flow information (total started, finished, in-flight; flow duration by flow type), attachments (count)
|
||||||
|
* Apache Artemis metrics: queue information for P2P and RPC services
|
||||||
|
* JVM statistics: classloading, garbage collection, memory, runtime, threading, operating system
|
||||||
|
* Hibernate statistics (only when node is started-up in `devMode` due to to expensive run-time costs)
|
||||||
|
|
||||||
|
When starting Corda nodes using Cordformation runner (see :doc:`running-a-node`), you should see a startup message similar to the following:
|
||||||
|
**Jolokia: Agent started with URL http://127.0.0.1:7005/jolokia/**
|
||||||
|
|
||||||
|
When starting Corda nodes using the `DriverDSL`, you should see a startup message in the logs similar to the following:
|
||||||
|
**Starting out-of-process Node USA Bank Corp, debug port is not enabled, jolokia monitoring port is 7005 {}**
|
||||||
|
|
||||||
|
Several Jolokia policy based security configuration files (``jolokia-access.xml``) are available for dev, test, and prod
|
||||||
|
environments under ``/config/<env>``.
|
||||||
|
|
||||||
|
The following diagram illustrates Corda flow metrics visualized using `hawtio <https://hawt.io>`_ :
|
||||||
|
|
||||||
|
.. image:: resources/hawtio-jmx.png
|
||||||
|
|
||||||
Memory usage and tuning
|
Memory usage and tuning
|
||||||
-----------------------
|
-----------------------
|
||||||
|
|
||||||
|
BIN
docs/source/resources/hawtio-jmx.png
Normal file
BIN
docs/source/resources/hawtio-jmx.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 242 KiB |
@ -23,6 +23,13 @@ class Utils {
|
|||||||
project.configurations.single { it.name == "compile" }.extendsFrom(configuration)
|
project.configurations.single { it.name == "compile" }.extendsFrom(configuration)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
fun createRuntimeConfiguration(name: String, project: Project) {
|
||||||
|
if(!project.configurations.any { it.name == name }) {
|
||||||
|
val configuration = project.configurations.create(name)
|
||||||
|
configuration.isTransitive = false
|
||||||
|
project.configurations.single { it.name == "runtime" }.extendsFrom(configuration)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -10,6 +10,8 @@ import java.io.File
|
|||||||
*/
|
*/
|
||||||
class Cordformation : Plugin<Project> {
|
class Cordformation : Plugin<Project> {
|
||||||
internal companion object {
|
internal companion object {
|
||||||
|
const val CORDFORMATION_TYPE = "cordformationInternal"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a resource file from this plugin's JAR file.
|
* Gets a resource file from this plugin's JAR file.
|
||||||
*
|
*
|
||||||
@ -31,5 +33,8 @@ class Cordformation : Plugin<Project> {
|
|||||||
|
|
||||||
override fun apply(project: Project) {
|
override fun apply(project: Project) {
|
||||||
Utils.createCompileConfiguration("cordapp", project)
|
Utils.createCompileConfiguration("cordapp", project)
|
||||||
|
Utils.createRuntimeConfiguration(CORDFORMATION_TYPE, project)
|
||||||
|
val jolokiaVersion = project.rootProject.ext<String>("jolokia_version")
|
||||||
|
project.dependencies.add(CORDFORMATION_TYPE, "org.jolokia:jolokia-jvm:$jolokiaVersion:agent")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package net.corda.plugins
|
package net.corda.plugins
|
||||||
|
|
||||||
import com.typesafe.config.*
|
import com.typesafe.config.ConfigFactory
|
||||||
|
import com.typesafe.config.ConfigRenderOptions
|
||||||
|
import com.typesafe.config.ConfigValueFactory
|
||||||
import net.corda.cordform.CordformNode
|
import net.corda.cordform.CordformNode
|
||||||
import org.bouncycastle.asn1.x500.X500Name
|
import org.bouncycastle.asn1.x500.X500Name
|
||||||
import org.bouncycastle.asn1.x500.style.BCStyle
|
import org.bouncycastle.asn1.x500.style.BCStyle
|
||||||
@ -90,6 +92,7 @@ class Node(private val project: Project) : CordformNode() {
|
|||||||
if (config.hasPath("webAddress")) {
|
if (config.hasPath("webAddress")) {
|
||||||
installWebserverJar()
|
installWebserverJar()
|
||||||
}
|
}
|
||||||
|
installAgentJar()
|
||||||
installBuiltCordapp()
|
installBuiltCordapp()
|
||||||
installCordapps()
|
installCordapps()
|
||||||
installConfig()
|
installConfig()
|
||||||
@ -177,6 +180,29 @@ class Node(private val project: Project) : CordformNode() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Installs the jolokia monitoring agent JAR to the node/drivers directory
|
||||||
|
*/
|
||||||
|
private fun installAgentJar() {
|
||||||
|
val jolokiaVersion = project.rootProject.ext<String>("jolokia_version")
|
||||||
|
val agentJar = project.configuration("runtime").files {
|
||||||
|
(it.group == "org.jolokia") &&
|
||||||
|
(it.name == "jolokia-jvm") &&
|
||||||
|
(it.version == jolokiaVersion)
|
||||||
|
// TODO: revisit when classifier attribute is added. eg && (it.classifier = "agent")
|
||||||
|
}.first() // should always be the jolokia agent fat jar: eg. jolokia-jvm-1.3.7-agent.jar
|
||||||
|
project.logger.info("Jolokia agent jar: $agentJar")
|
||||||
|
if (agentJar.isFile) {
|
||||||
|
val driversDir = File(nodeDir, "drivers")
|
||||||
|
project.copy {
|
||||||
|
it.apply {
|
||||||
|
from(agentJar)
|
||||||
|
into(driversDir)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Installs the configuration file to this node's directory and detokenises it.
|
* Installs the configuration file to this node's directory and detokenises it.
|
||||||
*/
|
*/
|
||||||
|
@ -22,6 +22,11 @@ private object debugPortAlloc {
|
|||||||
internal fun next() = basePort++
|
internal fun next() = basePort++
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private object monitoringPortAlloc {
|
||||||
|
private var basePort = 7005
|
||||||
|
internal fun next() = basePort++
|
||||||
|
}
|
||||||
|
|
||||||
fun main(args: Array<String>) {
|
fun main(args: Array<String>) {
|
||||||
val startedProcesses = mutableListOf<Process>()
|
val startedProcesses = mutableListOf<Process>()
|
||||||
val headless = GraphicsEnvironment.isHeadless() || args.contains(HEADLESS_FLAG)
|
val headless = GraphicsEnvironment.isHeadless() || args.contains(HEADLESS_FLAG)
|
||||||
@ -49,8 +54,9 @@ private abstract class JarType(private val jarName: String) {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
val debugPort = debugPortAlloc.next()
|
val debugPort = debugPortAlloc.next()
|
||||||
|
val monitoringPort = monitoringPortAlloc.next()
|
||||||
println("Starting $jarName in $dir on debug port $debugPort")
|
println("Starting $jarName in $dir on debug port $debugPort")
|
||||||
val process = (if (headless) ::HeadlessJavaCommand else ::TerminalWindowJavaCommand)(jarName, dir, debugPort, javaArgs, jvmArgs).start()
|
val process = (if (headless) ::HeadlessJavaCommand else ::TerminalWindowJavaCommand)(jarName, dir, debugPort, monitoringPort, javaArgs, jvmArgs).start()
|
||||||
if (os == OS.MACOS) Thread.sleep(1000)
|
if (os == OS.MACOS) Thread.sleep(1000)
|
||||||
return process
|
return process
|
||||||
}
|
}
|
||||||
@ -69,15 +75,23 @@ private abstract class JavaCommand(
|
|||||||
jarName: String,
|
jarName: String,
|
||||||
internal val dir: File,
|
internal val dir: File,
|
||||||
debugPort: Int?,
|
debugPort: Int?,
|
||||||
|
monitoringPort: Int?,
|
||||||
internal val nodeName: String,
|
internal val nodeName: String,
|
||||||
init: MutableList<String>.() -> Unit, args: List<String>,
|
init: MutableList<String>.() -> Unit, args: List<String>,
|
||||||
jvmArgs: List<String>
|
jvmArgs: List<String>
|
||||||
) {
|
) {
|
||||||
|
private val jolokiaJar by lazy {
|
||||||
|
File("$dir/drivers").listFiles { _, filename ->
|
||||||
|
filename.matches("jolokia-jvm-.*-agent\\.jar$".toRegex())
|
||||||
|
}.first().name
|
||||||
|
}
|
||||||
|
|
||||||
internal val command: List<String> = mutableListOf<String>().apply {
|
internal val command: List<String> = mutableListOf<String>().apply {
|
||||||
add(getJavaPath())
|
add(getJavaPath())
|
||||||
addAll(jvmArgs)
|
addAll(jvmArgs)
|
||||||
add("-Dname=$nodeName")
|
add("-Dname=$nodeName")
|
||||||
null != debugPort && add("-Dcapsule.jvm.args=-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=$debugPort")
|
null != debugPort && add("-Dcapsule.jvm.args=-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=$debugPort")
|
||||||
|
null != monitoringPort && add("-Dcapsule.jvm.args=-javaagent:drivers/$jolokiaJar=port=$monitoringPort")
|
||||||
add("-jar")
|
add("-jar")
|
||||||
add(jarName)
|
add(jarName)
|
||||||
init()
|
init()
|
||||||
@ -89,14 +103,14 @@ private abstract class JavaCommand(
|
|||||||
internal abstract fun getJavaPath(): String
|
internal abstract fun getJavaPath(): String
|
||||||
}
|
}
|
||||||
|
|
||||||
private class HeadlessJavaCommand(jarName: String, dir: File, debugPort: Int?, args: List<String>, jvmArgs: List<String>)
|
private class HeadlessJavaCommand(jarName: String, dir: File, debugPort: Int?, monitoringPort: Int?, args: List<String>, jvmArgs: List<String>)
|
||||||
: JavaCommand(jarName, dir, debugPort, dir.name, { add("--no-local-shell") }, args, jvmArgs) {
|
: JavaCommand(jarName, dir, debugPort, monitoringPort, dir.name, { add("--no-local-shell") }, args, jvmArgs) {
|
||||||
override fun processBuilder() = ProcessBuilder(command).redirectError(File("error.$nodeName.log")).inheritIO()
|
override fun processBuilder() = ProcessBuilder(command).redirectError(File("error.$nodeName.log")).inheritIO()
|
||||||
override fun getJavaPath() = File(File(System.getProperty("java.home"), "bin"), "java").path
|
override fun getJavaPath() = File(File(System.getProperty("java.home"), "bin"), "java").path
|
||||||
}
|
}
|
||||||
|
|
||||||
private class TerminalWindowJavaCommand(jarName: String, dir: File, debugPort: Int?, args: List<String>, jvmArgs: List<String>)
|
private class TerminalWindowJavaCommand(jarName: String, dir: File, debugPort: Int?, monitoringPort: Int?, args: List<String>, jvmArgs: List<String>)
|
||||||
: JavaCommand(jarName, dir, debugPort, "${dir.name}-$jarName", {}, args, jvmArgs) {
|
: JavaCommand(jarName, dir, debugPort, monitoringPort, "${dir.name}-$jarName", {}, args, jvmArgs) {
|
||||||
override fun processBuilder() = ProcessBuilder(when (os) {
|
override fun processBuilder() = ProcessBuilder(when (os) {
|
||||||
OS.MACOS -> {
|
OS.MACOS -> {
|
||||||
listOf("osascript", "-e", """tell app "Terminal"
|
listOf("osascript", "-e", """tell app "Terminal"
|
||||||
|
@ -20,7 +20,8 @@ const val NODE_DATABASE_PREFIX = "node_"
|
|||||||
data class DatabaseConfig(
|
data class DatabaseConfig(
|
||||||
val initialiseSchema: Boolean = true,
|
val initialiseSchema: Boolean = true,
|
||||||
val serverNameTablePrefix: String = "",
|
val serverNameTablePrefix: String = "",
|
||||||
val transactionIsolationLevel: TransactionIsolationLevel = TransactionIsolationLevel.REPEATABLE_READ
|
val transactionIsolationLevel: TransactionIsolationLevel = TransactionIsolationLevel.REPEATABLE_READ,
|
||||||
|
val exportHibernateJMXStatistics: Boolean = false
|
||||||
)
|
)
|
||||||
|
|
||||||
// This class forms part of the node config and so any changes to it must be handled with care
|
// This class forms part of the node config and so any changes to it must be handled with care
|
||||||
|
@ -17,8 +17,10 @@ import org.hibernate.type.AbstractSingleColumnStandardBasicType
|
|||||||
import org.hibernate.type.descriptor.java.PrimitiveByteArrayTypeDescriptor
|
import org.hibernate.type.descriptor.java.PrimitiveByteArrayTypeDescriptor
|
||||||
import org.hibernate.type.descriptor.sql.BlobTypeDescriptor
|
import org.hibernate.type.descriptor.sql.BlobTypeDescriptor
|
||||||
import org.hibernate.type.descriptor.sql.VarbinaryTypeDescriptor
|
import org.hibernate.type.descriptor.sql.VarbinaryTypeDescriptor
|
||||||
|
import java.lang.management.ManagementFactory
|
||||||
import java.sql.Connection
|
import java.sql.Connection
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
import javax.management.ObjectName
|
||||||
import javax.persistence.AttributeConverter
|
import javax.persistence.AttributeConverter
|
||||||
|
|
||||||
class HibernateConfiguration(
|
class HibernateConfiguration(
|
||||||
@ -60,9 +62,31 @@ class HibernateConfiguration(
|
|||||||
|
|
||||||
val sessionFactory = buildSessionFactory(config, metadataSources, databaseConfig.serverNameTablePrefix)
|
val sessionFactory = buildSessionFactory(config, metadataSources, databaseConfig.serverNameTablePrefix)
|
||||||
logger.info("Created session factory for schemas: $schemas")
|
logger.info("Created session factory for schemas: $schemas")
|
||||||
|
|
||||||
|
// export Hibernate JMX statistics
|
||||||
|
if (databaseConfig.exportHibernateJMXStatistics)
|
||||||
|
initStatistics(sessionFactory)
|
||||||
|
|
||||||
return sessionFactory
|
return sessionFactory
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NOTE: workaround suggested to overcome deprecation of StatisticsService (since Hibernate v4.0)
|
||||||
|
// https://stackoverflow.com/questions/23606092/hibernate-upgrade-statisticsservice
|
||||||
|
fun initStatistics(sessionFactory: SessionFactory) {
|
||||||
|
val statsName = ObjectName("org.hibernate:type=statistics")
|
||||||
|
val mbeanServer = ManagementFactory.getPlatformMBeanServer()
|
||||||
|
|
||||||
|
val statisticsMBean = DelegatingStatisticsService(sessionFactory.statistics)
|
||||||
|
statisticsMBean.isStatisticsEnabled = true
|
||||||
|
|
||||||
|
try {
|
||||||
|
mbeanServer.registerMBean(statisticsMBean, statsName)
|
||||||
|
}
|
||||||
|
catch (e: Exception) {
|
||||||
|
logger.warn(e.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun buildSessionFactory(config: Configuration, metadataSources: MetadataSources, tablePrefix: String): SessionFactory {
|
private fun buildSessionFactory(config: Configuration, metadataSources: MetadataSources, tablePrefix: String): SessionFactory {
|
||||||
config.standardServiceRegistryBuilder.applySettings(config.properties)
|
config.standardServiceRegistryBuilder.applySettings(config.properties)
|
||||||
val metadata = metadataSources.getMetadataBuilder(config.standardServiceRegistryBuilder.build()).run {
|
val metadata = metadataSources.getMetadataBuilder(config.standardServiceRegistryBuilder.build()).run {
|
||||||
|
@ -0,0 +1,227 @@
|
|||||||
|
package net.corda.nodeapi.internal.persistence
|
||||||
|
|
||||||
|
import javax.management.MXBean
|
||||||
|
|
||||||
|
import org.hibernate.stat.Statistics
|
||||||
|
import org.hibernate.stat.SecondLevelCacheStatistics
|
||||||
|
import org.hibernate.stat.QueryStatistics
|
||||||
|
import org.hibernate.stat.NaturalIdCacheStatistics
|
||||||
|
import org.hibernate.stat.EntityStatistics
|
||||||
|
import org.hibernate.stat.CollectionStatistics
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exposes Hibernate [Statistics] contract as JMX resource.
|
||||||
|
*/
|
||||||
|
@MXBean
|
||||||
|
interface StatisticsService : Statistics
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements the MXBean interface by delegating through the actual [Statistics] implementation retrieved from the
|
||||||
|
* session factory.
|
||||||
|
*/
|
||||||
|
class DelegatingStatisticsService(private val delegate: Statistics) : StatisticsService {
|
||||||
|
|
||||||
|
override fun clear() {
|
||||||
|
delegate.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getCloseStatementCount(): Long {
|
||||||
|
return delegate.closeStatementCount
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getCollectionFetchCount(): Long {
|
||||||
|
return delegate.collectionFetchCount
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getCollectionLoadCount(): Long {
|
||||||
|
return delegate.collectionLoadCount
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getCollectionRecreateCount(): Long {
|
||||||
|
return delegate.collectionRecreateCount
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getCollectionRemoveCount(): Long {
|
||||||
|
return delegate.collectionRemoveCount
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getCollectionRoleNames(): Array<String> {
|
||||||
|
return delegate.collectionRoleNames
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getCollectionStatistics(arg0: String): CollectionStatistics {
|
||||||
|
return delegate.getCollectionStatistics(arg0)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getCollectionUpdateCount(): Long {
|
||||||
|
return delegate.collectionUpdateCount
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getConnectCount(): Long {
|
||||||
|
return delegate.connectCount
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getEntityDeleteCount(): Long {
|
||||||
|
return delegate.entityDeleteCount
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getEntityFetchCount(): Long {
|
||||||
|
return delegate.entityFetchCount
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getEntityInsertCount(): Long {
|
||||||
|
return delegate.entityInsertCount
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getEntityLoadCount(): Long {
|
||||||
|
return delegate.entityLoadCount
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getEntityNames(): Array<String> {
|
||||||
|
return delegate.entityNames
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getEntityStatistics(arg0: String): EntityStatistics {
|
||||||
|
return delegate.getEntityStatistics(arg0)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getEntityUpdateCount(): Long {
|
||||||
|
return delegate.entityUpdateCount
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getFlushCount(): Long {
|
||||||
|
return delegate.flushCount
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getNaturalIdCacheHitCount(): Long {
|
||||||
|
return delegate.naturalIdCacheHitCount
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getNaturalIdCacheMissCount(): Long {
|
||||||
|
return delegate.naturalIdCacheMissCount
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getNaturalIdCachePutCount(): Long {
|
||||||
|
return delegate.naturalIdCachePutCount
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getNaturalIdCacheStatistics(arg0: String): NaturalIdCacheStatistics {
|
||||||
|
return delegate.getNaturalIdCacheStatistics(arg0)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getNaturalIdQueryExecutionCount(): Long {
|
||||||
|
return delegate.naturalIdQueryExecutionCount
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getNaturalIdQueryExecutionMaxTime(): Long {
|
||||||
|
return delegate.naturalIdQueryExecutionMaxTime
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getNaturalIdQueryExecutionMaxTimeRegion(): String {
|
||||||
|
return delegate.naturalIdQueryExecutionMaxTimeRegion
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getOptimisticFailureCount(): Long {
|
||||||
|
return delegate.optimisticFailureCount
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getPrepareStatementCount(): Long {
|
||||||
|
return delegate.prepareStatementCount
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getQueries(): Array<String> {
|
||||||
|
return delegate.queries
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getQueryCacheHitCount(): Long {
|
||||||
|
return delegate.queryCacheHitCount
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getQueryCacheMissCount(): Long {
|
||||||
|
return delegate.queryCacheMissCount
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getQueryCachePutCount(): Long {
|
||||||
|
return delegate.queryCachePutCount
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getQueryExecutionCount(): Long {
|
||||||
|
return delegate.queryExecutionCount
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getQueryExecutionMaxTime(): Long {
|
||||||
|
return delegate.queryExecutionMaxTime
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getQueryExecutionMaxTimeQueryString(): String {
|
||||||
|
return delegate.queryExecutionMaxTimeQueryString
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getQueryStatistics(arg0: String): QueryStatistics {
|
||||||
|
return delegate.getQueryStatistics(arg0)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getSecondLevelCacheHitCount(): Long {
|
||||||
|
return delegate.secondLevelCacheHitCount
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getSecondLevelCacheMissCount(): Long {
|
||||||
|
return delegate.secondLevelCacheMissCount
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getSecondLevelCachePutCount(): Long {
|
||||||
|
return delegate.secondLevelCachePutCount
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getSecondLevelCacheRegionNames(): Array<String> {
|
||||||
|
return delegate.secondLevelCacheRegionNames
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getSecondLevelCacheStatistics(arg0: String): SecondLevelCacheStatistics {
|
||||||
|
return delegate.getSecondLevelCacheStatistics(arg0)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getSessionCloseCount(): Long {
|
||||||
|
return delegate.sessionCloseCount
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getSessionOpenCount(): Long {
|
||||||
|
return delegate.sessionOpenCount
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getStartTime(): Long {
|
||||||
|
return delegate.startTime
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getSuccessfulTransactionCount(): Long {
|
||||||
|
return delegate.successfulTransactionCount
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getTransactionCount(): Long {
|
||||||
|
return delegate.transactionCount
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getUpdateTimestampsCacheHitCount(): Long {
|
||||||
|
return delegate.updateTimestampsCacheHitCount
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getUpdateTimestampsCacheMissCount(): Long {
|
||||||
|
return delegate.updateTimestampsCacheMissCount
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getUpdateTimestampsCachePutCount(): Long {
|
||||||
|
return delegate.updateTimestampsCachePutCount
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isStatisticsEnabled(): Boolean {
|
||||||
|
return delegate.isStatisticsEnabled
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun logSummary() {
|
||||||
|
delegate.logSummary()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setStatisticsEnabled(arg0: Boolean) {
|
||||||
|
delegate.isStatisticsEnabled = arg0
|
||||||
|
}
|
||||||
|
}
|
@ -43,6 +43,11 @@ sourceSets {
|
|||||||
// This prevents problems in IntelliJ with regard to duplicate source roots.
|
// This prevents problems in IntelliJ with regard to duplicate source roots.
|
||||||
processResources {
|
processResources {
|
||||||
from file("$rootDir/config/dev/log4j2.xml")
|
from file("$rootDir/config/dev/log4j2.xml")
|
||||||
|
from file("$rootDir/config/dev/jolokia-access.xml")
|
||||||
|
}
|
||||||
|
|
||||||
|
processTestResources {
|
||||||
|
from file("$rootDir/config/test/jolokia-access.xml")
|
||||||
}
|
}
|
||||||
|
|
||||||
// To find potential version conflicts, run "gradle htmlDependencyReport" and then look in
|
// To find potential version conflicts, run "gradle htmlDependencyReport" and then look in
|
||||||
@ -174,6 +179,9 @@ dependencies {
|
|||||||
testCompile "org.glassfish.jersey.core:jersey-server:${jersey_version}"
|
testCompile "org.glassfish.jersey.core:jersey-server:${jersey_version}"
|
||||||
testCompile "org.glassfish.jersey.containers:jersey-container-servlet-core:${jersey_version}"
|
testCompile "org.glassfish.jersey.containers:jersey-container-servlet-core:${jersey_version}"
|
||||||
testCompile "org.glassfish.jersey.containers:jersey-container-jetty-http:${jersey_version}"
|
testCompile "org.glassfish.jersey.containers:jersey-container-jetty-http:${jersey_version}"
|
||||||
|
|
||||||
|
// Jolokia JVM monitoring agent
|
||||||
|
runtime "org.jolokia:jolokia-jvm:${jolokia_version}:agent"
|
||||||
}
|
}
|
||||||
|
|
||||||
task integrationTest(type: Test) {
|
task integrationTest(type: Test) {
|
||||||
|
@ -42,7 +42,7 @@ task buildCordaJAR(type: FatCapsule, dependsOn: project(':node').compileJava) {
|
|||||||
|
|
||||||
capsuleManifest {
|
capsuleManifest {
|
||||||
applicationVersion = corda_release_version
|
applicationVersion = corda_release_version
|
||||||
appClassPath = ["jolokia-agent-war-${project.rootProject.ext.jolokia_version}.war"]
|
appClassPath = ["jolokia-war-${project.rootProject.ext.jolokia_version}.war"]
|
||||||
// See experimental/quasar-hook/README.md for how to generate.
|
// See experimental/quasar-hook/README.md for how to generate.
|
||||||
def quasarExcludeExpression = "x(antlr**;bftsmart**;ch**;co.paralleluniverse**;com.codahale**;com.esotericsoftware**;com.fasterxml**;com.google**;com.ibm**;com.intellij**;com.jcabi**;com.nhaarman**;com.opengamma**;com.typesafe**;com.zaxxer**;de.javakaffee**;groovy**;groovyjarjarantlr**;groovyjarjarasm**;io.atomix**;io.github**;io.netty**;jdk**;joptsimple**;junit**;kotlin**;net.bytebuddy**;net.i2p**;org.apache**;org.assertj**;org.bouncycastle**;org.codehaus**;org.crsh**;org.dom4j**;org.fusesource**;org.h2**;org.hamcrest**;org.hibernate**;org.jboss**;org.jcp**;org.joda**;org.junit**;org.mockito**;org.objectweb**;org.objenesis**;org.slf4j**;org.w3c**;org.xml**;org.yaml**;reflectasm**;rx**)"
|
def quasarExcludeExpression = "x(antlr**;bftsmart**;ch**;co.paralleluniverse**;com.codahale**;com.esotericsoftware**;com.fasterxml**;com.google**;com.ibm**;com.intellij**;com.jcabi**;com.nhaarman**;com.opengamma**;com.typesafe**;com.zaxxer**;de.javakaffee**;groovy**;groovyjarjarantlr**;groovyjarjarasm**;io.atomix**;io.github**;io.netty**;jdk**;joptsimple**;junit**;kotlin**;net.bytebuddy**;net.i2p**;org.apache**;org.assertj**;org.bouncycastle**;org.codehaus**;org.crsh**;org.dom4j**;org.fusesource**;org.h2**;org.hamcrest**;org.hibernate**;org.jboss**;org.jcp**;org.joda**;org.junit**;org.mockito**;org.objectweb**;org.objenesis**;org.slf4j**;org.w3c**;org.xml**;org.yaml**;reflectasm**;rx**)"
|
||||||
javaAgents = ["quasar-core-${quasar_version}-jdk8.jar=${quasarExcludeExpression}"]
|
javaAgents = ["quasar-core-${quasar_version}-jdk8.jar=${quasarExcludeExpression}"]
|
||||||
|
@ -81,13 +81,13 @@ open class NodeStartup(val args: Array<String>) {
|
|||||||
conf0
|
conf0
|
||||||
}
|
}
|
||||||
|
|
||||||
banJavaSerialisation(conf)
|
banJavaSerialisation(conf)
|
||||||
preNetworkRegistration(conf)
|
preNetworkRegistration(conf)
|
||||||
if (shouldRegisterWithNetwork(cmdlineOptions, conf)) {
|
if (shouldRegisterWithNetwork(cmdlineOptions, conf)) {
|
||||||
registerWithNetwork(cmdlineOptions, conf)
|
registerWithNetwork(cmdlineOptions, conf)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
logStartupInfo(versionInfo, cmdlineOptions, conf)
|
logStartupInfo(versionInfo, cmdlineOptions, conf)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
cmdlineOptions.baseDirectory.createDirectories()
|
cmdlineOptions.baseDirectory.createDirectories()
|
||||||
|
@ -113,7 +113,7 @@ data class NodeConfigurationImpl(
|
|||||||
// TODO See TODO above. Rename this to nodeInfoPollingFrequency and make it of type Duration
|
// TODO See TODO above. Rename this to nodeInfoPollingFrequency and make it of type Duration
|
||||||
override val additionalNodeInfoPollingFrequencyMsec: Long = 5.seconds.toMillis(),
|
override val additionalNodeInfoPollingFrequencyMsec: Long = 5.seconds.toMillis(),
|
||||||
override val sshd: SSHDConfiguration? = null,
|
override val sshd: SSHDConfiguration? = null,
|
||||||
override val database: DatabaseConfig = DatabaseConfig(initialiseSchema = devMode)
|
override val database: DatabaseConfig = DatabaseConfig(initialiseSchema = devMode, exportHibernateJMXStatistics = devMode)
|
||||||
) : NodeConfiguration {
|
) : NodeConfiguration {
|
||||||
override val exportJMXto: String get() = "http"
|
override val exportJMXto: String get() = "http"
|
||||||
|
|
||||||
|
@ -211,7 +211,12 @@ class ArtemisMessagingServer(private val config: NodeConfiguration,
|
|||||||
addressFullMessagePolicy = AddressFullMessagePolicy.FAIL
|
addressFullMessagePolicy = AddressFullMessagePolicy.FAIL
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}.configureAddressSecurity()
|
// JMX enablement
|
||||||
|
if (config.exportJMXto.isNotEmpty()) {isJMXManagementEnabled = true
|
||||||
|
isJMXUseBrokerName = true}
|
||||||
|
|
||||||
|
}.configureAddressSecurity()
|
||||||
|
|
||||||
|
|
||||||
private fun queueConfig(name: String, address: String = name, filter: String? = null, durable: Boolean): CoreQueueConfiguration {
|
private fun queueConfig(name: String, address: String = name, filter: String? = null, durable: Boolean): CoreQueueConfiguration {
|
||||||
return CoreQueueConfiguration().apply {
|
return CoreQueueConfiguration().apply {
|
||||||
|
@ -11,6 +11,7 @@ dataSourceProperties = {
|
|||||||
}
|
}
|
||||||
database = {
|
database = {
|
||||||
transactionIsolationLevel = "REPEATABLE_READ"
|
transactionIsolationLevel = "REPEATABLE_READ"
|
||||||
|
exportHibernateJMXStatistics = "false"
|
||||||
}
|
}
|
||||||
devMode = true
|
devMode = true
|
||||||
useHTTPS = false
|
useHTTPS = false
|
||||||
|
@ -4,16 +4,19 @@ import net.corda.core.concurrent.CordaFuture
|
|||||||
import net.corda.core.internal.div
|
import net.corda.core.internal.div
|
||||||
import net.corda.core.internal.list
|
import net.corda.core.internal.list
|
||||||
import net.corda.core.internal.readLines
|
import net.corda.core.internal.readLines
|
||||||
|
import net.corda.core.utilities.NetworkHostAndPort
|
||||||
import net.corda.core.utilities.getOrThrow
|
import net.corda.core.utilities.getOrThrow
|
||||||
import net.corda.node.internal.NodeStartup
|
import net.corda.node.internal.NodeStartup
|
||||||
import net.corda.testing.DUMMY_BANK_A
|
import net.corda.testing.DUMMY_BANK_A
|
||||||
import net.corda.testing.DUMMY_NOTARY
|
import net.corda.testing.DUMMY_NOTARY
|
||||||
import net.corda.testing.DUMMY_REGULATOR
|
import net.corda.testing.DUMMY_REGULATOR
|
||||||
import net.corda.testing.common.internal.ProjectStructure.projectRootDir
|
import net.corda.testing.common.internal.ProjectStructure.projectRootDir
|
||||||
|
import net.corda.testing.http.HttpApi
|
||||||
import net.corda.testing.internal.addressMustBeBound
|
import net.corda.testing.internal.addressMustBeBound
|
||||||
import net.corda.testing.internal.addressMustNotBeBound
|
import net.corda.testing.internal.addressMustNotBeBound
|
||||||
import net.corda.testing.node.NotarySpec
|
import net.corda.testing.node.NotarySpec
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
|
import org.json.simple.JSONObject
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.util.concurrent.Executors
|
import java.util.concurrent.Executors
|
||||||
import java.util.concurrent.ScheduledExecutorService
|
import java.util.concurrent.ScheduledExecutorService
|
||||||
@ -66,6 +69,20 @@ class DriverTests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `monitoring mode enables jolokia exporting of JMX metrics via HTTP JSON`() {
|
||||||
|
driver(jmxPolicy = JmxPolicy(true)) {
|
||||||
|
// start another node so we gain access to node JMX metrics
|
||||||
|
startNode(providedName = DUMMY_REGULATOR.name).getOrThrow()
|
||||||
|
|
||||||
|
val webAddress = NetworkHostAndPort("localhost", 7006)
|
||||||
|
// request access to some JMX metrics via Jolokia HTTP/JSON
|
||||||
|
val api = HttpApi.fromHostAndPort(webAddress, "/jolokia/")
|
||||||
|
val versionAsJson = api.getJson<JSONObject>("/jolokia/version/")
|
||||||
|
assertThat(versionAsJson.getValue("status")).isEqualTo(200)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `started node, which is not waited for in the driver, is shutdown when the driver exits`() {
|
fun `started node, which is not waited for in the driver, is shutdown when the driver exits`() {
|
||||||
// First check that the process-id file is created by the node on startup, so that we can be sure our check that
|
// First check that the process-id file is created by the node on startup, so that we can be sure our check that
|
||||||
|
@ -128,6 +128,10 @@ data class NodeParameters(
|
|||||||
fun setMaximumHeapSize(maximumHeapSize: String) = copy(maximumHeapSize = maximumHeapSize)
|
fun setMaximumHeapSize(maximumHeapSize: String) = copy(maximumHeapSize = maximumHeapSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data class JmxPolicy(val startJmxHttpServer: Boolean = false,
|
||||||
|
val jmxHttpServerPortAllocation: PortAllocation? =
|
||||||
|
if (startJmxHttpServer) PortAllocation.Incremental(7005) else null)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* [driver] allows one to start up nodes like this:
|
* [driver] allows one to start up nodes like this:
|
||||||
* driver {
|
* driver {
|
||||||
@ -155,6 +159,9 @@ data class NodeParameters(
|
|||||||
* not. Note that this may be overridden in [DriverDSL.startNode].
|
* not. Note that this may be overridden in [DriverDSL.startNode].
|
||||||
* @param notarySpecs The notaries advertised for this network. These nodes will be started automatically and will be
|
* @param notarySpecs The notaries advertised for this network. These nodes will be started automatically and will be
|
||||||
* available from [DriverDSL.notaryHandles]. Defaults to a simple validating notary.
|
* available from [DriverDSL.notaryHandles]. Defaults to a simple validating notary.
|
||||||
|
* @param jmxPolicy Used to specify whether to expose JMX metrics via Jolokia HHTP/JSON. Defines two attributes:
|
||||||
|
* startJmxHttpServer: indicates whether the spawned nodes should start with a Jolokia JMX agent to enable remote JMX monitoring using HTTP/JSON.
|
||||||
|
* jmxHttpServerPortAllocation: the port allocation strategy to use for remote Jolokia/JMX monitoring over HTTP. Defaults to incremental.
|
||||||
* @param dsl The dsl itself.
|
* @param dsl The dsl itself.
|
||||||
* @return The value returned in the [dsl] closure.
|
* @return The value returned in the [dsl] closure.
|
||||||
*/
|
*/
|
||||||
@ -171,6 +178,7 @@ fun <A> driver(
|
|||||||
waitForAllNodesToFinish: Boolean = defaultParameters.waitForAllNodesToFinish,
|
waitForAllNodesToFinish: Boolean = defaultParameters.waitForAllNodesToFinish,
|
||||||
notarySpecs: List<NotarySpec> = defaultParameters.notarySpecs,
|
notarySpecs: List<NotarySpec> = defaultParameters.notarySpecs,
|
||||||
extraCordappPackagesToScan: List<String> = defaultParameters.extraCordappPackagesToScan,
|
extraCordappPackagesToScan: List<String> = defaultParameters.extraCordappPackagesToScan,
|
||||||
|
jmxPolicy: JmxPolicy = JmxPolicy(),
|
||||||
dsl: DriverDSL.() -> A
|
dsl: DriverDSL.() -> A
|
||||||
): A {
|
): A {
|
||||||
return genericDriver(
|
return genericDriver(
|
||||||
@ -184,7 +192,8 @@ fun <A> driver(
|
|||||||
startNodesInProcess = startNodesInProcess,
|
startNodesInProcess = startNodesInProcess,
|
||||||
waitForNodesToFinish = waitForAllNodesToFinish,
|
waitForNodesToFinish = waitForAllNodesToFinish,
|
||||||
notarySpecs = notarySpecs,
|
notarySpecs = notarySpecs,
|
||||||
extraCordappPackagesToScan = extraCordappPackagesToScan
|
extraCordappPackagesToScan = extraCordappPackagesToScan,
|
||||||
|
jmxPolicy = jmxPolicy
|
||||||
),
|
),
|
||||||
coerce = { it },
|
coerce = { it },
|
||||||
dsl = dsl,
|
dsl = dsl,
|
||||||
@ -219,7 +228,9 @@ data class DriverParameters(
|
|||||||
val startNodesInProcess: Boolean = false,
|
val startNodesInProcess: Boolean = false,
|
||||||
val waitForAllNodesToFinish: Boolean = false,
|
val waitForAllNodesToFinish: Boolean = false,
|
||||||
val notarySpecs: List<NotarySpec> = listOf(NotarySpec(DUMMY_NOTARY.name)),
|
val notarySpecs: List<NotarySpec> = listOf(NotarySpec(DUMMY_NOTARY.name)),
|
||||||
val extraCordappPackagesToScan: List<String> = emptyList()
|
val extraCordappPackagesToScan: List<String> = emptyList(),
|
||||||
|
val jmxPolicy: JmxPolicy = JmxPolicy()
|
||||||
|
|
||||||
) {
|
) {
|
||||||
fun setIsDebug(isDebug: Boolean) = copy(isDebug = isDebug)
|
fun setIsDebug(isDebug: Boolean) = copy(isDebug = isDebug)
|
||||||
fun setDriverDirectory(driverDirectory: Path) = copy(driverDirectory = driverDirectory)
|
fun setDriverDirectory(driverDirectory: Path) = copy(driverDirectory = driverDirectory)
|
||||||
@ -232,4 +243,5 @@ data class DriverParameters(
|
|||||||
fun setWaitForAllNodesToFinish(waitForAllNodesToFinish: Boolean) = copy(waitForAllNodesToFinish = waitForAllNodesToFinish)
|
fun setWaitForAllNodesToFinish(waitForAllNodesToFinish: Boolean) = copy(waitForAllNodesToFinish = waitForAllNodesToFinish)
|
||||||
fun setExtraCordappPackagesToScan(extraCordappPackagesToScan: List<String>) = copy(extraCordappPackagesToScan = extraCordappPackagesToScan)
|
fun setExtraCordappPackagesToScan(extraCordappPackagesToScan: List<String>) = copy(extraCordappPackagesToScan = extraCordappPackagesToScan)
|
||||||
fun setNotarySpecs(notarySpecs: List<NotarySpec>) = copy(notarySpecs = notarySpecs)
|
fun setNotarySpecs(notarySpecs: List<NotarySpec>) = copy(notarySpecs = notarySpecs)
|
||||||
|
fun setJmxPolicy(jmxPolicy: JmxPolicy) = copy(jmxPolicy = jmxPolicy)
|
||||||
}
|
}
|
||||||
|
@ -72,6 +72,7 @@ class DriverDSLImpl(
|
|||||||
val startNodesInProcess: Boolean,
|
val startNodesInProcess: Boolean,
|
||||||
val waitForNodesToFinish: Boolean,
|
val waitForNodesToFinish: Boolean,
|
||||||
extraCordappPackagesToScan: List<String>,
|
extraCordappPackagesToScan: List<String>,
|
||||||
|
val jmxPolicy: JmxPolicy,
|
||||||
val notarySpecs: List<NotarySpec>
|
val notarySpecs: List<NotarySpec>
|
||||||
) : InternalDriverDSL {
|
) : InternalDriverDSL {
|
||||||
private var _executorService: ScheduledExecutorService? = null
|
private var _executorService: ScheduledExecutorService? = null
|
||||||
@ -96,11 +97,25 @@ class DriverDSLImpl(
|
|||||||
|
|
||||||
//TODO: remove this once we can bundle quasar properly.
|
//TODO: remove this once we can bundle quasar properly.
|
||||||
private val quasarJarPath: String by lazy {
|
private val quasarJarPath: String by lazy {
|
||||||
val cl = ClassLoader.getSystemClassLoader()
|
resolveJar(".*quasar.*\\.jar$")
|
||||||
val urls = (cl as URLClassLoader).urLs
|
}
|
||||||
val quasarPattern = ".*quasar.*\\.jar$".toRegex()
|
|
||||||
val quasarFileUrl = urls.first { quasarPattern.matches(it.path) }
|
private val jolokiaJarPath: String by lazy {
|
||||||
Paths.get(quasarFileUrl.toURI()).toString()
|
resolveJar(".*jolokia-jvm-.*-agent\\.jar$")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun resolveJar(jarNamePattern: String): String {
|
||||||
|
return try {
|
||||||
|
val cl = ClassLoader.getSystemClassLoader()
|
||||||
|
val urls = (cl as URLClassLoader).urLs
|
||||||
|
val jarPattern = jarNamePattern.toRegex()
|
||||||
|
val jarFileUrl = urls.first { jarPattern.matches(it.path) }
|
||||||
|
Paths.get(jarFileUrl.toURI()).toString()
|
||||||
|
}
|
||||||
|
catch(e: Exception) {
|
||||||
|
log.warn("Unable to locate JAR `$jarNamePattern` on classpath: ${e.message}", e)
|
||||||
|
throw e
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun shutdown() {
|
override fun shutdown() {
|
||||||
@ -380,7 +395,8 @@ class DriverDSLImpl(
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
val debugPort = if (isDebug) debugPortAllocation.nextPort() else null
|
val debugPort = if (isDebug) debugPortAllocation.nextPort() else null
|
||||||
val process = startOutOfProcessNode(configuration, config, quasarJarPath, debugPort, systemProperties, cordappPackages, maximumHeapSize)
|
val monitorPort = if (jmxPolicy.startJmxHttpServer) jmxPolicy.jmxHttpServerPortAllocation?.nextPort() else null
|
||||||
|
val process = startOutOfProcessNode(configuration, config, quasarJarPath, debugPort, jolokiaJarPath, monitorPort, systemProperties, cordappPackages, maximumHeapSize)
|
||||||
if (waitForNodesToFinish) {
|
if (waitForNodesToFinish) {
|
||||||
state.locked {
|
state.locked {
|
||||||
processes += process
|
processes += process
|
||||||
@ -473,11 +489,13 @@ class DriverDSLImpl(
|
|||||||
config: Config,
|
config: Config,
|
||||||
quasarJarPath: String,
|
quasarJarPath: String,
|
||||||
debugPort: Int?,
|
debugPort: Int?,
|
||||||
|
jolokiaJarPath: String,
|
||||||
|
monitorPort: Int?,
|
||||||
overriddenSystemProperties: Map<String, String>,
|
overriddenSystemProperties: Map<String, String>,
|
||||||
cordappPackages: List<String>,
|
cordappPackages: List<String>,
|
||||||
maximumHeapSize: String
|
maximumHeapSize: String
|
||||||
): Process {
|
): Process {
|
||||||
log.info("Starting out-of-process Node ${nodeConf.myLegalName.organisation}, debug port is " + (debugPort ?: "not enabled"))
|
log.info("Starting out-of-process Node ${nodeConf.myLegalName.organisation}, debug port is " + (debugPort ?: "not enabled") + ", jolokia monitoring port is " + (monitorPort ?: "not enabled"))
|
||||||
// Write node.conf
|
// Write node.conf
|
||||||
writeConfig(nodeConf.baseDirectory, "node.conf", config)
|
writeConfig(nodeConf.baseDirectory, "node.conf", config)
|
||||||
|
|
||||||
@ -498,6 +516,7 @@ class DriverDSLImpl(
|
|||||||
"org.objenesis**;org.slf4j**;org.w3c**;org.xml**;org.yaml**;reflectasm**;rx**)"
|
"org.objenesis**;org.slf4j**;org.w3c**;org.xml**;org.yaml**;reflectasm**;rx**)"
|
||||||
val extraJvmArguments = systemProperties.removeResolvedClasspath().map { "-D${it.key}=${it.value}" } +
|
val extraJvmArguments = systemProperties.removeResolvedClasspath().map { "-D${it.key}=${it.value}" } +
|
||||||
"-javaagent:$quasarJarPath=$excludePattern"
|
"-javaagent:$quasarJarPath=$excludePattern"
|
||||||
|
val jolokiaAgent = monitorPort?.let { "-javaagent:$jolokiaJarPath=port=$monitorPort,host=localhost" }
|
||||||
val loggingLevel = if (debugPort == null) "INFO" else "DEBUG"
|
val loggingLevel = if (debugPort == null) "INFO" else "DEBUG"
|
||||||
|
|
||||||
return ProcessUtilities.startCordaProcess(
|
return ProcessUtilities.startCordaProcess(
|
||||||
@ -508,7 +527,7 @@ class DriverDSLImpl(
|
|||||||
"--no-local-shell"
|
"--no-local-shell"
|
||||||
),
|
),
|
||||||
jdwpPort = debugPort,
|
jdwpPort = debugPort,
|
||||||
extraJvmArguments = extraJvmArguments,
|
extraJvmArguments = extraJvmArguments + listOfNotNull(jolokiaAgent),
|
||||||
errorLogPath = nodeConf.baseDirectory / NodeStartup.LOGS_DIRECTORY_NAME / "error.log",
|
errorLogPath = nodeConf.baseDirectory / NodeStartup.LOGS_DIRECTORY_NAME / "error.log",
|
||||||
workingDirectory = nodeConf.baseDirectory,
|
workingDirectory = nodeConf.baseDirectory,
|
||||||
maximumHeapSize = maximumHeapSize
|
maximumHeapSize = maximumHeapSize
|
||||||
@ -641,6 +660,7 @@ fun <DI : DriverDSL, D : InternalDriverDSL, A> genericDriver(
|
|||||||
startNodesInProcess: Boolean = defaultParameters.startNodesInProcess,
|
startNodesInProcess: Boolean = defaultParameters.startNodesInProcess,
|
||||||
notarySpecs: List<NotarySpec>,
|
notarySpecs: List<NotarySpec>,
|
||||||
extraCordappPackagesToScan: List<String> = defaultParameters.extraCordappPackagesToScan,
|
extraCordappPackagesToScan: List<String> = defaultParameters.extraCordappPackagesToScan,
|
||||||
|
jmxPolicy: JmxPolicy = JmxPolicy(),
|
||||||
driverDslWrapper: (DriverDSLImpl) -> D,
|
driverDslWrapper: (DriverDSLImpl) -> D,
|
||||||
coerce: (D) -> DI, dsl: DI.() -> A
|
coerce: (D) -> DI, dsl: DI.() -> A
|
||||||
): A {
|
): A {
|
||||||
@ -656,6 +676,7 @@ fun <DI : DriverDSL, D : InternalDriverDSL, A> genericDriver(
|
|||||||
startNodesInProcess = startNodesInProcess,
|
startNodesInProcess = startNodesInProcess,
|
||||||
waitForNodesToFinish = waitForNodesToFinish,
|
waitForNodesToFinish = waitForNodesToFinish,
|
||||||
extraCordappPackagesToScan = extraCordappPackagesToScan,
|
extraCordappPackagesToScan = extraCordappPackagesToScan,
|
||||||
|
jmxPolicy = jmxPolicy,
|
||||||
notarySpecs = notarySpecs
|
notarySpecs = notarySpecs
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -89,6 +89,7 @@ val fakeNodeLegalName = CordaX500Name(organisation = "Not:a:real:name", locality
|
|||||||
// Use a global pool so that we can run RPC tests in parallel
|
// Use a global pool so that we can run RPC tests in parallel
|
||||||
private val globalPortAllocation = PortAllocation.Incremental(10000)
|
private val globalPortAllocation = PortAllocation.Incremental(10000)
|
||||||
private val globalDebugPortAllocation = PortAllocation.Incremental(5005)
|
private val globalDebugPortAllocation = PortAllocation.Incremental(5005)
|
||||||
|
private val globalMonitorPortAllocation = PortAllocation.Incremental(7005)
|
||||||
fun <A> rpcDriver(
|
fun <A> rpcDriver(
|
||||||
isDebug: Boolean = false,
|
isDebug: Boolean = false,
|
||||||
driverDirectory: Path = Paths.get("build", getTimestampAsDirectoryName()),
|
driverDirectory: Path = Paths.get("build", getTimestampAsDirectoryName()),
|
||||||
@ -101,28 +102,29 @@ fun <A> rpcDriver(
|
|||||||
extraCordappPackagesToScan: List<String> = emptyList(),
|
extraCordappPackagesToScan: List<String> = emptyList(),
|
||||||
notarySpecs: List<NotarySpec> = emptyList(),
|
notarySpecs: List<NotarySpec> = emptyList(),
|
||||||
externalTrace: Trace? = null,
|
externalTrace: Trace? = null,
|
||||||
|
jmxPolicy: JmxPolicy = JmxPolicy(),
|
||||||
dsl: RPCDriverDSL.() -> A
|
dsl: RPCDriverDSL.() -> A
|
||||||
): A {
|
) : A {
|
||||||
return genericDriver(
|
return genericDriver(
|
||||||
driverDsl = RPCDriverDSL(
|
driverDsl = RPCDriverDSL(
|
||||||
DriverDSLImpl(
|
DriverDSLImpl(
|
||||||
portAllocation = portAllocation,
|
portAllocation = portAllocation,
|
||||||
debugPortAllocation = debugPortAllocation,
|
debugPortAllocation = debugPortAllocation,
|
||||||
systemProperties = systemProperties,
|
systemProperties = systemProperties,
|
||||||
driverDirectory = driverDirectory.toAbsolutePath(),
|
driverDirectory = driverDirectory.toAbsolutePath(),
|
||||||
useTestClock = useTestClock,
|
useTestClock = useTestClock,
|
||||||
isDebug = isDebug,
|
isDebug = isDebug,
|
||||||
startNodesInProcess = startNodesInProcess,
|
startNodesInProcess = startNodesInProcess,
|
||||||
waitForNodesToFinish = waitForNodesToFinish,
|
waitForNodesToFinish = waitForNodesToFinish,
|
||||||
extraCordappPackagesToScan = extraCordappPackagesToScan,
|
extraCordappPackagesToScan = extraCordappPackagesToScan,
|
||||||
notarySpecs = notarySpecs
|
notarySpecs = notarySpecs,
|
||||||
), externalTrace
|
jmxPolicy = jmxPolicy
|
||||||
),
|
), externalTrace
|
||||||
coerce = { it },
|
),
|
||||||
dsl = dsl,
|
coerce = { it },
|
||||||
initialiseSerialization = false
|
dsl = dsl,
|
||||||
)
|
initialiseSerialization = false
|
||||||
}
|
)}
|
||||||
|
|
||||||
private class SingleUserSecurityManager(val rpcUser: User) : ActiveMQSecurityManager3 {
|
private class SingleUserSecurityManager(val rpcUser: User) : ActiveMQSecurityManager3 {
|
||||||
override fun validateUser(user: String?, password: String?) = isValid(user, password)
|
override fun validateUser(user: String?, password: String?) = isValid(user, password)
|
||||||
|
@ -8,6 +8,7 @@ import net.corda.core.internal.concurrent.flatMap
|
|||||||
import net.corda.core.internal.concurrent.transpose
|
import net.corda.core.internal.concurrent.transpose
|
||||||
import net.corda.core.utilities.NetworkHostAndPort
|
import net.corda.core.utilities.NetworkHostAndPort
|
||||||
import net.corda.core.utilities.getOrThrow
|
import net.corda.core.utilities.getOrThrow
|
||||||
|
import net.corda.testing.driver.JmxPolicy
|
||||||
import net.corda.testing.internal.DriverDSLImpl
|
import net.corda.testing.internal.DriverDSLImpl
|
||||||
import net.corda.testing.driver.PortAllocation
|
import net.corda.testing.driver.PortAllocation
|
||||||
import net.corda.testing.driver.driver
|
import net.corda.testing.driver.driver
|
||||||
@ -41,6 +42,7 @@ private fun CordformDefinition.runNodes(waitForAllNodesToFinish: Boolean, block:
|
|||||||
.max()!!
|
.max()!!
|
||||||
driver(
|
driver(
|
||||||
isDebug = true,
|
isDebug = true,
|
||||||
|
jmxPolicy = JmxPolicy(true),
|
||||||
driverDirectory = nodesDirectory,
|
driverDirectory = nodesDirectory,
|
||||||
extraCordappPackagesToScan = cordappPackages,
|
extraCordappPackagesToScan = cordappPackages,
|
||||||
// Notaries are manually specified in Cordform so we don't want the driver automatically starting any
|
// Notaries are manually specified in Cordform so we don't want the driver automatically starting any
|
||||||
|
@ -24,6 +24,7 @@ import net.corda.node.services.Permissions.Companion.startFlow
|
|||||||
import net.corda.nodeapi.internal.config.User
|
import net.corda.nodeapi.internal.config.User
|
||||||
import net.corda.testing.ALICE
|
import net.corda.testing.ALICE
|
||||||
import net.corda.testing.BOB
|
import net.corda.testing.BOB
|
||||||
|
import net.corda.testing.driver.JmxPolicy
|
||||||
import net.corda.testing.driver.NodeHandle
|
import net.corda.testing.driver.NodeHandle
|
||||||
import net.corda.testing.driver.PortAllocation
|
import net.corda.testing.driver.PortAllocation
|
||||||
import net.corda.testing.driver.driver
|
import net.corda.testing.driver.driver
|
||||||
@ -64,14 +65,14 @@ class ExplorerSimulation(private val options: OptionSet) {
|
|||||||
|
|
||||||
private fun startDemoNodes() {
|
private fun startDemoNodes() {
|
||||||
val portAllocation = PortAllocation.Incremental(20000)
|
val portAllocation = PortAllocation.Incremental(20000)
|
||||||
driver(portAllocation = portAllocation, extraCordappPackagesToScan = listOf("net.corda.finance"), waitForAllNodesToFinish = true) {
|
driver(portAllocation = portAllocation, extraCordappPackagesToScan = listOf("net.corda.finance"), waitForAllNodesToFinish = true, jmxPolicy = JmxPolicy(true)) {
|
||||||
// TODO : Supported flow should be exposed somehow from the node instead of set of ServiceInfo.
|
// TODO : Supported flow should be exposed somehow from the node instead of set of ServiceInfo.
|
||||||
val alice = startNode(providedName = ALICE.name, rpcUsers = listOf(user))
|
val alice = startNode(providedName = ALICE.name, rpcUsers = listOf(user), customOverrides = mapOf("devMode" to "true"))
|
||||||
val bob = startNode(providedName = BOB.name, rpcUsers = listOf(user))
|
val bob = startNode(providedName = BOB.name, rpcUsers = listOf(user))
|
||||||
val ukBankName = CordaX500Name(organisation = "UK Bank Plc", locality = "London", country = "GB")
|
val ukBankName = CordaX500Name(organisation = "UK Bank Plc", locality = "London", country = "GB")
|
||||||
val usaBankName = CordaX500Name(organisation = "USA Bank Corp", locality = "New York", country = "US")
|
val usaBankName = CordaX500Name(organisation = "USA Bank Corp", locality = "New York", country = "US")
|
||||||
val issuerGBP = startNode(providedName = ukBankName, rpcUsers = listOf(manager),
|
val issuerGBP = startNode(providedName = ukBankName, rpcUsers = listOf(manager),
|
||||||
customOverrides = mapOf("issuableCurrencies" to listOf("GBP")))
|
customOverrides = mapOf("issuableCurrencies" to listOf("GBP"), "" to "true"))
|
||||||
val issuerUSD = startNode(providedName = usaBankName, rpcUsers = listOf(manager),
|
val issuerUSD = startNode(providedName = usaBankName, rpcUsers = listOf(manager),
|
||||||
customOverrides = mapOf("issuableCurrencies" to listOf("USD")))
|
customOverrides = mapOf("issuableCurrencies" to listOf("USD")))
|
||||||
|
|
||||||
|
@ -22,6 +22,7 @@ import net.corda.nodeapi.VerifierApi
|
|||||||
import net.corda.nodeapi.internal.config.NodeSSLConfiguration
|
import net.corda.nodeapi.internal.config.NodeSSLConfiguration
|
||||||
import net.corda.nodeapi.internal.config.SSLConfiguration
|
import net.corda.nodeapi.internal.config.SSLConfiguration
|
||||||
import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.NODE_USER
|
import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.NODE_USER
|
||||||
|
import net.corda.testing.driver.JmxPolicy
|
||||||
import net.corda.testing.driver.NodeHandle
|
import net.corda.testing.driver.NodeHandle
|
||||||
import net.corda.testing.driver.PortAllocation
|
import net.corda.testing.driver.PortAllocation
|
||||||
import net.corda.testing.driver.driver
|
import net.corda.testing.driver.driver
|
||||||
@ -59,6 +60,7 @@ fun <A> verifierDriver(
|
|||||||
waitForNodesToFinish: Boolean = false,
|
waitForNodesToFinish: Boolean = false,
|
||||||
extraCordappPackagesToScan: List<String> = emptyList(),
|
extraCordappPackagesToScan: List<String> = emptyList(),
|
||||||
notarySpecs: List<NotarySpec> = emptyList(),
|
notarySpecs: List<NotarySpec> = emptyList(),
|
||||||
|
jmxPolicy: JmxPolicy = JmxPolicy(),
|
||||||
dsl: VerifierDriverDSL.() -> A
|
dsl: VerifierDriverDSL.() -> A
|
||||||
) = genericDriver(
|
) = genericDriver(
|
||||||
driverDsl = VerifierDriverDSL(
|
driverDsl = VerifierDriverDSL(
|
||||||
@ -72,7 +74,8 @@ fun <A> verifierDriver(
|
|||||||
startNodesInProcess = startNodesInProcess,
|
startNodesInProcess = startNodesInProcess,
|
||||||
waitForNodesToFinish = waitForNodesToFinish,
|
waitForNodesToFinish = waitForNodesToFinish,
|
||||||
extraCordappPackagesToScan = extraCordappPackagesToScan,
|
extraCordappPackagesToScan = extraCordappPackagesToScan,
|
||||||
notarySpecs = notarySpecs
|
notarySpecs = notarySpecs,
|
||||||
|
jmxPolicy = jmxPolicy
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
coerce = { it },
|
coerce = { it },
|
||||||
|
@ -38,7 +38,7 @@ dependencies {
|
|||||||
compile "org.eclipse.jetty:jetty-servlet:$jetty_version"
|
compile "org.eclipse.jetty:jetty-servlet:$jetty_version"
|
||||||
compile "org.eclipse.jetty:jetty-webapp:$jetty_version"
|
compile "org.eclipse.jetty:jetty-webapp:$jetty_version"
|
||||||
compile "javax.servlet:javax.servlet-api:3.1.0"
|
compile "javax.servlet:javax.servlet-api:3.1.0"
|
||||||
compile "org.jolokia:jolokia-agent-war:$jolokia_version"
|
compile "org.jolokia:jolokia-war:$jolokia_version"
|
||||||
compile "commons-fileupload:commons-fileupload:$fileupload_version"
|
compile "commons-fileupload:commons-fileupload:$fileupload_version"
|
||||||
|
|
||||||
// Log4J: logging framework (with SLF4J bindings)
|
// Log4J: logging framework (with SLF4J bindings)
|
||||||
|
@ -58,7 +58,7 @@ class NodeWebServer(val config: WebServerConfig) {
|
|||||||
// Export JMX monitoring statistics and data over REST/JSON.
|
// Export JMX monitoring statistics and data over REST/JSON.
|
||||||
if (config.exportJMXto.split(',').contains("http")) {
|
if (config.exportJMXto.split(',').contains("http")) {
|
||||||
val classpath = System.getProperty("java.class.path").split(System.getProperty("path.separator"))
|
val classpath = System.getProperty("java.class.path").split(System.getProperty("path.separator"))
|
||||||
val warpath = classpath.firstOrNull { it.contains("jolokia-agent-war-2") && it.endsWith(".war") }
|
val warpath = classpath.firstOrNull { it.contains("jolokia-war") && it.endsWith(".war") }
|
||||||
if (warpath != null) {
|
if (warpath != null) {
|
||||||
handlerCollection.addHandler(WebAppContext().apply {
|
handlerCollection.addHandler(WebAppContext().apply {
|
||||||
// Find the jolokia WAR file on the classpath.
|
// Find the jolokia WAR file on the classpath.
|
||||||
|
Loading…
Reference in New Issue
Block a user