从外部CorDapp扩展和覆盖流
Corda 4于上周(2月21日)發(fā)布,帶來了大量的新功能,使Corda的使用更加愉快。 老實說,我有點假設(shè)有很多新功能。 我快速瀏覽了變更日志,主要是看到我的貢獻被引用,但是我記得看到很多文本行。 那一定是一件好事吧?
無論如何,這些功能之一就是能夠擴展和覆蓋流程。 當您意識到Corda用Kotlin編寫并完全繼承了繼承性時,聽起來并不是很花哨(對于Java也是如此)。 但是,不僅如此。 Corda需要將“啟動流”映射到與其響應(yīng)的交易對手流。
當兩方使用相同的CorDapp時,這很好。 在這種情況下,不會增加任何額外的復雜性。 另一方面,如果交易對手希望在收到交易后將一些數(shù)據(jù)發(fā)送到外部系統(tǒng),他們該怎么做? 原始的CorDapp不了解或不關(guān)心此系統(tǒng),因此無法滿足這些需求。 能夠解決這類問題,開發(fā)人員可以在現(xiàn)有的CorDapps的基礎(chǔ)上構(gòu)建并對其進行調(diào)整,使其更適合其用例。 此外,一旦制定了良好的實踐,擴展第三方CorDapps將變得更加容易,并且在其他人已經(jīng)解決了部分問題的情況下,不再需要開發(fā)團隊不斷重新發(fā)明輪子。 顯然,這假定可以訪問這些外部CorDapp,但這完全是可能的。 特別是在R3 Marketplace已經(jīng)展示了一個集合的情況下。
在本文中,我們將專注于擴展和覆蓋流程。 此外,我們將采用兩種不同觀點的觀點。
- CorDapp的開發(fā)人員/維護人員
- 想要使用和改編現(xiàn)有CorDapp的開發(fā)人員
為了使該過程正常進行,雙方都必須努力以適當?shù)姆绞骄帉懫鋺?yīng)用程序,以便可以利用這些好處。
我們將首先查看原始CorDapp必須包含的內(nèi)容,然后是開發(fā)人員必須進行擴展的內(nèi)容。
在繼續(xù)之前,這里有指向擴展和覆蓋流程的官方文檔的鏈接。
編寫基本流程以允許擴展
以一種易于擴展的方式編寫CorDapp可能需要進行大量思考。 這在很大程度上取決于CorDapp維護人員要實現(xiàn)的目標。 為開發(fā)人員提供一種擴展CorDapp的方法,以便他們可以將數(shù)據(jù)發(fā)送到外部系統(tǒng)或添加自己的日志記錄,這不會造成任何問題。 另一方面,允許更改事務(wù)的內(nèi)容或?qū)⑹聞?wù)的內(nèi)容發(fā)送給誰將需要更多的考慮以確保不會濫用CorDapp。 我希望在以后的帖子中進一步探討這個主題。
出于本文的目的,我們將介紹更簡單的選項。 讓我們直接進入,因為到目前為止有很多文本,沒有代碼。 以下是將充當“基本”流程的SendMessageFlow ,它將在后面的部分中進行擴展:
@InitiatingFlow open class SendMessageFlow(private val message: MessageState) :FlowLogic<SignedTransaction>() {open fun preTransactionBuild() {// to be implemented by sub type flows - otherwise do nothing}open fun preSignaturesCollected(transaction: SignedTransaction) {// to be implemented by sub type flows - otherwise do nothing}open fun postSignaturesCollected(transaction: SignedTransaction) {// to be implemented by sub type flows - otherwise do nothing}open fun postTransactionCommitted(transaction: SignedTransaction) {// to be implemented by sub type flows - otherwise do nothing}@Suspendablefinal override fun call(): SignedTransaction {logger.info("Started sending message ${message.contents}")preTransactionBuild()val tx = verifyAndSign(transaction())preSignaturesCollected(tx)val sessions = listOf(initiateFlow(message.recipient))val stx = collectSignature(tx, sessions)postSignaturesCollected(stx)return subFlow(FinalityFlow(stx, sessions)).also {logger.info("Finished sending message ${message.contents}")postTransactionCommitted(it)}}// collectSignature// verifyAndSign// transaction }我刪除了一些功能,因此我們可以專注于重要的事情。
允許擴展此類的第一步甚至有時很重要的步驟是它是open 。 這更多的是Kotlin,而不是Java,因為Kotlin中的所有類默認都是final 。 如果您使用Java編寫此代碼,則只需忽略最后幾句話!
接下來,有一系列可以被覆蓋的功能。 每個功能已放置在Flow主要執(zhí)行程序內(nèi)的適當位置。 Flow運行時將調(diào)用它們。 目前,由于沒有為CorDapp開發(fā)人員提供任何使用,因此為他們提供了空的實現(xiàn)。
關(guān)于open功能。 您可以命名它們或?qū)⑺鼈兎胖迷谌魏涡枰奈恢谩?我認為這些功能對于希望在基本應(yīng)用程序提供的內(nèi)容上增加額外可追溯性的開發(fā)人員很有用。
深入了解更多細節(jié)。 call函數(shù)已經(jīng)final (與Java中相同),以防止覆蓋Flow的全部內(nèi)容。 如果有人想采用您的Flow并完全取代其“主要”功能,那有什么意義呢? 對我來說,這似乎有點狡猾。 消除這種可能性使其final成為明智之舉。
稍后,我們將研究如何將此Flow子類化。
下面是與SendMessageResponder交互的SendMessageFlow 。 它遵循與上述相同的概念,因此在以后我僅將其顯示為參考:
@InitiatedBy(SendMessageFlow::class) open class SendMessageResponder(private val session: FlowSession) : FlowLogic<Unit>() {open fun postTransactionSigned(transaction: SignedTransaction) {// to be implemented by sub type flows - otherwise do nothing}open fun postTransactionCommitted(transaction: SignedTransaction) {// to be implemented by sub type flows - otherwise do nothing}@Suspendablefinal override fun call() {val stx = subFlow(object : SignTransactionFlow(session) {override fun checkTransaction(stx: SignedTransaction) {}})postTransactionSigned(stx)val committed = subFlow(ReceiveFinalityFlow(otherSideSession = session,expectedTxId = stx.id))postTransactionCommitted(committed)} }擴展現(xiàn)有的啟動流
在本節(jié)中,我們將看到開發(fā)人員如何利用在上一個Flow上完成的工作。 它已經(jīng)具有所有必需的功能。 唯一缺少的是開發(fā)人員想要添加的少量額外的可追溯性。這要歸功于基本Flow所添加的功能。 這不會造成任何問題。
讓我們從擴展啟動流開始。 這樣做的要求如下:
- 擴展基礎(chǔ)@InitiatingFlow
- 不要添加@InitiatingFlow到新的流程(錯誤會發(fā)生,如果你這樣做)
- 引用基本Flow的構(gòu)造函數(shù)(Java中的super )
- 覆蓋任何所需的功能
- 調(diào)用新流程而不是基本流程
閱讀該列表后,您可能已經(jīng)意識到,這幾乎是對面向?qū)ο笳Z言(例如Kotlin和Java)中繼承的描述。 Corda內(nèi)可能還有更多事情要做,但是從您的角度來看,您只是像往常一樣編寫普通的面向?qū)ο蟮拇a。
遵循這些要求,我們可以看到擴展Flow的外觀:
@StartableByRPC class CassandraSendMessageFlow(private val message: MessageState) :SendMessageFlow(message) {override fun preTransactionBuild() {serviceHub.cordaService(MessageRepository::class.java).save(message,sender = true,committed = false)logger.info("Starting transaction for message: $message")}override fun preSignaturesCollected(transaction: SignedTransaction) {val keys = transaction.requiredSigningKeys - ourIdentity.owningKeylogger.info("Collecting signatures from $keys for transaction for message: $message")}override fun postSignaturesCollected(transaction: SignedTransaction) {logger.info("Collected signatures for transaction for message: $message")}override fun postTransactionCommitted(transaction: SignedTransaction) {serviceHub.cordaService(MessageRepository::class.java).save(message,sender = true,committed = true)logger.info("Committed transaction for message: $message")} }我留下了實現(xiàn)我所談?wù)摰念~外可追溯性的所有嘈雜函數(shù),但這是由于沒有它們的類將有多么空。 由于不需要call 。 此流程僅需要覆蓋open功能。 老實說,根本不需要覆蓋它們,它們是可選的。 如果需要,此流可以覆蓋單個函數(shù),然后將其保留為空。
是否滿足上述所有要求?
- CassandraSendMessageFlow擴展了SendMessageFlow
- 看不到@InitiatingFlow
- 在Kotlin中,無論如何都必須調(diào)用super構(gòu)造函數(shù),這樣就完成了
- 在這種情況下,所有功能都被覆蓋
- 我們還沒有走這么遠
好的,到目前為止是4/5。 那是一個很好的開始。 要劃掉列表中的最后一項,我們需要查看其調(diào)用方式。 以下是調(diào)用基本SendMessageFlow和CassandraSendMessageFlow擴展Flow的代碼片段。
從SendMessageFlow開始:
proxy.startFlow(::SendMessageFlow, messageState)其次是CassandraSendMessageFlow :
proxy.startFlow(::CassandraSendMessageFlow, messageState)注意區(qū)別嗎? 在這種情況下,僅流的名稱已更改。 沒有其他的。
這兩個片段都是完全有效的。 仍然允許調(diào)用原始的SendMessageFlow 。 請記住,從我們的角度來看,它只是普通的面向?qū)ο蟠a。 它不會將多余的代碼添加到擴展的Flow中,但是它仍然可以執(zhí)行而不會出現(xiàn)問題。 完成此步驟符合擴展@InitiatingFlow的最后要求。
在結(jié)束本節(jié)之前,這里是Corda文檔中需要記住的重要信息:
“您必須確保子類中的發(fā)送/接收/子流的順序與父類兼容。”
我將把它放在以下所有部分中,因為不遵循這將導致您的流程失敗。
擴展響應(yīng)者流程
擴展響應(yīng)者流的方式與擴展@InitiatingFlow流的方式非常相似。 唯一的區(qū)別是它的調(diào)用方式。 如文檔中所述:
“ Corda會檢測到 BaseResponder 和 SubResponder 都已配置為響應(yīng)發(fā)起方。 然后,Corda將計算到 FlowLogic 的躍點, 并選擇距離最遠的實現(xiàn),即:子類化最多的實現(xiàn)。”
“最子類化”的陳述是本文的重要內(nèi)容。 因此,從開發(fā)人員的角度來看,他們所需要做的就是擴展外部基本“響應(yīng)者流程”,僅此而已。 我非常喜歡之前的需求列表,因此讓我們再看一遍擴展響應(yīng)者流程:
- 擴展基礎(chǔ)@InitiatedBy /響應(yīng)者流
- 將@InitiatedBy添加到新流程
- 引用基本Flow的構(gòu)造函數(shù)(Java中的super )
- 覆蓋任何所需的功能
如果您保持警惕,您可能已經(jīng)注意到,沒有提到如何調(diào)用它。 擴展的“響應(yīng)者流”不需要在其他任何地方調(diào)用或引用。 Corda將盡一切努力將所有物品路由到正確的位置。
可以肯定的是,讓我們快速看一個例子:
@InitiatedBy(SendMessageFlow::class) class CassandraSendMessageResponder(session: FlowSession) :SendMessageResponder(session) {override fun postTransactionSigned(transaction: SignedTransaction) {val message = transaction.coreTransaction.outputsOfType<MessageState>().single()logger.info("Signed transaction for message: $message")}override fun postTransactionCommitted(transaction: SignedTransaction) {val message = transaction.coreTransaction.outputsOfType<MessageState>().single()serviceHub.cordaService(MessageRepository::class.java).save(message,sender = false,committed = true)logger.info("Committed transaction for message: $message")} }此外,讓我們再次回顧“最子類化”的說法。 CassandraSendMessageResponder是SendMessageResponder的子類,因此由Corda選擇以處理來自發(fā)起流的請求。 但是,這可以采取進一步的措施。 如果還有另一個類,例如說SuperSpecialCassandraSendMessageResponder ,那么此流程現(xiàn)在就是Corda將開始使用的流程。 盡管我確實發(fā)現(xiàn)這種情況目前不太可能,但絕對值得了解。
再次復制并粘貼此語句,這樣您就不會忘記:
“您必須確保子類中的發(fā)送/接收/子流的順序與父類兼容?!?/strong>
覆蓋響應(yīng)者流程
這是故意的一個單獨的部分。 在這里,我們將專門討論覆蓋響應(yīng)程序流而不是擴展響應(yīng)流。 您為什么要這樣做,有什么區(qū)別? 回答第一個問題,開發(fā)人員可能想要編寫一個與原始基礎(chǔ)流程有很大差異的“響應(yīng)者流程”,但仍需要與外部CorDapp提供的特定“啟動流程”進行交互。 為此,他們可以覆蓋Flow。 另一個描述這個的詞可以是“替換”。 原始基本流程已完全被替代流程取代。 在這種情況下不涉及擴展。
我認為Corda文檔在此主題上的措辭非常好:
“雖然子類化方法可能對大多數(shù)應(yīng)用程序有用,但還有另一種機制可以覆蓋這種行為。 例如,如果特定的CordApp用戶需要這樣一個不同的響應(yīng)者,則將現(xiàn)有流子類化不是一個好的解決方案,這將很有用?!?/strong>
希望此摘錄以及我之前的描述將闡明擴展和覆蓋響應(yīng)程序流之間的區(qū)別。
那么,最重要的Flow會是什么樣子? 好吧,在合理范圍內(nèi),您真正想要的任何東西。 也許看起來像下面,盡管我對此表示懷疑:
@InitiatedBy(SendMessageFlow::class) class OverridingResponder(private val session: FlowSession) :FlowLogic<Unit>() {@Suspendableoverride fun call() {val stx = subFlow(object : SignTransactionFlow(session) {override fun checkTransaction(stx: SignedTransaction) {}})logger.info("Screw the original responder. I'll build my own responder... with blackjack and hookers!")subFlow(ReceiveFinalityFlow(otherSideSession = session,expectedTxId = stx.id))} }由于此流程完全替代了原始基本流程,因此它看起來就像普通的響應(yīng)者流程。 既然如此,那是一個。 這意味著它有@InitiatedBy引用了Initiating Flow,擴展了FlowLogic并實現(xiàn)了call函數(shù)。
最后一次將它放在這里:
“您必須確保子類中的發(fā)送/接收/子流的順序與父類兼容?!?/strong>
在這里,這比前幾節(jié)更為普遍。 由于整個call函數(shù)都已被覆蓋,因此您必須確保每個send和receive都在正確的位置,以便與啟動流的交互不會出現(xiàn)錯誤。
在配置方面,要做更多的事情,而不是擴展Flow。 在這種情況下,我們正在嘗試將響應(yīng)者完全替換為另一個。 為此,我們需要一種方法來告訴節(jié)點將交互從“發(fā)起流”重定向到新的優(yōu)先響應(yīng)器流。 Corda提供了一種做到這一點的方法。
要指定重定向,請將以下內(nèi)容添加到您的node.conf :
flowOverrides {overrides=[{initiator="com.lankydanblog.tutorial.base.flows.SendMessageFlow"responder="com.lankydanblog.tutorial.cassandra.flows.OverridingResponder"}] }顯然,更改您自己引用的類…
那么這是怎么回事? 這個配置說, SendMessageFlow通常與之交互SendMessageResponder現(xiàn)在將路線OverridingResponder代替。
為了使一切都變得簡單, Cordform插件提供了flowOverride方法作為deployNodes一部分。 然后,將為您生成上面的配置塊。 對于上面的示例,使用了以下代碼:
node {name "O=PartyA,L=London,C=GB"p2pPort 10002rpcSettings {address("localhost:10006")adminAddress("localhost:10046")}rpcUsers = [[user: "user1", "password": "test", "permissions": ["ALL"]]]cordapp(project(':cordapp-contracts-states'))cordapp(project(':cordapp'))cordapp(project(':cordapp-extended-cassandra'))// the important partflowOverride("com.lankydanblog.tutorial.base.flows.SendMessageFlow","com.lankydanblog.tutorial.cassandra.flows.OverridingResponder") }現(xiàn)在,在deployNodes運行并啟動了節(jié)點之后,來自SendMessageFlow或其任何子類的任何請求現(xiàn)在都將通信路由到OverridingResponder 。
結(jié)論
Corda 4提供的便捷功能之一就是能夠從第三方CorDapps(或您自己的)自定義Flow。 這可以通過擴展或覆蓋兩種方法來完成。
擴展將是我在這兩者之間的第一選擇,但確實需要CorDapp開發(fā)人員付出更多的努力。 他們必須提供足夠的自定義渠道,而不放棄對Flows原始功能的控制。 提供很少的自定義可能不會阻止其他開發(fā)人員使用他們的CorDapp。 但是,開發(fā)人員可能會對缺乏對自己的應(yīng)用程序的控制感到不滿。 這是一個濕滑的坡道,可通過自定義路線來控制原始意圖。 另一方面,實際上擴展Flow并不需要太多工作,這使開發(fā)人員更容易采用和適應(yīng)外部Flow。
另一方面,對于CorDapp開發(fā)人員而言,覆蓋不需要任何工作,而是可以利用外部響應(yīng)程序流將所有內(nèi)容放到開發(fā)人員中。 那是因為現(xiàn)有的Flow幾乎被丟棄了,并且回溯到原始實現(xiàn)的唯一參考就是指向Initiating Flow的鏈接。
通過同時支持Flow的擴展和覆蓋,CorDapp開發(fā)人員將能夠利用外部CorDapp,同時仍提供足夠的自定義功能來滿足他們可能擁有的所有業(yè)務(wù)需求。 隨著時間的流逝,開發(fā)人員將推動重用現(xiàn)有CorDapps的使用,因為它們提供了對其他自定義項的訪問權(quán)限,很快將與我們在任何工作中已經(jīng)利用的開源庫的地位相同。
這篇文章中使用的代碼可以在我的GitHub上找到 。 它包含CassandraSendMessageFlow的代碼,該代碼建立與外部Cassandra數(shù)據(jù)庫的連接以保存跟蹤樣式數(shù)據(jù)。 它還包含另一個模塊,該模塊發(fā)送HTTP請求作為其基本流擴展的一部分。 如果您在閱讀本文后仍然感到好奇,則此存儲庫可能會有所幫助。
如果您喜歡這篇文章或發(fā)現(xiàn)它對您有幫助(或兩者都有),請隨時在Twitter上@LankyDanDev關(guān)注我,并記住與可能對您有用的任何人分享!
翻譯自: https://www.javacodegeeks.com/2019/03/extending-overriding-flows-cordapps.html
總結(jié)
以上是生活随笔為你收集整理的从外部CorDapp扩展和覆盖流的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 电脑键盘偶尔失灵按一个字母会蹦出多个字母
- 下一篇: apache camel_Apache