Skip to content

Commit

Permalink
FABN-887 NodeSDK - deploy Java chaincode
Browse files Browse the repository at this point in the history
Allow for the install and instantiation of Java
chaincode.

Change-Id: I25082c6f4a6241dc5b1eb8e5980eaea2fbc1735f
Signed-off-by: Bret Harrison <beharrison@nc.rr.com>
  • Loading branch information
harrisob committed Sep 10, 2018
1 parent f013164 commit 8796095
Show file tree
Hide file tree
Showing 19 changed files with 776 additions and 18 deletions.
2 changes: 2 additions & 0 deletions build/tasks/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ gulp.task('run-full', ['clean-up', 'lint', 'pre-test', 'compile', 'docker-ready'
'test/integration/invoke.js',
'test/integration/network-config.js',
'test/integration/only-admin.js',
'test/integration/javachaincode/e2e.js',
'test/integration/discovery.js',
'test/integration/grpc.js',
// channel: mychannelts chaincode: examplets:v1
Expand Down Expand Up @@ -249,6 +250,7 @@ function shouldRunPKCS11Tests(tests) {
if (os.arch().match(/(x64|x86)/) === null ||
!(typeof process.env.PKCS11_TESTS === 'string' && process.env.PKCS11_TESTS.toLowerCase() == 'true')) {
tests.push('!test/unit/pkcs11.js');
tests.push('!test/integration/javachaincode/e2e.js');
}

return tests;
Expand Down
2 changes: 1 addition & 1 deletion fabric-client/lib/Client.js
Original file line number Diff line number Diff line change
Expand Up @@ -917,7 +917,7 @@ const Client = class extends BaseClient {
* where the chaincode source code resides.
* @property {string} chaincodeType - Optional. Type of chaincode. One of
* 'golang', 'car', 'node' or 'java'.
* Default is 'golang'. Note that 'java' is not yet supported.
* Default is 'golang'.
* @property {string[] | string} channelNames - Optional. When no targets are
* provided. The loaded network configuration will be searched for
* suitable target peers. Peers that are defined in the channels named
Expand Down
4 changes: 4 additions & 0 deletions fabric-client/lib/Packager.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
const Golang = require('./packager/Golang.js');
const Car = require('./packager/Car.js');
const Node = require('./packager/Node.js');
const Java = require('./packager/Java.js');
const utils = require('./utils.js');

const logger = utils.getLogger('packager');
Expand Down Expand Up @@ -59,6 +60,9 @@ module.exports.package = function(chaincodePath, chaincodeType, devmode, metadat
case 'node':
handler = new Node();
break;
case 'java':
handler = new Java();
break;
default:
handler = new Golang(['.go','.c','.h','.s']);
}
Expand Down
73 changes: 73 additions & 0 deletions fabric-client/lib/packager/Java.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*
Copyright 2017, 2018 IBM All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

'use strict';

const path = require('path');
const sbuf = require('stream-buffers');
const utils = require('../utils.js');
const walk = require('ignore-walk');

const logger = utils.getLogger('JavaPackager.js');

const BasePackager = require('./BasePackager');

class JavaPackager extends BasePackager {

/**
* Package chaincode source and metadata files for deployment.
* @param {string} chaincodePath The path to the top-level directory containing the source code.
* @param {string} [metadataPath] The path to the top-level directory containing metadata descriptors
* @returns {Promise.<byte[]>}
*/
async package (chaincodePath, metadataPath) {
logger.debug('packaging Java source from %s', chaincodePath);

const buffer = new sbuf.WritableStreamBuffer();
let descriptors = await this.findSource(chaincodePath);
if (metadataPath){
logger.debug('packaging metadata files from %s', metadataPath);

const metaDescriptors = await super.findMetadataDescriptors(metadataPath);
descriptors = descriptors.concat(metaDescriptors);
}
await super.generateTarGz(descriptors, buffer);

return buffer.getContents();
}

/**
* Given an input 'filePath', recursively parse the filesystem for any files
* that fit the criteria for being valid java chaincode source
* note: currently all files found in the source path are included
*
* @param filePath
* @returns {Promise}
*/
async findSource (filePath) {
const descriptors = [];

const files = await walk({path: filePath, follow: true});
if (files) {
files.forEach((entry) => {
const desc = {
name: path.join('src', entry).split('\\').join('/'), // for windows style paths
fqp: path.join(filePath, entry)
};

logger.debug('adding descriptor entry', desc);
descriptors.push(desc);
});
} else {
logger.debug(' No files found at this path %s', filePath);
}

return descriptors;
}
}

module.exports = JavaPackager;
1 change: 1 addition & 0 deletions scripts/check_license.sh
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ function filterExcludedFiles {
| grep -v "\.pb\.go$" \
| grep -v "\.yaml$" \
| grep -v "\.json$" \
| grep -v "\.gradle$" \
| sort -u`
}

Expand Down
29 changes: 29 additions & 0 deletions test/fixtures/src/java_cc/example_cc/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
plugins {
id 'com.github.johnrengelman.shadow' version '2.0.3'
id 'java'
}

group 'org.hyperledger.fabric'
version '1.0-SNAPSHOT'

sourceCompatibility = 1.8

repositories {
mavenLocal()
mavenCentral()
}

dependencies {
compile group: 'org.hyperledger.fabric', name: 'fabric-chaincode-shim', version: '1.3.0-SNAPSHOT'
testCompile group: 'junit', name: 'junit', version: '4.12'
}

shadowJar {
baseName = 'chaincode'
version = null
classifier = null

manifest {
attributes 'Main-Class': 'org.hyperledger.fabric.example.SimpleChaincode'
}
}
1 change: 1 addition & 0 deletions test/fixtures/src/java_cc/example_cc/settings.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
rootProject.name = 'fabric-chaincode-example-gradle'
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
/*
# Copyright IBM Corp. All Rights Reserved.
#
# SPDX-License-Identifier: Apache-2.0
*/


package org.hyperledger.fabric.example;

import java.util.List;
import java.util.Map;

import com.google.protobuf.ByteString;
import io.netty.handler.ssl.OpenSsl;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hyperledger.fabric.shim.ChaincodeBase;
import org.hyperledger.fabric.shim.ChaincodeStub;

import static java.nio.charset.StandardCharsets.UTF_8;

public class SimpleChaincode extends ChaincodeBase {

private static Log _logger = LogFactory.getLog(SimpleChaincode.class);

@Override
public Response init(ChaincodeStub stub) {
try {
_logger.info("Init java simple chaincode");
String func = stub.getFunction();

if (!func.equals("init")) {
return newErrorResponse("function other than init is not supported");
}
List<String> args = stub.getParameters();
if (args.size() != 4) {
newErrorResponse("Incorrect number of arguments. Expecting 4");
}
// Initialize the chaincode
String account1Key = args.get(0);
int account1Value = Integer.parseInt(args.get(1));
String account2Key = args.get(2);
int account2Value = Integer.parseInt(args.get(3));

_logger.info(String.format("account %s, value = %s; account %s, value %s", account1Key, account1Value, account2Key, account2Value));
stub.putStringState(account1Key, args.get(1));
stub.putStringState(account2Key, args.get(3));

return newSuccessResponse();
} catch (Throwable e) {
return newErrorResponse(e);
}
}

@Override
public Response invoke(ChaincodeStub stub) {
try {
_logger.info("Invoke java simple chaincode");
String func = stub.getFunction();
List<String> params = stub.getParameters();
if (func.equals("move")) {
return move(stub, params);
}
if (func.equals("delete")) {
return delete(stub, params);
}
if (func.equals("query")) {
return query(stub, params);
}
if (func.equals("throwError")) {
return newErrorResponse("throwError: an error occurred");
}
return newErrorResponse("Invalid invoke function name. Expecting one of: [\"move\", \"delete\", \"query\"] but found::" + func);
} catch (Throwable e) {
return newErrorResponse(e);
}
}

private Response move(ChaincodeStub stub, List<String> args) {
if (args.size() != 3) {
return newErrorResponse("Incorrect number of arguments. Expecting 3");
}
String accountFromKey = args.get(0);
String accountToKey = args.get(1);

String accountFromValueStr = stub.getStringState(accountFromKey);
if (accountFromValueStr == null) {
return newErrorResponse(String.format("Entity %s not found", accountFromKey));
}
int accountFromValue = Integer.parseInt(accountFromValueStr);

String accountToValueStr = stub.getStringState(accountToKey);
if (accountToValueStr == null) {
return newErrorResponse(String.format("Entity %s not found", accountToKey));
}
int accountToValue = Integer.parseInt(accountToValueStr);

int amount = Integer.parseInt(args.get(2));

if (amount > accountFromValue) {
return newErrorResponse(String.format("not enough money in account %s", accountFromKey));
}

accountFromValue -= amount;
accountToValue += amount;

_logger.info(String.format("new value of A: %s", accountFromValue));
_logger.info(String.format("new value of B: %s", accountToValue));

stub.putStringState(accountFromKey, Integer.toString(accountFromValue));
stub.putStringState(accountToKey, Integer.toString(accountToValue));

_logger.info("Transfer complete");

Map<String, byte[]> transientMap = stub.getTransient();
if (null != transientMap) {
if (transientMap.containsKey("event") && transientMap.get("event") != null) {
stub.setEvent("event", transientMap.get("event"));
}
if (transientMap.containsKey("result") && transientMap.get("result") != null) {
return newSuccessResponse(transientMap.get("result"));
}
}
return newSuccessResponse("move succeed",ByteString.copyFrom("move succeed", UTF_8).toByteArray());
}

// Deletes an entity from state
private Response delete(ChaincodeStub stub, List<String> args) {
if (args.size() != 1) {
return newErrorResponse("Incorrect number of arguments. Expecting 1");
}
String key = args.get(0);
// Delete the key from the state in ledger
stub.delState(key);
return newSuccessResponse();
}

// query callback representing the query of a chaincode
private Response query(ChaincodeStub stub, List<String> args) {
if (args.size() != 1) {
return newErrorResponse("Incorrect number of arguments. Expecting name of the person to query");
}
String key = args.get(0);
//byte[] stateBytes
String val = stub.getStringState(key);
if (val == null) {
return newErrorResponse(String.format("Error: state for %s is null", key));
}
_logger.info(String.format("Query Response:\nName: %s, Amount: %s\n", key, val));
return newSuccessResponse(val, ByteString.copyFrom(val, UTF_8).toByteArray());
}

public static void main(String[] args) {
System.out.println("OpenSSL avaliable: " + OpenSsl.isAvailable());
new SimpleChaincode().start(args);
}

}
29 changes: 29 additions & 0 deletions test/fixtures/src/java_cc/example_cc1/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
plugins {
id 'com.github.johnrengelman.shadow' version '2.0.3'
id 'java'
}

group 'org.hyperledger.fabric'
version '1.0-SNAPSHOT'

sourceCompatibility = 1.8

repositories {
mavenLocal()
mavenCentral()
}

dependencies {
compile group: 'org.hyperledger.fabric', name: 'fabric-chaincode-shim', version: '1.3.0-SNAPSHOT'
testCompile group: 'junit', name: 'junit', version: '4.12'
}

shadowJar {
baseName = 'chaincode'
version = null
classifier = null

manifest {
attributes 'Main-Class': 'org.hyperledger.fabric.example.SimpleChaincode'
}
}
1 change: 1 addition & 0 deletions test/fixtures/src/java_cc/example_cc1/settings.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
rootProject.name = 'fabric-chaincode-example-gradle'
Loading

0 comments on commit 8796095

Please sign in to comment.