[翻译] Hyperledger Fabric Java SDK上的Chaincode Event监听器教程

原文地址: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创建,聆听和处理的重要性。

Hyperledger Fabric中的开发人员交互

前提条件是系统已经运行,正如Lukas Kolisko本教程中所解释的那样。

在您拥有可以从Java SDK调用的工作链代码的基本系统之后,您可以开始在代码中包含event。

我们需要修改,创建或实现的文件按顺序:

chaincode -我们需要“PutState”与后设置从chaincode eventshim.ChaincodeStubInterface.SetEvent

ChaincodeEventCapture - 我们需要创建这个类来捕获chaincode event。

chaincodeEvents - 它是列表,恰好是当event来自链码时填充的Vector <ChaincodeEventCapture>。

ChaincodeEventListener - 这是我们需要实现接收链代码event的接口。

在链码中设置event(发出event)

这部分是最简单的一个过程(是的,谢谢Go!),你基本上必须在PutState之后立即添加SetEvent以让链代码发出event。有关活动的所有事情都在本视频中得到了很好的解释。

现在重要的是要知道每个事务只能设置一个event(只有最后一个SetEvent会被转移回SDK)

image.jpeg

我展示了一个代码示例,您可以从此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<ChaincodeEventCapture> 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<ChaincodeEventCapture> 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<ProposalResponse> successful = new LinkedList<>();
    Collection<ProposalResponse> failed = new LinkedList<>();

    // START CHAINCODE EVENT LISTENER HANDLER----------------------
    String expectedEventName = "AgentCreatedEvent";
    Vector<ChaincodeEventCapture> chaincodeEvents = new Vector<>(); // Test list to capture
    String chaincodeEventListenerHandle =
        SdkIntegration.setChaincodeEventListener(channel, expectedEventName, chaincodeEvents);
    // END CHAINCODE EVENT LISTENER HANDLER------------------------

    Collection<ProposalResponse> 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 <ChaincodeEventCapture> chaincodeEvents = new Vector <>();)然后我们创建ChaincodeEventListener,调用之前定义的函数(setChaincodeEventListener)。(
将(成功的)事务提议发送到Orderer之后 - 在这部分我们等待调用函数waitForChaincodeEvent在这里显示的 Event,它基本上是一个超时的orderer:

public static boolean waitForChaincodeEvent(Integer timeout, Channel channel,
    Vector<ChaincodeEventCapture> 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
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,367评论 6 512
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,959评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,750评论 0 357
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,226评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,252评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,975评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,592评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,497评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,027评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,147评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,274评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,953评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,623评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,143评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,260评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,607评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,271评论 2 358

推荐阅读更多精彩内容