pbfunc外部扩展函数_从外部CorDapp扩展和覆盖流
pbfunc外部擴展函數
Corda 4于上周(2月21日)發布,帶來了大量的新功能,使Corda更加令人愉快。 老實說,我有點假設有很多新功能。 我快速瀏覽了變更日志,主要是看到我的貢獻被引用,但是我記得看到很多文本行。 那一定是一件好事吧?
無論如何,這些功能之一就是能夠擴展和覆蓋流程。 當您意識到Corda用Kotlin編寫并完全繼承了繼承性時,聽起來并不是很花哨(對于Java也是如此)。 但是,不僅如此。 Corda需要將“發起流程”映射到與其響應的交易對手流程。
當兩方使用相同的CorDapp時,這很好。 在這種情況下,不會增加任何額外的復雜性。 另一方面,如果交易對手希望在收到交易后將一些數據發送到外部系統,他們該怎么做? 原始的CorDapp不了解或不關心此系統,因此無法滿足這些需求。 能夠解決這類問題,使開發人員可以在現有的CorDapps的基礎上進行構建,并使它們適應于其用例。 此外,一旦制定了良好的實踐,擴展第三方CorDapps將變得更加容易,并且在其他人已經解決了部分問題的同時,不再需要開發團隊不斷重新發明輪子。 顯然,這假定可以訪問這些外部CorDapp,但這完全是可能的。 特別是在R3 Marketplace已經展示了一個集合的情況下。
在本文中,我們將專注于擴展和覆蓋流程。 此外,我們將采用兩種不同觀點的觀點。
- CorDapp的開發人員/維護人員
- 想要使用和改編現有CorDapp的開發人員
為了使該過程正常進行,雙方都必須努力以適當的方式編寫其應用程序,以便可以利用其收益。
我們將首先查看原始CorDapp必須包含的內容,然后是開發人員必須進行擴展的內容。
在我們進行下一步之前,這里是指向有關擴展和覆蓋流程的官方文檔的鏈接。
編寫基本流程以允許擴展
以一種易于擴展的方式編寫CorDapp可能需要進行大量思考。 這在很大程度上取決于CorDapp維護人員要實現的目標。 為開發人員提供一種擴展CorDapp的方式,以便他們可以將數據發送到外部系統或添加自己的日志記錄,這不會造成任何問題。 另一方面,允許更改事務的內容或將事務的內容發送給誰將需要更多的考慮以確保不濫用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主要執行程序內的適當位置。 Flow運行時將調用它們。 目前,由于沒有為CorDapp開發人員提供任何使用,因此為他們提供了空的實現。
關于open功能。 您可以命名它們或將它們放置在任何需要的位置。 我認為這些功能對于希望在基本應用程序提供的內容上增加額外可追溯性的開發人員很有用。
深入了解更多細節。 call函數已被final (與Java中相同),以防止覆蓋Flow的全部內容。 如果有人想采用您的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)} }擴展現有的啟動流
在本節中,我們將看到開發人員如何利用在上一個Flow上完成的工作。 它已經具有所有必需的功能。 唯一缺少的是開發人員想要添加的少量額外的可跟蹤性。這要歸功于基本Flow所添加的功能。 這不會造成任何問題。
讓我們從擴展啟動流開始。 這樣做的要求如下:
- 擴展基礎@InitiatingFlow
- 不要添加@InitiatingFlow到新的流程(錯誤會發生,如果你這樣做)
- 引用基本Flow的構造函數(Java中的super )
- 覆蓋任何所需的功能
- 調用新流程而不是基本流程
閱讀該列表后,您可能已經意識到,這幾乎是對面向對象語言(例如Kotlin和Java)中繼承的描述。 Corda內可能還有更多事情要做,但是從您的角度來看,您只是像往常一樣編寫普通的面向對象代碼。
遵循這些要求,我們可以看到擴展的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")} }我留下了實現我所談論的額外可追溯性的所有嘈雜函數,但這是由于沒有它們的類將有多么空。 由于不需要call 。 此流程僅需要覆蓋open功能。 老實說,根本不需要覆蓋它們,它們是可選的。 如果需要,此流可以覆蓋單個函數,然后將其保留為空。
是否滿足上述所有要求?
- CassandraSendMessageFlow擴展了SendMessageFlow
- 看不到@InitiatingFlow
- 在Kotlin中,無論如何都必須調用super構造函數,這樣就完成了
- 在這種情況下,所有功能都被覆蓋
- 我們還沒有走這么遠
好的,到目前為止是4/5。 那是一個很好的開始。 要劃掉列表中的最后一項,我們需要查看其調用方式。 以下是調用基本SendMessageFlow和CassandraSendMessageFlow擴展Flow的代碼片段。
從SendMessageFlow開始:
proxy.startFlow(::SendMessageFlow, messageState)其次是CassandraSendMessageFlow :
proxy.startFlow(::CassandraSendMessageFlow, messageState)注意區別嗎? 在這種情況下,僅流的名稱已更改。 沒有其他的。
這兩個代碼段都是完全有效的。 仍然允許調用原始的SendMessageFlow 。 請記住,從我們的角度來看,它只是普通的面向對象代碼。 它不會在擴展的Flow中添加多余的代碼,但是它仍然可以執行而不會出現問題。 完成此步驟符合擴展@InitiatingFlow的最后要求。
在結束本節之前,這里是Corda文檔中需要記住的重要信息:
“您必須確保子類中的發送/接收/子流的順序與父類兼容。”
我將把它放在以下所有部分中,因為不遵循這將導致您的流程失敗。
擴展響應者流程
擴展響應者流的方式與擴展@InitiatingFlow流的方式非常相似。 唯一的區別是它的調用方式。 如文檔中所述:
“ Corda會檢測到 BaseResponder 和 SubResponder 都已配置為響應發起方。 然后,Corda將計算到 FlowLogic 的躍點, 并選擇距離最遠的實現,即:子類化最多的實現。”
“最子類化”的陳述是本文的重要內容。 因此,從開發人員的角度來看,他們所需要做的就是擴展外部基本響應程序流,僅此而已。 我非常喜歡之前的需求列表,因此讓我們再看一遍擴展響應者流程:
- 擴展基礎@InitiatedBy /響應者流
- 將@InitiatedBy添加到新流程
- 引用基本Flow的構造函數(Java中的super )
- 覆蓋任何所需的功能
如果您保持警惕,您可能已經注意到,沒有提到如何調用它。 擴展的“響應者流”不需要在其他任何地方調用或引用。 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選擇以處理來自啟動流的請求。 但是,這可以采取進一步的措施。 如果還有另一個類,例如說SuperSpecialCassandraSendMessageResponder ,那么此流程現在就是Corda將開始使用的流程。 盡管我確實發現這種情況目前不太可能,但絕對值得了解。
再次復制并粘貼此語句,這樣您就不會忘記:
“您必須確保子類中的發送/接收/子流的順序與父類兼容。”
覆蓋響應者流程
這是故意的一個單獨的部分。 在這里,我們將專門討論覆蓋響應程序流而不是擴展響應流。 您為什么要這樣做,有什么區別? 回答第一個問題,開發人員可能想要編寫一個與原始基本流程大不相同的Responder Flow,但仍需要與外部CorDapp提供的特定啟動流程進行交互。 為此,他們可以覆蓋Flow。 另一個描述這個的詞可以是“替換”。 原始基本流程完全被覆蓋流程取代。 在這種情況下不涉及擴展。
我認為Corda文檔在此主題上的措辭非常好:
“雖然子類化方法可能對大多數應用程序有用,但是還有另一種機制可以覆蓋這種行為。 例如,如果特定的CordApp用戶需要這樣一個不同的響應者,以子類化現有流將不是一個好的解決方案,那么這將很有用。”
希望此摘錄以及我之前的描述將闡明擴展和覆蓋響應程序流之間的區別。
那么,最重要的Flow會是什么樣子? 好吧,在合理范圍內,您真正想要的任何東西。 也許看起來像下面,盡管我對此表示懷疑:
@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))} }由于此流程完全替代了原始基本流程,因此它看起來就像普通的響應者流程。 既然是這樣,那就好了。 這意味著它有@InitiatedBy引用了Initiating Flow,擴展了FlowLogic并實現了call函數。
最后一次將它放在這里:
“您必須確保子類中的發送/接收/子流的順序與父類兼容。”
與上一節相比,這在這里更為普遍。 由于整個call函數都已被覆蓋,因此您必須確保每個send和receive都在正確的位置,以便與啟動流的交互運行不會出錯。
在配置方面,要做更多的事情,而不是擴展Flow。 在這種情況下,我們正在嘗試將響應者完全替換為另一個。 為此,我們需要一種方法來告訴節點將交互從“發起流”重定向到新的優先響應器流。 Corda提供了一種做到這一點的方法。
要指定重定向,請將以下內容添加到您的node.conf :
flowOverrides {overrides=[{initiator="com.lankydanblog.tutorial.base.flows.SendMessageFlow"responder="com.lankydanblog.tutorial.cassandra.flows.OverridingResponder"}] }顯然,更改您自己引用的類…
那么這是怎么回事? 這個配置說, SendMessageFlow通常與之交互SendMessageResponder現在將路線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") }現在,在deployNodes運行并啟動了節點之后,來自SendMessageFlow或其任何子類的任何請求現在都將通信路由到OverridingResponder 。
結論
Corda 4提供的便捷功能之一就是能夠從第三方CorDapps(或您自己的)自定義Flows。 這可以通過兩種方法來完成,即擴展或覆蓋。
擴展將是我在這兩者之間的第一選擇,但確實需要CorDapp開發人員付出更多的努力。 他們必須提供足夠的自定義渠道,而不能放棄對Flows原始功能的控制。 提供很少的自定義可能不會阻止其他開發人員使用他們的CorDapp。 但是,開發人員可能會對缺乏對自己的應用程序的控制感到不滿。 這是一個濕滑的坡道,可通過自定義路線來控制原始意圖。 另一方面,實際上擴展Flow并不需要太多工作,這使開發人員更容易采用和適應外部Flow。
另一方面,對于CorDapp開發人員而言,覆蓋不需要任何工作,而是可以利用外部“響應程序流”將所有內容放入開發人員。 那是因為現有的Flow幾乎被丟棄了,并且回溯到原始實現的唯一參考就是指向Initiating Flow的鏈接。
通過擁抱Flow的擴展和覆蓋,CorDapp開發人員將能夠利用外部CorDapp,同時仍提供足夠的自定義功能來滿足他們可能擁有的所有業務需求。 隨著時間的流逝,開發人員將推動重用現有CorDapps的使用,因為它們提供了對其他自定義的訪問權限,很快將與我們在任何工作中已經利用的開源庫的地位相同。
這篇文章中使用的代碼可以在我的GitHub上找到 。 它包含CassandraSendMessageFlow的代碼,該代碼建立與外部Cassandra數據庫的連接以保存跟蹤樣式數據。 它還包含另一個模塊,該模塊發送HTTP請求作為其基本流擴展的一部分。 如果您在閱讀本文后仍然感到好奇,則此存儲庫可能會有所幫助。
如果您喜歡這篇文章或發現它有幫助(或兩者都有),請隨時在Twitter上@LankyDanDev關注我,并記住與可能對您有用的任何人分享!
翻譯自: https://www.javacodegeeks.com/2019/03/extending-overriding-flows-cordapps.html
pbfunc外部擴展函數
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的pbfunc外部扩展函数_从外部CorDapp扩展和覆盖流的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 线程同步临界区(线程同步linux)
- 下一篇: 矩阵累积相乘 java_累积:轻松自定义