| @@ -1,4 +1,42 @@ | |||
| /target/ | |||
| *.classpath | |||
| *.project | |||
| **.classpath | |||
| **.project | |||
| **/bin/ | |||
| **.class | |||
| target/ | |||
| *.bak | |||
| bin/ | |||
| *.iml | |||
| *.ipr | |||
| *.iws | |||
| .idea | |||
| .classpath | |||
| .project | |||
| .settings/ | |||
| .DS_Store | |||
| .springBeans | |||
| .externalToolBuilders/ | |||
| *.versionsBackup | |||
| .factorypath | |||
| source/**/config/currentView | |||
| source/gateway/data/ | |||
| source/peer/data/ | |||
| *.orig | |||
| source/peer/config/view/current.view | |||
| source/contract/contract-jar/src/main/java/com/jd/blockchain/contract/*.contract | |||
| source/contract/contract-jar/src/main/java/com/jd/blockchain/contract/contract.properties | |||
| source/contract/logs | |||
| source/contract/tmp | |||
| source/contract/contract-model/src/main/resources/contractCoreLib/ | |||
| *.db | |||
| *.log | |||
| source/test/test-integration/txs | |||
| contract-libs/coreLib/contract-model-0.6.0-SNAPSHOT.jar | |||
| contract-libs/coreLib/crypto-framework-0.6.0-SNAPSHOT.jar | |||
| contract-libs/coreLib/ledger-model-0.6.0-SNAPSHOT.jar | |||
| contract-libs/coreLib/sdk-base-0.6.0-SNAPSHOT.jar | |||
| contract-libs/coreLib/utils-common-1.6.1-SNAPSHOT.jar | |||
| source/tools/tools-initializer/bftsmart.config | |||
| test/test-integration/runtime/ | |||
| @@ -0,0 +1,202 @@ | |||
| Apache License | |||
| Version 2.0, January 2004 | |||
| http://www.apache.org/licenses/ | |||
| TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | |||
| 1. Definitions. | |||
| "License" shall mean the terms and conditions for use, reproduction, | |||
| and distribution as defined by Sections 1 through 9 of this document. | |||
| "Licensor" shall mean the copyright owner or entity authorized by | |||
| the copyright owner that is granting the License. | |||
| "Legal Entity" shall mean the union of the acting entity and all | |||
| other entities that control, are controlled by, or are under common | |||
| control with that entity. For the purposes of this definition, | |||
| "control" means (i) the power, direct or indirect, to cause the | |||
| direction or management of such entity, whether by contract or | |||
| otherwise, or (ii) ownership of fifty percent (50%) or more of the | |||
| outstanding shares, or (iii) beneficial ownership of such entity. | |||
| "You" (or "Your") shall mean an individual or Legal Entity | |||
| exercising permissions granted by this License. | |||
| "Source" form shall mean the preferred form for making modifications, | |||
| including but not limited to software source code, documentation | |||
| source, and configuration files. | |||
| "Object" form shall mean any form resulting from mechanical | |||
| transformation or translation of a Source form, including but | |||
| not limited to compiled object code, generated documentation, | |||
| and conversions to other media types. | |||
| "Work" shall mean the work of authorship, whether in Source or | |||
| Object form, made available under the License, as indicated by a | |||
| copyright notice that is included in or attached to the work | |||
| (an example is provided in the Appendix below). | |||
| "Derivative Works" shall mean any work, whether in Source or Object | |||
| form, that is based on (or derived from) the Work and for which the | |||
| editorial revisions, annotations, elaborations, or other modifications | |||
| represent, as a whole, an original work of authorship. For the purposes | |||
| of this License, Derivative Works shall not include works that remain | |||
| separable from, or merely link (or bind by name) to the interfaces of, | |||
| the Work and Derivative Works thereof. | |||
| "Contribution" shall mean any work of authorship, including | |||
| the original version of the Work and any modifications or additions | |||
| to that Work or Derivative Works thereof, that is intentionally | |||
| submitted to Licensor for inclusion in the Work by the copyright owner | |||
| or by an individual or Legal Entity authorized to submit on behalf of | |||
| the copyright owner. For the purposes of this definition, "submitted" | |||
| means any form of electronic, verbal, or written communication sent | |||
| to the Licensor or its representatives, including but not limited to | |||
| communication on electronic mailing lists, source code control systems, | |||
| and issue tracking systems that are managed by, or on behalf of, the | |||
| Licensor for the purpose of discussing and improving the Work, but | |||
| excluding communication that is conspicuously marked or otherwise | |||
| designated in writing by the copyright owner as "Not a Contribution." | |||
| "Contributor" shall mean Licensor and any individual or Legal Entity | |||
| on behalf of whom a Contribution has been received by Licensor and | |||
| subsequently incorporated within the Work. | |||
| 2. Grant of Copyright License. Subject to the terms and conditions of | |||
| this License, each Contributor hereby grants to You a perpetual, | |||
| worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |||
| copyright license to reproduce, prepare Derivative Works of, | |||
| publicly display, publicly perform, sublicense, and distribute the | |||
| Work and such Derivative Works in Source or Object form. | |||
| 3. Grant of Patent License. Subject to the terms and conditions of | |||
| this License, each Contributor hereby grants to You a perpetual, | |||
| worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |||
| (except as stated in this section) patent license to make, have made, | |||
| use, offer to sell, sell, import, and otherwise transfer the Work, | |||
| where such license applies only to those patent claims licensable | |||
| by such Contributor that are necessarily infringed by their | |||
| Contribution(s) alone or by combination of their Contribution(s) | |||
| with the Work to which such Contribution(s) was submitted. If You | |||
| institute patent litigation against any entity (including a | |||
| cross-claim or counterclaim in a lawsuit) alleging that the Work | |||
| or a Contribution incorporated within the Work constitutes direct | |||
| or contributory patent infringement, then any patent licenses | |||
| granted to You under this License for that Work shall terminate | |||
| as of the date such litigation is filed. | |||
| 4. Redistribution. You may reproduce and distribute copies of the | |||
| Work or Derivative Works thereof in any medium, with or without | |||
| modifications, and in Source or Object form, provided that You | |||
| meet the following conditions: | |||
| (a) You must give any other recipients of the Work or | |||
| Derivative Works a copy of this License; and | |||
| (b) You must cause any modified files to carry prominent notices | |||
| stating that You changed the files; and | |||
| (c) You must retain, in the Source form of any Derivative Works | |||
| that You distribute, all copyright, patent, trademark, and | |||
| attribution notices from the Source form of the Work, | |||
| excluding those notices that do not pertain to any part of | |||
| the Derivative Works; and | |||
| (d) If the Work includes a "NOTICE" text file as part of its | |||
| distribution, then any Derivative Works that You distribute must | |||
| include a readable copy of the attribution notices contained | |||
| within such NOTICE file, excluding those notices that do not | |||
| pertain to any part of the Derivative Works, in at least one | |||
| of the following places: within a NOTICE text file distributed | |||
| as part of the Derivative Works; within the Source form or | |||
| documentation, if provided along with the Derivative Works; or, | |||
| within a display generated by the Derivative Works, if and | |||
| wherever such third-party notices normally appear. The contents | |||
| of the NOTICE file are for informational purposes only and | |||
| do not modify the License. You may add Your own attribution | |||
| notices within Derivative Works that You distribute, alongside | |||
| or as an addendum to the NOTICE text from the Work, provided | |||
| that such additional attribution notices cannot be construed | |||
| as modifying the License. | |||
| You may add Your own copyright statement to Your modifications and | |||
| may provide additional or different license terms and conditions | |||
| for use, reproduction, or distribution of Your modifications, or | |||
| for any such Derivative Works as a whole, provided Your use, | |||
| reproduction, and distribution of the Work otherwise complies with | |||
| the conditions stated in this License. | |||
| 5. Submission of Contributions. Unless You explicitly state otherwise, | |||
| any Contribution intentionally submitted for inclusion in the Work | |||
| by You to the Licensor shall be under the terms and conditions of | |||
| this License, without any additional terms or conditions. | |||
| Notwithstanding the above, nothing herein shall supersede or modify | |||
| the terms of any separate license agreement you may have executed | |||
| with Licensor regarding such Contributions. | |||
| 6. Trademarks. This License does not grant permission to use the trade | |||
| names, trademarks, service marks, or product names of the Licensor, | |||
| except as required for reasonable and customary use in describing the | |||
| origin of the Work and reproducing the content of the NOTICE file. | |||
| 7. Disclaimer of Warranty. Unless required by applicable law or | |||
| agreed to in writing, Licensor provides the Work (and each | |||
| Contributor provides its Contributions) on an "AS IS" BASIS, | |||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | |||
| implied, including, without limitation, any warranties or conditions | |||
| of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A | |||
| PARTICULAR PURPOSE. You are solely responsible for determining the | |||
| appropriateness of using or redistributing the Work and assume any | |||
| risks associated with Your exercise of permissions under this License. | |||
| 8. Limitation of Liability. In no event and under no legal theory, | |||
| whether in tort (including negligence), contract, or otherwise, | |||
| unless required by applicable law (such as deliberate and grossly | |||
| negligent acts) or agreed to in writing, shall any Contributor be | |||
| liable to You for damages, including any direct, indirect, special, | |||
| incidental, or consequential damages of any character arising as a | |||
| result of this License or out of the use or inability to use the | |||
| Work (including but not limited to damages for loss of goodwill, | |||
| work stoppage, computer failure or malfunction, or any and all | |||
| other commercial damages or losses), even if such Contributor | |||
| has been advised of the possibility of such damages. | |||
| 9. Accepting Warranty or Additional Liability. While redistributing | |||
| the Work or Derivative Works thereof, You may choose to offer, | |||
| and charge a fee for, acceptance of support, warranty, indemnity, | |||
| or other liability obligations and/or rights consistent with this | |||
| License. However, in accepting such obligations, You may act only | |||
| on Your own behalf and on Your sole responsibility, not on behalf | |||
| of any other Contributor, and only if You agree to indemnify, | |||
| defend, and hold each Contributor harmless for any liability | |||
| incurred by, or claims asserted against, such Contributor by reason | |||
| of your accepting any such warranty or additional liability. | |||
| END OF TERMS AND CONDITIONS | |||
| APPENDIX: How to apply the Apache License to your work. | |||
| To apply the Apache License to your work, attach the following | |||
| boilerplate notice, with the fields enclosed by brackets "[]" | |||
| replaced with your own identifying information. (Don't include | |||
| the brackets!) The text should be enclosed in the appropriate | |||
| comment syntax for the file format. We also recommend that a | |||
| file or class name and description of purpose be included on the | |||
| same "printed page" as the copyright notice for easier | |||
| identification within third-party archives. | |||
| Copyright [yyyy] [name of copyright owner] | |||
| Licensed under the Apache License, Version 2.0 (the "License"); | |||
| you may not use this file except in compliance with the License. | |||
| You may obtain a copy of the License at | |||
| http://www.apache.org/licenses/LICENSE-2.0 | |||
| Unless required by applicable law or agreed to in writing, software | |||
| distributed under the License is distributed on an "AS IS" BASIS, | |||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| See the License for the specific language governing permissions and | |||
| limitations under the License. | |||
| @@ -0,0 +1,619 @@ | |||
| [TOC] | |||
| #JD区块链 0.8.3-SNAPSHOT | |||
| ------------------------------------------------------------------------ | |||
| ### 版本修订历史 | |||
| <table> | |||
| <tr> | |||
| <th>版本号</th> | |||
| <th>作 者</th> | |||
| <th>修改日期</th> | |||
| <th>备 注</th> | |||
| </tr> | |||
| <tr> | |||
| <td>0.0.6</td> | |||
| <td>黄海泉</td> | |||
| <td>2017-11-10</td> | |||
| <td> | |||
| 定义JD区块链项目的目标与关键能力;<br> | |||
| 定义JD区块链的核心对象模型;<br> | |||
| 定义“账户”的生成算法和“区块/交易/操作/账户”的关键属性;<br> | |||
| 描述了编程接口的示例代码; | |||
| </td> | |||
| </tr> | |||
| <tr> | |||
| <td>0.0.7</td> | |||
| <td>黄海泉</td> | |||
| <td>2017-11-17</td> | |||
| <td> | |||
| 丰富了对“节点共识”、“节点分区”两项关键能力的详细描述; | |||
| </td> | |||
| </tr> | |||
| <tr> | |||
| <td>0.0.8</td> | |||
| <td>黄海泉</td> | |||
| <td>2018-07-17</td> | |||
| <td> | |||
| 增加部署图;增加智能合约开发的示例; | |||
| </td> | |||
| </tr> | |||
| </table> | |||
| ------------------------------------------------------------------------ | |||
| ## 一、概述 | |||
| JD区块链项目的目标是提供一个面向广泛的应用场景、满足企业核心需求的灵活和易用的区块链系统。 | |||
| 以下是 JD 区块链用以满足企业核心需求的关键能力,也是显著区别于其它区块链的重要特征: | |||
| - 快速共识 | |||
| - 节点分区 | |||
| - 并行多账本 | |||
| - 大数据伸缩存储 | |||
| - 条件检索 | |||
| - 面向对象的合约代码编程模型 | |||
| - 节点快速部署 | |||
| - 多终端灵活接入 | |||
| - 分布式自治的账户权限管理模型 | |||
| JD区块链对于关键能力的定义是建立在深入理解和抽象各种多样化需求的基础上的。 | |||
| - 快速共识(Efficient Consensus) | |||
| 我们认为,“快速”不仅仅体现在“用更短时间”达成共识,还要体现在交易(Transaction)要得到可靠地执行。 | |||
| 需要在算法和实现层面做出保障,确保所有提交的合法的交易会被系统在一个“确定性”和“足够短”的时间之内被严格地执行,系统不主动作出随机性地丢弃(不包括系统故障因素)。注:POW类算法产生的链分叉处理是一种系统随机性地丢弃交易的行为。 | |||
| 从使用者的视角来看,这种能力就是区块链系统的“可靠性”,这对于企业、金融场景而言尤其重要。 | |||
| - 节点分区(Peer Partition) | |||
| “分区”是一种分布式系统架构原则,通过将大范围目标按照某种相似特征分隔为一个个小范围目标,分别进行更高效地处理,这能从整体上提升整个系统的处理能力。 | |||
| 区块链系统也是一种分布式系统,沿用“分区”的思想这点来自以往的系统架构实践的经验,是有可能让区块链系统获得可媲美现有系统的处理能力。这种能力将可以无障碍地把区块链在应用于广泛的企业场景中。 | |||
| 在此,我们所说的“节点分区(Peer Partition)” 是共识过程中的物理通讯层面的分区,在共识过程中只有相关的物理节点才会建立通讯链路并复制和存储共识的状态数据。在一个区块链系统中,可以从节点全集中选择一个或多个节点子集,分别组成一个或多个节点分区(Peer Partition) | |||
| - 并行多账本 | |||
| 账本(Ledger) | |||
| - 大数据伸缩存储 | |||
| - 条件检索 | |||
| - 面向对象的合约代码编程模型 | |||
| - 节点快速部署 | |||
| - 多终端灵活接入 | |||
| - 分布式自治的账户权限管理模型 | |||
| ## 二、对象模型 | |||
| JD区块链的核心对象包括: | |||
| - 账本(Ledger) | |||
| 一份账本(Ledger)实质上是由两部分组成: | |||
| - 一组以“账户(Account)”表示的状态数据; | |||
| - 以“区块的链条(Block-chain)”表示的状态数据的变更历史; | |||
| JD区块链的“账本(Ledger)”是一个最顶层的管理数据的逻辑单元。在一个区块链节点构成的共识网络中,可以维护多套并行的“账本(Ledger)”。 | |||
| - 账户(Account) | |||
| - 在JD区块链中,账户(Account)被设计为包含“身份(Identity)”、“权限(Privilege)”、“状态(State)”、“控制规则(Control Rule)” 这4种属性的对象。 | |||
| - 其中,“身份(Identity)”、“权限(Privilege)”这两种属性是一个面向应用的系统的基本功能; | |||
| - “状态(State)”、“控制规则(Control Rule)” 这两种属性这是一个具备“图灵完备性”的系统的基本属性,使系统可以处理任意多样化的任务; | |||
| - 在这里,“身份(Identity)”是一个抽象表述,其形式上就是一个“区块链地址(Address)”和相应的“非对称秘钥钥对(KeyPair)”/证书。 简单来说,一个“账户(Account)”就是一个区块链地址、公私钥对以及一套的权限配置. | |||
| - 一个“账户(Account)”的“状态(State)”、“控制规则(Control Rule)” 这2种属性则是可选的,这提供了不同于其它区块链的账户/合约的一种新的使用方式,即一种数据和逻辑分离的应用设计思路(这在传统的web应用编程中被称为“贫血模型”)。同时,也意味着一个特定“账户”中的数据状态是可以跨账户/合约代码进行共享访问的。 | |||
| - 从应用的视角来看,对“账户”的使用方式可以有几种模式: | |||
| - 用于表示业务角色/用户: | |||
| - 用于表示业务数据:仅有“状态(State)”没有“控制规则(Control Rule)”的账户,可称为数据账户,就有些类似于关系数据库中的“表(Table)”的作用,不同业务类别的数据则使用不同的账户来管理。 | |||
| - 用于表示业务逻辑:仅有“控制规则(Control Rule)”没有“状态(State)”的账户,即所谓的合约账户,可表示某种用于处理数据的通用逻辑,可被授权访问特定的数据账户。 | |||
| - 区块(Block) | |||
| 在概念上,与通常所说的区块的概念是一致的。 | |||
| 在实现上,一个区块的主要内容是包含了某一时刻提交的所有交易以及交易执行之后的状态数据的快照的hash,而不存储具体的交易操作和状态数据。 | |||
| - 交易(Transaction) | |||
| 在概念上,与通常所说的Transaction的概念是一致的,表示一组需要原子执行的操作。 | |||
| - 操作(Operation) | |||
| 操作是针对“账户(Account)”的“写”指令,包括以下几类: | |||
| - 注册账户 | |||
| - 更新账户的状态数据 | |||
| - 更新账户的合约代码定义 | |||
| - 调用账户的合约代码 | |||
| - 合约代码(Contract Code) | |||
| 合约代码是一段用于对“账户(Account)”的状态数据执行操作的代码程序。 | |||
| ## 三、部署模型 | |||
| 1. 总体部署 | |||
|  | |||
| 2. 系统组件 | |||
| - 共识节点 | |||
| - 复制节点 | |||
| - SDK | |||
| - 网关 | |||
| - 终端 | |||
| 3. 配置和管理 | |||
| ## 四、账本结构 | |||
| ### 1. 账户生成算法 | |||
| - 公私钥对 | |||
| - 算法:默认ED25519 ,支持 SM2、CA; | |||
| - 公钥存储格式:版本 + 算法标识 + 公私钥原始内容 + 校验码 | |||
| - 字符编码方式:Base64 | |||
| - 地址 | |||
| - 算法 | |||
| - 给定公钥 P (或由私钥 R 算出公钥 P) | |||
| - 中间值 H1 = SHA256( P ) | |||
| - 中间值 H2 = RIPEMD-160( H1 ) | |||
| - 中间值 X = 版本 + 公钥算法标识 + H2 | |||
| - 校验和 C = 前4字节( SHA256( SHA256( X )) ) | |||
| - 地址 Address = Base58( X + C ) | |||
| ### 2. 区块 | |||
| <table> | |||
| <tr> | |||
| <th>属性</th> | |||
| <th>名称</th> | |||
| <th>说明</th> | |||
| </tr> | |||
| <tr> | |||
| <td>BlockHash</td> | |||
| <td>当前区块 hash</td> | |||
| <td>对区块中除此之外的其它所有属性一起进行哈希运算生成</td> | |||
| </tr> | |||
| <tr> | |||
| <td>BlockVersion </td> | |||
| <td>区块版本</td> | |||
| <td>表示区块-交易的属性结构的版本号;</td> | |||
| </tr> | |||
| <tr> | |||
| <td>PreviousBlockHash</td> | |||
| <td>上一区块 hash</td> | |||
| <td></td> | |||
| </tr> | |||
| <tr> | |||
| <td>BlockNumber</td> | |||
| <td>区块高度</td> | |||
| <td>区块高度是一个区块在链中的序号;<br>创始区块的高度为 0,每个新区块的高度依次递增;</td> | |||
| </tr> | |||
| <tr> | |||
| <td>AccountHash</td> | |||
| <td>账户树hash</td> | |||
| <td>账户的 Merkle Tree 根的 hash</td> | |||
| </tr> | |||
| <tr> | |||
| <td>AccountCount</td> | |||
| <td>账户数量</td> | |||
| <td>区块生成时账本中的全部账户的总数</td> | |||
| </tr> | |||
| <tr> | |||
| <td>TxTreeHash</td> | |||
| <td>交易树 hash</td> | |||
| <td>本区块的交易集合的 Merkle Tree 根的 hash</td> | |||
| </tr> | |||
| <tr> | |||
| <td>TxCount</td> | |||
| <td>区块交易数量</td> | |||
| <td>当前区块包含的交易的数量;</td> | |||
| </tr> | |||
| <tr> | |||
| <td>TxTotalCount</td> | |||
| <td>账本交易总数</td> | |||
| <td>截止到当前区块为止当前账本的所有交易的总数量;</td> | |||
| </tr> | |||
| <tr> | |||
| <td>CloseTime</td> | |||
| <td>区块关闭时间</td> | |||
| <td>生成当前区块时的区块链节点的网络时间;</td> | |||
| </tr> | |||
| </table> | |||
| ### 3. 交易 | |||
| <table> | |||
| <tr> | |||
| <th>属性</th> | |||
| <th>名称</th> | |||
| <th>说明</th> | |||
| </tr> | |||
| <tr> | |||
| <td>Hash</td> | |||
| <td>当前交易 hash</td> | |||
| <td>对交易中除此之外的其它所有属性一起进行哈希运算生成</td> | |||
| </tr> | |||
| <tr> | |||
| <td>LedgerNumber</td> | |||
| <td>区块高度</td> | |||
| <td>交易被包含的区块高度</td> | |||
| </tr> | |||
| <tr> | |||
| <td>BlobHash</td> | |||
| <td>交易数据块hash</td> | |||
| <td>交易的数据块是交易的原始数据,包含客户端提交的交易的全部操作及其参数; | |||
| <br>交易的参与者需要使用私钥对交易数据块进行签名;</td> | |||
| </tr> | |||
| <tr> | |||
| <td>Operations</td> | |||
| <td>操作列表</td> | |||
| <td>交易的操作列表;</td> | |||
| </tr> | |||
| <tr> | |||
| <td>Sponsor</td> | |||
| <td>交易发起人</td> | |||
| <td>交易发起人的账户地址;</td> | |||
| </tr> | |||
| <tr> | |||
| <td>SequenceNumber</td> | |||
| <td>交易序号</td> | |||
| <td>交易序号记录了一个特定的发起人的交易的顺序号,等同于该发起人历史上发起的交易的总数;</td> | |||
| </tr> | |||
| <tr> | |||
| <td>Signatures</td> | |||
| <td>签名列表</td> | |||
| <td>由交易发起人和其它参与者对交易数据块的签名的列表;</td> | |||
| </tr> | |||
| <tr> | |||
| <td>Result</td> | |||
| <td>交易结果</td> | |||
| <td>0 - 表示执行成功;非零表示执行失败;<br>注:最终的账本只包含成功的交易;</td> | |||
| </tr> | |||
| </table> | |||
| ### 4. 操作 | |||
| <table> | |||
| <tr> | |||
| <th>属性</th> | |||
| <th>名称</th> | |||
| <th>说明</th> | |||
| </tr> | |||
| <tr> | |||
| <td>OpType</td> | |||
| <td>操作类型</td> | |||
| <td> | |||
| 一级操作类型包括:注册账户、配置权限、写入键值数据、写入对象数据、定义合约代码、调用合约代码;<br> | |||
| “键值数据写入”操作的子操作类型包括:填入键值、移除键、数值增加、数值减少;<br> | |||
| “对象数据写入”操作的自操作类型包括:插入对象、更新对象、移除对象; | |||
| </td> | |||
| </tr> | |||
| <tr> | |||
| <td>Args</td> | |||
| <td>参数列表</td> | |||
| <td>与操作类型相对应的参数列表;</td> | |||
| </tr> | |||
| <tr> | |||
| <td>SubOps</td> | |||
| <td>子操作列表</td> | |||
| <td>“子操作”是“操作”的递归定义,由“操作类型”来标识;</td> | |||
| </tr> | |||
| </table> | |||
| ### 5. 账户 | |||
| <table> | |||
| <tr> | |||
| <th>属性</th> | |||
| <th>名称</th> | |||
| <th>说明</th> | |||
| </tr> | |||
| <tr> | |||
| <td>Address</td> | |||
| <td>地址</td> | |||
| <td>账户的唯一标识</td> | |||
| </tr> | |||
| <tr> | |||
| <td>RegNumber</td> | |||
| <td>注册号</td> | |||
| <td>账户被注册到区块链的区块高度;</td> | |||
| </tr> | |||
| <tr> | |||
| <td>TxSquenceNumber</td> | |||
| <td>交易序列号</td> | |||
| <td>由账户发起的交易的序列号,初始为 0,账户每发起一个交易则增加1;</td> | |||
| </tr> | |||
| <tr> | |||
| <td>ModelVersion</td> | |||
| <td>账户模型版本</td> | |||
| <td>表示构成一个账户结构的属性模型的程序版本号;</td> | |||
| </tr> | |||
| <tr> | |||
| <td>Version</td> | |||
| <td>账户版本</td> | |||
| <td>初始为 0,对账户的每一次变更(包括对权限设置、状态和合约代码的变更)都会使账户状态版本增加 1 ;<br>注:交易序号的改变不会导致账户版本的增加;</td> | |||
| </tr> | |||
| <tr> | |||
| <td>PrivilegeHash</td> | |||
| <td>权限 hash</td> | |||
| <td>权限树的根hash;</td> | |||
| </tr> | |||
| <tr> | |||
| <td>PrivilegeVersion</td> | |||
| <td>权限版本</td> | |||
| <td>初始为 0, 每次对权限的变更都导致版本号加 1;</td> | |||
| </tr> | |||
| <tr> | |||
| <td>StateType</td> | |||
| <td>状态类型</td> | |||
| <td>账户的状态类型有3种:空类型(NIL);键值类型;对象类型;</td> | |||
| </tr> | |||
| <tr> | |||
| <td>StateVersion</td> | |||
| <td>状态版本</td> | |||
| <td>账户的状态类型有3种:空类型(NIL);键值类型;对象类型;</td> | |||
| </tr> | |||
| <tr> | |||
| <td>StateHash</td> | |||
| <td>状态哈希</td> | |||
| <td>数据状态的 merkle tree 的根hash;</td> | |||
| </tr> | |||
| <tr> | |||
| <td>CodeHash</td> | |||
| <td>合约代码哈希</td> | |||
| <td>由“账户地址+合约代码版本号+合约代码内容”生成的哈希;</td> | |||
| </tr> | |||
| <tr> | |||
| <td>CodeVersion</td> | |||
| <td>代码版本</td> | |||
| <td>初始为 0,每次对代码的变更都使版本加 1 ;</td> | |||
| </tr> | |||
| </table> | |||
| ## 五、编程接口 | |||
| ### 1. 服务连接 | |||
| ```java | |||
| //创建服务代理 | |||
| public static BlockchainKeyPair CLIENT_CERT = BlockchainKeyGenerator.getInstance().generate(); | |||
| final String GATEWAY_IP = "127.0.0.1"; | |||
| final int GATEWAY_PORT = 80; | |||
| final boolean SECURE = false; | |||
| GatewayServiceFactory serviceFactory = GatewayServiceFactory.connect(GATEWAY_IP, GATEWAY_PORT, SECURE, | |||
| CLIENT_CERT); | |||
| // 创建服务代理; | |||
| BlockchainService service = serviceFactory.getBlockchainService(); | |||
| ``` | |||
| ### 2. 用户注册 | |||
| ```java | |||
| // 创建服务代理; | |||
| BlockchainService service = serviceFactory.getBlockchainService(); | |||
| // 在本地定义注册账号的 TX; | |||
| TransactionTemplate txTemp = service.newTransaction(ledgerHash); | |||
| SignatureFunction signatureFunction = asymmetricCryptography.getSignatureFunction(CryptoAlgorithm.ED25519); | |||
| CryptoKeyPair cryptoKeyPair = signatureFunction.generateKeyPair(); | |||
| BlockchainKeyPair user = new BlockchainKeyPair(cryptoKeyPair.getPubKey(), cryptoKeyPair.getPrivKey()); | |||
| txTemp.users().register(user.getIdentity()); | |||
| // TX 准备就绪; | |||
| PreparedTransaction prepTx = txTemp.prepare(); | |||
| // 使用私钥进行签名; | |||
| CryptoKeyPair keyPair = getSponsorKey(); | |||
| prepTx.sign(keyPair); | |||
| // 提交交易; | |||
| prepTx.commit(); | |||
| ``` | |||
| ### 3. 数据账户注册 | |||
| ```java | |||
| // 创建服务代理; | |||
| BlockchainService service = serviceFactory.getBlockchainService(); | |||
| // 在本地定义注册账号的 TX; | |||
| TransactionTemplate txTemp = service.newTransaction(ledgerHash); | |||
| SignatureFunction signatureFunction = asymmetricCryptography.getSignatureFunction(CryptoAlgorithm.ED25519); | |||
| CryptoKeyPair cryptoKeyPair = signatureFunction.generateKeyPair(); | |||
| BlockchainKeyPair dataAccount = new BlockchainKeyPair(cryptoKeyPair.getPubKey(), cryptoKeyPair.getPrivKey()); | |||
| txTemp.dataAccounts().register(dataAccount.getIdentity()); | |||
| // TX 准备就绪; | |||
| PreparedTransaction prepTx = txTemp.prepare(); | |||
| // 使用私钥进行签名; | |||
| CryptoKeyPair keyPair = getSponsorKey(); | |||
| prepTx.sign(keyPair); | |||
| // 提交交易; | |||
| prepTx.commit(); | |||
| ``` | |||
| ### 4. 写入数据 | |||
| ```java | |||
| // 创建服务代理; | |||
| BlockchainService service = serviceFactory.getBlockchainService(); | |||
| HashDigest ledgerHash = getLedgerHash(); | |||
| // 在本地定义注册账号的 TX; | |||
| TransactionTemplate txTemp = service.newTransaction(ledgerHash); | |||
| // -------------------------------------- | |||
| // 将商品信息写入到指定的账户中; | |||
| // 对象将被序列化为 JSON 形式存储,并基于 JSON 结构建立查询索引; | |||
| String commodityDataAccount = "GGhhreGeasdfasfUUfehf9932lkae99ds66jf=="; | |||
| Commodity commodity1 = new Commodity(); | |||
| txTemp.dataAccount(commodityDataAccount).set("ASSET_CODE", commodity1.getCode().getBytes(), -1); | |||
| // TX 准备就绪; | |||
| PreparedTransaction prepTx = txTemp.prepare(); | |||
| String txHash = ByteArray.toBase64(prepTx.getHash().toBytes()); | |||
| // 使用私钥进行签名; | |||
| CryptoKeyPair keyPair = getSponsorKey(); | |||
| prepTx.sign(keyPair); | |||
| // 提交交易; | |||
| prepTx.commit(); | |||
| ``` | |||
| ### 5. 查询数据 | |||
| > 注:详细的查询可参考模块sdk-samples中SDK_GateWay_Query_Test_相关测试用例 | |||
| ```java | |||
| // 创建服务代理; | |||
| BlockchainService service = serviceFactory.getBlockchainService(); | |||
| // 查询区块信息; | |||
| // 区块高度; | |||
| long ledgerNumber = service.getLedger(LEDGER_HASH).getLatestBlockHeight(); | |||
| // 最新区块; | |||
| LedgerBlock latestBlock = service.getBlock(LEDGER_HASH, ledgerNumber); | |||
| // 区块中的交易的数量; | |||
| long txCount = service.getTransactionCount(LEDGER_HASH, latestBlock.getHash()); | |||
| // 获取交易列表; | |||
| LedgerTransaction[] txList = service.getTransactions(LEDGER_HASH, ledgerNumber, 0, 100); | |||
| // 遍历交易列表 | |||
| for (LedgerTransaction ledgerTransaction : txList) { | |||
| TransactionContent txContent = ledgerTransaction.getTransactionContent(); | |||
| Operation[] operations = txContent.getOperations(); | |||
| if (operations != null && operations.length > 0) { | |||
| for (Operation operation : operations) { | |||
| operation = ClientOperationUtil.read(operation); | |||
| // 操作类型:数据账户注册操作 | |||
| if (operation instanceof DataAccountRegisterOperation) { | |||
| DataAccountRegisterOperation daro = (DataAccountRegisterOperation) operation; | |||
| BlockchainIdentity blockchainIdentity = daro.getAccountID(); | |||
| } | |||
| // 操作类型:用户注册操作 | |||
| else if (operation instanceof UserRegisterOperation) { | |||
| UserRegisterOperation uro = (UserRegisterOperation) operation; | |||
| BlockchainIdentity blockchainIdentity = uro.getUserID(); | |||
| } | |||
| // 操作类型:账本注册操作 | |||
| else if (operation instanceof LedgerInitOperation) { | |||
| LedgerInitOperation ledgerInitOperation = (LedgerInitOperation)operation; | |||
| LedgerInitSetting ledgerInitSetting = ledgerInitOperation.getInitSetting(); | |||
| ParticipantNode[] participantNodes = ledgerInitSetting.getConsensusParticipants(); | |||
| } | |||
| // 操作类型:合约发布操作 | |||
| else if (operation instanceof ContractCodeDeployOperation) { | |||
| ContractCodeDeployOperation ccdo = (ContractCodeDeployOperation) operation; | |||
| BlockchainIdentity blockchainIdentity = ccdo.getContractID(); | |||
| } | |||
| // 操作类型:合约执行操作 | |||
| else if (operation instanceof ContractEventSendOperation) { | |||
| ContractEventSendOperation ceso = (ContractEventSendOperation) operation; | |||
| } | |||
| // 操作类型:KV存储操作 | |||
| else if (operation instanceof DataAccountKVSetOperation) { | |||
| DataAccountKVSetOperation.KVWriteEntry[] kvWriteEntries = | |||
| ((DataAccountKVSetOperation) operation).getWriteSet(); | |||
| if (kvWriteEntries != null && kvWriteEntries.length > 0) { | |||
| for (DataAccountKVSetOperation.KVWriteEntry kvWriteEntry : kvWriteEntries) { | |||
| BytesValue bytesValue = kvWriteEntry.getValue(); | |||
| DataType dataType = bytesValue.getType(); | |||
| Object showVal = ClientOperationUtil.readValueByBytesValue(bytesValue); | |||
| System.out.println("writeSet.key=" + kvWriteEntry.getKey()); | |||
| System.out.println("writeSet.value=" + showVal); | |||
| System.out.println("writeSet.type=" + dataType); | |||
| System.out.println("writeSet.version=" + kvWriteEntry.getExpectedVersion()); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| } | |||
| } | |||
| // 根据交易的 hash 获得交易;注:客户端生成 PrepareTransaction 时得到交易hash; | |||
| HashDigest txHash = txList[0].getTransactionContent().getHash(); | |||
| Transaction tx = service.getTransactionByContentHash(LEDGER_HASH, txHash); | |||
| // 获取数据; | |||
| String commerceAccount = "GGhhreGeasdfasfUUfehf9932lkae99ds66jf=="; | |||
| String[] objKeys = new String[] { "x001", "x002" }; | |||
| KVDataEntry[] kvData = service.getDataEntries(LEDGER_HASH, commerceAccount, objKeys); | |||
| long payloadVersion = kvData[0].getVersion(); | |||
| // 获取数据账户下所有的KV列表 | |||
| KVDataEntry[] kvData = service.getDataEntries(ledgerHash, commerceAccount, 0, 100); | |||
| if (kvData != null && kvData.length > 0) { | |||
| for (KVDataEntry kvDatum : kvData) { | |||
| System.out.println("kvData.key=" + kvDatum.getKey()); | |||
| System.out.println("kvData.version=" + kvDatum.getVersion()); | |||
| System.out.println("kvData.type=" + kvDatum.getType()); | |||
| System.out.println("kvData.value=" + kvDatum.getValue()); | |||
| } | |||
| } | |||
| ``` | |||
| ### 6. 合约发布 | |||
| ```java | |||
| // 创建服务代理; | |||
| BlockchainService service = serviceFactory.getBlockchainService(); | |||
| // 在本地定义TX模板 | |||
| TransactionTemplate txTemp = service.newTransaction(ledgerHash); | |||
| // 合约内容读取 | |||
| byte[] contractBytes = FileUtils.readBytes(new File(CONTRACT_FILE)); | |||
| // 生成用户 | |||
| BlockchainIdentityData blockchainIdentity = new BlockchainIdentityData(getSponsorKey().getPubKey()); | |||
| // 发布合约 | |||
| txTemp.contracts().deploy(blockchainIdentity, contractBytes); | |||
| // TX 准备就绪; | |||
| PreparedTransaction prepTx = txTemp.prepare(); | |||
| // 使用私钥进行签名; | |||
| CryptoKeyPair keyPair = getSponsorKey(); | |||
| prepTx.sign(keyPair); | |||
| // 提交交易; | |||
| TransactionResponse transactionResponse = prepTx.commit(); | |||
| assertTrue(transactionResponse.isSuccess()); | |||
| // 打印合约地址 | |||
| System.out.println(blockchainIdentity.getAddress().toBase58()); | |||
| ``` | |||
| ### 7. 合约执行 | |||
| ```java | |||
| // 创建服务代理; | |||
| BlockchainService service = serviceFactory.getBlockchainService(); | |||
| // 在本地定义TX模板 | |||
| TransactionTemplate txTemp = service.newTransaction(ledgerHash); | |||
| // 合约地址 | |||
| String contractAddressBase58 = ""; | |||
| // Event | |||
| String event = ""; | |||
| // args(注意参数的格式) | |||
| byte[] args = "20##30##abc".getBytes(); | |||
| // 提交合约执行代码 | |||
| txTemp.contractEvents().send(contractAddressBase58, event, args); | |||
| // TX 准备就绪; | |||
| PreparedTransaction prepTx = txTemp.prepare(); | |||
| // 生成私钥并使用私钥进行签名; | |||
| CryptoKeyPair keyPair = getSponsorKey(); | |||
| prepTx.sign(keyPair); | |||
| // 提交交易; | |||
| TransactionResponse transactionResponse = prepTx.commit(); | |||
| assertTrue(transactionResponse.isSuccess()); | |||
| ``` | |||
| @@ -0,0 +1,11 @@ | |||
| <project xmlns="http://maven.apache.org/POM/4.0.0" | |||
| xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||
| xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |||
| <modelVersion>4.0.0</modelVersion> | |||
| <parent> | |||
| <groupId>com.jd.blockchain</groupId> | |||
| <artifactId>jdchain-root</artifactId> | |||
| <version>0.8.2.RELEASE</version> | |||
| </parent> | |||
| <artifactId>base</artifactId> | |||
| </project> | |||
| @@ -0,0 +1,148 @@ | |||
| package com.jd.blockchain.base.data; | |||
| /** | |||
| * @author huanghaiquan | |||
| * | |||
| */ | |||
| public interface TypeCodes { | |||
| public static final int BYTES_VALUE = 0x80; | |||
| public static final int BLOCK_CHAIN_IDENTITY = 0x90; | |||
| public static final int BLOCK = 0x100; | |||
| public static final int BLOCK_BODY = 0x110; | |||
| // public static final int BLOCK_LEDGER = 0x110; | |||
| public static final int BLOCK_GENESIS = 0x120; | |||
| public static final int DATA_SNAPSHOT = 0x130; | |||
| public static final int TX = 0x200; | |||
| public static final int TX_LEDGER = 0x201; | |||
| public static final int TX_CONTENT = 0x210; | |||
| public static final int TX_CONTENT_BODY = 0x220; | |||
| public static final int TX_OP = 0x300; | |||
| public static final int TX_OP_LEDGER_INIT = 0x301; | |||
| public static final int TX_OP_USER_REG = 0x310; | |||
| public static final int TX_OP_USER_INFO_SET = 0x311; | |||
| public static final int TX_OP_USER_INFO_SET_KV = 0x312; | |||
| public static final int TX_OP_DATA_ACC_REG = 0x320; | |||
| public static final int TX_OP_DATA_ACC_SET = 0x321; | |||
| public static final int TX_OP_DATA_ACC_SET_KV = 0x322; | |||
| public static final int TX_OP_CONTRACT_DEPLOY = 0x330; | |||
| public static final int TX_OP_CONTRACT_UPDATE = 0x331; | |||
| public static final int TX_OP_CONTRACT_EVENT_SEND = 0x340; | |||
| public static final int TX_RESPONSE = 0x350; | |||
| public static final int METADATA = 0x600; | |||
| public static final int METADATA_INIT_SETTING = 0x610; | |||
| public static final int METADATA_INIT_PERMISSION = 0x611; | |||
| public static final int METADATA_INIT_DECISION = 0x612; | |||
| public static final int METADATA_LEDGER_SETTING = 0x620; | |||
| public static final int METADATA_CONSENSUS_PARTICIPANT = 0x621; | |||
| // public static final int METADATA_CONSENSUS_NODE = 0x630; | |||
| public static final int METADATA_CONSENSUS_SETTING = 0x631; | |||
| // public static final int METADATA_PARTICIPANT_INFO = 0x640; | |||
| public static final int METADATA_CRYPTO_SETTING = 0x642; | |||
| // public static final int ACCOUNT = 0x700; | |||
| public static final int ACCOUNT_HEADER = 0x710; | |||
| public static final int USER = 0x800; | |||
| public static final int DATA = 0x900; | |||
| public static final int CONTRACT = 0xA00; | |||
| public static final int HASH = 0xB00; | |||
| public static final int HASH_OBJECT = 0xB10; | |||
| public static final int ENUM_TYPE = 0xB20; | |||
| public static final int ENUM_TYPE_CRYPTO_ALGORITHM = 0xB21; | |||
| public static final int ENUM_TYPE_TRANSACTION_STATE = 0xB22; | |||
| public static final int ENUM_TYPE_DATA_TYPE= 0xB23; | |||
| public static final int DIGITALSIGNATURE = 0xB30; | |||
| public static final int DIGITALSIGNATURE_BODY = 0xB31; | |||
| public static final int CLIENT_IDENTIFICATION = 0xC00; | |||
| public static final int CLIENT_IDENTIFICATIONS = 0xC10; | |||
| public static final int REQUEST = 0xD00; | |||
| public static final int REQUEST_NODE = 0xD10; | |||
| public static final int REQUEST_ENDPOINT = 0xD20; | |||
| // ------------------ 共识相关 ---------------- | |||
| public static final int CONSENSUS = 0x1000; | |||
| public static final int CONSENSUS_ACTION_REQUEST = CONSENSUS | 0x01; | |||
| public static final int CONSENSUS_ACTION_RESPONSE = CONSENSUS | 0x02; | |||
| public static final int CONSENSUS_SETTINGS = CONSENSUS | 0x03; | |||
| public static final int CONSENSUS_NODE_SETTINGS = CONSENSUS | 0x04; | |||
| public static final int CONSENSUS_CLI_INCOMING_SETTINGS = CONSENSUS | 0x05; | |||
| // ------------------ 共识相关(BFTSMART) ---------------- | |||
| public static final int CONSENSUS_BFTSMART = 0x1100; | |||
| public static final int CONSENSUS_BFTSMART_SETTINGS = CONSENSUS_BFTSMART | 0x01; | |||
| public static final int CONSENSUS_BFTSMART_NODE_SETTINGS = CONSENSUS_BFTSMART | 0x02; | |||
| public static final int CONSENSUS_BFTSMART_CLI_INCOMING_SETTINGS = CONSENSUS_BFTSMART | 0x03; | |||
| public static final int CONSENSUS_BFTSMART_BLOCK_SETTINGS = CONSENSUS_BFTSMART | 0x04; | |||
| // ------------------ 共识相关(MSGQUEUE) ---------------- | |||
| public static final int CONSENSUS_MSGQUEUE = 0x1200; | |||
| public static final int CONSENSUS_MSGQUEUE_SETTINGS = CONSENSUS_MSGQUEUE | 0x01; | |||
| public static final int CONSENSUS_MSGQUEUE_NODE_SETTINGS = CONSENSUS_MSGQUEUE | 0x02; | |||
| public static final int CONSENSUS_MSGQUEUE_CLI_INCOMING_SETTINGS = CONSENSUS_MSGQUEUE | 0x03; | |||
| public static final int CONSENSUS_MSGQUEUE_NETWORK_SETTINGS = CONSENSUS_MSGQUEUE | 0x04; | |||
| public static final int CONSENSUS_MSGQUEUE_BLOCK_SETTINGS = CONSENSUS_MSGQUEUE | 0x05; | |||
| } | |||
| @@ -0,0 +1,19 @@ | |||
| <project xmlns="http://maven.apache.org/POM/4.0.0" | |||
| xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||
| xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |||
| <modelVersion>4.0.0</modelVersion> | |||
| <parent> | |||
| <groupId>com.jd.blockchain</groupId> | |||
| <artifactId>jdchain-root</artifactId> | |||
| <version>0.8.2.RELEASE</version> | |||
| </parent> | |||
| <artifactId>binary-proto</artifactId> | |||
| <dependencies> | |||
| <dependency> | |||
| <groupId>com.jd.blockchain</groupId> | |||
| <artifactId>utils-common</artifactId> | |||
| <version>${project.version}</version> | |||
| </dependency> | |||
| </dependencies> | |||
| </project> | |||
| @@ -0,0 +1,61 @@ | |||
| package com.jd.blockchain.binaryproto; | |||
| import java.io.ByteArrayOutputStream; | |||
| import java.io.InputStream; | |||
| import java.io.OutputStream; | |||
| import com.jd.blockchain.binaryproto.impl2.DataContractContext; | |||
| import com.jd.blockchain.binaryproto.impl2.HeaderEncoder; | |||
| import com.jd.blockchain.utils.io.BytesOutputBuffer; | |||
| import com.jd.blockchain.utils.io.BytesSlice; | |||
| import com.jd.blockchain.utils.io.BytesUtils; | |||
| public class BinaryEncodingUtils { | |||
| public static void encode(Object data, Class<?> contractType, OutputStream out) { | |||
| DataContractEncoder encoder = DataContractContext.resolve(contractType); | |||
| if (encoder == null) { | |||
| throw new IllegalArgumentException("Contract Type not exist!--" + contractType.getName()); | |||
| } | |||
| BytesOutputBuffer buffer = new BytesOutputBuffer(); | |||
| encoder.encode(data, buffer); | |||
| buffer.writeTo(out); | |||
| } | |||
| public static byte[] encode(Object data, Class<?> contractType) { | |||
| ByteArrayOutputStream out = new ByteArrayOutputStream(); | |||
| encode(data, contractType, out); | |||
| return out.toByteArray(); | |||
| } | |||
| public static <T> T decode(InputStream in) { | |||
| byte[] bytes = BytesUtils.copyToBytes(in); | |||
| return decode(bytes); | |||
| } | |||
| public static <T> T decode(byte[] dataSegment) { | |||
| BytesSlice bytes = new BytesSlice(dataSegment, 0, dataSegment.length); | |||
| int code = HeaderEncoder.resolveCode(bytes); | |||
| long version = HeaderEncoder.resolveVersion(bytes); | |||
| DataContractEncoder encoder = DataContractContext.ENCODER_LOOKUP.lookup(code, version); | |||
| return encoder.decode(bytes.getInputStream()); | |||
| } | |||
| public static <T> T decodeAs(byte[] dataSegment, Class<T> contractType) { | |||
| DataContractEncoder encoder = DataContractContext.ENCODER_LOOKUP.lookup(contractType); | |||
| if (encoder == null) { | |||
| throw new DataContractException("Contract type is not registered! --" + contractType.toString()); | |||
| } | |||
| BytesSlice bytes = new BytesSlice(dataSegment, 0, dataSegment.length); | |||
| return encoder.decode(bytes.getInputStream()); | |||
| } | |||
| } | |||
| @@ -0,0 +1,80 @@ | |||
| //package com.jd.blockchain.binaryproto; | |||
| // | |||
| //import java.io.ByteArrayOutputStream; | |||
| //import java.io.InputStream; | |||
| //import java.io.OutputStream; | |||
| //import java.util.Arrays; | |||
| //import java.util.Collections; | |||
| //import java.util.List; | |||
| // | |||
| //import my.utils.io.BytesUtils; | |||
| // | |||
| ///** | |||
| // * 二进制数据段的头部; | |||
| // * | |||
| // * @author huanghaiquan | |||
| // * | |||
| // */ | |||
| //public class BinarySegmentHeader { | |||
| // | |||
| // public static final BinarySliceSpec CODE_SLICE_SPEC = new BinarySliceSpec(4, false, "Code", "Data Contract Code"); | |||
| // | |||
| // public static final BinarySliceSpec VERSION_SLICE_SPEC = new BinarySliceSpec(8, false, "Version", "Data Contract Version"); | |||
| // | |||
| // public static final List<BinarySliceSpec> HEADER_SLICES = Collections.unmodifiableList(Arrays.asList(CODE_SLICE_SPEC, VERSION_SLICE_SPEC)); | |||
| // | |||
| // private int code; | |||
| // | |||
| // private long version; | |||
| // | |||
| // public int getCode() { | |||
| // return code; | |||
| // } | |||
| // | |||
| // public long getVersion() { | |||
| // return version; | |||
| // } | |||
| // | |||
| // public BinarySegmentHeader(int code, long version) { | |||
| // this.code = code; | |||
| // this.version = version; | |||
| // } | |||
| // | |||
| // public static int resolveCode(InputStream in) { | |||
| // return BytesUtils.readInt(in); | |||
| // } | |||
| // | |||
| // | |||
| // public static long resolveVersion(InputStream in) { | |||
| // return BytesUtils.readLong(in); | |||
| // } | |||
| // | |||
| // public static BinarySegmentHeader resolveFrom(InputStream in) { | |||
| // int code = resolveCode(in); | |||
| // long version = resolveVersion(in); | |||
| // return new BinarySegmentHeader(code, version); | |||
| // } | |||
| // | |||
| // public static void writeCode(int code, OutputStream out) { | |||
| // BytesUtils.writeInt(code, out); | |||
| // } | |||
| // | |||
| // public static void writeVersion(long version, OutputStream out) { | |||
| // BytesUtils.writeLong(version, out); | |||
| // } | |||
| // | |||
| // public void writeTo(OutputStream out) { | |||
| // writeCode(code, out); | |||
| // writeVersion(version, out); | |||
| // } | |||
| // | |||
| // | |||
| // public byte[] toBytes() { | |||
| // ByteArrayOutputStream out =new ByteArrayOutputStream(); | |||
| // writeTo(out); | |||
| // return out.toByteArray(); | |||
| // } | |||
| // | |||
| // | |||
| // | |||
| //} | |||
| @@ -0,0 +1,82 @@ | |||
| package com.jd.blockchain.binaryproto; | |||
| /** | |||
| * 表示一个二进制数据片段的格式标准; | |||
| * <p> | |||
| * | |||
| * 一个数据契约的实例输出生成的二进制数据段{@link BinarySegmentHeader}是由一系列小的标准化的数据片段组成; | |||
| * | |||
| * @author huanghaiquan | |||
| * | |||
| */ | |||
| public class BinarySliceSpec { | |||
| private boolean repeatable; | |||
| private int length; | |||
| private boolean dynamic; | |||
| private String name; | |||
| private String description; | |||
| /** | |||
| * 是否重复多次;true 表示以一个头部表示接下来的片段将重复的次数; | |||
| * | |||
| * @return | |||
| */ | |||
| public boolean isRepeatable() { | |||
| return repeatable; | |||
| } | |||
| /** | |||
| * 字节长度; | |||
| * | |||
| * @return | |||
| */ | |||
| public int getLength() { | |||
| return length; | |||
| } | |||
| /** | |||
| * 长度是动态扩展的; | |||
| * | |||
| * @return | |||
| */ | |||
| public boolean isDynamic() { | |||
| return dynamic; | |||
| } | |||
| public String getName() { | |||
| return name; | |||
| } | |||
| public String getDescription() { | |||
| return description; | |||
| } | |||
| private BinarySliceSpec(String name, String description, boolean repeatable, int length, boolean dynamic) { | |||
| this.name = name; | |||
| this.description = description; | |||
| this.repeatable = repeatable; | |||
| this.length = length; | |||
| this.dynamic = dynamic; | |||
| } | |||
| public static BinarySliceSpec newFixedSlice(int length, String name, String description) { | |||
| return new BinarySliceSpec(name, description, false, length, false); | |||
| } | |||
| public static BinarySliceSpec newRepeatableFixedSlice(int length, String name, String description) { | |||
| return new BinarySliceSpec(name, description, true, length, false); | |||
| } | |||
| public static BinarySliceSpec newDynamicSlice(String name, String description) { | |||
| return new BinarySliceSpec(name, description, false, -1, true); | |||
| } | |||
| public static BinarySliceSpec newRepeatableDynamicSlice(String name, String description) { | |||
| return new BinarySliceSpec(name, description, true, -1, true); | |||
| } | |||
| } | |||
| @@ -0,0 +1,25 @@ | |||
| package com.jd.blockchain.binaryproto; | |||
| import java.lang.annotation.ElementType; | |||
| import java.lang.annotation.Retention; | |||
| import java.lang.annotation.RetentionPolicy; | |||
| import java.lang.annotation.Target; | |||
| /** | |||
| * Created by zhangshuang3 on 2018/7/19. | |||
| */ | |||
| @Target({ ElementType.METHOD, ElementType.CONSTRUCTOR }) | |||
| @Retention(RetentionPolicy.RUNTIME) | |||
| public @interface DConstructor { | |||
| /** | |||
| * 名称; | |||
| * <p> | |||
| * 默认为属性的名称; | |||
| * | |||
| * @return | |||
| */ | |||
| String name() default ""; | |||
| } | |||
| @@ -0,0 +1,48 @@ | |||
| package com.jd.blockchain.binaryproto; | |||
| import java.lang.annotation.ElementType; | |||
| import java.lang.annotation.Retention; | |||
| import java.lang.annotation.RetentionPolicy; | |||
| import java.lang.annotation.Target; | |||
| /** | |||
| * {@link DataContract} 表示数据契约,用于把一个接口类型声明为一份标准化“数据契约”; | |||
| * <p> | |||
| * “数据契约”的定义由类型编码({@link #code()})和属性列表(由标注 | |||
| * {@link DataField}定义)构成,其中属性列表中有唯一一个“主键字段”, 主键字段的值用于标识数据契约实例的唯一性; | |||
| * <p> | |||
| * “数据契约”通过属性可以引用其它的“数据契约”(由 {@link DataField#refContract()} = true | |||
| * 定义),这样便构成了“数据契约”嵌套定义的关系图; | |||
| * <p> | |||
| * 当对一个“数据契约”进行二进制序列化输出时,从根对象出发,对关系图中的每一个“数据契约”实例都输出为一个单独的二进制数据段{@link BinarySegmentHeader}, | |||
| * | |||
| * 父的数据段中会将子数据段的内容合并输出到对应字段的位置; | |||
| * | |||
| * <p> | |||
| * 在序列化输出数据段时,将按顺序先后输出类型编号({@link #code()})、版本标识、属性值列表;<p> | |||
| * 其中,“版本标识”是根据类型编号和属性列表(由 {@link DataField} 顺序和类型决定,与名称无关) 进行 SHA256 哈希后映射到 64 | |||
| * 位值空间的值,占用 8 字节空间; | |||
| * “版本标识” 用于在反序列化时校验数据格式的版本是否匹配,并允许数据契约升级后多版本数据并存; | |||
| * | |||
| * @author huanghaiquan | |||
| * | |||
| */ | |||
| @Target({ ElementType.TYPE }) | |||
| @Retention(RetentionPolicy.RUNTIME) | |||
| public @interface DataContract { | |||
| /** | |||
| * 类型编号; | |||
| * <p> | |||
| * 不同类型不能声明相同的编号; | |||
| * <p> | |||
| * | |||
| * @return | |||
| */ | |||
| int code(); | |||
| String name() default ""; | |||
| String description() default ""; | |||
| } | |||
| @@ -0,0 +1,47 @@ | |||
| package com.jd.blockchain.binaryproto; | |||
| import com.jd.blockchain.utils.io.BytesInputStream; | |||
| import com.jd.blockchain.utils.io.BytesOutputBuffer; | |||
| import com.jd.blockchain.utils.io.BytesSlice; | |||
| /** | |||
| * 二进制编码器; | |||
| * | |||
| * @author huanghaiquan | |||
| * | |||
| */ | |||
| public interface DataContractEncoder { | |||
| /** | |||
| * 数据契约的格式标准; | |||
| * | |||
| * @return | |||
| */ | |||
| DataSpecification getSepcification(); | |||
| /** | |||
| * 数据契约的接口类型; | |||
| * | |||
| * @return | |||
| */ | |||
| Class<?> getContractType(); | |||
| /** | |||
| * 按照数据格式标准序列化输出指定的数据对象; | |||
| * | |||
| * @param dataContract | |||
| * 数据对象; | |||
| * @param buffer | |||
| * 要写入的缓冲区; | |||
| * @return 返回写入的字节数; | |||
| */ | |||
| int encode(Object dataContract, BytesOutputBuffer buffer); | |||
| /** | |||
| * 按照数据格式标准将指定的二进制输入流反序列化生成数据对象; | |||
| * | |||
| * @param bytesStream | |||
| * @return | |||
| */ | |||
| <T> T decode(BytesInputStream bytesStream); | |||
| } | |||
| @@ -0,0 +1,14 @@ | |||
| package com.jd.blockchain.binaryproto; | |||
| public class DataContractException extends RuntimeException { | |||
| private static final long serialVersionUID = 5069307301932155810L; | |||
| public DataContractException(String message) { | |||
| super(message); | |||
| } | |||
| public DataContractException(String message, Throwable cause) { | |||
| super(message, cause); | |||
| } | |||
| } | |||
| @@ -0,0 +1,29 @@ | |||
| package com.jd.blockchain.binaryproto; | |||
| import com.jd.blockchain.binaryproto.impl2.DataContractContext; | |||
| /** | |||
| * 数据实体注册表; | |||
| * | |||
| * @author huanghaiquan | |||
| * | |||
| */ | |||
| public class DataContractRegistry { | |||
| private DataContractRegistry() { | |||
| } | |||
| public static DataContractEncoder register(Class<?> contractType) { | |||
| DataContractEncoder encoder = DataContractContext.resolve(contractType); | |||
| return encoder; | |||
| } | |||
| public static DataContractEncoder getEncoder(Class<?> contractType) { | |||
| return DataContractContext.ENCODER_LOOKUP.lookup(contractType); | |||
| } | |||
| public static DataContractEncoder getEncoder(int contractCode, long version) { | |||
| return DataContractContext.ENCODER_LOOKUP.lookup(contractCode, version); | |||
| } | |||
| } | |||
| @@ -0,0 +1,109 @@ | |||
| package com.jd.blockchain.binaryproto; | |||
| import java.lang.annotation.ElementType; | |||
| import java.lang.annotation.Retention; | |||
| import java.lang.annotation.RetentionPolicy; | |||
| import java.lang.annotation.Target; | |||
| import com.jd.blockchain.utils.ValueType; | |||
| /** | |||
| * 标记一个接口的字段作为数据契约的字段; | |||
| * <p> | |||
| * | |||
| * 字段的数据类型需要需要显式通过 | |||
| * {@link #primitiveType()}、{@link #refEnum()}、{@link #refContract()} | |||
| * 3个属性之一标注(只能标记一种); | |||
| * | |||
| * @author huanghaiquan | |||
| * | |||
| */ | |||
| @Target({ ElementType.METHOD }) | |||
| @Retention(RetentionPolicy.RUNTIME) | |||
| public @interface DataField { | |||
| /** | |||
| * 字段顺序; | |||
| * <p> | |||
| * | |||
| * 顺序编号实际并不会输出,仅用于对字段进行升序排列; | |||
| * <p> | |||
| * | |||
| * 注:对于已经发布使用的数据契约,都不应该调整顺序,否则会导致无法正确解析已有数据; | |||
| * | |||
| * @return | |||
| */ | |||
| int order(); | |||
| /** | |||
| * 基本数据类型; | |||
| * <p> | |||
| * | |||
| * 如果字段的类型属于 {@link ValueType} 枚举中的基本数据类型,则需要显式指定一种具体的类型; | |||
| * | |||
| * @return | |||
| */ | |||
| ValueType primitiveType() default ValueType.NIL; | |||
| /** | |||
| * 是否是枚举类型; | |||
| * <p> | |||
| * 如果为 true,则属性的声明类型必须是枚举类型,且该枚举类型已经标记 {@link EnumContract}; | |||
| * | |||
| * @return | |||
| */ | |||
| boolean refEnum() default false; | |||
| /** | |||
| * 嵌套的数据契约类型; | |||
| * | |||
| * 如果为 true,则属性的声明类型必须是接口类型,且该类型已经标记了 {@link DataContract}; | |||
| * | |||
| * @return | |||
| */ | |||
| boolean refContract() default false; | |||
| /** | |||
| * 嵌套的契约类型是否根据实际的对象实现的契约接口动态写入; | |||
| * | |||
| * @return | |||
| */ | |||
| boolean genericContract() default false; | |||
| /** | |||
| * 列表; | |||
| * | |||
| * @return | |||
| */ | |||
| boolean list() default false; | |||
| /** | |||
| * 最大长度,单位为“byte” | |||
| * <p> | |||
| * 仅对于文本、字节数组、大整数等相关的数据类型有效(即:{@link ValueType} 枚举中编码大于等于 0x20 | |||
| * {@link ValueType#TEXT}的数据类型); | |||
| * | |||
| * @return | |||
| */ | |||
| int maxSize() default -1; | |||
| /** | |||
| * 名称; | |||
| * <p> | |||
| * 默认为属性的名称; | |||
| * | |||
| * @return | |||
| */ | |||
| String name() default ""; | |||
| /** | |||
| * 关于字段的说明; | |||
| * <p> | |||
| * | |||
| * 说明内容将输出到数据段的数据结构描述文件; | |||
| * | |||
| * @return | |||
| */ | |||
| String decription() default ""; | |||
| } | |||
| @@ -0,0 +1,65 @@ | |||
| package com.jd.blockchain.binaryproto; | |||
| import java.util.List; | |||
| /** | |||
| * {@link DataSpecification} 表示数据契约的格式标准; | |||
| * <p> | |||
| * {@link DataSpecification} 提供了数据契约的格式描述(通过属性 {@link #getCode()}、 | |||
| * {@link #getVersion()}、 | |||
| * {@link #getFields()}),以及二进制数据片段序列的格式描述({@link #getSlices()}); | |||
| * | |||
| * <p> | |||
| * 其中,数据片段列表({@link #getSlices()})反映了数据契约实际输出的二进制序列:<br> | |||
| * 1、首个数据片段是数据契约的类型编码({@link #getCode()});<br> | |||
| * 2、接下来是数据契约的版本({@link #getVersion()});<br> | |||
| * 3、再接下来是与字段列表({@link #getFields()})一一对应的数据分片;<p> | |||
| * | |||
| * <p> | |||
| * | |||
| * | |||
| * @author huanghaiquan | |||
| * | |||
| */ | |||
| public interface DataSpecification { | |||
| /** | |||
| * 数据契约的类型编码; | |||
| * | |||
| * @return | |||
| */ | |||
| int getCode(); | |||
| /** | |||
| * 数据契约的版本; | |||
| * <p> | |||
| * | |||
| * 由类型编码{@link #getCode()}和字段列表{@link #getFields()} 进行哈希生成的 64 位整数; | |||
| * | |||
| * @return | |||
| */ | |||
| long getVersion(); | |||
| String getName(); | |||
| String getDescription(); | |||
| /** | |||
| * 按定义顺序排列的字段格式标准的列表; | |||
| * <p> | |||
| * 字段的顺序由 {@link DataField#order()} 定义; | |||
| * | |||
| * @return | |||
| */ | |||
| List<FieldSpec> getFields(); | |||
| /** | |||
| * 按顺序定义的二进制数据片段的格式标准的列表; | |||
| * | |||
| * @return | |||
| */ | |||
| List<BinarySliceSpec> getSlices(); | |||
| String toHtml(); | |||
| } | |||
| @@ -0,0 +1,26 @@ | |||
| package com.jd.blockchain.binaryproto; | |||
| import java.lang.annotation.ElementType; | |||
| import java.lang.annotation.Retention; | |||
| import java.lang.annotation.RetentionPolicy; | |||
| import java.lang.annotation.Target; | |||
| @Target({ ElementType.TYPE }) | |||
| @Retention(RetentionPolicy.RUNTIME) | |||
| public @interface EnumContract { | |||
| /** | |||
| * 类型编号; | |||
| * <p> | |||
| * | |||
| * 而且,不同类型不能声明相同的编号; <p> | |||
| * | |||
| * @return | |||
| */ | |||
| int code() ; | |||
| String name() default ""; | |||
| String decription() default ""; | |||
| } | |||
| @@ -0,0 +1,25 @@ | |||
| package com.jd.blockchain.binaryproto; | |||
| import java.lang.annotation.ElementType; | |||
| import java.lang.annotation.Retention; | |||
| import java.lang.annotation.RetentionPolicy; | |||
| import java.lang.annotation.Target; | |||
| import com.jd.blockchain.utils.ValueType; | |||
| @Target({ ElementType.FIELD, ElementType.METHOD }) | |||
| @Retention(RetentionPolicy.RUNTIME) | |||
| public @interface EnumField { | |||
| /** | |||
| * 枚举值的类型; | |||
| * | |||
| * <p> | |||
| * 注:只支持 {@link ValueType#INT8} ~ {@link ValueType#INT32} 这几种类型; | |||
| * | |||
| * | |||
| * @return | |||
| */ | |||
| ValueType type(); | |||
| } | |||
| @@ -0,0 +1,24 @@ | |||
| package com.jd.blockchain.binaryproto; | |||
| import java.util.Set; | |||
| import com.jd.blockchain.utils.ValueType; | |||
| public interface EnumSpecification { | |||
| int getCode(); | |||
| String getName(); | |||
| String getDescription(); | |||
| long getVersion(); | |||
| ValueType getValueType(); | |||
| int[] getItemValues(); | |||
| String[] getItemNames(); | |||
| } | |||
| @@ -0,0 +1,25 @@ | |||
| package com.jd.blockchain.binaryproto; | |||
| import java.lang.annotation.ElementType; | |||
| import java.lang.annotation.Retention; | |||
| import java.lang.annotation.RetentionPolicy; | |||
| import java.lang.annotation.Target; | |||
| /** | |||
| * Created by zhangshuang3 on 2018/7/19. | |||
| */ | |||
| @Target({ ElementType.PARAMETER, ElementType.FIELD }) | |||
| @Retention(RetentionPolicy.RUNTIME) | |||
| public @interface FieldSetter { | |||
| /** | |||
| * 名称; | |||
| * <p> | |||
| * 默认为属性的名称; | |||
| * | |||
| * @return | |||
| */ | |||
| String name() default ""; | |||
| String type() default ""; | |||
| } | |||
| @@ -0,0 +1,130 @@ | |||
| package com.jd.blockchain.binaryproto; | |||
| import com.jd.blockchain.utils.ValueType; | |||
| /** | |||
| * 表示数据契约字段的格式标准; | |||
| * | |||
| * @author huanghaiquan | |||
| * | |||
| */ | |||
| public interface FieldSpec { | |||
| // /** | |||
| // * 字段值的类型编码; | |||
| // * <p> | |||
| // * | |||
| // * 该编码是对{@link #getPrimitiveType()}、{@link #getRefEnum()}、{@link #getRefContract()} | |||
| // * 3个属性的联合编码,通过首字节标识出类型为这3中类型中的一种;<br> | |||
| // * 其中:<br> | |||
| // * 1、首字节为 0 表示为基本类型 {@link #getPrimitiveType()};<br> | |||
| // * 2、首字节为 1 表示为枚举类型 {@link #getRefEnum()},紧接其后是枚举类型的编码<br> | |||
| // * 3、首字节为 2 表示为数据契约类型 {@link #getRefContract()};<br> | |||
| // * 4、首字节为 3 表示为基本类型的数组类型 {@link #isRepeatable()} ()};<br> | |||
| // * 5、首字节为 4 表示为PubKey类型 {@link #isRefPubKey()};<br> | |||
| // * 6、首字节为 5 表示为PrivKey类型 {@link #isRefPrivKey()};<br> | |||
| // * 7、首字节为 6 表示为HashDigest类型 {@link #isRefHashDigest()};<br> | |||
| // * 8、首字节为 7 表示为数据契约类型数组 {@link #isList(), @link #getRefContract()};<br> | |||
| // * 9、首字节为 8 表示为BlockChainIdentity数据类型 {@link #isRefIdentity()} | |||
| // * 10、首字节为9 表示为NetworkAddress数据类型 {@link #isRefNetworkAddr()};<br> | |||
| // * @return | |||
| // */ | |||
| // long getTypeCode(); | |||
| /** | |||
| * 字段的值的类型; | |||
| * <p> | |||
| * 如果不是字段的值不是基本类型,则返回 null(即: {@link DataField#primitiveType()} 设置为 | |||
| * {@link ValueType#NIL}); | |||
| * | |||
| * @return | |||
| */ | |||
| ValueType getPrimitiveType(); | |||
| /** | |||
| * 字段的值引用的枚举契约; | |||
| * <p> | |||
| * 如果字段的值不是枚举契约类型,则返回 null; | |||
| * | |||
| * @return | |||
| */ | |||
| EnumSpecification getRefEnum(); | |||
| /** | |||
| * 字段的值引用的数据契约; | |||
| * <p> | |||
| * 如果字段的值不是数据契约类型,则返回 null; | |||
| * | |||
| * @return | |||
| */ | |||
| DataSpecification getRefContract(); | |||
| boolean isRepeatable(); | |||
| // /** | |||
| // * 字段的值引用的PubKey; | |||
| // * <p> | |||
| // * 如果字段的值不是PubKey,则返回 false; | |||
| // * | |||
| // * @return | |||
| // */ | |||
| // boolean isRefPubKey(); | |||
| // /** | |||
| // * 字段的值引用的PrivKey; | |||
| // * <p> | |||
| // * 如果字段的值不是PrivKey,则返回 false; | |||
| // * | |||
| // * @return | |||
| // */ | |||
| // boolean isRefPrivKey(); | |||
| // /** | |||
| // * 字段的值引用的HashDigest; | |||
| // * <p> | |||
| // * 如果字段的值不是HashDigest,则返回 false; | |||
| // * | |||
| // * @return | |||
| // */ | |||
| // boolean isRefHashDigest(); | |||
| // /** | |||
| // * 字段的值引用的SignatureDigest; | |||
| // * <p> | |||
| // * 如果字段的值不是SignatureDigest,则返回 false; | |||
| // * | |||
| // * @return | |||
| // */ | |||
| // boolean isRefSignatureDigest(); | |||
| // /** | |||
| // * 字段的值引用的BlockChainIdentity; | |||
| // * <p> | |||
| // * 如果字段的值不是HashDigest,则返回 false; | |||
| // * | |||
| // * @return | |||
| // */ | |||
| // boolean isRefIdentity(); | |||
| // /** | |||
| // * 字段的值引用的NetworkAddress; | |||
| // * <p> | |||
| // * 如果字段的值不是NetworkAddress,则返回 false; | |||
| // * | |||
| // * @return | |||
| // */ | |||
| // boolean isRefNetworkAddr(); | |||
| /** | |||
| * 最大长度;单位为“byte”; | |||
| * | |||
| * @return | |||
| * @see {@link DataField#maxSize()} | |||
| */ | |||
| int getMaxSize(); | |||
| String getName(); | |||
| String getDescription(); | |||
| /** | |||
| * 是否引用了一个通用数据契约类型,实际的类型需要根据实际的对象实例来定; | |||
| * @return | |||
| */ | |||
| boolean isGenericContract(); | |||
| // Class<?> getContractTypeResolver(); | |||
| } | |||
| @@ -0,0 +1,861 @@ | |||
| //package com.jd.blockchain.binaryproto.impl; | |||
| // | |||
| //import my.utils.ValueType; | |||
| //import my.utils.io.*; | |||
| //import my.utils.net.NetworkAddress; | |||
| // | |||
| //import java.io.IOException; | |||
| //import java.io.InputStream; | |||
| //import java.io.OutputStream; | |||
| //import java.lang.annotation.Annotation; | |||
| //import java.lang.reflect.Constructor; | |||
| //import java.lang.reflect.Field; | |||
| //import java.lang.reflect.InvocationTargetException; | |||
| //import java.lang.reflect.Method; | |||
| //import java.util.*; | |||
| // | |||
| //import com.jd.blockchain.binaryproto.BinaryEncoder; | |||
| //import com.jd.blockchain.binaryproto.ContractTypeResolver; | |||
| //import com.jd.blockchain.binaryproto.DConstructor; | |||
| //import com.jd.blockchain.binaryproto.DataContractException; | |||
| //import com.jd.blockchain.binaryproto.DataContractRegistry; | |||
| //import com.jd.blockchain.binaryproto.DataSpecification; | |||
| //import com.jd.blockchain.binaryproto.EnumSpecification; | |||
| //import com.jd.blockchain.binaryproto.FieldSetter; | |||
| //import com.jd.blockchain.binaryproto.FieldSpec; | |||
| // | |||
| ///** | |||
| // * Created by zhangshuang3 on 2018/6/21. | |||
| // */ | |||
| //public class BinaryEncoderImpl implements BinaryEncoder { | |||
| // private DataSpecification spec; | |||
| // private Class<?> contractType; | |||
| // | |||
| // public BinaryEncoderImpl(DataSpecification spec, Class<?> contractType) { | |||
| // this.spec = spec; | |||
| // this.contractType = contractType; | |||
| // } | |||
| // | |||
| // @Override | |||
| // public DataSpecification getSepcification() { | |||
| // return spec; | |||
| // } | |||
| // | |||
| // @Override | |||
| // public Class<?> getContractType() { | |||
| // return contractType; | |||
| // } | |||
| // | |||
| // public void write(FieldSpec spec, OutputStream out, Object value) { | |||
| // ValueType primitive = spec.getPrimitiveType(); | |||
| // EnumSpecification refEnum = spec.getRefEnum(); | |||
| // DataSpecification refContract = spec.getRefContract(); | |||
| // boolean list = spec.isRepeatable(); | |||
| // boolean refPubKey = spec.isRefPubKey(); | |||
| // boolean refPrivKey = spec.isRefPrivKey(); | |||
| // boolean refHashDigest = spec.isRefHashDigest(); | |||
| // boolean refSignatureDigest = spec.isRefSignatureDigest(); | |||
| // boolean refIdentity = spec.isRefIdentity(); | |||
| // boolean refNetworkAddr = spec.isRefNetworkAddr(); | |||
| // Class<?> contractTypeResolverClass = spec.getContractTypeResolver(); | |||
| // | |||
| // try { | |||
| // // basic data type | |||
| // if ((primitive != ValueType.NIL) && (list == false)) { | |||
| // writePrimitive(value, primitive, out); | |||
| // } | |||
| // // enum type | |||
| // else if (refEnum != null) { | |||
| // writeEnum(value, refEnum, out); | |||
| // } | |||
| // // ref contract | |||
| // else if (refContract != null && list == false) { | |||
| // writeContract(value, refContract, contractTypeResolverClass, out); | |||
| // } | |||
| // // array type | |||
| // else if ((primitive != ValueType.NIL) && (list == true)) { | |||
| // writePrimitiveList(value, primitive, out); | |||
| // } | |||
| // // refcontract array | |||
| // else if (refContract != null && list == true) { | |||
| // writeContractList(value, refContract, contractTypeResolverClass, out); | |||
| // } | |||
| // // HashDigest type | PubKey type | PrivKey type | |||
| // else if (refPubKey == true | refPrivKey == true | refHashDigest == true | refSignatureDigest == true) { | |||
| // writeBytesSerializeObject(value, out); | |||
| // } | |||
| // // BlockChainIdentity type | |||
| // // else if (refIdentity == true) { | |||
| // // if (obj == null) { | |||
| // // BytesEncoding.writeInNormal(null, out); | |||
| // // } | |||
| // // else { | |||
| // // BytesEncoding.writeInNormal(obj.getClass().getName().getBytes(), out); | |||
| // // BytesWriter writer = (BytesWriter)obj; | |||
| // // writer.writeTo(out); | |||
| // // } | |||
| // // } | |||
| // // NetworkAddress type | |||
| // else if (refNetworkAddr == true) { | |||
| // wirteNetworkAddressValue(value, out); | |||
| // } else { | |||
| // throw new IllegalStateException("Unexpected contract field and value!"); | |||
| // } | |||
| // } catch (InstantiationException | IllegalAccessException e) { | |||
| // throw new DataContractException(e.getMessage(), e); | |||
| // } | |||
| // | |||
| // } | |||
| // | |||
| // private void wirteNetworkAddressValue(Object value, OutputStream out) { | |||
| // if (value == null) { | |||
| // BytesEncoding.writeInNormal(null, out); | |||
| // } else { | |||
| // // write host ,port and secure flag | |||
| // NetworkAddress address = (NetworkAddress) value; | |||
| // BytesEncoding.writeInNormal(address.getHost().getBytes(), out); | |||
| // BytesUtils.writeInt(address.getPort(), out); | |||
| // BytesUtils.writeByte((byte) (address.isSecure() ? 1 : 0), out); | |||
| // } | |||
| // } | |||
| // | |||
| // private void writeBytesSerializeObject(Object value, OutputStream out) { | |||
| // if (value == null) { | |||
| // BytesEncoding.writeInNormal(null, out); | |||
| // } else { | |||
| // BytesEncoding.writeInNormal(value.getClass().getName().getBytes(), out); | |||
| // BytesSerializable serializable = (BytesSerializable) value; | |||
| // // refPubKey:1bytes algorithm code , 1byte key type, and others are raw bytes | |||
| // // refPrivKey:1bytes algorithm code , 1byte key type, and others are raw bytes | |||
| // // refHashDigest:1bytes algorithm code , and others are raw bytes | |||
| // BytesEncoding.writeInNormal(serializable.toBytes(), out); | |||
| // } | |||
| // } | |||
| // | |||
| // private void writeContractList(Object value, DataSpecification contractSpeci, Class<?> contractTypeResolverClass, | |||
| // OutputStream out) throws InstantiationException, IllegalAccessException { | |||
| // BinaryEncoder encoder1 = null; | |||
| // Object[] refContractArray = (Object[]) value; | |||
| // if (refContractArray == null) { | |||
| // BytesUtils.writeInt(0, out); | |||
| // } else { | |||
| // BytesUtils.writeInt(refContractArray.length, out); | |||
| // if (contractTypeResolverClass.isInterface() == true) { | |||
| // encoder1 = DataContractRegistry.getEncoder(contractSpeci.getCode(), contractSpeci.getVersion()); | |||
| // if (encoder1 == null) { | |||
| // throw new DataContractException("write: get encoder null error!"); | |||
| // } | |||
| // for (Object ref : refContractArray) { | |||
| // BytesUtils.writeInt(encoder1.getSepcification().getCode(), out); | |||
| // BytesUtils.writeLong(encoder1.getSepcification().getVersion(), out); | |||
| // // record class name | |||
| // BytesEncoding.writeInNormal(ref.getClass().getName().getBytes(), out); | |||
| // encoder1.encode(ref, out); | |||
| // } | |||
| // } else { | |||
| // // TODO: 不必每次都实例化,应该对此实例建立单例缓存; | |||
| // ContractTypeResolver resolver = (ContractTypeResolver) (contractTypeResolverClass.newInstance()); | |||
| // for (Object ref : refContractArray) { | |||
| // Class<?> subContractType = resolver.getContractType(ref, null); | |||
| // encoder1 = DataContractRegistry.register(subContractType); | |||
| // if (encoder1 == null) { | |||
| // throw new DataContractException("write: regist sub contract type failed error!"); | |||
| // } | |||
| // BytesUtils.writeInt(encoder1.getSepcification().getCode(), out); | |||
| // BytesUtils.writeLong(encoder1.getSepcification().getVersion(), out); | |||
| // // record class name | |||
| // BytesEncoding.writeInNormal(ref.getClass().getName().getBytes(), out); | |||
| // encoder1.encode(ref, out); | |||
| // encoder1 = null; | |||
| // } | |||
| // } | |||
| // } | |||
| // } | |||
| // | |||
| // private void writePrimitiveList(Object value, ValueType primitive, OutputStream out) { | |||
| // switch (primitive) { | |||
| // case BOOLEAN: | |||
| // if (value == null) { | |||
| // BytesUtils.writeInt(0, out); | |||
| // break; | |||
| // } | |||
| // | |||
| // boolean[] boolArray = (boolean[]) value; | |||
| // BytesUtils.writeInt(boolArray.length, out); | |||
| // for (boolean i : boolArray) { | |||
| // BytesUtils.writeByte((byte) (i ? 1 : 0), out); | |||
| // } | |||
| // break; | |||
| // case INT8: | |||
| // if (value == null) { | |||
| // BytesUtils.writeInt(0, out); | |||
| // break; | |||
| // } | |||
| // byte[] byteArray = (byte[]) value; | |||
| // BytesUtils.writeInt(byteArray.length, out); | |||
| // for (byte i : byteArray) { | |||
| // BytesUtils.writeByte(i, out); | |||
| // } | |||
| // break; | |||
| // case INT16: | |||
| // if (value == null) { | |||
| // BytesUtils.writeInt(0, out); | |||
| // break; | |||
| // } | |||
| // short[] shortArray = (short[]) value; | |||
| // BytesUtils.writeInt(shortArray.length, out); | |||
| // for (short i : shortArray) { | |||
| // byte[] bytes = BytesUtils.toBytes(i); | |||
| // BytesEncoding.writeInShort(bytes, out); | |||
| // } | |||
| // break; | |||
| // case INT32: | |||
| // if (value == null) { | |||
| // BytesUtils.writeInt(0, out); | |||
| // break; | |||
| // } | |||
| // int[] intArray = (int[]) value; | |||
| // BytesUtils.writeInt(intArray.length, out); | |||
| // for (int i : intArray) { | |||
| // BytesUtils.writeInt(i, out); | |||
| // } | |||
| // break; | |||
| // case INT64: | |||
| // if (value == null) { | |||
| // BytesUtils.writeInt(0, out); | |||
| // break; | |||
| // } | |||
| // long[] longArray = (long[]) value; | |||
| // BytesUtils.writeInt(longArray.length, out); | |||
| // for (long i : longArray) { | |||
| // BytesUtils.writeLong(i, out); | |||
| // } | |||
| // break; | |||
| // case DATETIME: | |||
| // if (value == null) { | |||
| // BytesUtils.writeInt(0, out); | |||
| // break; | |||
| // } | |||
| // Date[] dateArray = (Date[]) value; | |||
| // BytesUtils.writeInt(dateArray.length, out); | |||
| // long elemSeconds; | |||
| // for (Date i : dateArray) { | |||
| // elemSeconds = i.getTime(); | |||
| // BytesUtils.writeLong(elemSeconds, out); | |||
| // } | |||
| // break; | |||
| // case BYTES: | |||
| // if (value == null) { | |||
| // BytesUtils.writeInt(0, out); | |||
| // break; | |||
| // } | |||
| // ByteArray[] byteArrays = (ByteArray[]) value; | |||
| // BytesUtils.writeInt(byteArrays.length, out); | |||
| // for (ByteArray elem : byteArrays) { | |||
| // BytesEncoding.writeInNormal(elem.bytes(), out); | |||
| // } | |||
| // break; | |||
| // case TEXT: | |||
| // case JSON: | |||
| // case XML: | |||
| // case BIG_INT: | |||
| // case IMG: | |||
| // case VIDEO: | |||
| // case LOCATION: | |||
| // if (value == null) { | |||
| // BytesUtils.writeInt(0, out); | |||
| // break; | |||
| // } | |||
| // Object[] dynamicArray = (Object[]) value; | |||
| // BytesUtils.writeInt(dynamicArray.length, out); | |||
| // for (Object i : dynamicArray) { | |||
| // BytesEncoding.writeInNormal(i.toString().getBytes(), out); | |||
| // } | |||
| // break; | |||
| // default: | |||
| // throw new DataContractException("write: array type error!"); | |||
| // } | |||
| // } | |||
| // | |||
| // private void writeContract(Object value, DataSpecification contractSpeci, Class<?> contractTypeResolverClass, | |||
| // OutputStream out) throws InstantiationException, IllegalAccessException { | |||
| // BinaryEncoder encoder = null; | |||
| // if (contractTypeResolverClass.isInterface() == true) { | |||
| // encoder = DataContractRegistry.getEncoder(contractSpeci.getCode(), contractSpeci.getVersion()); | |||
| // if (encoder == null) { | |||
| // throw new DataContractException("write: get encoder null error!"); | |||
| // } | |||
| // } else { | |||
| // // get sub contract type | |||
| // ContractTypeResolver resolver = (ContractTypeResolver) (contractTypeResolverClass.newInstance()); | |||
| // if (resolver == null) { | |||
| // throw new DataContractException("write: newInstance null error!"); | |||
| // } | |||
| // Class<?> subContractType = resolver.getContractType(value, null); | |||
| // encoder = DataContractRegistry.register(subContractType); | |||
| // if (encoder == null) { | |||
| // throw new DataContractException("write: regist sub contract type failed error!"); | |||
| // } | |||
| // } | |||
| // BytesUtils.writeInt(encoder.getSepcification().getCode(), out); | |||
| // BytesUtils.writeLong(encoder.getSepcification().getVersion(), out); | |||
| // if (value == null) { | |||
| // BytesEncoding.writeInNormal(null, out); | |||
| // } else { | |||
| // // record class name | |||
| // BytesEncoding.writeInNormal(value.getClass().getName().getBytes(), out); | |||
| // encoder.encode(value, out); | |||
| // } | |||
| // } | |||
| // | |||
| // private void writeEnum(Object value, EnumSpecification enumType, OutputStream out) { | |||
| // int code = 0; | |||
| // EnumSpecificationImpl refEnumImpl = (EnumSpecificationImpl) enumType; | |||
| // Map<Object, Integer> constant = refEnumImpl.getEnumConstants(); | |||
| // for (Object enumConstant : constant.keySet()) { | |||
| // if (enumConstant.toString().equals(value.toString()) == true) { | |||
| // code = constant.get(enumConstant); | |||
| // break; | |||
| // } | |||
| // } | |||
| // switch (refEnumImpl.getValueType()) { | |||
| // case INT8: | |||
| // BytesUtils.writeByte((byte) (code & 0xff), out); | |||
| // break; | |||
| // case INT16: | |||
| // byte[] bytes = BytesUtils.toBytes((short) code); | |||
| // BytesEncoding.writeInShort(bytes, out); | |||
| // break; | |||
| // case INT32: | |||
| // BytesUtils.writeInt(code, out); | |||
| // break; | |||
| // default: | |||
| // throw new DataContractException("write: enum type error!"); | |||
| // } | |||
| // } | |||
| // | |||
| // private void writePrimitive(Object value, ValueType primitive, OutputStream out) { | |||
| // switch (primitive) { | |||
| // case BOOLEAN: | |||
| // BytesUtils.writeByte((byte) ((boolean) value ? 1 : 0), out); | |||
| // break; | |||
| // case INT8: | |||
| // BytesUtils.writeByte((byte) value, out); | |||
| // break; | |||
| // case INT16: | |||
| // // TODO 可修改为short类型 | |||
| // byte[] bytes = BytesUtils.toBytes((short) value); | |||
| // BytesEncoding.writeInShort(bytes, out); | |||
| // break; | |||
| // case INT32: | |||
| // BytesUtils.writeInt((int) value, out); | |||
| // break; | |||
| // case INT64: | |||
| // BytesUtils.writeLong((long) value, out); | |||
| // break; | |||
| // case DATETIME: | |||
| // long seconds = ((Date) value).getTime(); | |||
| // BytesUtils.writeLong(seconds, out); | |||
| // break; | |||
| // case BYTES: | |||
| // ByteArray byteArray = (ByteArray) value; | |||
| // BytesEncoding.writeInNormal(byteArray.bytes(), out); | |||
| // break; | |||
| // case TEXT: | |||
| // case JSON: | |||
| // case XML: | |||
| // case BIG_INT: | |||
| // case IMG: | |||
| // case VIDEO: | |||
| // case LOCATION: | |||
| // if (value == null) { | |||
| // BytesEncoding.writeInNormal(null, out); | |||
| // } else { | |||
| // BytesEncoding.writeInNormal(value.toString().getBytes(), out); | |||
| // } | |||
| // break; | |||
| // default: | |||
| // throw new DataContractException("write: primitive type error!"); | |||
| // } | |||
| // } | |||
| // | |||
| // @Override | |||
| // public void encode(Object data, OutputStream out) { | |||
| // DataSpecificationImpl impl = (DataSpecificationImpl) this.getSepcification(); | |||
| // List<FieldSpec> fields = impl.getFields(); | |||
| // | |||
| // try { | |||
| // for (FieldSpec spec : fields) { | |||
| // Method mth = ((FieldSpecImpl) spec).getReadMethod(); | |||
| // // mth.setAccessible(true); | |||
| // Object obj = mth.invoke(data); | |||
| // write(spec, out, obj); | |||
| // } | |||
| // } catch (SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { | |||
| // throw new DataContractException(e.getMessage(), e); | |||
| // } | |||
| // } | |||
| // | |||
| // public void read(FieldSpec spec, Method mth, InputStream in, Map<String, Object> fieldOutter) { | |||
| // ValueType primitive = spec.getPrimitiveType(); | |||
| // EnumSpecification refEnum = spec.getRefEnum(); | |||
| // DataSpecification refContract = spec.getRefContract(); | |||
| // boolean list = spec.isRepeatable(); | |||
| // boolean refPubKey = spec.isRefPubKey(); | |||
| // boolean refPrivKey = spec.isRefPrivKey(); | |||
| // boolean refHashDigest = spec.isRefHashDigest(); | |||
| // boolean refSignatureDigest = spec.isRefSignatureDigest(); | |||
| // boolean refIdentity = spec.isRefIdentity(); | |||
| // boolean refNetworkAddr = spec.isRefNetworkAddr(); | |||
| // | |||
| // try { | |||
| // // primitive data type | |||
| // if ((primitive != ValueType.NIL) && (list == false)) { | |||
| // switch (primitive) { | |||
| // case BOOLEAN: | |||
| // boolean boolValue = BytesUtils.readByte(in) == 1 ? true : false; | |||
| // if (fieldOutter != null) { | |||
| // fieldOutter.put(mth.getName(), boolValue); | |||
| // } | |||
| // break; | |||
| // case INT8: | |||
| // byte int8Value = BytesUtils.readByte(in); | |||
| // if (fieldOutter != null) { | |||
| // fieldOutter.put(mth.getName(), int8Value); | |||
| // } | |||
| // break; | |||
| // case INT16: | |||
| // short shortValue = (short) ((BytesUtils.readByte(in) << 8) | (BytesUtils.readByte(in))); | |||
| // if (fieldOutter != null) { | |||
| // fieldOutter.put(mth.getName(), shortValue); | |||
| // } | |||
| // break; | |||
| // case INT32: | |||
| // int intValue = BytesUtils.readInt(in); | |||
| // if (fieldOutter != null) { | |||
| // fieldOutter.put(mth.getName(), intValue); | |||
| // } | |||
| // break; | |||
| // case INT64: | |||
| // long value = BytesUtils.readLong(in); | |||
| // if (fieldOutter != null) { | |||
| // fieldOutter.put(mth.getName(), value); | |||
| // } | |||
| // break; | |||
| // case DATETIME: | |||
| // long seconds = BytesUtils.readLong(in); | |||
| // Date date = new Date(seconds); | |||
| // if (fieldOutter != null) { | |||
| // fieldOutter.put(mth.getName(), date); | |||
| // } | |||
| // break; | |||
| // case BYTES: | |||
| // byte[] bytes = BytesEncoding.read(NumberMask.NORMAL, in); | |||
| // if (fieldOutter != null) { | |||
| // fieldOutter.put(mth.getName(), ByteArray.wrap(bytes)); | |||
| // } | |||
| // break; | |||
| // case TEXT: | |||
| // case JSON: | |||
| // case XML: | |||
| // case BIG_INT: | |||
| // case IMG: | |||
| // case VIDEO: | |||
| // case LOCATION: | |||
| // byte[] dynamicResult = BytesEncoding.read(NumberMask.NORMAL, in); | |||
| // if (dynamicResult.length == 0) { | |||
| // if (fieldOutter != null) { | |||
| // fieldOutter.put(mth.getName(), null); | |||
| // } | |||
| // } else { | |||
| // StringBuffer buffer = new StringBuffer(); | |||
| // for (byte i : dynamicResult) { | |||
| // buffer.append((char) i); | |||
| // } | |||
| // if (fieldOutter != null) { | |||
| // fieldOutter.put(mth.getName(), buffer.toString()); | |||
| // } | |||
| // } | |||
| // break; | |||
| // default: | |||
| // throw new DataContractException("read: primitive type error!"); | |||
| // } | |||
| // } | |||
| // // enum type | |||
| // else if (refEnum != null) { | |||
| // int code = 0; | |||
| // switch (refEnum.getValueType()) { | |||
| // case INT8: | |||
| // code = BytesUtils.readByte(in); | |||
| // break; | |||
| // case INT16: | |||
| // code = (BytesUtils.readByte(in) << 8) | (BytesUtils.readByte(in)); | |||
| // break; | |||
| // case INT32: | |||
| // code = BytesUtils.readInt(in); | |||
| // break; | |||
| // default: | |||
| // throw new DataContractException("read: enum type error!"); | |||
| // } | |||
| // EnumSpecificationImpl refEnumImpl = (EnumSpecificationImpl) refEnum; | |||
| // Map<Object, Integer> constant = refEnumImpl.getEnumConstants(); | |||
| // Object enumConstant = null; | |||
| // for (Map.Entry<Object, Integer> vo : constant.entrySet()) { | |||
| // if (vo.getValue() == code) { | |||
| // enumConstant = vo.getKey(); | |||
| // break; | |||
| // } | |||
| // } | |||
| // if (fieldOutter != null) { | |||
| // fieldOutter.put(mth.getName(), enumConstant); | |||
| // } | |||
| // } | |||
| // // ref contract type | |||
| // else if (refContract != null && list == false) { | |||
| // BinaryEncoder encoder = null; | |||
| // Object object = null; | |||
| // int code = BytesUtils.readInt(in); | |||
| // long version = BytesUtils.readLong(in); | |||
| // encoder = DataContractRegistry.getEncoder(code, version); | |||
| // if (encoder == null) { | |||
| // throw new DataContractException("read: get encoder null error!"); | |||
| // } | |||
| // byte[] className = BytesEncoding.read(NumberMask.NORMAL, in); | |||
| // if (className.length != 0) { | |||
| // StringBuffer buffer = new StringBuffer(); | |||
| // for (byte i : className) { | |||
| // buffer.append((char) i); | |||
| // } | |||
| // object = encoder.decode(in, null, Class.forName(buffer.toString())); | |||
| // } | |||
| // if (fieldOutter != null) { | |||
| // fieldOutter.put(mth.getName(), object); | |||
| // } | |||
| // } | |||
| // // array type | |||
| // else if ((primitive != ValueType.NIL) && list == true) { | |||
| // int arrayCount = BytesUtils.readInt(in); | |||
| // int i; | |||
| // | |||
| // if (arrayCount == 0) { | |||
| // if (fieldOutter != null) { | |||
| // fieldOutter.put(mth.getName(), null); | |||
| // } | |||
| // } else { | |||
| // switch (primitive) { | |||
| // case BOOLEAN: | |||
| // boolean[] boolArray = new boolean[arrayCount]; | |||
| // for (i = 0; i < arrayCount; i++) { | |||
| // boolArray[i] = (BytesUtils.readByte(in) == 1) ? true : false; | |||
| // } | |||
| // if (fieldOutter != null) { | |||
| // fieldOutter.put(mth.getName(), boolArray); | |||
| // } | |||
| // break; | |||
| // case INT8: | |||
| // byte[] byteArray = new byte[arrayCount]; | |||
| // for (i = 0; i < arrayCount; i++) { | |||
| // byteArray[i] = (BytesUtils.readByte(in)); | |||
| // } | |||
| // if (fieldOutter != null) { | |||
| // fieldOutter.put(mth.getName(), byteArray); | |||
| // } | |||
| // break; | |||
| // case INT16: | |||
| // short[] shortArray = new short[arrayCount]; | |||
| // for (i = 0; i < arrayCount; i++) { | |||
| // shortArray[i] = (short) ((BytesUtils.readByte(in) << 8) | (BytesUtils.readByte(in))); | |||
| // } | |||
| // if (fieldOutter != null) { | |||
| // fieldOutter.put(mth.getName(), shortArray); | |||
| // } | |||
| // break; | |||
| // case INT32: | |||
| // int[] intArray = new int[arrayCount]; | |||
| // for (i = 0; i < arrayCount; i++) { | |||
| // intArray[i] = BytesUtils.readInt(in); | |||
| // } | |||
| // if (fieldOutter != null) { | |||
| // fieldOutter.put(mth.getName(), intArray); | |||
| // } | |||
| // break; | |||
| // case INT64: | |||
| // long[] longArray = new long[arrayCount]; | |||
| // for (i = 0; i < arrayCount; i++) { | |||
| // longArray[i] = BytesUtils.readLong(in); | |||
| // } | |||
| // if (fieldOutter != null) { | |||
| // fieldOutter.put(mth.getName(), longArray); | |||
| // } | |||
| // break; | |||
| // case DATETIME: | |||
| // Date[] dateArray = new Date[arrayCount]; | |||
| // for (i = 0; i < arrayCount; i++) { | |||
| // long seconds = BytesUtils.readLong(in); | |||
| // dateArray[i].setTime(seconds); | |||
| // } | |||
| // if (fieldOutter != null) { | |||
| // fieldOutter.put(mth.getName(), dateArray); | |||
| // } | |||
| // break; | |||
| // case TEXT: | |||
| // case JSON: | |||
| // case XML: | |||
| // case BYTES: | |||
| // case BIG_INT: | |||
| // case IMG: | |||
| // case VIDEO: | |||
| // case LOCATION: | |||
| // StringBuffer[] stringBufferArray = new StringBuffer[arrayCount]; | |||
| // String[] buffer = new String[arrayCount]; | |||
| // for (i = 0; i < arrayCount; i++) { | |||
| // byte[] dynamicResult = BytesEncoding.read(NumberMask.NORMAL, in); | |||
| // stringBufferArray[i] = new StringBuffer(); | |||
| // for (byte j : dynamicResult) { | |||
| // stringBufferArray[i].append((char) j); | |||
| // } | |||
| // buffer[i] = stringBufferArray[i].toString(); | |||
| // } | |||
| // if (fieldOutter != null) { | |||
| // fieldOutter.put(mth.getName(), buffer); | |||
| // } | |||
| // break; | |||
| // default: | |||
| // throw new DataContractException("read: array type error!"); | |||
| // } | |||
| // } | |||
| // } | |||
| // // ref contract type array | |||
| // else if (refContract != null && list == true) { | |||
| // int code; | |||
| // long version; | |||
| // BinaryEncoder encoder = null; | |||
| // Object[] refContractArray = null; | |||
| // int refContractArraySize = BytesUtils.readInt(in); | |||
| // if (refContractArraySize != 0) { | |||
| // refContractArray = new Object[refContractArraySize]; | |||
| // for (int i = 0; i < refContractArray.length; i++) { | |||
| // code = BytesUtils.readInt(in); | |||
| // version = BytesUtils.readLong(in); | |||
| // encoder = DataContractRegistry.getEncoder(code, version); | |||
| // if (encoder == null) { | |||
| // throw new DataContractException("read: get encoder null error!"); | |||
| // } | |||
| // byte[] className = BytesEncoding.read(NumberMask.NORMAL, in); | |||
| // if (className.length == 0) { | |||
| // refContractArray[i] = null; | |||
| // } else { | |||
| // StringBuffer buffer = new StringBuffer(); | |||
| // for (byte var : className) { | |||
| // buffer.append((char) var); | |||
| // } | |||
| // refContractArray[i] = encoder.decode(in, null, Class.forName(buffer.toString())); | |||
| // } | |||
| // } | |||
| // } | |||
| // if (fieldOutter != null) { | |||
| // fieldOutter.put(mth.getName(), refContractArray); | |||
| // } | |||
| // } else if (refPubKey == true | refPrivKey == true | refHashDigest == true | refSignatureDigest == true) { | |||
| // Object object = null; | |||
| // byte[] className = BytesEncoding.read(NumberMask.NORMAL, in); | |||
| // | |||
| // if (className.length != 0) { | |||
| // StringBuffer buffer = new StringBuffer(); | |||
| // for (byte var : className) { | |||
| // buffer.append((char) var); | |||
| // } | |||
| // byte[] bytes = BytesEncoding.read(NumberMask.NORMAL, in); | |||
| // object = Class.forName(buffer.toString()).getConstructor(byte[].class).newInstance(bytes); | |||
| // } | |||
| // if (fieldOutter != null) { | |||
| // fieldOutter.put(mth.getName(), object); | |||
| // } | |||
| // } | |||
| // // else if (refIdentity == true) { | |||
| // // BytesReader reader = null; | |||
| // // byte[] className = BytesEncoding.read(NumberMask.NORMAL, in); | |||
| // // if (className.length != 0) { | |||
| // // StringBuffer buffer = new StringBuffer(); | |||
| // // for (byte var : className) { | |||
| // // buffer.append((char) var); | |||
| // // } | |||
| // // reader = (BytesReader)Class.forName(buffer.toString()).newInstance(); | |||
| // // reader.resolvFrom(in); | |||
| // // } | |||
| // // if (fieldOutter != null) { | |||
| // // fieldOutter.put(mth.getName(), reader); | |||
| // // } | |||
| // // } | |||
| // else if (refNetworkAddr == true) { | |||
| // NetworkAddress networkAddress = null; | |||
| // byte[] buffer = BytesEncoding.read(NumberMask.NORMAL, in); | |||
| // if (buffer.length != 0) { | |||
| // StringBuffer host = new StringBuffer(); | |||
| // for (byte var : buffer) { | |||
| // host.append((char) var); | |||
| // } | |||
| // int port = BytesUtils.readInt(in); | |||
| // boolean secure = BytesUtils.readByte(in) == 1 ? true : false; | |||
| // networkAddress = new NetworkAddress(host.toString(), port, secure); | |||
| // } | |||
| // if (fieldOutter != null) { | |||
| // fieldOutter.put(mth.getName(), networkAddress); | |||
| // } | |||
| // } | |||
| // } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | NoSuchMethodException | |||
| // | InvocationTargetException e) { | |||
| // throw new DataContractException(e.getMessage(), e); | |||
| // } | |||
| // } | |||
| // | |||
| // public boolean checkConstructor(Class<?> concretedDataType) { | |||
| // int count = 0; | |||
| // Constructor<?>[] constructors = concretedDataType.getConstructors(); | |||
| // for (Constructor constructor : constructors) { | |||
| // if (constructor.getDeclaredAnnotation(DConstructor.class) != null) { | |||
| // count++; | |||
| // } | |||
| // } | |||
| // if (count >= 2) { | |||
| // return false; | |||
| // } | |||
| // return true; | |||
| // } | |||
| // | |||
| // // convert the first char to lowercase | |||
| // public String toLowerCaseFirstOne(String s) { | |||
| // if (Character.isLowerCase(s.charAt(0))) | |||
| // return s; | |||
| // else | |||
| // return (new StringBuilder()).append(Character.toLowerCase(s.charAt(0))).append(s.substring(1)).toString(); | |||
| // } | |||
| // | |||
| // public void executeSet(List<FieldSpec> fields, Map<String, Object> fieldOutter, Object object, | |||
| // Class<?> concretedDataType) { | |||
| // Method mth; | |||
| // try { | |||
| // for (FieldSpec spec : fields) { | |||
| // mth = ((FieldSpecImpl) spec).getReadMethod(); | |||
| // if (mth.getName().length() < 4) { | |||
| // throw new DataContractException("executeSet: mth name error!"); | |||
| // } | |||
| // // skip "get",get substring | |||
| // String getName = mth.getName().substring(3); | |||
| // // "set" concat with getmthName | |||
| // String setName = "set".concat(getName); | |||
| // Method[] allMths = concretedDataType.getMethods(); | |||
| // Method setMth = null; | |||
| // for (Method x : allMths) { | |||
| // if (x.getName().equals(setName)) { | |||
| // setMth = x; | |||
| // break; | |||
| // } | |||
| // } | |||
| // if (setMth != null) { | |||
| // // invoke related set method | |||
| // Object arg = fieldOutter.get(mth.getName()); | |||
| // if (arg != null) { | |||
| // setMth.invoke(object, arg); | |||
| // } | |||
| // } | |||
| // // set related member field | |||
| // else { | |||
| // String member = toLowerCaseFirstOne(getName); | |||
| // Field field = concretedDataType.getDeclaredField(member); | |||
| // if (field != null) { | |||
| // field.setAccessible(true); | |||
| // field.set(object, fieldOutter.get(mth.getName())); | |||
| // } | |||
| // } | |||
| // } | |||
| // } catch (IllegalAccessException | InvocationTargetException | NoSuchFieldException e) { | |||
| // throw new DataContractException(e.getMessage(), e); | |||
| // } | |||
| // } | |||
| // | |||
| // @Override | |||
| // public Object decode(InputStream in, Map<String, Object> fieldOutter, Class<?> concretedDataType) { | |||
| // // TODO: 未缓存对实现类的解析; | |||
| // DataSpecificationImpl impl = (DataSpecificationImpl) this.getSepcification(); | |||
| // List<FieldSpec> fields = impl.getFields(); | |||
| // Constructor constructor = null; | |||
| // Object object = null; | |||
| // Method mthType = null; | |||
| // | |||
| // if (fieldOutter == null) { | |||
| // fieldOutter = new HashMap<>(); | |||
| // } | |||
| // | |||
| // try { | |||
| // // first check constructor with annotation, count >=2 throw exception | |||
| // if (checkConstructor(concretedDataType) == false) { | |||
| // throw new DataContractException("decode: constructor with annotation number error!"); | |||
| // } | |||
| // // get constructor with annotation | |||
| // for (Constructor construct : concretedDataType.getConstructors()) { | |||
| // if (construct.getDeclaredAnnotation(DConstructor.class) != null) { | |||
| // constructor = construct; | |||
| // break; | |||
| // } | |||
| // } | |||
| // // fill fieldOutter with fieldsetter | |||
| // for (FieldSpec spec : fields) { | |||
| // Method mth = ((FieldSpecImpl) spec).getReadMethod(); | |||
| // if (mth == null) { | |||
| // throw new DataContractException("decode: mth null error!"); | |||
| // } | |||
| // read(spec, mth, in, fieldOutter); | |||
| // } | |||
| // // save constructor parameters | |||
| // if (constructor != null) { | |||
| // Annotation[][] annotations = constructor.getParameterAnnotations(); | |||
| // Object[] obj = new Object[annotations.length]; | |||
| // int i = 0; | |||
| // for (Annotation[] annoArray : annotations) { | |||
| // for (Annotation annotation : annoArray) { | |||
| // FieldSetter anno = (FieldSetter) annotation; | |||
| // obj[i] = fieldOutter.get(anno.name()); | |||
| // for (FieldSpec spec : fields) { | |||
| // mthType = ((FieldSpecImpl) spec).getReadMethod(); | |||
| // // in case :constructor and data contract method name is same ,but return type | |||
| // // is different | |||
| // if (mthType.getName().equals(anno.name())) { | |||
| // String retType = mthType.getReturnType().getSimpleName(); | |||
| // String annoType = anno.type(); | |||
| // if ((retType.equals(annoType) == false) && (retType.equals("ByteArray")) | |||
| // && (annoType.equals("byte[]"))) { | |||
| // ByteArray byteArray = (ByteArray) obj[i]; | |||
| // obj[i] = byteArray.bytes(); | |||
| // break; | |||
| // } else if ((retType.equals(annoType) == false) && (retType.equals("byte[]")) | |||
| // && (annoType.equals("ByteArray"))) { | |||
| // byte[] bytes = (byte[]) obj[i]; | |||
| // obj[i] = ByteArray.wrap(bytes); | |||
| // break; | |||
| // } | |||
| // } | |||
| // } | |||
| // i++; | |||
| // } | |||
| // } | |||
| // // exec constructor with parameters | |||
| // object = constructor.newInstance(obj); | |||
| // } | |||
| // if (object == null) { | |||
| // // use default constructor, | |||
| // constructor = concretedDataType.getDeclaredConstructor(); | |||
| // if (!constructor.isAccessible()) { | |||
| // constructor.setAccessible(true); | |||
| // } | |||
| // object = constructor.newInstance(); | |||
| // } | |||
| // // exec set method | |||
| // executeSet(fields, fieldOutter, object, concretedDataType); | |||
| // return object; | |||
| // } catch (IllegalAccessException | InstantiationException | InvocationTargetException | NoSuchMethodException | |||
| // | SecurityException e) { | |||
| // throw new DataContractException(e.getMessage(), e); | |||
| // } | |||
| // } | |||
| //} | |||
| @@ -0,0 +1,94 @@ | |||
| //package com.jd.blockchain.binaryproto.impl; | |||
| // | |||
| //import java.lang.reflect.Method; | |||
| //import java.util.*; | |||
| // | |||
| //import com.jd.blockchain.binaryproto.BinarySliceSpec; | |||
| //import com.jd.blockchain.binaryproto.DataField; | |||
| //import com.jd.blockchain.binaryproto.DataSpecification; | |||
| //import com.jd.blockchain.binaryproto.FieldSpec; | |||
| // | |||
| ///** | |||
| // * Created by zhangshuang3 on 2018/6/21. | |||
| // */ | |||
| //public class DataSpecificationImpl implements DataSpecification { | |||
| // private int code; | |||
| // private long version; | |||
| // private String name; | |||
| // private String description; | |||
| // private List<FieldSpec> fieldList = new ArrayList<FieldSpec>(); | |||
| // private List<BinarySliceSpec> sliceList = new ArrayList<>(); | |||
| // | |||
| // | |||
| // public DataSpecificationImpl() { | |||
| // | |||
| // } | |||
| // //sort method by order id | |||
| // public Map<FieldSpec, Method> sortMapByValues (Map<FieldSpec, Method> mths) { | |||
| // Set<Map.Entry<FieldSpec,Method>> mapEntries = mths.entrySet(); | |||
| // List<Map.Entry<FieldSpec,Method>> aList = new LinkedList<Map.Entry<FieldSpec, Method>>(mapEntries); | |||
| // //sort list | |||
| // Collections.sort(aList, new Comparator<Map.Entry<FieldSpec, Method>>() { | |||
| // @Override | |||
| // public int compare(Map.Entry<FieldSpec, Method> ele1, | |||
| // Map.Entry<FieldSpec, Method> ele2) { | |||
| // return (ele1.getValue().getAnnotation(DataField.class).order()) - (ele2.getValue().getAnnotation(DataField.class).order()); | |||
| // } | |||
| // }); | |||
| // int count = aList.size(); | |||
| // //init Capacity | |||
| // Map<FieldSpec, Method> aMap = new LinkedHashMap<FieldSpec, Method>(count); | |||
| // for(Map.Entry<FieldSpec, Method> entry: aList) { | |||
| // aMap.put(entry.getKey(), entry.getValue()); | |||
| // } | |||
| // return aMap; | |||
| // } | |||
| // | |||
| // @Override | |||
| // public int getCode() { | |||
| // return code; | |||
| // } | |||
| // public void setCode(int code) { | |||
| // this.code = code; | |||
| // } | |||
| // | |||
| // @Override | |||
| // public long getVersion() { | |||
| // return version; | |||
| // } | |||
| // public void setVersion(long version) { | |||
| // this.version = version; | |||
| // } | |||
| // | |||
| // @Override | |||
| // public String getName() { | |||
| // return name; | |||
| // } | |||
| // public void setName(String name) { | |||
| // this.name = name; | |||
| // } | |||
| // | |||
| // @Override | |||
| // public String getDescription() { | |||
| // return description; | |||
| // } | |||
| // public void setDescription(String description) {this.description = description;} | |||
| // | |||
| // @Override | |||
| // public List<FieldSpec> getFields() { | |||
| // return fieldList; | |||
| // } | |||
| // public void setFields(FieldSpec field) {this.fieldList.add(field);} | |||
| // | |||
| // @Override | |||
| // public List<BinarySliceSpec> getSlices() { | |||
| // return sliceList; | |||
| // } | |||
| // public void setSlices(BinarySliceSpec slice) { | |||
| // this.sliceList.add(slice); | |||
| // } | |||
| // @Override | |||
| // public String toHtml() { | |||
| // return null; | |||
| // } | |||
| //} | |||
| @@ -0,0 +1,32 @@ | |||
| package com.jd.blockchain.binaryproto.impl; | |||
| import java.util.HashMap; | |||
| import java.util.Map; | |||
| import com.jd.blockchain.binaryproto.EnumSpecification; | |||
| /** | |||
| * Created by zhangshuang3 on 2018/6/27. | |||
| */ | |||
| public class EnumContractRegistry { | |||
| private static Map<String, EnumSpecification> enumSpecs = new HashMap<String, EnumSpecification>(); | |||
| public EnumContractRegistry() { | |||
| } | |||
| public static EnumSpecification getEnumSpec(Class<?> contractType) { | |||
| //find encoder from dataSpecs by contractType | |||
| for (String key : enumSpecs.keySet()) | |||
| { | |||
| if (key.equals(contractType.getName())) { | |||
| return enumSpecs.get(key); | |||
| } | |||
| } | |||
| return null; | |||
| } | |||
| public static void setEnumSpecs(String key, EnumSpecification value) { | |||
| enumSpecs.put(key, value); | |||
| } | |||
| } | |||
| @@ -0,0 +1,84 @@ | |||
| //package com.jd.blockchain.binaryproto.impl; | |||
| // | |||
| //import java.lang.reflect.Method; | |||
| //import java.util.*; | |||
| // | |||
| //import com.jd.blockchain.binaryproto.EnumSpecification; | |||
| // | |||
| //import my.utils.ValueType; | |||
| // | |||
| ///** | |||
| // * Created by zhangshuang3 on 2018/6/21. | |||
| // */ | |||
| //public class EnumSpecificationImpl implements EnumSpecification { | |||
| // private int code; | |||
| // private long version; | |||
| // private String name; | |||
| // private String description; | |||
| // private ValueType item; | |||
| // private Set<Integer> intSet = new LinkedHashSet<>(); | |||
| // private Set<String> stringSet = new LinkedHashSet<>(); | |||
| // private Map<Object, Integer> readEnumConstants = new HashMap<Object, Integer>(); | |||
| // | |||
| // public EnumSpecificationImpl(int code, String name, String description) { | |||
| // this.code = code; | |||
| // this.name = name; | |||
| // this.description = description; | |||
| // } | |||
| // @Override | |||
| // public int getCode() { | |||
| // return this.code; | |||
| // } | |||
| // public void setCode(int code) { | |||
| // this.code = code; | |||
| // } | |||
| // @Override | |||
| // public long getVersion(){ | |||
| // return this.version; | |||
| // } | |||
| // public void setVersion(long version) { | |||
| // this.version = version; | |||
| // } | |||
| // @Override | |||
| // public String getName() { | |||
| // return this.name; | |||
| // } | |||
| // public void setName(String name) { | |||
| // this.name = name; | |||
| // } | |||
| // @Override | |||
| // public String getDescription() { | |||
| // return this.description; | |||
| // } | |||
| // public void setDescription(String description) { | |||
| // this.description = description; | |||
| // } | |||
| // @Override | |||
| // public ValueType getValueType() { | |||
| // return this.item; | |||
| // } | |||
| // public void setItemType(ValueType item) { | |||
| // this.item = item; | |||
| // } | |||
| // @Override | |||
| // public Set<Integer> getItemValues() { | |||
| // return this.intSet; | |||
| // } | |||
| // public void setItemValues(Integer item) { | |||
| // this.intSet.add(item); | |||
| // } | |||
| // @Override | |||
| // public Set<String> getItemNames() { | |||
| // return this.stringSet; | |||
| // } | |||
| // public void setItemNames(String item) { | |||
| // this.stringSet.add(item); | |||
| // } | |||
| // | |||
| // public Map<Object, Integer> getEnumConstants() { | |||
| // return this.readEnumConstants; | |||
| // } | |||
| // public void setEnumConstants(Object enumConstant , Integer code) { | |||
| // this.readEnumConstants.put(enumConstant, code); | |||
| // } | |||
| //} | |||
| @@ -0,0 +1,142 @@ | |||
| //package com.jd.blockchain.binaryproto.impl; | |||
| // | |||
| // | |||
| //import org.omg.CORBA.PUBLIC_MEMBER; | |||
| // | |||
| //import com.jd.blockchain.binaryproto.DataSpecification; | |||
| //import com.jd.blockchain.binaryproto.EnumSpecification; | |||
| //import com.jd.blockchain.binaryproto.FieldSpec; | |||
| // | |||
| //import my.utils.ValueType; | |||
| // | |||
| //import java.lang.reflect.Method; | |||
| // | |||
| ///** | |||
| // * Created by zhangshuang3 on 2018/6/21. | |||
| // */ | |||
| //public class FieldSpecImpl implements FieldSpec { | |||
| // private int typeCode; | |||
| // private ValueType primitiveType; | |||
| // private EnumSpecification enumSpec; | |||
| // private DataSpecification dataSpec; | |||
| // private boolean isRefPubKey; | |||
| // private boolean isRefPrivKey; | |||
| // private boolean isRefHashDigest; | |||
| // private boolean isRefSignatureDigest; | |||
| // private boolean isRefIdentity; | |||
| // private boolean isRefNetworkAddr; | |||
| // private String name; | |||
| // private String description; | |||
| // private boolean isList; | |||
| // private int maxLength; | |||
| // private Class<?> contractTypeResolver; | |||
| // private Method readMethod; | |||
| // | |||
| // public FieldSpecImpl() { | |||
| // | |||
| // } | |||
| // public Method getReadMethod() { | |||
| // return readMethod; | |||
| // } | |||
| // public void setReadMethod(Method readMethod) { | |||
| // this.readMethod = readMethod; | |||
| // readMethod.setAccessible(true); | |||
| // } | |||
| // @Override | |||
| // public int getTypeCode() {return typeCode;} | |||
| // public void setTypeCode(int typeCode) {this.typeCode = typeCode;} | |||
| // | |||
| // @Override | |||
| // public ValueType getPrimitiveType() {return primitiveType;} | |||
| // public void setPrimitiveType(ValueType primitiveType) { | |||
| // this.primitiveType = primitiveType; | |||
| // } | |||
| // @Override | |||
| // public EnumSpecification getRefEnum() {return enumSpec;} | |||
| // public void setRefEnum(EnumSpecification enumSpec) { | |||
| // this.enumSpec = enumSpec; | |||
| // } | |||
| // | |||
| // @Override | |||
| // public DataSpecification getRefContract() {return dataSpec;} | |||
| // public void setRefContract(DataSpecification dataSpec) { | |||
| // this.dataSpec = dataSpec; | |||
| // } | |||
| // | |||
| // @Override | |||
| // public boolean isRepeatable() {return isList;} | |||
| // public void setIsList(boolean isList) {this.isList = isList;} | |||
| // | |||
| // @Override | |||
| // public int getMaxSize() {return maxLength;} | |||
| // public void setMaxLength(int length) { | |||
| // this.maxLength = maxLength; | |||
| // } | |||
| // @Override | |||
| // public String getName() {return name;} | |||
| // public void setName(String name) { | |||
| // this.name = name; | |||
| // } | |||
| // | |||
| // @Override | |||
| // public String getDescription() {return description;} | |||
| // public void setDescription(String description) { | |||
| // this.description = description; | |||
| // } | |||
| // | |||
| // @Override | |||
| // public boolean isRefPubKey() { | |||
| // return isRefPubKey; | |||
| // } | |||
| // public void setRefPubKey(boolean pubKey) { | |||
| // this.isRefPubKey = pubKey; | |||
| // } | |||
| // | |||
| // @Override | |||
| // public boolean isRefPrivKey() { | |||
| // return isRefPrivKey; | |||
| // } | |||
| // public void setRefPrivKey(boolean privKey) { | |||
| // this.isRefPrivKey = privKey; | |||
| // } | |||
| // | |||
| // @Override | |||
| // public boolean isRefSignatureDigest() { | |||
| // return isRefSignatureDigest; | |||
| // } | |||
| // public void setRefSignatureDigest(boolean signatureDigest) { | |||
| // this.isRefSignatureDigest = signatureDigest; | |||
| // } | |||
| // | |||
| // @Override | |||
| // public boolean isRefHashDigest() { | |||
| // return isRefHashDigest; | |||
| // } | |||
| // public void setRefHashDigest(boolean hashDigest) { | |||
| // this.isRefHashDigest = hashDigest; | |||
| // } | |||
| // | |||
| // @Override | |||
| // public Class<?> getContractTypeResolver() { | |||
| // return this.contractTypeResolver; | |||
| // } | |||
| // public void setContractTypeResolver(Class<?> resolver) { | |||
| // this.contractTypeResolver = resolver; | |||
| // } | |||
| // | |||
| // @Override | |||
| // public boolean isRefIdentity() { | |||
| // return isRefIdentity; | |||
| // } | |||
| // public void setRefIdentity(boolean identity) { | |||
| // this.isRefIdentity = identity; | |||
| // } | |||
| // | |||
| // @Override | |||
| // public boolean isRefNetworkAddr() { | |||
| // return isRefNetworkAddr; | |||
| // } | |||
| // public void setRefNetworkAddr(boolean networkAddr) { | |||
| // this.isRefNetworkAddr = networkAddr; | |||
| // } | |||
| //} | |||
| @@ -0,0 +1,46 @@ | |||
| package com.jd.blockchain.binaryproto.impl2; | |||
| import com.jd.blockchain.utils.io.BytesInputStream; | |||
| import com.jd.blockchain.utils.io.BytesOutputBuffer; | |||
| import com.jd.blockchain.utils.io.BytesSlice; | |||
| import com.jd.blockchain.utils.io.NumberMask; | |||
| public abstract class AbstractDynamicValueConverter implements DynamicValueConverter { | |||
| protected Class<?> valueType; | |||
| private static final NumberMask SIZE_HEAD = NumberMask.NORMAL; | |||
| private static final byte[] NULL_HEAD = new byte[1]; | |||
| static { | |||
| SIZE_HEAD.writeMask(0, NULL_HEAD, 0); | |||
| } | |||
| public AbstractDynamicValueConverter(Class<?> valueType) { | |||
| this.valueType = valueType; | |||
| } | |||
| @Override | |||
| public Class<?> getValueType() { | |||
| return valueType; | |||
| } | |||
| @Override | |||
| public Object getDefaultValue() { | |||
| return null; | |||
| } | |||
| protected int writeSize(int size, BytesOutputBuffer buffer) { | |||
| int len = SIZE_HEAD.getMaskLength(size); | |||
| byte[] headerBytes = new byte[len]; | |||
| SIZE_HEAD.writeMask(size, headerBytes, 0); | |||
| buffer.write(headerBytes); | |||
| return len; | |||
| } | |||
| protected int readSize(BytesInputStream bytesStream) { | |||
| return SIZE_HEAD.resolveMaskedNumber(bytesStream); | |||
| } | |||
| } | |||
| @@ -0,0 +1,50 @@ | |||
| package com.jd.blockchain.binaryproto.impl2; | |||
| import java.lang.reflect.InvocationTargetException; | |||
| import java.lang.reflect.Method; | |||
| import com.jd.blockchain.binaryproto.BinarySliceSpec; | |||
| import com.jd.blockchain.binaryproto.FieldSpec; | |||
| public abstract class AbstractFieldEncoder implements FieldEncoder { | |||
| protected BinarySliceSpec sliceSpec; | |||
| protected FieldSpec fieldSpec; | |||
| protected Method reader; | |||
| public AbstractFieldEncoder(BinarySliceSpec sliceSpec, FieldSpec fieldSpec, Method reader) { | |||
| this.sliceSpec = sliceSpec; | |||
| this.fieldSpec = fieldSpec; | |||
| this.reader = reader; | |||
| } | |||
| @Override | |||
| public BinarySliceSpec getSliceSpecification() { | |||
| return sliceSpec; | |||
| } | |||
| @Override | |||
| public FieldSpec getFieldSpecification() { | |||
| return fieldSpec; | |||
| } | |||
| @Override | |||
| public Method getReader() { | |||
| return reader; | |||
| } | |||
| protected Object readValue(Object dataContract) { | |||
| try { | |||
| return reader.invoke(dataContract); | |||
| } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { | |||
| throw new IllegalStateException(e.getMessage(), e); | |||
| } | |||
| } | |||
| protected Object[] readArrayValue(Object dataContract) { | |||
| return (Object[]) readValue(dataContract); | |||
| } | |||
| } | |||
| @@ -0,0 +1,28 @@ | |||
| package com.jd.blockchain.binaryproto.impl2; | |||
| import com.jd.blockchain.utils.io.BytesSlice; | |||
| public class BoolConverter implements FixedValueConverter{ | |||
| @Override | |||
| public Class<?> getValueType() { | |||
| return boolean.class; | |||
| } | |||
| @Override | |||
| public Object getDefaultValue() { | |||
| return false; | |||
| } | |||
| @Override | |||
| public int encodeValue(Object value, byte[] buffer, int offset) { | |||
| buffer[offset] = ((Boolean)value).booleanValue() ? (byte)1 : (byte)0; | |||
| return 1; | |||
| } | |||
| @Override | |||
| public Object decodeValue(BytesSlice dataSlice) { | |||
| return dataSlice.getByte() == 1 ? Boolean.TRUE : Boolean.FALSE; | |||
| } | |||
| } | |||
| @@ -0,0 +1,28 @@ | |||
| package com.jd.blockchain.binaryproto.impl2; | |||
| import com.jd.blockchain.utils.io.BytesSlice; | |||
| public class BoolWrapperConverter implements FixedValueConverter{ | |||
| @Override | |||
| public Class<?> getValueType() { | |||
| return Boolean.class; | |||
| } | |||
| @Override | |||
| public Object getDefaultValue() { | |||
| return null; | |||
| } | |||
| @Override | |||
| public int encodeValue(Object value, byte[] buffer, int offset) { | |||
| buffer[offset] = ((Boolean)value).booleanValue() ? (byte)1 : (byte)0; | |||
| return 1; | |||
| } | |||
| @Override | |||
| public Object decodeValue(BytesSlice dataSlice) { | |||
| return dataSlice.getByte() == 1 ? Boolean.TRUE : Boolean.FALSE; | |||
| } | |||
| } | |||
| @@ -0,0 +1,55 @@ | |||
| package com.jd.blockchain.binaryproto.impl2; | |||
| import java.lang.reflect.Constructor; | |||
| import java.lang.reflect.InvocationTargetException; | |||
| import com.jd.blockchain.binaryproto.DataContractException; | |||
| import com.jd.blockchain.utils.io.BytesOutputBuffer; | |||
| import com.jd.blockchain.utils.io.BytesSerializable; | |||
| import com.jd.blockchain.utils.io.BytesSlice; | |||
| import com.jd.blockchain.utils.io.BytesUtils; | |||
| public class BytesSerializableValueConverter extends AbstractDynamicValueConverter { | |||
| private Constructor<?> constructor; | |||
| public BytesSerializableValueConverter(Class<?> valueType) { | |||
| super(valueType); | |||
| if (!BytesSerializable.class.isAssignableFrom(valueType)) { | |||
| throw new IllegalArgumentException("The specified type cann't be assigned as BytesSerializable!"); | |||
| } | |||
| // 检查是否存在以 byte[] 为参数的构造器; | |||
| try { | |||
| constructor = valueType.getConstructor(byte[].class); | |||
| constructor.setAccessible(true); | |||
| } catch (NoSuchMethodException e) { | |||
| throw new DataContractException("No constructor with byte's array argument! --" + e.getMessage(), e); | |||
| } catch (SecurityException e) { | |||
| throw new DataContractException(e.getMessage(), e); | |||
| } | |||
| } | |||
| @Override | |||
| public Object decodeValue(BytesSlice dataSlice) { | |||
| if (dataSlice.getSize() == 0) { | |||
| return null; | |||
| } | |||
| byte[] bytes = dataSlice.getBytesCopy(); | |||
| try { | |||
| return constructor.newInstance(bytes); | |||
| } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | |||
| | InvocationTargetException e) { | |||
| throw new DataContractException(e.getMessage(), e); | |||
| } | |||
| } | |||
| @Override | |||
| public int encodeDynamicValue(Object value, BytesOutputBuffer buffer) { | |||
| byte[] bytes = value == null ? BytesUtils.EMPTY_BYTES : ((BytesSerializable) value).toBytes(); | |||
| int size = bytes.length; | |||
| size += writeSize(size, buffer); | |||
| buffer.write(bytes); | |||
| return size; | |||
| } | |||
| } | |||
| @@ -0,0 +1,28 @@ | |||
| package com.jd.blockchain.binaryproto.impl2; | |||
| import com.jd.blockchain.utils.io.BytesOutputBuffer; | |||
| import com.jd.blockchain.utils.io.BytesSlice; | |||
| import com.jd.blockchain.utils.io.BytesUtils; | |||
| public class BytesValueConverter extends AbstractDynamicValueConverter { | |||
| public BytesValueConverter() { | |||
| super(byte[].class); | |||
| } | |||
| @Override | |||
| public int encodeDynamicValue(Object value, BytesOutputBuffer buffer) { | |||
| byte[] bytes =value == null ? BytesUtils.EMPTY_BYTES : (byte[]) value; | |||
| int size = bytes.length; | |||
| size += writeSize(size, buffer); | |||
| buffer.write(bytes); | |||
| return size; | |||
| } | |||
| @Override | |||
| public Object decodeValue(BytesSlice dataSlice) { | |||
| return dataSlice.getBytesCopy(); | |||
| } | |||
| } | |||
| @@ -0,0 +1,790 @@ | |||
| package com.jd.blockchain.binaryproto.impl2; | |||
| import java.lang.reflect.Field; | |||
| import java.lang.reflect.Method; | |||
| import java.util.ArrayList; | |||
| import java.util.Arrays; | |||
| import java.util.Collection; | |||
| import java.util.Collections; | |||
| import java.util.HashMap; | |||
| import java.util.List; | |||
| import java.util.Map; | |||
| import java.util.TreeMap; | |||
| import java.util.concurrent.ConcurrentHashMap; | |||
| import com.jd.blockchain.binaryproto.BinarySliceSpec; | |||
| import com.jd.blockchain.binaryproto.DataContract; | |||
| import com.jd.blockchain.binaryproto.DataContractEncoder; | |||
| import com.jd.blockchain.binaryproto.DataContractException; | |||
| import com.jd.blockchain.binaryproto.DataField; | |||
| import com.jd.blockchain.binaryproto.DataSpecification; | |||
| import com.jd.blockchain.binaryproto.EnumContract; | |||
| import com.jd.blockchain.binaryproto.EnumField; | |||
| import com.jd.blockchain.binaryproto.EnumSpecification; | |||
| import com.jd.blockchain.binaryproto.FieldSpec; | |||
| import com.jd.blockchain.binaryproto.impl2.EnumSpecificationInfo.EnumConstant; | |||
| import com.jd.blockchain.utils.ValueType; | |||
| import com.jd.blockchain.utils.io.BytesSerializable; | |||
| import com.jd.blockchain.utils.io.BytesUtils; | |||
| import com.jd.blockchain.utils.security.SHA256Hash; | |||
| import com.jd.blockchain.utils.security.ShaUtils; | |||
| public class DataContractContext { | |||
| public static DataContractEncoderLookup ENCODER_LOOKUP; | |||
| private static final Object MUTEX = new Object(); | |||
| private static final BinarySliceSpec HEAD_SLICE = BinarySliceSpec.newFixedSlice(HeaderEncoder.HEAD_BYTES, "HEAD", | |||
| "The code and version of data contract."); | |||
| private static final byte SINGLE_TYPE = 0; | |||
| private static final byte REPEATABLE_TYPE = 1; | |||
| /** | |||
| * 基本类型的字段; | |||
| */ | |||
| private static final byte PRIMITIVE_TYPE_FIELD = 0; | |||
| /** | |||
| * 枚举类型的字段; | |||
| */ | |||
| private static final byte ENUM_CONTRACT_FIELD = 1; | |||
| /** | |||
| * 引用一个具体的数据契约类型的字段; | |||
| */ | |||
| private static final byte DATA_CONTRACT_FIELD = 2; | |||
| /** | |||
| * 动态的数据契约类型的字段; | |||
| */ | |||
| private static final byte DYNAMIC_CONTRACT_FIELD = 3; | |||
| private static Map<Integer, ContractTypeVersionContext> codeMap = new ConcurrentHashMap<>(); | |||
| private static Map<Class<?>, DataContractEncoder> typeMap = new ConcurrentHashMap<>(); | |||
| private static Map<Class<?>, EnumSpecification> enumContractSpecMap = new ConcurrentHashMap<>(); | |||
| private static Map<ValueType, Map<Class<?>, ValueConverter>> primitiveTypeConverters = new HashMap<>(); | |||
| static { | |||
| addConverterMapping(ValueType.BOOLEAN, boolean.class, new BoolConverter()); | |||
| addConverterMapping(ValueType.BOOLEAN, Boolean.class, new BoolWrapperConverter()); | |||
| addConverterMapping(ValueType.INT8, byte.class, new Int8ByteConverter()); | |||
| addConverterMapping(ValueType.INT8, Byte.class, new Int8ByteWrapperConverter()); | |||
| addConverterMapping(ValueType.INT16, short.class, new Int16ShortConverter()); | |||
| addConverterMapping(ValueType.INT16, Short.class, new Int16ShortWrapperConverter()); | |||
| addConverterMapping(ValueType.INT16, char.class, new Int16CharConverter()); | |||
| addConverterMapping(ValueType.INT16, Character.class, new Int16CharWrapperConverter()); | |||
| addConverterMapping(ValueType.INT32, int.class, new Int32IntConverter()); | |||
| addConverterMapping(ValueType.INT32, Integer.class, new Int32IntWrapperConverter()); | |||
| addConverterMapping(ValueType.INT64, long.class, new Int64LongConverter()); | |||
| addConverterMapping(ValueType.INT64, Long.class, new Int64LongWrapperConverter()); | |||
| addConverterMapping(ValueType.TEXT, String.class, new StringValueConverter()); | |||
| addConverterMapping(ValueType.BYTES, byte[].class, new BytesValueConverter()); | |||
| ENCODER_LOOKUP = new DataContractEncoderLookup() { | |||
| @Override | |||
| public DataContractEncoder lookup(int code, long version) { | |||
| ContractTypeVersionContext ctx = codeMap.get(code); | |||
| if (ctx == null) { | |||
| return null; | |||
| } | |||
| // TODO: 未实现多个版本的处理; | |||
| return ctx.contractEncoder; | |||
| } | |||
| @Override | |||
| public DataContractEncoder lookup(Class<?> contractType) { | |||
| return typeMap.get(contractType); | |||
| } | |||
| }; | |||
| } | |||
| private static void addConverterMapping(ValueType protocalType, Class<?> javaType, ValueConverter converter) { | |||
| Map<Class<?>, ValueConverter> converterMap = primitiveTypeConverters.get(protocalType); | |||
| if (converterMap == null) { | |||
| converterMap = new HashMap<>(); | |||
| primitiveTypeConverters.put(protocalType, converterMap); | |||
| } | |||
| converterMap.put(javaType, converter); | |||
| } | |||
| private static ValueConverter getPrimitiveTypeConverter(ValueType protocalType, Class<?> javaType) { | |||
| Map<Class<?>, ValueConverter> converterMap = primitiveTypeConverters.get(protocalType); | |||
| if (converterMap != null) { | |||
| ValueConverter converter = converterMap.get(javaType); | |||
| if (converter != null) { | |||
| return converter; | |||
| } | |||
| if (ValueType.BYTES == protocalType && BytesSerializable.class.isAssignableFrom(javaType)) { | |||
| converter = new BytesSerializableValueConverter(javaType); | |||
| converterMap.put(javaType, converter); | |||
| return converter; | |||
| } | |||
| } | |||
| throw new IllegalArgumentException(String.format("Unsupport types mapping: [PrimitiveType=%s]-[JavaType=%s]", | |||
| protocalType.toString(), javaType.toString())); | |||
| } | |||
| public static DataContractEncoder resolve(Class<?> contractType) { | |||
| DataContractEncoder encoder = typeMap.get(contractType); | |||
| if (encoder != null) { | |||
| return encoder; | |||
| } | |||
| synchronized (MUTEX) { | |||
| encoder = typeMap.get(contractType); | |||
| if (encoder != null) { | |||
| return encoder; | |||
| } | |||
| ContractTypeVersionContext ctx = resolveContract(contractType); | |||
| encoder = ctx.contractEncoder; | |||
| } | |||
| return encoder; | |||
| } | |||
| /** | |||
| * 解析数据契约; <br> | |||
| * | |||
| * @param contractType | |||
| * @return | |||
| */ | |||
| private static ContractTypeVersionContext resolveContract(Class<?> contractType) { | |||
| // TODO: 未处理可能存在的循环依赖问题,这会导致解析方法陷入死循环; | |||
| if (!contractType.isInterface()) { | |||
| throw new IllegalArgumentException( | |||
| "The specified contractType [" + contractType.toString() + "] is not a interface!"); | |||
| } | |||
| DataContract annoContract = contractType.getAnnotation(DataContract.class); | |||
| if (annoContract == null) { | |||
| throw new IllegalArgumentException( | |||
| "Class[" + contractType.toString() + "] isn't annotated as DataContract!"); | |||
| } | |||
| int contractCode = annoContract.code(); | |||
| ContractTypeVersionContext ctx = codeMap.get(contractCode); | |||
| if (ctx != null) { | |||
| if (ctx.contractType == contractType) { | |||
| return ctx; | |||
| } else { | |||
| throw new IllegalStateException(String.format( | |||
| "Contract Code[%s] has been registered by type[%s]! Cann't register again with type[%s]!", | |||
| contractCode, ctx.contractType.getName(), contractType.getName())); | |||
| } | |||
| } | |||
| DataContractEncoder contractEncoder = resolveEncoder(contractType, annoContract); | |||
| ctx = new ContractTypeVersionContext(contractType, contractEncoder); | |||
| codeMap.put(contractCode, ctx); | |||
| typeMap.put(contractType, contractEncoder); | |||
| return ctx; | |||
| } | |||
| /** | |||
| * @param contractType | |||
| * @param annoContract | |||
| * @return | |||
| */ | |||
| private static DataContractEncoder resolveEncoder(Class<?> contractType, DataContract annoContract) { | |||
| DataContractEncoder encoder = typeMap.get(contractType); | |||
| if (encoder != null) { | |||
| return encoder; | |||
| } | |||
| if (!contractType.isInterface()) { | |||
| throw new IllegalArgumentException( | |||
| "The registering contract type is not a interface! --" + contractType.getName()); | |||
| } | |||
| // 解析获得数据契约的有序的字段列表; | |||
| List<FieldDeclaredInfo> allFields = resolveContractFields(contractType, annoContract); | |||
| // 解析每一个字段,生成字段的编码器和二进制片段描述符; | |||
| FieldSpecInfo[] fieldSpecs = new FieldSpecInfo[allFields.size()]; | |||
| BinarySliceSpec[] dataSliceSpecs = new BinarySliceSpec[allFields.size() + 1]; | |||
| FieldEncoder[] fieldEncoders = new FieldEncoder[allFields.size()]; | |||
| dataSliceSpecs[0] = HEAD_SLICE; | |||
| SHA256Hash versionHash = ShaUtils.hash_256();// 用于计算 DataContract 的版本号的哈希生成器; | |||
| int i = 0; | |||
| for (FieldDeclaredInfo fieldInfo : allFields) { | |||
| fieldSpecs[i] = fieldInfo.fieldSpec; | |||
| // 构建二进制片段; | |||
| dataSliceSpecs[i + 1] = buildSlice(fieldInfo.fieldSpec); | |||
| fieldEncoders[i] = buildFieldEncoder(fieldInfo, dataSliceSpecs[i + 1]); | |||
| // 按顺序计算字段类型, | |||
| byte[] fieldType = generateFieldTypeCode(fieldInfo.fieldSpec); | |||
| versionHash.update(fieldType); | |||
| i++; | |||
| } | |||
| // 数据契约的版本号取自对所有字段的数据类型的哈希前 8 位; | |||
| byte[] allFieldTypesHash = versionHash.complete(); | |||
| long version = BytesUtils.toLong(allFieldTypesHash); | |||
| HeaderEncoder headerEncoder = new HeaderEncoder(HEAD_SLICE, annoContract.code(), version, annoContract.name(), | |||
| annoContract.description()); | |||
| DataContractSpecification spec = new DataContractSpecification(annoContract.code(), version, | |||
| annoContract.name(), annoContract.description(), dataSliceSpecs, fieldSpecs); | |||
| DataContractEncoderImpl contractEncoder = new DataContractEncoderImpl(contractType, spec, headerEncoder, | |||
| fieldEncoders); | |||
| return contractEncoder; | |||
| } | |||
| /** | |||
| * 解析获得数据契约的有序的字段列表; | |||
| * | |||
| * @param contractType | |||
| * @param annoContract | |||
| * @return | |||
| */ | |||
| private static List<FieldDeclaredInfo> resolveContractFields(Class<?> contractType, DataContract annoContract) { | |||
| // 解析每一个方法,获得标注的合约字段,并按照声明的合约类型进行分组; | |||
| Map<Class<?>, DeclaredFieldGroup> declaredFielGroups = new HashMap<>(); | |||
| Method[] methods = contractType.getMethods(); | |||
| for (Method method : methods) { | |||
| DataField annoField = method.getAnnotation(DataField.class); | |||
| if (annoField == null) { | |||
| continue; | |||
| } | |||
| Class<?> declaredType = method.getDeclaringClass(); | |||
| DeclaredFieldGroup group = declaredFielGroups.get(declaredType); | |||
| if (group != null) { | |||
| FieldSpecInfo fieldSpec = resolveFieldSpec(method, annoField); | |||
| group.addField(method, annoField, fieldSpec); | |||
| continue; | |||
| } | |||
| if (declaredType == contractType) { | |||
| // 字段是由当前的数据契约类型所声明; | |||
| FieldSpecInfo fieldSpec = resolveFieldSpec(method, annoField); | |||
| group = new DeclaredFieldGroup(contractType, annoContract, method, annoField, fieldSpec); | |||
| declaredFielGroups.put(contractType, group); | |||
| continue; | |||
| } | |||
| // 字段由父接口声明,所以取父接口上的标注定义进行解析; | |||
| DataContract declaredContractAnnotation = declaredType.getAnnotation(DataContract.class); | |||
| if (declaredContractAnnotation == null) { | |||
| throw new DataContractException("Declare data contract field in a non-data-contract type! --[Type=" | |||
| + declaredType.getName() + "]"); | |||
| } | |||
| FieldSpecInfo fieldSpec = resolveFieldSpec(method, annoField); | |||
| group = new DeclaredFieldGroup(declaredType, declaredContractAnnotation, method, annoField, fieldSpec); | |||
| declaredFielGroups.put(declaredType, group); | |||
| } | |||
| DeclaredFieldGroup[] groups = declaredFielGroups.values() | |||
| .toArray(new DeclaredFieldGroup[declaredFielGroups.size()]); | |||
| for (DeclaredFieldGroup group : groups) { | |||
| // 计算继承距离; | |||
| int extendsionDistance = computeExtendsionDistance(contractType, group.declaredContractType); | |||
| if (extendsionDistance < 0) { | |||
| // 实际不会进入此分支; | |||
| throw new IllegalStateException("Illegal state that isn't expected to occur!"); | |||
| } | |||
| group.setExtendsionDistance(extendsionDistance); | |||
| } | |||
| // 按继承距离和数据契约的编码进行倒序排序,如果继承距离相同,则编码小的在前; | |||
| // 达到的效果:父接口声明的字段在前,子接口声明的字段在后;同一个继承级别,则编码小的在前; | |||
| Arrays.sort(groups, | |||
| (g1, g2) -> (g2.extendsionDistance == g1.extendsionDistance | |||
| ? g1.declaredContractAnnotation.code() - g2.declaredContractAnnotation.code() | |||
| : g2.extendsionDistance - g1.extendsionDistance)); | |||
| List<FieldDeclaredInfo> allFields = new ArrayList<>(); | |||
| for (DeclaredFieldGroup grp : groups) { | |||
| allFields.addAll(grp.getFields()); | |||
| } | |||
| return allFields; | |||
| } | |||
| /** | |||
| * 创建字段的编码器; | |||
| * | |||
| * @param fieldInfo | |||
| * @param sliceSpec | |||
| * @return | |||
| */ | |||
| private static FieldEncoder buildFieldEncoder(FieldDeclaredInfo fieldInfo, BinarySliceSpec sliceSpec) { | |||
| FieldSpecInfo fieldSpec = fieldInfo.fieldSpec; | |||
| if (fieldSpec.getPrimitiveType() != null) { | |||
| return buildPrimitiveFieldEncoder(fieldInfo, sliceSpec); | |||
| } else if (fieldSpec.getRefEnum() != null) { | |||
| return buildEnumFieldEncoder(fieldInfo, sliceSpec); | |||
| } else if (fieldSpec.getRefContract() != null) { | |||
| return buildContractFieldEncoder(fieldInfo, sliceSpec); | |||
| } else { | |||
| throw new IllegalStateException("Illegal states that has no type definition for field! --[ReadMethod=" | |||
| + fieldInfo.reader.toString() + ""); | |||
| } | |||
| } | |||
| /** | |||
| * 创建数据契约引用字段的编码器; | |||
| * | |||
| * @param fieldInfo | |||
| * @param sliceSpec | |||
| * @return | |||
| */ | |||
| private static FieldEncoder buildContractFieldEncoder(FieldDeclaredInfo fieldInfo, BinarySliceSpec sliceSpec) { | |||
| ValueConverter valueConverter; | |||
| if (fieldInfo.fieldSpec.isGenericContract()) { | |||
| Class<?> contractType = fieldInfo.fieldSpec.getDataType(); | |||
| valueConverter = new DataContractGenericRefConverter(contractType, ENCODER_LOOKUP); | |||
| } else { | |||
| Class<?> contractType = fieldInfo.fieldSpec.getDataType(); | |||
| DataContractEncoder encoder = typeMap.get(contractType); | |||
| valueConverter = new DataContractValueConverter(encoder); | |||
| } | |||
| return createFieldEncoder(sliceSpec, fieldInfo.fieldSpec, fieldInfo.reader, valueConverter); | |||
| } | |||
| /** | |||
| * 创建枚举类型的字段编码器; | |||
| * | |||
| * @param fieldInfo | |||
| * @param sliceSpec | |||
| * @return | |||
| */ | |||
| private static FieldEncoder buildEnumFieldEncoder(FieldDeclaredInfo fieldInfo, BinarySliceSpec sliceSpec) { | |||
| // 枚举类型的值转换器是由枚举值的范围检查加上一个基本类型的值转换器组成; | |||
| Class<?> enumType = fieldInfo.fieldSpec.getDataType(); | |||
| EnumSpecificationInfo enumSpec = (EnumSpecificationInfo) fieldInfo.fieldSpec.getRefEnum(); | |||
| int[] values = enumSpec.getItemValues(); | |||
| Object[] constants = enumSpec.getConstants(); | |||
| ValueType codeType = enumSpec.getValueType(); | |||
| ValueConverter baseConverter = getPrimitiveTypeConverter(codeType, enumSpec.getDataType()); | |||
| EnumValueConverter valueConverter = new EnumValueConverter(enumType, codeType, values, constants, | |||
| (FixedValueConverter) baseConverter); | |||
| return createFieldEncoder(sliceSpec, fieldInfo.fieldSpec, fieldInfo.reader, valueConverter); | |||
| } | |||
| /** | |||
| * 创建基本类型字段的编码器; | |||
| * | |||
| * @param fieldInfo | |||
| * @param sliceSpec | |||
| * @return | |||
| */ | |||
| private static FieldEncoder buildPrimitiveFieldEncoder(FieldDeclaredInfo fieldInfo, BinarySliceSpec sliceSpec) { | |||
| ValueConverter valueConverter = getPrimitiveTypeConverter(fieldInfo.fieldSpec.getPrimitiveType(), | |||
| fieldInfo.fieldSpec.getDataType()); | |||
| return createFieldEncoder(sliceSpec, fieldInfo.fieldSpec, fieldInfo.reader, valueConverter); | |||
| } | |||
| private static FieldEncoder createFieldEncoder(BinarySliceSpec sliceSpec, FieldSpec fieldSpec, Method reader, | |||
| ValueConverter valueConverter) { | |||
| if (sliceSpec.isRepeatable()) { | |||
| if (sliceSpec.isDynamic()) { | |||
| return new DynamicArrayFieldEncoder(sliceSpec, fieldSpec, reader, | |||
| (DynamicValueConverter) valueConverter); | |||
| } else { | |||
| return new FixedArrayFieldEncoder(sliceSpec, fieldSpec, reader, (FixedValueConverter) valueConverter); | |||
| } | |||
| } else { | |||
| if (sliceSpec.isDynamic()) { | |||
| return new DynamicFieldEncoder(sliceSpec, fieldSpec, reader, (DynamicValueConverter) valueConverter); | |||
| } else { | |||
| return new FixedFieldEncoder(sliceSpec, fieldSpec, reader, (FixedValueConverter) valueConverter); | |||
| } | |||
| } | |||
| } | |||
| private static BinarySliceSpec buildSlice(FieldSpecInfo fieldSpec) { | |||
| boolean fixed = false; | |||
| int len = -1; | |||
| ValueType fixedValueType = null; | |||
| if (fieldSpec.getPrimitiveType() != null && fieldSpec.getPrimitiveType() != ValueType.NIL) { | |||
| fixedValueType = fieldSpec.getPrimitiveType(); | |||
| } else if (fieldSpec.getRefEnum() != null) { | |||
| fixedValueType = fieldSpec.getRefEnum().getValueType(); | |||
| } | |||
| if (fixedValueType != null) { | |||
| switch (fixedValueType) { | |||
| case BOOLEAN: | |||
| fixed = true; | |||
| len = 1; | |||
| break; | |||
| case INT8: | |||
| fixed = true; | |||
| len = 1; | |||
| break; | |||
| case INT16: | |||
| fixed = true; | |||
| len = 2; | |||
| break; | |||
| case INT32: | |||
| fixed = true; | |||
| len = 4; | |||
| break; | |||
| case INT64: | |||
| fixed = true; | |||
| len = 8; | |||
| break; | |||
| default: | |||
| break; | |||
| } | |||
| } | |||
| if (fieldSpec.isRepeatable()) { | |||
| if (fixed) { | |||
| return BinarySliceSpec.newRepeatableFixedSlice(len, fieldSpec.getName(), fieldSpec.getDescription()); | |||
| } else { | |||
| return BinarySliceSpec.newRepeatableDynamicSlice(fieldSpec.getName(), fieldSpec.getDescription()); | |||
| } | |||
| } else { | |||
| if (fixed) { | |||
| return BinarySliceSpec.newFixedSlice(len, fieldSpec.getName(), fieldSpec.getDescription()); | |||
| } else { | |||
| return BinarySliceSpec.newDynamicSlice(fieldSpec.getName(), fieldSpec.getDescription()); | |||
| } | |||
| } | |||
| } | |||
| private static byte[] generateFieldTypeCode(FieldSpecInfo fieldSpec) { | |||
| byte repeatable = fieldSpec.isRepeatable() ? REPEATABLE_TYPE : SINGLE_TYPE; | |||
| byte[] codeBytes; | |||
| if (fieldSpec.getPrimitiveType() != null) { | |||
| // repeatable + type indicator + code of primitive type; | |||
| // 1 + 1 + 4; | |||
| codeBytes = new byte[6]; | |||
| codeBytes[0] = repeatable; | |||
| codeBytes[1] = PRIMITIVE_TYPE_FIELD; | |||
| BytesUtils.toBytes(fieldSpec.getPrimitiveType().CODE, codeBytes, 2); | |||
| } else if (fieldSpec.getRefEnum() != null) { | |||
| // repeatable + type indicator + code of enum contract + version of enum | |||
| // contract; | |||
| // 1+ 1 + 4 + 8; | |||
| codeBytes = new byte[14]; | |||
| codeBytes[0] = repeatable; | |||
| codeBytes[1] = ENUM_CONTRACT_FIELD; | |||
| EnumSpecification enumSpec = fieldSpec.getRefEnum(); | |||
| BytesUtils.toBytes(enumSpec.getCode(), codeBytes, 2); | |||
| BytesUtils.toBytes(enumSpec.getVersion(), codeBytes, 6); | |||
| } else if (fieldSpec.getRefContract() != null) { | |||
| // repeatable + type indicator + code of enum contract + version of enum | |||
| // contract; | |||
| // 1+ 1 + 4 + 8; | |||
| DataSpecification dataSpec = fieldSpec.getRefContract(); | |||
| codeBytes = new byte[14]; | |||
| codeBytes[0] = repeatable; | |||
| if (fieldSpec.isGenericContract()) { | |||
| codeBytes[1] = DYNAMIC_CONTRACT_FIELD; | |||
| } else { | |||
| codeBytes[1] = DATA_CONTRACT_FIELD; | |||
| } | |||
| BytesUtils.toBytes(dataSpec.getCode(), codeBytes, 2); | |||
| BytesUtils.toBytes(dataSpec.getVersion(), codeBytes, 6); | |||
| } else { | |||
| throw new DataContractException("Unknow field type!"); | |||
| } | |||
| return codeBytes; | |||
| } | |||
| /** | |||
| * 计算指定两个接口类型之间的继承距离;<br> | |||
| * | |||
| * 如果不具有继承关系,则返回 -1; | |||
| * | |||
| * @param subsTypes | |||
| * 子类型; | |||
| * @param superType | |||
| * 父类型; | |||
| * @return | |||
| */ | |||
| private static int computeExtendsionDistance(Class<?> subsTypes, Class<?> superType) { | |||
| if (subsTypes == superType) { | |||
| return 0; | |||
| } | |||
| Class<?>[] superIntfs = subsTypes.getInterfaces(); | |||
| for (Class<?> si : superIntfs) { | |||
| int dis = computeExtendsionDistance(si, superType); | |||
| if (dis > -1) { | |||
| return dis + 1; | |||
| } | |||
| } | |||
| return -1; | |||
| } | |||
| private static FieldSpecInfo resolveFieldSpec(Method accessor, DataField annoField) { | |||
| String name = annoField.name(); | |||
| name = name == null ? null : name.trim(); | |||
| if (name == null || name.length() > 0) { | |||
| name = accessor.getName(); | |||
| } | |||
| String desc = annoField.decription(); | |||
| desc = desc == null ? null : desc.trim(); | |||
| int order = annoField.order(); | |||
| boolean repeatable = annoField.list(); | |||
| Class<?> dataType = accessor.getReturnType(); | |||
| if (repeatable) { | |||
| if (!dataType.isArray()) { | |||
| throw new DataContractException("The annotated repeatable type mismatch non-array type[" | |||
| + dataType.getName() + "]! --[Field=" + accessor.toString() + "]"); | |||
| } | |||
| dataType = dataType.getComponentType(); | |||
| } | |||
| int maxSize = annoField.maxSize(); | |||
| ValueType primitiveType = annoField.primitiveType(); | |||
| if (primitiveType != null) { | |||
| primitiveType = verifyPrimitiveType(primitiveType, dataType, accessor); | |||
| } | |||
| boolean refEnum = annoField.refEnum(); | |||
| EnumSpecification enumSpecification = null; | |||
| if (refEnum) { | |||
| EnumContract annoEnumContract = dataType.getAnnotation(EnumContract.class); | |||
| if (annoEnumContract == null) { | |||
| throw new DataContractException("The data type of annotated enum field is not a EnumContract! --[Field=" | |||
| + accessor.toString() + "]"); | |||
| } | |||
| enumSpecification = resolveEnumContract(dataType, annoEnumContract); | |||
| } | |||
| boolean refContract = annoField.refContract(); | |||
| DataSpecification contractSpecification = null; | |||
| // Class<?> contractTypeResolverClazz = null; | |||
| if (refContract) { | |||
| // DataContract annoContract = dataType.getAnnotation(DataContract.class); | |||
| // if (annoContract == null) { | |||
| // throw new DataContractException( | |||
| // "The data type of annotated contract field is not a DataContract! --[Field=" | |||
| // + accessor.toString() + "]"); | |||
| // } | |||
| ContractTypeVersionContext contractContext = resolveContract(dataType); | |||
| DataContractEncoder encoder = contractContext.contractEncoder; | |||
| contractSpecification = encoder.getSepcification(); | |||
| // contractTypeResolverClazz = annoField.contractTypeResolver(); | |||
| // if (contractTypeResolverClazz != null | |||
| // && (!ContractTypeResolver.class.isAssignableFrom(contractTypeResolverClazz))) | |||
| // { | |||
| // throw new DataContractException( | |||
| // "The contract type resolver of contract field doesn't implement | |||
| // ContractTypeResolver interface! --[Field=" | |||
| // + accessor.toString() + "]"); | |||
| // } | |||
| } | |||
| if (primitiveType == null && enumSpecification == null && contractSpecification == null) { | |||
| throw new DataContractException( | |||
| "Miss data type definition of field! --[Field=" + accessor.toString() + "]"); | |||
| } | |||
| FieldSpecInfo fieldSpec = null; | |||
| if (primitiveType != null) { | |||
| fieldSpec = new FieldSpecInfo(order, name, desc, primitiveType, repeatable, maxSize, dataType); | |||
| } else if (enumSpecification != null) { | |||
| fieldSpec = new FieldSpecInfo(order, name, desc, enumSpecification, repeatable, dataType); | |||
| } else { | |||
| fieldSpec = new FieldSpecInfo(order, name, desc, contractSpecification, repeatable, dataType, | |||
| annoField.genericContract()); | |||
| } | |||
| return fieldSpec; | |||
| } | |||
| private static EnumSpecification resolveEnumContract(Class<?> dataType, EnumContract annoEnumContract) { | |||
| EnumSpecificationInfo enumSpec = (EnumSpecificationInfo) enumContractSpecMap.get(dataType); | |||
| if (enumSpec != null) { | |||
| return enumSpec; | |||
| } | |||
| try { | |||
| if (!dataType.isEnum()) { | |||
| throw new DataContractException("Field's type is not a enum type! --[" + dataType.toString() + "]"); | |||
| } | |||
| // TODO:暂时硬编码检索 code 字段; | |||
| Field codeField = dataType.getField("CODE"); | |||
| if (codeField == null) { | |||
| throw new DataContractException("Enum type miss the 'CODE' field! --[" + dataType.toString() + "]"); | |||
| } | |||
| EnumField fieldAnno = codeField.getAnnotation(EnumField.class); | |||
| if (fieldAnno == null) { | |||
| throw new DataContractException("Enum's 'CODE' field is not annotated with @EnumField !"); | |||
| } | |||
| // TODO: 暂时未实现枚举契约的版本号计算; | |||
| long version = 0; | |||
| enumSpec = new EnumSpecificationInfo(fieldAnno.type(), annoEnumContract.code(), version, | |||
| annoEnumContract.name(), annoEnumContract.decription(), codeField.getType()); | |||
| // get enum constants and CODE | |||
| Object[] enumItems = dataType.getEnumConstants(); | |||
| for (Object item : enumItems) { | |||
| int code = codeField.getInt(item); | |||
| EnumConstant constant = new EnumConstant(code, item.toString(), item); | |||
| enumSpec.addConstant(constant); | |||
| // enumSpec.setVersion(); if need | |||
| } | |||
| enumContractSpecMap.put(dataType, enumSpec); | |||
| return enumSpec; | |||
| } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) { | |||
| throw new DataContractException(e.getMessage(), e); | |||
| } | |||
| } | |||
| /** | |||
| * 解析指定的类型是否匹配;<br> | |||
| * | |||
| * 要求必须是显式地声明类型,因此不会根据 Java 语言的声明类型做自动转换; | |||
| * | |||
| * @param primitiveType | |||
| * @param dataType | |||
| * @return | |||
| */ | |||
| private static ValueType verifyPrimitiveType(ValueType primitiveType, Class<?> dataType, Method accessor) { | |||
| switch (primitiveType) { | |||
| case NIL: | |||
| return null; | |||
| case BOOLEAN: | |||
| if (dataType != Boolean.class && dataType != boolean.class) { | |||
| throw new DataContractException("The annotated primitive type[" + primitiveType.toString() | |||
| + "] mismatch java type[" + dataType.getName() + "]! --[Field=" + accessor.toString() + "]"); | |||
| } | |||
| break; | |||
| case INT8: | |||
| if (dataType != Byte.class && dataType != byte.class) { | |||
| throw new DataContractException("The annotated primitive type[" + primitiveType.toString() | |||
| + "] mismatch java type[" + dataType.getName() + "]! --[Field=" + accessor.toString() + "]"); | |||
| } | |||
| break; | |||
| case INT16: | |||
| if (dataType != Character.class && dataType != char.class && dataType != short.class) { | |||
| throw new DataContractException("The annotated primitive type[" + primitiveType.toString() | |||
| + "] mismatch java type[" + dataType.getName() + "]! --[Field=" + accessor.toString() + "]"); | |||
| } | |||
| break; | |||
| case INT32: | |||
| if (dataType != Integer.class && dataType != int.class) { | |||
| throw new DataContractException("The annotated primitive type[" + primitiveType.toString() | |||
| + "] mismatch java type[" + dataType.getName() + "]! --[Field=" + accessor.toString() + "]"); | |||
| } | |||
| break; | |||
| case INT64: | |||
| if (dataType != Long.class && dataType != long.class) { | |||
| throw new DataContractException("The annotated primitive type[" + primitiveType.toString() | |||
| + "] mismatch java type[" + dataType.getName() + "]! --[Field=" + accessor.toString() + "]"); | |||
| } | |||
| break; | |||
| case TEXT: | |||
| if (dataType != String.class) { | |||
| throw new DataContractException("The annotated primitive type[" + primitiveType.toString() | |||
| + "] mismatch java type[" + dataType.getName() + "]! --[Field=" + accessor.toString() + "]"); | |||
| } | |||
| break; | |||
| case BYTES: | |||
| if (dataType != byte[].class && (!BytesSerializable.class.isAssignableFrom(dataType))) { | |||
| throw new DataContractException("The annotated primitive type[" + primitiveType.toString() | |||
| + "] mismatch java type[" + dataType.getName() + "]! --[Field=" + accessor.toString() + "]"); | |||
| } | |||
| break; | |||
| default: | |||
| throw new DataContractException("Unsupported primitive type[" + primitiveType.toString() + "] ! --[Field=" | |||
| + accessor.toString() + "]"); | |||
| } | |||
| return primitiveType; | |||
| } | |||
| private static class DeclaredFieldGroup { | |||
| /** | |||
| * 声明的合约类型; | |||
| */ | |||
| public Class<?> declaredContractType; | |||
| /** | |||
| * | |||
| */ | |||
| public DataContract declaredContractAnnotation; | |||
| /** | |||
| * 声明类型距离要解析的类型的继承距离;直接继承的父接口的距离为 1,父接口的父接口为 2,以此类推; | |||
| */ | |||
| private int extendsionDistance; | |||
| private TreeMap<Integer, FieldDeclaredInfo> orderedFields = new TreeMap<>(); | |||
| public Collection<FieldDeclaredInfo> getFields() { | |||
| return orderedFields.values(); | |||
| } | |||
| public DeclaredFieldGroup(Class<?> declaredContractType, DataContract declaredContractAnnotation, | |||
| Method accessor, DataField fieldAnnotation, FieldSpecInfo fieldSpec) { | |||
| this.declaredContractType = declaredContractType; | |||
| this.declaredContractAnnotation = declaredContractAnnotation; | |||
| addField(accessor, fieldAnnotation, fieldSpec); | |||
| } | |||
| private void addField(Method accessor, DataField fieldAnnotation, FieldSpecInfo fieldSpec) { | |||
| // 检查字段的是否有重复序号; | |||
| FieldDeclaredInfo fieldInfo = new FieldDeclaredInfo(accessor, fieldAnnotation, fieldSpec); | |||
| FieldDeclaredInfo conflictedField = orderedFields.put(fieldSpec.getOrder(), fieldInfo); | |||
| if (conflictedField != null) { | |||
| // 有两个字段都声明了相同的序号,这容易导致无序状态; | |||
| throw new DataContractException(String.format("Declare two fields with the same order! --[%s][%s]", | |||
| fieldInfo.reader.toString(), conflictedField.reader.toString())); | |||
| } | |||
| } | |||
| @SuppressWarnings("unused") | |||
| public int getExtendsionDistance() { | |||
| return extendsionDistance; | |||
| } | |||
| public void setExtendsionDistance(int extendsionDistance) { | |||
| this.extendsionDistance = extendsionDistance; | |||
| } | |||
| } | |||
| private static class FieldDeclaredInfo { | |||
| public FieldSpecInfo fieldSpec; | |||
| public Method reader; | |||
| public DataField annoField; | |||
| public FieldDeclaredInfo(Method accessor, DataField annoField, FieldSpecInfo fieldSpec) { | |||
| this.reader = accessor; | |||
| this.annoField = annoField; | |||
| this.fieldSpec = fieldSpec; | |||
| } | |||
| } | |||
| private static class ContractTypeVersionContext { | |||
| public Class<?> contractType; | |||
| public DataContractEncoder contractEncoder; | |||
| // TODO:未实现多版本; | |||
| // private HashMap<Long, DataSpecification> versionMap = new HashMap<>(); | |||
| public ContractTypeVersionContext(Class<?> contractType, DataContractEncoder encoder) { | |||
| this.contractType = contractType; | |||
| this.contractEncoder = encoder; | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,133 @@ | |||
| package com.jd.blockchain.binaryproto.impl2; | |||
| import java.lang.reflect.Method; | |||
| import java.util.HashMap; | |||
| import java.util.Map; | |||
| import com.jd.blockchain.binaryproto.DataContractEncoder; | |||
| import com.jd.blockchain.binaryproto.DataSpecification; | |||
| import com.jd.blockchain.utils.io.BytesInputStream; | |||
| import com.jd.blockchain.utils.io.BytesOutputBuffer; | |||
| import com.jd.blockchain.utils.io.BytesSlice; | |||
| public class DataContractEncoderImpl implements DataContractEncoder { | |||
| private Class<?>[] contractTypeArray; | |||
| private Class<?> contractType; | |||
| private DataSpecification specification; | |||
| private HeaderEncoder headEncoder; | |||
| private FieldEncoder[] fieldEncoders; | |||
| // 字段的 Get 方法与编码器的映射表; | |||
| private Map<Method, Integer> fieldIndexMap; | |||
| /** | |||
| * @param contractType | |||
| * @param specification | |||
| * @param headEncoder | |||
| * 头部编码器; | |||
| * @param fieldEncoders | |||
| * 按顺序排列的字段编码器列表; | |||
| */ | |||
| public DataContractEncoderImpl(Class<?> contractType, DataSpecification specification, HeaderEncoder headEncoder, | |||
| FieldEncoder[] fieldEncoders) { | |||
| this.contractType = contractType; | |||
| this.contractTypeArray = new Class<?>[] { contractType }; | |||
| this.specification = specification; | |||
| this.headEncoder = headEncoder; | |||
| this.fieldEncoders = fieldEncoders; | |||
| this.fieldIndexMap = new HashMap<>(); | |||
| int i = 0; | |||
| for (FieldEncoder fieldEncoder : fieldEncoders) { | |||
| fieldIndexMap.put(fieldEncoder.getReader(), i); | |||
| i++; | |||
| } | |||
| } | |||
| HeaderEncoder getHeaderEncoder() { | |||
| return headEncoder; | |||
| } | |||
| Class<?>[] getContractTypeAsArray() { | |||
| return contractTypeArray; | |||
| } | |||
| int getFieldCount() { | |||
| return fieldEncoders.length; | |||
| } | |||
| FieldEncoder getFieldEncoder(int id) { | |||
| return fieldEncoders[id]; | |||
| } | |||
| /** | |||
| * 通过字段的声明方法返回字段的序号; | |||
| * | |||
| * @param declaredMethod | |||
| * 声明并标注为数据契约字段的方法;注:不能是覆盖的非标注方法,也不能是实现方法; | |||
| * @return 字段序号; 如果不存在,则返回 -1; | |||
| */ | |||
| int getFieldId(Method declaredMethod) { | |||
| Integer id = fieldIndexMap.get(declaredMethod); | |||
| return id == null ? -1 : id.intValue(); | |||
| } | |||
| /** | |||
| * 通过字段的声明方法返回字段的编码器; | |||
| * | |||
| * @param declaredMethod | |||
| * 声明并标注为数据契约字段的方法;注:不能是覆盖的非标注方法,也不能是实现方法; | |||
| * @return | |||
| */ | |||
| FieldEncoder getFieldEncoder(Method declaredMethod) { | |||
| Integer idx = fieldIndexMap.get(declaredMethod); | |||
| if (idx == null) { | |||
| return null; | |||
| } | |||
| return fieldEncoders[idx.intValue()]; | |||
| } | |||
| @Override | |||
| public DataSpecification getSepcification() { | |||
| return specification; | |||
| } | |||
| @Override | |||
| public Class<?> getContractType() { | |||
| return contractType; | |||
| } | |||
| @Override | |||
| public int encode(Object dataContract, BytesOutputBuffer buffer) { | |||
| int size = 0; | |||
| size += headEncoder.encode(dataContract, buffer); | |||
| if (dataContract != null) { | |||
| for (SliceEncoder sliceEncoder : fieldEncoders) { | |||
| size += sliceEncoder.encode(dataContract, buffer); | |||
| } | |||
| } | |||
| return size; | |||
| } | |||
| @SuppressWarnings("unchecked") | |||
| @Override | |||
| public <T> T decode(BytesInputStream bytesStream) { | |||
| BytesSlice headerSlice = bytesStream.getSlice(HeaderEncoder.HEAD_BYTES); | |||
| if (!headEncoder.verifyHeaders(headerSlice)) { | |||
| throw new IllegalArgumentException(String.format( | |||
| "The code and version resolved from bytes stream is not match this data contract encoder! --[expected=%s, %s][actual=%s, %s].", | |||
| headEncoder.getCode(), headEncoder.getVersion(), HeaderEncoder.resolveCode(headerSlice), | |||
| HeaderEncoder.resolveVersion(headerSlice))); | |||
| } | |||
| if (bytesStream.getSize() == HeaderEncoder.HEAD_BYTES) { | |||
| // 只有头部,没有值,表示空值; | |||
| return null; | |||
| } | |||
| return (T) DynamicDataContract.createContract(bytesStream, this); | |||
| } | |||
| } | |||
| @@ -0,0 +1,23 @@ | |||
| package com.jd.blockchain.binaryproto.impl2; | |||
| import com.jd.blockchain.binaryproto.DataContractEncoder; | |||
| public interface DataContractEncoderLookup { | |||
| /** | |||
| * 检索指定类型的编码器; | |||
| * | |||
| * @param contractType | |||
| * @return | |||
| */ | |||
| DataContractEncoder lookup(Class<?> contractType); | |||
| /** | |||
| * 检索指定 code 和 version 的编码器; | |||
| * | |||
| * @param contractType | |||
| * @return | |||
| */ | |||
| DataContractEncoder lookup(int code, long version); | |||
| } | |||
| @@ -0,0 +1,94 @@ | |||
| package com.jd.blockchain.binaryproto.impl2; | |||
| import java.util.Map; | |||
| import java.util.concurrent.ConcurrentHashMap; | |||
| import com.jd.blockchain.binaryproto.DataContract; | |||
| import com.jd.blockchain.binaryproto.DataContractEncoder; | |||
| import com.jd.blockchain.binaryproto.DataContractException; | |||
| import com.jd.blockchain.utils.io.BytesOutputBuffer; | |||
| import com.jd.blockchain.utils.io.BytesSlice; | |||
| public class DataContractGenericRefConverter extends AbstractDynamicValueConverter { | |||
| private final Object mutex = new Object(); | |||
| private DataContractEncoderLookup encoderLookup; | |||
| private Map<Class<?>, DataContractEncoder> encoderCache; | |||
| public DataContractGenericRefConverter(Class<?> baseType, DataContractEncoderLookup encoderLookup) { | |||
| super(baseType); | |||
| this.encoderLookup = encoderLookup; | |||
| this.encoderCache = new ConcurrentHashMap<>(); | |||
| } | |||
| private DataContractEncoder lookupEncoder(Class<?> dataObjectType) { | |||
| DataContractEncoder encoder = encoderCache.get(dataObjectType); | |||
| if (encoder != null) { | |||
| return encoder; | |||
| } | |||
| synchronized (mutex) { | |||
| encoder = encoderCache.get(dataObjectType); | |||
| if (encoder != null) { | |||
| return encoder; | |||
| } | |||
| Class<?>[] intfs = dataObjectType.getInterfaces(); | |||
| Class<?> contractType = null; | |||
| DataContract anno = null; | |||
| for (Class<?> itf : intfs) { | |||
| anno = itf.getAnnotation(DataContract.class); | |||
| if (anno != null) { | |||
| if (contractType == null) { | |||
| contractType = itf; | |||
| } else { | |||
| throw new DataContractException(String.format( | |||
| "Data object implements more than one DataContract interface! --[DataObject=%s]", | |||
| dataObjectType.toString())); | |||
| } | |||
| } | |||
| } | |||
| if (contractType == null) { | |||
| throw new DataContractException( | |||
| String.format("Data object doesn't implement any DataContract interface! --[DataObject=%s]", | |||
| dataObjectType.toString())); | |||
| } | |||
| encoder = encoderLookup.lookup(contractType); | |||
| if (encoder == null) { | |||
| throw new DataContractException(String.format( | |||
| "DataContract of the specified data object hasn't been registered! --[DataContract=%s][DataObject=%s]", | |||
| contractType.toString(), dataObjectType.toString())); | |||
| } | |||
| encoderCache.put(dataObjectType, encoder); | |||
| } | |||
| return encoder; | |||
| } | |||
| @Override | |||
| public int encodeDynamicValue(Object value, BytesOutputBuffer buffer) { | |||
| DataContractEncoder contractEncoder = lookupEncoder(value.getClass()); | |||
| BytesOutputBuffer contractBuffer = new BytesOutputBuffer(); | |||
| int size = contractEncoder.encode(value, contractBuffer); | |||
| size += writeSize(size, buffer); | |||
| buffer.write(contractBuffer); | |||
| return size; | |||
| } | |||
| @Override | |||
| public Object decodeValue(BytesSlice dataSlice) { | |||
| int code = HeaderEncoder.resolveCode(dataSlice); | |||
| long version = HeaderEncoder.resolveVersion(dataSlice); | |||
| DataContractEncoder contractEncoder = encoderLookup.lookup(code, version); | |||
| if (contractEncoder == null) { | |||
| throw new DataContractException( | |||
| String.format("No data contract was registered with code[%s] and version[%s]!", code, version)); | |||
| } | |||
| return contractEncoder.decode(dataSlice.getInputStream()); | |||
| } | |||
| } | |||
| @@ -0,0 +1,28 @@ | |||
| //package com.jd.blockchain.binaryproto.impl2; | |||
| // | |||
| //public class DataContractHeader { | |||
| // | |||
| // private int code; | |||
| // | |||
| // private int version; | |||
| // | |||
| // private String name; | |||
| // | |||
| // private String description; | |||
| // | |||
| // public DataContractHeader(int contractCode, int contractVersion, String name, String description) { | |||
| // this.code = contractCode; | |||
| // this.version = contractVersion; | |||
| // this.name = name ; | |||
| // this.description = description; | |||
| // } | |||
| // | |||
| // public int getContractVersion() { | |||
| // return version; | |||
| // } | |||
| // | |||
| // public int getContractCode() { | |||
| // return code; | |||
| // } | |||
| // | |||
| //} | |||
| @@ -0,0 +1,65 @@ | |||
| package com.jd.blockchain.binaryproto.impl2; | |||
| import java.util.ArrayList; | |||
| import java.util.Arrays; | |||
| import java.util.Collections; | |||
| import java.util.List; | |||
| import com.jd.blockchain.binaryproto.BinarySliceSpec; | |||
| import com.jd.blockchain.binaryproto.DataSpecification; | |||
| import com.jd.blockchain.binaryproto.FieldSpec; | |||
| public class DataContractSpecification implements DataSpecification { | |||
| private int code; | |||
| private long version; | |||
| private String name; | |||
| private String description; | |||
| private List<FieldSpec> fieldList; | |||
| private List<BinarySliceSpec> sliceList; | |||
| public DataContractSpecification(int code, long version, String name, String description, BinarySliceSpec[] slices, FieldSpec[] fields) { | |||
| this.code = code; | |||
| this.version = version; | |||
| this.name = name; | |||
| this.description = description; | |||
| this.fieldList = Collections.unmodifiableList(Arrays.asList(fields)); | |||
| this.sliceList = Collections.unmodifiableList(Arrays.asList(slices)); | |||
| } | |||
| @Override | |||
| public int getCode() { | |||
| return code; | |||
| } | |||
| @Override | |||
| public long getVersion() { | |||
| return version; | |||
| } | |||
| @Override | |||
| public String getName() { | |||
| return name; | |||
| } | |||
| @Override | |||
| public String getDescription() { | |||
| return description; | |||
| } | |||
| @Override | |||
| public List<FieldSpec> getFields() { | |||
| return fieldList; | |||
| } | |||
| @Override | |||
| public List<BinarySliceSpec> getSlices() { | |||
| return sliceList; | |||
| } | |||
| @Override | |||
| public String toHtml() { | |||
| throw new IllegalStateException("Not implemented!"); | |||
| } | |||
| } | |||
| @@ -0,0 +1,32 @@ | |||
| package com.jd.blockchain.binaryproto.impl2; | |||
| import com.jd.blockchain.binaryproto.DataContractEncoder; | |||
| import com.jd.blockchain.utils.io.BytesOutputBuffer; | |||
| import com.jd.blockchain.utils.io.BytesSlice; | |||
| public class DataContractValueConverter extends AbstractDynamicValueConverter { | |||
| private DataContractEncoder contractEncoder; | |||
| public DataContractValueConverter(DataContractEncoder contractEncoder) { | |||
| super(contractEncoder.getContractType()); | |||
| this.contractEncoder =contractEncoder; | |||
| } | |||
| @Override | |||
| public int encodeDynamicValue(Object value, BytesOutputBuffer buffer) { | |||
| BytesOutputBuffer contractBuffer = new BytesOutputBuffer(); | |||
| int size = contractEncoder.encode(value, contractBuffer); | |||
| size += writeSize(size, buffer); | |||
| buffer.write(contractBuffer); | |||
| return size; | |||
| } | |||
| @Override | |||
| public Object decodeValue(BytesSlice dataSlice) { | |||
| return contractEncoder.decode(dataSlice.getInputStream()); | |||
| } | |||
| } | |||
| @@ -0,0 +1,76 @@ | |||
| package com.jd.blockchain.binaryproto.impl2; | |||
| import java.lang.reflect.Array; | |||
| import java.lang.reflect.Method; | |||
| import com.jd.blockchain.binaryproto.BinarySliceSpec; | |||
| import com.jd.blockchain.binaryproto.FieldSpec; | |||
| import com.jd.blockchain.utils.io.BytesInputStream; | |||
| import com.jd.blockchain.utils.io.BytesOutputBuffer; | |||
| import com.jd.blockchain.utils.io.BytesSlice; | |||
| import com.jd.blockchain.utils.io.BytesSlices; | |||
| import com.jd.blockchain.utils.io.DynamicBytesSliceArray; | |||
| import com.jd.blockchain.utils.io.NumberMask; | |||
| public class DynamicArrayFieldEncoder extends AbstractFieldEncoder { | |||
| private DynamicValueConverter valueConverter; | |||
| public DynamicArrayFieldEncoder(BinarySliceSpec sliceSpec, FieldSpec fieldSpec, Method reader, | |||
| DynamicValueConverter valueConverter) { | |||
| super(sliceSpec, fieldSpec, reader); | |||
| this.valueConverter = valueConverter; | |||
| } | |||
| @Override | |||
| public int encode(Object dataContract, BytesOutputBuffer buffer) { | |||
| int size = 0; | |||
| Object[] values = readArrayValue(dataContract); | |||
| size += encodeArrayDynamic(values, buffer); | |||
| return size; | |||
| } | |||
| /** | |||
| * 对数组类型的值进行非固定长度的编码; | |||
| * | |||
| * @param values | |||
| * @param buffer | |||
| * @return | |||
| */ | |||
| private int encodeArrayDynamic(Object[] values, BytesOutputBuffer buffer) { | |||
| int size = 0; | |||
| int count = values == null ? 0 : values.length; | |||
| byte[] countBytes = NumberMask.NORMAL.generateMask(count); | |||
| buffer.write(countBytes); | |||
| size += countBytes.length; | |||
| for (int i = 0; i < count; i++) { | |||
| size += valueConverter.encodeDynamicValue(values[i], buffer); | |||
| } | |||
| return size; | |||
| } | |||
| @Override | |||
| public BytesSlices decode(BytesInputStream bytesStream) { | |||
| return DynamicBytesSliceArray.resolve(bytesStream); | |||
| } | |||
| @Override | |||
| public Object decodeField(BytesSlices fieldBytes) { | |||
| Object[] values = (Object[]) Array.newInstance(valueConverter.getValueType(), fieldBytes.getCount()); | |||
| BytesSlice itemSlice; | |||
| for (int i = 0; i < values.length; i++) { | |||
| itemSlice = fieldBytes.getDataSlice(i); | |||
| if (itemSlice.getSize() == 0) { | |||
| values[i] = valueConverter.getDefaultValue(); | |||
| } else { | |||
| values[i] = valueConverter.decodeValue(itemSlice); | |||
| } | |||
| } | |||
| return values; | |||
| } | |||
| } | |||
| @@ -0,0 +1,86 @@ | |||
| package com.jd.blockchain.binaryproto.impl2; | |||
| import java.lang.reflect.InvocationHandler; | |||
| import java.lang.reflect.Method; | |||
| import java.lang.reflect.Proxy; | |||
| import com.jd.blockchain.utils.io.BytesInputStream; | |||
| import com.jd.blockchain.utils.io.BytesSlices; | |||
| class DynamicDataContract implements InvocationHandler { | |||
| public static Method METHOD_GET_CLASS; | |||
| static { | |||
| try { | |||
| METHOD_GET_CLASS = Object.class.getMethod("getClass"); | |||
| } catch (NoSuchMethodException | SecurityException e) { | |||
| throw new IllegalStateException(e.getMessage(), e); | |||
| } | |||
| } | |||
| private DataContractEncoderImpl contractEncoder; | |||
| // private BytesSlice contractBytes; | |||
| // 字段的数据片段列表,首个是 HeaderSlice,其次是按字段顺序排列的数据片段; | |||
| private BytesSlices[] dataSlices; | |||
| private DynamicDataContract(BytesInputStream bytesStream, DataContractEncoderImpl contractEncoder) { | |||
| this.contractEncoder = contractEncoder; | |||
| init(bytesStream); | |||
| } | |||
| private void init(BytesInputStream bytesStream) { | |||
| // 解析出所有的数据片段; | |||
| dataSlices = new BytesSlices[contractEncoder.getFieldCount() + 1]; | |||
| dataSlices[0] = contractEncoder.getHeaderEncoder().decode(bytesStream); | |||
| for (int i = 1; i < dataSlices.length; i++) { | |||
| dataSlices[i] = contractEncoder.getFieldEncoder(i - 1).decode(bytesStream); | |||
| } | |||
| } | |||
| @SuppressWarnings("unchecked") | |||
| public static <T> T createContract(BytesInputStream bytesStream, DataContractEncoderImpl contractEncoder) { | |||
| return (T) Proxy.newProxyInstance(contractEncoder.getContractType().getClassLoader(), | |||
| contractEncoder.getContractTypeAsArray(), new DynamicDataContract(bytesStream, contractEncoder)); | |||
| } | |||
| @SuppressWarnings("unchecked") | |||
| public static <T> T createContract(byte[] contractBytes, DataContractEncoderImpl contractEncoder) { | |||
| return (T) Proxy.newProxyInstance(contractEncoder.getContractType().getClassLoader(), | |||
| contractEncoder.getContractTypeAsArray(), | |||
| new DynamicDataContract(new BytesInputStream(contractBytes, 0, contractBytes.length), contractEncoder)); | |||
| } | |||
| @Override | |||
| public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { | |||
| int fieldId = contractEncoder.getFieldId(method); | |||
| if (fieldId > -1) { | |||
| FieldEncoder encoder = contractEncoder.getFieldEncoder(fieldId); | |||
| return encoder.decodeField(dataSlices[fieldId + 1]); | |||
| } | |||
| if (METHOD_GET_CLASS == method) { | |||
| return contractEncoder.getContractType(); | |||
| } | |||
| // invoke method declared in type Object; | |||
| Object result; | |||
| try { | |||
| //for some special case, interface's method without annotation | |||
| result = method.invoke(this, args); | |||
| } catch (Exception e) { | |||
| if (method.getReturnType().isPrimitive()) { | |||
| result = 0; | |||
| } | |||
| else { | |||
| result = null; | |||
| } | |||
| e.printStackTrace(); | |||
| } | |||
| return result; | |||
| } | |||
| } | |||
| @@ -0,0 +1,45 @@ | |||
| package com.jd.blockchain.binaryproto.impl2; | |||
| import java.lang.reflect.Method; | |||
| import com.jd.blockchain.binaryproto.BinarySliceSpec; | |||
| import com.jd.blockchain.binaryproto.FieldSpec; | |||
| import com.jd.blockchain.utils.io.BytesInputStream; | |||
| import com.jd.blockchain.utils.io.BytesOutputBuffer; | |||
| import com.jd.blockchain.utils.io.BytesSlices; | |||
| import com.jd.blockchain.utils.io.SingleBytesSliceArray; | |||
| public class DynamicFieldEncoder extends AbstractFieldEncoder { | |||
| private DynamicValueConverter valueConverter; | |||
| public DynamicFieldEncoder(BinarySliceSpec sliceSpec, FieldSpec fieldSpec, Method reader, | |||
| DynamicValueConverter valueConverter) { | |||
| super(sliceSpec, fieldSpec, reader); | |||
| this.valueConverter = valueConverter; | |||
| } | |||
| @Override | |||
| public int encode(Object dataContract, BytesOutputBuffer buffer) { | |||
| int size = 0; | |||
| Object value = readValue(dataContract); | |||
| size += valueConverter.encodeDynamicValue(value, buffer); | |||
| return size; | |||
| } | |||
| @Override | |||
| public BytesSlices decode(BytesInputStream bytesStream) { | |||
| return SingleBytesSliceArray.resolveDynamic(bytesStream); | |||
| } | |||
| @Override | |||
| public Object decodeField(BytesSlices fieldBytes) { | |||
| // 非数组的字段,最多只有一个数据片段; | |||
| if (fieldBytes.getCount() == 0) { | |||
| return valueConverter.getDefaultValue(); | |||
| } | |||
| return valueConverter.decodeValue(fieldBytes.getDataSlice(0)); | |||
| } | |||
| } | |||
| @@ -0,0 +1,18 @@ | |||
| package com.jd.blockchain.binaryproto.impl2; | |||
| import com.jd.blockchain.utils.io.BytesOutputBuffer; | |||
| import com.jd.blockchain.utils.io.BytesSlice; | |||
| public interface DynamicValueConverter extends ValueConverter { | |||
| /** | |||
| * 写入一个动态长度的值;以一个头部的长度字节开始 | |||
| * @param value | |||
| * @param buffer | |||
| * @return | |||
| */ | |||
| int encodeDynamicValue(Object value, BytesOutputBuffer buffer); | |||
| Object decodeValue(BytesSlice dataSlice); | |||
| } | |||
| @@ -0,0 +1,127 @@ | |||
| package com.jd.blockchain.binaryproto.impl2; | |||
| import java.util.LinkedHashSet; | |||
| import java.util.Set; | |||
| import com.jd.blockchain.binaryproto.EnumSpecification; | |||
| import com.jd.blockchain.utils.ValueType; | |||
| /** | |||
| * Created by zhangshuang3 on 2018/6/21. | |||
| */ | |||
| public class EnumSpecificationInfo implements EnumSpecification { | |||
| private ValueType valueType; | |||
| private Class<?> dataType; | |||
| private int code; | |||
| private long version; | |||
| private String name; | |||
| private String description; | |||
| private Set<EnumConstant> items = new LinkedHashSet<>(); | |||
| // private Map<Object, Object> itemCodeMapping = new HashMap<>(); | |||
| // private Map<Object, Object> codeItemMapping = new HashMap<>(); | |||
| public EnumSpecificationInfo(ValueType valueType, int code, long version, String name, String description, Class<?> dataType) { | |||
| this.valueType = valueType; | |||
| this.code = code; | |||
| this.version = version; | |||
| this.name = name; | |||
| this.description = description; | |||
| this.dataType = dataType; | |||
| } | |||
| @Override | |||
| public int getCode() { | |||
| return this.code; | |||
| } | |||
| @Override | |||
| public long getVersion() { | |||
| return this.version; | |||
| } | |||
| @Override | |||
| public String getName() { | |||
| return this.name; | |||
| } | |||
| @Override | |||
| public String getDescription() { | |||
| return this.description; | |||
| } | |||
| @Override | |||
| public ValueType getValueType() { | |||
| return this.valueType; | |||
| } | |||
| @Override | |||
| public int[] getItemValues() { | |||
| int[] values = new int[items.size()]; | |||
| int i = 0; | |||
| for (EnumConstant it : items) { | |||
| values[i] = it.code; | |||
| i++; | |||
| } | |||
| return values; | |||
| } | |||
| @Override | |||
| public String[] getItemNames() { | |||
| String[] names = new String[items.size()]; | |||
| int i = 0; | |||
| for (EnumConstant it : items) { | |||
| names[i] = it.name; | |||
| i++; | |||
| } | |||
| return names; | |||
| } | |||
| public Class<?> getDataType() { | |||
| return dataType; | |||
| } | |||
| public Object[] getConstants() { | |||
| Object[] constants = new Object[items.size()]; | |||
| int i = 0; | |||
| for (EnumConstant it : items) { | |||
| constants[i] = it.constant; | |||
| i++; | |||
| } | |||
| return constants; | |||
| } | |||
| public void addConstant(EnumConstant item) { | |||
| items.add(item); | |||
| } | |||
| public static class EnumConstant { | |||
| private int code; | |||
| private String name; | |||
| private Object constant; | |||
| public int getCode() { | |||
| return code; | |||
| } | |||
| public String getName() { | |||
| return name; | |||
| } | |||
| public Object getConstant() { | |||
| return constant; | |||
| } | |||
| public EnumConstant(int code, String name, Object constant) { | |||
| this.code = code; | |||
| this.name = name; | |||
| this.constant = constant; | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,103 @@ | |||
| package com.jd.blockchain.binaryproto.impl2; | |||
| import com.jd.blockchain.binaryproto.DataContractException; | |||
| import com.jd.blockchain.utils.ValueType; | |||
| import com.jd.blockchain.utils.io.BytesSlice; | |||
| public class EnumValueConverter implements FixedValueConverter { | |||
| private Class<?> enumType; | |||
| private ValueType codeType; | |||
| private int[] values; | |||
| private Object[] constants; | |||
| private FixedValueConverter valueConverter; | |||
| public EnumValueConverter(Class<?> enumType, ValueType codeType, int[] values, Object[] constants, FixedValueConverter valueConverter) { | |||
| this.enumType = enumType; | |||
| this.values = values; | |||
| this.constants = constants; | |||
| this.valueConverter = valueConverter; | |||
| this.codeType = codeType; | |||
| } | |||
| @Override | |||
| public Class<?> getValueType() { | |||
| return enumType; | |||
| } | |||
| @Override | |||
| public Object getDefaultValue() { | |||
| return null; | |||
| } | |||
| //lookup CODE value | |||
| private Object getEnumCode(Object value, int[] codes, Object[] constants) { | |||
| int codeIndex = 0; | |||
| for (int i = 0; i < constants.length; i++) { | |||
| if (value.toString().equals(constants[i].toString())) { | |||
| codeIndex = i; | |||
| break; | |||
| } | |||
| } | |||
| switch (codeType) { | |||
| case INT8: | |||
| return (byte)codes[codeIndex]; | |||
| case INT16: | |||
| return (short)codes[codeIndex]; | |||
| case INT32: | |||
| return codes[codeIndex]; | |||
| default: | |||
| throw new DataContractException(String.format("Enum code error!")); | |||
| } | |||
| } | |||
| @Override | |||
| public int encodeValue(Object value, byte[] buffer, int offset) { | |||
| // 注:由于是通过反射调用的,已经在外围做了检查,此处不需要重复检查枚举值的范围; | |||
| //首先把枚举常量转换成对应的CODE | |||
| Object code = getEnumCode(value, values, constants); | |||
| return valueConverter.encodeValue(code, buffer, offset); | |||
| } | |||
| @Override | |||
| public Object decodeValue(BytesSlice dataSlice) { | |||
| Object v = valueConverter.decodeValue(dataSlice); | |||
| switch (codeType) { | |||
| case INT8: | |||
| for (int i = 0; i < values.length; i++) { | |||
| if ((byte)values[i] == (byte)v) { | |||
| return constants[i]; | |||
| } | |||
| } | |||
| throw new DataContractException(String.format( | |||
| "The decoding value is out of all enum constants! --[value=%s][enum type=%s]", v, enumType.toString())); | |||
| case INT16: | |||
| for (int i = 0; i < values.length; i++) { | |||
| if ((short)values[i] == (short)v) { | |||
| return constants[i]; | |||
| } | |||
| } | |||
| throw new DataContractException(String.format( | |||
| "The decoding value is out of all enum constants! --[value=%s][enum type=%s]", v, enumType.toString())); | |||
| case INT32: | |||
| for (int i = 0; i < values.length; i++) { | |||
| if ((int)values[i] == (int)v) { | |||
| return constants[i]; | |||
| } | |||
| } | |||
| throw new DataContractException(String.format( | |||
| "The decoding value is out of all enum constants! --[value=%s][enum type=%s]", v, enumType.toString())); | |||
| default: | |||
| throw new DataContractException(String.format("Enum code error!")); | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,16 @@ | |||
| package com.jd.blockchain.binaryproto.impl2; | |||
| import java.lang.reflect.Method; | |||
| import com.jd.blockchain.binaryproto.FieldSpec; | |||
| import com.jd.blockchain.utils.io.BytesSlices; | |||
| public interface FieldEncoder extends SliceEncoder { | |||
| Method getReader(); | |||
| FieldSpec getFieldSpecification(); | |||
| Object decodeField(BytesSlices fieldBytes); | |||
| } | |||
| @@ -0,0 +1,121 @@ | |||
| package com.jd.blockchain.binaryproto.impl2; | |||
| import com.jd.blockchain.binaryproto.DataSpecification; | |||
| import com.jd.blockchain.binaryproto.EnumSpecification; | |||
| import com.jd.blockchain.binaryproto.FieldSpec; | |||
| import com.jd.blockchain.utils.ValueType; | |||
| public class FieldSpecInfo implements FieldSpec { | |||
| private int order; | |||
| private String name; | |||
| private String description; | |||
| private boolean repeatable; | |||
| private ValueType primitiveType; | |||
| private EnumSpecification enumSpec; | |||
| private DataSpecification contractTypeSpec; | |||
| private int maxSize; | |||
| private Class<?> dataType; | |||
| private boolean isGenericContract = false; | |||
| public FieldSpecInfo(int order, String name, String decription, ValueType primitiveType, boolean repeatable, | |||
| int maxSize, Class<?> dataType) { | |||
| if (primitiveType == null) { | |||
| throw new IllegalArgumentException("primitiveType is null!"); | |||
| } | |||
| this.order = order; | |||
| this.name = name; | |||
| this.description = decription; | |||
| this.primitiveType = primitiveType; | |||
| this.repeatable = repeatable; | |||
| this.maxSize = maxSize; | |||
| this.dataType = dataType; | |||
| } | |||
| public FieldSpecInfo(int order, String name, String decription, EnumSpecification enumSpec, boolean repeatable, | |||
| Class<?> enumType) { | |||
| if (enumSpec == null) { | |||
| throw new IllegalArgumentException("enum specification is null!"); | |||
| } | |||
| this.order = order; | |||
| this.name = name; | |||
| this.description = decription; | |||
| this.enumSpec = enumSpec; | |||
| this.repeatable = repeatable; | |||
| this.maxSize = -1; | |||
| this.dataType = enumType; | |||
| } | |||
| public FieldSpecInfo(int order, String name, String decription, DataSpecification contractTypeSpec, | |||
| boolean repeatable, Class<?> contractType, boolean isGenericContract) { | |||
| if (contractTypeSpec == null) { | |||
| throw new IllegalArgumentException("contractType is null!"); | |||
| } | |||
| this.order = order; | |||
| this.name = name; | |||
| this.description = decription; | |||
| this.contractTypeSpec = contractTypeSpec; | |||
| this.repeatable = repeatable; | |||
| this.maxSize = -1; | |||
| this.dataType = contractType; | |||
| this.isGenericContract = isGenericContract; | |||
| } | |||
| @Override | |||
| public ValueType getPrimitiveType() { | |||
| return primitiveType; | |||
| } | |||
| @Override | |||
| public EnumSpecification getRefEnum() { | |||
| return enumSpec; | |||
| } | |||
| @Override | |||
| public DataSpecification getRefContract() { | |||
| return contractTypeSpec; | |||
| } | |||
| @Override | |||
| public boolean isRepeatable() { | |||
| return repeatable; | |||
| } | |||
| @Override | |||
| public int getMaxSize() { | |||
| return maxSize; | |||
| } | |||
| @Override | |||
| public String getName() { | |||
| return name; | |||
| } | |||
| @Override | |||
| public String getDescription() { | |||
| return description; | |||
| } | |||
| @Override | |||
| public boolean isGenericContract() { | |||
| return isGenericContract; | |||
| } | |||
| public int getOrder() { | |||
| return order; | |||
| } | |||
| public Class<?> getDataType() { | |||
| return dataType; | |||
| } | |||
| } | |||
| @@ -0,0 +1,79 @@ | |||
| package com.jd.blockchain.binaryproto.impl2; | |||
| import java.lang.reflect.Array; | |||
| import java.lang.reflect.Method; | |||
| import com.jd.blockchain.binaryproto.BinarySliceSpec; | |||
| import com.jd.blockchain.binaryproto.FieldSpec; | |||
| import com.jd.blockchain.utils.io.BytesInputStream; | |||
| import com.jd.blockchain.utils.io.BytesOutputBuffer; | |||
| import com.jd.blockchain.utils.io.BytesSlice; | |||
| import com.jd.blockchain.utils.io.BytesSlices; | |||
| import com.jd.blockchain.utils.io.FixedBytesSliceArray; | |||
| import com.jd.blockchain.utils.io.NumberMask; | |||
| public class FixedArrayFieldEncoder extends AbstractFieldEncoder { | |||
| private FixedValueConverter valueConverter; | |||
| public FixedArrayFieldEncoder(BinarySliceSpec sliceSpec, FieldSpec fieldSpec, Method reader, FixedValueConverter valueConverter) { | |||
| super(sliceSpec, fieldSpec, reader); | |||
| this.valueConverter = valueConverter; | |||
| } | |||
| @Override | |||
| public int encode(Object dataContract, BytesOutputBuffer buffer) { | |||
| int size = 0; | |||
| Object[] values = readArrayValue(dataContract); | |||
| size += encodeArray(values, buffer); | |||
| return size; | |||
| } | |||
| /** | |||
| * 对数组类型的值进行固定长度的编码; | |||
| * | |||
| * @param values | |||
| * @param buffer | |||
| * @return | |||
| */ | |||
| private int encodeArray(Object[] values, BytesOutputBuffer buffer) { | |||
| int count = values == null ? 0 : values.length; | |||
| int counterSize = NumberMask.NORMAL.getMaskLength(count); | |||
| int elementSize = sliceSpec.getLength(); | |||
| int size = counterSize + elementSize * count; | |||
| byte[] outbuff = new byte[size]; | |||
| NumberMask.NORMAL.writeMask(count, outbuff, 0); | |||
| for (int i = 0; i < count; i++) { | |||
| valueConverter.encodeValue(values[i], outbuff, counterSize + elementSize * i); | |||
| } | |||
| buffer.write(outbuff); | |||
| return size; | |||
| } | |||
| @Override | |||
| public BytesSlices decode(BytesInputStream bytesStream) { | |||
| return FixedBytesSliceArray.resolve(bytesStream, sliceSpec.getLength()); | |||
| } | |||
| @Override | |||
| public Object decodeField(BytesSlices fieldBytes) { | |||
| Object[] values = (Object[]) Array.newInstance(valueConverter.getValueType(), fieldBytes.getCount()); | |||
| BytesSlice itemSlice; | |||
| for (int i = 0; i < values.length; i++) { | |||
| itemSlice = fieldBytes.getDataSlice(i); | |||
| if (itemSlice.getSize() == 0) { | |||
| values[i] = valueConverter.getDefaultValue(); | |||
| } else { | |||
| values[i] = valueConverter.decodeValue(itemSlice); | |||
| } | |||
| } | |||
| return values; | |||
| } | |||
| } | |||
| @@ -0,0 +1,58 @@ | |||
| package com.jd.blockchain.binaryproto.impl2; | |||
| import java.lang.reflect.Method; | |||
| import com.jd.blockchain.binaryproto.BinarySliceSpec; | |||
| import com.jd.blockchain.binaryproto.FieldSpec; | |||
| import com.jd.blockchain.utils.io.BytesInputStream; | |||
| import com.jd.blockchain.utils.io.BytesOutputBuffer; | |||
| import com.jd.blockchain.utils.io.BytesSlices; | |||
| import com.jd.blockchain.utils.io.SingleBytesSliceArray; | |||
| public class FixedFieldEncoder extends AbstractFieldEncoder { | |||
| private FixedValueConverter valueConverter; | |||
| public FixedFieldEncoder(BinarySliceSpec sliceSpec, FieldSpec fieldSpec, Method reader, FixedValueConverter valueConverter) { | |||
| super(sliceSpec, fieldSpec, reader); | |||
| this.valueConverter = valueConverter; | |||
| } | |||
| @Override | |||
| public int encode(Object dataContract, BytesOutputBuffer buffer) { | |||
| int size = 0; | |||
| Object value = readValue(dataContract); | |||
| size += encodeValue(value, buffer); | |||
| return size; | |||
| } | |||
| /** | |||
| * 把固定长度的值序列化到指定的缓冲区; | |||
| * | |||
| * @param value | |||
| * @param buffer | |||
| * @return | |||
| */ | |||
| private int encodeValue(Object value, BytesOutputBuffer buffer) { | |||
| byte[] valueBytes = new byte[sliceSpec.getLength()]; | |||
| int size = valueConverter.encodeValue(value, valueBytes, 0); | |||
| buffer.write(valueBytes); | |||
| return size; | |||
| } | |||
| @Override | |||
| public BytesSlices decode(BytesInputStream bytesStream) { | |||
| return SingleBytesSliceArray.create(bytesStream, sliceSpec.getLength()); | |||
| } | |||
| @Override | |||
| public Object decodeField(BytesSlices fieldBytes) { | |||
| // 非数组的字段,最多只有一个数据片段; | |||
| if (fieldBytes.getCount() == 0) { | |||
| return valueConverter.getDefaultValue(); | |||
| } | |||
| return valueConverter.decodeValue(fieldBytes.getDataSlice(0)); | |||
| } | |||
| } | |||
| @@ -0,0 +1,25 @@ | |||
| package com.jd.blockchain.binaryproto.impl2; | |||
| import com.jd.blockchain.utils.io.BytesSlice; | |||
| public interface FixedValueConverter extends ValueConverter { | |||
| /** | |||
| * 将把固定长度的值序列化到指定的缓冲区; | |||
| * <p> | |||
| * | |||
| * 注:实现者应用确保写入的范围不要越界(超出 {@link #getSliceSpecification()} 属性指定的长度); | |||
| * | |||
| * @param value | |||
| * 要序列化的值; | |||
| * @param buffer | |||
| * 保存结果的缓冲区; | |||
| * @param offset | |||
| * 缓冲区的写入起始位置; | |||
| * @return 返回写入的长度; | |||
| */ | |||
| int encodeValue(Object value, byte[] buffer, int offset); | |||
| Object decodeValue(BytesSlice dataSlice); | |||
| } | |||
| @@ -0,0 +1,199 @@ | |||
| //package com.jd.blockchain.binaryproto.impl2; | |||
| // | |||
| //import java.lang.reflect.Array; | |||
| //import java.lang.reflect.InvocationTargetException; | |||
| //import java.lang.reflect.Method; | |||
| // | |||
| //import com.jd.blockchain.binaryproto.BinarySliceSpec; | |||
| //import com.jd.blockchain.binaryproto.FieldSpec; | |||
| // | |||
| //import my.utils.io.BytesInputStream; | |||
| //import my.utils.io.BytesOutputBuffer; | |||
| //import my.utils.io.BytesSlice; | |||
| //import my.utils.io.BytesSlices; | |||
| //import my.utils.io.DynamicBytesSliceArray; | |||
| //import my.utils.io.FixedBytesSliceArray; | |||
| //import my.utils.io.NumberMask; | |||
| //import my.utils.io.SingleBytesSliceArray; | |||
| // | |||
| //public abstract class GenericFieldEncoder implements FieldEncoder { | |||
| // | |||
| // protected BinarySliceSpec sliceSpec; | |||
| // | |||
| // protected FieldSpec fieldSpec; | |||
| // | |||
| // protected Method reader; | |||
| // | |||
| // public GenericFieldEncoder(BinarySliceSpec sliceSpec, FieldSpec fieldSpec, Method reader) { | |||
| // this.sliceSpec = sliceSpec; | |||
| // this.fieldSpec = fieldSpec; | |||
| // this.reader = reader; | |||
| // } | |||
| // | |||
| // @Override | |||
| // public BinarySliceSpec getSliceSpecification() { | |||
| // return sliceSpec; | |||
| // } | |||
| // | |||
| // @Override | |||
| // public FieldSpec getFieldSpecification() { | |||
| // return fieldSpec; | |||
| // } | |||
| // | |||
| // @Override | |||
| // public Method getReader() { | |||
| // return reader; | |||
| // } | |||
| // | |||
| // @Override | |||
| // public int encode(Object dataContract, BytesOutputBuffer buffer) { | |||
| // int size = 0; | |||
| // if (sliceSpec.isRepeatable()) { | |||
| // Object[] values = readArrayValue(dataContract); | |||
| // if (sliceSpec.isDynamic()) { | |||
| // size += encodeArrayDynamic(values, buffer); | |||
| // } else { | |||
| // size += encodeArray(values, buffer); | |||
| // } | |||
| // } else { | |||
| // Object value = readValue(dataContract); | |||
| // if (sliceSpec.isDynamic()) { | |||
| // size += encodeDynamicValue(value, buffer); | |||
| // } else { | |||
| // size += encodeValue(value, buffer); | |||
| // } | |||
| // } | |||
| // | |||
| // return size; | |||
| // } | |||
| // | |||
| // /** | |||
| // * 把固定长度的值序列化到指定的缓冲区; | |||
| // * | |||
| // * @param value | |||
| // * @param buffer | |||
| // * @return | |||
| // */ | |||
| // private int encodeValue(Object value, BytesOutputBuffer buffer) { | |||
| // byte[] valueBytes = new byte[sliceSpec.getLength()]; | |||
| // return encodeValue(value, valueBytes, 0); | |||
| // } | |||
| // | |||
| // /** | |||
| // * 将把固定长度的值序列化到指定的缓冲区; | |||
| // * <p> | |||
| // * | |||
| // * 注:实现者应用确保写入的范围不要越界(超出 {@link #getSliceSpecification()} 属性指定的长度); | |||
| // * | |||
| // * @param value | |||
| // * 要序列化的值; | |||
| // * @param buffer | |||
| // * 保存结果的缓冲区; | |||
| // * @param offset | |||
| // * 缓冲区的写入起始位置; | |||
| // * @return 返回写入的长度; | |||
| // */ | |||
| // abstract int encodeValue(Object value, byte[] buffer, int offset); | |||
| // | |||
| // abstract int encodeDynamicValue(Object value, BytesOutputBuffer buffer); | |||
| // | |||
| // /** | |||
| // * 对数组类型的值进行固定长度的编码; | |||
| // * | |||
| // * @param values | |||
| // * @param buffer | |||
| // * @return | |||
| // */ | |||
| // private int encodeArray(Object[] values, BytesOutputBuffer buffer) { | |||
| // int count = values == null ? 0 : values.length; | |||
| // | |||
| // int counterSize = NumberMask.NORMAL.getMaskLength(count); | |||
| // int elementSize = sliceSpec.getLength(); | |||
| // | |||
| // int size = counterSize + elementSize * count; | |||
| // byte[] outbuff = new byte[size]; | |||
| // NumberMask.NORMAL.writeMask(count, outbuff, 0); | |||
| // | |||
| // for (int i = 0; i < count; i++) { | |||
| // encodeValue(values[i], outbuff, counterSize + elementSize * i); | |||
| // } | |||
| // | |||
| // buffer.write(outbuff); | |||
| // return size; | |||
| // } | |||
| // | |||
| // /** | |||
| // * 对数组类型的值进行非固定长度的编码; | |||
| // * | |||
| // * @param values | |||
| // * @param buffer | |||
| // * @return | |||
| // */ | |||
| // private int encodeArrayDynamic(Object[] values, BytesOutputBuffer buffer) { | |||
| // int size = 0; | |||
| // | |||
| // int count = values == null ? 0 : values.length; | |||
| // byte[] countBytes = NumberMask.NORMAL.generateMask(count); | |||
| // buffer.write(countBytes); | |||
| // size += countBytes.length; | |||
| // | |||
| // for (int i = 0; i < count; i++) { | |||
| // size += encodeDynamicValue(values[i], buffer); | |||
| // } | |||
| // | |||
| // return size; | |||
| // } | |||
| // | |||
| // private Object readValue(Object dataContract) { | |||
| // try { | |||
| // return reader.invoke(dataContract); | |||
| // } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { | |||
| // throw new IllegalStateException(e.getMessage(), e); | |||
| // } | |||
| // } | |||
| // | |||
| // private Object[] readArrayValue(Object dataContract) { | |||
| // return (Object[]) readValue(dataContract); | |||
| // } | |||
| // | |||
| // @Override | |||
| // public BytesSlices decode(BytesInputStream bytesStream) { | |||
| // if (sliceSpec.isRepeatable()) { | |||
| // if (sliceSpec.isDynamic()) { | |||
| // return DynamicBytesSliceArray.resolve(bytesStream); | |||
| // }else { | |||
| // return FixedBytesSliceArray.resolve(bytesStream, sliceSpec.getLength()); | |||
| // } | |||
| // }else { | |||
| // if (sliceSpec.isDynamic()) { | |||
| // return SingleBytesSliceArray.resolveDynamic(bytesStream); | |||
| // }else { | |||
| // return SingleBytesSliceArray.create(bytesStream, sliceSpec.getLength()); | |||
| // } | |||
| // } | |||
| // } | |||
| // | |||
| // @Override | |||
| // public Object decodeField(BytesSlices fieldBytes) { | |||
| // if (sliceSpec.isRepeatable()) { | |||
| // Object[] values = (Object[]) Array.newInstance(getFieldType(), fieldBytes.getCount()); | |||
| // for (int i = 0; i < values.length; i++) { | |||
| // values[i] = decodeValue(fieldBytes.getDataSlice(i)); | |||
| // } | |||
| // return values; | |||
| // } | |||
| // //非数组的字段,最多只有一个数据片段; | |||
| // if (fieldBytes.getCount() == 0) { | |||
| // return getNullValue(); | |||
| // } | |||
| // return decodeValue(fieldBytes.getDataSlice(0)); | |||
| // } | |||
| // | |||
| // | |||
| // abstract Class<?> getFieldType(); | |||
| // | |||
| // abstract Object getNullValue(); | |||
| // | |||
| // abstract Object decodeValue(BytesSlice dataSlice); | |||
| // | |||
| //} | |||
| @@ -0,0 +1,102 @@ | |||
| package com.jd.blockchain.binaryproto.impl2; | |||
| import com.jd.blockchain.binaryproto.BinarySliceSpec; | |||
| import com.jd.blockchain.utils.io.BytesInputStream; | |||
| import com.jd.blockchain.utils.io.BytesOutputBuffer; | |||
| import com.jd.blockchain.utils.io.BytesSlice; | |||
| import com.jd.blockchain.utils.io.BytesSliceArrayWrapper; | |||
| import com.jd.blockchain.utils.io.BytesSlices; | |||
| import com.jd.blockchain.utils.io.BytesUtils; | |||
| /** | |||
| * 分段编码器; | |||
| * | |||
| * @author huanghaiquan | |||
| * | |||
| */ | |||
| public class HeaderEncoder implements SliceEncoder { | |||
| public static final int HEAD_BYTES = 12; | |||
| private BinarySliceSpec sliceSpec; | |||
| private int code; | |||
| private long version; | |||
| private String name; | |||
| private String description; | |||
| public int getCode() { | |||
| return code; | |||
| } | |||
| public long getVersion() { | |||
| return version; | |||
| } | |||
| public String getName() { | |||
| return name; | |||
| } | |||
| public String getDescription() { | |||
| return description; | |||
| } | |||
| public HeaderEncoder(BinarySliceSpec sliceSpec, int code, long version, String name, String description) { | |||
| this.sliceSpec = sliceSpec; | |||
| this.code = code; | |||
| this.version = version; | |||
| this.name = name; | |||
| this.description = description; | |||
| } | |||
| /** | |||
| * 校验指定的数据契约字节列表的头部是否一致; | |||
| * | |||
| * @param dataContractBytes | |||
| * @return | |||
| */ | |||
| boolean verifyHeaders(BytesSlice dataContractBytes) { | |||
| int code = resolveCode(dataContractBytes); | |||
| if (code != this.code) { | |||
| return false; | |||
| } | |||
| long version = resolveVersion(dataContractBytes); | |||
| return version == this.version; | |||
| } | |||
| public static int resolveCode(BytesSlice dataContractBytes) { | |||
| return dataContractBytes.getInt(); | |||
| } | |||
| public static long resolveVersion(BytesSlice dataContractBytes) { | |||
| return dataContractBytes.getLong(4); | |||
| } | |||
| @Override | |||
| public BinarySliceSpec getSliceSpecification() { | |||
| return sliceSpec; | |||
| } | |||
| @Override | |||
| public int encode(Object dataContract, BytesOutputBuffer buffer) { | |||
| byte[] headBytes = new byte[HEAD_BYTES]; | |||
| BytesUtils.toBytes(code, headBytes); | |||
| BytesUtils.toBytes(version, headBytes, 4); | |||
| buffer.write(headBytes); | |||
| return HEAD_BYTES; | |||
| } | |||
| @Override | |||
| public BytesSlices decode(BytesInputStream bytesStream) { | |||
| return BytesSliceArrayWrapper.wrap(bytesStream.readSlice(HEAD_BYTES)); | |||
| } | |||
| // @Override | |||
| // public BytesSlices decode(byte[] dataContractBytes, int offset) { | |||
| // return SingleBytesSliceArray.create(dataContractBytes, offset, HEAD_BYTES); | |||
| // } | |||
| } | |||
| @@ -0,0 +1,29 @@ | |||
| package com.jd.blockchain.binaryproto.impl2; | |||
| import com.jd.blockchain.utils.io.BytesSlice; | |||
| import com.jd.blockchain.utils.io.BytesUtils; | |||
| public class Int16CharConverter implements FixedValueConverter{ | |||
| @Override | |||
| public Class<?> getValueType() { | |||
| return char.class; | |||
| } | |||
| @Override | |||
| public Object getDefaultValue() { | |||
| return '\u0000'; | |||
| } | |||
| @Override | |||
| public int encodeValue(Object value, byte[] buffer, int offset) { | |||
| BytesUtils.toBytes((char)value, buffer, offset); | |||
| return 2; | |||
| } | |||
| @Override | |||
| public Object decodeValue(BytesSlice dataSlice) { | |||
| return dataSlice.getChar(); | |||
| } | |||
| } | |||
| @@ -0,0 +1,29 @@ | |||
| package com.jd.blockchain.binaryproto.impl2; | |||
| import com.jd.blockchain.utils.io.BytesSlice; | |||
| import com.jd.blockchain.utils.io.BytesUtils; | |||
| public class Int16CharWrapperConverter implements FixedValueConverter{ | |||
| @Override | |||
| public Class<?> getValueType() { | |||
| return Character.class; | |||
| } | |||
| @Override | |||
| public Object getDefaultValue() { | |||
| return null; | |||
| } | |||
| @Override | |||
| public int encodeValue(Object value, byte[] buffer, int offset) { | |||
| BytesUtils.toBytes((char)value, buffer, offset); | |||
| return 2; | |||
| } | |||
| @Override | |||
| public Object decodeValue(BytesSlice dataSlice) { | |||
| return dataSlice.getChar(); | |||
| } | |||
| } | |||
| @@ -0,0 +1,29 @@ | |||
| package com.jd.blockchain.binaryproto.impl2; | |||
| import com.jd.blockchain.utils.io.BytesSlice; | |||
| import com.jd.blockchain.utils.io.BytesUtils; | |||
| public class Int16ShortConverter implements FixedValueConverter{ | |||
| @Override | |||
| public Class<?> getValueType() { | |||
| return short.class; | |||
| } | |||
| @Override | |||
| public Object getDefaultValue() { | |||
| return 0; | |||
| } | |||
| @Override | |||
| public int encodeValue(Object value, byte[] buffer, int offset) { | |||
| BytesUtils.toBytes((short)value, buffer, offset); | |||
| return 2; | |||
| } | |||
| @Override | |||
| public Object decodeValue(BytesSlice dataSlice) { | |||
| return dataSlice.getShort(); | |||
| } | |||
| } | |||
| @@ -0,0 +1,29 @@ | |||
| package com.jd.blockchain.binaryproto.impl2; | |||
| import com.jd.blockchain.utils.io.BytesSlice; | |||
| import com.jd.blockchain.utils.io.BytesUtils; | |||
| public class Int16ShortWrapperConverter implements FixedValueConverter{ | |||
| @Override | |||
| public Class<?> getValueType() { | |||
| return Short.class; | |||
| } | |||
| @Override | |||
| public Object getDefaultValue() { | |||
| return null; | |||
| } | |||
| @Override | |||
| public int encodeValue(Object value, byte[] buffer, int offset) { | |||
| BytesUtils.toBytes((short)value, buffer, offset); | |||
| return 2; | |||
| } | |||
| @Override | |||
| public Object decodeValue(BytesSlice dataSlice) { | |||
| return dataSlice.getShort(); | |||
| } | |||
| } | |||
| @@ -0,0 +1,29 @@ | |||
| package com.jd.blockchain.binaryproto.impl2; | |||
| import com.jd.blockchain.utils.io.BytesSlice; | |||
| import com.jd.blockchain.utils.io.BytesUtils; | |||
| public class Int32IntConverter implements FixedValueConverter{ | |||
| @Override | |||
| public Class<?> getValueType() { | |||
| return int.class; | |||
| } | |||
| @Override | |||
| public Object getDefaultValue() { | |||
| return 0; | |||
| } | |||
| @Override | |||
| public int encodeValue(Object value, byte[] buffer, int offset) { | |||
| BytesUtils.toBytes((int)value, buffer, offset); | |||
| return 4; | |||
| } | |||
| @Override | |||
| public Object decodeValue(BytesSlice dataSlice) { | |||
| return dataSlice.getInt(); | |||
| } | |||
| } | |||
| @@ -0,0 +1,29 @@ | |||
| package com.jd.blockchain.binaryproto.impl2; | |||
| import com.jd.blockchain.utils.io.BytesSlice; | |||
| import com.jd.blockchain.utils.io.BytesUtils; | |||
| public class Int32IntWrapperConverter implements FixedValueConverter{ | |||
| @Override | |||
| public Class<?> getValueType() { | |||
| return Integer.class; | |||
| } | |||
| @Override | |||
| public Object getDefaultValue() { | |||
| return null; | |||
| } | |||
| @Override | |||
| public int encodeValue(Object value, byte[] buffer, int offset) { | |||
| BytesUtils.toBytes((int)value, buffer, offset); | |||
| return 4; | |||
| } | |||
| @Override | |||
| public Object decodeValue(BytesSlice dataSlice) { | |||
| return dataSlice.getInt(); | |||
| } | |||
| } | |||
| @@ -0,0 +1,29 @@ | |||
| package com.jd.blockchain.binaryproto.impl2; | |||
| import com.jd.blockchain.utils.io.BytesSlice; | |||
| import com.jd.blockchain.utils.io.BytesUtils; | |||
| public class Int64LongConverter implements FixedValueConverter{ | |||
| @Override | |||
| public Class<?> getValueType() { | |||
| return Long.class; | |||
| } | |||
| @Override | |||
| public Object getDefaultValue() { | |||
| return 0; | |||
| } | |||
| @Override | |||
| public int encodeValue(Object value, byte[] buffer, int offset) { | |||
| BytesUtils.toBytes((long)value, buffer, offset); | |||
| return 8; | |||
| } | |||
| @Override | |||
| public Object decodeValue(BytesSlice dataSlice) { | |||
| return dataSlice.getLong(); | |||
| } | |||
| } | |||
| @@ -0,0 +1,29 @@ | |||
| package com.jd.blockchain.binaryproto.impl2; | |||
| import com.jd.blockchain.utils.io.BytesSlice; | |||
| import com.jd.blockchain.utils.io.BytesUtils; | |||
| public class Int64LongWrapperConverter implements FixedValueConverter{ | |||
| @Override | |||
| public Class<?> getValueType() { | |||
| return Long.class; | |||
| } | |||
| @Override | |||
| public Object getDefaultValue() { | |||
| return null; | |||
| } | |||
| @Override | |||
| public int encodeValue(Object value, byte[] buffer, int offset) { | |||
| BytesUtils.toBytes((long)value, buffer, offset); | |||
| return 8; | |||
| } | |||
| @Override | |||
| public Object decodeValue(BytesSlice dataSlice) { | |||
| return dataSlice.getLong(); | |||
| } | |||
| } | |||
| @@ -0,0 +1,28 @@ | |||
| package com.jd.blockchain.binaryproto.impl2; | |||
| import com.jd.blockchain.utils.io.BytesSlice; | |||
| public class Int8ByteConverter implements FixedValueConverter{ | |||
| @Override | |||
| public Class<?> getValueType() { | |||
| return byte.class; | |||
| } | |||
| @Override | |||
| public Object getDefaultValue() { | |||
| return 0; | |||
| } | |||
| @Override | |||
| public int encodeValue(Object value, byte[] buffer, int offset) { | |||
| buffer[offset] = (byte)value; | |||
| return 1; | |||
| } | |||
| @Override | |||
| public Object decodeValue(BytesSlice dataSlice) { | |||
| return dataSlice.getByte(); | |||
| } | |||
| } | |||
| @@ -0,0 +1,28 @@ | |||
| package com.jd.blockchain.binaryproto.impl2; | |||
| import com.jd.blockchain.utils.io.BytesSlice; | |||
| public class Int8ByteWrapperConverter implements FixedValueConverter{ | |||
| @Override | |||
| public Class<?> getValueType() { | |||
| return Byte.class; | |||
| } | |||
| @Override | |||
| public Object getDefaultValue() { | |||
| return null; | |||
| } | |||
| @Override | |||
| public int encodeValue(Object value, byte[] buffer, int offset) { | |||
| buffer[offset] = (byte)value; | |||
| return 1; | |||
| } | |||
| @Override | |||
| public Object decodeValue(BytesSlice dataSlice) { | |||
| return dataSlice.getByte(); | |||
| } | |||
| } | |||
| @@ -0,0 +1,31 @@ | |||
| //package com.jd.blockchain.binaryproto.impl2; | |||
| // | |||
| //import java.lang.reflect.Method; | |||
| // | |||
| //import com.jd.blockchain.binaryproto.BinarySliceSpec; | |||
| //import com.jd.blockchain.binaryproto.FieldSpec; | |||
| // | |||
| //import my.utils.io.BytesOutputBuffer; | |||
| // | |||
| //public class RepeatableFieldEncoder extends AbstractFieldEncoder { | |||
| // | |||
| // | |||
| // public RepeatableFieldEncoder(BinarySliceSpec sliceSpec, FieldSpec fieldSpec, Method reader) { | |||
| // super(sliceSpec, fieldSpec, reader); | |||
| // } | |||
| // | |||
| // @Override | |||
| // public Object decodeValue(byte[] dataContractBytes, int offset) { | |||
| // // TODO Auto-generated method stub | |||
| // return null; | |||
| // } | |||
| // | |||
| // @Override | |||
| // public int encode(Object dataContract, BytesOutputBuffer buffer) { | |||
| // // TODO Auto-generated method stub | |||
| // return 0; | |||
| // } | |||
| // | |||
| // | |||
| // | |||
| //} | |||
| @@ -0,0 +1,55 @@ | |||
| package com.jd.blockchain.binaryproto.impl2; | |||
| import com.jd.blockchain.binaryproto.BinarySliceSpec; | |||
| import com.jd.blockchain.utils.io.BytesInputStream; | |||
| import com.jd.blockchain.utils.io.BytesOutputBuffer; | |||
| import com.jd.blockchain.utils.io.BytesSlices; | |||
| /** | |||
| * 分段编码器; | |||
| * | |||
| * @author huanghaiquan | |||
| * | |||
| */ | |||
| public interface SliceEncoder { | |||
| BinarySliceSpec getSliceSpecification(); | |||
| /** | |||
| * 将此编码器表示的数据契约分段输出的指定的缓冲区; | |||
| * | |||
| * @param dataContract | |||
| * 数据契约的实例;当前编码器从中读取分段的值; | |||
| * @param buffer | |||
| * 要写入的缓冲区;调用者需要确保 | |||
| * @param offset | |||
| * 缓冲区的写入起始位置; | |||
| * @return 写入的字节数; | |||
| */ | |||
| int encode(Object dataContract, BytesOutputBuffer buffer); | |||
| // /** | |||
| // * 从指定的数据契约的数据中读取当前编码器表示的分段的数据; | |||
| // * | |||
| // * @param dataContractBytes | |||
| // * @return | |||
| // */ | |||
| // BytesSlices decode(BytesSlice dataContractBytes, int offset); | |||
| /** | |||
| * 从指定的数据契约的数据中读取当前编码器表示的分段的数据; | |||
| * | |||
| * @param dataContractBytes | |||
| * @return | |||
| */ | |||
| BytesSlices decode(BytesInputStream bytesStream); | |||
| // /** | |||
| // * 从指定的数据契约的数据中读取当前编码器表示的分段的数据; | |||
| // * | |||
| // * @param dataContractBytes | |||
| // * @return | |||
| // */ | |||
| // BytesSlices decode(byte[] dataContractBytes, int offset); | |||
| } | |||
| @@ -0,0 +1,27 @@ | |||
| package com.jd.blockchain.binaryproto.impl2; | |||
| import com.jd.blockchain.utils.io.BytesOutputBuffer; | |||
| import com.jd.blockchain.utils.io.BytesSlice; | |||
| import com.jd.blockchain.utils.io.BytesUtils; | |||
| public class StringValueConverter extends AbstractDynamicValueConverter { | |||
| public StringValueConverter() { | |||
| super(String.class); | |||
| } | |||
| @Override | |||
| public Object decodeValue(BytesSlice dataSlice) { | |||
| return dataSlice.getString(); | |||
| } | |||
| @Override | |||
| public int encodeDynamicValue(Object value, BytesOutputBuffer buffer) { | |||
| byte[] bytes = value == null ? BytesUtils.EMPTY_BYTES : BytesUtils.toBytes((String) value); | |||
| int size = bytes.length; | |||
| size += writeSize(bytes.length, buffer); | |||
| buffer.write(bytes); | |||
| return size; | |||
| } | |||
| } | |||
| @@ -0,0 +1,14 @@ | |||
| package com.jd.blockchain.binaryproto.impl2; | |||
| public interface ValueConverter { | |||
| Class<?> getValueType(); | |||
| /** | |||
| * 返回类型的默认初始值; | |||
| * | |||
| * @return | |||
| */ | |||
| Object getDefaultValue(); | |||
| } | |||
| @@ -0,0 +1,547 @@ | |||
| package test.com.jd.blockchain.binaryproto; | |||
| import static org.junit.Assert.assertEquals; | |||
| import static org.junit.Assert.assertNotNull; | |||
| import static org.junit.Assert.assertNull; | |||
| import static org.junit.Assert.assertTrue; | |||
| import org.junit.Test; | |||
| import com.jd.blockchain.binaryproto.BinaryEncodingUtils; | |||
| import com.jd.blockchain.binaryproto.DataContractException; | |||
| import com.jd.blockchain.binaryproto.DataContractRegistry; | |||
| import com.jd.blockchain.utils.Bytes; | |||
| import com.jd.blockchain.utils.io.BytesEncoding; | |||
| import com.jd.blockchain.utils.io.BytesUtils; | |||
| import com.jd.blockchain.utils.io.NumberMask; | |||
| import com.jd.blockchain.utils.net.NetworkAddress; | |||
| public class BinaryEncodingTest { | |||
| /** | |||
| * 此测试用例是对序列化过程的验证,包括:头部、基本类型的值(boolean、整数、字符串、字节数组、BytesSerializable)、字段顺序的正确性; | |||
| */ | |||
| @Test | |||
| public void testEncoding_Header_OrderedPrimitiveFields() { | |||
| DataContractRegistry.register(PrimitiveDatas.class); | |||
| PrimitiveDatasImpl pd = new PrimitiveDatasImpl(); | |||
| NetworkAddress networkAddress = new NetworkAddress("192.168.1.1", 9001, false); | |||
| pd.setId(123); | |||
| pd.setEnable(true); | |||
| pd.setBoy((byte) 10); | |||
| pd.setAge((short) 100); | |||
| pd.setName("John"); | |||
| pd.setImage("Image of John".getBytes()); | |||
| pd.setFlag('x'); | |||
| pd.setValue(93239232); | |||
| pd.setConfig(Bytes.fromString("Configuration of something.")); | |||
| pd.setNetworkAddress(networkAddress); | |||
| byte[] bytes = BinaryEncodingUtils.encode(pd, PrimitiveDatas.class); | |||
| int offset = 0; | |||
| int code = BytesUtils.toInt(bytes, offset); | |||
| offset += 12; | |||
| assertEquals(0x05, code); | |||
| offset = assertFieldEncoding(pd, bytes, offset); | |||
| // 到了结尾; | |||
| assertEquals(bytes.length, offset); | |||
| } | |||
| private int assertFieldEncoding(PrimitiveDatas pd, byte[] bytes, int offset) { | |||
| // Code 和 Version 的占据了 12 字节; | |||
| int id = BytesUtils.toInt(bytes, offset); | |||
| offset += 4; | |||
| assertEquals(pd.getId(), id); | |||
| byte enable = bytes[offset]; | |||
| byte expEnable = pd.isEnable() ? (byte) 1 : (byte) 0; | |||
| offset += 1; | |||
| assertEquals(expEnable, enable); | |||
| byte boy = bytes[offset]; | |||
| offset += 1; | |||
| assertEquals(pd.isBoy(), boy); | |||
| short age = BytesUtils.toShort(bytes, offset); | |||
| offset += 2; | |||
| assertEquals(pd.getAge(), age); | |||
| byte[] nameBytes = BytesEncoding.readInNormal(bytes, offset); | |||
| String name = BytesUtils.toString(nameBytes); | |||
| int maskLen = NumberMask.NORMAL.resolveMaskLength(bytes[offset]); | |||
| offset += nameBytes.length + maskLen; | |||
| assertEquals(pd.getName().length(), name.length()); | |||
| long value = BytesUtils.toLong(bytes, offset); | |||
| offset += 8; | |||
| assertEquals(pd.getValue(), value); | |||
| byte[] image = BytesEncoding.readInNormal(bytes, offset); | |||
| maskLen = NumberMask.NORMAL.resolveMaskLength(bytes[offset]); | |||
| offset += image.length + maskLen; | |||
| assertTrue(BytesUtils.equals(pd.getImage(), image)); | |||
| char flag = BytesUtils.toChar(bytes, offset); | |||
| offset += 2; | |||
| assertEquals(pd.getFlag(), flag); | |||
| byte[] config = BytesEncoding.readInNormal(bytes, offset); | |||
| maskLen = NumberMask.NORMAL.resolveMaskLength(bytes[offset]); | |||
| offset += config.length + maskLen; | |||
| assertTrue(BytesUtils.equals(pd.getConfig().toBytes(), config)); | |||
| byte[] setting = BytesEncoding.readInNormal(bytes, offset); | |||
| assertEquals(0, setting.length); | |||
| maskLen = NumberMask.NORMAL.resolveMaskLength(bytes[offset]); | |||
| offset += setting.length + maskLen; | |||
| byte[] networkaddr = BytesEncoding.readInNormal(bytes, offset); | |||
| maskLen = NumberMask.NORMAL.resolveMaskLength(bytes[offset]); | |||
| offset += networkaddr.length + maskLen; | |||
| assertTrue(BytesUtils.equals(pd.getNetworkAddr().toBytes(), networkaddr)); | |||
| return offset; | |||
| } | |||
| @Test | |||
| public void testEncoding_Null() { | |||
| DataContractRegistry.register(PrimitiveDatas.class); | |||
| byte[] bytes = BinaryEncodingUtils.encode(null, PrimitiveDatas.class); | |||
| int offset = 0; | |||
| int code = BytesUtils.toInt(bytes, offset); | |||
| offset += 12;// 暂不校验 version; | |||
| assertEquals(0x05, code); | |||
| // 到了结尾; | |||
| assertEquals(bytes.length, offset); | |||
| } | |||
| /** | |||
| * 此测试用例是对序列化过程的验证,包括:头部、基本类型的值(boolean、整数、字符串、字节数组、BytesSerializable)、字段顺序的正确性; | |||
| */ | |||
| @Test | |||
| public void testDecoding_Header_OrderedPrimitiveFields() { | |||
| DataContractRegistry.register(PrimitiveDatas.class); | |||
| PrimitiveDatasImpl pd = new PrimitiveDatasImpl(); | |||
| NetworkAddress networkAddress = new NetworkAddress("192.168.1.1", 9001, false); | |||
| pd.setId(123); | |||
| pd.setEnable(true); | |||
| pd.setBoy((byte) 10); | |||
| pd.setAge((short) 100); | |||
| pd.setName("John"); | |||
| pd.setImage("Image of John".getBytes()); | |||
| pd.setFlag('x'); | |||
| pd.setValue(93239232); | |||
| pd.setConfig(Bytes.fromString("Configuration of something.")); | |||
| pd.setNetworkAddress(networkAddress); | |||
| byte[] bytes = BinaryEncodingUtils.encode(pd, PrimitiveDatas.class); | |||
| PrimitiveDatas decodeData = BinaryEncodingUtils.decode(bytes); | |||
| assertEquals(pd.getId(), decodeData.getId()); | |||
| assertEquals(pd.isEnable(), decodeData.isEnable()); | |||
| assertEquals(pd.isBoy(), decodeData.isBoy()); | |||
| assertEquals(pd.getAge(), decodeData.getAge()); | |||
| assertEquals(pd.getName(), decodeData.getName()); | |||
| assertTrue(BytesUtils.equals(pd.getImage(), decodeData.getImage())); | |||
| assertEquals(pd.getFlag(), decodeData.getFlag()); | |||
| assertEquals(pd.getValue(), decodeData.getValue()); | |||
| assertEquals(pd.getConfig(), decodeData.getConfig()); | |||
| assertNull(decodeData.getSetting()); | |||
| assertEquals(pd.getNetworkAddr().getHost(), decodeData.getNetworkAddr().getHost()); | |||
| assertEquals(pd.getNetworkAddr().getPort(), decodeData.getNetworkAddr().getPort()); | |||
| } | |||
| /** | |||
| * 此测试用例是对序列化过程的验证,包括:头部、枚举值、字段顺序的正确性; | |||
| */ | |||
| @Test | |||
| public void testEncoding_Header_OrderedEnumFields() { | |||
| DataContractRegistry.register(EnumDatas.class); | |||
| EnumDatasImpl enumDatas = new EnumDatasImpl(); | |||
| enumDatas.setLevel(EnumLevel.V1); | |||
| byte[] bytes = BinaryEncodingUtils.encode(enumDatas, EnumDatas.class); | |||
| int offset = 0; | |||
| int code = BytesUtils.toInt(bytes, offset); | |||
| offset += 12; | |||
| assertEquals(0x07, code); | |||
| byte enumCode = bytes[offset]; | |||
| offset += 1; | |||
| assertEquals(enumDatas.getLevel().CODE, enumCode); | |||
| // 到了结尾; | |||
| assertEquals(bytes.length, offset); | |||
| } | |||
| /** | |||
| * 此测试用例是对反序列化过程的验证,包括:头部、枚举值、字段顺序的正确性; | |||
| */ | |||
| @Test | |||
| public void testDecoding_Header_OrderedEnumFields() { | |||
| DataContractRegistry.register(EnumDatas.class); | |||
| EnumDatasImpl enumDatas = new EnumDatasImpl(); | |||
| enumDatas.setLevel(EnumLevel.V1); | |||
| byte[] bytes = BinaryEncodingUtils.encode(enumDatas, EnumDatas.class); | |||
| EnumDatas decodeData = BinaryEncodingUtils.decode(bytes); | |||
| assertEquals(enumDatas.getLevel(), decodeData.getLevel()); | |||
| } | |||
| /** | |||
| * 此测试用例是对序列化过程的验证,包括:头部、引用数据契约字段值、字段顺序的正确性; | |||
| */ | |||
| @Test | |||
| public void testEncoding_Header_SingleRefContractField() { | |||
| DataContractRegistry.register(RefContractDatas.class); | |||
| RefContractDatasImpl refContractDatas = new RefContractDatasImpl(); | |||
| PrimitiveDatasImpl primitiveDatas = new PrimitiveDatasImpl(); | |||
| NetworkAddress networkAddress = new NetworkAddress("192.168.1.1", 9001, false); | |||
| primitiveDatas.setId(123); | |||
| primitiveDatas.setEnable(true); | |||
| primitiveDatas.setBoy((byte) 10); | |||
| primitiveDatas.setAge((short) 100); | |||
| primitiveDatas.setName("John"); | |||
| primitiveDatas.setImage("Image of John".getBytes()); | |||
| primitiveDatas.setFlag('x'); | |||
| primitiveDatas.setValue(93239232); | |||
| primitiveDatas.setConfig(Bytes.fromString("Configuration of something.")); | |||
| primitiveDatas.setNetworkAddress(networkAddress); | |||
| refContractDatas.setPrimitiveDatas(primitiveDatas); | |||
| byte[] bytes = BinaryEncodingUtils.encode(refContractDatas, RefContractDatas.class); | |||
| int offset = 0; | |||
| int code = BytesUtils.toInt(bytes, offset); | |||
| offset += 12; | |||
| assertEquals(0x08, code); | |||
| // 引用合约字段的字节; | |||
| byte[] primitiveDataFieldBytes = BytesEncoding.readInNormal(bytes, offset); | |||
| int maskLen = NumberMask.NORMAL.resolveMaskLength(bytes[offset]); | |||
| // 获得引用合约码与版本 | |||
| int fieldOffset = 0; | |||
| int refCode = BytesUtils.toInt(primitiveDataFieldBytes, fieldOffset); | |||
| fieldOffset += 12; | |||
| assertEquals(0x05, refCode); | |||
| fieldOffset = assertFieldEncoding(refContractDatas.getPrimitive(), primitiveDataFieldBytes, fieldOffset); | |||
| assertEquals(primitiveDataFieldBytes.length, fieldOffset); | |||
| offset += primitiveDataFieldBytes.length + maskLen; | |||
| // 到了结尾; | |||
| assertEquals(bytes.length, offset); | |||
| } | |||
| /** | |||
| * 此测试用例是对序列化过程的验证,包括:头部、引用数据契约字段值、字段顺序的正确性; | |||
| */ | |||
| @Test | |||
| public void testEncoding_Header_NullRefContractFields() { | |||
| DataContractRegistry.register(RefContractDatas.class); | |||
| RefContractDatasImpl refContractDatas = new RefContractDatasImpl(); | |||
| byte[] bytes = BinaryEncodingUtils.encode(refContractDatas, RefContractDatas.class); | |||
| int offset = 0; | |||
| int code = BytesUtils.toInt(bytes, offset); | |||
| offset += 12; | |||
| assertEquals(0x08, code); | |||
| // 引用合约字段的字节; | |||
| byte[] primitiveDataFieldBytes = BytesEncoding.readInNormal(bytes, offset); | |||
| int maskLen = NumberMask.NORMAL.resolveMaskLength(bytes[offset]); | |||
| assertEquals(12, primitiveDataFieldBytes.length); | |||
| // 获得引用合约码与版本 | |||
| int fieldOffset = 0; | |||
| int refCode = BytesUtils.toInt(primitiveDataFieldBytes, fieldOffset); | |||
| fieldOffset += 12; | |||
| assertEquals(0x05, refCode); | |||
| assertEquals(primitiveDataFieldBytes.length, fieldOffset); | |||
| offset += primitiveDataFieldBytes.length + maskLen; | |||
| // 到了结尾; | |||
| assertEquals(bytes.length, offset); | |||
| } | |||
| /** | |||
| * 此测试用例是对反序列化过程的验证,包括:头部、引用数据契约字段值、字段顺序的正确性; | |||
| */ | |||
| @Test | |||
| public void testDecoding_Header_OrderedRefContractFields() { | |||
| DataContractRegistry.register(RefContractDatas.class); | |||
| RefContractDatasImpl refContractDatas = new RefContractDatasImpl(); | |||
| PrimitiveDatasImpl primitiveDatas = new PrimitiveDatasImpl(); | |||
| NetworkAddress networkAddress = new NetworkAddress("192.168.1.1", 9001, false); | |||
| primitiveDatas.setId(123); | |||
| primitiveDatas.setEnable(true); | |||
| primitiveDatas.setBoy((byte) 10); | |||
| primitiveDatas.setAge((short) 100); | |||
| primitiveDatas.setName("John"); | |||
| primitiveDatas.setImage("Image of John".getBytes()); | |||
| primitiveDatas.setFlag('x'); | |||
| primitiveDatas.setValue(93239232); | |||
| primitiveDatas.setConfig(Bytes.fromString("Configuration of something.")); | |||
| primitiveDatas.setNetworkAddress(networkAddress); | |||
| refContractDatas.setPrimitiveDatas(primitiveDatas); | |||
| byte[] bytes = BinaryEncodingUtils.encode(refContractDatas, RefContractDatas.class); | |||
| RefContractDatas decodeData = BinaryEncodingUtils.decode(bytes); | |||
| assertEquals(refContractDatas.getPrimitive().getId(), decodeData.getPrimitive().getId()); | |||
| assertEquals(refContractDatas.getPrimitive().isEnable(), decodeData.getPrimitive().isEnable()); | |||
| assertEquals(refContractDatas.getPrimitive().isBoy(), decodeData.getPrimitive().isBoy()); | |||
| assertEquals(refContractDatas.getPrimitive().getAge(), decodeData.getPrimitive().getAge()); | |||
| assertEquals(refContractDatas.getPrimitive().getName(), decodeData.getPrimitive().getName()); | |||
| assertTrue(BytesUtils.equals(refContractDatas.getPrimitive().getImage(), decodeData.getPrimitive().getImage())); | |||
| assertEquals(refContractDatas.getPrimitive().getFlag(), decodeData.getPrimitive().getFlag()); | |||
| assertEquals(refContractDatas.getPrimitive().getValue(), decodeData.getPrimitive().getValue()); | |||
| assertEquals(refContractDatas.getPrimitive().getConfig(), decodeData.getPrimitive().getConfig()); | |||
| assertEquals(refContractDatas.getPrimitive().getNetworkAddr().getHost(), decodeData.getPrimitive().getNetworkAddr().getHost()); | |||
| assertEquals(refContractDatas.getPrimitive().getNetworkAddr().getPort(), decodeData.getPrimitive().getNetworkAddr().getPort()); | |||
| } | |||
| /** | |||
| * 此测试用例是对序列化过程的验证,包括:头部、Generic引用数据契约字段值、字段顺序的正确性; | |||
| */ | |||
| @Test | |||
| public void testEncoding_Header_GenericRefContractArrayFields() { | |||
| DataContractRegistry.register(GenericRefContractDatas.class); | |||
| DataContractRegistry.register(Operation.class); | |||
| DataContractRegistry.register(SubOperation.class); | |||
| GenericRefContractDatasImpl genericRefContractDatas = new GenericRefContractDatasImpl(); | |||
| SubOperationImpl subOperation = new SubOperationImpl(); | |||
| subOperation.setUserName("Jerry"); | |||
| Operation[] operations = new Operation[1]; | |||
| operations[0] = subOperation; | |||
| genericRefContractDatas.setOperations(operations); | |||
| byte[] bytes = BinaryEncodingUtils.encode(genericRefContractDatas, GenericRefContractDatas.class); | |||
| int offset = 0; | |||
| int code = BytesUtils.toInt(bytes, offset); | |||
| offset += 12; | |||
| assertEquals(0xb, code); | |||
| offset = assertGenericRefContractArrayFields(bytes, offset, operations); | |||
| // 到了结尾; | |||
| assertEquals(bytes.length, offset); | |||
| } | |||
| private int assertGenericRefContractArrayFields(byte[] bytes, int offset, Operation[] expectedOperations) { | |||
| // count of operations; | |||
| int opCount = NumberMask.NORMAL.resolveMaskedNumber(bytes, offset); | |||
| byte opCountHeadBytes = bytes[offset]; | |||
| int maskLen = NumberMask.NORMAL.resolveMaskLength(opCountHeadBytes); | |||
| offset += maskLen; | |||
| assertEquals(expectedOperations.length, opCount); | |||
| // Field: operations; | |||
| for (int i = 0; i < opCount; i++) { | |||
| byte[] opertionItemBytes = BytesEncoding.readInNormal(bytes, offset); | |||
| int itemMaskLen = NumberMask.NORMAL.resolveMaskLength(bytes[offset]); | |||
| offset += opertionItemBytes.length + itemMaskLen; | |||
| // verify subOperation; | |||
| int itemOffset = 0; | |||
| int itemCode = BytesUtils.toInt(opertionItemBytes, itemOffset); | |||
| itemOffset += 12; | |||
| assertEquals(0xa, itemCode); | |||
| byte[] userNameBytes = BytesEncoding.readInNormal(opertionItemBytes, itemOffset); | |||
| int nameMaskLen = NumberMask.NORMAL.resolveMaskLength(opertionItemBytes[itemOffset]); | |||
| itemOffset += userNameBytes.length + nameMaskLen; | |||
| String userName = BytesUtils.toString(userNameBytes); | |||
| assertEquals(((SubOperation) expectedOperations[i]).getUserName(), userName); | |||
| assertEquals(opertionItemBytes.length, itemOffset); | |||
| } | |||
| return offset; | |||
| } | |||
| /** | |||
| * 此测试用例是对反序列化过程的验证,包括:头部、Generic引用数据契约字段值、字段顺序的正确性; | |||
| */ | |||
| @Test | |||
| public void testDecoding_Header_OrderedGenericRefContractFields() { | |||
| DataContractRegistry.register(GenericRefContractDatas.class); | |||
| DataContractRegistry.register(Operation.class); | |||
| DataContractRegistry.register(SubOperation.class); | |||
| GenericRefContractDatasImpl genericRefContractDatas = new GenericRefContractDatasImpl(); | |||
| SubOperationImpl subOperation = new SubOperationImpl(); | |||
| subOperation.setUserName("Jerry"); | |||
| Operation[] operations = new Operation[1]; | |||
| operations[0] = subOperation; | |||
| genericRefContractDatas.setOperations(operations); | |||
| byte[] bytes = BinaryEncodingUtils.encode(genericRefContractDatas, GenericRefContractDatas.class); | |||
| GenericRefContractDatas decodeData = BinaryEncodingUtils.decode(bytes); | |||
| assertEquals("Jerry", ((SubOperation) (decodeData.getOperations()[0])).getUserName()); | |||
| } | |||
| /** | |||
| * 此测试用例是对序列化过程的验证,包括:头部、Primitive, Enum, 引用数据契约, Generic引用数据契约字段值、字段顺序的正确性; | |||
| */ | |||
| @Test | |||
| public void testEncoding_Header_OrderedCompositeFields() { | |||
| DataContractRegistry.register(SubOperation.class); | |||
| DataContractRegistry.register(CompositeDatas.class); | |||
| CompositeDatasImpl compositeDatas = new CompositeDatasImpl(); | |||
| PrimitiveDatasImpl primitiveDatas = new PrimitiveDatasImpl(); | |||
| SubOperationImpl subOperation = new SubOperationImpl(); | |||
| NetworkAddress networkAddress = new NetworkAddress("192.168.1.1", 9001, false); | |||
| compositeDatas.setEnable(false); | |||
| compositeDatas.setLevel(EnumLevel.V1); | |||
| primitiveDatas.setId(123); | |||
| primitiveDatas.setEnable(true); | |||
| primitiveDatas.setBoy((byte) 10); | |||
| primitiveDatas.setAge((short) 100); | |||
| primitiveDatas.setName("John"); | |||
| primitiveDatas.setImage("Image of John".getBytes()); | |||
| primitiveDatas.setFlag('x'); | |||
| primitiveDatas.setValue(93239232); | |||
| primitiveDatas.setConfig(Bytes.fromString("Configuration of something.")); | |||
| primitiveDatas.setNetworkAddress(networkAddress); | |||
| compositeDatas.setPrimitiveDatas(primitiveDatas); | |||
| subOperation.setUserName("Jerry"); | |||
| Operation[] operations = new Operation[1]; | |||
| operations[0] = subOperation; | |||
| compositeDatas.setOperations(operations); | |||
| byte[] bytes = BinaryEncodingUtils.encode(compositeDatas, CompositeDatas.class); | |||
| int offset = 0; | |||
| int code = BytesUtils.toInt(bytes, offset); | |||
| offset += 12; | |||
| assertEquals(0xc, code); | |||
| // primitive type | |||
| assertEquals(compositeDatas.isEnable() ? (byte) 1 : (byte) 0, bytes[offset]); | |||
| offset += 1; | |||
| byte enumCode = bytes[offset]; | |||
| offset += 1; | |||
| assertEquals(compositeDatas.getLevel().CODE, enumCode); | |||
| // ---------------- | |||
| // 引用合约字段的字节; | |||
| byte[] primitiveDataFieldBytes = BytesEncoding.readInNormal(bytes, offset); | |||
| int maskLen = NumberMask.NORMAL.resolveMaskLength(bytes[offset]); | |||
| // 获得引用合约码与版本 | |||
| int fieldOffset = 0; | |||
| int primitiveDataCode = BytesUtils.toInt(primitiveDataFieldBytes, fieldOffset); | |||
| fieldOffset += 12; | |||
| assertEquals(0x05, primitiveDataCode); | |||
| fieldOffset = assertFieldEncoding(compositeDatas.getPrimitive(), primitiveDataFieldBytes, fieldOffset); | |||
| assertEquals(primitiveDataFieldBytes.length, fieldOffset); | |||
| offset += primitiveDataFieldBytes.length + maskLen; | |||
| // field: operation; | |||
| offset = assertGenericRefContractArrayFields(bytes, offset, operations); | |||
| // primitive | |||
| assertEquals(compositeDatas.getAge(), BytesUtils.toShort(bytes, offset)); | |||
| offset += 2; | |||
| // 到了结尾; | |||
| assertEquals(bytes.length, offset); | |||
| } | |||
| /** | |||
| * 此测试用例是对反序列化过程的验证,包括:头部、Primitive, Enum, 引用数据契约, Generic引用数据契约字段值、字段顺序的正确性; | |||
| */ | |||
| @Test | |||
| public void testDecoding_Header_OrderedCompositeFields() { | |||
| DataContractRegistry.register(SubOperation.class); | |||
| DataContractRegistry.register(CompositeDatas.class); | |||
| CompositeDatasImpl compositeDatas = new CompositeDatasImpl(); | |||
| PrimitiveDatasImpl primitiveDatas = new PrimitiveDatasImpl(); | |||
| SubOperationImpl subOperation = new SubOperationImpl(); | |||
| NetworkAddress networkAddress = new NetworkAddress("192.168.1.1", 9001, false); | |||
| compositeDatas.setEnable(false); | |||
| compositeDatas.setLevel(EnumLevel.V1); | |||
| compositeDatas.setAge((short) 100); | |||
| primitiveDatas.setId(123); | |||
| primitiveDatas.setEnable(true); | |||
| primitiveDatas.setBoy((byte) 10); | |||
| primitiveDatas.setAge((short) 100); | |||
| primitiveDatas.setName("John"); | |||
| primitiveDatas.setImage("Image of John".getBytes()); | |||
| primitiveDatas.setFlag('x'); | |||
| primitiveDatas.setValue(93239232); | |||
| primitiveDatas.setConfig(Bytes.fromString("Configuration of something.")); | |||
| primitiveDatas.setNetworkAddress(networkAddress); | |||
| compositeDatas.setPrimitiveDatas(primitiveDatas); | |||
| subOperation.setUserName("Jerry"); | |||
| Operation[] operations = new Operation[1]; | |||
| operations[0] = subOperation; | |||
| compositeDatas.setOperations(operations); | |||
| byte[] bytes = BinaryEncodingUtils.encode(compositeDatas, CompositeDatas.class); | |||
| CompositeDatas decodeData = BinaryEncodingUtils.decode(bytes); | |||
| assertEquals(compositeDatas.isEnable(), decodeData.isEnable()); | |||
| assertEquals(compositeDatas.getAge(), decodeData.getAge()); | |||
| assertEquals(compositeDatas.getPrimitive().getId(), decodeData.getPrimitive().getId()); | |||
| assertEquals(compositeDatas.getPrimitive().isEnable(), decodeData.getPrimitive().isEnable()); | |||
| assertEquals(compositeDatas.getPrimitive().isBoy(), decodeData.getPrimitive().isBoy()); | |||
| assertEquals(compositeDatas.getPrimitive().getAge(), decodeData.getPrimitive().getAge()); | |||
| assertEquals(compositeDatas.getPrimitive().getName(), decodeData.getPrimitive().getName()); | |||
| assertTrue(BytesUtils.equals(compositeDatas.getPrimitive().getImage(), decodeData.getPrimitive().getImage())); | |||
| assertEquals(compositeDatas.getPrimitive().getFlag(), decodeData.getPrimitive().getFlag()); | |||
| assertEquals(compositeDatas.getPrimitive().getValue(), decodeData.getPrimitive().getValue()); | |||
| assertEquals(compositeDatas.getPrimitive().getConfig(), decodeData.getPrimitive().getConfig()); | |||
| assertEquals(compositeDatas.getPrimitive().getNetworkAddr().getHost(), decodeData.getPrimitive().getNetworkAddr().getHost()); | |||
| assertEquals(compositeDatas.getPrimitive().getNetworkAddr().getPort(), decodeData.getPrimitive().getNetworkAddr().getPort()); | |||
| assertEquals("Jerry", ((SubOperation) (decodeData.getOperations()[0])).getUserName()); | |||
| assertEquals(compositeDatas.getLevel(), decodeData.getLevel()); | |||
| } | |||
| /** | |||
| * 验证解析一个定义有顺序重复的两个字段的类型时,将引发异常 {@link DataContractException} | |||
| */ | |||
| @Test | |||
| public void testFields_Order_confliction() { | |||
| DataContractException ex = null; | |||
| try { | |||
| DataContractRegistry.register(FieldOrderConflictedDatas.class); | |||
| } catch (DataContractException e) { | |||
| ex = e; | |||
| System.out.println( | |||
| "expected error of [" + FieldOrderConflictedDatas.class.toString() + "] --" + e.getMessage()); | |||
| } | |||
| assertNotNull(ex); | |||
| } | |||
| } | |||
| @@ -0,0 +1,29 @@ | |||
| package test.com.jd.blockchain.binaryproto; | |||
| import com.jd.blockchain.binaryproto.DataContract; | |||
| import com.jd.blockchain.binaryproto.DataField; | |||
| import com.jd.blockchain.utils.Bytes; | |||
| import com.jd.blockchain.utils.ValueType; | |||
| /** | |||
| * Created by zhangshuang3 on 2018/11/30. | |||
| */ | |||
| @DataContract(code = 0xc, name = "CompositeDatas", description = "") | |||
| public interface CompositeDatas { | |||
| @DataField(order = 1, primitiveType = ValueType.BOOLEAN) | |||
| boolean isEnable(); | |||
| @DataField(order = 2, refEnum = true) | |||
| EnumLevel getLevel(); | |||
| @DataField(order = 3, refContract = true) | |||
| PrimitiveDatas getPrimitive(); | |||
| @DataField(order=4, list = true, refContract=true, genericContract = true) | |||
| Operation[] getOperations(); | |||
| @DataField(order = 5, primitiveType = ValueType.INT16) | |||
| short getAge(); | |||
| } | |||
| @@ -0,0 +1,68 @@ | |||
| package test.com.jd.blockchain.binaryproto; | |||
| import java.util.ArrayList; | |||
| import java.util.List; | |||
| /** | |||
| * Created by zhangshuang3 on 2018/11/30. | |||
| */ | |||
| public class CompositeDatasImpl implements CompositeDatas{ | |||
| private boolean enable; | |||
| private EnumLevel level; | |||
| PrimitiveDatas primitiveDatas; | |||
| private List<Operation> operationList = new ArrayList<Operation>(); | |||
| private short age; | |||
| @Override | |||
| public boolean isEnable() { | |||
| return this.enable; | |||
| } | |||
| public void setEnable(boolean enable) { | |||
| this.enable = enable; | |||
| } | |||
| @Override | |||
| public EnumLevel getLevel() { | |||
| return this.level; | |||
| } | |||
| public void setLevel(EnumLevel level) { | |||
| this.level = level; | |||
| } | |||
| @Override | |||
| public PrimitiveDatas getPrimitive() { | |||
| return this.primitiveDatas; | |||
| } | |||
| public void setPrimitiveDatas(PrimitiveDatas primitiveDatas) { | |||
| this.primitiveDatas = primitiveDatas; | |||
| } | |||
| @Override | |||
| public Operation[] getOperations() { | |||
| return operationList.toArray(new Operation[operationList.size()]); | |||
| } | |||
| public void setOperations(Object[] operations) { | |||
| for (Object operation : operations) { | |||
| Operation op = (Operation)operation; | |||
| addOperation(op); | |||
| } | |||
| } | |||
| public void addOperation(Operation operation) { | |||
| operationList.add(operation); | |||
| } | |||
| @Override | |||
| public short getAge() { | |||
| return this.age; | |||
| } | |||
| public void setAge(short age) { | |||
| this.age = age; | |||
| } | |||
| } | |||
| @@ -0,0 +1,15 @@ | |||
| package test.com.jd.blockchain.binaryproto; | |||
| import com.jd.blockchain.binaryproto.DataContract; | |||
| import com.jd.blockchain.binaryproto.DataField; | |||
| /** | |||
| * Created by zhangshuang3 on 2018/11/29. | |||
| */ | |||
| @DataContract(code = 0x07, name = "EnumDatas", description = "") | |||
| public interface EnumDatas { | |||
| @DataField(order = 1, refEnum = true) | |||
| EnumLevel getLevel(); | |||
| } | |||
| @@ -0,0 +1,17 @@ | |||
| package test.com.jd.blockchain.binaryproto; | |||
| /** | |||
| * Created by zhangshuang3 on 2018/11/29. | |||
| */ | |||
| public class EnumDatasImpl implements EnumDatas { | |||
| private EnumLevel level; | |||
| @Override | |||
| public EnumLevel getLevel() { | |||
| return this.level; | |||
| } | |||
| public void setLevel(EnumLevel level) { | |||
| this.level = level; | |||
| } | |||
| } | |||
| @@ -0,0 +1,27 @@ | |||
| package test.com.jd.blockchain.binaryproto; | |||
| import com.jd.blockchain.binaryproto.EnumContract; | |||
| import com.jd.blockchain.binaryproto.EnumField; | |||
| import com.jd.blockchain.utils.ValueType; | |||
| /** | |||
| * Created by zhangshuang3 on 2018/11/29. | |||
| */ | |||
| @EnumContract(code=0x0100, name = "EnumLevel", decription = "") | |||
| public enum EnumLevel { | |||
| V1((byte) 1), | |||
| V2((byte) 2); | |||
| @EnumField(type= ValueType.INT8) | |||
| public final byte CODE; | |||
| public byte getCode() { | |||
| return CODE; | |||
| } | |||
| private EnumLevel(byte code) { | |||
| this.CODE = code; | |||
| } | |||
| } | |||
| @@ -0,0 +1,33 @@ | |||
| package test.com.jd.blockchain.binaryproto; | |||
| import com.jd.blockchain.binaryproto.DataContract; | |||
| import com.jd.blockchain.binaryproto.DataField; | |||
| import com.jd.blockchain.utils.Bytes; | |||
| import com.jd.blockchain.utils.ValueType; | |||
| /** | |||
| * Created by zhangshuang3 on 2018/7/11. | |||
| */ | |||
| @DataContract(code = 0x06, name = "Primitive", description = "") | |||
| public interface FieldOrderConflictedDatas { | |||
| @DataField(order = 2, primitiveType = ValueType.BOOLEAN) | |||
| boolean isEnable(); | |||
| @DataField(order = 3, primitiveType = ValueType.INT8) | |||
| byte isBoy(); | |||
| @DataField(order = 7, primitiveType = ValueType.INT16) | |||
| short getAge(); | |||
| @DataField(order = -1, primitiveType = ValueType.INT32) | |||
| int getId(); | |||
| @DataField(order = 6, primitiveType = ValueType.TEXT) | |||
| String getName(); | |||
| @DataField(order = 7, primitiveType = ValueType.INT64) | |||
| long getValue(); | |||
| } | |||
| @@ -0,0 +1,71 @@ | |||
| package test.com.jd.blockchain.binaryproto; | |||
| /** | |||
| * Created by zhangshuang3 on 2018/7/11. | |||
| */ | |||
| public class FieldOrderConflictedDatasImpl implements FieldOrderConflictedDatas { | |||
| private boolean enable; | |||
| private byte boy; | |||
| private short age; | |||
| private int id; | |||
| private String name; | |||
| private long value; | |||
| @Override | |||
| public int getId() { | |||
| return id; | |||
| } | |||
| public void setId(int id) { | |||
| this.id = id; | |||
| } | |||
| @Override | |||
| public String getName() { | |||
| return this.name; | |||
| } | |||
| public void setName(String name) { | |||
| this.name = name; | |||
| } | |||
| @Override | |||
| public short getAge() { | |||
| return age; | |||
| } | |||
| public void setAge(short age) { | |||
| this.age = age; | |||
| } | |||
| @Override | |||
| public byte isBoy() { | |||
| return this.boy; | |||
| } | |||
| public void setBoy(byte boy) { | |||
| this.boy = boy; | |||
| } | |||
| @Override | |||
| public boolean isEnable() { | |||
| return enable; | |||
| } | |||
| public void setEnable(boolean enable) { | |||
| this.enable = enable; | |||
| } | |||
| @Override | |||
| public long getValue() { | |||
| return value; | |||
| } | |||
| public void setValue(long value) { | |||
| this.value = value; | |||
| } | |||
| } | |||
| @@ -0,0 +1,15 @@ | |||
| package test.com.jd.blockchain.binaryproto; | |||
| import com.jd.blockchain.binaryproto.DataContract; | |||
| import com.jd.blockchain.binaryproto.DataField; | |||
| /** | |||
| * Created by zhangshuang3 on 2018/11/29. | |||
| */ | |||
| @DataContract(code = 0xb, name = "GenericRefContractDatas", description = "") | |||
| public interface GenericRefContractDatas { | |||
| @DataField(order=1, list = true, refContract=true, genericContract = true) | |||
| Operation[] getOperations(); | |||
| } | |||
| @@ -0,0 +1,30 @@ | |||
| package test.com.jd.blockchain.binaryproto; | |||
| import java.util.ArrayList; | |||
| import java.util.Collection; | |||
| import java.util.HashMap; | |||
| import java.util.List; | |||
| /** | |||
| * Created by zhangshuang3 on 2018/11/29. | |||
| */ | |||
| public class GenericRefContractDatasImpl implements GenericRefContractDatas{ | |||
| private List<Operation> operationList = new ArrayList<Operation>(); | |||
| @Override | |||
| public Operation[] getOperations() { | |||
| return operationList.toArray(new Operation[operationList.size()]); | |||
| } | |||
| public void setOperations(Object[] operations) { | |||
| for (Object operation : operations) { | |||
| Operation op = (Operation)operation; | |||
| addOperation(op); | |||
| } | |||
| } | |||
| public void addOperation(Operation operation) { | |||
| operationList.add(operation); | |||
| } | |||
| } | |||
| @@ -0,0 +1,10 @@ | |||
| package test.com.jd.blockchain.binaryproto; | |||
| import com.jd.blockchain.binaryproto.DataContract; | |||
| /** | |||
| * Created by zhangshuang3 on 2018/11/29. | |||
| */ | |||
| @DataContract(code = 0x09, name = "Operation", description = "") | |||
| public interface Operation { | |||
| } | |||
| @@ -0,0 +1,48 @@ | |||
| package test.com.jd.blockchain.binaryproto; | |||
| import com.jd.blockchain.binaryproto.DataContract; | |||
| import com.jd.blockchain.binaryproto.DataField; | |||
| import com.jd.blockchain.utils.Bytes; | |||
| import com.jd.blockchain.utils.ValueType; | |||
| import com.jd.blockchain.utils.net.NetworkAddress; | |||
| /** | |||
| * Created by zhangshuang3 on 2018/7/11. | |||
| */ | |||
| @DataContract(code = 0x05, name = "Primitive", description = "") | |||
| public interface PrimitiveDatas { | |||
| @DataField(order = 2, primitiveType = ValueType.BOOLEAN) | |||
| boolean isEnable(); | |||
| @DataField(order = 3, primitiveType = ValueType.INT8) | |||
| byte isBoy(); | |||
| @DataField(order = 4, primitiveType = ValueType.INT16) | |||
| short getAge(); | |||
| @DataField(order = -1, primitiveType = ValueType.INT32) | |||
| int getId(); | |||
| @DataField(order = 6, primitiveType = ValueType.TEXT) | |||
| String getName(); | |||
| @DataField(order = 7, primitiveType = ValueType.INT64) | |||
| long getValue(); | |||
| @DataField(order = 12, primitiveType = ValueType.BYTES) | |||
| byte[] getImage(); | |||
| @DataField(order = 100, primitiveType = ValueType.INT16) | |||
| char getFlag(); | |||
| @DataField(order = 200, primitiveType = ValueType.BYTES) | |||
| Bytes getConfig(); | |||
| @DataField(order = 201, primitiveType = ValueType.BYTES) | |||
| Bytes getSetting(); | |||
| @DataField(order = 202, primitiveType = ValueType.BYTES) | |||
| NetworkAddress getNetworkAddr(); | |||
| } | |||
| @@ -0,0 +1,130 @@ | |||
| package test.com.jd.blockchain.binaryproto; | |||
| import org.omg.CORBA.PUBLIC_MEMBER; | |||
| import com.jd.blockchain.utils.Bytes; | |||
| import com.jd.blockchain.utils.net.NetworkAddress; | |||
| /** | |||
| * Created by zhangshuang3 on 2018/7/11. | |||
| */ | |||
| public class PrimitiveDatasImpl implements PrimitiveDatas { | |||
| private boolean enable; | |||
| private byte boy; | |||
| private short age; | |||
| private int id; | |||
| private String name; | |||
| private long value; | |||
| private char flag; | |||
| private byte[] image; | |||
| private Bytes config; | |||
| private Bytes setting; | |||
| private NetworkAddress networkAddress; | |||
| @Override | |||
| public int getId() { | |||
| return id; | |||
| } | |||
| public void setId(int id) { | |||
| this.id = id; | |||
| } | |||
| @Override | |||
| public String getName() { | |||
| return this.name; | |||
| } | |||
| public void setName(String name) { | |||
| this.name = name; | |||
| } | |||
| @Override | |||
| public short getAge() { | |||
| return age; | |||
| } | |||
| public void setAge(short age) { | |||
| this.age = age; | |||
| } | |||
| @Override | |||
| public byte isBoy() { | |||
| return this.boy; | |||
| } | |||
| public void setBoy(byte boy) { | |||
| this.boy = boy; | |||
| } | |||
| @Override | |||
| public boolean isEnable() { | |||
| return enable; | |||
| } | |||
| public void setEnable(boolean enable) { | |||
| this.enable = enable; | |||
| } | |||
| @Override | |||
| public long getValue() { | |||
| return value; | |||
| } | |||
| @Override | |||
| public char getFlag() { | |||
| return flag; | |||
| } | |||
| public void setValue(long value) { | |||
| this.value = value; | |||
| } | |||
| public void setFlag(char flag) { | |||
| this.flag = flag; | |||
| } | |||
| @Override | |||
| public byte[] getImage() { | |||
| return image; | |||
| } | |||
| @Override | |||
| public Bytes getConfig() { | |||
| return config; | |||
| } | |||
| public void setImage(byte[] image) { | |||
| this.image = image; | |||
| } | |||
| public void setConfig(Bytes config) { | |||
| this.config = config; | |||
| } | |||
| public void setSetting(Bytes setting) { | |||
| this.setting = setting; | |||
| } | |||
| @Override | |||
| public Bytes getSetting() { | |||
| return setting; | |||
| } | |||
| @Override | |||
| public NetworkAddress getNetworkAddr() { | |||
| return networkAddress; | |||
| } | |||
| public void setNetworkAddress(NetworkAddress networkAddress) { | |||
| this.networkAddress = networkAddress; | |||
| } | |||
| } | |||
| @@ -0,0 +1,15 @@ | |||
| package test.com.jd.blockchain.binaryproto; | |||
| import com.jd.blockchain.binaryproto.DataContract; | |||
| import com.jd.blockchain.binaryproto.DataField; | |||
| import com.jd.blockchain.utils.ValueType; | |||
| /** | |||
| * Created by zhangshuang3 on 2018/11/29. | |||
| */ | |||
| @DataContract(code = 0x08, name = "RefContractDatas", description = "") | |||
| public interface RefContractDatas { | |||
| @DataField(order = 1, refContract = true) | |||
| PrimitiveDatas getPrimitive(); | |||
| } | |||
| @@ -0,0 +1,18 @@ | |||
| package test.com.jd.blockchain.binaryproto; | |||
| /** | |||
| * Created by zhangshuang3 on 2018/11/29. | |||
| */ | |||
| public class RefContractDatasImpl implements RefContractDatas{ | |||
| PrimitiveDatas primitiveDatas; | |||
| @Override | |||
| public PrimitiveDatas getPrimitive() { | |||
| return this.primitiveDatas; | |||
| } | |||
| public void setPrimitiveDatas(PrimitiveDatas primitiveDatas) { | |||
| this.primitiveDatas = primitiveDatas; | |||
| } | |||
| } | |||
| @@ -0,0 +1,16 @@ | |||
| package test.com.jd.blockchain.binaryproto; | |||
| import com.jd.blockchain.binaryproto.DataContract; | |||
| import com.jd.blockchain.binaryproto.DataField; | |||
| import com.jd.blockchain.utils.ValueType; | |||
| /** | |||
| * Created by zhangshuang3 on 2018/11/29. | |||
| */ | |||
| @DataContract(code = 0xa, name = "SubOperation", description = "") | |||
| public interface SubOperation extends Operation { | |||
| @DataField(order=1, primitiveType = ValueType.TEXT) | |||
| String getUserName(); | |||
| } | |||
| @@ -0,0 +1,17 @@ | |||
| package test.com.jd.blockchain.binaryproto; | |||
| /** | |||
| * Created by zhangshuang3 on 2018/11/29. | |||
| */ | |||
| public class SubOperationImpl implements SubOperation { | |||
| String userName; | |||
| @Override | |||
| public String getUserName() { | |||
| return this.userName; | |||
| } | |||
| public void setUserName(String userName) { | |||
| this.userName = userName; | |||
| } | |||
| } | |||
| @@ -0,0 +1,16 @@ | |||
| //package test.com.jd.blockchain.binaryproto.contract; | |||
| // | |||
| //import com.jd.blockchain.binaryproto.DataContract; | |||
| //import com.jd.blockchain.binaryproto.DataField; | |||
| //import com.jd.blockchain.binaryproto.ValueType; | |||
| // | |||
| //@DataContract(code=0x02, name="Address" , description="") | |||
| //public interface Address { | |||
| // | |||
| // @DataField(order=1, primitiveType=ValueType.TEXT) | |||
| // String getStreet(); | |||
| // | |||
| // @DataField(order=2, primitiveType=ValueType.INT32) | |||
| // int getNumber(); | |||
| // | |||
| //} | |||
| @@ -0,0 +1,19 @@ | |||
| package test.com.jd.blockchain.binaryproto.contract; | |||
| import com.jd.blockchain.binaryproto.DataContract; | |||
| import com.jd.blockchain.binaryproto.DataField; | |||
| import com.jd.blockchain.utils.ValueType; | |||
| /** | |||
| * Created by zhangshuang3 on 2018/7/9. | |||
| */ | |||
| @DataContract(code=0x02, name="Address" , description="") | |||
| public interface AddressCodeDuplicate { | |||
| @DataField(order=1, primitiveType= ValueType.TEXT) | |||
| String getStreet(); | |||
| @DataField(order=2, primitiveType=ValueType.INT32) | |||
| int getNumber(); | |||
| } | |||
| @@ -0,0 +1,19 @@ | |||
| package test.com.jd.blockchain.binaryproto.contract; | |||
| import com.jd.blockchain.binaryproto.DataContract; | |||
| import com.jd.blockchain.binaryproto.DataField; | |||
| import com.jd.blockchain.utils.ValueType; | |||
| /** | |||
| * Created by zhangshuang3 on 2018/7/9. | |||
| */ | |||
| @DataContract(code=0x03, name="Address" , description="") | |||
| public interface AddressOrderDuplicate { | |||
| @DataField(order=1, primitiveType= ValueType.TEXT) | |||
| String getStreet(); | |||
| @DataField(order=1, primitiveType=ValueType.INT32) | |||
| int getNumber(); | |||
| } | |||
| @@ -0,0 +1,25 @@ | |||
| package test.com.jd.blockchain.binaryproto.contract; | |||
| import com.jd.blockchain.binaryproto.DataContract; | |||
| import com.jd.blockchain.binaryproto.DataField; | |||
| import com.jd.blockchain.utils.ValueType; | |||
| /** | |||
| * Created by zhangshuang3 on 2018/7/11. | |||
| */ | |||
| @DataContract(code=0x08, name="Array" , description="") | |||
| public interface Array { | |||
| @DataField(order=1, primitiveType= ValueType.INT32, list=true) | |||
| int[] getScores(); | |||
| @DataField(order=2, primitiveType=ValueType.TEXT, list=true) | |||
| String[] getFeatures(); | |||
| @DataField(order=3, primitiveType=ValueType.BYTES) | |||
| byte[] getFamilyMemberAges(); | |||
| @DataField(order=4, primitiveType=ValueType.INT64, list=true) | |||
| long[] getFamilyMemberIds(); | |||
| } | |||
| @@ -0,0 +1,158 @@ | |||
| //package test.com.jd.blockchain.binaryproto.contract; | |||
| // | |||
| //import com.jd.blockchain.binaryproto.EnumContract; | |||
| //import com.jd.blockchain.binaryproto.EnumField; | |||
| //import com.jd.blockchain.binaryproto.ValueType; | |||
| //import com.jd.blockchain.crypto.CryptoAlgorithmType; | |||
| //import my.utils.io.BytesUtils; | |||
| // | |||
| ///** | |||
| // * Created by zhangshuang3 on 2018/7/30. | |||
| // */ | |||
| //@EnumContract(code=0x0102) | |||
| //public enum CryptoAlgorithm { | |||
| // | |||
| // // Hash 类; | |||
| // // SHA_128(CryptoAlgorithmMask.HASH, (byte) 0x01, false, false), | |||
| // | |||
| // SHA_256(CryptoAlgorithmType.HASH, (byte) 0x01, false, false), | |||
| // | |||
| // RIPLE160(CryptoAlgorithmType.HASH, (byte) 0x02, false, false), | |||
| // | |||
| // SM3(CryptoAlgorithmType.HASH, (byte) 0x03, false, false), | |||
| // | |||
| // // 非对称签名/加密算法; | |||
| // | |||
| // /** | |||
| // * RSA 签名算法;可签名,可加密; | |||
| // */ | |||
| // RSA(CryptoAlgorithmType.ASYMMETRIC, (byte) 0x01, true, true), | |||
| // | |||
| // /** | |||
| // * ED25519 签名算法;只用于签名,没有加密特性; | |||
| // */ | |||
| // ED25519(CryptoAlgorithmType.ASYMMETRIC, (byte) 0x02, true, false), | |||
| // | |||
| // /** | |||
| // * ECDSA 签名算法;只用于签名,没有加密特性;??? | |||
| // */ | |||
| // ECDSA(CryptoAlgorithmType.ASYMMETRIC, (byte) 0x03, true, false), | |||
| // | |||
| // /** | |||
| // * 国密 SM2 算法;可签名,可加密; | |||
| // */ | |||
| // SM2(CryptoAlgorithmType.ASYMMETRIC, (byte) 0x04, true, true), | |||
| // | |||
| // // 对称加密; | |||
| // /** | |||
| // * AES 算法;可加密; | |||
| // */ | |||
| // AES(CryptoAlgorithmType.SYMMETRIC, (byte) 0x01, false, true), | |||
| // | |||
| // SM4(CryptoAlgorithmType.SYMMETRIC, (byte) 0x02, false, true), | |||
| // | |||
| // // 随机性; | |||
| // /** | |||
| // * ????? 一种随机数算法,待定; | |||
| // */ | |||
| // JAVA_SECURE(CryptoAlgorithmType.RANDOM, (byte) 0x01, false, false); | |||
| // | |||
| // /** | |||
| // * 密码算法的代号;<br> | |||
| // * 注:只占16位; | |||
| // */ | |||
| // @EnumField(type = ValueType.INT8) | |||
| // public final byte CODE; | |||
| // | |||
| // private final boolean signable; | |||
| // | |||
| // private final boolean encryptable; | |||
| // | |||
| // private CryptoAlgorithm(byte algType, byte algId, boolean signable, boolean encryptable) { | |||
| // this.CODE = (byte) (algType | algId); | |||
| // this.signable = signable; | |||
| // this.encryptable = encryptable; | |||
| // } | |||
| // | |||
| // /** | |||
| // * 是否属于摘要算法; | |||
| // * | |||
| // * @return | |||
| // */ | |||
| // public boolean isHash() { | |||
| // return (CODE & CryptoAlgorithmType.HASH) == CryptoAlgorithmType.HASH; | |||
| // } | |||
| // | |||
| // /** | |||
| // * 是否属于非对称密码算法; | |||
| // * | |||
| // * @return | |||
| // */ | |||
| // public boolean isAsymmetric() { | |||
| // return (CODE & CryptoAlgorithmType.ASYMMETRIC) == CryptoAlgorithmType.ASYMMETRIC; | |||
| // } | |||
| // | |||
| // /** | |||
| // * 是否属于对称密码算法; | |||
| // * | |||
| // * @return | |||
| // */ | |||
| // public boolean isSymmetric() { | |||
| // return (CODE & CryptoAlgorithmType.SYMMETRIC) == CryptoAlgorithmType.SYMMETRIC; | |||
| // } | |||
| // | |||
| // /** | |||
| // * 是否属于随机数算法; | |||
| // * | |||
| // * @return | |||
| // */ | |||
| // public boolean isRandom() { | |||
| // return (CODE & CryptoAlgorithmType.RANDOM) == CryptoAlgorithmType.RANDOM; | |||
| // } | |||
| // | |||
| // /** | |||
| // * 是否支持签名操作; | |||
| // * | |||
| // * @return | |||
| // */ | |||
| // public boolean isSignable() { | |||
| // return signable; | |||
| // } | |||
| // | |||
| // /** | |||
| // * 是否支持加密操作; | |||
| // * | |||
| // * @return | |||
| // */ | |||
| // public boolean isEncryptable() { | |||
| // return encryptable; | |||
| // } | |||
| // | |||
| // /** | |||
| // * 返回指定编码对应的枚举实例;<br> | |||
| // * | |||
| // * 如果不存在,则返回 null; | |||
| // * | |||
| // * @param code | |||
| // * @return | |||
| // */ | |||
| // public static CryptoAlgorithm valueOf(byte code) { | |||
| // for (CryptoAlgorithm alg : CryptoAlgorithm.values()) { | |||
| // if (alg.CODE == code) { | |||
| // return alg; | |||
| // } | |||
| // } | |||
| // throw new IllegalArgumentException("CryptoAlgorithm doesn't support enum code[" + code + "]!"); | |||
| // } | |||
| // | |||
| // // /** | |||
| // // * @return | |||
| // // */ | |||
| // // public byte[] toBytes() { | |||
| // // byte[] bytes = BytesUtils.toBytes(CODE); | |||
| // // byte[] result = new byte[BYTES_SIZE]; | |||
| // // System.arraycopy(bytes, 2, result, 0, 2); | |||
| // // // TODO: 只返回最后2个字节; | |||
| // // return result; | |||
| // // } | |||
| //} | |||
| @@ -0,0 +1,41 @@ | |||
| //package test.com.jd.blockchain.binaryproto.contract; | |||
| // | |||
| //import com.jd.blockchain.binaryproto.DataContract; | |||
| //import com.jd.blockchain.binaryproto.DataField; | |||
| //import com.jd.blockchain.binaryproto.ValueType; | |||
| // | |||
| ///** | |||
| // * Created by zhangshuang3 on 2018/7/30. | |||
| // */ | |||
| //@DataContract(code=0x0d, name="CryptoSetting", description = "Crypto setting") | |||
| //public interface CryptoSetting { | |||
| // | |||
| // /** | |||
| // * 系统中使用的 Hash 算法; <br> | |||
| // * | |||
| // * 对于历史数据,如果它未发生更改,则总是按照该数据产生时采用的算法进行校验,即使当时指定的Hash算法和当前的不同;<br> | |||
| // * | |||
| // * 如果对数据进行了更新,则采用新的 Hash 算法来计算生成完整性证明; | |||
| // * | |||
| // * @return | |||
| // */ | |||
| // @DataField(order=1, refEnum=true) | |||
| // public HashAlgorithm getHashAlgorithm(); | |||
| // | |||
| // @DataField(order=2, refEnum=true) | |||
| // public CryptoAlgorithm getHashAlgorithm1(); | |||
| // | |||
| // /** | |||
| // * 当有完整性证明的数据被从持久化介质中加载时,是否对其进行完整性校验(重新计算 hash 比对是否一致); <br> | |||
| // * | |||
| // * 如果为 true ,则自动进行校验,如果校验失败,会引发异常; <br> | |||
| // * | |||
| // * 注意:开启此选项将对性能会产生负面影响,因此使用者需要在性能和数据安全性之间做出权衡; | |||
| // * | |||
| // * @return | |||
| // */ | |||
| // @DataField(order=3, primitiveType= ValueType.BOOLEAN) | |||
| // public boolean getAutoVerifyHash();//func name is getxxxxx type | |||
| // | |||
| //} | |||
| // | |||
| @@ -0,0 +1,50 @@ | |||
| //package test.com.jd.blockchain.binaryproto.contract; | |||
| // | |||
| //import com.jd.blockchain.binaryproto.EnumContract; | |||
| //import com.jd.blockchain.binaryproto.EnumField; | |||
| //import com.jd.blockchain.binaryproto.ValueType; | |||
| // | |||
| ///** | |||
| // * Created by zhangshuang3 on 2018/7/30. | |||
| // */ | |||
| //@EnumContract(code=0x0101) | |||
| //public enum HashAlgorithm { | |||
| // | |||
| // RIPE160((byte) 1), | |||
| // | |||
| // SHA256((byte) 2), | |||
| // | |||
| // SM3((byte) 4); | |||
| // | |||
| // @EnumField(type = ValueType.INT8) | |||
| // public final byte CODE; | |||
| // | |||
| // private HashAlgorithm(byte algorithm) { | |||
| // CODE = algorithm; | |||
| // } | |||
| // | |||
| // public byte getAlgorithm() { | |||
| // return CODE; | |||
| // } | |||
| // | |||
| // public static HashAlgorithm valueOf(byte algorithm) { | |||
| // for (HashAlgorithm hashAlgorithm : HashAlgorithm.values()) { | |||
| // if (hashAlgorithm.CODE == algorithm) { | |||
| // return hashAlgorithm; | |||
| // } | |||
| // } | |||
| // throw new IllegalArgumentException("Unsupported hash algorithm [" + algorithm + "]!"); | |||
| // } | |||
| // | |||
| // public static void checkHashAlgorithm(HashAlgorithm algorithm) { | |||
| // switch (algorithm) { | |||
| // case RIPE160: | |||
| // break; | |||
| // case SHA256: | |||
| // break; | |||
| // default: | |||
| // throw new IllegalArgumentException("Unsupported hash algorithm [" + algorithm + "]!"); | |||
| // } | |||
| // } | |||
| //} | |||
| // | |||
| @@ -0,0 +1,28 @@ | |||
| //package test.com.jd.blockchain.binaryproto.contract; | |||
| // | |||
| //import com.jd.blockchain.binaryproto.DataContract; | |||
| //import com.jd.blockchain.binaryproto.DataField; | |||
| //import com.jd.blockchain.binaryproto.ValueType; | |||
| //import com.jd.blockchain.crypto.hash.HashDigest; | |||
| // | |||
| ///** | |||
| // * Created by zhangshuang3 on 2018/7/30. | |||
| // */ | |||
| //@DataContract(code=0x12, name="LedgerBlock", description ="LedgerBlock") | |||
| //public interface LedgerBlock extends LedgerDataSnapshot{ | |||
| // | |||
| // @DataField(order=1, refHashDigest=true) | |||
| // HashDigest getHash(); | |||
| // | |||
| // @DataField(order=2, refHashDigest=true) | |||
| // HashDigest getPreviousHash(); | |||
| // | |||
| // @DataField(order=3, refHashDigest=true) | |||
| // HashDigest getLedgerHash(); | |||
| // | |||
| // @DataField(order=4, primitiveType=ValueType.INT64) | |||
| // long getHeight(); | |||
| // | |||
| // @DataField(order=5, refHashDigest=true) | |||
| // HashDigest getTransactionSetHash(); | |||
| //} | |||
| @@ -0,0 +1,34 @@ | |||
| //package test.com.jd.blockchain.binaryproto.contract; | |||
| // | |||
| //import com.jd.blockchain.binaryproto.DataContract; | |||
| //import com.jd.blockchain.binaryproto.DataField; | |||
| //import com.jd.blockchain.crypto.hash.HashDigest; | |||
| // | |||
| ///** | |||
| // * Created by zhangshuang3 on 2018/7/30. | |||
| // */ | |||
| //@DataContract(code=0x11, name="LedgerDataSnapshot", description ="LedgerDataSnapshot") | |||
| //public interface LedgerDataSnapshot { | |||
| // | |||
| // @DataField(order=1, refHashDigest=true) | |||
| // HashDigest getAdminAccountHash(); | |||
| // | |||
| // @DataField(order=2, refHashDigest=true) | |||
| // HashDigest getUserAccountSetHash(); | |||
| // | |||
| // @DataField(order=3, refHashDigest=true) | |||
| // HashDigest getUserPrivilegeHash(); | |||
| // | |||
| // @DataField(order=4, refHashDigest=true) | |||
| // HashDigest getDataAccountSetHash(); | |||
| // | |||
| // @DataField(order=5, refHashDigest=true) | |||
| // HashDigest getDataPrivilegeHash(); | |||
| // | |||
| // @DataField(order=6, refHashDigest=true) | |||
| // HashDigest getContractAccountSetHash(); | |||
| // | |||
| // @DataField(order=7, refHashDigest=true) | |||
| // HashDigest getContractPrivilegeHash(); | |||
| // | |||
| //} | |||
| @@ -0,0 +1,25 @@ | |||
| //package test.com.jd.blockchain.binaryproto.contract; | |||
| // | |||
| //import com.jd.blockchain.binaryproto.DataContract; | |||
| //import com.jd.blockchain.binaryproto.DataField; | |||
| //import com.jd.blockchain.binaryproto.ValueType; | |||
| // | |||
| ///** | |||
| // * Created by zhangshuang3 on 2018/7/30. | |||
| // */ | |||
| //@DataContract(code=0x0b, name="LedgerMetadata", description = "Ledger meta data") | |||
| //public interface LedgerMetadata { | |||
| // | |||
| // @DataField(order=1, primitiveType= ValueType.INT8, list=true) | |||
| // byte[] getSeed(); | |||
| // | |||
| // @DataField(order = 2, refContract=true) | |||
| // LedgerSetting getSetting(); | |||
| // | |||
| // @DataField(order=3, primitiveType=ValueType.INT8, list=true) | |||
| // byte[] getPrivilegesHash(); | |||
| // | |||
| // @DataField(order=4, primitiveType=ValueType.INT8, list=true) | |||
| // byte[] getParticipantsHash(); | |||
| // | |||
| //} | |||
| @@ -0,0 +1,21 @@ | |||
| //package test.com.jd.blockchain.binaryproto.contract; | |||
| // | |||
| //import com.jd.blockchain.binaryproto.DataContract; | |||
| //import com.jd.blockchain.binaryproto.DataField; | |||
| // | |||
| ///** | |||
| // * Created by zhangshuang3 on 2018/7/30. | |||
| // */ | |||
| //@DataContract(code=0x0c, name="LedgerSetting", description = "Ledger setting") | |||
| //public interface LedgerSetting { | |||
| // | |||
| // //@DataField(order=1, refContract=true) | |||
| // //ConsensusSetting getConsensusSetting(); | |||
| // | |||
| // @DataField(order=2, refContract=true) | |||
| // CryptoSetting getCryptoSetting(); | |||
| // | |||
| // @DataField(order=3, refContract=true) | |||
| // PrivilegeModelSetting getPrivilegesModelSetting(); | |||
| // | |||
| //} | |||
| @@ -0,0 +1,25 @@ | |||
| package test.com.jd.blockchain.binaryproto.contract; | |||
| import com.jd.blockchain.binaryproto.EnumContract; | |||
| import com.jd.blockchain.binaryproto.EnumField; | |||
| import com.jd.blockchain.utils.ValueType; | |||
| @EnumContract(code=0x0100) | |||
| public enum Level { | |||
| V1((byte) 1), | |||
| V2((byte) 2); | |||
| @EnumField(type=ValueType.INT8) | |||
| public final byte CODE; | |||
| public byte getCode() { | |||
| return CODE; | |||
| } | |||
| private Level(byte code) { | |||
| this.CODE = code; | |||
| } | |||
| } | |||
| @@ -0,0 +1,18 @@ | |||
| //package test.com.jd.blockchain.binaryproto.contract; | |||
| // | |||
| //import com.jd.blockchain.binaryproto.DataContract; | |||
| //import com.jd.blockchain.binaryproto.DataField; | |||
| //import com.jd.blockchain.binaryproto.ValueType; | |||
| // | |||
| ///** | |||
| // * Created by zhangshuang3 on 2018/7/30. | |||
| // */ | |||
| //@DataContract(code=0x10, name="Privilege", description ="Privilege") | |||
| //public interface Privilege { | |||
| // | |||
| // //SortedSet<Byte> getOpCodes(); implement later | |||
| // | |||
| // @DataField(order=2, primitiveType= ValueType.INT64) | |||
| // long getVersion(); | |||
| // | |||
| //} | |||
| @@ -0,0 +1,20 @@ | |||
| package test.com.jd.blockchain.binaryproto.contract; | |||
| import com.jd.blockchain.binaryproto.DataContract; | |||
| import com.jd.blockchain.binaryproto.DataField; | |||
| import com.jd.blockchain.utils.ValueType; | |||
| /** | |||
| * Created by zhangshuang3 on 2018/7/30. | |||
| */ | |||
| @DataContract(code=0x0f, name="PrivilegeModelSetting", description ="Privilege Model setting") | |||
| public interface PrivilegeModelSetting { | |||
| @DataField(order=1, primitiveType= ValueType.INT64) | |||
| long getLatestVersion(); | |||
| //@DataField(order=2, refContract=true) | |||
| //Privilege getPrivilege(long version); | |||
| } | |||