From 06d27b57c1c4b43b7e79ed4083c30939f16ebee9 Mon Sep 17 00:00:00 2001 From: Katelyn Baker Date: Tue, 26 Jun 2018 09:31:35 +0100 Subject: [PATCH 1/5] CORDA-1662 - Corda Serialization Evolution breaksdown with Java classes (#3427) Nullability logic was relying on annotations that Kotlin applies by default but is left to the developer in Javaland. Change this around so it works for both. In Kotlin, the property must be nullable, in Java, it can't be a primitive. --- .../internal/amqp/EvolutionSerializer.kt | 34 ++++-- .../internal/amqp/SerializationHelper.kt | 15 +++ .../internal/amqp/SerializerFactory.kt | 2 +- .../internal/carpenter/ClassCarpenter.kt | 8 +- .../internal/amqp/JavaEvolutionTests.java | 100 ++++++++++++++++++ ...ticInitialisationOfSerializedObjectTest.kt | 2 +- .../carpenter/CarpenterExceptionTests.kt | 2 +- .../JavaEvolutionTests.testN1AddsNullableInt | Bin 0 -> 259 bytes .../JavaEvolutionTests.testN2AddsPrimitive | Bin 0 -> 272 bytes 9 files changed, 149 insertions(+), 14 deletions(-) create mode 100644 serialization/src/test/java/net/corda/serialization/internal/amqp/JavaEvolutionTests.java create mode 100644 serialization/src/test/resources/net/corda/serialization/internal/amqp/JavaEvolutionTests.testN1AddsNullableInt create mode 100644 serialization/src/test/resources/net/corda/serialization/internal/amqp/JavaEvolutionTests.testN2AddsPrimitive diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/EvolutionSerializer.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/EvolutionSerializer.kt index 79e576c0bb..886172b3e4 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/EvolutionSerializer.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/EvolutionSerializer.kt @@ -14,6 +14,7 @@ import java.lang.reflect.Type import kotlin.reflect.KFunction import kotlin.reflect.full.findAnnotation import kotlin.reflect.jvm.javaType +import kotlin.reflect.jvm.jvmErasure /** @@ -117,12 +118,29 @@ abstract class EvolutionSerializer( readersAsSerialized: Map): AMQPSerializer { val constructorArgs = arrayOfNulls(constructor.parameters.size) + // Java doesn't care about nullability unless it's a primitive in which + // case it can't be referenced. Unfortunately whilst Kotlin does apply + // Nullability annotations we cannot use them here as they aren't + // retained at runtime so we cannot rely on the absence of + // any particular NonNullable annotation type to indicate cross + // compiler nullability + val isKotlin = (new.type.javaClass.declaredAnnotations.any { + it.annotationClass.qualifiedName == "kotlin.Metadata" + }) + constructor.parameters.withIndex().forEach { - readersAsSerialized[it.value.name!!]?.apply { - this.resultsIndex = it.index - } ?: if (!it.value.type.isMarkedNullable) { - throw NotSerializableException( - "New parameter ${it.value.name} is mandatory, should be nullable for evolution to work") + if ((readersAsSerialized[it.value.name!!] ?.apply { this.resultsIndex = it.index }) == null) { + // If there is no value in the byte stream to map to the parameter of the constructor + // this is ok IFF it's a Kotlin class and the parameter is non nullable OR + // its a Java class and the parameter is anything but an unboxed primitive. + // Otherwise we throw the error and leave + if ((isKotlin && !it.value.type.isMarkedNullable) + || (!isKotlin && isJavaPrimitive(it.value.type.jvmErasure.java)) + ) { + throw NotSerializableException( + "New parameter \"${it.value.name}\" is mandatory, should be nullable for evolution " + + "to work, isKotlinClass=$isKotlin type=${it.value.type}") + } } } return EvolutionSerializerViaConstructor(new.type, factory, readersAsSerialized, constructor, constructorArgs) @@ -151,8 +169,10 @@ abstract class EvolutionSerializer( * @param factory the [SerializerFactory] associated with the serialization * context this serializer is being built for */ - fun make(old: CompositeType, new: ObjectSerializer, - factory: SerializerFactory): AMQPSerializer { + fun make(old: CompositeType, + new: ObjectSerializer, + factory: SerializerFactory + ): AMQPSerializer { // The order in which the properties were serialised is important and must be preserved val readersAsSerialized = LinkedHashMap() old.fields.forEach { diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/SerializationHelper.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/SerializationHelper.kt index 65b07d2638..9e9edffbf7 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/SerializationHelper.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/SerializationHelper.kt @@ -540,3 +540,18 @@ fun hasCordaSerializable(type: Class<*>): Boolean { || type.interfaces.any(::hasCordaSerializable) || (type.superclass != null && hasCordaSerializable(type.superclass)) } + +fun isJavaPrimitive(type: Class<*>) = type in JavaPrimitiveTypes.primativeTypes + +private object JavaPrimitiveTypes { + val primativeTypes = hashSetOf>( + Boolean::class.java, + Char::class.java, + Byte::class.java, + Short::class.java, + Int::class.java, + Long::class.java, + Float::class.java, + Double::class.java, + Void::class.java) +} diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/SerializerFactory.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/SerializerFactory.kt index b45336bc2c..8871907eed 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/SerializerFactory.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/amqp/SerializerFactory.kt @@ -78,7 +78,7 @@ open class SerializerFactory( lenientCarpenter: Boolean = false, evolutionSerializerGetter: EvolutionSerializerGetterBase = EvolutionSerializerGetter(), fingerPrinter: FingerPrinter = SerializerFingerPrinter() - ) : this(whitelist, ClassCarpenterImpl(classLoader, whitelist, lenientCarpenter), evolutionSerializerGetter, fingerPrinter) + ) : this(whitelist, ClassCarpenterImpl(whitelist, classLoader, lenientCarpenter), evolutionSerializerGetter, fingerPrinter) init { fingerPrinter.setOwner(this) diff --git a/serialization/src/main/kotlin/net/corda/serialization/internal/carpenter/ClassCarpenter.kt b/serialization/src/main/kotlin/net/corda/serialization/internal/carpenter/ClassCarpenter.kt index 742f34482a..315ad35a50 100644 --- a/serialization/src/main/kotlin/net/corda/serialization/internal/carpenter/ClassCarpenter.kt +++ b/serialization/src/main/kotlin/net/corda/serialization/internal/carpenter/ClassCarpenter.kt @@ -104,10 +104,10 @@ interface ClassCarpenter { * Equals/hashCode methods are not yet supported. */ @DeleteForDJVM -class ClassCarpenterImpl(cl: ClassLoader = Thread.currentThread().contextClassLoader, - override val whitelist: ClassWhitelist, - private val lenient: Boolean = false) : ClassCarpenter { - +class ClassCarpenterImpl @JvmOverloads constructor (override val whitelist: ClassWhitelist, + cl: ClassLoader = Thread.currentThread().contextClassLoader, + private val lenient: Boolean = false +) : ClassCarpenter { // TODO: Generics. // TODO: Sandbox the generated code when a security manager is in use. // TODO: Generate equals/hashCode. diff --git a/serialization/src/test/java/net/corda/serialization/internal/amqp/JavaEvolutionTests.java b/serialization/src/test/java/net/corda/serialization/internal/amqp/JavaEvolutionTests.java new file mode 100644 index 0000000000..fa8449840e --- /dev/null +++ b/serialization/src/test/java/net/corda/serialization/internal/amqp/JavaEvolutionTests.java @@ -0,0 +1,100 @@ +package net.corda.serialization.internal.amqp; + +import kotlin.Suppress; +import net.corda.core.serialization.SerializedBytes; +import net.corda.serialization.internal.amqp.testutils.AMQPTestUtilsKt; +import net.corda.serialization.internal.amqp.testutils.TestSerializationContext; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import java.io.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +public class JavaEvolutionTests { + @Rule + public final ExpectedException exception = ExpectedException.none(); + + // Class as it was when it was serialized and written to disk. Uncomment + // if the test referencing the object needs regenerating. + /* + static class N1 { + private String word; + public N1(String word) { this.word = word; } + public String getWord() { return word; } + } + */ + // Class as it exists now with the newly added element + static class N1 { + private String word; + private Integer wibble; + + public N1(String word, Integer wibble) { + this.word = word; + this.wibble = wibble; + } + public String getWord() { return word; } + public Integer getWibble() { return wibble; } + } + + // Class as it was when it was serialized and written to disk. Uncomment + // if the test referencing the object needs regenerating. + /* + static class N2 { + private String word; + public N2(String word) { this.word = word; } + public String getWord() { return word; } + } + */ + + // Class as it exists now with the newly added element + @SuppressWarnings("unused") + static class N2 { + private String word; + private float wibble; + + public N2(String word, float wibble) { + this.word = word; + this.wibble = wibble; + } + public String getWord() { return word; } + public float getWibble() { return wibble; } + } + + SerializerFactory factory = AMQPTestUtilsKt.testDefaultFactory(); + + @Test + public void testN1AddsNullableInt() throws IOException { + // Uncomment to regenerate the base state of the test + /* + N1 n = new N1("potato"); + AMQPTestUtilsKt.writeTestResource(this, new SerializationOutput(factory).serialize( + n, TestSerializationContext.testSerializationContext)); + */ + + N1 n2 = new DeserializationInput(factory).deserialize( + new SerializedBytes<>(AMQPTestUtilsKt.readTestResource(this)), + N1.class, + TestSerializationContext.testSerializationContext); + assertEquals(n2.getWord(), "potato"); + assertNull(n2.getWibble()); + } + + @Test + public void testN2AddsPrimitive() throws IOException { + // Uncomment to regenerate the base state of the test + /* + N2 n = new N2("This is only a test"); + + AMQPTestUtilsKt.writeTestResource(this, new SerializationOutput(factory).serialize( + n, TestSerializationContext.testSerializationContext)); + */ + + exception.expect(NotSerializableException.class); + new DeserializationInput(factory).deserialize( + new SerializedBytes<>(AMQPTestUtilsKt.readTestResource(this)), + N2.class, + TestSerializationContext.testSerializationContext); + } +} diff --git a/serialization/src/test/kotlin/net/corda/serialization/internal/amqp/StaticInitialisationOfSerializedObjectTest.kt b/serialization/src/test/kotlin/net/corda/serialization/internal/amqp/StaticInitialisationOfSerializedObjectTest.kt index fa324ea34c..526c52c1c7 100644 --- a/serialization/src/test/kotlin/net/corda/serialization/internal/amqp/StaticInitialisationOfSerializedObjectTest.kt +++ b/serialization/src/test/kotlin/net/corda/serialization/internal/amqp/StaticInitialisationOfSerializedObjectTest.kt @@ -98,7 +98,7 @@ class StaticInitialisationOfSerializedObjectTest { // Version of a serializer factory that will allow the class carpenter living on the // factory to have a different whitelist applied to it than the factory class TestSerializerFactory(wl1: ClassWhitelist, wl2: ClassWhitelist) : - SerializerFactory(wl1, ClassCarpenterImpl(ClassLoader.getSystemClassLoader(), wl2)) + SerializerFactory(wl1, ClassCarpenterImpl(wl2, ClassLoader.getSystemClassLoader())) // This time have the serialization factory and the carpenter use different whitelists @Test diff --git a/serialization/src/test/kotlin/net/corda/serialization/internal/carpenter/CarpenterExceptionTests.kt b/serialization/src/test/kotlin/net/corda/serialization/internal/carpenter/CarpenterExceptionTests.kt index a3f936b4d9..8aeaa99ac7 100644 --- a/serialization/src/test/kotlin/net/corda/serialization/internal/carpenter/CarpenterExceptionTests.kt +++ b/serialization/src/test/kotlin/net/corda/serialization/internal/carpenter/CarpenterExceptionTests.kt @@ -89,7 +89,7 @@ class CarpenterExceptionTests { // carpent that class up. However, when looking at the fields specified as properties of that class // we set the class loader of the ClassCarpenter to reject one of them, resulting in a CarpentryError // which we then want the code to wrap in a NotSerializeableException - val cc = ClassCarpenterImpl(TestClassLoader(listOf(C2::class.jvmName)), AllWhitelist) + val cc = ClassCarpenterImpl(AllWhitelist, TestClassLoader(listOf(C2::class.jvmName))) val factory = TestFactory(cc) Assertions.assertThatThrownBy { diff --git a/serialization/src/test/resources/net/corda/serialization/internal/amqp/JavaEvolutionTests.testN1AddsNullableInt b/serialization/src/test/resources/net/corda/serialization/internal/amqp/JavaEvolutionTests.testN1AddsNullableInt new file mode 100644 index 0000000000000000000000000000000000000000..334de45fa2125fd3cd88fd994135a3dc7056df85 GIT binary patch literal 259 zcmYe!FG@*dWME)uIGO|`85kH3yk}-utdy5pqL&Pkv&u6_);4zY2=MpT&h~dHF^lvK zEb>UVwLQSexR9+Nza+6FAFi3{z*5Eoix}a2)&o;n7h1#3(ko6a%1q43tV%4&%+J%y z%qvMP%1g}AOUx}S(DOq4aZm{W literal 0 HcmV?d00001 diff --git a/serialization/src/test/resources/net/corda/serialization/internal/amqp/JavaEvolutionTests.testN2AddsPrimitive b/serialization/src/test/resources/net/corda/serialization/internal/amqp/JavaEvolutionTests.testN2AddsPrimitive new file mode 100644 index 0000000000000000000000000000000000000000..ffa5f54f8bbc6d748ec22cf7d83ed43d5214f632 GIT binary patch literal 272 zcmYe!FG@*dWME)uIGO|`85kH3{9$HTtdy5pqL&Pkv(h(pDKd%*4#|lscJWN{ji~TS zGck9xwLKulxKKDGBePfmi1PDtDisnHN>YnU;3hF0Sju={5hI+>dSEK+LTk9edc~Rf#2;`FVPoc_pbud5JlCiMfRZdR~cTiLPb&Ii(=E5TL2WDt<-|u5h!M52!H_ o<4A`CiUV!Z literal 0 HcmV?d00001 From 14810f25de5bfef44523e57f17d20e636d50f18a Mon Sep 17 00:00:00 2001 From: Chris Rankin Date: Tue, 26 Jun 2018 10:44:08 +0100 Subject: [PATCH 2/5] Fix warning for lambda. (#3438) --- .../deterministic/common/TransactionVerificationRequest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core-deterministic/testing/common/src/main/kotlin/net/corda/deterministic/common/TransactionVerificationRequest.kt b/core-deterministic/testing/common/src/main/kotlin/net/corda/deterministic/common/TransactionVerificationRequest.kt index fb3743688d..247e15c464 100644 --- a/core-deterministic/testing/common/src/main/kotlin/net/corda/deterministic/common/TransactionVerificationRequest.kt +++ b/core-deterministic/testing/common/src/main/kotlin/net/corda/deterministic/common/TransactionVerificationRequest.kt @@ -19,7 +19,7 @@ class TransactionVerificationRequest(val wtxToVerify: SerializedBytes() } val attachmentMap = attachments.mapNotNull { it as? MockContractAttachment } - .associateBy(Attachment::id, { ContractAttachment(it, it.contract, uploader=TEST_UPLOADER) }) + .associateBy(Attachment::id) { ContractAttachment(it, it.contract, uploader=TEST_UPLOADER) } val contractAttachmentMap = emptyMap() @Suppress("DEPRECATION") return wtxToVerify.deserialize().toLedgerTransaction( From 1d95ffba3a3de77b17447c0d8bc65c8d15042b77 Mon Sep 17 00:00:00 2001 From: Tommy Lillehagen Date: Tue, 26 Jun 2018 10:46:04 +0100 Subject: [PATCH 3/5] CORDA-1674 - Publish Node Explorer artefact (#3437) --- build.gradle | 1 + tools/explorer/capsule/build.gradle | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/build.gradle b/build.gradle index 7ded190f54..8001c6ac10 100644 --- a/build.gradle +++ b/build.gradle @@ -356,6 +356,7 @@ bintrayConfig { 'corda-serialization', 'corda-serialization-deterministic', 'corda-tools-blob-inspector', + 'corda-tools-explorer', 'corda-tools-network-bootstrapper' ] license { diff --git a/tools/explorer/capsule/build.gradle b/tools/explorer/capsule/build.gradle index 7f99cd75f2..df3b084b59 100644 --- a/tools/explorer/capsule/build.gradle +++ b/tools/explorer/capsule/build.gradle @@ -2,9 +2,15 @@ * This build.gradle exists to package Node Explorer as an executable fat jar. */ apply plugin: 'us.kirchmeier.capsule' +apply plugin: 'net.corda.plugins.publish-utils' +apply plugin: 'com.jfrog.artifactory' description 'Node Explorer' +configurations { + runtimeArtifacts.extendsFrom runtime +} + repositories { mavenLocal() mavenCentral() @@ -47,3 +53,19 @@ task buildExplorerJAR(type: FatCapsule, dependsOn: project(':tools:explorer').co } build.dependsOn buildExplorerJAR + +artifacts { + runtimeArtifacts buildExplorerJAR + publish buildExplorerJAR { + classifier "" + } +} + +jar { + classifier "ignore" +} + +publish { + disableDefaultJar = true + name 'corda-tools-explorer' +} From 4ea80916675fe76f7a0e94c6feb7b608a63c3569 Mon Sep 17 00:00:00 2001 From: Tudor Malene Date: Tue, 26 Jun 2018 11:32:49 +0100 Subject: [PATCH 4/5] CORDA-1670 fix multiple CorDapps for flow check (#3436) * CORDA-1670 fix multiple CorDapps for flow check * CORDA-1670 fix multiple CorDapps for flow check * CORDA-1670 address code review comment --- .../src/main/kotlin/net/corda/node/internal/NodeStartup.kt | 4 ++++ .../net/corda/node/internal/cordapp/CordappLoader.kt | 7 ++++++- .../net/corda/node/internal/cordapp/CordappProviderImpl.kt | 3 +++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt b/node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt index c2e326f048..587cd875e3 100644 --- a/node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt +++ b/node/src/main/kotlin/net/corda/node/internal/NodeStartup.kt @@ -15,6 +15,7 @@ import net.corda.node.NodeRegistrationOption import net.corda.node.SerialFilter import net.corda.node.VersionInfo import net.corda.node.defaultSerialFilter +import net.corda.node.internal.cordapp.MultipleCordappsForFlowException import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.config.NodeConfigurationImpl import net.corda.node.services.config.shouldStartLocalShell @@ -135,6 +136,9 @@ open class NodeStartup(val args: Array) { try { cmdlineOptions.baseDirectory.createDirectories() startNode(conf, versionInfo, startTime, cmdlineOptions) + } catch (e: MultipleCordappsForFlowException) { + logger.error(e.message) + return false } catch (e: CouldNotCreateDataSourceException) { logger.error(e.message, e.cause) return false diff --git a/node/src/main/kotlin/net/corda/node/internal/cordapp/CordappLoader.kt b/node/src/main/kotlin/net/corda/node/internal/cordapp/CordappLoader.kt index b213a0fb9e..cedd54b1fc 100644 --- a/node/src/main/kotlin/net/corda/node/internal/cordapp/CordappLoader.kt +++ b/node/src/main/kotlin/net/corda/node/internal/cordapp/CordappLoader.kt @@ -50,7 +50,7 @@ class CordappLoader private constructor(private val cordappJarPaths: List corDapp.allFlows.map { flow -> flow to corDapp } } .groupBy { it.first } .mapValues { - require(it.value.size == 1) { "There are multiple CorDapp jars on the classpath for flow ${it.value.first().first.name}: ${it.value.map { it.second.name }.joinToString()}." } + if(it.value.size > 1) { throw MultipleCordappsForFlowException("There are multiple CorDapp JARs on the classpath for flow ${it.value.first().first.name}: [ ${it.value.joinToString { it.second.name }} ].") } it.value.single().second } } @@ -363,3 +363,8 @@ class CordappLoader private constructor(private val cordappJarPaths: List Date: Tue, 26 Jun 2018 11:53:16 +0100 Subject: [PATCH 5/5] ENT-1467: Make the deterministic JDK image compatible with IntelliJ. (#3416) * Expand the deterministic JDK image to make it friendlier to IntelliJ. * Fix Gradle always to use the latest deterministic rt.jar available. * Write JDK items directly from Gradle. --- build.gradle | 1 - create-jdk8u/build.gradle | 1 + docs/source/deterministic-modules.rst | 72 +++++++++++++++------------ jdk8u-deterministic/build.gradle | 22 ++++++-- 4 files changed, 59 insertions(+), 37 deletions(-) diff --git a/build.gradle b/build.gradle index 8001c6ac10..499fc43e35 100644 --- a/build.gradle +++ b/build.gradle @@ -83,7 +83,6 @@ buildscript { ext.jcabi_manifests_version = '1.1' ext.picocli_version = '3.0.0' - ext.deterministic_rt_version = '1.0-SNAPSHOT' // Name of the IntelliJ SDK created for the deterministic Java rt.jar. // ext.deterministic_idea_sdk = '1.8 (Deterministic)' diff --git a/create-jdk8u/build.gradle b/create-jdk8u/build.gradle index 30e46cd17f..7c03dcbe3e 100644 --- a/create-jdk8u/build.gradle +++ b/create-jdk8u/build.gradle @@ -48,6 +48,7 @@ task makeJdk(type: Exec) { task runtimeJar(type: Jar, dependsOn: makeJdk) { baseName 'deterministic-rt' + inputs.dir "libs" from(zipTree("libs/rt.jar")) from(zipTree("libs/jce.jar")) diff --git a/docs/source/deterministic-modules.rst b/docs/source/deterministic-modules.rst index 0c77c088d1..0525de7027 100644 --- a/docs/source/deterministic-modules.rst +++ b/docs/source/deterministic-modules.rst @@ -104,54 +104,60 @@ This is possible, but slightly tricky to configure because IntelliJ will not rec Gradle be configured to use the Project's SDK. Creating the Deterministic SDK - #. Create a JDK Home directory with the following contents: + Gradle creates a suitable JDK image in the project's ``jdk8u-deterministic/jdk`` directory, and you can + configure IntelliJ to use this location for this SDK. However, you should also be aware that IntelliJ SDKs + are available for *all* projects to use. - ``jre/lib/rt.jar`` + To create this JDK image, execute the following: - where ``rt.jar`` here is this renamed artifact: + .. code-block:: bash - .. code-block:: xml + $ gradlew jdk8u-deterministic:copyJdk - - net.corda - deterministic-rt - api - + .. - .. + Now select ``File/Project Structure/Platform Settings/SDKs`` and add a new JDK SDK with the + ``jdk8u-deterministic/jdk`` directory as its home. Rename this SDK to something like "1.8 (Deterministic)". - .. note:: Gradle already creates this JDK in the project's ``jdk8u-deterministic/jdk`` directory, and you could - configure IntelliJ to use this location as well. However, you should also be aware that IntelliJ SDKs - are available for *all* projects to use. + This *should* be sufficient for IntelliJ. However, if IntelliJ realises that this SDK does not contain a + full JDK then you will need to configure the new SDK by hand: - To create this deterministic JDK image, execute the following: + #. Create a JDK Home directory with the following contents: - .. code-block:: bash + ``jre/lib/rt.jar`` - $ gradlew jdk8u-deterministic:copyJdk + where ``rt.jar`` here is this renamed artifact: - .. + .. code-block:: xml - #. While IntelliJ is *not* running, locate the ``config/options/jdk.table.xml`` file in IntelliJ's configuration - directory. Add an empty ```` section to this file: + + net.corda + deterministic-rt + api + - .. code-block:: xml + .. - - - - - - - - + #. While IntelliJ is *not* running, locate the ``config/options/jdk.table.xml`` file in IntelliJ's configuration + directory. Add an empty ```` section to this file: - .. + .. code-block:: xml - #. Open IntelliJ and select ``File/Project Structure/Platform Settings/SDKs``. The "1.8 (Deterministic)" SDK should - now be present. Select it and then click on the ``Classpath`` tab. Press the "Add" / "Plus" button to add - ``rt.jar`` to the SDK's classpath. Then select the ``Annotations`` tab and include the same JAR(s) as the other - SDKs. + + + + + + + + + + .. + + #. Open IntelliJ and select ``File/Project Structure/Platform Settings/SDKs``. The "1.8 (Deterministic)" SDK + should now be present. Select it and then click on the ``Classpath`` tab. Press the "Add" / "Plus" button to + add ``rt.jar`` to the SDK's classpath. Then select the ``Annotations`` tab and include the same JAR(s) as + the other SDKs. Configuring the Corda Project #. Open the root ``build.gradle`` file and define this property: diff --git a/jdk8u-deterministic/build.gradle b/jdk8u-deterministic/build.gradle index 13f468581f..2e4d0a2153 100644 --- a/jdk8u-deterministic/build.gradle +++ b/jdk8u-deterministic/build.gradle @@ -8,23 +8,39 @@ repositories { } ext { - jdk_home = "$projectDir/jdk" + jdk_home = "$projectDir/jdk".toString() rt_jar = "$jdk_home/jre/lib/rt.jar".toString() } configurations { - jdk + jdk.resolutionStrategy { + cacheChangingModulesFor 0, 'seconds' + } } dependencies { - jdk "net.corda:deterministic-rt:$deterministic_rt_version:api" + // Ensure everyone uses the latest SNAPSHOT. + jdk "net.corda:deterministic-rt:latest.integration:api" } task copyJdk(type: Copy) { + outputs.dir jdk_home + from(configurations.jdk.asPath) { rename 'deterministic-rt-(.*).jar', 'rt.jar' } into "$jdk_home/jre/lib" + + doLast { + def eol = System.getProperty('line.separator') + file("$jdk_home/release").write "JAVA_VERSION=\"1.8.0_172\"$eol" + mkdir "$jdk_home/bin" + file("$jdk_home/bin/javac").with { + write "#!/bin/sh\necho \"javac 1.8.0_172\"\n" + setExecutable true, false + return + } + } } assemble.dependsOn copyJdk