-
Notifications
You must be signed in to change notification settings - Fork 3.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Java off-chain data store sample using Fabric Gateway
Also minor implementation changes to TypeScript sample for better consistency between implementations. Signed-off-by: Mark S. Lewis <mark_lewis@uk.ibm.com>
- Loading branch information
1 parent
05791d3
commit 512bd67
Showing
40 changed files
with
1,925 additions
and
57 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
# Ignore Gradle project-specific cache directory | ||
.gradle | ||
|
||
# Ignore Gradle build output directory | ||
build | ||
|
||
# IntelliJ IDEA files | ||
.idea | ||
|
||
# Files generated by the application at runtime | ||
checkpoint.json | ||
store.log |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
/* | ||
* Copyright IBM Corp. All Rights Reserved. | ||
* | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
plugins { | ||
id 'application' // Support for building a CLI application in Java. | ||
id 'checkstyle' | ||
} | ||
|
||
repositories { | ||
mavenCentral() | ||
maven { | ||
url 'https://hyperledger-fabric.jfrog.io/artifactory/fabric-maven' | ||
} | ||
} | ||
|
||
dependencies { | ||
// implementation 'com.google.guava:guava:30.1.1-jre' | ||
implementation 'io.grpc:grpc-netty-shaded:1.46.0' | ||
implementation 'org.hyperledger.fabric:fabric-gateway:1.0.2-dev-20220518-1' | ||
implementation 'com.google.code.gson:gson:2.9.0' | ||
} | ||
|
||
java { | ||
toolchain { | ||
languageVersion = JavaLanguageVersion.of(11) | ||
} | ||
} | ||
|
||
checkstyle { | ||
toolVersion '10.2' | ||
} | ||
|
||
application { | ||
mainClass = 'App' | ||
} |
78 changes: 78 additions & 0 deletions
78
off_chain_data/application-java/app/src/main/java/App.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
/* | ||
* Copyright IBM Corp. All Rights Reserved. | ||
* | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
import java.io.PrintStream; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.concurrent.TimeUnit; | ||
import java.util.stream.Collectors; | ||
|
||
import io.grpc.ManagedChannel; | ||
|
||
public final class App { | ||
private static final long SHUTDOWN_TIMEOUT_SECONDS = 3; | ||
private static final Map<String, Command> COMMANDS = Map.ofEntries( | ||
Map.entry("getAllAssets", new GetAllAssets()), | ||
Map.entry("transact", new Transact()), | ||
Map.entry("listen", new Listen()) | ||
); | ||
|
||
private final List<String> commandNames; | ||
private final PrintStream out = System.out; | ||
|
||
App(final String[] args) { | ||
commandNames = List.of(args); | ||
} | ||
|
||
public void run() throws Exception { | ||
List<Command> commands = getCommands(); | ||
ManagedChannel grpcChannel = Connections.newGrpcConnection(); | ||
try { | ||
for (Command command : commands) { | ||
command.run(grpcChannel); | ||
} | ||
} finally { | ||
grpcChannel.shutdownNow().awaitTermination(SHUTDOWN_TIMEOUT_SECONDS, TimeUnit.SECONDS); | ||
} | ||
} | ||
|
||
private List<Command> getCommands() { | ||
List<Command> commands = commandNames.stream() | ||
.map(name -> { | ||
Command command = COMMANDS.get(name); | ||
if (command == null) { | ||
printUsage(); | ||
throw new IllegalArgumentException("Unknown command: " + name); | ||
} | ||
return command; | ||
}) | ||
.collect(Collectors.toList()); | ||
|
||
if (commands.isEmpty()) { | ||
printUsage(); | ||
throw new IllegalArgumentException("Missing command"); | ||
} | ||
|
||
return commands; | ||
} | ||
|
||
private void printUsage() { | ||
out.println("Arguments: <command1> [<command2> ...]"); | ||
out.println("Available commands: " + COMMANDS.keySet()); | ||
} | ||
|
||
public static void main(final String[] args) { | ||
try { | ||
new App(args).run(); | ||
} catch (ExpectedException e) { | ||
e.printStackTrace(System.out); | ||
} catch (Exception e) { | ||
System.err.print("\nUnexpected application error: "); | ||
e.printStackTrace(); | ||
System.exit(1); | ||
} | ||
} | ||
} |
57 changes: 57 additions & 0 deletions
57
off_chain_data/application-java/app/src/main/java/Asset.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
/* | ||
* Copyright IBM Corp. All Rights Reserved. | ||
* | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
/** | ||
* Object representation of an asset. Note that the private member variable names don't follow the normal Java naming | ||
* convention as they map to the JSON format expected by the smart contract. | ||
*/ | ||
public final class Asset { | ||
private final String ID; // checkstyle:ignore-line:MemberName | ||
private String Color; // checkstyle:ignore-line:MemberName | ||
private int Size; // checkstyle:ignore-line:MemberName | ||
private String Owner; // checkstyle:ignore-line:MemberName | ||
private int AppraisedValue; // checkstyle:ignore-line:MemberName | ||
|
||
public Asset(final String id) { | ||
this.ID = id; | ||
} | ||
|
||
public String getId() { | ||
return ID; | ||
} | ||
|
||
public String getColor() { | ||
return Color; | ||
} | ||
|
||
public void setColor(final String color) { | ||
this.Color = color; | ||
} | ||
|
||
public int getSize() { | ||
return Size; | ||
} | ||
|
||
public void setSize(final int size) { | ||
this.Size = size; | ||
} | ||
|
||
public String getOwner() { | ||
return Owner; | ||
} | ||
|
||
public void setOwner(final String owner) { | ||
this.Owner = owner; | ||
} | ||
|
||
public int getAppraisedValue() { | ||
return AppraisedValue; | ||
} | ||
|
||
public void setAppraisedValue(final int appraisedValue) { | ||
this.AppraisedValue = appraisedValue; | ||
} | ||
} |
51 changes: 51 additions & 0 deletions
51
off_chain_data/application-java/app/src/main/java/AssetTransferBasic.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
/* | ||
* Copyright IBM Corp. All Rights Reserved. | ||
* | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
import java.nio.charset.StandardCharsets; | ||
import java.util.List; | ||
|
||
import com.google.gson.Gson; | ||
import org.hyperledger.fabric.client.CommitException; | ||
import org.hyperledger.fabric.client.CommitStatusException; | ||
import org.hyperledger.fabric.client.Contract; | ||
import org.hyperledger.fabric.client.EndorseException; | ||
import org.hyperledger.fabric.client.SubmitException; | ||
|
||
public final class AssetTransferBasic { | ||
private static final Gson GSON = new Gson(); | ||
private final Contract contract; | ||
|
||
public AssetTransferBasic(final Contract contract) { | ||
this.contract = contract; | ||
} | ||
|
||
public void createAsset(final Asset asset) throws EndorseException, CommitException, SubmitException, CommitStatusException { | ||
contract.submitTransaction( | ||
"CreateAsset", | ||
asset.getId(), | ||
asset.getColor(), | ||
Integer.toString(asset.getSize()), | ||
asset.getOwner(), | ||
Integer.toString(asset.getAppraisedValue()) | ||
); | ||
} | ||
|
||
public String transferAsset(final String id, final String newOwner) throws EndorseException, CommitException, SubmitException, CommitStatusException { | ||
byte[] resultBytes = contract.submitTransaction("TransferAsset", id, newOwner); | ||
return new String(resultBytes, StandardCharsets.UTF_8); | ||
} | ||
|
||
public void deleteAsset(final String id) throws EndorseException, CommitException, SubmitException, CommitStatusException { | ||
contract.submitTransaction("DeleteAsset", id); | ||
} | ||
|
||
public List<Asset> getAllAssets() throws EndorseException, CommitException, SubmitException, CommitStatusException { | ||
byte[] resultBytes = contract.submitTransaction("GetAllAssets"); | ||
String resultJson = new String(resultBytes, StandardCharsets.UTF_8); | ||
Asset[] assets = GSON.fromJson(resultJson, Asset[].class); | ||
return assets != null ? List.of(assets) : List.of(); | ||
} | ||
} |
75 changes: 75 additions & 0 deletions
75
off_chain_data/application-java/app/src/main/java/BlockProcessor.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
/* | ||
* Copyright IBM Corp. All Rights Reserved. | ||
* | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
import java.io.IOException; | ||
import java.io.UncheckedIOException; | ||
import java.util.ArrayList; | ||
import java.util.List; | ||
import java.util.Optional; | ||
import java.util.stream.Collectors; | ||
|
||
import com.google.protobuf.InvalidProtocolBufferException; | ||
import org.hyperledger.fabric.client.Checkpointer; | ||
import parser.Block; | ||
import parser.Transaction; | ||
|
||
public final class BlockProcessor { | ||
private final Block block; | ||
private final Checkpointer checkpointer; | ||
private final Store store; | ||
|
||
public BlockProcessor(final Block block, final Checkpointer checkpointer, final Store store) { | ||
this.block = block; | ||
this.checkpointer = checkpointer; | ||
this.store = store; | ||
} | ||
|
||
public void process() { | ||
long blockNumber = block.getNumber(); | ||
System.out.println("\nReceived block " + Long.toUnsignedString(blockNumber)); | ||
|
||
try { | ||
List<Transaction> validTransactions = getNewTransactions().stream() | ||
.filter(Transaction::isValid) | ||
.collect(Collectors.toList()); | ||
|
||
for (Transaction transaction : validTransactions) { | ||
new TransactionProcessor(transaction, blockNumber, store).process(); | ||
|
||
String transactionId = transaction.getChannelHeader().getTxId(); | ||
checkpointer.checkpointTransaction(blockNumber, transactionId); | ||
} | ||
|
||
checkpointer.checkpointBlock(blockNumber); | ||
} catch (IOException e) { | ||
throw new UncheckedIOException(e); | ||
} | ||
} | ||
|
||
private List<Transaction> getNewTransactions() throws InvalidProtocolBufferException { | ||
List<Transaction> transactions = block.getTransactions(); | ||
|
||
Optional<String> lastTransactionId = checkpointer.getTransactionId(); | ||
if (lastTransactionId.isEmpty()) { | ||
// No previously processed transactions within this block so all are new | ||
return transactions; | ||
} | ||
|
||
List<String> transactionIds = new ArrayList<>(); | ||
for (Transaction transaction : transactions) { | ||
transactionIds.add(transaction.getChannelHeader().getTxId()); | ||
} | ||
|
||
// Ignore transactions up to the last processed transaction ID | ||
int lastProcessedIndex = transactionIds.indexOf(lastTransactionId.get()); | ||
if (lastProcessedIndex < 0) { | ||
throw new IllegalArgumentException("Checkpoint transaction ID " + lastTransactionId + " not found in block " | ||
+ Long.toUnsignedString(block.getNumber()) + " containing transactions: " + transactionIds); | ||
} | ||
|
||
return transactions.subList(lastProcessedIndex + 1, transactions.size()); | ||
} | ||
} |
Oops, something went wrong.