原文地址:https://medium.com/coinmonks/tutorial-chaincode-event-listener-on-hyperledger-fabric-java-sdk-557304f1fe28
自己跟着教程做了一遍,可以达到自己想实现的效果,监听到链码中的event。
Hyperledger Fabric Java SDK上的Chaincode Event监听器教程
Valerio Mattioli
嗨,大家好!本教程适用于已经具备fabric-sdk-java经验的所有Java开发人员。今天,我想向您展示如何设置“Chaincode Event监听器”,以准确了解提交的事务何时最终在区块链中提交,如图中所述的工作流程。
在进入代码之前,我给出了 Event的定义: Event是如何将结构与不同系统集成的主要方式。因此,您可以理解掌握 Event创建,聆听和处理的重要性。
前提条件是系统已经运行,正如Lukas Kolisko的本教程中所解释的那样。
在您拥有可以从Java SDK调用的工作链代码的基本系统之后,您可以开始在代码中包含event。
我们需要修改,创建或实现的文件按顺序:
chaincode -我们需要“PutState”与后设置从chaincode eventshim.ChaincodeStubInterface.SetEvent。
ChaincodeEventCapture - 我们需要创建这个类来捕获chaincode event。
chaincodeEvents - 它是列表,恰好是当event来自链码时填充的Vector
ChaincodeEventListener - 这是我们需要实现接收链代码event的接口。
在链码中设置event(发出event)
这部分是最简单的一个过程(是的,谢谢Go!),你基本上必须在PutState之后立即添加SetEvent以让链代码发出event。有关活动的所有事情都在本视频中得到了很好的解释。
现在重要的是要知道每个事务只能设置一个event(只有最后一个SetEvent会被转移回SDK)
我展示了一个代码示例,您可以从此repo中找到。
// tSet event 在chaincodehis 调用之后执行一些检查后的PutState
agent := a.CreateAgent(agentId, agentName, agentAddress, stub)
// ====代理已保存。Set Event ===
eventPayload:="Created Agent: " + agentId
payloadAsBytes := []byte(eventPayload)
eventError := stub.SetEvent("AgentCreatedEvent",payloadAsBytes)
添加这3行代码,我们已经完成了从链代码部分发出的 Event音乐会,现在让我们跳转到Java客户端应用程序 - SDK部分!
创建ChaincodeEventCapture类
现在是时候创建类来捕获链代码 Event了,我从fabric-sdk官方测试的测试端到端方案中学习了这种方法。
public class ChaincodeEventCapture {
private final String handle;
private final BlockEvent blockEvent;
private final ChaincodeEvent chaincodeEvent;
public ChaincodeEventCapture(String handle, BlockEvent blockEvent,
ChaincodeEvent chaincodeEvent) {
this.handle = handle;
this.blockEvent = blockEvent;
this.chaincodeEvent = chaincodeEvent;
}
/**
* @return the handle
*/
public String getHandle() {
return handle;
}
/**
* @return the blockEvent
*/
public BlockEvent getBlockEvent() {
return blockEvent;
}
/**
* @return the chaincodeEvent
*/
public ChaincodeEvent getChaincodeEvent() {
return chaincodeEvent;
}
}
创建ChaincodeEventCapture列表
这个列表是这个方法的核心,因为它将填充监听器捕获的 Event。
Vector chaincodeEvents = new Vector<>();
实现Chaincode Event监听器
这个列表是这个方法的核心,因为它将填充监听器捕获的 Event。这里的接口声明)
ChaincodeEventListener chaincodeEventListener = new ChaincodeEventListener() {
@Override
public void received(String handle, BlockEvent blockEvent, ChaincodeEvent chaincodeEvent) {
chaincodeEvents.add(new ChaincodeEventCapture(handle, blockEvent, chaincodeEvent));
String eventHub = blockEvent.getPeer();
if(eventHub != null){
eventHub = blockEvent.getPeer().getName()
} else {
eventHub = blockEvent.getEventHub().getName();
}
// Here put what you want to do when receive chaincode event
System.out.println("RECEIVED CHAINCODE EVENT with handle: " + handle + ", chaincodeId: " + chaincodeEvent.getChaincodeId() + ", chaincode event name: " + chaincodeEvent.getEventName() + ", transactionId: " + chaincodeEvent.getTxId() +", event Payload: " + new String(chaincodeEvent.getPayload()) + ", from eventHub: " + eventHub));
}
};
让我们把所有东西放在一起!
首先,在我的实现中,我创建了封装的方法 ChaincodeEventListener实现 和他的注册:
public static String setChaincodeEventListener(Channel channel,
String expectedEventName, Vector chaincodeEvents)
throws InvalidArgumentException {
ChaincodeEventListener chaincodeEventListener = new ChaincodeEventListener() {
@Override public void received(String handle, BlockEvent blockEvent,
ChaincodeEvent chaincodeEvent) {// ...the code up...}
};
// chaincode events.
String eventListenerHandle = channel.registerChaincodeEventListener(Pattern.compile(".*"),
Pattern.compile(Pattern.quote(expectedEventName)), chaincodeEventListener);
return eventListenerHandle;
}
其次,我们转到对chaincode的调用,因为您已经开始阅读本文并添加 Event处理部分。
public boolean createAgent(HFClient clientHF, User userHF, Channel channel, Agent newAgent)
throws ProposalException, InvalidArgumentException {
String chaincodeFunctionName = "CreateAgent";
String agentId = newAgent.getAgentId().toString();
String agentName = newAgent.getName().toString();
String agentAddress = newAgent.getAddress().toString();
String[] chaincodeArguments = new String[] {agentId, agentName, agentAddress};
Collection successful = new LinkedList<>();
Collection failed = new LinkedList<>();
// START CHAINCODE EVENT LISTENER HANDLER----------------------
String expectedEventName = "AgentCreatedEvent";
Vector chaincodeEvents = new Vector<>(); // Test list to capture
String chaincodeEventListenerHandle =
SdkIntegration.setChaincodeEventListener(channel, expectedEventName, chaincodeEvents);
// END CHAINCODE EVENT LISTENER HANDLER------------------------
Collection invokePropResp =
writeBlockchain(clientHF, userHF, channel, chaincodeName, chaincodeFunctionName,
chaincodeArguments);
boolean allPeerSucces = printWriteProposalResponse(successful, failed, invokePropResp);
System.out.println("successfully received transaction proposal responses.");
/**
* Send transaction to orderer only if all peer success
*/
sendTxToOrderer(userHF, channel, successful, allPeerSucces);
// START WAIT FOR THE EVENT-------------------------------------
boolean eventDone = false;
eventDone = SdkIntegration
.waitForChaincodeEvent(150, channel, chaincodeEvents, chaincodeEventListenerHandle);
log.info("eventDone: " + eventDone);
// END WAIT FOR THE EVENT---------------------------------------
return allPeerSucces;
}
正如你在上面的代码中所说,我们在调用中添加了两个部分(在注释中突出显示了开始和结尾以及尾随行:
在区块链中写入之前 - 我们首先创建将由eventListener填充 Event的空列表(Vector
将(成功的)事务提议发送到Orderer之后 - 在这部分我们等待调用函数waitForChaincodeEvent在这里显示的 Event,它基本上是一个超时的orderer:
public static boolean waitForChaincodeEvent(Integer timeout, Channel channel,
Vector chaincodeEvents, String chaincodeEventListenerHandle)
throws InvalidArgumentException {
boolean eventDone = false;
if (chaincodeEventListenerHandle != null) {
int numberEventsExpected = channel.getEventHubs().size() + channel
.getPeers(EnumSet.of(Peer.PeerRole.EVENT_SOURCE)).size();
log.info("numberEventsExpected: " + numberEventsExpected);
//just make sure we get the notifications
if (timeout.equals(0)) {
// get event without timer
while (chaincodeEvents.size() != numberEventsExpected) {
// do nothing
}
eventDone = true;
} else {
// get event with timer
for (int i = 0; i < timeout; i++) {
if (chaincodeEvents.size() == numberEventsExpected) {
eventDone = true;
break;
} else {
try {
double j = i;
j = j / 10;
log.info(j + " second");
Thread.sleep(100); // wait for the events for one tenth of second.
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
log.info("chaincodeEvents.size(): " + chaincodeEvents.size());
// unregister event listener
channel.unregisterChaincodeEventListener(chaincodeEventListenerHandle);
int i = 1;
// arrived event handling
for (ChaincodeEventCapture chaincodeEventCapture : chaincodeEvents) {
log.info("Event number. " + i);
log.info("event capture object: " + chaincodeEventCapture.toString());
log.info("Event Handle: " + chaincodeEventCapture.getHandle());
log.info("Event TxId: " + chaincodeEventCapture.getChaincodeEvent().getTxId());
log.info("Event Name: " + chaincodeEventCapture.getChaincodeEvent().getEventName());
log.info("Event Payload: " + chaincodeEventCapture.getChaincodeEvent()
.getPayload()); // byte
log.info("Event ChaincodeId: " + chaincodeEventCapture.getChaincodeEvent()
.getChaincodeId());
BlockEvent blockEvent = chaincodeEventCapture.getBlockEvent();
try {
log.info("Event Channel: " + blockEvent.getChannelId());
} catch (InvalidProtocolBufferException e) {
e.printStackTrace();
}
log.info("Event Hub: " + blockEvent.getEventHub());
i++;
}
} else {
log.info("chaincodeEvents.isEmpty(): " + chaincodeEvents.isEmpty());
}
log.info("eventDone: " + eventDone);
return eventDone;
}
如果你想要,为简单起见,首先测试 Event处理的工作,你可以用这个空的替换这个最后的函数(waitForChaincodeEvent)(我不推荐):
while (chaincodeEvents.isEmpty()) {
// do nothing
}
现在我们已经完成了!您正在处理hyperledger-fabric Java SDK上的chaincode Event!
我希望本教程有用!如果是,请不要犹豫,给我一个响亮的鼓掌!;)
再见!
- Valerio