CORDA-2050 Upgrade Corda to Java 11 (compatibility mode) (#5356)

Upgrade Corda to run with Java 11 (compatibility mode) - see https://github.com/corda/corda/pull/5356
This commit is contained in:
josecoll 2019-09-18 10:26:26 +01:00 committed by GitHub
parent 75e66f9db9
commit 3fafbe551c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
105 changed files with 971 additions and 522 deletions

View File

@ -1,5 +1,8 @@
import net.corda.testing.DistributedTesting import net.corda.testing.DistributedTesting
import static org.gradle.api.JavaVersion.VERSION_1_8
import static org.gradle.api.JavaVersion.VERSION_11
buildscript { buildscript {
// For sharing constants between builds // For sharing constants between builds
Properties constants = new Properties() Properties constants = new Properties()
@ -17,7 +20,15 @@ buildscript {
ext.warnings_as_errors = project.hasProperty("compilation.warningsAsErrors") ? project.property("compilation.warningsAsErrors").toBoolean() : false ext.warnings_as_errors = project.hasProperty("compilation.warningsAsErrors") ? project.property("compilation.warningsAsErrors").toBoolean() : false
ext.quasar_group = 'co.paralleluniverse' ext.quasar_group = 'co.paralleluniverse'
ext.quasar_version = constants.getProperty("quasarVersion") // Set version of Quasar according to version of Java used:
if (JavaVersion.current() == JavaVersion.VERSION_11) {
ext.quasar_version = constants.getProperty("quasarVersion11")
ext.quasar_classifier = constants.getProperty("quasarClassifier11")
}
else {
ext.quasar_version = constants.getProperty("quasarVersion")
ext.quasar_classifier = constants.getProperty("quasarClassifier")
}
ext.quasar_exclusions = [ ext.quasar_exclusions = [
'co.paralleluniverse**', 'co.paralleluniverse**',
'groovy**', 'groovy**',
@ -80,7 +91,7 @@ buildscript {
ext.h2_version = '1.4.199' // Update docs if renamed or removed. ext.h2_version = '1.4.199' // Update docs if renamed or removed.
ext.postgresql_version = '42.2.5' ext.postgresql_version = '42.2.5'
ext.rxjava_version = '1.3.8' ext.rxjava_version = '1.3.8'
ext.dokka_version = '0.9.17' ext.dokka_version = '0.9.18'
ext.eddsa_version = '0.2.0' ext.eddsa_version = '0.2.0'
ext.dependency_checker_version = '5.2.0' ext.dependency_checker_version = '5.2.0'
ext.commons_collections_version = '4.3' ext.commons_collections_version = '4.3'
@ -96,7 +107,6 @@ buildscript {
ext.docker_compose_rule_version = '0.35.0' ext.docker_compose_rule_version = '0.35.0'
ext.selenium_version = '3.141.59' ext.selenium_version = '3.141.59'
ext.ghostdriver_version = '2.1.0' ext.ghostdriver_version = '2.1.0'
ext.eaagentloader_version = '1.0.3'
ext.proguard_version = constants.getProperty('proguardVersion') ext.proguard_version = constants.getProperty('proguardVersion')
ext.jsch_version = '0.1.55' ext.jsch_version = '0.1.55'
ext.protonj_version = '0.33.0' // Overide Artemis version ext.protonj_version = '0.33.0' // Overide Artemis version
@ -106,8 +116,8 @@ buildscript {
ext.picocli_version = '3.9.6' ext.picocli_version = '3.9.6'
ext.commons_io_version = '2.6' ext.commons_io_version = '2.6'
ext.controlsfx_version = '8.40.15' ext.controlsfx_version = '8.40.15'
ext.fontawesomefx_commons_version = '8.15' ext.fontawesomefx_commons_version = '11.0'
ext.fontawesomefx_fontawesome_version = '4.7.0-5' ext.fontawesomefx_fontawesome_version = '4.7.0-11'
// Name of the IntelliJ SDK created for the deterministic Java rt.jar. // Name of the IntelliJ SDK created for the deterministic Java rt.jar.
// ext.deterministic_idea_sdk = '1.8 (Deterministic)' // ext.deterministic_idea_sdk = '1.8 (Deterministic)'
@ -179,8 +189,17 @@ apply plugin: 'com.jfrog.artifactory'
// with the run configurations. It also doesn't realise that the project is a Java 8 project and misconfigures // with the run configurations. It also doesn't realise that the project is a Java 8 project and misconfigures
// the resulting import. This fixes it. // the resulting import. This fixes it.
apply plugin: 'java' apply plugin: 'java'
sourceCompatibility = 1.8
targetCompatibility = 1.8 println "Java version: " + JavaVersion.current()
sourceCompatibility = VERSION_1_8
if (JavaVersion.current() == JavaVersion.VERSION_1_8)
targetCompatibility = VERSION_1_8
else
targetCompatibility = VERSION_11
println "Java source compatibility: " + sourceCompatibility
println "Java target compatibility: " + targetCompatibility
println "Quasar version: " + quasar_version
println "Quasar classifier: " + quasar_classifier
allprojects { allprojects {
apply plugin: 'kotlin' apply plugin: 'kotlin'
@ -210,9 +229,17 @@ allprojects {
nugetconfEnabled = false nugetconfEnabled = false
} }
} }
sourceCompatibility = 1.8 sourceCompatibility = VERSION_1_8
targetCompatibility = 1.8 if (JavaVersion.current() == JavaVersion.VERSION_1_8)
targetCompatibility = VERSION_1_8
else
targetCompatibility = VERSION_11
jacoco {
// JDK11 official support (https://github.com/jacoco/jacoco/releases/tag/v0.8.3)
toolVersion = "0.8.3"
}
tasks.withType(JavaCompile) { tasks.withType(JavaCompile) {
options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation" << "-Xlint:-options" << "-parameters" options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation" << "-Xlint:-options" << "-parameters"
if (warnings_as_errors) { if (warnings_as_errors) {
@ -248,6 +275,7 @@ allprojects {
tasks.withType(Test) { tasks.withType(Test) {
forkEvery = 10 forkEvery = 10
ignoreFailures = project.hasProperty('tests.ignoreFailures') ? project.property('tests.ignoreFailures').toBoolean() : false
failFast = project.hasProperty('tests.failFast') ? project.property('tests.failFast').toBoolean() : false failFast = project.hasProperty('tests.failFast') ? project.property('tests.failFast').toBoolean() : false
// Prevent the project from creating temporary files outside of the build directory. // Prevent the project from creating temporary files outside of the build directory.

View File

@ -1,3 +1,17 @@
// JDK 11 JavaFX
plugins {
id 'org.openjfx.javafxplugin' version '0.0.7' apply false
}
if (JavaVersion.current().isJava9Compatible()) {
apply plugin: 'org.openjfx.javafxplugin'
javafx {
version = "11.0.2"
modules = ['javafx.controls',
'javafx.fxml'
]
}
}
apply plugin: 'kotlin' apply plugin: 'kotlin'
apply plugin: 'net.corda.plugins.quasar-utils' apply plugin: 'net.corda.plugins.quasar-utils'
apply plugin: 'net.corda.plugins.publish-utils' apply plugin: 'net.corda.plugins.publish-utils'

View File

@ -0,0 +1,19 @@
package net.corda.client.jfx.utils;
import javafx.collections.ObservableList;
import kotlin.jvm.functions.Function1;
import kotlin.jvm.functions.Function2;
import org.jetbrains.annotations.NotNull;
// Java 9 introduces a new abstract method that we need to override (without using the explicit Kotlin `override` keyword to be backwards compatible
// https://docs.oracle.com/javase/9/docs/api/javafx/collections/transformation/TransformationList.html#getViewIndex-int-
public class AggregatedList<A,E,K> extends AbstractAggregatedList<A,E,K> {
@SuppressWarnings("unchecked")
public AggregatedList(@NotNull ObservableList<? extends E> list, @NotNull Function1<E,K> toKey, @NotNull Function2<K,ObservableList<E>,A> assemble) {
super(list, toKey, assemble);
}
public int getViewIndex(int i) {
return 0;
}
}

View File

@ -0,0 +1,16 @@
package net.corda.client.jfx.utils;
import javafx.collections.ObservableList;
import org.jetbrains.annotations.NotNull;
// Java 9 introduces a new abstract method that we need to override (without using the explicit Kotlin `override` keyword to be backwards compatible
// https://docs.oracle.com/javase/9/docs/api/javafx/collections/transformation/TransformationList.html#getViewIndex-int-
public class ConcatenatedList<A> extends AbstractConcatenatedList<A> {
public ConcatenatedList(@NotNull ObservableList<ObservableList<A>> sourceList) {
super(sourceList);
}
public int getViewIndex(int i) {
return 0;
}
}

View File

@ -0,0 +1,17 @@
package net.corda.client.jfx.utils;
import javafx.collections.ObservableList;
import org.jetbrains.annotations.NotNull;
// Java 9 introduces a new abstract method that we need to override (without using the explicit Kotlin `override` keyword to be backwards compatible
// https://docs.oracle.com/javase/9/docs/api/javafx/collections/transformation/TransformationList.html#getViewIndex-int-
public class FlattenedList extends AbstractFlattenedList {
@SuppressWarnings("unchecked")
public FlattenedList(@NotNull ObservableList sourceList) {
super(sourceList);
}
public int getViewIndex(int i) {
return 0;
}
}

View File

@ -0,0 +1,17 @@
package net.corda.client.jfx.utils;
import javafx.collections.ObservableList;
import kotlin.jvm.functions.Function1;
import org.jetbrains.annotations.NotNull;
// Java 9 introduces a new abstract method that we need to override (without using the explicit Kotlin `override` keyword to be backwards compatible
// https://docs.oracle.com/javase/9/docs/api/javafx/collections/transformation/TransformationList.html#getViewIndex-int-
public class MappedList<A,B> extends AbstractMappedList<A,B> {
public MappedList(@NotNull ObservableList<A> list, @NotNull Function1<A,B> function) {
super(list, function);
}
public int getViewIndex(int i) {
return 0;
}
}

View File

@ -0,0 +1,16 @@
package net.corda.client.jfx.utils;
import javafx.collections.ObservableList;
import org.jetbrains.annotations.NotNull;
// Java 9 introduces a new abstract method that we need to override (without using the explicit Kotlin `override` keyword to be backwards compatible
// https://docs.oracle.com/javase/9/docs/api/javafx/collections/transformation/TransformationList.html#getViewIndex-int-
public class ReplayedList<A> extends AbstractReplayedList<A> {
public ReplayedList(@NotNull ObservableList<A> sourceList) {
super(sourceList);
}
public int getViewIndex(int i) {
return 0;
}
}

View File

@ -63,7 +63,7 @@ object Models {
*/ */
private val dependencyGraph = HashMap<KClass<*>, MutableSet<KClass<*>>>() private val dependencyGraph = HashMap<KClass<*>, MutableSet<KClass<*>>>()
fun <M : Any> initModel(klass: KClass<M>) = modelStore.getOrPut(klass) { klass.java.newInstance() } fun <M : Any> initModel(klass: KClass<M>) = modelStore.getOrPut(klass) { klass.java.getDeclaredConstructor().newInstance() }
fun <M : Any> get(klass: KClass<M>, origin: KClass<*>): M { fun <M : Any> get(klass: KClass<M>, origin: KClass<*>): M {
dependencyGraph.getOrPut(origin) { mutableSetOf() }.add(klass) dependencyGraph.getOrPut(origin) { mutableSetOf() }.add(klass)
val model = initModel(klass) val model = initModel(klass)

View File

@ -6,7 +6,7 @@ import javafx.collections.ObservableList
import javafx.collections.transformation.TransformationList import javafx.collections.transformation.TransformationList
/** /**
* Given an [ObservableList]<[E]> and a grouping key [K], [AggregatedList] groups the elements by the key into a fresh * Given an [ObservableList]<[E]> and a grouping key [K], [AbstractAggregatedList] groups the elements by the key into a fresh
* [ObservableList]<[E]> for each group and exposes the groups as an observable list of [A]s by calling [assemble] on each. * [ObservableList]<[E]> for each group and exposes the groups as an observable list of [A]s by calling [assemble] on each.
* *
* Changes done to elements of the input list are reflected in the observable list of the respective group, whereas * Changes done to elements of the input list are reflected in the observable list of the respective group, whereas
@ -36,8 +36,8 @@ import javafx.collections.transformation.TransformationList
* @param toKey Function to extract the key from an element. * @param toKey Function to extract the key from an element.
* @param assemble Function to assemble the aggregation into the exposed [A]. * @param assemble Function to assemble the aggregation into the exposed [A].
*/ */
class AggregatedList<A, E : Any, K : Any>( abstract class AbstractAggregatedList<A, E : Any, K : Any>(
list: ObservableList<out E>, val list: ObservableList<out E>,
val toKey: (E) -> K, val toKey: (E) -> K,
val assemble: (K, ObservableList<E>) -> A val assemble: (K, ObservableList<E>) -> A
) : TransformationList<A, E>(list) { ) : TransformationList<A, E>(list) {

View File

@ -7,13 +7,14 @@ import javafx.collections.transformation.TransformationList
import java.util.* import java.util.*
/** /**
* [ConcatenatedList] takes a list of lists and concatenates them. Any change to the underlying lists or the outer list * [AbstractConcatenatedList] takes a list of lists and concatenates them. Any change to the underlying lists or the outer list
* is propagated as expected. * is propagated as expected.
*/ */
class ConcatenatedList<A>(sourceList: ObservableList<ObservableList<A>>) : TransformationList<A, ObservableList<A>>(sourceList) { abstract class AbstractConcatenatedList<A>(sourceList: ObservableList<ObservableList<A>>) : TransformationList<A, ObservableList<A>>(sourceList) {
// A wrapper for input lists so we hash differently even if a list is reused in the input. // A wrapper for input lists so we hash differently even if a list is reused in the input.
@VisibleForTesting @VisibleForTesting
internal class WrappedObservableList<A>( class WrappedObservableList<A>(
val observableList: ObservableList<A> val observableList: ObservableList<A>
) )
// First let's clarify some concepts as it's easy to confuse which list we're handling where. // First let's clarify some concepts as it's easy to confuse which list we're handling where.
@ -37,9 +38,9 @@ class ConcatenatedList<A>(sourceList: ObservableList<ObservableList<A>>) : Trans
// Note that similar to 'nestedIndexOffsets', 'startingOffsetOf' also isn't a one-to-one mapping because of // Note that similar to 'nestedIndexOffsets', 'startingOffsetOf' also isn't a one-to-one mapping because of
// potentially several empty nested lists. // potentially several empty nested lists.
@VisibleForTesting @VisibleForTesting
internal val indexMap = HashMap<WrappedObservableList<out A>, Pair<Int, ListChangeListener<A>>>() val indexMap = HashMap<WrappedObservableList<out A>, Pair<Int, ListChangeListener<A>>>()
@VisibleForTesting @VisibleForTesting
internal val nestedIndexOffsets = ArrayList<Int>(sourceList.size) val nestedIndexOffsets = ArrayList<Int>(sourceList.size)
init { init {
var offset = 0 var offset = 0

View File

@ -8,11 +8,10 @@ import javafx.collections.transformation.TransformationList
import java.util.* import java.util.*
/** /**
* [FlattenedList] flattens the passed in list of [ObservableValue]s so that changes in individual updates to the values * [AbstractFlattenedList] flattens the passed in list of [ObservableValue]s so that changes in individual updates to the values
* are reflected in the exposed list as expected. * are reflected in the exposed list as expected.
*/ */
class FlattenedList<A>(val sourceList: ObservableList<out ObservableValue<out A>>) : TransformationList<A, ObservableValue<out A>>(sourceList) { abstract class AbstractFlattenedList<A>(val sourceList: ObservableList<out ObservableValue<out A>>) : TransformationList<A, ObservableValue<out A>>(sourceList) {
/** /**
* We maintain an ObservableValue->index map. This is needed because we need the ObservableValue's index in order to * We maintain an ObservableValue->index map. This is needed because we need the ObservableValue's index in order to
* propagate a change and if the listener closure captures the index at the time of the call to * propagate a change and if the listener closure captures the index at the time of the call to

View File

@ -10,7 +10,8 @@ import java.util.*
* when an element is inserted or updated. * when an element is inserted or updated.
* Use this instead of [EasyBind.map] to trade off memory vs CPU, or if (god forbid) the mapped function is side-effecting. * Use this instead of [EasyBind.map] to trade off memory vs CPU, or if (god forbid) the mapped function is side-effecting.
*/ */
class MappedList<A, B>(list: ObservableList<A>, val function: (A) -> B) : TransformationList<B, A>(list) { abstract class AbstractMappedList<A, B>(list: ObservableList<A>, val function: (A) -> B) : TransformationList<B, A>(list) {
private val backingList = ArrayList<B>(list.size) private val backingList = ArrayList<B>(list.size)
init { init {

View File

@ -9,7 +9,7 @@ import java.util.*
* This list type just replays changes propagated from the underlying source list. Used for testing changes and backing a * This list type just replays changes propagated from the underlying source list. Used for testing changes and backing a
* non-backed observable * non-backed observable
*/ */
class ReplayedList<A>(sourceList: ObservableList<A>) : TransformationList<A, A>(sourceList) { abstract class AbstractReplayedList<A>(sourceList: ObservableList<A>) : TransformationList<A, A>(sourceList) {
val replayedList = ArrayList<A>(sourceList) val replayedList = ArrayList<A>(sourceList)

View File

@ -143,14 +143,16 @@ fun <A, B> ObservableList<out A>.foldObservable(initial: B, folderFunction: (B,
* val people: ObservableList<Person> = (..) * val people: ObservableList<Person> = (..)
* val heights: ObservableList<Long> = people.map(Person::height).flatten() * val heights: ObservableList<Long> = people.map(Person::height).flatten()
*/ */
fun <A> ObservableList<out ObservableValue<out A>>.flatten(): ObservableList<A> = FlattenedList(this) @Suppress("UNCHECKED_CAST")
fun <A> ObservableList<out ObservableValue<out A>>.flatten(): ObservableList<A> = FlattenedList(this) as ObservableList<A>
/** /**
* data class Person(val height: ObservableValue<Long>) * data class Person(val height: ObservableValue<Long>)
* val people: List<Person> = listOf(alice, bob) * val people: List<Person> = listOf(alice, bob)
* val heights: ObservableList<Long> = people.map(Person::height).sequence() * val heights: ObservableList<Long> = people.map(Person::height).sequence()
*/ */
fun <A> Collection<ObservableValue<out A>>.sequence(): ObservableList<A> = FlattenedList(FXCollections.observableArrayList(this)) @Suppress("UNCHECKED_CAST")
fun <A> Collection<ObservableValue<out A>>.sequence(): ObservableList<A> = FlattenedList(FXCollections.observableArrayList(this)) as ObservableList<A>
/** /**
* data class Person(val height: Long) * data class Person(val height: Long)
@ -173,9 +175,12 @@ fun <K, A> ObservableList<out A>.associateBy(toKey: (A) -> K): ObservableMap<K,
* val people: ObservableList<Person> = (..) * val people: ObservableList<Person> = (..)
* val heightToNames: ObservableMap<Long, ObservableList<String>> = people.associateByAggregation(Person::height) { name, person -> person.name } * val heightToNames: ObservableMap<Long, ObservableList<String>> = people.associateByAggregation(Person::height) { name, person -> person.name }
*/ */
@Suppress("UNCHECKED_CAST")
fun <K : Any, A : Any, B> ObservableList<out A>.associateByAggregation(toKey: (A) -> K, assemble: (K, A) -> B): ObservableMap<K, ObservableList<B>> { fun <K : Any, A : Any, B> ObservableList<out A>.associateByAggregation(toKey: (A) -> K, assemble: (K, A) -> B): ObservableMap<K, ObservableList<B>> {
return AssociatedList(AggregatedList(this, toKey) { key, members -> Pair(key, members) }, { it.first }) { key, pair -> @Suppress("UNCHECKED_CAST")
pair.second.map { assemble(key, it) } val sourceList = AggregatedList(this, toKey) { key, members -> Pair(key, members) } as AggregatedList<Pair<K, ObservableList<A>>, A, K>
return AssociatedList(sourceList, { (it as Pair<K,ObservableList<A>>).first }) { key, pair ->
(pair as Pair<K,ObservableList<A>>).second.map { assemble(key, it) }
} }
} }

View File

@ -16,7 +16,8 @@ class FlattenedListTest {
@Before @Before
fun setup() { fun setup() {
sourceList = FXCollections.observableArrayList(SimpleObjectProperty(1234)) sourceList = FXCollections.observableArrayList(SimpleObjectProperty(1234))
flattenedList = FlattenedList(sourceList) @Suppress("UNCHECKED_CAST")
flattenedList = FlattenedList(sourceList) as ObservableList<Int>
replayedList = ReplayedList(flattenedList) replayedList = ReplayedList(flattenedList)
} }

View File

@ -98,6 +98,9 @@ dependencies {
smokeTestImplementation "junit:junit:$junit_version" smokeTestImplementation "junit:junit:$junit_version"
smokeTestRuntimeOnly "org.junit.vintage:junit-vintage-engine:${junit_vintage_version}" smokeTestRuntimeOnly "org.junit.vintage:junit-vintage-engine:${junit_vintage_version}"
smokeTestRuntimeOnly "org.junit.platform:junit-platform-launcher:${junit_platform_version}" smokeTestRuntimeOnly "org.junit.platform:junit-platform-launcher:${junit_platform_version}"
// JDK11: required by Quasar at run-time
smokeTestRuntimeOnly "com.esotericsoftware:kryo:4.0.2"
} }
task integrationTest(type: Test) { task integrationTest(type: Test) {

View File

@ -12,7 +12,11 @@ java8MinUpdateVersion=171
# ***************************************************************# # ***************************************************************#
platformVersion=5 platformVersion=5
guavaVersion=28.0-jre guavaVersion=28.0-jre
# Quasar version to use with Java 8:
quasarVersion=0.7.10 quasarVersion=0.7.10
quasarClassifier=jdk8
# Quasar version to use with Java 11:
quasarVersion11=0.8.0
proguardVersion=6.1.1 proguardVersion=6.1.1
bouncycastleVersion=1.60 bouncycastleVersion=1.60
classgraphVersion=4.8.41 classgraphVersion=4.8.41

View File

@ -8,6 +8,9 @@ apply plugin: 'idea'
evaluationDependsOn(":core") evaluationDependsOn(":core")
// required by DJVM and Avian JVM (for running inside the SGX enclave) which only supports Java 8.
targetCompatibility = VERSION_1_8
def javaHome = System.getProperty('java.home') def javaHome = System.getProperty('java.home')
def jarBaseName = "corda-${project.name}".toString() def jarBaseName = "corda-${project.name}".toString()
@ -69,8 +72,12 @@ task predeterminise(type: ProGuardTask) {
injars patchCore injars patchCore
outjars file("$buildDir/proguard/pre-deterministic-${project.version}.jar") outjars file("$buildDir/proguard/pre-deterministic-${project.version}.jar")
libraryjars file("$javaHome/lib/rt.jar") if (JavaVersion.current().isJava9Compatible()) {
libraryjars file("$javaHome/lib/jce.jar") libraryjars "$javaHome/jmods"
} else {
libraryjars "$javaHome/lib/rt.jar"
libraryjars "$javaHome/lib/jce.jar"
}
configurations.compileClasspath.forEach { configurations.compileClasspath.forEach {
if (originalJar != it) { if (originalJar != it) {
libraryjars it, filter: '!META-INF/versions/**' libraryjars it, filter: '!META-INF/versions/**'
@ -118,8 +125,12 @@ task determinise(type: ProGuardTask) {
injars jarFilter injars jarFilter
outjars file("$buildDir/proguard/$jarBaseName-${project.version}.jar") outjars file("$buildDir/proguard/$jarBaseName-${project.version}.jar")
libraryjars file("$javaHome/lib/rt.jar") if (JavaVersion.current().isJava9Compatible()) {
libraryjars file("$javaHome/lib/jce.jar") libraryjars "$javaHome/jmods"
} else {
libraryjars "$javaHome/lib/rt.jar"
libraryjars "$javaHome/lib/jce.jar"
}
configurations.deterministicLibraries.forEach { configurations.deterministicLibraries.forEach {
libraryjars it, filter: '!META-INF/versions/**' libraryjars it, filter: '!META-INF/versions/**'
} }
@ -145,6 +156,8 @@ task determinise(type: ProGuardTask) {
} }
import net.corda.gradle.jarfilter.MetaFixerTask import net.corda.gradle.jarfilter.MetaFixerTask
import static org.gradle.api.JavaVersion.VERSION_1_8
task metafix(type: MetaFixerTask) { task metafix(type: MetaFixerTask) {
outputDir file("$buildDir/libs") outputDir file("$buildDir/libs")
jars determinise jars determinise

View File

@ -0,0 +1,61 @@
package net.corda.deterministic;
import java.security.Provider;
import java.security.SecureRandom;
import java.security.SecureRandomSpi;
import java.security.Security;
import java.util.concurrent.atomic.AtomicInteger;
import static org.junit.Assert.assertEquals;
/**
* Temporarily restore Sun's [SecureRandom] provider.
* This is ONLY for allowing us to generate test data, e.g. signatures.
*
* JDK11 upgrade: rewritten in Java to gain access to private internal JDK classes via module directives (not available to Kotlin compiler):
* sun.security.provider.SecureRandom()
*/
public class CheatingSecurityProvider extends Provider implements AutoCloseable {
private static AtomicInteger counter = new AtomicInteger();
@SuppressWarnings("deprecation") // JDK11: should replace with Provider(String name, double version, String info) (since 9)
public CheatingSecurityProvider() {
super("Cheat-" + counter.getAndIncrement(), 1.8, "Cheat security provider");
putService(new CheatingSecureRandomService(this));
assertEquals(1, Security.insertProviderAt(this, 1));
}
public void close() {
Security.removeProvider(getName());
}
private class SunSecureRandom extends SecureRandom {
public SunSecureRandom() {
// JDK11 upgrade: rewritten in Java to gain access to private internal JDK classes via open module directive
super(new sun.security.provider.SecureRandom(), null);
}
}
private class CheatingSecureRandomService extends Provider.Service {
public CheatingSecureRandomService(Provider provider) {
super(provider, "SecureRandom", "CheatingPRNG", CheatingSecureRandomSpi.class.getName(), null, null);
}
private SecureRandomSpi instance = new CheatingSecureRandomSpi();
public Object newInstance(Object constructorParameter){
return instance;
}
}
private class CheatingSecureRandomSpi extends SecureRandomSpi {
private SecureRandom secureRandom = new SunSecureRandom();
public void engineSetSeed(byte[] seed) { secureRandom.setSeed(seed); }
public void engineNextBytes(byte[] bytes) { secureRandom.nextBytes(bytes); }
public byte[] engineGenerateSeed(int numBytes) { return secureRandom.generateSeed(numBytes); }
}
}

View File

@ -1,44 +0,0 @@
package net.corda.deterministic
import org.junit.Assert.*
import java.security.Provider
import java.security.SecureRandom
import java.security.SecureRandomSpi
import java.security.Security
import java.util.concurrent.atomic.AtomicInteger
/**
* Temporarily restore Sun's [SecureRandom] provider.
* This is ONLY for allowing us to generate test data, e.g. signatures.
*/
class CheatingSecurityProvider : Provider("Cheat-${counter.getAndIncrement()}", 1.8, "Cheat security provider"), AutoCloseable {
private companion object {
private val counter = AtomicInteger()
}
init {
putService(CheatingSecureRandomService(this))
assertEquals(1, Security.insertProviderAt(this, 1))
}
override fun close() {
Security.removeProvider(name)
}
private class SunSecureRandom : SecureRandom(sun.security.provider.SecureRandom(), null)
private class CheatingSecureRandomService(provider: Provider)
: Provider.Service(provider, "SecureRandom", "CheatingPRNG", CheatingSecureRandomSpi::javaClass.name, null, null) {
private val instance: SecureRandomSpi = CheatingSecureRandomSpi()
override fun newInstance(constructorParameter: Any?) = instance
}
private class CheatingSecureRandomSpi : SecureRandomSpi() {
private val secureRandom: SecureRandom = SunSecureRandom()
override fun engineSetSeed(seed: ByteArray) = secureRandom.setSeed(seed)
override fun engineNextBytes(bytes: ByteArray) = secureRandom.nextBytes(bytes)
override fun engineGenerateSeed(numBytes: Int): ByteArray = secureRandom.generateSeed(numBytes)
}
}

View File

@ -57,7 +57,7 @@ class AttachmentsClassLoaderSerializationTests {
SecureHash.zeroHash, SecureHash.zeroHash,
{ attachmentTrustCalculator.calculate(it) }) { classLoader -> { attachmentTrustCalculator.calculate(it) }) { classLoader ->
val contractClass = Class.forName(ISOLATED_CONTRACT_CLASS_NAME, true, classLoader) val contractClass = Class.forName(ISOLATED_CONTRACT_CLASS_NAME, true, classLoader)
val contract = contractClass.newInstance() as Contract val contract = contractClass.getDeclaredConstructor().newInstance() as Contract
assertEquals("helloworld", contract.declaredField<Any?>("magicString").value) assertEquals("helloworld", contract.declaredField<Any?>("magicString").value)
val txt = IOUtils.toString(classLoader.getResourceAsStream("file1.txt"), Charsets.UTF_8.name()) val txt = IOUtils.toString(classLoader.getResourceAsStream("file1.txt"), Charsets.UTF_8.name())

View File

@ -90,7 +90,7 @@ class AttachmentsClassLoaderTests {
val classloader = createClassloader(isolatedId) val classloader = createClassloader(isolatedId)
val contractClass = Class.forName(ISOLATED_CONTRACT_CLASS_NAME, true, classloader) val contractClass = Class.forName(ISOLATED_CONTRACT_CLASS_NAME, true, classloader)
val contract = contractClass.newInstance() as Contract val contract = contractClass.getDeclaredConstructor().newInstance() as Contract
assertEquals("helloworld", contract.declaredField<Any?>("magicString").value) assertEquals("helloworld", contract.declaredField<Any?>("magicString").value)
} }

View File

@ -1,3 +1,5 @@
import static org.gradle.api.JavaVersion.VERSION_1_8
apply plugin: 'kotlin' apply plugin: 'kotlin'
apply plugin: 'kotlin-jpa' apply plugin: 'kotlin-jpa'
apply plugin: 'net.corda.plugins.quasar-utils' apply plugin: 'net.corda.plugins.quasar-utils'
@ -7,6 +9,11 @@ apply plugin: 'com.jfrog.artifactory'
description 'Corda core' description 'Corda core'
evaluationDependsOn(':node:capsule')
// required by DJVM and Avian JVM (for running inside the SGX enclave) which only supports Java 8.
targetCompatibility = VERSION_1_8
configurations { configurations {
integrationTestCompile.extendsFrom testCompile integrationTestCompile.extendsFrom testCompile
integrationTestRuntimeOnly.extendsFrom testRuntimeOnly integrationTestRuntimeOnly.extendsFrom testRuntimeOnly
@ -71,6 +78,9 @@ dependencies {
compile group: "io.github.classgraph", name: "classgraph", version: class_graph_version compile group: "io.github.classgraph", name: "classgraph", version: class_graph_version
// JDK11: required by Quasar at run-time
testRuntimeOnly "com.esotericsoftware:kryo:4.0.2"
testCompile "com.nhaarman:mockito-kotlin:$mockito_kotlin_version" testCompile "com.nhaarman:mockito-kotlin:$mockito_kotlin_version"
testCompile "org.mockito:mockito-core:$mockito_version" testCompile "org.mockito:mockito-core:$mockito_version"
testCompile "org.assertj:assertj-core:$assertj_version" testCompile "org.assertj:assertj-core:$assertj_version"

View File

@ -9,6 +9,7 @@ import org.bouncycastle.asn1.ASN1ObjectIdentifier
import java.security.Provider import java.security.Provider
@KeepForDJVM @KeepForDJVM
@Suppress("DEPRECATION") // JDK11: should replace with Provider(String name, double version, String info) (since 9)
class CordaSecurityProvider : Provider(PROVIDER_NAME, 0.1, "$PROVIDER_NAME security provider wrapper") { class CordaSecurityProvider : Provider(PROVIDER_NAME, 0.1, "$PROVIDER_NAME security provider wrapper") {
companion object { companion object {
const val PROVIDER_NAME = "Corda" const val PROVIDER_NAME = "Corda"

View File

@ -35,7 +35,7 @@ object ContractUpgradeFlow {
// DOCEND 1 // DOCEND 1
@Suspendable @Suspendable
override fun call(): Void? { override fun call(): Void? {
val upgrade = upgradedContractClass.newInstance() val upgrade = upgradedContractClass.getDeclaredConstructor().newInstance()
if (upgrade.legacyContract != stateAndRef.state.contract) { if (upgrade.legacyContract != stateAndRef.state.contract) {
throw FlowException("The contract state cannot be upgraded using provided UpgradedContract.") throw FlowException("The contract state cannot be upgraded using provided UpgradedContract.")
} }

View File

@ -41,6 +41,7 @@ fun checkMinimumPlatformVersion(minimumPlatformVersion: Int, requiredMinPlatform
} }
} }
// JDK11: revisit (JDK 9+ uses different numbering scheme: see https://docs.oracle.com/javase/9/docs/api/java/lang/Runtime.Version.html)
@Throws(NumberFormatException::class) @Throws(NumberFormatException::class)
fun getJavaUpdateVersion(javaVersion: String): Long = javaVersion.substringAfter("_").substringBefore("-").toLong() fun getJavaUpdateVersion(javaVersion: String): Long = javaVersion.substringAfter("_").substringBefore("-").toLong()

View File

@ -352,6 +352,7 @@ class DeclaredField<T>(clazz: Class<*>, name: String, private val receiver: Any?
val name: String = javaField.name val name: String = javaField.name
private fun <RESULT> Field.accessible(action: Field.() -> RESULT): RESULT { private fun <RESULT> Field.accessible(action: Field.() -> RESULT): RESULT {
@Suppress("DEPRECATION") // JDK11: isAccessible() should be replaced with canAccess() (since 9)
val accessible = isAccessible val accessible = isAccessible
isAccessible = true isAccessible = true
try { try {
@ -393,16 +394,17 @@ fun <K, V> Iterable<Pair<K, V>>.toMultiMap(): Map<K, List<V>> = this.groupBy({ i
val Class<*>.location: URL get() = protectionDomain.codeSource.location val Class<*>.location: URL get() = protectionDomain.codeSource.location
/** Convenience method to get the package name of a class literal. */ /** Convenience method to get the package name of a class literal. */
val KClass<*>.packageName: String get() = java.packageName val KClass<*>.packageName: String get() = java.packageName_
val Class<*>.packageName: String get() = requireNotNull(this.packageNameOrNull) { "$this not defined inside a package" } // re-defined to prevent clash with Java 9 Class.packageName: https://docs.oracle.com/javase/9/docs/api/java/lang/Class.html#getPackageName--
val Class<*>.packageName_: String get() = requireNotNull(this.packageNameOrNull) { "$this not defined inside a package" }
val Class<*>.packageNameOrNull: String? // This intentionally does not go via `package` as that code path is slow and contended and just ends up doing this. val Class<*>.packageNameOrNull: String? // This intentionally does not go via `package` as that code path is slow and contended and just ends up doing this.
get() { get() {
val name = this.getName() val name = this.name
val i = name.lastIndexOf('.') val i = name.lastIndexOf('.')
if (i != -1) { return if (i != -1) {
return name.substring(0, i) name.substring(0, i)
} else { } else {
return null null
} }
} }

View File

@ -366,7 +366,7 @@ class Verifier(val ltx: LedgerTransaction, private val transactionClassLoader: C
val contractInstances: List<Contract> = contractClasses.map { (contractClassName, contractClass) -> val contractInstances: List<Contract> = contractClasses.map { (contractClassName, contractClass) ->
try { try {
contractClass.newInstance() contractClass.getDeclaredConstructor().newInstance()
} catch (e: Exception) { } catch (e: Exception) {
throw TransactionVerificationException.ContractCreationError(ltx.id, contractClassName, e) throw TransactionVerificationException.ContractCreationError(ltx.id, contractClassName, e)
} }

View File

@ -133,7 +133,7 @@ data class ContractUpgradeWireTransaction(
private fun upgradedContract(className: ContractClassName, classLoader: ClassLoader): UpgradedContract<ContractState, ContractState> = try { private fun upgradedContract(className: ContractClassName, classLoader: ClassLoader): UpgradedContract<ContractState, ContractState> = try {
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
classLoader.loadClass(className).asSubclass(UpgradedContract::class.java).newInstance() as UpgradedContract<ContractState, ContractState> classLoader.loadClass(className).asSubclass(UpgradedContract::class.java).getDeclaredConstructor().newInstance() as UpgradedContract<ContractState, ContractState>
} catch (e: Exception) { } catch (e: Exception) {
throw TransactionVerificationException.ContractCreationError(id, className, e) throw TransactionVerificationException.ContractCreationError(id, className, e)
} }

View File

@ -0,0 +1,133 @@
package net.corda.core.internal;
import net.corda.core.crypto.Crypto;
import net.i2p.crypto.eddsa.EdDSAEngine;
import net.i2p.crypto.eddsa.EdDSAPublicKey;
import org.junit.Test;
import sun.security.util.BitArray;
import sun.security.util.ObjectIdentifier;
import sun.security.x509.AlgorithmId;
import sun.security.x509.X509Key;
import java.io.IOException;
import java.math.BigInteger;
import java.security.InvalidKeyException;
import java.security.KeyPair;
import java.security.SignatureException;
import java.util.Random;
import static org.junit.Assert.assertTrue;
/**
* JDK11 upgrade: rewritten in Java to gain access to private internal JDK classes via module directives (not available to Kotlin compiler):
* import sun.security.util.BitArray;
* import sun.security.util.ObjectIdentifier;
* import sun.security.x509.AlgorithmId;
* import sun.security.x509.X509Key;
*/
public class X509EdDSAEngineTest {
private static long SEED = 20170920L;
private static int TEST_DATA_SIZE = 2000;
// offset into an EdDSA header indicating where the key header and actual key start
// in the underlying byte array
private static int keyHeaderStart = 9;
private static int keyStart = 12;
private X509Key toX509Key(EdDSAPublicKey publicKey) throws IOException, InvalidKeyException {
byte[] internals = publicKey.getEncoded();
// key size in the header includes the count unused bits at the end of the key
// [keyHeaderStart + 2] but NOT the key header ID [keyHeaderStart] so the
// actual length of the key blob is size - 1
int keySize = (internals[keyHeaderStart + 1]) - 1;
byte[] key = new byte[keySize];
System.arraycopy(internals, keyStart, key, 0, keySize);
// 1.3.101.102 is the EdDSA OID
return new TestX509Key(new AlgorithmId(new ObjectIdentifier("1.3.101.112")), new BitArray(keySize * 8, key));
}
class TestX509Key extends X509Key {
TestX509Key(AlgorithmId algorithmId, BitArray key) throws InvalidKeyException {
this.algid = algorithmId;
this.setKey(key);
this.encode();
}
}
/**
* Put the X509EdDSA engine through basic tests to verify that the functions are hooked up correctly.
*/
@Test
public void SignAndVerify() throws InvalidKeyException, SignatureException {
X509EdDSAEngine engine = new X509EdDSAEngine();
KeyPair keyPair = Crypto.deriveKeyPairFromEntropy(Crypto.EDDSA_ED25519_SHA512, BigInteger.valueOf(SEED));
EdDSAPublicKey publicKey = (EdDSAPublicKey) keyPair.getPublic();
byte[] randomBytes = new byte[TEST_DATA_SIZE];
new Random(SEED).nextBytes(randomBytes);
engine.initSign(keyPair.getPrivate());
engine.update(randomBytes[0]);
engine.update(randomBytes, 1, randomBytes.length - 1);
// Now verify the signature
byte[] signature = engine.sign();
engine.initVerify(publicKey);
engine.update(randomBytes);
assertTrue(engine.verify(signature));
}
/**
* Verify that signing with an X509Key wrapped EdDSA key works.
*/
@Test
public void SignAndVerifyWithX509Key() throws InvalidKeyException, SignatureException, IOException {
X509EdDSAEngine engine = new X509EdDSAEngine();
KeyPair keyPair = Crypto.deriveKeyPairFromEntropy(Crypto.EDDSA_ED25519_SHA512, BigInteger.valueOf(SEED + 1));
X509Key publicKey = toX509Key((EdDSAPublicKey) keyPair.getPublic());
byte[] randomBytes = new byte[TEST_DATA_SIZE];
new Random(SEED + 1).nextBytes(randomBytes);
engine.initSign(keyPair.getPrivate());
engine.update(randomBytes[0]);
engine.update(randomBytes, 1, randomBytes.length - 1);
// Now verify the signature
byte[] signature = engine.sign();
engine.initVerify(publicKey);
engine.update(randomBytes);
assertTrue(engine.verify(signature));
}
/**
* Verify that signing with an X509Key wrapped EdDSA key succeeds when using the underlying EdDSAEngine.
*/
@Test
public void SignAndVerifyWithX509KeyAndOldEngineFails() throws InvalidKeyException, SignatureException, IOException {
X509EdDSAEngine engine = new X509EdDSAEngine();
KeyPair keyPair = Crypto.deriveKeyPairFromEntropy(Crypto.EDDSA_ED25519_SHA512, BigInteger.valueOf(SEED + 1));
X509Key publicKey = toX509Key((EdDSAPublicKey) keyPair.getPublic());
byte[] randomBytes = new byte[TEST_DATA_SIZE];
new Random(SEED + 1).nextBytes(randomBytes);
engine.initSign(keyPair.getPrivate());
engine.update(randomBytes[0]);
engine.update(randomBytes, 1, randomBytes.length - 1);
// Now verify the signature
byte[] signature = engine.sign();
engine.initVerify(publicKey);
engine.update(randomBytes);
engine.verify(signature);
}
/** Verify will fail if the input public key cannot be converted to EdDSA public key. */
@Test(expected = InvalidKeyException.class)
public void verifyWithNonSupportedKeyTypeFails() throws InvalidKeyException {
EdDSAEngine engine = new EdDSAEngine();
KeyPair keyPair = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, BigInteger.valueOf(SEED));
engine.initVerify(keyPair.getPublic());
}
}

View File

@ -1,125 +0,0 @@
package net.corda.core.internal
import net.corda.core.crypto.Crypto
import net.i2p.crypto.eddsa.EdDSAEngine
import net.i2p.crypto.eddsa.EdDSAPublicKey
import org.junit.Test
import sun.security.util.BitArray
import sun.security.util.ObjectIdentifier
import sun.security.x509.AlgorithmId
import sun.security.x509.X509Key
import java.math.BigInteger
import java.security.InvalidKeyException
import java.util.*
import kotlin.test.assertFailsWith
import kotlin.test.assertTrue
class TestX509Key(algorithmId: AlgorithmId, key: BitArray) : X509Key() {
init {
this.algid = algorithmId
this.setKey(key)
this.encode()
}
}
class X509EdDSAEngineTest {
companion object {
private const val SEED = 20170920L
private const val TEST_DATA_SIZE = 2000
// offset into an EdDSA header indicating where the key header and actual key start
// in the underlying byte array
private const val keyHeaderStart = 9
private const val keyStart = 12
private fun toX509Key(publicKey: EdDSAPublicKey): X509Key {
val internals = publicKey.encoded
// key size in the header includes the count unused bits at the end of the key
// [keyHeaderStart + 2] but NOT the key header ID [keyHeaderStart] so the
// actual length of the key blob is size - 1
val keySize = (internals[keyHeaderStart + 1].toInt()) - 1
val key = ByteArray(keySize)
System.arraycopy(internals, keyStart, key, 0, keySize)
// 1.3.101.102 is the EdDSA OID
return TestX509Key(AlgorithmId(ObjectIdentifier("1.3.101.112")), BitArray(keySize * 8, key))
}
}
/**
* Put the X509EdDSA engine through basic tests to verify that the functions are hooked up correctly.
*/
@Test
fun `sign and verify`() {
val engine = X509EdDSAEngine()
val keyPair = Crypto.deriveKeyPairFromEntropy(Crypto.EDDSA_ED25519_SHA512, BigInteger.valueOf(SEED))
val publicKey = keyPair.public as EdDSAPublicKey
val randomBytes = ByteArray(TEST_DATA_SIZE)
Random(SEED).nextBytes(randomBytes)
engine.initSign(keyPair.private)
engine.update(randomBytes[0])
engine.update(randomBytes, 1, randomBytes.size - 1)
// Now verify the signature
val signature = engine.sign()
engine.initVerify(publicKey)
engine.update(randomBytes)
assertTrue { engine.verify(signature) }
}
/**
* Verify that signing with an X509Key wrapped EdDSA key works.
*/
@Test
fun `sign and verify with X509Key`() {
val engine = X509EdDSAEngine()
val keyPair = Crypto.deriveKeyPairFromEntropy(Crypto.EDDSA_ED25519_SHA512, BigInteger.valueOf(SEED + 1))
val publicKey = toX509Key(keyPair.public as EdDSAPublicKey)
val randomBytes = ByteArray(TEST_DATA_SIZE)
Random(SEED + 1).nextBytes(randomBytes)
engine.initSign(keyPair.private)
engine.update(randomBytes[0])
engine.update(randomBytes, 1, randomBytes.size - 1)
// Now verify the signature
val signature = engine.sign()
engine.initVerify(publicKey)
engine.update(randomBytes)
assertTrue { engine.verify(signature) }
}
/**
* Verify that signing with an X509Key wrapped EdDSA key fails when using the underlying EdDSAEngine.
*/
@Test
fun `sign and verify with X509Key and old engine fails`() {
val engine = EdDSAEngine()
val keyPair = Crypto.deriveKeyPairFromEntropy(Crypto.EDDSA_ED25519_SHA512, BigInteger.valueOf(SEED + 1))
val publicKey = toX509Key(keyPair.public as EdDSAPublicKey)
val randomBytes = ByteArray(TEST_DATA_SIZE)
Random(SEED + 1).nextBytes(randomBytes)
engine.initSign(keyPair.private)
engine.update(randomBytes[0])
engine.update(randomBytes, 1, randomBytes.size - 1)
// Now verify the signature
val signature = engine.sign()
assertFailsWith<InvalidKeyException> {
engine.initVerify(publicKey)
engine.update(randomBytes)
engine.verify(signature)
}
}
/** Verify will fail if the input public key cannot be converted to EdDSA public key. */
@Test
fun `verify with non-supported key type fails`() {
val engine = EdDSAEngine()
val keyPair = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, BigInteger.valueOf(SEED))
assertFailsWith<InvalidKeyException> { engine.initVerify(keyPair.public) }
}
}

View File

@ -59,6 +59,7 @@ task buildDockerFolder(dependsOn: [":node:capsule:buildCordaJAR", shadowJar]) {
from "src/bash/generate-config.sh" from "src/bash/generate-config.sh"
from "src/docker/DockerfileAL" from "src/docker/DockerfileAL"
from "src/docker/Dockerfile" from "src/docker/Dockerfile"
from "src/docker/Dockerfile11"
rename(cordaJar.name, "corda.jar") rename(cordaJar.name, "corda.jar")
rename(shadowJar.archivePath.name, "config-exporter.jar") rename(shadowJar.archivePath.name, "config-exporter.jar")
} }
@ -67,8 +68,8 @@ task buildDockerFolder(dependsOn: [":node:capsule:buildCordaJAR", shadowJar]) {
final String runTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")) final String runTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"))
final String suffix = project.version.toString().toLowerCase().contains("snapshot") ? runTime : "RELEASE" final String suffix = project.version.toString().toLowerCase().contains("snapshot") ? runTime : "RELEASE"
final zuluBuildTags = ["corda/corda-zulu-${project.version.toString().toLowerCase()}:${suffix}", "corda/corda-zulu-${project.version.toString().toLowerCase()}:latest"] final zuluBuildTags = ["corda/corda-zulu-java${JavaVersion.current()}-${project.version.toString().toLowerCase()}:${suffix}", "corda/corda-zulu-java${JavaVersion.current()}-${project.version.toString().toLowerCase()}:latest"]
final correttoBuildTags = ["corda/corda-corretto-${project.version.toString().toLowerCase()}:${suffix}", "corda/corda-corretto-${project.version.toString().toLowerCase()}:latest"] final correttoBuildTags = ["corda/corda-corretto-java${JavaVersion.current()}-${project.version.toString().toLowerCase()}:${suffix}", "corda/corda-corretto-java${JavaVersion.current()}-${project.version.toString().toLowerCase()}:latest"]
task buildOfficialZuluDockerImage(type: DockerBuildImage, dependsOn: [buildDockerFolder]) { task buildOfficialZuluDockerImage(type: DockerBuildImage, dependsOn: [buildDockerFolder]) {
inputDir = new File(project.buildDir, "docker-temp") inputDir = new File(project.buildDir, "docker-temp")
@ -76,6 +77,12 @@ task buildOfficialZuluDockerImage(type: DockerBuildImage, dependsOn: [buildDocke
dockerFile = new File(new File(project.buildDir, "docker-temp"), "Dockerfile") dockerFile = new File(new File(project.buildDir, "docker-temp"), "Dockerfile")
} }
task buildOfficialZuluJDK11DockerImage(type: DockerBuildImage, dependsOn: [buildDockerFolder]) {
inputDir = new File(project.buildDir, "docker-temp")
tags = zuluBuildTags
dockerFile = new File(new File(project.buildDir, "docker-temp"), "Dockerfile11")
}
task buildOfficialCorrettoDockerImage(type: DockerBuildImage, dependsOn: [buildDockerFolder]) { task buildOfficialCorrettoDockerImage(type: DockerBuildImage, dependsOn: [buildDockerFolder]) {
inputDir = new File(project.buildDir, "docker-temp") inputDir = new File(project.buildDir, "docker-temp")
tags = correttoBuildTags tags = correttoBuildTags
@ -90,6 +97,14 @@ task pushZuluLatestTag('type': DockerPushImage, dependsOn: [buildOfficialZuluDoc
imageName = zuluBuildTags[1] imageName = zuluBuildTags[1]
} }
task pushZulu11TimeStampedTag('type': DockerPushImage, dependsOn: [buildOfficialZuluJDK11DockerImage]){
imageName = zuluBuildTags[0]
}
task pushZulu11LatestTag('type': DockerPushImage, dependsOn: [buildOfficialZuluJDK11DockerImage]){
imageName = zuluBuildTags[1]
}
task pushCorrettoTimeStampedTag('type': DockerPushImage, dependsOn: [buildOfficialCorrettoDockerImage]){ task pushCorrettoTimeStampedTag('type': DockerPushImage, dependsOn: [buildOfficialCorrettoDockerImage]){
imageName = correttoBuildTags[0] imageName = correttoBuildTags[0]
} }

View File

@ -0,0 +1,28 @@
# Build and publish an Azul Zulu patched JDK 11 to the R3 Azure docker registry as follows:
# colljos@ci-agent-101l:~$ cd /home/colljos/azul/case17645
# $docker build . -f Dockerfile.zulu-sa-jdk-11-patch --no-cache -t azul/zulu-sa-jdk:11.0.3_7_LTS
# $docker tag azul/zulu-sa-jdk:11.0.3_7_LTS corda.azurecr.io/jdk/azul/zulu-sa-jdk:11.0.3_7_LTS
# $docker login -u corda corda.azurecr.io
# docker push corda.azurecr.io/jdk/azul/zulu-sa-jdk:11.0.3_7_LTS
# Remember to set the DOCKER env variables accordingly to access the R3 Azure docker registry:
# export DOCKER_URL=https://corda.azurecr.io
# export DOCKER_USERNAME=<username>
# export DOCKER_PASSWORD=<password>
RUN addgroup corda && adduser --ingroup corda --disabled-password -gecos "" --shell /bin/bash corda
COPY zulu11.31.16-sa-jdk11.0.3-linux_x64.tar /opt
RUN tar xvf /opt/zulu11.31.16-sa-jdk11.0.3-linux_x64.tar -C /opt && ln -s /opt/zulu11.31.16-sa-jdk11.0.3-linux_x64 /opt/jdk
RUN rm /opt/zulu11.31.16-sa-jdk11.0.3-linux_x64.tar && \
chown -R corda /opt/zulu11.31.16-sa-jdk11.0.3-linux_x64 && \
chgrp -R corda /opt/zulu11.31.16-sa-jdk11.0.3-linux_x64
# Set environment
ENV JAVA_HOME /opt/jdk
ENV PATH ${PATH}:${JAVA_HOME}/bin
CMD ["java", "-version"]

View File

@ -0,0 +1,78 @@
# Using Azul Zulu patched JDK 11 (local built and published docker image)
# colljos@ci-agent-101l:~$ jdk11azul
# openjdk version "11.0.3" 2019-04-16 LTS
# OpenJDK Runtime Environment Zulu11.31+16-SA (build 11.0.3+7-LTS)
# OpenJDK 64-Bit Server VM Zulu11.31+16-SA (build 11.0.3+7-LTS, mixed mode)
# Remember to set the DOCKER env variables accordingly to access the R3 Azure docker registry:
# export DOCKER_URL=https://corda.azurecr.io
# export DOCKER_USERNAME=<username>
# export DOCKER_PASSWORD=<password>
FROM corda.azurecr.io/jdk/azul/zulu-sa-jdk:11.0.3_7_LTS
## Add packages, clean cache, create dirs, create corda user and change ownership
RUN apt-get update && \
apt-get -y upgrade && \
apt-get -y install bash curl unzip && \
rm -rf /var/lib/apt/lists/* && \
mkdir -p /opt/corda/cordapps && \
mkdir -p /opt/corda/persistence && \
mkdir -p /opt/corda/certificates && \
mkdir -p /opt/corda/drivers && \
mkdir -p /opt/corda/logs && \
mkdir -p /opt/corda/bin && \
mkdir -p /opt/corda/additional-node-infos && \
mkdir -p /etc/corda && \
chown -R corda /opt/corda && \
chgrp -R corda /opt/corda && \
chown -R corda /etc/corda && \
chgrp -R corda /etc/corda && \
chown -R corda /opt/corda && \
chgrp -R corda /opt/corda && \
chown -R corda /etc/corda && \
chgrp -R corda /etc/corda
ENV CORDAPPS_FOLDER="/opt/corda/cordapps" \
PERSISTENCE_FOLDER="/opt/corda/persistence" \
CERTIFICATES_FOLDER="/opt/corda/certificates" \
DRIVERS_FOLDER="/opt/corda/drivers" \
CONFIG_FOLDER="/etc/corda" \
MY_P2P_PORT=10200 \
MY_RPC_PORT=10201 \
MY_RPC_ADMIN_PORT=10202 \
PATH=$PATH:/opt/corda/bin \
JVM_ARGS="-XX:+UseG1GC -XX:+UnlockExperimentalVMOptions " \
CORDA_ARGS=""
##CORDAPPS FOLDER
VOLUME ["/opt/corda/cordapps"]
##PERSISTENCE FOLDER
VOLUME ["/opt/corda/persistence"]
##CERTS FOLDER
VOLUME ["/opt/corda/certificates"]
##OPTIONAL JDBC DRIVERS FOLDER
VOLUME ["/opt/corda/drivers"]
##LOG FOLDER
VOLUME ["/opt/corda/logs"]
##ADDITIONAL NODE INFOS FOLDER
VOLUME ["/opt/corda/additional-node-infos"]
##CONFIG LOCATION
VOLUME ["/etc/corda"]
##CORDA JAR
COPY --chown=corda:corda corda.jar /opt/corda/bin/corda.jar
##CONFIG MANIPULATOR JAR
COPY --chown=corda:corda config-exporter.jar /opt/corda/config-exporter.jar
##CONFIG GENERATOR SHELL SCRIPT
COPY --chown=corda:corda generate-config.sh /opt/corda/bin/config-generator
##CORDA RUN SCRIPT
COPY --chown=corda:corda run-corda.sh /opt/corda/bin/run-corda
##BASE CONFIG FOR GENERATOR
COPY --chown=corda:corda starting-node.conf /opt/corda/starting-node.conf
USER "corda"
EXPOSE ${MY_P2P_PORT} ${MY_RPC_PORT} ${MY_RPC_ADMIN_PORT}
WORKDIR /opt/corda
CMD ["run-corda"]

View File

@ -6,6 +6,7 @@ release, see :doc:`app-upgrade-notes`.
Unreleased Unreleased
---------- ----------
* Support for Java 11 (compatibility mode). Please read https://github.com/corda/corda/pull/5356.
* Removed the RPC exception privacy feature. Previously, in production mode, the exceptions thrown on the node were stripped of all content * Removed the RPC exception privacy feature. Previously, in production mode, the exceptions thrown on the node were stripped of all content
when rethrown on the RPC client. when rethrown on the RPC client.

View File

@ -26,10 +26,21 @@ sourceSets {
compileTestJava.dependsOn tasks.getByPath(':node:capsule:buildCordaJAR') compileTestJava.dependsOn tasks.getByPath(':node:capsule:buildCordaJAR')
dependencies { dependencies {
// Cordformation needs a SLF4J implementation when executing the Network
// Bootstrapper, but Log4J doesn't shutdown completely from within Gradle.
// Use a much simpler SLF4J implementation here instead.
cordaRuntime "org.slf4j:slf4j-simple:$slf4j_version"
compile project(':core') compile project(':core')
compile project(':client:jfx') compile project(':client:jfx')
compile project(':node-driver') compile (project(':node-driver')) {
compile project(':webserver') // We already have a SLF4J implementation on our runtime classpath,
// and we don't need another one.
exclude group: 'org.apache.logging.log4j'
}
compile (project(':webserver')) {
exclude group: "org.apache.logging.log4j"
}
testCompile project(':test-utils') testCompile project(':test-utils')
compile "org.graphstream:gs-core:1.3" compile "org.graphstream:gs-core:1.3"

View File

@ -4,7 +4,7 @@ import net.corda.core.contracts.LinearState
import net.corda.core.contracts.StateAndRef import net.corda.core.contracts.StateAndRef
import net.corda.core.contracts.UniqueIdentifier import net.corda.core.contracts.UniqueIdentifier
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.internal.packageName import net.corda.core.internal.packageName_
import net.corda.core.node.ServiceHub import net.corda.core.node.ServiceHub
import net.corda.core.node.services.queryBy import net.corda.core.node.services.queryBy
import net.corda.core.node.services.vault.QueryCriteria import net.corda.core.node.services.vault.QueryCriteria
@ -34,7 +34,7 @@ class WorkflowTransactionBuildTutorialTest {
@Before @Before
fun setup() { fun setup() {
mockNet = MockNetwork(threadPerNode = true, cordappPackages = listOf(javaClass.packageName)) mockNet = MockNetwork(threadPerNode = true, cordappPackages = listOf(javaClass.packageName_))
aliceNode = mockNet.createPartyNode(ALICE_NAME) aliceNode = mockNet.createPartyNode(ALICE_NAME)
bobNode = mockNet.createPartyNode(BOB_NAME) bobNode = mockNet.createPartyNode(BOB_NAME)
alice = aliceNode.services.myInfo.identityFromX500Name(ALICE_NAME) alice = aliceNode.services.myInfo.identityFromX500Name(ALICE_NAME)

View File

@ -4,6 +4,7 @@ import net.corda.core.contracts.Amount
import net.corda.core.contracts.ContractState import net.corda.core.contracts.ContractState
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.internal.packageName import net.corda.core.internal.packageName
import net.corda.core.internal.packageName_
import net.corda.core.node.services.queryBy import net.corda.core.node.services.queryBy
import net.corda.core.node.services.vault.* import net.corda.core.node.services.vault.*
import net.corda.core.utilities.OpaqueBytes import net.corda.core.utilities.OpaqueBytes
@ -31,7 +32,7 @@ class CustomVaultQueryTest {
@Before @Before
fun setup() { fun setup() {
mockNet = MockNetwork(threadPerNode = true, cordappPackages = listOf("net.corda.finance", IOUFlow::class.packageName, javaClass.packageName, "com.template")) mockNet = MockNetwork(threadPerNode = true, cordappPackages = listOf("net.corda.finance", IOUFlow::class.packageName, javaClass.packageName_, "com.template"))
nodeA = mockNet.createPartyNode() nodeA = mockNet.createPartyNode()
nodeB = mockNet.createPartyNode() nodeB = mockNet.createPartyNode()
notary = mockNet.defaultNotaryIdentity notary = mockNet.defaultNotaryIdentity

Binary file not shown.

View File

@ -38,6 +38,9 @@ dependencies {
compile "com.fasterxml.jackson.core:jackson-databind:$jackson_version" compile "com.fasterxml.jackson.core:jackson-databind:$jackson_version"
runtime 'com.mattbertolini:liquibase-slf4j:2.0.0' runtime 'com.mattbertolini:liquibase-slf4j:2.0.0'
// JDK11: required by Quasar at run-time
runtime "com.esotericsoftware:kryo:4.0.2"
testImplementation "org.junit.jupiter:junit-jupiter-api:${junit_jupiter_version}" testImplementation "org.junit.jupiter:junit-jupiter-api:${junit_jupiter_version}"
testImplementation "junit:junit:$junit_version" testImplementation "junit:junit:$junit_version"

View File

@ -107,7 +107,7 @@ class AttachmentsClassLoaderStaticContractTests {
@Test @Test
fun `verify that contract DummyContract is in classPath`() { fun `verify that contract DummyContract is in classPath`() {
val contractClass = Class.forName(ATTACHMENT_PROGRAM_ID) val contractClass = Class.forName(ATTACHMENT_PROGRAM_ID)
assertThat(contractClass.newInstance()).isInstanceOf(Contract::class.java) assertThat(contractClass.getDeclaredConstructor().newInstance()).isInstanceOf(Contract::class.java)
} }
private fun cordappLoaderForPackages(packages: Collection<String>): CordappLoader { private fun cordappLoaderForPackages(packages: Collection<String>): CordappLoader {

View File

@ -70,7 +70,9 @@ class TlsDiffProtocolsTest(private val serverAlgo: String, private val clientAlg
CIPHER_SUITES_ALL(arrayOf( CIPHER_SUITES_ALL(arrayOf(
// 1.3 only // 1.3 only
"TLS_AES_128_GCM_SHA256", "TLS_AES_128_GCM_SHA256",
"TLS_CHACHA20_POLY1305_SHA256", // Unsupported CipherSuite: TLS_CHACHA20_POLY1305_SHA256 (java version "11.0.2" 2019-01-15 LTS)
// Works with: openjdk version "12.0.1" 2019-04-16 (OpenJDK Runtime Environment (build 12.0.1+12))
// "TLS_CHACHA20_POLY1305_SHA256",
// 1.2 only // 1.2 only
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256" "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"

View File

@ -40,7 +40,6 @@ import org.bouncycastle.pqc.jcajce.provider.sphincs.BCSphincs256PrivateKey
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
import org.junit.rules.TemporaryFolder import org.junit.rules.TemporaryFolder
import sun.security.rsa.RSAPrivateCrtKeyImpl
import java.io.DataInputStream import java.io.DataInputStream
import java.io.DataOutputStream import java.io.DataOutputStream
import java.io.IOException import java.io.IOException
@ -86,7 +85,6 @@ class X509UtilitiesTest {
Triple(ECDSA_SECP256K1_SHA256,java.security.interfaces.ECPrivateKey::class.java, org.bouncycastle.jce.interfaces.ECPrivateKey::class.java), Triple(ECDSA_SECP256K1_SHA256,java.security.interfaces.ECPrivateKey::class.java, org.bouncycastle.jce.interfaces.ECPrivateKey::class.java),
Triple(EDDSA_ED25519_SHA512, EdDSAPrivateKey::class.java, EdDSAPrivateKey::class.java), Triple(EDDSA_ED25519_SHA512, EdDSAPrivateKey::class.java, EdDSAPrivateKey::class.java),
// By default, JKS returns SUN RSA key. // By default, JKS returns SUN RSA key.
Triple(RSA_SHA256, RSAPrivateCrtKeyImpl::class.java, BCRSAPrivateCrtKey::class.java),
Triple(SPHINCS256_SHA256, BCSphincs256PrivateKey::class.java, BCSphincs256PrivateKey::class.java) Triple(SPHINCS256_SHA256, BCSphincs256PrivateKey::class.java, BCSphincs256PrivateKey::class.java)
) )
} }

View File

@ -69,10 +69,10 @@ sourceSets {
} }
jib.container { jib.container {
mainClass = "net.corda.node.Corda" mainClass = "net.corda.node.Corda"
args = ['--log-to-console', '--no-local-shell', '--config-file=/config/node.conf'] args = ['--log-to-console', '--no-local-shell', '--config-file=/config/node.conf']
// The Groovy string needs to be converted to a `java.lang.String` below. // The Groovy string needs to be converted to a `java.lang.String` below.
jvmFlags = ['-Xmx1g', "-javaagent:/app/libs/quasar-core-${quasar_version}-jdk8.jar".toString()] jvmFlags = ['-Xmx1g', "-javaagent:/app/libs/quasar-core-${quasar_version}.jar".toString()]
} }
// Use manual resource copying of log4j2.xml rather than source sets. // Use manual resource copying of log4j2.xml rather than source sets.
@ -194,10 +194,7 @@ dependencies {
// Integration test helpers // Integration test helpers
integrationTestCompile "junit:junit:$junit_version" integrationTestCompile "junit:junit:$junit_version"
integrationTestCompile "org.assertj:assertj-core:${assertj_version}" integrationTestCompile "org.assertj:assertj-core:${assertj_version}"
// AgentLoader: dynamic loading of JVM agents
compile group: 'com.ea.agentloader', name: 'ea-agent-loader', version: "${eaagentloader_version}"
// BFT-Smart dependencies // BFT-Smart dependencies
compile 'com.github.bft-smart:library:master-v1.1-beta-g6215ec8-87' compile 'com.github.bft-smart:library:master-v1.1-beta-g6215ec8-87'
@ -221,7 +218,10 @@ dependencies {
compile "org.jolokia:jolokia-jvm:${jolokia_version}:agent" compile "org.jolokia:jolokia-jvm:${jolokia_version}:agent"
// Optional New Relic JVM reporter, used to push metrics to the configured account associated with a newrelic.yml configuration. See https://mvnrepository.com/artifact/com.palominolabs.metrics/metrics-new-relic // Optional New Relic JVM reporter, used to push metrics to the configured account associated with a newrelic.yml configuration. See https://mvnrepository.com/artifact/com.palominolabs.metrics/metrics-new-relic
compile "com.palominolabs.metrics:metrics-new-relic:${metrics_new_relic_version}" compile "com.palominolabs.metrics:metrics-new-relic:${metrics_new_relic_version}"
// Required by JVMAgentUtil (x-compatible java 8 & 11 agent lookup mechanism)
compile files("${System.properties['java.home']}/../lib/tools.jar")
testCompile(project(':test-cli')) testCompile(project(':test-cli'))
testCompile(project(':test-utils')) testCompile(project(':test-utils'))
@ -238,6 +238,11 @@ tasks.withType(JavaCompile) {
options.compilerArgs << '-proc:none' options.compilerArgs << '-proc:none'
} }
tasks.withType(Test) {
if (JavaVersion.current() == JavaVersion.VERSION_11)
jvmArgs '-Djdk.attach.allowAttachSelf=true'
}
task integrationTest(type: Test) { task integrationTest(type: Test) {
testClassesDirs = sourceSets.integrationTest.output.classesDirs testClassesDirs = sourceSets.integrationTest.output.classesDirs
classpath = sourceSets.integrationTest.runtimeClasspath classpath = sourceSets.integrationTest.runtimeClasspath

View File

@ -52,10 +52,12 @@ task buildCordaJAR(type: FatCapsule, dependsOn: project(':node').tasks.jar) {
applicationId = "net.corda.node.Corda" applicationId = "net.corda.node.Corda"
// 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**;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**;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**;org.jolokia**;com.lmax**;picocli**;liquibase**;com.github.benmanes**;org.json**;org.postgresql**;nonapi.io.github.classgraph**)" def quasarExcludeExpression = "x(antlr**;bftsmart**;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**;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**;org.jolokia**;com.lmax**;picocli**;liquibase**;com.github.benmanes**;org.json**;org.postgresql**;nonapi.io.github.classgraph**)"
javaAgents = ["quasar-core-${quasar_version}-jdk8.jar=${quasarExcludeExpression}"] javaAgents = quasar_classifier == null ? ["quasar-core-${quasar_version}.jar=${quasarExcludeExpression}"] : ["quasar-core-${quasar_version}-${quasar_classifier}.jar=${quasarExcludeExpression}"]
systemProperties['visualvm.display.name'] = 'Corda' systemProperties['visualvm.display.name'] = 'Corda'
minJavaVersion = '1.8.0' if (JavaVersion.current() == JavaVersion.VERSION_1_8) {
minUpdateVersion['1.8'] = java8_minUpdateVersion minJavaVersion = '1.8.0'
minUpdateVersion['1.8'] = java8_minUpdateVersion
}
caplets = ['CordaCaplet'] caplets = ['CordaCaplet']
// JVM configuration: // JVM configuration:
@ -65,6 +67,8 @@ task buildCordaJAR(type: FatCapsule, dependsOn: project(':node').tasks.jar) {
// //
// If you change these flags, please also update Driver.kt // If you change these flags, please also update Driver.kt
jvmArgs = ['-Xmx512m', '-XX:+UseG1GC'] jvmArgs = ['-Xmx512m', '-XX:+UseG1GC']
if (JavaVersion.current() == JavaVersion.VERSION_11)
jvmArgs += ['-Djdk.attach.allowAttachSelf=true']
} }
} }

View File

@ -9,6 +9,7 @@ import java.io.File;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.*; import java.util.*;
import java.util.stream.Stream;
public class CordaCaplet extends Capsule { public class CordaCaplet extends Capsule {
@ -185,8 +186,8 @@ public class CordaCaplet extends Capsule {
private static void checkJavaVersion() { private static void checkJavaVersion() {
String version = System.getProperty("java.version"); String version = System.getProperty("java.version");
if (version == null || !version.startsWith("1.8")) { if (version == null || Stream.of("1.8", "11").noneMatch(version::startsWith)) {
System.err.printf("Error: Unsupported Java version %s; currently only version 1.8 is supported.\n", version); System.err.printf("Error: Unsupported Java version %s; currently only version 1.8 or 11 is supported.\n", version);
System.exit(1); System.exit(1);
} }
} }

View File

@ -88,13 +88,14 @@ import net.corda.nodeapi.internal.cryptoservice.bouncycastle.BCCryptoService
import net.corda.nodeapi.internal.persistence.* import net.corda.nodeapi.internal.persistence.*
import net.corda.tools.shell.InteractiveShell import net.corda.tools.shell.InteractiveShell
import org.apache.activemq.artemis.utils.ReusableLatch import org.apache.activemq.artemis.utils.ReusableLatch
import org.jolokia.jvmagent.JolokiaServer
import org.jolokia.jvmagent.JolokiaServerConfig
import org.slf4j.Logger import org.slf4j.Logger
import rx.Observable import rx.Observable
import rx.Scheduler import rx.Scheduler
import java.io.IOException import java.io.IOException
import java.lang.reflect.InvocationTargetException import java.lang.reflect.InvocationTargetException
import java.nio.file.Path import java.nio.file.Path
import java.nio.file.Paths
import java.security.KeyPair import java.security.KeyPair
import java.security.KeyStoreException import java.security.KeyStoreException
import java.security.cert.X509Certificate import java.security.cert.X509Certificate
@ -331,7 +332,7 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
log.info("Node starting up ...") log.info("Node starting up ...")
val trustRoot = initKeyStores() val trustRoot = initKeyStores()
initialiseJVMAgents() initialiseJolokia()
schemaService.mappedSchemasWarnings().forEach { schemaService.mappedSchemasWarnings().forEach {
val warning = it.toWarning() val warning = it.toWarning()
@ -992,19 +993,15 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
return NodeVaultService(platformClock, keyManagementService, services, database, schemaService, cordappLoader.appClassLoader) return NodeVaultService(platformClock, keyManagementService, services, database, schemaService, cordappLoader.appClassLoader)
} }
/** Load configured JVM agents */ // JDK 11: switch to directly instantiating jolokia server (rather than indirectly via dynamically self attaching Java Agents,
private fun initialiseJVMAgents() { // which is no longer supported from JDK 9 onwards (https://bugs.java.com/bugdatabase/view_bug.do?bug_id=8180425).
// No longer need to use https://github.com/electronicarts/ea-agent-loader either (which is also deprecated)
private fun initialiseJolokia() {
configuration.jmxMonitoringHttpPort?.let { port -> configuration.jmxMonitoringHttpPort?.let { port ->
requireNotNull(NodeBuildProperties.JOLOKIA_AGENT_VERSION) { val config = JolokiaServerConfig(mapOf("port" to port.toString()))
"'jolokiaAgentVersion' missing from build properties" val server = JolokiaServer(config, false)
} log.info("Starting Jolokia server on HTTP port: $port")
log.info("Starting Jolokia agent on HTTP port: $port") server.start()
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)
} }
} }

View File

@ -43,7 +43,7 @@ object DataSourceFactory {
dataSource dataSource
} else { } else {
// Basic init for the one test that wants to go via this API but without starting a HikariPool: // Basic init for the one test that wants to go via this API but without starting a HikariPool:
(Class.forName(hikariProperties.getProperty("dataSourceClassName")).newInstance() as DataSource).also { (Class.forName(hikariProperties.getProperty("dataSourceClassName")).getDeclaredConstructor().newInstance() as DataSource).also {
PropertyElf.setTargetFromProperties(it, config.dataSourceProperties) PropertyElf.setTargetFromProperties(it, config.dataSourceProperties)
} }
} }

View File

@ -159,10 +159,14 @@ open class Node(configuration: NodeConfiguration,
} }
private fun hasMinimumJavaVersion(): Boolean { private fun hasMinimumJavaVersion(): Boolean {
// when the ext.java8_minUpdateVersion gradle constant changes, so must this check // JDK 11: review naming convention and checking of 'minUpdateVersion' and 'distributionType` (OpenJDK, Oracle, Zulu, AdoptOpenJDK, Cornetto)
return try { return try {
val update = getJavaUpdateVersion(SystemUtils.JAVA_VERSION) // To filter out cases like 1.8.0_202-ea if (SystemUtils.IS_JAVA_11)
SystemUtils.IS_JAVA_1_8 && update >= 171 return true
else {
val update = getJavaUpdateVersion(SystemUtils.JAVA_VERSION) // To filter out cases like 1.8.0_202-ea
(SystemUtils.IS_JAVA_1_8 && update >= 171)
}
} catch (e: NumberFormatException) { // custom JDKs may not have the update version (e.g. 1.8.0-adoptopenjdk) } catch (e: NumberFormatException) { // custom JDKs may not have the update version (e.g. 1.8.0-adoptopenjdk)
false false
} }

View File

@ -24,6 +24,7 @@ import net.corda.node.internal.subcommands.ValidateConfigurationCli.Companion.lo
import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.config.NodeConfiguration
import net.corda.node.services.config.shouldStartLocalShell import net.corda.node.services.config.shouldStartLocalShell
import net.corda.node.services.config.shouldStartSSHDaemon import net.corda.node.services.config.shouldStartSSHDaemon
import net.corda.node.utilities.JVMAgentUtil.getJvmAgentProperties
import net.corda.node.utilities.registration.NodeRegistrationException import net.corda.node.utilities.registration.NodeRegistrationException
import net.corda.nodeapi.internal.addShutdownHook import net.corda.nodeapi.internal.addShutdownHook
import net.corda.nodeapi.internal.persistence.CouldNotCreateDataSourceException import net.corda.nodeapi.internal.persistence.CouldNotCreateDataSourceException
@ -32,7 +33,6 @@ import net.corda.tools.shell.InteractiveShell
import org.fusesource.jansi.Ansi import org.fusesource.jansi.Ansi
import org.slf4j.bridge.SLF4JBridgeHandler import org.slf4j.bridge.SLF4JBridgeHandler
import picocli.CommandLine.Mixin import picocli.CommandLine.Mixin
import sun.misc.VMSupport
import java.io.IOException import java.io.IOException
import java.io.RandomAccessFile import java.io.RandomAccessFile
import java.lang.management.ManagementFactory import java.lang.management.ManagementFactory
@ -245,12 +245,13 @@ open class NodeStartup : NodeStartupLogging {
logger.info("PID: ${info.name.split("@").firstOrNull()}") // TODO Java 9 has better support for this logger.info("PID: ${info.name.split("@").firstOrNull()}") // TODO Java 9 has better support for this
logger.info("Main class: ${NodeConfiguration::class.java.location.toURI().path}") logger.info("Main class: ${NodeConfiguration::class.java.location.toURI().path}")
logger.info("CommandLine Args: ${info.inputArguments.joinToString(" ")}") logger.info("CommandLine Args: ${info.inputArguments.joinToString(" ")}")
logger.info("bootclasspath: ${info.bootClassPath}") // JDK 11 (bootclasspath no longer supported from JDK 9)
if (info.isBootClassPathSupported) logger.info("bootclasspath: ${info.bootClassPath}")
logger.info("classpath: ${info.classPath}") logger.info("classpath: ${info.classPath}")
logger.info("VM ${info.vmName} ${info.vmVendor} ${info.vmVersion}") logger.info("VM ${info.vmName} ${info.vmVendor} ${info.vmVersion}")
logger.info("Machine: ${lookupMachineNameAndMaybeWarn()}") logger.info("Machine: ${lookupMachineNameAndMaybeWarn()}")
logger.info("Working Directory: ${cmdLineOptions.baseDirectory}") logger.info("Working Directory: ${cmdLineOptions.baseDirectory}")
val agentProperties = VMSupport.getAgentProperties() val agentProperties = getJvmAgentProperties(logger)
if (agentProperties.containsKey("sun.jdwp.listenerAddress")) { if (agentProperties.containsKey("sun.jdwp.listenerAddress")) {
logger.info("Debug port: ${agentProperties.getProperty("sun.jdwp.listenerAddress")}") logger.info("Debug port: ${agentProperties.getProperty("sun.jdwp.listenerAddress")}")
} }

View File

@ -2,7 +2,6 @@ package net.corda.node.internal.artemis
import net.corda.core.internal.uncheckedCast import net.corda.core.internal.uncheckedCast
import net.corda.core.utilities.contextLogger import net.corda.core.utilities.contextLogger
import net.corda.core.utilities.debug
import net.corda.node.internal.security.Password import net.corda.node.internal.security.Password
import net.corda.node.internal.security.RPCSecurityManager import net.corda.node.internal.security.RPCSecurityManager
import net.corda.node.services.rpc.LoginListener import net.corda.node.services.rpc.LoginListener
@ -23,7 +22,6 @@ import javax.security.auth.callback.UnsupportedCallbackException
import javax.security.auth.login.FailedLoginException import javax.security.auth.login.FailedLoginException
import javax.security.auth.login.LoginException import javax.security.auth.login.LoginException
import javax.security.auth.spi.LoginModule import javax.security.auth.spi.LoginModule
import javax.security.cert.X509Certificate
/** /**
* *
@ -120,8 +118,9 @@ class BrokerJaasLoginModule : BaseBrokerJaasLoginModule() {
// The Main authentication logic, responsible for running all the configured checks for each user type // The Main authentication logic, responsible for running all the configured checks for each user type
// and return the actual User and principals // and return the actual User and principals
private fun authenticateAndAuthorise(username: String, certificates: Array<X509Certificate>?, password: String): Pair<String, List<RolePrincipal>> { @Suppress("DEPRECATION") // should use java.security.cert.X509Certificate
fun requireTls(certificates: Array<X509Certificate>?) = requireNotNull(certificates) { "No client certificates presented." } private fun authenticateAndAuthorise(username: String, certificates: Array<javax.security.cert.X509Certificate>?, password: String): Pair<String, List<RolePrincipal>> {
fun requireTls(certificates: Array<javax.security.cert.X509Certificate>?) = requireNotNull(certificates) { "No client certificates presented." }
return when (username) { return when (username) {
ArtemisMessagingComponent.NODE_P2P_USER -> { ArtemisMessagingComponent.NODE_P2P_USER -> {
@ -174,7 +173,8 @@ abstract class BaseBrokerJaasLoginModule : LoginModule {
protected lateinit var callbackHandler: CallbackHandler protected lateinit var callbackHandler: CallbackHandler
protected val principals = ArrayList<Principal>() protected val principals = ArrayList<Principal>()
protected fun getUsernamePasswordAndCerts(): Triple<String, String, Array<X509Certificate>?> { @Suppress("DEPRECATION") // should use java.security.cert.X509Certificate
protected fun getUsernamePasswordAndCerts(): Triple<String, String, Array<javax.security.cert.X509Certificate>?> {
val nameCallback = NameCallback("Username: ") val nameCallback = NameCallback("Username: ")
val passwordCallback = PasswordCallback("Password: ", false) val passwordCallback = PasswordCallback("Password: ", false)
val certificateCallback = CertificateCallback() val certificateCallback = CertificateCallback()

View File

@ -3,13 +3,13 @@ package net.corda.node.internal.artemis
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.nodeapi.internal.crypto.X509Utilities import net.corda.nodeapi.internal.crypto.X509Utilities
import java.security.KeyStore import java.security.KeyStore
import javax.security.cert.CertificateException import java.security.cert.CertificateException
import javax.security.cert.X509Certificate
sealed class CertificateChainCheckPolicy { sealed class CertificateChainCheckPolicy {
@FunctionalInterface @FunctionalInterface
interface Check { interface Check {
fun checkCertificateChain(theirChain: Array<X509Certificate>) @Suppress("DEPRECATION") // should use java.security.cert.X509Certificate
fun checkCertificateChain(theirChain: Array<javax.security.cert.X509Certificate>)
} }
abstract fun createCheck(keyStore: KeyStore, trustStore: KeyStore): Check abstract fun createCheck(keyStore: KeyStore, trustStore: KeyStore): Check
@ -17,7 +17,8 @@ sealed class CertificateChainCheckPolicy {
object Any : CertificateChainCheckPolicy() { object Any : CertificateChainCheckPolicy() {
override fun createCheck(keyStore: KeyStore, trustStore: KeyStore): Check { override fun createCheck(keyStore: KeyStore, trustStore: KeyStore): Check {
return object : Check { return object : Check {
override fun checkCertificateChain(theirChain: Array<X509Certificate>) { @Suppress("DEPRECATION") // should use java.security.cert.X509Certificate
override fun checkCertificateChain(theirChain: Array<javax.security.cert.X509Certificate>) {
// nothing to do here // nothing to do here
} }
} }
@ -28,7 +29,8 @@ sealed class CertificateChainCheckPolicy {
override fun createCheck(keyStore: KeyStore, trustStore: KeyStore): Check { override fun createCheck(keyStore: KeyStore, trustStore: KeyStore): Check {
val rootPublicKey = trustStore.getCertificate(X509Utilities.CORDA_ROOT_CA).publicKey val rootPublicKey = trustStore.getCertificate(X509Utilities.CORDA_ROOT_CA).publicKey
return object : Check { return object : Check {
override fun checkCertificateChain(theirChain: Array<X509Certificate>) { @Suppress("DEPRECATION") // should use java.security.cert.X509Certificate
override fun checkCertificateChain(theirChain: Array<javax.security.cert.X509Certificate>) {
val theirRoot = theirChain.last().publicKey val theirRoot = theirChain.last().publicKey
if (rootPublicKey != theirRoot) { if (rootPublicKey != theirRoot) {
throw CertificateException("Root certificate mismatch, their root = $theirRoot") throw CertificateException("Root certificate mismatch, their root = $theirRoot")
@ -42,7 +44,8 @@ sealed class CertificateChainCheckPolicy {
override fun createCheck(keyStore: KeyStore, trustStore: KeyStore): Check { override fun createCheck(keyStore: KeyStore, trustStore: KeyStore): Check {
val ourPublicKey = keyStore.getCertificate(X509Utilities.CORDA_CLIENT_TLS).publicKey val ourPublicKey = keyStore.getCertificate(X509Utilities.CORDA_CLIENT_TLS).publicKey
return object : Check { return object : Check {
override fun checkCertificateChain(theirChain: Array<X509Certificate>) { @Suppress("DEPRECATION") // should use java.security.cert.X509Certificate
override fun checkCertificateChain(theirChain: Array<javax.security.cert.X509Certificate>) {
val theirLeaf = theirChain.first().publicKey val theirLeaf = theirChain.first().publicKey
if (ourPublicKey != theirLeaf) { if (ourPublicKey != theirLeaf) {
throw CertificateException("Leaf certificate mismatch, their leaf = $theirLeaf") throw CertificateException("Leaf certificate mismatch, their leaf = $theirLeaf")
@ -56,7 +59,8 @@ sealed class CertificateChainCheckPolicy {
override fun createCheck(keyStore: KeyStore, trustStore: KeyStore): Check { override fun createCheck(keyStore: KeyStore, trustStore: KeyStore): Check {
val trustedPublicKeys = trustedAliases.map { trustStore.getCertificate(it).publicKey }.toSet() val trustedPublicKeys = trustedAliases.map { trustStore.getCertificate(it).publicKey }.toSet()
return object : Check { return object : Check {
override fun checkCertificateChain(theirChain: Array<X509Certificate>) { @Suppress("DEPRECATION") // should use java.security.cert.X509Certificate
override fun checkCertificateChain(theirChain: Array<javax.security.cert.X509Certificate>) {
if (!theirChain.any { it.publicKey in trustedPublicKeys }) { if (!theirChain.any { it.publicKey in trustedPublicKeys }) {
throw CertificateException("Their certificate chain contained none of the trusted ones") throw CertificateException("Their certificate chain contained none of the trusted ones")
} }
@ -73,7 +77,8 @@ sealed class CertificateChainCheckPolicy {
class UsernameMustMatchCommonNameCheck : Check { class UsernameMustMatchCommonNameCheck : Check {
lateinit var username: String lateinit var username: String
override fun checkCertificateChain(theirChain: Array<X509Certificate>) { @Suppress("DEPRECATION") // should use java.security.cert.X509Certificate
override fun checkCertificateChain(theirChain: Array<javax.security.cert.X509Certificate>) {
if (!theirChain.any { certificate -> CordaX500Name.parse(certificate.subjectDN.name).commonName == username }) { if (!theirChain.any { certificate -> CordaX500Name.parse(certificate.subjectDN.name).commonName == username }) {
throw CertificateException("Client certificate does not match login username.") throw CertificateException("Client certificate does not match login username.")
} }

View File

@ -26,7 +26,6 @@ import net.corda.nodeapi.internal.network.NETWORK_PARAMS_FILE_NAME
import net.corda.nodeapi.internal.network.SignedNetworkParameters import net.corda.nodeapi.internal.network.SignedNetworkParameters
import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.nodeapi.internal.persistence.CordaPersistence
import net.corda.nodeapi.internal.persistence.SchemaMigration import net.corda.nodeapi.internal.persistence.SchemaMigration
import sun.reflect.generics.reflectiveObjects.NotImplementedException
import java.nio.file.Paths import java.nio.file.Paths
import java.time.Clock import java.time.Clock
import java.time.Duration import java.time.Duration
@ -49,11 +48,11 @@ class MigrationServicesForResolution(
val cordappLoader = SchemaMigration.loader.get() val cordappLoader = SchemaMigration.loader.get()
override fun getAppContext(): CordappContext { override fun getAppContext(): CordappContext {
throw NotImplementedException() TODO("not implemented")
} }
override fun getContractAttachmentID(contractClassName: ContractClassName): AttachmentId? { override fun getContractAttachmentID(contractClassName: ContractClassName): AttachmentId? {
throw NotImplementedException() TODO("not implemented")
} }
} }
private val cordappLoader = SchemaMigration.loader.get() private val cordappLoader = SchemaMigration.loader.get()

View File

@ -42,13 +42,12 @@ import org.objenesis.instantiator.ObjectInstantiator
import org.objenesis.strategy.InstantiatorStrategy import org.objenesis.strategy.InstantiatorStrategy
import org.objenesis.strategy.StdInstantiatorStrategy import org.objenesis.strategy.StdInstantiatorStrategy
import org.slf4j.Logger import org.slf4j.Logger
import sun.security.ec.ECPublicKeyImpl
import sun.security.provider.certpath.X509CertPath
import java.io.BufferedInputStream import java.io.BufferedInputStream
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
import java.io.FileInputStream import java.io.FileInputStream
import java.io.InputStream import java.io.InputStream
import java.lang.reflect.Modifier.isPublic import java.lang.reflect.Modifier.isPublic
import java.security.PrivateKey
import java.security.PublicKey import java.security.PublicKey
import java.security.cert.CertPath import java.security.cert.CertPath
import java.security.cert.X509Certificate import java.security.cert.X509Certificate
@ -97,7 +96,8 @@ object DefaultKryoCustomizer {
register(BufferedInputStream::class.java, InputStreamSerializer) register(BufferedInputStream::class.java, InputStreamSerializer)
register(Class.forName("sun.net.www.protocol.jar.JarURLConnection\$JarURLInputStream"), InputStreamSerializer) register(Class.forName("sun.net.www.protocol.jar.JarURLConnection\$JarURLInputStream"), InputStreamSerializer)
noReferencesWithin<WireTransaction>() noReferencesWithin<WireTransaction>()
register(ECPublicKeyImpl::class.java, publicKeySerializer) register(PublicKey::class.java, publicKeySerializer)
register(PrivateKey::class.java, PrivateKeySerializer)
register(EdDSAPublicKey::class.java, publicKeySerializer) register(EdDSAPublicKey::class.java, publicKeySerializer)
register(EdDSAPrivateKey::class.java, PrivateKeySerializer) register(EdDSAPrivateKey::class.java, PrivateKeySerializer)
register(CompositeKey::class.java, publicKeySerializer) // Using a custom serializer for compactness register(CompositeKey::class.java, publicKeySerializer) // Using a custom serializer for compactness
@ -109,7 +109,6 @@ object DefaultKryoCustomizer {
register(Class::class.java, ClassSerializer) register(Class::class.java, ClassSerializer)
register(FileInputStream::class.java, InputStreamSerializer) register(FileInputStream::class.java, InputStreamSerializer)
register(CertPath::class.java, CertPathSerializer) register(CertPath::class.java, CertPathSerializer)
register(X509CertPath::class.java, CertPathSerializer)
register(BCECPrivateKey::class.java, PrivateKeySerializer) register(BCECPrivateKey::class.java, PrivateKeySerializer)
register(BCECPublicKey::class.java, publicKeySerializer) register(BCECPublicKey::class.java, publicKeySerializer)
register(BCRSAPrivateCrtKey::class.java, PrivateKeySerializer) register(BCRSAPrivateCrtKey::class.java, PrivateKeySerializer)

View File

@ -41,10 +41,10 @@ import net.corda.core.utilities.contextLogger
import net.corda.node.internal.NodeStartup import net.corda.node.internal.NodeStartup
import net.corda.node.services.api.CheckpointStorage import net.corda.node.services.api.CheckpointStorage
import net.corda.node.services.statemachine.* import net.corda.node.services.statemachine.*
import net.corda.node.utilities.JVMAgentUtil.getJvmAgentProperties
import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.nodeapi.internal.persistence.CordaPersistence
import net.corda.serialization.internal.CheckpointSerializeAsTokenContextImpl import net.corda.serialization.internal.CheckpointSerializeAsTokenContextImpl
import net.corda.serialization.internal.withTokenContext import net.corda.serialization.internal.withTokenContext
import sun.misc.VMSupport
import java.nio.file.Path import java.nio.file.Path
import java.time.Duration import java.time.Duration
import java.time.Instant import java.time.Instant
@ -150,9 +150,9 @@ class CheckpointDumper(private val checkpointStorage: CheckpointStorage, private
} }
private fun checkpointAgentRunning(): Boolean { private fun checkpointAgentRunning(): Boolean {
val agentProperties = VMSupport.getAgentProperties() val agentProperties = getJvmAgentProperties(log)
return agentProperties.values.any { value -> return agentProperties.values.any { value ->
(value is String && value.contains("checkpoint-agent.jar")) value is String && value.contains("checkpoint-agent.jar")
} }
} }

View File

@ -1,52 +0,0 @@
package net.corda.node.utilities
import com.ea.agentloader.AgentLoader
import net.corda.core.internal.exists
import net.corda.core.internal.isRegularFile
import net.corda.core.internal.toPath
import java.net.URL
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(URL::toPath)
?.firstOrNull { it.fileName.toString() == jarFileName }
}
}
}

View File

@ -0,0 +1,25 @@
package net.corda.node.utilities
import com.sun.tools.attach.VirtualMachine
import org.slf4j.Logger
import java.lang.management.ManagementFactory
import java.util.*
object JVMAgentUtil {
/**
* Utility to attach to own VM at run-time and obtain agent details.
* In Java 9 this requires setting the following run-time jvm flag: -Djdk.attach.allowAttachSelf=true
* This mechanism supersedes the usage of VMSupport which is not available from Java 9 onwards.
*/
fun getJvmAgentProperties(log: Logger): Properties {
val jvmPid = ManagementFactory.getRuntimeMXBean().name.substringBefore('@')
return try {
val vm = VirtualMachine.attach(jvmPid)
return vm.agentProperties
} catch (e: Exception) {
log.warn("Unable to determine whether checkpoint agent is running: ${e.message}.\n" +
"You may need to pass in -Djdk.attach.allowAttachSelf=true if running on a Java 9 or later VM")
Properties()
}
}
}

View File

@ -23,7 +23,9 @@ import net.corda.testing.internal.configureDatabase
import net.corda.testing.internal.createNodeInfoAndSigned import net.corda.testing.internal.createNodeInfoAndSigned
import net.corda.testing.internal.rigorousMock import net.corda.testing.internal.rigorousMock
import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties import net.corda.testing.node.MockServices.Companion.makeTestDataSourceProperties
import org.apache.commons.lang3.SystemUtils
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.junit.Ignore
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
import org.junit.rules.TemporaryFolder import org.junit.rules.TemporaryFolder
@ -32,6 +34,7 @@ import java.time.Duration
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.assertFailsWith import kotlin.test.assertFailsWith
import kotlin.test.assertNull import kotlin.test.assertNull
import kotlin.test.assertTrue
class NodeTest { class NodeTest {
@Rule @Rule
@ -143,6 +146,14 @@ class NodeTest {
} }
} }
// JDK 11 check
@Test
fun `test getJavaRuntimeVersion`() {
assertTrue(SystemUtils.IS_JAVA_1_8 || SystemUtils.IS_JAVA_11)
}
// JDK11: revisit (JDK 9+ uses different numbering scheme: see https://docs.oracle.com/javase/9/docs/api/java/lang/Runtime.Version.html)
@Ignore
@Test @Test
fun `test getJavaUpdateVersion`() { fun `test getJavaUpdateVersion`() {
assertThat(getJavaUpdateVersion("1.8.0_202-ea")).isEqualTo(202) assertThat(getJavaUpdateVersion("1.8.0_202-ea")).isEqualTo(202)

View File

@ -2,13 +2,13 @@ package net.corda.node.internal.cordapp
import co.paralleluniverse.fibers.Suspendable import co.paralleluniverse.fibers.Suspendable
import net.corda.core.flows.* import net.corda.core.flows.*
import net.corda.core.internal.packageName
import net.corda.node.VersionInfo import net.corda.node.VersionInfo
import net.corda.nodeapi.internal.DEV_PUB_KEY_HASHES import net.corda.nodeapi.internal.DEV_PUB_KEY_HASHES
import net.corda.testing.node.internal.cordappWithPackages import net.corda.testing.node.internal.cordappWithPackages
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.junit.Test import org.junit.Test
import java.nio.file.Paths import java.nio.file.Paths
import net.corda.core.internal.packageName_
@InitiatingFlow @InitiatingFlow
class DummyFlow : FlowLogic<Unit>() { class DummyFlow : FlowLogic<Unit>() {
@ -79,7 +79,7 @@ class JarScanningCordappLoaderTest {
@Test @Test
fun `flows are loaded by loader`() { fun `flows are loaded by loader`() {
val jarFile = cordappWithPackages(javaClass.packageName).jarFile val jarFile = cordappWithPackages(javaClass.packageName_).jarFile
val loader = JarScanningCordappLoader.fromJarUrls(listOf(jarFile.toUri().toURL())) val loader = JarScanningCordappLoader.fromJarUrls(listOf(jarFile.toUri().toURL()))
// One cordapp from this source tree. In gradle it will also pick up the node jar. // One cordapp from this source tree. In gradle it will also pick up the node jar.

View File

@ -24,6 +24,7 @@ import net.corda.testing.core.ALICE_NAME
import net.corda.testing.core.TestIdentity import net.corda.testing.core.TestIdentity
import net.corda.testing.core.internal.CheckpointSerializationEnvironmentRule import net.corda.testing.core.internal.CheckpointSerializationEnvironmentRule
import net.corda.testing.internal.rigorousMock import net.corda.testing.internal.rigorousMock
import org.apache.commons.lang3.SystemUtils
import org.assertj.core.api.Assertions.* import org.assertj.core.api.Assertions.*
import org.junit.Assert.assertArrayEquals import org.junit.Assert.assertArrayEquals
import org.junit.Assert.assertEquals import org.junit.Assert.assertEquals
@ -353,8 +354,11 @@ class KryoTests(private val compression: CordaSerializationEncoding?) {
val obj = Holder(ByteArray(20000)) val obj = Holder(ByteArray(20000))
val uncompressedSize = obj.checkpointSerialize(context.withEncoding(null)).size val uncompressedSize = obj.checkpointSerialize(context.withEncoding(null)).size
val compressedSize = obj.checkpointSerialize(context.withEncoding(CordaSerializationEncoding.SNAPPY)).size val compressedSize = obj.checkpointSerialize(context.withEncoding(CordaSerializationEncoding.SNAPPY)).size
// If these need fixing, sounds like Kryo wire format changed and checkpoints might not surive an upgrade. // If these need fixing, sounds like Kryo wire format changed and checkpoints might not survive an upgrade.
assertEquals(20222, uncompressedSize) if (SystemUtils.IS_JAVA_11)
assertEquals(20172, uncompressedSize)
else
assertEquals(20222, uncompressedSize)
assertEquals(1111, compressedSize) assertEquals(1111, compressedSize)
} }
} }

View File

@ -4,7 +4,6 @@ import com.nhaarman.mockito_kotlin.any
import com.nhaarman.mockito_kotlin.mock import com.nhaarman.mockito_kotlin.mock
import com.nhaarman.mockito_kotlin.times import com.nhaarman.mockito_kotlin.times
import com.nhaarman.mockito_kotlin.verify import com.nhaarman.mockito_kotlin.verify
import com.sun.xml.internal.messaging.saaj.util.ByteOutputStream
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.internal.SignedDataWithCert import net.corda.core.internal.SignedDataWithCert
import net.corda.core.node.NetworkParameters import net.corda.core.node.NetworkParameters
@ -26,8 +25,7 @@ import org.junit.After
import org.junit.Before import org.junit.Before
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
import java.io.PrintStream import kotlin.test.assertNull
import kotlin.streams.toList
class DBNetworkParametersStorageTest { class DBNetworkParametersStorageTest {
@Rule @Rule
@ -98,24 +96,11 @@ class DBNetworkParametersStorageTest {
@Test @Test
fun `try save parameters with incorrect signature`() { fun `try save parameters with incorrect signature`() {
database.transaction { database.transaction {
val consoleOutput = interceptConsoleOutput { // logs a warning (java.security.cert.CertPathValidatorException: Cert path failed to validate)
networkParametersService.lookup(hash3) assertNull(networkParametersService.lookup(hash3))
}
assertThat(consoleOutput).anySatisfy {
it.contains("Caused by: java.security.cert.CertPathValidatorException: subject/issuer name chaining check failed")
}
} }
} }
private fun interceptConsoleOutput(block: () -> Unit): List<String> {
val oldOut = System.out
val out = ByteOutputStream()
System.setOut(PrintStream(out))
block()
System.setOut(oldOut)
return out.bytes.inputStream().bufferedReader().lines().toList()
}
private fun createMockNetworkMapClient(): NetworkMapClient { private fun createMockNetworkMapClient(): NetworkMapClient {
return mock { return mock {
on { getNetworkParameters(any()) }.then { on { getNetworkParameters(any()) }.then {

View File

@ -5,7 +5,7 @@ import com.esotericsoftware.kryo.KryoException
import net.corda.core.contracts.UniqueIdentifier import net.corda.core.contracts.UniqueIdentifier
import net.corda.core.flows.FlowLogic import net.corda.core.flows.FlowLogic
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.internal.packageName import net.corda.core.internal.packageName_
import net.corda.core.schemas.MappedSchema import net.corda.core.schemas.MappedSchema
import net.corda.nodeapi.internal.persistence.CordaPersistence import net.corda.nodeapi.internal.persistence.CordaPersistence
import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.common.internal.testNetworkParameters
@ -45,7 +45,7 @@ class ExposeJpaToFlowsTests {
fun setUp() { fun setUp() {
mockNet = MockNetwork(MockNetworkParameters(cordappsForAllNodes = listOf(enclosedCordapp()))) mockNet = MockNetwork(MockNetworkParameters(cordappsForAllNodes = listOf(enclosedCordapp())))
val (db, mockServices) = MockServices.makeTestDatabaseAndMockServices( val (db, mockServices) = MockServices.makeTestDatabaseAndMockServices(
cordappPackages = listOf(javaClass.packageName), cordappPackages = listOf(javaClass.packageName_),
identityService = makeTestIdentityService(myself.identity), identityService = makeTestIdentityService(myself.identity),
initialIdentity = myself, initialIdentity = myself,
networkParameters = testNetworkParameters(minimumPlatformVersion = 4) networkParameters = testNetworkParameters(minimumPlatformVersion = 4)

View File

@ -12,14 +12,23 @@ dependencies {
cordapp project(':finance:contracts') cordapp project(':finance:contracts')
cordapp project(':finance:workflows') cordapp project(':finance:workflows')
// Cordformation needs a SLF4J implementation when executing the Network
// Bootstrapper, but Log4J doesn't shutdown completely from within Gradle.
// Use a much simpler SLF4J implementation here instead.
cordaRuntime "org.slf4j:slf4j-simple:$slf4j_version"
// Corda integration dependencies // Corda integration dependencies
cordaRuntime project(path: ":node:capsule", configuration: 'runtimeArtifacts') cordaRuntime project(path: ":node:capsule", configuration: 'runtimeArtifacts')
cordaRuntime project(path: ":webserver:webcapsule", configuration: 'runtimeArtifacts') cordaRuntime project(path: ":webserver:webcapsule", configuration: 'runtimeArtifacts')
cordaCompile project(':core') cordaCompile project(':core')
cordaCompile project(':client:jfx') cordaCompile project(':client:jfx')
cordaCompile project(':client:rpc') cordaCompile project(':client:rpc')
cordaCompile project(':webserver') cordaCompile (project(':webserver')) {
cordaCompile project(':node-driver') exclude group: "org.apache.logging.log4j"
}
cordaCompile (project(':node-driver')) {
exclude group: "org.apache.logging.log4j"
}
// Javax is required for webapis // Javax is required for webapis
compile "org.glassfish.jersey.core:jersey-server:${jersey_version}" compile "org.glassfish.jersey.core:jersey-server:${jersey_version}"

View File

@ -4,7 +4,10 @@ apply plugin: 'net.corda.plugins.cordformation'
dependencies { dependencies {
runtimeOnly project(':node-api') runtimeOnly project(':node-api')
runtimeOnly "org.apache.logging.log4j:log4j-slf4j-impl:$log4j_version" // Cordformation needs a SLF4J implementation when executing the Network
// Bootstrapper, but Log4J doesn't shutdown completely from within Gradle.
// Use a much simpler SLF4J implementation here instead.
cordaRuntime "org.slf4j:slf4j-simple:$slf4j_version"
// Corda integration dependencies // Corda integration dependencies
runtime project(path: ":node:capsule", configuration: 'runtimeArtifacts') runtime project(path: ":node:capsule", configuration: 'runtimeArtifacts')

View File

@ -34,11 +34,18 @@ dependencies {
cordapp project(path: ':samples:simm-valuation-demo:contracts-states', configuration: 'shrinkArtifacts') cordapp project(path: ':samples:simm-valuation-demo:contracts-states', configuration: 'shrinkArtifacts')
cordapp project(':samples:simm-valuation-demo:flows') cordapp project(':samples:simm-valuation-demo:flows')
// Cordformation needs a SLF4J implementation when executing the Network
// Bootstrapper, but Log4J doesn't shutdown completely from within Gradle.
// Use a much simpler SLF4J implementation here instead.
cordaRuntime "org.slf4j:slf4j-simple:$slf4j_version"
// Corda integration dependencies // Corda integration dependencies
cordaRuntime project(path: ":node:capsule", configuration: 'runtimeArtifacts') cordaRuntime project(path: ":node:capsule", configuration: 'runtimeArtifacts')
cordaRuntime project(path: ":webserver:webcapsule", configuration: 'runtimeArtifacts') cordaRuntime project(path: ":webserver:webcapsule", configuration: 'runtimeArtifacts')
cordaCompile project(':core') cordaCompile project(':core')
cordaCompile project(':webserver') cordaCompile (project(':webserver')) {
exclude group: "org.apache.logging.log4j"
}
// Javax is required for webapis // Javax is required for webapis
compile "org.glassfish.jersey.core:jersey-server:$jersey_version" compile "org.glassfish.jersey.core:jersey-server:$jersey_version"

View File

@ -51,8 +51,12 @@ task shrink(type: ProGuardTask) {
injars jar injars jar
outjars shrinkJar outjars shrinkJar
libraryjars "$javaHome/lib/rt.jar" if (JavaVersion.current().isJava9Compatible()) {
libraryjars "$javaHome/lib/jce.jar" libraryjars "$javaHome/jmods"
} else {
libraryjars "$javaHome/lib/rt.jar"
libraryjars "$javaHome/lib/jce.jar"
}
configurations.runtimeClasspath.forEach { configurations.runtimeClasspath.forEach {
libraryjars it.path, filter: '!META-INF/versions/**' libraryjars it.path, filter: '!META-INF/versions/**'
} }

View File

@ -1,3 +1,5 @@
import static org.gradle.api.JavaVersion.VERSION_1_8
description 'Corda serialization (deterministic)' description 'Corda serialization (deterministic)'
apply from: '../deterministic.gradle' apply from: '../deterministic.gradle'
@ -8,6 +10,9 @@ apply plugin: 'idea'
evaluationDependsOn(":serialization") evaluationDependsOn(":serialization")
// required by DJVM and Avian JVM (for running inside the SGX enclave) which only supports Java 8.
targetCompatibility = VERSION_1_8
def javaHome = System.getProperty('java.home') def javaHome = System.getProperty('java.home')
def jarBaseName = "corda-${project.name}".toString() def jarBaseName = "corda-${project.name}".toString()
@ -68,9 +73,13 @@ task predeterminise(type: ProGuardTask, dependsOn: project(':core-deterministic'
injars patchSerialization injars patchSerialization
outjars file("$buildDir/proguard/pre-deterministic-${project.version}.jar") outjars file("$buildDir/proguard/pre-deterministic-${project.version}.jar")
libraryjars file("$javaHome/lib/rt.jar") if (JavaVersion.current().isJava9Compatible()) {
libraryjars file("$javaHome/lib/jce.jar") libraryjars "$javaHome/jmods"
libraryjars file("$javaHome/lib/ext/sunec.jar") } else {
libraryjars file("$javaHome/lib/rt.jar")
libraryjars file("$javaHome/lib/jce.jar")
libraryjars file("$javaHome/lib/ext/sunec.jar")
}
configurations.compileClasspath.forEach { configurations.compileClasspath.forEach {
if (originalJar != it) { if (originalJar != it) {
libraryjars it, filter: '!META-INF/versions/**' libraryjars it, filter: '!META-INF/versions/**'
@ -113,8 +122,12 @@ task determinise(type: ProGuardTask) {
injars jarFilter injars jarFilter
outjars file("$buildDir/proguard/$jarBaseName-${project.version}.jar") outjars file("$buildDir/proguard/$jarBaseName-${project.version}.jar")
libraryjars file("$javaHome/lib/rt.jar") if (JavaVersion.current().isJava9Compatible()) {
libraryjars file("$javaHome/lib/jce.jar") libraryjars "$javaHome/jmods"
} else {
libraryjars file("$javaHome/lib/rt.jar")
libraryjars file("$javaHome/lib/jce.jar")
}
configurations.deterministicLibraries.forEach { configurations.deterministicLibraries.forEach {
libraryjars it, filter: '!META-INF/versions/**' libraryjars it, filter: '!META-INF/versions/**'
} }

View File

@ -242,14 +242,14 @@ class SerializationOutputTests(private val compression: CordaSerializationEncodi
} }
val des = DeserializationInput(freshDeserializationFactory) val des = DeserializationInput(freshDeserializationFactory)
val desObj = des.deserialize(bytes, testSerializationContext.withEncodingWhitelist(encodingWhitelist)) val desObj = des.deserialize(bytes, testSerializationContext.withEncodingWhitelist(encodingWhitelist))
assertTrue(Objects.deepEquals(obj, desObj) == expectedEqual) assertTrue(deepEquals(obj, desObj) == expectedEqual)
// Now repeat with a re-used factory // Now repeat with a re-used factory
val ser2 = SerializationOutput(factory) val ser2 = SerializationOutput(factory)
val des2 = DeserializationInput(factory) val des2 = DeserializationInput(factory)
val desObj2 = des2.deserialize(ser2.serialize(obj, compression), testSerializationContext.withEncodingWhitelist(encodingWhitelist)) val desObj2 = des2.deserialize(ser2.serialize(obj, compression), testSerializationContext.withEncodingWhitelist(encodingWhitelist))
assertTrue(Objects.deepEquals(obj, desObj2) == expectedEqual) assertTrue(deepEquals(obj, desObj2) == expectedEqual)
assertTrue(Objects.deepEquals(desObj, desObj2) == expectDeserializedEqual) assertTrue(deepEquals(desObj, desObj2) == expectDeserializedEqual)
// TODO: add some schema assertions to check correctly formed. // TODO: add some schema assertions to check correctly formed.
return desObj return desObj
@ -580,7 +580,7 @@ class SerializationOutputTests(private val compression: CordaSerializationEncodi
assertTrue(desThrowable is CordaRuntimeException) // Since we don't handle the other case(s) yet assertTrue(desThrowable is CordaRuntimeException) // Since we don't handle the other case(s) yet
if (desThrowable is CordaRuntimeException) { if (desThrowable is CordaRuntimeException) {
assertEquals("${t.javaClass.name}: ${t.message}", desThrowable.message) assertEquals("${t.javaClass.name}: ${t.message}", desThrowable.message)
assertTrue(Objects.deepEquals(t.stackTrace, desThrowable.stackTrace)) assertTrue(Objects.deepEquals(t.stackTrace.toStackTraceBasic, desThrowable.stackTrace.toStackTraceBasic))
assertEquals(t.suppressed.size, desThrowable.suppressed.size) assertEquals(t.suppressed.size, desThrowable.suppressed.size)
t.suppressed.zip(desThrowable.suppressed).forEach { (before, after) -> assertSerializedThrowableEquivalent(before, after) } t.suppressed.zip(desThrowable.suppressed).forEach { (before, after) -> assertSerializedThrowableEquivalent(before, after) }
} }
@ -1521,5 +1521,36 @@ class SerializationOutputTests(private val compression: CordaSerializationEncodi
assertEquals(20059, uncompressedSize) assertEquals(20059, uncompressedSize)
assertEquals(1018, compressedSize) assertEquals(1018, compressedSize)
} }
}
// JDK11: backwards compatibility function to deal with StacktraceElement comparison pre-JPMS
private fun deepEquals(a: Any?, b: Any?): Boolean {
return if (a === b)
true
else if (a == null || b == null)
false
else {
if (a is Exception && b is Exception)
(a.cause == b.cause && a.localizedMessage == b.localizedMessage && a.message == b.message) &&
Objects.deepEquals(a.stackTrace.toStackTraceBasic, b.stackTrace.toStackTraceBasic)
else
Objects.deepEquals(a, b)
}
}
private val <T> Array<T>.toStackTraceBasic: Unit
get() {
this.map { StackTraceElementBasic(it as StackTraceElement) }
}
// JPMS adds additional fields that are not equal according to classloader/module hierarchy
data class StackTraceElementBasic(val ste: StackTraceElement) {
override fun equals(other: Any?): Boolean {
return if (other is StackTraceElementBasic)
(ste.className == other.ste.className) &&
(ste.methodName == other.ste.methodName) &&
(ste.fileName == other.ste.fileName) &&
(ste.lineNumber == other.ste.lineNumber)
else false
}
}
}

View File

@ -1,9 +1,14 @@
import static org.gradle.api.JavaVersion.VERSION_1_8
apply plugin: 'kotlin' apply plugin: 'kotlin'
apply plugin: 'net.corda.plugins.publish-utils' apply plugin: 'net.corda.plugins.publish-utils'
apply plugin: 'com.jfrog.artifactory' apply plugin: 'com.jfrog.artifactory'
description 'Corda serialization' description 'Corda serialization'
// required by DJVM and Avian JVM (for running inside the SGX enclave) which only supports Java 8.
targetCompatibility = VERSION_1_8
dependencies { dependencies {
compile project(":core") compile project(":core")

View File

@ -2,8 +2,6 @@ package net.corda.serialization.internal
import net.corda.core.DeleteForDJVM import net.corda.core.DeleteForDJVM
import net.corda.core.serialization.ClassWhitelist import net.corda.core.serialization.ClassWhitelist
import sun.misc.Unsafe
import sun.security.util.Password
import java.io.* import java.io.*
import java.lang.invoke.* import java.lang.invoke.*
import java.lang.reflect.AccessibleObject import java.lang.reflect.AccessibleObject
@ -50,7 +48,6 @@ object AllButBlacklisted : ClassWhitelist {
ClassLoader::class.java.name, ClassLoader::class.java.name,
Handler::class.java.name, // MemoryHandler, StreamHandler Handler::class.java.name, // MemoryHandler, StreamHandler
Runtime::class.java.name, Runtime::class.java.name,
Unsafe::class.java.name,
ZipFile::class.java.name, ZipFile::class.java.name,
Provider::class.java.name, Provider::class.java.name,
SecurityManager::class.java.name, SecurityManager::class.java.name,
@ -62,7 +59,6 @@ object AllButBlacklisted : ClassWhitelist {
// java.security. // java.security.
KeyStore::class.java.name, KeyStore::class.java.name,
Password::class.java.name,
AccessController::class.java.name, AccessController::class.java.name,
Permission::class.java.name, Permission::class.java.name,

View File

@ -5,7 +5,6 @@ import net.corda.core.utilities.NetworkHostAndPort
import org.apache.activemq.artemis.api.core.SimpleString import org.apache.activemq.artemis.api.core.SimpleString
import rx.Notification import rx.Notification
import rx.exceptions.OnErrorNotImplementedException import rx.exceptions.OnErrorNotImplementedException
import sun.security.x509.X509CertImpl
import java.security.cert.CRLReason import java.security.cert.CRLReason
import java.util.* import java.util.*
@ -62,7 +61,6 @@ object DefaultWhitelist : SerializationWhitelist {
StackTraceElement::class.java, StackTraceElement::class.java,
// Implementation of X509Certificate. // Implementation of X509Certificate.
X509CertImpl::class.java,
CRLReason::class.java CRLReason::class.java
) )
} }

View File

@ -88,9 +88,7 @@ open class ArraySerializer(override val type: Type, factory: LocalSerializerFact
context: SerializationContext context: SerializationContext
): Any { ): Any {
if (obj is List<*>) { if (obj is List<*>) {
return obj.map { return obj.map { input.readObjectOrNull(it, schemas, elementType, context) }.toArrayOfType(elementType)
input.readObjectOrNull(redescribe(it, elementType), schemas, elementType, context)
}.toArrayOfType(elementType)
} else throw AMQPNotSerializableException(type, "Expected a List but found $obj") } else throw AMQPNotSerializableException(type, "Expected a List but found $obj")
} }

View File

@ -26,10 +26,12 @@ private class ConstructorCaller(private val javaConstructor: Constructor<Any>):
try { try {
javaConstructor.newInstance(*parameters) javaConstructor.newInstance(*parameters)
} catch (e: InvocationTargetException) { } catch (e: InvocationTargetException) {
@Suppress("DEPRECATION") // JDK11: isAccessible() should be replaced with canAccess() (since 9)
throw NotSerializableException( throw NotSerializableException(
"Constructor for ${javaConstructor.declaringClass} (isAccessible=${javaConstructor.isAccessible}) " + "Constructor for ${javaConstructor.declaringClass} (isAccessible=${javaConstructor.isAccessible}) " +
"failed when called with parameters ${parameters.toList()}: ${e.cause!!.message}") "failed when called with parameters ${parameters.toList()}: ${e.cause!!.message}")
} catch (e: IllegalAccessException) { } catch (e: IllegalAccessException) {
@Suppress("DEPRECATION") // JDK11: isAccessible() should be replaced with canAccess() (since 9)
throw NotSerializableException( throw NotSerializableException(
"Constructor for ${javaConstructor.declaringClass} (isAccessible=${javaConstructor.isAccessible}) " + "Constructor for ${javaConstructor.declaringClass} (isAccessible=${javaConstructor.isAccessible}) " +
"not accessible: ${e.message}") "not accessible: ${e.message}")
@ -44,10 +46,12 @@ private class SetterCaller(val setter: Method): (Any, Any?) -> Unit {
try { try {
setter.invoke(target, value) setter.invoke(target, value)
} catch (e: InvocationTargetException) { } catch (e: InvocationTargetException) {
@Suppress("DEPRECATION") // JDK11: isAccessible() should be replaced with canAccess() (since 9)
throw NotSerializableException( throw NotSerializableException(
"Setter ${setter.declaringClass}.${setter.name} (isAccessible=${setter.isAccessible} " + "Setter ${setter.declaringClass}.${setter.name} (isAccessible=${setter.isAccessible} " +
"failed when called with parameter $value: ${e.cause!!.message}") "failed when called with parameter $value: ${e.cause!!.message}")
} catch (e: IllegalAccessException) { } catch (e: IllegalAccessException) {
@Suppress("DEPRECATION") // JDK11: isAccessible() should be replaced with canAccess() (since 9)
throw NotSerializableException( throw NotSerializableException(
"Setter ${setter.declaringClass}.${setter.name} (isAccessible=${setter.isAccessible} " + "Setter ${setter.declaringClass}.${setter.name} (isAccessible=${setter.isAccessible} " +
"not accessible: ${e.message}") "not accessible: ${e.message}")

View File

@ -346,7 +346,7 @@ class ClassCarpenterImpl @JvmOverloads constructor (override val whitelist: Clas
visitCode() visitCode()
visitLdcInsn(Type.getType("L${schema.jvmName};")) visitLdcInsn(Type.getType("L${schema.jvmName};"))
visitVarInsn(ALOAD, 0) visitVarInsn(ALOAD, 0)
visitMethodInsn(INVOKESTATIC, jlEnum, "valueOf", "(L$jlClass;L$jlString;)L$jlEnum;", true) visitMethodInsn(INVOKESTATIC, jlEnum, "valueOf", "(L$jlClass;L$jlString;)L$jlEnum;", false)
visitTypeInsn(CHECKCAST, schema.jvmName) visitTypeInsn(CHECKCAST, schema.jvmName)
visitInsn(ARETURN) visitInsn(ARETURN)
visitMaxs(0, 0) visitMaxs(0, 0)

View File

@ -3,6 +3,7 @@ package net.corda.serialization.internal.amqp
import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.CordaSerializable
import net.corda.serialization.internal.amqp.testutils.* import net.corda.serialization.internal.amqp.testutils.*
import org.junit.Test import org.junit.Test
import java.lang.Character.valueOf
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.fail import kotlin.test.fail
@ -49,9 +50,9 @@ class DeserializeSimpleTypesTests {
@Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN") @Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")
@Test @Test
fun testCharacter() { fun testCharacter() {
data class C(val c: Character) data class C(val c: Char)
val c = C(Character('c')) val c = C(valueOf('c'))
val serialisedC = SerializationOutput(sf1).serialize(c) val serialisedC = SerializationOutput(sf1).serialize(c)
val deserializedC = DeserializationInput(sf1).deserialize(serialisedC) val deserializedC = DeserializationInput(sf1).deserialize(serialisedC)
@ -90,9 +91,9 @@ class DeserializeSimpleTypesTests {
@Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN") @Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")
@Test @Test
fun testArrayOfInteger() { fun testArrayOfInteger() {
class IA(val ia: Array<Integer>) class IA(val ia: Array<Int>)
val ia = IA(arrayOf(Integer(1), Integer(2), Integer(3))) val ia = IA(arrayOf(Integer.valueOf(1), Integer.valueOf(2), Integer.valueOf(3)))
assertEquals("class [Ljava.lang.Integer;", ia.ia::class.java.toString()) assertEquals("class [Ljava.lang.Integer;", ia.ia::class.java.toString())
assertEquals(AMQPTypeIdentifiers.nameForType(ia.ia::class.java), "int[]") assertEquals(AMQPTypeIdentifiers.nameForType(ia.ia::class.java), "int[]")

View File

@ -108,7 +108,7 @@ internal object ProjectStructure {
} }
fun Any.writeTestResource(bytes: OpaqueBytes) { fun Any.writeTestResource(bytes: OpaqueBytes) {
val dir = ProjectStructure.projectRootDir / "serialization" / "src" / "test" / "resources" / javaClass.packageName.replace('.', separatorChar) val dir = ProjectStructure.projectRootDir / "serialization" / "src" / "test" / "resources" / javaClass.packageName_.replace('.', separatorChar)
bytes.open().copyTo(dir / testResourceName(), REPLACE_EXISTING) bytes.open().copyTo(dir / testResourceName(), REPLACE_EXISTING)
} }

View File

@ -31,7 +31,7 @@ class ClassCarpenterTest {
assertEquals(0, clazz.nonSyntheticFields.size) assertEquals(0, clazz.nonSyntheticFields.size)
assertEquals(2, clazz.nonSyntheticMethods.size) // get, toString assertEquals(2, clazz.nonSyntheticMethods.size) // get, toString
assertEquals(0, clazz.declaredConstructors[0].parameterCount) assertEquals(0, clazz.declaredConstructors[0].parameterCount)
clazz.newInstance() // just test there's no exception. clazz.getDeclaredConstructor().newInstance()
} }
@Test @Test

View File

@ -93,13 +93,11 @@ project(":common-logging").projectDir = new File("$settingsDir/common/logging")
apply from: 'buildCacheSettings.gradle' apply from: 'buildCacheSettings.gradle'
if (JavaVersion.current() == JavaVersion.VERSION_1_8) { include 'core-deterministic'
include 'core-deterministic' include 'core-deterministic:testing'
include 'core-deterministic:testing' include 'core-deterministic:testing:data'
include 'core-deterministic:testing:data' include 'core-deterministic:testing:verifier'
include 'core-deterministic:testing:verifier' include 'serialization-deterministic'
include 'serialization-deterministic'
}
include 'tools:checkpoint-agent'
findProject(':tools:checkpoint-agent')?.name = 'checkpoint-agent'
include 'tools:checkpoint-agent'
findProject(':tools:checkpoint-agent')?.name = 'checkpoint-agent'

View File

@ -50,6 +50,15 @@ dependencies {
compile "org.glassfish.jersey.containers:jersey-container-jetty-http:${jersey_version}" compile "org.glassfish.jersey.containers:jersey-container-jetty-http:${jersey_version}"
} }
compileJava {
doFirst {
if (JavaVersion.current() == JavaVersion.VERSION_11)
options.compilerArgs = [
'--add-exports', 'java.base/sun.nio.ch=ALL-UNNAMED'
]
}
}
task integrationTest(type: Test) { task integrationTest(type: Test) {
testClassesDirs = sourceSets.integrationTest.output.classesDirs testClassesDirs = sourceSets.integrationTest.output.classesDirs
classpath = sourceSets.integrationTest.runtimeClasspath classpath = sourceSets.integrationTest.runtimeClasspath
@ -60,6 +69,8 @@ jar {
} }
publish { publish {
publishSources = false
publishJavadoc = false
name jar.baseName name jar.baseName
} }

View File

@ -0,0 +1,79 @@
package net.corda.testing.driver;
import sun.misc.Unsafe;
import sun.nio.ch.DirectBuffer;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.lang.reflect.Field;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
/**
* JDK11 upgrade: rewritten in Java to gain access to private internal JDK classes via module directives (not available to Kotlin compiler):
* import sun.misc.Unsafe;
* import sun.nio.ch.DirectBuffer;
*/
class SharedMemoryIncremental extends PortAllocation {
static private final int DEFAULT_START_PORT = 10_000;
static private final int FIRST_EPHEMERAL_PORT = 30_000;
private int startPort;
private int endPort;
private SharedMemoryIncremental(int startPort, int endPort) {
this.startPort = startPort;
this.endPort = endPort;
try {
mb = backingFile.getChannel().map(FileChannel.MapMode.READ_WRITE, 0, 16);
startingAddress = ((DirectBuffer) mb).address();
} catch (IOException e) {
e.printStackTrace();
}
}
private File file = new File(System.getProperty("user.home"), "corda-" + startPort + "-to-" + endPort + "-port-allocator.bin");
private RandomAccessFile backingFile;
{
try {
backingFile = new RandomAccessFile(file, "rw");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
private MappedByteBuffer mb;
private Long startingAddress;
public static SharedMemoryIncremental INSTANCE = new SharedMemoryIncremental(DEFAULT_START_PORT, FIRST_EPHEMERAL_PORT);
static private Unsafe UNSAFE = getUnsafe();
static private Unsafe getUnsafe() {
try {
Field f = Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);
return (Unsafe) f.get(null);
} catch (NoSuchFieldException | IllegalAccessException e) {
e.printStackTrace();
return null;
}
}
@Override
public int nextPort() {
Long oldValue;
Long newValue;
do {
oldValue = UNSAFE.getLongVolatile(null, startingAddress);
if (oldValue + 1 >= endPort || oldValue < startPort) {
newValue = Long.valueOf(startPort);
} else {
newValue = (oldValue + 1);
}
} while (!UNSAFE.compareAndSwapLong(null, startingAddress, oldValue, newValue));
return newValue.intValue();
}
}

View File

@ -16,7 +16,6 @@ import net.corda.core.utilities.NetworkHostAndPort
import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.getOrThrow
import net.corda.testing.common.internal.testNetworkParameters import net.corda.testing.common.internal.testNetworkParameters
import net.corda.testing.core.DUMMY_NOTARY_NAME import net.corda.testing.core.DUMMY_NOTARY_NAME
import net.corda.testing.driver.internal.SharedMemoryIncremental
import net.corda.testing.driver.internal.incrementalPortAllocation import net.corda.testing.driver.internal.incrementalPortAllocation
import net.corda.testing.driver.internal.internalServices import net.corda.testing.driver.internal.internalServices
import net.corda.testing.node.NotarySpec import net.corda.testing.node.NotarySpec

View File

@ -1,74 +0,0 @@
package net.corda.testing.driver.internal
import net.corda.core.utilities.contextLogger
import net.corda.testing.driver.PortAllocation
import net.corda.testing.internal.isLocalPortBound
import sun.misc.Unsafe
import sun.nio.ch.DirectBuffer
import java.io.File
import java.io.RandomAccessFile
import java.nio.MappedByteBuffer
import java.nio.channels.FileChannel
/**
* It uses backing file to store information about last allocated port.
* Implementation note:
* The (small)file is read into memory and then `Unsafe` operation is used to work directly with that memory
* performing atomic compare and swap operations as necessary
* This enables the same file to be used my multiple processed running on the same machine such that they will be
* able to concurrently allocate ports without clashing with each other.
*/
internal class SharedMemoryIncremental
private constructor(private val startPort: Int, private val endPort: Int,
file: File = File(System.getProperty("user.home"), "corda-$startPort-to-$endPort-port-allocator.bin")) : PortAllocation() {
private val backingFile: RandomAccessFile = RandomAccessFile(file, "rw")
private val mb: MappedByteBuffer
private val memoryOffsetAddress: Long
init {
mb = backingFile.channel.map(FileChannel.MapMode.READ_WRITE, 0, 16) // TODO: Do we really need 16 bytes? Given that we care about Int it should be enough to have 4
memoryOffsetAddress = (mb as DirectBuffer).address()
}
/**
* An implementation of [PortAllocation] which allocates ports sequentially
*/
companion object {
private val UNSAFE: Unsafe = getUnsafe()
private fun getUnsafe(): Unsafe {
val f = Unsafe::class.java.getDeclaredField("theUnsafe")
f.isAccessible = true
return f.get(null) as Unsafe
}
val INSTANCE = SharedMemoryIncremental(DEFAULT_START_PORT, FIRST_EPHEMERAL_PORT)
val logger = contextLogger()
}
override fun nextPort(): Int {
var newValue: Long
do {
val oldValue = UNSAFE.getLongVolatile(null, memoryOffsetAddress)
newValue = if (oldValue + 1 >= endPort || oldValue < startPort) {
logger.warn("Port allocation rolling over: oldValue=$oldValue, startPort=$startPort, endPort=$endPort")
startPort.toLong()
} else {
(oldValue + 1)
}
val compareAndSwapSuccess = UNSAFE.compareAndSwapLong(null, memoryOffsetAddress, oldValue, newValue)
val success = if (!compareAndSwapSuccess) false else {
val alreadyBound = isLocalPortBound(newValue.toInt())
if (alreadyBound) {
logger.warn("Port $newValue appears to be bound. Allocator will skip it.")
}
!alreadyBound
}
} while (!success)
return newValue.toInt()
}
}

View File

@ -57,7 +57,6 @@ import java.io.File
import java.lang.management.ManagementFactory import java.lang.management.ManagementFactory
import java.net.ConnectException import java.net.ConnectException
import java.net.URL import java.net.URL
import java.net.URLClassLoader
import java.nio.file.Path import java.nio.file.Path
import java.security.cert.X509Certificate import java.security.cert.X509Certificate
import java.time.Duration import java.time.Duration
@ -123,7 +122,7 @@ class DriverDSLImpl(
private val state = ThreadBox(State()) private val state = ThreadBox(State())
//TODO: remove this once we can bundle quasar properly. //TODO: remove this once we can bundle quasar properly.
private val quasarJarPath: String by lazy { resolveJar(".*quasar.*\\.jar$") } private val quasarJarPath: String by lazy { resolveJar("co.paralleluniverse.fibers.Suspendable") }
private fun NodeConfig.checkAndOverrideForInMemoryDB(): NodeConfig = this.run { private fun NodeConfig.checkAndOverrideForInMemoryDB(): NodeConfig = this.run {
if (inMemoryDB && corda.dataSourceProperties.getProperty("dataSource.url").startsWith("jdbc:h2:")) { if (inMemoryDB && corda.dataSourceProperties.getProperty("dataSource.url").startsWith("jdbc:h2:")) {
@ -135,15 +134,13 @@ class DriverDSLImpl(
} }
} }
private fun resolveJar(jarNamePattern: String): String { private fun resolveJar(className: String): String {
return try { return try {
val cl = ClassLoader.getSystemClassLoader() val type = Class.forName(className)
val urls = (cl as URLClassLoader).urLs val src = type.protectionDomain.codeSource
val jarPattern = jarNamePattern.toRegex() src.location.toPath().toString()
val jarFileUrl = urls.first { jarPattern.matches(it.path) }
jarFileUrl.toPath().toString()
} catch (e: Exception) { } catch (e: Exception) {
log.warn("Unable to locate JAR `$jarNamePattern` on classpath: ${e.message}", e) log.warn("Unable to locate JAR for class given by `$className` on classpath: ${e.message}", e)
throw e throw e
} }
} }
@ -390,7 +387,7 @@ class DriverDSLImpl(
// In this case we're dealing with the the RPCDriver or one of it's cousins which are internal and we don't care about them // In this case we're dealing with the the RPCDriver or one of it's cousins which are internal and we don't care about them
emptyList() emptyList()
} else { } else {
listOf(Class.forName(stackTrace[index + 1].className).packageName) listOf(Class.forName(stackTrace[index + 1].className).packageName_)
} }
} }

View File

@ -88,8 +88,12 @@ data class TestCordappImpl(val scanPackage: String, override val config: Map<Str
runGradleBuild(projectRoot) runGradleBuild(projectRoot)
val libs = projectRoot / "build" / "libs" val libs = projectRoot / "build" / "libs"
val jars = libs.list { it.filter { it.toString().endsWith(".jar") }.toList() } val jars = libs.list {
.sortedBy { it.attributes().creationTime() } it.filter { it.toString().endsWith(".jar") }
.filter { !it.toString().endsWith("sources.jar") }
.filter { !it.toString().endsWith("javadoc.jar") }
.toList()
}.sortedBy { it.attributes().creationTime() }
checkNotNull(jars.lastOrNull()) { "No jars were built in $libs" } checkNotNull(jars.lastOrNull()) { "No jars were built in $libs" }
} }
} }

View File

@ -1,4 +1,4 @@
package net.corda.testing.node; package net.corda.testing.node.internal;
import co.paralleluniverse.fibers.Suspendable; import co.paralleluniverse.fibers.Suspendable;
import net.corda.core.Utils; import net.corda.core.Utils;
@ -6,6 +6,9 @@ import net.corda.core.concurrent.CordaFuture;
import net.corda.core.flows.*; import net.corda.core.flows.*;
import net.corda.core.identity.Party; import net.corda.core.identity.Party;
import net.corda.core.utilities.UntrustworthyData; import net.corda.core.utilities.UntrustworthyData;
import net.corda.testing.node.MockNetwork;
import net.corda.testing.node.MockNetworkParameters;
import net.corda.testing.node.StartedMockNode;
import org.junit.After; import org.junit.After;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;

View File

@ -6,7 +6,7 @@ dependencies {
compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version" compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
compile "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:$jackson_version" compile "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:$jackson_version"
compile "com.fasterxml.jackson.core:jackson-databind:$jackson_version" compile "com.fasterxml.jackson.core:jackson-databind:$jackson_version"
compile "com.fasterxml.jackson.module:jackson-module-kotlin:2.9.+" compile "com.fasterxml.jackson.module:jackson-module-kotlin:$jackson_version"
compile "org.junit.jupiter:junit-jupiter-api:${junit_jupiter_version}" compile "org.junit.jupiter:junit-jupiter-api:${junit_jupiter_version}"
compile "junit:junit:${junit_version}" compile "junit:junit:${junit_version}"

View File

@ -13,7 +13,7 @@ import kotlin.collections.ArrayList
class CommandLineCompatibilityChecker { class CommandLineCompatibilityChecker {
companion object { companion object {
fun printCommandLineYAML(clazz: Class<*>) { fun printCommandLineYAML(clazz: Class<*>) {
CommandLineCompatibilityChecker().printCommandDescription(CommandLine(clazz.newInstance())) CommandLineCompatibilityChecker().printCommandDescription(CommandLine(clazz.getDeclaredConstructor().newInstance()))
} }
} }
@ -154,7 +154,7 @@ class CommandLineCompatibilityChecker {
fun checkCommandLineIsBackwardsCompatible(commandLineToCheck: Class<*>): List<CliBackwardsCompatibilityValidationCheck> { fun checkCommandLineIsBackwardsCompatible(commandLineToCheck: Class<*>): List<CliBackwardsCompatibilityValidationCheck> {
val commandLineToCheckName = commandLineToCheck.canonicalName val commandLineToCheckName = commandLineToCheck.canonicalName
val instance = commandLineToCheck.newInstance() val instance = commandLineToCheck.getDeclaredConstructor().newInstance()
val resourceAsStream = this.javaClass.classLoader.getResourceAsStream("$commandLineToCheckName.yml") val resourceAsStream = this.javaClass.classLoader.getResourceAsStream("$commandLineToCheckName.yml")
?: throw IllegalStateException("$commandLineToCheckName.yml not found on classpath").also { ?: throw IllegalStateException("$commandLineToCheckName.yml not found on classpath").also {
printCommandLineYAML(commandLineToCheck) printCommandLineYAML(commandLineToCheck)

View File

@ -73,7 +73,7 @@ class DBRunnerExtension : Extension, BeforeAllCallback, AfterAllCallback, Before
private fun createDatabaseContext(groupName: String, defaultContextClassName: String): TestDatabaseContext { private fun createDatabaseContext(groupName: String, defaultContextClassName: String): TestDatabaseContext {
val className = System.getProperty("test.db.context") ?: defaultContextClassName val className = System.getProperty("test.db.context") ?: defaultContextClassName
val ctx = Class.forName(className).newInstance() as TestDatabaseContext val ctx = Class.forName(className).getDeclaredConstructor().newInstance() as TestDatabaseContext
ctx.initialize(groupName) ctx.initialize(groupName)
return ctx return ctx
} }

View File

@ -28,6 +28,7 @@ dependencies {
compile "com.squareup.okhttp3:okhttp:$okhttp_version" compile "com.squareup.okhttp3:okhttp:$okhttp_version"
compile project(':confidential-identities') compile project(':confidential-identities')
testCompile "org.apache.commons:commons-lang3:3.9"
} }
jar { jar {

View File

@ -8,6 +8,7 @@ import net.corda.testing.core.internal.JarSignatureTestUtils.updateJar
import net.corda.testing.core.internal.JarSignatureTestUtils.addIndexList import net.corda.testing.core.internal.JarSignatureTestUtils.addIndexList
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.internal.* import net.corda.core.internal.*
import org.apache.commons.lang3.SystemUtils
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.junit.After import org.junit.After
import org.junit.AfterClass import org.junit.AfterClass
@ -15,6 +16,7 @@ import org.junit.BeforeClass
import org.junit.Test import org.junit.Test
import java.nio.file.Files import java.nio.file.Files
import java.nio.file.Path import java.nio.file.Path
import java.security.PublicKey
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.assertFailsWith import kotlin.test.assertFailsWith
@ -135,7 +137,8 @@ class JarSignatureCollectorTest {
@Test @Test
fun `one signer with EC algorithm`() { fun `one signer with EC algorithm`() {
dir.createJar(FILENAME, "_signable1", "_signable2") dir.createJar(FILENAME, "_signable1", "_signable2")
val key = dir.signJar(FILENAME, CHARLIE, "storepass", CHARLIE_PASS) // JDK11: Warning: Different store and key passwords not supported for PKCS12 KeyStores. Ignoring user-specified -keypass value.
val key = signAs(CHARLIE, CHARLIE_PASS)
assertEquals(listOf(key), dir.getJarSigners(FILENAME)) // We only used CHARLIE's distinguished name, so the keys will be different. assertEquals(listOf(key), dir.getJarSigners(FILENAME)) // We only used CHARLIE's distinguished name, so the keys will be different.
} }
@ -147,6 +150,15 @@ class JarSignatureCollectorTest {
assertEquals(listOf(key), dir.getJarSigners(FILENAME)) assertEquals(listOf(key), dir.getJarSigners(FILENAME))
} }
private fun signAsAlice() = dir.signJar(FILENAME, ALICE, "storepass", ALICE_PASS) private fun signAsAlice() = signAs(ALICE, ALICE_PASS)
private fun signAsBob() = dir.signJar(FILENAME, BOB, "storepass", BOB_PASS) private fun signAsBob() = signAs(BOB, BOB_PASS)
// JDK11: Warning: Different store and key passwords not supported for PKCS12 KeyStores. Ignoring user-specified -keypass value.
// TODO: use programmatic API support to implement signing (see https://docs.oracle.com/javase/9/docs/api/jdk/security/jarsigner/JarSigner.html)
private fun signAs(alias: String, keyPassword: String = alias) : PublicKey {
return if (SystemUtils.IS_JAVA_11)
dir.signJar(FILENAME, alias, "storepass", "storepass")
else
dir.signJar(FILENAME, alias, "storepass", keyPassword)
}
} }

View File

@ -33,7 +33,7 @@ dependencies {
compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version" compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
compile "javassist:javassist:$javaassist_version" compile "javassist:javassist:$javaassist_version"
compile "com.esotericsoftware:kryo:4.0.0" compile "com.esotericsoftware:kryo:4.0.0"
compile "co.paralleluniverse:quasar-core:$quasar_version:jdk8" compile "co.paralleluniverse:quasar-core:$quasar_version"
compile (project(':core')) { compile (project(':core')) {
transitive = false transitive = false

View File

@ -213,6 +213,7 @@ object CheckpointHook : ClassFileTransformer {
private fun <T> getArrayValue(clazz: Class<T>, value: Any?): String? { private fun <T> getArrayValue(clazz: Class<T>, value: Any?): String? {
if (clazz.isArray) { if (clazz.isArray) {
log.debug { "readFieldExit array type: $clazz, value: $value]" } log.debug { "readFieldExit array type: $clazz, value: $value]" }
@Suppress("UNCHECKED_CAST")
if (Array<Number>::class.java.isAssignableFrom(clazz)) { if (Array<Number>::class.java.isAssignableFrom(clazz)) {
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
val numberValue = value as Array<Number> val numberValue = value as Array<Number>

View File

@ -12,8 +12,6 @@ dependencies {
compile "info.picocli:picocli:$picocli_version" compile "info.picocli:picocli:$picocli_version"
compile "commons-io:commons-io:$commons_io_version" compile "commons-io:commons-io:$commons_io_version"
compile "com.jcabi:jcabi-manifests:$jcabi_manifests_version" compile "com.jcabi:jcabi-manifests:$jcabi_manifests_version"
compile "org.slf4j:slf4j-api:$slf4j_version"
compile "org.apache.logging.log4j:log4j-slf4j-impl:$log4j_version"
// JAnsi: for drawing things to the terminal in nicely coloured ways. // JAnsi: for drawing things to the terminal in nicely coloured ways.
compile "org.fusesource.jansi:jansi:$jansi_version" compile "org.fusesource.jansi:jansi:$jansi_version"

View File

@ -1,3 +1,19 @@
// JDK 11 JavaFX
plugins {
id 'org.openjfx.javafxplugin' version '0.0.7' apply false
}
if (JavaVersion.current().isJava9Compatible()) {
apply plugin: 'org.openjfx.javafxplugin'
javafx {
version = "11.0.2"
modules = ['javafx.controls',
'javafx.fxml',
'javafx.swing'
]
}
}
ext { ext {
tornadofx_version = '1.7.15' tornadofx_version = '1.7.15'
jna_version = '4.5.2' jna_version = '4.5.2'

View File

@ -1,3 +1,19 @@
// JDK 11 JavaFX
plugins {
id 'org.openjfx.javafxplugin' version '0.0.7' apply false
}
if (JavaVersion.current().isJava9Compatible()) {
apply plugin: 'org.openjfx.javafxplugin'
javafx {
version = "11.0.2"
modules = ['javafx.controls',
'javafx.fxml',
'javafx.swing'
]
}
}
apply plugin: 'java' apply plugin: 'java'
apply plugin: 'kotlin' apply plugin: 'kotlin'
apply plugin: 'application' apply plugin: 'application'

View File

@ -11,14 +11,6 @@ configurations {
runtimeArtifacts.extendsFrom runtimeClasspath runtimeArtifacts.extendsFrom runtimeClasspath
} }
// Force the Caplet to target Java 6. This ensures that running 'java -jar explorer.jar' on any Java 6 VM upwards
// will get as far as the Capsule version checks, meaning that if your JVM is too old, you will at least get
// a sensible error message telling you what to do rather than a bytecode version exception that doesn't.
// If we introduce .java files into this module that need Java 8+ then we will have to push the caplet into
// its own module so its target can be controlled individually, but for now this suffices.
sourceCompatibility = 1.6
targetCompatibility = 1.6
capsule { capsule {
version capsule_version version capsule_version
} }

View File

@ -1,6 +1,5 @@
package net.corda.explorer.views.cordapps.cash package net.corda.explorer.views.cordapps.cash
import com.sun.javafx.collections.ObservableListWrapper
import de.jensd.fx.glyphs.fontawesome.FontAwesomeIcon import de.jensd.fx.glyphs.fontawesome.FontAwesomeIcon
import de.jensd.fx.glyphs.fontawesome.FontAwesomeIconView import de.jensd.fx.glyphs.fontawesome.FontAwesomeIconView
import javafx.beans.binding.Bindings import javafx.beans.binding.Bindings
@ -270,7 +269,7 @@ class CashViewer : CordaView("Cash") {
cashStatesList.apply { cashStatesList.apply {
// TODO update this once we have actual timestamps. // TODO update this once we have actual timestamps.
itemsProperty().bind(selectedNode.map { it?.states?.map { StateRow(LocalDateTime.now(), it) } ?: ObservableListWrapper(emptyList()) }) itemsProperty().bind(selectedNode.map { it?.states?.map { StateRow(LocalDateTime.now(), it) } ?: FXCollections.emptyObservableList() })
setCustomCellFactory { StateRowGraphic(it).root } setCustomCellFactory { StateRowGraphic(it).root }
} }

View File

@ -1,3 +1,19 @@
// JDK 11 JavaFX
plugins {
id 'org.openjfx.javafxplugin' version '0.0.7' apply false
}
if (JavaVersion.current().isJava9Compatible()) {
apply plugin: 'org.openjfx.javafxplugin'
javafx {
version = "11.0.2"
modules = ['javafx.controls',
'javafx.fxml',
'javafx.swing'
]
}
}
ext { ext {
tornadofx_version = '1.7.15' tornadofx_version = '1.7.15'
} }

Some files were not shown because too many files have changed in this diff Show More