Skip to content

Commit

Permalink
Java shim/chaincode project reorg, separate java docker env
Browse files Browse the repository at this point in the history
Fixes the following issues
#2146
#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 <satheesh.ceg@gmail.com>
  • Loading branch information
xspeedcruiser committed Aug 19, 2016
1 parent 8ec1147 commit 97ed71f
Show file tree
Hide file tree
Showing 31 changed files with 848 additions and 412 deletions.
24 changes: 20 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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,%,${@}})
Expand Down
84 changes: 77 additions & 7 deletions bddtests/java_shim.feature
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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]"
9 changes: 6 additions & 3 deletions core/chaincode/chaincode_support.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
22 changes: 16 additions & 6 deletions core/chaincode/platforms/java/package.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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
}
2 changes: 1 addition & 1 deletion core/chaincode/platforms/java/test/java_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
31 changes: 21 additions & 10 deletions core/chaincode/shim/java/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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'
}
24 changes: 24 additions & 0 deletions core/chaincode/shim/java/javabuild.sh
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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<String, String> rangeQueryState(String startKey, String endKey, int limit) {
// HashMap<String, String> map = new HashMap<>();
// for (RangeQueryStateKeyValue mapping : handler.handleRangeQueryState(
// startKey, endKey, limit, uuid).getKeysAndValuesList()) {
// map.put(mapping.getKey(), mapping.getValue().toStringUtf8());
// }
// return map;
// }
public Map<String, String> rangeQueryState(String startKey, String endKey) {
Map<String, String> retMap = new HashMap<>();
for (Map.Entry<String, ByteString> 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<String, ByteString> rangeQueryRawState(String startKey, String endKey) {
Map<String, ByteString> map = new HashMap<>();
for (Chaincode.RangeQueryStateKeyValue mapping : handler.handleRangeQueryState(
startKey, endKey, uuid).getKeysAndValuesList()) {
map.put(mapping.getKey(), mapping.getValue());
}
return map;
}

/**
*
Expand Down
Loading

0 comments on commit 97ed71f

Please sign in to comment.