Skip to content
This repository was archived by the owner on Apr 22, 2025. It is now read-only.

Commit ebb86c8

Browse files
FGJ-86: Allow transaction ID to be accessed on Transaction object (#50)
Also allow a commit handler to be specified on a per-transaction basis to support the scenario where application want to submit some transactions as fire-and-forget, and then use the transaction ID to asynchronously listen for the transaction to be committed. Signed-off-by: Mark S. Lewis <[email protected]>
1 parent da703d9 commit ebb86c8

File tree

5 files changed

+148
-26
lines changed

5 files changed

+148
-26
lines changed

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@
8686
<dependency>
8787
<groupId>org.hyperledger.fabric-sdk-java</groupId>
8888
<artifactId>fabric-sdk-java</artifactId>
89-
<version>2.1.1</version>
89+
<version>2.1.2</version>
9090
</dependency>
9191
<dependency>
9292
<groupId>org.mockito</groupId>

src/main/java/org/hyperledger/fabric/gateway/Transaction.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import java.util.concurrent.TimeUnit;
1212
import java.util.concurrent.TimeoutException;
1313

14+
import org.hyperledger.fabric.gateway.spi.CommitHandlerFactory;
1415
import org.hyperledger.fabric.sdk.Peer;
1516

1617
/**
@@ -31,6 +32,17 @@ public interface Transaction {
3132
*/
3233
String getName();
3334

35+
/**
36+
* Get the transaction ID that will be used when submitting this transaction. This can be useful for:
37+
* <ul>
38+
* <li>Asynchronously listening for commit events for this transaction when using the
39+
* {@link DefaultCommitHandlers#NONE} commit handler.</li>
40+
* <li>Correlating client application operations with activity in Fabric peers and orderers.</li>
41+
* </ul>
42+
* @return A transaction ID.
43+
*/
44+
String getTransactionId();
45+
3446
/**
3547
* Set transient data that will be passed to the transaction function
3648
* but will not be stored on the ledger. This can be used to pass
@@ -49,6 +61,14 @@ public interface Transaction {
4961
*/
5062
Transaction setCommitTimeout(long timeout, TimeUnit timeUnit);
5163

64+
/**
65+
* Set the commit handler to use for this transaction invocation instead of the default handler configured for the
66+
* gateway.
67+
* @param commitHandler A commit handler implementation.
68+
* @return this transaction object to allow method chaining.
69+
*/
70+
Transaction setCommitHandler(CommitHandlerFactory commitHandler);
71+
5272
/**
5373
* Set the peers that should be used for endorsement of transaction submitted to the ledger using
5474
* {@link #submit(String...)}.

src/main/java/org/hyperledger/fabric/gateway/impl/TransactionImpl.java

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import org.hyperledger.fabric.sdk.exception.InvalidArgumentException;
3434
import org.hyperledger.fabric.sdk.exception.ProposalException;
3535
import org.hyperledger.fabric.sdk.exception.ServiceDiscoveryException;
36+
import org.hyperledger.fabric.sdk.transaction.TransactionContext;
3637

3738
import static org.hyperledger.fabric.sdk.Channel.DiscoveryOptions.createDiscoveryOptions;
3839

@@ -47,11 +48,12 @@ public final class TransactionImpl implements Transaction {
4748
private final NetworkImpl network;
4849
private final Channel channel;
4950
private final GatewayImpl gateway;
50-
private final CommitHandlerFactory commitHandlerFactory;
51+
private CommitHandlerFactory commitHandlerFactory;
5152
private TimePeriod commitTimeout;
5253
private final QueryHandler queryHandler;
5354
private Map<String, byte[]> transientData = null;
5455
private Collection<Peer> endorsingPeers = null;
56+
private final TransactionContext transactionContext;
5557

5658
TransactionImpl(final ContractImpl contract, final String name) {
5759
this.contract = contract;
@@ -62,13 +64,19 @@ public final class TransactionImpl implements Transaction {
6264
commitHandlerFactory = gateway.getCommitHandlerFactory();
6365
commitTimeout = gateway.getCommitTimeout();
6466
queryHandler = network.getQueryHandler();
67+
transactionContext = channel.newTransactionContext();
6568
}
6669

6770
@Override
6871
public String getName() {
6972
return name;
7073
}
7174

75+
@Override
76+
public String getTransactionId() {
77+
return transactionContext.getTxID();
78+
}
79+
7280
@Override
7381
public Transaction setTransient(final Map<String, byte[]> transientData) {
7482
this.transientData = transientData;
@@ -81,6 +89,12 @@ public Transaction setCommitTimeout(final long timeout, final TimeUnit timeUnit)
8189
return this;
8290
}
8391

92+
@Override
93+
public Transaction setCommitHandler(final CommitHandlerFactory commitHandler) {
94+
commitHandlerFactory = commitHandler;
95+
return this;
96+
}
97+
8498
@Override
8599
public Transaction setEndorsingPeers(final Collection<Peer> peers) {
86100
endorsingPeers = peers;
@@ -116,8 +130,7 @@ private Collection<ProposalResponse> sendTransactionProposal(final TransactionPr
116130
} else if (network.getGateway().isDiscoveryEnabled()) {
117131
Channel.DiscoveryOptions discoveryOptions = createDiscoveryOptions()
118132
.setEndorsementSelector(ServiceDiscovery.EndorsementSelector.ENDORSEMENT_SELECTION_RANDOM)
119-
.setInspectResults(true)
120-
.setForceDiscovery(true);
133+
.setInspectResults(true);
121134
return channel.sendTransactionProposalToEndorsers(request, discoveryOptions);
122135
} else {
123136
return channel.sendTransactionProposal(request);
@@ -127,9 +140,8 @@ private Collection<ProposalResponse> sendTransactionProposal(final TransactionPr
127140
private byte[] commitTransaction(final Collection<ProposalResponse> validResponses)
128141
throws TimeoutException, ContractException, InterruptedException {
129142
ProposalResponse proposalResponse = validResponses.iterator().next();
130-
String transactionId = proposalResponse.getTransactionID();
131143

132-
CommitHandler commitHandler = commitHandlerFactory.create(transactionId, network);
144+
CommitHandler commitHandler = commitHandlerFactory.create(getTransactionId(), network);
133145
commitHandler.startListening();
134146

135147
try {
@@ -172,6 +184,7 @@ private void configureRequest(final TransactionRequest request, final String...
172184
request.setChaincodeName(contract.getChaincodeId());
173185
request.setFcn(name);
174186
request.setArgs(args);
187+
request.setTransactionContext(transactionContext);
175188
}
176189

177190
private Collection<ProposalResponse> validatePeerResponses(final Collection<ProposalResponse> proposalResponses)

src/test/java/org/hyperledger/fabric/gateway/TestUtils.java

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,13 @@
1313
import java.nio.file.Path;
1414
import java.nio.file.Paths;
1515
import java.nio.file.attribute.FileAttribute;
16-
import java.security.NoSuchAlgorithmException;
17-
import java.security.NoSuchProviderException;
18-
import java.security.cert.CertificateException;
1916
import java.util.Arrays;
2017
import java.util.Collection;
2118
import java.util.EnumSet;
19+
import java.util.concurrent.atomic.AtomicLong;
2220
import java.util.concurrent.atomic.AtomicReference;
2321
import java.util.function.Consumer;
2422

25-
import org.bouncycastle.operator.OperatorCreationException;
2623
import org.hyperledger.fabric.gateway.impl.GatewayImpl;
2724
import org.hyperledger.fabric.gateway.impl.identity.GatewayUser;
2825
import org.hyperledger.fabric.gateway.spi.PeerDisconnectEvent;
@@ -34,10 +31,13 @@
3431
import org.hyperledger.fabric.sdk.HFClient;
3532
import org.hyperledger.fabric.sdk.Peer;
3633
import org.hyperledger.fabric.sdk.ProposalResponse;
34+
import org.hyperledger.fabric.sdk.QueryByChaincodeRequest;
35+
import org.hyperledger.fabric.sdk.TransactionProposalRequest;
3736
import org.hyperledger.fabric.sdk.User;
3837
import org.hyperledger.fabric.sdk.exception.InvalidArgumentException;
3938
import org.hyperledger.fabric.sdk.exception.ServiceDiscoveryException;
4039
import org.hyperledger.fabric.sdk.identity.X509Enrollment;
40+
import org.hyperledger.fabric.sdk.transaction.TransactionContext;
4141
import org.mockito.Mockito;
4242

4343
public final class TestUtils {
@@ -46,6 +46,8 @@ public final class TestUtils {
4646
private static final String UNUSED_FILE_PREFIX = "fgj-unused-";
4747
private static final Path NETWORK_CONFIG_PATH = Paths.get("src", "test", "java", "org", "hyperledger", "fabric", "gateway", "connection.json");
4848

49+
private final AtomicLong currentTransactionId = new AtomicLong();
50+
4951
public static TestUtils getInstance() {
5052
return INSTANCE;
5153
}
@@ -79,6 +81,8 @@ public HFClient newMockClient() {
7981

8082
HFClient mockClient = Mockito.mock(HFClient.class);
8183
Mockito.when(mockClient.getUserContext()).thenReturn(user);
84+
Mockito.when(mockClient.newTransactionProposalRequest()).thenReturn(TransactionProposalRequest.newInstance(user));
85+
Mockito.when(mockClient.newQueryProposalRequest()).thenReturn(QueryByChaincodeRequest.newInstance(user));
8286

8387
return mockClient;
8488
}
@@ -100,6 +104,8 @@ public Peer newMockPeer(String name) {
100104
public Channel newMockChannel(String name) {
101105
Channel mockChannel = Mockito.mock(Channel.class);
102106
Mockito.when(mockChannel.getName()).thenReturn(name);
107+
Mockito.when(mockChannel.newTransactionContext())
108+
.thenAnswer(invocation -> newMockTransactionContext());
103109

104110
AtomicReference<Channel.SDPeerAddition> sdPeerAdditionRef = new AtomicReference<>(newMockSDPeerAddition());
105111
Mockito.when(mockChannel.getSDPeerAddition())
@@ -110,6 +116,16 @@ public Channel newMockChannel(String name) {
110116
return mockChannel;
111117
}
112118

119+
private TransactionContext newMockTransactionContext() {
120+
TransactionContext mockContext = Mockito.mock(TransactionContext.class);
121+
Mockito.when(mockContext.getTxID()).thenReturn(newFakeTransactionId());
122+
return mockContext;
123+
}
124+
125+
private String newFakeTransactionId() {
126+
return Long.toHexString(currentTransactionId.incrementAndGet());
127+
}
128+
113129
private Channel.SDPeerAddition newMockSDPeerAddition() {
114130
Channel.SDPeerAddition mockPeerAddition = Mockito.mock(Channel.SDPeerAddition.class);
115131
try {
@@ -166,6 +182,14 @@ public Throwable getCause() {
166182
};
167183
}
168184

185+
public ProposalResponse newSuccessfulProposalResponse() {
186+
return newSuccessfulProposalResponse(new byte[0]);
187+
}
188+
189+
public ProposalResponse newSuccessfulProposalResponse(String responsePayload) {
190+
return newSuccessfulProposalResponse(responsePayload.getBytes());
191+
}
192+
169193
public ProposalResponse newSuccessfulProposalResponse(byte[] responsePayload) {
170194
ProposalResponse response = newProposalResponse(200, responsePayload);
171195
Mockito.when(response.getStatus()).thenReturn(ChaincodeResponse.Status.SUCCESS);

0 commit comments

Comments
 (0)