From c45aa9ffb1ef537a75b7f72ca855e4925aba90af Mon Sep 17 00:00:00 2001 From: Clinton Alexander Date: Mon, 22 Aug 2016 11:49:55 +0100 Subject: [PATCH 01/11] Started capsule work. --- build.gradle | 4 ++++ src/main/java/CordaCaplet.java | 8 ++++++++ 2 files changed, 12 insertions(+) create mode 100644 src/main/java/CordaCaplet.java diff --git a/build.gradle b/build.gradle index 2acd0cc068..f509a56117 100644 --- a/build.gradle +++ b/build.gradle @@ -108,6 +108,7 @@ dependencies { compile "org.jetbrains.kotlin:kotlin-test:$kotlin_version" compile "org.jetbrains.kotlinx:kotlinx-support-jdk8:0.2" compile 'com.squareup.okhttp3:okhttp:3.3.1' + compile 'co.paralleluniverse:capsule:1.0.3' // Unit testing helpers. testCompile 'junit:junit:4.12' @@ -199,11 +200,14 @@ applicationDistribution.into("bin") { task createCapsule(type: FatCapsule, dependsOn: 'quasarScan') { applicationClass 'com.r3corda.node.MainKt' + applicationSource = files(project.tasks.findByName('jar'), 'build/classes/main/CordaCaplet.class') + capsuleManifest { appClassPath = ["jolokia-agent-war-${project.ext.jolokia_version}.war"] systemProperties['log4j.configuration'] = 'log4j2.xml' javaAgents = ["quasar-core-${quasar_version}-jdk8.jar"] minJavaVersion = '1.8.0' + caplets = ['CordaCaplet'] } } diff --git a/src/main/java/CordaCaplet.java b/src/main/java/CordaCaplet.java new file mode 100644 index 0000000000..a2aa247053 --- /dev/null +++ b/src/main/java/CordaCaplet.java @@ -0,0 +1,8 @@ +// Please do not move me. I need to be in the global namespace :( + +public class CordaCaplet extends Capsule { + + protected CordaCaplet(Capsule pred) { + super(pred); + } +} From f6407b9e952563b0c56964df47c6e2d715b8bbdb Mon Sep 17 00:00:00 2001 From: Clinton Alexander Date: Mon, 22 Aug 2016 16:21:51 +0100 Subject: [PATCH 02/11] Caplet now loads plugins from plugins dir. --- src/main/java/CordaCaplet.java | 38 +++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/src/main/java/CordaCaplet.java b/src/main/java/CordaCaplet.java index a2aa247053..d118716328 100644 --- a/src/main/java/CordaCaplet.java +++ b/src/main/java/CordaCaplet.java @@ -1,8 +1,44 @@ -// Please do not move me. I need to be in the global namespace :( +// Due to Capsule being in the default package, which cannot be imported, this caplet +// must also be in the default package. When using Kotlin there are a whole host of exceptions +// trying to construct this from Capsule, so it is written in Java. + +import java.io.File; +import java.nio.file.Path; +import java.util.List; +import java.util.Map; public class CordaCaplet extends Capsule { protected CordaCaplet(Capsule pred) { super(pred); } + + @Override + @SuppressWarnings("unchecked") + protected T attribute(Map.Entry attr) { + if(ATTR_APP_CLASS_PATH == attr) { + T cp = super.attribute(attr); + List classpath = (List) cp; + return (T) augmentClasspath(classpath); + } + return super.attribute(attr); + } + + // TODO: If/when capsule fix the globbing issue (See: https://github.com/puniverse/capsule/issues/109) + // then replace this with a simple glob + // TODO: Make directory configurable + private List augmentClasspath(List classpath) { + File dir = new File("plugins"); + if(!dir.exists()) { + dir.mkdir(); + } + + File[] files = dir.listFiles(); + for (File file : files) { + if (file.isFile()) { + classpath.add(file.toPath().toAbsolutePath()); + } + } + return classpath; + } } From 214c98b6aac4a096524f31543c7390152c935805 Mon Sep 17 00:00:00 2001 From: Clinton Alexander Date: Tue, 23 Aug 2016 10:25:06 +0100 Subject: [PATCH 03/11] Added first Cordapp documentation. --- docs/source/creating-a-cordapp.rst | 65 ++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 docs/source/creating-a-cordapp.rst diff --git a/docs/source/creating-a-cordapp.rst b/docs/source/creating-a-cordapp.rst new file mode 100644 index 0000000000..c79d4560e9 --- /dev/null +++ b/docs/source/creating-a-cordapp.rst @@ -0,0 +1,65 @@ +Creating a Cordapp +================== + +A Cordapp is an application that runs on the Corda platform using Corda APIs and plugin system. Cordapps will be self +contained in separate JARs from the Corda core JAR that is created and distributed. + +Plugins +------- + +.. note:: Currently plugins are only supported via Kotlin or Java. + +To create a plugin you must extend from ``com.r3corda.core.node.CordaPluginRegistry``. The JavaDoc will contain +specific details of the implementation, but there are broadly the following extention points; + +1. Required protocols: Explicitly state which protocols are required for your plugin. +2. Service plugins: Register your services that run when Corda runs, which will be provided a ``ServiceHub``. +3. Web APIs: You may register your own endpoints under /api/ of the build in web server. +4. Static web endpoints: You may register your own static serving directories for serving web content. + +Services are used primarily for creating your own protocols, which will be the work horse of your Corda plugins. Web +APIs and serving directories will be for creating your own interface to Corda. + +Standalone Nodes +---------------- + +To use a plugin you must also have a standalone node. To create a standalone node you run: + +**Windows**:: + + gradlew.bat createStandalone + +**Other**:: + + ./gradlew createStandalone + +This will output the standalone to ``build/libs/r3prototyping-x.y-SNAPSHOT-capsule.jar`` and several sample/standard +standalone nodes to ``build/standalone``. For now you can use the ``build/standalone/nodea`` directory as a template or +example. + +Each standalone node must contain a ``node.conf`` file in the same directory as the standalone JAR file. After first +execution of the standalone node there will be many other configuration and persistence files created in this directory. + +.. warning:: Deleting your standalone node directory in any way, including ``gradle clean`` will delete all persistence. + +.. note:: Outside of development environments do not store your standalone node in the build folder. + +Installing Plugins +------------------ + +Once you have created your distribution JAR you can install it to a node by adding it to ``/plugins/``. In this +case the ``node_dir`` is the location where your standalone node's JAR and configuration file is. + +.. note:: If the plugins directory does not exist you can create it manually. + +Starting your Node +------------------ + +Now you have a standalone node with your Cordapp installed, you can run it by navigating to ```` and running + + java -jar r3prototyping-x.y-SNAPSHOT-capsule.jar + +The plugin should automatically be registered and the configuration file used. + +.. warning:: If your working directory is not ```` your plugins and configuration will not be used. + From c03d9a2cf7a34aa0fe40c48a233f00a563527fd0 Mon Sep 17 00:00:00 2001 From: Clinton Alexander Date: Wed, 24 Aug 2016 15:33:43 +0100 Subject: [PATCH 04/11] Review fixes. --- docs/source/creating-a-cordapp.rst | 18 ++++++++++-------- docs/source/index.rst | 1 + 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/docs/source/creating-a-cordapp.rst b/docs/source/creating-a-cordapp.rst index c79d4560e9..2c2b39e80c 100644 --- a/docs/source/creating-a-cordapp.rst +++ b/docs/source/creating-a-cordapp.rst @@ -1,20 +1,22 @@ Creating a Cordapp ================== -A Cordapp is an application that runs on the Corda platform using Corda APIs and plugin system. Cordapps will be self -contained in separate JARs from the Corda core JAR that is created and distributed. +A Cordapp is an application that runs on the Corda platform using Corda APIs and plugin system. Cordapps are self +contained in separate JARs from the Corda standalone JAR that are created and distributed. Plugins ------- -.. note:: Currently plugins are only supported via Kotlin or Java. +.. note:: Currently plugins are only supported for JVM languages. -To create a plugin you must extend from ``com.r3corda.core.node.CordaPluginRegistry``. The JavaDoc will contain -specific details of the implementation, but there are broadly the following extention points; +To create a plugin you must extend from `CordaPluginRegistry`_. The JavaDoc contains +specific details of the implementation, but you can extend the server in the following ways: -1. Required protocols: Explicitly state which protocols are required for your plugin. -2. Service plugins: Register your services that run when Corda runs, which will be provided a ``ServiceHub``. -3. Web APIs: You may register your own endpoints under /api/ of the build in web server. +.. _CordaPluginRegistry: api/com.r3corda.core.node/-corda-plugin-registry/index.html + +1. Required protocols: Specify which protocols will be whitelisted for use in your web APIs. +2. Service plugins: Register your services that run when Corda runs. +3. Web APIs: You may register your own endpoints under /api/ of the built-in web server. 4. Static web endpoints: You may register your own static serving directories for serving web content. Services are used primarily for creating your own protocols, which will be the work horse of your Corda plugins. Web diff --git a/docs/source/index.rst b/docs/source/index.rst index 46da547db0..e1fbac846b 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -31,6 +31,7 @@ Read on to learn: transaction-data-types consensus messaging + creating-a-cordapp running-the-demos node-administration From 24df3e3c52d3a78360ae5bff41cf0327c997ebd0 Mon Sep 17 00:00:00 2001 From: Clinton Alexander Date: Wed, 24 Aug 2016 16:18:52 +0100 Subject: [PATCH 05/11] Removed term standalone and replaced with more neutral language. --- build.gradle | 14 +++++++------- buildSrc/scripts/{runstandalone => runnodes} | 0 2 files changed, 7 insertions(+), 7 deletions(-) rename buildSrc/scripts/{runstandalone => runnodes} (100%) diff --git a/build.gradle b/build.gradle index f509a56117..8ba7f671e9 100644 --- a/build.gradle +++ b/build.gradle @@ -211,35 +211,35 @@ task createCapsule(type: FatCapsule, dependsOn: 'quasarScan') { } } -task createStandalone(dependsOn: 'createCapsule') << { +task installTemplateNodes(dependsOn: 'createCapsule') << { copy { from createCapsule.outputs.getFiles() from 'config/dev/nameservernode.conf' - into "${buildDir}/standalone/nameserver" + into "${buildDir}/nodes/nameserver" rename 'nameservernode.conf', 'node.conf' } copy { from createCapsule.outputs.getFiles() from 'config/dev/generalnodea.conf' - into "${buildDir}/standalone/nodea" + into "${buildDir}/nodes/nodea" rename 'generalnodea.conf', 'node.conf' } copy { from createCapsule.outputs.getFiles() from 'config/dev/generalnodeb.conf' - into "${buildDir}/standalone/nodeb" + into "${buildDir}/nodes/nodeb" rename 'generalnodeb.conf', 'node.conf' } - delete("${buildDir}/standalone/runstandalone") + delete("${buildDir}/nodes/runnodes") def jarName = createCapsule.outputs.getFiles().getSingleFile().getName() copy { - from "buildSrc/scripts/runstandalone" + from "buildSrc/scripts/runnodes" filter { String line -> line.replace("JAR_NAME", jarName) } filter(org.apache.tools.ant.filters.FixCrLfFilter.class, eol: org.apache.tools.ant.filters.FixCrLfFilter.CrLf.newInstance("lf")) - into "${buildDir}/standalone" + into "${buildDir}/nodes" } } \ No newline at end of file diff --git a/buildSrc/scripts/runstandalone b/buildSrc/scripts/runnodes similarity index 100% rename from buildSrc/scripts/runstandalone rename to buildSrc/scripts/runnodes From 70c057dfef64b7e2e6ce1ae16977576c23ba1472 Mon Sep 17 00:00:00 2001 From: Clinton Alexander Date: Wed, 24 Aug 2016 16:19:11 +0100 Subject: [PATCH 06/11] Review changes. --- docs/source/creating-a-cordapp.rst | 57 +++++++++++++++--------------- 1 file changed, 28 insertions(+), 29 deletions(-) diff --git a/docs/source/creating-a-cordapp.rst b/docs/source/creating-a-cordapp.rst index 2c2b39e80c..3f8327e1fa 100644 --- a/docs/source/creating-a-cordapp.rst +++ b/docs/source/creating-a-cordapp.rst @@ -2,62 +2,61 @@ Creating a Cordapp ================== A Cordapp is an application that runs on the Corda platform using Corda APIs and plugin system. Cordapps are self -contained in separate JARs from the Corda standalone JAR that are created and distributed. +contained in separate JARs from the Corda node server JAR that are created and distributed. -Plugins -------- +App Plugins +----------- -.. note:: Currently plugins are only supported for JVM languages. +.. note:: Currently apps are only supported for JVM languages. -To create a plugin you must extend from `CordaPluginRegistry`_. The JavaDoc contains +To create an app plugin you must you must extend from `CordaPluginRegistry`_. The JavaDoc contains specific details of the implementation, but you can extend the server in the following ways: .. _CordaPluginRegistry: api/com.r3corda.core.node/-corda-plugin-registry/index.html +.. _ServiceHubInternal: api/com.r3corda.node.services.api/-service-hub-internal/index.html +.. _ServiceHub: api/com.r3corda.node.services.api/-service-hub/index.html 1. Required protocols: Specify which protocols will be whitelisted for use in your web APIs. -2. Service plugins: Register your services that run when Corda runs. +2. Service plugins: Register your :ref:`services`. 3. Web APIs: You may register your own endpoints under /api/ of the built-in web server. 4. Static web endpoints: You may register your own static serving directories for serving web content. -Services are used primarily for creating your own protocols, which will be the work horse of your Corda plugins. Web -APIs and serving directories will be for creating your own interface to Corda. +Services +-------- -Standalone Nodes ----------------- +.. _services: -To use a plugin you must also have a standalone node. To create a standalone node you run: +Services are classes which are constructed after the node has started. It is provided a `ServiceHubInternal`_ which +allows a more rich API than the `ServiceHub`_ exposed to contracts. It enables adding protocols, registering +message handlers and more. The service does not run in a thread, the only entry point to the service is during +construction, where message handlers should be registered and threads started. -**Windows**:: - gradlew.bat createStandalone +Starting Nodes +-------------- -**Other**:: +To use an app you must also have a node server. To create a node server run the gradle createStandalone task. - ./gradlew createStandalone +This will output the node JAR to ``build/libs/r3prototyping-x.y-SNAPSHOT-capsule.jar`` and several sample/standard +node servers to ``build/standalone``. For now you can use the ``build/standalone/nodea`` configuration as a template. -This will output the standalone to ``build/libs/r3prototyping-x.y-SNAPSHOT-capsule.jar`` and several sample/standard -standalone nodes to ``build/standalone``. For now you can use the ``build/standalone/nodea`` directory as a template or -example. +Each node server must have a ``node.conf`` file in the same directory as the node JAR file. After first +execution of the node server there will be many other configuration and persistence files created in this directory. -Each standalone node must contain a ``node.conf`` file in the same directory as the standalone JAR file. After first -execution of the standalone node there will be many other configuration and persistence files created in this directory. +.. note:: Outside of development environments do not store your node directories in the build folder. -.. warning:: Deleting your standalone node directory in any way, including ``gradle clean`` will delete all persistence. - -.. note:: Outside of development environments do not store your standalone node in the build folder. - -Installing Plugins +Installing Apps ------------------ -Once you have created your distribution JAR you can install it to a node by adding it to ``/plugins/``. In this -case the ``node_dir`` is the location where your standalone node's JAR and configuration file is. +Once you have created your app JAR you can install it to a node by adding it to ``/plugins/``. In this +case the ``node_dir`` is the location where your node server's JAR and configuration file is. -.. note:: If the plugins directory does not exist you can create it manually. +.. note:: If the directory does not exist you can create it manually. Starting your Node ------------------ -Now you have a standalone node with your Cordapp installed, you can run it by navigating to ```` and running +Now you have a node server with your Cordapp installed, you can run it by navigating to ```` and running java -jar r3prototyping-x.y-SNAPSHOT-capsule.jar From 46528723382ed37da9d85ec72ac63ca3e50efac5 Mon Sep 17 00:00:00 2001 From: Clinton Alexander Date: Wed, 24 Aug 2016 16:28:28 +0100 Subject: [PATCH 07/11] Renamed corda output JAR to corda.jar. --- build.gradle | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/build.gradle b/build.gradle index 8ba7f671e9..ac12331bd7 100644 --- a/build.gradle +++ b/build.gradle @@ -197,9 +197,9 @@ applicationDistribution.into("bin") { fileMode = 0755 } -task createCapsule(type: FatCapsule, dependsOn: 'quasarScan') { +task buildCordaJAR(type: FatCapsule, dependsOn: 'quasarScan') { applicationClass 'com.r3corda.node.MainKt' - + archiveName 'corda.jar' applicationSource = files(project.tasks.findByName('jar'), 'build/classes/main/CordaCaplet.class') capsuleManifest { @@ -211,30 +211,30 @@ task createCapsule(type: FatCapsule, dependsOn: 'quasarScan') { } } -task installTemplateNodes(dependsOn: 'createCapsule') << { +task installTemplateNodes(dependsOn: 'buildCordaJAR') << { copy { - from createCapsule.outputs.getFiles() + from buildCordaJAR.outputs.getFiles() from 'config/dev/nameservernode.conf' into "${buildDir}/nodes/nameserver" rename 'nameservernode.conf', 'node.conf' } copy { - from createCapsule.outputs.getFiles() + from buildCordaJAR.outputs.getFiles() from 'config/dev/generalnodea.conf' into "${buildDir}/nodes/nodea" rename 'generalnodea.conf', 'node.conf' } copy { - from createCapsule.outputs.getFiles() + from buildCordaJAR.outputs.getFiles() from 'config/dev/generalnodeb.conf' into "${buildDir}/nodes/nodeb" rename 'generalnodeb.conf', 'node.conf' } delete("${buildDir}/nodes/runnodes") - def jarName = createCapsule.outputs.getFiles().getSingleFile().getName() + def jarName = buildCordaJAR.outputs.getFiles().getSingleFile().getName() copy { from "buildSrc/scripts/runnodes" filter { String line -> line.replace("JAR_NAME", jarName) } From bdcbd80d4ec3f8177ec65c6799bf6daaf57fef0f Mon Sep 17 00:00:00 2001 From: Clinton Alexander Date: Wed, 24 Aug 2016 16:30:35 +0100 Subject: [PATCH 08/11] Updated docs to reflect gradle changes. --- docs/source/creating-a-cordapp.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/source/creating-a-cordapp.rst b/docs/source/creating-a-cordapp.rst index 3f8327e1fa..a3eb561973 100644 --- a/docs/source/creating-a-cordapp.rst +++ b/docs/source/creating-a-cordapp.rst @@ -35,10 +35,10 @@ construction, where message handlers should be registered and threads started. Starting Nodes -------------- -To use an app you must also have a node server. To create a node server run the gradle createStandalone task. +To use an app you must also have a node server. To create a node server run the gradle installTemplateNodes task. -This will output the node JAR to ``build/libs/r3prototyping-x.y-SNAPSHOT-capsule.jar`` and several sample/standard -node servers to ``build/standalone``. For now you can use the ``build/standalone/nodea`` configuration as a template. +This will output the node JAR to ``build/libs/corda.jar`` and several sample/standard +node servers to ``build/nodes``. For now you can use the ``build/nodes/nodea`` configuration as a template. Each node server must have a ``node.conf`` file in the same directory as the node JAR file. After first execution of the node server there will be many other configuration and persistence files created in this directory. @@ -58,7 +58,7 @@ Starting your Node Now you have a node server with your Cordapp installed, you can run it by navigating to ```` and running - java -jar r3prototyping-x.y-SNAPSHOT-capsule.jar + java -jar corda.jar The plugin should automatically be registered and the configuration file used. From a8d73fb73fe12dc0b28bca558b4101b5eb247d12 Mon Sep 17 00:00:00 2001 From: Clinton Alexander Date: Wed, 24 Aug 2016 16:40:38 +0100 Subject: [PATCH 09/11] Caplet now filters for JAR files. --- src/main/java/CordaCaplet.java | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/main/java/CordaCaplet.java b/src/main/java/CordaCaplet.java index d118716328..fcf28f14c7 100644 --- a/src/main/java/CordaCaplet.java +++ b/src/main/java/CordaCaplet.java @@ -2,6 +2,8 @@ // must also be in the default package. When using Kotlin there are a whole host of exceptions // trying to construct this from Capsule, so it is written in Java. +import org.apache.commons.io.FilenameUtils; + import java.io.File; import java.nio.file.Path; import java.util.List; @@ -13,6 +15,9 @@ public class CordaCaplet extends Capsule { super(pred); } + /** + * Overriding the Caplet classpath generation via the intended interface in Capsule. + */ @Override @SuppressWarnings("unchecked") protected T attribute(Map.Entry attr) { @@ -24,9 +29,8 @@ public class CordaCaplet extends Capsule { return super.attribute(attr); } - // TODO: If/when capsule fix the globbing issue (See: https://github.com/puniverse/capsule/issues/109) - // then replace this with a simple glob - // TODO: Make directory configurable + // TODO: Make directory configurable via the capsule manifest. + // TODO: Add working directory variable to capsules string replacement variables. private List augmentClasspath(List classpath) { File dir = new File("plugins"); if(!dir.exists()) { @@ -35,7 +39,8 @@ public class CordaCaplet extends Capsule { File[] files = dir.listFiles(); for (File file : files) { - if (file.isFile()) { + Boolean isJAR = FilenameUtils.getExtension(file.getName()).toLowerCase() == "jar"; + if (file.isFile() && isJAR) { classpath.add(file.toPath().toAbsolutePath()); } } From f46cbd32c698a2720ed7a1fa5fd966da225f0b30 Mon Sep 17 00:00:00 2001 From: Clinton Alexander Date: Wed, 24 Aug 2016 17:44:47 +0100 Subject: [PATCH 10/11] Fixed isJar checks. --- src/main/java/CordaCaplet.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/java/CordaCaplet.java b/src/main/java/CordaCaplet.java index fcf28f14c7..078749efdb 100644 --- a/src/main/java/CordaCaplet.java +++ b/src/main/java/CordaCaplet.java @@ -39,11 +39,15 @@ public class CordaCaplet extends Capsule { File[] files = dir.listFiles(); for (File file : files) { - Boolean isJAR = FilenameUtils.getExtension(file.getName()).toLowerCase() == "jar"; - if (file.isFile() && isJAR) { + if (file.isFile() && isJAR(file)) { classpath.add(file.toPath().toAbsolutePath()); } } return classpath; } + + private Boolean isJAR(File file) { + String[] parts = file.getName().split("\\."); + return (parts.length > 1) && (parts[parts.length - 1].toLowerCase().equals("jar")); + } } From f2a93ea67bb3189428a5cee5bbce8c4226236a2d Mon Sep 17 00:00:00 2001 From: Clinton Alexander Date: Wed, 24 Aug 2016 21:03:20 +0100 Subject: [PATCH 11/11] Fixed review comments. --- buildSrc/scripts/runnodes | 2 ++ docs/source/creating-a-cordapp.rst | 19 +++++++++---------- src/main/java/CordaCaplet.java | 5 +++-- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/buildSrc/scripts/runnodes b/buildSrc/scripts/runnodes index dad232ec07..a6bec7cef8 100644 --- a/buildSrc/scripts/runnodes +++ b/buildSrc/scripts/runnodes @@ -1,4 +1,6 @@ #!/usr/bin/env bash +# Creates three nodes. A network map and notary node and two regular nodes that can be extended with cordapps. + set -euo pipefail trap 'kill $(jobs -p)' SIGINT SIGTERM EXIT export CAPSULE_CACHE_DIR=cache diff --git a/docs/source/creating-a-cordapp.rst b/docs/source/creating-a-cordapp.rst index a3eb561973..3831eb9c07 100644 --- a/docs/source/creating-a-cordapp.rst +++ b/docs/source/creating-a-cordapp.rst @@ -1,8 +1,8 @@ Creating a Cordapp ================== -A Cordapp is an application that runs on the Corda platform using Corda APIs and plugin system. Cordapps are self -contained in separate JARs from the Corda node server JAR that are created and distributed. +A Cordapp is an application that runs on the Corda platform using the platform APIs and plugin system. They are self +contained in separate JARs from the node server JAR that are created and distributed. App Plugins ----------- @@ -12,10 +12,6 @@ App Plugins To create an app plugin you must you must extend from `CordaPluginRegistry`_. The JavaDoc contains specific details of the implementation, but you can extend the server in the following ways: -.. _CordaPluginRegistry: api/com.r3corda.core.node/-corda-plugin-registry/index.html -.. _ServiceHubInternal: api/com.r3corda.node.services.api/-service-hub-internal/index.html -.. _ServiceHub: api/com.r3corda.node.services.api/-service-hub/index.html - 1. Required protocols: Specify which protocols will be whitelisted for use in your web APIs. 2. Service plugins: Register your :ref:`services`. 3. Web APIs: You may register your own endpoints under /api/ of the built-in web server. @@ -27,8 +23,8 @@ Services .. _services: Services are classes which are constructed after the node has started. It is provided a `ServiceHubInternal`_ which -allows a more rich API than the `ServiceHub`_ exposed to contracts. It enables adding protocols, registering -message handlers and more. The service does not run in a thread, the only entry point to the service is during +allows a richer API than the `ServiceHub`_ exposed to contracts. It enables adding protocols, registering +message handlers and more. The service does not run in a separate thread, so the only entry point to the service is during construction, where message handlers should be registered and threads started. @@ -38,7 +34,7 @@ Starting Nodes To use an app you must also have a node server. To create a node server run the gradle installTemplateNodes task. This will output the node JAR to ``build/libs/corda.jar`` and several sample/standard -node servers to ``build/nodes``. For now you can use the ``build/nodes/nodea`` configuration as a template. +node setups to ``build/nodes``. For now you can use the ``build/nodes/nodea`` configuration as a template. Each node server must have a ``node.conf`` file in the same directory as the node JAR file. After first execution of the node server there will be many other configuration and persistence files created in this directory. @@ -56,7 +52,7 @@ case the ``node_dir`` is the location where your node server's JAR and configura Starting your Node ------------------ -Now you have a node server with your Cordapp installed, you can run it by navigating to ```` and running +Now you have a node server with your app installed, you can run it by navigating to ```` and running java -jar corda.jar @@ -64,3 +60,6 @@ The plugin should automatically be registered and the configuration file used. .. warning:: If your working directory is not ```` your plugins and configuration will not be used. +.. _CordaPluginRegistry: api/com.r3corda.core.node/-corda-plugin-registry/index.html +.. _ServiceHubInternal: api/com.r3corda.node.services.api/-service-hub-internal/index.html +.. _ServiceHub: api/com.r3corda.node.services.api/-service-hub/index.html \ No newline at end of file diff --git a/src/main/java/CordaCaplet.java b/src/main/java/CordaCaplet.java index 078749efdb..9f734bc968 100644 --- a/src/main/java/CordaCaplet.java +++ b/src/main/java/CordaCaplet.java @@ -21,6 +21,8 @@ public class CordaCaplet extends Capsule { @Override @SuppressWarnings("unchecked") protected T attribute(Map.Entry attr) { + // Equality is used here because Capsule never instantiates these attributes but instead reuses the ones + // defined as public static final fields on the Capsule class, therefore referential equality is safe. if(ATTR_APP_CLASS_PATH == attr) { T cp = super.attribute(attr); List classpath = (List) cp; @@ -47,7 +49,6 @@ public class CordaCaplet extends Capsule { } private Boolean isJAR(File file) { - String[] parts = file.getName().split("\\."); - return (parts.length > 1) && (parts[parts.length - 1].toLowerCase().equals("jar")); + return file.getName().toLowerCase().endsWith(".jar"); } }