mirror of
https://github.com/corda/corda.git
synced 2025-02-21 09:51:57 +00:00
Merged in clint-caplet (pull request #292)
Node server now loads plugins/cordapps from directory
This commit is contained in:
commit
4cb6d11e9f
28
build.gradle
28
build.gradle
@ -108,6 +108,7 @@ dependencies {
|
|||||||
compile "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
|
compile "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
|
||||||
compile "org.jetbrains.kotlinx:kotlinx-support-jdk8:0.2"
|
compile "org.jetbrains.kotlinx:kotlinx-support-jdk8:0.2"
|
||||||
compile 'com.squareup.okhttp3:okhttp:3.3.1'
|
compile 'com.squareup.okhttp3:okhttp:3.3.1'
|
||||||
|
compile 'co.paralleluniverse:capsule:1.0.3'
|
||||||
|
|
||||||
// Unit testing helpers.
|
// Unit testing helpers.
|
||||||
testCompile 'junit:junit:4.12'
|
testCompile 'junit:junit:4.12'
|
||||||
@ -196,46 +197,49 @@ applicationDistribution.into("bin") {
|
|||||||
fileMode = 0755
|
fileMode = 0755
|
||||||
}
|
}
|
||||||
|
|
||||||
task createCapsule(type: FatCapsule, dependsOn: 'quasarScan') {
|
task buildCordaJAR(type: FatCapsule, dependsOn: 'quasarScan') {
|
||||||
applicationClass 'com.r3corda.node.MainKt'
|
applicationClass 'com.r3corda.node.MainKt'
|
||||||
|
archiveName 'corda.jar'
|
||||||
|
applicationSource = files(project.tasks.findByName('jar'), 'build/classes/main/CordaCaplet.class')
|
||||||
|
|
||||||
capsuleManifest {
|
capsuleManifest {
|
||||||
appClassPath = ["jolokia-agent-war-${project.ext.jolokia_version}.war"]
|
appClassPath = ["jolokia-agent-war-${project.ext.jolokia_version}.war"]
|
||||||
systemProperties['log4j.configuration'] = 'log4j2.xml'
|
systemProperties['log4j.configuration'] = 'log4j2.xml'
|
||||||
javaAgents = ["quasar-core-${quasar_version}-jdk8.jar"]
|
javaAgents = ["quasar-core-${quasar_version}-jdk8.jar"]
|
||||||
minJavaVersion = '1.8.0'
|
minJavaVersion = '1.8.0'
|
||||||
|
caplets = ['CordaCaplet']
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
task createStandalone(dependsOn: 'createCapsule') << {
|
task installTemplateNodes(dependsOn: 'buildCordaJAR') << {
|
||||||
copy {
|
copy {
|
||||||
from createCapsule.outputs.getFiles()
|
from buildCordaJAR.outputs.getFiles()
|
||||||
from 'config/dev/nameservernode.conf'
|
from 'config/dev/nameservernode.conf'
|
||||||
into "${buildDir}/standalone/nameserver"
|
into "${buildDir}/nodes/nameserver"
|
||||||
rename 'nameservernode.conf', 'node.conf'
|
rename 'nameservernode.conf', 'node.conf'
|
||||||
}
|
}
|
||||||
|
|
||||||
copy {
|
copy {
|
||||||
from createCapsule.outputs.getFiles()
|
from buildCordaJAR.outputs.getFiles()
|
||||||
from 'config/dev/generalnodea.conf'
|
from 'config/dev/generalnodea.conf'
|
||||||
into "${buildDir}/standalone/nodea"
|
into "${buildDir}/nodes/nodea"
|
||||||
rename 'generalnodea.conf', 'node.conf'
|
rename 'generalnodea.conf', 'node.conf'
|
||||||
}
|
}
|
||||||
|
|
||||||
copy {
|
copy {
|
||||||
from createCapsule.outputs.getFiles()
|
from buildCordaJAR.outputs.getFiles()
|
||||||
from 'config/dev/generalnodeb.conf'
|
from 'config/dev/generalnodeb.conf'
|
||||||
into "${buildDir}/standalone/nodeb"
|
into "${buildDir}/nodes/nodeb"
|
||||||
rename 'generalnodeb.conf', 'node.conf'
|
rename 'generalnodeb.conf', 'node.conf'
|
||||||
}
|
}
|
||||||
|
|
||||||
delete("${buildDir}/standalone/runstandalone")
|
delete("${buildDir}/nodes/runnodes")
|
||||||
def jarName = createCapsule.outputs.getFiles().getSingleFile().getName()
|
def jarName = buildCordaJAR.outputs.getFiles().getSingleFile().getName()
|
||||||
copy {
|
copy {
|
||||||
from "buildSrc/scripts/runstandalone"
|
from "buildSrc/scripts/runnodes"
|
||||||
filter { String line -> line.replace("JAR_NAME", jarName) }
|
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"))
|
filter(org.apache.tools.ant.filters.FixCrLfFilter.class, eol: org.apache.tools.ant.filters.FixCrLfFilter.CrLf.newInstance("lf"))
|
||||||
into "${buildDir}/standalone"
|
into "${buildDir}/nodes"
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -1,4 +1,6 @@
|
|||||||
#!/usr/bin/env bash
|
#!/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
|
set -euo pipefail
|
||||||
trap 'kill $(jobs -p)' SIGINT SIGTERM EXIT
|
trap 'kill $(jobs -p)' SIGINT SIGTERM EXIT
|
||||||
export CAPSULE_CACHE_DIR=cache
|
export CAPSULE_CACHE_DIR=cache
|
65
docs/source/creating-a-cordapp.rst
Normal file
65
docs/source/creating-a-cordapp.rst
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
Creating a Cordapp
|
||||||
|
==================
|
||||||
|
|
||||||
|
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
|
||||||
|
-----------
|
||||||
|
|
||||||
|
.. note:: Currently apps are only supported for JVM languages.
|
||||||
|
|
||||||
|
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:
|
||||||
|
|
||||||
|
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.
|
||||||
|
4. Static web endpoints: You may register your own static serving directories for serving web content.
|
||||||
|
|
||||||
|
Services
|
||||||
|
--------
|
||||||
|
|
||||||
|
.. _services:
|
||||||
|
|
||||||
|
Services are classes which are constructed after the node has started. It is provided a `ServiceHubInternal`_ which
|
||||||
|
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.
|
||||||
|
|
||||||
|
|
||||||
|
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 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.
|
||||||
|
|
||||||
|
.. note:: Outside of development environments do not store your node directories in the build folder.
|
||||||
|
|
||||||
|
Installing Apps
|
||||||
|
------------------
|
||||||
|
|
||||||
|
Once you have created your app JAR you can install it to a node by adding it to ``<node_dir>/plugins/``. In this
|
||||||
|
case the ``node_dir`` is the location where your node server's JAR and configuration file is.
|
||||||
|
|
||||||
|
.. note:: If the directory does not exist you can create it manually.
|
||||||
|
|
||||||
|
Starting your Node
|
||||||
|
------------------
|
||||||
|
|
||||||
|
Now you have a node server with your app installed, you can run it by navigating to ``<node_dir>`` and running
|
||||||
|
|
||||||
|
java -jar corda.jar
|
||||||
|
|
||||||
|
The plugin should automatically be registered and the configuration file used.
|
||||||
|
|
||||||
|
.. warning:: If your working directory is not ``<node_dir>`` 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
|
@ -31,6 +31,7 @@ Read on to learn:
|
|||||||
transaction-data-types
|
transaction-data-types
|
||||||
consensus
|
consensus
|
||||||
messaging
|
messaging
|
||||||
|
creating-a-cordapp
|
||||||
running-the-demos
|
running-the-demos
|
||||||
node-administration
|
node-administration
|
||||||
|
|
||||||
|
54
src/main/java/CordaCaplet.java
Normal file
54
src/main/java/CordaCaplet.java
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
// 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 org.apache.commons.io.FilenameUtils;
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overriding the Caplet classpath generation via the intended interface in Capsule.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
protected <T> T attribute(Map.Entry<String, T> 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<Path> classpath = (List<Path>) cp;
|
||||||
|
return (T) augmentClasspath(classpath);
|
||||||
|
}
|
||||||
|
return super.attribute(attr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Make directory configurable via the capsule manifest.
|
||||||
|
// TODO: Add working directory variable to capsules string replacement variables.
|
||||||
|
private List<Path> augmentClasspath(List<Path> classpath) {
|
||||||
|
File dir = new File("plugins");
|
||||||
|
if(!dir.exists()) {
|
||||||
|
dir.mkdir();
|
||||||
|
}
|
||||||
|
|
||||||
|
File[] files = dir.listFiles();
|
||||||
|
for (File file : files) {
|
||||||
|
if (file.isFile() && isJAR(file)) {
|
||||||
|
classpath.add(file.toPath().toAbsolutePath());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return classpath;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Boolean isJAR(File file) {
|
||||||
|
return file.getName().toLowerCase().endsWith(".jar");
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user