From 97ed71fda82328e838af7c3aae0edebc6c5d7f69 Mon Sep 17 00:00:00 2001 From: Satheesh Kathamuthu Date: Thu, 4 Aug 2016 19:25:50 +0530 Subject: [PATCH] Java shim/chaincode project reorg, separate java docker env Fixes the following issues https://github.com/hyperledger/fabric/issues/2146 https://github.com/hyperledger/fabric/issues/2139 1. Separate java docker image 2. Added java package info to proto files 3. Removed duplicate .proto files checked into java shim project, and copy it as part of build process 4. Modified java unit test to refer to new path 5. Updated JAVAChaincode documentation to reflect new changes and added instructions for developing new chaincode 6. Split java chaincode examples from java shim project Change-Id: I7cbe92449c30700f17949b03f8fc7c06e0c7efb6 Signed-off-by: Satheesh Kathamuthu --- Makefile | 24 ++- bddtests/java_shim.feature | 84 +++++++- core/chaincode/chaincode_support.go | 9 +- core/chaincode/platforms/java/package.go | 22 +- .../platforms/java/test/java_test.go | 2 +- core/chaincode/shim/java/build.gradle | 31 ++- core/chaincode/shim/java/javabuild.sh | 24 +++ .../hyperledger/java/shim/ChaincodeBase.java | 10 +- .../hyperledger/java/shim/ChaincodeStub.java | 38 +++- .../org/hyperledger/java/shim/Handler.java | 197 ++++++++---------- .../hyperledger/java/shim/NextStateInfo.java | 2 +- .../shim/java/src/main/proto/chaincode.proto | 194 ----------------- .../java/src/main/proto/chaincodeevent.proto | 26 --- core/container/util/writer.go | 47 ++++- docs/Setup/JAVAChaincode.md | 44 ++-- examples/chaincode/java/Example/build.gradle | 78 +++++++ .../src/main/java/example/Example.java | 0 .../chaincode/java/LinkExample/build.gradle | 77 +++++++ .../src/main/java/example/LinkExample.java | 0 .../chaincode/java/MapExample/build.gradle | 76 +++++++ .../src/main/java/example/MapExample.java | 0 .../chaincode/java/RangeExample/build.gradle | 87 ++++++++ .../src/main/java/example/RangeExample.java | 84 ++++++++ .../chaincode/java/SimpleSample/build.gradle | 71 +++++++ .../src/main/java/example/SimpleSample.java | 0 images/javaenv/Dockerfile.in | 9 + peer/core.yaml | 8 +- protos/chaincode.proto | 2 +- protos/chaincodeevent.proto | 1 + scripts/provision/docker.sh | 7 - settings.gradle | 6 + 31 files changed, 848 insertions(+), 412 deletions(-) create mode 100755 core/chaincode/shim/java/javabuild.sh delete mode 100644 core/chaincode/shim/java/src/main/proto/chaincode.proto delete mode 100755 core/chaincode/shim/java/src/main/proto/chaincodeevent.proto create mode 100644 examples/chaincode/java/Example/build.gradle rename {core/chaincode/shim/java => examples/chaincode/java/Example}/src/main/java/example/Example.java (100%) create mode 100644 examples/chaincode/java/LinkExample/build.gradle rename {core/chaincode/shim/java => examples/chaincode/java/LinkExample}/src/main/java/example/LinkExample.java (100%) create mode 100644 examples/chaincode/java/MapExample/build.gradle rename {core/chaincode/shim/java => examples/chaincode/java/MapExample}/src/main/java/example/MapExample.java (100%) create mode 100644 examples/chaincode/java/RangeExample/build.gradle create mode 100644 examples/chaincode/java/RangeExample/src/main/java/example/RangeExample.java create mode 100644 examples/chaincode/java/SimpleSample/build.gradle rename {core/chaincode/shim/java => examples/chaincode/java/SimpleSample}/src/main/java/example/SimpleSample.java (100%) create mode 100644 images/javaenv/Dockerfile.in create mode 100644 settings.gradle diff --git a/Makefile b/Makefile index 9719a2f262b..78200fbd804 100644 --- a/Makefile +++ b/Makefile @@ -54,8 +54,10 @@ SUBDIRS:=$(strip $(SUBDIRS)) BASEIMAGE_RELEASE = $(shell cat ./images/base/release) BASEIMAGE_DEPS = $(shell git ls-files images/base scripts/provision) +JAVASHIM_DEPS = $(shell git ls-files core/chaincode/shim/java) PROJECT_FILES = $(shell git ls-files) -IMAGES = base src ccenv peer membersrvc +IMAGES = base src ccenv peer membersrvc javaenv + all: peer membersrvc checks @@ -135,9 +137,9 @@ build/docker/bin/%: build/image/src/.dummy $(PROJECT_FILES) build/bin: mkdir -p $@ -# Both peer and peer-image depend on ccenv-image -build/bin/peer: build/image/ccenv/.dummy -build/image/peer/.dummy: build/image/ccenv/.dummy +# Both peer and peer-image depend on ccenv-image and javaenv-image (all docker env images it supports) +build/bin/peer: build/image/ccenv/.dummy build/image/javaenv/.dummy +build/image/peer/.dummy: build/image/ccenv/.dummy build/image/javaenv/.dummy build/image/peer/.dummy: build/docker/bin/examples/events/block-listener/ build/bin/%: build/image/base/.dummy $(PROJECT_FILES) @@ -170,6 +172,20 @@ build/image/ccenv/.dummy: build/image/src/.dummy build/image/ccenv/bin/protoc-ge docker build -t $(PROJECT_NAME)-ccenv:latest $(@D) @touch $@ +# Special override for java-image +build/image/javaenv/.dummy: Makefile $(JAVASHIM_DEPS) + @echo "Building docker javaenv-image" + @mkdir -p $(@D) + @cat images/javaenv/Dockerfile.in > $(@D)/Dockerfile + # Following items are packed and sent to docker context while building image + # 1. Java shim layer source code + # 2. Proto files used to generate java classes + # 3. Gradle settings file + @git ls-files core/chaincode/shim/java | tar -jcT - > $(@D)/javashimsrc.tar.bz2 + @git ls-files protos settings.gradle | tar -jcT - > $(@D)/protos.tar.bz2 + docker build -t $(PROJECT_NAME)-javaenv:latest $(@D) + @touch $@ + # Default rule for image creation build/image/%/.dummy: build/image/src/.dummy build/docker/bin/% $(eval TARGET = ${patsubst build/image/%/.dummy,%,${@}}) diff --git a/bddtests/java_shim.feature b/bddtests/java_shim.feature index 929e2e15130..7a5117d0c75 100644 --- a/bddtests/java_shim.feature +++ b/bddtests/java_shim.feature @@ -6,16 +6,32 @@ # # @chaincodeImagesUpToDate use this if all scenarios chaincode images are up to date, and do NOT require building. BE SURE!!! +# +#Copyright DTCC 2016 All Rights Reserved. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. +# +# + + #@chaincodeImagesUpToDate Feature: SimpleSample Java example -#@doNotDecompose -# @wip Scenario: java SimpleSample chaincode example single peer Given we compose "docker-compose-1.yml" When requesting "/chain" from "vp0" Then I should get a JSON response with "height" = "1" - When I deploy lang chaincode "core/chaincode/shim/java" of "JAVA" with ctor "init" to "vp0" + When I deploy lang chaincode "examples/chaincode/java/SimpleSample" of "JAVA" with ctor "init" to "vp0" | arg1 | arg2 | arg3 | arg4 | | a | 100 | b | 200 | Then I should have received a chaincode name @@ -24,12 +40,12 @@ Feature: SimpleSample Java example When requesting "/chain" from "vp0" Then I should get a JSON response with "height" = "2" - When I query chaincode "example2" function name "query" on "vp0": + When I query chaincode "SimpleSample" function name "query" on "vp0": |arg1| | a | Then I should get a JSON response with "result.message" = "{'Name':'a','Amount':'100'}" - When I invoke chaincode "example2" function name "transfer" on "vp0" + When I invoke chaincode "SimpleSample" function name "transfer" on "vp0" |arg1|arg2|arg3| | a | b | 10 | Then I should have received a transactionID @@ -38,12 +54,66 @@ Feature: SimpleSample Java example When requesting "/chain" from "vp0" Then I should get a JSON response with "height" = "3" - When I query chaincode "example2" function name "query" on "vp0": + When I query chaincode "SimpleSample" function name "query" on "vp0": |arg1| | a | Then I should get a JSON response with "result.message" = "{'Name':'a','Amount':'90'}" - When I query chaincode "example2" function name "query" on "vp0": + When I query chaincode "SimpleSample" function name "query" on "vp0": |arg1| | b | Then I should get a JSON response with "result.message" = "{'Name':'b','Amount':'210'}" + +Scenario: java RangeExample chaincode single peer + Given we compose "docker-compose-1.yml" + When requesting "/chain" from "vp0" + Then I should get a JSON response with "height" = "1" + When I deploy lang chaincode "examples/chaincode/java/RangeExample" of "JAVA" with ctor "init" to "vp0" + || + || + Then I should have received a chaincode name + Then I wait up to "300" seconds for transaction to be committed to all peers + + When requesting "/chain" from "vp0" + Then I should get a JSON response with "height" = "2" + + When I invoke chaincode "RangeExample" function name "put" on "vp0" + |arg1|arg2| + | a | alice | + Then I should have received a transactionID + Then I wait up to "25" seconds for transaction to be committed to all peers + + When requesting "/chain" from "vp0" + Then I should get a JSON response with "height" = "3" + + When I invoke chaincode "RangeExample" function name "put" on "vp0" + |arg1|arg2| + | b | bob | + Then I should have received a transactionID + Then I wait up to "25" seconds for transaction to be committed to all peers + + + When I query chaincode "RangeExample" function name "get" on "vp0": + |arg1| + | a | + Then I should get a JSON response with "result.message" = "alice" + + When I query chaincode "RangeExample" function name "get" on "vp0": + |arg1| + | b | + Then I should get a JSON response with "result.message" = "bob" + + + When I query chaincode "RangeExample" function name "keys" on "vp0": + || + || + Then I should get a JSON response with "result.message" = "[a, b]" + When I invoke chaincode "RangeExample" function name "del" on "vp0" + |arg1| + | b | + Then I should have received a transactionID + Then I wait up to "25" seconds for transaction to be committed to all peers + When I query chaincode "RangeExample" function name "keys" on "vp0": + || + || + Then I should get a JSON response with "result.message" = "[a]" \ No newline at end of file diff --git a/core/chaincode/chaincode_support.go b/core/chaincode/chaincode_support.go index 0a081d71384..7d1072253c5 100644 --- a/core/chaincode/chaincode_support.go +++ b/core/chaincode/chaincode_support.go @@ -298,10 +298,13 @@ func (chaincodeSupport *ChaincodeSupport) getArgsAndEnv(cID *pb.ChaincodeID, cLa case pb.ChaincodeSpec_JAVA: //TODO add security args args = strings.Split( - fmt.Sprintf("/usr/bin/gradle run -p /root -PappArgs=[\"-a\",\"%s\",\"-i\",\"%s\"]"+ - " -x processResources -x classes", chaincodeSupport.peerAddress, cID.Name), + fmt.Sprintf("/root/Chaincode/bin/runChaincode -a %s -i %s", + chaincodeSupport.peerAddress, cID.Name), " ") - chaincodeLogger.Debugf("Executable is gradle run on chaincode ID %s", cID.Name) + if chaincodeSupport.peerTLS { + args = append(args, " -s") + } + chaincodeLogger.Debugf("Executable is %s", args[0]) default: return nil, nil, fmt.Errorf("Unknown chaincodeType: %s", cLang) } diff --git a/core/chaincode/platforms/java/package.go b/core/chaincode/platforms/java/package.go index 09e571ceb86..29b73b8f95f 100644 --- a/core/chaincode/platforms/java/package.go +++ b/core/chaincode/platforms/java/package.go @@ -6,9 +6,9 @@ import ( "strings" "time" - "github.com/spf13/viper" - + cutil "github.com/hyperledger/fabric/core/container/util" pb "github.com/hyperledger/fabric/protos" + "github.com/spf13/viper" ) //tw is expected to have the chaincode in it from GenerateHashcode. @@ -38,20 +38,30 @@ func writeChaincodePackage(spec *pb.ChaincodeSpec, tw *tar.Writer) error { } urlLocation = urlLocation[strings.LastIndex(urlLocation, "/")+1:] - var newRunLine string + var dockerFileContents string + var buf []string + if viper.GetBool("security.enabled") { //todo } else { - newRunLine = fmt.Sprintf("COPY %s /root/\n"+ - "RUN cd /root/ && gradle build", urlLocation) + buf = append(buf, viper.GetString("chaincode.java.Dockerfile")) + buf = append(buf, "COPY src /root") + buf = append(buf, "RUN gradle -b build.gradle build") + buf = append(buf, "RUN unzip -od /root build/distributions/Chaincode.zip") + } + dockerFileContents = strings.Join(buf, "\n") - dockerFileContents := fmt.Sprintf("%s\n%s", viper.GetString("chaincode.java.Dockerfile"), newRunLine) dockerFileSize := int64(len([]byte(dockerFileContents))) //Make headers identical by using zero time var zeroTime time.Time tw.WriteHeader(&tar.Header{Name: "Dockerfile", Size: dockerFileSize, ModTime: zeroTime, AccessTime: zeroTime, ChangeTime: zeroTime}) tw.Write([]byte(dockerFileContents)) + err := cutil.WriteJavaProjectToPackage(tw, spec.ChaincodeID.Path) + if err != nil { + return fmt.Errorf("Error writing Chaincode package contents: %s", err) + } + return nil } diff --git a/core/chaincode/platforms/java/test/java_test.go b/core/chaincode/platforms/java/test/java_test.go index be3cdfc5734..6eac2e01334 100644 --- a/core/chaincode/platforms/java/test/java_test.go +++ b/core/chaincode/platforms/java/test/java_test.go @@ -40,7 +40,7 @@ func TestJava_BuildImage(t *testing.T) { return } - chaincodePath := "../../../shim/java" + chaincodePath := "../../../../../examples/chaincode/java/SimpleSample" //TODO find a better way to launch example java chaincode spec := &pb.ChaincodeSpec{Type: pb.ChaincodeSpec_JAVA, ChaincodeID: &pb.ChaincodeID{Path: chaincodePath}, CtorMsg: &pb.ChaincodeInput{Args: shim.ToChaincodeArgs("f")}} if _, err := vm.BuildChaincodeContainer(spec); err != nil { diff --git a/core/chaincode/shim/java/build.gradle b/core/chaincode/shim/java/build.gradle index 4314021aaea..8f8b3fece0a 100644 --- a/core/chaincode/shim/java/build.gradle +++ b/core/chaincode/shim/java/build.gradle @@ -24,21 +24,15 @@ buildscript { classpath 'com.google.protobuf:protobuf-gradle-plugin:0.7.6' } } - + plugins { id "java" id "com.google.protobuf" version "0.7.6" id "eclipse" - id "application" } +archivesBaseName = 'chaincode' +version = '1.0' -mainClassName = "example.SimpleSample" - -run { - if (project.hasProperty("appArgs")) { - args = Eval.me(appArgs) - } -} sourceSets { main { @@ -82,8 +76,25 @@ protobuf { } } +task copyToLib(type: Copy) { + into "$buildDir/libs" + from configurations.runtime +} + + +task copyProtos(type:Copy){ + into "${projectDir}/src/main/proto" + from "${rootDir}/protos" + include '**/chaincodeevent.proto' + include '**/chaincode.proto' +} + +tasks['build'].mustRunAfter tasks['copyProtos'] +build.dependsOn(copyProtos) +build.finalizedBy(copyToLib) + dependencies { - compile 'com.google.protobuf:protobuf-java:3.0.0-beta-2' + compile 'com.google.protobuf:protobuf-java:3.0.0-beta-2' compile 'io.grpc:grpc-all:0.13.2' compile 'commons-cli:commons-cli:1.3.1' } diff --git a/core/chaincode/shim/java/javabuild.sh b/core/chaincode/shim/java/javabuild.sh new file mode 100755 index 00000000000..f8e8e42d7e9 --- /dev/null +++ b/core/chaincode/shim/java/javabuild.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +# +#Copyright DTCC 2016 All Rights Reserved. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. +# +# +set -e +PARENTDIR=$(pwd) + + +gradle -q -b ${PARENTDIR}/core/chaincode/shim/java/build.gradle clean +gradle -q -b ${PARENTDIR}/core/chaincode/shim/java/build.gradle build diff --git a/core/chaincode/shim/java/src/main/java/org/hyperledger/java/shim/ChaincodeBase.java b/core/chaincode/shim/java/src/main/java/org/hyperledger/java/shim/ChaincodeBase.java index 32e4e2f9a05..2671dfe69e7 100644 --- a/core/chaincode/shim/java/src/main/java/org/hyperledger/java/shim/ChaincodeBase.java +++ b/core/chaincode/shim/java/src/main/java/org/hyperledger/java/shim/ChaincodeBase.java @@ -35,11 +35,11 @@ import io.grpc.netty.NettyChannelBuilder; import io.grpc.stub.StreamObserver; import io.netty.handler.ssl.SslContext; -import protos.Chaincode.ChaincodeID; -import protos.Chaincode.ChaincodeMessage; -import protos.Chaincode.ChaincodeMessage.Type; -import protos.ChaincodeSupportGrpc; -import protos.ChaincodeSupportGrpc.ChaincodeSupportStub; +import org.hyperledger.protos.Chaincode.ChaincodeID; +import org.hyperledger.protos.Chaincode.ChaincodeMessage; +import org.hyperledger.protos.Chaincode.ChaincodeMessage.Type; +import org.hyperledger.protos.ChaincodeSupportGrpc; +import org.hyperledger.protos.ChaincodeSupportGrpc.ChaincodeSupportStub; public abstract class ChaincodeBase { diff --git a/core/chaincode/shim/java/src/main/java/org/hyperledger/java/shim/ChaincodeStub.java b/core/chaincode/shim/java/src/main/java/org/hyperledger/java/shim/ChaincodeStub.java index 31c0882c05a..3b9931a8b00 100644 --- a/core/chaincode/shim/java/src/main/java/org/hyperledger/java/shim/ChaincodeStub.java +++ b/core/chaincode/shim/java/src/main/java/org/hyperledger/java/shim/ChaincodeStub.java @@ -17,6 +17,10 @@ package org.hyperledger.java.shim; import com.google.protobuf.ByteString; +import org.hyperledger.protos.Chaincode; +import java.util.HashMap; +import java.util.Map; + import java.util.List; @@ -65,20 +69,34 @@ public void delState(String key) { } /** - * + * Given a start key and end key, this method returns a map of items with value converted to UTF-8 string. * @param startKey * @param endKey - * @param limit * @return */ -// public HashMap rangeQueryState(String startKey, String endKey, int limit) { -// HashMap map = new HashMap<>(); -// for (RangeQueryStateKeyValue mapping : handler.handleRangeQueryState( -// startKey, endKey, limit, uuid).getKeysAndValuesList()) { -// map.put(mapping.getKey(), mapping.getValue().toStringUtf8()); -// } -// return map; -// } + public Map rangeQueryState(String startKey, String endKey) { + Map retMap = new HashMap<>(); + for (Map.Entry item: rangeQueryRawState(startKey, endKey).entrySet()) { + retMap.put(item.getKey(), item.getValue().toStringUtf8()); + } + return retMap; + } + + /** + * This method is same as rangeQueryState, except it returns value in ByteString, useful in cases where + * serialized object can be retrieved. + * @param startKey + * @param endKey + * @return + */ + public Map rangeQueryRawState(String startKey, String endKey) { + Map map = new HashMap<>(); + for (Chaincode.RangeQueryStateKeyValue mapping : handler.handleRangeQueryState( + startKey, endKey, uuid).getKeysAndValuesList()) { + map.put(mapping.getKey(), mapping.getValue()); + } + return map; + } /** * diff --git a/core/chaincode/shim/java/src/main/java/org/hyperledger/java/shim/Handler.java b/core/chaincode/shim/java/src/main/java/org/hyperledger/java/shim/Handler.java index 95b1094314a..652d602cd4a 100644 --- a/core/chaincode/shim/java/src/main/java/org/hyperledger/java/shim/Handler.java +++ b/core/chaincode/shim/java/src/main/java/org/hyperledger/java/shim/Handler.java @@ -19,30 +19,10 @@ Licensed to the Apache Software Foundation (ASF) under one package org.hyperledger.java.shim; -import static org.hyperledger.java.fsm.CallbackType.AFTER_EVENT; -import static org.hyperledger.java.fsm.CallbackType.BEFORE_EVENT; -import static org.hyperledger.java.fsm.CallbackType.ENTER_STATE; -import static protos.Chaincode.ChaincodeMessage.Type.COMPLETED; -import static protos.Chaincode.ChaincodeMessage.Type.DEL_STATE; -import static protos.Chaincode.ChaincodeMessage.Type.ERROR; -import static protos.Chaincode.ChaincodeMessage.Type.GET_STATE; -import static protos.Chaincode.ChaincodeMessage.Type.INIT; -import static protos.Chaincode.ChaincodeMessage.Type.INVOKE_CHAINCODE; -import static protos.Chaincode.ChaincodeMessage.Type.INVOKE_QUERY; -import static protos.Chaincode.ChaincodeMessage.Type.PUT_STATE; -import static protos.Chaincode.ChaincodeMessage.Type.QUERY; -import static protos.Chaincode.ChaincodeMessage.Type.QUERY_COMPLETED; -import static protos.Chaincode.ChaincodeMessage.Type.QUERY_ERROR; -import static protos.Chaincode.ChaincodeMessage.Type.READY; -import static protos.Chaincode.ChaincodeMessage.Type.REGISTERED; -import static protos.Chaincode.ChaincodeMessage.Type.RESPONSE; -import static protos.Chaincode.ChaincodeMessage.Type.TRANSACTION; - -import java.util.HashMap; -import java.util.List; - import com.google.protobuf.ByteString; - +import io.grpc.stub.StreamObserver; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.hyperledger.java.fsm.CBDesc; import org.hyperledger.java.fsm.Event; import org.hyperledger.java.fsm.EventDesc; @@ -50,15 +30,15 @@ Licensed to the Apache Software Foundation (ASF) under one import org.hyperledger.java.fsm.exceptions.CancelledException; import org.hyperledger.java.fsm.exceptions.NoTransitionException; import org.hyperledger.java.helper.Channel; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import io.grpc.stub.StreamObserver; -import protos.Chaincode.ChaincodeID; -import protos.Chaincode.ChaincodeInput; -import protos.Chaincode.ChaincodeMessage; -import protos.Chaincode.ChaincodeMessage.Builder; -import protos.Chaincode.ChaincodeSpec; -import protos.Chaincode.PutStateInfo; +import org.hyperledger.protos.Chaincode.*; +import org.hyperledger.protos.Chaincode.ChaincodeMessage.Builder; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.hyperledger.java.fsm.CallbackType.*; +import static org.hyperledger.protos.Chaincode.ChaincodeMessage.Type.*; public class Handler { @@ -67,8 +47,8 @@ public class Handler { private StreamObserver chatStream; private ChaincodeBase chaincode; - private HashMap isTransaction; - private HashMap> responseChannel; + private Map isTransaction; + private Map> responseChannel; public Channel nextState; private FSM fsm; @@ -692,80 +672,79 @@ public void handleDeleteState(String key, String uuid) { } } -// public RangeQueryStateResponse handleRangeQueryState(String startKey, String endKey, int limit, String uuid) { -// // Create the channel on which to communicate the response from validating peer -// Channel responseChannel; -// try { -// responseChannel = createChannel(uuid); -// } catch (Exception e) { -// logger.debug(String.format("[%s]Another state request pending for this Uuid." -// + " Cannot process.", shortID(uuid))); -// throw e; -// } -// -// //Defer -// try { -// // Send RANGE_QUERY_STATE message to validator chaincode support -// RangeQueryStateInfo payload = RangeQueryStateInfo.newBuilder() -// .setStartKey(startKey) -// .setEndKey(endKey) -// .setLimit(limit) -// .build(); -// -// ChaincodeMessage message = ChaincodeMessage.newBuilder() -// .setType(RANGE_QUERY_STATE) -// .setPayload(payload.toByteString()) -// .setTxid(uuid) -// .build(); -// -// logger.debug(String.format("[%s]Sending %s", shortID(message), RANGE_QUERY_STATE)); -// try { -// serialSend(message); -// } catch (Exception e){ -// logger.error(String.format("[%s]error sending %s", shortID(message), RANGE_QUERY_STATE)); -// throw new RuntimeException("could not send message"); -// } -// -// // Wait on responseChannel for response -// ChaincodeMessage response; -// try { -// response = receiveChannel(responseChannel); -// } catch (Exception e) { -// logger.error(String.format("[%s]Received unexpected message type", uuid)); -// throw new RuntimeException("Received unexpected message type"); -// } -// -// if (response.getType() == RESPONSE) { -// // Success response -// logger.debug(String.format("[%s]Received %s. Successfully got range", -// shortID(response.getTxid()), RESPONSE)); -// -// RangeQueryStateResponse rangeQueryResponse; -// try { -// rangeQueryResponse = RangeQueryStateResponse.parseFrom(response.getPayload()); -// } catch (Exception e) { -// logger.error(String.format("[%s]unmarshall error", shortID(response.getTxid()))); -// throw new RuntimeException("Error unmarshalling RangeQueryStateResponse."); -// } -// -// return rangeQueryResponse; -// } -// -// if (response.getType() == ERROR) { -// // Error response -// logger.error(String.format("[%s]Received %s", -// shortID(response.getTxid()), ERROR)); -// throw new RuntimeException(response.getPayload().toStringUtf8()); -// } -// -// // Incorrect chaincode message received -// logger.error(String.format("Incorrect chaincode message %s recieved. Expecting %s or %s", -// response.getType(), RESPONSE, ERROR)); -// throw new RuntimeException("Incorrect chaincode message received"); -// } finally { -// deleteChannel(uuid); -// } -// } + public RangeQueryStateResponse handleRangeQueryState(String startKey, String endKey, String uuid) { + // Create the channel on which to communicate the response from validating peer + Channel responseChannel; + try { + responseChannel = createChannel(uuid); + } catch (Exception e) { + logger.debug(String.format("[%s]Another state request pending for this Uuid." + + " Cannot process.", shortID(uuid))); + throw e; + } + + //Defer + try { + // Send RANGE_QUERY_STATE message to validator chaincode support + RangeQueryState payload = RangeQueryState.newBuilder() + .setStartKey(startKey) + .setEndKey(endKey) + .build(); + + ChaincodeMessage message = ChaincodeMessage.newBuilder() + .setType(RANGE_QUERY_STATE) + .setPayload(payload.toByteString()) + .setTxid(uuid) + .build(); + + logger.debug(String.format("[%s]Sending %s", shortID(message), RANGE_QUERY_STATE)); + try { + serialSend(message); + } catch (Exception e){ + logger.error(String.format("[%s]error sending %s", shortID(message), RANGE_QUERY_STATE)); + throw new RuntimeException("could not send message"); + } + + // Wait on responseChannel for response + ChaincodeMessage response; + try { + response = receiveChannel(responseChannel); + } catch (Exception e) { + logger.error(String.format("[%s]Received unexpected message type", uuid)); + throw new RuntimeException("Received unexpected message type"); + } + + if (response.getType() == RESPONSE) { + // Success response + logger.debug(String.format("[%s]Received %s. Successfully got range", + shortID(response.getTxid()), RESPONSE)); + + RangeQueryStateResponse rangeQueryResponse; + try { + rangeQueryResponse = RangeQueryStateResponse.parseFrom(response.getPayload()); + } catch (Exception e) { + logger.error(String.format("[%s]unmarshall error", shortID(response.getTxid()))); + throw new RuntimeException("Error unmarshalling RangeQueryStateResponse."); + } + + return rangeQueryResponse; + } + + if (response.getType() == ERROR) { + // Error response + logger.error(String.format("[%s]Received %s", + shortID(response.getTxid()), ERROR)); + throw new RuntimeException(response.getPayload().toStringUtf8()); + } + + // Incorrect chaincode message received + logger.error(String.format("Incorrect chaincode message %s recieved. Expecting %s or %s", + response.getType(), RESPONSE, ERROR)); + throw new RuntimeException("Incorrect chaincode message received"); + } finally { + deleteChannel(uuid); + } + } public ByteString handleInvokeChaincode(String chaincodeName, String function, List args, String uuid) { // Check if this is a transaction @@ -916,7 +895,6 @@ public ByteString handleQueryChaincode(String chaincodeName, String function, Li // handleMessage message handles loop for org.hyperledger.java.shim side of chaincode/validator stream. public synchronized void handleMessage(ChaincodeMessage message) throws Exception { - if (message.getType() == ChaincodeMessage.Type.KEEPALIVE){ logger.debug(String.format("[%s] Recieved KEEPALIVE message, do nothing", shortID(message))); @@ -924,6 +902,7 @@ public synchronized void handleMessage(ChaincodeMessage message) throws Exceptio // and it does not touch the state machine return; } + logger.debug(String.format("[%s]Handling ChaincodeMessage of type: %s(state:%s)", shortID(message), message.getType(), fsm.current())); diff --git a/core/chaincode/shim/java/src/main/java/org/hyperledger/java/shim/NextStateInfo.java b/core/chaincode/shim/java/src/main/java/org/hyperledger/java/shim/NextStateInfo.java index 0c6cbd844dc..3745a79e57c 100644 --- a/core/chaincode/shim/java/src/main/java/org/hyperledger/java/shim/NextStateInfo.java +++ b/core/chaincode/shim/java/src/main/java/org/hyperledger/java/shim/NextStateInfo.java @@ -16,7 +16,7 @@ package org.hyperledger.java.shim; -import protos.Chaincode.ChaincodeMessage; +import org.hyperledger.protos.Chaincode.ChaincodeMessage; public class NextStateInfo { diff --git a/core/chaincode/shim/java/src/main/proto/chaincode.proto b/core/chaincode/shim/java/src/main/proto/chaincode.proto deleted file mode 100644 index 495817d56bd..00000000000 --- a/core/chaincode/shim/java/src/main/proto/chaincode.proto +++ /dev/null @@ -1,194 +0,0 @@ -/* -Copyright IBM Corp. 2016 All Rights Reserved. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -syntax = "proto3"; - -package protos; - -import "chaincodeevent.proto"; -import "google/protobuf/timestamp.proto"; - - -// Confidentiality Levels -enum ConfidentialityLevel { - PUBLIC = 0; - CONFIDENTIAL = 1; -} - - -//ChaincodeID contains the path as specified by the deploy transaction -//that created it as well as the hashCode that is generated by the -//system for the path. From the user level (ie, CLI, REST API and so on) -//deploy transaction is expected to provide the path and other requests -//are expected to provide the hashCode. The other value will be ignored. -//Internally, the structure could contain both values. For instance, the -//hashCode will be set when first generated using the path -message ChaincodeID { - //deploy transaction will use the path - string path = 1; - - //all other requests will use the name (really a hashcode) generated by - //the deploy transaction - string name = 2; -} - -// Carries the chaincode function and its arguments. -message ChaincodeInput { - repeated bytes args = 1; -} - -// Carries the chaincode specification. This is the actual metadata required for -// defining a chaincode. -message ChaincodeSpec { - - enum Type { - UNDEFINED = 0; - GOLANG = 1; - NODE = 2; - CAR = 3; - JAVA = 4; - } - - Type type = 1; - ChaincodeID chaincodeID = 2; - ChaincodeInput ctorMsg = 3; - int32 timeout = 4; - string secureContext = 5; - ConfidentialityLevel confidentialityLevel = 6; - bytes metadata = 7; - repeated string attributes = 8; -} - -// Specify the deployment of a chaincode. -// TODO: Define `codePackage`. -message ChaincodeDeploymentSpec { - - enum ExecutionEnvironment { - DOCKER = 0; - SYSTEM = 1; - } - - ChaincodeSpec chaincodeSpec = 1; - // Controls when the chaincode becomes executable. - google.protobuf.Timestamp effectiveDate = 2; - bytes codePackage = 3; - ExecutionEnvironment execEnv= 4; - -} - -// Carries the chaincode function and its arguments. -message ChaincodeInvocationSpec { - - ChaincodeSpec chaincodeSpec = 1; - // This field can contain a user-specified ID generation algorithm - // If supplied, this will be used to generate a ID - // If not supplied (left empty), sha256base64 will be used - // The algorithm consists of two parts: - // 1, a hash function - // 2, a decoding used to decode user (string) input to bytes - // Currently, SHA256 with BASE64 is supported (e.g. idGenerationAlg='sha256base64') - string idGenerationAlg = 2; -} - -// This structure contain transaction data that we send to the chaincode -// container shim and allow the chaincode to access through the shim interface. -// TODO: Consider remove this message and just pass the transaction object -// to the shim and/or allow the chaincode to query transactions. -message ChaincodeSecurityContext { - bytes callerCert = 1; - bytes callerSign = 2; - bytes payload = 3; - bytes binding = 4; - bytes metadata = 5; - bytes parentMetadata = 6; - google.protobuf.Timestamp txTimestamp = 7; // transaction timestamp -} - -message ChaincodeMessage { - - enum Type { - UNDEFINED = 0; - REGISTER = 1; - REGISTERED = 2; - INIT = 3; - READY = 4; - TRANSACTION = 5; - COMPLETED = 6; - ERROR = 7; - GET_STATE = 8; - PUT_STATE = 9; - DEL_STATE = 10; - INVOKE_CHAINCODE = 11; - INVOKE_QUERY = 12; - RESPONSE = 13; - QUERY = 14; - QUERY_COMPLETED = 15; - QUERY_ERROR = 16; - RANGE_QUERY_STATE = 17; - RANGE_QUERY_STATE_NEXT = 18; - RANGE_QUERY_STATE_CLOSE = 19; - KEEPALIVE = 20; - } - - Type type = 1; - google.protobuf.Timestamp timestamp = 2; - bytes payload = 3; - string txid = 4; - ChaincodeSecurityContext securityContext = 5; - - //event emmited by chaincode. Used only with Init or Invoke. - // This event is then stored (currently) - //with Block.NonHashData.TransactionResult - ChaincodeEvent chaincodeEvent = 6; -} - -message PutStateInfo { - string key = 1; - bytes value = 2; -} - -message RangeQueryState { - string startKey = 1; - string endKey = 2; -} - -message RangeQueryStateNext { - string ID = 1; -} - -message RangeQueryStateClose { - string ID = 1; -} - -message RangeQueryStateKeyValue { - string key = 1; - bytes value = 2; -} - -message RangeQueryStateResponse { - repeated RangeQueryStateKeyValue keysAndValues = 1; - bool hasMore = 2; - string ID = 3; -} - -// Interface that provides support to chaincode execution. ChaincodeContext -// provides the context necessary for the server to respond appropriately. -service ChaincodeSupport { - - rpc Register(stream ChaincodeMessage) returns (stream ChaincodeMessage) {} - - -} diff --git a/core/chaincode/shim/java/src/main/proto/chaincodeevent.proto b/core/chaincode/shim/java/src/main/proto/chaincodeevent.proto deleted file mode 100755 index 0c2b3bfce1d..00000000000 --- a/core/chaincode/shim/java/src/main/proto/chaincodeevent.proto +++ /dev/null @@ -1,26 +0,0 @@ -/* -Copyright IBM Corp. 2016 All Rights Reserved. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -syntax = "proto3"; -package protos; - -//Chaincode is used for events and registrations that are specific to chaincode -//string type - "chaincode" -message ChaincodeEvent { - string chaincodeID = 1; - string txID = 2; - string eventName = 3; - bytes payload = 4; -} diff --git a/core/container/util/writer.go b/core/container/util/writer.go index e9a644c4ff6..d5dc29f3a31 100644 --- a/core/container/util/writer.go +++ b/core/container/util/writer.go @@ -39,14 +39,14 @@ var fileTypes = map[string]bool{ ".yaml": true, ".json": true, } +var javaFileTypes = map[string]bool{ + ".java": true, + ".properties": true, + ".gradle": true, +} -//WriteGopathSrc tars up files under gopath src -func WriteGopathSrc(tw *tar.Writer, excludeDir string) error { - gopath := os.Getenv("GOPATH") - // Only take the first element of GOPATH - gopath = filepath.SplitList(gopath)[0] - - rootDirectory := filepath.Join(gopath, "src") +func WriteFolderToTarPackage(tw *tar.Writer, srcPath string, excludeDir string, includeFileTypeMap map[string]bool) error { + rootDirectory := srcPath vmLogger.Infof("rootDirectory = %s", rootDirectory) //append "/" if necessary @@ -78,7 +78,7 @@ func WriteGopathSrc(tw *tar.Writer, excludeDir string) error { // we only want 'fileTypes' source files at this point ext := filepath.Ext(path) - if _, ok := fileTypes[ext]; ok != true { + if _, ok := includeFileTypeMap[ext]; ok != true { return nil } @@ -97,6 +97,22 @@ func WriteGopathSrc(tw *tar.Writer, excludeDir string) error { vmLogger.Infof("Error walking rootDirectory: %s", err) return err } + return nil +} + +//WriteGopathSrc tars up files under gopath src +func WriteGopathSrc(tw *tar.Writer, excludeDir string) error { + gopath := os.Getenv("GOPATH") + // Only take the first element of GOPATH + gopath = filepath.SplitList(gopath)[0] + + rootDirectory := filepath.Join(gopath, "src") + vmLogger.Infof("rootDirectory = %s", rootDirectory) + + if err := WriteFolderToTarPackage(tw, rootDirectory, excludeDir, fileTypes); err != nil { + vmLogger.Errorf("Error writing folder to tar package %s", err) + return err + } // Add the certificates to tar if viper.GetBool("peer.tls.enabled") { @@ -114,6 +130,21 @@ func WriteGopathSrc(tw *tar.Writer, excludeDir string) error { return nil } +func WriteJavaProjectToPackage(tw *tar.Writer, srcPath string) error { + + if err := WriteFolderToTarPackage(tw, srcPath, "", javaFileTypes); err != nil { + + vmLogger.Errorf("Error writing folder to tar package %s", err) + return err + } + // Write the tar file out + if err := tw.Close(); err != nil { + return err + } + return nil + +} + //WriteFileToPackage writes a file to the tarball func WriteFileToPackage(localpath string, packagepath string, tw *tar.Writer) error { fd, err := os.Open(localpath) diff --git a/docs/Setup/JAVAChaincode.md b/docs/Setup/JAVAChaincode.md index 87dd6e88ee4..b62857b6140 100644 --- a/docs/Setup/JAVAChaincode.md +++ b/docs/Setup/JAVAChaincode.md @@ -30,7 +30,7 @@ Note: This guide generally assumes you have followed the Chaincode development e * Deploy the chaincode, ``` - peer chaincode deploy -l java -n map -p /opt/gopath/src/github.com/hyperledger/fabric/core/chaincode/shim/java -c '{"Function": "init", "Args": ["a","100", "b", "200"]}' + peer chaincode deploy -l java -p /opt/gopath/src/github.com/hyperledger/fabric/examples/chaincode/java/SimpleSample -c '{"Args": ["init", "a","100", "b", "200"]}' ``` `6d9a704d95284593fe802a5de89f84e86fb975f00830bc6488713f9441b835cf32d9cd07b087b90e5cb57a88360f90a4de39521a5595545ad689cd64791679e9` @@ -43,29 +43,27 @@ Note: This guide generally assumes you have followed the Chaincode development e * Invoke a transfer transaction, ``` - /opt/gopath/src/github.com/hyperledger/fabric/core/chaincode/shim/java$ peer chaincode invoke -l java \ + peer chaincode invoke -l java \ -n 6d9a704d95284593fe802a5de89f84e86fb975f00830bc6488713f9441b835cf32d9cd07b087b90e5cb57a88360f90a4de39521a5595545ad689cd64791679e9 \ - -c '{"Function": "transfer", "Args": ["a","b", "20"]}' + -c '{"Args": ["transfer", "a", "b", "10"]}' ``` `c7dde1d7-fae5-4b68-9ab1-928d61d1e346` * Query the values of a and b after the transfer ``` - /opt/gopath/src/github.com/hyperledger/fabric/core/chaincode/shim/java$ peer chaincode query -l java \ + peer chaincode query -l java \ -n 6d9a704d95284593fe802a5de89f84e86fb975f00830bc6488713f9441b835cf32d9cd07b087b90e5cb57a88360f90a4de39521a5595545ad689cd64791679e9 \ - -c '{"Function": "query", "Args": ["a"]}' + -c '{ "Args": ["query", "a"]}' {"Name":"a","Amount":"80"} - /opt/gopath/src/github.com/hyperledger/fabric/core/chaincode/shim/java$ peer chaincode query -l java \ + peer chaincode query -l java \ -n 6d9a704d95284593fe802a5de89f84e86fb975f00830bc6488713f9441b835cf32d9cd07b087b90e5cb57a88360f90a4de39521a5595545ad689cd64791679e9 \ - -c '{"Function": "query", "Args": ["b"]}' + -c '{ "Args": ["query", "b"]}' {"Name":"b","Amount":"220"} ``` -* To develop your own chaincodes, simply extend the Chaincode class (demonstrated in the SimpleSample Example under the examples package) - ### Java chaincode deployment in DEV Mode @@ -78,37 +76,49 @@ Note: This guide generally assumes you have followed the Chaincode development e ``` 3. Open the second Vagrant terminal, and change to Java shim root folder and run gradle build, ``` - cd $GOPATH/src/github.com/hyperledger/fabric/core/chaincode/shim/java - gradle build + cd $GOPATH/src/github.com/hyperledger/fabric/examples/chaincode/java/SimpleSample + gradle -b build.gradle build ``` -4. Run the SimpleSample chaincode using the `gradle run` +4. Run the SimpleSample chaincode using the `gradle -b build.gradle run` 5. Open the third Vagrant terminal to run init and invoke on the chaincode - - peer chaincode deploy -l java -n SimpleSample -c '{"Function": "init", "Args": ["a","100", "b", "200"]}' + peer chaincode deploy -l java -n SimpleSample -c '{"Args": ["init", "a","100", "b", "200"]}' ``` 2016/06/28 19:10:15 Load docker HostConfig: %+v &{[] [] [] [] false map[] [] false [] [] [] [] host { 0} [] { map[]} false [] 0 0 0 false 0 0 0 0 []} 19:10:15.461 [crypto] main -> INFO 002 Log level recognized 'info', set to INFO SimpleSample ``` - peer chaincode invoke -l java -n SimpleSample -c '{"Function": "transfer", "Args": ["a", "b", "10"]}' + peer chaincode invoke -l java -n SimpleSample -c '{"Args": ["transfer", "a", "b", "10"]}' ``` 2016/06/28 19:11:13 Load docker HostConfig: %+v &{[] [] [] [] false map[] [] false [] [] [] [] host { 0} [] { map[]} false [] 0 0 0 false 0 0 0 0 []} 19:11:13.553 [crypto] main -> INFO 002 Log level recognized 'info', set to INFO 978ff89e-e4ef-43da-a9f8-625f2f6f04e5 ``` - peer chaincode query -l java -n SimpleSample -c '{"Function": "query", "Args": ["a"]}' + peer chaincode query -l java -n SimpleSample -c '{ "Args": ["query", "a"]}' ``` 2016/06/28 19:12:19 Load docker HostConfig: %+v &{[] [] [] [] false map[] [] false [] [] [] [] host { 0} [] { map[]} false [] 0 0 0 false 0 0 0 0 []} 19:12:19.289 [crypto] main -> INFO 002 Log level recognized 'info', set to INFO {"Name":"a","Amount":"90"} ``` - peer chaincode query -l java -n SimpleSample -c '{"Function": "query", "Args": ["b"]}' + peer chaincode query -l java -n SimpleSample -c '{"Args": ["query", "b"]}' ``` 2016/06/28 19:12:25 Load docker HostConfig: %+v &{[] [] [] [] false map[] [] false [] [] [] [] host { 0} [] { map[]} false [] 0 0 0 false 0 0 0 0 []} 19:12:25.667 [crypto] main -> INFO 002 Log level recognized 'info', set to INFO {"Name":"b","Amount":"210"} ``` + +### Developing new JAVA chaincode +1. Create a new Java project structure. +2. Use existing `build.grade` from any example JAVA Chaincode project like `examples/chaincode/java/SimpleSample`. +3. Make your main class extend ChaincodeBase class and implement the following methods from base class. + 1. `public String run(ChaincodeStub stub, String function, String[] args) ` + 2. `public String query(ChaincodeStub stub, String function, String[] args)` + 3. `public String getChaincodeID()` +4. Modify the `mainClassName` in `build.gradle` to point to your new class. +5. Build this project using `gradle -b build.gradle build` +6. Run this chaincode after starting a peer in dev-mode as above using `gradle -b build.gradle run` + + diff --git a/examples/chaincode/java/Example/build.gradle b/examples/chaincode/java/Example/build.gradle new file mode 100644 index 00000000000..11e24c41451 --- /dev/null +++ b/examples/chaincode/java/Example/build.gradle @@ -0,0 +1,78 @@ +/* +Copyright DTCC 2016 All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + + +buildscript { + repositories { + mavenLocal() + mavenCentral() + jcenter() + } +} + + +plugins { + id "java" + id "eclipse" + id "application" +} + + +task printClasspath { + doLast { + configurations.testRuntime.each { println it } + } +} + +archivesBaseName = "java-example" +mainClassName="example.Example" + +run { + if (project.hasProperty("appArgs")) { + args = Eval.me(appArgs) + } +} + +sourceSets { + main { + java { + srcDir 'src/main/java' + } + } +} + +repositories { + mavenLocal() + mavenCentral() +} + + +distZip { + archiveName='Chaincode.zip' + duplicatesStrategy='exclude' +} + + +startScripts{ + applicationName='runChaincode' +} + + +dependencies { + compile 'io.grpc:grpc-all:0.13.2' + compile 'commons-cli:commons-cli:1.3.1' + compile project(':core:chaincode:shim:java') + } diff --git a/core/chaincode/shim/java/src/main/java/example/Example.java b/examples/chaincode/java/Example/src/main/java/example/Example.java similarity index 100% rename from core/chaincode/shim/java/src/main/java/example/Example.java rename to examples/chaincode/java/Example/src/main/java/example/Example.java diff --git a/examples/chaincode/java/LinkExample/build.gradle b/examples/chaincode/java/LinkExample/build.gradle new file mode 100644 index 00000000000..e6a7da1108d --- /dev/null +++ b/examples/chaincode/java/LinkExample/build.gradle @@ -0,0 +1,77 @@ +/* +Copyright DTCC 2016 All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + + +buildscript { + repositories { + mavenLocal() + mavenCentral() + jcenter() + } +} + + +plugins { + id "java" + id "eclipse" + id "application" +} + + +task printClasspath { + doLast { + configurations.testRuntime.each { println it } + } +} + +archivesBaseName = "java-linkexample" +mainClassName="example.LinkExample" + +run { + if (project.hasProperty("appArgs")) { + args = Eval.me(appArgs) + } +} + +sourceSets { + main { + java { + srcDir 'src/main/java' + } + } +} + +repositories { + mavenLocal() + mavenCentral() +} + + +distZip { + archiveName='Chaincode.zip' + duplicatesStrategy='exclude' +} + +startScripts{ + applicationName='runChaincode' +} + + +dependencies { + compile 'io.grpc:grpc-all:0.13.2' + compile 'commons-cli:commons-cli:1.3.1' + compile project(':core:chaincode:shim:java') + } diff --git a/core/chaincode/shim/java/src/main/java/example/LinkExample.java b/examples/chaincode/java/LinkExample/src/main/java/example/LinkExample.java similarity index 100% rename from core/chaincode/shim/java/src/main/java/example/LinkExample.java rename to examples/chaincode/java/LinkExample/src/main/java/example/LinkExample.java diff --git a/examples/chaincode/java/MapExample/build.gradle b/examples/chaincode/java/MapExample/build.gradle new file mode 100644 index 00000000000..cdd264fb146 --- /dev/null +++ b/examples/chaincode/java/MapExample/build.gradle @@ -0,0 +1,76 @@ +/* +Copyright DTCC 2016 All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + + +buildscript { + repositories { + mavenLocal() + mavenCentral() + jcenter() + } +} + + +plugins { + id "java" + id "eclipse" + id "application" +} + + +task printClasspath { + doLast { + configurations.testRuntime.each { println it } + } +} + +archivesBaseName = "java-mapexample" +mainClassName="example.MapExample" + +run { + if (project.hasProperty("appArgs")) { + args = Eval.me(appArgs) + } +} + +sourceSets { + main { + java { + srcDir 'src/main/java' + } + } +} + +repositories { + mavenLocal() + mavenCentral() +} + +distZip { + archiveName='Chaincode.zip' + duplicatesStrategy='exclude' +} + +startScripts{ + applicationName='runChaincode' +} + + +dependencies { + compile 'io.grpc:grpc-all:0.13.2' + compile 'commons-cli:commons-cli:1.3.1' + compile project(':core:chaincode:shim:java') + } diff --git a/core/chaincode/shim/java/src/main/java/example/MapExample.java b/examples/chaincode/java/MapExample/src/main/java/example/MapExample.java similarity index 100% rename from core/chaincode/shim/java/src/main/java/example/MapExample.java rename to examples/chaincode/java/MapExample/src/main/java/example/MapExample.java diff --git a/examples/chaincode/java/RangeExample/build.gradle b/examples/chaincode/java/RangeExample/build.gradle new file mode 100644 index 00000000000..d36b0ed21bd --- /dev/null +++ b/examples/chaincode/java/RangeExample/build.gradle @@ -0,0 +1,87 @@ +/* +Copyright DTCC 2016 All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + + +buildscript { + repositories { + mavenLocal() + mavenCentral() + jcenter() + } +} + + +plugins { + id "java" + id "eclipse" + id "application" +} + + +// task printClasspath { +// doLast { +// configurations.testRuntime.each { println it } +// } +// } + +archivesBaseName = "RangeExample" +mainClassName="example.RangeExample" + +run { + if (project.hasProperty("appArgs")) { + args = Eval.me(appArgs) + } +} + +sourceSets { + main { + java { + srcDir 'src/main/java' + } + } +} + +repositories { + mavenLocal() + mavenCentral() +} + + +distZip { +// destinationDir = file("${projectDir}") + archiveName='Chaincode.zip' + duplicatesStrategy='exclude' +} + +startScripts{ + applicationName='runChaincode' +} + +// jar.doFirst { +// destinationDir = file("${projectDir}") +// manifest { +// attributes ('Main-Class': mainClassName, +// 'Class-Path': configurations.runtime.each { "/root/lib"+"/$it.name" }.join(' ') +// ) +// } +// } + + +dependencies { + compile 'io.grpc:grpc-all:0.13.2' + compile 'commons-cli:commons-cli:1.3.1' + compile project(':core:chaincode:shim:java') + } diff --git a/examples/chaincode/java/RangeExample/src/main/java/example/RangeExample.java b/examples/chaincode/java/RangeExample/src/main/java/example/RangeExample.java new file mode 100644 index 00000000000..4d949db5283 --- /dev/null +++ b/examples/chaincode/java/RangeExample/src/main/java/example/RangeExample.java @@ -0,0 +1,84 @@ +/* +Copyright DTCC 2016 All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package example; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.hyperledger.java.shim.ChaincodeBase; +import org.hyperledger.java.shim.ChaincodeStub; + +import java.util.Map; + +/** + * Created by cadmin on 6/30/16. + */ +public class RangeExample extends ChaincodeBase { + private static Log log = LogFactory.getLog(RangeExample.class); + @java.lang.Override + public String run(ChaincodeStub stub, String function, String[] args) { + log.info("In run, function:"+function); + switch (function) { + case "put": + for (int i = 0; i < args.length; i += 2) + stub.putState(args[i], args[i + 1]); + break; + case "del": + for (String arg : args) + stub.delState(arg); + break; + default: + log.error("No matching case for function:"+function); + + } + return null; + } + + + @java.lang.Override + public String query(ChaincodeStub stub, String function, String[] args) { + log.info("query"); + switch (function){ + case "get": { + return stub.getState(args[0]); + } + case "keys":{ + Map keysIter = null; + if (args.length >= 2) { + keysIter = stub.rangeQueryState(args[0], args[1]); + }else{ + keysIter = stub.rangeQueryState("",""); + } + + return keysIter.keySet().toString(); + } + default: + log.error("No matching case for function:"+function); + return ""; + } + + } + + @java.lang.Override + public String getChaincodeID() { + return "RangeExample"; + } + + public static void main(String[] args) throws Exception { + log.info("starting"); + new RangeExample().start(args); + } + +} diff --git a/examples/chaincode/java/SimpleSample/build.gradle b/examples/chaincode/java/SimpleSample/build.gradle new file mode 100644 index 00000000000..85b14480217 --- /dev/null +++ b/examples/chaincode/java/SimpleSample/build.gradle @@ -0,0 +1,71 @@ +/* +Copyright DTCC 2016 All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + + +buildscript { + repositories { + mavenLocal() + mavenCentral() + jcenter() + } +} + + +plugins { + id "java" + id "eclipse" + id "application" +} + + +archivesBaseName = "java-simplesample" +mainClassName="example.SimpleSample" + +run { + if (project.hasProperty("appArgs")) { + args = Eval.me(appArgs) + } +} + +sourceSets { + main { + java { + srcDir 'src/main/java' + } + } +} + +repositories { + mavenLocal() + mavenCentral() +} + + +distZip { + archiveName='Chaincode.zip' + duplicatesStrategy='exclude' +} + +startScripts{ + applicationName='runChaincode' +} + + +dependencies { + compile 'io.grpc:grpc-all:0.13.2' + compile 'commons-cli:commons-cli:1.3.1' + compile project(':core:chaincode:shim:java') + } diff --git a/core/chaincode/shim/java/src/main/java/example/SimpleSample.java b/examples/chaincode/java/SimpleSample/src/main/java/example/SimpleSample.java similarity index 100% rename from core/chaincode/shim/java/src/main/java/example/SimpleSample.java rename to examples/chaincode/java/SimpleSample/src/main/java/example/SimpleSample.java diff --git a/images/javaenv/Dockerfile.in b/images/javaenv/Dockerfile.in new file mode 100644 index 00000000000..48ad2844568 --- /dev/null +++ b/images/javaenv/Dockerfile.in @@ -0,0 +1,9 @@ +FROM java:openjdk-8 +RUN wget https://services.gradle.org/distributions/gradle-2.12-bin.zip -P /tmp --quiet +RUN unzip -q /tmp/gradle-2.12-bin.zip -d /opt && rm /tmp/gradle-2.12-bin.zip +RUN ln -s /opt/gradle-2.12/bin/gradle /usr/bin +ADD javashimsrc.tar.bz2 /root +ADD protos.tar.bz2 /root +WORKDIR /root +# Build java shim after copying proto files from fabric/proto +RUN core/chaincode/shim/java/javabuild.sh diff --git a/peer/core.yaml b/peer/core.yaml index 9dcac2814e1..53760262078 100644 --- a/peer/core.yaml +++ b/peer/core.yaml @@ -294,10 +294,12 @@ chaincode: Dockerfile: | FROM hyperledger/fabric-ccenv java: - # This is the same bases image used for golang implementation - # TODO Consider moving java installation from common provision shell script to here + # This is an image based on java:openjdk-8 with addition compiler + # tools added for java shim layer packaging. + # This image is packed with shim layer libraries that are necessary + # for Java chaincode runtime. Dockerfile: | - from hyperledger/fabric-baseimage + from hyperledger/fabric-javaenv # timeout in millisecs for starting up a container and waiting for Register # to come through. 1sec should be plenty for chaincode unit tests diff --git a/protos/chaincode.proto b/protos/chaincode.proto index 495817d56bd..cc62c23a1dc 100644 --- a/protos/chaincode.proto +++ b/protos/chaincode.proto @@ -17,7 +17,7 @@ limitations under the License. syntax = "proto3"; package protos; - +option java_package = "org.hyperledger.protos"; import "chaincodeevent.proto"; import "google/protobuf/timestamp.proto"; diff --git a/protos/chaincodeevent.proto b/protos/chaincodeevent.proto index 032eb269939..202e5619ecb 100755 --- a/protos/chaincodeevent.proto +++ b/protos/chaincodeevent.proto @@ -15,6 +15,7 @@ limitations under the License. */ syntax = "proto3"; package protos; +option java_package = "org.hyperledger.protos"; //ChaincodeEvent is used for events and registrations that are specific to chaincode //string type - "chaincode" diff --git a/scripts/provision/docker.sh b/scripts/provision/docker.sh index 4f74cba280c..8ed21a7ca14 100755 --- a/scripts/provision/docker.sh +++ b/scripts/provision/docker.sh @@ -104,13 +104,6 @@ done` COPY scripts $REMOTESCRIPTS RUN $REMOTESCRIPTS/common.sh RUN chmod a+rw -R /opt/gopath -RUN add-apt-repository ppa:openjdk-r/ppa -y -RUN apt-get update && apt-get install openjdk-8-jdk -y -RUN wget https://services.gradle.org/distributions/gradle-2.12-bin.zip -P /tmp --quiet -RUN unzip -q /tmp/gradle-2.12-bin.zip -d /opt && rm /tmp/gradle-2.12-bin.zip -RUN ln -s /opt/gradle-2.12/bin/gradle /usr/bin -# TODO JAVA_HOME set here, consider using update-java-alternatives -ENV JAVA_HOME /usr/lib/jvm/java-1.8.0-openjdk-amd64 EOF diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 00000000000..3743501471f --- /dev/null +++ b/settings.gradle @@ -0,0 +1,6 @@ +include ":core:chaincode:shim:java" +include ":examples:chaincode:java:Example" +include ":examples:chaincode:java:MapExample" +include ":examples:chaincode:java:LinkExample" +include ":examples:chaincode:java:SimpleSample" +include ":examples:chaincode:java:RangeExample"