[
  {
    "path": ".gitignore",
    "content": ".svn/\ntarget/\ntest-output/\n*.class\n.classpath\n.project\n.settings/\ntmp\ntemp\n*.log\nantx.properties\n.idea/\n*.iml\nhtdocs\npackage-info.java\n.DS_Store\n*.tar.gz\ndocker/data/\n"
  },
  {
    "path": "HEADER.txt",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n"
  },
  {
    "path": "LICENSE.txt",
    "content": "\r\n                                 Apache License\r\n                           Version 2.0, January 2004\r\n                        http://www.apache.org/licenses/\r\n\r\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\r\n\r\n   1. Definitions.\r\n\r\n      \"License\" shall mean the terms and conditions for use, reproduction,\r\n      and distribution as defined by Sections 1 through 9 of this document.\r\n\r\n      \"Licensor\" shall mean the copyright owner or entity authorized by\r\n      the copyright owner that is granting the License.\r\n\r\n      \"Legal Entity\" shall mean the union of the acting entity and all\r\n      other entities that control, are controlled by, or are under common\r\n      control with that entity. For the purposes of this definition,\r\n      \"control\" means (i) the power, direct or indirect, to cause the\r\n      direction or management of such entity, whether by contract or\r\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\r\n      outstanding shares, or (iii) beneficial ownership of such entity.\r\n\r\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\r\n      exercising permissions granted by this License.\r\n\r\n      \"Source\" form shall mean the preferred form for making modifications,\r\n      including but not limited to software source code, documentation\r\n      source, and configuration files.\r\n\r\n      \"Object\" form shall mean any form resulting from mechanical\r\n      transformation or translation of a Source form, including but\r\n      not limited to compiled object code, generated documentation,\r\n      and conversions to other media types.\r\n\r\n      \"Work\" shall mean the work of authorship, whether in Source or\r\n      Object form, made available under the License, as indicated by a\r\n      copyright notice that is included in or attached to the work\r\n      (an example is provided in the Appendix below).\r\n\r\n      \"Derivative Works\" shall mean any work, whether in Source or Object\r\n      form, that is based on (or derived from) the Work and for which the\r\n      editorial revisions, annotations, elaborations, or other modifications\r\n      represent, as a whole, an original work of authorship. For the purposes\r\n      of this License, Derivative Works shall not include works that remain\r\n      separable from, or merely link (or bind by name) to the interfaces of,\r\n      the Work and Derivative Works thereof.\r\n\r\n      \"Contribution\" shall mean any work of authorship, including\r\n      the original version of the Work and any modifications or additions\r\n      to that Work or Derivative Works thereof, that is intentionally\r\n      submitted to Licensor for inclusion in the Work by the copyright owner\r\n      or by an individual or Legal Entity authorized to submit on behalf of\r\n      the copyright owner. For the purposes of this definition, \"submitted\"\r\n      means any form of electronic, verbal, or written communication sent\r\n      to the Licensor or its representatives, including but not limited to\r\n      communication on electronic mailing lists, source code control systems,\r\n      and issue tracking systems that are managed by, or on behalf of, the\r\n      Licensor for the purpose of discussing and improving the Work, but\r\n      excluding communication that is conspicuously marked or otherwise\r\n      designated in writing by the copyright owner as \"Not a Contribution.\"\r\n\r\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\r\n      on behalf of whom a Contribution has been received by Licensor and\r\n      subsequently incorporated within the Work.\r\n\r\n   2. Grant of Copyright License. Subject to the terms and conditions of\r\n      this License, each Contributor hereby grants to You a perpetual,\r\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\r\n      copyright license to reproduce, prepare Derivative Works of,\r\n      publicly display, publicly perform, sublicense, and distribute the\r\n      Work and such Derivative Works in Source or Object form.\r\n\r\n   3. Grant of Patent License. Subject to the terms and conditions of\r\n      this License, each Contributor hereby grants to You a perpetual,\r\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\r\n      (except as stated in this section) patent license to make, have made,\r\n      use, offer to sell, sell, import, and otherwise transfer the Work,\r\n      where such license applies only to those patent claims licensable\r\n      by such Contributor that are necessarily infringed by their\r\n      Contribution(s) alone or by combination of their Contribution(s)\r\n      with the Work to which such Contribution(s) was submitted. If You\r\n      institute patent litigation against any entity (including a\r\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\r\n      or a Contribution incorporated within the Work constitutes direct\r\n      or contributory patent infringement, then any patent licenses\r\n      granted to You under this License for that Work shall terminate\r\n      as of the date such litigation is filed.\r\n\r\n   4. Redistribution. You may reproduce and distribute copies of the\r\n      Work or Derivative Works thereof in any medium, with or without\r\n      modifications, and in Source or Object form, provided that You\r\n      meet the following conditions:\r\n\r\n      (a) You must give any other recipients of the Work or\r\n          Derivative Works a copy of this License; and\r\n\r\n      (b) You must cause any modified files to carry prominent notices\r\n          stating that You changed the files; and\r\n\r\n      (c) You must retain, in the Source form of any Derivative Works\r\n          that You distribute, all copyright, patent, trademark, and\r\n          attribution notices from the Source form of the Work,\r\n          excluding those notices that do not pertain to any part of\r\n          the Derivative Works; and\r\n\r\n      (d) If the Work includes a \"NOTICE\" text file as part of its\r\n          distribution, then any Derivative Works that You distribute must\r\n          include a readable copy of the attribution notices contained\r\n          within such NOTICE file, excluding those notices that do not\r\n          pertain to any part of the Derivative Works, in at least one\r\n          of the following places: within a NOTICE text file distributed\r\n          as part of the Derivative Works; within the Source form or\r\n          documentation, if provided along with the Derivative Works; or,\r\n          within a display generated by the Derivative Works, if and\r\n          wherever such third-party notices normally appear. The contents\r\n          of the NOTICE file are for informational purposes only and\r\n          do not modify the License. You may add Your own attribution\r\n          notices within Derivative Works that You distribute, alongside\r\n          or as an addendum to the NOTICE text from the Work, provided\r\n          that such additional attribution notices cannot be construed\r\n          as modifying the License.\r\n\r\n      You may add Your own copyright statement to Your modifications and\r\n      may provide additional or different license terms and conditions\r\n      for use, reproduction, or distribution of Your modifications, or\r\n      for any such Derivative Works as a whole, provided Your use,\r\n      reproduction, and distribution of the Work otherwise complies with\r\n      the conditions stated in this License.\r\n\r\n   5. Submission of Contributions. Unless You explicitly state otherwise,\r\n      any Contribution intentionally submitted for inclusion in the Work\r\n      by You to the Licensor shall be under the terms and conditions of\r\n      this License, without any additional terms or conditions.\r\n      Notwithstanding the above, nothing herein shall supersede or modify\r\n      the terms of any separate license agreement you may have executed\r\n      with Licensor regarding such Contributions.\r\n\r\n   6. Trademarks. This License does not grant permission to use the trade\r\n      names, trademarks, service marks, or product names of the Licensor,\r\n      except as required for reasonable and customary use in describing the\r\n      origin of the Work and reproducing the content of the NOTICE file.\r\n\r\n   7. Disclaimer of Warranty. Unless required by applicable law or\r\n      agreed to in writing, Licensor provides the Work (and each\r\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\r\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\r\n      implied, including, without limitation, any warranties or conditions\r\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\r\n      PARTICULAR PURPOSE. You are solely responsible for determining the\r\n      appropriateness of using or redistributing the Work and assume any\r\n      risks associated with Your exercise of permissions under this License.\r\n\r\n   8. Limitation of Liability. In no event and under no legal theory,\r\n      whether in tort (including negligence), contract, or otherwise,\r\n      unless required by applicable law (such as deliberate and grossly\r\n      negligent acts) or agreed to in writing, shall any Contributor be\r\n      liable to You for damages, including any direct, indirect, special,\r\n      incidental, or consequential damages of any character arising as a\r\n      result of this License or out of the use or inability to use the\r\n      Work (including but not limited to damages for loss of goodwill,\r\n      work stoppage, computer failure or malfunction, or any and all\r\n      other commercial damages or losses), even if such Contributor\r\n      has been advised of the possibility of such damages.\r\n\r\n   9. Accepting Warranty or Additional Liability. While redistributing\r\n      the Work or Derivative Works thereof, You may choose to offer,\r\n      and charge a fee for, acceptance of support, warranty, indemnity,\r\n      or other liability obligations and/or rights consistent with this\r\n      License. However, in accepting such obligations, You may act only\r\n      on Your own behalf and on Your sole responsibility, not on behalf\r\n      of any other Contributor, and only if You agree to indemnify,\r\n      defend, and hold each Contributor harmless for any liability\r\n      incurred by, or claims asserted against, such Contributor by reason\r\n      of your accepting any such warranty or additional liability.\r\n\r\n   END OF TERMS AND CONDITIONS\r\n\r\n   APPENDIX: How to apply the Apache License to your work.\r\n\r\n      To apply the Apache License to your work, attach the following\r\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\r\n      replaced with your own identifying information. (Don't include\r\n      the brackets!)  The text should be enclosed in the appropriate\r\n      comment syntax for the file format. We also recommend that a\r\n      file or class name and description of purpose be included on the\r\n      same \"printed page\" as the copyright notice for easier\r\n      identification within third-party archives.\r\n\r\n   Copyright [yyyy] [name of copyright owner]\r\n\r\n   Licensed under the Apache License, Version 2.0 (the \"License\");\r\n   you may not use this file except in compliance with the License.\r\n   You may obtain a copy of the License at\r\n\r\n       http://www.apache.org/licenses/LICENSE-2.0\r\n\r\n   Unless required by applicable law or agreed to in writing, software\r\n   distributed under the License is distributed on an \"AS IS\" BASIS,\r\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n   See the License for the specific language governing permissions and\r\n   limitations under the License.\r\n"
  },
  {
    "path": "README.md",
    "content": "<h1>环境搭建 & 打包</h1>\n<strong>环境搭建：</strong>\n<ol>\n<li>进入 $otter_home/lib 目录</li>\n<li>执行：bash install.sh</li>\n</ol>\n<strong>打包：</strong>\n<ol>\n<li>进入$otter_home目录</li>\n<li>执行：mvn clean install -Dmaven.test.skip -Denv=release</li>\n<li>发布包位置：$otter_home/target</li>\n</ol>\n\n<h1>\n<a name=\"%E9%A1%B9%E7%9B%AE%E8%83%8C%E6%99%AF\" class=\"anchor\" href=\"#%E9%A1%B9%E7%9B%AE%E8%83%8C%E6%99%AF\"><span class=\"octicon octicon-link\"></span></a>项目背景</h1>\n<p>\n   &nbsp;&nbsp;&nbsp;阿里巴巴B2B公司，因为业务的特性，卖家主要集中在国内，买家主要集中在国外，所以衍生出了杭州和美国异地机房的需求，同时为了提升用户体验，整个机房的架构为双A，两边均可写，由此诞生了otter这样一个产品。 </p>\n<p>\n   &nbsp;&nbsp;&nbsp;otter第一版本可追溯到04~05年，此次外部开源的版本为第4版，开发时间从2011年7月份一直持续到现在，目前阿里巴巴B2B内部的本地/异地机房的同步需求基本全上了otter4。\n</p>\n<strong>目前同步规模：</strong>\n<ol>\n<li>同步数据量6亿</li>\n<li>文件同步1.5TB(2000w张图片)</li>\n<li>涉及200+个数据库实例之间的同步</li>\n<li>80+台机器的集群规模</li>\n</ol>\n<h1>\n<a name=\"%E9%A1%B9%E7%9B%AE%E4%BB%8B%E7%BB%8D\" class=\"anchor\" href=\"#%E9%A1%B9%E7%9B%AE%E4%BB%8B%E7%BB%8D\"><span class=\"octicon octicon-link\"></span></a>项目介绍</h1>\n<p>名称：otter ['ɒtə(r)]</p>\n<p>译意： 水獭，数据搬运工</p>\n<p>语言： 纯java开发</p>\n<p>定位： 基于数据库增量日志解析，准实时同步到本机房或异地机房的mysql/oracle数据库. 一个分布式数据库同步系统</p>\n<p> </p>\n<h1>\n<a name=\"%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86\" class=\"anchor\" href=\"#%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86\"><span class=\"octicon octicon-link\"></span></a>工作原理</h1>\n<p><img width=\"848\" src=\"https://camo.githubusercontent.com/2988fbbc7ddfe94ed027cd71720b1ffa5912a635/687474703a2f2f646c322e69746579652e636f6d2f75706c6f61642f6174746163686d656e742f303038382f313138392f64343230636131342d326438302d336435352d383038312d6239303833363036613830312e6a7067\" height=\"303\" alt=\"\"></p>\n<p>原理描述：</p>\n<p>1.   基于Canal开源产品，获取数据库增量日志数据。 什么是Canal,  请<a href=\"https://github.com/alibaba/canal\">点击</a></p>\n<p>2.   典型管理系统架构，manager(web管理)+node(工作节点)</p>\n<p>       &nbsp;&nbsp;&nbsp; a.  manager运行时推送同步配置到node节点</p>\n<p>       &nbsp;&nbsp;&nbsp; b.  node节点将同步状态反馈到manager上</p>\n<p>3.  基于zookeeper，解决分布式状态调度的，允许多node节点之间协同工作. </p>\n<h3>\n<a name=\"%E4%BB%80%E4%B9%88%E6%98%AFcanal-\" class=\"anchor\" href=\"#%E4%BB%80%E4%B9%88%E6%98%AFcanal-\"><span class=\"octicon octicon-link\"></span></a>什么是canal? </h3>\notter之前开源的一个子项目，开源链接地址：<a href=\"http://github.com/alibaba/canal\">http://github.com/alibaba/canal</a>\n<p> </p>\n<h1>\n<a name=\"introduction\" class=\"anchor\" href=\"#introduction\"><span class=\"octicon octicon-link\"></span></a>Introduction</h1>\n<p>See the page for introduction: <a class=\"internal present\" href=\"https://github.com/alibaba/otter/wiki/Introduction\">Introduction</a>.</p>\n<h1>\n<a name=\"quickstart\" class=\"anchor\" href=\"#quickstart\"><span class=\"octicon octicon-link\"></span></a>QuickStart</h1>\n<p>See the page for quick start: <a class=\"internal present\" href=\"https://github.com/alibaba/otter/wiki/QuickStart\">QuickStart</a>.</p>\n<p> </p>\n<h1>\n<a name=\"adminguide\" class=\"anchor\" href=\"#adminguide\"><span class=\"octicon octicon-link\"></span></a>AdminGuide</h1>\n<p>See the page for admin deploy guide : <a class=\"internal present\" href=\"https://github.com/alibaba/otter/wiki/Adminguide\">AdminGuide</a></p>\n<p> </p>\n<h1>\n<a name=\"%E7%9B%B8%E5%85%B3%E6%96%87%E6%A1%A3\" class=\"anchor\" href=\"#%E7%9B%B8%E5%85%B3%E6%96%87%E6%A1%A3\"><span class=\"octicon octicon-link\"></span></a>相关文档</h1>\n<p>See the page for 文档: <a class=\"internal present\" href=\"https://github.com/alibaba/otter/wiki/%E7%9B%B8%E5%85%B3ppt%26pdf\">相关PPT&amp;PDF</a></p>\n<p> </p>\n<h1>\n<a name=\"%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98\" class=\"anchor\" href=\"#%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98\"><span class=\"octicon octicon-link\"></span></a>常见问题</h1>\n<p>See the page for FAQ: <a class=\"internal present\" href=\"https://github.com/alibaba/otter/wiki/Faq\">FAQ</a></p>\n<p> </p>\n\n<h1>\n<a name=\"%E7%89%88%E6%9C%AC%E7%9B%B8%E5%85%B3-\" class=\"anchor\" href=\"#%E7%89%88%E6%9C%AC%E7%9B%B8%E5%85%B3-\"><span class=\"octicon octicon-link\"></span></a>版本相关: </h1>\n<p>1. 建议版本：4.2.15  (otter开源版本从内部演变而来，所以初始版本直接从4.x开始) </p>\n<p>2. 下载发布包：<a href=\"https://github.com/alibaba/otter/releases\">download </a></p>\n<p>3. maven依赖 ： 暂无 </p>\n\n<h1>相关开源</h1>\n<ol>\n<li>阿里巴巴mysql数据库binlog的增量订阅&消费组件：<a href=\"http://github.com/alibaba/canal\">http://github.com/alibaba/canal</a></li>\n<li>阿里巴巴去Oracle数据迁移同步工具(目标支持MySQL/DRDS)：<a href=\"http://github.com/alibaba/yugong\">http://github.com/alibaba/yugong</a></li>\n</ol>\n\n<p> </p>\n<h1>\n<a name=\"%E9%97%AE%E9%A2%98%E5%8F%8D%E9%A6%88\" class=\"anchor\" href=\"#%E9%97%AE%E9%A2%98%E5%8F%8D%E9%A6%88\"><span class=\"octicon octicon-link\"></span></a>问题反馈</h1>\n<h3>\n<a name=\"%E6%B3%A8%E6%84%8Fcanalotter-qq%E8%AE%A8%E8%AE%BA%E7%BE%A4%E5%B7%B2%E7%BB%8F%E5%BB%BA%E7%AB%8B%E7%BE%A4%E5%8F%B7161559791-%E6%AC%A2%E8%BF%8E%E5%8A%A0%E5%85%A5%E8%BF%9B%E8%A1%8C%E6%8A%80%E6%9C%AF%E8%AE%A8%E8%AE%BA\" class=\"anchor\" href=\"#%E6%B3%A8%E6%84%8Fcanalotter-qq%E8%AE%A8%E8%AE%BA%E7%BE%A4%E5%B7%B2%E7%BB%8F%E5%BB%BA%E7%AB%8B%E7%BE%A4%E5%8F%B7161559791-%E6%AC%A2%E8%BF%8E%E5%8A%A0%E5%85%A5%E8%BF%9B%E8%A1%8C%E6%8A%80%E6%9C%AF%E8%AE%A8%E8%AE%BA\"><span class=\"octicon octicon-link\"></span></a>注意：canal&amp;otter QQ讨论群已经建立，群号：161559791 ，欢迎加入进行技术讨论。</h3>\n\n<p>1.  <span>qq交流群： 161559791</span></p>\n<p><span>2.  </span><span>邮件交流： jianghang115@gmail.com</span></p>\n<p><span>3.  </span><span>新浪微博： agapple0002</span></p>\n<p><span>4.  </span><span>报告issue：</span><a href=\"https://github.com/alibaba/otter/issues\">issues</a></p>\n<p> </p>\n"
  },
  {
    "path": "codeformat.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<profiles version=\"12\">\n<profile kind=\"CodeFormatterProfile\" name=\"otter-format\" version=\"12\">\n<setting id=\"org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.disabling_tag\" value=\"@formatter:off\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration\" value=\"end_of_line\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.blank_lines_before_field\" value=\"0\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.use_on_off_tags\" value=\"false\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_ellipsis\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_multiple_fields\" value=\"16\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer\" value=\"18\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_conditional_expression\" value=\"4\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_binary_operator\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_array_initializer\" value=\"end_of_line\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.blank_lines_after_package\" value=\"1\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.continuation_indentation\" value=\"2\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation\" value=\"84\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk\" value=\"1\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_binary_operator\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.blank_lines_before_package\" value=\"0\"/>\n<setting id=\"org.eclipse.jdt.core.compiler.source\" value=\"1.7\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.comment.format_line_comments\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.join_wrapped_lines\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call\" value=\"84\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.blank_lines_before_member_type\" value=\"1\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.align_type_members_on_columns\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation\" value=\"84\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_unary_operator\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.comment.indent_parameter_description\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.lineSplit\" value=\"120\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration\" value=\"1\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.indentation.size\" value=\"4\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.enabling_tag\" value=\"@formatter:on\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration\" value=\"0\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_assignment\" value=\"0\"/>\n<setting id=\"org.eclipse.jdt.core.compiler.problem.assertIdentifier\" value=\"error\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.tabulation.char\" value=\"space\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.indent_statements_compare_to_body\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.blank_lines_before_method\" value=\"1\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line\" value=\"false\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration\" value=\"18\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration\" value=\"end_of_line\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_method_declaration\" value=\"0\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_switch\" value=\"end_of_line\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments\" value=\"false\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.compiler.problem.enumIdentifier\" value=\"error\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_ellipsis\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_block\" value=\"end_of_line\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_method_declaration\" value=\"end_of_line\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.compact_else_if\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.comment.indent_root_tags\" value=\"false\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_enum_constant\" value=\"end_of_line\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch\" value=\"16\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.tabulation.size\" value=\"4\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration\" value=\"18\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.indent_empty_lines\" value=\"false\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_block_in_case\" value=\"end_of_line\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve\" value=\"1\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression\" value=\"84\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.compiler.compliance\" value=\"1.7\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer\" value=\"2\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression\" value=\"84\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_unary_operator\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line\" value=\"false\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line\" value=\"false\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration\" value=\"0\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_binary_expression\" value=\"18\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration\" value=\"end_of_line\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode\" value=\"enabled\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_after_label\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant\" value=\"18\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.comment.format_javadoc_comments\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.comment.line_length\" value=\"80\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.blank_lines_between_import_groups\" value=\"1\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_semicolon\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration\" value=\"end_of_line\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body\" value=\"0\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.wrap_before_binary_operator\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations\" value=\"1\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.indent_statements_compare_to_block\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration\" value=\"18\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.join_lines_in_comments\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_compact_if\" value=\"16\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.blank_lines_before_imports\" value=\"1\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.comment.format_html\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration\" value=\"18\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.comment.format_source_code\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration\" value=\"18\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.compiler.codegen.targetPlatform\" value=\"1.7\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_resources_in_try\" value=\"80\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations\" value=\"false\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation\" value=\"0\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.comment.format_header\" value=\"false\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.comment.format_block_comments\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.alignment_for_enum_constants\" value=\"18\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.brace_position_for_type_declaration\" value=\"end_of_line\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.blank_lines_after_imports\" value=\"1\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header\" value=\"true\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for\" value=\"insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column\" value=\"false\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments\" value=\"do not insert\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column\" value=\"false\"/>\n<setting id=\"org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line\" value=\"true\"/>\n</profile>\n</profiles>\n"
  },
  {
    "path": "codetemplates.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?><templates><template autoinsert=\"true\" context=\"gettercomment_context\" deleted=\"false\" description=\"Comment for getter method\" enabled=\"true\" id=\"org.eclipse.jdt.ui.text.codetemplates.gettercomment\" name=\"gettercomment\">/**\n * @return the ${bare_field_name}\n */</template><template autoinsert=\"true\" context=\"settercomment_context\" deleted=\"false\" description=\"Comment for setter method\" enabled=\"true\" id=\"org.eclipse.jdt.ui.text.codetemplates.settercomment\" name=\"settercomment\">/**\n * @param ${param} the ${bare_field_name} to set\n */</template><template autoinsert=\"true\" context=\"constructorcomment_context\" deleted=\"false\" description=\"Comment for created constructors\" enabled=\"true\" id=\"org.eclipse.jdt.ui.text.codetemplates.constructorcomment\" name=\"constructorcomment\">/**\n * ${tags}\n */</template><template autoinsert=\"false\" context=\"filecomment_context\" deleted=\"false\" description=\"Comment for created Java files\" enabled=\"true\" id=\"org.eclipse.jdt.ui.text.codetemplates.filecomment\" name=\"filecomment\"/><template autoinsert=\"false\" context=\"typecomment_context\" deleted=\"false\" description=\"Comment for created types\" enabled=\"true\" id=\"org.eclipse.jdt.ui.text.codetemplates.typecomment\" name=\"typecomment\">/**\n * @author ${user} ${date} ${time}\n * @since 1.0.0\n */</template><template autoinsert=\"true\" context=\"fieldcomment_context\" deleted=\"false\" description=\"Comment for fields\" enabled=\"true\" id=\"org.eclipse.jdt.ui.text.codetemplates.fieldcomment\" name=\"fieldcomment\">/**\n * \n */</template><template autoinsert=\"true\" context=\"methodcomment_context\" deleted=\"false\" description=\"Comment for non-overriding methods\" enabled=\"true\" id=\"org.eclipse.jdt.ui.text.codetemplates.methodcomment\" name=\"methodcomment\">/**\n * ${tags}\n */</template><template autoinsert=\"false\" context=\"overridecomment_context\" deleted=\"false\" description=\"Comment for overriding methods\" enabled=\"true\" id=\"org.eclipse.jdt.ui.text.codetemplates.overridecomment\" name=\"overridecomment\"/><template autoinsert=\"true\" context=\"delegatecomment_context\" deleted=\"false\" description=\"Comment for delegate methods\" enabled=\"true\" id=\"org.eclipse.jdt.ui.text.codetemplates.delegatecomment\" name=\"delegatecomment\">/**\n * ${tags}\n * ${see_to_target}\n */</template><template autoinsert=\"false\" context=\"newtype_context\" deleted=\"false\" description=\"Newly created files\" enabled=\"true\" id=\"org.eclipse.jdt.ui.text.codetemplates.newtype\" name=\"newtype\">${filecomment}\n${package_declaration}\n${typecomment}\n${type_declaration}</template><template autoinsert=\"true\" context=\"classbody_context\" deleted=\"false\" description=\"Code in new class type bodies\" enabled=\"true\" id=\"org.eclipse.jdt.ui.text.codetemplates.classbody\" name=\"classbody\">\n</template><template autoinsert=\"true\" context=\"interfacebody_context\" deleted=\"false\" description=\"Code in new interface type bodies\" enabled=\"true\" id=\"org.eclipse.jdt.ui.text.codetemplates.interfacebody\" name=\"interfacebody\">\n</template><template autoinsert=\"true\" context=\"enumbody_context\" deleted=\"false\" description=\"Code in new enum type bodies\" enabled=\"true\" id=\"org.eclipse.jdt.ui.text.codetemplates.enumbody\" name=\"enumbody\">\n</template><template autoinsert=\"true\" context=\"annotationbody_context\" deleted=\"false\" description=\"Code in new annotation type bodies\" enabled=\"true\" id=\"org.eclipse.jdt.ui.text.codetemplates.annotationbody\" name=\"annotationbody\">\n</template><template autoinsert=\"true\" context=\"catchblock_context\" deleted=\"false\" description=\"Code in new catch blocks\" enabled=\"true\" id=\"org.eclipse.jdt.ui.text.codetemplates.catchblock\" name=\"catchblock\">// ${todo} Auto-generated catch block\n${exception_var}.printStackTrace();</template><template autoinsert=\"true\" context=\"methodbody_context\" deleted=\"false\" description=\"Code in created method stubs\" enabled=\"true\" id=\"org.eclipse.jdt.ui.text.codetemplates.methodbody\" name=\"methodbody\">// ${todo} Auto-generated method stub\n${body_statement}</template><template autoinsert=\"true\" context=\"constructorbody_context\" deleted=\"false\" description=\"Code in created constructor stubs\" enabled=\"true\" id=\"org.eclipse.jdt.ui.text.codetemplates.constructorbody\" name=\"constructorbody\">${body_statement}\n// ${todo} Auto-generated constructor stub</template><template autoinsert=\"true\" context=\"getterbody_context\" deleted=\"false\" description=\"Code in created getters\" enabled=\"true\" id=\"org.eclipse.jdt.ui.text.codetemplates.getterbody\" name=\"getterbody\">return ${field};</template><template autoinsert=\"true\" context=\"setterbody_context\" deleted=\"false\" description=\"Code in created setters\" enabled=\"true\" id=\"org.eclipse.jdt.ui.text.codetemplates.setterbody\" name=\"setterbody\">${field} = ${param};</template></templates>\n"
  },
  {
    "path": "docker/Dockerfile",
    "content": "FROM canal/otter-osbase:v1\n\nMAINTAINER agapple (jianghang115@gmail.com)\n\n# install otter\nCOPY image/ /tmp/docker/\nCOPY manager.deployer-*.tar.gz /home/admin/\nCOPY node.deployer-*.tar.gz /home/admin/\n\nRUN \\\n    cp -R /tmp/docker/alidata /alidata && \\\n    chmod +x /alidata/bin/* && \\\n    mkdir -p /home/admin && \\\n    cp -R /tmp/docker/admin/* /home/admin/  && \\\n    /bin/cp -f alidata/bin/lark-wait /usr/bin/lark-wait && \\\n    mkdir -p /home/admin/manager && \\\n    tar -xzvf /home/admin/manager.deployer-*.tar.gz -C /home/admin/manager && \\\n    /bin/rm -f /home/admin/manager.deployer-*.tar.gz && \\\n    mkdir -p /home/admin/node && \\\n    tar -xzvf /home/admin/node.deployer-*.tar.gz -C /home/admin/node && \\\n    /bin/rm -f /home/admin/node.deployer-*.tar.gz && \\\n    mkdir -p home/admin/manager/logs  && \\\n    mkdir -p home/admin/node/logs  && \\\n    mkdir -p home/admin/zkData  && \\\n    chmod +x /home/admin/*.sh  && \\\n    chmod +x /home/admin/bin/*.sh  && \\\n    chown admin: -R /home/admin && \\\n    yum clean all && \\\n    true\n\nENV DOCKER_DEPLOY_TYPE=VM PATH=$PATH:/usr/local/mysql/bin:/usr/local/mysql/scripts\n\nWORKDIR /home/admin\n\nENTRYPOINT [ \"/alidata/bin/main.sh\" ]\nCMD [ \"/home/admin/app.sh\" ]\n"
  },
  {
    "path": "docker/base/Dockerfile",
    "content": "FROM canal/osbase:v1\n\nMAINTAINER agapple (jianghang115@gmail.com)\n\nRUN \\\n    groupadd -r mysql && useradd -r -g mysql mysql && \\\n    yum -y install wget mysql-server --nogpgcheck && \\\n    yum clean all && \\\n    wget -q https://mirrors.tuna.tsinghua.edu.cn/apache/zookeeper/zookeeper-3.4.13/zookeeper-3.4.13.tar.gz -O /home/admin/zookeeper-3.4.13.tar.gz && \\\n    tar -xzvf /home/admin/zookeeper-*.tar.gz -C /home/admin && \\\n    /bin/cp -rf /home/admin/zookeeper-3.4.13/conf/zoo_sample.cfg /home/admin/zookeeper-3.4.13/conf/zoo.cfg && \\\n    sed -i -e 's/^dataDir=\\/tmp\\/zookeeper$/dataDir=\\/home\\/admin\\/zkData/' /home/admin/zookeeper-3.4.13/conf/zoo.cfg && \\\n    sed -i -e 's/^#autopurge/autopurge/' /home/admin/zookeeper-3.4.13/conf/zoo.cfg && \\\n    /bin/rm -f /home/admin/zookeeper-3.4.13.tar.gz && \\\n    true\n\nCMD [\"/bin/bash\"]\n"
  },
  {
    "path": "docker/build.sh",
    "content": "#!/bin/bash\n\ncurrent_path=`pwd`\ncase \"`uname`\" in\n    Darwin)\n        bin_abs_path=`cd $(dirname $0); pwd`\n        ;;\n    Linux)\n        bin_abs_path=$(readlink -f $(dirname $0))\n        ;;\n    *)\n        bin_abs_path=`cd $(dirname $0); pwd`\n        ;;\nesac\nBASE=${bin_abs_path}\n\nif [ \"$1\" == \"base\" ] ; then\n    docker build --no-cache -t canal/otter-osbase $BASE/base\nelse \n    rm -rf $BASE/node.*.tar.gz ; \n    rm -rf $BASE/manager.*.tar.gz ; \n    cd $BASE/../ && mvn clean package -Dmaven.test.skip -Denv=release && cd $current_path ;\n    cp $BASE/../target/node.deployer-*.tar.gz $BASE/\n    cp $BASE/../target/manager.deployer-*.tar.gz $BASE/\n    docker build --no-cache -t canal/otter-all $BASE/\nfi\n\n"
  },
  {
    "path": "docker/image/admin/app.sh",
    "content": "#!/bin/bash\nset -e\n\nsource /etc/profile\nexport JAVA_HOME=/usr/java/latest\nexport PATH=$JAVA_HOME/bin:$PATH\ntouch /tmp/start.log\nchown admin: /tmp/start.log\nchown admin: /home/admin/manager\nchown admin: /home/admin/node\nchown admin: /home/admin/zkData\nhost=`hostname -i`\n\n# default config\nif [ -z \"${RUN_MODE}\" ]; then\n    RUN_MODE=\"ALL\"\nfi\n\nif [ -z \"${MYSQL_USER_PASSWORD}\" ]; then\n    MYSQL_USER_PASSWORD=\"otter\"\nfi\nif [ -z \"${OTTER_MANAGER_MYSQL}\" ]; then\n    OTTER_MANAGER_MYSQL=\"127.0.0.1:3306\"\nfi\n# waitterm\n#   wait TERM/INT signal.\n#   see: http://veithen.github.io/2014/11/16/sigterm-propagation.html\nwaitterm() {\n        local PID\n        # any process to block\n        tail -f /dev/null &\n        PID=\"$!\"\n        # setup trap, could do nothing, or just kill the blocker\n        trap \"kill -TERM ${PID}\" TERM INT\n        # wait for signal, ignore wait exit code\n        wait \"${PID}\" || true\n        # clear trap\n        trap - TERM INT\n        # wait blocker, ignore blocker exit code\n        wait \"${PID}\" 2>/dev/null || true\n}\n\n# waittermpid \"${PIDFILE}\".\n#   monitor process by pidfile && wait TERM/INT signal.\n#   if the process disappeared, return 1, means exit with ERROR.\n#   if TERM or INT signal received, return 0, means OK to exit.\nwaittermpid() {\n        local PIDFILE PID do_run error\n        PIDFILE=\"${1?}\"\n        do_run=true\n        error=0\n        trap \"do_run=false\" TERM INT\n        while \"${do_run}\" ; do\n                PID=\"$(cat \"${PIDFILE}\")\"\n                if ! ps -p \"${PID}\" >/dev/null 2>&1 ; then\n                        do_run=false\n                        error=1\n                else\n                        sleep 1\n                fi\n        done\n        trap - TERM INT\n        return \"${error}\"\n}\n\n\nfunction checkStart() {\n    local name=$1\n    local cmd=$2\n    local timeout=$3\n    cost=5\n    while [ $timeout -gt 0 ]; do\n        ST=`eval $cmd`\n        if [ \"$ST\" == \"0\" ]; then\n            sleep 1\n            let timeout=timeout-1\n            let cost=cost+1\n        elif [ \"$ST\" == \"\" ]; then\n            sleep 1\n            let timeout=timeout-1\n            let cost=cost+1\n        else\n            break\n        fi\n    done\n    echo \"$name start successful\"\n}\n\nfunction start_zookeeper() {\n    echo \"start zookeeper ...\"\n    # start zookeeper\n    rm -f /home/admin/zkData/myid\n    sed -i '/^server\\..*/d' /home/admin/zookeeper-3.4.13/conf/zoo.cfg\n    su admin -c \"mkdir -p /home/admin/zkData; cd /home/admin/zkData; /home/admin/zookeeper-3.4.13/bin/zkServer.sh start >> /home/admin/zkData/zookeeper.log 2>&1\"\n    sleep 5\n    #check start\n    checkStart \"zookeeper\" \"echo stat | nc 127.0.0.1 2181 | grep -c Outstanding\" 30\n}\n\nfunction stop_zookeeper() {\n    # stop zookeeper\n    echo \"stop zookeeper\"\n    su admin -c 'mkdir -p /home/admin/zkData; cd /home/admin/zkData; /home/admin/zookeeper-3.4.13/bin/zkServer.sh stop >> /home/admin/zkData/zookeeper.log 2>&1'\n    echo \"stop zookeeper successful ...\"\n}\n\nfunction start_manager() {\n    echo \"start manager ...\"\n    # start manager\n    if [ -n \"${OTTER_MANAGER_MYSQL}\" ] ; then\n        cmd=\"sed -i -e 's/^otter.database.driver.url.*$/otter.database.driver.url = jdbc:mysql:\\/\\/${OTTER_MANAGER_MYSQL}\\/otter/' /home/admin/manager/conf/otter.properties\"\n        eval $cmd\n        cmd=\"sed -i -e 's/^otter.database.driver.username.*$/otter.database.driver.username = ${MYSQL_USER}/' /home/admin/manager/conf/otter.properties\"\n        eval $cmd\n        cmd=\"sed -i -e 's/^otter.database.driver.password.*$/otter.database.driver.password = ${MYSQL_USER_PASSWORD}/' /home/admin/manager/conf/otter.properties\"\n        eval $cmd\n        cmd=\"sed -i -e 's/^otter.communication.manager.port.*$/otter.communication.manager.port = 8081/' /home/admin/manager/conf/otter.properties\"\n        eval $cmd\n        cmd=\"sed -i -e 's/^otter.domainName.*$/otter.domainName = ${host}/' /home/admin/manager/conf/otter.properties\"\n        eval $cmd\n    fi\n    su admin -c \"cd /home/admin/manager/bin ; sh startup.sh 1>>/tmp/start.log 2>&1\"\n    #check start\n    sleep 5\n    checkStart \"manager\" \"nc 127.0.0.1 8080 -w 1 -z | wc -l\" 60\n}\n\nfunction stop_manager() {\n    # stop manager\n    echo \"stop manager\"\n    su admin -c 'cd /home/admin/manager/bin; sh stop.sh 1>>/tmp/start.log 2>&1'\n    echo \"stop manager successful ...\"\n}\n\nfunction start_node() {\n    echo \"start node ...\"\n    # start node\n    cmd=\"sed -i -e 's/^otter.manager.address.*$/otter.manager.address = 127.0.0.1:8081/' /home/admin/node/conf/otter.properties\"\n    eval $cmd\n    \n    su admin -c 'cd /home/admin/node/bin/ && echo 1 > /home/admin/node/conf/nid && sh startup.sh 1>>/tmp/start.log 2>&1'\n    sleep 5\n    #check start\n    checkStart \"node\" \"nc 127.0.0.1 2088 -w 1 -z | wc -l\" 30\n}\n\nfunction stop_node() {\n    # stop node\n    echo \"stop node\"\n    su admin -c 'cd /home/admin/node/bin/ && sh stop.sh'\n    echo \"stop node successful ...\"\n}\n\nfunction start_mysql() {\n    echo \"start mysql ...\"\n    # start mysql\n    MYSQL_ROOT_PASSWORD=Hello1234\n    MYSQL_USER=otter\n    MYSQL_DATABASE=otter\n    if [ -z \"$(ls -A /var/lib/mysql)\" ]; then\n        mysql_install_db --user=mysql --datadir=/var/lib/mysql 1>>/tmp/start.log 2>&1\n        # These statements _must_ be on individual lines, and _must_ end with\n        # semicolons (no line breaks or comments are permitted).\n        # TODO proper SQL escaping on ALL the things D:\n        TEMP_FILE='/tmp/init.sql'\n        echo \"update mysql.user set password=password('${MYSQL_ROOT_PASSWORD}') where user='root';\" >> $TEMP_FILE\n        echo \"grant all privileges on *.* to 'root'@'%' WITH GRANT OPTION ;\" >> $TEMP_FILE\n        echo \"create database if not exists $MYSQL_DATABASE ;\" >> $TEMP_FILE\n        echo \"create user $MYSQL_USER identified by '$MYSQL_USER_PASSWORD' ;\" >> $TEMP_FILE\n        echo \"grant all privileges on $MYSQL_DATABASE.* to '$MYSQL_USER'@'%' identified by '$MYSQL_USER_PASSWORD' ;\" >> $TEMP_FILE\n        echo \"grant all privileges on $MYSQL_DATABASE.* to '$MYSQL_USER'@'localhost' identified by '$MYSQL_USER_PASSWORD' ;\" >> $TEMP_FILE\n        echo \"flush privileges;\" >> $TEMP_FILE\n        service mysqld start\n        checkStart \"mysql\" \"echo 'show status' | mysql -s -h127.0.0.1 -P3306 -uroot | grep -c Uptime\" 30\n        mysql -h127.0.0.1 -uroot -e \"source $TEMP_FILE\" 1>>/tmp/start.log 2>&1\n\n        cmd=\"sed -i -e 's/#OTTER_MY_ZK#/127.0.0.1:2181/' /home/admin/bin/ddl.sql\"\n        eval $cmd\n        cmd=\"sed -i -e 's/#OTTER_NODE_HOST#/127.0.0.1/' /home/admin/bin/ddl.sql\"\n        eval $cmd\n        cmd=\"mysql -h127.0.0.1 -u$MYSQL_USER -p$MYSQL_USER_PASSWORD $MYSQL_DATABASE -e 'source /home/admin/bin/ddl.sql' 1>>/tmp/start.log 2>&1\"\n        eval $cmd\n        /bin/rm -f /home/admin/bin/ddl.sql\n    else\n        chown -R mysql:mysql /var/lib/mysql\n        service mysqld start\n        #check start\n        checkStart \"mysql\" \"echo 'show status' | mysql -b -s  -h127.0.0.1 -P3306 -uroot -p$MYSQL_ROOT_PASSWORD | grep -c Uptime\" 30\n    fi\n}\n\nfunction stop_mysql() {\n    echo \"stop mysql ...\"\n    # stop mysql\n    service mysqld stop\n    echo \"stop mysql successful ...\"\n}\n\necho \"==> START ...\"\nstart_mysql\nstart_zookeeper\nstart_manager\nstart_node\necho \"you can visit manager link : http://$host:8080/ , just have fun !\"\n\necho \"==> START SUCCESSFUL ...\"\n\ntail -f /dev/null &\n# wait TERM signal\nwaitterm\n\necho \"==> STOP\"\n\nstop_node\nstop_manager\nstop_zookeeper\nstop_zookeeper\nstop_mysql\n\necho \"==> STOP SUCCESSFUL ...\""
  },
  {
    "path": "docker/image/admin/bin/clean_log",
    "content": "# cron clean log once per minute\n*/2 * * * * admin /home/admin/bin/clean_log.sh >>/tmp/clean_log.log 2>&1\n"
  },
  {
    "path": "docker/image/admin/bin/clean_log.sh",
    "content": "#!/bin/bash\n\n# Global Settings\nPATH=\"$HOME/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/usr/X11R6/bin:/root/bin\"\nexport PATH\n\nCUTOFF=\"85\"\n#获取磁盘使用率最高的分区\nUSAGE=$(df -h|awk 'NR>1 {gsub(/%$/,\"\",$5);print $5 }'|sort -nr|head -1)\nbefore=$USAGE\n\nbaseClean(){\n    #删除tmp目录15天前的文件。\n    #更新文档时间戳\n    if [ -d /tmp/hsperfdata_admin ]\n    then\n        touch /tmp/hsperfdata_admin\n        touch /tmp/hsperfdata_admin/*\n    fi\n\n    find /tmp/ -type f -mtime +15 | xargs -t rm -rf >/dev/null 2>&1\n\n\n    now=$(df -h|awk 'NR>1 {gsub(/%$/,\"\",$5);print $5 }'|sort -nr|head -1)\n    echo \"before:$before; now:$now\"\n}\n\n\nMANAGER_DIR=\"/home/admin/manager/logs\"\nNODE_DIR=\"/home/admin/node/logs\"\nif [[ -d $MANAGER_DIR ]]; then\n  USAGE=$(df -h|awk 'NR>1 {gsub(/%$/,\"\",$5);print $5 }'|sort -nr|head -1)\n  if [[ $USAGE -ge 90 ]]; then\n        find $MANAGER_DIR -type f -mtime +7 | xargs rm -rf {}\n  fi\n  USAGE=$(df -h|awk 'NR>1 {gsub(/%$/,\"\",$5);print $5 }'|sort -nr|head -1)\n  if [[ $USAGE -ge 80 ]]; then\n        find $MANAGER_DIR -type f -mtime +3 | xargs rm -rf {}\n  fi\n  USAGE=$(df -h|awk 'NR>1 {gsub(/%$/,\"\",$5);print $5 }'|sort -nr|head -1)\n  if [[ $USAGE -ge 80 ]]; then\n        find $MANAGER_DIR -type d -empty -mtime +3 | grep -v manager | xargs rm -rf {}\n        find $MANAGER_DIR -type f -iname '*.tmp' | xargs rm -rf {}\n  fi\n  baseClean\n  exit 0\nfi\n\nif [[ -d $NODE_DIR ]]; then\n  USAGE=$(df -h|awk 'NR>1 {gsub(/%$/,\"\",$5);print $5 }'|sort -nr|head -1)\n  if [[ $USAGE -ge 90 ]]; then\n        find $NODE_DIR -type f -mtime +7 | xargs rm -rf {}\n  fi\n  USAGE=$(df -h|awk 'NR>1 {gsub(/%$/,\"\",$5);print $5 }'|sort -nr|head -1)\n  if [[ $USAGE -ge 80 ]]; then\n        find $NODE_DIR -type f -mtime +3 | xargs rm -rf {}\n  fi\n  USAGE=$(df -h|awk 'NR>1 {gsub(/%$/,\"\",$5);print $5 }'|sort -nr|head -1)\n  if [[ $USAGE -ge 80 ]]; then\n        find $NODE_DIR -type d -empty -mtime +3 | grep -v node | xargs rm -rf {}\n        find $NODE_DIR -type f -iname '*.tmp' | xargs rm -rf {}\n  fi\n  baseClean\n  exit 0\nfi"
  },
  {
    "path": "docker/image/admin/bin/ddl.sql",
    "content": "CREATE DATABASE /*!32312 IF NOT EXISTS*/ `otter` /*!40100 DEFAULT CHARACTER SET utf8 COLLATE utf8_bin */;\n\nUSE `otter`;\n\nCREATE TABLE `ALARM_RULE` (\n  `ID` bigint(20) unsigned NOT NULL AUTO_INCREMENT,\n  `MONITOR_NAME` varchar(1024) DEFAULT NULL,\n  `RECEIVER_KEY` varchar(1024) DEFAULT NULL,\n  `STATUS` varchar(32) DEFAULT NULL,\n  `PIPELINE_ID` bigint(20) NOT NULL,\n  `DESCRIPTION` varchar(256) DEFAULT NULL,\n  `GMT_CREATE` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',\n  `GMT_MODIFIED` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n  `MATCH_VALUE` varchar(1024) DEFAULT NULL,\n  `PARAMETERS` text DEFAULT NULL,\n  PRIMARY KEY (`ID`)\n) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;\n\nCREATE TABLE `AUTOKEEPER_CLUSTER` (\n  `ID` bigint(20) NOT NULL AUTO_INCREMENT,\n  `CLUSTER_NAME` varchar(200) NOT NULL,\n  `SERVER_LIST` varchar(1024) NOT NULL,\n  `DESCRIPTION` varchar(200) DEFAULT NULL,\n  `GMT_CREATE` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',\n  `GMT_MODIFIED` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n  PRIMARY KEY (`ID`)\n) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;\n\nCREATE TABLE `CANAL` (\n  `ID` bigint(20) unsigned NOT NULL AUTO_INCREMENT,\n  `NAME` varchar(200) DEFAULT NULL,\n  `DESCRIPTION` varchar(200) DEFAULT NULL,\n  `PARAMETERS` text DEFAULT NULL,\n  `GMT_CREATE` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',\n  `GMT_MODIFIED` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n  PRIMARY KEY (`ID`),\n  UNIQUE KEY `CANALUNIQUE` (`NAME`)\n) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;\n\nCREATE TABLE `CHANNEL` (\n  `ID` bigint(20) NOT NULL AUTO_INCREMENT,\n  `NAME` varchar(200) NOT NULL,\n  `DESCRIPTION` varchar(200) DEFAULT NULL,\n  `PARAMETERS` text DEFAULT NULL,\n  `GMT_CREATE` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',\n  `GMT_MODIFIED` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n  PRIMARY KEY (`ID`),\n  UNIQUE KEY `CHANNELUNIQUE` (`NAME`)\n) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;\n\nCREATE TABLE `COLUMN_PAIR` (\n  `ID` bigint(20) NOT NULL AUTO_INCREMENT,\n  `SOURCE_COLUMN` varchar(200) DEFAULT NULL,\n  `TARGET_COLUMN` varchar(200) DEFAULT NULL,\n  `DATA_MEDIA_PAIR_ID` bigint(20) NOT NULL,\n  `GMT_CREATE` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',\n  `GMT_MODIFIED` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n  PRIMARY KEY (`ID`),\n  KEY `idx_DATA_MEDIA_PAIR_ID` (`DATA_MEDIA_PAIR_ID`)\n) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;\n\nCREATE TABLE `COLUMN_PAIR_GROUP` (\n  `ID` bigint(20) NOT NULL AUTO_INCREMENT,\n  `DATA_MEDIA_PAIR_ID` bigint(20) NOT NULL,\n  `COLUMN_PAIR_CONTENT` text DEFAULT NULL,\n  `GMT_CREATE` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',\n  `GMT_MODIFIED` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n  PRIMARY KEY (`ID`),\n  KEY `idx_DATA_MEDIA_PAIR_ID` (`DATA_MEDIA_PAIR_ID`)\n) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;\n\nCREATE TABLE `DATA_MEDIA` (\n  `ID` bigint(20) NOT NULL AUTO_INCREMENT,\n  `NAME` varchar(200) NOT NULL,\n  `NAMESPACE` varchar(200) NOT NULL,\n  `PROPERTIES` varchar(1000) NOT NULL,\n  `DATA_MEDIA_SOURCE_ID` bigint(20) NOT NULL,\n  `GMT_CREATE` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',\n  `GMT_MODIFIED` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n  PRIMARY KEY (`ID`),\n  UNIQUE KEY `DATAMEDIAUNIQUE` (`NAME`,`NAMESPACE`,`DATA_MEDIA_SOURCE_ID`)\n) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;\n\nCREATE TABLE `DATA_MEDIA_PAIR` (\n  `ID` bigint(20) NOT NULL AUTO_INCREMENT,\n  `PULLWEIGHT` bigint(20) DEFAULT NULL,\n  `PUSHWEIGHT` bigint(20) DEFAULT NULL,\n  `RESOLVER` text DEFAULT NULL,\n  `FILTER` text DEFAULT NULL,\n  `SOURCE_DATA_MEDIA_ID` bigint(20) DEFAULT NULL,\n  `TARGET_DATA_MEDIA_ID` bigint(20) DEFAULT NULL,\n  `PIPELINE_ID` bigint(20) NOT NULL,\n  `COLUMN_PAIR_MODE` varchar(20) DEFAULT NULL,\n  `GMT_CREATE` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',\n  `GMT_MODIFIED` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n  PRIMARY KEY (`ID`),\n  KEY `idx_PipelineID` (`PIPELINE_ID`,`ID`)\n) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;\n\nCREATE TABLE `DATA_MEDIA_SOURCE` (\n  `ID` bigint(20) NOT NULL AUTO_INCREMENT,\n  `NAME` varchar(200) NOT NULL,\n  `TYPE` varchar(20) NOT NULL,\n  `PROPERTIES` varchar(1000) NOT NULL,\n  `GMT_CREATE` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',\n  `GMT_MODIFIED` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n  PRIMARY KEY (`ID`),\n  UNIQUE KEY `DATAMEDIASOURCEUNIQUE` (`NAME`)\n) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;\n\nCREATE TABLE `DELAY_STAT` (\n  `ID` bigint(20) NOT NULL AUTO_INCREMENT,\n  `DELAY_TIME` bigint(20) NOT NULL,\n  `DELAY_NUMBER` bigint(20) NOT NULL,\n  `PIPELINE_ID` bigint(20) NOT NULL,\n  `GMT_CREATE` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',\n  `GMT_MODIFIED` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n  PRIMARY KEY (`ID`),\n  KEY `idx_PipelineID_GmtModified_ID` (`PIPELINE_ID`,`GMT_MODIFIED`,`ID`),\n  KEY `idx_Pipeline_GmtCreate` (`PIPELINE_ID`,`GMT_CREATE`),\n  KEY `idx_GmtCreate_id` (`GMT_CREATE`,`ID`)\n) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;\n\nCREATE TABLE `LOG_RECORD` (\n  `ID` bigint(20) NOT NULL AUTO_INCREMENT,\n  `NID` varchar(200) DEFAULT NULL,\n  `CHANNEL_ID` varchar(200) NOT NULL,\n  `PIPELINE_ID` varchar(200) NOT NULL,\n  `TITLE` varchar(1000) DEFAULT NULL,\n  `MESSAGE` text DEFAULT NULL,\n  `GMT_CREATE` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',\n  `GMT_MODIFIED` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n  PRIMARY KEY (`ID`),\n  KEY `logRecord_pipelineId` (`PIPELINE_ID`)\n) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;\n\nCREATE TABLE `NODE` (\n  `ID` bigint(20) NOT NULL AUTO_INCREMENT,\n  `NAME` varchar(200) NOT NULL,\n  `IP` varchar(200) NOT NULL,\n  `PORT` bigint(20) NOT NULL,\n  `DESCRIPTION` varchar(200) DEFAULT NULL,\n  `PARAMETERS` text DEFAULT NULL,\n  `GMT_CREATE` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',\n  `GMT_MODIFIED` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n  PRIMARY KEY (`ID`),\n  UNIQUE KEY `NODEUNIQUE` (`NAME`,`IP`)\n) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;\n\nCREATE TABLE `PIPELINE` (\n  `ID` bigint(20) NOT NULL AUTO_INCREMENT,\n  `NAME` varchar(200) NOT NULL,\n  `DESCRIPTION` varchar(200) DEFAULT NULL,\n  `PARAMETERS` text DEFAULT NULL,\n  `CHANNEL_ID` bigint(20) NOT NULL,\n  `GMT_CREATE` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',\n  `GMT_MODIFIED` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n  PRIMARY KEY (`ID`),\n  UNIQUE KEY `PIPELINEUNIQUE` (`NAME`,`CHANNEL_ID`),\n  KEY `idx_ChannelID` (`CHANNEL_ID`,`ID`)\n) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;\n\nCREATE TABLE `PIPELINE_NODE_RELATION` (\n  `ID` bigint(20) NOT NULL AUTO_INCREMENT,\n  `NODE_ID` bigint(20) NOT NULL,\n  `PIPELINE_ID` bigint(20) NOT NULL,\n  `LOCATION` varchar(20) NOT NULL,\n  `GMT_CREATE` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',\n  `GMT_MODIFIED` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n  PRIMARY KEY (`ID`),\n  KEY `idx_PipelineID` (`PIPELINE_ID`)\n) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;\n\nCREATE TABLE `SYSTEM_PARAMETER` (\n  `ID` bigint(20) unsigned NOT NULL,\n  `VALUE` text DEFAULT NULL,\n  `GMT_CREATE` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',\n  `GMT_MODIFIED` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n  PRIMARY KEY (`ID`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\nCREATE TABLE `TABLE_HISTORY_STAT` (\n  `ID` bigint(20) unsigned NOT NULL AUTO_INCREMENT,\n  `FILE_SIZE` bigint(20) DEFAULT NULL,\n  `FILE_COUNT` bigint(20) DEFAULT NULL,\n  `INSERT_COUNT` bigint(20) DEFAULT NULL,\n  `UPDATE_COUNT` bigint(20) DEFAULT NULL,\n  `DELETE_COUNT` bigint(20) DEFAULT NULL,\n  `DATA_MEDIA_PAIR_ID` bigint(20) DEFAULT NULL,\n  `PIPELINE_ID` bigint(20) DEFAULT NULL,\n  `START_TIME` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',\n  `END_TIME` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',\n  `GMT_CREATE` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',\n  `GMT_MODIFIED` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n  PRIMARY KEY (`ID`),\n  KEY `idx_DATA_MEDIA_PAIR_ID_END_TIME` (`DATA_MEDIA_PAIR_ID`,`END_TIME`),\n  KEY `idx_GmtCreate_id` (`GMT_CREATE`,`ID`)\n) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;\n\nCREATE TABLE `TABLE_STAT` (\n  `ID` bigint(20) NOT NULL AUTO_INCREMENT,\n  `FILE_SIZE` bigint(20) NOT NULL,\n  `FILE_COUNT` bigint(20) NOT NULL,\n  `INSERT_COUNT` bigint(20) NOT NULL,\n  `UPDATE_COUNT` bigint(20) NOT NULL,\n  `DELETE_COUNT` bigint(20) NOT NULL,\n  `DATA_MEDIA_PAIR_ID` bigint(20) NOT NULL,\n  `PIPELINE_ID` bigint(20) NOT NULL,\n  `GMT_CREATE` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',\n  `GMT_MODIFIED` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n  PRIMARY KEY (`ID`),\n  KEY `idx_PipelineID_DataMediaPairID` (`PIPELINE_ID`,`DATA_MEDIA_PAIR_ID`)\n) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;\n\nCREATE TABLE `THROUGHPUT_STAT` (\n  `ID` bigint(20) NOT NULL AUTO_INCREMENT,\n  `TYPE` varchar(20) NOT NULL,\n  `NUMBER` bigint(20) NOT NULL,\n  `SIZE` bigint(20) NOT NULL,\n  `PIPELINE_ID` bigint(20) NOT NULL,\n  `START_TIME` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',\n  `END_TIME` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',\n  `GMT_CREATE` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',\n  `GMT_MODIFIED` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n  PRIMARY KEY (`ID`),\n  KEY `idx_PipelineID_Type_GmtCreate_ID` (`PIPELINE_ID`,`TYPE`,`GMT_CREATE`,`ID`),\n  KEY `idx_PipelineID_Type_EndTime_ID` (`PIPELINE_ID`,`TYPE`,`END_TIME`,`ID`),\n  KEY `idx_GmtCreate_id` (`GMT_CREATE`,`ID`)\n) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;\n\nCREATE TABLE `USER` (\n  `ID` bigint(20) NOT NULL AUTO_INCREMENT,\n  `USERNAME` varchar(20) NOT NULL,\n  `PASSWORD` varchar(20) NOT NULL,\n  `AUTHORIZETYPE` varchar(20) NOT NULL,\n  `DEPARTMENT` varchar(20) NOT NULL,\n  `REALNAME` varchar(20) NOT NULL,\n  `GMT_CREATE` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',\n  `GMT_MODIFIED` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n  PRIMARY KEY (`ID`),\n  UNIQUE KEY `USERUNIQUE` (`USERNAME`)\n) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;\n\nCREATE TABLE  `DATA_MATRIX` (\n  `ID` bigint(20) NOT NULL AUTO_INCREMENT,\n  `GROUP_KEY` varchar(200) DEFAULT NULL,\n  `MASTER` varchar(200) DEFAULT NULL,\n  `SLAVE` varchar(200) DEFAULT NULL,\n  `DESCRIPTION` varchar(200) DEFAULT NULL,\n  `GMT_CREATE` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',\n  `GMT_MODIFIED` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n  PRIMARY KEY (`ID`),\n  KEY `GROUPKEY` (`GROUP_KEY`)\n) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;\n\nCREATE TABLE IF NOT EXISTS `meta_history` (\n  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',\n  `gmt_create` datetime NOT NULL COMMENT '创建时间',\n  `gmt_modified` datetime NOT NULL COMMENT '修改时间',\n  `destination` varchar(128) DEFAULT NULL COMMENT '通道名称',\n  `binlog_file` varchar(64) DEFAULT NULL COMMENT 'binlog文件名',\n  `binlog_offest` bigint(20) DEFAULT NULL COMMENT 'binlog偏移量',\n  `binlog_master_id` varchar(64) DEFAULT NULL COMMENT 'binlog节点id',\n  `binlog_timestamp` bigint(20) DEFAULT NULL COMMENT 'binlog应用的时间戳',\n  `use_schema` varchar(1024) DEFAULT NULL COMMENT '执行sql时对应的schema',\n  `sql_schema` varchar(1024) DEFAULT NULL COMMENT '对应的schema',\n  `sql_table` varchar(1024) DEFAULT NULL COMMENT '对应的table',\n  `sql_text` longtext DEFAULT NULL COMMENT '执行的sql',\n  `sql_type` varchar(256) DEFAULT NULL COMMENT 'sql类型',\n  `extra` text DEFAULT NULL COMMENT '额外的扩展信息',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY binlog_file_offest(`destination`,`binlog_master_id`,`binlog_file`,`binlog_offest`),\n  KEY `destination` (`destination`),\n  KEY `destination_timestamp` (`destination`,`binlog_timestamp`),\n  KEY `gmt_modified` (`gmt_modified`)\n) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='表结构变化明细表';\n\nCREATE TABLE IF NOT EXISTS `meta_snapshot` (\n  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',\n  `gmt_create` datetime NOT NULL COMMENT '创建时间',\n  `gmt_modified` datetime NOT NULL COMMENT '修改时间',\n  `destination` varchar(128) DEFAULT NULL COMMENT '通道名称',\n  `binlog_file` varchar(64) DEFAULT NULL COMMENT 'binlog文件名',\n  `binlog_offest` bigint(20) DEFAULT NULL COMMENT 'binlog偏移量',\n  `binlog_master_id` varchar(64) DEFAULT NULL COMMENT 'binlog节点id',\n  `binlog_timestamp` bigint(20) DEFAULT NULL COMMENT 'binlog应用的时间戳',\n  `data` longtext DEFAULT NULL COMMENT '表结构数据',\n  `extra` text DEFAULT NULL COMMENT '额外的扩展信息',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY binlog_file_offest(`destination`,`binlog_master_id`,`binlog_file`,`binlog_offest`),\n  KEY `destination` (`destination`),\n  KEY `destination_timestamp` (`destination`,`binlog_timestamp`),\n  KEY `gmt_modified` (`gmt_modified`)\n) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='表结构记录表快照表';\n\n\ninsert into USER(ID,USERNAME,PASSWORD,AUTHORIZETYPE,DEPARTMENT,REALNAME,GMT_CREATE,GMT_MODIFIED) values(null,'admin','801fc357a5a74743894a','ADMIN','admin','admin',now(),now());\ninsert into USER(ID,USERNAME,PASSWORD,AUTHORIZETYPE,DEPARTMENT,REALNAME,GMT_CREATE,GMT_MODIFIED) values(null,'guest','471e02a154a2121dc577','OPERATOR','guest','guest',now(),now());\n\nreplace into AUTOKEEPER_CLUSTER(ID,CLUSTER_NAME,SERVER_LIST,DESCRIPTION,GMT_CREATE,GMT_MODIFIED) values(1,'default','[\"#OTTER_MY_ZK#\"]',NULL,now(),now());\nreplace into NODE(ID,NAME,IP,PORT,DESCRIPTION,PARAMETERS,GMT_CREATE,GMT_MODIFIED) values(1,'default','#OTTER_NODE_HOST#',2088,NULL,'{\"downloadPort\":2089,\"mbeanPort\":2090,\"useExternalIp\":false,\"zkCluster\":{\"clusterName\":\"default\",\"id\":1}}', now(), now());\n"
  },
  {
    "path": "docker/image/admin/health.sh",
    "content": "#!/bin/sh\nCHECK_URL=\"http://127.0.0.1:8080/ok.htm\"\nCHECK_POINT=\"ok\"\nCHECK_COUNT=`curl -s --connect-timeout 7 --max-time 7 $CHECK_URL | grep -c $CHECK_POINT`\nif [ $CHECK_COUNT -eq 0 ]; then\n    echo \"[FAILED]\"\n    status=0\n\terror=1\nelse\n    echo \"[  OK  ]\"\n    status=1\n\terror=0\nfi"
  },
  {
    "path": "docker/image/alidata/bin/exec_rc_local.sh",
    "content": "#!/bin/bash\n\nif [ \"${SKIP_EXEC_RC_LOCAL}\" = \"YES\" ] ; then\n\techo \"skip /etc/rc.local: SKIP_EXEC_RC_LOCAL=${SKIP_EXEC_RC_LOCAL}\"\n\texit\nfi\n\nif [ \"${DOCKER_DEPLOY_TYPE}\" = \"HOST\" ] ; then\n\techo \"skip /etc/rc.local: DOCKER_DEPLOY_TYPE=${DOCKER_DEPLOY_TYPE}\"\n\texit\nfi"
  },
  {
    "path": "docker/image/alidata/bin/lark-wait",
    "content": "#!/bin/bash\nset -e\n\nchown admin: -R /home/admin/\nsource /alidata/lib/proc.sh\nwaitterm\n"
  },
  {
    "path": "docker/image/alidata/bin/main.sh",
    "content": "#!/bin/bash\n\n[ -n \"${DOCKER_DEPLOY_TYPE}\" ] || DOCKER_DEPLOY_TYPE=\"VM\"\necho \"DOCKER_DEPLOY_TYPE=${DOCKER_DEPLOY_TYPE}\"\n\n# run init scripts\nfor e in $(ls /alidata/init/*) ; do\n\t[ -x \"${e}\" ] || continue\n\techo \"==> INIT $e\"\n\t$e\n\techo \"==> EXIT CODE: $?\"\ndone\n\necho \"==> INIT DEFAULT\"\nservice sshd start\nservice crond start\n\n#echo \"check hostname -i: `hostname -i`\"\n#hti_num=`hostname -i|awk '{print NF}'`\n#if [ $hti_num -gt 1 ];then\n#    echo \"hostname -i result error:`hostname -i`\"\n#    exit 120\n#fi\n\necho \"==> INIT DONE\"\necho \"==> RUN ${*}\"\nexec \"${@}\""
  },
  {
    "path": "docker/image/alidata/init/02init-sshd.sh",
    "content": "#!/bin/bash\n\n# set port\nif [ -z \"${SSHD_PORT}\" ] ; then\n\tSSHD_PORT=22\n\t[ \"${DOCKER_DEPLOY_TYPE}\" = \"HOST\" ] && SSHD_PORT=2222\nfi\n\nsed -r -i '/^OPTIONS=/ d' /etc/sysconfig/sshd\necho 'OPTIONS=\"-p '\"${SSHD_PORT}\"'\"' >> /etc/sysconfig/sshd\n\n# set admin ssh pulic key\nif [ \"${USE_ADMIN_PASSAGE}\" = \"YES\" ] ; then\n    echo \"set admin passage\"\n    mkdir -p /home/admin/.ssh\n    chown admin:admin /home/admin/.ssh\n    chown admin:admin /home/admin/.ssh/authorized_keys\n    chmod 644 /home/admin/.ssh/authorized_keys\nfi\n"
  },
  {
    "path": "docker/image/alidata/init/fix-hosts.py",
    "content": "#!/usr/bin/python\n# -*- coding: utf-8 -*-\n#****************************************************************#\n# Create Date: 2017-01-06 17:58\n#***************************************************************#\n\nimport socket\nimport shutil\nfrom time import gmtime, strftime\n\n# get host_name\nhost_name = socket.gethostname()\ntmp_file = \"/tmp/.lark-fix-host.hosts\"\nhost_file = \"/etc/hosts\"\nbak_file_name = \"/tmp/hosts-fix-bak.%s\" % ( strftime(\"%Y-%m-%d_%H-%M-%S\", gmtime()) )\n\n# load /etc/hosts file context\nFH = open(host_file,\"r\")\nfile_lines = [ i.rstrip() for i in FH.readlines()]\nFH.close()\nfile_lines_reverse = file_lines[::-1]\nnew_lines = []\nbad_lines = []\nlast_match_line = \"\"\n\nfor line in file_lines_reverse:\n    if line.find(host_name) < 0:  # 不匹配的行直接跳过\n        new_lines.append(line + \"\\n\")\n        continue\n\n    cols = line.split()\n    new_cols = []\n    if cols[0].startswith(\"#\"): # 跳过已经注释掉的行\n        new_lines.append(line + \"\\n\")\n        continue\n    for col in cols:\n        if not col == host_name: # 跳过不匹配的列\n            new_cols.append(col)\n            continue\n\n        if cols[0] == \"127.0.0.1\": # 如果第一列是 127.0.0.1 就跳过匹配的列, 防止 hostname -i 返回 127.0.0.1\n            continue\n\n        # 如果已经发现过匹配的列, 就丢掉重复的列\n        if not len(last_match_line) == 0:\n            continue\n\n        new_cols.append(col)\n        last_match_line = line\n\n    # 跳过 xx.xx.xx.xx hostname 这样的重复列\n    if len(new_cols) == 1:\n        continue\n\n    new_l = \"%s\\n\" % \" \".join(new_cols)\n    new_lines.append(new_l)\n\n# save tmp hosts\n\nFH2=file(tmp_file,\"w+\")\nFH2.writelines( new_lines[::-1])\nFH2.close()\n\n# mv to /etc/hosts\nshutil.copy(host_file, bak_file_name)\nshutil.move(tmp_file, host_file)\n"
  },
  {
    "path": "docker/image/alidata/lib/proc.sh",
    "content": "# waitterm\n#   wait TERM/INT signal.\n#   see: http://veithen.github.io/2014/11/16/sigterm-propagation.html\nwaitterm() {\n\tlocal PID\n\t# any process to block\n\ttail -f /dev/null &\n\tPID=\"$!\"\n\t# setup trap, could do nothing, or just kill the blocker\n\ttrap \"kill -TERM ${PID}\" TERM INT\n\t# wait for signal, ignore wait exit code\n\twait \"${PID}\" || true\n\t# clear trap\n\ttrap - TERM INT\n\t# wait blocker, ignore blocker exit code\n\twait \"${PID}\" 2>/dev/null || true\n}\n\n# waittermpid \"${PIDFILE}\".\n#   monitor process by pidfile && wait TERM/INT signal.\n#   if the process disappeared, return 1, means exit with ERROR.\n#   if TERM or INT signal received, return 0, means OK to exit.\nwaittermpid() {\n\tlocal PIDFILE PID do_run error\n\tPIDFILE=\"${1?}\"\n\tdo_run=true\n\terror=0\n\ttrap \"do_run=false\" TERM INT\n\twhile \"${do_run}\" ; do\n\t\tPID=\"$(cat \"${PIDFILE}\")\"\n\t\tif ! ps -p \"${PID}\" >/dev/null 2>&1 ; then\n\t\t\tdo_run=false\n\t\t\terror=1\n\t\telse\n\t\t\tsleep 1\n\t\tfi\n\tdone\n\ttrap - TERM INT\n\treturn \"${error}\"\n}\n"
  },
  {
    "path": "docker/run.sh",
    "content": "#!/bin/bash\n\nfunction usage() {\n    echo \"Usage:\"\n    echo \"  run.sh [CONFIG]\"\n    echo \"example:\"\n    echo \"  run.sh NODE -e OTTER_MANAGER=127.0.0.1:8080\"\n    exit\n}\n\nfunction check_port() {\n    local port=$1\n    local TL=$(which telnet)\n    if [ -f $TL ]; then\n        data=`echo quit | telnet 127.0.0.1 $port| grep -ic connected`\n        echo $data\n        return\n    fi\n\n    local NC=$(which nc)\n    if [ -f $NC ]; then\n        data=`nc -z -w 1 127.0.0.1 $port | grep -ic succeeded`\n        echo $data\n        return\n    fi\n    echo \"0\"\n    return\n}\n\nfunction getMyIp() {\n    case \"`uname`\" in\n        Darwin)\n         myip=`echo \"show State:/Network/Global/IPv4\" | scutil | grep PrimaryInterface | awk '{print $3}' | xargs ifconfig | grep inet | grep -v inet6 | awk '{print $2}'`\n         ;;\n        *)\n         myip=`ip route get 1 | awk '{print $NF;exit}'`\n         ;;\n  esac\n  echo $myip\n}\n\nNET_MODE=\"\"\ncase \"`uname`\" in\n    Darwin)\n        bin_abs_path=`cd $(dirname $0); pwd`\n        ;;\n    Linux)\n        bin_abs_path=$(readlink -f $(dirname $0))\n        NET_MODE=\"--net=host\"\n        ;;\n    *)\n        NET_MODE=\"--net=host\"\n        bin_abs_path=`cd $(dirname $0); pwd`\n        ;;\nesac\nBASE=${bin_abs_path}\nif [ \"$1\" == \"-h\" ] ; then\n    usage\nelif [ \"$1\" == \"help\" ] ; then\n    usage\nfi\n\nDATA=\"$BASE/data\"\nmkdir -p $DATA\nRUN_MODE=$1\nCONFIG=${@:2}\nVOLUMNS=\"-v $DATA/mysql:/var/lib/mysql -v $DATA/zkData:/home/admin/zkData\"\nPORTLIST=\"8080 8081 2181 2088 2089 2090\"\n\nPORTS=\"\"\nfor PORT in $PORTLIST ; do\n    #exist=`check_port $PORT`\n    exist=\"0\"\n    if [ \"$exist\" == \"0\" ]; then\n        PORTS=\"$PORTS -p $PORT:$PORT\"\n    else\n        echo \"port $PORT is used , pls check\"\n        exit 1\n    fi\ndone\nMEMORY=\"-m 4096m\"\nLOCALHOST=`getMyIp`\ncmd=\"docker run -d -it -h $LOCALHOST $CONFIG --name=otter-all $VOLUMNS $NET_MODE $PORTS $MEMORY canal/otter-all\"\necho $cmd\neval $cmd\n"
  },
  {
    "path": "lib/install.bat",
    "content": "call mvn install:install-file -DgroupId=com.oracle -DartifactId=ojdbc6 -Dversion=11.1.0.7.0 -Dpackaging=jar -Dfile=ojdbc6.jar -DgeneratePom=true\ncall mvn install:install-file -DgroupId=org.jtester -DartifactId=jtester -Dversion=1.1.8 -Dpackaging=jar -DpomFile=jtester-1.1.8.pom -Dfile=jtester-1.1.8.jar -Dsources=jtester-1.1.8-sources.jar\ncall mvn install:install-file -DgroupId=mockit -DartifactId=jmockit -Dversion=0.999.10 -Dpackaging=jar -DpomFile=jmockit-0.999.10.pom -Dfile=jmockit-0.999.10.jar -Dsources=jmockit-0.999.10-sources.jar\n"
  },
  {
    "path": "lib/install.sh",
    "content": "#!/bin/bash\nmvn install:install-file -DgroupId=com.oracle -DartifactId=ojdbc6 -Dversion=11.1.0.7.0 -Dpackaging=jar -Dfile=ojdbc6.jar -DgeneratePom=true\nmvn install:install-file -DgroupId=org.jtester -DartifactId=jtester -Dversion=1.1.8 -Dpackaging=jar -DpomFile=jtester-1.1.8.pom -Dfile=jtester-1.1.8.jar -Dsources=jtester-1.1.8-sources.jar\nmvn install:install-file -DgroupId=mockit -DartifactId=jmockit -Dversion=0.999.10 -Dpackaging=jar -DpomFile=jmockit-0.999.10.pom -Dfile=jmockit-0.999.10.jar -Dsources=jmockit-0.999.10-sources.jar\n"
  },
  {
    "path": "lib/jmockit-0.999.10.pom",
    "content": "<project\r\n   xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\r\n   xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd\">\r\n   <modelVersion>4.0.0</modelVersion>\r\n   \r\n   <groupId>mockit</groupId>\r\n   <artifactId>jmockit</artifactId>\r\n   <version>0.999.10</version>\r\n   <name>Main</name>\r\n\r\n   <build>\r\n      <resources>\r\n         <resource>\r\n            <directory>src</directory>\r\n            <includes><include>jmockit.properties</include></includes>\r\n         </resource>\r\n      </resources>\r\n      <plugins>\r\n         <plugin>\r\n            <artifactId>maven-jar-plugin</artifactId><version>2.2</version>\r\n            <executions>\r\n               <execution>\r\n                  <phase>compile</phase>\r\n                  <goals><goal>jar</goal></goals>\r\n               </execution>\r\n            </executions>\r\n            <configuration>\r\n               <archive>\r\n                  <addMavenDescriptor>false</addMavenDescriptor>\r\n                  <forced/><manifestFile>META-INF/MANIFEST.MF</manifestFile>\r\n               </archive>\r\n               <excludes><exclude>Temp.class</exclude></excludes>\r\n            </configuration>\r\n         </plugin>\r\n         <plugin>\r\n            <artifactId>maven-source-plugin</artifactId><version>2.1.2</version>\r\n            <executions>\r\n               <execution>\r\n                  <id>attach-sources</id>\r\n                  <goals><goal>jar</goal></goals>\r\n               </execution>\r\n            </executions>\r\n         </plugin>\r\n         <plugin>\r\n            <artifactId>maven-surefire-plugin</artifactId><version>2.5</version>\r\n            <configuration>\r\n               <testNGArtifactName>none:none</testNGArtifactName>\r\n               <argLine>-javaagent:\"${project.build.directory}\"/jmockit-${project.version}.jar</argLine>\r\n               <excludes>\r\n                  <exclude>**/*$*</exclude>\r\n                  <exclude>**/Base*Test.class</exclude>\r\n                  <exclude>**/testng/*Test.class</exclude>\r\n                  <exclude>**/MockStateBetweenTestMethodsNGTest.class</exclude>\r\n               </excludes>\r\n            </configuration>\r\n         </plugin>\r\n      </plugins>\r\n   </build>\r\n\r\n   <dependencies>\r\n      <dependency>\r\n         <groupId>junit</groupId><artifactId>junit</artifactId><version>${junit.version}</version>\r\n         <optional>true</optional>\r\n      </dependency>\r\n      <dependency>\r\n         <groupId>org.testng</groupId><artifactId>testng</artifactId><version>6.0.1</version>\r\n         <optional>true</optional>\r\n         <exclusions>\r\n            <exclusion><groupId>org.beanshell</groupId><artifactId>bsh</artifactId></exclusion>\r\n            <exclusion><groupId>com.google.inject</groupId><artifactId>guice</artifactId></exclusion>\r\n            <exclusion><groupId>com.beust</groupId><artifactId>jcommander</artifactId></exclusion>\r\n            <exclusion><groupId>org.yaml</groupId><artifactId>snakeyaml</artifactId></exclusion>\r\n         </exclusions>\r\n      </dependency>\r\n      <dependency>\r\n         <groupId>commons-logging</groupId><artifactId>commons-logging</artifactId><version>1.1.1</version>\r\n         <optional>true</optional>\r\n      </dependency>\r\n      <dependency>\r\n         <groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId><version>1.5.8</version>\r\n         <optional>true</optional>\r\n      </dependency>\r\n      <dependency>\r\n         <groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.15</version>\r\n         <optional>true</optional>\r\n         <exclusions>\r\n            <exclusion><groupId>javax.jms</groupId><artifactId>jms</artifactId></exclusion>\r\n            <exclusion><groupId>javax.mail</groupId><artifactId>mail</artifactId></exclusion>\r\n            <exclusion><groupId>com.sun.jmx</groupId><artifactId>jmxri</artifactId></exclusion>\r\n            <exclusion><groupId>com.sun.jdmk</groupId><artifactId>jmxtools</artifactId></exclusion>\r\n         </exclusions>\r\n      </dependency>\r\n   </dependencies>\r\n</project>\r\n"
  },
  {
    "path": "lib/jtester-1.1.8.pom",
    "content": "<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\r\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd\">\r\n\t<modelVersion>4.0.0</modelVersion>\r\n\t<groupId>org.jtester</groupId>\r\n\t<artifactId>jtester</artifactId>\r\n\t<version>1.1.8</version>\r\n\t<packaging>jar</packaging>\r\n\t<name>jtester core</name>\r\n\r\n\t<dependencies>\r\n\t\t<dependency>\r\n\t\t\t<groupId>org.testng</groupId>\r\n\t\t\t<artifactId>testng</artifactId>\r\n\t\t\t<version>6.1.1</version>\r\n\t\t</dependency>\r\n\t\t<dependency>\r\n\t\t\t<groupId>mockit</groupId>\r\n\t\t\t<artifactId>jmockit</artifactId>\r\n\t\t\t<version>0.999.10</version>\r\n\t\t</dependency>\r\n\t\t<dependency>\r\n\t\t\t<groupId>commons-dbcp</groupId>\r\n\t\t\t<artifactId>commons-dbcp</artifactId>\r\n\t\t\t<version>1.2.2</version>\r\n\t\t</dependency>\r\n\t\t<dependency>\r\n\t\t\t<groupId>cglib</groupId>\r\n\t\t\t<artifactId>cglib-nodep</artifactId>\r\n\t\t\t<version>2.1_3</version>\r\n\t\t</dependency>\r\n\t\t<dependency>\r\n\t\t\t<groupId>org.fitnesse</groupId>\r\n\t\t\t<artifactId>fitnesse</artifactId>\r\n\t\t\t<version>20111025</version>\r\n\t\t</dependency>\r\n\t\t<dependency>\r\n\t\t\t<groupId>ognl</groupId>\r\n\t\t\t<artifactId>ognl</artifactId>\r\n\t\t\t<version>2.7.2</version>\r\n\t\t</dependency>\r\n\r\n\t\t<dependency>\r\n\t\t\t<groupId>org.springframework</groupId>\r\n\t\t\t<artifactId>spring</artifactId>\r\n\t\t\t<version>2.5.4</version>\r\n\t\t\t<scope>provided</scope>\r\n\t\t</dependency>\r\n\t\t<dependency>\r\n\t\t\t<groupId>org.springframework</groupId>\r\n\t\t\t<artifactId>spring-aspects</artifactId>\r\n\t\t\t<version>2.5.4</version>\r\n\t\t\t<scope>provided</scope>\r\n\t\t</dependency>\r\n\t\t<dependency>\r\n\t\t\t<groupId>javax.transaction</groupId>\r\n\t\t\t<artifactId>jta</artifactId>\r\n\t\t\t<version>1.1</version>\r\n\t\t\t<scope>provided</scope>\r\n\t\t</dependency>\r\n\t\t<dependency>\r\n\t\t\t<groupId>org.apache.ibatis</groupId>\r\n\t\t\t<artifactId>ibatis-sqlmap</artifactId>\r\n\t\t\t<version>2.3.4.726</version>\r\n\t\t\t<scope>provided</scope>\r\n\t\t</dependency>\r\n\t\t<dependency>\r\n\t\t\t<groupId>org.springframework</groupId>\r\n\t\t\t<artifactId>spring-aop</artifactId>\r\n\t\t\t<version>2.5.4</version>\r\n\t\t\t<scope>provided</scope>\r\n\t\t</dependency>\r\n\t\t<dependency>\r\n\t\t\t<groupId>javax.annotation</groupId>\r\n\t\t\t<artifactId>jsr250-api</artifactId>\r\n\t\t\t<version>1.0</version>\r\n\t\t\t<scope>provided</scope>\r\n\t\t</dependency>\r\n\t\t<dependency>\r\n\t\t\t<groupId>mysql</groupId>\r\n\t\t\t<artifactId>mysql-connector-java</artifactId>\r\n\t\t\t<version>5.1.6</version>\r\n\t\t\t<scope>provided</scope>\r\n\t\t</dependency>\r\n\t\t<dependency>\r\n\t\t\t<groupId>com.h2database</groupId>\r\n\t\t\t<artifactId>h2</artifactId>\r\n\t\t\t<version>1.1.107</version>\r\n\t\t\t<scope>provided</scope>\r\n\t\t</dependency>\r\n\t\t<dependency>\r\n\t\t\t<groupId>com.oracle</groupId>\r\n\t\t\t<artifactId>ojdbc6</artifactId>\r\n\t\t\t<version>11.1.0.7.0</version>\r\n\t\t\t<scope>provided</scope>\r\n\t\t</dependency>\r\n\t\t<dependency>\r\n\t\t\t<groupId>javax.servlet</groupId>\r\n\t\t\t<artifactId>servlet-api</artifactId>\r\n\t\t\t<version>2.4</version>\r\n\t\t\t<scope>provided</scope>\r\n\t\t</dependency>\r\n\t</dependencies>\r\n\r\n\t<build>\r\n\t\t<sourceDirectory>src/main/java</sourceDirectory>\r\n\t\t<outputDirectory>target/classes</outputDirectory>\r\n\t\t<resources>\r\n\t\t\t<resource>\r\n\t\t\t\t<directory>src/main/resources</directory>\r\n\t\t\t</resource>\r\n\t\t</resources>\r\n\r\n\t\t<plugins>\r\n\t\t\t<plugin>\r\n\t\t\t\t<artifactId>maven-compiler-plugin</artifactId>\r\n\t\t\t\t<version>2.0.2</version>\r\n\t\t\t\t<configuration>\r\n\t\t\t\t\t<source>1.5</source>\r\n\t\t\t\t\t<target>1.5</target>\r\n\t\t\t\t\t<encoding>utf-8</encoding>\r\n\t\t\t\t</configuration>\r\n\t\t\t</plugin>\r\n\t\t\t<plugin>\r\n\t\t\t\t<groupId>org.apache.maven.plugins</groupId>\r\n\t\t\t\t<artifactId>maven-jar-plugin</artifactId>\r\n\t\t\t\t<version>2.2</version>\r\n\t\t\t\t<!-- <configuration> -->\r\n\t\t\t\t<!-- <archive> -->\r\n\t\t\t\t<!-- <manifestFile>src/main/resources/META-INF/MANIFEST.MF</manifestFile> -->\r\n\t\t\t\t<!-- </archive> -->\r\n\t\t\t\t<!-- </configuration> -->\r\n\t\t\t</plugin>\r\n\t\t\t<plugin>\r\n\t\t\t\t<artifactId>maven-source-plugin</artifactId>\r\n\t\t\t\t<executions>\r\n\t\t\t\t\t<execution>\r\n\t\t\t\t\t\t<id>attach-sources</id>\r\n\t\t\t\t\t\t<goals>\r\n\t\t\t\t\t\t\t<goal>jar</goal>\r\n\t\t\t\t\t\t</goals>\r\n\t\t\t\t\t</execution>\r\n\t\t\t\t</executions>\r\n\t\t\t</plugin>\r\n\t\t</plugins>\r\n\t</build>\r\n</project>\r\n"
  },
  {
    "path": "manager/biz/pom.xml",
    "content": "<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\">\n\t<modelVersion>4.0.0</modelVersion>\n\t<parent>\n\t\t<groupId>com.alibaba.otter</groupId>\n\t\t<artifactId>manager</artifactId>\n\t\t<version>4.2.19-SNAPSHOT</version>\n\t\t<relativePath>../pom.xml</relativePath>\n\t</parent>\n\t<groupId>com.alibaba.otter</groupId>\n\t<artifactId>manager.biz</artifactId>\n\t<packaging>jar</packaging>\n\t<name>manager biz module for otter</name>\n\t<url>http://github.com/alibaba/otter</url>\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>com.alibaba.otter</groupId>\n\t\t\t<artifactId>shared.common</artifactId>\n\t\t\t<version>${project.version}</version>\n\t\t</dependency>\n\n\t\t<dependency>\n\t\t\t<groupId>com.alibaba.otter</groupId>\n\t\t\t<artifactId>shared.push</artifactId>\n\t\t\t<version>${project.version}</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>com.alibaba.otter</groupId>\n\t\t\t<artifactId>canal.instance.manager</artifactId>\n\t\t\t<version>${otter_canal_version}</version>\n\t\t\t<exclusions>\n\t\t\t\t<exclusion>\n\t\t\t\t\t<groupId>org.springframework</groupId>\n\t\t\t\t\t<artifactId>spring</artifactId>\n\t\t\t\t</exclusion>\n\t\t\t</exclusions>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>cglib</groupId>\n\t\t\t<artifactId>cglib-nodep</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.apache.ibatis</groupId>\n\t\t\t<artifactId>ibatis-sqlmap</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>commons-dbcp</groupId>\n\t\t\t<artifactId>commons-dbcp</artifactId>\n\t\t</dependency>\n\n\t\t<dependency>\n\t\t\t<groupId>mysql</groupId>\n\t\t\t<artifactId>mysql-connector-java</artifactId>\n\t\t</dependency>\n\t\t<!-- oracle -->\n\t\t<dependency>\n\t\t\t<groupId>com.oracle</groupId>\n\t\t\t<artifactId>ojdbc6</artifactId>\n\t\t</dependency>\n\n\t\t<!-- spring -->\n\t\t<dependency>\n\t\t\t<groupId>org.springframework</groupId>\n\t\t\t<artifactId>spring-core</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework</groupId>\n\t\t\t<artifactId>spring-beans</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework</groupId>\n\t\t\t<artifactId>spring-aop</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework</groupId>\n\t\t\t<artifactId>spring-context</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework</groupId>\n\t\t\t<artifactId>spring-context-support</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework</groupId>\n\t\t\t<artifactId>spring-tx</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework</groupId>\n\t\t\t<artifactId>spring-jdbc</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework</groupId>\n\t\t\t<artifactId>spring-orm</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework</groupId>\n\t\t\t<artifactId>spring-web</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework</groupId>\n\t\t\t<artifactId>spring-webmvc</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework</groupId>\n\t\t\t<artifactId>spring-test</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>javax.mail</groupId>\n\t\t\t<artifactId>mail</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>com.alibaba</groupId>\n\t\t\t<artifactId>fastjson</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>com.google.guava</groupId>\n\t\t\t<artifactId>guava</artifactId>\n\t\t</dependency>\n\n\t\t<!-- test dependency -->\n\t\t<dependency>\n\t\t\t<groupId>org.jtester</groupId>\n\t\t\t<artifactId>jtester</artifactId>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>junit</groupId>\n\t\t\t<artifactId>junit</artifactId>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\t</dependencies>\n</project>\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/autokeeper/AutoKeeperStatService.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.autokeeper;\n\nimport java.util.List;\n\nimport com.alibaba.otter.shared.common.model.autokeeper.AutoKeeperConnectionStat;\nimport com.alibaba.otter.shared.common.model.autokeeper.AutoKeeperServerStat;\n\n/**\n * zookeeper状态查询接口\n * \n * @author jianghang 2012-9-21 下午02:42:16\n * @version 4.1.0\n */\npublic interface AutoKeeperStatService {\n\n    /**\n     * 根据serverIp查询对应的统计信息，包括Connection/Watch/Ephemeral等统计信息\n     * \n     * @param serverIp\n     * @return\n     */\n    public AutoKeeperServerStat findServerStat(String serverIp);\n\n    /**\n     * 根据sessionId查询对应的统计信息，包括详细的Connection/Watch/Ephemeral等统计信息\n     * \n     * @param sessionId\n     * @return\n     */\n    public AutoKeeperServerStat findServerStatBySessionId(String sessionId);\n\n    /**\n     * 根据sessionId查询对应的connction链接\n     * \n     * @param sessionId\n     * @return\n     */\n    public AutoKeeperConnectionStat findConnectionBySessionId(String sessionId);\n\n    /**\n     * 根据临时节点路径查询对应的connection统计信息\n     * \n     * @param path\n     * @return\n     */\n    public AutoKeeperConnectionStat findConnectionByEphemeralPath(String path);\n\n    /**\n     * 根据watcher路径查询对应的connection统计信息\n     * \n     * @param path\n     * @return\n     */\n    public List<AutoKeeperConnectionStat> findConnectionByWatcherPath(String path);\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/autokeeper/impl/AutoKeeperCollector.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.autokeeper.impl;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.ScheduledExecutorService;\nimport java.util.concurrent.TimeUnit;\n\nimport javax.annotation.Resource;\n\nimport org.apache.commons.lang.StringUtils;\nimport org.springframework.beans.factory.InitializingBean;\n\nimport com.alibaba.otter.manager.biz.common.exceptions.ManagerException;\nimport com.alibaba.otter.manager.biz.config.autokeeper.AutoKeeperClusterService;\nimport com.alibaba.otter.manager.biz.utils.RegexUtils;\nimport com.alibaba.otter.shared.common.model.autokeeper.AutoKeeperCluster;\nimport com.alibaba.otter.shared.common.model.autokeeper.AutoKeeperConnectionStat;\nimport com.alibaba.otter.shared.common.model.autokeeper.AutoKeeperEphemeralStat;\nimport com.alibaba.otter.shared.common.model.autokeeper.AutoKeeperQuorumType;\nimport com.alibaba.otter.shared.common.model.autokeeper.AutoKeeperServerStat;\nimport com.alibaba.otter.shared.common.model.autokeeper.AutoKeeperWatchStat;\nimport com.alibaba.otter.shared.common.utils.cmd.Exec;\nimport com.alibaba.otter.shared.common.utils.cmd.Exec.Result;\nimport com.alibaba.otter.shared.common.utils.thread.NamedThreadFactory;\n\n/**\n * 对应的数据采集器\n * \n * @author jianghang 2012-9-21 下午03:05:28\n * @version 4.1.0\n */\npublic class AutoKeeperCollector implements InitializingBean {\n\n    @Resource(name = \"autoKeeperClusterService\")\n    private AutoKeeperClusterService autoKeeperClusterService;\n\n    private static final String      MODE_FOLLOWER            = \"Mode: follower\";\n    private static final String      MODE_LEADERER            = \"Mode: leader\";\n    private static final String      MODE_OBSERVER            = \"Mode: observer\";\n    private static final String      MODE_STANDALONE          = \"Mode: standalone\";\n    private static final String      NODE_COUNT               = \"Node count:\";\n    private static final String      STRING_LATENCY           = \"Latency min/avg/max:\";\n    private static final String      STRING_SENT              = \"Sent:\";\n    private static final String      STRING_RECEIVED          = \"Received:\";\n    private static final String      STRING_OUTSTANDING       = \"Outstanding:\";\n    private static final String      COMMA                    = \",\";\n    private static final String      BRACKETS                 = \")\";\n    private static final String      COLON                    = \":\";\n    private static final String      WRAP                     = \"\\n\";\n    private static final String      CMD_STAT                 = \"echo stat | nc %s %s\";\n    private static final String      CMD_CONS                 = \"echo cons | nc %s %s\";\n    private static final String      CMD_DUMP                 = \"echo dump | nc %s %s\";\n    private static final String      CMD_WCHC                 = \"echo wchc | nc %s %s\";\n    private static final long        DEFAULT_COLLECT_INTERVAL = 300;\n    private long                     delay                    = 1;\n    private int                      singleSize               = 1;\n    private long                     collectInterval          = DEFAULT_COLLECT_INTERVAL;\n\n    private AutoKeeperData           autoKeeperData;\n    private ScheduledExecutorService collectorExecutor;\n\n    public void collectorConnectionStat(String address) {\n        List<String> netAddress = splitAddress(address);\n        if (netAddress.isEmpty()) {\n            return;\n        }\n        String ip = netAddress.get(0);\n        String port = netAddress.get(1);\n        String[] cmd = { \"/bin/bash\", \"-c\", String.format(CMD_CONS, ip, port) };\n        String cmdresult = collector(cmd);\n        String[] result = cmdresult.split(WRAP);\n        List<AutoKeeperConnectionStat> summary = new ArrayList<AutoKeeperConnectionStat>();\n\n        for (String line : result) {\n\n            if (StringUtils.isBlank(line)) {\n                continue;\n            }\n\n            String[] lineArray = line.split(\":\");\n            if (2 != lineArray.length) {\n                continue;\n            }\n\n            AutoKeeperConnectionStat autoKeeperConnectionStat = new AutoKeeperConnectionStat();\n            autoKeeperConnectionStat.setOriginalContent(line);\n            String clientIp = StringUtils.trimToEmpty(line.split(\":\")[0].replace(\"/\", \"\"));\n            String sessionId = StringUtils.trimToEmpty(RegexUtils.findFirst(line.split(\":\")[1], \"sid=(?s).*?[,)]\")).replace(\"sid=\",\n                                                                                                                            StringUtils.EMPTY).replace(COMMA,\n                                                                                                                                                       StringUtils.EMPTY).replace(BRACKETS,\n                                                                                                                                                                                  StringUtils.EMPTY);\n            String queued = StringUtils.trimToEmpty(RegexUtils.findFirst(line.split(\":\")[1], \"queued=(?s).*?[,)]\")).replace(\"queued=\",\n                                                                                                                            StringUtils.EMPTY).replace(COMMA,\n                                                                                                                                                       StringUtils.EMPTY).replace(BRACKETS,\n                                                                                                                                                                                  StringUtils.EMPTY);\n            String receive = StringUtils.trimToEmpty(RegexUtils.findFirst(line.split(\":\")[1], \"recved=(?s).*?[,)]\")).replace(\"recved=\",\n                                                                                                                             StringUtils.EMPTY).replace(COMMA,\n                                                                                                                                                        StringUtils.EMPTY).replace(BRACKETS,\n                                                                                                                                                                                   StringUtils.EMPTY);\n            String sent = StringUtils.trimToEmpty(RegexUtils.findFirst(line.split(\":\")[1], \"sent=(?s).*?[,)]\")).replace(\"sent=\",\n                                                                                                                        StringUtils.EMPTY).replace(COMMA,\n                                                                                                                                                   StringUtils.EMPTY).replace(BRACKETS,\n                                                                                                                                                                              StringUtils.EMPTY);\n            String minlat = StringUtils.trimToEmpty(RegexUtils.findFirst(line.split(\":\")[1], \"minlat=(?s).*?[,)]\")).replace(\"minlat=\",\n                                                                                                                            StringUtils.EMPTY).replace(COMMA,\n                                                                                                                                                       StringUtils.EMPTY).replace(BRACKETS,\n                                                                                                                                                                                  StringUtils.EMPTY);\n            String avglat = StringUtils.trimToEmpty(RegexUtils.findFirst(line.split(\":\")[1], \"avglat=(?s).*?[,)]\")).replace(\"avglat=\",\n                                                                                                                            StringUtils.EMPTY).replace(COMMA,\n                                                                                                                                                       StringUtils.EMPTY).replace(BRACKETS,\n                                                                                                                                                                                  StringUtils.EMPTY);\n            String maxlat = StringUtils.trimToEmpty(RegexUtils.findFirst(line.split(\":\")[1], \"maxlat=(?s).*?[,)]\")).replace(\"maxlat=\",\n                                                                                                                            StringUtils.EMPTY).replace(COMMA,\n                                                                                                                                                       StringUtils.EMPTY).replace(BRACKETS,\n                                                                                                                                                                                  StringUtils.EMPTY);\n            autoKeeperConnectionStat.setServerAddress(ip);\n            autoKeeperConnectionStat.setClientAddress(clientIp);\n            autoKeeperConnectionStat.setSessionId(sessionId);\n            if (StringUtils.isNotEmpty(queued)) {\n                autoKeeperConnectionStat.setQueued(Long.parseLong(queued));\n            }\n            if (StringUtils.isNotEmpty(receive)) {\n                autoKeeperConnectionStat.setRecved(Long.parseLong(receive));\n            }\n            if (StringUtils.isNotEmpty(sent)) {\n                autoKeeperConnectionStat.setSent(Long.parseLong(sent));\n            }\n            if (StringUtils.isNotEmpty(minlat)) {\n                autoKeeperConnectionStat.setMinLatency(Long.parseLong(minlat));\n            }\n            if (StringUtils.isNotEmpty(avglat)) {\n                autoKeeperConnectionStat.setAvgLatency(Long.parseLong(avglat));\n            }\n            if (StringUtils.isNotEmpty(maxlat)) {\n                autoKeeperConnectionStat.setMaxLatency(Long.parseLong(maxlat));\n            }\n\n            summary.add(autoKeeperConnectionStat);\n        }\n        autoKeeperData.joinConnection(address, summary);\n    }\n\n    public void collectorServerStat(String address) {\n        List<String> netAddress = splitAddress(address);\n        if (netAddress.isEmpty()) {\n            return;\n        }\n        String ip = netAddress.get(0);\n        String port = netAddress.get(1);\n        String[] cmd = { \"/bin/bash\", \"-c\", String.format(CMD_STAT, ip, port) };\n        String cmdresult = collector(cmd);\n        String[] result = cmdresult.split(WRAP);\n        AutoKeeperServerStat summary = new AutoKeeperServerStat();\n        summary.setOriginalContent(cmdresult);\n        for (String line : result) {\n\n            if (line.contains(MODE_FOLLOWER)) {\n                summary.setQuorumType(AutoKeeperQuorumType.FOLLOWER);\n            } else if (line.contains(MODE_LEADERER)) {\n                summary.setQuorumType(AutoKeeperQuorumType.LEADER);\n            } else if (line.contains(MODE_STANDALONE)) {\n                summary.setQuorumType(AutoKeeperQuorumType.STANDALONE);\n            } else if (line.contains(MODE_OBSERVER)) {\n                summary.setQuorumType(AutoKeeperQuorumType.OBSERVER);\n            } else if (line.contains(STRING_LATENCY)) {\n                List<String> latency = Arrays.asList(StringUtils.trimToEmpty(line.replace(STRING_LATENCY,\n                                                                                          StringUtils.EMPTY)).split(\"/\"));\n                summary.setMinLatency(Long.parseLong(latency.get(0)));\n                summary.setAvgLatency(Long.parseLong(latency.get(1)));\n                summary.setMaxLatency(Long.parseLong(latency.get(2)));\n            } else if (line.contains(STRING_OUTSTANDING)) {\n                summary.setQueued(Long.parseLong(StringUtils.trimToEmpty(line.replace(STRING_OUTSTANDING,\n                                                                                      StringUtils.EMPTY))));\n            } else if (line.contains(NODE_COUNT)) {\n                summary.setNodeCount(Long.parseLong(StringUtils.trimToEmpty(line.replace(NODE_COUNT, StringUtils.EMPTY))));\n            } else if (line.contains(STRING_SENT)) {\n                summary.setSent(Long.parseLong(StringUtils.trimToEmpty(line.replace(STRING_SENT, StringUtils.EMPTY))));\n            } else if (line.contains(STRING_RECEIVED)) {\n                summary.setRecved(Long.parseLong(StringUtils.trimToEmpty(line.replace(STRING_RECEIVED,\n                                                                                      StringUtils.EMPTY))));\n            }\n        }\n\n        autoKeeperData.joinServer(address, summary);\n    }\n\n    public void collectorEphemeralStat(String address) {\n        List<String> netAddress = splitAddress(address);\n        if (netAddress.isEmpty()) {\n            return;\n        }\n        String ip = netAddress.get(0);\n        String port = netAddress.get(1);\n        String[] cmd = { \"/bin/bash\", \"-c\", String.format(CMD_DUMP, ip, port) };\n        String cmdresult = collector(cmd);\n\n        Map<String, List<String>> pathMap = groupSessionPath(cmdresult);\n\n        List<AutoKeeperEphemeralStat> autoKeeperEphemeralStats = new ArrayList<AutoKeeperEphemeralStat>();\n        for (Map.Entry<String, List<String>> entry : pathMap.entrySet()) {\n            AutoKeeperEphemeralStat autoKeeperEphemeralStat = new AutoKeeperEphemeralStat();\n            autoKeeperEphemeralStat.setSessionId(entry.getKey());\n            autoKeeperEphemeralStat.setPaths(entry.getValue());\n            autoKeeperEphemeralStats.add(autoKeeperEphemeralStat);\n        }\n\n        autoKeeperData.joinEphemeral(address, autoKeeperEphemeralStats);\n\n    }\n\n    public void collectorWatchStat(String address) {\n        List<String> netAddress = splitAddress(address);\n        if (netAddress.isEmpty()) {\n            return;\n        }\n        String ip = netAddress.get(0);\n        String port = netAddress.get(1);\n        String[] cmd = { \"/bin/bash\", \"-c\", String.format(CMD_WCHC, ip, port) };\n        String cmdresult = collector(cmd);\n\n        Map<String, List<String>> pathMap = groupSessionPath(cmdresult);\n\n        List<AutoKeeperWatchStat> autoKeeperWatchStats = new ArrayList<AutoKeeperWatchStat>();\n        for (Map.Entry<String, List<String>> entry : pathMap.entrySet()) {\n            AutoKeeperWatchStat autoKeeperWatchStat = new AutoKeeperWatchStat();\n            autoKeeperWatchStat.setSessionId(entry.getKey());\n            autoKeeperWatchStat.setPaths(entry.getValue());\n            autoKeeperWatchStats.add(autoKeeperWatchStat);\n        }\n\n        autoKeeperData.joinWatch(address, autoKeeperWatchStats);\n\n    }\n\n    public static String collector(String[] command) {\n        Result result = null;\n        try {\n            result = Exec.execute(command);\n            if (result.getExitCode() == 0) {\n                return result.getStdout();\n            } else {\n                return result.getStderr();\n            }\n        } catch (Exception e) {\n            throw new ManagerException(e);\n        }\n    }\n\n    private List<String> splitAddress(String address) {\n        List<String> ipPort = Arrays.asList(address.split(\":\"));\n        if (ipPort.size() != 2) {\n            return new ArrayList<String>();\n        }\n        return ipPort;\n    }\n\n    /**\n     * <pre>\n     * key=sessionId\n     * value=pathList\n     * </pre>\n     */\n    private Map<String, List<String>> groupSessionPath(String cmdresult) {\n        String[] result = cmdresult.split(WRAP);\n\n        Map<String, List<String>> pathMap = new HashMap<String, List<String>>();\n        String sessionId = StringUtils.EMPTY;\n        for (String line : result) {\n            line = StringUtils.trimToEmpty(line);\n            if (StringUtils.isBlank(line)) {\n                continue;\n            }\n            if (line.startsWith(\"0x\")) {\n                sessionId = line.replace(COLON, StringUtils.EMPTY);\n                pathMap.put(sessionId, new ArrayList<String>());\n            } else if (line.startsWith(\"/\")) {\n                List<String> paths = pathMap.get(sessionId);\n                paths.add(line);\n            }\n        }\n        return pathMap;\n    }\n\n    @Override\n    public void afterPropertiesSet() throws Exception {\n        collectorExecutor = Executors.newScheduledThreadPool(singleSize, new NamedThreadFactory(\"collector-thread\",\n                                                                                                true));\n        startCollect();\n    }\n\n    private void startCollect() {\n\n        // 启动定时工作任务\n        collectorExecutor.scheduleAtFixedRate(new Runnable() {\n\n            @Override\n            public void run() {\n                List<AutoKeeperCluster> autoKeeperClusters = autoKeeperClusterService.listAutoKeeperClusters();\n                if (!autoKeeperClusters.isEmpty()) {\n                    autoKeeperData.persist();\n                    for (AutoKeeperCluster autoKeeperCluster : autoKeeperClusters) {\n                        List<String> servers = autoKeeperCluster.getServerList();\n                        for (String address : servers) {\n                            collectorServerStat(address);\n                            collectorConnectionStat(address);\n                            collectorWatchStat(address);\n                            collectorEphemeralStat(address);\n                        }\n                    }\n                }\n            }\n        }, delay, collectInterval, TimeUnit.SECONDS);\n\n    }\n\n    public void setAutoKeeperClusterService(AutoKeeperClusterService autoKeeperClusterService) {\n        this.autoKeeperClusterService = autoKeeperClusterService;\n    }\n\n    public void setAutoKeeperData(AutoKeeperData autoKeeperData) {\n        this.autoKeeperData = autoKeeperData;\n    }\n\n    public void setCollectInterval(long collectInterval) {\n        this.collectInterval = collectInterval;\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/autokeeper/impl/AutoKeeperData.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.autokeeper.impl;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport com.alibaba.otter.shared.common.model.autokeeper.AutoKeeperConnectionStat;\nimport com.alibaba.otter.shared.common.model.autokeeper.AutoKeeperEphemeralStat;\nimport com.alibaba.otter.shared.common.model.autokeeper.AutoKeeperServerStat;\nimport com.alibaba.otter.shared.common.model.autokeeper.AutoKeeperWatchStat;\nimport com.google.common.collect.MapMaker;\n\n/**\n * join server之间的统计数据，提供当前最新和完整的数据结构方便数据查询\n * \n * <pre>\n * server -> \n *        connection -> \n *               ->  watcher\n *               ->  ephemeral\n * </pre>\n * \n * @author jianghang 2012-9-21 下午03:02:00\n * @version 4.1.0\n */\npublic class AutoKeeperData implements AutoKeeperPersist {\n\n    private Map<String, AutoKeeperServerStat>     serverStats     = new MapMaker().makeMap(); // serverIp和server的对应关系\n    private Map<String, AutoKeeperConnectionStat> connectionStats = new MapMaker().makeMap(); // sessionId和connection的对应关系\n\n    public void joinServer(String address, AutoKeeperServerStat summary) {\n        serverStats.put(address, summary);\n    }\n\n    public void joinConnection(String address, List<AutoKeeperConnectionStat> connections) {\n        // 记录connection引用\n        for (AutoKeeperConnectionStat connection : connections) {\n            connectionStats.put(connection.getSessionId(), connection);\n        }\n\n        // 添加引用到server中\n        if (serverStats.containsKey(address)) {\n            serverStats.get(address).getConnectionStats().addAll(connections);\n        }\n    }\n\n    public void joinEphemeral(String address, List<AutoKeeperEphemeralStat> ephemerals) {\n        for (AutoKeeperEphemeralStat ephemeral : ephemerals) {\n            if (connectionStats.containsKey(ephemeral.getSessionId())) {\n                // 找到对应的connection进行关联，填充数据\n                connectionStats.get(ephemeral.getSessionId()).getEphemeralStats().add(ephemeral);\n            }\n        }\n    }\n\n    public void joinWatch(String address, List<AutoKeeperWatchStat> watches) {\n        for (AutoKeeperWatchStat watch : watches) {\n            if (connectionStats.containsKey(watch.getSessionId())) {\n                // 找到对应的connection进行关联，填充数据\n                connectionStats.get(watch.getSessionId()).getWatchStats().add(watch);\n            }\n        }\n    }\n\n    public void persist() {\n        serverStats.clear();\n        connectionStats.clear();\n    }\n\n    public Map<String, AutoKeeperServerStat> getServerStats() {\n        return serverStats;\n    }\n\n    public Map<String, AutoKeeperConnectionStat> getConnectionStats() {\n        return connectionStats;\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/autokeeper/impl/AutoKeeperPersist.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.autokeeper.impl;\n\n/**\n * 数据持久化接口，会有持久化调度器定时触发\n * \n * @author jianghang 2012-9-21 下午03:04:37\n * @version 4.1.0\n */\npublic interface AutoKeeperPersist {\n\n    /**\n     * 会有\n     */\n    public void persist();\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/autokeeper/impl/AutoKeeperStatServiceImpl.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.autokeeper.impl;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport com.alibaba.otter.manager.biz.autokeeper.AutoKeeperStatService;\nimport com.alibaba.otter.shared.common.model.autokeeper.AutoKeeperConnectionStat;\nimport com.alibaba.otter.shared.common.model.autokeeper.AutoKeeperEphemeralStat;\nimport com.alibaba.otter.shared.common.model.autokeeper.AutoKeeperServerStat;\nimport com.alibaba.otter.shared.common.model.autokeeper.AutoKeeperWatchStat;\n\n/**\n * 提供autokeeper相关的数据查询接口\n * \n * @author simon 2012-9-28 上午11:00:07\n * @version 4.1.0\n */\npublic class AutoKeeperStatServiceImpl implements AutoKeeperStatService {\n\n    private AutoKeeperData autoKeeperData;\n\n    public AutoKeeperServerStat findServerStat(String serverAddress) {\n        return autoKeeperData.getServerStats().get(serverAddress);\n    }\n\n    public AutoKeeperServerStat findServerStatBySessionId(String sessionId) {\n        String serverAddress = autoKeeperData.getConnectionStats().get(sessionId).getServerAddress();\n        return findServerStat(serverAddress);\n    }\n\n    public AutoKeeperConnectionStat findConnectionBySessionId(String sessionId) {\n        return autoKeeperData.getConnectionStats().get(sessionId);\n    }\n\n    public AutoKeeperConnectionStat findConnectionByEphemeralPath(String path) {\n        for (AutoKeeperConnectionStat connection : autoKeeperData.getConnectionStats().values()) {\n            for (AutoKeeperEphemeralStat ephemeral : connection.getEphemeralStats()) {\n                if (ephemeral.getPaths().contains(path)) {\n                    return connection;\n                }\n            }\n        }\n\n        return null;\n    }\n\n    public List<AutoKeeperConnectionStat> findConnectionByWatcherPath(String path) {\n        List<AutoKeeperConnectionStat> connections = new ArrayList<AutoKeeperConnectionStat>();\n        for (AutoKeeperConnectionStat connection : autoKeeperData.getConnectionStats().values()) {\n            for (AutoKeeperWatchStat watch : connection.getWatchStats()) {\n                if (watch.getPaths().contains(path)) {\n                    connections.add(connection);\n                }\n            }\n        }\n\n        return connections;\n    }\n\n    public void setAutoKeeperData(AutoKeeperData autoKeeperData) {\n        this.autoKeeperData = autoKeeperData;\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/common/DataSourceCreator.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.common;\n\nimport java.sql.SQLException;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport javax.sql.DataSource;\n\nimport org.apache.commons.dbcp.BasicDataSource;\nimport org.apache.commons.lang.StringUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.DisposableBean;\nimport org.springframework.util.Assert;\nimport org.springframework.util.CollectionUtils;\n\nimport com.alibaba.otter.common.push.datasource.DataSourceHanlder;\nimport com.alibaba.otter.shared.common.model.config.data.DataMediaSource;\nimport com.alibaba.otter.shared.common.model.config.data.DataMediaType;\nimport com.alibaba.otter.shared.common.model.config.data.db.DbMediaSource;\n\npublic class DataSourceCreator implements DisposableBean {\n\n    private static final Logger     logger                        = LoggerFactory.getLogger(DataSourceCreator.class);\n\n    private int                     maxWait                       = 60 * 1000;\n\n    private int                     minIdle                       = 0;\n\n    private int                     initialSize                   = 1;\n\n    private int                     maxActive                     = 5;\n\n    private int                     maxIdle                       = 1;\n\n    private int                     numTestsPerEvictionRun        = -1;\n\n    private int                     timeBetweenEvictionRunsMillis = 60 * 1000;\n\n    private int                     removeAbandonedTimeout        = 10 * 60;\n\n    private int                     minEvictableIdleTimeMillis    = 30 * 60 * 1000;\n\n    private List<DataSourceHanlder> dataSourceHandlers;\n\n    /**\n     * 直接创建数据源，不做cache\n     */\n    public DataSource createDataSource(DataMediaSource dataMediaSource) {\n        Assert.notNull(dataMediaSource);\n        DbMediaSource dbMediaSource = (DbMediaSource) dataMediaSource;\n\n        // 扩展功能,可以自定义一些自己实现的 dataSource\n        DataSource customDataSource = preCreate(0L, dbMediaSource);\n        if (customDataSource != null) {\n            return customDataSource;\n        }\n\n        return createDataSource(dbMediaSource.getUrl(),\n            dbMediaSource.getUsername(),\n            dbMediaSource.getPassword(),\n            dbMediaSource.getDriver(),\n            dbMediaSource.getType(),\n            dbMediaSource.getEncode());\n    }\n\n    public void destroyDataSource(DataSource dataSource) {\n        try {\n            // for filter to destroy custom datasource\n            if (letHandlerDestroyIfSupport(0L, dataSource)) {\n                return;\n            }\n\n            if (dataSource == null) {\n                return;\n            }\n\n            BasicDataSource basicDataSource = (BasicDataSource) dataSource;\n            basicDataSource.close();\n        } catch (SQLException e) {\n            logger.error(\"ERROR ## close the datasource has an error\", e);\n        }\n    }\n\n    public void destroy() throws Exception {\n    }\n\n    /**\n     * 扩展功能,可以自定义一些自己实现的 dataSource\n     */\n    private DataSource preCreate(Long pipelineId, DbMediaSource dbMediaSource) {\n\n        if (CollectionUtils.isEmpty(dataSourceHandlers)) {\n            return null;\n        }\n\n        DataSource dataSource = null;\n        for (DataSourceHanlder handler : dataSourceHandlers) {\n            if (handler.support(dbMediaSource)) {\n                dataSource = handler.create(pipelineId, dbMediaSource);\n                if (dataSource != null) {\n                    return dataSource;\n                }\n            }\n        }\n        return null;\n    }\n\n    public boolean letHandlerDestroyIfSupport(Long pipelineId, DataSource source) {\n        boolean destroied = false;\n\n        if (CollectionUtils.isEmpty(this.dataSourceHandlers)) {\n            return destroied;\n        }\n\n        for (DataSourceHanlder handler : this.dataSourceHandlers) {\n            if (handler.support(source)) {\n                handler.destory(pipelineId);\n                destroied = true;\n                return destroied;\n            }\n        }\n        return destroied;\n\n    }\n\n    private DataSource createDataSource(String url, String userName, String password, String driverClassName,\n                                        DataMediaType dataMediaType, String encoding) {\n        BasicDataSource dbcpDs = new BasicDataSource();\n\n        dbcpDs.setInitialSize(initialSize);// 初始化连接池时创建的连接数\n        dbcpDs.setMaxActive(maxActive);// 连接池允许的最大并发连接数，值为非正数时表示不限制\n        dbcpDs.setMaxIdle(maxIdle);// 连接池中的最大空闲连接数，超过时，多余的空闲连接将会被释放，值为负数时表示不限制\n        dbcpDs.setMinIdle(minIdle);// 连接池中的最小空闲连接数，低于此数值时将会创建所欠缺的连接，值为0时表示不创建\n        dbcpDs.setMaxWait(maxWait);// 以毫秒表示的当连接池中没有可用连接时等待可用连接返回的时间，超时则抛出异常，值为-1时表示无限等待\n        dbcpDs.setRemoveAbandoned(true);// 是否清除已经超过removeAbandonedTimeout设置的无效连接\n        dbcpDs.setLogAbandoned(true);// 当清除无效链接时是否在日志中记录清除信息的标志\n        dbcpDs.setRemoveAbandonedTimeout(removeAbandonedTimeout); // 以秒表示清除无效链接的时限\n        dbcpDs.setNumTestsPerEvictionRun(numTestsPerEvictionRun);// 确保连接池中没有已破损的连接\n        dbcpDs.setTestOnBorrow(false);// 指定连接被调用时是否经过校验\n        dbcpDs.setTestOnReturn(false);// 指定连接返回到池中时是否经过校验\n        dbcpDs.setTestWhileIdle(true);// 指定连接进入空闲状态时是否经过空闲对象驱逐进程的校验\n        dbcpDs.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis); // 以毫秒表示空闲对象驱逐进程由运行状态进入休眠状态的时长，值为非正数时表示不运行任何空闲对象驱逐进程\n        dbcpDs.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis); // 以毫秒表示连接被空闲对象驱逐进程驱逐前在池中保持空闲状态的最小时间\n\n        // 动态的参数\n        dbcpDs.setDriverClassName(driverClassName);\n        dbcpDs.setUrl(url);\n        dbcpDs.setUsername(userName);\n        dbcpDs.setPassword(password);\n\n        if (dataMediaType.isOracle()) {\n            dbcpDs.addConnectionProperty(\"restrictGetTables\", \"true\");\n            // dbcpDs.setValidationQuery(\"select 1 from dual\");\n        } else if (dataMediaType.isMysql()) {\n            // open the batch mode for mysql since 5.1.8\n            dbcpDs.addConnectionProperty(\"useServerPrepStmts\", \"false\");\n            dbcpDs.addConnectionProperty(\"rewriteBatchedStatements\", \"true\");\n            dbcpDs.addConnectionProperty(\"zeroDateTimeBehavior\", \"convertToNull\");// 将0000-00-00的时间类型返回null\n            dbcpDs.addConnectionProperty(\"yearIsDateType\", \"false\");// 直接返回字符串，不做year转换date处理\n            dbcpDs.addConnectionProperty(\"noDatetimeStringSync\", \"true\");// 返回时间类型的字符串,不做时区处理\n            if (StringUtils.isNotEmpty(encoding)) {\n                if (StringUtils.equalsIgnoreCase(encoding, \"utf8mb4\")) {\n                    dbcpDs.addConnectionProperty(\"characterEncoding\", \"utf8\");\n                    dbcpDs.setConnectionInitSqls(Arrays.asList(\"set names utf8mb4\"));\n                } else {\n                    dbcpDs.addConnectionProperty(\"characterEncoding\", encoding);\n                }\n            }\n            // dbcpDs.setValidationQuery(\"select 1\");\n        } else {\n            logger.error(\"ERROR ## Unknow database type\");\n        }\n\n        return dbcpDs;\n    }\n\n    public void setMaxWait(int maxWait) {\n        this.maxWait = maxWait;\n    }\n\n    public void setMinIdle(int minIdle) {\n        this.minIdle = minIdle;\n    }\n\n    public void setInitialSize(int initialSize) {\n        this.initialSize = initialSize;\n    }\n\n    public void setMaxActive(int maxActive) {\n        this.maxActive = maxActive;\n    }\n\n    public void setMaxIdle(int maxIdle) {\n        this.maxIdle = maxIdle;\n    }\n\n    public void setNumTestsPerEvictionRun(int numTestsPerEvictionRun) {\n        this.numTestsPerEvictionRun = numTestsPerEvictionRun;\n    }\n\n    public void setTimeBetweenEvictionRunsMillis(int timeBetweenEvictionRunsMillis) {\n        this.timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis;\n    }\n\n    public void setRemoveAbandonedTimeout(int removeAbandonedTimeout) {\n        this.removeAbandonedTimeout = removeAbandonedTimeout;\n    }\n\n    public void setMinEvictableIdleTimeMillis(int minEvictableIdleTimeMillis) {\n        this.minEvictableIdleTimeMillis = minEvictableIdleTimeMillis;\n    }\n\n    public void setDataSourceHandlers(List<DataSourceHanlder> dataSourceHandlers) {\n        this.dataSourceHandlers = dataSourceHandlers;\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/common/alarm/AbstractAlarmService.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.common.alarm;\n\nimport java.util.concurrent.BlockingQueue;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.LinkedBlockingQueue;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.locks.LockSupport;\n\nimport org.apache.commons.lang.StringUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.DisposableBean;\nimport org.springframework.beans.factory.InitializingBean;\n\n/**\n * 报警服务实现\n * \n * @author jianghang 2011-11-3 上午11:12:16\n * @version 4.0.0\n */\npublic abstract class AbstractAlarmService implements AlarmService, InitializingBean, DisposableBean {\n\n    private static final Logger         logger = LoggerFactory.getLogger(AbstractAlarmService.class);\n\n    private BlockingQueue<AlarmMessage> queue  = new LinkedBlockingQueue<AlarmMessage>(3 * 3 * 3600);\n    private ExecutorService             executor;\n    private int                         period = 150;                                                // milliseconds\n\n    public void sendAlarm(AlarmMessage data) {\n        try {\n            if (!queue.offer(data, 2, TimeUnit.SECONDS)) {\n                logger.error(String.format(\"alarm sent to queue error : [%s]\", data.toString()));\n            }\n        } catch (Exception e) {\n            logger.error(String.format(\"send alarm [%s] to drgoon agent error!\", data.toString()), e);\n        }\n    }\n\n    private void sendAlarmInternal() {\n        AlarmMessage data = null;\n        try {\n            data = queue.take();\n            doSend(data);\n            logger.info(String.format(\"has sent alarm [%s] to drgoon agent.\", data.toString()));\n        } catch (InterruptedException e) {\n            logger.warn(\"otter-sendAlarm-worker was interrupted\", e);\n        } catch (Exception e) {\n            logger.error(String.format(\"send alarm [%s] to drgoon agent error!\", data.toString()), e);\n        }\n    }\n\n    protected abstract void doSend(AlarmMessage data) throws Exception;\n\n    public void afterPropertiesSet() throws Exception {\n        executor = Executors.newFixedThreadPool(1);\n        executor.submit(new Runnable() {\n\n            public void run() {\n                while (!Thread.currentThread().isInterrupted()) {\n                    sendAlarmInternal();\n                    LockSupport.parkNanos(period * 1000L * 1000L);\n                }\n            }\n        });\n    }\n\n    @Override\n    public void destroy() throws Exception {\n        if (executor != null && !executor.isShutdown()) {\n            executor.shutdown();\n            executor.awaitTermination(2, TimeUnit.SECONDS);\n        }\n        if (!queue.isEmpty()) {\n            int size = queue.size();\n            logger.warn(String.format(\"there are %d alarms wait to be sent \\n %s\", size, dumpQueue()));\n        }\n    }\n\n    protected String dumpQueue() {\n        if (queue.isEmpty()) {\n            return StringUtils.EMPTY;\n        }\n        StringBuilder sb = new StringBuilder();\n        for (AlarmMessage data : queue) {\n            sb.append(data.toString()).append(\"\\n\");\n        }\n\n        return sb.toString();\n    }\n\n    // ============= setter ===============\n\n    public void setPeriod(int period) {\n        this.period = period;\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/common/alarm/AlarmMessage.java",
    "content": "package com.alibaba.otter.manager.biz.common.alarm;\n\nimport java.io.Serializable;\n\nimport org.apache.commons.lang.builder.ToStringBuilder;\n\nimport com.alibaba.otter.shared.common.utils.OtterToStringStyle;\n\npublic class AlarmMessage implements Serializable {\n\n    private static final long serialVersionUID = 6110474591366995515L;\n    private String            message;\n    private String            receiveKey;\n\n    public AlarmMessage(){\n\n    }\n\n    public AlarmMessage(String message, String receiveKey){\n        this.message = message;\n        this.receiveKey = receiveKey;\n    }\n\n    public String getMessage() {\n        return message;\n    }\n\n    public void setMessage(String message) {\n        this.message = message;\n    }\n\n    public String getReceiveKey() {\n        return receiveKey;\n    }\n\n    public void setReceiveKey(String receiveKey) {\n        this.receiveKey = receiveKey;\n    }\n\n    @Override\n    public String toString() {\n        return ToStringBuilder.reflectionToString(this, OtterToStringStyle.DEFAULT_STYLE);\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/common/alarm/AlarmService.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.common.alarm;\n\n\n/**\n * 报警服务service定义,暂时先简单实现：利用dragoon的报警推送机制进行短信，邮件，旺旺信息等报警\n * \n * @author jianghang 2011-9-26 下午10:27:44\n * @version 4.0.0\n */\npublic interface AlarmService {\n\n    /**\n     * 发送基于kv的报警信息\n     * \n     * <pre>\n     * Map内容；\n     * 1. message : 报警内容\n     * 2. receiveKey : 报警接收者信息\n     * </pre>\n     * \n     * @param data\n     */\n    public void sendAlarm(AlarmMessage data);\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/common/alarm/DefaultAlarmService.java",
    "content": "package com.alibaba.otter.manager.biz.common.alarm;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.apache.commons.lang.StringUtils;\nimport org.springframework.mail.SimpleMailMessage;\nimport org.springframework.mail.javamail.JavaMailSender;\nimport org.springframework.mail.javamail.JavaMailSenderImpl;\n\nimport com.alibaba.otter.manager.biz.config.parameter.SystemParameterService;\nimport com.alibaba.otter.shared.common.model.config.parameter.SystemParameter;\n\n/**\n * 发送邮件进行报警\n * \n * @author jianghang 2013-9-6 上午11:42:04\n * @since 4.2.2\n */\npublic class DefaultAlarmService extends AbstractAlarmService {\n\n    private static final String    TITLE = \"alarm_from_otter\";\n    private String                 username;\n    private JavaMailSender         mailSender;\n    private SystemParameterService systemParameterService;\n\n    public void doSend(AlarmMessage data) throws Exception {\n        SimpleMailMessage mail = new SimpleMailMessage(); // 只发送纯文本\n        mail.setFrom(username);\n        mail.setSubject(TITLE);// 主题\n        mail.setText(data.getMessage());// 邮件内容\n        String receiveKeys[] = StringUtils.split(StringUtils.replace(data.getReceiveKey(), \";\", \",\"), \",\");\n\n        SystemParameter systemParameter = systemParameterService.find();\n        List<String> mailAddress = new ArrayList<String>();\n        for (String receiveKey : receiveKeys) {\n            String receiver = convertToReceiver(systemParameter, receiveKey);\n            String strs[] = StringUtils.split(StringUtils.replace(receiver, \";\", \",\"), \",\");\n            for (String str : strs) {\n                if (isMail(str)) {\n                    if (str != null) {\n                        mailAddress.add(str);\n                    }\n                } else if (isSms(str)) {\n                    // do nothing\n                }\n            }\n        }\n\n        if (!mailAddress.isEmpty()) {\n            mail.setTo(mailAddress.toArray(new String[mailAddress.size()]));\n            doSendMail(mail);\n        }\n    }\n\n    private void doSendMail(SimpleMailMessage mail) {\n        if (mailSender instanceof JavaMailSenderImpl) {\n            JavaMailSenderImpl mailSenderImpl = (JavaMailSenderImpl) mailSender;\n            if (StringUtils.isNotEmpty(mailSenderImpl.getUsername())\n                && StringUtils.isNotEmpty(mailSenderImpl.getPassword())) {\n                // 正确设置了账户/密码，才尝试发送邮件\n                mailSender.send(mail);\n            }\n        }\n    }\n\n    private boolean isMail(String receiveKey) {\n        return StringUtils.contains(receiveKey, '@');\n    }\n\n    private boolean isSms(String receiveKey) {\n        return false;\n    }\n\n    private String convertToReceiver(SystemParameter systemParameter, String receiveKey) {\n        if (StringUtils.equalsIgnoreCase(systemParameter.getDefaultAlarmReceiveKey(), receiveKey)) {\n            return systemParameter.getDefaultAlarmReceiver();\n        } else {\n            return systemParameter.getAlarmReceiver().get(receiveKey);\n        }\n    }\n\n    public void setMailSender(JavaMailSender mailSender) {\n        this.mailSender = mailSender;\n    }\n\n    public void setUsername(String username) {\n        this.username = username;\n    }\n\n    public void setSystemParameterService(SystemParameterService systemParameterService) {\n        this.systemParameterService = systemParameterService;\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/common/arbitrate/ArbitrateConfigImpl.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.common.arbitrate;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.springframework.beans.factory.InitializingBean;\n\nimport com.alibaba.otter.manager.biz.config.channel.ChannelService;\nimport com.alibaba.otter.manager.biz.config.node.NodeService;\nimport com.alibaba.otter.shared.arbitrate.impl.config.ArbitrateConfig;\nimport com.alibaba.otter.shared.arbitrate.impl.config.ArbitrateConfigRegistry;\nimport com.alibaba.otter.shared.common.model.config.ConfigException;\nimport com.alibaba.otter.shared.common.model.config.channel.Channel;\nimport com.alibaba.otter.shared.common.model.config.node.Node;\nimport com.alibaba.otter.shared.common.model.config.pipeline.Pipeline;\nimport com.alibaba.otter.shared.common.utils.cache.RefreshMemoryMirror;\nimport com.alibaba.otter.shared.common.utils.cache.RefreshMemoryMirror.ComputeFunction;\nimport com.google.common.base.Function;\nimport com.google.common.collect.OtterMigrateMap;\n\n/**\n * manager下的基于db查询的{@linkplain ArbitrateConfig}实现\n * \n * @author jianghang 2011-11-3 上午11:09:24\n * @version 4.0.0\n */\npublic class ArbitrateConfigImpl implements ArbitrateConfig, InitializingBean {\n\n    private static final Long                  DEFAULT_PERIOD = 60 * 1000L;\n    private Long                               timeout        = DEFAULT_PERIOD;\n    private RefreshMemoryMirror<Long, Channel> channelCache;\n    private Map<Long, Long>                    channelMapping;\n    private ChannelService                     channelService;\n    private NodeService                        nodeService;\n    private RefreshMemoryMirror<Long, Node>    nodeCache;\n\n    public ArbitrateConfigImpl(){\n        // 注册自己到arbitrate模块\n        ArbitrateConfigRegistry.regist(this);\n    }\n\n    public Node currentNode() {\n        return null;\n    }\n\n    public Node findNode(Long nid) {\n        return nodeCache.get(nid);\n    }\n\n    public Channel findChannel(Long channelId) {\n        return channelCache.get(channelId);\n    }\n\n    public Channel findChannelByPipelineId(Long pipelineId) {\n        Long channelId = channelMapping.get(pipelineId);\n        return channelCache.get(channelId);\n    }\n\n    public Pipeline findOppositePipeline(Long pipelineId) {\n        Long channelId = channelMapping.get(pipelineId);\n        Channel channel = channelCache.get(channelId);\n        List<Pipeline> pipelines = channel.getPipelines();\n        for (Pipeline pipeline : pipelines) {\n            if (pipeline.getId().equals(pipelineId) == false) {// 这里假定pipeline只有两个\n                return pipeline;\n            }\n        }\n\n        return null;\n    }\n\n    public Pipeline findPipeline(Long pipelineId) {\n        Long channelId = channelMapping.get(pipelineId);\n        Channel channel = channelCache.get(channelId);\n        List<Pipeline> pipelines = channel.getPipelines();\n        for (Pipeline pipeline : pipelines) {\n            if (pipeline.getId().equals(pipelineId)) {\n                return pipeline;\n            }\n        }\n\n        throw new ConfigException(\"no pipeline for pipelineId[\" + pipelineId + \"]\");\n    }\n\n    public void afterPropertiesSet() throws Exception {\n        // 获取一下nid变量\n        channelMapping = OtterMigrateMap.makeComputingMap(new Function<Long, Long>() {\n\n            public Long apply(Long pipelineId) {\n                // 处理下pipline -> channel映射关系不存在的情况\n                Channel channel = channelService.findByPipelineId(pipelineId);\n                if (channel == null) {\n                    throw new ConfigException(\"No Such Channel by pipelineId[\" + pipelineId + \"]\");\n                }\n\n                updateMapping(channel, pipelineId);// 排除下自己\n                channelCache.put(channel.getId(), channel);// 更新下channelCache\n                return channel.getId();\n\n            }\n        });\n\n        channelCache = new RefreshMemoryMirror<Long, Channel>(timeout, new ComputeFunction<Long, Channel>() {\n\n            public Channel apply(Long key, Channel oldValue) {\n                Channel channel = channelService.findById(key);\n                if (channel == null) {\n                    // 其他情况直接返回内存中的旧值\n                    return oldValue;\n                } else {\n                    updateMapping(channel, null);// 排除下自己\n                    return channel;\n                }\n            }\n        });\n\n        nodeCache = new RefreshMemoryMirror<Long, Node>(timeout, new ComputeFunction<Long, Node>() {\n\n            public Node apply(Long key, Node oldValue) {\n                Node node = nodeService.findById(key);\n                if (node == null) {\n                    return oldValue;\n                } else {\n                    return node;\n                }\n            }\n        });\n    }\n\n    private void updateMapping(Channel channel, Long excludeId) {\n        Long channelId = channel.getId();\n        List<Pipeline> pipelines = channel.getPipelines();\n        for (Pipeline pipeline : pipelines) {\n            if (excludeId == null || !pipeline.getId().equals(excludeId)) {\n                channelMapping.put(pipeline.getId(), channelId);\n            }\n        }\n    }\n\n    public void setChannelService(ChannelService channelService) {\n        this.channelService = channelService;\n    }\n\n    public void setNodeService(NodeService nodeService) {\n        this.nodeService = nodeService;\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/common/arbitrate/DeadNodeListener.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.common.arbitrate;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.concurrent.DelayQueue;\nimport java.util.concurrent.Delayed;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.TimeUnit;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.DisposableBean;\nimport org.springframework.beans.factory.InitializingBean;\n\nimport com.alibaba.otter.manager.biz.config.channel.ChannelService;\nimport com.alibaba.otter.manager.biz.monitor.PassiveMonitor;\nimport com.alibaba.otter.shared.arbitrate.ArbitrateManageService;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.monitor.NodeMonitor;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.monitor.listener.NodeListener;\nimport com.alibaba.otter.shared.common.model.config.alarm.MonitorName;\nimport com.alibaba.otter.shared.common.model.config.channel.Channel;\nimport com.alibaba.otter.shared.common.model.config.channel.ChannelStatus;\nimport com.alibaba.otter.shared.communication.model.arbitrate.NodeAlarmEvent;\nimport com.google.common.collect.Lists;\n\n/**\n * 默认的死亡节点处理\n * \n * @author jianghang 2011-9-26 下午10:37:40\n * @version 4.0.0\n */\npublic class DeadNodeListener implements NodeListener, InitializingBean, DisposableBean {\n\n    private static final Logger                  logger       = LoggerFactory.getLogger(DeadNodeListener.class);\n    private volatile DelayQueue<DeadNodeDelayed> queue        = new DelayQueue<DeadNodeDelayed>();\n    private NodeMonitor                          nodeMonitor;\n    private ArbitrateManageService               arbitrateManageService;\n    private PassiveMonitor                       exceptionRuleMonitor;\n    private ChannelService                       channelService;\n    private ExecutorService                      executor;\n    private long                                 checkTime    = 60 * 1000L;                                     // 30秒\n    private volatile List<Long>                  currentNodes = new ArrayList<Long>();                          // 当前存活节点\n\n    public DeadNodeListener(){\n    }\n\n    public void afterPropertiesSet() throws Exception {\n        nodeMonitor.addListener(this);\n        executor = Executors.newFixedThreadPool(1);\n        executor.submit(new Runnable() {\n\n            public void run() {\n                while (true) {\n                    DeadNodeDelayed delay = null;\n                    try {\n                        delay = queue.take();\n                        processDead(delay.getNid());\n                    } catch (Throwable e) {\n                        logger.error(String.format(\"error happened with [%s]\", delay.toString()), e);\n                    }\n                }\n            }\n        });\n    }\n\n    public synchronized void processChanged(List<Long> nodes) {\n        Set<Long> deadNodes = new HashSet<Long>();\n        for (Long node : currentNodes) {\n            if (!nodes.contains(node)) {\n                deadNodes.add(node);\n            }\n        }\n\n        currentNodes = nodes;// 切换引用，需设置为volatile保证线程安全&可见性\n        // 处理下dead node\n        if (deadNodes.size() > 0) {\n            for (Long nid : deadNodes) {\n                Delayed delayed = new DeadNodeDelayed(nid, checkTime);\n                if (!queue.contains(delayed)) {// 不重复添加\n                    queue.add(new DeadNodeDelayed(nid, checkTime));\n                }\n            }\n        }\n    }\n\n    private void processDead(Long deadNode) {\n        List<Long> aliveNodes = nodeMonitor.getAliveNodes(true);\n        // 需要考虑一种网络瞬断的情况，会导致node所有出现重连，导致出现restart风暴，执行restart时需要重新check下是否存活\n        if (aliveNodes.contains(deadNode)) {\n            logger.warn(\"dead node[{}] happend just one moment , check it's alived\", deadNode);\n            return;\n        }\n\n        // 发送一条报警信息\n        List<Long> channelIds = Lists.newArrayList();\n        List<Channel> channels = channelService.listByNodeId(deadNode, ChannelStatus.START);\n        for (Channel channel : channels) {\n            channelIds.add(channel.getId());\n        }\n        Collections.sort(channelIds);\n\n        NodeAlarmEvent alarm = new NodeAlarmEvent();\n        alarm.setPipelineId(-1L);\n        alarm.setTitle(MonitorName.EXCEPTION.name());\n        alarm.setMessage(String.format(\"nid:%s is dead and restart cids:%s\", String.valueOf(deadNode),\n                                       channelIds.toString()));\n        try {\n            exceptionRuleMonitor.feed(alarm, alarm.getPipelineId());\n        } catch (Exception e) {\n            logger.error(String.format(\"ERROR # exceptionRuleMonitor error for %s\", alarm.toString()), e);\n        }\n\n        for (Long channelId : channelIds) {// 重启一下对应的channel\n            boolean result = arbitrateManageService.channelEvent().restart(channelId);\n            if (result) {\n                channelService.notifyChannel(channelId);// 推送一下配置\n            }\n        }\n\n    }\n\n    public void destroy() throws Exception {\n        nodeMonitor.removeListener(this);\n        executor.shutdownNow();\n    }\n\n    /**\n     * 构建一个基于时间的delay queue，解决一个问题：node出现网络闪段，其jvm并不是真实关闭，可以间隔几秒后check一下是否存活再判断是否进行RESTART操作\n     * \n     * @author jianghang 2012-8-29 下午01:42:47\n     * @version 4.1.0\n     */\n    public static class DeadNodeDelayed implements Delayed {\n\n        // init time for nano\n        private static final long MILL_ORIGIN = System.currentTimeMillis();\n        private long              nid;\n        private long              now;                                     // 记录具体的now的偏移时间点，单位ms\n        private long              timeout;                                 // 记录具体需要被delayed处理的偏移时间点,单位ms\n\n        public DeadNodeDelayed(long nid, long timeout){\n            this.nid = nid;\n            this.timeout = timeout;\n            this.now = System.currentTimeMillis() - MILL_ORIGIN;\n        }\n\n        public long getNid() {\n            return nid;\n        }\n\n        public long getNow() {\n            return now;\n        }\n\n        public long getDelay(TimeUnit unit) {\n            long currNow = System.currentTimeMillis() - MILL_ORIGIN;\n            long d = unit.convert(now + timeout - currNow, TimeUnit.MILLISECONDS);\n            return d;\n        }\n\n        public int compareTo(Delayed other) {\n            if (other == this) { // compare zero ONLY if same object\n                return 0;\n            } else if (other instanceof DeadNodeDelayed) {\n                DeadNodeDelayed x = (DeadNodeDelayed) other;\n                long diff = now + timeout - (x.now + x.timeout);\n                return (diff == 0) ? 0 : ((diff < 0) ? 1 : -1); // 时间越小的，越应该排在前面\n            } else {\n                long d = (getDelay(TimeUnit.MILLISECONDS) - other.getDelay(TimeUnit.MILLISECONDS));\n                return (d == 0) ? 0 : ((d < 0) ? 1 : -1);\n            }\n        }\n\n        @Override\n        public int hashCode() {\n            final int prime = 31;\n            int result = 1;\n            result = prime * result + (int) (nid ^ (nid >>> 32));\n            return result;\n        }\n\n        public boolean equals(Object obj) {\n            if (this == obj) {\n                return true;\n            }\n            if (obj == null) {\n                return false;\n            }\n            if (!(obj instanceof DeadNodeDelayed)) {\n                return false;\n            }\n            DeadNodeDelayed other = (DeadNodeDelayed) obj;\n            if (nid != other.nid) {\n                return false;\n            }\n            return true;\n        }\n\n    }\n\n    public void setNodeMonitor(NodeMonitor nodeMonitor) {\n        this.nodeMonitor = nodeMonitor;\n    }\n\n    public void setArbitrateManageService(ArbitrateManageService arbitrateManageService) {\n        this.arbitrateManageService = arbitrateManageService;\n    }\n\n    public void setChannelService(ChannelService channelService) {\n        this.channelService = channelService;\n    }\n\n    public void setExceptionRuleMonitor(PassiveMonitor exceptionRuleMonitor) {\n        this.exceptionRuleMonitor = exceptionRuleMonitor;\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/common/basedao/GenericDAO.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.common.basedao;\n\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * @author simon 2011-10-31 上午09:40:47\n */\npublic interface GenericDAO<T> {\n\n    public T insert(T entityObj);\n\n    public void delete(Long identity);\n\n    public void update(T entityObj);\n\n    public List<T> listAll();\n\n    public List<T> listByCondition(Map condition);\n\n    public List<T> listByMultiId(Long... identities);\n\n    public T findById(Long identity);\n\n    public int getCount();\n\n    public int getCount(Map condition);\n\n    public boolean checkUnique(T entityObj);\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/common/baseservice/GenericService.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.common.baseservice;\n\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * @author simon 2011-10-31 上午10:34:17\n */\npublic interface GenericService<T> {\n\n    public void create(T entityObj);\n\n    public void remove(Long identity);\n\n    public void modify(T entityObj);\n\n    public T findById(Long identity);\n\n    public List<T> listByIds(Long... identities);\n\n    public List<T> listAll();\n\n    public List<T> listByCondition(Map condition);\n\n    public int getCount();\n\n    public int getCount(Map condition);\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/common/exceptions/InvalidConfigureException.java",
    "content": "package com.alibaba.otter.manager.biz.common.exceptions;\n\npublic class InvalidConfigureException extends RuntimeException {\n\n    private static final long serialVersionUID = 1L;\n\n    public static enum INVALID_TYPE {\n        DDL, HOME\n    }\n\n    private INVALID_TYPE type;\n\n    public InvalidConfigureException(INVALID_TYPE type){\n        super(type.name());\n        this.type = type;\n    }\n\n    public INVALID_TYPE getType() {\n        return type;\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/common/exceptions/ManagerException.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.common.exceptions;\n\n/**\n * ManagerException for Manager Model\n * \n * @author simon 2011-11-13 下午07:38:47\n */\npublic class ManagerException extends RuntimeException {\n\n    private static final long serialVersionUID = 1L;\n\n    public ManagerException(String cause){\n        super(cause);\n    }\n\n    public ManagerException(Throwable t){\n        super(t);\n    }\n\n    public ManagerException(String cause, Throwable t){\n        super(cause, t);\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/common/exceptions/RepeatConfigureException.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.common.exceptions;\n\n/**\n * @author simon 2011-11-14 下午11:04:32\n */\npublic class RepeatConfigureException extends RuntimeException {\n\n    private static final long serialVersionUID = 1L;\n\n    public RepeatConfigureException(String cause){\n        super(cause);\n    }\n\n    public RepeatConfigureException(Throwable t){\n        super(t);\n    }\n\n    public RepeatConfigureException(String cause, Throwable t){\n        super(cause, t);\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/alarm/AlarmRuleService.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.config.alarm;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport com.alibaba.otter.shared.common.model.config.alarm.AlarmRule;\nimport com.alibaba.otter.shared.common.model.config.alarm.AlarmRuleStatus;\n\n/**\n * @author simon 2012-8-24 上午5:27:35\n * @author zebin.xuzb\n * @version 4.1.0\n */\npublic interface AlarmRuleService {\n\n    void create(AlarmRule alarmRule);\n\n    void modify(AlarmRule alarmRule);\n\n    void remove(Long alarmRuleId);\n\n    public void enableMonitor(Long alarmRuleId);\n\n    public void disableMonitor(Long alarmRuleId);\n\n    public void disableMonitor(Long alarmRuleId, String pauseTime);\n\n    List<AlarmRule> getAllAlarmRules(AlarmRuleStatus status);\n\n    AlarmRule getAlarmRuleById(Long AlarmRuleId);\n\n    /**\n     * 获取所有状态为 status 的 {@linkplain AlarmRule}，并且按照pipelineId分区\n     * \n     * @param status\n     * @return\n     */\n    Map<Long, List<AlarmRule>> getAlarmRules(AlarmRuleStatus status);\n\n    List<AlarmRule> getAlarmRules(Long pipelineId);\n\n    List<AlarmRule> getAlarmRules(Long pipelineId, AlarmRuleStatus status);\n\n    List<AlarmRule> listAllAlarmRules(Map condition);\n\n    public int getCount();\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/alarm/dal/AlarmRuleDAO.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.config.alarm.dal;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport com.alibaba.otter.manager.biz.config.alarm.dal.dataobject.AlarmRuleDO;\nimport com.alibaba.otter.shared.common.model.config.alarm.AlarmRuleStatus;\n\n/**\n * @author simon 2012-8-24 上午5:17:00\n * @version 4.1.0\n */\npublic interface AlarmRuleDAO {\n\n    public AlarmRuleDO insert(AlarmRuleDO entityObj);\n\n    public void update(AlarmRuleDO entityObj);\n\n    public void delete(Long id);\n\n    public AlarmRuleDO findById(Long alarmRuleId);\n\n    public List<AlarmRuleDO> listByPipelineId(Long pipelineId);\n\n    public List<AlarmRuleDO> listByPipelineId(Long pipelineId, AlarmRuleStatus status);\n\n    public List<AlarmRuleDO> listAll();\n\n    public List<AlarmRuleDO> listAllByPipeline(Map condition);\n\n    public List<AlarmRuleDO> listByStatus(AlarmRuleStatus status);\n\n    public int getCount();\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/alarm/dal/dataobject/AlarmRuleDO.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.config.alarm.dal.dataobject;\n\nimport java.io.Serializable;\nimport java.util.Date;\n\nimport com.alibaba.otter.shared.common.model.config.alarm.AlarmRuleStatus;\nimport com.alibaba.otter.shared.common.model.config.alarm.MonitorName;\n\n/**\n * @author simon 2012-8-22 下午3:42:34\n * @version 4.1.0\n */\npublic class AlarmRuleDO implements Serializable {\n\n    private static final long  serialVersionUID   = -1284784362325347636L;\n    private Long               id;\n    private Long               pipelineId;\n    private AlarmRuleStatus    status;\n    private MonitorName        monitorName;\n    private String             receiverKey;\n    private String             matchValue;\n    private AlarmRuleParameter alarmRuleParameter = new AlarmRuleParameter();\n    private String             description;\n    private Date               gmtCreate;\n    private Date               gmtModified;\n\n    public Long getId() {\n        return id;\n    }\n\n    public void setId(Long id) {\n        this.id = id;\n    }\n\n    public Long getPipelineId() {\n        return pipelineId;\n    }\n\n    public void setPipelineId(Long pipelineId) {\n        this.pipelineId = pipelineId;\n    }\n\n    public AlarmRuleStatus getStatus() {\n        return status;\n    }\n\n    public void setStatus(AlarmRuleStatus status) {\n        this.status = status;\n    }\n\n    public MonitorName getMonitorName() {\n        return monitorName;\n    }\n\n    public void setMonitorName(MonitorName monitorName) {\n        this.monitorName = monitorName;\n    }\n\n    public String getReceiverKey() {\n        return receiverKey;\n    }\n\n    public void setReceiverKey(String receiverKey) {\n        this.receiverKey = receiverKey;\n    }\n\n    public String getMatchValue() {\n        return matchValue;\n    }\n\n    public void setMatchValue(String matchValue) {\n        this.matchValue = matchValue;\n    }\n\n    public AlarmRuleParameter getAlarmRuleParameter() {\n        return alarmRuleParameter;\n    }\n\n    public void setAlarmRuleParameter(AlarmRuleParameter alarmRuleParameter) {\n        this.alarmRuleParameter = alarmRuleParameter;\n    }\n\n    public String getDescription() {\n        return description;\n    }\n\n    public void setDescription(String description) {\n        this.description = description;\n    }\n\n    public Date getGmtCreate() {\n        return gmtCreate;\n    }\n\n    public void setGmtCreate(Date gmtCreate) {\n        this.gmtCreate = gmtCreate;\n    }\n\n    public Date getGmtModified() {\n        return gmtModified;\n    }\n\n    public void setGmtModified(Date gmtModified) {\n        this.gmtModified = gmtModified;\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/alarm/dal/dataobject/AlarmRuleParameter.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.config.alarm.dal.dataobject;\n\nimport java.io.Serializable;\n\n/**\n * @author simon 2012-9-12 上午10:35:13\n * @version 4.1.0\n */\npublic class AlarmRuleParameter implements Serializable {\n\n    private static final long serialVersionUID = 1570395344191530689L;\n    private Long              intervalTime     = 1800L;\n    private String            pauseTime;\n    private Integer           recoveryThresold = 3;\n    private Boolean           autoRecovery     = false;\n\n    public Long getIntervalTime() {\n        return intervalTime;\n    }\n\n    public void setIntervalTime(Long intervalTime) {\n        this.intervalTime = intervalTime;\n    }\n\n    public String getPauseTime() {\n        return pauseTime;\n    }\n\n    public void setPauseTime(String pauseTime) {\n        this.pauseTime = pauseTime;\n    }\n\n    public Boolean getAutoRecovery() {\n        return autoRecovery;\n    }\n\n    public void setAutoRecovery(Boolean autoRecovery) {\n        this.autoRecovery = autoRecovery;\n    }\n\n    public Integer getRecoveryThresold() {\n        return recoveryThresold;\n    }\n\n    public void setRecoveryThresold(Integer recoveryThresold) {\n        this.recoveryThresold = recoveryThresold;\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/alarm/dal/ibatis/AlarmRuleParameterTypeHandler.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.config.alarm.dal.ibatis;\n\nimport java.sql.SQLException;\n\nimport com.alibaba.otter.manager.biz.config.alarm.dal.dataobject.AlarmRuleParameter;\nimport com.alibaba.otter.shared.common.utils.JsonUtils;\nimport com.ibatis.sqlmap.client.extensions.ParameterSetter;\nimport com.ibatis.sqlmap.client.extensions.ResultGetter;\nimport com.ibatis.sqlmap.client.extensions.TypeHandlerCallback;\n\n/**\n * 用于AlarmRuleParameter的解析\n * \n * @author simon\n */\npublic class AlarmRuleParameterTypeHandler implements TypeHandlerCallback {\n\n    @Override\n    public void setParameter(ParameterSetter setter, Object parameter) throws SQLException {\n        setter.setString(JsonUtils.marshalToString(parameter));\n    }\n\n    @Override\n    public Object getResult(ResultGetter getter) throws SQLException {\n        return JsonUtils.unmarshalFromString(getter.getString(), AlarmRuleParameter.class);\n    }\n\n    public Object valueOf(String s) {\n        return JsonUtils.unmarshalFromString(s, AlarmRuleParameter.class);\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/alarm/dal/ibatis/IbatisAlarmRuleDAO.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.config.alarm.dal.ibatis;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.springframework.orm.ibatis.support.SqlMapClientDaoSupport;\n\nimport com.alibaba.otter.shared.common.utils.Assert;\nimport com.alibaba.otter.manager.biz.config.alarm.dal.AlarmRuleDAO;\nimport com.alibaba.otter.manager.biz.config.alarm.dal.dataobject.AlarmRuleDO;\nimport com.alibaba.otter.shared.common.model.config.alarm.AlarmRuleStatus;\n\n/**\n * @author simon\n */\npublic class IbatisAlarmRuleDAO extends SqlMapClientDaoSupport implements AlarmRuleDAO {\n\n    public AlarmRuleDO insert(AlarmRuleDO entityObj) {\n        Assert.assertNotNull(entityObj);\n        getSqlMapClientTemplate().insert(\"insertAlarmRule\", entityObj);\n        return entityObj;\n    }\n\n    public void update(AlarmRuleDO entityObj) {\n        Assert.assertNotNull(entityObj);\n        getSqlMapClientTemplate().update(\"updateAlarmRule\", entityObj);\n    }\n\n    public void delete(Long id) {\n        Assert.assertNotNull(id);\n        getSqlMapClientTemplate().update(\"deleteAlarmRuleById\", id);\n    }\n\n    public AlarmRuleDO findById(Long alarmRuleId) {\n        Assert.assertNotNull(alarmRuleId);\n        AlarmRuleDO alarmRuleDo = (AlarmRuleDO) getSqlMapClientTemplate().queryForObject(\"findByRuleId\", alarmRuleId);\n        return alarmRuleDo;\n    }\n\n    public List<AlarmRuleDO> listByPipelineId(Long pipelineId) {\n        Assert.assertNotNull(pipelineId);\n        List<AlarmRuleDO> alarmRuleDos = getSqlMapClientTemplate().queryForList(\"listAlarmByPipelineId\", pipelineId);\n        return alarmRuleDos;\n    }\n\n    public List<AlarmRuleDO> listByPipelineId(Long pipelineId, AlarmRuleStatus status) {\n        List<AlarmRuleDO> alarmRuleDos = listByPipelineId(pipelineId);\n        List<AlarmRuleDO> result = new ArrayList<AlarmRuleDO>();\n        for (AlarmRuleDO alarmRuleDo : alarmRuleDos) {\n            if (alarmRuleDo.getStatus().equals(status)) {\n                result.add(alarmRuleDo);\n            }\n        }\n        return result;\n    }\n\n    public List<AlarmRuleDO> listAll() {\n        List<AlarmRuleDO> alarmRuleDos = getSqlMapClientTemplate().queryForList(\"listAllAlarmRule\");\n        return alarmRuleDos;\n    }\n\n    public List<AlarmRuleDO> listAllByPipeline(Map condition) {\n        List<AlarmRuleDO> alarmRuleDos = getSqlMapClientTemplate().queryForList(\"listAllAlarmOrderByPipeline\",\n                                                                                condition);\n        return alarmRuleDos;\n    }\n\n    public List<AlarmRuleDO> listByStatus(AlarmRuleStatus status) {\n        List<AlarmRuleDO> alarmRuleDos = getSqlMapClientTemplate().queryForList(\"listAlarmByStatus\", status);\n        return alarmRuleDos;\n    }\n\n    public int getCount() {\n        Integer count = (Integer) getSqlMapClientTemplate().queryForObject(\"getAlarmRuleCount\");\n        return count.intValue();\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/alarm/impl/AlarmRuleServiceImpl.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.config.alarm.impl;\n\nimport java.text.ParseException;\nimport java.text.SimpleDateFormat;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.apache.commons.lang.StringUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.alibaba.otter.manager.biz.common.exceptions.ManagerException;\nimport com.alibaba.otter.manager.biz.config.alarm.AlarmRuleService;\nimport com.alibaba.otter.manager.biz.config.alarm.dal.AlarmRuleDAO;\nimport com.alibaba.otter.manager.biz.config.alarm.dal.dataobject.AlarmRuleDO;\nimport com.alibaba.otter.manager.biz.config.alarm.dal.dataobject.AlarmRuleParameter;\nimport com.alibaba.otter.shared.common.model.config.alarm.AlarmRule;\nimport com.alibaba.otter.shared.common.model.config.alarm.AlarmRuleStatus;\nimport com.alibaba.otter.shared.common.utils.Assert;\n\n/**\n * @author simon 2012-8-24 上午5:29:55\n * @version 4.1.0\n */\npublic class AlarmRuleServiceImpl implements AlarmRuleService {\n\n    private static final Logger logger           = LoggerFactory.getLogger(AlarmRuleServiceImpl.class);\n    public static final String  TIMESTAMP_FORMAT = \"yyyy-MM-dd HH:mm:ss\";\n    private AlarmRuleDAO        alarmRuleDao;\n\n    public void create(AlarmRule alarmRule) {\n        Assert.assertNotNull(alarmRule);\n        alarmRuleDao.insert(modelToDo(alarmRule));\n    }\n\n    public void modify(AlarmRule alarmRule) {\n        AlarmRuleDO alarmRuleDo = modelToDo(alarmRule);\n        alarmRuleDao.update(alarmRuleDo);\n\n    }\n\n    public void remove(Long alarmRuleId) {\n        alarmRuleDao.delete(alarmRuleId);\n    }\n\n    private void switchAlarmRuleStatus(Long alarmRuleId, AlarmRuleStatus alarmRuleStatus, String pauseTime) {\n        AlarmRuleDO alarmRuleDo = alarmRuleDao.findById(alarmRuleId);\n\n        if (null == alarmRuleDo) {\n            String exceptionCause = \"query alarmRule:\" + alarmRuleId + \" return null.\";\n            logger.error(\"ERROR ## \" + exceptionCause);\n            throw new ManagerException(exceptionCause);\n        }\n\n        alarmRuleDo.setStatus(alarmRuleStatus);\n        if (alarmRuleDo.getAlarmRuleParameter() != null) {\n            alarmRuleDo.getAlarmRuleParameter().setPauseTime(pauseTime);\n        } else if (StringUtils.isNotEmpty(pauseTime)) {\n            alarmRuleDo.setAlarmRuleParameter(new AlarmRuleParameter());\n            alarmRuleDo.getAlarmRuleParameter().setPauseTime(pauseTime);\n        }\n        alarmRuleDao.update(alarmRuleDo);\n    }\n\n    public void enableMonitor(Long alarmRuleId) {\n        switchAlarmRuleStatus(alarmRuleId, AlarmRuleStatus.ENABLE, null);\n    }\n\n    public void disableMonitor(Long alarmRuleId) {\n        switchAlarmRuleStatus(alarmRuleId, AlarmRuleStatus.DISABLE, null);\n    }\n\n    public void disableMonitor(Long alarmRuleId, String pauseTime) {\n        switchAlarmRuleStatus(alarmRuleId, AlarmRuleStatus.ENABLE, pauseTime);\n    }\n\n    public AlarmRule getAlarmRuleById(Long AlarmRuleId) {\n        Assert.assertNotNull(AlarmRuleId);\n        return doToModel(alarmRuleDao.findById(AlarmRuleId));\n    }\n\n    public List<AlarmRule> getAllAlarmRules(AlarmRuleStatus status) {\n        Assert.assertNotNull(status);\n        List<AlarmRuleDO> alarmRuleDos = alarmRuleDao.listByStatus(status);\n        return doToModel(alarmRuleDos);\n    }\n\n    public Map<Long, List<AlarmRule>> getAlarmRules(AlarmRuleStatus status) {\n        Assert.assertNotNull(status);\n        List<AlarmRule> alarmRules = getAllAlarmRules(status);\n        Map<Long, List<AlarmRule>> result = new HashMap<Long, List<AlarmRule>>();\n        for (AlarmRule alarmRule : alarmRules) {\n            List<AlarmRule> rules = result.get(alarmRule.getPipelineId());\n            if (rules == null) {\n                rules = new ArrayList<AlarmRule>();\n            }\n            if (!rules.contains(alarmRule)) {\n                rules.add(alarmRule);\n            }\n            result.put(alarmRule.getPipelineId(), rules);\n        }\n        return result;\n    }\n\n    public List<AlarmRule> getAlarmRules(Long pipelineId) {\n        Assert.assertNotNull(pipelineId);\n        List<AlarmRuleDO> alarmRuleDos = alarmRuleDao.listByPipelineId(pipelineId);\n        return doToModel(alarmRuleDos);\n    }\n\n    public List<AlarmRule> getAlarmRules(Long pipelineId, AlarmRuleStatus status) {\n        Assert.assertNotNull(pipelineId);\n        Assert.assertNotNull(status);\n        List<AlarmRuleDO> alarmRuleDos = alarmRuleDao.listByPipelineId(pipelineId, status);\n\n        return doToModel(alarmRuleDos);\n    }\n\n    public List<AlarmRule> listAllAlarmRules(Map condition) {\n        List<AlarmRule> alarmRules = doToModel(alarmRuleDao.listAllByPipeline(condition));\n        return alarmRules;\n    }\n\n    public int getCount() {\n        return alarmRuleDao.getCount();\n    }\n\n    private AlarmRule doToModel(AlarmRuleDO alarmRuleDo) {\n        AlarmRule alarmRule = new AlarmRule();\n        alarmRule.setId(alarmRuleDo.getId());\n        alarmRule.setMatchValue(alarmRuleDo.getMatchValue());\n        alarmRule.setMonitorName(alarmRuleDo.getMonitorName());\n        alarmRule.setReceiverKey(alarmRuleDo.getReceiverKey());\n        // 如果数据库里面的数据为空，则返回默认值\n        alarmRule.setIntervalTime(alarmRuleDo.getAlarmRuleParameter() == null ? 1800L : alarmRuleDo.getAlarmRuleParameter().getIntervalTime());\n        String pauseTime = alarmRuleDo.getAlarmRuleParameter() == null ? null : alarmRuleDo.getAlarmRuleParameter().getPauseTime();\n        if (StringUtils.isNotEmpty(pauseTime)) {\n            SimpleDateFormat format = new SimpleDateFormat(TIMESTAMP_FORMAT);\n            try {\n                alarmRule.setPauseTime(format.parse(pauseTime));\n            } catch (ParseException e) {\n                throw new ManagerException(e);\n            }\n        }\n\n        alarmRule.setAutoRecovery(alarmRuleDo.getAlarmRuleParameter() == null ? false : alarmRuleDo.getAlarmRuleParameter().getAutoRecovery());\n        alarmRule.setRecoveryThresold(alarmRuleDo.getAlarmRuleParameter() == null ? 3 : alarmRuleDo.getAlarmRuleParameter().getRecoveryThresold());\n        alarmRule.setPipelineId(alarmRuleDo.getPipelineId());\n        alarmRule.setStatus(alarmRuleDo.getStatus());\n        alarmRule.setDescription(alarmRuleDo.getDescription());\n        alarmRule.setGmtCreate(alarmRuleDo.getGmtCreate());\n        alarmRule.setGmtModified(alarmRuleDo.getGmtModified());\n        return alarmRule;\n    }\n\n    private List<AlarmRule> doToModel(List<AlarmRuleDO> alarmRuleDos) {\n        List<AlarmRule> alarmRules = new ArrayList<AlarmRule>();\n        for (AlarmRuleDO alarmRuleDo : alarmRuleDos) {\n            alarmRules.add(doToModel(alarmRuleDo));\n        }\n        return alarmRules;\n    }\n\n    private AlarmRuleDO modelToDo(AlarmRule alarmRule) {\n        AlarmRuleDO alarmRuleDo = new AlarmRuleDO();\n        alarmRuleDo.setId(alarmRule.getId());\n        alarmRuleDo.setMatchValue(alarmRule.getMatchValue());\n        alarmRuleDo.setMonitorName(alarmRule.getMonitorName());\n        alarmRuleDo.setReceiverKey(alarmRule.getReceiverKey());\n        alarmRuleDo.setPipelineId(alarmRule.getPipelineId());\n        alarmRuleDo.setStatus(alarmRule.getStatus());\n        alarmRuleDo.setDescription(alarmRule.getDescription());\n        alarmRuleDo.setGmtCreate(alarmRule.getGmtCreate());\n        alarmRuleDo.setGmtModified(alarmRule.getGmtModified());\n        AlarmRuleParameter alarmRuleParameter = new AlarmRuleParameter();\n        alarmRuleParameter.setIntervalTime(alarmRule.getIntervalTime());\n        if (alarmRule.getPauseTime() != null) {\n            SimpleDateFormat format = new SimpleDateFormat(TIMESTAMP_FORMAT);\n            alarmRuleParameter.setPauseTime(format.format(alarmRule.getPauseTime()));\n        }\n        alarmRuleParameter.setAutoRecovery(alarmRule.getAutoRecovery());\n        alarmRuleParameter.setRecoveryThresold(alarmRule.getRecoveryThresold());\n        alarmRuleDo.setAlarmRuleParameter(alarmRuleParameter);\n\n        return alarmRuleDo;\n    }\n\n    public void setAlarmRuleDao(AlarmRuleDAO alarmRuleDao) {\n        this.alarmRuleDao = alarmRuleDao;\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/autokeeper/AutoKeeperClusterService.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.config.autokeeper;\n\nimport java.util.List;\n\nimport com.alibaba.otter.shared.common.model.autokeeper.AutoKeeperCluster;\n\n/**\n * @author simon 2012-9-24 下午5:35:01\n * @version 4.1.0\n */\npublic interface AutoKeeperClusterService {\n\n    public AutoKeeperCluster findAutoKeeperClusterById(Long id);\n\n    public List<AutoKeeperCluster> listAutoKeeperClusters();\n\n    public void modifyAutoKeeperCluster(AutoKeeperCluster autoKeeperCluster);\n\n    public void createAutoKeeperCluster(AutoKeeperCluster autoKeeperCluster);\n\n    public void removeAutoKeeperCluster(Long id);\n\n    public Integer getCount();\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/autokeeper/dal/AutoKeeperClusterDAO.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.config.autokeeper.dal;\n\nimport java.util.List;\n\nimport com.alibaba.otter.manager.biz.config.autokeeper.dal.dataobject.AutoKeeperClusterDO;\n\npublic interface AutoKeeperClusterDAO {\n\n    public AutoKeeperClusterDO findAutoKeeperClusterById(Long id);\n\n    public List<AutoKeeperClusterDO> listAutoKeeperClusters();\n\n    public void updateAutoKeeperCluster(AutoKeeperClusterDO autoKeeperClusterDo);\n\n    public void insertAutoKeeperClusterDO(AutoKeeperClusterDO autoKeeperClusterDo);\n\n    public void delete(Long clusterId);\n\n    public Integer getCount();\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/autokeeper/dal/dataobject/AutoKeeperClusterDO.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.config.autokeeper.dal.dataobject;\n\nimport java.util.Date;\n\nimport org.apache.commons.lang.builder.ToStringBuilder;\n\nimport com.alibaba.otter.shared.common.utils.OtterToStringStyle;\n\n/**\n * zk集群监控对象\n * \n * @author jianghang 2012-9-21 下午01:54:17\n * @version 4.1.0\n */\npublic class AutoKeeperClusterDO {\n\n    private Long   id;\n    private String clusterName;\n    private String serverList; // 机器列表\n    private String description; // 描述\n    private Date   gmtCreate;\n    private Date   gmtModified;\n\n    public Long getId() {\n        return id;\n    }\n\n    public void setId(Long id) {\n        this.id = id;\n    }\n\n    public String getClusterName() {\n        return clusterName;\n    }\n\n    public void setClusterName(String clusterName) {\n        this.clusterName = clusterName;\n    }\n\n    public String getServerList() {\n        return serverList;\n    }\n\n    public void setServerList(String serverList) {\n        this.serverList = serverList;\n    }\n\n    public String getDescription() {\n        return description;\n    }\n\n    public void setDescription(String description) {\n        this.description = description;\n    }\n\n    public Date getGmtCreate() {\n        return gmtCreate;\n    }\n\n    public void setGmtCreate(Date gmtCreate) {\n        this.gmtCreate = gmtCreate;\n    }\n\n    public Date getGmtModified() {\n        return gmtModified;\n    }\n\n    public void setGmtModified(Date gmtModified) {\n        this.gmtModified = gmtModified;\n    }\n\n    public String toString() {\n        return ToStringBuilder.reflectionToString(this, OtterToStringStyle.DEFAULT_STYLE);\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/autokeeper/dal/ibatis/IbatisAutoKeeperClusterDAO.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.config.autokeeper.dal.ibatis;\n\nimport java.util.List;\n\nimport org.springframework.orm.ibatis.support.SqlMapClientDaoSupport;\n\nimport com.alibaba.otter.shared.common.utils.Assert;\nimport com.alibaba.otter.manager.biz.config.autokeeper.dal.AutoKeeperClusterDAO;\nimport com.alibaba.otter.manager.biz.config.autokeeper.dal.dataobject.AutoKeeperClusterDO;\n\n/**\n * 类IbatisAutoKeeperClusterDAO.java的实现描述：TODO 类实现描述\n * \n * @author simon 2012-9-24 下午5:17:17\n * @version 4.1.0\n */\npublic class IbatisAutoKeeperClusterDAO extends SqlMapClientDaoSupport implements AutoKeeperClusterDAO {\n\n    @Override\n    public AutoKeeperClusterDO findAutoKeeperClusterById(Long id) {\n        Assert.assertNotNull(id);\n        return (AutoKeeperClusterDO) getSqlMapClientTemplate().queryForObject(\"findAutoKeeperClusterById\", id);\n    }\n\n    @Override\n    public List<AutoKeeperClusterDO> listAutoKeeperClusters() {\n        List<AutoKeeperClusterDO> autoKeeperClusterDOs = getSqlMapClientTemplate().queryForList(\"listAutoKeeperClusters\");\n        return autoKeeperClusterDOs;\n    }\n\n    @Override\n    public void updateAutoKeeperCluster(AutoKeeperClusterDO autoKeeperClusterDo) {\n        Assert.assertNotNull(autoKeeperClusterDo);\n        getSqlMapClientTemplate().update(\"updateAutoKeeperCluster\", autoKeeperClusterDo);\n    }\n\n    @Override\n    public void insertAutoKeeperClusterDO(AutoKeeperClusterDO autoKeeperClusterDo) {\n        Assert.assertNotNull(autoKeeperClusterDo);\n        getSqlMapClientTemplate().insert(\"insertAutoKeeperCluster\", autoKeeperClusterDo);\n    }\n\n    @Override\n    public void delete(Long clusterId) {\n        Assert.assertNotNull(clusterId);\n        getSqlMapClientTemplate().insert(\"deleteAutoKeeperClusterById\", clusterId);\n    }\n\n    @Override\n    public Integer getCount() {\n        Integer count = (Integer) getSqlMapClientTemplate().queryForObject(\"getAutoKeeperClusterCount\");\n        return count.intValue();\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/autokeeper/impl/AutoKeeperClusterServiceImpl.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.config.autokeeper.impl;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport com.alibaba.fastjson.TypeReference;\nimport com.alibaba.otter.manager.biz.config.autokeeper.AutoKeeperClusterService;\nimport com.alibaba.otter.manager.biz.config.autokeeper.dal.AutoKeeperClusterDAO;\nimport com.alibaba.otter.manager.biz.config.autokeeper.dal.dataobject.AutoKeeperClusterDO;\nimport com.alibaba.otter.shared.common.model.autokeeper.AutoKeeperCluster;\nimport com.alibaba.otter.shared.common.utils.JsonUtils;\n\n/**\n * 类AutoKeeperClusterServiceImpl.java的实现描述：TODO 类实现描述\n * \n * @author simon 2012-9-24 下午5:44:43\n * @version 4.1.0\n */\npublic class AutoKeeperClusterServiceImpl implements AutoKeeperClusterService {\n\n    private AutoKeeperClusterDAO autoKeeperClusterDao;\n\n    @Override\n    public AutoKeeperCluster findAutoKeeperClusterById(Long id) {\n        AutoKeeperClusterDO autoKeeperClusterDO = autoKeeperClusterDao.findAutoKeeperClusterById(id);\n        return autoKeeperClusterDO == null ? null : doToModel(autoKeeperClusterDO);\n    }\n\n    @Override\n    public List<AutoKeeperCluster> listAutoKeeperClusters() {\n        return doToModel(autoKeeperClusterDao.listAutoKeeperClusters());\n    }\n\n    @Override\n    public void modifyAutoKeeperCluster(AutoKeeperCluster autoKeeperCluster) {\n        autoKeeperClusterDao.updateAutoKeeperCluster(modelToDo(autoKeeperCluster));\n    }\n\n    @Override\n    public void createAutoKeeperCluster(AutoKeeperCluster autoKeeperCluster) {\n        autoKeeperClusterDao.insertAutoKeeperClusterDO(modelToDo(autoKeeperCluster));\n    }\n\n    @Override\n    public void removeAutoKeeperCluster(Long id) {\n        autoKeeperClusterDao.delete(id);\n    }\n\n    private AutoKeeperCluster doToModel(AutoKeeperClusterDO autoKeeperClusterDo) {\n        AutoKeeperCluster autoKeeperCluster = new AutoKeeperCluster();\n        autoKeeperCluster.setId(autoKeeperClusterDo.getId());\n        autoKeeperCluster.setClusterName(autoKeeperClusterDo.getClusterName());\n        autoKeeperCluster.setDescription(autoKeeperClusterDo.getDescription());\n        autoKeeperCluster.setServerList(JsonUtils.unmarshalFromString(autoKeeperClusterDo.getServerList(),\n                                                                      new TypeReference<List<String>>() {\n                                                                      }));\n        autoKeeperCluster.setGmtCreate(autoKeeperClusterDo.getGmtCreate());\n        autoKeeperCluster.setGmtModified(autoKeeperClusterDo.getGmtModified());\n        return autoKeeperCluster;\n    }\n\n    public Integer getCount() {\n        return autoKeeperClusterDao.getCount();\n    }\n\n    private List<AutoKeeperCluster> doToModel(List<AutoKeeperClusterDO> autoKeeperClusterDos) {\n        List<AutoKeeperCluster> autoKeeperClusters = new ArrayList<AutoKeeperCluster>();\n        for (AutoKeeperClusterDO autoKeeperClusterDo : autoKeeperClusterDos) {\n            autoKeeperClusters.add(doToModel(autoKeeperClusterDo));\n        }\n        return autoKeeperClusters;\n    }\n\n    private AutoKeeperClusterDO modelToDo(AutoKeeperCluster autoKeeperCluster) {\n        AutoKeeperClusterDO autokeeperClusterDo = new AutoKeeperClusterDO();\n        autokeeperClusterDo.setId(autoKeeperCluster.getId());\n        autokeeperClusterDo.setClusterName(autoKeeperCluster.getClusterName());\n        autokeeperClusterDo.setDescription(autoKeeperCluster.getDescription());\n        autokeeperClusterDo.setServerList(JsonUtils.marshalToString(autoKeeperCluster.getServerList()));\n        autokeeperClusterDo.setGmtCreate(autoKeeperCluster.getGmtCreate());\n        autokeeperClusterDo.setGmtModified(autoKeeperCluster.getGmtModified());\n        return autokeeperClusterDo;\n    }\n\n    public void setAutoKeeperClusterDao(AutoKeeperClusterDAO autoKeeperClusterDao) {\n        this.autoKeeperClusterDao = autoKeeperClusterDao;\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/canal/CanalService.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.config.canal;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport com.alibaba.otter.canal.instance.manager.model.Canal;\n\n/**\n * @author sarah.lij 2012-7-25 下午04:02:20\n */\npublic interface CanalService {\n\n    public void create(Canal canal);\n\n    public void remove(Long canalId);\n\n    public void modify(Canal canal);\n\n    public List<Canal> listByIds(Long... identities);\n\n    public List<Canal> listAll();\n\n    public Canal findById(Long canalId);\n\n    public Canal findByName(String name);\n\n    public int getCount(Map condition);\n\n    public List<Canal> listByCondition(Map condition);\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/canal/dal/CanalDAO.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.config.canal.dal;\n\nimport com.alibaba.otter.manager.biz.common.basedao.GenericDAO;\nimport com.alibaba.otter.manager.biz.config.canal.dal.dataobject.CanalDO;\n\n/**\n * @author sarah.lij 2012-7-25 下午05:05:37\n */\npublic interface CanalDAO extends GenericDAO<CanalDO> {\n\n    public CanalDO findByName(String name);\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/canal/dal/dataobject/CanalDO.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.config.canal.dal.dataobject;\n\nimport java.io.Serializable;\nimport java.util.Date;\n\nimport com.alibaba.otter.canal.instance.manager.model.CanalParameter;\nimport com.alibaba.otter.canal.instance.manager.model.CanalStatus;\n\n/**\n * 类CanalDO.java的实现描述：TODO 类实现描述\n * \n * @author sarah.lij 2012-7-25 下午05:11:18\n */\npublic class CanalDO implements Serializable {\n\n    private static final long serialVersionUID = 9148286590254926037L;\n\n    private Long              id;                                     // 唯一标示id\n    private String            name;                                   // canal名字\n    private String            description;                            // 描述\n    private CanalStatus       status;\n    private CanalParameter    parameters;                             // 参数定义\n    private Date              gmtCreate;                              // 创建时间\n    private Date              gmtModified;                            // 修改时间\n\n    public Long getId() {\n        return id;\n    }\n\n    public void setId(Long id) {\n        this.id = id;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public void setName(String name) {\n        this.name = name;\n    }\n\n    public String getDescription() {\n        return description;\n    }\n\n    public void setDescription(String description) {\n        this.description = description;\n    }\n\n    public CanalStatus getStatus() {\n        return status;\n    }\n\n    public void setStatus(CanalStatus status) {\n        this.status = status;\n    }\n\n    public CanalParameter getParameters() {\n        return parameters;\n    }\n\n    public void setParameters(CanalParameter parameters) {\n        this.parameters = parameters;\n    }\n\n    public Date getGmtCreate() {\n        return gmtCreate;\n    }\n\n    public void setGmtCreate(Date gmtCreate) {\n        this.gmtCreate = gmtCreate;\n    }\n\n    public Date getGmtModified() {\n        return gmtModified;\n    }\n\n    public void setGmtModified(Date gmtModified) {\n        this.gmtModified = gmtModified;\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/canal/dal/ibatis/CanalParameterTypeHandler.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.config.canal.dal.ibatis;\n\nimport java.sql.SQLException;\n\nimport com.alibaba.otter.canal.instance.manager.model.CanalParameter;\nimport com.alibaba.otter.shared.common.utils.JsonUtils;\nimport com.ibatis.sqlmap.client.extensions.ParameterSetter;\nimport com.ibatis.sqlmap.client.extensions.ResultGetter;\nimport com.ibatis.sqlmap.client.extensions.TypeHandlerCallback;\n\n/**\n * @author sarah.lij 2012-7-25 下午05:12:52\n */\npublic class CanalParameterTypeHandler implements TypeHandlerCallback {\n\n    @Override\n    public void setParameter(ParameterSetter setter, Object parameter) throws SQLException {\n        setter.setString(JsonUtils.marshalToString(parameter));\n    }\n\n    @Override\n    public Object getResult(ResultGetter getter) throws SQLException {\n        return JsonUtils.unmarshalFromString(getter.getString(), CanalParameter.class);\n    }\n\n    public Object valueOf(String s) {\n        return JsonUtils.unmarshalFromString(s, CanalParameter.class);\n    }\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/canal/dal/ibatis/IbatisCanalDAO.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.config.canal.dal.ibatis;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.springframework.orm.ibatis.support.SqlMapClientDaoSupport;\n\nimport com.alibaba.otter.shared.common.utils.Assert;\nimport com.alibaba.otter.manager.biz.config.canal.dal.CanalDAO;\nimport com.alibaba.otter.manager.biz.config.canal.dal.dataobject.CanalDO;\n\n/**\n * @author sarah.lij 2012-7-25 下午05:12:29\n */\npublic class IbatisCanalDAO extends SqlMapClientDaoSupport implements CanalDAO {\n\n    public CanalDO insert(CanalDO canal) {\n        Assert.assertNotNull(canal);\n        getSqlMapClientTemplate().insert(\"insertCanal\", canal);\n        return canal;\n    }\n\n    public void delete(Long canalId) {\n        Assert.assertNotNull(canalId);\n        getSqlMapClientTemplate().delete(\"deleteCanalById\", canalId);\n    }\n\n    public void update(CanalDO canal) {\n        Assert.assertNotNull(canal);\n        getSqlMapClientTemplate().update(\"updateCanal\", canal);\n    }\n\n    public List<CanalDO> listAll() {\n        return (List<CanalDO>) getSqlMapClientTemplate().queryForList(\"listCanals\");\n    }\n\n    public List<CanalDO> listByMultiId(Long... identities) {\n        List<CanalDO> canalDos = getSqlMapClientTemplate().queryForList(\"listCanalByIds\", identities);\n        return canalDos;\n    }\n\n    public boolean checkUnique(CanalDO canal) {\n        int count = (Integer) getSqlMapClientTemplate().queryForObject(\"checkCanalUnique\", canal);\n        return count == 0 ? true : false;\n    }\n\n    public CanalDO findByName(String name) {\n        Assert.assertNotNull(name);\n        return (CanalDO) getSqlMapClientTemplate().queryForObject(\"findCanalByName\", name);\n    }\n\n    public CanalDO findById(Long identity) {\n        throw new UnsupportedOperationException();\n    }\n\n    public int getCount() {\n        return 0;\n    }\n\n    public int getCount(Map condition) {\n        Integer count = (Integer) getSqlMapClientTemplate().queryForObject(\"getCanalCount\", condition);\n        return count.intValue();\n    }\n\n    public List<CanalDO> listByCondition(Map condition) {\n        List<CanalDO> canalDos = getSqlMapClientTemplate().queryForList(\"listCanals\", condition);\n        return canalDos;\n    }\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/canal/impl/CanalServiceImpl.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.config.canal.impl;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.apache.commons.lang.StringUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.transaction.TransactionStatus;\nimport org.springframework.transaction.support.TransactionCallbackWithoutResult;\nimport org.springframework.transaction.support.TransactionTemplate;\n\nimport com.alibaba.otter.canal.instance.manager.model.Canal;\nimport com.alibaba.otter.canal.instance.manager.model.CanalParameter;\nimport com.alibaba.otter.manager.biz.common.exceptions.ManagerException;\nimport com.alibaba.otter.manager.biz.common.exceptions.RepeatConfigureException;\nimport com.alibaba.otter.manager.biz.config.autokeeper.AutoKeeperClusterService;\nimport com.alibaba.otter.manager.biz.config.canal.CanalService;\nimport com.alibaba.otter.manager.biz.config.canal.dal.CanalDAO;\nimport com.alibaba.otter.manager.biz.config.canal.dal.dataobject.CanalDO;\nimport com.alibaba.otter.manager.biz.config.node.impl.NodeServiceImpl;\nimport com.alibaba.otter.shared.arbitrate.ArbitrateViewService;\nimport com.alibaba.otter.shared.common.model.autokeeper.AutoKeeperCluster;\nimport com.alibaba.otter.shared.common.utils.Assert;\nimport com.alibaba.otter.shared.common.utils.JsonUtils;\n\n/**\n * @author sarah.lij 2012-7-25 下午04:04:43\n */\npublic class CanalServiceImpl implements CanalService {\n\n    private static final Logger      logger = LoggerFactory.getLogger(NodeServiceImpl.class);\n\n    private CanalDAO                 canalDao;\n    private TransactionTemplate      transactionTemplate;\n    private AutoKeeperClusterService autoKeeperClusterService;\n    private ArbitrateViewService     arbitrateViewService;\n\n    /**\n     * 添加\n     */\n    public void create(final Canal canal) {\n        Assert.assertNotNull(canal);\n        transactionTemplate.execute(new TransactionCallbackWithoutResult() {\n\n            protected void doInTransactionWithoutResult(TransactionStatus status) {\n\n                try {\n                    CanalDO canalDO = modelToDo(canal);\n                    canalDO.setId(0L);\n                    if (!canalDao.checkUnique(canalDO)) {\n                        String exceptionCause = \"exist the same repeat canal in the database.\";\n                        logger.warn(\"WARN ## \" + exceptionCause);\n                        throw new RepeatConfigureException(exceptionCause);\n                    }\n                    canalDao.insert(canalDO);\n                    canal.setId(canalDO.getId());\n                } catch (RepeatConfigureException rce) {\n                    throw rce;\n                } catch (Exception e) {\n                    logger.error(\"ERROR ## create canal has an exception!\");\n                    throw new ManagerException(e);\n                }\n            }\n        });\n    }\n\n    /**\n     * 删除\n     */\n    public void remove(final Long canalId) {\n        Assert.assertNotNull(canalId);\n        transactionTemplate.execute(new TransactionCallbackWithoutResult() {\n\n            protected void doInTransactionWithoutResult(TransactionStatus status) {\n\n                try {\n                    Canal canal = findById(canalId);\n                    canalDao.delete(canalId);\n                    arbitrateViewService.removeCanal(canal.getName()); // 删除canal节点信息\n                } catch (Exception e) {\n                    logger.error(\"ERROR ## remove canal(\" + canalId + \") has an exception!\");\n                    throw new ManagerException(e);\n                }\n            }\n        });\n\n    }\n\n    /**\n     * 修改\n     */\n    public void modify(final Canal canal) {\n        Assert.assertNotNull(canal);\n        transactionTemplate.execute(new TransactionCallbackWithoutResult() {\n\n            protected void doInTransactionWithoutResult(TransactionStatus status) {\n\n                try {\n                    CanalDO canalDo = modelToDo(canal);\n                    if (canalDao.checkUnique(canalDo)) {\n                        canalDao.update(canalDo);\n                    } else {\n                        String exceptionCause = \"exist the same repeat canal in the database.\";\n                        logger.warn(\"WARN ## \" + exceptionCause);\n                        throw new RepeatConfigureException(exceptionCause);\n                    }\n                } catch (RepeatConfigureException rce) {\n                    throw rce;\n                } catch (Exception e) {\n                    logger.error(\"ERROR ## modify canal(\" + canal.getId() + \") has an exception!\");\n                    throw new ManagerException(e);\n                }\n            }\n        });\n\n    }\n\n    public List<Canal> listByIds(Long... identities) {\n\n        List<Canal> canals = new ArrayList<Canal>();\n        try {\n            List<CanalDO> canalDos = null;\n            if (identities.length < 1) {\n                canalDos = canalDao.listAll();\n                if (canalDos.isEmpty()) {\n                    logger.debug(\"DEBUG ## couldn't query any canal, maybe hasn't create any canal.\");\n                    return canals;\n                }\n            } else {\n                canalDos = canalDao.listByMultiId(identities);\n                if (canalDos.isEmpty()) {\n                    String exceptionCause = \"couldn't query any canal by canalIds:\" + Arrays.toString(identities);\n                    logger.error(\"ERROR ## \" + exceptionCause);\n                    throw new ManagerException(exceptionCause);\n                }\n            }\n            canals = doToModel(canalDos);\n        } catch (Exception e) {\n            logger.error(\"ERROR ## query channels has an exception!\");\n            throw new ManagerException(e);\n        }\n\n        return canals;\n    }\n\n    public List<Canal> listAll() {\n        return listByIds();\n    }\n\n    public Canal findById(Long canalId) {\n        Assert.assertNotNull(canalId);\n        List<Canal> canals = listByIds(canalId);\n        if (canals.size() != 1) {\n            String exceptionCause = \"query canalId:\" + canalId + \" return null.\";\n            logger.error(\"ERROR ## \" + exceptionCause);\n            throw new ManagerException(exceptionCause);\n        }\n\n        return canals.get(0);\n    }\n\n    public Canal findByName(String name) {\n        Assert.assertNotNull(name);\n        CanalDO canalDo = canalDao.findByName(name);\n        if (canalDo == null) {\n            String exceptionCause = \"query name:\" + name + \" return null.\";\n            logger.error(\"ERROR ## \" + exceptionCause);\n            throw new ManagerException(exceptionCause);\n        }\n\n        return doToModel(canalDo);\n    }\n\n    @Override\n    public int getCount(Map condition) {\n        return canalDao.getCount(condition);\n    }\n\n    @Override\n    public List<Canal> listByCondition(Map condition) {\n        List<CanalDO> canalDos = canalDao.listByCondition(condition);\n        if (canalDos.isEmpty()) {\n            logger.debug(\"DEBUG ## couldn't query any canal by the condition:\" + JsonUtils.marshalToString(condition));\n            return new ArrayList<Canal>();\n        }\n\n        return doToModel(canalDos);\n    }\n\n    /**\n     * 用于Model对象转化为DO对象\n     * \n     * @param canal\n     * @return CanalDO\n     */\n    private CanalDO modelToDo(Canal canal) {\n        CanalDO canalDo = new CanalDO();\n        try {\n            canalDo.setId(canal.getId());\n            canalDo.setName(canal.getName());\n            canalDo.setStatus(canal.getStatus());\n            canalDo.setDescription(canal.getDesc());\n            canalDo.setParameters(canal.getCanalParameter());\n            canalDo.setGmtCreate(canal.getGmtCreate());\n            canalDo.setGmtModified(canal.getGmtModified());\n        } catch (Exception e) {\n            logger.error(\"ERROR ## change the canal Model to Do has an exception\");\n            throw new ManagerException(e);\n        }\n        return canalDo;\n    }\n\n    /**\n     * 用于DO对象转化为Model对象\n     * \n     * @param canalDo\n     * @return Canal\n     */\n    private Canal doToModel(CanalDO canalDo) {\n        Canal canal = new Canal();\n        try {\n            canal.setId(canalDo.getId());\n            canal.setName(canalDo.getName());\n            canal.setStatus(canalDo.getStatus());\n            canal.setDesc(canalDo.getDescription());\n            CanalParameter parameter = canalDo.getParameters();\n            AutoKeeperCluster zkCluster = autoKeeperClusterService.findAutoKeeperClusterById(parameter.getZkClusterId());\n            if (zkCluster != null) {\n                parameter.setZkClusters(Arrays.asList(StringUtils.join(zkCluster.getServerList(), ',')));\n            }\n            canal.setCanalParameter(canalDo.getParameters());\n            canal.setGmtCreate(canalDo.getGmtCreate());\n            canal.setGmtModified(canalDo.getGmtModified());\n        } catch (Exception e) {\n            logger.error(\"ERROR ## change the canal Do to Model has an exception\");\n            throw new ManagerException(e);\n        }\n\n        return canal;\n    }\n\n    private List<Canal> doToModel(List<CanalDO> canalDos) {\n        List<Canal> canals = new ArrayList<Canal>();\n        for (CanalDO canalDo : canalDos) {\n            canals.add(doToModel(canalDo));\n        }\n        return canals;\n    }\n\n    /* ------------------------setter / getter--------------------------- */\n\n    public void setCanalDao(CanalDAO canalDao) {\n        this.canalDao = canalDao;\n    }\n\n    public void setTransactionTemplate(TransactionTemplate transactionTemplate) {\n        this.transactionTemplate = transactionTemplate;\n    }\n\n    public void setAutoKeeperClusterService(AutoKeeperClusterService autoKeeperClusterService) {\n        this.autoKeeperClusterService = autoKeeperClusterService;\n    }\n\n    public void setArbitrateViewService(ArbitrateViewService arbitrateViewService) {\n        this.arbitrateViewService = arbitrateViewService;\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/channel/ChannelService.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.config.channel;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport com.alibaba.otter.manager.biz.common.baseservice.GenericService;\nimport com.alibaba.otter.shared.common.model.config.channel.Channel;\nimport com.alibaba.otter.shared.common.model.config.channel.ChannelStatus;\n\n/**\n * @author simon\n */\npublic interface ChannelService extends GenericService<Channel> {\n\n    public Channel findByPipelineId(Long pipelineId);\n\n    public Channel findByIdWithoutColumn(Long pipelineId);\n\n    public List<Channel> listByPipelineIds(Long... pipelineIds);\n\n    public List<Channel> listByNodeId(Long nodeId);\n\n    public List<Channel> listOnlyChannels(Long... identities);\n\n    public List<Long> listAllChannelId();\n\n    public List<Channel> listByNodeId(Long nodeId, ChannelStatus... status);\n\n    public List<Channel> listByConditionWithoutColumn(Map condition);\n\n    public void stopChannel(Long channelId);\n\n    public void notifyChannel(Long channelId);\n\n    public void startChannel(Long channelId);\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/channel/dal/ChannelDAO.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.config.channel.dal;\n\nimport java.util.List;\n\nimport com.alibaba.otter.manager.biz.common.basedao.GenericDAO;\nimport com.alibaba.otter.manager.biz.config.channel.dal.dataobject.ChannelDO;\n\n/**\n * @author simon\n */\npublic interface ChannelDAO extends GenericDAO<ChannelDO> {\n\n    public List<ChannelDO> listChannelPks();\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/channel/dal/dataobject/ChannelDO.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.config.channel.dal.dataobject;\n\nimport java.io.Serializable;\nimport java.util.Date;\n\nimport com.alibaba.otter.shared.common.model.config.channel.ChannelParameter;\nimport com.alibaba.otter.shared.common.model.config.channel.ChannelStatus;\n\n/**\n * @author simon\n */\npublic class ChannelDO implements Serializable {\n\n    private static final long serialVersionUID = 3708730560311969117L;\n    private Long              id;                                     // 唯一标示id\n    private String            name;                                   // channel命名\n    private ChannelStatus     status;                                 // 运行状态\n    private String            description;                            // 描述信息\n    private ChannelParameter  parameters;                             // 配置参数\n    private Date              gmtCreate;\n    private Date              gmtModified;\n\n    public Long getId() {\n        return id;\n    }\n\n    public void setId(Long id) {\n        this.id = id;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public void setName(String name) {\n        this.name = name;\n    }\n\n    public ChannelStatus getStatus() {\n        return status;\n    }\n\n    public void setStatus(ChannelStatus status) {\n        this.status = status;\n    }\n\n    public String getDescription() {\n        return description;\n    }\n\n    public void setDescription(String description) {\n        this.description = description;\n    }\n\n    public ChannelParameter getParameters() {\n        return parameters;\n    }\n\n    public void setParameters(ChannelParameter parameters) {\n        this.parameters = parameters;\n    }\n\n    public Date getGmtCreate() {\n        return gmtCreate;\n    }\n\n    public void setGmtCreate(Date gmtCreate) {\n        this.gmtCreate = gmtCreate;\n    }\n\n    public Date getGmtModified() {\n        return gmtModified;\n    }\n\n    public void setGmtModified(Date gmtModified) {\n        this.gmtModified = gmtModified;\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/channel/dal/ibatis/ChannelParameterTypeHandler.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.config.channel.dal.ibatis;\n\nimport java.sql.SQLException;\n\nimport com.alibaba.otter.shared.common.model.config.channel.ChannelParameter;\nimport com.alibaba.otter.shared.common.utils.JsonUtils;\nimport com.ibatis.sqlmap.client.extensions.ParameterSetter;\nimport com.ibatis.sqlmap.client.extensions.ResultGetter;\nimport com.ibatis.sqlmap.client.extensions.TypeHandlerCallback;\n\n/**\n * 用于ChannelParameter的解析\n * \n * @author simon\n */\npublic class ChannelParameterTypeHandler implements TypeHandlerCallback {\n\n    @Override\n    public void setParameter(ParameterSetter setter, Object parameter) throws SQLException {\n        setter.setString(JsonUtils.marshalToString(parameter));\n    }\n\n    @Override\n    public Object getResult(ResultGetter getter) throws SQLException {\n        return JsonUtils.unmarshalFromString(getter.getString(), ChannelParameter.class);\n    }\n\n    public Object valueOf(String s) {\n        return JsonUtils.unmarshalFromString(s, ChannelParameter.class);\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/channel/dal/ibatis/IbatisChannelDAO.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.config.channel.dal.ibatis;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.springframework.orm.ibatis.support.SqlMapClientDaoSupport;\n\nimport com.alibaba.otter.shared.common.utils.Assert;\nimport com.alibaba.otter.manager.biz.config.channel.dal.ChannelDAO;\nimport com.alibaba.otter.manager.biz.config.channel.dal.dataobject.ChannelDO;\n\n/**\n * Channel的DAO层，ibatis的实现，主要是CRUD操作。\n * \n * @author simon\n */\npublic class IbatisChannelDAO extends SqlMapClientDaoSupport implements ChannelDAO {\n\n    public ChannelDO insert(ChannelDO entityObj) {\n        Assert.assertNotNull(entityObj);\n        getSqlMapClientTemplate().insert(\"insertChannel\", entityObj);\n        return entityObj;\n    }\n\n    public void delete(Long identity) {\n        Assert.assertNotNull(identity);\n        getSqlMapClientTemplate().delete(\"deleteChannelById\", identity);\n    }\n\n    public void update(ChannelDO entityObj) {\n        Assert.assertNotNull(entityObj);\n        getSqlMapClientTemplate().update(\"updateChannel\", entityObj);\n    }\n\n    public boolean checkUnique(ChannelDO entityObj) {\n        Assert.assertNotNull(entityObj);\n        int count = (Integer) getSqlMapClientTemplate().queryForObject(\"checkChannelUnique\", entityObj);\n        return count == 0 ? true : false;\n    }\n\n    public List<ChannelDO> listAll() {\n        List<ChannelDO> channels = getSqlMapClientTemplate().queryForList(\"listChannels\");\n        return channels;\n    }\n\n    public List<ChannelDO> listChannelPks() {\n        List<ChannelDO> channels = getSqlMapClientTemplate().queryForList(\"listChannelPks\");\n        return channels;\n    }\n\n    public List<ChannelDO> listByCondition(Map condition) {\n\n        List<ChannelDO> channelDos = getSqlMapClientTemplate().queryForList(\"listChannels\", condition);\n        return channelDos;\n    }\n\n    public int getCount() {\n        Integer count = (Integer) getSqlMapClientTemplate().queryForObject(\"getChannelCount\");\n        return count.intValue();\n    }\n\n    public int getCount(Map condition) {\n        Integer count = (Integer) getSqlMapClientTemplate().queryForObject(\"getChannelCount\", condition);\n        return count.intValue();\n    }\n\n    public List<ChannelDO> listByMultiId(Long... identities) {\n        List<ChannelDO> channelDos = getSqlMapClientTemplate().queryForList(\"listChannelByIds\", identities);\n        return channelDos;\n    }\n\n    public ChannelDO findById(Long identity) {\n        Assert.assertNotNull(identity);\n        return (ChannelDO) getSqlMapClientTemplate().queryForObject(\"findChannelById\", identity);\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/channel/impl/ChannelServiceImpl.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.config.channel.impl;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.transaction.TransactionStatus;\nimport org.springframework.transaction.support.TransactionCallbackWithoutResult;\nimport org.springframework.transaction.support.TransactionTemplate;\n\nimport com.alibaba.otter.manager.biz.common.exceptions.InvalidConfigureException;\nimport com.alibaba.otter.manager.biz.common.exceptions.InvalidConfigureException.INVALID_TYPE;\nimport com.alibaba.otter.manager.biz.common.exceptions.ManagerException;\nimport com.alibaba.otter.manager.biz.common.exceptions.RepeatConfigureException;\nimport com.alibaba.otter.manager.biz.config.channel.ChannelService;\nimport com.alibaba.otter.manager.biz.config.channel.dal.ChannelDAO;\nimport com.alibaba.otter.manager.biz.config.channel.dal.dataobject.ChannelDO;\nimport com.alibaba.otter.manager.biz.config.parameter.SystemParameterService;\nimport com.alibaba.otter.manager.biz.config.pipeline.PipelineService;\nimport com.alibaba.otter.manager.biz.remote.ConfigRemoteService;\nimport com.alibaba.otter.shared.arbitrate.ArbitrateManageService;\nimport com.alibaba.otter.shared.common.model.config.channel.Channel;\nimport com.alibaba.otter.shared.common.model.config.channel.ChannelStatus;\nimport com.alibaba.otter.shared.common.model.config.parameter.SystemParameter;\nimport com.alibaba.otter.shared.common.model.config.pipeline.Pipeline;\nimport com.alibaba.otter.shared.common.model.config.pipeline.PipelineParameter;\nimport com.alibaba.otter.shared.common.utils.Assert;\nimport com.alibaba.otter.shared.common.utils.JsonUtils;\n\n/**\n * 主要提供增加、删除、修改、列表功能； 提供开启和停止channel方法，需要调用仲裁器方法\n * \n * @author simon\n */\npublic class ChannelServiceImpl implements ChannelService {\n\n    private static final Logger    logger = LoggerFactory.getLogger(ChannelServiceImpl.class);\n\n    private SystemParameterService systemParameterService;\n    private ArbitrateManageService arbitrateManageService;\n    private TransactionTemplate    transactionTemplate;\n    private ConfigRemoteService    configRemoteService;\n    private PipelineService        pipelineService;\n    private ChannelDAO             channelDao;\n\n    /**\n     * 添加Channel\n     */\n    public void create(final Channel channel) {\n        Assert.assertNotNull(channel);\n\n        transactionTemplate.execute(new TransactionCallbackWithoutResult() {\n\n            @Override\n            protected void doInTransactionWithoutResult(TransactionStatus status) {\n                try {\n\n                    ChannelDO channelDo = modelToDo(channel);\n                    channelDo.setId(0L);\n\n                    if (!channelDao.checkUnique(channelDo)) {\n                        String exceptionCause = \"exist the same name channel in the database.\";\n                        logger.warn(\"WARN ## \" + exceptionCause);\n                        throw new RepeatConfigureException(exceptionCause);\n                    }\n                    channelDao.insert(channelDo);\n                    arbitrateManageService.channelEvent().init(channelDo.getId());\n\n                } catch (RepeatConfigureException rce) {\n                    throw rce;\n                } catch (Exception e) {\n                    logger.error(\"ERROR ## create channel has an exception \", e);\n                    throw new ManagerException(e);\n                }\n            }\n        });\n    }\n\n    /**\n     * 修改Channel\n     */\n    public void modify(Channel channel) {\n\n        Assert.assertNotNull(channel);\n\n        try {\n            ChannelDO channelDo = modelToDo(channel);\n            if (channelDao.checkUnique(channelDo)) {\n                channelDao.update(channelDo);\n            } else {\n                String exceptionCause = \"exist the same name channel in the database.\";\n                logger.warn(\"WARN ## \" + exceptionCause);\n                throw new RepeatConfigureException(exceptionCause);\n            }\n\n        } catch (RepeatConfigureException rce) {\n            throw rce;\n        } catch (Exception e) {\n            logger.error(\"ERROR ## modify channel has an exception \", e);\n            throw new ManagerException(e);\n        }\n\n    }\n\n    /**\n     * 删除Channel\n     */\n    public void remove(final Long channelId) {\n        Assert.assertNotNull(channelId);\n\n        transactionTemplate.execute(new TransactionCallbackWithoutResult() {\n\n            @Override\n            protected void doInTransactionWithoutResult(TransactionStatus status) {\n                try {\n                    arbitrateManageService.channelEvent().destory(channelId);\n                    channelDao.delete(channelId);\n                } catch (Exception e) {\n                    logger.error(\"ERROR ## remove channel has an exception \", e);\n                    throw new ManagerException(e);\n                }\n            }\n        });\n\n    }\n\n    /*--------------------优化内容：listAll、listByIds、findById合并-------------------------------*/\n\n    public List<Channel> listByIds(Long... identities) {\n\n        List<Channel> channels = new ArrayList<Channel>();\n        try {\n            List<ChannelDO> channelDos = null;\n            if (identities.length < 1) {\n                channelDos = channelDao.listAll();\n                if (channelDos.isEmpty()) {\n                    logger.debug(\"DEBUG ## couldn't query any channel, maybe hasn't create any channel.\");\n                    return channels;\n                }\n            } else {\n                channelDos = channelDao.listByMultiId(identities);\n                if (channelDos.isEmpty()) {\n                    String exceptionCause = \"couldn't query any channel by channelIds:\" + Arrays.toString(identities);\n                    logger.error(\"ERROR ## \" + exceptionCause);\n                    throw new ManagerException(exceptionCause);\n                }\n            }\n            channels = doToModel(channelDos);\n        } catch (Exception e) {\n            logger.error(\"ERROR ## query channels has an exception!\");\n            throw new ManagerException(e);\n        }\n\n        return channels;\n    }\n\n    /**\n     * 列出所有的Channel对象\n     */\n    public List<Channel> listAll() {\n        return listByIds();\n    }\n\n    public List<Channel> listOnlyChannels(Long... identities) {\n\n        List<Channel> channels = new ArrayList<Channel>();\n        try {\n            List<ChannelDO> channelDos = null;\n            if (identities.length < 1) {\n                channelDos = channelDao.listAll();\n                if (channelDos.isEmpty()) {\n                    logger.debug(\"DEBUG ## couldn't query any channel, maybe hasn't create any channel.\");\n                    return channels;\n                }\n            } else {\n                channelDos = channelDao.listByMultiId(identities);\n                if (channelDos.isEmpty()) {\n                    String exceptionCause = \"couldn't query any channel by channelIds:\" + Arrays.toString(identities);\n                    logger.error(\"ERROR ## \" + exceptionCause);\n                    throw new ManagerException(exceptionCause);\n                }\n            }\n            channels = doToModelOnlyChannels(channelDos);\n        } catch (Exception e) {\n            logger.error(\"ERROR ## query channels has an exception!\");\n            throw new ManagerException(e);\n        }\n\n        return channels;\n    }\n\n    public List<Channel> listByCondition(Map condition) {\n        List<ChannelDO> channelDos = channelDao.listByCondition(condition);\n        if (channelDos.isEmpty()) {\n            logger.debug(\"DEBUG ## couldn't query any channel by the condition:\" + JsonUtils.marshalToString(condition));\n            return new ArrayList<Channel>();\n        }\n        return doToModel(channelDos);\n    }\n\n    public List<Channel> listByConditionWithoutColumn(Map condition) {\n        List<ChannelDO> channelDos = channelDao.listByCondition(condition);\n        if (channelDos.isEmpty()) {\n            logger.debug(\"DEBUG ## couldn't query any channel by the condition:\" + JsonUtils.marshalToString(condition));\n            return new ArrayList<Channel>();\n        }\n        return doToModelWithColumn(channelDos);\n    }\n\n    public List<Long> listAllChannelId() {\n        List<ChannelDO> channelDos = channelDao.listChannelPks();\n        List<Long> channelPks = new ArrayList<Long>();\n        if (channelDos.isEmpty()) {\n            logger.debug(\"DEBUG ## couldn't query any channel\");\n        }\n        for (ChannelDO channelDo : channelDos) {\n            channelPks.add(channelDo.getId());\n        }\n        return channelPks;\n    }\n\n    /**\n     * <pre>\n     * 通过ChannelId找到对应的Channel对象\n     * 并且根据ChannelId找到对应的所有Pipeline。\n     * </pre>\n     */\n    public Channel findById(Long channelId) {\n        Assert.assertNotNull(channelId);\n        List<Channel> channels = listByIds(channelId);\n        if (channels.size() != 1) {\n            String exceptionCause = \"query channelId:\" + channelId + \" return null.\";\n            logger.error(\"ERROR ## \" + exceptionCause);\n            throw new ManagerException(exceptionCause);\n        }\n        return channels.get(0);\n    }\n\n    public Channel findByIdWithoutColumn(Long channelId) {\n        List<ChannelDO> channelDos = channelDao.listByMultiId(channelId);\n        if (channelDos.size() != 1) {\n            String exceptionCause = \"query channelId:\" + channelId + \" return null.\";\n            logger.error(\"ERROR ## \" + exceptionCause);\n            throw new ManagerException(exceptionCause);\n        }\n\n        List<Channel> channels = doToModelWithColumn(channelDos);\n        return channels.get(0);\n    }\n\n    /*--------------------外部关联查询Channel-----------------------*/\n\n    /**\n     * <pre>\n     * 根据PipelineID找到对应的Channel\n     * 优化设想：\n     *    应该通过变长参数达到后期扩展的方便性\n     * </pre>\n     */\n    public Channel findByPipelineId(Long pipelineId) {\n        Pipeline pipeline = pipelineService.findById(pipelineId);\n        Channel channel = findById(pipeline.getChannelId());\n        return channel;\n    }\n\n    /**\n     * <pre>\n     * 根据PipelineID找到对应的Channel\n     * 优化设想：\n     *    应该通过变长参数达到后期扩展的方便性\n     * </pre>\n     */\n    public List<Channel> listByPipelineIds(Long... pipelineIds) {\n        List<Channel> channels = new ArrayList<Channel>();\n        try {\n            List<Pipeline> pipelines = pipelineService.listByIds(pipelineIds);\n\n            List<Long> channelIds = new ArrayList<Long>();\n\n            for (Pipeline pipeline : pipelines) {\n                if (!channelIds.contains(pipeline.getChannelId())) {\n                    channelIds.add(pipeline.getChannelId());\n                }\n            }\n            channels = listByIds(channelIds.toArray(new Long[channelIds.size()]));\n        } catch (Exception e) {\n            logger.error(\"ERROR ## list query channel by pipelineIds:\" + pipelineIds.toString() + \" has an exception!\");\n            throw new ManagerException(e);\n        }\n        return channels;\n    }\n\n    /**\n     * pipelineService 根据NodeId找到对应已启动的Channel列表。\n     */\n    public List<Channel> listByNodeId(Long nodeId) {\n        return listByNodeId(nodeId, new ChannelStatus[] {});\n    }\n\n    /**\n     * 根据NodeId和Channel状态找到对应的Channel列表。\n     */\n    public List<Channel> listByNodeId(Long nodeId, ChannelStatus... statuses) {\n        List<Channel> channels = new ArrayList<Channel>();\n        List<Channel> results = new ArrayList<Channel>();\n        try {\n            List<Pipeline> pipelines = pipelineService.listByNodeId(nodeId);\n            List<Long> pipelineIds = new ArrayList<Long>();\n            for (Pipeline pipeline : pipelines) {\n                pipelineIds.add(pipeline.getId());\n            }\n\n            if (pipelineIds.isEmpty()) { // 没有关联任务直接返回\n                return channels;\n            }\n\n            // 反查对应的channel\n            channels = listByPipelineIds(pipelineIds.toArray(new Long[pipelineIds.size()]));\n            if (null == statuses || statuses.length == 0) {\n                return channels;\n            }\n\n            for (Channel channel : channels) {\n                for (ChannelStatus status : statuses) {\n                    if (channel.getStatus().equals(status)) {\n                        results.add(channel);\n                    }\n                }\n            }\n        } catch (Exception e) {\n            logger.error(\"ERROR ## list query channel by nodeId:\" + nodeId + \" has an exception!\");\n            throw new ManagerException(e);\n        }\n        return results;\n    }\n\n    /**\n     * 拿到channel总数进行分页\n     */\n    public int getCount() {\n        return channelDao.getCount();\n    }\n\n    public int getCount(Map condition) {\n        return channelDao.getCount(condition);\n    }\n\n    /*----------------------Start/Stop Channel 短期优化：增加异常和条件判断--------------------------*/\n    /**\n     * <pre>\n     * 切换Channel状态\n     *      1.首先判断Channel是否为空或状态位是否正确\n     *      2.通知总裁器，更新节点\n     *      3.数据库数据库更新状态\n     *      4.调用远程方法，推送Channel到node节点\n     * </pre>\n     */\n    private void switchChannelStatus(final Long channelId, final ChannelStatus channelStatus) {\n        transactionTemplate.execute(new TransactionCallbackWithoutResult() {\n\n            @Override\n            protected void doInTransactionWithoutResult(TransactionStatus status) {\n                try {\n                    final ChannelDO channelDo = channelDao.findById(channelId);\n\n                    if (null == channelDo) {\n                        String exceptionCause = \"query channelId:\" + channelId + \" return null.\";\n                        logger.error(\"ERROR ## \" + exceptionCause);\n                        throw new ManagerException(exceptionCause);\n                    }\n\n                    ChannelStatus oldStatus = arbitrateManageService.channelEvent().status(channelDo.getId());\n                    Channel channel = doToModel(channelDo);\n                    // 检查下ddl/home配置\n                    List<Pipeline> pipelines = channel.getPipelines();\n                    if (pipelines.size() > 1) {\n                        boolean ddlSync = true;\n                        boolean homeSync = true;\n                        for (Pipeline pipeline : pipelines) {\n                            homeSync &= pipeline.getParameters().isHome();\n                            ddlSync &= pipeline.getParameters().getDdlSync();\n                        }\n\n                        if (ddlSync) {\n                            throw new InvalidConfigureException(INVALID_TYPE.DDL);\n                        }\n\n                        if (homeSync) {\n                            throw new InvalidConfigureException(INVALID_TYPE.HOME);\n                        }\n                    }\n\n                    channel.setStatus(oldStatus);\n                    ChannelStatus newStatus = channelStatus;\n                    if (newStatus != null) {\n                        if (newStatus.equals(oldStatus)) {\n                            // String exceptionCause = \"switch the channel(\" +\n                            // channelId + \") status to \" +\n                            // channelStatus\n                            // + \" but it had the status:\" + oldStatus;\n                            // logger.error(\"ERROR ## \" + exceptionCause);\n                            // throw new ManagerException(exceptionCause);\n                            // ignored\n                            return;\n                        } else {\n                            channel.setStatus(newStatus);// 强制修改为当前变更状态\n                        }\n                    } else {\n                        newStatus = oldStatus;\n                    }\n\n                    // 针对关闭操作，要优先更改对应的status，避免node工作线程继续往下跑\n                    if (newStatus.isStop()) {\n                        arbitrateManageService.channelEvent().stop(channelId);\n                    } else if (newStatus.isPause()) {\n                        arbitrateManageService.channelEvent().pause(channelId);\n                    }\n\n                    // 通知变更内容\n                    boolean result = configRemoteService.notifyChannel(channel);// 客户端响应成功，才更改对应的状态\n\n                    if (result) {\n                        // 针对启动的话，需要先通知到客户端，客户端启动线程后，再更改channel状态\n                        if (newStatus.isStart()) {\n                            arbitrateManageService.channelEvent().start(channelId);\n                        }\n                    }\n\n                } catch (Exception e) {\n                    logger.error(\"ERROR ## switch the channel(\" + channelId + \") status has an exception.\");\n                    throw new ManagerException(e);\n                }\n            }\n        });\n\n    }\n\n    public void stopChannel(Long channelId) {\n        switchChannelStatus(channelId, ChannelStatus.STOP);\n    }\n\n    public void startChannel(Long channelId) {\n        switchChannelStatus(channelId, ChannelStatus.START);\n    }\n\n    public void notifyChannel(Long channelId) {\n        switchChannelStatus(channelId, null);\n    }\n\n    /*----------------------DO <-> MODEL 组装方法--------------------------*/\n    /**\n     * <pre>\n     * 用于Model对象转化为DO对象\n     * 优化：\n     *      无SQL交互，只是简单进行字段组装，暂时无须优化\n     * </pre>\n     * \n     * @param channel\n     * @return ChannelDO\n     */\n    private ChannelDO modelToDo(Channel channel) {\n\n        ChannelDO channelDO = new ChannelDO();\n        try {\n            channelDO.setId(channel.getId());\n            channelDO.setName(channel.getName());\n            channelDO.setDescription(channel.getDescription());\n            channelDO.setStatus(channel.getStatus());\n            channelDO.setParameters(channel.getParameters());\n            channelDO.setGmtCreate(channel.getGmtCreate());\n            channelDO.setGmtModified(channel.getGmtModified());\n        } catch (Exception e) {\n            logger.error(\"ERROR ## change the channel Model to Do has an exception\");\n            throw new ManagerException(e);\n        }\n        return channelDO;\n    }\n\n    /**\n     * <pre>\n     * 用于DO对象转化为Model对象\n     * 现阶段优化：\n     *      需要五次SQL交互:pipeline\\node\\dataMediaPair\\dataMedia\\dataMediaSource（五个层面）\n     *      目前优化方案为单层只执行一次SQL，避免重复循环造成IO及数据库查询开销\n     * 长期优化：\n     *      对SQL进行改造，尽量减小SQL调用次数\n     * </pre>\n     * \n     * @param channelDO\n     * @return Channel\n     */\n\n    private Channel doToModel(ChannelDO channelDo) {\n        Channel channel = new Channel();\n        try {\n            channel.setId(channelDo.getId());\n            channel.setName(channelDo.getName());\n            channel.setDescription(channelDo.getDescription());\n            channel.setStatus(arbitrateManageService.channelEvent().status(channelDo.getId()));\n            channel.setParameters(channelDo.getParameters());\n            channel.setGmtCreate(channelDo.getGmtCreate());\n            channel.setGmtModified(channelDo.getGmtModified());\n            List<Pipeline> pipelines = pipelineService.listByChannelIds(channelDo.getId());\n            // 合并PipelineParameter和ChannelParameter\n            SystemParameter systemParameter = systemParameterService.find();\n            for (Pipeline pipeline : pipelines) {\n                PipelineParameter parameter = new PipelineParameter();\n                parameter.merge(systemParameter);\n                parameter.merge(channel.getParameters());\n                // 最后复制pipelineId参数\n                parameter.merge(pipeline.getParameters());\n                pipeline.setParameters(parameter);\n                // pipeline.getParameters().merge(channel.getParameters());\n            }\n            channel.setPipelines(pipelines);\n        } catch (Exception e) {\n            logger.error(\"ERROR ## change the channel DO to Model has an exception\");\n            throw new ManagerException(e);\n        }\n\n        return channel;\n    }\n\n    /**\n     * <pre>\n     * 用于DO对象数组转化为Model对象数组\n     * 现阶段优化：\n     *      需要五次SQL交互:pipeline\\node\\dataMediaPair\\dataMedia\\dataMediaSource（五个层面）\n     *      目前优化方案为单层只执行一次SQL，避免重复循环造成IO及数据库查询开销\n     * 长期优化：\n     *      对SQL进行改造，尽量减小SQL调用次数\n     * </pre>\n     * \n     * @param channelDO\n     * @return Channel\n     */\n    private List<Channel> doToModel(List<ChannelDO> channelDos) {\n        List<Channel> channels = new ArrayList<Channel>();\n        try {\n            // 1.将ChannelID单独拿出来\n            List<Long> channelIds = new ArrayList<Long>();\n            for (ChannelDO channelDo : channelDos) {\n                channelIds.add(channelDo.getId());\n            }\n            Long[] idArray = new Long[channelIds.size()];\n\n            // 拿到所有的Pipeline进行ChannelID过滤，避免重复查询。\n            List<Pipeline> pipelines = pipelineService.listByChannelIds(channelIds.toArray(idArray));\n            SystemParameter systemParameter = systemParameterService.find();\n            for (ChannelDO channelDo : channelDos) {\n                Channel channel = new Channel();\n                channel.setId(channelDo.getId());\n                channel.setName(channelDo.getName());\n                channel.setDescription(channelDo.getDescription());\n                ChannelStatus channelStatus = arbitrateManageService.channelEvent().status(channelDo.getId());\n                channel.setStatus(null == channelStatus ? ChannelStatus.STOP : channelStatus);\n                channel.setParameters(channelDo.getParameters());\n                channel.setGmtCreate(channelDo.getGmtCreate());\n                channel.setGmtModified(channelDo.getGmtModified());\n                // 遍历，将该Channel节点下的Pipeline提取出来。\n                List<Pipeline> subPipelines = new ArrayList<Pipeline>();\n                for (Pipeline pipeline : pipelines) {\n                    if (pipeline.getChannelId().equals(channelDo.getId())) {\n                        // 合并PipelineParameter和ChannelParameter\n                        PipelineParameter parameter = new PipelineParameter();\n                        parameter.merge(systemParameter);\n                        parameter.merge(channel.getParameters());\n                        // 最后复制pipelineId参数\n                        parameter.merge(pipeline.getParameters());\n                        pipeline.setParameters(parameter);\n                        subPipelines.add(pipeline);\n                    }\n                }\n\n                channel.setPipelines(subPipelines);\n                channels.add(channel);\n            }\n        } catch (Exception e) {\n            logger.error(\"ERROR ## change the channels DO to Model has an exception\");\n            throw new ManagerException(e);\n        }\n\n        return channels;\n    }\n\n    private List<Channel> doToModelWithColumn(List<ChannelDO> channelDos) {\n        List<Channel> channels = new ArrayList<Channel>();\n        try {\n            // 1.将ChannelID单独拿出来\n            List<Long> channelIds = new ArrayList<Long>();\n            for (ChannelDO channelDo : channelDos) {\n                channelIds.add(channelDo.getId());\n            }\n            Long[] idArray = new Long[channelIds.size()];\n\n            // 拿到所有的Pipeline进行ChannelID过滤，避免重复查询。\n            List<Pipeline> pipelines = pipelineService.listByChannelIdsWithoutColumn(channelIds.toArray(idArray));\n            SystemParameter systemParameter = systemParameterService.find();\n            for (ChannelDO channelDo : channelDos) {\n                Channel channel = new Channel();\n                channel.setId(channelDo.getId());\n                channel.setName(channelDo.getName());\n                channel.setDescription(channelDo.getDescription());\n                ChannelStatus channelStatus = arbitrateManageService.channelEvent().status(channelDo.getId());\n                channel.setStatus(null == channelStatus ? ChannelStatus.STOP : channelStatus);\n                channel.setParameters(channelDo.getParameters());\n                channel.setGmtCreate(channelDo.getGmtCreate());\n                channel.setGmtModified(channelDo.getGmtModified());\n                // 遍历，将该Channel节点下的Pipeline提取出来。\n                List<Pipeline> subPipelines = new ArrayList<Pipeline>();\n                for (Pipeline pipeline : pipelines) {\n                    if (pipeline.getChannelId().equals(channelDo.getId())) {\n                        // 合并PipelineParameter和ChannelParameter\n                        PipelineParameter parameter = new PipelineParameter();\n                        parameter.merge(systemParameter);\n                        parameter.merge(channel.getParameters());\n                        // 最后复制pipelineId参数\n                        parameter.merge(pipeline.getParameters());\n                        pipeline.setParameters(parameter);\n                        subPipelines.add(pipeline);\n                    }\n                }\n\n                channel.setPipelines(subPipelines);\n                channels.add(channel);\n            }\n        } catch (Exception e) {\n            logger.error(\"ERROR ## change the channels DO to Model has an exception\");\n            throw new ManagerException(e);\n        }\n\n        return channels;\n    }\n\n    private List<Channel> doToModelOnlyChannels(List<ChannelDO> channelDos) {\n        List<Channel> channels = new ArrayList<Channel>();\n        try {\n            // 1.将ChannelID单独拿出来\n            List<Long> channelIds = new ArrayList<Long>();\n            for (ChannelDO channelDo : channelDos) {\n                channelIds.add(channelDo.getId());\n            }\n\n            for (ChannelDO channelDo : channelDos) {\n                Channel channel = new Channel();\n                channel.setId(channelDo.getId());\n                channel.setName(channelDo.getName());\n                channel.setDescription(channelDo.getDescription());\n                ChannelStatus channelStatus = arbitrateManageService.channelEvent().status(channelDo.getId());\n                channel.setStatus(null == channelStatus ? ChannelStatus.STOP : channelStatus);\n                channel.setParameters(channelDo.getParameters());\n                channel.setGmtCreate(channelDo.getGmtCreate());\n                channel.setGmtModified(channelDo.getGmtModified());\n                // 遍历，将该Channel节点下的Pipeline提取出来。\n                List<Pipeline> subPipelines = new ArrayList<Pipeline>();\n                channel.setPipelines(subPipelines);\n                channels.add(channel);\n            }\n        } catch (Exception e) {\n            logger.error(\"ERROR ## change the channels doToModelOnlyChannels has an exception\");\n            throw new ManagerException(e);\n        }\n\n        return channels;\n    }\n\n    /* ------------------------setter / getter--------------------------- */\n\n    public void setPipelineService(PipelineService pipelineService) {\n        this.pipelineService = pipelineService;\n    }\n\n    public void setChannelDao(ChannelDAO channelDao) {\n        this.channelDao = channelDao;\n    }\n\n    public void setArbitrateManageService(ArbitrateManageService arbitrateManageService) {\n        this.arbitrateManageService = arbitrateManageService;\n    }\n\n    public void setTransactionTemplate(TransactionTemplate transactionTemplate) {\n        this.transactionTemplate = transactionTemplate;\n    }\n\n    public void setConfigRemoteService(ConfigRemoteService configRemoteService) {\n        this.configRemoteService = configRemoteService;\n    }\n\n    public void setSystemParameterService(SystemParameterService systemParameterService) {\n        this.systemParameterService = systemParameterService;\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/datacolumnpair/DataColumnPairGroupService.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.config.datacolumnpair;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport com.alibaba.otter.manager.biz.common.baseservice.GenericService;\nimport com.alibaba.otter.shared.common.model.config.data.ColumnGroup;\n\n/**\n * 类DataColumnPairGroupService.java的实现描述：TODO 类实现描述\n * \n * @author simon 2012-4-20 下午4:08:24\n */\npublic interface DataColumnPairGroupService extends GenericService<ColumnGroup> {\n\n    public void removeByDataMediaPairId(Long dataMediaPairId);\n\n    public List<ColumnGroup> listByDataMediaPairId(Long dataMediaPairId);\n\n    public Map<Long, List<ColumnGroup>> listByDataMediaPairIds(Long... dataMediaPairId);\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/datacolumnpair/DataColumnPairService.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.config.datacolumnpair;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport com.alibaba.otter.manager.biz.common.baseservice.GenericService;\nimport com.alibaba.otter.shared.common.model.config.data.ColumnPair;\n\n/**\n * 类DataColumnPairService.java的实现描述：TODO 类实现描述\n * \n * @author simon 2012-4-20 下午4:07:47\n */\npublic interface DataColumnPairService extends GenericService<ColumnPair> {\n\n    public List<ColumnPair> listByDataMediaPairId(Long dataMediaPairId);\n\n    public Map<Long, List<ColumnPair>> listByDataMediaPairIds(Long... dataMediaPairIds);\n\n    public void createBatch(List<ColumnPair> dataColumnPairs);\n\n    public void removeByDataMediaPairId(Long dataMediaPairId);\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/datacolumnpair/dal/DataColumnPairDAO.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.config.datacolumnpair.dal;\n\nimport java.util.List;\n\nimport com.alibaba.otter.manager.biz.common.basedao.GenericDAO;\nimport com.alibaba.otter.manager.biz.config.datacolumnpair.dal.dataobject.DataColumnPairDO;\n\n/**\n * 类DataColumnPairDAO.java的实现描述：TODO 类实现描述\n * \n * @author simon 2012-4-20 下午4:08:55\n */\npublic interface DataColumnPairDAO extends GenericDAO<DataColumnPairDO> {\n\n    public List<DataColumnPairDO> listByDataMediaPairId(Long dataMediaPairId);\n\n    public List<DataColumnPairDO> listByDataMediaPairIds(Long... dataMediaPairIds);\n\n    public void insertBatch(List<DataColumnPairDO> dataColumnPairDos);\n\n    public void deleteByDataMediaPairId(Long dataMediaPairId);\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/datacolumnpair/dal/DataColumnPairGroupDAO.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.config.datacolumnpair.dal;\n\nimport java.util.List;\n\nimport com.alibaba.otter.manager.biz.common.basedao.GenericDAO;\nimport com.alibaba.otter.manager.biz.config.datacolumnpair.dal.dataobject.DataColumnPairGroupDO;\n\n/**\n * 类DataColumnPairGroupDAO.java的实现描述：TODO 类实现描述\n * \n * @author simon 2012-4-20 下午4:09:09\n */\npublic interface DataColumnPairGroupDAO extends GenericDAO<DataColumnPairGroupDO> {\n\n    public void deleteByDataMediaPairId(Long dataMediaPairId);\n\n    public List<DataColumnPairGroupDO> ListByDataMediaPairId(Long dataMediaPairId);\n\n    public List<DataColumnPairGroupDO> ListByDataMediaPairIds(Long... dataMediaPairIds);\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/datacolumnpair/dal/dataobject/DataColumnPairDO.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.config.datacolumnpair.dal.dataobject;\n\nimport java.io.Serializable;\nimport java.util.Date;\n\n/**\n * 类DataColumnPairDO.java的实现描述：TODO 类实现描述\n * \n * @author simon 2012-4-20 下午4:09:38\n */\npublic class DataColumnPairDO implements Serializable {\n\n    private static final long serialVersionUID = 194553152360180533L;\n    private Long              id;\n    private String            sourceColumnName;                      // 源字段\n    private String            targetColumnName;                      // 目标字段\n    private Long              dataMediaPairId;\n    private Date              gmtCreate;\n    private Date              gmtModified;\n\n    public Long getId() {\n        return id;\n    }\n\n    public void setId(Long id) {\n        this.id = id;\n    }\n\n    public String getSourceColumnName() {\n        return sourceColumnName;\n    }\n\n    public void setSourceColumnName(String sourceColumnName) {\n        this.sourceColumnName = sourceColumnName;\n    }\n\n    public String getTargetColumnName() {\n        return targetColumnName;\n    }\n\n    public void setTargetColumnName(String targetColumnName) {\n        this.targetColumnName = targetColumnName;\n    }\n\n    public Long getDataMediaPairId() {\n        return dataMediaPairId;\n    }\n\n    public void setDataMediaPairId(Long dataMediaPairId) {\n        this.dataMediaPairId = dataMediaPairId;\n    }\n\n    public Date getGmtCreate() {\n        return gmtCreate;\n    }\n\n    public void setGmtCreate(Date gmtCreate) {\n        this.gmtCreate = gmtCreate;\n    }\n\n    public Date getGmtModified() {\n        return gmtModified;\n    }\n\n    public void setGmtModified(Date gmtModified) {\n        this.gmtModified = gmtModified;\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/datacolumnpair/dal/dataobject/DataColumnPairGroupDO.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.config.datacolumnpair.dal.dataobject;\n\nimport java.io.Serializable;\nimport java.util.Date;\n\n/**\n * 类DataColumnPairGroupDO.java的实现描述：TODO 类实现描述\n * \n * @author simon 2012-4-20 下午4:09:50\n */\npublic class DataColumnPairGroupDO implements Serializable {\n\n    private static final long serialVersionUID = 7205447225855754450L;\n    private Long              id;\n    private String            columnPairContent;\n    private Long              dataMediaPairId;\n    private Date              gmtCreate;\n    private Date              gmtModified;\n\n    public Long getId() {\n        return id;\n    }\n\n    public void setId(Long id) {\n        this.id = id;\n    }\n\n    public String getColumnPairContent() {\n        return columnPairContent;\n    }\n\n    public void setColumnPairContent(String columnPairContent) {\n        this.columnPairContent = columnPairContent;\n    }\n\n    public Long getDataMediaPairId() {\n        return dataMediaPairId;\n    }\n\n    public void setDataMediaPairId(Long dataMediaPairId) {\n        this.dataMediaPairId = dataMediaPairId;\n    }\n\n    public Date getGmtCreate() {\n        return gmtCreate;\n    }\n\n    public void setGmtCreate(Date gmtCreate) {\n        this.gmtCreate = gmtCreate;\n    }\n\n    public Date getGmtModified() {\n        return gmtModified;\n    }\n\n    public void setGmtModified(Date gmtModified) {\n        this.gmtModified = gmtModified;\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/datacolumnpair/dal/ibatis/IbatisDataColumnPairDAO.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.config.datacolumnpair.dal.ibatis;\n\nimport java.sql.SQLException;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.springframework.orm.ibatis.support.SqlMapClientDaoSupport;\n\nimport com.alibaba.otter.shared.common.utils.Assert;\nimport com.alibaba.otter.manager.biz.config.datacolumnpair.dal.DataColumnPairDAO;\nimport com.alibaba.otter.manager.biz.config.datacolumnpair.dal.dataobject.DataColumnPairDO;\nimport com.alibaba.otter.manager.biz.config.datacolumnpair.dal.dataobject.DataColumnPairGroupDO;\n\n/**\n * 类IbatisDataColumnPairDAO.java的实现描述：TODO 类实现描述\n * \n * @author simon 2012-4-20 下午4:10:48\n */\npublic class IbatisDataColumnPairDAO extends SqlMapClientDaoSupport implements DataColumnPairDAO {\n\n    public DataColumnPairGroupDO insert(DataColumnPairGroupDO dataColumnPairDo) {\n        Assert.assertNotNull(dataColumnPairDo);\n        getSqlMapClientTemplate().insert(\"insertDataColumnPair\", dataColumnPairDo);\n        return dataColumnPairDo;\n    }\n\n    public void insertBatch(List<DataColumnPairDO> dataColumnPairDos) {\n        try {\n            getSqlMapClientTemplate().getSqlMapClient().startBatch();\n\n            Iterator it = dataColumnPairDos.iterator();\n            while (it.hasNext()) {\n                DataColumnPairDO dataColumnPairDo = (DataColumnPairDO) it.next();\n                getSqlMapClientTemplate().getSqlMapClient().insert(\"insertDataColumnPair\", dataColumnPairDo);\n            }\n\n            getSqlMapClientTemplate().getSqlMapClient().executeBatch();\n\n        } catch (SQLException ex1) {\n            ex1.printStackTrace();\n        }\n\n    }\n\n    public void delete(Long dataColumnPairId) {\n        Assert.assertNotNull(dataColumnPairId);\n        getSqlMapClientTemplate().delete(\"deleteDataColumnPairById\", dataColumnPairId);\n    }\n\n    public void deleteByDataMediaPairId(Long dataMediaPairId) {\n        Assert.assertNotNull(dataMediaPairId);\n        getSqlMapClientTemplate().delete(\"deleteDataColumnPairByDataMediaPairId\", dataMediaPairId);\n    }\n\n    public void update(DataColumnPairGroupDO dataColumnPairDo) {\n        Assert.assertNotNull(dataColumnPairDo);\n        getSqlMapClientTemplate().update(\"updateDataColumnPair\", dataColumnPairDo);\n\n    }\n\n    public List<DataColumnPairDO> listAll() {\n        List<DataColumnPairDO> dataColumnPairGroupDos = getSqlMapClientTemplate().queryForList(\"listDataColumnPairs\");\n        return dataColumnPairGroupDos;\n    }\n\n    public List<DataColumnPairDO> listByCondition(Map condition) {\n        return null;\n    }\n\n    public List<DataColumnPairDO> listByMultiId(Long... identities) {\n        return null;\n    }\n\n    public DataColumnPairDO findById(Long identity) {\n        Assert.assertNotNull(identity);\n        return (DataColumnPairDO) getSqlMapClientTemplate().queryForObject(\"findDataColumnPairById\", identity);\n    }\n\n    public int getCount() {\n        return 0;\n    }\n\n    public int getCount(Map condition) {\n        return 0;\n    }\n\n    public boolean checkUnique(DataColumnPairGroupDO entityObj) {\n        return false;\n    }\n\n    public List<DataColumnPairDO> listByDataMediaPairId(Long dataMediaPairId) {\n        List<DataColumnPairDO> dataColumnPairDos = getSqlMapClientTemplate().queryForList(\"listDataColumnPairByDataMediaPairId\",\n                                                                                          dataMediaPairId);\n        return dataColumnPairDos;\n    }\n\n    public List<DataColumnPairDO> listByDataMediaPairIds(Long... dataMediaPairIds) {\n        List<DataColumnPairDO> dataColumnPairDos = getSqlMapClientTemplate().queryForList(\"listDataColumnPairByDataMediaPairIds\",\n                                                                                          dataMediaPairIds);\n        return dataColumnPairDos;\n    }\n\n    public DataColumnPairDO insert(DataColumnPairDO entityObj) {\n        // TODO Auto-generated method stub\n        return null;\n    }\n\n    public void update(DataColumnPairDO entityObj) {\n        // TODO Auto-generated method stub\n\n    }\n\n    public boolean checkUnique(DataColumnPairDO entityObj) {\n        // TODO Auto-generated method stub\n        return false;\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/datacolumnpair/dal/ibatis/IbatisDataColumnPairGroupDAO.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.config.datacolumnpair.dal.ibatis;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.springframework.orm.ibatis.support.SqlMapClientDaoSupport;\n\nimport com.alibaba.otter.shared.common.utils.Assert;\nimport com.alibaba.otter.manager.biz.config.datacolumnpair.dal.DataColumnPairGroupDAO;\nimport com.alibaba.otter.manager.biz.config.datacolumnpair.dal.dataobject.DataColumnPairGroupDO;\n\n/**\n * 类IbatisDataColumnPairGroupDAO.java的实现描述：TODO 类实现描述\n * \n * @author simon 2012-4-20 下午4:11:06\n */\npublic class IbatisDataColumnPairGroupDAO extends SqlMapClientDaoSupport implements DataColumnPairGroupDAO {\n\n    public DataColumnPairGroupDO insert(DataColumnPairGroupDO entityObj) {\n        Assert.assertNotNull(entityObj);\n        getSqlMapClientTemplate().insert(\"insertDataColumnPairGroup\", entityObj);\n        return entityObj;\n    }\n\n    public void delete(Long identity) {\n        // TODO Auto-generated method stub\n\n    }\n\n    public void deleteByDataMediaPairId(Long dataMediaPairId) {\n        Assert.assertNotNull(dataMediaPairId);\n        getSqlMapClientTemplate().delete(\"deleteDataColumnPairGroupByDataMediaPairId\", dataMediaPairId);\n\n    }\n\n    public void update(DataColumnPairGroupDO entityObj) {\n        // TODO Auto-generated method stub\n\n    }\n\n    public List<DataColumnPairGroupDO> listAll() {\n        // TODO Auto-generated method stub\n        return null;\n    }\n\n    public List<DataColumnPairGroupDO> ListByDataMediaPairId(Long dataMediaPairId) {\n        Assert.assertNotNull(dataMediaPairId);\n        List<DataColumnPairGroupDO> dataColumnPairGroupDos = getSqlMapClientTemplate().queryForList(\"listDataColumnPairGroupByDataMediaPairId\",\n                                                                                                    dataMediaPairId);\n        return dataColumnPairGroupDos;\n    }\n\n    public List<DataColumnPairGroupDO> ListByDataMediaPairIds(Long... dataMediaPairIds) {\n        Assert.assertNotNull(dataMediaPairIds);\n        List<DataColumnPairGroupDO> dataColumnPairGroupDos = getSqlMapClientTemplate().queryForList(\"listDataColumnPairGroupByDataMediaPairIds\",\n                                                                                                    dataMediaPairIds);\n        return dataColumnPairGroupDos;\n    }\n\n    public List<DataColumnPairGroupDO> listByCondition(Map condition) {\n        // TODO Auto-generated method stub\n        return null;\n    }\n\n    public List<DataColumnPairGroupDO> listByMultiId(Long... identities) {\n        // TODO Auto-generated method stub\n        return null;\n    }\n\n    public DataColumnPairGroupDO findById(Long identity) {\n        // TODO Auto-generated method stub\n        return null;\n    }\n\n    public int getCount() {\n        // TODO Auto-generated method stub\n        return 0;\n    }\n\n    public int getCount(Map condition) {\n        // TODO Auto-generated method stub\n        return 0;\n    }\n\n    public boolean checkUnique(DataColumnPairGroupDO entityObj) {\n        // TODO Auto-generated method stub\n        return false;\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/datacolumnpair/impl/DataColumnPairGroupServiceImpl.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.config.datacolumnpair.impl;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.apache.commons.lang.StringUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.util.CollectionUtils;\n\nimport com.alibaba.otter.shared.common.utils.Assert;\nimport com.alibaba.fastjson.TypeReference;\nimport com.alibaba.otter.manager.biz.common.exceptions.ManagerException;\nimport com.alibaba.otter.manager.biz.common.exceptions.RepeatConfigureException;\nimport com.alibaba.otter.manager.biz.config.datacolumnpair.DataColumnPairGroupService;\nimport com.alibaba.otter.manager.biz.config.datacolumnpair.DataColumnPairService;\nimport com.alibaba.otter.manager.biz.config.datacolumnpair.dal.DataColumnPairGroupDAO;\nimport com.alibaba.otter.manager.biz.config.datacolumnpair.dal.dataobject.DataColumnPairGroupDO;\nimport com.alibaba.otter.shared.common.model.config.data.ColumnGroup;\nimport com.alibaba.otter.shared.common.model.config.data.ColumnPair;\nimport com.alibaba.otter.shared.common.utils.JsonUtils;\n\n/**\n * @author simon 2012-4-20 下午4:11:32\n */\npublic class DataColumnPairGroupServiceImpl implements DataColumnPairGroupService {\n\n    private static final Logger    logger = LoggerFactory.getLogger(DataColumnPairGroupServiceImpl.class);\n\n    private DataColumnPairGroupDAO dataColumnPairGroupDao;\n\n    private DataColumnPairService  dataColumnPairService;\n\n    @Override\n    public void create(ColumnGroup entityObj) {\n        Assert.assertNotNull(entityObj);\n\n        try {\n            DataColumnPairGroupDO dataColumnPairGroupDo = modelToDo(entityObj);\n            dataColumnPairGroupDao.insert(dataColumnPairGroupDo);\n        } catch (RepeatConfigureException rcf) {\n            throw rcf;\n        } catch (Exception e) {\n            logger.error(\"ERROR ## create dataColumnPairGroup has an exception!\");\n            throw new ManagerException(e);\n        }\n    }\n\n    @Override\n    public List<ColumnGroup> listByDataMediaPairId(Long dataMediaPairId) {\n        Assert.assertNotNull(dataMediaPairId);\n        List<DataColumnPairGroupDO> dataColumnPairGroupDos = dataColumnPairGroupDao.ListByDataMediaPairId(dataMediaPairId);\n        if (CollectionUtils.isEmpty(dataColumnPairGroupDos)) {\n            return new ArrayList<ColumnGroup>();\n        }\n\n        return doToModel(dataColumnPairGroupDos);\n    }\n\n    @Override\n    public Map<Long, List<ColumnGroup>> listByDataMediaPairIds(Long... dataMediaPairIds) {\n        Assert.assertNotNull(dataMediaPairIds);\n        Map<Long, List<ColumnGroup>> dataColumnGroups = new HashMap<Long, List<ColumnGroup>>();\n        try {\n            List<DataColumnPairGroupDO> dataColumnPairGroupDos = dataColumnPairGroupDao.ListByDataMediaPairIds(dataMediaPairIds);\n            if (CollectionUtils.isEmpty(dataColumnPairGroupDos)) {\n                logger.debug(\"DEBUG ## couldn't query any dataColumnPairGroup, maybe hasn't create any dataColumnPairGroup.\");\n                return dataColumnGroups;\n            }\n\n            for (DataColumnPairGroupDO dataColumnPairGroupDo : dataColumnPairGroupDos) {\n                List<ColumnGroup> columnGroups = dataColumnGroups.get(dataColumnPairGroupDo.getDataMediaPairId());\n                if (columnGroups != null) {\n                    if (!columnGroups.contains(doToModel(dataColumnPairGroupDo))) {\n                        columnGroups.add(doToModel(dataColumnPairGroupDo));\n                    }\n                } else {\n                    columnGroups = new ArrayList<ColumnGroup>();\n                    columnGroups.add(doToModel(dataColumnPairGroupDo));\n                    dataColumnGroups.put(dataColumnPairGroupDo.getDataMediaPairId(), columnGroups);\n                }\n            }\n        } catch (Exception e) {\n            logger.error(\"ERROR ## query dataColumnPairGroup by dataMediaId:\" + dataMediaPairIds + \" has an exception!\");\n            throw new ManagerException(e);\n        }\n\n        return dataColumnGroups;\n    }\n\n    @Override\n    public void remove(Long identity) {\n\n    }\n\n    @Override\n    public void removeByDataMediaPairId(Long dataMediaPairId) {\n        Assert.assertNotNull(dataMediaPairId);\n        dataColumnPairGroupDao.deleteByDataMediaPairId(dataMediaPairId);\n    }\n\n    @Override\n    public void modify(ColumnGroup entityObj) {\n\n    }\n\n    @Override\n    public ColumnGroup findById(Long identity) {\n        return null;\n    }\n\n    @Override\n    public List<ColumnGroup> listByIds(Long... identities) {\n        return null;\n    }\n\n    @Override\n    public List<ColumnGroup> listAll() {\n        return null;\n    }\n\n    @Override\n    public List<ColumnGroup> listByCondition(Map condition) {\n        return null;\n    }\n\n    @Override\n    public int getCount() {\n        return 0;\n    }\n\n    @Override\n    public int getCount(Map condition) {\n        return 0;\n    }\n\n    /*-------------------------------------------------------------*/\n    /**\n     * 用于DO对象转化为Model对象\n     */\n    private ColumnGroup doToModel(DataColumnPairGroupDO dataColumnPairGroupDo) {\n        ColumnGroup columnGroup = new ColumnGroup();\n        columnGroup.setId(dataColumnPairGroupDo.getId());\n        List<ColumnPair> columnPairs = new ArrayList<ColumnPair>();\n        if (StringUtils.isNotBlank(dataColumnPairGroupDo.getColumnPairContent())) {\n            columnPairs = JsonUtils.unmarshalFromString(dataColumnPairGroupDo.getColumnPairContent(),\n                                                        new TypeReference<ArrayList<ColumnPair>>() {\n                                                        });\n        }\n\n        columnGroup.setColumnPairs(columnPairs);\n        columnGroup.setDataMediaPairId(dataColumnPairGroupDo.getDataMediaPairId());\n        columnGroup.setGmtCreate(dataColumnPairGroupDo.getGmtCreate());\n        columnGroup.setGmtModified(dataColumnPairGroupDo.getGmtModified());\n\n        return columnGroup;\n    }\n\n    private List<ColumnGroup> doToModel(List<DataColumnPairGroupDO> dataColumnPairGroupDos) {\n        List<ColumnGroup> columnGroups = new ArrayList<ColumnGroup>();\n        for (DataColumnPairGroupDO dataColumnPairGroupDO : dataColumnPairGroupDos) {\n            columnGroups.add(doToModel(dataColumnPairGroupDO));\n        }\n\n        return columnGroups;\n    }\n\n    /**\n     * 用于Model对象转化为DO对象\n     * \n     * @param dataColumnPair\n     * @return DataMediaPairDO\n     */\n    private DataColumnPairGroupDO modelToDo(ColumnGroup columnGroup) {\n        DataColumnPairGroupDO dataColumnPairGroupDo = new DataColumnPairGroupDO();\n        dataColumnPairGroupDo.setId(columnGroup.getId());\n        dataColumnPairGroupDo.setColumnPairContent(JsonUtils.marshalToString(columnGroup.getColumnPairs()));\n        dataColumnPairGroupDo.setDataMediaPairId(columnGroup.getDataMediaPairId());\n        dataColumnPairGroupDo.setGmtCreate(columnGroup.getGmtCreate());\n        dataColumnPairGroupDo.setGmtModified(columnGroup.getGmtModified());\n\n        return dataColumnPairGroupDo;\n    }\n\n    public DataColumnPairGroupDAO getDataColumnPairGroupDao() {\n        return dataColumnPairGroupDao;\n    }\n\n    public void setDataColumnPairGroupDao(DataColumnPairGroupDAO dataColumnPairGroupDao) {\n        this.dataColumnPairGroupDao = dataColumnPairGroupDao;\n    }\n\n    public DataColumnPairService getDataColumnPairService() {\n        return dataColumnPairService;\n    }\n\n    public void setDataColumnPairService(DataColumnPairService dataColumnPairService) {\n        this.dataColumnPairService = dataColumnPairService;\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/datacolumnpair/impl/DataColumnPairServiceImpl.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.config.datacolumnpair.impl;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.alibaba.otter.shared.common.utils.Assert;\nimport com.alibaba.otter.manager.biz.common.exceptions.ManagerException;\nimport com.alibaba.otter.manager.biz.common.exceptions.RepeatConfigureException;\nimport com.alibaba.otter.manager.biz.config.datacolumnpair.DataColumnPairService;\nimport com.alibaba.otter.manager.biz.config.datacolumnpair.dal.DataColumnPairDAO;\nimport com.alibaba.otter.manager.biz.config.datacolumnpair.dal.dataobject.DataColumnPairDO;\nimport com.alibaba.otter.shared.common.model.config.data.Column;\nimport com.alibaba.otter.shared.common.model.config.data.ColumnPair;\n\n/**\n * 类DataColumnPairServiceImpl.java的实现描述：TODO 类实现描述\n * \n * @author simon 2012-4-20 下午4:11:42\n */\npublic class DataColumnPairServiceImpl implements DataColumnPairService {\n\n    private static final Logger logger = LoggerFactory.getLogger(DataColumnPairServiceImpl.class);\n\n    private DataColumnPairDAO   dataColumnPairDao;\n\n    public void create(ColumnPair entityObj) {\n        Assert.assertNotNull(entityObj);\n\n        try {\n            DataColumnPairDO dataColumnPairDo = modelToDo(entityObj);\n            dataColumnPairDao.insert(dataColumnPairDo);\n        } catch (RepeatConfigureException rcf) {\n            throw rcf;\n        } catch (Exception e) {\n            logger.error(\"ERROR ## create dataColumnPair has an exception!\");\n            throw new ManagerException(e);\n        }\n\n    }\n\n    public void createBatch(List<ColumnPair> dataColumnPairs) {\n        Assert.assertNotNull(dataColumnPairs);\n\n        try {\n\n            List<DataColumnPairDO> dataColumnPairDos = new ArrayList<DataColumnPairDO>();\n\n            for (ColumnPair columnPair : dataColumnPairs) {\n                DataColumnPairDO dataColumnPairDo = modelToDo(columnPair);\n                dataColumnPairDos.add(dataColumnPairDo);\n            }\n            dataColumnPairDao.insertBatch(dataColumnPairDos);\n        } catch (RepeatConfigureException rcf) {\n            throw rcf;\n        } catch (Exception e) {\n            logger.error(\"ERROR ## create dataColumnPair has an exception!\");\n            throw new ManagerException(e);\n        }\n    }\n\n    public void remove(Long identity) {\n        Assert.assertNotNull(identity);\n        try {\n            dataColumnPairDao.delete(identity);\n        } catch (Exception e) {\n            logger.error(\"ERROR ## remove dataColumnPair has an exception!\");\n            throw new ManagerException(e);\n        }\n    }\n\n    public void modify(ColumnPair entityObj) {\n        Assert.assertNotNull(entityObj);\n\n        try {\n            DataColumnPairDO dataColumnPairDo = modelToDo(entityObj);\n            dataColumnPairDao.update(dataColumnPairDo);\n        } catch (RepeatConfigureException rce) {\n            throw rce;\n        } catch (Exception e) {\n            logger.error(\"ERROR ## modify dataColumnPair has an exception!\");\n            throw new ManagerException(e);\n        }\n    }\n\n    public ColumnPair findById(Long identity) {\n        Assert.assertNotNull(identity);\n        DataColumnPairDO columePairDo = dataColumnPairDao.findById(identity);\n        if (columePairDo == null) {\n            return null;\n        }\n        return doToModel(columePairDo);\n\n    }\n\n    public List<ColumnPair> listByIds(Long... identities) {\n        return null;\n    }\n\n    public List<ColumnPair> listAll() {\n        return null;\n    }\n\n    public List<ColumnPair> listByDataMediaPairId(Long dataMediaPairId) {\n        Assert.assertNotNull(dataMediaPairId);\n        List<ColumnPair> dataColumnPairs = new ArrayList<ColumnPair>();\n        try {\n            List<DataColumnPairDO> dataColumnPairDos = dataColumnPairDao.listByDataMediaPairId(dataMediaPairId);\n            if (dataColumnPairDos.isEmpty()) {\n                logger.debug(\"DEBUG ## couldn't query any dataColumnPair, maybe hasn't create any dataColumnPair.\");\n                return dataColumnPairs;\n            }\n            dataColumnPairs = doToModel(dataColumnPairDos);\n        } catch (Exception e) {\n            logger.error(\"ERROR ## query dataColumnPair by dataMediaId:\" + dataMediaPairId + \" has an exception!\");\n            throw new ManagerException(e);\n        }\n\n        return dataColumnPairs;\n    }\n\n    public Map<Long, List<ColumnPair>> listByDataMediaPairIds(Long... dataMediaPairIds) {\n        Assert.assertNotNull(dataMediaPairIds);\n        Map<Long, List<ColumnPair>> dataColumnPairs = new HashMap<Long, List<ColumnPair>>();\n        try {\n            List<DataColumnPairDO> dataColumnPairDos = dataColumnPairDao.listByDataMediaPairIds(dataMediaPairIds);\n            if (dataColumnPairDos.isEmpty()) {\n                logger.debug(\"DEBUG ## couldn't query any dataColumnPair, maybe hasn't create any dataColumnPair.\");\n                return dataColumnPairs;\n            }\n            for (DataColumnPairDO dataColumnPairDo : dataColumnPairDos) {\n                List<ColumnPair> columnPairs = dataColumnPairs.get(dataColumnPairDo.getDataMediaPairId());\n                if (columnPairs != null) {\n                    if (!columnPairs.contains(doToModel(dataColumnPairDo))) {\n                        columnPairs.add(doToModel(dataColumnPairDo));\n                    }\n                } else {\n                    columnPairs = new ArrayList<ColumnPair>();\n                    columnPairs.add(doToModel(dataColumnPairDo));\n                    dataColumnPairs.put(dataColumnPairDo.getDataMediaPairId(), columnPairs);\n                }\n            }\n\n        } catch (Exception e) {\n            logger.error(\"ERROR ## query dataColumnPair by dataMediaId:\" + dataMediaPairIds + \" has an exception!\");\n            throw new ManagerException(e);\n        }\n\n        return dataColumnPairs;\n    }\n\n    public void removeByDataMediaPairId(Long dataMediaPairId) {\n        Assert.assertNotNull(dataMediaPairId);\n        try {\n            dataColumnPairDao.deleteByDataMediaPairId(dataMediaPairId);\n        } catch (Exception e) {\n            logger.error(\"ERROR ## remove dataColumnPair has an exception!\");\n            throw new ManagerException(e);\n        }\n    }\n\n    public List<ColumnPair> listByCondition(Map condition) {\n        return null;\n    }\n\n    public int getCount() {\n        return 0;\n    }\n\n    public int getCount(Map condition) {\n        return 0;\n    }\n\n    /*-------------------------------------------------------------*/\n    /**\n     * 用于DO对象转化为Model对象\n     */\n    private ColumnPair doToModel(DataColumnPairDO dataColumnPairDo) {\n\n        Column sourceColumn = dataColumnPairDo.getSourceColumnName() == null ? null : new Column(\n                                                                                                 dataColumnPairDo.getSourceColumnName());\n        Column targetColumn = dataColumnPairDo.getTargetColumnName() == null ? null : new Column(\n                                                                                                 dataColumnPairDo.getTargetColumnName());\n        ColumnPair columnPair = new ColumnPair(sourceColumn, targetColumn);\n        columnPair.setId(dataColumnPairDo.getId());\n        columnPair.setDataMediaPairId(dataColumnPairDo.getDataMediaPairId());\n        columnPair.setGmtCreate(dataColumnPairDo.getGmtCreate());\n        columnPair.setGmtModified(dataColumnPairDo.getGmtModified());\n\n        return columnPair;\n    }\n\n    private List<ColumnPair> doToModel(List<DataColumnPairDO> dataColumnPairDos) {\n\n        List<ColumnPair> columnPairs = new ArrayList<ColumnPair>();\n        for (DataColumnPairDO dataColumnPairDo : dataColumnPairDos) {\n            columnPairs.add(doToModel(dataColumnPairDo));\n        }\n\n        return columnPairs;\n    }\n\n    /**\n     * 用于Model对象转化为DO对象\n     * \n     * @param dataColumnPair\n     * @return DataMediaPairDO\n     */\n    private DataColumnPairDO modelToDo(ColumnPair dataColumnPair) {\n        DataColumnPairDO dataColumnPairDo = new DataColumnPairDO();\n        dataColumnPairDo.setId(dataColumnPair.getId());\n        dataColumnPairDo.setSourceColumnName(dataColumnPair.getSourceColumn() == null ? null : dataColumnPair.getSourceColumn().getName());\n        dataColumnPairDo.setTargetColumnName(dataColumnPair.getTargetColumn() == null ? null : dataColumnPair.getTargetColumn().getName());\n        dataColumnPairDo.setDataMediaPairId(dataColumnPair.getDataMediaPairId());\n        dataColumnPairDo.setGmtCreate(dataColumnPair.getGmtCreate());\n        dataColumnPairDo.setGmtModified(dataColumnPair.getGmtModified());\n\n        return dataColumnPairDo;\n    }\n\n    public DataColumnPairDAO getDataColumnPairDao() {\n        return dataColumnPairDao;\n    }\n\n    public void setDataColumnPairDao(DataColumnPairDAO dataColumnPairDao) {\n        this.dataColumnPairDao = dataColumnPairDao;\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/datamatrix/DataMatrixService.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.config.datamatrix;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport com.alibaba.otter.shared.common.model.config.data.DataMatrix;\n\npublic interface DataMatrixService {\n\n    public void create(DataMatrix DataMatrix);\n\n    public void remove(Long DataMatrixId);\n\n    public void modify(DataMatrix DataMatrix);\n\n    public List<DataMatrix> listByIds(Long... identities);\n\n    public List<DataMatrix> listAll();\n\n    public DataMatrix findById(Long DataMatrixId);\n\n    public DataMatrix findByGroupKey(String name);\n\n    public int getCount(Map condition);\n\n    public List<DataMatrix> listByCondition(Map condition);\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/datamatrix/dal/DataMatrixDAO.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.config.datamatrix.dal;\n\nimport com.alibaba.otter.manager.biz.common.basedao.GenericDAO;\nimport com.alibaba.otter.manager.biz.config.datamatrix.dal.dataobject.DataMatrixDO;\n\npublic interface DataMatrixDAO extends GenericDAO<DataMatrixDO> {\n\n    public DataMatrixDO findByGroupKey(String groupKey);\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/datamatrix/dal/dataobject/DataMatrixDO.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.config.datamatrix.dal.dataobject;\n\nimport java.io.Serializable;\nimport java.util.Date;\n\npublic class DataMatrixDO implements Serializable {\n\n    private static final long serialVersionUID = 9148286590254926037L;\n    private Long              id;                                     // 唯一标示id\n    private String            groupKey;                               // groupKey\n    private String            master;\n    private String            slave;\n    private String            description;                            // 描述\n    private Date              gmtCreate;                              // 创建时间\n    private Date              gmtModified;                            // 修改时间\n\n    public Long getId() {\n        return id;\n    }\n\n    public void setId(Long id) {\n        this.id = id;\n    }\n\n    public String getGroupKey() {\n        return groupKey;\n    }\n\n    public void setGroupKey(String groupKey) {\n        this.groupKey = groupKey;\n    }\n\n    public String getMaster() {\n        return master;\n    }\n\n    public void setMaster(String master) {\n        this.master = master;\n    }\n\n    public String getSlave() {\n        return slave;\n    }\n\n    public void setSlave(String slave) {\n        this.slave = slave;\n    }\n\n    public String getDescription() {\n        return description;\n    }\n\n    public void setDescription(String description) {\n        this.description = description;\n    }\n\n    public Date getGmtCreate() {\n        return gmtCreate;\n    }\n\n    public void setGmtCreate(Date gmtCreate) {\n        this.gmtCreate = gmtCreate;\n    }\n\n    public Date getGmtModified() {\n        return gmtModified;\n    }\n\n    public void setGmtModified(Date gmtModified) {\n        this.gmtModified = gmtModified;\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/datamatrix/dal/ibatis/IbatisDataMatrixDAO.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.config.datamatrix.dal.ibatis;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.springframework.orm.ibatis.support.SqlMapClientDaoSupport;\n\nimport com.alibaba.otter.shared.common.utils.Assert;\nimport com.alibaba.otter.manager.biz.config.datamatrix.dal.DataMatrixDAO;\nimport com.alibaba.otter.manager.biz.config.datamatrix.dal.dataobject.DataMatrixDO;\n\npublic class IbatisDataMatrixDAO extends SqlMapClientDaoSupport implements DataMatrixDAO {\n\n    public DataMatrixDO insert(DataMatrixDO matrixDo) {\n        Assert.assertNotNull(matrixDo);\n        getSqlMapClientTemplate().insert(\"insertDataMatrix\", matrixDo);\n        return matrixDo;\n    }\n\n    public void delete(Long matrixId) {\n        Assert.assertNotNull(matrixId);\n        getSqlMapClientTemplate().delete(\"deleteDataMatrixById\", matrixId);\n    }\n\n    public void update(DataMatrixDO matrixDo) {\n        Assert.assertNotNull(matrixDo);\n        getSqlMapClientTemplate().update(\"updateDataMatrix\", matrixDo);\n    }\n\n    public List<DataMatrixDO> listAll() {\n        return (List<DataMatrixDO>) getSqlMapClientTemplate().queryForList(\"listDataMatrixs\");\n    }\n\n    public List<DataMatrixDO> listByMultiId(Long... identities) {\n        List<DataMatrixDO> DataMatrixDOs = getSqlMapClientTemplate().queryForList(\"listDataMatrixByIds\", identities);\n        return DataMatrixDOs;\n    }\n\n    public boolean checkUnique(DataMatrixDO matrixDo) {\n        int count = (Integer) getSqlMapClientTemplate().queryForObject(\"checkDataMatrixUnique\", matrixDo);\n        return count == 0 ? true : false;\n    }\n\n    public DataMatrixDO findByGroupKey(String groupKey) {\n        Assert.assertNotNull(groupKey);\n        return (DataMatrixDO) getSqlMapClientTemplate().queryForObject(\"findDataMatrixByGroupKey\", groupKey);\n    }\n\n    public DataMatrixDO findById(Long identity) {\n        throw new UnsupportedOperationException();\n    }\n\n    public int getCount() {\n        return 0;\n    }\n\n    public int getCount(Map condition) {\n        Integer count = (Integer) getSqlMapClientTemplate().queryForObject(\"getDataMatrixCount\", condition);\n        return count.intValue();\n    }\n\n    public List<DataMatrixDO> listByCondition(Map condition) {\n        List<DataMatrixDO> DataMatrixDOs = getSqlMapClientTemplate().queryForList(\"listDataMatrixs\", condition);\n        return DataMatrixDOs;\n    }\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/datamatrix/impl/DataMatrixServiceImpl.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.config.datamatrix.impl;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.transaction.TransactionStatus;\nimport org.springframework.transaction.support.TransactionCallbackWithoutResult;\nimport org.springframework.transaction.support.TransactionTemplate;\n\nimport com.alibaba.otter.shared.common.utils.Assert;\nimport com.alibaba.otter.manager.biz.common.exceptions.ManagerException;\nimport com.alibaba.otter.manager.biz.common.exceptions.RepeatConfigureException;\nimport com.alibaba.otter.manager.biz.config.datamatrix.DataMatrixService;\nimport com.alibaba.otter.manager.biz.config.datamatrix.dal.DataMatrixDAO;\nimport com.alibaba.otter.manager.biz.config.datamatrix.dal.dataobject.DataMatrixDO;\nimport com.alibaba.otter.manager.biz.config.node.impl.NodeServiceImpl;\nimport com.alibaba.otter.shared.common.model.config.data.DataMatrix;\nimport com.alibaba.otter.shared.common.utils.JsonUtils;\n\npublic class DataMatrixServiceImpl implements DataMatrixService {\n\n    private static final Logger logger = LoggerFactory.getLogger(NodeServiceImpl.class);\n\n    private DataMatrixDAO       dataMatrixDao;\n    private TransactionTemplate transactionTemplate;\n\n    /**\n     * 添加\n     */\n    public void create(final DataMatrix matrix) {\n        Assert.assertNotNull(matrix);\n        transactionTemplate.execute(new TransactionCallbackWithoutResult() {\n\n            protected void doInTransactionWithoutResult(TransactionStatus status) {\n\n                try {\n                    DataMatrixDO matrixlDO = modelToDo(matrix);\n                    matrixlDO.setId(0L);\n                    if (!dataMatrixDao.checkUnique(matrixlDO)) {\n                        String exceptionCause = \"exist the same repeat canal in the database.\";\n                        logger.warn(\"WARN ## \" + exceptionCause);\n                        throw new RepeatConfigureException(exceptionCause);\n                    }\n                    dataMatrixDao.insert(matrixlDO);\n                } catch (RepeatConfigureException rce) {\n                    throw rce;\n                } catch (Exception e) {\n                    logger.error(\"ERROR ## create canal has an exception!\");\n                    throw new ManagerException(e);\n                }\n            }\n        });\n    }\n\n    /**\n     * 删除\n     */\n    public void remove(final Long matrixId) {\n        Assert.assertNotNull(matrixId);\n        transactionTemplate.execute(new TransactionCallbackWithoutResult() {\n\n            protected void doInTransactionWithoutResult(TransactionStatus status) {\n\n                try {\n                    dataMatrixDao.delete(matrixId);\n                } catch (Exception e) {\n                    logger.error(\"ERROR ## remove canal(\" + matrixId + \") has an exception!\");\n                    throw new ManagerException(e);\n                }\n            }\n        });\n\n    }\n\n    /**\n     * 修改\n     */\n    public void modify(final DataMatrix matrix) {\n        Assert.assertNotNull(matrix);\n        transactionTemplate.execute(new TransactionCallbackWithoutResult() {\n\n            protected void doInTransactionWithoutResult(TransactionStatus status) {\n\n                try {\n                    DataMatrixDO matrixDo = modelToDo(matrix);\n                    if (dataMatrixDao.checkUnique(matrixDo)) {\n                        dataMatrixDao.update(matrixDo);\n                    } else {\n                        String exceptionCause = \"exist the same repeat matrix in the database.\";\n                        logger.warn(\"WARN ## \" + exceptionCause);\n                        throw new RepeatConfigureException(exceptionCause);\n                    }\n                } catch (RepeatConfigureException rce) {\n                    throw rce;\n                } catch (Exception e) {\n                    logger.error(\"ERROR ## modify canal(\" + matrix.getId() + \") has an exception!\");\n                    throw new ManagerException(e);\n                }\n            }\n        });\n\n    }\n\n    public List<DataMatrix> listByIds(Long... identities) {\n        List<DataMatrix> matrixs = new ArrayList<DataMatrix>();\n        try {\n            List<DataMatrixDO> matrixDos = null;\n            if (identities.length < 1) {\n                matrixDos = dataMatrixDao.listAll();\n                if (matrixDos.isEmpty()) {\n                    logger.debug(\"DEBUG ## couldn't query any canal, maybe hasn't create any canal.\");\n                    return matrixs;\n                }\n            } else {\n                matrixDos = dataMatrixDao.listByMultiId(identities);\n                if (matrixDos.isEmpty()) {\n                    String exceptionCause = \"couldn't query any canal by matrixIds:\" + Arrays.toString(identities);\n                    logger.error(\"ERROR ## \" + exceptionCause);\n                    throw new ManagerException(exceptionCause);\n                }\n            }\n            matrixs = doToModel(matrixDos);\n        } catch (Exception e) {\n            logger.error(\"ERROR ## query channels has an exception!\");\n            throw new ManagerException(e);\n        }\n\n        return matrixs;\n    }\n\n    public List<DataMatrix> listAll() {\n        return listByIds();\n    }\n\n    public DataMatrix findById(Long matrixId) {\n        Assert.assertNotNull(matrixId);\n        List<DataMatrix> canals = listByIds(matrixId);\n        if (canals.size() != 1) {\n            String exceptionCause = \"query matrixId:\" + matrixId + \" return null.\";\n            logger.error(\"ERROR ## \" + exceptionCause);\n            throw new ManagerException(exceptionCause);\n        }\n\n        return canals.get(0);\n    }\n\n    public DataMatrix findByGroupKey(String groupKey) {\n        Assert.assertNotNull(groupKey);\n        DataMatrixDO matrixDo = dataMatrixDao.findByGroupKey(groupKey);\n        if (matrixDo == null) {\n            String exceptionCause = \"query name:\" + groupKey + \" return null.\";\n            logger.error(\"ERROR ## \" + exceptionCause);\n            throw new ManagerException(exceptionCause);\n        }\n\n        return doToModel(matrixDo);\n    }\n\n    @Override\n    public int getCount(Map condition) {\n        return dataMatrixDao.getCount(condition);\n    }\n\n    @Override\n    public List<DataMatrix> listByCondition(Map condition) {\n        List<DataMatrixDO> matrixDos = dataMatrixDao.listByCondition(condition);\n        if (matrixDos.isEmpty()) {\n            logger.debug(\"DEBUG ## couldn't query any canal by the condition:\" + JsonUtils.marshalToString(condition));\n            return new ArrayList<DataMatrix>();\n        }\n\n        return doToModel(matrixDos);\n    }\n\n    /**\n     * 用于Model对象转化为DO对象\n     */\n    private DataMatrixDO modelToDo(DataMatrix matrix) {\n        DataMatrixDO matrixDo = new DataMatrixDO();\n        try {\n            matrixDo.setId(matrix.getId());\n            matrixDo.setGroupKey(matrix.getGroupKey());\n            matrixDo.setDescription(matrix.getDescription());\n            matrixDo.setMaster(matrix.getMaster());\n            matrixDo.setSlave(matrix.getSlave());\n            matrixDo.setGmtCreate(matrix.getGmtCreate());\n            matrixDo.setGmtModified(matrix.getGmtModified());\n        } catch (Exception e) {\n            logger.error(\"ERROR ## change the matrix Model to Do has an exception\");\n            throw new ManagerException(e);\n        }\n        return matrixDo;\n    }\n\n    /**\n     * 用于DO对象转化为Model对象\n     */\n    private DataMatrix doToModel(DataMatrixDO matrixDo) {\n        DataMatrix matrix = new DataMatrix();\n        try {\n            matrix.setId(matrixDo.getId());\n            matrix.setGroupKey(matrixDo.getGroupKey());\n            matrix.setDescription(matrixDo.getDescription());\n            matrix.setMaster(matrixDo.getMaster());\n            matrix.setSlave(matrixDo.getSlave());\n            matrix.setGmtCreate(matrixDo.getGmtCreate());\n            matrix.setGmtModified(matrixDo.getGmtModified());\n        } catch (Exception e) {\n            logger.error(\"ERROR ## change the canal Do to Model has an exception\");\n            throw new ManagerException(e);\n        }\n\n        return matrix;\n    }\n\n    private List<DataMatrix> doToModel(List<DataMatrixDO> matrixDos) {\n        List<DataMatrix> matrixs = new ArrayList<DataMatrix>();\n        for (DataMatrixDO matrixDo : matrixDos) {\n            matrixs.add(doToModel(matrixDo));\n        }\n        return matrixs;\n    }\n\n    /* ------------------------setter / getter--------------------------- */\n\n    public void setTransactionTemplate(TransactionTemplate transactionTemplate) {\n        this.transactionTemplate = transactionTemplate;\n    }\n\n    public void setDataMatrixDao(DataMatrixDAO dataMatrixDao) {\n        this.dataMatrixDao = dataMatrixDao;\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/datamedia/DataMediaService.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.config.datamedia;\n\nimport java.util.List;\n\nimport com.alibaba.otter.manager.biz.common.baseservice.GenericService;\nimport com.alibaba.otter.shared.common.model.config.data.DataMedia;\n\n/**\n * @author simon\n */\npublic interface DataMediaService extends GenericService<DataMedia> {\n\n    // public List<DataMedia> listDataMediaByIds(Long... dataMediaIds);\n\n    public List<DataMedia> listByDataMediaSourceId(Long dataMediaSourceId);\n\n    public Long createReturnId(DataMedia dataMedia);\n\n    public List<String> queryColumnByMedia(DataMedia dataMedia);\n\n    public List<String> queryColumnByMediaId(Long dataMediaId);\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/datamedia/dal/DataMediaDAO.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.config.datamedia.dal;\n\nimport java.util.List;\n\nimport com.alibaba.otter.manager.biz.common.basedao.GenericDAO;\nimport com.alibaba.otter.manager.biz.config.datamedia.dal.dataobject.DataMediaDO;\n\n/**\n * @author simon\n */\npublic interface DataMediaDAO extends GenericDAO<DataMediaDO> {\n\n    public List<DataMediaDO> listByDataMediaSourceId(Long dataMediaSourceId);\n\n    public DataMediaDO checkUniqueAndReturnExist(DataMediaDO dataMedia);\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/datamedia/dal/dataobject/DataMediaDO.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.config.datamedia.dal.dataobject;\n\nimport java.io.Serializable;\nimport java.util.Date;\n\n/**\n * @author simon\n */\npublic class DataMediaDO implements Serializable {\n\n    private static final long serialVersionUID = 1830886218829190716L;\n\n    private Long              id;\n    private String            name;                                   // 介质名称\n    private String            namespace;                              // 介质类型\n    private String            properties;\n    private Long              dataMediaSourceId;\n    private Date              gmtCreate;\n    private Date              gmtModified;\n\n    public Long getId() {\n        return id;\n    }\n\n    public void setId(Long id) {\n        this.id = id;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public void setName(String name) {\n        this.name = name;\n    }\n\n    public String getNamespace() {\n        return namespace;\n    }\n\n    public void setNamespace(String namespace) {\n        this.namespace = namespace;\n    }\n\n    public String getProperties() {\n        return properties;\n    }\n\n    public void setProperties(String properties) {\n        this.properties = properties;\n    }\n\n    public Long getDataMediaSourceId() {\n        return dataMediaSourceId;\n    }\n\n    public void setDataMediaSourceId(Long dataMediaSourceId) {\n        this.dataMediaSourceId = dataMediaSourceId;\n    }\n\n    public Date getGmtCreate() {\n        return gmtCreate;\n    }\n\n    public void setGmtCreate(Date gmtCreate) {\n        this.gmtCreate = gmtCreate;\n    }\n\n    public Date getGmtModified() {\n        return gmtModified;\n    }\n\n    public void setGmtModified(Date gmtModified) {\n        this.gmtModified = gmtModified;\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/datamedia/dal/ibatis/IbatisDataMediaDAO.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.config.datamedia.dal.ibatis;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.springframework.orm.ibatis.support.SqlMapClientDaoSupport;\n\nimport com.alibaba.otter.shared.common.utils.Assert;\nimport com.alibaba.otter.manager.biz.config.datamedia.dal.DataMediaDAO;\nimport com.alibaba.otter.manager.biz.config.datamedia.dal.dataobject.DataMediaDO;\n\n/**\n * DataMedia的DAO层，ibatis的实现，主要是CRUD操作。\n * \n * @author simon\n */\npublic class IbatisDataMediaDAO extends SqlMapClientDaoSupport implements DataMediaDAO {\n\n    public DataMediaDO insert(DataMediaDO dataMedia) {\n        Assert.assertNotNull(dataMedia);\n        getSqlMapClientTemplate().insert(\"insertDataMedia\", dataMedia);\n        return dataMedia;\n    }\n\n    public void delete(Long dataMediaId) {\n        Assert.assertNotNull(dataMediaId);\n        getSqlMapClientTemplate().delete(\"deleteDataMediaById\", dataMediaId);\n    }\n\n    public void update(DataMediaDO dataMedia) {\n        Assert.assertNotNull(dataMedia);\n        getSqlMapClientTemplate().update(\"updateDataMedia\", dataMedia);\n    }\n\n    public boolean checkUnique(DataMediaDO dataMedia) {\n        int count = (Integer) getSqlMapClientTemplate().queryForObject(\"checkDataMediaUnique\", dataMedia);\n        return count == 0 ? true : false;\n    }\n\n    public DataMediaDO checkUniqueAndReturnExist(DataMediaDO dataMedia) {\n        return (DataMediaDO) getSqlMapClientTemplate().queryForObject(\"checkDataMediaUniqueAndReturnTheExist\",\n                                                                      dataMedia);\n    }\n\n    public DataMediaDO findById(Long dataMediaId) {\n        Assert.assertNotNull(dataMediaId);\n        return (DataMediaDO) getSqlMapClientTemplate().queryForObject(\"findDataMediaById\", dataMediaId);\n    }\n\n    public List<DataMediaDO> listAll() {\n        return (List<DataMediaDO>) getSqlMapClientTemplate().queryForList(\"listDataMedias\");\n    }\n\n    public List<DataMediaDO> listByDataMediaSourceId(Long dataMediaSourceId) {\n        Assert.assertNotNull(dataMediaSourceId);\n        return (List<DataMediaDO>) getSqlMapClientTemplate().queryForList(\"listDataMediasByDataMediaSourceId\",\n                                                                          dataMediaSourceId);\n    }\n\n    public List<DataMediaDO> listByCondition(Map condition) {\n        List<DataMediaDO> dataMediaDos = getSqlMapClientTemplate().queryForList(\"listDataMedias\", condition);\n        return dataMediaDos;\n    }\n\n    public List<DataMediaDO> listByMultiId(Long... identities) {\n        List<DataMediaDO> dataMediaDos = getSqlMapClientTemplate().queryForList(\"listDataMediaByIds\", identities);\n        return dataMediaDos;\n    }\n\n    public int getCount() {\n        Integer count = (Integer) getSqlMapClientTemplate().queryForObject(\"getDataMediaCount\");\n        return count.intValue();\n    }\n\n    public int getCount(Map condition) {\n        Integer count = (Integer) getSqlMapClientTemplate().queryForObject(\"getDataMediaCount\", condition);\n        return count.intValue();\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/datamedia/impl/DataMediaServiceImpl.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.config.datamedia.impl;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Map;\n\nimport javax.sql.DataSource;\n\nimport org.apache.ddlutils.model.Column;\nimport org.apache.ddlutils.model.Table;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.jdbc.core.JdbcTemplate;\n\nimport com.alibaba.otter.shared.common.utils.Assert;\nimport com.alibaba.otter.manager.biz.common.DataSourceCreator;\nimport com.alibaba.otter.manager.biz.common.exceptions.ManagerException;\nimport com.alibaba.otter.manager.biz.common.exceptions.RepeatConfigureException;\nimport com.alibaba.otter.manager.biz.config.datamedia.DataMediaService;\nimport com.alibaba.otter.manager.biz.config.datamedia.dal.DataMediaDAO;\nimport com.alibaba.otter.manager.biz.config.datamedia.dal.dataobject.DataMediaDO;\nimport com.alibaba.otter.manager.biz.config.datamediasource.DataMediaSourceService;\nimport com.alibaba.otter.shared.common.model.config.data.DataMedia;\nimport com.alibaba.otter.shared.common.model.config.data.DataMediaSource;\nimport com.alibaba.otter.shared.common.model.config.data.db.DbDataMedia;\nimport com.alibaba.otter.shared.common.model.config.data.mq.MqDataMedia;\nimport com.alibaba.otter.shared.common.utils.JsonUtils;\nimport com.alibaba.otter.shared.common.utils.meta.DdlUtils;\n\n/**\n * @author simon\n */\npublic class DataMediaServiceImpl implements DataMediaService {\n\n    private static final Logger    logger = LoggerFactory.getLogger(DataMediaServiceImpl.class);\n\n    private DataMediaDAO           dataMediaDao;\n\n    private DataMediaSourceService dataMediaSourceService;\n\n    private DataSourceCreator      dataSourceCreator;\n\n    @Override\n    public List<String> queryColumnByMediaId(Long dataMediaId) {\n        return queryColumnByMedia(findById(dataMediaId));\n    }\n\n    @Override\n    public List<String> queryColumnByMedia(DataMedia dataMedia) {\n        List<String> columnResult = new ArrayList<String>();\n        if (dataMedia.getSource().getType().isNapoli()) {\n            return columnResult;\n        }\n\n        DataSource dataSource = dataSourceCreator.createDataSource(dataMedia.getSource());\n        // 针对multi表，直接获取第一个匹配的表结构\n        String schemaName = dataMedia.getNamespaceMode().getSingleValue();\n        String tableName = dataMedia.getNameMode().getSingleValue();\n        try {\n            Table table = DdlUtils.findTable(new JdbcTemplate(dataSource), schemaName, schemaName, tableName);\n            for (Column column : table.getColumns()) {\n                columnResult.add(column.getName());\n            }\n        } catch (Exception e) {\n            logger.error(\"ERROR ## DdlUtils find table happen error!\", e);\n        }\n\n        return columnResult;\n    }\n\n    /**\n     * 添加\n     */\n    @Override\n    public void create(DataMedia dataMedia) {\n        Assert.assertNotNull(dataMedia);\n        try {\n            DataMediaDO dataMediaDo = modelToDo(dataMedia);\n            dataMediaDo.setId(0L);\n            if (!dataMediaDao.checkUnique(dataMediaDo)) {\n                String exceptionCause = \"exist the same name dataMedia in the database.\";\n                logger.warn(\"WARN ## \" + exceptionCause);\n                throw new RepeatConfigureException(exceptionCause);\n            }\n\n            dataMediaDao.insert(dataMediaDo);\n        } catch (RepeatConfigureException rce) {\n            throw rce;\n        } catch (Exception e) {\n            logger.error(\"ERROR ## create dataMedia has an exception!\");\n            throw new ManagerException(e);\n        }\n    }\n\n    /**\n     * 添加\n     */\n    @Override\n    public Long createReturnId(DataMedia dataMedia) {\n        Assert.assertNotNull(dataMedia);\n        try {\n            DataMediaDO dataMediaDo = modelToDo(dataMedia);\n            dataMediaDo.setId(0L);\n            DataMediaDO dataMediaDoInDb = dataMediaDao.checkUniqueAndReturnExist(dataMediaDo);\n            if (dataMediaDoInDb == null) {\n                dataMediaDo = dataMediaDao.insert(dataMediaDo);\n            } else {\n                dataMediaDo = dataMediaDoInDb;\n            }\n            return dataMediaDo.getId();\n        } catch (RepeatConfigureException rce) {\n            throw rce;\n        } catch (Exception e) {\n            logger.error(\"ERROR ## create dataMedia has an exception!\");\n            throw new ManagerException(e);\n        }\n    }\n\n    /**\n     * 删除\n     */\n    @Override\n    public void remove(Long dataMediaId) {\n        Assert.assertNotNull(dataMediaId);\n        try {\n            dataMediaDao.delete(dataMediaId);\n        } catch (Exception e) {\n            logger.error(\"ERROR ## remove dataMedia has an exception!\");\n            throw new ManagerException(e);\n        }\n\n    }\n\n    /**\n     * 修改\n     */\n    @Override\n    public void modify(DataMedia dataMedia) {\n        Assert.assertNotNull(dataMedia);\n        try {\n            DataMediaDO dataMediaDo = modelToDo(dataMedia);\n            if (dataMediaDao.checkUnique(dataMediaDo)) {\n                dataMediaDao.update(dataMediaDo);\n            } else {\n                String exceptionCause = \"exist the same name dataMedia in the database.\";\n                logger.warn(\"WARN ## \" + exceptionCause);\n                throw new RepeatConfigureException(exceptionCause);\n            }\n        } catch (RepeatConfigureException rce) {\n            throw rce;\n        } catch (Exception e) {\n            logger.error(\"ERROR ## modify dataMedia has an exception!\");\n            throw new ManagerException(e);\n        }\n    }\n\n    /**\n     * 查出所有的DataMedia\n     */\n    @Override\n    public List<DataMedia> listAll() {\n        return listByIds();\n    }\n\n    @Override\n    public List<DataMedia> listByCondition(Map condition) {\n        List<DataMedia> dataMedias = new ArrayList<DataMedia>();\n        try {\n            List<DataMediaDO> dataMediaDos = dataMediaDao.listByCondition(condition);\n            if (dataMediaDos.isEmpty()) {\n                logger.debug(\"DEBUG ## couldn't query any dataMedias by the condition:\"\n                             + JsonUtils.marshalToString(condition));\n                return dataMedias;\n            }\n            dataMedias = doToModel(dataMediaDos);\n        } catch (Exception e) {\n            logger.error(\"ERROR ## query dataMedias by condition has an exception!\");\n            throw new ManagerException(e);\n        }\n\n        return dataMedias;\n    }\n\n    /**\n     * 根据dataMediaId查询出dataMedia Model\n     */\n    @Override\n    public DataMedia findById(Long dataMediaId) {\n        Assert.assertNotNull(dataMediaId);\n        List<DataMedia> dataMedias = listByIds(dataMediaId);\n        if (dataMedias.size() != 1) {\n            String exceptionCause = \"query dataMediaId:\" + dataMediaId + \" but return \" + dataMedias.size()\n                                    + \" dataMedia.\";\n            logger.error(\"ERROR ## \" + exceptionCause);\n            throw new ManagerException(exceptionCause);\n        }\n        return dataMedias.get(0);\n\n    }\n\n    @Override\n    public List<DataMedia> listByIds(Long... identities) {\n        List<DataMedia> dataMedias = new ArrayList<DataMedia>();\n        try {\n            List<DataMediaDO> dataMediaDos = null;\n            if (identities.length < 1) {\n                dataMediaDos = dataMediaDao.listAll();\n                if (dataMediaDos.isEmpty()) {\n                    logger.debug(\"DEBUG ## couldn't query any dataMedia, maybe hasn't create any dataMedia.\");\n                    return dataMedias;\n                }\n            } else {\n                dataMediaDos = dataMediaDao.listByMultiId(identities);\n                if (dataMediaDos.isEmpty()) {\n                    String exceptionCause = \"couldn't query any dataMedia by dataMediaIds:\"\n                                            + Arrays.toString(identities);\n                    logger.error(\"ERROR ## \" + exceptionCause);\n                    throw new ManagerException(exceptionCause);\n                }\n            }\n            dataMedias = doToModel(dataMediaDos);\n        } catch (Exception e) {\n            logger.error(\"ERROR ## query dataMedias has an exception!\");\n            throw new ManagerException(e);\n        }\n\n        return dataMedias;\n    }\n\n    @Override\n    public List<DataMedia> listByDataMediaSourceId(Long dataMediaSourceId) {\n        Assert.assertNotNull(dataMediaSourceId);\n        List<DataMediaDO> dataMediaDos = null;\n        try {\n            dataMediaDos = dataMediaDao.listByDataMediaSourceId(dataMediaSourceId);\n            if (dataMediaDos.isEmpty()) {\n                logger.debug(\"DEBUG ## couldn't query any dataMedia, maybe hasn't create any dataMedia.\");\n                return new ArrayList<DataMedia>();\n            }\n        } catch (Exception e) {\n            logger.error(\"ERROR ## query dataMedias by sourceId:\" + dataMediaSourceId + \" has an exception!\");\n            throw new ManagerException(e);\n        }\n        return doToModel(dataMediaDos);\n    }\n\n    @Override\n    public int getCount() {\n        return dataMediaDao.getCount();\n    }\n\n    @Override\n    public int getCount(Map condition) {\n        return dataMediaDao.getCount(condition);\n    }\n\n    /**\n     * 用于Model对象转化为DO对象\n     * \n     * @param dataMedia\n     * @return DataMediaDO\n     */\n    private DataMediaDO modelToDo(DataMedia dataMedia) {\n\n        DataMediaDO dataMediaDo = new DataMediaDO();\n\n        try {\n            dataMediaDo.setId(dataMedia.getId());\n            dataMediaDo.setName(dataMedia.getName());\n            dataMediaDo.setNamespace(dataMedia.getNamespace());\n            dataMediaDo.setDataMediaSourceId(dataMedia.getSource().getId());\n            // if (dataMedia instanceof DbDataMedia) {\n            // dataMediaDo.setProperties(JsonUtils.marshalToString((DbDataMedia) dataMedia));\n            // }\n            dataMediaDo.setProperties(JsonUtils.marshalToString(dataMedia));\n            dataMediaDo.setGmtCreate(dataMedia.getGmtCreate());\n            dataMediaDo.setGmtModified(dataMedia.getGmtModified());\n        } catch (Exception e) {\n            logger.error(\"ERROR ## change the dataMedia Model to Do has an exception\");\n            throw new ManagerException(e);\n        }\n\n        return dataMediaDo;\n    }\n\n    /**\n     * 用于DO对象转化为Model对象\n     * \n     * @param dataMediaDo\n     * @return DataMedia\n     */\n    private DataMedia doToModel(DataMediaDO dataMediaDo) {\n        DataMedia dataMedia = null;\n        try {\n            DataMediaSource dataMediaSource = dataMediaSourceService.findById(dataMediaDo.getDataMediaSourceId());\n            if (dataMediaSource.getType().isMysql() || dataMediaSource.getType().isOracle()) {\n                dataMedia = JsonUtils.unmarshalFromString(dataMediaDo.getProperties(), DbDataMedia.class);\n                dataMedia.setSource(dataMediaSource);\n            } else if (dataMediaSource.getType().isNapoli() || dataMediaSource.getType().isMq()) {\n                dataMedia = JsonUtils.unmarshalFromString(dataMediaDo.getProperties(), MqDataMedia.class);\n                dataMedia.setSource(dataMediaSource);\n            }\n\n            dataMedia.setId(dataMediaDo.getId());\n            dataMedia.setGmtCreate(dataMediaDo.getGmtCreate());\n            dataMedia.setGmtModified(dataMediaDo.getGmtModified());\n\n        } catch (Exception e) {\n            logger.error(\"ERROR ## change the dataMedia Do to Model has an exception\");\n            throw new ManagerException(e);\n        }\n\n        return dataMedia;\n    }\n\n    private List<DataMedia> doToModel(List<DataMediaDO> dataMediaDos) {\n        List<DataMedia> dataMedias = new ArrayList<DataMedia>();\n        for (DataMediaDO dataMediaDo : dataMediaDos) {\n            dataMedias.add(doToModel(dataMediaDo));\n        }\n\n        return dataMedias;\n    }\n\n    /* ------------------------setter / getter--------------------------- */\n\n    public void setDataMediaDao(DataMediaDAO dataMediaDao) {\n        this.dataMediaDao = dataMediaDao;\n    }\n\n    public void setDataMediaSourceService(DataMediaSourceService dataMediaSourceService) {\n        this.dataMediaSourceService = dataMediaSourceService;\n    }\n\n    public void setDataSourceCreator(DataSourceCreator dataSourceCreator) {\n        this.dataSourceCreator = dataSourceCreator;\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/datamediapair/DataMediaPairService.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.config.datamediapair;\n\nimport java.util.List;\n\nimport com.alibaba.otter.manager.biz.common.baseservice.GenericService;\nimport com.alibaba.otter.shared.common.model.config.data.DataMediaPair;\n\n/**\n * @author simon\n */\npublic interface DataMediaPairService extends GenericService<DataMediaPair> {\n\n    public List<DataMediaPair> listByPipelineId(Long pipelineId);\n\n    public List<DataMediaPair> listByPipelineIdWithoutColumn(Long pipelineId);\n\n    public List<DataMediaPair> listByDataMediaId(Long dataMediaId);\n\n    public Long createAndReturnId(DataMediaPair dataMediaPair);\n\n    public boolean createIfNotExist(DataMediaPair dataMediaPair);\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/datamediapair/dal/DataMediaPairDAO.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.config.datamediapair.dal;\n\nimport java.util.List;\n\nimport com.alibaba.otter.manager.biz.common.basedao.GenericDAO;\nimport com.alibaba.otter.manager.biz.config.datamediapair.dal.dataobject.DataMediaPairDO;\n\n/**\n * @author simon\n */\npublic interface DataMediaPairDAO extends GenericDAO<DataMediaPairDO> {\n\n    public List<DataMediaPairDO> listByPipelineId(Long pipelineId);\n\n    public List<DataMediaPairDO> listByDataMediaId(Long dataMediaId);\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/datamediapair/dal/dataobject/DataMediaPairDO.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.config.datamediapair.dal.dataobject;\n\nimport java.io.Serializable;\nimport java.util.Date;\n\nimport com.alibaba.otter.shared.common.model.config.data.ColumnPairMode;\n\n/**\n * @author simon\n */\npublic class DataMediaPairDO implements Serializable {\n\n    private static final long serialVersionUID = -7771432925148858183L;\n    private Long              id;\n    private Long              sourceDataMediaId;\n    private Long              targetDataMediaId;\n    private Long              pullWeight;                              // 介质A中获取数据的权重\n    private Long              pushWeight;                              // 介质B中写入数据的权重\n    private String            resolver;                                // 关联数据解析类\n    private String            filter;                                  // 数据过滤处理类\n    private ColumnPairMode    columnPairMode;\n    private Long              pipelineId;                              // 同步任务id\n    private Date              gmtCreate;\n    private Date              gmtModified;\n\n    public Long getId() {\n        return id;\n    }\n\n    public void setId(Long id) {\n        this.id = id;\n    }\n\n    public Long getSourceDataMediaId() {\n        return sourceDataMediaId;\n    }\n\n    public void setSourceDataMediaId(Long sourceDataMediaId) {\n        this.sourceDataMediaId = sourceDataMediaId;\n    }\n\n    public Long getTargetDataMediaId() {\n        return targetDataMediaId;\n    }\n\n    public void setTargetDataMediaId(Long targetDataMediaId) {\n        this.targetDataMediaId = targetDataMediaId;\n    }\n\n    public Long getPipelineId() {\n        return pipelineId;\n    }\n\n    public void setPipelineId(Long pipelineId) {\n        this.pipelineId = pipelineId;\n    }\n\n    public Date getGmtModified() {\n        return gmtModified;\n    }\n\n    public void setGmtModified(Date gmtModified) {\n        this.gmtModified = gmtModified;\n    }\n\n    public Date getGmtCreate() {\n        return gmtCreate;\n    }\n\n    public void setGmtCreate(Date gmtCreate) {\n        this.gmtCreate = gmtCreate;\n    }\n\n    public Long getPullWeight() {\n        return pullWeight;\n    }\n\n    public void setPullWeight(Long pullWeight) {\n        this.pullWeight = pullWeight;\n    }\n\n    public Long getPushWeight() {\n        return pushWeight;\n    }\n\n    public void setPushWeight(Long pushWeight) {\n        this.pushWeight = pushWeight;\n    }\n\n    public String getResolver() {\n        return resolver;\n    }\n\n    public void setResolver(String resolver) {\n        this.resolver = resolver;\n    }\n\n    public String getFilter() {\n        return filter;\n    }\n\n    public void setFilter(String filter) {\n        this.filter = filter;\n    }\n\n    public ColumnPairMode getColumnPairMode() {\n        return columnPairMode;\n    }\n\n    public void setColumnPairMode(ColumnPairMode columnPairMode) {\n        this.columnPairMode = columnPairMode;\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/datamediapair/dal/ibatis/IbatisDataMediaPairDAO.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.config.datamediapair.dal.ibatis;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.springframework.orm.ibatis.support.SqlMapClientDaoSupport;\n\nimport com.alibaba.otter.shared.common.utils.Assert;\nimport com.alibaba.otter.manager.biz.config.datamediapair.dal.DataMediaPairDAO;\nimport com.alibaba.otter.manager.biz.config.datamediapair.dal.dataobject.DataMediaPairDO;\nimport com.alibaba.otter.shared.common.model.config.data.ColumnPair;\n\n/**\n * DataMediaPair的DAO层，ibatis的实现，主要是CRUD操作。\n * \n * @author simon\n */\npublic class IbatisDataMediaPairDAO extends SqlMapClientDaoSupport implements DataMediaPairDAO {\n\n    public DataMediaPairDO insert(DataMediaPairDO dataMediaPair) {\n        Assert.assertNotNull(dataMediaPair);\n        getSqlMapClientTemplate().insert(\"insertDataMediaPair\", dataMediaPair);\n        return dataMediaPair;\n    }\n\n    public void insertColumnPairs(List<ColumnPair> ColumnPairs) {\n        Assert.assertNotNull(ColumnPairs);\n        getSqlMapClientTemplate().insert(\"insertColumnPairs\", ColumnPairs);\n    }\n\n    public void delete(Long dataMediaPairId) {\n        Assert.assertNotNull(dataMediaPairId);\n        getSqlMapClientTemplate().delete(\"deleteDataMediaPairById\", dataMediaPairId);\n    }\n\n    public void update(DataMediaPairDO dataMediaPair) {\n        Assert.assertNotNull(dataMediaPair);\n        getSqlMapClientTemplate().update(\"updateDataMediaPair\", dataMediaPair);\n    }\n\n    public boolean checkUnique(DataMediaPairDO dataMediaPair) {\n        int count = (Integer) getSqlMapClientTemplate().queryForObject(\"checkDataMediaPairUnique\", dataMediaPair);\n        return count == 0 ? true : false;\n    }\n\n    public DataMediaPairDO findById(Long dataMediaPairId) {\n        Assert.assertNotNull(dataMediaPairId);\n        return (DataMediaPairDO) getSqlMapClientTemplate().queryForObject(\"findDataMediaPairById\", dataMediaPairId);\n    }\n\n    public List<DataMediaPairDO> listAll() {\n\n        return (List<DataMediaPairDO>) getSqlMapClientTemplate().queryForList(\"listDataMediaPairs\");\n    }\n\n    public List<DataMediaPairDO> listByPipelineId(Long pipelineId) {\n        Assert.assertNotNull(pipelineId);\n        return (List<DataMediaPairDO>) getSqlMapClientTemplate().queryForList(\"listDataMediaPairsByPipelineId\",\n                                                                              pipelineId);\n    }\n\n    public List<DataMediaPairDO> listByCondition(Map condition) {\n        List<DataMediaPairDO> dataMediaPairDos = getSqlMapClientTemplate().queryForList(\"listDataMediaPairs\", condition);\n        return dataMediaPairDos;\n    }\n\n    public List<DataMediaPairDO> listByDataMediaId(Long dataMediaId) {\n        Assert.assertNotNull(dataMediaId);\n        return (List<DataMediaPairDO>) getSqlMapClientTemplate().queryForList(\"listDataMediaPairsByDataMediaId\",\n                                                                              dataMediaId);\n    }\n\n    public List<DataMediaPairDO> listByMultiId(Long... identities) {\n        List<DataMediaPairDO> dataMediaPairDos = getSqlMapClientTemplate().queryForList(\"listDataMediaPairByIds\",\n                                                                                        identities);\n        return dataMediaPairDos;\n    }\n\n    public int getCount() {\n        Integer count = (Integer) getSqlMapClientTemplate().queryForObject(\"getDataMediaPairCount\");\n        return count.intValue();\n    }\n\n    public int getCount(Map condition) {\n        Integer count = (Integer) getSqlMapClientTemplate().queryForObject(\"getDataMediaPairCount\", condition);\n        return count.intValue();\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/datamediapair/impl/DataMediaPairServiceImpl.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.config.datamediapair.impl;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.apache.commons.lang.StringUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.alibaba.otter.shared.common.utils.Assert;\nimport com.alibaba.otter.manager.biz.common.exceptions.ManagerException;\nimport com.alibaba.otter.manager.biz.common.exceptions.RepeatConfigureException;\nimport com.alibaba.otter.manager.biz.config.datacolumnpair.DataColumnPairGroupService;\nimport com.alibaba.otter.manager.biz.config.datacolumnpair.DataColumnPairService;\nimport com.alibaba.otter.manager.biz.config.datamedia.DataMediaService;\nimport com.alibaba.otter.manager.biz.config.datamediapair.DataMediaPairService;\nimport com.alibaba.otter.manager.biz.config.datamediapair.dal.DataMediaPairDAO;\nimport com.alibaba.otter.manager.biz.config.datamediapair.dal.dataobject.DataMediaPairDO;\nimport com.alibaba.otter.shared.common.model.config.data.ColumnGroup;\nimport com.alibaba.otter.shared.common.model.config.data.ColumnPair;\nimport com.alibaba.otter.shared.common.model.config.data.DataMedia;\nimport com.alibaba.otter.shared.common.model.config.data.DataMediaPair;\nimport com.alibaba.otter.shared.common.model.config.data.ExtensionData;\nimport com.alibaba.otter.shared.common.utils.JsonUtils;\n\n/**\n * @author simon\n */\npublic class DataMediaPairServiceImpl implements DataMediaPairService {\n\n    private static final Logger        logger = LoggerFactory.getLogger(DataMediaPairServiceImpl.class);\n\n    private DataMediaPairDAO           dataMediaPairDao;\n\n    private DataMediaService           dataMediaService;\n\n    private DataColumnPairService      dataColumnPairService;\n\n    private DataColumnPairGroupService dataColumnPairGroupService;\n\n    /**\n     * 添加\n     */\n    public void create(DataMediaPair dataMediaPair) {\n        createAndReturnId(dataMediaPair);\n    }\n\n    /**\n     * 添加并返回插入的id\n     */\n    public Long createAndReturnId(DataMediaPair dataMediaPair) {\n        Assert.assertNotNull(dataMediaPair);\n\n        try {\n            DataMediaPairDO dataMediaPairDo = modelToDo(dataMediaPair);\n            dataMediaPairDo.setId(0L);\n            if (!dataMediaPairDao.checkUnique(dataMediaPairDo)) {\n                String exceptionCause = \"exist the same pair in the database.\";\n                logger.warn(\"WARN ## \" + exceptionCause);\n                throw new RepeatConfigureException(exceptionCause);\n            }\n\n            dataMediaPairDao.insert(dataMediaPairDo);\n            return dataMediaPairDo.getId();\n        } catch (RepeatConfigureException rcf) {\n            throw rcf;\n        } catch (Exception e) {\n            logger.error(\"ERROR ## create dataMediaPair has an exception!\", e);\n            throw new ManagerException(e);\n        }\n\n    }\n\n    /**\n     * 添加并返回插入的id\n     */\n    public boolean createIfNotExist(DataMediaPair dataMediaPair) {\n        Assert.assertNotNull(dataMediaPair);\n\n        try {\n            DataMediaPairDO dataMediaPairDo = modelToDo(dataMediaPair);\n            dataMediaPairDo.setId(0L);\n            if (!dataMediaPairDao.checkUnique(dataMediaPairDo)) {\n                return false;\n            }\n            dataMediaPairDao.insert(dataMediaPairDo);\n            return true;\n        } catch (Exception e) {\n            logger.error(\"ERROR ## create dataMediaPair has an exception!\", e);\n            throw new ManagerException(e);\n        }\n\n    }\n\n    /**\n     * 删除\n     */\n    public void remove(Long dataMediaPairId) {\n        Assert.assertNotNull(dataMediaPairId);\n\n        try {\n            dataMediaPairDao.delete(dataMediaPairId);\n        } catch (Exception e) {\n            logger.error(\"ERROR ## remove dataMediaPair has an exception!\", e);\n            throw new ManagerException(e);\n        }\n    }\n\n    /**\n     * 修改\n     */\n    public void modify(DataMediaPair dataMediaPair) {\n        Assert.assertNotNull(dataMediaPair);\n\n        try {\n            DataMediaPairDO dataMediaPairDo = modelToDo(dataMediaPair);\n            if (dataMediaPairDao.checkUnique(dataMediaPairDo)) {\n                dataMediaPairDao.update(dataMediaPairDo);\n            } else {\n                String exceptionCause = \"exist the same pair in the database.\";\n                logger.warn(\"WARN ## \" + exceptionCause);\n                throw new RepeatConfigureException(exceptionCause);\n            }\n        } catch (RepeatConfigureException rce) {\n            throw rce;\n        } catch (Exception e) {\n            logger.error(\"ERROR ## modify dataMediaPair has an exception!\", e);\n            throw new ManagerException(e);\n        }\n    }\n\n    /*-----------------------------------查询方法，整合-----------------------------------------*/\n\n    public List<DataMediaPair> listByIds(Long... identities) {\n        List<DataMediaPair> dataMediaPairs = new ArrayList<DataMediaPair>();\n        try {\n            List<DataMediaPairDO> dataMediaPairDos = null;\n            if (identities.length < 1) {\n                dataMediaPairDos = dataMediaPairDao.listAll();\n                if (dataMediaPairDos.isEmpty()) {\n                    logger.debug(\"DEBUG ## couldn't query any dataMediaPair, maybe hasn't create any dataMediaPair.\");\n                    return dataMediaPairs;\n                }\n            } else {\n                dataMediaPairDos = dataMediaPairDao.listByMultiId(identities);\n                if (dataMediaPairDos.isEmpty()) {\n                    String exceptionCause = \"couldn't query any dataMediaPair by dataMediaPairIds:\"\n                                            + Arrays.toString(identities);\n                    logger.error(\"ERROR ## \" + exceptionCause);\n                    throw new ManagerException(exceptionCause);\n                }\n            }\n            dataMediaPairs = doToModel(dataMediaPairDos);\n        } catch (Exception e) {\n            logger.error(\"ERROR ## query dataMediaPairs has an exception!\", e);\n            throw new ManagerException(e);\n        }\n\n        return dataMediaPairs;\n\n    }\n\n    /**\n     * 查找所有额DataMediaPair\n     */\n    public List<DataMediaPair> listAll() {\n        return listByIds();\n    }\n\n    @Override\n    public List<DataMediaPair> listByCondition(Map condition) {\n        List<DataMediaPair> dataMediaPairs = new ArrayList<DataMediaPair>();\n\n        try {\n            List<DataMediaPairDO> dataMediaPairDos = dataMediaPairDao.listByCondition(condition);\n            if (dataMediaPairDos.isEmpty()) {\n                logger.debug(\"DEBUG ## couldn't query any DataMediaPairs by the condition:\"\n                             + JsonUtils.marshalToString(condition));\n                return dataMediaPairs;\n            }\n            dataMediaPairs = doToModel(dataMediaPairDos);\n        } catch (Exception e) {\n            logger.error(\"ERROR ## query dataMediaPairs by condition has an exception!\", e);\n            throw new ManagerException(e);\n        }\n        return dataMediaPairs;\n    }\n\n    /**\n     * 根据对应的dataMediaPairId找到对应的dataMediaPair\n     */\n    public DataMediaPair findById(Long dataMediaPairId) {\n        Assert.assertNotNull(dataMediaPairId);\n        List<DataMediaPair> dataMediaPairs = listByIds(dataMediaPairId);\n        if (dataMediaPairs.size() != 1) {\n            String exceptionCause = \"query dataMediaPairId:\" + dataMediaPairId + \" but return \" + dataMediaPairs.size()\n                                    + \" Pairs.\";\n            logger.error(\"ERROR ## \" + exceptionCause);\n            throw new ManagerException(exceptionCause);\n        }\n        return dataMediaPairs.get(0);\n    }\n\n    /**\n     * 根据PipelineId找到该枝干下的所有DataMediaPairs\n     */\n    public List<DataMediaPair> listByPipelineId(Long pipelineId) {\n        Assert.assertNotNull(pipelineId);\n        List<DataMediaPair> dataMediaPairs = new ArrayList<DataMediaPair>();\n        try {\n            List<DataMediaPairDO> dataMediaPairDos = dataMediaPairDao.listByPipelineId(pipelineId);\n            if (dataMediaPairDos.isEmpty()) {\n                logger.debug(\"DEBUG ## couldn't query any dataMediaPair, maybe hasn't create any dataMediaPair.\");\n                return dataMediaPairs;\n            }\n            dataMediaPairs = doToModel(dataMediaPairDos);\n        } catch (Exception e) {\n            logger.error(\"ERROR ## query dataMediaPairs by pipelineId:\" + pipelineId + \" has an exception!\", e);\n            throw new ManagerException(e);\n        }\n\n        return dataMediaPairs;\n    }\n\n    @Override\n    public List<DataMediaPair> listByPipelineIdWithoutColumn(Long pipelineId) {\n        Assert.assertNotNull(pipelineId);\n        List<DataMediaPair> dataMediaPairs = new ArrayList<DataMediaPair>();\n        try {\n            List<DataMediaPairDO> dataMediaPairDos = dataMediaPairDao.listByPipelineId(pipelineId);\n            if (dataMediaPairDos.isEmpty()) {\n                logger.debug(\"DEBUG ## couldn't query any dataMediaPair, maybe hasn't create any dataMediaPair.\");\n                return dataMediaPairs;\n            }\n            dataMediaPairs = doToModelWithoutOther(dataMediaPairDos);\n        } catch (Exception e) {\n            logger.error(\"ERROR ## query dataMediaPairs by pipelineId:\" + pipelineId + \" has an exception!\", e);\n            throw new ManagerException(e);\n        }\n\n        return dataMediaPairs;\n    }\n\n    @Override\n    public List<DataMediaPair> listByDataMediaId(Long dataMediaId) {\n        Assert.assertNotNull(dataMediaId);\n        List<DataMediaPair> dataMediaPairs = new ArrayList<DataMediaPair>();\n        try {\n            List<DataMediaPairDO> dataMediaPairDos = dataMediaPairDao.listByDataMediaId(dataMediaId);\n            if (dataMediaPairDos.isEmpty()) {\n                logger.debug(\"DEBUG ## couldn't query any dataMediaPair, maybe hasn't create any dataMediaPair.\");\n                return dataMediaPairs;\n            }\n            dataMediaPairs = doToModel(dataMediaPairDos);\n        } catch (Exception e) {\n            logger.error(\"ERROR ## query dataMediaPairs by dataMediaId:\" + dataMediaId + \" has an exception!\", e);\n            throw new ManagerException(e);\n        }\n\n        return dataMediaPairs;\n    }\n\n    public int getCount() {\n        return dataMediaPairDao.getCount();\n    }\n\n    public int getCount(Map condition) {\n        return dataMediaPairDao.getCount(condition);\n    }\n\n    /*-------------------------------------------------------------*/\n\n    private DataMediaPair doToModel(DataMediaPairDO dataMediaPairDo, List<ColumnPair> columnPairs,\n                                    List<ColumnGroup> columnGroups) {\n        DataMediaPair dataMediaPair = new DataMediaPair();\n        try {\n            dataMediaPair.setId(dataMediaPairDo.getId());\n            dataMediaPair.setPipelineId(dataMediaPairDo.getPipelineId());\n            dataMediaPair.setPullWeight(dataMediaPairDo.getPullWeight());\n            dataMediaPair.setPushWeight(dataMediaPairDo.getPushWeight());\n            if (StringUtils.isNotBlank(dataMediaPairDo.getFilter())) {\n                dataMediaPair.setFilterData(JsonUtils.unmarshalFromString(dataMediaPairDo.getFilter(),\n                                                                          ExtensionData.class));\n            }\n\n            if (StringUtils.isNotBlank(dataMediaPairDo.getResolver())) {\n                dataMediaPair.setResolverData(JsonUtils.unmarshalFromString(dataMediaPairDo.getResolver(),\n                                                                            ExtensionData.class));\n            }\n            dataMediaPair.setColumnPairs(columnPairs);\n            dataMediaPair.setColumnGroups(columnGroups);\n            dataMediaPair.setColumnPairMode(dataMediaPairDo.getColumnPairMode());\n            dataMediaPair.setGmtCreate(dataMediaPairDo.getGmtCreate());\n            dataMediaPair.setGmtModified(dataMediaPairDo.getGmtModified());\n\n            // 组装DataMedia\n            List<DataMedia> dataMedias = dataMediaService.listByIds(dataMediaPairDo.getSourceDataMediaId(),\n                                                                    dataMediaPairDo.getTargetDataMediaId());\n            if (null == dataMedias || dataMedias.size() != 2) {\n                // 抛出异常\n                return dataMediaPair;\n            }\n\n            for (DataMedia dataMedia : dataMedias) {\n                if (dataMedia.getId().equals(dataMediaPairDo.getSourceDataMediaId())) {\n                    dataMediaPair.setSource(dataMedia);\n                } else if (dataMedia.getId().equals(dataMediaPairDo.getTargetDataMediaId())) {\n                    dataMediaPair.setTarget(dataMedia);\n                }\n            }\n        } catch (Exception e) {\n            logger.error(\"ERROR ## change the dataMediaPair Do to Model has an exception\", e);\n            throw new ManagerException(e);\n        }\n\n        return dataMediaPair;\n    }\n\n    private List<DataMediaPair> doToModel(List<DataMediaPairDO> dataMediaPairDos) {\n        List<Long> dataMediaPairIds = new ArrayList<Long>();\n        for (DataMediaPairDO dataMediaPairDo : dataMediaPairDos) {\n            dataMediaPairIds.add(dataMediaPairDo.getId());\n        }\n        Map<Long, List<ColumnPair>> columnPairMap = dataColumnPairService.listByDataMediaPairIds(dataMediaPairIds.toArray(new Long[dataMediaPairIds.size()]));\n        Map<Long, List<ColumnGroup>> columnPairGroupMap = dataColumnPairGroupService.listByDataMediaPairIds(dataMediaPairIds.toArray(new Long[dataMediaPairIds.size()]));\n        List<DataMediaPair> dataMediaPairs = new ArrayList<DataMediaPair>();\n        for (DataMediaPairDO dataMediaPairDo : dataMediaPairDos) {\n            List<ColumnPair> columnPairs = columnPairMap.get(dataMediaPairDo.getId()) == null ? new ArrayList<ColumnPair>() : columnPairMap.get(dataMediaPairDo.getId());\n            List<ColumnGroup> columnGroups = columnPairGroupMap.get(dataMediaPairDo.getId()) == null ? new ArrayList<ColumnGroup>() : columnPairGroupMap.get(dataMediaPairDo.getId());\n            dataMediaPairs.add(doToModel(dataMediaPairDo, columnPairs, columnGroups));\n        }\n\n        return dataMediaPairs;\n    }\n\n    private List<DataMediaPair> doToModelWithoutOther(List<DataMediaPairDO> dataMediaPairDos) {\n        List<DataMediaPair> dataMediaPairs = new ArrayList<DataMediaPair>();\n        for (DataMediaPairDO dataMediaPairDo : dataMediaPairDos) {\n            dataMediaPairs.add(doToModel(dataMediaPairDo, null, null));\n        }\n\n        return dataMediaPairs;\n    }\n\n    /**\n     * 用于Model对象转化为DO对象\n     * \n     * @param dataMediaPair\n     * @return DataMediaPairDO\n     */\n    private DataMediaPairDO modelToDo(DataMediaPair dataMediaPair) {\n        DataMediaPairDO dataMediaPairDo = new DataMediaPairDO();\n        try {\n            dataMediaPairDo.setId(dataMediaPair.getId());\n            dataMediaPairDo.setPipelineId(dataMediaPair.getPipelineId());\n            dataMediaPairDo.setSourceDataMediaId(dataMediaPair.getSource().getId());\n            dataMediaPairDo.setTargetDataMediaId(dataMediaPair.getTarget().getId());\n            dataMediaPairDo.setFilter(JsonUtils.marshalToString(dataMediaPair.getFilterData()));\n            dataMediaPairDo.setResolver(JsonUtils.marshalToString(dataMediaPair.getResolverData()));\n            dataMediaPairDo.setPullWeight(dataMediaPair.getPullWeight());\n            dataMediaPairDo.setPushWeight(dataMediaPair.getPushWeight());\n            dataMediaPairDo.setColumnPairMode(dataMediaPair.getColumnPairMode());\n            dataMediaPairDo.setGmtCreate(dataMediaPair.getGmtCreate());\n            dataMediaPairDo.setGmtModified(dataMediaPair.getGmtModified());\n        } catch (Exception e) {\n            logger.error(\"ERROR ## change the dataMediaPair Model to Do has an exception\", e);\n            throw new ManagerException(e);\n        }\n\n        return dataMediaPairDo;\n    }\n\n    /* ------------------------setter / getter--------------------------- */\n\n    public void setDataMediaPairDao(DataMediaPairDAO dataMediaPairDao) {\n        this.dataMediaPairDao = dataMediaPairDao;\n    }\n\n    public void setDataMediaService(DataMediaService dataMediaService) {\n        this.dataMediaService = dataMediaService;\n    }\n\n    public void setDataColumnPairService(DataColumnPairService dataColumnPairService) {\n        this.dataColumnPairService = dataColumnPairService;\n    }\n\n    public void setDataColumnPairGroupService(DataColumnPairGroupService dataColumnPairGroupService) {\n        this.dataColumnPairGroupService = dataColumnPairGroupService;\n    }\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/datamediasource/DataMediaSourceService.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.config.datamediasource;\n\nimport com.alibaba.otter.shared.common.model.config.data.DataMediaSource;\nimport com.alibaba.otter.manager.biz.common.baseservice.GenericService;\n\n/**\n * @author simon\n */\npublic interface DataMediaSourceService extends GenericService<DataMediaSource> {\n\n    // public List<DataMediaSource> listDataMediaSourceById(Long... dataMediaSourceIds);\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/datamediasource/dal/DataMediaSourceDAO.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.config.datamediasource.dal;\n\nimport com.alibaba.otter.manager.biz.common.basedao.GenericDAO;\nimport com.alibaba.otter.manager.biz.config.datamediasource.dal.dataobject.DataMediaSourceDO;\n\n/**\n * @author simon\n */\npublic interface DataMediaSourceDAO extends GenericDAO<DataMediaSourceDO> {\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/datamediasource/dal/dataobject/DataMediaSourceDO.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.config.datamediasource.dal.dataobject;\n\nimport java.io.Serializable;\nimport java.util.Date;\n\nimport com.alibaba.otter.shared.common.model.config.data.DataMediaType;\n\n/**\n * @author simon\n */\npublic class DataMediaSourceDO implements Serializable {\n\n    private static final long serialVersionUID = 5123273832849527936L;\n    private Long              id;\n    private String            name;\n    private DataMediaType     type;\n    private String            properties;\n    private Date              gmtCreate;\n    private Date              gmtModified;\n\n    public Long getId() {\n        return id;\n    }\n\n    public void setId(Long id) {\n        this.id = id;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public void setName(String name) {\n        this.name = name;\n    }\n\n    public DataMediaType getType() {\n        return type;\n    }\n\n    public void setType(DataMediaType type) {\n        this.type = type;\n    }\n\n    public Date getGmtCreate() {\n        return gmtCreate;\n    }\n\n    public void setGmtCreate(Date gmtCreate) {\n        this.gmtCreate = gmtCreate;\n    }\n\n    public Date getGmtModified() {\n        return gmtModified;\n    }\n\n    public void setGmtModified(Date gmtModified) {\n        this.gmtModified = gmtModified;\n    }\n\n    public String getProperties() {\n        return properties;\n    }\n\n    public void setProperties(String properties) {\n        this.properties = properties;\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/datamediasource/dal/ibatis/IbatisDataMediaSourceDAO.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.config.datamediasource.dal.ibatis;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.springframework.orm.ibatis.support.SqlMapClientDaoSupport;\n\nimport com.alibaba.otter.shared.common.utils.Assert;\nimport com.alibaba.otter.manager.biz.config.datamediasource.dal.DataMediaSourceDAO;\nimport com.alibaba.otter.manager.biz.config.datamediasource.dal.dataobject.DataMediaSourceDO;\n\n/**\n * DataMediaSource的DAO层，ibatis的实现，主要是CRUD操作。\n * \n * @author simon\n */\npublic class IbatisDataMediaSourceDAO extends SqlMapClientDaoSupport implements DataMediaSourceDAO {\n\n    public DataMediaSourceDO insert(DataMediaSourceDO dataMediaSourceDO) {\n        Assert.assertNotNull(dataMediaSourceDO);\n        getSqlMapClientTemplate().insert(\"insertDataMediaSource\", dataMediaSourceDO);\n        return dataMediaSourceDO;\n    }\n\n    public void delete(Long dataMediaSourceId) {\n        Assert.assertNotNull(dataMediaSourceId);\n        getSqlMapClientTemplate().delete(\"deleteDataMediaSourceById\", dataMediaSourceId);\n    }\n\n    public void update(DataMediaSourceDO dataMediaSourceDO) {\n        Assert.assertNotNull(dataMediaSourceDO);\n        getSqlMapClientTemplate().update(\"updateDataMediaSource\", dataMediaSourceDO);\n    }\n\n    public boolean checkUnique(DataMediaSourceDO dataMediaSourceDO) {\n        int count = (Integer) getSqlMapClientTemplate().queryForObject(\"checkDataMediaSourceUnique\", dataMediaSourceDO);\n        return count == 0 ? true : false;\n    }\n\n    public DataMediaSourceDO findById(Long dataMediaSourceId) {\n        Assert.assertNotNull(dataMediaSourceId);\n        return (DataMediaSourceDO) getSqlMapClientTemplate().queryForObject(\"findDataMediaSourceById\",\n                                                                            dataMediaSourceId);\n    }\n\n    public List<DataMediaSourceDO> listByCondition(Map condition) {\n        List<DataMediaSourceDO> dataMediaSourceDos = getSqlMapClientTemplate().queryForList(\"listDataMediaSources\",\n                                                                                            condition);\n        return dataMediaSourceDos;\n    }\n\n    public List<DataMediaSourceDO> listAll() {\n\n        return (List<DataMediaSourceDO>) getSqlMapClientTemplate().queryForList(\"listDataMediaSources\");\n    }\n\n    public List<DataMediaSourceDO> listByMultiId(Long... identities) {\n        List<DataMediaSourceDO> dataMediaSourceDos = getSqlMapClientTemplate().queryForList(\"listSourceByIds\",\n                                                                                            identities);\n        return dataMediaSourceDos;\n    }\n\n    public int getCount() {\n        Integer count = (Integer) getSqlMapClientTemplate().queryForObject(\"getSourceCount\");\n        return count.intValue();\n    }\n\n    public int getCount(Map condition) {\n        Integer count = (Integer) getSqlMapClientTemplate().queryForObject(\"getSourceCount\", condition);\n        return count.intValue();\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/datamediasource/impl/DataMediaSourceServiceImpl.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.config.datamediasource.impl;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.alibaba.otter.shared.common.utils.Assert;\nimport com.alibaba.otter.manager.biz.common.exceptions.ManagerException;\nimport com.alibaba.otter.manager.biz.common.exceptions.RepeatConfigureException;\nimport com.alibaba.otter.manager.biz.config.datamediasource.DataMediaSourceService;\nimport com.alibaba.otter.manager.biz.config.datamediasource.dal.DataMediaSourceDAO;\nimport com.alibaba.otter.manager.biz.config.datamediasource.dal.dataobject.DataMediaSourceDO;\nimport com.alibaba.otter.shared.common.model.config.data.DataMediaSource;\nimport com.alibaba.otter.shared.common.model.config.data.db.DbMediaSource;\nimport com.alibaba.otter.shared.common.model.config.data.mq.MqMediaSource;\nimport com.alibaba.otter.shared.common.utils.JsonUtils;\n\n/**\n * @author simon\n */\npublic class DataMediaSourceServiceImpl implements DataMediaSourceService {\n\n    private static final Logger logger = LoggerFactory.getLogger(DataMediaSourceServiceImpl.class);\n\n    private DataMediaSourceDAO  dataMediaSourceDao;\n\n    /**\n     * 添加\n     */\n    public void create(DataMediaSource dataMediaSource) {\n        Assert.assertNotNull(dataMediaSource);\n        try {\n            DataMediaSourceDO dataMediaSourceDo = modelToDo(dataMediaSource);\n            dataMediaSourceDo.setId(0L);\n\n            if (!dataMediaSourceDao.checkUnique(dataMediaSourceDo)) {\n                String exceptionCause = \"exist the same name source in the database.\";\n                logger.warn(\"WARN ## \" + exceptionCause);\n                throw new RepeatConfigureException(exceptionCause);\n            }\n\n            dataMediaSourceDao.insert(dataMediaSourceDo);\n\n        } catch (RepeatConfigureException rce) {\n            throw rce;\n        } catch (Exception e) {\n            logger.error(\"ERROR ## create dataMediaSource has an exception!\");\n            throw new ManagerException(e);\n        }\n    }\n\n    /**\n     * 删除\n     */\n    public void remove(Long dataMediaSourceId) {\n        Assert.assertNotNull(dataMediaSourceId);\n\n        try {\n            dataMediaSourceDao.delete(dataMediaSourceId);\n        } catch (Exception e) {\n            logger.error(\"ERROR ## remove dataMediaSource has an exception!\");\n            throw new ManagerException(e);\n        }\n\n    }\n\n    /**\n     * 修改\n     */\n    public void modify(DataMediaSource dataMediaSource) {\n        Assert.assertNotNull(dataMediaSource);\n\n        try {\n            DataMediaSourceDO dataMediaSourceDo = modelToDo(dataMediaSource);\n            if (dataMediaSourceDao.checkUnique(dataMediaSourceDo)) {\n                dataMediaSourceDao.update(dataMediaSourceDo);\n            } else {\n                String exceptionCause = \"exist the same name source in the database.\";\n                logger.warn(\"WARN ## \" + exceptionCause);\n                throw new RepeatConfigureException(exceptionCause);\n            }\n        } catch (RepeatConfigureException rce) {\n            throw rce;\n        } catch (Exception e) {\n            logger.error(\"ERROR ## modify dataMediaSource has an exception!\");\n            throw new ManagerException(e);\n        }\n    }\n\n    /**\n     * 查出所有的DataMediaSource\n     */\n    public List<DataMediaSource> listAll() {\n\n        return listByIds();\n    }\n\n    @Override\n    public List<DataMediaSource> listByCondition(Map condition) {\n        List<DataMediaSource> dataMediaSources = new ArrayList<DataMediaSource>();\n        try {\n            List<DataMediaSourceDO> dataMediaSourceDos = dataMediaSourceDao.listByCondition(condition);\n            if (dataMediaSourceDos.isEmpty()) {\n                logger.debug(\"DEBUG ## couldn't query any DataMediaSources by the condition:\"\n                             + JsonUtils.marshalToString(condition));\n                return dataMediaSources;\n            }\n            dataMediaSources = doToModel(dataMediaSourceDos);\n        } catch (Exception e) {\n            logger.error(\"ERROR ## query DataMediaSources by condition has an exception!\");\n            throw new ManagerException(e);\n        }\n\n        return dataMediaSources;\n    }\n\n    public List<DataMediaSource> listByIds(Long... identities) {\n\n        List<DataMediaSourceDO> dataMediaSourceDos = null;\n        if (identities.length < 1) {\n            dataMediaSourceDos = dataMediaSourceDao.listAll();\n            if (dataMediaSourceDos.isEmpty()) {\n                logger.debug(\"DEBUG ## couldn't query any dataMediaSource, maybe hasn't create any dataMediaSource.\");\n                return new ArrayList<DataMediaSource>();\n            }\n        } else {\n            dataMediaSourceDos = dataMediaSourceDao.listByMultiId(identities);\n            if (dataMediaSourceDos.isEmpty()) {\n                String exceptionCause = \"couldn't query any dataMediaSource by dataMediaSourceIds:\"\n                                        + Arrays.toString(identities);\n                logger.error(\"ERROR ## \" + exceptionCause);\n                throw new ManagerException(exceptionCause);\n            }\n        }\n\n        return doToModel(dataMediaSourceDos);\n    }\n\n    /**\n     * 根据DataMediaSourceId找到对应的DataMediaSource\n     */\n    public DataMediaSource findById(Long dataMediaSourceId) {\n        Assert.assertNotNull(dataMediaSourceId);\n        List<DataMediaSource> dataMediaSources = listByIds(dataMediaSourceId);\n        if (dataMediaSources.size() != 1) {\n            String exceptionCause = \"query dataMediaSourceId:\" + dataMediaSourceId + \" but return \"\n                                    + dataMediaSources.size() + \" dataMediaSource.\";\n            logger.error(\"ERROR ## \" + exceptionCause);\n            throw new ManagerException(exceptionCause);\n        }\n        return dataMediaSources.get(0);\n\n    }\n\n    public int getCount() {\n        return dataMediaSourceDao.getCount();\n    }\n\n    public int getCount(Map condition) {\n        return dataMediaSourceDao.getCount(condition);\n    }\n\n    /**\n     * 类型：数据库类型 Mysql和Oracle 用于Model对象转化为DO对象\n     * \n     * @param dataMediaSource\n     * @return DataMediaSourceDO\n     */\n    private DataMediaSourceDO modelToDo(DataMediaSource dataMediaSource) {\n        DataMediaSourceDO dataMediaSourceDo = new DataMediaSourceDO();\n        try {\n            dataMediaSourceDo.setId(dataMediaSource.getId());\n            dataMediaSourceDo.setName(dataMediaSource.getName());\n            dataMediaSourceDo.setType(dataMediaSource.getType());\n            if (dataMediaSource instanceof DbMediaSource) {\n                dataMediaSourceDo.setProperties(JsonUtils.marshalToString((DbMediaSource) dataMediaSource));\n            } else if (dataMediaSource instanceof MqMediaSource) {\n                dataMediaSourceDo.setProperties(JsonUtils.marshalToString((MqMediaSource) dataMediaSource));\n            }\n\n            dataMediaSourceDo.setGmtCreate(dataMediaSource.getGmtCreate());\n            dataMediaSourceDo.setGmtModified(dataMediaSource.getGmtModified());\n        } catch (Exception e) {\n            logger.error(\"ERROR ## change the dataMediaSource Model to Do has an exception\");\n            throw new ManagerException(e);\n        }\n\n        return dataMediaSourceDo;\n    }\n\n    /**\n     * 类型：数据库类型 Mysql和Oracle 用于DO对象转化为Model对象\n     * \n     * @param dataMediaSourceDo\n     * @return DataMediaSource\n     */\n    private DataMediaSource doToModel(DataMediaSourceDO dataMediaSourceDo) {\n\n        DataMediaSource dataMediaSource = new DbMediaSource();\n        try {\n            if (dataMediaSourceDo.getType().isMysql() || dataMediaSourceDo.getType().isOracle()) {\n                dataMediaSource = JsonUtils.unmarshalFromString(dataMediaSourceDo.getProperties(), DbMediaSource.class);\n            } else if (dataMediaSourceDo.getType().isNapoli() || dataMediaSourceDo.getType().isMq()) {\n                dataMediaSource = JsonUtils.unmarshalFromString(dataMediaSourceDo.getProperties(), MqMediaSource.class);\n            }\n\n            dataMediaSource.setId(dataMediaSourceDo.getId());\n            dataMediaSource.setGmtCreate(dataMediaSourceDo.getGmtCreate());\n            dataMediaSource.setGmtModified(dataMediaSourceDo.getGmtModified());\n        } catch (Exception e) {\n            logger.error(\"ERROR ## change the dataMediaSource Do to Model has an exception\");\n            throw new ManagerException(e);\n        }\n\n        return dataMediaSource;\n    }\n\n    private List<DataMediaSource> doToModel(List<DataMediaSourceDO> dataMediaSourceDos) {\n        List<DataMediaSource> dataMediaSources = new ArrayList<DataMediaSource>();\n        for (DataMediaSourceDO dataMediaSourceDo : dataMediaSourceDos) {\n            dataMediaSources.add(doToModel(dataMediaSourceDo));\n        }\n        return dataMediaSources;\n    }\n\n    /* ------------------------setter / getter--------------------------- */\n\n    public void setDataMediaSourceDao(DataMediaSourceDAO dataMediaSourceDao) {\n        this.dataMediaSourceDao = dataMediaSourceDao;\n    }\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/node/NodeService.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.config.node;\n\nimport com.alibaba.otter.shared.common.model.config.node.Node;\nimport com.alibaba.otter.manager.biz.common.baseservice.GenericService;\n\n/**\n * @author simon\n */\npublic interface NodeService extends GenericService<Node> {\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/node/dal/NodeDAO.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.config.node.dal;\n\nimport com.alibaba.otter.manager.biz.common.basedao.GenericDAO;\nimport com.alibaba.otter.manager.biz.config.node.dal.dataobject.NodeDO;\n\n/**\n * @author simon\n */\npublic interface NodeDAO extends GenericDAO<NodeDO> {\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/node/dal/dataobject/NodeDO.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.config.node.dal.dataobject;\n\nimport java.io.Serializable;\nimport java.util.Date;\n\nimport com.alibaba.otter.shared.common.model.config.node.NodeParameter;\nimport com.alibaba.otter.shared.common.model.config.node.NodeStatus;\n\n/**\n * @author simon\n */\npublic class NodeDO implements Serializable {\n\n    private static final long serialVersionUID = 9148286590254926037L;\n    private Long              id;                                     // 唯一标示id\n    private String            name;                                   // 机器名字\n    private String            ip;                                     // 机器ip\n    private Long              port;                                   // 和manager对应的通讯端口\n    private NodeStatus        status;                                 // 对应状态\n    private String            description;                            // 详细描述\n    private NodeParameter     parameters;                             // node对应参数信息\n    private Date              gmtCreate;                              // 创建时间\n    private Date              gmtModified;                            // 修改时间\n\n    public Long getId() {\n        return id;\n    }\n\n    public void setId(Long id) {\n        this.id = id;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public void setName(String name) {\n        this.name = name;\n    }\n\n    public String getIp() {\n        return ip;\n    }\n\n    public void setIp(String ip) {\n        this.ip = ip;\n    }\n\n    public Long getPort() {\n        return port;\n    }\n\n    public void setPort(Long port) {\n        this.port = port;\n    }\n\n    public NodeStatus getStatus() {\n        return status;\n    }\n\n    public void setStatus(NodeStatus status) {\n        this.status = status;\n    }\n\n    public String getDescription() {\n        return description;\n    }\n\n    public void setDescription(String description) {\n        this.description = description;\n    }\n\n    public NodeParameter getParameters() {\n        return parameters;\n    }\n\n    public void setParameters(NodeParameter parameters) {\n        this.parameters = parameters;\n    }\n\n    public Date getGmtCreate() {\n        return gmtCreate;\n    }\n\n    public void setGmtCreate(Date gmtCreate) {\n        this.gmtCreate = gmtCreate;\n    }\n\n    public Date getGmtModified() {\n        return gmtModified;\n    }\n\n    public void setGmtModified(Date gmtModified) {\n        this.gmtModified = gmtModified;\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/node/dal/ibatis/IbatisNodeDAO.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.config.node.dal.ibatis;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.springframework.orm.ibatis.support.SqlMapClientDaoSupport;\n\nimport com.alibaba.otter.shared.common.utils.Assert;\nimport com.alibaba.otter.manager.biz.config.node.dal.NodeDAO;\nimport com.alibaba.otter.manager.biz.config.node.dal.dataobject.NodeDO;\n\n/**\n * Node的DAO层，ibatis的实现，主要是CRUD操作。\n * \n * @author simon\n */\npublic class IbatisNodeDAO extends SqlMapClientDaoSupport implements NodeDAO {\n\n    public NodeDO insert(NodeDO node) {\n        Assert.assertNotNull(node);\n        getSqlMapClientTemplate().insert(\"insertNode\", node);\n        return node;\n    }\n\n    public void delete(Long nodeId) {\n        Assert.assertNotNull(nodeId);\n        getSqlMapClientTemplate().delete(\"deleteNodeById\", nodeId);\n    }\n\n    public void update(NodeDO node) {\n        Assert.assertNotNull(node);\n        getSqlMapClientTemplate().update(\"updateNode\", node);\n    }\n\n    public boolean checkUnique(NodeDO node) {\n        int count = (Integer) getSqlMapClientTemplate().queryForObject(\"checkNodeUnique\", node);\n        return count == 0 ? true : false;\n    }\n\n    public List<NodeDO> listByCondition(Map condition) {\n        List<NodeDO> nodeDos = getSqlMapClientTemplate().queryForList(\"listNodes\", condition);\n        return nodeDos;\n    }\n\n    public NodeDO findById(Long nodeId) {\n        Assert.assertNotNull(nodeId);\n        return (NodeDO) getSqlMapClientTemplate().queryForObject(\"findNodeById\", nodeId);\n    }\n\n    public List<NodeDO> listAll() {\n        return (List<NodeDO>) getSqlMapClientTemplate().queryForList(\"listNodes\");\n    }\n\n    public List<NodeDO> listByMultiId(Long... identities) {\n        List<NodeDO> nodeDos = getSqlMapClientTemplate().queryForList(\"listNodeByIds\", identities);\n        return nodeDos;\n    }\n\n    public int getCount() {\n        Integer count = (Integer) getSqlMapClientTemplate().queryForObject(\"getNodeCount\");\n        return count.intValue();\n    }\n\n    public int getCount(Map condition) {\n        Integer count = (Integer) getSqlMapClientTemplate().queryForObject(\"getNodeCount\", condition);\n        return count.intValue();\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/node/dal/ibatis/NodeParameterTypeHandler.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.config.node.dal.ibatis;\n\nimport java.sql.SQLException;\n\nimport com.alibaba.otter.shared.common.model.config.node.NodeParameter;\nimport com.alibaba.otter.shared.common.utils.JsonUtils;\nimport com.ibatis.sqlmap.client.extensions.ParameterSetter;\nimport com.ibatis.sqlmap.client.extensions.ResultGetter;\nimport com.ibatis.sqlmap.client.extensions.TypeHandlerCallback;\n\n/**\n * 用于NodeParameter的解析\n * \n * @author simon\n */\npublic class NodeParameterTypeHandler implements TypeHandlerCallback {\n\n    @Override\n    public void setParameter(ParameterSetter setter, Object parameter) throws SQLException {\n        setter.setString(JsonUtils.marshalToString(parameter));\n    }\n\n    @Override\n    public Object getResult(ResultGetter getter) throws SQLException {\n        return JsonUtils.unmarshalFromString(getter.getString(), NodeParameter.class);\n    }\n\n    public Object valueOf(String s) {\n        return JsonUtils.unmarshalFromString(s, NodeParameter.class);\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/node/impl/NodeServiceImpl.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.config.node.impl;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.transaction.TransactionStatus;\nimport org.springframework.transaction.support.TransactionCallbackWithoutResult;\nimport org.springframework.transaction.support.TransactionTemplate;\n\nimport com.alibaba.otter.manager.biz.common.exceptions.ManagerException;\nimport com.alibaba.otter.manager.biz.common.exceptions.RepeatConfigureException;\nimport com.alibaba.otter.manager.biz.config.autokeeper.AutoKeeperClusterService;\nimport com.alibaba.otter.manager.biz.config.node.NodeService;\nimport com.alibaba.otter.manager.biz.config.node.dal.NodeDAO;\nimport com.alibaba.otter.manager.biz.config.node.dal.dataobject.NodeDO;\nimport com.alibaba.otter.shared.arbitrate.ArbitrateManageService;\nimport com.alibaba.otter.shared.common.model.autokeeper.AutoKeeperCluster;\nimport com.alibaba.otter.shared.common.model.config.node.Node;\nimport com.alibaba.otter.shared.common.model.config.node.NodeParameter;\nimport com.alibaba.otter.shared.common.model.config.node.NodeStatus;\nimport com.alibaba.otter.shared.common.utils.Assert;\nimport com.alibaba.otter.shared.common.utils.JsonUtils;\n\n/**\n * @author simon\n */\npublic class NodeServiceImpl implements NodeService {\n\n    private static final Logger      logger = LoggerFactory.getLogger(NodeServiceImpl.class);\n\n    private NodeDAO                  nodeDao;\n    private TransactionTemplate      transactionTemplate;\n    private ArbitrateManageService   arbitrateManageService;\n    private AutoKeeperClusterService autoKeeperClusterService;\n\n    /**\n     * 添加\n     */\n    public void create(final Node node) {\n        Assert.assertNotNull(node);\n        transactionTemplate.execute(new TransactionCallbackWithoutResult() {\n\n            protected void doInTransactionWithoutResult(TransactionStatus status) {\n\n                try {\n                    NodeDO nodeDo = modelToDo(node);\n                    nodeDo.setId(0L);\n                    if (!nodeDao.checkUnique(nodeDo)) {\n                        String exceptionCause = \"exist the same repeat node in the database.\";\n                        logger.warn(\"WARN ## \" + exceptionCause);\n                        throw new RepeatConfigureException(exceptionCause);\n                    }\n                    nodeDao.insert(nodeDo);\n\n                } catch (RepeatConfigureException rce) {\n                    throw rce;\n                } catch (Exception e) {\n                    logger.error(\"ERROR ## create node has an exception!\");\n                    throw new ManagerException(e);\n                }\n            }\n        });\n    }\n\n    /**\n     * 删除\n     */\n    public void remove(final Long nodeId) {\n        Assert.assertNotNull(nodeId);\n        transactionTemplate.execute(new TransactionCallbackWithoutResult() {\n\n            protected void doInTransactionWithoutResult(TransactionStatus status) {\n\n                try {\n                    nodeDao.delete(nodeId);\n                } catch (Exception e) {\n                    logger.error(\"ERROR ## remove node(\" + nodeId + \") has an exception!\");\n                    throw new ManagerException(e);\n                }\n            }\n        });\n\n    }\n\n    /**\n     * 修改\n     */\n    public void modify(final Node node) {\n        Assert.assertNotNull(node);\n        transactionTemplate.execute(new TransactionCallbackWithoutResult() {\n\n            protected void doInTransactionWithoutResult(TransactionStatus status) {\n\n                try {\n                    NodeDO nodeDo = modelToDo(node);\n                    if (nodeDao.checkUnique(nodeDo)) {\n                        nodeDao.update(nodeDo);\n                    } else {\n                        String exceptionCause = \"exist the same repeat node in the database.\";\n                        logger.warn(\"WARN ## \" + exceptionCause);\n                        throw new RepeatConfigureException(exceptionCause);\n                    }\n                } catch (RepeatConfigureException rce) {\n                    throw rce;\n                } catch (Exception e) {\n                    logger.error(\"ERROR ## modify node(\" + node.getId() + \") has an exception!\");\n                    throw new ManagerException(e);\n                }\n            }\n        });\n\n    }\n\n    /**\n     * 查找出所有node\n     */\n    public List<Node> listAll() {\n\n        return listByIds();\n    }\n\n    /**\n     * 根据nodeid到找node\n     */\n    public Node findById(Long nodeId) {\n        Assert.assertNotNull(nodeId);\n        List<Node> nodes = listByIds(nodeId);\n        if (nodes.size() != 1) {\n            String exceptionCause = \"query nodeId:\" + nodeId + \" return null.\";\n            logger.error(\"ERROR ## \" + exceptionCause);\n            throw new ManagerException(exceptionCause);\n        }\n\n        return nodes.get(0);\n\n    }\n\n    public List<Node> listByIds(Long... identities) {\n        List<Node> nodes = new ArrayList<Node>();\n        try {\n            List<NodeDO> nodeDos = null;\n            if (identities.length < 1) {\n                nodeDos = nodeDao.listAll();\n                if (nodeDos.isEmpty()) {\n                    logger.debug(\"DEBUG ## couldn't query any node, maybe hasn't create any channel.\");\n                    return nodes;\n                }\n            } else {\n                nodeDos = nodeDao.listByMultiId(identities);\n                if (nodeDos.isEmpty()) {\n                    String exceptionCause = \"couldn't query any node by nodeIds:\" + Arrays.toString(identities);\n                    logger.error(\"ERROR ## \" + exceptionCause);\n                    throw new ManagerException(exceptionCause);\n                }\n            }\n            // 验证zk的node信息\n            List<Long> nodeIds = arbitrateManageService.nodeEvent().liveNodes();\n            for (NodeDO nodeDo : nodeDos) {\n                if (nodeIds.contains(nodeDo.getId())) {\n                    nodeDo.setStatus(NodeStatus.START);\n                } else {\n                    nodeDo.setStatus(NodeStatus.STOP);\n                }\n            }\n\n            nodes = doToModel(nodeDos);\n        } catch (Exception e) {\n            logger.error(\"ERROR ## query nodes has an exception!\");\n            throw new ManagerException(e);\n        }\n\n        return nodes;\n    }\n\n    public int getCount() {\n        return nodeDao.getCount();\n    }\n\n    public int getCount(Map condition) {\n        return nodeDao.getCount(condition);\n    }\n\n    public List<Node> listByCondition(Map condition) {\n        List<NodeDO> nodeDos = nodeDao.listByCondition(condition);\n        if (nodeDos.isEmpty()) {\n            logger.debug(\"DEBUG ## couldn't query any node by the condition:\" + JsonUtils.marshalToString(condition));\n            return new ArrayList<Node>();\n        }\n        // 验证zk的node信息\n        List<Long> nodeIds = arbitrateManageService.nodeEvent().liveNodes();\n        for (NodeDO nodeDo : nodeDos) {\n            if (null != nodeIds && nodeIds.contains(nodeDo.getId())) {\n                nodeDo.setStatus(NodeStatus.START);\n            } else {\n                nodeDo.setStatus(NodeStatus.STOP);\n            }\n        }\n        return doToModel(nodeDos);\n    }\n\n    /**\n     * 用于Model对象转化为DO对象\n     * \n     * @param node\n     * @return NodeDO\n     */\n    private NodeDO modelToDo(Node node) {\n        NodeDO nodeDo = new NodeDO();\n        try {\n            nodeDo.setId(node.getId());\n            nodeDo.setIp(node.getIp());\n            nodeDo.setName(node.getName());\n            nodeDo.setPort(node.getPort());\n            nodeDo.setDescription(node.getDescription());\n            nodeDo.setStatus(node.getStatus());\n            nodeDo.setParameters(node.getParameters());\n            nodeDo.setGmtCreate(node.getGmtCreate());\n            nodeDo.setGmtModified(node.getGmtModified());\n        } catch (Exception e) {\n            logger.error(\"ERROR ## change the node Model to Do has an exception\");\n            throw new ManagerException(e);\n        }\n        return nodeDo;\n    }\n\n    /**\n     * 用于DO对象转化为Model对象\n     * \n     * @param nodeDo\n     * @return Node\n     */\n    private Node doToModel(NodeDO nodeDo) {\n        Node node = new Node();\n        try {\n            node.setId(nodeDo.getId());\n            node.setIp(nodeDo.getIp());\n            node.setName(nodeDo.getName());\n            node.setPort(nodeDo.getPort());\n            node.setDescription(nodeDo.getDescription());\n            node.setStatus(nodeDo.getStatus());\n            // 处理下zk集群\n            NodeParameter parameter = nodeDo.getParameters();\n            if (parameter.getZkCluster() != null) {\n                AutoKeeperCluster zkCluster = autoKeeperClusterService.findAutoKeeperClusterById(parameter.getZkCluster().getId());\n                parameter.setZkCluster(zkCluster);\n            }\n\n            node.setParameters(parameter);\n            node.setGmtCreate(nodeDo.getGmtCreate());\n            node.setGmtModified(nodeDo.getGmtModified());\n        } catch (Exception e) {\n            logger.error(\"ERROR ## change the node Do to Model has an exception\");\n            throw new ManagerException(e);\n        }\n\n        return node;\n    }\n\n    private List<Node> doToModel(List<NodeDO> nodeDos) {\n        List<Node> nodes = new ArrayList<Node>();\n        for (NodeDO nodeDo : nodeDos) {\n            nodes.add(doToModel(nodeDo));\n        }\n\n        return nodes;\n    }\n\n    /* ------------------------setter / getter--------------------------- */\n\n    public void setNodeDao(NodeDAO nodeDao) {\n        this.nodeDao = nodeDao;\n    }\n\n    public void setTransactionTemplate(TransactionTemplate transactionTemplate) {\n        this.transactionTemplate = transactionTemplate;\n    }\n\n    public void setArbitrateManageService(ArbitrateManageService arbitrateManageService) {\n        this.arbitrateManageService = arbitrateManageService;\n    }\n\n    public void setAutoKeeperClusterService(AutoKeeperClusterService autoKeeperClusterService) {\n        this.autoKeeperClusterService = autoKeeperClusterService;\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/parameter/SystemParameterService.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.config.parameter;\n\nimport com.alibaba.otter.shared.common.model.config.parameter.SystemParameter;\n\n/**\n * @author sarah.lij 2012-4-13 下午04:28:00\n */\npublic interface SystemParameterService {\n\n    public void createOrUpdate(SystemParameter systemParameter);\n\n    public SystemParameter find();\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/parameter/dal/SystemParameterDAO.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.config.parameter.dal;\n\nimport com.alibaba.otter.manager.biz.common.basedao.GenericDAO;\nimport com.alibaba.otter.manager.biz.config.parameter.dal.dataobject.SystemParameterDO;\n\n/**\n * @author sarah.lij 2012-4-13 下午04:44:24\n */\npublic interface SystemParameterDAO extends GenericDAO<SystemParameterDO> {\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/parameter/dal/dataobject/SystemParameterDO.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.config.parameter.dal.dataobject;\n\nimport java.io.Serializable;\nimport java.util.Date;\n\nimport com.alibaba.otter.shared.common.model.config.parameter.SystemParameter;\n\n/**\n * @author sarah.lij 2012-4-13 下午04:46:04\n */\npublic class SystemParameterDO implements Serializable {\n\n    private static final long serialVersionUID = 9148286590254926037L;\n    private Long              id;                                     // 唯一标示id\n    private SystemParameter   value;                                  // 系统参数值\n    private Date              gmtCreate;                              // 创建时间\n    private Date              gmtModified;                            // 修改时间\n\n    public Long getId() {\n        return id;\n    }\n\n    public void setId(Long id) {\n        this.id = id;\n    }\n\n    public SystemParameter getValue() {\n        return value;\n    }\n\n    public void setValue(SystemParameter value) {\n        this.value = value;\n    }\n\n    public Date getGmtCreate() {\n        return gmtCreate;\n    }\n\n    public void setGmtCreate(Date gmtCreate) {\n        this.gmtCreate = gmtCreate;\n    }\n\n    public Date getGmtModified() {\n        return gmtModified;\n    }\n\n    public void setGmtModified(Date gmtModified) {\n        this.gmtModified = gmtModified;\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/parameter/dal/ibatis/IbatisSystemParameterDAO.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.config.parameter.dal.ibatis;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.springframework.orm.ibatis.support.SqlMapClientDaoSupport;\n\nimport com.alibaba.otter.shared.common.utils.Assert;\nimport com.alibaba.otter.manager.biz.config.parameter.dal.SystemParameterDAO;\nimport com.alibaba.otter.manager.biz.config.parameter.dal.dataobject.SystemParameterDO;\n\n/**\n * SystemParameter的DAO层，ibatis的实现，主要是CRUD操作。\n * \n * @author sarah.lij 2012-4-13 下午04:57:52\n */\npublic class IbatisSystemParameterDAO extends SqlMapClientDaoSupport implements SystemParameterDAO {\n\n    public SystemParameterDO insert(SystemParameterDO systemParameter) {\n        Assert.assertNotNull(systemParameter);\n        getSqlMapClientTemplate().insert(\"insertParameter\", systemParameter);\n        return systemParameter;\n    }\n\n    public void update(SystemParameterDO systemParameter) {\n        throw new UnsupportedOperationException();\n    }\n\n    public void delete(Long parameterId) {\n        throw new UnsupportedOperationException();\n    }\n\n    public boolean checkUnique(SystemParameterDO systemParameter) {\n        throw new UnsupportedOperationException();\n    }\n\n    public SystemParameterDO findById(Long parameterId) {\n        throw new UnsupportedOperationException();\n    }\n\n    public int getCount() {\n        throw new UnsupportedOperationException();\n    }\n\n    public int getCount(Map condition) {\n        throw new UnsupportedOperationException();\n    }\n\n    public List<SystemParameterDO> listAll() {\n        return (List<SystemParameterDO>) getSqlMapClientTemplate().queryForList(\"listParameters\");\n    }\n\n    public List<SystemParameterDO> listByCondition(Map condition) {\n        throw new UnsupportedOperationException();\n    }\n\n    public List<SystemParameterDO> listByMultiId(Long... identities) {\n        throw new UnsupportedOperationException();\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/parameter/dal/ibatis/SystemParameterTypeHandler.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.config.parameter.dal.ibatis;\n\nimport java.sql.SQLException;\n\nimport com.alibaba.otter.shared.common.model.config.parameter.SystemParameter;\nimport com.alibaba.otter.shared.common.utils.JsonUtils;\nimport com.ibatis.sqlmap.client.extensions.ParameterSetter;\nimport com.ibatis.sqlmap.client.extensions.ResultGetter;\nimport com.ibatis.sqlmap.client.extensions.TypeHandlerCallback;\n\n/**\n * 用于SystemParameter的解析\n * \n * @author simon\n */\npublic class SystemParameterTypeHandler implements TypeHandlerCallback {\n\n    @Override\n    public void setParameter(ParameterSetter setter, Object parameter) throws SQLException {\n        setter.setString(JsonUtils.marshalToString(parameter));\n    }\n\n    @Override\n    public Object getResult(ResultGetter getter) throws SQLException {\n        return JsonUtils.unmarshalFromString(getter.getString(), SystemParameter.class);\n    }\n\n    public Object valueOf(String s) {\n        return JsonUtils.unmarshalFromString(s, SystemParameter.class);\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/parameter/impl/SystemParameterServiceImpl.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.config.parameter.impl;\n\nimport java.util.Date;\nimport java.util.List;\n \nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.alibaba.otter.shared.common.utils.Assert;\nimport com.alibaba.otter.manager.biz.common.exceptions.ManagerException;\nimport com.alibaba.otter.manager.biz.config.parameter.SystemParameterService;\nimport com.alibaba.otter.manager.biz.config.parameter.dal.SystemParameterDAO;\nimport com.alibaba.otter.manager.biz.config.parameter.dal.dataobject.SystemParameterDO;\nimport com.alibaba.otter.shared.common.model.config.parameter.SystemParameter;\n\n/**\n * @author sarah.lij 2012-4-13 下午04:32:48\n */\npublic class SystemParameterServiceImpl implements SystemParameterService {\n\n    private static final Logger logger = LoggerFactory.getLogger(SystemParameterServiceImpl.class);\n\n    private SystemParameterDAO  systemParameterDao;\n\n    /**\n     * 添加\n     */\n    public void createOrUpdate(SystemParameter systemParameter) {\n        Assert.assertNotNull(systemParameter);\n        try {\n            SystemParameterDO systemParameterDo = modelToDo(systemParameter);\n            systemParameterDo.setId(1L);\n            systemParameterDao.insert(systemParameterDo); // 底层使用merge sql，不需要判断update\n        } catch (Exception e) {\n            logger.error(\"ERROR ## create SystemParameter has an exception!\");\n            throw new ManagerException(e);\n        }\n    }\n\n    public SystemParameter find() {\n        List<SystemParameterDO> systemParameterDos = systemParameterDao.listAll();\n        if (systemParameterDos.isEmpty()) {\n            logger.debug(\"DEBUG ## couldn't query any SystemParameter, maybe hasn't create any SystemParameter.\");\n            return new SystemParameter();\n        } else {\n            return doToModel(systemParameterDos.get(0));\n        }\n    }\n\n    /**\n     * 类型：数据库类型 Mysql和Oracle 用于Model对象转化为DO对象\n     * \n     * @param SystemParameter\n     * @return SystemParameterDO\n     */\n    private SystemParameterDO modelToDo(SystemParameter systemParameter) {\n        SystemParameterDO systemParameterDo = new SystemParameterDO();\n        systemParameterDo.setValue(systemParameter);\n        systemParameterDo.setGmtCreate(new Date());\n        systemParameterDo.setGmtModified(new Date());\n        return systemParameterDo;\n    }\n\n    /**\n     * 类型：数据库类型 Mysql和Oracle 用于DO对象转化为Model对象\n     * \n     * @param SystemParameterDo\n     * @return SystemParameter\n     */\n    private SystemParameter doToModel(SystemParameterDO systemParameterDo) {\n        return systemParameterDo.getValue();\n    }\n\n    /* ------------------------setter / getter--------------------------- */\n\n    public void setsystemParameterDao(SystemParameterDAO systemParameterDao) {\n        this.systemParameterDao = systemParameterDao;\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/pipeline/PipelineService.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.config.pipeline;\n\nimport java.util.List;\n\nimport com.alibaba.otter.manager.biz.common.baseservice.GenericService;\nimport com.alibaba.otter.shared.common.model.config.pipeline.Pipeline;\n\n/**\n * @author simon\n */\npublic interface PipelineService extends GenericService<Pipeline> {\n\n    public List<Pipeline> listByChannelIds(Long... channelIds);\n\n    public List<Pipeline> listByChannelIdsWithoutOther(Long... channelIds);\n\n    public List<Pipeline> listByChannelIdsWithoutColumn(Long... channelIds);\n\n    public List<Pipeline> listByNodeId(Long nodeId);\n\n    public boolean hasRelation(Long nodeId);\n\n    public List<Pipeline> listByDestinationWithoutOther(String destination);\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/pipeline/dal/PipelineDAO.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.config.pipeline.dal;\n\nimport java.util.List;\n\nimport com.alibaba.otter.manager.biz.common.basedao.GenericDAO;\nimport com.alibaba.otter.manager.biz.config.pipeline.dal.dataobject.PipelineDO;\n\n/**\n * @author simon\n */\npublic interface PipelineDAO extends GenericDAO<PipelineDO> {\n\n    public List<PipelineDO> listByChannelIds(Long... channelId);\n\n    public List<PipelineDO> listByDestinationCondition(String destination);\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/pipeline/dal/PipelineNodeRelationDAO.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.config.pipeline.dal;\n\nimport java.util.List;\n\nimport com.alibaba.otter.manager.biz.common.basedao.GenericDAO;\nimport com.alibaba.otter.manager.biz.config.pipeline.dal.dataobject.PipelineNodeRelationDO;\n\n/**\n * 考虑是否需要 类PipelineNodeRelationDAO.java的实现描述：TODO 类实现描述\n * \n * @author simon 2011-10-31 下午11:41:53\n */\npublic interface PipelineNodeRelationDAO extends GenericDAO<PipelineNodeRelationDO> {\n\n    public void insertBatch(List<PipelineNodeRelationDO> pipelineNodeRelationDos);\n\n    public void updateByNodeId(Long... nodeId);\n\n    public void deleteByPipelineId(Long pipelineId);\n\n    public void deleteByNodeId(Long... nodeId);\n\n    public List<PipelineNodeRelationDO> listByPipelineIds(Long... pipelineId);\n\n    public List<PipelineNodeRelationDO> listByNodeId(Long nodeId);\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/pipeline/dal/dataobject/PipelineDO.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.config.pipeline.dal.dataobject;\n\nimport java.io.Serializable;\nimport java.util.Date;\n\nimport com.alibaba.otter.shared.common.model.config.pipeline.PipelineParameter;\n\n/**\n * @author simon\n */\npublic class PipelineDO implements Serializable {\n\n    private static final long serialVersionUID = -4894036418246404446L;\n    private Long              id;\n    private String            name;\n    private PipelineParameter parameters;\n    private String            description;                             // 描述信息\n    private Long              channelId;                               // 对应关联的channel唯一标示id\n    private Date              gmtCreate;\n    private Date              gmtModified;\n\n    public Long getId() {\n        return id;\n    }\n\n    public void setId(Long id) {\n        this.id = id;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public void setName(String name) {\n        this.name = name;\n    }\n\n    public PipelineParameter getParameters() {\n        return parameters;\n    }\n\n    public void setParameters(PipelineParameter parameters) {\n        this.parameters = parameters;\n    }\n\n    public String getDescription() {\n        return description;\n    }\n\n    public void setDescription(String description) {\n        this.description = description;\n    }\n\n    public Long getChannelId() {\n        return channelId;\n    }\n\n    public void setChannelId(Long channelId) {\n        this.channelId = channelId;\n    }\n\n    public Date getGmtCreate() {\n        return gmtCreate;\n    }\n\n    public void setGmtCreate(Date gmtCreate) {\n        this.gmtCreate = gmtCreate;\n    }\n\n    public Date getGmtModified() {\n        return gmtModified;\n    }\n\n    public void setGmtModified(Date gmtModified) {\n        this.gmtModified = gmtModified;\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/pipeline/dal/dataobject/PipelineNodeRelationDO.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.config.pipeline.dal.dataobject;\n\nimport java.io.Serializable;\nimport java.util.Date;\n\npublic class PipelineNodeRelationDO implements Serializable {\n\n    private static final long serialVersionUID = -2066978336563209425L;\n    private Long              id;\n    private Long              nodeId;\n    private Long              PipelineId;\n    private Location          location;                                // 表示Node位于该pipeline的源或是目的\n    private Date              gmtCreate;\n    private Date              gmtModified;\n\n    public static enum Location {\n        SELECT, EXTRACT, LOAD;\n\n        public boolean isSelect() {\n            return this.equals(Location.SELECT);\n        }\n\n        public boolean isExtract() {\n            return this.equals(Location.EXTRACT);\n        }\n\n        public boolean isLoad() {\n            return this.equals(Location.LOAD);\n        }\n    }\n\n    public Long getId() {\n        return id;\n    }\n\n    public void setId(Long id) {\n        this.id = id;\n    }\n\n    public Long getNodeId() {\n        return nodeId;\n    }\n\n    public void setNodeId(Long nodeId) {\n        this.nodeId = nodeId;\n    }\n\n    public Long getPipelineId() {\n        return PipelineId;\n    }\n\n    public void setPipelineId(Long pipelineId) {\n        PipelineId = pipelineId;\n    }\n\n    public Location getLocation() {\n        return location;\n    }\n\n    public void setLocation(Location location) {\n        this.location = location;\n    }\n\n    public Date getGmtCreate() {\n        return gmtCreate;\n    }\n\n    public void setGmtCreate(Date gmtCreate) {\n        this.gmtCreate = gmtCreate;\n    }\n\n    public Date getGmtModified() {\n        return gmtModified;\n    }\n\n    public void setGmtModified(Date gmtModified) {\n        this.gmtModified = gmtModified;\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/pipeline/dal/ibatis/IbatisPipelineDAO.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.config.pipeline.dal.ibatis;\n\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.springframework.orm.ibatis.support.SqlMapClientDaoSupport;\n\nimport com.alibaba.otter.manager.biz.config.pipeline.dal.PipelineDAO;\nimport com.alibaba.otter.manager.biz.config.pipeline.dal.dataobject.PipelineDO;\nimport com.alibaba.otter.shared.common.utils.Assert;\n\n/**\n * Pipeline的DAO层，ibatis的实现，主要是CRUD操作。\n * \n * @author simon\n */\npublic class IbatisPipelineDAO extends SqlMapClientDaoSupport implements PipelineDAO {\n\n    public PipelineDO insert(PipelineDO pipelineDo) {\n        Assert.assertNotNull(pipelineDo);\n        getSqlMapClientTemplate().insert(\"insertPipeline\", pipelineDo);\n        return pipelineDo;\n    }\n\n    public void delete(Long pipelineId) {\n        Assert.assertNotNull(pipelineId);\n        getSqlMapClientTemplate().delete(\"deletePipelineById\", pipelineId);\n    }\n\n    public void update(PipelineDO pipelineDO) {\n        Assert.assertNotNull(pipelineDO);\n        getSqlMapClientTemplate().update(\"updatePipeline\", pipelineDO);\n    }\n\n    public boolean checkUnique(PipelineDO pipelineDO) {\n        int count = (Integer) getSqlMapClientTemplate().queryForObject(\"checkPipelineUnique\", pipelineDO);\n        return count == 0 ? true : false;\n    }\n\n    public PipelineDO findById(Long pipelineId) {\n        Assert.assertNotNull(pipelineId);\n        return (PipelineDO) getSqlMapClientTemplate().queryForObject(\"findPipelineById\", pipelineId);\n    }\n\n    public List<PipelineDO> listByChannelIds(Long... channelId) {\n        Assert.assertNotNull(channelId);\n        return (List<PipelineDO>) getSqlMapClientTemplate().queryForList(\"listPipelinesByChannelIds\", channelId);\n    }\n\n    public List<PipelineDO> listByCondition(Map condition) {\n        List<PipelineDO> pipelineDos = getSqlMapClientTemplate().queryForList(\"listPipelines\", condition);\n        return pipelineDos;\n    }\n\n    public List<PipelineDO> listAll() {\n        List<PipelineDO> pipelines = getSqlMapClientTemplate().queryForList(\"listPipelines\");\n        return pipelines;\n    }\n\n    public List<PipelineDO> listByMultiId(Long... identities) {\n        List<PipelineDO> pipelineDos = getSqlMapClientTemplate().queryForList(\"listPipelineByIds\", identities);\n        return pipelineDos;\n    }\n\n    public int getCount() {\n        Integer count = (Integer) getSqlMapClientTemplate().queryForObject(\"getPipelineCount\");\n        return count.intValue();\n    }\n\n    public int getCount(Map condition) {\n        Integer count = (Integer) getSqlMapClientTemplate().queryForObject(\"getPipelineCount\", condition);\n        return count.intValue();\n    }\n\n    public List<PipelineDO> listByDestinationCondition(String canalName) {\n        Map<String, String> map = new HashMap<String, String>();\n        map.put(\"searchKey\", canalName);\n        List<PipelineDO> pipelineDos = getSqlMapClientTemplate().queryForList(\"listByDestinationCondition\", map);\n        return pipelineDos;\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/pipeline/dal/ibatis/IbatisPipelineNodeRelationDAO.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.config.pipeline.dal.ibatis;\n\nimport java.sql.SQLException;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.springframework.orm.ibatis.support.SqlMapClientDaoSupport;\n\nimport com.alibaba.otter.shared.common.utils.Assert;\nimport com.alibaba.otter.manager.biz.config.pipeline.dal.PipelineNodeRelationDAO;\nimport com.alibaba.otter.manager.biz.config.pipeline.dal.dataobject.PipelineNodeRelationDO;\n\n/**\n * @author simon\n */\npublic class IbatisPipelineNodeRelationDAO extends SqlMapClientDaoSupport implements PipelineNodeRelationDAO {\n\n    public PipelineNodeRelationDO insert(PipelineNodeRelationDO pipelineNodeRelationDo) {\n        Assert.assertNotNull(pipelineNodeRelationDo);\n        getSqlMapClientTemplate().insert(\"insertPipelineNodeRelation\", pipelineNodeRelationDo);\n        return pipelineNodeRelationDo;\n    }\n\n    public void insertBatch(List<PipelineNodeRelationDO> pipelineNodeRelationDos) {\n        try {\n            getSqlMapClientTemplate().getSqlMapClient().startBatch();\n\n            Iterator it = pipelineNodeRelationDos.iterator();\n            while (it.hasNext()) {\n                PipelineNodeRelationDO pipelineNodeRelationDo = (PipelineNodeRelationDO) it.next();\n                getSqlMapClientTemplate().getSqlMapClient().insert(\"insertPipelineNodeRelation\", pipelineNodeRelationDo);\n            }\n\n            getSqlMapClientTemplate().getSqlMapClient().executeBatch();\n\n        } catch (SQLException ex1) {\n            ex1.printStackTrace();\n        }\n\n    }\n\n    public void delete(Long pipelineNodeRelationId) {\n        Assert.assertNotNull(pipelineNodeRelationId);\n        getSqlMapClientTemplate().delete(\"deletePipelineNodeRelationById\", pipelineNodeRelationId);\n    }\n\n    public void update(PipelineNodeRelationDO pipelineNodeRelationDo) {\n        Assert.assertNotNull(pipelineNodeRelationDo);\n        getSqlMapClientTemplate().update(\"updatePipelineNodeRelation\", pipelineNodeRelationDo);\n    }\n\n    public List<PipelineNodeRelationDO> listByPipelineIds(Long... pipelineId) {\n        Assert.assertNotNull(pipelineId);\n        return (List<PipelineNodeRelationDO>) getSqlMapClientTemplate().queryForList(\"listRelationsByPipelineIds\",\n                                                                                     pipelineId);\n    }\n\n    public List<PipelineNodeRelationDO> listByNodeId(Long nodeId) {\n        Assert.assertNotNull(nodeId);\n        return (List<PipelineNodeRelationDO>) getSqlMapClientTemplate().queryForList(\"listRelationsByNodeId\", nodeId);\n    }\n\n    public boolean checkUnique(PipelineNodeRelationDO entityObj) {\n        // TODO Auto-generated method stub\n        return false;\n    }\n\n    public List<PipelineNodeRelationDO> listByCondition(Map condition) {\n        // TODO Auto-generated method stub\n        return null;\n    }\n\n    public List<PipelineNodeRelationDO> listAll() {\n        // TODO Auto-generated method stub\n        return null;\n    }\n\n    public List<PipelineNodeRelationDO> listByMultiId(Long... identities) {\n        // TODO Auto-generated method stub\n        return null;\n    }\n\n    public PipelineNodeRelationDO findById(Long identity) {\n        // TODO Auto-generated method stub\n        return null;\n    }\n\n    public void updateByNodeId(Long... nodeId) {\n        // TODO Auto-generated method stub\n\n    }\n\n    public void deleteByPipelineId(Long pipelineId) {\n        getSqlMapClientTemplate().delete(\"deleteRelationByPipelineId\", pipelineId);\n    }\n\n    public void deleteByNodeId(Long... nodeId) {\n        // TODO Auto-generated method stub\n\n    }\n\n    public int getCount() {\n        Integer count = (Integer) getSqlMapClientTemplate().queryForObject(\"getRelationCount\");\n        return count.intValue();\n    }\n\n    public int getCount(Map condition) {\n        // TODO Auto-generated method stub\n        return 0;\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/pipeline/dal/ibatis/PipelineParameterTypeHandler.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.config.pipeline.dal.ibatis;\n\nimport java.sql.SQLException;\n\nimport com.alibaba.otter.shared.common.model.config.pipeline.PipelineParameter;\nimport com.alibaba.otter.shared.common.utils.JsonUtils;\nimport com.ibatis.sqlmap.client.extensions.ParameterSetter;\nimport com.ibatis.sqlmap.client.extensions.ResultGetter;\nimport com.ibatis.sqlmap.client.extensions.TypeHandlerCallback;\n\n/**\n * 用于PipelineParameter的解析\n * \n * @author simon\n */\npublic class PipelineParameterTypeHandler implements TypeHandlerCallback {\n\n    @Override\n    public void setParameter(ParameterSetter setter, Object parameter) throws SQLException {\n        setter.setString(JsonUtils.marshalToStringWithoutTransient(parameter));\n    }\n\n    @Override\n    public Object getResult(ResultGetter getter) throws SQLException {\n        return JsonUtils.unmarshalFromString(getter.getString(), PipelineParameter.class);\n    }\n\n    public Object valueOf(String s) {\n        return JsonUtils.unmarshalFromString(s, PipelineParameter.class);\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/pipeline/impl/PipelineServiceImpl.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.config.pipeline.impl;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.transaction.TransactionStatus;\nimport org.springframework.transaction.support.TransactionCallbackWithoutResult;\nimport org.springframework.transaction.support.TransactionTemplate;\n\nimport com.alibaba.otter.manager.biz.common.exceptions.ManagerException;\nimport com.alibaba.otter.manager.biz.common.exceptions.RepeatConfigureException;\nimport com.alibaba.otter.manager.biz.config.datamediapair.DataMediaPairService;\nimport com.alibaba.otter.manager.biz.config.node.NodeService;\nimport com.alibaba.otter.manager.biz.config.pipeline.PipelineService;\nimport com.alibaba.otter.manager.biz.config.pipeline.dal.PipelineDAO;\nimport com.alibaba.otter.manager.biz.config.pipeline.dal.PipelineNodeRelationDAO;\nimport com.alibaba.otter.manager.biz.config.pipeline.dal.dataobject.PipelineDO;\nimport com.alibaba.otter.manager.biz.config.pipeline.dal.dataobject.PipelineNodeRelationDO;\nimport com.alibaba.otter.manager.biz.config.pipeline.dal.dataobject.PipelineNodeRelationDO.Location;\nimport com.alibaba.otter.shared.arbitrate.ArbitrateManageService;\nimport com.alibaba.otter.shared.arbitrate.ArbitrateViewService;\nimport com.alibaba.otter.shared.common.model.config.data.DataMediaPair;\nimport com.alibaba.otter.shared.common.model.config.data.DataMediaPairComparable;\nimport com.alibaba.otter.shared.common.model.config.node.Node;\nimport com.alibaba.otter.shared.common.model.config.pipeline.Pipeline;\nimport com.alibaba.otter.shared.common.utils.Assert;\nimport com.alibaba.otter.shared.common.utils.JsonUtils;\n\n/**\n * @author simon\n */\npublic class PipelineServiceImpl implements PipelineService {\n\n    private static final Logger     logger = LoggerFactory.getLogger(PipelineServiceImpl.class);\n\n    private PipelineDAO             pipelineDao;\n    private PipelineNodeRelationDAO pipelineNodeRelationDao;\n    private DataMediaPairService    dataMediaPairService;\n    private NodeService             nodeService;\n    private TransactionTemplate     transactionTemplate;\n    private ArbitrateManageService  arbitrateManageService;\n    private ArbitrateViewService    arbitrateViewService;\n\n    /**\n     * 添加\n     */\n    public void create(final Pipeline pipeline) {\n        Assert.assertNotNull(pipeline);\n        transactionTemplate.execute(new TransactionCallbackWithoutResult() {\n\n            @Override\n            protected void doInTransactionWithoutResult(TransactionStatus status) {\n\n                try {\n                    PipelineDO pipelineDo = modelToDo(pipeline);\n                    pipelineDo.setId(0L);\n\n                    if (!pipelineDao.checkUnique(pipelineDo)) {\n                        String exceptionCause = \"exist the same name pipeline under the channel(\"\n                                                + pipelineDo.getChannelId() + \") in the database.\";\n                        logger.warn(\"WARN ## \" + exceptionCause);\n                        throw new RepeatConfigureException(exceptionCause);\n                    }\n                    pipelineDao.insert(pipelineDo);\n\n                    List<PipelineNodeRelationDO> pipelineNodeRelationDos = new ArrayList<PipelineNodeRelationDO>();\n\n                    for (Node node : pipeline.getSelectNodes()) {\n                        PipelineNodeRelationDO pipelineNodeRelationDo = new PipelineNodeRelationDO();\n                        pipelineNodeRelationDo.setPipelineId(pipelineDo.getId());\n                        pipelineNodeRelationDo.setNodeId(node.getId());\n                        pipelineNodeRelationDo.setLocation(Location.SELECT);\n                        pipelineNodeRelationDos.add(pipelineNodeRelationDo);\n                    }\n\n                    for (Node node : pipeline.getExtractNodes()) {\n                        PipelineNodeRelationDO pipelineNodeRelationDo = new PipelineNodeRelationDO();\n                        pipelineNodeRelationDo.setPipelineId(pipelineDo.getId());\n                        pipelineNodeRelationDo.setNodeId(node.getId());\n                        pipelineNodeRelationDo.setLocation(Location.EXTRACT);\n                        pipelineNodeRelationDos.add(pipelineNodeRelationDo);\n                    }\n\n                    for (Node node : pipeline.getLoadNodes()) {\n                        PipelineNodeRelationDO pipelineNodeRelationDo = new PipelineNodeRelationDO();\n                        pipelineNodeRelationDo.setPipelineId(pipelineDo.getId());\n                        pipelineNodeRelationDo.setNodeId(node.getId());\n                        pipelineNodeRelationDo.setLocation(Location.LOAD);\n                        pipelineNodeRelationDos.add(pipelineNodeRelationDo);\n                    }\n\n                    pipelineNodeRelationDao.insertBatch(pipelineNodeRelationDos);\n                    arbitrateManageService.pipelineEvent().init(pipelineDo.getChannelId(), pipelineDo.getId());\n                } catch (RepeatConfigureException rce) {\n                    throw rce;\n                } catch (Exception e) {\n                    logger.error(\"ERROR ## create pipeline has an exception!\");\n                    throw new ManagerException(e);\n                }\n            }\n        });\n\n    }\n\n    /**\n     * 修改\n     */\n    public void modify(Pipeline pipeline) {\n        Assert.assertNotNull(pipeline);\n        try {\n\n            PipelineDO pipelineDo = modelToDo(pipeline);\n\n            if (!pipelineDao.checkUnique(pipelineDo)) {\n                String exceptionCause = \"exist the same name pipeline under the channel(\" + pipelineDo.getChannelId()\n                                        + \") in the database.\";\n                logger.warn(\"WARN ## \" + exceptionCause);\n                throw new RepeatConfigureException(exceptionCause);\n            }\n\n            pipelineNodeRelationDao.deleteByPipelineId(pipelineDo.getId());\n\n            pipelineDao.update(pipelineDo);\n\n            List<PipelineNodeRelationDO> pipelineNodeRelationDos = new ArrayList<PipelineNodeRelationDO>();\n\n            for (Node node : pipeline.getSelectNodes()) {\n                PipelineNodeRelationDO pipelineNodeRelationDo = new PipelineNodeRelationDO();\n                pipelineNodeRelationDo.setPipelineId(pipelineDo.getId());\n                pipelineNodeRelationDo.setNodeId(node.getId());\n                pipelineNodeRelationDo.setLocation(Location.SELECT);\n                pipelineNodeRelationDos.add(pipelineNodeRelationDo);\n            }\n\n            for (Node node : pipeline.getExtractNodes()) {\n                PipelineNodeRelationDO pipelineNodeRelationDo = new PipelineNodeRelationDO();\n                pipelineNodeRelationDo.setPipelineId(pipelineDo.getId());\n                pipelineNodeRelationDo.setNodeId(node.getId());\n                pipelineNodeRelationDo.setLocation(Location.EXTRACT);\n                pipelineNodeRelationDos.add(pipelineNodeRelationDo);\n            }\n\n            for (Node node : pipeline.getLoadNodes()) {\n                PipelineNodeRelationDO pipelineNodeRelationDo = new PipelineNodeRelationDO();\n                pipelineNodeRelationDo.setPipelineId(pipelineDo.getId());\n                pipelineNodeRelationDo.setNodeId(node.getId());\n                pipelineNodeRelationDo.setLocation(Location.LOAD);\n                pipelineNodeRelationDos.add(pipelineNodeRelationDo);\n            }\n\n            pipelineNodeRelationDao.insertBatch(pipelineNodeRelationDos);\n        } catch (Exception e) {\n            logger.error(\"ERROR ## modify the pipeline(\" + pipeline.getId() + \") has an exception!\");\n            throw new ManagerException(e);\n        }\n    }\n\n    /**\n     * 删除\n     */\n    public void remove(final Long pipelineId) {\n        Assert.assertNotNull(pipelineId);\n        transactionTemplate.execute(new TransactionCallbackWithoutResult() {\n\n            @Override\n            protected void doInTransactionWithoutResult(TransactionStatus status) {\n                try {\n                    PipelineDO pipelineDO = pipelineDao.findById(pipelineId);\n                    if (pipelineDO != null) {\n                        pipelineDao.delete(pipelineId);\n                        pipelineNodeRelationDao.deleteByPipelineId(pipelineId);\n                        // 删除历史cursor\n                        String destination = pipelineDO.getParameters().getDestinationName();\n                        short clientId = pipelineDO.getId().shortValue();\n                        arbitrateViewService.removeCanal(destination, clientId);\n                        arbitrateManageService.pipelineEvent().destory(pipelineDO.getChannelId(), pipelineId);\n                    }\n                } catch (Exception e) {\n                    logger.error(\"ERROR ## remove the pipeline(\" + pipelineId + \") has an exception!\");\n                    throw new ManagerException(e);\n                }\n            }\n        });\n    }\n\n    public int getCount() {\n        return pipelineDao.getCount();\n    }\n\n    public int getCount(Map condition) {\n        return pipelineDao.getCount(condition);\n    }\n\n    /*-------------------------------------查询方法----------------------------------------------*/\n    /**\n     * 根据pipelineId找到pipeline\n     */\n    public Pipeline findById(Long pipelineId) {\n\n        Assert.assertNotNull(pipelineId);\n        List<Pipeline> pipeline = listByIds(pipelineId);\n\n        if (pipeline.size() != 1) {\n            String exceptionCause = \"query pipeline by pipelineId:\" + pipelineId + \" but return \" + pipeline.size()\n                                    + \" pipeline!\";\n            logger.error(\"ERROR ## \" + exceptionCause);\n            throw new ManagerException(exceptionCause);\n        }\n        return pipeline.get(0);\n    }\n\n    /**\n     * 更具channelId找到所属所有pipeline\n     */\n    public List<Pipeline> listByChannelIds(Long... channelId) {\n        Assert.assertNotNull(channelId);\n        List<Pipeline> pipelines = new ArrayList<Pipeline>();\n        try {\n\n            List<PipelineDO> pipelineDos = pipelineDao.listByChannelIds(channelId);\n            if (pipelineDos.isEmpty()) {\n                logger.debug(\"DEBUG ## query pipeline by channelId:\" + channelId + \" return null.\");\n                return pipelines;\n            }\n            pipelines = doToModel(pipelineDos);\n        } catch (Exception e) {\n            logger.error(\"ERROR ## query pipelines by channelIds:\" + channelId.toString() + \" has an exception!\");\n            throw new ManagerException(e);\n        }\n        return pipelines;\n    }\n\n    public List<Pipeline> listByChannelIdsWithoutOther(Long... channelIds) {\n        Assert.assertNotNull(channelIds);\n        List<Pipeline> pipelines = new ArrayList<Pipeline>();\n        try {\n\n            List<PipelineDO> pipelineDos = pipelineDao.listByChannelIds(channelIds);\n            if (pipelineDos.isEmpty()) {\n                logger.debug(\"DEBUG ## query pipeline by channelId:\" + channelIds + \" return null.\");\n                return pipelines;\n            }\n            pipelines = doToModelWithoutOther(pipelineDos);\n        } catch (Exception e) {\n            logger.error(\"ERROR ## query pipelines by channelIds:\" + channelIds.toString() + \" has an exception!\");\n            throw new ManagerException(e);\n        }\n        return pipelines;\n    }\n\n    public List<Pipeline> listByChannelIdsWithoutColumn(Long... channelIds) {\n        Assert.assertNotNull(channelIds);\n        List<Pipeline> pipelines = new ArrayList<Pipeline>();\n        try {\n\n            List<PipelineDO> pipelineDos = pipelineDao.listByChannelIds(channelIds);\n            if (pipelineDos.isEmpty()) {\n                logger.debug(\"DEBUG ## query pipeline by channelId:\" + channelIds + \" return null.\");\n                return pipelines;\n            }\n            pipelines = doToModelWithoutColumn(pipelineDos);\n        } catch (Exception e) {\n            logger.error(\"ERROR ## query pipelines by channelIds:\" + channelIds.toString() + \" has an exception!\");\n            throw new ManagerException(e);\n        }\n        return pipelines;\n    }\n\n    public List<Pipeline> listByNodeId(Long nodeId) {\n        Assert.assertNotNull(nodeId);\n        List<Pipeline> pipelines = new ArrayList<Pipeline>();\n        try {\n            List<PipelineNodeRelationDO> relations = pipelineNodeRelationDao.listByNodeId(nodeId);\n            if (relations.isEmpty()) {\n                logger.debug(\"DEBUG ## query the relation by nodeId:\" + nodeId\n                             + \" return null,maybe hasn't create any relations.\");\n                return pipelines;\n            }\n\n            List<Long> piplineIds = new ArrayList<Long>();\n            for (PipelineNodeRelationDO relation : relations) {\n                piplineIds.add(relation.getPipelineId());\n            }\n\n            List<PipelineDO> pipelineDos = pipelineDao.listByMultiId(piplineIds.toArray(new Long[piplineIds.size()]));\n            if (pipelineDos.isEmpty()) {\n                String exceptionCause = \"query the pipelines by pipelineIds:\" + piplineIds.toString() + \" return null!\";\n                logger.error(\"ERROR ## \" + exceptionCause);\n                throw new ManagerException(exceptionCause);\n            }\n\n            pipelines = doToModel(pipelineDos);\n        } catch (Exception e) {\n            logger.error(\"ERROR ## query the pipelines by nodeId:\" + nodeId + \" has an exception!\");\n            throw new ManagerException(e);\n        }\n\n        return pipelines;\n    }\n\n    @Override\n    public List<Pipeline> listByCondition(Map condition) {\n        List<PipelineDO> pipelineDos = pipelineDao.listByCondition(condition);\n        if (pipelineDos.isEmpty()) {\n            logger.debug(\"DEBUG ## couldn't query any pipelines by the condition:\"\n                         + JsonUtils.marshalToString(condition));\n            return new ArrayList<Pipeline>();\n        }\n        return doToModel(pipelineDos);\n    }\n\n    @Override\n    public List<Pipeline> listByIds(Long... identities) {\n\n        List<Pipeline> pipelines = new ArrayList<Pipeline>();\n        try {\n            List<PipelineDO> pipelineDos = new ArrayList<PipelineDO>();\n            if (identities.length < 1) {\n                pipelineDos = pipelineDao.listAll();\n                if (pipelineDos.isEmpty()) {\n                    logger.debug(\"DEBUG ## couldn't query any pipeline, maybe hasn't create any pipeline.\");\n                    return pipelines;\n                }\n            } else {\n                pipelineDos = pipelineDao.listByMultiId(identities);\n                if (pipelineDos.isEmpty()) {\n                    String exceptionCause = \"couldn't query any pipeline by pipelineIds:\" + Arrays.toString(identities);\n                    logger.error(\"ERROR ## \" + exceptionCause);\n                    throw new ManagerException(exceptionCause);\n                }\n            }\n            pipelines = doToModel(pipelineDos);\n        } catch (Exception e) {\n            logger.error(\"ERROR ## query pipelines has an exception!\");\n            throw new ManagerException(e);\n        }\n        return pipelines;\n    }\n\n    @Override\n    public List<Pipeline> listAll() {\n        return listByIds();\n    }\n\n    @Override\n    public boolean hasRelation(Long nodeId) {\n        List<PipelineNodeRelationDO> relations = pipelineNodeRelationDao.listByNodeId(nodeId);\n        if (relations.isEmpty()) {\n            return false;\n        } else {\n            return true;\n        }\n    }\n\n    public List<Pipeline> listByDestinationWithoutOther(String destination) {\n        Assert.assertNotNull(destination);\n        List<Pipeline> pipelines = new ArrayList<Pipeline>();\n        try {\n\n            List<PipelineDO> pipelineDos = pipelineDao.listByDestinationCondition(destination);\n            if (pipelineDos.isEmpty()) {\n                logger.debug(\"DEBUG ## query pipeline by destination:\" + destination + \" return null.\");\n                return pipelines;\n            }\n            pipelines = doToModelWithoutOther(pipelineDos);\n        } catch (Exception e) {\n            logger.error(\"ERROR ## query pipelines by destination:\" + destination + \" has an exception!\");\n            throw new ManagerException(e);\n        }\n        return pipelines;\n    }\n\n    /**\n     * 用于DO对象转化为Model对象\n     * \n     * @param pipelineDO\n     * @return Pipeline\n     */\n    private Pipeline doToModel(PipelineDO pipelineDo) {\n        Pipeline pipeline = new Pipeline();\n        try {\n            pipeline.setId(pipelineDo.getId());\n            pipeline.setName(pipelineDo.getName());\n            pipeline.setParameters(pipelineDo.getParameters());\n            pipeline.setDescription(pipelineDo.getDescription());\n            pipeline.setGmtCreate(pipelineDo.getGmtCreate());\n            pipeline.setGmtModified(pipelineDo.getGmtModified());\n            pipeline.setChannelId(pipelineDo.getChannelId());\n            pipeline.getParameters().setMainstemClientId(pipeline.getId().shortValue());\n\n            // 组装DatamediaPair\n            List<DataMediaPair> pairs = dataMediaPairService.listByPipelineId(pipelineDo.getId());\n            Collections.sort(pairs, new DataMediaPairComparable());\n            pipeline.setPairs(pairs);\n\n            // 组装Node\n            List<PipelineNodeRelationDO> relations = pipelineNodeRelationDao.listByPipelineIds(pipelineDo.getId());\n\n            List<Long> totalNodeIds = new ArrayList<Long>();\n\n            for (PipelineNodeRelationDO relation : relations) {\n                Long nodeId = relation.getNodeId();\n                if (!totalNodeIds.contains(nodeId)) {\n                    totalNodeIds.add(nodeId);\n                }\n            }\n\n            List<Node> totalNodes = nodeService.listByIds(totalNodeIds.toArray(new Long[totalNodeIds.size()]));\n            List<Node> selectNodes = new ArrayList<Node>();\n            List<Node> extractNodes = new ArrayList<Node>();\n            List<Node> loadNodes = new ArrayList<Node>();\n\n            for (Node node : totalNodes) {\n                for (PipelineNodeRelationDO relation : relations) {\n                    if (node.getId().equals(relation.getNodeId())) {\n                        if (relation.getLocation().isSelect()) {\n                            selectNodes.add(node);\n                        } else if (relation.getLocation().isExtract()) {\n                            extractNodes.add(node);\n                        } else if (relation.getLocation().isLoad()) {\n                            loadNodes.add(node);\n                        }\n                    }\n                }\n            }\n\n            pipeline.setSelectNodes(selectNodes);\n            pipeline.setExtractNodes(extractNodes);\n            pipeline.setLoadNodes(loadNodes);\n\n        } catch (Exception e) {\n            logger.error(\"ERROR ## change the pipeline Do to Model has an exception\");\n            throw new ManagerException(e);\n        }\n\n        return pipeline;\n    }\n\n    private Pipeline doToModelWithoutColumn(PipelineDO pipelineDo) {\n        Pipeline pipeline = new Pipeline();\n        try {\n            pipeline.setId(pipelineDo.getId());\n            pipeline.setName(pipelineDo.getName());\n            pipeline.setParameters(pipelineDo.getParameters());\n            pipeline.setDescription(pipelineDo.getDescription());\n            pipeline.setGmtCreate(pipelineDo.getGmtCreate());\n            pipeline.setGmtModified(pipelineDo.getGmtModified());\n            pipeline.setChannelId(pipelineDo.getChannelId());\n            pipeline.getParameters().setMainstemClientId(pipeline.getId().shortValue());\n\n            // 组装DatamediaPair\n            List<DataMediaPair> pairs = dataMediaPairService.listByPipelineIdWithoutColumn(pipelineDo.getId());\n            Collections.sort(pairs, new DataMediaPairComparable());\n            pipeline.setPairs(pairs);\n\n            // 组装Node\n            List<PipelineNodeRelationDO> relations = pipelineNodeRelationDao.listByPipelineIds(pipelineDo.getId());\n\n            List<Long> totalNodeIds = new ArrayList<Long>();\n\n            for (PipelineNodeRelationDO relation : relations) {\n                Long nodeId = relation.getNodeId();\n                if (!totalNodeIds.contains(nodeId)) {\n                    totalNodeIds.add(nodeId);\n                }\n            }\n\n            List<Node> totalNodes = nodeService.listByIds(totalNodeIds.toArray(new Long[totalNodeIds.size()]));\n            List<Node> selectNodes = new ArrayList<Node>();\n            List<Node> extractNodes = new ArrayList<Node>();\n            List<Node> loadNodes = new ArrayList<Node>();\n\n            for (Node node : totalNodes) {\n                for (PipelineNodeRelationDO relation : relations) {\n                    if (node.getId().equals(relation.getNodeId())) {\n                        if (relation.getLocation().isSelect()) {\n                            selectNodes.add(node);\n                        } else if (relation.getLocation().isExtract()) {\n                            extractNodes.add(node);\n                        } else if (relation.getLocation().isLoad()) {\n                            loadNodes.add(node);\n                        }\n                    }\n                }\n            }\n\n            pipeline.setSelectNodes(selectNodes);\n            pipeline.setExtractNodes(extractNodes);\n            pipeline.setLoadNodes(loadNodes);\n\n        } catch (Exception e) {\n            logger.error(\"ERROR ## change the pipeline Do to Model has an exception\");\n            throw new ManagerException(e);\n        }\n\n        return pipeline;\n    }\n\n    private Pipeline doToModelWithoutOther(PipelineDO pipelineDo) {\n        Pipeline pipeline = new Pipeline();\n        try {\n            pipeline.setId(pipelineDo.getId());\n            pipeline.setName(pipelineDo.getName());\n            pipeline.setParameters(pipelineDo.getParameters());\n            pipeline.setDescription(pipelineDo.getDescription());\n            pipeline.setGmtCreate(pipelineDo.getGmtCreate());\n            pipeline.setGmtModified(pipelineDo.getGmtModified());\n            pipeline.setChannelId(pipelineDo.getChannelId());\n            pipeline.getParameters().setMainstemClientId(pipeline.getId().shortValue());\n\n        } catch (Exception e) {\n            logger.error(\"ERROR ## change the pipeline Do to Model has an exception\");\n            throw new ManagerException(e);\n        }\n\n        return pipeline;\n    }\n\n    private List<Pipeline> doToModel(List<PipelineDO> pipelineDos) {\n        List<Pipeline> pipelines = new ArrayList<Pipeline>();\n        for (PipelineDO pipelineDo : pipelineDos) {\n            pipelines.add(doToModel(pipelineDo));\n        }\n        return pipelines;\n    }\n\n    private List<Pipeline> doToModelWithoutOther(List<PipelineDO> pipelineDos) {\n        List<Pipeline> pipelines = new ArrayList<Pipeline>();\n        for (PipelineDO pipelineDo : pipelineDos) {\n            pipelines.add(doToModelWithoutOther(pipelineDo));\n        }\n        return pipelines;\n    }\n\n    private List<Pipeline> doToModelWithoutColumn(List<PipelineDO> pipelineDos) {\n        List<Pipeline> pipelines = new ArrayList<Pipeline>();\n        for (PipelineDO pipelineDo : pipelineDos) {\n            pipelines.add(doToModelWithoutColumn(pipelineDo));\n        }\n        return pipelines;\n    }\n\n    /**\n     * 用于Model对象转化为DO对象\n     * \n     * @param pipeline\n     * @return PipelineDO\n     */\n    private PipelineDO modelToDo(Pipeline pipeline) {\n        PipelineDO pipelineDO = new PipelineDO();\n\n        try {\n            pipelineDO.setId(pipeline.getId());\n            pipelineDO.setName(pipeline.getName());\n            pipelineDO.setParameters(pipeline.getParameters());\n            pipelineDO.setDescription(pipeline.getDescription());\n            pipelineDO.setChannelId(pipeline.getChannelId());\n            pipelineDO.setGmtCreate(pipeline.getGmtCreate());\n            pipelineDO.setGmtModified(pipeline.getGmtModified());\n\n        } catch (Exception e) {\n            logger.error(\"ERROR ## change the pipeline Model to Do has an exception\");\n            throw new ManagerException(e);\n        }\n\n        return pipelineDO;\n    }\n\n    /* ------------------------setter / getter--------------------------- */\n    public void setPipelineDao(PipelineDAO pipelineDao) {\n        this.pipelineDao = pipelineDao;\n    }\n\n    public void setNodeService(NodeService nodeService) {\n        this.nodeService = nodeService;\n    }\n\n    public void setPipelineNodeRelationDao(PipelineNodeRelationDAO pipelineNodeRelationDao) {\n        this.pipelineNodeRelationDao = pipelineNodeRelationDao;\n    }\n\n    public void setDataMediaPairService(DataMediaPairService dataMediaPairService) {\n        this.dataMediaPairService = dataMediaPairService;\n    }\n\n    public void setTransactionTemplate(TransactionTemplate transactionTemplate) {\n        this.transactionTemplate = transactionTemplate;\n    }\n\n    public void setArbitrateManageService(ArbitrateManageService arbitrateManageService) {\n        this.arbitrateManageService = arbitrateManageService;\n    }\n\n    public void setArbitrateViewService(ArbitrateViewService arbitrateViewService) {\n        this.arbitrateViewService = arbitrateViewService;\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/record/LogRecordService.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.config.record;\n\nimport java.util.List;\n\nimport com.alibaba.otter.manager.biz.common.baseservice.GenericService;\nimport com.alibaba.otter.shared.common.model.config.record.LogRecord;\nimport com.alibaba.otter.shared.communication.core.model.Event;\n\n/**\n * @author simon 2012-6-15 下午1:49:17\n */\npublic interface LogRecordService extends GenericService<LogRecord> {\n\n    public void create(Event event);\n\n    public List<LogRecord> listByPipelineId(Long pipelineId);\n\n    public List<LogRecord> listByPipelineIdWithoutContent(Long pipelineId);\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/record/dal/LogRecordDAO.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.config.record.dal;\n\nimport java.util.List;\n\nimport com.alibaba.otter.manager.biz.common.basedao.GenericDAO;\nimport com.alibaba.otter.manager.biz.config.record.dal.dataobject.LogRecordDO;\n\n/**\n * 类LogRecordDao.java的实现描述：TODO 类实现描述\n * \n * @author simon 2012-6-15 下午1:50:01\n */\npublic interface LogRecordDAO extends GenericDAO<LogRecordDO> {\n\n    public List<LogRecordDO> listByPipelineId(Long pipelineId);\n\n    public List<LogRecordDO> listByPipelineIdWithoutContent(Long pipelineId);\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/record/dal/dataobject/LogRecordDO.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.config.record.dal.dataobject;\n\nimport java.io.Serializable;\nimport java.util.Date;\n\n/**\n * @author simon 2012-6-15 下午1:51:35\n */\npublic class LogRecordDO implements Serializable {\n\n    private static final long serialVersionUID = -4295402837089297629L;\n    private Long              id;\n    private Long              pipelineId;\n    private Long              channelId;\n    private Long              nid;\n    private String            title;\n    private String            message;\n    private Date              gmtCreate;\n    private Date              gmtModified;\n\n    public Long getId() {\n        return id;\n    }\n\n    public void setId(Long id) {\n        this.id = id;\n    }\n\n    public Long getPipelineId() {\n        return pipelineId;\n    }\n\n    public void setPipelineId(Long pipelineId) {\n        this.pipelineId = pipelineId;\n    }\n\n    public Long getChannelId() {\n        return channelId;\n    }\n\n    public void setChannelId(Long channelId) {\n        this.channelId = channelId;\n    }\n\n    public Long getNid() {\n        return nid;\n    }\n\n    public void setNid(Long nid) {\n        this.nid = nid;\n    }\n\n    public String getTitle() {\n        return title;\n    }\n\n    public void setTitle(String title) {\n        this.title = title;\n    }\n\n    public String getMessage() {\n        return message;\n    }\n\n    public void setMessage(String message) {\n        this.message = message;\n    }\n\n    public Date getGmtCreate() {\n        return gmtCreate;\n    }\n\n    public void setGmtCreate(Date gmtCreate) {\n        this.gmtCreate = gmtCreate;\n    }\n\n    public Date getGmtModified() {\n        return gmtModified;\n    }\n\n    public void setGmtModified(Date gmtModified) {\n        this.gmtModified = gmtModified;\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/record/dal/ibatis/IbatisLogRecordDAO.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.config.record.dal.ibatis;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.springframework.orm.ibatis.support.SqlMapClientDaoSupport;\n\nimport com.alibaba.otter.shared.common.utils.Assert;\nimport com.alibaba.otter.manager.biz.config.record.dal.LogRecordDAO;\nimport com.alibaba.otter.manager.biz.config.record.dal.dataobject.LogRecordDO;\n\n/**\n * 类IbatisLogRecordDAO.java的实现描述：TODO 类实现描述\n * \n * @author simon 2012-6-15 下午1:52:15\n */\npublic class IbatisLogRecordDAO extends SqlMapClientDaoSupport implements LogRecordDAO {\n\n    public LogRecordDO insert(LogRecordDO entityObj) {\n        Assert.assertNotNull(entityObj);\n        getSqlMapClientTemplate().insert(\"insertLogRecord\", entityObj);\n        return entityObj;\n    }\n\n    public void delete(Long identity) {\n        Assert.assertNotNull(identity);\n        getSqlMapClientTemplate().delete(\"deleteLogRecordById\", identity);\n\n    }\n\n    public void update(LogRecordDO entityObj) {\n        Assert.assertNotNull(entityObj);\n        getSqlMapClientTemplate().update(\"updateLogRecord\", entityObj);\n\n    }\n\n    public List<LogRecordDO> listAll() {\n        List<LogRecordDO> logRecordDos = getSqlMapClientTemplate().queryForList(\"listLogRecords\");\n        return logRecordDos;\n    }\n\n    public List<LogRecordDO> listByCondition(Map condition) {\n\n        List<LogRecordDO> logRecordDos = getSqlMapClientTemplate().queryForList(\"listLogRecordsWithCondition\",\n                                                                                condition);\n        return logRecordDos;\n    }\n\n    public List<LogRecordDO> listByMultiId(Long... identities) {\n        // TODO Auto-generated method stub\n        return null;\n    }\n\n    public LogRecordDO findById(Long identity) {\n        Assert.assertNotNull(identity);\n        return (LogRecordDO) getSqlMapClientTemplate().queryForObject(\"findLogRecordById\", identity);\n    }\n\n    public int getCount() {\n        Integer count = (Integer) getSqlMapClientTemplate().queryForObject(\"getLogRecordCount\");\n        return count.intValue();\n    }\n\n    public int getCount(Map condition) {\n        Integer count = (Integer) getSqlMapClientTemplate().queryForObject(\"getLogRecordCountWithPIdAndSearchKey\",\n                                                                           condition);\n        return count.intValue();\n    }\n\n    public boolean checkUnique(LogRecordDO entityObj) {\n        // TODO Auto-generated method stub\n        return false;\n    }\n\n    public List<LogRecordDO> listByPipelineId(Long pipelineId) {\n        List<LogRecordDO> logRecordDos = getSqlMapClientTemplate().queryForList(\"listLogRecordsByPipelineId\",\n                                                                                pipelineId);\n        return logRecordDos;\n    }\n\n    public List<LogRecordDO> listByPipelineIdWithoutContent(Long pipelineId) {\n        List<LogRecordDO> logRecordDos = getSqlMapClientTemplate().queryForList(\"listLogRecordsByPipelineIdWithoutContent\",\n                                                                                pipelineId);\n        return logRecordDos;\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/record/impl/LogRecordServiceImpl.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.config.record.impl;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.alibaba.otter.manager.biz.common.exceptions.ManagerException;\nimport com.alibaba.otter.manager.biz.config.channel.ChannelService;\nimport com.alibaba.otter.manager.biz.config.record.LogRecordService;\nimport com.alibaba.otter.manager.biz.config.record.dal.LogRecordDAO;\nimport com.alibaba.otter.manager.biz.config.record.dal.dataobject.LogRecordDO;\nimport com.alibaba.otter.shared.common.model.config.channel.Channel;\nimport com.alibaba.otter.shared.common.model.config.pipeline.Pipeline;\nimport com.alibaba.otter.shared.common.model.config.record.LogRecord;\nimport com.alibaba.otter.shared.common.utils.Assert;\nimport com.alibaba.otter.shared.common.utils.JsonUtils;\nimport com.alibaba.otter.shared.communication.core.model.Event;\nimport com.alibaba.otter.shared.communication.model.arbitrate.NodeAlarmEvent;\n\n/**\n * 类LogRecordServiceImpl.java的实现描述：TODO 类实现描述\n * \n * @author simon 2012-6-15 下午5:01:04\n * @version 4.1.0\n */\npublic class LogRecordServiceImpl implements LogRecordService {\n\n    private static final Logger logger = LoggerFactory.getLogger(LogRecordServiceImpl.class);\n\n    private ChannelService      channelService;\n    private LogRecordDAO        logRecordDao;\n\n    public void create(Event event) {\n        LogRecord logRecord = new LogRecord();\n        if (event instanceof NodeAlarmEvent) {\n            NodeAlarmEvent nodeAlarmEvent = (NodeAlarmEvent) event;\n            Pipeline tempPipeline = new Pipeline();\n            tempPipeline.setId(nodeAlarmEvent.getPipelineId());\n            logRecord.setPipeline(tempPipeline);\n            logRecord.setNid(nodeAlarmEvent.getNid());\n            logRecord.setTitle(nodeAlarmEvent.getTitle());\n            logRecord.setMessage(nodeAlarmEvent.getMessage());\n        }\n        create(logRecord);\n    }\n\n    public void create(LogRecord entityObj) {\n        Assert.assertNotNull(entityObj);\n        logRecordDao.insert(modelToDo(entityObj));\n    }\n\n    public void remove(Long identity) {\n        Assert.assertNotNull(identity);\n        logRecordDao.delete(identity);\n\n    }\n\n    public void modify(LogRecord entityObj) {\n\n    }\n\n    public LogRecord findById(Long identity) {\n\n        return null;\n    }\n\n    public List<LogRecord> listByPipelineId(Long pipelineId) {\n        Assert.assertNotNull(pipelineId);\n        List<LogRecordDO> logRecordDos = logRecordDao.listByPipelineId(pipelineId);\n        return doToModel(logRecordDos);\n    }\n\n    public List<LogRecord> listByPipelineIdWithoutContent(Long pipelineId) {\n        Assert.assertNotNull(pipelineId);\n        List<LogRecordDO> logRecordDos = logRecordDao.listByPipelineIdWithoutContent(pipelineId);\n        return doToModel(logRecordDos);\n    }\n\n    public List<LogRecord> listByIds(Long... identities) {\n\n        return null;\n    }\n\n    public List<LogRecord> listAll() {\n        List<LogRecordDO> logRecordDos = logRecordDao.listAll();\n        return doToModel(logRecordDos);\n    }\n\n    public List<LogRecord> listByCondition(Map condition) {\n        List<LogRecordDO> logRecordDos = logRecordDao.listByCondition(condition);\n        if (logRecordDos.isEmpty()) {\n            logger.debug(\"DEBUG ## couldn't query any log record by the condition:\"\n                         + JsonUtils.marshalToString(condition));\n            return new ArrayList<LogRecord>();\n        }\n        return doToModel(logRecordDos);\n    }\n\n    public int getCount() {\n\n        return 0;\n    }\n\n    public int getCount(Map condition) {\n\n        return logRecordDao.getCount(condition);\n    }\n\n    /*----------------------DO <-> MODEL 组装方法--------------------------*/\n    /**\n     * <pre>\n     * 用于Model对象转化为DO对象\n     * 优化：\n     *      无SQL交互，只是简单进行字段组装，暂时无须优化\n     * </pre>\n     * \n     * @param channel\n     * @return ChannelDO\n     */\n    private LogRecordDO modelToDo(LogRecord entityObj) {\n\n        LogRecordDO logRecordDo = new LogRecordDO();\n        try {\n\n            if (entityObj.getPipeline() != null && entityObj.getPipeline().getId() > 0) {\n                Channel channel = channelService.findByPipelineId(entityObj.getPipeline().getId());\n                logRecordDo.setChannelId(channel.getId());\n                logRecordDo.setPipelineId(entityObj.getPipeline().getId());\n            } else {\n                logRecordDo.setChannelId(-1l);\n                logRecordDo.setPipelineId(-1l);\n            }\n\n            logRecordDo.setNid(entityObj.getNid());\n            logRecordDo.setTitle(entityObj.getTitle());\n            String message = entityObj.getMessage();\n            if (message != null && message.length() > 65535 / 3) {\n                message = message.substring(0, 65535 / 3);\n            }\n            logRecordDo.setMessage(message);\n            logRecordDo.setGmtCreate(entityObj.getGmtCreate());\n            logRecordDo.setGmtModified(entityObj.getGmtModified());\n\n        } catch (Exception e) {\n            logger.error(\"ERROR ## has an error where write log to db\");\n            throw new ManagerException(e);\n        }\n        return logRecordDo;\n    }\n\n    /**\n     * <pre>\n     * 用于DO对象转化为Model对象\n     * </pre>\n     * \n     * @param channelDO\n     * @return Channel\n     */\n\n    private LogRecord doToModel(LogRecordDO logRecordDo) {\n        LogRecord logRecord = new LogRecord();\n        try {\n\n            logRecord.setId(logRecordDo.getId());\n            if (logRecordDo.getPipelineId() > 0 && logRecordDo.getChannelId() > 0) {\n                try {\n                    Channel channel = channelService.findByPipelineId(logRecordDo.getPipelineId());\n                    logRecord.setChannel(channel);\n                    for (Pipeline pipeline : channel.getPipelines()) {\n                        if (pipeline.getId().equals(logRecordDo.getPipelineId())) {\n                            logRecord.setPipeline(pipeline);\n                        }\n                    }\n                } catch (Exception e) {\n                    // 可能历史的log记录对应的channel/pipeline已经被删除了，直接忽略吧\n                    Channel channel = new Channel();\n                    channel.setId(0l);\n                    logRecord.setChannel(channel);\n                    Pipeline pipeline = new Pipeline();\n                    pipeline.setId(0l);\n                    logRecord.setPipeline(pipeline);\n                }\n            } else {\n                Channel channel = new Channel();\n                channel.setId(-1l);\n                logRecord.setChannel(channel);\n                Pipeline pipeline = new Pipeline();\n                pipeline.setId(-1l);\n                logRecord.setPipeline(pipeline);\n            }\n\n            logRecord.setTitle(logRecordDo.getTitle());\n            logRecord.setNid(logRecordDo.getNid());\n            logRecord.setMessage(logRecordDo.getMessage());\n            logRecord.setGmtCreate(logRecordDo.getGmtCreate());\n            logRecord.setGmtModified(logRecordDo.getGmtModified());\n\n        } catch (Exception e) {\n            logger.error(\"ERROR ## \");\n            throw new ManagerException(e);\n        }\n\n        return logRecord;\n    }\n\n    private List<LogRecord> doToModel(List<LogRecordDO> logRecordDos) {\n        List<LogRecord> logRecords = new ArrayList<LogRecord>();\n        try {\n            for (LogRecordDO logRecordDo : logRecordDos) {\n                logRecords.add(doToModel(logRecordDo));\n            }\n\n        } catch (Exception e) {\n            logger.error(\"ERROR ##\");\n            throw new ManagerException(e);\n        }\n\n        return logRecords;\n    }\n\n    public ChannelService getChannelService() {\n        return channelService;\n    }\n\n    public void setChannelService(ChannelService channelService) {\n        this.channelService = channelService;\n    }\n\n    public LogRecordDAO getLogRecordDao() {\n        return logRecordDao;\n    }\n\n    public void setLogRecordDao(LogRecordDAO logRecordDao) {\n        this.logRecordDao = logRecordDao;\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/utils/ListTypeHandler.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.config.utils;\n\nimport java.sql.SQLException;\nimport java.util.List;\n\nimport com.alibaba.fastjson.TypeReference;\nimport com.alibaba.otter.shared.common.utils.JsonUtils;\nimport com.ibatis.sqlmap.client.extensions.ParameterSetter;\nimport com.ibatis.sqlmap.client.extensions.ResultGetter;\nimport com.ibatis.sqlmap.client.extensions.TypeHandlerCallback;\n\n/**\n * 用于List数据结构的解析，ibatis相关\n * \n * @author simon\n */\npublic class ListTypeHandler implements TypeHandlerCallback {\n\n    @Override\n    public void setParameter(ParameterSetter setter, Object parameter) throws SQLException {\n        setter.setString(JsonUtils.marshalToString(parameter));\n    }\n\n    @Override\n    public Object getResult(ResultGetter getter) throws SQLException {\n        return JsonUtils.unmarshalFromString(getter.getString(), new TypeReference<List<Long>>() {\n        });\n    }\n\n    public Object valueOf(String s) {\n        return JsonUtils.unmarshalFromString(s, List.class);\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/utils/MapTypeHandler.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.config.utils;\n\nimport java.sql.SQLException;\nimport java.util.Map;\n\nimport com.alibaba.otter.shared.common.utils.JsonUtils;\nimport com.ibatis.sqlmap.client.extensions.ParameterSetter;\nimport com.ibatis.sqlmap.client.extensions.ResultGetter;\nimport com.ibatis.sqlmap.client.extensions.TypeHandlerCallback;\n\n/**\n * 用于Map数据结构的解析，ibatis相关\n * \n * @author simon\n */\npublic class MapTypeHandler implements TypeHandlerCallback {\n\n    @Override\n    public void setParameter(ParameterSetter setter, Object parameter) throws SQLException {\n        setter.setString(JsonUtils.marshalToString(parameter));\n    }\n\n    @Override\n    public Object getResult(ResultGetter getter) throws SQLException {\n        return JsonUtils.unmarshalFromString(getter.getString(), Map.class);\n    }\n\n    public Object valueOf(String s) {\n        return JsonUtils.unmarshalFromString(s, Map.class);\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/monitor/AlarmController.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.monitor;\n\nimport com.alibaba.otter.manager.biz.common.alarm.AlarmMessage;\nimport com.alibaba.otter.shared.common.model.config.alarm.AlarmRule;\n\n/**\n * @author zebin.xuzb\n * @version 4.1.0\n */\npublic interface AlarmController {\n\n    public AlarmMessage control(AlarmRule rule, String message, AlarmMessage data);\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/monitor/AlarmRecovery.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.monitor;\n\nimport com.alibaba.otter.shared.common.model.config.alarm.AlarmRule;\n\n/**\n * 报警尝试自行恢复机制\n * \n * @author jianghang 2012-9-19 下午04:43:30\n * @version 4.1.0\n */\npublic interface AlarmRecovery {\n\n    /**\n     * 根据规则，强制进行recovery操作\n     */\n    public void recovery(Long channelId);\n\n    /**\n     * 根据规则，强制进行recovery操作\n     */\n    public void recovery(AlarmRule alarmRule);\n\n    /**\n     * 根据规则+触发次数，进行recovery操作\n     */\n    public void recovery(AlarmRule alarmRule, long alarmCount);\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/monitor/Monitor.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.monitor;\n\nimport java.util.List;\n\nimport com.alibaba.otter.shared.common.model.config.alarm.AlarmRule;\n\n/**\n * 主动监控者，需要自己去寻找数据来进行监控\n * \n * @author zebin.xuzb @ 2012-8-23\n * @version 4.1.0\n */\npublic interface Monitor {\n\n    public void explore();\n\n    public void explore(Long... pipelineIds);\n\n    public void explore(List<AlarmRule> rules);\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/monitor/MonitorRuleExplorerRegisty.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.monitor;\n\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\n\nimport org.springframework.util.CollectionUtils;\n\nimport com.alibaba.otter.manager.biz.monitor.impl.AbstractRuleMonitor;\nimport com.alibaba.otter.shared.common.model.config.alarm.MonitorName;\n\n/**\n * @author zebin.xuzb @ 2012-8-29\n * @version 4.1.0\n */\npublic class MonitorRuleExplorerRegisty {\n\n    // Map<MonitorName, Map<explorerName, MonitorExplorer>>\n    private static Map<MonitorName, Map<String, Monitor>> registy = new ConcurrentHashMap<MonitorName, Map<String, Monitor>>(\n                                                                                                                             16);\n\n    public static void register(MonitorName monitorName, Monitor explorer) {\n        MonitorRuleExplorerRegisty.register(monitorName, null, explorer);\n    }\n\n    synchronized public static void register(MonitorName monitorName, String explorerName, Monitor explorer) {\n        if (monitorName == null || explorer == null) {\n            return;\n        }\n\n        if (!AbstractRuleMonitor.class.isAssignableFrom(explorer.getClass())) {\n            throw new UnsupportedOperationException(\n                                                    \"only accept AbstractRuleMonitorExplorer or it's subclass to regist\");\n        }\n\n        Map<String, Monitor> explorers = registy.get(monitorName);\n        if (explorers == null) {\n            explorers = new ConcurrentHashMap<String, Monitor>(16);\n            registy.put(monitorName, explorers);\n        }\n\n        if (explorerName == null) {\n            explorerName = explorer.getClass().getName();\n        }\n\n        explorers.put(explorerName, explorer);\n    }\n\n    public static Collection<Monitor> findExplorer(MonitorName monitorName) {\n        if (monitorName == null) {\n            return Collections.EMPTY_LIST;\n        }\n        Map<String, Monitor> explorers = registy.get(monitorName);\n        if (CollectionUtils.isEmpty(explorers)) {\n            return Collections.EMPTY_LIST;\n        }\n        return explorers.values();\n    }\n\n    public static void unRegister(MonitorName monitorName, Monitor explorer) {\n        if (monitorName == null || explorer == null) {\n            return;\n        }\n\n        Map<String, Monitor> explorers = registy.get(monitorName);\n        if (CollectionUtils.isEmpty(explorers)) {\n            return;\n        }\n\n        String explorerName = explorer.getClass().getName();\n        explorers.remove(explorerName);\n    }\n\n    public static void unRegister(MonitorName monitorName, String explorerName) {\n        if (monitorName == null || explorerName == null) {\n            return;\n        }\n\n        Map<String, Monitor> explorers = registy.get(monitorName);\n        if (CollectionUtils.isEmpty(explorers)) {\n            return;\n        }\n        explorers.remove(explorerName);\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/monitor/MonitorTimer.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.monitor;\n\nimport java.util.Date;\nimport java.util.concurrent.ConcurrentHashMap;\n\nimport com.alibaba.otter.shared.common.model.config.alarm.MonitorName;\n\n/**\n * @author simon 2012-9-11 下午3:25:14\n * @version 4.1.0\n */\npublic class MonitorTimer extends ConcurrentHashMap<MonitorName, Date> {\n\n    private static final long serialVersionUID = -2129810461060521223L;\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/monitor/PassiveMonitor.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.monitor;\n\nimport java.util.List;\n\nimport com.alibaba.otter.shared.common.model.config.alarm.AlarmRule;\n\n/**\n * 被动监控者，由其他人喂给他需要监控的数据\n * \n * @author zebin.xuzb @ 2012-8-30\n * @version 4.1.0\n */\npublic interface PassiveMonitor {\n\n    public void feed(Object data, Long pipelineId);\n\n    public void feed(Object data, List<AlarmRule> rules);\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/monitor/impl/AbstractRuleMonitor.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.monitor.impl;\n\nimport java.util.Calendar;\nimport java.util.List;\n\nimport javax.annotation.Resource;\n\nimport org.apache.commons.lang.StringUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.alibaba.otter.manager.biz.common.alarm.AbstractAlarmService;\nimport com.alibaba.otter.manager.biz.common.alarm.AlarmMessage;\nimport com.alibaba.otter.manager.biz.config.record.LogRecordService;\nimport com.alibaba.otter.manager.biz.monitor.AlarmController;\nimport com.alibaba.otter.manager.biz.monitor.Monitor;\nimport com.alibaba.otter.manager.biz.monitor.PassiveMonitor;\nimport com.alibaba.otter.shared.common.model.config.alarm.AlarmRule;\nimport com.alibaba.otter.shared.common.model.config.alarm.MonitorName;\nimport com.alibaba.otter.shared.common.model.config.pipeline.Pipeline;\nimport com.alibaba.otter.shared.common.model.config.record.LogRecord;\n\n/**\n * 一类报警规则的匹配规则和报警\n * \n * @author zebin.xuzb @ 2012-8-29\n * @version 4.1.0\n */\npublic abstract class AbstractRuleMonitor implements Monitor, PassiveMonitor {\n\n    protected static final Logger log = LoggerFactory.getLogger(\"monitorInfo\");\n\n    @Resource(name = \"alarmService\")\n    private AbstractAlarmService  alarmService;\n\n    @Resource(name = \"logRecordService\")\n    private LogRecordService      logRecordService;\n\n    @Resource(name = \"alarmController\")\n    private AlarmController       alarmController;\n\n    @Override\n    public void explore() {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public void explore(Long... pipelineIds) {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public void feed(Object data, Long pipelineId) {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public void feed(Object data, List<AlarmRule> rules) {\n        throw new UnsupportedOperationException();\n    }\n\n    protected void sendAlarm(AlarmRule rule, String message) {\n        AlarmMessage data = new AlarmMessage();\n        data.setMessage(message);\n        data.setReceiveKey(rule.getReceiverKey());\n\n        data = alarmController.control(rule, message, data);\n        postProcessAlarmData(data);\n\n        if (data == null) {\n            log.info(\"has suppressed alarm : \" + message);\n            return;\n        }\n        alarmService.sendAlarm(data);\n        log.info(\"has send alarm : \" + data + \"; rule is \" + rule);\n    }\n\n    protected void logRecordAlarm(Long pipelineId, MonitorName monitorName, String message) {\n        logRecordAlarm(pipelineId, -1l, monitorName, message);\n    }\n\n    protected void logRecordAlarm(Long pipelineId, Long nodeId, MonitorName monitorName, String message) {\n\n        Pipeline pipeline = new Pipeline();\n        pipeline.setId(pipelineId);\n        LogRecord logRecord = new LogRecord();\n        logRecord.setTitle(monitorName.toString());\n        logRecord.setNid(nodeId);\n        logRecord.setPipeline(pipeline);\n        logRecord.setMessage(message);\n        logRecordService.create(logRecord);\n    }\n\n    protected void postProcessAlarmData(AlarmMessage data) {\n        // do nothing by default\n    }\n\n    // 规则一般是 xxxx@16:00-24:00,00:00-06:00\n    // 目前仅仅是局限在某一天的时间段中\n    protected boolean inPeriod(AlarmRule alarmRule) {\n        String rule = alarmRule.getMatchValue();\n        if (StringUtils.isEmpty(rule)) {\n            log.info(\"rule is empty \" + alarmRule);\n            return false;\n        }\n\n        String periods = StringUtils.substringAfterLast(rule, \"@\");\n        if (StringUtils.isEmpty(periods)) {\n            // 没有时间要求，则任务在报警时间段内\n            return isInPeriodWhenNoPeriod();\n        }\n\n        Calendar calendar = currentCalendar();\n        periods = StringUtils.trim(periods);\n        for (String period : StringUtils.split(periods, \",\")) {\n            String[] startAndEnd = StringUtils.split(period, \"-\");\n            if (startAndEnd == null || startAndEnd.length != 2) {\n                log.error(\"error period time format in rule : \" + alarmRule);\n                return isInPeriodWhenErrorFormat();\n            }\n\n            String start = startAndEnd[0];\n            String end = startAndEnd[1];\n            if (checkInPeriod(calendar, start, end)) {\n                log.info(\"rule is in period : \" + alarmRule);\n                return true;\n            }\n        }\n\n        log.info(\"rule is not in period : \" + alarmRule);\n        return false;\n    }\n\n    protected boolean checkInPeriod(Calendar now, String start, String end) {\n        return isAfter(now, start) && !isAfter(now, end);\n    }\n\n    protected boolean isAfter(Calendar now, String time) {\n        String[] hourAndMin = StringUtils.split(time, \":\");\n        if (hourAndMin == null || hourAndMin.length != 2) {\n            log.error(\"error period time format in rule : \" + time);\n            return isInPeriodWhenErrorFormat();\n        }\n\n        int hour;\n        int min;\n        try {\n            hour = Integer.parseInt(hourAndMin[0]);\n            min = Integer.parseInt(hourAndMin[1]);\n        } catch (NumberFormatException e) {\n            log.error(\"error period time format in rule : \" + time, e);\n            return isInPeriodWhenErrorFormat();\n        }\n\n        if (hour > 24 || min > 60) {\n            log.error(\"error period time format in rule : \" + time);\n            return isInPeriodWhenErrorFormat();\n        }\n\n        Calendar when = (Calendar) now.clone();\n        when.set(Calendar.HOUR_OF_DAY, hour);\n        when.set(Calendar.MINUTE, min);\n\n        return !now.before(when);\n    }\n\n    /**\n     * 如果时间段格式有问题，则默认是在时间段内。子类可以修改此策略\n     */\n    protected boolean isInPeriodWhenErrorFormat() {\n        return true;\n    }\n\n    /**\n     * 如果没有时间段格式，则默认是在时间段内。子类可以修改此策略\n     */\n    protected boolean isInPeriodWhenNoPeriod() {\n        return true;\n    }\n\n    /**\n     * 返回当前的时间，方便测试\n     */\n    protected Calendar currentCalendar() {\n        Calendar calendar = Calendar.getInstance();\n        return calendar;\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/monitor/impl/AlarmRecoveryDelayed.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.monitor.impl;\n\nimport java.util.concurrent.Delayed;\nimport java.util.concurrent.TimeUnit;\n\nimport com.alibaba.otter.manager.biz.common.arbitrate.DeadNodeListener.DeadNodeDelayed;\n\n/**\n * recovery异步延迟处理对象\n * \n * @author jianghang 2012-9-20 上午10:42:35\n * @version 4.1.0\n */\npublic class AlarmRecoveryDelayed implements Delayed {\n\n    // init time for nano\n    private static final long MILL_ORIGIN = System.currentTimeMillis();\n    private long              ruleId;\n    private long              channelId;\n    private boolean           stop        = false;\n    private long              now;                                     // 记录具体的now的偏移时间点，单位ms\n    private long              timeout;                                 // 记录具体需要被delayed处理的偏移时间点,单位ms\n\n    public AlarmRecoveryDelayed(long channelId, long ruleId, boolean stop, long timeout){\n        this.channelId = channelId;\n        this.ruleId = ruleId;\n        this.stop = stop;\n        this.timeout = timeout;\n        this.now = System.currentTimeMillis() - MILL_ORIGIN;\n    }\n\n    public long getChannelId() {\n        return channelId;\n    }\n\n    public long getRuleId() {\n        return ruleId;\n    }\n\n    public long getNow() {\n        return now;\n    }\n\n    public boolean isStop() {\n        return stop;\n    }\n\n    public void setStop(boolean stop) {\n        this.stop = stop;\n    }\n\n    public long getDelay(TimeUnit unit) {\n        long currNow = System.currentTimeMillis() - MILL_ORIGIN;\n        long d = unit.convert(now + timeout - currNow, TimeUnit.MILLISECONDS);\n        return d;\n    }\n\n    public int compareTo(Delayed other) {\n        if (other == this) { // compare zero ONLY if same object\n            return 0;\n        } else if (other instanceof AlarmRecoveryDelayed) {\n            AlarmRecoveryDelayed x = (AlarmRecoveryDelayed) other;\n            long diff = now + timeout - (x.now + x.timeout);\n            return (diff == 0) ? 0 : ((diff < 0) ? 1 : -1); // 时间越小的，越应该排在前面\n        } else {\n            long d = (getDelay(TimeUnit.MILLISECONDS) - other.getDelay(TimeUnit.MILLISECONDS));\n            return (d == 0) ? 0 : ((d < 0) ? 1 : -1);\n        }\n    }\n\n    @Override\n    public int hashCode() {\n        final int prime = 31;\n        int result = 1;\n        result = prime * result + (int) (channelId ^ (channelId >>> 32));\n        return result;\n    }\n\n    public boolean equals(Object obj) {\n        if (this == obj) {\n            return true;\n        }\n        if (obj == null) {\n            return false;\n        }\n        if (!(obj instanceof DeadNodeDelayed)) {\n            return false;\n        }\n        AlarmRecoveryDelayed other = (AlarmRecoveryDelayed) obj;\n        if (channelId != other.channelId) {\n            return false;\n        }\n        return true;\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/monitor/impl/DefaultAlarmController.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.monitor.impl;\n\nimport java.util.Map;\nimport java.util.concurrent.TimeUnit;\n\nimport com.alibaba.otter.manager.biz.common.alarm.AlarmMessage;\nimport com.alibaba.otter.manager.biz.monitor.AlarmController;\nimport com.alibaba.otter.manager.biz.monitor.AlarmRecovery;\nimport com.alibaba.otter.shared.common.model.config.alarm.AlarmRule;\nimport com.alibaba.otter.shared.common.model.config.alarm.MonitorName;\nimport com.google.common.collect.OtterMigrateMap;\n\n/**\n * @author zebin.xuzb 2012-9-11 下午3:47:28\n * @version 4.1.0\n */\npublic class DefaultAlarmController implements AlarmController {\n\n    // seconds\n    private Long                    DEFAULT_THRESHOLD = 1800L;\n    private Map<PoolKey, PoolValue> pool              = OtterMigrateMap.makeSoftValueMapWithTimeout(1, TimeUnit.HOURS);\n    private AlarmRecovery           restartAlarmRecovery;\n\n    @Override\n    public AlarmMessage control(AlarmRule rule, String message, AlarmMessage data) {\n        // rule为空不控制\n        if (rule == null) {\n            return data;\n        }\n\n        // second\n        Long threshold = rule.getIntervalTime() == null ? DEFAULT_THRESHOLD : rule.getIntervalTime();\n\n        PoolKey key = new PoolKey(rule, message, data);\n        PoolValue value = pool.get(key);\n        boolean needAlarm = true;\n\n        Long now = System.currentTimeMillis();\n        // 第一次报警,或是之前已经清空了\n        if (value == null) {\n            value = new PoolValue(now);\n            pool.put(key, new PoolValue(now));\n        } else {\n            Long latest = value.getLastAlarmTime();\n            // 如果第二次报警超过阀值，则进行报警\n            if ((now - latest) > (threshold * 1000)) {\n                value.updateAlarmTime(now);// 更新最后一次报警时间\n                pool.put(key, value);\n            } else { // 第二次报警没有超过阀值，则存下来，不进行报警\n                value.addSuppressTimes(); // 增加报警压制次数\n                pool.put(key, value);\n                needAlarm = false;\n            }\n        }\n\n        if (rule.getAutoRecovery()) {// 尝试一下恢复机制\n            restartAlarmRecovery.recovery(rule, value.getSuppressTimes());\n        }\n\n        if (needAlarm) {\n            return data;\n        } else {\n            return null;\n        }\n    }\n\n    private static class PoolKey {\n\n        private Long        pipelineId;\n        private MonitorName monitorName;\n        private String      receiveKey;\n        private String      matchValue;\n\n        public PoolKey(AlarmRule rule, String messageToSend, AlarmMessage data){\n            // used to hash compute\n            this.pipelineId = rule.getPipelineId();\n            this.monitorName = rule.getMonitorName();\n            this.receiveKey = rule.getReceiverKey();\n            this.matchValue = rule.getMatchValue();\n        }\n\n        @Override\n        public int hashCode() {\n            final int prime = 31;\n            int result = 1;\n            result = prime * result + ((matchValue == null) ? 0 : matchValue.hashCode());\n            result = prime * result + ((monitorName == null) ? 0 : monitorName.hashCode());\n            result = prime * result + ((pipelineId == null) ? 0 : pipelineId.hashCode());\n            result = prime * result + ((receiveKey == null) ? 0 : receiveKey.hashCode());\n            return result;\n        }\n\n        @Override\n        public boolean equals(Object obj) {\n            if (this == obj) return true;\n            if (obj == null) return false;\n            if (getClass() != obj.getClass()) return false;\n            PoolKey other = (PoolKey) obj;\n            if (matchValue == null) {\n                if (other.matchValue != null) return false;\n            } else if (!matchValue.equals(other.matchValue)) return false;\n            if (monitorName != other.monitorName) return false;\n            if (pipelineId == null) {\n                if (other.pipelineId != null) return false;\n            } else if (!pipelineId.equals(other.pipelineId)) return false;\n            if (receiveKey == null) {\n                if (other.receiveKey != null) return false;\n            } else if (!receiveKey.equals(other.receiveKey)) return false;\n            return true;\n        }\n\n    }\n\n    private static class PoolValue {\n\n        private Long lastAlarmTime;    // mill\n        private long suppressTimes = 1; // 报警压制次数\n\n        public PoolValue(Long happendTime){\n            this.lastAlarmTime = happendTime;\n        }\n\n        /**\n         * 增加报警压制次数\n         */\n        public void addSuppressTimes() {\n            suppressTimes++;\n        }\n\n        /**\n         * 获取报警压制次数\n         */\n        public long getSuppressTimes() {\n            return suppressTimes;\n        }\n\n        /**\n         * 最后报警时间\n         */\n        public Long getLastAlarmTime() {\n            return lastAlarmTime;\n        }\n\n        /**\n         * 更新报警时间\n         */\n        public void updateAlarmTime(Long lastAlarmTime) {\n            this.lastAlarmTime = lastAlarmTime;\n        }\n\n    }\n\n    public void setRestartAlarmRecovery(AlarmRecovery restartAlarmRecovery) {\n        this.restartAlarmRecovery = restartAlarmRecovery;\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/monitor/impl/DelayStatRuleMonitor.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.monitor.impl;\n\nimport java.util.Date;\nimport java.util.List;\n\nimport org.apache.commons.lang.StringUtils;\nimport org.springframework.util.CollectionUtils;\n\nimport com.alibaba.otter.manager.biz.statistics.delay.DelayStatService;\nimport com.alibaba.otter.shared.common.model.config.alarm.AlarmRule;\nimport com.alibaba.otter.shared.common.model.config.alarm.MonitorName;\nimport com.alibaba.otter.shared.common.model.statistics.delay.DelayStat;\n\n/**\n * @author zebin.xuzb @ 2012-8-29\n * @version 4.1.0\n */\npublic class DelayStatRuleMonitor extends AbstractRuleMonitor {\n\n    private static final String DELAY_TIME_MESSAGE        = \"pid:%s delay_time:%s seconds\";\n    private static final String DELAY_UPDATE_MESSAGE      = \"pid:%s delay %s seconds no update\";\n    private static final String DELAY_TIME_UPDATE_MESSAGE = \"pid:%s delay_time:%s seconds, but delay %s seconds no update\";\n\n    private DelayStatService    delayStatService;\n\n    @Override\n    public void explore(List<AlarmRule> rules) {\n        if (CollectionUtils.isEmpty(rules)) {\n            return;\n        }\n\n        // 进入到监控项级别的rule，pipelineId一定是相同的\n        Long pipelineId = rules.get(0).getPipelineId();\n        DelayStat delayStat = delayStatService.findRealtimeDelayStat(pipelineId);\n        Long delayTime = 0L; // seconds\n        Long delayUpdate = 0L;\n        if (delayStat.getDelayTime() != null) {\n            delayTime = delayStat.getDelayTime() / 1000;\n        }\n        if (delayStat.getGmtCreate() != null) {\n            delayUpdate = (new Date().getTime() - delayStat.getGmtCreate().getTime()) / 1000;\n        }\n\n        boolean delayTimeFlag = false;\n        boolean delayUpdateFlag = false;\n        for (AlarmRule rule : rules) {\n            if (rule.getMonitorName().isDelayTime()) {\n                delayTimeFlag |= checkDelayTime(rule, delayTime);\n                if (delayTimeFlag) { //如果出现超时，再check下是否因为最后更新时间过久了\n                    delayUpdateFlag |= checkDelayTime(rule, delayUpdate);//检查delay统计的最后更新时间，这也做为delay监控的一部分\n                }\n            }\n        }\n\n        if (delayTimeFlag && !delayUpdateFlag) {\n            logRecordAlarm(pipelineId, MonitorName.DELAYTIME, String.format(DELAY_TIME_MESSAGE, pipelineId, delayTime));\n        } else if (delayTimeFlag && delayUpdateFlag) {\n            logRecordAlarm(pipelineId, MonitorName.DELAYTIME,\n                           String.format(DELAY_TIME_UPDATE_MESSAGE, pipelineId, delayTime, delayUpdate));\n        } else if (delayUpdateFlag) {\n            logRecordAlarm(pipelineId, MonitorName.DELAYTIME,\n                           String.format(DELAY_UPDATE_MESSAGE, pipelineId, delayUpdate));\n        }\n    }\n\n    private boolean checkDelayTime(AlarmRule rule, Long delayTime) {\n\n        if (!inPeriod(rule)) {\n            return false;\n        }\n\n        String matchValue = rule.getMatchValue();\n        matchValue = StringUtils.substringBeforeLast(matchValue, \"@\");\n        Long maxDelayTime = Long.parseLong(StringUtils.trim(matchValue));\n        if (delayTime >= maxDelayTime) {\n            sendAlarm(rule, String.format(DELAY_TIME_MESSAGE, rule.getPipelineId(), delayTime));\n            return true;\n        }\n        return false;\n    }\n\n    public void setDelayStatService(DelayStatService delayStatService) {\n        this.delayStatService = delayStatService;\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/monitor/impl/ExceptionRuleMonitor.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.monitor.impl;\n\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.List;\n\nimport javax.annotation.Resource;\n\nimport org.apache.commons.lang.StringUtils;\nimport org.springframework.util.CollectionUtils;\n\nimport com.alibaba.otter.manager.biz.config.alarm.AlarmRuleService;\nimport com.alibaba.otter.shared.common.model.config.alarm.AlarmRule;\nimport com.alibaba.otter.shared.common.model.config.alarm.AlarmRuleStatus;\nimport com.alibaba.otter.shared.common.model.config.alarm.MonitorName;\nimport com.alibaba.otter.shared.communication.model.arbitrate.NodeAlarmEvent;\n\n/**\n * @author zebin.xuzb @ 2012-8-29\n * @version 4.1.0\n */\npublic class ExceptionRuleMonitor extends AbstractRuleMonitor {\n\n    private static final String MESAGE_FORMAT = \"pid:%s nid:%s exception:%s\";\n\n    @Resource(name = \"alarmRuleService\")\n    private AlarmRuleService    alarmRuleService;\n\n    // ExceptionRuleMonitor(){\n    // MonitorRuleExplorerRegisty.register(MonitorName.EXCEPTON, this);\n    // }\n\n    @Override\n    public void explore(List<AlarmRule> rules) {\n        throw new UnsupportedOperationException();\n    }\n\n    @Override\n    public void feed(Object data, Long pipelineId) {\n        if (!(data instanceof NodeAlarmEvent)) {\n            return;\n        }\n        NodeAlarmEvent alarmEvent = (NodeAlarmEvent) data;\n        // 异常一定需要记录日志\n        String message = String.format(MESAGE_FORMAT, alarmEvent.getPipelineId(), alarmEvent.getNid(),\n                                       alarmEvent.getMessage());\n        logRecordAlarm(pipelineId, alarmEvent.getNid(), MonitorName.EXCEPTION, message);\n        // 报警检查\n        List<AlarmRule> rules = alarmRuleService.getAlarmRules(pipelineId, AlarmRuleStatus.ENABLE);\n\n        // TODO 需要给 alarmRuleService 提需求\n        Date now = new Date();\n        List<AlarmRule> exceptionRules = new ArrayList<AlarmRule>();\n        for (AlarmRule rule : rules) {\n            if (MonitorName.EXCEPTION.equals(rule.getMonitorName()) && checkEnable(rule, now)) {\n                exceptionRules.add(rule);\n            }\n        }\n\n        if (CollectionUtils.isEmpty(exceptionRules)) {\n            return;\n        }\n\n        for (AlarmRule rule : exceptionRules) {\n            check(rule, alarmEvent);\n        }\n    }\n\n    private boolean checkEnable(AlarmRule rule, Date now) {\n        return rule.getPauseTime() == null || rule.getPauseTime().before(now);\n    }\n\n    private void check(AlarmRule rule, NodeAlarmEvent alarmEvent) {\n        if (!inPeriod(rule)) {\n            return;\n        }\n\n        String matchValue = rule.getMatchValue();\n        matchValue = StringUtils.substringBeforeLast(matchValue, \"@\");\n\n        String[] matchValues = StringUtils.split(matchValue, \",\");\n\n        for (String match : matchValues) {\n            if (StringUtils.containsIgnoreCase(alarmEvent.getMessage(), match)) {\n                String message = String.format(MESAGE_FORMAT, alarmEvent.getPipelineId(), alarmEvent.getNid(),\n                                               alarmEvent.getMessage());\n                sendAlarm(rule, message);\n                break;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/monitor/impl/GlobalMonitor.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.monitor.impl;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport java.util.concurrent.Callable;\nimport java.util.concurrent.ExecutionException;\nimport java.util.concurrent.ExecutorCompletionService;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Future;\nimport java.util.concurrent.LinkedBlockingQueue;\nimport java.util.concurrent.ThreadPoolExecutor;\nimport java.util.concurrent.TimeUnit;\n\nimport org.apache.commons.lang.exception.ExceptionUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.DisposableBean;\nimport org.springframework.beans.factory.InitializingBean;\nimport org.springframework.util.CollectionUtils;\n\nimport com.alibaba.otter.manager.biz.config.alarm.AlarmRuleService;\nimport com.alibaba.otter.manager.biz.config.channel.ChannelService;\nimport com.alibaba.otter.manager.biz.monitor.AlarmRecovery;\nimport com.alibaba.otter.manager.biz.monitor.Monitor;\nimport com.alibaba.otter.shared.arbitrate.ArbitrateManageService;\nimport com.alibaba.otter.shared.common.model.config.alarm.AlarmRule;\nimport com.alibaba.otter.shared.common.model.config.alarm.AlarmRuleStatus;\nimport com.alibaba.otter.shared.common.model.config.channel.ChannelStatus;\nimport com.alibaba.otter.shared.common.utils.thread.NamedThreadFactory;\n\n/**\n * 监控机制实现，不会自动触发，需要外部配置定时任务。 <br/>\n * 调用http://xxxx/monitor/monitor_trigger.htm?token=otter来进行触发，主要考虑otter集群，只需要有一个监控运行即可，如果jvm自运行，会同时并行监控\n * \n * @author zebin.xuzb @ 2012-8-23\n * @version 4.1.0\n */\npublic class GlobalMonitor implements Monitor, InitializingBean, DisposableBean {\n\n    protected static final Logger  log             = LoggerFactory.getLogger(\"monitorInfo\");\n    private static final int       DEFAULT_THREADS = 5;\n\n    private int                    nThreads;\n    private boolean                needConcurrent  = true;\n    private boolean                recoveryPaused  = true;\n    private ExecutorService        executor;\n\n    private AlarmRuleService       alarmRuleService;\n    private Monitor                pipelineMonitor;\n\n    private ChannelService         channelService;\n    private ArbitrateManageService arbitrateManageService;\n    private AlarmRecovery          restartAlarmRecovery;\n\n    @Override\n    public void explore() {\n        Map<Long, List<AlarmRule>> rules = alarmRuleService.getAlarmRules(AlarmRuleStatus.ENABLE);\n        if (!CollectionUtils.isEmpty(rules)) {\n            if (needConcurrent) {\n                concurrentProcess(rules);\n            } else {// 串行\n                serialProcess(rules);\n            }\n        } else {\n            log.warn(\"no enabled alarm rule at all. Check the rule setting please!\");\n        }\n\n        // 自动恢复机制\n        if (recoveryPaused) {\n            List<Long> channelIds = channelService.listAllChannelId();\n            if (needConcurrent) {\n                concurrentProcess(channelIds);\n            } else {// 串行\n                serialProcess(channelIds);\n            }\n        }\n    }\n\n    private void concurrentProcess(Map<Long, List<AlarmRule>> rules) {\n        ExecutorCompletionService completionExecutor = new ExecutorCompletionService(executor);\n        List<Future> futures = new ArrayList<Future>();\n        for (Entry<Long, List<AlarmRule>> entry : rules.entrySet()) {\n            final List<AlarmRule> alarmRules = entry.getValue();\n            futures.add(completionExecutor.submit(new Callable<Object>() {\n\n                @Override\n                public Object call() throws Exception {\n                    pipelineMonitor.explore(alarmRules);\n                    return null;\n                }\n            }));\n        }\n\n        List<Throwable> exceptions = new ArrayList<Throwable>();\n        int index = 0;\n        int size = futures.size();\n        while (index < size) {\n            try {\n                Future<?> future = completionExecutor.take();\n                future.get();\n            } catch (InterruptedException e) {\n                exceptions.add(e);\n            } catch (ExecutionException e) {\n                exceptions.add(e);\n            }\n            index++;\n        }\n\n        if (!exceptions.isEmpty()) {\n            StringBuilder sb = new StringBuilder(exceptions.size() + \" exception happens in global monitor\\n\");\n            sb.append(\"exception stack start :\\n\");\n            for (Throwable t : exceptions) {\n                sb.append(ExceptionUtils.getStackTrace(t));\n            }\n            sb.append(\"exception stack end \\n\");\n            throw new IllegalStateException(sb.toString());\n        }\n    }\n\n    private void serialProcess(Map<Long, List<AlarmRule>> rules) {\n        for (Entry<Long, List<AlarmRule>> entry : rules.entrySet()) {\n            List<AlarmRule> alarmRules = entry.getValue();\n            pipelineMonitor.explore(alarmRules);\n        }\n    }\n\n    private void concurrentProcess(List<Long> channelIds) {\n        ExecutorCompletionService completionExecutor = new ExecutorCompletionService(executor);\n        List<Future> futures = new ArrayList<Future>();\n        for (final Long channelId : channelIds) {\n            futures.add(completionExecutor.submit(new Callable<Object>() {\n\n                @Override\n                public Object call() throws Exception {\n                    ChannelStatus status = arbitrateManageService.channelEvent().status(channelId);\n                    if (status.isPause()) {\n                        restartAlarmRecovery.recovery(channelId);\n                    }\n                    return null;\n                }\n            }));\n        }\n\n        List<Throwable> exceptions = new ArrayList<Throwable>();\n        int index = 0;\n        int size = futures.size();\n        while (index < size) {\n            try {\n                Future<?> future = completionExecutor.take();\n                future.get();\n            } catch (InterruptedException e) {\n                exceptions.add(e);\n            } catch (ExecutionException e) {\n                exceptions.add(e);\n            }\n            index++;\n        }\n\n        if (!exceptions.isEmpty()) {\n            StringBuilder sb = new StringBuilder(exceptions.size() + \" exception happens in global monitor\\n\");\n            sb.append(\"exception stack start :\\n\");\n            for (Throwable t : exceptions) {\n                sb.append(ExceptionUtils.getStackTrace(t));\n            }\n            sb.append(\"exception stack end \\n\");\n            throw new IllegalStateException(sb.toString());\n        }\n    }\n\n    private void serialProcess(List<Long> channelIds) {\n        for (Long channelId : channelIds) {\n            ChannelStatus status = arbitrateManageService.channelEvent().status(channelId);\n            if (status.isPause()) {\n                restartAlarmRecovery.recovery(channelId);\n            }\n        }\n    }\n\n    @Override\n    public void afterPropertiesSet() throws Exception {\n        nThreads = nThreads <= 0 ? DEFAULT_THREADS : nThreads;\n        executor = new ThreadPoolExecutor(nThreads, nThreads, 0, TimeUnit.MILLISECONDS,\n                                          new LinkedBlockingQueue<Runnable>(nThreads * 2),\n                                          new NamedThreadFactory(\"global monitor\", false),\n                                          new ThreadPoolExecutor.CallerRunsPolicy());\n\n    }\n\n    @Override\n    public void destroy() throws Exception {\n        if (executor != null && !executor.isShutdown()) {\n            executor.shutdown();\n        }\n    }\n\n    @Override\n    public void explore(Long... pipelineIds) {\n        throw new UnsupportedOperationException(\"doesn't support right now\");\n    }\n\n    @Override\n    public void explore(List<AlarmRule> rules) {\n        throw new UnsupportedOperationException(\"doesn't support right now\");\n    }\n\n    // ============== setter ==============\n    public void setnThreads(int nThreads) {\n        this.nThreads = nThreads;\n    }\n\n    public void setNeedConcurrent(boolean needConcurrent) {\n        this.needConcurrent = needConcurrent;\n    }\n\n    public void setAlarmRuleService(AlarmRuleService alarmRuleService) {\n        this.alarmRuleService = alarmRuleService;\n    }\n\n    public void setPipelineMonitor(Monitor pipelineMonitor) {\n        this.pipelineMonitor = pipelineMonitor;\n    }\n\n    public void setChannelService(ChannelService channelService) {\n        this.channelService = channelService;\n    }\n\n    public void setArbitrateManageService(ArbitrateManageService arbitrateManageService) {\n        this.arbitrateManageService = arbitrateManageService;\n    }\n\n    public void setRestartAlarmRecovery(AlarmRecovery restartAlarmRecovery) {\n        this.restartAlarmRecovery = restartAlarmRecovery;\n    }\n\n    public void setRecoveryPaused(boolean recoveryPaused) {\n        this.recoveryPaused = recoveryPaused;\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/monitor/impl/PipelineMonitor.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.monitor.impl;\n\nimport java.util.Date;\nimport java.util.LinkedList;\nimport java.util.List;\n\nimport javax.annotation.Resource;\n\nimport com.alibaba.otter.manager.biz.config.pipeline.PipelineService;\nimport com.alibaba.otter.manager.biz.monitor.Monitor;\nimport com.alibaba.otter.shared.arbitrate.ArbitrateManageService;\nimport com.alibaba.otter.shared.common.model.config.alarm.AlarmRule;\nimport com.alibaba.otter.shared.common.model.config.channel.ChannelStatus;\nimport com.alibaba.otter.shared.common.model.config.pipeline.Pipeline;\n\n/**\n * @author zebin.xuzb @ 2012-8-29\n * @version 4.1.0\n */\npublic class PipelineMonitor implements Monitor {\n\n    @Resource(name = \"delayStatRuleMonitor\")\n    private Monitor                delayStatRuleMonitor;\n\n    // @Resource(name = \"exceptionRuleMonitor\")\n    // private Monitor exceptionRuleMonitor;\n\n    @Resource(name = \"pipelineTimeoutRuleMonitor\")\n    private Monitor                pipelineTimeoutRuleMonitor;\n\n    @Resource(name = \"processTimeoutRuleMonitor\")\n    private Monitor                processTimeoutRuleMonitor;\n\n    @Resource(name = \"positionTimeoutRuleMonitor\")\n    private Monitor                positionTimeoutRuleMonitor;\n\n    @Resource(name = \"pipelineService\")\n    private PipelineService        pipelineService;\n\n    @Resource(name = \"arbitrateManageService\")\n    private ArbitrateManageService arbitrateManageService;\n\n    @Override\n    public void explore(List<AlarmRule> rules) {\n        Long pipelineId = rules.get(0).getPipelineId();\n        Pipeline pipeline = pipelineService.findById(pipelineId);\n        // 如果处于stop状态，则忽略报警\n        ChannelStatus status = arbitrateManageService.channelEvent().status(pipeline.getChannelId());\n        if (status == null || status.isStop()) {\n            return;\n        }\n\n        List<AlarmRule> delayTimeRules = new LinkedList<AlarmRule>();\n        List<AlarmRule> exceptonRules = new LinkedList<AlarmRule>();\n        List<AlarmRule> pipelineTimeoutRules = new LinkedList<AlarmRule>();\n        List<AlarmRule> processTimeoutRules = new LinkedList<AlarmRule>();\n        List<AlarmRule> positionTimeoutRules = new LinkedList<AlarmRule>();\n\n        Date now = new Date();\n        for (AlarmRule rule : rules) {\n            switch (rule.getMonitorName()) {\n                case DELAYTIME:\n                    if (checkEnable(rule, now)) {\n                        delayTimeRules.add(rule);\n                    }\n                    break;\n                case EXCEPTION:\n                    if (checkEnable(rule, now)) {\n                        exceptonRules.add(rule);\n                    }\n                    break;\n                case PIPELINETIMEOUT:\n                    if (checkEnable(rule, now)) {\n                        pipelineTimeoutRules.add(rule);\n                    }\n                    break;\n                case PROCESSTIMEOUT:\n                    if (checkEnable(rule, now)) {\n                        processTimeoutRules.add(rule);\n                    }\n                    break;\n                case POSITIONTIMEOUT:\n                    if (checkEnable(rule, now)) {\n                        positionTimeoutRules.add(rule);\n                    }\n                    break;\n                default:\n                    break;\n            }\n        }\n\n        if (!delayTimeRules.isEmpty()) {\n            delayStatRuleMonitor.explore(delayTimeRules);\n        }\n\n        if (!pipelineTimeoutRules.isEmpty()) {\n            pipelineTimeoutRuleMonitor.explore(pipelineTimeoutRules);\n        }\n\n        if (!processTimeoutRules.isEmpty()) {\n            processTimeoutRuleMonitor.explore(processTimeoutRules);\n        }\n\n        if (!positionTimeoutRules.isEmpty()) {\n            positionTimeoutRuleMonitor.explore(positionTimeoutRules);\n        }\n\n    }\n\n    private boolean checkEnable(AlarmRule rule, Date now) {\n        return rule.getPauseTime() == null || rule.getPauseTime().before(now);\n    }\n\n    public void explore() {\n        throw new UnsupportedOperationException();\n    }\n\n    public void explore(Long... pipelineIds) {\n        throw new UnsupportedOperationException();\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/monitor/impl/PipelineTimeoutRuleMonitor.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.monitor.impl;\n\nimport java.util.Date;\nimport java.util.List;\n\nimport javax.annotation.Resource;\n\nimport org.apache.commons.lang.StringUtils;\nimport org.springframework.util.CollectionUtils;\n\nimport com.alibaba.otter.manager.biz.monitor.MonitorRuleExplorerRegisty;\nimport com.alibaba.otter.manager.biz.statistics.throughput.ThroughputStatService;\nimport com.alibaba.otter.manager.biz.statistics.throughput.param.ThroughputCondition;\nimport com.alibaba.otter.shared.common.model.config.alarm.AlarmRule;\nimport com.alibaba.otter.shared.common.model.config.alarm.MonitorName;\nimport com.alibaba.otter.shared.common.model.statistics.throughput.ThroughputStat;\nimport com.alibaba.otter.shared.common.model.statistics.throughput.ThroughputType;\n\n/**\n * @author zebin.xuzb @ 2012-8-29\n * @version 4.1.0\n */\npublic class PipelineTimeoutRuleMonitor extends AbstractRuleMonitor {\n\n    private static final String   TIME_OUT_MESSAGE = \"pid:%s elapsed %s seconds no sync\";\n\n    @Resource(name = \"throughputStatService\")\n    private ThroughputStatService throughputStatService;\n\n    PipelineTimeoutRuleMonitor(){\n        MonitorRuleExplorerRegisty.register(MonitorName.PIPELINETIMEOUT, this);\n    }\n\n    @Override\n    public void explore(List<AlarmRule> rules) {\n        if (CollectionUtils.isEmpty(rules)) {\n            return;\n        }\n        Long pipelineId = rules.get(0).getPipelineId();\n\n        ThroughputCondition condition = new ThroughputCondition();\n        condition.setPipelineId(pipelineId);\n        condition.setType(ThroughputType.ROW);\n        ThroughputStat stat = throughputStatService.findThroughputStatByPipelineId(condition);\n\n        long latestSyncTime = 0L;\n        if (stat != null && stat.getGmtModified() != null) {\n            Date modifiedDate = stat.getGmtModified();\n            latestSyncTime = modifiedDate.getTime();\n        }\n        long now = System.currentTimeMillis();\n        long elapsed = now - latestSyncTime;\n        boolean flag = false;\n        for (AlarmRule rule : rules) {\n            flag |= checkTimeout(rule, elapsed);\n        }\n\n        if (flag) {\n            logRecordAlarm(pipelineId, MonitorName.PIPELINETIMEOUT,\n                           String.format(TIME_OUT_MESSAGE, pipelineId, (elapsed / 1000)));\n        }\n    }\n\n    private boolean checkTimeout(AlarmRule rule, long elapsed) {\n        if (!inPeriod(rule)) {\n            return false;\n        }\n\n        String matchValue = rule.getMatchValue();\n        matchValue = StringUtils.substringBeforeLast(matchValue, \"@\");\n        Long maxSpentTime = Long.parseLong(StringUtils.trim(matchValue));\n        // sinceLastSync是毫秒，而 maxSpentTime 是秒\n        if (elapsed >= (maxSpentTime * 1000)) {\n            sendAlarm(rule, String.format(TIME_OUT_MESSAGE, rule.getPipelineId(), (elapsed / 1000)));\n            return true;\n        }\n        return false;\n    }\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/monitor/impl/PositionTimeoutRuleMonitor.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.monitor.impl;\n\nimport java.util.Date;\nimport java.util.List;\n\nimport org.apache.commons.lang.StringUtils;\nimport org.springframework.util.CollectionUtils;\n\nimport com.alibaba.otter.manager.biz.config.pipeline.PipelineService;\nimport com.alibaba.otter.manager.biz.monitor.MonitorRuleExplorerRegisty;\nimport com.alibaba.otter.shared.arbitrate.ArbitrateViewService;\nimport com.alibaba.otter.shared.arbitrate.model.PositionEventData;\nimport com.alibaba.otter.shared.common.model.config.alarm.AlarmRule;\nimport com.alibaba.otter.shared.common.model.config.alarm.MonitorName;\nimport com.alibaba.otter.shared.common.model.config.pipeline.Pipeline;\n\n/**\n * 位点超时监控\n * \n * @author jianghang 2012-12-12 上午10:33:12\n * @version 4.1.3\n */\npublic class PositionTimeoutRuleMonitor extends AbstractRuleMonitor {\n\n    private PipelineService      pipelineService;\n    private ArbitrateViewService arbitrateViewService;\n    private static final String  TIME_OUT_MESSAGE = \"pid:%s position %s seconds no update\";\n\n    PositionTimeoutRuleMonitor(){\n        MonitorRuleExplorerRegisty.register(MonitorName.POSITIONTIMEOUT, this);\n    }\n\n    @Override\n    public void explore(List<AlarmRule> rules) {\n        if (CollectionUtils.isEmpty(rules)) {\n            return;\n        }\n        Long pipelineId = rules.get(0).getPipelineId();\n        Pipeline pipeline = pipelineService.findById(pipelineId);\n        PositionEventData data = arbitrateViewService.getCanalCursor(pipeline.getParameters().getDestinationName(),\n                                                                     pipeline.getParameters().getMainstemClientId());\n\n        long latestSyncTime = 0L;\n        if (data != null && data.getModifiedTime() != null) {\n            Date modifiedDate = data.getModifiedTime();\n            latestSyncTime = modifiedDate.getTime();\n        } else {\n            return;\n        }\n\n        long now = System.currentTimeMillis();\n        long elapsed = now - latestSyncTime;\n        boolean flag = false;\n        for (AlarmRule rule : rules) {\n            flag |= checkTimeout(rule, elapsed);\n        }\n\n        if (flag) {\n            logRecordAlarm(pipelineId, MonitorName.POSITIONTIMEOUT,\n                           String.format(TIME_OUT_MESSAGE, pipelineId, (elapsed / 1000)));\n        }\n    }\n\n    private boolean checkTimeout(AlarmRule rule, long elapsed) {\n        if (!inPeriod(rule)) {\n            return false;\n        }\n\n        String matchValue = rule.getMatchValue();\n        matchValue = StringUtils.substringBeforeLast(matchValue, \"@\");\n        Long maxSpentTime = Long.parseLong(StringUtils.trim(matchValue));\n        // sinceLastSync是毫秒，而 maxSpentTime 是秒\n        if (elapsed >= (maxSpentTime * 1000)) {\n            sendAlarm(rule, String.format(TIME_OUT_MESSAGE, rule.getPipelineId(), (elapsed / 1000)));\n            return true;\n        }\n        return false;\n    }\n\n    public void setPipelineService(PipelineService pipelineService) {\n        this.pipelineService = pipelineService;\n    }\n\n    public void setArbitrateViewService(ArbitrateViewService arbitrateViewService) {\n        this.arbitrateViewService = arbitrateViewService;\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/monitor/impl/ProcessTimeoutRuleMonitor.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.monitor.impl;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\n\nimport javax.annotation.Resource;\n\nimport org.apache.commons.lang.StringUtils;\nimport org.springframework.util.CollectionUtils;\n\nimport com.alibaba.otter.manager.biz.monitor.MonitorRuleExplorerRegisty;\nimport com.alibaba.otter.manager.biz.statistics.stage.ProcessStatService;\nimport com.alibaba.otter.shared.common.model.config.alarm.AlarmRule;\nimport com.alibaba.otter.shared.common.model.config.alarm.MonitorName;\nimport com.alibaba.otter.shared.common.model.statistics.stage.ProcessStat;\n\n/**\n * @author zebin.xuzb @ 2012-8-29\n * @version 4.1.0\n */\npublic class ProcessTimeoutRuleMonitor extends AbstractRuleMonitor {\n\n    private static final String TIME_OUT_MESSAGE = \"pid:%s processIds:%s elapsed %s seconds\";\n\n    @Resource(name = \"processStatService\")\n    private ProcessStatService  processStatService;\n\n    ProcessTimeoutRuleMonitor(){\n        MonitorRuleExplorerRegisty.register(MonitorName.PIPELINETIMEOUT, this);\n    }\n\n    @Override\n    public void explore(List<AlarmRule> rules) {\n        if (CollectionUtils.isEmpty(rules)) {\n            return;\n        }\n        Long pipelineId = rules.get(0).getPipelineId();\n\n        List<ProcessStat> processStats = processStatService.listRealtimeProcessStat(pipelineId);\n        if (CollectionUtils.isEmpty(processStats)) {\n            return;\n        }\n\n        long now = System.currentTimeMillis();\n        Map<Long, Long> processTime = new HashMap<Long, Long>();\n        for (ProcessStat processStat : processStats) {\n            Long timeout = 0L;\n            if (!CollectionUtils.isEmpty(processStat.getStageStats())) {\n                timeout = now - processStat.getStageStats().get(0).getStartTime();\n            }\n            processTime.put(processStat.getProcessId(), timeout);\n        }\n\n        String message = StringUtils.EMPTY;\n        for (AlarmRule rule : rules) {\n            if (message.isEmpty()) {\n                message = checkTimeout(rule, processTime);\n            } else {\n                checkTimeout(rule, processTime);\n            }\n        }\n\n        if (!message.isEmpty()) {\n            logRecordAlarm(pipelineId, MonitorName.PROCESSTIMEOUT, message);\n        }\n\n    }\n\n    private String checkTimeout(AlarmRule rule, Map<Long, Long> processTime) {\n        if (!inPeriod(rule)) {\n            return StringUtils.EMPTY;\n        }\n\n        String matchValue = rule.getMatchValue();\n        matchValue = StringUtils.substringBeforeLast(matchValue, \"@\");\n        Long maxSpentTime = Long.parseLong(StringUtils.trim(matchValue));\n        List<Long> timeoutProcessIds = new LinkedList<Long>();\n        Collections.sort(timeoutProcessIds);\n        long maxSpent = 0;\n        for (Entry<Long, Long> entry : processTime.entrySet()) {\n            // maxSpentTime 是秒，而processTime的value是毫秒\n            if (entry.getValue() >= (maxSpentTime * 1000)) {\n                timeoutProcessIds.add(entry.getKey());\n                maxSpent = maxSpent > entry.getValue() ? maxSpent : entry.getValue();\n            }\n        }\n\n        if (CollectionUtils.isEmpty(timeoutProcessIds)) {\n            return StringUtils.EMPTY;\n        }\n\n        String processIds = StringUtils.join(timeoutProcessIds, \",\");\n        String message = String.format(TIME_OUT_MESSAGE, rule.getPipelineId(), processIds, (maxSpent / 1000));\n        sendAlarm(rule, message);\n        return message;\n    }\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/monitor/impl/RestartAlarmRecovery.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.monitor.impl;\n\nimport java.util.concurrent.DelayQueue;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.DisposableBean;\nimport org.springframework.beans.factory.InitializingBean;\n\nimport com.alibaba.otter.manager.biz.config.channel.ChannelService;\nimport com.alibaba.otter.manager.biz.config.pipeline.PipelineService;\nimport com.alibaba.otter.manager.biz.monitor.AlarmRecovery;\nimport com.alibaba.otter.manager.biz.monitor.PassiveMonitor;\nimport com.alibaba.otter.shared.arbitrate.ArbitrateManageService;\nimport com.alibaba.otter.shared.common.model.config.alarm.AlarmRule;\nimport com.alibaba.otter.shared.common.model.config.alarm.MonitorName;\nimport com.alibaba.otter.shared.common.model.config.pipeline.Pipeline;\nimport com.alibaba.otter.shared.communication.model.arbitrate.NodeAlarmEvent;\n\n/**\n * 基于RESTART命令的恢复机制\n * \n * @author jianghang 2012-9-19 下午04:44:12\n * @version 4.1.0\n */\npublic class RestartAlarmRecovery implements AlarmRecovery, InitializingBean, DisposableBean {\n\n    private static final Logger                       logger    = LoggerFactory.getLogger(RestartAlarmRecovery.class);\n    private volatile DelayQueue<AlarmRecoveryDelayed> queue     = new DelayQueue<AlarmRecoveryDelayed>();\n    private long                                      checkTime = 10 * 1000L;                                         // 5秒\n    private ExecutorService                           executor;\n    private PipelineService                           pipelineService;\n    private PassiveMonitor                            exceptionRuleMonitor;\n    private ArbitrateManageService                    arbitrateManageService;\n    private ChannelService                            channelService;\n\n    public void recovery(Long channelId) {\n        AlarmRecoveryDelayed delayed = new AlarmRecoveryDelayed(channelId, -1, false, checkTime);\n        // 做异步处理，避免并发时重复执行recovery\n        synchronized (queue) {\n            if (!queue.contains(delayed)) {\n                queue.add(delayed);\n            }\n        }\n    }\n\n    public void recovery(AlarmRule alarmRule) {\n        Pipeline pipeline = pipelineService.findById(alarmRule.getPipelineId());\n        AlarmRecoveryDelayed delayed = new AlarmRecoveryDelayed(pipeline.getChannelId(), alarmRule.getId(), false,\n                                                                checkTime);\n        // 做异步处理，避免并发时重复执行recovery\n        synchronized (queue) {\n            if (!queue.contains(delayed)) {\n                queue.add(delayed);\n            }\n        }\n    }\n\n    public void recovery(AlarmRule alarmRule, long alarmCount) {\n        if (alarmCount >= alarmRule.getRecoveryThresold()) {\n            synchronized (queue) {\n                // 做异步处理，避免并发时重复执行recovery\n                Pipeline pipeline = pipelineService.findById(alarmRule.getPipelineId());\n                // 超过2倍阀值，强制停止一下通道释放一下内存\n                boolean needStop = (alarmCount >= alarmRule.getRecoveryThresold() + 1);// recovery的下一次启用修复\n                AlarmRecoveryDelayed delayed = new AlarmRecoveryDelayed(pipeline.getChannelId(), alarmRule.getId(),\n                                                                        needStop, checkTime);\n                if (!queue.contains(delayed)) {\n                    queue.add(delayed);\n                }\n            }\n        }\n    }\n\n    private boolean processRecovery(Long channelId, Long ruleId, boolean needStop) {\n        boolean result = true;\n        if (!needStop) {\n            result = arbitrateManageService.channelEvent().restart(channelId);\n            if (result) {\n                channelService.notifyChannel(channelId);// 推送一下配置\n            }\n        } else {\n            // 解决process rpc模式下释放不完整，通过stop完整释放一次所有对象资源\n            channelService.stopChannel(channelId);\n            channelService.startChannel(channelId);\n        }\n\n        NodeAlarmEvent alarm = new NodeAlarmEvent();\n        alarm.setPipelineId(-1L);\n        alarm.setTitle(MonitorName.EXCEPTION.name());\n        if (result) {\n            if (!needStop) {\n                alarm.setMessage(String.format(\"cid:%s restart recovery successful for rid:%s\",\n                                               String.valueOf(channelId), String.valueOf(ruleId)));\n            } else {\n                alarm.setMessage(String.format(\"cid:%s stop recovery successful for rid:%s\", String.valueOf(channelId),\n                                               String.valueOf(ruleId)));\n            }\n\n            try {\n                exceptionRuleMonitor.feed(alarm, alarm.getPipelineId());\n            } catch (Exception e) {\n                logger.error(String.format(\"ERROR # exceptionRuleMonitor error for %s\", alarm.toString()), e);\n            }\n        }\n\n        return result;\n    }\n\n    public void afterPropertiesSet() throws Exception {\n        executor = Executors.newFixedThreadPool(1);\n        executor.submit(new Runnable() {\n\n            public void run() {\n                while (true) {\n                    AlarmRecoveryDelayed delay = null;\n                    try {\n                        delay = queue.take();\n                        processRecovery(delay.getChannelId(), delay.getRuleId(), delay.isStop());\n                    } catch (Throwable e) {\n                        // 出错了，重新加入补救处理\n                        if (!queue.contains(delay)) {\n                            queue.add(delay);\n                        }\n                        logger.error(String.format(\"error happened with [%s]\", delay.toString()), e);\n                    }\n                }\n            }\n        });\n    }\n\n    public void destroy() throws Exception {\n        executor.shutdownNow();\n    }\n\n    public void setArbitrateManageService(ArbitrateManageService arbitrateManageService) {\n        this.arbitrateManageService = arbitrateManageService;\n    }\n\n    public void setPipelineService(PipelineService pipelineService) {\n        this.pipelineService = pipelineService;\n    }\n\n    public void setExceptionRuleMonitor(PassiveMonitor exceptionRuleMonitor) {\n        this.exceptionRuleMonitor = exceptionRuleMonitor;\n    }\n\n    public void setChannelService(ChannelService channelService) {\n        this.channelService = channelService;\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/monitor/impl/SelfMonitor.java",
    "content": "package com.alibaba.otter.manager.biz.monitor.impl;\n\nimport java.util.List;\nimport java.util.concurrent.ScheduledExecutorService;\nimport java.util.concurrent.ScheduledFuture;\nimport java.util.concurrent.ScheduledThreadPoolExecutor;\nimport java.util.concurrent.ThreadPoolExecutor;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.atomic.AtomicBoolean;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.DisposableBean;\nimport org.springframework.beans.factory.InitializingBean;\n\nimport com.alibaba.otter.manager.biz.monitor.Monitor;\nimport com.alibaba.otter.shared.common.model.config.alarm.AlarmRule;\nimport com.alibaba.otter.shared.common.utils.thread.NamedThreadFactory;\n\n/**\n * jvm内自动运行，不需要通过外部定时触发\n * \n * @author jianghang 2013-9-6 上午10:22:28\n * @since 4.2.2\n */\npublic class SelfMonitor implements Monitor, InitializingBean, DisposableBean {\n\n    protected static final Logger    log          = LoggerFactory.getLogger(\"monitorInfo\");\n    private static final int         DEFAULT_POOL = 1;\n    private ScheduledExecutorService executor;\n    private ScheduledFuture          future;\n    private GlobalMonitor            monitor;\n    private AtomicBoolean            enable       = new AtomicBoolean(true);\n    private int                      interval     = 120;\n\n    public void explore() {\n        monitor.explore();\n    }\n\n    public void explore(Long... pipelineIds) {\n        monitor.explore(pipelineIds);\n    }\n\n    public void explore(List<AlarmRule> rules) {\n        monitor.explore(rules);\n    }\n\n    public void destroy() throws Exception {\n        if (enable.get()) {\n            stop();\n        }\n    }\n\n    public void afterPropertiesSet() throws Exception {\n        if (enable.get()) {\n            start();\n        }\n    }\n\n    private synchronized void start() {\n        if (executor == null) {\n            executor = new ScheduledThreadPoolExecutor(DEFAULT_POOL, new NamedThreadFactory(\"Self-Monitor\"),\n                                                       new ThreadPoolExecutor.CallerRunsPolicy());\n        }\n        if (future == null) {\n            future = executor.scheduleWithFixedDelay(new Runnable() {\n\n                public void run() {\n                    try {\n                        monitor.explore();// 定时调用 \n                    } catch (Exception e) {\n                        log.error(\"self-monitor failed.\", e);\n                    }\n                }\n            }, interval, interval, TimeUnit.SECONDS);\n        }\n    }\n\n    private synchronized void stop() {\n        if (future != null) {\n            future.cancel(true);\n        }\n\n        if (executor != null) {\n            try {\n                executor.awaitTermination(2 * 1000, TimeUnit.MILLISECONDS);\n            } catch (InterruptedException e) {\n                // ignore\n            }\n        }\n    }\n\n    public void setMonitor(GlobalMonitor monitor) {\n        this.monitor = monitor;\n    }\n\n    public void setEnable(boolean enable) {\n        this.enable.set(enable);\n    }\n\n    public void setInterval(int interval) {\n        this.interval = interval;\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/remote/ArbitrateRemoteService.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.remote;\n\nimport com.alibaba.otter.shared.communication.model.arbitrate.NodeAlarmEvent;\nimport com.alibaba.otter.shared.communication.model.arbitrate.StopChannelEvent;\nimport com.alibaba.otter.shared.communication.model.arbitrate.StopNodeEvent;\n\npublic interface ArbitrateRemoteService {\n\n    /**\n     * 处理node信息报警\n     */\n    public void onNodeAlarm(NodeAlarmEvent event);\n\n    /**\n     * 处理客户端关闭channel的事件\n     */\n    public void onStopNode(StopNodeEvent event);\n\n    /**\n     * 处理客户端关闭channel的事件\n     */\n    public void onStopChannel(StopChannelEvent event);\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/remote/CanalRemoteService.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.remote;\n\nimport com.alibaba.otter.canal.instance.manager.model.Canal;\nimport com.alibaba.otter.shared.communication.model.canal.FindCanalEvent;\nimport com.alibaba.otter.shared.communication.model.canal.FindFilterEvent;\n\n/**\n * canal远程服务接口\n * \n * @author jianghang 2012-8-1 下午04:12:41\n * @version 4.1.0\n */\npublic interface CanalRemoteService {\n\n    /**\n     * 接收客户端的查询Canal请求\n     */\n    public Canal onFindCanal(FindCanalEvent event);\n\n    /**\n     * 接收客户端的查询filter请求\n     */\n    public String onFindFilter(FindFilterEvent event);\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/remote/ConfigRemoteService.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.remote;\n\nimport java.util.List;\n\nimport com.alibaba.otter.shared.common.model.config.channel.Channel;\nimport com.alibaba.otter.shared.common.model.config.node.Node;\nimport com.alibaba.otter.shared.communication.model.config.FindChannelEvent;\nimport com.alibaba.otter.shared.communication.model.config.FindMediaEvent;\nimport com.alibaba.otter.shared.communication.model.config.FindNodeEvent;\nimport com.alibaba.otter.shared.communication.model.config.FindTaskEvent;\n\n/**\n * 针对manager config对象的远程服务接口定义\n * \n * @author jianghang\n */\npublic interface ConfigRemoteService {\n\n    /**\n     * 将channel对象重新通知下对应的工作节点\n     */\n    public boolean notifyChannel(Channel channel);\n\n    /**\n     * 接收客户端的查询channel请求\n     */\n    public Channel onFindChannel(FindChannelEvent event);\n\n    /**\n     * 接收客户端的查询Node请求\n     */\n    public Node onFindNode(FindNodeEvent event);\n\n    /**\n     * 接收客户端根据nid查询需要处理的Channel请求\n     */\n    public List<Channel> onFindTask(FindTaskEvent event);\n\n    /**\n     * 返回media信息\n     */\n    public String onFindMedia(FindMediaEvent event);\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/remote/NodeRemoteService.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.remote;\n\nimport java.util.List;\n\n/**\n * 查询node节点的一些运行信息\n * \n * @author jianghang 2012-7-30 上午10:36:14\n */\npublic interface NodeRemoteService {\n\n    /**\n     * 返回当前运行pipeline数量，可能只运行S/E/T/L的某个模块\n     */\n    public int getRunningPipelineCount(Long nid);\n\n    /**\n     * 返回当前运行中的pipeline的id列表\n     */\n    public List<Long> getRunningPipelines(Long nid);\n\n    /**\n     * 获取当前使用的heap区大小\n     */\n    public String getHeapMemoryUsage(Long nid);\n\n    /**\n     * 获取系统对应的load\n     */\n    public String getNodeSystemInfo(Long nid);\n\n    /**\n     * 获取node节点对应的版本信息\n     */\n    public String getNodeVersionInfo(Long nid);\n\n    /**\n     * 获取node共享线程线程池的线程数\n     */\n    public int getThreadPoolSize(Long nid);\n\n    /**\n     * 获取当前node共享线程的当前活跃线程数\n     */\n    public int getThreadActiveSize(Long nid);\n\n    /**\n     * 设置是否开启profile统计\n     */\n    public void setProfile(Long nid, boolean profile);\n\n    /**\n     * 设置对应的s/e/t/l seda模型的线程池大小\n     */\n    public void setThreadPoolSize(Long nid, int size);\n\n    /**\n     * 当前节点是否运行select\n     */\n    public boolean isSelectRunning(Long nid, Long pipelineId);\n\n    /**\n     * 当前节点是否运行extract\n     */\n    public boolean isExtractRunning(Long nid, Long pipelineId);\n\n    /**\n     * 当前节点是否运行transform\n     */\n    public boolean isTransformRunning(Long nid, Long pipelineId);\n\n    /**\n     * 当前节点是否运行load\n     */\n    public boolean isLoadRunning(Long nid, Long pipelineId);\n\n    /**\n     * select stage统计信息\n     */\n    public String selectStageAggregation(Long nid, Long pipelineId);\n\n    /**\n     * extract stage统计信息\n     */\n    public String extractStageAggregation(Long nid, Long pipelineId);\n\n    /**\n     * transform stage统计信息\n     */\n    public String transformStageAggregation(Long nid, Long pipelineId);\n\n    /**\n     * load stage统计信息\n     */\n    public String loadStageAggregation(Long nid, Long pipelineId);\n\n    /**\n     * select pending队列信息\n     */\n    public String selectPendingProcess(Long nid, Long pipelineId);\n\n    /**\n     * extract pending队列信息\n     */\n    public String extractPendingProcess(Long nid, Long pipelineId);\n\n    /**\n     * transform pending队列信息\n     */\n    public String transformPendingProcess(Long nid, Long pipelineId);\n\n    /**\n     * load pending队列信息\n     */\n    public String loadPendingProcess(Long nid, Long pipelineId);\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/remote/StatsRemoteService.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.remote;\n\nimport com.alibaba.otter.shared.communication.model.statistics.DelayCountEvent;\nimport com.alibaba.otter.shared.communication.model.statistics.TableStatEvent;\nimport com.alibaba.otter.shared.communication.model.statistics.ThroughputStatEvent;\n\n/**\n * 统计相关远程接口定义\n * \n * @author jianghang\n */\npublic interface StatsRemoteService {\n\n    /**\n     * 接收inc delay统计信息\n     */\n    public void onDelayCount(DelayCountEvent event);\n\n    /**\n     * 接收table load相关数据信息\n     */\n    public void onTableStat(TableStatEvent event);\n\n    /**\n     * 接收吞吐量相关统计信息\n     */\n    public void onThroughputStat(ThroughputStatEvent event);\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/remote/impl/ArbitrateRemoteServiceImpl.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.remote.impl;\n\nimport java.util.List;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.util.Assert;\n\nimport com.alibaba.otter.manager.biz.config.channel.ChannelService;\nimport com.alibaba.otter.manager.biz.monitor.PassiveMonitor;\nimport com.alibaba.otter.manager.biz.remote.ArbitrateRemoteService;\nimport com.alibaba.otter.shared.arbitrate.ArbitrateManageService;\nimport com.alibaba.otter.shared.common.model.config.channel.Channel;\nimport com.alibaba.otter.shared.common.model.config.channel.ChannelStatus;\nimport com.alibaba.otter.shared.communication.core.CommunicationRegistry;\nimport com.alibaba.otter.shared.communication.model.arbitrate.ArbitrateEventType;\nimport com.alibaba.otter.shared.communication.model.arbitrate.NodeAlarmEvent;\nimport com.alibaba.otter.shared.communication.model.arbitrate.StopChannelEvent;\nimport com.alibaba.otter.shared.communication.model.arbitrate.StopNodeEvent;\n\n/**\n * 处理仲裁器事件的远程接口\n * \n * @author jianghang 2011-11-24 下午09:19:09\n * @version 4.0.0\n */\npublic class ArbitrateRemoteServiceImpl implements ArbitrateRemoteService {\n\n    private static final Logger    logger = LoggerFactory.getLogger(ArbitrateRemoteServiceImpl.class);\n    private ArbitrateManageService arbitrateManageService;\n    private ChannelService         channelService;\n    private PassiveMonitor         exceptionRuleMonitor;\n\n    public ArbitrateRemoteServiceImpl(){\n        CommunicationRegistry.regist(ArbitrateEventType.nodeAlarm, this);\n        CommunicationRegistry.regist(ArbitrateEventType.stopChannel, this);\n        CommunicationRegistry.regist(ArbitrateEventType.stopNode, this);\n    }\n\n    public void onNodeAlarm(NodeAlarmEvent event) {\n        try {\n            exceptionRuleMonitor.feed(event, event.getPipelineId());\n        } catch (Exception e) {\n            logger.error(String.format(\"ERROR # exceptionRuleMonitor error for  %s\", event.toString()), e);\n        }\n    }\n\n    public void onStopChannel(StopChannelEvent event) {\n        channelService.stopChannel(event.getChannelId());\n    }\n\n    public void onStopNode(StopNodeEvent event) {\n        Assert.notNull(event);\n\n        List<Channel> channels = channelService.listByNodeId(event.getNid(), ChannelStatus.START);\n        for (Channel channel : channels) {// 重启一下对应的channel\n            boolean result = arbitrateManageService.channelEvent().restart(channel.getId());\n            if (result) {\n                channelService.notifyChannel(channel.getId());// 推送一下配置\n            }\n        }\n    }\n\n    // ===================== setter / getter =====================\n\n    public void setChannelService(ChannelService channelService) {\n        this.channelService = channelService;\n    }\n\n    public void setArbitrateManageService(ArbitrateManageService arbitrateManageService) {\n        this.arbitrateManageService = arbitrateManageService;\n    }\n\n    public void setExceptionRuleMonitor(PassiveMonitor exceptionRuleMonitor) {\n        this.exceptionRuleMonitor = exceptionRuleMonitor;\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/remote/impl/CanalRemoteServiceImpl.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.remote.impl;\n\nimport com.alibaba.otter.canal.instance.manager.model.Canal;\nimport com.alibaba.otter.canal.instance.manager.model.CanalParameter;\nimport com.alibaba.otter.manager.biz.config.canal.CanalService;\nimport com.alibaba.otter.manager.biz.remote.CanalRemoteService;\nimport com.alibaba.otter.shared.communication.core.CommunicationRegistry;\nimport com.alibaba.otter.shared.communication.model.canal.CanalEventType;\nimport com.alibaba.otter.shared.communication.model.canal.FindCanalEvent;\nimport com.alibaba.otter.shared.communication.model.canal.FindFilterEvent;\n\n/**\n * 获取对应的canal配置\n * \n * @author jianghang 2012-8-1 下午04:44:40\n * @version 4.1.0\n */\npublic class CanalRemoteServiceImpl implements CanalRemoteService {\n\n    private CanalService canalService;\n    private String       tsdbJdbcUrl;\n    private String       tsdbJdbcUserName;\n    private String       tsdbJdbcPassword;\n\n    public CanalRemoteServiceImpl(){\n        CommunicationRegistry.regist(CanalEventType.findCanal, this);\n        CommunicationRegistry.regist(CanalEventType.findFilter, this);\n    }\n\n    public Canal onFindCanal(FindCanalEvent event) {\n        String destination = event.getDestination();\n        Canal canal = canalService.findByName(destination);\n        // set default jdbc url\n        CanalParameter parameter = canal.getCanalParameter();\n        if (parameter.getTsdbEnable() != null && parameter.getTsdbEnable()) {\n            parameter.setTsdbJdbcUrl(tsdbJdbcUrl);\n            parameter.setTsdbJdbcUserName(tsdbJdbcUserName);\n            parameter.setTsdbJdbcPassword(tsdbJdbcPassword);\n        }\n        return canal;\n    }\n\n    public String onFindFilter(FindFilterEvent event) {\n        // TODO 根据同步队列的需求，直接设定filter过滤\n        return \".*\\\\..*\";\n    }\n\n    public void setCanalService(CanalService canalService) {\n        this.canalService = canalService;\n    }\n\n    public void setTsdbJdbcUrl(String tsdbJdbcUrl) {\n        this.tsdbJdbcUrl = tsdbJdbcUrl;\n    }\n\n    public void setTsdbJdbcUserName(String tsdbJdbcUserName) {\n        this.tsdbJdbcUserName = tsdbJdbcUserName;\n    }\n\n    public void setTsdbJdbcPassword(String tsdbJdbcPassword) {\n        this.tsdbJdbcPassword = tsdbJdbcPassword;\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/remote/impl/ConfigRemoteServiceImpl.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.remote.impl;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\nimport org.apache.commons.lang.ArrayUtils;\nimport org.apache.commons.lang.StringUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.util.Assert;\nimport org.springframework.util.CollectionUtils;\n\nimport com.alibaba.otter.manager.biz.common.exceptions.ManagerException;\nimport com.alibaba.otter.manager.biz.config.channel.ChannelService;\nimport com.alibaba.otter.manager.biz.config.datamatrix.DataMatrixService;\nimport com.alibaba.otter.manager.biz.config.node.NodeService;\nimport com.alibaba.otter.manager.biz.remote.ConfigRemoteService;\nimport com.alibaba.otter.shared.common.model.config.channel.Channel;\nimport com.alibaba.otter.shared.common.model.config.channel.ChannelStatus;\nimport com.alibaba.otter.shared.common.model.config.data.DataMatrix;\nimport com.alibaba.otter.shared.common.model.config.node.Node;\nimport com.alibaba.otter.shared.common.model.config.pipeline.Pipeline;\nimport com.alibaba.otter.shared.common.utils.JsonUtils;\nimport com.alibaba.otter.shared.communication.core.CommunicationClient;\nimport com.alibaba.otter.shared.communication.core.CommunicationRegistry;\nimport com.alibaba.otter.shared.communication.model.config.ConfigEventType;\nimport com.alibaba.otter.shared.communication.model.config.FindChannelEvent;\nimport com.alibaba.otter.shared.communication.model.config.FindMediaEvent;\nimport com.alibaba.otter.shared.communication.model.config.FindNodeEvent;\nimport com.alibaba.otter.shared.communication.model.config.FindTaskEvent;\nimport com.alibaba.otter.shared.communication.model.config.NotifyChannelEvent;\n\n/**\n * Config的remote接口处理\n * \n * @author jianghang 2011-10-21 下午02:53:53\n * @version 4.0.0\n */\npublic class ConfigRemoteServiceImpl implements ConfigRemoteService {\n\n    private static final Logger logger = LoggerFactory.getLogger(ConfigRemoteServiceImpl.class);\n    private CommunicationClient communicationClient;\n    private ChannelService      channelService;\n    private NodeService         nodeService;\n    private DataMatrixService   dataMatrixService;\n\n    public ConfigRemoteServiceImpl(){\n        // 注册一下事件处理\n        CommunicationRegistry.regist(ConfigEventType.findChannel, this);\n        CommunicationRegistry.regist(ConfigEventType.findNode, this);\n        CommunicationRegistry.regist(ConfigEventType.findTask, this);\n        CommunicationRegistry.regist(ConfigEventType.findMedia, this);\n    }\n\n    public boolean notifyChannel(final Channel channel) {\n        Assert.notNull(channel);\n        // 获取所有的Node节点\n        NotifyChannelEvent event = new NotifyChannelEvent();\n        event.setChannel(channel);\n\n        Set<String> addrsSet = new HashSet<String>();\n\n        // 组装当前otter所有的存活的node节点\n        // List<Node> nodes = nodeService.listAll();\n        // for (Node node : nodes) {\n        // if (node.getStatus().isStart() &&\n        // StringUtils.isNotEmpty(node.getIp()) && node.getPort() != 0) {\n        // final String addr = node.getIp() + \":\" + node.getPort();\n        // addrsList.add(addr);\n        // }\n        // }\n\n        // 组装当前pipeline下的存活的node节点\n        for (Pipeline pipeline : channel.getPipelines()) {\n            List<Node> nodes = new ArrayList<Node>();\n            nodes.addAll(pipeline.getSelectNodes());\n            nodes.addAll(pipeline.getExtractNodes());\n            nodes.addAll(pipeline.getLoadNodes());\n            for (Node node : nodes) {\n                if (node.getStatus().isStart() && StringUtils.isNotEmpty(node.getIp()) && node.getPort() != 0) {\n                    String addr = node.getIp() + \":\" + node.getPort();\n                    if (node.getParameters().getUseExternalIp()) {\n                        addr = node.getParameters().getExternalIp() + \":\" + node.getPort();\n                    }\n                    addrsSet.add(addr);\n                }\n            }\n        }\n\n        List<String> addrsList = new ArrayList<String>(addrsSet);\n        if (CollectionUtils.isEmpty(addrsList) && channel.getStatus().isStart()) {\n            throw new ManagerException(\"no live node for notifyChannel\");\n        } else if (CollectionUtils.isEmpty(addrsList)) {\n            // 针对关闭操作，可直接处理\n            return true;\n        } else {\n            Collections.shuffle(addrsList);// 做一下随机，避免每次选择的机器都是同一台\n            try {\n                String[] addrs = addrsList.toArray(new String[addrsList.size()]);\n                List<Boolean> result = (List<Boolean>) communicationClient.call(addrs, event); // 推送配置\n                logger.info(\"## notifyChannel to [{}] channel[{}] result[{}]\",\n                    new Object[] { ArrayUtils.toString(addrs), channel.toString(), result });\n\n                boolean flag = true;\n                for (Boolean f : result) {\n                    flag &= f;\n                }\n\n                return flag;\n            } catch (Exception e) {\n                logger.error(\"## notifyChannel error!\", e);\n                throw new ManagerException(e);\n            }\n        }\n    }\n\n    /**\n     * 根据对应的工作节点机器id，获取相关的channel任务\n     */\n    public Channel onFindChannel(FindChannelEvent event) {\n        Assert.notNull(event);\n        Long channelId = event.getChannelId();\n        Long pipelineId = event.getPipelineId();\n        Channel channel = null;\n        if (channelId != null) {\n            channel = channelService.findById(channelId);\n        } else {\n            Assert.notNull(pipelineId);\n            channel = channelService.findByPipelineId(pipelineId);\n        }\n\n        return channel;\n    }\n\n    public Node onFindNode(FindNodeEvent event) {\n        Assert.notNull(event);\n        Assert.notNull(event.getNid());\n        return nodeService.findById(event.getNid());\n    }\n\n    public List<Channel> onFindTask(FindTaskEvent event) {\n        Assert.notNull(event);\n        Assert.notNull(event.getNid());\n        // 同时查询start/pause状态的同步任务，因为在发布时重启jvm刚好在执行stopNode的restart指令，此时channel处于pause状态，丢失了任务命令\n        return channelService.listByNodeId(event.getNid(), ChannelStatus.START, ChannelStatus.PAUSE);\n    }\n\n    public String onFindMedia(FindMediaEvent event) {\n        Assert.notNull(event);\n        Assert.notNull(event.getDataId());\n        DataMatrix matrix = dataMatrixService.findByGroupKey(event.getDataId());\n        return JsonUtils.marshalToString(matrix);\n    }\n\n    // =============== setter / getter ===================\n\n    public void setCommunicationClient(CommunicationClient communicationClient) {\n        this.communicationClient = communicationClient;\n    }\n\n    public void setChannelService(ChannelService channelService) {\n        this.channelService = channelService;\n    }\n\n    public void setNodeService(NodeService nodeService) {\n        this.nodeService = nodeService;\n    }\n\n    public void setDataMatrixService(DataMatrixService dataMatrixService) {\n        this.dataMatrixService = dataMatrixService;\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/remote/impl/NodeMBeanServiceImpl.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.remote.impl;\n\nimport java.text.MessageFormat;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.TimeUnit;\n\nimport javax.management.MBeanServerConnection;\nimport javax.management.ObjectName;\nimport javax.management.remote.JMXConnector;\nimport javax.management.remote.JMXConnectorFactory;\nimport javax.management.remote.JMXServiceURL;\n\nimport com.alibaba.otter.manager.biz.common.exceptions.ManagerException;\nimport com.alibaba.otter.manager.biz.config.node.NodeService;\nimport com.alibaba.otter.manager.biz.remote.NodeRemoteService;\nimport com.alibaba.otter.shared.common.model.config.node.Node;\nimport com.google.common.base.Function;\nimport com.google.common.collect.OtterMigrateMap;\n\n/**\n * 基于node Mbean获取数据的实现\n * \n * @author jianghang 2012-7-30 上午10:38:30\n */\npublic class NodeMBeanServiceImpl implements NodeRemoteService {\n\n    private static final String              MBEAN_NAME  = \"bean:name=otterControllor\";\n    private static final String              SERVICE_URL = \"service:jmx:rmi://{0}/jndi/rmi://{0}:{1}/mbean\";\n    private ObjectName                       objectName;\n    private NodeService                      nodeService;\n    private Map<Long, MBeanServerConnection> mbeanServers;\n\n    public NodeMBeanServiceImpl(){\n        try {\n            objectName = new ObjectName(MBEAN_NAME);\n        } catch (Exception e) {\n            throw new ManagerException(e);\n        }\n\n        mbeanServers = OtterMigrateMap.makeSoftValueComputingMapWithTimeout(new Function<Long, MBeanServerConnection>() {\n\n            public MBeanServerConnection apply(Long nid) {\n                Node node = nodeService.findById(nid);\n                String ip = node.getIp();\n                if (node.getParameters().getUseExternalIp()) {\n                    ip = node.getParameters().getExternalIp();\n                }\n\n                int port = node.getPort().intValue() + 1;\n                Integer mbeanPort = node.getParameters().getMbeanPort();\n                if (mbeanPort != null && mbeanPort != 0) {// 做个兼容处理，<=4.2.2版本没有mbeanPort设置\n                    port = mbeanPort;\n                }\n\n                try {\n                    JMXServiceURL serviceURL = new JMXServiceURL(MessageFormat.format(SERVICE_URL,\n                        ip,\n                        String.valueOf(port)));\n                    JMXConnector cntor = JMXConnectorFactory.connect(serviceURL, null);\n                    MBeanServerConnection mbsc = cntor.getMBeanServerConnection();\n                    return mbsc;\n                } catch (Exception e) {\n                    throw new ManagerException(e);\n                }\n            }\n\n        },5, TimeUnit.MINUTES);\n    }\n\n    public String getHeapMemoryUsage(Long nid) {\n        return (String) getAttribute(nid, \"HeapMemoryUsage\");\n    }\n\n    public String getNodeSystemInfo(Long nid) {\n        return (String) getAttribute(nid, \"NodeSystemInfo\");\n    }\n\n    public String getNodeVersionInfo(Long nid) {\n        return (String) getAttribute(nid, \"NodeVersionInfo\");\n    }\n\n    public int getRunningPipelineCount(Long nid) {\n        return (Integer) getAttribute(nid, \"RunningPipelineCount\");\n    }\n\n    public List<Long> getRunningPipelines(Long nid) {\n        return (List<Long>) getAttribute(nid, \"RunningPipelines\");\n    }\n\n    public int getThreadPoolSize(Long nid) {\n        return (Integer) getAttribute(nid, \"ThreadPoolSize\");\n    }\n\n    public void setProfile(Long nid, boolean profile) {\n        try {\n            mbeanServers.get(nid).invoke(objectName,\n                \"setProfile\",\n                new Object[] { profile },\n                new String[] { \"java.lang.Boolean\" });\n        } catch (Exception e) {\n            mbeanServers.remove(nid);\n            throw new ManagerException(e);\n        }\n    }\n\n    public void setThreadPoolSize(Long nid, int size) {\n        try {\n            mbeanServers.get(nid).invoke(objectName,\n                \"setThreadPoolSize\",\n                new Object[] { size },\n                new String[] { \"java.lang.Integer\" });\n        } catch (Exception e) {\n            mbeanServers.remove(nid);\n            throw new ManagerException(e);\n        }\n    }\n\n    public int getThreadActiveSize(Long nid) {\n        return (Integer) getAttribute(nid, \"ThreadActiveSize\");\n    }\n\n    public boolean isSelectRunning(Long nid, Long pipelineId) {\n        return (Boolean) invoke(nid, pipelineId, \"isSelectRunning\");\n    }\n\n    public boolean isExtractRunning(Long nid, Long pipelineId) {\n        return (Boolean) invoke(nid, pipelineId, \"isExtractRunning\");\n    }\n\n    public boolean isTransformRunning(Long nid, Long pipelineId) {\n        return (Boolean) invoke(nid, pipelineId, \"isTransformRunning\");\n    }\n\n    public boolean isLoadRunning(Long nid, Long pipelineId) {\n        return (Boolean) invoke(nid, pipelineId, \"isLoadRunning\");\n    }\n\n    public String selectStageAggregation(Long nid, Long pipelineId) {\n        return (String) invoke(nid, pipelineId, \"selectStageAggregation\");\n    }\n\n    public String extractStageAggregation(Long nid, Long pipelineId) {\n        return (String) invoke(nid, pipelineId, \"extractStageAggregation\");\n    }\n\n    public String transformStageAggregation(Long nid, Long pipelineId) {\n        return (String) invoke(nid, pipelineId, \"transformStageAggregation\");\n    }\n\n    public String loadStageAggregation(Long nid, Long pipelineId) {\n        return (String) invoke(nid, pipelineId, \"loadStageAggregation\");\n    }\n\n    public String selectPendingProcess(Long nid, Long pipelineId) {\n        return (String) invoke(nid, pipelineId, \"selectPendingProcess\");\n    }\n\n    public String extractPendingProcess(Long nid, Long pipelineId) {\n        return (String) invoke(nid, pipelineId, \"extractPendingProcess\");\n    }\n\n    public String transformPendingProcess(Long nid, Long pipelineId) {\n        return (String) invoke(nid, pipelineId, \"transformPendingProcess\");\n    }\n\n    public String loadPendingProcess(Long nid, Long pipelineId) {\n        return (String) invoke(nid, pipelineId, \"loadPendingProcess\");\n    }\n\n    private Object getAttribute(Long nid, String attribute) {\n        try {\n            return mbeanServers.get(nid).getAttribute(objectName, attribute);\n        } catch (Exception e) {\n            mbeanServers.remove(nid);\n            throw new ManagerException(e);\n        }\n    }\n\n    private Object invoke(Long nid, Long pipelineId, String method) {\n        try {\n            return mbeanServers.get(nid).invoke(objectName,\n                method,\n                new Object[] { pipelineId },\n                new String[] { \"java.lang.Long\" });\n        } catch (Exception e) {\n            mbeanServers.remove(nid);\n            throw new ManagerException(e);\n        }\n    }\n\n    // ====================== setter / getter ============================\n\n    public void setNodeService(NodeService nodeService) {\n        this.nodeService = nodeService;\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/remote/impl/StatsRemoteServiceImpl.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.remote.impl;\n\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.concurrent.ScheduledThreadPoolExecutor;\nimport java.util.concurrent.ThreadPoolExecutor;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.atomic.AtomicLong;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.util.Assert;\n\nimport com.alibaba.otter.manager.biz.remote.StatsRemoteService;\nimport com.alibaba.otter.manager.biz.statistics.delay.DelayStatService;\nimport com.alibaba.otter.manager.biz.statistics.table.TableStatService;\nimport com.alibaba.otter.manager.biz.statistics.throughput.ThroughputStatService;\nimport com.alibaba.otter.shared.common.model.statistics.delay.DelayCount;\nimport com.alibaba.otter.shared.common.model.statistics.delay.DelayStat;\nimport com.alibaba.otter.shared.common.model.statistics.table.TableStat;\nimport com.alibaba.otter.shared.common.model.statistics.throughput.ThroughputStat;\nimport com.alibaba.otter.shared.common.model.statistics.throughput.ThroughputType;\nimport com.alibaba.otter.shared.common.utils.thread.NamedThreadFactory;\nimport com.alibaba.otter.shared.communication.core.CommunicationRegistry;\nimport com.alibaba.otter.shared.communication.model.statistics.DelayCountEvent;\nimport com.alibaba.otter.shared.communication.model.statistics.StatisticsEventType;\nimport com.alibaba.otter.shared.communication.model.statistics.TableStatEvent;\nimport com.alibaba.otter.shared.communication.model.statistics.ThroughputStatEvent;\nimport com.google.common.base.Function;\nimport com.google.common.collect.OtterMigrateMap;\n\n/**\n * 统计模块远程接口\n * \n * @author jianghang 2011-10-21 下午03:04:40\n * @version 4.0.0\n */\npublic class StatsRemoteServiceImpl implements StatsRemoteService {\n\n    private static final Logger                            logger       = LoggerFactory.getLogger(StatsRemoteServiceImpl.class);\n    private static final int                               DEFAULT_POOL = 10;\n    private DelayStatService                               delayStatService;\n    private TableStatService                               tableStatService;\n    private ThroughputStatService                          throughputStatService;\n    private Long                                           statUnit     = 60 * 1000L;                                           //统计周期，默认60秒\n    private ScheduledThreadPoolExecutor                    scheduler;\n    private Map<Long, AvgStat>                             delayStats;\n    private Map<Long, Map<ThroughputType, ThroughputStat>> throughputStats;\n\n    public StatsRemoteServiceImpl(){\n        // 注册一下事件处理\n        CommunicationRegistry.regist(StatisticsEventType.delayCount, this);\n        CommunicationRegistry.regist(StatisticsEventType.tableStat, this);\n        CommunicationRegistry.regist(StatisticsEventType.throughputStat, this);\n\n        delayStats = OtterMigrateMap.makeComputingMap(new Function<Long, AvgStat>() {\n\n            public AvgStat apply(Long pipelineId) {\n                return new AvgStat();\n            }\n        });\n        throughputStats = OtterMigrateMap.makeComputingMap(new Function<Long, Map<ThroughputType, ThroughputStat>>() {\n\n            public Map<ThroughputType, ThroughputStat> apply(Long pipelineId) {\n                return new HashMap<ThroughputType, ThroughputStat>();\n            }\n        });\n\n        scheduler = new ScheduledThreadPoolExecutor(DEFAULT_POOL, new NamedThreadFactory(\"Otter-Statistics-Server\"),\n                                                    new ThreadPoolExecutor.CallerRunsPolicy());\n        if (statUnit > 0) {\n            scheduler.scheduleAtFixedRate(new Runnable() {\n\n                public void run() {\n                    try {\n                        flushDelayStat();\n                    } catch (Exception e) {\n                        logger.error(\"flush delay stat failed!\", e);\n                    }\n                }\n            }, statUnit, statUnit, TimeUnit.MILLISECONDS);\n\n            scheduler.scheduleAtFixedRate(new Runnable() {\n\n                public void run() {\n                    try {\n                        flushThroughputStat();\n                    } catch (Exception e) {\n                        logger.error(\"flush Throughput stat failed!\", e);\n                    }\n                }\n            }, statUnit, statUnit, TimeUnit.MILLISECONDS);\n        }\n    }\n\n    public void onDelayCount(DelayCountEvent event) {\n        Assert.notNull(event);\n        Assert.notNull(event.getCount());\n\n        // 更新delay queue的计数器\n        DelayCount count = event.getCount();\n        // 构造一次delay stat快照\n        DelayStat stat = new DelayStat();\n        stat.setPipelineId(count.getPipelineId());\n        stat.setDelayNumber(0L); // 不再记录堆积量\n        stat.setDelayTime(count.getTime() >= 0 ? count.getTime() : 0); // 只记录延迟时间，负数直接归为0\n\n        if (statUnit <= 0) {\n            delayStatService.createDelayStat(stat);\n        } else {\n            synchronized (delayStats) {\n                delayStats.get(count.getPipelineId()).merge(stat);\n            }\n        }\n    }\n\n    public void onThroughputStat(ThroughputStatEvent event) {\n        Assert.notNull(event);\n        Assert.notNull(event.getStats());\n        if (statUnit <= 0) {\n            for (ThroughputStat stat : event.getStats()) {\n                throughputStatService.createOrUpdateThroughput(stat);\n            }\n        } else {\n            synchronized (throughputStats) {\n                for (ThroughputStat stat : event.getStats()) {\n                    Map<ThroughputType, ThroughputStat> data = throughputStats.get(stat.getPipelineId());\n                    ThroughputStat old = data.get(stat.getType());\n                    if (old != null) {\n                        //执行合并\n                        old.setNumber(stat.getNumber() + old.getNumber());\n                        old.setSize(stat.getSize() + old.getSize());\n                        if (stat.getEndTime().after(old.getEndTime())) {\n                            old.setEndTime(stat.getEndTime());\n                        }\n\n                        if (stat.getStartTime().before(old.getStartTime())) {\n                            old.setStartTime(stat.getStartTime());\n                        }\n                    } else {\n                        data.put(stat.getType(), stat);\n                    }\n                }\n            }\n        }\n    }\n\n    public void onTableStat(TableStatEvent event) {\n        Assert.notNull(event);\n        Assert.notNull(event.getStats());\n        for (TableStat stat : event.getStats()) {\n            tableStatService.updateTableStat(stat);\n        }\n    }\n\n    private void flushDelayStat() {\n        synchronized (delayStats) {\n            // 需要做同步，避免delay数据丢失\n            for (Map.Entry<Long, AvgStat> stat : delayStats.entrySet()) {\n                if (stat.getValue().count.get() > 0) {\n                    DelayStat delay = new DelayStat();\n                    delay.setPipelineId(stat.getKey());\n                    delay.setDelayTime(stat.getValue().getAvg());\n                    delay.setDelayNumber(0L);\n                    delayStatService.createDelayStat(delay);\n                }\n            }\n            delayStats.clear();\n        }\n    }\n\n    private void flushThroughputStat() {\n        synchronized (throughputStats) {\n            Collection<Map<ThroughputType, ThroughputStat>> stats = throughputStats.values();\n            for (Map<ThroughputType, ThroughputStat> stat : stats) {\n                for (ThroughputStat data : stat.values()) {\n                    throughputStatService.createOrUpdateThroughput(data);\n                }\n            }\n            throughputStats.clear();\n        }\n    }\n\n    public static class AvgStat {\n\n        private AtomicLong number = new AtomicLong(0L);\n        private AtomicLong count  = new AtomicLong(0L);\n\n        public void merge(DelayStat stat) {\n            count.incrementAndGet();\n            number.addAndGet(stat.getDelayTime());\n        }\n\n        public Long getAvg() {\n            if (count.get() > 0) {\n                return number.get() / count.get();\n            } else {\n                return 0L;\n            }\n        }\n    }\n\n    // ===================== setter / getter =====================\n\n    public void setDelayStatService(DelayStatService delayStatService) {\n        this.delayStatService = delayStatService;\n    }\n\n    public void setTableStatService(TableStatService tableStatService) {\n        this.tableStatService = tableStatService;\n    }\n\n    public void setThroughputStatService(ThroughputStatService throughputStatService) {\n        this.throughputStatService = throughputStatService;\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/remote/interceptor/RemoteExceptionLoggerInterceptor.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.remote.interceptor;\n\nimport java.io.PrintWriter;\nimport java.io.StringWriter;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.aop.ThrowsAdvice;\nimport org.springframework.dao.DataAccessException;\n\nimport com.alibaba.otter.shared.communication.model.OtterRemoteException;\nimport com.alibaba.otter.manager.biz.common.exceptions.ManagerException;\n\n/**\n * 拦截所有的异常调用\n * \n * @author jianghang 2011-11-28 下午02:20:14\n * @version 4.0.0\n */\npublic class RemoteExceptionLoggerInterceptor implements ThrowsAdvice {\n\n    private static Logger log = LoggerFactory.getLogger(RemoteExceptionLoggerInterceptor.class);\n\n    public void afterThrowing(Throwable ex) throws Throwable {\n        if (log.isErrorEnabled()) {\n            log.error(\"log exception message:\", ex);\n        }\n        String msg = null;\n        String stack = null;\n        if (ex instanceof ManagerException) {\n            msg = ex.getMessage();\n            stack = getStackTrace(ex);\n        } else if (ex instanceof IllegalArgumentException) {\n            msg = ex.getMessage();\n            stack = getStackTrace(ex);\n        } else if (ex instanceof DataAccessException) {\n            msg = ex.getMessage();\n            stack = getStackTrace(ex);\n        } else if (ex instanceof RuntimeException) {\n            msg = ex.getMessage();\n            stack = getStackTrace(ex);\n        }\n\n        throw new OtterRemoteException(msg, stack);\n    }\n\n    /**\n     * 打印业务异常堆栈\n     */\n    private String getStackTrace(Throwable ex) {\n        StringWriter out = new StringWriter();\n        ex.printStackTrace(new PrintWriter(out));\n        return out.getBuffer().toString();\n    }\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/statistics/delay/DelayCounter.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.statistics.delay;\n\n/**\n * @author jianghang 2011-11-21 下午03:07:35\n * @version 4.0.0\n */\npublic interface DelayCounter {\n\n    public Long incAndGet(Long pipelineId, Long number);\n\n    public Long decAndGet(Long pipelineId, Long number);\n\n    public Long setAndGet(Long pipelineId, Long number);\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/statistics/delay/DelayStatService.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.statistics.delay;\n\nimport java.util.Date;\nimport java.util.List;\nimport java.util.Map;\n\nimport com.alibaba.otter.manager.biz.statistics.delay.param.DelayStatInfo;\nimport com.alibaba.otter.manager.biz.statistics.delay.param.TopDelayStat;\nimport com.alibaba.otter.shared.common.model.statistics.delay.DelayStat;\n\n/**\n * @author jianghang 2011-9-8 下午12:37:14\n */\npublic interface DelayStatService {\n\n    public void createDelayStat(DelayStat stat);\n\n    public DelayStat findRealtimeDelayStat(Long pipelineId);\n\n    public Map<Long, DelayStatInfo> listTimelineDelayStat(Long pipelineId, Date start, Date end);\n\n    public List<TopDelayStat> listTopDelayStat(String searchKey, int topN);\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/statistics/delay/dal/DelayStatDAO.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.statistics.delay.dal;\n\nimport java.util.Date;\nimport java.util.List;\n\nimport com.alibaba.otter.manager.biz.statistics.delay.dal.dataobject.DelayStatDO;\nimport com.alibaba.otter.manager.biz.statistics.delay.param.TopDelayStat;\n\n/**\n * @author simon\n */\npublic interface DelayStatDAO {\n\n    public void insertDelayStat(DelayStatDO delayStat);\n\n    public void deleteDelayStat(Long delayStatId);\n\n    public void modifyDelayStat(DelayStatDO delayStat);\n\n    public DelayStatDO findDelayStatById(Long delayStatId);\n\n    public DelayStatDO findRealtimeDelayStat(Long pipelineId);\n\n    public List<DelayStatDO> listDelayStatsByPipelineId(Long pipelineId);\n\n    public List<DelayStatDO> listTimelineDelayStatsByPipelineId(Long pipelineId, Date start, Date end);\n\n    public List<TopDelayStat> listTopDelayStatsByName(String name, int topN);\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/statistics/delay/dal/dataobject/DelayStatDO.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.statistics.delay.dal.dataobject;\n\nimport java.io.Serializable;\nimport java.util.Date;\n\nimport org.apache.commons.lang.builder.ToStringBuilder;\n\nimport com.alibaba.otter.shared.common.utils.OtterToStringStyle;\n\n/**\n * @author danping.yudp\n */\n\npublic class DelayStatDO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n    private Long              id;\n    private Long              delayTime;\n    private Long              delayNumber;\n    private Long              pipelineId;\n    private Date              gmtCreate;\n    private Date              gmtModified;\n\n    public Long getId() {\n        return id;\n    }\n\n    public void setId(Long id) {\n        this.id = id;\n    }\n\n    public Long getDelayTime() {\n        return delayTime;\n    }\n\n    public void setDelayTime(Long delayTime) {\n        this.delayTime = delayTime;\n    }\n\n    public Long getDelayNumber() {\n        return delayNumber;\n    }\n\n    public void setDelayNumber(Long delayNumber) {\n        this.delayNumber = delayNumber;\n    }\n\n    public Long getPipelineId() {\n        return pipelineId;\n    }\n\n    public void setPipelineId(Long pipelineId) {\n        this.pipelineId = pipelineId;\n    }\n\n    public Date getGmtCreate() {\n        return gmtCreate;\n    }\n\n    public void setGmtCreate(Date gmtCreate) {\n        this.gmtCreate = gmtCreate;\n    }\n\n    public Date getGmtModified() {\n        return gmtModified;\n    }\n\n    public void setGmtModified(Date gmtModified) {\n        this.gmtModified = gmtModified;\n    }\n\n    @Override\n    public String toString() {\n        return ToStringBuilder.reflectionToString(this, OtterToStringStyle.DEFAULT_STYLE);\n    }\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/statistics/delay/dal/ibatis/IbatisDelayStatDAO.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.statistics.delay.dal.ibatis;\n\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.springframework.orm.ibatis.support.SqlMapClientDaoSupport;\n\nimport com.alibaba.otter.shared.common.utils.Assert;\nimport com.alibaba.otter.manager.biz.statistics.delay.dal.DelayStatDAO;\nimport com.alibaba.otter.manager.biz.statistics.delay.dal.dataobject.DelayStatDO;\nimport com.alibaba.otter.manager.biz.statistics.delay.param.TimelineDelayCondition;\nimport com.alibaba.otter.manager.biz.statistics.delay.param.TopDelayStat;\n\n/**\n * @author danping.yudp\n */\npublic class IbatisDelayStatDAO extends SqlMapClientDaoSupport implements DelayStatDAO {\n\n    @Override\n    public void insertDelayStat(DelayStatDO delayStat) {\n        Assert.assertNotNull(delayStat);\n        getSqlMapClientTemplate().insert(\"insertDelayStat\", delayStat);\n    }\n\n    @Override\n    public void deleteDelayStat(Long delayStatId) {\n        Assert.assertNotNull(delayStatId);\n        getSqlMapClientTemplate().delete(\"deleteDelayStatById\", delayStatId);\n    }\n\n    @Override\n    public void modifyDelayStat(DelayStatDO delayStat) {\n        Assert.assertNotNull(delayStat);\n        getSqlMapClientTemplate().update(\"modifyDelayStat\", delayStat);\n    }\n\n    @Override\n    public DelayStatDO findDelayStatById(Long delayStatId) {\n        Assert.assertNotNull(delayStatId);\n        return (DelayStatDO) getSqlMapClientTemplate().queryForObject(\"findDelayStatById\", delayStatId);\n    }\n\n    @Override\n    public DelayStatDO findRealtimeDelayStat(Long pipelineId) {\n        Assert.assertNotNull(pipelineId);\n        return (DelayStatDO) getSqlMapClientTemplate().queryForObject(\"findRealtimeDelayStat\", pipelineId);\n    }\n\n    @Override\n    public List<DelayStatDO> listDelayStatsByPipelineId(Long pipelineId) {\n        Assert.assertNotNull(pipelineId);\n        return (List<DelayStatDO>) getSqlMapClientTemplate().queryForList(\"listDelayStatsByPipelineId\", pipelineId);\n    }\n\n    @Override\n    public List<DelayStatDO> listTimelineDelayStatsByPipelineId(Long pipelineId, Date start, Date end) {\n        TimelineDelayCondition tdc = new TimelineDelayCondition();\n        tdc.setPipelineId(pipelineId);\n        tdc.setStart(start);\n        tdc.setEnd(end);\n        Assert.assertNotNull(tdc);\n        return (List<DelayStatDO>) getSqlMapClientTemplate().queryForList(\"listTimelineDelayStatsByPipelineId\", tdc);\n    }\n\n    public List<TopDelayStat> listTopDelayStatsByName(String searchKey, int topN) {\n        Map<String, Object> param = new HashMap<String, Object>();\n        param.put(\"searchKey\", searchKey);\n        param.put(\"topN\", topN);\n        return (List<TopDelayStat>) getSqlMapClientTemplate().queryForList(\"listTopByName\", param);\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/statistics/delay/impl/DelayStatServiceImpl.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.statistics.delay.impl;\n\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport com.alibaba.otter.shared.common.utils.Assert;\nimport com.alibaba.otter.manager.biz.statistics.delay.DelayStatService;\nimport com.alibaba.otter.manager.biz.statistics.delay.dal.DelayStatDAO;\nimport com.alibaba.otter.manager.biz.statistics.delay.dal.dataobject.DelayStatDO;\nimport com.alibaba.otter.manager.biz.statistics.delay.param.DelayStatInfo;\nimport com.alibaba.otter.manager.biz.statistics.delay.param.TopDelayStat;\nimport com.alibaba.otter.shared.common.model.statistics.delay.DelayStat;\n\n/**\n * @author danping.yudp\n */\npublic class DelayStatServiceImpl implements DelayStatService {\n\n    private DelayStatDAO delayStatDao;\n\n    public DelayStatDAO getDelayStatDao() {\n        return delayStatDao;\n    }\n\n    public void setDelayStatDao(DelayStatDAO delayStatDao) {\n        this.delayStatDao = delayStatDao;\n    }\n\n    /**\n     * 在数据库中插入delayStat\n     */\n    public void createDelayStat(DelayStat stat) {\n        Assert.assertNotNull(stat);\n        delayStatDao.insertDelayStat(delayStatModelToDo(stat));\n    }\n\n    /**\n     * 通过pipeLineId得到一个以gmtCreate倒排序的第一条记录\n     */\n    public DelayStat findRealtimeDelayStat(Long pipelineId) {\n        Assert.assertNotNull(pipelineId);\n        DelayStatDO delayStatDO = delayStatDao.findRealtimeDelayStat(pipelineId);\n        DelayStat delayStat = new DelayStat();\n        if (delayStatDO != null) {\n            delayStat = delayStatDOToModel(delayStatDO);\n        }\n        return delayStat;\n    }\n\n    /**\n     * 列出pipeLineId下，start-end时间段下的delayStat\n     */\n    public Map<Long, DelayStatInfo> listTimelineDelayStat(Long pipelineId, Date start, Date end) {\n\n        Map<Long, DelayStatInfo> delayStatInfos = new LinkedHashMap<Long, DelayStatInfo>();\n        List<DelayStatDO> delayStatDOs = delayStatDao.listTimelineDelayStatsByPipelineId(pipelineId, start, end);\n        int size = delayStatDOs.size();\n        int k = size - 1;\n        for (Long i = start.getTime(); i <= end.getTime(); i += 60 * 1000) {\n            DelayStatInfo delayStatInfo = new DelayStatInfo();\n            List<DelayStat> delayStats = new ArrayList<DelayStat>();\n            // 取出每个时间点i以内的数据，k是一个游标，每次遍历时前面已经取过了的数据就不用再遍历了\n            for (int j = k; j >= 0; --j) {\n                if ((i - delayStatDOs.get(j).getGmtModified().getTime() <= 60 * 1000)\n                    && (i - delayStatDOs.get(j).getGmtModified().getTime() >= 0)) {\n                    delayStats.add(delayStatDOToModel(delayStatDOs.get(j)));\n                    k = j - 1;\n                }// 如果不满足if条件，则后面的数据也不用再遍历\n                else {\n                    break;\n                }\n            }\n            if (delayStats.size() > 0) {\n                delayStatInfo.setItems(delayStats);\n                delayStatInfos.put(i, delayStatInfo);\n            }\n\n        }\n        return delayStatInfos;\n    }\n\n    public List<TopDelayStat> listTopDelayStat(String searchKey, int topN) {\n        return delayStatDao.listTopDelayStatsByName(searchKey, topN);\n    }\n\n    /**\n     * 用于Model对象转化为DO对象\n     * \n     * @param delayStat\n     * @return DelayStatDO\n     */\n    private DelayStatDO delayStatModelToDo(DelayStat delayStat) {\n        DelayStatDO delayStatDO = new DelayStatDO();\n        delayStatDO.setId(delayStat.getId());\n        delayStatDO.setDelayTime(delayStat.getDelayTime());\n        delayStatDO.setDelayNumber(delayStat.getDelayNumber());\n        delayStatDO.setPipelineId(delayStat.getPipelineId());\n        delayStatDO.setGmtCreate(delayStat.getGmtCreate());\n        delayStatDO.setGmtModified(delayStat.getGmtModified());\n        return delayStatDO;\n\n    }\n\n    /**\n     * 用于DO对象转化为Model对象\n     * \n     * @param delayStatDO\n     * @return DelayStat\n     */\n    private DelayStat delayStatDOToModel(DelayStatDO delayStatDO) {\n        DelayStat delayStat = new DelayStat();\n        delayStat.setId(delayStatDO.getId());\n        delayStat.setDelayTime(delayStatDO.getDelayTime());\n        delayStat.setDelayNumber(delayStatDO.getDelayNumber());\n        delayStat.setPipelineId(delayStatDO.getPipelineId());\n        delayStat.setGmtCreate(delayStatDO.getGmtCreate());\n        delayStat.setGmtModified(delayStatDO.getGmtModified());\n        return delayStat;\n\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/statistics/delay/param/DelayCountParam.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.statistics.delay.param;\n\n/**\n * @author danping.yudp\n */\npublic class DelayCountParam {\n\n    private Long pipelineId;\n    private Long number;\n\n    public Long getPipelineId() {\n        return pipelineId;\n    }\n\n    public void setPipelineId(Long pipelineId) {\n        this.pipelineId = pipelineId;\n    }\n\n    public Long getNumber() {\n        return number;\n    }\n\n    public void setNumber(Long number) {\n        this.number = number;\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/statistics/delay/param/DelayStatInfo.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.statistics.delay.param;\n\nimport java.io.Serializable;\nimport java.util.List;\n\nimport com.alibaba.otter.shared.common.model.statistics.delay.DelayStat;\n\npublic class DelayStatInfo implements Serializable {\n\n    private static final long serialVersionUID = -6145961871313642767L;\n    private List<DelayStat>   items;\n\n    /**\n     * 一段时间内堆积量的平均值统计\n     */\n\n    public Double getAvgDelayNumber() {\n        Double avgDelayNumber = 0.0;\n        if (items.size() != 0) {\n            for (DelayStat item : items) {\n                avgDelayNumber += item.getDelayNumber();\n            }\n            avgDelayNumber = avgDelayNumber / items.size();\n        }\n        return avgDelayNumber;\n    }\n\n    /**\n     * 一段时间内延迟时间的平均值统计\n     */\n\n    public Double getAvgDelayTime() {\n        Double avgDelayTime = 0.0;\n        if (items.size() != 0) {\n            for (DelayStat item : items) {\n                avgDelayTime += item.getDelayTime();\n            }\n            avgDelayTime = avgDelayTime / items.size();\n        }\n        return avgDelayTime;\n    }\n\n    // ===================== setter / getter =========================\n\n    public List<DelayStat> getItems() {\n        return items;\n    }\n\n    public void setItems(List<DelayStat> items) {\n        this.items = items;\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/statistics/delay/param/TimelineDelayCondition.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.statistics.delay.param;\n\nimport java.util.Date;\n\n/**\n * @author danping.yudp\n */\npublic class TimelineDelayCondition {\n\n    private Long pipelineId;\n    private Date start;\n    private Date end;\n\n    public Long getPipelineId() {\n        return pipelineId;\n    }\n\n    public void setPipelineId(Long pipelineId) {\n        this.pipelineId = pipelineId;\n    }\n\n    public Date getStart() {\n        return start;\n    }\n\n    public void setStart(Date start) {\n        this.start = start;\n    }\n\n    public Date getEnd() {\n        return end;\n    }\n\n    public void setEnd(Date end) {\n        this.end = end;\n    }\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/statistics/delay/param/TopDelayStat.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.statistics.delay.param;\n\nimport java.util.Date;\n\n/**\n * @author jianghang 2013-3-12 下午06:12:38\n * @version 4.1.7\n */\npublic class TopDelayStat {\n\n    private String   channelName;\n    private String   pipelineName;\n    private Long     channelId;\n    private Long     pipelineId;\n    private Long     delayTime;\n    private Date     lastUpdate;                     // 延迟统计最后一次更新时间\n    private Long     statTime;                       // stat统计时间范围,分钟为单位\n    private DataStat dbStat   = new DataStat(0L, 0L);\n    private DataStat fileStat = new DataStat(0L, 0L);\n\n    public String getChannelName() {\n        return channelName;\n    }\n\n    public void setChannelName(String channelName) {\n        this.channelName = channelName;\n    }\n\n    public String getPipelineName() {\n        return pipelineName;\n    }\n\n    public void setPipelineName(String pipelineName) {\n        this.pipelineName = pipelineName;\n    }\n\n    public Long getChannelId() {\n        return channelId;\n    }\n\n    public void setChannelId(Long channelId) {\n        this.channelId = channelId;\n    }\n\n    public Long getPipelineId() {\n        return pipelineId;\n    }\n\n    public void setPipelineId(Long pipelineId) {\n        this.pipelineId = pipelineId;\n    }\n\n    public Long getDelayTime() {\n        return delayTime;\n    }\n\n    public void setDelayTime(Long delayTime) {\n        this.delayTime = delayTime;\n    }\n\n    public Date getLastUpdate() {\n        return lastUpdate;\n    }\n\n    public void setLastUpdate(Date lastUpdate) {\n        this.lastUpdate = lastUpdate;\n    }\n\n    public Long getStatTime() {\n        return statTime;\n    }\n\n    public void setStatTime(Long statTime) {\n        this.statTime = statTime;\n    }\n\n    public DataStat getDbStat() {\n        return dbStat;\n    }\n\n    public void setDbStat(DataStat dbStat) {\n        this.dbStat = dbStat;\n    }\n\n    public DataStat getFileStat() {\n        return fileStat;\n    }\n\n    public void setFileStat(DataStat fileStat) {\n        this.fileStat = fileStat;\n    }\n\n    /**\n     * 获取延迟统计最后更新时间距当前时间的差值\n     */\n    public Long getLastUpdateDelay() {\n        return (new Date().getTime() - lastUpdate.getTime()) / 1000;\n    }\n\n    public static class DataStat {\n\n        public DataStat(Long number, Long size){\n            this.number = number;\n            this.size = size;\n        }\n\n        private Long number;\n        private Long size;\n\n        public Long getNumber() {\n            return number;\n        }\n\n        public void setNumber(Long number) {\n            this.number = number;\n        }\n\n        public Long getSize() {\n            return size;\n        }\n\n        public void setSize(Long size) {\n            this.size = size;\n        }\n\n    }\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/statistics/stage/ProcessStatService.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.statistics.stage;\n\nimport java.util.Date;\nimport java.util.List;\n\nimport com.alibaba.otter.shared.common.model.statistics.stage.ProcessStat;\n\n/**\n * @author jianghang 2011-9-8 下午12:48:21\n */\npublic interface ProcessStatService {\n\n    public List<ProcessStat> listRealtimeProcessStat(Long pipelineId);\n\n    public List<ProcessStat> listRealtimeProcessStat(Long channelId, Long pipelineId);\n\n    public List<ProcessStat> listTimelineProcessStat(Long pipelineId, Date start, Date end);\n\n    public void createProcessStat(ProcessStat stat);\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/statistics/stage/dal/ProcessDAO.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.statistics.stage.dal;\n\nimport java.util.List;\n\nimport com.alibaba.otter.manager.biz.statistics.stage.dal.dataobject.ProcessStatDO;\n\n/**\n * TODO Comment of ProcessDAO\n * \n * @author danping.yudp\n */\npublic interface ProcessDAO {\n\n    public void insertProcessStat(ProcessStatDO processStat);\n\n    public void deleteProcessStat(Long processId);\n\n    public void modifyProcessStat(ProcessStatDO processStat);\n\n    public ProcessStatDO findByProcessId(Long processId);\n\n    public List<ProcessStatDO> listAllProcessStat();\n\n    public List<ProcessStatDO> listProcessStatsByPipelineId(Long pipelineId);\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/statistics/stage/dal/dataobject/ProcessStatDO.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.statistics.stage.dal.dataobject;\n\nimport java.io.Serializable;\nimport java.util.List;\n\nimport org.apache.commons.lang.builder.ToStringBuilder;\n\nimport com.alibaba.otter.shared.common.model.statistics.stage.StageStat;\nimport com.alibaba.otter.shared.common.utils.OtterToStringStyle;\n\n/**\n * TODO Comment of TableStat\n * \n * @author danping.yudp\n */\n\npublic class ProcessStatDO implements Serializable {\n\n    private static final long serialVersionUID = -5625269232233751756L;\n    private Long              pipelineId;\n    private Long              processId;\n    private List<StageStat>   stageStats;                              // 当前process的阶段列表\n\n    public Long getPipelineId() {\n        return pipelineId;\n    }\n\n    public void setPipelineId(Long pipelineId) {\n        this.pipelineId = pipelineId;\n    }\n\n    public Long getProcessId() {\n        return processId;\n    }\n\n    public void setProcessId(Long processId) {\n        this.processId = processId;\n    }\n\n    public List<StageStat> getStageStats() {\n        return stageStats;\n    }\n\n    public void setStageStats(List<StageStat> stageStats) {\n        this.stageStats = stageStats;\n    }\n\n    @Override\n    public String toString() {\n        return ToStringBuilder.reflectionToString(this, OtterToStringStyle.DEFAULT_STYLE);\n    }\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/statistics/stage/dal/ibatis/IbatisProcessDAO.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.statistics.stage.dal.ibatis;\n\nimport java.util.List;\n\nimport org.springframework.orm.ibatis.support.SqlMapClientDaoSupport;\n\nimport com.alibaba.otter.manager.biz.statistics.stage.dal.ProcessDAO;\nimport com.alibaba.otter.manager.biz.statistics.stage.dal.dataobject.ProcessStatDO;\n\n/**\n * TODO Comment of IbatisProcessStatDAO\n * \n * @author danping.yudp\n */\npublic class IbatisProcessDAO extends SqlMapClientDaoSupport implements ProcessDAO {\n\n    @Override\n    public void insertProcessStat(ProcessStatDO processStat) {\n\n        getSqlMapClientTemplate().insert(\"\", processStat);\n    }\n\n    @Override\n    public void deleteProcessStat(Long processId) {\n        getSqlMapClientTemplate().delete(\"\", processId);\n    }\n\n    @Override\n    public void modifyProcessStat(ProcessStatDO processStat) {\n        getSqlMapClientTemplate().update(\"\", processStat);\n    }\n\n    @Override\n    public ProcessStatDO findByProcessId(Long processId) {\n\n        return (ProcessStatDO) getSqlMapClientTemplate().queryForObject(\"\", processId);\n    }\n\n    @Override\n    public List<ProcessStatDO> listAllProcessStat() {\n\n        return (List<ProcessStatDO>) getSqlMapClientTemplate().queryForList(\"\");\n    }\n\n    @Override\n    public List<ProcessStatDO> listProcessStatsByPipelineId(Long pipelineId) {\n\n        return (List<ProcessStatDO>) getSqlMapClientTemplate().queryForList(\"\", pipelineId);\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/statistics/stage/impl/ProcessStatServiceImpl.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.statistics.stage.impl;\n\nimport java.util.Date;\nimport java.util.List;\n\nimport com.alibaba.otter.manager.biz.config.channel.ChannelService;\nimport com.alibaba.otter.manager.biz.statistics.stage.ProcessStatService;\nimport com.alibaba.otter.shared.arbitrate.ArbitrateViewService;\nimport com.alibaba.otter.shared.common.model.config.channel.Channel;\nimport com.alibaba.otter.shared.common.model.statistics.stage.ProcessStat;\n\n/**\n * @author jianghang 2012-1-10 下午02:10:31\n * @version 4.0.0\n */\npublic class ProcessStatServiceImpl implements ProcessStatService {\n\n    private ArbitrateViewService arbitrateViewService;\n    private ChannelService       channelService;\n\n    public void createProcessStat(ProcessStat stat) {\n        throw new UnsupportedOperationException(\"unsupport method!\");\n    }\n\n    @Override\n    public List<ProcessStat> listRealtimeProcessStat(Long pipelineId) {\n        Channel channel = channelService.findByPipelineId(pipelineId);\n        return listRealtimeProcessStat(channel.getId(), pipelineId);\n    }\n\n    @Override\n    public List<ProcessStat> listRealtimeProcessStat(Long channelId, Long pipelineId) {\n        return arbitrateViewService.listProcesses(channelId, pipelineId);\n    }\n\n    @Override\n    public List<ProcessStat> listTimelineProcessStat(Long pipelineId, Date start, Date end) {\n        throw new UnsupportedOperationException(\"unsupport method!\");\n    }\n\n    // ======================= setter / getter =====================\n\n    public void setArbitrateViewService(ArbitrateViewService arbitrateViewService) {\n        this.arbitrateViewService = arbitrateViewService;\n    }\n\n    public void setChannelService(ChannelService channelService) {\n        this.channelService = channelService;\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/statistics/table/TableStatService.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.statistics.table;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport com.alibaba.otter.manager.biz.statistics.table.param.BehaviorHistoryInfo;\nimport com.alibaba.otter.manager.biz.statistics.table.param.TimelineBehaviorHistoryCondition;\nimport com.alibaba.otter.shared.common.model.statistics.table.TableStat;\n\n/**\n * @author jianghang 2011-9-8 下午06:27:29\n */\npublic interface TableStatService {\n\n    /**\n     * 增量更新对应的Table统计状态\n     */\n    public void updateTableStat(TableStat stat);\n\n    /**\n     * 查询对应同步任务下的统计信息\n     */\n    public List<TableStat> listTableStat(Long pipelineId);\n\n    /**\n     * 更新对应的history信息，做报表统计用\n     */\n    public void insertBehaviorHistory(TableStat stat);\n\n    /**\n     * 查询对应的报表数据\n     */\n    public Map<Long, BehaviorHistoryInfo> listTimelineBehaviorHistory(TimelineBehaviorHistoryCondition condition);\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/statistics/table/dal/TableHistoryStatDAO.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.statistics.table.dal;\n\nimport java.util.List;\n\nimport com.alibaba.otter.manager.biz.statistics.table.dal.dataobject.TableHistoryStatDO;\nimport com.alibaba.otter.manager.biz.statistics.table.param.BehaviorHistoryCondition;\n\npublic interface TableHistoryStatDAO {\n\n    /**\n     * 插入记录\n     */\n    public void insertTableHistoryStat(TableHistoryStatDO tableHistoryStatDO);\n\n    /**\n     * 根据pairId列出 start-end时间段下的tableStat\n     */\n    public List<TableHistoryStatDO> listTimelineTableStat(BehaviorHistoryCondition condition);\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/statistics/table/dal/TableStatDAO.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.statistics.table.dal;\n\nimport java.util.List;\n\nimport com.alibaba.otter.manager.biz.statistics.table.dal.dataobject.TableStatDO;\n\n/**\n * @author simon\n */\npublic interface TableStatDAO {\n\n    public void insertTableStat(TableStatDO tableStat);\n\n    public void deleteTableStat(Long tableStatId);\n\n    public int modifyTableStat(TableStatDO tableStat);\n\n    public TableStatDO findTableStatById(Long tableStatId);\n\n    public TableStatDO findTableStatByPipelineIdAndPairId(Long pipelineId, Long dataMediaPairId);\n\n    public List<TableStatDO> listTableStatsByPipelineId(Long pipelineId);\n\n    public List<TableStatDO> listTableStatsByPairId(Long dataMediaPairId);\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/statistics/table/dal/dataobject/TableHistoryStatDO.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.statistics.table.dal.dataobject;\n\nimport java.io.Serializable;\nimport java.util.Date;\n\nimport org.apache.commons.lang.builder.ToStringBuilder;\n\nimport com.alibaba.otter.shared.common.utils.OtterToStringStyle;\n\n/**\n * @author danping.yudp\n */\n\npublic class TableHistoryStatDO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n    private Long              id;\n    private Date              startTime;\n    private Date              endTime;\n    private Long              fileSize;\n    private Long              fileCount;\n    private Long              insertCount;\n    private Long              updateCount;\n    private Long              deleteCount;\n    private Long              dataMediaPairId;\n    private Long              pipelineId;\n    private Date              gmtCreate;\n    private Date              gmtModified;\n\n    public Long getId() {\n        return id;\n    }\n\n    public void setId(Long id) {\n        this.id = id;\n    }\n\n    public Date getStartTime() {\n        return startTime;\n    }\n\n    public void setStartTime(Date startTime) {\n        this.startTime = startTime;\n    }\n\n    public Date getEndTime() {\n        return endTime;\n    }\n\n    public void setEndTime(Date endTime) {\n        this.endTime = endTime;\n    }\n\n    public Long getFileSize() {\n        return fileSize;\n    }\n\n    public void setFileSize(Long fileSize) {\n        this.fileSize = fileSize;\n    }\n\n    public Long getFileCount() {\n        return fileCount;\n    }\n\n    public void setFileCount(Long fileCount) {\n        this.fileCount = fileCount;\n    }\n\n    public Long getInsertCount() {\n        return insertCount;\n    }\n\n    public void setInsertCount(Long insertCount) {\n        this.insertCount = insertCount;\n    }\n\n    public Long getUpdateCount() {\n        return updateCount;\n    }\n\n    public void setUpdateCount(Long updateCount) {\n        this.updateCount = updateCount;\n    }\n\n    public Long getDeleteCount() {\n        return deleteCount;\n    }\n\n    public void setDeleteCount(Long deleteCount) {\n        this.deleteCount = deleteCount;\n    }\n\n    public Long getDataMediaPairId() {\n        return dataMediaPairId;\n    }\n\n    public void setDataMediaPairId(Long dataMediaPairId) {\n        this.dataMediaPairId = dataMediaPairId;\n    }\n\n    public Long getPipelineId() {\n        return pipelineId;\n    }\n\n    public void setPipelineId(Long pipelineId) {\n        this.pipelineId = pipelineId;\n    }\n\n    public Date getGmtCreate() {\n        return gmtCreate;\n    }\n\n    public void setGmtCreate(Date gmtCreate) {\n        this.gmtCreate = gmtCreate;\n    }\n\n    public Date getGmtModified() {\n        return gmtModified;\n    }\n\n    public void setGmtModified(Date gmtModified) {\n        this.gmtModified = gmtModified;\n    }\n\n    @Override\n    public String toString() {\n        return ToStringBuilder.reflectionToString(this, OtterToStringStyle.DEFAULT_STYLE);\n    }\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/statistics/table/dal/dataobject/TableStatDO.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.statistics.table.dal.dataobject;\n\nimport java.io.Serializable;\nimport java.util.Date;\n\nimport org.apache.commons.lang.builder.ToStringBuilder;\n\nimport com.alibaba.otter.shared.common.utils.OtterToStringStyle;\n\n/**\n * @author danping.yudp\n */\n\npublic class TableStatDO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n    private Long              id;\n    private Long              fileSize;\n    private Long              fileCount;\n    private Long              insertCount;\n    private Long              updateCount;\n    private Long              deleteCount;\n    private Long              dataMediaPairId;\n    private Long              pipelineId;\n    private Date              gmtCreate;\n    private Date              gmtModified;\n\n    public Long getId() {\n        return id;\n    }\n\n    public void setId(Long id) {\n        this.id = id;\n    }\n\n    public Long getFileSize() {\n        return fileSize;\n    }\n\n    public void setFileSize(Long fileSize) {\n        this.fileSize = fileSize;\n    }\n\n    public Long getFileCount() {\n        return fileCount;\n    }\n\n    public void setFileCount(Long fileCount) {\n        this.fileCount = fileCount;\n    }\n\n    public Long getInsertCount() {\n        return insertCount;\n    }\n\n    public void setInsertCount(Long insertCount) {\n        this.insertCount = insertCount;\n    }\n\n    public Long getUpdateCount() {\n        return updateCount;\n    }\n\n    public void setUpdateCount(Long updateCount) {\n        this.updateCount = updateCount;\n    }\n\n    public Long getDeleteCount() {\n        return deleteCount;\n    }\n\n    public void setDeleteCount(Long deleteCount) {\n        this.deleteCount = deleteCount;\n    }\n\n    public Long getDataMediaPairId() {\n        return dataMediaPairId;\n    }\n\n    public void setDataMediaPairId(Long dataMediaPairId) {\n        this.dataMediaPairId = dataMediaPairId;\n    }\n\n    public Long getPipelineId() {\n        return pipelineId;\n    }\n\n    public void setPipelineId(Long pipelineId) {\n        this.pipelineId = pipelineId;\n    }\n\n    public Date getGmtCreate() {\n        return gmtCreate;\n    }\n\n    public void setGmtCreate(Date gmtCreate) {\n        this.gmtCreate = gmtCreate;\n    }\n\n    public Date getGmtModified() {\n        return gmtModified;\n    }\n\n    public void setGmtModified(Date gmtModified) {\n        this.gmtModified = gmtModified;\n    }\n\n    @Override\n    public String toString() {\n        return ToStringBuilder.reflectionToString(this, OtterToStringStyle.DEFAULT_STYLE);\n    }\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/statistics/table/dal/ibatis/IbatisTableHistoryStatDAO.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.statistics.table.dal.ibatis;\n\nimport java.util.List;\n\nimport org.springframework.orm.ibatis.support.SqlMapClientDaoSupport;\n\nimport com.alibaba.otter.manager.biz.statistics.table.dal.TableHistoryStatDAO;\nimport com.alibaba.otter.manager.biz.statistics.table.dal.dataobject.TableHistoryStatDO;\nimport com.alibaba.otter.manager.biz.statistics.table.param.BehaviorHistoryCondition;\n\n/**\n * @author sarah.lij 2012-7-17 下午06:35:52\n */\npublic class IbatisTableHistoryStatDAO extends SqlMapClientDaoSupport implements TableHistoryStatDAO {\n\n    @Override\n    public void insertTableHistoryStat(TableHistoryStatDO tableHistoryStatDO) {\n        getSqlMapClientTemplate().insert(\"insertTableHistoryStat\", tableHistoryStatDO);\n    }\n\n    @Override\n    public List<TableHistoryStatDO> listTimelineTableStat(BehaviorHistoryCondition condition) {\n        return (List<TableHistoryStatDO>) getSqlMapClientTemplate().queryForList(\"listTimelineTableStat\", condition);\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/statistics/table/dal/ibatis/IbatisTableStatDAO.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.statistics.table.dal.ibatis;\n\nimport java.util.List;\n\nimport org.springframework.orm.ibatis.support.SqlMapClientDaoSupport;\n\nimport com.alibaba.otter.manager.biz.statistics.table.dal.TableStatDAO;\nimport com.alibaba.otter.manager.biz.statistics.table.dal.dataobject.TableStatDO;\nimport com.alibaba.otter.manager.biz.statistics.table.param.BehaviorHistoryCondition;\n\n/**\n * @author simon\n */\npublic class IbatisTableStatDAO extends SqlMapClientDaoSupport implements TableStatDAO {\n\n    @Override\n    public void insertTableStat(TableStatDO tableStat) {\n\n        getSqlMapClientTemplate().insert(\"insertTableStat\", tableStat);\n    }\n\n    @Override\n    public void deleteTableStat(Long tableStatId) {\n        getSqlMapClientTemplate().delete(\"deleteTableStatById\", tableStatId);\n    }\n\n    @Override\n    public int modifyTableStat(TableStatDO tableStat) {\n        return (getSqlMapClientTemplate().update(\"modifyTableStat\", tableStat));\n    }\n\n    @Override\n    public TableStatDO findTableStatById(Long tableStatId) {\n\n        return (TableStatDO) getSqlMapClientTemplate().queryForObject(\"findTableStatById\", tableStatId);\n    }\n\n    @Override\n    public TableStatDO findTableStatByPipelineIdAndPairId(Long pipelineId, Long dataMediaPairId) {\n        TableStatDO tableStat = new TableStatDO();\n        tableStat.setPipelineId(pipelineId);\n        tableStat.setDataMediaPairId(dataMediaPairId);\n        return (TableStatDO) getSqlMapClientTemplate().queryForObject(\"findTableStatByPipelineIdAndDataMediaPairId\",\n                                                                      tableStat);\n    }\n\n    @Override\n    public List<TableStatDO> listTableStatsByPipelineId(Long pipelineId) {\n\n        return (List<TableStatDO>) getSqlMapClientTemplate().queryForList(\"listTableStatsByPipelineId\", pipelineId);\n    }\n\n    @Override\n    public List<TableStatDO> listTableStatsByPairId(Long dataMediaPairId) {\n\n        return (List<TableStatDO>) getSqlMapClientTemplate().queryForList(\"listTableStatsByDataMediaPairId\",\n                                                                          dataMediaPairId);\n    }\n\n    public List<TableStatDO> listTimelineTableStat(BehaviorHistoryCondition condition) {\n        return (List<TableStatDO>) getSqlMapClientTemplate().queryForList(\"listTimelineTableStat\", condition);\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/statistics/table/impl/TableStatServiceImpl.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.statistics.table.impl;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.ScheduledThreadPoolExecutor;\nimport java.util.concurrent.ThreadPoolExecutor;\nimport java.util.concurrent.TimeUnit;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.InitializingBean;\n\nimport com.alibaba.otter.manager.biz.statistics.table.TableStatService;\nimport com.alibaba.otter.manager.biz.statistics.table.dal.TableHistoryStatDAO;\nimport com.alibaba.otter.manager.biz.statistics.table.dal.TableStatDAO;\nimport com.alibaba.otter.manager.biz.statistics.table.dal.dataobject.TableHistoryStatDO;\nimport com.alibaba.otter.manager.biz.statistics.table.dal.dataobject.TableStatDO;\nimport com.alibaba.otter.manager.biz.statistics.table.param.BehaviorHistoryInfo;\nimport com.alibaba.otter.manager.biz.statistics.table.param.TimelineBehaviorHistoryCondition;\nimport com.alibaba.otter.shared.common.model.statistics.table.TableStat;\nimport com.alibaba.otter.shared.common.utils.Assert;\nimport com.alibaba.otter.shared.common.utils.thread.NamedThreadFactory;\n\n/**\n * @author danping.yudp\n */\npublic class TableStatServiceImpl implements TableStatService, InitializingBean {\n\n    private static final Logger         logger     = LoggerFactory.getLogger(TableStatServiceImpl.class);\n    private TableStatDAO                tableStatDao;\n    private TableHistoryStatDAO         tableHistoryStatDao;\n    private Map<Long, TableStat>        tableStats = new HashMap<Long, TableStat>();\n    private Long                        statUnit   = 60 * 1000L;                                         //统计周期，默认60秒\n    private ScheduledThreadPoolExecutor scheduler;\n\n    /**\n     * 先通过pipeLineId和DataMediaPairId在数据库里查找对应的tableStat，如果有，则增量更新对应的Table统计状态， 如果没有则将该数据插入\n     */\n    public void updateTableStat(TableStat stat) {\n        Assert.assertNotNull(stat);\n        int affect = tableStatDao.modifyTableStat(tableStatModelToDo(stat));\n        if (affect == 0) {\n            tableStatDao.insertTableStat(tableStatModelToDo(stat));\n        }\n\n        if (stat.getStartTime() != null && stat.getEndTime() != null) {\n            if (statUnit <= 0) {\n                insertBehaviorHistory(stat);\n            } else {\n                synchronized (tableStats) {\n                    // 插入历史数据表\n                    TableStat old = tableStats.get(stat.getDataMediaPairId());\n                    if (old != null) {\n                        //合并数据\n                        old.setInsertCount(stat.getInsertCount() + old.getInsertCount());\n                        old.setUpdateCount(stat.getUpdateCount() + old.getUpdateCount());\n                        old.setDeleteCount(stat.getDeleteCount() + old.getDeleteCount());\n                        old.setFileCount(stat.getFileCount() + old.getFileCount());\n                        old.setFileSize(stat.getFileSize() + old.getFileSize());\n                        if (stat.getEndTime().after(old.getEndTime())) {\n                            old.setEndTime(stat.getEndTime());\n                        }\n                        if (stat.getStartTime().before(old.getStartTime())) {\n                            old.setStartTime(stat.getStartTime());\n                        }\n                    } else {\n                        tableStats.put(stat.getDataMediaPairId(), stat);\n                    }\n                }\n            }\n        }\n    }\n\n    /**\n     * 列出对应同步任务下的统计信息\n     */\n    public List<TableStat> listTableStat(Long pipelineId) {\n        Assert.assertNotNull(pipelineId);\n        List<TableStatDO> tableStatDOs = tableStatDao.listTableStatsByPipelineId(pipelineId);\n        List<TableStat> tableStats = new ArrayList<TableStat>();\n        for (TableStatDO tableStatDO : tableStatDOs) {\n            tableStats.add(tableStatDOToModel(tableStatDO));\n        }\n        return tableStats;\n    }\n\n    public void insertBehaviorHistory(TableStat stat) {\n        tableHistoryStatDao.insertTableHistoryStat(tableHistoryStatModelToDo(stat));\n    }\n\n    /**\n     * 列出pairId下，start-end时间段下的tableStat, 首先从数据库中取出这一段时间所有数据，该数据都是根据end_time倒排序的, 每隔1分钟将这些数据分组\n     */\n    public Map<Long, BehaviorHistoryInfo> listTimelineBehaviorHistory(TimelineBehaviorHistoryCondition condition) {\n\n        Assert.assertNotNull(condition);\n        Map<Long, BehaviorHistoryInfo> behaviorHistoryInfos = new LinkedHashMap<Long, BehaviorHistoryInfo>();\n        List<TableHistoryStatDO> tableHistoryStatDOs = tableHistoryStatDao.listTimelineTableStat(condition);\n        int size = tableHistoryStatDOs.size();\n        int k = size - 1;\n        for (Long i = condition.getStart().getTime(); i <= condition.getEnd().getTime(); i += 60 * 1000) {\n            BehaviorHistoryInfo behaviorHistoryInfo = new BehaviorHistoryInfo();\n            List<TableStat> tableStat = new ArrayList<TableStat>();\n            // 取出每个时间点i以内的数据，k是一个游标，每次遍历时前面已经取过了的数据就不用再遍历了\n            for (int j = k; j >= 0; --j) {\n                if ((i - tableHistoryStatDOs.get(j).getEndTime().getTime() <= 60 * 1000)\n                    && (i - tableHistoryStatDOs.get(j).getEndTime().getTime() >= 0)) {\n                    tableStat.add(tableHistoryStatDOToModel(tableHistoryStatDOs.get(j)));\n                    k = j - 1;\n                }// 如果不满足if条件，则后面的数据也不用再遍历\n                else {\n                    break;\n                }\n            }\n            if (tableStat.size() > 0) {\n                behaviorHistoryInfo.setItems(tableStat);\n                behaviorHistoryInfos.put(i, behaviorHistoryInfo);\n            }\n\n        }\n        return behaviorHistoryInfos;\n    }\n\n    private void flushBehaviorHistory() {\n        synchronized (tableStats) {\n            // 需要做同步，避免delay数据丢失\n            Collection<TableStat> stats = tableStats.values();\n            for (TableStat stat : stats) {\n                insertBehaviorHistory(stat);\n            }\n            tableStats.clear();\n        }\n    }\n\n    public void afterPropertiesSet() throws Exception {\n        scheduler = new ScheduledThreadPoolExecutor(1, new NamedThreadFactory(\"Otter-Statistics-Table\"),\n                                                    new ThreadPoolExecutor.CallerRunsPolicy());\n        if (statUnit > 0) {\n            scheduler.scheduleAtFixedRate(new Runnable() {\n\n                public void run() {\n                    try {\n                        flushBehaviorHistory();\n                    } catch (Exception e) {\n                        logger.error(\"flush delay stat failed!\", e);\n                    }\n                }\n            }, statUnit, statUnit, TimeUnit.MILLISECONDS);\n        }\n    }\n\n    /**\n     * 用于Model对象转化为DO对象\n     * \n     * @param tableStat\n     * @return TableStatDO\n     */\n    private TableStatDO tableStatModelToDo(TableStat tableStat) {\n        TableStatDO tableStatDO = new TableStatDO();\n        tableStatDO.setId(tableStat.getId());\n        tableStatDO.setPipelineId(tableStat.getPipelineId());\n        tableStatDO.setDataMediaPairId(tableStat.getDataMediaPairId());\n        tableStatDO.setFileSize(tableStat.getFileSize());\n        tableStatDO.setFileCount(tableStat.getFileCount());\n        tableStatDO.setDeleteCount(tableStat.getDeleteCount());\n        tableStatDO.setInsertCount(tableStat.getInsertCount());\n        tableStatDO.setUpdateCount(tableStat.getUpdateCount());\n        tableStatDO.setGmtCreate(tableStat.getGmtCreate());\n        tableStatDO.setGmtModified(tableStat.getGmtModified());\n        return tableStatDO;\n\n    }\n\n    /**\n     * 用于DO对象转化为Model对象\n     * \n     * @param tableStatDO\n     * @return TableStat\n     */\n    private TableStat tableStatDOToModel(TableStatDO tableStatDO) {\n        TableStat tableStat = new TableStat();\n        tableStat.setId(tableStatDO.getId());\n        tableStat.setPipelineId(tableStatDO.getPipelineId());\n        tableStat.setDataMediaPairId(tableStatDO.getDataMediaPairId());\n        tableStat.setFileSize(tableStatDO.getFileSize());\n        tableStat.setFileCount(tableStatDO.getFileCount());\n        tableStat.setDeleteCount(tableStatDO.getDeleteCount());\n        tableStat.setInsertCount(tableStatDO.getInsertCount());\n        tableStat.setUpdateCount(tableStatDO.getUpdateCount());\n        tableStat.setGmtCreate(tableStatDO.getGmtCreate());\n        tableStat.setGmtModified(tableStatDO.getGmtModified());\n        return tableStat;\n\n    }\n\n    /**\n     * 用于Model对象转化为DO对象\n     * \n     * @param tableStat\n     * @return TableHistoryStatDO\n     */\n    private TableHistoryStatDO tableHistoryStatModelToDo(TableStat tableStat) {\n        TableHistoryStatDO tableHistoryStatDO = new TableHistoryStatDO();\n        tableHistoryStatDO.setId(tableStat.getId());\n        tableHistoryStatDO.setPipelineId(tableStat.getPipelineId());\n        tableHistoryStatDO.setDataMediaPairId(tableStat.getDataMediaPairId());\n        tableHistoryStatDO.setStartTime(tableStat.getStartTime());\n        tableHistoryStatDO.setEndTime(tableStat.getEndTime());\n        tableHistoryStatDO.setFileSize(tableStat.getFileSize());\n        tableHistoryStatDO.setFileCount(tableStat.getFileCount());\n        tableHistoryStatDO.setDeleteCount(tableStat.getDeleteCount());\n        tableHistoryStatDO.setInsertCount(tableStat.getInsertCount());\n        tableHistoryStatDO.setUpdateCount(tableStat.getUpdateCount());\n        tableHistoryStatDO.setGmtCreate(tableStat.getGmtCreate());\n        tableHistoryStatDO.setGmtModified(tableStat.getGmtModified());\n        return tableHistoryStatDO;\n\n    }\n\n    /**\n     * 用于DO对象转化为Model对象\n     * \n     * @param TableHistoryStatDO\n     * @return TableStat\n     */\n    private TableStat tableHistoryStatDOToModel(TableHistoryStatDO tableHistoryStatDO) {\n        TableStat tableStat = new TableStat();\n        tableStat.setId(tableHistoryStatDO.getId());\n        tableStat.setPipelineId(tableHistoryStatDO.getPipelineId());\n        tableStat.setDataMediaPairId(tableHistoryStatDO.getDataMediaPairId());\n        tableStat.setStartTime(tableHistoryStatDO.getStartTime());\n        tableStat.setEndTime(tableHistoryStatDO.getEndTime());\n        tableStat.setFileSize(tableHistoryStatDO.getFileSize());\n        tableStat.setFileCount(tableHistoryStatDO.getFileCount());\n        tableStat.setDeleteCount(tableHistoryStatDO.getDeleteCount());\n        tableStat.setInsertCount(tableHistoryStatDO.getInsertCount());\n        tableStat.setUpdateCount(tableHistoryStatDO.getUpdateCount());\n        tableStat.setGmtCreate(tableHistoryStatDO.getGmtCreate());\n        tableStat.setGmtModified(tableHistoryStatDO.getGmtModified());\n        return tableStat;\n\n    }\n\n    public void setTableStatDao(TableStatDAO tableStatDao) {\n        this.tableStatDao = tableStatDao;\n    }\n\n    public void setTableHistoryStatDao(TableHistoryStatDAO tableHistoryStatDao) {\n        this.tableHistoryStatDao = tableHistoryStatDao;\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/statistics/table/param/BehaviorHistoryCondition.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.statistics.table.param;\n\nimport org.apache.commons.lang.builder.ToStringBuilder;\n\nimport com.alibaba.otter.shared.common.utils.OtterToStringStyle;\n\n/**\n * @author jianghang 2011-9-8 下午01:21:09\n */\npublic class BehaviorHistoryCondition {\n\n    private Long    pairId;\n    private boolean detail;\n\n    public Long getPairId() {\n        return pairId;\n    }\n\n    public void setPairId(Long pairId) {\n        this.pairId = pairId;\n    }\n\n    public boolean isDetail() {\n        return detail;\n    }\n\n    public void setDetail(boolean detail) {\n        this.detail = detail;\n    }\n\n    @Override\n    public String toString() {\n        return ToStringBuilder.reflectionToString(this, OtterToStringStyle.DEFAULT_STYLE);\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/statistics/table/param/BehaviorHistoryInfo.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.statistics.table.param;\n\nimport java.util.List;\n\nimport com.alibaba.otter.shared.common.model.statistics.table.TableStat;\n\n/**\n * @author sarah.lij 2012-7-13 下午03:29:08\n */\npublic class BehaviorHistoryInfo {\n\n    private List<TableStat> items;\n\n    /**\n     * 对应insertCount的数据统计平均值\n     */\n\n    public Long getInsertCountAvg() {\n        Long insertCountAvg = 0L;\n        if (items.size() != 0) {\n            for (TableStat item : items) {\n                if (item.getEndTime().equals(item.getStartTime())) {\n                    insertCountAvg += item.getInsertCount();\n                } else {\n                    insertCountAvg += item.getInsertCount() * 1000\n                                      / (item.getEndTime().getTime() - item.getStartTime().getTime());\n                }\n            }\n            insertCountAvg = insertCountAvg / items.size();\n        }\n        return insertCountAvg;\n\n    }\n\n    /**\n     * 对应updateCount的数据统计平均值\n     */\n\n    public Long getUpdateCountAvg() {\n        Long updateCountAvg = 0L;\n        if (items.size() != 0) {\n            for (TableStat item : items) {\n                if (item.getEndTime().equals(item.getStartTime())) {\n                    updateCountAvg += item.getUpdateCount();\n                } else {\n                    updateCountAvg += item.getUpdateCount() * 1000\n                                      / (item.getEndTime().getTime() - item.getStartTime().getTime());\n                }\n            }\n            updateCountAvg = updateCountAvg / items.size();\n        }\n        return updateCountAvg;\n\n    }\n\n    /**\n     * 对应deleteCount的数据统计平均值\n     */\n\n    public Long getDeleteCountAvg() {\n        Long deleteCountAvg = 0L;\n        if (items.size() != 0) {\n            for (TableStat item : items) {\n                if (item.getEndTime().equals(item.getStartTime())) {\n                    deleteCountAvg += item.getDeleteCount();\n                } else {\n                    deleteCountAvg += item.getDeleteCount() * 1000\n                                      / (item.getEndTime().getTime() - item.getStartTime().getTime());\n                }\n            }\n            deleteCountAvg = deleteCountAvg / items.size();\n        }\n        return deleteCountAvg;\n    }\n\n    /**\n     * 对应deleteCount的数据统计平均值\n     */\n\n    public Long getFileCountAvg() {\n        Long fileCountAvg = 0L;\n        if (items.size() != 0) {\n            for (TableStat item : items) {\n                if (item.getEndTime().equals(item.getStartTime())) {\n                    fileCountAvg += item.getFileCount();\n                } else {\n                    fileCountAvg += item.getFileCount() * 1000\n                                    / (item.getEndTime().getTime() - item.getStartTime().getTime());\n                }\n            }\n            fileCountAvg = fileCountAvg / items.size();\n        }\n        return fileCountAvg;\n    }\n\n    /**\n     * 对应deleteCount的数据统计平均值\n     */\n\n    public Long getFileSizeAvg() {\n        Long fileSizeAvg = 0L;\n        if (items.size() != 0) {\n            for (TableStat item : items) {\n                if (item.getEndTime().equals(item.getStartTime())) {\n                    fileSizeAvg += item.getFileSize();\n                } else {\n                    fileSizeAvg += item.getFileSize() * 1000\n                                   / (item.getEndTime().getTime() - item.getStartTime().getTime());\n                }\n            }\n            fileSizeAvg = fileSizeAvg / items.size();\n        }\n        return fileSizeAvg;\n    }\n\n    /**\n     * 对应insertCount的数据统计\n     */\n    public Long getInsertNumber() {\n        Long insertNumber = 0L;\n        if (items.size() != 0) {\n            for (TableStat item : items) {\n                insertNumber += item.getInsertCount();\n            }\n        }\n        return insertNumber;\n    }\n\n    /**\n     * 对应updateCount的数据统计\n     */\n    public Long getUpdateNumber() {\n        Long updateNumber = 0L;\n        if (items.size() != 0) {\n            for (TableStat item : items) {\n                updateNumber += item.getUpdateCount();\n            }\n        }\n        return updateNumber;\n    }\n\n    /**\n     * 对应deleteCount的数据统计\n     */\n    public Long getDeleteNumber() {\n        Long deleteNumber = 0L;\n        if (items.size() != 0) {\n            for (TableStat item : items) {\n                deleteNumber += item.getDeleteCount();\n            }\n        }\n        return deleteNumber;\n    }\n\n    /**\n     * 对应fileNumber的数据统计\n     */\n    public Long getFileNumber() {\n        Long fileNumber = 0L;\n        if (items.size() != 0) {\n            for (TableStat item : items) {\n                fileNumber += item.getFileCount();\n            }\n        }\n        return fileNumber;\n    }\n\n    /**\n     * 对应fileSize的数据统计\n     */\n    public Long getFileSize() {\n        Long fileSize = 0L;\n        if (items.size() != 0) {\n            for (TableStat item : items) {\n                fileSize += item.getFileSize();\n            }\n        }\n        return fileSize;\n    }\n\n    // ===================== setter / getter =========================\n\n    public List<TableStat> getItems() {\n        return items;\n    }\n\n    public void setItems(List<TableStat> items) {\n        this.items = items;\n    }\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/statistics/table/param/TimelineBehaviorHistoryCondition.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.statistics.table.param;\n\nimport java.util.Date;\n\n/**\n * @author sarah.lij 2012-7-13 下午04:51:55\n */\npublic class TimelineBehaviorHistoryCondition extends BehaviorHistoryCondition {\n\n    private Date start;\n    private Date end;\n\n    public Date getStart() {\n        return start;\n    }\n\n    public void setStart(Date start) {\n        this.start = start;\n    }\n\n    public Date getEnd() {\n        return end;\n    }\n\n    public void setEnd(Date end) {\n        this.end = end;\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/statistics/throughput/ThroughputStatService.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.statistics.throughput;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport com.alibaba.otter.manager.biz.statistics.throughput.param.AnalysisType;\nimport com.alibaba.otter.manager.biz.statistics.throughput.param.RealtimeThroughputCondition;\nimport com.alibaba.otter.manager.biz.statistics.throughput.param.ThroughputCondition;\nimport com.alibaba.otter.manager.biz.statistics.throughput.param.ThroughputInfo;\nimport com.alibaba.otter.manager.biz.statistics.throughput.param.TimelineThroughputCondition;\nimport com.alibaba.otter.shared.common.model.statistics.throughput.ThroughputStat;\n\n/**\n * @author jianghang 2011-9-8 下午01:26:31\n */\npublic interface ThroughputStatService {\n\n    public Map<AnalysisType, ThroughputInfo> listRealtimeThroughput(RealtimeThroughputCondition condition);\n\n    public Map<Long, ThroughputInfo> listTimelineThroughput(TimelineThroughputCondition condition);\n\n    public List<ThroughputStat> listRealtimeThroughputByPipelineIds(List<Long> pipelineIds, int minute);\n\n    public ThroughputStat findThroughputStatByPipelineId(ThroughputCondition condition);\n\n    public void createOrUpdateThroughput(ThroughputStat item);\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/statistics/throughput/dal/ThroughputDAO.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.statistics.throughput.dal;\n\nimport java.util.List;\n\nimport com.alibaba.otter.manager.biz.statistics.throughput.dal.dataobject.ThroughputStatDO;\nimport com.alibaba.otter.manager.biz.statistics.throughput.param.RealtimeThroughputCondition;\nimport com.alibaba.otter.manager.biz.statistics.throughput.param.ThroughputCondition;\nimport com.alibaba.otter.manager.biz.statistics.throughput.param.TimelineThroughputCondition;\n\n/**\n * @author simon\n */\npublic interface ThroughputDAO {\n\n    public void insertThroughputStat(ThroughputStatDO throughputStat);\n\n    public void deleteThroughputStat(Long throughputStatId);\n\n    public void modifyThroughputStat(ThroughputStatDO throughputStat);\n\n    public ThroughputStatDO findThroughputStatById(Long throughputStatId);\n\n    public List<ThroughputStatDO> listRealtimeThroughputStat(RealtimeThroughputCondition condition);\n\n    public List<ThroughputStatDO> listTimelineThroughputStat(TimelineThroughputCondition condition);\n\n    public ThroughputStatDO findRealtimeThroughputStat(ThroughputCondition condition);\n\n    public List<ThroughputStatDO> listThroughputStatByPipelineId(Long pipelineId);\n\n    public List<ThroughputStatDO> listRealTimeThroughputStatByPipelineIds(List<Long> pipelineIds, int minute);\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/statistics/throughput/dal/dataobject/ThroughputStatDO.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.statistics.throughput.dal.dataobject;\n\nimport java.io.Serializable;\nimport java.util.Date;\n\nimport org.apache.commons.lang.builder.ToStringBuilder;\n\nimport com.alibaba.otter.shared.common.model.statistics.throughput.ThroughputType;\nimport com.alibaba.otter.shared.common.utils.OtterToStringStyle;\n\n/**\n * @author danping.yudp\n */\n\npublic class ThroughputStatDO implements Serializable {\n\n    private static final long serialVersionUID = 1L;\n    private Long              id;\n    private Long              pipelineId;\n    private Date              startTime;\n    private Date              endTime;\n    private ThroughputType    type;\n    private Long              number;\n    private Long              size;\n    private Date              gmtCreate;\n    private Date              gmtModified;\n\n    public Long getId() {\n        return id;\n    }\n\n    public void setId(Long id) {\n        this.id = id;\n    }\n\n    public Long getPipelineId() {\n        return pipelineId;\n    }\n\n    public void setPipelineId(Long pipelineId) {\n        this.pipelineId = pipelineId;\n    }\n\n    public Date getStartTime() {\n        return startTime;\n    }\n\n    public void setStartTime(Date startTime) {\n        this.startTime = startTime;\n    }\n\n    public Date getEndTime() {\n        return endTime;\n    }\n\n    public void setEndTime(Date endTime) {\n        this.endTime = endTime;\n    }\n\n    public ThroughputType getType() {\n        return type;\n    }\n\n    public void setType(ThroughputType type) {\n        this.type = type;\n    }\n\n    public Long getNumber() {\n        return number;\n    }\n\n    public void setNumber(Long number) {\n        this.number = number;\n    }\n\n    public Long getSize() {\n        return size;\n    }\n\n    public void setSize(Long size) {\n        this.size = size;\n    }\n\n    public Date getGmtCreate() {\n        return gmtCreate;\n    }\n\n    public void setGmtCreate(Date gmtCreate) {\n        this.gmtCreate = gmtCreate;\n    }\n\n    public Date getGmtModified() {\n        return gmtModified;\n    }\n\n    public void setGmtModified(Date gmtModified) {\n        this.gmtModified = gmtModified;\n    }\n\n    @Override\n    public String toString() {\n        return ToStringBuilder.reflectionToString(this, OtterToStringStyle.DEFAULT_STYLE);\n    }\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/statistics/throughput/dal/ibatis/IbatisThroughputDAO.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.statistics.throughput.dal.ibatis;\n\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.springframework.orm.ibatis.support.SqlMapClientDaoSupport;\n\nimport com.alibaba.otter.manager.biz.statistics.throughput.dal.ThroughputDAO;\nimport com.alibaba.otter.manager.biz.statistics.throughput.dal.dataobject.ThroughputStatDO;\nimport com.alibaba.otter.manager.biz.statistics.throughput.param.RealtimeThroughputCondition;\nimport com.alibaba.otter.manager.biz.statistics.throughput.param.ThroughputCondition;\nimport com.alibaba.otter.manager.biz.statistics.throughput.param.TimelineThroughputCondition;\n\n/**\n * @author simon\n */\npublic class IbatisThroughputDAO extends SqlMapClientDaoSupport implements ThroughputDAO {\n\n    @Override\n    public void insertThroughputStat(ThroughputStatDO throughputStat) {\n\n        getSqlMapClientTemplate().insert(\"insertThroughputStat\", throughputStat);\n    }\n\n    @Override\n    public void deleteThroughputStat(Long throughputStatId) {\n        getSqlMapClientTemplate().delete(\"deleteThroughputStat\", throughputStatId);\n    }\n\n    @Override\n    public void modifyThroughputStat(ThroughputStatDO throughputStat) {\n        getSqlMapClientTemplate().update(\"modifyThroughputStat\", throughputStat);\n    }\n\n    @Override\n    public ThroughputStatDO findThroughputStatById(Long throughputStatId) {\n\n        return (ThroughputStatDO) getSqlMapClientTemplate().queryForObject(\"findThroughputStatById\", throughputStatId);\n    }\n\n    @Override\n    public List<ThroughputStatDO> listRealtimeThroughputStat(RealtimeThroughputCondition condition) {\n\n        return (List<ThroughputStatDO>) getSqlMapClientTemplate().queryForList(\"listRealtimeThroughputStat\", condition);\n    }\n\n    @Override\n    public List<ThroughputStatDO> listTimelineThroughputStat(TimelineThroughputCondition condition) {\n\n        return (List<ThroughputStatDO>) getSqlMapClientTemplate().queryForList(\"listTimelineThroughputStat\", condition);\n    }\n\n    @Override\n    public List<ThroughputStatDO> listThroughputStatByPipelineId(Long pipelineId) {\n\n        return (List<ThroughputStatDO>) getSqlMapClientTemplate().queryForList(\"listThroughputStatByPipelineId\",\n                                                                               pipelineId);\n    }\n\n    @Override\n    public ThroughputStatDO findRealtimeThroughputStat(ThroughputCondition condition) {\n\n        return (ThroughputStatDO) getSqlMapClientTemplate().queryForObject(\"findRealtimeThroughputStat\", condition);\n    }\n\n    public List<ThroughputStatDO> listRealTimeThroughputStatByPipelineIds(List<Long> pipelineIds, int minute) {\n        Map<String, Object> param = new HashMap<String, Object>();\n        param.put(\"pipelineIds\", pipelineIds);\n        param.put(\"minute\", minute);\n        return (List<ThroughputStatDO>) getSqlMapClientTemplate().queryForList(\"listRealtimeThroughputStatByPipelineIds\",\n                                                                               param);\n    }\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/statistics/throughput/impl/ThroughputStatServiceImpl.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.statistics.throughput.impl;\n\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport com.alibaba.otter.shared.common.utils.Assert;\nimport com.alibaba.otter.manager.biz.statistics.throughput.ThroughputStatService;\nimport com.alibaba.otter.manager.biz.statistics.throughput.dal.ThroughputDAO;\nimport com.alibaba.otter.manager.biz.statistics.throughput.dal.dataobject.ThroughputStatDO;\nimport com.alibaba.otter.manager.biz.statistics.throughput.param.AnalysisType;\nimport com.alibaba.otter.manager.biz.statistics.throughput.param.RealtimeThroughputCondition;\nimport com.alibaba.otter.manager.biz.statistics.throughput.param.ThroughputCondition;\nimport com.alibaba.otter.manager.biz.statistics.throughput.param.ThroughputInfo;\nimport com.alibaba.otter.manager.biz.statistics.throughput.param.TimelineThroughputCondition;\nimport com.alibaba.otter.shared.common.model.statistics.throughput.ThroughputStat;\n\n/**\n * @author danping.yudp\n */\npublic class ThroughputStatServiceImpl implements ThroughputStatService {\n\n    private ThroughputDAO throughputDao;\n\n    public ThroughputDAO getThroughputDao() {\n        return throughputDao;\n    }\n\n    public void setThroughputDao(ThroughputDAO throughputDao) {\n        this.throughputDao = throughputDao;\n    }\n\n    /**\n     * 在数据库中插入throughputStat\n     */\n    public void createOrUpdateThroughput(ThroughputStat item) {\n        Assert.assertNotNull(item);\n        throughputDao.insertThroughputStat(throughputStatModelToDo(item));\n    }\n\n    public ThroughputStat findThroughputStatByPipelineId(ThroughputCondition condition) {\n        Assert.assertNotNull(condition);\n        ThroughputStatDO throughputStatDO = throughputDao.findRealtimeThroughputStat(condition);\n        ThroughputStat throughputStat = new ThroughputStat();\n        if (throughputStatDO != null) {\n            throughputStat = throughputStatDOToModel(throughputStatDO);\n        }\n        return throughputStat;\n    }\n\n    /**\n     * 3种时间间隔的统计信息\n     */\n\n    public Map<AnalysisType, ThroughputInfo> listRealtimeThroughput(RealtimeThroughputCondition condition) {\n        Assert.assertNotNull(condition);\n        Map<AnalysisType, ThroughputInfo> throughputInfos = new HashMap<AnalysisType, ThroughputInfo>();\n        TimelineThroughputCondition timelineCondition = new TimelineThroughputCondition();\n        Date realtime = new Date(System.currentTimeMillis());\n        timelineCondition.setPipelineId(condition.getPipelineId());\n        timelineCondition.setType(condition.getType());\n        timelineCondition.setStart(new Date(realtime.getTime() - condition.getMax() * 60 * 1000));\n        timelineCondition.setEnd(realtime);\n        List<ThroughputStatDO> throughputStatDOs = throughputDao.listTimelineThroughputStat(timelineCondition);\n        for (AnalysisType analysisType : condition.getAnalysisType()) {\n            ThroughputInfo throughputInfo = new ThroughputInfo();\n            List<ThroughputStat> throughputStat = new ArrayList<ThroughputStat>();\n            for (ThroughputStatDO throughputStatDO : throughputStatDOs) {\n                if (realtime.getTime() - throughputStatDO.getEndTime().getTime() <= analysisType.getValue() * 60 * 1000) {\n                    throughputStat.add(throughputStatDOToModel(throughputStatDO));\n                }\n            }\n            throughputInfo.setItems(throughputStat);\n            throughputInfo.setSeconds(analysisType.getValue() * 60L);\n            throughputInfos.put(analysisType, throughputInfo);\n        }\n        return throughputInfos;\n\n    }\n\n    /**\n     * <pre>\n     * 列出pipeLineId下，start-end时间段下的throughputStat \n     * 首先从数据库中取出这一段时间所以数据，该数据都是根据end_time倒排序的, 每隔1分钟将这些数据分组\n     * </pre>\n     */\n    public Map<Long, ThroughputInfo> listTimelineThroughput(TimelineThroughputCondition condition) {\n        Assert.assertNotNull(condition);\n        Map<Long, ThroughputInfo> throughputInfos = new LinkedHashMap<Long, ThroughputInfo>();\n        List<ThroughputStatDO> throughputStatDOs = throughputDao.listTimelineThroughputStat(condition);\n        int size = throughputStatDOs.size();\n        int k = size - 1;\n        for (Long i = condition.getStart().getTime(); i <= condition.getEnd().getTime(); i += 60 * 1000) {\n            ThroughputInfo throughputInfo = new ThroughputInfo();\n            List<ThroughputStat> throughputStat = new ArrayList<ThroughputStat>();\n            // 取出每个时间点i以内的数据，k是一个游标，每次遍历时前面已经取过了的数据就不用再遍历了\n            for (int j = k; j >= 0; --j) {\n                if ((i - throughputStatDOs.get(j).getEndTime().getTime() <= 60 * 1000)\n                    && (i - throughputStatDOs.get(j).getEndTime().getTime() >= 0)) {\n                    throughputStat.add(throughputStatDOToModel(throughputStatDOs.get(j)));\n                    k = j - 1;\n                }// 如果不满足if条件，则后面的数据也不用再遍历\n                else {\n                    break;\n                }\n            }\n            if (throughputStat.size() > 0) {\n                throughputInfo.setItems(throughputStat);\n                throughputInfo.setSeconds(1 * 60L);\n                throughputInfos.put(i, throughputInfo);\n            }\n\n        }\n        return throughputInfos;\n    }\n\n    public List<ThroughputStat> listRealtimeThroughputByPipelineIds(List<Long> pipelineIds, int minute) {\n        Assert.assertNotNull(pipelineIds);\n        List<ThroughputStatDO> throughputStatDOs = throughputDao.listRealTimeThroughputStatByPipelineIds(pipelineIds,\n                                                                                                         minute);\n\n        List<ThroughputStat> infos = new ArrayList<ThroughputStat>();\n        for (ThroughputStatDO throughputStatDO : throughputStatDOs) {\n            infos.add(throughputStatDOToModel(throughputStatDO));\n        }\n\n        return infos;\n    }\n\n    /**\n     * 用于Model对象转化为DO对象\n     * \n     * @param throughputStat\n     * @return throughputStatDO\n     */\n    private ThroughputStatDO throughputStatModelToDo(ThroughputStat throughputStat) {\n        ThroughputStatDO throughputStatDO = new ThroughputStatDO();\n        throughputStatDO.setId(throughputStat.getId());\n        throughputStatDO.setPipelineId(throughputStat.getPipelineId());\n        throughputStatDO.setStartTime(throughputStat.getStartTime());\n        throughputStatDO.setEndTime(throughputStat.getEndTime());\n        throughputStatDO.setType(throughputStat.getType());\n        throughputStatDO.setNumber(throughputStat.getNumber());\n        throughputStatDO.setSize(throughputStat.getSize());\n        throughputStatDO.setGmtCreate(throughputStat.getGmtCreate());\n        throughputStatDO.setGmtModified(throughputStat.getGmtModified());\n        return throughputStatDO;\n    }\n\n    /**\n     * 用于DO对象转化为Model对象\n     * \n     * @param throughputStatDO\n     * @return throughputStat\n     */\n    private ThroughputStat throughputStatDOToModel(ThroughputStatDO throughputStatDO) {\n        ThroughputStat throughputStat = new ThroughputStat();\n        throughputStat.setId(throughputStatDO.getId());\n        throughputStat.setPipelineId(throughputStatDO.getPipelineId());\n        throughputStat.setStartTime(throughputStatDO.getStartTime());\n        throughputStat.setEndTime(throughputStatDO.getEndTime());\n        throughputStat.setType(throughputStatDO.getType());\n        throughputStat.setNumber(throughputStatDO.getNumber());\n        throughputStat.setSize(throughputStatDO.getSize());\n        throughputStat.setGmtCreate(throughputStatDO.getGmtCreate());\n        throughputStat.setGmtModified(throughputStatDO.getGmtModified());\n        return throughputStat;\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/statistics/throughput/param/AnalysisType.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.statistics.throughput.param;\n\n/**\n * @author jianghang 2011-9-8 下午01:22:51\n */\npublic enum AnalysisType {\n    ONE_MINUTE(1), FIVE_MINUTE(5), FIFTEEN_MINUTE(15);\n\n    private final int value;\n\n    public int getValue() {\n        return value;\n    }\n\n    AnalysisType(int value){\n        this.value = value;\n    }\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/statistics/throughput/param/RealtimeThroughputCondition.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.statistics.throughput.param;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\n/**\n * @author jianghang 2011-9-8 下午01:23:59\n */\npublic class RealtimeThroughputCondition extends ThroughputCondition {\n\n    private List<AnalysisType> analysisType;\n\n    /**\n     * analysisType的最大value\n     */\n    public int getMax() {\n        List<Integer> value = new ArrayList<Integer>();\n        for (AnalysisType at : analysisType) {\n            value.add(at.getValue());\n        }\n        return Collections.max(value);\n    }\n\n    /**\n     * analysisType的最小value\n     */\n    public int getMin() {\n        List<Integer> value = new ArrayList<Integer>();\n        for (AnalysisType at : analysisType) {\n            value.add(at.getValue());\n        }\n        return Collections.min(value);\n    }\n\n    // ===================== setter / getter =========================\n\n    public List<AnalysisType> getAnalysisType() {\n        return analysisType;\n    }\n\n    public void setAnalysisType(List<AnalysisType> analysisType) {\n        this.analysisType = analysisType;\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/statistics/throughput/param/ThroughputCondition.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.statistics.throughput.param;\n\nimport org.apache.commons.lang.builder.ToStringBuilder;\n\nimport com.alibaba.otter.shared.common.model.statistics.throughput.ThroughputType;\nimport com.alibaba.otter.shared.common.utils.OtterToStringStyle;\n\n/**\n * @author jianghang 2011-9-8 下午01:21:09\n */\npublic class ThroughputCondition {\n\n    private Long           pipelineId;\n    private ThroughputType type;\n    private boolean        detail;\n\n    public Long getPipelineId() {\n        return pipelineId;\n    }\n\n    public void setPipelineId(Long pipelineId) {\n        this.pipelineId = pipelineId;\n    }\n\n    public ThroughputType getType() {\n        return type;\n    }\n\n    public void setType(ThroughputType type) {\n        this.type = type;\n    }\n\n    public boolean isDetail() {\n        return detail;\n    }\n\n    public void setDetail(boolean detail) {\n        this.detail = detail;\n    }\n\n    @Override\n    public String toString() {\n        return ToStringBuilder.reflectionToString(this, OtterToStringStyle.DEFAULT_STYLE);\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/statistics/throughput/param/ThroughputInfo.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.statistics.throughput.param;\n\nimport java.util.List;\n\nimport com.alibaba.otter.shared.common.model.statistics.throughput.ThroughputStat;\n\n/**\n * @author jianghang 2011-9-8 下午01:27:33\n */\npublic class ThroughputInfo {\n\n    private Long                 seconds = 60L;\n    private List<ThroughputStat> items;\n\n    /**\n     * 对应number的数据统计平均值\n     */\n    public Long getTps() {\n        Long tps = 0L;\n        if (items.size() != 0) {\n            for (ThroughputStat item : items) {\n                if (item.getEndTime().equals(item.getStartTime())) {\n                    tps += item.getNumber();\n                } else {\n                    tps += item.getNumber() * 1000 / (item.getEndTime().getTime() - item.getStartTime().getTime());\n                }\n            }\n            if (seconds != 0) {\n                tps = tps / seconds;\n            }\n        }\n        return tps;\n    }\n\n    /**\n     * 对应size的数据统计平均值\n     */\n    public Long getQuantity() {\n        Long quantity = 0L;\n        if (items.size() != 0) {\n            for (ThroughputStat item : items) {\n                if (item.getEndTime().equals(item.getStartTime())) {\n                    quantity += item.getSize();\n                } else {\n                    quantity += item.getSize() * 1000 / (item.getEndTime().getTime() - item.getStartTime().getTime());\n                }\n            }\n\n            if (seconds != 0) {\n                quantity = quantity / items.size();\n            }\n        }\n        return quantity;\n    }\n\n    /**\n     * 对应number的数据统计\n     */\n    public Long getNumber() {\n        Long number = 0L;\n        if (items.size() != 0) {\n            for (ThroughputStat item : items) {\n                number += item.getNumber();\n            }\n        }\n        return number;\n    }\n\n    /**\n     * 对应size的数据统计\n     */\n    public Long getSize() {\n        Long size = 0L;\n        if (items.size() != 0) {\n            for (ThroughputStat item : items) {\n                size += item.getSize();\n            }\n        }\n        return size;\n    }\n\n    // ===================== setter / getter =========================\n\n    public List<ThroughputStat> getItems() {\n        return items;\n    }\n\n    public void setItems(List<ThroughputStat> items) {\n        this.items = items;\n    }\n\n    public Long getSeconds() {\n        return seconds;\n    }\n\n    public void setSeconds(Long seconds) {\n        this.seconds = seconds;\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/statistics/throughput/param/TimelineThroughputCondition.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.statistics.throughput.param;\n\nimport java.util.Date;\n\n/**\n * @author jianghang 2011-9-8 下午01:25:25\n */\npublic class TimelineThroughputCondition extends ThroughputCondition {\n\n    private Date start;\n    private Date end;\n\n    public Date getStart() {\n        return start;\n    }\n\n    public void setStart(Date start) {\n        this.start = start;\n    }\n\n    public Date getEnd() {\n        return end;\n    }\n\n    public void setEnd(Date end) {\n        this.end = end;\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/user/UserService.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.user;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport com.alibaba.otter.shared.common.model.user.User;\n\n/**\n * @author simon\n */\npublic interface UserService {\n\n    public void createUser(User user);\n\n    public void deleteUser(Long userId);\n\n    public void updataUser(User user);\n\n    public User findUserById(Long userId);\n\n    public User login(String name, String password);\n\n    public List<User> ListAllUsers();\n\n    public List<User> listByCondition(Map condition);\n\n    public int getCount();\n\n    public int getCount(Map condition);\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/user/dal/UserDAO.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.user.dal;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport com.alibaba.otter.manager.biz.user.dal.dataobject.UserDO;\n\n/**\n * TODO Comment of UserDAO\n * \n * @author simon\n */\npublic interface UserDAO {\n\n    public UserDO findUserById(Long userId);\n\n    public UserDO insertUser(UserDO user);\n\n    public void updateUser(UserDO user);\n\n    public void deleteUser(Long userId);\n\n    public List<UserDO> listAllUsers();\n\n    public List<UserDO> listByCondition(Map condition);\n\n    public UserDO getAuthenticatedUser(String name, String password);\n\n    public boolean chackUnique(UserDO user);\n\n    public int getCount();\n\n    public int getCount(Map condition);\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/user/dal/dataobject/UserDO.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.user.dal.dataobject;\n\nimport java.io.Serializable;\nimport java.util.Date;\n\nimport com.alibaba.otter.shared.common.model.user.AuthorizeType;\n\npublic class UserDO implements Serializable {\n\n    private static final long serialVersionUID = 6373904824730876247L;\n    private Long              id;\n    private String            name;\n    private String            password;\n    private String            department;\n    private String            realName;\n    private AuthorizeType     authorizeType;\n    private Date              gmtCreate;\n    private Date              gmtModified;\n\n    public Long getId() {\n        return id;\n    }\n\n    public void setId(Long id) {\n        this.id = id;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public void setName(String name) {\n        this.name = name;\n    }\n\n    public String getPassword() {\n        return password;\n    }\n\n    public void setPassword(String password) {\n        this.password = password;\n    }\n\n    public String getDepartment() {\n        return department;\n    }\n\n    public void setDepartment(String department) {\n        this.department = department;\n    }\n\n    public String getRealName() {\n        return realName;\n    }\n\n    public void setRealName(String realName) {\n        this.realName = realName;\n    }\n\n    public Date getGmtCreate() {\n        return gmtCreate;\n    }\n\n    public void setGmtCreate(Date gmtCreate) {\n        this.gmtCreate = gmtCreate;\n    }\n\n    public Date getGmtModified() {\n        return gmtModified;\n    }\n\n    public void setGmtModified(Date gmtModified) {\n        this.gmtModified = gmtModified;\n    }\n\n    public AuthorizeType getAuthorizeType() {\n        return authorizeType;\n    }\n\n    public void setAuthorizeType(AuthorizeType authorizeType) {\n        this.authorizeType = authorizeType;\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/user/dal/ibatis/IbatisUserDAO.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.user.dal.ibatis;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.springframework.orm.ibatis.support.SqlMapClientDaoSupport;\n\nimport com.alibaba.otter.shared.common.utils.Assert;\nimport com.alibaba.otter.manager.biz.user.dal.UserDAO;\nimport com.alibaba.otter.manager.biz.user.dal.dataobject.UserDO;\n\n/**\n * TODO Comment of IbatisUserDAO\n * \n * @author simon\n */\npublic class IbatisUserDAO extends SqlMapClientDaoSupport implements UserDAO {\n\n    public UserDO findUserById(Long userId) {\n        Assert.assertNotNull(userId);\n        return (UserDO) getSqlMapClientTemplate().queryForObject(\"findUserById\", userId);\n    }\n\n    public List<UserDO> listAllUsers() {\n        return (List<UserDO>) getSqlMapClientTemplate().queryForList(\"listUsers\");\n    }\n\n    public List<UserDO> listByCondition(Map condition) {\n        return (List<UserDO>) getSqlMapClientTemplate().queryForList(\"listUsers\", condition);\n    }\n\n    public UserDO insertUser(UserDO user) {\n        Assert.assertNotNull(user);\n        getSqlMapClientTemplate().insert(\"insertUser\", user);\n        return user;\n    }\n\n    public void updateUser(UserDO user) {\n        Assert.assertNotNull(user);\n        getSqlMapClientTemplate().update(\"updateUser\", user);\n    }\n\n    public boolean chackUnique(UserDO user) {\n        Assert.assertNotNull(user);\n        int count = (Integer) getSqlMapClientTemplate().queryForObject(\"checkUserUnique\", user);\n        return count == 0 ? true : false;\n    }\n\n    public void deleteUser(Long userId) {\n        Assert.assertNotNull(userId);\n        getSqlMapClientTemplate().delete(\"deleteUserById\", userId);\n    }\n\n    public UserDO getAuthenticatedUser(String name, String password) {\n        UserDO userDo = new UserDO();\n\n        userDo.setName(name);\n        userDo.setPassword(password);\n\n        return (UserDO) getSqlMapClientTemplate().queryForObject(\"getUserByNameAndPassword\", userDo);\n    }\n\n    public int getCount() {\n        Integer count = (Integer) getSqlMapClientTemplate().queryForObject(\"getUserCount\");\n        return count.intValue();\n    }\n\n    public int getCount(Map condition) {\n        Integer count = (Integer) getSqlMapClientTemplate().queryForObject(\"getUserCount\", condition);\n        return count.intValue();\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/user/impl/UserServiceImpl.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.user.impl;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.alibaba.otter.shared.common.utils.Assert;\nimport com.alibaba.otter.shared.common.model.user.User;\nimport com.alibaba.otter.manager.biz.common.exceptions.ManagerException;\nimport com.alibaba.otter.manager.biz.common.exceptions.RepeatConfigureException;\nimport com.alibaba.otter.manager.biz.user.UserService;\nimport com.alibaba.otter.manager.biz.user.dal.UserDAO;\nimport com.alibaba.otter.manager.biz.user.dal.dataobject.UserDO;\n\n/**\n * TODO Comment of UserServiceImpl\n * \n * @author simon\n */\npublic class UserServiceImpl implements UserService {\n\n    private static final Logger logger = LoggerFactory.getLogger(UserServiceImpl.class);\n    private UserDAO             userDao;\n\n    public void createUser(User user) {\n        Assert.assertNotNull(user);\n        try {\n            UserDO userDo = userDao.insertUser(modelToDo(user));\n            if (userDo.getId() == 0) {\n                String exceptionCause = \"exist the same name user in the database.\";\n                logger.warn(\"WARN ## \" + exceptionCause);\n                throw new RepeatConfigureException(exceptionCause);\n            }\n        } catch (RepeatConfigureException rce) {\n            throw rce;\n        } catch (Exception e) {\n            logger.error(\"ERROR ## create user has an exception!\");\n            throw new ManagerException(e);\n        }\n    }\n\n    public void deleteUser(Long userId) {\n        Assert.assertNotNull(userId);\n        userDao.deleteUser(userId);\n    }\n\n    public void updataUser(User user) {\n        Assert.assertNotNull(user);\n        try {\n            UserDO UserDo = modelToDo(user);\n            if (userDao.chackUnique(UserDo)) {\n                userDao.updateUser(UserDo);\n            } else {\n                String exceptionCause = \"exist the same name user in the database.\";\n                logger.warn(\"WARN ## \" + exceptionCause);\n                throw new RepeatConfigureException(exceptionCause);\n            }\n        } catch (RepeatConfigureException rce) {\n            throw rce;\n        } catch (Exception e) {\n            logger.error(\"ERROR ## create user has an exception!\");\n            throw new ManagerException(e);\n        }\n    }\n\n    public User findUserById(Long userId) {\n        Assert.assertNotNull(userId);\n        return doToModel(userDao.findUserById(userId));\n    }\n\n    public List<User> ListAllUsers() {\n        List<UserDO> userDos = userDao.listAllUsers();\n        List<User> users = new ArrayList<User>();\n        for (UserDO userDo : userDos) {\n            users.add(doToModel(userDo));\n        }\n        return users;\n    }\n\n    public List<User> listByCondition(Map condition) {\n        List<UserDO> userDos = userDao.listByCondition(condition);\n        List<User> users = new ArrayList<User>();\n        for (UserDO userDo : userDos) {\n            users.add(doToModel(userDo));\n        }\n        return users;\n    }\n\n    /**\n     * 拿到user总数进行分页\n     */\n    public int getCount() {\n        return userDao.getCount();\n    }\n\n    public int getCount(Map condition) {\n        return userDao.getCount(condition);\n    }\n\n    @Override\n    public User login(String name, String password) {\n        UserDO userDo = userDao.getAuthenticatedUser(name, password);\n        if (null == userDo) {\n            return null;\n        }\n        return doToModel(userDo);\n    }\n\n    private User doToModel(UserDO userDo) {\n        User user = new User();\n        user.setId(userDo.getId());\n        user.setName(userDo.getName());\n        user.setDepartment(userDo.getDepartment());\n        user.setRealName(userDo.getRealName());\n        user.setAuthorizeType(userDo.getAuthorizeType());\n        user.setGmtCreate(userDo.getGmtCreate());\n        user.setGmtModified(userDo.getGmtModified());\n        return user;\n    }\n\n    private UserDO modelToDo(User user) {\n        UserDO userDo = new UserDO();\n        userDo.setId(user.getId());\n        userDo.setName(user.getName());\n        userDo.setPassword(user.getPassword());\n        userDo.setDepartment(user.getDepartment());\n        userDo.setRealName(user.getRealName());\n        userDo.setAuthorizeType(user.getAuthorizeType());\n        userDo.setGmtCreate(user.getGmtCreate());\n        userDo.setGmtModified(user.getGmtModified());\n        return userDo;\n    }\n\n    /* ------------------------setter / getter--------------------------- */\n\n    public UserDAO getUserDao() {\n        return userDao;\n    }\n\n    public void setUserDao(UserDAO userDao) {\n        this.userDao = userDao;\n    }\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/utils/DataSourceChecker.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.utils;\n\nimport java.sql.Connection;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.sql.Statement;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport javax.sql.DataSource;\n\nimport org.apache.commons.lang.StringUtils;\nimport org.apache.ddlutils.model.Table;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.jdbc.core.JdbcTemplate;\n\nimport com.alibaba.otter.manager.biz.common.DataSourceCreator;\nimport com.alibaba.otter.manager.biz.config.datamediasource.DataMediaSourceService;\nimport com.alibaba.otter.shared.common.model.config.ConfigHelper;\nimport com.alibaba.otter.shared.common.model.config.ModeValueFilter;\nimport com.alibaba.otter.shared.common.model.config.data.DataMedia.ModeValue;\nimport com.alibaba.otter.shared.common.model.config.data.DataMediaSource;\nimport com.alibaba.otter.shared.common.model.config.data.DataMediaType;\nimport com.alibaba.otter.shared.common.model.config.data.db.DbMediaSource;\nimport com.alibaba.otter.shared.common.utils.meta.DdlSchemaFilter;\nimport com.alibaba.otter.shared.common.utils.meta.DdlTableNameFilter;\nimport com.alibaba.otter.shared.common.utils.meta.DdlUtils;\n\n/**\n * @author simon 2011-11-25 下午04:57:55\n */\npublic class DataSourceChecker {\n\n    private static final Logger    logger             = LoggerFactory.getLogger(DataSourceChecker.class);\n    private DataMediaSourceService dataMediaSourceService;\n    private DataSourceCreator      dataSourceCreator;\n\n    // private static final String MYSQL_FLAG = \"mysql\";\n\n    // private static final String ORACLE_FLAG = \"oracle\";\n\n    // 选择的数据库类型和jdbc-url不匹配\n    // private static final String DBTYPE_CONFLICT =\n    // \"\\u9009\\u62e9\\u7684\\u6570\\u636e\\u5e93\\u7c7b\\u578b\\u548cjdbc-url\\u4e0d\\u5339\\u914d\";\n    // 恭喜,数据库通过验证!\n    private static final String    DATABASE_SUCCESS   = \"\\u606d\\u559c,\\u6570\\u636e\\u5e93\\u901a\\u8fc7\\u9a8c\\u8bc1!\";\n    // 抱歉,数据库未通过验证,请检查相关配置!\n    private static final String    DATABASE_FAIL      = \"\\u62b1\\u6b49,\\u6570\\u636e\\u5e93\\u672a\\u901a\\u8fc7\\u9a8c\\u8bc1,\\u8bf7\\u68c0\\u67e5\\u76f8\\u5173\\u914d\\u7f6e!\";\n    // 恭喜,select操作成功,权限正常!\n    private static final String    TABLE_SUCCESS      = \"\\u606d\\u559c,select\\u64cd\\u4f5c\\u6210\\u529f,\\u6743\\u9650\\u6b63\\u5e38!\";\n    // 抱歉select操作报错,请检查权限配置!\n    private static final String    TABLE_FAIL         = \"\\u62b1\\u6b49,\\u64cd\\u4f5c\\u62a5\\u9519,\\u8bf7\\u68c0\\u67e5\\u6743\\u9650\\u914d\\u7f6e!\";\n    // 恭喜,编码验证正确!\n    private static final String    ENCODE_QUERY_ERROR = \"\\u6267\\u884cSQL\\u51fa\\u9519,\\u8bf7\\u68c0\\u67e5\\u6570\\u636e\\u5e93\\u7c7b\\u578b\\u662f\\u5426\\u9009\\u62e9\\u6b63\\u786e!\";\n    // 抱歉,字符集不匹配,实际数据库默认字符集为:\n    private static final String    ENCODE_FAIL        = \"\\u62b1\\u6b49,\\u5b57\\u7b26\\u96c6\\u4e0d\\u5339\\u914d,\\u5b9e\\u9645\\u6570\\u636e\\u5e93\\u9ed8\\u8ba4\\u5b57\\u7b26\\u96c6\\u4e3a:\";\n    // SELECT未成功\n    private static final String    SELECT_FAIL        = \"SELECT\\u672a\\u6210\\u529f\";\n\n    // DELETE未成功\n    // private static final String DELETE_FAIL = \"DELETE\\u672a\\u6210\\u529f\";\n    // INSERT未成功\n    // private static final String INSERT_FAIL = \"INSERT\\u672a\\u6210\\u529f\";\n\n    // 分库前缀必须大于后缀，且不能为负数\n    // private static final String SPLIT_INDEX_FAIL =\n    // \"\\u5206\\u5e93\\u524d\\u7f00\\u5fc5\\u987b\\u5927\\u4e8e\\u540e\\u7f00\\uff0c\\u4e14\\u4e0d\\u80fd\\u4e3a\\u8d1f\\u6570\";\n\n    private void closeConnection(Connection conn) {\n        closeConnection(conn, null, null);\n    }\n\n    private void closeConnection(Connection conn, Statement st) {\n        closeConnection(conn, st, null);\n    }\n\n    private void closeConnection(Connection conn, Statement st, ResultSet rs) {\n        try {\n            if (null != rs) {\n                rs.close();\n            }\n            if (null != st) {\n                st.close();\n            }\n            if (null != conn) {\n                conn.close();\n            }\n\n        } catch (SQLException e) {\n            logger.error(\"\", e);\n        }\n    }\n\n    @SuppressWarnings(\"resource\")\n    public String check(String url, String username, String password, String encode, String sourceType) {\n        Connection conn = null;\n        Statement stmt = null;\n        ResultSet rs = null;\n        // boolean typeConflict = true;\n        // if ((sourceType.toLowerCase().equals(MYSQL_FLAG) &&\n        // url.toLowerCase().contains(MYSQL_FLAG))\n        // || sourceType.toLowerCase().equals(ORACLE_FLAG) &&\n        // url.toLowerCase().contains(ORACLE_FLAG)) {\n        // typeConflict = false;\n        // }\n        //\n        // if (typeConflict) {\n        // return DBTYPE_CONFLICT;\n        // }\n\n        DataSource dataSource = null;\n        try {\n\n            DbMediaSource dbMediaSource = new DbMediaSource();\n            dbMediaSource.setUrl(url);\n            dbMediaSource.setUsername(username);\n            dbMediaSource.setPassword(password);\n            dbMediaSource.setEncode(encode);\n\n            if (sourceType.equalsIgnoreCase(\"MYSQL\")) {\n                dbMediaSource.setType(DataMediaType.MYSQL);\n                dbMediaSource.setDriver(\"com.mysql.jdbc.Driver\");\n            } else if (sourceType.equalsIgnoreCase(\"ORACLE\")) {\n                dbMediaSource.setType(DataMediaType.ORACLE);\n                dbMediaSource.setDriver(\"oracle.jdbc.driver.OracleDriver\");\n            }\n\n            dataSource = dataSourceCreator.createDataSource(dbMediaSource);\n            try {\n                conn = dataSource.getConnection();\n            } catch (Exception e) {\n                logger.error(\"check error!\", e);\n            }\n\n            if (null == conn) {\n                return DATABASE_FAIL;\n            }\n\n            stmt = conn.createStatement();\n            String sql = null;\n            if (sourceType.equals(\"MYSQL\")) {\n                sql = \"SHOW VARIABLES LIKE 'character_set_database'\";\n            } else if (sourceType.equals(\"ORACLE\")) {\n                // sql\n                // =\"select * from V$NLS_PARAMETERS where parameter in('NLS_LANGUAGE','NLS_TERRITORY','NLS_CHARACTERSET')\";\n                sql = \"select * from V$NLS_PARAMETERS where parameter in('NLS_CHARACTERSET')\";\n            }\n            rs = stmt.executeQuery(sql);\n            while (rs.next()) {\n                String defaultEncode = null;\n                if (sourceType.equals(\"MYSQL\")) {\n                    defaultEncode = ((String) rs.getObject(2)).toLowerCase();\n                    defaultEncode = defaultEncode.equals(\"iso-8859-1\") ? \"latin1\" : defaultEncode;\n                    if (!encode.toLowerCase().equals(defaultEncode)) {\n                        return ENCODE_FAIL + defaultEncode;\n                    }\n                } else if (sourceType.equals(\"ORACLE\")) {\n                    // ORACLE查询服务器默认字符集需要管理员权限\n                    defaultEncode = ((String) rs.getObject(2)).toLowerCase();\n                    defaultEncode = defaultEncode.equalsIgnoreCase(\"zhs16gbk\") ? \"gbk\" : defaultEncode;\n                    defaultEncode = defaultEncode.equalsIgnoreCase(\"us7ascii\") ? \"iso-8859-1\" : defaultEncode;\n                    if (!encode.toLowerCase().equals(defaultEncode)) {\n                        return ENCODE_FAIL + defaultEncode;\n                    }\n                }\n\n            }\n\n        } catch (SQLException se) {\n            logger.error(\"check error!\", se);\n            return ENCODE_QUERY_ERROR;\n        } catch (Exception e) {\n            logger.error(\"check error!\", e);\n            return DATABASE_FAIL;\n        } finally {\n            closeConnection(conn);\n            dataSourceCreator.destroyDataSource(dataSource);\n        }\n\n        return DATABASE_SUCCESS;\n\n    }\n\n    public String checkMap(String namespace, String name, Long dataSourceId) {\n        Connection conn = null;\n        Statement stmt = null;\n        DataMediaSource source = dataMediaSourceService.findById(dataSourceId);\n        DataSource dataSource = null;\n        try {\n            DbMediaSource dbMediaSource = (DbMediaSource) source;\n            dataSource = dataSourceCreator.createDataSource(dbMediaSource);\n            // conn = dataSource.getConnection();\n            // if (null == conn) {\n            // return DATABASE_FAIL;\n            // }\n            ModeValue namespaceValue = ConfigHelper.parseMode(namespace);\n            ModeValue nameValue = ConfigHelper.parseMode(name);\n            String tempNamespace = namespaceValue.getSingleValue();\n            String tempName = nameValue.getSingleValue();\n\n            // String descSql = \"desc \" + tempNamespace + \".\" + tempName;\n            // stmt = conn.createStatement();\n\n            try {\n                Table table = DdlUtils.findTable(new JdbcTemplate(dataSource), tempNamespace, tempNamespace, tempName);\n                if (table == null) {\n                    return SELECT_FAIL;\n                }\n            } catch (SQLException se) {\n                logger.error(\"check error!\", se);\n                return SELECT_FAIL;\n            } catch (Exception e) {\n                logger.error(\"check error!\", e);\n                return SELECT_FAIL;\n            }\n\n            // String selectSql = \"SELECT * from \" + tempNamespace + \".\" +\n            // tempName + \" where 1 = 0\";\n            // String insertSql = \"INSERT INTO \" + tempNamespace + \".\" +\n            // tempName + \" select * from \";\n            // insertSql += \"( SELECT * from \" + tempNamespace + \".\" + tempName\n            // + \") table2 where 1 = 0\";\n            // String deleteSql = \"DELETE from \" + tempNamespace + \".\" +\n            // tempName + \" where 1 = 0\";\n            //\n            // stmt = conn.createStatement();\n            //\n            // try {\n            // stmt.executeQuery(selectSql);\n            // } catch (SQLException se) {\n            // return SELECT_FAIL;\n            // }\n            //\n            // try {\n            // stmt.execute(insertSql);\n            // } catch (SQLException se) {\n            // return INSERT_FAIL;\n            // }\n            //\n            // try {\n            // stmt.execute(deleteSql);\n            // } catch (SQLException se) {\n            // return DELETE_FAIL;\n            // }\n\n        } finally {\n            closeConnection(conn, stmt);\n            dataSourceCreator.destroyDataSource(dataSource);\n        }\n\n        return TABLE_SUCCESS;\n\n    }\n\n    public String checkNamespaceTables(final String namespace, final String name, final Long dataSourceId) {\n        DataSource dataSource = null;\n        try {\n            DataMediaSource source = dataMediaSourceService.findById(dataSourceId);\n            DbMediaSource dbMediaSource = (DbMediaSource) source;\n            dataSource = dataSourceCreator.createDataSource(dbMediaSource);\n            JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);\n\n            List<String> schemaList;\n            {\n                ModeValue mode = ConfigHelper.parseMode(namespace);\n                String schemaPattern = ConfigHelper.makeSQLPattern(mode, namespace);\n                final ModeValueFilter modeValueFilter = ConfigHelper.makeModeValueFilter(mode, namespace);\n                if (source.getType().isOracle()) {\n                    schemaList = Arrays.asList(namespace);\n                } else {\n                    schemaList = DdlUtils.findSchemas(jdbcTemplate, schemaPattern, new DdlSchemaFilter() {\n\n                        @Override\n                        public boolean accept(String schemaName) {\n                            return modeValueFilter.accept(schemaName);\n                        }\n                    });\n                }\n            }\n\n            final List<String> matchSchemaTables = new ArrayList<String>();\n            matchSchemaTables.add(\"Find schema and tables:\");\n            if (schemaList != null) {\n                ModeValue mode = ConfigHelper.parseMode(name);\n                String tableNamePattern = ConfigHelper.makeSQLPattern(mode, name);\n                final ModeValueFilter modeValueFilter = ConfigHelper.makeModeValueFilter(mode, name);\n                for (String schema : schemaList) {\n                    DdlUtils.findTables(jdbcTemplate, schema, schema, tableNamePattern, null, new DdlTableNameFilter() {\n\n                        @Override\n                        public boolean accept(String catalogName, String schemaName, String tableName) {\n                            if (modeValueFilter.accept(tableName)) {\n                                matchSchemaTables.add(schemaName + \".\" + tableName);\n                            }\n                            return false;\n                        }\n                    });\n                }\n            }\n            if (matchSchemaTables.size() == 1) {\n                return TABLE_FAIL;\n            }\n            return StringUtils.join(matchSchemaTables, \"<br>\\n\");\n        } catch (Exception e) {\n            logger.error(\"check error!\", e);\n            return TABLE_FAIL;\n        } finally {\n            dataSourceCreator.destroyDataSource(dataSource);\n        }\n    }\n\n    public void setDataMediaSourceService(DataMediaSourceService dataMediaSourceService) {\n        this.dataMediaSourceService = dataMediaSourceService;\n    }\n\n    public void setDataSourceCreator(DataSourceCreator dataSourceCreator) {\n        this.dataSourceCreator = dataSourceCreator;\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/main/java/com/alibaba/otter/manager/biz/utils/RegexUtils.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.utils;\n\nimport java.util.Map;\n\nimport org.apache.commons.lang.StringUtils;\nimport org.apache.oro.text.regex.MalformedPatternException;\nimport org.apache.oro.text.regex.Pattern;\nimport org.apache.oro.text.regex.PatternCompiler;\nimport org.apache.oro.text.regex.PatternMatcher;\nimport org.apache.oro.text.regex.Perl5Compiler;\nimport org.apache.oro.text.regex.Perl5Matcher;\n\nimport com.alibaba.otter.manager.biz.common.exceptions.ManagerException;\nimport com.google.common.base.Function;\nimport com.google.common.collect.OtterMigrateMap;\n\n/**\n * @author simon 2012-9-25 下午5:01:48\n * @version 4.1.0\n */\npublic class RegexUtils {\n\n    private static Map<String, Pattern> patterns = null;\n\n    static {\n        patterns = OtterMigrateMap.makeSoftValueComputingMap(new Function<String, Pattern>() {\n\n            public Pattern apply(String pattern) {\n                try {\n                    PatternCompiler pc = new Perl5Compiler();\n                    return pc.compile(pattern, Perl5Compiler.CASE_INSENSITIVE_MASK | Perl5Compiler.READ_ONLY_MASK);\n                } catch (MalformedPatternException e) {\n                    throw new ManagerException(e);\n                }\n            }\n        });\n    }\n\n    public static String findFirst(String originalStr, String regex) {\n        if (StringUtils.isBlank(originalStr) || StringUtils.isBlank(regex)) {\n            return StringUtils.EMPTY;\n        }\n\n        PatternMatcher matcher = new Perl5Matcher();\n        if (matcher.contains(originalStr, patterns.get(regex))) {\n            return StringUtils.trimToEmpty(matcher.getMatch().group(0));\n        }\n        return StringUtils.EMPTY;\n    }\n}\n"
  },
  {
    "path": "manager/biz/src/main/resources/spring/otter-manager-alarm.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<beans xmlns=\"http://www.springframework.org/schema/beans\"\n\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:tx=\"http://www.springframework.org/schema/tx\"\n\txmlns:aop=\"http://www.springframework.org/schema/aop\" xmlns:lang=\"http://www.springframework.org/schema/lang\"\n\txmlns:context=\"http://www.springframework.org/schema/context\"\n\txsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd\n           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd\n           http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-2.0.xsd\n           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd\n           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd\"\n\tdefault-autowire=\"byName\">\n\n\t<bean id=\"mailSender\" class=\"org.springframework.mail.javamail.JavaMailSenderImpl\">\n\t\t<property name=\"host\" value=\"${otter.manager.monitor.email.host}\" />\n\t\t<property name=\"username\" value=\"${otter.manager.monitor.email.username}\" />\n\t\t<property name=\"password\" value=\"${otter.manager.monitor.email.password}\" />\n\t\t<property name=\"defaultEncoding\" value=\"UTF-8\" />\n\t\t<property name=\"javaMailProperties\">\n\t\t\t<props>\n\t\t\t\t<prop key=\"mail.smtp.auth\">true</prop>\n\t\t\t\t<prop key=\"mail.smtp.timeout\">25000</prop>\n\t\t\t\t<prop key=\"mail.smtp.port\">${otter.manager.monitor.email.stmp.port:465}</prop>\n\t\t\t\t<prop key=\"mail.smtp.socketFactory.port\">${otter.manager.monitor.email.stmp.port:465}</prop>\n\t\t\t\t<prop key=\"mail.smtp.socketFactory.fallback\">false</prop>\n\t\t\t\t<prop key=\"mail.smtp.socketFactory.class\">javax.net.ssl.SSLSocketFactory</prop>\n\t\t\t</props>\n\t\t</property>\n\t</bean>\n\n\t<bean id=\"alarmService\" class=\"com.alibaba.otter.manager.biz.common.alarm.DefaultAlarmService\">\n\t\t<property name=\"mailSender\" ref=\"mailSender\" />\n\t\t<property name=\"username\" value=\"${otter.manager.monitor.email.username}\" />\n\t</bean>\n</beans>"
  },
  {
    "path": "manager/biz/src/main/resources/spring/otter-manager-arbitrate.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<beans xmlns=\"http://www.springframework.org/schema/beans\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n    xmlns:tx=\"http://www.springframework.org/schema/tx\" xmlns:aop=\"http://www.springframework.org/schema/aop\"\n    xmlns:lang=\"http://www.springframework.org/schema/lang\" xmlns:context=\"http://www.springframework.org/schema/context\"\n    xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd\n           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd\n           http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-2.0.xsd\n           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd\n           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd\"\n    default-autowire=\"byName\">\n\n    <bean id=\"arbitrateConfigImpl\" class=\"com.alibaba.otter.manager.biz.common.arbitrate.ArbitrateConfigImpl\" depends-on=\"zookeeperClient\" lazy-init=\"false\" />\n    \n    <bean id=\"deadNodeListener\" class=\"com.alibaba.otter.manager.biz.common.arbitrate.DeadNodeListener\" depends-on=\"arbitrateConfigImpl\" lazy-init=\"false\">\n    </bean>\n    \n</beans>"
  },
  {
    "path": "manager/biz/src/main/resources/spring/otter-manager-autokeeper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<beans xmlns=\"http://www.springframework.org/schema/beans\"\n\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:tx=\"http://www.springframework.org/schema/tx\"\n\txmlns:aop=\"http://www.springframework.org/schema/aop\" xmlns:lang=\"http://www.springframework.org/schema/lang\"\n\txmlns:context=\"http://www.springframework.org/schema/context\"\n\txsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd\n           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd\n           http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-2.0.xsd\n           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd\n           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd\"\n\tdefault-autowire=\"byName\">\n\n\t<bean id=\"autoKeeperCollector\" class=\"com.alibaba.otter.manager.biz.autokeeper.impl.AutoKeeperCollector\" >\n\t\t<property name=\"collectInterval\"><value>300</value></property>\n\t</bean>\n    \n    <bean id=\"autoKeeperData\" class=\"com.alibaba.otter.manager.biz.autokeeper.impl.AutoKeeperData\" />\n</beans>"
  },
  {
    "path": "manager/biz/src/main/resources/spring/otter-manager-dal.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<beans xmlns=\"http://www.springframework.org/schema/beans\"\n\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:tx=\"http://www.springframework.org/schema/tx\"\n\txmlns:aop=\"http://www.springframework.org/schema/aop\" xmlns:lang=\"http://www.springframework.org/schema/lang\"\n\txmlns:context=\"http://www.springframework.org/schema/context\"\n\txsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd\n           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd\n           http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-2.0.xsd\n           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd\n           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd\"\n\tdefault-autowire=\"byName\">\n\n\n\t<bean id=\"sqlMapClient\" class=\"org.springframework.orm.ibatis.SqlMapClientFactoryBean\">\n\t\t<property name=\"dataSource\" ref=\"dataSource\" />\n\t\t<property name=\"configLocation\">\n\t\t\t<value>classpath:sqlmap/sqlmap.xml</value>\n\t\t</property>\n\t</bean>\n\n\t<bean id=\"baseSeoDAO\" abstract=\"true\">\n\t\t<property name=\"sqlMapClient\" ref=\"SqlMapClient\" />\n\t</bean>\n\n\t<bean id=\"throughputDao\"\n\t\tclass=\"com.alibaba.otter.manager.biz.statistics.throughput.dal.ibatis.IbatisThroughputDAO\" />\n\t<bean id=\"tableStatDao\"\n\t\tclass=\"com.alibaba.otter.manager.biz.statistics.table.dal.ibatis.IbatisTableStatDAO\" />\n\t<bean id=\"tableHistoryStatDao\"\n\t\tclass=\"com.alibaba.otter.manager.biz.statistics.table.dal.ibatis.IbatisTableHistoryStatDAO\" />\n\t<bean id=\"delayStatDao\"\n\t\tclass=\"com.alibaba.otter.manager.biz.statistics.delay.dal.ibatis.IbatisDelayStatDAO\" />\n\t<bean id=\"userDao\"\n\t\tclass=\"com.alibaba.otter.manager.biz.user.dal.ibatis.IbatisUserDAO\" />\n\t<bean id=\"channelDao\"\n\t\tclass=\"com.alibaba.otter.manager.biz.config.channel.dal.ibatis.IbatisChannelDAO\" />\n\t<bean id=\"nodeDao\"\n\t\tclass=\"com.alibaba.otter.manager.biz.config.node.dal.ibatis.IbatisNodeDAO\" />\n\t<bean id=\"canalDao\"\n\t\tclass=\"com.alibaba.otter.manager.biz.config.canal.dal.ibatis.IbatisCanalDAO\" />\n\t<bean id=\"dataMatrixDao\"\n\t\tclass=\"com.alibaba.otter.manager.biz.config.datamatrix.dal.ibatis.IbatisDataMatrixDAO\" />\n\t<bean id=\"dataMediaDao\"\n\t\tclass=\"com.alibaba.otter.manager.biz.config.datamedia.dal.ibatis.IbatisDataMediaDAO\" />\n\t<bean id=\"dataMediaSourceDao\"\n\t\tclass=\"com.alibaba.otter.manager.biz.config.datamediasource.dal.ibatis.IbatisDataMediaSourceDAO\" />\n\t<bean id=\"dataMediaPairDao\"\n\t\tclass=\"com.alibaba.otter.manager.biz.config.datamediapair.dal.ibatis.IbatisDataMediaPairDAO\" />\n\t<bean id=\"pipelineDao\"\n\t\tclass=\"com.alibaba.otter.manager.biz.config.pipeline.dal.ibatis.IbatisPipelineDAO\" />\n\t<bean id=\"pipelineNodeRelationDao\"\n\t\tclass=\"com.alibaba.otter.manager.biz.config.pipeline.dal.ibatis.IbatisPipelineNodeRelationDAO\" />\n\t<bean id=\"dataColumnPairDao\"\n\t\tclass=\"com.alibaba.otter.manager.biz.config.datacolumnpair.dal.ibatis.IbatisDataColumnPairDAO\" />\n\t<bean id=\"dataColumnPairGroupDao\"\n\t\tclass=\"com.alibaba.otter.manager.biz.config.datacolumnpair.dal.ibatis.IbatisDataColumnPairGroupDAO\" />\n\t<bean id=\"systemParameterDao\"\n\t\tclass=\"com.alibaba.otter.manager.biz.config.parameter.dal.ibatis.IbatisSystemParameterDAO\" />\n\t<bean id=\"logRecordDao\"\n\t\tclass=\" com.alibaba.otter.manager.biz.config.record.dal.ibatis.IbatisLogRecordDAO\" />\n\t<bean id=\"alarmRuleDao\"\n\t\tclass=\"com.alibaba.otter.manager.biz.config.alarm.dal.ibatis.IbatisAlarmRuleDAO\" />\n\t<bean id=\"autoKeeperClusterDao\"\n\t\tclass=\"com.alibaba.otter.manager.biz.config.autokeeper.dal.ibatis.IbatisAutoKeeperClusterDAO\" />\n</beans>\n"
  },
  {
    "path": "manager/biz/src/main/resources/spring/otter-manager-datasource.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE beans PUBLIC \"-//SPRING//DTD BEAN//EN\" \"http://www.springframework.org/dtd/spring-beans-2.0.dtd\">\n<beans default-autowire=\"byName\">\n\t<bean id=\"dataSource\" class=\"org.apache.commons.dbcp.BasicDataSource\" destroy-method=\"close\">\n        <property name=\"driverClassName\" value=\"${otter.database.driver.class.name}\" />\n        <property name=\"url\" value=\"${otter.database.driver.url}\" />\n\t\t<property name=\"username\" value=\"${otter.database.driver.username}\" />\n\t\t<property name=\"password\" value=\"${otter.database.driver.password}\" />\n        <property name=\"maxActive\"><value>20</value></property>\n        <property name=\"initialSize\"><value>1</value></property>\n        <property name=\"maxWait\"><value>60000</value></property>\n        <property name=\"maxIdle\"><value>20</value></property> <!-- 可以和maxActive保持一致 -->\n        <property name=\"minIdle\"><value>1</value></property>  <!-- 可以和initialSize保持一致 -->\n        <property name=\"removeAbandoned\"><value>true</value></property>\n        <property name=\"removeAbandonedTimeout\"><value>180</value></property>\n        <property name=\"timeBetweenEvictionRunsMillis\"><value>60000</value></property>\n        <property name=\"minEvictableIdleTimeMillis\"><value>180000</value></property>\n        <property name=\"connectionProperties\"><value>useUnicode=true;characterEncoding=utf-8</value></property>\n        <!-- sql检查配置,在idle空闲线程检查时,检查链接有效性，丢弃无效的链接,实现自动重连-->\n        <!-- 注意使用cobar时,因为走了F5进行负载,F5默认5分钟会关闭空闲链接,所以需要通过心跳SQL保证数据库链接不被异常关闭 -->\n        <property name=\"testWhileIdle\"><value>true</value></property>\n        <property name=\"testOnBorrow\"><value>false</value></property>\n        <property name=\"testOnReturn\"><value>false</value></property>\n        <property name=\"validationQuery\"><value>SELECT @@version</value></property>\n        <property name=\"numTestsPerEvictionRun\"><value>-1</value></property>\n\t</bean>\n\t\n\t<bean id=\"transactionManager\" class=\"org.springframework.jdbc.datasource.DataSourceTransactionManager\">\n        <property name=\"dataSource\" ref=\"dataSource\"/>\n\t</bean>\n\t\n\t<bean id=\"transactionTemplate\" class=\"org.springframework.transaction.support.TransactionTemplate\"> \n\t\t<property name=\"transactionManager\" ref=\"transactionManager\" />\n\t</bean>\n\t\n\t<bean id=\"dataSourceCreator\" class=\"com.alibaba.otter.manager.biz.common.DataSourceCreator\" >\n\t\t<property name=\"dataSourceHandlers\">\n\t\t\t<list>\n\t\t\t\t<ref bean=\"mediaPushDataSourceHandler\" />\n\t\t\t</list>\n\t\t</property>\n\t</bean>\n\t\n\t<bean id=\"mediaPushDataSourceHandler\" class=\"com.alibaba.otter.common.push.datasource.media.MediaPushDataSourceHandler\" scope=\"singleton\" />\n</beans>\n"
  },
  {
    "path": "manager/biz/src/main/resources/spring/otter-manager-monitor.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<beans xmlns=\"http://www.springframework.org/schema/beans\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n    xmlns:tx=\"http://www.springframework.org/schema/tx\" xmlns:aop=\"http://www.springframework.org/schema/aop\"\n    xmlns:lang=\"http://www.springframework.org/schema/lang\" xmlns:context=\"http://www.springframework.org/schema/context\"\n    xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd\n           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd\n           http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-2.0.xsd\n           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd\n           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd\"\n    default-autowire=\"byName\">\n     \n     <bean id=\"globalMonitor\" class=\"com.alibaba.otter.manager.biz.monitor.impl.GlobalMonitor\">\n     \t<property name=\"nThreads\" value=\"8\" />\n     \t<property name=\"needConcurrent\" value=\"true\" />\n     </bean>\n     \n     <bean id=\"selfMonitor\" class=\"com.alibaba.otter.manager.biz.monitor.impl.SelfMonitor\">\n     \t<property name=\"enable\" value=\"${otter.manager.monitor.self.enable:true}\" />\n     \t<property name=\"interval\" value=\"${otter.manager.monitor.self.interval:120}\" />\n     \t<property name=\"monitor\" ref=\"globalMonitor\" />\n     </bean>\n     \n     <bean id=\"pipelineMonitor\" class=\"com.alibaba.otter.manager.biz.monitor.impl.PipelineMonitor\" />\n     \n     <bean id=\"delayStatRuleMonitor\" class=\"com.alibaba.otter.manager.biz.monitor.impl.DelayStatRuleMonitor\" />\n     <bean id=\"exceptionRuleMonitor\" class=\"com.alibaba.otter.manager.biz.monitor.impl.ExceptionRuleMonitor\" />\n     <bean id=\"pipelineTimeoutRuleMonitor\" class=\"com.alibaba.otter.manager.biz.monitor.impl.PipelineTimeoutRuleMonitor\" />\n     <bean id=\"processTimeoutRuleMonitor\" class=\"com.alibaba.otter.manager.biz.monitor.impl.ProcessTimeoutRuleMonitor\" />\n     <bean id=\"positionTimeoutRuleMonitor\" class=\"com.alibaba.otter.manager.biz.monitor.impl.PositionTimeoutRuleMonitor\" />\n     \n     <bean id=\"alarmController\" class=\"com.alibaba.otter.manager.biz.monitor.impl.DefaultAlarmController\" />\n     <bean id=\"restartAlarmRecovery\"  class=\"com.alibaba.otter.manager.biz.monitor.impl.RestartAlarmRecovery\" />\n</beans>"
  },
  {
    "path": "manager/biz/src/main/resources/spring/otter-manager-remote.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<beans xmlns=\"http://www.springframework.org/schema/beans\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\r\n    xmlns:tx=\"http://www.springframework.org/schema/tx\" xmlns:aop=\"http://www.springframework.org/schema/aop\"\r\n    xmlns:lang=\"http://www.springframework.org/schema/lang\" xmlns:context=\"http://www.springframework.org/schema/context\"\r\n    xmlns:dwr=\"http://www.directwebremoting.org/schema/spring-dwr\"\r\n    xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd\r\n           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd\r\n           http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-2.0.xsd\r\n           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd\r\n           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd\r\n           http://www.directwebremoting.org/schema/spring-dwr http://www.directwebremoting.org/schema/spring-dwr-2.0.xsd\"\r\n    default-autowire=\"byName\">\r\n\r\n\t<!--  communication tool -->\r\n\t<!-- \r\n\t<bean id=\"endpoint\" class=\"com.alibaba.otter.shared.communication.core.impl.rmi.RmiCommunicationEndpoint\" scope=\"singleton\" init-method=\"initial\" destroy-method=\"destory\">\r\n\t\t<property name=\"port\" value=\"${otter.communication.manager.port}\" />\r\n\t</bean>\r\n\t\r\n\t<bean id=\"poolCommunicationClient\" class=\"com.alibaba.otter.shared.communication.core.impl.DefaultCommunicationClientImpl\" init-method=\"initial\" destroy-method=\"destory\">\r\n\t\t<property name=\"factory\">\r\n\t\t\t<bean class=\"com.alibaba.otter.shared.communication.core.impl.connection.CommunicationConnectionPoolFactory\" init-method=\"initial\" destroy-method=\"destory\" >\r\n\t\t\t\t<property name=\"factory\">\r\n\t\t\t\t\t<bean class=\"com.alibaba.otter.shared.communication.core.impl.rmi.RmiCommunicationConnectionFactory\" />\r\n\t\t\t\t</property>\r\n\t\t\t\t<property name=\"maxActive\" value=\"${otter.communication.pool.size}\" />\r\n\t\t\t</bean>\r\n\t\t</property>\r\n\t</bean>\r\n\t -->\r\n\t<bean id=\"endpoint\" class=\"com.alibaba.otter.shared.communication.core.impl.dubbo.DubboCommunicationEndpoint\" init-method=\"initial\" destroy-method=\"destory\">\r\n\t\t<property name=\"port\" value=\"${otter.communication.manager.port:1099}\" />\r\n\t\t<property name=\"payload\" value=\"${otter.communication.payload:8388608}\" />\r\n\t</bean>\r\n\t<bean id=\"communicationClient\" class=\"com.alibaba.otter.shared.communication.core.impl.DefaultCommunicationClientImpl\" init-method=\"initial\" destroy-method=\"destory\">\r\n\t\t<property name=\"poolSize\" value=\"${otter.communication.pool.size:10}\" />\r\n\t\t<property name=\"factory\">\r\n\t\t\t<bean class=\"com.alibaba.otter.shared.communication.core.impl.dubbo.DubboCommunicationConnectionFactory\">\r\n\t\t\t\t<property name=\"payload\" value=\"${otter.communication.payload:8388608}\" />\r\n\t\t\t</bean>\r\n\t\t</property>\r\n\t</bean>\r\n\r\n\t<!-- 异常处理拦截器 -->\r\n\t<bean id=\"remoteExceptionLoggerInterceptor\" class=\"com.alibaba.otter.manager.biz.remote.interceptor.RemoteExceptionLoggerInterceptor\" scope=\"singleton\"/>\r\n\t<bean id=\"baseRemoteService\" class=\"org.springframework.aop.framework.ProxyFactoryBean\" abstract=\"true\">\r\n\t\t<property name=\"proxyTargetClass\" value=\"true\"/>\r\n\t\t<!-- 强制使用cglib代理 -->\r\n\t\t<property name=\"interceptorNames\">\r\n\t\t\t<list>\r\n\t\t\t\t<value>remoteExceptionLoggerInterceptor</value>\r\n\t\t\t</list>\r\n\t\t</property>\r\n\t</bean>\r\n\t\r\n\t<bean id=\"configRemoteService\" parent=\"baseRemoteService\" scope=\"singleton\">\r\n\t\t<property name=\"targetName\" value=\"configRemoteServiceTarget\"/>\r\n\t</bean>\r\n\t<bean id=\"configRemoteServiceTarget\" class=\"com.alibaba.otter.manager.biz.remote.impl.ConfigRemoteServiceImpl\"  scope=\"singleton\">\r\n\t\t<property name=\"communicationClient\" ref=\"communicationClient\" />\r\n\t</bean>\r\n\t\r\n\t<bean id=\"statsRemoteService\" parent=\"baseRemoteService\" scope=\"singleton\">\r\n\t\t<property name=\"targetName\" value=\"statsRemoteServiceTarget\"/>\r\n\t</bean>\r\n\t<bean id=\"statsRemoteServiceTarget\" class=\"com.alibaba.otter.manager.biz.remote.impl.StatsRemoteServiceImpl\"  scope=\"singleton\">\r\n\t</bean>\r\n\t\r\n\t<bean id=\"arbitrateRemoteService\" parent=\"baseRemoteService\" scope=\"singleton\">\r\n\t\t<property name=\"targetName\" value=\"arbitrateRemoteServiceTarget\"/>\r\n\t</bean>\r\n\t<bean id=\"arbitrateRemoteServiceTarget\" class=\"com.alibaba.otter.manager.biz.remote.impl.ArbitrateRemoteServiceImpl\"  scope=\"singleton\">\r\n\t</bean>\r\n\t\r\n\t<bean id=\"canalRemoteService\" parent=\"baseRemoteService\" scope=\"singleton\">\r\n\t\t<property name=\"targetName\" value=\"canalRemoteServiceTarget\"/>\r\n\t</bean>\r\n\t<bean id=\"canalRemoteServiceTarget\" class=\"com.alibaba.otter.manager.biz.remote.impl.CanalRemoteServiceImpl\"  scope=\"singleton\">\r\n\t\t<property name=\"tsdbJdbcUrl\" value=\"${otter.database.driver.url}\" />\r\n\t\t<property name=\"tsdbJdbcUserName\" value=\"${otter.database.driver.username}\" />\r\n\t\t<property name=\"tsdbJdbcPassword\" value=\"${otter.database.driver.password}\" />\r\n\t</bean>\r\n\t\r\n\t<!-- MBEAN -->\r\n\t<bean id=\"nodeRemoteService\" class=\"com.alibaba.otter.manager.biz.remote.impl.NodeMBeanServiceImpl\">\r\n\t\t<dwr:remote javascript=\"Node\">\r\n\t\t\t<dwr:include method=\"isSelectRunning\"/>\r\n\t\t\t<dwr:include method=\"isExtractRunning\"/>\r\n\t\t\t<dwr:include method=\"isTransformRunning\"/>\r\n\t\t\t<dwr:include method=\"isLoadRunning\"/>\r\n\t\t\t<dwr:include method=\"selectStageAggregation\"/>\r\n\t\t\t<dwr:include method=\"extractStageAggregation\"/>\r\n\t\t\t<dwr:include method=\"transformStageAggregation\"/>\r\n\t\t\t<dwr:include method=\"loadStageAggregation\"/>\r\n\t\t\t<dwr:include method=\"selectPendingProcess\"/>\r\n\t\t\t<dwr:include method=\"extractPendingProcess\"/>\r\n\t\t\t<dwr:include method=\"transformPendingProcess\"/>\r\n\t\t\t<dwr:include method=\"loadPendingProcess\"/>\r\n\t\t</dwr:remote>\r\n\t</bean>\r\n</beans>\r\n"
  },
  {
    "path": "manager/biz/src/main/resources/spring/otter-manager-service.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<beans xmlns=\"http://www.springframework.org/schema/beans\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n    xmlns:tx=\"http://www.springframework.org/schema/tx\" xmlns:aop=\"http://www.springframework.org/schema/aop\"\n    xmlns:lang=\"http://www.springframework.org/schema/lang\" xmlns:context=\"http://www.springframework.org/schema/context\"\n    xmlns:dwr=\"http://www.directwebremoting.org/schema/spring-dwr\"\n    xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd\n           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd\n           http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-2.0.xsd\n           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd\n           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd\n           http://www.directwebremoting.org/schema/spring-dwr http://www.directwebremoting.org/schema/spring-dwr-2.0.xsd\"\n    default-autowire=\"byName\">\n    \n\t<bean id=\"channelService\" class=\"com.alibaba.otter.manager.biz.config.channel.impl.ChannelServiceImpl\" depends-on=\"arbitrateConfigImpl\">\n\t\t<property name=\"pipelineService\">\n\t\t\t<ref bean=\"pipelineService\" />\n\t\t</property>\n\t\t<property name=\"channelDao\">\n\t\t\t<ref bean=\"channelDao\" />\n\t\t</property>\n\t\t<property name=\"transactionTemplate\">\n\t\t\t<ref bean=\"transactionTemplate\" />\n\t\t</property>\n\t</bean>\n\n\t<bean id=\"pipelineService\" class=\"com.alibaba.otter.manager.biz.config.pipeline.impl.PipelineServiceImpl\">\n\t\t<property name=\"pipelineDao\">\n\t\t\t<ref bean=\"pipelineDao\" />\n\t\t</property>\n\t\t<property name=\"pipelineNodeRelationDao\">\n\t\t\t<ref bean=\"pipelineNodeRelationDao\" />\n\t\t</property>\n\t\t<property name=\"dataMediaPairService\">\n\t\t\t<ref bean=\"dataMediaPairService\" />\n\t\t</property>\n\t\t<property name=\"transactionTemplate\">\n\t\t\t<ref bean=\"transactionTemplate\" />\n\t\t</property>\n\t</bean>\n\n\t<bean id=\"dataMediaService\" class=\"com.alibaba.otter.manager.biz.config.datamedia.impl.DataMediaServiceImpl\">\n\t\t<property name=\"dataMediaDao\">\n\t\t\t<ref bean=\"dataMediaDao\" />\n\t\t</property>\n\t\t<property name=\"dataMediaSourceService\">\n\t\t\t<ref bean=\"dataMediaSourceService\" />\n\t\t</property>\n\t</bean>\n\n\t<bean id=\"dataMediaPairService\" class=\"com.alibaba.otter.manager.biz.config.datamediapair.impl.DataMediaPairServiceImpl\">\n\t\t<property name=\"dataMediaPairDao\">\n\t\t\t<ref bean=\"dataMediaPairDao\" />\n\t\t</property>\n\t\t<property name=\"dataMediaService\">\n\t\t\t<ref bean=\"dataMediaService\" />\n\t\t</property>\n\t\t<property name=\"dataColumnPairService\">\n\t\t\t<ref bean=\"dataColumnPairService\" />\n\t\t</property>\n\t\t<property name=\"dataColumnPairGroupService\">\n\t\t\t<ref bean=\"dataColumnPairGroupService\" />\n\t\t</property>\n\t</bean>\n\n\t<bean id=\"dataMediaSourceService\" class=\"com.alibaba.otter.manager.biz.config.datamediasource.impl.DataMediaSourceServiceImpl\">\n\t\t<property name=\"dataMediaSourceDao\">\n\t\t\t<ref bean=\"dataMediaSourceDao\" />\n\t\t</property>\n\t</bean>\n\t\n\t<bean id=\"nodeService\" class=\"com.alibaba.otter.manager.biz.config.node.impl.NodeServiceImpl\">\n\t\t<property name=\"nodeDao\">\n\t\t\t<ref bean=\"nodeDao\" />\n\t\t</property>\n\t\t<property name=\"transactionTemplate\">\n\t\t\t<ref bean=\"transactionTemplate\" />\n\t\t</property>\n\t</bean>\n\t\n\t<bean id=\"canalService\" class=\"com.alibaba.otter.manager.biz.config.canal.impl.CanalServiceImpl\">\n\t\t<property name=\"canalDao\">\n\t\t\t<ref bean=\"canalDao\" />\n\t\t</property>\n\t\t<property name=\"transactionTemplate\">\n\t\t\t<ref bean=\"transactionTemplate\" />\n\t\t</property>\n\t</bean>\n\t\n\t<bean id=\"dataMatrixService\" class=\"com.alibaba.otter.manager.biz.config.datamatrix.impl.DataMatrixServiceImpl\">\n\t\t<property name=\"dataMatrixDao\">\n\t\t\t<ref bean=\"dataMatrixDao\" />\n\t\t</property>\n\t\t<property name=\"transactionTemplate\">\n\t\t\t<ref bean=\"transactionTemplate\" />\n\t\t</property>\n\t</bean>\n\t\n\t<bean id=\"userService\" class=\"com.alibaba.otter.manager.biz.user.impl.UserServiceImpl\">\n\t\t<property name=\"userDao\">\n\t\t\t<ref bean=\"userDao\" />\n\t\t</property>\n\t</bean>\n\t\n\t<bean id=\"delayStatService\"\n\t\tclass=\"com.alibaba.otter.manager.biz.statistics.delay.impl.DelayStatServiceImpl\">\n\t\t<property name=\"delayStatDao\">\n\t\t\t<ref bean=\"delayStatDao\" />\n\t\t</property>\n\t</bean>\n\t\n\t<bean id=\"processStatService\"\n\t\tclass=\"com.alibaba.otter.manager.biz.statistics.stage.impl.ProcessStatServiceImpl\">\n\t</bean>\n\t\n\t<bean id=\"tableStatService\"\n\t\tclass=\"com.alibaba.otter.manager.biz.statistics.table.impl.TableStatServiceImpl\">\n\t\t<property name=\"tableStatDao\">\n\t\t\t<ref bean=\"tableStatDao\" />\n\t\t</property>\n\t</bean>\n\t\n \t<bean id=\"throughputStatService\"\n\t\tclass=\"com.alibaba.otter.manager.biz.statistics.throughput.impl.ThroughputStatServiceImpl\">\n\t\t<property name=\"throughputDao\">\n\t\t\t<ref bean=\"throughputDao\" />\n\t\t</property>\n\t</bean>\n\t\n\t<bean id=\"dataColumnPairService\"\n\t\tclass=\"com.alibaba.otter.manager.biz.config.datacolumnpair.impl.DataColumnPairServiceImpl\">\n\t\t<property name=\"dataColumnPairDao\">\n\t\t\t<ref bean=\"dataColumnPairDao\" />\n\t\t</property>\n\t</bean>\n\t\n\t<bean id=\"dataColumnPairGroupService\"\n\t\tclass=\"com.alibaba.otter.manager.biz.config.datacolumnpair.impl.DataColumnPairGroupServiceImpl\">\n\t\t<property name=\"dataColumnPairGroupDao\">\n\t\t\t<ref bean=\"dataColumnPairGroupDao\" />\n\t\t</property>\n\t\t\n\t\t<property name=\"dataColumnPairService\">\n\t\t\t<ref bean=\"dataColumnPairService\" />\n\t\t</property>\n\t</bean>\n\t\n\t<bean id=\"logRecordService\"\n\t\tclass=\"com.alibaba.otter.manager.biz.config.record.impl.LogRecordServiceImpl\">\n\t\t<property name=\"channelService\">\n\t\t\t<ref bean=\"channelService\" />\n\t\t</property>\n\t\t\n\t\t<property name=\"logRecordDao\">\n\t\t\t<ref bean=\"logRecordDao\" />\n\t\t</property>\n\t</bean>\n\t\n\t<bean id=\"alarmRuleService\"\n\t\tclass=\"com.alibaba.otter.manager.biz.config.alarm.impl.AlarmRuleServiceImpl\">\n\t\t\n\t\t<property name=\"alarmRuleDao\">\n\t\t\t<ref bean=\"alarmRuleDao\" />\n\t\t</property>\n\t</bean>\n\t\n\t<bean id=\"autoKeeperClusterService\"\n\t\tclass=\"com.alibaba.otter.manager.biz.config.autokeeper.impl.AutoKeeperClusterServiceImpl\">\n\t\t<property name=\"autoKeeperClusterDao\">\n\t\t\t<ref bean=\"autoKeeperClusterDao\" />\n\t\t</property>\n\t</bean>\n\t\n\t<bean id=\"dataSourceChecker\" class=\"com.alibaba.otter.manager.biz.utils.DataSourceChecker\">\n\t\t<property name=\"dataMediaSourceService\">\n\t\t\t<ref bean=\"dataMediaSourceService\" />\n\t\t</property>\n\t\t<property name=\"dataSourceCreator\">\n\t\t\t<ref bean=\"dataSourceCreator\" />\n\t\t</property>\n\t\t<dwr:remote javascript=\"Hello\">\n\t\t\t<dwr:include method=\"check\" />\n\t\t\t<dwr:include method=\"checkMap\" />\n\t\t\t<dwr:include method=\"checkNamespaceTables\" />\n\t\t</dwr:remote>\n\t</bean>\n\t\n\t<bean id=\"systemParameterService\"\n\t\tclass=\"com.alibaba.otter.manager.biz.config.parameter.impl.SystemParameterServiceImpl\">\n\t\t<property name=\"systemParameterDao\">\n\t\t\t<ref bean=\"systemParameterDao\" />\n\t\t</property>\n\t</bean>\t\n\t<bean id=\"autoKeeperStatService\"\n\t\tclass=\"com.alibaba.otter.manager.biz.autokeeper.impl.AutoKeeperStatServiceImpl\">\n\t</bean>\t\n</beans>"
  },
  {
    "path": "manager/biz/src/main/resources/sqlmap/sqlmap-mapping-alarmRule.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>  \n    <!DOCTYPE sqlMap PUBLIC \"-//ibatis.apache.org//DTD SQL Map 2.0//EN\"  \n       \"http://ibatis.apache.org/dtd/sql-map-2.dtd\">\n\n<sqlMap namespace=\"com.alibaba.otter.manager.biz.config.alarm.dal.dataobject.AlarmRuleDO\">\n\t<typeAlias alias=\"ALARM_RULE\"\n\t\ttype=\"com.alibaba.otter.manager.biz.config.alarm.dal.dataobject.AlarmRuleDO\" />\n\n\t<resultMap id=\"alarmRuleResult\" class=\"ALARM_RULE\">\n\t\t<result property=\"id\" column=\"ID\" />\n\t\t<result property=\"pipelineId\" column=\"PIPELINE_ID\" />\n\t\t<result property=\"monitorName\" column=\"MONITOR_NAME\" />\n\t\t<result property=\"status\" column=\"STATUS\" />\n\t\t<result property=\"receiverKey\" column=\"RECEIVER_KEY\" />\n\t\t<result property=\"matchValue\" column=\"MATCH_VALUE\" />\n\t\t<result property=\"alarmRuleParameter\" column=\"PARAMETERS\" />\n\t\t<result property=\"description\" column=\"DESCRIPTION\" />\n\t\t<result property=\"gmtCreate\" column=\"GMT_CREATE\" />\n\t\t<result property=\"gmtModified\" column=\"GMT_MODIFIED\" />\n\t</resultMap>\n\t\n\t<!-- all columns -->\n\t<sql id=\"allAlarmRuleColumns\">ID,PIPELINE_ID,MONITOR_NAME,STATUS,RECEIVER_KEY,MATCH_VALUE,PARAMETERS,DESCRIPTION,GMT_CREATE,GMT_MODIFIED</sql>\n\n\t<insert id=\"insertAlarmRule\" parameterClass=\"ALARM_RULE\">\n\t\tinsert into ALARM_RULE\n\t\t(PIPELINE_ID,MONITOR_NAME,STATUS,RECEIVER_KEY,MATCH_VALUE,PARAMETERS,DESCRIPTION,GMT_CREATE,GMT_MODIFIED)\n\t\tVALUES \n\t\t(#pipelineId#,#monitorName#,#status#,#receiverKey#,#matchValue#,#alarmRuleParameter#,#description#,now(),now())\n\t\t<selectKey keyProperty=\"id\" resultClass=\"long\">\n\t\t\tselect\n\t\t\tlast_insert_id()\n\t\t</selectKey>\n\t</insert>\n\t\n\t<update id=\"updateAlarmRule\" parameterClass=\"ALARM_RULE\"><![CDATA[\n\t\tupdate ALARM_RULE\n\t\tset\n\t\tMONITOR_NAME=#monitorName#,\n\t\tRECEIVER_KEY=#receiverKey#,\n\t\tMATCH_VALUE=#matchValue#,\n\t\tPARAMETERS=#alarmRuleParameter#,\n\t\tSTATUS=#status#,\n\t\tDESCRIPTION=#description#,\n\t\tGMT_MODIFIED=now()\n\t\tWHERE ID = #id#\n\t]]></update>\n\t\n\t<delete id=\"deleteAlarmRuleById\" parameterClass=\"long\"><![CDATA[\n    \tdelete from ALARM_RULE where ID = #value#\n    ]]></delete>\n    \n\t<select id=\"findByRuleId\" resultMap=\"alarmRuleResult\"\n\t\tparameterClass=\"long\">\n\t\tselect  \n\t\t<include refid=\"allAlarmRuleColumns\" />\n\t\tfrom ALARM_RULE where ID = #alarmRuleId#\n\t</select>\n\n\t<select id=\"listAlarmByPipelineId\" resultMap=\"alarmRuleResult\"\n\t\tparameterClass=\"long\">\n\t\tselect  \n\t\t<include refid=\"allAlarmRuleColumns\" />\n\t\tfrom ALARM_RULE where PIPELINE_ID = #pipelineId#\n\t</select>\n\t\n\t<select id=\"listAllAlarmRule\" resultMap=\"alarmRuleResult\">\n\t\tselect  \n\t\t<include refid=\"allAlarmRuleColumns\" />\n\t\tfrom ALARM_RULE where PIPELINE_ID = #pipelineId#\n\t</select>\n\t\n\t<select id=\"listAllAlarmOrderByPipeline\" resultMap=\"alarmRuleResult\">\n\t\tselect  \n\t\t<include refid=\"allAlarmRuleColumns\" />\n\t\tfrom ALARM_RULE order by PIPELINE_ID asc \n\t\t<dynamic>\n\t\t\t<isNotEmpty property=\"offset\" >\n\t\t\t\t<isNotEmpty property=\"length\">\n\t\t\t\t\tlimit #offset#, #length#\n\t\t\t\t</isNotEmpty>\n\t\t\t</isNotEmpty>\n\t\t</dynamic>\n\t</select>\n\t\n\t<select id=\"getAlarmRuleCount\" resultClass=\"Integer\">\n\t\tselect count(*) from ALARM_RULE \n\t</select>\n\t\n\t<select id=\"listAlarmByStatus\" resultMap=\"alarmRuleResult\">\n\t\tselect  \n\t\t<include refid=\"allAlarmRuleColumns\" />\n\t\tfrom ALARM_RULE where STATUS = #status#\n\t</select>\n\t\n</sqlMap>  "
  },
  {
    "path": "manager/biz/src/main/resources/sqlmap/sqlmap-mapping-autokeeper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>  \n    <!DOCTYPE sqlMap PUBLIC \"-//ibatis.apache.org//DTD SQL Map 2.0//EN\"  \n       \"http://ibatis.apache.org/dtd/sql-map-2.dtd\">\n\n<sqlMap namespace=\"com.alibaba.otter.manager.biz.config.autokeeper.dal.dataobject.AutoKeeperClusterDO\">\n\t<typeAlias alias=\"autoKeeperCluster\"\n\t\ttype=\"com.alibaba.otter.manager.biz.config.autokeeper.dal.dataobject.AutoKeeperClusterDO\" />\n\n\t<resultMap id=\"autoKeeperClusterResult\" class=\"autoKeeperCluster\">\n\t\t<result property=\"id\" column=\"ID\" />\n\t\t<result property=\"clusterName\" column=\"CLUSTER_NAME\" />\n\t\t<result property=\"serverList\" column=\"SERVER_LIST\" />\n\t\t<result property=\"description\" column=\"DESCRIPTION\" />\n\t\t<result property=\"gmtCreate\" column=\"GMT_CREATE\" />\n\t\t<result property=\"gmtModified\" column=\"GMT_MODIFIED\" />\n\t</resultMap>\n\t\n\n\t<!-- all columns -->\n\t<sql id=\"allAutoKeeperClusterColumns\">ID,CLUSTER_NAME,SERVER_LIST,DESCRIPTION,GMT_CREATE,GMT_MODIFIED</sql>\n\t\n\t\n\t<select id=\"findAutoKeeperClusterById\" resultMap=\"autoKeeperClusterResult\" parameterClass=\"long\">\n\t\tselect\n\t\t<include refid=\"allAutoKeeperClusterColumns\" />\n\t\tfrom AUTOKEEPER_CLUSTER where ID = #value#\n\t</select>\n\t\n\t<select id=\"listAutoKeeperClusters\" resultMap=\"autoKeeperClusterResult\">\n\t\tselect\n\t\t<include refid=\"allAutoKeeperClusterColumns\" />\n\t\tfrom AUTOKEEPER_CLUSTER \n\t\tORDER BY ID ASC \n\t\t<dynamic>\n\t\t\t<isNotEmpty property=\"offset\">\n\t\t\t\t<isNotEmpty property=\"length\">\n\t\t\t\t\tlimit #offset#, #length#\n\t\t\t\t</isNotEmpty>\n\t\t\t</isNotEmpty>\n\t\t</dynamic>\n\t</select>\n\t\n\t\n\t<insert id=\"insertAutoKeeperCluster\" parameterClass=\"autoKeeperCluster\">\n\t\tinsert into AUTOKEEPER_CLUSTER\n\t\t(CLUSTER_NAME,SERVER_LIST,DESCRIPTION,GMT_CREATE,GMT_MODIFIED)\n\t\tvalues (#clusterName#,#serverList#,#description#,now(),now())\n\t</insert>\n\t\n\n\t<update id=\"updateAutoKeeperCluster\" parameterClass=\"autoKeeperCluster\">\n\t\tupdate AUTOKEEPER_CLUSTER\n\t\tset\n\t\tCLUSTER_NAME=#clusterName#,\n\t\tSERVER_LIST=#serverList#,\n\t\tDESCRIPTION=#description#,\n\t\tGMT_MODIFIED=now()\n\t\tWHERE ID = #id#  \n\t</update>\n \n\t<delete id=\"deleteAutoKeeperClusterById\" parameterClass=\"long\">\n    \tdelete from AUTOKEEPER_CLUSTER where ID = #value#\n    </delete>\n    \n    <select id=\"getAutoKeeperClusterCount\" resultClass=\"Integer\">\n\t\tselect count(*) from AUTOKEEPER_CLUSTER \n\t\t<dynamic prepend=\"where\">\n\t\t\t<isNotEmpty property=\"searchKey\">\n\t\t\t\tNAME like concat('%',replace(#searchKey#,'_','\\_'),'%')\n\t\t\t\tor ID like concat('%',replace(#searchKey#,'_','\\_'),'%')\n\t\t\t</isNotEmpty>\n\t\t</dynamic>\n\t</select>\n\n</sqlMap>  "
  },
  {
    "path": "manager/biz/src/main/resources/sqlmap/sqlmap-mapping-canal.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>  \n    <!DOCTYPE sqlMap PUBLIC \"-//ibatis.apache.org//DTD SQL Map 2.0//EN\"  \n       \"http://ibatis.apache.org/dtd/sql-map-2.dtd\">\n\n<sqlMap\n\tnamespace=\"com.alibaba.otter.manager.biz.config.canal.dal.dataobject.CanalDO\">\n\t<typeAlias alias=\"canal\"\n\t\ttype=\"com.alibaba.otter.manager.biz.config.canal.dal.dataobject.CanalDO\" />\n\n\t<resultMap id=\"canalResult\" class=\"canal\">\n\t\t<result property=\"id\" column=\"ID\" />\n\t\t<result property=\"name\" column=\"NAME\" />\n\t\t<result property=\"description\" column=\"DESCRIPTION\" />\n\t\t<result property=\"parameters\" column=\"PARAMETERS\" />\n\t\t<result property=\"gmtCreate\" column=\"GMT_CREATE\" />\n\t\t<result property=\"gmtModified\" column=\"GMT_MODIFIED\" />\n\t</resultMap>\n\n\t<!-- all canal columns -->\n\t<sql id=\"allCanalColumns\">ID,NAME,DESCRIPTION,PARAMETERS,GMT_CREATE,GMT_MODIFIED\n\t</sql>\n\n\t<update id=\"updateCanal\" parameterClass=\"canal\"><![CDATA[\n\t\tupdate CANAL\n\t\tset\n\t\tDESCRIPTION=#description#,\n\t\tPARAMETERS=#parameters#,\n\t\tGMT_MODIFIED=now()\n\t\tWHERE ID = #id#\n\t]]></update>\n\n\t<select id=\"checkCanalUnique\" resultClass=\"Integer\"\n\t\tparameterClass=\"canal\">\n\t\tselect count(*) from CANAL\n\t\twhere CANAL.ID != #id#\n\t\tand\n\t\tCANAL.NAME = #name#\n\t</select>\n\n\t<insert id=\"insertCanal\" parameterClass=\"canal\">\n\t\tinsert into CANAL\n\t\t(NAME, DESCRIPTION, PARAMETERS,GMT_CREATE,GMT_MODIFIED)\n\t\tSELECT\n\t\t#name#,#description#,#parameters#,now(),now()\n\t\tfrom dual\n\t\tWHERE not exists (select * from CANAL\n\t\twhere CANAL.NAME = #name# );\n\t\t<selectKey keyProperty=\"id\" resultClass=\"long\">\n\t\t\tselect\n\t\t\tlast_insert_id()\n\t\t</selectKey>\n\t</insert>\n\n\t<delete id=\"deleteCanalById\" parameterClass=\"long\"><![CDATA[\n    \tdelete from CANAL where ID = #value#\n    ]]></delete>\n    \n    \n    <select id=\"findCanalByName\" parameterClass=\"string\" resultClass=\"canal\">\n\t\tselect\n\t\t<include refid=\"allCanalColumns\" />\n\t\tfrom CANAL\n\t\twhere NAME = #name#\n\t</select>\n  \t\n\t<select id=\"listCanalByIds\" resultMap=\"canalResult\">\n\t\tselect\n\t\t<include refid=\"allCanalColumns\" />\n\t\tfrom CANAL where ID in\n\t\t<iterate open=\"(\" close=\")\" conjunction=\",\">\n\t\t\t#[]#\n\t\t</iterate>\n\t\tORDER BY ID DESC\n\t</select>\n\t\n\t<select id=\"getCanalCount\" resultClass=\"Integer\">\n\t\tselect count(*) from CANAL \n\t\t<dynamic prepend=\"where\">\n\t\t\t<isNotEmpty property=\"searchKey\">\n\t\t\t\tNAME like concat('%',replace(#searchKey#,'_','\\_'),'%')\n\t\t\t\tor ID like concat('%',replace(#searchKey#,'_','\\_'),'%')\n\t\t\t\tor PARAMETERS like concat('%',replace(#searchKey#,'_','\\_'),'%')\n\t\t\t</isNotEmpty>\n\t\t</dynamic>\n\t</select>\n\t\n\t<select id=\"listCanals\" resultMap=\"canalResult\">\n\t\tselect\n\t\t<include refid=\"allCanalColumns\" />\n\t\tfrom CANAL\n\t\t<dynamic prepend=\"where\">\n\t\t\t<isNotEmpty property=\"searchKey\">\n\t\t\t\tNAME like concat('%',replace(#searchKey#,'_','\\_'),'%')\n\t\t\t\tor ID like concat('%',replace(#searchKey#,'_','\\_'),'%')\n\t\t\t\tor PARAMETERS like concat('%',replace(#searchKey#,'_','\\_'),'%')\n\t\t\t</isNotEmpty>\n\t\t</dynamic>\n\n\t\tORDER BY ID DESC\n\n\t\t<dynamic>\n\t\t\t<isNotEmpty property=\"offset\">\n\t\t\t\t<isNotEmpty property=\"length\">\n\t\t\t\t\tlimit #offset#, #length#\n\t\t\t\t</isNotEmpty>\n\t\t\t</isNotEmpty>\n\t\t</dynamic>\n\t</select>\n\n</sqlMap>  "
  },
  {
    "path": "manager/biz/src/main/resources/sqlmap/sqlmap-mapping-channel.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>  \n    <!DOCTYPE sqlMap PUBLIC \"-//ibatis.apache.org//DTD SQL Map 2.0//EN\"  \n       \"http://ibatis.apache.org/dtd/sql-map-2.dtd\">\n\n<sqlMap namespace=\"com.alibaba.otter.manager.biz.config.channel.dal.dataobject.ChannelDO\">\n\t<typeAlias alias=\"channel\"\n\t\ttype=\"com.alibaba.otter.manager.biz.config.channel.dal.dataobject.ChannelDO\" />\n\n\t<resultMap id=\"channelResult\" class=\"channel\">\n\t\t<result property=\"id\" column=\"ID\" />\n\t\t<result property=\"name\" column=\"NAME\" />\n\t\t<result property=\"description\" column=\"DESCRIPTION\" />\n\t\t<result property=\"parameters\" column=\"PARAMETERS\" />\n\t\t<result property=\"gmtCreate\" column=\"GMT_CREATE\" />\n\t\t<result property=\"gmtModified\" column=\"GMT_MODIFIED\" />\n\t</resultMap>\n\t\n\t<resultMap id=\"channelPkResult\" class=\"channel\">\n\t\t<result property=\"id\" column=\"ID\" />\n\t</resultMap>\n\n\t<!-- all columns -->\n\t<sql id=\"allChannelColumns\">ID,NAME,DESCRIPTION,PARAMETERS,GMT_CREATE,GMT_MODIFIED</sql>\n\t\n\t<sql id=\"channelPk\">ID</sql>\n\t\n\t\n\t<select id=\"listChannelPks\" resultMap=\"channelPkResult\">\n\t\tselect\n\t\t<include refid=\"channelPk\" />\n\t\tfrom CHANNEL \n\t\t<dynamic prepend=\"where\">\n\t\t\t<isNotEmpty property=\"searchKey\">\n\t\t\tNAME like concat('%',replace(#searchKey#,'_','\\_'),'%') \n\t\t\tor ID like concat('%',replace(#searchKey#,'_','\\_'),'%')\n\t\t\t</isNotEmpty>\n\t\t</dynamic>\n\t\n\t\tORDER BY ID DESC\n\t\t\n\t\t<dynamic>\n\t\t\t<isNotEmpty property=\"offset\" >\n\t\t\t\t<isNotEmpty property=\"length\">\n\t\t\t\t\tlimit #offset#, #length#\n\t\t\t\t</isNotEmpty>\n\t\t\t</isNotEmpty>\n\t\t</dynamic>\n\t</select>\t\n\t\n\t<select id=\"findChannelById\" resultMap=\"channelResult\"\n\t\tparameterClass=\"long\">\n\t\tselect\n\t\t<include refid=\"allChannelColumns\" />\n\t\tfrom CHANNEL where ID = #value#\n\t</select>\n\t\n\t<select id=\"getChannelCount\" resultClass=\"Integer\">\n\t\tselect count(*) from CHANNEL \n\t\t<dynamic prepend=\"where\">\n\t\t\t<isNotEmpty property=\"searchKey\">\n\t\t\tNAME like concat('%',replace(#searchKey#,'_','\\_'),'%') \n\t\t\tor ID like concat('%',replace(#searchKey#,'_','\\_'),'%')\n\t\t\t</isNotEmpty>\n\t\t</dynamic>\n\t</select>\n\t\n\t<select id=\"listChannelByIds\" resultMap=\"channelResult\" >\n\t\tselect\n\t\t<include refid=\"allChannelColumns\" />\n\t\tfrom CHANNEL where ID in\n\t\t<iterate open=\"(\" close=\")\" conjunction=\",\">\n\t\t\t#[]#\n\t\t</iterate>\n\t\tORDER BY ID DESC\n\t</select>\t\n\t\n\t<select id=\"listChannels\" resultMap=\"channelResult\">\n\t\tselect\n\t\t<include refid=\"allChannelColumns\" />\n\t\tfrom CHANNEL \n\t\t<dynamic prepend=\"where\">\n\t\t\t<isNotEmpty property=\"searchKey\">\n\t\t\tNAME like concat('%',replace(#searchKey#,'_','\\_'),'%') \n\t\t\tor ID like concat('%',replace(#searchKey#,'_','\\_'),'%')\n\t\t\t</isNotEmpty>\n\t\t</dynamic>\n\t\n\t\tORDER BY ID DESC\n\t\t\n\t\t<dynamic>\n\t\t\t<isNotEmpty property=\"offset\" >\n\t\t\t\t<isNotEmpty property=\"length\">\n\t\t\t\t\tlimit #offset#, #length#\n\t\t\t\t</isNotEmpty>\n\t\t\t</isNotEmpty>\n\t\t</dynamic>\n\t</select>\t\n\n\n\t<update id=\"updateChannel\" parameterClass=\"channel\">\n\t\tupdate CHANNEL\n\t\tset\n\t\tNAME=#name#,\n\t\tDESCRIPTION=#description#,\n\t\tPARAMETERS=#parameters#,\n\t\tGMT_MODIFIED=now()\n\t\tWHERE ID = #id#  \n\t</update>\n\t\n\t<select id=\"checkChannelUnique\" resultClass=\"Integer\"\n\t\tparameterClass=\"channel\">\n\t\tselect count(*) from CHANNEL\n\t\twhere CHANNEL.ID != #id#\n\t\tand CHANNEL.NAME = #name#\n\t</select>\n\t\n\t<insert id=\"insertChannel\" parameterClass=\"channel\">\n\t\tinsert into CHANNEL\n\t\t(NAME, DESCRIPTION,PARAMETERS, GMT_CREATE, GMT_MODIFIED)\n\t\tSELECT #name#,#description#,#parameters#,now(),now() \n\t\tfrom dual\n\t\tWHERE not exists (select * from CHANNEL\n\t\twhere CHANNEL.NAME = #name#); \n\t\t<selectKey keyProperty=\"id\" resultClass=\"long\">\n\t\t\tselect last_insert_id()\n\t\t</selectKey>\n\t</insert>\n \n\t<delete id=\"deleteChannelById\" parameterClass=\"long\">\n    \tdelete from CHANNEL where ID = #value#\n    </delete>\n\n</sqlMap>  "
  },
  {
    "path": "manager/biz/src/main/resources/sqlmap/sqlmap-mapping-datacolumnpair.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>  \n    <!DOCTYPE sqlMap PUBLIC \"-//ibatis.apache.org//DTD SQL Map 2.0//EN\"  \n       \"http://ibatis.apache.org/dtd/sql-map-2.dtd\">\n\n<sqlMap namespace=\"com.alibaba.otter.manager.biz.config.datacolumnpair.dal.dataobject.DataColumnPairDO\">\n\t<typeAlias alias=\"dataColumnPair\"\n\t\ttype=\"com.alibaba.otter.manager.biz.config.datacolumnpair.dal.dataobject.DataColumnPairDO\" />\n\n\t<resultMap id=\"dataColumnPairResult\" class=\"dataColumnPair\">\n\t\t<result property=\"id\" column=\"ID\" />\n\t\t<result property=\"sourceColumnName\" column=\"SOURCE_COLUMN\" />\n\t\t<result property=\"targetColumnName\" column=\"TARGET_COLUMN\" />\n\t\t<result property=\"dataMediaPairId\" column=\"DATA_MEDIA_PAIR_ID\" />\n\t\t<result property=\"gmtCreate\" column=\"GMT_CREATE\" />\n\t\t<result property=\"gmtModified\" column=\"GMT_MODIFIED\" />\n\t</resultMap>\n\n\t<!-- all dataMediaPair columns -->\n\t<sql id=\"allDataColumnPairColumns\">ID,SOURCE_COLUMN,TARGET_COLUMN,DATA_MEDIA_PAIR_ID,GMT_CREATE,GMT_MODIFIED</sql>\n\n\t<select id=\"findDataColumnPairById\" resultMap=\"dataColumnPairResult\" parameterClass=\"long\">\n\t\tselect\n\t\t<include refid=\"allDataColumnPairColumns\" />\n\t\tfrom COLUMN_PAIR where ID = #value#\n\t</select>\n\t\n\t<select id=\"listDataColumnPairByDataMediaPairId\" resultMap=\"dataColumnPairResult\" parameterClass=\"long\">\n\t\tselect\n\t\t<include refid=\"allDataColumnPairColumns\" />\n\t\tfrom COLUMN_PAIR where DATA_MEDIA_PAIR_ID = #value#\n\t</select>\n\t\n\t<select id=\"listDataColumnPairByDataMediaPairIds\" resultMap=\"dataColumnPairResult\">\n\t\tselect\n\t\t<include refid=\"allDataColumnPairColumns\" />\n\t\tfrom COLUMN_PAIR where DATA_MEDIA_PAIR_ID in\n\t\t<iterate open=\"(\" close=\")\" conjunction=\",\">\n\t\t\t#[]#\n\t\t</iterate>\n\t</select>\n\t\n\t\n\n\t<update id=\"updateDataColumnPair\" parameterClass=\"dataColumnPair\">\n\t\tupdate COLUMN_PAIR\n\t\tset\n\t\tSOURCE_COLUMN = #sourceColumnName#,\n\t\tTARGET_COLUMN = #targetColumnName#,\n\t\tDATA_MEDIA_PAIR_ID = #dataMediaPairId#,\n\t\tGMT_MODIFIED=now()\n\t\tWHERE ID = #id#\n\t</update>\n\t\n\t\n\t<insert id=\"insertDataColumnPair\" parameterClass=\"dataColumnPair\">\n\t\tinsert into COLUMN_PAIR\n\t\t(SOURCE_COLUMN,TARGET_COLUMN,DATA_MEDIA_PAIR_ID,GMT_CREATE, GMT_MODIFIED)\n\t\tSELECT #sourceColumnName#,#targetColumnName#,#dataMediaPairId#,now(),now() \n\t\tfrom dual \n\t\tWHERE not exists (select * from COLUMN_PAIR \n\t\twhere COLUMN_PAIR.SOURCE_COLUMN = #sourceColumnName# \n\t\tand COLUMN_PAIR.TARGET_COLUMN = #targetColumnName# \n\t\tand COLUMN_PAIR.DATA_MEDIA_PAIR_ID = #dataMediaPairId#); \n\t\t<selectKey keyProperty=\"id\" resultClass=\"long\">\n\t\t\tselect last_insert_id()\n\t\t</selectKey>\n\t</insert>\n\n\t<delete id=\"deleteDataColumnPairById\" parameterClass=\"long\">\n    \tdelete from COLUMN_PAIR where ID = #value#\n    </delete>\n    \n    <delete id=\"deleteDataColumnPairByDataMediaPairId\" parameterClass=\"long\">\n    \tdelete from COLUMN_PAIR where DATA_MEDIA_PAIR_ID = #value#\n    </delete>\n\n</sqlMap>  "
  },
  {
    "path": "manager/biz/src/main/resources/sqlmap/sqlmap-mapping-datacolumnpairgroup.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>  \n    <!DOCTYPE sqlMap PUBLIC \"-//ibatis.apache.org//DTD SQL Map 2.0//EN\"  \n       \"http://ibatis.apache.org/dtd/sql-map-2.dtd\">\n\n<sqlMap namespace=\"com.alibaba.otter.manager.biz.config.datacolumnpair.dal.dataobject.DataColumnPairGroupDO\">\n\t<typeAlias alias=\"dataColumnPairGroup\"\n\t\ttype=\"com.alibaba.otter.manager.biz.config.datacolumnpair.dal.dataobject.DataColumnPairGroupDO\"/>\n\n\t<resultMap id=\"dataColumnPairGroupResult\" class=\"dataColumnPairGroup\">\n\t\t<result property=\"id\" column=\"ID\" />\n\t\t<result property=\"columnPairContent\" column=\"COLUMN_PAIR_CONTENT\" />\n\t\t<result property=\"dataMediaPairId\" column=\"DATA_MEDIA_PAIR_ID\" />\n\t\t<result property=\"gmtCreate\" column=\"GMT_CREATE\" />\n\t\t<result property=\"gmtModified\" column=\"GMT_MODIFIED\" />\n\t</resultMap>\n\n\t<!-- all dataMediaPair columns -->\n\t<sql id=\"allDataColumnPairGroupColumns\">ID,COLUMN_PAIR_CONTENT,DATA_MEDIA_PAIR_ID,GMT_CREATE,GMT_MODIFIED</sql>\n\n\t<select id=\"findDataColumnPairGroupById\" resultMap=\"dataColumnPairGroupResult\" parameterClass=\"long\">\n\t\tselect\n\t\t<include refid=\"allDataColumnPairGroupColumns\" />\n\t\tfrom COLUMN_PAIR_GROUP where ID = #value#\n\t</select>\n\t\n\t<select id=\"listDataColumnPairGroupByDataMediaPairId\" resultMap=\"dataColumnPairGroupResult\" parameterClass=\"long\">\n\t\tselect\n\t\t<include refid=\"allDataColumnPairGroupColumns\" />\n\t\tfrom COLUMN_PAIR_GROUP where DATA_MEDIA_PAIR_ID = #value#\n\t</select>\n\t\n\t<select id=\"listDataColumnPairGroupByDataMediaPairIds\" resultMap=\"dataColumnPairGroupResult\">\n\t\tselect\n\t\t<include refid=\"allDataColumnPairGroupColumns\" />\n\t\tfrom COLUMN_PAIR_GROUP where DATA_MEDIA_PAIR_ID in\n\t\t<iterate open=\"(\" close=\")\" conjunction=\",\">\n\t\t\t#[]#\n\t\t</iterate>\n\t</select>\n\n\t<update id=\"updateDataColumnPairGroup\" parameterClass=\"dataColumnPairGroup\">\n\t\tupdate COLUMN_PAIR_GROUP\n\t\tset\n\t\tCOLUMN_PAIR_CONTENT = #columnPairContent#,\n\t\tDATA_MEDIA_PAIR_ID = #dataMediaPairId#,\n\t\tGMT_MODIFIED=now()\n\t\tWHERE ID = #id#\n\t</update>\n\t\n\t\n\t<insert id=\"insertDataColumnPairGroup\" parameterClass=\"dataColumnPairGroup\">\n\t\tinsert into COLUMN_PAIR_GROUP\n\t\t(COLUMN_PAIR_CONTENT,DATA_MEDIA_PAIR_ID,GMT_CREATE, GMT_MODIFIED)\n\t\tSELECT #columnPairContent#,#dataMediaPairId#,now(),now() \n\t\tfrom dual \n\t\tWHERE not exists (select * from COLUMN_PAIR_GROUP \n\t\twhere COLUMN_PAIR_GROUP.COLUMN_PAIR_CONTENT = #columnPairContent# \n\t\tand COLUMN_PAIR_GROUP.DATA_MEDIA_PAIR_ID = #dataMediaPairId#); \n\t\t<selectKey keyProperty=\"id\" resultClass=\"long\">\n\t\t\tselect last_insert_id()\n\t\t</selectKey>\n\t</insert>\n\n\t<delete id=\"deleteDataColumnPairGroupById\" parameterClass=\"long\">\n    \tdelete from COLUMN_PAIR_GROUP where ID = #value#\n    </delete>\n    \n    <delete id=\"deleteDataColumnPairGroupByDataMediaPairId\" parameterClass=\"long\">\n    \tdelete from COLUMN_PAIR_GROUP where DATA_MEDIA_PAIR_ID = #value#\n    </delete>\n\n</sqlMap>  "
  },
  {
    "path": "manager/biz/src/main/resources/sqlmap/sqlmap-mapping-datamatrix.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>  \n    <!DOCTYPE sqlMap PUBLIC \"-//ibatis.apache.org//DTD SQL Map 2.0//EN\"  \n       \"http://ibatis.apache.org/dtd/sql-map-2.dtd\">\n\n<sqlMap\n\tnamespace=\"com.alibaba.otter.manager.biz.config.datamatrix.dal.dataobject.DataMatrixDO\">\n\t<typeAlias alias=\"dataMatrix\" type=\"com.alibaba.otter.manager.biz.config.datamatrix.dal.dataobject.DataMatrixDO\" />\n\n\t<resultMap id=\"dataMatrixResult\" class=\"dataMatrix\">\n\t\t<result property=\"id\" column=\"ID\" />\n\t\t<result property=\"groupKey\" column=\"GROUP_KEY\" />\n\t\t<result property=\"description\" column=\"DESCRIPTION\" />\n\t\t<result property=\"master\" column=\"MASTER\" />\n\t\t<result property=\"slave\" column=\"SLAVE\" />\n\t\t<result property=\"gmtCreate\" column=\"GMT_CREATE\" />\n\t\t<result property=\"gmtModified\" column=\"GMT_MODIFIED\" />\n\t</resultMap>\n\n\t<!-- all columns -->\n\t<sql id=\"allColumns\">ID,GROUP_KEY,MASTER,SLAVE,DESCRIPTION,GMT_CREATE,GMT_MODIFIED\n\t</sql>\n\n\t<update id=\"updateDataMatrix\" parameterClass=\"dataMatrix\"><![CDATA[\n\t\tupdate DATA_MATRIX\n\t\tset\n\t\tGROUP_KEY=#groupKey#,\n\t\tMASTER=#master#,\n\t\tSLAVE=#slave#,\n\t\tDESCRIPTION=#description#,\n\t\tGMT_MODIFIED=now()\n\t\tWHERE ID = #id#\n\t]]></update>\n\n\t<select id=\"checkDataMatrixUnique\" resultClass=\"Integer\" parameterClass=\"dataMatrix\">\n\t\tselect count(*) from DATA_MATRIX\n\t\twhere ID != #id#\n\t\tand\n\t\tGROUP_KEY = #groupKey#\n\t</select>\n\n\t<insert id=\"insertDataMatrix\" parameterClass=\"dataMatrix\">\n\t\tinsert into DATA_MATRIX\n\t\t(GROUP_KEY,MASTER,SLAVE,DESCRIPTION,GMT_CREATE,GMT_MODIFIED)\n\t\tSELECT\n\t\t#groupKey#,#master#,#slave#,#description#,now(),now()\n\t\tfrom dual\n\t\tWHERE not exists (select * from DATA_MATRIX\n\t\twhere DATA_MATRIX.GROUP_KEY = #groupKey# );\n\t\t<selectKey keyProperty=\"id\" resultClass=\"long\">\n\t\t\tselect\n\t\t\tlast_insert_id()\n\t\t</selectKey>\n\t</insert>\n\n\t<delete id=\"deleteDataMatrixById\" parameterClass=\"long\"><![CDATA[\n    \tdelete from DATA_MATRIX where ID = #value#\n    ]]></delete>\n    \n    \n    <select id=\"findDataMatrixByGroupKey\" parameterClass=\"string\" resultMap=\"dataMatrixResult\">\n\t\tselect\n\t\t<include refid=\"allColumns\" />\n\t\tfrom DATA_MATRIX\n\t\twhere GROUP_KEY = #groupKey#\n\t</select>\n  \t\n\t<select id=\"listDataMatrixByIds\" resultMap=\"dataMatrixResult\">\n\t\tselect\n\t\t<include refid=\"allColumns\" />\n\t\tfrom DATA_MATRIX where ID in\n\t\t<iterate open=\"(\" close=\")\" conjunction=\",\">\n\t\t\t#[]#\n\t\t</iterate>\n\t\tORDER BY ID DESC\n\t</select>\n\t\n\t<select id=\"getDataMatrixCount\" resultClass=\"Integer\">\n\t\tselect count(*) from DATA_MATRIX \n\t\t<dynamic prepend=\"where\">\n\t\t\t<isNotEmpty property=\"searchKey\">\n\t\t\t\tGROUP_KEY like concat('%',replace(#searchKey#,'_','\\_'),'%')\n\t\t\t\tor ID like concat('%',replace(#searchKey#,'_','\\_'),'%')\n\t\t\t\tor MASTER like concat('%',replace(#searchKey#,'_','\\_'),'%')\n\t\t\t\tor SLAVE like concat('%',replace(#searchKey#,'_','\\_'),'%')\n\t\t\t</isNotEmpty>\n\t\t</dynamic>\n\t</select>\n\t\n\t<select id=\"listDataMatrixs\" resultMap=\"dataMatrixResult\">\n\t\tselect\n\t\t<include refid=\"allColumns\" />\n\t\tfrom DATA_MATRIX\n\t\t<dynamic prepend=\"where\">\n\t\t\t<isNotEmpty property=\"searchKey\">\n\t\t\t\tGROUP_KEY like concat('%',replace(#searchKey#,'_','\\_'),'%')\n\t\t\t\tor ID like concat('%',replace(#searchKey#,'_','\\_'),'%')\n\t\t\t\tor MASTER like concat('%',replace(#searchKey#,'_','\\_'),'%')\n\t\t\t\tor SLAVE like concat('%',replace(#searchKey#,'_','\\_'),'%')\n\t\t\t</isNotEmpty>\n\t\t</dynamic>\n\t\tORDER BY ID DESC\n\t\t<dynamic>\n\t\t\t<isNotEmpty property=\"offset\">\n\t\t\t\t<isNotEmpty property=\"length\">\n\t\t\t\t\tlimit #offset#, #length#\n\t\t\t\t</isNotEmpty>\n\t\t\t</isNotEmpty>\n\t\t</dynamic>\n\t</select>\n</sqlMap>  \n"
  },
  {
    "path": "manager/biz/src/main/resources/sqlmap/sqlmap-mapping-datamedia.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>  \n    <!DOCTYPE sqlMap PUBLIC \"-//ibatis.apache.org//DTD SQL Map 2.0//EN\"  \n       \"http://ibatis.apache.org/dtd/sql-map-2.dtd\">\n\n<sqlMap namespace=\"com.alibaba.otter.manager.biz.config.datamedia.dal.dataobject.DataMediaDO\">\n\t<typeAlias alias=\"dataMedia\"\n\t\ttype=\"com.alibaba.otter.manager.biz.config.datamedia.dal.dataobject.DataMediaDO\" />\n\n\t<resultMap id=\"dataMediaResult\" class=\"dataMedia\">\n\t\t<result property=\"id\" column=\"ID\" />\n\t\t<result property=\"name\" column=\"NAME\" />\n\t\t<result property=\"namespace\" column=\"NAMESPACE\" />\n\t\t<result property=\"properties\" column=\"PROPERTIES\" />\n\t\t<result property=\"dataMediaSourceId\" column=\"DATA_MEDIA_SOURCE_ID\" />\n\t\t<result property=\"gmtCreate\" column=\"GMT_CREATE\" />\n\t\t<result property=\"gmtModified\" column=\"GMT_MODIFIED\" />\n\t</resultMap>\n\n\t<!-- all datamedia columns -->\n\t<sql id=\"allDataMediaColumns\">ID,NAME,NAMESPACE,PROPERTIES,DATA_MEDIA_SOURCE_ID,GMT_CREATE,GMT_MODIFIED</sql>\n\n\t<select id=\"findDataMediaById\" resultMap=\"dataMediaResult\" parameterClass=\"long\">\n\t\tselect\n\t\t<include refid=\"allDataMediaColumns\" />\n\t\tfrom DATA_MEDIA where ID = #value#\n\t</select>\n\t\n\t<select id=\"getDataMediaCount\" resultClass=\"Integer\">\n\t\tselect count(*) from DATA_MEDIA \n\t\t<dynamic prepend=\"where\">\n\t\t\t<isNotEmpty property=\"searchKey\">\n\t\t\tNAME like concat('%',replace(#searchKey#,'_','\\_'),'%') \n\t\t\tor NAMESPACE like concat('%',replace(#searchKey#,'_','\\_'),'%')\n\t\t\tor ID like concat('%',replace(#searchKey#,'_','\\_'),'%')\n\t\t\t</isNotEmpty>\n\t\t</dynamic>\n\t</select>\n\t\n\t<select id=\"findDataMediaByDataMediaSourceId\" resultMap=\"dataMediaResult\" parameterClass=\"long\">\n\t\tselect\n\t\t<include refid=\"allDataMediaColumns\" />\n\t\tfrom DATA_MEDIA where DATA_MEDIA_SOURCE_ID = #value#\n\t</select>\n\n\t<select id=\"listDataMedias\" resultMap=\"dataMediaResult\">\n\t\tselect\n\t\t<include refid=\"allDataMediaColumns\" />\n\t\tfrom DATA_MEDIA \n\t\t<dynamic prepend=\"where\">\n\t\t\t<isNotEmpty property=\"searchKey\">\n\t\t\tNAME like concat('%',replace(#searchKey#,'_','\\_'),'%') \n\t\t\tor NAMESPACE like concat('%',replace(#searchKey#,'_','\\_'),'%')\n\t\t\tor ID like concat('%',replace(#searchKey#,'_','\\_'),'%')\n\t\t\t</isNotEmpty>\n\t\t</dynamic>\n\t\n\t\tORDER BY ID DESC\n\t\t\n\t\t<dynamic>\n\t\t\t<isNotEmpty property=\"offset\" >\n\t\t\t\t<isNotEmpty property=\"length\">\n\t\t\t\t\tlimit #offset#, #length#\n\t\t\t\t</isNotEmpty>\n\t\t\t</isNotEmpty>\n\t\t</dynamic>\n\t</select>\n\t\n\t<select id=\"listDataMediaByIds\" resultMap=\"dataMediaResult\" >\n\t\tselect\n\t\t<include refid=\"allDataMediaColumns\" />\n\t\tfrom DATA_MEDIA where ID in\n\t\t<iterate open=\"(\" close=\")\" conjunction=\",\">\n\t\t\t#[]#\n\t\t</iterate>\n\t\tORDER BY ID DESC\n\t</select>\n\t\n\t<select id=\"listDataMediasByDataMediaSourceId\" resultMap=\"dataMediaResult\" parameterClass=\"long\">\n\t\tselect\n\t\t<include refid=\"allDataMediaColumns\" />\n\t\tfrom DATA_MEDIA where DATA_MEDIA_SOURCE_ID = #value# ORDER BY ID DESC\n\t</select>\n\n\t<update id=\"updateDataMedia\" parameterClass=\"dataMedia\"><![CDATA[\n\t\tupdate DATA_MEDIA\n\t\tset\n\t\tNAME=#name#,\n\t\tNAMESPACE=#namespace#,\n\t\tPROPERTIES=#properties#,\n\t\tDATA_MEDIA_SOURCE_ID=#dataMediaSourceId#,\n\t\tGMT_MODIFIED=now()\n\t\tWHERE ID = #id#\n\t]]></update>\n\n\t<select id=\"checkDataMediaUnique\" resultClass=\"Integer\"\n\t\tparameterClass=\"dataMedia\">\n\t\tselect count(*) from DATA_MEDIA\n\t\twhere DATA_MEDIA.ID != #id#\n\t\tand DATA_MEDIA.NAME = #name#\n\t\tand DATA_MEDIA.NAMESPACE = #namespace#\n\t\tand DATA_MEDIA.DATA_MEDIA_SOURCE_ID = #dataMediaSourceId#\n\t</select>\n\t\n\t<select id=\"checkDataMediaUniqueAndReturnTheExist\" resultMap=\"dataMediaResult\"\n\t\tparameterClass=\"dataMedia\">\n\t\tselect <include refid=\"allDataMediaColumns\" /> from DATA_MEDIA\n\t\twhere DATA_MEDIA.ID != #id#\n\t\tand DATA_MEDIA.NAME = #name#\n\t\tand DATA_MEDIA.NAMESPACE = #namespace#\n\t\tand DATA_MEDIA.DATA_MEDIA_SOURCE_ID = #dataMediaSourceId#\n\t</select>\n\n\t<insert id=\"insertDataMedia\" parameterClass=\"dataMedia\">\n\t\tinsert into DATA_MEDIA\n\t\t(NAME,NAMESPACE,PROPERTIES,DATA_MEDIA_SOURCE_ID,GMT_CREATE, GMT_MODIFIED)\n\t\tSELECT #name#,#namespace#,#properties#,#dataMediaSourceId#,now(),now()\n\t\tfrom dual\n\t\tWHERE not exists (select * from DATA_MEDIA\n\t\twhere DATA_MEDIA.NAME = #name# \n\t\tand DATA_MEDIA.NAMESPACE = #namespace#\n\t\tand DATA_MEDIA.DATA_MEDIA_SOURCE_ID = #dataMediaSourceId#); \n\t\t<selectKey keyProperty=\"id\" resultClass=\"long\">\n\t\t\tselect\n\t\t\tlast_insert_id()\n\t\t</selectKey>\n\t</insert>\n\n\t<delete id=\"deleteDataMediaById\" parameterClass=\"long\"><![CDATA[\n    \tdelete from DATA_MEDIA where ID = #value#\n    ]]></delete>\n\n</sqlMap>  "
  },
  {
    "path": "manager/biz/src/main/resources/sqlmap/sqlmap-mapping-datamediapair.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>  \n    <!DOCTYPE sqlMap PUBLIC \"-//ibatis.apache.org//DTD SQL Map 2.0//EN\"  \n       \"http://ibatis.apache.org/dtd/sql-map-2.dtd\">\n\n<sqlMap namespace=\"com.alibaba.otter.manager.biz.config.datamediapair.dal.dataobject.DataMediaPairDO\">\n\t<typeAlias alias=\"dataMediaPair\"\n\t\ttype=\"com.alibaba.otter.manager.biz.config.datamediapair.dal.dataobject.DataMediaPairDO\" />\n\n\t<resultMap id=\"dataMediaPairResult\" class=\"dataMediaPair\">\n\t\t<result property=\"id\" column=\"ID\" />\n\t\t<result property=\"sourceDataMediaId\" column=\"SOURCE_DATA_MEDIA_ID\" />\n\t\t<result property=\"targetDataMediaId\" column=\"TARGET_DATA_MEDIA_ID\" />\n\t\t<result property=\"pullWeight\" column=\"PULLWEIGHT\" />\n\t\t<result property=\"pushWeight\" column=\"PUSHWEIGHT\" />\n\t\t<result property=\"filter\" column=\"FILTER\" />\n\t\t<result property=\"resolver\" column=\"RESOLVER\" />\n\t\t<result property=\"pipelineId\" column=\"PIPELINE_ID\" />\n\t\t<result property=\"columnPairMode\" column=\"COLUMN_PAIR_MODE\" />\n\t\t<result property=\"gmtCreate\" column=\"GMT_CREATE\" />\n\t\t<result property=\"gmtModified\" column=\"GMT_MODIFIED\" />\n\t</resultMap>\n\n\t<!-- all dataMediaPair columns -->\n\t<sql id=\"allDataMediaPairColumns\">ID,SOURCE_DATA_MEDIA_ID,TARGET_DATA_MEDIA_ID,PULLWEIGHT,PUSHWEIGHT,FILTER,RESOLVER,PIPELINE_ID,COLUMN_PAIR_MODE,GMT_CREATE,GMT_MODIFIED</sql>\n\n\t<select id=\"findDataMediaPairById\" resultMap=\"dataMediaPairResult\" parameterClass=\"long\">\n\t\tselect\n\t\t<include refid=\"allDataMediaPairColumns\" />\n\t\tfrom DATA_MEDIA_PAIR where ID = #value#\n\t</select>\n\t\n\t<select id=\"getDataMediaPairCount\" resultClass=\"Integer\">\n\t\tselect count(*) from DATA_MEDIA_PAIR \n\t\t<dynamic prepend=\"where\">\n\t\t\t<isNotEmpty property=\"searchKey\">\n\t\t\tNAME like concat('%',replace(#searchKey#,'_','\\_'),'%') \n\t\t\tor ID like concat('%',replace(#searchKey#,'_','\\_'),'%')\n\t\t\t</isNotEmpty>\n\t\t</dynamic>\n\t</select>\n\n\t<select id=\"listDataMediaPairs\" resultMap=\"dataMediaPairResult\">\n\t\tselect\n\t\t<include refid=\"allDataMediaPairColumns\" />\n\t\tfrom DATA_MEDIA_PAIR \n\t\t<dynamic prepend=\"where\">\n\t\t\t<isNotEmpty property=\"searchKey\">\n\t\t\tNAME like concat('%',replace(#searchKey#,'_','\\_'),'%') \n\t\t\tor ID like concat('%',replace(#searchKey#,'_','\\_'),'%')\n\t\t\t</isNotEmpty>\n\t\t</dynamic>\n\t\n\t\tORDER BY ID DESC\n\t\t\n\t\t<dynamic>\n\t\t\t<isNotEmpty property=\"offset\" >\n\t\t\t\t<isNotEmpty property=\"length\">\n\t\t\t\t\tlimit #offset#, #length#\n\t\t\t\t</isNotEmpty>\n\t\t\t</isNotEmpty>\n\t\t</dynamic>\n\t</select>\n\t\n\t<select id=\"listDataMediaPairByIds\" resultMap=\"dataMediaPairResult\" >\n\t\tselect\n\t\t<include refid=\"allDataMediaPairColumns\" />\n\t\tfrom DATA_MEDIA_PAIR where ID in\n\t\t<iterate open=\"(\" close=\")\" conjunction=\",\">\n\t\t\t#[]#\n\t\t</iterate>\n\t\tORDER BY ID DESC\n\t</select>\n\t\n\t<select id=\"listDataMediaPairsByPipelineId\" resultMap=\"dataMediaPairResult\" parameterClass=\"long\">\n\t\tselect\n\t\t<include refid=\"allDataMediaPairColumns\" />\n\t\tfrom DATA_MEDIA_PAIR where PIPELINE_ID = #value# ORDER BY ID DESC\n\t</select>\n\t\n\t<select id=\"listDataMediaPairsByDataMediaId\" resultMap=\"dataMediaPairResult\" parameterClass=\"long\">\n\t\tselect\n\t\t<include refid=\"allDataMediaPairColumns\" />\n\t\tfrom DATA_MEDIA_PAIR where SOURCE_DATA_MEDIA_ID = #value# or TARGET_DATA_MEDIA_ID = #value# ORDER BY ID DESC\n\t</select>\n\n\t\n\n\t<update id=\"updateDataMediaPair\" parameterClass=\"dataMediaPair\">\n\t\tupdate DATA_MEDIA_PAIR\n\t\tset\n\t\tSOURCE_DATA_MEDIA_ID = #sourceDataMediaId#,\n\t\tTARGET_DATA_MEDIA_ID = #targetDataMediaId#,\n\t\tPULLWEIGHT = #pullWeight#,\n\t\tPUSHWEIGHT = #pushWeight#,\n\t\tFILTER = #filter#,\n\t\tRESOLVER = #resolver#,\n\t\tPIPELINE_ID = #pipelineId#,\n\t\tCOLUMN_PAIR_MODE=#columnPairMode#,\n\t\tGMT_MODIFIED=now()\n\t\tWHERE ID = #id#\n\t</update>\n\t\n\t<select id=\"checkDataMediaPairUnique\" resultClass=\"Integer\"\n\t\tparameterClass=\"dataMediaPair\">\n\t\tselect count(*) from DATA_MEDIA_PAIR\n\t\twhere\n\t\tDATA_MEDIA_PAIR.ID != #id#\n\t\tand DATA_MEDIA_PAIR.SOURCE_DATA_MEDIA_ID =\n\t\t#sourceDataMediaId#\n\t\tand DATA_MEDIA_PAIR.TARGET_DATA_MEDIA_ID = #targetDataMediaId#\n\t\tand DATA_MEDIA_PAIR.PIPELINE_ID = #pipelineId#\n\t</select>\n\t\n\t<insert id=\"insertDataMediaPair\" parameterClass=\"dataMediaPair\">\n\t\tinsert into DATA_MEDIA_PAIR\n\t\t(SOURCE_DATA_MEDIA_ID,TARGET_DATA_MEDIA_ID,PULLWEIGHT,PUSHWEIGHT,FILTER,RESOLVER,PIPELINE_ID,COLUMN_PAIR_MODE,GMT_CREATE,GMT_MODIFIED)\n\t\tSELECT #sourceDataMediaId#,#targetDataMediaId#,#pullWeight#,#pushWeight#,#filter#,#resolver#,#pipelineId#,#columnPairMode#,now(),now() \n\t\tfrom dual \n\t\tWHERE not exists (select * from DATA_MEDIA_PAIR \n\t\twhere DATA_MEDIA_PAIR.SOURCE_DATA_MEDIA_ID = #sourceDataMediaId# \n\t\tand DATA_MEDIA_PAIR.TARGET_DATA_MEDIA_ID = #targetDataMediaId# \n\t\tand DATA_MEDIA_PAIR.PIPELINE_ID = #pipelineId#); \n\t\t<selectKey keyProperty=\"id\" resultClass=\"long\">\n\t\t\tselect last_insert_id()\n\t\t</selectKey>\n\t</insert>\n<!-- \n\t<insert id=\"insertDataMediaPair\" parameterClass=\"dataMediaPair\">\n\t\tinsert into DATA_MEDIA_PAIR\n\t\t(SOURCE_DATA_MEDIA_ID,TARGET_DATA_MEDIA_ID,PULLWEIGHT,PUSHWEIGHT,FILTER,RESOLVER,PIPELINE_ID,GMT_CREATE, GMT_MODIFIED)\n\t\tvalues\n\t\t(#sourceDataMediaId#,#targetDataMediaId#,#pullWeight#,#pushWeight#,#filter#,#resolver#,#pipelineId#,now(),now())\n\t\t<selectKey keyProperty=\"id\" resultClass=\"long\">\n\t\t\tselect last_insert_id()\n\t\t</selectKey>\n\t</insert>\n -->\n\t<delete id=\"deleteDataMediaPairById\" parameterClass=\"long\"><![CDATA[\n    \tdelete from DATA_MEDIA_PAIR where ID = #value#\n    ]]></delete>\n\n</sqlMap>  "
  },
  {
    "path": "manager/biz/src/main/resources/sqlmap/sqlmap-mapping-datamediasource.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>  \n    <!DOCTYPE sqlMap PUBLIC \"-//ibatis.apache.org//DTD SQL Map 2.0//EN\"  \n       \"http://ibatis.apache.org/dtd/sql-map-2.dtd\">\n\n<sqlMap namespace=\"com.alibaba.otter.manager.biz.config.datamediasource.dal.dataobject.DataMediaSourceDO\">\n\t<typeAlias alias=\"dataMediaSource\"\n\t\ttype=\"com.alibaba.otter.manager.biz.config.datamediasource.dal.dataobject.DataMediaSourceDO\" />\n\n\t<resultMap id=\"dataMediaSourceResult\" class=\"dataMediaSource\">\n\t\t<result property=\"id\" column=\"ID\" />\n\t\t<result property=\"name\" column=\"NAME\" />\n\t\t<result property=\"type\" column=\"TYPE\" typeHandler=\"\"/>\n\t\t<result property=\"properties\" column=\"PROPERTIES\" />\n\t\t<result property=\"gmtCreate\" column=\"GMT_CREATE\" />\n\t\t<result property=\"gmtModified\" column=\"GMT_MODIFIED\" />\n\t</resultMap>\n\n\t<!-- all dataMediaSource columns -->\n\t<sql id=\"allDataMediaSourceColumns\">ID,NAME,TYPE,PROPERTIES,GMT_CREATE,GMT_MODIFIED</sql>\n\n\t<select id=\"findDataMediaSourceById\" resultMap=\"dataMediaSourceResult\"\n\t\tparameterClass=\"long\">\n\t\tselect\n\t\t<include refid=\"allDataMediaSourceColumns\" />\n\t\tfrom DATA_MEDIA_SOURCE where ID = #value#\n\t</select>\n\t\n\t<select id=\"getSourceCount\" resultClass=\"Integer\">\n\t\tselect count(*) from DATA_MEDIA_SOURCE \n\t\t<dynamic prepend=\"where\">\n\t\t\t<isNotEmpty property=\"searchKey\">\n\t\t\tNAME like concat('%',replace(#searchKey#,'_','\\_'),'%') \n\t\t\tor ID like concat('%',replace(#searchKey#,'_','\\_'),'%')\n\t\t\tor TYPE like concat('%',replace(#searchKey#,'_','\\_'),'%')\n\t\t\tor PROPERTIES like concat('%',replace(#searchKey#,'_','\\_'),'%')\n\t\t\t</isNotEmpty>\n\t\t</dynamic>\n\t</select>\n\n\t<select id=\"listDataMediaSources\" resultMap=\"dataMediaSourceResult\">\n\t\tselect\n\t\t<include refid=\"allDataMediaSourceColumns\" />\n\t\tfrom DATA_MEDIA_SOURCE \n\t\t<dynamic prepend=\"where\">\n\t\t\t<isNotEmpty property=\"searchKey\">\n\t\t\tNAME like concat('%',replace(#searchKey#,'_','\\_'),'%') \n\t\t\tor ID like concat('%',replace(#searchKey#,'_','\\_'),'%')\n\t\t\tor TYPE like concat('%',replace(#searchKey#,'_','\\_'),'%')\n\t\t\tor PROPERTIES like concat('%',replace(#searchKey#,'_','\\_'),'%')\n\t\t\t</isNotEmpty>\n\t\t</dynamic>\n\t\n\t\tORDER BY ID DESC\n\t\t\n\t\t<dynamic>\n\t\t\t<isNotEmpty property=\"offset\" >\n\t\t\t\t<isNotEmpty property=\"length\">\n\t\t\t\t\tlimit #offset#, #length#\n\t\t\t\t</isNotEmpty>\n\t\t\t</isNotEmpty>\n\t\t</dynamic>\n\t</select>\n\t\n\t<select id=\"listSourceByIds\" resultMap=\"dataMediaSourceResult\" >\n\t\tselect\n\t\t<include refid=\"allDataMediaSourceColumns\" />\n\t\tfrom DATA_MEDIA_SOURCE where ID in\n\t\t<iterate open=\"(\" close=\")\" conjunction=\",\">\n\t\t\t#[]#\n\t\t</iterate>\n\t\tORDER BY ID DESC\n\t</select>\n\n\t<update id=\"updateDataMediaSource\" parameterClass=\"dataMediaSource\"><![CDATA[\n\t\tupdate DATA_MEDIA_SOURCE\n\t\tset\n\t\tNAME=#name#,\n\t\tTYPE=#type#,\n\t\tPROPERTIES=#properties#,\n\t\tGMT_MODIFIED=now()\n\t\tWHERE ID = #id#\n\t]]></update>\n\t\n\t<select id=\"checkDataMediaSourceUnique\" resultClass=\"Integer\"\n\t\tparameterClass=\"dataMediaSource\">\n\t\tselect count(*) from DATA_MEDIA_SOURCE\n\t\twhere DATA_MEDIA_SOURCE.ID != #id#\n\t\tand DATA_MEDIA_SOURCE.NAME = #name#\n\t</select>\n\t\n\t<insert id=\"insertDataMediaSource\" parameterClass=\"dataMediaSource\">\n\t\tinsert into DATA_MEDIA_SOURCE\n\t\t(NAME,TYPE,PROPERTIES,GMT_CREATE, GMT_MODIFIED)\n\t\tSELECT #name#,#type#,#properties#,now(),now()\n\t\tfrom dual\n\t\tWHERE not exists (select * from DATA_MEDIA_SOURCE\n\t\twhere DATA_MEDIA_SOURCE.NAME = #name# ); \n\t\t<selectKey keyProperty=\"id\" resultClass=\"long\">\n\t\t\tselect\n\t\t\tlast_insert_id()\n\t\t</selectKey>\n\t</insert>\n\n\t<delete id=\"deleteDataMediaSourceById\" parameterClass=\"long\"><![CDATA[\n    \tdelete from DATA_MEDIA_SOURCE where ID = #value#\n    ]]></delete>\n\n</sqlMap>  "
  },
  {
    "path": "manager/biz/src/main/resources/sqlmap/sqlmap-mapping-delayStat.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>  \r\n    <!DOCTYPE sqlMap PUBLIC \"-//ibatis.apache.org//DTD SQL Map 2.0//EN\"  \r\n       \"http://ibatis.apache.org/dtd/sql-map-2.dtd\">\r\n\r\n<sqlMap namespace=\"com.alibaba.otter.manager.biz.statistics.delay.dal.dataobject.DelayStatDO\">\r\n\t<typeAlias alias=\"delayStat\"\r\n\t\ttype=\"com.alibaba.otter.manager.biz.statistics.delay.dal.dataobject.DelayStatDO\" />\r\n\t<typeAlias alias=\"timelineDelayCondition\"\r\n\t\ttype=\"com.alibaba.otter.manager.biz.statistics.delay.param.TimelineDelayCondition\" />\r\n\t<typeAlias alias=\"topDelayStat\"\r\n\t\ttype=\"com.alibaba.otter.manager.biz.statistics.delay.param.TopDelayStat\" />\r\n\r\n\t<resultMap id=\"delayStatResult\" class=\"delayStat\">\r\n\t\t<result property=\"id\" column=\"ID\" />\r\n\t\t<result property=\"delayTime\" column=\"DELAY_TIME\" />\r\n\t\t<result property=\"delayNumber\" column=\"DELAY_NUMBER\" />\r\n\t\t<result property=\"pipelineId\" column=\"PIPELINE_ID\" />\r\n\t\t<result property=\"gmtCreate\" column=\"GMT_CREATE\" />\r\n\t\t<result property=\"gmtModified\" column=\"GMT_MODIFIED\" />\r\n\t</resultMap>\r\n\t\r\n\t<resultMap id=\"topDelayStatResult\" class=\"topDelayStat\">\r\n\t\t<result property=\"channelName\" column=\"CHANNEL_NAME\" />\r\n\t\t<result property=\"pipelineName\" column=\"PIPELINE_NAME\" />\r\n\t\t<result property=\"channelId\" column=\"CHANNEL_ID\" />\r\n\t\t<result property=\"pipelineId\" column=\"PIPELINE_ID\" />\r\n\t\t<result property=\"delayTime\" column=\"DELAY_TIME\" />\r\n\t\t<result property=\"lastUpdate\" column=\"GMT_CREATE\" />\r\n\t</resultMap>\r\n\t<!-- all delayStat columns -->\r\n\t<sql id=\"allDelayStatColumns\">ID,DELAY_TIME,DELAY_NUMBER,PIPELINE_ID,GMT_CREATE,GMT_MODIFIED</sql>\r\n\r\n\t<!-- findDelayStatById -->\r\n\t<select id=\"findDelayStatById\" resultMap=\"delayStatResult\" parameterClass=\"long\">\r\n\t\tselect\r\n\t\t<include refid=\"allDelayStatColumns\" />\r\n\t\tfrom DELAY_STAT where ID = #value#\r\n\t</select>\t\r\n\t\r\n\t<!-- findRealtimeDelayStat -->\r\n\t<select id=\"findRealtimeDelayStat\" resultMap=\"delayStatResult\" parameterClass=\"long\">\r\n\t\tselect\r\n\t\t<include refid=\"allDelayStatColumns\" />\r\n\t\tfrom DELAY_STAT \r\n\t\twhere PIPELINE_ID=#pipelineId#\r\n\t\tORDER BY GMT_MODIFIED DESC, ID DESC limit 1\r\n\t</select>\t\t\r\n\r\n\t<!-- listAllDelayStat -->\r\n\t<select id=\"listAllDelayStat\" resultMap=\"delayStatResult\">\r\n\t\tselect\r\n\t\t<include refid=\"allDelayStatColumns\" />\r\n\t\tfrom DELAY_STAT ORDER BY GMT_MODIFIED DESC,ID DESC\r\n\t</select>\r\n\t\r\n\t<!-- listDelayStatsByPipelineId -->\r\n\t<select id=\"listDelayStatsByPipelineId\" resultMap=\"delayStatResult\" parameterClass=\"long\">\r\n\t\tselect\r\n\t\t<include refid=\"allDelayStatColumns\" />\r\n\t\tfrom DELAY_STAT \r\n\t\twhere PIPELINE_ID=#pipelineId#\r\n\t\tORDER BY GMT_MODIFIED DESC,ID DESC\r\n\t</select>\r\n\r\n\t<!-- listTimelineDelayStatsByPipelineId -->\r\n\t<select id=\"listTimelineDelayStatsByPipelineId\" resultMap=\"delayStatResult\" parameterClass=\"timelineDelayCondition\">\r\n\t\tselect\r\n\t\t<include refid=\"allDelayStatColumns\" />\r\n\t\tfrom DELAY_STAT \r\n\t\twhere PIPELINE_ID=#pipelineId# and <![CDATA[ GMT_CREATE>= #start# and GMT_CREATE <= #end#]]>\r\n\t\tORDER BY GMT_MODIFIED DESC,ID DESC\r\n\t</select>\r\n\t\r\n\t<update id=\"modifyDelayStat\" parameterClass=\"delayStat\"><![CDATA[\r\n\t\tupdate DELAY_STAT\r\n\t\tset\r\n\t\tDELAY_TIME=#delayTime#,\r\n\t\tDELAY_NUMBER=#delayNumber#,\r\n\t\tPIPELINE_ID=#pipelineId#,\r\n\t\tGMT_MODIFIED=now()\r\n\t\tWHERE ID = #id#\r\n\t]]></update>\r\n\r\n\t<insert id=\"insertDelayStat\" parameterClass=\"delayStat\">\r\n\t\tinsert into DELAY_STAT\r\n\t\t(DELAY_TIME, DELAY_NUMBER, PIPELINE_ID, GMT_CREATE, GMT_MODIFIED)\r\n\t\tvalues\r\n\t\t(#delayTime#,#delayNumber#,#pipelineId#,now(),now())\r\n\t\t<selectKey keyProperty=\"id\" resultClass=\"long\">\r\n\t\t\tselect last_insert_id()\r\n\t\t</selectKey>\r\n\t</insert>\r\n\r\n\t<delete id=\"deleteDelayStatById\" parameterClass=\"long\"><![CDATA[\r\n    \tdelete from DELAY_STAT where ID = #value#\r\n    ]]></delete>\r\n\t\r\n\t<!-- listTopByName -->\r\n\t<select id=\"listTopByName\" resultMap=\"topDelayStatResult\">\r\n\t\tselect p.* , d.DELAY_TIME , d.GMT_CREATE\r\n\t\tfrom DELAY_STAT d , (\r\n\t\t\tselect p.CHANNEL_ID , p.ID as PIPELINE_ID , c.NAME as CHANNEL_NAME, p.NAME as PIPELINE_NAME from CHANNEL c , PIPELINE p  \r\n\t\t\twhere 1 = 1 and c.ID = p.CHANNEL_ID\r\n\t\t\t<dynamic prepend=\"and\">\r\n\t\t\t\t<isNotEmpty property=\"searchKey\">\r\n\t\t\t\tc.NAME like concat('%',replace(#searchKey#,'_','\\_'),'%')   \r\n\t\t\t\t</isNotEmpty>\r\n\t\t\t</dynamic>\r\n\t\t\t) p \r\n\t\t\twhere d.ID = (\r\n\t\t\t\tselect d1.ID from DELAY_STAT d1 \r\n\t\t\t\t\twhere d1.PIPELINE_ID = p.PIPELINE_ID order by d1.PIPELINE_ID desc , d1.GMT_CREATE desc limit 1\r\n\t\t\t\t)\r\n\t\t\torder by d.DELAY_TIME desc , d.ID desc\r\n\t\t\t<dynamic>\r\n\t\t\t\t<isNotEmpty property=\"topN\" >\r\n\t\t\t\t\t\tlimit #topN#\r\n\t\t\t\t</isNotEmpty>\r\n\t\t\t</dynamic>\r\n\t</select>\r\n</sqlMap>  \r\n"
  },
  {
    "path": "manager/biz/src/main/resources/sqlmap/sqlmap-mapping-logrecord.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>  \n    <!DOCTYPE sqlMap PUBLIC \"-//ibatis.apache.org//DTD SQL Map 2.0//EN\"  \n       \"http://ibatis.apache.org/dtd/sql-map-2.dtd\">\n\n<sqlMap namespace=\"com.alibaba.otter.manager.biz.config.record.dal.dataobject.LogRecordDO\">\n\t<typeAlias alias=\"logRecord\"\n\t\ttype=\"com.alibaba.otter.manager.biz.config.record.dal.dataobject.LogRecordDO\" />\n\n\t<resultMap id=\"logRecordResult\" class=\"logRecord\">\n\t\t<result property=\"id\" column=\"ID\" />\n\t\t<result property=\"channelId\" column=\"CHANNEL_ID\" />\n\t\t<result property=\"pipelineId\" column=\"PIPELINE_ID\" />\n\t\t<result property=\"nid\" column=\"NID\" />\n\t\t<result property=\"title\" column=\"TITLE\" />\n\t\t<result property=\"message\" column=\"MESSAGE\" />\n\t\t<result property=\"gmtCreate\" column=\"GMT_CREATE\" />\n\t\t<result property=\"gmtModified\" column=\"GMT_MODIFIED\" />\n\t</resultMap>\n\t\n\t<resultMap id=\"logRecordWithoutMessageResult\" class=\"logRecord\">\n\t\t<result property=\"id\" column=\"ID\" />\n\t\t<result property=\"channelId\" column=\"CHANNEL_ID\" />\n\t\t<result property=\"pipelineId\" column=\"PIPELINE_ID\" />\n\t\t<result property=\"nid\" column=\"NID\" />\n\t\t<result property=\"title\" column=\"TITLE\" />\n\t\t<result property=\"gmtCreate\" column=\"GMT_CREATE\" />\n\t\t<result property=\"gmtModified\" column=\"GMT_MODIFIED\" />\n\t</resultMap>\n\n\t<!-- all columns -->\n\t<sql id=\"allLogRecordColumns\">ID,CHANNEL_ID,PIPELINE_ID,NID,TITLE,MESSAGE,GMT_CREATE,GMT_MODIFIED</sql>\n\t\n\t<!-- all columns -->\n\t<sql id=\"allLogRecordColumnsWithoutMessage\">ID,CHANNEL_ID,PIPELINE_ID,NID,TITLE,GMT_CREATE,GMT_MODIFIED</sql>\n\t\n\t<select id=\"findLogRecordById\" resultMap=\"logRecordResult\"\n\t\tparameterClass=\"long\">\n\t\tselect\n\t\t<include refid=\"allLogRecordColumns\" />\n\t\tfrom LOG_RECORD where ID = #value#\n\t</select>\n\t\n\t<select id=\"getLogRecordCount\" resultClass=\"Integer\">\n\t\tselect count(*) from LOG_RECORD \n\t\t<dynamic prepend=\"where\">\n\t\t\t<isNotEmpty property=\"searchKey\">\n\t\t\tCHANNEL_ID like concat('%',replace(#searchKey#,'_','\\_'),'%') \n\t\t\tor PIPELINE_ID like concat('%',replace(#searchKey#,'_','\\_'),'%')\n\t\t\t</isNotEmpty>\n\t\t</dynamic>\n\t</select>\n\t\n\t<select id=\"getLogRecordCountWithPIdAndSearchKey\" resultClass=\"Integer\">\n\t\tselect count(*) from LOG_RECORD where 1 = 1 \n\t\t<dynamic>\n\t\t\t<isNotEmpty property=\"pipelineId\" prepend=\"and\">\n\t\t\t\tPIPELINE_ID = #pipelineId#\n\t\t\t</isNotEmpty>\n\t\t\t<isNotEmpty property=\"monitorName\" prepend=\"and\">\n\t\t\t\tTITLE = #monitorName# \n\t\t\t</isNotEmpty>\n\t\t\t<isNotEmpty property=\"searchKey\" prepend=\"and\">\n\t\t\t\tMESSAGE like concat('%',replace(#searchKey#,'_','\\_'),'%')\n\t\t\t</isNotEmpty>\n\t\t</dynamic>\n\t</select>\n\t\n\t<select id=\"listLogRecordsByPipelineId\" resultMap=\"logRecordResult\" >\n\t\tselect\n\t\t<include refid=\"allLogRecordColumns\" />\n\t\tfrom LOG_RECORD where PIPELINE_ID = #value# \n\t\tORDER BY ID DESC\n\t</select>\t\n\t\n\t<select id=\"listLogRecordsByPipelineIdWithoutContent\" resultMap=\"logRecordWithoutMessageResult\" >\n\t\tselect\n\t\t<include refid=\"allLogRecordColumnsWithoutMessage\" />\n\t\tfrom LOG_RECORD where PIPELINE_ID = #value# \n\t\tORDER BY ID DESC\n\t</select>\t\n\t\n\t<select id=\"listLogRecords\" resultMap=\"logRecordResult\">\n\t\tselect\n\t\t<include refid=\"allLogRecordColumns\" />\n\t\tfrom LOG_RECORD \n\t\t<dynamic prepend=\"where\">\n\t\t\t<isNotEmpty property=\"searchKey\">\n\t\t\tCHANNEL_ID like concat('%',replace(#searchKey#,'_','\\_'),'%') \n\t\t\tor PIPELINE_ID like concat('%',replace(#searchKey#,'_','\\_'),'%')\n\t\t\t</isNotEmpty>\n\t\t</dynamic>\n\t\n\t\tORDER BY ID DESC\n\t\t\n\t\t<dynamic>\n\t\t\t<isNotEmpty property=\"offset\" >\n\t\t\t\t<isNotEmpty property=\"length\">\n\t\t\t\t\tlimit #offset#, #length#\n\t\t\t\t</isNotEmpty>\n\t\t\t</isNotEmpty>\n\t\t</dynamic>\n\t</select>\t\n\t\n\t<select id=\"listLogRecordsWithCondition\" resultMap=\"logRecordResult\">\n\t\tselect\n\t\t<include refid=\"allLogRecordColumns\" />\n\t\tfrom LOG_RECORD where 1 = 1 \n\t\t<dynamic>\n\t\t\t<isNotEmpty property=\"pipelineId\" prepend=\"and\">\n\t\t\t\tPIPELINE_ID = #pipelineId#\n\t\t\t</isNotEmpty>\n\t\t\t<isNotEmpty property=\"monitorName\" prepend=\"and\">\n\t\t\t\tTITLE = #monitorName# \n\t\t\t</isNotEmpty>\n\t\t\t<isNotEmpty property=\"searchKey\" prepend=\"and\">\n\t\t\t\tMESSAGE like concat('%',replace(#searchKey#,'_','\\_'),'%')\n\t\t\t</isNotEmpty>\n\t\t</dynamic>\n\t\tORDER BY ID DESC\n\t\t<dynamic>\n\t\t\t<isNotEmpty property=\"offset\" >\n\t\t\t\t<isNotEmpty property=\"length\">\n\t\t\t\t\tlimit #offset#, #length#\n\t\t\t\t</isNotEmpty>\n\t\t\t</isNotEmpty>\n\t\t</dynamic>\n\t</select>\t\n\n\n\t<update id=\"updateLogRecord\" parameterClass=\"logRecord\">\n\t\tupdate LOG_RECORD\n\t\tset\n\t\tCHANNEL_ID=#channelId#,\n\t\tPIPELINE_ID=#pipelineId#,\n\t\tNID=#nid#,\n\t\tTITLE=#title#,\n\t\tMESSAGE=#message#,\n\t\tGMT_MODIFIED=now()\n\t\tWHERE ID = #id#  \n\t</update>\n\t\n\t\n\t<insert id=\"insertLogRecord\" parameterClass=\"logRecord\">\n\t\tinsert into LOG_RECORD\n\t\t(CHANNEL_ID,PIPELINE_ID,NID,TITLE,MESSAGE,GMT_CREATE,GMT_MODIFIED)\n\t\tvalues (#channelId#,#pipelineId#,#nid#,#title#,#message#,now(),now())\n\t</insert>\n \n\t<delete id=\"deleteLogRecordById\" parameterClass=\"long\">\n    \tdelete from LOG_RECORD where ID = #value#\n    </delete>\n\n</sqlMap>  "
  },
  {
    "path": "manager/biz/src/main/resources/sqlmap/sqlmap-mapping-node.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>  \n    <!DOCTYPE sqlMap PUBLIC \"-//ibatis.apache.org//DTD SQL Map 2.0//EN\"  \n       \"http://ibatis.apache.org/dtd/sql-map-2.dtd\">\n\n<sqlMap\n\tnamespace=\"com.alibaba.otter.manager.biz.config.node.dal.dataobject.NodeDO\">\n\t<typeAlias alias=\"node\"\n\t\ttype=\"com.alibaba.otter.manager.biz.config.node.dal.dataobject.NodeDO\" />\n\n\t<resultMap id=\"nodeResult\" class=\"node\">\n\t\t<result property=\"id\" column=\"ID\" />\n\t\t<result property=\"name\" column=\"NAME\" />\n\t\t<result property=\"ip\" column=\"IP\" />\n\t\t<result property=\"port\" column=\"PORT\" />\n\t\t<result property=\"description\" column=\"DESCRIPTION\" />\n\t\t<result property=\"parameters\" column=\"PARAMETERS\" />\n\t\t<result property=\"gmtCreate\" column=\"GMT_CREATE\" />\n\t\t<result property=\"gmtModified\" column=\"GMT_MODIFIED\" />\n\t</resultMap>\n\n\t<!-- all node columns -->\n\t<sql id=\"allNodeColumns\">ID,NAME,IP,PORT,DESCRIPTION,PARAMETERS,GMT_CREATE,GMT_MODIFIED\n\t</sql>\n\n\t<select id=\"findNodeById\" resultMap=\"nodeResult\" parameterClass=\"long\">\n\t\tselect\n\t\t<include refid=\"allNodeColumns\" />\n\t\tfrom NODE where ID = #value#\n\t</select>\n\n\t<select id=\"getNodeCount\" resultClass=\"Integer\">\n\t\tselect count(*) from NODE \n\t\t<dynamic prepend=\"where\">\n\t\t\t<isNotEmpty property=\"searchKey\">\n\t\t\t\tNAME like concat('%',replace(#searchKey#,'_','\\_'),'%')\n\t\t\t\tor ID like concat('%',replace(#searchKey#,'_','\\_'),'%')\n\t\t\t\tor ip like concat('%',replace(#searchKey#,'_','\\_'),'%')\n\t\t\t</isNotEmpty>\n\t\t</dynamic>\n\t</select>\n\n\t<select id=\"listNodes\" resultMap=\"nodeResult\">\n\t\tselect\n\t\t<include refid=\"allNodeColumns\" />\n\t\tfrom NODE\n\t\t<dynamic prepend=\"where\">\n\t\t\t<isNotEmpty property=\"searchKey\">\n\t\t\t\tNAME like concat('%',replace(#searchKey#,'_','\\_'),'%')\n\t\t\t\tor ID like concat('%',replace(#searchKey#,'_','\\_'),'%')\n\t\t\t\tor ip like concat('%',replace(#searchKey#,'_','\\_'),'%')\n\t\t\t</isNotEmpty>\n\t\t</dynamic>\n\n\t\tORDER BY ID DESC\n\n\t\t<dynamic>\n\t\t\t<isNotEmpty property=\"offset\">\n\t\t\t\t<isNotEmpty property=\"length\">\n\t\t\t\t\tlimit #offset#, #length#\n\t\t\t\t</isNotEmpty>\n\t\t\t</isNotEmpty>\n\t\t</dynamic>\n\t</select>\n\n\t<select id=\"listNodeByIds\" resultMap=\"nodeResult\">\n\t\tselect\n\t\t<include refid=\"allNodeColumns\" />\n\t\tfrom NODE where ID in\n\t\t<iterate open=\"(\" close=\")\" conjunction=\",\">\n\t\t\t#[]#\n\t\t</iterate>\n\t\tORDER BY ID DESC\n\t</select>\n\n\t<update id=\"updateNode\" parameterClass=\"node\"><![CDATA[\n\t\tupdate NODE\n\t\tset\n\t\tNAME=#name#,\n\t\tIP=#ip#,\n\t\tPORT=#port#,\n\t\tDESCRIPTION=#description#,\n\t\tPARAMETERS=#parameters#,\n\t\tGMT_MODIFIED=now()\n\t\tWHERE ID = #id#\n\t]]></update>\n\n\t<select id=\"checkNodeUnique\" resultClass=\"Integer\"\n\t\tparameterClass=\"node\">\n\t\tselect count(*) from NODE\n\t\twhere NODE.ID != #id#\n\t\tand\n\t\tNODE.NAME = #name#\n\t\tand NODE.IP = #ip#\n\t</select>\n\n\t<insert id=\"insertNode\" parameterClass=\"node\">\n\t\tinsert into NODE\n\t\t(NAME,IP, PORT, DESCRIPTION, PARAMETERS,GMT_CREATE,\n\t\tGMT_MODIFIED)\n\t\tSELECT\n\t\t#name#,#ip#,#port#,#description#,#parameters#,now(),now()\n\t\tfrom dual\n\t\tWHERE not exists (select * from NODE\n\t\twhere NODE.NAME = #name# or\n\t\t( NODE.IP = #ip# and\n\t\tNODE.PORT = #port# )\n\t\t);\n\t\t<selectKey keyProperty=\"id\" resultClass=\"long\">\n\t\t\tselect\n\t\t\tlast_insert_id()\n\t\t</selectKey>\n\t</insert>\n\n\t<delete id=\"deleteNodeById\" parameterClass=\"long\"><![CDATA[\n    \tdelete from NODE where ID = #value#\n    ]]></delete>\n\n</sqlMap>  "
  },
  {
    "path": "manager/biz/src/main/resources/sqlmap/sqlmap-mapping-pipeline.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>  \n    <!DOCTYPE sqlMap PUBLIC \"-//ibatis.apache.org//DTD SQL Map 2.0//EN\"  \n       \"http://ibatis.apache.org/dtd/sql-map-2.dtd\">\n\n<sqlMap namespace=\"com.alibaba.otter.manager.biz.config.pipeline.dal.dataobject.PipelineDO\">\n\t<typeAlias alias=\"pipeline\"\n\t\ttype=\"com.alibaba.otter.manager.biz.config.pipeline.dal.dataobject.PipelineDO\" />\n\n\t<resultMap id=\"pipelineResult\" class=\"pipeline\">\n\t\t<result property=\"id\" column=\"ID\" />\n\t\t<result property=\"name\" column=\"NAME\" />\n\t\t<result property=\"parameters\" column=\"PARAMETERS\" />\n\t\t<result property=\"description\" column=\"DESCRIPTION\" />\n\t\t<result property=\"channelId\" column=\"CHANNEL_ID\" />\n\t\t<result property=\"gmtCreate\" column=\"GMT_CREATE\" />\n\t\t<result property=\"gmtModified\" column=\"GMT_MODIFIED\" />\n\t</resultMap>\n\n\t<!-- all pipeline columns -->\n\t<sql id=\"allPipelineColumns\">ID,NAME,PARAMETERS,DESCRIPTION,CHANNEL_ID,GMT_CREATE,GMT_MODIFIED\n\t</sql>\n\n\n\t<select id=\"findPipelineById\" resultMap=\"pipelineResult\"\n\t\tparameterClass=\"long\">\n\t\tselect\n\t\t<include refid=\"allPipelineColumns\" />\n\t\tfrom PIPELINE where ID = #value#\n\t</select>\n\n\t<select id=\"getPipelineCount\" resultClass=\"Integer\">\n\t\tselect count(*) from PIPELINE \n\t\t<dynamic prepend=\"where\">\n\t\t\t<isNotEmpty property=\"searchKey\">\n\t\t\tNAME like concat('%',replace(#searchKey#,'_','\\_'),'%') \n\t\t\tor ID like concat('%',replace(#searchKey#,'_','\\_'),'%')\n\t\t\t</isNotEmpty>\n\t\t</dynamic>\n\t</select>\n\n\t<select id=\"listPipelinesByChannelIds\" resultMap=\"pipelineResult\">\n\t\tselect\n\t\t<include refid=\"allPipelineColumns\" />\n\t\tfrom PIPELINE where CHANNEL_ID in\n\t\t<iterate open=\"(\" close=\")\" conjunction=\",\">\n\t\t\t#[]# \n\t\t</iterate>\n\t\tORDER BY ID DESC\n\t</select>\n\t\n\t<select id=\"listPipelines\" resultMap=\"pipelineResult\" parameterClass=\"long\">\n\t\tselect\n\t\t<include refid=\"allPipelineColumns\" />\n\t\tfrom PIPELINE \n\t\t<dynamic prepend=\"where\">\n\t\t\t<isNotEmpty property=\"searchKey\">\n\t\t\tNAME like concat('%',replace(#searchKey#,'_','\\_'),'%') \n\t\t\tor ID like concat('%',replace(#searchKey#,'_','\\_'),'%')\n\t\t\t</isNotEmpty>\n\t\t</dynamic>\n\t\n\t\tORDER BY ID DESC\n\t\t\n\t\t<dynamic>\n\t\t\t<isNotEmpty property=\"offset\" >\n\t\t\t\t<isNotEmpty property=\"length\">\n\t\t\t\t\tlimit #offset#, #length#\n\t\t\t\t</isNotEmpty>\n\t\t\t</isNotEmpty>\n\t\t</dynamic>\n\t</select>\n\n\t<select id=\"listPipelineByIds\" resultMap=\"pipelineResult\" >\n\t\tselect\n\t\t<include refid=\"allPipelineColumns\" />\n\t\tfrom PIPELINE where ID in\n\t\t<iterate open=\"(\" close=\")\" conjunction=\",\">\n\t\t\t#[]#\n\t\t</iterate>\n\t\tORDER BY ID DESC\n\t</select>\n\n\t<update id=\"updatePipeline\" parameterClass=\"pipeline\"><![CDATA[\n\t\tupdate PIPELINE\n\t\tset\n\t\tNAME=#name#,\n\t\tPARAMETERS=#parameters#,\n\t\tDESCRIPTION=#description#,\n\t\tCHANNEL_ID=#channelId#,\n\t\tGMT_MODIFIED = now() \n\t\tWHERE ID = #id#  \n\t]]></update>\n\t\n\t<select id=\"checkPipelineUnique\" resultClass=\"Integer\"\n\t\tparameterClass=\"pipeline\">\n\t\tselect count(*) from PIPELINE\n\t\twhere PIPELINE.ID != #id#\n\t\tand PIPELINE.CHANNEL_ID = #channelId# \n\t\tand PIPELINE.NAME = #name#\n\t</select>\n\t\n\t<insert id=\"insertPipeline\" parameterClass=\"pipeline\">\n\t\tinsert into PIPELINE\n\t\t(NAME, PARAMETERS,DESCRIPTION,CHANNEL_ID,GMT_CREATE, GMT_MODIFIED)\n\t\tSELECT #name#,#parameters#,#description#,#channelId#,now(),now()\n\t\tfrom dual\n\t\tWHERE not exists (select * from PIPELINE\n\t\twhere PIPELINE.CHANNEL_ID = #channelId# and PIPELINE.NAME = #name#); \n\t\t<selectKey keyProperty=\"id\" resultClass=\"long\">\n\t\t\tselect last_insert_id()\n\t\t</selectKey>\n\t</insert>\n\t\n\t<delete id=\"deletePipelineById\" parameterClass=\"long\"><![CDATA[\n    \tdelete from PIPELINE where ID = #value#\n    ]]></delete>\n\n\n\t<select id=\"listByDestinationCondition\" resultMap=\"pipelineResult\">\n\t\tselect\n\t\t<include refid=\"allPipelineColumns\" />\n\t\tfrom PIPELINE \n\t\t<dynamic prepend=\"where\">\n\t\t\t<isNotEmpty property=\"searchKey\">\n\t\t\tPARAMETERS like concat('%\"destinationName\":\"',replace(#searchKey#,'_','\\_'),'\"%') \n\t\t\t</isNotEmpty>\n\t\t</dynamic>\n\t\tORDER BY ID DESC\n\t\t<dynamic>\n\t\t\t<isNotEmpty property=\"offset\" >\n\t\t\t\t<isNotEmpty property=\"length\">\n\t\t\t\t\tlimit #offset#, #length#\n\t\t\t\t</isNotEmpty>\n\t\t\t</isNotEmpty>\n\t\t</dynamic>\n\t</select>\n</sqlMap>  "
  },
  {
    "path": "manager/biz/src/main/resources/sqlmap/sqlmap-mapping-pipelinenoderelation.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>  \n    <!DOCTYPE sqlMap PUBLIC \"-//ibatis.apache.org//DTD SQL Map 2.0//EN\"  \n       \"http://ibatis.apache.org/dtd/sql-map-2.dtd\">\n\n<sqlMap\n\tnamespace=\"com.alibaba.otter.manager.biz.config.pipeline.dal.dataobject.PipelineNodeRelationDO\">\n\t<typeAlias alias=\"pipelineNodeRelation\"\n\t\ttype=\"com.alibaba.otter.manager.biz.config.pipeline.dal.dataobject.PipelineNodeRelationDO\" />\n\n\t<resultMap id=\"pipelineNodeRelationResult\" class=\"pipelineNodeRelation\">\n\t\t<result property=\"id\" column=\"ID\" />\n\t\t<result property=\"nodeId\" column=\"NODE_ID\" />\n\t\t<result property=\"pipelineId\" column=\"PIPELINE_ID\" />\n\t\t<result property=\"location\" column=\"LOCATION\" typeHandler=\"\" />\n\t\t<result property=\"gmtCreate\" column=\"GMT_CREATE\" />\n\t\t<result property=\"gmtModified\" column=\"GMT_MODIFIED\" />\n\t</resultMap>\n\n\t<!-- all pipeline columns -->\n\t<sql id=\"allPipelineNodeRelationColumns\">ID,NODE_ID,PIPELINE_ID,LOCATION,GMT_CREATE,GMT_MODIFIED\n\t</sql>\n\n\n\t<select id=\"findPipelineNodeRelationById\" resultMap=\"pipelineNodeRelationResult\"\n\t\tparameterClass=\"long\">\n\t\tselect\n\t\t<include refid=\"allPipelineNodeRelationColumns\" />\n\t\tfrom PIPELINE_NODE_RELATION where ID = #value#\n\t</select>\n\n\t<select id=\"getRelationCount\" resultClass=\"Integer\">\n\t\tselect count(*) from PIPELINE_NODE_RELATION\n\t</select>\n\n\t<select id=\"listRelationsByNodeId\" resultMap=\"pipelineNodeRelationResult\"\n\t\tparameterClass=\"long\">\n\t\tselect\n\t\t<include refid=\"allPipelineNodeRelationColumns\" />\n\t\tfrom PIPELINE_NODE_RELATION where NODE_ID = #value# ORDER BY ID DESC\n\t</select>\n\n\t<select id=\"listRelationsByPipelineIds\" resultMap=\"pipelineNodeRelationResult\">\n\t\tselect\n\t\t<include refid=\"allPipelineNodeRelationColumns\" />\n\t\tfrom PIPELINE_NODE_RELATION where PIPELINE_ID in\n\t\t<iterate open=\"(\" close=\")\" conjunction=\",\">\n\t\t\t#[]#\n\t\t</iterate>\n\t\t ORDER BY ID DESC\n\t</select>\n\n\t<update id=\"updatePipelineNodeRelation\" parameterClass=\"pipelineNodeRelation\"><![CDATA[\n\t\tupdate PIPELINE_NODE_RELATION\n\t\tset\n\t\tNODE_ID=#nodeId#,\n\t\tPIPELINE_ID=#pipelineId#,\n\t\tLOCATION=#location#,\n\t\tGMT_MODIFIED = now() \n\t\tWHERE ID = #id#  \n\t]]></update>\n\n\t<insert id=\"insertPipelineNodeRelation\" parameterClass=\"pipelineNodeRelation\">\n\t\tinsert into PIPELINE_NODE_RELATION\n\t\t(NODE_ID, PIPELINE_ID,LOCATION,\n\t\tGMT_CREATE, GMT_MODIFIED)\n\t\tvalues\n\t\t(#nodeId#,#pipelineId#,#location#,now(),now())\n\t\t<selectKey keyProperty=\"id\" resultClass=\"long\">\n\t\t\tselect\n\t\t\tlast_insert_id()\n\t\t</selectKey>\n\t</insert>\n\n\t<delete id=\"deletePipelineNodeRelationById\" parameterClass=\"long\"><![CDATA[\n    \tdelete from PIPELINE_NODE_RELATION where ID = #value#\n    ]]></delete>\n\n\n\t<delete id=\"deleteRelationByPipelineId\" parameterClass=\"long\"><![CDATA[\n    \tdelete from PIPELINE_NODE_RELATION where PIPELINE_ID = #value#\n    ]]></delete>\n    \n</sqlMap>  "
  },
  {
    "path": "manager/biz/src/main/resources/sqlmap/sqlmap-mapping-systemParameter.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>  \n    <!DOCTYPE sqlMap PUBLIC \"-//ibatis.apache.org//DTD SQL Map 2.0//EN\"  \n       \"http://ibatis.apache.org/dtd/sql-map-2.dtd\">\n\n<sqlMap\n\tnamespace=\"com.alibaba.otter.manager.biz.config.parameter.dal.dataobject.SystemParameterDO\">\n\t<typeAlias alias=\"systemParameter\"\n\t\ttype=\"com.alibaba.otter.manager.biz.config.parameter.dal.dataobject.SystemParameterDO\" />\n\n\t<resultMap id=\"systemParameterResult\" class=\"systemParameter\">\n\t    <result property=\"id\" column=\"ID\" />\n\t    <result property=\"value\" column=\"VALUE\" />\n\t\t<result property=\"gmtCreate\" column=\"GMT_CREATE\" />\n\t\t<result property=\"gmtModified\" column=\"GMT_MODIFIED\" />\n\t</resultMap>\n\n\t<!-- all parameter columns -->\n\t<sql id=\"allParameterColumns\">ID,VALUE,GMT_CREATE,GMT_MODIFIED</sql>\n\n\t<select id=\"listParameters\" resultMap=\"systemParameterResult\" >\n\t\tselect\n\t\t<include refid=\"allParameterColumns\" />\n\t\tfrom SYSTEM_PARAMETER  \n\t</select>\n\n\t<insert id=\"insertParameter\" parameterClass=\"systemParameter\">\n\tinsert into SYSTEM_PARAMETER (ID,VALUE,GMT_CREATE,GMT_MODIFIED) values(#id#,#value#,#gmtCreate#,#gmtModified#) \n\ton duplicate key update VALUE = values(VALUE), GMT_CREATE = values(GMT_CREATE),GMT_MODIFIED = values(GMT_MODIFIED);\n\t</insert>\n</sqlMap>  "
  },
  {
    "path": "manager/biz/src/main/resources/sqlmap/sqlmap-mapping-tableHistoryStat.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>  \r\n    <!DOCTYPE sqlMap PUBLIC \"-//ibatis.apache.org//DTD SQL Map 2.0//EN\"  \r\n       \"http://ibatis.apache.org/dtd/sql-map-2.dtd\">\r\n\r\n<sqlMap namespace=\"com.alibaba.otter.manager.biz.statistics.table.dal.dataobject.TableHistoryStatDO\">\r\n\t<typeAlias alias=\"tableHistoryStat\"\r\n\t\ttype=\"com.alibaba.otter.manager.biz.statistics.table.dal.dataobject.TableHistoryStatDO\" />\r\n\t<typeAlias alias=\"timelineBehaviorHistoryCondition\"\r\n\t\ttype=\"com.alibaba.otter.manager.biz.statistics.table.param.TimelineBehaviorHistoryCondition\" />\r\n\r\n\r\n\t\r\n\t<resultMap id=\"tableHistoryStatResult\" class=\"tableHistoryStat\">\r\n\t\t<result property=\"id\" column=\"ID\" />\r\n\t\t<result property=\"pipelineId\" column=\"PIPELINE_ID\" />\r\n\t\t<result property=\"dataMediaPairId\" column=\"DATA_MEDIA_PAIR_ID\" />\r\n\t\t<result property=\"startTime\" column=\"START_TIME\" />\r\n\t\t<result property=\"endTime\" column=\"END_TIME\" />\r\n\t\t<result property=\"fileSize\" column=\"FILE_SIZE\" />\r\n\t\t<result property=\"fileCount\" column=\"FILE_COUNT\" />\r\n\t\t<result property=\"deleteCount\" column=\"DELETE_COUNT\" />\r\n\t\t<result property=\"updateCount\" column=\"UPDATE_COUNT\" />\r\n\t\t<result property=\"insertCount\" column=\"INSERT_COUNT\" />\r\n\t\t<result property=\"gmtCreate\" column=\"GMT_CREATE\" />\r\n\t\t<result property=\"gmtModified\" column=\"GMT_MODIFIED\" />\r\n\t</resultMap>\r\n\r\n\t<!-- all tableHistoryStat columns -->\r\n\t<sql id=\"allTableHistoryStatColumns\">ID,PIPELINE_ID,DATA_MEDIA_PAIR_ID,START_TIME,END_TIME,FILE_SIZE,FILE_COUNT,DELETE_COUNT,UPDATE_COUNT,INSERT_COUNT,GMT_CREATE,GMT_MODIFIED</sql>\r\n\t\r\n    <insert id=\"insertTableHistoryStat\" parameterClass=\"tableHistoryStat\">\r\n\t\tinsert into TABLE_HISTORY_STAT\r\n\t\t(PIPELINE_ID,DATA_MEDIA_PAIR_ID,FILE_SIZE,FILE_COUNT,DELETE_COUNT,UPDATE_COUNT,INSERT_COUNT,START_TIME,END_TIME,GMT_CREATE,GMT_MODIFIED)\r\n\t\tvalues\r\n\t\t(#pipelineId#,#dataMediaPairId#,#fileSize#,#fileCount#,#deleteCount#,#updateCount#,#insertCount#,#startTime#,#endTime#,now(),now())\r\n\t\t<selectKey keyProperty=\"id\" resultClass=\"long\">\r\n\t\t\tselect last_insert_id()\r\n\t\t</selectKey>\r\n\t</insert>\r\n    \r\n    <!-- listTimelineTableStat -->\r\n\t<select id=\"listTimelineTableStat\" resultMap=\"tableHistoryStatResult\" parameterClass=\"timelineBehaviorHistoryCondition\">\r\n\t\tselect\r\n\t\t<include refid=\"allTableHistoryStatColumns\" />\r\n\t\tfrom TABLE_HISTORY_STAT \r\n\t\twhere DATA_MEDIA_PAIR_ID=#pairId# and <![CDATA[ END_TIME>= #start# and END_TIME <= #end#]]>\r\n\t\tORDER BY END_TIME DESC,ID DESC\r\n\t</select>\r\n\r\n</sqlMap>  "
  },
  {
    "path": "manager/biz/src/main/resources/sqlmap/sqlmap-mapping-tableStat.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>  \r\n    <!DOCTYPE sqlMap PUBLIC \"-//ibatis.apache.org//DTD SQL Map 2.0//EN\"  \r\n       \"http://ibatis.apache.org/dtd/sql-map-2.dtd\">\r\n\r\n<sqlMap namespace=\"com.alibaba.otter.manager.biz.statistics.table.dal.dataobject.TableStatDO\">\r\n\t<typeAlias alias=\"tableStat\"\r\n\t\ttype=\"com.alibaba.otter.manager.biz.statistics.table.dal.dataobject.TableStatDO\" />\r\n\t<typeAlias alias=\"timelineBehaviorHistoryCondition\"\r\n\t\ttype=\"com.alibaba.otter.manager.biz.statistics.table.param.TimelineBehaviorHistoryCondition\" />\r\n\r\n\t<resultMap id=\"tableStatResult\" class=\"tableStat\">\r\n\t\t<result property=\"id\" column=\"ID\" />\r\n\t\t<result property=\"pipelineId\" column=\"PIPELINE_ID\" />\r\n\t\t<result property=\"dataMediaPairId\" column=\"DATA_MEDIA_PAIR_ID\" />\r\n\t\t<result property=\"fileSize\" column=\"FILE_SIZE\" />\r\n\t\t<result property=\"fileCount\" column=\"FILE_COUNT\" />\r\n\t\t<result property=\"deleteCount\" column=\"DELETE_COUNT\" />\r\n\t\t<result property=\"updateCount\" column=\"UPDATE_COUNT\" />\r\n\t\t<result property=\"insertCount\" column=\"INSERT_COUNT\" />\r\n\t\t<result property=\"gmtCreate\" column=\"GMT_CREATE\" />\r\n\t\t<result property=\"gmtModified\" column=\"GMT_MODIFIED\" />\r\n\t</resultMap>\r\n\t\r\n\t<!-- all tableStat columns -->\r\n\t<sql id=\"allTableStatColumns\">ID,PIPELINE_ID,DATA_MEDIA_PAIR_ID,FILE_SIZE,FILE_COUNT,DELETE_COUNT,UPDATE_COUNT,INSERT_COUNT,GMT_CREATE,GMT_MODIFIED</sql>\r\n\r\n\t<!-- findTableStatById -->\r\n\t<select id=\"findTableStatById\" resultMap=\"tableStatResult\" parameterClass=\"long\">\r\n\t\tselect\r\n\t\t<include refid=\"allTableStatColumns\" />\r\n\t\tfrom TABLE_STAT where ID = #value#\r\n\t</select>\t\r\n\t\r\n\t<!-- findTableStatByPipelineIdAndDataMediaPairId -->\r\n\t<select id=\"findTableStatByPipelineIdAndDataMediaPairId\" resultMap=\"tableStatResult\" parameterClass=\"tableStat\">\r\n\t\tselect\r\n\t\t<include refid=\"allTableStatColumns\" />\r\n\t\tfrom TABLE_STAT where PIPELINE_ID=#pipelineId# and DATA_MEDIA_PAIR_ID=#dataMediaPairId#\r\n\t</select>\t\t\r\n\r\n\t<!-- listAllTableStat -->\r\n\t<select id=\"listAllTableStat\" resultMap=\"tableStatResult\">\r\n\t\tselect\r\n\t\t<include refid=\"allTableStatColumns\" />\r\n\t\tfrom TABLE_STAT ORDER BY GMT_CREATE DESC,ID DESC\r\n\t</select>\r\n\t\r\n\t<!-- listTableStatsByPipelineId -->\r\n\t<select id=\"listTableStatsByPipelineId\" resultMap=\"tableStatResult\" parameterClass=\"long\">\r\n\t\tselect\r\n\t\t<include refid=\"allTableStatColumns\" />\r\n\t\tfrom TABLE_STAT \r\n\t\twhere PIPELINE_ID=#pipelineId#\r\n\t\tORDER BY GMT_CREATE DESC,ID DESC\r\n\t</select>\r\n\t\r\n\t<!-- listTableStatsByDataMediaPairId -->\r\n\t<select id=\"listTableStatsByDataMediaPairId\" resultMap=\"tableStatResult\" parameterClass=\"long\">\r\n\t\tselect\r\n\t\t<include refid=\"allTableStatColumns\" />\r\n\t\tfrom TABLE_STAT \r\n\t\twhere DATA_MEDIA_PAIR_ID=#dataMediaPairId#\r\n\t\tORDER BY GMT_CREATE DESC,ID DESC\r\n\t</select>\r\n\r\n\t<update id=\"modifyTableStat\" parameterClass=\"tableStat\"><![CDATA[\r\n\t\tupdate TABLE_STAT\r\n\t\tset\r\n\t\tFILE_SIZE=FILE_SIZE+#fileSize#,\r\n\t\tFILE_COUNT=FILE_COUNT+#fileCount#,\r\n\t\tDELETE_COUNT=DELETE_COUNT+#deleteCount#,\r\n\t\tUPDATE_COUNT=UPDATE_COUNT+#updateCount#,\r\n\t\tINSERT_COUNT=INSERT_COUNT+#insertCount#,\r\n\t\tGMT_MODIFIED=now()\r\n\t\tWHERE PIPELINE_ID=#pipelineId# and DATA_MEDIA_PAIR_ID=#dataMediaPairId#\r\n\t]]></update>\r\n\r\n\t<insert id=\"insertTableStat\" parameterClass=\"tableStat\">\r\n\t\tinsert into TABLE_STAT\r\n\t\t(PIPELINE_ID,DATA_MEDIA_PAIR_ID,FILE_SIZE,FILE_COUNT,DELETE_COUNT,UPDATE_COUNT,INSERT_COUNT,GMT_CREATE,GMT_MODIFIED)\r\n\t\tvalues\r\n\t\t(#pipelineId#,#dataMediaPairId#,#fileSize#,#fileCount#,#deleteCount#,#updateCount#,#insertCount#,now(),now())\r\n\t\t<selectKey keyProperty=\"id\" resultClass=\"long\">\r\n\t\t\tselect last_insert_id()\r\n\t\t</selectKey>\r\n\t</insert>\r\n\r\n\t<delete id=\"deleteTableStatById\" parameterClass=\"long\"><![CDATA[\r\n    \tdelete from TABLE_STAT where ID = #value#\r\n    ]]></delete>\r\n    \r\n</sqlMap>  "
  },
  {
    "path": "manager/biz/src/main/resources/sqlmap/sqlmap-mapping-throughputStat.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>  \r\n    <!DOCTYPE sqlMap PUBLIC \"-//ibatis.apache.org//DTD SQL Map 2.0//EN\"  \r\n       \"http://ibatis.apache.org/dtd/sql-map-2.dtd\">\r\n\r\n<sqlMap namespace=\"com.alibaba.otter.manager.biz.statistics.throughput.dal.dataobject.ThroughputStatDO\">\r\n\t<typeAlias alias=\"throughputStat\"\r\n\t\ttype=\"com.alibaba.otter.manager.biz.statistics.throughput.dal.dataobject.ThroughputStatDO\" />\r\n\t<typeAlias alias=\"throughputCondition\"\r\n\t\ttype=\"com.alibaba.otter.manager.biz.statistics.throughput.param.ThroughputCondition\" />\r\n\t<typeAlias alias=\"realtimeThroughputCondition\"\r\n\t\ttype=\"com.alibaba.otter.manager.biz.statistics.throughput.param.RealtimeThroughputCondition\" />\r\n\t<typeAlias alias=\"timelineThroughputCondition\"\r\n\t\ttype=\"com.alibaba.otter.manager.biz.statistics.throughput.param.TimelineThroughputCondition\" />\r\n\r\n\t<resultMap id=\"throughputStatResult\" class=\"throughputStat\">\r\n\t\t<result property=\"id\" column=\"ID\" />\r\n\t\t<result property=\"pipelineId\" column=\"PIPELINE_ID\" />\r\n\t\t<result property=\"startTime\" column=\"START_TIME\" />\r\n\t\t<result property=\"endTime\" column=\"END_TIME\" />\r\n\t\t<result property=\"type\" column=\"TYPE\" />\r\n\t\t<result property=\"number\" column=\"NUMBER\" />\r\n\t\t<result property=\"size\" column=\"SIZE\" />\r\n\t\t<result property=\"gmtCreate\" column=\"GMT_CREATE\" />\r\n\t\t<result property=\"gmtModified\" column=\"GMT_MODIFIED\" />\r\n\t</resultMap>\r\n\t\r\n\t<resultMap id=\"throughputStatSimpleResult\" class=\"throughputStat\">\r\n\t\t<result property=\"pipelineId\" column=\"PIPELINE_ID\" />\r\n\t\t<result property=\"type\" column=\"TYPE\" />\r\n\t\t<result property=\"number\" column=\"NUMBER\" />\r\n\t\t<result property=\"size\" column=\"SIZE\" />\r\n\t</resultMap>\r\n\r\n\t<!-- all throughputStat columns -->\r\n\t<sql id=\"allThroughputStatColumns\">ID,PIPELINE_ID,START_TIME,END_TIME,TYPE,NUMBER,SIZE,GMT_CREATE,GMT_MODIFIED</sql>\r\n\r\n\t<!-- findThroughputStatById -->\r\n\t<select id=\"findThroughputStatById\" resultMap=\"throughputStatResult\" parameterClass=\"long\">\r\n\t\tselect\r\n\t\t<include refid=\"allThroughputStatColumns\" />\r\n\t\tfrom THROUGHPUT_STAT where ID = #value#\r\n\t</select>\t\t\r\n\t\r\n\t<!-- listRealtimeThroughputStat -->\r\n\t<select id=\"listRealtimeThroughputStat\" resultMap=\"throughputStatResult\" parameterClass=\"realtimeThroughputCondition\">\r\n\t\tselect\r\n\t\t<include refid=\"allThroughputStatColumns\" />\r\n\t\tfrom THROUGHPUT_STAT \r\n\t\twhere PIPELINE_ID=#pipelineId# and TYPE=#type# and <![CDATA[ END_TIME >= now() - INTERVAL #max# MINUTE ]]>\r\n\t\tORDER BY END_TIME DESC,ID DESC\r\n\t</select>\r\n\r\n\t<!-- listTimelineThroughputStat -->\r\n\t<select id=\"listTimelineThroughputStat\" resultMap=\"throughputStatResult\" parameterClass=\"timelineThroughputCondition\">\r\n\t\tselect\r\n\t\t<include refid=\"allThroughputStatColumns\" />\r\n\t\tfrom THROUGHPUT_STAT \r\n\t\twhere PIPELINE_ID=#pipelineId# and TYPE=#type# and <![CDATA[ END_TIME>= #start# and END_TIME <= #end#]]>\r\n\t\tORDER BY END_TIME DESC,ID DESC\r\n\t</select>\r\n\t\r\n\t<!-- listAllThroughputStat -->\r\n\t<select id=\"listAllThroughputStat\" resultMap=\"throughputStatResult\">\r\n\t\tselect\r\n\t\t<include refid=\"allThroughputStatColumns\" />\r\n\t\tfrom THROUGHPUT_STAT ORDER BY END_TIME DESC,ID DESC\r\n\t</select>\r\n\t\r\n\t<!-- findRealtimeThroughputStat -->\r\n\t<select id=\"findRealtimeThroughputStat\" resultMap=\"throughputStatResult\" parameterClass=\"throughputCondition\">\r\n\t\tselect\r\n\t\t<include refid=\"allThroughputStatColumns\" />\r\n\t\tfrom THROUGHPUT_STAT \r\n\t\twhere PIPELINE_ID=#pipelineId# and TYPE=#type#\r\n\t\tORDER BY END_TIME DESC,ID DESC limit 1\r\n\t</select>\r\n\t\r\n\t<!-- listThroughputStatByPipelineId -->\r\n\t<select id=\"listThroughputStatByPipelineId\" resultMap=\"throughputStatResult\" parameterClass=\"long\">\r\n\t\tselect\r\n\t\t<include refid=\"allThroughputStatColumns\" />\r\n\t\tfrom THROUGHPUT_STAT \r\n\t\twhere PIPELINE_ID=#pipelineId#\r\n\t\tORDER BY END_TIME DESC,ID DESC\r\n\t</select>\r\n\r\n\t<update id=\"modifyThroughputStat\" parameterClass=\"throughputStat\"><![CDATA[\r\n\t\tupdate THROUGHPUT_STAT\r\n\t\tset\r\n\t\tPIPELINE_ID=#pipelineId#,\r\n\t\tSTART_TIME=#startTime#,\r\n\t\tEND_TIME=#endTime#,\r\n\t\tTYPE=#type#,\r\n\t\tNUMBER=#number#,\r\n\t\tSIZE=#size#,\r\n\t\tGMT_MODIFIED=now()\r\n\t\tWHERE ID = #id#\r\n\t]]></update>\r\n\r\n\t<insert id=\"insertThroughputStat\" parameterClass=\"throughputStat\">\r\n\t\tinsert into THROUGHPUT_STAT\r\n\t\t(PIPELINE_ID,START_TIME,END_TIME,TYPE,NUMBER,SIZE,GMT_CREATE,GMT_MODIFIED)\r\n\t\tvalues\r\n\t\t(#pipelineId#,#startTime#,#endTime#,#type#,#number#,#size#,now(),now())\r\n\t\t<selectKey keyProperty=\"id\" resultClass=\"long\">\r\n\t\t\tselect last_insert_id()\r\n\t\t</selectKey>\r\n\t</insert>\r\n\r\n\t<delete id=\"deleteThroughputStat\" parameterClass=\"long\"><![CDATA[\r\n    \tdelete from THROUGHPUT_STAT where ID = #value#\r\n    ]]></delete>\r\n\r\n\r\n\t<!-- listThroughputStatByPipelineId -->\r\n\t<select id=\"listRealtimeThroughputStatByPipelineIds\" resultMap=\"throughputStatSimpleResult\">\r\n\t\tselect PIPELINE_ID, type , sum(NUMBER) as number , sum(SIZE) as size \r\n\t\tfrom THROUGHPUT_STAT \r\n\t\twhere PIPELINE_ID in\r\n\t\t<iterate property=\"pipelineIds\" open=\"(\" close=\")\" conjunction=\",\">\r\n\t\t\t#pipelineIds[]#\r\n\t\t</iterate>\r\n\t\tand TYPE in ('ROW','FILE') and END_TIME >= (now() - INTERVAL #minute# MINUTE)\r\n\t \tgroup by PIPELINE_ID , TYPE;\r\n\t</select>\r\n</sqlMap>  "
  },
  {
    "path": "manager/biz/src/main/resources/sqlmap/sqlmap-mapping-user.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>  \n    <!DOCTYPE sqlMap PUBLIC \"-//ibatis.apache.org//DTD SQL Map 2.0//EN\"  \n       \"http://ibatis.apache.org/dtd/sql-map-2.dtd\">\n\n<sqlMap namespace=\"com.alibaba.otter.manager.biz.user.dal.dataobject.UserDO\">\n\t<typeAlias alias=\"user\"\n\t\ttype=\"com.alibaba.otter.manager.biz.user.dal.dataobject.UserDO\" />\n\n\t<resultMap id=\"userResult\" class=\"user\">\n\t\t<result property=\"id\" column=\"ID\" />\n\t\t<result property=\"name\" column=\"USERNAME\" />\n\t\t<result property=\"password\" column=\"PASSWORD\" />\n\t\t<result property=\"department\" column=\"DEPARTMENT\" />\n\t\t<result property=\"realName\" column=\"REALNAME\" />\n\t\t<result property=\"authorizeType\" column=\"AUTHORIZETYPE\" typeHandler=\"\" />\n\t\t<result property=\"gmtCreate\" column=\"GMT_CREATE\" />\n\t\t<result property=\"gmtModified\" column=\"GMT_MODIFIED\" />\n\t</resultMap>\n\n\t<!-- all user columns -->\n\t<sql id=\"allUserColumns\">ID,USERNAME,PASSWORD,DEPARTMENT,REALNAME,AUTHORIZETYPE,GMT_CREATE,GMT_MODIFIED</sql>\n\n\t<!-- getUserById -->\n\t<select id=\"findUserById\" resultMap=\"userResult\" parameterClass=\"long\">\n\t\tselect\n\t\t<include refid=\"allUserColumns\" />\n\t\tfrom USER where ID = #value#\n\t</select>\n\n\t\n\t<select id=\"getUserCount\" resultClass=\"Integer\">\n\t\tselect count(*) from USER \n\t\t<dynamic prepend=\"where\">\n\t\t\t<isNotEmpty property=\"searchKey\">\n\t\t\tUSERNAME like concat('%',replace(#searchKey#,'_','\\_'),'%') \n\t\t\tor ID like concat('%',replace(#searchKey#,'_','\\_'),'%') \n\t\t\tor DEPARTMENT like concat('%',replace(#searchKey#,'_','\\_'),'%')\n\t\t\tor REALNAME like concat('%',replace(#searchKey#,'_','\\_'),'%')\n\t\t\t</isNotEmpty>\n\t\t</dynamic>\n\t</select>\n\n\t<!-- listUsers -->\n\t<select id=\"listUsers\" resultMap=\"userResult\">\n\t\tselect\n\t\t<include refid=\"allUserColumns\" />\n\t\tfrom USER \n\t\t<dynamic prepend=\"where\">\n\t\t\t<isNotEmpty property=\"searchKey\">\n\t\t\tUSERNAME like concat('%',replace(#searchKey#,'_','\\_'),'%') \n\t\t\tor ID like concat('%',replace(#searchKey#,'_','\\_'),'%') \n\t\t\tor DEPARTMENT like concat('%',replace(#searchKey#,'_','\\_'),'%')\n\t\t\tor REALNAME like concat('%',replace(#searchKey#,'_','\\_'),'%')\n\t\t\t</isNotEmpty>\n\t\t</dynamic>\n\t\n\t\tORDER BY ID DESC\n\t\t\n\t\t<dynamic>\n\t\t\t<isNotEmpty property=\"offset\" >\n\t\t\t\t<isNotEmpty property=\"length\">\n\t\t\t\t\tlimit #offset#, #length#\n\t\t\t\t</isNotEmpty>\n\t\t\t</isNotEmpty>\n\t\t</dynamic>\n\t</select>\n\n\t<update id=\"updateUser\" parameterClass=\"user\">\n\t\tupdate USER\n\t\tset\n\t\t<dynamic >\n\t\t\t<isNotEmpty property=\"name\">\n\t\t\t\tUSERNAME=#name#,\n\t\t\t</isNotEmpty>\n\t\t\t<isNotEmpty property=\"password\">\n\t\t\t\tPASSWORD=#password#,\n\t\t\t</isNotEmpty>\n\t\t</dynamic>\n\t\tDEPARTMENT=#department#,\n\t\tREALNAME=#realName#,\n\t\tAUTHORIZETYPE=#authorizeType#,\n\t\tGMT_MODIFIED=now()\n\t\tWHERE ID = #id#\n\t</update>\n\t\n\t<select id=\"checkUserUnique\" resultClass=\"Integer\"\n\t\tparameterClass=\"user\">\n\t\tselect count(*) from USER\n\t\twhere USER.ID != #id#\n\t\tand USER.USERNAME = #name#\n\t</select>\n\t\n\t<!--\n      - ===============================================\n      - 验证name、password\n      - ===============================================\n     -->\n    <select id=\"getUserByNameAndPassword\" resultMap=\"userResult\" parameterClass=\"user\">\n        select\n                <include refid=\"allUserColumns\" />\n        from\n                USER\n        where\n                USERNAME = #name#\n            and PASSWORD = #password#\n    </select>\n\n\n\t<insert id=\"insertUser\" parameterClass=\"user\">\n\t\tinsert into USER\n\t\t(USERNAME, PASSWORD,DEPARTMENT,REALNAME,AUTHORIZETYPE, GMT_CREATE, GMT_MODIFIED)\n\t\tSELECT #name#,#password#,#department#,#realName#,#authorizeType#,now(),now()\n\t\tfrom dual\n\t\tWHERE not exists (select * from USER\n\t\twhere USER.USERNAME = #name#); \n\t\t<selectKey keyProperty=\"id\" resultClass=\"long\">\n\t\t\tselect last_insert_id()\n\t\t</selectKey>\n\t</insert>\n\t\n\t\n\n\t<delete id=\"deleteUserById\" parameterClass=\"long\"><![CDATA[\n    \tdelete from USER where ID = #value#\n    ]]></delete>\n\n</sqlMap>  "
  },
  {
    "path": "manager/biz/src/main/resources/sqlmap/sqlmap.xml",
    "content": "<!DOCTYPE sqlMapConfig SYSTEM \"http://ibatis.apache.org/dtd/sql-map-config-2.dtd\">\n<sqlMapConfig>\n\t<settings cacheModelsEnabled=\"true\" enhancementEnabled=\"false\"\n\t\tlazyLoadingEnabled=\"false\" maxRequests=\"3000\" maxSessions=\"3000\"\n\t\tmaxTransactions=\"3000\" useStatementNamespaces=\"false\" />\n\t\t\n\t<typeHandler javaType=\"com.alibaba.otter.shared.common.model.config.channel.ChannelParameter\" callback=\"com.alibaba.otter.manager.biz.config.channel.dal.ibatis.ChannelParameterTypeHandler\"/>\n\t<typeHandler javaType=\"com.alibaba.otter.shared.common.model.config.pipeline.PipelineParameter\" callback=\"com.alibaba.otter.manager.biz.config.pipeline.dal.ibatis.PipelineParameterTypeHandler\"/>\n\t<typeHandler javaType=\"com.alibaba.otter.shared.common.model.config.node.NodeParameter\" callback=\"com.alibaba.otter.manager.biz.config.node.dal.ibatis.NodeParameterTypeHandler\"/>\n\t<typeHandler javaType=\"com.alibaba.otter.canal.instance.manager.model.CanalParameter\" callback=\"com.alibaba.otter.manager.biz.config.canal.dal.ibatis.CanalParameterTypeHandler\"/>\n\t<typeHandler javaType=\"com.alibaba.otter.shared.common.model.config.parameter.SystemParameter\" callback=\"com.alibaba.otter.manager.biz.config.parameter.dal.ibatis.SystemParameterTypeHandler\"/>\n\t<typeHandler javaType=\"com.alibaba.otter.manager.biz.config.alarm.dal.dataobject.AlarmRuleParameter\" callback=\"com.alibaba.otter.manager.biz.config.alarm.dal.ibatis.AlarmRuleParameterTypeHandler\"/>\n\t\n\t<typeHandler javaType=\"java.util.List\" callback=\"com.alibaba.otter.manager.biz.config.utils.ListTypeHandler\"/>\n\t<typeHandler javaType=\"java.util.Map\" callback=\"com.alibaba.otter.manager.biz.config.utils.MapTypeHandler\"/>\n\t\n\t<sqlMap resource=\"sqlmap/sqlmap-mapping-user.xml\" />\n\t<sqlMap resource=\"sqlmap/sqlmap-mapping-channel.xml\" />\n\t<sqlMap resource=\"sqlmap/sqlmap-mapping-pipeline.xml\" />\n\t<sqlMap resource=\"sqlmap/sqlmap-mapping-datamedia.xml\" />\n\t<sqlMap resource=\"sqlmap/sqlmap-mapping-datamediasource.xml\" />\n\t<sqlMap resource=\"sqlmap/sqlmap-mapping-datamediapair.xml\" />\n\t<sqlMap resource=\"sqlmap/sqlmap-mapping-node.xml\" />\n\t<sqlMap resource=\"sqlmap/sqlmap-mapping-canal.xml\" />\n\t<sqlMap resource=\"sqlmap/sqlmap-mapping-logrecord.xml\" />\n\t<sqlMap resource=\"sqlmap/sqlmap-mapping-pipelinenoderelation.xml\" />\n\t<sqlMap resource=\"sqlmap/sqlmap-mapping-delayStat.xml\" />\n\t<sqlMap resource=\"sqlmap/sqlmap-mapping-tableStat.xml\" />\n\t<sqlMap resource=\"sqlmap/sqlmap-mapping-tableHistoryStat.xml\" />\n\t<sqlMap resource=\"sqlmap/sqlmap-mapping-throughputStat.xml\" />\n\t<sqlMap resource=\"sqlmap/sqlmap-mapping-systemParameter.xml\" />\n\t<sqlMap resource=\"sqlmap/sqlmap-mapping-datacolumnpair.xml\" />\n\t<sqlMap resource=\"sqlmap/sqlmap-mapping-datacolumnpairgroup.xml\" />\n\t<sqlMap resource=\"sqlmap/sqlmap-mapping-alarmRule.xml\" />\n\t<sqlMap resource=\"sqlmap/sqlmap-mapping-autokeeper.xml\" />\n\t<sqlMap resource=\"sqlmap/sqlmap-mapping-datamatrix.xml\" />\n</sqlMapConfig>"
  },
  {
    "path": "manager/biz/src/test/java/com/alibaba/otter/manager/biz/BaseOtterTest.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz;\n\nimport java.util.Map;\n\nimport org.jtester.annotations.SpringApplicationContext;\nimport org.jtester.core.TestedObject;\nimport org.springframework.beans.factory.BeanFactory;\nimport org.springframework.beans.factory.config.AutowireCapableBeanFactory;\nimport org.testng.annotations.BeforeMethod;\n\nimport com.alibaba.otter.shared.arbitrate.impl.setl.ArbitrateFactory;\nimport com.alibaba.otter.shared.common.utils.TestUtils;\n\n/**\n * @author jianghang 2011-9-16 下午02:58:37\n * @version 4.0.0\n */\n@SpringApplicationContext(\"applicationContext.xml\")\npublic class BaseOtterTest extends org.jtester.testng.JTester {\n\n    @BeforeMethod\n    public void setUp() {\n        try {\n            Map cache = (Map) TestUtils.getField(new ArbitrateFactory(), \"cache\");\n            cache.clear();\n        } catch (Exception e) {\n            want.fail();\n        }\n    }\n\n    protected BeanFactory getBeanFactory() {\n        return (BeanFactory) TestedObject.getSpringBeanFactory();\n    }\n\n    protected void autowire(Object obj) {\n        // 重新注入一下对象\n        ((AutowireCapableBeanFactory) TestedObject.getSpringBeanFactory()).autowireBeanProperties(obj,\n                                                                                                  AutowireCapableBeanFactory.AUTOWIRE_BY_NAME,\n                                                                                                  true);\n    }\n}\n"
  },
  {
    "path": "manager/biz/src/test/java/com/alibaba/otter/manager/biz/autokeeper/impl/AutoKeeperCollectorTest.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.autokeeper.impl;\n\nimport java.util.Set;\n\nimport org.jtester.annotations.SpringBeanByName;\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.manager.biz.BaseOtterTest;\nimport com.alibaba.otter.manager.biz.autokeeper.AutoKeeperStatService;\nimport com.alibaba.otter.shared.common.model.autokeeper.AutoKeeperConnectionStat;\nimport com.alibaba.otter.shared.common.model.autokeeper.AutoKeeperServerStat;\n\n/**\n * @author simon 2012-9-29 下午5:23:59\n * @version 4.1.0\n */\npublic class AutoKeeperCollectorTest extends BaseOtterTest {\n\n    @SpringBeanByName\n    private AutoKeeperCollector   autoKeeperCollector;\n\n    @SpringBeanByName\n    private AutoKeeperStatService autoKeeperStatService;\n\n    private final static String   ADDRESS = \"127.0.0.1:2181\";\n\n    @Test\n    public void testCollectorServerStat() {\n        autoKeeperCollector.collectorServerStat(ADDRESS);\n        autoKeeperCollector.collectorConnectionStat(ADDRESS);\n        autoKeeperCollector.collectorWatchStat(ADDRESS);\n        autoKeeperCollector.collectorEphemeralStat(ADDRESS);\n        AutoKeeperServerStat stat = autoKeeperStatService.findServerStat(ADDRESS);\n        Set<AutoKeeperConnectionStat> conns = stat.getConnectionStats();\n        for (AutoKeeperConnectionStat autoKeeperConnectionStat : conns) {\n            autoKeeperStatService.findConnectionBySessionId(autoKeeperConnectionStat.getSessionId());\n            autoKeeperStatService.findServerStatBySessionId(autoKeeperConnectionStat.getSessionId());\n            String path = autoKeeperConnectionStat.getClientAddress();\n            System.out.println(path);\n        }\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/test/java/com/alibaba/otter/manager/biz/monitor/AbstractRuleMonitorInPeriodTest.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.monitor;\n\nimport java.lang.reflect.Method;\nimport java.util.Calendar;\nimport java.util.List;\n\nimport org.springframework.util.ReflectionUtils;\nimport org.testng.Assert;\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.manager.biz.monitor.impl.AbstractRuleMonitor;\nimport com.alibaba.otter.shared.common.model.config.alarm.AlarmRule;\n\n/**\n * @version 4.1.0\n */\npublic class AbstractRuleMonitorInPeriodTest {\n\n    private AbstractRuleMonitor monitor        = new AbstractRuleMonitor() {\n\n                                                   @Override\n                                                   public void explore(List<AlarmRule> rules) {\n                                                       // do nothing\n                                                   }\n\n                                                   // 14:33:00\n                                                   @Override\n                                                   protected Calendar currentCalendar() {\n                                                       Calendar calendar = Calendar.getInstance();\n                                                       calendar.set(Calendar.AM_PM, Calendar.PM);\n                                                       calendar.set(Calendar.HOUR, 2);\n                                                       calendar.set(Calendar.MINUTE, 33);\n                                                       return calendar;\n                                                   }\n\n                                               };\n\n    private Method              inPeriodMethod = ReflectionUtils.findMethod(AbstractRuleMonitor.class, \"inPeriod\",\n                                                                            new Class[] { String.class });\n\n    @Test\n    public void testInPeriod() {\n        ReflectionUtils.makeAccessible(inPeriodMethod);\n        String rule = \"aaf@3452zd@qwas:213-adz@10:00-13:00,14:00-15:00\";\n        boolean isInPeriod = (Boolean) ReflectionUtils.invokeMethod(inPeriodMethod, monitor, new Object[] { rule });\n        Assert.assertTrue(isInPeriod);\n    }\n\n    @Test\n    public void testNotInPeriod() {\n        ReflectionUtils.makeAccessible(inPeriodMethod);\n        String rule = \"aaf@3452zd@qwas:213-adz@14:40-15:00\";\n        boolean isInPeriod = (Boolean) ReflectionUtils.invokeMethod(inPeriodMethod, monitor, new Object[] { rule });\n        Assert.assertFalse(isInPeriod);\n    }\n\n    @Test\n    public void testCriticalInPeriod() {\n        ReflectionUtils.makeAccessible(inPeriodMethod);\n        String rule = \"aaf@3452zd@qwas:213-adz@14:33-15:00\";\n        boolean isInPeriod = (Boolean) ReflectionUtils.invokeMethod(inPeriodMethod, monitor, new Object[] { rule });\n        Assert.assertTrue(isInPeriod);\n    }\n\n    @Test\n    public void testErrorFormatInPeriod() {\n        ReflectionUtils.makeAccessible(inPeriodMethod);\n        String rule = \"aaf@3452zd@qwas:213-adz@14:331-15:00\";\n        boolean isInPeriod = (Boolean) ReflectionUtils.invokeMethod(inPeriodMethod, monitor, new Object[] { rule });\n        Assert.assertTrue(isInPeriod);\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/test/java/com/alibaba/otter/manager/biz/monitor/AlarmServiceTest.java",
    "content": "package com.alibaba.otter.manager.biz.monitor;\n\nimport org.apache.commons.lang.exception.ExceptionUtils;\nimport org.jtester.annotations.SpringBeanByName;\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.manager.biz.BaseOtterTest;\nimport com.alibaba.otter.manager.biz.common.alarm.AlarmMessage;\nimport com.alibaba.otter.manager.biz.common.alarm.DefaultAlarmService;\n\npublic class AlarmServiceTest extends BaseOtterTest {\n\n    @SpringBeanByName\n    private DefaultAlarmService alarmService;\n\n    @Test\n    public void test_simple() {\n        AlarmMessage data = new AlarmMessage();\n        data.setMessage(\"this is test\");\n        data.setReceiveKey(\"jianghang.loujh@alibaba-inc.com\");\n        try {\n            alarmService.doSend(data);\n        } catch (Exception e) {\n            want.fail(ExceptionUtils.getFullStackTrace(e));\n        }\n    }\n}\n"
  },
  {
    "path": "manager/biz/src/test/java/com/alibaba/otter/manager/biz/monitor/ExceptionRuleMonitorTest.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.monitor;\n\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.List;\n\nimport mockit.Mocked;\n\nimport org.jtester.annotations.SpringBeanFrom;\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.manager.biz.BaseOtterTest;\nimport com.alibaba.otter.manager.biz.config.alarm.AlarmRuleService;\nimport com.alibaba.otter.manager.biz.monitor.impl.ExceptionRuleMonitor;\nimport com.alibaba.otter.shared.common.model.config.alarm.AlarmRule;\nimport com.alibaba.otter.shared.common.model.config.alarm.AlarmRuleStatus;\nimport com.alibaba.otter.shared.common.model.config.alarm.MonitorName;\nimport com.alibaba.otter.shared.communication.model.arbitrate.NodeAlarmEvent;\n\n/**\n * @author zebin.xuzb 2012-9-4 下午5:20:28\n * @version 4.1.0\n */\npublic class ExceptionRuleMonitorTest extends BaseOtterTest {\n\n    private ExceptionRuleMonitor monitor = new ExceptionRuleMonitor();\n\n    @SpringBeanFrom\n    @Mocked\n    private AlarmRuleService     alarmRuleService;\n\n    @Test\n    public void testSerialProcess() {\n        new NonStrictExpectations() {\n\n            {\n                alarmRuleService.getAlarmRules(anyLong, AlarmRuleStatus.ENABLE);\n                List<AlarmRule> rules = new ArrayList<AlarmRule>();\n                AlarmRule rule = new AlarmRule();\n                rule.setDescription(\"xxx\");\n                rule.setGmtCreate(new Date());\n                rule.setGmtModified(new Date());\n                rule.setId(1L);\n                rule.setMatchValue(\"EXCEPTION\");\n                rule.setMonitorName(MonitorName.EXCEPTION);\n                rule.setPipelineId(2L);\n                rule.setReceiverKey(\"otterteam\");\n                rule.setStatus(AlarmRuleStatus.ENABLE);\n\n                rules.add(rule);\n                returns(rules);\n            }\n        };\n\n        NodeAlarmEvent event = new NodeAlarmEvent();\n        event.setMessage(\"pid:77 nid:5 exception:EXCEPTON,nid:5[setl:ERROR ## SelectTask processId = 644408,parallelism = 5,ProcessEnd processId = 644394 invalid]\");\n        event.setNid(5L);\n        event.setPipelineId(2L);\n        event.setTitle(\"EXCEPTON\");\n        monitor.feed(event, 2L);\n    }\n}\n"
  },
  {
    "path": "manager/biz/src/test/java/com/alibaba/otter/manager/biz/monitor/GlobalMonitorTest.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.monitor;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport mockit.Mocked;\n\nimport org.jtester.annotations.SpringBeanByName;\nimport org.jtester.annotations.SpringBeanFrom;\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.manager.biz.BaseOtterTest;\nimport com.alibaba.otter.manager.biz.config.alarm.AlarmRuleService;\nimport com.alibaba.otter.manager.biz.monitor.impl.AbstractRuleMonitor;\nimport com.alibaba.otter.manager.biz.monitor.impl.GlobalMonitor;\nimport com.alibaba.otter.shared.common.model.config.alarm.AlarmRule;\nimport com.alibaba.otter.shared.common.model.config.alarm.AlarmRuleStatus;\n\n/**\n * @author zebin.xuzb 2012-9-4 下午5:20:28\n * @version 4.1.0\n */\npublic class GlobalMonitorTest extends BaseOtterTest {\n\n    private Monitor          normalPipelineMonitor    = new AbstractRuleMonitor() {\n\n                                                          @Override\n                                                          public void explore(List<AlarmRule> rules) {\n                                                              System.out.println(\" normal monitor executed\");\n                                                          }\n                                                      };\n\n    private Monitor          exceptionPipelineMonitor = new AbstractRuleMonitor() {\n\n                                                          @Override\n                                                          public void explore(List<AlarmRule> rules) {\n                                                              throw new RuntimeException(\n                                                                                         \" exception happens in monitor executed\");\n                                                          }\n                                                      };\n\n    @SpringBeanFrom\n    @Mocked\n    private AlarmRuleService alarmRuleService;\n\n    @SpringBeanByName\n    private GlobalMonitor    globalMonitor;\n\n    @Test\n    public void testSerialProcess() {\n        new NonStrictExpectations() {\n\n            {\n                alarmRuleService.getAlarmRules(AlarmRuleStatus.ENABLE);\n                Map<Long, List<AlarmRule>> allRules = new HashMap<Long, List<AlarmRule>>();\n                for (long i = 0; i < 10; i++) {\n                    List<AlarmRule> rules = new ArrayList<AlarmRule>();\n                    for (int j = 0; j < 5; j++) {\n                        rules.add(new AlarmRule());\n                    }\n                    allRules.put(i + 1, rules);\n                }\n                returns(allRules);\n            }\n\n        };\n\n        globalMonitor.setNeedConcurrent(false);\n        globalMonitor.setPipelineMonitor(normalPipelineMonitor);\n        globalMonitor.explore();\n    }\n\n    @Test\n    public void testConcurrentProcess() {\n        new NonStrictExpectations() {\n\n            {\n                alarmRuleService.getAlarmRules(AlarmRuleStatus.ENABLE);\n                Map<Long, List<AlarmRule>> allRules = new HashMap<Long, List<AlarmRule>>();\n                for (long i = 0; i < 10; i++) {\n                    List<AlarmRule> rules = new ArrayList<AlarmRule>();\n                    for (int j = 0; j < 5; j++) {\n                        rules.add(new AlarmRule());\n                    }\n                    allRules.put(i + 1, rules);\n                }\n                returns(allRules);\n            }\n\n        };\n\n        globalMonitor.setNeedConcurrent(true);\n        globalMonitor.setPipelineMonitor(normalPipelineMonitor);\n        globalMonitor.explore();\n    }\n\n    @Test\n    public void testConcurrentProcessWithException() {\n        new NonStrictExpectations() {\n\n            {\n                alarmRuleService.getAlarmRules(AlarmRuleStatus.ENABLE);\n                Map<Long, List<AlarmRule>> allRules = new HashMap<Long, List<AlarmRule>>();\n                for (long i = 0; i < 10; i++) {\n                    List<AlarmRule> rules = new ArrayList<AlarmRule>();\n                    for (int j = 0; j < 5; j++) {\n                        rules.add(new AlarmRule());\n                    }\n                    allRules.put(i + 1, rules);\n                }\n                returns(allRules);\n            }\n\n        };\n\n        globalMonitor.setNeedConcurrent(true);\n        globalMonitor.setPipelineMonitor(exceptionPipelineMonitor);\n        try {\n            globalMonitor.explore();\n        } catch (Exception e) {\n            return;\n        }\n        throw new IllegalStateException(\"unreached code\");\n    }\n\n}\n"
  },
  {
    "path": "manager/biz/src/test/java/com/alibaba/otter/manager/biz/service/NodeSerivceTest.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.service;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport mockit.Mocked;\n\nimport org.jtester.annotations.SpringBeanByName;\nimport org.jtester.annotations.SpringBeanFrom;\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.manager.biz.BaseOtterTest;\nimport com.alibaba.otter.manager.biz.config.node.NodeService;\nimport com.alibaba.otter.manager.biz.config.node.dal.NodeDAO;\nimport com.alibaba.otter.manager.biz.config.node.dal.dataobject.NodeDO;\nimport com.alibaba.otter.shared.arbitrate.ArbitrateManageService;\nimport com.alibaba.otter.shared.arbitrate.impl.manage.NodeArbitrateEvent;\n\npublic class NodeSerivceTest extends BaseOtterTest {\n\n    @SpringBeanByName\n    private NodeService            nodeService;\n\n    @SpringBeanFrom\n    @Mocked\n    private NodeDAO                nodeDao;\n\n    @SpringBeanFrom\n    @Mocked\n    private ArbitrateManageService arbitrateManageService;\n\n    @Test\n    public void testListAllNodes() {\n        new NonStrictExpectations() {\n\n            {\n                nodeDao.listAll();\n                List<NodeDO> nodeDos = new ArrayList<NodeDO>();\n                for (int i = 0; i < 10; i++) {\n                    NodeDO nodeDo = new NodeDO();\n                    nodeDo.setId(Long.valueOf(1));\n                    nodeDo.setIp(\"127.0.0.1\");\n                    nodeDos.add(nodeDo);\n                }\n                returns(nodeDos);\n\n                arbitrateManageService.nodeEvent();\n                returns(new NodeArbitrateEvent() {\n\n                    @Override\n                    public List<Long> liveNodes() {\n                        return Arrays.asList(1L);\n                    }\n\n                });\n            }\n        };\n\n        want.number(nodeService.listAll().size()).isEqualTo(10);\n        want.string(nodeService.listAll().get(0).getIp()).isEqualTo(\"127.0.0.1\");\n    }\n}\n"
  },
  {
    "path": "manager/biz/src/test/java/com/alibaba/otter/manager/biz/service/PipelineSerivceTest.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.biz.service;\n\nimport com.alibaba.otter.manager.biz.BaseOtterTest;\n\npublic class PipelineSerivceTest extends BaseOtterTest {\n\n}\n"
  },
  {
    "path": "manager/biz/src/test/resources/applicationContext.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<beans xmlns=\"http://www.springframework.org/schema/beans\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n    xmlns:tx=\"http://www.springframework.org/schema/tx\" xmlns:aop=\"http://www.springframework.org/schema/aop\"\n    xmlns:lang=\"http://www.springframework.org/schema/lang\" xmlns:context=\"http://www.springframework.org/schema/context\"\n    xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd\n           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd\n           http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-2.0.xsd\n           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd\n           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd\"\n    default-autowire=\"byName\">\n\n\t<!-- properties -->\n\t<bean class=\"com.alibaba.otter.shared.common.utils.spring.PropertyPlaceholderConfigurer\" lazy-init=\"false\">\n\t\t<property name=\"ignoreResourceNotFound\" value=\"true\" />\n\t\t<property name=\"systemPropertiesModeName\" value=\"SYSTEM_PROPERTIES_MODE_OVERRIDE\"/><!-- 允许system覆盖 -->\n\t\t<property name=\"locations\">\n\t\t\t<list>\n\t\t\t\t<value>classpath:otter.properties</value>\n\t\t\t</list>\n\t\t</property>\n\t</bean>\n    <import resource=\"classpath*:spring/otter-*.xml\" />\n</beans>"
  },
  {
    "path": "manager/deployer/pom.xml",
    "content": "<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\">\n\t<modelVersion>4.0.0</modelVersion>\n\t<parent>\n\t\t<groupId>com.alibaba.otter</groupId>\n\t\t<artifactId>manager</artifactId>\n\t\t<version>4.2.19-SNAPSHOT</version>\n\t\t<relativePath>../pom.xml</relativePath>\n\t</parent>\n\t<groupId>com.alibaba.otter</groupId>\n\t<artifactId>manager.deployer</artifactId>\n\t<packaging>jar</packaging>\n\t<name>manager deployer module for otter</name>\n\t<url>http://github.com/alibaba/otter</url>\n\t\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>org.eclipse.jetty</groupId>\n\t\t\t<artifactId>jetty-server</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.eclipse.jetty</groupId>\n\t\t\t<artifactId>jetty-webapp</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>com.alibaba.otter</groupId>\n\t\t\t<artifactId>manager.web</artifactId>\n\t\t\t<version>${project.version}</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>com.alibaba.otter</groupId>\n\t\t\t<artifactId>manager.biz</artifactId>\n\t\t\t<version>${project.version}</version>\n\t\t</dependency>\n\t</dependencies>\n\t\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<artifactId>maven-jar-plugin</artifactId>\n\t\t\t\t<configuration>\n\t\t\t\t\t<excludes>\n\t\t\t\t\t\t<exclude>**/jetty.xml</exclude>\n\t\t\t\t\t\t<exclude>**/logback.xml</exclude>\n\t\t\t\t\t\t<exclude>**/otter.properties</exclude>\n\t\t\t\t\t\t<exclude>**/webapp/</exclude>\n\t\t\t\t\t</excludes>\n\t\t\t\t</configuration>\n\t\t\t</plugin>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.apache.maven.plugins</groupId>\n\t\t\t\t<artifactId>maven-assembly-plugin</artifactId>\n\t\t\t\t<!-- 这是最新版本，推荐使用这个版本 -->\n\t\t\t\t<version>2.2.1</version>\n\t\t\t\t<executions>\n\t\t\t\t\t<execution>\n\t\t\t\t\t\t<id>assemble</id>\n\t\t\t\t\t\t<goals>\n\t\t\t\t\t\t\t<goal>single</goal>\n\t\t\t\t\t\t</goals>\n\t\t\t\t\t\t<phase>package</phase>\n\t\t\t\t\t</execution>\n\t\t\t\t</executions>\n\t\t\t\t<configuration>\n\t\t\t\t\t<appendAssemblyId>false</appendAssemblyId>\n\t\t\t\t\t<attach>false</attach>\n\t\t\t\t</configuration>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\n\t<profiles>\n\t\t<profile>\n\t\t\t<id>dev</id>\n\t\t\t<activation>\n\t\t\t\t<activeByDefault>true</activeByDefault>\n\t\t\t\t<property>\n\t\t\t\t\t<name>env</name>\n\t\t\t\t\t<value>dev</value>\n\t\t\t\t</property>\n\t\t\t</activation>\n\t\t\t<build>\n\t\t\t\t<plugins>\n\t\t\t\t\t<plugin>\n\t\t\t\t\t\t<artifactId>maven-assembly-plugin</artifactId>\n\t\t\t\t\t\t<configuration>\n\t\t\t\t\t\t\t<!-- maven assembly插件需要一个描述文件 来告诉插件包的结构以及打包所需的文件来自哪里 -->\n\t\t\t\t\t\t\t<descriptors>\n\t\t\t\t\t\t\t\t<descriptor>${basedir}/src/main/assembly/dev.xml</descriptor>\n\t\t\t\t\t\t\t</descriptors>\n\t\t\t\t\t\t\t<finalName>manager</finalName>\n\t\t\t\t\t\t\t<outputDirectory>${project.build.directory}</outputDirectory>\n\t\t\t\t\t\t</configuration>\n\t\t\t\t\t</plugin>\n\t\t\t\t</plugins>\n\t\t\t</build>\n\n\t\t</profile>\n\n\t\t<profile>\n\t\t\t<id>release</id>\n\t\t\t<activation>\n\t\t\t\t<property>\n\t\t\t\t\t<name>env</name>\n\t\t\t\t\t<value>release</value>\n\t\t\t\t</property>\n\t\t\t</activation>\n\t\t\t<build>\n\t\t\t\t<plugins>\n\t\t\t\t\t<plugin>\n\t\t\t\t\t\t<artifactId>maven-assembly-plugin</artifactId>\n\t\t\t\t\t\t<configuration>\n\t\t\t\t\t\t\t<!-- 发布模式使用的maven assembly插件描述文件 -->\n\t\t\t\t\t\t\t<descriptors>\n\t\t\t\t\t\t\t\t<descriptor>${basedir}/src/main/assembly/release.xml</descriptor>\n\t\t\t\t\t\t\t</descriptors>\n\t\t\t\t\t\t\t<!-- 如果一个应用的包含多个deploy模块，如果使用同样的包名， 如果把它们复制的一个目录中可能会失败，所以包名加了 artifactId以示区分 -->\n\t\t\t\t\t\t\t<finalName>${project.artifactId}-${project.version}</finalName>\n\t\t\t\t\t\t\t<!-- scm 要求 release 模式打出的包放到顶级目录下的target子目录中 -->\n\t\t\t\t\t\t\t<outputDirectory>${project.parent.parent.build.directory}</outputDirectory>\n\t\t\t\t\t\t</configuration>\n\t\t\t\t\t</plugin>\n\t\t\t\t</plugins>\n\t\t\t</build>\n\t\t</profile>\n\t\t\n\t\t<profile>\n\t\t\t<id>mvn</id>\n\t\t\t<activation>\n\t\t\t\t<activeByDefault>false</activeByDefault>\n\t\t\t\t<property>\n\t\t\t\t\t<name>env</name>\n\t\t\t\t\t<value>mvn</value>\n\t\t\t\t</property>\n\t\t\t</activation>\n\t\t\t<build>\n\t\t\t\t<plugins>\n\t\t\t\t\t<plugin>\n\t\t\t\t\t\t<artifactId>maven-jar-plugin</artifactId>\n\t\t\t\t\t\t<configuration>\n\t\t\t\t\t\t\t<excludes>\n\t\t\t\t\t\t\t\t<exclude>**/logback.xml</exclude>\n\t\t\t\t\t\t\t\t<exclude>**/otter.properties</exclude>\n\t\t\t\t\t\t\t</excludes>\n\t\t\t\t\t\t</configuration>\n\t\t\t\t\t</plugin>\n\t\t\t\t\t<plugin>\n\t\t\t\t\t\t<artifactId>maven-assembly-plugin</artifactId>\n\t\t\t\t\t\t<configuration>\n\t\t\t\t\t\t\t<!-- 发布模式使用的maven assembly插件描述文件 -->\n\t\t\t\t\t\t\t<descriptors>\n\t\t\t\t\t\t\t\t<descriptor>${basedir}/src/main/assembly/mvn.xml</descriptor>\n\t\t\t\t\t\t\t</descriptors>\n\t\t\t\t\t\t\t<!-- 如果一个应用的包含多个deploy模块，如果使用同样的包名， 如果把它们复制的一个目录中可能会失败，所以包名加了 artifactId以示区分 -->\n\t\t\t\t\t\t\t<finalName>${project.artifactId}-${project.version}</finalName>\n\t\t\t\t\t\t\t<!-- scm 要求 release 模式打出的包放到顶级目录下的target子目录中 -->\n\t\t\t\t\t\t\t<outputDirectory>${project.parent.parent.build.directory}</outputDirectory>\n\t\t\t\t\t\t</configuration>\n\t\t\t\t\t</plugin>\n\t\t\t\t</plugins>\n\t\t\t</build>\n\t\t</profile>\n\t</profiles>\n</project>\n"
  },
  {
    "path": "manager/deployer/src/main/assembly/component.xml",
    "content": "<component>\n\t<fileSets>\n\t\t<fileSet>\n\t\t\t<directory>./src/main/bin</directory>\n\t\t\t<outputDirectory>bin</outputDirectory>\n\t\t\t<includes>\n\t\t\t\t<include>**/*</include>\n\t\t\t</includes>\n\t\t\t<fileMode>0755</fileMode>\n\t\t</fileSet>\n\t\t<fileSet>\n\t\t\t<directory>./src/main/resources/webapp</directory>\n\t\t\t<outputDirectory>webapp</outputDirectory>\n\t\t\t<includes>\n\t\t\t\t<include>**/*</include>\n\t\t\t</includes>\n\t\t</fileSet>\n\t</fileSets>\n\t<files>\n\t\t<file>\n\t\t\t<source>./src/main/resources/logback.xml</source>\n\t\t\t<outputDirectory>conf</outputDirectory>\n\t\t</file>\n\t\t<file>\n\t\t\t<source>./src/main/resources/otter.properties</source>\n\t\t\t<outputDirectory>conf</outputDirectory>\n\t\t</file>\n\t\t<file>\n\t\t\t<source>./src/main/resources/jetty.xml</source>\n\t\t\t<outputDirectory>conf</outputDirectory>\n\t\t</file>\n\t\t<file>\n\t\t\t<source>../../lib/ojdbc6.jar</source>\n\t\t\t<outputDirectory>lib</outputDirectory>\n\t\t</file>\n\t</files>\n\t<dependencySets>\n\t\t<dependencySet>\n\t\t\t<outputDirectory>lib</outputDirectory>\n\t\t</dependencySet>\n\t</dependencySets>\n</component>\n"
  },
  {
    "path": "manager/deployer/src/main/assembly/dev.xml",
    "content": "<assembly\n\txsi:schemaLocation=\"http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd\"\n\txmlns=\"http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2\"\n\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n\t<id>dev</id>\n\t<formats>\n\t\t<format>dir</format>\n\t</formats>\n\t<includeBaseDirectory>false</includeBaseDirectory>\n\t <componentDescriptors>\n    <componentDescriptor>src/main/assembly/component.xml</componentDescriptor>\n  </componentDescriptors>\n</assembly>\n"
  },
  {
    "path": "manager/deployer/src/main/assembly/mvn.xml",
    "content": "<assembly\n\txsi:schemaLocation=\"http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd\"\n\txmlns=\"http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2\"\n\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n\t<id>mvn</id>\n\t<formats>\n\t\t<format>tar.gz</format>\n\t</formats>\n\t<includeBaseDirectory>false</includeBaseDirectory>\n\t<fileSets>\n\t\t<fileSet>\n\t\t\t<directory>./src/main/bin</directory>\n\t\t\t<outputDirectory>bin</outputDirectory>\n\t\t\t<includes>\n\t\t\t\t<include>**/*</include>\n\t\t\t</includes>\n\t\t\t<fileMode>0755</fileMode>\n\t\t</fileSet>\n\t</fileSets>\n\t<files>\n\t\t<file>\n\t\t\t<source>./src/main/resources/logback.xml</source>\n\t\t\t<outputDirectory>conf</outputDirectory>\n\t\t</file>\n\t\t<file>\n\t\t\t<source>./src/main/resources/otter.properties</source>\n\t\t\t<outputDirectory>conf</outputDirectory>\n\t\t</file>\n\t\t<file>\n\t\t\t<source>../../lib/ojdbc6.jar</source>\n\t\t\t<outputDirectory>lib</outputDirectory>\n\t\t</file>\n\t</files>\n\t<dependencySets>\n\t\t<dependencySet>\n\t\t\t<outputDirectory>lib</outputDirectory>\n\t\t</dependencySet>\n\t</dependencySets>\n</assembly>\n"
  },
  {
    "path": "manager/deployer/src/main/assembly/release.xml",
    "content": "<assembly\n\txsi:schemaLocation=\"http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd\"\n\txmlns=\"http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2\"\n\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n\t<id>release</id>\n\t<formats>\n\t\t<format>tar.gz</format>\n\t</formats>\n\t<includeBaseDirectory>false</includeBaseDirectory>\n\t<componentDescriptors>\n    <componentDescriptor>src/main/assembly/component.xml</componentDescriptor>\n  </componentDescriptors>\n</assembly>"
  },
  {
    "path": "manager/deployer/src/main/bin/startup.bat",
    "content": "@echo off\n@if not \"%ECHO%\" == \"\"  echo %ECHO%\n@if \"%OS%\" == \"Windows_NT\"  setlocal\n\nset ENV_PATH=.\\\nif \"%OS%\" == \"Windows_NT\" set ENV_PATH=%~dp0%\n\nset conf_dir=%ENV_PATH%\\..\\conf\nset webapp_dir=%ENV_PATH%\\..\\\nset otter_conf=%conf_dir%\\otter.properties\nset logback_configurationFile=%conf_dir%\\logback.xml\n\nset CLASSPATH=%webapp_dir%;%conf_dir%;%conf_dir%\\..\\lib\\*;%CLASSPATH%\n\nset JAVA_MEM_OPTS= -Xms128m -Xmx512m -XX:PermSize=128m\nset JAVA_OPTS_EXT= -Djava.awt.headless=true -Djava.net.preferIPv4Stack=true -Dapplication.codeset=UTF-8 -Dfile.encoding=UTF-8\nset JAVA_DEBUG_OPT= -server -Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,address=7099,server=y,suspend=n\nset CANAL_OPTS= -DappName=otter-manager -Ddubbo.application.logger=slf4j -Dlogback.configurationFile=\"%logback_configurationFile%\" -Dotter.conf=\"%otter_conf%\"\n\nset JAVA_OPTS= %JAVA_MEM_OPTS% %JAVA_OPTS_EXT% %JAVA_DEBUG_OPT% %OTTER_OPTS%\n\nset CMD_STR= java %JAVA_OPTS% -classpath \"%CLASSPATH%\" com.alibaba.otter.manager.deployer.OtterManagerLauncher\necho start cmd : %CMD_STR%\n\njava %JAVA_OPTS% -classpath \"%CLASSPATH%\" com.alibaba.otter.manager.deployer.OtterManagerLauncher"
  },
  {
    "path": "manager/deployer/src/main/bin/startup.sh",
    "content": "#!/bin/bash\n\ncurrent_path=`pwd`\ncase \"`uname`\" in\n    Linux)\n\t\tbin_abs_path=$(readlink -f $(dirname $0))\n\t\t;;\n\t*)\n\t\tbin_abs_path=`cd $(dirname $0); pwd`\n\t\t;;\nesac\nbase=${bin_abs_path}/..\notter_conf=$base/conf/otter.properties\nlogback_configurationFile=$base/conf/logback.xml\n\nexport LANG=en_US.UTF-8\nexport BASE=$base\n\nif [ -f $base/bin/otter.pid ] ; then\n\techo \"found otter.pid , Please run stop.sh first ,then startup.sh\" 2>&2\n    exit 1\nfi\n\nif [ ! -d $base/logs ] ; then \n\tmkdir -p $base/logs\nfi\n\n## set java path\nif [ -z \"$JAVA\" ] ; then\n  JAVA=$(which java)\nfi\n\nALIBABA_JAVA=\"/usr/alibaba/java/bin/java\"\nTAOBAO_JAVA=\"/opt/taobao/java/bin/java\"\nif [ -z \"$JAVA\" ]; then\n  if [ -f $ALIBABA_JAVA ] ; then\n        JAVA=$ALIBABA_JAVA\n  elif [ -f $TAOBAO_JAVA ] ; then\n        JAVA=$TAOBAO_JAVA\n  else\n        echo \"Cannot find a Java JDK. Please set either set JAVA or put java (>=1.5) in your PATH.\" 2>&2\n    \texit 1\n  fi\nfi\n\n\ncase \"$#\" \nin\n0 ) \n\t;;\n1 )\t\n\tvar=$*\n\tif [ -f $var ] ; then \n\t\totter_conf=$var\n\telse\n\t\techo \"THE PARAMETER IS NOT CORRECT.PLEASE CHECK AGAIN.\"\n        exit\n\tfi;;\n2 )\t\n\tvar=$1\n\tif [ -f $var ] ; then\n\t\totter_conf=$var\n\telse \n\t\tif [ \"$1\" = \"debug\" ]; then\n\t\t\tDEBUG_PORT=$2\n\t\t\tDEBUG_SUSPEND=\"n\"\n\t\t\tJAVA_DEBUG_OPT=\"-Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,address=$DEBUG_PORT,server=y,suspend=$DEBUG_SUSPEND\"\n\t\tfi\n     fi;;\n* )\n\techo \"THE PARAMETERS MUST BE TWO OR LESS.PLEASE CHECK AGAIN.\"\n\texit;;\nesac\n\n\nstr=`file $JAVA | grep 64-bit`\nif [ -n \"$str\" ]; then\n\tJAVA_OPTS=\"-server -Xms2048m -Xmx3072m -Xmn1024m -XX:SurvivorRatio=2 -XX:PermSize=96m -XX:MaxPermSize=256m -Xss256k -XX:-UseAdaptiveSizePolicy -XX:MaxTenuringThreshold=15 -XX:+DisableExplicitGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:+UseFastAccessorMethods -XX:+UseCMSInitiatingOccupancyOnly -XX:+HeapDumpOnOutOfMemoryError\"\nelse\n\tJAVA_OPTS=\"-server -Xms1024m -Xmx1024m -XX:NewSize=256m -XX:MaxNewSize=256m -XX:MaxPermSize=128m \"\nfi\n\nJAVA_OPTS=\" $JAVA_OPTS -Djava.awt.headless=true -Djava.net.preferIPv4Stack=true -Dfile.encoding=UTF-8\"\nOTTER_OPTS=\"-DappName=otter-manager -Ddubbo.application.logger=slf4j -Dlogback.configurationFile=$logback_configurationFile -Dotter.conf=$otter_conf\"\n\nif [ -e $otter_conf -a -e $logback_configurationFile ]\nthen \n\t\n\tfor i in $base/lib/*;\n\t\tdo CLASSPATH=$i:\"$CLASSPATH\";\n\tdone\n \tCLASSPATH=\"$base:$base/conf:$CLASSPATH\";\n \t\n \techo \"cd to $bin_abs_path for workaround relative path\"\n  \tcd $bin_abs_path\n \t\n\techo LOG CONFIGURATION : $logback_configurationFile\n\techo otter conf : $otter_conf \n\techo CLASSPATH :$CLASSPATH\n\t$JAVA $JAVA_OPTS $JAVA_DEBUG_OPT $OTTER_OPTS -classpath .:$CLASSPATH com.alibaba.otter.manager.deployer.OtterManagerLauncher 1>>$base/logs/manager.log 2>&1 &\n\techo $! > $base/bin/otter.pid \n\t\n\techo \"cd to $current_path for continue\"\n  \tcd $current_path\nelse \n\techo \"otter conf(\"$otter_conf\") OR log configration file($logback_configurationFile) is not exist,please create then first!\"\nfi\n"
  },
  {
    "path": "manager/deployer/src/main/bin/stop.sh",
    "content": "#!/bin/bash\n\ncygwin=false;\nlinux=false;\ncase \"`uname`\" in\n    CYGWIN*)\n        cygwin=true\n        ;;\n    Linux*)\n    \tlinux=true\n    \t;;\nesac\n\nget_pid() {\t\n\tSTR=$1\n\tPID=$2\n    if $cygwin; then\n        JAVA_CMD=\"$JAVA_HOME\\bin\\java\"\n        JAVA_CMD=`cygpath --path --unix $JAVA_CMD`\n        JAVA_PID=`ps |grep $JAVA_CMD |awk '{print $1}'`\n    else\n        if $linux; then\n\t        if [ ! -z \"$PID\" ]; then\n\t        \tJAVA_PID=`ps -C java -f --width 1000|grep \"$STR\"|grep \"$PID\"|grep -v grep|awk '{print $2}'`\n\t\t    else \n\t\t        JAVA_PID=`ps -C java -f --width 1000|grep \"$STR\"|grep -v grep|awk '{print $2}'`\n\t        fi\n\t    else\n\t    \tif [ ! -z \"$PID\" ]; then\n\t        \tJAVA_PID=`ps aux |grep \"$STR\"|grep \"$PID\"|grep -v grep|awk '{print $2}'`\n\t\t    else \n\t\t        JAVA_PID=`ps aux |grep \"$STR\"|grep -v grep|awk '{print $2}'`\n\t        fi\n\t    fi\n    fi\n    echo $JAVA_PID;\n}\n\nbase=`dirname $0`/..\npidfile=$base/bin/otter.pid\nif [ ! -f \"$pidfile\" ];then\n\techo \"otter is not running. exists\"\n\texit\nfi\n\npid=`cat $pidfile`\nif [ \"$pid\" == \"\" ] ; then\n\tpid=`get_pid \"appName=otter-manager\"`\nfi\n\necho -e \"`hostname`: stopping otter $pid ... \"\nkill $pid\n\nLOOPS=0\nwhile (true); \ndo \n\tgpid=`get_pid \"appName=otter-manager\" \"$pid\"`\n    if [ \"$gpid\" == \"\" ] ; then\n    \techo \"Oook! cost:$LOOPS\"\n    \t`rm $pidfile`\n    \tbreak;\n    fi\n    let LOOPS=LOOPS+1\n    sleep 1\ndone"
  },
  {
    "path": "manager/deployer/src/main/java/com/alibaba/otter/manager/deployer/JettyEmbedServer.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.deployer;\n\nimport org.eclipse.jetty.server.Handler;\nimport org.eclipse.jetty.server.Server;\nimport org.eclipse.jetty.util.resource.Resource;\nimport org.eclipse.jetty.webapp.WebAppContext;\nimport org.eclipse.jetty.xml.XmlConfiguration;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * 嵌入式jetty启动\n * \n * @author jianghang 2013-8-8 下午4:04:17\n * @since 4.2.0\n */\npublic class JettyEmbedServer {\n\n    private static final Logger logger         = LoggerFactory.getLogger(JettyEmbedServer.class);\n    private static final String DEFAULT_CONFIG = \"jetty.xml\";\n    private Server              server;\n    private String              config         = DEFAULT_CONFIG;\n\n    public JettyEmbedServer(String jettyXml){\n        this.config = jettyXml;\n    }\n\n    public void start() throws Exception {\n        Resource configXml = Resource.newSystemResource(config);\n        XmlConfiguration configuration = new XmlConfiguration(configXml.getInputStream());\n        server = (Server) configuration.configure();\n\n        //        Integer port = getPort();\n        //        if (port != null && port > 0) {\n        //            Connector[] connectors = server.getConnectors();\n        //            for (Connector connector : connectors) {\n        //                connector.setPort(port);\n        //            }\n        //        }\n        Handler handler = server.getHandler();\n        if (handler != null && handler instanceof WebAppContext) {\n            WebAppContext webAppContext = (WebAppContext) handler;\n            webAppContext.setResourceBase(JettyEmbedServer.class.getResource(\"/webapp\").toString());\n        }\n        server.start();\n        if (logger.isInfoEnabled()) {\n            logger.info(\"##Jetty Embed Server is startup!\");\n        }\n    }\n\n    public void join() throws Exception {\n        server.join();\n        if (logger.isInfoEnabled()) {\n            logger.info(\"##Jetty Embed Server joined!\");\n        }\n    }\n\n    public void stop() throws Exception {\n        server.stop();\n        if (logger.isInfoEnabled()) {\n            logger.info(\"##Jetty Embed Server is stop!\");\n        }\n    }\n\n    // ================ setter / getter ================\n\n    public void setConfig(String config) {\n        this.config = config;\n    }\n\n}\n"
  },
  {
    "path": "manager/deployer/src/main/java/com/alibaba/otter/manager/deployer/OtterManagerLauncher.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.deployer;\n\nimport java.io.FileInputStream;\nimport java.util.Map.Entry;\nimport java.util.Properties;\n\nimport org.apache.commons.lang.StringUtils;\nimport org.apache.commons.lang.exception.ExceptionUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\npublic class OtterManagerLauncher {\n\n    private static final Logger logger               = LoggerFactory.getLogger(OtterManagerLauncher.class);\n    private static final String CLASSPATH_URL_PREFIX = \"classpath:\";\n\n    public static void main(String[] args) throws Throwable {\n        try {\n            String conf = System.getProperty(\"otter.conf\", \"classpath:otter.properties\");\n            Properties properties = new Properties();\n            if (conf.startsWith(CLASSPATH_URL_PREFIX)) {\n                conf = StringUtils.substringAfter(conf, CLASSPATH_URL_PREFIX);\n                properties.load(OtterManagerLauncher.class.getClassLoader().getResourceAsStream(conf));\n            } else {\n                properties.load(new FileInputStream(conf));\n            }\n\n            // 合并配置到system参数中\n            mergeProps(properties);\n\n            logger.info(\"## start the manager server.\");\n            final JettyEmbedServer server = new JettyEmbedServer(properties.getProperty(\"otter.jetty\", \"jetty.xml\"));\n            server.start();\n            logger.info(\"## the manager server is running now ......\");\n            Runtime.getRuntime().addShutdownHook(new Thread() {\n\n                public void run() {\n                    try {\n                        logger.info(\"## stop the manager server\");\n                        server.join();\n                    } catch (Throwable e) {\n                        logger.warn(\"##something goes wrong when stopping manager Server:\\n{}\",\n                                    ExceptionUtils.getFullStackTrace(e));\n                    } finally {\n                        logger.info(\"## manager server is down.\");\n                    }\n                }\n\n            });\n        } catch (Throwable e) {\n            logger.error(\"## Something goes wrong when starting up the manager Server:\\n{}\",\n                         ExceptionUtils.getFullStackTrace(e));\n            System.exit(0);\n        }\n    }\n\n    private static void mergeProps(Properties props) {\n        for (Entry<Object, Object> entry : props.entrySet()) {\n            System.setProperty((String) entry.getKey(), (String) entry.getValue());\n        }\n    }\n}\n"
  },
  {
    "path": "manager/deployer/src/main/resources/jetty.xml",
    "content": "<?xml version=\"1.0\"?>\n<!DOCTYPE Configure PUBLIC \"-//Jetty//Configure//EN\" \"http://www.eclipse.org/jetty/configure.dtd\">\n\n<!-- =============================================================== -->\n<!-- Configure the Jetty Server                                      -->\n<!--                                                                 -->\n<!-- Documentation of this file format can be found at:              -->\n<!-- http://wiki.eclipse.org/Jetty/Reference/jetty.xml_syntax        -->\n<!--                                                                 -->\n<!-- Additional configuration files are available in $JETTY_HOME/etc -->\n<!-- and can be mixed in.  For example:                              -->\n<!--   java -jar start.jar etc/jetty.xml etc/jetty-ssl.xml           -->\n<!--                                                                 -->\n<!-- See start.ini file for the default configuraton files           -->\n<!-- =============================================================== -->\n\n\n<Configure id=\"Server\" class=\"org.eclipse.jetty.server.Server\">\n\n    <!-- =========================================================== -->\n    <!-- Server Thread Pool                                          -->\n    <!-- =========================================================== -->\n    <Set name=\"ThreadPool\">\n      <!-- Default queued blocking threadpool -->\n      <New class=\"org.eclipse.jetty.util.thread.QueuedThreadPool\">\n        <Set name=\"minThreads\">1</Set>\n        <Set name=\"maxThreads\">250</Set>\n      </New>\n    </Set>\n\n    <!-- =========================================================== -->\n    <!-- Set connectors                                              -->\n    <!-- =========================================================== -->\n    <!--\n    <Call name=\"addConnector\">\n      <Arg>\n          <New class=\"org.eclipse.jetty.server.bio.SocketConnector\">\n            <Set name=\"port\"><SystemProperty name=\"otter.port\" default=\"8080\" /></Set>\n            <Set name=\"forwarded\">true</Set>\n            <Set name=\"forwardedHostHeader\">ignore</Set>\n            <Set name=\"forwardedServerHeader\">ignore</Set>\n            <Set name=\"acceptQueueSize\">256</Set>\n            <Set name=\"statsOn\">false</Set>\n            <Set name=\"maxIdleTime\">600000</Set>\n            <Set name=\"lowResourcesMaxIdleTime\">5000</Set>\n            <Set name=\"requestHeaderSize\">8192</Set>\n\t\t\t<Set name=\"responseHeaderSize\">8192</Set>\n          </New>\n      </Arg>\n    </Call>\n     -->\n    <Call name=\"addConnector\">\n      <Arg>\n          <New class=\"org.eclipse.jetty.server.nio.SelectChannelConnector\">\n            <Set name=\"port\"><SystemProperty name=\"otter.port\" default=\"8080\" /></Set>\n            <Set name=\"forwarded\">true</Set>\n            <Set name=\"forwardedHostHeader\">ignore</Set>\n            <Set name=\"forwardedServerHeader\">ignore</Set>\n            <Set name=\"maxIdleTime\">600000</Set>\n            <Set name=\"Acceptors\">2</Set>\n            <Set name=\"acceptQueueSize\">256</Set>\n            <Set name=\"statsOn\">false</Set>\n            <Set name=\"confidentialPort\">8443</Set>\n            <Set name=\"lowResourcesConnections\">2000</Set>\n            <Set name=\"lowResourcesMaxIdleTime\">5000</Set>\n            <Set name=\"requestHeaderSize\">8192</Set>\n\t\t\t<Set name=\"responseHeaderSize\">8192</Set>\n          </New>\n      </Arg>\n    </Call>\n    <!-- =========================================================== -->\n    <!-- Set handler Collection Structure                            -->\n    <!-- =========================================================== -->\n    <Set name=\"handler\">\n\t\t<New id=\"WebAppContext\" class=\"org.eclipse.jetty.webapp.WebAppContext\">\n\t\t   \t<Set name=\"contextPath\">/</Set>\n\t\t   \t<Set name=\"parentLoaderPriority\">true</Set>\n\t\t   \t<Set name=\"resourceBase\">webapp</Set>\n\t\t</New>\n\t</Set>\n\n    <!-- =========================================================== -->\n    <!-- extra options                                               -->\n    <!-- =========================================================== -->\n    <Set name=\"stopAtShutdown\">true</Set>\n    <Set name=\"sendServerVersion\">false</Set>\n    <Set name=\"sendDateHeader\">true</Set>\n    <Set name=\"gracefulShutdown\">1000</Set>\n</Configure>\n"
  },
  {
    "path": "manager/deployer/src/main/resources/logback.xml",
    "content": "<configuration scan=\"true\" scanPeriod=\" 5 seconds\">\r\n\t<jmxConfigurator />\r\n\t\r\n\t<appender name=\"STDOUT\" class=\"ch.qos.logback.core.ConsoleAppender\">\r\n\t\t<encoder>\r\n\t\t\t<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%X{requestURIWithQueryString}] %-5level %logger{56} - %msg%n</pattern>\r\n\t\t</encoder>\r\n\t</appender>\r\n\t<appender name=\"WEBX-ROOT\" class=\"ch.qos.logback.core.rolling.RollingFileAppender\">\r\n\t\t<append>true</append>\r\n        <file>../logs/manager.log</file>\r\n        <rollingPolicy class=\"ch.qos.logback.core.rolling.TimeBasedRollingPolicy\">\r\n          <!-- daily rollover -->\r\n          <fileNamePattern>../logs/%d{yyyy-MM-dd}/manager-%d{yyyy-MM-dd}-%i.log</fileNamePattern>\r\n\t\t  <timeBasedFileNamingAndTriggeringPolicy class=\"ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP\">\r\n            <!-- or whenever the file size reaches 100MB -->\r\n            <maxFileSize>30MB</maxFileSize>\r\n          </timeBasedFileNamingAndTriggeringPolicy>\r\n          <!-- keep 180 days' worth of history -->\r\n          <maxHistory>180</maxHistory>\r\n        </rollingPolicy>\r\n        <encoder>\r\n          <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%X{requestURIWithQueryString}] %-5level %logger{56} - %msg%n</pattern>\r\n        </encoder>\r\n\t</appender>\r\n\t\r\n\t<appender name=\"COMMUNICATION\" class=\"ch.qos.logback.core.rolling.RollingFileAppender\">\r\n\t\t<append>true</append>\r\n        <file>../logs/communication.log</file>\r\n        <rollingPolicy class=\"ch.qos.logback.core.rolling.TimeBasedRollingPolicy\">\r\n          <!-- daily rollover -->\r\n          <fileNamePattern>../logs/%d{yyyy-MM-dd}/communication-%d{yyyy-MM-dd}-%i.log</fileNamePattern>\r\n\t\t  <timeBasedFileNamingAndTriggeringPolicy class=\"ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP\">\r\n            <!-- or whenever the file size reaches 100MB -->\r\n            <maxFileSize>30MB</maxFileSize>\r\n          </timeBasedFileNamingAndTriggeringPolicy>\r\n          <!-- keep 180 days' worth of history -->\r\n          <maxHistory>180</maxHistory>\r\n        </rollingPolicy>\r\n        <encoder>\r\n          <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %logger{56} - %msg%n</pattern>\r\n        </encoder>\r\n\t</appender>\r\n\t\r\n\t<appender name=\"ALARM\" class=\"ch.qos.logback.core.rolling.RollingFileAppender\">\r\n\t\t<append>true</append>\r\n        <file>../logs/alarm.log</file>\r\n        <rollingPolicy class=\"ch.qos.logback.core.rolling.TimeBasedRollingPolicy\">\r\n          <!-- daily rollover -->\r\n          <fileNamePattern>../logs/%d{yyyy-MM-dd}/alarm-%d{yyyy-MM-dd}-%i.log</fileNamePattern>\r\n\t\t  <timeBasedFileNamingAndTriggeringPolicy class=\"ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP\">\r\n            <!-- or whenever the file size reaches 100MB -->\r\n            <maxFileSize>30MB</maxFileSize>\r\n          </timeBasedFileNamingAndTriggeringPolicy>\r\n          <!-- keep 180 days' worth of history -->\r\n          <maxHistory>180</maxHistory>\r\n        </rollingPolicy>\r\n        <encoder>\r\n          <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %logger{56} - %msg%n</pattern>\r\n        </encoder>\r\n\t</appender>\r\n\t<appender name=\"monitorInfo\" class=\"ch.qos.logback.core.rolling.RollingFileAppender\">\r\n\t\t<append>true</append>\r\n        <file>../logs/monitor_info.log</file>\r\n        <rollingPolicy class=\"ch.qos.logback.core.rolling.TimeBasedRollingPolicy\">\r\n          <!-- daily rollover -->\r\n          <fileNamePattern>../logs/%d{yyyy-MM-dd}/monitor_info-%d{yyyy-MM-dd}-%i.log</fileNamePattern>\r\n\t\t  <timeBasedFileNamingAndTriggeringPolicy class=\"ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP\">\r\n            <!-- or whenever the file size reaches 100MB -->\r\n            <maxFileSize>30MB</maxFileSize>\r\n          </timeBasedFileNamingAndTriggeringPolicy>\r\n          <!-- keep 180 days' worth of history -->\r\n          <maxHistory>180</maxHistory>\r\n        </rollingPolicy>\r\n        <encoder>\r\n          <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %logger{56} - %msg%n</pattern>\r\n        </encoder>\r\n\t</appender>\r\n\t<appender name=\"monitorTrigger\" class=\"ch.qos.logback.core.rolling.RollingFileAppender\">\r\n\t\t<append>true</append>\r\n        <file>../logs/monitor_trigger.log</file>\r\n        <rollingPolicy class=\"ch.qos.logback.core.rolling.TimeBasedRollingPolicy\">\r\n          <!-- daily rollover -->\r\n          <fileNamePattern>../logs/%d{yyyy-MM-dd}/monitor_trigger-%d{yyyy-MM-dd}-%i.log</fileNamePattern>\r\n\t\t  <timeBasedFileNamingAndTriggeringPolicy class=\"ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP\">\r\n            <!-- or whenever the file size reaches 100MB -->\r\n            <maxFileSize>30MB</maxFileSize>\r\n          </timeBasedFileNamingAndTriggeringPolicy>\r\n          <!-- keep 180 days' worth of history -->\r\n          <maxHistory>180</maxHistory>\r\n        </rollingPolicy>\r\n        <encoder>\r\n          <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %logger{56} - %msg%n</pattern>\r\n        </encoder>\r\n\t</appender>\r\n\t<appender name=\"position\" class=\"ch.qos.logback.core.rolling.RollingFileAppender\">\r\n\t\t<append>true</append>\r\n        <file>../logs/position.log</file>\r\n        <rollingPolicy class=\"ch.qos.logback.core.rolling.TimeBasedRollingPolicy\">\r\n          <!-- daily rollover -->\r\n          <fileNamePattern>../logs/%d{yyyy-MM-dd}/position-%d{yyyy-MM-dd}-%i.log</fileNamePattern>\r\n\t\t  <timeBasedFileNamingAndTriggeringPolicy class=\"ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP\">\r\n            <!-- or whenever the file size reaches 100MB -->\r\n            <maxFileSize>30MB</maxFileSize>\r\n          </timeBasedFileNamingAndTriggeringPolicy>\r\n          <!-- keep 180 days' worth of history -->\r\n          <maxHistory>180</maxHistory>\r\n        </rollingPolicy>\r\n        <encoder>\r\n          <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %logger{56} - %msg%n</pattern>\r\n        </encoder>\r\n\t</appender>\r\n\r\n\t<logger name=\"com.alibaba.otter.shared.communication.core\" additivity=\"false\">  \r\n        <level value=\"info\" />  \r\n        <appender-ref ref=\"COMMUNICATION\" />\r\n    </logger>\r\n\t<logger name=\"com.alibaba.otter.manager.biz.common.alarm\" additivity=\"false\">  \r\n        <level value=\"info\" />  \r\n        <appender-ref ref=\"ALARM\" />\r\n    </logger>\r\n    <logger name=\"monitorInfo\" additivity=\"false\">  \r\n        <level value=\"info\" />  \r\n        <appender-ref ref=\"monitorInfo\" />\r\n    </logger>\r\n    <logger name=\"monitorTrigger\" additivity=\"false\">  \r\n        <level value=\"error\" />  \r\n        <appender-ref ref=\"monitorTrigger\" />\r\n    </logger>\r\n\t<logger name=\"com.alibaba.otter.manager.web.home.module.action.PositionAction\" additivity=\"false\">  \r\n        <level value=\"info\" />  \r\n        <appender-ref ref=\"position\" />\r\n    </logger>\r\n    <logger name=\"com.alibaba.otter.manager.deployer\" additivity=\"false\">  \r\n        <level value=\"info\" />  \r\n        <appender-ref ref=\"WEBX-ROOT\" />\r\n    </logger>\r\n    <logger name=\"com.alibaba.dubbo.rpc.support.RpcUtils\" additivity=\"false\">  \r\n     \t<level value=\"error\" />  \r\n        <appender-ref ref=\"WEBX-ROOT\" />\r\n    </logger>\r\n    \r\n\t<root level=\"WARN\">\r\n\t\t<appender-ref ref=\"WEBX-ROOT\" />\r\n\t</root>\r\n</configuration>"
  },
  {
    "path": "manager/deployer/src/main/resources/otter.properties",
    "content": "## otter manager domain name\notter.domainName = 127.0.0.1\n## otter manager http port\notter.port = 8080\n## jetty web config xml\notter.jetty = jetty.xml\n\n## otter manager database config\notter.database.driver.class.name = com.mysql.jdbc.Driver\notter.database.driver.url = jdbc:mysql://127.0.0.1:3306/otter\notter.database.driver.username = root\notter.database.driver.password = hello\n\n## otter communication port\notter.communication.manager.port = 1099\n\n## otter communication payload size (default = 8388608)\notter.communication.payload = 8388608\n\n## otter communication pool size\notter.communication.pool.size = 10\n\n## default zookeeper address\notter.zookeeper.cluster.default = 127.0.0.1:2181\n## default zookeeper sesstion timeout = 60s\notter.zookeeper.sessionTimeout = 60000\n\n## otter arbitrate connect manager config\notter.manager.address = ${otter.domainName}:${otter.communication.manager.port}\n\n## should run in product mode , true/false\notter.manager.productionMode = true\n\n## self-monitor enable or disable\notter.manager.monitor.self.enable = true\n## self-montir interval , default 120s\notter.manager.monitor.self.interval = 120\n## auto-recovery paused enable or disable\notter.manager.monitor.recovery.paused = true\n# manager email user config\notter.manager.monitor.email.host = smtp.gmail.com\notter.manager.monitor.email.username = \notter.manager.monitor.email.password = \notter.manager.monitor.email.stmp.port = 465"
  },
  {
    "path": "manager/deployer/src/main/resources/sql/otter-manager-schema.sql",
    "content": "CREATE DATABASE /*!32312 IF NOT EXISTS*/ `otter` /*!40100 DEFAULT CHARACTER SET utf8 COLLATE utf8_bin */;\n\nUSE `otter`;\n\nSET sql_mode='ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION';\n\nCREATE TABLE `ALARM_RULE` (\n  `ID` bigint(20) unsigned NOT NULL AUTO_INCREMENT,\n  `MONITOR_NAME` varchar(1024) DEFAULT NULL,\n  `RECEIVER_KEY` varchar(1024) DEFAULT NULL,\n  `STATUS` varchar(32) DEFAULT NULL,\n  `PIPELINE_ID` bigint(20) NOT NULL,\n  `DESCRIPTION` varchar(256) DEFAULT NULL,\n  `GMT_CREATE` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',\n  `GMT_MODIFIED` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n  `MATCH_VALUE` varchar(1024) DEFAULT NULL,\n  `PARAMETERS` text DEFAULT NULL,\n  PRIMARY KEY (`ID`)\n) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;\n\nCREATE TABLE `AUTOKEEPER_CLUSTER` (\n  `ID` bigint(20) NOT NULL AUTO_INCREMENT,\n  `CLUSTER_NAME` varchar(200) NOT NULL,\n  `SERVER_LIST` varchar(1024) NOT NULL,\n  `DESCRIPTION` varchar(200) DEFAULT NULL,\n  `GMT_CREATE` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',\n  `GMT_MODIFIED` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n  PRIMARY KEY (`ID`)\n) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;\n\nCREATE TABLE `CANAL` (\n  `ID` bigint(20) unsigned NOT NULL AUTO_INCREMENT,\n  `NAME` varchar(200) DEFAULT NULL,\n  `DESCRIPTION` varchar(200) DEFAULT NULL,\n  `PARAMETERS` text DEFAULT NULL,\n  `GMT_CREATE` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',\n  `GMT_MODIFIED` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n  PRIMARY KEY (`ID`),\n  UNIQUE KEY `CANALUNIQUE` (`NAME`)\n) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;\n\nCREATE TABLE `CHANNEL` (\n  `ID` bigint(20) NOT NULL AUTO_INCREMENT,\n  `NAME` varchar(200) NOT NULL,\n  `DESCRIPTION` varchar(200) DEFAULT NULL,\n  `PARAMETERS` text DEFAULT NULL,\n  `GMT_CREATE` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',\n  `GMT_MODIFIED` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n  PRIMARY KEY (`ID`),\n  UNIQUE KEY `CHANNELUNIQUE` (`NAME`)\n) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;\n\nCREATE TABLE `COLUMN_PAIR` (\n  `ID` bigint(20) NOT NULL AUTO_INCREMENT,\n  `SOURCE_COLUMN` varchar(200) DEFAULT NULL,\n  `TARGET_COLUMN` varchar(200) DEFAULT NULL,\n  `DATA_MEDIA_PAIR_ID` bigint(20) NOT NULL,\n  `GMT_CREATE` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',\n  `GMT_MODIFIED` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n  PRIMARY KEY (`ID`),\n  KEY `idx_DATA_MEDIA_PAIR_ID` (`DATA_MEDIA_PAIR_ID`)\n) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;\n\nCREATE TABLE `COLUMN_PAIR_GROUP` (\n  `ID` bigint(20) NOT NULL AUTO_INCREMENT,\n  `DATA_MEDIA_PAIR_ID` bigint(20) NOT NULL,\n  `COLUMN_PAIR_CONTENT` text DEFAULT NULL,\n  `GMT_CREATE` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',\n  `GMT_MODIFIED` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n  PRIMARY KEY (`ID`),\n  KEY `idx_DATA_MEDIA_PAIR_ID` (`DATA_MEDIA_PAIR_ID`)\n) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;\n\nCREATE TABLE `DATA_MEDIA` (\n  `ID` bigint(20) NOT NULL AUTO_INCREMENT,\n  `NAME` varchar(200) NOT NULL,\n  `NAMESPACE` varchar(200) NOT NULL,\n  `PROPERTIES` varchar(1000) NOT NULL,\n  `DATA_MEDIA_SOURCE_ID` bigint(20) NOT NULL,\n  `GMT_CREATE` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',\n  `GMT_MODIFIED` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n  PRIMARY KEY (`ID`),\n  UNIQUE KEY `DATAMEDIAUNIQUE` (`NAME`,`NAMESPACE`,`DATA_MEDIA_SOURCE_ID`)\n) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;\n\nCREATE TABLE `DATA_MEDIA_PAIR` (\n  `ID` bigint(20) NOT NULL AUTO_INCREMENT,\n  `PULLWEIGHT` bigint(20) DEFAULT NULL,\n  `PUSHWEIGHT` bigint(20) DEFAULT NULL,\n  `RESOLVER` text DEFAULT NULL,\n  `FILTER` text DEFAULT NULL,\n  `SOURCE_DATA_MEDIA_ID` bigint(20) DEFAULT NULL,\n  `TARGET_DATA_MEDIA_ID` bigint(20) DEFAULT NULL,\n  `PIPELINE_ID` bigint(20) NOT NULL,\n  `COLUMN_PAIR_MODE` varchar(20) DEFAULT NULL,\n  `GMT_CREATE` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',\n  `GMT_MODIFIED` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n  PRIMARY KEY (`ID`),\n  KEY `idx_PipelineID` (`PIPELINE_ID`,`ID`)\n) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;\n\nCREATE TABLE `DATA_MEDIA_SOURCE` (\n  `ID` bigint(20) NOT NULL AUTO_INCREMENT,\n  `NAME` varchar(200) NOT NULL,\n  `TYPE` varchar(20) NOT NULL,\n  `PROPERTIES` varchar(1000) NOT NULL,\n  `GMT_CREATE` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',\n  `GMT_MODIFIED` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n  PRIMARY KEY (`ID`),\n  UNIQUE KEY `DATAMEDIASOURCEUNIQUE` (`NAME`)\n) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;\n\nCREATE TABLE `DELAY_STAT` (\n  `ID` bigint(20) NOT NULL AUTO_INCREMENT,\n  `DELAY_TIME` bigint(20) NOT NULL,\n  `DELAY_NUMBER` bigint(20) NOT NULL,\n  `PIPELINE_ID` bigint(20) NOT NULL,\n  `GMT_CREATE` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',\n  `GMT_MODIFIED` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n  PRIMARY KEY (`ID`),\n  KEY `idx_PipelineID_GmtModified_ID` (`PIPELINE_ID`,`GMT_MODIFIED`,`ID`),\n  KEY `idx_Pipeline_GmtCreate` (`PIPELINE_ID`,`GMT_CREATE`),\n  KEY `idx_GmtCreate_id` (`GMT_CREATE`,`ID`)\n) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;\n\nCREATE TABLE `LOG_RECORD` (\n  `ID` bigint(20) NOT NULL AUTO_INCREMENT,\n  `NID` varchar(200) DEFAULT NULL,\n  `CHANNEL_ID` varchar(200) NOT NULL,\n  `PIPELINE_ID` varchar(200) NOT NULL,\n  `TITLE` varchar(1000) DEFAULT NULL,\n  `MESSAGE` text DEFAULT NULL,\n  `GMT_CREATE` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',\n  `GMT_MODIFIED` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n  PRIMARY KEY (`ID`),\n  KEY `logRecord_pipelineId` (`PIPELINE_ID`)\n) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;\n\nCREATE TABLE `NODE` (\n  `ID` bigint(20) NOT NULL AUTO_INCREMENT,\n  `NAME` varchar(200) NOT NULL,\n  `IP` varchar(200) NOT NULL,\n  `PORT` bigint(20) NOT NULL,\n  `DESCRIPTION` varchar(200) DEFAULT NULL,\n  `PARAMETERS` text DEFAULT NULL,\n  `GMT_CREATE` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',\n  `GMT_MODIFIED` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n  PRIMARY KEY (`ID`),\n  UNIQUE KEY `NODEUNIQUE` (`NAME`,`IP`)\n) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;\n\nCREATE TABLE `PIPELINE` (\n  `ID` bigint(20) NOT NULL AUTO_INCREMENT,\n  `NAME` varchar(200) NOT NULL,\n  `DESCRIPTION` varchar(200) DEFAULT NULL,\n  `PARAMETERS` text DEFAULT NULL,\n  `CHANNEL_ID` bigint(20) NOT NULL,\n  `GMT_CREATE` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',\n  `GMT_MODIFIED` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n  PRIMARY KEY (`ID`),\n  UNIQUE KEY `PIPELINEUNIQUE` (`NAME`,`CHANNEL_ID`),\n  KEY `idx_ChannelID` (`CHANNEL_ID`,`ID`)\n) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;\n\nCREATE TABLE `PIPELINE_NODE_RELATION` (\n  `ID` bigint(20) NOT NULL AUTO_INCREMENT,\n  `NODE_ID` bigint(20) NOT NULL,\n  `PIPELINE_ID` bigint(20) NOT NULL,\n  `LOCATION` varchar(20) NOT NULL,\n  `GMT_CREATE` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',\n  `GMT_MODIFIED` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n  PRIMARY KEY (`ID`),\n  KEY `idx_PipelineID` (`PIPELINE_ID`)\n) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;\n\nCREATE TABLE `SYSTEM_PARAMETER` (\n  `ID` bigint(20) unsigned NOT NULL,\n  `VALUE` text DEFAULT NULL,\n  `GMT_CREATE` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',\n  `GMT_MODIFIED` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n  PRIMARY KEY (`ID`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\nCREATE TABLE `TABLE_HISTORY_STAT` (\n  `ID` bigint(20) unsigned NOT NULL AUTO_INCREMENT,\n  `FILE_SIZE` bigint(20) DEFAULT NULL,\n  `FILE_COUNT` bigint(20) DEFAULT NULL,\n  `INSERT_COUNT` bigint(20) DEFAULT NULL,\n  `UPDATE_COUNT` bigint(20) DEFAULT NULL,\n  `DELETE_COUNT` bigint(20) DEFAULT NULL,\n  `DATA_MEDIA_PAIR_ID` bigint(20) DEFAULT NULL,\n  `PIPELINE_ID` bigint(20) DEFAULT NULL,\n  `START_TIME` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',\n  `END_TIME` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',\n  `GMT_CREATE` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',\n  `GMT_MODIFIED` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n  PRIMARY KEY (`ID`),\n  KEY `idx_DATA_MEDIA_PAIR_ID_END_TIME` (`DATA_MEDIA_PAIR_ID`,`END_TIME`),\n  KEY `idx_GmtCreate_id` (`GMT_CREATE`,`ID`)\n) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;\n\nCREATE TABLE `TABLE_STAT` (\n  `ID` bigint(20) NOT NULL AUTO_INCREMENT,\n  `FILE_SIZE` bigint(20) NOT NULL,\n  `FILE_COUNT` bigint(20) NOT NULL,\n  `INSERT_COUNT` bigint(20) NOT NULL,\n  `UPDATE_COUNT` bigint(20) NOT NULL,\n  `DELETE_COUNT` bigint(20) NOT NULL,\n  `DATA_MEDIA_PAIR_ID` bigint(20) NOT NULL,\n  `PIPELINE_ID` bigint(20) NOT NULL,\n  `GMT_CREATE` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',\n  `GMT_MODIFIED` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n  PRIMARY KEY (`ID`),\n  KEY `idx_PipelineID_DataMediaPairID` (`PIPELINE_ID`,`DATA_MEDIA_PAIR_ID`)\n) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;\n\nCREATE TABLE `THROUGHPUT_STAT` (\n  `ID` bigint(20) NOT NULL AUTO_INCREMENT,\n  `TYPE` varchar(20) NOT NULL,\n  `NUMBER` bigint(20) NOT NULL,\n  `SIZE` bigint(20) NOT NULL,\n  `PIPELINE_ID` bigint(20) NOT NULL,\n  `START_TIME` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',\n  `END_TIME` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',\n  `GMT_CREATE` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',\n  `GMT_MODIFIED` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n  PRIMARY KEY (`ID`),\n  KEY `idx_PipelineID_Type_GmtCreate_ID` (`PIPELINE_ID`,`TYPE`,`GMT_CREATE`,`ID`),\n  KEY `idx_PipelineID_Type_EndTime_ID` (`PIPELINE_ID`,`TYPE`,`END_TIME`,`ID`),\n  KEY `idx_GmtCreate_id` (`GMT_CREATE`,`ID`)\n) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;\n\nCREATE TABLE `USER` (\n  `ID` bigint(20) NOT NULL AUTO_INCREMENT,\n  `USERNAME` varchar(20) NOT NULL,\n  `PASSWORD` varchar(20) NOT NULL,\n  `AUTHORIZETYPE` varchar(20) NOT NULL,\n  `DEPARTMENT` varchar(20) NOT NULL,\n  `REALNAME` varchar(20) NOT NULL,\n  `GMT_CREATE` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',\n  `GMT_MODIFIED` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n  PRIMARY KEY (`ID`),\n  UNIQUE KEY `USERUNIQUE` (`USERNAME`)\n) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;\n\nCREATE TABLE  `DATA_MATRIX` (\n  `ID` bigint(20) NOT NULL AUTO_INCREMENT,\n  `GROUP_KEY` varchar(200) DEFAULT NULL,\n  `MASTER` varchar(200) DEFAULT NULL,\n  `SLAVE` varchar(200) DEFAULT NULL,\n  `DESCRIPTION` varchar(200) DEFAULT NULL,\n  `GMT_CREATE` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',\n  `GMT_MODIFIED` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n  PRIMARY KEY (`ID`),\n  KEY `GROUPKEY` (`GROUP_KEY`)\n) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;\n\nCREATE TABLE IF NOT EXISTS `meta_history` (\n  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',\n  `gmt_create` datetime NOT NULL COMMENT '创建时间',\n  `gmt_modified` datetime NOT NULL COMMENT '修改时间',\n  `destination` varchar(128) DEFAULT NULL COMMENT '通道名称',\n  `binlog_file` varchar(64) DEFAULT NULL COMMENT 'binlog文件名',\n  `binlog_offest` bigint(20) DEFAULT NULL COMMENT 'binlog偏移量',\n  `binlog_master_id` varchar(64) DEFAULT NULL COMMENT 'binlog节点id',\n  `binlog_timestamp` bigint(20) DEFAULT NULL COMMENT 'binlog应用的时间戳',\n  `use_schema` varchar(1024) DEFAULT NULL COMMENT '执行sql时对应的schema',\n  `sql_schema` varchar(1024) DEFAULT NULL COMMENT '对应的schema',\n  `sql_table` varchar(1024) DEFAULT NULL COMMENT '对应的table',\n  `sql_text` longtext DEFAULT NULL COMMENT '执行的sql',\n  `sql_type` varchar(256) DEFAULT NULL COMMENT 'sql类型',\n  `extra` text DEFAULT NULL COMMENT '额外的扩展信息',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY binlog_file_offest(`destination`,`binlog_master_id`,`binlog_file`,`binlog_offest`),\n  KEY `destination` (`destination`),\n  KEY `destination_timestamp` (`destination`,`binlog_timestamp`),\n  KEY `gmt_modified` (`gmt_modified`)\n) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='表结构变化明细表';\n\nCREATE TABLE IF NOT EXISTS `meta_snapshot` (\n  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',\n  `gmt_create` datetime NOT NULL COMMENT '创建时间',\n  `gmt_modified` datetime NOT NULL COMMENT '修改时间',\n  `destination` varchar(128) DEFAULT NULL COMMENT '通道名称',\n  `binlog_file` varchar(64) DEFAULT NULL COMMENT 'binlog文件名',\n  `binlog_offest` bigint(20) DEFAULT NULL COMMENT 'binlog偏移量',\n  `binlog_master_id` varchar(64) DEFAULT NULL COMMENT 'binlog节点id',\n  `binlog_timestamp` bigint(20) DEFAULT NULL COMMENT 'binlog应用的时间戳',\n  `data` longtext DEFAULT NULL COMMENT '表结构数据',\n  `extra` text DEFAULT NULL COMMENT '额外的扩展信息',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY binlog_file_offest(`destination`,`binlog_master_id`,`binlog_file`,`binlog_offest`),\n  KEY `destination` (`destination`),\n  KEY `destination_timestamp` (`destination`,`binlog_timestamp`),\n  KEY `gmt_modified` (`gmt_modified`)\n) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='表结构记录表快照表';\n\n\ninsert into USER(ID,USERNAME,PASSWORD,AUTHORIZETYPE,DEPARTMENT,REALNAME,GMT_CREATE,GMT_MODIFIED) values(null,'admin','801fc357a5a74743894a','ADMIN','admin','admin',now(),now());\ninsert into USER(ID,USERNAME,PASSWORD,AUTHORIZETYPE,DEPARTMENT,REALNAME,GMT_CREATE,GMT_MODIFIED) values(null,'guest','471e02a154a2121dc577','OPERATOR','guest','guest',now(),now());\n"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/META-INF/MANIFEST.MF",
    "content": "Manifest-Version: 1.0\r\nClass-Path: "
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/WEB-INF/applicationContext.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<beans xmlns=\"http://www.springframework.org/schema/beans\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n    xmlns:tx=\"http://www.springframework.org/schema/tx\" xmlns:aop=\"http://www.springframework.org/schema/aop\"\n    xmlns:lang=\"http://www.springframework.org/schema/lang\" xmlns:context=\"http://www.springframework.org/schema/context\"\n    xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd\n           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd\n           http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-2.0.xsd\n           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd\n           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd\"\n    default-autowire=\"byName\">\n    \n    <!-- properties -->\n\t<bean class=\"com.alibaba.otter.shared.common.utils.spring.PropertyPlaceholderConfigurer\" lazy-init=\"false\">\n\t\t<property name=\"ignoreResourceNotFound\" value=\"true\" />\n\t\t<property name=\"systemPropertiesModeName\" value=\"SYSTEM_PROPERTIES_MODE_OVERRIDE\"/><!-- 允许system覆盖 -->\n\t\t<property name=\"locations\">\n\t\t\t<list>\n\t\t\t\t<value>classpath:otter.properties</value>\n\t\t\t</list>\n\t\t</property>\n\t</bean>\n\t\n    <import resource=\"classpath*:spring/otter-manager-*.xml\"/>\n\t<import resource=\"classpath*:spring/otter-arbitrate-*.xml\"/>\n\t<import resource=\"classpath*:spring/otter-push-*.xml\"/>\n</beans>"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/WEB-INF/common/pipeline-exception.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<beans:beans xmlns:beans=\"http://www.springframework.org/schema/beans\"\n\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:p=\"http://www.springframework.org/schema/p\"\n\txmlns:services=\"http://www.alibaba.com/schema/services\"\n\txmlns:pl-conditions=\"http://www.alibaba.com/schema/services/pipeline/conditions\"\n\txmlns:pl-valves=\"http://www.alibaba.com/schema/services/pipeline/valves\"\n\txsi:schemaLocation=\"\n                 http://www.alibaba.com/schema/services http://localhost:8080/schema/services.xsd\n                 http://www.alibaba.com/schema/services/pipeline/conditions http://localhost:8080/schema/services-pipeline-conditions.xsd\n                 http://www.alibaba.com/schema/services/pipeline/valves http://localhost:8080/schema/services-pipeline-valves.xsd\n                 http://www.springframework.org/schema/beans http://localhost:8080/schema/www.springframework.org/schema/beans/spring-beans.xsd\n             \">\n\n\t<services:pipeline id=\"exceptionPipeline\">\n\n\t\t<!-- 初始化turbine rundata，并在pipelineContext中设置可能会用到的对象(如rundata、utils)，以便valve取得。 -->\n        <pl-valves:prepareForTurbine />\n\n\t\t<!-- 根据异常，选择适当的错误页面及statusCode。 -->\n\t\t<pl-valves:handleException defaultPage=\"error.vm\">\n\t\t\t<on-exception\n\t\t\t\ttype=\"com.alibaba.citrus.service.template.TemplateNotFoundException\"\n\t\t\t\tstatusCode=\"404\" />\n\t\t\t<on-exception\n\t\t\t\ttype=\"com.alibaba.citrus.service.moduleloader.ModuleNotFoundException\"\n\t\t\t\tstatusCode=\"404\" />\n\t\t\t<on-exception type=\"java.lang.RuntimeException\"\n\t\t\t\tstatusCode=\"404\" />\n\t\t</pl-valves:handleException>\n\n        <pl-valves:performTemplateScreen />\n        <pl-valves:renderTemplate />\n\t</services:pipeline>\n\n</beans:beans>\n"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/WEB-INF/common/pipeline.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<beans:beans xmlns:beans=\"http://www.springframework.org/schema/beans\"\n             xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n             xmlns:p=\"http://www.springframework.org/schema/p\"\n             xmlns:services=\"http://www.alibaba.com/schema/services\"\n             xmlns:pl-conditions=\"http://www.alibaba.com/schema/services/pipeline/conditions\"\n             xmlns:pl-valves=\"http://www.alibaba.com/schema/services/pipeline/valves\"\n             xsi:schemaLocation=\"\n                 http://www.alibaba.com/schema/services http://localhost:8080/schema/services.xsd\n                 http://www.alibaba.com/schema/services/pipeline/conditions http://localhost:8080/schema/services-pipeline-conditions.xsd\n                 http://www.alibaba.com/schema/services/pipeline/valves http://localhost:8080/schema/services-pipeline-valves.xsd\n                 http://www.springframework.org/schema/beans http://localhost:8080/schema/www.springframework.org/schema/beans/spring-beans.xsd\n             \">\n             \n    <services:pipeline>\n\n        <!-- 初始化turbine rundata，并在pipelineContext中设置可能会用到的对象(如rundata、utils)，以便valve取得。 -->\n        <pl-valves:prepareForTurbine />\n        \n        <!-- 设置日志系统的上下文，支持把当前请求的详情打印在日志中。 -->\n        <pl-valves:setLoggingContext />\n        \n        <!-- 分析URL，取得target。 -->\n        <pl-valves:analyzeURL homepage=\"index.vm\" />\n        \n        <!-- 权限控制 -->\n        <pl-valves:valve class=\"com.alibaba.otter.manager.web.webx.valve.AuthContextValve\" >\n        \t<beans:property name=\"loginLink\" value=\"otterLoginLink\" />\n        \t<beans:property name=\"redirectParmeter\" value=\"done\" />\n        </pl-valves:valve> \n                \n        <!-- 检查csrf token，防止csrf攻击和重复提交。假如request和session中的token不匹配，则出错，或显示expired页面。 -->\n        <pl-valves:checkCsrfToken />\n        <pl-valves:loop>\n            <pl-valves:choose>\n                <when>\n                    <!-- 执行带模板的screen，默认有layout。 -->\n                    <pl-conditions:target-extension-condition extension=\"null, vm, jsp\" />\n                    <pl-valves:performAction />\n                    <pl-valves:performTemplateScreen />\n                    <pl-valves:renderTemplate />\n                </when>\n                <when>\n                    <!-- 执行不带模板的screen，无layout。 -->\n                    <pl-conditions:target-extension-condition extension=\"do\" />\n                    <pl-valves:performAction />\n                    <pl-valves:performScreen />\n                </when>\n                <when>\n                    <!-- 创建JSON，无模板，无layout。 -->\n                    <pl-conditions:target-extension-condition extension=\"json\" />\n                    <pl-valves:performScreen />\n                    <pl-valves:renderResultAsJson />\n                </when>\n                <otherwise>\n                    <!-- 将控制交还给servlet engine。 -->\n                    <pl-valves:exit />\n                </otherwise>\n            </pl-valves:choose>\n\n            <!-- 假如rundata.setRedirectTarget()被设置，则循环，否则退出循环。 -->\n            <pl-valves:breakUnlessTargetRedirected />\n        </pl-valves:loop>\n    </services:pipeline>\n</beans:beans>"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/WEB-INF/common/resources.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<beans:beans xmlns:beans=\"http://www.springframework.org/schema/beans\"\n\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:p=\"http://www.springframework.org/schema/p\"\n\txmlns:services=\"http://www.alibaba.com/schema/services\"\n\txmlns:res-filters=\"http://www.alibaba.com/schema/services/resource-loading/filters\"\n\txmlns:res-loaders=\"http://www.alibaba.com/schema/services/resource-loading/loaders\"\n\txsi:schemaLocation=\"\n                 http://www.alibaba.com/schema/services http://localhost:8080/schema/services.xsd\n                 http://www.alibaba.com/schema/services/resource-loading/filters http://localhost:8080/schema/services-resource-loading-filters.xsd\n                 http://www.alibaba.com/schema/services/resource-loading/loaders http://localhost:8080/schema/services-resource-loading-loaders.xsd\n                 http://www.springframework.org/schema/beans http://localhost:8080/schema/www.springframework.org/schema/beans/spring-beans.xsd\n             \">\n\n\t<services:resource-loading>\n\n\t\t<resource-alias pattern=\"/\" name=\"/webapp\" />\n\t\t<resource-alias pattern=\"/templates\" name=\"/webapp/templates\" />\n\t\t\n\t\t<!-- 全局模板目录： /*/templates/global -->\n\t\t<resource-alias pattern=\"/*/templates/global\" name=\"/webapp/templates/home\" />\n\n\t\t<!-- 配置文件根目录：/conf -->\n\t\t<resource-alias pattern=\"/conf\" name=\"/webapp/WEB-INF\" />\n\t\t\n\t\t<!-- 子模块模板目录：/*/templates -->\n\t\t<!-- \n\t\t<resource pattern=\"/templates\">\n\t\t\t<res-loaders:classpath-loader />\n\t\t</resource>\n\t\t -->\n\n\t\t<!-- 内部资源 -->\n\t\t<resource pattern=\"/webapp\" internal=\"true\">\n\t\t\t<res-loaders:webapp-loader />\n\t\t</resource>\n\t\t<resource pattern=\"/classpath\" internal=\"true\">\n\t\t\t<res-loaders:classpath-loader />\n\t\t</resource>\n\t</services:resource-loading>\n</beans:beans>\n"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/WEB-INF/common/uris.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<beans:beans xmlns:beans=\"http://www.springframework.org/schema/beans\"\n             xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n             xmlns:p=\"http://www.springframework.org/schema/p\"\n             xmlns:services=\"http://www.alibaba.com/schema/services\"\n             xmlns:uris=\"http://www.alibaba.com/schema/services/uris\"\n             xmlns:uri-interceptors=\"http://www.alibaba.com/schema/services/uris/interceptors\"\n             xsi:schemaLocation=\"\n                 http://www.alibaba.com/schema/services http://localhost:8080/schema/services.xsd\n                 http://www.alibaba.com/schema/services/uris http://localhost:8080/schema/services-uris.xsd\n                 http://www.alibaba.com/schema/services/uris/interceptors http://localhost:8080/schema/services-uris-interceptors.xsd\n                 http://www.springframework.org/schema/beans http://localhost:8080/schema/www.springframework.org/schema/beans/spring-beans.xsd\n             \">\n\n\t<services:uris>\n\t\t<uris:uri id=\"server\">\n            <serverURI>http://${otter.domainName}:${otter.port}/</serverURI>\n\t\t</uris:uri>\n\t\t<uris:turbine-uri id=\"homeModule\" exposed=\"true\" extends=\"server\">\n\t\t\t<componentPath>/</componentPath>\n\t\t</uris:turbine-uri>\n\n\t\t<uris:turbine-content-uri id=\"homeContent\" exposed=\"true\"\n\t\t\textends=\"homeModule\" />\n\t\t<!-- ================================================================ -->\n\t\t<!-- Link Level： 继承前述各类links。 -->\n\t\t<!-- -->\n\t\t<!-- 使用方法： link -->\n\t\t<!-- ================================================================ -->\n\t\t<uris:turbine-uri id=\"channelListLink\" exposed=\"true\" extends=\"homeModule\">\n\t\t\t<target>channelList.vm</target>\n\t\t</uris:turbine-uri>\n\t\t<uris:turbine-uri id=\"selectDataMediaSourceLink\" exposed=\"true\"\n\t\t\textends=\"homeModule\">\n\t\t\t<target>selectDataSource.vm</target>\n\t\t</uris:turbine-uri>\n\t\t<uris:turbine-uri id=\"channelAddLink\" exposed=\"true\" extends=\"homeModule\">\n\t\t\t<target>addChannel.vm</target>\n\t\t</uris:turbine-uri>\n\t\t<uris:turbine-uri id=\"dataMediaListLink\" exposed=\"true\"\n\t\t\textends=\"homeModule\">\n\t\t\t<target>dataMediaList.vm</target>\n\t\t</uris:turbine-uri>\n\t\t<uris:turbine-uri id=\"dataMediaSourceListLink\" exposed=\"true\"\n\t\t\textends=\"homeModule\">\n\t\t\t<target>dataSourceList.vm</target>\n\t\t</uris:turbine-uri>\n\t\t<uris:turbine-uri id=\"userListLink\" exposed=\"true\" extends=\"homeModule\">\n\t\t\t<target>userManager.vm</target>\n\t\t</uris:turbine-uri>\n\t\t<uris:turbine-uri id=\"systemReductionLink\" exposed=\"true\"\n\t\t\textends=\"homeModule\">\n\t\t\t<target>systemReduction.vm</target>\n\t\t</uris:turbine-uri>\n\t\t<uris:turbine-uri id=\"nodeListLink\" exposed=\"true\" extends=\"homeModule\">\n\t\t\t<target>nodeList.vm</target>\n\t\t</uris:turbine-uri>\n\t\t<uris:turbine-uri id=\"otterLoginLink\" exposed=\"true\" extends=\"homeModule\">\n\t\t\t<target>login.vm</target>\n\t\t</uris:turbine-uri>\n\t\t<uris:turbine-uri id=\"errorForbiddenLink\" exposed=\"true\" extends=\"homeModule\">\n\t\t\t<target>forbidden.vm</target>\n\t\t</uris:turbine-uri>\n\t\t<uris:turbine-uri id=\"userAddLink\" exposed=\"true\" extends=\"homeModule\">\n\t\t\t<target>addUser.vm</target>\n\t\t</uris:turbine-uri>\n\t\t<uris:turbine-uri id=\"nodeAddLink\" exposed=\"true\" extends=\"homeModule\">\n\t\t\t<target>addNode.vm</target>\n\t\t</uris:turbine-uri>\n\t\t<uris:turbine-uri id=\"analysisDelayStatLink\" exposed=\"true\"\n\t\t\textends=\"homeModule\">\n\t\t\t<target>analysisDelayStat.vm</target>\n\t\t</uris:turbine-uri>\n\t\t<uris:turbine-uri id=\"analysisThroughputHistoryLink\" exposed=\"true\"\n\t\t\textends=\"homeModule\">\n\t\t\t<target>analysisThroughputHistory.vm</target>\n\t\t</uris:turbine-uri>\n\t\t<uris:turbine-uri id=\"systemParameterLink\" exposed=\"true\"\n\t\t\textends=\"homeModule\">\n\t\t\t<target>systemParameter.vm</target>\n\t\t</uris:turbine-uri>\n\t\t<uris:turbine-uri id=\"conflictStatListLink\" exposed=\"true\"\n\t\t\textends=\"homeModule\">\n\t\t\t<target>conflictStatList.vm</target>\n\t\t</uris:turbine-uri>\n\t\t<uris:turbine-uri id=\"conflictDetailStatListLink\" exposed=\"true\"\n\t\t\textends=\"homeModule\">\n\t\t\t<target>conflictDetailStatList.vm</target>\n\t\t</uris:turbine-uri>\n\t\t<uris:turbine-uri id=\"logRecordLink\" exposed=\"true\" extends=\"homeModule\">\n\t\t\t<target>logRecordList.vm</target>\n\t\t</uris:turbine-uri>\n\t\t<uris:turbine-uri id=\"behaviorHistoryCurveLink\" exposed=\"true\"\n\t\t\textends=\"homeModule\">\n\t\t\t<target>behaviorHistoryCurve.vm</target>\n\t\t</uris:turbine-uri>\n\t\t<uris:turbine-uri id=\"canalListLink\" exposed=\"true\" extends=\"homeModule\">\n\t\t\t<target>canalList.vm</target>\n\t\t</uris:turbine-uri>\n\t\t<uris:turbine-uri id=\"dataMatrixListLink\" exposed=\"true\"\n\t\t\textends=\"homeModule\">\n\t\t\t<target>dataMatrixList.vm</target>\n\t\t</uris:turbine-uri>\n\t\t<uris:turbine-uri id=\"canalAddLink\" exposed=\"true\" extends=\"homeModule\">\n\t\t\t<target>addCanal.vm</target>\n\t\t</uris:turbine-uri>\n\t\t<uris:turbine-uri id=\"alarmRuleListLink\" exposed=\"true\"\n\t\t\textends=\"homeModule\">\n\t\t\t<target>alarmRuleList.vm</target>\n\t\t</uris:turbine-uri>\n\t\t<uris:turbine-uri id=\"alarmLogLink\" exposed=\"true\" extends=\"homeModule\">\n\t\t\t<target>alarmSystemList.vm</target>\n\t\t</uris:turbine-uri>\n\t\t<uris:turbine-uri id=\"analysisTopStatLink\" exposed=\"true\"\n\t\t\textends=\"homeModule\">\n\t\t\t<target>analysisTopStat.vm</target>\n\t\t</uris:turbine-uri>\n\t\t<uris:turbine-uri id=\"autoKeeperClustersListLink\" exposed=\"true\"\n\t\t\textends=\"homeModule\">\n\t\t\t<target>autoKeeperClustersList.vm</target>\n\t\t</uris:turbine-uri>\n\t\t<uris:turbine-uri id=\"autoKeeperClustersDetailLink\" exposed=\"true\"\n\t\t\textends=\"homeModule\">\n\t\t\t<target>autoKeeperClustersDetail.vm</target>\n\t\t</uris:turbine-uri>\n\t\t<uris:turbine-uri id=\"autoKeeperClientPathLink\" exposed=\"true\"\n\t\t\textends=\"homeModule\">\n\t\t\t<target>autoKeeperClientPath.vm</target>\n\t\t</uris:turbine-uri>\n\t\t<uris:turbine-uri id=\"zookeeperAddLink\" exposed=\"true\" extends=\"homeModule\">\n\t\t\t<target>addZookeeper.vm</target>\n\t\t</uris:turbine-uri>\n\t\t<uris:turbine-uri id=\"sqlInitLink\" exposed=\"true\" extends=\"homeModule\">\n\t\t\t<target>initSql.vm</target>\n\t\t</uris:turbine-uri>\n\t\t<uris:turbine-uri id=\"wikiLink\" exposed=\"true\" extends=\"homeModule\">\n\t\t\t<target>wikiGuide.vm</target>\n\t\t</uris:turbine-uri>\n\t</services:uris>\n</beans:beans>"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/WEB-INF/common/webx-component-and-root.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<beans:beans xmlns:beans=\"http://www.springframework.org/schema/beans\"\n\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:p=\"http://www.springframework.org/schema/p\"\n\txmlns:services=\"http://www.alibaba.com/schema/services\"\n\txmlns:dr-factories=\"http://www.alibaba.com/schema/services/data-resolver/factories\"\n\txmlns:mapping-rules=\"http://www.alibaba.com/schema/services/mapping-rules\"\n\txmlns:tpl-engines=\"http://www.alibaba.com/schema/services/template/engines\"\n\txmlns:ftl-plugins=\"http://www.alibaba.com/schema/services/template/engines/freemarker/plugins\"\n\txmlns:vm-plugins=\"http://www.alibaba.com/schema/services/template/engines/velocity/plugins\"\n\txsi:schemaLocation=\"\n                 http://www.alibaba.com/schema/services http://localhost:8080/schema/services.xsd\n                 http://www.alibaba.com/schema/services/data-resolver/factories http://localhost:8080/schema/services-data-resolver-factories.xsd\n                 http://www.alibaba.com/schema/services/mapping-rules http://localhost:8080/schema/services-mapping-rules.xsd\n                 http://www.alibaba.com/schema/services/template/engines http://localhost:8080/schema/services-template-engines.xsd\n                 http://www.alibaba.com/schema/services/template/engines/freemarker/plugins http://localhost:8080/schema/services-template-engines-freemarker-plugins.xsd\n                 http://www.alibaba.com/schema/services/template/engines/velocity/plugins http://localhost:8080/schema/services-template-engines-velocity-plugins.xsd\n                 http://www.springframework.org/schema/beans http://localhost:8080/schema/www.springframework.org/schema/beans/spring-beans.xsd\n             \">\n\n\t<!-- 此文件被 webx.xml 和 webx-*.xml 同时引用。 -->\n\t<!-- 注意：此文件需要property-placeholder变量：${component}。 -->\n\t<!-- services:webx-configuration / -->\n\n\t<!-- Template渲染服务。 -->\n    <services:template searchExtensions=\"true\">\n        <tpl-engines:velocity-engine templateEncoding=\"UTF-8\" strictReference=\"false\" path=\"/templates/${component}\">\n            <global-macros>\n                <name>global/*.vm</name>\n            </global-macros>\n            <plugins>\n                <vm-plugins:escape-support defaultEscape=\"html\">\n                    <noescape>\n                        <if-matches pattern=\"^control\\.\" />\n                        <if-matches pattern=\"^screen_placeholder\" />\n                        <if-matches pattern=\"^stringEscapeUtil\\.escape\" />\n                        <if-matches pattern=\"^csrfToken\\.(get)?(\\w*)hiddenField\" />\n                    </noescape>\n                </vm-plugins:escape-support>\n            </plugins>\n        </tpl-engines:velocity-engine>\n    </services:template>\n\n\t<!-- 名称查找规则。 -->\n    <services:mapping-rules>\n\n        <!-- External target name => Internal target name -->\n        <mapping-rules:extension-rule id=\"extension.input\">\n            <!-- 默认后缀 -->\n            <mapping extension=\"\" to=\"\" />\n\n            <!-- JSP -->\n            <mapping extension=\"jhtml\" to=\"\" />\n            <mapping extension=\"jsp\" to=\"\" />\n            <mapping extension=\"jspx\" to=\"\" />\n            <mapping extension=\"php\" to=\"\" />\n\n            <!-- Velocity -->\n            <mapping extension=\"htm\" to=\"\" />\n            <mapping extension=\"vhtml\" to=\"\" />\n            <mapping extension=\"vm\" to=\"\" />\n        </mapping-rules:extension-rule>\n\n        <!-- Internal target name => External target name -->\n        <mapping-rules:extension-rule id=\"extension.output\">\n            <!-- 默认后缀 -->\n            <mapping extension=\"\" to=\"htm\" />\n\n            <!-- JSP -->\n            <mapping extension=\"jhtml\" to=\"jhtml\" />\n            <mapping extension=\"jsp\" to=\"jhtml\" />\n            <mapping extension=\"jspx\" to=\"jhtml\" />\n            <mapping extension=\"php\" to=\"jhtml\" />\n\n            <!-- Velocity -->\n            <mapping extension=\"htm\" to=\"htm\" />\n            <mapping extension=\"vhtml\" to=\"htm\" />\n            <mapping extension=\"vm\" to=\"htm\" />\n        </mapping-rules:extension-rule>\n\n        <!-- Target name => Action module name -->\n        <mapping-rules:direct-module-rule id=\"action\" />\n\n        <!-- Target name => Screen module name (*.do) -->\n        <mapping-rules:direct-module-rule id=\"screen.notemplate\" />\n\n        <!-- Target name => Screen module name (*.jsp, *.vm) -->\n        <mapping-rules:fallback-module-rule id=\"screen\" moduleType=\"screen\" matchLastName=\"true\" />\n\n        <!-- Target name => Screen template name -->\n        <mapping-rules:direct-template-rule id=\"screen.template\" templatePrefix=\"screen\" />\n\n        <!-- Target name => Layout template name -->\n        <mapping-rules:fallback-template-rule id=\"layout.template\" templatePrefix=\"layout\" />\n\n        <!-- Target name => Control module name (setControl method) -->\n        <mapping-rules:direct-module-rule id=\"control.notemplate\" />\n\n        <!-- Target name => Control module name (setTemplate method) -->\n        <mapping-rules:fallback-module-rule id=\"control\" moduleType=\"control\" />\n\n        <!-- Target name => Control template name -->\n        <mapping-rules:direct-template-rule id=\"control.template\" templatePrefix=\"control\" />\n\n    </services:mapping-rules>\n\n\t<!-- 支持注入参数。 -->\n\t<services:data-resolver>\n\t\t<dr-factories:turbine-rundata-resolver />\n\t\t<dr-factories:parameter-resolver />\n\t\t<dr-factories:form-resolver />\n\t</services:data-resolver>\n\n</beans:beans>\n"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/WEB-INF/common/webx-component.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<beans:beans xmlns:beans=\"http://www.springframework.org/schema/beans\"\n             xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n             xmlns:p=\"http://www.springframework.org/schema/p\"\n             xmlns:services=\"http://www.alibaba.com/schema/services\"\n             xmlns:pull-factories=\"http://www.alibaba.com/schema/services/pull/factories\"\n             xsi:schemaLocation=\"\n                 http://www.alibaba.com/schema/services http://localhost:8080/schema/services.xsd\n                 http://www.alibaba.com/schema/services/pull/factories http://localhost:8080/schema/services-pull-factories.xsd\n                 http://www.springframework.org/schema/beans http://localhost:8080/schema/www.springframework.org/schema/beans/spring-beans.xsd\n             \">\n\n    <!-- 此文件仅被 webx-*.xml 引用。 -->\n    <!-- 注意：此文件需要property-placeholder变量：${component}。 -->\n\n    <!-- 综合设置。 -->\n    <services:webx-configuration />\n\n    <!-- 将beans暴露给模板。 -->\n    <services:pull>\n        <!-- Webx3 tools。 -->\n        <pull-factories:utils />\n        <pull-factories:rundata-tool />\n        <pull-factories:csrfToken />\n        <pull-factories:form-tool />\n        <pull-factories:control-tool detailLevel=\"throwException\"/>\n        <pull-factories:uris-tool />\n    </services:pull>\n</beans:beans>\n"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/WEB-INF/home/form.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<beans:beans xmlns:beans=\"http://www.springframework.org/schema/beans\"\n\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:p=\"http://www.springframework.org/schema/p\"\n\txmlns:services=\"http://www.alibaba.com/schema/services\"\n\txmlns:fm-conditions=\"http://www.alibaba.com/schema/services/form/conditions\"\n\txmlns:fm-validators=\"http://www.alibaba.com/schema/services/form/validators\"\n\txsi:schemaLocation=\"\n                 http://www.alibaba.com/schema/services http://localhost:8080/schema/services.xsd\n                 http://www.alibaba.com/schema/services/form/conditions http://localhost:8080/schema/services-form-conditions.xsd\n                 http://www.alibaba.com/schema/services/form/validators http://localhost:8080/schema/services-form-validators.xsd\n                 http://www.springframework.org/schema/beans http://localhost:8080/schema/www.springframework.org/schema/beans/spring-beans.xsd\n             \">\n\n\t<services:form postOnlyByDefault=\"true\">\n\t\t<!-- - ======== - 用来检查csrf token。 - ======== -->\n\t\t<group name=\"csrfCheck\">\n\t\t\t<field name=\"csrfToken\">\n\t\t\t\t<fm-validators:csrf-validator>\n\t\t\t\t\t<message>提交的数据已过期</message>\n\t\t\t\t</fm-validators:csrf-validator>\n\t\t\t</field>\n\t\t</group>\n\n\t\t<!-- - ======== - channel info - ======== -->\n\t\t<group name=\"channelInfo\" extends=\"csrfCheck\">\n\t\t\t<field name=\"formChannelError\">\n\t\t\t\t<fm-validators:custom-error id=\"invalidChannelName\">\n\t\t\t\t\t<message>插入的Channel和数据库有重名</message>\n\t\t\t\t</fm-validators:custom-error>\n\t\t\t</field>\n\t\t\t<field name=\"id\" displayName=\"Channel的ID\" />\n\t\t\t<field name=\"name\" displayName=\"Channel名称\">\n\t\t\t\t<fm-validators:required-validator>\n\t\t\t\t\t<message>必须填写${displayName}</message>\n\t\t\t\t</fm-validators:required-validator>\n\t\t\t\t<fm-validators:string-length-validator minLength=\"4\"\n\t\t\t\t\tmaxLength=\"64\">\n\t\t\t\t\t<message>${displayName} 最少由${minLength}个字符组成，最多不能超过${maxLength}个字符</message>\n\t\t\t\t</fm-validators:string-length-validator>\n\t\t\t</field>\n\t\t\t<field name=\"status\" displayName=\"Channel运行状态\" />\n\t\t\t<field name=\"description\" displayName=\"Channel描述\" />\n\t\t</group>\n\n\t\t<!-- - ======== - channel parameter - ======== -->\n\t\t<group name=\"channelParameterInfo\" extends=\"csrfCheck\">\n\t\t\t<field name=\"channelId\" displayName=\"Channel的ID\" />\n\t\t\t<field name=\"enableRemedy\" displayName=\"冲突补救\">\n\t\t\t\t<fm-validators:required-validator>\n\t\t\t\t\t<message>必须填写${displayName}</message>\n\t\t\t\t</fm-validators:required-validator>\n\t\t\t</field>\n\t\t\t<field name=\"remedyDelayThresoldForMedia\"\n\t\t\t\tdisplayName=\"一致性反查数据库阀值\">\n\t\t\t\t<fm-validators:if test=\"enableRemedy.value == true\">\n\t\t\t\t\t<fm-validators:required-validator>\n\t\t\t\t\t\t<message>必须填写${displayName}</message>\n\t\t\t\t\t</fm-validators:required-validator>\n\t\t\t\t\t<fm-validators:number-validator>\n\t\t\t\t\t\t<message>${displayName} 必须是数字且是整数</message>\n\t\t\t\t\t</fm-validators:number-validator>\n\t\t\t\t\t<fm-validators:number-validator greaterThan=\"0\">\n\t\t\t\t\t\t<message>${displayName} 必须是大于0的整数</message>\n\t\t\t\t\t</fm-validators:number-validator>\n\t\t\t\t</fm-validators:if>\n\t\t\t</field>\n\t\t\t<field name=\"remedyAlgorithm\" displayName=\"一致性算法\">\n\t\t\t\t<fm-validators:if test=\"enableRemedy.value == true\">\n\t\t\t\t\t<fm-validators:required-validator>\n\t\t\t\t\t\t<message>必须填写${displayName}</message>\n\t\t\t\t\t</fm-validators:required-validator>\n\t\t\t\t</fm-validators:if>\n\t\t\t</field>\n\n\t\t\t<field name=\"syncMode\" displayName=\"同步模式\">\n\t\t\t\t<fm-validators:required-validator>\n\t\t\t\t\t<message>必须填写${displayName}</message>\n\t\t\t\t</fm-validators:required-validator>\n\t\t\t</field>\n\t\t\t<field name=\"syncConsistency\" displayName=\"同步一致性\">\n\t\t\t\t<fm-validators:required-validator>\n\t\t\t\t\t<message>必须填写${displayName}</message>\n\t\t\t\t</fm-validators:required-validator>\n\t\t\t</field>\n\t\t</group>\n\n\t\t<!-- - ======== - pipeline info - ======== -->\n\t\t<group name=\"pipelineInfo\" extends=\"csrfCheck\">\n\t\t\t<field name=\"formPipelineError\">\n\t\t\t\t<fm-validators:custom-error id=\"invalidPipelineName\">\n\t\t\t\t\t<message>插入的Pipeline在该Channel节点下有重名</message>\n\t\t\t\t</fm-validators:custom-error>\n\t\t\t\t<fm-validators:custom-error id=\"invalidDestinationName\">\n\t\t\t\t\t<message>canal已经被其他pipeline所关联</message>\n\t\t\t\t</fm-validators:custom-error>\n\t\t\t</field>\n\t\t\t<field name=\"id\" displayName=\"pipeline的id\" />\n\t\t\t<field name=\"channelId\" displayName=\"关联的ChannelId\" />\n\t\t\t<field name=\"name\" displayName=\"pipeline的名字\">\n\t\t\t\t<fm-validators:required-validator>\n\t\t\t\t\t<message>必须填写${displayName}</message>\n\t\t\t\t</fm-validators:required-validator>\n\t\t\t\t<fm-validators:string-length-validator minLength=\"4\"\n\t\t\t\t\tmaxLength=\"64\">\n\t\t\t\t\t<message>${displayName} 最少由${minLength}个字符组成，最多不能超过${maxLength}个字符</message>\n\t\t\t\t</fm-validators:string-length-validator>\n\t\t\t</field>\n\t\t\t<field name=\"description\" displayName=\"pipeline的描述\" />\n\t\t\t<field name=\"selectNodeIds\" displayName=\"Select节点的ID集合\">\n\t\t\t\t<fm-validators:required-validator>\n\t\t\t\t\t<message>必须选择一个以上${displayName}</message>\n\t\t\t\t</fm-validators:required-validator>\n\t\t\t</field>\n\t\t\t<!-- <field name=\"extractNodeIds\" displayName=\"Extract节点的ID集合\"> \n\t\t\t\t<required-validator> <message>必须选择一个以上${displayName}</message> </required-validator> \n\t\t\t\t</field> -->\n\t\t\t<field name=\"loadNodeIds\" displayName=\"Load节点的ID集合\">\n\t\t\t\t<fm-validators:required-validator>\n\t\t\t\t\t<message>必须选择一个以上${displayName}</message>\n\t\t\t\t</fm-validators:required-validator>\n\t\t\t</field>\n\t\t</group>\n\t\t<!-- - ======== - pipeline parameter - ======== -->\n\t\t<group name=\"pipelineParameterInfo\" extends=\"csrfCheck\">\n\t\t\t<field name=\"pipelineId\" displayName=\"pipeline的ID\" />\n\t\t\t<field name=\"parallelism\" displayName=\"并行度\">\n\t\t\t\t<fm-validators:required-validator>\n\t\t\t\t\t<message>必须填写${displayName}</message>\n\t\t\t\t</fm-validators:required-validator>\n\t\t\t\t<fm-validators:number-validator>\n\t\t\t\t\t<message>${displayName} 必须是数字且是整数</message>\n\t\t\t\t</fm-validators:number-validator>\n\t\t\t\t<fm-validators:number-validator greaterThan=\"0\">\n\t\t\t\t\t<message>${displayName} 必须是大于0的整数</message>\n\t\t\t\t</fm-validators:number-validator>\n\t\t\t</field>\n\t\t\t<field name=\"extractPoolSize\" displayName=\"反查线程数\">\n\t\t\t\t<fm-validators:required-validator>\n\t\t\t\t\t<message>必须填写${displayName}</message>\n\t\t\t\t</fm-validators:required-validator>\n\t\t\t\t<fm-validators:number-validator>\n\t\t\t\t\t<message>${displayName} 必须是数字且是整数</message>\n\t\t\t\t</fm-validators:number-validator>\n\t\t\t\t<fm-validators:number-validator greaterThan=\"0\">\n\t\t\t\t\t<message>${displayName} 必须是大于0的整数</message>\n\t\t\t\t</fm-validators:number-validator>\n\t\t\t</field>\n\t\t\t<field name=\"loadPoolSize\" displayName=\"数据载入线程数\">\n\t\t\t\t<fm-validators:required-validator>\n\t\t\t\t\t<message>必须填写${displayName}</message>\n\t\t\t\t</fm-validators:required-validator>\n\t\t\t\t<fm-validators:number-validator>\n\t\t\t\t\t<message>${displayName} 必须是数字且是整数</message>\n\t\t\t\t</fm-validators:number-validator>\n\t\t\t\t<fm-validators:number-validator greaterThan=\"0\">\n\t\t\t\t\t<message>${displayName} 必须是大于0的整数</message>\n\t\t\t\t</fm-validators:number-validator>\n\t\t\t</field>\n\t\t\t<field name=\"fileLoadPoolSize\" displayName=\"文件载入线程数\">\n\t\t\t\t<fm-validators:required-validator>\n\t\t\t\t\t<message>必须填写${displayName}</message>\n\t\t\t\t</fm-validators:required-validator>\n\t\t\t\t<fm-validators:number-validator>\n\t\t\t\t\t<message>${displayName} 必须是数字且是整数</message>\n\t\t\t\t</fm-validators:number-validator>\n\t\t\t\t<fm-validators:number-validator greaterThan=\"0\">\n\t\t\t\t\t<message>${displayName} 必须是大于0的整数</message>\n\t\t\t\t</fm-validators:number-validator>\n\t\t\t</field>\n\t\t\t<field name=\"useBatch\" displayName=\"使用batch\" />\n\t\t\t<field name=\"needDeleteColumns\" displayName=\"删除类型需要全字段\" />\n\t\t\t<field name=\"skipLoadException\" displayName=\"跳过Load异常\" />\n\t\t\t<field name=\"skipSelectException\" displayName=\"跳过Select异常\" />\n\t\t\t<field name=\"skipDdlException\" displayName=\"跳过ddl异常\" />\n\t\t\t<field name=\"skipFreedom\" displayName=\"跳过自由门数据\" />\n\t\t\t<field name=\"arbitrateMode\" displayName=\"仲裁器调度模式\" />\n\t\t\t<field name=\"lbAlgorithm\" displayName=\"负载均衡算法\" />\n\t\t\t<field name=\"dumpSelector\" displayName=\"是否记录selector日志\" />\n\t\t\t<field name=\"dumpSelectorDetail\" displayName=\"是否记录selector详细日志\" />\n\t\t\t<field name=\"dumpEvent\" displayName=\"是否记录load日志\" />\n\t\t\t<field name=\"home\" displayName=\"主站点\" />\n\t\t\t<field name=\"useLocalFileMutliThread\" displayName=\"local文件同步启用多线程\" />\n\t\t\t<field name=\"useFileEncrypt\" displayName=\"文件传输加密\" />\n\t\t\t<field name=\"useExternalIp\" displayName=\"启用公网同步\" />\n\t\t\t<field name=\"fileDetect\" displayName=\"是否开启文件重复同步对比\" />\n\t\t\t<field name=\"pipeChooseType\" displayName=\"传输模式\" />\n\t\t\t<field name=\"useTableTransform\" displayName=\"表类型转化\" />\n\t\t\t<field name=\"enableCompatibleMissColumn\"\n\t\t\t\tdisplayName=\"兼容字段新增同步\" />\n\t\t\t<field name=\"skipNoRow\" displayName=\"忽略反查无记录\" />\n\t\t\t<field name=\"selectorMode\" displayName=\"数据提取模式\">\n\t\t\t\t<fm-validators:required-validator>\n\t\t\t\t\t<message>必须填写${displayName}</message>\n\t\t\t\t</fm-validators:required-validator>\n\t\t\t</field>\n\t\t\t<field name=\"destinationName\" displayName=\"DestinationName\">\n\t\t\t\t<fm-validators:required-validator>\n\t\t\t\t\t<message>必须填写${displayName}</message>\n\t\t\t\t</fm-validators:required-validator>\n\t\t\t</field>\n\t\t\t<field name=\"mainstemBatchsize\" displayName=\"主道消费批次大小\">\n\t\t\t\t<fm-validators:required-validator>\n\t\t\t\t\t<message>必须填写${displayName}</message>\n\t\t\t\t</fm-validators:required-validator>\n\t\t\t\t<fm-validators:number-validator>\n\t\t\t\t\t<message>${displayName} 必须是数字且是整数</message>\n\t\t\t\t</fm-validators:number-validator>\n\t\t\t\t<fm-validators:number-validator greaterThan=\"0\">\n\t\t\t\t\t<message>${displayName} 必须是大于0的整数</message>\n\t\t\t\t</fm-validators:number-validator>\n\t\t\t</field>\n\t\t\t<field name=\"loadBatchsize\" displayName=\"Load批次大小\">\n\t\t\t\t<fm-validators:required-validator>\n\t\t\t\t\t<message>必须填写${displayName}</message>\n\t\t\t\t</fm-validators:required-validator>\n\t\t\t\t<fm-validators:number-validator>\n\t\t\t\t\t<message>${displayName} 必须是数字且是整数</message>\n\t\t\t\t</fm-validators:number-validator>\n\t\t\t\t<fm-validators:number-validator greaterThan=\"0\">\n\t\t\t\t\t<message>${displayName} 必须是大于0的整数</message>\n\t\t\t\t</fm-validators:number-validator>\n\t\t\t</field>\n\t\t\t<field name=\"channelInfo\" displayName=\"同步标记\" />\n\t\t\t<field name=\"dryRun\" displayName=\"DryRun模式\" />\n\t\t\t<field name=\"ddlSync\" displayName=\"支持ddl同步\" />\n\t\t\t<field name=\"batchTimeout\" displayName=\"获取批次数据超时时间\">\n\t\t\t\t<fm-validators:if test=\"selectorMode.value == 'Canal'\">\n\t\t\t\t\t<fm-validators:required-validator>\n\t\t\t\t\t\t<message>必须填写${displayName}</message>\n\t\t\t\t\t</fm-validators:required-validator>\n\t\t\t\t</fm-validators:if>\n\t\t\t</field>\n\t\t</group>\n\n\t\t<!-- - ======== - 数据库的 dataMediaSource - ======== -->\n\t\t<group name=\"dataMediaSourceInfo\" extends=\"csrfCheck\">\n\t\t\t<field name=\"formDataMediaSourceError\">\n\t\t\t\t<fm-validators:custom-error id=\"invalidDataMediaSource\">\n\t\t\t\t\t<message>数据库中存在重名的数据源</message>\n\t\t\t\t</fm-validators:custom-error>\n\t\t\t\t<fm-validators:custom-error id=\"canntConnectDataSource\">\n\t\t\t\t\t<message>数据库不能正常连接，请确认后再添加</message>\n\t\t\t\t</fm-validators:custom-error>\n\t\t\t</field>\n\t\t\t<field name=\"id\" displayName=\"dataSource的id\" />\n\t\t\t<field name=\"name\" displayName=\"dataSource的名称\">\n\t\t\t\t<fm-validators:required-validator>\n\t\t\t\t\t<message>必须填写${displayName}</message>\n\t\t\t\t</fm-validators:required-validator>\n\t\t\t</field>\n\t\t\t<field name=\"type\" displayName=\"dataSource的类型\" />\n\t\t\t<field name=\"username\" displayName=\"用户名\" />\n\t\t\t<field name=\"password\" displayName=\"密码\" />\n\t\t\t<field name=\"url\" displayName=\"链接\">\n\t\t\t\t<fm-validators:required-validator>\n\t\t\t\t\t<message>必须填写${displayName}</message>\n\t\t\t\t</fm-validators:required-validator>\n\t\t\t</field>\n\t\t\t<field name=\"encode\" displayName=\"数据库编码\">\n\t\t\t\t<fm-validators:required-validator>\n\t\t\t\t\t<message>必须选择${displayName}</message>\n\t\t\t\t</fm-validators:required-validator>\n\t\t\t</field>\n\t\t</group>\n\n\t\t<!-- - ======== - 字段映射 - ======== -->\n\t\t<group name=\"columnPairInfo\" extends=\"csrfCheck\">\n\t\t\t<field name=\"formColumnPairError\">\n\t\t\t\t<fm-validators:custom-error id=\"invalidColumnPair\">\n\t\t\t\t\t<message>左右映射关系数量不一致</message>\n\t\t\t\t</fm-validators:custom-error>\n\t\t\t</field>\n\t\t\t<field name=\"id\" displayName=\"id\" />\n\t\t\t<field name=\"dltTarget_l\" displayName=\"源字段集合\" />\n\t\t\t<field name=\"dltTarget_r\" displayName=\"目标字段集合\" />\n\t\t</group>\n\n\t\t<!-- - ======== - 字段组 - ======== -->\n\t\t<group name=\"columnPairGroupInfo\" extends=\"csrfCheck\">\n\t\t\t<field name=\"formColumnPairGroupError\">\n\t\t\t\t<fm-validators:custom-error id=\"invalidColumnPairGroup\">\n\t\t\t\t\t<message>映射组出错</message>\n\t\t\t\t</fm-validators:custom-error>\n\t\t\t</field>\n\t\t\t<field name=\"id\" displayName=\"id\" />\n\t\t\t<field name=\"groupResult\" displayName=\"字段组集合\" />\n\t\t</group>\n\n\t\t<!-- - ======== - dataMedia info - ======== -->\n\t\t<group name=\"dataMediaInfo\" extends=\"csrfCheck\">\n\t\t\t<field name=\"formDataMediaError\">\n\t\t\t\t<fm-validators:custom-error id=\"invalidDataMedia\">\n\t\t\t\t\t<message>数据库中存在重复的数据表</message>\n\t\t\t\t</fm-validators:custom-error>\n\t\t\t\t<fm-validators:custom-error id=\"canntSelectDataMedia\">\n\t\t\t\t\t<message>数据库不正常，不能添加</message>\n\t\t\t\t</fm-validators:custom-error>\n\t\t\t</field>\n\t\t\t<field name=\"id\" displayName=\"dataMedia的id\" />\n\t\t\t<field name=\"name\" displayName=\"名称\" />\n\t\t\t<field name=\"namespace\" displayName=\"命名空间\">\n\t\t\t\t<fm-validators:required-validator>\n\t\t\t\t\t<message>必须填写 ${displayName}</message>\n\t\t\t\t</fm-validators:required-validator>\n\t\t\t</field>\n\t\t\t<field name=\"encode\" displayName=\"数据编码\" />\n\t\t\t<field name=\"sourceName\" displayName=\"数据源名称\" />\n\t\t\t<field name=\"sourceId\" displayName=\"数据源\">\n\t\t\t\t<fm-validators:required-validator>\n\t\t\t\t\t<message>必须选择一个 ${displayName}</message>\n\t\t\t\t</fm-validators:required-validator>\n\t\t\t</field>\n\t\t</group>\n\n\t\t<!-- - ======== - zookeeperCluster info - ======== -->\n\t\t<group name=\"autokeeperClusterInfo\" extends=\"csrfCheck\">\n\t\t\t<field name=\"formAutokeeperClusterError\">\n\t\t\t\t<fm-validators:custom-error id=\"invalidAutokeeperCluster\">\n\t\t\t\t\t<message>数据库中存在重复的数据表</message>\n\t\t\t\t</fm-validators:custom-error>\n\t\t\t</field>\n\t\t\t<field name=\"id\" displayName=\"cluster的id\" />\n\t\t\t<field name=\"clusterName\" displayName=\"名称\" />\n\t\t\t<field name=\"zookeeperClusters\" displayName=\"ZK的服务器集群\">\n\t\t\t\t<fm-validators:required-validator>\n\t\t\t\t\t<message>必须填写${displayName}</message>\n\t\t\t\t</fm-validators:required-validator>\n\t\t\t\t<fm-validators:any-of>\n\t\t\t\t\t<fm-validators:regexp-validator pattern=\"^((((?:(?:25[0-5]|2[0-4]\\d|[01]?\\d?\\d)\\.){3}(?:25[0-5]|2[0-4]\\d|[01]?\\d?\\d)\\:\\d+\\,?)+)\\;)+$\">\n\t\t\t\t\t\t<message>${displayName} 不符合格式（1.1.1.1:80;）</message>\n\t\t\t\t\t</fm-validators:regexp-validator>\n\t\t\t\t\t<fm-validators:regexp-validator pattern=\"^((((([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\\-]*[a-zA-Z0-9])\\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\\-]*[A-Za-z0-9])\\:\\d+\\,?)+)\\;)+$\">\n\t\t\t\t\t\t<message>${displayName} 不是一个合法的(hostname:80;)</message>\n\t\t\t\t\t</fm-validators:regexp-validator>\n\t\t\t\t\t<fm-validators:regexp-validator pattern=\"^((((([a-zA-Z]|[a-zA-Z][a-zA-Z0-9\\-]*[a-zA-Z0-9])\\.)*([A-Za-z]|[A-Za-z][A-Za-z0-9\\-]*[A-Za-z0-9])\\:\\d+\\,?)+)\\;)+$\">\n\t\t\t\t\t\t<message>${displayName} 不是一个合法的(hostname:80;)</message>\n\t\t\t\t\t</fm-validators:regexp-validator>\n\t\t\t\t</fm-validators:any-of>\n\t\t\t</field>\n\t\t\t<field name=\"description\" displayName=\"描述信息\" />\n\t\t</group>\n\n\t\t<!-- - ======== - dataMediaPair info - ======== -->\n\t\t<group name=\"dataMediaPairInfo\" extends=\"csrfCheck\">\n\t\t\t<field name=\"formDataMediaPairError\">\n\t\t\t\t<fm-validators:custom-error id=\"invalidDataMediaPair\">\n\t\t\t\t\t<message>数据库中存在重复的映射关系</message>\n\t\t\t\t</fm-validators:custom-error>\n\t\t\t</field>\n\t\t\t<field name=\"batchPairContent\" displayName=\"批量导入大本文\">\n\t\t\t</field>\n\t\t\t<field name=\"id\" displayName=\"dataMedia的id\" />\n\t\t\t<field name=\"pullWeight\" displayName=\"提取权重\" />\n\n\t\t\t<field name=\"pushWeight\" displayName=\"载入权重\">\n\t\t\t\t<fm-validators:required-validator>\n\t\t\t\t\t<message>必须填写${displayName}</message>\n\t\t\t\t</fm-validators:required-validator>\n\t\t\t\t<fm-validators:number-validator>\n\t\t\t\t\t<message>${displayName} 必须是数字且是整数</message>\n\t\t\t\t</fm-validators:number-validator>\n\t\t\t\t<fm-validators:number-validator greaterThan=\"0\">\n\t\t\t\t\t<message>${displayName} 必须是大于0的整数</message>\n\t\t\t\t</fm-validators:number-validator>\n\t\t\t</field>\n\t\t\t<field name=\"filterType\" displayName=\"processor类型\" />\n\t\t\t<field name=\"filterText\" displayName=\"processor文本\" />\n\t\t\t<field name=\"resolverType\" displayName=\"resolver类型\" />\n\t\t\t<field name=\"resolverText\" displayName=\"resolver文本\" />\n\t\t\t<field name=\"filter\" displayName=\"过滤器类\" />\n\t\t\t<field name=\"sourceDataMediaName\"\n\t\t\t\tdisplayName=\"源数据表名称\" />\n\t\t\t<field name=\"targetDataMediaName\"\n\t\t\t\tdisplayName=\"目标数据表名称\" />\n\t\t\t<field name=\"sourceDataMediaId\" displayName=\"源数据表\">\n\t\t\t\t<fm-validators:required-validator>\n\t\t\t\t\t<message>必须选择 ${displayName}</message>\n\t\t\t\t</fm-validators:required-validator>\n\t\t\t</field>\n\t\t\t<field name=\"targetDataMediaId\" displayName=\"目标数据表\">\n\t\t\t\t<fm-validators:required-validator>\n\t\t\t\t\t<message>必须选择 ${displayName}</message>\n\t\t\t\t</fm-validators:required-validator>\n\t\t\t\t<fm-validators:string-compare-validator notEqualTo=\"sourceDataMediaId\">\n\t\t\t\t\t<message>${displayName} 不能与 ${sourceDataMediaId.displayName} 相同</message>\n\t\t\t\t</fm-validators:string-compare-validator>\n\t\t\t</field>\n\t\t\t<field name=\"pipelineId\" displayName=\"所属pipeline\" />\n\t\t\t<field name=\"columnPairMode\" displayName=\"视图配置模式\" />\n\t\t</group>\n\n\t\t<!-- - ======== - batchDataMediaPair info - ======== -->\n\t\t<group name=\"batchDataMediaPairInfo\" extends=\"csrfCheck\">\n\t\t\t<field name=\"formBatchDataMediaPairError\">\n\t\t\t\t<fm-validators:custom-error id=\"invalidBatchDataMediaPair\">\n\t\t\t\t\t<message>批量导入失败，请检查配置文档，可能原因:文档字段缺失或者sourceId不存在</message>\n\t\t\t\t</fm-validators:custom-error>\n\t\t\t</field>\n\t\t\t<field name=\"batchPairContent\" displayName=\"批量导入大本文\">\n\t\t\t</field>\n\t\t\t<field name=\"pipelineId\" displayName=\"所属pipeline\" />\n\t\t</group>\n\n\t\t<!-- - ======== - node info - ======== -->\n\t\t<group name=\"nodeInfo\" extends=\"csrfCheck\">\n\t\t\t<field name=\"formNodeError\">\n\t\t\t\t<fm-validators:custom-error id=\"invalidNode\">\n\t\t\t\t\t<message>数据库中存在重复的Node配置(可能原因：Node重复，或者IP+PORT重复)</message>\n\t\t\t\t</fm-validators:custom-error>\n\t\t\t</field>\n\t\t\t<field name=\"id\" displayName=\"服务器的ID\" />\n\t\t\t<field name=\"name\" displayName=\"服务器的名字\">\n\t\t\t\t<fm-validators:required-validator>\n\t\t\t\t\t<message>必须填写${displayName}</message>\n\t\t\t\t</fm-validators:required-validator>\n\t\t\t\t<fm-validators:string-length-validator minLength=\"4\"\n\t\t\t\t\tmaxLength=\"64\">\n\t\t\t\t\t<message>${displayName} 最少由${minLength}个字符组成，最多不能超过${maxLength}个字符</message>\n\t\t\t\t</fm-validators:string-length-validator>\n\t\t\t</field>\n\t\t\t<field name=\"ip\" displayName=\"服务器的IP\">\n\t\t\t\t<fm-validators:required-validator>\n\t\t\t\t\t<message>必须填写${displayName}</message>\n\t\t\t\t</fm-validators:required-validator>\n\t\t\t\t\n\t\t\t\t<fm-validators:any-of>\n\t\t\t\t\t<fm-validators:regexp-validator pattern=\"((?:(?:25[0-5]|2[0-4]\\d|[01]?\\d?\\d)\\.){3}(?:25[0-5]|2[0-4]\\d|[01]?\\d?\\d))\">\n\t\t\t\t\t\t<message>${displayName} 不符合格式（1.1.1.1）</message>\n\t\t\t\t\t</fm-validators:regexp-validator>\n\t\t\t\t\t<fm-validators:regexp-validator pattern=\"^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\\-]*[a-zA-Z0-9])\\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\\-]*[A-Za-z0-9])$\">\n\t\t\t\t\t\t<message>${displayName} 不是一个合法的(hostname)</message>\n\t\t\t\t\t</fm-validators:regexp-validator>\n\t\t\t\t\t<fm-validators:regexp-validator pattern=\"^(([a-zA-Z]|[a-zA-Z][a-zA-Z0-9\\-]*[a-zA-Z0-9])\\.)*([A-Za-z]|[A-Za-z][A-Za-z0-9\\-]*[A-Za-z0-9])$\">\n\t\t\t\t\t\t<message>${displayName} 不是一个合法的(hostname)</message>\n\t\t\t\t\t</fm-validators:regexp-validator>\n\t\t\t\t</fm-validators:any-of>\n\t\t\t</field>\n\t\t\t<field name=\"port\" displayName=\"服务器的端口\">\n\t\t\t\t<fm-validators:required-validator>\n\t\t\t\t\t<message>必须填写${displayName}</message>\n\t\t\t\t</fm-validators:required-validator>\n\t\t\t\t<fm-validators:number-validator>\n\t\t\t\t\t<message>${displayName} 必须是数字且是整数</message>\n\t\t\t\t</fm-validators:number-validator>\n\t\t\t\t<fm-validators:number-validator greaterThan=\"0\">\n\t\t\t\t\t<message>${displayName} 必须是大于0的整数</message>\n\t\t\t\t</fm-validators:number-validator>\n\t\t\t</field>\n\t\t\t<field name=\"description\" displayName=\"服务器的描述\" />\n\t\t</group>\n\n\t\t<!-- - ======== - alarmRule info - ======== -->\n\t\t<group name=\"alarmRuleInfo\" extends=\"csrfCheck\">\n\t\t\t<field name=\"formAlarmRuleError\">\n\t\t\t\t<fm-validators:custom-error id=\"invalidAlarmRule\">\n\t\t\t\t\t<message>错误的监控配置</message>\n\t\t\t\t</fm-validators:custom-error>\n\t\t\t</field>\n\t\t\t<field name=\"id\" displayName=\"监控的ID\" />\n\t\t\t<field name=\"monitorName\" displayName=\"监控项目\">\n\t\t\t\t<fm-validators:required-validator>\n\t\t\t\t\t<message>必须填写${displayName}</message>\n\t\t\t\t</fm-validators:required-validator>\n\t\t\t</field>\n\t\t\t<field name=\"pipelineId\" displayName=\"pipelineId\" />\n\t\t\t<field name=\"status\" displayName=\"状态\" />\n\t\t\t<field name=\"intervalTime\" displayName=\"报警间隔时间\">\n\t\t\t\t<fm-validators:required-validator>\n\t\t\t\t\t<message>必须填写${displayName}</message>\n\t\t\t\t</fm-validators:required-validator>\n\t\t\t</field>\n\t\t\t<field name=\"matchValue\" displayName=\"监控的阈值\">\n\t\t\t\t<fm-validators:required-validator>\n\t\t\t\t\t<message>必须填写${displayName}</message>\n\t\t\t\t</fm-validators:required-validator>\n\t\t\t</field>\n\t\t\t<field name=\"receiverKey\" displayName=\"发送对象KEY\">\n\t\t\t\t<fm-validators:required-validator>\n\t\t\t\t\t<message>必须填写${displayName}</message>\n\t\t\t\t</fm-validators:required-validator>\n\t\t\t</field>\n\t\t\t<field name=\"autoRecovery\" displayName=\"自动恢复\" />\n\t\t\t<field name=\"recoveryThresold\" displayName=\"自动恢复阀值\">\n\t\t\t\t<fm-validators:number-validator>\n\t\t\t\t\t<message>${displayName} 必须是数字且是整数</message>\n\t\t\t\t</fm-validators:number-validator>\n\t\t\t</field>\n\t\t\t<field name=\"description\" displayName=\"服务器的描述\" />\n\t\t</group>\n\n\t\t<!-- - ======== - nodeParameter info - ======== -->\n\t\t<group name=\"nodeParameterInfo\" extends=\"csrfCheck\">\n\t\t\t<field name=\"downloadPort\" displayName=\"下载端口\">\n\t\t\t\t<!-- <required-validator> <message>必须填写${displayName}</message> </required-validator> -->\n\t\t\t\t<fm-validators:number-validator>\n\t\t\t\t\t<message>${displayName} 必须是数字且是整数</message>\n\t\t\t\t</fm-validators:number-validator>\n\t\t\t\t<fm-validators:number-validator greaterThan=\"0\">\n\t\t\t\t\t<message>${displayName} 必须是大于0的整数</message>\n\t\t\t\t</fm-validators:number-validator>\n\t\t\t</field>\n\t\t\t<field name=\"mbeanPort\" displayName=\"MBean端口\">\n\t\t\t\t<!-- <required-validator> <message>必须填写${displayName}</message> </required-validator> -->\n\t\t\t\t<fm-validators:number-validator>\n\t\t\t\t\t<message>${displayName} 必须是数字且是整数</message>\n\t\t\t\t</fm-validators:number-validator>\n\t\t\t\t<fm-validators:number-validator greaterThan=\"0\">\n\t\t\t\t\t<message>${displayName} 必须是大于0的整数</message>\n\t\t\t\t</fm-validators:number-validator>\n\t\t\t</field>\n\t\t\t<field name=\"externalIp\" displayName=\"外部IP\">\n\t\t\t\t<fm-validators:if test=\"useExternalIp.value\">\n\t\t\t\t\t<fm-validators:required-validator>\n\t\t\t\t\t\t<message>启用外部IP通讯时必须填写${displayName}</message>\n\t\t\t\t\t</fm-validators:required-validator>\n\t\t\t\t</fm-validators:if>\n\t\t\t</field>\n\t\t\t<field name=\"useExternalIp\" displayName=\"启用外部IP\" />\n\t\t\t<field name=\"autoKeeperClusterId\" displayName=\"zookeeper集群\" />\n\t\t</group>\n\n\t\t<group name=\"userInfo\" extends=\"csrfCheck\">\n\t\t\t<field name=\"formUserError\">\n\t\t\t\t<fm-validators:custom-error id=\"invalidUser\">\n\t\t\t\t\t<message>用户名重复</message>\n\t\t\t\t</fm-validators:custom-error>\n\t\t\t\t<fm-validators:custom-error id=\"passwordTooLess\">\n\t\t\t\t\t<message>新修改的密码低于6位</message>\n\t\t\t\t</fm-validators:custom-error>\n\t\t\t</field>\n\t\t\t<field name=\"id\" displayName=\"用户ID\" />\n\t\t\t<field name=\"department\" displayName=\"部门\">\n\t\t\t\t<fm-validators:required-validator>\n\t\t\t\t\t<message>必须填写 ${displayName}</message>\n\t\t\t\t</fm-validators:required-validator>\n\t\t\t</field>\n\t\t\t<field name=\"realName\" displayName=\"真实姓名\">\n\t\t\t\t<fm-validators:required-validator>\n\t\t\t\t\t<message>必须填写 ${displayName}</message>\n\t\t\t\t</fm-validators:required-validator>\n\t\t\t</field>\n\t\t\t<field name=\"authorizeType\" displayName=\"权限\" />\n\t\t</group>\n\n\t\t<group name=\"addUserInfo\" extends=\"userInfo\">\n\t\t\t<field name=\"name\" displayName=\"登录名\">\n\t\t\t\t<fm-validators:required-validator>\n\t\t\t\t\t<message>必须填写 ${displayName}</message>\n\t\t\t\t</fm-validators:required-validator>\n\t\t\t\t<fm-validators:string-length-validator minLength=\"3\"\n\t\t\t\t\tmaxLength=\"64\">\n\t\t\t\t\t<message>${displayName} 最少由${minLength}个字符组成，最多不能超过${maxLength}个字符</message>\n\t\t\t\t</fm-validators:string-length-validator>\n\t\t\t</field>\n\t\t\t<field name=\"password\" displayName=\"密码\">\n\t\t\t\t<fm-validators:required-validator>\n\t\t\t\t\t<message>必须填写 ${displayName}</message>\n\t\t\t\t</fm-validators:required-validator>\n\t\t\t</field>\n\t\t\t<field name=\"rePassword\" displayName=\"重新输入一次密码\">\n\t\t\t\t<fm-validators:required-validator>\n\t\t\t\t\t<message>必须填写 ${displayName}</message>\n\t\t\t\t</fm-validators:required-validator>\n\t\t\t\t<fm-validators:string-compare-validator equalTo=\"password\">\n\t\t\t\t\t<message>两次密码输入不相同</message>\n\t\t\t\t</fm-validators:string-compare-validator>\n\t\t\t</field>\n\t\t</group>\n\n\t\t<group name=\"editUserInfo\" extends=\"userInfo\">\n\t\t\t<field name=\"name\" displayName=\"登录名\" />\n\t\t\t<field name=\"password\" displayName=\"密码\" />\n\t\t\t<field name=\"rePassword\" displayName=\"重新输入一次密码\">\n\t\t\t\t<fm-validators:string-compare-validator equalTo=\"password\">\n\t\t\t\t\t<message>两次密码输入不相同</message>\n\t\t\t\t</fm-validators:string-compare-validator>\n\t\t\t</field>\n\t\t</group>\n\n\n\t\t<group name=\"login\">\n\t\t\t<field name=\"loginError\">\n\t\t\t\t<fm-validators:custom-error id=\"invalidUserOrPassword\">\n\t\t\t\t\t<message>用户名或密码不正确,请重新输入</message>\n\t\t\t\t</fm-validators:custom-error>\n\t\t\t</field>\n\t\t\t<field name=\"name\" displayName=\"登录名\">\n\t\t\t\t<fm-validators:required-validator>\n\t\t\t\t\t<message>必须填写 ${displayName}</message>\n\t\t\t\t</fm-validators:required-validator>\n\t\t\t</field>\n\t\t\t<field name=\"password\" displayName=\"密码\">\n\t\t\t\t<fm-validators:required-validator>\n\t\t\t\t\t<message>必须填写 ${displayName}</message>\n\t\t\t\t</fm-validators:required-validator>\n\t\t\t</field>\n\t\t</group>\n\n\t\t<!-- - ======== - systemPamameter info - ======== -->\n\t\t<group name=\"systemParameterInfo\" extends=\"csrfCheck\">\n\t\t\t<field name=\"id\" displayName=\"系统参数ID\">\n\t\t\t\t<fm-validators:required-validator>\n\t\t\t\t\t<message>必须填写${displayName}</message>\n\t\t\t\t</fm-validators:required-validator>\n\t\t\t</field>\n\t\t</group>\n\n\t\t<!-- - ======== - sysremPamameterDetail info - ======== -->\n\t\t<group name=\"systemParameterDetailInfo\"\n\t\t\textends=\"csrfCheck\">\n\t\t\t<field name=\"systemSchema\" displayName=\"system.schema\">\n\t\t\t\t<fm-validators:required-validator>\n\t\t\t\t\t<message>必须填写${displayName}</message>\n\t\t\t\t</fm-validators:required-validator>\n\t\t\t</field>\n\t\t\t<field name=\"systemMarkTable\" displayName=\"system.markTable\">\n\t\t\t\t<fm-validators:required-validator>\n\t\t\t\t\t<message>必须填写${displayName}</message>\n\t\t\t\t</fm-validators:required-validator>\n\t\t\t</field>\n\t\t\t<field name=\"systemMarkTableColumn\"\n\t\t\t\tdisplayName=\"system.markTable.column\">\n\t\t\t\t<fm-validators:required-validator>\n\t\t\t\t\t<message>必须填写${displayName}</message>\n\t\t\t\t</fm-validators:required-validator>\n\t\t\t</field>\n\t\t\t<field name=\"systemMarkTableInfo\"\n\t\t\t\tdisplayName=\"system.markTable.info\">\n\t\t\t\t<fm-validators:required-validator>\n\t\t\t\t\t<message>必须填写${displayName}</message>\n\t\t\t\t</fm-validators:required-validator>\n\t\t\t</field>\n\t\t\t<field name=\"systemBufferTable\" displayName=\"system.bufferTable\">\n\t\t\t\t<fm-validators:required-validator>\n\t\t\t\t\t<message>必须填写${displayName}</message>\n\t\t\t\t</fm-validators:required-validator>\n\t\t\t</field>\n\t\t\t<field name=\"systemDualTable\" displayName=\"system.dualTable\">\n\t\t\t\t<fm-validators:required-validator>\n\t\t\t\t\t<message>必须填写${displayName}</message>\n\t\t\t\t</fm-validators:required-validator>\n\t\t\t</field>\n\t\t\t<field name=\"retriever\" displayName=\"otter.download.retriever\">\n\t\t\t\t<fm-validators:required-validator>\n\t\t\t\t\t<message>必须填写${displayName}</message>\n\t\t\t\t</fm-validators:required-validator>\n\t\t\t</field>\n\t\t\t<field name=\"defaultAlarmReceiver\" displayName=\"默认报警联系人\">\n\t\t\t\t<fm-validators:required-validator>\n\t\t\t\t\t<message>必须填写${displayName}</message>\n\t\t\t\t</fm-validators:required-validator>\n\t\t\t</field>\n\t\t\t<field name=\"alarmReceiver\" displayName=\"自定义报警联系人\"/>\n\t\t</group>\n\n\t\t<!-- - ======== - canal info - ======== -->\n\t\t<group name=\"canalInfo\" extends=\"csrfCheck\">\n\t\t\t<field name=\"formCanalError\">\n\t\t\t\t<fm-validators:custom-error id=\"invalidCanal\">\n\t\t\t\t\t<message>数据库中存在重复的Canal配置(可能原因：Canal Name重复 )</message>\n\t\t\t\t</fm-validators:custom-error>\n\t\t\t</field>\n\t\t\t<field name=\"id\" displayName=\"canal ID\" />\n\t\t\t<field name=\"name\" displayName=\"canal的名字\" />\n\t\t\t<field name=\"desc\" displayName=\"canal的描述\" />\n\t\t</group>\n\n\t\t<!-- - ======== - canalParameter info - ======== -->\n\t\t<group name=\"canalParameterInfo\" extends=\"csrfCheck\">\n\t\t\t<field name=\"formHeartBeatError\">\n\t\t\t\t<fm-validators:custom-error id=\"invaliedHeartBeat\">\n\t\t\t\t\t<message>心跳SQL不允许使用select,建议为insert/update,定时产生binlog数据</message>\n\t\t\t\t</fm-validators:custom-error>\n\t\t\t</field>\n\t\t\t<field name=\"id\" displayName=\"canal ID\" />\n\t\t\t<field name=\"runMode\" displayName=\"运行模式\" />\n\t\t\t<field name=\"clusterMode\" displayName=\"集群模式\" />\n\t\t\t<field name=\"autoKeeperClusterId\" displayName=\"zookeeper集群\" />\n\t\t\t<field name=\"zkClusters\" displayName=\"ZK集群\">\n\t\t\t\t<fm-validators:any-of>\n\t\t\t\t\t<fm-validators:regexp-validator pattern=\"^((((?:(?:25[0-5]|2[0-4]\\d|[01]?\\d?\\d)\\.){3}(?:25[0-5]|2[0-4]\\d|[01]?\\d?\\d)\\:\\d+\\,?)+)\\;)+$\">\n\t\t\t\t\t\t<message>${displayName} 不符合格式（1.1.1.1:80;）</message>\n\t\t\t\t\t</fm-validators:regexp-validator>\n\t\t\t\t\t<fm-validators:regexp-validator pattern=\"^((((([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\\-]*[a-zA-Z0-9])\\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\\-]*[A-Za-z0-9])\\:\\d+\\,?)+)\\;)+$\">\n\t\t\t\t\t\t<message>${displayName} 不是一个合法的(hostname:80;)</message>\n\t\t\t\t\t</fm-validators:regexp-validator>\n\t\t\t\t\t<fm-validators:regexp-validator pattern=\"^((((([a-zA-Z]|[a-zA-Z][a-zA-Z0-9\\-]*[a-zA-Z0-9])\\.)*([A-Za-z]|[A-Za-z][A-Za-z0-9\\-]*[A-Za-z0-9])\\:\\d+\\,?)+)\\;)+$\">\n\t\t\t\t\t\t<message>${displayName} 不是一个合法的(hostname:80;)</message>\n\t\t\t\t\t</fm-validators:regexp-validator>\n\t\t\t\t</fm-validators:any-of>\n\t\t\t</field>\n\t\t\t<field name=\"metaMode\" displayName=\"meta机制\">\n\t\t\t\t<fm-validators:required-validator>\n\t\t\t\t\t<message>必须填写${displayName}</message>\n\t\t\t\t</fm-validators:required-validator>\n\t\t\t</field>\n\t\t\t<field name=\"indexMode\" displayName=\"索引机制\">\n\t\t\t\t<fm-validators:required-validator>\n\t\t\t\t\t<message>必须填写${displayName}</message>\n\t\t\t\t</fm-validators:required-validator>\n\t\t\t</field>\n\t\t\t<field name=\"sourcingType\" displayName=\"数据来源类型\">\n\t\t\t\t<fm-validators:required-validator>\n\t\t\t\t\t<message>必须填写${displayName}</message>\n\t\t\t\t</fm-validators:required-validator>\n\t\t\t</field>\n\t\t\t<field name=\"haMode\" displayName=\"ha机制\">\n\t\t\t\t<fm-validators:required-validator>\n\t\t\t\t\t<message>必须填写${displayName}</message>\n\t\t\t\t</fm-validators:required-validator>\n\t\t\t</field>\n\t\t\t<field name=\"storageMode\" displayName=\"存储机制\">\n\t\t\t\t<fm-validators:required-validator>\n\t\t\t\t\t<message>必须填写${displayName}</message>\n\t\t\t\t</fm-validators:required-validator>\n\t\t\t</field>\n\n\t\t\t<field name=\"groupDbAddresses\" displayName=\"数据库信息\">\n\t\t\t\t<fm-validators:if test=\"haMode.value == 'HEARTBEAT'\">\n\t\t\t\t\t<fm-validators:required-validator>\n\t\t\t\t\t\t<message>必须填写${displayName}</message>\n\t\t\t\t\t</fm-validators:required-validator>\n\t\t\t\t\t<fm-validators:any-of>\n\t\t\t\t\t\t<fm-validators:regexp-validator pattern=\"^((((?:(?:25[0-5]|2[0-4]\\d|[01]?\\d?\\d)\\.){3}(?:25[0-5]|2[0-4]\\d|[01]?\\d?\\d)\\:\\d+\\,?)+)\\;)+$\">\n\t\t\t\t\t\t\t<message>${displayName} 不符合格式（127.0.0.1:3306;）</message>\n\t\t\t\t\t\t</fm-validators:regexp-validator>\n\t\t\t\t\t\t<fm-validators:regexp-validator pattern=\"^((((([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\\-]*[a-zA-Z0-9])\\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\\-]*[A-Za-z0-9])\\:\\d+\\,?)+)\\;)+$\">\n\t\t\t\t\t\t\t<message>${displayName} 不是一个合法的(hostname:3306;)</message>\n\t\t\t\t\t\t</fm-validators:regexp-validator>\n\t\t\t\t\t\t<fm-validators:regexp-validator pattern=\"^((((([a-zA-Z]|[a-zA-Z][a-zA-Z0-9\\-]*[a-zA-Z0-9])\\.)*([A-Za-z]|[A-Za-z][A-Za-z0-9\\-]*[A-Za-z0-9])\\:\\d+\\,?)+)\\;)+$\">\n\t\t\t\t\t\t\t<message>${displayName} 不是一个合法的(hostname:3306;)</message>\n\t\t\t\t\t\t</fm-validators:regexp-validator>\n\t\t\t\t\t</fm-validators:any-of>\n\t\t\t\t</fm-validators:if>\n\t\t\t</field>\n\t\t\t<field name=\"dbUsername\" displayName=\"数据库帐号\">\n\t\t\t\t<fm-validators:if test=\"haMode.value != 'TDDL'\">\n\t\t\t\t\t<fm-validators:required-validator>\n\t\t\t\t\t\t<message>不使用tddl配置时必须填写${displayName}</message>\n\t\t\t\t\t</fm-validators:required-validator>\n\t\t\t\t</fm-validators:if>\n\t\t\t</field>\n\t\t\t<field name=\"dbPassword\" displayName=\"数据库密码\">\n\t\t\t\t<fm-validators:if test=\"haMode.value != 'TDDL'\">\n\t\t\t\t\t<fm-validators:required-validator>\n\t\t\t\t\t\t<message>不使用tddl配置时必须填写${displayName}</message>\n\t\t\t\t\t</fm-validators:required-validator>\n\t\t\t\t</fm-validators:if>\n\t\t\t</field>\n\t\t\t<field name=\"slaveId\" displayName=\"链接到mysql的slaveId\">\n\t\t\t\t<!-- <fm-validators:if test=\"sourcingType.value == 'MYSQL'\"> <required-validator> <message>数据源为MYSQL时必须填写${displayName}</message> \n\t\t\t\t\t</required-validator> </fm-validators:if> -->\n\t\t\t</field>\n\t\t\t<field name=\"connectionCharset\" displayName=\"connectionCharset\">\n\t\t\t\t<fm-validators:required-validator>\n\t\t\t\t\t<message>必须填写${displayName}</message>\n\t\t\t\t</fm-validators:required-validator>\n\t\t\t</field>\n\t\t\t<field name=\"localBinlogDirectory\"\n\t\t\t\tdisplayName=\"本地localBinlog目录\">\n\t\t\t\t<fm-validators:if test=\"sourcingType.value == 'LOCALBINLOG'\">\n\t\t\t\t\t<fm-validators:required-validator>\n\t\t\t\t\t\t<message>数据源为LOCALBINLOG时必须填写${displayName}</message>\n\t\t\t\t\t</fm-validators:required-validator>\n\t\t\t\t</fm-validators:if>\n\t\t\t</field>\n\t\t\t<field name=\"rsList\" displayName=\"rsList\">\n\t\t\t\t<fm-validators:if test=\"sourcingType.value == 'OCEANBASE'\">\n\t\t\t\t\t<fm-validators:required-validator>\n\t\t\t\t\t\t<message>数据源为OceanBase时必须填写${displayName}</message>\n\t\t\t\t\t</fm-validators:required-validator>\n\t\t\t\t</fm-validators:if>\n\t\t\t</field>\n\t\t\t<field name=\"tenant\" displayName=\"tenant\">\n\t\t\t\t<fm-validators:if test=\"sourcingType.value == 'OCEANBASE'\">\n\t\t\t\t\t<fm-validators:required-validator>\n\t\t\t\t\t\t<message>数据源为OceanBase时必须填写${displayName}</message>\n\t\t\t\t\t</fm-validators:required-validator>\n\t\t\t\t</fm-validators:if>\n\t\t\t</field>\n\t\t\t<field name=\"positions\" displayName=\"位点信息\">\n\t\t\t\t<fm-validators:regexp-validator pattern=\"^(\\{.*\\}\\;)+$\">\n\t\t\t\t\t<message>${displayName} 不符合格式（{\"journalName\":\"\",\"position\":0,\"timestamp\":0};）</message>\n\t\t\t\t</fm-validators:regexp-validator>\n\t\t\t</field>\n\t\t\t<field name=\"storageBatchMode\" displayName=\"内存存储batch获取模式\">\n\t\t\t\t<fm-validators:if test=\"storageMode.value == 'MEMORY'\">\n\t\t\t\t\t<fm-validators:required-validator>\n\t\t\t\t\t\t<message>存储模式为MEMORY时必须填写${displayName}</message>\n\t\t\t\t\t</fm-validators:required-validator>\n\t\t\t\t</fm-validators:if>\n\t\t\t</field>\n\t\t\t<field name=\"memoryStorageBufferSize\"\n\t\t\t\tdisplayName=\"内存存储buffer记录数\">\n\t\t\t\t<fm-validators:if test=\"storageMode.value == 'MEMORY'\">\n\t\t\t\t\t<fm-validators:required-validator>\n\t\t\t\t\t\t<message>存储模式为MEMORY时必须填写${displayName}</message>\n\t\t\t\t\t</fm-validators:required-validator>\n\t\t\t\t</fm-validators:if>\n\t\t\t</field>\n\t\t\t<field name=\"memoryStorageBufferMemUnit\"\n\t\t\t\tdisplayName=\"内存存储buffer记录单元大小\">\n\t\t\t\t<fm-validators:if test=\"storageMode.value == 'MEMORY'\">\n\t\t\t\t\t<fm-validators:required-validator>\n\t\t\t\t\t\t<message>存储模式为MEMORY时必须填写${displayName}</message>\n\t\t\t\t\t</fm-validators:required-validator>\n\t\t\t\t</fm-validators:if>\n\t\t\t</field>\n\t\t\t<field name=\"fileStorageDirectory\"\n\t\t\t\tdisplayName=\"文件存储的目录位置\">\n\t\t\t\t<fm-validators:if test=\"storageMode.value == 'FILE'\">\n\t\t\t\t\t<fm-validators:required-validator>\n\t\t\t\t\t\t<message>存储模式为FILE时必须填写${displayName}</message>\n\t\t\t\t\t</fm-validators:required-validator>\n\t\t\t\t</fm-validators:if>\n\t\t\t</field>\n\t\t\t<field name=\"fileStorageStoreCount\"\n\t\t\t\tdisplayName=\"每个文件store存储的记录数\">\n\t\t\t\t<fm-validators:if test=\"storageMode.value == 'FILE'\">\n\t\t\t\t\t<fm-validators:required-validator>\n\t\t\t\t\t\t<message>存储模式为FILE时必须填写${displayName}</message>\n\t\t\t\t\t</fm-validators:required-validator>\n\t\t\t\t</fm-validators:if>\n\t\t\t</field>\n\t\t\t<field name=\"fileStorageRollverCount\"\n\t\t\t\tdisplayName=\"store文件的个数\">\n\t\t\t\t<fm-validators:if test=\"storageMode.value == 'FILE'\">\n\t\t\t\t\t<fm-validators:required-validator>\n\t\t\t\t\t\t<message>存储模式为FILE时必须填写${displayName}</message>\n\t\t\t\t\t</fm-validators:required-validator>\n\t\t\t\t</fm-validators:if>\n\t\t\t</field>\n\t\t\t<field name=\"fileStoragePercentThresold\"\n\t\t\t\tdisplayName=\"整个store存储占disk硬盘的百分比\">\n\t\t\t\t<fm-validators:if test=\"storageMode.value == 'FILE'\">\n\t\t\t\t\t<fm-validators:required-validator>\n\t\t\t\t\t\t<message>存储模式为FILE时必须填写${displayName}</message>\n\t\t\t\t\t</fm-validators:required-validator>\n\t\t\t\t</fm-validators:if>\n\t\t\t</field>\n\t\t\t<field name=\"metaqStoreUri\" displayName=\"metaq store uri\">\n\t\t\t\t<fm-validators:if test=\"storageMode.value == 'METAQ'\">\n\t\t\t\t\t<fm-validators:required-validator>\n\t\t\t\t\t\t<message>存储模式为METAQ时必须填写${displayName}</message>\n\t\t\t\t\t</fm-validators:required-validator>\n\t\t\t\t</fm-validators:if>\n\t\t\t</field>\n\t\t\t<field name=\"port\" displayName=\"服务端口\" />\n\t\t\t<field name=\"defaultConnectionTimeoutInSeconds\"\n\t\t\t\tdisplayName=\"默认连接超时时间\">\n\t\t\t\t<fm-validators:required-validator>\n\t\t\t\t\t<message>必须填写${displayName}</message>\n\t\t\t\t</fm-validators:required-validator>\n\t\t\t</field>\n\t\t\t<field name=\"receiveBufferSize\" displayName=\"接收BufferSize\">\n\t\t\t\t<fm-validators:required-validator>\n\t\t\t\t\t<message>必须填写${displayName}</message>\n\t\t\t\t</fm-validators:required-validator>\n\t\t\t</field>\n\t\t\t<field name=\"sendBufferSize\" displayName=\"发送BufferSize\">\n\t\t\t\t<fm-validators:required-validator>\n\t\t\t\t\t<message>必须填写${displayName}</message>\n\t\t\t\t</fm-validators:required-validator>\n\t\t\t</field>\n\n\t\t\t<field name=\"tsdbEnable\" displayName=\"是否开启表结构tsdb\">\n\t\t\t\t<fm-validators:required-validator>\n\t\t\t\t\t<message>必须填写${displayName}</message>\n\t\t\t\t</fm-validators:required-validator>\n\t\t\t</field>\n\t\t\t<field name=\"gtidEnable\" displayName=\"是否启用gtid位点\">\n\t\t\t\t<fm-validators:required-validator>\n\t\t\t\t\t<message>必须填写${displayName}</message>\n\t\t\t\t</fm-validators:required-validator>\n\t\t\t</field>\n\t\t\t<field name=\"detectingEnable\" displayName=\"是否开启心跳\">\n\t\t\t\t<fm-validators:required-validator>\n\t\t\t\t\t<message>必须填写${displayName}</message>\n\t\t\t\t</fm-validators:required-validator>\n\t\t\t</field>\n\t\t\t<field name=\"detectingSQL\" displayName=\"心跳sql\">\n\t\t\t\t<fm-validators:if test=\"detectingEnable.value == true\">\n\t\t\t\t\t<fm-validators:required-validator>\n\t\t\t\t\t\t<message>开启心跳模式时必须填写${displayName}</message>\n\t\t\t\t\t</fm-validators:required-validator>\n\t\t\t\t</fm-validators:if>\n\t\t\t</field>\n\t\t\t<field name=\"detectingIntervalInSeconds\"\n\t\t\t\tdisplayName=\"心跳检测频率\">\n\t\t\t\t<fm-validators:if test=\"detectingEnable.value == true\">\n\t\t\t\t\t<fm-validators:required-validator>\n\t\t\t\t\t\t<message>开启心跳模式时必须填写${displayName}</message>\n\t\t\t\t\t</fm-validators:required-validator>\n\t\t\t\t</fm-validators:if>\n\t\t\t</field>\n\t\t\t<field name=\"detectingTimeoutThresholdInSeconds\"\n\t\t\t\tdisplayName=\"心跳超时时间\">\n\t\t\t\t<fm-validators:if test=\"detectingEnable.value == true\">\n\t\t\t\t\t<fm-validators:required-validator>\n\t\t\t\t\t\t<message>开启心跳模式时必须填写${displayName}</message>\n\t\t\t\t\t</fm-validators:required-validator>\n\t\t\t\t</fm-validators:if>\n\t\t\t</field>\n\t\t\t<field name=\"detectingRetryTimes\"\n\t\t\t\tdisplayName=\"心跳检查重试次数\">\n\t\t\t\t<fm-validators:if test=\"detectingEnable.value == true\">\n\t\t\t\t\t<fm-validators:required-validator>\n\t\t\t\t\t\t<message>开启心跳模式时必须填写${displayName}</message>\n\t\t\t\t\t</fm-validators:required-validator>\n\t\t\t\t</fm-validators:if>\n\t\t\t</field>\n\t\t\t<field name=\"app\" displayName=\"tddl app key\">\n\t\t\t\t<fm-validators:if test=\"haMode.value == 'TDDL'\">\n\t\t\t\t\t<fm-validators:required-validator>\n\t\t\t\t\t\t<message>HA模式为TDDL时必须填写${displayName}</message>\n\t\t\t\t\t</fm-validators:required-validator>\n\t\t\t\t</fm-validators:if>\n\t\t\t</field>\n\t\t\t<field name=\"group\" displayName=\"tddl group key\">\n\t\t\t\t<fm-validators:if test=\"haMode.value == 'TDDL'\">\n\t\t\t\t\t<fm-validators:required-validator>\n\t\t\t\t\t\t<message>HA模式为TDDL时必须填写${displayName}</message>\n\t\t\t\t\t</fm-validators:required-validator>\n\t\t\t\t</fm-validators:if>\n\t\t\t</field>\n\n\t\t\t<field name=\"mediaGroup\" displayName=\"media group key\">\n\t\t\t\t<fm-validators:if test=\"haMode.value == 'MEDIA'\">\n\t\t\t\t\t<fm-validators:required-validator>\n\t\t\t\t\t\t<message>HA模式为MEDIA时必须填写${displayName}</message>\n\t\t\t\t\t</fm-validators:required-validator>\n\t\t\t\t</fm-validators:if>\n\t\t\t</field>\n\n\t\t\t<field name=\"fallbackIntervalInSeconds\"\n\t\t\t\tdisplayName=\"切换回退时间\">\n\t\t\t\t<fm-validators:required-validator>\n\t\t\t\t\t<message>必须填写${displayName}</message>\n\t\t\t\t</fm-validators:required-validator>\n\t\t\t</field>\n\t\t\t<field name=\"heartbeatHaEnable\" displayName=\"是否启用心跳HA\">\n\t\t\t\t<fm-validators:required-validator>\n\t\t\t\t\t<message>必须填写${displayName}</message>\n\t\t\t\t</fm-validators:required-validator>\n\t\t\t</field>\n\t\t\t<field name=\"blackFilter\" displayName=\"过滤表达式\"/>\n\t\t\t<field name=\"rdsAccesskey\" displayName=\"rds accesskey\" />\n\t\t\t<field name=\"rdsSecretkey\" displayName=\"rds secretkey\" />\n\t\t\t<field name=\"rdsInstanceId\" displayName=\"rds instance\" />\n\t\t</group>\n\n\t\t<group name=\"dataMatrixInfo\" extends=\"csrfCheck\">\n\t\t\t<field name=\"formDataMatrixError\">\n\t\t\t\t<fm-validators:custom-error id=\"invalidDataMatrix\">\n\t\t\t\t\t<message>重复的groupKey设置</message>\n\t\t\t\t</fm-validators:custom-error>\n\t\t\t</field>\n\t\t\t<field name=\"groupKey\" displayName=\"groupKey\">\n\t\t\t\t<fm-validators:required-validator>\n\t\t\t\t\t<message>必须填写 ${displayName}</message>\n\t\t\t\t</fm-validators:required-validator>\n\t\t\t</field>\n\t\t\t<field name=\"master\" displayName=\"master\">\n\t\t\t\t<fm-validators:required-validator>\n\t\t\t\t\t<message>必须填写 ${displayName}</message>\n\t\t\t\t</fm-validators:required-validator>\n\t\t\t</field>\n\t\t\t<field name=\"slave\" displayName=\"slave\" />\n\t\t\t<field name=\"description\" displayName=\"描述\" />\n\t\t\t<field name=\"id\" displayName=\"matrix id\" />\n\t\t</group>\n\t</services:form>\n</beans:beans>\n"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/WEB-INF/web.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<web-app version=\"2.4\" xmlns=\"http://java.sun.com/xml/ns/j2ee\"\n\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://java.sun.com/xml/ns/j2ee  http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd\">\n\n\t<context-param>\n\t\t<param-name>logSystem</param-name>\n\t\t<param-value>logback</param-value>\n\t</context-param>\n\t<context-param>\n\t\t<param-name>logConfiguration</param-name>\n\t\t<param-value>/WEB-INF/logback.xml</param-value>\n\t</context-param>\n \n \t<!-- \n\t<listener>\n\t\t<listener-class>com.alibaba.citrus.logconfig.LogConfiguratorListener</listener-class>\n\t</listener>\n \t-->\n \n\t<listener>\n\t\t<listener-class>com.alibaba.citrus.webx.context.WebxContextLoaderListener</listener-class>\n\t</listener>\n\n\t<filter>\n\t\t<filter-name>mdc</filter-name>\n\t\t<filter-class>com.alibaba.citrus.webx.servlet.SetLoggingContextFilter</filter-class>\n\t</filter>\n\n\t<filter>\n\t\t<filter-name>webx</filter-name>\n\t\t<filter-class>com.alibaba.citrus.webx.servlet.WebxFrameworkFilter</filter-class>\n\t</filter>\n\n\t<filter-mapping>\n\t\t<filter-name>mdc</filter-name>\n\t\t<url-pattern>/*</url-pattern>\n\t</filter-mapping>\n\n\t<filter-mapping>\n\t\t<filter-name>webx</filter-name>\n\t\t<url-pattern>/*</url-pattern>\n\t</filter-mapping>\n\n\t<welcome-file-list>\n\t\t<welcome-file>index.html</welcome-file>\n\t\t<welcome-file>index.jsp</welcome-file>\n\t</welcome-file-list>\n\n\t<!-- 新增的dwr配置 -->  \n\t<!-- \n    <servlet>  \n        <servlet-name>dwr-invoker</servlet-name>  \n        <servlet-class>uk.ltd.getahead.dwr.DWRServlet</servlet-class>  \n        <init-param>  \n            <description></description>  \n            <param-name>debug</param-name>  \n            <param-value>true</param-value>  \n        </init-param>  \n        <init-param>  \n            <param-name>crossDomainSessionSecurity</param-name>  \n            <param-value>false</param-value>  \n        </init-param>\n        <init-param>  \n            <param-name>logLevel</param-name>  \n            <param-value>warn</param-value>  \n        </init-param>  \n    </servlet> -->\n    \n\t<servlet>\n\t\t<servlet-name>dwr-invoker</servlet-name>\n\t\t<servlet-class>org.directwebremoting.spring.DwrSpringServlet</servlet-class>\n\t\t<init-param>\n\t\t\t<param-name>debug</param-name>\n\t\t\t<param-value>true</param-value>\n\t\t</init-param>\n\t\t<!-- 新加corssDomainSessionSecurity参数 -->  \n\t\t<init-param>     \n\t\t\t<param-name>crossDomainSessionSecurity</param-name>     \n\t\t\t<param-value>false</param-value>     \n\t\t</init-param>  \n\t</servlet>  \n   \n    <servlet-mapping>  \n        <servlet-name>dwr-invoker</servlet-name>  \n        <url-pattern>/dwr/*</url-pattern>  \n    </servlet-mapping>\n    \n    \n</web-app>\n"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/WEB-INF/webx-home.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<beans:beans xmlns:beans=\"http://www.springframework.org/schema/beans\"\n\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:p=\"http://www.springframework.org/schema/p\"\n\txmlns:services=\"http://www.alibaba.com/schema/services\"\n\txmlns:ml-adapters=\"http://www.alibaba.com/schema/services/module-loader/adapters\"\n\txmlns:ml-factories=\"http://www.alibaba.com/schema/services/module-loader/factories\"\n\txsi:schemaLocation=\"\n                 http://www.alibaba.com/schema/services http://localhost:8080/schema/services.xsd\n                 http://www.alibaba.com/schema/services/module-loader/adapters http://localhost:8080/schema/services-module-loader-adapters.xsd\n                 http://www.alibaba.com/schema/services/module-loader/factories http://localhost:8080/schema/services-module-loader-factories.xsd\n                 http://www.springframework.org/schema/beans http://localhost:8080/schema/www.springframework.org/schema/beans/spring-beans.xsd\n             \">\n\n\t<!-- 支持${xxx}替换。 -->\n    <services:property-placeholder location=\"classpath:otter.properties\">\n        <property key=\"component\">home</property>\n    </services:property-placeholder>\n    \n\t<!-- 共享配置。 -->\n\t<beans:import resource=\"common/webx-component-and-root.xml\" />\n\t<beans:import resource=\"common/webx-component.xml\" />\n\n\t<!-- 执行管道。 -->\n\t<beans:import resource=\"common/pipeline.xml\" />\n\n\t<!-- 表单验证。 -->\n\t<beans:import resource=\"home/form.xml\" />\n\n\t<!-- 装载模块。 -->\n\t<services:module-loader>\n\t\t<ml-factories:class-modules>\n\t\t\t<search-packages type=\"$1\" packages=\"com.alibaba.otter.manager.web.home.module.*\" />\n\t\t</ml-factories:class-modules>\n\t</services:module-loader>\n</beans:beans>\n"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/WEB-INF/webx.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<beans:beans xmlns:beans=\"http://www.springframework.org/schema/beans\"\n\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:p=\"http://www.springframework.org/schema/p\"\n\txmlns:services=\"http://www.alibaba.com/schema/services\"\n\txmlns:ml-adapters=\"http://www.alibaba.com/schema/services/module-loader/adapters\"\n\txmlns:ml-factories=\"http://www.alibaba.com/schema/services/module-loader/factories\"\n\txmlns:pull-factories=\"http://www.alibaba.com/schema/services/pull/factories\"\n\txmlns:request-contexts=\"http://www.alibaba.com/schema/services/request-contexts\"\n\txmlns:basic-interceptors=\"http://www.alibaba.com/schema/services/request-contexts/basic/interceptors\"\n\txmlns:parser-filters=\"http://www.alibaba.com/schema/services/request-contexts/parser/filters\"\n\txmlns:session-encoders=\"http://www.alibaba.com/schema/services/request-contexts/session/encoders\"\n\txmlns:session-idgens=\"http://www.alibaba.com/schema/services/request-contexts/session/idgens\"\n\txmlns:session-interceptors=\"http://www.alibaba.com/schema/services/request-contexts/session/interceptors\"\n\txmlns:model-encoders=\"http://www.alibaba.com/schema/services/request-contexts/session/model-encoders\"\n\txmlns:session-stores=\"http://www.alibaba.com/schema/services/request-contexts/session/stores\"\n\txsi:schemaLocation=\"\n                 http://www.alibaba.com/schema/services http://localhost:8080/schema/services.xsd\n                 http://www.alibaba.com/schema/services/module-loader/adapters http://localhost:8080/schema/services-module-loader-adapters.xsd\n                 http://www.alibaba.com/schema/services/module-loader/factories http://localhost:8080/schema/services-module-loader-factories.xsd\n                 http://www.alibaba.com/schema/services/pull/factories http://localhost:8080/schema/services-pull-factories.xsd\n                 http://www.alibaba.com/schema/services/request-contexts http://localhost:8080/schema/services-request-contexts.xsd\n                 http://www.alibaba.com/schema/services/request-contexts/basic/interceptors http://localhost:8080/schema/services-request-contexts-basic-interceptors.xsd\n                 http://www.alibaba.com/schema/services/request-contexts/parser/filters http://localhost:8080/schema/services-request-contexts-parser-filters.xsd\n                 http://www.alibaba.com/schema/services/request-contexts/session/encoders http://localhost:8080/schema/services-request-contexts-session-encoders.xsd\n                 http://www.alibaba.com/schema/services/request-contexts/session/idgens http://localhost:8080/schema/services-request-contexts-session-idgens.xsd\n                 http://www.alibaba.com/schema/services/request-contexts/session/interceptors http://localhost:8080/schema/services-request-contexts-session-interceptors.xsd\n                 http://www.alibaba.com/schema/services/request-contexts/session/model-encoders http://localhost:8080/schema/services-request-contexts-session-model-encoders.xsd\n                 http://www.alibaba.com/schema/services/request-contexts/session/stores http://localhost:8080/schema/services-request-contexts-session-stores.xsd\n                 http://www.springframework.org/schema/beans http://localhost:8080/schema/www.springframework.org/schema/beans/spring-beans.xsd\n             \">\n             \n\t<beans:description>父容器所在的配置</beans:description>\n\n\t<!-- 支持${xxx}替换。 -->\n\t<services:property-placeholder location=\"classpath:otter.properties\">\n\t\t<property key=\"component\">common</property>\n\t</services:property-placeholder>\n\n\t<!-- 共享配置。 -->\n\t<beans:import resource=\"common/webx-component-and-root.xml\" />\n\n\t<!-- 异常管道。 -->\n\t<beans:import resource=\"common/pipeline-exception.xml\" />\n\n\t<!-- 资源装载。 -->\n\t<beans:import resource=\"common/resources.xml\" />\n\n\t<!-- URI生成。 -->\n\t<beans:import resource=\"common/uris.xml\" />\n\n\t<!-- 引入相关资源 -->\n\t<beans:import resource=\"applicationContext.xml\" />\n\n\t<!-- 综合设置。 -->\n    <services:webx-configuration>\n        <!-- 默认将productionMode设为true，建议在jetty插件中设置-DproductionMode=false。 -->\n        <productionMode>${otter.manager.productionMode:true}</productionMode>\n        <components defaultComponent=\"home\" />\n    </services:webx-configuration>\n\n\t<!-- 设置request/response/session。 -->\n\t <services:request-contexts>\n\t\t<request-contexts:basic />\n        <request-contexts:buffered />\n        <request-contexts:lazy-commit />\n        <request-contexts:parser>\n         \t<filters>\n                <parser-filters:uploaded-file-whitelist extensions=\"jpg, gif, png\" />\n            </filters>\n        </request-contexts:parser>\n\t\t<request-contexts:set-locale defaultLocale=\"zh_CN\" defaultCharset=\"UTF-8\" />\n\t\t<request-contexts:session forceExpirationPeriod=\"14400\">\n\t\t\t<stores>\n                <session-stores:cookie-store id=\"temporaryCookie\">\n                    <cookie name=\"OTTER_WEBX_JSESSIONID\" />\n                </session-stores:cookie-store>\n            </stores>\n            <store-mappings>\n                <match name=\"*\" store=\"temporaryCookie\" />\n            </store-mappings>\n            <interceptors>\n                <session-interceptors:lifecycle-logger />\n                <session-interceptors:attribute-whitelist>\n                    <attribute name=\"_csrf_token\" />\n                    <attribute name=\"_lang\" />\n                    <attribute name=\"managerUser\" />\n                </session-interceptors:attribute-whitelist>\n            </interceptors>\n\t\t</request-contexts:session>\n\t</services:request-contexts>\n\n\t<!-- 支持上传文件。 -->\n\t<services:upload sizeMax=\"5M\" />\n\n\t<!-- 将beans暴露给模板。这里定义的tools可被所有components之间共享。 -->\n\t<services:pull>\n\t\t<pull-factories:utils />\n\t\t<pull-factories:page-tool />\n\t\t<pull-factories:control-tool detailLevel=\"throwException\" />\n\t\t<pull-factories:uris-tool />\n\t\t<pull-factories:bean-tool scope=\"global\" id=\"numberFormat\"\n\t\t\tclass=\"com.alibaba.otter.manager.web.common.NumberFormatUtil\" />\n\t</services:pull>\n\n\t<!-- 装载模块。 -->\n\t <services:module-loader>\n        <ml-factories:class-modules>\n            <search-packages type=\"$1\" packages=\"com.alibaba.otter.manager.web.home.module.*\" />\n        </ml-factories:class-modules>\n    </services:module-loader>\n    \n\t<!-- 权限配置 -->\n\t<beans:bean id=\"urlAnalyze\"\n\t\tclass=\"com.alibaba.otter.manager.web.webx.valve.auth.RegExpURLAnalyze\">\n\t\t<beans:property name=\"anonymous\">\n\t\t\t<beans:bean\n\t\t\t\tclass=\"com.alibaba.otter.manager.web.webx.valve.auth.AuthorizeProtected\">\n\t\t\t\t<beans:property name=\"urlProtected\">\n\t\t\t\t\t<beans:value>\n\t\t\t\t\t\t/\n\t\t\t\t\t\t/dwr/.*\n\t\t\t\t\t\t/js/.*\n\t\t\t\t\t\t/css/.*\n\t\t\t\t\t\t/images/.*\n\t\t\t\t\t\t/monitor/.*\n\t\t\t\t\t\t/.*\\.css\n\t\t\t\t\t\t/.*\\.png\n\t\t\t\t\t\t/.*\\.js\n\t\t\t\t\t\t/.*login\\.htm\n\t\t\t\t\t\t/.*error\\.htm\n\t\t\t\t\t\t/.*Error\\.htm\n\t\t\t\t\t\t/.*\\_error\\.htm\n\t\t\t\t\t\t/.*forbidden\\.htm\n\t\t\t\t\t\t/.*check.*\\.htm\n\t\t\t\t\t\t/.*conflictStatList\\.htm\n\t\t\t\t\t\t/.*conflict_stat_list\\.htm\n\t\t\t\t\t\t/.*behaviorHistoryCurve\\.htm\n\t\t\t\t\t\t/.*behavior_history_curve\\.htm\n\t\t\t\t\t\t/.*List\\.htm\n\t\t\t\t\t\t/.*Info\\.htm\n\t\t\t\t\t\t/.*\\_list\\.htm\n\t\t\t\t\t\t/.*\\_info\\.htm\n\t\t\t\t\t\t/.*\\_detail\\.htm\n\t\t\t\t\t\t/.*\\_path\\.htm\n\t\t\t\t\t\t/.*search.*\\.htm\n\t\t\t\t\t\t/.*analysis.*\\.htm\n\t\t\t\t\t\t/.*record.*\\.htm\n\t\t\t\t\t\t/.*Record.*\\.htm\n\t\t\t\t\t\t/.*alarmRuleList.*\\.htm\n\t\t\t\t\t\t/.*alarm_rule_list.*\\.htm\n\t\t\t\t\t\t/.*init_sql.*\\.htm\n\t\t\t\t\t\t/.*wiki_guide.*\\.htm\n\t\t\t\t\t</beans:value>\n\t\t\t\t</beans:property>\n\t\t\t\t<beans:property name=\"actionProtected\">\n\t\t\t\t\t<beans:value>\n\t\t\t\t\t\tuserAction|login\n\t\t\t\t\t\tuserAction|logout\n\t\t\t\t\t\t.*Action|search\n\t\t\t\t\t</beans:value>\n\t\t\t\t</beans:property>\n\t\t\t</beans:bean>\n\t\t</beans:property>\n\t\t<beans:property name=\"operator\">\n\t\t\t<beans:bean\n\t\t\t\tclass=\"com.alibaba.otter.manager.web.webx.valve.auth.AuthorizeProtected\">\n\t\t\t\t<beans:property name=\"urlProtected\">\n\t\t\t\t\t<beans:value>\n\n\t\t\t\t\t</beans:value>\n\t\t\t\t</beans:property>\n\t\t\t\t<beans:property name=\"actionProtected\">\n\t\t\t\t\t<beans:value>\n\n\t\t\t\t\t</beans:value>\n\t\t\t\t</beans:property>\n\t\t\t</beans:bean>\n\t\t</beans:property>\n\t\t<beans:property name=\"admin\">\n\t\t\t<beans:bean\n\t\t\t\tclass=\"com.alibaba.otter.manager.web.webx.valve.auth.AuthorizeProtected\">\n\t\t\t\t<beans:property name=\"urlProtected\">\n\t\t\t\t\t<beans:value>\n\t\t\t\t\t\t/.*add.*\\.htm\n\t\t\t\t\t\t/.*edit.*\\.htm\n\t\t\t\t\t\t/.*select.*\\.htm\n\t\t\t\t\t\t/.*userManager\\.htm\n\t\t\t\t\t\t/.*user_manager\\.htm\n\t\t\t\t\t\t/.*system_reduction\\.htm\n\t\t\t\t\t\t/.*system_parameter\\.htm\n\t\t\t\t\t\t/.*systemParameter\\.htm\n\t\t\t\t\t</beans:value>\n\t\t\t\t</beans:property>\n\t\t\t\t<beans:property name=\"actionProtected\">\n\t\t\t\t\t<beans:value>\n\t\t\t\t\t\t.*Action|add\n\t\t\t\t\t\t.*Action|edit\n\t\t\t\t\t\t.*Action|delete\n\t\t\t\t\t\t.*Action|status\n\t\t\t\t\t\t.*Action|restart\n\t\t\t\t\t\t.*Action|all_status\n\t\t\t\t\t\tDataMatrixAction|switch\n\t\t\t\t\t\tSwitchWarmupAction|switch\n\t\t\t\t\t\tSwitchWarmupAction|restart\n\t\t\t\t\t</beans:value>\n\t\t\t\t</beans:property>\n\t\t\t</beans:bean>\n\t\t</beans:property>\n\t</beans:bean>\n\t<beans:bean id=\"apiAuthService\" class=\"com.alibaba.otter.manager.web.common.api.DefaultApiAuthService\" />\n\t\n\t<beans:bean class=\"org.springframework.beans.factory.config.CustomEditorConfigurer\" depends-on=\"arbitrateConfigImpl\">  \n\t   <beans:property name=\"customEditors\">  \n\t\t    <beans:map>  \n\t\t       <beans:entry key=\"com.alibaba.otter.manager.web.webx.valve.auth.url.URLProtected\" value=\"com.alibaba.otter.manager.web.webx.valve.auth.url.URLProtectedEditor\" /> \n\t\t       <beans:entry key=\"com.alibaba.otter.manager.web.webx.valve.auth.action.ActionProtected\" value=\"com.alibaba.otter.manager.web.webx.valve.auth.action.ActionProtectedEditor\" /> \n\t\t    </beans:map>\n\t   </beans:property>\n\t</beans:bean>\n</beans:beans>\n"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/css/JSONFormatter.css",
    "content": "table#conflict-table ul li {\n  margin: 0px 0 0; \n  padding: 0; \n  line-height: 20px;\n}\ntable#conflict-table ul li b {\n    color: black;\n    font-style: normal; }\n table#conflict-table ul {\n    margin: 8px 0 8px 0;\n    padding: 5px 0; }\n table#conflict-table li {\n    padding: 0 0 0px;\n    list-style-type: none;\n    margin: 0;\n    color: #666666; }"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/css/commons.css",
    "content": "@charset \"utf-8\";\r\n/* CSS Document */\r\nhtml, body, div, p, ul, li, dl, dt, dd, h1, h2, h3, h4, h5, h6, form, input, select, button, textarea, iframe, table, th, td{ \r\n    margin:0; \r\n\tpadding:0; \r\n}\r\nselect,textarea,input{\r\n\tborder:#b3b3b3 1px solid;\r\n}\r\ninput.radio,input.checkbox{\r\n\tborder:none;\r\n}\r\nimg{ \r\n    border:none;\r\n}\t\r\nul, li{ \r\n    list-style-type:none; \r\n}\r\nbody{\r\n\tmargin:0;\r\n\tfont-family:Arial, Helvetica, sans-serif;\r\n\tfont-size:12px;\r\n\tcolor:#586573;\r\n\tmin-width:1150px;\r\n\tword-wrap:break-word;\r\n    word-break:break-all;\r\n}\r\nbody.pop{\r\n\tbackground-color:transparent;\r\n}\r\nbody.pop_del{\r\n\tmin-width:750px;\r\n}\r\na{\r\n\tcolor:#2f7ed3;\r\n    cursor:pointer;\r\n}\r\na.fontlink{\r\n\tcolor:#2f7ed3;\r\n\ttext-decoration:underline;\r\n}\r\n.none{\r\n\tdisplay:none;\r\n}\r\n.left{\r\n\tfloat:left;\r\n}\r\n.right{\r\n\tfloat:right;\r\n}\r\n\r\n/*head*/\r\n.head_right{\r\n\tfloat:right;\r\n\tpadding-top:45px;\r\n\tmargin:0 10px 0 0;\r\n\tcolor:#FFF;\r\n}\r\n.head_right span{\r\n\tcolor:#fff;\r\n\tline-height:30px;\r\n\tfloat:right;\r\n\tmargin-right:15px;\r\n}\r\n.head_right span a{\r\n\tcolor:#fff;\r\n}\r\n.exit{\r\n\tfloat:right;\r\n\tpadding-top:3px;\r\n}\r\n\r\n/*menu*/\r\n.nav ul li{\r\n\tfloat:left;\r\n\theight:31px;\r\n\tline-height:24px;\r\n}\r\n.nav ul li a{\r\n\tpadding-top:5px;\r\n\theight:26px;\r\n\tcolor:#FFF;\r\n\tdisplay:block;\r\n\twidth:157px;\r\n    text-decoration:none;  \r\n    text-align:center;\r\n}\t\r\n.nav ul li.current a{\r\n\tcolor:#000;\r\n\tdisplay:block;\r\n\twidth:157px;\r\n    text-decoration:none;  \r\n    text-align:center;\r\n}\r\n.nav ul li.current ul li a{ \r\n\tcolor:#fff;\r\n\tdisplay:block;\r\n\twidth:157px;\r\n    text-decoration:none;  \r\n    text-align:center;\r\n}\r\n.nav li:hover ul{\r\n\tdisplay:block;\r\n}\t\r\n.nav ul li ul{\r\n\tposition:absolute;\r\n\tdisplay:none;\r\n\tlist-style-type: none;\r\n\ttext-align:left;\r\n}\t\r\n.nav ul li ul li{\r\n\tdisplay:block;\r\n    text-decoration:none; \r\n\theight:28;\r\n\tclear: both; \r\n}\r\n.nav ul li ul li a{\r\n\tcolor:#fff;\r\n\tdisplay:block;\r\n    text-decoration:none; \r\n}\t\r\n.nav ul li ul li a:hover{ \r\n\tbackground-image:none;\r\n}\r\n.nav ul li.sub_nav a{\r\n\tcolor:#fff;\r\n\tdisplay:block;\r\n\twidth:157px;\r\n    text-decoration:none;  \r\n    text-align:center;\r\n\tline-height:26px;\r\n}\r\n.nav ul li.sub_nav ul li a{ \r\n\tcolor:#fff;\r\n\tdisplay:block;\r\n\twidth:157px;\r\n    text-decoration:none;  \r\n    text-align:center;\r\n}\r\n.nav ul li.current_nosub a{\r\n\tcolor:#000;\r\n\tdisplay:block;\r\n\twidth:157px;\r\n    text-decoration:none;  \r\n    text-align:center;\r\n\tline-height:26px;\r\n}\r\n.about{\r\n\tclear:right;\r\n    float:right;\r\n    line-height:28px;\r\n    margin-right:20px;\r\n}\r\n.about a{\r\n   color:#fff;\r\n}\r\n\r\n/*全局搜索*/\r\n.search_o{\r\n\tclear:left;\r\n\twidth:500px;\r\n\tmargin-bottom:10px;\r\n}\r\n.search_btn{\r\n\tmargin-top:5px;\r\n\t*margin:0;\r\n}\r\n\r\n.search_btn2{ \r\n    display:block; \r\n    width:40px; \r\n    height:32px; \r\n    margin:0 auto; \r\n   background: url(../images/search_go.png) no-repeat; \r\n} \r\n\r\n.search_btn2:hover{ \r\n    display:block; \r\n    background:url(../images/search_go.png) no-repeat 0 -32px;\r\n} \r\n\r\n.btn_search{\r\n\tbackground:url(../images/btn_service.png);\r\n\twidth:132px;\r\n\theight:20px;\r\n\ttext-align:center;\r\n\tline-height:20px;\r\n\tmargin-top:10px;\r\n}\r\n.btn_search a,.btn_info a{\r\n\ttext-decoration:none;\r\n\tcolor:#666;\r\n\tdisplay:block;\r\n}\r\n.search{\r\n\twidth:500px;\r\n\tmargin:150px auto;\r\n}\r\n.search_categories{\r\n\theight:18px;\r\n}\r\n.search_categories ul li{\r\n\tfloat:left;\r\n\twidth:60px;\r\n\tline-height:10px;\r\n\t*line-height:15px;\r\n\ttext-align:center;\r\n}\r\n.search_categories ul li a{\r\n\tcolor:#586573;\r\n\ttext-decoration:none;\r\n}\r\n.search_categories ul li span.search_line{\r\n\tmargin-left:10px;\r\n\tcolor:#CCC;\r\n}\r\n.search_categories ul li span.search_line_end{\r\n\tmargin-left:10px;\r\n\tcolor:#fff;\r\n}\r\n\r\n\r\n/*main*/\r\n.main{ \r\n    background: url(../images/main_bg.png) repeat-x;\r\n\tmin-height:229px;\r\n\tpadding:15px 20px 20px 20px;\r\n\t*padding-bottom:0;\r\n\tmin-height:593px;\r\n\theight:100%;\r\n}\r\n.title{\r\n\tfont-family:\"微软雅黑\";\r\n\tfont-weight:200;\r\n    color:#696969;\r\n\tfont-size:16px;\r\n\tfloat:left;\r\n\tmargin-bottom:10px;\r\n}\r\n.title_num{\r\n\tfont-size:14px;\r\n\tfloat:left;\r\n\tpadding:13px 0 0 10px;\r\n\tpadding-top:9px\\0;\r\n\t*padding-top:9px;\r\n}\r\n.crumbs{ \r\n    float:left;\r\n\tpadding-top:15px;\r\n\tpadding-left:20px;\r\n    color:#999; \r\n}\r\n.crumbs a{\r\n\tcolor:#999;\r\n\ttext-decoration:none;\r\n}\r\n.crumbs a:hover{\r\n\tcolor:#F60;\r\n}\r\n.num_o{\r\n\tclear:left;\r\n\tpadding-top:10px;\r\n\t*padding-top:0;\r\n}\r\n.num_t{\r\n\tclear:left;\r\n\tpadding-top:10px;\r\n}\r\n\r\n/*table_search*/\r\n.search_list{\r\n\toverflow:hidden;\r\n\tmargin-bottom:10px;\r\n}\r\n.search_list table th{\r\n\tfont-weight:normal;\r\n\tpadding-right:10px;\r\n}\r\n.search_list table td{\r\n\tfont-weight:normal;\r\n\tpadding-right:15px;\r\n}\r\n\t\r\n/*table_ico_btn*/\r\n.ico_btn{\r\n\tclear:both;\r\n}\r\n.ico_btn a,table.list td a{\r\n\ttext-decoration:none;\r\n}\r\n.ico_btn span.ico_font,table.list td span.ico_font{\r\n\tmargin-left:5px;\r\n}\r\n.ico_btn span.ico_line,table.list td span.ico_line{\r\n\tmargin:0 10px 0 10px;\r\n\tcolor:#CCC;\r\n}\r\n\r\n.ico_btn span.ico_font,table.list2 td span.ico_font{\r\n\tmargin-left:5px;\r\n}\r\n.ico_btn span.ico_line,table.list2 td span.ico_line{\r\n\tmargin:0 10px 0 10px;\r\n\tcolor:#CCC;\r\n}\r\n\r\n/*table*/\r\ntable.list{ \r\n    color:#586573;\r\n\tclear:left;\r\n\twidth:100%;\r\n\tborder:#dedede 1px solid;\r\n\tmargin-bottom:10px;\r\n}\r\ntable.list th{\r\n\theight:29px;\r\n\tcolor:#fff;\r\n\tline-height:29px;\r\n\ttext-align:left;\r\n\tpadding-left:8px; \r\n\tfont-weight:bold;\r\n}\r\ntable.list td{\t\r\n\ttext-align:left;\r\n\tpadding-left:10px;\r\n\tline-height:34px;\r\n\tborder-top:#d4d4d4 1px dashed;\r\n}\r\n\r\ntable.list2{ \r\n    color:#586573;\r\n\tclear:left;\r\n\twidth:100%;\r\n\tborder:#dedede 1px solid;\r\n\tmargin-bottom:10px;\r\n}\r\ntable.list2 th{\r\n\theight:29px;\r\n\tcolor:#000;\r\n\tline-height:29px;\r\n\ttext-align:left;\r\n\tpadding-left:8px; \r\n\tfont-weight:bold;\r\n}\r\ntable.list2 td{\t\r\n\ttext-align:left;\r\n\tpadding-left:10px;\r\n\tline-height:34px;\r\n\tborder-top:#d4d4d4 1px dashed;\r\n}\r\n\r\n/*footer*/\r\n.footer span{\r\n\tcolor:#fff;\r\n\tline-height:26px;\r\n\tmargin-left:20px;\r\n}\r\n\r\n/*button*/\r\n.btn a,.login_btn a{ \r\n\ttext-decoration:none; \r\n\tcolor:#586573;\r\n\tdisplay:block;\r\n}\r\nspan.star{\r\n\tcolor:#F00;\r\n}\r\n\r\n/*pop*/\r\ntable.pop_top td.pop_lefttop_small h1{\r\n\tfont-size:24px;\r\n\tcolor:#000;\r\n\tpadding-left:90px;\r\n\tpadding-top:20px;\r\n}\r\ntable.pop_top td.pop_midtop_small{\r\n\tbackground:url(../images/pop_midtop_small.png);\r\n\theight:105px;\r\n}\r\ntable.pop_top td.pop_righttop_small{\r\n\tbackground:url(../images/pop_righttop_small.png);\r\n\twidth:46px;\r\n\theight:105px;\r\n}\r\ntable.pop_mid td.pop_left{\r\n\tbackground:url(../images/pop_left.png);\r\n\twidth:19px;\r\n}\r\ntable.pop_mid td.pop_content{\r\n\tbackground:#f7f7f7;\r\n}\r\ntable.pop_mid td.pop_right{\r\n\tbackground:url(../images/pop_right.png);\r\n\twidth:19px;\r\n}\r\ntable.pop_mid td.pop_leftbottom_small{\r\n\tbackground:url(../images/pop_leftbottom.png);\r\n\twidth:19px;\r\n\theight:22px;\r\n}\r\ntable.pop_mid td.pop_midbottom_small{\r\n    background:url(../images/pop_midbottom.png);\r\n\theight:22px;\r\n}\r\ntable.pop_mid td.pop_rightbottom_small{\r\n\tbackground:url(../images/pop_rightbottom.png);\r\n\twidth:19px;\r\n\theight:22px;\r\n}\r\ntable.pop_box{\r\n\tborder-top:#cdcdcd 1px dashed;\r\n\tborder-bottom:#cdcdcd 1px dashed;\r\n\tmargin:0px auto 20px auto;\r\n\tpadding:0;\r\n}\r\ntable.pop_box th{\r\n\ttext-align:right;\r\n\theight:30px;\r\n}\r\ntable.pop_box th.space{\r\n\theight:15px;\r\n}\r\n.pop_close{\r\n\tbackground:url(../images/pop_close.png);\r\n\twidth:20px;\r\n\theight:20px;\r\n\tmargin:10px 0 0 13px;\r\n\tcursor:pointer;\r\n}\r\n.tip_close{\r\n\tmargin:13px 14px 0 0;\r\n\tfloat:right;\r\n}\r\n.pop_tipmessage table,.pop_tipmessage_del table,.pop_tipmessage_succeed table,.pop_tipmessage_choose table{\r\n\tpadding:100px 60px 0 0;\r\n\tfloat:right;\r\n\t*margin:100px 60px 0 0;\r\n}\r\n.pop_tipmessage table th,.pop_tipmessage_del table th,.pop_tipmessage_succeed table th,.pop_tipmessage_choose table th{\r\n\tfont-size:30px;\r\n\tfont-weight:500;\r\n\tcolor:#333;\r\n\ttext-align:right;\r\n}\r\n.pop_tipmessage table td,.pop_tipmessage_del table td,.pop_tipmessage_succeed table td,.pop_tipmessage_choose table td{\r\n\tline-height:60px;\r\n\ttext-align:right;\r\n}\r\n.pop_tipmessage table td.pop_tip_btn,.pop_tipmessage_del table td.pop_tip_btn,.pop_tipmessage_succeed table td.pop_tip_btn,.pop_tipmessage_choose table td.pop_tip_btn{\r\n\tpadding-top:30px;\r\n}\r\n\r\n\r\n/*翻页*/\r\nspan.go a{\r\n\tfont-weight:bold;\r\n\tmargin-left:5px;\r\n\ttext-decoration:none;\r\n\tfont-size:14px;\r\n}\r\n.prev,.num{\r\n\tpadding:0 5px 0 5px;\r\n}\r\n\r\n/*login*/\r\n.user{\r\n\tcolor:#fff;\r\n\tfloat:right;\r\n\twidth:400px;\r\n\tmargin-top:80px; \r\n\tmargin-right:50px;\r\n}\r\n.user table td{\r\n\tline-height:42px;\r\n\tpadding-left:10px;\r\n}\t\r\ninput.login_input{\r\n    border:#989898 1px solid;\r\n\tpadding: 2px 0 0 4px;\r\n\twidth:220px;\r\n\theight:17px;\r\n}\r\n\r\n/*横向tab*/\r\n.contentbox_tab{\r\n\tclear:both;\r\n\tbackground:#fff;\r\n\tpadding:15px 15px 8px 15px;\r\n\toverflow:hidden;\r\n\tmargin-right:-10px;\r\n\tmargin-bottom:8px;\r\n}\r\n.menubox ul li{\r\n\tfloat:left;\r\n\tdisplay:block;\r\n\tcursor:pointer;\r\n\ttext-align:center;\r\n\tline-height:28px;\r\n\tline-height:28px\\0;\r\n\tpadding:0 10px 0 10px;\r\n}\r\n.menubox ul li a{\r\n\ttext-decoration:none;\r\n\tcolor:#fff;\r\n\tpadding:0 10px 0 10px;\r\n}\r\n.menubox ul li.tab_active{\r\n\tbackground:url(../images/tab_active.png);\r\n\tpadding:0 10px 0 10px;\r\n\theight:29px;\r\n}\r\n.menubox ul li.tab_active a{\r\n\tcolor:#666;\r\n}\r\n.tab{\t\r\n\tclear:left;\r\n\tclear:left\\0;\r\n\t*margin-top:-10px;\r\n}\r\n.box_tab{\r\n\tbackground:#f5f5f5;\r\n\tborder:#ccc 1px solid;\r\n}\r\n\r\n/*纵向tab*/\r\n.favorite{\r\n\twidth:291px;\r\n\tclear:left;\r\n\tfloat:left;\r\n\t*margin-top:10px;\r\n}\r\n.fav_tab{\r\n\tfloat:right;\r\n}\r\n.fav_tab ul li{\r\n\tbackground:url(../images/fav_tab_normal.png);\r\n\twidth:20px;\r\n\theight:70px;\r\n}\r\n.fav_tab ul li.active{\r\n\tbackground:url(../images/fav_tab_active.png);\r\n\twidth:20px;\r\n\theight:70px;\r\n}\r\n.fav_tab ul li a,.fav_tab ul li.active a{\r\n\ttext-decoration:none;\r\n\tpadding:6px 0 0 4px;\r\n\tdisplay:block;\r\n\tline-height:15px;\r\n}\r\n.fav_box{\r\n\tfloat:left;\r\n\twidth:269px;\r\n\tmin-height:150px;\r\n\tbackground:#f5f5f5;\r\n\tborder:#ccc 1px solid;\r\n}\r\n.fav_box_title{\r\n\tbackground:url(../images/fav_title.png);\r\n\theight:30px;\r\n\tline-height:30px;\r\n\tpadding-left:15px;\r\n}\r\n.fav_box_title span{\r\n\tpadding-left:5px;\r\n}\r\n.fav_box_title img{\r\n\tmargin-left:20px;\r\n}\r\n.fav_title_left{\r\n\tfloat:left;\r\n}\r\n.fav_content{\r\n\tmargin:10px 0 10px 15px;\r\n}\r\n.fav_content table td{\r\n\tline-height:23px;\r\n}\r\n.fav_tips{\r\n\twidth:210px; \r\n\toverflow:hidden; \r\n\ttext-overflow:ellipsis; \r\n\twhite-space:nowrap;\r\n}\r\n.fav_tips a{\r\n\ttext-decoration:none;\r\n}\r\n\r\n/*setting*/\r\n.setting_box{\r\n\tbackground:#f0f2f4;\r\n\tborder:#ccc 1px solid;\r\n\tclear:both;\r\n\tpadding:20px 20px 25px 20px;\r\n\t*padding-top:10px;\r\n\tmargin-bottom:10px;\r\n}\r\n.setting_box table.setting th{\r\n\tfont-weight:normal;\r\n\ttext-align:right;\r\n\tline-height:32px;\r\n\twidth:150px;\r\n}\r\n.setting_box table.setting td{\r\n\tpadding-left:15px;\r\n}\r\nspan.red{\r\n\tcolor:#F00;\r\n\tmargin-left:10px;\r\n}\r\ninput.setting_input{\r\n\twidth:200px;\r\n\theight:18px;\r\n}\r\nselect.setting_select{\r\n\twidth:202px;\r\n\theight:18px;\r\n}\r\n.setting_box_page{\r\n\tpadding-bottom:30px;\r\n}\r\n\r\n/*pop_tips*/\r\n.pop_tips{\r\n\twidth:300px; \r\n\toverflow:hidden; \r\n\ttext-overflow:ellipsis; \r\n\twhite-space:nowrap;\r\n}\r\n\r\n/*表格隔行变色*/\r\ntr.odd{ \r\n    background:#fff;\r\n}\r\ntr.wh{ \r\n    background:#f5f5f5;\r\n}\r\ntr.highlight{ \r\n    background:#fff9b7;\r\n}\r\n\r\nDIV.neat-dialog-cont {\r\n Z-INDEX: 98; BACKGROUND: none transparent scroll repeat 0% 0%; LEFT: 0px; WIDTH: 100%; POSITION: absolute; TOP: 0px; HEIGHT: 100%\r\n}\r\nDIV.neat-dialog-bg {\r\n Z-INDEX: -1; FILTER: alpha(opacity=70); LEFT: 0px; WIDTH: 100%; POSITION: absolute; TOP: 0px; HEIGHT: 100%; BACKGROUND-COLOR: #eee; opacity: 0.7\r\n}\r\nDIV.neat-dialog {\r\n BORDER-RIGHT: #555 1px solid; BORDER-TOP: #555 1px solid; Z-INDEX: 99; MARGIN-LEFT: auto; BORDER-LEFT: #555 1px solid; WIDTH: 65%; MARGIN-RIGHT: auto; BORDER-BOTTOM: #555 1px solid; POSITION: relative; TOP: 25%; BACKGROUND-COLOR: #fff ; font-size:14px;\r\n}\r\nDIV.neat-dialog-title {\r\n PADDING-RIGHT: 0.3em; PADDING-LEFT: 0.3em; FONT-SIZE: 1.2em; \r\n PADDING-BOTTOM: 0.1em; MARGIN: 0px; LINE-HEIGHT: 1.2em; PADDING-TOP: 0.1em; BORDER-BOTTOM: #444 1px solid; \r\n POSITION: relative;\r\n background-color:#EEEEEE;\r\n}\r\nIMG.nd-cancel {\r\n RIGHT: 0.2em; POSITION: absolute; TOP: 0.2em\r\n}\r\nDIV.neat-dialog P {\r\n PADDING-RIGHT: 0.2em; PADDING-LEFT: 0.2em; PADDING-BOTTOM: 0.2em; PADDING-TOP: 0.2em; TEXT-ALIGN: center\r\n}"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/css/otter.css",
    "content": ".setting_box table.setting_otter th{\r\n\tfont-weight:normal;\r\n\ttext-align:right;\r\n\tline-height:32px;\r\n\twidth:200px;\r\n}\r\n.setting_box table.setting_otter td{\r\n\tpadding-left:10px;\r\n}\r\ntd.textarea{\r\n\tpadding:5px 0 12px 0;\r\n}\r\ntd.textarea_b{\r\n\tpadding:0 0 6px 0;\r\n}\r\ntable.b input{\r\n\twidth:280px;\r\n}\r\n.box_tab{\r\n        clear:both;\r\n        padding:15px 15px 8px 15px;\r\n        margin-right:-10px;\r\n        margin-bottom:8px;\r\n}\r\n.header{\r\n    background:url(../images/logo.png);\r\n        width:723px;\r\n        height:78px;\r\n        float:left;\r\n        }\r\n.logo{\r\n    background:url(../images/login_logo.png);\r\n        width:187px;\r\n        height:152px;\r\n        float:left;\r\n        margin:40px 0 0 120px;\r\n        }\r\n\r\n\r\n.sel_l{\r\n   clear:left;\r\n   float:left;\r\n   margin-right:50px;\r\n}\r\n.sel_r{\r\n   float:left;\r\n}\r\n.sel_l select,.sel_r select{\r\n   height:182px;\r\n   width:100%;\r\n}\r\n.box1,.box3,.box2{\r\n    width:400px;\r\n\tmargin-bottom:10px;\r\n}\r\n.box2{\r\n   height:20px;\r\n }\r\n.box3{\r\n    clear:left;\r\n}\r\ninput.btn_down1{\r\n    background:url(../images/down1.png);\r\n\twidth:33px;\r\n\theight:20px;\r\n\tborder:none;\r\n}\r\ninput.btn_down2{\r\n    background:url(../images/down2.png);\r\n\twidth:33px;\r\n\theight:20px;\r\n\tborder:none;\r\n}\r\ninput.btn_up1{\r\n    background:url(../images/up1.png);\r\n\twidth:33px;\r\n\theight:20px;\r\n\tborder:none;\r\n}\r\ninput.btn_up2{\r\n    background:url(../images/up2.png);\r\n\twidth:33px;\r\n\theight:20px;\r\n\tborder:none;\r\n}\r\n"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/css/skin.css",
    "content": "@charset \"utf-8\";\r\n/* CSS Document */\r\n/*head*/\r\n.head{\r\n\tmin-width:1150px;\r\n    height:78px; \r\n\tbackground-image:url(../images/head_bg.png);\r\n}\r\n\r\n/*menu*/\r\n.nav,.nav_harbor{ \r\n\tcolor:#FFF;\r\n    background-image:url(../images/nav_bg.png); \r\n\tmin-width:1150px;\r\n\theight:31px;\r\n\tclear:right;\r\n}\r\n.nav ul li a:hover,.nav_harbor ul li a:hover,.nav_harbor ul li:hover a{\r\n\tbackground-image:url(../images/nav_pass.png);\r\n}\r\n.nav ul li ul,.nav ul li ul li{ \r\n    background-color:#3d8efa;\r\n}\r\n.nav ul li ul li a:hover{ \r\n    background-color:#64a6fd;\r\n}\r\n.nav ul li.sub_nav{\r\n\tbackground: url(../images/nav_btn_bg22.png) no-repeat; \r\n}\r\n.nav ul li.sub_nav:hover{\r\n    color:#fff;\r\n    background:url(../images/nav_btn_bg22.png) no-repeat 0 -31px;\r\n}\r\n.nav ul li.sub_nav a:hover{\r\n\tcolor:#fff;\r\n    background:url(../images/nav_btn_bg22.png) no-repeat 0 -31px;\r\n}\r\n.nav ul li.sub_nav ul li a:hover{\r\n    background-color:#64a6fd;\r\n\tbackground-image:none;\r\n}\r\n.nav ul li.current_nosub{\r\n\tbackground: url(../images/nav_btn_bg44.png) no-repeat; \r\n}\r\n.nav ul li.current_nosub :hover{\r\n    background:url(../images/nav_btn_bg44.png) no-repeat;\r\n}\r\n.nav ul li.current:hover > a{\r\n    background:url(../images/nav_btn_bg22.png) no-repeat 0 -31px;\r\n\tcolor:#fff;\r\n}\r\n.nav ul li.current,.nav_harbor ul li.current{\r\n\tbackground-image:url(../images/nav_btn_bg3.png); \r\n\tbackground-position:right;\r\n}\r\n.nav ul li.current a:hover{\r\n\tbackground:url(../images/nav_btn_bg22.png) no-repeat 0 -31px;\r\n}\r\n.nav ul li.current ul li a:hover{\r\n    background-color:#64a6fd;\r\n\tbackground-image:none;\r\n}\r\n\r\n.nav_harbor li .sub{\r\n\tbackground-color:#3d8efa;\r\n}\r\n.nav_harbor .sub ul li a:hover {\r\n\tbackground-color:#64a5fc;\r\n}\r\nli.sub_font{\r\n\tcolor:#fff82d;\r\n\tfont-weight:bold;\r\n}\r\n\r\n/*全局搜索*/\r\n.search_input{\r\n\tbackground:url(../images/search_input.png);\r\n\twidth:438px;\r\n\theight:31px;\r\n\tfloat:left;\r\n}\r\n.search_input input{\r\n\tborder:none;\r\n\tbackground:url(../images/input.png);\r\n\twidth:350px;\r\n\theight:21px;\r\n\tmargin:5px 0 0 80px;\r\n\t*margin:4px 0 0 80px;\r\n\tcolor:#7d8a98;\r\n\tline-height:20px;\r\n}\r\n.active{\r\n\tbackground:url(../images/search_active.png);\r\n\twidth:52px;\r\n\theight:20px;\r\n\tdisplay:block;\r\n}\r\n\r\n/*table*/\r\ntable.list th{\r\n    background:url(../images/table_title_bg.png) repeat-x;\r\n}\r\ntable.list th.head_title{\r\n\tbackground:url(../images/table_title_bg.png);\r\n\twidth:10px;\r\n\theight:29px;\r\n}\r\n\r\ntable.list2 th{\r\n    background:url(../images/nav_selected.png);\r\n}\r\ntable.list2 th.head_title{\r\n\tbackground-color:#f5f5f5;\r\n\twidth:10px;\r\n\theight:29px;\r\n}\r\n\r\n/*pop*/\r\ntable.pop_top td.pop_lefttop_small{\r\n\tbackground:url(../images/pop_lefttop_small.png);\r\n\twidth:300px;\r\n\theight:105px;\r\n}\r\n.pop_tipmessage{\r\n\tbackground:url(../images/tip_warning.png);\r\n}\r\n.pop_tipmessage_choose{\r\n\tbackground:url(../images/tip_choose.png);\r\n}\r\n.pop_tipmessage_del{\r\n\tbackground:url(../images/tip_del.png);\r\n}\r\n.pop_tipmessage_succeed{\r\n\tbackground:url(../images/tip_succeed.png);\r\n}\r\n.pop_tipmessage,.pop_tipmessage_choose,.pop_tipmessage_del,.pop_tipmessage_succeed{\r\n\twidth:720px;\r\n\theight:300px;\r\n}\r\n\r\n/*button*/\r\n.btn{\r\n\twidth:65px;\r\n\theight:20px;\r\n    background:url(../images/button_normal.png);\r\n\ttext-align:center;\r\n\tfloat:left;\r\n\tmargin-right:5px;\r\n\tline-height:20px;\r\n\tdisplay:block;\r\n}\r\n.btn a:hover{ \r\n\tcolor: #C60;\r\n\twidth:65px;\r\n\theight:20px;\r\n    background:url(../images/button_hover.png);\r\n\ttext-align:center;\r\n\tfloat:left;\r\n\tmargin-right:5px;\r\n\tdisplay:block;\r\n}\r\n.login_btn{\r\n\twidth:65px;\r\n\theight:20px;\r\n    background:url(../images/login_btn_normal.png);\r\n\ttext-align:center;\r\n\tmargin-right:5px;\r\n\tline-height:20px;\r\n\tdisplay:block;\r\n}\r\n.login_btn a:hover{ \r\n\tcolor: #C60;\r\n\twidth:65px;\r\n\theight:20px;\r\n    background:url(../images/login_btn_hover.png);\r\n\ttext-align:center;\r\n\tfloat:left;\r\n\tmargin-right:5px;\r\n\tdisplay:block;\r\n}\r\n\t\r\n\r\n/*footer*/\r\n.footer{\r\n\tmargin-top:10px;\r\n\t*margin:0;\r\n\tmin-width:1150px;\r\n\tbackground:#808080;\r\n\theight:28px;\r\n}\r\n\r\n/*翻页*/\r\n.page{ \r\n    color:#2a7ec5;\r\n\tfloat:right;\r\n\tpadding-bottom:5px;\r\n}\r\n.page a{\r\n\tcolor:#2a7ec5;\r\n}\r\na.current_num{\r\n\tcolor:#f09a3e;\r\n}\r\n\r\n/*login*/\r\n.login{\r\n\tbackground-image:url(../images/login_bg.png);\r\n\tbackground-repeat:no-repeat;\r\n\tbackground-color:#1a4f89;\r\n}\r\n.login_container{\r\n\twidth:100%;\r\n\tmargin-top:200px;\r\n\tbackground-image:url(../images/login_shadow.png);\r\n\theight:231px;\r\n}\r\n.login_box{\r\n\tmargin:0 auto;\r\n    background-image: url(../images/login_box.png); \r\n    width:804px; \r\n\theight:231px;\r\n}\r\n\r\n/*tab*/\r\n.menubox{\r\n\tbackground-image: url(../images/tab_bg2.png);\r\n\theight:29px;\r\n\twidth:100%;\r\n\tpadding-left:10px;\r\n}\r\n\r\n/*可伸缩区块*/\r\na.title_o{\r\n\tfont-size:14px;\r\n\tfont-weight:bold;\r\n\ttext-decoration:none;\r\n\tcolor:#2f7ed3;\r\n}\r\n\r\n/*error*/\r\n.error{\r\n\tbackground:url(../images/404.png);\r\n\twidth:586px;\r\n\theight:392px;\r\n\tmargin:50px auto;\r\n}\r\n\r\n.error_forbidden{\r\n\tbackground:url(../images/forbidden.png);\r\n\twidth:626px;\r\n\theight:302px;\r\n\tmargin:50px auto;\r\n}\r\n\r\n/*pop_tips*/\r\n.bubbleInfo{    \r\n    position: relative;\r\n}\r\n.popup {\r\n\tposition: absolute; \r\n\tdisplay:none;\r\n}\r\n.pop_tips_top{\r\n\tbackground:url(../images/pop_tips_top.png);\r\n\twidth:500px;\r\n\theight:15px;\r\n}\r\n.pop_tips_mid{\r\n\tbackground:url(../images/pop_tips_mid.png);\r\n\twidth:500px;\r\n\toverflow:hidden;\r\n\tpadding:10px 0 10px 0;\r\n}\r\n.pop_tips_bottom{\r\n\tbackground:url(../images/pop_tips_bottom.png);\r\n\twidth:500px;\r\n\theight:8px;\r\n}\r\n.pop_tips_font{\r\n\tline-height:24px;\r\n\tpadding:0 20px 0 20px;\r\n\tcolor:#fff;\r\n}"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/js/JSONFormatter.js",
    "content": "JSONFormatter = (function() {\n\n  var init = function( json, options ) { \n    \n    // default settings\n    var settings = $.extend( {\n      'appendTo' : 'body',\n      'list_id' : 'json',\n      'collapse' : false\n    }, options);\n    \n    var loopCount = 0;\n    \n    loopObjectOfObjects = function(json2, ulId) {\n      $.each(json2, function(k3, v3) {\n        // object of objects\n        if(typeof v3 == 'object') {\n          $('#' + settings.list_id + ' #' + ulId).append('<li><span>{</span> <ul id=\"' + ulId + '-' + k3 + '\"></ul></li>');\n          $.each(v3, function(k4, v4) {\n            if(typeof v4 == 'object' && v4 != null) {\n              $('#' + settings.list_id + ' #' + ulId + '-' + k3).append('<li>' + k4 + ' <span>{</span> <ul id=\"'+k4+'-'+loopCount+'\"></ul></li>');\n              loopAgain(v4, k4, k4 + '-' + loopCount);\n            }\n            else {\n              $('#' + settings.list_id + ' #' + ulId + '-' + k3).append('<li>' + k4 + ': ' + v4 + '</li>');\n            }\n\n          });\n        } \n        else {\n          // normal array\n          $('#' + settings.list_id + ' #' + ulId).append('<li>' + v3 + '</li>')\n        }\n      });\n    },\n\n    loopAgain = function(v, k, ulId) {\n      loopCount++;\n      $.each(v, function(nextKey, nextVal) {\n        var nextListId = nextKey + '-' + loopCount;\n        var newList = '<ul id=\"' + nextListId + '\"></ul>';\n        if(nextVal != null && typeof nextVal == 'object') {\n          if(nextVal.length == 0) {\n            // an empty object, just output that\n            $('#' + settings.list_id + ' #' + ulId).append('<li>' + nextKey + ': []</li>');\n          } \n          else if(nextVal.length >= 1) {\n            // an object of objects\n            $('#' + settings.list_id + ' #' + ulId).append('<li><b>' + nextKey + ':</b> <span>[</span> ' + newList + '</li>');\n            loopObjectOfObjects(nextVal, nextListId);\n          }\n          else if(nextVal.length == undefined) {\n            // next node\n            $('#' + settings.list_id + ' #' + ulId).append('<li><b>' + nextKey + ':</b> <span>{</span> ' + newList + '</li>');\n            loopAgain(nextVal, nextKey, nextListId);\n          }        \n        }\n        else {\n          // value|key\n          // if(nextKey.val == undefined) {\n          //   $('#' + settings.list_id + ' #' + ulId).append('<li>' + nextVal + '</li>');\n          //   \n          // }\n          // else {\n            $('#' + settings.list_id + ' #' + ulId).append('<li>'+ nextKey + ': ' + nextVal + '</li>');\n            \n          // }\n        }\n      });\n    },\n    \n    addClosingBraces = function() {\n      $('#' + settings.list_id + ' span').each(function() {\n        var closingBrace = '<span>}</span>';\n        if($(this).text() == \"[\") {\n          closingBrace = '<span>]</span>';\n        }\n        $(this).parent().find('ul').eq(0).after(closingBrace);\n      });      \n    };\n\n    var jsonList = $('<ul id=\"' + settings.list_id + '\" />');\n\n    $(settings.appendTo).append(jsonList);\n\n    $.each(json, function(key, val) {\n      \n      \n      \n      if(val != null && typeof val == 'object') {\n        var goObj = false;\n        var goArray = false;\n        var nk = '';\n        $.each(val, function(nextKey, nextVal) {\n        \n          if(nextVal != null && typeof nextVal == 'object') {\n            if(nextVal.length == undefined) {\n              goObj = true;\n              nk = nextKey;\n            }\n            else {\n              goObj = false;\n            }\n          }\n          else {\n            // console.log('nextVal ' + nextVal);\n            goArray = true;\n          }\n        });\n\n        if(goObj) {\n          $('#' + settings.list_id).append('<li><b>' + key + ':</b> <span>[</span><ul id=\"' + nk + '-' + loopCount + '\"></ul></li>');\n          loopObjectOfObjects(val, nk + '-' + loopCount);\n        }\n        else if(goArray) {\n          $('#' + settings.list_id).append('<li><b>' + key + ':</b> <span>[</span><ul id=\"' + nk + '-' + loopCount + '\"></ul></li>');\n          loopAgain(val, nk, nk + '-' + loopCount);\n        }\n        else {\n          $('#' + settings.list_id).append('<li><b>' + key + ':</b> <span>{</span><ul id=\"' + key + '-' + loopCount + '\"></ul></li>');\n          loopAgain(val, key, key + '-' + loopCount);              \n        }\n        \n      }\n      else {\n        $('#' + settings.list_id).append('<li>' + key + ': ' + val + '</li>');\n      }\n    });\n    \n    addClosingBraces();\n    \n    if(settings.collapse) {\n      addToggles(settings.list_id);      \n    }\n    \n  },\n  \n  addToggles = function( listId ) {\n    $('#' + listId + \" > li\").find('ul').each(function() {\n      $(this).parent().find('span').eq(0).after('<span class=\"toggle fake-link\"> - </span>');\n    });\n\n    $('#' + listId +' .toggle').next().slideUp().end().text(' + ');\n\n    $('#' + listId +' .toggle').live('click', function() {\n      if($(this).next().is(\":visible\")) {\n        $(this).next().slideUp().end().text(' + ');\n      }\n      else {\n        $(this).next().slideDown().end().text(' - ');\n      }\n    });\n  };\n  \n  return {\n\n    format: function(json, options) {\n      init(json, options);\n    }\n\n  }\n  \n\n})();"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/js/My97DatePicker/My97DatePicker.html",
    "content": "<html>\r\n<head>\r\n<meta http-equiv=\"content-type\" content=\"text/xml; charset=utf-8\" />\r\n<title>My97DatePicker</title>\r\n<script type=\"text/javascript\" src=\"config.js\"></script>\r\n<script>\r\nif(parent==window)\r\n\tlocation.href = 'http://www.my97.net';\r\nvar $d, $dp, $pdp = parent.$dp, $dt, $tdt, $sdt, $IE=$pdp.ie, $FF = $pdp.ff,$OPERA=$pdp.opera, $ny, $cMark = false;\r\nif ($pdp.eCont) {\r\n\t$dp = {};\r\n\tfor (var p in $pdp) {\r\n\t\t$dp[p] = $pdp[p];\r\n\t}\r\n}\r\nelse\r\n\t$dp = $pdp;\r\n\t\r\n$dp.getLangIndex = function(name){\r\n\tvar arr = langList;\r\n\tfor (var i = 0; i < arr.length; i++) {\r\n\t\tif (arr[i].name == name) {\r\n\t\t\treturn i;\r\n\t\t}\r\n\t}\r\n\treturn -1;\r\n}\r\n\r\n$dp.getLang = function(name){\r\n\tvar index = $dp.getLangIndex(name);\r\n\tif (index == -1) {\r\n\t\tindex = 0;\r\n\t}\r\n\treturn langList[index];\r\n}\r\n \r\n$dp.realLang = $dp.getLang($dp.lang);\r\ndocument.write(\"<script src='lang/\" + $dp.realLang.name + \".js' charset='\" + $dp.realLang.charset + \"'><\\/script>\");\r\n\r\nfor (var i = 0; i < skinList.length; i++) {\r\n    document.write('<link rel=\"stylesheet\" type=\"text/css\" href=\"skin/' + skinList[i].name + '/datepicker.css\" title=\"' + skinList[i].name + '\" charset=\"' + skinList[i].charset + '\" disabled=\"true\"/>');\r\n}\r\n</script>\r\n<script type=\"text/javascript\" src=\"calendar.js\"></script>\r\n</head>\r\n<body leftmargin=\"0\" topmargin=\"0\" onload=\"$c.autoSize()\" tabindex=0>\r\n</body>\r\n</html>\r\n<script>new My97DP();</script>"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/js/My97DatePicker/WdatePicker.js",
    "content": "/*\r\n * My97 DatePicker 4.72 Release\r\n * License: http://www.my97.net/dp/license.asp\r\n */\r\nvar $dp,WdatePicker;(function(){var _={\r\n$wdate:true,\r\n$dpPath:\"\",\r\n$crossFrame:true,\r\ndoubleCalendar:false,\r\nenableKeyboard:true,\r\nenableInputMask:true,\r\nautoUpdateOnChanged:null,\r\nwhichDayIsfirstWeek:4,\r\nposition:{},\r\nlang:\"auto\",\r\nskin:\"default\",\r\ndateFmt:\"yyyy-MM-dd\",\r\nrealDateFmt:\"yyyy-MM-dd\",\r\nrealTimeFmt:\"HH:mm:ss\",\r\nrealFullFmt:\"%Date %Time\",\r\nminDate:\"1900-01-01 00:00:00\",\r\nmaxDate:\"2099-12-31 23:59:59\",\r\nstartDate:\"\",\r\nalwaysUseStartDate:false,\r\nyearOffset:1911,\r\nfirstDayOfWeek:0,\r\nisShowWeek:false,\r\nhighLineWeekDay:true,\r\nisShowClear:true,\r\nisShowToday:true,\r\nisShowOK:true,\r\nisShowOthers:true,\r\nreadOnly:false,\r\nerrDealMode:0,\r\nautoPickDate:null,\r\nqsEnabled:true,\r\nautoShowQS:false,\r\n\r\nspecialDates:null,specialDays:null,disabledDates:null,disabledDays:null,opposite:false,onpicking:null,onpicked:null,onclearing:null,oncleared:null,ychanging:null,ychanged:null,Mchanging:null,Mchanged:null,dchanging:null,dchanged:null,Hchanging:null,Hchanged:null,mchanging:null,mchanged:null,schanging:null,schanged:null,eCont:null,vel:null,errMsg:\"\",quickSel:[],has:{}};WdatePicker=U;var X=window,O=\"document\",J=\"documentElement\",C=\"getElementsByTagName\",V,A,T,I,b;switch(navigator.appName){case\"Microsoft Internet Explorer\":T=true;break;case\"Opera\":b=true;break;default:I=true;break}A=L();if(_.$wdate)M(A+\"skin/WdatePicker.css\");V=X;if(_.$crossFrame){try{while(V.parent&&V.parent[O]!=V[O]&&V.parent[O][C](\"frameset\").length==0)V=V.parent}catch(P){}}if(!V.$dp)V.$dp={ff:I,ie:T,opera:b,el:null,win:X,status:0,defMinDate:_.minDate,defMaxDate:_.maxDate,flatCfgs:[]};B();if($dp.status==0)Z(X,function(){U(null,true)});if(!X[O].docMD){E(X[O],\"onmousedown\",D);X[O].docMD=true}if(!V[O].docMD){E(V[O],\"onmousedown\",D);V[O].docMD=true}E(X,\"onunload\",function(){if($dp.dd)Q($dp.dd,\"none\")});function B(){V.$dp=V.$dp||{};obj={$:function($){return(typeof $==\"string\")?X[O].getElementById($):$},$D:function($,_){return this.$DV(this.$($).value,_)},$DV:function(_,$){if(_!=\"\"){this.dt=$dp.cal.splitDate(_,$dp.cal.dateFmt);if($)for(var B in $)if(this.dt[B]===undefined)this.errMsg=\"invalid property:\"+B;else{this.dt[B]+=$[B];if(B==\"M\"){var C=$[\"M\"]>0?1:0,A=new Date(this.dt[\"y\"],this.dt[\"M\"],0).getDate();this.dt[\"d\"]=Math.min(A+C,this.dt[\"d\"])}}if(this.dt.refresh())return this.dt}return\"\"},show:function(){var A=V[O].getElementsByTagName(\"div\"),$=100000;for(var B=0;B<A.length;B++){var _=parseInt(A[B].style.zIndex);if(_>$)$=_}this.dd.style.zIndex=$+2;Q(this.dd,\"block\")},hide:function(){Q(this.dd,\"none\")},attachEvent:E};for(var $ in obj)V.$dp[$]=obj[$];$dp=V.$dp;$dp.dd=V[O].getElementById(\"_my97DP\")}function E(A,$,_){if(T)A.attachEvent($,_);else if(_){var B=$.replace(/on/,\"\");_._ieEmuEventHandler=function($){return _($)};A.addEventListener(B,_._ieEmuEventHandler,false)}}function L(){var _,A,$=X[O][C](\"script\");for(var B=0;B<$.length;B++){_=$[B].src.substring(0,$[B].src.toLowerCase().indexOf(\"wdatepicker.js\"));A=_.lastIndexOf(\"/\");if(A>0)_=_.substring(0,A+1);if(_)break}return _}function F(F){var E,C;if(F.substring(0,1)!=\"/\"&&F.indexOf(\"://\")==-1){E=V.location.href;C=location.href;if(E.indexOf(\"?\")>-1)E=E.substring(0,E.indexOf(\"?\"));if(C.indexOf(\"?\")>-1)C=C.substring(0,C.indexOf(\"?\"));var G,I,$=\"\",D=\"\",A=\"\",J,H,B=\"\";for(J=0;J<Math.max(E.length,C.length);J++){G=E.charAt(J).toLowerCase();I=C.charAt(J).toLowerCase();if(G==I){if(G==\"/\")H=J}else{$=E.substring(H+1,E.length);$=$.substring(0,$.lastIndexOf(\"/\"));D=C.substring(H+1,C.length);D=D.substring(0,D.lastIndexOf(\"/\"));break}}if($!=\"\")for(J=0;J<$.split(\"/\").length;J++)B+=\"../\";if(D!=\"\")B+=D+\"/\";F=E.substring(0,E.lastIndexOf(\"/\")+1)+B+F}_.$dpPath=F}function M(A,$,B){var D=X[O][C](\"HEAD\").item(0),_=X[O].createElement(\"link\");if(D){_.href=A;_.rel=\"stylesheet\";_.type=\"text/css\";if($)_.title=$;if(B)_.charset=B;D.appendChild(_)}}function Z($,_){E($,\"onload\",_)}function G($){$=$||V;var A=0,_=0;while($!=V){var D=$.parent[O][C](\"iframe\");for(var F=0;F<D.length;F++){try{if(D[F].contentWindow==$){var E=W(D[F]);A+=E.left;_+=E.top;break}}catch(B){}}$=$.parent}return{\"leftM\":A,\"topM\":_}}function W(F){if(F.getBoundingClientRect)return F.getBoundingClientRect();else{var A={ROOT_TAG:/^body|html$/i,OP_SCROLL:/^(?:inline|table-row)$/i},E=false,H=null,_=F.offsetTop,G=F.offsetLeft,D=F.offsetWidth,B=F.offsetHeight,C=F.offsetParent;if(C!=F)while(C){G+=C.offsetLeft;_+=C.offsetTop;if(S(C,\"position\").toLowerCase()==\"fixed\")E=true;else if(C.tagName.toLowerCase()==\"body\")H=C.ownerDocument.defaultView;C=C.offsetParent}C=F.parentNode;while(C.tagName&&!A.ROOT_TAG.test(C.tagName)){if(C.scrollTop||C.scrollLeft)if(!A.OP_SCROLL.test(Q(C)))if(!b||C.style.overflow!==\"visible\"){G-=C.scrollLeft;_-=C.scrollTop}C=C.parentNode}if(!E){var $=a(H);G-=$.left;_-=$.top}D+=G;B+=_;return{\"left\":G,\"top\":_,\"right\":D,\"bottom\":B}}}function N($){$=$||V;var B=$[O],A=($.innerWidth)?$.innerWidth:(B[J]&&B[J].clientWidth)?B[J].clientWidth:B.body.offsetWidth,_=($.innerHeight)?$.innerHeight:(B[J]&&B[J].clientHeight)?B[J].clientHeight:B.body.offsetHeight;return{\"width\":A,\"height\":_}}function a($){$=$||V;var B=$[O],A=B[J],_=B.body;B=(A&&A.scrollTop!=null&&(A.scrollTop>_.scrollTop||A.scrollLeft>_.scrollLeft))?A:_;return{\"top\":B.scrollTop,\"left\":B.scrollLeft}}function D($){var _=$?($.srcElement||$.target):null;try{if($dp.cal&&!$dp.eCont&&$dp.dd&&_!=$dp.el&&$dp.dd.style.display==\"block\")$dp.cal.close()}catch($){}}function Y(){$dp.status=2;H()}function H(){if($dp.flatCfgs.length>0){var $=$dp.flatCfgs.shift();$.el={innerHTML:\"\"};$.autoPickDate=true;$.qsEnabled=false;K($)}}var R,$;function U(J,C){$dp.win=X;B();J=J||{};if(C){if(!G()){$=$||setInterval(function(){if(V[O].readyState==\"complete\")clearInterval($);U(null,true)},50);return}if($dp.status==0){$dp.status=1;K({el:{innerHTML:\"\"}},true)}else return}else if(J.eCont){J.eCont=$dp.$(J.eCont);$dp.flatCfgs.push(J);if($dp.status==2)H()}else{if($dp.status==0){U(null,true);return}if($dp.status!=2)return;var F=D();if(F){$dp.srcEl=F.srcElement||F.target;F.cancelBubble=true}$dp.el=J.el=$dp.$(J.el||$dp.srcEl);if(!$dp.el||$dp.el[\"My97Mark\"]===true||$dp.el.disabled||($dp.el==$dp.el&&Q($dp.dd)!=\"none\"&&$dp.dd.style.left!=\"-1970px\")){$dp.el[\"My97Mark\"]=false;return}K(J);if(F&&$dp.el.nodeType==1&&$dp.el[\"My97Mark\"]===undefined){$dp.el[\"My97Mark\"]=false;var _,A;if(F.type==\"focus\"){_=\"onclick\";A=\"onfocus\"}else{_=\"onfocus\";A=\"onclick\"}E($dp.el,_,$dp.el[A])}}function G(){if(T&&V!=X&&V[O].readyState!=\"complete\")return false;return true}function D(){if(I){func=D.caller;while(func!=null){var $=func.arguments[0];if($&&($+\"\").indexOf(\"Event\")>=0)return $;func=func.caller}return null}return event}}function S(_,$){return _.currentStyle?_.currentStyle[$]:document.defaultView.getComputedStyle(_,false)[$]}function Q(_,$){if(_)if($!=null)_.style.display=$;else return S(_,\"display\")}function K(H,$){for(var D in _)if(D.substring(0,1)!=\"$\")$dp[D]=_[D];for(D in H)if($dp[D]!==undefined)$dp[D]=H[D];var E=$dp.el?$dp.el.nodeName:\"INPUT\";if($||$dp.eCont||new RegExp(/input|textarea|div|span|p|a/ig).test(E))$dp.elProp=E==\"INPUT\"?\"value\":\"innerHTML\";else return;if($dp.lang==\"auto\")$dp.lang=T?navigator.browserLanguage.toLowerCase():navigator.language.toLowerCase();if(!$dp.dd||$dp.eCont||($dp.lang&&$dp.realLang&&$dp.realLang.name!=$dp.lang&&$dp.getLangIndex&&$dp.getLangIndex($dp.lang)>=0)){if($dp.dd&&!$dp.eCont)V[O].body.removeChild($dp.dd);if(_.$dpPath==\"\")F(A);var B=\"<iframe style=\\\"width:1px;height:1px\\\" src=\\\"\"+_.$dpPath+\"My97DatePicker.html\\\" frameborder=\\\"0\\\" border=\\\"0\\\" scrolling=\\\"no\\\"></iframe>\";if($dp.eCont){$dp.eCont.innerHTML=B;Z($dp.eCont.childNodes[0],Y)}else{$dp.dd=V[O].createElement(\"DIV\");$dp.dd.id=\"_my97DP\";$dp.dd.style.cssText=\"position:absolute\";$dp.dd.innerHTML=B;V[O].body.appendChild($dp.dd);Z($dp.dd.childNodes[0],Y);if($)$dp.dd.style.left=$dp.dd.style.top=\"-1970px\";else{$dp.show();C()}}}else if($dp.cal){$dp.show();$dp.cal.init();if(!$dp.eCont)C()}function C(){var F=$dp.position.left,B=$dp.position.top,C=$dp.el;if(C!=$dp.srcEl&&(Q(C)==\"none\"||C.type==\"hidden\"))C=$dp.srcEl;var H=W(C),$=G(X),D=N(V),A=a(V),E=$dp.dd.offsetHeight,_=$dp.dd.offsetWidth;if(isNaN(B)){if(B==\"above\"||(B!=\"under\"&&(($.topM+H.bottom+E>D.height)&&($.topM+H.top-E>0))))B=A.top+$.topM+H.top-E-2;else B=A.top+$.topM+Math.min(H.bottom,D.height-E)+2}else B+=A.top+$.topM;if(isNaN(F))F=A.left+Math.min($.leftM+H.left,D.width-_-5)-(T?2:0);else F+=A.left+$.leftM;$dp.dd.style.top=B+\"px\";$dp.dd.style.left=F+\"px\"}}})()"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/js/My97DatePicker/calendar.js",
    "content": "/*\r\n * My97 DatePicker 4.72 Release\r\n * License: http://www.my97.net/dp/license.asp\r\n */\r\neval(function(p,a,c,k,e,d){e=function(c){return(c<a?\"\":e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)d[e(c)]=k[c]||e(c);k=[function(e){return d[e]}];e=function(){return'\\\\w+'};c=1;};while(c--)if(k[c])p=p.replace(new RegExp('\\\\b'+e(c)+'\\\\b','g'),k[c]);return p;}('o $c;k($5u){5Q.2X.7n(\"6G\",l($){k(!$)h.25();t $});5Q.2X.7e(\"6w\",l(){o $=h.6t;3i($.5M!=1)$=$.7g;t $});7f.2X.2I=l($,b){o A=$.1l(/6p/,\"\");b.5R=l($){6L.1Y=$;t b()};h.7t(A,b.5R,1m)}}l 5H(){$c=h;h.2Y=[];$d=1Q.7q(\"x\");$d.1d=\"4d\";$d.1L=\"<x 1D=6Y><x Y=\\\\\"3H 6W\\\\\"><a 3W=\\\\\"###\\\\\"></a></x><x Y=\\\\\"3H 76\\\\\"><a 3W=\\\\\"###\\\\\"></a></x><x 1c=\\\\\"3g:2v\\\\\"><x Y=\\\\\"33 7a\\\\\"></x><1v Y=3M></x><x 1c=\\\\\"3g:2v\\\\\"><x Y=\\\\\"33 81\\\\\"></x><1v Y=3M></x><x Y=\\\\\"3H 83\\\\\"><a 3W=\\\\\"###\\\\\"></a></x><x Y=\\\\\"3H 84\\\\\"><a 3W=\\\\\"###\\\\\"></a></x><x 1c=\\\\\"3g:5i\\\\\"></x></x><x 1c=\\\\\"86:85;7B:6A\\\\\"></x><x></x><x 1D=7x><x Y=\\\\\"33 7A\\\\\"></x><x Y=\\\\\"33 7M\\\\\"></x><x Y=\\\\\"33 7P\\\\\"></x><1x 2o=0 2m=0 2u=0><1j><18 7J=2><4h 1D=7L></4h>&4B;<1v Y=7o 4b=2><1v 1g=\\\\\":\\\\\" Y=5P 5N><1v Y=5O 4b=2><1v 1g=\\\\\":\\\\\" Y=5P 5N><1v Y=5O 4b=2></18><18><1O 1D=7I></1O></18></1j><1j><18><1O 1D=7Q></1O></18></1j></1x></x><x 1D=7O></x><x 1D=7N><1v Y=4e 1D=7G 3o=1O><1v Y=4e 1D=7z 3o=1O><1v Y=4e 1D=7E 3o=1O></x>\";6M($d,l(){3t()});A();$f.1W=[1Q,$d.1M,$d.1t,$d.2V,$d.3r,$d.3I,$d.2S,$d.28,$d.1U];1b(o B=0;B<$f.1W.u;B++){o b=$f.1W[B];b.3q=B==$f.1W.u-1?$f.1W[1]:$f.1W[B+1];$f.2I(b,\"4c\",4R)}h.5F();$();4Q(\"y,M,H,m,s\");$d.5S.1q=l(){4Z(1)};$d.5T.1q=l(){4Z(-1)};$d.4i.1q=l(){k($d.1E.1c.2h!=\"6K\"){$c.4p();3w($d.1E)}q 1n($d.1E)};1Q.6N.4q($d);l A(){o b=$(\"a\");1r=$(\"x\"),1I=$(\"1v\"),4g=$(\"1O\"),5G=$(\"4h\");$d.3y=b[0];$d.3V=b[1];$d.42=b[3];$d.3Y=b[2];$d.3K=1r[9];$d.1M=1I[0];$d.1t=1I[1];$d.4k=1r[0];$d.3T=1r[4];$d.2J=1r[6];$d.1E=1r[10];$d.2T=1r[11];$d.2H=1r[12];$d.6R=1r[13];$d.6Q=1r[14];$d.6O=1r[15];$d.4i=1r[16];$d.3z=1r[17];$d.2V=1I[2];$d.3r=1I[4];$d.3I=1I[6];$d.2S=1I[7];$d.28=1I[8];$d.1U=1I[9];$d.5S=4g[0];$d.5T=4g[1];$d.5L=5G[0];l $($){t $d.4o($)}}l $(){$d.3y.1q=l(){$1P=$1P<=0?$1P-1:-1;k($1P%5==0){$d.1t.2d();t}$d.1t.1g=$n.y-1;$d.1t.2n()};$d.3V.1q=l(){$n.2C(\"M\",-1);$d.1M.2n()};$d.42.1q=l(){$n.2C(\"M\",1);$d.1M.2n()};$d.3Y.1q=l(){$1P=$1P>=0?$1P+1:1;k($1P%5==0){$d.1t.2d();t}$d.1t.1g=$n.y+1;$d.1t.2n()}}}5H.2X={5F:l(){$1P=0;$f.5K=h;k($f.3N&&$f.z.3N!=1i){$f.z.3N=19;$f.z.4w()}$();h.5j();$n=h.6f=1a 1C();$1B=1a 1C();$1w=h.2w=1a 1C();h.1y=h.34($f.1y);h.2P=$f.2P==1i?($f.Z.2a&&$f.Z.2a?1m:19):$f.2P;$f.2z=$f.2z==1i?($f.4j&&$f.Z.d?1m:19):$f.2z;h.49=h.3f(\"8a\");h.68=h.3f(\"8b\");h.64=h.3f(\"89\");h.5C=h.3f(\"87\");h.1X=h.3P($f.1X,$f.1X!=$f.5D?$f.1S:$f.3j,$f.5D);h.1Z=h.3P($f.1Z,$f.1Z!=$f.5E?$f.1S:$f.3j,$f.5E);k(h.1X.2r(h.1Z)>0)$f.4f=$1k.7V;k(h.1R()){h.5J();h.3O=$f.z[$f.1z]}q h.3m(1m,2);4u($n);$d.5L.1L=$1k.7R;$d.2S.1g=$1k.7S;$d.28.1g=$1k.7Z;$d.1U.1g=$1k.80;$d.1U.1N=!$c.1u($1w);h.5c();h.6l();k($f.4f)7Y($f.4f);h.4C();k($f.z.5M==1&&$f.z[\"4m\"]===4Y){$f.2I($f.z,\"4c\",4R);$f.2I($f.z,\"2n\",l(){k($f.1K.1c.2h==\"2E\"){$c.3Q();k($f.5K.3O!=$f.z[$f.1z]&&$f.z.75)4I($f.z,\"73\")}})}$c.1f=$f.z;3t();l $(){o b,$;1b(b=0;($=1Q.4o(\"71\")[b]);b++)k($[\"72\"].1o(\"1c\")!=-1&&$[\"5I\"]){$.1N=19;k($[\"5I\"]==$f.79)$.1N=1m}}},5J:l(){o b=h.2L();k(b!=0){o $;k(b>0)$=h.1Z;q $=h.1X;k($f.Z.3u){$n.y=$.y;$n.M=$.M;$n.d=$.d}k($f.Z.2a){$n.H=$.H;$n.m=$.m;$n.s=$.s}}},3b:l(J,C,Q,E,B,G,F,K,L){o $;k(J&&J.1R)$=J;q{$=1a 1C();k(J!=\"\"){C=C||$f.1y;o H,P=0,O,A=/3a|2l|36|y|2A|2Z|3U|M|1K|d|%2k|4J|H|4K|m|4G|s|3c|D|4H|W|w/g,b=C.35(A);A.2x=0;k(L)O=J.4O(/\\\\W+/);q{o D=0,M=\"^\";3i((O=A.3h(C))!==1i){k(D>=0)M+=C.1F(D,O.3x);D=A.2x;2G(O[0]){1e\"3a\":M+=\"(\\\\\\\\d{4})\";1h;1e\"2l\":M+=\"(\\\\\\\\d{3})\";1h;1e\"2A\":1e\"2Z\":1e\"3c\":1e\"D\":M+=\"(\\\\\\\\D+)\";1h;5X:M+=\"(\\\\\\\\d\\\\\\\\d?)\";1h}}M+=\".*$\";O=1a 4r(M).3h(J);P=1}k(O){1b(H=0;H<b.u;H++){o I=O[H+P];k(I)2G(b[H]){1e\"2A\":1e\"2Z\":$.M=N(b[H],I);1h;1e\"y\":1e\"36\":I=3E(I,0);k(I<50)I+=5z;q I+=78;$.y=I;1h;1e\"2l\":$.y=3E(I,0)+$f.5p;1h;5X:$[b[H].4T(-1)]=I;1h}}}q $.d=32}}$.6r(Q,E,B,G,F,K);t $;l N(A,$){o b=A==\"2A\"?$1k.5m:$1k.29;1b(o B=0;B<12;B++)k(b[B].3X()==$.77(0,b[B].u).3X())t B+1;t-1}},3f:l(b){o B,$=$f[b],A=\"(?:\";k($){1b(B=0;B<$.u;B++){A+=h.34($[B]);k(B!=$.u-1)A+=\"|\"}A=1a 4r(A+\")\")}q A=1i;t A},2p:l(){o $=h.4s();k($f.z[$f.1z]!=$)$f.z[$f.1z]=$;h.3R()},3R:l($){o b=$f.$($f.6V),$=2K($,h.4s($f.1S));k(b)b.1g=$;$f.z[\"3v\"]=$},34:l(s){o 3D=\"2N\",1s,2B,6c=/#?\\\\{(.*?)\\\\}/;s=s+\"\";1b(o i=0;i<3D.u;i++)s=s.1l(\"%\"+3D.1J(i),h.1V(3D.1J(i),1i,$1B));k(s.1F(0,3)==\"#F{\"){s=s.1F(3,s.u-1);k(s.1o(\"t \")<0)s=\"t \"+s;s=$f.4N.2W(\"1a 6S(\\\\\"\"+s+\"\\\\\");\");s=s()}q 3i((1s=6c.3h(s))!=1i){1s.2x=1s.3x+1s[1].u+1s[0].u-1s[1].u-1;2B=2e(2W(1s[1]));k(2B<0)2B=\"2b\"+(-2B);s=s.1F(0,1s.3x)+2B+s.1F(1s.2x+1)}t s},3P:l(A,B,b){o $;A=h.34(A);k(!A||A==\"\")A=b;k(6Z A==\"70\")$=A;q{$=h.3b(A,B,1i,1i,1,0,0,0,19);$.y=(\"\"+$.y).1l(/^2b/,\"-\");$.M=(\"\"+$.M).1l(/^2b/,\"-\");$.d=(\"\"+$.d).1l(/^2b/,\"-\");$.H=(\"\"+$.H).1l(/^2b/,\"-\");$.m=(\"\"+$.m).1l(/^2b/,\"-\");$.s=(\"\"+$.s).1l(/^2b/,\"-\");k(A.1o(\"%2k\")>=0){A=A.1l(/%2k/g,\"0\");$.d=0;$.M=2e($.M)+1}$.20()}t $},1R:l(){o b,$;k($f.7b||($f.6b!=\"\"&&$f.z[$f.1z]==\"\")){b=h.34($f.6b);$=$f.1S}q{b=$f.z[$f.1z];$=h.1y}$n.2c(h.3b(b,$));k(b!=\"\"){o A=1;k($f.Z.3u&&!h.44($n)){$n.y=$1B.y;$n.M=$1B.M;$n.d=$1B.d;A=0}k($f.Z.2a&&!h.4a($n)){$n.H=$1B.H;$n.m=$1B.m;$n.s=$1B.s;A=0}t A&&h.1u($n)}t 1},44:l($){k($.y!=1i)$=3n($.y,4)+\"-\"+$.M+\"-\"+$.d;t $.35(/^((\\\\d{2}(([69][7p])|([6a][26]))[\\\\-\\\\/\\\\s]?((((0?[6h])|(1[6i]))[\\\\-\\\\/\\\\s]?((0?[1-9])|([1-2][0-9])|(3[6g])))|(((0?[6e])|(11))[\\\\-\\\\/\\\\s]?((0?[1-9])|([1-2][0-9])|(30)))|(0?2[\\\\-\\\\/\\\\s]?((0?[1-9])|([1-2][0-9])))))|(\\\\d{2}(([69][74])|([6a][7u]))[\\\\-\\\\/\\\\s]?((((0?[6h])|(1[6i]))[\\\\-\\\\/\\\\s]?((0?[1-9])|([1-2][0-9])|(3[6g])))|(((0?[6e])|(11))[\\\\-\\\\/\\\\s]?((0?[1-9])|([1-2][0-9])|(30)))|(0?2[\\\\-\\\\/\\\\s]?((0?[1-9])|(1[0-9])|(2[0-8]))))))(\\\\s(((0?[0-9])|([1-2][0-3]))\\\\:([0-5]?[0-9])((\\\\s)|(\\\\:([0-5]?[0-9])))))?$/)},4a:l($){k($.H!=1i)$=$.H+\":\"+$.m+\":\"+$.s;t $.35(/^([0-9]|([0-1][0-9])|([2][0-3])):([0-9]|([0-5][0-9])):([0-9]|([0-5][0-9]))$/)},2L:l($,A){$=$||$n;o b=$.2r(h.1X,A);k(b>0){b=$.2r(h.1Z,A);k(b<0)b=0}t b},1u:l($,A,B){A=A||$f.Z.3A;o b=h.2L($,A);k(b==0){b=1;k(A==\"d\"&&B==1i)B=2y.5Y((1a 1G($.y,$.M-1,$.d).21()-$f.41)%7);b=!h.67(B)&&!h.5Z($,A)}q b=0;t b},62:l(){o b=$f.z,A=h,$=$f.z[$f.1z];k($!=1i){k($!=\"\")A.2w.2c(A.3b($,A.1y));k($==\"\"||(A.44(A.2w)&&A.4a(A.2w)&&A.1u(A.2w))){k($!=\"\"){A.6f.2c(A.2w);A.2p()}q A.3R(\"\")}q t 1m}t 19},3Q:l($){3t();k(h.62()){h.3m(19);$f.1n()}q{k($){3k($);h.3m(1m,2)}q h.3m(1m);$f.24()}},3F:l(){o E,C,D,K,A,H=1a 2s(),F=$1k.5y,G=$f.41,I=\"\",$=\"\",b=1a 1C($n.y,$n.M,$n.d,0,0,0),J=b.y,B=b.M;A=1-1a 1G(J,B-1,1).21()+G;k(A>1)A-=7;H.a(\"<1x Y=5g 2U=3p% 2u=0 2o=0 2m=0>\");H.a(\"<1j Y=5f 4A=5h>\");k($f.61)H.a(\"<18>\"+F[0]+\"</18>\");1b(E=0;E<7;E++)H.a(\"<18>\"+F[(G+E)%7+1]+\"</18>\");H.a(\"</1j>\");1b(E=1,C=A;E<7;E++){H.a(\"<1j>\");1b(D=0;D<7;D++){b.1R(J,B,C++);b.20();k(b.M==B){K=19;k(b.2r($1w,\"d\")==0)I=\"7s\";q k(b.2r($1B,\"d\")==0)I=\"7d\";q I=($f.63&&(0==(G+D)%7||6==(G+D)%7)?\"7k\":\"7l\");$=($f.63&&(0==(G+D)%7||6==(G+D)%7)?\"7i\":\"7j\")}q k($f.5s){K=19;I=\"7c\";$=\"8L\"}q K=1m;k($f.61&&D==0&&(E<4||K))H.a(\"<18 Y=8H>\"+4t(b,$f.41==0?1:0)+\"</18>\");H.a(\"<18 \");k(K){k(h.1u(b,\"d\",D)){k(h.65(2y.5Y((1a 1G(b.y,b.M-1,b.d).21()-$f.41)%7))||h.66(b))I=\"8K\";H.a(\"1q=\\\\\"2O(\"+b.y+\",\"+b.M+\",\"+b.d+\");\\\\\" \");H.a(\"2t=\\\\\"h.1d=\\'\"+$+\"\\'\\\\\" \");H.a(\"2q=\\\\\"h.1d=\\'\"+I+\"\\'\\\\\" \")}q I=\"8M\";H.a(\"Y=\"+I);H.a(\">\"+b.d+\"</18>\")}q H.a(\"></18>\")}H.a(\"</1j>\")}H.a(\"</1x>\");t H.j()},5Z:l(b,A){o $=h.47(b,h.49,A);t(h.49&&$f.5e)?!$:$},67:l($){t h.4x($,h.68)},66:l($){t h.47($,h.64)},65:l($){t h.4x($,h.5C)},47:l($,B,A){o b=A==\"d\"?$f.4l:$f.1S;t B?B.4P(h.3S(b,$)):0},4x:l(b,$){t $?$.4P(b):0},2R:l(p,c,r,e,2j){o s=1a 2s(),4y=2j?\"r\"+p:p;5b=$n[p];s.a(\"<1x 2o=0 2m=3 2u=0\");1b(o i=0;i<r;i++){s.a(\"<1j 3d=\\\\\"3d\\\\\">\");1b(o j=0;j<c;j++){s.a(\"<18 3d \");$n[p]=2W(e);k(($f.5e&&h.2L($n,p)==0)||h.1u($n,p)){s.a(\"Y=\\'1A\\' 2t=\\\\\"h.1d=\\'3e\\'\\\\\" 2q=\\\\\"h.1d=\\'1A\\'\\\\\" 3Z=\\\\\"\");s.a(\"1n($d.\"+p+\"D);$d.\"+4y+\"I.1g=\"+$n[p]+\";$d.\"+4y+\"I.4w();\\\\\"\")}q s.a(\"Y=\\'4z\\'\");s.a(\">\"+(p==\"M\"?$1k.29[$n[p]-1]:$n[p])+\"</18>\")}s.a(\"</1j>\")}s.a(\"</1x>\");$n[p]=5b;t s.j()},4E:l($,b){k($){o A=$.8Q;k($6m)A=$.8V().2v;b.1c.2v=A}},8E:l($){h.4E($,$d.3T);$d.3T.1L=h.2R(\"M\",2,6,\"i+j*6+1\",$==$d.2i)},4v:l(b,A){o $=1a 2s();A=2K(A,$n.y-5);$.a(h.2R(\"y\",2,5,A+\"+i+j*5\",b==$d.2D));$.a(\"<1x 2o=0 2m=3 2u=0 4A=5h><1j><18 \");$.a(h.1X.y<A?\"Y=\\'1A\\' 2t=\\\\\"h.1d=\\'3e\\'\\\\\" 2q=\\\\\"h.1d=\\'1A\\'\\\\\" 3Z=\\'k(1Y.25)1Y.25();1Y.4S=19;$c.4v(0,\"+(A-10)+\")\\'\":\"Y=\\'4z\\'\");$.a(\">\\\\8l</18><18 Y=\\'1A\\' 2t=\\\\\"h.1d=\\'3e\\'\\\\\" 2q=\\\\\"h.1d=\\'1A\\'\\\\\" 3Z=\\\\\"1n($d.2J);$d.1t.4w();\\\\\">\\\\5l</18><18 \");$.a(h.1Z.y>A+10?\"Y=\\'1A\\' 2t=\\\\\"h.1d=\\'3e\\'\\\\\" 2q=\\\\\"h.1d=\\'1A\\'\\\\\" 3Z=\\'k(1Y.25)1Y.25();1Y.4S=19;$c.4v(0,\"+(A+10)+\")\\'\":\"Y=\\'4z\\'\");$.a(\">\\\\8p</18></1j></1x>\");h.4E(b,$d.2J);$d.2J.1L=$.j()},3C:l(A,b,$){$d[A+\"D\"].1L=h.2R(A,6,b,$)},8n:l(){h.3C(\"H\",4,\"i * 6 + j\")},8e:l(){h.3C(\"m\",2,\"i * 30 + j * 5\")},8c:l(){h.3C(\"s\",1,\"j * 10\")},4p:l(A){h.6F();o b=h.2Y,C=b.1c,$=1a 2s();$.a(\"<1x Y=5g 2U=3p% 2f=3p% 2u=0 2o=0 2m=0>\");$.a(\"<1j Y=5f><18><x 1c=\\\\\"3g:2v\\\\\">\"+$1k.8g+\"</x>\");k(!A)$.a(\"<x 1c=\\\\\"3g:5i;8z:8y\\\\\" 1q=\\\\\"1n($d.1E);\\\\\">\\\\5l</x>\");$.a(\"</18></1j>\");1b(o B=0;B<b.u;B++)k(b[B]){$.a(\"<1j><18 1c=\\'55-4A:2v\\' 3d=\\'3d\\' Y=\\'1A\\' 2t=\\\\\"h.1d=\\'3e\\'\\\\\" 2q=\\\\\"h.1d=\\'1A\\'\\\\\" 1q=\\\\\"\");$.a(\"2O(\"+b[B].y+\", \"+b[B].M+\", \"+b[B].d+\",\"+b[B].H+\",\"+b[B].m+\",\"+b[B].s+\");\\\\\">\");$.a(\"&4B;\"+h.3S(1i,b[B]));$.a(\"</18></1j>\")}q $.a(\"<1j><18 Y=\\'1A\\'>&4B;</18></1j>\");$.a(\"</1x>\");$d.1E.1L=$.j()},5j:l(){$(/w/);$(/4H|W/);$(/3c|D/);$(/3a|2l|36|y/);$(/2A|2Z|3U|M/);$(/1K|d/);$(/4J|H/);$(/4K|m/);$(/4G|s/);$f.Z.3u=($f.Z.y||$f.Z.M||$f.Z.d)?19:1m;$f.Z.2a=($f.Z.H||$f.Z.m||$f.Z.s)?19:1m;$f.3j=$f.3j.1l(/%1G/,$f.4l).1l(/%8w/,$f.5d);k($f.Z.3u){k($f.Z.2a)$f.1S=$f.3j;q $f.1S=$f.4l}q $f.1S=$f.5d;l $(b){o $=(b+\"\").4T(1,2);$f.Z[$]=b.3h($f.1y)?($f.Z.3A=$,19):1m}},5c:l(){o $=0;$f.Z.y?($=1,24($d.1t,$d.3y,$d.3Y)):1n($d.1t,$d.3y,$d.3Y);$f.Z.M?($=1,24($d.1M,$d.3V,$d.42)):1n($d.1M,$d.3V,$d.42);$?24($d.4k):1n($d.4k);k($f.Z.2a){24($d.2H);3G($d.2V,$f.Z.H);3G($d.3r,$f.Z.m);3G($d.3I,$f.Z.s)}q 1n($d.2H);2M($d.2S,$f.5w);2M($d.28,$f.5x);2M($d.1U,$f.4j);2M($d.4i,!$f.5n&&$f.Z.d&&$f.8t);k($f.6v||!($f.5w||$f.5x||$f.4j))1n($d.3z);q 24($d.3z)},3m:l(B,D){o A=$f.z,b=$5u?\"Y\":\"1d\";k(B)C(A);q{k(D==1i)D=$f.8s;2G(D){1e 0:k(8B($1k.8C)){A[$f.1z]=h.3O;C(A)}q $(A);1h;1e 1:A[$f.1z]=h.3O;C(A);1h;1e 2:$(A);1h}}l C(A){o B=A.1d;k(B){o $=B.1l(/5B/g,\"\");k(B!=$)A.5A(b,$)}}l $($){$.5A(b,$.1d+\" 5B\")}},1V:l(D,b,$){$=$||$1w;o H,C=[D+D,D],E,A=$[D],F=l($){t 3n(A,$.u)};2G(D){1e\"w\":A=21($);1h;1e\"D\":o G=21($)+1;F=l($){t $.u==2?$1k.8A[G]:$1k.5y[G]};1h;1e\"W\":A=4t($);1h;1e\"y\":C=[\"3a\",\"2l\",\"36\",\"y\"];b=b||C[0];F=l(b){t 3n((b.u<4)?(b.u<3?$.y%3p:($.y+5z-$f.5p)%8x):A,b.u)};1h;1e\"M\":C=[\"2A\",\"2Z\",\"3U\",\"M\"];F=l($){t($.u==4)?$1k.5m[A-1]:($.u==3)?$1k.29[A-1]:3n(A,$.u)};1h}b=b||D+D;k(\"2N\".1o(D)>-1&&D!=\"y\"&&!$f.Z[D])k(\"8h\".1o(D)>-1)A=0;q A=1;o B=[];1b(H=0;H<C.u;H++){E=C[H];k(b.1o(E)>=0){B[H]=F(E);b=b.1l(E,\"{\"+H+\"}\")}}1b(H=0;H<B.u;H++)b=b.1l(1a 4r(\"\\\\\\\\{\"+H+\"\\\\\\\\}\",\"g\"),B[H]);t b},3S:l(b,$){$=$||h.3b($f.z[$f.1z],h.1y)||$1w;b=b||h.1y;k(b.1o(\"%2k\")>=0){o A=1a 1C();A.2c($);A.d=0;A.M=2e(A.M)+1;A.20();b=b.1l(/%2k/g,A.d)}o B=\"8d\";1b(o D=0;D<B.u;D++){o C=B.1J(D);b=h.1V(C,b,$)}k($f.Z[\"D\"]){b=b.1l(/3c/g,\"%1K\").1l(/D/g,\"%d\");b=h.1V(\"M\",b,$);b=b.1l(/\\\\%1K/g,h.1V(\"D\",\"3c\")).1l(/\\\\%d/g,h.1V(\"D\",\"D\"))}q b=h.1V(\"M\",b,$);t b},8o:l(b,$){t h.1V(b,$,$n)},4s:l($){t h.3S($,$n)},4C:l(){$d.3K.1L=\"\";k($f.5n){$c.2P=19;$f.5s=1m;$d.1d=\"4d 8T\";o $=1a 2s();$.a(\"<1x Y=8R 2U=3p% 2o=0 2m=0 2u=1><1j><18 5q=5r>\");$.a(h.3F());$.a(\"</18><18 5q=5r>\");$n.2C(\"M\",1);$.a(h.3F());$d.2i=$d.1M.5o(19);$d.2D=$d.1t.5o(19);$d.3K.4q($d.2i);$d.3K.4q($d.2D);$d.2i.1g=$1k.29[$n.M-1];$d.2i[\"3v\"]=$n.M;$d.2D.1g=$n.y;4Q(\"6H,6E\");$d.2i.1d=$d.2D.1d=\"3M\";$n.2C(\"M\",-1);$.a(\"</18></1j></1x>\");$d.2T.1L=$.j()}q{$d.1d=\"4d\";$d.2T.1L=h.3F()}k(!$f.Z.d||$f.8J){h.4p(19);3w($d.1E)}q 1n($d.1E);h.6P()},6P:l(){o b=8W.1Q.4o(\"8k\");1b(o C=0;C<b.u;C++){o $=$d.1c.2f;$d.1c.2f=\"\";o A=$d.3l;k(b[C].8j==6L&&A){b[C].1c.2U=$d.6q+\"6s\";o B=$d.2H.3l;k(B&&$d.3z.1c.2h==\"2E\"&&$d.2H.1c.2h!=\"2E\"&&1Q.6N.8f-A>=B){A+=B;$d.1c.2f=A}q $d.1c.2f=$;b[C].1c.2f=2y.5v(A,$d.3l)+\"6s\"}}$d.1E.1c.2U=$d.2T.6q;$d.1E.1c.2f=$d.2T.3l},4W:l(){$n.d=2y.8D(1a 1G($n.y,$n.M,0).2F(),$n.d);$1w.2c($n);h.2p();k(!$f.6v)k(h.1u($n)){4n();1n($f.1K)}k($f.6u)2g(\"6u\")},6l:l(){$d.2S.1q=l(){k(!2g(\"8q\")){$f.z[$f.1z]=\"\";$c.3R(\"\");4n();1n($f.1K);k($f.6n)2g(\"6n\")}};$d.1U.1q=l(){2O()};k(h.1u($1B)){$d.28.1N=1m;$d.28.1q=l(){$n.2c($1B);2O()}}q $d.28.1N=19},6F:l(){o H,G,A,F,C=[],$=5,E=$f.6I.u,b=$f.Z.3A;k(E>$)E=$;q k(b==\"m\"||b==\"s\")C=[-60,-30,0,30,60,-15,15,-45,45];q 1b(H=0;H<$;H++)C[H]=$n[b]-2+H;1b(H=G=0;H<E;H++){A=h.3P($f.6I[H]);k(h.1u(A))h.2Y[G++]=A}o B=\"2N\",D=[1,1,1,0,0,0];1b(H=0;H<=B.1o(b);H++)D[H]=$n[B.1J(H)];1b(H=0;G<$;H++)k(H<C.u){A=1a 1C(D[0],D[1],D[2],D[3],D[4],D[5]);A[b]=C[H];A.20();k(h.1u(A))h.2Y[G++]=A}q h.2Y[G++]=1i}};l 4n(){o b=$f.z;8F{k(b.1c.2h!=\"2E\"&&b.3o!=\"6A\"&&(b.6D.3X()==\"1v\"||b.6D.3X()==\"8G\")){k($f.8N==b)$f.z[\"4m\"]=19;$f.z.2d();t}}8O($){}b[\"4m\"]=1m}l 2s(){h.s=1a 7h();h.i=0;h.a=l($){h.s[h.i++]=$};h.j=l(){t h.s.7r(\"\")}}l 4t($,B){B=B||0;o b=1a 1G($.y,$.M-1,$.d+B);b.6z(b.2F()-(b.21()+6)%7+$f.7v-1);o A=b.6J();b.7m(0);b.6z(4);t 2y.6X((A-b.6J())/(7*6T))+1}l 21($){o b=1a 1G($.y,$.M-1,$.d);t b.21()}l 24(){3s(2Q,\"\")}l 3w(){3s(2Q,\"6K\")}l 1n(){3s(2Q,\"2E\")}l 3s(b,$){1b(i=0;i<b.u;i++)b[i].1c.2h=$}l 2M(b,$){$?24(b):1n(b)}l 3G(b,$){k($)b.1N=1m;q{b.1N=19;b.1g=\"7T\"}}l c(p,1H){k(p==\"M\")1H=43(1H,1,12);q k(p==\"H\")1H=43(1H,0,23);q k(\"5k\".1o(p)>=0)1H=43(1H,0,59);k($1w[p]!=1H&&!2g(p+\"7U\")){o 6o=\"1T(\\\\\"\"+p+\"\\\\\",\"+1H+\")\",3B=$c.2L();k(3B==0)2W(6o);q k(3B<0)4D($c.1X);q k(3B>0)4D($c.1Z);$d.1U.1N=!$c.1u($1w);k(\"7C\".1o(p)>=0)$c.4C();2g(p+\"7D\")}l 4D($){4u($c.1u($)?$:$1w)}}l 4u($){1T(\"y\",$.y);1T(\"M\",$.M);1T(\"d\",$.d);1T(\"H\",$.H);1T(\"m\",$.m);1T(\"s\",$.s)}l 2O(F,B,b,D,C,A){o $=1a 1C($n.y,$n.M,$n.d,$n.H,$n.m,$n.s);$n.1R(F,B,b,D,C,A);k(!2g(\"7H\")){o E=$.y==F&&$.M==B&&$.d==b;k(!E&&2Q.u!=0){c(\"y\",F);c(\"M\",B);c(\"d\",b);$c.1f=$f.z;k($f.2z)$c.2p()}k($c.2P||E||2Q.u==0)$c.4W()}q $n=$}l 2g($){o b;k($f[$])b=$f[$].4V($f.z,$f);t b}l 1T(b,$){k($==1i)$=$n[b];$1w[b]=$n[b]=$;k(\"7K\".1o(b)>=0)$d[b+\"I\"].1g=$;k(b==\"M\"){$d.1M[\"3v\"]=$;$d.1M.1g=$1k.29[$-1]}}l 43(b,$,A){k(b<$)b=$;q k(b>A)b=A;t b}l 6M($,b){$.2I(\"4c\",l(){o $=1Y,A=($.4M==4Y)?$.4F:$.4M;k(A==9)b()})}l 3n($,b){$=$+\"\";3i($.u<b)$=\"0\"+$;t $}l 3t(){1n($d.2J,$d.3T,$d.6R,$d.6Q,$d.6O)}l 4Z($){k($c.1f==4Y)$c.1f=$d.2V;2G($c.1f){1e $d.2V:c(\"H\",$n.H+$);1h;1e $d.3r:c(\"m\",$n.m+$);1h;1e $d.3I:c(\"s\",$n.s+$);1h}k($f.2z)$c.2p()}l 1C(D,A,$,C,B,b){h.1R(D,A,$,C,B,b)}1C.2X={1R:l(E,B,b,D,C,A){o $=1a 1G();h.y=1p(E,h.y,$.52());h.M=1p(B,h.M,$.5a()+1);h.d=$f.Z.d?1p(b,h.d,$.2F()):1;h.H=1p(D,h.H,$.54());h.m=1p(C,h.m,$.53());h.s=1p(A,h.s,$.56())},2c:l($){k($)h.1R($.y,$.M,$.d,$.H,$.m,$.s)},6r:l(E,B,b,D,C,A){o $=1a 1G();h.y=1p(h.y,E,$.52());h.M=1p(h.M,B,$.5a()+1);h.d=$f.Z.d?1p(h.d,b,$.2F()):1;h.H=1p(h.H,D,$.54());h.m=1p(h.m,C,$.53());h.s=1p(h.s,A,$.56())},2r:l($,C){o A=\"2N\",b,B;C=A.1o(C);C=C>=0?C:5;1b(o D=0;D<=C;D++){B=A.1J(D);b=h[B]-$[B];k(b>0)t 1;q k(b<0)t-1}t 0},20:l(){o $=1a 1G(h.y,h.M-1,h.d,h.H,h.m,h.s);h.y=$.52();h.M=$.5a()+1;h.d=$.2F();h.H=$.54();h.m=$.53();h.s=$.56();t!6j(h.y)},2C:l(b,$){k(\"2N\".1o(b)>=0){o A=h.d;k(b==\"M\")h.d=1;h[b]+=$;h.20();h.d=A}}};l 2e($){t 7F($,10)}l 3E($,b){t 2K(2e($),b)}l 1p($,A,b){t 3E($,2K(A,b))}l 2K($,b){t $==1i||6j($)?b:$}l 4I(A,$){k($6m)A.4I(\"6p\"+$);q{o b=1Q.82(\"88\");b.7W($,19,19);A.7X(b)}}l 3J($){o A,B,b=\"y,M,H,m,s,6E,6H\".4O(\",\");1b(B=0;B<b.u;B++){A=b[B];k($d[A+\"I\"]==$)t A.4T(A.u-1,A.u)}t 0}l 6k($){o b=3J(h);k(!b)t;$c.1f=h;k(b==\"y\")h.1d=\"6x\";q k(b==\"M\"){h.1d=\"6x\";h.1g=h[\"3v\"]}h.4U();$c[\"2R\"+b](h);3w($d[b+\"D\"])}l 3L(6y){o p=3J(h),2j,51,v=h.1g,6C=$n[p];k(p==0)t;$n[p]=6B(v)>=0?6B(v):$n[p];k(p==\"y\"){2j=h==$d.2D;k(2j&&$n.M==12)$n.y-=1}q k(p==\"M\"){2j=h==$d.2i;k(2j){51=$1k.29[$n[p]-1];k(6C==12)$n.y+=1;$n.2C(\"M\",-1)}k($1w.M==$n.M)h.1g=51||$1k.29[$n[p]-1];k(($1w.y!=$n.y))c(\"y\",$n.y)}2W(\"c(\\\\\"\"+p+\"\\\\\",\"+$n[p]+\")\");k(6y!==19){k(p==\"y\"||p==\"M\")h.1d=\"3M\";1n($d[p+\"D\"])}k($f.2z)$c.2p()}l 3k($){k($.25){$.25();$.8i()}q{$.4S=19;$.6G=1m}k($5t)$.4F=0}l 4Q($){o A=$.4O(\",\");1b(o B=0;B<A.u;B++){o b=A[B]+\"I\";$d[b].8u=6k;$d[b].2n=3L}}l 4R(M){o H=M.6w||M.6t,Q=M.4M||M.4F;58=$f.1K.1c.2h!=\"2E\";k(Q>=8m&&Q<=8U)Q-=48;k($f.8I&&58){k(!H.3q){H.3q=$f.1W[1];$c.1f=$f.z}k(H==$f.z)$c.1f=$f.z;k(Q==27)k(H==$f.z){$c.3Q();t}q $f.z.2d();k(Q>=37&&Q<=40){o U;k($c.1f==$f.z||$c.1f==$d.1U)k($f.Z.d){U=\"d\";k(Q==38)$n[U]-=7;q k(Q==39)$n[U]+=1;q k(Q==37)$n[U]-=1;q $n[U]+=7;$n.20();c(\"y\",$n[\"y\"]);c(\"M\",$n[\"M\"]);c(\"d\",$n[U]);3k(M);t}q{U=$f.Z.3A;$d[U+\"I\"].2d()}U=U||3J($c.1f);k(U){k(Q==38||Q==39)$n[U]+=1;q $n[U]-=1;$n.20();$c.1f.1g=$n[U];3L.4V($c.1f,19);$c.1f.4U()}}q k(Q==9){o D=H.3q;1b(o R=0;R<$f.1W.u;R++)k(D.1N==19||D.3l==0)D=D.3q;q 1h;k($c.1f!=D){$c.1f=D;D.2d()}}q k(Q==13){3L.4V($c.1f);k($c.1f.3o==\"1O\")$c.1f.8P();q $c.4W();$c.1f=$f.z}}q k(Q==9&&H==$f.z)$c.3Q();k($f.8S&&!$5t&&!$f.3N&&$c.1f==$f.z&&(Q>=48&&Q<=57)){o T=$f.z,S=T.1g,F=E(T),I={22:\"\",1s:[]},R=0,K,N=0,X=0,O=0,J,b=/3a|2l|36|y|3U|M|1K|d|%2k|4J|H|4K|m|4G|s|4H|W|w/g,L=$f.1y.35(b),B,A,$,V,W,G,J=0;k(S!=\"\"){O=S.35(/[0-9]/g);O=O==1i?0:O.u;1b(R=0;R<L.u;R++)O-=2y.5v(L[R].u,2);O=O>=0?1:0;k(O==1&&F>=S.u)F=S.u-1}S=S.1F(0,F)+8r.8v(Q)+S.1F(F+O);F++;1b(R=0;R<S.u;R++){o C=S.1J(R);k(/[0-9]/.4P(C))I.22+=C;q I.1s[R]=1}S=\"\";b.2x=0;3i((K=b.3h($f.1y))!==1i){X=K.3x-(K[0]==\"%2k\"?1:0);k(N>=0){S+=$f.1y.1F(N,X);k(F>=N+J&&F<=X+J)F+=X-N}N=b.2x;G=N-X;B=I.22.1F(0,G);A=K[0].1J(0);$=2e(B.1J(0));k(I.22.u>1){V=I.22.1J(1);W=$*10+2e(V)}q{V=\"\";W=$}k(I.1s[X+1]||A==\"M\"&&W>12||A==\"d\"&&W>31||A==\"H\"&&W>23||\"5k\".1o(A)>=0&&W>59){k(K[0].u==2)B=\"0\"+$;q B=$;F++}q k(G==1){B=W;G++;J++}S+=B;I.22=I.22.1F(G);k(I.22==\"\")1h}T.1g=S;P(T,F);3k(M)}k(58&&$c.1f!=$f.z&&!((Q>=48&&Q<=57)||Q==8||Q==46))3k(M);l E(A){o b=0;k($f.4N.1Q.6d){o B=$f.4N.1Q.6d.6U(),$=B.55.u;B.5V(\"4X\",-A.1g.u);b=B.55.u-$}q k(A.4L||A.4L==\"0\")b=A.4L;t b}l P(b,A){k(b.5U){b.2d();b.5U(A,A)}q k(b.5W){o $=b.5W();$.7w(19);$.7y(\"4X\",A);$.5V(\"4X\",A);$.4U()}}}',62,555,'|||||||||||_||||dp||this|||if|function||dt|var||else|||return|length|||div||el|||||||||||||||||||||||||class|has|||||||||td|true|new|for|style|className|case|currFocus|value|break|null|tr|lang|replace|false|hide|indexOf|pInt3|onclick|divs|arr|yI|checkValid|input|sdt|table|dateFmt|elProp|menu|tdt|DPDate|id|qsDivSel|substring|Date|pv|ipts|charAt|dd|innerHTML|MI|disabled|button|ny|document|loadDate|realFmt|sv|okI|getP|focusArr|minDate|event|maxDate|refresh|getDay|str||show|preventDefault|||todayI|aMonStr|st|9700|loadFromDate|focus|pInt|height|callFunc|display|rMI|isR|ld|yyy|cellpadding|onblur|cellspacing|update|onmouseout|compareWith|sb|onmouseover|border|left|date|lastIndex|Math|autoUpdateOnChanged|MMMM|tmpEval|attr|ryI|none|getDate|switch|tDiv|attachEvent|yD|rtn|checkRange|shorH|yMdHms|day_Click|autoPickDate|arguments|_f|clearI|dDiv|width|HI|eval|prototype|QS|MMM||||menuSel|doExp|match|yy||||yyyy|splitDate|DD|nowrap|menuOn|_initRe|float|exec|while|realFullFmt|_cancelKey|offsetHeight|mark|doStr|type|100|nextCtrl|mI|setDisp|hideSel|sd|realValue|showB|index|navLeftImg|bDiv|minUnit|rv|_fHMS|ps|pInt2|_fd|disHMS|navImg|sI|_foundInput|rMD|_blur|yminput|readOnly|oldValue|doCustomDate|close|setRealValue|getDateStr|MD|MM|leftImg|href|toLowerCase|navRightImg|onmousedown||firstDayOfWeek|rightImg|makeInRange|isDate|||testDate||ddateRe|isTime|maxlength|onkeydown|WdateDiv|dpButton|errMsg|btns|span|qsDiv|isShowOK|titleDiv|realDateFmt|My97Mark|elFocus|getElementsByTagName|_fillQS|appendChild|RegExp|getNewDateStr|getWeek|_setAll|_fy|blur|testDay|fp|invalidMenu|align|nbsp|draw|_setFrom|_fMyPos|keyCode|ss|WW|fireEvent|HH|mm|selectionStart|which|win|split|test|_inputBindEvent|_tab|cancelBubble|slice|select|call|pickDate|character|undefined|updownEvent||mStr|getFullYear|getMinutes|getHours|text|getSeconds||isShow||getMonth|bak|initShowAndHide|realTimeFmt|opposite|MTitle|WdayTable|center|right|_dealFmt|ms|xd7|aLongMonStr|doubleCalendar|cloneNode|yearOffset|valign|top|isShowOthers|OPERA|FF|max|isShowClear|isShowToday|aWeekStr|2000|setAttribute|WdateFmtErr|sdayRe|defMinDate|defMaxDate|init|spans|My97DP|title|_makeDateInRange|cal|timeSpan|nodeType|readonly|tE|tm|Event|_ieEmuEventHandler|upButton|downButton|setSelectionRange|moveStart|createTextRange|default|abs|testDisDate||isShowWeek|checkAndUpdate|highLineWeekDay|sdateRe|testSpeDay|testSpeDate|testDisDay|ddayRe|02468|13579|startDate|re|selection|469|newdate|01|13578|02|isNaN|_focus|initBtn|IE|oncleared|func|on|offsetWidth|coverDate|px|target|onpicked|eCont|srcElement|yminputfocus|showDiv|setDate|hidden|Number|oldv|nodeName|ry|initQS|returnValue|rM|quickSel|valueOf|block|window|attachTabEvent|body|sD|autoSize|mD|HD|Function|86400000|createRange|vel|NavImgll|round|dpTitle|typeof|object|link|rel|change|1235679|onchange|NavImgl|substr|1900|skin|MMenu|alwaysUseStartDate|WotherDay|Wtoday|__defineGetter__|HTMLElement|parentNode|Array|WwdayOn|WdayOn|Wwday|Wday|setMonth|__defineSetter__|tB|048|createElement|join|Wselday|addEventListener|01345789|whichDayIsfirstWeek|collapse|dpTime|moveEnd|dpTodayInput|hhMenu|overflow|yMd|changed|dpOkInput|parseInt|dpClearInput|onpicking|dpTimeUp|rowspan|yHms|dpTimeStr|mmMenu|dpControl|dpQS|ssMenu|dpTimeDown|timeStr|clearStr|00|changing|err_1|initEvent|dispatchEvent|alert|todayStr|okStr|YMenu|createEvent|NavImgrr|NavImgr|absolute|position|specialDays|HTMLEvents|specialDates|disabledDates|disabledDays|_fs|ydHmswW|_fm|scrollHeight|quickStr|Hms|stopPropagation|contentWindow|iframe|u2190|96|_fH|getNewP|u2192|onclearing|String|errDealMode|qsEnabled|onfocus|fromCharCode|Time|1000|pointer|cursor|aLongWeekStr|confirm|errAlertMsg|min|_fM|try|textarea|Wweek|enableKeyboard|autoShowQS|WspecialDay|WotherDayOn|WinvalidDay|srcEl|catch|click|offsetLeft|WdayTable2|enableInputMask|WdateDiv2|105|getBoundingClientRect|parent'.split('|'),0,{}))"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/js/My97DatePicker/config.js",
    "content": "var langList = \r\n[\r\n\t{name:'en',\tcharset:'UTF-8'},\r\n\t{name:'zh-cn',\tcharset:'UTF-8'},\r\n\t{name:'zh-tw',\tcharset:'UTF-8'}\r\n];\r\n\r\nvar skinList = \r\n[\r\n\t{name:'default',\tcharset:'UTF-8'},\r\n\t{name:'whyGreen',\tcharset:'UTF-8'}\r\n];"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/js/My97DatePicker/lang/en.js",
    "content": "var $lang={\r\nerrAlertMsg: \"Invalid date or the date out of range,redo or not?\",\r\naWeekStr: [\"wk\", \"Sun\", \"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\", \"Sat\"],\r\naLongWeekStr:[\"wk\",\"Sunday\",\"Monday\",\"Tuesday\",\"Wednesday\",\"Thursday\",\"Friday\",\"Saturday\",\"Sunday\"],\r\naMonStr: [\"Jan\", \"Feb\", \"Mar\", \"Apr\", \"May\", \"Jun\", \"Jul\", \"Aug\", \"Sep\", \"Oct\", \"Nov\", \"Dec\"],\r\naLongMonStr: [\"January\",\"February\",\"March\",\"April\",\"May\",\"June\",\"July\",\"August\",\"September\",\"October\",\"November\",\"December\"],\r\nclearStr: \"Clear\",\r\ntodayStr: \"Today\",\r\nokStr: \"OK\",\r\nupdateStr: \"OK\",\r\ntimeStr: \"Time\",\r\nquickStr: \"Quick Selection\",\r\nerr_1: 'MinDate Cannot be bigger than MaxDate!'\r\n}"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/js/My97DatePicker/lang/zh-cn.js",
    "content": "var $lang={\r\nerrAlertMsg: \"\\u4E0D\\u5408\\u6CD5\\u7684\\u65E5\\u671F\\u683C\\u5F0F\\u6216\\u8005\\u65E5\\u671F\\u8D85\\u51FA\\u9650\\u5B9A\\u8303\\u56F4,\\u9700\\u8981\\u64A4\\u9500\\u5417?\",\r\naWeekStr: [\"\\u5468\",\"\\u65E5\",\"\\u4E00\",\"\\u4E8C\",\"\\u4E09\",\"\\u56DB\",\"\\u4E94\",\"\\u516D\"],\r\naLongWeekStr:[\"\\u5468\",\"\\u661F\\u671F\\u65E5\",\"\\u661F\\u671F\\u4E00\",\"\\u661F\\u671F\\u4E8C\",\"\\u661F\\u671F\\u4E09\",\"\\u661F\\u671F\\u56DB\",\"\\u661F\\u671F\\u4E94\",\"\\u661F\\u671F\\u516D\"],\r\naMonStr: [\"\\u4E00\\u6708\",\"\\u4E8C\\u6708\",\"\\u4E09\\u6708\",\"\\u56DB\\u6708\",\"\\u4E94\\u6708\",\"\\u516D\\u6708\",\"\\u4E03\\u6708\",\"\\u516B\\u6708\",\"\\u4E5D\\u6708\",\"\\u5341\\u6708\",\"\\u5341\\u4E00\",\"\\u5341\\u4E8C\"],\r\naLongMonStr: [\"\\u4E00\\u6708\",\"\\u4E8C\\u6708\",\"\\u4E09\\u6708\",\"\\u56DB\\u6708\",\"\\u4E94\\u6708\",\"\\u516D\\u6708\",\"\\u4E03\\u6708\",\"\\u516B\\u6708\",\"\\u4E5D\\u6708\",\"\\u5341\\u6708\",\"\\u5341\\u4E00\\u6708\",\"\\u5341\\u4E8C\\u6708\"],\r\nclearStr: \"\\u6E05\\u7A7A\",\r\ntodayStr: \"\\u4ECA\\u5929\",\r\nokStr: \"\\u786E\\u5B9A\",\r\nupdateStr: \"\\u786E\\u5B9A\",\r\ntimeStr: \"\\u65F6\\u95F4\",\r\nquickStr: \"\\u5FEB\\u901F\\u9009\\u62E9\", \r\nerr_1: '\\u6700\\u5C0F\\u65E5\\u671F\\u4E0D\\u80FD\\u5927\\u4E8E\\u6700\\u5927\\u65E5\\u671F!'\r\n}"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/js/My97DatePicker/lang/zh-tw.js",
    "content": "var $lang={\r\nerrAlertMsg: \"\\u4E0D\\u5408\\u6CD5\\u7684\\u65E5\\u671F\\u683C\\u5F0F\\u6216\\u8005\\u65E5\\u671F\\u8D85\\u51FA\\u9650\\u5B9A\\u7BC4\\u570D,\\u9700\\u8981\\u64A4\\u92B7\\u55CE?\",\r\naWeekStr: [\"\\u5468\",\"\\u65E5\",\"\\u4E00\",\"\\u4E8C\",\"\\u4E09\",\"\\u56DB\",\"\\u4E94\",\"\\u516D\"],\r\naLongWeekStr:[\"\\u5468\",\"\\u661F\\u671F\\u65E5\",\"\\u661F\\u671F\\u4E00\",\"\\u661F\\u671F\\u4E8C\",\"\\u661F\\u671F\\u4E09\",\"\\u661F\\u671F\\u56DB\",\"\\u661F\\u671F\\u4E94\",\"\\u661F\\u671F\\u516D\"],\r\naMonStr: [\"\\u4E00\\u6708\",\"\\u4E8C\\u6708\",\"\\u4E09\\u6708\",\"\\u56DB\\u6708\",\"\\u4E94\\u6708\",\"\\u516D\\u6708\",\"\\u4E03\\u6708\",\"\\u516B\\u6708\",\"\\u4E5D\\u6708\",\"\\u5341\\u6708\",\"\\u5341\\u4E00\",\"\\u5341\\u4E8C\"],\r\naLongMonStr: [\"\\u4E00\\u6708\",\"\\u4E8C\\u6708\",\"\\u4E09\\u6708\",\"\\u56DB\\u6708\",\"\\u4E94\\u6708\",\"\\u516D\\u6708\",\"\\u4E03\\u6708\",\"\\u516B\\u6708\",\"\\u4E5D\\u6708\",\"\\u5341\\u6708\",\"\\u5341\\u4E00\\u6708\",\"\\u5341\\u4E8C\\u6708\"],\r\nclearStr: \"\\u6E05\\u7A7A\",\r\ntodayStr: \"\\u4ECA\\u5929\",\r\nokStr: \"\\u78BA\\u5B9A\",\r\nupdateStr: \"\\u78BA\\u5B9A\",\r\ntimeStr: \"\\u6642\\u9593\",\r\nquickStr: \"\\u5FEB\\u901F\\u9078\\u64C7\",\r\nerr_1: '\\u6700\\u5C0F\\u65E5\\u671F\\u4E0D\\u80FD\\u5927\\u65BC\\u6700\\u5927\\u65E5\\u671F!'\r\n}"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/js/My97DatePicker/skin/WdatePicker.css",
    "content": ".Wdate{\r\n\tborder:#999 1px solid;\r\n\theight:20px;\r\n\tbackground:#fff url(datePicker.gif) no-repeat right;\r\n}\r\n\r\n.WdateFmtErr{\r\n\tfont-weight:bold;\r\n\tcolor:red;\r\n}"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/js/My97DatePicker/skin/default/datepicker.css",
    "content": "/* \r\n * My97 DatePicker 4.7\r\n */\r\n\r\n.WdateDiv{\r\n\twidth:180px;\r\n\tbackground-color:#FFFFFF;\r\n\tborder:#bbb 1px solid;\r\n\tpadding:2px;\r\n}\r\n\r\n.WdateDiv2{\r\n\twidth:360px;\r\n}\r\n.WdateDiv *{font-size:9pt;}\r\n\r\n.WdateDiv .NavImg a{\r\n\tdisplay:block;\r\n\tcursor:pointer;\r\n\theight:16px;\r\n\twidth:16px;\r\n}\r\n\r\n.WdateDiv .NavImgll a{\r\n\tfloat:left;\r\n\tbackground:transparent url(img.gif) no-repeat scroll 0 0;\r\n}\r\n.WdateDiv .NavImgl a{\r\n\tfloat:left;\r\n\tbackground:transparent url(img.gif) no-repeat scroll -16px 0;\r\n}\r\n.WdateDiv .NavImgr a{\r\n\tfloat:right;\r\n\tbackground:transparent url(img.gif) no-repeat scroll -32px 0;\r\n}\r\n.WdateDiv .NavImgrr a{\r\n\tfloat:right;\r\n\tbackground:transparent url(img.gif) no-repeat scroll -48px 0;\r\n}\r\n\r\n.WdateDiv #dpTitle{\r\n\theight:24px;\r\n\tmargin-bottom:2px;\r\n\tpadding:1px;\r\n}\r\n\r\n.WdateDiv .yminput{\r\n\tmargin-top:2px;\r\n\ttext-align:center;\r\n\theight:20px;\r\n\tborder:0px;\r\n\twidth:50px;\r\n\tcursor:pointer;\t\t\r\n}\r\n\r\n.WdateDiv .yminputfocus{\r\n\tmargin-top:2px;\r\n\ttext-align:center;\r\n\tfont-weight:bold;\r\n\theight:20px;\r\n\tcolor:blue;\r\n\tborder:#ccc 1px solid;\r\n\twidth:50px;\r\n}\r\n\r\n.WdateDiv .menuSel{\r\n\tz-index:1;\r\n\tposition:absolute;\r\n\tbackground-color:#FFFFFF;\t\r\n\tborder:#ccc 1px solid;\r\n\tdisplay:none;\r\n}\r\n\r\n.WdateDiv .menu{\r\n\tcursor:pointer;\r\n\tbackground-color:#fff;\r\n}\r\n\r\n.WdateDiv .menuOn{\r\n\tcursor:pointer;\r\n\tbackground-color:#BEEBEE;\r\n}\r\n\r\n.WdateDiv .invalidMenu{\r\n\tcolor:#aaa;\r\n}\r\n\r\n.WdateDiv .YMenu{\r\n\tmargin-top:20px;\r\n\t\r\n}\r\n\r\n.WdateDiv .MMenu{\r\n\tmargin-top:20px;\r\n\t*width:62px;\r\n}\r\n\r\n.WdateDiv .hhMenu{\r\n\tmargin-top:-90px; \r\n\tmargin-left:26px;\r\n}\r\n\r\n.WdateDiv .mmMenu{\r\n\tmargin-top:-46px; \r\n\tmargin-left:26px;\r\n}\r\n\r\n.WdateDiv .ssMenu{\r\n\tmargin-top:-24px; \r\n\tmargin-left:26px;\r\n}\r\n\r\n .WdateDiv .Wweek {\r\n \ttext-align:center;\r\n\tbackground:#DAF3F5;\r\n\tborder-right:#BDEBEE 1px solid;\r\n }\r\n\r\n.WdateDiv .MTitle{\r\n\tbackground-color:#BDEBEE;\r\n}\r\n.WdateDiv .WdayTable2{\r\n\tborder-collapse:collapse;\r\n\tborder:#c5d9e8 1px solid;\r\n}\r\n.WdateDiv .WdayTable2 table{\r\n\tborder:0;\r\n}\r\n\r\n.WdateDiv .WdayTable{\r\n\tline-height:20px;\r\n\tborder:#c5d9e8 1px solid;\r\n}\r\n.WdateDiv .WdayTable td{\r\n\ttext-align:center;\r\n}\r\n\r\n.WdateDiv .Wday{\r\n\tcursor:pointer;\r\n}\r\n\r\n.WdateDiv .WdayOn{\r\n\tcursor:pointer;\r\n\tbackground-color:#C0EBEF;\r\n}\r\n\r\n.WdateDiv .Wwday{\r\n\tcursor:pointer;\r\n\tcolor:#FF2F2F;\r\n}\r\n\r\n.WdateDiv .WwdayOn{\r\n\tcursor:pointer;\r\n\tcolor:#000;\r\n\tbackground-color:#C0EBEF;\r\n}\r\n.WdateDiv .Wtoday{\r\n\tcursor:pointer;\r\n\tcolor:blue;\r\n}\r\n.WdateDiv .Wselday{\r\n\tbackground-color:#A9E4E9;\r\n}\r\n.WdateDiv .WspecialDay{\r\n\tbackground-color:#66F4DF;\r\n}\r\n\r\n.WdateDiv .WotherDay{ \r\n\tcursor:pointer;\r\n\tcolor:#6A6AFF;\t\r\n}\r\n\r\n.WdateDiv .WotherDayOn{ \r\n\tcursor:pointer;\r\n\tbackground-color:#C0EBEF;\t\r\n}\r\n\r\n.WdateDiv .WinvalidDay{\r\n\tcolor:#aaa;\r\n}\r\n\r\n.WdateDiv #dpTime{\r\n\tfloat:left;\r\n\tmargin-top:3px;\r\n\tmargin-right:30px;\r\n}\r\n\r\n.WdateDiv #dpTime #dpTimeStr{\r\n\tmargin-left:1px;\r\n}\r\n\r\n.WdateDiv #dpTime input{\r\n\twidth:18px;\r\n\theight:20px;\r\n\ttext-align:center;\r\n\tborder:#ccc 1px solid;\t\r\n}\r\n\r\n.WdateDiv #dpTime .tB{\r\n\tborder-right:0px;\r\n}\r\n\r\n.WdateDiv #dpTime .tE{\r\n\tborder-left:0;\r\n\tborder-right:0;\r\n}\r\n\r\n.WdateDiv #dpTime .tm{\r\n\twidth:7px;\r\n\tborder-left:0;\r\n\tborder-right:0;\r\n}\r\n\r\n.WdateDiv #dpTime #dpTimeUp{\r\n\theight:10px;\r\n\twidth:13px;\r\n\tborder:0px;\r\n\tbackground:url(img.gif) no-repeat -32px -16px;\r\n}\r\n\r\n.WdateDiv #dpTime #dpTimeDown{\r\n\theight:10px;\r\n\twidth:13px;\r\n\tborder:0px;\r\n    background:url(img.gif) no-repeat -48px -16px;\r\n}\r\n\r\n .WdateDiv #dpQS {\r\n \tfloat:left;\r\n\tmargin-right:3px;\r\n\tmargin-top:3px;\r\n\tbackground:url(img.gif) no-repeat 0px -16px;\r\n\twidth:20px;\r\n\theight:20px;\r\n\tcursor:pointer;\r\n }\r\n.WdateDiv #dpControl {\r\n\ttext-align:right;\t\r\n}\r\n.WdateDiv .dpButton{ \r\n\theight:20px;\r\n\twidth:45px;\r\n\tborder:#ccc 1px solid;\r\n\tmargin-top:2px;\r\n\tmargin-right:1px;\r\n}"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/js/My97DatePicker/skin/whyGreen/datepicker.css",
    "content": "/* \r\n * My97 DatePicker 4.7 Skin:whyGreen\r\n */ \r\n.WdateDiv{\r\n\twidth:180px;\r\n\tbackground-color:#fff;\r\n\tborder:#C5E1E4 1px solid;\r\n\tpadding:2px;\r\n}\r\n\r\n.WdateDiv2{\r\n\twidth:360px;\r\n}\r\n.WdateDiv *{font-size:9pt;}\r\n\r\n.WdateDiv .NavImg a{\r\n\tcursor:pointer;\r\n\tdisplay:block;\r\n\twidth:16px;\r\n\theight:16px;\r\n\tmargin-top:1px;\r\n}\r\n\r\n.WdateDiv .NavImgll a{\r\n\tfloat:left;\r\n\tbackground:url(img.gif) no-repeat;\r\n}\r\n.WdateDiv .NavImgl a{\r\n\tfloat:left;\r\n\tbackground:url(img.gif) no-repeat -16px 0px;\r\n}\r\n.WdateDiv .NavImgr a{\r\n\tfloat:right;\r\n\tbackground:url(img.gif) no-repeat -32px 0px;\r\n}\r\n.WdateDiv .NavImgrr a{\r\n\tfloat:right;\r\n\tbackground:url(img.gif) no-repeat -48px 0px;\r\n}\r\n\r\n.WdateDiv #dpTitle{\r\n\theight:24px;\r\n\tpadding:1px;\r\n\tborder:#c5d9e8 1px solid;\r\n\tbackground:url(bg.jpg);\r\n\tmargin-bottom:2px;\r\n}\r\n\r\n.WdateDiv .yminput{\r\n\tmargin-top:2px;\r\n\ttext-align:center;\r\n\tborder:0px;\r\n\theight:20px;\r\n\twidth:50px;\r\n\tcolor:#034c50;\r\n\tbackground-color:transparent;\r\n\tcursor:pointer;\r\n}\r\n\r\n.WdateDiv .yminputfocus{\r\n\tmargin-top:2px;\r\n\ttext-align:center;\r\n\tborder:#939393 1px solid;\r\n\tfont-weight:bold;\r\n\tcolor:#034c50;\t\r\n\theight:20px;\r\n\twidth:50px;\r\n}\r\n\r\n.WdateDiv .menuSel{\r\n\tz-index:1;\r\n\tposition:absolute;\r\n\tbackground-color:#FFFFFF;\r\n\tborder:#A3C6C8 1px solid;\r\n\tdisplay:none;\r\n}\r\n\r\n.WdateDiv .menu{\r\n\tcursor:pointer;\r\n\tbackground-color:#fff;\r\n\tcolor:#11777C;\r\n}\r\n\r\n.WdateDiv .menuOn{\r\n\tcursor:pointer;\r\n\tbackground-color:#BEEBEE;\r\n}\r\n\r\n.WdateDiv .invalidMenu{\r\n\tcolor:#aaa;\r\n}\r\n\r\n.WdateDiv .YMenu{\r\n\tmargin-top:20px;\r\n}\r\n\r\n.WdateDiv .MMenu{\r\n\tmargin-top:20px;\r\n\t*width:62px;\r\n}\r\n\r\n.WdateDiv .hhMenu{\r\n\tmargin-top:-90px; \r\n\tmargin-left:26px;\r\n}\r\n\r\n.WdateDiv .mmMenu{\r\n\tmargin-top:-46px; \r\n\tmargin-left:26px;\r\n}\r\n\r\n.WdateDiv .ssMenu{\r\n\tmargin-top:-24px; \r\n\tmargin-left:26px;\r\n}\r\n\r\n .WdateDiv .Wweek {\r\n \ttext-align:center;\r\n\tbackground:#DAF3F5;\r\n\tborder-right:#BDEBEE 1px solid;\r\n }\r\n\r\n.WdateDiv .MTitle{\r\n\tcolor:#13777e;\r\n\tbackground-color:#bdebee;\r\n}\r\n.WdateDiv .WdayTable2{\r\n\tborder-collapse:collapse;\r\n\tborder:#BEE9F0 1px solid;\r\n}\r\n.WdateDiv .WdayTable2 table{\r\n\tborder:0;\r\n}\r\n\r\n.WdateDiv .WdayTable{\r\n\tline-height:20px;\t\r\n\tcolor:#13777e;\r\n\tbackground-color:#edfbfb;\r\n\tborder:#BEE9F0 1px solid;\r\n}\r\n.WdateDiv .WdayTable td{\r\n\ttext-align:center;\r\n}\r\n\r\n.WdateDiv .Wday{\r\n\tcursor:pointer;\r\n}\r\n\r\n.WdateDiv .WdayOn{\r\n\tcursor:pointer;\r\n\tbackground-color:#74d2d9 ;\r\n}\r\n\r\n.WdateDiv .Wwday{\r\n\tcursor:pointer;\r\n\tcolor:#ab1e1e;\r\n}\r\n\r\n.WdateDiv .WwdayOn{\r\n\tcursor:pointer;\r\n\tbackground-color:#74d2d9;\r\n}\r\n.WdateDiv .Wtoday{\r\n\tcursor:pointer;\r\n\tcolor:blue;\r\n}\r\n.WdateDiv .Wselday{\r\n\tbackground-color:#A7E2E7;\r\n}\r\n.WdateDiv .WspecialDay{\r\n\tbackground-color:#66F4DF;\r\n}\r\n\r\n.WdateDiv .WotherDay{ \r\n\tcursor:pointer;\r\n\tcolor:#0099CC;\t\r\n}\r\n\r\n.WdateDiv .WotherDayOn{ \r\n\tcursor:pointer;\r\n\tbackground-color:#C0EBEF;\t\r\n}\r\n\r\n.WdateDiv .WinvalidDay{\r\n\tcolor:#aaa;\r\n}\r\n\r\n.WdateDiv #dpTime{\r\n\tfloat:left;\r\n\tmargin-top:3px;\r\n\tmargin-right:30px;\r\n}\r\n\r\n.WdateDiv #dpTime #dpTimeStr{\r\n\tmargin-left:1px;\r\n\tcolor:#497F7F;\r\n}\r\n\r\n.WdateDiv #dpTime input{\r\n\theight:20px;\r\n\twidth:18px;\r\n\ttext-align:center;\r\n\tcolor:#333;\r\n\tborder:#61CAD0 1px solid;\t\r\n}\r\n\r\n.WdateDiv #dpTime .tB{\r\n\tborder-right:0px;\r\n}\r\n\r\n.WdateDiv #dpTime .tE{\r\n\tborder-left:0;\r\n\tborder-right:0;\r\n}\r\n\r\n.WdateDiv #dpTime .tm{\r\n\twidth:7px;\r\n\tborder-left:0;\r\n\tborder-right:0;\r\n}\r\n\r\n.WdateDiv #dpTime #dpTimeUp{\r\n\theight:10px;\r\n\twidth:13px;\r\n\tborder:0px;\r\n\tbackground:url(img.gif) no-repeat -32px -16px;\r\n}\r\n\r\n.WdateDiv #dpTime #dpTimeDown{\r\n\theight:10px;\r\n\twidth:13px;\r\n\tborder:0px;\r\n\tbackground:url(img.gif) no-repeat -48px -16px;\r\n}\r\n\r\n .WdateDiv #dpQS {\r\n \tfloat:left;\r\n\tmargin-right:3px;\r\n\tmargin-top:3px;\r\n\tbackground:url(img.gif) no-repeat 0px -16px;\r\n\twidth:20px;\r\n\theight:20px;\r\n\tcursor:pointer;\r\n }\r\n.WdateDiv #dpControl {\r\n\ttext-align:right;\r\n\tmargin-top:3px;\r\n}\r\n.WdateDiv .dpButton{ \r\n\theight:20px;\r\n\twidth:45px;\r\n\tmargin-top:2px;\r\n\tborder:#38B1B9 1px solid;\r\n\tbackground-color:#CFEBEE;\r\n\tcolor:#08575B;\r\n}"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/js/analysisStatus.js",
    "content": " \r\n\r\nfunction strFormat(s) {\r\n    var flag;\r\n    var arr = s.split(\".\");\r\n    var l = arr[0].split(\"\").reverse();\r\n    var t = \"\"\r\n    for (i = 0; i < l.length; i++) {\r\n        t += l[i] + ((i + 1) % 3 == 0 && (i + 1) != l.length ? \",\" : \"\");\r\n    }\r\n    if (arr.length == 2) return t.split(\"\").reverse().join(\"\") + \".\" + arr[1];\r\n    else return t.split(\"\").reverse().join(\"\");\r\n} \r\n\r\nfunction sizeFormat(val, axis) { \r\n    if (val > 1073741824) \r\n      return (val / 1073741824).toFixed(axis.tickDecimals) + \" TB\"; \r\n    else if (val > 1048576) \r\n    \treturn (val / 1048576).toFixed(axis.tickDecimals) + \" GB\"; \r\n    else if (val > 1024)\r\n    \treturn (val / 1024).toFixed(axis.tickDecimals) + \" MB\"; \r\n    else \r\n      return val.toFixed(axis.tickDecimals) + \" KB\"; \r\n}\r\n\r\nfunction delayFormat(val, axis) { \r\n\tif (val > 86400000)\r\n\t\treturn (val / 86400000).toFixed(axis.tickDecimals) + \" d\"; \r\n\telse if (val > 3600000) \r\n      return (val / 3600000).toFixed(axis.tickDecimals) + \" h\"; \r\n    else if (val > 60000) \r\n    \treturn (val / 60000).toFixed(axis.tickDecimals) + \" m\"; \r\n    else if (val > 1000)\r\n    \treturn (val / 1000).toFixed(axis.tickDecimals) + \" s\"; \r\n    else \r\n      return val.toFixed(axis.tickDecimals) + \" ms\"; \r\n}\r\n    \r\n    function drawDelayGraph(id,flotData) {\r\n        var delayOptions = {\r\n\t\tlegend: {\r\n                        show: true,\r\n                        position:\"nw\",\r\n                },\r\n        series: {\r\n                    lines: { show: true },\r\n                    points: { show: true }\r\n                },\r\n          grid: { hoverable: true, clickable: false },\r\n                //colors:[\"#edc240\",\"#cb4b4b\",\"#4da74d\",\"#9440ed\"],\r\n            xaxis: {\r\n                tickFormatter: function(val, axis) {\r\n                    return new Date(val).toTimeString().split(' ')[0];\r\n                }\r\n            },\r\n            yaxis: {\r\n                tickFormatter: function(val, axis) {\r\n//                \treturn strFormat(val.toFixed(axis.tickDecimals));\r\n                \treturn delayFormat(val, axis);\r\n                }\r\n            }\r\n        };\r\n        if (($(id).width() !== 0) && ($(id).height() !== 0)) {\r\n            $.plot($(id), [{\r\n                data: flotData\r\n            }], delayOptions);\r\n        }\r\n    }\r\n\r\n\r\n    function createDelayStatFlash(flotTime) {\r\n        if (($(\"#delayTime\").width() !== 0) && ($(\"#delayTime\").height() !== 0)) {\r\n            $.plot($(\"#delayTime\"), [{\r\n                data: [[0, 0]]\r\n            }]);\r\n        } \r\n        drawDelayGraph(\"#delayTime\",flotTime);\r\n    }\r\n    \r\n    function doPlot(position,flotNumber,flotTime) {\r\n        $.plot($(\"#placeholder\"),\r\n           [ { data: flotTime, label: \"延迟时间 (秒)\" }],\r\n           { \r\n               xaxes: [ { \r\n                   tickFormatter: function(val, axis) {\r\n                      return new Date(val).toTimeString().split(' ')[0]; }} ],\r\n               yaxes: [ \r\n                        { \r\n                            tickFormatter: function(val, axis) {\r\n                               return val.toFixed(axis.tickDecimals) + '';}\r\n                        },\r\n                        {\r\n                          // align if we are to the right\r\n                          alignTicksWithAxis: position == \"right\" ? 1 : null,\r\n                          position: position,\r\n                          tickFormatter: function(val, axis) {\r\n                              return val.toFixed(axis.tickDecimals);\r\n                          }\r\n                        } ],\r\n\t\t       legend: {\r\n                        show: true,\r\n                        position:\"nw\",\r\n                       },\r\n               series: {\r\n                       lines: { show: true },\r\n                       points: { show: true }\r\n                       },\r\n                 grid: { hoverable: true, clickable: false },\r\n                       });\r\n    }\r\n    \r\n    \r\n    function createDelayStat(flotTime) {\r\n        if (($(\"#placeholder\").width() !== 0) && ($(\"#placeholder\").height() !== 0)) {\r\n            $.plot($(\"#placeholder\"),\r\n                    [ { data: [[0, 0]], label: \"延迟时间 (秒)\" }]);\r\n        }\r\n    \tdoPlot(\"right\",flotTime);\r\n    }\r\n    \r\n    function doPlot(id,position,flotNumber,flotSize) {\r\n        $.plot($(id),\r\n           [ { data: flotNumber, label: \"number （条）\" },\r\n             { data: flotSize, label: \"size (KB)\", yaxis: 2 }],\r\n           { \r\n               xaxes: [ { \r\n                   tickFormatter: function(val, axis) {\r\n                      return new Date(val).toTimeString().split(' ')[0]; }} ],\r\n               yaxes: [ \r\n                        { \r\n                            tickFormatter: function(val, axis) {\r\n                               return strFormat(val.toFixed(axis.tickDecimals));}\r\n                        },\r\n                        {\r\n                          // align if we are to the right\r\n                          alignTicksWithAxis: position == \"right\" ? 1 : null,\r\n                          position: position,\r\n                          tickFormatter: function(val, axis) {\r\n                              return strFormat(val.toFixed(axis.tickDecimals));\r\n                          }\r\n                        } ],\r\n\t\t       legend: {\r\n                        show: true,\r\n                        position:\"nw\",\r\n                       },\r\n               series: {\r\n                       lines: { show: true },\r\n                       points: { show: true }\r\n                       },\r\n                 grid: { hoverable: true, clickable: false },\r\n                       });\r\n    }\r\n    \r\n    \r\n    function plotThroughput(id,flotData){\r\n        var throughputOptions = {\r\n        \t\tlegend: {\r\n                                show: true,\r\n                                position:\"nw\",\r\n                        },\r\n                series: {\r\n                            lines: { show: true },\r\n                            points: { show: true }\r\n                        },\r\n                  grid: { hoverable: true, clickable: false },\r\n                        //colors:[\"#edc240\",\"#cb4b4b\",\"#4da74d\",\"#9440ed\"],\r\n                    xaxis: {\r\n                        tickFormatter: function(val, axis) {\r\n                            return new Date(val).toTimeString().split(' ')[0];\r\n                        }\r\n                    },\r\n                    yaxis: {\r\n                        tickFormatter: function(val, axis) {\r\n                            return strFormat(val.toFixed(axis.tickDecimals));\r\n                        }\r\n                    }\r\n                };\r\n                if (($(id).width() !== 0) && ($(id).height() !== 0)) {\r\n                    $.plot($(id), [{\r\n                        data: flotData\r\n                    }], throughputOptions);\r\n                }\r\n    }\r\n    \r\n    function plotSizeThroughput(id,flotData){\r\n        var throughputOptions = {\r\n        \t\tlegend: {\r\n                                show: true,\r\n                                position:\"nw\",\r\n                        },\r\n                series: {\r\n                            lines: { show: true },\r\n                            points: { show: true }\r\n                        },\r\n                  grid: { hoverable: true, clickable: false },\r\n                        //colors:[\"#edc240\",\"#cb4b4b\",\"#4da74d\",\"#9440ed\"],\r\n                    xaxis: {\r\n                        tickFormatter: function(val, axis) {\r\n                            return new Date(val).toTimeString().split(' ')[0];\r\n                        }\r\n                    },\r\n                    yaxis: {\r\n                        tickFormatter: function(val, axis) {\r\n                            return sizeFormat(val, axis);\r\n                        }\r\n                    }\r\n                };\r\n                if (($(id).width() !== 0) && ($(id).height() !== 0)) {\r\n                    $.plot($(id), [{\r\n                        data: flotData\r\n                    }], throughputOptions);\r\n                }\r\n    }\r\n    \r\n    function createRowThroughputFlash(flotNumber,flotSize){\r\n        if (($(\"#rowNumber\").width() !== 0) && ($(\"#rowNumber\").height() !== 0)) {\r\n            $.plot($(\"#rowNumber\"), [{\r\n                label: \"number\",\r\n                data: [[0, 0]]\r\n            }]);\r\n        }\r\n        plotThroughput(\"#rowNumber\",flotNumber);\r\n        plotSizeThroughput(\"#rowSize\",flotSize);\r\n        \r\n    }\r\n    \r\n    function createFileThroughputFlash(flotNumber,flotSize){\r\n        if (($(\"#fileNumber\").width() !== 0) && ($(\"#fileNumber\").height() !== 0)) {\r\n            $.plot($(\"#fileNumber\"), [{\r\n                label: \"number\",\r\n                data: [[0, 0]]\r\n            }]);\r\n        }\r\n        plotThroughput(\"#fileNumber\",flotNumber);\r\n        plotSizeThroughput(\"#fileSize\",flotSize);\r\n    }\r\n    \r\n    function drawBehaviorGraph(id,flotData) {\r\n        var behaviorOptions = {\r\n\t\tlegend: {\r\n                        show: true,\r\n                        position:\"nw\",\r\n                },\r\n        series: {\r\n                    lines: { show: true },\r\n                    points: { show: true }\r\n                },\r\n          grid: { hoverable: true, clickable: false },\r\n                //colors:[\"#edc240\",\"#cb4b4b\",\"#4da74d\",\"#9440ed\"],\r\n            xaxis: {\r\n                tickFormatter: function(val, axis) {\r\n                    return new Date(val).toTimeString().split(' ')[0];\r\n                }\r\n            },\r\n            yaxis: {\r\n                tickFormatter: function(val, axis) {\r\n                    return strFormat(val.toFixed(axis.tickDecimals));\r\n                }\r\n            }\r\n        };\r\n        if (($(id).width() !== 0) && ($(id).height() !== 0)) {\r\n            $.plot($(id), [{\r\n                data: flotData\r\n            }], behaviorOptions);\r\n        }\r\n    }\r\n    \r\n    function createBehaviorFlash(flotNumber1,flotNumber2,flotNumber3,flotNumber4,flotNumber5) {\r\n        if (($(\"#insertNumber\").width() !== 0) && ($(\"insertNumber\").height() !== 0)) {\r\n            $.plot($(\"#insertNumber\"), [{\r\n                data: [[0, 0]]\r\n            }]);\r\n        } \r\n        \r\n        if (($(\"#updateNumber\").width() !== 0) && ($(\"#updateNumber\").height() !== 0)) {\r\n            $.plot($(\"#updateNumber\"), [{\r\n                data: [[0, 0]]\r\n            }]);\r\n        } \r\n        \r\n        if (($(\"#deleteNumber\").width() !== 0) && ($(\"#deleteNumber\").height() !== 0)) {\r\n            $.plot($(\"#deleteNumber\"), [{\r\n                data: [[0, 0]]\r\n            }]);\r\n        } \r\n        \r\n        if (($(\"#fileNumber\").width() !== 0) && ($(\"#fileNumber\").height() !== 0)) {\r\n            $.plot($(\"#fileNumber\"), [{\r\n                data: [[0, 0]]\r\n            }]);\r\n        }\r\n        \r\n        if (($(\"#fileSize\").width() !== 0) && ($(\"#fileSize\").height() !== 0)) {\r\n            $.plot($(\"#fileSize\"), [{\r\n                data: [[0, 0]]\r\n            }]);\r\n        }\r\n        \r\n        drawBehaviorGraph(\"#insertNumber\",flotNumber1);\r\n        drawBehaviorGraph(\"#updateNumber\",flotNumber2);\r\n        drawBehaviorGraph(\"#deleteNumber\",flotNumber3);\r\n        plotThroughput(\"#fileNumber\",flotNumber4);\r\n        plotSizeThroughput(\"#fileSize\",flotNumber5);\r\n    }\r\n    \r\n    function doPlot(position,flotNumber1,flotNumber2,flotNumber3,flotNumber4,flotNumber5) {\r\n        $.plot($(\"#placeholder\"),\r\n           [ { data: flotNumber1, label: \"insert （条）\" },\r\n             { data: flotNumber2, label: \"update （条）\", yaxis: 2 },\r\n             { data: flotNumber3, label: \"delete （条）\", yaxis: 3 },\r\n             { data: flotNumber3, label: \"文件数量 （条）\", yaxis: 4 },\r\n             { data: flotNumber3, label: \"文件大小 （KB）\", yaxis: 5 }],\r\n           { \r\n               xaxes: [ { \r\n                   tickFormatter: function(val, axis) {\r\n                      return new Date(val).toTimeString().split(' ')[0]; }} ],\r\n               yaxes: [ \r\n                        { \r\n                            tickFormatter: function(val, axis) {\r\n                               return val.toFixed(axis.tickDecimals) + '';}\r\n                        },\r\n                        {\r\n                          // align if we are to the right\r\n                          alignTicksWithAxis: position == \"right\" ? 1 : null,\r\n                          position: position,\r\n                          tickFormatter: function(val, axis) {\r\n                              return val.toFixed(axis.tickDecimals);\r\n                          }\r\n                        } ],\r\n\t\t       legend: {\r\n                        show: true,\r\n                        position:\"nw\",\r\n                       },\r\n               series: {\r\n                       lines: { show: true },\r\n                       points: { show: true }\r\n                       },\r\n                 grid: { hoverable: true, clickable: false },\r\n                       });\r\n    }\r\n    \r\n    \r\n    function createBehaviorHistory(flotNumber1,flotNumber2,flotNumber3,flotNumber4,flotNumber5) {\r\n        if (($(\"#placeholder\").width() !== 0) && ($(\"#placeholder\").height() !== 0)) {\r\n            $.plot($(\"#placeholder\"),\r\n                    [ { data: [[0, 0]], label: \"insert （条）\" },\r\n                      { data: [[0, 0]], label: \"update （条）\", yaxis: 2 },\r\n                      { data: [[0, 0]], label: \"delete （条）\", yaxis: 3 },\r\n                      { data: [[0, 0]], label: \"文件数量 （条）\", yaxis: 4 },\r\n                      { data: [[0, 0]], label: \"文件大小 （KB）\", yaxis: 5 }]);\r\n        }\r\n    \tdoPlot(\"right\",flotNumber1,flotNumber2,flotNumber3,flotNumber4,flotNumber5);\r\n    }\r\n    \r\n\r\n"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/js/dbCheck.js",
    "content": "function check() {  \n\tvar sourceUrl = document.getElementById('sourceUrl').value;\n    var sourceUserName = document.getElementById('sourceUserName').value;\n    var sourcePassword = document.getElementById('sourcePassword').value;\n    var sourceEncode = document.getElementById('sourceEncode').value;\n    var sourceType = document.getElementById('sourceType').value;\n    Hello.check(sourceUrl, sourceUserName, sourcePassword, sourceEncode, sourceType, callback);  \n}  \n\nfunction checkMap() {  \n    var namespace = document.getElementById('namespace').value;\n    var name = document.getElementById('name').value;\n    var dataSourceId = document.getElementById('dataSourceId').value;\n    \n    Hello.checkMap(namespace, name, dataSourceId, callback);  \n}\n  \nfunction callback(msg) {  \n    DWRUtil.setValue('result', msg);  \n}\n\nfunction checkNamespaceTables() {  \n    var namespace = document.getElementById('namespace').value;\n    var name = document.getElementById('name').value;\n    var dataSourceId = document.getElementById('dataSourceId').value;\n    \n    Hello.checkNamespaceTables(namespace, name, dataSourceId, callback2);  \n}\n  \nfunction callback2(msg) {  \n    var result = document.getElementById('result');\n    if (/.*Find schema.*/.test(msg)) {\n        result.innerHTML = \"<font style='color:color;'>\" + msg + \"</font>\";\n    } else {\n        result.innerHTML = msg;\n    }\n}\n\nfunction changeform(){\n}\n"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/js/flot/excanvas.js",
    "content": "// Copyright 2006 Google Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//   http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n\n// Known Issues:\n//\n// * Patterns only support repeat.\n// * Radial gradient are not implemented. The VML version of these look very\n//   different from the canvas one.\n// * Clipping paths are not implemented.\n// * Coordsize. The width and height attribute have higher priority than the\n//   width and height style values which isn't correct.\n// * Painting mode isn't implemented.\n// * Canvas width/height should is using content-box by default. IE in\n//   Quirks mode will draw the canvas using border-box. Either change your\n//   doctype to HTML5\n//   (http://www.whatwg.org/specs/web-apps/current-work/#the-doctype)\n//   or use Box Sizing Behavior from WebFX\n//   (http://webfx.eae.net/dhtml/boxsizing/boxsizing.html)\n// * Non uniform scaling does not correctly scale strokes.\n// * Filling very large shapes (above 5000 points) is buggy.\n// * Optimize. There is always room for speed improvements.\n\n// Only add this code if we do not already have a canvas implementation\nif (!document.createElement('canvas').getContext) {\n\n(function() {\n\n  // alias some functions to make (compiled) code shorter\n  var m = Math;\n  var mr = m.round;\n  var ms = m.sin;\n  var mc = m.cos;\n  var abs = m.abs;\n  var sqrt = m.sqrt;\n\n  // this is used for sub pixel precision\n  var Z = 10;\n  var Z2 = Z / 2;\n\n  /**\n   * This funtion is assigned to the <canvas> elements as element.getContext().\n   * @this {HTMLElement}\n   * @return {CanvasRenderingContext2D_}\n   */\n  function getContext() {\n    return this.context_ ||\n        (this.context_ = new CanvasRenderingContext2D_(this));\n  }\n\n  var slice = Array.prototype.slice;\n\n  /**\n   * Binds a function to an object. The returned function will always use the\n   * passed in {@code obj} as {@code this}.\n   *\n   * Example:\n   *\n   *   g = bind(f, obj, a, b)\n   *   g(c, d) // will do f.call(obj, a, b, c, d)\n   *\n   * @param {Function} f The function to bind the object to\n   * @param {Object} obj The object that should act as this when the function\n   *     is called\n   * @param {*} var_args Rest arguments that will be used as the initial\n   *     arguments when the function is called\n   * @return {Function} A new function that has bound this\n   */\n  function bind(f, obj, var_args) {\n    var a = slice.call(arguments, 2);\n    return function() {\n      return f.apply(obj, a.concat(slice.call(arguments)));\n    };\n  }\n\n  function encodeHtmlAttribute(s) {\n    return String(s).replace(/&/g, '&amp;').replace(/\"/g, '&quot;');\n  }\n\n  function addNamespacesAndStylesheet(doc) {\n    // create xmlns\n    if (!doc.namespaces['g_vml_']) {\n      doc.namespaces.add('g_vml_', 'urn:schemas-microsoft-com:vml',\n                         '#default#VML');\n\n    }\n    if (!doc.namespaces['g_o_']) {\n      doc.namespaces.add('g_o_', 'urn:schemas-microsoft-com:office:office',\n                         '#default#VML');\n    }\n\n    // Setup default CSS.  Only add one style sheet per document\n    if (!doc.styleSheets['ex_canvas_']) {\n      var ss = doc.createStyleSheet();\n      ss.owningElement.id = 'ex_canvas_';\n      ss.cssText = 'canvas{display:inline-block;overflow:hidden;' +\n          // default size is 300x150 in Gecko and Opera\n          'text-align:left;width:300px;height:150px}';\n    }\n  }\n\n  // Add namespaces and stylesheet at startup.\n  addNamespacesAndStylesheet(document);\n\n  var G_vmlCanvasManager_ = {\n    init: function(opt_doc) {\n      if (/MSIE/.test(navigator.userAgent) && !window.opera) {\n        var doc = opt_doc || document;\n        // Create a dummy element so that IE will allow canvas elements to be\n        // recognized.\n        doc.createElement('canvas');\n        doc.attachEvent('onreadystatechange', bind(this.init_, this, doc));\n      }\n    },\n\n    init_: function(doc) {\n      // find all canvas elements\n      var els = doc.getElementsByTagName('canvas');\n      for (var i = 0; i < els.length; i++) {\n        this.initElement(els[i]);\n      }\n    },\n\n    /**\n     * Public initializes a canvas element so that it can be used as canvas\n     * element from now on. This is called automatically before the page is\n     * loaded but if you are creating elements using createElement you need to\n     * make sure this is called on the element.\n     * @param {HTMLElement} el The canvas element to initialize.\n     * @return {HTMLElement} the element that was created.\n     */\n    initElement: function(el) {\n      if (!el.getContext) {\n        el.getContext = getContext;\n\n        // Add namespaces and stylesheet to document of the element.\n        addNamespacesAndStylesheet(el.ownerDocument);\n\n        // Remove fallback content. There is no way to hide text nodes so we\n        // just remove all childNodes. We could hide all elements and remove\n        // text nodes but who really cares about the fallback content.\n        el.innerHTML = '';\n\n        // do not use inline function because that will leak memory\n        el.attachEvent('onpropertychange', onPropertyChange);\n        el.attachEvent('onresize', onResize);\n\n        var attrs = el.attributes;\n        if (attrs.width && attrs.width.specified) {\n          // TODO: use runtimeStyle and coordsize\n          // el.getContext().setWidth_(attrs.width.nodeValue);\n          el.style.width = attrs.width.nodeValue + 'px';\n        } else {\n          el.width = el.clientWidth;\n        }\n        if (attrs.height && attrs.height.specified) {\n          // TODO: use runtimeStyle and coordsize\n          // el.getContext().setHeight_(attrs.height.nodeValue);\n          el.style.height = attrs.height.nodeValue + 'px';\n        } else {\n          el.height = el.clientHeight;\n        }\n        //el.getContext().setCoordsize_()\n      }\n      return el;\n    }\n  };\n\n  function onPropertyChange(e) {\n    var el = e.srcElement;\n\n    switch (e.propertyName) {\n      case 'width':\n        el.getContext().clearRect();\n        el.style.width = el.attributes.width.nodeValue + 'px';\n        // In IE8 this does not trigger onresize.\n        el.firstChild.style.width =  el.clientWidth + 'px';\n        break;\n      case 'height':\n        el.getContext().clearRect();\n        el.style.height = el.attributes.height.nodeValue + 'px';\n        el.firstChild.style.height = el.clientHeight + 'px';\n        break;\n    }\n  }\n\n  function onResize(e) {\n    var el = e.srcElement;\n    if (el.firstChild) {\n      el.firstChild.style.width =  el.clientWidth + 'px';\n      el.firstChild.style.height = el.clientHeight + 'px';\n    }\n  }\n\n  G_vmlCanvasManager_.init();\n\n  // precompute \"00\" to \"FF\"\n  var decToHex = [];\n  for (var i = 0; i < 16; i++) {\n    for (var j = 0; j < 16; j++) {\n      decToHex[i * 16 + j] = i.toString(16) + j.toString(16);\n    }\n  }\n\n  function createMatrixIdentity() {\n    return [\n      [1, 0, 0],\n      [0, 1, 0],\n      [0, 0, 1]\n    ];\n  }\n\n  function matrixMultiply(m1, m2) {\n    var result = createMatrixIdentity();\n\n    for (var x = 0; x < 3; x++) {\n      for (var y = 0; y < 3; y++) {\n        var sum = 0;\n\n        for (var z = 0; z < 3; z++) {\n          sum += m1[x][z] * m2[z][y];\n        }\n\n        result[x][y] = sum;\n      }\n    }\n    return result;\n  }\n\n  function copyState(o1, o2) {\n    o2.fillStyle     = o1.fillStyle;\n    o2.lineCap       = o1.lineCap;\n    o2.lineJoin      = o1.lineJoin;\n    o2.lineWidth     = o1.lineWidth;\n    o2.miterLimit    = o1.miterLimit;\n    o2.shadowBlur    = o1.shadowBlur;\n    o2.shadowColor   = o1.shadowColor;\n    o2.shadowOffsetX = o1.shadowOffsetX;\n    o2.shadowOffsetY = o1.shadowOffsetY;\n    o2.strokeStyle   = o1.strokeStyle;\n    o2.globalAlpha   = o1.globalAlpha;\n    o2.font          = o1.font;\n    o2.textAlign     = o1.textAlign;\n    o2.textBaseline  = o1.textBaseline;\n    o2.arcScaleX_    = o1.arcScaleX_;\n    o2.arcScaleY_    = o1.arcScaleY_;\n    o2.lineScale_    = o1.lineScale_;\n  }\n\n  var colorData = {\n    aliceblue: '#F0F8FF',\n    antiquewhite: '#FAEBD7',\n    aquamarine: '#7FFFD4',\n    azure: '#F0FFFF',\n    beige: '#F5F5DC',\n    bisque: '#FFE4C4',\n    black: '#000000',\n    blanchedalmond: '#FFEBCD',\n    blueviolet: '#8A2BE2',\n    brown: '#A52A2A',\n    burlywood: '#DEB887',\n    cadetblue: '#5F9EA0',\n    chartreuse: '#7FFF00',\n    chocolate: '#D2691E',\n    coral: '#FF7F50',\n    cornflowerblue: '#6495ED',\n    cornsilk: '#FFF8DC',\n    crimson: '#DC143C',\n    cyan: '#00FFFF',\n    darkblue: '#00008B',\n    darkcyan: '#008B8B',\n    darkgoldenrod: '#B8860B',\n    darkgray: '#A9A9A9',\n    darkgreen: '#006400',\n    darkgrey: '#A9A9A9',\n    darkkhaki: '#BDB76B',\n    darkmagenta: '#8B008B',\n    darkolivegreen: '#556B2F',\n    darkorange: '#FF8C00',\n    darkorchid: '#9932CC',\n    darkred: '#8B0000',\n    darksalmon: '#E9967A',\n    darkseagreen: '#8FBC8F',\n    darkslateblue: '#483D8B',\n    darkslategray: '#2F4F4F',\n    darkslategrey: '#2F4F4F',\n    darkturquoise: '#00CED1',\n    darkviolet: '#9400D3',\n    deeppink: '#FF1493',\n    deepskyblue: '#00BFFF',\n    dimgray: '#696969',\n    dimgrey: '#696969',\n    dodgerblue: '#1E90FF',\n    firebrick: '#B22222',\n    floralwhite: '#FFFAF0',\n    forestgreen: '#228B22',\n    gainsboro: '#DCDCDC',\n    ghostwhite: '#F8F8FF',\n    gold: '#FFD700',\n    goldenrod: '#DAA520',\n    grey: '#808080',\n    greenyellow: '#ADFF2F',\n    honeydew: '#F0FFF0',\n    hotpink: '#FF69B4',\n    indianred: '#CD5C5C',\n    indigo: '#4B0082',\n    ivory: '#FFFFF0',\n    khaki: '#F0E68C',\n    lavender: '#E6E6FA',\n    lavenderblush: '#FFF0F5',\n    lawngreen: '#7CFC00',\n    lemonchiffon: '#FFFACD',\n    lightblue: '#ADD8E6',\n    lightcoral: '#F08080',\n    lightcyan: '#E0FFFF',\n    lightgoldenrodyellow: '#FAFAD2',\n    lightgreen: '#90EE90',\n    lightgrey: '#D3D3D3',\n    lightpink: '#FFB6C1',\n    lightsalmon: '#FFA07A',\n    lightseagreen: '#20B2AA',\n    lightskyblue: '#87CEFA',\n    lightslategray: '#778899',\n    lightslategrey: '#778899',\n    lightsteelblue: '#B0C4DE',\n    lightyellow: '#FFFFE0',\n    limegreen: '#32CD32',\n    linen: '#FAF0E6',\n    magenta: '#FF00FF',\n    mediumaquamarine: '#66CDAA',\n    mediumblue: '#0000CD',\n    mediumorchid: '#BA55D3',\n    mediumpurple: '#9370DB',\n    mediumseagreen: '#3CB371',\n    mediumslateblue: '#7B68EE',\n    mediumspringgreen: '#00FA9A',\n    mediumturquoise: '#48D1CC',\n    mediumvioletred: '#C71585',\n    midnightblue: '#191970',\n    mintcream: '#F5FFFA',\n    mistyrose: '#FFE4E1',\n    moccasin: '#FFE4B5',\n    navajowhite: '#FFDEAD',\n    oldlace: '#FDF5E6',\n    olivedrab: '#6B8E23',\n    orange: '#FFA500',\n    orangered: '#FF4500',\n    orchid: '#DA70D6',\n    palegoldenrod: '#EEE8AA',\n    palegreen: '#98FB98',\n    paleturquoise: '#AFEEEE',\n    palevioletred: '#DB7093',\n    papayawhip: '#FFEFD5',\n    peachpuff: '#FFDAB9',\n    peru: '#CD853F',\n    pink: '#FFC0CB',\n    plum: '#DDA0DD',\n    powderblue: '#B0E0E6',\n    rosybrown: '#BC8F8F',\n    royalblue: '#4169E1',\n    saddlebrown: '#8B4513',\n    salmon: '#FA8072',\n    sandybrown: '#F4A460',\n    seagreen: '#2E8B57',\n    seashell: '#FFF5EE',\n    sienna: '#A0522D',\n    skyblue: '#87CEEB',\n    slateblue: '#6A5ACD',\n    slategray: '#708090',\n    slategrey: '#708090',\n    snow: '#FFFAFA',\n    springgreen: '#00FF7F',\n    steelblue: '#4682B4',\n    tan: '#D2B48C',\n    thistle: '#D8BFD8',\n    tomato: '#FF6347',\n    turquoise: '#40E0D0',\n    violet: '#EE82EE',\n    wheat: '#F5DEB3',\n    whitesmoke: '#F5F5F5',\n    yellowgreen: '#9ACD32'\n  };\n\n\n  function getRgbHslContent(styleString) {\n    var start = styleString.indexOf('(', 3);\n    var end = styleString.indexOf(')', start + 1);\n    var parts = styleString.substring(start + 1, end).split(',');\n    // add alpha if needed\n    if (parts.length == 4 && styleString.substr(3, 1) == 'a') {\n      alpha = Number(parts[3]);\n    } else {\n      parts[3] = 1;\n    }\n    return parts;\n  }\n\n  function percent(s) {\n    return parseFloat(s) / 100;\n  }\n\n  function clamp(v, min, max) {\n    return Math.min(max, Math.max(min, v));\n  }\n\n  function hslToRgb(parts){\n    var r, g, b;\n    h = parseFloat(parts[0]) / 360 % 360;\n    if (h < 0)\n      h++;\n    s = clamp(percent(parts[1]), 0, 1);\n    l = clamp(percent(parts[2]), 0, 1);\n    if (s == 0) {\n      r = g = b = l; // achromatic\n    } else {\n      var q = l < 0.5 ? l * (1 + s) : l + s - l * s;\n      var p = 2 * l - q;\n      r = hueToRgb(p, q, h + 1 / 3);\n      g = hueToRgb(p, q, h);\n      b = hueToRgb(p, q, h - 1 / 3);\n    }\n\n    return '#' + decToHex[Math.floor(r * 255)] +\n        decToHex[Math.floor(g * 255)] +\n        decToHex[Math.floor(b * 255)];\n  }\n\n  function hueToRgb(m1, m2, h) {\n    if (h < 0)\n      h++;\n    if (h > 1)\n      h--;\n\n    if (6 * h < 1)\n      return m1 + (m2 - m1) * 6 * h;\n    else if (2 * h < 1)\n      return m2;\n    else if (3 * h < 2)\n      return m1 + (m2 - m1) * (2 / 3 - h) * 6;\n    else\n      return m1;\n  }\n\n  function processStyle(styleString) {\n    var str, alpha = 1;\n\n    styleString = String(styleString);\n    if (styleString.charAt(0) == '#') {\n      str = styleString;\n    } else if (/^rgb/.test(styleString)) {\n      var parts = getRgbHslContent(styleString);\n      var str = '#', n;\n      for (var i = 0; i < 3; i++) {\n        if (parts[i].indexOf('%') != -1) {\n          n = Math.floor(percent(parts[i]) * 255);\n        } else {\n          n = Number(parts[i]);\n        }\n        str += decToHex[clamp(n, 0, 255)];\n      }\n      alpha = parts[3];\n    } else if (/^hsl/.test(styleString)) {\n      var parts = getRgbHslContent(styleString);\n      str = hslToRgb(parts);\n      alpha = parts[3];\n    } else {\n      str = colorData[styleString] || styleString;\n    }\n    return {color: str, alpha: alpha};\n  }\n\n  var DEFAULT_STYLE = {\n    style: 'normal',\n    variant: 'normal',\n    weight: 'normal',\n    size: 10,\n    family: 'sans-serif'\n  };\n\n  // Internal text style cache\n  var fontStyleCache = {};\n\n  function processFontStyle(styleString) {\n    if (fontStyleCache[styleString]) {\n      return fontStyleCache[styleString];\n    }\n\n    var el = document.createElement('div');\n    var style = el.style;\n    try {\n      style.font = styleString;\n    } catch (ex) {\n      // Ignore failures to set to invalid font.\n    }\n\n    return fontStyleCache[styleString] = {\n      style: style.fontStyle || DEFAULT_STYLE.style,\n      variant: style.fontVariant || DEFAULT_STYLE.variant,\n      weight: style.fontWeight || DEFAULT_STYLE.weight,\n      size: style.fontSize || DEFAULT_STYLE.size,\n      family: style.fontFamily || DEFAULT_STYLE.family\n    };\n  }\n\n  function getComputedStyle(style, element) {\n    var computedStyle = {};\n\n    for (var p in style) {\n      computedStyle[p] = style[p];\n    }\n\n    // Compute the size\n    var canvasFontSize = parseFloat(element.currentStyle.fontSize),\n        fontSize = parseFloat(style.size);\n\n    if (typeof style.size == 'number') {\n      computedStyle.size = style.size;\n    } else if (style.size.indexOf('px') != -1) {\n      computedStyle.size = fontSize;\n    } else if (style.size.indexOf('em') != -1) {\n      computedStyle.size = canvasFontSize * fontSize;\n    } else if(style.size.indexOf('%') != -1) {\n      computedStyle.size = (canvasFontSize / 100) * fontSize;\n    } else if (style.size.indexOf('pt') != -1) {\n      computedStyle.size = fontSize / .75;\n    } else {\n      computedStyle.size = canvasFontSize;\n    }\n\n    // Different scaling between normal text and VML text. This was found using\n    // trial and error to get the same size as non VML text.\n    computedStyle.size *= 0.981;\n\n    return computedStyle;\n  }\n\n  function buildStyle(style) {\n    return style.style + ' ' + style.variant + ' ' + style.weight + ' ' +\n        style.size + 'px ' + style.family;\n  }\n\n  function processLineCap(lineCap) {\n    switch (lineCap) {\n      case 'butt':\n        return 'flat';\n      case 'round':\n        return 'round';\n      case 'square':\n      default:\n        return 'square';\n    }\n  }\n\n  /**\n   * This class implements CanvasRenderingContext2D interface as described by\n   * the WHATWG.\n   * @param {HTMLElement} surfaceElement The element that the 2D context should\n   * be associated with\n   */\n  function CanvasRenderingContext2D_(surfaceElement) {\n    this.m_ = createMatrixIdentity();\n\n    this.mStack_ = [];\n    this.aStack_ = [];\n    this.currentPath_ = [];\n\n    // Canvas context properties\n    this.strokeStyle = '#000';\n    this.fillStyle = '#000';\n\n    this.lineWidth = 1;\n    this.lineJoin = 'miter';\n    this.lineCap = 'butt';\n    this.miterLimit = Z * 1;\n    this.globalAlpha = 1;\n    this.font = '10px sans-serif';\n    this.textAlign = 'left';\n    this.textBaseline = 'alphabetic';\n    this.canvas = surfaceElement;\n\n    var el = surfaceElement.ownerDocument.createElement('div');\n    el.style.width =  surfaceElement.clientWidth + 'px';\n    el.style.height = surfaceElement.clientHeight + 'px';\n    el.style.overflow = 'hidden';\n    el.style.position = 'absolute';\n    surfaceElement.appendChild(el);\n\n    this.element_ = el;\n    this.arcScaleX_ = 1;\n    this.arcScaleY_ = 1;\n    this.lineScale_ = 1;\n  }\n\n  var contextPrototype = CanvasRenderingContext2D_.prototype;\n  contextPrototype.clearRect = function() {\n    if (this.textMeasureEl_) {\n      this.textMeasureEl_.removeNode(true);\n      this.textMeasureEl_ = null;\n    }\n    this.element_.innerHTML = '';\n  };\n\n  contextPrototype.beginPath = function() {\n    // TODO: Branch current matrix so that save/restore has no effect\n    //       as per safari docs.\n    this.currentPath_ = [];\n  };\n\n  contextPrototype.moveTo = function(aX, aY) {\n    var p = this.getCoords_(aX, aY);\n    this.currentPath_.push({type: 'moveTo', x: p.x, y: p.y});\n    this.currentX_ = p.x;\n    this.currentY_ = p.y;\n  };\n\n  contextPrototype.lineTo = function(aX, aY) {\n    var p = this.getCoords_(aX, aY);\n    this.currentPath_.push({type: 'lineTo', x: p.x, y: p.y});\n\n    this.currentX_ = p.x;\n    this.currentY_ = p.y;\n  };\n\n  contextPrototype.bezierCurveTo = function(aCP1x, aCP1y,\n                                            aCP2x, aCP2y,\n                                            aX, aY) {\n    var p = this.getCoords_(aX, aY);\n    var cp1 = this.getCoords_(aCP1x, aCP1y);\n    var cp2 = this.getCoords_(aCP2x, aCP2y);\n    bezierCurveTo(this, cp1, cp2, p);\n  };\n\n  // Helper function that takes the already fixed cordinates.\n  function bezierCurveTo(self, cp1, cp2, p) {\n    self.currentPath_.push({\n      type: 'bezierCurveTo',\n      cp1x: cp1.x,\n      cp1y: cp1.y,\n      cp2x: cp2.x,\n      cp2y: cp2.y,\n      x: p.x,\n      y: p.y\n    });\n    self.currentX_ = p.x;\n    self.currentY_ = p.y;\n  }\n\n  contextPrototype.quadraticCurveTo = function(aCPx, aCPy, aX, aY) {\n    // the following is lifted almost directly from\n    // http://developer.mozilla.org/en/docs/Canvas_tutorial:Drawing_shapes\n\n    var cp = this.getCoords_(aCPx, aCPy);\n    var p = this.getCoords_(aX, aY);\n\n    var cp1 = {\n      x: this.currentX_ + 2.0 / 3.0 * (cp.x - this.currentX_),\n      y: this.currentY_ + 2.0 / 3.0 * (cp.y - this.currentY_)\n    };\n    var cp2 = {\n      x: cp1.x + (p.x - this.currentX_) / 3.0,\n      y: cp1.y + (p.y - this.currentY_) / 3.0\n    };\n\n    bezierCurveTo(this, cp1, cp2, p);\n  };\n\n  contextPrototype.arc = function(aX, aY, aRadius,\n                                  aStartAngle, aEndAngle, aClockwise) {\n    aRadius *= Z;\n    var arcType = aClockwise ? 'at' : 'wa';\n\n    var xStart = aX + mc(aStartAngle) * aRadius - Z2;\n    var yStart = aY + ms(aStartAngle) * aRadius - Z2;\n\n    var xEnd = aX + mc(aEndAngle) * aRadius - Z2;\n    var yEnd = aY + ms(aEndAngle) * aRadius - Z2;\n\n    // IE won't render arches drawn counter clockwise if xStart == xEnd.\n    if (xStart == xEnd && !aClockwise) {\n      xStart += 0.125; // Offset xStart by 1/80 of a pixel. Use something\n                       // that can be represented in binary\n    }\n\n    var p = this.getCoords_(aX, aY);\n    var pStart = this.getCoords_(xStart, yStart);\n    var pEnd = this.getCoords_(xEnd, yEnd);\n\n    this.currentPath_.push({type: arcType,\n                           x: p.x,\n                           y: p.y,\n                           radius: aRadius,\n                           xStart: pStart.x,\n                           yStart: pStart.y,\n                           xEnd: pEnd.x,\n                           yEnd: pEnd.y});\n\n  };\n\n  contextPrototype.rect = function(aX, aY, aWidth, aHeight) {\n    this.moveTo(aX, aY);\n    this.lineTo(aX + aWidth, aY);\n    this.lineTo(aX + aWidth, aY + aHeight);\n    this.lineTo(aX, aY + aHeight);\n    this.closePath();\n  };\n\n  contextPrototype.strokeRect = function(aX, aY, aWidth, aHeight) {\n    var oldPath = this.currentPath_;\n    this.beginPath();\n\n    this.moveTo(aX, aY);\n    this.lineTo(aX + aWidth, aY);\n    this.lineTo(aX + aWidth, aY + aHeight);\n    this.lineTo(aX, aY + aHeight);\n    this.closePath();\n    this.stroke();\n\n    this.currentPath_ = oldPath;\n  };\n\n  contextPrototype.fillRect = function(aX, aY, aWidth, aHeight) {\n    var oldPath = this.currentPath_;\n    this.beginPath();\n\n    this.moveTo(aX, aY);\n    this.lineTo(aX + aWidth, aY);\n    this.lineTo(aX + aWidth, aY + aHeight);\n    this.lineTo(aX, aY + aHeight);\n    this.closePath();\n    this.fill();\n\n    this.currentPath_ = oldPath;\n  };\n\n  contextPrototype.createLinearGradient = function(aX0, aY0, aX1, aY1) {\n    var gradient = new CanvasGradient_('gradient');\n    gradient.x0_ = aX0;\n    gradient.y0_ = aY0;\n    gradient.x1_ = aX1;\n    gradient.y1_ = aY1;\n    return gradient;\n  };\n\n  contextPrototype.createRadialGradient = function(aX0, aY0, aR0,\n                                                   aX1, aY1, aR1) {\n    var gradient = new CanvasGradient_('gradientradial');\n    gradient.x0_ = aX0;\n    gradient.y0_ = aY0;\n    gradient.r0_ = aR0;\n    gradient.x1_ = aX1;\n    gradient.y1_ = aY1;\n    gradient.r1_ = aR1;\n    return gradient;\n  };\n\n  contextPrototype.drawImage = function(image, var_args) {\n    var dx, dy, dw, dh, sx, sy, sw, sh;\n\n    // to find the original width we overide the width and height\n    var oldRuntimeWidth = image.runtimeStyle.width;\n    var oldRuntimeHeight = image.runtimeStyle.height;\n    image.runtimeStyle.width = 'auto';\n    image.runtimeStyle.height = 'auto';\n\n    // get the original size\n    var w = image.width;\n    var h = image.height;\n\n    // and remove overides\n    image.runtimeStyle.width = oldRuntimeWidth;\n    image.runtimeStyle.height = oldRuntimeHeight;\n\n    if (arguments.length == 3) {\n      dx = arguments[1];\n      dy = arguments[2];\n      sx = sy = 0;\n      sw = dw = w;\n      sh = dh = h;\n    } else if (arguments.length == 5) {\n      dx = arguments[1];\n      dy = arguments[2];\n      dw = arguments[3];\n      dh = arguments[4];\n      sx = sy = 0;\n      sw = w;\n      sh = h;\n    } else if (arguments.length == 9) {\n      sx = arguments[1];\n      sy = arguments[2];\n      sw = arguments[3];\n      sh = arguments[4];\n      dx = arguments[5];\n      dy = arguments[6];\n      dw = arguments[7];\n      dh = arguments[8];\n    } else {\n      throw Error('Invalid number of arguments');\n    }\n\n    var d = this.getCoords_(dx, dy);\n\n    var w2 = sw / 2;\n    var h2 = sh / 2;\n\n    var vmlStr = [];\n\n    var W = 10;\n    var H = 10;\n\n    // For some reason that I've now forgotten, using divs didn't work\n    vmlStr.push(' <g_vml_:group',\n                ' coordsize=\"', Z * W, ',', Z * H, '\"',\n                ' coordorigin=\"0,0\"' ,\n                ' style=\"width:', W, 'px;height:', H, 'px;position:absolute;');\n\n    // If filters are necessary (rotation exists), create them\n    // filters are bog-slow, so only create them if abbsolutely necessary\n    // The following check doesn't account for skews (which don't exist\n    // in the canvas spec (yet) anyway.\n\n    if (this.m_[0][0] != 1 || this.m_[0][1] ||\n        this.m_[1][1] != 1 || this.m_[1][0]) {\n      var filter = [];\n\n      // Note the 12/21 reversal\n      filter.push('M11=', this.m_[0][0], ',',\n                  'M12=', this.m_[1][0], ',',\n                  'M21=', this.m_[0][1], ',',\n                  'M22=', this.m_[1][1], ',',\n                  'Dx=', mr(d.x / Z), ',',\n                  'Dy=', mr(d.y / Z), '');\n\n      // Bounding box calculation (need to minimize displayed area so that\n      // filters don't waste time on unused pixels.\n      var max = d;\n      var c2 = this.getCoords_(dx + dw, dy);\n      var c3 = this.getCoords_(dx, dy + dh);\n      var c4 = this.getCoords_(dx + dw, dy + dh);\n\n      max.x = m.max(max.x, c2.x, c3.x, c4.x);\n      max.y = m.max(max.y, c2.y, c3.y, c4.y);\n\n      vmlStr.push('padding:0 ', mr(max.x / Z), 'px ', mr(max.y / Z),\n                  'px 0;filter:progid:DXImageTransform.Microsoft.Matrix(',\n                  filter.join(''), \", sizingmethod='clip');\");\n\n    } else {\n      vmlStr.push('top:', mr(d.y / Z), 'px;left:', mr(d.x / Z), 'px;');\n    }\n\n    vmlStr.push(' \">' ,\n                '<g_vml_:image src=\"', image.src, '\"',\n                ' style=\"width:', Z * dw, 'px;',\n                ' height:', Z * dh, 'px\"',\n                ' cropleft=\"', sx / w, '\"',\n                ' croptop=\"', sy / h, '\"',\n                ' cropright=\"', (w - sx - sw) / w, '\"',\n                ' cropbottom=\"', (h - sy - sh) / h, '\"',\n                ' />',\n                '</g_vml_:group>');\n\n    this.element_.insertAdjacentHTML('BeforeEnd', vmlStr.join(''));\n  };\n\n  contextPrototype.stroke = function(aFill) {\n    var W = 10;\n    var H = 10;\n    // Divide the shape into chunks if it's too long because IE has a limit\n    // somewhere for how long a VML shape can be. This simple division does\n    // not work with fills, only strokes, unfortunately.\n    var chunkSize = 5000;\n\n    var min = {x: null, y: null};\n    var max = {x: null, y: null};\n\n    for (var j = 0; j < this.currentPath_.length; j += chunkSize) {\n      var lineStr = [];\n      var lineOpen = false;\n\n      lineStr.push('<g_vml_:shape',\n                   ' filled=\"', !!aFill, '\"',\n                   ' style=\"position:absolute;width:', W, 'px;height:', H, 'px;\"',\n                   ' coordorigin=\"0,0\"',\n                   ' coordsize=\"', Z * W, ',', Z * H, '\"',\n                   ' stroked=\"', !aFill, '\"',\n                   ' path=\"');\n\n      var newSeq = false;\n\n      for (var i = j; i < Math.min(j + chunkSize, this.currentPath_.length); i++) {\n        if (i % chunkSize == 0 && i > 0) { // move into position for next chunk\n          lineStr.push(' m ', mr(this.currentPath_[i-1].x), ',', mr(this.currentPath_[i-1].y));\n        }\n\n        var p = this.currentPath_[i];\n        var c;\n\n        switch (p.type) {\n          case 'moveTo':\n            c = p;\n            lineStr.push(' m ', mr(p.x), ',', mr(p.y));\n            break;\n          case 'lineTo':\n            lineStr.push(' l ', mr(p.x), ',', mr(p.y));\n            break;\n          case 'close':\n            lineStr.push(' x ');\n            p = null;\n            break;\n          case 'bezierCurveTo':\n            lineStr.push(' c ',\n                         mr(p.cp1x), ',', mr(p.cp1y), ',',\n                         mr(p.cp2x), ',', mr(p.cp2y), ',',\n                         mr(p.x), ',', mr(p.y));\n            break;\n          case 'at':\n          case 'wa':\n            lineStr.push(' ', p.type, ' ',\n                         mr(p.x - this.arcScaleX_ * p.radius), ',',\n                         mr(p.y - this.arcScaleY_ * p.radius), ' ',\n                         mr(p.x + this.arcScaleX_ * p.radius), ',',\n                         mr(p.y + this.arcScaleY_ * p.radius), ' ',\n                         mr(p.xStart), ',', mr(p.yStart), ' ',\n                         mr(p.xEnd), ',', mr(p.yEnd));\n            break;\n        }\n  \n  \n        // TODO: Following is broken for curves due to\n        //       move to proper paths.\n  \n        // Figure out dimensions so we can do gradient fills\n        // properly\n        if (p) {\n          if (min.x == null || p.x < min.x) {\n            min.x = p.x;\n          }\n          if (max.x == null || p.x > max.x) {\n            max.x = p.x;\n          }\n          if (min.y == null || p.y < min.y) {\n            min.y = p.y;\n          }\n          if (max.y == null || p.y > max.y) {\n            max.y = p.y;\n          }\n        }\n      }\n      lineStr.push(' \">');\n  \n      if (!aFill) {\n        appendStroke(this, lineStr);\n      } else {\n        appendFill(this, lineStr, min, max);\n      }\n  \n      lineStr.push('</g_vml_:shape>');\n  \n      this.element_.insertAdjacentHTML('beforeEnd', lineStr.join(''));\n    }\n  };\n\n  function appendStroke(ctx, lineStr) {\n    var a = processStyle(ctx.strokeStyle);\n    var color = a.color;\n    var opacity = a.alpha * ctx.globalAlpha;\n    var lineWidth = ctx.lineScale_ * ctx.lineWidth;\n\n    // VML cannot correctly render a line if the width is less than 1px.\n    // In that case, we dilute the color to make the line look thinner.\n    if (lineWidth < 1) {\n      opacity *= lineWidth;\n    }\n\n    lineStr.push(\n      '<g_vml_:stroke',\n      ' opacity=\"', opacity, '\"',\n      ' joinstyle=\"', ctx.lineJoin, '\"',\n      ' miterlimit=\"', ctx.miterLimit, '\"',\n      ' endcap=\"', processLineCap(ctx.lineCap), '\"',\n      ' weight=\"', lineWidth, 'px\"',\n      ' color=\"', color, '\" />'\n    );\n  }\n\n  function appendFill(ctx, lineStr, min, max) {\n    var fillStyle = ctx.fillStyle;\n    var arcScaleX = ctx.arcScaleX_;\n    var arcScaleY = ctx.arcScaleY_;\n    var width = max.x - min.x;\n    var height = max.y - min.y;\n    if (fillStyle instanceof CanvasGradient_) {\n      // TODO: Gradients transformed with the transformation matrix.\n      var angle = 0;\n      var focus = {x: 0, y: 0};\n\n      // additional offset\n      var shift = 0;\n      // scale factor for offset\n      var expansion = 1;\n\n      if (fillStyle.type_ == 'gradient') {\n        var x0 = fillStyle.x0_ / arcScaleX;\n        var y0 = fillStyle.y0_ / arcScaleY;\n        var x1 = fillStyle.x1_ / arcScaleX;\n        var y1 = fillStyle.y1_ / arcScaleY;\n        var p0 = ctx.getCoords_(x0, y0);\n        var p1 = ctx.getCoords_(x1, y1);\n        var dx = p1.x - p0.x;\n        var dy = p1.y - p0.y;\n        angle = Math.atan2(dx, dy) * 180 / Math.PI;\n\n        // The angle should be a non-negative number.\n        if (angle < 0) {\n          angle += 360;\n        }\n\n        // Very small angles produce an unexpected result because they are\n        // converted to a scientific notation string.\n        if (angle < 1e-6) {\n          angle = 0;\n        }\n      } else {\n        var p0 = ctx.getCoords_(fillStyle.x0_, fillStyle.y0_);\n        focus = {\n          x: (p0.x - min.x) / width,\n          y: (p0.y - min.y) / height\n        };\n\n        width  /= arcScaleX * Z;\n        height /= arcScaleY * Z;\n        var dimension = m.max(width, height);\n        shift = 2 * fillStyle.r0_ / dimension;\n        expansion = 2 * fillStyle.r1_ / dimension - shift;\n      }\n\n      // We need to sort the color stops in ascending order by offset,\n      // otherwise IE won't interpret it correctly.\n      var stops = fillStyle.colors_;\n      stops.sort(function(cs1, cs2) {\n        return cs1.offset - cs2.offset;\n      });\n\n      var length = stops.length;\n      var color1 = stops[0].color;\n      var color2 = stops[length - 1].color;\n      var opacity1 = stops[0].alpha * ctx.globalAlpha;\n      var opacity2 = stops[length - 1].alpha * ctx.globalAlpha;\n\n      var colors = [];\n      for (var i = 0; i < length; i++) {\n        var stop = stops[i];\n        colors.push(stop.offset * expansion + shift + ' ' + stop.color);\n      }\n\n      // When colors attribute is used, the meanings of opacity and o:opacity2\n      // are reversed.\n      lineStr.push('<g_vml_:fill type=\"', fillStyle.type_, '\"',\n                   ' method=\"none\" focus=\"100%\"',\n                   ' color=\"', color1, '\"',\n                   ' color2=\"', color2, '\"',\n                   ' colors=\"', colors.join(','), '\"',\n                   ' opacity=\"', opacity2, '\"',\n                   ' g_o_:opacity2=\"', opacity1, '\"',\n                   ' angle=\"', angle, '\"',\n                   ' focusposition=\"', focus.x, ',', focus.y, '\" />');\n    } else if (fillStyle instanceof CanvasPattern_) {\n      if (width && height) {\n        var deltaLeft = -min.x;\n        var deltaTop = -min.y;\n        lineStr.push('<g_vml_:fill',\n                     ' position=\"',\n                     deltaLeft / width * arcScaleX * arcScaleX, ',',\n                     deltaTop / height * arcScaleY * arcScaleY, '\"',\n                     ' type=\"tile\"',\n                     // TODO: Figure out the correct size to fit the scale.\n                     //' size=\"', w, 'px ', h, 'px\"',\n                     ' src=\"', fillStyle.src_, '\" />');\n       }\n    } else {\n      var a = processStyle(ctx.fillStyle);\n      var color = a.color;\n      var opacity = a.alpha * ctx.globalAlpha;\n      lineStr.push('<g_vml_:fill color=\"', color, '\" opacity=\"', opacity,\n                   '\" />');\n    }\n  }\n\n  contextPrototype.fill = function() {\n    this.stroke(true);\n  };\n\n  contextPrototype.closePath = function() {\n    this.currentPath_.push({type: 'close'});\n  };\n\n  /**\n   * @private\n   */\n  contextPrototype.getCoords_ = function(aX, aY) {\n    var m = this.m_;\n    return {\n      x: Z * (aX * m[0][0] + aY * m[1][0] + m[2][0]) - Z2,\n      y: Z * (aX * m[0][1] + aY * m[1][1] + m[2][1]) - Z2\n    };\n  };\n\n  contextPrototype.save = function() {\n    var o = {};\n    copyState(this, o);\n    this.aStack_.push(o);\n    this.mStack_.push(this.m_);\n    this.m_ = matrixMultiply(createMatrixIdentity(), this.m_);\n  };\n\n  contextPrototype.restore = function() {\n    if (this.aStack_.length) {\n      copyState(this.aStack_.pop(), this);\n      this.m_ = this.mStack_.pop();\n    }\n  };\n\n  function matrixIsFinite(m) {\n    return isFinite(m[0][0]) && isFinite(m[0][1]) &&\n        isFinite(m[1][0]) && isFinite(m[1][1]) &&\n        isFinite(m[2][0]) && isFinite(m[2][1]);\n  }\n\n  function setM(ctx, m, updateLineScale) {\n    if (!matrixIsFinite(m)) {\n      return;\n    }\n    ctx.m_ = m;\n\n    if (updateLineScale) {\n      // Get the line scale.\n      // Determinant of this.m_ means how much the area is enlarged by the\n      // transformation. So its square root can be used as a scale factor\n      // for width.\n      var det = m[0][0] * m[1][1] - m[0][1] * m[1][0];\n      ctx.lineScale_ = sqrt(abs(det));\n    }\n  }\n\n  contextPrototype.translate = function(aX, aY) {\n    var m1 = [\n      [1,  0,  0],\n      [0,  1,  0],\n      [aX, aY, 1]\n    ];\n\n    setM(this, matrixMultiply(m1, this.m_), false);\n  };\n\n  contextPrototype.rotate = function(aRot) {\n    var c = mc(aRot);\n    var s = ms(aRot);\n\n    var m1 = [\n      [c,  s, 0],\n      [-s, c, 0],\n      [0,  0, 1]\n    ];\n\n    setM(this, matrixMultiply(m1, this.m_), false);\n  };\n\n  contextPrototype.scale = function(aX, aY) {\n    this.arcScaleX_ *= aX;\n    this.arcScaleY_ *= aY;\n    var m1 = [\n      [aX, 0,  0],\n      [0,  aY, 0],\n      [0,  0,  1]\n    ];\n\n    setM(this, matrixMultiply(m1, this.m_), true);\n  };\n\n  contextPrototype.transform = function(m11, m12, m21, m22, dx, dy) {\n    var m1 = [\n      [m11, m12, 0],\n      [m21, m22, 0],\n      [dx,  dy,  1]\n    ];\n\n    setM(this, matrixMultiply(m1, this.m_), true);\n  };\n\n  contextPrototype.setTransform = function(m11, m12, m21, m22, dx, dy) {\n    var m = [\n      [m11, m12, 0],\n      [m21, m22, 0],\n      [dx,  dy,  1]\n    ];\n\n    setM(this, m, true);\n  };\n\n  /**\n   * The text drawing function.\n   * The maxWidth argument isn't taken in account, since no browser supports\n   * it yet.\n   */\n  contextPrototype.drawText_ = function(text, x, y, maxWidth, stroke) {\n    var m = this.m_,\n        delta = 1000,\n        left = 0,\n        right = delta,\n        offset = {x: 0, y: 0},\n        lineStr = [];\n\n    var fontStyle = getComputedStyle(processFontStyle(this.font),\n                                     this.element_);\n\n    var fontStyleString = buildStyle(fontStyle);\n\n    var elementStyle = this.element_.currentStyle;\n    var textAlign = this.textAlign.toLowerCase();\n    switch (textAlign) {\n      case 'left':\n      case 'center':\n      case 'right':\n        break;\n      case 'end':\n        textAlign = elementStyle.direction == 'ltr' ? 'right' : 'left';\n        break;\n      case 'start':\n        textAlign = elementStyle.direction == 'rtl' ? 'right' : 'left';\n        break;\n      default:\n        textAlign = 'left';\n    }\n\n    // 1.75 is an arbitrary number, as there is no info about the text baseline\n    switch (this.textBaseline) {\n      case 'hanging':\n      case 'top':\n        offset.y = fontStyle.size / 1.75;\n        break;\n      case 'middle':\n        break;\n      default:\n      case null:\n      case 'alphabetic':\n      case 'ideographic':\n      case 'bottom':\n        offset.y = -fontStyle.size / 2.25;\n        break;\n    }\n\n    switch(textAlign) {\n      case 'right':\n        left = delta;\n        right = 0.05;\n        break;\n      case 'center':\n        left = right = delta / 2;\n        break;\n    }\n\n    var d = this.getCoords_(x + offset.x, y + offset.y);\n\n    lineStr.push('<g_vml_:line from=\"', -left ,' 0\" to=\"', right ,' 0.05\" ',\n                 ' coordsize=\"100 100\" coordorigin=\"0 0\"',\n                 ' filled=\"', !stroke, '\" stroked=\"', !!stroke,\n                 '\" style=\"position:absolute;width:1px;height:1px;\">');\n\n    if (stroke) {\n      appendStroke(this, lineStr);\n    } else {\n      // TODO: Fix the min and max params.\n      appendFill(this, lineStr, {x: -left, y: 0},\n                 {x: right, y: fontStyle.size});\n    }\n\n    var skewM = m[0][0].toFixed(3) + ',' + m[1][0].toFixed(3) + ',' +\n                m[0][1].toFixed(3) + ',' + m[1][1].toFixed(3) + ',0,0';\n\n    var skewOffset = mr(d.x / Z) + ',' + mr(d.y / Z);\n\n    lineStr.push('<g_vml_:skew on=\"t\" matrix=\"', skewM ,'\" ',\n                 ' offset=\"', skewOffset, '\" origin=\"', left ,' 0\" />',\n                 '<g_vml_:path textpathok=\"true\" />',\n                 '<g_vml_:textpath on=\"true\" string=\"',\n                 encodeHtmlAttribute(text),\n                 '\" style=\"v-text-align:', textAlign,\n                 ';font:', encodeHtmlAttribute(fontStyleString),\n                 '\" /></g_vml_:line>');\n\n    this.element_.insertAdjacentHTML('beforeEnd', lineStr.join(''));\n  };\n\n  contextPrototype.fillText = function(text, x, y, maxWidth) {\n    this.drawText_(text, x, y, maxWidth, false);\n  };\n\n  contextPrototype.strokeText = function(text, x, y, maxWidth) {\n    this.drawText_(text, x, y, maxWidth, true);\n  };\n\n  contextPrototype.measureText = function(text) {\n    if (!this.textMeasureEl_) {\n      var s = '<span style=\"position:absolute;' +\n          'top:-20000px;left:0;padding:0;margin:0;border:none;' +\n          'white-space:pre;\"></span>';\n      this.element_.insertAdjacentHTML('beforeEnd', s);\n      this.textMeasureEl_ = this.element_.lastChild;\n    }\n    var doc = this.element_.ownerDocument;\n    this.textMeasureEl_.innerHTML = '';\n    this.textMeasureEl_.style.font = this.font;\n    // Don't use innerHTML or innerText because they allow markup/whitespace.\n    this.textMeasureEl_.appendChild(doc.createTextNode(text));\n    return {width: this.textMeasureEl_.offsetWidth};\n  };\n\n  /******** STUBS ********/\n  contextPrototype.clip = function() {\n    // TODO: Implement\n  };\n\n  contextPrototype.arcTo = function() {\n    // TODO: Implement\n  };\n\n  contextPrototype.createPattern = function(image, repetition) {\n    return new CanvasPattern_(image, repetition);\n  };\n\n  // Gradient / Pattern Stubs\n  function CanvasGradient_(aType) {\n    this.type_ = aType;\n    this.x0_ = 0;\n    this.y0_ = 0;\n    this.r0_ = 0;\n    this.x1_ = 0;\n    this.y1_ = 0;\n    this.r1_ = 0;\n    this.colors_ = [];\n  }\n\n  CanvasGradient_.prototype.addColorStop = function(aOffset, aColor) {\n    aColor = processStyle(aColor);\n    this.colors_.push({offset: aOffset,\n                       color: aColor.color,\n                       alpha: aColor.alpha});\n  };\n\n  function CanvasPattern_(image, repetition) {\n    assertImageIsValid(image);\n    switch (repetition) {\n      case 'repeat':\n      case null:\n      case '':\n        this.repetition_ = 'repeat';\n        break\n      case 'repeat-x':\n      case 'repeat-y':\n      case 'no-repeat':\n        this.repetition_ = repetition;\n        break;\n      default:\n        throwException('SYNTAX_ERR');\n    }\n\n    this.src_ = image.src;\n    this.width_ = image.width;\n    this.height_ = image.height;\n  }\n\n  function throwException(s) {\n    throw new DOMException_(s);\n  }\n\n  function assertImageIsValid(img) {\n    if (!img || img.nodeType != 1 || img.tagName != 'IMG') {\n      throwException('TYPE_MISMATCH_ERR');\n    }\n    if (img.readyState != 'complete') {\n      throwException('INVALID_STATE_ERR');\n    }\n  }\n\n  function DOMException_(s) {\n    this.code = this[s];\n    this.message = s +': DOM Exception ' + this.code;\n  }\n  var p = DOMException_.prototype = new Error;\n  p.INDEX_SIZE_ERR = 1;\n  p.DOMSTRING_SIZE_ERR = 2;\n  p.HIERARCHY_REQUEST_ERR = 3;\n  p.WRONG_DOCUMENT_ERR = 4;\n  p.INVALID_CHARACTER_ERR = 5;\n  p.NO_DATA_ALLOWED_ERR = 6;\n  p.NO_MODIFICATION_ALLOWED_ERR = 7;\n  p.NOT_FOUND_ERR = 8;\n  p.NOT_SUPPORTED_ERR = 9;\n  p.INUSE_ATTRIBUTE_ERR = 10;\n  p.INVALID_STATE_ERR = 11;\n  p.SYNTAX_ERR = 12;\n  p.INVALID_MODIFICATION_ERR = 13;\n  p.NAMESPACE_ERR = 14;\n  p.INVALID_ACCESS_ERR = 15;\n  p.VALIDATION_ERR = 16;\n  p.TYPE_MISMATCH_ERR = 17;\n\n  // set up externs\n  G_vmlCanvasManager = G_vmlCanvasManager_;\n  CanvasRenderingContext2D = CanvasRenderingContext2D_;\n  CanvasGradient = CanvasGradient_;\n  CanvasPattern = CanvasPattern_;\n  DOMException = DOMException_;\n})();\n\n} // if\n"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/js/flot/jquery.colorhelpers.js",
    "content": "/* Plugin for jQuery for working with colors.\n * \n * Version 1.1.\n * \n * Inspiration from jQuery color animation plugin by John Resig.\n *\n * Released under the MIT license by Ole Laursen, October 2009.\n *\n * Examples:\n *\n *   $.color.parse(\"#fff\").scale('rgb', 0.25).add('a', -0.5).toString()\n *   var c = $.color.extract($(\"#mydiv\"), 'background-color');\n *   console.log(c.r, c.g, c.b, c.a);\n *   $.color.make(100, 50, 25, 0.4).toString() // returns \"rgba(100,50,25,0.4)\"\n *\n * Note that .scale() and .add() return the same modified object\n * instead of making a new one.\n *\n * V. 1.1: Fix error handling so e.g. parsing an empty string does\n * produce a color rather than just crashing.\n */ \n\n(function($) {\n    $.color = {};\n\n    // construct color object with some convenient chainable helpers\n    $.color.make = function (r, g, b, a) {\n        var o = {};\n        o.r = r || 0;\n        o.g = g || 0;\n        o.b = b || 0;\n        o.a = a != null ? a : 1;\n\n        o.add = function (c, d) {\n            for (var i = 0; i < c.length; ++i)\n                o[c.charAt(i)] += d;\n            return o.normalize();\n        };\n        \n        o.scale = function (c, f) {\n            for (var i = 0; i < c.length; ++i)\n                o[c.charAt(i)] *= f;\n            return o.normalize();\n        };\n        \n        o.toString = function () {\n            if (o.a >= 1.0) {\n                return \"rgb(\"+[o.r, o.g, o.b].join(\",\")+\")\";\n            } else {\n                return \"rgba(\"+[o.r, o.g, o.b, o.a].join(\",\")+\")\";\n            }\n        };\n\n        o.normalize = function () {\n            function clamp(min, value, max) {\n                return value < min ? min: (value > max ? max: value);\n            }\n            \n            o.r = clamp(0, parseInt(o.r), 255);\n            o.g = clamp(0, parseInt(o.g), 255);\n            o.b = clamp(0, parseInt(o.b), 255);\n            o.a = clamp(0, o.a, 1);\n            return o;\n        };\n\n        o.clone = function () {\n            return $.color.make(o.r, o.b, o.g, o.a);\n        };\n\n        return o.normalize();\n    }\n\n    // extract CSS color property from element, going up in the DOM\n    // if it's \"transparent\"\n    $.color.extract = function (elem, css) {\n        var c;\n        do {\n            c = elem.css(css).toLowerCase();\n            // keep going until we find an element that has color, or\n            // we hit the body\n            if (c != '' && c != 'transparent')\n                break;\n            elem = elem.parent();\n        } while (!$.nodeName(elem.get(0), \"body\"));\n\n        // catch Safari's way of signalling transparent\n        if (c == \"rgba(0, 0, 0, 0)\")\n            c = \"transparent\";\n        \n        return $.color.parse(c);\n    }\n    \n    // parse CSS color string (like \"rgb(10, 32, 43)\" or \"#fff\"),\n    // returns color object, if parsing failed, you get black (0, 0,\n    // 0) out\n    $.color.parse = function (str) {\n        var res, m = $.color.make;\n\n        // Look for rgb(num,num,num)\n        if (res = /rgb\\(\\s*([0-9]{1,3})\\s*,\\s*([0-9]{1,3})\\s*,\\s*([0-9]{1,3})\\s*\\)/.exec(str))\n            return m(parseInt(res[1], 10), parseInt(res[2], 10), parseInt(res[3], 10));\n        \n        // Look for rgba(num,num,num,num)\n        if (res = /rgba\\(\\s*([0-9]{1,3})\\s*,\\s*([0-9]{1,3})\\s*,\\s*([0-9]{1,3})\\s*,\\s*([0-9]+(?:\\.[0-9]+)?)\\s*\\)/.exec(str))\n            return m(parseInt(res[1], 10), parseInt(res[2], 10), parseInt(res[3], 10), parseFloat(res[4]));\n            \n        // Look for rgb(num%,num%,num%)\n        if (res = /rgb\\(\\s*([0-9]+(?:\\.[0-9]+)?)\\%\\s*,\\s*([0-9]+(?:\\.[0-9]+)?)\\%\\s*,\\s*([0-9]+(?:\\.[0-9]+)?)\\%\\s*\\)/.exec(str))\n            return m(parseFloat(res[1])*2.55, parseFloat(res[2])*2.55, parseFloat(res[3])*2.55);\n\n        // Look for rgba(num%,num%,num%,num)\n        if (res = /rgba\\(\\s*([0-9]+(?:\\.[0-9]+)?)\\%\\s*,\\s*([0-9]+(?:\\.[0-9]+)?)\\%\\s*,\\s*([0-9]+(?:\\.[0-9]+)?)\\%\\s*,\\s*([0-9]+(?:\\.[0-9]+)?)\\s*\\)/.exec(str))\n            return m(parseFloat(res[1])*2.55, parseFloat(res[2])*2.55, parseFloat(res[3])*2.55, parseFloat(res[4]));\n        \n        // Look for #a0b1c2\n        if (res = /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(str))\n            return m(parseInt(res[1], 16), parseInt(res[2], 16), parseInt(res[3], 16));\n\n        // Look for #fff\n        if (res = /#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(str))\n            return m(parseInt(res[1]+res[1], 16), parseInt(res[2]+res[2], 16), parseInt(res[3]+res[3], 16));\n\n        // Otherwise, we're most likely dealing with a named color\n        var name = $.trim(str).toLowerCase();\n        if (name == \"transparent\")\n            return m(255, 255, 255, 0);\n        else {\n            // default to black\n            res = lookupColors[name] || [0, 0, 0];\n            return m(res[0], res[1], res[2]);\n        }\n    }\n    \n    var lookupColors = {\n        aqua:[0,255,255],\n        azure:[240,255,255],\n        beige:[245,245,220],\n        black:[0,0,0],\n        blue:[0,0,255],\n        brown:[165,42,42],\n        cyan:[0,255,255],\n        darkblue:[0,0,139],\n        darkcyan:[0,139,139],\n        darkgrey:[169,169,169],\n        darkgreen:[0,100,0],\n        darkkhaki:[189,183,107],\n        darkmagenta:[139,0,139],\n        darkolivegreen:[85,107,47],\n        darkorange:[255,140,0],\n        darkorchid:[153,50,204],\n        darkred:[139,0,0],\n        darksalmon:[233,150,122],\n        darkviolet:[148,0,211],\n        fuchsia:[255,0,255],\n        gold:[255,215,0],\n        green:[0,128,0],\n        indigo:[75,0,130],\n        khaki:[240,230,140],\n        lightblue:[173,216,230],\n        lightcyan:[224,255,255],\n        lightgreen:[144,238,144],\n        lightgrey:[211,211,211],\n        lightpink:[255,182,193],\n        lightyellow:[255,255,224],\n        lime:[0,255,0],\n        magenta:[255,0,255],\n        maroon:[128,0,0],\n        navy:[0,0,128],\n        olive:[128,128,0],\n        orange:[255,165,0],\n        pink:[255,192,203],\n        purple:[128,0,128],\n        violet:[128,0,128],\n        red:[255,0,0],\n        silver:[192,192,192],\n        white:[255,255,255],\n        yellow:[255,255,0]\n    };\n})(jQuery);\n"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/js/flot/jquery.flot.crosshair.js",
    "content": "/*\nFlot plugin for showing crosshairs, thin lines, when the mouse hovers\nover the plot.\n\n  crosshair: {\n    mode: null or \"x\" or \"y\" or \"xy\"\n    color: color\n    lineWidth: number\n  }\n\nSet the mode to one of \"x\", \"y\" or \"xy\". The \"x\" mode enables a\nvertical crosshair that lets you trace the values on the x axis, \"y\"\nenables a horizontal crosshair and \"xy\" enables them both. \"color\" is\nthe color of the crosshair (default is \"rgba(170, 0, 0, 0.80)\"),\n\"lineWidth\" is the width of the drawn lines (default is 1).\n\nThe plugin also adds four public methods:\n\n  - setCrosshair(pos)\n\n    Set the position of the crosshair. Note that this is cleared if\n    the user moves the mouse. \"pos\" is in coordinates of the plot and\n    should be on the form { x: xpos, y: ypos } (you can use x2/x3/...\n    if you're using multiple axes), which is coincidentally the same\n    format as what you get from a \"plothover\" event. If \"pos\" is null,\n    the crosshair is cleared.\n\n  - clearCrosshair()\n\n    Clear the crosshair.\n\n  - lockCrosshair(pos)\n\n    Cause the crosshair to lock to the current location, no longer\n    updating if the user moves the mouse. Optionally supply a position\n    (passed on to setCrosshair()) to move it to.\n\n    Example usage:\n      var myFlot = $.plot( $(\"#graph\"), ..., { crosshair: { mode: \"x\" } } };\n      $(\"#graph\").bind(\"plothover\", function (evt, position, item) {\n        if (item) {\n          // Lock the crosshair to the data point being hovered\n          myFlot.lockCrosshair({ x: item.datapoint[0], y: item.datapoint[1] });\n        }\n        else {\n          // Return normal crosshair operation\n          myFlot.unlockCrosshair();\n        }\n      });\n\n  - unlockCrosshair()\n\n    Free the crosshair to move again after locking it.\n*/\n\n(function ($) {\n    var options = {\n        crosshair: {\n            mode: null, // one of null, \"x\", \"y\" or \"xy\",\n            color: \"rgba(170, 0, 0, 0.80)\",\n            lineWidth: 1\n        }\n    };\n    \n    function init(plot) {\n        // position of crosshair in pixels\n        var crosshair = { x: -1, y: -1, locked: false };\n\n        plot.setCrosshair = function setCrosshair(pos) {\n            if (!pos)\n                crosshair.x = -1;\n            else {\n                var o = plot.p2c(pos);\n                crosshair.x = Math.max(0, Math.min(o.left, plot.width()));\n                crosshair.y = Math.max(0, Math.min(o.top, plot.height()));\n            }\n            \n            plot.triggerRedrawOverlay();\n        };\n        \n        plot.clearCrosshair = plot.setCrosshair; // passes null for pos\n        \n        plot.lockCrosshair = function lockCrosshair(pos) {\n            if (pos)\n                plot.setCrosshair(pos);\n            crosshair.locked = true;\n        }\n\n        plot.unlockCrosshair = function unlockCrosshair() {\n            crosshair.locked = false;\n        }\n\n        function onMouseOut(e) {\n            if (crosshair.locked)\n                return;\n\n            if (crosshair.x != -1) {\n                crosshair.x = -1;\n                plot.triggerRedrawOverlay();\n            }\n        }\n\n        function onMouseMove(e) {\n            if (crosshair.locked)\n                return;\n                \n            if (plot.getSelection && plot.getSelection()) {\n                crosshair.x = -1; // hide the crosshair while selecting\n                return;\n            }\n                \n            var offset = plot.offset();\n            crosshair.x = Math.max(0, Math.min(e.pageX - offset.left, plot.width()));\n            crosshair.y = Math.max(0, Math.min(e.pageY - offset.top, plot.height()));\n            plot.triggerRedrawOverlay();\n        }\n        \n        plot.hooks.bindEvents.push(function (plot, eventHolder) {\n            if (!plot.getOptions().crosshair.mode)\n                return;\n\n            eventHolder.mouseout(onMouseOut);\n            eventHolder.mousemove(onMouseMove);\n        });\n\n        plot.hooks.drawOverlay.push(function (plot, ctx) {\n            var c = plot.getOptions().crosshair;\n            if (!c.mode)\n                return;\n\n            var plotOffset = plot.getPlotOffset();\n            \n            ctx.save();\n            ctx.translate(plotOffset.left, plotOffset.top);\n\n            if (crosshair.x != -1) {\n                ctx.strokeStyle = c.color;\n                ctx.lineWidth = c.lineWidth;\n                ctx.lineJoin = \"round\";\n\n                ctx.beginPath();\n                if (c.mode.indexOf(\"x\") != -1) {\n                    ctx.moveTo(crosshair.x, 0);\n                    ctx.lineTo(crosshair.x, plot.height());\n                }\n                if (c.mode.indexOf(\"y\") != -1) {\n                    ctx.moveTo(0, crosshair.y);\n                    ctx.lineTo(plot.width(), crosshair.y);\n                }\n                ctx.stroke();\n            }\n            ctx.restore();\n        });\n\n        plot.hooks.shutdown.push(function (plot, eventHolder) {\n            eventHolder.unbind(\"mouseout\", onMouseOut);\n            eventHolder.unbind(\"mousemove\", onMouseMove);\n        });\n    }\n    \n    $.plot.plugins.push({\n        init: init,\n        options: options,\n        name: 'crosshair',\n        version: '1.0'\n    });\n})(jQuery);\n"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/js/flot/jquery.flot.fillbetween.js",
    "content": "/*\nFlot plugin for computing bottoms for filled line and bar charts.\n\nThe case: you've got two series that you want to fill the area\nbetween. In Flot terms, you need to use one as the fill bottom of the\nother. You can specify the bottom of each data point as the third\ncoordinate manually, or you can use this plugin to compute it for you.\n\nIn order to name the other series, you need to give it an id, like this\n\n  var dataset = [\n       { data: [ ... ], id: \"foo\" } ,         // use default bottom\n       { data: [ ... ], fillBetween: \"foo\" }, // use first dataset as bottom\n       ];\n\n  $.plot($(\"#placeholder\"), dataset, { line: { show: true, fill: true }});\n\nAs a convenience, if the id given is a number that doesn't appear as\nan id in the series, it is interpreted as the index in the array\ninstead (so fillBetween: 0 can also mean the first series).\n  \nInternally, the plugin modifies the datapoints in each series. For\nline series, extra data points might be inserted through\ninterpolation. Note that at points where the bottom line is not\ndefined (due to a null point or start/end of line), the current line\nwill show a gap too. The algorithm comes from the jquery.flot.stack.js\nplugin, possibly some code could be shared.\n*/\n\n(function ($) {\n    var options = {\n        series: { fillBetween: null } // or number\n    };\n    \n    function init(plot) {\n        function findBottomSeries(s, allseries) {\n            var i;\n            for (i = 0; i < allseries.length; ++i) {\n                if (allseries[i].id == s.fillBetween)\n                    return allseries[i];\n            }\n\n            if (typeof s.fillBetween == \"number\") {\n                i = s.fillBetween;\n            \n                if (i < 0 || i >= allseries.length)\n                    return null;\n\n                return allseries[i];\n            }\n            \n            return null;\n        }\n        \n        function computeFillBottoms(plot, s, datapoints) {\n            if (s.fillBetween == null)\n                return;\n\n            var other = findBottomSeries(s, plot.getData());\n            if (!other)\n                return;\n\n            var ps = datapoints.pointsize,\n                points = datapoints.points,\n                otherps = other.datapoints.pointsize,\n                otherpoints = other.datapoints.points,\n                newpoints = [],\n                px, py, intery, qx, qy, bottom,\n                withlines = s.lines.show,\n                withbottom = ps > 2 && datapoints.format[2].y,\n                withsteps = withlines && s.lines.steps,\n                fromgap = true,\n                i = 0, j = 0, l;\n\n            while (true) {\n                if (i >= points.length)\n                    break;\n\n                l = newpoints.length;\n\n                if (points[i] == null) {\n                    // copy gaps\n                    for (m = 0; m < ps; ++m)\n                        newpoints.push(points[i + m]);\n                    i += ps;\n                }\n                else if (j >= otherpoints.length) {\n                    // for lines, we can't use the rest of the points\n                    if (!withlines) {\n                        for (m = 0; m < ps; ++m)\n                            newpoints.push(points[i + m]);\n                    }\n                    i += ps;\n                }\n                else if (otherpoints[j] == null) {\n                    // oops, got a gap\n                    for (m = 0; m < ps; ++m)\n                        newpoints.push(null);\n                    fromgap = true;\n                    j += otherps;\n                }\n                else {\n                    // cases where we actually got two points\n                    px = points[i];\n                    py = points[i + 1];\n                    qx = otherpoints[j];\n                    qy = otherpoints[j + 1];\n                    bottom = 0;\n\n                    if (px == qx) {\n                        for (m = 0; m < ps; ++m)\n                            newpoints.push(points[i + m]);\n\n                        //newpoints[l + 1] += qy;\n                        bottom = qy;\n                        \n                        i += ps;\n                        j += otherps;\n                    }\n                    else if (px > qx) {\n                        // we got past point below, might need to\n                        // insert interpolated extra point\n                        if (withlines && i > 0 && points[i - ps] != null) {\n                            intery = py + (points[i - ps + 1] - py) * (qx - px) / (points[i - ps] - px);\n                            newpoints.push(qx);\n                            newpoints.push(intery)\n                            for (m = 2; m < ps; ++m)\n                                newpoints.push(points[i + m]);\n                            bottom = qy; \n                        }\n\n                        j += otherps;\n                    }\n                    else { // px < qx\n                        if (fromgap && withlines) {\n                            // if we come from a gap, we just skip this point\n                            i += ps;\n                            continue;\n                        }\n                            \n                        for (m = 0; m < ps; ++m)\n                            newpoints.push(points[i + m]);\n                        \n                        // we might be able to interpolate a point below,\n                        // this can give us a better y\n                        if (withlines && j > 0 && otherpoints[j - otherps] != null)\n                            bottom = qy + (otherpoints[j - otherps + 1] - qy) * (px - qx) / (otherpoints[j - otherps] - qx);\n\n                        //newpoints[l + 1] += bottom;\n                        \n                        i += ps;\n                    }\n\n                    fromgap = false;\n                    \n                    if (l != newpoints.length && withbottom)\n                        newpoints[l + 2] = bottom;\n                }\n\n                // maintain the line steps invariant\n                if (withsteps && l != newpoints.length && l > 0\n                    && newpoints[l] != null\n                    && newpoints[l] != newpoints[l - ps]\n                    && newpoints[l + 1] != newpoints[l - ps + 1]) {\n                    for (m = 0; m < ps; ++m)\n                        newpoints[l + ps + m] = newpoints[l + m];\n                    newpoints[l + 1] = newpoints[l - ps + 1];\n                }\n            }\n\n            datapoints.points = newpoints;\n        }\n        \n        plot.hooks.processDatapoints.push(computeFillBottoms);\n    }\n    \n    $.plot.plugins.push({\n        init: init,\n        options: options,\n        name: 'fillbetween',\n        version: '1.0'\n    });\n})(jQuery);\n"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/js/flot/jquery.flot.image.js",
    "content": "/*\nFlot plugin for plotting images, e.g. useful for putting ticks on a\nprerendered complex visualization.\n\nThe data syntax is [[image, x1, y1, x2, y2], ...] where (x1, y1) and\n(x2, y2) are where you intend the two opposite corners of the image to\nend up in the plot. Image must be a fully loaded Javascript image (you\ncan make one with new Image()). If the image is not complete, it's\nskipped when plotting.\n\nThere are two helpers included for retrieving images. The easiest work\nthe way that you put in URLs instead of images in the data (like\n[\"myimage.png\", 0, 0, 10, 10]), then call $.plot.image.loadData(data,\noptions, callback) where data and options are the same as you pass in\nto $.plot. This loads the images, replaces the URLs in the data with\nthe corresponding images and calls \"callback\" when all images are\nloaded (or failed loading). In the callback, you can then call $.plot\nwith the data set. See the included example.\n\nA more low-level helper, $.plot.image.load(urls, callback) is also\nincluded. Given a list of URLs, it calls callback with an object\nmapping from URL to Image object when all images are loaded or have\nfailed loading.\n\nOptions for the plugin are\n\n  series: {\n      images: {\n          show: boolean\n          anchor: \"corner\" or \"center\"\n          alpha: [0,1]\n      }\n  }\n\nwhich can be specified for a specific series\n\n  $.plot($(\"#placeholder\"), [{ data: [ ... ], images: { ... } ])\n\nNote that because the data format is different from usual data points,\nyou can't use images with anything else in a specific data series.\n\nSetting \"anchor\" to \"center\" causes the pixels in the image to be\nanchored at the corner pixel centers inside of at the pixel corners,\neffectively letting half a pixel stick out to each side in the plot.\n\n\nA possible future direction could be support for tiling for large\nimages (like Google Maps).\n\n*/\n\n(function ($) {\n    var options = {\n        series: {\n            images: {\n                show: false,\n                alpha: 1,\n                anchor: \"corner\" // or \"center\"\n            }\n        }\n    };\n\n    $.plot.image = {};\n\n    $.plot.image.loadDataImages = function (series, options, callback) {\n        var urls = [], points = [];\n\n        var defaultShow = options.series.images.show;\n        \n        $.each(series, function (i, s) {\n            if (!(defaultShow || s.images.show))\n                return;\n            \n            if (s.data)\n                s = s.data;\n\n            $.each(s, function (i, p) {\n                if (typeof p[0] == \"string\") {\n                    urls.push(p[0]);\n                    points.push(p);\n                }\n            });\n        });\n\n        $.plot.image.load(urls, function (loadedImages) {\n            $.each(points, function (i, p) {\n                var url = p[0];\n                if (loadedImages[url])\n                    p[0] = loadedImages[url];\n            });\n\n            callback();\n        });\n    }\n    \n    $.plot.image.load = function (urls, callback) {\n        var missing = urls.length, loaded = {};\n        if (missing == 0)\n            callback({});\n\n        $.each(urls, function (i, url) {\n            var handler = function () {\n                --missing;\n                \n                loaded[url] = this;\n                \n                if (missing == 0)\n                    callback(loaded);\n            };\n\n            $('<img />').load(handler).error(handler).attr('src', url);\n        });\n    }\n    \n    function drawSeries(plot, ctx, series) {\n        var plotOffset = plot.getPlotOffset();\n        \n        if (!series.images || !series.images.show)\n            return;\n        \n        var points = series.datapoints.points,\n            ps = series.datapoints.pointsize;\n        \n        for (var i = 0; i < points.length; i += ps) {\n            var img = points[i],\n                x1 = points[i + 1], y1 = points[i + 2],\n                x2 = points[i + 3], y2 = points[i + 4],\n                xaxis = series.xaxis, yaxis = series.yaxis,\n                tmp;\n\n            // actually we should check img.complete, but it\n            // appears to be a somewhat unreliable indicator in\n            // IE6 (false even after load event)\n            if (!img || img.width <= 0 || img.height <= 0)\n                continue;\n\n            if (x1 > x2) {\n                tmp = x2;\n                x2 = x1;\n                x1 = tmp;\n            }\n            if (y1 > y2) {\n                tmp = y2;\n                y2 = y1;\n                y1 = tmp;\n            }\n            \n            // if the anchor is at the center of the pixel, expand the \n            // image by 1/2 pixel in each direction\n            if (series.images.anchor == \"center\") {\n                tmp = 0.5 * (x2-x1) / (img.width - 1);\n                x1 -= tmp;\n                x2 += tmp;\n                tmp = 0.5 * (y2-y1) / (img.height - 1);\n                y1 -= tmp;\n                y2 += tmp;\n            }\n            \n            // clip\n            if (x1 == x2 || y1 == y2 ||\n                x1 >= xaxis.max || x2 <= xaxis.min ||\n                y1 >= yaxis.max || y2 <= yaxis.min)\n                continue;\n\n            var sx1 = 0, sy1 = 0, sx2 = img.width, sy2 = img.height;\n            if (x1 < xaxis.min) {\n                sx1 += (sx2 - sx1) * (xaxis.min - x1) / (x2 - x1);\n                x1 = xaxis.min;\n            }\n\n            if (x2 > xaxis.max) {\n                sx2 += (sx2 - sx1) * (xaxis.max - x2) / (x2 - x1);\n                x2 = xaxis.max;\n            }\n\n            if (y1 < yaxis.min) {\n                sy2 += (sy1 - sy2) * (yaxis.min - y1) / (y2 - y1);\n                y1 = yaxis.min;\n            }\n\n            if (y2 > yaxis.max) {\n                sy1 += (sy1 - sy2) * (yaxis.max - y2) / (y2 - y1);\n                y2 = yaxis.max;\n            }\n            \n            x1 = xaxis.p2c(x1);\n            x2 = xaxis.p2c(x2);\n            y1 = yaxis.p2c(y1);\n            y2 = yaxis.p2c(y2);\n            \n            // the transformation may have swapped us\n            if (x1 > x2) {\n                tmp = x2;\n                x2 = x1;\n                x1 = tmp;\n            }\n            if (y1 > y2) {\n                tmp = y2;\n                y2 = y1;\n                y1 = tmp;\n            }\n\n            tmp = ctx.globalAlpha;\n            ctx.globalAlpha *= series.images.alpha;\n            ctx.drawImage(img,\n                          sx1, sy1, sx2 - sx1, sy2 - sy1,\n                          x1 + plotOffset.left, y1 + plotOffset.top,\n                          x2 - x1, y2 - y1);\n            ctx.globalAlpha = tmp;\n        }\n    }\n\n    function processRawData(plot, series, data, datapoints) {\n        if (!series.images.show)\n            return;\n\n        // format is Image, x1, y1, x2, y2 (opposite corners)\n        datapoints.format = [\n            { required: true },\n            { x: true, number: true, required: true },\n            { y: true, number: true, required: true },\n            { x: true, number: true, required: true },\n            { y: true, number: true, required: true }\n        ];\n    }\n    \n    function init(plot) {\n        plot.hooks.processRawData.push(processRawData);\n        plot.hooks.drawSeries.push(drawSeries);\n    }\n    \n    $.plot.plugins.push({\n        init: init,\n        options: options,\n        name: 'image',\n        version: '1.1'\n    });\n})(jQuery);\n"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/js/flot/jquery.flot.js",
    "content": "/*! Javascript plotting library for jQuery, v. 0.7.\n *\n * Released under the MIT license by IOLA, December 2007.\n *\n */\n\n// first an inline dependency, jquery.colorhelpers.js, we inline it here\n// for convenience\n\n/* Plugin for jQuery for working with colors.\n * \n * Version 1.1.\n * \n * Inspiration from jQuery color animation plugin by John Resig.\n *\n * Released under the MIT license by Ole Laursen, October 2009.\n *\n * Examples:\n *\n *   $.color.parse(\"#fff\").scale('rgb', 0.25).add('a', -0.5).toString()\n *   var c = $.color.extract($(\"#mydiv\"), 'background-color');\n *   console.log(c.r, c.g, c.b, c.a);\n *   $.color.make(100, 50, 25, 0.4).toString() // returns \"rgba(100,50,25,0.4)\"\n *\n * Note that .scale() and .add() return the same modified object\n * instead of making a new one.\n *\n * V. 1.1: Fix error handling so e.g. parsing an empty string does\n * produce a color rather than just crashing.\n */ \n(function(B){B.color={};B.color.make=function(F,E,C,D){var G={};G.r=F||0;G.g=E||0;G.b=C||0;G.a=D!=null?D:1;G.add=function(J,I){for(var H=0;H<J.length;++H){G[J.charAt(H)]+=I}return G.normalize()};G.scale=function(J,I){for(var H=0;H<J.length;++H){G[J.charAt(H)]*=I}return G.normalize()};G.toString=function(){if(G.a>=1){return\"rgb(\"+[G.r,G.g,G.b].join(\",\")+\")\"}else{return\"rgba(\"+[G.r,G.g,G.b,G.a].join(\",\")+\")\"}};G.normalize=function(){function H(J,K,I){return K<J?J:(K>I?I:K)}G.r=H(0,parseInt(G.r),255);G.g=H(0,parseInt(G.g),255);G.b=H(0,parseInt(G.b),255);G.a=H(0,G.a,1);return G};G.clone=function(){return B.color.make(G.r,G.b,G.g,G.a)};return G.normalize()};B.color.extract=function(D,C){var E;do{E=D.css(C).toLowerCase();if(E!=\"\"&&E!=\"transparent\"){break}D=D.parent()}while(!B.nodeName(D.get(0),\"body\"));if(E==\"rgba(0, 0, 0, 0)\"){E=\"transparent\"}return B.color.parse(E)};B.color.parse=function(F){var E,C=B.color.make;if(E=/rgb\\(\\s*([0-9]{1,3})\\s*,\\s*([0-9]{1,3})\\s*,\\s*([0-9]{1,3})\\s*\\)/.exec(F)){return C(parseInt(E[1],10),parseInt(E[2],10),parseInt(E[3],10))}if(E=/rgba\\(\\s*([0-9]{1,3})\\s*,\\s*([0-9]{1,3})\\s*,\\s*([0-9]{1,3})\\s*,\\s*([0-9]+(?:\\.[0-9]+)?)\\s*\\)/.exec(F)){return C(parseInt(E[1],10),parseInt(E[2],10),parseInt(E[3],10),parseFloat(E[4]))}if(E=/rgb\\(\\s*([0-9]+(?:\\.[0-9]+)?)\\%\\s*,\\s*([0-9]+(?:\\.[0-9]+)?)\\%\\s*,\\s*([0-9]+(?:\\.[0-9]+)?)\\%\\s*\\)/.exec(F)){return C(parseFloat(E[1])*2.55,parseFloat(E[2])*2.55,parseFloat(E[3])*2.55)}if(E=/rgba\\(\\s*([0-9]+(?:\\.[0-9]+)?)\\%\\s*,\\s*([0-9]+(?:\\.[0-9]+)?)\\%\\s*,\\s*([0-9]+(?:\\.[0-9]+)?)\\%\\s*,\\s*([0-9]+(?:\\.[0-9]+)?)\\s*\\)/.exec(F)){return C(parseFloat(E[1])*2.55,parseFloat(E[2])*2.55,parseFloat(E[3])*2.55,parseFloat(E[4]))}if(E=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(F)){return C(parseInt(E[1],16),parseInt(E[2],16),parseInt(E[3],16))}if(E=/#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(F)){return C(parseInt(E[1]+E[1],16),parseInt(E[2]+E[2],16),parseInt(E[3]+E[3],16))}var D=B.trim(F).toLowerCase();if(D==\"transparent\"){return C(255,255,255,0)}else{E=A[D]||[0,0,0];return C(E[0],E[1],E[2])}};var A={aqua:[0,255,255],azure:[240,255,255],beige:[245,245,220],black:[0,0,0],blue:[0,0,255],brown:[165,42,42],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgrey:[169,169,169],darkgreen:[0,100,0],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkviolet:[148,0,211],fuchsia:[255,0,255],gold:[255,215,0],green:[0,128,0],indigo:[75,0,130],khaki:[240,230,140],lightblue:[173,216,230],lightcyan:[224,255,255],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightyellow:[255,255,224],lime:[0,255,0],magenta:[255,0,255],maroon:[128,0,0],navy:[0,0,128],olive:[128,128,0],orange:[255,165,0],pink:[255,192,203],purple:[128,0,128],violet:[128,0,128],red:[255,0,0],silver:[192,192,192],white:[255,255,255],yellow:[255,255,0]}})(jQuery);\n\n// the actual Flot code\n(function($) {\n    function Plot(placeholder, data_, options_, plugins) {\n        // data is on the form:\n        //   [ series1, series2 ... ]\n        // where series is either just the data as [ [x1, y1], [x2, y2], ... ]\n        // or { data: [ [x1, y1], [x2, y2], ... ], label: \"some label\", ... }\n        \n        var series = [],\n            options = {\n                // the color theme used for graphs\n                colors: [\"#edc240\", \"#afd8f8\", \"#cb4b4b\", \"#4da74d\", \"#9440ed\"],\n                legend: {\n                    show: true,\n                    noColumns: 1, // number of colums in legend table\n                    labelFormatter: null, // fn: string -> string\n                    labelBoxBorderColor: \"#ccc\", // border color for the little label boxes\n                    container: null, // container (as jQuery object) to put legend in, null means default on top of graph\n                    position: \"ne\", // position of default legend container within plot\n                    margin: 5, // distance from grid edge to default legend container within plot\n                    backgroundColor: null, // null means auto-detect\n                    backgroundOpacity: 0.85 // set to 0 to avoid background\n                },\n                xaxis: {\n                    show: null, // null = auto-detect, true = always, false = never\n                    position: \"bottom\", // or \"top\"\n                    mode: null, // null or \"time\"\n                    color: null, // base color, labels, ticks\n                    tickColor: null, // possibly different color of ticks, e.g. \"rgba(0,0,0,0.15)\"\n                    transform: null, // null or f: number -> number to transform axis\n                    inverseTransform: null, // if transform is set, this should be the inverse function\n                    min: null, // min. value to show, null means set automatically\n                    max: null, // max. value to show, null means set automatically\n                    autoscaleMargin: null, // margin in % to add if auto-setting min/max\n                    ticks: null, // either [1, 3] or [[1, \"a\"], 3] or (fn: axis info -> ticks) or app. number of ticks for auto-ticks\n                    tickFormatter: null, // fn: number -> string\n                    labelWidth: null, // size of tick labels in pixels\n                    labelHeight: null,\n                    reserveSpace: null, // whether to reserve space even if axis isn't shown\n                    tickLength: null, // size in pixels of ticks, or \"full\" for whole line\n                    alignTicksWithAxis: null, // axis number or null for no sync\n                    \n                    // mode specific options\n                    tickDecimals: null, // no. of decimals, null means auto\n                    tickSize: null, // number or [number, \"unit\"]\n                    minTickSize: null, // number or [number, \"unit\"]\n                    monthNames: null, // list of names of months\n                    timeformat: null, // format string to use\n                    twelveHourClock: false // 12 or 24 time in time mode\n                },\n                yaxis: {\n                    autoscaleMargin: 0.02,\n                    position: \"left\" // or \"right\"\n                },\n                xaxes: [],\n                yaxes: [],\n                series: {\n                    points: {\n                        show: false,\n                        radius: 3,\n                        lineWidth: 2, // in pixels\n                        fill: true,\n                        fillColor: \"#ffffff\",\n                        symbol: \"circle\" // or callback\n                    },\n                    lines: {\n                        // we don't put in show: false so we can see\n                        // whether lines were actively disabled \n                        lineWidth: 2, // in pixels\n                        fill: false,\n                        fillColor: null,\n                        steps: false\n                    },\n                    bars: {\n                        show: false,\n                        lineWidth: 2, // in pixels\n                        barWidth: 1, // in units of the x axis\n                        fill: true,\n                        fillColor: null,\n                        align: \"left\", // or \"center\" \n                        horizontal: false\n                    },\n                    shadowSize: 3\n                },\n                grid: {\n                    show: true,\n                    aboveData: false,\n                    color: \"#545454\", // primary color used for outline and labels\n                    backgroundColor: null, // null for transparent, else color\n                    borderColor: null, // set if different from the grid color\n                    tickColor: null, // color for the ticks, e.g. \"rgba(0,0,0,0.15)\"\n                    labelMargin: 5, // in pixels\n                    axisMargin: 8, // in pixels\n                    borderWidth: 2, // in pixels\n                    minBorderMargin: null, // in pixels, null means taken from points radius\n                    markings: null, // array of ranges or fn: axes -> array of ranges\n                    markingsColor: \"#f4f4f4\",\n                    markingsLineWidth: 2,\n                    // interactive stuff\n                    clickable: false,\n                    hoverable: false,\n                    autoHighlight: true, // highlight in case mouse is near\n                    mouseActiveRadius: 10 // how far the mouse can be away to activate an item\n                },\n                hooks: {}\n            },\n        canvas = null,      // the canvas for the plot itself\n        overlay = null,     // canvas for interactive stuff on top of plot\n        eventHolder = null, // jQuery object that events should be bound to\n        ctx = null, octx = null,\n        xaxes = [], yaxes = [],\n        plotOffset = { left: 0, right: 0, top: 0, bottom: 0},\n        canvasWidth = 0, canvasHeight = 0,\n        plotWidth = 0, plotHeight = 0,\n        hooks = {\n            processOptions: [],\n            processRawData: [],\n            processDatapoints: [],\n            drawSeries: [],\n            draw: [],\n            bindEvents: [],\n            drawOverlay: [],\n            shutdown: []\n        },\n        plot = this;\n\n        // public functions\n        plot.setData = setData;\n        plot.setupGrid = setupGrid;\n        plot.draw = draw;\n        plot.getPlaceholder = function() { return placeholder; };\n        plot.getCanvas = function() { return canvas; };\n        plot.getPlotOffset = function() { return plotOffset; };\n        plot.width = function () { return plotWidth; };\n        plot.height = function () { return plotHeight; };\n        plot.offset = function () {\n            var o = eventHolder.offset();\n            o.left += plotOffset.left;\n            o.top += plotOffset.top;\n            return o;\n        };\n        plot.getData = function () { return series; };\n        plot.getAxes = function () {\n            var res = {}, i;\n            $.each(xaxes.concat(yaxes), function (_, axis) {\n                if (axis)\n                    res[axis.direction + (axis.n != 1 ? axis.n : \"\") + \"axis\"] = axis;\n            });\n            return res;\n        };\n        plot.getXAxes = function () { return xaxes; };\n        plot.getYAxes = function () { return yaxes; };\n        plot.c2p = canvasToAxisCoords;\n        plot.p2c = axisToCanvasCoords;\n        plot.getOptions = function () { return options; };\n        plot.highlight = highlight;\n        plot.unhighlight = unhighlight;\n        plot.triggerRedrawOverlay = triggerRedrawOverlay;\n        plot.pointOffset = function(point) {\n            return {\n                left: parseInt(xaxes[axisNumber(point, \"x\") - 1].p2c(+point.x) + plotOffset.left),\n                top: parseInt(yaxes[axisNumber(point, \"y\") - 1].p2c(+point.y) + plotOffset.top)\n            };\n        };\n        plot.shutdown = shutdown;\n        plot.resize = function () {\n            getCanvasDimensions();\n            resizeCanvas(canvas);\n            resizeCanvas(overlay);\n        };\n\n        // public attributes\n        plot.hooks = hooks;\n        \n        // initialize\n        initPlugins(plot);\n        parseOptions(options_);\n        setupCanvases();\n        setData(data_);\n        setupGrid();\n        draw();\n        bindEvents();\n\n\n        function executeHooks(hook, args) {\n            args = [plot].concat(args);\n            for (var i = 0; i < hook.length; ++i)\n                hook[i].apply(this, args);\n        }\n\n        function initPlugins() {\n            for (var i = 0; i < plugins.length; ++i) {\n                var p = plugins[i];\n                p.init(plot);\n                if (p.options)\n                    $.extend(true, options, p.options);\n            }\n        }\n        \n        function parseOptions(opts) {\n            var i;\n            \n            $.extend(true, options, opts);\n            \n            if (options.xaxis.color == null)\n                options.xaxis.color = options.grid.color;\n            if (options.yaxis.color == null)\n                options.yaxis.color = options.grid.color;\n            \n            if (options.xaxis.tickColor == null) // backwards-compatibility\n                options.xaxis.tickColor = options.grid.tickColor;\n            if (options.yaxis.tickColor == null) // backwards-compatibility\n                options.yaxis.tickColor = options.grid.tickColor;\n\n            if (options.grid.borderColor == null)\n                options.grid.borderColor = options.grid.color;\n            if (options.grid.tickColor == null)\n                options.grid.tickColor = $.color.parse(options.grid.color).scale('a', 0.22).toString();\n            \n            // fill in defaults in axes, copy at least always the\n            // first as the rest of the code assumes it'll be there\n            for (i = 0; i < Math.max(1, options.xaxes.length); ++i)\n                options.xaxes[i] = $.extend(true, {}, options.xaxis, options.xaxes[i]);\n            for (i = 0; i < Math.max(1, options.yaxes.length); ++i)\n                options.yaxes[i] = $.extend(true, {}, options.yaxis, options.yaxes[i]);\n\n            // backwards compatibility, to be removed in future\n            if (options.xaxis.noTicks && options.xaxis.ticks == null)\n                options.xaxis.ticks = options.xaxis.noTicks;\n            if (options.yaxis.noTicks && options.yaxis.ticks == null)\n                options.yaxis.ticks = options.yaxis.noTicks;\n            if (options.x2axis) {\n                options.xaxes[1] = $.extend(true, {}, options.xaxis, options.x2axis);\n                options.xaxes[1].position = \"top\";\n            }\n            if (options.y2axis) {\n                options.yaxes[1] = $.extend(true, {}, options.yaxis, options.y2axis);\n                options.yaxes[1].position = \"right\";\n            }\n            if (options.grid.coloredAreas)\n                options.grid.markings = options.grid.coloredAreas;\n            if (options.grid.coloredAreasColor)\n                options.grid.markingsColor = options.grid.coloredAreasColor;\n            if (options.lines)\n                $.extend(true, options.series.lines, options.lines);\n            if (options.points)\n                $.extend(true, options.series.points, options.points);\n            if (options.bars)\n                $.extend(true, options.series.bars, options.bars);\n            if (options.shadowSize != null)\n                options.series.shadowSize = options.shadowSize;\n\n            // save options on axes for future reference\n            for (i = 0; i < options.xaxes.length; ++i)\n                getOrCreateAxis(xaxes, i + 1).options = options.xaxes[i];\n            for (i = 0; i < options.yaxes.length; ++i)\n                getOrCreateAxis(yaxes, i + 1).options = options.yaxes[i];\n\n            // add hooks from options\n            for (var n in hooks)\n                if (options.hooks[n] && options.hooks[n].length)\n                    hooks[n] = hooks[n].concat(options.hooks[n]);\n\n            executeHooks(hooks.processOptions, [options]);\n        }\n\n        function setData(d) {\n            series = parseData(d);\n            fillInSeriesOptions();\n            processData();\n        }\n        \n        function parseData(d) {\n            var res = [];\n            for (var i = 0; i < d.length; ++i) {\n                var s = $.extend(true, {}, options.series);\n\n                if (d[i].data != null) {\n                    s.data = d[i].data; // move the data instead of deep-copy\n                    delete d[i].data;\n\n                    $.extend(true, s, d[i]);\n\n                    d[i].data = s.data;\n                }\n                else\n                    s.data = d[i];\n                res.push(s);\n            }\n\n            return res;\n        }\n        \n        function axisNumber(obj, coord) {\n            var a = obj[coord + \"axis\"];\n            if (typeof a == \"object\") // if we got a real axis, extract number\n                a = a.n;\n            if (typeof a != \"number\")\n                a = 1; // default to first axis\n            return a;\n        }\n\n        function allAxes() {\n            // return flat array without annoying null entries\n            return $.grep(xaxes.concat(yaxes), function (a) { return a; });\n        }\n        \n        function canvasToAxisCoords(pos) {\n            // return an object with x/y corresponding to all used axes \n            var res = {}, i, axis;\n            for (i = 0; i < xaxes.length; ++i) {\n                axis = xaxes[i];\n                if (axis && axis.used)\n                    res[\"x\" + axis.n] = axis.c2p(pos.left);\n            }\n\n            for (i = 0; i < yaxes.length; ++i) {\n                axis = yaxes[i];\n                if (axis && axis.used)\n                    res[\"y\" + axis.n] = axis.c2p(pos.top);\n            }\n            \n            if (res.x1 !== undefined)\n                res.x = res.x1;\n            if (res.y1 !== undefined)\n                res.y = res.y1;\n\n            return res;\n        }\n        \n        function axisToCanvasCoords(pos) {\n            // get canvas coords from the first pair of x/y found in pos\n            var res = {}, i, axis, key;\n\n            for (i = 0; i < xaxes.length; ++i) {\n                axis = xaxes[i];\n                if (axis && axis.used) {\n                    key = \"x\" + axis.n;\n                    if (pos[key] == null && axis.n == 1)\n                        key = \"x\";\n\n                    if (pos[key] != null) {\n                        res.left = axis.p2c(pos[key]);\n                        break;\n                    }\n                }\n            }\n            \n            for (i = 0; i < yaxes.length; ++i) {\n                axis = yaxes[i];\n                if (axis && axis.used) {\n                    key = \"y\" + axis.n;\n                    if (pos[key] == null && axis.n == 1)\n                        key = \"y\";\n\n                    if (pos[key] != null) {\n                        res.top = axis.p2c(pos[key]);\n                        break;\n                    }\n                }\n            }\n            \n            return res;\n        }\n        \n        function getOrCreateAxis(axes, number) {\n            if (!axes[number - 1])\n                axes[number - 1] = {\n                    n: number, // save the number for future reference\n                    direction: axes == xaxes ? \"x\" : \"y\",\n                    options: $.extend(true, {}, axes == xaxes ? options.xaxis : options.yaxis)\n                };\n                \n            return axes[number - 1];\n        }\n\n        function fillInSeriesOptions() {\n            var i;\n            \n            // collect what we already got of colors\n            var neededColors = series.length,\n                usedColors = [],\n                assignedColors = [];\n            for (i = 0; i < series.length; ++i) {\n                var sc = series[i].color;\n                if (sc != null) {\n                    --neededColors;\n                    if (typeof sc == \"number\")\n                        assignedColors.push(sc);\n                    else\n                        usedColors.push($.color.parse(series[i].color));\n                }\n            }\n            \n            // we might need to generate more colors if higher indices\n            // are assigned\n            for (i = 0; i < assignedColors.length; ++i) {\n                neededColors = Math.max(neededColors, assignedColors[i] + 1);\n            }\n\n            // produce colors as needed\n            var colors = [], variation = 0;\n            i = 0;\n            while (colors.length < neededColors) {\n                var c;\n                if (options.colors.length == i) // check degenerate case\n                    c = $.color.make(100, 100, 100);\n                else\n                    c = $.color.parse(options.colors[i]);\n\n                // vary color if needed\n                var sign = variation % 2 == 1 ? -1 : 1;\n                c.scale('rgb', 1 + sign * Math.ceil(variation / 2) * 0.2)\n\n                // FIXME: if we're getting to close to something else,\n                // we should probably skip this one\n                colors.push(c);\n                \n                ++i;\n                if (i >= options.colors.length) {\n                    i = 0;\n                    ++variation;\n                }\n            }\n\n            // fill in the options\n            var colori = 0, s;\n            for (i = 0; i < series.length; ++i) {\n                s = series[i];\n                \n                // assign colors\n                if (s.color == null) {\n                    s.color = colors[colori].toString();\n                    ++colori;\n                }\n                else if (typeof s.color == \"number\")\n                    s.color = colors[s.color].toString();\n\n                // turn on lines automatically in case nothing is set\n                if (s.lines.show == null) {\n                    var v, show = true;\n                    for (v in s)\n                        if (s[v] && s[v].show) {\n                            show = false;\n                            break;\n                        }\n                    if (show)\n                        s.lines.show = true;\n                }\n\n                // setup axes\n                s.xaxis = getOrCreateAxis(xaxes, axisNumber(s, \"x\"));\n                s.yaxis = getOrCreateAxis(yaxes, axisNumber(s, \"y\"));\n            }\n        }\n        \n        function processData() {\n            var topSentry = Number.POSITIVE_INFINITY,\n                bottomSentry = Number.NEGATIVE_INFINITY,\n                fakeInfinity = Number.MAX_VALUE,\n                i, j, k, m, length,\n                s, points, ps, x, y, axis, val, f, p;\n\n            function updateAxis(axis, min, max) {\n                if (min < axis.datamin && min != -fakeInfinity)\n                    axis.datamin = min;\n                if (max > axis.datamax && max != fakeInfinity)\n                    axis.datamax = max;\n            }\n\n            $.each(allAxes(), function (_, axis) {\n                // init axis\n                axis.datamin = topSentry;\n                axis.datamax = bottomSentry;\n                axis.used = false;\n            });\n            \n            for (i = 0; i < series.length; ++i) {\n                s = series[i];\n                s.datapoints = { points: [] };\n                \n                executeHooks(hooks.processRawData, [ s, s.data, s.datapoints ]);\n            }\n            \n            // first pass: clean and copy data\n            for (i = 0; i < series.length; ++i) {\n                s = series[i];\n\n                var data = s.data, format = s.datapoints.format;\n\n                if (!format) {\n                    format = [];\n                    // find out how to copy\n                    format.push({ x: true, number: true, required: true });\n                    format.push({ y: true, number: true, required: true });\n\n                    if (s.bars.show || (s.lines.show && s.lines.fill)) {\n                        format.push({ y: true, number: true, required: false, defaultValue: 0 });\n                        if (s.bars.horizontal) {\n                            delete format[format.length - 1].y;\n                            format[format.length - 1].x = true;\n                        }\n                    }\n                    \n                    s.datapoints.format = format;\n                }\n\n                if (s.datapoints.pointsize != null)\n                    continue; // already filled in\n\n                s.datapoints.pointsize = format.length;\n                \n                ps = s.datapoints.pointsize;\n                points = s.datapoints.points;\n\n                insertSteps = s.lines.show && s.lines.steps;\n                s.xaxis.used = s.yaxis.used = true;\n                \n                for (j = k = 0; j < data.length; ++j, k += ps) {\n                    p = data[j];\n\n                    var nullify = p == null;\n                    if (!nullify) {\n                        for (m = 0; m < ps; ++m) {\n                            val = p[m];\n                            f = format[m];\n\n                            if (f) {\n                                if (f.number && val != null) {\n                                    val = +val; // convert to number\n                                    if (isNaN(val))\n                                        val = null;\n                                    else if (val == Infinity)\n                                        val = fakeInfinity;\n                                    else if (val == -Infinity)\n                                        val = -fakeInfinity;\n                                }\n\n                                if (val == null) {\n                                    if (f.required)\n                                        nullify = true;\n                                    \n                                    if (f.defaultValue != null)\n                                        val = f.defaultValue;\n                                }\n                            }\n                            \n                            points[k + m] = val;\n                        }\n                    }\n                    \n                    if (nullify) {\n                        for (m = 0; m < ps; ++m) {\n                            val = points[k + m];\n                            if (val != null) {\n                                f = format[m];\n                                // extract min/max info\n                                if (f.x)\n                                    updateAxis(s.xaxis, val, val);\n                                if (f.y)\n                                    updateAxis(s.yaxis, val, val);\n                            }\n                            points[k + m] = null;\n                        }\n                    }\n                    else {\n                        // a little bit of line specific stuff that\n                        // perhaps shouldn't be here, but lacking\n                        // better means...\n                        if (insertSteps && k > 0\n                            && points[k - ps] != null\n                            && points[k - ps] != points[k]\n                            && points[k - ps + 1] != points[k + 1]) {\n                            // copy the point to make room for a middle point\n                            for (m = 0; m < ps; ++m)\n                                points[k + ps + m] = points[k + m];\n\n                            // middle point has same y\n                            points[k + 1] = points[k - ps + 1];\n\n                            // we've added a point, better reflect that\n                            k += ps;\n                        }\n                    }\n                }\n            }\n\n            // give the hooks a chance to run\n            for (i = 0; i < series.length; ++i) {\n                s = series[i];\n                \n                executeHooks(hooks.processDatapoints, [ s, s.datapoints]);\n            }\n\n            // second pass: find datamax/datamin for auto-scaling\n            for (i = 0; i < series.length; ++i) {\n                s = series[i];\n                points = s.datapoints.points,\n                ps = s.datapoints.pointsize;\n\n                var xmin = topSentry, ymin = topSentry,\n                    xmax = bottomSentry, ymax = bottomSentry;\n                \n                for (j = 0; j < points.length; j += ps) {\n                    if (points[j] == null)\n                        continue;\n\n                    for (m = 0; m < ps; ++m) {\n                        val = points[j + m];\n                        f = format[m];\n                        if (!f || val == fakeInfinity || val == -fakeInfinity)\n                            continue;\n                        \n                        if (f.x) {\n                            if (val < xmin)\n                                xmin = val;\n                            if (val > xmax)\n                                xmax = val;\n                        }\n                        if (f.y) {\n                            if (val < ymin)\n                                ymin = val;\n                            if (val > ymax)\n                                ymax = val;\n                        }\n                    }\n                }\n                \n                if (s.bars.show) {\n                    // make sure we got room for the bar on the dancing floor\n                    var delta = s.bars.align == \"left\" ? 0 : -s.bars.barWidth/2;\n                    if (s.bars.horizontal) {\n                        ymin += delta;\n                        ymax += delta + s.bars.barWidth;\n                    }\n                    else {\n                        xmin += delta;\n                        xmax += delta + s.bars.barWidth;\n                    }\n                }\n                \n                updateAxis(s.xaxis, xmin, xmax);\n                updateAxis(s.yaxis, ymin, ymax);\n            }\n\n            $.each(allAxes(), function (_, axis) {\n                if (axis.datamin == topSentry)\n                    axis.datamin = null;\n                if (axis.datamax == bottomSentry)\n                    axis.datamax = null;\n            });\n        }\n\n        function makeCanvas(skipPositioning, cls) {\n            var c = document.createElement('canvas');\n            c.className = cls;\n            c.width = canvasWidth;\n            c.height = canvasHeight;\n                    \n            if (!skipPositioning)\n                $(c).css({ position: 'absolute', left: 0, top: 0 });\n                \n            $(c).appendTo(placeholder);\n                \n            if (!c.getContext) // excanvas hack\n                c = window.G_vmlCanvasManager.initElement(c);\n\n            // used for resetting in case we get replotted\n            c.getContext(\"2d\").save();\n            \n            return c;\n        }\n\n        function getCanvasDimensions() {\n            canvasWidth = placeholder.width();\n            canvasHeight = placeholder.height();\n            \n            if (canvasWidth <= 0 || canvasHeight <= 0)\n                throw \"Invalid dimensions for plot, width = \" + canvasWidth + \", height = \" + canvasHeight;\n        }\n\n        function resizeCanvas(c) {\n            // resizing should reset the state (excanvas seems to be\n            // buggy though)\n            if (c.width != canvasWidth)\n                c.width = canvasWidth;\n\n            if (c.height != canvasHeight)\n                c.height = canvasHeight;\n\n            // so try to get back to the initial state (even if it's\n            // gone now, this should be safe according to the spec)\n            var cctx = c.getContext(\"2d\");\n            cctx.restore();\n\n            // and save again\n            cctx.save();\n        }\n        \n        function setupCanvases() {\n            var reused,\n                existingCanvas = placeholder.children(\"canvas.base\"),\n                existingOverlay = placeholder.children(\"canvas.overlay\");\n\n            if (existingCanvas.length == 0 || existingOverlay == 0) {\n                // init everything\n                \n                placeholder.html(\"\"); // make sure placeholder is clear\n            \n                placeholder.css({ padding: 0 }); // padding messes up the positioning\n                \n                if (placeholder.css(\"position\") == 'static')\n                    placeholder.css(\"position\", \"relative\"); // for positioning labels and overlay\n\n                getCanvasDimensions();\n                \n                canvas = makeCanvas(true, \"base\");\n                overlay = makeCanvas(false, \"overlay\"); // overlay canvas for interactive features\n\n                reused = false;\n            }\n            else {\n                // reuse existing elements\n\n                canvas = existingCanvas.get(0);\n                overlay = existingOverlay.get(0);\n\n                reused = true;\n            }\n\n            ctx = canvas.getContext(\"2d\");\n            octx = overlay.getContext(\"2d\");\n\n            // we include the canvas in the event holder too, because IE 7\n            // sometimes has trouble with the stacking order\n            eventHolder = $([overlay, canvas]);\n\n            if (reused) {\n                // run shutdown in the old plot object\n                placeholder.data(\"plot\").shutdown();\n\n                // reset reused canvases\n                plot.resize();\n                \n                // make sure overlay pixels are cleared (canvas is cleared when we redraw)\n                octx.clearRect(0, 0, canvasWidth, canvasHeight);\n                \n                // then whack any remaining obvious garbage left\n                eventHolder.unbind();\n                placeholder.children().not([canvas, overlay]).remove();\n            }\n\n            // save in case we get replotted\n            placeholder.data(\"plot\", plot);\n        }\n\n        function bindEvents() {\n            // bind events\n            if (options.grid.hoverable) {\n                eventHolder.mousemove(onMouseMove);\n                eventHolder.mouseleave(onMouseLeave);\n            }\n\n            if (options.grid.clickable)\n                eventHolder.click(onClick);\n\n            executeHooks(hooks.bindEvents, [eventHolder]);\n        }\n\n        function shutdown() {\n            if (redrawTimeout)\n                clearTimeout(redrawTimeout);\n            \n            eventHolder.unbind(\"mousemove\", onMouseMove);\n            eventHolder.unbind(\"mouseleave\", onMouseLeave);\n            eventHolder.unbind(\"click\", onClick);\n            \n            executeHooks(hooks.shutdown, [eventHolder]);\n        }\n\n        function setTransformationHelpers(axis) {\n            // set helper functions on the axis, assumes plot area\n            // has been computed already\n            \n            function identity(x) { return x; }\n            \n            var s, m, t = axis.options.transform || identity,\n                it = axis.options.inverseTransform;\n            \n            // precompute how much the axis is scaling a point\n            // in canvas space\n            if (axis.direction == \"x\") {\n                s = axis.scale = plotWidth / Math.abs(t(axis.max) - t(axis.min));\n                m = Math.min(t(axis.max), t(axis.min));\n            }\n            else {\n                s = axis.scale = plotHeight / Math.abs(t(axis.max) - t(axis.min));\n                s = -s;\n                m = Math.max(t(axis.max), t(axis.min));\n            }\n\n            // data point to canvas coordinate\n            if (t == identity) // slight optimization\n                axis.p2c = function (p) { return (p - m) * s; };\n            else\n                axis.p2c = function (p) { return (t(p) - m) * s; };\n            // canvas coordinate to data point\n            if (!it)\n                axis.c2p = function (c) { return m + c / s; };\n            else\n                axis.c2p = function (c) { return it(m + c / s); };\n        }\n\n        function measureTickLabels(axis) {\n            var opts = axis.options, i, ticks = axis.ticks || [], labels = [],\n                l, w = opts.labelWidth, h = opts.labelHeight, dummyDiv;\n\n            function makeDummyDiv(labels, width) {\n                return $('<div style=\"position:absolute;top:-10000px;' + width + 'font-size:smaller\">' +\n                         '<div class=\"' + axis.direction + 'Axis ' + axis.direction + axis.n + 'Axis\">'\n                         + labels.join(\"\") + '</div></div>')\n                    .appendTo(placeholder);\n            }\n            \n            if (axis.direction == \"x\") {\n                // to avoid measuring the widths of the labels (it's slow), we\n                // construct fixed-size boxes and put the labels inside\n                // them, we don't need the exact figures and the\n                // fixed-size box content is easy to center\n                if (w == null)\n                    w = Math.floor(canvasWidth / (ticks.length > 0 ? ticks.length : 1));\n\n                // measure x label heights\n                if (h == null) {\n                    labels = [];\n                    for (i = 0; i < ticks.length; ++i) {\n                        l = ticks[i].label;\n                        if (l)\n                            labels.push('<div class=\"tickLabel\" style=\"float:left;width:' + w + 'px\">' + l + '</div>');\n                    }\n\n                    if (labels.length > 0) {\n                        // stick them all in the same div and measure\n                        // collective height\n                        labels.push('<div style=\"clear:left\"></div>');\n                        dummyDiv = makeDummyDiv(labels, \"width:10000px;\");\n                        h = dummyDiv.height();\n                        dummyDiv.remove();\n                    }\n                }\n            }\n            else if (w == null || h == null) {\n                // calculate y label dimensions\n                for (i = 0; i < ticks.length; ++i) {\n                    l = ticks[i].label;\n                    if (l)\n                        labels.push('<div class=\"tickLabel\">' + l + '</div>');\n                }\n                \n                if (labels.length > 0) {\n                    dummyDiv = makeDummyDiv(labels, \"\");\n                    if (w == null)\n                        w = dummyDiv.children().width();\n                    if (h == null)\n                        h = dummyDiv.find(\"div.tickLabel\").height();\n                    dummyDiv.remove();\n                }\n            }\n\n            if (w == null)\n                w = 0;\n            if (h == null)\n                h = 0;\n\n            axis.labelWidth = w;\n            axis.labelHeight = h;\n        }\n\n        function allocateAxisBoxFirstPhase(axis) {\n            // find the bounding box of the axis by looking at label\n            // widths/heights and ticks, make room by diminishing the\n            // plotOffset\n\n            var lw = axis.labelWidth,\n                lh = axis.labelHeight,\n                pos = axis.options.position,\n                tickLength = axis.options.tickLength,\n                axismargin = options.grid.axisMargin,\n                padding = options.grid.labelMargin,\n                all = axis.direction == \"x\" ? xaxes : yaxes,\n                index;\n\n            // determine axis margin\n            var samePosition = $.grep(all, function (a) {\n                return a && a.options.position == pos && a.reserveSpace;\n            });\n            if ($.inArray(axis, samePosition) == samePosition.length - 1)\n                axismargin = 0; // outermost\n\n            // determine tick length - if we're innermost, we can use \"full\"\n            if (tickLength == null)\n                tickLength = \"full\";\n\n            var sameDirection = $.grep(all, function (a) {\n                return a && a.reserveSpace;\n            });\n\n            var innermost = $.inArray(axis, sameDirection) == 0;\n            if (!innermost && tickLength == \"full\")\n                tickLength = 5;\n                \n            if (!isNaN(+tickLength))\n                padding += +tickLength;\n\n            // compute box\n            if (axis.direction == \"x\") {\n                lh += padding;\n                \n                if (pos == \"bottom\") {\n                    plotOffset.bottom += lh + axismargin;\n                    axis.box = { top: canvasHeight - plotOffset.bottom, height: lh };\n                }\n                else {\n                    axis.box = { top: plotOffset.top + axismargin, height: lh };\n                    plotOffset.top += lh + axismargin;\n                }\n            }\n            else {\n                lw += padding;\n                \n                if (pos == \"left\") {\n                    axis.box = { left: plotOffset.left + axismargin, width: lw };\n                    plotOffset.left += lw + axismargin;\n                }\n                else {\n                    plotOffset.right += lw + axismargin;\n                    axis.box = { left: canvasWidth - plotOffset.right, width: lw };\n                }\n            }\n\n             // save for future reference\n            axis.position = pos;\n            axis.tickLength = tickLength;\n            axis.box.padding = padding;\n            axis.innermost = innermost;\n        }\n\n        function allocateAxisBoxSecondPhase(axis) {\n            // set remaining bounding box coordinates\n            if (axis.direction == \"x\") {\n                axis.box.left = plotOffset.left;\n                axis.box.width = plotWidth;\n            }\n            else {\n                axis.box.top = plotOffset.top;\n                axis.box.height = plotHeight;\n            }\n        }\n        \n        function setupGrid() {\n            var i, axes = allAxes();\n\n            // first calculate the plot and axis box dimensions\n\n            $.each(axes, function (_, axis) {\n                axis.show = axis.options.show;\n                if (axis.show == null)\n                    axis.show = axis.used; // by default an axis is visible if it's got data\n                \n                axis.reserveSpace = axis.show || axis.options.reserveSpace;\n\n                setRange(axis);\n            });\n\n            allocatedAxes = $.grep(axes, function (axis) { return axis.reserveSpace; });\n\n            plotOffset.left = plotOffset.right = plotOffset.top = plotOffset.bottom = 0;\n            if (options.grid.show) {\n                $.each(allocatedAxes, function (_, axis) {\n                    // make the ticks\n                    setupTickGeneration(axis);\n                    setTicks(axis);\n                    snapRangeToTicks(axis, axis.ticks);\n\n                    // find labelWidth/Height for axis\n                    measureTickLabels(axis);\n                });\n\n                // with all dimensions in house, we can compute the\n                // axis boxes, start from the outside (reverse order)\n                for (i = allocatedAxes.length - 1; i >= 0; --i)\n                    allocateAxisBoxFirstPhase(allocatedAxes[i]);\n\n                // make sure we've got enough space for things that\n                // might stick out\n                var minMargin = options.grid.minBorderMargin;\n                if (minMargin == null) {\n                    minMargin = 0;\n                    for (i = 0; i < series.length; ++i)\n                        minMargin = Math.max(minMargin, series[i].points.radius + series[i].points.lineWidth/2);\n                }\n                    \n                for (var a in plotOffset) {\n                    plotOffset[a] += options.grid.borderWidth;\n                    plotOffset[a] = Math.max(minMargin, plotOffset[a]);\n                }\n            }\n            \n            plotWidth = canvasWidth - plotOffset.left - plotOffset.right;\n            plotHeight = canvasHeight - plotOffset.bottom - plotOffset.top;\n\n            // now we got the proper plotWidth/Height, we can compute the scaling\n            $.each(axes, function (_, axis) {\n                setTransformationHelpers(axis);\n            });\n\n            if (options.grid.show) {\n                $.each(allocatedAxes, function (_, axis) {\n                    allocateAxisBoxSecondPhase(axis);\n                });\n\n                insertAxisLabels();\n            }\n            \n            insertLegend();\n        }\n        \n        function setRange(axis) {\n            var opts = axis.options,\n                min = +(opts.min != null ? opts.min : axis.datamin),\n                max = +(opts.max != null ? opts.max : axis.datamax),\n                delta = max - min;\n\n            if (delta == 0.0) {\n                // degenerate case\n                var widen = max == 0 ? 1 : 0.01;\n\n                if (opts.min == null)\n                    min -= widen;\n                // always widen max if we couldn't widen min to ensure we\n                // don't fall into min == max which doesn't work\n                if (opts.max == null || opts.min != null)\n                    max += widen;\n            }\n            else {\n                // consider autoscaling\n                var margin = opts.autoscaleMargin;\n                if (margin != null) {\n                    if (opts.min == null) {\n                        min -= delta * margin;\n                        // make sure we don't go below zero if all values\n                        // are positive\n                        if (min < 0 && axis.datamin != null && axis.datamin >= 0)\n                            min = 0;\n                    }\n                    if (opts.max == null) {\n                        max += delta * margin;\n                        if (max > 0 && axis.datamax != null && axis.datamax <= 0)\n                            max = 0;\n                    }\n                }\n            }\n            axis.min = min;\n            axis.max = max;\n        }\n\n        function setupTickGeneration(axis) {\n            var opts = axis.options;\n                \n            // estimate number of ticks\n            var noTicks;\n            if (typeof opts.ticks == \"number\" && opts.ticks > 0)\n                noTicks = opts.ticks;\n            else\n                // heuristic based on the model a*sqrt(x) fitted to\n                // some data points that seemed reasonable\n                noTicks = 0.3 * Math.sqrt(axis.direction == \"x\" ? canvasWidth : canvasHeight);\n\n            var delta = (axis.max - axis.min) / noTicks,\n                size, generator, unit, formatter, i, magn, norm;\n\n            if (opts.mode == \"time\") {\n                // pretty handling of time\n                \n                // map of app. size of time units in milliseconds\n                var timeUnitSize = {\n                    \"second\": 1000,\n                    \"minute\": 60 * 1000,\n                    \"hour\": 60 * 60 * 1000,\n                    \"day\": 24 * 60 * 60 * 1000,\n                    \"month\": 30 * 24 * 60 * 60 * 1000,\n                    \"year\": 365.2425 * 24 * 60 * 60 * 1000\n                };\n\n\n                // the allowed tick sizes, after 1 year we use\n                // an integer algorithm\n                var spec = [\n                    [1, \"second\"], [2, \"second\"], [5, \"second\"], [10, \"second\"],\n                    [30, \"second\"], \n                    [1, \"minute\"], [2, \"minute\"], [5, \"minute\"], [10, \"minute\"],\n                    [30, \"minute\"], \n                    [1, \"hour\"], [2, \"hour\"], [4, \"hour\"],\n                    [8, \"hour\"], [12, \"hour\"],\n                    [1, \"day\"], [2, \"day\"], [3, \"day\"],\n                    [0.25, \"month\"], [0.5, \"month\"], [1, \"month\"],\n                    [2, \"month\"], [3, \"month\"], [6, \"month\"],\n                    [1, \"year\"]\n                ];\n\n                var minSize = 0;\n                if (opts.minTickSize != null) {\n                    if (typeof opts.tickSize == \"number\")\n                        minSize = opts.tickSize;\n                    else\n                        minSize = opts.minTickSize[0] * timeUnitSize[opts.minTickSize[1]];\n                }\n\n                for (var i = 0; i < spec.length - 1; ++i)\n                    if (delta < (spec[i][0] * timeUnitSize[spec[i][1]]\n                                 + spec[i + 1][0] * timeUnitSize[spec[i + 1][1]]) / 2\n                       && spec[i][0] * timeUnitSize[spec[i][1]] >= minSize)\n                        break;\n                size = spec[i][0];\n                unit = spec[i][1];\n                \n                // special-case the possibility of several years\n                if (unit == \"year\") {\n                    magn = Math.pow(10, Math.floor(Math.log(delta / timeUnitSize.year) / Math.LN10));\n                    norm = (delta / timeUnitSize.year) / magn;\n                    if (norm < 1.5)\n                        size = 1;\n                    else if (norm < 3)\n                        size = 2;\n                    else if (norm < 7.5)\n                        size = 5;\n                    else\n                        size = 10;\n\n                    size *= magn;\n                }\n\n                axis.tickSize = opts.tickSize || [size, unit];\n                \n                generator = function(axis) {\n                    var ticks = [],\n                        tickSize = axis.tickSize[0], unit = axis.tickSize[1],\n                        d = new Date(axis.min);\n                    \n                    var step = tickSize * timeUnitSize[unit];\n\n                    if (unit == \"second\")\n                        d.setUTCSeconds(floorInBase(d.getUTCSeconds(), tickSize));\n                    if (unit == \"minute\")\n                        d.setUTCMinutes(floorInBase(d.getUTCMinutes(), tickSize));\n                    if (unit == \"hour\")\n                        d.setUTCHours(floorInBase(d.getUTCHours(), tickSize));\n                    if (unit == \"month\")\n                        d.setUTCMonth(floorInBase(d.getUTCMonth(), tickSize));\n                    if (unit == \"year\")\n                        d.setUTCFullYear(floorInBase(d.getUTCFullYear(), tickSize));\n                    \n                    // reset smaller components\n                    d.setUTCMilliseconds(0);\n                    if (step >= timeUnitSize.minute)\n                        d.setUTCSeconds(0);\n                    if (step >= timeUnitSize.hour)\n                        d.setUTCMinutes(0);\n                    if (step >= timeUnitSize.day)\n                        d.setUTCHours(0);\n                    if (step >= timeUnitSize.day * 4)\n                        d.setUTCDate(1);\n                    if (step >= timeUnitSize.year)\n                        d.setUTCMonth(0);\n\n\n                    var carry = 0, v = Number.NaN, prev;\n                    do {\n                        prev = v;\n                        v = d.getTime();\n                        ticks.push(v);\n                        if (unit == \"month\") {\n                            if (tickSize < 1) {\n                                // a bit complicated - we'll divide the month\n                                // up but we need to take care of fractions\n                                // so we don't end up in the middle of a day\n                                d.setUTCDate(1);\n                                var start = d.getTime();\n                                d.setUTCMonth(d.getUTCMonth() + 1);\n                                var end = d.getTime();\n                                d.setTime(v + carry * timeUnitSize.hour + (end - start) * tickSize);\n                                carry = d.getUTCHours();\n                                d.setUTCHours(0);\n                            }\n                            else\n                                d.setUTCMonth(d.getUTCMonth() + tickSize);\n                        }\n                        else if (unit == \"year\") {\n                            d.setUTCFullYear(d.getUTCFullYear() + tickSize);\n                        }\n                        else\n                            d.setTime(v + step);\n                    } while (v < axis.max && v != prev);\n\n                    return ticks;\n                };\n\n                formatter = function (v, axis) {\n                    var d = new Date(v);\n\n                    // first check global format\n                    if (opts.timeformat != null)\n                        return $.plot.formatDate(d, opts.timeformat, opts.monthNames);\n                    \n                    var t = axis.tickSize[0] * timeUnitSize[axis.tickSize[1]];\n                    var span = axis.max - axis.min;\n                    var suffix = (opts.twelveHourClock) ? \" %p\" : \"\";\n                    \n                    if (t < timeUnitSize.minute)\n                        fmt = \"%h:%M:%S\" + suffix;\n                    else if (t < timeUnitSize.day) {\n                        if (span < 2 * timeUnitSize.day)\n                            fmt = \"%h:%M\" + suffix;\n                        else\n                            fmt = \"%b %d %h:%M\" + suffix;\n                    }\n                    else if (t < timeUnitSize.month)\n                        fmt = \"%b %d\";\n                    else if (t < timeUnitSize.year) {\n                        if (span < timeUnitSize.year)\n                            fmt = \"%b\";\n                        else\n                            fmt = \"%b %y\";\n                    }\n                    else\n                        fmt = \"%y\";\n                    \n                    return $.plot.formatDate(d, fmt, opts.monthNames);\n                };\n            }\n            else {\n                // pretty rounding of base-10 numbers\n                var maxDec = opts.tickDecimals;\n                var dec = -Math.floor(Math.log(delta) / Math.LN10);\n                if (maxDec != null && dec > maxDec)\n                    dec = maxDec;\n\n                magn = Math.pow(10, -dec);\n                norm = delta / magn; // norm is between 1.0 and 10.0\n                \n                if (norm < 1.5)\n                    size = 1;\n                else if (norm < 3) {\n                    size = 2;\n                    // special case for 2.5, requires an extra decimal\n                    if (norm > 2.25 && (maxDec == null || dec + 1 <= maxDec)) {\n                        size = 2.5;\n                        ++dec;\n                    }\n                }\n                else if (norm < 7.5)\n                    size = 5;\n                else\n                    size = 10;\n\n                size *= magn;\n                \n                if (opts.minTickSize != null && size < opts.minTickSize)\n                    size = opts.minTickSize;\n\n                axis.tickDecimals = Math.max(0, maxDec != null ? maxDec : dec);\n                axis.tickSize = opts.tickSize || size;\n\n                generator = function (axis) {\n                    var ticks = [];\n\n                    // spew out all possible ticks\n                    var start = floorInBase(axis.min, axis.tickSize),\n                        i = 0, v = Number.NaN, prev;\n                    do {\n                        prev = v;\n                        v = start + i * axis.tickSize;\n                        ticks.push(v);\n                        ++i;\n                    } while (v < axis.max && v != prev);\n                    return ticks;\n                };\n\n                formatter = function (v, axis) {\n                    return v.toFixed(axis.tickDecimals);\n                };\n            }\n\n            if (opts.alignTicksWithAxis != null) {\n                var otherAxis = (axis.direction == \"x\" ? xaxes : yaxes)[opts.alignTicksWithAxis - 1];\n                if (otherAxis && otherAxis.used && otherAxis != axis) {\n                    // consider snapping min/max to outermost nice ticks\n                    var niceTicks = generator(axis);\n                    if (niceTicks.length > 0) {\n                        if (opts.min == null)\n                            axis.min = Math.min(axis.min, niceTicks[0]);\n                        if (opts.max == null && niceTicks.length > 1)\n                            axis.max = Math.max(axis.max, niceTicks[niceTicks.length - 1]);\n                    }\n                    \n                    generator = function (axis) {\n                        // copy ticks, scaled to this axis\n                        var ticks = [], v, i;\n                        for (i = 0; i < otherAxis.ticks.length; ++i) {\n                            v = (otherAxis.ticks[i].v - otherAxis.min) / (otherAxis.max - otherAxis.min);\n                            v = axis.min + v * (axis.max - axis.min);\n                            ticks.push(v);\n                        }\n                        return ticks;\n                    };\n                    \n                    // we might need an extra decimal since forced\n                    // ticks don't necessarily fit naturally\n                    if (axis.mode != \"time\" && opts.tickDecimals == null) {\n                        var extraDec = Math.max(0, -Math.floor(Math.log(delta) / Math.LN10) + 1),\n                            ts = generator(axis);\n\n                        // only proceed if the tick interval rounded\n                        // with an extra decimal doesn't give us a\n                        // zero at end\n                        if (!(ts.length > 1 && /\\..*0$/.test((ts[1] - ts[0]).toFixed(extraDec))))\n                            axis.tickDecimals = extraDec;\n                    }\n                }\n            }\n\n            axis.tickGenerator = generator;\n            if ($.isFunction(opts.tickFormatter))\n                axis.tickFormatter = function (v, axis) { return \"\" + opts.tickFormatter(v, axis); };\n            else\n                axis.tickFormatter = formatter;\n        }\n        \n        function setTicks(axis) {\n            var oticks = axis.options.ticks, ticks = [];\n            if (oticks == null || (typeof oticks == \"number\" && oticks > 0))\n                ticks = axis.tickGenerator(axis);\n            else if (oticks) {\n                if ($.isFunction(oticks))\n                    // generate the ticks\n                    ticks = oticks({ min: axis.min, max: axis.max });\n                else\n                    ticks = oticks;\n            }\n\n            // clean up/labelify the supplied ticks, copy them over\n            var i, v;\n            axis.ticks = [];\n            for (i = 0; i < ticks.length; ++i) {\n                var label = null;\n                var t = ticks[i];\n                if (typeof t == \"object\") {\n                    v = +t[0];\n                    if (t.length > 1)\n                        label = t[1];\n                }\n                else\n                    v = +t;\n                if (label == null)\n                    label = axis.tickFormatter(v, axis);\n                if (!isNaN(v))\n                    axis.ticks.push({ v: v, label: label });\n            }\n        }\n\n        function snapRangeToTicks(axis, ticks) {\n            if (axis.options.autoscaleMargin && ticks.length > 0) {\n                // snap to ticks\n                if (axis.options.min == null)\n                    axis.min = Math.min(axis.min, ticks[0].v);\n                if (axis.options.max == null && ticks.length > 1)\n                    axis.max = Math.max(axis.max, ticks[ticks.length - 1].v);\n            }\n        }\n      \n        function draw() {\n            ctx.clearRect(0, 0, canvasWidth, canvasHeight);\n\n            var grid = options.grid;\n\n            // draw background, if any\n            if (grid.show && grid.backgroundColor)\n                drawBackground();\n            \n            if (grid.show && !grid.aboveData)\n                drawGrid();\n\n            for (var i = 0; i < series.length; ++i) {\n                executeHooks(hooks.drawSeries, [ctx, series[i]]);\n                drawSeries(series[i]);\n            }\n\n            executeHooks(hooks.draw, [ctx]);\n            \n            if (grid.show && grid.aboveData)\n                drawGrid();\n        }\n\n        function extractRange(ranges, coord) {\n            var axis, from, to, key, axes = allAxes();\n\n            for (i = 0; i < axes.length; ++i) {\n                axis = axes[i];\n                if (axis.direction == coord) {\n                    key = coord + axis.n + \"axis\";\n                    if (!ranges[key] && axis.n == 1)\n                        key = coord + \"axis\"; // support x1axis as xaxis\n                    if (ranges[key]) {\n                        from = ranges[key].from;\n                        to = ranges[key].to;\n                        break;\n                    }\n                }\n            }\n\n            // backwards-compat stuff - to be removed in future\n            if (!ranges[key]) {\n                axis = coord == \"x\" ? xaxes[0] : yaxes[0];\n                from = ranges[coord + \"1\"];\n                to = ranges[coord + \"2\"];\n            }\n\n            // auto-reverse as an added bonus\n            if (from != null && to != null && from > to) {\n                var tmp = from;\n                from = to;\n                to = tmp;\n            }\n            \n            return { from: from, to: to, axis: axis };\n        }\n        \n        function drawBackground() {\n            ctx.save();\n            ctx.translate(plotOffset.left, plotOffset.top);\n\n            ctx.fillStyle = getColorOrGradient(options.grid.backgroundColor, plotHeight, 0, \"rgba(255, 255, 255, 0)\");\n            ctx.fillRect(0, 0, plotWidth, plotHeight);\n            ctx.restore();\n        }\n\n        function drawGrid() {\n            var i;\n            \n            ctx.save();\n            ctx.translate(plotOffset.left, plotOffset.top);\n\n            // draw markings\n            var markings = options.grid.markings;\n            if (markings) {\n                if ($.isFunction(markings)) {\n                    var axes = plot.getAxes();\n                    // xmin etc. is backwards compatibility, to be\n                    // removed in the future\n                    axes.xmin = axes.xaxis.min;\n                    axes.xmax = axes.xaxis.max;\n                    axes.ymin = axes.yaxis.min;\n                    axes.ymax = axes.yaxis.max;\n                    \n                    markings = markings(axes);\n                }\n\n                for (i = 0; i < markings.length; ++i) {\n                    var m = markings[i],\n                        xrange = extractRange(m, \"x\"),\n                        yrange = extractRange(m, \"y\");\n\n                    // fill in missing\n                    if (xrange.from == null)\n                        xrange.from = xrange.axis.min;\n                    if (xrange.to == null)\n                        xrange.to = xrange.axis.max;\n                    if (yrange.from == null)\n                        yrange.from = yrange.axis.min;\n                    if (yrange.to == null)\n                        yrange.to = yrange.axis.max;\n\n                    // clip\n                    if (xrange.to < xrange.axis.min || xrange.from > xrange.axis.max ||\n                        yrange.to < yrange.axis.min || yrange.from > yrange.axis.max)\n                        continue;\n\n                    xrange.from = Math.max(xrange.from, xrange.axis.min);\n                    xrange.to = Math.min(xrange.to, xrange.axis.max);\n                    yrange.from = Math.max(yrange.from, yrange.axis.min);\n                    yrange.to = Math.min(yrange.to, yrange.axis.max);\n\n                    if (xrange.from == xrange.to && yrange.from == yrange.to)\n                        continue;\n\n                    // then draw\n                    xrange.from = xrange.axis.p2c(xrange.from);\n                    xrange.to = xrange.axis.p2c(xrange.to);\n                    yrange.from = yrange.axis.p2c(yrange.from);\n                    yrange.to = yrange.axis.p2c(yrange.to);\n                    \n                    if (xrange.from == xrange.to || yrange.from == yrange.to) {\n                        // draw line\n                        ctx.beginPath();\n                        ctx.strokeStyle = m.color || options.grid.markingsColor;\n                        ctx.lineWidth = m.lineWidth || options.grid.markingsLineWidth;\n                        ctx.moveTo(xrange.from, yrange.from);\n                        ctx.lineTo(xrange.to, yrange.to);\n                        ctx.stroke();\n                    }\n                    else {\n                        // fill area\n                        ctx.fillStyle = m.color || options.grid.markingsColor;\n                        ctx.fillRect(xrange.from, yrange.to,\n                                     xrange.to - xrange.from,\n                                     yrange.from - yrange.to);\n                    }\n                }\n            }\n            \n            // draw the ticks\n            var axes = allAxes(), bw = options.grid.borderWidth;\n\n            for (var j = 0; j < axes.length; ++j) {\n                var axis = axes[j], box = axis.box,\n                    t = axis.tickLength, x, y, xoff, yoff;\n                if (!axis.show || axis.ticks.length == 0)\n                    continue\n                \n                ctx.strokeStyle = axis.options.tickColor || $.color.parse(axis.options.color).scale('a', 0.22).toString();\n                ctx.lineWidth = 1;\n\n                // find the edges\n                if (axis.direction == \"x\") {\n                    x = 0;\n                    if (t == \"full\")\n                        y = (axis.position == \"top\" ? 0 : plotHeight);\n                    else\n                        y = box.top - plotOffset.top + (axis.position == \"top\" ? box.height : 0);\n                }\n                else {\n                    y = 0;\n                    if (t == \"full\")\n                        x = (axis.position == \"left\" ? 0 : plotWidth);\n                    else\n                        x = box.left - plotOffset.left + (axis.position == \"left\" ? box.width : 0);\n                }\n                \n                // draw tick bar\n                if (!axis.innermost) {\n                    ctx.beginPath();\n                    xoff = yoff = 0;\n                    if (axis.direction == \"x\")\n                        xoff = plotWidth;\n                    else\n                        yoff = plotHeight;\n                    \n                    if (ctx.lineWidth == 1) {\n                        x = Math.floor(x) + 0.5;\n                        y = Math.floor(y) + 0.5;\n                    }\n\n                    ctx.moveTo(x, y);\n                    ctx.lineTo(x + xoff, y + yoff);\n                    ctx.stroke();\n                }\n\n                // draw ticks\n                ctx.beginPath();\n                for (i = 0; i < axis.ticks.length; ++i) {\n                    var v = axis.ticks[i].v;\n                    \n                    xoff = yoff = 0;\n\n                    if (v < axis.min || v > axis.max\n                        // skip those lying on the axes if we got a border\n                        || (t == \"full\" && bw > 0\n                            && (v == axis.min || v == axis.max)))\n                        continue;\n\n                    if (axis.direction == \"x\") {\n                        x = axis.p2c(v);\n                        yoff = t == \"full\" ? -plotHeight : t;\n                        \n                        if (axis.position == \"top\")\n                            yoff = -yoff;\n                    }\n                    else {\n                        y = axis.p2c(v);\n                        xoff = t == \"full\" ? -plotWidth : t;\n                        \n                        if (axis.position == \"left\")\n                            xoff = -xoff;\n                    }\n\n                    if (ctx.lineWidth == 1) {\n                        if (axis.direction == \"x\")\n                            x = Math.floor(x) + 0.5;\n                        else\n                            y = Math.floor(y) + 0.5;\n                    }\n\n                    ctx.moveTo(x, y);\n                    ctx.lineTo(x + xoff, y + yoff);\n                }\n                \n                ctx.stroke();\n            }\n            \n            \n            // draw border\n            if (bw) {\n                ctx.lineWidth = bw;\n                ctx.strokeStyle = options.grid.borderColor;\n                ctx.strokeRect(-bw/2, -bw/2, plotWidth + bw, plotHeight + bw);\n            }\n\n            ctx.restore();\n        }\n\n        function insertAxisLabels() {\n            placeholder.find(\".tickLabels\").remove();\n            \n            var html = ['<div class=\"tickLabels\" style=\"font-size:smaller\">'];\n\n            var axes = allAxes();\n            for (var j = 0; j < axes.length; ++j) {\n                var axis = axes[j], box = axis.box;\n                if (!axis.show)\n                    continue;\n                //debug: html.push('<div style=\"position:absolute;opacity:0.10;background-color:red;left:' + box.left + 'px;top:' + box.top + 'px;width:' + box.width +  'px;height:' + box.height + 'px\"></div>')\n                html.push('<div class=\"' + axis.direction + 'Axis ' + axis.direction + axis.n + 'Axis\" style=\"color:' + axis.options.color + '\">');\n                for (var i = 0; i < axis.ticks.length; ++i) {\n                    var tick = axis.ticks[i];\n                    if (!tick.label || tick.v < axis.min || tick.v > axis.max)\n                        continue;\n\n                    var pos = {}, align;\n                    \n                    if (axis.direction == \"x\") {\n                        align = \"center\";\n                        pos.left = Math.round(plotOffset.left + axis.p2c(tick.v) - axis.labelWidth/2);\n                        if (axis.position == \"bottom\")\n                            pos.top = box.top + box.padding;\n                        else\n                            pos.bottom = canvasHeight - (box.top + box.height - box.padding);\n                    }\n                    else {\n                        pos.top = Math.round(plotOffset.top + axis.p2c(tick.v) - axis.labelHeight/2);\n                        if (axis.position == \"left\") {\n                            pos.right = canvasWidth - (box.left + box.width - box.padding)\n                            align = \"right\";\n                        }\n                        else {\n                            pos.left = box.left + box.padding;\n                            align = \"left\";\n                        }\n                    }\n\n                    pos.width = axis.labelWidth;\n\n                    var style = [\"position:absolute\", \"text-align:\" + align ];\n                    for (var a in pos)\n                        style.push(a + \":\" + pos[a] + \"px\")\n                    \n                    html.push('<div class=\"tickLabel\" style=\"' + style.join(';') + '\">' + tick.label + '</div>');\n                }\n                html.push('</div>');\n            }\n\n            html.push('</div>');\n\n            placeholder.append(html.join(\"\"));\n        }\n\n        function drawSeries(series) {\n            if (series.lines.show)\n                drawSeriesLines(series);\n            if (series.bars.show)\n                drawSeriesBars(series);\n            if (series.points.show)\n                drawSeriesPoints(series);\n        }\n        \n        function drawSeriesLines(series) {\n            function plotLine(datapoints, xoffset, yoffset, axisx, axisy) {\n                var points = datapoints.points,\n                    ps = datapoints.pointsize,\n                    prevx = null, prevy = null;\n                \n                ctx.beginPath();\n                for (var i = ps; i < points.length; i += ps) {\n                    var x1 = points[i - ps], y1 = points[i - ps + 1],\n                        x2 = points[i], y2 = points[i + 1];\n                    \n                    if (x1 == null || x2 == null)\n                        continue;\n\n                    // clip with ymin\n                    if (y1 <= y2 && y1 < axisy.min) {\n                        if (y2 < axisy.min)\n                            continue;   // line segment is outside\n                        // compute new intersection point\n                        x1 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1;\n                        y1 = axisy.min;\n                    }\n                    else if (y2 <= y1 && y2 < axisy.min) {\n                        if (y1 < axisy.min)\n                            continue;\n                        x2 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1;\n                        y2 = axisy.min;\n                    }\n\n                    // clip with ymax\n                    if (y1 >= y2 && y1 > axisy.max) {\n                        if (y2 > axisy.max)\n                            continue;\n                        x1 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1;\n                        y1 = axisy.max;\n                    }\n                    else if (y2 >= y1 && y2 > axisy.max) {\n                        if (y1 > axisy.max)\n                            continue;\n                        x2 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1;\n                        y2 = axisy.max;\n                    }\n\n                    // clip with xmin\n                    if (x1 <= x2 && x1 < axisx.min) {\n                        if (x2 < axisx.min)\n                            continue;\n                        y1 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1;\n                        x1 = axisx.min;\n                    }\n                    else if (x2 <= x1 && x2 < axisx.min) {\n                        if (x1 < axisx.min)\n                            continue;\n                        y2 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1;\n                        x2 = axisx.min;\n                    }\n\n                    // clip with xmax\n                    if (x1 >= x2 && x1 > axisx.max) {\n                        if (x2 > axisx.max)\n                            continue;\n                        y1 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1;\n                        x1 = axisx.max;\n                    }\n                    else if (x2 >= x1 && x2 > axisx.max) {\n                        if (x1 > axisx.max)\n                            continue;\n                        y2 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1;\n                        x2 = axisx.max;\n                    }\n\n                    if (x1 != prevx || y1 != prevy)\n                        ctx.moveTo(axisx.p2c(x1) + xoffset, axisy.p2c(y1) + yoffset);\n                    \n                    prevx = x2;\n                    prevy = y2;\n                    ctx.lineTo(axisx.p2c(x2) + xoffset, axisy.p2c(y2) + yoffset);\n                }\n                ctx.stroke();\n            }\n\n            function plotLineArea(datapoints, axisx, axisy) {\n                var points = datapoints.points,\n                    ps = datapoints.pointsize,\n                    bottom = Math.min(Math.max(0, axisy.min), axisy.max),\n                    i = 0, top, areaOpen = false,\n                    ypos = 1, segmentStart = 0, segmentEnd = 0;\n\n                // we process each segment in two turns, first forward\n                // direction to sketch out top, then once we hit the\n                // end we go backwards to sketch the bottom\n                while (true) {\n                    if (ps > 0 && i > points.length + ps)\n                        break;\n\n                    i += ps; // ps is negative if going backwards\n\n                    var x1 = points[i - ps],\n                        y1 = points[i - ps + ypos],\n                        x2 = points[i], y2 = points[i + ypos];\n\n                    if (areaOpen) {\n                        if (ps > 0 && x1 != null && x2 == null) {\n                            // at turning point\n                            segmentEnd = i;\n                            ps = -ps;\n                            ypos = 2;\n                            continue;\n                        }\n\n                        if (ps < 0 && i == segmentStart + ps) {\n                            // done with the reverse sweep\n                            ctx.fill();\n                            areaOpen = false;\n                            ps = -ps;\n                            ypos = 1;\n                            i = segmentStart = segmentEnd + ps;\n                            continue;\n                        }\n                    }\n\n                    if (x1 == null || x2 == null)\n                        continue;\n\n                    // clip x values\n                    \n                    // clip with xmin\n                    if (x1 <= x2 && x1 < axisx.min) {\n                        if (x2 < axisx.min)\n                            continue;\n                        y1 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1;\n                        x1 = axisx.min;\n                    }\n                    else if (x2 <= x1 && x2 < axisx.min) {\n                        if (x1 < axisx.min)\n                            continue;\n                        y2 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1;\n                        x2 = axisx.min;\n                    }\n\n                    // clip with xmax\n                    if (x1 >= x2 && x1 > axisx.max) {\n                        if (x2 > axisx.max)\n                            continue;\n                        y1 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1;\n                        x1 = axisx.max;\n                    }\n                    else if (x2 >= x1 && x2 > axisx.max) {\n                        if (x1 > axisx.max)\n                            continue;\n                        y2 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1;\n                        x2 = axisx.max;\n                    }\n\n                    if (!areaOpen) {\n                        // open area\n                        ctx.beginPath();\n                        ctx.moveTo(axisx.p2c(x1), axisy.p2c(bottom));\n                        areaOpen = true;\n                    }\n                    \n                    // now first check the case where both is outside\n                    if (y1 >= axisy.max && y2 >= axisy.max) {\n                        ctx.lineTo(axisx.p2c(x1), axisy.p2c(axisy.max));\n                        ctx.lineTo(axisx.p2c(x2), axisy.p2c(axisy.max));\n                        continue;\n                    }\n                    else if (y1 <= axisy.min && y2 <= axisy.min) {\n                        ctx.lineTo(axisx.p2c(x1), axisy.p2c(axisy.min));\n                        ctx.lineTo(axisx.p2c(x2), axisy.p2c(axisy.min));\n                        continue;\n                    }\n                    \n                    // else it's a bit more complicated, there might\n                    // be a flat maxed out rectangle first, then a\n                    // triangular cutout or reverse; to find these\n                    // keep track of the current x values\n                    var x1old = x1, x2old = x2;\n\n                    // clip the y values, without shortcutting, we\n                    // go through all cases in turn\n                    \n                    // clip with ymin\n                    if (y1 <= y2 && y1 < axisy.min && y2 >= axisy.min) {\n                        x1 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1;\n                        y1 = axisy.min;\n                    }\n                    else if (y2 <= y1 && y2 < axisy.min && y1 >= axisy.min) {\n                        x2 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1;\n                        y2 = axisy.min;\n                    }\n\n                    // clip with ymax\n                    if (y1 >= y2 && y1 > axisy.max && y2 <= axisy.max) {\n                        x1 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1;\n                        y1 = axisy.max;\n                    }\n                    else if (y2 >= y1 && y2 > axisy.max && y1 <= axisy.max) {\n                        x2 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1;\n                        y2 = axisy.max;\n                    }\n\n                    // if the x value was changed we got a rectangle\n                    // to fill\n                    if (x1 != x1old) {\n                        ctx.lineTo(axisx.p2c(x1old), axisy.p2c(y1));\n                        // it goes to (x1, y1), but we fill that below\n                    }\n                    \n                    // fill triangular section, this sometimes result\n                    // in redundant points if (x1, y1) hasn't changed\n                    // from previous line to, but we just ignore that\n                    ctx.lineTo(axisx.p2c(x1), axisy.p2c(y1));\n                    ctx.lineTo(axisx.p2c(x2), axisy.p2c(y2));\n\n                    // fill the other rectangle if it's there\n                    if (x2 != x2old) {\n                        ctx.lineTo(axisx.p2c(x2), axisy.p2c(y2));\n                        ctx.lineTo(axisx.p2c(x2old), axisy.p2c(y2));\n                    }\n                }\n            }\n\n            ctx.save();\n            ctx.translate(plotOffset.left, plotOffset.top);\n            ctx.lineJoin = \"round\";\n\n            var lw = series.lines.lineWidth,\n                sw = series.shadowSize;\n            // FIXME: consider another form of shadow when filling is turned on\n            if (lw > 0 && sw > 0) {\n                // draw shadow as a thick and thin line with transparency\n                ctx.lineWidth = sw;\n                ctx.strokeStyle = \"rgba(0,0,0,0.1)\";\n                // position shadow at angle from the mid of line\n                var angle = Math.PI/18;\n                plotLine(series.datapoints, Math.sin(angle) * (lw/2 + sw/2), Math.cos(angle) * (lw/2 + sw/2), series.xaxis, series.yaxis);\n                ctx.lineWidth = sw/2;\n                plotLine(series.datapoints, Math.sin(angle) * (lw/2 + sw/4), Math.cos(angle) * (lw/2 + sw/4), series.xaxis, series.yaxis);\n            }\n\n            ctx.lineWidth = lw;\n            ctx.strokeStyle = series.color;\n            var fillStyle = getFillStyle(series.lines, series.color, 0, plotHeight);\n            if (fillStyle) {\n                ctx.fillStyle = fillStyle;\n                plotLineArea(series.datapoints, series.xaxis, series.yaxis);\n            }\n\n            if (lw > 0)\n                plotLine(series.datapoints, 0, 0, series.xaxis, series.yaxis);\n            ctx.restore();\n        }\n\n        function drawSeriesPoints(series) {\n            function plotPoints(datapoints, radius, fillStyle, offset, shadow, axisx, axisy, symbol) {\n                var points = datapoints.points, ps = datapoints.pointsize;\n\n                for (var i = 0; i < points.length; i += ps) {\n                    var x = points[i], y = points[i + 1];\n                    if (x == null || x < axisx.min || x > axisx.max || y < axisy.min || y > axisy.max)\n                        continue;\n                    \n                    ctx.beginPath();\n                    x = axisx.p2c(x);\n                    y = axisy.p2c(y) + offset;\n                    if (symbol == \"circle\")\n                        ctx.arc(x, y, radius, 0, shadow ? Math.PI : Math.PI * 2, false);\n                    else\n                        symbol(ctx, x, y, radius, shadow);\n                    ctx.closePath();\n                    \n                    if (fillStyle) {\n                        ctx.fillStyle = fillStyle;\n                        ctx.fill();\n                    }\n                    ctx.stroke();\n                }\n            }\n            \n            ctx.save();\n            ctx.translate(plotOffset.left, plotOffset.top);\n\n            var lw = series.points.lineWidth,\n                sw = series.shadowSize,\n                radius = series.points.radius,\n                symbol = series.points.symbol;\n            if (lw > 0 && sw > 0) {\n                // draw shadow in two steps\n                var w = sw / 2;\n                ctx.lineWidth = w;\n                ctx.strokeStyle = \"rgba(0,0,0,0.1)\";\n                plotPoints(series.datapoints, radius, null, w + w/2, true,\n                           series.xaxis, series.yaxis, symbol);\n\n                ctx.strokeStyle = \"rgba(0,0,0,0.2)\";\n                plotPoints(series.datapoints, radius, null, w/2, true,\n                           series.xaxis, series.yaxis, symbol);\n            }\n\n            ctx.lineWidth = lw;\n            ctx.strokeStyle = series.color;\n            plotPoints(series.datapoints, radius,\n                       getFillStyle(series.points, series.color), 0, false,\n                       series.xaxis, series.yaxis, symbol);\n            ctx.restore();\n        }\n\n        function drawBar(x, y, b, barLeft, barRight, offset, fillStyleCallback, axisx, axisy, c, horizontal, lineWidth) {\n            var left, right, bottom, top,\n                drawLeft, drawRight, drawTop, drawBottom,\n                tmp;\n\n            // in horizontal mode, we start the bar from the left\n            // instead of from the bottom so it appears to be\n            // horizontal rather than vertical\n            if (horizontal) {\n                drawBottom = drawRight = drawTop = true;\n                drawLeft = false;\n                left = b;\n                right = x;\n                top = y + barLeft;\n                bottom = y + barRight;\n\n                // account for negative bars\n                if (right < left) {\n                    tmp = right;\n                    right = left;\n                    left = tmp;\n                    drawLeft = true;\n                    drawRight = false;\n                }\n            }\n            else {\n                drawLeft = drawRight = drawTop = true;\n                drawBottom = false;\n                left = x + barLeft;\n                right = x + barRight;\n                bottom = b;\n                top = y;\n\n                // account for negative bars\n                if (top < bottom) {\n                    tmp = top;\n                    top = bottom;\n                    bottom = tmp;\n                    drawBottom = true;\n                    drawTop = false;\n                }\n            }\n           \n            // clip\n            if (right < axisx.min || left > axisx.max ||\n                top < axisy.min || bottom > axisy.max)\n                return;\n            \n            if (left < axisx.min) {\n                left = axisx.min;\n                drawLeft = false;\n            }\n\n            if (right > axisx.max) {\n                right = axisx.max;\n                drawRight = false;\n            }\n\n            if (bottom < axisy.min) {\n                bottom = axisy.min;\n                drawBottom = false;\n            }\n            \n            if (top > axisy.max) {\n                top = axisy.max;\n                drawTop = false;\n            }\n\n            left = axisx.p2c(left);\n            bottom = axisy.p2c(bottom);\n            right = axisx.p2c(right);\n            top = axisy.p2c(top);\n            \n            // fill the bar\n            if (fillStyleCallback) {\n                c.beginPath();\n                c.moveTo(left, bottom);\n                c.lineTo(left, top);\n                c.lineTo(right, top);\n                c.lineTo(right, bottom);\n                c.fillStyle = fillStyleCallback(bottom, top);\n                c.fill();\n            }\n\n            // draw outline\n            if (lineWidth > 0 && (drawLeft || drawRight || drawTop || drawBottom)) {\n                c.beginPath();\n\n                // FIXME: inline moveTo is buggy with excanvas\n                c.moveTo(left, bottom + offset);\n                if (drawLeft)\n                    c.lineTo(left, top + offset);\n                else\n                    c.moveTo(left, top + offset);\n                if (drawTop)\n                    c.lineTo(right, top + offset);\n                else\n                    c.moveTo(right, top + offset);\n                if (drawRight)\n                    c.lineTo(right, bottom + offset);\n                else\n                    c.moveTo(right, bottom + offset);\n                if (drawBottom)\n                    c.lineTo(left, bottom + offset);\n                else\n                    c.moveTo(left, bottom + offset);\n                c.stroke();\n            }\n        }\n        \n        function drawSeriesBars(series) {\n            function plotBars(datapoints, barLeft, barRight, offset, fillStyleCallback, axisx, axisy) {\n                var points = datapoints.points, ps = datapoints.pointsize;\n                \n                for (var i = 0; i < points.length; i += ps) {\n                    if (points[i] == null)\n                        continue;\n                    drawBar(points[i], points[i + 1], points[i + 2], barLeft, barRight, offset, fillStyleCallback, axisx, axisy, ctx, series.bars.horizontal, series.bars.lineWidth);\n                }\n            }\n\n            ctx.save();\n            ctx.translate(plotOffset.left, plotOffset.top);\n\n            // FIXME: figure out a way to add shadows (for instance along the right edge)\n            ctx.lineWidth = series.bars.lineWidth;\n            ctx.strokeStyle = series.color;\n            var barLeft = series.bars.align == \"left\" ? 0 : -series.bars.barWidth/2;\n            var fillStyleCallback = series.bars.fill ? function (bottom, top) { return getFillStyle(series.bars, series.color, bottom, top); } : null;\n            plotBars(series.datapoints, barLeft, barLeft + series.bars.barWidth, 0, fillStyleCallback, series.xaxis, series.yaxis);\n            ctx.restore();\n        }\n\n        function getFillStyle(filloptions, seriesColor, bottom, top) {\n            var fill = filloptions.fill;\n            if (!fill)\n                return null;\n\n            if (filloptions.fillColor)\n                return getColorOrGradient(filloptions.fillColor, bottom, top, seriesColor);\n            \n            var c = $.color.parse(seriesColor);\n            c.a = typeof fill == \"number\" ? fill : 0.4;\n            c.normalize();\n            return c.toString();\n        }\n        \n        function insertLegend() {\n            placeholder.find(\".legend\").remove();\n\n            if (!options.legend.show)\n                return;\n            \n            var fragments = [], rowStarted = false,\n                lf = options.legend.labelFormatter, s, label;\n            for (var i = 0; i < series.length; ++i) {\n                s = series[i];\n                label = s.label;\n                if (!label)\n                    continue;\n                \n                if (i % options.legend.noColumns == 0) {\n                    if (rowStarted)\n                        fragments.push('</tr>');\n                    fragments.push('<tr>');\n                    rowStarted = true;\n                }\n\n                if (lf)\n                    label = lf(label, s);\n                \n                fragments.push(\n                    '<td class=\"legendColorBox\"><div style=\"border:1px solid ' + options.legend.labelBoxBorderColor + ';padding:1px\"><div style=\"width:4px;height:0;border:5px solid ' + s.color + ';overflow:hidden\"></div></div></td>' +\n                    '<td class=\"legendLabel\">' + label + '</td>');\n            }\n            if (rowStarted)\n                fragments.push('</tr>');\n            \n            if (fragments.length == 0)\n                return;\n\n            var table = '<table style=\"font-size:smaller;color:' + options.grid.color + '\">' + fragments.join(\"\") + '</table>';\n            if (options.legend.container != null)\n                $(options.legend.container).html(table);\n            else {\n                var pos = \"\",\n                    p = options.legend.position,\n                    m = options.legend.margin;\n                if (m[0] == null)\n                    m = [m, m];\n                if (p.charAt(0) == \"n\")\n                    pos += 'top:' + (m[1] + plotOffset.top) + 'px;';\n                else if (p.charAt(0) == \"s\")\n                    pos += 'bottom:' + (m[1] + plotOffset.bottom) + 'px;';\n                if (p.charAt(1) == \"e\")\n                    pos += 'right:' + (m[0] + plotOffset.right) + 'px;';\n                else if (p.charAt(1) == \"w\")\n                    pos += 'left:' + (m[0] + plotOffset.left) + 'px;';\n                var legend = $('<div class=\"legend\">' + table.replace('style=\"', 'style=\"position:absolute;' + pos +';') + '</div>').appendTo(placeholder);\n                if (options.legend.backgroundOpacity != 0.0) {\n                    // put in the transparent background\n                    // separately to avoid blended labels and\n                    // label boxes\n                    var c = options.legend.backgroundColor;\n                    if (c == null) {\n                        c = options.grid.backgroundColor;\n                        if (c && typeof c == \"string\")\n                            c = $.color.parse(c);\n                        else\n                            c = $.color.extract(legend, 'background-color');\n                        c.a = 1;\n                        c = c.toString();\n                    }\n                    var div = legend.children();\n                    $('<div style=\"position:absolute;width:' + div.width() + 'px;height:' + div.height() + 'px;' + pos +'background-color:' + c + ';\"> </div>').prependTo(legend).css('opacity', options.legend.backgroundOpacity);\n                }\n            }\n        }\n\n\n        // interactive features\n        \n        var highlights = [],\n            redrawTimeout = null;\n        \n        // returns the data item the mouse is over, or null if none is found\n        function findNearbyItem(mouseX, mouseY, seriesFilter) {\n            var maxDistance = options.grid.mouseActiveRadius,\n                smallestDistance = maxDistance * maxDistance + 1,\n                item = null, foundPoint = false, i, j;\n\n            for (i = series.length - 1; i >= 0; --i) {\n                if (!seriesFilter(series[i]))\n                    continue;\n                \n                var s = series[i],\n                    axisx = s.xaxis,\n                    axisy = s.yaxis,\n                    points = s.datapoints.points,\n                    ps = s.datapoints.pointsize,\n                    mx = axisx.c2p(mouseX), // precompute some stuff to make the loop faster\n                    my = axisy.c2p(mouseY),\n                    maxx = maxDistance / axisx.scale,\n                    maxy = maxDistance / axisy.scale;\n\n                // with inverse transforms, we can't use the maxx/maxy\n                // optimization, sadly\n                if (axisx.options.inverseTransform)\n                    maxx = Number.MAX_VALUE;\n                if (axisy.options.inverseTransform)\n                    maxy = Number.MAX_VALUE;\n                \n                if (s.lines.show || s.points.show) {\n                    for (j = 0; j < points.length; j += ps) {\n                        var x = points[j], y = points[j + 1];\n                        if (x == null)\n                            continue;\n                        \n                        // For points and lines, the cursor must be within a\n                        // certain distance to the data point\n                        if (x - mx > maxx || x - mx < -maxx ||\n                            y - my > maxy || y - my < -maxy)\n                            continue;\n\n                        // We have to calculate distances in pixels, not in\n                        // data units, because the scales of the axes may be different\n                        var dx = Math.abs(axisx.p2c(x) - mouseX),\n                            dy = Math.abs(axisy.p2c(y) - mouseY),\n                            dist = dx * dx + dy * dy; // we save the sqrt\n\n                        // use <= to ensure last point takes precedence\n                        // (last generally means on top of)\n                        if (dist < smallestDistance) {\n                            smallestDistance = dist;\n                            item = [i, j / ps];\n                        }\n                    }\n                }\n                    \n                if (s.bars.show && !item) { // no other point can be nearby\n                    var barLeft = s.bars.align == \"left\" ? 0 : -s.bars.barWidth/2,\n                        barRight = barLeft + s.bars.barWidth;\n                    \n                    for (j = 0; j < points.length; j += ps) {\n                        var x = points[j], y = points[j + 1], b = points[j + 2];\n                        if (x == null)\n                            continue;\n  \n                        // for a bar graph, the cursor must be inside the bar\n                        if (series[i].bars.horizontal ? \n                            (mx <= Math.max(b, x) && mx >= Math.min(b, x) && \n                             my >= y + barLeft && my <= y + barRight) :\n                            (mx >= x + barLeft && mx <= x + barRight &&\n                             my >= Math.min(b, y) && my <= Math.max(b, y)))\n                                item = [i, j / ps];\n                    }\n                }\n            }\n\n            if (item) {\n                i = item[0];\n                j = item[1];\n                ps = series[i].datapoints.pointsize;\n                \n                return { datapoint: series[i].datapoints.points.slice(j * ps, (j + 1) * ps),\n                         dataIndex: j,\n                         series: series[i],\n                         seriesIndex: i };\n            }\n            \n            return null;\n        }\n\n        function onMouseMove(e) {\n            if (options.grid.hoverable)\n                triggerClickHoverEvent(\"plothover\", e,\n                                       function (s) { return s[\"hoverable\"] != false; });\n        }\n\n        function onMouseLeave(e) {\n            if (options.grid.hoverable)\n                triggerClickHoverEvent(\"plothover\", e,\n                                       function (s) { return false; });\n        }\n\n        function onClick(e) {\n            triggerClickHoverEvent(\"plotclick\", e,\n                                   function (s) { return s[\"clickable\"] != false; });\n        }\n\n        // trigger click or hover event (they send the same parameters\n        // so we share their code)\n        function triggerClickHoverEvent(eventname, event, seriesFilter) {\n            var offset = eventHolder.offset(),\n                canvasX = event.pageX - offset.left - plotOffset.left,\n                canvasY = event.pageY - offset.top - plotOffset.top,\n            pos = canvasToAxisCoords({ left: canvasX, top: canvasY });\n\n            pos.pageX = event.pageX;\n            pos.pageY = event.pageY;\n\n            var item = findNearbyItem(canvasX, canvasY, seriesFilter);\n\n            if (item) {\n                // fill in mouse pos for any listeners out there\n                item.pageX = parseInt(item.series.xaxis.p2c(item.datapoint[0]) + offset.left + plotOffset.left);\n                item.pageY = parseInt(item.series.yaxis.p2c(item.datapoint[1]) + offset.top + plotOffset.top);\n            }\n\n            if (options.grid.autoHighlight) {\n                // clear auto-highlights\n                for (var i = 0; i < highlights.length; ++i) {\n                    var h = highlights[i];\n                    if (h.auto == eventname &&\n                        !(item && h.series == item.series &&\n                          h.point[0] == item.datapoint[0] &&\n                          h.point[1] == item.datapoint[1]))\n                        unhighlight(h.series, h.point);\n                }\n                \n                if (item)\n                    highlight(item.series, item.datapoint, eventname);\n            }\n            \n            placeholder.trigger(eventname, [ pos, item ]);\n        }\n\n        function triggerRedrawOverlay() {\n            if (!redrawTimeout)\n                redrawTimeout = setTimeout(drawOverlay, 30);\n        }\n\n        function drawOverlay() {\n            redrawTimeout = null;\n\n            // draw highlights\n            octx.save();\n            octx.clearRect(0, 0, canvasWidth, canvasHeight);\n            octx.translate(plotOffset.left, plotOffset.top);\n            \n            var i, hi;\n            for (i = 0; i < highlights.length; ++i) {\n                hi = highlights[i];\n\n                if (hi.series.bars.show)\n                    drawBarHighlight(hi.series, hi.point);\n                else\n                    drawPointHighlight(hi.series, hi.point);\n            }\n            octx.restore();\n            \n            executeHooks(hooks.drawOverlay, [octx]);\n        }\n        \n        function highlight(s, point, auto) {\n            if (typeof s == \"number\")\n                s = series[s];\n\n            if (typeof point == \"number\") {\n                var ps = s.datapoints.pointsize;\n                point = s.datapoints.points.slice(ps * point, ps * (point + 1));\n            }\n\n            var i = indexOfHighlight(s, point);\n            if (i == -1) {\n                highlights.push({ series: s, point: point, auto: auto });\n\n                triggerRedrawOverlay();\n            }\n            else if (!auto)\n                highlights[i].auto = false;\n        }\n            \n        function unhighlight(s, point) {\n            if (s == null && point == null) {\n                highlights = [];\n                triggerRedrawOverlay();\n            }\n            \n            if (typeof s == \"number\")\n                s = series[s];\n\n            if (typeof point == \"number\")\n                point = s.data[point];\n\n            var i = indexOfHighlight(s, point);\n            if (i != -1) {\n                highlights.splice(i, 1);\n\n                triggerRedrawOverlay();\n            }\n        }\n        \n        function indexOfHighlight(s, p) {\n            for (var i = 0; i < highlights.length; ++i) {\n                var h = highlights[i];\n                if (h.series == s && h.point[0] == p[0]\n                    && h.point[1] == p[1])\n                    return i;\n            }\n            return -1;\n        }\n        \n        function drawPointHighlight(series, point) {\n            var x = point[0], y = point[1],\n                axisx = series.xaxis, axisy = series.yaxis;\n            \n            if (x < axisx.min || x > axisx.max || y < axisy.min || y > axisy.max)\n                return;\n            \n            var pointRadius = series.points.radius + series.points.lineWidth / 2;\n            octx.lineWidth = pointRadius;\n            octx.strokeStyle = $.color.parse(series.color).scale('a', 0.5).toString();\n            var radius = 1.5 * pointRadius,\n                x = axisx.p2c(x),\n                y = axisy.p2c(y);\n            \n            octx.beginPath();\n            if (series.points.symbol == \"circle\")\n                octx.arc(x, y, radius, 0, 2 * Math.PI, false);\n            else\n                series.points.symbol(octx, x, y, radius, false);\n            octx.closePath();\n            octx.stroke();\n        }\n\n        function drawBarHighlight(series, point) {\n            octx.lineWidth = series.bars.lineWidth;\n            octx.strokeStyle = $.color.parse(series.color).scale('a', 0.5).toString();\n            var fillStyle = $.color.parse(series.color).scale('a', 0.5).toString();\n            var barLeft = series.bars.align == \"left\" ? 0 : -series.bars.barWidth/2;\n            drawBar(point[0], point[1], point[2] || 0, barLeft, barLeft + series.bars.barWidth,\n                    0, function () { return fillStyle; }, series.xaxis, series.yaxis, octx, series.bars.horizontal, series.bars.lineWidth);\n        }\n\n        function getColorOrGradient(spec, bottom, top, defaultColor) {\n            if (typeof spec == \"string\")\n                return spec;\n            else {\n                // assume this is a gradient spec; IE currently only\n                // supports a simple vertical gradient properly, so that's\n                // what we support too\n                var gradient = ctx.createLinearGradient(0, top, 0, bottom);\n                \n                for (var i = 0, l = spec.colors.length; i < l; ++i) {\n                    var c = spec.colors[i];\n                    if (typeof c != \"string\") {\n                        var co = $.color.parse(defaultColor);\n                        if (c.brightness != null)\n                            co = co.scale('rgb', c.brightness)\n                        if (c.opacity != null)\n                            co.a *= c.opacity;\n                        c = co.toString();\n                    }\n                    gradient.addColorStop(i / (l - 1), c);\n                }\n                \n                return gradient;\n            }\n        }\n    }\n\n    $.plot = function(placeholder, data, options) {\n        //var t0 = new Date();\n        var plot = new Plot($(placeholder), data, options, $.plot.plugins);\n        //(window.console ? console.log : alert)(\"time used (msecs): \" + ((new Date()).getTime() - t0.getTime()));\n        return plot;\n    };\n\n    $.plot.version = \"0.7\";\n    \n    $.plot.plugins = [];\n\n    // returns a string with the date d formatted according to fmt\n    $.plot.formatDate = function(d, fmt, monthNames) {\n        var leftPad = function(n) {\n            n = \"\" + n;\n            return n.length == 1 ? \"0\" + n : n;\n        };\n        \n        var r = [];\n        var escape = false, padNext = false;\n        var hours = d.getUTCHours();\n        var isAM = hours < 12;\n        if (monthNames == null)\n            monthNames = [\"Jan\", \"Feb\", \"Mar\", \"Apr\", \"May\", \"Jun\", \"Jul\", \"Aug\", \"Sep\", \"Oct\", \"Nov\", \"Dec\"];\n\n        if (fmt.search(/%p|%P/) != -1) {\n            if (hours > 12) {\n                hours = hours - 12;\n            } else if (hours == 0) {\n                hours = 12;\n            }\n        }\n        for (var i = 0; i < fmt.length; ++i) {\n            var c = fmt.charAt(i);\n            \n            if (escape) {\n                switch (c) {\n                case 'h': c = \"\" + hours; break;\n                case 'H': c = leftPad(hours); break;\n                case 'M': c = leftPad(d.getUTCMinutes()); break;\n                case 'S': c = leftPad(d.getUTCSeconds()); break;\n                case 'd': c = \"\" + d.getUTCDate(); break;\n                case 'm': c = \"\" + (d.getUTCMonth() + 1); break;\n                case 'y': c = \"\" + d.getUTCFullYear(); break;\n                case 'b': c = \"\" + monthNames[d.getUTCMonth()]; break;\n                case 'p': c = (isAM) ? (\"\" + \"am\") : (\"\" + \"pm\"); break;\n                case 'P': c = (isAM) ? (\"\" + \"AM\") : (\"\" + \"PM\"); break;\n                case '0': c = \"\"; padNext = true; break;\n                }\n                if (c && padNext) {\n                    c = leftPad(c);\n                    padNext = false;\n                }\n                r.push(c);\n                if (!padNext)\n                    escape = false;\n            }\n            else {\n                if (c == \"%\")\n                    escape = true;\n                else\n                    r.push(c);\n            }\n        }\n        return r.join(\"\");\n    };\n    \n    // round to nearby lower multiple of base\n    function floorInBase(n, base) {\n        return base * Math.floor(n / base);\n    }\n    \n})(jQuery);\n"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/js/flot/jquery.flot.navigate.js",
    "content": "/*\nFlot plugin for adding panning and zooming capabilities to a plot.\n\nThe default behaviour is double click and scrollwheel up/down to zoom\nin, drag to pan. The plugin defines plot.zoom({ center }),\nplot.zoomOut() and plot.pan(offset) so you easily can add custom\ncontrols. It also fires a \"plotpan\" and \"plotzoom\" event when\nsomething happens, useful for synchronizing plots.\n\nOptions:\n\n  zoom: {\n    interactive: false\n    trigger: \"dblclick\" // or \"click\" for single click\n    amount: 1.5         // 2 = 200% (zoom in), 0.5 = 50% (zoom out)\n  }\n  \n  pan: {\n    interactive: false\n    cursor: \"move\"      // CSS mouse cursor value used when dragging, e.g. \"pointer\"\n    frameRate: 20\n  }\n\n  xaxis, yaxis, x2axis, y2axis: {\n    zoomRange: null  // or [number, number] (min range, max range) or false\n    panRange: null   // or [number, number] (min, max) or false\n  }\n  \n\"interactive\" enables the built-in drag/click behaviour. If you enable\ninteractive for pan, then you'll have a basic plot that supports\nmoving around; the same for zoom.\n\n\"amount\" specifies the default amount to zoom in (so 1.5 = 150%)\nrelative to the current viewport.\n\n\"cursor\" is a standard CSS mouse cursor string used for visual\nfeedback to the user when dragging.\n\n\"frameRate\" specifies the maximum number of times per second the plot\nwill update itself while the user is panning around on it (set to null\nto disable intermediate pans, the plot will then not update until the\nmouse button is released).\n\n\"zoomRange\" is the interval in which zooming can happen, e.g. with\nzoomRange: [1, 100] the zoom will never scale the axis so that the\ndifference between min and max is smaller than 1 or larger than 100.\nYou can set either end to null to ignore, e.g. [1, null]. If you set\nzoomRange to false, zooming on that axis will be disabled.\n\n\"panRange\" confines the panning to stay within a range, e.g. with\npanRange: [-10, 20] panning stops at -10 in one end and at 20 in the\nother. Either can be null, e.g. [-10, null]. If you set\npanRange to false, panning on that axis will be disabled.\n\nExample API usage:\n\n  plot = $.plot(...);\n  \n  // zoom default amount in on the pixel (10, 20) \n  plot.zoom({ center: { left: 10, top: 20 } });\n\n  // zoom out again\n  plot.zoomOut({ center: { left: 10, top: 20 } });\n\n  // zoom 200% in on the pixel (10, 20) \n  plot.zoom({ amount: 2, center: { left: 10, top: 20 } });\n  \n  // pan 100 pixels to the left and 20 down\n  plot.pan({ left: -100, top: 20 })\n\nHere, \"center\" specifies where the center of the zooming should\nhappen. Note that this is defined in pixel space, not the space of the\ndata points (you can use the p2c helpers on the axes in Flot to help\nyou convert between these).\n\n\"amount\" is the amount to zoom the viewport relative to the current\nrange, so 1 is 100% (i.e. no change), 1.5 is 150% (zoom in), 0.7 is\n70% (zoom out). You can set the default in the options.\n  \n*/\n\n\n// First two dependencies, jquery.event.drag.js and\n// jquery.mousewheel.js, we put them inline here to save people the\n// effort of downloading them.\n\n/*\njquery.event.drag.js ~ v1.5 ~ Copyright (c) 2008, Three Dub Media (http://threedubmedia.com)  \nLicensed under the MIT License ~ http://threedubmedia.googlecode.com/files/MIT-LICENSE.txt\n*/\n(function(E){E.fn.drag=function(L,K,J){if(K){this.bind(\"dragstart\",L)}if(J){this.bind(\"dragend\",J)}return !L?this.trigger(\"drag\"):this.bind(\"drag\",K?K:L)};var A=E.event,B=A.special,F=B.drag={not:\":input\",distance:0,which:1,dragging:false,setup:function(J){J=E.extend({distance:F.distance,which:F.which,not:F.not},J||{});J.distance=I(J.distance);A.add(this,\"mousedown\",H,J);if(this.attachEvent){this.attachEvent(\"ondragstart\",D)}},teardown:function(){A.remove(this,\"mousedown\",H);if(this===F.dragging){F.dragging=F.proxy=false}G(this,true);if(this.detachEvent){this.detachEvent(\"ondragstart\",D)}}};B.dragstart=B.dragend={setup:function(){},teardown:function(){}};function H(L){var K=this,J,M=L.data||{};if(M.elem){K=L.dragTarget=M.elem;L.dragProxy=F.proxy||K;L.cursorOffsetX=M.pageX-M.left;L.cursorOffsetY=M.pageY-M.top;L.offsetX=L.pageX-L.cursorOffsetX;L.offsetY=L.pageY-L.cursorOffsetY}else{if(F.dragging||(M.which>0&&L.which!=M.which)||E(L.target).is(M.not)){return }}switch(L.type){case\"mousedown\":E.extend(M,E(K).offset(),{elem:K,target:L.target,pageX:L.pageX,pageY:L.pageY});A.add(document,\"mousemove mouseup\",H,M);G(K,false);F.dragging=null;return false;case !F.dragging&&\"mousemove\":if(I(L.pageX-M.pageX)+I(L.pageY-M.pageY)<M.distance){break}L.target=M.target;J=C(L,\"dragstart\",K);if(J!==false){F.dragging=K;F.proxy=L.dragProxy=E(J||K)[0]}case\"mousemove\":if(F.dragging){J=C(L,\"drag\",K);if(B.drop){B.drop.allowed=(J!==false);B.drop.handler(L)}if(J!==false){break}L.type=\"mouseup\"}case\"mouseup\":A.remove(document,\"mousemove mouseup\",H);if(F.dragging){if(B.drop){B.drop.handler(L)}C(L,\"dragend\",K)}G(K,true);F.dragging=F.proxy=M.elem=false;break}return true}function C(M,K,L){M.type=K;var J=E.event.handle.call(L,M);return J===false?false:J||M.result}function I(J){return Math.pow(J,2)}function D(){return(F.dragging===false)}function G(K,J){if(!K){return }K.unselectable=J?\"off\":\"on\";K.onselectstart=function(){return J};if(K.style){K.style.MozUserSelect=J?\"\":\"none\"}}})(jQuery);\n\n\n/* jquery.mousewheel.min.js\n * Copyright (c) 2009 Brandon Aaron (http://brandonaaron.net)\n * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)\n * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses.\n * Thanks to: http://adomas.org/javascript-mouse-wheel/ for some pointers.\n * Thanks to: Mathias Bank(http://www.mathias-bank.de) for a scope bug fix.\n *\n * Version: 3.0.2\n * \n * Requires: 1.2.2+\n */\n(function(c){var a=[\"DOMMouseScroll\",\"mousewheel\"];c.event.special.mousewheel={setup:function(){if(this.addEventListener){for(var d=a.length;d;){this.addEventListener(a[--d],b,false)}}else{this.onmousewheel=b}},teardown:function(){if(this.removeEventListener){for(var d=a.length;d;){this.removeEventListener(a[--d],b,false)}}else{this.onmousewheel=null}}};c.fn.extend({mousewheel:function(d){return d?this.bind(\"mousewheel\",d):this.trigger(\"mousewheel\")},unmousewheel:function(d){return this.unbind(\"mousewheel\",d)}});function b(f){var d=[].slice.call(arguments,1),g=0,e=true;f=c.event.fix(f||window.event);f.type=\"mousewheel\";if(f.wheelDelta){g=f.wheelDelta/120}if(f.detail){g=-f.detail/3}d.unshift(f,g);return c.event.handle.apply(this,d)}})(jQuery);\n\n\n\n\n(function ($) {\n    var options = {\n        xaxis: {\n            zoomRange: null, // or [number, number] (min range, max range)\n            panRange: null // or [number, number] (min, max)\n        },\n        zoom: {\n            interactive: false,\n            trigger: \"dblclick\", // or \"click\" for single click\n            amount: 1.5 // how much to zoom relative to current position, 2 = 200% (zoom in), 0.5 = 50% (zoom out)\n        },\n        pan: {\n            interactive: false,\n            cursor: \"move\",\n            frameRate: 20\n        }\n    };\n\n    function init(plot) {\n        function onZoomClick(e, zoomOut) {\n            var c = plot.offset();\n            c.left = e.pageX - c.left;\n            c.top = e.pageY - c.top;\n            if (zoomOut)\n                plot.zoomOut({ center: c });\n            else\n                plot.zoom({ center: c });\n        }\n\n        function onMouseWheel(e, delta) {\n            onZoomClick(e, delta < 0);\n            return false;\n        }\n        \n        var prevCursor = 'default', prevPageX = 0, prevPageY = 0,\n            panTimeout = null;\n\n        function onDragStart(e) {\n            if (e.which != 1)  // only accept left-click\n                return false;\n            var c = plot.getPlaceholder().css('cursor');\n            if (c)\n                prevCursor = c;\n            plot.getPlaceholder().css('cursor', plot.getOptions().pan.cursor);\n            prevPageX = e.pageX;\n            prevPageY = e.pageY;\n        }\n        \n        function onDrag(e) {\n            var frameRate = plot.getOptions().pan.frameRate;\n            if (panTimeout || !frameRate)\n                return;\n\n            panTimeout = setTimeout(function () {\n                plot.pan({ left: prevPageX - e.pageX,\n                           top: prevPageY - e.pageY });\n                prevPageX = e.pageX;\n                prevPageY = e.pageY;\n                                                    \n                panTimeout = null;\n            }, 1 / frameRate * 1000);\n        }\n\n        function onDragEnd(e) {\n            if (panTimeout) {\n                clearTimeout(panTimeout);\n                panTimeout = null;\n            }\n                    \n            plot.getPlaceholder().css('cursor', prevCursor);\n            plot.pan({ left: prevPageX - e.pageX,\n                       top: prevPageY - e.pageY });\n        }\n        \n        function bindEvents(plot, eventHolder) {\n            var o = plot.getOptions();\n            if (o.zoom.interactive) {\n                eventHolder[o.zoom.trigger](onZoomClick);\n                eventHolder.mousewheel(onMouseWheel);\n            }\n\n            if (o.pan.interactive) {\n                eventHolder.bind(\"dragstart\", { distance: 10 }, onDragStart);\n                eventHolder.bind(\"drag\", onDrag);\n                eventHolder.bind(\"dragend\", onDragEnd);\n            }\n        }\n\n        plot.zoomOut = function (args) {\n            if (!args)\n                args = {};\n            \n            if (!args.amount)\n                args.amount = plot.getOptions().zoom.amount\n\n            args.amount = 1 / args.amount;\n            plot.zoom(args);\n        }\n        \n        plot.zoom = function (args) {\n            if (!args)\n                args = {};\n            \n            var c = args.center,\n                amount = args.amount || plot.getOptions().zoom.amount,\n                w = plot.width(), h = plot.height();\n\n            if (!c)\n                c = { left: w / 2, top: h / 2 };\n                \n            var xf = c.left / w,\n                yf = c.top / h,\n                minmax = {\n                    x: {\n                        min: c.left - xf * w / amount,\n                        max: c.left + (1 - xf) * w / amount\n                    },\n                    y: {\n                        min: c.top - yf * h / amount,\n                        max: c.top + (1 - yf) * h / amount\n                    }\n                };\n\n            $.each(plot.getAxes(), function(_, axis) {\n                var opts = axis.options,\n                    min = minmax[axis.direction].min,\n                    max = minmax[axis.direction].max,\n                    zr = opts.zoomRange;\n\n                if (zr === false) // no zooming on this axis\n                    return;\n                    \n                min = axis.c2p(min);\n                max = axis.c2p(max);\n                if (min > max) {\n                    // make sure min < max\n                    var tmp = min;\n                    min = max;\n                    max = tmp;\n                }\n\n                var range = max - min;\n                if (zr &&\n                    ((zr[0] != null && range < zr[0]) ||\n                     (zr[1] != null && range > zr[1])))\n                    return;\n            \n                opts.min = min;\n                opts.max = max;\n            });\n            \n            plot.setupGrid();\n            plot.draw();\n            \n            if (!args.preventEvent)\n                plot.getPlaceholder().trigger(\"plotzoom\", [ plot ]);\n        }\n\n        plot.pan = function (args) {\n            var delta = {\n                x: +args.left,\n                y: +args.top\n            };\n\n            if (isNaN(delta.x))\n                delta.x = 0;\n            if (isNaN(delta.y))\n                delta.y = 0;\n\n            $.each(plot.getAxes(), function (_, axis) {\n                var opts = axis.options,\n                    min, max, d = delta[axis.direction];\n\n                min = axis.c2p(axis.p2c(axis.min) + d),\n                max = axis.c2p(axis.p2c(axis.max) + d);\n\n                var pr = opts.panRange;\n                if (pr === false) // no panning on this axis\n                    return;\n                \n                if (pr) {\n                    // check whether we hit the wall\n                    if (pr[0] != null && pr[0] > min) {\n                        d = pr[0] - min;\n                        min += d;\n                        max += d;\n                    }\n                    \n                    if (pr[1] != null && pr[1] < max) {\n                        d = pr[1] - max;\n                        min += d;\n                        max += d;\n                    }\n                }\n                \n                opts.min = min;\n                opts.max = max;\n            });\n            \n            plot.setupGrid();\n            plot.draw();\n            \n            if (!args.preventEvent)\n                plot.getPlaceholder().trigger(\"plotpan\", [ plot ]);\n        }\n\n        function shutdown(plot, eventHolder) {\n            eventHolder.unbind(plot.getOptions().zoom.trigger, onZoomClick);\n            eventHolder.unbind(\"mousewheel\", onMouseWheel);\n            eventHolder.unbind(\"dragstart\", onDragStart);\n            eventHolder.unbind(\"drag\", onDrag);\n            eventHolder.unbind(\"dragend\", onDragEnd);\n            if (panTimeout)\n                clearTimeout(panTimeout);\n        }\n        \n        plot.hooks.bindEvents.push(bindEvents);\n        plot.hooks.shutdown.push(shutdown);\n    }\n    \n    $.plot.plugins.push({\n        init: init,\n        options: options,\n        name: 'navigate',\n        version: '1.3'\n    });\n})(jQuery);\n"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/js/flot/jquery.flot.pie.js",
    "content": "/*\r\nFlot plugin for rendering pie charts. The plugin assumes the data is \r\ncoming is as a single data value for each series, and each of those \r\nvalues is a positive value or zero (negative numbers don't make \r\nany sense and will cause strange effects). The data values do \r\nNOT need to be passed in as percentage values because it \r\ninternally calculates the total and percentages.\r\n\r\n* Created by Brian Medendorp, June 2009\r\n* Updated November 2009 with contributions from: btburnett3, Anthony Aragues and Xavi Ivars\r\n\r\n* Changes:\r\n\t2009-10-22: lineJoin set to round\r\n\t2009-10-23: IE full circle fix, donut\r\n\t2009-11-11: Added basic hover from btburnett3 - does not work in IE, and center is off in Chrome and Opera\r\n\t2009-11-17: Added IE hover capability submitted by Anthony Aragues\r\n\t2009-11-18: Added bug fix submitted by Xavi Ivars (issues with arrays when other JS libraries are included as well)\r\n\t\t\r\n\r\nAvailable options are:\r\nseries: {\r\n\tpie: {\r\n\t\tshow: true/false\r\n\t\tradius: 0-1 for percentage of fullsize, or a specified pixel length, or 'auto'\r\n\t\tinnerRadius: 0-1 for percentage of fullsize or a specified pixel length, for creating a donut effect\r\n\t\tstartAngle: 0-2 factor of PI used for starting angle (in radians) i.e 3/2 starts at the top, 0 and 2 have the same result\r\n\t\ttilt: 0-1 for percentage to tilt the pie, where 1 is no tilt, and 0 is completely flat (nothing will show)\r\n\t\toffset: {\r\n\t\t\ttop: integer value to move the pie up or down\r\n\t\t\tleft: integer value to move the pie left or right, or 'auto'\r\n\t\t},\r\n\t\tstroke: {\r\n\t\t\tcolor: any hexidecimal color value (other formats may or may not work, so best to stick with something like '#FFF')\r\n\t\t\twidth: integer pixel width of the stroke\r\n\t\t},\r\n\t\tlabel: {\r\n\t\t\tshow: true/false, or 'auto'\r\n\t\t\tformatter:  a user-defined function that modifies the text/style of the label text\r\n\t\t\tradius: 0-1 for percentage of fullsize, or a specified pixel length\r\n\t\t\tbackground: {\r\n\t\t\t\tcolor: any hexidecimal color value (other formats may or may not work, so best to stick with something like '#000')\r\n\t\t\t\topacity: 0-1\r\n\t\t\t},\r\n\t\t\tthreshold: 0-1 for the percentage value at which to hide labels (if they're too small)\r\n\t\t},\r\n\t\tcombine: {\r\n\t\t\tthreshold: 0-1 for the percentage value at which to combine slices (if they're too small)\r\n\t\t\tcolor: any hexidecimal color value (other formats may or may not work, so best to stick with something like '#CCC'), if null, the plugin will automatically use the color of the first slice to be combined\r\n\t\t\tlabel: any text value of what the combined slice should be labeled\r\n\t\t}\r\n\t\thighlight: {\r\n\t\t\topacity: 0-1\r\n\t\t}\r\n\t}\r\n}\r\n\r\nMore detail and specific examples can be found in the included HTML file.\r\n\r\n*/\r\n\r\n(function ($) \r\n{\r\n\tfunction init(plot) // this is the \"body\" of the plugin\r\n\t{\r\n\t\tvar canvas = null;\r\n\t\tvar target = null;\r\n\t\tvar maxRadius = null;\r\n\t\tvar centerLeft = null;\r\n\t\tvar centerTop = null;\r\n\t\tvar total = 0;\r\n\t\tvar redraw = true;\r\n\t\tvar redrawAttempts = 10;\r\n\t\tvar shrink = 0.95;\r\n\t\tvar legendWidth = 0;\r\n\t\tvar processed = false;\r\n\t\tvar raw = false;\r\n\t\t\r\n\t\t// interactive variables\t\r\n\t\tvar highlights = [];\t\r\n\t\r\n\t\t// add hook to determine if pie plugin in enabled, and then perform necessary operations\r\n\t\tplot.hooks.processOptions.push(checkPieEnabled);\r\n\t\tplot.hooks.bindEvents.push(bindEvents);\t\r\n\r\n\t\t// check to see if the pie plugin is enabled\r\n\t\tfunction checkPieEnabled(plot, options)\r\n\t\t{\r\n\t\t\tif (options.series.pie.show)\r\n\t\t\t{\r\n\t\t\t\t//disable grid\r\n\t\t\t\toptions.grid.show = false;\r\n\t\t\t\t\r\n\t\t\t\t// set labels.show\r\n\t\t\t\tif (options.series.pie.label.show=='auto')\r\n\t\t\t\t\tif (options.legend.show)\r\n\t\t\t\t\t\toptions.series.pie.label.show = false;\r\n\t\t\t\t\telse\r\n\t\t\t\t\t\toptions.series.pie.label.show = true;\r\n\t\t\t\t\r\n\t\t\t\t// set radius\r\n\t\t\t\tif (options.series.pie.radius=='auto')\r\n\t\t\t\t\tif (options.series.pie.label.show)\r\n\t\t\t\t\t\toptions.series.pie.radius = 3/4;\r\n\t\t\t\t\telse\r\n\t\t\t\t\t\toptions.series.pie.radius = 1;\r\n\t\t\t\t\t\t\r\n\t\t\t\t// ensure sane tilt\r\n\t\t\t\tif (options.series.pie.tilt>1)\r\n\t\t\t\t\toptions.series.pie.tilt=1;\r\n\t\t\t\tif (options.series.pie.tilt<0)\r\n\t\t\t\t\toptions.series.pie.tilt=0;\r\n\t\t\t\r\n\t\t\t\t// add processData hook to do transformations on the data\r\n\t\t\t\tplot.hooks.processDatapoints.push(processDatapoints);\r\n\t\t\t\tplot.hooks.drawOverlay.push(drawOverlay);\t\r\n\t\t\t\t\r\n\t\t\t\t// add draw hook\r\n\t\t\t\tplot.hooks.draw.push(draw);\r\n\t\t\t}\r\n\t\t}\r\n\t\r\n\t\t// bind hoverable events\r\n\t\tfunction bindEvents(plot, eventHolder) \t\t\r\n\t\t{\t\t\r\n\t\t\tvar options = plot.getOptions();\r\n\t\t\t\r\n\t\t\tif (options.series.pie.show && options.grid.hoverable)\r\n\t\t\t\teventHolder.unbind('mousemove').mousemove(onMouseMove);\r\n\t\t\t\t\r\n\t\t\tif (options.series.pie.show && options.grid.clickable)\r\n\t\t\t\teventHolder.unbind('click').click(onClick);\r\n\t\t}\t\r\n\t\t\r\n\r\n\t\t// debugging function that prints out an object\r\n\t\tfunction alertObject(obj)\r\n\t\t{\r\n\t\t\tvar msg = '';\r\n\t\t\tfunction traverse(obj, depth)\r\n\t\t\t{\r\n\t\t\t\tif (!depth)\r\n\t\t\t\t\tdepth = 0;\r\n\t\t\t\tfor (var i = 0; i < obj.length; ++i)\r\n\t\t\t\t{\r\n\t\t\t\t\tfor (var j=0; j<depth; j++)\r\n\t\t\t\t\t\tmsg += '\\t';\r\n\t\t\t\t\r\n\t\t\t\t\tif( typeof obj[i] == \"object\")\r\n\t\t\t\t\t{\t// its an object\r\n\t\t\t\t\t\tmsg += ''+i+':\\n';\r\n\t\t\t\t\t\ttraverse(obj[i], depth+1);\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse\r\n\t\t\t\t\t{\t// its a value\r\n\t\t\t\t\t\tmsg += ''+i+': '+obj[i]+'\\n';\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\ttraverse(obj);\r\n\t\t\talert(msg);\r\n\t\t}\r\n\t\t\r\n\t\tfunction calcTotal(data)\r\n\t\t{\r\n\t\t\tfor (var i = 0; i < data.length; ++i)\r\n\t\t\t{\r\n\t\t\t\tvar item = parseFloat(data[i].data[0][1]);\r\n\t\t\t\tif (item)\r\n\t\t\t\t\ttotal += item;\r\n\t\t\t}\r\n\t\t}\t\r\n\t\t\r\n\t\tfunction processDatapoints(plot, series, data, datapoints) \r\n\t\t{\t\r\n\t\t\tif (!processed)\r\n\t\t\t{\r\n\t\t\t\tprocessed = true;\r\n\t\t\t\r\n\t\t\t\tcanvas = plot.getCanvas();\r\n\t\t\t\ttarget = $(canvas).parent();\r\n\t\t\t\toptions = plot.getOptions();\r\n\t\t\t\r\n\t\t\t\tplot.setData(combine(plot.getData()));\r\n\t\t\t}\r\n\t\t}\r\n\t\t\r\n\t\tfunction setupPie()\r\n\t\t{\r\n\t\t\tlegendWidth = target.children().filter('.legend').children().width();\r\n\t\t\r\n\t\t\t// calculate maximum radius and center point\r\n\t\t\tmaxRadius =  Math.min(canvas.width,(canvas.height/options.series.pie.tilt))/2;\r\n\t\t\tcenterTop = (canvas.height/2)+options.series.pie.offset.top;\r\n\t\t\tcenterLeft = (canvas.width/2);\r\n\t\t\t\r\n\t\t\tif (options.series.pie.offset.left=='auto')\r\n\t\t\t\tif (options.legend.position.match('w'))\r\n\t\t\t\t\tcenterLeft += legendWidth/2;\r\n\t\t\t\telse\r\n\t\t\t\t\tcenterLeft -= legendWidth/2;\r\n\t\t\telse\r\n\t\t\t\tcenterLeft += options.series.pie.offset.left;\r\n\t\t\t\t\t\r\n\t\t\tif (centerLeft<maxRadius)\r\n\t\t\t\tcenterLeft = maxRadius;\r\n\t\t\telse if (centerLeft>canvas.width-maxRadius)\r\n\t\t\t\tcenterLeft = canvas.width-maxRadius;\r\n\t\t}\r\n\t\t\r\n\t\tfunction fixData(data)\r\n\t\t{\r\n\t\t\tfor (var i = 0; i < data.length; ++i)\r\n\t\t\t{\r\n\t\t\t\tif (typeof(data[i].data)=='number')\r\n\t\t\t\t\tdata[i].data = [[1,data[i].data]];\r\n\t\t\t\telse if (typeof(data[i].data)=='undefined' || typeof(data[i].data[0])=='undefined')\r\n\t\t\t\t{\r\n\t\t\t\t\tif (typeof(data[i].data)!='undefined' && typeof(data[i].data.label)!='undefined')\r\n\t\t\t\t\t\tdata[i].label = data[i].data.label; // fix weirdness coming from flot\r\n\t\t\t\t\tdata[i].data = [[1,0]];\r\n\t\t\t\t\t\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\treturn data;\r\n\t\t}\r\n\t\t\r\n\t\tfunction combine(data)\r\n\t\t{\r\n\t\t\tdata = fixData(data);\r\n\t\t\tcalcTotal(data);\r\n\t\t\tvar combined = 0;\r\n\t\t\tvar numCombined = 0;\r\n\t\t\tvar color = options.series.pie.combine.color;\r\n\t\t\t\r\n\t\t\tvar newdata = [];\r\n\t\t\tfor (var i = 0; i < data.length; ++i)\r\n\t\t\t{\r\n\t\t\t\t// make sure its a number\r\n\t\t\t\tdata[i].data[0][1] = parseFloat(data[i].data[0][1]);\r\n\t\t\t\tif (!data[i].data[0][1])\r\n\t\t\t\t\tdata[i].data[0][1] = 0;\r\n\t\t\t\t\t\r\n\t\t\t\tif (data[i].data[0][1]/total<=options.series.pie.combine.threshold)\r\n\t\t\t\t{\r\n\t\t\t\t\tcombined += data[i].data[0][1];\r\n\t\t\t\t\tnumCombined++;\r\n\t\t\t\t\tif (!color)\r\n\t\t\t\t\t\tcolor = data[i].color;\r\n\t\t\t\t}\t\t\t\t\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\tnewdata.push({\r\n\t\t\t\t\t\tdata: [[1,data[i].data[0][1]]], \r\n\t\t\t\t\t\tcolor: data[i].color, \r\n\t\t\t\t\t\tlabel: data[i].label,\r\n\t\t\t\t\t\tangle: (data[i].data[0][1]*(Math.PI*2))/total,\r\n\t\t\t\t\t\tpercent: (data[i].data[0][1]/total*100)\r\n\t\t\t\t\t});\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\tif (numCombined>0)\r\n\t\t\t\tnewdata.push({\r\n\t\t\t\t\tdata: [[1,combined]], \r\n\t\t\t\t\tcolor: color, \r\n\t\t\t\t\tlabel: options.series.pie.combine.label,\r\n\t\t\t\t\tangle: (combined*(Math.PI*2))/total,\r\n\t\t\t\t\tpercent: (combined/total*100)\r\n\t\t\t\t});\r\n\t\t\treturn newdata;\r\n\t\t}\t\t\r\n\t\t\r\n\t\tfunction draw(plot, newCtx)\r\n\t\t{\r\n\t\t\tif (!target) return; // if no series were passed\r\n\t\t\tctx = newCtx;\r\n\t\t\r\n\t\t\tsetupPie();\r\n\t\t\tvar slices = plot.getData();\r\n\t\t\r\n\t\t\tvar attempts = 0;\r\n\t\t\twhile (redraw && attempts<redrawAttempts)\r\n\t\t\t{\r\n\t\t\t\tredraw = false;\r\n\t\t\t\tif (attempts>0)\r\n\t\t\t\t\tmaxRadius *= shrink;\r\n\t\t\t\tattempts += 1;\r\n\t\t\t\tclear();\r\n\t\t\t\tif (options.series.pie.tilt<=0.8)\r\n\t\t\t\t\tdrawShadow();\r\n\t\t\t\tdrawPie();\r\n\t\t\t}\r\n\t\t\tif (attempts >= redrawAttempts) {\r\n\t\t\t\tclear();\r\n\t\t\t\ttarget.prepend('<div class=\"error\">Could not draw pie with labels contained inside canvas</div>');\r\n\t\t\t}\r\n\t\t\t\r\n\t\t\tif ( plot.setSeries && plot.insertLegend )\r\n\t\t\t{\r\n\t\t\t\tplot.setSeries(slices);\r\n\t\t\t\tplot.insertLegend();\r\n\t\t\t}\r\n\t\t\t\r\n\t\t\t// we're actually done at this point, just defining internal functions at this point\r\n\t\t\t\r\n\t\t\tfunction clear()\r\n\t\t\t{\r\n\t\t\t\tctx.clearRect(0,0,canvas.width,canvas.height);\r\n\t\t\t\ttarget.children().filter('.pieLabel, .pieLabelBackground').remove();\r\n\t\t\t}\r\n\t\t\t\r\n\t\t\tfunction drawShadow()\r\n\t\t\t{\r\n\t\t\t\tvar shadowLeft = 5;\r\n\t\t\t\tvar shadowTop = 15;\r\n\t\t\t\tvar edge = 10;\r\n\t\t\t\tvar alpha = 0.02;\r\n\t\t\t\r\n\t\t\t\t// set radius\r\n\t\t\t\tif (options.series.pie.radius>1)\r\n\t\t\t\t\tvar radius = options.series.pie.radius;\r\n\t\t\t\telse\r\n\t\t\t\t\tvar radius = maxRadius * options.series.pie.radius;\r\n\t\t\t\t\t\r\n\t\t\t\tif (radius>=(canvas.width/2)-shadowLeft || radius*options.series.pie.tilt>=(canvas.height/2)-shadowTop || radius<=edge)\r\n\t\t\t\t\treturn;\t// shadow would be outside canvas, so don't draw it\r\n\t\t\t\r\n\t\t\t\tctx.save();\r\n\t\t\t\tctx.translate(shadowLeft,shadowTop);\r\n\t\t\t\tctx.globalAlpha = alpha;\r\n\t\t\t\tctx.fillStyle = '#000';\r\n\r\n\t\t\t\t// center and rotate to starting position\r\n\t\t\t\tctx.translate(centerLeft,centerTop);\r\n\t\t\t\tctx.scale(1, options.series.pie.tilt);\r\n\t\t\t\t\r\n\t\t\t\t//radius -= edge;\r\n\t\t\t\tfor (var i=1; i<=edge; i++)\r\n\t\t\t\t{\r\n\t\t\t\t\tctx.beginPath();\r\n\t\t\t\t\tctx.arc(0,0,radius,0,Math.PI*2,false);\r\n\t\t\t\t\tctx.fill();\r\n\t\t\t\t\tradius -= i;\r\n\t\t\t\t}\t\r\n\t\t\t\t\r\n\t\t\t\tctx.restore();\r\n\t\t\t}\r\n\t\t\t\r\n\t\t\tfunction drawPie()\r\n\t\t\t{\r\n\t\t\t\tstartAngle = Math.PI*options.series.pie.startAngle;\r\n\t\t\t\t\r\n\t\t\t\t// set radius\r\n\t\t\t\tif (options.series.pie.radius>1)\r\n\t\t\t\t\tvar radius = options.series.pie.radius;\r\n\t\t\t\telse\r\n\t\t\t\t\tvar radius = maxRadius * options.series.pie.radius;\r\n\t\t\t\t\r\n\t\t\t\t// center and rotate to starting position\r\n\t\t\t\tctx.save();\r\n\t\t\t\tctx.translate(centerLeft,centerTop);\r\n\t\t\t\tctx.scale(1, options.series.pie.tilt);\r\n\t\t\t\t//ctx.rotate(startAngle); // start at top; -- This doesn't work properly in Opera\r\n\t\t\t\t\r\n\t\t\t\t// draw slices\r\n\t\t\t\tctx.save();\r\n\t\t\t\tvar currentAngle = startAngle;\r\n\t\t\t\tfor (var i = 0; i < slices.length; ++i)\r\n\t\t\t\t{\r\n\t\t\t\t\tslices[i].startAngle = currentAngle;\r\n\t\t\t\t\tdrawSlice(slices[i].angle, slices[i].color, true);\r\n\t\t\t\t}\r\n\t\t\t\tctx.restore();\r\n\t\t\t\t\r\n\t\t\t\t// draw slice outlines\r\n\t\t\t\tctx.save();\r\n\t\t\t\tctx.lineWidth = options.series.pie.stroke.width;\r\n\t\t\t\tcurrentAngle = startAngle;\r\n\t\t\t\tfor (var i = 0; i < slices.length; ++i)\r\n\t\t\t\t\tdrawSlice(slices[i].angle, options.series.pie.stroke.color, false);\r\n\t\t\t\tctx.restore();\r\n\t\t\t\t\t\r\n\t\t\t\t// draw donut hole\r\n\t\t\t\tdrawDonutHole(ctx);\r\n\t\t\t\t\r\n\t\t\t\t// draw labels\r\n\t\t\t\tif (options.series.pie.label.show)\r\n\t\t\t\t\tdrawLabels();\r\n\t\t\t\t\r\n\t\t\t\t// restore to original state\r\n\t\t\t\tctx.restore();\r\n\t\t\t\t\r\n\t\t\t\tfunction drawSlice(angle, color, fill)\r\n\t\t\t\t{\t\r\n\t\t\t\t\tif (angle<=0)\r\n\t\t\t\t\t\treturn;\r\n\t\t\t\t\r\n\t\t\t\t\tif (fill)\r\n\t\t\t\t\t\tctx.fillStyle = color;\r\n\t\t\t\t\telse\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tctx.strokeStyle = color;\r\n\t\t\t\t\t\tctx.lineJoin = 'round';\r\n\t\t\t\t\t}\r\n\t\t\t\t\t\t\r\n\t\t\t\t\tctx.beginPath();\r\n\t\t\t\t\tif (Math.abs(angle - Math.PI*2) > 0.000000001)\r\n\t\t\t\t\t\tctx.moveTo(0,0); // Center of the pie\r\n\t\t\t\t\telse if ($.browser.msie)\r\n\t\t\t\t\t\tangle -= 0.0001;\r\n\t\t\t\t\t//ctx.arc(0,0,radius,0,angle,false); // This doesn't work properly in Opera\r\n\t\t\t\t\tctx.arc(0,0,radius,currentAngle,currentAngle+angle,false);\r\n\t\t\t\t\tctx.closePath();\r\n\t\t\t\t\t//ctx.rotate(angle); // This doesn't work properly in Opera\r\n\t\t\t\t\tcurrentAngle += angle;\r\n\t\t\t\t\t\r\n\t\t\t\t\tif (fill)\r\n\t\t\t\t\t\tctx.fill();\r\n\t\t\t\t\telse\r\n\t\t\t\t\t\tctx.stroke();\r\n\t\t\t\t}\r\n\t\t\t\t\r\n\t\t\t\tfunction drawLabels()\r\n\t\t\t\t{\r\n\t\t\t\t\tvar currentAngle = startAngle;\r\n\t\t\t\t\t\r\n\t\t\t\t\t// set radius\r\n\t\t\t\t\tif (options.series.pie.label.radius>1)\r\n\t\t\t\t\t\tvar radius = options.series.pie.label.radius;\r\n\t\t\t\t\telse\r\n\t\t\t\t\t\tvar radius = maxRadius * options.series.pie.label.radius;\r\n\t\t\t\t\t\r\n\t\t\t\t\tfor (var i = 0; i < slices.length; ++i)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tif (slices[i].percent >= options.series.pie.label.threshold*100)\r\n\t\t\t\t\t\t\tdrawLabel(slices[i], currentAngle, i);\r\n\t\t\t\t\t\tcurrentAngle += slices[i].angle;\r\n\t\t\t\t\t}\r\n\t\t\t\t\t\r\n\t\t\t\t\tfunction drawLabel(slice, startAngle, index)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tif (slice.data[0][1]==0)\r\n\t\t\t\t\t\t\treturn;\r\n\t\t\t\t\t\t\t\r\n\t\t\t\t\t\t// format label text\r\n\t\t\t\t\t\tvar lf = options.legend.labelFormatter, text, plf = options.series.pie.label.formatter;\r\n\t\t\t\t\t\tif (lf)\r\n\t\t\t\t\t\t\ttext = lf(slice.label, slice);\r\n\t\t\t\t\t\telse\r\n\t\t\t\t\t\t\ttext = slice.label;\r\n\t\t\t\t\t\tif (plf)\r\n\t\t\t\t\t\t\ttext = plf(text, slice);\r\n\t\t\t\t\t\t\t\r\n\t\t\t\t\t\tvar halfAngle = ((startAngle+slice.angle) + startAngle)/2;\r\n\t\t\t\t\t\tvar x = centerLeft + Math.round(Math.cos(halfAngle) * radius);\r\n\t\t\t\t\t\tvar y = centerTop + Math.round(Math.sin(halfAngle) * radius) * options.series.pie.tilt;\r\n\t\t\t\t\t\t\r\n\t\t\t\t\t\tvar html = '<span class=\"pieLabel\" id=\"pieLabel'+index+'\" style=\"position:absolute;top:' + y + 'px;left:' + x + 'px;\">' + text + \"</span>\";\r\n\t\t\t\t\t\ttarget.append(html);\r\n\t\t\t\t\t\tvar label = target.children('#pieLabel'+index);\r\n\t\t\t\t\t\tvar labelTop = (y - label.height()/2);\r\n\t\t\t\t\t\tvar labelLeft = (x - label.width()/2);\r\n\t\t\t\t\t\tlabel.css('top', labelTop);\r\n\t\t\t\t\t\tlabel.css('left', labelLeft);\r\n\t\t\t\t\t\t\r\n\t\t\t\t\t\t// check to make sure that the label is not outside the canvas\r\n\t\t\t\t\t\tif (0-labelTop>0 || 0-labelLeft>0 || canvas.height-(labelTop+label.height())<0 || canvas.width-(labelLeft+label.width())<0)\r\n\t\t\t\t\t\t\tredraw = true;\r\n\t\t\t\t\t\t\r\n\t\t\t\t\t\tif (options.series.pie.label.background.opacity != 0) {\r\n\t\t\t\t\t\t\t// put in the transparent background separately to avoid blended labels and label boxes\r\n\t\t\t\t\t\t\tvar c = options.series.pie.label.background.color;\r\n\t\t\t\t\t\t\tif (c == null) {\r\n\t\t\t\t\t\t\t\tc = slice.color;\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\tvar pos = 'top:'+labelTop+'px;left:'+labelLeft+'px;';\r\n\t\t\t\t\t\t\t$('<div class=\"pieLabelBackground\" style=\"position:absolute;width:' + label.width() + 'px;height:' + label.height() + 'px;' + pos +'background-color:' + c + ';\"> </div>').insertBefore(label).css('opacity', options.series.pie.label.background.opacity);\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t} // end individual label function\r\n\t\t\t\t} // end drawLabels function\r\n\t\t\t} // end drawPie function\r\n\t\t} // end draw function\r\n\t\t\r\n\t\t// Placed here because it needs to be accessed from multiple locations \r\n\t\tfunction drawDonutHole(layer)\r\n\t\t{\r\n\t\t\t// draw donut hole\r\n\t\t\tif(options.series.pie.innerRadius > 0)\r\n\t\t\t{\r\n\t\t\t\t// subtract the center\r\n\t\t\t\tlayer.save();\r\n\t\t\t\tinnerRadius = options.series.pie.innerRadius > 1 ? options.series.pie.innerRadius : maxRadius * options.series.pie.innerRadius;\r\n\t\t\t\tlayer.globalCompositeOperation = 'destination-out'; // this does not work with excanvas, but it will fall back to using the stroke color\r\n\t\t\t\tlayer.beginPath();\r\n\t\t\t\tlayer.fillStyle = options.series.pie.stroke.color;\r\n\t\t\t\tlayer.arc(0,0,innerRadius,0,Math.PI*2,false);\r\n\t\t\t\tlayer.fill();\r\n\t\t\t\tlayer.closePath();\r\n\t\t\t\tlayer.restore();\r\n\t\t\t\t\r\n\t\t\t\t// add inner stroke\r\n\t\t\t\tlayer.save();\r\n\t\t\t\tlayer.beginPath();\r\n\t\t\t\tlayer.strokeStyle = options.series.pie.stroke.color;\r\n\t\t\t\tlayer.arc(0,0,innerRadius,0,Math.PI*2,false);\r\n\t\t\t\tlayer.stroke();\r\n\t\t\t\tlayer.closePath();\r\n\t\t\t\tlayer.restore();\r\n\t\t\t\t// TODO: add extra shadow inside hole (with a mask) if the pie is tilted.\r\n\t\t\t}\r\n\t\t}\r\n\t\t\r\n\t\t//-- Additional Interactive related functions --\r\n\t\t\r\n\t\tfunction isPointInPoly(poly, pt)\r\n\t\t{\r\n\t\t\tfor(var c = false, i = -1, l = poly.length, j = l - 1; ++i < l; j = i)\r\n\t\t\t\t((poly[i][1] <= pt[1] && pt[1] < poly[j][1]) || (poly[j][1] <= pt[1] && pt[1]< poly[i][1]))\r\n\t\t\t\t&& (pt[0] < (poly[j][0] - poly[i][0]) * (pt[1] - poly[i][1]) / (poly[j][1] - poly[i][1]) + poly[i][0])\r\n\t\t\t\t&& (c = !c);\r\n\t\t\treturn c;\r\n\t\t}\r\n\t\t\r\n\t\tfunction findNearbySlice(mouseX, mouseY)\r\n\t\t{\r\n\t\t\tvar slices = plot.getData(),\r\n\t\t\t\toptions = plot.getOptions(),\r\n\t\t\t\tradius = options.series.pie.radius > 1 ? options.series.pie.radius : maxRadius * options.series.pie.radius;\r\n\t\t\t\r\n\t\t\tfor (var i = 0; i < slices.length; ++i) \r\n\t\t\t{\r\n\t\t\t\tvar s = slices[i];\t\r\n\t\t\t\t\r\n\t\t\t\tif(s.pie.show)\r\n\t\t\t\t{\r\n\t\t\t\t\tctx.save();\r\n\t\t\t\t\tctx.beginPath();\r\n\t\t\t\t\tctx.moveTo(0,0); // Center of the pie\r\n\t\t\t\t\t//ctx.scale(1, options.series.pie.tilt);\t// this actually seems to break everything when here.\r\n\t\t\t\t\tctx.arc(0,0,radius,s.startAngle,s.startAngle+s.angle,false);\r\n\t\t\t\t\tctx.closePath();\r\n\t\t\t\t\tx = mouseX-centerLeft;\r\n\t\t\t\t\ty = mouseY-centerTop;\r\n\t\t\t\t\tif(ctx.isPointInPath)\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tif (ctx.isPointInPath(mouseX-centerLeft, mouseY-centerTop))\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t//alert('found slice!');\r\n\t\t\t\t\t\t\tctx.restore();\r\n\t\t\t\t\t\t\treturn {datapoint: [s.percent, s.data], dataIndex: 0, series: s, seriesIndex: i};\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\t\t\t\t\telse\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\t// excanvas for IE doesn;t support isPointInPath, this is a workaround. \r\n\t\t\t\t\t\tp1X = (radius * Math.cos(s.startAngle));\r\n\t\t\t\t\t\tp1Y = (radius * Math.sin(s.startAngle));\r\n\t\t\t\t\t\tp2X = (radius * Math.cos(s.startAngle+(s.angle/4)));\r\n\t\t\t\t\t\tp2Y = (radius * Math.sin(s.startAngle+(s.angle/4)));\r\n\t\t\t\t\t\tp3X = (radius * Math.cos(s.startAngle+(s.angle/2)));\r\n\t\t\t\t\t\tp3Y = (radius * Math.sin(s.startAngle+(s.angle/2)));\r\n\t\t\t\t\t\tp4X = (radius * Math.cos(s.startAngle+(s.angle/1.5)));\r\n\t\t\t\t\t\tp4Y = (radius * Math.sin(s.startAngle+(s.angle/1.5)));\r\n\t\t\t\t\t\tp5X = (radius * Math.cos(s.startAngle+s.angle));\r\n\t\t\t\t\t\tp5Y = (radius * Math.sin(s.startAngle+s.angle));\r\n\t\t\t\t\t\tarrPoly = [[0,0],[p1X,p1Y],[p2X,p2Y],[p3X,p3Y],[p4X,p4Y],[p5X,p5Y]];\r\n\t\t\t\t\t\tarrPoint = [x,y];\r\n\t\t\t\t\t\t// TODO: perhaps do some mathmatical trickery here with the Y-coordinate to compensate for pie tilt?\r\n\t\t\t\t\t\tif(isPointInPoly(arrPoly, arrPoint))\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tctx.restore();\r\n\t\t\t\t\t\t\treturn {datapoint: [s.percent, s.data], dataIndex: 0, series: s, seriesIndex: i};\r\n\t\t\t\t\t\t}\t\t\t\r\n\t\t\t\t\t}\r\n\t\t\t\t\tctx.restore();\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\t\r\n\t\t\treturn null;\r\n\t\t}\r\n\r\n\t\tfunction onMouseMove(e) \r\n\t\t{\r\n\t\t\ttriggerClickHoverEvent('plothover', e);\r\n\t\t}\r\n\t\t\r\n        function onClick(e) \r\n\t\t{\r\n\t\t\ttriggerClickHoverEvent('plotclick', e);\r\n        }\r\n\r\n\t\t// trigger click or hover event (they send the same parameters so we share their code)\r\n\t\tfunction triggerClickHoverEvent(eventname, e) \r\n\t\t{\r\n\t\t\tvar offset = plot.offset(),\r\n\t\t\t\tcanvasX = parseInt(e.pageX - offset.left),\r\n\t\t\t\tcanvasY =  parseInt(e.pageY - offset.top),\r\n\t\t\t\titem = findNearbySlice(canvasX, canvasY);\r\n\t\t\t\r\n\t\t\tif (options.grid.autoHighlight) \r\n\t\t\t{\r\n\t\t\t\t// clear auto-highlights\r\n\t\t\t\tfor (var i = 0; i < highlights.length; ++i) \r\n\t\t\t\t{\r\n\t\t\t\t\tvar h = highlights[i];\r\n\t\t\t\t\tif (h.auto == eventname && !(item && h.series == item.series))\r\n\t\t\t\t\t\tunhighlight(h.series);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\t\r\n\t\t\t// highlight the slice\r\n\t\t\tif (item) \r\n\t\t\t    highlight(item.series, eventname);\r\n\t\t\t\t\r\n\t\t\t// trigger any hover bind events\r\n\t\t\tvar pos = { pageX: e.pageX, pageY: e.pageY };\r\n\t\t\ttarget.trigger(eventname, [ pos, item ]);\t\r\n\t\t}\r\n\r\n\t\tfunction highlight(s, auto) \r\n\t\t{\r\n\t\t\tif (typeof s == \"number\")\r\n\t\t\t\ts = series[s];\r\n\r\n\t\t\tvar i = indexOfHighlight(s);\r\n\t\t\tif (i == -1) \r\n\t\t\t{\r\n\t\t\t\thighlights.push({ series: s, auto: auto });\r\n\t\t\t\tplot.triggerRedrawOverlay();\r\n\t\t\t}\r\n\t\t\telse if (!auto)\r\n\t\t\t\thighlights[i].auto = false;\r\n\t\t}\r\n\r\n\t\tfunction unhighlight(s) \r\n\t\t{\r\n\t\t\tif (s == null) \r\n\t\t\t{\r\n\t\t\t\thighlights = [];\r\n\t\t\t\tplot.triggerRedrawOverlay();\r\n\t\t\t}\r\n\t\t\t\r\n\t\t\tif (typeof s == \"number\")\r\n\t\t\t\ts = series[s];\r\n\r\n\t\t\tvar i = indexOfHighlight(s);\r\n\t\t\tif (i != -1) \r\n\t\t\t{\r\n\t\t\t\thighlights.splice(i, 1);\r\n\t\t\t\tplot.triggerRedrawOverlay();\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tfunction indexOfHighlight(s) \r\n\t\t{\r\n\t\t\tfor (var i = 0; i < highlights.length; ++i) \r\n\t\t\t{\r\n\t\t\t\tvar h = highlights[i];\r\n\t\t\t\tif (h.series == s)\r\n\t\t\t\t\treturn i;\r\n\t\t\t}\r\n\t\t\treturn -1;\r\n\t\t}\r\n\r\n\t\tfunction drawOverlay(plot, octx) \r\n\t\t{\r\n\t\t\t//alert(options.series.pie.radius);\r\n\t\t\tvar options = plot.getOptions();\r\n\t\t\t//alert(options.series.pie.radius);\r\n\t\t\t\r\n\t\t\tvar radius = options.series.pie.radius > 1 ? options.series.pie.radius : maxRadius * options.series.pie.radius;\r\n\r\n\t\t\toctx.save();\r\n\t\t\toctx.translate(centerLeft, centerTop);\r\n\t\t\toctx.scale(1, options.series.pie.tilt);\r\n\t\t\t\r\n\t\t\tfor (i = 0; i < highlights.length; ++i) \r\n\t\t\t\tdrawHighlight(highlights[i].series);\r\n\t\t\t\r\n\t\t\tdrawDonutHole(octx);\r\n\r\n\t\t\toctx.restore();\r\n\r\n\t\t\tfunction drawHighlight(series) \r\n\t\t\t{\r\n\t\t\t\tif (series.angle < 0) return;\r\n\t\t\t\t\r\n\t\t\t\t//octx.fillStyle = parseColor(options.series.pie.highlight.color).scale(null, null, null, options.series.pie.highlight.opacity).toString();\r\n\t\t\t\toctx.fillStyle = \"rgba(255, 255, 255, \"+options.series.pie.highlight.opacity+\")\"; // this is temporary until we have access to parseColor\r\n\t\t\t\t\r\n\t\t\t\toctx.beginPath();\r\n\t\t\t\tif (Math.abs(series.angle - Math.PI*2) > 0.000000001)\r\n\t\t\t\t\toctx.moveTo(0,0); // Center of the pie\r\n\t\t\t\toctx.arc(0,0,radius,series.startAngle,series.startAngle+series.angle,false);\r\n\t\t\t\toctx.closePath();\r\n\t\t\t\toctx.fill();\r\n\t\t\t}\r\n\t\t\t\r\n\t\t}\t\r\n\t\t\r\n\t} // end init (plugin body)\r\n\t\r\n\t// define pie specific options and their default values\r\n\tvar options = {\r\n\t\tseries: {\r\n\t\t\tpie: {\r\n\t\t\t\tshow: false,\r\n\t\t\t\tradius: 'auto',\t// actual radius of the visible pie (based on full calculated radius if <=1, or hard pixel value)\r\n\t\t\t\tinnerRadius:0, /* for donut */\r\n\t\t\t\tstartAngle: 3/2,\r\n\t\t\t\ttilt: 1,\r\n\t\t\t\toffset: {\r\n\t\t\t\t\ttop: 0,\r\n\t\t\t\t\tleft: 'auto'\r\n\t\t\t\t},\r\n\t\t\t\tstroke: {\r\n\t\t\t\t\tcolor: '#FFF',\r\n\t\t\t\t\twidth: 1\r\n\t\t\t\t},\r\n\t\t\t\tlabel: {\r\n\t\t\t\t\tshow: 'auto',\r\n\t\t\t\t\tformatter: function(label, slice){\r\n\t\t\t\t\t\treturn '<div style=\"font-size:x-small;text-align:center;padding:2px;color:'+slice.color+';\">'+label+'<br/>'+Math.round(slice.percent)+'%</div>';\r\n\t\t\t\t\t},\t// formatter function\r\n\t\t\t\t\tradius: 1,\t// radius at which to place the labels (based on full calculated radius if <=1, or hard pixel value)\r\n\t\t\t\t\tbackground: {\r\n\t\t\t\t\t\tcolor: null,\r\n\t\t\t\t\t\topacity: 0\r\n\t\t\t\t\t},\r\n\t\t\t\t\tthreshold: 0\t// percentage at which to hide the label (i.e. the slice is too narrow)\r\n\t\t\t\t},\r\n\t\t\t\tcombine: {\r\n\t\t\t\t\tthreshold: -1,\t// percentage at which to combine little slices into one larger slice\r\n\t\t\t\t\tcolor: null,\t// color to give the new slice (auto-generated if null)\r\n\t\t\t\t\tlabel: 'Other'\t// label to give the new slice\r\n\t\t\t\t},\r\n\t\t\t\thighlight: {\r\n\t\t\t\t\t//color: '#FFF',\t\t// will add this functionality once parseColor is available\r\n\t\t\t\t\topacity: 0.5\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t};\r\n    \r\n\t$.plot.plugins.push({\r\n\t\tinit: init,\r\n\t\toptions: options,\r\n\t\tname: \"pie\",\r\n\t\tversion: \"1.0\"\r\n\t});\r\n})(jQuery);\r\n"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/js/flot/jquery.flot.resize.js",
    "content": "/*\nFlot plugin for automatically redrawing plots when the placeholder\nsize changes, e.g. on window resizes.\n\nIt works by listening for changes on the placeholder div (through the\njQuery resize event plugin) - if the size changes, it will redraw the\nplot.\n\nThere are no options. If you need to disable the plugin for some\nplots, you can just fix the size of their placeholders.\n*/\n\n\n/* Inline dependency: \n * jQuery resize event - v1.1 - 3/14/2010\n * http://benalman.com/projects/jquery-resize-plugin/\n * \n * Copyright (c) 2010 \"Cowboy\" Ben Alman\n * Dual licensed under the MIT and GPL licenses.\n * http://benalman.com/about/license/\n */\n(function($,h,c){var a=$([]),e=$.resize=$.extend($.resize,{}),i,k=\"setTimeout\",j=\"resize\",d=j+\"-special-event\",b=\"delay\",f=\"throttleWindow\";e[b]=250;e[f]=true;$.event.special[j]={setup:function(){if(!e[f]&&this[k]){return false}var l=$(this);a=a.add(l);$.data(this,d,{w:l.width(),h:l.height()});if(a.length===1){g()}},teardown:function(){if(!e[f]&&this[k]){return false}var l=$(this);a=a.not(l);l.removeData(d);if(!a.length){clearTimeout(i)}},add:function(l){if(!e[f]&&this[k]){return false}var n;function m(s,o,p){var q=$(this),r=$.data(this,d);r.w=o!==c?o:q.width();r.h=p!==c?p:q.height();n.apply(this,arguments)}if($.isFunction(l)){n=l;return m}else{n=l.handler;l.handler=m}}};function g(){i=h[k](function(){a.each(function(){var n=$(this),m=n.width(),l=n.height(),o=$.data(this,d);if(m!==o.w||l!==o.h){n.trigger(j,[o.w=m,o.h=l])}});g()},e[b])}})(jQuery,this);\n\n\n(function ($) {\n    var options = { }; // no options\n\n    function init(plot) {\n        function onResize() {\n            var placeholder = plot.getPlaceholder();\n\n            // somebody might have hidden us and we can't plot\n            // when we don't have the dimensions\n            if (placeholder.width() == 0 || placeholder.height() == 0)\n                return;\n\n            plot.resize();\n            plot.setupGrid();\n            plot.draw();\n        }\n        \n        function bindEvents(plot, eventHolder) {\n            plot.getPlaceholder().resize(onResize);\n        }\n\n        function shutdown(plot, eventHolder) {\n            plot.getPlaceholder().unbind(\"resize\", onResize);\n        }\n        \n        plot.hooks.bindEvents.push(bindEvents);\n        plot.hooks.shutdown.push(shutdown);\n    }\n    \n    $.plot.plugins.push({\n        init: init,\n        options: options,\n        name: 'resize',\n        version: '1.0'\n    });\n})(jQuery);\n"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/js/flot/jquery.flot.selection.js",
    "content": "/*\nFlot plugin for selecting regions.\n\nThe plugin defines the following options:\n\n  selection: {\n    mode: null or \"x\" or \"y\" or \"xy\",\n    color: color\n  }\n\nSelection support is enabled by setting the mode to one of \"x\", \"y\" or\n\"xy\". In \"x\" mode, the user will only be able to specify the x range,\nsimilarly for \"y\" mode. For \"xy\", the selection becomes a rectangle\nwhere both ranges can be specified. \"color\" is color of the selection\n(if you need to change the color later on, you can get to it with\nplot.getOptions().selection.color).\n\nWhen selection support is enabled, a \"plotselected\" event will be\nemitted on the DOM element you passed into the plot function. The\nevent handler gets a parameter with the ranges selected on the axes,\nlike this:\n\n  placeholder.bind(\"plotselected\", function(event, ranges) {\n    alert(\"You selected \" + ranges.xaxis.from + \" to \" + ranges.xaxis.to)\n    // similar for yaxis - with multiple axes, the extra ones are in\n    // x2axis, x3axis, ...\n  });\n\nThe \"plotselected\" event is only fired when the user has finished\nmaking the selection. A \"plotselecting\" event is fired during the\nprocess with the same parameters as the \"plotselected\" event, in case\nyou want to know what's happening while it's happening,\n\nA \"plotunselected\" event with no arguments is emitted when the user\nclicks the mouse to remove the selection.\n\nThe plugin allso adds the following methods to the plot object:\n\n- setSelection(ranges, preventEvent)\n\n  Set the selection rectangle. The passed in ranges is on the same\n  form as returned in the \"plotselected\" event. If the selection mode\n  is \"x\", you should put in either an xaxis range, if the mode is \"y\"\n  you need to put in an yaxis range and both xaxis and yaxis if the\n  selection mode is \"xy\", like this:\n\n    setSelection({ xaxis: { from: 0, to: 10 }, yaxis: { from: 40, to: 60 } });\n\n  setSelection will trigger the \"plotselected\" event when called. If\n  you don't want that to happen, e.g. if you're inside a\n  \"plotselected\" handler, pass true as the second parameter. If you\n  are using multiple axes, you can specify the ranges on any of those,\n  e.g. as x2axis/x3axis/... instead of xaxis, the plugin picks the\n  first one it sees.\n  \n- clearSelection(preventEvent)\n\n  Clear the selection rectangle. Pass in true to avoid getting a\n  \"plotunselected\" event.\n\n- getSelection()\n\n  Returns the current selection in the same format as the\n  \"plotselected\" event. If there's currently no selection, the\n  function returns null.\n\n*/\n\n(function ($) {\n    function init(plot) {\n        var selection = {\n                first: { x: -1, y: -1}, second: { x: -1, y: -1},\n                show: false,\n                active: false\n            };\n\n        // FIXME: The drag handling implemented here should be\n        // abstracted out, there's some similar code from a library in\n        // the navigation plugin, this should be massaged a bit to fit\n        // the Flot cases here better and reused. Doing this would\n        // make this plugin much slimmer.\n        var savedhandlers = {};\n\n        var mouseUpHandler = null;\n        \n        function onMouseMove(e) {\n            if (selection.active) {\n                updateSelection(e);\n                \n                plot.getPlaceholder().trigger(\"plotselecting\", [ getSelection() ]);\n            }\n        }\n\n        function onMouseDown(e) {\n            if (e.which != 1)  // only accept left-click\n                return;\n            \n            // cancel out any text selections\n            document.body.focus();\n\n            // prevent text selection and drag in old-school browsers\n            if (document.onselectstart !== undefined && savedhandlers.onselectstart == null) {\n                savedhandlers.onselectstart = document.onselectstart;\n                document.onselectstart = function () { return false; };\n            }\n            if (document.ondrag !== undefined && savedhandlers.ondrag == null) {\n                savedhandlers.ondrag = document.ondrag;\n                document.ondrag = function () { return false; };\n            }\n\n            setSelectionPos(selection.first, e);\n\n            selection.active = true;\n\n            // this is a bit silly, but we have to use a closure to be\n            // able to whack the same handler again\n            mouseUpHandler = function (e) { onMouseUp(e); };\n            \n            $(document).one(\"mouseup\", mouseUpHandler);\n        }\n\n        function onMouseUp(e) {\n            mouseUpHandler = null;\n            \n            // revert drag stuff for old-school browsers\n            if (document.onselectstart !== undefined)\n                document.onselectstart = savedhandlers.onselectstart;\n            if (document.ondrag !== undefined)\n                document.ondrag = savedhandlers.ondrag;\n\n            // no more dragging\n            selection.active = false;\n            updateSelection(e);\n\n            if (selectionIsSane())\n                triggerSelectedEvent();\n            else {\n                // this counts as a clear\n                plot.getPlaceholder().trigger(\"plotunselected\", [ ]);\n                plot.getPlaceholder().trigger(\"plotselecting\", [ null ]);\n            }\n\n            return false;\n        }\n\n        function getSelection() {\n            if (!selectionIsSane())\n                return null;\n\n            var r = {}, c1 = selection.first, c2 = selection.second;\n            $.each(plot.getAxes(), function (name, axis) {\n                if (axis.used) {\n                    var p1 = axis.c2p(c1[axis.direction]), p2 = axis.c2p(c2[axis.direction]); \n                    r[name] = { from: Math.min(p1, p2), to: Math.max(p1, p2) };\n                }\n            });\n            return r;\n        }\n\n        function triggerSelectedEvent() {\n            var r = getSelection();\n\n            plot.getPlaceholder().trigger(\"plotselected\", [ r ]);\n\n            // backwards-compat stuff, to be removed in future\n            if (r.xaxis && r.yaxis)\n                plot.getPlaceholder().trigger(\"selected\", [ { x1: r.xaxis.from, y1: r.yaxis.from, x2: r.xaxis.to, y2: r.yaxis.to } ]);\n        }\n\n        function clamp(min, value, max) {\n            return value < min ? min: (value > max ? max: value);\n        }\n\n        function setSelectionPos(pos, e) {\n            var o = plot.getOptions();\n            var offset = plot.getPlaceholder().offset();\n            var plotOffset = plot.getPlotOffset();\n            pos.x = clamp(0, e.pageX - offset.left - plotOffset.left, plot.width());\n            pos.y = clamp(0, e.pageY - offset.top - plotOffset.top, plot.height());\n\n            if (o.selection.mode == \"y\")\n                pos.x = pos == selection.first ? 0 : plot.width();\n\n            if (o.selection.mode == \"x\")\n                pos.y = pos == selection.first ? 0 : plot.height();\n        }\n\n        function updateSelection(pos) {\n            if (pos.pageX == null)\n                return;\n\n            setSelectionPos(selection.second, pos);\n            if (selectionIsSane()) {\n                selection.show = true;\n                plot.triggerRedrawOverlay();\n            }\n            else\n                clearSelection(true);\n        }\n\n        function clearSelection(preventEvent) {\n            if (selection.show) {\n                selection.show = false;\n                plot.triggerRedrawOverlay();\n                if (!preventEvent)\n                    plot.getPlaceholder().trigger(\"plotunselected\", [ ]);\n            }\n        }\n\n        // function taken from markings support in Flot\n        function extractRange(ranges, coord) {\n            var axis, from, to, key, axes = plot.getAxes();\n\n            for (var k in axes) {\n                axis = axes[k];\n                if (axis.direction == coord) {\n                    key = coord + axis.n + \"axis\";\n                    if (!ranges[key] && axis.n == 1)\n                        key = coord + \"axis\"; // support x1axis as xaxis\n                    if (ranges[key]) {\n                        from = ranges[key].from;\n                        to = ranges[key].to;\n                        break;\n                    }\n                }\n            }\n\n            // backwards-compat stuff - to be removed in future\n            if (!ranges[key]) {\n                axis = coord == \"x\" ? plot.getXAxes()[0] : plot.getYAxes()[0];\n                from = ranges[coord + \"1\"];\n                to = ranges[coord + \"2\"];\n            }\n\n            // auto-reverse as an added bonus\n            if (from != null && to != null && from > to) {\n                var tmp = from;\n                from = to;\n                to = tmp;\n            }\n            \n            return { from: from, to: to, axis: axis };\n        }\n        \n        function setSelection(ranges, preventEvent) {\n            var axis, range, o = plot.getOptions();\n\n            if (o.selection.mode == \"y\") {\n                selection.first.x = 0;\n                selection.second.x = plot.width();\n            }\n            else {\n                range = extractRange(ranges, \"x\");\n\n                selection.first.x = range.axis.p2c(range.from);\n                selection.second.x = range.axis.p2c(range.to);\n            }\n\n            if (o.selection.mode == \"x\") {\n                selection.first.y = 0;\n                selection.second.y = plot.height();\n            }\n            else {\n                range = extractRange(ranges, \"y\");\n\n                selection.first.y = range.axis.p2c(range.from);\n                selection.second.y = range.axis.p2c(range.to);\n            }\n\n            selection.show = true;\n            plot.triggerRedrawOverlay();\n            if (!preventEvent && selectionIsSane())\n                triggerSelectedEvent();\n        }\n\n        function selectionIsSane() {\n            var minSize = 5;\n            return Math.abs(selection.second.x - selection.first.x) >= minSize &&\n                Math.abs(selection.second.y - selection.first.y) >= minSize;\n        }\n\n        plot.clearSelection = clearSelection;\n        plot.setSelection = setSelection;\n        plot.getSelection = getSelection;\n\n        plot.hooks.bindEvents.push(function(plot, eventHolder) {\n            var o = plot.getOptions();\n            if (o.selection.mode != null) {\n                eventHolder.mousemove(onMouseMove);\n                eventHolder.mousedown(onMouseDown);\n            }\n        });\n\n\n        plot.hooks.drawOverlay.push(function (plot, ctx) {\n            // draw selection\n            if (selection.show && selectionIsSane()) {\n                var plotOffset = plot.getPlotOffset();\n                var o = plot.getOptions();\n\n                ctx.save();\n                ctx.translate(plotOffset.left, plotOffset.top);\n\n                var c = $.color.parse(o.selection.color);\n\n                ctx.strokeStyle = c.scale('a', 0.8).toString();\n                ctx.lineWidth = 1;\n                ctx.lineJoin = \"round\";\n                ctx.fillStyle = c.scale('a', 0.4).toString();\n\n                var x = Math.min(selection.first.x, selection.second.x),\n                    y = Math.min(selection.first.y, selection.second.y),\n                    w = Math.abs(selection.second.x - selection.first.x),\n                    h = Math.abs(selection.second.y - selection.first.y);\n\n                ctx.fillRect(x, y, w, h);\n                ctx.strokeRect(x, y, w, h);\n\n                ctx.restore();\n            }\n        });\n        \n        plot.hooks.shutdown.push(function (plot, eventHolder) {\n            eventHolder.unbind(\"mousemove\", onMouseMove);\n            eventHolder.unbind(\"mousedown\", onMouseDown);\n            \n            if (mouseUpHandler)\n                $(document).unbind(\"mouseup\", mouseUpHandler);\n        });\n\n    }\n\n    $.plot.plugins.push({\n        init: init,\n        options: {\n            selection: {\n                mode: null, // one of null, \"x\", \"y\" or \"xy\"\n                color: \"#e8cfac\"\n            }\n        },\n        name: 'selection',\n        version: '1.1'\n    });\n})(jQuery);\n"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/js/flot/jquery.flot.stack.js",
    "content": "/*\nFlot plugin for stacking data sets, i.e. putting them on top of each\nother, for accumulative graphs.\n\nThe plugin assumes the data is sorted on x (or y if stacking\nhorizontally). For line charts, it is assumed that if a line has an\nundefined gap (from a null point), then the line above it should have\nthe same gap - insert zeros instead of \"null\" if you want another\nbehaviour. This also holds for the start and end of the chart. Note\nthat stacking a mix of positive and negative values in most instances\ndoesn't make sense (so it looks weird).\n\nTwo or more series are stacked when their \"stack\" attribute is set to\nthe same key (which can be any number or string or just \"true\"). To\nspecify the default stack, you can set\n\n  series: {\n    stack: null or true or key (number/string)\n  }\n\nor specify it for a specific series\n\n  $.plot($(\"#placeholder\"), [{ data: [ ... ], stack: true }])\n  \nThe stacking order is determined by the order of the data series in\nthe array (later series end up on top of the previous).\n\nInternally, the plugin modifies the datapoints in each series, adding\nan offset to the y value. For line series, extra data points are\ninserted through interpolation. If there's a second y value, it's also\nadjusted (e.g for bar charts or filled areas).\n*/\n\n(function ($) {\n    var options = {\n        series: { stack: null } // or number/string\n    };\n    \n    function init(plot) {\n        function findMatchingSeries(s, allseries) {\n            var res = null\n            for (var i = 0; i < allseries.length; ++i) {\n                if (s == allseries[i])\n                    break;\n                \n                if (allseries[i].stack == s.stack)\n                    res = allseries[i];\n            }\n            \n            return res;\n        }\n        \n        function stackData(plot, s, datapoints) {\n            if (s.stack == null)\n                return;\n\n            var other = findMatchingSeries(s, plot.getData());\n            if (!other)\n                return;\n\n            var ps = datapoints.pointsize,\n                points = datapoints.points,\n                otherps = other.datapoints.pointsize,\n                otherpoints = other.datapoints.points,\n                newpoints = [],\n                px, py, intery, qx, qy, bottom,\n                withlines = s.lines.show,\n                horizontal = s.bars.horizontal,\n                withbottom = ps > 2 && (horizontal ? datapoints.format[2].x : datapoints.format[2].y),\n                withsteps = withlines && s.lines.steps,\n                fromgap = true,\n                keyOffset = horizontal ? 1 : 0,\n                accumulateOffset = horizontal ? 0 : 1,\n                i = 0, j = 0, l;\n\n            while (true) {\n                if (i >= points.length)\n                    break;\n\n                l = newpoints.length;\n\n                if (points[i] == null) {\n                    // copy gaps\n                    for (m = 0; m < ps; ++m)\n                        newpoints.push(points[i + m]);\n                    i += ps;\n                }\n                else if (j >= otherpoints.length) {\n                    // for lines, we can't use the rest of the points\n                    if (!withlines) {\n                        for (m = 0; m < ps; ++m)\n                            newpoints.push(points[i + m]);\n                    }\n                    i += ps;\n                }\n                else if (otherpoints[j] == null) {\n                    // oops, got a gap\n                    for (m = 0; m < ps; ++m)\n                        newpoints.push(null);\n                    fromgap = true;\n                    j += otherps;\n                }\n                else {\n                    // cases where we actually got two points\n                    px = points[i + keyOffset];\n                    py = points[i + accumulateOffset];\n                    qx = otherpoints[j + keyOffset];\n                    qy = otherpoints[j + accumulateOffset];\n                    bottom = 0;\n\n                    if (px == qx) {\n                        for (m = 0; m < ps; ++m)\n                            newpoints.push(points[i + m]);\n\n                        newpoints[l + accumulateOffset] += qy;\n                        bottom = qy;\n                        \n                        i += ps;\n                        j += otherps;\n                    }\n                    else if (px > qx) {\n                        // we got past point below, might need to\n                        // insert interpolated extra point\n                        if (withlines && i > 0 && points[i - ps] != null) {\n                            intery = py + (points[i - ps + accumulateOffset] - py) * (qx - px) / (points[i - ps + keyOffset] - px);\n                            newpoints.push(qx);\n                            newpoints.push(intery + qy);\n                            for (m = 2; m < ps; ++m)\n                                newpoints.push(points[i + m]);\n                            bottom = qy; \n                        }\n\n                        j += otherps;\n                    }\n                    else { // px < qx\n                        if (fromgap && withlines) {\n                            // if we come from a gap, we just skip this point\n                            i += ps;\n                            continue;\n                        }\n                            \n                        for (m = 0; m < ps; ++m)\n                            newpoints.push(points[i + m]);\n                        \n                        // we might be able to interpolate a point below,\n                        // this can give us a better y\n                        if (withlines && j > 0 && otherpoints[j - otherps] != null)\n                            bottom = qy + (otherpoints[j - otherps + accumulateOffset] - qy) * (px - qx) / (otherpoints[j - otherps + keyOffset] - qx);\n\n                        newpoints[l + accumulateOffset] += bottom;\n                        \n                        i += ps;\n                    }\n\n                    fromgap = false;\n                    \n                    if (l != newpoints.length && withbottom)\n                        newpoints[l + 2] += bottom;\n                }\n\n                // maintain the line steps invariant\n                if (withsteps && l != newpoints.length && l > 0\n                    && newpoints[l] != null\n                    && newpoints[l] != newpoints[l - ps]\n                    && newpoints[l + 1] != newpoints[l - ps + 1]) {\n                    for (m = 0; m < ps; ++m)\n                        newpoints[l + ps + m] = newpoints[l + m];\n                    newpoints[l + 1] = newpoints[l - ps + 1];\n                }\n            }\n\n            datapoints.points = newpoints;\n        }\n        \n        plot.hooks.processDatapoints.push(stackData);\n    }\n    \n    $.plot.plugins.push({\n        init: init,\n        options: options,\n        name: 'stack',\n        version: '1.2'\n    });\n})(jQuery);\n"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/js/flot/jquery.flot.symbol.js",
    "content": "/*\nFlot plugin that adds some extra symbols for plotting points.\n\nThe symbols are accessed as strings through the standard symbol\nchoice:\n\n  series: {\n      points: {\n          symbol: \"square\" // or \"diamond\", \"triangle\", \"cross\"\n      }\n  }\n\n*/\n\n(function ($) {\n    function processRawData(plot, series, datapoints) {\n        // we normalize the area of each symbol so it is approximately the\n        // same as a circle of the given radius\n\n        var handlers = {\n            square: function (ctx, x, y, radius, shadow) {\n                // pi * r^2 = (2s)^2  =>  s = r * sqrt(pi)/2\n                var size = radius * Math.sqrt(Math.PI) / 2;\n                ctx.rect(x - size, y - size, size + size, size + size);\n            },\n            diamond: function (ctx, x, y, radius, shadow) {\n                // pi * r^2 = 2s^2  =>  s = r * sqrt(pi/2)\n                var size = radius * Math.sqrt(Math.PI / 2);\n                ctx.moveTo(x - size, y);\n                ctx.lineTo(x, y - size);\n                ctx.lineTo(x + size, y);\n                ctx.lineTo(x, y + size);\n                ctx.lineTo(x - size, y);\n            },\n            triangle: function (ctx, x, y, radius, shadow) {\n                // pi * r^2 = 1/2 * s^2 * sin (pi / 3)  =>  s = r * sqrt(2 * pi / sin(pi / 3))\n                var size = radius * Math.sqrt(2 * Math.PI / Math.sin(Math.PI / 3));\n                var height = size * Math.sin(Math.PI / 3);\n                ctx.moveTo(x - size/2, y + height/2);\n                ctx.lineTo(x + size/2, y + height/2);\n                if (!shadow) {\n                    ctx.lineTo(x, y - height/2);\n                    ctx.lineTo(x - size/2, y + height/2);\n                }\n            },\n            cross: function (ctx, x, y, radius, shadow) {\n                // pi * r^2 = (2s)^2  =>  s = r * sqrt(pi)/2\n                var size = radius * Math.sqrt(Math.PI) / 2;\n                ctx.moveTo(x - size, y - size);\n                ctx.lineTo(x + size, y + size);\n                ctx.moveTo(x - size, y + size);\n                ctx.lineTo(x + size, y - size);\n            }\n        }\n\n        var s = series.points.symbol;\n        if (handlers[s])\n            series.points.symbol = handlers[s];\n    }\n    \n    function init(plot) {\n        plot.hooks.processDatapoints.push(processRawData);\n    }\n    \n    $.plot.plugins.push({\n        init: init,\n        name: 'symbols',\n        version: '1.0'\n    });\n})(jQuery);\n"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/js/flot/jquery.flot.threshold.js",
    "content": "/*\nFlot plugin for thresholding data. Controlled through the option\n\"threshold\" in either the global series options\n\n  series: {\n    threshold: {\n      below: number\n      color: colorspec\n    }\n  }\n\nor in a specific series\n\n  $.plot($(\"#placeholder\"), [{ data: [ ... ], threshold: { ... }}])\n\nThe data points below \"below\" are drawn with the specified color. This\nmakes it easy to mark points below 0, e.g. for budget data.\n\nInternally, the plugin works by splitting the data into two series,\nabove and below the threshold. The extra series below the threshold\nwill have its label cleared and the special \"originSeries\" attribute\nset to the original series. You may need to check for this in hover\nevents.\n*/\n\n(function ($) {\n    var options = {\n        series: { threshold: null } // or { below: number, color: color spec}\n    };\n    \n    function init(plot) {\n        function thresholdData(plot, s, datapoints) {\n            if (!s.threshold)\n                return;\n            \n            var ps = datapoints.pointsize, i, x, y, p, prevp,\n                thresholded = $.extend({}, s); // note: shallow copy\n\n            thresholded.datapoints = { points: [], pointsize: ps };\n            thresholded.label = null;\n            thresholded.color = s.threshold.color;\n            thresholded.threshold = null;\n            thresholded.originSeries = s;\n            thresholded.data = [];\n\n            var below = s.threshold.below,\n                origpoints = datapoints.points,\n                addCrossingPoints = s.lines.show;\n\n            threspoints = [];\n            newpoints = [];\n\n            for (i = 0; i < origpoints.length; i += ps) {\n                x = origpoints[i]\n                y = origpoints[i + 1];\n\n                prevp = p;\n                if (y < below)\n                    p = threspoints;\n                else\n                    p = newpoints;\n\n                if (addCrossingPoints && prevp != p && x != null\n                    && i > 0 && origpoints[i - ps] != null) {\n                    var interx = (x - origpoints[i - ps]) / (y - origpoints[i - ps + 1]) * (below - y) + x;\n                    prevp.push(interx);\n                    prevp.push(below);\n                    for (m = 2; m < ps; ++m)\n                        prevp.push(origpoints[i + m]);\n                    \n                    p.push(null); // start new segment\n                    p.push(null);\n                    for (m = 2; m < ps; ++m)\n                        p.push(origpoints[i + m]);\n                    p.push(interx);\n                    p.push(below);\n                    for (m = 2; m < ps; ++m)\n                        p.push(origpoints[i + m]);\n                }\n\n                p.push(x);\n                p.push(y);\n            }\n\n            datapoints.points = newpoints;\n            thresholded.datapoints.points = threspoints;\n            \n            if (thresholded.datapoints.points.length > 0)\n                plot.getData().push(thresholded);\n                \n            // FIXME: there are probably some edge cases left in bars\n        }\n        \n        plot.hooks.processDatapoints.push(thresholdData);\n    }\n    \n    $.plot.plugins.push({\n        init: init,\n        options: options,\n        name: 'threshold',\n        version: '1.0'\n    });\n})(jQuery);\n"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/js/flot/jquery.js",
    "content": "/*!\n * jQuery JavaScript Library v1.5.1\n * http://jquery.com/\n *\n * Copyright 2011, John Resig\n * Dual licensed under the MIT or GPL Version 2 licenses.\n * http://jquery.org/license\n *\n * Includes Sizzle.js\n * http://sizzlejs.com/\n * Copyright 2011, The Dojo Foundation\n * Released under the MIT, BSD, and GPL Licenses.\n *\n * Date: Wed Feb 23 13:55:29 2011 -0500\n */\n(function( window, undefined ) {\n\n// Use the correct document accordingly with window argument (sandbox)\nvar document = window.document;\nvar jQuery = (function() {\n\n// Define a local copy of jQuery\nvar jQuery = function( selector, context ) {\n\t\t// The jQuery object is actually just the init constructor 'enhanced'\n\t\treturn new jQuery.fn.init( selector, context, rootjQuery );\n\t},\n\n\t// Map over jQuery in case of overwrite\n\t_jQuery = window.jQuery,\n\n\t// Map over the $ in case of overwrite\n\t_$ = window.$,\n\n\t// A central reference to the root jQuery(document)\n\trootjQuery,\n\n\t// A simple way to check for HTML strings or ID strings\n\t// (both of which we optimize for)\n\tquickExpr = /^(?:[^<]*(<[\\w\\W]+>)[^>]*$|#([\\w\\-]+)$)/,\n\n\t// Check if a string has a non-whitespace character in it\n\trnotwhite = /\\S/,\n\n\t// Used for trimming whitespace\n\ttrimLeft = /^\\s+/,\n\ttrimRight = /\\s+$/,\n\n\t// Check for digits\n\trdigit = /\\d/,\n\n\t// Match a standalone tag\n\trsingleTag = /^<(\\w+)\\s*\\/?>(?:<\\/\\1>)?$/,\n\n\t// JSON RegExp\n\trvalidchars = /^[\\],:{}\\s]*$/,\n\trvalidescape = /\\\\(?:[\"\\\\\\/bfnrt]|u[0-9a-fA-F]{4})/g,\n\trvalidtokens = /\"[^\"\\\\\\n\\r]*\"|true|false|null|-?\\d+(?:\\.\\d*)?(?:[eE][+\\-]?\\d+)?/g,\n\trvalidbraces = /(?:^|:|,)(?:\\s*\\[)+/g,\n\n\t// Useragent RegExp\n\trwebkit = /(webkit)[ \\/]([\\w.]+)/,\n\tropera = /(opera)(?:.*version)?[ \\/]([\\w.]+)/,\n\trmsie = /(msie) ([\\w.]+)/,\n\trmozilla = /(mozilla)(?:.*? rv:([\\w.]+))?/,\n\n\t// Keep a UserAgent string for use with jQuery.browser\n\tuserAgent = navigator.userAgent,\n\n\t// For matching the engine and version of the browser\n\tbrowserMatch,\n\n\t// Has the ready events already been bound?\n\treadyBound = false,\n\n\t// The deferred used on DOM ready\n\treadyList,\n\n\t// Promise methods\n\tpromiseMethods = \"then done fail isResolved isRejected promise\".split( \" \" ),\n\n\t// The ready event handler\n\tDOMContentLoaded,\n\n\t// Save a reference to some core methods\n\ttoString = Object.prototype.toString,\n\thasOwn = Object.prototype.hasOwnProperty,\n\tpush = Array.prototype.push,\n\tslice = Array.prototype.slice,\n\ttrim = String.prototype.trim,\n\tindexOf = Array.prototype.indexOf,\n\n\t// [[Class]] -> type pairs\n\tclass2type = {};\n\njQuery.fn = jQuery.prototype = {\n\tconstructor: jQuery,\n\tinit: function( selector, context, rootjQuery ) {\n\t\tvar match, elem, ret, doc;\n\n\t\t// Handle $(\"\"), $(null), or $(undefined)\n\t\tif ( !selector ) {\n\t\t\treturn this;\n\t\t}\n\n\t\t// Handle $(DOMElement)\n\t\tif ( selector.nodeType ) {\n\t\t\tthis.context = this[0] = selector;\n\t\t\tthis.length = 1;\n\t\t\treturn this;\n\t\t}\n\n\t\t// The body element only exists once, optimize finding it\n\t\tif ( selector === \"body\" && !context && document.body ) {\n\t\t\tthis.context = document;\n\t\t\tthis[0] = document.body;\n\t\t\tthis.selector = \"body\";\n\t\t\tthis.length = 1;\n\t\t\treturn this;\n\t\t}\n\n\t\t// Handle HTML strings\n\t\tif ( typeof selector === \"string\" ) {\n\t\t\t// Are we dealing with HTML string or an ID?\n\t\t\tmatch = quickExpr.exec( selector );\n\n\t\t\t// Verify a match, and that no context was specified for #id\n\t\t\tif ( match && (match[1] || !context) ) {\n\n\t\t\t\t// HANDLE: $(html) -> $(array)\n\t\t\t\tif ( match[1] ) {\n\t\t\t\t\tcontext = context instanceof jQuery ? context[0] : context;\n\t\t\t\t\tdoc = (context ? context.ownerDocument || context : document);\n\n\t\t\t\t\t// If a single string is passed in and it's a single tag\n\t\t\t\t\t// just do a createElement and skip the rest\n\t\t\t\t\tret = rsingleTag.exec( selector );\n\n\t\t\t\t\tif ( ret ) {\n\t\t\t\t\t\tif ( jQuery.isPlainObject( context ) ) {\n\t\t\t\t\t\t\tselector = [ document.createElement( ret[1] ) ];\n\t\t\t\t\t\t\tjQuery.fn.attr.call( selector, context, true );\n\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tselector = [ doc.createElement( ret[1] ) ];\n\t\t\t\t\t\t}\n\n\t\t\t\t\t} else {\n\t\t\t\t\t\tret = jQuery.buildFragment( [ match[1] ], [ doc ] );\n\t\t\t\t\t\tselector = (ret.cacheable ? jQuery.clone(ret.fragment) : ret.fragment).childNodes;\n\t\t\t\t\t}\n\n\t\t\t\t\treturn jQuery.merge( this, selector );\n\n\t\t\t\t// HANDLE: $(\"#id\")\n\t\t\t\t} else {\n\t\t\t\t\telem = document.getElementById( match[2] );\n\n\t\t\t\t\t// Check parentNode to catch when Blackberry 4.6 returns\n\t\t\t\t\t// nodes that are no longer in the document #6963\n\t\t\t\t\tif ( elem && elem.parentNode ) {\n\t\t\t\t\t\t// Handle the case where IE and Opera return items\n\t\t\t\t\t\t// by name instead of ID\n\t\t\t\t\t\tif ( elem.id !== match[2] ) {\n\t\t\t\t\t\t\treturn rootjQuery.find( selector );\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Otherwise, we inject the element directly into the jQuery object\n\t\t\t\t\t\tthis.length = 1;\n\t\t\t\t\t\tthis[0] = elem;\n\t\t\t\t\t}\n\n\t\t\t\t\tthis.context = document;\n\t\t\t\t\tthis.selector = selector;\n\t\t\t\t\treturn this;\n\t\t\t\t}\n\n\t\t\t// HANDLE: $(expr, $(...))\n\t\t\t} else if ( !context || context.jquery ) {\n\t\t\t\treturn (context || rootjQuery).find( selector );\n\n\t\t\t// HANDLE: $(expr, context)\n\t\t\t// (which is just equivalent to: $(context).find(expr)\n\t\t\t} else {\n\t\t\t\treturn this.constructor( context ).find( selector );\n\t\t\t}\n\n\t\t// HANDLE: $(function)\n\t\t// Shortcut for document ready\n\t\t} else if ( jQuery.isFunction( selector ) ) {\n\t\t\treturn rootjQuery.ready( selector );\n\t\t}\n\n\t\tif (selector.selector !== undefined) {\n\t\t\tthis.selector = selector.selector;\n\t\t\tthis.context = selector.context;\n\t\t}\n\n\t\treturn jQuery.makeArray( selector, this );\n\t},\n\n\t// Start with an empty selector\n\tselector: \"\",\n\n\t// The current version of jQuery being used\n\tjquery: \"1.5.1\",\n\n\t// The default length of a jQuery object is 0\n\tlength: 0,\n\n\t// The number of elements contained in the matched element set\n\tsize: function() {\n\t\treturn this.length;\n\t},\n\n\ttoArray: function() {\n\t\treturn slice.call( this, 0 );\n\t},\n\n\t// Get the Nth element in the matched element set OR\n\t// Get the whole matched element set as a clean array\n\tget: function( num ) {\n\t\treturn num == null ?\n\n\t\t\t// Return a 'clean' array\n\t\t\tthis.toArray() :\n\n\t\t\t// Return just the object\n\t\t\t( num < 0 ? this[ this.length + num ] : this[ num ] );\n\t},\n\n\t// Take an array of elements and push it onto the stack\n\t// (returning the new matched element set)\n\tpushStack: function( elems, name, selector ) {\n\t\t// Build a new jQuery matched element set\n\t\tvar ret = this.constructor();\n\n\t\tif ( jQuery.isArray( elems ) ) {\n\t\t\tpush.apply( ret, elems );\n\n\t\t} else {\n\t\t\tjQuery.merge( ret, elems );\n\t\t}\n\n\t\t// Add the old object onto the stack (as a reference)\n\t\tret.prevObject = this;\n\n\t\tret.context = this.context;\n\n\t\tif ( name === \"find\" ) {\n\t\t\tret.selector = this.selector + (this.selector ? \" \" : \"\") + selector;\n\t\t} else if ( name ) {\n\t\t\tret.selector = this.selector + \".\" + name + \"(\" + selector + \")\";\n\t\t}\n\n\t\t// Return the newly-formed element set\n\t\treturn ret;\n\t},\n\n\t// Execute a callback for every element in the matched set.\n\t// (You can seed the arguments with an array of args, but this is\n\t// only used internally.)\n\teach: function( callback, args ) {\n\t\treturn jQuery.each( this, callback, args );\n\t},\n\n\tready: function( fn ) {\n\t\t// Attach the listeners\n\t\tjQuery.bindReady();\n\n\t\t// Add the callback\n\t\treadyList.done( fn );\n\n\t\treturn this;\n\t},\n\n\teq: function( i ) {\n\t\treturn i === -1 ?\n\t\t\tthis.slice( i ) :\n\t\t\tthis.slice( i, +i + 1 );\n\t},\n\n\tfirst: function() {\n\t\treturn this.eq( 0 );\n\t},\n\n\tlast: function() {\n\t\treturn this.eq( -1 );\n\t},\n\n\tslice: function() {\n\t\treturn this.pushStack( slice.apply( this, arguments ),\n\t\t\t\"slice\", slice.call(arguments).join(\",\") );\n\t},\n\n\tmap: function( callback ) {\n\t\treturn this.pushStack( jQuery.map(this, function( elem, i ) {\n\t\t\treturn callback.call( elem, i, elem );\n\t\t}));\n\t},\n\n\tend: function() {\n\t\treturn this.prevObject || this.constructor(null);\n\t},\n\n\t// For internal use only.\n\t// Behaves like an Array's method, not like a jQuery method.\n\tpush: push,\n\tsort: [].sort,\n\tsplice: [].splice\n};\n\n// Give the init function the jQuery prototype for later instantiation\njQuery.fn.init.prototype = jQuery.fn;\n\njQuery.extend = jQuery.fn.extend = function() {\n\tvar options, name, src, copy, copyIsArray, clone,\n\t\ttarget = arguments[0] || {},\n\t\ti = 1,\n\t\tlength = arguments.length,\n\t\tdeep = false;\n\n\t// Handle a deep copy situation\n\tif ( typeof target === \"boolean\" ) {\n\t\tdeep = target;\n\t\ttarget = arguments[1] || {};\n\t\t// skip the boolean and the target\n\t\ti = 2;\n\t}\n\n\t// Handle case when target is a string or something (possible in deep copy)\n\tif ( typeof target !== \"object\" && !jQuery.isFunction(target) ) {\n\t\ttarget = {};\n\t}\n\n\t// extend jQuery itself if only one argument is passed\n\tif ( length === i ) {\n\t\ttarget = this;\n\t\t--i;\n\t}\n\n\tfor ( ; i < length; i++ ) {\n\t\t// Only deal with non-null/undefined values\n\t\tif ( (options = arguments[ i ]) != null ) {\n\t\t\t// Extend the base object\n\t\t\tfor ( name in options ) {\n\t\t\t\tsrc = target[ name ];\n\t\t\t\tcopy = options[ name ];\n\n\t\t\t\t// Prevent never-ending loop\n\t\t\t\tif ( target === copy ) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// Recurse if we're merging plain objects or arrays\n\t\t\t\tif ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {\n\t\t\t\t\tif ( copyIsArray ) {\n\t\t\t\t\t\tcopyIsArray = false;\n\t\t\t\t\t\tclone = src && jQuery.isArray(src) ? src : [];\n\n\t\t\t\t\t} else {\n\t\t\t\t\t\tclone = src && jQuery.isPlainObject(src) ? src : {};\n\t\t\t\t\t}\n\n\t\t\t\t\t// Never move original objects, clone them\n\t\t\t\t\ttarget[ name ] = jQuery.extend( deep, clone, copy );\n\n\t\t\t\t// Don't bring in undefined values\n\t\t\t\t} else if ( copy !== undefined ) {\n\t\t\t\t\ttarget[ name ] = copy;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// Return the modified object\n\treturn target;\n};\n\njQuery.extend({\n\tnoConflict: function( deep ) {\n\t\twindow.$ = _$;\n\n\t\tif ( deep ) {\n\t\t\twindow.jQuery = _jQuery;\n\t\t}\n\n\t\treturn jQuery;\n\t},\n\n\t// Is the DOM ready to be used? Set to true once it occurs.\n\tisReady: false,\n\n\t// A counter to track how many items to wait for before\n\t// the ready event fires. See #6781\n\treadyWait: 1,\n\n\t// Handle when the DOM is ready\n\tready: function( wait ) {\n\t\t// A third-party is pushing the ready event forwards\n\t\tif ( wait === true ) {\n\t\t\tjQuery.readyWait--;\n\t\t}\n\n\t\t// Make sure that the DOM is not already loaded\n\t\tif ( !jQuery.readyWait || (wait !== true && !jQuery.isReady) ) {\n\t\t\t// Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).\n\t\t\tif ( !document.body ) {\n\t\t\t\treturn setTimeout( jQuery.ready, 1 );\n\t\t\t}\n\n\t\t\t// Remember that the DOM is ready\n\t\t\tjQuery.isReady = true;\n\n\t\t\t// If a normal DOM Ready event fired, decrement, and wait if need be\n\t\t\tif ( wait !== true && --jQuery.readyWait > 0 ) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// If there are functions bound, to execute\n\t\t\treadyList.resolveWith( document, [ jQuery ] );\n\n\t\t\t// Trigger any bound ready events\n\t\t\tif ( jQuery.fn.trigger ) {\n\t\t\t\tjQuery( document ).trigger( \"ready\" ).unbind( \"ready\" );\n\t\t\t}\n\t\t}\n\t},\n\n\tbindReady: function() {\n\t\tif ( readyBound ) {\n\t\t\treturn;\n\t\t}\n\n\t\treadyBound = true;\n\n\t\t// Catch cases where $(document).ready() is called after the\n\t\t// browser event has already occurred.\n\t\tif ( document.readyState === \"complete\" ) {\n\t\t\t// Handle it asynchronously to allow scripts the opportunity to delay ready\n\t\t\treturn setTimeout( jQuery.ready, 1 );\n\t\t}\n\n\t\t// Mozilla, Opera and webkit nightlies currently support this event\n\t\tif ( document.addEventListener ) {\n\t\t\t// Use the handy event callback\n\t\t\tdocument.addEventListener( \"DOMContentLoaded\", DOMContentLoaded, false );\n\n\t\t\t// A fallback to window.onload, that will always work\n\t\t\twindow.addEventListener( \"load\", jQuery.ready, false );\n\n\t\t// If IE event model is used\n\t\t} else if ( document.attachEvent ) {\n\t\t\t// ensure firing before onload,\n\t\t\t// maybe late but safe also for iframes\n\t\t\tdocument.attachEvent(\"onreadystatechange\", DOMContentLoaded);\n\n\t\t\t// A fallback to window.onload, that will always work\n\t\t\twindow.attachEvent( \"onload\", jQuery.ready );\n\n\t\t\t// If IE and not a frame\n\t\t\t// continually check to see if the document is ready\n\t\t\tvar toplevel = false;\n\n\t\t\ttry {\n\t\t\t\ttoplevel = window.frameElement == null;\n\t\t\t} catch(e) {}\n\n\t\t\tif ( document.documentElement.doScroll && toplevel ) {\n\t\t\t\tdoScrollCheck();\n\t\t\t}\n\t\t}\n\t},\n\n\t// See test/unit/core.js for details concerning isFunction.\n\t// Since version 1.3, DOM methods and functions like alert\n\t// aren't supported. They return false on IE (#2968).\n\tisFunction: function( obj ) {\n\t\treturn jQuery.type(obj) === \"function\";\n\t},\n\n\tisArray: Array.isArray || function( obj ) {\n\t\treturn jQuery.type(obj) === \"array\";\n\t},\n\n\t// A crude way of determining if an object is a window\n\tisWindow: function( obj ) {\n\t\treturn obj && typeof obj === \"object\" && \"setInterval\" in obj;\n\t},\n\n\tisNaN: function( obj ) {\n\t\treturn obj == null || !rdigit.test( obj ) || isNaN( obj );\n\t},\n\n\ttype: function( obj ) {\n\t\treturn obj == null ?\n\t\t\tString( obj ) :\n\t\t\tclass2type[ toString.call(obj) ] || \"object\";\n\t},\n\n\tisPlainObject: function( obj ) {\n\t\t// Must be an Object.\n\t\t// Because of IE, we also have to check the presence of the constructor property.\n\t\t// Make sure that DOM nodes and window objects don't pass through, as well\n\t\tif ( !obj || jQuery.type(obj) !== \"object\" || obj.nodeType || jQuery.isWindow( obj ) ) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Not own constructor property must be Object\n\t\tif ( obj.constructor &&\n\t\t\t!hasOwn.call(obj, \"constructor\") &&\n\t\t\t!hasOwn.call(obj.constructor.prototype, \"isPrototypeOf\") ) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Own properties are enumerated firstly, so to speed up,\n\t\t// if last one is own, then all properties are own.\n\n\t\tvar key;\n\t\tfor ( key in obj ) {}\n\n\t\treturn key === undefined || hasOwn.call( obj, key );\n\t},\n\n\tisEmptyObject: function( obj ) {\n\t\tfor ( var name in obj ) {\n\t\t\treturn false;\n\t\t}\n\t\treturn true;\n\t},\n\n\terror: function( msg ) {\n\t\tthrow msg;\n\t},\n\n\tparseJSON: function( data ) {\n\t\tif ( typeof data !== \"string\" || !data ) {\n\t\t\treturn null;\n\t\t}\n\n\t\t// Make sure leading/trailing whitespace is removed (IE can't handle it)\n\t\tdata = jQuery.trim( data );\n\n\t\t// Make sure the incoming data is actual JSON\n\t\t// Logic borrowed from http://json.org/json2.js\n\t\tif ( rvalidchars.test(data.replace(rvalidescape, \"@\")\n\t\t\t.replace(rvalidtokens, \"]\")\n\t\t\t.replace(rvalidbraces, \"\")) ) {\n\n\t\t\t// Try to use the native JSON parser first\n\t\t\treturn window.JSON && window.JSON.parse ?\n\t\t\t\twindow.JSON.parse( data ) :\n\t\t\t\t(new Function(\"return \" + data))();\n\n\t\t} else {\n\t\t\tjQuery.error( \"Invalid JSON: \" + data );\n\t\t}\n\t},\n\n\t// Cross-browser xml parsing\n\t// (xml & tmp used internally)\n\tparseXML: function( data , xml , tmp ) {\n\n\t\tif ( window.DOMParser ) { // Standard\n\t\t\ttmp = new DOMParser();\n\t\t\txml = tmp.parseFromString( data , \"text/xml\" );\n\t\t} else { // IE\n\t\t\txml = new ActiveXObject( \"Microsoft.XMLDOM\" );\n\t\t\txml.async = \"false\";\n\t\t\txml.loadXML( data );\n\t\t}\n\n\t\ttmp = xml.documentElement;\n\n\t\tif ( ! tmp || ! tmp.nodeName || tmp.nodeName === \"parsererror\" ) {\n\t\t\tjQuery.error( \"Invalid XML: \" + data );\n\t\t}\n\n\t\treturn xml;\n\t},\n\n\tnoop: function() {},\n\n\t// Evalulates a script in a global context\n\tglobalEval: function( data ) {\n\t\tif ( data && rnotwhite.test(data) ) {\n\t\t\t// Inspired by code by Andrea Giammarchi\n\t\t\t// http://webreflection.blogspot.com/2007/08/global-scope-evaluation-and-dom.html\n\t\t\tvar head = document.head || document.getElementsByTagName( \"head\" )[0] || document.documentElement,\n\t\t\t\tscript = document.createElement( \"script\" );\n\n\t\t\tif ( jQuery.support.scriptEval() ) {\n\t\t\t\tscript.appendChild( document.createTextNode( data ) );\n\t\t\t} else {\n\t\t\t\tscript.text = data;\n\t\t\t}\n\n\t\t\t// Use insertBefore instead of appendChild to circumvent an IE6 bug.\n\t\t\t// This arises when a base node is used (#2709).\n\t\t\thead.insertBefore( script, head.firstChild );\n\t\t\thead.removeChild( script );\n\t\t}\n\t},\n\n\tnodeName: function( elem, name ) {\n\t\treturn elem.nodeName && elem.nodeName.toUpperCase() === name.toUpperCase();\n\t},\n\n\t// args is for internal usage only\n\teach: function( object, callback, args ) {\n\t\tvar name, i = 0,\n\t\t\tlength = object.length,\n\t\t\tisObj = length === undefined || jQuery.isFunction(object);\n\n\t\tif ( args ) {\n\t\t\tif ( isObj ) {\n\t\t\t\tfor ( name in object ) {\n\t\t\t\t\tif ( callback.apply( object[ name ], args ) === false ) {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tfor ( ; i < length; ) {\n\t\t\t\t\tif ( callback.apply( object[ i++ ], args ) === false ) {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t// A special, fast, case for the most common use of each\n\t\t} else {\n\t\t\tif ( isObj ) {\n\t\t\t\tfor ( name in object ) {\n\t\t\t\t\tif ( callback.call( object[ name ], name, object[ name ] ) === false ) {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tfor ( var value = object[0];\n\t\t\t\t\ti < length && callback.call( value, i, value ) !== false; value = object[++i] ) {}\n\t\t\t}\n\t\t}\n\n\t\treturn object;\n\t},\n\n\t// Use native String.trim function wherever possible\n\ttrim: trim ?\n\t\tfunction( text ) {\n\t\t\treturn text == null ?\n\t\t\t\t\"\" :\n\t\t\t\ttrim.call( text );\n\t\t} :\n\n\t\t// Otherwise use our own trimming functionality\n\t\tfunction( text ) {\n\t\t\treturn text == null ?\n\t\t\t\t\"\" :\n\t\t\t\ttext.toString().replace( trimLeft, \"\" ).replace( trimRight, \"\" );\n\t\t},\n\n\t// results is for internal usage only\n\tmakeArray: function( array, results ) {\n\t\tvar ret = results || [];\n\n\t\tif ( array != null ) {\n\t\t\t// The window, strings (and functions) also have 'length'\n\t\t\t// The extra typeof function check is to prevent crashes\n\t\t\t// in Safari 2 (See: #3039)\n\t\t\t// Tweaked logic slightly to handle Blackberry 4.7 RegExp issues #6930\n\t\t\tvar type = jQuery.type(array);\n\n\t\t\tif ( array.length == null || type === \"string\" || type === \"function\" || type === \"regexp\" || jQuery.isWindow( array ) ) {\n\t\t\t\tpush.call( ret, array );\n\t\t\t} else {\n\t\t\t\tjQuery.merge( ret, array );\n\t\t\t}\n\t\t}\n\n\t\treturn ret;\n\t},\n\n\tinArray: function( elem, array ) {\n\t\tif ( array.indexOf ) {\n\t\t\treturn array.indexOf( elem );\n\t\t}\n\n\t\tfor ( var i = 0, length = array.length; i < length; i++ ) {\n\t\t\tif ( array[ i ] === elem ) {\n\t\t\t\treturn i;\n\t\t\t}\n\t\t}\n\n\t\treturn -1;\n\t},\n\n\tmerge: function( first, second ) {\n\t\tvar i = first.length,\n\t\t\tj = 0;\n\n\t\tif ( typeof second.length === \"number\" ) {\n\t\t\tfor ( var l = second.length; j < l; j++ ) {\n\t\t\t\tfirst[ i++ ] = second[ j ];\n\t\t\t}\n\n\t\t} else {\n\t\t\twhile ( second[j] !== undefined ) {\n\t\t\t\tfirst[ i++ ] = second[ j++ ];\n\t\t\t}\n\t\t}\n\n\t\tfirst.length = i;\n\n\t\treturn first;\n\t},\n\n\tgrep: function( elems, callback, inv ) {\n\t\tvar ret = [], retVal;\n\t\tinv = !!inv;\n\n\t\t// Go through the array, only saving the items\n\t\t// that pass the validator function\n\t\tfor ( var i = 0, length = elems.length; i < length; i++ ) {\n\t\t\tretVal = !!callback( elems[ i ], i );\n\t\t\tif ( inv !== retVal ) {\n\t\t\t\tret.push( elems[ i ] );\n\t\t\t}\n\t\t}\n\n\t\treturn ret;\n\t},\n\n\t// arg is for internal usage only\n\tmap: function( elems, callback, arg ) {\n\t\tvar ret = [], value;\n\n\t\t// Go through the array, translating each of the items to their\n\t\t// new value (or values).\n\t\tfor ( var i = 0, length = elems.length; i < length; i++ ) {\n\t\t\tvalue = callback( elems[ i ], i, arg );\n\n\t\t\tif ( value != null ) {\n\t\t\t\tret[ ret.length ] = value;\n\t\t\t}\n\t\t}\n\n\t\t// Flatten any nested arrays\n\t\treturn ret.concat.apply( [], ret );\n\t},\n\n\t// A global GUID counter for objects\n\tguid: 1,\n\n\tproxy: function( fn, proxy, thisObject ) {\n\t\tif ( arguments.length === 2 ) {\n\t\t\tif ( typeof proxy === \"string\" ) {\n\t\t\t\tthisObject = fn;\n\t\t\t\tfn = thisObject[ proxy ];\n\t\t\t\tproxy = undefined;\n\n\t\t\t} else if ( proxy && !jQuery.isFunction( proxy ) ) {\n\t\t\t\tthisObject = proxy;\n\t\t\t\tproxy = undefined;\n\t\t\t}\n\t\t}\n\n\t\tif ( !proxy && fn ) {\n\t\t\tproxy = function() {\n\t\t\t\treturn fn.apply( thisObject || this, arguments );\n\t\t\t};\n\t\t}\n\n\t\t// Set the guid of unique handler to the same of original handler, so it can be removed\n\t\tif ( fn ) {\n\t\t\tproxy.guid = fn.guid = fn.guid || proxy.guid || jQuery.guid++;\n\t\t}\n\n\t\t// So proxy can be declared as an argument\n\t\treturn proxy;\n\t},\n\n\t// Mutifunctional method to get and set values to a collection\n\t// The value/s can be optionally by executed if its a function\n\taccess: function( elems, key, value, exec, fn, pass ) {\n\t\tvar length = elems.length;\n\n\t\t// Setting many attributes\n\t\tif ( typeof key === \"object\" ) {\n\t\t\tfor ( var k in key ) {\n\t\t\t\tjQuery.access( elems, k, key[k], exec, fn, value );\n\t\t\t}\n\t\t\treturn elems;\n\t\t}\n\n\t\t// Setting one attribute\n\t\tif ( value !== undefined ) {\n\t\t\t// Optionally, function values get executed if exec is true\n\t\t\texec = !pass && exec && jQuery.isFunction(value);\n\n\t\t\tfor ( var i = 0; i < length; i++ ) {\n\t\t\t\tfn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value, pass );\n\t\t\t}\n\n\t\t\treturn elems;\n\t\t}\n\n\t\t// Getting an attribute\n\t\treturn length ? fn( elems[0], key ) : undefined;\n\t},\n\n\tnow: function() {\n\t\treturn (new Date()).getTime();\n\t},\n\n\t// Create a simple deferred (one callbacks list)\n\t_Deferred: function() {\n\t\tvar // callbacks list\n\t\t\tcallbacks = [],\n\t\t\t// stored [ context , args ]\n\t\t\tfired,\n\t\t\t// to avoid firing when already doing so\n\t\t\tfiring,\n\t\t\t// flag to know if the deferred has been cancelled\n\t\t\tcancelled,\n\t\t\t// the deferred itself\n\t\t\tdeferred  = {\n\n\t\t\t\t// done( f1, f2, ...)\n\t\t\t\tdone: function() {\n\t\t\t\t\tif ( !cancelled ) {\n\t\t\t\t\t\tvar args = arguments,\n\t\t\t\t\t\t\ti,\n\t\t\t\t\t\t\tlength,\n\t\t\t\t\t\t\telem,\n\t\t\t\t\t\t\ttype,\n\t\t\t\t\t\t\t_fired;\n\t\t\t\t\t\tif ( fired ) {\n\t\t\t\t\t\t\t_fired = fired;\n\t\t\t\t\t\t\tfired = 0;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tfor ( i = 0, length = args.length; i < length; i++ ) {\n\t\t\t\t\t\t\telem = args[ i ];\n\t\t\t\t\t\t\ttype = jQuery.type( elem );\n\t\t\t\t\t\t\tif ( type === \"array\" ) {\n\t\t\t\t\t\t\t\tdeferred.done.apply( deferred, elem );\n\t\t\t\t\t\t\t} else if ( type === \"function\" ) {\n\t\t\t\t\t\t\t\tcallbacks.push( elem );\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif ( _fired ) {\n\t\t\t\t\t\t\tdeferred.resolveWith( _fired[ 0 ], _fired[ 1 ] );\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\treturn this;\n\t\t\t\t},\n\n\t\t\t\t// resolve with given context and args\n\t\t\t\tresolveWith: function( context, args ) {\n\t\t\t\t\tif ( !cancelled && !fired && !firing ) {\n\t\t\t\t\t\tfiring = 1;\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\twhile( callbacks[ 0 ] ) {\n\t\t\t\t\t\t\t\tcallbacks.shift().apply( context, args );\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// We have to add a catch block for\n\t\t\t\t\t\t// IE prior to 8 or else the finally\n\t\t\t\t\t\t// block will never get executed\n\t\t\t\t\t\tcatch (e) {\n\t\t\t\t\t\t\tthrow e;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tfinally {\n\t\t\t\t\t\t\tfired = [ context, args ];\n\t\t\t\t\t\t\tfiring = 0;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\treturn this;\n\t\t\t\t},\n\n\t\t\t\t// resolve with this as context and given arguments\n\t\t\t\tresolve: function() {\n\t\t\t\t\tdeferred.resolveWith( jQuery.isFunction( this.promise ) ? this.promise() : this, arguments );\n\t\t\t\t\treturn this;\n\t\t\t\t},\n\n\t\t\t\t// Has this deferred been resolved?\n\t\t\t\tisResolved: function() {\n\t\t\t\t\treturn !!( firing || fired );\n\t\t\t\t},\n\n\t\t\t\t// Cancel\n\t\t\t\tcancel: function() {\n\t\t\t\t\tcancelled = 1;\n\t\t\t\t\tcallbacks = [];\n\t\t\t\t\treturn this;\n\t\t\t\t}\n\t\t\t};\n\n\t\treturn deferred;\n\t},\n\n\t// Full fledged deferred (two callbacks list)\n\tDeferred: function( func ) {\n\t\tvar deferred = jQuery._Deferred(),\n\t\t\tfailDeferred = jQuery._Deferred(),\n\t\t\tpromise;\n\t\t// Add errorDeferred methods, then and promise\n\t\tjQuery.extend( deferred, {\n\t\t\tthen: function( doneCallbacks, failCallbacks ) {\n\t\t\t\tdeferred.done( doneCallbacks ).fail( failCallbacks );\n\t\t\t\treturn this;\n\t\t\t},\n\t\t\tfail: failDeferred.done,\n\t\t\trejectWith: failDeferred.resolveWith,\n\t\t\treject: failDeferred.resolve,\n\t\t\tisRejected: failDeferred.isResolved,\n\t\t\t// Get a promise for this deferred\n\t\t\t// If obj is provided, the promise aspect is added to the object\n\t\t\tpromise: function( obj ) {\n\t\t\t\tif ( obj == null ) {\n\t\t\t\t\tif ( promise ) {\n\t\t\t\t\t\treturn promise;\n\t\t\t\t\t}\n\t\t\t\t\tpromise = obj = {};\n\t\t\t\t}\n\t\t\t\tvar i = promiseMethods.length;\n\t\t\t\twhile( i-- ) {\n\t\t\t\t\tobj[ promiseMethods[i] ] = deferred[ promiseMethods[i] ];\n\t\t\t\t}\n\t\t\t\treturn obj;\n\t\t\t}\n\t\t} );\n\t\t// Make sure only one callback list will be used\n\t\tdeferred.done( failDeferred.cancel ).fail( deferred.cancel );\n\t\t// Unexpose cancel\n\t\tdelete deferred.cancel;\n\t\t// Call given func if any\n\t\tif ( func ) {\n\t\t\tfunc.call( deferred, deferred );\n\t\t}\n\t\treturn deferred;\n\t},\n\n\t// Deferred helper\n\twhen: function( object ) {\n\t\tvar lastIndex = arguments.length,\n\t\t\tdeferred = lastIndex <= 1 && object && jQuery.isFunction( object.promise ) ?\n\t\t\t\tobject :\n\t\t\t\tjQuery.Deferred(),\n\t\t\tpromise = deferred.promise();\n\n\t\tif ( lastIndex > 1 ) {\n\t\t\tvar array = slice.call( arguments, 0 ),\n\t\t\t\tcount = lastIndex,\n\t\t\t\tiCallback = function( index ) {\n\t\t\t\t\treturn function( value ) {\n\t\t\t\t\t\tarray[ index ] = arguments.length > 1 ? slice.call( arguments, 0 ) : value;\n\t\t\t\t\t\tif ( !( --count ) ) {\n\t\t\t\t\t\t\tdeferred.resolveWith( promise, array );\n\t\t\t\t\t\t}\n\t\t\t\t\t};\n\t\t\t\t};\n\t\t\twhile( ( lastIndex-- ) ) {\n\t\t\t\tobject = array[ lastIndex ];\n\t\t\t\tif ( object && jQuery.isFunction( object.promise ) ) {\n\t\t\t\t\tobject.promise().then( iCallback(lastIndex), deferred.reject );\n\t\t\t\t} else {\n\t\t\t\t\t--count;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif ( !count ) {\n\t\t\t\tdeferred.resolveWith( promise, array );\n\t\t\t}\n\t\t} else if ( deferred !== object ) {\n\t\t\tdeferred.resolve( object );\n\t\t}\n\t\treturn promise;\n\t},\n\n\t// Use of jQuery.browser is frowned upon.\n\t// More details: http://docs.jquery.com/Utilities/jQuery.browser\n\tuaMatch: function( ua ) {\n\t\tua = ua.toLowerCase();\n\n\t\tvar match = rwebkit.exec( ua ) ||\n\t\t\tropera.exec( ua ) ||\n\t\t\trmsie.exec( ua ) ||\n\t\t\tua.indexOf(\"compatible\") < 0 && rmozilla.exec( ua ) ||\n\t\t\t[];\n\n\t\treturn { browser: match[1] || \"\", version: match[2] || \"0\" };\n\t},\n\n\tsub: function() {\n\t\tfunction jQuerySubclass( selector, context ) {\n\t\t\treturn new jQuerySubclass.fn.init( selector, context );\n\t\t}\n\t\tjQuery.extend( true, jQuerySubclass, this );\n\t\tjQuerySubclass.superclass = this;\n\t\tjQuerySubclass.fn = jQuerySubclass.prototype = this();\n\t\tjQuerySubclass.fn.constructor = jQuerySubclass;\n\t\tjQuerySubclass.subclass = this.subclass;\n\t\tjQuerySubclass.fn.init = function init( selector, context ) {\n\t\t\tif ( context && context instanceof jQuery && !(context instanceof jQuerySubclass) ) {\n\t\t\t\tcontext = jQuerySubclass(context);\n\t\t\t}\n\n\t\t\treturn jQuery.fn.init.call( this, selector, context, rootjQuerySubclass );\n\t\t};\n\t\tjQuerySubclass.fn.init.prototype = jQuerySubclass.fn;\n\t\tvar rootjQuerySubclass = jQuerySubclass(document);\n\t\treturn jQuerySubclass;\n\t},\n\n\tbrowser: {}\n});\n\n// Create readyList deferred\nreadyList = jQuery._Deferred();\n\n// Populate the class2type map\njQuery.each(\"Boolean Number String Function Array Date RegExp Object\".split(\" \"), function(i, name) {\n\tclass2type[ \"[object \" + name + \"]\" ] = name.toLowerCase();\n});\n\nbrowserMatch = jQuery.uaMatch( userAgent );\nif ( browserMatch.browser ) {\n\tjQuery.browser[ browserMatch.browser ] = true;\n\tjQuery.browser.version = browserMatch.version;\n}\n\n// Deprecated, use jQuery.browser.webkit instead\nif ( jQuery.browser.webkit ) {\n\tjQuery.browser.safari = true;\n}\n\nif ( indexOf ) {\n\tjQuery.inArray = function( elem, array ) {\n\t\treturn indexOf.call( array, elem );\n\t};\n}\n\n// IE doesn't match non-breaking spaces with \\s\nif ( rnotwhite.test( \"\\xA0\" ) ) {\n\ttrimLeft = /^[\\s\\xA0]+/;\n\ttrimRight = /[\\s\\xA0]+$/;\n}\n\n// All jQuery objects should point back to these\nrootjQuery = jQuery(document);\n\n// Cleanup functions for the document ready method\nif ( document.addEventListener ) {\n\tDOMContentLoaded = function() {\n\t\tdocument.removeEventListener( \"DOMContentLoaded\", DOMContentLoaded, false );\n\t\tjQuery.ready();\n\t};\n\n} else if ( document.attachEvent ) {\n\tDOMContentLoaded = function() {\n\t\t// Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).\n\t\tif ( document.readyState === \"complete\" ) {\n\t\t\tdocument.detachEvent( \"onreadystatechange\", DOMContentLoaded );\n\t\t\tjQuery.ready();\n\t\t}\n\t};\n}\n\n// The DOM ready check for Internet Explorer\nfunction doScrollCheck() {\n\tif ( jQuery.isReady ) {\n\t\treturn;\n\t}\n\n\ttry {\n\t\t// If IE is used, use the trick by Diego Perini\n\t\t// http://javascript.nwbox.com/IEContentLoaded/\n\t\tdocument.documentElement.doScroll(\"left\");\n\t} catch(e) {\n\t\tsetTimeout( doScrollCheck, 1 );\n\t\treturn;\n\t}\n\n\t// and execute any waiting functions\n\tjQuery.ready();\n}\n\n// Expose jQuery to the global object\nreturn jQuery;\n\n})();\n\n\n(function() {\n\n\tjQuery.support = {};\n\n\tvar div = document.createElement(\"div\");\n\n\tdiv.style.display = \"none\";\n\tdiv.innerHTML = \"   <link/><table></table><a href='/a' style='color:red;float:left;opacity:.55;'>a</a><input type='checkbox'/>\";\n\n\tvar all = div.getElementsByTagName(\"*\"),\n\t\ta = div.getElementsByTagName(\"a\")[0],\n\t\tselect = document.createElement(\"select\"),\n\t\topt = select.appendChild( document.createElement(\"option\") ),\n\t\tinput = div.getElementsByTagName(\"input\")[0];\n\n\t// Can't get basic test support\n\tif ( !all || !all.length || !a ) {\n\t\treturn;\n\t}\n\n\tjQuery.support = {\n\t\t// IE strips leading whitespace when .innerHTML is used\n\t\tleadingWhitespace: div.firstChild.nodeType === 3,\n\n\t\t// Make sure that tbody elements aren't automatically inserted\n\t\t// IE will insert them into empty tables\n\t\ttbody: !div.getElementsByTagName(\"tbody\").length,\n\n\t\t// Make sure that link elements get serialized correctly by innerHTML\n\t\t// This requires a wrapper element in IE\n\t\thtmlSerialize: !!div.getElementsByTagName(\"link\").length,\n\n\t\t// Get the style information from getAttribute\n\t\t// (IE uses .cssText insted)\n\t\tstyle: /red/.test( a.getAttribute(\"style\") ),\n\n\t\t// Make sure that URLs aren't manipulated\n\t\t// (IE normalizes it by default)\n\t\threfNormalized: a.getAttribute(\"href\") === \"/a\",\n\n\t\t// Make sure that element opacity exists\n\t\t// (IE uses filter instead)\n\t\t// Use a regex to work around a WebKit issue. See #5145\n\t\topacity: /^0.55$/.test( a.style.opacity ),\n\n\t\t// Verify style float existence\n\t\t// (IE uses styleFloat instead of cssFloat)\n\t\tcssFloat: !!a.style.cssFloat,\n\n\t\t// Make sure that if no value is specified for a checkbox\n\t\t// that it defaults to \"on\".\n\t\t// (WebKit defaults to \"\" instead)\n\t\tcheckOn: input.value === \"on\",\n\n\t\t// Make sure that a selected-by-default option has a working selected property.\n\t\t// (WebKit defaults to false instead of true, IE too, if it's in an optgroup)\n\t\toptSelected: opt.selected,\n\n\t\t// Will be defined later\n\t\tdeleteExpando: true,\n\t\toptDisabled: false,\n\t\tcheckClone: false,\n\t\tnoCloneEvent: true,\n\t\tnoCloneChecked: true,\n\t\tboxModel: null,\n\t\tinlineBlockNeedsLayout: false,\n\t\tshrinkWrapBlocks: false,\n\t\treliableHiddenOffsets: true\n\t};\n\n\tinput.checked = true;\n\tjQuery.support.noCloneChecked = input.cloneNode( true ).checked;\n\n\t// Make sure that the options inside disabled selects aren't marked as disabled\n\t// (WebKit marks them as diabled)\n\tselect.disabled = true;\n\tjQuery.support.optDisabled = !opt.disabled;\n\n\tvar _scriptEval = null;\n\tjQuery.support.scriptEval = function() {\n\t\tif ( _scriptEval === null ) {\n\t\t\tvar root = document.documentElement,\n\t\t\t\tscript = document.createElement(\"script\"),\n\t\t\t\tid = \"script\" + jQuery.now();\n\n\t\t\ttry {\n\t\t\t\tscript.appendChild( document.createTextNode( \"window.\" + id + \"=1;\" ) );\n\t\t\t} catch(e) {}\n\n\t\t\troot.insertBefore( script, root.firstChild );\n\n\t\t\t// Make sure that the execution of code works by injecting a script\n\t\t\t// tag with appendChild/createTextNode\n\t\t\t// (IE doesn't support this, fails, and uses .text instead)\n\t\t\tif ( window[ id ] ) {\n\t\t\t\t_scriptEval = true;\n\t\t\t\tdelete window[ id ];\n\t\t\t} else {\n\t\t\t\t_scriptEval = false;\n\t\t\t}\n\n\t\t\troot.removeChild( script );\n\t\t\t// release memory in IE\n\t\t\troot = script = id  = null;\n\t\t}\n\n\t\treturn _scriptEval;\n\t};\n\n\t// Test to see if it's possible to delete an expando from an element\n\t// Fails in Internet Explorer\n\ttry {\n\t\tdelete div.test;\n\n\t} catch(e) {\n\t\tjQuery.support.deleteExpando = false;\n\t}\n\n\tif ( !div.addEventListener && div.attachEvent && div.fireEvent ) {\n\t\tdiv.attachEvent(\"onclick\", function click() {\n\t\t\t// Cloning a node shouldn't copy over any\n\t\t\t// bound event handlers (IE does this)\n\t\t\tjQuery.support.noCloneEvent = false;\n\t\t\tdiv.detachEvent(\"onclick\", click);\n\t\t});\n\t\tdiv.cloneNode(true).fireEvent(\"onclick\");\n\t}\n\n\tdiv = document.createElement(\"div\");\n\tdiv.innerHTML = \"<input type='radio' name='radiotest' checked='checked'/>\";\n\n\tvar fragment = document.createDocumentFragment();\n\tfragment.appendChild( div.firstChild );\n\n\t// WebKit doesn't clone checked state correctly in fragments\n\tjQuery.support.checkClone = fragment.cloneNode(true).cloneNode(true).lastChild.checked;\n\n\t// Figure out if the W3C box model works as expected\n\t// document.body must exist before we can do this\n\tjQuery(function() {\n\t\tvar div = document.createElement(\"div\"),\n\t\t\tbody = document.getElementsByTagName(\"body\")[0];\n\n\t\t// Frameset documents with no body should not run this code\n\t\tif ( !body ) {\n\t\t\treturn;\n\t\t}\n\n\t\tdiv.style.width = div.style.paddingLeft = \"1px\";\n\t\tbody.appendChild( div );\n\t\tjQuery.boxModel = jQuery.support.boxModel = div.offsetWidth === 2;\n\n\t\tif ( \"zoom\" in div.style ) {\n\t\t\t// Check if natively block-level elements act like inline-block\n\t\t\t// elements when setting their display to 'inline' and giving\n\t\t\t// them layout\n\t\t\t// (IE < 8 does this)\n\t\t\tdiv.style.display = \"inline\";\n\t\t\tdiv.style.zoom = 1;\n\t\t\tjQuery.support.inlineBlockNeedsLayout = div.offsetWidth === 2;\n\n\t\t\t// Check if elements with layout shrink-wrap their children\n\t\t\t// (IE 6 does this)\n\t\t\tdiv.style.display = \"\";\n\t\t\tdiv.innerHTML = \"<div style='width:4px;'></div>\";\n\t\t\tjQuery.support.shrinkWrapBlocks = div.offsetWidth !== 2;\n\t\t}\n\n\t\tdiv.innerHTML = \"<table><tr><td style='padding:0;border:0;display:none'></td><td>t</td></tr></table>\";\n\t\tvar tds = div.getElementsByTagName(\"td\");\n\n\t\t// Check if table cells still have offsetWidth/Height when they are set\n\t\t// to display:none and there are still other visible table cells in a\n\t\t// table row; if so, offsetWidth/Height are not reliable for use when\n\t\t// determining if an element has been hidden directly using\n\t\t// display:none (it is still safe to use offsets if a parent element is\n\t\t// hidden; don safety goggles and see bug #4512 for more information).\n\t\t// (only IE 8 fails this test)\n\t\tjQuery.support.reliableHiddenOffsets = tds[0].offsetHeight === 0;\n\n\t\ttds[0].style.display = \"\";\n\t\ttds[1].style.display = \"none\";\n\n\t\t// Check if empty table cells still have offsetWidth/Height\n\t\t// (IE < 8 fail this test)\n\t\tjQuery.support.reliableHiddenOffsets = jQuery.support.reliableHiddenOffsets && tds[0].offsetHeight === 0;\n\t\tdiv.innerHTML = \"\";\n\n\t\tbody.removeChild( div ).style.display = \"none\";\n\t\tdiv = tds = null;\n\t});\n\n\t// Technique from Juriy Zaytsev\n\t// http://thinkweb2.com/projects/prototype/detecting-event-support-without-browser-sniffing/\n\tvar eventSupported = function( eventName ) {\n\t\tvar el = document.createElement(\"div\");\n\t\teventName = \"on\" + eventName;\n\n\t\t// We only care about the case where non-standard event systems\n\t\t// are used, namely in IE. Short-circuiting here helps us to\n\t\t// avoid an eval call (in setAttribute) which can cause CSP\n\t\t// to go haywire. See: https://developer.mozilla.org/en/Security/CSP\n\t\tif ( !el.attachEvent ) {\n\t\t\treturn true;\n\t\t}\n\n\t\tvar isSupported = (eventName in el);\n\t\tif ( !isSupported ) {\n\t\t\tel.setAttribute(eventName, \"return;\");\n\t\t\tisSupported = typeof el[eventName] === \"function\";\n\t\t}\n\t\tel = null;\n\n\t\treturn isSupported;\n\t};\n\n\tjQuery.support.submitBubbles = eventSupported(\"submit\");\n\tjQuery.support.changeBubbles = eventSupported(\"change\");\n\n\t// release memory in IE\n\tdiv = all = a = null;\n})();\n\n\n\nvar rbrace = /^(?:\\{.*\\}|\\[.*\\])$/;\n\njQuery.extend({\n\tcache: {},\n\n\t// Please use with caution\n\tuuid: 0,\n\n\t// Unique for each copy of jQuery on the page\n\t// Non-digits removed to match rinlinejQuery\n\texpando: \"jQuery\" + ( jQuery.fn.jquery + Math.random() ).replace( /\\D/g, \"\" ),\n\n\t// The following elements throw uncatchable exceptions if you\n\t// attempt to add expando properties to them.\n\tnoData: {\n\t\t\"embed\": true,\n\t\t// Ban all objects except for Flash (which handle expandos)\n\t\t\"object\": \"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000\",\n\t\t\"applet\": true\n\t},\n\n\thasData: function( elem ) {\n\t\telem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ];\n\n\t\treturn !!elem && !isEmptyDataObject( elem );\n\t},\n\n\tdata: function( elem, name, data, pvt /* Internal Use Only */ ) {\n\t\tif ( !jQuery.acceptData( elem ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\tvar internalKey = jQuery.expando, getByName = typeof name === \"string\", thisCache,\n\n\t\t\t// We have to handle DOM nodes and JS objects differently because IE6-7\n\t\t\t// can't GC object references properly across the DOM-JS boundary\n\t\t\tisNode = elem.nodeType,\n\n\t\t\t// Only DOM nodes need the global jQuery cache; JS object data is\n\t\t\t// attached directly to the object so GC can occur automatically\n\t\t\tcache = isNode ? jQuery.cache : elem,\n\n\t\t\t// Only defining an ID for JS objects if its cache already exists allows\n\t\t\t// the code to shortcut on the same path as a DOM node with no cache\n\t\t\tid = isNode ? elem[ jQuery.expando ] : elem[ jQuery.expando ] && jQuery.expando;\n\n\t\t// Avoid doing any more work than we need to when trying to get data on an\n\t\t// object that has no data at all\n\t\tif ( (!id || (pvt && id && !cache[ id ][ internalKey ])) && getByName && data === undefined ) {\n\t\t\treturn;\n\t\t}\n\n\t\tif ( !id ) {\n\t\t\t// Only DOM nodes need a new unique ID for each element since their data\n\t\t\t// ends up in the global cache\n\t\t\tif ( isNode ) {\n\t\t\t\telem[ jQuery.expando ] = id = ++jQuery.uuid;\n\t\t\t} else {\n\t\t\t\tid = jQuery.expando;\n\t\t\t}\n\t\t}\n\n\t\tif ( !cache[ id ] ) {\n\t\t\tcache[ id ] = {};\n\n\t\t\t// TODO: This is a hack for 1.5 ONLY. Avoids exposing jQuery\n\t\t\t// metadata on plain JS objects when the object is serialized using\n\t\t\t// JSON.stringify\n\t\t\tif ( !isNode ) {\n\t\t\t\tcache[ id ].toJSON = jQuery.noop;\n\t\t\t}\n\t\t}\n\n\t\t// An object can be passed to jQuery.data instead of a key/value pair; this gets\n\t\t// shallow copied over onto the existing cache\n\t\tif ( typeof name === \"object\" || typeof name === \"function\" ) {\n\t\t\tif ( pvt ) {\n\t\t\t\tcache[ id ][ internalKey ] = jQuery.extend(cache[ id ][ internalKey ], name);\n\t\t\t} else {\n\t\t\t\tcache[ id ] = jQuery.extend(cache[ id ], name);\n\t\t\t}\n\t\t}\n\n\t\tthisCache = cache[ id ];\n\n\t\t// Internal jQuery data is stored in a separate object inside the object's data\n\t\t// cache in order to avoid key collisions between internal data and user-defined\n\t\t// data\n\t\tif ( pvt ) {\n\t\t\tif ( !thisCache[ internalKey ] ) {\n\t\t\t\tthisCache[ internalKey ] = {};\n\t\t\t}\n\n\t\t\tthisCache = thisCache[ internalKey ];\n\t\t}\n\n\t\tif ( data !== undefined ) {\n\t\t\tthisCache[ name ] = data;\n\t\t}\n\n\t\t// TODO: This is a hack for 1.5 ONLY. It will be removed in 1.6. Users should\n\t\t// not attempt to inspect the internal events object using jQuery.data, as this\n\t\t// internal data object is undocumented and subject to change.\n\t\tif ( name === \"events\" && !thisCache[name] ) {\n\t\t\treturn thisCache[ internalKey ] && thisCache[ internalKey ].events;\n\t\t}\n\n\t\treturn getByName ? thisCache[ name ] : thisCache;\n\t},\n\n\tremoveData: function( elem, name, pvt /* Internal Use Only */ ) {\n\t\tif ( !jQuery.acceptData( elem ) ) {\n\t\t\treturn;\n\t\t}\n\n\t\tvar internalKey = jQuery.expando, isNode = elem.nodeType,\n\n\t\t\t// See jQuery.data for more information\n\t\t\tcache = isNode ? jQuery.cache : elem,\n\n\t\t\t// See jQuery.data for more information\n\t\t\tid = isNode ? elem[ jQuery.expando ] : jQuery.expando;\n\n\t\t// If there is already no cache entry for this object, there is no\n\t\t// purpose in continuing\n\t\tif ( !cache[ id ] ) {\n\t\t\treturn;\n\t\t}\n\n\t\tif ( name ) {\n\t\t\tvar thisCache = pvt ? cache[ id ][ internalKey ] : cache[ id ];\n\n\t\t\tif ( thisCache ) {\n\t\t\t\tdelete thisCache[ name ];\n\n\t\t\t\t// If there is no data left in the cache, we want to continue\n\t\t\t\t// and let the cache object itself get destroyed\n\t\t\t\tif ( !isEmptyDataObject(thisCache) ) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// See jQuery.data for more information\n\t\tif ( pvt ) {\n\t\t\tdelete cache[ id ][ internalKey ];\n\n\t\t\t// Don't destroy the parent cache unless the internal data object\n\t\t\t// had been the only thing left in it\n\t\t\tif ( !isEmptyDataObject(cache[ id ]) ) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\tvar internalCache = cache[ id ][ internalKey ];\n\n\t\t// Browsers that fail expando deletion also refuse to delete expandos on\n\t\t// the window, but it will allow it on all other JS objects; other browsers\n\t\t// don't care\n\t\tif ( jQuery.support.deleteExpando || cache != window ) {\n\t\t\tdelete cache[ id ];\n\t\t} else {\n\t\t\tcache[ id ] = null;\n\t\t}\n\n\t\t// We destroyed the entire user cache at once because it's faster than\n\t\t// iterating through each key, but we need to continue to persist internal\n\t\t// data if it existed\n\t\tif ( internalCache ) {\n\t\t\tcache[ id ] = {};\n\t\t\t// TODO: This is a hack for 1.5 ONLY. Avoids exposing jQuery\n\t\t\t// metadata on plain JS objects when the object is serialized using\n\t\t\t// JSON.stringify\n\t\t\tif ( !isNode ) {\n\t\t\t\tcache[ id ].toJSON = jQuery.noop;\n\t\t\t}\n\n\t\t\tcache[ id ][ internalKey ] = internalCache;\n\n\t\t// Otherwise, we need to eliminate the expando on the node to avoid\n\t\t// false lookups in the cache for entries that no longer exist\n\t\t} else if ( isNode ) {\n\t\t\t// IE does not allow us to delete expando properties from nodes,\n\t\t\t// nor does it have a removeAttribute function on Document nodes;\n\t\t\t// we must handle all of these cases\n\t\t\tif ( jQuery.support.deleteExpando ) {\n\t\t\t\tdelete elem[ jQuery.expando ];\n\t\t\t} else if ( elem.removeAttribute ) {\n\t\t\t\telem.removeAttribute( jQuery.expando );\n\t\t\t} else {\n\t\t\t\telem[ jQuery.expando ] = null;\n\t\t\t}\n\t\t}\n\t},\n\n\t// For internal use only.\n\t_data: function( elem, name, data ) {\n\t\treturn jQuery.data( elem, name, data, true );\n\t},\n\n\t// A method for determining if a DOM node can handle the data expando\n\tacceptData: function( elem ) {\n\t\tif ( elem.nodeName ) {\n\t\t\tvar match = jQuery.noData[ elem.nodeName.toLowerCase() ];\n\n\t\t\tif ( match ) {\n\t\t\t\treturn !(match === true || elem.getAttribute(\"classid\") !== match);\n\t\t\t}\n\t\t}\n\n\t\treturn true;\n\t}\n});\n\njQuery.fn.extend({\n\tdata: function( key, value ) {\n\t\tvar data = null;\n\n\t\tif ( typeof key === \"undefined\" ) {\n\t\t\tif ( this.length ) {\n\t\t\t\tdata = jQuery.data( this[0] );\n\n\t\t\t\tif ( this[0].nodeType === 1 ) {\n\t\t\t\t\tvar attr = this[0].attributes, name;\n\t\t\t\t\tfor ( var i = 0, l = attr.length; i < l; i++ ) {\n\t\t\t\t\t\tname = attr[i].name;\n\n\t\t\t\t\t\tif ( name.indexOf( \"data-\" ) === 0 ) {\n\t\t\t\t\t\t\tname = name.substr( 5 );\n\t\t\t\t\t\t\tdataAttr( this[0], name, data[ name ] );\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn data;\n\n\t\t} else if ( typeof key === \"object\" ) {\n\t\t\treturn this.each(function() {\n\t\t\t\tjQuery.data( this, key );\n\t\t\t});\n\t\t}\n\n\t\tvar parts = key.split(\".\");\n\t\tparts[1] = parts[1] ? \".\" + parts[1] : \"\";\n\n\t\tif ( value === undefined ) {\n\t\t\tdata = this.triggerHandler(\"getData\" + parts[1] + \"!\", [parts[0]]);\n\n\t\t\t// Try to fetch any internally stored data first\n\t\t\tif ( data === undefined && this.length ) {\n\t\t\t\tdata = jQuery.data( this[0], key );\n\t\t\t\tdata = dataAttr( this[0], key, data );\n\t\t\t}\n\n\t\t\treturn data === undefined && parts[1] ?\n\t\t\t\tthis.data( parts[0] ) :\n\t\t\t\tdata;\n\n\t\t} else {\n\t\t\treturn this.each(function() {\n\t\t\t\tvar $this = jQuery( this ),\n\t\t\t\t\targs = [ parts[0], value ];\n\n\t\t\t\t$this.triggerHandler( \"setData\" + parts[1] + \"!\", args );\n\t\t\t\tjQuery.data( this, key, value );\n\t\t\t\t$this.triggerHandler( \"changeData\" + parts[1] + \"!\", args );\n\t\t\t});\n\t\t}\n\t},\n\n\tremoveData: function( key ) {\n\t\treturn this.each(function() {\n\t\t\tjQuery.removeData( this, key );\n\t\t});\n\t}\n});\n\nfunction dataAttr( elem, key, data ) {\n\t// If nothing was found internally, try to fetch any\n\t// data from the HTML5 data-* attribute\n\tif ( data === undefined && elem.nodeType === 1 ) {\n\t\tdata = elem.getAttribute( \"data-\" + key );\n\n\t\tif ( typeof data === \"string\" ) {\n\t\t\ttry {\n\t\t\t\tdata = data === \"true\" ? true :\n\t\t\t\tdata === \"false\" ? false :\n\t\t\t\tdata === \"null\" ? null :\n\t\t\t\t!jQuery.isNaN( data ) ? parseFloat( data ) :\n\t\t\t\t\trbrace.test( data ) ? jQuery.parseJSON( data ) :\n\t\t\t\t\tdata;\n\t\t\t} catch( e ) {}\n\n\t\t\t// Make sure we set the data so it isn't changed later\n\t\t\tjQuery.data( elem, key, data );\n\n\t\t} else {\n\t\t\tdata = undefined;\n\t\t}\n\t}\n\n\treturn data;\n}\n\n// TODO: This is a hack for 1.5 ONLY to allow objects with a single toJSON\n// property to be considered empty objects; this property always exists in\n// order to make sure JSON.stringify does not expose internal metadata\nfunction isEmptyDataObject( obj ) {\n\tfor ( var name in obj ) {\n\t\tif ( name !== \"toJSON\" ) {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\treturn true;\n}\n\n\n\n\njQuery.extend({\n\tqueue: function( elem, type, data ) {\n\t\tif ( !elem ) {\n\t\t\treturn;\n\t\t}\n\n\t\ttype = (type || \"fx\") + \"queue\";\n\t\tvar q = jQuery._data( elem, type );\n\n\t\t// Speed up dequeue by getting out quickly if this is just a lookup\n\t\tif ( !data ) {\n\t\t\treturn q || [];\n\t\t}\n\n\t\tif ( !q || jQuery.isArray(data) ) {\n\t\t\tq = jQuery._data( elem, type, jQuery.makeArray(data) );\n\n\t\t} else {\n\t\t\tq.push( data );\n\t\t}\n\n\t\treturn q;\n\t},\n\n\tdequeue: function( elem, type ) {\n\t\ttype = type || \"fx\";\n\n\t\tvar queue = jQuery.queue( elem, type ),\n\t\t\tfn = queue.shift();\n\n\t\t// If the fx queue is dequeued, always remove the progress sentinel\n\t\tif ( fn === \"inprogress\" ) {\n\t\t\tfn = queue.shift();\n\t\t}\n\n\t\tif ( fn ) {\n\t\t\t// Add a progress sentinel to prevent the fx queue from being\n\t\t\t// automatically dequeued\n\t\t\tif ( type === \"fx\" ) {\n\t\t\t\tqueue.unshift(\"inprogress\");\n\t\t\t}\n\n\t\t\tfn.call(elem, function() {\n\t\t\t\tjQuery.dequeue(elem, type);\n\t\t\t});\n\t\t}\n\n\t\tif ( !queue.length ) {\n\t\t\tjQuery.removeData( elem, type + \"queue\", true );\n\t\t}\n\t}\n});\n\njQuery.fn.extend({\n\tqueue: function( type, data ) {\n\t\tif ( typeof type !== \"string\" ) {\n\t\t\tdata = type;\n\t\t\ttype = \"fx\";\n\t\t}\n\n\t\tif ( data === undefined ) {\n\t\t\treturn jQuery.queue( this[0], type );\n\t\t}\n\t\treturn this.each(function( i ) {\n\t\t\tvar queue = jQuery.queue( this, type, data );\n\n\t\t\tif ( type === \"fx\" && queue[0] !== \"inprogress\" ) {\n\t\t\t\tjQuery.dequeue( this, type );\n\t\t\t}\n\t\t});\n\t},\n\tdequeue: function( type ) {\n\t\treturn this.each(function() {\n\t\t\tjQuery.dequeue( this, type );\n\t\t});\n\t},\n\n\t// Based off of the plugin by Clint Helfers, with permission.\n\t// http://blindsignals.com/index.php/2009/07/jquery-delay/\n\tdelay: function( time, type ) {\n\t\ttime = jQuery.fx ? jQuery.fx.speeds[time] || time : time;\n\t\ttype = type || \"fx\";\n\n\t\treturn this.queue( type, function() {\n\t\t\tvar elem = this;\n\t\t\tsetTimeout(function() {\n\t\t\t\tjQuery.dequeue( elem, type );\n\t\t\t}, time );\n\t\t});\n\t},\n\n\tclearQueue: function( type ) {\n\t\treturn this.queue( type || \"fx\", [] );\n\t}\n});\n\n\n\n\nvar rclass = /[\\n\\t\\r]/g,\n\trspaces = /\\s+/,\n\trreturn = /\\r/g,\n\trspecialurl = /^(?:href|src|style)$/,\n\trtype = /^(?:button|input)$/i,\n\trfocusable = /^(?:button|input|object|select|textarea)$/i,\n\trclickable = /^a(?:rea)?$/i,\n\trradiocheck = /^(?:radio|checkbox)$/i;\n\njQuery.props = {\n\t\"for\": \"htmlFor\",\n\t\"class\": \"className\",\n\treadonly: \"readOnly\",\n\tmaxlength: \"maxLength\",\n\tcellspacing: \"cellSpacing\",\n\trowspan: \"rowSpan\",\n\tcolspan: \"colSpan\",\n\ttabindex: \"tabIndex\",\n\tusemap: \"useMap\",\n\tframeborder: \"frameBorder\"\n};\n\njQuery.fn.extend({\n\tattr: function( name, value ) {\n\t\treturn jQuery.access( this, name, value, true, jQuery.attr );\n\t},\n\n\tremoveAttr: function( name, fn ) {\n\t\treturn this.each(function(){\n\t\t\tjQuery.attr( this, name, \"\" );\n\t\t\tif ( this.nodeType === 1 ) {\n\t\t\t\tthis.removeAttribute( name );\n\t\t\t}\n\t\t});\n\t},\n\n\taddClass: function( value ) {\n\t\tif ( jQuery.isFunction(value) ) {\n\t\t\treturn this.each(function(i) {\n\t\t\t\tvar self = jQuery(this);\n\t\t\t\tself.addClass( value.call(this, i, self.attr(\"class\")) );\n\t\t\t});\n\t\t}\n\n\t\tif ( value && typeof value === \"string\" ) {\n\t\t\tvar classNames = (value || \"\").split( rspaces );\n\n\t\t\tfor ( var i = 0, l = this.length; i < l; i++ ) {\n\t\t\t\tvar elem = this[i];\n\n\t\t\t\tif ( elem.nodeType === 1 ) {\n\t\t\t\t\tif ( !elem.className ) {\n\t\t\t\t\t\telem.className = value;\n\n\t\t\t\t\t} else {\n\t\t\t\t\t\tvar className = \" \" + elem.className + \" \",\n\t\t\t\t\t\t\tsetClass = elem.className;\n\n\t\t\t\t\t\tfor ( var c = 0, cl = classNames.length; c < cl; c++ ) {\n\t\t\t\t\t\t\tif ( className.indexOf( \" \" + classNames[c] + \" \" ) < 0 ) {\n\t\t\t\t\t\t\t\tsetClass += \" \" + classNames[c];\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\telem.className = jQuery.trim( setClass );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn this;\n\t},\n\n\tremoveClass: function( value ) {\n\t\tif ( jQuery.isFunction(value) ) {\n\t\t\treturn this.each(function(i) {\n\t\t\t\tvar self = jQuery(this);\n\t\t\t\tself.removeClass( value.call(this, i, self.attr(\"class\")) );\n\t\t\t});\n\t\t}\n\n\t\tif ( (value && typeof value === \"string\") || value === undefined ) {\n\t\t\tvar classNames = (value || \"\").split( rspaces );\n\n\t\t\tfor ( var i = 0, l = this.length; i < l; i++ ) {\n\t\t\t\tvar elem = this[i];\n\n\t\t\t\tif ( elem.nodeType === 1 && elem.className ) {\n\t\t\t\t\tif ( value ) {\n\t\t\t\t\t\tvar className = (\" \" + elem.className + \" \").replace(rclass, \" \");\n\t\t\t\t\t\tfor ( var c = 0, cl = classNames.length; c < cl; c++ ) {\n\t\t\t\t\t\t\tclassName = className.replace(\" \" + classNames[c] + \" \", \" \");\n\t\t\t\t\t\t}\n\t\t\t\t\t\telem.className = jQuery.trim( className );\n\n\t\t\t\t\t} else {\n\t\t\t\t\t\telem.className = \"\";\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn this;\n\t},\n\n\ttoggleClass: function( value, stateVal ) {\n\t\tvar type = typeof value,\n\t\t\tisBool = typeof stateVal === \"boolean\";\n\n\t\tif ( jQuery.isFunction( value ) ) {\n\t\t\treturn this.each(function(i) {\n\t\t\t\tvar self = jQuery(this);\n\t\t\t\tself.toggleClass( value.call(this, i, self.attr(\"class\"), stateVal), stateVal );\n\t\t\t});\n\t\t}\n\n\t\treturn this.each(function() {\n\t\t\tif ( type === \"string\" ) {\n\t\t\t\t// toggle individual class names\n\t\t\t\tvar className,\n\t\t\t\t\ti = 0,\n\t\t\t\t\tself = jQuery( this ),\n\t\t\t\t\tstate = stateVal,\n\t\t\t\t\tclassNames = value.split( rspaces );\n\n\t\t\t\twhile ( (className = classNames[ i++ ]) ) {\n\t\t\t\t\t// check each className given, space seperated list\n\t\t\t\t\tstate = isBool ? state : !self.hasClass( className );\n\t\t\t\t\tself[ state ? \"addClass\" : \"removeClass\" ]( className );\n\t\t\t\t}\n\n\t\t\t} else if ( type === \"undefined\" || type === \"boolean\" ) {\n\t\t\t\tif ( this.className ) {\n\t\t\t\t\t// store className if set\n\t\t\t\t\tjQuery._data( this, \"__className__\", this.className );\n\t\t\t\t}\n\n\t\t\t\t// toggle whole className\n\t\t\t\tthis.className = this.className || value === false ? \"\" : jQuery._data( this, \"__className__\" ) || \"\";\n\t\t\t}\n\t\t});\n\t},\n\n\thasClass: function( selector ) {\n\t\tvar className = \" \" + selector + \" \";\n\t\tfor ( var i = 0, l = this.length; i < l; i++ ) {\n\t\t\tif ( (\" \" + this[i].className + \" \").replace(rclass, \" \").indexOf( className ) > -1 ) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\t\treturn false;\n\t},\n\n\tval: function( value ) {\n\t\tif ( !arguments.length ) {\n\t\t\tvar elem = this[0];\n\n\t\t\tif ( elem ) {\n\t\t\t\tif ( jQuery.nodeName( elem, \"option\" ) ) {\n\t\t\t\t\t// attributes.value is undefined in Blackberry 4.7 but\n\t\t\t\t\t// uses .value. See #6932\n\t\t\t\t\tvar val = elem.attributes.value;\n\t\t\t\t\treturn !val || val.specified ? elem.value : elem.text;\n\t\t\t\t}\n\n\t\t\t\t// We need to handle select boxes special\n\t\t\t\tif ( jQuery.nodeName( elem, \"select\" ) ) {\n\t\t\t\t\tvar index = elem.selectedIndex,\n\t\t\t\t\t\tvalues = [],\n\t\t\t\t\t\toptions = elem.options,\n\t\t\t\t\t\tone = elem.type === \"select-one\";\n\n\t\t\t\t\t// Nothing was selected\n\t\t\t\t\tif ( index < 0 ) {\n\t\t\t\t\t\treturn null;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Loop through all the selected options\n\t\t\t\t\tfor ( var i = one ? index : 0, max = one ? index + 1 : options.length; i < max; i++ ) {\n\t\t\t\t\t\tvar option = options[ i ];\n\n\t\t\t\t\t\t// Don't return options that are disabled or in a disabled optgroup\n\t\t\t\t\t\tif ( option.selected && (jQuery.support.optDisabled ? !option.disabled : option.getAttribute(\"disabled\") === null) &&\n\t\t\t\t\t\t\t\t(!option.parentNode.disabled || !jQuery.nodeName( option.parentNode, \"optgroup\" )) ) {\n\n\t\t\t\t\t\t\t// Get the specific value for the option\n\t\t\t\t\t\t\tvalue = jQuery(option).val();\n\n\t\t\t\t\t\t\t// We don't need an array for one selects\n\t\t\t\t\t\t\tif ( one ) {\n\t\t\t\t\t\t\t\treturn value;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// Multi-Selects return an array\n\t\t\t\t\t\t\tvalues.push( value );\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Fixes Bug #2551 -- select.val() broken in IE after form.reset()\n\t\t\t\t\tif ( one && !values.length && options.length ) {\n\t\t\t\t\t\treturn jQuery( options[ index ] ).val();\n\t\t\t\t\t}\n\n\t\t\t\t\treturn values;\n\t\t\t\t}\n\n\t\t\t\t// Handle the case where in Webkit \"\" is returned instead of \"on\" if a value isn't specified\n\t\t\t\tif ( rradiocheck.test( elem.type ) && !jQuery.support.checkOn ) {\n\t\t\t\t\treturn elem.getAttribute(\"value\") === null ? \"on\" : elem.value;\n\t\t\t\t}\n\n\t\t\t\t// Everything else, we just grab the value\n\t\t\t\treturn (elem.value || \"\").replace(rreturn, \"\");\n\n\t\t\t}\n\n\t\t\treturn undefined;\n\t\t}\n\n\t\tvar isFunction = jQuery.isFunction(value);\n\n\t\treturn this.each(function(i) {\n\t\t\tvar self = jQuery(this), val = value;\n\n\t\t\tif ( this.nodeType !== 1 ) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif ( isFunction ) {\n\t\t\t\tval = value.call(this, i, self.val());\n\t\t\t}\n\n\t\t\t// Treat null/undefined as \"\"; convert numbers to string\n\t\t\tif ( val == null ) {\n\t\t\t\tval = \"\";\n\t\t\t} else if ( typeof val === \"number\" ) {\n\t\t\t\tval += \"\";\n\t\t\t} else if ( jQuery.isArray(val) ) {\n\t\t\t\tval = jQuery.map(val, function (value) {\n\t\t\t\t\treturn value == null ? \"\" : value + \"\";\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tif ( jQuery.isArray(val) && rradiocheck.test( this.type ) ) {\n\t\t\t\tthis.checked = jQuery.inArray( self.val(), val ) >= 0;\n\n\t\t\t} else if ( jQuery.nodeName( this, \"select\" ) ) {\n\t\t\t\tvar values = jQuery.makeArray(val);\n\n\t\t\t\tjQuery( \"option\", this ).each(function() {\n\t\t\t\t\tthis.selected = jQuery.inArray( jQuery(this).val(), values ) >= 0;\n\t\t\t\t});\n\n\t\t\t\tif ( !values.length ) {\n\t\t\t\t\tthis.selectedIndex = -1;\n\t\t\t\t}\n\n\t\t\t} else {\n\t\t\t\tthis.value = val;\n\t\t\t}\n\t\t});\n\t}\n});\n\njQuery.extend({\n\tattrFn: {\n\t\tval: true,\n\t\tcss: true,\n\t\thtml: true,\n\t\ttext: true,\n\t\tdata: true,\n\t\twidth: true,\n\t\theight: true,\n\t\toffset: true\n\t},\n\n\tattr: function( elem, name, value, pass ) {\n\t\t// don't get/set attributes on text, comment and attribute nodes\n\t\tif ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || elem.nodeType === 2 ) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tif ( pass && name in jQuery.attrFn ) {\n\t\t\treturn jQuery(elem)[name](value);\n\t\t}\n\n\t\tvar notxml = elem.nodeType !== 1 || !jQuery.isXMLDoc( elem ),\n\t\t\t// Whether we are setting (or getting)\n\t\t\tset = value !== undefined;\n\n\t\t// Try to normalize/fix the name\n\t\tname = notxml && jQuery.props[ name ] || name;\n\n\t\t// Only do all the following if this is a node (faster for style)\n\t\tif ( elem.nodeType === 1 ) {\n\t\t\t// These attributes require special treatment\n\t\t\tvar special = rspecialurl.test( name );\n\n\t\t\t// Safari mis-reports the default selected property of an option\n\t\t\t// Accessing the parent's selectedIndex property fixes it\n\t\t\tif ( name === \"selected\" && !jQuery.support.optSelected ) {\n\t\t\t\tvar parent = elem.parentNode;\n\t\t\t\tif ( parent ) {\n\t\t\t\t\tparent.selectedIndex;\n\n\t\t\t\t\t// Make sure that it also works with optgroups, see #5701\n\t\t\t\t\tif ( parent.parentNode ) {\n\t\t\t\t\t\tparent.parentNode.selectedIndex;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// If applicable, access the attribute via the DOM 0 way\n\t\t\t// 'in' checks fail in Blackberry 4.7 #6931\n\t\t\tif ( (name in elem || elem[ name ] !== undefined) && notxml && !special ) {\n\t\t\t\tif ( set ) {\n\t\t\t\t\t// We can't allow the type property to be changed (since it causes problems in IE)\n\t\t\t\t\tif ( name === \"type\" && rtype.test( elem.nodeName ) && elem.parentNode ) {\n\t\t\t\t\t\tjQuery.error( \"type property can't be changed\" );\n\t\t\t\t\t}\n\n\t\t\t\t\tif ( value === null ) {\n\t\t\t\t\t\tif ( elem.nodeType === 1 ) {\n\t\t\t\t\t\t\telem.removeAttribute( name );\n\t\t\t\t\t\t}\n\n\t\t\t\t\t} else {\n\t\t\t\t\t\telem[ name ] = value;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// browsers index elements by id/name on forms, give priority to attributes.\n\t\t\t\tif ( jQuery.nodeName( elem, \"form\" ) && elem.getAttributeNode(name) ) {\n\t\t\t\t\treturn elem.getAttributeNode( name ).nodeValue;\n\t\t\t\t}\n\n\t\t\t\t// elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set\n\t\t\t\t// http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/\n\t\t\t\tif ( name === \"tabIndex\" ) {\n\t\t\t\t\tvar attributeNode = elem.getAttributeNode( \"tabIndex\" );\n\n\t\t\t\t\treturn attributeNode && attributeNode.specified ?\n\t\t\t\t\t\tattributeNode.value :\n\t\t\t\t\t\trfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ?\n\t\t\t\t\t\t\t0 :\n\t\t\t\t\t\t\tundefined;\n\t\t\t\t}\n\n\t\t\t\treturn elem[ name ];\n\t\t\t}\n\n\t\t\tif ( !jQuery.support.style && notxml && name === \"style\" ) {\n\t\t\t\tif ( set ) {\n\t\t\t\t\telem.style.cssText = \"\" + value;\n\t\t\t\t}\n\n\t\t\t\treturn elem.style.cssText;\n\t\t\t}\n\n\t\t\tif ( set ) {\n\t\t\t\t// convert the value to a string (all browsers do this but IE) see #1070\n\t\t\t\telem.setAttribute( name, \"\" + value );\n\t\t\t}\n\n\t\t\t// Ensure that missing attributes return undefined\n\t\t\t// Blackberry 4.7 returns \"\" from getAttribute #6938\n\t\t\tif ( !elem.attributes[ name ] && (elem.hasAttribute && !elem.hasAttribute( name )) ) {\n\t\t\t\treturn undefined;\n\t\t\t}\n\n\t\t\tvar attr = !jQuery.support.hrefNormalized && notxml && special ?\n\t\t\t\t\t// Some attributes require a special call on IE\n\t\t\t\t\telem.getAttribute( name, 2 ) :\n\t\t\t\t\telem.getAttribute( name );\n\n\t\t\t// Non-existent attributes return null, we normalize to undefined\n\t\t\treturn attr === null ? undefined : attr;\n\t\t}\n\t\t// Handle everything which isn't a DOM element node\n\t\tif ( set ) {\n\t\t\telem[ name ] = value;\n\t\t}\n\t\treturn elem[ name ];\n\t}\n});\n\n\n\n\nvar rnamespaces = /\\.(.*)$/,\n\trformElems = /^(?:textarea|input|select)$/i,\n\trperiod = /\\./g,\n\trspace = / /g,\n\trescape = /[^\\w\\s.|`]/g,\n\tfcleanup = function( nm ) {\n\t\treturn nm.replace(rescape, \"\\\\$&\");\n\t};\n\n/*\n * A number of helper functions used for managing events.\n * Many of the ideas behind this code originated from\n * Dean Edwards' addEvent library.\n */\njQuery.event = {\n\n\t// Bind an event to an element\n\t// Original by Dean Edwards\n\tadd: function( elem, types, handler, data ) {\n\t\tif ( elem.nodeType === 3 || elem.nodeType === 8 ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// TODO :: Use a try/catch until it's safe to pull this out (likely 1.6)\n\t\t// Minor release fix for bug #8018\n\t\ttry {\n\t\t\t// For whatever reason, IE has trouble passing the window object\n\t\t\t// around, causing it to be cloned in the process\n\t\t\tif ( jQuery.isWindow( elem ) && ( elem !== window && !elem.frameElement ) ) {\n\t\t\t\telem = window;\n\t\t\t}\n\t\t}\n\t\tcatch ( e ) {}\n\n\t\tif ( handler === false ) {\n\t\t\thandler = returnFalse;\n\t\t} else if ( !handler ) {\n\t\t\t// Fixes bug #7229. Fix recommended by jdalton\n\t\t\treturn;\n\t\t}\n\n\t\tvar handleObjIn, handleObj;\n\n\t\tif ( handler.handler ) {\n\t\t\thandleObjIn = handler;\n\t\t\thandler = handleObjIn.handler;\n\t\t}\n\n\t\t// Make sure that the function being executed has a unique ID\n\t\tif ( !handler.guid ) {\n\t\t\thandler.guid = jQuery.guid++;\n\t\t}\n\n\t\t// Init the element's event structure\n\t\tvar elemData = jQuery._data( elem );\n\n\t\t// If no elemData is found then we must be trying to bind to one of the\n\t\t// banned noData elements\n\t\tif ( !elemData ) {\n\t\t\treturn;\n\t\t}\n\n\t\tvar events = elemData.events,\n\t\t\teventHandle = elemData.handle;\n\n\t\tif ( !events ) {\n\t\t\telemData.events = events = {};\n\t\t}\n\n\t\tif ( !eventHandle ) {\n\t\t\telemData.handle = eventHandle = function() {\n\t\t\t\t// Handle the second event of a trigger and when\n\t\t\t\t// an event is called after a page has unloaded\n\t\t\t\treturn typeof jQuery !== \"undefined\" && !jQuery.event.triggered ?\n\t\t\t\t\tjQuery.event.handle.apply( eventHandle.elem, arguments ) :\n\t\t\t\t\tundefined;\n\t\t\t};\n\t\t}\n\n\t\t// Add elem as a property of the handle function\n\t\t// This is to prevent a memory leak with non-native events in IE.\n\t\teventHandle.elem = elem;\n\n\t\t// Handle multiple events separated by a space\n\t\t// jQuery(...).bind(\"mouseover mouseout\", fn);\n\t\ttypes = types.split(\" \");\n\n\t\tvar type, i = 0, namespaces;\n\n\t\twhile ( (type = types[ i++ ]) ) {\n\t\t\thandleObj = handleObjIn ?\n\t\t\t\tjQuery.extend({}, handleObjIn) :\n\t\t\t\t{ handler: handler, data: data };\n\n\t\t\t// Namespaced event handlers\n\t\t\tif ( type.indexOf(\".\") > -1 ) {\n\t\t\t\tnamespaces = type.split(\".\");\n\t\t\t\ttype = namespaces.shift();\n\t\t\t\thandleObj.namespace = namespaces.slice(0).sort().join(\".\");\n\n\t\t\t} else {\n\t\t\t\tnamespaces = [];\n\t\t\t\thandleObj.namespace = \"\";\n\t\t\t}\n\n\t\t\thandleObj.type = type;\n\t\t\tif ( !handleObj.guid ) {\n\t\t\t\thandleObj.guid = handler.guid;\n\t\t\t}\n\n\t\t\t// Get the current list of functions bound to this event\n\t\t\tvar handlers = events[ type ],\n\t\t\t\tspecial = jQuery.event.special[ type ] || {};\n\n\t\t\t// Init the event handler queue\n\t\t\tif ( !handlers ) {\n\t\t\t\thandlers = events[ type ] = [];\n\n\t\t\t\t// Check for a special event handler\n\t\t\t\t// Only use addEventListener/attachEvent if the special\n\t\t\t\t// events handler returns false\n\t\t\t\tif ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) {\n\t\t\t\t\t// Bind the global event handler to the element\n\t\t\t\t\tif ( elem.addEventListener ) {\n\t\t\t\t\t\telem.addEventListener( type, eventHandle, false );\n\n\t\t\t\t\t} else if ( elem.attachEvent ) {\n\t\t\t\t\t\telem.attachEvent( \"on\" + type, eventHandle );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif ( special.add ) {\n\t\t\t\tspecial.add.call( elem, handleObj );\n\n\t\t\t\tif ( !handleObj.handler.guid ) {\n\t\t\t\t\thandleObj.handler.guid = handler.guid;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Add the function to the element's handler list\n\t\t\thandlers.push( handleObj );\n\n\t\t\t// Keep track of which events have been used, for global triggering\n\t\t\tjQuery.event.global[ type ] = true;\n\t\t}\n\n\t\t// Nullify elem to prevent memory leaks in IE\n\t\telem = null;\n\t},\n\n\tglobal: {},\n\n\t// Detach an event or set of events from an element\n\tremove: function( elem, types, handler, pos ) {\n\t\t// don't do events on text and comment nodes\n\t\tif ( elem.nodeType === 3 || elem.nodeType === 8 ) {\n\t\t\treturn;\n\t\t}\n\n\t\tif ( handler === false ) {\n\t\t\thandler = returnFalse;\n\t\t}\n\n\t\tvar ret, type, fn, j, i = 0, all, namespaces, namespace, special, eventType, handleObj, origType,\n\t\t\telemData = jQuery.hasData( elem ) && jQuery._data( elem ),\n\t\t\tevents = elemData && elemData.events;\n\n\t\tif ( !elemData || !events ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// types is actually an event object here\n\t\tif ( types && types.type ) {\n\t\t\thandler = types.handler;\n\t\t\ttypes = types.type;\n\t\t}\n\n\t\t// Unbind all events for the element\n\t\tif ( !types || typeof types === \"string\" && types.charAt(0) === \".\" ) {\n\t\t\ttypes = types || \"\";\n\n\t\t\tfor ( type in events ) {\n\t\t\t\tjQuery.event.remove( elem, type + types );\n\t\t\t}\n\n\t\t\treturn;\n\t\t}\n\n\t\t// Handle multiple events separated by a space\n\t\t// jQuery(...).unbind(\"mouseover mouseout\", fn);\n\t\ttypes = types.split(\" \");\n\n\t\twhile ( (type = types[ i++ ]) ) {\n\t\t\torigType = type;\n\t\t\thandleObj = null;\n\t\t\tall = type.indexOf(\".\") < 0;\n\t\t\tnamespaces = [];\n\n\t\t\tif ( !all ) {\n\t\t\t\t// Namespaced event handlers\n\t\t\t\tnamespaces = type.split(\".\");\n\t\t\t\ttype = namespaces.shift();\n\n\t\t\t\tnamespace = new RegExp(\"(^|\\\\.)\" +\n\t\t\t\t\tjQuery.map( namespaces.slice(0).sort(), fcleanup ).join(\"\\\\.(?:.*\\\\.)?\") + \"(\\\\.|$)\");\n\t\t\t}\n\n\t\t\teventType = events[ type ];\n\n\t\t\tif ( !eventType ) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif ( !handler ) {\n\t\t\t\tfor ( j = 0; j < eventType.length; j++ ) {\n\t\t\t\t\thandleObj = eventType[ j ];\n\n\t\t\t\t\tif ( all || namespace.test( handleObj.namespace ) ) {\n\t\t\t\t\t\tjQuery.event.remove( elem, origType, handleObj.handler, j );\n\t\t\t\t\t\teventType.splice( j--, 1 );\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tspecial = jQuery.event.special[ type ] || {};\n\n\t\t\tfor ( j = pos || 0; j < eventType.length; j++ ) {\n\t\t\t\thandleObj = eventType[ j ];\n\n\t\t\t\tif ( handler.guid === handleObj.guid ) {\n\t\t\t\t\t// remove the given handler for the given type\n\t\t\t\t\tif ( all || namespace.test( handleObj.namespace ) ) {\n\t\t\t\t\t\tif ( pos == null ) {\n\t\t\t\t\t\t\teventType.splice( j--, 1 );\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif ( special.remove ) {\n\t\t\t\t\t\t\tspecial.remove.call( elem, handleObj );\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif ( pos != null ) {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// remove generic event handler if no more handlers exist\n\t\t\tif ( eventType.length === 0 || pos != null && eventType.length === 1 ) {\n\t\t\t\tif ( !special.teardown || special.teardown.call( elem, namespaces ) === false ) {\n\t\t\t\t\tjQuery.removeEvent( elem, type, elemData.handle );\n\t\t\t\t}\n\n\t\t\t\tret = null;\n\t\t\t\tdelete events[ type ];\n\t\t\t}\n\t\t}\n\n\t\t// Remove the expando if it's no longer used\n\t\tif ( jQuery.isEmptyObject( events ) ) {\n\t\t\tvar handle = elemData.handle;\n\t\t\tif ( handle ) {\n\t\t\t\thandle.elem = null;\n\t\t\t}\n\n\t\t\tdelete elemData.events;\n\t\t\tdelete elemData.handle;\n\n\t\t\tif ( jQuery.isEmptyObject( elemData ) ) {\n\t\t\t\tjQuery.removeData( elem, undefined, true );\n\t\t\t}\n\t\t}\n\t},\n\n\t// bubbling is internal\n\ttrigger: function( event, data, elem /*, bubbling */ ) {\n\t\t// Event object or event type\n\t\tvar type = event.type || event,\n\t\t\tbubbling = arguments[3];\n\n\t\tif ( !bubbling ) {\n\t\t\tevent = typeof event === \"object\" ?\n\t\t\t\t// jQuery.Event object\n\t\t\t\tevent[ jQuery.expando ] ? event :\n\t\t\t\t// Object literal\n\t\t\t\tjQuery.extend( jQuery.Event(type), event ) :\n\t\t\t\t// Just the event type (string)\n\t\t\t\tjQuery.Event(type);\n\n\t\t\tif ( type.indexOf(\"!\") >= 0 ) {\n\t\t\t\tevent.type = type = type.slice(0, -1);\n\t\t\t\tevent.exclusive = true;\n\t\t\t}\n\n\t\t\t// Handle a global trigger\n\t\t\tif ( !elem ) {\n\t\t\t\t// Don't bubble custom events when global (to avoid too much overhead)\n\t\t\t\tevent.stopPropagation();\n\n\t\t\t\t// Only trigger if we've ever bound an event for it\n\t\t\t\tif ( jQuery.event.global[ type ] ) {\n\t\t\t\t\t// XXX This code smells terrible. event.js should not be directly\n\t\t\t\t\t// inspecting the data cache\n\t\t\t\t\tjQuery.each( jQuery.cache, function() {\n\t\t\t\t\t\t// internalKey variable is just used to make it easier to find\n\t\t\t\t\t\t// and potentially change this stuff later; currently it just\n\t\t\t\t\t\t// points to jQuery.expando\n\t\t\t\t\t\tvar internalKey = jQuery.expando,\n\t\t\t\t\t\t\tinternalCache = this[ internalKey ];\n\t\t\t\t\t\tif ( internalCache && internalCache.events && internalCache.events[ type ] ) {\n\t\t\t\t\t\t\tjQuery.event.trigger( event, data, internalCache.handle.elem );\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Handle triggering a single element\n\n\t\t\t// don't do events on text and comment nodes\n\t\t\tif ( !elem || elem.nodeType === 3 || elem.nodeType === 8 ) {\n\t\t\t\treturn undefined;\n\t\t\t}\n\n\t\t\t// Clean up in case it is reused\n\t\t\tevent.result = undefined;\n\t\t\tevent.target = elem;\n\n\t\t\t// Clone the incoming data, if any\n\t\t\tdata = jQuery.makeArray( data );\n\t\t\tdata.unshift( event );\n\t\t}\n\n\t\tevent.currentTarget = elem;\n\n\t\t// Trigger the event, it is assumed that \"handle\" is a function\n\t\tvar handle = jQuery._data( elem, \"handle\" );\n\n\t\tif ( handle ) {\n\t\t\thandle.apply( elem, data );\n\t\t}\n\n\t\tvar parent = elem.parentNode || elem.ownerDocument;\n\n\t\t// Trigger an inline bound script\n\t\ttry {\n\t\t\tif ( !(elem && elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()]) ) {\n\t\t\t\tif ( elem[ \"on\" + type ] && elem[ \"on\" + type ].apply( elem, data ) === false ) {\n\t\t\t\t\tevent.result = false;\n\t\t\t\t\tevent.preventDefault();\n\t\t\t\t}\n\t\t\t}\n\n\t\t// prevent IE from throwing an error for some elements with some event types, see #3533\n\t\t} catch (inlineError) {}\n\n\t\tif ( !event.isPropagationStopped() && parent ) {\n\t\t\tjQuery.event.trigger( event, data, parent, true );\n\n\t\t} else if ( !event.isDefaultPrevented() ) {\n\t\t\tvar old,\n\t\t\t\ttarget = event.target,\n\t\t\t\ttargetType = type.replace( rnamespaces, \"\" ),\n\t\t\t\tisClick = jQuery.nodeName( target, \"a\" ) && targetType === \"click\",\n\t\t\t\tspecial = jQuery.event.special[ targetType ] || {};\n\n\t\t\tif ( (!special._default || special._default.call( elem, event ) === false) &&\n\t\t\t\t!isClick && !(target && target.nodeName && jQuery.noData[target.nodeName.toLowerCase()]) ) {\n\n\t\t\t\ttry {\n\t\t\t\t\tif ( target[ targetType ] ) {\n\t\t\t\t\t\t// Make sure that we don't accidentally re-trigger the onFOO events\n\t\t\t\t\t\told = target[ \"on\" + targetType ];\n\n\t\t\t\t\t\tif ( old ) {\n\t\t\t\t\t\t\ttarget[ \"on\" + targetType ] = null;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tjQuery.event.triggered = true;\n\t\t\t\t\t\ttarget[ targetType ]();\n\t\t\t\t\t}\n\n\t\t\t\t// prevent IE from throwing an error for some elements with some event types, see #3533\n\t\t\t\t} catch (triggerError) {}\n\n\t\t\t\tif ( old ) {\n\t\t\t\t\ttarget[ \"on\" + targetType ] = old;\n\t\t\t\t}\n\n\t\t\t\tjQuery.event.triggered = false;\n\t\t\t}\n\t\t}\n\t},\n\n\thandle: function( event ) {\n\t\tvar all, handlers, namespaces, namespace_re, events,\n\t\t\tnamespace_sort = [],\n\t\t\targs = jQuery.makeArray( arguments );\n\n\t\tevent = args[0] = jQuery.event.fix( event || window.event );\n\t\tevent.currentTarget = this;\n\n\t\t// Namespaced event handlers\n\t\tall = event.type.indexOf(\".\") < 0 && !event.exclusive;\n\n\t\tif ( !all ) {\n\t\t\tnamespaces = event.type.split(\".\");\n\t\t\tevent.type = namespaces.shift();\n\t\t\tnamespace_sort = namespaces.slice(0).sort();\n\t\t\tnamespace_re = new RegExp(\"(^|\\\\.)\" + namespace_sort.join(\"\\\\.(?:.*\\\\.)?\") + \"(\\\\.|$)\");\n\t\t}\n\n\t\tevent.namespace = event.namespace || namespace_sort.join(\".\");\n\n\t\tevents = jQuery._data(this, \"events\");\n\n\t\thandlers = (events || {})[ event.type ];\n\n\t\tif ( events && handlers ) {\n\t\t\t// Clone the handlers to prevent manipulation\n\t\t\thandlers = handlers.slice(0);\n\n\t\t\tfor ( var j = 0, l = handlers.length; j < l; j++ ) {\n\t\t\t\tvar handleObj = handlers[ j ];\n\n\t\t\t\t// Filter the functions by class\n\t\t\t\tif ( all || namespace_re.test( handleObj.namespace ) ) {\n\t\t\t\t\t// Pass in a reference to the handler function itself\n\t\t\t\t\t// So that we can later remove it\n\t\t\t\t\tevent.handler = handleObj.handler;\n\t\t\t\t\tevent.data = handleObj.data;\n\t\t\t\t\tevent.handleObj = handleObj;\n\n\t\t\t\t\tvar ret = handleObj.handler.apply( this, args );\n\n\t\t\t\t\tif ( ret !== undefined ) {\n\t\t\t\t\t\tevent.result = ret;\n\t\t\t\t\t\tif ( ret === false ) {\n\t\t\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\t\t\tevent.stopPropagation();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif ( event.isImmediatePropagationStopped() ) {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn event.result;\n\t},\n\n\tprops: \"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which\".split(\" \"),\n\n\tfix: function( event ) {\n\t\tif ( event[ jQuery.expando ] ) {\n\t\t\treturn event;\n\t\t}\n\n\t\t// store a copy of the original event object\n\t\t// and \"clone\" to set read-only properties\n\t\tvar originalEvent = event;\n\t\tevent = jQuery.Event( originalEvent );\n\n\t\tfor ( var i = this.props.length, prop; i; ) {\n\t\t\tprop = this.props[ --i ];\n\t\t\tevent[ prop ] = originalEvent[ prop ];\n\t\t}\n\n\t\t// Fix target property, if necessary\n\t\tif ( !event.target ) {\n\t\t\t// Fixes #1925 where srcElement might not be defined either\n\t\t\tevent.target = event.srcElement || document;\n\t\t}\n\n\t\t// check if target is a textnode (safari)\n\t\tif ( event.target.nodeType === 3 ) {\n\t\t\tevent.target = event.target.parentNode;\n\t\t}\n\n\t\t// Add relatedTarget, if necessary\n\t\tif ( !event.relatedTarget && event.fromElement ) {\n\t\t\tevent.relatedTarget = event.fromElement === event.target ? event.toElement : event.fromElement;\n\t\t}\n\n\t\t// Calculate pageX/Y if missing and clientX/Y available\n\t\tif ( event.pageX == null && event.clientX != null ) {\n\t\t\tvar doc = document.documentElement,\n\t\t\t\tbody = document.body;\n\n\t\t\tevent.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0);\n\t\t\tevent.pageY = event.clientY + (doc && doc.scrollTop  || body && body.scrollTop  || 0) - (doc && doc.clientTop  || body && body.clientTop  || 0);\n\t\t}\n\n\t\t// Add which for key events\n\t\tif ( event.which == null && (event.charCode != null || event.keyCode != null) ) {\n\t\t\tevent.which = event.charCode != null ? event.charCode : event.keyCode;\n\t\t}\n\n\t\t// Add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs)\n\t\tif ( !event.metaKey && event.ctrlKey ) {\n\t\t\tevent.metaKey = event.ctrlKey;\n\t\t}\n\n\t\t// Add which for click: 1 === left; 2 === middle; 3 === right\n\t\t// Note: button is not normalized, so don't use it\n\t\tif ( !event.which && event.button !== undefined ) {\n\t\t\tevent.which = (event.button & 1 ? 1 : ( event.button & 2 ? 3 : ( event.button & 4 ? 2 : 0 ) ));\n\t\t}\n\n\t\treturn event;\n\t},\n\n\t// Deprecated, use jQuery.guid instead\n\tguid: 1E8,\n\n\t// Deprecated, use jQuery.proxy instead\n\tproxy: jQuery.proxy,\n\n\tspecial: {\n\t\tready: {\n\t\t\t// Make sure the ready event is setup\n\t\t\tsetup: jQuery.bindReady,\n\t\t\tteardown: jQuery.noop\n\t\t},\n\n\t\tlive: {\n\t\t\tadd: function( handleObj ) {\n\t\t\t\tjQuery.event.add( this,\n\t\t\t\t\tliveConvert( handleObj.origType, handleObj.selector ),\n\t\t\t\t\tjQuery.extend({}, handleObj, {handler: liveHandler, guid: handleObj.handler.guid}) );\n\t\t\t},\n\n\t\t\tremove: function( handleObj ) {\n\t\t\t\tjQuery.event.remove( this, liveConvert( handleObj.origType, handleObj.selector ), handleObj );\n\t\t\t}\n\t\t},\n\n\t\tbeforeunload: {\n\t\t\tsetup: function( data, namespaces, eventHandle ) {\n\t\t\t\t// We only want to do this special case on windows\n\t\t\t\tif ( jQuery.isWindow( this ) ) {\n\t\t\t\t\tthis.onbeforeunload = eventHandle;\n\t\t\t\t}\n\t\t\t},\n\n\t\t\tteardown: function( namespaces, eventHandle ) {\n\t\t\t\tif ( this.onbeforeunload === eventHandle ) {\n\t\t\t\t\tthis.onbeforeunload = null;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n};\n\njQuery.removeEvent = document.removeEventListener ?\n\tfunction( elem, type, handle ) {\n\t\tif ( elem.removeEventListener ) {\n\t\t\telem.removeEventListener( type, handle, false );\n\t\t}\n\t} :\n\tfunction( elem, type, handle ) {\n\t\tif ( elem.detachEvent ) {\n\t\t\telem.detachEvent( \"on\" + type, handle );\n\t\t}\n\t};\n\njQuery.Event = function( src ) {\n\t// Allow instantiation without the 'new' keyword\n\tif ( !this.preventDefault ) {\n\t\treturn new jQuery.Event( src );\n\t}\n\n\t// Event object\n\tif ( src && src.type ) {\n\t\tthis.originalEvent = src;\n\t\tthis.type = src.type;\n\n\t\t// Events bubbling up the document may have been marked as prevented\n\t\t// by a handler lower down the tree; reflect the correct value.\n\t\tthis.isDefaultPrevented = (src.defaultPrevented || src.returnValue === false ||\n\t\t\tsrc.getPreventDefault && src.getPreventDefault()) ? returnTrue : returnFalse;\n\n\t// Event type\n\t} else {\n\t\tthis.type = src;\n\t}\n\n\t// timeStamp is buggy for some events on Firefox(#3843)\n\t// So we won't rely on the native value\n\tthis.timeStamp = jQuery.now();\n\n\t// Mark it as fixed\n\tthis[ jQuery.expando ] = true;\n};\n\nfunction returnFalse() {\n\treturn false;\n}\nfunction returnTrue() {\n\treturn true;\n}\n\n// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding\n// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html\njQuery.Event.prototype = {\n\tpreventDefault: function() {\n\t\tthis.isDefaultPrevented = returnTrue;\n\n\t\tvar e = this.originalEvent;\n\t\tif ( !e ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// if preventDefault exists run it on the original event\n\t\tif ( e.preventDefault ) {\n\t\t\te.preventDefault();\n\n\t\t// otherwise set the returnValue property of the original event to false (IE)\n\t\t} else {\n\t\t\te.returnValue = false;\n\t\t}\n\t},\n\tstopPropagation: function() {\n\t\tthis.isPropagationStopped = returnTrue;\n\n\t\tvar e = this.originalEvent;\n\t\tif ( !e ) {\n\t\t\treturn;\n\t\t}\n\t\t// if stopPropagation exists run it on the original event\n\t\tif ( e.stopPropagation ) {\n\t\t\te.stopPropagation();\n\t\t}\n\t\t// otherwise set the cancelBubble property of the original event to true (IE)\n\t\te.cancelBubble = true;\n\t},\n\tstopImmediatePropagation: function() {\n\t\tthis.isImmediatePropagationStopped = returnTrue;\n\t\tthis.stopPropagation();\n\t},\n\tisDefaultPrevented: returnFalse,\n\tisPropagationStopped: returnFalse,\n\tisImmediatePropagationStopped: returnFalse\n};\n\n// Checks if an event happened on an element within another element\n// Used in jQuery.event.special.mouseenter and mouseleave handlers\nvar withinElement = function( event ) {\n\t// Check if mouse(over|out) are still within the same parent element\n\tvar parent = event.relatedTarget;\n\n\t// Firefox sometimes assigns relatedTarget a XUL element\n\t// which we cannot access the parentNode property of\n\ttry {\n\n\t\t// Chrome does something similar, the parentNode property\n\t\t// can be accessed but is null.\n\t\tif ( parent !== document && !parent.parentNode ) {\n\t\t\treturn;\n\t\t}\n\t\t// Traverse up the tree\n\t\twhile ( parent && parent !== this ) {\n\t\t\tparent = parent.parentNode;\n\t\t}\n\n\t\tif ( parent !== this ) {\n\t\t\t// set the correct event type\n\t\t\tevent.type = event.data;\n\n\t\t\t// handle event if we actually just moused on to a non sub-element\n\t\t\tjQuery.event.handle.apply( this, arguments );\n\t\t}\n\n\t// assuming we've left the element since we most likely mousedover a xul element\n\t} catch(e) { }\n},\n\n// In case of event delegation, we only need to rename the event.type,\n// liveHandler will take care of the rest.\ndelegate = function( event ) {\n\tevent.type = event.data;\n\tjQuery.event.handle.apply( this, arguments );\n};\n\n// Create mouseenter and mouseleave events\njQuery.each({\n\tmouseenter: \"mouseover\",\n\tmouseleave: \"mouseout\"\n}, function( orig, fix ) {\n\tjQuery.event.special[ orig ] = {\n\t\tsetup: function( data ) {\n\t\t\tjQuery.event.add( this, fix, data && data.selector ? delegate : withinElement, orig );\n\t\t},\n\t\tteardown: function( data ) {\n\t\t\tjQuery.event.remove( this, fix, data && data.selector ? delegate : withinElement );\n\t\t}\n\t};\n});\n\n// submit delegation\nif ( !jQuery.support.submitBubbles ) {\n\n\tjQuery.event.special.submit = {\n\t\tsetup: function( data, namespaces ) {\n\t\t\tif ( this.nodeName && this.nodeName.toLowerCase() !== \"form\" ) {\n\t\t\t\tjQuery.event.add(this, \"click.specialSubmit\", function( e ) {\n\t\t\t\t\tvar elem = e.target,\n\t\t\t\t\t\ttype = elem.type;\n\n\t\t\t\t\tif ( (type === \"submit\" || type === \"image\") && jQuery( elem ).closest(\"form\").length ) {\n\t\t\t\t\t\ttrigger( \"submit\", this, arguments );\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t\tjQuery.event.add(this, \"keypress.specialSubmit\", function( e ) {\n\t\t\t\t\tvar elem = e.target,\n\t\t\t\t\t\ttype = elem.type;\n\n\t\t\t\t\tif ( (type === \"text\" || type === \"password\") && jQuery( elem ).closest(\"form\").length && e.keyCode === 13 ) {\n\t\t\t\t\t\ttrigger( \"submit\", this, arguments );\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t} else {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t},\n\n\t\tteardown: function( namespaces ) {\n\t\t\tjQuery.event.remove( this, \".specialSubmit\" );\n\t\t}\n\t};\n\n}\n\n// change delegation, happens here so we have bind.\nif ( !jQuery.support.changeBubbles ) {\n\n\tvar changeFilters,\n\n\tgetVal = function( elem ) {\n\t\tvar type = elem.type, val = elem.value;\n\n\t\tif ( type === \"radio\" || type === \"checkbox\" ) {\n\t\t\tval = elem.checked;\n\n\t\t} else if ( type === \"select-multiple\" ) {\n\t\t\tval = elem.selectedIndex > -1 ?\n\t\t\t\tjQuery.map( elem.options, function( elem ) {\n\t\t\t\t\treturn elem.selected;\n\t\t\t\t}).join(\"-\") :\n\t\t\t\t\"\";\n\n\t\t} else if ( elem.nodeName.toLowerCase() === \"select\" ) {\n\t\t\tval = elem.selectedIndex;\n\t\t}\n\n\t\treturn val;\n\t},\n\n\ttestChange = function testChange( e ) {\n\t\tvar elem = e.target, data, val;\n\n\t\tif ( !rformElems.test( elem.nodeName ) || elem.readOnly ) {\n\t\t\treturn;\n\t\t}\n\n\t\tdata = jQuery._data( elem, \"_change_data\" );\n\t\tval = getVal(elem);\n\n\t\t// the current data will be also retrieved by beforeactivate\n\t\tif ( e.type !== \"focusout\" || elem.type !== \"radio\" ) {\n\t\t\tjQuery._data( elem, \"_change_data\", val );\n\t\t}\n\n\t\tif ( data === undefined || val === data ) {\n\t\t\treturn;\n\t\t}\n\n\t\tif ( data != null || val ) {\n\t\t\te.type = \"change\";\n\t\t\te.liveFired = undefined;\n\t\t\tjQuery.event.trigger( e, arguments[1], elem );\n\t\t}\n\t};\n\n\tjQuery.event.special.change = {\n\t\tfilters: {\n\t\t\tfocusout: testChange,\n\n\t\t\tbeforedeactivate: testChange,\n\n\t\t\tclick: function( e ) {\n\t\t\t\tvar elem = e.target, type = elem.type;\n\n\t\t\t\tif ( type === \"radio\" || type === \"checkbox\" || elem.nodeName.toLowerCase() === \"select\" ) {\n\t\t\t\t\ttestChange.call( this, e );\n\t\t\t\t}\n\t\t\t},\n\n\t\t\t// Change has to be called before submit\n\t\t\t// Keydown will be called before keypress, which is used in submit-event delegation\n\t\t\tkeydown: function( e ) {\n\t\t\t\tvar elem = e.target, type = elem.type;\n\n\t\t\t\tif ( (e.keyCode === 13 && elem.nodeName.toLowerCase() !== \"textarea\") ||\n\t\t\t\t\t(e.keyCode === 32 && (type === \"checkbox\" || type === \"radio\")) ||\n\t\t\t\t\ttype === \"select-multiple\" ) {\n\t\t\t\t\ttestChange.call( this, e );\n\t\t\t\t}\n\t\t\t},\n\n\t\t\t// Beforeactivate happens also before the previous element is blurred\n\t\t\t// with this event you can't trigger a change event, but you can store\n\t\t\t// information\n\t\t\tbeforeactivate: function( e ) {\n\t\t\t\tvar elem = e.target;\n\t\t\t\tjQuery._data( elem, \"_change_data\", getVal(elem) );\n\t\t\t}\n\t\t},\n\n\t\tsetup: function( data, namespaces ) {\n\t\t\tif ( this.type === \"file\" ) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tfor ( var type in changeFilters ) {\n\t\t\t\tjQuery.event.add( this, type + \".specialChange\", changeFilters[type] );\n\t\t\t}\n\n\t\t\treturn rformElems.test( this.nodeName );\n\t\t},\n\n\t\tteardown: function( namespaces ) {\n\t\t\tjQuery.event.remove( this, \".specialChange\" );\n\n\t\t\treturn rformElems.test( this.nodeName );\n\t\t}\n\t};\n\n\tchangeFilters = jQuery.event.special.change.filters;\n\n\t// Handle when the input is .focus()'d\n\tchangeFilters.focus = changeFilters.beforeactivate;\n}\n\nfunction trigger( type, elem, args ) {\n\t// Piggyback on a donor event to simulate a different one.\n\t// Fake originalEvent to avoid donor's stopPropagation, but if the\n\t// simulated event prevents default then we do the same on the donor.\n\t// Don't pass args or remember liveFired; they apply to the donor event.\n\tvar event = jQuery.extend( {}, args[ 0 ] );\n\tevent.type = type;\n\tevent.originalEvent = {};\n\tevent.liveFired = undefined;\n\tjQuery.event.handle.call( elem, event );\n\tif ( event.isDefaultPrevented() ) {\n\t\targs[ 0 ].preventDefault();\n\t}\n}\n\n// Create \"bubbling\" focus and blur events\nif ( document.addEventListener ) {\n\tjQuery.each({ focus: \"focusin\", blur: \"focusout\" }, function( orig, fix ) {\n\t\tjQuery.event.special[ fix ] = {\n\t\t\tsetup: function() {\n\t\t\t\tthis.addEventListener( orig, handler, true );\n\t\t\t},\n\t\t\tteardown: function() {\n\t\t\t\tthis.removeEventListener( orig, handler, true );\n\t\t\t}\n\t\t};\n\n\t\tfunction handler( e ) {\n\t\t\te = jQuery.event.fix( e );\n\t\t\te.type = fix;\n\t\t\treturn jQuery.event.handle.call( this, e );\n\t\t}\n\t});\n}\n\njQuery.each([\"bind\", \"one\"], function( i, name ) {\n\tjQuery.fn[ name ] = function( type, data, fn ) {\n\t\t// Handle object literals\n\t\tif ( typeof type === \"object\" ) {\n\t\t\tfor ( var key in type ) {\n\t\t\t\tthis[ name ](key, data, type[key], fn);\n\t\t\t}\n\t\t\treturn this;\n\t\t}\n\n\t\tif ( jQuery.isFunction( data ) || data === false ) {\n\t\t\tfn = data;\n\t\t\tdata = undefined;\n\t\t}\n\n\t\tvar handler = name === \"one\" ? jQuery.proxy( fn, function( event ) {\n\t\t\tjQuery( this ).unbind( event, handler );\n\t\t\treturn fn.apply( this, arguments );\n\t\t}) : fn;\n\n\t\tif ( type === \"unload\" && name !== \"one\" ) {\n\t\t\tthis.one( type, data, fn );\n\n\t\t} else {\n\t\t\tfor ( var i = 0, l = this.length; i < l; i++ ) {\n\t\t\t\tjQuery.event.add( this[i], type, handler, data );\n\t\t\t}\n\t\t}\n\n\t\treturn this;\n\t};\n});\n\njQuery.fn.extend({\n\tunbind: function( type, fn ) {\n\t\t// Handle object literals\n\t\tif ( typeof type === \"object\" && !type.preventDefault ) {\n\t\t\tfor ( var key in type ) {\n\t\t\t\tthis.unbind(key, type[key]);\n\t\t\t}\n\n\t\t} else {\n\t\t\tfor ( var i = 0, l = this.length; i < l; i++ ) {\n\t\t\t\tjQuery.event.remove( this[i], type, fn );\n\t\t\t}\n\t\t}\n\n\t\treturn this;\n\t},\n\n\tdelegate: function( selector, types, data, fn ) {\n\t\treturn this.live( types, data, fn, selector );\n\t},\n\n\tundelegate: function( selector, types, fn ) {\n\t\tif ( arguments.length === 0 ) {\n\t\t\t\treturn this.unbind( \"live\" );\n\n\t\t} else {\n\t\t\treturn this.die( types, null, fn, selector );\n\t\t}\n\t},\n\n\ttrigger: function( type, data ) {\n\t\treturn this.each(function() {\n\t\t\tjQuery.event.trigger( type, data, this );\n\t\t});\n\t},\n\n\ttriggerHandler: function( type, data ) {\n\t\tif ( this[0] ) {\n\t\t\tvar event = jQuery.Event( type );\n\t\t\tevent.preventDefault();\n\t\t\tevent.stopPropagation();\n\t\t\tjQuery.event.trigger( event, data, this[0] );\n\t\t\treturn event.result;\n\t\t}\n\t},\n\n\ttoggle: function( fn ) {\n\t\t// Save reference to arguments for access in closure\n\t\tvar args = arguments,\n\t\t\ti = 1;\n\n\t\t// link all the functions, so any of them can unbind this click handler\n\t\twhile ( i < args.length ) {\n\t\t\tjQuery.proxy( fn, args[ i++ ] );\n\t\t}\n\n\t\treturn this.click( jQuery.proxy( fn, function( event ) {\n\t\t\t// Figure out which function to execute\n\t\t\tvar lastToggle = ( jQuery._data( this, \"lastToggle\" + fn.guid ) || 0 ) % i;\n\t\t\tjQuery._data( this, \"lastToggle\" + fn.guid, lastToggle + 1 );\n\n\t\t\t// Make sure that clicks stop\n\t\t\tevent.preventDefault();\n\n\t\t\t// and execute the function\n\t\t\treturn args[ lastToggle ].apply( this, arguments ) || false;\n\t\t}));\n\t},\n\n\thover: function( fnOver, fnOut ) {\n\t\treturn this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );\n\t}\n});\n\nvar liveMap = {\n\tfocus: \"focusin\",\n\tblur: \"focusout\",\n\tmouseenter: \"mouseover\",\n\tmouseleave: \"mouseout\"\n};\n\njQuery.each([\"live\", \"die\"], function( i, name ) {\n\tjQuery.fn[ name ] = function( types, data, fn, origSelector /* Internal Use Only */ ) {\n\t\tvar type, i = 0, match, namespaces, preType,\n\t\t\tselector = origSelector || this.selector,\n\t\t\tcontext = origSelector ? this : jQuery( this.context );\n\n\t\tif ( typeof types === \"object\" && !types.preventDefault ) {\n\t\t\tfor ( var key in types ) {\n\t\t\t\tcontext[ name ]( key, data, types[key], selector );\n\t\t\t}\n\n\t\t\treturn this;\n\t\t}\n\n\t\tif ( jQuery.isFunction( data ) ) {\n\t\t\tfn = data;\n\t\t\tdata = undefined;\n\t\t}\n\n\t\ttypes = (types || \"\").split(\" \");\n\n\t\twhile ( (type = types[ i++ ]) != null ) {\n\t\t\tmatch = rnamespaces.exec( type );\n\t\t\tnamespaces = \"\";\n\n\t\t\tif ( match )  {\n\t\t\t\tnamespaces = match[0];\n\t\t\t\ttype = type.replace( rnamespaces, \"\" );\n\t\t\t}\n\n\t\t\tif ( type === \"hover\" ) {\n\t\t\t\ttypes.push( \"mouseenter\" + namespaces, \"mouseleave\" + namespaces );\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tpreType = type;\n\n\t\t\tif ( type === \"focus\" || type === \"blur\" ) {\n\t\t\t\ttypes.push( liveMap[ type ] + namespaces );\n\t\t\t\ttype = type + namespaces;\n\n\t\t\t} else {\n\t\t\t\ttype = (liveMap[ type ] || type) + namespaces;\n\t\t\t}\n\n\t\t\tif ( name === \"live\" ) {\n\t\t\t\t// bind live handler\n\t\t\t\tfor ( var j = 0, l = context.length; j < l; j++ ) {\n\t\t\t\t\tjQuery.event.add( context[j], \"live.\" + liveConvert( type, selector ),\n\t\t\t\t\t\t{ data: data, selector: selector, handler: fn, origType: type, origHandler: fn, preType: preType } );\n\t\t\t\t}\n\n\t\t\t} else {\n\t\t\t\t// unbind live handler\n\t\t\t\tcontext.unbind( \"live.\" + liveConvert( type, selector ), fn );\n\t\t\t}\n\t\t}\n\n\t\treturn this;\n\t};\n});\n\nfunction liveHandler( event ) {\n\tvar stop, maxLevel, related, match, handleObj, elem, j, i, l, data, close, namespace, ret,\n\t\telems = [],\n\t\tselectors = [],\n\t\tevents = jQuery._data( this, \"events\" );\n\n\t// Make sure we avoid non-left-click bubbling in Firefox (#3861) and disabled elements in IE (#6911)\n\tif ( event.liveFired === this || !events || !events.live || event.target.disabled || event.button && event.type === \"click\" ) {\n\t\treturn;\n\t}\n\n\tif ( event.namespace ) {\n\t\tnamespace = new RegExp(\"(^|\\\\.)\" + event.namespace.split(\".\").join(\"\\\\.(?:.*\\\\.)?\") + \"(\\\\.|$)\");\n\t}\n\n\tevent.liveFired = this;\n\n\tvar live = events.live.slice(0);\n\n\tfor ( j = 0; j < live.length; j++ ) {\n\t\thandleObj = live[j];\n\n\t\tif ( handleObj.origType.replace( rnamespaces, \"\" ) === event.type ) {\n\t\t\tselectors.push( handleObj.selector );\n\n\t\t} else {\n\t\t\tlive.splice( j--, 1 );\n\t\t}\n\t}\n\n\tmatch = jQuery( event.target ).closest( selectors, event.currentTarget );\n\n\tfor ( i = 0, l = match.length; i < l; i++ ) {\n\t\tclose = match[i];\n\n\t\tfor ( j = 0; j < live.length; j++ ) {\n\t\t\thandleObj = live[j];\n\n\t\t\tif ( close.selector === handleObj.selector && (!namespace || namespace.test( handleObj.namespace )) && !close.elem.disabled ) {\n\t\t\t\telem = close.elem;\n\t\t\t\trelated = null;\n\n\t\t\t\t// Those two events require additional checking\n\t\t\t\tif ( handleObj.preType === \"mouseenter\" || handleObj.preType === \"mouseleave\" ) {\n\t\t\t\t\tevent.type = handleObj.preType;\n\t\t\t\t\trelated = jQuery( event.relatedTarget ).closest( handleObj.selector )[0];\n\t\t\t\t}\n\n\t\t\t\tif ( !related || related !== elem ) {\n\t\t\t\t\telems.push({ elem: elem, handleObj: handleObj, level: close.level });\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tfor ( i = 0, l = elems.length; i < l; i++ ) {\n\t\tmatch = elems[i];\n\n\t\tif ( maxLevel && match.level > maxLevel ) {\n\t\t\tbreak;\n\t\t}\n\n\t\tevent.currentTarget = match.elem;\n\t\tevent.data = match.handleObj.data;\n\t\tevent.handleObj = match.handleObj;\n\n\t\tret = match.handleObj.origHandler.apply( match.elem, arguments );\n\n\t\tif ( ret === false || event.isPropagationStopped() ) {\n\t\t\tmaxLevel = match.level;\n\n\t\t\tif ( ret === false ) {\n\t\t\t\tstop = false;\n\t\t\t}\n\t\t\tif ( event.isImmediatePropagationStopped() ) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn stop;\n}\n\nfunction liveConvert( type, selector ) {\n\treturn (type && type !== \"*\" ? type + \".\" : \"\") + selector.replace(rperiod, \"`\").replace(rspace, \"&\");\n}\n\njQuery.each( (\"blur focus focusin focusout load resize scroll unload click dblclick \" +\n\t\"mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave \" +\n\t\"change select submit keydown keypress keyup error\").split(\" \"), function( i, name ) {\n\n\t// Handle event binding\n\tjQuery.fn[ name ] = function( data, fn ) {\n\t\tif ( fn == null ) {\n\t\t\tfn = data;\n\t\t\tdata = null;\n\t\t}\n\n\t\treturn arguments.length > 0 ?\n\t\t\tthis.bind( name, data, fn ) :\n\t\t\tthis.trigger( name );\n\t};\n\n\tif ( jQuery.attrFn ) {\n\t\tjQuery.attrFn[ name ] = true;\n\t}\n});\n\n\n/*!\n * Sizzle CSS Selector Engine\n *  Copyright 2011, The Dojo Foundation\n *  Released under the MIT, BSD, and GPL Licenses.\n *  More information: http://sizzlejs.com/\n */\n(function(){\n\nvar chunker = /((?:\\((?:\\([^()]+\\)|[^()]+)+\\)|\\[(?:\\[[^\\[\\]]*\\]|['\"][^'\"]*['\"]|[^\\[\\]'\"]+)+\\]|\\\\.|[^ >+~,(\\[\\\\]+)+|[>+~])(\\s*,\\s*)?((?:.|\\r|\\n)*)/g,\n\tdone = 0,\n\ttoString = Object.prototype.toString,\n\thasDuplicate = false,\n\tbaseHasDuplicate = true,\n\trBackslash = /\\\\/g,\n\trNonWord = /\\W/;\n\n// Here we check if the JavaScript engine is using some sort of\n// optimization where it does not always call our comparision\n// function. If that is the case, discard the hasDuplicate value.\n//   Thus far that includes Google Chrome.\n[0, 0].sort(function() {\n\tbaseHasDuplicate = false;\n\treturn 0;\n});\n\nvar Sizzle = function( selector, context, results, seed ) {\n\tresults = results || [];\n\tcontext = context || document;\n\n\tvar origContext = context;\n\n\tif ( context.nodeType !== 1 && context.nodeType !== 9 ) {\n\t\treturn [];\n\t}\n\t\n\tif ( !selector || typeof selector !== \"string\" ) {\n\t\treturn results;\n\t}\n\n\tvar m, set, checkSet, extra, ret, cur, pop, i,\n\t\tprune = true,\n\t\tcontextXML = Sizzle.isXML( context ),\n\t\tparts = [],\n\t\tsoFar = selector;\n\t\n\t// Reset the position of the chunker regexp (start from head)\n\tdo {\n\t\tchunker.exec( \"\" );\n\t\tm = chunker.exec( soFar );\n\n\t\tif ( m ) {\n\t\t\tsoFar = m[3];\n\t\t\n\t\t\tparts.push( m[1] );\n\t\t\n\t\t\tif ( m[2] ) {\n\t\t\t\textra = m[3];\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t} while ( m );\n\n\tif ( parts.length > 1 && origPOS.exec( selector ) ) {\n\n\t\tif ( parts.length === 2 && Expr.relative[ parts[0] ] ) {\n\t\t\tset = posProcess( parts[0] + parts[1], context );\n\n\t\t} else {\n\t\t\tset = Expr.relative[ parts[0] ] ?\n\t\t\t\t[ context ] :\n\t\t\t\tSizzle( parts.shift(), context );\n\n\t\t\twhile ( parts.length ) {\n\t\t\t\tselector = parts.shift();\n\n\t\t\t\tif ( Expr.relative[ selector ] ) {\n\t\t\t\t\tselector += parts.shift();\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tset = posProcess( selector, set );\n\t\t\t}\n\t\t}\n\n\t} else {\n\t\t// Take a shortcut and set the context if the root selector is an ID\n\t\t// (but not if it'll be faster if the inner selector is an ID)\n\t\tif ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML &&\n\t\t\t\tExpr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) {\n\n\t\t\tret = Sizzle.find( parts.shift(), context, contextXML );\n\t\t\tcontext = ret.expr ?\n\t\t\t\tSizzle.filter( ret.expr, ret.set )[0] :\n\t\t\t\tret.set[0];\n\t\t}\n\n\t\tif ( context ) {\n\t\t\tret = seed ?\n\t\t\t\t{ expr: parts.pop(), set: makeArray(seed) } :\n\t\t\t\tSizzle.find( parts.pop(), parts.length === 1 && (parts[0] === \"~\" || parts[0] === \"+\") && context.parentNode ? context.parentNode : context, contextXML );\n\n\t\t\tset = ret.expr ?\n\t\t\t\tSizzle.filter( ret.expr, ret.set ) :\n\t\t\t\tret.set;\n\n\t\t\tif ( parts.length > 0 ) {\n\t\t\t\tcheckSet = makeArray( set );\n\n\t\t\t} else {\n\t\t\t\tprune = false;\n\t\t\t}\n\n\t\t\twhile ( parts.length ) {\n\t\t\t\tcur = parts.pop();\n\t\t\t\tpop = cur;\n\n\t\t\t\tif ( !Expr.relative[ cur ] ) {\n\t\t\t\t\tcur = \"\";\n\t\t\t\t} else {\n\t\t\t\t\tpop = parts.pop();\n\t\t\t\t}\n\n\t\t\t\tif ( pop == null ) {\n\t\t\t\t\tpop = context;\n\t\t\t\t}\n\n\t\t\t\tExpr.relative[ cur ]( checkSet, pop, contextXML );\n\t\t\t}\n\n\t\t} else {\n\t\t\tcheckSet = parts = [];\n\t\t}\n\t}\n\n\tif ( !checkSet ) {\n\t\tcheckSet = set;\n\t}\n\n\tif ( !checkSet ) {\n\t\tSizzle.error( cur || selector );\n\t}\n\n\tif ( toString.call(checkSet) === \"[object Array]\" ) {\n\t\tif ( !prune ) {\n\t\t\tresults.push.apply( results, checkSet );\n\n\t\t} else if ( context && context.nodeType === 1 ) {\n\t\t\tfor ( i = 0; checkSet[i] != null; i++ ) {\n\t\t\t\tif ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && Sizzle.contains(context, checkSet[i])) ) {\n\t\t\t\t\tresults.push( set[i] );\n\t\t\t\t}\n\t\t\t}\n\n\t\t} else {\n\t\t\tfor ( i = 0; checkSet[i] != null; i++ ) {\n\t\t\t\tif ( checkSet[i] && checkSet[i].nodeType === 1 ) {\n\t\t\t\t\tresults.push( set[i] );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t} else {\n\t\tmakeArray( checkSet, results );\n\t}\n\n\tif ( extra ) {\n\t\tSizzle( extra, origContext, results, seed );\n\t\tSizzle.uniqueSort( results );\n\t}\n\n\treturn results;\n};\n\nSizzle.uniqueSort = function( results ) {\n\tif ( sortOrder ) {\n\t\thasDuplicate = baseHasDuplicate;\n\t\tresults.sort( sortOrder );\n\n\t\tif ( hasDuplicate ) {\n\t\t\tfor ( var i = 1; i < results.length; i++ ) {\n\t\t\t\tif ( results[i] === results[ i - 1 ] ) {\n\t\t\t\t\tresults.splice( i--, 1 );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn results;\n};\n\nSizzle.matches = function( expr, set ) {\n\treturn Sizzle( expr, null, null, set );\n};\n\nSizzle.matchesSelector = function( node, expr ) {\n\treturn Sizzle( expr, null, null, [node] ).length > 0;\n};\n\nSizzle.find = function( expr, context, isXML ) {\n\tvar set;\n\n\tif ( !expr ) {\n\t\treturn [];\n\t}\n\n\tfor ( var i = 0, l = Expr.order.length; i < l; i++ ) {\n\t\tvar match,\n\t\t\ttype = Expr.order[i];\n\t\t\n\t\tif ( (match = Expr.leftMatch[ type ].exec( expr )) ) {\n\t\t\tvar left = match[1];\n\t\t\tmatch.splice( 1, 1 );\n\n\t\t\tif ( left.substr( left.length - 1 ) !== \"\\\\\" ) {\n\t\t\t\tmatch[1] = (match[1] || \"\").replace( rBackslash, \"\" );\n\t\t\t\tset = Expr.find[ type ]( match, context, isXML );\n\n\t\t\t\tif ( set != null ) {\n\t\t\t\t\texpr = expr.replace( Expr.match[ type ], \"\" );\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tif ( !set ) {\n\t\tset = typeof context.getElementsByTagName !== \"undefined\" ?\n\t\t\tcontext.getElementsByTagName( \"*\" ) :\n\t\t\t[];\n\t}\n\n\treturn { set: set, expr: expr };\n};\n\nSizzle.filter = function( expr, set, inplace, not ) {\n\tvar match, anyFound,\n\t\told = expr,\n\t\tresult = [],\n\t\tcurLoop = set,\n\t\tisXMLFilter = set && set[0] && Sizzle.isXML( set[0] );\n\n\twhile ( expr && set.length ) {\n\t\tfor ( var type in Expr.filter ) {\n\t\t\tif ( (match = Expr.leftMatch[ type ].exec( expr )) != null && match[2] ) {\n\t\t\t\tvar found, item,\n\t\t\t\t\tfilter = Expr.filter[ type ],\n\t\t\t\t\tleft = match[1];\n\n\t\t\t\tanyFound = false;\n\n\t\t\t\tmatch.splice(1,1);\n\n\t\t\t\tif ( left.substr( left.length - 1 ) === \"\\\\\" ) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tif ( curLoop === result ) {\n\t\t\t\t\tresult = [];\n\t\t\t\t}\n\n\t\t\t\tif ( Expr.preFilter[ type ] ) {\n\t\t\t\t\tmatch = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter );\n\n\t\t\t\t\tif ( !match ) {\n\t\t\t\t\t\tanyFound = found = true;\n\n\t\t\t\t\t} else if ( match === true ) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif ( match ) {\n\t\t\t\t\tfor ( var i = 0; (item = curLoop[i]) != null; i++ ) {\n\t\t\t\t\t\tif ( item ) {\n\t\t\t\t\t\t\tfound = filter( item, match, i, curLoop );\n\t\t\t\t\t\t\tvar pass = not ^ !!found;\n\n\t\t\t\t\t\t\tif ( inplace && found != null ) {\n\t\t\t\t\t\t\t\tif ( pass ) {\n\t\t\t\t\t\t\t\t\tanyFound = true;\n\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tcurLoop[i] = false;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t} else if ( pass ) {\n\t\t\t\t\t\t\t\tresult.push( item );\n\t\t\t\t\t\t\t\tanyFound = true;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif ( found !== undefined ) {\n\t\t\t\t\tif ( !inplace ) {\n\t\t\t\t\t\tcurLoop = result;\n\t\t\t\t\t}\n\n\t\t\t\t\texpr = expr.replace( Expr.match[ type ], \"\" );\n\n\t\t\t\t\tif ( !anyFound ) {\n\t\t\t\t\t\treturn [];\n\t\t\t\t\t}\n\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Improper expression\n\t\tif ( expr === old ) {\n\t\t\tif ( anyFound == null ) {\n\t\t\t\tSizzle.error( expr );\n\n\t\t\t} else {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\told = expr;\n\t}\n\n\treturn curLoop;\n};\n\nSizzle.error = function( msg ) {\n\tthrow \"Syntax error, unrecognized expression: \" + msg;\n};\n\nvar Expr = Sizzle.selectors = {\n\torder: [ \"ID\", \"NAME\", \"TAG\" ],\n\n\tmatch: {\n\t\tID: /#((?:[\\w\\u00c0-\\uFFFF\\-]|\\\\.)+)/,\n\t\tCLASS: /\\.((?:[\\w\\u00c0-\\uFFFF\\-]|\\\\.)+)/,\n\t\tNAME: /\\[name=['\"]*((?:[\\w\\u00c0-\\uFFFF\\-]|\\\\.)+)['\"]*\\]/,\n\t\tATTR: /\\[\\s*((?:[\\w\\u00c0-\\uFFFF\\-]|\\\\.)+)\\s*(?:(\\S?=)\\s*(?:(['\"])(.*?)\\3|(#?(?:[\\w\\u00c0-\\uFFFF\\-]|\\\\.)*)|)|)\\s*\\]/,\n\t\tTAG: /^((?:[\\w\\u00c0-\\uFFFF\\*\\-]|\\\\.)+)/,\n\t\tCHILD: /:(only|nth|last|first)-child(?:\\(\\s*(even|odd|(?:[+\\-]?\\d+|(?:[+\\-]?\\d*)?n\\s*(?:[+\\-]\\s*\\d+)?))\\s*\\))?/,\n\t\tPOS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\\((\\d*)\\))?(?=[^\\-]|$)/,\n\t\tPSEUDO: /:((?:[\\w\\u00c0-\\uFFFF\\-]|\\\\.)+)(?:\\((['\"]?)((?:\\([^\\)]+\\)|[^\\(\\)]*)+)\\2\\))?/\n\t},\n\n\tleftMatch: {},\n\n\tattrMap: {\n\t\t\"class\": \"className\",\n\t\t\"for\": \"htmlFor\"\n\t},\n\n\tattrHandle: {\n\t\thref: function( elem ) {\n\t\t\treturn elem.getAttribute( \"href\" );\n\t\t},\n\t\ttype: function( elem ) {\n\t\t\treturn elem.getAttribute( \"type\" );\n\t\t}\n\t},\n\n\trelative: {\n\t\t\"+\": function(checkSet, part){\n\t\t\tvar isPartStr = typeof part === \"string\",\n\t\t\t\tisTag = isPartStr && !rNonWord.test( part ),\n\t\t\t\tisPartStrNotTag = isPartStr && !isTag;\n\n\t\t\tif ( isTag ) {\n\t\t\t\tpart = part.toLowerCase();\n\t\t\t}\n\n\t\t\tfor ( var i = 0, l = checkSet.length, elem; i < l; i++ ) {\n\t\t\t\tif ( (elem = checkSet[i]) ) {\n\t\t\t\t\twhile ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {}\n\n\t\t\t\t\tcheckSet[i] = isPartStrNotTag || elem && elem.nodeName.toLowerCase() === part ?\n\t\t\t\t\t\telem || false :\n\t\t\t\t\t\telem === part;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif ( isPartStrNotTag ) {\n\t\t\t\tSizzle.filter( part, checkSet, true );\n\t\t\t}\n\t\t},\n\n\t\t\">\": function( checkSet, part ) {\n\t\t\tvar elem,\n\t\t\t\tisPartStr = typeof part === \"string\",\n\t\t\t\ti = 0,\n\t\t\t\tl = checkSet.length;\n\n\t\t\tif ( isPartStr && !rNonWord.test( part ) ) {\n\t\t\t\tpart = part.toLowerCase();\n\n\t\t\t\tfor ( ; i < l; i++ ) {\n\t\t\t\t\telem = checkSet[i];\n\n\t\t\t\t\tif ( elem ) {\n\t\t\t\t\t\tvar parent = elem.parentNode;\n\t\t\t\t\t\tcheckSet[i] = parent.nodeName.toLowerCase() === part ? parent : false;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t} else {\n\t\t\t\tfor ( ; i < l; i++ ) {\n\t\t\t\t\telem = checkSet[i];\n\n\t\t\t\t\tif ( elem ) {\n\t\t\t\t\t\tcheckSet[i] = isPartStr ?\n\t\t\t\t\t\t\telem.parentNode :\n\t\t\t\t\t\t\telem.parentNode === part;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif ( isPartStr ) {\n\t\t\t\t\tSizzle.filter( part, checkSet, true );\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\t\"\": function(checkSet, part, isXML){\n\t\t\tvar nodeCheck,\n\t\t\t\tdoneName = done++,\n\t\t\t\tcheckFn = dirCheck;\n\n\t\t\tif ( typeof part === \"string\" && !rNonWord.test( part ) ) {\n\t\t\t\tpart = part.toLowerCase();\n\t\t\t\tnodeCheck = part;\n\t\t\t\tcheckFn = dirNodeCheck;\n\t\t\t}\n\n\t\t\tcheckFn( \"parentNode\", part, doneName, checkSet, nodeCheck, isXML );\n\t\t},\n\n\t\t\"~\": function( checkSet, part, isXML ) {\n\t\t\tvar nodeCheck,\n\t\t\t\tdoneName = done++,\n\t\t\t\tcheckFn = dirCheck;\n\n\t\t\tif ( typeof part === \"string\" && !rNonWord.test( part ) ) {\n\t\t\t\tpart = part.toLowerCase();\n\t\t\t\tnodeCheck = part;\n\t\t\t\tcheckFn = dirNodeCheck;\n\t\t\t}\n\n\t\t\tcheckFn( \"previousSibling\", part, doneName, checkSet, nodeCheck, isXML );\n\t\t}\n\t},\n\n\tfind: {\n\t\tID: function( match, context, isXML ) {\n\t\t\tif ( typeof context.getElementById !== \"undefined\" && !isXML ) {\n\t\t\t\tvar m = context.getElementById(match[1]);\n\t\t\t\t// Check parentNode to catch when Blackberry 4.6 returns\n\t\t\t\t// nodes that are no longer in the document #6963\n\t\t\t\treturn m && m.parentNode ? [m] : [];\n\t\t\t}\n\t\t},\n\n\t\tNAME: function( match, context ) {\n\t\t\tif ( typeof context.getElementsByName !== \"undefined\" ) {\n\t\t\t\tvar ret = [],\n\t\t\t\t\tresults = context.getElementsByName( match[1] );\n\n\t\t\t\tfor ( var i = 0, l = results.length; i < l; i++ ) {\n\t\t\t\t\tif ( results[i].getAttribute(\"name\") === match[1] ) {\n\t\t\t\t\t\tret.push( results[i] );\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn ret.length === 0 ? null : ret;\n\t\t\t}\n\t\t},\n\n\t\tTAG: function( match, context ) {\n\t\t\tif ( typeof context.getElementsByTagName !== \"undefined\" ) {\n\t\t\t\treturn context.getElementsByTagName( match[1] );\n\t\t\t}\n\t\t}\n\t},\n\tpreFilter: {\n\t\tCLASS: function( match, curLoop, inplace, result, not, isXML ) {\n\t\t\tmatch = \" \" + match[1].replace( rBackslash, \"\" ) + \" \";\n\n\t\t\tif ( isXML ) {\n\t\t\t\treturn match;\n\t\t\t}\n\n\t\t\tfor ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) {\n\t\t\t\tif ( elem ) {\n\t\t\t\t\tif ( not ^ (elem.className && (\" \" + elem.className + \" \").replace(/[\\t\\n\\r]/g, \" \").indexOf(match) >= 0) ) {\n\t\t\t\t\t\tif ( !inplace ) {\n\t\t\t\t\t\t\tresult.push( elem );\n\t\t\t\t\t\t}\n\n\t\t\t\t\t} else if ( inplace ) {\n\t\t\t\t\t\tcurLoop[i] = false;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn false;\n\t\t},\n\n\t\tID: function( match ) {\n\t\t\treturn match[1].replace( rBackslash, \"\" );\n\t\t},\n\n\t\tTAG: function( match, curLoop ) {\n\t\t\treturn match[1].replace( rBackslash, \"\" ).toLowerCase();\n\t\t},\n\n\t\tCHILD: function( match ) {\n\t\t\tif ( match[1] === \"nth\" ) {\n\t\t\t\tif ( !match[2] ) {\n\t\t\t\t\tSizzle.error( match[0] );\n\t\t\t\t}\n\n\t\t\t\tmatch[2] = match[2].replace(/^\\+|\\s*/g, '');\n\n\t\t\t\t// parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6'\n\t\t\t\tvar test = /(-?)(\\d*)(?:n([+\\-]?\\d*))?/.exec(\n\t\t\t\t\tmatch[2] === \"even\" && \"2n\" || match[2] === \"odd\" && \"2n+1\" ||\n\t\t\t\t\t!/\\D/.test( match[2] ) && \"0n+\" + match[2] || match[2]);\n\n\t\t\t\t// calculate the numbers (first)n+(last) including if they are negative\n\t\t\t\tmatch[2] = (test[1] + (test[2] || 1)) - 0;\n\t\t\t\tmatch[3] = test[3] - 0;\n\t\t\t}\n\t\t\telse if ( match[2] ) {\n\t\t\t\tSizzle.error( match[0] );\n\t\t\t}\n\n\t\t\t// TODO: Move to normal caching system\n\t\t\tmatch[0] = done++;\n\n\t\t\treturn match;\n\t\t},\n\n\t\tATTR: function( match, curLoop, inplace, result, not, isXML ) {\n\t\t\tvar name = match[1] = match[1].replace( rBackslash, \"\" );\n\t\t\t\n\t\t\tif ( !isXML && Expr.attrMap[name] ) {\n\t\t\t\tmatch[1] = Expr.attrMap[name];\n\t\t\t}\n\n\t\t\t// Handle if an un-quoted value was used\n\t\t\tmatch[4] = ( match[4] || match[5] || \"\" ).replace( rBackslash, \"\" );\n\n\t\t\tif ( match[2] === \"~=\" ) {\n\t\t\t\tmatch[4] = \" \" + match[4] + \" \";\n\t\t\t}\n\n\t\t\treturn match;\n\t\t},\n\n\t\tPSEUDO: function( match, curLoop, inplace, result, not ) {\n\t\t\tif ( match[1] === \"not\" ) {\n\t\t\t\t// If we're dealing with a complex expression, or a simple one\n\t\t\t\tif ( ( chunker.exec(match[3]) || \"\" ).length > 1 || /^\\w/.test(match[3]) ) {\n\t\t\t\t\tmatch[3] = Sizzle(match[3], null, null, curLoop);\n\n\t\t\t\t} else {\n\t\t\t\t\tvar ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not);\n\n\t\t\t\t\tif ( !inplace ) {\n\t\t\t\t\t\tresult.push.apply( result, ret );\n\t\t\t\t\t}\n\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\n\t\t\t} else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\t\n\t\t\treturn match;\n\t\t},\n\n\t\tPOS: function( match ) {\n\t\t\tmatch.unshift( true );\n\n\t\t\treturn match;\n\t\t}\n\t},\n\t\n\tfilters: {\n\t\tenabled: function( elem ) {\n\t\t\treturn elem.disabled === false && elem.type !== \"hidden\";\n\t\t},\n\n\t\tdisabled: function( elem ) {\n\t\t\treturn elem.disabled === true;\n\t\t},\n\n\t\tchecked: function( elem ) {\n\t\t\treturn elem.checked === true;\n\t\t},\n\t\t\n\t\tselected: function( elem ) {\n\t\t\t// Accessing this property makes selected-by-default\n\t\t\t// options in Safari work properly\n\t\t\tif ( elem.parentNode ) {\n\t\t\t\telem.parentNode.selectedIndex;\n\t\t\t}\n\t\t\t\n\t\t\treturn elem.selected === true;\n\t\t},\n\n\t\tparent: function( elem ) {\n\t\t\treturn !!elem.firstChild;\n\t\t},\n\n\t\tempty: function( elem ) {\n\t\t\treturn !elem.firstChild;\n\t\t},\n\n\t\thas: function( elem, i, match ) {\n\t\t\treturn !!Sizzle( match[3], elem ).length;\n\t\t},\n\n\t\theader: function( elem ) {\n\t\t\treturn (/h\\d/i).test( elem.nodeName );\n\t\t},\n\n\t\ttext: function( elem ) {\n\t\t\t// IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc) \n\t\t\t// use getAttribute instead to test this case\n\t\t\treturn \"text\" === elem.getAttribute( 'type' );\n\t\t},\n\t\tradio: function( elem ) {\n\t\t\treturn \"radio\" === elem.type;\n\t\t},\n\n\t\tcheckbox: function( elem ) {\n\t\t\treturn \"checkbox\" === elem.type;\n\t\t},\n\n\t\tfile: function( elem ) {\n\t\t\treturn \"file\" === elem.type;\n\t\t},\n\t\tpassword: function( elem ) {\n\t\t\treturn \"password\" === elem.type;\n\t\t},\n\n\t\tsubmit: function( elem ) {\n\t\t\treturn \"submit\" === elem.type;\n\t\t},\n\n\t\timage: function( elem ) {\n\t\t\treturn \"image\" === elem.type;\n\t\t},\n\n\t\treset: function( elem ) {\n\t\t\treturn \"reset\" === elem.type;\n\t\t},\n\n\t\tbutton: function( elem ) {\n\t\t\treturn \"button\" === elem.type || elem.nodeName.toLowerCase() === \"button\";\n\t\t},\n\n\t\tinput: function( elem ) {\n\t\t\treturn (/input|select|textarea|button/i).test( elem.nodeName );\n\t\t}\n\t},\n\tsetFilters: {\n\t\tfirst: function( elem, i ) {\n\t\t\treturn i === 0;\n\t\t},\n\n\t\tlast: function( elem, i, match, array ) {\n\t\t\treturn i === array.length - 1;\n\t\t},\n\n\t\teven: function( elem, i ) {\n\t\t\treturn i % 2 === 0;\n\t\t},\n\n\t\todd: function( elem, i ) {\n\t\t\treturn i % 2 === 1;\n\t\t},\n\n\t\tlt: function( elem, i, match ) {\n\t\t\treturn i < match[3] - 0;\n\t\t},\n\n\t\tgt: function( elem, i, match ) {\n\t\t\treturn i > match[3] - 0;\n\t\t},\n\n\t\tnth: function( elem, i, match ) {\n\t\t\treturn match[3] - 0 === i;\n\t\t},\n\n\t\teq: function( elem, i, match ) {\n\t\t\treturn match[3] - 0 === i;\n\t\t}\n\t},\n\tfilter: {\n\t\tPSEUDO: function( elem, match, i, array ) {\n\t\t\tvar name = match[1],\n\t\t\t\tfilter = Expr.filters[ name ];\n\n\t\t\tif ( filter ) {\n\t\t\t\treturn filter( elem, i, match, array );\n\n\t\t\t} else if ( name === \"contains\" ) {\n\t\t\t\treturn (elem.textContent || elem.innerText || Sizzle.getText([ elem ]) || \"\").indexOf(match[3]) >= 0;\n\n\t\t\t} else if ( name === \"not\" ) {\n\t\t\t\tvar not = match[3];\n\n\t\t\t\tfor ( var j = 0, l = not.length; j < l; j++ ) {\n\t\t\t\t\tif ( not[j] === elem ) {\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn true;\n\n\t\t\t} else {\n\t\t\t\tSizzle.error( name );\n\t\t\t}\n\t\t},\n\n\t\tCHILD: function( elem, match ) {\n\t\t\tvar type = match[1],\n\t\t\t\tnode = elem;\n\n\t\t\tswitch ( type ) {\n\t\t\t\tcase \"only\":\n\t\t\t\tcase \"first\":\n\t\t\t\t\twhile ( (node = node.previousSibling) )\t {\n\t\t\t\t\t\tif ( node.nodeType === 1 ) { \n\t\t\t\t\t\t\treturn false; \n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif ( type === \"first\" ) { \n\t\t\t\t\t\treturn true; \n\t\t\t\t\t}\n\n\t\t\t\t\tnode = elem;\n\n\t\t\t\tcase \"last\":\n\t\t\t\t\twhile ( (node = node.nextSibling) )\t {\n\t\t\t\t\t\tif ( node.nodeType === 1 ) { \n\t\t\t\t\t\t\treturn false; \n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\treturn true;\n\n\t\t\t\tcase \"nth\":\n\t\t\t\t\tvar first = match[2],\n\t\t\t\t\t\tlast = match[3];\n\n\t\t\t\t\tif ( first === 1 && last === 0 ) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tvar doneName = match[0],\n\t\t\t\t\t\tparent = elem.parentNode;\n\t\n\t\t\t\t\tif ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) {\n\t\t\t\t\t\tvar count = 0;\n\t\t\t\t\t\t\n\t\t\t\t\t\tfor ( node = parent.firstChild; node; node = node.nextSibling ) {\n\t\t\t\t\t\t\tif ( node.nodeType === 1 ) {\n\t\t\t\t\t\t\t\tnode.nodeIndex = ++count;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} \n\n\t\t\t\t\t\tparent.sizcache = doneName;\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\tvar diff = elem.nodeIndex - last;\n\n\t\t\t\t\tif ( first === 0 ) {\n\t\t\t\t\t\treturn diff === 0;\n\n\t\t\t\t\t} else {\n\t\t\t\t\t\treturn ( diff % first === 0 && diff / first >= 0 );\n\t\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\tID: function( elem, match ) {\n\t\t\treturn elem.nodeType === 1 && elem.getAttribute(\"id\") === match;\n\t\t},\n\n\t\tTAG: function( elem, match ) {\n\t\t\treturn (match === \"*\" && elem.nodeType === 1) || elem.nodeName.toLowerCase() === match;\n\t\t},\n\t\t\n\t\tCLASS: function( elem, match ) {\n\t\t\treturn (\" \" + (elem.className || elem.getAttribute(\"class\")) + \" \")\n\t\t\t\t.indexOf( match ) > -1;\n\t\t},\n\n\t\tATTR: function( elem, match ) {\n\t\t\tvar name = match[1],\n\t\t\t\tresult = Expr.attrHandle[ name ] ?\n\t\t\t\t\tExpr.attrHandle[ name ]( elem ) :\n\t\t\t\t\telem[ name ] != null ?\n\t\t\t\t\t\telem[ name ] :\n\t\t\t\t\t\telem.getAttribute( name ),\n\t\t\t\tvalue = result + \"\",\n\t\t\t\ttype = match[2],\n\t\t\t\tcheck = match[4];\n\n\t\t\treturn result == null ?\n\t\t\t\ttype === \"!=\" :\n\t\t\t\ttype === \"=\" ?\n\t\t\t\tvalue === check :\n\t\t\t\ttype === \"*=\" ?\n\t\t\t\tvalue.indexOf(check) >= 0 :\n\t\t\t\ttype === \"~=\" ?\n\t\t\t\t(\" \" + value + \" \").indexOf(check) >= 0 :\n\t\t\t\t!check ?\n\t\t\t\tvalue && result !== false :\n\t\t\t\ttype === \"!=\" ?\n\t\t\t\tvalue !== check :\n\t\t\t\ttype === \"^=\" ?\n\t\t\t\tvalue.indexOf(check) === 0 :\n\t\t\t\ttype === \"$=\" ?\n\t\t\t\tvalue.substr(value.length - check.length) === check :\n\t\t\t\ttype === \"|=\" ?\n\t\t\t\tvalue === check || value.substr(0, check.length + 1) === check + \"-\" :\n\t\t\t\tfalse;\n\t\t},\n\n\t\tPOS: function( elem, match, i, array ) {\n\t\t\tvar name = match[2],\n\t\t\t\tfilter = Expr.setFilters[ name ];\n\n\t\t\tif ( filter ) {\n\t\t\t\treturn filter( elem, i, match, array );\n\t\t\t}\n\t\t}\n\t}\n};\n\nvar origPOS = Expr.match.POS,\n\tfescape = function(all, num){\n\t\treturn \"\\\\\" + (num - 0 + 1);\n\t};\n\nfor ( var type in Expr.match ) {\n\tExpr.match[ type ] = new RegExp( Expr.match[ type ].source + (/(?![^\\[]*\\])(?![^\\(]*\\))/.source) );\n\tExpr.leftMatch[ type ] = new RegExp( /(^(?:.|\\r|\\n)*?)/.source + Expr.match[ type ].source.replace(/\\\\(\\d+)/g, fescape) );\n}\n\nvar makeArray = function( array, results ) {\n\tarray = Array.prototype.slice.call( array, 0 );\n\n\tif ( results ) {\n\t\tresults.push.apply( results, array );\n\t\treturn results;\n\t}\n\t\n\treturn array;\n};\n\n// Perform a simple check to determine if the browser is capable of\n// converting a NodeList to an array using builtin methods.\n// Also verifies that the returned array holds DOM nodes\n// (which is not the case in the Blackberry browser)\ntry {\n\tArray.prototype.slice.call( document.documentElement.childNodes, 0 )[0].nodeType;\n\n// Provide a fallback method if it does not work\n} catch( e ) {\n\tmakeArray = function( array, results ) {\n\t\tvar i = 0,\n\t\t\tret = results || [];\n\n\t\tif ( toString.call(array) === \"[object Array]\" ) {\n\t\t\tArray.prototype.push.apply( ret, array );\n\n\t\t} else {\n\t\t\tif ( typeof array.length === \"number\" ) {\n\t\t\t\tfor ( var l = array.length; i < l; i++ ) {\n\t\t\t\t\tret.push( array[i] );\n\t\t\t\t}\n\n\t\t\t} else {\n\t\t\t\tfor ( ; array[i]; i++ ) {\n\t\t\t\t\tret.push( array[i] );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn ret;\n\t};\n}\n\nvar sortOrder, siblingCheck;\n\nif ( document.documentElement.compareDocumentPosition ) {\n\tsortOrder = function( a, b ) {\n\t\tif ( a === b ) {\n\t\t\thasDuplicate = true;\n\t\t\treturn 0;\n\t\t}\n\n\t\tif ( !a.compareDocumentPosition || !b.compareDocumentPosition ) {\n\t\t\treturn a.compareDocumentPosition ? -1 : 1;\n\t\t}\n\n\t\treturn a.compareDocumentPosition(b) & 4 ? -1 : 1;\n\t};\n\n} else {\n\tsortOrder = function( a, b ) {\n\t\tvar al, bl,\n\t\t\tap = [],\n\t\t\tbp = [],\n\t\t\taup = a.parentNode,\n\t\t\tbup = b.parentNode,\n\t\t\tcur = aup;\n\n\t\t// The nodes are identical, we can exit early\n\t\tif ( a === b ) {\n\t\t\thasDuplicate = true;\n\t\t\treturn 0;\n\n\t\t// If the nodes are siblings (or identical) we can do a quick check\n\t\t} else if ( aup === bup ) {\n\t\t\treturn siblingCheck( a, b );\n\n\t\t// If no parents were found then the nodes are disconnected\n\t\t} else if ( !aup ) {\n\t\t\treturn -1;\n\n\t\t} else if ( !bup ) {\n\t\t\treturn 1;\n\t\t}\n\n\t\t// Otherwise they're somewhere else in the tree so we need\n\t\t// to build up a full list of the parentNodes for comparison\n\t\twhile ( cur ) {\n\t\t\tap.unshift( cur );\n\t\t\tcur = cur.parentNode;\n\t\t}\n\n\t\tcur = bup;\n\n\t\twhile ( cur ) {\n\t\t\tbp.unshift( cur );\n\t\t\tcur = cur.parentNode;\n\t\t}\n\n\t\tal = ap.length;\n\t\tbl = bp.length;\n\n\t\t// Start walking down the tree looking for a discrepancy\n\t\tfor ( var i = 0; i < al && i < bl; i++ ) {\n\t\t\tif ( ap[i] !== bp[i] ) {\n\t\t\t\treturn siblingCheck( ap[i], bp[i] );\n\t\t\t}\n\t\t}\n\n\t\t// We ended someplace up the tree so do a sibling check\n\t\treturn i === al ?\n\t\t\tsiblingCheck( a, bp[i], -1 ) :\n\t\t\tsiblingCheck( ap[i], b, 1 );\n\t};\n\n\tsiblingCheck = function( a, b, ret ) {\n\t\tif ( a === b ) {\n\t\t\treturn ret;\n\t\t}\n\n\t\tvar cur = a.nextSibling;\n\n\t\twhile ( cur ) {\n\t\t\tif ( cur === b ) {\n\t\t\t\treturn -1;\n\t\t\t}\n\n\t\t\tcur = cur.nextSibling;\n\t\t}\n\n\t\treturn 1;\n\t};\n}\n\n// Utility function for retreiving the text value of an array of DOM nodes\nSizzle.getText = function( elems ) {\n\tvar ret = \"\", elem;\n\n\tfor ( var i = 0; elems[i]; i++ ) {\n\t\telem = elems[i];\n\n\t\t// Get the text from text nodes and CDATA nodes\n\t\tif ( elem.nodeType === 3 || elem.nodeType === 4 ) {\n\t\t\tret += elem.nodeValue;\n\n\t\t// Traverse everything else, except comment nodes\n\t\t} else if ( elem.nodeType !== 8 ) {\n\t\t\tret += Sizzle.getText( elem.childNodes );\n\t\t}\n\t}\n\n\treturn ret;\n};\n\n// Check to see if the browser returns elements by name when\n// querying by getElementById (and provide a workaround)\n(function(){\n\t// We're going to inject a fake input element with a specified name\n\tvar form = document.createElement(\"div\"),\n\t\tid = \"script\" + (new Date()).getTime(),\n\t\troot = document.documentElement;\n\n\tform.innerHTML = \"<a name='\" + id + \"'/>\";\n\n\t// Inject it into the root element, check its status, and remove it quickly\n\troot.insertBefore( form, root.firstChild );\n\n\t// The workaround has to do additional checks after a getElementById\n\t// Which slows things down for other browsers (hence the branching)\n\tif ( document.getElementById( id ) ) {\n\t\tExpr.find.ID = function( match, context, isXML ) {\n\t\t\tif ( typeof context.getElementById !== \"undefined\" && !isXML ) {\n\t\t\t\tvar m = context.getElementById(match[1]);\n\n\t\t\t\treturn m ?\n\t\t\t\t\tm.id === match[1] || typeof m.getAttributeNode !== \"undefined\" && m.getAttributeNode(\"id\").nodeValue === match[1] ?\n\t\t\t\t\t\t[m] :\n\t\t\t\t\t\tundefined :\n\t\t\t\t\t[];\n\t\t\t}\n\t\t};\n\n\t\tExpr.filter.ID = function( elem, match ) {\n\t\t\tvar node = typeof elem.getAttributeNode !== \"undefined\" && elem.getAttributeNode(\"id\");\n\n\t\t\treturn elem.nodeType === 1 && node && node.nodeValue === match;\n\t\t};\n\t}\n\n\troot.removeChild( form );\n\n\t// release memory in IE\n\troot = form = null;\n})();\n\n(function(){\n\t// Check to see if the browser returns only elements\n\t// when doing getElementsByTagName(\"*\")\n\n\t// Create a fake element\n\tvar div = document.createElement(\"div\");\n\tdiv.appendChild( document.createComment(\"\") );\n\n\t// Make sure no comments are found\n\tif ( div.getElementsByTagName(\"*\").length > 0 ) {\n\t\tExpr.find.TAG = function( match, context ) {\n\t\t\tvar results = context.getElementsByTagName( match[1] );\n\n\t\t\t// Filter out possible comments\n\t\t\tif ( match[1] === \"*\" ) {\n\t\t\t\tvar tmp = [];\n\n\t\t\t\tfor ( var i = 0; results[i]; i++ ) {\n\t\t\t\t\tif ( results[i].nodeType === 1 ) {\n\t\t\t\t\t\ttmp.push( results[i] );\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tresults = tmp;\n\t\t\t}\n\n\t\t\treturn results;\n\t\t};\n\t}\n\n\t// Check to see if an attribute returns normalized href attributes\n\tdiv.innerHTML = \"<a href='#'></a>\";\n\n\tif ( div.firstChild && typeof div.firstChild.getAttribute !== \"undefined\" &&\n\t\t\tdiv.firstChild.getAttribute(\"href\") !== \"#\" ) {\n\n\t\tExpr.attrHandle.href = function( elem ) {\n\t\t\treturn elem.getAttribute( \"href\", 2 );\n\t\t};\n\t}\n\n\t// release memory in IE\n\tdiv = null;\n})();\n\nif ( document.querySelectorAll ) {\n\t(function(){\n\t\tvar oldSizzle = Sizzle,\n\t\t\tdiv = document.createElement(\"div\"),\n\t\t\tid = \"__sizzle__\";\n\n\t\tdiv.innerHTML = \"<p class='TEST'></p>\";\n\n\t\t// Safari can't handle uppercase or unicode characters when\n\t\t// in quirks mode.\n\t\tif ( div.querySelectorAll && div.querySelectorAll(\".TEST\").length === 0 ) {\n\t\t\treturn;\n\t\t}\n\t\n\t\tSizzle = function( query, context, extra, seed ) {\n\t\t\tcontext = context || document;\n\n\t\t\t// Only use querySelectorAll on non-XML documents\n\t\t\t// (ID selectors don't work in non-HTML documents)\n\t\t\tif ( !seed && !Sizzle.isXML(context) ) {\n\t\t\t\t// See if we find a selector to speed up\n\t\t\t\tvar match = /^(\\w+$)|^\\.([\\w\\-]+$)|^#([\\w\\-]+$)/.exec( query );\n\t\t\t\t\n\t\t\t\tif ( match && (context.nodeType === 1 || context.nodeType === 9) ) {\n\t\t\t\t\t// Speed-up: Sizzle(\"TAG\")\n\t\t\t\t\tif ( match[1] ) {\n\t\t\t\t\t\treturn makeArray( context.getElementsByTagName( query ), extra );\n\t\t\t\t\t\n\t\t\t\t\t// Speed-up: Sizzle(\".CLASS\")\n\t\t\t\t\t} else if ( match[2] && Expr.find.CLASS && context.getElementsByClassName ) {\n\t\t\t\t\t\treturn makeArray( context.getElementsByClassName( match[2] ), extra );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tif ( context.nodeType === 9 ) {\n\t\t\t\t\t// Speed-up: Sizzle(\"body\")\n\t\t\t\t\t// The body element only exists once, optimize finding it\n\t\t\t\t\tif ( query === \"body\" && context.body ) {\n\t\t\t\t\t\treturn makeArray( [ context.body ], extra );\n\t\t\t\t\t\t\n\t\t\t\t\t// Speed-up: Sizzle(\"#ID\")\n\t\t\t\t\t} else if ( match && match[3] ) {\n\t\t\t\t\t\tvar elem = context.getElementById( match[3] );\n\n\t\t\t\t\t\t// Check parentNode to catch when Blackberry 4.6 returns\n\t\t\t\t\t\t// nodes that are no longer in the document #6963\n\t\t\t\t\t\tif ( elem && elem.parentNode ) {\n\t\t\t\t\t\t\t// Handle the case where IE and Opera return items\n\t\t\t\t\t\t\t// by name instead of ID\n\t\t\t\t\t\t\tif ( elem.id === match[3] ) {\n\t\t\t\t\t\t\t\treturn makeArray( [ elem ], extra );\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\treturn makeArray( [], extra );\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\ttry {\n\t\t\t\t\t\treturn makeArray( context.querySelectorAll(query), extra );\n\t\t\t\t\t} catch(qsaError) {}\n\n\t\t\t\t// qSA works strangely on Element-rooted queries\n\t\t\t\t// We can work around this by specifying an extra ID on the root\n\t\t\t\t// and working up from there (Thanks to Andrew Dupont for the technique)\n\t\t\t\t// IE 8 doesn't work on object elements\n\t\t\t\t} else if ( context.nodeType === 1 && context.nodeName.toLowerCase() !== \"object\" ) {\n\t\t\t\t\tvar oldContext = context,\n\t\t\t\t\t\told = context.getAttribute( \"id\" ),\n\t\t\t\t\t\tnid = old || id,\n\t\t\t\t\t\thasParent = context.parentNode,\n\t\t\t\t\t\trelativeHierarchySelector = /^\\s*[+~]/.test( query );\n\n\t\t\t\t\tif ( !old ) {\n\t\t\t\t\t\tcontext.setAttribute( \"id\", nid );\n\t\t\t\t\t} else {\n\t\t\t\t\t\tnid = nid.replace( /'/g, \"\\\\$&\" );\n\t\t\t\t\t}\n\t\t\t\t\tif ( relativeHierarchySelector && hasParent ) {\n\t\t\t\t\t\tcontext = context.parentNode;\n\t\t\t\t\t}\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\tif ( !relativeHierarchySelector || hasParent ) {\n\t\t\t\t\t\t\treturn makeArray( context.querySelectorAll( \"[id='\" + nid + \"'] \" + query ), extra );\n\t\t\t\t\t\t}\n\n\t\t\t\t\t} catch(pseudoError) {\n\t\t\t\t\t} finally {\n\t\t\t\t\t\tif ( !old ) {\n\t\t\t\t\t\t\toldContext.removeAttribute( \"id\" );\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\n\t\t\treturn oldSizzle(query, context, extra, seed);\n\t\t};\n\n\t\tfor ( var prop in oldSizzle ) {\n\t\t\tSizzle[ prop ] = oldSizzle[ prop ];\n\t\t}\n\n\t\t// release memory in IE\n\t\tdiv = null;\n\t})();\n}\n\n(function(){\n\tvar html = document.documentElement,\n\t\tmatches = html.matchesSelector || html.mozMatchesSelector || html.webkitMatchesSelector || html.msMatchesSelector,\n\t\tpseudoWorks = false;\n\n\ttry {\n\t\t// This should fail with an exception\n\t\t// Gecko does not error, returns false instead\n\t\tmatches.call( document.documentElement, \"[test!='']:sizzle\" );\n\t\n\t} catch( pseudoError ) {\n\t\tpseudoWorks = true;\n\t}\n\n\tif ( matches ) {\n\t\tSizzle.matchesSelector = function( node, expr ) {\n\t\t\t// Make sure that attribute selectors are quoted\n\t\t\texpr = expr.replace(/\\=\\s*([^'\"\\]]*)\\s*\\]/g, \"='$1']\");\n\n\t\t\tif ( !Sizzle.isXML( node ) ) {\n\t\t\t\ttry { \n\t\t\t\t\tif ( pseudoWorks || !Expr.match.PSEUDO.test( expr ) && !/!=/.test( expr ) ) {\n\t\t\t\t\t\treturn matches.call( node, expr );\n\t\t\t\t\t}\n\t\t\t\t} catch(e) {}\n\t\t\t}\n\n\t\t\treturn Sizzle(expr, null, null, [node]).length > 0;\n\t\t};\n\t}\n})();\n\n(function(){\n\tvar div = document.createElement(\"div\");\n\n\tdiv.innerHTML = \"<div class='test e'></div><div class='test'></div>\";\n\n\t// Opera can't find a second classname (in 9.6)\n\t// Also, make sure that getElementsByClassName actually exists\n\tif ( !div.getElementsByClassName || div.getElementsByClassName(\"e\").length === 0 ) {\n\t\treturn;\n\t}\n\n\t// Safari caches class attributes, doesn't catch changes (in 3.2)\n\tdiv.lastChild.className = \"e\";\n\n\tif ( div.getElementsByClassName(\"e\").length === 1 ) {\n\t\treturn;\n\t}\n\t\n\tExpr.order.splice(1, 0, \"CLASS\");\n\tExpr.find.CLASS = function( match, context, isXML ) {\n\t\tif ( typeof context.getElementsByClassName !== \"undefined\" && !isXML ) {\n\t\t\treturn context.getElementsByClassName(match[1]);\n\t\t}\n\t};\n\n\t// release memory in IE\n\tdiv = null;\n})();\n\nfunction dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {\n\tfor ( var i = 0, l = checkSet.length; i < l; i++ ) {\n\t\tvar elem = checkSet[i];\n\n\t\tif ( elem ) {\n\t\t\tvar match = false;\n\n\t\t\telem = elem[dir];\n\n\t\t\twhile ( elem ) {\n\t\t\t\tif ( elem.sizcache === doneName ) {\n\t\t\t\t\tmatch = checkSet[elem.sizset];\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tif ( elem.nodeType === 1 && !isXML ){\n\t\t\t\t\telem.sizcache = doneName;\n\t\t\t\t\telem.sizset = i;\n\t\t\t\t}\n\n\t\t\t\tif ( elem.nodeName.toLowerCase() === cur ) {\n\t\t\t\t\tmatch = elem;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\telem = elem[dir];\n\t\t\t}\n\n\t\t\tcheckSet[i] = match;\n\t\t}\n\t}\n}\n\nfunction dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {\n\tfor ( var i = 0, l = checkSet.length; i < l; i++ ) {\n\t\tvar elem = checkSet[i];\n\n\t\tif ( elem ) {\n\t\t\tvar match = false;\n\t\t\t\n\t\t\telem = elem[dir];\n\n\t\t\twhile ( elem ) {\n\t\t\t\tif ( elem.sizcache === doneName ) {\n\t\t\t\t\tmatch = checkSet[elem.sizset];\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tif ( elem.nodeType === 1 ) {\n\t\t\t\t\tif ( !isXML ) {\n\t\t\t\t\t\telem.sizcache = doneName;\n\t\t\t\t\t\telem.sizset = i;\n\t\t\t\t\t}\n\n\t\t\t\t\tif ( typeof cur !== \"string\" ) {\n\t\t\t\t\t\tif ( elem === cur ) {\n\t\t\t\t\t\t\tmatch = true;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t} else if ( Sizzle.filter( cur, [elem] ).length > 0 ) {\n\t\t\t\t\t\tmatch = elem;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\telem = elem[dir];\n\t\t\t}\n\n\t\t\tcheckSet[i] = match;\n\t\t}\n\t}\n}\n\nif ( document.documentElement.contains ) {\n\tSizzle.contains = function( a, b ) {\n\t\treturn a !== b && (a.contains ? a.contains(b) : true);\n\t};\n\n} else if ( document.documentElement.compareDocumentPosition ) {\n\tSizzle.contains = function( a, b ) {\n\t\treturn !!(a.compareDocumentPosition(b) & 16);\n\t};\n\n} else {\n\tSizzle.contains = function() {\n\t\treturn false;\n\t};\n}\n\nSizzle.isXML = function( elem ) {\n\t// documentElement is verified for cases where it doesn't yet exist\n\t// (such as loading iframes in IE - #4833) \n\tvar documentElement = (elem ? elem.ownerDocument || elem : 0).documentElement;\n\n\treturn documentElement ? documentElement.nodeName !== \"HTML\" : false;\n};\n\nvar posProcess = function( selector, context ) {\n\tvar match,\n\t\ttmpSet = [],\n\t\tlater = \"\",\n\t\troot = context.nodeType ? [context] : context;\n\n\t// Position selectors must be done after the filter\n\t// And so must :not(positional) so we move all PSEUDOs to the end\n\twhile ( (match = Expr.match.PSEUDO.exec( selector )) ) {\n\t\tlater += match[0];\n\t\tselector = selector.replace( Expr.match.PSEUDO, \"\" );\n\t}\n\n\tselector = Expr.relative[selector] ? selector + \"*\" : selector;\n\n\tfor ( var i = 0, l = root.length; i < l; i++ ) {\n\t\tSizzle( selector, root[i], tmpSet );\n\t}\n\n\treturn Sizzle.filter( later, tmpSet );\n};\n\n// EXPOSE\njQuery.find = Sizzle;\njQuery.expr = Sizzle.selectors;\njQuery.expr[\":\"] = jQuery.expr.filters;\njQuery.unique = Sizzle.uniqueSort;\njQuery.text = Sizzle.getText;\njQuery.isXMLDoc = Sizzle.isXML;\njQuery.contains = Sizzle.contains;\n\n\n})();\n\n\nvar runtil = /Until$/,\n\trparentsprev = /^(?:parents|prevUntil|prevAll)/,\n\t// Note: This RegExp should be improved, or likely pulled from Sizzle\n\trmultiselector = /,/,\n\tisSimple = /^.[^:#\\[\\.,]*$/,\n\tslice = Array.prototype.slice,\n\tPOS = jQuery.expr.match.POS,\n\t// methods guaranteed to produce a unique set when starting from a unique set\n\tguaranteedUnique = {\n\t\tchildren: true,\n\t\tcontents: true,\n\t\tnext: true,\n\t\tprev: true\n\t};\n\njQuery.fn.extend({\n\tfind: function( selector ) {\n\t\tvar ret = this.pushStack( \"\", \"find\", selector ),\n\t\t\tlength = 0;\n\n\t\tfor ( var i = 0, l = this.length; i < l; i++ ) {\n\t\t\tlength = ret.length;\n\t\t\tjQuery.find( selector, this[i], ret );\n\n\t\t\tif ( i > 0 ) {\n\t\t\t\t// Make sure that the results are unique\n\t\t\t\tfor ( var n = length; n < ret.length; n++ ) {\n\t\t\t\t\tfor ( var r = 0; r < length; r++ ) {\n\t\t\t\t\t\tif ( ret[r] === ret[n] ) {\n\t\t\t\t\t\t\tret.splice(n--, 1);\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn ret;\n\t},\n\n\thas: function( target ) {\n\t\tvar targets = jQuery( target );\n\t\treturn this.filter(function() {\n\t\t\tfor ( var i = 0, l = targets.length; i < l; i++ ) {\n\t\t\t\tif ( jQuery.contains( this, targets[i] ) ) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t},\n\n\tnot: function( selector ) {\n\t\treturn this.pushStack( winnow(this, selector, false), \"not\", selector);\n\t},\n\n\tfilter: function( selector ) {\n\t\treturn this.pushStack( winnow(this, selector, true), \"filter\", selector );\n\t},\n\n\tis: function( selector ) {\n\t\treturn !!selector && jQuery.filter( selector, this ).length > 0;\n\t},\n\n\tclosest: function( selectors, context ) {\n\t\tvar ret = [], i, l, cur = this[0];\n\n\t\tif ( jQuery.isArray( selectors ) ) {\n\t\t\tvar match, selector,\n\t\t\t\tmatches = {},\n\t\t\t\tlevel = 1;\n\n\t\t\tif ( cur && selectors.length ) {\n\t\t\t\tfor ( i = 0, l = selectors.length; i < l; i++ ) {\n\t\t\t\t\tselector = selectors[i];\n\n\t\t\t\t\tif ( !matches[selector] ) {\n\t\t\t\t\t\tmatches[selector] = jQuery.expr.match.POS.test( selector ) ?\n\t\t\t\t\t\t\tjQuery( selector, context || this.context ) :\n\t\t\t\t\t\t\tselector;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\twhile ( cur && cur.ownerDocument && cur !== context ) {\n\t\t\t\t\tfor ( selector in matches ) {\n\t\t\t\t\t\tmatch = matches[selector];\n\n\t\t\t\t\t\tif ( match.jquery ? match.index(cur) > -1 : jQuery(cur).is(match) ) {\n\t\t\t\t\t\t\tret.push({ selector: selector, elem: cur, level: level });\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tcur = cur.parentNode;\n\t\t\t\t\tlevel++;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn ret;\n\t\t}\n\n\t\tvar pos = POS.test( selectors ) ?\n\t\t\tjQuery( selectors, context || this.context ) : null;\n\n\t\tfor ( i = 0, l = this.length; i < l; i++ ) {\n\t\t\tcur = this[i];\n\n\t\t\twhile ( cur ) {\n\t\t\t\tif ( pos ? pos.index(cur) > -1 : jQuery.find.matchesSelector(cur, selectors) ) {\n\t\t\t\t\tret.push( cur );\n\t\t\t\t\tbreak;\n\n\t\t\t\t} else {\n\t\t\t\t\tcur = cur.parentNode;\n\t\t\t\t\tif ( !cur || !cur.ownerDocument || cur === context ) {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tret = ret.length > 1 ? jQuery.unique(ret) : ret;\n\n\t\treturn this.pushStack( ret, \"closest\", selectors );\n\t},\n\n\t// Determine the position of an element within\n\t// the matched set of elements\n\tindex: function( elem ) {\n\t\tif ( !elem || typeof elem === \"string\" ) {\n\t\t\treturn jQuery.inArray( this[0],\n\t\t\t\t// If it receives a string, the selector is used\n\t\t\t\t// If it receives nothing, the siblings are used\n\t\t\t\telem ? jQuery( elem ) : this.parent().children() );\n\t\t}\n\t\t// Locate the position of the desired element\n\t\treturn jQuery.inArray(\n\t\t\t// If it receives a jQuery object, the first element is used\n\t\t\telem.jquery ? elem[0] : elem, this );\n\t},\n\n\tadd: function( selector, context ) {\n\t\tvar set = typeof selector === \"string\" ?\n\t\t\t\tjQuery( selector, context ) :\n\t\t\t\tjQuery.makeArray( selector ),\n\t\t\tall = jQuery.merge( this.get(), set );\n\n\t\treturn this.pushStack( isDisconnected( set[0] ) || isDisconnected( all[0] ) ?\n\t\t\tall :\n\t\t\tjQuery.unique( all ) );\n\t},\n\n\tandSelf: function() {\n\t\treturn this.add( this.prevObject );\n\t}\n});\n\n// A painfully simple check to see if an element is disconnected\n// from a document (should be improved, where feasible).\nfunction isDisconnected( node ) {\n\treturn !node || !node.parentNode || node.parentNode.nodeType === 11;\n}\n\njQuery.each({\n\tparent: function( elem ) {\n\t\tvar parent = elem.parentNode;\n\t\treturn parent && parent.nodeType !== 11 ? parent : null;\n\t},\n\tparents: function( elem ) {\n\t\treturn jQuery.dir( elem, \"parentNode\" );\n\t},\n\tparentsUntil: function( elem, i, until ) {\n\t\treturn jQuery.dir( elem, \"parentNode\", until );\n\t},\n\tnext: function( elem ) {\n\t\treturn jQuery.nth( elem, 2, \"nextSibling\" );\n\t},\n\tprev: function( elem ) {\n\t\treturn jQuery.nth( elem, 2, \"previousSibling\" );\n\t},\n\tnextAll: function( elem ) {\n\t\treturn jQuery.dir( elem, \"nextSibling\" );\n\t},\n\tprevAll: function( elem ) {\n\t\treturn jQuery.dir( elem, \"previousSibling\" );\n\t},\n\tnextUntil: function( elem, i, until ) {\n\t\treturn jQuery.dir( elem, \"nextSibling\", until );\n\t},\n\tprevUntil: function( elem, i, until ) {\n\t\treturn jQuery.dir( elem, \"previousSibling\", until );\n\t},\n\tsiblings: function( elem ) {\n\t\treturn jQuery.sibling( elem.parentNode.firstChild, elem );\n\t},\n\tchildren: function( elem ) {\n\t\treturn jQuery.sibling( elem.firstChild );\n\t},\n\tcontents: function( elem ) {\n\t\treturn jQuery.nodeName( elem, \"iframe\" ) ?\n\t\t\telem.contentDocument || elem.contentWindow.document :\n\t\t\tjQuery.makeArray( elem.childNodes );\n\t}\n}, function( name, fn ) {\n\tjQuery.fn[ name ] = function( until, selector ) {\n\t\tvar ret = jQuery.map( this, fn, until ),\n\t\t\t// The variable 'args' was introduced in\n\t\t\t// https://github.com/jquery/jquery/commit/52a0238\n\t\t\t// to work around a bug in Chrome 10 (Dev) and should be removed when the bug is fixed.\n\t\t\t// http://code.google.com/p/v8/issues/detail?id=1050\n\t\t\targs = slice.call(arguments);\n\n\t\tif ( !runtil.test( name ) ) {\n\t\t\tselector = until;\n\t\t}\n\n\t\tif ( selector && typeof selector === \"string\" ) {\n\t\t\tret = jQuery.filter( selector, ret );\n\t\t}\n\n\t\tret = this.length > 1 && !guaranteedUnique[ name ] ? jQuery.unique( ret ) : ret;\n\n\t\tif ( (this.length > 1 || rmultiselector.test( selector )) && rparentsprev.test( name ) ) {\n\t\t\tret = ret.reverse();\n\t\t}\n\n\t\treturn this.pushStack( ret, name, args.join(\",\") );\n\t};\n});\n\njQuery.extend({\n\tfilter: function( expr, elems, not ) {\n\t\tif ( not ) {\n\t\t\texpr = \":not(\" + expr + \")\";\n\t\t}\n\n\t\treturn elems.length === 1 ?\n\t\t\tjQuery.find.matchesSelector(elems[0], expr) ? [ elems[0] ] : [] :\n\t\t\tjQuery.find.matches(expr, elems);\n\t},\n\n\tdir: function( elem, dir, until ) {\n\t\tvar matched = [],\n\t\t\tcur = elem[ dir ];\n\n\t\twhile ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) {\n\t\t\tif ( cur.nodeType === 1 ) {\n\t\t\t\tmatched.push( cur );\n\t\t\t}\n\t\t\tcur = cur[dir];\n\t\t}\n\t\treturn matched;\n\t},\n\n\tnth: function( cur, result, dir, elem ) {\n\t\tresult = result || 1;\n\t\tvar num = 0;\n\n\t\tfor ( ; cur; cur = cur[dir] ) {\n\t\t\tif ( cur.nodeType === 1 && ++num === result ) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\treturn cur;\n\t},\n\n\tsibling: function( n, elem ) {\n\t\tvar r = [];\n\n\t\tfor ( ; n; n = n.nextSibling ) {\n\t\t\tif ( n.nodeType === 1 && n !== elem ) {\n\t\t\t\tr.push( n );\n\t\t\t}\n\t\t}\n\n\t\treturn r;\n\t}\n});\n\n// Implement the identical functionality for filter and not\nfunction winnow( elements, qualifier, keep ) {\n\tif ( jQuery.isFunction( qualifier ) ) {\n\t\treturn jQuery.grep(elements, function( elem, i ) {\n\t\t\tvar retVal = !!qualifier.call( elem, i, elem );\n\t\t\treturn retVal === keep;\n\t\t});\n\n\t} else if ( qualifier.nodeType ) {\n\t\treturn jQuery.grep(elements, function( elem, i ) {\n\t\t\treturn (elem === qualifier) === keep;\n\t\t});\n\n\t} else if ( typeof qualifier === \"string\" ) {\n\t\tvar filtered = jQuery.grep(elements, function( elem ) {\n\t\t\treturn elem.nodeType === 1;\n\t\t});\n\n\t\tif ( isSimple.test( qualifier ) ) {\n\t\t\treturn jQuery.filter(qualifier, filtered, !keep);\n\t\t} else {\n\t\t\tqualifier = jQuery.filter( qualifier, filtered );\n\t\t}\n\t}\n\n\treturn jQuery.grep(elements, function( elem, i ) {\n\t\treturn (jQuery.inArray( elem, qualifier ) >= 0) === keep;\n\t});\n}\n\n\n\n\nvar rinlinejQuery = / jQuery\\d+=\"(?:\\d+|null)\"/g,\n\trleadingWhitespace = /^\\s+/,\n\trxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\\w:]+)[^>]*)\\/>/ig,\n\trtagName = /<([\\w:]+)/,\n\trtbody = /<tbody/i,\n\trhtml = /<|&#?\\w+;/,\n\trnocache = /<(?:script|object|embed|option|style)/i,\n\t// checked=\"checked\" or checked\n\trchecked = /checked\\s*(?:[^=]|=\\s*.checked.)/i,\n\twrapMap = {\n\t\toption: [ 1, \"<select multiple='multiple'>\", \"</select>\" ],\n\t\tlegend: [ 1, \"<fieldset>\", \"</fieldset>\" ],\n\t\tthead: [ 1, \"<table>\", \"</table>\" ],\n\t\ttr: [ 2, \"<table><tbody>\", \"</tbody></table>\" ],\n\t\ttd: [ 3, \"<table><tbody><tr>\", \"</tr></tbody></table>\" ],\n\t\tcol: [ 2, \"<table><tbody></tbody><colgroup>\", \"</colgroup></table>\" ],\n\t\tarea: [ 1, \"<map>\", \"</map>\" ],\n\t\t_default: [ 0, \"\", \"\" ]\n\t};\n\nwrapMap.optgroup = wrapMap.option;\nwrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;\nwrapMap.th = wrapMap.td;\n\n// IE can't serialize <link> and <script> tags normally\nif ( !jQuery.support.htmlSerialize ) {\n\twrapMap._default = [ 1, \"div<div>\", \"</div>\" ];\n}\n\njQuery.fn.extend({\n\ttext: function( text ) {\n\t\tif ( jQuery.isFunction(text) ) {\n\t\t\treturn this.each(function(i) {\n\t\t\t\tvar self = jQuery( this );\n\n\t\t\t\tself.text( text.call(this, i, self.text()) );\n\t\t\t});\n\t\t}\n\n\t\tif ( typeof text !== \"object\" && text !== undefined ) {\n\t\t\treturn this.empty().append( (this[0] && this[0].ownerDocument || document).createTextNode( text ) );\n\t\t}\n\n\t\treturn jQuery.text( this );\n\t},\n\n\twrapAll: function( html ) {\n\t\tif ( jQuery.isFunction( html ) ) {\n\t\t\treturn this.each(function(i) {\n\t\t\t\tjQuery(this).wrapAll( html.call(this, i) );\n\t\t\t});\n\t\t}\n\n\t\tif ( this[0] ) {\n\t\t\t// The elements to wrap the target around\n\t\t\tvar wrap = jQuery( html, this[0].ownerDocument ).eq(0).clone(true);\n\n\t\t\tif ( this[0].parentNode ) {\n\t\t\t\twrap.insertBefore( this[0] );\n\t\t\t}\n\n\t\t\twrap.map(function() {\n\t\t\t\tvar elem = this;\n\n\t\t\t\twhile ( elem.firstChild && elem.firstChild.nodeType === 1 ) {\n\t\t\t\t\telem = elem.firstChild;\n\t\t\t\t}\n\n\t\t\t\treturn elem;\n\t\t\t}).append(this);\n\t\t}\n\n\t\treturn this;\n\t},\n\n\twrapInner: function( html ) {\n\t\tif ( jQuery.isFunction( html ) ) {\n\t\t\treturn this.each(function(i) {\n\t\t\t\tjQuery(this).wrapInner( html.call(this, i) );\n\t\t\t});\n\t\t}\n\n\t\treturn this.each(function() {\n\t\t\tvar self = jQuery( this ),\n\t\t\t\tcontents = self.contents();\n\n\t\t\tif ( contents.length ) {\n\t\t\t\tcontents.wrapAll( html );\n\n\t\t\t} else {\n\t\t\t\tself.append( html );\n\t\t\t}\n\t\t});\n\t},\n\n\twrap: function( html ) {\n\t\treturn this.each(function() {\n\t\t\tjQuery( this ).wrapAll( html );\n\t\t});\n\t},\n\n\tunwrap: function() {\n\t\treturn this.parent().each(function() {\n\t\t\tif ( !jQuery.nodeName( this, \"body\" ) ) {\n\t\t\t\tjQuery( this ).replaceWith( this.childNodes );\n\t\t\t}\n\t\t}).end();\n\t},\n\n\tappend: function() {\n\t\treturn this.domManip(arguments, true, function( elem ) {\n\t\t\tif ( this.nodeType === 1 ) {\n\t\t\t\tthis.appendChild( elem );\n\t\t\t}\n\t\t});\n\t},\n\n\tprepend: function() {\n\t\treturn this.domManip(arguments, true, function( elem ) {\n\t\t\tif ( this.nodeType === 1 ) {\n\t\t\t\tthis.insertBefore( elem, this.firstChild );\n\t\t\t}\n\t\t});\n\t},\n\n\tbefore: function() {\n\t\tif ( this[0] && this[0].parentNode ) {\n\t\t\treturn this.domManip(arguments, false, function( elem ) {\n\t\t\t\tthis.parentNode.insertBefore( elem, this );\n\t\t\t});\n\t\t} else if ( arguments.length ) {\n\t\t\tvar set = jQuery(arguments[0]);\n\t\t\tset.push.apply( set, this.toArray() );\n\t\t\treturn this.pushStack( set, \"before\", arguments );\n\t\t}\n\t},\n\n\tafter: function() {\n\t\tif ( this[0] && this[0].parentNode ) {\n\t\t\treturn this.domManip(arguments, false, function( elem ) {\n\t\t\t\tthis.parentNode.insertBefore( elem, this.nextSibling );\n\t\t\t});\n\t\t} else if ( arguments.length ) {\n\t\t\tvar set = this.pushStack( this, \"after\", arguments );\n\t\t\tset.push.apply( set, jQuery(arguments[0]).toArray() );\n\t\t\treturn set;\n\t\t}\n\t},\n\n\t// keepData is for internal use only--do not document\n\tremove: function( selector, keepData ) {\n\t\tfor ( var i = 0, elem; (elem = this[i]) != null; i++ ) {\n\t\t\tif ( !selector || jQuery.filter( selector, [ elem ] ).length ) {\n\t\t\t\tif ( !keepData && elem.nodeType === 1 ) {\n\t\t\t\t\tjQuery.cleanData( elem.getElementsByTagName(\"*\") );\n\t\t\t\t\tjQuery.cleanData( [ elem ] );\n\t\t\t\t}\n\n\t\t\t\tif ( elem.parentNode ) {\n\t\t\t\t\telem.parentNode.removeChild( elem );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn this;\n\t},\n\n\tempty: function() {\n\t\tfor ( var i = 0, elem; (elem = this[i]) != null; i++ ) {\n\t\t\t// Remove element nodes and prevent memory leaks\n\t\t\tif ( elem.nodeType === 1 ) {\n\t\t\t\tjQuery.cleanData( elem.getElementsByTagName(\"*\") );\n\t\t\t}\n\n\t\t\t// Remove any remaining nodes\n\t\t\twhile ( elem.firstChild ) {\n\t\t\t\telem.removeChild( elem.firstChild );\n\t\t\t}\n\t\t}\n\n\t\treturn this;\n\t},\n\n\tclone: function( dataAndEvents, deepDataAndEvents ) {\n\t\tdataAndEvents = dataAndEvents == null ? false : dataAndEvents;\n\t\tdeepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents;\n\n\t\treturn this.map( function () {\n\t\t\treturn jQuery.clone( this, dataAndEvents, deepDataAndEvents );\n\t\t});\n\t},\n\n\thtml: function( value ) {\n\t\tif ( value === undefined ) {\n\t\t\treturn this[0] && this[0].nodeType === 1 ?\n\t\t\t\tthis[0].innerHTML.replace(rinlinejQuery, \"\") :\n\t\t\t\tnull;\n\n\t\t// See if we can take a shortcut and just use innerHTML\n\t\t} else if ( typeof value === \"string\" && !rnocache.test( value ) &&\n\t\t\t(jQuery.support.leadingWhitespace || !rleadingWhitespace.test( value )) &&\n\t\t\t!wrapMap[ (rtagName.exec( value ) || [\"\", \"\"])[1].toLowerCase() ] ) {\n\n\t\t\tvalue = value.replace(rxhtmlTag, \"<$1></$2>\");\n\n\t\t\ttry {\n\t\t\t\tfor ( var i = 0, l = this.length; i < l; i++ ) {\n\t\t\t\t\t// Remove element nodes and prevent memory leaks\n\t\t\t\t\tif ( this[i].nodeType === 1 ) {\n\t\t\t\t\t\tjQuery.cleanData( this[i].getElementsByTagName(\"*\") );\n\t\t\t\t\t\tthis[i].innerHTML = value;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t// If using innerHTML throws an exception, use the fallback method\n\t\t\t} catch(e) {\n\t\t\t\tthis.empty().append( value );\n\t\t\t}\n\n\t\t} else if ( jQuery.isFunction( value ) ) {\n\t\t\tthis.each(function(i){\n\t\t\t\tvar self = jQuery( this );\n\n\t\t\t\tself.html( value.call(this, i, self.html()) );\n\t\t\t});\n\n\t\t} else {\n\t\t\tthis.empty().append( value );\n\t\t}\n\n\t\treturn this;\n\t},\n\n\treplaceWith: function( value ) {\n\t\tif ( this[0] && this[0].parentNode ) {\n\t\t\t// Make sure that the elements are removed from the DOM before they are inserted\n\t\t\t// this can help fix replacing a parent with child elements\n\t\t\tif ( jQuery.isFunction( value ) ) {\n\t\t\t\treturn this.each(function(i) {\n\t\t\t\t\tvar self = jQuery(this), old = self.html();\n\t\t\t\t\tself.replaceWith( value.call( this, i, old ) );\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tif ( typeof value !== \"string\" ) {\n\t\t\t\tvalue = jQuery( value ).detach();\n\t\t\t}\n\n\t\t\treturn this.each(function() {\n\t\t\t\tvar next = this.nextSibling,\n\t\t\t\t\tparent = this.parentNode;\n\n\t\t\t\tjQuery( this ).remove();\n\n\t\t\t\tif ( next ) {\n\t\t\t\t\tjQuery(next).before( value );\n\t\t\t\t} else {\n\t\t\t\t\tjQuery(parent).append( value );\n\t\t\t\t}\n\t\t\t});\n\t\t} else {\n\t\t\treturn this.pushStack( jQuery(jQuery.isFunction(value) ? value() : value), \"replaceWith\", value );\n\t\t}\n\t},\n\n\tdetach: function( selector ) {\n\t\treturn this.remove( selector, true );\n\t},\n\n\tdomManip: function( args, table, callback ) {\n\t\tvar results, first, fragment, parent,\n\t\t\tvalue = args[0],\n\t\t\tscripts = [];\n\n\t\t// We can't cloneNode fragments that contain checked, in WebKit\n\t\tif ( !jQuery.support.checkClone && arguments.length === 3 && typeof value === \"string\" && rchecked.test( value ) ) {\n\t\t\treturn this.each(function() {\n\t\t\t\tjQuery(this).domManip( args, table, callback, true );\n\t\t\t});\n\t\t}\n\n\t\tif ( jQuery.isFunction(value) ) {\n\t\t\treturn this.each(function(i) {\n\t\t\t\tvar self = jQuery(this);\n\t\t\t\targs[0] = value.call(this, i, table ? self.html() : undefined);\n\t\t\t\tself.domManip( args, table, callback );\n\t\t\t});\n\t\t}\n\n\t\tif ( this[0] ) {\n\t\t\tparent = value && value.parentNode;\n\n\t\t\t// If we're in a fragment, just use that instead of building a new one\n\t\t\tif ( jQuery.support.parentNode && parent && parent.nodeType === 11 && parent.childNodes.length === this.length ) {\n\t\t\t\tresults = { fragment: parent };\n\n\t\t\t} else {\n\t\t\t\tresults = jQuery.buildFragment( args, this, scripts );\n\t\t\t}\n\n\t\t\tfragment = results.fragment;\n\n\t\t\tif ( fragment.childNodes.length === 1 ) {\n\t\t\t\tfirst = fragment = fragment.firstChild;\n\t\t\t} else {\n\t\t\t\tfirst = fragment.firstChild;\n\t\t\t}\n\n\t\t\tif ( first ) {\n\t\t\t\ttable = table && jQuery.nodeName( first, \"tr\" );\n\n\t\t\t\tfor ( var i = 0, l = this.length, lastIndex = l - 1; i < l; i++ ) {\n\t\t\t\t\tcallback.call(\n\t\t\t\t\t\ttable ?\n\t\t\t\t\t\t\troot(this[i], first) :\n\t\t\t\t\t\t\tthis[i],\n\t\t\t\t\t\t// Make sure that we do not leak memory by inadvertently discarding\n\t\t\t\t\t\t// the original fragment (which might have attached data) instead of\n\t\t\t\t\t\t// using it; in addition, use the original fragment object for the last\n\t\t\t\t\t\t// item instead of first because it can end up being emptied incorrectly\n\t\t\t\t\t\t// in certain situations (Bug #8070).\n\t\t\t\t\t\t// Fragments from the fragment cache must always be cloned and never used\n\t\t\t\t\t\t// in place.\n\t\t\t\t\t\tresults.cacheable || (l > 1 && i < lastIndex) ?\n\t\t\t\t\t\t\tjQuery.clone( fragment, true, true ) :\n\t\t\t\t\t\t\tfragment\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif ( scripts.length ) {\n\t\t\t\tjQuery.each( scripts, evalScript );\n\t\t\t}\n\t\t}\n\n\t\treturn this;\n\t}\n});\n\nfunction root( elem, cur ) {\n\treturn jQuery.nodeName(elem, \"table\") ?\n\t\t(elem.getElementsByTagName(\"tbody\")[0] ||\n\t\telem.appendChild(elem.ownerDocument.createElement(\"tbody\"))) :\n\t\telem;\n}\n\nfunction cloneCopyEvent( src, dest ) {\n\n\tif ( dest.nodeType !== 1 || !jQuery.hasData( src ) ) {\n\t\treturn;\n\t}\n\n\tvar internalKey = jQuery.expando,\n\t\toldData = jQuery.data( src ),\n\t\tcurData = jQuery.data( dest, oldData );\n\n\t// Switch to use the internal data object, if it exists, for the next\n\t// stage of data copying\n\tif ( (oldData = oldData[ internalKey ]) ) {\n\t\tvar events = oldData.events;\n\t\t\t\tcurData = curData[ internalKey ] = jQuery.extend({}, oldData);\n\n\t\tif ( events ) {\n\t\t\tdelete curData.handle;\n\t\t\tcurData.events = {};\n\n\t\t\tfor ( var type in events ) {\n\t\t\t\tfor ( var i = 0, l = events[ type ].length; i < l; i++ ) {\n\t\t\t\t\tjQuery.event.add( dest, type + ( events[ type ][ i ].namespace ? \".\" : \"\" ) + events[ type ][ i ].namespace, events[ type ][ i ], events[ type ][ i ].data );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunction cloneFixAttributes(src, dest) {\n\t// We do not need to do anything for non-Elements\n\tif ( dest.nodeType !== 1 ) {\n\t\treturn;\n\t}\n\n\tvar nodeName = dest.nodeName.toLowerCase();\n\n\t// clearAttributes removes the attributes, which we don't want,\n\t// but also removes the attachEvent events, which we *do* want\n\tdest.clearAttributes();\n\n\t// mergeAttributes, in contrast, only merges back on the\n\t// original attributes, not the events\n\tdest.mergeAttributes(src);\n\n\t// IE6-8 fail to clone children inside object elements that use\n\t// the proprietary classid attribute value (rather than the type\n\t// attribute) to identify the type of content to display\n\tif ( nodeName === \"object\" ) {\n\t\tdest.outerHTML = src.outerHTML;\n\n\t} else if ( nodeName === \"input\" && (src.type === \"checkbox\" || src.type === \"radio\") ) {\n\t\t// IE6-8 fails to persist the checked state of a cloned checkbox\n\t\t// or radio button. Worse, IE6-7 fail to give the cloned element\n\t\t// a checked appearance if the defaultChecked value isn't also set\n\t\tif ( src.checked ) {\n\t\t\tdest.defaultChecked = dest.checked = src.checked;\n\t\t}\n\n\t\t// IE6-7 get confused and end up setting the value of a cloned\n\t\t// checkbox/radio button to an empty string instead of \"on\"\n\t\tif ( dest.value !== src.value ) {\n\t\t\tdest.value = src.value;\n\t\t}\n\n\t// IE6-8 fails to return the selected option to the default selected\n\t// state when cloning options\n\t} else if ( nodeName === \"option\" ) {\n\t\tdest.selected = src.defaultSelected;\n\n\t// IE6-8 fails to set the defaultValue to the correct value when\n\t// cloning other types of input fields\n\t} else if ( nodeName === \"input\" || nodeName === \"textarea\" ) {\n\t\tdest.defaultValue = src.defaultValue;\n\t}\n\n\t// Event data gets referenced instead of copied if the expando\n\t// gets copied too\n\tdest.removeAttribute( jQuery.expando );\n}\n\njQuery.buildFragment = function( args, nodes, scripts ) {\n\tvar fragment, cacheable, cacheresults,\n\t\tdoc = (nodes && nodes[0] ? nodes[0].ownerDocument || nodes[0] : document);\n\n\t// Only cache \"small\" (1/2 KB) HTML strings that are associated with the main document\n\t// Cloning options loses the selected state, so don't cache them\n\t// IE 6 doesn't like it when you put <object> or <embed> elements in a fragment\n\t// Also, WebKit does not clone 'checked' attributes on cloneNode, so don't cache\n\tif ( args.length === 1 && typeof args[0] === \"string\" && args[0].length < 512 && doc === document &&\n\t\targs[0].charAt(0) === \"<\" && !rnocache.test( args[0] ) && (jQuery.support.checkClone || !rchecked.test( args[0] )) ) {\n\n\t\tcacheable = true;\n\t\tcacheresults = jQuery.fragments[ args[0] ];\n\t\tif ( cacheresults ) {\n\t\t\tif ( cacheresults !== 1 ) {\n\t\t\t\tfragment = cacheresults;\n\t\t\t}\n\t\t}\n\t}\n\n\tif ( !fragment ) {\n\t\tfragment = doc.createDocumentFragment();\n\t\tjQuery.clean( args, doc, fragment, scripts );\n\t}\n\n\tif ( cacheable ) {\n\t\tjQuery.fragments[ args[0] ] = cacheresults ? fragment : 1;\n\t}\n\n\treturn { fragment: fragment, cacheable: cacheable };\n};\n\njQuery.fragments = {};\n\njQuery.each({\n\tappendTo: \"append\",\n\tprependTo: \"prepend\",\n\tinsertBefore: \"before\",\n\tinsertAfter: \"after\",\n\treplaceAll: \"replaceWith\"\n}, function( name, original ) {\n\tjQuery.fn[ name ] = function( selector ) {\n\t\tvar ret = [],\n\t\t\tinsert = jQuery( selector ),\n\t\t\tparent = this.length === 1 && this[0].parentNode;\n\n\t\tif ( parent && parent.nodeType === 11 && parent.childNodes.length === 1 && insert.length === 1 ) {\n\t\t\tinsert[ original ]( this[0] );\n\t\t\treturn this;\n\n\t\t} else {\n\t\t\tfor ( var i = 0, l = insert.length; i < l; i++ ) {\n\t\t\t\tvar elems = (i > 0 ? this.clone(true) : this).get();\n\t\t\t\tjQuery( insert[i] )[ original ]( elems );\n\t\t\t\tret = ret.concat( elems );\n\t\t\t}\n\n\t\t\treturn this.pushStack( ret, name, insert.selector );\n\t\t}\n\t};\n});\n\nfunction getAll( elem ) {\n\tif ( \"getElementsByTagName\" in elem ) {\n\t\treturn elem.getElementsByTagName( \"*\" );\n\t\n\t} else if ( \"querySelectorAll\" in elem ) {\n\t\treturn elem.querySelectorAll( \"*\" );\n\n\t} else {\n\t\treturn [];\n\t}\n}\n\njQuery.extend({\n\tclone: function( elem, dataAndEvents, deepDataAndEvents ) {\n\t\tvar clone = elem.cloneNode(true),\n\t\t\t\tsrcElements,\n\t\t\t\tdestElements,\n\t\t\t\ti;\n\n\t\tif ( (!jQuery.support.noCloneEvent || !jQuery.support.noCloneChecked) &&\n\t\t\t\t(elem.nodeType === 1 || elem.nodeType === 11) && !jQuery.isXMLDoc(elem) ) {\n\t\t\t// IE copies events bound via attachEvent when using cloneNode.\n\t\t\t// Calling detachEvent on the clone will also remove the events\n\t\t\t// from the original. In order to get around this, we use some\n\t\t\t// proprietary methods to clear the events. Thanks to MooTools\n\t\t\t// guys for this hotness.\n\n\t\t\tcloneFixAttributes( elem, clone );\n\n\t\t\t// Using Sizzle here is crazy slow, so we use getElementsByTagName\n\t\t\t// instead\n\t\t\tsrcElements = getAll( elem );\n\t\t\tdestElements = getAll( clone );\n\n\t\t\t// Weird iteration because IE will replace the length property\n\t\t\t// with an element if you are cloning the body and one of the\n\t\t\t// elements on the page has a name or id of \"length\"\n\t\t\tfor ( i = 0; srcElements[i]; ++i ) {\n\t\t\t\tcloneFixAttributes( srcElements[i], destElements[i] );\n\t\t\t}\n\t\t}\n\n\t\t// Copy the events from the original to the clone\n\t\tif ( dataAndEvents ) {\n\t\t\tcloneCopyEvent( elem, clone );\n\n\t\t\tif ( deepDataAndEvents ) {\n\t\t\t\tsrcElements = getAll( elem );\n\t\t\t\tdestElements = getAll( clone );\n\n\t\t\t\tfor ( i = 0; srcElements[i]; ++i ) {\n\t\t\t\t\tcloneCopyEvent( srcElements[i], destElements[i] );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Return the cloned set\n\t\treturn clone;\n},\n\tclean: function( elems, context, fragment, scripts ) {\n\t\tcontext = context || document;\n\n\t\t// !context.createElement fails in IE with an error but returns typeof 'object'\n\t\tif ( typeof context.createElement === \"undefined\" ) {\n\t\t\tcontext = context.ownerDocument || context[0] && context[0].ownerDocument || document;\n\t\t}\n\n\t\tvar ret = [];\n\n\t\tfor ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {\n\t\t\tif ( typeof elem === \"number\" ) {\n\t\t\t\telem += \"\";\n\t\t\t}\n\n\t\t\tif ( !elem ) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Convert html string into DOM nodes\n\t\t\tif ( typeof elem === \"string\" && !rhtml.test( elem ) ) {\n\t\t\t\telem = context.createTextNode( elem );\n\n\t\t\t} else if ( typeof elem === \"string\" ) {\n\t\t\t\t// Fix \"XHTML\"-style tags in all browsers\n\t\t\t\telem = elem.replace(rxhtmlTag, \"<$1></$2>\");\n\n\t\t\t\t// Trim whitespace, otherwise indexOf won't work as expected\n\t\t\t\tvar tag = (rtagName.exec( elem ) || [\"\", \"\"])[1].toLowerCase(),\n\t\t\t\t\twrap = wrapMap[ tag ] || wrapMap._default,\n\t\t\t\t\tdepth = wrap[0],\n\t\t\t\t\tdiv = context.createElement(\"div\");\n\n\t\t\t\t// Go to html and back, then peel off extra wrappers\n\t\t\t\tdiv.innerHTML = wrap[1] + elem + wrap[2];\n\n\t\t\t\t// Move to the right depth\n\t\t\t\twhile ( depth-- ) {\n\t\t\t\t\tdiv = div.lastChild;\n\t\t\t\t}\n\n\t\t\t\t// Remove IE's autoinserted <tbody> from table fragments\n\t\t\t\tif ( !jQuery.support.tbody ) {\n\n\t\t\t\t\t// String was a <table>, *may* have spurious <tbody>\n\t\t\t\t\tvar hasBody = rtbody.test(elem),\n\t\t\t\t\t\ttbody = tag === \"table\" && !hasBody ?\n\t\t\t\t\t\t\tdiv.firstChild && div.firstChild.childNodes :\n\n\t\t\t\t\t\t\t// String was a bare <thead> or <tfoot>\n\t\t\t\t\t\t\twrap[1] === \"<table>\" && !hasBody ?\n\t\t\t\t\t\t\t\tdiv.childNodes :\n\t\t\t\t\t\t\t\t[];\n\n\t\t\t\t\tfor ( var j = tbody.length - 1; j >= 0 ; --j ) {\n\t\t\t\t\t\tif ( jQuery.nodeName( tbody[ j ], \"tbody\" ) && !tbody[ j ].childNodes.length ) {\n\t\t\t\t\t\t\ttbody[ j ].parentNode.removeChild( tbody[ j ] );\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t\t// IE completely kills leading whitespace when innerHTML is used\n\t\t\t\tif ( !jQuery.support.leadingWhitespace && rleadingWhitespace.test( elem ) ) {\n\t\t\t\t\tdiv.insertBefore( context.createTextNode( rleadingWhitespace.exec(elem)[0] ), div.firstChild );\n\t\t\t\t}\n\n\t\t\t\telem = div.childNodes;\n\t\t\t}\n\n\t\t\tif ( elem.nodeType ) {\n\t\t\t\tret.push( elem );\n\t\t\t} else {\n\t\t\t\tret = jQuery.merge( ret, elem );\n\t\t\t}\n\t\t}\n\n\t\tif ( fragment ) {\n\t\t\tfor ( i = 0; ret[i]; i++ ) {\n\t\t\t\tif ( scripts && jQuery.nodeName( ret[i], \"script\" ) && (!ret[i].type || ret[i].type.toLowerCase() === \"text/javascript\") ) {\n\t\t\t\t\tscripts.push( ret[i].parentNode ? ret[i].parentNode.removeChild( ret[i] ) : ret[i] );\n\n\t\t\t\t} else {\n\t\t\t\t\tif ( ret[i].nodeType === 1 ) {\n\t\t\t\t\t\tret.splice.apply( ret, [i + 1, 0].concat(jQuery.makeArray(ret[i].getElementsByTagName(\"script\"))) );\n\t\t\t\t\t}\n\t\t\t\t\tfragment.appendChild( ret[i] );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn ret;\n\t},\n\n\tcleanData: function( elems ) {\n\t\tvar data, id, cache = jQuery.cache, internalKey = jQuery.expando, special = jQuery.event.special,\n\t\t\tdeleteExpando = jQuery.support.deleteExpando;\n\n\t\tfor ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {\n\t\t\tif ( elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()] ) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tid = elem[ jQuery.expando ];\n\n\t\t\tif ( id ) {\n\t\t\t\tdata = cache[ id ] && cache[ id ][ internalKey ];\n\n\t\t\t\tif ( data && data.events ) {\n\t\t\t\t\tfor ( var type in data.events ) {\n\t\t\t\t\t\tif ( special[ type ] ) {\n\t\t\t\t\t\t\tjQuery.event.remove( elem, type );\n\n\t\t\t\t\t\t// This is a shortcut to avoid jQuery.event.remove's overhead\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tjQuery.removeEvent( elem, type, data.handle );\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Null the DOM reference to avoid IE6/7/8 leak (#7054)\n\t\t\t\t\tif ( data.handle ) {\n\t\t\t\t\t\tdata.handle.elem = null;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif ( deleteExpando ) {\n\t\t\t\t\tdelete elem[ jQuery.expando ];\n\n\t\t\t\t} else if ( elem.removeAttribute ) {\n\t\t\t\t\telem.removeAttribute( jQuery.expando );\n\t\t\t\t}\n\n\t\t\t\tdelete cache[ id ];\n\t\t\t}\n\t\t}\n\t}\n});\n\nfunction evalScript( i, elem ) {\n\tif ( elem.src ) {\n\t\tjQuery.ajax({\n\t\t\turl: elem.src,\n\t\t\tasync: false,\n\t\t\tdataType: \"script\"\n\t\t});\n\t} else {\n\t\tjQuery.globalEval( elem.text || elem.textContent || elem.innerHTML || \"\" );\n\t}\n\n\tif ( elem.parentNode ) {\n\t\telem.parentNode.removeChild( elem );\n\t}\n}\n\n\n\n\nvar ralpha = /alpha\\([^)]*\\)/i,\n\tropacity = /opacity=([^)]*)/,\n\trdashAlpha = /-([a-z])/ig,\n\trupper = /([A-Z])/g,\n\trnumpx = /^-?\\d+(?:px)?$/i,\n\trnum = /^-?\\d/,\n\n\tcssShow = { position: \"absolute\", visibility: \"hidden\", display: \"block\" },\n\tcssWidth = [ \"Left\", \"Right\" ],\n\tcssHeight = [ \"Top\", \"Bottom\" ],\n\tcurCSS,\n\n\tgetComputedStyle,\n\tcurrentStyle,\n\n\tfcamelCase = function( all, letter ) {\n\t\treturn letter.toUpperCase();\n\t};\n\njQuery.fn.css = function( name, value ) {\n\t// Setting 'undefined' is a no-op\n\tif ( arguments.length === 2 && value === undefined ) {\n\t\treturn this;\n\t}\n\n\treturn jQuery.access( this, name, value, true, function( elem, name, value ) {\n\t\treturn value !== undefined ?\n\t\t\tjQuery.style( elem, name, value ) :\n\t\t\tjQuery.css( elem, name );\n\t});\n};\n\njQuery.extend({\n\t// Add in style property hooks for overriding the default\n\t// behavior of getting and setting a style property\n\tcssHooks: {\n\t\topacity: {\n\t\t\tget: function( elem, computed ) {\n\t\t\t\tif ( computed ) {\n\t\t\t\t\t// We should always get a number back from opacity\n\t\t\t\t\tvar ret = curCSS( elem, \"opacity\", \"opacity\" );\n\t\t\t\t\treturn ret === \"\" ? \"1\" : ret;\n\n\t\t\t\t} else {\n\t\t\t\t\treturn elem.style.opacity;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t},\n\n\t// Exclude the following css properties to add px\n\tcssNumber: {\n\t\t\"zIndex\": true,\n\t\t\"fontWeight\": true,\n\t\t\"opacity\": true,\n\t\t\"zoom\": true,\n\t\t\"lineHeight\": true\n\t},\n\n\t// Add in properties whose names you wish to fix before\n\t// setting or getting the value\n\tcssProps: {\n\t\t// normalize float css property\n\t\t\"float\": jQuery.support.cssFloat ? \"cssFloat\" : \"styleFloat\"\n\t},\n\n\t// Get and set the style property on a DOM Node\n\tstyle: function( elem, name, value, extra ) {\n\t\t// Don't set styles on text and comment nodes\n\t\tif ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Make sure that we're working with the right name\n\t\tvar ret, origName = jQuery.camelCase( name ),\n\t\t\tstyle = elem.style, hooks = jQuery.cssHooks[ origName ];\n\n\t\tname = jQuery.cssProps[ origName ] || origName;\n\n\t\t// Check if we're setting a value\n\t\tif ( value !== undefined ) {\n\t\t\t// Make sure that NaN and null values aren't set. See: #7116\n\t\t\tif ( typeof value === \"number\" && isNaN( value ) || value == null ) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// If a number was passed in, add 'px' to the (except for certain CSS properties)\n\t\t\tif ( typeof value === \"number\" && !jQuery.cssNumber[ origName ] ) {\n\t\t\t\tvalue += \"px\";\n\t\t\t}\n\n\t\t\t// If a hook was provided, use that value, otherwise just set the specified value\n\t\t\tif ( !hooks || !(\"set\" in hooks) || (value = hooks.set( elem, value )) !== undefined ) {\n\t\t\t\t// Wrapped to prevent IE from throwing errors when 'invalid' values are provided\n\t\t\t\t// Fixes bug #5509\n\t\t\t\ttry {\n\t\t\t\t\tstyle[ name ] = value;\n\t\t\t\t} catch(e) {}\n\t\t\t}\n\n\t\t} else {\n\t\t\t// If a hook was provided get the non-computed value from there\n\t\t\tif ( hooks && \"get\" in hooks && (ret = hooks.get( elem, false, extra )) !== undefined ) {\n\t\t\t\treturn ret;\n\t\t\t}\n\n\t\t\t// Otherwise just get the value from the style object\n\t\t\treturn style[ name ];\n\t\t}\n\t},\n\n\tcss: function( elem, name, extra ) {\n\t\t// Make sure that we're working with the right name\n\t\tvar ret, origName = jQuery.camelCase( name ),\n\t\t\thooks = jQuery.cssHooks[ origName ];\n\n\t\tname = jQuery.cssProps[ origName ] || origName;\n\n\t\t// If a hook was provided get the computed value from there\n\t\tif ( hooks && \"get\" in hooks && (ret = hooks.get( elem, true, extra )) !== undefined ) {\n\t\t\treturn ret;\n\n\t\t// Otherwise, if a way to get the computed value exists, use that\n\t\t} else if ( curCSS ) {\n\t\t\treturn curCSS( elem, name, origName );\n\t\t}\n\t},\n\n\t// A method for quickly swapping in/out CSS properties to get correct calculations\n\tswap: function( elem, options, callback ) {\n\t\tvar old = {};\n\n\t\t// Remember the old values, and insert the new ones\n\t\tfor ( var name in options ) {\n\t\t\told[ name ] = elem.style[ name ];\n\t\t\telem.style[ name ] = options[ name ];\n\t\t}\n\n\t\tcallback.call( elem );\n\n\t\t// Revert the old values\n\t\tfor ( name in options ) {\n\t\t\telem.style[ name ] = old[ name ];\n\t\t}\n\t},\n\n\tcamelCase: function( string ) {\n\t\treturn string.replace( rdashAlpha, fcamelCase );\n\t}\n});\n\n// DEPRECATED, Use jQuery.css() instead\njQuery.curCSS = jQuery.css;\n\njQuery.each([\"height\", \"width\"], function( i, name ) {\n\tjQuery.cssHooks[ name ] = {\n\t\tget: function( elem, computed, extra ) {\n\t\t\tvar val;\n\n\t\t\tif ( computed ) {\n\t\t\t\tif ( elem.offsetWidth !== 0 ) {\n\t\t\t\t\tval = getWH( elem, name, extra );\n\n\t\t\t\t} else {\n\t\t\t\t\tjQuery.swap( elem, cssShow, function() {\n\t\t\t\t\t\tval = getWH( elem, name, extra );\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\tif ( val <= 0 ) {\n\t\t\t\t\tval = curCSS( elem, name, name );\n\n\t\t\t\t\tif ( val === \"0px\" && currentStyle ) {\n\t\t\t\t\t\tval = currentStyle( elem, name, name );\n\t\t\t\t\t}\n\n\t\t\t\t\tif ( val != null ) {\n\t\t\t\t\t\t// Should return \"auto\" instead of 0, use 0 for\n\t\t\t\t\t\t// temporary backwards-compat\n\t\t\t\t\t\treturn val === \"\" || val === \"auto\" ? \"0px\" : val;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif ( val < 0 || val == null ) {\n\t\t\t\t\tval = elem.style[ name ];\n\n\t\t\t\t\t// Should return \"auto\" instead of 0, use 0 for\n\t\t\t\t\t// temporary backwards-compat\n\t\t\t\t\treturn val === \"\" || val === \"auto\" ? \"0px\" : val;\n\t\t\t\t}\n\n\t\t\t\treturn typeof val === \"string\" ? val : val + \"px\";\n\t\t\t}\n\t\t},\n\n\t\tset: function( elem, value ) {\n\t\t\tif ( rnumpx.test( value ) ) {\n\t\t\t\t// ignore negative width and height values #1599\n\t\t\t\tvalue = parseFloat(value);\n\n\t\t\t\tif ( value >= 0 ) {\n\t\t\t\t\treturn value + \"px\";\n\t\t\t\t}\n\n\t\t\t} else {\n\t\t\t\treturn value;\n\t\t\t}\n\t\t}\n\t};\n});\n\nif ( !jQuery.support.opacity ) {\n\tjQuery.cssHooks.opacity = {\n\t\tget: function( elem, computed ) {\n\t\t\t// IE uses filters for opacity\n\t\t\treturn ropacity.test((computed && elem.currentStyle ? elem.currentStyle.filter : elem.style.filter) || \"\") ?\n\t\t\t\t(parseFloat(RegExp.$1) / 100) + \"\" :\n\t\t\t\tcomputed ? \"1\" : \"\";\n\t\t},\n\n\t\tset: function( elem, value ) {\n\t\t\tvar style = elem.style;\n\n\t\t\t// IE has trouble with opacity if it does not have layout\n\t\t\t// Force it by setting the zoom level\n\t\t\tstyle.zoom = 1;\n\n\t\t\t// Set the alpha filter to set the opacity\n\t\t\tvar opacity = jQuery.isNaN(value) ?\n\t\t\t\t\"\" :\n\t\t\t\t\"alpha(opacity=\" + value * 100 + \")\",\n\t\t\t\tfilter = style.filter || \"\";\n\n\t\t\tstyle.filter = ralpha.test(filter) ?\n\t\t\t\tfilter.replace(ralpha, opacity) :\n\t\t\t\tstyle.filter + ' ' + opacity;\n\t\t}\n\t};\n}\n\nif ( document.defaultView && document.defaultView.getComputedStyle ) {\n\tgetComputedStyle = function( elem, newName, name ) {\n\t\tvar ret, defaultView, computedStyle;\n\n\t\tname = name.replace( rupper, \"-$1\" ).toLowerCase();\n\n\t\tif ( !(defaultView = elem.ownerDocument.defaultView) ) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tif ( (computedStyle = defaultView.getComputedStyle( elem, null )) ) {\n\t\t\tret = computedStyle.getPropertyValue( name );\n\t\t\tif ( ret === \"\" && !jQuery.contains( elem.ownerDocument.documentElement, elem ) ) {\n\t\t\t\tret = jQuery.style( elem, name );\n\t\t\t}\n\t\t}\n\n\t\treturn ret;\n\t};\n}\n\nif ( document.documentElement.currentStyle ) {\n\tcurrentStyle = function( elem, name ) {\n\t\tvar left,\n\t\t\tret = elem.currentStyle && elem.currentStyle[ name ],\n\t\t\trsLeft = elem.runtimeStyle && elem.runtimeStyle[ name ],\n\t\t\tstyle = elem.style;\n\n\t\t// From the awesome hack by Dean Edwards\n\t\t// http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291\n\n\t\t// If we're not dealing with a regular pixel number\n\t\t// but a number that has a weird ending, we need to convert it to pixels\n\t\tif ( !rnumpx.test( ret ) && rnum.test( ret ) ) {\n\t\t\t// Remember the original values\n\t\t\tleft = style.left;\n\n\t\t\t// Put in the new values to get a computed value out\n\t\t\tif ( rsLeft ) {\n\t\t\t\telem.runtimeStyle.left = elem.currentStyle.left;\n\t\t\t}\n\t\t\tstyle.left = name === \"fontSize\" ? \"1em\" : (ret || 0);\n\t\t\tret = style.pixelLeft + \"px\";\n\n\t\t\t// Revert the changed values\n\t\t\tstyle.left = left;\n\t\t\tif ( rsLeft ) {\n\t\t\t\telem.runtimeStyle.left = rsLeft;\n\t\t\t}\n\t\t}\n\n\t\treturn ret === \"\" ? \"auto\" : ret;\n\t};\n}\n\ncurCSS = getComputedStyle || currentStyle;\n\nfunction getWH( elem, name, extra ) {\n\tvar which = name === \"width\" ? cssWidth : cssHeight,\n\t\tval = name === \"width\" ? elem.offsetWidth : elem.offsetHeight;\n\n\tif ( extra === \"border\" ) {\n\t\treturn val;\n\t}\n\n\tjQuery.each( which, function() {\n\t\tif ( !extra ) {\n\t\t\tval -= parseFloat(jQuery.css( elem, \"padding\" + this )) || 0;\n\t\t}\n\n\t\tif ( extra === \"margin\" ) {\n\t\t\tval += parseFloat(jQuery.css( elem, \"margin\" + this )) || 0;\n\n\t\t} else {\n\t\t\tval -= parseFloat(jQuery.css( elem, \"border\" + this + \"Width\" )) || 0;\n\t\t}\n\t});\n\n\treturn val;\n}\n\nif ( jQuery.expr && jQuery.expr.filters ) {\n\tjQuery.expr.filters.hidden = function( elem ) {\n\t\tvar width = elem.offsetWidth,\n\t\t\theight = elem.offsetHeight;\n\n\t\treturn (width === 0 && height === 0) || (!jQuery.support.reliableHiddenOffsets && (elem.style.display || jQuery.css( elem, \"display\" )) === \"none\");\n\t};\n\n\tjQuery.expr.filters.visible = function( elem ) {\n\t\treturn !jQuery.expr.filters.hidden( elem );\n\t};\n}\n\n\n\n\nvar r20 = /%20/g,\n\trbracket = /\\[\\]$/,\n\trCRLF = /\\r?\\n/g,\n\trhash = /#.*$/,\n\trheaders = /^(.*?):[ \\t]*([^\\r\\n]*)\\r?$/mg, // IE leaves an \\r character at EOL\n\trinput = /^(?:color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,\n\t// #7653, #8125, #8152: local protocol detection\n\trlocalProtocol = /(?:^file|^widget|\\-extension):$/,\n\trnoContent = /^(?:GET|HEAD)$/,\n\trprotocol = /^\\/\\//,\n\trquery = /\\?/,\n\trscript = /<script\\b[^<]*(?:(?!<\\/script>)<[^<]*)*<\\/script>/gi,\n\trselectTextarea = /^(?:select|textarea)/i,\n\trspacesAjax = /\\s+/,\n\trts = /([?&])_=[^&]*/,\n\trucHeaders = /(^|\\-)([a-z])/g,\n\trucHeadersFunc = function( _, $1, $2 ) {\n\t\treturn $1 + $2.toUpperCase();\n\t},\n\trurl = /^([\\w\\+\\.\\-]+:)\\/\\/([^\\/?#:]*)(?::(\\d+))?/,\n\n\t// Keep a copy of the old load method\n\t_load = jQuery.fn.load,\n\n\t/* Prefilters\n\t * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example)\n\t * 2) These are called:\n\t *    - BEFORE asking for a transport\n\t *    - AFTER param serialization (s.data is a string if s.processData is true)\n\t * 3) key is the dataType\n\t * 4) the catchall symbol \"*\" can be used\n\t * 5) execution will start with transport dataType and THEN continue down to \"*\" if needed\n\t */\n\tprefilters = {},\n\n\t/* Transports bindings\n\t * 1) key is the dataType\n\t * 2) the catchall symbol \"*\" can be used\n\t * 3) selection will start with transport dataType and THEN go to \"*\" if needed\n\t */\n\ttransports = {},\n\n\t// Document location\n\tajaxLocation,\n\n\t// Document location segments\n\tajaxLocParts;\n\n// #8138, IE may throw an exception when accessing\n// a field from document.location if document.domain has been set\ntry {\n\tajaxLocation = document.location.href;\n} catch( e ) {\n\t// Use the href attribute of an A element\n\t// since IE will modify it given document.location\n\tajaxLocation = document.createElement( \"a\" );\n\tajaxLocation.href = \"\";\n\tajaxLocation = ajaxLocation.href;\n}\n\n// Segment location into parts\najaxLocParts = rurl.exec( ajaxLocation.toLowerCase() );\n\n// Base \"constructor\" for jQuery.ajaxPrefilter and jQuery.ajaxTransport\nfunction addToPrefiltersOrTransports( structure ) {\n\n\t// dataTypeExpression is optional and defaults to \"*\"\n\treturn function( dataTypeExpression, func ) {\n\n\t\tif ( typeof dataTypeExpression !== \"string\" ) {\n\t\t\tfunc = dataTypeExpression;\n\t\t\tdataTypeExpression = \"*\";\n\t\t}\n\n\t\tif ( jQuery.isFunction( func ) ) {\n\t\t\tvar dataTypes = dataTypeExpression.toLowerCase().split( rspacesAjax ),\n\t\t\t\ti = 0,\n\t\t\t\tlength = dataTypes.length,\n\t\t\t\tdataType,\n\t\t\t\tlist,\n\t\t\t\tplaceBefore;\n\n\t\t\t// For each dataType in the dataTypeExpression\n\t\t\tfor(; i < length; i++ ) {\n\t\t\t\tdataType = dataTypes[ i ];\n\t\t\t\t// We control if we're asked to add before\n\t\t\t\t// any existing element\n\t\t\t\tplaceBefore = /^\\+/.test( dataType );\n\t\t\t\tif ( placeBefore ) {\n\t\t\t\t\tdataType = dataType.substr( 1 ) || \"*\";\n\t\t\t\t}\n\t\t\t\tlist = structure[ dataType ] = structure[ dataType ] || [];\n\t\t\t\t// then we add to the structure accordingly\n\t\t\t\tlist[ placeBefore ? \"unshift\" : \"push\" ]( func );\n\t\t\t}\n\t\t}\n\t};\n}\n\n//Base inspection function for prefilters and transports\nfunction inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR,\n\t\tdataType /* internal */, inspected /* internal */ ) {\n\n\tdataType = dataType || options.dataTypes[ 0 ];\n\tinspected = inspected || {};\n\n\tinspected[ dataType ] = true;\n\n\tvar list = structure[ dataType ],\n\t\ti = 0,\n\t\tlength = list ? list.length : 0,\n\t\texecuteOnly = ( structure === prefilters ),\n\t\tselection;\n\n\tfor(; i < length && ( executeOnly || !selection ); i++ ) {\n\t\tselection = list[ i ]( options, originalOptions, jqXHR );\n\t\t// If we got redirected to another dataType\n\t\t// we try there if executing only and not done already\n\t\tif ( typeof selection === \"string\" ) {\n\t\t\tif ( !executeOnly || inspected[ selection ] ) {\n\t\t\t\tselection = undefined;\n\t\t\t} else {\n\t\t\t\toptions.dataTypes.unshift( selection );\n\t\t\t\tselection = inspectPrefiltersOrTransports(\n\t\t\t\t\t\tstructure, options, originalOptions, jqXHR, selection, inspected );\n\t\t\t}\n\t\t}\n\t}\n\t// If we're only executing or nothing was selected\n\t// we try the catchall dataType if not done already\n\tif ( ( executeOnly || !selection ) && !inspected[ \"*\" ] ) {\n\t\tselection = inspectPrefiltersOrTransports(\n\t\t\t\tstructure, options, originalOptions, jqXHR, \"*\", inspected );\n\t}\n\t// unnecessary when only executing (prefilters)\n\t// but it'll be ignored by the caller in that case\n\treturn selection;\n}\n\njQuery.fn.extend({\n\tload: function( url, params, callback ) {\n\t\tif ( typeof url !== \"string\" && _load ) {\n\t\t\treturn _load.apply( this, arguments );\n\n\t\t// Don't do a request if no elements are being requested\n\t\t} else if ( !this.length ) {\n\t\t\treturn this;\n\t\t}\n\n\t\tvar off = url.indexOf( \" \" );\n\t\tif ( off >= 0 ) {\n\t\t\tvar selector = url.slice( off, url.length );\n\t\t\turl = url.slice( 0, off );\n\t\t}\n\n\t\t// Default to a GET request\n\t\tvar type = \"GET\";\n\n\t\t// If the second parameter was provided\n\t\tif ( params ) {\n\t\t\t// If it's a function\n\t\t\tif ( jQuery.isFunction( params ) ) {\n\t\t\t\t// We assume that it's the callback\n\t\t\t\tcallback = params;\n\t\t\t\tparams = undefined;\n\n\t\t\t// Otherwise, build a param string\n\t\t\t} else if ( typeof params === \"object\" ) {\n\t\t\t\tparams = jQuery.param( params, jQuery.ajaxSettings.traditional );\n\t\t\t\ttype = \"POST\";\n\t\t\t}\n\t\t}\n\n\t\tvar self = this;\n\n\t\t// Request the remote document\n\t\tjQuery.ajax({\n\t\t\turl: url,\n\t\t\ttype: type,\n\t\t\tdataType: \"html\",\n\t\t\tdata: params,\n\t\t\t// Complete callback (responseText is used internally)\n\t\t\tcomplete: function( jqXHR, status, responseText ) {\n\t\t\t\t// Store the response as specified by the jqXHR object\n\t\t\t\tresponseText = jqXHR.responseText;\n\t\t\t\t// If successful, inject the HTML into all the matched elements\n\t\t\t\tif ( jqXHR.isResolved() ) {\n\t\t\t\t\t// #4825: Get the actual response in case\n\t\t\t\t\t// a dataFilter is present in ajaxSettings\n\t\t\t\t\tjqXHR.done(function( r ) {\n\t\t\t\t\t\tresponseText = r;\n\t\t\t\t\t});\n\t\t\t\t\t// See if a selector was specified\n\t\t\t\t\tself.html( selector ?\n\t\t\t\t\t\t// Create a dummy div to hold the results\n\t\t\t\t\t\tjQuery(\"<div>\")\n\t\t\t\t\t\t\t// inject the contents of the document in, removing the scripts\n\t\t\t\t\t\t\t// to avoid any 'Permission Denied' errors in IE\n\t\t\t\t\t\t\t.append(responseText.replace(rscript, \"\"))\n\n\t\t\t\t\t\t\t// Locate the specified elements\n\t\t\t\t\t\t\t.find(selector) :\n\n\t\t\t\t\t\t// If not, just inject the full result\n\t\t\t\t\t\tresponseText );\n\t\t\t\t}\n\n\t\t\t\tif ( callback ) {\n\t\t\t\t\tself.each( callback, [ responseText, status, jqXHR ] );\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\treturn this;\n\t},\n\n\tserialize: function() {\n\t\treturn jQuery.param( this.serializeArray() );\n\t},\n\n\tserializeArray: function() {\n\t\treturn this.map(function(){\n\t\t\treturn this.elements ? jQuery.makeArray( this.elements ) : this;\n\t\t})\n\t\t.filter(function(){\n\t\t\treturn this.name && !this.disabled &&\n\t\t\t\t( this.checked || rselectTextarea.test( this.nodeName ) ||\n\t\t\t\t\trinput.test( this.type ) );\n\t\t})\n\t\t.map(function( i, elem ){\n\t\t\tvar val = jQuery( this ).val();\n\n\t\t\treturn val == null ?\n\t\t\t\tnull :\n\t\t\t\tjQuery.isArray( val ) ?\n\t\t\t\t\tjQuery.map( val, function( val, i ){\n\t\t\t\t\t\treturn { name: elem.name, value: val.replace( rCRLF, \"\\r\\n\" ) };\n\t\t\t\t\t}) :\n\t\t\t\t\t{ name: elem.name, value: val.replace( rCRLF, \"\\r\\n\" ) };\n\t\t}).get();\n\t}\n});\n\n// Attach a bunch of functions for handling common AJAX events\njQuery.each( \"ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend\".split( \" \" ), function( i, o ){\n\tjQuery.fn[ o ] = function( f ){\n\t\treturn this.bind( o, f );\n\t};\n} );\n\njQuery.each( [ \"get\", \"post\" ], function( i, method ) {\n\tjQuery[ method ] = function( url, data, callback, type ) {\n\t\t// shift arguments if data argument was omitted\n\t\tif ( jQuery.isFunction( data ) ) {\n\t\t\ttype = type || callback;\n\t\t\tcallback = data;\n\t\t\tdata = undefined;\n\t\t}\n\n\t\treturn jQuery.ajax({\n\t\t\ttype: method,\n\t\t\turl: url,\n\t\t\tdata: data,\n\t\t\tsuccess: callback,\n\t\t\tdataType: type\n\t\t});\n\t};\n} );\n\njQuery.extend({\n\n\tgetScript: function( url, callback ) {\n\t\treturn jQuery.get( url, undefined, callback, \"script\" );\n\t},\n\n\tgetJSON: function( url, data, callback ) {\n\t\treturn jQuery.get( url, data, callback, \"json\" );\n\t},\n\n\t// Creates a full fledged settings object into target\n\t// with both ajaxSettings and settings fields.\n\t// If target is omitted, writes into ajaxSettings.\n\tajaxSetup: function ( target, settings ) {\n\t\tif ( !settings ) {\n\t\t\t// Only one parameter, we extend ajaxSettings\n\t\t\tsettings = target;\n\t\t\ttarget = jQuery.extend( true, jQuery.ajaxSettings, settings );\n\t\t} else {\n\t\t\t// target was provided, we extend into it\n\t\t\tjQuery.extend( true, target, jQuery.ajaxSettings, settings );\n\t\t}\n\t\t// Flatten fields we don't want deep extended\n\t\tfor( var field in { context: 1, url: 1 } ) {\n\t\t\tif ( field in settings ) {\n\t\t\t\ttarget[ field ] = settings[ field ];\n\t\t\t} else if( field in jQuery.ajaxSettings ) {\n\t\t\t\ttarget[ field ] = jQuery.ajaxSettings[ field ];\n\t\t\t}\n\t\t}\n\t\treturn target;\n\t},\n\n\tajaxSettings: {\n\t\turl: ajaxLocation,\n\t\tisLocal: rlocalProtocol.test( ajaxLocParts[ 1 ] ),\n\t\tglobal: true,\n\t\ttype: \"GET\",\n\t\tcontentType: \"application/x-www-form-urlencoded\",\n\t\tprocessData: true,\n\t\tasync: true,\n\t\t/*\n\t\ttimeout: 0,\n\t\tdata: null,\n\t\tdataType: null,\n\t\tusername: null,\n\t\tpassword: null,\n\t\tcache: null,\n\t\ttraditional: false,\n\t\theaders: {},\n\t\tcrossDomain: null,\n\t\t*/\n\n\t\taccepts: {\n\t\t\txml: \"application/xml, text/xml\",\n\t\t\thtml: \"text/html\",\n\t\t\ttext: \"text/plain\",\n\t\t\tjson: \"application/json, text/javascript\",\n\t\t\t\"*\": \"*/*\"\n\t\t},\n\n\t\tcontents: {\n\t\t\txml: /xml/,\n\t\t\thtml: /html/,\n\t\t\tjson: /json/\n\t\t},\n\n\t\tresponseFields: {\n\t\t\txml: \"responseXML\",\n\t\t\ttext: \"responseText\"\n\t\t},\n\n\t\t// List of data converters\n\t\t// 1) key format is \"source_type destination_type\" (a single space in-between)\n\t\t// 2) the catchall symbol \"*\" can be used for source_type\n\t\tconverters: {\n\n\t\t\t// Convert anything to text\n\t\t\t\"* text\": window.String,\n\n\t\t\t// Text to html (true = no transformation)\n\t\t\t\"text html\": true,\n\n\t\t\t// Evaluate text as a json expression\n\t\t\t\"text json\": jQuery.parseJSON,\n\n\t\t\t// Parse text as xml\n\t\t\t\"text xml\": jQuery.parseXML\n\t\t}\n\t},\n\n\tajaxPrefilter: addToPrefiltersOrTransports( prefilters ),\n\tajaxTransport: addToPrefiltersOrTransports( transports ),\n\n\t// Main method\n\tajax: function( url, options ) {\n\n\t\t// If url is an object, simulate pre-1.5 signature\n\t\tif ( typeof url === \"object\" ) {\n\t\t\toptions = url;\n\t\t\turl = undefined;\n\t\t}\n\n\t\t// Force options to be an object\n\t\toptions = options || {};\n\n\t\tvar // Create the final options object\n\t\t\ts = jQuery.ajaxSetup( {}, options ),\n\t\t\t// Callbacks context\n\t\t\tcallbackContext = s.context || s,\n\t\t\t// Context for global events\n\t\t\t// It's the callbackContext if one was provided in the options\n\t\t\t// and if it's a DOM node or a jQuery collection\n\t\t\tglobalEventContext = callbackContext !== s &&\n\t\t\t\t( callbackContext.nodeType || callbackContext instanceof jQuery ) ?\n\t\t\t\t\t\tjQuery( callbackContext ) : jQuery.event,\n\t\t\t// Deferreds\n\t\t\tdeferred = jQuery.Deferred(),\n\t\t\tcompleteDeferred = jQuery._Deferred(),\n\t\t\t// Status-dependent callbacks\n\t\t\tstatusCode = s.statusCode || {},\n\t\t\t// ifModified key\n\t\t\tifModifiedKey,\n\t\t\t// Headers (they are sent all at once)\n\t\t\trequestHeaders = {},\n\t\t\t// Response headers\n\t\t\tresponseHeadersString,\n\t\t\tresponseHeaders,\n\t\t\t// transport\n\t\t\ttransport,\n\t\t\t// timeout handle\n\t\t\ttimeoutTimer,\n\t\t\t// Cross-domain detection vars\n\t\t\tparts,\n\t\t\t// The jqXHR state\n\t\t\tstate = 0,\n\t\t\t// To know if global events are to be dispatched\n\t\t\tfireGlobals,\n\t\t\t// Loop variable\n\t\t\ti,\n\t\t\t// Fake xhr\n\t\t\tjqXHR = {\n\n\t\t\t\treadyState: 0,\n\n\t\t\t\t// Caches the header\n\t\t\t\tsetRequestHeader: function( name, value ) {\n\t\t\t\t\tif ( !state ) {\n\t\t\t\t\t\trequestHeaders[ name.toLowerCase().replace( rucHeaders, rucHeadersFunc ) ] = value;\n\t\t\t\t\t}\n\t\t\t\t\treturn this;\n\t\t\t\t},\n\n\t\t\t\t// Raw string\n\t\t\t\tgetAllResponseHeaders: function() {\n\t\t\t\t\treturn state === 2 ? responseHeadersString : null;\n\t\t\t\t},\n\n\t\t\t\t// Builds headers hashtable if needed\n\t\t\t\tgetResponseHeader: function( key ) {\n\t\t\t\t\tvar match;\n\t\t\t\t\tif ( state === 2 ) {\n\t\t\t\t\t\tif ( !responseHeaders ) {\n\t\t\t\t\t\t\tresponseHeaders = {};\n\t\t\t\t\t\t\twhile( ( match = rheaders.exec( responseHeadersString ) ) ) {\n\t\t\t\t\t\t\t\tresponseHeaders[ match[1].toLowerCase() ] = match[ 2 ];\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tmatch = responseHeaders[ key.toLowerCase() ];\n\t\t\t\t\t}\n\t\t\t\t\treturn match === undefined ? null : match;\n\t\t\t\t},\n\n\t\t\t\t// Overrides response content-type header\n\t\t\t\toverrideMimeType: function( type ) {\n\t\t\t\t\tif ( !state ) {\n\t\t\t\t\t\ts.mimeType = type;\n\t\t\t\t\t}\n\t\t\t\t\treturn this;\n\t\t\t\t},\n\n\t\t\t\t// Cancel the request\n\t\t\t\tabort: function( statusText ) {\n\t\t\t\t\tstatusText = statusText || \"abort\";\n\t\t\t\t\tif ( transport ) {\n\t\t\t\t\t\ttransport.abort( statusText );\n\t\t\t\t\t}\n\t\t\t\t\tdone( 0, statusText );\n\t\t\t\t\treturn this;\n\t\t\t\t}\n\t\t\t};\n\n\t\t// Callback for when everything is done\n\t\t// It is defined here because jslint complains if it is declared\n\t\t// at the end of the function (which would be more logical and readable)\n\t\tfunction done( status, statusText, responses, headers ) {\n\n\t\t\t// Called once\n\t\t\tif ( state === 2 ) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// State is \"done\" now\n\t\t\tstate = 2;\n\n\t\t\t// Clear timeout if it exists\n\t\t\tif ( timeoutTimer ) {\n\t\t\t\tclearTimeout( timeoutTimer );\n\t\t\t}\n\n\t\t\t// Dereference transport for early garbage collection\n\t\t\t// (no matter how long the jqXHR object will be used)\n\t\t\ttransport = undefined;\n\n\t\t\t// Cache response headers\n\t\t\tresponseHeadersString = headers || \"\";\n\n\t\t\t// Set readyState\n\t\t\tjqXHR.readyState = status ? 4 : 0;\n\n\t\t\tvar isSuccess,\n\t\t\t\tsuccess,\n\t\t\t\terror,\n\t\t\t\tresponse = responses ? ajaxHandleResponses( s, jqXHR, responses ) : undefined,\n\t\t\t\tlastModified,\n\t\t\t\tetag;\n\n\t\t\t// If successful, handle type chaining\n\t\t\tif ( status >= 200 && status < 300 || status === 304 ) {\n\n\t\t\t\t// Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.\n\t\t\t\tif ( s.ifModified ) {\n\n\t\t\t\t\tif ( ( lastModified = jqXHR.getResponseHeader( \"Last-Modified\" ) ) ) {\n\t\t\t\t\t\tjQuery.lastModified[ ifModifiedKey ] = lastModified;\n\t\t\t\t\t}\n\t\t\t\t\tif ( ( etag = jqXHR.getResponseHeader( \"Etag\" ) ) ) {\n\t\t\t\t\t\tjQuery.etag[ ifModifiedKey ] = etag;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// If not modified\n\t\t\t\tif ( status === 304 ) {\n\n\t\t\t\t\tstatusText = \"notmodified\";\n\t\t\t\t\tisSuccess = true;\n\n\t\t\t\t// If we have data\n\t\t\t\t} else {\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\tsuccess = ajaxConvert( s, response );\n\t\t\t\t\t\tstatusText = \"success\";\n\t\t\t\t\t\tisSuccess = true;\n\t\t\t\t\t} catch(e) {\n\t\t\t\t\t\t// We have a parsererror\n\t\t\t\t\t\tstatusText = \"parsererror\";\n\t\t\t\t\t\terror = e;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// We extract error from statusText\n\t\t\t\t// then normalize statusText and status for non-aborts\n\t\t\t\terror = statusText;\n\t\t\t\tif( !statusText || status ) {\n\t\t\t\t\tstatusText = \"error\";\n\t\t\t\t\tif ( status < 0 ) {\n\t\t\t\t\t\tstatus = 0;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Set data for the fake xhr object\n\t\t\tjqXHR.status = status;\n\t\t\tjqXHR.statusText = statusText;\n\n\t\t\t// Success/Error\n\t\t\tif ( isSuccess ) {\n\t\t\t\tdeferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] );\n\t\t\t} else {\n\t\t\t\tdeferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] );\n\t\t\t}\n\n\t\t\t// Status-dependent callbacks\n\t\t\tjqXHR.statusCode( statusCode );\n\t\t\tstatusCode = undefined;\n\n\t\t\tif ( fireGlobals ) {\n\t\t\t\tglobalEventContext.trigger( \"ajax\" + ( isSuccess ? \"Success\" : \"Error\" ),\n\t\t\t\t\t\t[ jqXHR, s, isSuccess ? success : error ] );\n\t\t\t}\n\n\t\t\t// Complete\n\t\t\tcompleteDeferred.resolveWith( callbackContext, [ jqXHR, statusText ] );\n\n\t\t\tif ( fireGlobals ) {\n\t\t\t\tglobalEventContext.trigger( \"ajaxComplete\", [ jqXHR, s] );\n\t\t\t\t// Handle the global AJAX counter\n\t\t\t\tif ( !( --jQuery.active ) ) {\n\t\t\t\t\tjQuery.event.trigger( \"ajaxStop\" );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Attach deferreds\n\t\tdeferred.promise( jqXHR );\n\t\tjqXHR.success = jqXHR.done;\n\t\tjqXHR.error = jqXHR.fail;\n\t\tjqXHR.complete = completeDeferred.done;\n\n\t\t// Status-dependent callbacks\n\t\tjqXHR.statusCode = function( map ) {\n\t\t\tif ( map ) {\n\t\t\t\tvar tmp;\n\t\t\t\tif ( state < 2 ) {\n\t\t\t\t\tfor( tmp in map ) {\n\t\t\t\t\t\tstatusCode[ tmp ] = [ statusCode[tmp], map[tmp] ];\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\ttmp = map[ jqXHR.status ];\n\t\t\t\t\tjqXHR.then( tmp, tmp );\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn this;\n\t\t};\n\n\t\t// Remove hash character (#7531: and string promotion)\n\t\t// Add protocol if not provided (#5866: IE7 issue with protocol-less urls)\n\t\t// We also use the url parameter if available\n\t\ts.url = ( ( url || s.url ) + \"\" ).replace( rhash, \"\" ).replace( rprotocol, ajaxLocParts[ 1 ] + \"//\" );\n\n\t\t// Extract dataTypes list\n\t\ts.dataTypes = jQuery.trim( s.dataType || \"*\" ).toLowerCase().split( rspacesAjax );\n\n\t\t// Determine if a cross-domain request is in order\n\t\tif ( !s.crossDomain ) {\n\t\t\tparts = rurl.exec( s.url.toLowerCase() );\n\t\t\ts.crossDomain = !!( parts &&\n\t\t\t\t( parts[ 1 ] != ajaxLocParts[ 1 ] || parts[ 2 ] != ajaxLocParts[ 2 ] ||\n\t\t\t\t\t( parts[ 3 ] || ( parts[ 1 ] === \"http:\" ? 80 : 443 ) ) !=\n\t\t\t\t\t\t( ajaxLocParts[ 3 ] || ( ajaxLocParts[ 1 ] === \"http:\" ? 80 : 443 ) ) )\n\t\t\t);\n\t\t}\n\n\t\t// Convert data if not already a string\n\t\tif ( s.data && s.processData && typeof s.data !== \"string\" ) {\n\t\t\ts.data = jQuery.param( s.data, s.traditional );\n\t\t}\n\n\t\t// Apply prefilters\n\t\tinspectPrefiltersOrTransports( prefilters, s, options, jqXHR );\n\n\t\t// If request was aborted inside a prefiler, stop there\n\t\tif ( state === 2 ) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// We can fire global events as of now if asked to\n\t\tfireGlobals = s.global;\n\n\t\t// Uppercase the type\n\t\ts.type = s.type.toUpperCase();\n\n\t\t// Determine if request has content\n\t\ts.hasContent = !rnoContent.test( s.type );\n\n\t\t// Watch for a new set of requests\n\t\tif ( fireGlobals && jQuery.active++ === 0 ) {\n\t\t\tjQuery.event.trigger( \"ajaxStart\" );\n\t\t}\n\n\t\t// More options handling for requests with no content\n\t\tif ( !s.hasContent ) {\n\n\t\t\t// If data is available, append data to url\n\t\t\tif ( s.data ) {\n\t\t\t\ts.url += ( rquery.test( s.url ) ? \"&\" : \"?\" ) + s.data;\n\t\t\t}\n\n\t\t\t// Get ifModifiedKey before adding the anti-cache parameter\n\t\t\tifModifiedKey = s.url;\n\n\t\t\t// Add anti-cache in url if needed\n\t\t\tif ( s.cache === false ) {\n\n\t\t\t\tvar ts = jQuery.now(),\n\t\t\t\t\t// try replacing _= if it is there\n\t\t\t\t\tret = s.url.replace( rts, \"$1_=\" + ts );\n\n\t\t\t\t// if nothing was replaced, add timestamp to the end\n\t\t\t\ts.url = ret + ( (ret === s.url ) ? ( rquery.test( s.url ) ? \"&\" : \"?\" ) + \"_=\" + ts : \"\" );\n\t\t\t}\n\t\t}\n\n\t\t// Set the correct header, if data is being sent\n\t\tif ( s.data && s.hasContent && s.contentType !== false || options.contentType ) {\n\t\t\trequestHeaders[ \"Content-Type\" ] = s.contentType;\n\t\t}\n\n\t\t// Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.\n\t\tif ( s.ifModified ) {\n\t\t\tifModifiedKey = ifModifiedKey || s.url;\n\t\t\tif ( jQuery.lastModified[ ifModifiedKey ] ) {\n\t\t\t\trequestHeaders[ \"If-Modified-Since\" ] = jQuery.lastModified[ ifModifiedKey ];\n\t\t\t}\n\t\t\tif ( jQuery.etag[ ifModifiedKey ] ) {\n\t\t\t\trequestHeaders[ \"If-None-Match\" ] = jQuery.etag[ ifModifiedKey ];\n\t\t\t}\n\t\t}\n\n\t\t// Set the Accepts header for the server, depending on the dataType\n\t\trequestHeaders.Accept = s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[0] ] ?\n\t\t\ts.accepts[ s.dataTypes[0] ] + ( s.dataTypes[ 0 ] !== \"*\" ? \", */*; q=0.01\" : \"\" ) :\n\t\t\ts.accepts[ \"*\" ];\n\n\t\t// Check for headers option\n\t\tfor ( i in s.headers ) {\n\t\t\tjqXHR.setRequestHeader( i, s.headers[ i ] );\n\t\t}\n\n\t\t// Allow custom headers/mimetypes and early abort\n\t\tif ( s.beforeSend && ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || state === 2 ) ) {\n\t\t\t\t// Abort if not done already\n\t\t\t\tjqXHR.abort();\n\t\t\t\treturn false;\n\n\t\t}\n\n\t\t// Install callbacks on deferreds\n\t\tfor ( i in { success: 1, error: 1, complete: 1 } ) {\n\t\t\tjqXHR[ i ]( s[ i ] );\n\t\t}\n\n\t\t// Get transport\n\t\ttransport = inspectPrefiltersOrTransports( transports, s, options, jqXHR );\n\n\t\t// If no transport, we auto-abort\n\t\tif ( !transport ) {\n\t\t\tdone( -1, \"No Transport\" );\n\t\t} else {\n\t\t\tjqXHR.readyState = 1;\n\t\t\t// Send global event\n\t\t\tif ( fireGlobals ) {\n\t\t\t\tglobalEventContext.trigger( \"ajaxSend\", [ jqXHR, s ] );\n\t\t\t}\n\t\t\t// Timeout\n\t\t\tif ( s.async && s.timeout > 0 ) {\n\t\t\t\ttimeoutTimer = setTimeout( function(){\n\t\t\t\t\tjqXHR.abort( \"timeout\" );\n\t\t\t\t}, s.timeout );\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tstate = 1;\n\t\t\t\ttransport.send( requestHeaders, done );\n\t\t\t} catch (e) {\n\t\t\t\t// Propagate exception as error if not done\n\t\t\t\tif ( status < 2 ) {\n\t\t\t\t\tdone( -1, e );\n\t\t\t\t// Simply rethrow otherwise\n\t\t\t\t} else {\n\t\t\t\t\tjQuery.error( e );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn jqXHR;\n\t},\n\n\t// Serialize an array of form elements or a set of\n\t// key/values into a query string\n\tparam: function( a, traditional ) {\n\t\tvar s = [],\n\t\t\tadd = function( key, value ) {\n\t\t\t\t// If value is a function, invoke it and return its value\n\t\t\t\tvalue = jQuery.isFunction( value ) ? value() : value;\n\t\t\t\ts[ s.length ] = encodeURIComponent( key ) + \"=\" + encodeURIComponent( value );\n\t\t\t};\n\n\t\t// Set traditional to true for jQuery <= 1.3.2 behavior.\n\t\tif ( traditional === undefined ) {\n\t\t\ttraditional = jQuery.ajaxSettings.traditional;\n\t\t}\n\n\t\t// If an array was passed in, assume that it is an array of form elements.\n\t\tif ( jQuery.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) {\n\t\t\t// Serialize the form elements\n\t\t\tjQuery.each( a, function() {\n\t\t\t\tadd( this.name, this.value );\n\t\t\t} );\n\n\t\t} else {\n\t\t\t// If traditional, encode the \"old\" way (the way 1.3.2 or older\n\t\t\t// did it), otherwise encode params recursively.\n\t\t\tfor ( var prefix in a ) {\n\t\t\t\tbuildParams( prefix, a[ prefix ], traditional, add );\n\t\t\t}\n\t\t}\n\n\t\t// Return the resulting serialization\n\t\treturn s.join( \"&\" ).replace( r20, \"+\" );\n\t}\n});\n\nfunction buildParams( prefix, obj, traditional, add ) {\n\tif ( jQuery.isArray( obj ) && obj.length ) {\n\t\t// Serialize array item.\n\t\tjQuery.each( obj, function( i, v ) {\n\t\t\tif ( traditional || rbracket.test( prefix ) ) {\n\t\t\t\t// Treat each array item as a scalar.\n\t\t\t\tadd( prefix, v );\n\n\t\t\t} else {\n\t\t\t\t// If array item is non-scalar (array or object), encode its\n\t\t\t\t// numeric index to resolve deserialization ambiguity issues.\n\t\t\t\t// Note that rack (as of 1.0.0) can't currently deserialize\n\t\t\t\t// nested arrays properly, and attempting to do so may cause\n\t\t\t\t// a server error. Possible fixes are to modify rack's\n\t\t\t\t// deserialization algorithm or to provide an option or flag\n\t\t\t\t// to force array serialization to be shallow.\n\t\t\t\tbuildParams( prefix + \"[\" + ( typeof v === \"object\" || jQuery.isArray(v) ? i : \"\" ) + \"]\", v, traditional, add );\n\t\t\t}\n\t\t});\n\n\t} else if ( !traditional && obj != null && typeof obj === \"object\" ) {\n\t\t// If we see an array here, it is empty and should be treated as an empty\n\t\t// object\n\t\tif ( jQuery.isArray( obj ) || jQuery.isEmptyObject( obj ) ) {\n\t\t\tadd( prefix, \"\" );\n\n\t\t// Serialize object item.\n\t\t} else {\n\t\t\tfor ( var name in obj ) {\n\t\t\t\tbuildParams( prefix + \"[\" + name + \"]\", obj[ name ], traditional, add );\n\t\t\t}\n\t\t}\n\n\t} else {\n\t\t// Serialize scalar item.\n\t\tadd( prefix, obj );\n\t}\n}\n\n// This is still on the jQuery object... for now\n// Want to move this to jQuery.ajax some day\njQuery.extend({\n\n\t// Counter for holding the number of active queries\n\tactive: 0,\n\n\t// Last-Modified header cache for next request\n\tlastModified: {},\n\tetag: {}\n\n});\n\n/* Handles responses to an ajax request:\n * - sets all responseXXX fields accordingly\n * - finds the right dataType (mediates between content-type and expected dataType)\n * - returns the corresponding response\n */\nfunction ajaxHandleResponses( s, jqXHR, responses ) {\n\n\tvar contents = s.contents,\n\t\tdataTypes = s.dataTypes,\n\t\tresponseFields = s.responseFields,\n\t\tct,\n\t\ttype,\n\t\tfinalDataType,\n\t\tfirstDataType;\n\n\t// Fill responseXXX fields\n\tfor( type in responseFields ) {\n\t\tif ( type in responses ) {\n\t\t\tjqXHR[ responseFields[type] ] = responses[ type ];\n\t\t}\n\t}\n\n\t// Remove auto dataType and get content-type in the process\n\twhile( dataTypes[ 0 ] === \"*\" ) {\n\t\tdataTypes.shift();\n\t\tif ( ct === undefined ) {\n\t\t\tct = s.mimeType || jqXHR.getResponseHeader( \"content-type\" );\n\t\t}\n\t}\n\n\t// Check if we're dealing with a known content-type\n\tif ( ct ) {\n\t\tfor ( type in contents ) {\n\t\t\tif ( contents[ type ] && contents[ type ].test( ct ) ) {\n\t\t\t\tdataTypes.unshift( type );\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\t// Check to see if we have a response for the expected dataType\n\tif ( dataTypes[ 0 ] in responses ) {\n\t\tfinalDataType = dataTypes[ 0 ];\n\t} else {\n\t\t// Try convertible dataTypes\n\t\tfor ( type in responses ) {\n\t\t\tif ( !dataTypes[ 0 ] || s.converters[ type + \" \" + dataTypes[0] ] ) {\n\t\t\t\tfinalDataType = type;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif ( !firstDataType ) {\n\t\t\t\tfirstDataType = type;\n\t\t\t}\n\t\t}\n\t\t// Or just use first one\n\t\tfinalDataType = finalDataType || firstDataType;\n\t}\n\n\t// If we found a dataType\n\t// We add the dataType to the list if needed\n\t// and return the corresponding response\n\tif ( finalDataType ) {\n\t\tif ( finalDataType !== dataTypes[ 0 ] ) {\n\t\t\tdataTypes.unshift( finalDataType );\n\t\t}\n\t\treturn responses[ finalDataType ];\n\t}\n}\n\n// Chain conversions given the request and the original response\nfunction ajaxConvert( s, response ) {\n\n\t// Apply the dataFilter if provided\n\tif ( s.dataFilter ) {\n\t\tresponse = s.dataFilter( response, s.dataType );\n\t}\n\n\tvar dataTypes = s.dataTypes,\n\t\tconverters = {},\n\t\ti,\n\t\tkey,\n\t\tlength = dataTypes.length,\n\t\ttmp,\n\t\t// Current and previous dataTypes\n\t\tcurrent = dataTypes[ 0 ],\n\t\tprev,\n\t\t// Conversion expression\n\t\tconversion,\n\t\t// Conversion function\n\t\tconv,\n\t\t// Conversion functions (transitive conversion)\n\t\tconv1,\n\t\tconv2;\n\n\t// For each dataType in the chain\n\tfor( i = 1; i < length; i++ ) {\n\n\t\t// Create converters map\n\t\t// with lowercased keys\n\t\tif ( i === 1 ) {\n\t\t\tfor( key in s.converters ) {\n\t\t\t\tif( typeof key === \"string\" ) {\n\t\t\t\t\tconverters[ key.toLowerCase() ] = s.converters[ key ];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Get the dataTypes\n\t\tprev = current;\n\t\tcurrent = dataTypes[ i ];\n\n\t\t// If current is auto dataType, update it to prev\n\t\tif( current === \"*\" ) {\n\t\t\tcurrent = prev;\n\t\t// If no auto and dataTypes are actually different\n\t\t} else if ( prev !== \"*\" && prev !== current ) {\n\n\t\t\t// Get the converter\n\t\t\tconversion = prev + \" \" + current;\n\t\t\tconv = converters[ conversion ] || converters[ \"* \" + current ];\n\n\t\t\t// If there is no direct converter, search transitively\n\t\t\tif ( !conv ) {\n\t\t\t\tconv2 = undefined;\n\t\t\t\tfor( conv1 in converters ) {\n\t\t\t\t\ttmp = conv1.split( \" \" );\n\t\t\t\t\tif ( tmp[ 0 ] === prev || tmp[ 0 ] === \"*\" ) {\n\t\t\t\t\t\tconv2 = converters[ tmp[1] + \" \" + current ];\n\t\t\t\t\t\tif ( conv2 ) {\n\t\t\t\t\t\t\tconv1 = converters[ conv1 ];\n\t\t\t\t\t\t\tif ( conv1 === true ) {\n\t\t\t\t\t\t\t\tconv = conv2;\n\t\t\t\t\t\t\t} else if ( conv2 === true ) {\n\t\t\t\t\t\t\t\tconv = conv1;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t// If we found no converter, dispatch an error\n\t\t\tif ( !( conv || conv2 ) ) {\n\t\t\t\tjQuery.error( \"No conversion from \" + conversion.replace(\" \",\" to \") );\n\t\t\t}\n\t\t\t// If found converter is not an equivalence\n\t\t\tif ( conv !== true ) {\n\t\t\t\t// Convert with 1 or 2 converters accordingly\n\t\t\t\tresponse = conv ? conv( response ) : conv2( conv1(response) );\n\t\t\t}\n\t\t}\n\t}\n\treturn response;\n}\n\n\n\n\nvar jsc = jQuery.now(),\n\tjsre = /(\\=)\\?(&|$)|()\\?\\?()/i;\n\n// Default jsonp settings\njQuery.ajaxSetup({\n\tjsonp: \"callback\",\n\tjsonpCallback: function() {\n\t\treturn jQuery.expando + \"_\" + ( jsc++ );\n\t}\n});\n\n// Detect, normalize options and install callbacks for jsonp requests\njQuery.ajaxPrefilter( \"json jsonp\", function( s, originalSettings, jqXHR ) {\n\n\tvar dataIsString = ( typeof s.data === \"string\" );\n\n\tif ( s.dataTypes[ 0 ] === \"jsonp\" ||\n\t\toriginalSettings.jsonpCallback ||\n\t\toriginalSettings.jsonp != null ||\n\t\ts.jsonp !== false && ( jsre.test( s.url ) ||\n\t\t\t\tdataIsString && jsre.test( s.data ) ) ) {\n\n\t\tvar responseContainer,\n\t\t\tjsonpCallback = s.jsonpCallback =\n\t\t\t\tjQuery.isFunction( s.jsonpCallback ) ? s.jsonpCallback() : s.jsonpCallback,\n\t\t\tprevious = window[ jsonpCallback ],\n\t\t\turl = s.url,\n\t\t\tdata = s.data,\n\t\t\treplace = \"$1\" + jsonpCallback + \"$2\",\n\t\t\tcleanUp = function() {\n\t\t\t\t// Set callback back to previous value\n\t\t\t\twindow[ jsonpCallback ] = previous;\n\t\t\t\t// Call if it was a function and we have a response\n\t\t\t\tif ( responseContainer && jQuery.isFunction( previous ) ) {\n\t\t\t\t\twindow[ jsonpCallback ]( responseContainer[ 0 ] );\n\t\t\t\t}\n\t\t\t};\n\n\t\tif ( s.jsonp !== false ) {\n\t\t\turl = url.replace( jsre, replace );\n\t\t\tif ( s.url === url ) {\n\t\t\t\tif ( dataIsString ) {\n\t\t\t\t\tdata = data.replace( jsre, replace );\n\t\t\t\t}\n\t\t\t\tif ( s.data === data ) {\n\t\t\t\t\t// Add callback manually\n\t\t\t\t\turl += (/\\?/.test( url ) ? \"&\" : \"?\") + s.jsonp + \"=\" + jsonpCallback;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\ts.url = url;\n\t\ts.data = data;\n\n\t\t// Install callback\n\t\twindow[ jsonpCallback ] = function( response ) {\n\t\t\tresponseContainer = [ response ];\n\t\t};\n\n\t\t// Install cleanUp function\n\t\tjqXHR.then( cleanUp, cleanUp );\n\n\t\t// Use data converter to retrieve json after script execution\n\t\ts.converters[\"script json\"] = function() {\n\t\t\tif ( !responseContainer ) {\n\t\t\t\tjQuery.error( jsonpCallback + \" was not called\" );\n\t\t\t}\n\t\t\treturn responseContainer[ 0 ];\n\t\t};\n\n\t\t// force json dataType\n\t\ts.dataTypes[ 0 ] = \"json\";\n\n\t\t// Delegate to script\n\t\treturn \"script\";\n\t}\n} );\n\n\n\n\n// Install script dataType\njQuery.ajaxSetup({\n\taccepts: {\n\t\tscript: \"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript\"\n\t},\n\tcontents: {\n\t\tscript: /javascript|ecmascript/\n\t},\n\tconverters: {\n\t\t\"text script\": function( text ) {\n\t\t\tjQuery.globalEval( text );\n\t\t\treturn text;\n\t\t}\n\t}\n});\n\n// Handle cache's special case and global\njQuery.ajaxPrefilter( \"script\", function( s ) {\n\tif ( s.cache === undefined ) {\n\t\ts.cache = false;\n\t}\n\tif ( s.crossDomain ) {\n\t\ts.type = \"GET\";\n\t\ts.global = false;\n\t}\n} );\n\n// Bind script tag hack transport\njQuery.ajaxTransport( \"script\", function(s) {\n\n\t// This transport only deals with cross domain requests\n\tif ( s.crossDomain ) {\n\n\t\tvar script,\n\t\t\thead = document.head || document.getElementsByTagName( \"head\" )[0] || document.documentElement;\n\n\t\treturn {\n\n\t\t\tsend: function( _, callback ) {\n\n\t\t\t\tscript = document.createElement( \"script\" );\n\n\t\t\t\tscript.async = \"async\";\n\n\t\t\t\tif ( s.scriptCharset ) {\n\t\t\t\t\tscript.charset = s.scriptCharset;\n\t\t\t\t}\n\n\t\t\t\tscript.src = s.url;\n\n\t\t\t\t// Attach handlers for all browsers\n\t\t\t\tscript.onload = script.onreadystatechange = function( _, isAbort ) {\n\n\t\t\t\t\tif ( !script.readyState || /loaded|complete/.test( script.readyState ) ) {\n\n\t\t\t\t\t\t// Handle memory leak in IE\n\t\t\t\t\t\tscript.onload = script.onreadystatechange = null;\n\n\t\t\t\t\t\t// Remove the script\n\t\t\t\t\t\tif ( head && script.parentNode ) {\n\t\t\t\t\t\t\thead.removeChild( script );\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Dereference the script\n\t\t\t\t\t\tscript = undefined;\n\n\t\t\t\t\t\t// Callback if not abort\n\t\t\t\t\t\tif ( !isAbort ) {\n\t\t\t\t\t\t\tcallback( 200, \"success\" );\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t\t// Use insertBefore instead of appendChild  to circumvent an IE6 bug.\n\t\t\t\t// This arises when a base node is used (#2709 and #4378).\n\t\t\t\thead.insertBefore( script, head.firstChild );\n\t\t\t},\n\n\t\t\tabort: function() {\n\t\t\t\tif ( script ) {\n\t\t\t\t\tscript.onload( 0, 1 );\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\t}\n} );\n\n\n\n\nvar // #5280: next active xhr id and list of active xhrs' callbacks\n\txhrId = jQuery.now(),\n\txhrCallbacks,\n\n\t// XHR used to determine supports properties\n\ttestXHR;\n\n// #5280: Internet Explorer will keep connections alive if we don't abort on unload\nfunction xhrOnUnloadAbort() {\n\tjQuery( window ).unload(function() {\n\t\t// Abort all pending requests\n\t\tfor ( var key in xhrCallbacks ) {\n\t\t\txhrCallbacks[ key ]( 0, 1 );\n\t\t}\n\t});\n}\n\n// Functions to create xhrs\nfunction createStandardXHR() {\n\ttry {\n\t\treturn new window.XMLHttpRequest();\n\t} catch( e ) {}\n}\n\nfunction createActiveXHR() {\n\ttry {\n\t\treturn new window.ActiveXObject( \"Microsoft.XMLHTTP\" );\n\t} catch( e ) {}\n}\n\n// Create the request object\n// (This is still attached to ajaxSettings for backward compatibility)\njQuery.ajaxSettings.xhr = window.ActiveXObject ?\n\t/* Microsoft failed to properly\n\t * implement the XMLHttpRequest in IE7 (can't request local files),\n\t * so we use the ActiveXObject when it is available\n\t * Additionally XMLHttpRequest can be disabled in IE7/IE8 so\n\t * we need a fallback.\n\t */\n\tfunction() {\n\t\treturn !this.isLocal && createStandardXHR() || createActiveXHR();\n\t} :\n\t// For all other browsers, use the standard XMLHttpRequest object\n\tcreateStandardXHR;\n\n// Test if we can create an xhr object\ntestXHR = jQuery.ajaxSettings.xhr();\njQuery.support.ajax = !!testXHR;\n\n// Does this browser support crossDomain XHR requests\njQuery.support.cors = testXHR && ( \"withCredentials\" in testXHR );\n\n// No need for the temporary xhr anymore\ntestXHR = undefined;\n\n// Create transport if the browser can provide an xhr\nif ( jQuery.support.ajax ) {\n\n\tjQuery.ajaxTransport(function( s ) {\n\t\t// Cross domain only allowed if supported through XMLHttpRequest\n\t\tif ( !s.crossDomain || jQuery.support.cors ) {\n\n\t\t\tvar callback;\n\n\t\t\treturn {\n\t\t\t\tsend: function( headers, complete ) {\n\n\t\t\t\t\t// Get a new xhr\n\t\t\t\t\tvar xhr = s.xhr(),\n\t\t\t\t\t\thandle,\n\t\t\t\t\t\ti;\n\n\t\t\t\t\t// Open the socket\n\t\t\t\t\t// Passing null username, generates a login popup on Opera (#2865)\n\t\t\t\t\tif ( s.username ) {\n\t\t\t\t\t\txhr.open( s.type, s.url, s.async, s.username, s.password );\n\t\t\t\t\t} else {\n\t\t\t\t\t\txhr.open( s.type, s.url, s.async );\n\t\t\t\t\t}\n\n\t\t\t\t\t// Apply custom fields if provided\n\t\t\t\t\tif ( s.xhrFields ) {\n\t\t\t\t\t\tfor ( i in s.xhrFields ) {\n\t\t\t\t\t\t\txhr[ i ] = s.xhrFields[ i ];\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Override mime type if needed\n\t\t\t\t\tif ( s.mimeType && xhr.overrideMimeType ) {\n\t\t\t\t\t\txhr.overrideMimeType( s.mimeType );\n\t\t\t\t\t}\n\n\t\t\t\t\t// Requested-With header\n\t\t\t\t\t// Not set for crossDomain requests with no content\n\t\t\t\t\t// (see why at http://trac.dojotoolkit.org/ticket/9486)\n\t\t\t\t\t// Won't change header if already provided\n\t\t\t\t\tif ( !( s.crossDomain && !s.hasContent ) && !headers[\"X-Requested-With\"] ) {\n\t\t\t\t\t\theaders[ \"X-Requested-With\" ] = \"XMLHttpRequest\";\n\t\t\t\t\t}\n\n\t\t\t\t\t// Need an extra try/catch for cross domain requests in Firefox 3\n\t\t\t\t\ttry {\n\t\t\t\t\t\tfor ( i in headers ) {\n\t\t\t\t\t\t\txhr.setRequestHeader( i, headers[ i ] );\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch( _ ) {}\n\n\t\t\t\t\t// Do send the request\n\t\t\t\t\t// This may raise an exception which is actually\n\t\t\t\t\t// handled in jQuery.ajax (so no try/catch here)\n\t\t\t\t\txhr.send( ( s.hasContent && s.data ) || null );\n\n\t\t\t\t\t// Listener\n\t\t\t\t\tcallback = function( _, isAbort ) {\n\n\t\t\t\t\t\tvar status,\n\t\t\t\t\t\t\tstatusText,\n\t\t\t\t\t\t\tresponseHeaders,\n\t\t\t\t\t\t\tresponses,\n\t\t\t\t\t\t\txml;\n\n\t\t\t\t\t\t// Firefox throws exceptions when accessing properties\n\t\t\t\t\t\t// of an xhr when a network error occured\n\t\t\t\t\t\t// http://helpful.knobs-dials.com/index.php/Component_returned_failure_code:_0x80040111_(NS_ERROR_NOT_AVAILABLE)\n\t\t\t\t\t\ttry {\n\n\t\t\t\t\t\t\t// Was never called and is aborted or complete\n\t\t\t\t\t\t\tif ( callback && ( isAbort || xhr.readyState === 4 ) ) {\n\n\t\t\t\t\t\t\t\t// Only called once\n\t\t\t\t\t\t\t\tcallback = undefined;\n\n\t\t\t\t\t\t\t\t// Do not keep as active anymore\n\t\t\t\t\t\t\t\tif ( handle ) {\n\t\t\t\t\t\t\t\t\txhr.onreadystatechange = jQuery.noop;\n\t\t\t\t\t\t\t\t\tdelete xhrCallbacks[ handle ];\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// If it's an abort\n\t\t\t\t\t\t\t\tif ( isAbort ) {\n\t\t\t\t\t\t\t\t\t// Abort it manually if needed\n\t\t\t\t\t\t\t\t\tif ( xhr.readyState !== 4 ) {\n\t\t\t\t\t\t\t\t\t\txhr.abort();\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tstatus = xhr.status;\n\t\t\t\t\t\t\t\t\tresponseHeaders = xhr.getAllResponseHeaders();\n\t\t\t\t\t\t\t\t\tresponses = {};\n\t\t\t\t\t\t\t\t\txml = xhr.responseXML;\n\n\t\t\t\t\t\t\t\t\t// Construct response list\n\t\t\t\t\t\t\t\t\tif ( xml && xml.documentElement /* #4958 */ ) {\n\t\t\t\t\t\t\t\t\t\tresponses.xml = xml;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tresponses.text = xhr.responseText;\n\n\t\t\t\t\t\t\t\t\t// Firefox throws an exception when accessing\n\t\t\t\t\t\t\t\t\t// statusText for faulty cross-domain requests\n\t\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\t\tstatusText = xhr.statusText;\n\t\t\t\t\t\t\t\t\t} catch( e ) {\n\t\t\t\t\t\t\t\t\t\t// We normalize with Webkit giving an empty statusText\n\t\t\t\t\t\t\t\t\t\tstatusText = \"\";\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\t// Filter status for non standard behaviors\n\n\t\t\t\t\t\t\t\t\t// If the request is local and we have data: assume a success\n\t\t\t\t\t\t\t\t\t// (success with no data won't get notified, that's the best we\n\t\t\t\t\t\t\t\t\t// can do given current implementations)\n\t\t\t\t\t\t\t\t\tif ( !status && s.isLocal && !s.crossDomain ) {\n\t\t\t\t\t\t\t\t\t\tstatus = responses.text ? 200 : 404;\n\t\t\t\t\t\t\t\t\t// IE - #1450: sometimes returns 1223 when it should be 204\n\t\t\t\t\t\t\t\t\t} else if ( status === 1223 ) {\n\t\t\t\t\t\t\t\t\t\tstatus = 204;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} catch( firefoxAccessException ) {\n\t\t\t\t\t\t\tif ( !isAbort ) {\n\t\t\t\t\t\t\t\tcomplete( -1, firefoxAccessException );\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Call complete if needed\n\t\t\t\t\t\tif ( responses ) {\n\t\t\t\t\t\t\tcomplete( status, statusText, responses, responseHeaders );\n\t\t\t\t\t\t}\n\t\t\t\t\t};\n\n\t\t\t\t\t// if we're in sync mode or it's in cache\n\t\t\t\t\t// and has been retrieved directly (IE6 & IE7)\n\t\t\t\t\t// we need to manually fire the callback\n\t\t\t\t\tif ( !s.async || xhr.readyState === 4 ) {\n\t\t\t\t\t\tcallback();\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Create the active xhrs callbacks list if needed\n\t\t\t\t\t\t// and attach the unload handler\n\t\t\t\t\t\tif ( !xhrCallbacks ) {\n\t\t\t\t\t\t\txhrCallbacks = {};\n\t\t\t\t\t\t\txhrOnUnloadAbort();\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// Add to list of active xhrs callbacks\n\t\t\t\t\t\thandle = xhrId++;\n\t\t\t\t\t\txhr.onreadystatechange = xhrCallbacks[ handle ] = callback;\n\t\t\t\t\t}\n\t\t\t\t},\n\n\t\t\t\tabort: function() {\n\t\t\t\t\tif ( callback ) {\n\t\t\t\t\t\tcallback(0,1);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\t});\n}\n\n\n\n\nvar elemdisplay = {},\n\trfxtypes = /^(?:toggle|show|hide)$/,\n\trfxnum = /^([+\\-]=)?([\\d+.\\-]+)([a-z%]*)$/i,\n\ttimerId,\n\tfxAttrs = [\n\t\t// height animations\n\t\t[ \"height\", \"marginTop\", \"marginBottom\", \"paddingTop\", \"paddingBottom\" ],\n\t\t// width animations\n\t\t[ \"width\", \"marginLeft\", \"marginRight\", \"paddingLeft\", \"paddingRight\" ],\n\t\t// opacity animations\n\t\t[ \"opacity\" ]\n\t];\n\njQuery.fn.extend({\n\tshow: function( speed, easing, callback ) {\n\t\tvar elem, display;\n\n\t\tif ( speed || speed === 0 ) {\n\t\t\treturn this.animate( genFx(\"show\", 3), speed, easing, callback);\n\n\t\t} else {\n\t\t\tfor ( var i = 0, j = this.length; i < j; i++ ) {\n\t\t\t\telem = this[i];\n\t\t\t\tdisplay = elem.style.display;\n\n\t\t\t\t// Reset the inline display of this element to learn if it is\n\t\t\t\t// being hidden by cascaded rules or not\n\t\t\t\tif ( !jQuery._data(elem, \"olddisplay\") && display === \"none\" ) {\n\t\t\t\t\tdisplay = elem.style.display = \"\";\n\t\t\t\t}\n\n\t\t\t\t// Set elements which have been overridden with display: none\n\t\t\t\t// in a stylesheet to whatever the default browser style is\n\t\t\t\t// for such an element\n\t\t\t\tif ( display === \"\" && jQuery.css( elem, \"display\" ) === \"none\" ) {\n\t\t\t\t\tjQuery._data(elem, \"olddisplay\", defaultDisplay(elem.nodeName));\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Set the display of most of the elements in a second loop\n\t\t\t// to avoid the constant reflow\n\t\t\tfor ( i = 0; i < j; i++ ) {\n\t\t\t\telem = this[i];\n\t\t\t\tdisplay = elem.style.display;\n\n\t\t\t\tif ( display === \"\" || display === \"none\" ) {\n\t\t\t\t\telem.style.display = jQuery._data(elem, \"olddisplay\") || \"\";\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn this;\n\t\t}\n\t},\n\n\thide: function( speed, easing, callback ) {\n\t\tif ( speed || speed === 0 ) {\n\t\t\treturn this.animate( genFx(\"hide\", 3), speed, easing, callback);\n\n\t\t} else {\n\t\t\tfor ( var i = 0, j = this.length; i < j; i++ ) {\n\t\t\t\tvar display = jQuery.css( this[i], \"display\" );\n\n\t\t\t\tif ( display !== \"none\" && !jQuery._data( this[i], \"olddisplay\" ) ) {\n\t\t\t\t\tjQuery._data( this[i], \"olddisplay\", display );\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Set the display of the elements in a second loop\n\t\t\t// to avoid the constant reflow\n\t\t\tfor ( i = 0; i < j; i++ ) {\n\t\t\t\tthis[i].style.display = \"none\";\n\t\t\t}\n\n\t\t\treturn this;\n\t\t}\n\t},\n\n\t// Save the old toggle function\n\t_toggle: jQuery.fn.toggle,\n\n\ttoggle: function( fn, fn2, callback ) {\n\t\tvar bool = typeof fn === \"boolean\";\n\n\t\tif ( jQuery.isFunction(fn) && jQuery.isFunction(fn2) ) {\n\t\t\tthis._toggle.apply( this, arguments );\n\n\t\t} else if ( fn == null || bool ) {\n\t\t\tthis.each(function() {\n\t\t\t\tvar state = bool ? fn : jQuery(this).is(\":hidden\");\n\t\t\t\tjQuery(this)[ state ? \"show\" : \"hide\" ]();\n\t\t\t});\n\n\t\t} else {\n\t\t\tthis.animate(genFx(\"toggle\", 3), fn, fn2, callback);\n\t\t}\n\n\t\treturn this;\n\t},\n\n\tfadeTo: function( speed, to, easing, callback ) {\n\t\treturn this.filter(\":hidden\").css(\"opacity\", 0).show().end()\n\t\t\t\t\t.animate({opacity: to}, speed, easing, callback);\n\t},\n\n\tanimate: function( prop, speed, easing, callback ) {\n\t\tvar optall = jQuery.speed(speed, easing, callback);\n\n\t\tif ( jQuery.isEmptyObject( prop ) ) {\n\t\t\treturn this.each( optall.complete );\n\t\t}\n\n\t\treturn this[ optall.queue === false ? \"each\" : \"queue\" ](function() {\n\t\t\t// XXX 'this' does not always have a nodeName when running the\n\t\t\t// test suite\n\n\t\t\tvar opt = jQuery.extend({}, optall), p,\n\t\t\t\tisElement = this.nodeType === 1,\n\t\t\t\thidden = isElement && jQuery(this).is(\":hidden\"),\n\t\t\t\tself = this;\n\n\t\t\tfor ( p in prop ) {\n\t\t\t\tvar name = jQuery.camelCase( p );\n\n\t\t\t\tif ( p !== name ) {\n\t\t\t\t\tprop[ name ] = prop[ p ];\n\t\t\t\t\tdelete prop[ p ];\n\t\t\t\t\tp = name;\n\t\t\t\t}\n\n\t\t\t\tif ( prop[p] === \"hide\" && hidden || prop[p] === \"show\" && !hidden ) {\n\t\t\t\t\treturn opt.complete.call(this);\n\t\t\t\t}\n\n\t\t\t\tif ( isElement && ( p === \"height\" || p === \"width\" ) ) {\n\t\t\t\t\t// Make sure that nothing sneaks out\n\t\t\t\t\t// Record all 3 overflow attributes because IE does not\n\t\t\t\t\t// change the overflow attribute when overflowX and\n\t\t\t\t\t// overflowY are set to the same value\n\t\t\t\t\topt.overflow = [ this.style.overflow, this.style.overflowX, this.style.overflowY ];\n\n\t\t\t\t\t// Set display property to inline-block for height/width\n\t\t\t\t\t// animations on inline elements that are having width/height\n\t\t\t\t\t// animated\n\t\t\t\t\tif ( jQuery.css( this, \"display\" ) === \"inline\" &&\n\t\t\t\t\t\t\tjQuery.css( this, \"float\" ) === \"none\" ) {\n\t\t\t\t\t\tif ( !jQuery.support.inlineBlockNeedsLayout ) {\n\t\t\t\t\t\t\tthis.style.display = \"inline-block\";\n\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tvar display = defaultDisplay(this.nodeName);\n\n\t\t\t\t\t\t\t// inline-level elements accept inline-block;\n\t\t\t\t\t\t\t// block-level elements need to be inline with layout\n\t\t\t\t\t\t\tif ( display === \"inline\" ) {\n\t\t\t\t\t\t\t\tthis.style.display = \"inline-block\";\n\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tthis.style.display = \"inline\";\n\t\t\t\t\t\t\t\tthis.style.zoom = 1;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif ( jQuery.isArray( prop[p] ) ) {\n\t\t\t\t\t// Create (if needed) and add to specialEasing\n\t\t\t\t\t(opt.specialEasing = opt.specialEasing || {})[p] = prop[p][1];\n\t\t\t\t\tprop[p] = prop[p][0];\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif ( opt.overflow != null ) {\n\t\t\t\tthis.style.overflow = \"hidden\";\n\t\t\t}\n\n\t\t\topt.curAnim = jQuery.extend({}, prop);\n\n\t\t\tjQuery.each( prop, function( name, val ) {\n\t\t\t\tvar e = new jQuery.fx( self, opt, name );\n\n\t\t\t\tif ( rfxtypes.test(val) ) {\n\t\t\t\t\te[ val === \"toggle\" ? hidden ? \"show\" : \"hide\" : val ]( prop );\n\n\t\t\t\t} else {\n\t\t\t\t\tvar parts = rfxnum.exec(val),\n\t\t\t\t\t\tstart = e.cur();\n\n\t\t\t\t\tif ( parts ) {\n\t\t\t\t\t\tvar end = parseFloat( parts[2] ),\n\t\t\t\t\t\t\tunit = parts[3] || ( jQuery.cssNumber[ name ] ? \"\" : \"px\" );\n\n\t\t\t\t\t\t// We need to compute starting value\n\t\t\t\t\t\tif ( unit !== \"px\" ) {\n\t\t\t\t\t\t\tjQuery.style( self, name, (end || 1) + unit);\n\t\t\t\t\t\t\tstart = ((end || 1) / e.cur()) * start;\n\t\t\t\t\t\t\tjQuery.style( self, name, start + unit);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// If a +=/-= token was provided, we're doing a relative animation\n\t\t\t\t\t\tif ( parts[1] ) {\n\t\t\t\t\t\t\tend = ((parts[1] === \"-=\" ? -1 : 1) * end) + start;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\te.custom( start, end, unit );\n\n\t\t\t\t\t} else {\n\t\t\t\t\t\te.custom( start, val, \"\" );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\n\t\t\t// For JS strict compliance\n\t\t\treturn true;\n\t\t});\n\t},\n\n\tstop: function( clearQueue, gotoEnd ) {\n\t\tvar timers = jQuery.timers;\n\n\t\tif ( clearQueue ) {\n\t\t\tthis.queue([]);\n\t\t}\n\n\t\tthis.each(function() {\n\t\t\t// go in reverse order so anything added to the queue during the loop is ignored\n\t\t\tfor ( var i = timers.length - 1; i >= 0; i-- ) {\n\t\t\t\tif ( timers[i].elem === this ) {\n\t\t\t\t\tif (gotoEnd) {\n\t\t\t\t\t\t// force the next step to be the last\n\t\t\t\t\t\ttimers[i](true);\n\t\t\t\t\t}\n\n\t\t\t\t\ttimers.splice(i, 1);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\t// start the next in the queue if the last step wasn't forced\n\t\tif ( !gotoEnd ) {\n\t\t\tthis.dequeue();\n\t\t}\n\n\t\treturn this;\n\t}\n\n});\n\nfunction genFx( type, num ) {\n\tvar obj = {};\n\n\tjQuery.each( fxAttrs.concat.apply([], fxAttrs.slice(0,num)), function() {\n\t\tobj[ this ] = type;\n\t});\n\n\treturn obj;\n}\n\n// Generate shortcuts for custom animations\njQuery.each({\n\tslideDown: genFx(\"show\", 1),\n\tslideUp: genFx(\"hide\", 1),\n\tslideToggle: genFx(\"toggle\", 1),\n\tfadeIn: { opacity: \"show\" },\n\tfadeOut: { opacity: \"hide\" },\n\tfadeToggle: { opacity: \"toggle\" }\n}, function( name, props ) {\n\tjQuery.fn[ name ] = function( speed, easing, callback ) {\n\t\treturn this.animate( props, speed, easing, callback );\n\t};\n});\n\njQuery.extend({\n\tspeed: function( speed, easing, fn ) {\n\t\tvar opt = speed && typeof speed === \"object\" ? jQuery.extend({}, speed) : {\n\t\t\tcomplete: fn || !fn && easing ||\n\t\t\t\tjQuery.isFunction( speed ) && speed,\n\t\t\tduration: speed,\n\t\t\teasing: fn && easing || easing && !jQuery.isFunction(easing) && easing\n\t\t};\n\n\t\topt.duration = jQuery.fx.off ? 0 : typeof opt.duration === \"number\" ? opt.duration :\n\t\t\topt.duration in jQuery.fx.speeds ? jQuery.fx.speeds[opt.duration] : jQuery.fx.speeds._default;\n\n\t\t// Queueing\n\t\topt.old = opt.complete;\n\t\topt.complete = function() {\n\t\t\tif ( opt.queue !== false ) {\n\t\t\t\tjQuery(this).dequeue();\n\t\t\t}\n\t\t\tif ( jQuery.isFunction( opt.old ) ) {\n\t\t\t\topt.old.call( this );\n\t\t\t}\n\t\t};\n\n\t\treturn opt;\n\t},\n\n\teasing: {\n\t\tlinear: function( p, n, firstNum, diff ) {\n\t\t\treturn firstNum + diff * p;\n\t\t},\n\t\tswing: function( p, n, firstNum, diff ) {\n\t\t\treturn ((-Math.cos(p*Math.PI)/2) + 0.5) * diff + firstNum;\n\t\t}\n\t},\n\n\ttimers: [],\n\n\tfx: function( elem, options, prop ) {\n\t\tthis.options = options;\n\t\tthis.elem = elem;\n\t\tthis.prop = prop;\n\n\t\tif ( !options.orig ) {\n\t\t\toptions.orig = {};\n\t\t}\n\t}\n\n});\n\njQuery.fx.prototype = {\n\t// Simple function for setting a style value\n\tupdate: function() {\n\t\tif ( this.options.step ) {\n\t\t\tthis.options.step.call( this.elem, this.now, this );\n\t\t}\n\n\t\t(jQuery.fx.step[this.prop] || jQuery.fx.step._default)( this );\n\t},\n\n\t// Get the current size\n\tcur: function() {\n\t\tif ( this.elem[this.prop] != null && (!this.elem.style || this.elem.style[this.prop] == null) ) {\n\t\t\treturn this.elem[ this.prop ];\n\t\t}\n\n\t\tvar parsed,\n\t\t\tr = jQuery.css( this.elem, this.prop );\n\t\t// Empty strings, null, undefined and \"auto\" are converted to 0,\n\t\t// complex values such as \"rotate(1rad)\" are returned as is,\n\t\t// simple values such as \"10px\" are parsed to Float.\n\t\treturn isNaN( parsed = parseFloat( r ) ) ? !r || r === \"auto\" ? 0 : r : parsed;\n\t},\n\n\t// Start an animation from one number to another\n\tcustom: function( from, to, unit ) {\n\t\tvar self = this,\n\t\t\tfx = jQuery.fx;\n\n\t\tthis.startTime = jQuery.now();\n\t\tthis.start = from;\n\t\tthis.end = to;\n\t\tthis.unit = unit || this.unit || ( jQuery.cssNumber[ this.prop ] ? \"\" : \"px\" );\n\t\tthis.now = this.start;\n\t\tthis.pos = this.state = 0;\n\n\t\tfunction t( gotoEnd ) {\n\t\t\treturn self.step(gotoEnd);\n\t\t}\n\n\t\tt.elem = this.elem;\n\n\t\tif ( t() && jQuery.timers.push(t) && !timerId ) {\n\t\t\ttimerId = setInterval(fx.tick, fx.interval);\n\t\t}\n\t},\n\n\t// Simple 'show' function\n\tshow: function() {\n\t\t// Remember where we started, so that we can go back to it later\n\t\tthis.options.orig[this.prop] = jQuery.style( this.elem, this.prop );\n\t\tthis.options.show = true;\n\n\t\t// Begin the animation\n\t\t// Make sure that we start at a small width/height to avoid any\n\t\t// flash of content\n\t\tthis.custom(this.prop === \"width\" || this.prop === \"height\" ? 1 : 0, this.cur());\n\n\t\t// Start by showing the element\n\t\tjQuery( this.elem ).show();\n\t},\n\n\t// Simple 'hide' function\n\thide: function() {\n\t\t// Remember where we started, so that we can go back to it later\n\t\tthis.options.orig[this.prop] = jQuery.style( this.elem, this.prop );\n\t\tthis.options.hide = true;\n\n\t\t// Begin the animation\n\t\tthis.custom(this.cur(), 0);\n\t},\n\n\t// Each step of an animation\n\tstep: function( gotoEnd ) {\n\t\tvar t = jQuery.now(), done = true;\n\n\t\tif ( gotoEnd || t >= this.options.duration + this.startTime ) {\n\t\t\tthis.now = this.end;\n\t\t\tthis.pos = this.state = 1;\n\t\t\tthis.update();\n\n\t\t\tthis.options.curAnim[ this.prop ] = true;\n\n\t\t\tfor ( var i in this.options.curAnim ) {\n\t\t\t\tif ( this.options.curAnim[i] !== true ) {\n\t\t\t\t\tdone = false;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif ( done ) {\n\t\t\t\t// Reset the overflow\n\t\t\t\tif ( this.options.overflow != null && !jQuery.support.shrinkWrapBlocks ) {\n\t\t\t\t\tvar elem = this.elem,\n\t\t\t\t\t\toptions = this.options;\n\n\t\t\t\t\tjQuery.each( [ \"\", \"X\", \"Y\" ], function (index, value) {\n\t\t\t\t\t\telem.style[ \"overflow\" + value ] = options.overflow[index];\n\t\t\t\t\t} );\n\t\t\t\t}\n\n\t\t\t\t// Hide the element if the \"hide\" operation was done\n\t\t\t\tif ( this.options.hide ) {\n\t\t\t\t\tjQuery(this.elem).hide();\n\t\t\t\t}\n\n\t\t\t\t// Reset the properties, if the item has been hidden or shown\n\t\t\t\tif ( this.options.hide || this.options.show ) {\n\t\t\t\t\tfor ( var p in this.options.curAnim ) {\n\t\t\t\t\t\tjQuery.style( this.elem, p, this.options.orig[p] );\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Execute the complete function\n\t\t\t\tthis.options.complete.call( this.elem );\n\t\t\t}\n\n\t\t\treturn false;\n\n\t\t} else {\n\t\t\tvar n = t - this.startTime;\n\t\t\tthis.state = n / this.options.duration;\n\n\t\t\t// Perform the easing function, defaults to swing\n\t\t\tvar specialEasing = this.options.specialEasing && this.options.specialEasing[this.prop];\n\t\t\tvar defaultEasing = this.options.easing || (jQuery.easing.swing ? \"swing\" : \"linear\");\n\t\t\tthis.pos = jQuery.easing[specialEasing || defaultEasing](this.state, n, 0, 1, this.options.duration);\n\t\t\tthis.now = this.start + ((this.end - this.start) * this.pos);\n\n\t\t\t// Perform the next step of the animation\n\t\t\tthis.update();\n\t\t}\n\n\t\treturn true;\n\t}\n};\n\njQuery.extend( jQuery.fx, {\n\ttick: function() {\n\t\tvar timers = jQuery.timers;\n\n\t\tfor ( var i = 0; i < timers.length; i++ ) {\n\t\t\tif ( !timers[i]() ) {\n\t\t\t\ttimers.splice(i--, 1);\n\t\t\t}\n\t\t}\n\n\t\tif ( !timers.length ) {\n\t\t\tjQuery.fx.stop();\n\t\t}\n\t},\n\n\tinterval: 13,\n\n\tstop: function() {\n\t\tclearInterval( timerId );\n\t\ttimerId = null;\n\t},\n\n\tspeeds: {\n\t\tslow: 600,\n\t\tfast: 200,\n\t\t// Default speed\n\t\t_default: 400\n\t},\n\n\tstep: {\n\t\topacity: function( fx ) {\n\t\t\tjQuery.style( fx.elem, \"opacity\", fx.now );\n\t\t},\n\n\t\t_default: function( fx ) {\n\t\t\tif ( fx.elem.style && fx.elem.style[ fx.prop ] != null ) {\n\t\t\t\tfx.elem.style[ fx.prop ] = (fx.prop === \"width\" || fx.prop === \"height\" ? Math.max(0, fx.now) : fx.now) + fx.unit;\n\t\t\t} else {\n\t\t\t\tfx.elem[ fx.prop ] = fx.now;\n\t\t\t}\n\t\t}\n\t}\n});\n\nif ( jQuery.expr && jQuery.expr.filters ) {\n\tjQuery.expr.filters.animated = function( elem ) {\n\t\treturn jQuery.grep(jQuery.timers, function( fn ) {\n\t\t\treturn elem === fn.elem;\n\t\t}).length;\n\t};\n}\n\nfunction defaultDisplay( nodeName ) {\n\tif ( !elemdisplay[ nodeName ] ) {\n\t\tvar elem = jQuery(\"<\" + nodeName + \">\").appendTo(\"body\"),\n\t\t\tdisplay = elem.css(\"display\");\n\n\t\telem.remove();\n\n\t\tif ( display === \"none\" || display === \"\" ) {\n\t\t\tdisplay = \"block\";\n\t\t}\n\n\t\telemdisplay[ nodeName ] = display;\n\t}\n\n\treturn elemdisplay[ nodeName ];\n}\n\n\n\n\nvar rtable = /^t(?:able|d|h)$/i,\n\trroot = /^(?:body|html)$/i;\n\nif ( \"getBoundingClientRect\" in document.documentElement ) {\n\tjQuery.fn.offset = function( options ) {\n\t\tvar elem = this[0], box;\n\n\t\tif ( options ) {\n\t\t\treturn this.each(function( i ) {\n\t\t\t\tjQuery.offset.setOffset( this, options, i );\n\t\t\t});\n\t\t}\n\n\t\tif ( !elem || !elem.ownerDocument ) {\n\t\t\treturn null;\n\t\t}\n\n\t\tif ( elem === elem.ownerDocument.body ) {\n\t\t\treturn jQuery.offset.bodyOffset( elem );\n\t\t}\n\n\t\ttry {\n\t\t\tbox = elem.getBoundingClientRect();\n\t\t} catch(e) {}\n\n\t\tvar doc = elem.ownerDocument,\n\t\t\tdocElem = doc.documentElement;\n\n\t\t// Make sure we're not dealing with a disconnected DOM node\n\t\tif ( !box || !jQuery.contains( docElem, elem ) ) {\n\t\t\treturn box ? { top: box.top, left: box.left } : { top: 0, left: 0 };\n\t\t}\n\n\t\tvar body = doc.body,\n\t\t\twin = getWindow(doc),\n\t\t\tclientTop  = docElem.clientTop  || body.clientTop  || 0,\n\t\t\tclientLeft = docElem.clientLeft || body.clientLeft || 0,\n\t\t\tscrollTop  = (win.pageYOffset || jQuery.support.boxModel && docElem.scrollTop  || body.scrollTop ),\n\t\t\tscrollLeft = (win.pageXOffset || jQuery.support.boxModel && docElem.scrollLeft || body.scrollLeft),\n\t\t\ttop  = box.top  + scrollTop  - clientTop,\n\t\t\tleft = box.left + scrollLeft - clientLeft;\n\n\t\treturn { top: top, left: left };\n\t};\n\n} else {\n\tjQuery.fn.offset = function( options ) {\n\t\tvar elem = this[0];\n\n\t\tif ( options ) {\n\t\t\treturn this.each(function( i ) {\n\t\t\t\tjQuery.offset.setOffset( this, options, i );\n\t\t\t});\n\t\t}\n\n\t\tif ( !elem || !elem.ownerDocument ) {\n\t\t\treturn null;\n\t\t}\n\n\t\tif ( elem === elem.ownerDocument.body ) {\n\t\t\treturn jQuery.offset.bodyOffset( elem );\n\t\t}\n\n\t\tjQuery.offset.initialize();\n\n\t\tvar computedStyle,\n\t\t\toffsetParent = elem.offsetParent,\n\t\t\tprevOffsetParent = elem,\n\t\t\tdoc = elem.ownerDocument,\n\t\t\tdocElem = doc.documentElement,\n\t\t\tbody = doc.body,\n\t\t\tdefaultView = doc.defaultView,\n\t\t\tprevComputedStyle = defaultView ? defaultView.getComputedStyle( elem, null ) : elem.currentStyle,\n\t\t\ttop = elem.offsetTop,\n\t\t\tleft = elem.offsetLeft;\n\n\t\twhile ( (elem = elem.parentNode) && elem !== body && elem !== docElem ) {\n\t\t\tif ( jQuery.offset.supportsFixedPosition && prevComputedStyle.position === \"fixed\" ) {\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcomputedStyle = defaultView ? defaultView.getComputedStyle(elem, null) : elem.currentStyle;\n\t\t\ttop  -= elem.scrollTop;\n\t\t\tleft -= elem.scrollLeft;\n\n\t\t\tif ( elem === offsetParent ) {\n\t\t\t\ttop  += elem.offsetTop;\n\t\t\t\tleft += elem.offsetLeft;\n\n\t\t\t\tif ( jQuery.offset.doesNotAddBorder && !(jQuery.offset.doesAddBorderForTableAndCells && rtable.test(elem.nodeName)) ) {\n\t\t\t\t\ttop  += parseFloat( computedStyle.borderTopWidth  ) || 0;\n\t\t\t\t\tleft += parseFloat( computedStyle.borderLeftWidth ) || 0;\n\t\t\t\t}\n\n\t\t\t\tprevOffsetParent = offsetParent;\n\t\t\t\toffsetParent = elem.offsetParent;\n\t\t\t}\n\n\t\t\tif ( jQuery.offset.subtractsBorderForOverflowNotVisible && computedStyle.overflow !== \"visible\" ) {\n\t\t\t\ttop  += parseFloat( computedStyle.borderTopWidth  ) || 0;\n\t\t\t\tleft += parseFloat( computedStyle.borderLeftWidth ) || 0;\n\t\t\t}\n\n\t\t\tprevComputedStyle = computedStyle;\n\t\t}\n\n\t\tif ( prevComputedStyle.position === \"relative\" || prevComputedStyle.position === \"static\" ) {\n\t\t\ttop  += body.offsetTop;\n\t\t\tleft += body.offsetLeft;\n\t\t}\n\n\t\tif ( jQuery.offset.supportsFixedPosition && prevComputedStyle.position === \"fixed\" ) {\n\t\t\ttop  += Math.max( docElem.scrollTop, body.scrollTop );\n\t\t\tleft += Math.max( docElem.scrollLeft, body.scrollLeft );\n\t\t}\n\n\t\treturn { top: top, left: left };\n\t};\n}\n\njQuery.offset = {\n\tinitialize: function() {\n\t\tvar body = document.body, container = document.createElement(\"div\"), innerDiv, checkDiv, table, td, bodyMarginTop = parseFloat( jQuery.css(body, \"marginTop\") ) || 0,\n\t\t\thtml = \"<div style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;'><div></div></div><table style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;' cellpadding='0' cellspacing='0'><tr><td></td></tr></table>\";\n\n\t\tjQuery.extend( container.style, { position: \"absolute\", top: 0, left: 0, margin: 0, border: 0, width: \"1px\", height: \"1px\", visibility: \"hidden\" } );\n\n\t\tcontainer.innerHTML = html;\n\t\tbody.insertBefore( container, body.firstChild );\n\t\tinnerDiv = container.firstChild;\n\t\tcheckDiv = innerDiv.firstChild;\n\t\ttd = innerDiv.nextSibling.firstChild.firstChild;\n\n\t\tthis.doesNotAddBorder = (checkDiv.offsetTop !== 5);\n\t\tthis.doesAddBorderForTableAndCells = (td.offsetTop === 5);\n\n\t\tcheckDiv.style.position = \"fixed\";\n\t\tcheckDiv.style.top = \"20px\";\n\n\t\t// safari subtracts parent border width here which is 5px\n\t\tthis.supportsFixedPosition = (checkDiv.offsetTop === 20 || checkDiv.offsetTop === 15);\n\t\tcheckDiv.style.position = checkDiv.style.top = \"\";\n\n\t\tinnerDiv.style.overflow = \"hidden\";\n\t\tinnerDiv.style.position = \"relative\";\n\n\t\tthis.subtractsBorderForOverflowNotVisible = (checkDiv.offsetTop === -5);\n\n\t\tthis.doesNotIncludeMarginInBodyOffset = (body.offsetTop !== bodyMarginTop);\n\n\t\tbody.removeChild( container );\n\t\tbody = container = innerDiv = checkDiv = table = td = null;\n\t\tjQuery.offset.initialize = jQuery.noop;\n\t},\n\n\tbodyOffset: function( body ) {\n\t\tvar top = body.offsetTop,\n\t\t\tleft = body.offsetLeft;\n\n\t\tjQuery.offset.initialize();\n\n\t\tif ( jQuery.offset.doesNotIncludeMarginInBodyOffset ) {\n\t\t\ttop  += parseFloat( jQuery.css(body, \"marginTop\") ) || 0;\n\t\t\tleft += parseFloat( jQuery.css(body, \"marginLeft\") ) || 0;\n\t\t}\n\n\t\treturn { top: top, left: left };\n\t},\n\n\tsetOffset: function( elem, options, i ) {\n\t\tvar position = jQuery.css( elem, \"position\" );\n\n\t\t// set position first, in-case top/left are set even on static elem\n\t\tif ( position === \"static\" ) {\n\t\t\telem.style.position = \"relative\";\n\t\t}\n\n\t\tvar curElem = jQuery( elem ),\n\t\t\tcurOffset = curElem.offset(),\n\t\t\tcurCSSTop = jQuery.css( elem, \"top\" ),\n\t\t\tcurCSSLeft = jQuery.css( elem, \"left\" ),\n\t\t\tcalculatePosition = (position === \"absolute\" && jQuery.inArray('auto', [curCSSTop, curCSSLeft]) > -1),\n\t\t\tprops = {}, curPosition = {}, curTop, curLeft;\n\n\t\t// need to be able to calculate position if either top or left is auto and position is absolute\n\t\tif ( calculatePosition ) {\n\t\t\tcurPosition = curElem.position();\n\t\t}\n\n\t\tcurTop  = calculatePosition ? curPosition.top  : parseInt( curCSSTop,  10 ) || 0;\n\t\tcurLeft = calculatePosition ? curPosition.left : parseInt( curCSSLeft, 10 ) || 0;\n\n\t\tif ( jQuery.isFunction( options ) ) {\n\t\t\toptions = options.call( elem, i, curOffset );\n\t\t}\n\n\t\tif (options.top != null) {\n\t\t\tprops.top = (options.top - curOffset.top) + curTop;\n\t\t}\n\t\tif (options.left != null) {\n\t\t\tprops.left = (options.left - curOffset.left) + curLeft;\n\t\t}\n\n\t\tif ( \"using\" in options ) {\n\t\t\toptions.using.call( elem, props );\n\t\t} else {\n\t\t\tcurElem.css( props );\n\t\t}\n\t}\n};\n\n\njQuery.fn.extend({\n\tposition: function() {\n\t\tif ( !this[0] ) {\n\t\t\treturn null;\n\t\t}\n\n\t\tvar elem = this[0],\n\n\t\t// Get *real* offsetParent\n\t\toffsetParent = this.offsetParent(),\n\n\t\t// Get correct offsets\n\t\toffset       = this.offset(),\n\t\tparentOffset = rroot.test(offsetParent[0].nodeName) ? { top: 0, left: 0 } : offsetParent.offset();\n\n\t\t// Subtract element margins\n\t\t// note: when an element has margin: auto the offsetLeft and marginLeft\n\t\t// are the same in Safari causing offset.left to incorrectly be 0\n\t\toffset.top  -= parseFloat( jQuery.css(elem, \"marginTop\") ) || 0;\n\t\toffset.left -= parseFloat( jQuery.css(elem, \"marginLeft\") ) || 0;\n\n\t\t// Add offsetParent borders\n\t\tparentOffset.top  += parseFloat( jQuery.css(offsetParent[0], \"borderTopWidth\") ) || 0;\n\t\tparentOffset.left += parseFloat( jQuery.css(offsetParent[0], \"borderLeftWidth\") ) || 0;\n\n\t\t// Subtract the two offsets\n\t\treturn {\n\t\t\ttop:  offset.top  - parentOffset.top,\n\t\t\tleft: offset.left - parentOffset.left\n\t\t};\n\t},\n\n\toffsetParent: function() {\n\t\treturn this.map(function() {\n\t\t\tvar offsetParent = this.offsetParent || document.body;\n\t\t\twhile ( offsetParent && (!rroot.test(offsetParent.nodeName) && jQuery.css(offsetParent, \"position\") === \"static\") ) {\n\t\t\t\toffsetParent = offsetParent.offsetParent;\n\t\t\t}\n\t\t\treturn offsetParent;\n\t\t});\n\t}\n});\n\n\n// Create scrollLeft and scrollTop methods\njQuery.each( [\"Left\", \"Top\"], function( i, name ) {\n\tvar method = \"scroll\" + name;\n\n\tjQuery.fn[ method ] = function(val) {\n\t\tvar elem = this[0], win;\n\n\t\tif ( !elem ) {\n\t\t\treturn null;\n\t\t}\n\n\t\tif ( val !== undefined ) {\n\t\t\t// Set the scroll offset\n\t\t\treturn this.each(function() {\n\t\t\t\twin = getWindow( this );\n\n\t\t\t\tif ( win ) {\n\t\t\t\t\twin.scrollTo(\n\t\t\t\t\t\t!i ? val : jQuery(win).scrollLeft(),\n\t\t\t\t\t\ti ? val : jQuery(win).scrollTop()\n\t\t\t\t\t);\n\n\t\t\t\t} else {\n\t\t\t\t\tthis[ method ] = val;\n\t\t\t\t}\n\t\t\t});\n\t\t} else {\n\t\t\twin = getWindow( elem );\n\n\t\t\t// Return the scroll offset\n\t\t\treturn win ? (\"pageXOffset\" in win) ? win[ i ? \"pageYOffset\" : \"pageXOffset\" ] :\n\t\t\t\tjQuery.support.boxModel && win.document.documentElement[ method ] ||\n\t\t\t\t\twin.document.body[ method ] :\n\t\t\t\telem[ method ];\n\t\t}\n\t};\n});\n\nfunction getWindow( elem ) {\n\treturn jQuery.isWindow( elem ) ?\n\t\telem :\n\t\telem.nodeType === 9 ?\n\t\t\telem.defaultView || elem.parentWindow :\n\t\t\tfalse;\n}\n\n\n\n\n// Create innerHeight, innerWidth, outerHeight and outerWidth methods\njQuery.each([ \"Height\", \"Width\" ], function( i, name ) {\n\n\tvar type = name.toLowerCase();\n\n\t// innerHeight and innerWidth\n\tjQuery.fn[\"inner\" + name] = function() {\n\t\treturn this[0] ?\n\t\t\tparseFloat( jQuery.css( this[0], type, \"padding\" ) ) :\n\t\t\tnull;\n\t};\n\n\t// outerHeight and outerWidth\n\tjQuery.fn[\"outer\" + name] = function( margin ) {\n\t\treturn this[0] ?\n\t\t\tparseFloat( jQuery.css( this[0], type, margin ? \"margin\" : \"border\" ) ) :\n\t\t\tnull;\n\t};\n\n\tjQuery.fn[ type ] = function( size ) {\n\t\t// Get window width or height\n\t\tvar elem = this[0];\n\t\tif ( !elem ) {\n\t\t\treturn size == null ? null : this;\n\t\t}\n\n\t\tif ( jQuery.isFunction( size ) ) {\n\t\t\treturn this.each(function( i ) {\n\t\t\t\tvar self = jQuery( this );\n\t\t\t\tself[ type ]( size.call( this, i, self[ type ]() ) );\n\t\t\t});\n\t\t}\n\n\t\tif ( jQuery.isWindow( elem ) ) {\n\t\t\t// Everyone else use document.documentElement or document.body depending on Quirks vs Standards mode\n\t\t\t// 3rd condition allows Nokia support, as it supports the docElem prop but not CSS1Compat\n\t\t\tvar docElemProp = elem.document.documentElement[ \"client\" + name ];\n\t\t\treturn elem.document.compatMode === \"CSS1Compat\" && docElemProp ||\n\t\t\t\telem.document.body[ \"client\" + name ] || docElemProp;\n\n\t\t// Get document width or height\n\t\t} else if ( elem.nodeType === 9 ) {\n\t\t\t// Either scroll[Width/Height] or offset[Width/Height], whichever is greater\n\t\t\treturn Math.max(\n\t\t\t\telem.documentElement[\"client\" + name],\n\t\t\t\telem.body[\"scroll\" + name], elem.documentElement[\"scroll\" + name],\n\t\t\t\telem.body[\"offset\" + name], elem.documentElement[\"offset\" + name]\n\t\t\t);\n\n\t\t// Get or set width or height on the element\n\t\t} else if ( size === undefined ) {\n\t\t\tvar orig = jQuery.css( elem, type ),\n\t\t\t\tret = parseFloat( orig );\n\n\t\t\treturn jQuery.isNaN( ret ) ? orig : ret;\n\n\t\t// Set the width or height on the element (default to pixels if value is unitless)\n\t\t} else {\n\t\t\treturn this.css( type, typeof size === \"string\" ? size : size + \"px\" );\n\t\t}\n\t};\n\n});\n\n\nwindow.jQuery = window.$ = jQuery;\n})(window);\n"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/js/gchartapi.js",
    "content": "if(!window['googleLT_']){window['googleLT_']=(new Date()).getTime();}if (!window['google']) {\nwindow['google'] = {};\n}\nif (!window['google']['loader']) {\nwindow['google']['loader'] = {};\ngoogle.loader.ServiceBase = 'https://www.google.com/uds';\ngoogle.loader.GoogleApisBase = 'https://ajax.googleapis.com/ajax';\ngoogle.loader.ApiKey = 'notsupplied';\ngoogle.loader.KeyVerified = true;\ngoogle.loader.LoadFailure = false;\ngoogle.loader.Secure = true;\ngoogle.loader.GoogleLocale = 'www.google.com';\ngoogle.loader.ClientLocation = null;\ngoogle.loader.AdditionalParams = '';\n(function() {var d=void 0,g=!0,h=null,k=!1,l=encodeURIComponent,m=window,n=document;function p(a,b){return a.load=b}var r=\"push\",s=\"replace\",t=\"charAt\",v=\"indexOf\",w=\"ServiceBase\",x=\"name\",y=\"getTime\",z=\"length\",A=\"prototype\",B=\"setTimeout\",C=\"loader\",D=\"substring\",E=\"join\",F=\"toLowerCase\";function G(a){return a in H?H[a]:H[a]=-1!=navigator.userAgent[F]()[v](a)}var H={};function I(a,b){var c=function(){};c.prototype=b[A];a.T=b[A];a.prototype=new c}\nfunction J(a,b,c){var e=Array[A].slice.call(arguments,2)||[];return function(){var c=e.concat(Array[A].slice.call(arguments));return a.apply(b,c)}}function K(a){a=Error(a);a.toString=function(){return this.message};return a}function L(a,b){for(var c=a.split(/\\./),e=m,f=0;f<c[z]-1;f++)e[c[f]]||(e[c[f]]={}),e=e[c[f]];e[c[c[z]-1]]=b}function aa(a,b,c){a[b]=c}if(!M)var M=L;if(!N)var N=aa;google[C].v={};M(\"google.loader.callbacks\",google[C].v);var O={},P={};google[C].eval={};M(\"google.loader.eval\",google[C].eval);\np(google,function(a,b,c){function e(a){var b=a.split(\".\");if(2<b[z])throw K(\"Module: '\"+a+\"' not found!\");\"undefined\"!=typeof b[1]&&(f=b[0],c.packages=c.packages||[],c.packages[r](b[1]))}var f=a;c=c||{};if(a instanceof Array||a&&\"object\"==typeof a&&\"function\"==typeof a[E]&&\"function\"==typeof a.reverse)for(var j=0;j<a[z];j++)e(a[j]);else e(a);if(a=O[\":\"+f]){c&&(!c.language&&c.locale)&&(c.language=c.locale);c&&\"string\"==typeof c.callback&&(j=c.callback,j.match(/^[[\\]A-Za-z0-9._]+$/)&&(j=m.eval(j),c.callback=\nj));if((j=c&&c.callback!=h)&&!a.s(b))throw K(\"Module: '\"+f+\"' must be loaded before DOM onLoad!\");j?a.m(b,c)?m[B](c.callback,0):a.load(b,c):a.m(b,c)||a.load(b,c)}else throw K(\"Module: '\"+f+\"' not found!\");});M(\"google.load\",google.load);\ngoogle.S=function(a,b){b?(0==Q[z]&&(R(m,\"load\",S),!G(\"msie\")&&!G(\"safari\")&&!G(\"konqueror\")&&G(\"mozilla\")||m.opera?m.addEventListener(\"DOMContentLoaded\",S,k):G(\"msie\")?n.write(\"<script defer onreadystatechange='google.loader.domReady()' src=//:><\\/script>\"):(G(\"safari\")||G(\"konqueror\"))&&m[B](ba,10)),Q[r](a)):R(m,\"load\",a)};M(\"google.setOnLoadCallback\",google.S);\nfunction R(a,b,c){if(a.addEventListener)a.addEventListener(b,c,k);else if(a.attachEvent)a.attachEvent(\"on\"+b,c);else{var e=a[\"on\"+b];if(e!=h){var f=[c,e];c=function(){for(var a=0;a<f[z];a++)f[a]()}}a[\"on\"+b]=c}}var Q=[];google[C].O=function(){var a=m.event.srcElement;\"complete\"==a.readyState&&(a.onreadystatechange=h,a.parentNode.removeChild(a),S())};M(\"google.loader.domReady\",google[C].O);var ca={loaded:g,complete:g};function ba(){ca[n.readyState]?S():0<Q[z]&&m[B](ba,10)}\nfunction S(){for(var a=0;a<Q[z];a++)Q[a]();Q.length=0}google[C].d=function(a,b,c){if(c){var e;\"script\"==a?(e=n.createElement(\"script\"),e.type=\"text/javascript\",e.src=b):\"css\"==a&&(e=n.createElement(\"link\"),e.type=\"text/css\",e.href=b,e.rel=\"stylesheet\");(a=n.getElementsByTagName(\"head\")[0])||(a=n.body.parentNode.appendChild(n.createElement(\"head\")));a.appendChild(e)}else\"script\"==a?n.write('<script src=\"'+b+'\" type=\"text/javascript\"><\\/script>'):\"css\"==a&&n.write('<link href=\"'+b+'\" type=\"text/css\" rel=\"stylesheet\"></link>')};\nM(\"google.loader.writeLoadTag\",google[C].d);google[C].P=function(a){P=a};M(\"google.loader.rfm\",google[C].P);google[C].R=function(a){for(var b in a)\"string\"==typeof b&&(b&&\":\"==b[t](0)&&!O[b])&&(O[b]=new T(b[D](1),a[b]))};M(\"google.loader.rpl\",google[C].R);google[C].Q=function(a){if((a=a.specs)&&a[z])for(var b=0;b<a[z];++b){var c=a[b];\"string\"==typeof c?O[\":\"+c]=new V(c):(c=new W(c[x],c.baseSpec,c.customSpecs),O[\":\"+c[x]]=c)}};M(\"google.loader.rm\",google[C].Q);google[C].loaded=function(a){O[\":\"+a.module].l(a)};\nM(\"google.loader.loaded\",google[C].loaded);google[C].N=function(){return\"qid=\"+((new Date)[y]().toString(16)+Math.floor(1E7*Math.random()).toString(16))};M(\"google.loader.createGuidArg_\",google[C].N);L(\"google_exportSymbol\",L);L(\"google_exportProperty\",aa);google[C].a={};M(\"google.loader.themes\",google[C].a);google[C].a.H=\"//www.google.com/cse/style/look/bubblegum.css\";N(google[C].a,\"BUBBLEGUM\",google[C].a.H);google[C].a.J=\"//www.google.com/cse/style/look/greensky.css\";N(google[C].a,\"GREENSKY\",google[C].a.J);\ngoogle[C].a.I=\"//www.google.com/cse/style/look/espresso.css\";N(google[C].a,\"ESPRESSO\",google[C].a.I);google[C].a.L=\"//www.google.com/cse/style/look/shiny.css\";N(google[C].a,\"SHINY\",google[C].a.L);google[C].a.K=\"//www.google.com/cse/style/look/minimalist.css\";N(google[C].a,\"MINIMALIST\",google[C].a.K);google[C].a.M=\"//www.google.com/cse/style/look/v2/default.css\";N(google[C].a,\"V2_DEFAULT\",google[C].a.M);function V(a){this.b=a;this.o=[];this.n={};this.e={};this.f={};this.j=g;this.c=-1}\nV[A].g=function(a,b){var c=\"\";b!=d&&(b.language!=d&&(c+=\"&hl=\"+l(b.language)),b.nocss!=d&&(c+=\"&output=\"+l(\"nocss=\"+b.nocss)),b.nooldnames!=d&&(c+=\"&nooldnames=\"+l(b.nooldnames)),b.packages!=d&&(c+=\"&packages=\"+l(b.packages)),b.callback!=h&&(c+=\"&async=2\"),b.style!=d&&(c+=\"&style=\"+l(b.style)),b.noexp!=d&&(c+=\"&noexp=true\"),b.other_params!=d&&(c+=\"&\"+b.other_params));if(!this.j){google[this.b]&&google[this.b].JSHash&&(c+=\"&sig=\"+l(google[this.b].JSHash));var e=[],f;for(f in this.n)\":\"==f[t](0)&&e[r](f[D](1));\nfor(f in this.e)\":\"==f[t](0)&&this.e[f]&&e[r](f[D](1));c+=\"&have=\"+l(e[E](\",\"))}return google[C][w]+\"/?file=\"+this.b+\"&v=\"+a+google[C].AdditionalParams+c};V[A].t=function(a){var b=h;a&&(b=a.packages);var c=h;if(b)if(\"string\"==typeof b)c=[a.packages];else if(b[z]){c=[];for(a=0;a<b[z];a++)\"string\"==typeof b[a]&&c[r](b[a][s](/^\\s*|\\s*$/,\"\")[F]())}c||(c=[\"default\"]);b=[];for(a=0;a<c[z];a++)this.n[\":\"+c[a]]||b[r](c[a]);return b};\np(V[A],function(a,b){var c=this.t(b),e=b&&b.callback!=h;if(e)var f=new X(b.callback);for(var j=[],q=c[z]-1;0<=q;q--){var u=c[q];e&&f.A(u);if(this.e[\":\"+u])c.splice(q,1),e&&this.f[\":\"+u][r](f);else j[r](u)}if(c[z]){b&&b.packages&&(b.packages=c.sort()[E](\",\"));for(q=0;q<j[z];q++)u=j[q],this.f[\":\"+u]=[],e&&this.f[\":\"+u][r](f);if(!b&&P[\":\"+this.b]!=h&&P[\":\"+this.b].versions[\":\"+a]!=h&&!google[C].AdditionalParams&&this.j){c=P[\":\"+this.b];google[this.b]=google[this.b]||{};for(var U in c.properties)U&&\":\"==\nU[t](0)&&(google[this.b][U[D](1)]=c.properties[U]);google[C].d(\"script\",google[C][w]+c.path+c.js,e);c.css&&google[C].d(\"css\",google[C][w]+c.path+c.css,e)}else(!b||!b.autoloaded)&&google[C].d(\"script\",this.g(a,b),e);this.j&&(this.j=k,this.c=(new Date)[y](),1!=this.c%100&&(this.c=-1));for(q=0;q<j[z];q++)u=j[q],this.e[\":\"+u]=g}});\nV[A].l=function(a){-1!=this.c&&(da(\"al_\"+this.b,\"jl.\"+((new Date)[y]()-this.c),g),this.c=-1);this.o=this.o.concat(a.components);google[C][this.b]||(google[C][this.b]={});google[C][this.b].packages=this.o.slice(0);for(var b=0;b<a.components[z];b++){this.n[\":\"+a.components[b]]=g;this.e[\":\"+a.components[b]]=k;var c=this.f[\":\"+a.components[b]];if(c){for(var e=0;e<c[z];e++)c[e].B(a.components[b]);delete this.f[\":\"+a.components[b]]}}};V[A].m=function(a,b){return 0==this.t(b)[z]};V[A].s=function(){return g};\nfunction X(a){this.D=a;this.q={};this.r=0}X[A].A=function(a){this.r++;this.q[\":\"+a]=g};X[A].B=function(a){this.q[\":\"+a]&&(this.q[\":\"+a]=k,this.r--,0==this.r&&m[B](this.D,0))};function W(a,b,c){this.name=a;this.C=b;this.p=c;this.u=this.h=k;this.k=[];google[C].v[this[x]]=J(this.l,this)}I(W,V);p(W[A],function(a,b){var c=b&&b.callback!=h;c?(this.k[r](b.callback),b.callback=\"google.loader.callbacks.\"+this[x]):this.h=g;(!b||!b.autoloaded)&&google[C].d(\"script\",this.g(a,b),c)});W[A].m=function(a,b){return b&&b.callback!=h?this.u:this.h};W[A].l=function(){this.u=g;for(var a=0;a<this.k[z];a++)m[B](this.k[a],0);this.k=[]};\nvar Y=function(a,b){return a.string?l(a.string)+\"=\"+l(b):a.regex?b[s](/(^.*$)/,a.regex):\"\"};W[A].g=function(a,b){return this.F(this.w(a),a,b)};\nW[A].F=function(a,b,c){var e=\"\";a.key&&(e+=\"&\"+Y(a.key,google[C].ApiKey));a.version&&(e+=\"&\"+Y(a.version,b));b=google[C].Secure&&a.ssl?a.ssl:a.uri;if(c!=h)for(var f in c)a.params[f]?e+=\"&\"+Y(a.params[f],c[f]):\"other_params\"==f?e+=\"&\"+c[f]:\"base_domain\"==f&&(b=\"http://\"+c[f]+a.uri[D](a.uri[v](\"/\",7)));google[this[x]]={};-1==b[v](\"?\")&&e&&(e=\"?\"+e[D](1));return b+e};W[A].s=function(a){return this.w(a).deferred};W[A].w=function(a){if(this.p)for(var b=0;b<this.p[z];++b){var c=this.p[b];if(RegExp(c.pattern).test(a))return c}return this.C};function T(a,b){this.b=a;this.i=b;this.h=k}I(T,V);p(T[A],function(a,b){this.h=g;google[C].d(\"script\",this.g(a,b),k)});T[A].m=function(){return this.h};T[A].l=function(){};T[A].g=function(a,b){if(!this.i.versions[\":\"+a]){if(this.i.aliases){var c=this.i.aliases[\":\"+a];c&&(a=c)}if(!this.i.versions[\":\"+a])throw K(\"Module: '\"+this.b+\"' with version '\"+a+\"' not found!\");}return google[C].GoogleApisBase+\"/libs/\"+this.b+\"/\"+a+\"/\"+this.i.versions[\":\"+a][b&&b.uncompressed?\"uncompressed\":\"compressed\"]};\nT[A].s=function(){return k};var ea=k,Z=[],fa=(new Date)[y](),ha=function(){ea||(R(m,\"unload\",ga),ea=g)},ia=function(a,b){ha();if(!google[C].Secure&&(!google[C].Options||google[C].Options.csi===k)){for(var c=0;c<a[z];c++)a[c]=l(a[c][F]()[s](/[^a-z0-9_.]+/g,\"_\"));for(c=0;c<b[z];c++)b[c]=l(b[c][F]()[s](/[^a-z0-9_.]+/g,\"_\"));m[B](J($,h,\"//gg.google.com/csi?s=uds&v=2&action=\"+a[E](\",\")+\"&it=\"+b[E](\",\")),1E4)}},da=function(a,b,c){c?ia([a],[b]):(ha(),Z[r](\"r\"+Z[z]+\"=\"+l(a+(b?\"|\"+b:\"\"))),m[B](ga,5<Z[z]?0:15E3))},ga=function(){if(Z[z]){var a=\ngoogle[C][w];0==a[v](\"http:\")&&(a=a[s](/^http:/,\"https:\"));$(a+\"/stats?\"+Z[E](\"&\")+\"&nc=\"+(new Date)[y]()+\"_\"+((new Date)[y]()-fa));Z.length=0}},$=function(a){var b=new Image,c=$.G++;$.z[c]=b;b.onload=b.onerror=function(){delete $.z[c]};b.src=a;b=h};$.z={};$.G=0;L(\"google.loader.recordCsiStat\",ia);L(\"google.loader.recordStat\",da);L(\"google.loader.createImageForLogging\",$);\n\n}) ();google.loader.rm({\"specs\":[\"feeds\",\"spreadsheets\",\"gdata\",\"visualization\",{\"name\":\"sharing\",\"baseSpec\":{\"uri\":\"http://www.google.com/s2/sharing/js\",\"ssl\":null,\"key\":{\"string\":\"key\"},\"version\":{\"string\":\"v\"},\"deferred\":false,\"params\":{\"language\":{\"string\":\"hl\"}}}},\"search\",\"orkut\",\"ads\",\"elements\",{\"name\":\"books\",\"baseSpec\":{\"uri\":\"http://books.google.com/books/api.js\",\"ssl\":\"https://encrypted.google.com/books/api.js\",\"key\":{\"string\":\"key\"},\"version\":{\"string\":\"v\"},\"deferred\":true,\"params\":{\"callback\":{\"string\":\"callback\"},\"language\":{\"string\":\"hl\"}}}},{\"name\":\"friendconnect\",\"baseSpec\":{\"uri\":\"http://www.google.com/friendconnect/script/friendconnect.js\",\"ssl\":null,\"key\":{\"string\":\"key\"},\"version\":{\"string\":\"v\"},\"deferred\":false,\"params\":{}}},\"identitytoolkit\",\"ima\",{\"name\":\"maps\",\"baseSpec\":{\"uri\":\"http://maps.google.com/maps?file\\u003dgoogleapi\",\"ssl\":\"https://maps-api-ssl.google.com/maps?file\\u003dgoogleapi\",\"key\":{\"string\":\"key\"},\"version\":{\"string\":\"v\"},\"deferred\":true,\"params\":{\"callback\":{\"regex\":\"callback\\u003d$1\\u0026async\\u003d2\"},\"language\":{\"string\":\"hl\"}}},\"customSpecs\":[{\"uri\":\"http://maps.googleapis.com/maps/api/js\",\"ssl\":\"https://maps.googleapis.com/maps/api/js\",\"version\":{\"string\":\"v\"},\"deferred\":true,\"params\":{\"callback\":{\"string\":\"callback\"},\"language\":{\"string\":\"hl\"}},\"pattern\":\"^(3|3..*)$\"}]},\"payments\",\"wave\",\"annotations_v2\",\"earth\",\"language\",{\"name\":\"annotations\",\"baseSpec\":{\"uri\":\"http://www.google.com/reviews/scripts/annotations_bootstrap.js\",\"ssl\":null,\"key\":{\"string\":\"key\"},\"version\":{\"string\":\"v\"},\"deferred\":true,\"params\":{\"callback\":{\"string\":\"callback\"},\"language\":{\"string\":\"hl\"},\"country\":{\"string\":\"gl\"}}}},\"picker\"]});\ngoogle.loader.rfm({\":search\":{\"versions\":{\":1\":\"1\",\":1.0\":\"1\"},\"path\":\"/api/search/1.0/8c68537a8c14de310f268bd7f81c9c67/\",\"js\":\"default+en.I.js\",\"css\":\"default+en.css\",\"properties\":{\":JSHash\":\"8c68537a8c14de310f268bd7f81c9c67\",\":NoOldNames\":false,\":Version\":\"1.0\"}},\":language\":{\"versions\":{\":1\":\"1\",\":1.0\":\"1\"},\"path\":\"/api/language/1.0/5c0e5bb3f3395bbb139742509e2bb268/\",\"js\":\"default+en.I.js\",\"properties\":{\":JSHash\":\"5c0e5bb3f3395bbb139742509e2bb268\",\":Version\":\"1.0\"}},\":feeds\":{\"versions\":{\":1\":\"1\",\":1.0\":\"1\"},\"path\":\"/api/feeds/1.0/77f89919ef841f93359ce886504e4e3f/\",\"js\":\"default+en.I.js\",\"css\":\"default+en.css\",\"properties\":{\":JSHash\":\"77f89919ef841f93359ce886504e4e3f\",\":Version\":\"1.0\"}},\":spreadsheets\":{\"versions\":{\":0\":\"1\",\":0.4\":\"1\"},\"path\":\"/api/spreadsheets/0.4/87ff7219e9f8a8164006cbf28d5e911a/\",\"js\":\"default.I.js\",\"properties\":{\":JSHash\":\"87ff7219e9f8a8164006cbf28d5e911a\",\":Version\":\"0.4\"}},\":ima\":{\"versions\":{\":3\":\"1\",\":3.0\":\"1\"},\"path\":\"/api/ima/3.0/28a914332232c9a8ac0ae8da68b1006e/\",\"js\":\"default.I.js\",\"properties\":{\":JSHash\":\"28a914332232c9a8ac0ae8da68b1006e\",\":Version\":\"3.0\"}},\":wave\":{\"versions\":{\":1\":\"1\",\":1.0\":\"1\"},\"path\":\"/api/wave/1.0/3b6f7573ff78da6602dda5e09c9025bf/\",\"js\":\"default.I.js\",\"properties\":{\":JSHash\":\"3b6f7573ff78da6602dda5e09c9025bf\",\":Version\":\"1.0\"}},\":annotations\":{\"versions\":{\":1\":\"1\",\":1.0\":\"1\"},\"path\":\"/api/annotations/1.0/632d801f04d14d064b3a2e4290697a29/\",\"js\":\"default+en.I.js\",\"properties\":{\":JSHash\":\"632d801f04d14d064b3a2e4290697a29\",\":Version\":\"1.0\"}},\":earth\":{\"versions\":{\":1\":\"1\",\":1.0\":\"1\"},\"path\":\"/api/earth/1.0/109c7b2bae7fe6cc34ea875176165d81/\",\"js\":\"default.I.js\",\"properties\":{\":JSHash\":\"109c7b2bae7fe6cc34ea875176165d81\",\":Version\":\"1.0\"}},\":picker\":{\"versions\":{\":1\":\"1\",\":1.0\":\"1\"},\"path\":\"/api/picker/1.0/05c87704cd84b49307c16b1e4e9aee7c/\",\"js\":\"default.I.js\",\"css\":\"default.css\",\"properties\":{\":JSHash\":\"05c87704cd84b49307c16b1e4e9aee7c\",\":Version\":\"1.0\"}}});\ngoogle.loader.rpl({\":scriptaculous\":{\"versions\":{\":1.8.3\":{\"uncompressed\":\"scriptaculous.js\",\"compressed\":\"scriptaculous.js\"},\":1.9.0\":{\"uncompressed\":\"scriptaculous.js\",\"compressed\":\"scriptaculous.js\"},\":1.8.2\":{\"uncompressed\":\"scriptaculous.js\",\"compressed\":\"scriptaculous.js\"},\":1.8.1\":{\"uncompressed\":\"scriptaculous.js\",\"compressed\":\"scriptaculous.js\"}},\"aliases\":{\":1.8\":\"1.8.3\",\":1\":\"1.9.0\",\":1.9\":\"1.9.0\"}},\":yui\":{\"versions\":{\":2.6.0\":{\"uncompressed\":\"build/yuiloader/yuiloader.js\",\"compressed\":\"build/yuiloader/yuiloader-min.js\"},\":2.9.0\":{\"uncompressed\":\"build/yuiloader/yuiloader.js\",\"compressed\":\"build/yuiloader/yuiloader-min.js\"},\":2.7.0\":{\"uncompressed\":\"build/yuiloader/yuiloader.js\",\"compressed\":\"build/yuiloader/yuiloader-min.js\"},\":2.8.0r4\":{\"uncompressed\":\"build/yuiloader/yuiloader.js\",\"compressed\":\"build/yuiloader/yuiloader-min.js\"},\":2.8.2r1\":{\"uncompressed\":\"build/yuiloader/yuiloader.js\",\"compressed\":\"build/yuiloader/yuiloader-min.js\"},\":2.8.1\":{\"uncompressed\":\"build/yuiloader/yuiloader.js\",\"compressed\":\"build/yuiloader/yuiloader-min.js\"},\":3.3.0\":{\"uncompressed\":\"build/yui/yui.js\",\"compressed\":\"build/yui/yui-min.js\"}},\"aliases\":{\":3\":\"3.3.0\",\":2\":\"2.9.0\",\":2.7\":\"2.7.0\",\":2.8.2\":\"2.8.2r1\",\":2.6\":\"2.6.0\",\":2.9\":\"2.9.0\",\":2.8\":\"2.8.2r1\",\":2.8.0\":\"2.8.0r4\",\":3.3\":\"3.3.0\"}},\":swfobject\":{\"versions\":{\":2.1\":{\"uncompressed\":\"swfobject_src.js\",\"compressed\":\"swfobject.js\"},\":2.2\":{\"uncompressed\":\"swfobject_src.js\",\"compressed\":\"swfobject.js\"}},\"aliases\":{\":2\":\"2.2\"}},\":ext-core\":{\"versions\":{\":3.1.0\":{\"uncompressed\":\"ext-core-debug.js\",\"compressed\":\"ext-core.js\"},\":3.0.0\":{\"uncompressed\":\"ext-core-debug.js\",\"compressed\":\"ext-core.js\"}},\"aliases\":{\":3\":\"3.1.0\",\":3.0\":\"3.0.0\",\":3.1\":\"3.1.0\"}},\":webfont\":{\"versions\":{\":1.0.28\":{\"uncompressed\":\"webfont_debug.js\",\"compressed\":\"webfont.js\"},\":1.0.27\":{\"uncompressed\":\"webfont_debug.js\",\"compressed\":\"webfont.js\"},\":1.0.29\":{\"uncompressed\":\"webfont_debug.js\",\"compressed\":\"webfont.js\"},\":1.0.12\":{\"uncompressed\":\"webfont_debug.js\",\"compressed\":\"webfont.js\"},\":1.0.13\":{\"uncompressed\":\"webfont_debug.js\",\"compressed\":\"webfont.js\"},\":1.0.14\":{\"uncompressed\":\"webfont_debug.js\",\"compressed\":\"webfont.js\"},\":1.0.15\":{\"uncompressed\":\"webfont_debug.js\",\"compressed\":\"webfont.js\"},\":1.0.10\":{\"uncompressed\":\"webfont_debug.js\",\"compressed\":\"webfont.js\"},\":1.0.11\":{\"uncompressed\":\"webfont_debug.js\",\"compressed\":\"webfont.js\"},\":1.0.2\":{\"uncompressed\":\"webfont_debug.js\",\"compressed\":\"webfont.js\"},\":1.0.1\":{\"uncompressed\":\"webfont_debug.js\",\"compressed\":\"webfont.js\"},\":1.0.0\":{\"uncompressed\":\"webfont_debug.js\",\"compressed\":\"webfont.js\"},\":1.0.6\":{\"uncompressed\":\"webfont_debug.js\",\"compressed\":\"webfont.js\"},\":1.0.19\":{\"uncompressed\":\"webfont_debug.js\",\"compressed\":\"webfont.js\"},\":1.0.5\":{\"uncompressed\":\"webfont_debug.js\",\"compressed\":\"webfont.js\"},\":1.0.18\":{\"uncompressed\":\"webfont_debug.js\",\"compressed\":\"webfont.js\"},\":1.0.4\":{\"uncompressed\":\"webfont_debug.js\",\"compressed\":\"webfont.js\"},\":1.0.17\":{\"uncompressed\":\"webfont_debug.js\",\"compressed\":\"webfont.js\"},\":1.0.3\":{\"uncompressed\":\"webfont_debug.js\",\"compressed\":\"webfont.js\"},\":1.0.16\":{\"uncompressed\":\"webfont_debug.js\",\"compressed\":\"webfont.js\"},\":1.0.9\":{\"uncompressed\":\"webfont_debug.js\",\"compressed\":\"webfont.js\"},\":1.0.21\":{\"uncompressed\":\"webfont_debug.js\",\"compressed\":\"webfont.js\"},\":1.0.22\":{\"uncompressed\":\"webfont_debug.js\",\"compressed\":\"webfont.js\"},\":1.0.25\":{\"uncompressed\":\"webfont_debug.js\",\"compressed\":\"webfont.js\"},\":1.0.26\":{\"uncompressed\":\"webfont_debug.js\",\"compressed\":\"webfont.js\"},\":1.0.23\":{\"uncompressed\":\"webfont_debug.js\",\"compressed\":\"webfont.js\"},\":1.0.24\":{\"uncompressed\":\"webfont_debug.js\",\"compressed\":\"webfont.js\"}},\"aliases\":{\":1\":\"1.0.29\",\":1.0\":\"1.0.29\"}},\":mootools\":{\"versions\":{\":1.3.1\":{\"uncompressed\":\"mootools.js\",\"compressed\":\"mootools-yui-compressed.js\"},\":1.1.1\":{\"uncompressed\":\"mootools.js\",\"compressed\":\"mootools-yui-compressed.js\"},\":1.3.0\":{\"uncompressed\":\"mootools.js\",\"compressed\":\"mootools-yui-compressed.js\"},\":1.3.2\":{\"uncompressed\":\"mootools.js\",\"compressed\":\"mootools-yui-compressed.js\"},\":1.1.2\":{\"uncompressed\":\"mootools.js\",\"compressed\":\"mootools-yui-compressed.js\"},\":1.2.3\":{\"uncompressed\":\"mootools.js\",\"compressed\":\"mootools-yui-compressed.js\"},\":1.2.4\":{\"uncompressed\":\"mootools.js\",\"compressed\":\"mootools-yui-compressed.js\"},\":1.2.1\":{\"uncompressed\":\"mootools.js\",\"compressed\":\"mootools-yui-compressed.js\"},\":1.2.2\":{\"uncompressed\":\"mootools.js\",\"compressed\":\"mootools-yui-compressed.js\"},\":1.2.5\":{\"uncompressed\":\"mootools.js\",\"compressed\":\"mootools-yui-compressed.js\"},\":1.4.0\":{\"uncompressed\":\"mootools.js\",\"compressed\":\"mootools-yui-compressed.js\"},\":1.4.1\":{\"uncompressed\":\"mootools.js\",\"compressed\":\"mootools-yui-compressed.js\"},\":1.4.2\":{\"uncompressed\":\"mootools.js\",\"compressed\":\"mootools-yui-compressed.js\"}},\"aliases\":{\":1\":\"1.1.2\",\":1.11\":\"1.1.1\",\":1.4\":\"1.4.2\",\":1.3\":\"1.3.2\",\":1.2\":\"1.2.5\",\":1.1\":\"1.1.2\"}},\":jqueryui\":{\"versions\":{\":1.8.0\":{\"uncompressed\":\"jquery-ui.js\",\"compressed\":\"jquery-ui.min.js\"},\":1.8.2\":{\"uncompressed\":\"jquery-ui.js\",\"compressed\":\"jquery-ui.min.js\"},\":1.8.1\":{\"uncompressed\":\"jquery-ui.js\",\"compressed\":\"jquery-ui.min.js\"},\":1.8.15\":{\"uncompressed\":\"jquery-ui.js\",\"compressed\":\"jquery-ui.min.js\"},\":1.8.14\":{\"uncompressed\":\"jquery-ui.js\",\"compressed\":\"jquery-ui.min.js\"},\":1.8.13\":{\"uncompressed\":\"jquery-ui.js\",\"compressed\":\"jquery-ui.min.js\"},\":1.8.12\":{\"uncompressed\":\"jquery-ui.js\",\"compressed\":\"jquery-ui.min.js\"},\":1.8.11\":{\"uncompressed\":\"jquery-ui.js\",\"compressed\":\"jquery-ui.min.js\"},\":1.8.10\":{\"uncompressed\":\"jquery-ui.js\",\"compressed\":\"jquery-ui.min.js\"},\":1.8.17\":{\"uncompressed\":\"jquery-ui.js\",\"compressed\":\"jquery-ui.min.js\"},\":1.8.16\":{\"uncompressed\":\"jquery-ui.js\",\"compressed\":\"jquery-ui.min.js\"},\":1.6.0\":{\"uncompressed\":\"jquery-ui.js\",\"compressed\":\"jquery-ui.min.js\"},\":1.8.9\":{\"uncompressed\":\"jquery-ui.js\",\"compressed\":\"jquery-ui.min.js\"},\":1.8.7\":{\"uncompressed\":\"jquery-ui.js\",\"compressed\":\"jquery-ui.min.js\"},\":1.8.8\":{\"uncompressed\":\"jquery-ui.js\",\"compressed\":\"jquery-ui.min.js\"},\":1.7.2\":{\"uncompressed\":\"jquery-ui.js\",\"compressed\":\"jquery-ui.min.js\"},\":1.8.5\":{\"uncompressed\":\"jquery-ui.js\",\"compressed\":\"jquery-ui.min.js\"},\":1.7.3\":{\"uncompressed\":\"jquery-ui.js\",\"compressed\":\"jquery-ui.min.js\"},\":1.8.6\":{\"uncompressed\":\"jquery-ui.js\",\"compressed\":\"jquery-ui.min.js\"},\":1.7.0\":{\"uncompressed\":\"jquery-ui.js\",\"compressed\":\"jquery-ui.min.js\"},\":1.7.1\":{\"uncompressed\":\"jquery-ui.js\",\"compressed\":\"jquery-ui.min.js\"},\":1.8.4\":{\"uncompressed\":\"jquery-ui.js\",\"compressed\":\"jquery-ui.min.js\"},\":1.5.3\":{\"uncompressed\":\"jquery-ui.js\",\"compressed\":\"jquery-ui.min.js\"},\":1.5.2\":{\"uncompressed\":\"jquery-ui.js\",\"compressed\":\"jquery-ui.min.js\"}},\"aliases\":{\":1.8\":\"1.8.17\",\":1.7\":\"1.7.3\",\":1.6\":\"1.6.0\",\":1\":\"1.8.17\",\":1.5\":\"1.5.3\",\":1.8.3\":\"1.8.4\"}},\":chrome-frame\":{\"versions\":{\":1.0.2\":{\"uncompressed\":\"CFInstall.js\",\"compressed\":\"CFInstall.min.js\"},\":1.0.1\":{\"uncompressed\":\"CFInstall.js\",\"compressed\":\"CFInstall.min.js\"},\":1.0.0\":{\"uncompressed\":\"CFInstall.js\",\"compressed\":\"CFInstall.min.js\"}},\"aliases\":{\":1\":\"1.0.2\",\":1.0\":\"1.0.2\"}},\":jquery\":{\"versions\":{\":1.6.2\":{\"uncompressed\":\"jquery.js\",\"compressed\":\"jquery.min.js\"},\":1.3.1\":{\"uncompressed\":\"jquery.js\",\"compressed\":\"jquery.min.js\"},\":1.6.1\":{\"uncompressed\":\"jquery.js\",\"compressed\":\"jquery.min.js\"},\":1.3.0\":{\"uncompressed\":\"jquery.js\",\"compressed\":\"jquery.min.js\"},\":1.6.4\":{\"uncompressed\":\"jquery.js\",\"compressed\":\"jquery.min.js\"},\":1.6.3\":{\"uncompressed\":\"jquery.js\",\"compressed\":\"jquery.min.js\"},\":1.3.2\":{\"uncompressed\":\"jquery.js\",\"compressed\":\"jquery.min.js\"},\":1.6.0\":{\"uncompressed\":\"jquery.js\",\"compressed\":\"jquery.min.js\"},\":1.2.3\":{\"uncompressed\":\"jquery.js\",\"compressed\":\"jquery.min.js\"},\":1.7.0\":{\"uncompressed\":\"jquery.js\",\"compressed\":\"jquery.min.js\"},\":1.7.1\":{\"uncompressed\":\"jquery.js\",\"compressed\":\"jquery.min.js\"},\":1.2.6\":{\"uncompressed\":\"jquery.js\",\"compressed\":\"jquery.min.js\"},\":1.4.3\":{\"uncompressed\":\"jquery.js\",\"compressed\":\"jquery.min.js\"},\":1.4.4\":{\"uncompressed\":\"jquery.js\",\"compressed\":\"jquery.min.js\"},\":1.5.1\":{\"uncompressed\":\"jquery.js\",\"compressed\":\"jquery.min.js\"},\":1.5.0\":{\"uncompressed\":\"jquery.js\",\"compressed\":\"jquery.min.js\"},\":1.4.0\":{\"uncompressed\":\"jquery.js\",\"compressed\":\"jquery.min.js\"},\":1.5.2\":{\"uncompressed\":\"jquery.js\",\"compressed\":\"jquery.min.js\"},\":1.4.1\":{\"uncompressed\":\"jquery.js\",\"compressed\":\"jquery.min.js\"},\":1.4.2\":{\"uncompressed\":\"jquery.js\",\"compressed\":\"jquery.min.js\"}},\"aliases\":{\":1.7\":\"1.7.1\",\":1.6\":\"1.6.4\",\":1\":\"1.7.1\",\":1.5\":\"1.5.2\",\":1.4\":\"1.4.4\",\":1.3\":\"1.3.2\",\":1.2\":\"1.2.6\"}},\":dojo\":{\"versions\":{\":1.3.1\":{\"uncompressed\":\"dojo/dojo.xd.js.uncompressed.js\",\"compressed\":\"dojo/dojo.xd.js\"},\":1.3.0\":{\"uncompressed\":\"dojo/dojo.xd.js.uncompressed.js\",\"compressed\":\"dojo/dojo.xd.js\"},\":1.6.1\":{\"uncompressed\":\"dojo/dojo.xd.js.uncompressed.js\",\"compressed\":\"dojo/dojo.xd.js\"},\":1.1.1\":{\"uncompressed\":\"dojo/dojo.xd.js.uncompressed.js\",\"compressed\":\"dojo/dojo.xd.js\"},\":1.3.2\":{\"uncompressed\":\"dojo/dojo.xd.js.uncompressed.js\",\"compressed\":\"dojo/dojo.xd.js\"},\":1.6.0\":{\"uncompressed\":\"dojo/dojo.xd.js.uncompressed.js\",\"compressed\":\"dojo/dojo.xd.js\"},\":1.2.3\":{\"uncompressed\":\"dojo/dojo.xd.js.uncompressed.js\",\"compressed\":\"dojo/dojo.xd.js\"},\":1.7.2\":{\"uncompressed\":\"dojo/dojo.js.uncompressed.js\",\"compressed\":\"dojo/dojo.js\"},\":1.7.0\":{\"uncompressed\":\"dojo/dojo.js.uncompressed.js\",\"compressed\":\"dojo/dojo.js\"},\":1.7.1\":{\"uncompressed\":\"dojo/dojo.js.uncompressed.js\",\"compressed\":\"dojo/dojo.js\"},\":1.4.3\":{\"uncompressed\":\"dojo/dojo.xd.js.uncompressed.js\",\"compressed\":\"dojo/dojo.xd.js\"},\":1.5.1\":{\"uncompressed\":\"dojo/dojo.xd.js.uncompressed.js\",\"compressed\":\"dojo/dojo.xd.js\"},\":1.5.0\":{\"uncompressed\":\"dojo/dojo.xd.js.uncompressed.js\",\"compressed\":\"dojo/dojo.xd.js\"},\":1.2.0\":{\"uncompressed\":\"dojo/dojo.xd.js.uncompressed.js\",\"compressed\":\"dojo/dojo.xd.js\"},\":1.4.0\":{\"uncompressed\":\"dojo/dojo.xd.js.uncompressed.js\",\"compressed\":\"dojo/dojo.xd.js\"},\":1.4.1\":{\"uncompressed\":\"dojo/dojo.xd.js.uncompressed.js\",\"compressed\":\"dojo/dojo.xd.js\"}},\"aliases\":{\":1.7\":\"1.7.2\",\":1\":\"1.6.1\",\":1.6\":\"1.6.1\",\":1.5\":\"1.5.1\",\":1.4\":\"1.4.3\",\":1.3\":\"1.3.2\",\":1.2\":\"1.2.3\",\":1.1\":\"1.1.1\"}},\":prototype\":{\"versions\":{\":1.7.0.0\":{\"uncompressed\":\"prototype.js\",\"compressed\":\"prototype.js\"},\":1.6.0.2\":{\"uncompressed\":\"prototype.js\",\"compressed\":\"prototype.js\"},\":1.6.1.0\":{\"uncompressed\":\"prototype.js\",\"compressed\":\"prototype.js\"},\":1.6.0.3\":{\"uncompressed\":\"prototype.js\",\"compressed\":\"prototype.js\"}},\"aliases\":{\":1.7\":\"1.7.0.0\",\":1.6.1\":\"1.6.1.0\",\":1\":\"1.7.0.0\",\":1.6\":\"1.6.1.0\",\":1.7.0\":\"1.7.0.0\",\":1.6.0\":\"1.6.0.3\"}}});\n}\n"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/js/jquery.simplemodal-1.4.js",
    "content": "/*\n * SimpleModal 1.4 - jQuery Plugin\n * http://www.ericmmartin.com/projects/simplemodal/\n * Copyright (c) 2010 Eric Martin (http://twitter.com/ericmmartin)\n * Dual licensed under the MIT and GPL licenses\n * Revision: $Id: jquery.simplemodal.js 256 2010-07-27 23:05:43Z emartin24 $\n */\n\n/**\n * SimpleModal is a lightweight jQuery plugin that provides a simple\n * interface to create a modal dialog.\n *\n * The goal of SimpleModal is to provide developers with a cross-browser\n * overlay and container that will be populated with data provided to\n * SimpleModal.\n *\n * There are two ways to call SimpleModal:\n * 1) As a chained function on a jQuery object, like $('#myDiv').modal();.\n * This call would place the DOM object, #myDiv, inside a modal dialog.\n * Chaining requires a jQuery object. An optional options object can be\n * passed as a parameter.\n *\n * @example $('<div>my data</div>').modal({options});\n * @example $('#myDiv').modal({options});\n * @example jQueryObject.modal({options});\n *\n * 2) As a stand-alone function, like $.modal(data). The data parameter\n * is required and an optional options object can be passed as a second\n * parameter. This method provides more flexibility in the types of data\n * that are allowed. The data could be a DOM object, a jQuery object, HTML\n * or a string.\n *\n * @example $.modal('<div>my data</div>', {options});\n * @example $.modal('my data', {options});\n * @example $.modal($('#myDiv'), {options});\n * @example $.modal(jQueryObject, {options});\n * @example $.modal(document.getElementById('myDiv'), {options});\n *\n * A SimpleModal call can contain multiple elements, but only one modal\n * dialog can be created at a time. Which means that all of the matched\n * elements will be displayed within the modal container.\n *\n * SimpleModal internally sets the CSS needed to display the modal dialog\n * properly in all browsers, yet provides the developer with the flexibility\n * to easily control the look and feel. The styling for SimpleModal can be\n * done through external stylesheets, or through SimpleModal, using the\n * overlayCss, containerCss, and dataCss options.\n *\n * SimpleModal has been tested in the following browsers:\n * - IE 6, 7, 8\n * - Firefox 2, 3\n * - Opera 9, 10\n * - Safari 3, 4, 5\n * - Chrome 1, 2, 3, 4, 5\n *\n * @name SimpleModal\n * @type jQuery\n * @requires jQuery v1.2.4\n * @cat Plugins/Windows and Overlays\n * @author Eric Martin (http://ericmmartin.com)\n * @version 1.4\n */\n;(function ($) {\n\tvar ie6 = $.browser.msie && parseInt($.browser.version) === 6 && typeof window['XMLHttpRequest'] !== 'object',\n\t\tieQuirks = null,\n\t\tw = [];\n\n\t/*\n\t * Create and display a modal dialog.\n\t *\n\t * @param {string, object} data A string, jQuery object or DOM object\n\t * @param {object} [options] An optional object containing options overrides\n\t */\n\t$.modal = function (data, options) {\n\t\treturn $.modal.impl.init(data, options);\n\t};\n\n\t/*\n\t * Close the modal dialog.\n\t */\n\t$.modal.close = function () {\n\t\t$.modal.impl.close();\n\t};\n\n\t/*\n\t * Set focus on first or last visible input in the modal dialog. To focus on the last\n\t * element, call $.modal.focus('last'). If no input elements are found, focus is placed\n\t * on the data wrapper element.\n\t */\n\t$.modal.focus = function (pos) {\n\t\t$.modal.impl.focus(pos);\n\t};\n\n\t/*\n\t * Determine and set the dimensions of the modal dialog container.\n\t * setPosition() is called if the autoPosition option is true.\n\t */\n\t$.modal.setContainerDimensions = function () {\n\t\t$.modal.impl.setContainerDimensions();\n\t};\n\n\t/*\n\t * Re-position the modal dialog.\n\t */\n\t$.modal.setPosition = function () {\n\t\t$.modal.impl.setPosition();\n\t};\n\n\t/*\n\t * Update the modal dialog. If new dimensions are passed, they will be used to determine\n\t * the dimensions of the container.\n\t *\n\t * setContainerDimensions() is called, which in turn calls setPosition(), if enabled.\n\t * Lastly, focus() is called is the focus option is true.\n\t */\n\t$.modal.update = function (height, width) {\n\t\t$.modal.impl.update(height, width);\n\t};\n\n\t/*\n\t * Chained function to create a modal dialog.\n\t *\n\t * @param {object} [options] An optional object containing options overrides\n\t */\n\t$.fn.modal = function (options) {\n\t\treturn $.modal.impl.init(this, options);\n\t};\n\n\t/*\n\t * SimpleModal default options\n\t *\n\t * appendTo:\t\t(String:'body') The jQuery selector to append the elements to. For .NET, use 'form'.\n\t * focus:\t\t\t(Boolean:true) Focus in the first visible, enabled element?\n\t * opacity:\t\t\t(Number:50) The opacity value for the overlay div, from 0 - 100\n\t * overlayId:\t\t(String:'simplemodal-overlay') The DOM element id for the overlay div\n\t * overlayCss:\t\t(Object:{}) The CSS styling for the overlay div\n\t * containerId:\t\t(String:'simplemodal-container') The DOM element id for the container div\n\t * containerCss:\t(Object:{}) The CSS styling for the container div\n\t * dataId:\t\t\t(String:'simplemodal-data') The DOM element id for the data div\n\t * dataCss:\t\t\t(Object:{}) The CSS styling for the data div\n\t * minHeight:\t\t(Number:null) The minimum height for the container\n\t * minWidth:\t\t(Number:null) The minimum width for the container\n\t * maxHeight:\t\t(Number:null) The maximum height for the container. If not specified, the window height is used.\n\t * maxWidth:\t\t(Number:null) The maximum width for the container. If not specified, the window width is used.\n\t * autoResize:\t\t(Boolean:false) Automatically resize the container if it exceeds the browser window dimensions?\n\t * autoPosition:\t(Boolean:true) Automatically position the container upon creation and on window resize?\n\t * zIndex:\t\t\t(Number: 1000) Starting z-index value\n\t * close:\t\t\t(Boolean:true) If true, closeHTML, escClose and overClose will be used if set.\n\t \t\t\t\t\t\t\tIf false, none of them will be used.\n\t * closeHTML:\t\t(String:'<a class=\"modalCloseImg\" title=\"Close\"></a>') The HTML for the default close link.\n\t\t\t\t\t\t\t\tSimpleModal will automatically add the closeClass to this element.\n\t * closeClass:\t\t(String:'simplemodal-close') The CSS class used to bind to the close event\n\t * escClose:\t\t(Boolean:true) Allow Esc keypress to close the dialog?\n\t * overlayClose:\t(Boolean:false) Allow click on overlay to close the dialog?\n\t * position:\t\t(Array:null) Position of container [top, left]. Can be number of pixels or percentage\n\t * persist:\t\t\t(Boolean:false) Persist the data across modal calls? Only used for existing\n\t\t\t\t\t\t\t\tDOM elements. If true, the data will be maintained across modal calls, if false,\n\t\t\t\t\t\t\t\tthe data will be reverted to its original state.\n\t * modal:\t\t\t(Boolean:true) User will be unable to interact with the page below the modal or tab away from the dialog.\n\t\t\t\t\t\t\t\tIf false, the overlay, iframe, and certain events will be disabled allowing the user to interact\n\t\t\t\t\t\t\t\twith the page below the dialog.\n\t * onOpen:\t\t\t(Function:null) The callback function used in place of SimpleModal's open\n\t * onShow:\t\t\t(Function:null) The callback function used after the modal dialog has opened\n\t * onClose:\t\t\t(Function:null) The callback function used in place of SimpleModal's close\n\t */\n\t$.modal.defaults = {\n\t\tappendTo: 'body',\n\t\tfocus: true,\n\t\topacity: 50,\n\t\toverlayId: 'simplemodal-overlay',\n\t\toverlayCss: {},\n\t\tcontainerId: 'simplemodal-container',\n\t\tcontainerCss: {},\n\t\tdataId: 'simplemodal-data',\n\t\tdataCss: {},\n\t\tminHeight: null,\n\t\tminWidth: null,\n\t\tmaxHeight: null,\n\t\tmaxWidth: null,\n\t\tautoResize: false,\n\t\tautoPosition: true,\n\t\tzIndex: 1000,\n\t\tclose: true,\n\t\tcloseHTML: '<a class=\"modalCloseImg\" title=\"Close\"></a>',\n\t\tcloseClass: 'simplemodal-close',\n\t\tescClose: true,\n\t\toverlayClose: false,\n\t\tposition: null,\n\t\tpersist: false,\n\t\tmodal: true,\n\t\tonOpen: null,\n\t\tonShow: null,\n\t\tonClose: null\n\t};\n\n\t/*\n\t * Main modal object\n\t * o = options\n\t */\n\t$.modal.impl = {\n\t\t/*\n\t\t * Contains the modal dialog elements and is the object passed\n\t\t * back to the callback (onOpen, onShow, onClose) functions\n\t\t */\n\t\td: {},\n\t\t/*\n\t\t * Initialize the modal dialog\n\t\t */\n\t\tinit: function (data, options) {\n\t\t\tvar s = this;\n\n\t\t\t// don't allow multiple calls\n\t\t\tif (s.d.data) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t// $.boxModel is undefined if checked earlier\n\t\t\tieQuirks = $.browser.msie && !$.boxModel;\n\n\t\t\t// merge defaults and user options\n\t\t\ts.o = $.extend({}, $.modal.defaults, options);\n\n\t\t\t// keep track of z-index\n\t\t\ts.zIndex = s.o.zIndex;\n\n\t\t\t// set the onClose callback flag\n\t\t\ts.occb = false;\n\n\t\t\t// determine how to handle the data based on its type\n\t\t\tif (typeof data === 'object') {\n\t\t\t\t// convert DOM object to a jQuery object\n\t\t\t\tdata = data instanceof jQuery ? data : $(data);\n\t\t\t\ts.d.placeholder = false;\n\n\t\t\t\t// if the object came from the DOM, keep track of its parent\n\t\t\t\tif (data.parent().parent().size() > 0) {\n\t\t\t\t\tdata.before($('<span></span>')\n\t\t\t\t\t\t.attr('id', 'simplemodal-placeholder')\n\t\t\t\t\t\t.css({display: 'none'}));\n\n\t\t\t\t\ts.d.placeholder = true;\n\t\t\t\t\ts.display = data.css('display');\n\n\t\t\t\t\t// persist changes? if not, make a clone of the element\n\t\t\t\t\tif (!s.o.persist) {\n\t\t\t\t\t\ts.d.orig = data.clone(true);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if (typeof data === 'string' || typeof data === 'number') {\n\t\t\t\t// just insert the data as innerHTML\n\t\t\t\tdata = $('<div></div>').html(data);\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// unsupported data type!\n\t\t\t\talert('SimpleModal Error: Unsupported data type: ' + typeof data);\n\t\t\t\treturn s;\n\t\t\t}\n\n\t\t\t// create the modal overlay, container and, if necessary, iframe\n\t\t\ts.create(data);\n\t\t\tdata = null;\n\n\t\t\t// display the modal dialog\n\t\t\ts.open();\n\n\t\t\t// useful for adding events/manipulating data in the modal dialog\n\t\t\tif ($.isFunction(s.o.onShow)) {\n\t\t\t\ts.o.onShow.apply(s, [s.d]);\n\t\t\t}\n\n\t\t\t// don't break the chain =)\n\t\t\treturn s;\n\t\t},\n\t\t/*\n\t\t * Create and add the modal overlay and container to the page\n\t\t */\n\t\tcreate: function (data) {\n\t\t\tvar s = this;\n\n\t\t\t// get the window properties\n\t\t\tw = s.getDimensions();\n\n\t\t\t// add an iframe to prevent select options from bleeding through\n\t\t\tif (s.o.modal && ie6) {\n\t\t\t\ts.d.iframe = $('<iframe src=\"javascript:false;\"></iframe>')\n\t\t\t\t\t.css($.extend(s.o.iframeCss, {\n\t\t\t\t\t\tdisplay: 'none',\n\t\t\t\t\t\topacity: 0,\n\t\t\t\t\t\tposition: 'fixed',\n\t\t\t\t\t\theight: w[0],\n\t\t\t\t\t\twidth: w[1],\n\t\t\t\t\t\tzIndex: s.o.zIndex,\n\t\t\t\t\t\ttop: 0,\n\t\t\t\t\t\tleft: 0\n\t\t\t\t\t}))\n\t\t\t\t\t.appendTo(s.o.appendTo);\n\t\t\t}\n\n\t\t\t// create the overlay\n\t\t\ts.d.overlay = $('<div></div>')\n\t\t\t\t.attr('id', s.o.overlayId)\n\t\t\t\t.addClass('simplemodal-overlay')\n\t\t\t\t.css($.extend(s.o.overlayCss, {\n\t\t\t\t\tdisplay: 'none',\n\t\t\t\t\topacity: s.o.opacity / 100,\n\t\t\t\t\theight: s.o.modal ? w[0] : 0,\n\t\t\t\t\twidth: s.o.modal ? w[1] : 0,\n\t\t\t\t\tposition: 'fixed',\n\t\t\t\t\tleft: 0,\n\t\t\t\t\ttop: 0,\n\t\t\t\t\tzIndex: s.o.zIndex + 1\n\t\t\t\t}))\n\t\t\t\t.appendTo(s.o.appendTo);\n\n\t\t\t// create the container\n\t\t\ts.d.container = $('<div></div>')\n\t\t\t\t.attr('id', s.o.containerId)\n\t\t\t\t.addClass('simplemodal-container')\n\t\t\t\t.css($.extend(s.o.containerCss, {\n\t\t\t\t\tdisplay: 'none',\n\t\t\t\t\tposition: 'fixed',\n\t\t\t\t\tzIndex: s.o.zIndex + 2\n\t\t\t\t}))\n\t\t\t\t.append(s.o.close && s.o.closeHTML\n\t\t\t\t\t? $(s.o.closeHTML).addClass(s.o.closeClass)\n\t\t\t\t\t: '')\n\t\t\t\t.appendTo(s.o.appendTo);\n\n\t\t\ts.d.wrap = $('<div></div>')\n\t\t\t\t.attr('tabIndex', -1)\n\t\t\t\t.addClass('simplemodal-wrap')\n\t\t\t\t.css({height: '100%', outline: 0, width: '100%'})\n\t\t\t\t.appendTo(s.d.container);\n\n\t\t\t// add styling and attributes to the data\n\t\t\t// append to body to get correct dimensions, then move to wrap\n\t\t\ts.d.data = data\n\t\t\t\t.attr('id', data.attr('id') || s.o.dataId)\n\t\t\t\t.addClass('simplemodal-data')\n\t\t\t\t.css($.extend(s.o.dataCss, {\n\t\t\t\t\t\tdisplay: 'none'\n\t\t\t\t}))\n\t\t\t\t.appendTo('body');\n\t\t\tdata = null;\n\n\t\t\ts.setContainerDimensions();\n\t\t\ts.d.data.appendTo(s.d.wrap);\n\n\t\t\t// fix issues with IE\n\t\t\tif (ie6 || ieQuirks) {\n\t\t\t\ts.fixIE();\n\t\t\t}\n\t\t},\n\t\t/*\n\t\t * Bind events\n\t\t */\n\t\tbindEvents: function () {\n\t\t\tvar s = this;\n\n\t\t\t// bind the close event to any element with the closeClass class\n\t\t\t$('.' + s.o.closeClass).bind('click.simplemodal', function (e) {\n\t\t\t\te.preventDefault();\n\t\t\t\ts.close();\n\t\t\t});\n\n\t\t\t// bind the overlay click to the close function, if enabled\n\t\t\tif (s.o.modal && s.o.close && s.o.overlayClose) {\n\t\t\t\ts.d.overlay.bind('click.simplemodal', function (e) {\n\t\t\t\t\te.preventDefault();\n\t\t\t\t\ts.close();\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// bind keydown events\n\t\t\t$(document).bind('keydown.simplemodal', function (e) {\n\t\t\t\tif (s.o.modal && e.keyCode === 9) { // TAB\n\t\t\t\t\ts.watchTab(e);\n\t\t\t\t}\n\t\t\t\telse if ((s.o.close && s.o.escClose) && e.keyCode === 27) { // ESC\n\t\t\t\t\te.preventDefault();\n\t\t\t\t\ts.close();\n\t\t\t\t}\n\t\t\t});\n\n\t\t\t// update window size\n\t\t\t$(window).bind('resize.simplemodal', function () {\n\t\t\t\t// redetermine the window width/height\n\t\t\t\tw = s.getDimensions();\n\n\t\t\t\t// reposition the dialog\n\t\t\t\ts.o.autoResize ? s.setContainerDimensions() : s.o.autoPosition && s.setPosition();\n\n\t\t\t\tif (ie6 || ieQuirks) {\n\t\t\t\t\ts.fixIE();\n\t\t\t\t}\n\t\t\t\telse if (s.o.modal) {\n\t\t\t\t\t// update the iframe & overlay\n\t\t\t\t\ts.d.iframe && s.d.iframe.css({height: w[0], width: w[1]});\n\t\t\t\t\ts.d.overlay.css({height: w[0], width: w[1]});\n\t\t\t\t}\n\t\t\t});\n\t\t},\n\t\t/*\n\t\t * Unbind events\n\t\t */\n\t\tunbindEvents: function () {\n\t\t\t$('.' + this.o.closeClass).unbind('click.simplemodal');\n\t\t\t$(document).unbind('keydown.simplemodal');\n\t\t\t$(window).unbind('resize.simplemodal');\n\t\t\tthis.d.overlay.unbind('click.simplemodal');\n\t\t},\n\t\t/*\n\t\t * Fix issues in IE6 and IE7 in quirks mode\n\t\t */\n\t\tfixIE: function () {\n\t\t\tvar s = this, p = s.o.position;\n\n\t\t\t// simulate fixed position - adapted from BlockUI\n\t\t\t$.each([s.d.iframe || null, !s.o.modal ? null : s.d.overlay, s.d.container], function (i, el) {\n\t\t\t\tif (el) {\n\t\t\t\t\tvar bch = 'document.body.clientHeight', bcw = 'document.body.clientWidth',\n\t\t\t\t\t\tbsh = 'document.body.scrollHeight', bsl = 'document.body.scrollLeft',\n\t\t\t\t\t\tbst = 'document.body.scrollTop', bsw = 'document.body.scrollWidth',\n\t\t\t\t\t\tch = 'document.documentElement.clientHeight', cw = 'document.documentElement.clientWidth',\n\t\t\t\t\t\tsl = 'document.documentElement.scrollLeft', st = 'document.documentElement.scrollTop',\n\t\t\t\t\t\ts = el[0].style;\n\n\t\t\t\t\ts.position = 'absolute';\n\t\t\t\t\tif (i < 2) {\n\t\t\t\t\t\ts.removeExpression('height');\n\t\t\t\t\t\ts.removeExpression('width');\n\t\t\t\t\t\ts.setExpression('height','' + bsh + ' > ' + bch + ' ? ' + bsh + ' : ' + bch + ' + \"px\"');\n\t\t\t\t\t\ts.setExpression('width','' + bsw + ' > ' + bcw + ' ? ' + bsw + ' : ' + bcw + ' + \"px\"');\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tvar te, le;\n\t\t\t\t\t\tif (p && p.constructor === Array) {\n\t\t\t\t\t\t\tvar top = p[0]\n\t\t\t\t\t\t\t\t? typeof p[0] === 'number' ? p[0].toString() : p[0].replace(/px/, '')\n\t\t\t\t\t\t\t\t: el.css('top').replace(/px/, '');\n\t\t\t\t\t\t\tte = top.indexOf('%') === -1\n\t\t\t\t\t\t\t\t? top + ' + (t = ' + st + ' ? ' + st + ' : ' + bst + ') + \"px\"'\n\t\t\t\t\t\t\t\t: parseInt(top.replace(/%/, '')) + ' * ((' + ch + ' || ' + bch + ') / 100) + (t = ' + st + ' ? ' + st + ' : ' + bst + ') + \"px\"';\n\n\t\t\t\t\t\t\tif (p[1]) {\n\t\t\t\t\t\t\t\tvar left = typeof p[1] === 'number' ? p[1].toString() : p[1].replace(/px/, '');\n\t\t\t\t\t\t\t\tle = left.indexOf('%') === -1\n\t\t\t\t\t\t\t\t\t? left + ' + (t = ' + sl + ' ? ' + sl + ' : ' + bsl + ') + \"px\"'\n\t\t\t\t\t\t\t\t\t: parseInt(left.replace(/%/, '')) + ' * ((' + cw + ' || ' + bcw + ') / 100) + (t = ' + sl + ' ? ' + sl + ' : ' + bsl + ') + \"px\"';\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\tte = '(' + ch + ' || ' + bch + ') / 2 - (this.offsetHeight / 2) + (t = ' + st + ' ? ' + st + ' : ' + bst + ') + \"px\"';\n\t\t\t\t\t\t\tle = '(' + cw + ' || ' + bcw + ') / 2 - (this.offsetWidth / 2) + (t = ' + sl + ' ? ' + sl + ' : ' + bsl + ') + \"px\"';\n\t\t\t\t\t\t}\n\t\t\t\t\t\ts.removeExpression('top');\n\t\t\t\t\t\ts.removeExpression('left');\n\t\t\t\t\t\ts.setExpression('top', te);\n\t\t\t\t\t\ts.setExpression('left', le);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\t\t},\n\t\t/*\n\t\t * Place focus on the first or last visible input\n\t\t */\n\t\tfocus: function (pos) {\n\t\t\tvar s = this, p = pos && $.inArray(pos, ['first', 'last']) !== -1 ? pos : 'first';\n\n\t\t\t// focus on dialog or the first visible/enabled input element\n\t\t\tvar input = $(':input:enabled:visible:' + p, s.d.wrap);\n\t\t\tsetTimeout(function () {\n\t\t\t\tinput.length > 0 ? input.focus() : s.d.wrap.focus();\n\t\t\t}, 10);\n\t\t},\n\t\tgetDimensions: function () {\n\t\t\tvar el = $(window);\n\n\t\t\t// fix a jQuery/Opera bug with determining the window height\n\t\t\tvar h = $.browser.opera && $.browser.version > '9.5' && $.fn.jquery < '1.3'\n\t\t\t\t\t\t|| $.browser.opera && $.browser.version < '9.5' && $.fn.jquery > '1.2.6'\n\t\t\t\t? el[0].innerHeight : el.height();\n\n\t\t\treturn [h, el.width()];\n\t\t},\n\t\tgetVal: function (v, d) {\n\t\t\treturn v ? (typeof v === 'number' ? v\n\t\t\t\t\t: v === 'auto' ? 0\n\t\t\t\t\t: v.indexOf('%') > 0 ? ((parseInt(v.replace(/%/, '')) / 100) * (d === 'h' ? w[0] : w[1]))\n\t\t\t\t\t: parseInt(v.replace(/px/, '')))\n\t\t\t\t: null;\n\t\t},\n\t\t/*\n\t\t * Update the container. Set new dimensions, if provided.\n\t\t * Focus, if enabled. Re-bind events.\n\t\t */\n\t\tupdate: function (height, width) {\n\t\t\tvar s = this;\n\n\t\t\t// prevent update if dialog does not exist\n\t\t\tif (!s.d.data) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t// reset orig values\n\t\t\ts.d.origHeight = s.getVal(height, 'h');\n\t\t\ts.d.origWidth = s.getVal(width, 'w');\n\n\t\t\t// hide data to prevent screen flicker\n\t\t\ts.d.data.hide();\n\t\t\theight && s.d.container.css('height', height);\n\t\t\twidth && s.d.container.css('width', width);\n\t\t\ts.setContainerDimensions();\n\t\t\ts.d.data.show();\n\t\t\ts.o.focus && s.focus();\n\n\t\t\t// rebind events\n\t\t\ts.unbindEvents();\n\t\t\ts.bindEvents();\n\t\t},\n\t\tsetContainerDimensions: function () {\n\t\t\tvar s = this;\n\n\t\t\t// get the dimensions for the container and data\n\t\t\tvar ch = s.d.origHeight ? s.d.origHeight : $.browser.opera ? s.d.container.height() : s.getVal(s.d.container.css('height'), 'h'),\n\t\t\t\tcw = s.d.origWidth ? s.d.origWidth : $.browser.opera ? s.d.container.width() : s.getVal(s.d.container.css('width'), 'w'),\n\t\t\t\tdh = s.d.data.outerHeight(true), dw = s.d.data.outerWidth(true);\n\n\t\t\ts.d.origHeight = s.d.origHeight || ch;\n\t\t\ts.d.origWidth = s.d.origWidth || cw;\n\n\t\t\t// mxoh = max option height, mxow = max option width\n\t\t\tvar mxoh = s.o.maxHeight ? s.getVal(s.o.maxHeight, 'h') : null,\n\t\t\t\tmxow = s.o.maxWidth ? s.getVal(s.o.maxWidth, 'w') : null,\n\t\t\t\tmh = mxoh && mxoh < w[0] ? mxoh : w[0],\n\t\t\t\tmw = mxow && mxow < w[1] ? mxow : w[1];\n\n\t\t\t// moh = min option height\n\t\t\tvar moh = s.o.minHeight ? s.getVal(s.o.minHeight, 'h') : 'auto';\n\t\t\tif (!ch) {\n\t\t\t\tif (!dh) {ch = moh;}\n\t\t\t\telse {\n\t\t\t\t\tif (dh > mh) {ch = mh;}\n\t\t\t\t\telse if (s.o.minHeight && moh !== 'auto' && dh < moh) {ch = moh;}\n\t\t\t\t\telse {ch = dh;}\n\t\t\t\t}\n\t\t\t}\n\t\t\telse {\n\t\t\t\tch = s.o.autoResize && ch > mh ? mh : ch;\n\t\t\t}\n\n\t\t\t// mow = min option width\n\t\t\tvar mow = s.o.minWidth ? s.getVal(s.o.minWidth, 'w') : 'auto';\n\t\t\tif (!cw) {\n\t\t\t\tif (!dw) {cw = mow;}\n\t\t\t\telse {\n\t\t\t\t\tif (dw > mw) {cw = mw;}\n\t\t\t\t\telse if (s.o.minWidth && mow !== 'auto' && dw < mow) {cw = mow;}\n\t\t\t\t\telse {cw = dw;}\n\t\t\t\t}\n\t\t\t}\n\t\t\telse {\n\t\t\t\tcw = s.o.autoResize && cw > mw ? mw : cw;\n\t\t\t}\n\n\t\t\ts.d.container.css({height: ch, width: cw});\n\t\t\ts.d.wrap.css({overflow: (dh > ch || dw > cw) ? 'auto' : 'visible'});\n\t\t\ts.o.autoPosition && s.setPosition();\n\t\t},\n\t\tsetPosition: function () {\n\t\t\tvar s = this, top, left,\n\t\t\t\thc = (w[0]/2) - (s.d.container.outerHeight(true)/2),\n\t\t\t\tvc = (w[1]/2) - (s.d.container.outerWidth(true)/2);\n\n\t\t\tif (s.o.position && Object.prototype.toString.call(s.o.position) === '[object Array]') {\n\t\t\t\ttop = s.o.position[0] || hc;\n\t\t\t\tleft = s.o.position[1] || vc;\n\t\t\t} else {\n\t\t\t\ttop = hc;\n\t\t\t\tleft = vc;\n\t\t\t}\n\t\t\ts.d.container.css({left: left, top: top});\n\t\t},\n\t\twatchTab: function (e) {\n\t\t\tvar s = this;\n\n\t\t\tif ($(e.target).parents('.simplemodal-container').length > 0) {\n\t\t\t\t// save the list of inputs\n\t\t\t\ts.inputs = $(':input:enabled:visible:first, :input:enabled:visible:last', s.d.data[0]);\n\n\t\t\t\t// if it's the first or last tabbable element, refocus\n\t\t\t\tif ((!e.shiftKey && e.target === s.inputs[s.inputs.length -1]) ||\n\t\t\t\t\t\t(e.shiftKey && e.target === s.inputs[0]) ||\n\t\t\t\t\t\ts.inputs.length === 0) {\n\t\t\t\t\te.preventDefault();\n\t\t\t\t\tvar pos = e.shiftKey ? 'last' : 'first';\n\t\t\t\t\ts.focus(pos);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// might be necessary when custom onShow callback is used\n\t\t\t\te.preventDefault();\n\t\t\t\ts.focus();\n\t\t\t}\n\t\t},\n\t\t/*\n\t\t * Open the modal dialog elements\n\t\t * - Note: If you use the onOpen callback, you must \"show\" the\n\t\t *\t        overlay and container elements manually\n\t\t *         (the iframe will be handled by SimpleModal)\n\t\t */\n\t\topen: function () {\n\t\t\tvar s = this;\n\t\t\t// display the iframe\n\t\t\ts.d.iframe && s.d.iframe.show();\n\n\t\t\tif ($.isFunction(s.o.onOpen)) {\n\t\t\t\t// execute the onOpen callback\n\t\t\t\ts.o.onOpen.apply(s, [s.d]);\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// display the remaining elements\n\t\t\t\ts.d.overlay.show();\n\t\t\t\ts.d.container.show();\n\t\t\t\ts.d.data.show();\n\t\t\t}\n\n\t\t\ts.o.focus && s.focus();\n\n\t\t\t// bind default events\n\t\t\ts.bindEvents();\n\t\t},\n\t\t/*\n\t\t * Close the modal dialog\n\t\t * - Note: If you use an onClose callback, you must remove the\n\t\t *         overlay, container and iframe elements manually\n\t\t *\n\t\t * @param {boolean} external Indicates whether the call to this\n\t\t *     function was internal or external. If it was external, the\n\t\t *     onClose callback will be ignored\n\t\t */\n\t\tclose: function () {\n\t\t\tvar s = this;\n\n\t\t\t// prevent close when dialog does not exist\n\t\t\tif (!s.d.data) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t// remove the default events\n\t\t\ts.unbindEvents();\n\n\t\t\tif ($.isFunction(s.o.onClose) && !s.occb) {\n\t\t\t\t// set the onClose callback flag\n\t\t\t\ts.occb = true;\n\n\t\t\t\t// execute the onClose callback\n\t\t\t\ts.o.onClose.apply(s, [s.d]);\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// if the data came from the DOM, put it back\n\t\t\t\tif (s.d.placeholder) {\n\t\t\t\t\tvar ph = $('#simplemodal-placeholder');\n\t\t\t\t\t// save changes to the data?\n\t\t\t\t\tif (s.o.persist) {\n\t\t\t\t\t\t// insert the (possibly) modified data back into the DOM\n\t\t\t\t\t\tph.replaceWith(s.d.data.removeClass('simplemodal-data').css('display', s.display));\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\t// remove the current and insert the original,\n\t\t\t\t\t\t// unmodified data back into the DOM\n\t\t\t\t\t\ts.d.data.hide().remove();\n\t\t\t\t\t\tph.replaceWith(s.d.orig);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t// otherwise, remove it\n\t\t\t\t\ts.d.data.hide().remove();\n\t\t\t\t}\n\n\t\t\t\t// remove the remaining elements\n\t\t\t\ts.d.container.hide().remove();\n\t\t\t\ts.d.overlay.hide();\n\t\t\t\ts.d.iframe && s.d.iframe.hide().remove();\n\t\t\t\tsetTimeout(function(){\n\t\t\t\t\t// opera work-around\n\t\t\t\t\ts.d.overlay.remove();\n\n\t\t\t\t\t// reset the dialog object\n\t\t\t\t\ts.d = {};\n\t\t\t\t}, 10);\n\t\t\t}\n\t\t}\n\t};\n})(jQuery);\n"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/js/nodeCheck.js",
    "content": "function isRunning(nid,pid,stage) {\n\tif(stage == 'SELECT') {\n\t\tNode.isSelectRunning(nid,pid,callback);  \n\t}else if(stage == 'EXTRACT'){\n\t\tNode.isExtractRunning(nid,pid,callback);  \n\t}else if(stage == 'TRANSFORM') {\n\t\tNode.isTransformRunning(nid,pid,callback);  \n\t}else {\n\t\tNode.isLoadRunning(nid,pid,callback);  \n\t}\n}\n\nfunction aggregation(nid,pid,stage) {\n\tif(stage == 'SELECT') {\n\t\tNode.selectStageAggregation(nid,pid,callback);  \n\t}else if(stage == 'EXTRACT'){\n\t\tNode.extractStageAggregation(nid,pid,callback);  \n\t}else if(stage == 'TRANSFORM') {\n\t\tNode.transformStageAggregation(nid,pid,callback);  \n\t}else {\n\t\tNode.loadStageAggregation(nid,pid,callback);  \n\t}\n}\n\nfunction pending(nid,pid,stage) {\n\tif(stage == 'SELECT') {\n\t\tNode.selectPendingProcess(nid,pid,callback);  \n\t}else if(stage == 'EXTRACT'){\n\t\tNode.extractPendingProcess(nid,pid,callback);  \n\t}else if(stage == 'TRANSFORM') {\n\t\tNode.transformPendingProcess(nid,pid,callback);  \n\t}else {\n\t\tNode.loadPendingProcess(nid,pid,callback);  \n\t}\n}\n\nfunction callback(msg) {\n\talert(msg);\n}"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/js/pop.js",
    "content": "// JavaScript Document\r\n$(function() {\r\n\t\t\t\r\n\t\t\tfunction showModal(src, height, width) {\r\n\t\t\t\tjQuery.modal('<iframe src=\"' + src + '\" height=\"' + height + '\" width=\"' + width + '\" frameborder=\"0\" allowTransparency=true>', {\r\n\t\t\t\t\tcloseHTML : \"<input type='button' style='display:none'/>\",\r\n\t\t\t\t\tcloseClass: \"modalClose\",\r\n\t\t\t\t\topacity : 35,\r\n\t\t\t\t\toverlayCss : {\r\n\t\t\t\t\t\tbackgroundColor: \"#000\"\r\n\t\t\t\t\t}\r\n\t\t\t\t});\r\n\t\t\t}\r\n\t\t\t\r\n\t\t\t$(\".addnamespace\").click(function() {\r\n\t\t\t\tshowModal(\"addnamespace.html\", \"630\", \"600\");\r\n\t\t\t});\r\n\r\n\t\t\tfunction megaHoverOver(){\r\n\t\t\t\t$(this).find(\".sub\").stop().fadeTo('fast', 1).show();\r\n\t\t\t\t//Calculate width of all ul's\r\n\t\t\t\t(function($) { \r\n\t\t\t\t\tjQuery.fn.calcSubWidth = function() {\r\n\t\t\t\t\t\trowWidth = 0;\r\n\t\t\t\t\t\t//Calculate row\r\n\t\t\t\t\t\t$(this).find(\"ul\").each(function() {\t\t\t\t\t\r\n\t\t\t\t\t\t\trowWidth += $(this).width(); \r\n\t\t\t\t\t\t});\t\r\n\t\t\t\t\t};\r\n\t\t\t\t})(jQuery); \r\n\t\t\t\t\r\n\t\t\t\tif ( $(this).find(\".row\").length > 0 ) { //If row exists...\r\n\t\t\t\t\tvar biggestRow = 0;\t\r\n\t\t\t\t\t//Calculate each row\r\n\t\t\t\t\t$(this).find(\".row\").each(function() {\t\t\t\t\t\t\t   \r\n\t\t\t\t\t\t$(this).calcSubWidth();\r\n\t\t\t\t\t\t//Find biggest row\r\n\t\t\t\t\t\tif(rowWidth > biggestRow) {\r\n\t\t\t\t\t\t\tbiggestRow = rowWidth;\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t});\r\n\t\t\t\t\t//Set width\r\n\t\t\t\t\t$(this).find(\".sub\").css({'width' :biggestRow});\r\n\t\t\t\t\t$(this).find(\".row:last\").css({'margin':'0'});\r\n\t\t\t\t} else { //If row does not exist...\r\n\t\t\t\t\t$(this).calcSubWidth();\r\n\t\t\t\t\t//Set Width\r\n\t\t\t\t\t$(this).find(\".sub\").css({'width' : rowWidth});\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\tfunction megaHoverOut(){ \r\n\t\t\t  $(this).find(\".sub\").stop().fadeTo('fast', 0, function() {\r\n\t\t\t\t  $(this).hide(); \r\n\t\t\t  });\r\n\t\t\t}\r\n\t\t\tvar config = {    \r\n\t\t\t\t sensitivity: 1, // number = sensitivity threshold (must be 1 or higher)    \r\n\t\t\t\t interval: 100, // number = milliseconds for onMouseOver polling interval    \r\n\t\t\t\t over: megaHoverOver, // function = onMouseOver callback (REQUIRED)    \r\n\t\t\t\t timeout: 200, // number = milliseconds delay before onMouseOut    \r\n\t\t\t\t out: megaHoverOut // function = onMouseOut callback (REQUIRED)    \r\n\t\t\t};\r\n\t\t});"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/js/trcolor.js",
    "content": "// JavaScript Document\r\n// JavaScript Document\r\nwindow.onload = function() {\r\n $('.changecolor_g tbody tr:even').addClass('odd');\r\n $('.changecolor_g tbody tr').hover(\r\n  function() {$(this).addClass('highlight');},\r\n  function() {$(this).removeClass('highlight');}\r\n );\r\n \r\n $('.changecolor_w tbody tr:even').addClass('wh');\r\n $('.changecolor_w tbody tr').hover(\r\n  function() {$(this).addClass('highlight');},\r\n  function() {$(this).removeClass('highlight');}\r\n );\r\n}\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/templates/home/control/navigation.vm",
    "content": "#set($user = $rundata.request.session.getAttribute(\"managerUser\"))\n<!--页面头部-->\n<div class=\"head\">\n  <div class=\"header\"></div>\n  <div class=\"head_right\">\n    <div class=\"exit\">\n\t\t#set ($logoutURL = $homeModule.setAction(\"userAction\").addQueryData(\"eventSubmitDoLogout\", \"true\"))\n\t\t<a href=\"$logoutURL\" title=\"重新登录\"><img src=\"images/exit.png\" width=\"14\" height=\"19\" /></a></div>\n    <span>$!user.name #if($user.authorizeType.isAdmin()) （超级管理员） #elseif($user.authorizeType.isOperator()) （普通用户） #else 匿名用户 #end，欢迎您进入！</span>\n  </div>\n</div>\n\n<!--横向菜单-->\n<div class=\"nav\">\n   <ul>\n    <li id=\"sync\"><a href=\"$channelListLink\">同步管理</a></li>\n\t<li id=\"node\" class=\"sub_nav\"><a href=\"#\">机器管理</a>\n\t\t<ul>\n    \t\t<li><a href=\"$autoKeeperClustersListLink\">zookeeper管理</a></li>\n    \t\t<li><a href=\"$nodeListLink\">Node管理</a></li>\n\t\t</ul>\n\t</li>\n    <li id=\"datamedia\" class=\"sub_nav\"><a href=\"#\">配置管理</a>\n      <ul>\n        <li><a href=\"$dataMediaSourceListLink\">数据源配置</a></li>\n        <li><a href=\"$dataMediaListLink\">数据表配置</a></li>\n\t\t<li><a href=\"$canalListLink\">canal配置</a></li>\n\t\t<li><a href=\"$dataMatrixListLink\">主备配置</a></li>\n      </ul>   \n    </li>\n\t<li id=\"record\" class=\"sub_nav\"><a href=\"#\">监控管理</a>\n\t\t<ul>\n\t\t\t<li><a href=\"$logRecordLink\">日志记录</a></li>\n\t\t\t<li><a href=\"$alarmLogLink\">监控列表</a></li>\n\t\t\t<li><a href=\"${analysisTopStatLink}?topN=15&statTime=1&searchKey=\">同步TOP</a></li>\n        </ul>\n\t</li>\n\t<li id=\"manual\" class=\"sub_nav\"><a href=\"#\">使用文档</a>\n\t      <ul>\n\t\t\t<li><a href=\"$wikiLink\">wiki文档</a></li>\n\t        <li><a href=\"$sqlInitLink\">数据库初始化</a></li>\n\t      </ul>\n\t    </li>\n\t#if($user.authorizeType.isAdmin())\n\t\t<li id=\"user\" class=\"sub_nav\"><a href=\"#\">系统管理</a>\n\t      <ul>\n\t        <li><a href=\"$userListLink\">权限管理</a></li>\n\t        <li><a href=\"$systemReductionLink\">系统初始化</a></li>\n\t\t\t<li><a href=\"$systemParameterLink\">系统参数</a></li>\n\t      </ul>   \n\t    </li>\n\t#end\n   </ul>\n  <div class=\"about\"><a href=\"http://github.com/alibaba/otter\" target=\"_blank\">About</a></div>\n</div>\n\n<script type=\"text/javascript\" src=\"js/jquery-1.4.2.min.js\"></script> \n<script language=\"javascript\">\n<!--\n        function changeNav(navName) {\n        \tvar element = document.getElementById(navName);\n        \telement.setAttribute(\"class\", \"current_nosub\"); //for firefox\n        \telement.setAttribute(\"className\", \"current_nosub\"); //for IE\n\t\t}\n\t\tfunction getElement(key){\n      \t\treturn document.getElementById(key);\n   \t\t}\n   \n        function pageNavigation(event,pageIndex){\n            stopDefault(event);\n            pageNavCallBack(pageIndex);\n        }\n    \t\n    \tfunction pageNavCallBack(page){\n        \tgetElement('pageIndex').value=page;\n        \tgetElement('pageform').submit();\n        \treturn false;\n        }\n\t\t\n\t\tfunction stopDefault(e) { //函数功能：阻止浏览器的默认行为\n\t\t\te = e || window.event;  //兼容firefox\n    \t\tif ( e && e.preventDefault ) {\n         \t\te.preventDefault(); //阻止默认浏览器动作(W3C)\n     \t\t} else {\n         \t\te.returnValue = false; //IE中阻止函数器默认动作的方式\n     \t\t}\n    \t\treturn false;\n\t\t} \n\t\t\n\t\tfunction changeDisplay(id , action) {\n    \t\tvar trs = $(\"tr[class='\" + id +\"']\");   \n            for(i = 0; i < trs.length; i++){     \n                    trs[i].style.display = action;\n            }  \n    \t}\n//-->\n</script>"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/templates/home/layout/default.vm",
    "content": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n<html xmlns=\"http://www.w3.org/1999/xhtml\">\n<head>\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\n<title>Otter Manager</title>\n<link type=\"text/css\" href=\"css/commons.css\" rel=\"stylesheet\"/>\n<link type=\"text/css\" href=\"css/skin.css\" rel=\"stylesheet\"/>\n<link type=\"text/css\" href=\"css/otter.css\" rel=\"stylesheet\"/>\n</head>\n<body>\n\n$screen_placeholder\n\n<!--页脚-->\n<div class=\"footer\">\n <span>Copyright&nbsp;@&nbsp;2012-2018&nbsp;&nbsp;alibaba.com&nbsp;&nbsp;版权所有</span>\n <div align=\"center\">\n     <font color=\"#ffffff\">$!{systemUtil.HostInfo.Name} - $!{numberFormat.getManagerVersionInfo()}</font>\n </div>\n</div>\n</body>\n</html>"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/templates/home/screen/addAlarmRule.vm",
    "content": "$control.setTemplate(\"home:navigation.vm\")\r\n<script language=\"javascript\">\r\n<!--\r\n\tchangeNav(\"sync\");\r\n//-->\r\n</script>\r\n#macro (addAlarmRuleMessage $field)\r\n    #if (!$field.valid) $field.message #end\r\n#end\t\r\n\r\n<div class=\"main\">\r\n  <div class=\"title\"> \r\n    <h2>添加监控</h2>\r\n  </div>\r\n <div class=\"crumbs\"><a href=\"channelList.htm?channelId=$channelId\">channel管理</a>&nbsp;&nbsp;>&nbsp;&nbsp;<a href=\"pipelineList.htm?channelId=$channelId\">Pipeline管理</a>&nbsp;&nbsp;>&nbsp;&nbsp;<a href=\"alarmRuleList.htm?pipelineId=$pipelineId\">监控管理</a>&nbsp;&nbsp;>&nbsp;&nbsp;添加监控</div>\r\n \r\n \r\n<form name=\"addAlarmRuleForm\" method=\"post\" enctype=\"multipart/form-data\">\r\n\t$csrfToken.hiddenField\r\n\t<input type=\"hidden\" name=\"action\" value=\"alarm_rule_action\"/>\r\n\t<input type=\"hidden\" name=\"event_submit_do_add\" value=\"1\" />\r\n <div class=\"setting_box\">\r\n \r\n   #set ($alarmRuleGroup = $form.alarmRuleInfo.defaultInstance)\r\n   <input type=\"hidden\" name=\"$alarmRuleGroup.pipelineId.key\" value=\"$pipelineId\"/>\r\n   <input type=\"hidden\" name=\"$alarmRuleGroup.status.key\" value=\"DISABLE\"/>\r\n   \r\n    <table cellpadding=\"0\" cellspacing=\"0\" class=\"setting_otter\">\r\n\t\t<span class=\"red\">#addAlarmRuleMessage ($alarmRuleGroup.formAlarmRuleError)</span>\r\n      <tr> \r\n        <th width=\"300\">监控项目：</th>\r\n        <td width=\"329\">\r\n\t\t\t<select name=\"$alarmRuleGroup.monitorName.key\" id=\"select\">\r\n                <option value=\"DELAYTIME\" selected=\"selected\">延迟</option>\r\n\t\t\t\t<option value=\"PIPELINETIMEOUT\">Pipeline超时</option>\r\n\t\t\t\t<option value=\"PROCESSTIMEOUT\">Process超时</option>\r\n            \t<option value=\"POSITIONTIMEOUT\" >Position超时</option>\r\n\t\t\t\t<option value=\"EXCEPTION\">异常</option>\r\n            </select><span class=\"red\">*</span>\r\n\t\t</td>\r\n      </tr>\r\n      <tr> \r\n        <th>阈值：</th>\r\n        <td>\r\n\t\t\t<input type=\"text\" name=\"$alarmRuleGroup.matchValue.key\" value=\"$!alarmRuleGroup.matchValue.value\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#addAlarmRuleMessage ($alarmRuleGroup.matchValue)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  <tr> \r\n        <th>报警间隔时间(秒)：</th>\r\n        <td>\r\n\t\t\t<input type=\"text\" name=\"$alarmRuleGroup.intervalTime.key\" value=\"$!alarmRuleGroup.intervalTime.value\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#addAlarmRuleMessage ($alarmRuleGroup.intervalTime)</span>\r\n\t\t</td>\r\n      </tr>\r\n      <tr> \r\n        <th>发送人KEY：</th>\r\n        <td>\r\n\t\t\t<input type=\"text\" name=\"$alarmRuleGroup.receiverKey.key\" value=\"$!alarmRuleGroup.receiverKey.value\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#addAlarmRuleMessage ($alarmRuleGroup.receiverKey)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  <tr> \r\n        <th>开始自动恢复：</th>\r\n        <td>\r\n\t\t\t<input type=\"radio\" name=\"$alarmRuleGroup.autoRecovery.key\" value=\"true\" id=\"RadioGroup2_0\"  class=\"radio\"/>是\r\n            <input type=\"radio\" name=\"$alarmRuleGroup.autoRecovery.key\" value=\"false\" id=\"RadioGroup2_1\" checked class=\"radio\"/>否 \r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#addAlarmRuleMessage ($alarmRuleGroup.autoRecovery)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  <tr> \r\n        <th>触发自动恢复阀值：</th>\r\n        <td>\r\n\t\t\t<input type=\"text\" name=\"$alarmRuleGroup.recoveryThresold.key\" value=\"2\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#addAlarmRuleMessage ($alarmRuleGroup.recoveryThresold)</span>\r\n\t\t</td>\r\n      </tr>\r\n      <tr>\r\n       <th>描述：</th>\r\n       <td>\r\n    \t\t<textarea cols=\"45\" rows=\"5\" name=\"$alarmRuleGroup.description.key\">$!alarmRuleGroup.description.value</textarea>\r\n\t   </td>\r\n      </tr>\r\n    </table>\r\n </div>\r\n   <div class=\"btn\"><a href=\"javascript:document.addAlarmRuleForm.submit();\">保存</a></div> \r\n  \r\n  </form>\r\n</div>"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/templates/home/screen/addBatchDataMediaPair.vm",
    "content": "\r\n$control.setTemplate(\"home:navigation.vm\")\r\n<script language=\"javascript\">\r\n<!--\r\n\tchangeNav(\"sync\");\r\n//-->\r\nfunction changeKeyword(id, name, local) {\r\n\tif(local == 'source'){\r\n    \tif( document.getElementById(\"sourceDataMediaId\") && document.getElementById(\"sourceDataMediaName\")){\r\n    \t\tdocument.getElementById('sourceDataMediaId').value = id;\r\n        \tdocument.getElementById('sourceDataMediaName').value = name;\r\n    \t}\r\n\t}else{\r\n\t\tif( document.getElementById(\"targetDataMediaId\") && document.getElementById(\"targetDataMediaName\")){\r\n    \t\tdocument.getElementById('targetDataMediaId').value = id;\r\n        \tdocument.getElementById('targetDataMediaName').value = name;\r\n    \t}\r\n\t}\r\n\t\r\n}\r\n</script>\r\n#macro (addBatchDataMediaPairMessage $field)\r\n    #if (!$field.valid) $field.message #end\r\n#end\t\r\n\r\n<div class=\"main\">\r\n  <div class=\"title\"> \r\n    <h2>添加映射关系</h2>\r\n  </div> <div class=\"crumbs\"><a href=\"channelList.htm?channelId=$channelId\">Channel管理</a>&nbsp;&nbsp;>&nbsp;&nbsp;<a href=\"pipelineList.htm?channelId=$channelId\">Pipeline管理</a>&nbsp;&nbsp;>&nbsp;&nbsp;<a href=\"dataMediaPairList.htm?pipelineId=$pipelineId\">映射关系管理</a>&nbsp;&nbsp;>&nbsp;&nbsp;<a href=\"addDataMediaPair.htm?pipelineId=$pipelineId\">批量添加映射关系</a> </div> \r\n\r\n <form name=\"addPairForm\" method=\"post\" enctype=\"multipart/form-data\">\r\n\t$csrfToken.hiddenField\r\n\t<input type=\"hidden\" name=\"action\" value=\"data_media_pair_action\"/>\r\n\t<input type=\"hidden\" name=\"event_submit_do_batch_add\" value=\"1\" />\r\n\t<div class=\"setting_box\">\r\n\t\t#set ($batchDataMediaPairGroup = $form.batchDataMediaPairInfo.defaultInstance)\r\n\t\t<input type=\"hidden\" name=\"$batchDataMediaPairGroup.pipelineId.key\" value=\"$pipelineId\"/>\r\n\t\t<input type=\"hidden\" name=\"pipelineId\" value=\"$pipelineId\"/>\r\n\t\t<input type=\"hidden\" name=\"channelId\" value=\"$channelId\"/>\r\n\t\t<table cellpadding=\"0\" cellspacing=\"0\" class=\"setting setting_otter\">\r\n\t\t\t<span class=\"red\">#addBatchDataMediaPairMessage ($batchDataMediaPairGroup.formBatchDataMediaPairError)</span>\r\n\t\t\t<tr> \r\n\t\t\t\t<td>\r\n\t\t\t\t\t格式<span class=\"red\">(红色为必填)</span>：<br/>\r\n\t\t\t\t\t1. 最简配置 <span class=\"red\">schema,table,sourceId1,sourceId2,</span>loadWeight<br/>\r\n\t\t\t\t\t2. 表名不一致 <span class=\"red\">schema1,table1,sourceId1,schema2,table2,sourceId2,</span>loadWeight<br/>\r\n\t\t\t\t</td>\r\n\t\t\t</tr>\r\n\t\t\t<tr> \r\n\t\t\t\t<td>\r\n\t\t\t\t\t<textarea cols=100 rows=10 name=\"$batchDataMediaPairGroup.batchPairContent.key\" ></textarea>\r\n\t\t\t\t</td>\r\n\t\t\t</tr>\r\n\t\t</table>\r\n\t</div>\r\n\t<input type=\"submit\" name=\"submitKey\" value=\"保存\" class=\"button\"></input>\r\n </form>\r\n</div>\r\n\r\n"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/templates/home/screen/addCanal.vm",
    "content": "$control.setTemplate(\"home:navigation.vm\")\r\n<script language=\"javascript\">\r\n<!--\r\n\tchangeNav(\"canal\");\r\n\t\r\n\tfunction changePositionConfig() {\r\n\t\tvar obj = document.getElementById('positionSuperConfig');\r\n\t\tif(obj.checked) {\r\n\t\t\tchangeDisplay('positionSuperConfig','table-row');\r\n\t\t\tchangeDisplay('positionGtidConfig','table-row');\r\n\t\t}else {\r\n\t\t\tchangeDisplay('positionSuperConfig','none');\r\n\t\t\tchangeDisplay('positionGtidConfig','none');\r\n\t\t}\r\n\t}\r\n\t\r\n\tfunction changeNetworkConfig() {\r\n\t\tvar obj = document.getElementById('networkSuperConfig');\r\n\t\tif(obj.checked) {\r\n\t\t\tchangeDisplay('networkSuperConfig','table-row');\r\n\t\t}else {\r\n\t\t\tchangeDisplay('networkSuperConfig','none');\r\n\t\t}\r\n\t}\r\n//-->\r\n</script>\r\n#macro (addCanalMessage $field)\r\n    #if (!$field.valid) $!field.message #end\r\n#end\t\r\n\r\n<div class=\"main\">\r\n  <div class=\"title\"> \r\n    <h2>添加canal</h2>\r\n  </div>\r\n <div class=\"crumbs\"><a href=\"canalList.htm\">canal管理</a>&nbsp;&nbsp;>&nbsp;&nbsp;<a href=\"addCanal.htm\">添加canal</a></div>\r\n \r\n \r\n<form name=\"addCanalForm\" method=\"post\" enctype=\"multipart/form-data\">\r\n\t$csrfToken.hiddenField\r\n\t<input type=\"hidden\" name=\"action\" value=\"canal_action\"/>\r\n\t<input type=\"hidden\" name=\"event_submit_do_add\" value=\"1\" />\r\n <div class=\"setting_box\">\r\n \r\n   #set ($canalGroup = $form.canalInfo.defaultInstance)\r\n   #set ($canalParameterGroup = $form.canalParameterInfo.defaultInstance)\r\n   \r\n    <table cellpadding=\"0\" cellspacing=\"0\" class=\"setting_otter\">\r\n    <tr>\r\n    \t<th width=\"300\"></th>\r\n    \t<td width=\"329\">\r\n    \t\t#foreach($f in ${canalParameterGroup.getFields()})\r\n    \t\t\t#if (!$f.valid) \r\n    \t\t\t\t<span class=\"red\">$!f.message</span><br/>\r\n    \t\t\t#end\r\n    \t\t#end\r\n    \t</td>\r\n    </tr>\r\n\t\t<tr> \r\n        <th width=\"300\">canal名称：</th>\r\n        <td width=\"329\">\r\n            <input type=\"text\" name=\"$canalGroup.name.key\" value=\"$!canal.name.value\" class=\"setting_input\"/><span class=\"red\">* (注意:保存后不可修改)</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#addCanalMessage ($canalGroup.name) #addCanalMessage ($canalGroup.formCanalError)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  \r\n\t  <tr> \r\n        <th width=\"300\">运行模式：</th>\r\n        <td width=\"329\">\r\n              <input type=\"radio\" name=\"$canalParameterGroup.runMode.key\" value=\"EMBEDDED\" id=\"RadioGroup1_0\" checked=\"checked\" class=\"radio\"/>嵌入式 &nbsp;\r\n              <input type=\"radio\" name=\"$canalParameterGroup.runMode.key\" value=\"SERVICE\" id=\"RadioGroup1_1\" disabled class=\"radio\"/>独立服务 &nbsp;\r\n\t\t\t  <br />\r\n\t\t\t  <span class=\"red\">#addCanalMessage ($canalParameterGroup.runMode)</span>\r\n        </td>\r\n      </tr>\r\n\t  <tr> \r\n        <th>zookeeper集群：</th>\r\n        <td>\r\n            <select id=\"zkCluster\" name=\"$canalParameterGroup.autoKeeperClusterId.key\">\r\n\t\t\t#foreach($zkCluster in $zkClusters)\r\n            <option value=\"$zkCluster.id\" #if($velocityCount == 0)selected#end>$zkCluster.clusterName</option>\r\n\t\t\t#end\r\n\t\t\t</select><span class=\"red\">*</span>\r\n        </td>\r\n      </tr>\r\n\t  #*\r\n\t  <tr> \r\n        <th>zooKeeper集群：</th>\r\n        <td>\r\n\t\t\t<textarea class=\"service\" name=\"$canalParameterGroup.zkClusters.key\" cols=\"45\" rows=\"5\" >$!canalParameterGroup.zkClusters.value</textarea><span class=\"red\">*</span>\r\n\t\t\t  <br />\r\n\t\t\t  <span>格式如 10.20.10.20:8080;(必须以分号结束，可添多个)</span>\r\n\t\t\t  <br />\r\n\t\t\t<span class=\"red\">#addCanalMessage ($canalParameterGroup.zkClusters)</span>\r\n\t\t</td>\r\n\t  </tr>\r\n\t  *#\r\n\t  <tr> \r\n        <th width=\"300\">-------------------------------</th>\r\n\t\t<td width=\"329\">---------------------------------------------------------------------------------------------</td>\r\n      </tr>\r\n\t  \r\n\t  <div id=\"dbConfig\">\r\n\t  <tr> \r\n        <th width=\"300\">数据源类型：</th>\r\n        <td width=\"329\">\r\n\t\t\t<input type=\"radio\" name=\"$canalParameterGroup.sourcingType.key\" value=\"MYSQL\" onclick=\"changeDisplay('mysqlSourcing','table-row');changeDisplay('oracleSourcing','none');changeDisplay('localSourcing','none');changeDisplay('obSourcing','none');\" id=\"RadioGroup5_0\" checked=\"checked\" class=\"radio\"/>mysql &nbsp;\r\n\t\t\t#**\r\n\t\t\t<input type=\"radio\" name=\"$canalParameterGroup.sourcingType.key\" value=\"ORACLE\" onclick=\"changeDisplay('mysqlSourcing','none');changeDisplay('oracleSourcing','table-row');changeDisplay('localSourcing','none');\" id=\"RadioGroup5_1\" class=\"radio\"/>oracle &nbsp;\r\n            <input type=\"radio\" name=\"$canalParameterGroup.sourcingType.key\" value=\"LOCALBINLOG\" onclick=\"changeDisplay('mysqlSourcing','none');changeDisplay('oracleSourcing','none');changeDisplay('localSourcing','table-row');\" id=\"RadioGroup5_2\" class=\"radio\"/>localbinlog &nbsp;\r\n\t\t\t*#\r\n\t\t\t<input type=\"radio\" name=\"$canalParameterGroup.sourcingType.key\" value=\"OCEANBASE\" onclick=\"changeDisplay('mysqlSourcing','none');changeDisplay('oracleSourcing','none');changeDisplay('localSourcing','none');changeDisplay('obSourcing','table-row');\" id=\"RadioGroup5_3\" class=\"radio\"/>oceanbase &nbsp;\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#addCanalMessage ($canalParameterGroup.sourcingType)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  <tr> \r\n        <th width=\"300\">数据库地址：</th>\r\n        <td width=\"329\">\r\n\t\t\t<textarea class=\"service\" name=\"$canalParameterGroup.groupDbAddresses.key\" cols=\"45\" rows=\"5\" >$!canal.canalParameter.groupDbAddresses.value</textarea><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span>格式如 127.0.0.1:3306;(必须以分号结束，可添多个)</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#addCanalMessage ($canalParameterGroup.groupDbAddresses)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  <tr> \r\n        <th width=\"300\">数据库帐号：</th>\r\n        <td width=\"329\">\r\n            <input type=\"text\" name=\"$canalParameterGroup.dbUsername.key\" value=\"$!canal.canalParameter.dbUsername.value\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#addCanalMessage ($canalParameterGroup.dbUsername)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  <tr> \r\n        <th width=\"300\">数据库密码：</th>\r\n        <td width=\"329\">\r\n            <input type=\"password\" name=\"$canalParameterGroup.dbPassword.key\" value=\"\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#addCanalMessage ($canalParameterGroup.dbPassword)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  <tr> \r\n        <th width=\"300\">connectionCharset：</th>\r\n        <td width=\"329\">\r\n            <input type=\"text\" name=\"$canalParameterGroup.connectionCharset.key\" value=\"UTF-8\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#addCanalMessage ($canalParameterGroup.connectionCharset)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  #**\r\n\t  <tr class=\"mysqlSourcing\"> \r\n        <th width=\"300\">链接到mysql的slaveId：</th>\r\n        <td width=\"329\">\r\n            <input type=\"text\" name=\"$canalParameterGroup.slaveId.key\" value=\"$!canal.canalParameter.slaveId.value\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#addCanalMessage ($canalParameterGroup.slaveId)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  *#\r\n\t  <tr class=\"localSourcing\">\r\n        <th width=\"300\">本地localBinlog目录：</th>\r\n        <td width=\"329\">\r\n            <input type=\"text\" name=\"$canalParameterGroup.localBinlogDirectory.key\" value=\"$!canal.canalParameter.localBinlogDirectory.value\" class=\"setting_input\"/>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#addCanalMessage ($canalParameterGroup.localBinlogDirectory)</span>\r\n\t\t</td>\r\n      </tr>\r\n      <tr class=\"obSourcing\">\r\n\t\t<th width=\"300\">rsList：</th>\r\n\t\t<td width=\"329\">\r\n\t\t\t<input type=\"text\" name=\"$canalParameterGroup.rsList.key\" value=\"$!canal.canalParameter.rsList.value\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#addCanalMessage ($canalParameterGroup.rsList)</span>\r\n\t\t</td>\r\n      </tr>\r\n      <tr class=\"obSourcing\">\r\n\t\t<th width=\"300\">tenant：</th>\r\n\t\t<td width=\"329\">\r\n\t\t\t<input type=\"text\" name=\"$canalParameterGroup.tenant.key\" value=\"$!canal.canalParameter.tenant.value\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#addCanalMessage ($canalParameterGroup.tenant)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  </div>\r\n\t  \r\n\t  <div id=\"positionConfig\">\r\n\t  <tr>\r\n\t\t<th width=\"300\">位点自定义设置：</th>\r\n        <td width=\"329\">\r\n              <input id=\"positionSuperConfig\" type='checkbox' value=1  class=\"setting_input\" onclick=\"changePositionConfig()\" />\r\n\t\t\t  <br />\r\n        </td>\r\n      </tr>\r\n      \r\n\t  <tr class=\"positionGtidConfig\">\r\n\t\t<th width=\"300\">是否启用gtid位点：</th>\r\n        <td width=\"329\">\r\n            <input type=\"radio\" name=\"$canalParameterGroup.gtidEnable.key\" value=\"true\" id=\"RadioGroup8_0_0\" #if($!canal.canalParameter.gtidEnable) checked=\"checked\" #end class=\"radio\"/>是 &nbsp;\r\n            <input type=\"radio\" name=\"$canalParameterGroup.gtidEnable.key\" value=\"false\" id=\"RadioGroup8_1_0\" #if(!$!canal.canalParameter.gtidEnable) checked=\"checked\" #end class=\"radio\"/>否 &nbsp;\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#addCanalMessage ($canalParameterGroup.gtidEnable)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  <tr class=\"positionSuperConfig\"> \r\n        <th width=\"300\">位点信息：</th>\r\n        <td width=\"329\">\r\n\t\t\t<textarea class=\"service\" name=\"$canalParameterGroup.positions.key\" cols=\"45\" rows=\"5\" >$!canalParameterGroup.positions.value</textarea><span class=\"red\">*</span>\r\n\t\t\t示例：{\"journalName\":\"\",\"position\":0,\"timestamp\":0}; (必须以分号结束，可添多个)</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#addCanalMessage ($canalParameterGroup.positions)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  </div>\r\n\t  \r\n\t  <div id=\"tsdbConfig\">\r\n\t  <tr>\r\n\t\t<th width=\"300\">是否开启表结构TSDB：</th>\r\n        <td width=\"329\">\r\n            <input type=\"radio\" name=\"$canalParameterGroup.tsdbEnable.key\" value=\"true\" id=\"RadioGroup8_0_0\" #if($!canal.canalParameter.tsdbEnable) checked=\"checked\" #end class=\"radio\"/>是 &nbsp;\r\n            <input type=\"radio\" name=\"$canalParameterGroup.tsdbEnable.key\" value=\"false\" id=\"RadioGroup8_1_0\" #if(!$!canal.canalParameter.tsdbEnable) checked=\"checked\" #end class=\"radio\"/>否 &nbsp;\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#addCanalMessage ($canalParameterGroup.tsdbEnable)</span>\r\n\t\t</td>\r\n      </tr>\r\n      </div>\r\n      \r\n      <div id=\"rdsConfig\">\r\n\t  <tr class=\"rdsConfig\">\r\n        <th width=\"300\">rds accesskey：</th>\r\n        <td width=\"329\">\r\n            <input type=\"text\" name=\"$canalParameterGroup.rdsAccesskey.key\" value=\"\" class=\"setting_input\"/>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#addCanalMessage ($canalParameterGroup.rdsAccesskey)</span>\r\n\t\t</td>\r\n      </tr>\r\n      <tr class=\"rdsConfig\">\r\n        <th width=\"300\">rds secretkey：</th>\r\n        <td width=\"329\">\r\n            <input type=\"text\" name=\"$canalParameterGroup.rdsSecretkey.key\" value=\"\" class=\"setting_input\"/>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#addCanalMessage ($canalParameterGroup.rdsSecretkey)</span>\r\n\t\t</td>\r\n      </tr>\r\n      <tr class=\"rdsConfig\">\r\n        <th width=\"300\">rds instanceId：</th>\r\n        <td width=\"329\">\r\n            <input type=\"text\" name=\"$canalParameterGroup.rdsInstanceId.key\" value=\"\" class=\"setting_input\"/>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#addCanalMessage ($canalParameterGroup.rdsInstance)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  </div>\r\n\t  \r\n\t  <tr> \r\n        <th width=\"300\">-------------------------------</th>\r\n\t\t<td width=\"329\">---------------------------------------------------------------------------------------------</td>\r\n      </tr>\r\n\t  \r\n\t  <div id=\"storageConfig\">\r\n\t  <tr> \r\n        <th width=\"300\">存储机制：</th>\r\n        <td width=\"329\">\r\n\t\t\t<input type=\"radio\" name=\"$canalParameterGroup.storageMode.key\" onclick=\"changeDisplay('memoryStorage','table-row');changeDisplay('fileStorage','none');\" value=\"MEMORY\" id=\"RadioGroup4_0\" checked=\"checked\" class=\"radio\"/>memory &nbsp;\r\n            <input type=\"radio\" name=\"$canalParameterGroup.storageMode.key\" onclick=\"changeDisplay('memoryStorage','none');changeDisplay('fileStorage','table-row');\" value=\"FILE\" id=\"RadioGroup4_2\" disabled class=\"radio\"/>file &nbsp;\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#addCanalMessage ($canalParameterGroup.storageMode)</span>\r\n\t\t</td>\r\n      </tr>\r\n      <tr class=\"memoryStorage\">\r\n        <th width=\"300\">内存存储batch获取模式：</th>\r\n        <td width=\"329\">\r\n            <input type=\"radio\" name=\"$canalParameterGroup.storageBatchMode.key\" value=\"MEMSIZE\" id=\"RadioGroup6_0\" checked=\"checked\" class=\"radio\"/>MEMSIZE &nbsp;\r\n\t\t\t<input type=\"radio\" name=\"$canalParameterGroup.storageBatchMode.key\" value=\"ITEMSIZE\" id=\"RadioGroup6_1\" class=\"radio\"/>ITEMSIZE &nbsp;\r\n\t\t\t<br />\r\n\t\t\t<span><code>MEMSIZE模式 内存大小计算 = 记录数 * 记录单元大小</code></span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#addCanalMessage ($canalParameterGroup.storageBatchMode)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  <tr class=\"memoryStorage\">\r\n        <th width=\"300\">内存存储buffer记录数：</th>\r\n        <td width=\"329\">\r\n            <input type=\"text\" name=\"$canalParameterGroup.memoryStorageBufferSize.key\" value=\"32768\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#addCanalMessage ($canalParameterGroup.memoryStorageBufferSize)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  <tr class=\"memoryStorage\">\r\n        <th width=\"300\">内存存储buffer记录单元大小：</th>\r\n        <td width=\"329\">\r\n            <input type=\"text\" name=\"$canalParameterGroup.memoryStorageBufferMemUnit.key\" value=\"1024\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#addCanalMessage ($canalParameterGroup.memoryStorageBufferMemUnit)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  <tr class=\"fileStorage\"> \r\n        <th width=\"300\">文件存储的目录位置：</th>\r\n        <td width=\"329\">\r\n            <input type=\"text\" name=\"$canalParameterGroup.fileStorageDirectory.key\" value=\"$!canal.canalParameter.fileStorageDirectory.value\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#addCanalMessage ($canalParameterGroup.fileStorageDirectory)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  <tr class=\"fileStorage\"> \r\n        <th width=\"300\">文件存储store记录数：</th>\r\n        <td width=\"329\">\r\n            <input type=\"text\" name=\"$canalParameterGroup.fileStorageStoreCount.key\" value=\"$!canal.canalParameter.fileStorageStoreCount.value\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#addCanalMessage ($canalParameterGroup.fileStorageStoreCount)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  <tr class=\"fileStorage\"> \r\n        <th width=\"300\">文件存储store文件个数：</th>\r\n        <td width=\"329\">\r\n            <input type=\"text\" name=\"$canalParameterGroup.fileStorageRollverCount.key\" value=\"$!canal.canalParameter.fileStorageRollverCount.value\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#addCanalMessage ($canalParameterGroup.fileStorageRollverCount)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  <tr class=\"fileStorage\"> \r\n        <th width=\"300\">文件存储store存储占disk百分比：</th>\r\n        <td width=\"329\">\r\n            <input type=\"text\" name=\"$canalParameterGroup.fileStoragePercentThresold.key\" value=\"$!canal.canalParameter.fileStoragePercentThresold.value\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#addCanalMessage ($canalParameterGroup.fileStoragePercentThresold)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  </div>\r\n\t  \r\n\t  <tr> \r\n        <th width=\"300\">-------------------------------</th>\r\n\t\t<td width=\"329\">---------------------------------------------------------------------------------------------</td>\r\n      </tr>\r\n\t  \r\n\t  <div id=\"haConfig\">\r\n\t  <tr> \r\n        <th width=\"300\">HA机制：</th>\r\n        <td width=\"329\">\r\n\t\t\t<input type=\"radio\" name=\"$canalParameterGroup.haMode.key\" value=\"HEARTBEAT\" onclick=\"changeDisplay('heartbeatHa','table-row');changeDisplay('mediaHa','none');\" id=\"RadioGroup6_0\" checked=\"checked\" class=\"radio\"/>heartbeat &nbsp;\r\n            <input type=\"radio\" name=\"$canalParameterGroup.haMode.key\" value=\"MEDIA\" onclick=\"changeDisplay('heartbeatHa','none');changeDisplay('mediaHa','table-row');\" id=\"RadioGroup6_2\" class=\"radio\"/>media &nbsp;\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#addCanalMessage ($canalParameterGroup.haMode)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  </div>\r\n\t  \r\n\t  <tr class=\"mediaHa\">\r\n        <th width=\"300\">media group key:</th>\r\n        <td width=\"329\">\r\n\t\t\t<input type=\"text\" name=\"$canalParameterGroup.mediaGroup.key\" value=\"$!canal.canalParameter.mediaGroup.value\" class=\"setting_input\"/>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#addCanalMessage ($canalParameterGroup.mediaGroup)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  \r\n\t  <div id=\"detectConfig\">\r\n\t  <tr> \r\n        <th width=\"300\">是否开启心跳：</th>\r\n        <td width=\"329\">\r\n\t\t\t<input type=\"radio\" name=\"$canalParameterGroup.detectingEnable.key\" value=\"true\" onclick=\"changeDisplay('detectEnable','table-row');\" id=\"RadioGroup8_0\" class=\"radio\"/>是 &nbsp;\r\n            <input type=\"radio\" name=\"$canalParameterGroup.detectingEnable.key\" value=\"false\" onclick=\"changeDisplay('detectEnable','none');\" id=\"RadioGroup8_1\" checked=\"checked\" class=\"radio\"/>否 &nbsp;\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#addCanalMessage ($canalParameterGroup.detectingEnable)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  \r\n\t  <tr class=\"detectEnable\"> \r\n        <th width=\"300\">心跳sql：</th>\r\n        <td width=\"329\">\r\n\t\t\t<textarea class=\"service\" name=\"$canalParameterGroup.detectingSQL.key\" cols=\"45\" rows=\"5\" >insert into retl.xdual values(1,now()) on duplicate key update x=now()</textarea><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#addCanalMessage ($canalParameterGroup.detectingSQL) #addCanalMessage ($canalParameterGroup.formHeartBeatError)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  \r\n\t  <tr class=\"detectEnable\">\r\n        <th width=\"300\">心跳检测频率(s)：</th>\r\n        <td width=\"329\">\r\n            <input type=\"text\" name=\"$canalParameterGroup.detectingIntervalInSeconds.key\" value=\"5\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#addCanalMessage ($canalParameterGroup.detectingIntervalInSeconds)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  \r\n\t <tr class=\"detectEnable\">\r\n        <th width=\"300\">心跳超时时间(s)：</th>\r\n        <td width=\"329\">\r\n            <input type=\"text\" name=\"$canalParameterGroup.detectingTimeoutThresholdInSeconds.key\" value=\"30\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#addCanalMessage ($canalParameterGroup.detectingTimeoutThresholdInSeconds)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  \r\n\t <tr class=\"detectEnable\">\r\n        <th width=\"300\">心跳检查重试次数：</th>\r\n        <td width=\"329\">\r\n            <input type=\"text\" name=\"$canalParameterGroup.detectingRetryTimes.key\" value=\"3\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#addCanalMessage ($canalParameterGroup.detectingRetryTimes)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  \r\n\t  <tr class=\"detectEnable\">\r\n        <th width=\"300\">是否启用心跳HA：</th>\r\n        <td width=\"329\">\r\n\t\t\t<input type=\"radio\" name=\"$canalParameterGroup.heartbeatHaEnable.key\" value=\"true\" id=\"RadioGroup8_0\" class=\"radio\"/>是 &nbsp;\r\n            <input type=\"radio\" name=\"$canalParameterGroup.heartbeatHaEnable.key\" value=\"false\" id=\"RadioGroup8_1\" checked=\"checked\" class=\"radio\"/>否 &nbsp;\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#addCanalMessage ($canalParameterGroup.heartbeatHaEnable)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  </div>\r\n\t  \r\n\t  <div id=\"networkConfig\">\r\n\t  <tr>\r\n\t\t<th width=\"300\">其他参数设置：</th>\r\n        <td width=\"329\">\r\n              <input id=\"networkSuperConfig\" type='checkbox' name='super' value=1  class=\"setting_input\" onclick=\"changeNetworkConfig()\" />\r\n\t\t\t  <br />\r\n        </td>\r\n      </tr>\r\n\t  \r\n\t <tr class=\"networkSuperConfig\"> \r\n        <th width=\"300\">meta机制：</th>\r\n        <td width=\"329\">\r\n\t\t\t<input type=\"radio\" name=\"$canalParameterGroup.metaMode.key\" value=\"MEMORY\" id=\"RadioGroup3_0\" class=\"radio\"/>memory &nbsp;\r\n            <input type=\"radio\" name=\"$canalParameterGroup.metaMode.key\" value=\"ZOOKEEPER\" id=\"RadioGroup3_1\" class=\"radio\"/>zookeeper &nbsp;\r\n\t\t\t<input type=\"radio\" name=\"$canalParameterGroup.metaMode.key\" value=\"MIXED\" id=\"RadioGroup3_2\" checked=\"checked\" class=\"radio\"/>mixed &nbsp;\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#addCanalMessage ($canalParameterGroup.metaMode)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t <tr class=\"networkSuperConfig\"> \r\n        <th width=\"300\">索引机制：</th>\r\n        <td width=\"329\">\r\n\t\t\t<input type=\"radio\" name=\"$canalParameterGroup.indexMode.key\" value=\"MEMORY\" id=\"RadioGroup7_0\" class=\"radio\"/>memory &nbsp;\r\n            <input type=\"radio\" name=\"$canalParameterGroup.indexMode.key\" value=\"ZOOKEEPER\" id=\"RadioGroup7_1\" class=\"radio\"/>zookeeper &nbsp;\r\n\t\t\t<input type=\"radio\" name=\"$canalParameterGroup.indexMode.key\" value=\"MIXED\" id=\"RadioGroup7_2\" class=\"radio\"/>mixed &nbsp;\r\n\t\t\t<input type=\"radio\" name=\"$canalParameterGroup.indexMode.key\" value=\"META\" id=\"RadioGroup7_3\" class=\"radio\"/>meta &nbsp;\r\n\t\t\t<input type=\"radio\" name=\"$canalParameterGroup.indexMode.key\" value=\"MEMORY_META_FAILBACK\" id=\"RadioGroup7_4\" checked=\"checked\" class=\"radio\"/>memory_meta_failback &nbsp;\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#addCanalMessage ($canalParameterGroup.indexMode)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  <tr class=\"networkSuperConfig\"> \r\n        <th width=\"300\">服务端口：</th>\r\n        <td width=\"329\">\r\n            <input type=\"text\" name=\"$canalParameterGroup.port.key\" value=\"11111\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#addCanalMessage ($canalParameterGroup.port)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  \r\n\t <tr class=\"networkSuperConfig\"> \r\n        <th width=\"300\">默认连接超时(s)：</th>\r\n        <td width=\"329\">\r\n            <input type=\"text\" name=\"$canalParameterGroup.defaultConnectionTimeoutInSeconds.key\" value=\"30\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#addCanalMessage ($canalParameterGroup.defaultConnectionTimeoutInSeconds)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t <tr class=\"networkSuperConfig\"> \r\n        <th width=\"300\">sendBufferSize：</th>\r\n        <td width=\"329\">\r\n            <input type=\"text\" name=\"$canalParameterGroup.receiveBufferSize.key\" value=\"16384\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#addCanalMessage ($canalParameterGroup.receiveBufferSize)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  <tr class=\"networkSuperConfig\"> \r\n        <th width=\"300\">receiveBufferSize：</th>\r\n        <td width=\"329\">\r\n            <input type=\"text\" name=\"$canalParameterGroup.sendBufferSize.key\" value=\"16384\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#addCanalMessage($canalParameterGroup.sendBufferSize)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  <tr class=\"networkSuperConfig\"> \r\n        <th width=\"300\">切换回退时间：</th>\r\n        <td width=\"329\">\r\n            <input type=\"text\" name=\"$canalParameterGroup.fallbackIntervalInSeconds.key\" value=\"60\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#addCanalMessage($canalParameterGroup.fallbackIntervalInSeconds)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  </div>\r\n\t  <tr>\r\n       <th>过滤表达式：</th>\r\n       <td>\r\n    \t\t<textarea cols=\"45\" rows=\"5\" name=\"$canalParameterGroup.blackFilter.key\">$!canalParameterGroup.blackFilter.value</textarea><span class=\"red\">*</span>\r\n    \t\t<br />\r\n    \t\t<span class=\"red\">#addCanalMessage ($canalParameterGroup.blackFilter)</span>\r\n\t   </td>\r\n      </tr>\r\n\t  <tr>\r\n       <th>描述信息：</th>\r\n       <td>\r\n    \t\t<textarea cols=\"45\" rows=\"5\" name=\"$canalGroup.desc.key\">$!canalGroup.desc.value</textarea><span class=\"red\">*</span>\r\n    \t\t<br />\r\n    \t\t<span class=\"red\">#addCanalMessage ($canalGroup.desc)</span>\r\n\t   </td>\r\n      </tr>     \r\n    </table>\r\n </div>\r\n   <div class=\"btn\"><a href=\"javascript:document.addCanalForm.submit();\">保存</a></div> \r\n  </form>\r\n</div>\r\n\t\r\n<script language=\"javascript\">\r\n<!--\r\n\tchangeDisplay('mysqlSourcing','table-row')\r\n\tchangeDisplay('oracleSourcing','none')\r\n\tchangeDisplay('localSourcing','none')\r\n\tchangeDisplay('obSourcing','none')\r\n\t\r\n\tchangeDisplay('memoryStorage','table-row')\r\n\tchangeDisplay('fileStorage','none')\r\n\t\r\n\tchangeDisplay('heartbeatHa','table-row')\r\n\tchangeDisplay('mediaHa','none')\r\n\t\r\n\tchangeDisplay('detectEnable','none')\r\n\t\r\n\tchangePositionConfig();\r\n\tchangeNetworkConfig();\r\n//-->\r\n</script>"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/templates/home/screen/addChannel.vm",
    "content": "$control.setTemplate(\"home:navigation.vm\")\r\n<script language=\"javascript\">\r\n<!--\r\n\tchangeNav(\"sync\");\r\n//-->\r\n</script>\r\n#macro (addChannelMessage $field)\r\n    #if (!$field.valid) $field.message #end\r\n#end\t\r\n\r\n<div class=\"main\">\r\n  <div class=\"title\"> \r\n    <h2>添加Channel</h2>\r\n  </div>\r\n <div class=\"crumbs\"><a href=\"channelList.htm\">Channel管理</a>&nbsp;&nbsp;>&nbsp;&nbsp;<a href=\"addChannel.htm\">添加Channel</a></div>\r\n \r\n \r\n<form name=\"addChannelForm\" method=\"post\" enctype=\"multipart/form-data\">\r\n\t$csrfToken.hiddenField\r\n\t<input type=\"hidden\" name=\"action\" value=\"channel_action\"/>\r\n\t<input type=\"hidden\" name=\"event_submit_do_add\" value=\"1\" />\r\n <div class=\"setting_box\">\r\n \r\n   #set ($channelGroup = $form.channelInfo.defaultInstance)\r\n   #set ($channelParameterGroup = $form.channelParameterInfo.defaultInstance)\r\n   \r\n    <table cellpadding=\"0\" cellspacing=\"0\" class=\"setting_otter\">\r\n      <tr> \r\n        <th width=\"300\">Channel Name：</th>\r\n        <td width=\"329\">\r\n            <input type=\"text\" name=\"$channelGroup.name.key\" value=\"$!channelGroup.name.value\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n            <span>4-15个字符</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#addChannelMessage ($channelGroup.name)#addChannelMessage ($channelGroup.formChannelError)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  <tr> \r\n        <th>同步一致性：</th>\r\n        <td>\r\n\t\t\t<input name=\"$channelParameterGroup.syncConsistency.key\" type=\"radio\" value=\"MEDIA\" class=\"radio\"/>\r\n\t\t\t基于数据库反查&nbsp;&nbsp;&nbsp;&nbsp; \r\n\t\t\t<input name=\"$channelParameterGroup.syncConsistency.key\" type=\"radio\" value=\"BASE\" checked='checked' class=\"radio\"/>\r\n\t\t\t基于当前日志变更\r\n\t\t</td>\r\n      </tr>\r\n      <tr> \r\n        <th>同步模式：</th>\r\n        <td><input name=\"$channelParameterGroup.syncMode.key\" type=\"radio\" value=\"ROW\" class=\"radio\"/>\r\n          行记录模式&nbsp;&nbsp;&nbsp;&nbsp; \r\n          <input name=\"$channelParameterGroup.syncMode.key\" type=\"radio\" value=\"FIELD\" checked='checked' class=\"radio\"/>\r\n          列记录模式</td>\r\n      </tr>\r\n\t  <tr> \r\n        <th>是否开启数据一致性：</th>\r\n        <td>\r\n\t\t\t<input type=\"radio\" name=\"$channelParameterGroup.enableRemedy.key\" value=\"true\" onclick=\"changeDisplay('enableRemedy','table-row')\" class=\"radio\"/>是\r\n            <input type=\"radio\" name=\"$channelParameterGroup.enableRemedy.key\" value=\"false\" onclick=\"changeDisplay('enableRemedy','none')\" checked=\"checked\" class=\"radio\"/>否 \r\n\t\t</td>\r\n      </tr>\r\n\t  <tr class=\"enableRemedy\"> \r\n        <th>一致性算法：</th>\r\n        <td>\r\n\t\t\t<input name=\"$channelParameterGroup.remedyAlgorithm.key\" type=\"radio\" value=\"LOOPBACK\" checked='checked' class=\"radio\"/>\r\n\t\t\t单向回环补救&nbsp;&nbsp;&nbsp;&nbsp; \r\n\t\t\t<input name=\"$channelParameterGroup.remedyAlgorithm.key\" type=\"radio\" value=\"INTERSECTION\" disabled class=\"radio\"/>\r\n\t\t\t时间交集补救&nbsp;&nbsp;&nbsp;\r\n\t\t</td>\r\n      </tr>\r\n\t  <tr class=\"enableRemedy\"> \r\n        <th>一致性反查数据库延迟阀值(s)：</th>\r\n        <td>\r\n              <input name=\"$channelParameterGroup.remedyDelayThresoldForMedia.key\" value=\"60\" type=\"text\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t  <br />\r\n\t\t\t  <span class=\"red\">#addChannelMessage ($channelParameterGroup.remedyDelayThresoldForMedia)</span>\r\n        </td>\r\n      </tr>\r\n      <tr>\r\n       <th>描述：</th>\r\n       <td><textarea cols=\"45\" rows=\"5\" name=\"$channelGroup.description.key\">$!group.description.value</textarea><span class=\"red\">#addChannelMessage ($channelGroup.description)</span><span class=\"red\">*</span></td>\r\n      </tr>\r\n    </table>\r\n </div>\r\n   <div class=\"btn\"><a href=\"javascript:document.addChannelForm.submit();\">保存</a></div> \r\n  \r\n  </form>\r\n</div>\r\n\t\r\n<script language=\"javascript\">\r\n<!--\r\n\tchangeDisplay('enableDetect','none')\r\n\tchangeDisplay('enableRemedy','none')\r\n//-->\r\n</script>\r\n"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/templates/home/screen/addColumnPair.vm",
    "content": "$control.setTemplate(\"home:navigation.vm\")\n<script type=\"text/javascript\" src=\"js/jquery-1.4.2.min.js\"></script> \n<script type=\"text/javascript\"> \n\tchangeNav(\"sync\");\n    /* 添加选择的项 */ \n    function Add(ObjSource, ObjTarget) { \n       if(ObjSource.val() ==null) return;    // 如果没有选择则退出函数，无这句话的话IE6会报错 \n        jQuery.each(ObjSource.val(), function(i, n) {    // 循环原列表中选中的值，依次添加到目标列表中 \n            var html = \"<option value='\" + n + \"' selected='selected'>\" + n + \"</option>\"; \n            ObjTarget.append(html); \n        }); \n        ObjSource.find(\"option:selected\").remove(); // 原列表中选中的值删除 \n    } \n    /* 添加全部 */ \n    function AddAll(ObjSource, ObjTarget) { \n        ObjTarget.append(ObjSource.html()); // 目标列表的HTML加上原列表的所有HTML \n        ObjSource.empty(); // 原列表清空 \n    }\n\t\n\tjQuery(document).ready(function($){\n\t\t$('#submit').click(function(){\n\t\t\t$('select#dltTarget_l option').each(function(index, el){\n\t\t\t\t$(el).attr('selected','selected');\n\t\t\t});\n\t\t\treturn true;\n\t\t});\n\t});\n\t\n\t\n\tjQuery(document).ready(function($){\n\t\t$('#submit').click(function(){\n\t\t\t$('select#dltTarget_r option').each(function(index, el){\n\t\t\t\t$(el).attr('selected','selected');\n\t\t\t});\n\t\t\treturn true;\n\t\t});\n\t});\n\t\n\tjQuery(document).ready(function($){\n\t\t$('#submitnext').click(function(){\n\t\t\t$('select#dltTarget_l option').each(function(index, el){\n\t\t\t\t$(el).attr('selected','selected');\n\t\t\t});\n\t\t\treturn true;\n\t\t});\n\t});\n\t\n\t\n\tjQuery(document).ready(function($){\n\t\t$('#submitnext').click(function(){\n\t\t\t$('select#dltTarget_r option').each(function(index, el){\n\t\t\t\t$(el).attr('selected','selected');\n\t\t\t});\n\t\t\treturn true;\n\t\t});\n\t});\n</script>\n#macro (addColumnPairMessage $field)\n    #if (!$field.valid) $field.message #end\n#end\n<div class=\"main\">\n  <div class=\"title\"> \n    <h2>Table详细</h2>\n  </div> \n<div class=\"crumbs\"><a href=\"channelList.htm?channelId=$channelId\">Channel管理</a>&nbsp;&nbsp;>&nbsp;&nbsp;<a href=\"pipelineList.htm?channelId=$channelId\">Pipeline管理</a>&nbsp;&nbsp;>&nbsp;&nbsp;<a href=\"dataMediaPairList.htm?pipelineId=$pipelineId\">映射关系管理</a>&nbsp;&nbsp;>&nbsp;&nbsp;<a href=\"addColumnPair.htm?pipelineId=$pipelineId&channelId=$channelId&dataMediaPairId=$dataMediaPairId&sourceMediaId=$sourceMediaId&targetMediaId=$targetMediaId\">编辑视图同步</a> </div>\n  <form name=\"addColumnPairForm\" method=\"post\" enctype=\"multipart/form-data\">\n\t$csrfToken.hiddenField\n\t<input type=\"hidden\" name=\"action\" value=\"column_pair_action\"/>\n\t<input type=\"hidden\" name=\"event_submit_do_save\" value=\"1\" />\n\t<input type=\"hidden\" name=\"dataMediaPairId\" value=\"$dataMediaPairId\" />\n\t<input type=\"hidden\" name=\"sourceMediaId\" value=\"$sourceMediaId\" />\n\t<input type=\"hidden\" name=\"targetMediaId\" value=\"$targetMediaId\" />\n\t<input type=\"hidden\" name=\"pipelineId\" value=\"$pipelineId\" />\n\t<input type=\"hidden\" name=\"channelId\" value=\"$channelId\" />\n\t#set ($columnPair = $form.columnPairInfo.defaultInstance)\n\t<input type=\"hidden\" name=\"$columnPair.id.key\" value=\"1\"/>\n  <div class=\"sel_l\">\n  <div class=\"box1\"> \n        <select id=\"dltSource_l\" name=\"dltSource_l\"  multiple=\"multiple\"> \n\t\t\t#foreach ($sourceColumn in $sourceColumns)\n\t\t\t\t<option value=\"$!sourceColumn\">$!sourceColumn</option> \n            #end\n        </select> \n  </div> \n  <div class=\"box2\"> \n        <input type=\"button\" class=\"btn_down1\" onclick=\"Add($('#dltSource_l'),$('#dltTarget_l'))\" /> \n        <input type=\"button\" class=\"btn_down2\" onclick=\"AddAll($('#dltSource_l'),$('#dltTarget_l'))\" /> \n        <input type=\"button\" class=\"btn_up1\" onclick=\"Add($('#dltTarget_l'),$('#dltSource_l'))\" /> \n        <input type=\"button\" class=\"btn_up2\" onclick=\"AddAll($('#dltTarget_l'),$('#dltSource_l'))\" /> \n  </div> \n  <div class=\"box3\"> \n        <select id=\"dltTarget_l\" name=\"$columnPair.dltTarget_l.key\" multiple=\"multiple\"> \n\t\t\t#foreach ($underSourceColumn in $underSourceColumns)\n\t\t\t\t<option value=\"$!underSourceColumn\" selected=\"selected\">$!underSourceColumn</option> \n            #end\n        </select> \n  </div> \n  \n  <input type=\"submit\" id=\"submit\" name=\"submitKey\" value=\"保存\" class=\"button\"></input>\n  <input type=\"submit\" id=\"submitnext\" name=\"submitKey\" value=\"下一步\" class=\"button\"></input>\n  <span class=\"red\">#addColumnPairMessage ($columnPair.formColumnPairError)</span>\n  </div>\n  \n  <div class=\"sel_r\">\n  <div class=\"box1\"> \n        <select id=\"dltSource_r\" name=\"dltSource_r\" multiple=\"multiple\"> \n            #foreach ($targetColumn in $targetColumns)\n\t\t\t\t<option value=\"$!targetColumn\">$!targetColumn</option> \n            #end\n        </select> \n  </div> \n  <div class=\"box2\"> \n        <input type=\"button\" class=\"btn_down1\" onclick=\"Add($('#dltSource_r'),$('#dltTarget_r'))\" /> \n        <input type=\"button\" class=\"btn_down2\" onclick=\"AddAll($('#dltSource_r'),$('#dltTarget_r'))\" /> \n        <input type=\"button\" class=\"btn_up1\" onclick=\"Add($('#dltTarget_r'),$('#dltSource_r'))\" /> \n        <input type=\"button\" class=\"btn_up2\" onclick=\"AddAll($('#dltTarget_r'),$('#dltSource_r'))\" /> \n  </div> \n  <div class=\"box3\"> \n        <select id=\"dltTarget_r\" name=\"$columnPair.dltTarget_r.key\" multiple=\"multiple\"> \n\t\t\t#foreach ($underTargetColumn in $underTargetColumns)\n\t\t\t\t<option value=\"$!underTargetColumn\" selected=\"selected\">$!underTargetColumn</option> \n            #end\n        </select> \n  </div> \n  </div>\n  </form>  \n</div>"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/templates/home/screen/addColumnPairGroup.vm",
    "content": "$control.setTemplate(\"home:navigation.vm\")\n<script type=\"text/javascript\" src=\"js/jquery-1.4.2.min.js\"></script> \n<script type=\"text/javascript\"> \n\tchangeNav(\"sync\");\n    /* 添加选择的项 */ \n    function AddDown() { \n\t\tvar selected = $(\"select#dltSource\").find(\"option:selected\");\n        if(selected ==null) return;    // 如果没有选择则退出函数，无这句话的话IE6会报错 \n        selected.each(function(i, n) {    // 循环原列表中选中的值，依次添加到目标列表中 \n            var html = \"<option value='\" + $(n).attr('value') + \"' selected='selected'>\" + $(n).html() + \"</option>\"; \n            $('#dltTarget').append(html); \n        }); \n        selected.remove(); // 原列表中选中的值删除 \n    }\n\t\n\tfunction AddUp() { \n\t\tvar selected = $(\"select#dltTarget\").find(\"option:selected\");\n        if(selected ==null) return;    // 如果没有选择则退出函数，无这句话的话IE6会报错 \n        selected.each(function(i, n) {    // 循环原列表中选中的值，依次添加到目标列表中 \n            var html = \"<option value='\" + $(n).attr('value') + \"' selected='selected'>\" + $(n).html() + \"</option>\"; \n            $('#dltSource').append(html); \n        }); \n        selected.remove(); // 原列表中选中的值删除 \n    }\n    /* 添加全部 */ \n    function AddAll(ObjSource, ObjTarget) { \n        ObjTarget.append(ObjSource.html()); // 目标列表的HTML加上原列表的所有HTML \n        ObjSource.empty(); // 原列表清空 \n    } \n\t\n\t\n\tjQuery(document).ready(function($){\n\t\t$('#submit').click(function(){\n\t\t\t$('select#dltTarget option').each(function(index, el){\n\t\t\t\t$(el).attr('selected','selected');\n\t\t\t});\n\t\t\treturn true;\n\t\t});\n\t});\n\t\n\t\n</script>\n#macro (addColumnPairGroupMessage $field)\n    #if (!$field.valid) $field.message #end\n#end\n<div class=\"main\">\n  <div class=\"title\"> \n    <h2>Table详细</h2>\n  </div> <div class=\"crumbs\"><a href=\"channelList.htm?channelId=$channelId\">Channel管理</a>&nbsp;&nbsp;>&nbsp;&nbsp;<a href=\"pipelineList.htm?channelId=$channelId\">Pipeline管理</a>&nbsp;&nbsp;>&nbsp;&nbsp;<a href=\"dataMediaPairList.htm?pipelineId=$pipelineId\">映射关系管理</a>&nbsp;&nbsp;>&nbsp;&nbsp;<a href=\"addColumnPairGroup.htm?dataMediaPairId=$dataMediaPairId&channelId=$channelId&pipelineId=$pipelineId\">编辑组合同步</a> </div>\n \n  <form name=\"addColumnPairForm\" method=\"post\" enctype=\"multipart/form-data\">\n\t$csrfToken.hiddenField\n\t<input type=\"hidden\" name=\"action\" value=\"column_pair_group_action\"/>\n\t<input type=\"hidden\" name=\"event_submit_do_save\" value=\"1\" />\n\t<input type=\"hidden\" name=\"dataMediaPairId\" value=\"$dataMediaPairId\" />\n\t<input type=\"hidden\" name=\"sourceMediaId\" value=\"$sourceMediaId\" />\n\t<input type=\"hidden\" name=\"targetMediaId\" value=\"$targetMediaId\" />\n\t<input type=\"hidden\" name=\"pipelineId\" value=\"$pipelineId\" />\n\t#set ($columnGroup = $form.columnPairGroupInfo.defaultInstance)\n\t<input type=\"hidden\" name=\"$columnGroup.id.key\" value=\"1\"/>\n\t\n  <div class=\"sel_l\">\n  <div class=\"box1\"> \n        <select id=\"dltSource\" name=\"dltSource\"  multiple=\"multiple\"> \n\t\t\t#foreach ($columnPair in $preColumnPairs)\n\t\t\t\t<option value=\"$!columnPair.sourceColumn.name:$!columnPair.targetColumn.name\">[$!columnPair.sourceColumn.name] - [$!columnPair.targetColumn.name]</option> \n            #end\n        </select> \n  </div> \n  <div class=\"box2\"> \n        <input type=\"button\" class=\"btn_down1\" onclick=\"AddDown()\" /> \n        <input type=\"button\" class=\"btn_down2\" onclick=\"AddAll($('#dltSource'),$('#dltTarget'))\" /> \n        <input type=\"button\" class=\"btn_up1\" onclick=\"AddUp()\" /> \n        <input type=\"button\" class=\"btn_up2\" onclick=\"AddAll($('#dltTarget'),$('#dltSource'))\" /> \n  </div> \n  <div class=\"box3\"> \n        <select id=\"dltTarget\" name=\"$columnGroup.groupResult.key\" multiple=\"multiple\"> \n\t\t\t#foreach ($columnPair in $columnPairs)\n\t\t\t\t<option value=\"$!columnPair.sourceColumn.name:$!columnPair.targetColumn.name\">[$!columnPair.sourceColumn.name] - [$!columnPair.targetColumn.name]</option> \n            #end\n        </select> \n  </div> \n  \n  <input  id=\"submit\" type=\"submit\" name=\"submitKey\" value=\"保存\" class=\"button\"></input>\n  <span class=\"red\">#addColumnPairGroupMessage ($columnGroup.formColumnPairGroupError)</span>\n  </div>\n  \n  </form>  \n</div>"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/templates/home/screen/addDataMatrix.vm",
    "content": "$control.setTemplate(\"home:navigation.vm\")\r\n<script language=\"javascript\">\r\n<!--\r\n\tchangeNav(\"datamedia\");\r\n//-->\r\n</script>\r\n#macro (addDataMatrixMessage $field)\r\n    #if (!$field.valid) $field.message #end\r\n#end\t\r\n\r\n<div class=\"main\">\r\n  <div class=\"title\"> \r\n    <h2>添加主备配置</h2>\r\n  </div>\r\n  <div class=\"crumbs\"><a href=\"dataMediaList.htm\">数据表配置</a>&nbsp;&nbsp;>&nbsp;&nbsp;<a href=\"addDataMatrix.htm\">添加主备配置</a></div>\r\n\r\n <form id=\"addDataMatrixForm\" name=\"addDataMatrixForm\" method=\"post\" enctype=\"multipart/form-data\">\r\n\t$csrfToken.hiddenField\r\n\t<input type=\"hidden\" name=\"action\" value=\"data_matrix_action\"/>\r\n\t<input type=\"hidden\" name=\"event_submit_do_add\" value=\"1\" />\r\n <div class=\"setting_box\">\r\n\t#set ($dataMatrixGroup = $form.dataMatrixInfo.defaultInstance)\r\n    <table cellpadding=\"0\" cellspacing=\"0\" class=\"setting_otter\">\r\n        <tr>\r\n        \t<th width=\"300\"></th>\r\n        \t<td width=\"329\">\r\n        \t\t#foreach($f in ${dataMatrixGroup.getFields()})\r\n        \t\t\t#if (!$f.valid) \r\n        \t\t\t\t<span class=\"red\">$!f.message</span><br/>\r\n        \t\t\t#end\r\n        \t\t#end\r\n        \t</td>\r\n        </tr>\r\n      <tr> \r\n        <th width=\"300\">group Key：</th>\r\n        <td width=\"329\">\r\n\t\t\t<input id=\"groupKey\" name=\"$dataMatrixGroup.groupKey.key\" value=\"$!dataMatrixGroup.groupKey.value\" type=\"text\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#addDataMatrixMessage ($dataMatrixGroup.groupKey)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  <tr> \r\n        <th width=\"300\">master：</th>\r\n        <td width=\"329\">\r\n\t\t\t<input id=\"master\" name=\"$dataMatrixGroup.master.key\" value=\"$!dataMatrixGroup.master.value\" type=\"text\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#addDataMatrixMessage ($dataMatrixGroup.master)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  <tr> \r\n        <th width=\"300\">slave：</th>\r\n        <td width=\"329\">\r\n\t\t\t<input id=\"slaves\" name=\"$dataMatrixGroup.slave.key\"  value=\"$dataMatrixGroup.slave.value\" type=\"text\" class=\"setting_input\"/>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#addDataMatrixMessage ($dataMatrixGroup.slave)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t   <tr>\r\n       <th>描述信息：</th>\r\n       <td>\r\n    \t\t<textarea cols=\"45\" rows=\"5\" name=\"$dataMatrixGroup.description.key\">$!dataMatrixGroup.description.value</textarea><span class=\"red\">*</span>\r\n    \t\t<br />\r\n    \t\t<span class=\"red\">#addDataMatrixMessage ($dataMatrixGroup.description)</span>\r\n\t   </td>\r\n      </tr>   \r\n    </table>\r\n </div>\r\n  <div class=\"btn\"><a href=\"javascript:document.addDataMatrixForm.submit();\">保存</a></div>\r\n  </form>\r\n  <br />\r\n  <br />\r\n  <br />\r\n</div>\r\n"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/templates/home/screen/addDataMedia.vm",
    "content": "$control.setTemplate(\"home:navigation.vm\")\r\n<script type='text/javascript' src='dwr/interface/Hello.js'></script>  \r\n<script type='text/javascript' src='dwr/engine.js'></script>  \r\n<script type='text/javascript' src='dwr/util.js'></script>  \r\n<script type='text/javascript' src='js/dbCheck.js'></script>\r\n<script language=\"javascript\">\r\n<!--\r\n\tchangeNav(\"datamedia\");\r\n//-->\r\n\r\nfunction changeKeyword(id, name) {\r\n\tif( document.getElementById(\"dataSourceId\") && document.getElementById(\"dataSourceName\")){\r\n    \tdocument.getElementById('dataSourceId').value = id;\r\n    \tdocument.getElementById('dataSourceName').value = name;\r\n\t}\r\n}\r\n</script>\r\n#macro (addDataMediaMessage $field)\r\n    #if (!$field.valid) $field.message #end\r\n#end\t\r\n\r\n<div class=\"main\">\r\n  <div class=\"title\"> \r\n    <h2>添加数据表</h2>\r\n  </div>\r\n  <div class=\"crumbs\"><a href=\"dataMediaList.htm\">数据表配置</a>&nbsp;&nbsp;>&nbsp;&nbsp;<a href=\"addDataMedia.htm\">添加数据表</a></div>\r\n\r\n <form id=\"addDataMediaForm\" name=\"addDataMediaForm\" method=\"post\" enctype=\"multipart/form-data\">\r\n\t$csrfToken.hiddenField\r\n\t<input type=\"hidden\" name=\"action\" value=\"data_media_action\"/>\r\n\t<input type=\"hidden\" name=\"event_submit_do_add\" value=\"1\" />\r\n <div class=\"setting_box\">\r\n\t#set ($dataMediaGroup = $form.dataMediaInfo.defaultInstance)\r\n\t\r\n    <table cellpadding=\"0\" cellspacing=\"0\" class=\"setting_otter\">\r\n\t\t<tr>\r\n            <span class=\"red\">#addDataMediaMessage ($dataMediaGroup.formDataMediaError)</span>\r\n        </tr>\r\n      <tr> \r\n        <th width=\"300\">schema name：</th>\r\n        <td width=\"329\">\r\n\t\t\t<input id=\"namespace\" name=\"$dataMediaGroup.namespace.key\" value=\"$!dataMediaGroup.namespace.value\" type=\"text\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#addDataMediaMessage ($dataMediaGroup.namespace)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  <tr> \r\n        <th width=\"300\">table name：</th>\r\n        <td width=\"329\">\r\n\t\t\t<input id=\"name\" name=\"$dataMediaGroup.name.key\" value=\"$!dataMediaGroup.name.value\" type=\"text\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#addDataMediaMessage ($dataMediaGroup.name)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  <tr> \r\n        <th>数据源：</th>\r\n        <td>\r\n\t\t\t<input id=\"dataSourceName\" name=\"$dataMediaGroup.sourceName.key\" value=\"$!dataMediaGroup.sourceName.value\" type=\"text\" class=\"setting_input\" readonly />\r\n\t\t\t<input id=\"dataSourceId\" name=\"$dataMediaGroup.sourceId.key\" value=\"$!dataMediaGroup.sourceId.value\" type=\"hidden\" class=\"setting_input\"  />\r\n\t\t\t<input type=\"button\" value=\"查找数据源\" onclick=\"window.open('selectDataSource.htm', 'selectDataSource')\"><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#addDataMediaMessage ($dataMediaGroup.sourceId)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  <tr> \r\n        <th><input type='button' value='验证连接表' onclick='checkMap();' /><br><hr align=\"right\" style=\"width:100px;\">\r\n            <input type='button' value='查询Schema&Table' onclick='checkNamespaceTables()' />\r\n\t\t</th>\r\n        <td>\r\n\t\t\t<span class=\"red\" id=\"result\"></span>\r\n\t\t</td>\r\n      </tr>\r\n\t  <tr> \r\n        <th>table示例说明</th>\r\n        <td>\r\n\t\t\t<br/>\r\n\t\t\t单表配置: alibaba.product<br/>\r\n\t\t\t分表配置: alibaba[1-64].product , alibaba.product[01-32]<br/>\r\n\t\t\t正则配置: (.*).(.*)<br/>\r\n\t\t</td>\r\n      </tr>\r\n    </table>\r\n\t \r\n </div>\r\n  <div class=\"btn\"><a href=\"javascript:document.addDataMediaForm.submit();\">保存</a></div>\r\n  </form>\r\n  <br />\r\n  <br />\r\n  <br />\r\n</div>\r\n"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/templates/home/screen/addDataMediaPair.vm",
    "content": "$control.setTemplate(\"home:navigation.vm\")\r\n<script language=\"javascript\">\r\n<!--\r\n\tchangeNav(\"sync\");\r\n//-->\r\nfunction changeKeyword(id, name, local) {\r\n\tif(local == 'source'){\r\n    \tif( document.getElementById(\"sourceDataMediaId\") && document.getElementById(\"sourceDataMediaName\")){\r\n    \t\tdocument.getElementById('sourceDataMediaId').value = id;\r\n        \tdocument.getElementById('sourceDataMediaName').value = name;\r\n    \t}\r\n\t}else{\r\n\t\tif( document.getElementById(\"targetDataMediaId\") && document.getElementById(\"targetDataMediaName\")){\r\n    \t\tdocument.getElementById('targetDataMediaId').value = id;\r\n        \tdocument.getElementById('targetDataMediaName').value = name;\r\n    \t}\r\n\t}\r\n}\r\n</script>\r\n#macro (addDataMediaPairMessage $field)\r\n    #if (!$field.valid) $field.message #end\r\n#end\t\r\n\r\n<div class=\"main\">\r\n  <div class=\"title\"> \r\n    <h2>添加映射关系</h2>\r\n  </div> <div class=\"crumbs\"><a href=\"channelList.htm?channelId=$channelId\">Channel管理</a>&nbsp;&nbsp;>&nbsp;&nbsp;<a href=\"pipelineList.htm?channelId=$channelId\">Pipeline管理</a>&nbsp;&nbsp;>&nbsp;&nbsp;<a href=\"dataMediaPairList.htm?pipelineId=$pipelineId\">映射关系管理</a>&nbsp;&nbsp;>&nbsp;&nbsp;<a href=\"addDataMediaPair.htm?pipelineId=$pipelineId\">添加映射关系</a> </div> \r\n\r\n <form name=\"addPairForm\" method=\"post\" enctype=\"multipart/form-data\">\r\n\t$csrfToken.hiddenField\r\n\t<input type=\"hidden\" name=\"action\" value=\"data_media_pair_action\"/>\r\n\t<input type=\"hidden\" name=\"event_submit_do_add\" value=\"1\" />\r\n <div class=\"setting_box\">\r\n\t#set ($dataMediaPairGroup = $form.dataMediaPairInfo.defaultInstance)\r\n\t\r\n\t<input type=\"hidden\" name=\"$dataMediaPairGroup.pipelineId.key\" value=\"$pipelineId\"/>\r\n\t<input type=\"hidden\" name=\"pipelineId\" value=\"$pipelineId\"/>\r\n\t<input type=\"hidden\" name=\"channelId\" value=\"$channelId\"/>\r\n    <table cellpadding=\"0\" cellspacing=\"0\" class=\"setting setting_otter\">\r\n\t\t<span class=\"red\">#addDataMediaPairMessage ($dataMediaPairGroup.formDataMediaPairError)</span>\r\n\t  <tr> \r\n        <th>源数据表：</th>\r\n        <td>\r\n\t\t\t<input id=\"sourceDataMediaName\" type=\"text\" name=\"$dataMediaPairGroup.sourceDataMediaName.key\" value=\"$!dataMediaPairGroup.sourceDataMediaName.value\" class=\"setting_input\" readonly />\r\n\t\t\t<input id=\"sourceDataMediaId\" name=\"$dataMediaPairGroup.sourceDataMediaId.key\" value=\"$!dataMediaPairGroup.sourceDataMediaId.value\" type=\"hidden\" class=\"setting_input\"  />\r\n\t\t\t<input type=\"button\" value=\"查找数据表\" onclick=\"window.open('selectDataMedia.htm?local=source', 'selectSourceDataMedia')\"><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#addDataMediaPairMessage ($dataMediaPairGroup.sourceDataMediaId)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  <tr> \r\n        <th>目标数据表：</th>\r\n        <td>\r\n\t\t\t<input id=\"targetDataMediaName\" type=\"text\" name=\"$dataMediaPairGroup.targetDataMediaName.key\" value=\"$!dataMediaPairGroup.targetDataMediaName.value\" class=\"setting_input\" readonly />\r\n\t\t\t<input id=\"targetDataMediaId\" name=\"$dataMediaPairGroup.targetDataMediaId.key\" value=\"$!dataMediaPairGroup.targetDataMediaId.value\" type=\"hidden\" class=\"setting_input\"  />\r\n\t\t\t<input type=\"button\" value=\"查找数据表\" onclick=\"window.open('selectDataMedia.htm?local=target', 'selectTargerDataMedia')\"><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#addDataMediaPairMessage ($dataMediaPairGroup.targetDataMediaId)</span>\r\n\t\t</td>\r\n      </tr>\r\n      <tr>\r\n        <th>权重:</th>\r\n        <td>\r\n          <input type=\"text\" name=\"$dataMediaPairGroup.pushWeight.key\" value=\"5\" id=\"textfield5\" /><span class=\"red\">*</span>\r\n\t\t  <br />\r\n\t\t  <span class=\"red\">#addDataMediaPairMessage ($dataMediaPairGroup.pushWeight)</span>\r\n        </td>\r\n      </tr>\r\n\t  <tr>\r\n        <th>视图模式:</th>\r\n        <td>\r\n\t\t  <input type=\"radio\" name=\"$dataMediaPairGroup.columnPairMode.key\" value=\"INCLUDE\" id=\"RadioGroup2_0\" checked=\"checked\" class=\"radio\"/>include\r\n          <input type=\"radio\" name=\"$dataMediaPairGroup.columnPairMode.key\" value=\"EXCLUDE\" id=\"RadioGroup2_1\" class=\"radio\"/>exclude\r\n\t\t  <br />\r\n\t\t  <span class=\"red\">#addDataMediaPairMessage ($dataMediaPairGroup.columnPairMode)</span>\r\n        </td>\r\n      </tr>\r\n      <tr>\r\n        <th>EventProcessor类型:</th>\r\n        <td>\r\n\t\t\t<select name=\"$dataMediaPairGroup.filterType.key\" id=\"select\">\r\n\t\t\t\t<option value=\"CLAZZ\" selected=\"selected\">CLAZZ</option>\r\n\t\t\t\t<option value=\"SOURCE\">SOURCE</option>\r\n            </select><span class=\"red\">*</span>\r\n        </td>\r\n      </tr>\r\n\t  <tr>\r\n        <th>EventProcessor文本:</th>\r\n            <td><textarea cols=\"90\" rows=\"10\" name=\"$dataMediaPairGroup.filterText.key\">$!dataMediaPairGroup.filterText.value</textarea></td>\r\n      </tr>\r\n\t  <tr>\r\n        <th>FileResolver类型:</th>\r\n\t\t<td>\r\n        <select name=\"$dataMediaPairGroup.resolverType.key\" id=\"select\">\r\n\t\t\t\t<option value=\"CLAZZ\" selected=\"selected\">CLAZZ</option>\r\n\t\t\t\t<option value=\"SOURCE\">SOURCE</option>\r\n            </select><span class=\"red\">*</span>\r\n\t\t</td>\t\r\n      </tr>\r\n      <tr>\r\n        <th>FileResolver文本:</th>\r\n            <td><textarea cols=\"90\" rows=\"10\" name=\"$dataMediaPairGroup.resolverText.key\">$!dataMediaPairGroup.resolverText.value</textarea></td>\r\n      </tr>\r\n    </table>\r\n </div>\r\n  <input type=\"submit\" name=\"submitKey\" value=\"保存\" class=\"button\"></input>\r\n  <input type=\"submit\" name=\"submitKey\" value=\"下一步\" class=\"button\"></input>\r\n </form>\r\n</div>\r\n\r\n"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/templates/home/screen/addDataSource.vm",
    "content": "$control.setTemplate(\"home:navigation.vm\")\r\n<script type='text/javascript' src='dwr/interface/Hello.js'></script>  \r\n<script type='text/javascript' src='dwr/engine.js'></script>  \r\n<script type='text/javascript' src='dwr/util.js'></script>  \r\n<script type='text/javascript' src='js/dbCheck.js'></script>\r\n<script language=\"javascript\">\r\n<!--\r\n\tchangeNav(\"datamedia\");\r\n\r\n//-->\r\n</script>\r\n#macro (addDataSourceMessage $field)\r\n    #if (!$field.valid) $field.message #end\r\n#end\r\n\r\n<div class=\"main\">\r\n  <div class=\"title\"> \r\n    <h2>添加数据源</h2>\r\n  </div>\r\n  <div class=\"crumbs\"><a href=\"dataSourceList.htm\">数据源配置</a>&nbsp;&nbsp;>&nbsp;&nbsp;<a href=\"addDataSource.htm\">添加数据源</a></div>\r\n \r\n <form name=\"addSourceForm\" method=\"post\" enctype=\"multipart/form-data\">\r\n\t$csrfToken.hiddenField\r\n\t<input type=\"hidden\" name=\"action\" value=\"data_media_source_action\"/>\r\n\t<input type=\"hidden\" name=\"event_submit_do_add\" value=\"1\" />\r\n <div class=\"setting_box\">\r\n\t#set ($dataMediaSourceGroup = $form.dataMediaSourceInfo.defaultInstance)\r\n    <table cellpadding=\"0\" cellspacing=\"0\" class=\"setting_otter\">\r\n\t\t<span class=\"red\">#addDataSourceMessage ($dataMediaSourceGroup.formDataMediaSourceError)</span>\r\n      <tr> \r\n        <th width=\"300\">数据源名字：</th>\r\n        <td width=\"329\">\r\n\t\t\t<input type=\"text\" name=\"$dataMediaSourceGroup.name.key\" value=\"$!dataMediaSourceGroup.name.value\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#addDataSourceMessage ($dataMediaSourceGroup.name)</span>\r\n\t\t</td>\r\n      </tr>\r\n      <tr> \r\n        <th>类型：</th>\r\n        <td>\r\n            <select id=\"sourceType\" name=\"$dataMediaSourceGroup.type.key\" onchange=\"changeform();\" >\r\n            <option value=\"MYSQL\">MySQL</option>\r\n            <option value=\"ORACLE\">Oracle</option>\r\n\t\t\t</select><span class=\"red\">*</span>\r\n        </td>\r\n      </tr>\r\n      <tr> \r\n        <th>用户名：</th>\r\n        <td>\r\n\t\t\t<input id=\"sourceUserName\" name=\"$dataMediaSourceGroup.username.key\" value=\"$!dataMediaSourceGroup.username.value\" type=\"text\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#addDataSourceMessage ($dataMediaSourceGroup.username)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  <tr> \r\n        <th>密码：</th>\r\n        <td>\r\n\t\t\t<input id=\"sourcePassword\" name=\"$dataMediaSourceGroup.password.key\" type=\"password\" class=\"setting_input\"/>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#addDataSourceMessage ($dataMediaSourceGroup.password)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  <tr> \r\n        <th>URL：</th>\r\n        <td>\r\n\t\t\t<input id=\"sourceUrl\" name=\"$dataMediaSourceGroup.url.key\" value=\"$!dataMediaSourceGroup.url.value\" type=\"text\" class=\"setting_input\"/>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#addDataSourceMessage ($dataMediaSourceGroup.url)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  #**\r\n\t  <tr> \r\n        <th>StorePath：</th>\r\n        <td>\r\n\t\t\t<input id=\"sourceStorePath\" name=\"$dataMediaSourceGroup.storePath.key\" value=\"$!dataMediaSourceGroup.storePath.value\" type=\"text\" class=\"setting_input\"/><span class=\"red\">Napoli</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#addDataSourceMessage ($dataMediaSourceGroup.storePath)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  *#\r\n\t  <tr> \r\n        <th>编码：</th>\r\n        <td>\r\n\t\t\t<select id=\"sourceEncode\" name=\"$dataMediaSourceGroup.encode.key\"  style=\"width:200px;\" > \r\n\t\t\t\t<option value=\"GBK\" #if($!dataMediaSourceGroup.encode.value == 'GBK') selected  #end>GBK</option>\r\n\t\t\t\t<option value=\"UTF8\" #if($!dataMediaSourceGroup.encode.value == 'UTF8') selected  #end>UTF8</option>\r\n\t\t\t\t<option value=\"UTF8MB4\" #if($!dataMediaSourceGroup.encode.value == 'UTF8MB4') selected  #end>UTF8MB4</option>\r\n\t\t\t\t<option value=\"ISO-8859-1\" #if($!dataMediaSourceGroup.encode.value == 'ISO-8859-1') selected  #end>ISO-8859-1</option>\r\n            </select><span class=\"red\">*</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  <tr> \r\n        <th><input type='button' value='验证连接数据源' onclick='check();' /></th>\r\n        <td>\r\n\t\t\t<span class=\"red\" id=\"result\"></span>\r\n\t\t</td>\r\n      </tr>\r\n    <tr> \r\n        <th>url示例说明</th>\r\n        <td>\r\n\t\t\tmysql例子: jdbc:mysql://10.20.144.15:3306<br/>\r\n\t\t\toracle例子 : jdbc:oracle:thin:@10.20.144.29:1521:OINTEST<br/>\r\n\t\t\tmedia例子 : jdbc:mysql://groupKey=key (更改 key)\r\n\t\t</td>\r\n      </tr>\r\n      \r\n    </table>\r\n          \r\n </div>\r\n  <div class=\"btn\"><a href=\"javascript:document.addSourceForm.submit();\">保存</a></div>\r\n </form>\r\n</div>\r\n"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/templates/home/screen/addNode.vm",
    "content": "$control.setTemplate(\"home:navigation.vm\")\r\n<script language=\"javascript\">\r\n<!--\r\n\tchangeNav(\"node\");\r\n//-->\r\n</script>\r\n#macro (addNodeMessage $field)\r\n    #if (!$field.valid) $field.message #end\r\n#end\t\r\n\r\n<div class=\"main\">\r\n  <div class=\"title\"> \r\n    <h2>添加机器</h2>\r\n  </div>\r\n <div class=\"crumbs\"><a href=\"nodeList.htm\">机器管理</a>&nbsp;&nbsp;>&nbsp;&nbsp;<a href=\"addNode.htm\">添加机器</a></div>\r\n \r\n \r\n<form name=\"addNodeForm\" method=\"post\" enctype=\"multipart/form-data\">\r\n\t$csrfToken.hiddenField\r\n\t<input type=\"hidden\" name=\"action\" value=\"node_action\"/>\r\n\t<input type=\"hidden\" name=\"event_submit_do_add\" value=\"1\" />\r\n <div class=\"setting_box\">\r\n \r\n   #set ($nodeGroup = $form.nodeInfo.defaultInstance)\r\n   #set ($nodeParameterGroup = $form.nodeParameterInfo.defaultInstance)\r\n   \r\n    <table cellpadding=\"0\" cellspacing=\"0\" class=\"setting_otter\">\r\n\t\t<span class=\"red\">#addNodeMessage ($nodeGroup.formNodeError)</span>\r\n      <tr> \r\n        <th width=\"300\">机器名称：</th>\r\n        <td width=\"329\">\r\n            <input type=\"text\" name=\"$nodeGroup.name.key\" value=\"$!nodeGroup.name.value\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#addNodeMessage ($nodeGroup.name)</span>\r\n\t\t</td>\r\n      </tr>\r\n      <tr> \r\n        <th>机器IP：</th>\r\n        <td>\r\n\t\t\t<input type=\"text\" name=\"$nodeGroup.ip.key\" value=\"$!nodeGroup.ip.value\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#addNodeMessage ($nodeGroup.ip)</span>\r\n\t\t</td>\r\n      </tr>\r\n      <tr> \r\n        <th>机器端口：</th>\r\n        <td>\r\n\t\t\t<input type=\"text\" name=\"$nodeGroup.port.key\" value=\"$!nodeGroup.port.value\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#addNodeMessage ($nodeGroup.port)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  #**\r\n\t  <tr> \r\n        <th>ZooKeeper集群：</th>\r\n        <td>\r\n\t\t\t<textarea class=\"service\" name=\"$nodeParameterGroup.zkClusters.key\" cols=\"45\" rows=\"5\" >$!nodeParameterGroup.zkClusters.value</textarea><span class=\"red\">*</span>\r\n\t\t\t  <br />\r\n\t\t\t  <span>格式如 10.20.10.20:8080;（必须以分号结束，可添多个）</span>\r\n\t\t\t  <br />\r\n\t\t\t<span class=\"red\">#addNodeMessage ($nodeParameterGroup.zkClusters)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  *#\r\n\t  <tr> \r\n        <th>下载端口：</th>\r\n        <td>\r\n\t\t\t<input type=\"text\" name=\"$nodeParameterGroup.downloadPort.key\" value=\"$!nodeParameterGroup.downloadPort.value\" class=\"setting_input\"/>\r\n\t\t\t<br />\r\n\t\t    <span>可为空，不填写默认即为：机器端口 + 1</span>\r\n\t\t    <br />\r\n\t\t\t<span class=\"red\">#addNodeMessage ($nodeParameterGroup.downloadPort)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  <tr> \r\n        <th>MBean端口：</th>\r\n        <td>\r\n\t\t\t<input type=\"text\" name=\"$nodeParameterGroup.mbeanPort.key\" value=\"$!nodeParameterGroup.mbeanPort.value\" class=\"setting_input\"/>\r\n\t\t\t<br />\r\n\t\t\t<span>可为空，不填写默认即为：机器端口 + 2</span>\r\n\t\t    <br />\r\n\t\t\t<span class=\"red\">#addNodeMessage ($nodeParameterGroup.mbeanPort)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  <tr> \r\n        <th>外部IP：</th>\r\n        <td>\r\n\t\t\t<input type=\"text\" name=\"$nodeParameterGroup.externalIp.key\" value=\"$!nodeParameterGroup.externalIp.value\" class=\"setting_input\" />\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#addNodeMessage ($nodeParameterGroup.externalIp)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  <tr> \r\n        <th>启用外部IP：</th>\r\n        <td width=\"329\">\r\n              <input type=\"radio\" name=\"$nodeParameterGroup.useExternalIp.key\" value=\"true\" id=\"RadioGroup2_0\" class=\"radio\"/>是\r\n              <input type=\"radio\" name=\"$nodeParameterGroup.useExternalIp.key\" value=\"false\" id=\"RadioGroup2_1\" checked=\"checked\" class=\"radio\"/>否 \r\n        </td>\r\n      </tr>\r\n\t  <tr> \r\n        <th>zookeeper集群：</th>\r\n        <td>\r\n            <select id=\"zkCluster\" name=\"$nodeParameterGroup.autoKeeperClusterId.key\">\r\n\t\t\t#foreach($zkCluster in $zkClusters)\r\n            <option value=\"$zkCluster.id\" #if($velocityCount == 0)selected#end>$zkCluster.clusterName</option>\r\n\t\t\t#end\r\n\t\t\t</select>\r\n        </td>\r\n      </tr>\r\n      <tr>\r\n       <th>描述：</th>\r\n       <td>\r\n    \t\t<textarea cols=\"45\" rows=\"5\" name=\"$nodeGroup.description.key\">$!nodeGroup.description.value</textarea>\r\n    \t\t<br />\r\n    \t\t<span class=\"red\">#addNodeMessage ($nodeGroup.description)</span>\r\n\t   </td>\r\n      </tr>\r\n    </table>\r\n </div>\r\n   <div class=\"btn\"><a href=\"javascript:document.addNodeForm.submit();\">保存</a></div> \r\n  \r\n  </form>\r\n</div>"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/templates/home/screen/addPipeline.vm",
    "content": "$control.setTemplate(\"home:navigation.vm\")\r\n<script language=\"javascript\">\r\n<!--\r\n\tchangeNav(\"sync\");\r\n\tfunction changeSuperConfig() {\r\n\t\tvar obj = document.getElementById('superConfig');\r\n\t\tif(obj.checked) {\r\n\t\t\tchangeDisplay('super','table-row');\r\n\t\t}else {\r\n\t\t\tchangeDisplay('super','none');\r\n\t\t}\r\n\t}\r\n\t\r\n\tfunction changeKeyword(id, name) {\r\n    \tif( document.getElementById(\"destinationName\")){\r\n        \tdocument.getElementById('destinationName').value = name;\r\n    \t}\r\n\t}\r\n\r\n//-->\r\n</script>\r\n#macro (addPipelineMessage $field)\r\n    #if (!$field.valid) $field.message #end\r\n#end\r\n\r\n<div class=\"main\">\r\n  <div class=\"title\"> \r\n    <h2>添加Pipeline</h2>\r\n  </div>\r\n <div class=\"crumbs\"><a href=\"channelList.htm?channelId=$channelId\">Channel管理</a>&nbsp;&nbsp;>&nbsp;&nbsp;<a href=\"pipelineList.htm?channelId=$channelId\">Pipeline管理</a>&nbsp;&nbsp;>&nbsp;&nbsp;<a href=\"addPipeline.htm?channelId=$channelId\">添加Pipeline</a></div>   \r\n \r\n <form name=\"addPipelineForm\" method=\"post\" enctype=\"multipart/form-data\">\r\n\t$csrfToken.hiddenField\r\n\t<input type=\"hidden\" name=\"action\" value=\"pipeline_action\"/>\r\n\t<input type=\"hidden\" name=\"event_submit_do_add\" value=\"1\" />\r\n\t\r\n <div class=\"setting_box\">\r\n\t#set ($pipelineGroup = $form.pipelineInfo.defaultInstance)\r\n    #set ($pipelineParameterGroup = $form.pipelineParameterInfo.defaultInstance)\r\n   \r\n\t<input type=\"hidden\" name=\"$pipelineGroup.id.key\" value=\"$pipeline.id\" />\r\n\t<input type=\"hidden\" name=\"$pipelineGroup.channelId.key\" value=\"$channelId\"/>\r\n\t<input type=\"hidden\" name=\"channelId\" value=\"$channelId\"/>\r\n    <table cellpadding=\"0\" cellspacing=\"0\" class=\"setting_otter\">\r\n      <tr> \r\n        <th width=\"300\">Pipeline名字：</th>\r\n        <td width=\"329\">\r\n\t\t\t<input name=\"$pipelineGroup.name.key\" value=\"$!pipelineGroup.name.value\" type=\"text\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n            <span>4-15个字符</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#addPipelineMessage ($pipelineGroup.name) #addPipelineMessage ($pipelineGroup.formPipelineError)</span>\r\n\t\t</td>\r\n      </tr>\r\n      <tr> \r\n        <th width=\"300\">Select机器：</th>\r\n        <td width=\"329\" class=\"textarea_b\">\r\n\t\t\t<select name=\"$pipelineGroup.selectNodeIds.key\" multiple=\"multiple\" style=\"height:100px;width:200px\">\r\n                <optgroup label=\"Select\">\r\n\t\t\t\t\t#foreach ($node in $nodes)\r\n    \t\t\t\t<option value=\"$node.id\" #if($!pipelineGroup.selectNodeIds.value == $node.id) selected #end>$node.name</option>\r\n\t\t\t\t\t#end\r\n\t\t\t\t</optgroup>\r\n            </select><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#addPipelineMessage ($pipelineGroup.selectNodeIds)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  #**\r\n\t  <tr> \r\n        <th width=\"300\">Extract机器：</th>\r\n        <td width=\"329\" class=\"textarea\">\r\n\t\t\t<select name=\"$pipelineGroup.extractNodeIds.key\" multiple=\"multiple\" style=\"height:100px;width:200px\">\r\n                <optgroup label=\"Extract\">\r\n\t\t\t\t\t#foreach ($node in $nodes)\r\n    \t\t\t\t<option value=\"$node.id\" #if($!pipelineGroup.extractNodeIds.value == $node.id) selected #end>$node.name</option>\r\n\t\t\t\t\t#end\r\n\t\t\t\t</optgroup>\r\n            </select><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#addPipelineMessage ($pipelineGroup.extractNodeIds)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  *#\r\n      <tr> \r\n        <th width=\"300\">Load机器：</th>\r\n        <td width=\"329\" class=\"textarea_b\">\r\n\t\t\t<select name=\"$pipelineGroup.loadNodeIds.key\" multiple=\"multiple\" style=\"height:100px;width:200px\">\r\n                <optgroup label=\"Load\">\r\n    \t\t\t\t#foreach ($node in $nodes)\r\n    \t\t\t\t<option value=\"$node.id\" #if($!pipelineGroup.loadNodeIds.value == $node.id) selected #end>$node.name</option>\r\n\t\t\t\t\t#end\r\n\t\t\t\t</optgroup>\r\n            </select><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#addPipelineMessage ($pipelineGroup.loadNodeIds)</span>\r\n\t\t</td>\r\n      </tr>\r\n      <tr> \r\n        <th width=\"300\">并行度：</th>\r\n        <td width=\"329\">\r\n\t\t\t<input name=\"$pipelineParameterGroup.parallelism.key\" value=\"5\" type=\"text\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#addPipelineMessage ($pipelineParameterGroup.parallelism)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  <tr> \r\n        <th width=\"300\">数据反查线程数：</th>\r\n        <td width=\"329\">\r\n\t\t\t<input name=\"$pipelineParameterGroup.extractPoolSize.key\" value=\"10\" type=\"text\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#addPipelineMessage ($pipelineParameterGroup.extractPoolSize)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  <tr> \r\n        <th width=\"300\">数据载入线程数：</th>\r\n        <td width=\"329\">\r\n\t\t\t<input name=\"$pipelineParameterGroup.loadPoolSize.key\" value=\"15\" type=\"text\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#addPipelineMessage ($pipelineParameterGroup.loadPoolSize)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  <tr> \r\n        <th width=\"300\">文件载入线程数：</th>\r\n        <td width=\"329\">\r\n\t\t\t<input name=\"$pipelineParameterGroup.fileLoadPoolSize.key\" value=\"15\" type=\"text\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#addPipelineMessage ($pipelineParameterGroup.fileLoadPoolSize)</span>\r\n\t\t</td>\r\n      </tr>\r\n      <tr> \r\n        <th width=\"300\">主站点：</th>\r\n        <td width=\"329\">\r\n              <input type=\"radio\" name=\"$pipelineParameterGroup.home.key\" value=\"true\" id=\"RadioGroup1_0\" class=\"radio\"/>是\r\n              <input type=\"radio\" name=\"$pipelineParameterGroup.home.key\" value=\"false\" id=\"RadioGroup1_1\" checked=\"checked\"  class=\"radio\"/>否 \r\n        </td>\r\n      </tr>\r\n\t  <tr> \r\n        <th width=\"300\">同步数据来源：</th>\r\n        <td width=\"329\">\r\n\t\t\t<input type=\"radio\" name=\"$pipelineParameterGroup.selectorMode.key\" value=\"Canal\" onclick=\"changeDisplay('eromanga','none');changeDisplay('canal','table-row');\" checked=\"checked\" class=\"radio\"/>Canal\r\n            <span class=\"red\">*</span>\r\n        </td>\r\n      </tr>\r\n\t  <tr> \r\n        <th width=\"300\">Canal名字：</th>\r\n        <td width=\"329\">\r\n\t\t\t  <input id=\"destinationName\" name=\"$pipelineParameterGroup.destinationName.key\" value=\"$!pipelineParameterGroup.destinationName.value\" class=\"setting_input\" />\r\n\t\t\t  <input type=\"button\" value=\"查找Canal\" onclick=\"window.open('selectCanal.htm', 'selectCanal')\"><span class=\"red\">*</span>\r\n\t\t\t  <br />\r\n\t\t\t  <span class=\"red\">#addPipelineMessage ($pipelineParameterGroup.destinationName)</span>\r\n        </td>\r\n      </tr>\r\n\t  #**\r\n\t  <tr> \r\n        <th width=\"300\">消费端ID：</th>\r\n        <td width=\"329\">\r\n              <input name=\"$pipelineParameterGroup.mainstemClientId.key\" value=\"1001\" type=\"text\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t  <br />\r\n\t\t\t  <span class=\"red\">#addPipelineMessage ($pipelineParameterGroup.mainstemClientId)</span>\r\n        </td>\r\n      </tr>\r\n\t  *#\r\n\t  <tr> \r\n        <th width=\"300\">消费批次大小：</th>\r\n        <td width=\"329\">\r\n              <input name=\"$pipelineParameterGroup.mainstemBatchsize.key\" value=\"6000\" type=\"text\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t  <br />\r\n\t\t\t  <span class=\"red\">#addPipelineMessage ($pipelineParameterGroup.mainstemBatchsize)</span>\r\n        </td>\r\n      </tr>\r\n\t  <tr class=\"canal\"> \r\n        <th width=\"300\">获取批次数据超时时间(毫秒): </th>\r\n        <td width=\"329\">\r\n              <input name=\"$pipelineParameterGroup.batchTimeout.key\" value=\"-1\" type=\"text\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t  <br />\r\n\t\t\t  <span>格式: -1不进行控制，0代表永久，>0则按照指定时间控制</span>\r\n\t\t\t  <br />\r\n\t\t\t  <span class=\"red\">#addPipelineMessage ($pipelineParameterGroup.batchTimeout)</span>\r\n        </td>\r\n      </tr>\r\n\t  <tr> \r\n        <th width=\"300\">Load批次大小：</th>\r\n        <td width=\"329\">\r\n              <input name=\"$pipelineParameterGroup.loadBatchsize.key\" value=\"50\" type=\"text\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t  <br />\r\n\t\t\t  <span class=\"red\">#addPipelineMessage ($pipelineParameterGroup.loadBatchsize)</span>\r\n        </td>\r\n      </tr>\r\n\t  <tr>\r\n       <th>描述：</th>\r\n       <td>\r\n\t\t\t<textarea name=\"$pipelineGroup.description.key\" cols=\"45\" rows=\"5\">$!pipelineGroup.description.value</textarea><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#addPipelineMessage ($!pipelineGroup.description)</span>\r\n\t   </td>\r\n      </tr>\r\n\t  <tr>\r\n\t\t<th width=\"300\">高级设置：</th>\r\n        <td width=\"329\">\r\n              <input id=\"superConfig\" type='checkbox' name='super' value=1  class=\"setting_input\" onclick=\"changeSuperConfig()\" />\r\n\t\t\t  <br />\r\n        </td>\r\n      </tr>\r\n\t  <tr class=\"super\"> \r\n        <th width=\"300\">使用batch：</th>\r\n        <td width=\"329\">\r\n              <input type=\"radio\" name=\"$pipelineParameterGroup.useBatch.key\" value=\"true\" id=\"RadioGroup2_0\" checked=\"checked\" class=\"radio\"/>是\r\n              <input type=\"radio\" name=\"$pipelineParameterGroup.useBatch.key\" value=\"false\" id=\"RadioGroup2_1\" class=\"radio\"/>否 \r\n        </td>\r\n      </tr>\r\n\t  <tr class=\"super\"> \r\n        <th width=\"300\">是否跳过Select异常：</th>\r\n        <td width=\"329\">\r\n              <input type=\"radio\" name=\"$pipelineParameterGroup.skipSelectException.key\" value=\"true\" id=\"RadioGroup2_0\" class=\"radio\"/>是\r\n              <input type=\"radio\" name=\"$pipelineParameterGroup.skipSelectException.key\" value=\"false\" id=\"RadioGroup2_1\" checked=\"checked\" class=\"radio\"/>否 \r\n        </td>\r\n      </tr>\r\n\t  <tr class=\"super\"> \r\n        <th width=\"300\">是否跳过Load异常：</th>\r\n        <td width=\"329\">\r\n              <input type=\"radio\" name=\"$pipelineParameterGroup.skipLoadException.key\" value=\"true\" id=\"RadioGroup2_0\" class=\"radio\"/>是\r\n              <input type=\"radio\" name=\"$pipelineParameterGroup.skipLoadException.key\" value=\"false\" id=\"RadioGroup2_1\" checked=\"checked\" class=\"radio\"/>否 \r\n        </td>\r\n      </tr>\r\n\t  <tr class=\"super\"> \r\n        <th width=\"300\">仲裁器调度模式：</th>\r\n        <td width=\"329\">\r\n            <select name=\"$pipelineParameterGroup.arbitrateMode.key\" id=\"select\">\r\n\t\t\t\t<option value=\"AUTOMATIC\" selected=\"selected\">自动选择</option>\r\n                <option value=\"RPC\">RPC</option>\r\n\t\t\t\t<option value=\"ZOOKEEPER\" >ZOOKEEPER</option>\r\n            \t<option value=\"MEMORY\">MEMORY</option>\r\n            </select><span class=\"red\">*</span>\r\n\t\t\t\r\n        </td>\r\n      </tr>\r\n      <tr class=\"super\"> \r\n        <th width=\"300\">负载均衡算法：</th>\r\n        <td width=\"329\">\r\n            <select name=\"$pipelineParameterGroup.lbAlgorithm.key\" id=\"select\">\r\n\t\t\t\t<option value=\"Stick\" selected=\"selected\">Stick</option>\r\n                <option value=\"RoundRbin\">RoundRbin</option>\r\n            \t<option value=\"Random\">Random</option>\r\n            </select><span class=\"red\">*</span>\r\n\t\t\t\r\n        </td>\r\n      </tr>\r\n\t <tr class=\"super\"> \r\n        <th width=\"300\">传输模式：</th>\r\n        <td width=\"329\">\r\n            <select name=\"$pipelineParameterGroup.pipeChooseType.key\" id=\"select\">\r\n            \t<option value=\"AUTOMATIC\" selected=\"selected\">自动选择</option>\r\n                <option value=\"RPC\">RPC</option>\r\n\t\t\t\t<option value=\"HTTP\">HTTP</option>\r\n            </select><span class=\"red\">*</span>\r\n        </td>\r\n      </tr>\r\n\t  <tr class=\"super\"> \r\n        <th width=\"300\">记录selector日志：</th>\r\n        <td width=\"329\">\r\n              <input type=\"radio\" name=\"$pipelineParameterGroup.dumpSelector.key\" value=\"true\" id=\"RadioGroup1_2\" checked=\"checked\" class=\"radio\"/>是\r\n              <input type=\"radio\" name=\"$pipelineParameterGroup.dumpSelector.key\" value=\"false\" id=\"RadioGroup1_3\" class=\"radio\"/>否 \r\n        </td>\r\n      </tr>\r\n\t  <tr class=\"super\"> \r\n        <th width=\"300\">记录selector详细日志：</th>\r\n        <td width=\"329\">\r\n              <input type=\"radio\" name=\"$pipelineParameterGroup.dumpSelectorDetail.key\" value=\"true\" id=\"RadioGroup1_2\"  class=\"radio\"/>是\r\n              <input type=\"radio\" name=\"$pipelineParameterGroup.dumpSelectorDetail.key\" value=\"false\" id=\"RadioGroup1_3\" checked=\"checked\" class=\"radio\"/>否 \r\n        </td>\r\n      </tr>\r\n\t  <tr class=\"super\"> \r\n        <th width=\"300\">记录load日志：</th>\r\n        <td width=\"329\">\r\n              <input type=\"radio\" name=\"$pipelineParameterGroup.dumpEvent.key\" value=\"true\" id=\"RadioGroup1_2\" class=\"radio\"/>是\r\n              <input type=\"radio\" name=\"$pipelineParameterGroup.dumpEvent.key\" value=\"false\" id=\"RadioGroup1_3\" checked=\"checked\" class=\"radio\"/>否 \r\n        </td>\r\n      </tr>\r\n\t  <tr class=\"super\"> \r\n        <th width=\"300\">dryRun模式：</th>\r\n        <td width=\"329\">\r\n              <input type=\"radio\" name=\"$pipelineParameterGroup.dryRun.key\" value=\"true\" id=\"RadioGroup1_2\" class=\"radio\"/>是\r\n              <input type=\"radio\" name=\"$pipelineParameterGroup.dryRun.key\" value=\"false\" id=\"RadioGroup1_3\" checked=\"checked\" class=\"radio\"/>否 \r\n        </td>\r\n      </tr>\r\n\t  <tr class=\"super\"> \r\n        <th width=\"300\">支持ddl同步：</th>\r\n        <td width=\"329\">\r\n              <input type=\"radio\" name=\"$pipelineParameterGroup.ddlSync.key\" value=\"true\" id=\"RadioGroup1_2\" checked=\"checked\" class=\"radio\"/>是\r\n              <input type=\"radio\" name=\"$pipelineParameterGroup.ddlSync.key\" value=\"false\" id=\"RadioGroup1_3\" class=\"radio\"/>否 \r\n        </td>\r\n      </tr>\r\n\t  <tr class=\"super\"> \r\n        <th width=\"300\">跳过ddl异常：</th>\r\n        <td width=\"329\">\r\n              <input type=\"radio\" name=\"$pipelineParameterGroup.skipDdlException.key\" value=\"true\" id=\"RadioGroup2_0\" class=\"radio\"/>是\r\n              <input type=\"radio\" name=\"$pipelineParameterGroup.skipDdlException.key\" value=\"false\" id=\"RadioGroup2_1\" checked=\"checked\" class=\"radio\"/>否 \r\n        </td>\r\n      </tr>\r\n\t  <tr class=\"super\"> \r\n        <th width=\"300\">文件重复同步对比：</th>\r\n        <td width=\"329\">\r\n              <input type=\"radio\" name=\"$pipelineParameterGroup.fileDetect.key\" value=\"true\" id=\"RadioGroup1_2\" class=\"radio\"/>是\r\n              <input type=\"radio\" name=\"$pipelineParameterGroup.fileDetect.key\" value=\"false\" id=\"RadioGroup1_3\" checked=\"checked\" class=\"radio\"/>否 \r\n        </td>\r\n      </tr>\r\n\t  <tr class=\"super\"> \r\n        <th width=\"300\">文件传输加密：</th>\r\n        <td width=\"329\">\r\n              <input type=\"radio\" name=\"$pipelineParameterGroup.useFileEncrypt.key\" value=\"true\" id=\"RadioGroup1_2\" class=\"radio\"/>是\r\n              <input type=\"radio\" name=\"$pipelineParameterGroup.useFileEncrypt.key\" value=\"false\" id=\"RadioGroup1_3\" checked=\"checked\" class=\"radio\"/>否 \r\n        </td>\r\n      </tr>\r\n\t  <tr class=\"super\"> \r\n        <th width=\"300\">启用公网同步：</th>\r\n        <td width=\"329\">\r\n              <input type=\"radio\" name=\"$pipelineParameterGroup.useExternalIp.key\" value=\"true\" id=\"RadioGroup1_2\" class=\"radio\"/>是\r\n              <input type=\"radio\" name=\"$pipelineParameterGroup.useExternalIp.key\" value=\"false\" id=\"RadioGroup1_3\" checked=\"checked\" class=\"radio\"/>否 \r\n        </td>\r\n      </tr>\r\n\t  <tr class=\"super\"> \r\n        <th width=\"300\">跳过自由门数据：</th>\r\n        <td width=\"329\">\r\n              <input type=\"radio\" name=\"$pipelineParameterGroup.skipFreedom.key\" value=\"true\" id=\"RadioGroup1_2\" class=\"radio\"/>是\r\n              <input type=\"radio\" name=\"$pipelineParameterGroup.skipFreedom.key\" value=\"false\" id=\"RadioGroup1_3\" checked=\"checked\" class=\"radio\"/>否 \r\n        </td>\r\n      </tr>\r\n\t  <tr class=\"super\"> \r\n        <th width=\"300\">跳过反查无记录数据：</th>\r\n        <td width=\"329\">\r\n              <input type=\"radio\" name=\"$pipelineParameterGroup.skipNoRow.key\" value=\"true\" id=\"RadioGroup1_2\" class=\"radio\"/>是\r\n              <input type=\"radio\" name=\"$pipelineParameterGroup.skipNoRow.key\" value=\"false\" id=\"RadioGroup1_3\" checked=\"checked\" class=\"radio\"/>否 \r\n        </td>\r\n      </tr>\r\n\t  <tr class=\"super\"> \r\n        <th width=\"300\">启用数据表类型转化：</th>\r\n        <td width=\"329\">\r\n              <input type=\"radio\" name=\"$pipelineParameterGroup.useTableTransform.key\" value=\"true\" id=\"RadioGroup1_2\" class=\"radio\"/>是\r\n              <input type=\"radio\" name=\"$pipelineParameterGroup.useTableTransform.key\" value=\"false\" id=\"RadioGroup1_3\" checked=\"checked\" class=\"radio\"/>否 \r\n        </td>\r\n      </tr>\r\n\t  <tr class=\"super\"> \r\n        <th width=\"300\">兼容字段新增同步：</th>\r\n        <td width=\"329\">\r\n              <input type=\"radio\" name=\"$pipelineParameterGroup.enableCompatibleMissColumn.key\" value=\"true\" id=\"RadioGroup1_2\" checked=\"checked\" class=\"radio\"/>是\r\n              <input type=\"radio\" name=\"$pipelineParameterGroup.enableCompatibleMissColumn.key\" value=\"false\" id=\"RadioGroup1_3\" class=\"radio\"/>否 \r\n        </td>\r\n      </tr>\r\n\t <tr class=\"super\"> \r\n        <th width=\"300\">自定义同步标记：</th>\r\n        <td width=\"329\">\r\n              <input name=\"$pipelineParameterGroup.channelInfo.key\" type=\"text\" class=\"setting_input\"/>\r\n\t\t\t  <br />\r\n\t\t\t  <span class=\"red\">#addPipelineMessage ($pipelineParameterGroup.channelInfo)</span>\r\n        </td>\r\n      </tr>\r\n    </table>\r\n </div>\r\n  <div class=\"btn\"><a href=\"javascript:document.addPipelineForm.submit();\">保存</a></div>\r\n </form>\r\n</div>\r\n\r\n<script language=\"javascript\">\r\n<!--\r\n\tchangeDisplay('canal','table-row')\r\n\tchangeSuperConfig();\r\n//-->\r\n</script>\r\n"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/templates/home/screen/addUser.vm",
    "content": "$control.setTemplate(\"home:navigation.vm\")\r\n<script language=\"javascript\">\r\n<!--\r\n\tchangeNav(\"user\");\r\n//-->\r\n</script>\r\n#macro (addUserMessage $field)\r\n    #if (!$field.valid) $field.message #end\r\n#end\t\r\n<div class=\"main\">\r\n  <div class=\"title\"> \r\n    <h2>添加用户</h2>\r\n  </div>\r\n <div class=\"crumbs\"><a href=\"userManager.htm\">权限管理</a>&nbsp;&nbsp;>&nbsp;&nbsp;<a href=\"addUser.htm\">添加用户</a></div>\r\n <form name=\"addUserForm\" method=\"post\" enctype=\"multipart/form-data\">\r\n\t$csrfToken.hiddenField\r\n\t<input type=\"hidden\" name=\"action\" value=\"user_action\"/>\r\n\t<input type=\"hidden\" name=\"event_submit_do_add\" value=\"1\" />\r\n <div class=\"setting_box\">\r\n\t\r\n\t#set ($userGroup = $form.addUserInfo.defaultInstance)\r\n    <table cellpadding=\"0\" cellspacing=\"0\" class=\"setting_otter\">\r\n      <tr> \r\n        <th width=\"300\">用户名：</th>\r\n        <td width=\"329\">\r\n\t\t\t<input name=\"$userGroup.name.key\" value=\"$!userGroup.name.value\" type=\"text\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#addUserMessage ($userGroup.name)</span>\r\n\t\t</td>\r\n      </tr>\r\n      <tr> \r\n        <th>密码：</th>\r\n        <td>\r\n\t\t\t<input name=\"$userGroup.password.key\" type=\"password\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#addUserMessage ($userGroup.password)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  <tr> \r\n        <th>重复输入密码：</th>\r\n        <td>\r\n\t\t\t<input name=\"$userGroup.rePassword.key\" type=\"password\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#addUserMessage ($userGroup.rePassword)</span>\r\n\t\t</td>\r\n      </tr>\r\n      <tr> \r\n        <th>部门：</th>\r\n        <td>\r\n\t\t\t<input name=\"$userGroup.department.key\" value=\"$!userGroup.department.value\"  type=\"text\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#addUserMessage ($userGroup.department)</span>\r\n\t\t</td>\r\n      </tr>\r\n      <tr> \r\n        <th>真实姓名：</th>\r\n        <td>\r\n\t\t\t<input name=\"$userGroup.realName.key\" value=\"$!userGroup.realName.value\"  type=\"text\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#addUserMessage ($userGroup.realName)</span>\r\n\t\t</td>\r\n      </tr>\r\n      <tr> \r\n        <th>权限选择：</th>\r\n        <td><input name=\"$userGroup.authorizeType.key\" type=\"radio\" value=\"ADMIN\" class=\"radio\"/>\r\n          超级管理员 &nbsp;&nbsp;&nbsp;&nbsp;\r\n            <input name=\"$userGroup.authorizeType.key\" type=\"radio\" value=\"OPERATOR\" checked=\"checked\" class=\"radio\"/>\r\n          普通用户</td>\r\n      </tr>\r\n    </table>\r\n </div>\r\n </form>\r\n  <div class=\"btn\"><a href=\"javascript:document.addUserForm.submit();\">保存</a></div>\r\n</div>\r\n"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/templates/home/screen/addZookeeper.vm",
    "content": "$control.setTemplate(\"home:navigation.vm\")\r\n<script language=\"javascript\">\r\n<!--\r\n\tchangeNav(\"record\");\r\n//-->\r\n</script>\r\n#macro (addAutoKeeperClusterMessage $field)\r\n    #if (!$field.valid) $field.message #end\r\n#end\r\n\r\n\r\n<div class=\"main\">\r\n  <div class=\"title\"> \r\n    <h2>添加Zookeeper集群</h2>\r\n  </div>\r\n\r\n#if($message == \"init\")\r\n\t<span class=\"red\">ZooKeeper集群为空，无法添加Node或Canal，请先至少添加一个ZooKeeper集群</span>\r\n#end\r\n \r\n<form name=\"addZkClusterForm\" method=\"post\" enctype=\"multipart/form-data\">\r\n\t$csrfToken.hiddenField\r\n\t<input type=\"hidden\" name=\"action\" value=\"auto_keeper_cluster_action\"/>\r\n\t<input type=\"hidden\" name=\"event_submit_do_add\" value=\"1\" />\r\n <div class=\"setting_box\">\r\n \r\n   #set ($autoKeeperClusterGroup = $form.autokeeperClusterInfo.defaultInstance)\r\n    <table cellpadding=\"0\" cellspacing=\"0\" class=\"setting_otter\">\r\n\t\t<span class=\"red\">#addAutoKeeperClusterMessage ($autoKeeperClusterGroup.formAutokeeperClusterError)</span>\r\n      <tr> \r\n        <th width=\"300\">集群名字：</th>\r\n        <td width=\"329\">\r\n            <input type=\"text\" name=\"$autoKeeperClusterGroup.clusterName.key\" value=\"$!autoKeeperClusterGroup.clusterName.value\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#addAutoKeeperClusterMessage ($autoKeeperClusterGroup.clusterName)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  <tr> \r\n        <th>ZooKeeper集群：</th>\r\n        <td>\r\n\t\t\t<textarea class=\"service\" name=\"$autoKeeperClusterGroup.zookeeperClusters.key\" cols=\"45\" rows=\"5\" >$!autoKeeperClusterGroup.zookeeperClusters.value</textarea><span class=\"red\">*</span>\r\n\t\t\t  <br />\r\n\t\t\t  <span>格式如 10.20.10.20:8080;（必须以分号结束，可添多个）</span>\r\n\t\t\t  <br />\r\n\t\t\t<span class=\"red\">#addAutoKeeperClusterMessage ($autoKeeperClusterGroup.zookeeperClusters)</span>\r\n\t\t</td>\r\n      </tr>\r\n      <tr>\r\n       <th>描述：</th>\r\n       <td>\r\n    \t\t<textarea cols=\"45\" rows=\"5\" name=\"$autoKeeperClusterGroup.description.key\">$!autoKeeperClusterGroup.description.value</textarea>\r\n    \t\t<br />\r\n    \t\t<span class=\"red\">#addAutoKeeperClusterMessage ($autoKeeperClusterGroup.description)</span>\r\n\t   </td>\r\n      </tr>\r\n    </table>\r\n </div>\r\n   <div class=\"btn\"><a href=\"javascript:document.addZkClusterForm.submit();\">保存</a></div> \r\n  \r\n  </form>\r\n</div>"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/templates/home/screen/alarmRuleList.vm",
    "content": "$control.setTemplate(\"home:navigation.vm\")\r\n#set($user = $rundata.request.session.getAttribute(\"managerUser\"))\r\n<script type=\"text/javascript\" src=\"js/trcolor.js\"></script>\r\n<script type=\"text/javascript\" src=\"js/jquery-1.4.2.min.js\"></script> \r\n<script language=\"javascript\" type=\"text/javascript\" src=\"js/My97DatePicker/WdatePicker.js\" ></script>\r\n\r\n<script language=\"javascript\">\r\n    <!--\r\n\tchangeNav(\"sync\");\r\n    $(function () {\r\n        $('.bubbleInfo').each(function () {\r\n            var distance = 10;\r\n            var time = 250;\r\n            var hideDelay = 200;\r\n\r\n            var hideDelayTimer = null;\r\n\r\n            var beingShown = false;\r\n            var shown = false;\r\n            var trigger = $('.trigger', this);\r\n            var info = $('.popup', this).css('opacity', 0);\r\n\r\n\r\n            $([trigger.get(0), info.get(0)]).mouseover(function () {\r\n                if (hideDelayTimer) clearTimeout(hideDelayTimer);\r\n                if (beingShown || shown) {\r\n                    // don't trigger the animation again\r\n                    return;\r\n                } else {\r\n                    // reset position of info box\r\n                    beingShown = true;\r\n\r\n                    info.css({\r\n                        top: 10,\r\n                        left: 10,\r\n                        display: 'block'\r\n                    }).animate({\r\n                        top: '+=' + distance + 'px',\r\n                        opacity: 1\r\n                    }, time, 'swing', function() {\r\n                        beingShown = false;\r\n                        shown = true;\r\n                    });\r\n                }\r\n\r\n                return false;\r\n            }).mouseout(function () {\r\n                if (hideDelayTimer) clearTimeout(hideDelayTimer);\r\n                hideDelayTimer = setTimeout(function () {\r\n                    hideDelayTimer = null;\r\n                    info.animate({\r\n                        top: '-=' + distance + 'px',\r\n                        opacity: 0\r\n                    }, time, 'swing', function () {\r\n                        shown = false;\r\n                        info.css('display', 'none');\r\n                    });\r\n\r\n                }, hideDelay);\r\n\r\n                return false;\r\n            });\r\n        });\r\n    });\r\n    \r\n\t function(dp){\r\n\t\t\t $('pauseTime')\r\n\t }\r\n    //-->\r\n</script>\r\n\r\n\r\n<div class=\"main\">\r\n  <div class=\"title\"> \r\n    <h2>监控规则管理</h2>\r\n  </div>\r\n  <div class=\"crumbs\"><a href=\"channelList.htm?channelId=$channelId\">channel管理</a>&nbsp;&nbsp;>&nbsp;&nbsp;<a href=\"pipelineList.htm?channelId=$channelId\">Pipeline管理</a>&nbsp;&nbsp;>&nbsp;&nbsp;监控管理</div>\r\n  <div class=\"crumbs\"></div>\r\n  <div class=\"tab\" id=\"Tab2\">\r\n        <div class=\"menubox\">\r\n        <ul>\r\n        <li id=\"two1\"><a href=\"dataMediaPairList.htm?pipelineId=$pipelineId\">映射关系列表</a></li>\r\n           \r\n        <li id=\"two2\"><a href=\"analysisThroughputStat.htm?pipelineId=$pipelineId\">吞吐量</a></li>\r\n        \r\n        <li id=\"two3\"><a href=\"analysisDelayStat.htm?pipelineId=$pipelineId\">延迟时间</a></li>\r\n           \r\n        <li id=\"two4\"><a href=\"analysisStageStat.htm?pipelineId=$pipelineId\">同步进度</a></li>   \r\n\t\t\r\n\t\t<li id=\"two5\"><a href=\"analysisThroughputHistory.htm?pipelineId=$pipelineId\">历史吞吐量</a></li>\r\n\t\t\r\n\t\t<li id=\"two6\" class=\"tab_active\"><a href=\"alarmRuleList.htm?pipelineId=$pipelineId\">监控管理</a></li>\r\n        \r\n\t\t<li id=\"two7\"><a href=\"logRecordTab.htm?pipelineId=$pipelineId\">日志记录</a></li>\r\n\t\t\r\n\t\t</ul>\r\n        </div>\r\n         <div class=\"contentbox_tab box_tab\">  \r\n           <div id=\"con_two_1\">\r\n<!--列表-->\r\n \r\n        <table border=\"0\" cellspacing=\"0\" cellpadding=\"0\" class=\"list2 changecolor_g\">\r\n          <tr> \r\n            <th width=\"8%\">序号</th>\r\n            <th width=\"8%\">监控项目</th>\r\n\t\t\t<th width=\"8%\">阈值</th>\r\n\t\t\t<th width=\"8%\">状态</th>\r\n\t\t\t<th width=\"8%\">发送对象</th>\r\n\t\t\t<th width=\"12%\">暂停时间</th>\r\n        \t<th width=\"24%\">操作</th>\r\n          </tr>\r\n\t\t\t#set($flag = false)\r\n\t\t\t#foreach ($alarmRule in $alarmRules)\r\n\t\t\t\t#set($flag = true)\r\n\t\t\t<tr> \r\n\t\t\t\t<td width=\"8%\">$alarmRule.id</td>\r\n\t\t\t\t<td width=\"8%\">#if($alarmRule.monitorName.isQueueSize()) 堆积 #elseif($alarmRule.monitorName.isDelayTime()) 延迟 #elseif($alarmRule.monitorName.isPipelineTimeout()) Pipeline超时 #elseif($alarmRule.monitorName.isProcessTimeout()) Process超时 #elseif($alarmRule.monitorName.isException()) 异常 #elseif($alarmRule.monitorName.isPaused()) 挂起 #elseif($alarmRule.monitorName.isPositionTimeout()) Position超时 #end</td>\r\n\t\t\t\t<td width=\"8%\">$alarmRule.matchValue</td>\r\n\t\t\t\t<td width=\"8%\">#if($alarmRule.status.isEnable() && !$alarmRule.isPaused()) <img src=\"images/alarm.png\" title=\"开启\" width=\"22\" height=\"22\" /> #else <img src=\"images/alarm_on.png\" title=\"暂停\" width=\"22\" height=\"22\" /> #end</td>\r\n\t\t\t\t<td width=\"8%\">$alarmRule.receiverKey</td>\r\n\t\t\t\t<td width=\"12%\">#if($alarmRule.isPaused())$!numberFormat.format($alarmRule.pauseTime)#end</td>\r\n\t\t\t\t<td width=\"24%\">\r\n\t\t\t\t\t<img src=\"images/ico_edit.png\" width=\"13\" height=\"13\" /><span title=\"$alarmRule.description\" class=\"ico_font\">描述</span>\r\n\t\t\t\t\t<span class=\"ico_line\">|</span><img src=\"images/ico_edit.png\" width=\"13\" height=\"13\" /><span class=\"ico_font\"><a href=\"logRecordList.htm?pipelineId=$alarmRule.pipelineId&monitorName=$alarmRule.monitorName\">历史</a></span>\r\n\t\t\t\t\t\r\n\t\t\t\t\t#if($user.authorizeType.isAdmin())\r\n\t\t\t\t\t\t#if($alarmRule.status.isEnable())\r\n    \t\t\t\t\t\t#if($alarmRule.isPaused())\r\n\t\t\t\t\t\t\t\t#set ($reEnableURL = $homeModule.setAction(\"AlarmRuleAction\").addQueryData(\"alarmRuleId\", $alarmRule.id).addQueryData(\"status\", \"enable\").addQueryData(\"pipelineId\",$pipelineId).addQueryData(\"eventSubmitDoStatus\", \"true\").render())\r\n\t\t\t\t\t\t\t\t<span class=\"ico_line\">|</span><a href=\"$reEnableURL\"><img src=\"images/ico_edit.png\" width=\"13\" height=\"13\" /><span class=\"ico_font\">恢复</span></a>\r\n\t\t\t\t\t\t\t#else\r\n        \t\t\t\t\t\t#set ($disableURL = $homeModule.setAction(\"AlarmRuleAction\").addQueryData(\"alarmRuleId\", $alarmRule.id).addQueryData(\"status\", \"disable\").addQueryData(\"pipelineId\",$pipelineId).addQueryData(\"eventSubmitDoStatus\", \"true\").render())\r\n\t\t\t\t\t\t\t\t<span class=\"ico_line\">|</span><a href=\"#\" onclick=\"WdatePicker({el:'rule_${alarmRule.id}',dateFmt:'yyyy-MM-dd HH:mm:ss',minDate:'%y-%M-%d %H:%m:%s',qsEnabled:true,quickSel:['%y-%M-%d #{%H+2}:%m:%s','%y-%M-%d #{%H+6}:%m:%s','%y-%M-%d #{%H+12}:%m:%s','%y-%M-#{%d+1} %H:%m:%s}','%y-%M-#{%d+2} %H:%m:%s}'], onpicked:function(){url='${disableURL}&pauseTime=' + $dp.cal.getDateStr();window.location.href=url;}});return false;\"><img src=\"images/ico_edit.png\" width=\"13\" height=\"13\" /><span class=\"ico_font\">暂停</span><span id=\"rule_${alarmRule.id}\" style=\"display:none;\" /></a>\r\n\t\t\t\t\t\t\t#end\r\n    \t\t\t\t\t#else\r\n    \t\t\t\t\t\t#set ($enableURL = $homeModule.setAction(\"AlarmRuleAction\").addQueryData(\"alarmRuleId\", $alarmRule.id).addQueryData(\"status\", \"enable\").addQueryData(\"pipelineId\",$pipelineId).addQueryData(\"eventSubmitDoStatus\", \"true\").render())\r\n    \t\t\t\t\t\t<span class=\"ico_line\">|</span><a href=\"$enableURL\"><img src=\"images/ico_edit.png\" width=\"13\" height=\"13\" /><span class=\"ico_font\">开启</span></a>\r\n\t\t\t\t\t\t#end\r\n    \t\t\t\t\t#set ($editURL = $homeModule.setTarget(\"editAlarmRule.vm\").addQueryData(\"alarmRuleId\", $alarmRule.id).render())\r\n\t\t\t\t\t\t<span class=\"ico_line\">|</span><a href=\"$editURL\"><img src=\"images/ico_edit.png\" width=\"13\" height=\"13\" /><span class=\"ico_font\">编辑</span></a>\r\n\t\t\t\t\t\t#if($alarmRule.status.isEnable() && !$alarmRule.isPaused())\r\n    \t\t\t\t\t\t<span class=\"ico_line\">|</span><img src=\"images/ico_del.png\" width=\"9\" height=\"9\" /><span title=\"暂停监控后才可以删除\" class=\"ico_font\">删除</span>\r\n\t\t\t\t\t\t#else\r\n        \t\t\t\t\t#set ($removeURL = $homeModule.setAction(\"AlarmRuleAction\").addQueryData(\"alarmRuleId\", $alarmRule.id).addQueryData(\"pipelineId\",$pipelineId).addQueryData(\"eventSubmitDoDelete\", \"true\").render())\r\n\t\t\t\t\t\t\t<span class=\"ico_line\">|</span><a href=\"$removeURL\"><img src=\"images/ico_del.png\" width=\"9\" height=\"9\" /><span class=\"ico_font\">删除</span></a>\r\n\t\t\t\t\t\t#end\r\n\t\t\t\t\t#end\r\n\t\t\t\t</td>\r\n\t\t\t</tr>\r\n\t\t\t#end\r\n\t\t \r\n\t\t  \r\n        </table>\r\n\t\t#if($user.authorizeType.isAdmin())\r\n        \t\t<div class=\"btn\">\r\n        \t\t\t#set ($addURL = $homeModule.setTarget(\"addAlarmRule.vm\").addQueryData(\"pipelineId\", $pipelineId).render())\r\n        \t\t\t<a href=\"$addURL\">添加</a>\r\n        \t\t</div>\r\n\t\t\t\t<div class=\"btn\">\r\n\t\t\t\t\t#set ($onekeyMonitorURL = $homeModule.setAction(\"AlarmRuleAction\").addQueryData(\"pipelineId\", $pipelineId).addQueryData(\"eventSubmitDoOnekeyAddMonitor\", \"true\"))\r\n        \t\t\t<a href=\"$onekeyMonitorURL\">一键添加</a>\r\n        \t\t</div>\r\n\t\t\t\t#if($flag)\r\n\t\t\t\t<div class=\"btn\">\r\n\t\t\t\t\t#set ($enableAllURL = $homeModule.setAction(\"AlarmRuleAction\").addQueryData(\"pipelineId\", $pipelineId).addQueryData(\"status\", \"enable\").addQueryData(\"eventSubmitDoStatusByPipeline\", \"true\").render())\r\n        \t\t\t<a href=\"$enableAllURL\">全部开启</a>\r\n\t\t\t\t</div>\r\n\t\t\t\t<div class=\"btn\">\r\n\t\t\t\t\t#set ($disableAllURL = $homeModule.setAction(\"AlarmRuleAction\").addQueryData(\"pipelineId\", $pipelineId).addQueryData(\"status\", \"disable\").addQueryData(\"eventSubmitDoStatusByPipeline\", \"true\").render())\r\n\t\t\t\t\t<a href=\"#\" onclick=\"WdatePicker({el:'rule_all',dateFmt:'yyyy-MM-dd HH:mm:ss',minDate:'%y-%M-%d %H:%m:%s',qsEnabled:true,quickSel:['%y-%M-%d #{%H+2}:%m:%s','%y-%M-%d #{%H+6}:%m:%s','%y-%M-%d #{%H+12}:%m:%s','%y-%M-#{%d+1} %H:%m:%s','%y-%M-#{%d+2} %H:%m:%s'], onpicked:function(){url='${disableAllURL}&pauseTime=' + $dp.cal.getDateStr();window.location.href=url;}});return false;\">全部暂停<span id=\"rule_all\" style=\"display:none;\" /></a>\r\n\t\t\t\t</div>\r\n\t\t\t\t#end\r\n\t\t#end\t\t\r\n\t\t   </div>\r\n\t\t </div>\r\n  </div>\r\n</div>"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/templates/home/screen/alarmSystemList.vm",
    "content": "$control.setTemplate(\"home:navigation.vm\")\n#set($user = $rundata.request.session.getAttribute(\"managerUser\"))\n<script type=\"text/javascript\" src=\"js/trcolor.js\"></script>\n<script type=\"text/javascript\" src=\"js/jquery-1.4.2.min.js\"></script>\n<script language=\"javascript\" type=\"text/javascript\" src=\"js/My97DatePicker/WdatePicker.js\" ></script>\n\n<script language=\"javascript\">\n    <!--\n\tchangeNav(\"record\");\n    $(function () {\n        $('.bubbleInfo').each(function () {\n            var distance = 10;\n            var time = 250;\n            var hideDelay = 200;\n\n            var hideDelayTimer = null;\n\n            var beingShown = false;\n            var shown = false;\n            var trigger = $('.trigger', this);\n            var info = $('.popup', this).css('opacity', 0);\n\n\n            $([trigger.get(0), info.get(0)]).mouseover(function () {\n                if (hideDelayTimer) clearTimeout(hideDelayTimer);\n                if (beingShown || shown) {\n                    // don't trigger the animation again\n                    return;\n                } else {\n                    // reset position of info box\n                    beingShown = true;\n\n                    info.css({\n                        top: 10,\n                        left: 10,\n                        display: 'block'\n                    }).animate({\n                        top: '+=' + distance + 'px',\n                        opacity: 1\n                    }, time, 'swing', function() {\n                        beingShown = false;\n                        shown = true;\n                    });\n                }\n\n                return false;\n            }).mouseout(function () {\n                if (hideDelayTimer) clearTimeout(hideDelayTimer);\n                hideDelayTimer = setTimeout(function () {\n                    hideDelayTimer = null;\n                    info.animate({\n                        top: '-=' + distance + 'px',\n                        opacity: 0\n                    }, time, 'swing', function () {\n                        shown = false;\n                        info.css('display', 'none');\n                    });\n\n                }, hideDelay);\n\n                return false;\n            });\n        });\n    });\n    \n    //-->\n</script>\n\n\n<div class=\"main\">\n  <div class=\"title\"> \n    <h2>监控配置列表</h2>\n  </div>\n  <div class=\"crumbs\"></div>     \n<!--列表-->\n  <!--分页表单-->\n   <form id=\"pageform\" name=\"pageform\" action=\"$homeModule.setTarget('alarmSystemList.vm')\" method=\"post\">\n    \t<input type=\"hidden\" id=\"pageIndex\" name=\"pageIndex\" value=\"\"/>\n\t\t<input type=\"hidden\" id=\"searchKey\" name=\"searchKey\" value=\"$!searchKey\"/>\n   </form>\n   \n        <table border=\"0\" cellspacing=\"0\" cellpadding=\"0\" class=\"list changecolor_g\">\n          <tr> \n            <th width=\"8%\">序号</th>\n\t\t\t<th width=\"8%\">PIPELINE通道</th>\n            <th width=\"8%\">监控项目</th>\n\t\t\t<th width=\"8%\">阈值</th>\n\t\t\t<th width=\"8%\">状态</th>\n\t\t\t<th width=\"8%\">发送对象</th>\n\t\t\t<th width=\"12%\">暂停时间</th>\n        \t<th width=\"24%\">操作</th>\n            \n          </tr>\n\t\t\t#set($flag = false)\n\t\t\t#foreach ($alarmRule in $alarmRules)\n\t\t\t\t#set($flag = true)\n\t\t\t<tr> \n\t\t\t\t<td width=\"8%\">$alarmRule.id</td>\n                <td width=\"8%\"><a href=\"dataMediaPairList.htm?pipelineId=$alarmRule.pipelineId\" >$alarmRule.pipelineId</a></td>\n                <td width=\"8%\">#if($alarmRule.monitorName.isQueueSize()) 堆积 #elseif($alarmRule.monitorName.isDelayTime()) 延迟 #elseif($alarmRule.monitorName.isPipelineTimeout()) Pipeline超时 #elseif($alarmRule.monitorName.isProcessTimeout()) Process超时 #elseif($alarmRule.monitorName.isException()) 异常 #elseif($alarmRule.monitorName.isPositionTimeout()) Position超时 #end</td>\n\t\t\t\t<td width=\"8%\">$alarmRule.matchValue</td>\n\t\t\t\t<td width=\"8%\">#if($alarmRule.status.isEnable() && !$alarmRule.isPaused()) <img src=\"images/alarm.png\" title=\"开启\" width=\"22\" height=\"22\" /> #else <img src=\"images/alarm_on.png\" title=\"暂停\" width=\"22\" height=\"22\" /> #end</td>\n\t\t\t\t<td width=\"8%\">$alarmRule.receiverKey</td>\n\t\t\t\t<td width=\"12%\">#if($alarmRule.isPaused())$!numberFormat.format($alarmRule.pauseTime)#end</td>\n\t\t\t\t<td width=\"24%\">\n\t\t\t\t\t<img src=\"images/ico_edit.png\" width=\"13\" height=\"13\" /><span title=\"$alarmRule.description\" class=\"ico_font\">描述</span>\n                    <span class=\"ico_line\">|</span><img src=\"images/ico_edit.png\" width=\"13\" height=\"13\" /><span title=\"$alarmRule.description\" class=\"ico_font\"><a href=\"logRecordList.htm?pipelineId=$alarmRule.pipelineId&monitorName=$alarmRule.monitorName\">历史</a></span>\n\t\t\t\t\t\n\t\t\t\t\t#if($user.authorizeType.isAdmin())\n\t\t\t\t\t\t#if($alarmRule.status.isEnable())\n    \t\t\t\t\t\t#if($alarmRule.isPaused())\n\t\t\t\t\t\t\t\t#set ($reEnableURL = $homeModule.setAction(\"AlarmRuleAction\").addQueryData(\"alarmRuleId\", $alarmRule.id).addQueryData(\"status\", \"enable\").addQueryData(\"eventSubmitDoStatusSystem\", \"true\").render())\n\t\t\t\t\t\t\t\t<span class=\"ico_line\">|</span><a href=\"$reEnableURL\"><img src=\"images/ico_edit.png\" width=\"13\" height=\"13\" /><span class=\"ico_font\">恢复</span></a>\n\t\t\t\t\t\t\t#else\n        \t\t\t\t\t\t#set ($disableURL = $homeModule.setAction(\"AlarmRuleAction\").addQueryData(\"alarmRuleId\", $alarmRule.id).addQueryData(\"status\", \"disable\").addQueryData(\"eventSubmitDoStatusSystem\", \"true\").render())\n\t\t\t\t\t\t\t\t<span class=\"ico_line\">|</span><a href=\"#\" onclick=\"WdatePicker({el:'rule_${alarmRule.id}',dateFmt:'yyyy-MM-dd HH:mm:ss',minDate:'%y-%M-%d %H:%m:%s',qsEnabled:true,quickSel:['%y-%M-%d #{%H+2}:%m:%s','%y-%M-%d #{%H+6}:%m:%s','%y-%M-%d #{%H+12}:%m:%s','%y-%M-#{%d+1} %H:%m:%s','%y-%M-#{%d+2} %H:%m:%s'], onpicked:function(){url='${disableURL}&pauseTime=' + $dp.cal.getDateStr();window.location.href=url;}});return false;\"><img src=\"images/ico_edit.png\" width=\"13\" height=\"13\" /><span class=\"ico_font\" id=\"\">暂停</span><span id=\"rule_${alarmRule.id}\" style=\"display:none;\" /></a>\n\t\t\t\t\t\t\t#end\n    \t\t\t\t\t#else\n    \t\t\t\t\t\t#set ($enableURL = $homeModule.setAction(\"AlarmRuleAction\").addQueryData(\"alarmRuleId\", $alarmRule.id).addQueryData(\"status\", \"enable\").addQueryData(\"pageIndex\", $paginator.page).addQueryData(\"eventSubmitDoStatusSystem\", \"true\").render())\n    \t\t\t\t\t\t<span class=\"ico_line\">|</span><a href=\"$enableURL\"><img src=\"images/ico_edit.png\" width=\"13\" height=\"13\" /><span class=\"ico_font\">开启</span></a>\n\t\t\t\t\t\t#end\n\t\t\t\t\t\t\n\t\t\t\t\t\t#set ($editURL = $homeModule.setTarget(\"editAlarmRule.vm\").addQueryData(\"alarmRuleId\", $alarmRule.id).render())\n    \t\t\t\t\t<span class=\"ico_line\">|</span><a href=\"$editURL\"><img src=\"images/ico_edit.png\" width=\"13\" height=\"13\" /><span class=\"ico_font\">编辑</span></a>\n\t\t\t\t\t\t#if($alarmRule.status.isEnable() && !$alarmRule.isPaused())\n\t\t\t\t\t\t\t<span class=\"ico_line\">|</span><img src=\"images/ico_del.png\" width=\"9\" height=\"9\" /><span title=\"暂停监控后才可以删除\" class=\"ico_font\">删除</span>\n\t\t\t\t\t\t#else\n\t\t\t\t\t\t\t#set ($removeURL = $homeModule.setAction(\"AlarmRuleAction\").addQueryData(\"alarmRuleId\", $alarmRule.id).addQueryData(\"pipelineId\",$alarmRule.pipelineId).addQueryData(\"eventSubmitDoDelete\", \"true\").render())\n\t\t\t\t\t\t\t<span class=\"ico_line\">|</span><a href=\"$removeURL\"><img src=\"images/ico_del.png\" width=\"9\" height=\"9\" /><span class=\"ico_font\">删除</span></a>\n\t\t\t\t\t\t#end\n\t\t\t\t\t#end\n\t\t\t\t</td>\n\t\t\t</tr>\n\t\t\t#end\n\t\t \n\t\t  \n        </table>\n\t\t#if($user.authorizeType.isAdmin())\n\t\t\t\t#if($flag)\n\t\t\t\t<div class=\"btn\">\n\t\t\t\t\t#set ($enableAllURL = $homeModule.setAction(\"AlarmRuleAction\").addQueryData(\"pageIndex\", $paginator.page).addQueryData(\"alarmRuleIds\", $alarmRuleIds).addQueryData(\"status\", \"enable\").addQueryData(\"eventSubmitDoStatusByRule\", \"true\").render())\n        \t\t\t<a href=\"$enableAllURL\">全部开启</a>\n\t\t\t\t</div>\n\t\t\t\t<div class=\"btn\">\n\t\t\t\t\t#set ($disableAllURL = $homeModule.setAction(\"AlarmRuleAction\").addQueryData(\"pageIndex\", $paginator.page).addQueryData(\"alarmRuleIds\", $alarmRuleIds).addQueryData(\"status\", \"disable\").addQueryData(\"eventSubmitDoStatusByRule\", \"true\").render())\n        \t\t\t<a href=\"#\" onclick=\"WdatePicker({el:'rule_all',dateFmt:'yyyy-MM-dd HH:mm:ss',minDate:'%y-%M-%d %H:%m:%s',qsEnabled:true,quickSel:['%y-%M-%d #{%H+2}:%m:%s','%y-%M-%d #{%H+6}:%m:%s','%y-%M-%d #{%H+12}:%m:%s','%y-%M-#{%d+1} %H:%m:%s','%y-%M-#{%d+2} %H:%m:%s'], onpicked:function(){url='${disableAllURL}&pauseTime=' + $dp.cal.getDateStr();window.location.href=url;}});return false;\">全部暂停<span id=\"rule_all\" style=\"display:none;\" /></a>\n\t\t\t\t</div>\n\t\t\t\t#end\n\t\t#end\t\t\n\t\t <!--分页-->\n     <div class=\"page\">共$paginator.items条数据&nbsp;&nbsp;第$paginator.page页/共$paginator.pages页&nbsp;&nbsp; \n       \n\t   #if($paginator.page == 1)\n            <font color=\"999999\">首页</font>\n\t   #else\n\t\t\t<a href=\"#\" class=\"prev\" onclick=\"pageNavigation(this,1)\">首页</a>\n\t   #end\n\t   \n\t   #if($paginator.page > 1)\n\t\t\t#set($pre_page = $paginator.page - 1)\n\t\t\t\t<a href=\"#\" class=\"prev\" onclick=\"pageNavigation(this,$pre_page)\">上一页</a>\n\t   #else\n            <font color=\"999999\">上一页</font>\n\t   #end\n\t   ##分页下标\n\t   #set($counts_keys = $paginator.getSlider(7))\n\t   #foreach( $thisPage in $counts_keys)\n\t\t\t#if( $thisPage == $paginator.page)\n                <b>$thisPage</b>\n\t\t\t#else\n\t\t\t\t#if($thisPage != 0)\n\t\t\t\t\t<a href=\"#\" class=\"num\" onclick=\"pageNavigation(this,$thisPage)\">$thisPage</a> \n\t\t\t\t#end\n\t\t\t#end\n\t   #end\n\t   \n\t   #if($paginator.page < $paginator.pages)\n\t\t\t#set($next_page = $paginator.page + 1)\n\t\t\t\t<a href=\"#\" class=\"prev\" onclick=\"pageNavigation(this,$next_page)\">下一页</a>\n\t   #else\n            <font color=\"999999\">下一页</font>\n\t   #end\n\t   \n\t   #if($paginator.page == $paginator.pages)\n            <font color=\"999999\">末页</font>\n\t   #else\n\t\t\t<a href=\"#\" class=\"prev\" onclick=\"pageNavigation(this,$paginator.pages)\">末页</a>\n\t   #end\n     </div>    \n</div>"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/templates/home/screen/analysisDelayStat.vm",
    "content": "$control.setTemplate(\"home:navigation.vm\")\r\n<script language=\"javascript\" type=\"text/javascript\" src=\"js/jquery-1.4.2.min.js\" > </script>\r\n<script language=\"javascript\" type=\"text/javascript\" src=\"js/jquery.simplemodal-1.4.js\" ></script>\r\n<script language=\"javascript\" type=\"text/javascript\" src=\"js/My97DatePicker/WdatePicker.js\" ></script>\r\n<script language=\"javascript\" type=\"text/javascript\" src=\"js/analysisStatus.js\" ></script>\r\n\r\n<script language=\"javascript\" type=\"text/javascript\" src=\"js/flot/jquery.colorhelpers.js\" > </script>\r\n<script language=\"javascript\" type=\"text/javascript\" src=\"js/flot/jquery.flot.js\" ></script>\r\n<script language=\"javascript\" type=\"text/javascript\" src=\"js/flot/jquery.flot.crosshair.js\"></script>\r\n<script language=\"javascript\" type=\"text/javascript\" src=\"js/flot/jquery.flot.fillbetween.js\"></script>\r\n<script language=\"javascript\" type=\"text/javascript\" src=\"js/flot/jquery.flot.image.js\" > </script>\r\n<script language=\"javascript\" type=\"text/javascript\" src=\"js/flot/jquery.flot.navigate.js\" ></script>\r\n<script language=\"javascript\" type=\"text/javascript\" src=\"js/flot/jquery.flot.pie.js\"></script>\r\n<script language=\"javascript\" type=\"text/javascript\" src=\"js/flot/jquery.flot.selection.js\"></script>\r\n<script language=\"javascript\" type=\"text/javascript\" src=\"js/flot/jquery.flot.stack.js\" > </script>\r\n<script language=\"javascript\" type=\"text/javascript\" src=\"js/flot/jquery.flot.symbol.js\" ></script>\r\n<script language=\"javascript\" type=\"text/javascript\" src=\"js/flot/jquery.flot.threshold.js\"></script>\r\n<script language=\"javascript\" type=\"text/javascript\" src=\"js/flot/jquery.flot.resize.js\"></script>\r\n<!--[if IE]><script language=\"javascript\" type=\"text/javascript\" src=\"js/flot/excanvas.min.js\"></script><![endif]-->\r\n<script type=\"text/javascript\">\r\n\r\n//****************0.create JVM Flash*********************************\r\n$(function () { \r\n\tvar number;\r\n\tvar time;\r\n\tvar create;\r\n\tvar flotTime=[];\r\n\t#foreach ($key in $delayStatInfos.keySet())\r\n\t\ttime=$delayStatInfos.get($key).avgDelayTime;\r\n\t\tcreate=$key;\r\n\t\tflotTime.push([create,time]);\r\n\t#end\r\n\t\r\n\t\t\t\t\r\n\tcreateDelayStatFlash(flotTime);\r\n\t\t\t\t\r\n\t\t\t\t\r\n\tfunction showTooltip(x, y, contents) {\r\n        $('<div id=\"tooltip\">' + contents + '</div>').css( {\r\n            position: 'absolute',\r\n            display: 'none',\r\n            top: y + 5,\r\n            left: x + 5,\r\n            border: '1px solid #fdd',\r\n            padding: '2px',\r\n            'background-color': '#fee',\r\n            opacity: 0.80\r\n        }).appendTo(\"body\").fadeIn(200);\r\n    };\r\n \r\n    var previousPoint = null;\r\n\t\r\n\r\nfunction showTip(id){\r\n\t$(id).bind(\"plothover\", function (event, pos, item) {\r\n            if (item) {\r\n                if (previousPoint != item.dataIndex) {\r\n                    previousPoint = item.dataIndex;\r\n                    \r\n                    $(\"#tooltip\").remove();\r\n                    var x = item.datapoint[0],\r\n                        y = strFormat(item.datapoint[1].toFixed(2));  \r\n\t\t\t\t\tvar dateTime = new Date();  \r\n                    dateTime.setTime(x);      \r\n                    var dateTimeStr = dateTime.toLocaleString();  \r\n                    dateTimeStr = dateTimeStr.replace(' ',''); \r\n\t\t\t\t\tvar context = \"(\" + dateTimeStr + \" , \" + y+  \")\";\r\n                    showTooltip(item.pageX, item.pageY,context);\r\n                }\r\n            }\r\n            else {\r\n                $(\"#tooltip\").remove();\r\n                previousPoint = null;            \r\n            }\r\n    })\r\n};\r\n\tshowTip(\"#delayTime\");\r\n});\r\n\r\n\r\n</script>\r\n\r\n<div class=\"main\">\r\n  <div class=\"title\"> \r\n    <h2>延迟时间</h2>\r\n  </div>\r\n  <div class=\"crumbs\"><a href=\"channelList.htm?channelId=$channel.id\">channel管理</a>&nbsp;&nbsp;>&nbsp;&nbsp;<a href=\"pipeline_list.htm?channelId=$channel.id\">Pipeline</a>&nbsp;&nbsp;>&nbsp;&nbsp;<a href=\"analysisDelayStat.htm?pipelineId=$pipelineId\">延迟时间</a></div>  \r\n     <!--Channel搜索-->\r\n  <div class=\"crumbs\"></div>     \r\n     <div class=\"tab\" id=\"Tab2\" >\r\n        <div class=\"menubox\">\r\n        <ul>\r\n        <li id=\"two1\"><a href=\"dataMediaPairList.htm?pipelineId=$pipelineId\">映射关系列表</a></li>\r\n           \r\n        <li id=\"two2\" ><a href=\"analysisThroughputStat.htm?pipelineId=$pipelineId\">吞吐量</a></li>\r\n        \r\n        <li id=\"two3\" class=\"tab_active\"><a href=\"analysisDelayStat.htm?pipelineId=$pipelineId\">延迟时间</a></li>\r\n           \r\n        <li id=\"two4\"><a href=\"analysisStageStat.htm?pipelineId=$pipelineId\">同步进度</a></li>  \r\n\t\t\r\n\t\t<li id=\"two5\"><a href=\"analysisThroughputHistory.htm?pipelineId=$pipelineId\">历史吞吐量</a></li>\r\n\t\t\r\n\t\t<li id=\"two6\"><a href=\"alarmRuleList.htm?pipelineId=$pipelineId\">监控管理</a></li>\r\n        \r\n\t\t<li id=\"two7\"><a href=\"logRecordTab.htm?pipelineId=$pipelineId\">日志记录</a></li>\r\n\t\t\r\n        </ul>\r\n        </div>\r\n\t\t<div class=\"contentbox_tab box_tab\">\r\n\t\t  <div class=\"search_o\" style=\"margin:5px 0 15px 15px;\" > \r\n\t\t<form name=\"search_delayStat\" method=\"post\">\r\n\t\t\t<div class=\"divInPut\">\r\n\t\t\t\t<table border=\"0\" cellspacing=\"0\" cellpadding=\"0\" style=\"margin-bottom:6px;\">\r\n\t\t\t\t\t<tr>\r\n\t\t\t\t\t<td><img src=\"images/search_global_l.png\" width=\"78\" height=\"32\" /></td>\r\n\t\t\t\t\t<td background=\"images/search_global_m.png\">\r\n                                                时间：<input name=\"d5221\" id=\"d5221\" class=\"Wdate\" type=\"text\" value=\"$!start\" onclick=\"WdatePicker({dateFmt:'yyyy-MM-dd HH:mm',maxDate:'#F{$dp.$D(\\'d5222\\')}'})\"/>\r\n                </td>                       \r\n\t\t\t\t<td background=\"images/search_global_m.png\"> 至：\r\n                  <input name=\"d5222\" id=\"d5222\" class=\"Wdate\" type=\"text\" value=\"$!end\" onclick=\"WdatePicker({dateFmt:'yyyy-MM-dd HH:mm',minDate:'#F{$dp.$D(\\'d5221\\')}'})\"/>\r\n                </td>\r\n\t\t\t\t<td><div ><a class=\"search_btn2\" href=\"javascript:document.search_delayStat.submit();\"></a></div>\r\n\t\t\t\t</td>\r\n\t\t\t\t<td><img src=\"images/search_global_r2.png\"/></td>\r\n\t\t\t   </tr>\t\t\t\r\n               </table>\r\n               </div>\t\r\n\t\t\t   </form>\r\n\t\t\t   </div>\r\n\t\t\t#*\r\n\t\t\t<fieldset style=\"border:1px #ccc solid; margin:0 0 20px 15px;width:730px;padding:25px 10px 20px 15px;\"><legend><b>堆积量（条）</b></legend>\r\n\t\t\t    <div id=\"delayNumber\" style=\"width:700px;height:250px\"></div>\r\n\t\t\t</fieldset>\r\n\t\t\t*#\r\n\t\t\t&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\r\n            \t平均延迟: <span style=\"font-weight: bold;\">$!numberFormat.formatDelay($delayAvg)</span>\r\n            <br/><br/>\r\n\t\t\t<fieldset style=\"border:1px #ccc solid; margin:0 0 20px 15px;width:730px;padding:25px 10px 20px 15px;\"><legend><b>延迟时间</b></legend>\r\n\t\t\t    <div id=\"delayTime\" style=\"width:700px;height:250px\"></div>\r\n\t\t\t</fieldset>\r\n  </div>\r\n</div>\r\n</div>"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/templates/home/screen/analysisStageStat.vm",
    "content": "$control.setTemplate(\"home:navigation.vm\")\r\n#set($user = $rundata.request.session.getAttribute(\"managerUser\"))\r\n<script language=\"javascript\">\r\n<!--\r\n\tchangeNav(\"sync\");\r\n\t\r\n\tfunction showdiv(targetid,objN){\r\n      var target=document.getElementById(targetid);\r\n      var clicktext=document.getElementById(objN)\r\n\r\n            if (target.style.display==\"block\"){\r\n                target.style.display=\"none\";\r\n                clicktext.innerText=\"查看\";\r\n  \r\n\r\n            } else {\r\n                target.style.display=\"block\";\r\n                clicktext.innerText='关闭';\r\n            }\r\n   \r\n\t}\r\n//-->\r\n</script>\r\n<div class=\"main\">\r\n  <div class=\"title\"> \r\n    <h2>同步进度</h2>\r\n  </div>\r\n  <div class=\"crumbs\"><a href=\"channelList.htm?channelId=$pipeline.channelId\">channel管理</a>&nbsp;&nbsp;>&nbsp;&nbsp;<a href=\"pipeline_list.htm?channelId=$pipeline.channelId\">Pipeline</a>&nbsp;&nbsp;>&nbsp;&nbsp;<a href=\"analysisStageStat.htm?pipelineId=$pipelineId\">同步进度</a></div>   \r\n  <div class=\"crumbs\"></div>     \r\n     <div class=\"tab\" id=\"Tab2\">\r\n        <div class=\"menubox\">\r\n        <ul>\r\n        <li id=\"two1\"><a href=\"dataMediaPairList.htm?pipelineId=$pipelineId\">映射关系列表</a></li>\r\n           \r\n        <li id=\"two2\" ><a href=\"analysisThroughputStat.htm?pipelineId=$pipelineId\">吞吐量</a></li>\r\n        \r\n        <li id=\"two3\"><a href=\"analysisDelayStat.htm?pipelineId=$pipelineId\">延迟时间</a></li>\r\n           \r\n        <li id=\"two4\" class=\"tab_active\"><a href=\"analysisStageStat.htm?pipelineId=$pipelineId\">同步进度</a></li>  \r\n\t\t\r\n\t\t<li id=\"two5\"><a href=\"analysisThroughputHistory.htm?pipelineId=$pipelineId\">历史吞吐量</a></li>\r\n\t\t\r\n\t\t<li id=\"two6\"><a href=\"alarmRuleList.htm?pipelineId=$pipelineId\">监控管理</a></li>\r\n        \r\n\t\t<li id=\"two7\"><a href=\"logRecordTab.htm?pipelineId=$pipelineId\">日志记录</a></li>\r\n\r\n        </ul>\r\n        </div>\r\n         <div class=\"contentbox_tab box_tab\"> \r\n\t\t\t<div id=\"con_two_1\" style=\"margin-bottom:10px\">\r\n\t\t\t\t<table border=\"0\" cellspacing=\"0\" cellpadding=\"0\" id=\"list\" style=\"table-layout:fixed\" onClick=\"displayLegendDiv(event);\">\r\n                     <tr align=\"left\"> \r\n\t\t\t\t\t\t<th>SELECT:</th>\r\n\t\t\t\t\t\t<th><div style=\"width:12px; height:12px; background-color:#D9B300; border:1px #D9B300 solid;margin:0 15px 0 5px;\"></div></th>\r\n\t\t\t\t\t\t<th><div style=\"width:30px\"></div></th>\r\n\t\t\t\t\t\t<th>EXTRACT:</th>\r\n\t\t\t\t\t\t<th><div style=\"width:12px; height:12px; background-color:#FFC1E0; border:1px #FFA1E0 solid;margin:0 15px 0 5px;\"></div></th>\r\n\t\t\t\t\t\t<th><div style=\"width:30px\"></div></th>\r\n\t\t\t\t\t\t<th>TRANSFORM:</th>\r\n\t\t\t\t\t\t<th><div style=\"width:12px; height:12px; background-color:#4DFFFF; border:1px #32bfe2 solid;margin:0 15px 0 5px;\"></div></th>\r\n\t\t\t\t\t\t<th><div style=\"width:30px\"></div></th>\r\n\t\t\t\t\t\t<th>LOAD:</th>\r\n\t\t\t\t\t\t<th><div style=\"width:12px; height:12px; background-color:#79FF79; border:1px #41bb4c solid;margin:0 15px 0 5px;\"></div></th>\r\n\t\t\t\t\t\t<th><div style=\"width:30px\"></div></th>\r\n\t\t\t\t\t\t<th>RUNNING:</th>\r\n\t\t\t\t\t\t<th><div style=\"width:12px; height:12px; background-color:#FF2D2D; border:1px #e78000 solid;margin:0 15px 0 5px;\"></div></th>\r\n\t\t\t\t\t\t<th><div style=\"width:30px\"></div></th>\r\n\t\t\t\t\t\t<th>END:</th>\r\n\t\t\t\t\t\t<th><div style=\"width:12px; height:12px; background-color:#2bbb2e; border:1px #013f04 solid;margin:0 15px 0 5px;\"></div></th>\r\n\t\t\t\t\t\t<th><div style=\"width:20px\"></div></th>\r\n\t\t\t\t\t\t\r\n\t\t\t\t\t </tr>\r\n\t\t\t\t\t </table>\r\n\t\t\t\t\t <table>\r\n\t\t\t\t\t <tr>\r\n\t\t\t\t\t\t<th><div style=\"height:12px;\"></div>(单位&nbsp;:&nbsp;&nbsp;毫秒)</th>\r\n\t\t\t\t\t </tr>\r\n\t\t\t\t\t </table>\r\n\t\t\t\t\t <br/>\r\n\t\t\t\t\t <table class=\"list changecolor_w\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\" style=\"table-layout:fixed ; width:40%\" onClick=\"displayLegendDiv(event);\">\r\n\t\t\t\t\t <tr>\r\n\t\t\t\t\t\t<td style=\"width:25%\">mainstem状态 : </td>\r\n\t\t\t\t\t\t<td style=\"width:72%\">\r\n                    \t\t#if(!$mainstemData)\r\n                    \t\t\t<font color=\"#FF0000\">未工作</font>\r\n                    \t\t#else\r\n                        \t\t#set ($nodeInfoURL = $homeModule.setTarget(\"nodeInfo.vm\").addQueryData(\"nodeId\", $mainstemData.nid).fork())\r\n\t\t\t\t\t\t\t\t#set ($switchURL = $homeModule.setTarget(\"analysisStageStat.vm\").addQueryData(\"pipelineId\", $pipelineId).setAction(\"switchWarmupAction\").addQueryData(\"eventSubmitDoSwitch\", \"true\").render())\r\n\t\t\t\t\t\t\t\t#set ($restartURL = $homeModule.setTarget(\"analysisStageStat.vm\").addQueryData(\"pipelineId\", $pipelineId).setAction(\"switchWarmupAction\").addQueryData(\"eventSubmitDoRestart\", \"true\").render())\r\n                        \t\t#if($mainstemData.status.isTaking())\r\n                        \t\t\t<font color=\"#FF0000\">定位中</font> ( node : $mainstemData.nid ) <span class=\"ico_line\">|</span> <a href=\"$nodeInfoURL\"><img src=\"images/ico_edit.png\" width=\"13\" height=\"13\" /><span class=\"ico_font\">查看</span></a>\r\n\t\t\t\t\t\t\t\t\t#if($user.authorizeType.isAdmin())\r\n\t\t\t\t\t\t\t\t\t<span class=\"ico_line\">|</span><a href=\"$switchURL\"><img src=\"images/ico_edit.png\" width=\"13\" height=\"13\" /><span class=\"ico_font\">切换</span></a>\r\n\t\t\t\t\t\t\t\t\t<span class=\"ico_line\">|</span><a href=\"$restartURL\"><img src=\"images/ico_edit.png\" width=\"13\" height=\"13\" /><span class=\"ico_font\">重启</span></a>\r\n\t\t\t\t\t\t\t\t\t#end\r\n                        \t\t#else\r\n                        \t\t\t工作中 ( node : $mainstemData.nid ) <span class=\"ico_line\">|</span> <a href=\"$nodeInfoURL\"><img src=\"images/ico_edit.png\" width=\"13\" height=\"13\" /><span class=\"ico_font\">查看</span></a>\r\n\t\t\t\t\t\t\t\t\t#if($user.authorizeType.isAdmin())\r\n\t\t\t\t\t\t\t\t\t<span class=\"ico_line\">|</span><a href=\"$switchURL\"><img src=\"images/ico_edit.png\" width=\"13\" height=\"13\" /><span class=\"ico_font\">切换</span></a>\r\n\t\t\t\t\t\t\t\t\t<span class=\"ico_line\">|</span><a href=\"$restartURL\"><img src=\"images/ico_edit.png\" width=\"13\" height=\"13\" /><span class=\"ico_font\">重启</span></a>\r\n\t\t\t\t\t\t\t\t\t#end\r\n                        \t\t#end\r\n    \t\t\t\t\t\t#end\r\n\t\t\t\t\t\t</b></td>\r\n\t\t\t\t\t </tr>\r\n\t\t\t\t\t <tr>\r\n\t\t\t\t\t\t<td style=\"width:25%\">position 状态 : </td>\r\n\t\t\t\t\t\t<td style=\"width:72%\">\r\n                    \t\t#if(!$positionData)\r\n                    \t\t\t<font color=\"#FF0000\">无信息</font>\r\n                    \t\t#else\r\n\t\t\t\t\t\t\t\t#set ($removeURL = $homeModule.setTarget(\"analysisStageStat.vm\").addQueryData(\"pipelineId\", $pipelineId).setAction(\"PositionAction\").addQueryData(\"eventSubmitDoRemove\", \"true\").render())\r\n                    \t\t\t最后更新时间 : $!numberFormat.format($positionData.modifiedTime) <span class=\"ico_line\">|</span><img src=\"images/ico_edit.png\" width=\"13\" height=\"13\" /><a id=\"showtext-position\" onClick=\"showdiv('contentid-position','showtext-position')\"><span class=\"ico_font\">查看</span></a>\r\n\t\t\t\t\t\t\t\t#if($user.authorizeType.isAdmin())\r\n    \t\t\t\t\t\t\t\t#if($channelStatus.isStop()) \r\n    \t\t\t\t\t\t\t\t\t<span class=\"ico_line\">|</span><a href=\"javascript:if(confirm('确实要删除吗?'))location='$removeURL'\"><img src=\"images/ico_del.png\" width=\"13\" height=\"13\" /><span class=\"ico_font\">删除</span></a>\r\n    \t\t\t\t\t\t\t\t#else\r\n    \t\t\t\t\t\t\t\t\t<span class=\"ico_line\">|</span><img src=\"images/ico_del.png\" width=\"13\" height=\"13\" /><span class=\"ico_font\" title=\"需要先停用channel\">删除</span></a>\r\n    \t\t\t\t\t\t\t\t#end\r\n\t\t\t\t\t\t\t\t#end\r\n    \t\t\t\t\t\t#end\r\n\t\t\t\t\t\t</b></td>\r\n\t\t\t\t\t </tr>\r\n\t\t\t\t\t <tr>\r\n\t\t\t\t\t\t<td width=\"100%\" colspan=\"2\" class=\"message\">\r\n\t\t\t\t\t\t\t<div id=\"contentid-position\" class=\"contentid\" style=\"display:none\">#noescape() $!positionData.position #end</div>\r\n\t\t\t\t\t\t</td>\r\n\t\t\t\t\t </tr>\r\n\t\t\t\t\t </table>\r\n\t\t#foreach ($processStat in $processStats)\r\n\t\t\t<table border=\"0\" cellspacing=\"0\" cellpadding=\"0\" id=\"list\" style=\"margin-top:16px;table-layout:fixed;\" onClick=\"displayLegendDiv(event);\">\r\n\t\t  <tr align=\"left\" style=\"background-color:#eee;\"> \r\n\t\t\t<td style=\"width:75px;height:30px;padding:0px 0px 0px 0px;text-align:center\">$processStat.processId</td>\r\n\t\t\t<td style=\"width:1px;height:60px;background-color:#ddd;padding:0px 0px 0px 0px;\"></td>\r\n\t\t\t#foreach($stageStat in $processStat.stageStats)\r\n\t\t\t\t#set($temp=$velocityCount)\r\n    \t\t\t#set($context = \"\")\r\n\t\t\t\t#if($stageStat.number)\r\n\t\t\t\t\t#set($context=$!{context} + \"number:\" + $!numberFormat.format($stageStat.number) +\"&#10;\")\r\n\t\t\t\t#end\r\n\t\t\t\t\r\n\t\t\t\t#if($stageStat.size)\r\n\t\t\t\t\t#set($context=$!{context} + \"size:\" + $!numberFormat.format($stageStat.size)+\"&#10;\")\r\n\t\t\t\t#end\r\n\t\t\t\t\r\n\t\t\t\t#if($stageStat.exts && $stageStat.exts.size() > 0)\r\n\t\t\t\t\t#set($context=$!{context} + \"exts:&#10;\")\r\n\t\t\t\t\t#foreach($key in $stageStat.exts.keySet())\r\n\t\t\t\t\t\t#if($key == \"desc\")\r\n\t\t\t\t\t\t\t#set($descExts = $!{stageStat.exts.get($key)})\r\n\t\t\t\t\t\t\t#foreach($descIter in $descExts)\r\n\t\t\t\t\t\t\t\t#foreach($descIter in $descExts)\r\n\t\t\t\t\t\t\t\t\t#foreach($deskey in $!{descIter.keySet()})\r\n\t\t\t\t\t\t\t\t\t\t#set($value = \"$descIter.get($deskey)\")\r\n        \t\t\t\t\t\t\t\t#set($context = $!{context} + \"&#09; \"+ $!{deskey} + \":\" + $stringUtil.replace($value , '\"',\"'\") + \"&#10;\")\r\n\t\t\t\t\t\t\t\t\t#end\r\n\t\t\t\t\t\t\t\t#end\r\n\t\t\t\t\t\t\t#end\r\n\t\t\t\t\t\t#else\r\n    \t\t\t\t\t\t#set($context = $!{context} + \"&#09; \"+ $!{key} + \":\" + $!{stageStat.exts.get($key)} +\"&#10;\")\r\n\t\t\t\t\t\t#end\r\n\t\t\t\t\t#end\r\n\t\t\t\t#end\r\n\t\t\t\t\r\n\t\t\t\t#if(!$stringUtil.isEmpty($!context))\r\n\t\t\t\t\t#set($context = $stringUtil.substringBeforeLast($context , \"&#10;\"))\r\n\t\t\t\t#end\r\n\t\t\t\r\n\t\t\t\t#if($temp==1)\r\n\t\t\t\t\t#if($stageStat.startTime)\r\n\t\t\t\t\t\t#set($time = $stageStat.startTime - $stageStart)\r\n\t\t\t\t\t    #set($width = $time * $offset)\r\n\t\t\t\t\t    #if($width>0)\r\n\t\t\t\t\t\t\t#if($width<1)\r\n\t\t\t\t\t\t\t\t#set($width=1)\r\n\t\t\t\t\t\t\t#end\r\n\t\t\t\t\t\t\t<td style=\"width:${width}px;height:60px;padding:0px 0px 0px 0px;\">$!numberFormat.format($time)</td>\r\n\t\t\t\t\t\t#end\r\n\t\t\t\t\t\t#if($stageStat.endTime)\r\n\t\t\t\t\t\t\t#set($time = $stageStat.endTime - $stageStat.startTime)\r\n\t\t\t\t\t\t\t#set($width = $time * $offset)\r\n\t\t\t\t\t\t\t#if($width<1)\r\n\t\t\t\t\t\t\t\t#set($width=1)\r\n\t\t\t\t\t\t\t#end\r\n\t\t\t\t\t\t\t<td id =\"select\" style=\"width:${width}px;height:60px;background-color:#D9B300;padding:0px 0px 0px 0px;\" title=\"#noescape()$!context#end\">$!numberFormat.format($time)</td>\r\n\t\t\t\t\t\t#else\r\n\t\t\t\t\t\t\t#set($time = $stageEnd - $stageStat.startTime)\r\n\t\t\t\t\t\t\t#set($width = $time * $offset)\r\n\t\t\t\t\t\t\t#if($width<1)\r\n\t\t\t\t\t\t\t\t#set($width=1)\r\n\t\t\t\t\t\t\t#end\r\n\t\t\t\t\t\t\t<td style=\"width:${width}px;height:60px;background-color:#FF2D2D;padding:0px 0px 0px 0px;\" title=\"#noescape()$!context#end\">$!numberFormat.format($time)</td>\r\n\t\t\t\t\t\t#end\r\n\t\t\t\t\t#end\t\t\t\t\t\r\n\t\t\t\t#end\r\n\t\t\t    #if($temp==2)\r\n\t\t\t\t\t#if($stageStat.startTime)\r\n\t\t\t\t\t\t#set($time = $stageStat.startTime - $preStageStat.endTime)\r\n\t\t\t\t\t\t#set($width = $time * $offset)\r\n\t\t\t\t\t\t#if($width>0)\r\n\t\t\t\t\t\t\t#if($width<1)\r\n\t\t\t\t\t\t\t\t#set($width=1)\r\n\t\t\t\t\t\t\t#end\r\n\t\t\t\t\t\t\t<td style=\"width:${width}px;height:60px;padding:0px 0px 0px 0px;\">$!numberFormat.format($time)</td>\r\n\t\t\t\t\t\t#end\r\n\t\t\t\t\t\t#if($stageStat.endTime)\r\n\t\t\t\t\t\t\t#set($time = $stageStat.endTime - $stageStat.startTime)\r\n\t\t\t\t\t\t\t#set($width = $time * $offset)\r\n\t\t\t\t\t\t\t#if($width<1)\r\n\t\t\t\t\t\t\t\t#set($width=1)\r\n\t\t\t\t\t\t\t#end\r\n\t\t\t\t\t\t\t<td style=\"width:${width}px;height:60px;background-color:#FFC1E0;padding:0px 0px 0px 0px;\" title=\"#noescape()$!context#end\">$!numberFormat.format($time)</td>\r\n\t\t\t\t\t\t#else\r\n\t\t\t\t\t\t\t#set($time = $stageEnd - $stageStat.startTime)\r\n\t\t\t\t\t\t\t#set($width = $time * $offset)\r\n\t\t\t\t\t\t\t#if($width<1)\r\n\t\t\t\t\t\t\t\t#set($width=1)\r\n\t\t\t\t\t\t\t#end\r\n\t\t\t\t\t\t\t<td style=\"width:${width}px;height:60px;background-color:#FF2D2D;padding:0px 0px 0px 0px;\" title=\"#noescape()$!context#end\">$!numberFormat.format($time)</td>\r\n\t\t\t\t\t\t#end\r\n\t\t\t\t\t#end\r\n\t\t\t\t#end\r\n\t\t\t\t#if($temp==3)\r\n\t\t\t\t\t#if($stageStat.startTime)\r\n\t\t\t\t\t\t#set($time = $stageStat.startTime - $preStageStat.endTime)\r\n\t\t\t\t\t\t#set($width = $time * $offset)\r\n\t\t\t\t\t\t#if($width>0)\r\n\t\t\t\t\t\t\t#if($width<1)\r\n\t\t\t\t\t\t\t\t#set($width=1)\r\n\t\t\t\t\t\t\t#end\r\n\t\t\t\t\t\t\t<td style=\"width:${width}px;height:60px;padding:0px 0px 0px 0px;\">$!numberFormat.format($time)</td>\r\n\t\t\t\t\t\t#end \r\n\t\t\t\t\t\t#if($stageStat.endTime)\r\n\t\t\t\t\t\t\t#set($time = $stageStat.endTime - $stageStat.startTime)\r\n\t\t\t\t\t\t\t#set($width = $time * $offset)\r\n\t\t\t\t\t\t\t#if($width<1)\r\n\t\t\t\t\t\t\t\t#set($width=1)\r\n\t\t\t\t\t\t\t#end\r\n\t\t\t\t\t\t\t<td style=\"width:${width}px;height:60px;background-color:#4DFFFF;padding:0px 0px 0px 0px;\" title=\"#noescape()$!context#end\">$!numberFormat.format($time)</td>\r\n\t\t\t\t\t\t#else\r\n\t\t\t\t\t\t\t#set($time = $stageEnd - $stageStat.startTime)\r\n\t\t\t\t\t\t\t#set($width = $time * $offset)\r\n\t\t\t\t\t\t\t#if($width<1)\r\n\t\t\t\t\t\t\t\t#set($width=1)\r\n\t\t\t\t\t\t\t#end\r\n\t\t\t\t\t\t\t<td style=\"width:${width}px;height:60px;background-color:#FF2D2D;padding:0px 0px 0px 0px;\" title=\"#noescape()$!context#end\">$!numberFormat.format($time)</td>\r\n\t\t\t\t\t\t#end\r\n\t\t\t\t\t#end\r\n\t\t\t\t#end\r\n\t\t\t\t#if($temp==4)\r\n\t\t\t\t\t#if($stageStat.startTime)\r\n\t\t\t\t\t\t#set($time = $stageStat.startTime - $preStageStat.endTime)\r\n\t\t\t\t\t\t#set($width = $time * $offset)\r\n\t\t\t\t\t\t#if($width>0)\r\n\t\t\t\t\t\t\t#if($width<1)\r\n\t\t\t\t\t\t\t\t#set($width=1)\r\n\t\t\t\t\t\t\t#end\r\n\t\t\t\t\t\t\t<td style=\"width:${width}px;height:60px;padding:0px 0px 0px 0px;\">$!numberFormat.format($time)</td>\r\n\t\t\t\t\t\t#end\r\n\t\t\t\t\t\t#if($stageStat.endTime)\r\n\t\t\t\t\t\t\t#set($time = $stageStat.endTime - $stageStat.startTime)\r\n\t\t\t\t\t\t\t#set($width = $time * $offset)\r\n\t\t\t\t\t\t\t#if($width<1)\r\n\t\t\t\t\t\t\t\t#set($width=1)\r\n\t\t\t\t\t\t\t#end\r\n\t\t\t\t\t\t\t<td style=\"width:${width}px;height:60px;background-color:#79FF79;padding:0px 0px 0px 0px;\" title=\"#noescape()$!context#end\">$!numberFormat.format($time)</td>\r\n\t\t\t\t\t\t\t<td style=\"width:10px;height:60px;background-color:#2bbb2e;padding:0px 0px 0px 0px;\"></td>\r\n\t\t\t\t\t\t#else\r\n\t\t\t\t\t\t\t#set($time = $stageEnd - $stageStat.startTime)\r\n\t\t\t\t\t\t\t#set($width = $time * $offset)\r\n\t\t\t\t\t\t\t#if($width<1)\r\n\t\t\t\t\t\t\t\t#set($width=1)\r\n\t\t\t\t\t\t\t#end\r\n\t\t\t\t\t\t\t<td style=\"width:${width}px;height:60px;background-color:#FF2D2D;padding:0px 0px 0px 0px;\" title=\"#noescape()$!context#end\">$!numberFormat.format($time)</td>\r\n\t\t\t\t\t\t#end\r\n\t\t\t\t\t#end\r\n\t\t\t\t#end\r\n\t\t\t\t#set ($preStageStat = $stageStat)\r\n\t\t\t#end\r\n\t\t\t<td><b>&nbsp;&nbsp;&nbsp;总时间:$!numberFormat.format($processTime.get($processStat.processId))</b></td>\r\n          </tr>\r\n\t\t   </table>\r\n\t\t#end\r\n\t\t \r\n      </div>    \r\n    </div>\r\n  </div>\r\n</div>"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/templates/home/screen/analysisThroughputHistory.vm",
    "content": "$control.setTemplate(\"home:navigation.vm\")\r\n<script language=\"javascript\" type=\"text/javascript\" src=\"js/jquery-1.4.2.min.js\" > </script>\r\n<script language=\"javascript\" type=\"text/javascript\" src=\"js/jquery.simplemodal-1.4.js\" ></script>\r\n<script language=\"javascript\" type=\"text/javascript\" src=\"js/My97DatePicker/WdatePicker.js\" ></script>\r\n<script language=\"javascript\" type=\"text/javascript\" src=\"js/analysisStatus.js\" ></script>\r\n\r\n<script language=\"javascript\" type=\"text/javascript\" src=\"js/flot/jquery.colorhelpers.js\" > </script>\r\n<script language=\"javascript\" type=\"text/javascript\" src=\"js/flot/jquery.flot.js\" ></script>\r\n<script language=\"javascript\" type=\"text/javascript\" src=\"js/flot/jquery.flot.crosshair.js\"></script>\r\n<script language=\"javascript\" type=\"text/javascript\" src=\"js/flot/jquery.flot.fillbetween.js\"></script>\r\n<script language=\"javascript\" type=\"text/javascript\" src=\"js/flot/jquery.flot.image.js\" > </script>\r\n<script language=\"javascript\" type=\"text/javascript\" src=\"js/flot/jquery.flot.navigate.js\" ></script>\r\n<script language=\"javascript\" type=\"text/javascript\" src=\"js/flot/jquery.flot.pie.js\"></script>\r\n<script language=\"javascript\" type=\"text/javascript\" src=\"js/flot/jquery.flot.selection.js\"></script>\r\n<script language=\"javascript\" type=\"text/javascript\" src=\"js/flot/jquery.flot.stack.js\" > </script>\r\n<script language=\"javascript\" type=\"text/javascript\" src=\"js/flot/jquery.flot.symbol.js\" ></script>\r\n<script language=\"javascript\" type=\"text/javascript\" src=\"js/flot/jquery.flot.threshold.js\"></script>\r\n<script language=\"javascript\" type=\"text/javascript\" src=\"js/flot/jquery.flot.resize.js\"></script>\r\n<!--[if IE]><script language=\"javascript\" type=\"text/javascript\" src=\"js/flot/excanvas.min.js\"></script><![endif]-->\r\n<script type=\"text/javascript\">\r\n\r\n//****************0.create JVM Flash*********************************\r\n$(function () { \r\n\r\n\tvar historyNumber;\r\n\tvar historySize;\r\n\tvar time;\r\n\tvar flotNumber1=[];\r\n\tvar flotSize1=[];\r\n\tvar flotNumber2=[];\r\n\tvar flotSize2=[];\r\n\t#foreach ($key in $throughputInfos1.keySet())\r\n\t\thistoryNumber=$throughputInfos1.get($key).number;\r\n\t\thistorySize=Math.round($throughputInfos1.get($key).size / 1024);\r\n\t\ttime=$key;\r\n\t\tflotNumber1.push([time,historyNumber]);\r\n\t\tflotSize1.push([time,historySize]);\r\n\t#end\r\n\r\n    #foreach ($key in $throughputInfos2.keySet())\r\n\t\thistoryNumber=$throughputInfos2.get($key).Number;\r\n\t\thistorySize=Math.round($throughputInfos2.get($key).size / 1024);\r\n\t\ttime=$key;\r\n\t\tflotNumber2.push([time,historyNumber]);\r\n\t\tflotSize2.push([time,historySize]);\r\n\t#end\r\n\r\n\t\t\t\t\r\n\tcreateRowThroughputFlash(flotNumber1,flotSize1);\r\n\tcreateFileThroughputFlash(flotNumber2,flotSize2);\r\n\t\t\t\t\r\n\t\t\t\t\r\n\tfunction showTooltip(x, y, contents) {\r\n        $('<div id=\"tooltip\">' + contents + '</div>').css( {\r\n            position: 'absolute',\r\n            display: 'none',\r\n            top: y + 5,\r\n            left: x + 5,\r\n            border: '1px solid #fdd',\r\n            padding: '2px',\r\n            'background-color': '#fee',\r\n            opacity: 0.80\r\n        }).appendTo(\"body\").fadeIn(200);\r\n    };\r\n \r\n    var previousPoint = null;\r\n\r\nfunction showTip(id) {\t\r\n    $(id).bind(\"plothover\", function (event, pos, item) {\r\n            if (item) {\r\n                if (previousPoint != item.dataIndex) {\r\n                    previousPoint = item.dataIndex;\r\n                    \r\n                    $(\"#tooltip\").remove();\r\n                    var x = item.datapoint[0],\r\n                        y = strFormat(item.datapoint[1].toFixed(0));                  \r\n                    var dateTime = new Date();  \r\n                    dateTime.setTime(x);      \r\n                    var dateTimeStr = dateTime.toLocaleString();  \r\n                    dateTimeStr = dateTimeStr.replace(' ',''); \r\n\t\t\t\t\tvar context =\"(\" + dateTimeStr + \" , \" + y+  \")\";\r\n                    showTooltip(item.pageX, item.pageY,context);\r\n                }\r\n            }\r\n            else {\r\n                $(\"#tooltip\").remove();\r\n                previousPoint = null;            \r\n            }\r\n    })\r\n};\r\n\r\n    showTip(\"#rowNumber\");\r\n\tshowTip(\"#rowSize\");\r\n\tshowTip(\"#fileNumber\");\r\n\tshowTip(\"#fileSize\");\r\n\t\r\n\t$(\"#tabid1\").show();\r\n\t$(\"#tabid2\").hide();\r\n\t\r\n\t\r\n\r\n});\r\n\r\n  var lastTabId = 1;\r\n  function changeTab(n){\r\n      var tabnav = \"tab\"+ n;\r\n      var tabid = \"tabid\"+ n;\r\n      if(lastTabId != n){\r\n\t\tdocument.getElementById(\"tabid\" + lastTabId).style.display = \"none\";\r\n\t\tvar element = document.getElementById(\"tab\"+ lastTabId);\r\n\t\telement.removeAttribute(\"class\"); //for firefox\r\n\t\telement.removeAttribute(\"className\"); //for IE\r\n\t  \r\n        lastTabId = n;\r\n    }\r\n\t\r\n\tdocument.getElementById(tabid).style.display=\"block\";\r\n\t\r\n\tvar element = document.getElementById(tabnav);\r\n\telement.setAttribute(\"class\",\"tab_active\"); //for firefox\r\n\telement.setAttribute(\"className\",\"tab_active\"); //for IE\r\n  };\r\n\r\n</script>\r\n\r\n<div class=\"main\">\r\n  <div class=\"title\"> \r\n    <h2>历史吞吐量</h2>\r\n  </div>\r\n  <div class=\"crumbs\"><a href=\"channelList.htm?channelId=$channel.id\">channel管理</a>&nbsp;&nbsp;>&nbsp;&nbsp;<a href=\"pipeline_list.htm?channelId=$channel.id\">Pipeline</a>&nbsp;&nbsp;>&nbsp;&nbsp;<a href=\"analysisThroughputHistory.htm?pipelineId=$pipelineId\">历史吞吐量</a></div>  \r\n     <!--Channel搜索-->\r\n  <div class=\"crumbs\"></div>     \r\n     <div class=\"tab\" id=\"Tab2\" >\r\n        <div class=\"menubox\">\r\n        <ul>\r\n        <li id=\"two1\"><a href=\"dataMediaPairList.htm?pipelineId=$pipelineId\">映射关系列表</a></li>\r\n           \r\n        <li id=\"two2\"><a href=\"analysisThroughputStat.htm?pipelineId=$pipelineId\">吞吐量</a></li>\r\n        \r\n        <li id=\"two3\"><a href=\"analysisDelayStat.htm?pipelineId=$pipelineId\">延迟时间</a></li>\r\n           \r\n        <li id=\"two4\"><a href=\"analysisStageStat.htm?pipelineId=$pipelineId\">同步进度</a></li>  \r\n\t\t\r\n\t\t<li id=\"two5\" class=\"tab_active\"><a href=\"analysisThroughputHistory.htm?pipelineId=$pipelineId\">历史吞吐量</a></li>\r\n\t\t\r\n\t\t<li id=\"two6\"><a href=\"alarmRuleList.htm?pipelineId=$pipelineId\">监控管理</a></li>\r\n        \r\n\t\t<li id=\"two7\"><a href=\"logRecordTab.htm?pipelineId=$pipelineId\">日志记录</a></li>\r\n\t\t\r\n        </ul>\r\n        </div>\r\n\t\t<div class=\"menubox\" style=\"margin:15px 0 0 0;\">\r\n        <ul>\r\n        <li id=\"tab1\" onclick=\"changeTab(1)\">ROW类型</li>\r\n           \r\n        <li id=\"tab2\" onclick=\"changeTab(2)\">FILE类型</li>\r\n                             \r\n        </ul>\r\n        </div>\r\n\t\t<div class=\"contentbox_tab box_tab\">\r\n\t\t  <div class=\"search_o\" style=\"margin:5px 0 15px 15px;\" > \r\n\t\t<form name=\"search_throughput\" method=\"post\">\r\n\t\t\t<input type=\"hidden\" name=\"pipelineId\" value=\"$pipelineId\"/>\r\n\t\t\t<div class=\"divInPut\">\r\n\t\t\t\t<table border=\"0\" cellspacing=\"0\" cellpadding=\"0\" style=\"margin-bottom:6px;\">\r\n\t\t\t\t\t<tr>\r\n\t\t\t\t\t<td><img src=\"images/search_global_l.png\" width=\"78\" height=\"32\" /></td>\r\n\t\t\t\t\t<td background=\"images/search_global_m.png\">\r\n                                                时间：<input name=\"d5221\" id=\"d5221\" class=\"Wdate\" type=\"text\" value=\"$!start\" onclick=\"WdatePicker({dateFmt:'yyyy-MM-dd HH:mm',maxDate:'#F{$dp.$D(\\'d5222\\')}'})\"/>\r\n                </td>                       \r\n\t\t\t\t<td background=\"images/search_global_m.png\"> 至：\r\n                  <input name=\"d5222\" id=\"d5222\" class=\"Wdate\" type=\"text\" value=\"$!end\" onclick=\"WdatePicker({dateFmt:'yyyy-MM-dd HH:mm',minDate:'#F{$dp.$D(\\'d5221\\')}',minDate:'#F{$dp.$D(\\'d5221\\')}'})\"/>\r\n                </td>\r\n\t\t\t\t<td><div ><a class=\"search_btn2\" href=\"javascript:document.search_throughput.submit();\"></a></div>\r\n\t\t\t\t</td>\r\n\t\t\t\t<td><img src=\"images/search_global_r2.png\"/></td>\r\n\t\t\t   </tr>\t\t\t\r\n               </table>\r\n               </div>\t\r\n\t\t\t   </form>\r\n\t\t\t   </div>\r\n\t\t\t   \r\n\t\t\t<div id=\"tabid1\" style=\"clear:both;\">\r\n\t\t\t&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\r\n            \t总记录数: <span style=\"font-weight: bold;\">$!numberFormat.format($totalRecord1)</span> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\r\n            \t总大小:<span style=\"font-weight: bold;\">$!numberFormat.formatFileSize($totalSize1)</span>\r\n            <br/><br/>\r\n\t\t\t<fieldset id=\"Number1\" style=\"border:1px #ccc solid; margin:0 0 20px 15px;width:730px;padding:25px 10px 20px 15px;\"><legend><b>记录(条)</b></legend>\r\n\t\t\t    <div id=\"rowNumber\" style=\"width:700px;height:250px\"></div>\r\n\t\t\t</fieldset>\r\n\t\t    <fieldset id=\"Size1\" style=\"border:1px #ccc solid; margin:0 0 20px 15px;width:730px;padding:25px 10px 20px 15px;\"><legend><b>size(KB)</b></legend>\r\n\t\t\t    <div id=\"rowSize\" style=\"width:700px;height:250px\"></div>\r\n\t\t\t</fieldset>\r\n\t\t\t</div>\r\n\t\t\t<div id=\"tabid2\" style=\"clear:both;\">\r\n\t\t\t\t&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\r\n            \t总记录数: <span style=\"font-weight: bold;\">$!numberFormat.format($totalRecord2)</span> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\r\n            \t总大小:<span style=\"font-weight: bold;\">$!numberFormat.formatFileSize($totalSize2)</span>\r\n            <br/><br/>\r\n\t\t\t<fieldset id=\"Number2\" style=\"border:1px #ccc solid; margin:0 0 20px 15px;width:730px;padding:25px 10px 20px 15px;\"><legend><b>文件(个)</b></legend>\r\n\t\t\t    <div id=\"fileNumber\" style=\"width:700px;height:250px\"></div>\r\n\t\t\t</fieldset>\r\n\t\t    <fieldset id=\"Size2\" style=\"border:1px #ccc solid; margin:0 0 20px 15px;width:730px;padding:25px 10px 20px 15px;\"><legend><b>size(KB)</b></legend>\r\n\t\t\t    <div id=\"fileSize\" style=\"width:700px;height:250px\"></div>\r\n\t\t\t</fieldset>\r\n\t\t\t</div>\r\n  </div>\r\n</div>\r\n</div>\r\n\t\r\n<script type=\"text/javascript\">\r\n  changeTab(1);\r\n</script>"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/templates/home/screen/analysisThroughputStat.vm",
    "content": "\r\n<script type=\"text/javascript\" src=\"js/trcolor.js\"></script>\r\n<script type=\"text/javascript\" src=\"js/jquery-1.4.2.min.js\"></script> \r\n\r\n$control.setTemplate(\"home:navigation.vm\")\r\n<script language=\"javascript\">\r\n<!--\r\n\tchangeNav(\"sync\");\r\n//-->\r\n</script>\r\n\r\n<div class=\"main\">\r\n  <div class=\"title\"> \r\n    <h2>吞吐量</h2>\r\n  </div>\r\n  <div class=\"crumbs\"><a href=\"channelList.htm?channelId=$channel.id\">channel管理</a>&nbsp;&nbsp;>&nbsp;&nbsp;<a href=\"pipeline_list.htm?channelId=$channel.id\">Pipeline</a>&nbsp;&nbsp;>&nbsp;&nbsp;<a href=\"analysisThroughputStat.htm?pipelineId=$pipelineId\">吞吐量</a></div>\r\n<div class=\"crumbs\"></div>     \r\n     <div class=\"tab\" id=\"Tab2\">\r\n        <div class=\"menubox\">\r\n        <ul>\r\n        <li id=\"two1\"><a href=\"dataMediaPairList.htm?pipelineId=$pipelineId\">映射关系列表</a></li>\r\n           \r\n        <li id=\"two2\" class=\"tab_active\"><a href=\"analysisThroughputStat.htm?pipelineId=$pipelineId\">吞吐量</a></li>\r\n        \r\n        <li id=\"two3\"><a href=\"analysisDelayStat.htm?pipelineId=$pipelineId\">延迟时间</a></li>\r\n           \r\n        <li id=\"two4\"><a href=\"analysisStageStat.htm?pipelineId=$pipelineId\">同步进度</a></li>   \r\n\t\t\r\n\t\t<li id=\"two5\"><a href=\"analysisThroughputHistory.htm?pipelineId=$pipelineId\">历史吞吐量</a></li>\r\n\t\t\r\n\t\t<li id=\"two6\"><a href=\"alarmRuleList.htm?pipelineId=$pipelineId\">监控管理</a></li>\r\n        \r\n\t\t<li id=\"two7\"><a href=\"logRecordTab.htm?pipelineId=$pipelineId\">日志记录</a></li>\r\n\t\t\r\n\t\t</ul>\r\n        </div>\r\n         <div class=\"contentbox_tab box_tab\">  \r\n           <div id=\"con_two_1\">\r\n<!--列表-->\r\n     \r\n        <table border=\"0\" cellspacing=\"0\" cellpadding=\"0\" class=\"list2 changecolor_g\">\r\n          <tr> \r\n            <th width=\"6%\">PipelineId</th>\r\n            <th width=\"4%\">类型</th>\r\n            <th width=\"11%\">TPS<span class=\"STYLE3\">&nbsp;(1分钟)</span></th>\r\n            <th width=\"11%\">TPS<span class=\"STYLE3\">&nbsp;(5分钟)</span></th>\r\n            <th width=\"11%\">TPS<span class=\"STYLE3\">&nbsp;(15分钟)</span></th>\r\n            <th width=\"11%\">吞吐量<span class=\"STYLE3\">&nbsp;(1分钟)</span></th>\r\n            <th width=\"11%\">吞吐量<span class=\"STYLE3\">&nbsp;(5分钟)</span></th>\r\n            <th width=\"11%\">吞吐量<span class=\"STYLE3\">&nbsp;(15分钟)</span></th>\r\n\t\t\t<th width=\"20%\">最后同步时间</th>\r\n          </tr>\r\n          <tr> \r\n            <td width=\"6%\">$pipelineId</td>\r\n            <td width=\"4%\">FILE </td>\r\n            <td>$!numberFormat.format($throughputInfos1.get($one).Tps)</td>\r\n            <td>$!numberFormat.format($throughputInfos1.get($five).Tps)</td>\r\n            <td>$!numberFormat.format($throughputInfos1.get($fifteen).Tps)</td>\r\n            <td>$!numberFormat.formatFileSize($throughputInfos1.get($one).Quantity)</td>\r\n            <td>$!numberFormat.formatFileSize($throughputInfos1.get($five).Quantity)</span></td>\r\n            <td>$!numberFormat.formatFileSize($throughputInfos1.get($fifteen).Quantity)</td>\r\n            <td>$!numberFormat.format($!throughputStat1.gmtModified)</td>\r\n          </tr>\r\n          <tr> \r\n            <td width=\"6%\">$pipelineId</td>\r\n            <td width=\"4%\">ROW </td>\r\n            <td>$!numberFormat.format($throughputInfos2.get($one).Tps)</td>\r\n            <td>$!numberFormat.format($throughputInfos2.get($five).Tps)</td>\r\n            <td>$!numberFormat.format($throughputInfos2.get($fifteen).Tps)</td>\r\n            <td>$!numberFormat.formatFileSize($throughputInfos2.get($one).Quantity)</td>\r\n            <td>$!numberFormat.formatFileSize($throughputInfos2.get($five).Quantity)</td>\r\n            <td>$!numberFormat.formatFileSize($throughputInfos2.get($fifteen).Quantity)</td>\r\n            <td>$!numberFormat.format($!throughputStat2.gmtModified)</td>\r\n          </tr>\r\n        </table>\r\n             \r\n                 \r\n    </div>\r\n  </div>\r\n</div>\r\n</div>"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/templates/home/screen/analysisTopStat.vm",
    "content": "$control.setTemplate(\"home:navigation.vm\")\r\n#set($user = $rundata.request.session.getAttribute(\"managerUser\"))\r\n<script type=\"text/javascript\" src=\"js/trcolor.js\"></script>\r\n<script type=\"text/javascript\" src=\"js/jquery-1.4.2.min.js\"></script> \r\n<script language=\"javascript\">\r\n    <!--\r\n\tchangeNav(\"record\");\r\n    //-->\r\n</script>\r\n<style type=\"text/css\">\r\n<!--\r\n\r\na { text-decoration: none; }\r\n\r\n.showtext { cursor: hand; cursor:pointer;}\r\n.contentid { margin-top: 10px; margin-bottom: 10px; width: 400px; border: 1px solid #CCC; background: #F1F1F1; padding: 15px; display: none; }\r\n-->\r\n</style>\r\n\r\n<!--页面主体-->\r\n<div class=\"main\">\r\n  <div class=\"title\"> \r\n    <h2>同步TOPN</h2>\r\n  </div>\r\n   \r\n   <!--Node搜索-->\r\n   <div class=\"search_o\"> \r\n\t\t<form name=\"search_node\" action=\"analysis_top_stat.htm\"  method=\"post\">\r\n\t\t\t##$csrfToken.hiddenField\r\n\t\t\t<div class=\"search_input\">\r\n\t\t\t\t<input name=\"searchKey\" type=\"text\" value=\"请输入关键字(目前channel名字搜索)\"  onfocus=\"if(this.value == '请输入关键字(目前channel名字搜索)') {this.value='';}\" onblur=\"if(this.value == '') {this.value = '请输入关键字(目前channel名字搜索)';}\" />\r\n\t\t\t</div>\r\n\t\t\t<div class=\"search_btn\"><a href=\"javascript:document.search_node.submit();\"><img src=\"images/search_btn.png\" width=\"39\" height=\"31\" /></a></div>\r\n        </form>\r\n   </div>\r\n      \r\n   <!--列表-->\r\n  <table border=\"0\" cellspacing=\"0\" cellpadding=\"0\" class=\"list changecolor_w\">\r\n          <tr> \r\n            <th width=\"5%\">TOP</th>\r\n            <th width=\"12%\">Channel名字</th>\r\n\t\t\t<th width=\"10%\">Pipeline信息</th>\r\n\t\t\t<th width=\"9%\">延迟时间</th>\r\n\t\t\t<th width=\"9%\">最后采集间隔</th>\r\n\t\t\t<th width=\"9%\">DB数量(${statTime}分种)</th>\r\n\t\t\t<th width=\"9%\">DB大小(${statTime}分种)</th>\r\n\t\t\t<th width=\"9%\">文件数量(${statTime}分种)</th>\r\n\t\t\t<th width=\"9%\">文件大小(${statTime}分种)</th>\r\n\t\t\t<th width=\"9%\">channel状态</th>\r\n\t\t\t<th width=\"9%\">pipeline状态</th>\r\n          </tr>\r\n\t\t  #foreach ($top in $tops)\r\n\t\t   <tr> \r\n             <td width=\"3%\">$velocityCount</td>\r\n\t\t\t <td width=\"10%\">\r\n\t\t\t\t#set ($channelURL = $homeModule.setTarget(\"channelList.vm\").addQueryData(\"channelId\", $top.channelId))\r\n\t\t\t\t<a href=\"$channelURL\">$!top.channelName</a>\r\n\t\t\t </td>\r\n\t\t\t <td width=\"8%\">\r\n\t\t\t\t#set ($pipelineURL = $homeModule.setTarget(\"pipelineList.vm\").addQueryData(\"channelId\", $top.channelId).addQueryData(\"pipelineId\", $top.pipelineId))\r\n\t\t\t\t<a href=\"$pipelineURL\">$!top.pipelineName</a>\r\n\t\t\t </td>\r\n\t\t\t <td width=\"10%\">$!numberFormat.formatDelay($!top.delayTime)</td>\r\n\t\t\t <td width=\"10%\">\r\n\t\t\t\t#if($!top.lastUpdateDelay)\r\n    \t\t\t\t#if($!top.lastUpdateDelay > 1800)\r\n    \t\t\t\t\t<font color=\"#FF0000\">$!top.lastUpdateDelay s</font>\r\n    \t\t\t\t#else\r\n    \t\t\t\t\t$!top.lastUpdateDelay s\r\n\t\t\t\t\t#end\r\n\t\t\t\t#else\r\n\t\t\t\t\t无信息\r\n\t\t\t\t#end\r\n\t\t\t </td>\r\n\t\t\t <td width=\"9%\">$!numberFormat.format($!top.dbStat.number)</td>\r\n\t\t\t <td width=\"9%\">$!numberFormat.formatFileSize($!top.dbStat.size)</td>\r\n\t\t\t <td width=\"9%\">$!numberFormat.format($!top.fileStat.number)</td>\r\n\t\t\t <td width=\"9%\">$!numberFormat.formatFileSize($!top.fileStat.size)</td>\r\n\t\t\t <td width=\"9%\">\r\n\t\t\t\t#set($channelStatus = $!channelStatuses.get($top.channelId))\r\n\t\t\t\t#if($!channelStatus.isStart())  <font style=\"color:green\">运行</font>\r\n                #elseif($!channelStatus.isPause())  <font style=\"color:red\">挂起</font>\r\n\t\t\t\t#else <font style=\"color:gray\">停止</font>\r\n\t\t\t\t#end\r\n\t\t\t </td>\r\n\t\t\t <td width=\"9%\">\r\n\t\t\t\t#set($mainstemStatus = $!mainstemStatuses.get($top.pipelineId))\r\n        \t\t#if(!$mainstemStatus)\r\n        \t\t\t<font color=\"#FF0000\">未工作</font>\r\n        \t\t#else\r\n            \t\t#if($!mainstemStatus.status.isTaking())\r\n            \t\t\t<font color=\"#FF0000\">定位中</font>\r\n            \t\t#else\r\n            \t\t\t工作中\r\n            \t\t#end\r\n        \t\t#end\r\n\t\t\t </td>\r\n\t\t   </tr>\r\n\t\t  #end\r\n        </table>\t\r\n</div>\r\n"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/templates/home/screen/autoKeeperClientPath.vm",
    "content": "$control.setTemplate(\"home:navigation.vm\")\r\n#set($user = $rundata.request.session.getAttribute(\"managerUser\"))\r\n<script type=\"text/javascript\" src=\"js/trcolor.js\"></script>\r\n<script type=\"text/javascript\" src=\"js/jquery-1.4.2.min.js\"></script> \r\n<script type=\"text/javascript\" src=\"js/jquery.simplemodal-1.4.js\"></script> \r\n<script language=\"javascript\">\r\n\tchangeNav(\"node\");\r\n\t\r\n\t function showdiv(targetid,objN){\r\n   \r\n      var target=document.getElementById(targetid);\r\n      var clicktext=document.getElementById(objN)\r\n\r\n            if (target.style.display==\"block\"){\r\n                target.style.display=\"none\";\r\n                clicktext.innerText=\"点击查看详细信息\";\r\n  \r\n\r\n            } else {\r\n                target.style.display=\"block\";\r\n                clicktext.innerText='关闭详细信息信息';\r\n            }\r\n   \r\n\t}\r\n\tfunction NeatDialog(sHTML, sTitle, bCancel)\r\n    {\r\n      window.neatDialog = null;\r\n      this.elt = null;\r\n      if (document.createElement  &&  document.getElementById)\r\n      {\r\n        var dg = document.createElement(\"div\");\r\n        dg.className = \"neat-dialog\";\r\n        if (sTitle)\r\n          sHTML = '<div class=\"neat-dialog-title\">'+sTitle+\r\n                  ((bCancel)?\r\n                    '<img src=\"x.gif\" alt=\"Cancel\" class=\"nd-cancel\" />':'')+\r\n                    '</div>\\n' + sHTML;\r\n        dg.innerHTML = sHTML;\r\n        var dbg = document.createElement(\"div\");\r\n        dbg.id = \"nd-bdg\";\r\n        dbg.className = \"neat-dialog-bg\";\r\n        var dgc = document.createElement(\"div\");\r\n        dgc.className = \"neat-dialog-cont\";\r\n        dgc.appendChild(dbg);\r\n        dgc.appendChild(dg);\r\n        if (document.body.offsetLeft > 0)\r\n        dgc.style.marginLeft = document.body.offsetLeft + \"px\";\r\n        document.body.appendChild(dgc);\r\n        if (bCancel) document.getElementById(\"nd-cancel\").onclick = function()\r\n        {\r\n          window.neatDialog.close();\r\n        };\r\n        this.elt = dgc;\r\n        window.neatDialog = this;\r\n      }\r\n    }\r\n    NeatDialog.prototype.close = function()\r\n    {\r\n      if (this.elt)\r\n      {\r\n        this.elt.style.display = \"none\";\r\n        this.elt.parentNode.removeChild(this.elt);\r\n      }\r\n      window.neatDialog = null;\r\n    }\r\n\r\n\tfunction openDialog( content )\r\n  \t{\r\n   \t\tvar sHTML = '<p><button onclick=\"window.neatDialog.close()\">关闭</button></p>' + content + '<p><button onclick=\"window.neatDialog.close()\">关闭</button></p>';\r\n        new NeatDialog(sHTML, \"<b>详情</b>\", false);\r\n      \r\n    }\r\n</script>\r\n<style type=\"text/css\">\r\n<!--\r\n\r\na { text-decoration: none; }\r\n\r\n.watchShow { cursor: hand; cursor:pointer;}\r\n.watchContent { margin-top: 10px; margin-bottom: 10px; width: 200px; border: 1px solid #CCC; background: #F1F1F1; padding: 15px; display: none; }\r\n.ephemeralShow { cursor: hand; cursor:pointer;}\r\n.ephemeralContent { margin-top: 10px; margin-bottom: 10px; width: 200px; border: 1px solid #CCC; background: #F1F1F1; padding: 15px; display: none; }\r\n-->\r\n</style>\r\n\r\n\r\n<!--页面主体-->\r\n<div class=\"main\">\r\n   \r\n  <div class=\"title\"> \r\n    <h2>Zookeeper Client连接信息</h2>\r\n  </div>\r\n   <!--分页表单-->\r\n   <form id=\"pageform\" name=\"pageform\" action=\"$homeModule.setTarget('nodeList.vm')\" method=\"post\">\r\n    \t<input type=\"hidden\" id=\"pageIndex\" name=\"pageIndex\" value=\"\"/>\r\n\t\t<input type=\"hidden\" id=\"searchKey\" name=\"searchKey\" value=\"$!searchKey\"/>\r\n   </form>\r\n   \r\n   <!--列表-->\r\n     \r\n  <table border=\"0\" cellspacing=\"0\" cellpadding=\"0\" class=\"list changecolor_w\">\r\n    <tr> \r\n      <th width=\"3%\">序号</th>\r\n      <th width=\"10%\">clientAdress</th>\r\n\t  <th width=\"12%\">sessionId</th>\r\n\t  <th width=\"20%\">watchStats</th>\r\n\t  <th width=\"20%\">ephemeralStats</th>\r\n\t  <th width=\"6%\">MaxLat</th>\r\n\t  <th width=\"6%\">AvgLat</th>\r\n\t  <th width=\"6%\">MinLat</th>\r\n\t  <th width=\"6%\">Queued</th>\r\n\t  <th width=\"6%\">Recved</th>\r\n\t  <th>Sent</th>\r\n\t  \r\n    </tr>\r\n\t#set($num = 0)\r\n\t#foreach($autoKeeperConnectionStat in $autoKeeperConnectionStats)\r\n\t\t#set($num = $num+1)\r\n        <tr> \r\n          <td>$num</td>\r\n\t\t  #set($content=$!autoKeeperConnectionStat.getHtmlOriginalContent())\r\n\t\t  <td>$!autoKeeperConnectionStat.clientAddress <img style=\"cursor:pointer;\" src=\"/images/seeDetail.png\" onclick=\"openDialog('$!content')\"</img></td>\r\n\t\t  <td>$!autoKeeperConnectionStat.sessionId</td>\r\n\t\t  <td>\r\n\t\t\t<a id=\"watchShow-$velocityCount\" onClick=\"showdiv('watchContent-$velocityCount','watchShow-$velocityCount')\">点击查看详细信息</a>\r\n\t\t\t<div id=\"watchContent-$velocityCount\" class=\"watchContent\">\r\n\t\t\t\t#foreach($autoKeeperWatchStat in $!autoKeeperConnectionStat.watchStats)\r\n\t\t\t\t\t#foreach($path in $!autoKeeperWatchStat.paths)\r\n\t\t\t\t\t\t$path <br />\r\n\t\t\t\t\t#end\r\n\t\t\t\t#end\r\n\t\t\t</div>\r\n\t\t  </td>\r\n\t\t  <td>\r\n\t\t\t<a id=\"ephemeralShow-$velocityCount\" onClick=\"showdiv('ephemeralContent-$velocityCount','ephemeralShow-$velocityCount')\">点击查看详细信息</a>\r\n\t\t\t<div id=\"ephemeralContent-$velocityCount\" class=\"ephemeralContent\">\r\n\t\t\t\t#foreach($autoKeeperWatchStat in $!autoKeeperConnectionStat.ephemeralStats)\r\n\t\t\t\t\t#foreach($path in $!autoKeeperWatchStat.paths)\r\n\t\t\t\t\t\t$path <br />\r\n\t\t\t\t\t#end\r\n\t\t\t\t#end\r\n\t\t\t</div>\r\n\t\t  </td>\r\n\t\t  <td>$!autoKeeperConnectionStat.maxLatency</td>\r\n    \t  <td>$!autoKeeperConnectionStat.avgLatency</td>\r\n    \t  <td>$!autoKeeperConnectionStat.minLatency</td>\r\n    \t  <td>$!autoKeeperConnectionStat.queued</td>\r\n    \t  <td>$!autoKeeperConnectionStat.recved</td>\r\n    \t  <td>$!autoKeeperConnectionStat.sent</td>\r\n        </tr>\r\n\t#end\r\n    \r\n  </table>\r\n  <!--常规按钮-->\r\n</div>\r\n"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/templates/home/screen/autoKeeperClustersDetail.vm",
    "content": "$control.setTemplate(\"home:navigation.vm\")\r\n#set($user = $rundata.request.session.getAttribute(\"managerUser\"))\r\n<script type=\"text/javascript\" src=\"js/trcolor.js\"></script>\r\n<script type=\"text/javascript\" src=\"js/jquery-1.4.2.min.js\"></script> \r\n<script type=\"text/javascript\" src=\"js/jquery.simplemodal-1.4.js\"></script> \r\n<script language=\"javascript\">\r\n\tchangeNav(\"node\");\r\n\t\r\n\tfunction NeatDialog(sHTML, sTitle, bCancel)\r\n    {\r\n      window.neatDialog = null;\r\n      this.elt = null;\r\n      if (document.createElement  &&  document.getElementById)\r\n      {\r\n        var dg = document.createElement(\"div\");\r\n        dg.className = \"neat-dialog\";\r\n        if (sTitle)\r\n          sHTML = '<div class=\"neat-dialog-title\">'+sTitle+\r\n                  ((bCancel)?\r\n                    '<img src=\"x.gif\" alt=\"Cancel\" class=\"nd-cancel\" />':'')+\r\n                    '</div>\\n' + sHTML;\r\n        dg.innerHTML = sHTML;\r\n        var dbg = document.createElement(\"div\");\r\n        dbg.id = \"nd-bdg\";\r\n        dbg.className = \"neat-dialog-bg\";\r\n        var dgc = document.createElement(\"div\");\r\n        dgc.className = \"neat-dialog-cont\";\r\n        dgc.appendChild(dbg);\r\n        dgc.appendChild(dg);\r\n        if (document.body.offsetLeft > 0)\r\n        dgc.style.marginLeft = document.body.offsetLeft + \"px\";\r\n        document.body.appendChild(dgc);\r\n        if (bCancel) document.getElementById(\"nd-cancel\").onclick = function()\r\n        {\r\n          window.neatDialog.close();\r\n        };\r\n        this.elt = dgc;\r\n        window.neatDialog = this;\r\n      }\r\n    }\r\n    NeatDialog.prototype.close = function()\r\n    {\r\n      if (this.elt)\r\n      {\r\n        this.elt.style.display = \"none\";\r\n        this.elt.parentNode.removeChild(this.elt);\r\n      }\r\n      window.neatDialog = null;\r\n    }\r\n\r\n\tfunction openDialog( content )\r\n  \t{\r\n   \t\tvar sHTML = '<p><button onclick=\"window.neatDialog.close()\">关闭</button></p>' + content + '<p><button onclick=\"window.neatDialog.close()\">关闭</button></p>';\r\n        new NeatDialog(sHTML, \"<b>详情</b>\", false);\r\n      \r\n    }\r\n\r\n</script>\r\n\r\n\r\n\r\n<!--页面主体-->\r\n<div class=\"main\">\r\n   \r\n  <div class=\"title\"> \r\n    <h2>Zookeeper Server连接信息</h2>\r\n  </div>\r\n   <!--分页表单-->\r\n   <form id=\"pageform\" name=\"pageform\" action=\"$homeModule.setTarget('nodeList.vm')\" method=\"post\">\r\n    \t<input type=\"hidden\" id=\"pageIndex\" name=\"pageIndex\" value=\"\"/>\r\n\t\t<input type=\"hidden\" id=\"searchKey\" name=\"searchKey\" value=\"$!searchKey\"/>\r\n   </form>\r\n\r\n   <!--列表-->\r\n     \r\n  <table border=\"0\" cellspacing=\"0\" cellpadding=\"0\" class=\"list changecolor_w\">\r\n    <tr> \r\n      <th>序号</th>\r\n      <th>地址</th>\r\n\t  <th>角色</th>\r\n\t  <th>MaxLatency</th>\r\n\t  <th>AvgLatency</th>\r\n\t  <th>MinLatency</th>\r\n\t  <th>Queued</th>\r\n\t  <th>Recved</th>\r\n\t  <th>Sent</th>\r\n      <th>NodeCount</th>\r\n\t  <th>Client</th>\r\n    </tr>\r\n\t#set($num = 0)\r\n\t#foreach($statMapEntry in $statMap.entrySet())\r\n\t\t#set($num = $num+1)\r\n\t\t#set ($pathUrl = $homeModule.setTarget(\"autoKeeperClientPath.vm\").addQueryData(\"address\", $!statMapEntry.getKey()).addQueryData(\"clusterId\", $clusterId))\r\n        <tr> \r\n          <td>$num</td>\r\n\t\t  #set($content=$!statMapEntry.getValue().getHtmlOriginalContent())\r\n          <td>$!statMapEntry.getKey() <img style=\"cursor:pointer;\" src=\"/images/seeDetail.png\" onclick=\"openDialog('$!content')\" /> </td>\r\n\t\t  <td>$!statMapEntry.getValue().getQuorumType().toString()</td>\r\n\t\t  <td>$!statMapEntry.getValue().getMaxLatency()</td>\r\n\t\t  <td>$!statMapEntry.getValue().getAvgLatency()</td>\r\n\t\t  <td>$!statMapEntry.getValue().getMinLatency()</td>\r\n\t\t  <td>$!statMapEntry.getValue().getQueued()</td>\r\n\t\t  <td>$!statMapEntry.getValue().getRecved()</td>\r\n\t\t  <td>$!statMapEntry.getValue().getSent()</td>\r\n\t\t  <td>$!statMapEntry.getValue().getNodeCount()</td>\r\n          <td><a href=\"$pathUrl\">查看</a></td>\r\n        </tr>\r\n\t#end\r\n    \r\n  </table>\r\n  <!--常规按钮-->\r\n</div>\r\n"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/templates/home/screen/autoKeeperClustersList.vm",
    "content": "$control.setTemplate(\"home:navigation.vm\")\r\n#set($user = $rundata.request.session.getAttribute(\"managerUser\"))\r\n<script type=\"text/javascript\" src=\"js/trcolor.js\"></script>\r\n<script type=\"text/javascript\" src=\"js/jquery-1.4.2.min.js\"></script> \r\n<script type=\"text/javascript\" src=\"js/jquery.simplemodal-1.4.js\"></script> \r\n<script language=\"javascript\">\r\n\tchangeNav(\"node\");\r\n</script>\r\n\r\n\r\n\r\n<!--页面主体-->\r\n<div class=\"main\">\r\n   \r\n  <div class=\"title\"> \r\n    <h2>Zookeeper管理</h2>\r\n  </div>\r\n   <div class=\"crumbs\"><a href=\"auto_keeper_clusters_list.htm\">Zookeeper管理</a> </div> \r\n   <!--分页表单-->\r\n   <form id=\"pageform\" name=\"pageform\" action=\"$homeModule.setTarget('autoKeeperClustersList.vm')\" method=\"post\">\r\n    \t<input type=\"hidden\" id=\"pageIndex\" name=\"pageIndex\" value=\"\"/>\r\n\t\t<input type=\"hidden\" id=\"searchKey\" name=\"searchKey\" value=\"$!searchKey\"/>\r\n   </form>\r\n   <!--Node搜索-->\r\n   <div class=\"search_o\"> \r\n\t\t<form name=\"search_zookeeper\" action=\"auto_keeper_clusters_list.htm\"  method=\"post\">\r\n\t\t\t##$csrfToken.hiddenField\r\n\t\t\t<div class=\"search_input\">\r\n\t\t\t\t<input name=\"searchKey\" type=\"text\" value=\"请输入关键字(目前支持Zookeeper的地址搜索)\"  onfocus=\"if(this.value == '请输入关键字(目前支持Zookeeper的地址搜索)') {this.value='';}\" onblur=\"if(this.value == '') {this.value = '请输入关键字(目前支持Zookeeper的地址搜索)';}\" />\r\n\t\t\t</div>\r\n\t\t\t<div class=\"search_btn\"><a href=\"javascript:document.search_zookeeper.submit();\"><img src=\"images/search_btn.png\" width=\"39\" height=\"31\" /></a></div>\r\n        </form>\r\n   </div>\r\n   \r\n   <!--列表-->\r\n     \r\n  <table border=\"0\" cellspacing=\"0\" cellpadding=\"0\" class=\"list changecolor_w\">\r\n    <tr> \r\n      <th>序号</th>\r\n      <th>集群名字</th>\r\n      <th>集群地址</th>\r\n\t  <th>描述</th>\r\n      <th>操作</th>\r\n\t  \r\n    </tr>\r\n\t#foreach($autoKeeperCluster in $autoKeeperClusters)\r\n        <tr> \r\n          <td width=\"4%\">$autoKeeperCluster.id</td>\r\n\t\t  #set ($detailUrl = $homeModule.setTarget(\"autoKeeperClustersDetail.vm\").addQueryData(\"clusterId\", $autoKeeperCluster.id).render())\r\n          <td width=\"10%\"><a href=\"$detailUrl\">$!autoKeeperCluster.clusterName</a></td>\r\n          <td width=\"40%\">$!autoKeeperCluster.serverList</td>\r\n\t\t  <td width=\"15%\">$!autoKeeperCluster.description</td>\r\n          <td>\r\n\t\t\t<a href=\"$detailUrl\"><img src=\"images/ico_edit.png\" width=\"13\" height=\"13\" /><span class=\"ico_font\">查看</span></a>\r\n\t\t\t#if($user.authorizeType.isAdmin())\r\n\t\t\t\t#set ($editURL = $homeModule.setTarget(\"editAutoKeeper.vm\").addQueryData(\"clusterId\", $autoKeeperCluster.id))\r\n\t\t\t\t<span class=\"ico_line\">|</span><a href=\"$editURL\"><img src=\"images/ico_edit.png\" width=\"13\" height=\"13\" /><span class=\"ico_font\">编辑</span></a>\r\n    \t\t\t#set ($refreshURL = $homeModule.setAction(\"AutoKeeperClusterAction\").addQueryData(\"clusterId\", $autoKeeperCluster.id).addQueryData(\"eventSubmitDoRefresh\", \"true\"))\r\n        \t\t<span class=\"ico_line\">|</span><a href=\"$refreshURL\"><img src=\"images/ico_edit.png\" width=\"13\" height=\"13\" /><span class=\"ico_font\">刷新</span></a>\r\n    \t\t\t#set ($removeURL = $homeModule.setAction(\"AutoKeeperClusterAction\").addQueryData(\"clusterId\", $autoKeeperCluster.id).addQueryData(\"eventSubmitDoDelete\", \"true\"))\r\n    \t\t\t<span class=\"ico_line\">|</span><a href=\"$removeURL\"><img src=\"images/ico_del.png\" width=\"9\" height=\"9\"  />删除</a>\r\n\t\t\t#end\r\n\t\t  </td>\r\n        </tr>\r\n\t#end\r\n    \r\n  </table>\r\n  <!--常规按钮-->\r\n  #if($user.authorizeType.isAdmin())\r\n\t<div class=\"btn\"><a href=\"$zookeeperAddLink\">添加</a></div>\r\n  #end\r\n  <!--分页-->\r\n     <div class=\"page\">共$paginator.items条数据&nbsp;&nbsp;第$paginator.page页/共$paginator.pages页&nbsp;&nbsp; \r\n       \r\n\t   #if($paginator.page == 1)\r\n            <font color=\"999999\">首页</font>\r\n\t   #else\r\n\t\t\t<a href=\"#\" class=\"prev\" onclick=\"pageNavigation(this,1)\">首页</a>\r\n\t   #end\r\n\t   \r\n\t   #if($paginator.page > 1)\r\n\t\t\t#set($pre_page = $paginator.page - 1)\r\n\t\t\t\t<a href=\"#\" class=\"prev\" onclick=\"pageNavigation(this,$pre_page)\">上一页</a>\r\n\t   #else\r\n            <font color=\"999999\">上一页</font>\r\n\t   #end\r\n\t   ##分页下标\r\n\t   #set($counts_keys = $paginator.getSlider(7))\r\n\t   #foreach( $thisPage in $counts_keys)\r\n\t\t\t#if( $thisPage == $paginator.page)\r\n                <b>$thisPage</b>\r\n\t\t\t#else\r\n\t\t\t\t#if($thisPage != 0)\r\n\t\t\t\t\t<a href=\"#\" class=\"num\" onclick=\"pageNavigation(this,$thisPage)\">$thisPage</a> \r\n\t\t\t\t#end\r\n\t\t\t#end\r\n\t   #end\r\n\t   \r\n\t   #if($paginator.page < $paginator.pages)\r\n\t\t\t#set($next_page = $paginator.page + 1)\r\n\t\t\t\t<a href=\"#\" class=\"prev\" onclick=\"pageNavigation(this,$next_page)\">下一页</a>\r\n\t   #else\r\n            <font color=\"999999\">下一页</font>\r\n\t   #end\r\n\t   \r\n\t   #if($paginator.page == $paginator.pages)\r\n            <font color=\"999999\">末页</font>\r\n\t   #else\r\n\t\t\t<a href=\"#\" class=\"prev\" onclick=\"pageNavigation(this,$paginator.pages)\">末页</a>\r\n\t   #end\r\n     </div>     \r\n</div>\r\n"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/templates/home/screen/behaviorHistoryCurve.vm",
    "content": "$control.setTemplate(\"home:navigation.vm\")\r\n<script language=\"javascript\" type=\"text/javascript\" src=\"js/jquery-1.4.2.min.js\" > </script>\r\n<script language=\"javascript\" type=\"text/javascript\" src=\"js/jquery.simplemodal-1.4.js\" ></script>\r\n<script language=\"javascript\" type=\"text/javascript\" src=\"js/My97DatePicker/WdatePicker.js\" ></script>\r\n<script language=\"javascript\" type=\"text/javascript\" src=\"js/analysisStatus.js\" ></script>\r\n\r\n<script language=\"javascript\" type=\"text/javascript\" src=\"js/flot/jquery.colorhelpers.js\" > </script>\r\n<script language=\"javascript\" type=\"text/javascript\" src=\"js/flot/jquery.flot.js\" ></script>\r\n<script language=\"javascript\" type=\"text/javascript\" src=\"js/flot/jquery.flot.crosshair.js\"></script>\r\n<script language=\"javascript\" type=\"text/javascript\" src=\"js/flot/jquery.flot.fillbetween.js\"></script>\r\n<script language=\"javascript\" type=\"text/javascript\" src=\"js/flot/jquery.flot.image.js\" > </script>\r\n<script language=\"javascript\" type=\"text/javascript\" src=\"js/flot/jquery.flot.navigate.js\" ></script>\r\n<script language=\"javascript\" type=\"text/javascript\" src=\"js/flot/jquery.flot.pie.js\"></script>\r\n<script language=\"javascript\" type=\"text/javascript\" src=\"js/flot/jquery.flot.selection.js\"></script>\r\n<script language=\"javascript\" type=\"text/javascript\" src=\"js/flot/jquery.flot.stack.js\" > </script>\r\n<script language=\"javascript\" type=\"text/javascript\" src=\"js/flot/jquery.flot.symbol.js\" ></script>\r\n<script language=\"javascript\" type=\"text/javascript\" src=\"js/flot/jquery.flot.threshold.js\"></script>\r\n<script language=\"javascript\" type=\"text/javascript\" src=\"js/flot/jquery.flot.resize.js\"></script>\r\n<!--[if IE]><script language=\"javascript\" type=\"text/javascript\" src=\"js/flot/excanvas.min.js\"></script><![endif]-->\r\n<script type=\"text/javascript\">\r\n\r\n//****************0.create JVM Flash*********************************\r\n$(function () { \r\n\r\n\tvar historyNumber1;\r\n\tvar historyNumber2;\r\n\tvar historyNumber3;\r\n\tvar historyNumber4;\r\n\tvar historyNumber5;\r\n\tvar time;\r\n\tvar flotNumber1=[];\r\n\tvar flotNumber2=[];\r\n\tvar flotNumber3=[];\r\n\tvar flotNumber4=[];\r\n\tvar flotNumber5=[];\r\n\t#foreach ($key in $behaviourHistoryInfos.keySet())\r\n\t\ttime=$key;\r\n\t\thistoryNumber1=$behaviourHistoryInfos.get($key).insertNumber;\r\n\t\thistoryNumber2=$behaviourHistoryInfos.get($key).updateNumber;\r\n\t\thistoryNumber3=$behaviourHistoryInfos.get($key).deleteNumber;\r\n\t\thistoryNumber4=$behaviourHistoryInfos.get($key).fileNumber;\r\n\t\thistoryNumber5=$behaviourHistoryInfos.get($key).fileSize / 1024;\r\n\t\tflotNumber1.push([time,historyNumber1]);\r\n\t\tflotNumber2.push([time,historyNumber2]);\r\n\t\tflotNumber3.push([time,historyNumber3]);\r\n\t\tflotNumber4.push([time,historyNumber4]);\r\n\t\tflotNumber5.push([time,historyNumber5]);\r\n\t#end\r\n\t\t\t\t\t\r\n\tcreateBehaviorFlash(flotNumber1,flotNumber2,flotNumber3,flotNumber4,flotNumber5);\r\n\t\t\t\t\r\n\tfunction showTooltip(x, y, contents) {\r\n        $('<div id=\"tooltip\">' + contents + '</div>').css( {\r\n            position: 'absolute',\r\n            display: 'none',\r\n            top: y + 5,\r\n            left: x + 5,\r\n            border: '1px solid #fdd',\r\n            padding: '2px',\r\n            'background-color': '#fee',\r\n            opacity: 0.80\r\n        }).appendTo(\"body\").fadeIn(200);\r\n    };\r\n \r\n    var previousPoint = null;\r\n\r\nfunction showTip(id) {\t\r\n    $(id).bind(\"plothover\", function (event, pos, item) {\r\n            if (item) {\r\n                if (previousPoint != item.dataIndex) {\r\n                    previousPoint = item.dataIndex;\r\n                    \r\n                    $(\"#tooltip\").remove();\r\n                    var x = item.datapoint[0],\r\n                        y = strFormat(item.datapoint[1].toFixed(0));                  \r\n                    var dateTime = new Date();  \r\n                    dateTime.setTime(x);      \r\n                    var dateTimeStr = dateTime.toLocaleString();  \r\n                    dateTimeStr = dateTimeStr.replace(' ',''); \r\n\t\t\t\t\tvar context =\"(\" + dateTimeStr + \" , \" + y+  \")\";\r\n                    showTooltip(item.pageX, item.pageY,context);\r\n                }\r\n            }\r\n            else {\r\n                $(\"#tooltip\").remove();\r\n                previousPoint = null;            \r\n            }\r\n    })\r\n};\r\n\r\n    showTip(\"#insertNumber\");\r\n\tshowTip(\"#updateNumber\");\r\n\tshowTip(\"#deleteNumber\");\r\n\tshowTip(\"#fileNumber\");\r\n\tshowTip(\"#fileSize\");\r\n\r\n\t\r\n\t$(\"#tabid1\").show();\r\n\t$(\"#tabid2\").hide();\r\n\t$(\"#tabid3\").hide();\r\n\t$(\"#tabid4\").hide();\r\n\t$(\"#tabid5\").hide();\r\n});\r\n\r\n  var lastTabId = 1;\r\n  function changeTab(n){\r\n      var tabnav = \"tab\"+ n;\r\n      var tabid = \"tabid\"+ n;\r\n      if(lastTabId != n){\r\n\t\tdocument.getElementById(\"tabid\" + lastTabId).style.display = \"none\";\r\n\t\tvar element = document.getElementById(\"tab\"+ lastTabId);\r\n\t\telement.removeAttribute(\"class\"); //for firefox\r\n\t\telement.removeAttribute(\"className\"); //for IE\r\n\t  \r\n        lastTabId = n;\r\n    }\r\n\t\r\n\tdocument.getElementById(tabid).style.display=\"block\";\r\n\t\r\n\tvar element = document.getElementById(tabnav);\r\n\telement.setAttribute(\"class\",\"tab_active\"); //for firefox\r\n\telement.setAttribute(\"className\",\"tab_active\"); //for IE\r\n  };\r\n</script>\r\n\r\n<div class=\"main\">\r\n  <div class=\"title\"> \r\n    <h2>行为曲线</h2>\r\n  </div>\r\n  <div class=\"crumbs\"><a href=\"channelList.htm?channelId=$channel.id\">channel管理</a>&nbsp;&nbsp;>&nbsp;&nbsp;<a href=\"pipeline_list.htm?channelId=$channel.id\">Pipeline</a>&nbsp;&nbsp;>&nbsp;&nbsp;<a href=\"behaviorHistoryCurve.htm?dataMediaPairId=$dataMediaPair.id\">行为曲线</a></div>  \r\n     <!--Channel搜索-->\r\n  <div class=\"crumbs\"></div>     \r\n     <div class=\"tab\" id=\"Tab2\" >\r\n        \r\n\t\t<div class=\"menubox\" style=\"margin:15px 0 0 0;\">\r\n        <ul>\r\n        <li id=\"tab1\" onclick=\"changeTab(1)\">insert类型</li>\r\n           \r\n        <li id=\"tab2\" onclick=\"changeTab(2)\">update类型</li>\r\n\t\t\r\n\t\t<li id=\"tab3\" onclick=\"changeTab(3)\">delete类型</li>\r\n        \r\n\t\t<li id=\"tab4\" onclick=\"changeTab(4)\">文件数量</li>\r\n\t\t\r\n\t\t<li id=\"tab5\" onclick=\"changeTab(5)\">文件大小</li>\r\n        </ul>\r\n        </div>\r\n\t\t<div class=\"contentbox_tab box_tab\">\r\n\t\t  <div class=\"search_o\" style=\"margin:5px 0 15px 15px;\" > \r\n\t\t<form name=\"search_behaviorHistory\" method=\"post\">\r\n\t\t\t<input type=\"hidden\" name=\"pairId\" value=\"$dataMediaPair.id\"/>\r\n\t\t\t<div class=\"divInPut\">\r\n\t\t\t\t<table border=\"0\" cellspacing=\"0\" cellpadding=\"0\" style=\"margin-bottom:6px;\">\r\n\t\t\t\t\t<tr>\r\n\t\t\t\t\t<td><img src=\"images/search_global_l.png\" width=\"78\" height=\"32\" /></td>\r\n\t\t\t\t\t<td background=\"images/search_global_m.png\">\r\n                                                时间：<input name=\"d5221\" id=\"d5221\" class=\"Wdate\" type=\"text\" value=\"$!start\" onclick=\"WdatePicker({dateFmt:'yyyy-MM-dd HH:mm',maxDate:'#F{$dp.$D(\\'d5222\\')}'})\"/>\r\n                </td>                       \r\n\t\t\t\t<td background=\"images/search_global_m.png\"> 至：\r\n                  <input name=\"d5222\" id=\"d5222\" class=\"Wdate\" type=\"text\" value=\"$!end\" onclick=\"WdatePicker({dateFmt:'yyyy-MM-dd HH:mm',minDate:'#F{$dp.$D(\\'d5221\\')}'})\"/>\r\n                </td>\r\n\t\t\t\t<td><div ><a class=\"search_btn2\" href=\"javascript:document.search_behaviorHistory.submit();\"></a></div>\r\n\t\t\t\t</td>\r\n\t\t\t\t<td><img src=\"images/search_global_r2.png\"/></td>\r\n\t\t\t   </tr>\t\t\t\r\n              </table>\r\n             </div>\t\r\n\t\t\t</form>\r\n\t\t   </div>\r\n\t\t\t<div id=\"tabid1\" style=\"clear:both;\">\r\n\t\t\t\t&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\r\n            \t总记录数: <span style=\"font-weight: bold;\">$!numberFormat.format($totalInsert)</span> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\r\n            <br/><br/>\r\n\t\t\t<fieldset id=\"Number1\" style=\"border:1px #ccc solid; margin:0 0 20px 15px;width:730px;padding:25px 10px 20px 15px;\"><legend><b>insert(条)</b></legend>\r\n\t\t\t    <div id=\"insertNumber\" style=\"width:700px;height:250px\"></div>\r\n\t\t\t</fieldset>\r\n\t\t\t</div>\r\n\t\t\t\r\n\t\t\t<div id=\"tabid2\" style=\"clear:both;\">\r\n\t\t\t\t&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\r\n            \t总记录数: <span style=\"font-weight: bold;\">$!numberFormat.format($totalUpdate)</span> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\r\n            <br/><br/>\r\n\t\t\t<fieldset id=\"Number2\" style=\"border:1px #ccc solid; margin:0 0 20px 15px;width:730px;padding:25px 10px 20px 15px;\"><legend><b>update(条)</b></legend>\r\n\t\t\t    <div id=\"updateNumber\" style=\"width:700px;height:250px\"></div>\r\n\t\t\t</fieldset>\r\n\t\t\t</div>\r\n\t\t\t\r\n\t\t\t<div id=\"tabid3\" style=\"clear:both;\">\r\n\t\t\t\t&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\r\n            \t总记录数: <span style=\"font-weight: bold;\">$!numberFormat.format($totalDelete)</span> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\r\n            <br/><br/>\r\n\t\t\t<fieldset id=\"Number3\" style=\"border:1px #ccc solid; margin:0 0 20px 15px;width:730px;padding:25px 10px 20px 15px;\"><legend><b>delete(条)</b></legend>\r\n\t\t\t    <div id=\"deleteNumber\" style=\"width:700px;height:250px\"></div>\r\n\t\t\t</fieldset>\r\n\t\t\t</div>\r\n\t\t\t\r\n\t\t\t<div id=\"tabid4\" style=\"clear:both;\">\r\n\t\t\t\t&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\r\n            \t总文件数: <span style=\"font-weight: bold;\">$!numberFormat.format($totalFileCount)</span> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\r\n            <br/><br/>\r\n\t\t\t<fieldset id=\"Number4\" style=\"border:1px #ccc solid; margin:0 0 20px 15px;width:730px;padding:25px 10px 20px 15px;\"><legend><b>文件数量(条)</b></legend>\r\n\t\t\t    <div id=\"fileNumber\" style=\"width:700px;height:250px\"></div>\r\n\t\t\t</fieldset>\r\n\t\t\t</div>\r\n\t\t\t\r\n\t\t\t<div id=\"tabid5\" style=\"clear:both;\">\r\n\t\t\t\t&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\r\n            \t总大小: <span style=\"font-weight: bold;\">$!numberFormat.formatFileSize($totalFileSize)</span> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\r\n            <br/><br/>\r\n\t\t\t<fieldset id=\"Number5\" style=\"border:1px #ccc solid; margin:0 0 20px 15px;width:730px;padding:25px 10px 20px 15px;\"><legend><b>文件大小(KB)</b></legend>\r\n\t\t\t    <div id=\"fileSize\" style=\"width:700px;height:250px\"></div>\r\n\t\t\t</fieldset>\r\n\t\t\t</div>\r\n  </div>\r\n</div>\r\n</div>\r\n<script type=\"text/javascript\">\r\n  changeTab(1);\r\n</script>"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/templates/home/screen/canalInfo.vm",
    "content": "$control.setTemplate(\"home:navigation.vm\")\r\n<script language=\"javascript\">\r\n<script language=\"javascript\">\r\n<!--\r\n\tchangeNav(\"datamedia\");\r\n//-->\r\n\t\t\r\n</script>\r\n\r\n<div class=\"main\">\r\n <div class=\"title\"><h2>Canal信息</h2></div>\r\n <div class=\"crumbs\">Canal信息</div>\r\n <div class=\"setting_box\">\r\n <table cellpadding=\"0\" cellspacing=\"0\" class=\"setting\">\r\n  <tr>\r\n  <th>canal序号：</th><td>$!canal.id</td>\r\n  </tr>\r\n  <tr>\r\n  <th>canal名称：</th><td>$!canal.name</td>\r\n  </tr>\r\n  <tr>\r\n  <th>运行状态：</th><td>$!canal.status</td>\r\n  </tr>\r\n  <tr>\r\n  <th>运行模式：</th><td>$!canal.canalParameter.runMode</td>\r\n  </tr>\r\n  <tr>\r\n  <th>Zookeeper集群：</th><td>$zkCluster.clusterName [ $stringUtil.join($zkCluster.serverList,\",\") ]</td>\r\n  </tr>\r\n  <tr>\r\n  <th>数据源类型：</th><td>$!canal.canalParameter.sourcingType</td>\r\n  </tr>\r\n  <tr>\r\n  <th>数据库地址：</th><td>$!numberFormat.formatGroupDbAddress($canal.canalParameter.sourcingType,$canal.canalParameter.groupDbAddresses)</td>\r\n  </tr>\r\n  <tr>\r\n  <th>数据库帐号：</th><td>$!canal.canalParameter.dbUsername</td>\r\n  </tr>\r\n  <tr>\r\n  <th>connectionCharset：</th><td>$!canal.canalParameter.connectionCharset</td>\r\n  </tr>\r\n  #**\r\n  #if($!canal.canalParameter.sourcingType.isMysql())\r\n  <tr>\r\n  <th>链接到mysql的slaveId：</th><td>$!canal.canalParameter.slaveId</td>\r\n  </tr>\r\n  #end\r\n  *#\r\n  \r\n  #if($!canal.canalParameter.sourcingType.isLocalBinlog())\r\n  <tr>\r\n  <th>本地localBinlog目录：</th><td>$!canal.canalParameter.localBinlogDirectory</td>\r\n  </tr>\r\n  #end\r\n\r\n  #if($!canal.canalParameter.sourcingType.isOceanBase())\r\n  <tr>\r\n  <th>rsList：</th><td>$!canalExtra.get(\"rsList\")</td>\r\n  </tr>\r\n\r\n  <tr>\r\n  <th>tenant：</th><td>$!canalExtra.get(\"tenant\")</td>\r\n  </tr>\r\n  #end\r\n  \r\n  <tr>\r\n  <th>是否启用gtid位点：</th><td>$!canal.canalParameter.gtidEnable</td>\r\n  </tr>\r\n  \r\n  <tr>\r\n  <th>位点信息：</th><td>#foreach($position in $!canal.canalParameter.positions)$position;#end</td>\r\n  </tr>\r\n  \r\n  <tr>\r\n  <th>是否开启表结构TSDB：</th><td>$!canal.canalParameter.tsdbEnable</td>\r\n  </tr>\r\n  \r\n  <tr>\r\n  <th>rds accesskey：</th><td>$!canal.canalParameter.rdsAccesskey</td>\r\n  </tr>\r\n  \r\n  <tr>\r\n  <th>rds secretkey：</th><td>$!canal.canalParameter.rdsSecretkey</td>\r\n  </tr>\r\n  \r\n  <tr>\r\n  <th>rds instanceId：</th><td>$!canal.canalParameter.rdsInstance</td>\r\n  </tr>\r\n \r\n  <tr>\r\n  <th>存储机制：</th><td>$!canal.canalParameter.storageMode</td>\r\n  </tr>\r\n  \r\n  #if($!canal.canalParameter.storageMode.isMemory())\r\n  <tr>\r\n  <th>内存存储batch获取模式：</th><td>$!canal.canalParameter.storageBatchMode</td>\r\n  </tr>\r\n  <tr>\r\n  <th>内存存储buffer记录数：</th><td>$!canal.canalParameter.memoryStorageBufferSize</td>\r\n  </tr>\r\n  <tr>\r\n  <th>内存存储buffer记录单元大小：</th><td>$!canal.canalParameter.memoryStorageBufferMemUnit</td>\r\n  </tr>\r\n  #end\r\n  \r\n  #if($!canal.canalParameter.storageMode.isFile())\r\n  <tr>\r\n  <th>文件存储的目录位置：</th><td>$!canal.canalParameter.fileStorageDirectory</td>\r\n  </tr>\r\n  <tr>\r\n  <th>文件存储store记录数：</th><td>$!canal.canalParameter.fileStorageStoreCount</td>\r\n  </tr>\r\n  <tr>\r\n  <th>store文件个数：</th><td>$!canal.canalParameter.fileStorageRollverCount</td>\r\n  </tr>\r\n  <tr>\r\n  <th>store存储占disk百分比：</th><td>$!canal.canalParameter.fileStoragePercentThresold</td>\r\n  </tr>\r\n  #end\r\n  \r\n  <tr>\r\n  <th>HA机制：</th><td>$!canal.canalParameter.haMode</td>\r\n  </tr>\r\n  \r\n  #if($!canal.canalParameter.haMode.isMedia())\r\n  <tr>\r\n  <th>media group key：</th><td>$!canal.canalParameter.mediaGroup</td>\r\n  </tr>\r\n  #end\r\n  \r\n  <tr>\r\n  <th>是否开启心跳：</th><td>$!canal.canalParameter.detectingEnable</td>\r\n  </tr>\r\n  #if($!canal.canalParameter.detectingEnable)\r\n  <tr>\r\n  <th>心跳sql：</th><td>$!canal.canalParameter.detectingSQL</td>\r\n  </tr>\r\n  <tr>\r\n  <th>心跳检测频率：</th><td>$!canal.canalParameter.detectingIntervalInSeconds</td>\r\n  </tr>\r\n  <tr>\r\n  <th>心跳超时时间：</th><td>$!canal.canalParameter.detectingTimeoutThresholdInSeconds</td>\r\n  </tr>\r\n  <tr>\r\n  <th>心跳检查重试次数：</th><td>$!canal.canalParameter.detectingRetryTimes</td>\r\n  </tr>\r\n  <tr>\r\n  <th>是否启用心跳HA：</th><td>$!canal.canalParameter.heartbeatHaEnable</td>\r\n  </tr>\r\n  #end\r\n  \r\n  <tr>\r\n  <th>meta机制：</th><td>$!canal.canalParameter.metaMode</td>\r\n  </tr>\r\n  <tr>\r\n  <th>索引机制：</th><td>$!canal.canalParameter.indexMode</td>\r\n  </tr>\r\n  <tr>\r\n  <th>服务端口：</th><td>$!canal.canalParameter.port</td>\r\n  </tr>\r\n  <tr>\r\n  <th>默认连接超时：</th><td>$!canal.canalParameter.defaultConnectionTimeout</td>\r\n  </tr>\r\n  <tr>\r\n  <th>接收BufferSize：</th><td>$!canal.canalParameter.receiveBufferSize</td>\r\n  </tr>\r\n  <tr>\r\n  <th>发送BufferSize：</th><td>$!canal.canalParameter.sendBufferSize</td>\r\n  </tr>\r\n  <tr>\r\n  <th>切换回退时间：</th><td>$!canal.canalParameter.fallbackIntervalInSeconds</td>\r\n  </tr>\r\n  <tr>\r\n  <th>过滤表达式：</th><td>$!canal.canalParameter.blackFilter</td>\r\n  </tr>\r\n  <tr>\r\n  <th>描述信息：</th><td>$!canal.desc</td>\r\n  </tr>\r\n </table>\r\n </div>\r\n \r\n <table border=\"0\" cellspacing=\"0\" cellpadding=\"0\" class=\"list changecolor_w\" width=\"60%\">\r\n    <tr>\r\n      <th>channel名字</th>\r\n      <th>pipeline名字</th>\r\n\t  <th>消费端ID</th>\r\n\t  <th>消费批次大小</th>\r\n\t  <th>获取数据超时时间</th>\r\n\t  <th>操作</th>\r\n    </tr>\r\n    \r\n    #foreach ($pipeline in $pipelines)\r\n\t\t<tr>\r\n\t\t  #set($channel = $channelMap.get($pipeline.channelId))\r\n\t\t  #set ($channelURL = $homeModule.setTarget(\"channelList.vm\").addQueryData(\"channelId\", $pipeline.channelId))\r\n\t\t  <td width=\"16%\"><a href=\"$channelURL\">$channel.name</a></td>\r\n\t\t  #set ($pipelineURL = $homeModule.setTarget(\"pipelineList.vm\").addQueryData(\"pipelineId\", $pipeline.id).addQueryData(\"channelId\", $pipeline.channelId))\r\n    \t  <td width=\"16%\"><a href=\"$pipelineURL\">$pipeline.name</a></td>\r\n          <td width=\"12%\">$!pipeline.parameters.mainstemClientId</td>\r\n\t\t  <td width=\"12%\">$!pipeline.parameters.mainstemBatchsize</td>\r\n\t\t  <td width=\"12%\">$!pipeline.parameters.batchTimeout</td>\r\n\t\t  <td>\r\n\t\t\t#set ($pipelineInfoURL = $homeModule.setTarget(\"pipelineInfo.vm\").addQueryData(\"pipelineId\", $pipeline.id))\r\n    \t\t<a href=\"$pipelineInfoURL\"><img src=\"images/ico_edit.png\" width=\"13\" height=\"13\" /><span class=\"ico_font\">查看</span></a>\r\n\t\t  </td>\r\n\t    </tr>\r\n\t#end\r\n  </table>\r\n</div>\r\n"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/templates/home/screen/canalList.vm",
    "content": "$control.setTemplate(\"home:navigation.vm\")\r\n#set($user = $rundata.request.session.getAttribute(\"managerUser\"))\r\n<script type=\"text/javascript\" src=\"js/trcolor.js\"></script>\r\n<script type=\"text/javascript\" src=\"js/jquery-1.4.2.min.js\"></script> \r\n<script type=\"text/javascript\" src=\"js/jquery.simplemodal-1.4.js\"></script> \r\n\r\n<script language=\"javascript\">\r\n<!--\r\n\tchangeNav(\"datamedia\");\r\n//-->\r\n\t\t\r\n</script>\r\n<!--页面主体-->\r\n<div class=\"main\">\r\n  <div class=\"title\"> \r\n    <h2>Canal配置</h2>\r\n  </div>\r\n  <div class=\"crumbs\"><a href=\"canalList.htm\">Canal配置</a></div>\r\n  <div class=\"crumbs\"></div> \r\n  \r\n   <!--分页表单-->\r\n   <form id=\"pageform\" name=\"pageform\" action=\"$homeModule.setTarget('canalList.vm')\" method=\"post\">\r\n    \t<input type=\"hidden\" id=\"pageIndex\" name=\"pageIndex\" value=\"\"/>\r\n\t\t<input type=\"hidden\" id=\"searchKey\" name=\"searchKey\" value=\"$!searchKey\"/>\r\n   </form>\r\n   <!--Canal搜索-->\r\n   <div class=\"search_o\"> \r\n\t\t<form name=\"search_canal\" action=\"canalList.htm\"  method=\"post\">\r\n\t\t\t##$csrfToken.hiddenField\r\n\t\t\t<div class=\"search_input\">\r\n\t\t\t\t<input name=\"searchKey\" type=\"text\" value=\"请输入关键字(目前支持Canal的ID、名字搜索)\"  onfocus=\"if(this.value == '请输入关键字(目前支持Canal的ID、名字搜索)') {this.value='';}\" onblur=\"if(this.value == '') {this.value = '请输入关键字(目前支持Canal的ID、名字搜索)';}\" />\r\n\t\t\t</div>\r\n\t\t\t<div class=\"search_btn\"><a href=\"javascript:document.search_canal.submit();\"><img src=\"images/search_btn.png\" width=\"39\" height=\"31\" /></a></div>\r\n        </form>\r\n   </div>\r\n  \r\n<!--列表-->\r\n \r\n      <table border=\"0\" cellspacing=\"0\" cellpadding=\"0\" class=\"list changecolor_g\">\r\n         <tr> \r\n            <th width=\"5%\">序号</th>\r\n            <th width=\"12%\">Canal名字</th>\r\n\t\t\t<th width=\"8%\">数据源类型</th>\r\n\t\t\t<th width=\"10%\">编码</th>\r\n\t\t\t<th width=\"12%\">链接配置</th>\r\n        \t<th width=\"24%\">操作</th> \r\n         </tr>\r\n           \r\n\t\t#foreach ($seniorCanal in $seniorCanals)\r\n\t\t\t<tr>\r\n\t\t\t\t<td width=\"5%\">$!seniorCanal.id</td>\r\n\t\t\t\t<td width=\"5%\">$!seniorCanal.name</td>\r\n\t\t\t\t<td width=\"5%\">$!seniorCanal.canalParameter.sourcingType</td>\r\n\t\t\t\t<td width=\"5%\">$!seniorCanal.canalParameter.connectionCharset</td>\r\n\t\t\t\t<td width=\"5%\">$!seniorCanal.getUrl()</td>\r\n\t\t\t\t<td>\r\n\t\t\t\t\t#set ($canalInfoURL = $homeModule.setTarget(\"canalInfo.vm\").addQueryData(\"canalId\", $seniorCanal.id))\r\n\t\t\t\t\t<a href=\"$canalInfoURL\"><img src=\"images/ico_edit.png\" width=\"13\" height=\"13\" /><span class=\"ico_font\">查看</span></a>\r\n\t\t\t\t\t#if($user.authorizeType.isAdmin())\r\n\t\t\t\t\t\t#set ($editURL = $homeModule.setTarget(\"editCanal.vm\").addQueryData(\"canalId\", $seniorCanal.id))\r\n\t\t\t\t\t\t<span class=\"ico_line\">|</span><a href=\"$editURL\"><img src=\"images/ico_edit.png\" width=\"13\" height=\"13\" /><span class=\"ico_font\">编辑</span></a>\r\n\t\t\t\t\t\t#if($seniorCanal.isUsed())\r\n\t\t\t\t\t\t\t<span class=\"ico_line\">|</span><img src=\"images/ico_del.png\" width=\"9\" height=\"9\" /><span class=\"ico_font\" title=\"已被pipeline#foreach($pipeline in $seniorCanal.pipelines)[$pipeline.id]#end使用，清空关联才能删除\">删除</span></td>\r\n\t\t\t\t\t\t#else\r\n\t\t\t\t\t\t\t#set ($removeURL = $homeModule.setAction(\"canalAction\").addQueryData(\"canalId\", $seniorCanal.id).addQueryData(\"eventSubmitDoDelete\", \"true\"))\r\n\t\t\t\t\t\t\t<span class=\"ico_line\">|</span><a href=\"javascript:if(confirm('确实要删除吗?'))location='$removeURL'\" class=\"link del\"><img src=\"images/ico_del.png\" width=\"9\" height=\"9\" /><span class=\"ico_font\">删除</span></a></td>\r\n\t\t\t\t\t\t#end\r\n\t\t\t\t\t#end\r\n\t\t\t\t</td>\r\n\t\t    </tr>\r\n\t\t#end\r\n     </table>\r\n\t\t#if($user.authorizeType.isAdmin())\r\n\t\t\t<div class=\"btn\"><a href=\"$canalAddLink\">添加</a></div>\r\n\t\t#end\r\n\t\t\r\n\t\t<!--分页-->\r\n     <div class=\"page\">共$paginator.items条数据&nbsp;&nbsp;第$paginator.page页/共$paginator.pages页&nbsp;&nbsp; \r\n       \r\n\t   #if($paginator.page == 1)\r\n            <font color=\"999999\">首页</font>\r\n\t   #else\r\n\t\t\t<a href=\"#\" class=\"prev\" onclick=\"pageNavigation(this,1)\">首页</a>\r\n\t   #end\r\n\t   \r\n\t   #if($paginator.page > 1)\r\n\t\t\t#set($pre_page = $paginator.page - 1)\r\n\t\t\t\t<a href=\"#\" class=\"prev\" onclick=\"pageNavigation(this,$pre_page)\">上一页</a>\r\n\t   #else\r\n            <font color=\"999999\">上一页</font>\r\n\t   #end\r\n\t   ##分页下标\r\n\t   #set($counts_keys = $paginator.getSlider(7))\r\n\t   #foreach( $thisPage in $counts_keys)\r\n\t\t\t#if( $thisPage == $paginator.page)\r\n                <b>$thisPage</b>\r\n\t\t\t#else\r\n\t\t\t\t#if($thisPage != 0)\r\n\t\t\t\t\t<a href=\"#\" class=\"num\" onclick=\"pageNavigation(this,$thisPage)\">$thisPage</a> \r\n\t\t\t\t#end\r\n\t\t\t#end\r\n\t   #end\r\n\t   \r\n\t   #if($paginator.page < $paginator.pages)\r\n\t\t\t#set($next_page = $paginator.page + 1)\r\n\t\t\t\t<a href=\"#\" class=\"prev\" onclick=\"pageNavigation(this,$next_page)\">下一页</a>\r\n\t   #else\r\n            <font color=\"999999\">下一页</font>\r\n\t   #end\r\n\t   \r\n\t   #if($paginator.page == $paginator.pages)\r\n            <font color=\"999999\">末页</font>\r\n\t   #else\r\n\t\t\t<a href=\"#\" class=\"prev\" onclick=\"pageNavigation(this,$paginator.pages)\">末页</a>\r\n\t   #end\r\n     </div>     \r\n        \r\n</div>"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/templates/home/screen/channelInfo.vm",
    "content": "$control.setTemplate(\"home:navigation.vm\")\r\n\r\n<div class=\"main\">\r\n <div class=\"title\"><h2>Channel信息</h2></div>\r\n <div class=\"crumbs\">Channel信息</div>\r\n <div class=\"setting_box\">\r\n <table cellpadding=\"0\" cellspacing=\"0\" class=\"setting\">\r\n  <tr>\r\n  <th>Channel序号：</th><td>$!channel.id</td>\r\n  </tr>\r\n  <tr>\r\n  <th>Channel名字：</th><td>$!channel.name</td>\r\n  </tr>\r\n  <tr>\r\n  <th>同步方向：</th><td>#if($channel.pipelines.size() == 0) 无 \r\n\t\t\t\t#elseif($channel.pipelines.size() == 1) 单向\r\n\t\t\t\t#elseif($channel.pipelines.size() == 2) 双向\r\n\t\t\t\t#end</td>\r\n  </tr>\r\n  <tr>\r\n  <th>运行状态：</th><td>#if($channel.status.isStart()) 运行 #elseif($channel.status.isPause()) 挂起 #elseif($channel.status.isStop()) 停止 #end</td>\r\n  </tr>\r\n  <tr>\r\n  <th>同步一致性：</th><td>#if ($channel.parameters.SyncConsistency.isMedia()) 基于数据库反查 #elseif($channel.parameters.SyncConsistency.isBase()) 基于当前日志变更 #end</td>\r\n  </tr>\r\n  <tr>\r\n  <th>同步模式：</th><td>#if($channel.parameters.syncMode.isRow()) 行记录同步 #else 列记录同步 #end</td>\r\n  </tr>\r\n  <tr>\r\n  <th>是否开启数据一致性：</th><td>#if($channel.parameters.isEnableRemedy()) 开启 #else 关闭 #end</td>\r\n  </tr>\r\n  #if($channel.parameters.isEnableRemedy())\r\n  <tr>\r\n  <th>一致性算法：</th><td>#if($channel.parameters.remedyAlgorithm.isLoopback()) 单向回环补救 #else 时间交集补救 #end</td>\r\n  </tr>\r\n  <tr>\r\n  <th>一致性反查数据库延迟阀值：</th><td>$!numberFormat.format($channel.parameters.remedyDelayThresoldForMedia) 秒</td>\r\n  </tr\r\n  #end\r\n  <tr>\r\n  <th>描述信息：</th><td>$!channel.description</td>\r\n  </tr>\r\n </table>\r\n </div>\r\n</div>\r\n"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/templates/home/screen/channelList.vm",
    "content": "$control.setTemplate(\"home:navigation.vm\")\r\n#set($user = $rundata.request.session.getAttribute(\"managerUser\"))\r\n<script type=\"text/javascript\" src=\"js/trcolor.js\"></script>\r\n<script type=\"text/javascript\" src=\"js/jquery-1.4.2.min.js\"></script> \r\n<script type=\"text/javascript\" src=\"js/jquery.simplemodal-1.4.js\"></script> \r\n<script language=\"javascript\">\r\n<!--\r\n\tchangeNav(\"sync\");\r\n    $(function () {\r\n        $('.bubbleInfo').each(function () {\r\n            var distance = 10;\r\n            var time = 250;\r\n            var hideDelay = 200;\r\n\r\n            var hideDelayTimer = null;\r\n\r\n            var beingShown = false;\r\n            var shown = false;\r\n            var trigger = $('.trigger', this);\r\n            var info = $('.popup', this).css('opacity', 0);\r\n\r\n\r\n            $([trigger.get(0), info.get(0)]).mouseover(function () {\r\n                if (hideDelayTimer) clearTimeout(hideDelayTimer);\r\n                if (beingShown || shown) {\r\n                    // don't trigger the animation again\r\n                    return;\r\n                } else {\r\n                    // reset position of info box\r\n                    beingShown = true;\r\n\r\n                    info.css({\r\n                        top: 10,\r\n                        left: 10,\r\n                        display: 'block'\r\n                    }).animate({\r\n                        top: '+=' + distance + 'px',\r\n                        opacity: 1\r\n                    }, time, 'swing', function() {\r\n                        beingShown = false;\r\n                        shown = true;\r\n                    });\r\n                }\r\n\r\n                return false;\r\n            }).mouseout(function () {\r\n                if (hideDelayTimer) clearTimeout(hideDelayTimer);\r\n                hideDelayTimer = setTimeout(function () {\r\n                    hideDelayTimer = null;\r\n                    info.animate({\r\n                        top: '-=' + distance + 'px',\r\n                        opacity: 0\r\n                    }, time, 'swing', function () {\r\n                        shown = false;\r\n                        info.css('display', 'none');\r\n                    });\r\n\r\n                }, hideDelay);\r\n\r\n                return false;\r\n            });\r\n        });\r\n    });\r\n    \r\n    //-->\r\n\t\t\r\n</script>\r\n\r\n<!--页面主体-->\r\n<div class=\"main\">\r\n   <div class=\"title\">\r\n    <h2>Channel管理</h2>\r\n  </div>\r\n   <div class=\"crumbs\"><a href=\"channelList.htm\">Channel管理</a></div> \r\n   \r\n   <!--分页表单-->\r\n   <form id=\"pageform\" name=\"pageform\" action=\"$homeModule.setTarget('channelList.vm')\" method=\"post\">\r\n    \t<input type=\"hidden\" id=\"pageIndex\" name=\"pageIndex\" value=\"\"/>\r\n\t\t<input type=\"hidden\" id=\"searchKey\" name=\"searchKey\" value=\"$!searchKey\"/>\r\n   </form>\r\n   \r\n   <!--Channel搜索-->\r\n   <div class=\"search_o\"> \r\n\t\t<form name=\"search_channel\" action=\"channelList.htm\" method=\"post\">\r\n\t\t\t##$csrfToken.hiddenField\r\n\t\t\t<div class=\"search_input\">\r\n\t\t\t\t<input name=\"searchKey\" type=\"text\" value=\"请输入关键字(目前支持Channel的ID、名字搜索)\"  onfocus=\"if(this.value == '请输入关键字(目前支持Channel的ID、名字搜索)') {this.value='';}\" onblur=\"if(this.value == '') {this.value = '请输入关键字(目前支持Channel的ID、名字搜索)';}\" />\r\n\t\t\t</div>\r\n\t\t\t<div class=\"search_btn\"><a href=\"javascript:document.search_channel.submit();\"><img src=\"images/search_btn.png\" width=\"39\" height=\"31\" /></a></div>\r\n        </form>\r\n   </div>\r\n   <!--列表-->\r\n    \r\n  <table border=\"0\" cellspacing=\"0\" cellpadding=\"0\" class=\"list changecolor_w\">\r\n    <tr> \r\n      <th>序号</th>\r\n      <th>Channel名字</th>\r\n      <th>运行状态</th>\r\n\t  <th>同步方向</th>\r\n\t  <th>操作</th>\r\n    </tr>\r\n    \r\n    #foreach ($channel in $channels)\r\n\t\t<tr> \r\n\t      <td width=\"5%\">$channel.id</td>\r\n\t      <td width=\"16%\">\r\n\t\t\t#set ($pipelineURL = $homeModule.setTarget(\"pipelineList.vm\").addQueryData(\"channelId\", $channel.id))\r\n\t\t\t<a href=\"$pipelineURL\" id=\"download\" class=\"trigger\">$channel.name</a>\r\n\t      </td>\r\n\t      \r\n\t      \t<td>\r\n                #if($channel.status.isStart())  <a href=\"\" style=\"color:green\">运行</a>\r\n                #elseif($channel.status.isPause())  <a href=\"\" style=\"color:red\">挂起</a>\r\n\t\t\t\t#else <a href=\"\" style=\"color:gray\">停止</a>\r\n\t\t\t\t#end\r\n\t\t\t</td>\r\n\t      <td>\r\n\t\t\t\t#if($channel.pipelines.size() == 0) 无 \r\n\t\t\t\t#elseif($channel.pipelines.size() == 1) 单向\r\n\t\t\t\t#elseif($channel.pipelines.size() == 2) 双向\r\n\t\t\t\t#end\r\n\t\t\t</td>\r\n\t     \r\n\t\t  \r\n\t\t  <td><p>\r\n        \t\t\t\t#set ($channelInfoURL = $homeModule.setTarget(\"channelInfo.vm\").addQueryData(\"channelId\", $channel.id))\r\n\t\t\t\t\t\t<a href=\"$channelInfoURL\"><img src=\"images/ico_edit.png\" width=\"13\" height=\"13\" /><span class=\"ico_font\">查看</span></a>\r\n\t\t\t\t\t\t\r\n\t\t\t\t\t\t#set ($notifyURL = $homeModule.setAction(\"channelAction\").addQueryData(\"channelId\", $channel.id).addQueryData(\"pageIndex\", $paginator.page).addQueryData(\"searchKey\", $!searchKey).addQueryData(\"eventSubmitDoNotify\", \"true\").render())\r\n    \t\t\t\t\t#if($user.authorizeType.isAdmin())\r\n\t\t\t\t\t\t\t#if($channel.status.isStart() || $channel.status.isPause())\r\n\t\t\t\t\t\t\t\t#set ($stopURL = $homeModule.setTarget(\"channelList.vm\").setAction(\"channelAction\").addQueryData(\"channelId\", $channel.id).addQueryData(\"status\", \"stop\").addQueryData(\"pageIndex\", $paginator.page).addQueryData(\"searchKey\", $!searchKey).addQueryData(\"eventSubmitDoStatus\", \"true\"))\r\n\t\t\t\t\t\t\t\t<span class=\"ico_line\">|</span><a href=\"javascript:if(confirm('确实关闭Channel吗?'))location='$stopURL'\"><img src=\"images/ico_edit.png\" width=\"13\" height=\"13\" /><span class=\"ico_font\">停用</span></a>\r\n\t\t\t\t\t\t\t\t<span class=\"ico_line\">|</span><a href=\"javascript:if(confirm('确实推送Channel吗?'))location='$notifyURL'\"><img src=\"images/ico_edit.png\" width=\"13\" height=\"13\" /><span class=\"ico_font\">推送</span></a>\r\n\t\t\t\t\t\t\t#end\r\n\t\t\t\t\t\t\t#if($channel.status.isStop() || $channel.status.isPause())\r\n\t\t\t\t\t\t\t\t#set($flag_go = false)\r\n\t\t\t\t\t\t\t\t#set($flag_back = false)\r\n\t\t\t\t\t\t\t\t#set($delFlag = false)\r\n\t\t\t\t\t\t\t\t#set($num = 1)\r\n\t\t\t\t\t\t\t\t#foreach($pipeline in $channel.pipelines)\r\n\t\t\t\t\t\t\t\t\t#set($delFlag = true)\r\n\t\t\t\t\t\t\t\t\t#foreach($pair in $pipeline.pairs)\r\n\t\t\t\t\t\t\t\t\t\t#if($num == 1)\r\n\t\t\t\t\t\t\t\t\t\t\t#set($flag_go = true)\r\n\t\t\t\t\t\t\t\t\t\t#elseif($num == 2)\r\n\t\t\t\t\t\t\t\t\t\t\t#set($flag_back = true)\r\n\t\t\t\t\t\t\t\t\t\t#end\r\n\t\t\t\t\t\t\t\t\t\t\r\n\t\t\t\t\t\t\t\t\t#end\r\n\t\t\t\t\t\t\t\t\t#set($num = 2)\r\n\t\t\t\t\t\t\t\t#end\r\n\t\t\t\t\t\t\t\t<!-- 判断Pipeline的S.E.L模块关联的NODE节点启动状态 -->\r\n\t\t\t\t\t\t\t\t\t#set($sflag_go = false)\r\n\t\t\t\t\t\t\t\t\t#set($eflag_go = false)\r\n\t\t\t\t\t\t\t\t\t#set($lflag_go = false)\r\n\t\t\t\t\t\t\t\t\t\r\n\t\t\t\t\t\t\t\t\t#set($sflag_back = false)\r\n\t\t\t\t\t\t\t\t\t#set($eflag_back = false)\r\n\t\t\t\t\t\t\t\t\t#set($lflag_back = false)\r\n\t\t\t\t\t\t\t\t\t\r\n\t\t\t\t\t\t\t\t\t#set($num = 1)\r\n\t\t\t\t\t\t\t\t\t#foreach($pipeline in $channel.pipelines)\r\n\t\t\t\t\t\t\t\t\t\t#foreach($selectNode in $pipeline.selectNodes)\r\n\t\t\t\t\t\t\t\t\t\t\t#if($selectNode.status.isStart())\r\n\t\t\t\t\t\t\t\t\t\t\t\t#if($num == 1)\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t#set($sflag_go = true)\r\n\t\t\t\t\t\t\t\t\t\t\t\t#elseif($num == 2)\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t#set($sflag_back = true)\r\n\t\t\t\t\t\t\t\t\t\t\t\t#end\r\n\t\t\t\t\t\t\t\t\t\t\t#end\r\n\t\t\t\t\t\t\t\t\t\t#end\r\n\t\t\t\t\t\t\t\t\t\t#foreach($extractNode in $pipeline.extractNodes)\r\n\t\t\t\t\t\t\t\t\t\t\t#if($extractNode.status.isStart())\r\n\t\t\t\t\t\t\t\t\t\t\t\t#if($num == 1)\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t#set($eflag_go = true)\r\n\t\t\t\t\t\t\t\t\t\t\t\t#elseif($num == 2)\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t#set($eflag_back = true)\r\n\t\t\t\t\t\t\t\t\t\t\t\t#end\r\n\t\t\t\t\t\t\t\t\t\t\t#end\r\n\t\t\t\t\t\t\t\t\t\t#end\r\n\t\t\t\t\t\t\t\t\t\t#foreach($loadNode in $pipeline.loadNodes)\r\n\t\t\t\t\t\t\t\t\t\t\t#if($loadNode.status.isStart())\r\n\t\t\t\t\t\t\t\t\t\t\t\t#if($num == 1)\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t#set($lflag_go = true)\r\n\t\t\t\t\t\t\t\t\t\t\t\t#elseif($num == 2)\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t#set($lflag_back = true)\r\n\t\t\t\t\t\t\t\t\t\t\t\t#end\r\n\t\t\t\t\t\t\t\t\t\t\t#end\r\n\t\t\t\t\t\t\t\t\t\t#end\r\n\t\t\t\t\t\t\t\t\t\t#set($num = 2)\r\n\t\t\t\t\t\t\t\t\t#end\r\n\t\t\t\t\t\t\t\t\t\r\n\t\t\t\t\t\t\t\t\t#if($channel.pipelines.size() == 1)\r\n\t\t\t\t\t\t\t\t\t\t#set($flag = $flag_go)\r\n\t\t\t\t\t\t\t\t\t\t#set($sflag = $sflag_go)\r\n\t\t\t\t\t\t\t\t\t\t#set($eflag = $eflag_go)\r\n\t\t\t\t\t\t\t\t\t\t#set($lflag = $lflag_go)\r\n\t\t\t\t\t\t\t\t\t#elseif($channel.pipelines.size() == 2)\r\n\t\t\t\t\t\t\t\t\t\t#set($flag = $flag_go && $flag_back)\r\n\t\t\t\t\t\t\t\t\t\t#set($sflag = $sflag_go && $sflag_back)\r\n\t\t\t\t\t\t\t\t\t\t#set($eflag = $eflag_go && $eflag_back)\r\n\t\t\t\t\t\t\t\t\t\t#set($lflag = $lflag_go && $lflag_back)\r\n\t\t\t\t\t\t\t\t\t#end\r\n\t\t\t\t\t\t\t\t\t\r\n\t\t\t\t\t\t\t\t#if($channel.status.isStop())\r\n\t\t\t\t\t\t\t\t\t#if($flag)\r\n\t\t\t\t\t\t\t\t\t\t#if($sflag && $eflag && $lflag)\r\n\t\t\t\t\t\t\t\t\t\t\t#if($channel.processEmpty)\r\n        \t\t\t\t\t\t\t\t\t\t#set ($startURL = $homeModule.setAction(\"channelAction\").addQueryData(\"channelId\", $channel.id).addQueryData(\"status\", \"start\").addQueryData(\"pageIndex\", $paginator.page).addQueryData(\"searchKey\", $!searchKey).addQueryData(\"eventSubmitDoStatus\", \"true\"))\r\n        \t\t\t\t\t\t\t\t\t\t<span class=\"ico_line\">|</span><a href=\"javascript:if(confirm('确实开启Channel吗?'))location='$startURL'\"><img src=\"images/ico_edit.png\" width=\"13\" height=\"13\" /><span class=\"ico_font\">启用</span></a>\r\n    \t\t\t\t\t\t\t\t\t\t#else\r\n    \t\t\t\t\t\t\t\t\t\t\t<span class=\"ico_line\">|</span><img src=\"images/ico_edit.png\" width=\"13\" height=\"13\" /><span title=\"process里面还有同步数据，不用启用channel\" class=\"ico_font\">启用</span>\r\n    \t\t\t\t\t\t\t\t\t\t#end\r\n    \t\t\t\t\t\t\t\t\t#else\r\n    \t\t\t\t\t\t\t\t\t\t<span class=\"ico_line\">|</span><img src=\"images/ico_edit.png\" width=\"13\" height=\"13\" /><span class=\"ico_font\" title=\"#if(!$sflag)S.#end #if(!$eflag)E.#end #if(!$lflag)L#end 节点未启动\">启用</span>\r\n    \t\t\t\t\t\t\t\t\t#end\r\n\t\t\t\t\t\t\t\t\t#else\r\n\t\t\t\t\t\t\t\t\t\t#if(!$delFlag)\r\n\t\t\t\t\t\t\t\t\t\t\t<span class=\"ico_line\">|</span><img src=\"images/ico_edit.png\" width=\"13\" height=\"13\" /><span class=\"ico_font\" title=\"pipeline没有配置，不能启动\">启用</span>\r\n\t\t\t\t\t\t\t\t\t\t#else\r\n\t\t\t\t\t\t\t\t\t\t\t<span class=\"ico_line\">|</span><img src=\"images/ico_edit.png\" width=\"13\" height=\"13\" /><span class=\"ico_font\" title=\"映射关系没有配置，不能启动\">启用</span>\r\n\t\t\t\t\t\t\t\t\t\t#end\r\n\t\t\t\t\t\t\t\t\t\t\r\n\t\t\t\t\t\t\t\t\t#end\r\n\t\t\t\t\t\t\t\t\t\r\n\t\t\t\t\t\t\t\t#end\r\n\t\t\t\t\t\t\t\t\r\n\t\t\t\t\t\t\t\t#if($flag && $channel.status.isPause())\r\n\t\t\t\t\t\t\t\t\t#if($sflag && $eflag && $lflag)\r\n\t\t\t\t\t\t\t\t\t\t#if($channel.processEmpty)\r\n\t\t\t\t\t\t\t\t\t\t\t#set ($startURL = $homeModule.setAction(\"channelAction\").addQueryData(\"channelId\", $channel.id).addQueryData(\"status\", \"start\").addQueryData(\"pageIndex\", $paginator.page).addQueryData(\"searchKey\", $!searchKey).addQueryData(\"eventSubmitDoStatus\", \"true\"))\r\n\t\t\t\t\t\t\t\t\t\t\t<span class=\"ico_line\">|</span><a href=\"javascript:if(confirm('确实恢复Channel吗?'))location='$startURL'\"><img src=\"images/ico_edit.png\" width=\"13\" height=\"13\" /><span class=\"ico_font\">解挂</span></a>\r\n\t\t\t\t\t\t\t\t\t\t#else\r\n\t\t\t\t\t\t\t\t\t\t\t<span class=\"ico_line\">|</span><img src=\"images/ico_edit.png\" width=\"13\" height=\"13\" /><span title=\"process里面还有同步数据，请直接停止channel\" class=\"ico_font\">解挂</span>\r\n\t\t\t\t\t\t\t\t\t\t#end\r\n\t\t\t\t\t\t\t\t\t#else\r\n\t\t\t\t\t\t\t\t\t\t<span class=\"ico_line\">|</span><img src=\"images/ico_edit.png\" width=\"13\" height=\"13\" /><span title=\"#if(!$sflag)S.#end #if(!$eflag)E.#end #if(!$lflag)L#end 节点未启动\" class=\"ico_font\">解挂</span>\r\n\t\t\t\t\t\t\t\t\t#end\r\n\t\t\t\t\t\t\t\t#end\r\n\t\t\t\t\t\t\t\t#if($channel.status.isPause())\r\n    \t\t\t\t\t\t\t\t<span class=\"ico_line\">|</span><img src=\"images/ico_edit.png\" width=\"13\" height=\"13\" /><span title=\"Channel在挂起的时候不能编辑\" class=\"ico_font\">编辑</span>\r\n\t\t\t\t\t\t\t\t#else\r\n\t\t\t\t\t\t\t\t\t#set ($editURL = $homeModule.setTarget(\"editChannel.vm\").addQueryData(\"channelId\", $channel.id).addQueryData(\"pageIndex\", $!paginator.page).addQueryData(\"searchKey\", $!searchKey))\r\n    \t\t\t\t\t\t\t\t<span class=\"ico_line\">|</span><a href=\"$editURL\"><img src=\"images/ico_edit.png\" width=\"13\" height=\"13\" /><span class=\"ico_font\">编辑</span></a>\r\n\t\t\t\t\t\t\t\t#end\r\n\t\t\t\t\t\t\t\t\r\n\t\t\t\t\t\t\t\t<span class=\"ico_line\">|</span><a href=\"javascript:if(confirm('确实推送Channel吗?'))location='$notifyURL'\"><img src=\"images/ico_edit.png\" width=\"13\" height=\"13\" /><span class=\"ico_font\">推送</span></a>\r\n\t\t\t\t\t\t\t\t\r\n\t\t\t\t\t\t\t\t#if($delFlag)\r\n    \t\t\t\t\t\t\t\t<span class=\"ico_line\">|</span><img src=\"images/ico_del.png\" width=\"9\" height=\"9\"  /><span class=\"ico_font\" title=\"channel必须不包含pipeline才能执行删除\">删除</span>\r\n\t\t\t\t\t\t\t\t#else\r\n\t\t\t\t\t\t\t\t\t#set ($removeURL = $homeModule.setAction(\"channelAction\").addQueryData(\"channelId\", $channel.id).addQueryData(\"pageIndex\", $!paginator.page).addQueryData(\"searchKey\", $!searchKey).addQueryData(\"eventSubmitDoDelete\", \"true\"))\r\n    \t\t\t\t\t\t\t\t<span class=\"ico_line\">|</span><a href=\"javascript:if(confirm('确实要删除吗?'))location='$removeURL'\" class=\"link del\" ><img src=\"images/ico_del.png\" width=\"9\" height=\"9\" /><span class=\"ico_font\">删除</span></a>\r\n\t\t\t\t\t\t\t\t#end\r\n\t\t\t\t\t\t\t#end\r\n\t\t\t\t\t\t#end\r\n    \t  </p></td>\r\n\t    </tr>\r\n\t#end\r\n  \r\n    \r\n    \r\n  </table>\r\n  <!--常规按钮-->\r\n\t  #if($user.authorizeType.isAdmin())\r\n\t\t<div class=\"btn\"><a href=\"$channelAddLink\">添加</a></div>\r\n\t  #end\r\n      \r\n     <!--分页-->\r\n     <div class=\"page\">共$paginator.items条数据&nbsp;&nbsp;第$paginator.page页/共$paginator.pages页&nbsp;&nbsp; \r\n       \r\n\t   #if($paginator.page == 1)\r\n            <font color=\"999999\">首页</font>\r\n\t   #else\r\n\t\t\t<a href=\"#\" class=\"prev\" onclick=\"pageNavigation(this,1)\">首页</a>\r\n\t   #end\r\n\t   \r\n\t   #if($paginator.page > 1)\r\n\t\t\t#set($pre_page = $paginator.page - 1)\r\n\t\t\t\t<a href=\"#\" class=\"prev\" onclick=\"pageNavigation(this,$pre_page)\">上一页</a>\r\n\t   #else\r\n            <font color=\"999999\">上一页</font>\r\n\t   #end\r\n\t   ##分页下标\r\n\t   #set($counts_keys = $paginator.getSlider(7))\r\n\t   #foreach( $thisPage in $counts_keys)\r\n\t\t\t#if( $thisPage == $paginator.page)\r\n                <b>$thisPage</b>\r\n\t\t\t#else\r\n\t\t\t\t#if($thisPage != 0)\r\n\t\t\t\t\t<a href=\"#\" class=\"num\" onclick=\"pageNavigation(this,$thisPage)\">$thisPage</a> \r\n\t\t\t\t#end\r\n\t\t\t#end\r\n\t   #end\r\n\t   \r\n\t   #if($paginator.page < $paginator.pages)\r\n\t\t\t#set($next_page = $paginator.page + 1)\r\n\t\t\t\t<a href=\"#\" class=\"prev\" onclick=\"pageNavigation(this,$next_page)\">下一页</a>\r\n\t   #else\r\n            <font color=\"999999\">下一页</font>\r\n\t   #end\r\n\t   \r\n\t   #if($paginator.page == $paginator.pages)\r\n            <font color=\"999999\">末页</font>\r\n\t   #else\r\n\t\t\t<a href=\"#\" class=\"prev\" onclick=\"pageNavigation(this,$paginator.pages)\">末页</a>\r\n\t   #end\r\n     </div>    \r\n</div>\r\n\r\n<script language=\"javascript\">\r\n#if($!errorType == \"DDL\")\r\n\talert(\"一个channel中只允许开启单向ddl同步!\");\r\n#elseif($!errorType == \"HOME\")\r\n\talert(\"一个channel中只允许单向开启为主站点!\");\r\n#end\r\n</script>"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/templates/home/screen/checkDelayStat.vm",
    "content": "$rundata.setLayoutEnabled(false)\r\n\r\n$!result"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/templates/home/screen/dataMatrixInfo.vm",
    "content": "$control.setTemplate(\"home:navigation.vm\")\r\n#set($user = $rundata.request.session.getAttribute(\"managerUser\"))\r\n\r\n<div class=\"main\">\r\n <div class=\"title\"><h2>主备配置信息</h2></div>\r\n <div class=\"crumbs\">主备配置信息</div>\r\n <div class=\"setting_box\">\r\n <table cellpadding=\"0\" cellspacing=\"0\" class=\"setting\">\r\n  <tr>\r\n  <th>序号：</th><td>$!dataMatrix.id</td>\r\n  </tr>\r\n  <tr>\r\n  <th>groupKey：</th><td>$!dataMatrix.groupKey</td>\r\n  </tr>\r\n  <tr>\r\n  <th>master：</th><td>$!dataMatrix.master</td>\r\n  </tr>\r\n  <tr>\r\n  <th>slave：</th><td>$!dataMatrix.slave</td>\r\n  </tr>\r\n  <tr>\r\n  <th>描述信息：</th><td>$!dataMatrix.description</td>\r\n  </tr>\r\n </table>\r\n </div>\r\n\r\n <table border=\"0\" cellspacing=\"0\" cellpadding=\"0\" class=\"list changecolor_w\" width=\"60%\">\r\n    <tr>\r\n\t  <th>序号</th>\r\n      <th>数据源名字</th>\r\n      <th>类型</th>\r\n\t  <th>编码</th>\r\n      <th>URL</th>\r\n\t  <th>操作</th>\r\n    </tr>\r\n    \r\n    #foreach ($dataSource in $dataSources)\r\n\t\t<tr> \r\n\t\t  <td width=\"5%\">$!dataSource.id</td>\r\n          <td width=\"12%\">$!dataSource.name</td>\r\n          <td>$!dataSource.type</td>\r\n          <td>$!dataSource.encode</td>\r\n          <td>$!dataSource.url</td>\r\n\t\t  <td>\r\n\t\t\t#set ($dataSourceInfoURL = $homeModule.setTarget(\"dataSourceInfo.vm\").addQueryData(\"dataMediaSourceId\", $dataSource.id))\r\n\t\t\t<a href=\"$dataSourceInfoURL\"><img src=\"images/ico_edit.png\" width=\"13\" height=\"13\" /><span class=\"ico_font\">查看</span></a>\r\n    \t\t#if($user.authorizeType.isAdmin())\r\n        \t\t#set ($editURL = $homeModule.setTarget(\"editDataSource.vm\").addQueryData(\"dataMediaSourceId\", $dataSource.id))\r\n    \t\t<span class=\"ico_line\">|</span><a href=\"$editURL\"><img src=\"images/ico_edit.png\" alt=\" \" width=\"13\" height=\"13\" /><span class=\"ico_font\">编辑</span></a>\r\n    \t\t#end\r\n\t\t  </td>\r\n\t    </tr>\r\n\t#end\r\n  </table>\r\n</div>"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/templates/home/screen/dataMatrixList.vm",
    "content": "$control.setTemplate(\"home:navigation.vm\")\r\n#set($user = $rundata.request.session.getAttribute(\"managerUser\"))\r\n<script type=\"text/javascript\" src=\"js/trcolor.js\"></script>\r\n<script type=\"text/javascript\" src=\"js/jquery-1.4.2.min.js\"></script> \r\n<script type=\"text/javascript\" src=\"js/jquery.simplemodal-1.4.js\"></script> \r\n\r\n<script language=\"javascript\">\r\n<!--\r\n\tchangeNav(\"datamedia\");\r\n//-->\r\n\t\t\r\n</script>\r\n<!--页面主体-->\r\n<div class=\"main\">\r\n  <div class=\"title\"> \r\n    <h2>主备设置列表</h2>\r\n  </div>\r\n  <div class=\"crumbs\"><a href=\"dataMatrixList.htm\">主备设置列表</a></div>\r\n  <div class=\"crumbs\"></div> \r\n  \r\n   <!--分页表单-->\r\n   <form id=\"pageform\" name=\"pageform\" action=\"$homeModule.setTarget('dataMatrixList.vm')\" method=\"post\">\r\n    \t<input type=\"hidden\" id=\"pageIndex\" name=\"pageIndex\" value=\"\"/>\r\n\t\t<input type=\"hidden\" id=\"searchKey\" name=\"searchKey\" value=\"$!searchKey\"/>\r\n   </form>\r\n   <!--dataMatrix搜索-->\r\n   <div class=\"search_o\"> \r\n\t\t<form name=\"search_dataMatrix\" action=\"dataMatrixList.htm\"  method=\"post\">\r\n\t\t\t##$csrfToken.hiddenField\r\n\t\t\t<div class=\"search_input\">\r\n\t\t\t\t<input name=\"searchKey\" type=\"text\" value=\"请输入关键字(目前支持Matrx的ID、名字搜索)\"  onfocus=\"if(this.value == '请输入关键字(目前支持Matrx的ID、名字搜索)') {this.value='';}\" onblur=\"if(this.value == '') {this.value = '请输入关键字(目前支持Matrx的ID、名字搜索)';}\" />\r\n\t\t\t</div>\r\n\t\t\t<div class=\"search_btn\"><a href=\"javascript:document.search_dataMatrix.submit();\"><img src=\"images/search_btn.png\" width=\"39\" height=\"31\" /></a></div>\r\n        </form>\r\n   </div>\r\n  \r\n<!--列表-->\r\n \r\n      <table border=\"0\" cellspacing=\"0\" cellpadding=\"0\" class=\"list changecolor_g\">\r\n         <tr> \r\n            <th width=\"8%\">序号</th>\r\n            <th width=\"16%\">groupKey</th>\r\n\t\t\t<th width=\"16%\">master</th>\r\n\t\t\t<th width=\"16%\">slave</th>\r\n\t\t\t<th width=\"20%\">最后修改时间</th>\r\n        \t<th width=\"24%\">操作</th> \r\n         </tr>\r\n           \r\n\t\t#foreach ($dataMatrix in $dataMatrixs)\r\n\t\t\t<tr>\r\n\t\t\t\t<td width=\"5%\">$!dataMatrix.id</td>\r\n\t\t\t\t<td width=\"5%\">$!dataMatrix.groupKey</td>\r\n\t\t\t\t<td width=\"5%\">$!dataMatrix.master</td>\r\n\t\t\t\t<td width=\"5%\">$!dataMatrix.slave</td>\r\n\t\t\t\t<td width=\"8%\">$!numberFormat.format($!dataMatrix.gmtModified)</td>\r\n\t\t\t\t<td>\r\n\t\t\t\t\t#set ($dataMatrixInfoURL = $homeModule.setTarget(\"dataMatrixInfo.vm\").addQueryData(\"matrixId\", $dataMatrix.id))\r\n\t\t\t\t\t<a href=\"$dataMatrixInfoURL\"><img src=\"images/ico_edit.png\" width=\"13\" height=\"13\" /><span class=\"ico_font\">查看</span></a>\r\n\t\t\t\t\t#if($user.authorizeType.isAdmin())\r\n\t\t\t\t\t\t#set ($editURL = $homeModule.setTarget(\"editDataMatrix.vm\").addQueryData(\"matrixId\", $dataMatrix.id))\r\n\t\t\t\t\t\t<span class=\"ico_line\">|</span><a href=\"$editURL\"><img src=\"images/ico_edit.png\" width=\"13\" height=\"13\" /><span class=\"ico_font\">编辑</span></a>\r\n\t\t\t\t\t\t#set ($switchRL = $homeModule.setAction(\"dataMatrixAction\").addQueryData(\"matrixId\", $dataMatrix.id).addQueryData(\"eventSubmitDoSwitch\", \"true\"))\r\n\t\t\t\t\t\t<span class=\"ico_line\">|</span><a href=\"javascript:if(confirm('确实要执行主备IP切换吗?'))location='$switchRL'\"><img src=\"images/ico_edit.png\" width=\"13\" height=\"13\" /><span class=\"ico_font\">切换</span></a>\r\n\t\t\t\t\t\t#if($!dataMatrix.isUsed())\r\n            \t\t\t\t<span class=\"ico_line\">|</span><img src=\"images/ico_del.png\" width=\"9\" height=\"9\" /><span class=\"ico_font\" title=\"已被数据源使用，清空关联才能删除\">删除</span>\r\n            \t\t\t#else\r\n\t\t\t\t\t\t\t#set ($removeURL = $homeModule.setAction(\"dataMatrixAction\").addQueryData(\"matrixId\", $dataMatrix.id).addQueryData(\"eventSubmitDoDelete\", \"true\"))\r\n\t\t\t\t\t\t\t<span class=\"ico_line\">|</span><a href=\"javascript:if(confirm('确实要删除吗?'))location='$removeURL'\" class=\"link del\"><img src=\"images/ico_del.png\" width=\"9\" height=\"9\" /><span class=\"ico_font\">删除</span></a></td>\r\n\t\t\t\t\t\t#end\r\n\t\t\t\t\t#end\r\n\t\t\t\t</td>\r\n\t\t    </tr>\r\n\t\t#end\r\n     </table>\r\n\t\t#if($user.authorizeType.isAdmin())\r\n\t\t\t#set ($addURL = $homeModule.setTarget(\"addDataMatrix.vm\"))\r\n\t\t\t<div class=\"btn\"><a href=\"$addURL\">添加</a></div>\r\n\t\t#end\r\n\t\t\r\n\t\t<!--分页-->\r\n     <div class=\"page\">共$paginator.items条数据&nbsp;&nbsp;第$paginator.page页/共$paginator.pages页&nbsp;&nbsp; \r\n       \r\n\t   #if($paginator.page == 1)\r\n            <font color=\"999999\">首页</font>\r\n\t   #else\r\n\t\t\t<a href=\"#\" class=\"prev\" onclick=\"pageNavigation(this,1)\">首页</a>\r\n\t   #end\r\n\t   \r\n\t   #if($paginator.page > 1)\r\n\t\t\t#set($pre_page = $paginator.page - 1)\r\n\t\t\t\t<a href=\"#\" class=\"prev\" onclick=\"pageNavigation(this,$pre_page)\">上一页</a>\r\n\t   #else\r\n            <font color=\"999999\">上一页</font>\r\n\t   #end\r\n\t   ##分页下标\r\n\t   #set($counts_keys = $paginator.getSlider(7))\r\n\t   #foreach( $thisPage in $counts_keys)\r\n\t\t\t#if( $thisPage == $paginator.page)\r\n                <b>$thisPage</b>\r\n\t\t\t#else\r\n\t\t\t\t#if($thisPage != 0)\r\n\t\t\t\t\t<a href=\"#\" class=\"num\" onclick=\"pageNavigation(this,$thisPage)\">$thisPage</a> \r\n\t\t\t\t#end\r\n\t\t\t#end\r\n\t   #end\r\n\t   \r\n\t   #if($paginator.page < $paginator.pages)\r\n\t\t\t#set($next_page = $paginator.page + 1)\r\n\t\t\t\t<a href=\"#\" class=\"prev\" onclick=\"pageNavigation(this,$next_page)\">下一页</a>\r\n\t   #else\r\n            <font color=\"999999\">下一页</font>\r\n\t   #end\r\n\t   \r\n\t   #if($paginator.page == $paginator.pages)\r\n            <font color=\"999999\">末页</font>\r\n\t   #else\r\n\t\t\t<a href=\"#\" class=\"prev\" onclick=\"pageNavigation(this,$paginator.pages)\">末页</a>\r\n\t   #end\r\n     </div>     \r\n        \r\n</div>"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/templates/home/screen/dataMediaInfo.vm",
    "content": "$control.setTemplate(\"home:navigation.vm\")\r\n\r\n<div class=\"main\">\r\n <div class=\"title\"><h2>数据表信息</h2></div>\r\n <div class=\"crumbs\">数据表信息</div>\r\n <div class=\"setting_box\">\r\n <table cellpadding=\"0\" cellspacing=\"0\" class=\"setting\">\r\n  <tr>\r\n  <th>数据表序号：</th><td>$!dataMedia.id</td>\r\n  </tr>\r\n  <tr>\r\n  <th>schema name：</th><td>$!dataMedia.namespace</td>\r\n  </tr>\r\n  <tr>\r\n  <th>table name：</th><td>$!dataMedia.name</td>\r\n  </tr>\r\n  <tr>\r\n  <th>数据源名称：</th><td><a href=\"dataSourceInfo.htm?dataMediaSourceId=$dataMedia.source.id\">$!dataMedia.source.name</a></td>\r\n  </tr>\r\n  <tr>\r\n  <th>数据源类型：</th><td>$!dataMedia.source.type</td>\r\n  </tr>\r\n </table>\r\n </div>\r\n \r\n <table border=\"0\" cellspacing=\"0\" cellpadding=\"0\" class=\"list changecolor_w\">\r\n    <tr> \r\n      <th>源端</th>\r\n      <th>目标</th>\r\n\t  <th>所属Pipeline</th>\r\n\t  <th>所属Channel</th>\r\n    </tr>\r\n    \r\n    #foreach ($seniorDataMediapair in $seniorDataMediapairs)\r\n\t\t<tr> \r\n\t      <td width=\"16%\">\r\n\t\t\t#if($seniorDataMediapair.dataMediaPair.source.id == $!dataMedia.id)\r\n                <font color=\"red\">$seniorDataMediapair.dataMediaPair.source.namespace.$seniorDataMediapair.dataMediaPair.source.name</font>\r\n\t\t\t#else\r\n                <a href=\"dataMediaInfo.htm?dataMediaId=$seniorDataMediapair.dataMediaPair.source.id\">$seniorDataMediapair.dataMediaPair.source.namespace.$seniorDataMediapair.dataMediaPair.source.name</a>\r\n\t\t\t#end\r\n\t\t  </td>\r\n          <td>\r\n\t\t\t#if($seniorDataMediapair.dataMediaPair.target.id == $!dataMedia.id)\r\n                <font color=\"red\">$seniorDataMediapair.dataMediaPair.target.namespace.$seniorDataMediapair.dataMediaPair.target.name</font>\r\n\t\t\t#else\r\n                <a href=\"dataMediaInfo.htm?dataMediaId=$seniorDataMediapair.dataMediaPair.target.id\">$seniorDataMediapair.dataMediaPair.target.namespace.$seniorDataMediapair.dataMediaPair.target.name</a>\r\n\t\t\t#end\r\n\t\t  </td>\r\n\t      #foreach($pipeline in $seniorDataMediapair.channel.pipelines)\r\n\t\t\t#if($pipeline.id == $seniorDataMediapair.dataMediaPair.pipelineId)\r\n\t\t\t\t<td>\r\n\t\t\t\t\t#set ($pipelineInfoURL = $homeModule.setTarget(\"pipelineList.vm\").addQueryData(\"pipelineId\", $seniorDataMediapair.dataMediaPair.pipelineId).addQueryData(\"channelId\", $seniorDataMediapair.channel.id))\r\n                    <a href=\"$pipelineInfoURL\">$pipeline.name</a>\r\n\t\t\t\t</td>\r\n\t\t\t#end\r\n\t\t  #end\r\n\t\t  <td>\r\n\t\t\t#set ($channelInfoURL = $homeModule.setTarget(\"channelList.vm\").addQueryData(\"channelId\", $seniorDataMediapair.channel.id))\r\n            <a href=\"$channelInfoURL\">$seniorDataMediapair.channel.name</a>\r\n\t\t  </td>\r\n\t    </tr>\r\n\t#end\r\n  </table>\r\n</div>\r\n"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/templates/home/screen/dataMediaList.vm",
    "content": "$control.setTemplate(\"home:navigation.vm\")\r\n#set($user = $rundata.request.session.getAttribute(\"managerUser\"))\r\n<script type=\"text/javascript\" src=\"js/trcolor.js\"></script>\r\n<script type=\"text/javascript\" src=\"js/jquery-1.4.2.min.js\"></script> \r\n<script type=\"text/javascript\" src=\"js/jquery.simplemodal-1.4.js\"></script> \r\n<script language=\"javascript\">\r\n\tchangeNav(\"datamedia\");\r\n</script>\r\n\r\n<!--页面主体-->\r\n<div class=\"main\">\r\n   \r\n  <div class=\"title\"> \r\n    <h2>数据表管理</h2>\r\n  </div>\r\n   <div class=\"crumbs\"><a href=\"dataMediaList.htm\">数据表管理</a> </div> \r\n   <!--分页表单-->\r\n   <form id=\"pageform\" name=\"pageform\" action=\"$homeModule.setTarget('dataMediaList.vm')\" method=\"post\">\r\n    \t<input type=\"hidden\" id=\"pageIndex\" name=\"pageIndex\" value=\"\"/>\r\n\t\t<input type=\"hidden\" id=\"searchKey\" name=\"searchKey\" value=\"$!searchKey\"/>\r\n   </form>\r\n   <!--DataMedia搜索-->\r\n   <div class=\"search_o\"> \r\n\t\t<form name=\"search_data_media\" action=\"dataMediaList.htm\" method=\"post\">\r\n\t\t\t##$csrfToken.hiddenField\r\n\t\t\t<div class=\"search_input\">\r\n\t\t\t\t<input name=\"searchKey\" type=\"text\" value=\"请输入关键字(目前支持DataMedia的ID、名字搜索)\"  onfocus=\"if(this.value == '请输入关键字(目前支持DataMedia的ID、名字搜索)') {this.value='';}\" onblur=\"if(this.value == '') {this.value = '请输入关键字(目前支持DataMedia的ID、名字搜索)';}\" />\r\n\t\t\t</div>\r\n\t\t\t<div class=\"search_btn\"><a href=\"javascript:document.search_data_media.submit();\"><img src=\"images/search_btn.png\" width=\"39\" height=\"31\" /></a></div>\r\n        </form>\r\n   </div>\r\n   \r\n   <!--列表-->\r\n     \r\n  <table border=\"0\" cellspacing=\"0\" cellpadding=\"0\" class=\"list changecolor_w\" >\r\n    <tr> \r\n      <th>序号</th>\r\n\t  <th>Schema Name</th>\r\n\t  <th>Table Name</th>\r\n\t  <th>数据源</th>\r\n\t  <th>源类型</th>\r\n\t  <th>操作</th>\r\n    </tr>\r\n\t#foreach ($dataMedia in $dataMedias)\r\n\t\r\n    <tr> \r\n      <td width=\"9%\">$!dataMedia.id</td>\r\n\t  <td>$!dataMedia.namespace</td>\r\n      <td width=\"16%\">$!dataMedia.name</td>\r\n      <td>\r\n\t\t#set ($dataSourceInfoURL = $homeModule.setTarget(\"dataSourceInfo.vm\").addQueryData(\"dataMediaSourceId\", $!dataMedia.source.id))\r\n\t\t<a href=\"$dataSourceInfoURL\">$!dataMedia.source.name</a>\r\n\t  </td>\r\n\t  <td>$!dataMedia.source.type</td>\r\n\t  <td>\r\n\t\t#set ($dataMediaInfoURL = $homeModule.setTarget(\"dataMediaInfo.vm\").addQueryData(\"dataMediaId\", $dataMedia.id))\r\n\t\t<a href=\"$dataMediaInfoURL\"><img src=\"images/ico_edit.png\" width=\"13\" height=\"13\" /><span class=\"ico_font\">查看</span></a>\r\n\t\t#if($user.authorizeType.isAdmin())\r\n\t\t\t\r\n    \t\t#set ($editURL = $homeModule.setTarget(\"editDataMedia.vm\").addQueryData(\"dataMediaId\", $dataMedia.id).addQueryData(\"pageIndex\", $!paginator.page).addQueryData(\"searchKey\", $!searchKey))\r\n    \t\t<span class=\"ico_line\">|</span><a href=\"$editURL\"><img src=\"images/ico_edit.png\" alt=\"\" width=\"13\" height=\"13\" /><span class=\"ico_font\">编辑</span></a>\r\n\t\t\t\r\n\t\t\t#if($!dataMedia.isUsed())\r\n\t\t\t\t<span class=\"ico_line\">|</span><img src=\"images/ico_del.png\" width=\"9\" height=\"9\" /><span class=\"ico_font\" title=\"已经被Pipeline#foreach($pair in $!dataMedia.pairs)[$pair.pipelineId]#end使用，不能删除\">删除</span></td>\r\n\t\t\t#else\r\n\t\t\t\t#set ($removeURL = $homeModule.setAction(\"dataMediaAction\").addQueryData(\"dataMediaId\", $dataMedia.id).addQueryData(\"pageIndex\", $!paginator.page).addQueryData(\"searchKey\", $!searchKey).addQueryData(\"eventSubmitDoDelete\", \"true\"))\r\n\t\t\t\t<span class=\"ico_line\">|</span><a href=\"javascript:if(confirm('确实要删除吗?'))location='$removeURL'\" class=\"link del\"><img src=\"images/ico_del.png\" width=\"9\" height=\"9\" /><span class=\"ico_font\">删除</span></a></td>\r\n\t\t\t#end\r\n    \t\t\r\n\t\t#end\r\n    </tr>\r\n    #end\r\n  </table>\r\n     <!--常规按钮-->\r\n     #if($user.authorizeType.isAdmin())\r\n\t\t\t<div class=\"btn\"><a href=\"addDataMedia.htm\">添加</a></div>\r\n\t #end\r\n  \r\n     \r\n     <!--分页-->\r\n     <div class=\"page\">共$paginator.items条数据&nbsp;&nbsp;第$paginator.page页/共$paginator.pages页&nbsp;&nbsp; \r\n       \r\n\t   #if($paginator.page == 1)\r\n            <font color=\"999999\">首页</font>\r\n\t   #else\r\n\t\t\t<a href=\"#\" class=\"prev\" onclick=\"pageNavigation(this,1)\">首页</a>\r\n\t   #end\r\n\t   \r\n\t   #if($paginator.page > 1)\r\n\t\t\t#set($pre_page = $paginator.page - 1)\r\n\t\t\t\t<a href=\"#\" class=\"prev\" onclick=\"pageNavigation(this,$pre_page)\">上一页</a>\r\n\t   #else\r\n            <font color=\"999999\">上一页</font>\r\n\t   #end\r\n\t   ##分页下标\r\n\t   #set($counts_keys = $paginator.getSlider(7))\r\n\t   #foreach( $thisPage in $counts_keys)\r\n\t\t\t#if( $thisPage == $paginator.page)\r\n                <b>$thisPage</b>\r\n\t\t\t#else\r\n\t\t\t\t#if($thisPage != 0)\r\n\t\t\t\t\t<a href=\"#\" class=\"num\" onclick=\"pageNavigation(this,$thisPage)\">$thisPage</a> \r\n\t\t\t\t#end\r\n\t\t\t#end\r\n\t   #end\r\n\t   \r\n\t   #if($paginator.page < $paginator.pages)\r\n\t\t\t#set($next_page = $paginator.page + 1)\r\n\t\t\t\t<a href=\"#\" class=\"prev\" onclick=\"pageNavigation(this,$next_page)\">下一页</a>\r\n\t   #else\r\n            <font color=\"999999\">下一页</font>\r\n\t   #end\r\n\t   \r\n\t   #if($paginator.page == $paginator.pages)\r\n            <font color=\"999999\">末页</font>\r\n\t   #else\r\n\t\t\t<a href=\"#\" class=\"prev\" onclick=\"pageNavigation(this,$paginator.pages)\">末页</a>\r\n\t   #end\r\n     </div>     \r\n</div>\r\n"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/templates/home/screen/dataMediaPairInfo.vm",
    "content": "$control.setTemplate(\"home:navigation.vm\")\r\n<script language=\"javascript\">\r\n<!--\r\n\tfunction showdiv(targetid,objN){\r\n      var target=document.getElementById(targetid);\r\n      var clicktext=document.getElementById(objN)\r\n\r\n            if (target.style.display==\"block\"){\r\n                target.style.display=\"none\";\r\n                clicktext.innerText=\"查看\";\r\n  \r\n\r\n            } else {\r\n                target.style.display=\"block\";\r\n                clicktext.innerText='关闭';\r\n            }\r\n   \r\n\t}\r\n//-->\r\n</script>\r\n\r\n<div class=\"main\">\r\n <div class=\"title\"><h2>映射关系信息</h2></div>\r\n <div class=\"crumbs\"><a href=\"channelList.htm?channelId=$channelId\">Channel管理</a>&nbsp;&nbsp;>&nbsp;&nbsp;<a href=\"pipelineList.htm?channelId=$channelId\">Pipeline管理</a>&nbsp;&nbsp;>&nbsp;&nbsp;<a href=\"dataMediaPairList.htm?pipelineId=$dataMediaPair.pipelineId\">映射关系管理</a>&nbsp;&nbsp;>&nbsp;&nbsp;<a href=\"dataMediaPairInfo.htm?dataMediaPairId=$dataMediaPair.id\">映射关系信息</a> </div>\r\n <div class=\"setting_box\">\r\n <table cellpadding=\"0\" cellspacing=\"0\" class=\"setting\">\r\n  <tr>\r\n  <th>映射关系序号：</th><td>$!dataMediaPair.id</td>\r\n  </tr>\r\n  <tr>\r\n  <th>源端数据表：</th><td><span class=\"red\">Schema Name:</span>$!dataMediaPair.source.namespace<span class=\"red\">Table Name:</span><a href=\"dataMediaInfo.htm?dataMediaId=$dataMediaPair.source.id\">$!dataMediaPair.source.name</a><span class=\"red\">Source Name:</span><a href=\"dataSourceInfo.htm?dataMediaSourceId=$dataMediaPair.source.source.id\">$!dataMediaPair.source.source.name</a> </td>\r\n  </tr>\r\n  <tr>\r\n  <th>目标数据表：</th><td><span class=\"red\">Schema Name:</span>$!dataMediaPair.target.namespace<span class=\"red\">Table Name:</span><a href=\"dataMediaInfo.htm?dataMediaId=$dataMediaPair.target.id\">$!dataMediaPair.target.name</a><span class=\"red\">Source Name:</span><a href=\"dataSourceInfo.htm?dataMediaSourceId=$dataMediaPair.target.source.id\">$!dataMediaPair.target.source.name</a></td>\r\n  </tr>\r\n  <tr>\r\n  <th>权重：</th><td>$!dataMediaPair.pushWeight</td>\r\n  </tr>\r\n  <tr>\r\n  <th>视图模式：</th><td>$!dataMediaPair.columnPairMode</td>\r\n  </tr>\r\n  <tr>\r\n  <tr>\r\n\t<th>Event Processor：</th>\r\n\t<td>\r\n\t\t#if(!$stringUtil.isEmpty($!dataMediaPair.filterData.clazzPath)) \r\n\t\t\t[$!dataMediaPair.filterData.extensionDataType : $!dataMediaPair.filterData.clazzPath] \r\n\t\t#elseif(!$stringUtil.isEmpty($!dataMediaPair.filterData.sourceText))\r\n\t\t\t[$!dataMediaPair.filterData.extensionDataType : <a id=\"showtext-position2\" onClick=\"showdiv('contentid-position2','showtext-position2')\"><span class=\"ico_font\">查看</span></a>] \r\n\t\t#end\r\n\t</td>\r\n  </tr>\r\n  <tr>\r\n\t<td class=\"message\" colspan=\"2\">\r\n\t\t<div id=\"contentid-position2\" class=\"contentid\" style=\"display:none\">\r\n\t\t\t<textarea cols=\"90\" rows=\"10\">#noescape() $!dataMediaPair.filterData.sourceText #end</textarea>\r\n\t\t</div>\r\n\t</td>\r\n  </tr>\r\n  <th>File Resolver：</th>\r\n\t<td>\r\n\t\t#if(!$stringUtil.isEmpty($!dataMediaPair.resolverData.clazzPath))\r\n\t\t\t[$!dataMediaPair.resolverData.extensionDataType : $!dataMediaPair.resolverData.clazzPath] \r\n\t\t#elseif(!$stringUtil.isEmpty($!dataMediaPair.resolverData.sourceText))\r\n\t\t\t[$!dataMediaPair.resolverData.extensionDataType : <a id=\"showtext-position1\" onClick=\"showdiv('contentid-position1','showtext-position1')\"><span class=\"ico_font\">查看</span></a>] \r\n\t\t#end\r\n\t</td>\r\n  </tr>\r\n  <tr>\r\n\t<td class=\"message\" colspan=\"2\">\r\n\t\t<div id=\"contentid-position1\" class=\"contentid\" style=\"display:none\">\r\n\t\t\t<textarea cols=\"90\" rows=\"10\">#noescape() $!dataMediaPair.resolverData.sourceText #end</textarea>\r\n\t\t</div>\r\n\t</td>\r\n  </tr>\r\n  <tr>\r\n\t<th>字段同步：</th>\r\n\t\t<td>\r\n\t\t\t#foreach($columnPair in $columnPairs)\r\n                <span class=\"red\">[</span> $columnPair.sourceColumn.name - $columnPair.targetColumn.name  <span class=\"red\">]</span>\r\n\t\t\t#end\r\n\t\t</td>\r\n  </tr>\r\n  \r\n  \r\n  <tr>\r\n\t<th>组合同步：</th>\r\n\t\t<td>\r\n\t\t\t#foreach($columnPair in $columnGroup.columnPairs)\r\n\t\t\t\t <span class=\"red\">[</span> $columnPair.sourceColumn.name - $columnPair.targetColumn.name  <span class=\"red\">]</span>\r\n\t\t\t#end\r\n\t\t</td>\r\n  </tr>\r\n </table>\r\n </div>\r\n</div>\r\n\r\n"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/templates/home/screen/dataMediaPairList.vm",
    "content": "<script type=\"text/javascript\" src=\"js/trcolor.js\"></script>\r\n<script type=\"text/javascript\" src=\"js/jquery-1.4.2.min.js\"></script> \r\n\r\n$control.setTemplate(\"home:navigation.vm\")\r\n#set($user = $rundata.request.session.getAttribute(\"managerUser\"))\r\n<script language=\"javascript\">\r\n<!--\r\n\tchangeNav(\"sync\");\r\n\t\r\n\tfunction setTab(name,cursel,n){\r\n \t\tfor(i=1;i<=n;i++){\r\n  \t\t\tvar menu=document.getElementById(name+i);\r\n  \t\t\tvar con=document.getElementById(\"con_\"+name+\"_\"+i);\r\n  \t\t\tmenu.className=i==cursel?\"tab_active\":\"\";\r\n  \t\t\tcon.style.display=i==cursel?\"block\":\"none\";\r\n \t\t}\r\n\t}\t\r\n//-->\r\n</script>\r\n<div class=\"main\">\r\n  <div class=\"title\"> \r\n    <h2>映射关系列表</h2>\r\n  </div>   \r\n  <div class=\"crumbs\"><a href=\"channelList.htm?channelId=$channel.id\">Channel管理</a>&nbsp;&nbsp;>&nbsp;&nbsp;<a href=\"pipelineList.htm?channelId=$channel.id\">Pipeline管理</a>&nbsp;&nbsp;>&nbsp;&nbsp;<a href=\"dataMediaPairList.htm?pipelineId=$pipelineId\">映射关系列表</a></div>\r\n  <div class=\"crumbs\"></div>   \r\n  <div class=\"tab\" id=\"Tab2\">\r\n        <div class=\"menubox\">\r\n        <ul>\r\n\t\t\r\n        <li id=\"two1\" class=\"tab_active\"><a href=\"dataMediaPairList.htm?pipelineId=$pipelineId\">映射关系列表</a></li>\r\n           \r\n        <li id=\"two2\"><a href=\"analysisThroughputStat.htm?pipelineId=$pipelineId\">吞吐量</a></li>\r\n        \r\n        <li id=\"two3\"><a href=\"analysisDelayStat.htm?pipelineId=$pipelineId\">延迟时间</a></li>\r\n           \r\n        <li id=\"two4\"><a href=\"analysisStageStat.htm?pipelineId=$pipelineId\">同步进度</a></li>  \r\n\t\t\r\n\t\t<li id=\"two5\"><a href=\"analysisThroughputHistory.htm?pipelineId=$pipelineId\">历史吞吐量</a></li>\r\n\t\t\r\n\t\t<li id=\"two6\"><a href=\"alarmRuleList.htm?pipelineId=$pipelineId\">监控管理</a></li>\r\n        \r\n\t\t<li id=\"two7\"><a href=\"logRecordTab.htm?pipelineId=$pipelineId\">日志记录</a></li>\r\n\r\n        </ul>\r\n        </div>\r\n         <div class=\"contentbox_tab box_tab\">  \r\n           <div id=\"con_two_1\">\r\n        \r\n        <table border=\"0\" cellspacing=\"0\" cellpadding=\"0\" class=\"list2 changecolor_g\">\r\n          <tr> \r\n            <th width=\"3%\">序号</th>\r\n            <th width=\"10%\">源表</th>\r\n            <th width=\"10%\">目标表</th>\r\n            <th width=\"5%\">权重</th>\r\n\t\t\t<th width=\"7%\">FileSize</th>\r\n\t\t\t<th width=\"7%\">FileCount</th>\r\n\t\t\t<th width=\"7%\">DeleteCount</th>\r\n\t\t\t<th width=\"7%\">UpdateCount</th>\r\n\t\t\t<th width=\"7%\">InsertCount</th>\r\n            <th width=\"10%\">最后同步时间</th>\r\n\t\t\t<th width=\"16%\">操作</th>\r\n            \r\n          </tr>\r\n\t\t  #foreach ($dataMediaPair in $dataMediaPairs)\r\n          <tr> \r\n            <td width=\"5%\">$!dataMediaPair.id</td>\r\n            <td width=\"8%\"><a href=\"dataMediaInfo.htm?dataMediaId=$dataMediaPair.source.id\">$!dataMediaPair.source.namespace.$!dataMediaPair.source.name</a></td>\r\n\t\t\t<td><a href=\"dataMediaInfo.htm?dataMediaId=$dataMediaPair.target.id\">$!dataMediaPair.target.namespace.$!dataMediaPair.target.name</a></td>\r\n            <td>$!dataMediaPair.pushWeight</td>\r\n\t\t\t<td>$!numberFormat.formatFileSize($tableStatMap.get($dataMediaPair.id).fileSize)</td>\r\n\t\t\t<td>$!numberFormat.format($tableStatMap.get($dataMediaPair.id).fileCount)</td>\r\n\t\t\t<td>$!numberFormat.format($tableStatMap.get($dataMediaPair.id).deleteCount)</td>\r\n\t\t\t<td>$!numberFormat.format($tableStatMap.get($dataMediaPair.id).updateCount)</td>\r\n\t\t\t<td>$!numberFormat.format($tableStatMap.get($dataMediaPair.id).insertCount)</td>\r\n            <td>$!numberFormat.format($!tableStatMap.get($dataMediaPair.id).gmtModified)</td>\r\n\t\t\t<td>\r\n\t\t\t\t#set ($dataMediaPairInfoURL = $homeModule.setTarget(\"dataMediaPairInfo.vm\").addQueryData(\"dataMediaPairId\", $dataMediaPair.id))\r\n\t\t\t\t<a href=\"$dataMediaPairInfoURL\"><img src=\"images/ico_edit.png\" width=\"13\" height=\"13\" /><span class=\"ico_font\">查看</span></a>\r\n\t\t\t\t\r\n    \t\t\t#if($user.authorizeType.isAdmin() && $channel.status.isStop())\r\n    \t\t\t\t#set ($editURL = $homeModule.setTarget(\"editDataMediaPair.vm\").addQueryData(\"pipelineId\", $dataMediaPair.pipelineId).addQueryData(\"dataMediaPairId\", $dataMediaPair.id))\r\n    \t\t\t\t<a href=\"$editURL\"><img src=\"images/ico_edit.png\" width=\"13\" height=\"13\" /><span class=\"ico_font\">编辑</span></a>\r\n    \t\t\t\t#set ($removeURL = $homeModule.setAction(\"dataMediaPairAction\").addQueryData(\"dataMediaPairId\", $dataMediaPair.id).addQueryData(\"pipelineId\", $dataMediaPair.pipelineId).addQueryData(\"eventSubmitDoDelete\", \"true\"))\r\n    \t\t\t\t<a href=\"javascript:if(confirm('确实要删除吗?'))location='$removeURL'\" class=\"link del\"><img src=\"images/ico_del.png\" alt=\"\" width=\"9\" height=\"9\" /><span class=\"ico_font\">删除</span></a>\r\n\t\t\t\t#end\r\n\t\t\t\t\r\n\t\t\t\t#set ($behaviorHistoryCurveURL = $homeModule.setTarget(\"behaviorHistoryCurve.vm\").addQueryData(\"dataMediaPairId\", $dataMediaPair.id))\r\n\t\t\t\t<a href=\"$behaviorHistoryCurveURL\"><img src=\"images/ico_edit.png\" width=\"13\" height=\"13\" /><span class=\"ico_font\">行为曲线</span></a>\r\n\t\t\t\t\r\n\t\t\t</td>\r\n          </tr>\r\n          #end\r\n        </table>\r\n\t\t\r\n\t\t\t#if($user.authorizeType.isAdmin() && $channel.status.isStop())\r\n    \t\t\t\t<div class=\"btn\">\r\n    \t\t\t\t\t#set ($addURL = $homeModule.setTarget(\"addDataMediaPair.vm\").addQueryData(\"pipelineId\", $pipelineId))\r\n    \t\t\t\t\t<a href=\"$addURL\">添加</a>\r\n    \t\t\t\t</div>\r\n\t\t\t\t\t\r\n\t\t\t\t\t<div class=\"btn\">\r\n    \t\t\t\t\t#set ($addURL = $homeModule.setTarget(\"addBatchDataMediaPair.vm\").addQueryData(\"pipelineId\", $pipelineId))\r\n    \t\t\t\t\t<a href=\"$addURL\">批量添加</a>\r\n    \t\t\t\t</div>\r\n\t\t\t#end\r\n\r\n       <!--</div>-->\r\n    </div>\r\n\t</div>\r\n  </div>\r\n</div>"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/templates/home/screen/dataSourceInfo.vm",
    "content": "$control.setTemplate(\"home:navigation.vm\")\r\n#set($user = $rundata.request.session.getAttribute(\"managerUser\"))\r\n\r\n<div class=\"main\">\r\n <div class=\"title\"><h2>数据源信息</h2></div>\r\n <div class=\"crumbs\">数据源信息</div>\r\n <div class=\"setting_box\">\r\n <table cellpadding=\"0\" cellspacing=\"0\" class=\"setting\">\r\n  <tr>\r\n  <th>数据源序号：</th><td>$!source.id</td>\r\n  </tr>\r\n  <tr>\r\n  <th>数据源名：</th><td>$!source.name</td>\r\n  </tr>\r\n  <tr>\r\n  <th>数据类型：</th><td>$!source.type</td>\r\n  </tr>\r\n  <tr>\r\n  <th>编码：</th><td>$!source.encode</td>\r\n  </tr>\r\n  <tr>\r\n  <th>URL：</th><td>$!source.url</td>\r\n  </tr>\r\n </table>\r\n </div>\r\n \r\n <table border=\"0\" cellspacing=\"0\" cellpadding=\"0\" class=\"list changecolor_w\" width=\"60%\">\r\n    <tr>\r\n\t  <th>序号</th>\r\n      <th>Schema Name</th>\r\n      <th>Table Name</th>\r\n\t  <th>操作</th>\r\n    </tr>\r\n    \r\n    #foreach ($dataMedia in $dataMedias)\r\n\t\t<tr> \r\n\t\t  <td width=\"15%\">$!dataMedia.id</td>\r\n    \t  <td width=\"25%\">$!dataMedia.namespace</td>\r\n          <td width=\"25%\">$!dataMedia.name</td>\r\n\t\t  <td>\r\n\t\t\t#set ($dataMediaInfoURL = $homeModule.setTarget(\"dataMediaInfo.vm\").addQueryData(\"dataMediaId\", $dataMedia.id))\r\n    \t\t<a href=\"$dataMediaInfoURL\"><img src=\"images/ico_edit.png\" width=\"13\" height=\"13\" /><span class=\"ico_font\">查看</span></a>\r\n    \t\t#if($user.authorizeType.isAdmin())\r\n        \t\t#set ($editURL = $homeModule.setTarget(\"editDataMedia.vm\").addQueryData(\"dataMediaId\", $dataMedia.id))\r\n        \t\t<span class=\"ico_line\">|</span><a href=\"$editURL\"><img src=\"images/ico_edit.png\" alt=\"\" width=\"13\" height=\"13\" /><span class=\"ico_font\">编辑</span></a>\r\n    \t\t#end\r\n\t\t  </td>\r\n\t    </tr>\r\n\t#end\r\n  </table>\r\n</div>\r\n"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/templates/home/screen/dataSourceList.vm",
    "content": "$control.setTemplate(\"home:navigation.vm\")\r\n#set($user = $rundata.request.session.getAttribute(\"managerUser\"))\r\n<script type=\"text/javascript\" src=\"js/trcolor.js\"></script>\r\n<script type=\"text/javascript\" src=\"js/jquery-1.4.2.min.js\"></script> \r\n<script type=\"text/javascript\" src=\"js/jquery.simplemodal-1.4.js\"></script> \r\n<script language=\"javascript\">\r\n        changeNav(\"datamedia\");\r\n</script>\r\n\r\n\r\n\r\n<!--页面主体-->\r\n<div class=\"main\">\r\n   \r\n  <div class=\"title\"> \r\n    <h2>数据源配置</h2>\r\n  </div>\r\n   <div class=\"crumbs\"><a href=\"dataSourceList.htm\">数据源配置</a></div> \r\n   <!--分页表单-->\r\n   <form id=\"pageform\" name=\"pageform\" action=\"$homeModule.setTarget('dataSourceList.vm')\" method=\"post\">\r\n    \t<input type=\"hidden\" id=\"pageIndex\" name=\"pageIndex\" value=\"\"/>\r\n\t\t<input type=\"hidden\" id=\"searchKey\" name=\"searchKey\" value=\"$!searchKey\"/>\r\n   </form>\r\n    <!--DataMedia搜索-->\r\n   <div class=\"search_o\"> \r\n\t\t<form name=\"search_data_source\" action=\"dataSourceList.htm\" method=\"post\">\r\n\t\t\t##$csrfToken.hiddenField\r\n\t\t\t<div class=\"search_input\">\r\n\t\t\t\t<input name=\"searchKey\" type=\"text\" value=\"请输入关键字(目前支持DataSource的ID、名字搜索)\"  onfocus=\"if(this.value == '请输入关键字(目前支持DataSource的ID、名字搜索)') {this.value='';}\" onblur=\"if(this.value == '') {this.value = '请输入关键字(目前支持DataSource的ID、名字搜索)';}\" />\r\n\t\t\t</div>\r\n\t\t\t<div class=\"search_btn\"><a href=\"javascript:document.search_data_source.submit();\"><img src=\"images/search_btn.png\" width=\"39\" height=\"31\" /></a></div>\r\n        </form>\r\n   </div>\r\n   \r\n   <!--列表-->\r\n     \r\n  <table border=\"0\" cellspacing=\"0\" cellpadding=\"0\" class=\"list changecolor_w\">\r\n    <tr> \r\n      <th>序号</th>\r\n      <th>数据源名字</th>\r\n      <th>类型</th>\r\n\t  <th>编码</th>\r\n      <th>URL</th>\r\n\t  <th>操作</th>\r\n    </tr>\r\n\t\r\n\t#foreach ($source in $sources)\r\n    <tr> \r\n      <td width=\"5%\">$!source.id</td>\r\n      <td width=\"12%\">$!source.name</td>\r\n      <td>$!source.type</td>\r\n\t  <td>$!source.encode</td>\r\n      <td>$!source.url</td>\r\n\t  \r\n\t  <td>\r\n\t\t#set ($dataSourceInfoURL = $homeModule.setTarget(\"dataSourceInfo.vm\").addQueryData(\"dataMediaSourceId\", $source.id))\r\n\t\t<a href=\"$dataSourceInfoURL\"><img src=\"images/ico_edit.png\" width=\"13\" height=\"13\" /><span class=\"ico_font\">查看</span></a>\r\n\t\t#if($user.authorizeType.isAdmin())\r\n    \t\t#set ($editURL = $homeModule.setTarget(\"editDataSource.vm\").addQueryData(\"dataMediaSourceId\", $source.id).addQueryData(\"pageIndex\", $!paginator.page).addQueryData(\"searchKey\", $!searchKey))\r\n    \t\t<span class=\"ico_line\">|</span><a href=\"$editURL\"><img src=\"images/ico_edit.png\" alt=\" \" width=\"13\" height=\"13\" /><span class=\"ico_font\">编辑</span></a>\r\n    \t\t\r\n\t\t\t#if($!source.isUsed())\r\n\t\t\t\t<span class=\"ico_line\">|</span><img src=\"images/ico_del.png\" width=\"9\" height=\"9\" /><span class=\"ico_font\" title=\"已被数据表#foreach($dataMedia in $source.dataMedias)[$dataMedia.id]#end使用，清空关联才能删除\">删除</span>\r\n\t\t\t#else\r\n\t\t\t\t#set ($removeURL = $homeModule.setAction(\"dataMediaSourceAction\").addQueryData(\"dataMediaSourceId\", $source.id).addQueryData(\"pageIndex\", $!paginator.page).addQueryData(\"searchKey\", $!searchKey).addQueryData(\"eventSubmitDoDelete\", \"true\"))\r\n\t\t\t\t<span class=\"ico_line\">|</span><a href=\"javascript:if(confirm('确实要删除吗?'))location='$removeURL'\" class=\"link del\"><img src=\"images/ico_del.png\" width=\"9\" height=\"9\" /><span class=\"ico_font\">删除</span></a>\r\n\t\t\t#end\r\n\t\t\t\r\n\t\t#end\r\n\t  </td>\r\n\t  \r\n    </tr>\r\n    #end\r\n  </table>\r\n  <!--常规按钮-->\r\n      #if($user.authorizeType.isAdmin())\r\n\t\t<div class=\"btn\"><a href=\"addDataSource.htm\">添加</a></div>\r\n\t  #end\r\n  \r\n     \r\n     <!--分页-->\r\n     <div class=\"page\">共$paginator.items条数据&nbsp;&nbsp;第$paginator.page页/共$paginator.pages页&nbsp;&nbsp; \r\n       \r\n\t   #if($paginator.page == 1)\r\n            <font color=\"999999\">首页</font>\r\n\t   #else\r\n\t\t\t<a href=\"#\" class=\"prev\" onclick=\"pageNavigation(this,1)\">首页</a>\r\n\t   #end\r\n\t   \r\n\t   #if($paginator.page > 1)\r\n\t\t\t#set($pre_page = $paginator.page - 1)\r\n\t\t\t\t<a href=\"#\" class=\"prev\" onclick=\"pageNavigation(this,$pre_page)\">上一页</a>\r\n\t   #else\r\n            <font color=\"999999\">上一页</font>\r\n\t   #end\r\n\t   ##分页下标\r\n\t   #set($counts_keys = $paginator.getSlider(7))\r\n\t   #foreach( $thisPage in $counts_keys)\r\n\t\t\t#if( $thisPage == $paginator.page)\r\n                <b>$thisPage</b>\r\n\t\t\t#else\r\n\t\t\t\t#if($thisPage != 0)\r\n\t\t\t\t\t<a href=\"#\" class=\"num\" onclick=\"pageNavigation(this,$thisPage)\">$thisPage</a> \r\n\t\t\t\t#end\r\n\t\t\t#end\r\n\t   #end\r\n\t   \r\n\t   #if($paginator.page < $paginator.pages)\r\n\t\t\t#set($next_page = $paginator.page + 1)\r\n\t\t\t\t<a href=\"#\" class=\"prev\" onclick=\"pageNavigation(this,$next_page)\">下一页</a>\r\n\t   #else\r\n            <font color=\"999999\">下一页</font>\r\n\t   #end\r\n\t   \r\n\t   #if($paginator.page == $paginator.pages)\r\n            <font color=\"999999\">末页</font>\r\n\t   #else\r\n\t\t\t<a href=\"#\" class=\"prev\" onclick=\"pageNavigation(this,$paginator.pages)\">末页</a>\r\n\t   #end\r\n     </div>         \r\n</div>\r\n"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/templates/home/screen/editAlarmRule.vm",
    "content": "$control.setTemplate(\"home:navigation.vm\")\r\n<script language=\"javascript\">\r\n<!--\r\n\tchangeNav(\"sync\");\r\n//-->\r\n</script>\r\n\r\n#macro (editAlarmRuleMessage $field)\r\n    #if (!$field.valid) $field.message #end\r\n#end\t\r\n\r\n<div class=\"main\">\r\n  <div class=\"title\"> \r\n    <h2>添加监控</h2>\r\n  </div>\r\n <div class=\"crumbs\"><a href=\"channelList.htm?channelId=$channelId\">channel管理</a>&nbsp;&nbsp;>&nbsp;&nbsp;<a href=\"pipelineList.htm?channelId=$channelId\">Pipeline管理</a>&nbsp;&nbsp;>&nbsp;&nbsp;<a href=\"alarmRuleList.htm?pipelineId=$alarmRule.pipelineId\">监控管理</a>&nbsp;&nbsp;>&nbsp;&nbsp;修改监控</div>\r\n \r\n \r\n<form name=\"editAlarmRuleForm\" method=\"post\" enctype=\"multipart/form-data\">\r\n\t$csrfToken.hiddenField\r\n\t<input type=\"hidden\" name=\"action\" value=\"alarm_rule_action\"/>\r\n\t<input type=\"hidden\" name=\"event_submit_do_edit\" value=\"1\" />\r\n <div class=\"setting_box\">\r\n \r\n   #set ($alarmRuleGroup = $form.alarmRuleInfo.defaultInstance)\r\n   <input type=\"hidden\" name=\"$alarmRuleGroup.id.key\" value=\"$alarmRule.id\" />\r\n   <input type=\"hidden\" name=\"$alarmRuleGroup.pipelineId.key\" value=\"$alarmRule.pipelineId\"/>\r\n   <input type=\"hidden\" name=\"$alarmRuleGroup.status.key\" value=\"$alarmRule.status\"/>\r\n   \r\n    <table cellpadding=\"0\" cellspacing=\"0\" class=\"setting_otter\">\r\n\t\t<span class=\"red\">#editAlarmRuleMessage ($alarmRuleGroup.formAlarmRuleError)</span>\r\n      <tr> \r\n        <th width=\"300\">监控项目：</th>\r\n        <td width=\"329\">\r\n\t\t\t<select name=\"$alarmRuleGroup.monitorName.key\" id=\"select\">\r\n                <option value=\"DELAYTIME\" #if($alarmRule.monitorName.isDelayTime()) selected=\"selected\" #end>延迟</option>\r\n\t\t\t\t<option value=\"PIPELINETIMEOUT\" #if($alarmRule.monitorName.isPipelineTimeout()) selected=\"selected\" #end>Pipeline超时</option>\r\n\t\t\t\t<option value=\"PROCESSTIMEOUT\" #if($alarmRule.monitorName.isProcessTimeout()) selected=\"selected\" #end>Process超时</option>\r\n\t\t\t\t<option value=\"POSITIONTIMEOUT\" #if($alarmRule.monitorName.isPositionTimeout()) selected=\"selected\" #end>Position超时</option>\r\n\t\t\t\t<option value=\"EXCEPTION\" #if($alarmRule.monitorName.isException()) selected=\"selected\" #end>异常</option>\r\n            </select><span class=\"red\">*</span>\r\n\t\t</td>\r\n      </tr>\r\n      <tr> \r\n        <th>阈值：</th>\r\n        <td>\r\n\t\t\t<input type=\"text\" name=\"$alarmRuleGroup.matchValue.key\" value=\"$alarmRule.matchValue\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#editAlarmRuleMessage ($alarmRuleGroup.matchValue)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  <tr> \r\n        <th>报警间隔时间(秒)：</th>\r\n        <td>\r\n\t\t\t<input type=\"text\" name=\"$alarmRuleGroup.intervalTime.key\" value=\"$!alarmRule.intervalTime\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#editAlarmRuleMessage ($alarmRuleGroup.intervalTime)</span>\r\n\t\t</td>\r\n      </tr>\r\n      <tr> \r\n        <th>发送人KEY：</th>\r\n        <td>\r\n\t\t\t<input type=\"text\" name=\"$alarmRuleGroup.receiverKey.key\" value=\"$alarmRule.receiverKey\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#editAlarmRuleMessage ($alarmRuleGroup.receiverKey)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  <tr> \r\n        <th>开始自动恢复：</th>\r\n        <td>\r\n\t\t\t<input type=\"radio\" name=\"$alarmRuleGroup.autoRecovery.key\" value=\"true\" id=\"RadioGroup2_0\"  #if ($!alarmRule.autoRecovery) checked=\"checked\" #end class=\"radio\"/>是\r\n            <input type=\"radio\" name=\"$alarmRuleGroup.autoRecovery.key\" value=\"false\" id=\"RadioGroup2_1\" #if (!$!alarmRule.autoRecovery) checked=\"checked\" #end class=\"radio\"/>否 \r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#editAlarmRuleMessage ($alarmRuleGroup.autoRecovery)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  <tr> \r\n        <th>触发自动恢复阀值：</th>\r\n        <td>\r\n\t\t\t<input type=\"text\" name=\"$alarmRuleGroup.recoveryThresold.key\" value=\"$alarmRule.recoveryThresold\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#editAlarmRuleMessage ($alarmRuleGroup.recoveryThresold)</span>\r\n\t\t</td>\r\n      </tr>\r\n      <tr>\r\n       <th>描述：</th>\r\n       <td>\r\n    \t\t<textarea cols=\"45\" rows=\"5\" name=\"$alarmRuleGroup.description.key\">$!alarmRule.description</textarea>\r\n\t   </td>\r\n      </tr>\r\n    </table>\r\n </div>\r\n   <div class=\"btn\"><a href=\"javascript:document.editAlarmRuleForm.submit();\">保存</a></div> \r\n  \r\n  </form>\r\n</div>"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/templates/home/screen/editAutoKeeper.vm",
    "content": "$control.setTemplate(\"home:navigation.vm\")\r\n<script language=\"javascript\">\r\n<!--\r\n\tchangeNav(\"node\");\r\n//-->\r\n</script>\r\n\r\n#macro (editAutoKeeperClusterMessage $field)\r\n    #if (!$field.valid) $field.message #end\r\n#end\t\r\n\r\n<div class=\"main\">\r\n  <div class=\"title\"> \r\n    <h2>修改集群配置</h2>\r\n  </div>\r\n \r\n<form name=\"editZkClusterForm\" method=\"post\" enctype=\"multipart/form-data\">\r\n\t$csrfToken.hiddenField\r\n\t<input type=\"hidden\" name=\"action\" value=\"auto_keeper_cluster_action\"/>\r\n\t<input type=\"hidden\" name=\"event_submit_do_edit\" value=\"1\" />\r\n\t\r\n <div class=\"setting_box\">\r\n \r\n   #set ($autoKeeperClusterGroup = $form.autokeeperClusterInfo.defaultInstance)\r\n   <input type=\"hidden\" name=\"$autoKeeperClusterGroup.id.key\" value=\"$!autoKeeperCluster.id\" />\r\n    <table cellpadding=\"0\" cellspacing=\"0\" class=\"setting_otter\">\r\n\t\t<span class=\"red\">#editAutoKeeperClusterMessage ($autoKeeperClusterGroup.formAutokeeperClusterError)</span>\r\n      <tr> \r\n        <th width=\"300\">集群名字：</th>\r\n        <td width=\"329\">\r\n            <input type=\"text\" name=\"$autoKeeperClusterGroup.clusterName.key\" value=\"$!autoKeeperCluster.clusterName\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#editAutoKeeperClusterMessage ($autoKeeperClusterGroup.clusterName)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  <tr> \r\n        <th>ZooKeeper集群：</th>\r\n        <td>\r\n\t\t\t<textarea class=\"service\" name=\"$autoKeeperClusterGroup.zookeeperClusters.key\" cols=\"45\" rows=\"5\" >#foreach($address in $!autoKeeperCluster.serverList)$address;#end</textarea><span class=\"red\">*</span>\r\n\t\t\t  <br />\r\n\t\t\t  <span>格式如 10.20.10.20:8080;（必须以分号结束，可添多个）</span>\r\n\t\t\t  <br />\r\n\t\t\t<span class=\"red\">#editAutoKeeperClusterMessage ($autoKeeperClusterGroup.zookeeperClusters)</span>\r\n\t\t</td>\r\n      </tr>\r\n      <tr>\r\n       <th>描述：</th>\r\n       <td>\r\n    \t\t<textarea cols=\"45\" rows=\"5\" name=\"$autoKeeperClusterGroup.description.key\">$!autoKeeperCluster.description</textarea>\r\n    \t\t<br />\r\n    \t\t<span class=\"red\">#editAutoKeeperClusterMessage ($autoKeeperClusterGroup.description)</span>\r\n\t   </td>\r\n      </tr>\r\n    </table>\r\n </div>\r\n   <div class=\"btn\"><a href=\"javascript:document.editZkClusterForm.submit();\">保存</a></div> \r\n  \r\n  </form>\r\n</div>"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/templates/home/screen/editCanal.vm",
    "content": "$control.setTemplate(\"home:navigation.vm\")\r\n<script language=\"javascript\">\r\n<!--\r\n\tchangeNav(\"canal\");\r\n\t\r\n\tfunction changePositionConfig() {\r\n\t\tvar obj = document.getElementById('positionSuperConfig');\r\n\t\tif(obj.checked) {\r\n\t\t\tchangeDisplay('positionSuperConfig','table-row');\r\n\t\t\tchangeDisplay('positionGtidConfig','table-row');\r\n\t\t}else {\r\n\t\t\tchangeDisplay('positionSuperConfig','none');\r\n\t\t\tchangeDisplay('positionGtidConfig','none');\r\n\t\t}\r\n\t}\r\n\t\r\n\tfunction changeNetworkConfig() {\r\n\t\tvar obj = document.getElementById('networkSuperConfig');\r\n\t\tif(obj.checked) {\r\n\t\t\tchangeDisplay('networkSuperConfig','table-row');\r\n\t\t}else {\r\n\t\t\tchangeDisplay('networkSuperConfig','none');\r\n\t\t}\r\n\t}\r\n//-->\r\n</script>\r\n#macro (editCanalMessage $field)\r\n    #if (!$field.valid) $field.message #end\r\n#end\t\r\n\r\n<div class=\"main\">\r\n  <div class=\"title\"> \r\n    <h2>编辑canal</h2>\r\n  </div>\r\n <div class=\"crumbs\"><a href=\"canalList.htm\">canal管理</a>&nbsp;&nbsp;>&nbsp;&nbsp;<a href=\"editCanal.htm?canalId=$canal.id\">编辑canal</a></div>\r\n \r\n \r\n<form name=\"editCanalForm\" method=\"post\" enctype=\"multipart/form-data\">\r\n\t$csrfToken.hiddenField\r\n\t<input type=\"hidden\" name=\"action\" value=\"canal_action\"/>\r\n\t<input type=\"hidden\" name=\"event_submit_do_edit\" value=\"1\" />\r\n\t\r\n <div class=\"setting_box\">\r\n \r\n   #set ($canalGroup = $form.canalInfo.defaultInstance)\r\n   #set ($canalParameterGroup = $form.canalParameterInfo.defaultInstance)\r\n   \r\n\t<input type=\"hidden\" name=\"$canalGroup.id.key\" value=\"$canal.id\" />\r\n\t<input type=\"hidden\" name=\"canalId\" value=\"$canal.id\" />\r\n\t\r\n    <table cellpadding=\"0\" cellspacing=\"0\" class=\"setting_otter\">\r\n    \t<tr>\r\n    \t<th width=\"300\"></th>\r\n    \t<td width=\"329\">\r\n    \t\t#foreach($f in ${canalParameterGroup.getFields()})\r\n    \t\t\t#if (!$f.valid) \r\n    \t\t\t\t<span class=\"red\">$!f.message</span><br/>\r\n    \t\t\t#end\r\n    \t\t#end\r\n    \t</td>\r\n    </tr>\r\n    \r\n      <tr> \r\n        <th width=\"300\">canal名称：</th>\r\n        <td width=\"329\">\r\n            <input type=\"text\" name=\"$canalGroup.name.key\" value=\"$!canal.name\" disabled class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#editCanalMessage ($canalGroup.name) #editCanalMessage ($canalGroup.formCanalError)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  \r\n\t  <tr> \r\n        <th width=\"300\">运行模式：</th>\r\n        <td width=\"329\">\r\n            <input type=\"radio\" name=\"$canalParameterGroup.runMode.key\" value=\"EMBEDDED\" id=\"RadioGroup1_0\" #if($!canal.canalParameter.runMode.isEmbedded()) checked=\"checked\" #end class=\"radio\"/>嵌入式 &nbsp;\r\n            <input type=\"radio\" name=\"$canalParameterGroup.runMode.key\" value=\"SERVICE\" id=\"RadioGroup1_1\" #if($!canal.canalParameter.runMode.isService()) checked=\"checked\" #end disabled class=\"radio\"/>独立服务 &nbsp;\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#editCanalMessage ($canalParameterGroup.runMode)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t <tr> \r\n        <th>zookeeper集群：</th>\r\n        <td>\r\n            <select id=\"zkCluster\" name=\"$canalParameterGroup.autoKeeperClusterId.key\">\r\n\t\t\t#foreach($zkCluster in $zkClusters)\r\n            <option value=\"$zkCluster.id\" #if($!canal.canalParameter.zkClusterId == $zkCluster.id)selected#end>$zkCluster.clusterName</option>\r\n\t\t\t#end\r\n        </td>\r\n      </tr>\r\n\t  #*\r\n\t  <tr> \r\n        <th>ZooKeeper集群：</th>\r\n        <td>\r\n\t\t\t<textarea class=\"service\" name=\"$canalParameterGroup.zkClusters.key\" cols=\"45\" rows=\"5\" >#foreach($storeAddress in $!canal.canalParameter.zkClusters)$storeAddress;#end</textarea><span class=\"red\">*</span>\r\n\t\t\t  <br />\r\n\t\t\t  <span>格式如 10.20.10.20:8080;（必须以分号结束，可添多个）</span>\r\n\t\t\t  <br />\r\n\t\t\t<span class=\"red\">#editCanalMessage ($canalParameterGroup.zkClusters)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  *#\r\n\t  <tr> \r\n        <th width=\"300\">-------------------------------</th>\r\n\t\t<td width=\"329\">---------------------------------------------------------------------------------------------</td>\r\n      </tr>\r\n\t  \r\n\t  <div id=\"dbConfig\">\r\n\t\t<tr> \r\n        <th width=\"300\">数据源类型：</th>\r\n        <td width=\"329\">\r\n            <input type=\"radio\" name=\"$canalParameterGroup.sourcingType.key\" value=\"MYSQL\" onclick=\"changeDisplay('mysqlSourcing','table-row');changeDisplay('oracleSourcing','none');changeDisplay('localSourcing','none');changeDisplay('obSourcing','none');\" id=\"RadioGroup5_0\" #if($!canal.canalParameter.sourcingType.isMysql()) checked=\"checked\" #end class=\"radio\"/>mysql &nbsp;\r\n\t\t\t#**\r\n\t\t\t<input type=\"radio\" name=\"$canalParameterGroup.sourcingType.key\" value=\"ORACLE\" onclick=\"changeDisplay('mysqlSourcing','none');changeDisplay('oracleSourcing','table-row');changeDisplay('localSourcing','none');\" id=\"RadioGroup5_1\" #if($!canal.canalParameter.sourcingType.isOracle()) checked=\"checked\" #end class=\"radio\"/>oracle &nbsp;\r\n            <input type=\"radio\" name=\"$canalParameterGroup.sourcingType.key\" value=\"LOCALBINLOG\" onclick=\"changeDisplay('mysqlSourcing','none');changeDisplay('oracleSourcing','none');changeDisplay('localSourcing','table-row');\" id=\"RadioGroup5_2\" #if($!canal.canalParameter.sourcingType.isLocalBinlog()) checked=\"checked\" #end class=\"radio\"/>localbinlog &nbsp;\r\n\t\t\t*#\r\n            <input type=\"radio\" name=\"$canalParameterGroup.sourcingType.key\" value=\"OCEANBASE\" onclick=\"changeDisplay('mysqlSourcing','none');changeDisplay('oracleSourcing','none');changeDisplay('localSourcing','none');changeDisplay('obSourcing','table-row');\" id=\"RadioGroup5_3\" #if($!canal.canalParameter.sourcingType.isOceanBase()) checked=\"checked\" #end class=\"radio\"/>oceanbase &nbsp;\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#editCanalMessage ($canalParameterGroup.sourcingType)</span>\r\n\t\t</td>\r\n        </tr>\r\n\t\t\r\n\t\t<tr> \r\n            <th width=\"300\">数据库地址：</th>\r\n            <td width=\"329\">\r\n    \t\t\t<textarea class=\"service\" name=\"$canalParameterGroup.groupDbAddresses.key\" cols=\"45\" rows=\"5\" >$!numberFormat.formatGroupDbAddress($canal.canalParameter.sourcingType,$canal.canalParameter.groupDbAddresses)</textarea><span class=\"red\">*</span>\r\n    \t\t\t<br />\r\n    \t\t\t<span>格式如 127.0.0.1:3306,127.0.0.1:3307;(必须以分号结束，逗号代表分组，可添多个)</span>\r\n    \t\t\t<br />\r\n    \t\t\t<span class=\"red\">#editCanalMessage ($canalParameterGroup.groupDbAddresses)</span>\r\n    \t\t</td>\r\n          </tr>\r\n    \t  <tr> \r\n            <th width=\"300\">数据库帐号：</th>\r\n            <td width=\"329\">\r\n                <input type=\"text\" name=\"$canalParameterGroup.dbUsername.key\" value=\"$!canal.canalParameter.dbUsername\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n    \t\t\t<br />\r\n    \t\t\t<span class=\"red\">#editCanalMessage ($canalParameterGroup.dbUsername)</span>\r\n    \t\t</td>\r\n          </tr>\r\n    \t  <tr> \r\n            <th width=\"300\">数据库密码：</th>\r\n            <td width=\"329\">\r\n                <input type=\"password\" name=\"$canalParameterGroup.dbPassword.key\" value=\"$!canal.canalParameter.dbPassword\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n    \t\t\t<br />\r\n    \t\t\t<span class=\"red\">#editCanalMessage ($canalParameterGroup.dbPassword)</span>\r\n    \t\t</td>\r\n          </tr>\r\n    \t <tr> \r\n            <th width=\"300\">connectionCharset：</th>\r\n            <td width=\"329\">\r\n                <input type=\"text\" name=\"$canalParameterGroup.connectionCharset.key\" value=\"$!canal.canalParameter.connectionCharset\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n    \t\t\t<br />\r\n    \t\t\t<span class=\"red\">#editCanalMessage ($canalParameterGroup.connectionCharset)</span>\r\n    \t\t</td>\r\n          </tr>\r\n\t\t #**\r\n\t\t <tr class=\"mysqlSourcing\"> \r\n            <th width=\"300\">链接到mysql的slaveId：</th>\r\n            <td width=\"329\">\r\n                <input type=\"text\" name=\"$canalParameterGroup.slaveId.key\" value=\"$!canal.canalParameter.slaveId\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n    \t\t\t<br />\r\n    \t\t\t<span class=\"red\">#editCanalMessage ($canalParameterGroup.slaveId)</span>\r\n    \t\t</td>\r\n          </tr>\r\n\t\t  *#\r\n\t\t  <input type=\"hidden\" name=\"$canalParameterGroup.slaveId.key\" value=\"$!canal.canalParameter.slaveId\" />\r\n\t\t  <tr class=\"localSourcing\">\r\n            <th width=\"300\">本地localBinlog目录：</th>\r\n            <td width=\"329\">\r\n                <input type=\"text\" name=\"$canalParameterGroup.localBinlogDirectory.key\" value=\"$!canal.canalParameter.localBinlogDirectory\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n    \t\t\t<br />\r\n    \t\t\t<span class=\"red\">#editCanalMessage ($canalParameterGroup.localBinlogDirectory)</span>\r\n    \t\t</td>\r\n          </tr>\r\n          <tr class=\"obSourcing\">\r\n    \t\t<th width=\"300\">rsList：</th>\r\n    \t\t<td width=\"329\">\r\n                <input type=\"text\" name=\"$canalParameterGroup.rsList.key\" value=\"$!canalExtra.get(\"rsList\")\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n                <br />\r\n                <span class=\"red\">#editCanalMessage ($canalParameterGroup.rsList)</span>\r\n    \t\t</td>\r\n          </tr>\r\n          <tr class=\"obSourcing\">\r\n    \t\t<th width=\"300\">tenant：</th>\r\n    \t\t<td width=\"329\">\r\n                <input type=\"text\" name=\"$canalParameterGroup.tenant.key\" value=\"$!canalExtra.get(\"tenant\")\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n                <br />\r\n                <span class=\"red\">#editCanalMessage ($canalParameterGroup.tenant)</span>\r\n    \t\t</td>\r\n          </tr>\r\n\t  </div>\r\n\t  \r\n\t  <div id=\"positionConfig\">\r\n\t  <tr>\r\n\t\t<th width=\"300\">位点自定义设置：</th>\r\n        <td width=\"329\">\r\n              <input id=\"positionSuperConfig\" type='checkbox' value=1  class=\"setting_input\" onclick=\"changePositionConfig()\" />\r\n\t\t\t  <br />\r\n        </td>\r\n      </tr>\r\n      \r\n      <tr class=\"positionGtidConfig\">\r\n\t\t<th width=\"300\">是否启用gtid位点：</th>\r\n        <td width=\"329\">\r\n            <input type=\"radio\" name=\"$canalParameterGroup.gtidEnable.key\" value=\"true\" id=\"RadioGroup8_0_0\" #if($!canal.canalParameter.gtidEnable) checked=\"checked\" #end class=\"radio\"/>是 &nbsp;\r\n            <input type=\"radio\" name=\"$canalParameterGroup.gtidEnable.key\" value=\"false\" id=\"RadioGroup8_1_0\" #if(!$!canal.canalParameter.gtidEnable) checked=\"checked\" #end class=\"radio\"/>否 &nbsp;\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#editCanalMessage ($canalParameterGroup.gtidEnable)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  <tr class=\"positionSuperConfig\"> \r\n        <th width=\"300\">位点信息：</th>\r\n        <td width=\"329\">\r\n\t\t\t<textarea class=\"service\" name=\"$canalParameterGroup.positions.key\" cols=\"45\" rows=\"5\" >#foreach($position in $!canal.canalParameter.positions)$position;#end</textarea><span class=\"red\">*</span>\r\n\t\t\t示例：{\"journalName\":\"\",\"position\":0,\"timestamp\":0}; (必须以分号结束，可添多个)</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#editCanalMessage ($canalParameterGroup.positions)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  </div>\r\n\t  \r\n\t  <div id=\"tsdbConfig\">\r\n\t  <tr>\r\n\t\t<th width=\"300\">是否开启表结构TSDB：</th>\r\n        <td width=\"329\">\r\n            <input type=\"radio\" name=\"$canalParameterGroup.tsdbEnable.key\" value=\"true\" id=\"RadioGroup8_0_0\" #if($!canal.canalParameter.tsdbEnable) checked=\"checked\" #end class=\"radio\"/>是 &nbsp;\r\n            <input type=\"radio\" name=\"$canalParameterGroup.tsdbEnable.key\" value=\"false\" id=\"RadioGroup8_1_0\" #if(!$!canal.canalParameter.tsdbEnable) checked=\"checked\" #end class=\"radio\"/>否 &nbsp;\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#editCanalMessage ($canalParameterGroup.tsdbEnable)</span>\r\n\t\t</td>\r\n      </tr>\r\n      </div>\r\n      \r\n      <div id=\"rdsConfig\">\r\n\t  <tr class=\"rdsConfig\">\r\n        <th width=\"300\">rds accesskey：</th>\r\n        <td width=\"329\">\r\n            <input type=\"text\" name=\"$canalParameterGroup.rdsAccesskey.key\" value=\"$!canal.canalParameter.rdsAccesskey\" class=\"setting_input\"/>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#editCanalMessage ($canalParameterGroup.rdsAccesskey)</span>\r\n\t\t</td>\r\n      </tr>\r\n       <tr class=\"rdsConfig\">\r\n        <th width=\"300\">rds secretkey：</th>\r\n        <td width=\"329\">\r\n            <input type=\"text\" name=\"$canalParameterGroup.rdsSecretkey.key\" value=\"$!canal.canalParameter.rdsSecretkey\" class=\"setting_input\"/>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#editCanalMessage ($canalParameterGroup.rdsSecretkey)</span>\r\n\t\t</td>\r\n      </tr>\r\n       <tr class=\"rdsConfig\">\r\n        <th width=\"300\">rds instanceId：</th>\r\n        <td width=\"329\">\r\n            <input type=\"text\" name=\"$canalParameterGroup.rdsInstanceId.key\" value=\"$!canal.canalParameter.rdsInstanceId\" class=\"setting_input\"/>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#editCanalMessage ($canalParameterGroup.rdsInstanceId)</span>\r\n\t\t</td>\r\n      </tr>\r\n      </div>\r\n\t  \r\n\t  <tr> \r\n        <th width=\"300\">-------------------------------</th>\r\n\t\t<td width=\"329\">---------------------------------------------------------------------------------------------</td>\r\n      </tr>\r\n\t  \r\n\t  <div id=\"storageConfig\">\r\n\t\t<tr> \r\n        <th width=\"300\">存储机制：</th>\r\n        <td width=\"329\">\r\n            <input type=\"radio\" name=\"$canalParameterGroup.storageMode.key\" value=\"MEMORY\" onclick=\"changeDisplay('memoryStorage','table-row');changeDisplay('fileStorage','none');\" id=\"RadioGroup4_0\" #if($!canal.canalParameter.storageMode.isMemory()) checked=\"checked\" #end class=\"radio\" class=\"radio\"/>memory &nbsp;\r\n            <input type=\"radio\" name=\"$canalParameterGroup.storageMode.key\" value=\"FILE\" onclick=\"changeDisplay('memoryStorage','none');changeDisplay('fileStorage','table-row');\" id=\"RadioGroup4_2\" #if($!canal.canalParameter.storageMode.isFile()) checked=\"checked\" #end disabled class=\"radio\"/>file &nbsp;\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#editCanalMessage ($canalParameterGroup.storageMode)</span>\r\n\t\t</td>\r\n       </tr>\r\n\t   <tr class=\"memoryStorage\">\r\n        <th width=\"300\">内存存储batch获取模式：</th>\r\n        <td width=\"329\">\r\n            <input type=\"radio\" name=\"$canalParameterGroup.storageBatchMode.key\" value=\"MEMSIZE\" id=\"RadioGroup6_0\" #if($!canal.canalParameter.storageBatchMode.isMemSize()) checked=\"checked\" #end class=\"radio\"/>MEMSIZE &nbsp;\r\n\t\t\t<input type=\"radio\" name=\"$canalParameterGroup.storageBatchMode.key\" value=\"ITEMSIZE\" id=\"RadioGroup6_1\" #if($!canal.canalParameter.storageBatchMode.isItemSize()) checked=\"checked\" #end class=\"radio\"/>ITEMSIZE &nbsp;\r\n\t\t\t<br />\r\n\t\t\t<span><code>MEMSIZE模式 内存大小计算 = 记录数 * 记录单元大小</code></span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#editCanalMessage ($canalParameterGroup.storageBatchMode)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  <tr class=\"memoryStorage\">\r\n        <th width=\"300\">内存存储buffer记录数：</th>\r\n        <td width=\"329\">\r\n            <input type=\"text\" name=\"$canalParameterGroup.memoryStorageBufferSize.key\" value=\"$!canal.canalParameter.memoryStorageBufferSize\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#editCanalMessage ($canalParameterGroup.memoryStorageBufferSize)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  <tr class=\"memoryStorage\">\r\n        <th width=\"300\">内存存储buffer记录单元大小：</th>\r\n        <td width=\"329\">\r\n            <input type=\"text\" name=\"$canalParameterGroup.memoryStorageBufferMemUnit.key\" value=\"$!canal.canalParameter.memoryStorageBufferMemUnit\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#editCanalMessage ($canalParameterGroup.memoryStorageBufferMemUnit)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  \r\n\t <tr class=\"fileStorage\"> \r\n        <th width=\"300\">文件存储的目录位置：</th>\r\n        <td width=\"329\">\r\n            <input type=\"text\" name=\"$canalParameterGroup.fileStorageDirectory.key\" value=\"$!canal.canalParameter.fileStorageDirectory\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#editCanalMessage ($canalParameterGroup.fileStorageDirectory)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  <tr class=\"fileStorage\"> \r\n        <th width=\"300\">文件存储store记录数：</th>\r\n        <td width=\"329\">\r\n            <input type=\"text\" name=\"$canalParameterGroup.fileStorageStoreCount.key\" value=\"$!canal.canalParameter.fileStorageStoreCount\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#editCanalMessage ($canalParameterGroup.fileStorageStoreCount)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  <tr class=\"fileStorage\"> \r\n        <th width=\"300\">文件存储store文件个数：</th>\r\n        <td width=\"329\">\r\n            <input type=\"text\" name=\"$canalParameterGroup.fileStorageRollverCount.key\" value=\"$!canal.canalParameter.fileStorageRollverCount\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#editCanalMessage ($canalParameterGroup.fileStorageRollverCount)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t <tr class=\"fileStorage\"> \r\n        <th width=\"300\">文件存储store存储占disk百分比：</th>\r\n        <td width=\"329\">\r\n            <input type=\"text\" name=\"$canalParameterGroup.fileStoragePercentThresold.key\" value=\"$!canal.canalParameter.fileStoragePercentThresold\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#editCanalMessage ($canalParameterGroup.fileStoragePercentThresold)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  </div>\r\n\t  \r\n\t  <tr> \r\n        <th width=\"300\">-------------------------------</th>\r\n\t\t<td width=\"329\">---------------------------------------------------------------------------------------------</td>\r\n      </tr>\r\n\t  \r\n\t  <div id=\"haConfig\">\r\n\t  <tr> \r\n        <th width=\"300\">HA机制：</th>\r\n        <td width=\"329\">\r\n            <input type=\"radio\" name=\"$canalParameterGroup.haMode.key\" value=\"HEARTBEAT\" onclick=\"changeDisplay('heartbeatHa','table-row');changeDisplay('mediaHa','none');\" id=\"RadioGroup6_0\" #if($!canal.canalParameter.haMode.isHeartBeat()) checked=\"checked\" #end class=\"radio\"/>heartbeat &nbsp;\r\n            <input type=\"radio\" name=\"$canalParameterGroup.haMode.key\" value=\"MEDIA\" onclick=\"changeDisplay('heartbeatHa','none');changeDisplay('mediaHa','table-row');\" id=\"RadioGroup6_1\" #if($!canal.canalParameter.haMode.isMedia()) checked=\"checked\" #end class=\"radio\"/>media &nbsp;\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#editCanalMessage ($canalParameterGroup.haMode)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  </div>\r\n\t  \r\n\t  <tr class=\"mediaHa\">\r\n        <th width=\"300\">media group key:</th>\r\n        <td width=\"329\">\r\n\t\t\t<input type=\"text\" name=\"$canalParameterGroup.mediaGroup.key\" value=\"$!canal.canalParameter.mediaGroup\" class=\"setting_input\"/>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#editCanalMessage ($canalParameterGroup.mediaGroup)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  \r\n\t  <div id=\"detectConfig\">\r\n\t\t<tr> \r\n        <th width=\"300\">是否开启心跳：</th>\r\n        <td width=\"329\">\r\n            <input type=\"radio\" name=\"$canalParameterGroup.detectingEnable.key\" value=\"true\" onclick=\"changeDisplay('detectEnable','table-row');\" id=\"RadioGroup8_0\" #if($!canal.canalParameter.detectingEnable) checked=\"checked\" #end class=\"radio\"/>是 &nbsp;\r\n            <input type=\"radio\" name=\"$canalParameterGroup.detectingEnable.key\" value=\"false\" onclick=\"changeDisplay('detectEnable','none');\" id=\"RadioGroup8_1\" #if(!$!canal.canalParameter.detectingEnable) checked=\"checked\" #end class=\"radio\"/>否 &nbsp;\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#editCanalMessage ($canalParameterGroup.detectingEnable)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  \r\n\t  <tr class=\"detectEnable\"> \r\n        <th width=\"300\">心跳sql：</th>\r\n        <td width=\"329\">\r\n            <textarea class=\"service\" name=\"$canalParameterGroup.detectingSQL.key\" cols=\"45\" rows=\"5\">$!canal.canalParameter.detectingSQL</textarea><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#editCanalMessage ($canalParameterGroup.detectingSQL) #editCanalMessage ($canalParameterGroup.formHeartBeatError)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  \r\n\t  <tr class=\"detectEnable\"> \r\n        <th width=\"300\">心跳检测频率(s)：</th>\r\n        <td width=\"329\">\r\n            <input type=\"text\" name=\"$canalParameterGroup.detectingIntervalInSeconds.key\" value=\"$!canal.canalParameter.detectingIntervalInSeconds\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#editCanalMessage ($canalParameterGroup.detectingIntervalInSeconds)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  \r\n\t  <tr class=\"detectEnable\"> \r\n        <th width=\"300\">心跳超时时间(s)：</th>\r\n        <td width=\"329\">\r\n            <input type=\"text\" name=\"$canalParameterGroup.detectingTimeoutThresholdInSeconds.key\" value=\"$!canal.canalParameter.detectingTimeoutThresholdInSeconds\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#editCanalMessage ($canalParameterGroup.detectingTimeoutThresholdInSeconds)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  \r\n\t  <tr class=\"detectEnable\"> \r\n        <th width=\"300\">心跳检查重试次数：</th>\r\n        <td width=\"329\">\r\n            <input type=\"text\" name=\"$canalParameterGroup.detectingRetryTimes.key\" value=\"$!canal.canalParameter.detectingRetryTimes\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#editCanalMessage ($canalParameterGroup.detectingRetryTimes)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  \r\n\t  <tr class=\"detectEnable\">\r\n        <th width=\"300\">是否启用心跳HA：</th>\r\n        <td width=\"329\">\r\n\t\t\t<input type=\"radio\" name=\"$canalParameterGroup.heartbeatHaEnable.key\" value=\"true\" id=\"RadioGroup8_0\" #if($!canal.canalParameter.heartbeatHaEnable) checked=\"checked\" #end class=\"radio\"/>是 &nbsp;\r\n            <input type=\"radio\" name=\"$canalParameterGroup.heartbeatHaEnable.key\" value=\"false\" id=\"RadioGroup8_1\" #if(!$!canal.canalParameter.heartbeatHaEnable) checked=\"checked\" #end class=\"radio\"/>否 &nbsp;\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#editCanalMessage ($canalParameterGroup.heartbeatHaEnable)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  </div>\r\n\t  \r\n\t  <div id=\"networkConfig\">\r\n\t  <tr>\r\n\t\t<th width=\"300\">其他参数设置：</th>\r\n        <td width=\"329\">\r\n              <input id=\"networkSuperConfig\" type='checkbox' name='super' value=1  class=\"setting_input\" onclick=\"changeNetworkConfig()\" />\r\n\t\t\t  <br />\r\n        </td>\r\n      </tr>\r\n\t  \r\n\t  <tr class=\"networkSuperConfig\"> \r\n        <th width=\"300\">meta机制：</th>\r\n        <td width=\"329\">\r\n            <input type=\"radio\" name=\"$canalParameterGroup.metaMode.key\" value=\"MEMORY\" id=\"RadioGroup3_0\" #if($!canal.canalParameter.metaMode.isMemory()) checked=\"checked\" #end class=\"radio\"/>memory &nbsp;\r\n            <input type=\"radio\" name=\"$canalParameterGroup.metaMode.key\" value=\"ZOOKEEPER\" id=\"RadioGroup3_1\" #if($!canal.canalParameter.metaMode.isZookeeper()) checked=\"checked\" #end class=\"radio\"/>zookeeper &nbsp;\r\n\t\t\t<input type=\"radio\" name=\"$canalParameterGroup.metaMode.key\" value=\"MIXED\" id=\"RadioGroup3_2\" #if($!canal.canalParameter.metaMode.isMixed()) checked=\"checked\" #end class=\"radio\"/>mixed &nbsp;\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#editCanalMessage ($canalParameterGroup.metaMode)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  \r\n\t  <tr class=\"networkSuperConfig\"> \r\n        <th width=\"300\">索引机制：</th>\r\n        <td width=\"329\">\r\n            <input type=\"radio\" name=\"$canalParameterGroup.indexMode.key\" value=\"MEMORY\" id=\"RadioGroup7_0\" #if($!canal.canalParameter.indexMode.isMemory()) checked=\"checked\" #end class=\"radio\"/>memory &nbsp;\r\n            <input type=\"radio\" name=\"$canalParameterGroup.indexMode.key\" value=\"ZOOKEEPER\" id=\"RadioGroup7_1\" #if($!canal.canalParameter.indexMode.isZookeeper()) checked=\"checked\" #end class=\"radio\"/>zookeeper &nbsp;\r\n\t\t\t<input type=\"radio\" name=\"$canalParameterGroup.indexMode.key\" value=\"MIXED\" id=\"RadioGroup7_2\" #if($!canal.canalParameter.indexMode.isMixed()) checked=\"checked\" #end class=\"radio\"/>mixed &nbsp;\r\n\t\t\t<input type=\"radio\" name=\"$canalParameterGroup.indexMode.key\" value=\"META\" id=\"RadioGroup7_3\" #if($!canal.canalParameter.indexMode.isMeta()) checked=\"checked\" #end class=\"radio\"/>meta &nbsp;\r\n\t\t\t<input type=\"radio\" name=\"$canalParameterGroup.indexMode.key\" value=\"MEMORY_META_FAILBACK\" #if($!canal.canalParameter.indexMode.isMemoryMetaFailback()) checked=\"checked\" #end id=\"RadioGroup7_4\" class=\"radio\"/>memory_meta_failback &nbsp;\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#editCanalMessage ($canalParameterGroup.indexMode)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  <tr class=\"networkSuperConfig\"> \r\n        <th width=\"300\">服务端口：</th>\r\n        <td width=\"329\">\r\n            <input type=\"text\" name=\"$canalParameterGroup.port.key\" value=\"$!canal.canalParameter.port\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#editCanalMessage ($canalParameterGroup.port)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  \r\n\t <tr class=\"networkSuperConfig\"> \r\n        <th width=\"300\">默认连接超时(s)：</th>\r\n        <td width=\"329\">\r\n            <input type=\"text\" name=\"$canalParameterGroup.defaultConnectionTimeoutInSeconds.key\" value=\"$!canal.canalParameter.defaultConnectionTimeoutInSeconds\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#editCanalMessage ($canalParameterGroup.defaultConnectionTimeoutInSeconds)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  \r\n\t  <tr class=\"networkSuperConfig\"> \r\n        <th width=\"300\">receiveBufferSize：</th>\r\n        <td width=\"329\">\r\n            <input type=\"text\" name=\"$canalParameterGroup.receiveBufferSize.key\" value=\"$!canal.canalParameter.receiveBufferSize\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#editCanalMessage ($canalParameterGroup.receiveBufferSize)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  \r\n\t  <tr class=\"networkSuperConfig\"> \r\n        <th width=\"300\">sendBufferSize：</th>\r\n        <td width=\"329\">\r\n            <input type=\"text\" name=\"$canalParameterGroup.sendBufferSize.key\" value=\"$!canal.canalParameter.sendBufferSize\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#editCanalMessage ($canalParameterGroup.sendBufferSize)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  \r\n\t  <tr class=\"networkSuperConfig\"> \r\n        <th width=\"300\">切换回退时间：</th>\r\n        <td width=\"329\">\r\n            <input type=\"text\" name=\"$canalParameterGroup.fallbackIntervalInSeconds.key\" value=\"$!canal.canalParameter.fallbackIntervalInSeconds\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#editCanalMessage($canalParameterGroup.fallbackIntervalInSeconds)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  </div>\r\n\t  \r\n\t <tr>\r\n       <th>过滤表达式：</th>\r\n       <td>\r\n    \t\t<textarea cols=\"45\" rows=\"5\" name=\"$canalParameterGroup.blackFilter.key\">$!canal.canalParameter.blackFilter</textarea><span class=\"red\">*</span>\r\n    \t\t<br />\r\n    \t\t<span class=\"red\">#editCanalMessage ($canalParameterGroup.blackFilter)</span>\r\n\t   </td>\r\n      </tr>\r\n\t  <tr>\r\n       <th>描述信息：</th>\r\n       <td>\r\n    \t\t<textarea cols=\"45\" rows=\"5\" name=\"$canalGroup.desc.key\">$!canal.desc</textarea><span class=\"red\">*</span>\r\n    \t\t<br />\r\n    \t\t<span class=\"red\">#editCanalMessage ($canalGroup.desc)</span>\r\n\t   </td>\r\n      </tr>\r\n    </table>\r\n </div>\r\n   <div class=\"btn\"><a href=\"javascript:document.editCanalForm.submit();\">保存</a></div> \r\n  \r\n  </form>\r\n</div>\r\n\t\r\n<script language=\"javascript\">\r\n<!--\r\n\t#if($!canal.canalParameter.sourcingType.isMysql())\r\n\t\tchangeDisplay('mysqlSourcing','table-row')\r\n\t#else\r\n\t\tchangeDisplay('mysqlSourcing','none')\r\n\t#end\r\n\r\n\t#if($!canal.canalParameter.sourcingType.isOracle())\r\n\t\tchangeDisplay('oracleSourcing','table-row')\r\n\t#else\r\n\t\tchangeDisplay('oracleSourcing','none')\r\n\t#end\r\n\r\n\t#if($!canal.canalParameter.sourcingType.isLocalBinlog())\r\n\t\tchangeDisplay('localSourcing','table-row')\r\n\t#else\r\n\t\tchangeDisplay('localSourcing','none')\r\n\t#end\r\n\r\n\t#if($!canal.canalParameter.sourcingType.isOceanBase())\r\n\t\tchangeDisplay('obSourcing','table-row')\r\n\t#else\r\n\t\tchangeDisplay('obSourcing','none')\r\n\t#end\r\n\t\r\n\t#if($!canal.canalParameter.storageMode.isMemory())\r\n\t\tchangeDisplay('memoryStorage','table-row')\r\n\t#else\r\n\t\tchangeDisplay('memoryStorage','none')\r\n\t#end\r\n\r\n\t#if($!canal.canalParameter.storageMode.isFile())\r\n\t\tchangeDisplay('fileStorage','table-row')\r\n\t#else\r\n\t\tchangeDisplay('fileStorage','none')\r\n\t#end\r\n\t\r\n\t#if($!canal.canalParameter.haMode.isHeartBeat())\r\n\t\tchangeDisplay('heartbeatHa','table-row')\r\n\t#else\r\n\t\tchangeDisplay('heartbeatHa','none')\r\n\t#end\r\n\r\n\t#if($!canal.canalParameter.haMode.isMedia())\r\n\t\tchangeDisplay('mediaHa','table-row')\r\n\t#else\r\n    \tchangeDisplay('mediaHa','none')\r\n\t#end\r\n\t\r\n\t#if($!canal.canalParameter.detectingEnable)\r\n\t\tchangeDisplay('detectEnable','table-row')\r\n\t#else\r\n\t\tchangeDisplay('detectEnable','none')\r\n\t#end\r\n\t\r\n\tchangePositionConfig();\r\n\tchangeNetworkConfig();\r\n//-->\r\n</script>"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/templates/home/screen/editChannel.vm",
    "content": "$control.setTemplate(\"home:navigation.vm\")\r\n<script language=\"javascript\">\r\n<!--\r\n\tchangeNav(\"sync\");\r\n//-->\r\n</script>\r\n#macro (editChannelMessage $field)\r\n    #if (!$field.valid) $field.message #end\r\n#end\r\n<div class=\"main\">\r\n  <div class=\"title\"> \r\n    <h2>编辑Channel</h2>\r\n  </div>\r\n <div class=\"crumbs\"><a href=\"channelList.htm?channelId=$channel.id\">Channel管理</a>&nbsp;&nbsp;>&nbsp;&nbsp;<a href=\"editChannel.htm?channelId=$channel.id\">编辑Channel</a></div>\r\n \r\n <form name=\"editChannelForm\" method=\"post\" enctype=\"multipart/form-data\">\r\n\t$csrfToken.hiddenField\r\n\t<input type=\"hidden\" name=\"action\" value=\"channel_action\"/>\r\n\t<input type=\"hidden\" name=\"event_submit_do_edit\" value=\"1\" />\r\n\t<input type=\"hidden\" id=\"pageIndex\" name=\"pageIndex\" value=\"$!pageIndex\"/>\r\n\t<input type=\"hidden\" id=\"searchKey\" name=\"searchKey\" value=\"$!searchKey\"/>\r\n <div class=\"setting_box\">\r\n\t\r\n\t#set ($channelGroup = $form.channelInfo.defaultInstance)\r\n\t#set ($channelParameterGroup = $form.channelParameterInfo.defaultInstance)\r\n\t\r\n\t<input type=\"hidden\" name=\"$channelGroup.id.key\" value=\"$channel.id\" />\r\n\t<input type=\"hidden\" name=\"channelId\" value=\"$channel.id\" />\r\n    <table cellpadding=\"0\" cellspacing=\"0\" class=\"setting_otter\">\r\n      <tr> \r\n        <th width=\"300\">Channel Name：</th>\r\n        <td width=\"329\">\r\n\t\t\t<input type=\"text\" name=\"$channelGroup.name.key\" value=\"$channel.name\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#editChannelMessage ($channelGroup.name) #editChannelMessage ($channelGroup.formChannelError)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  <tr> \r\n        <th>同步一致性：</th>\r\n        <td>\r\n\t\t\t<input name=\"$channelParameterGroup.SyncConsistency.key\" type=\"radio\" value=\"MEDIA\" #if ($channel.parameters.SyncConsistency.isMedia()) checked='checked' #end class=\"radio\"/>\r\n\t\t\t基于数据库反查&nbsp;&nbsp;&nbsp;&nbsp; \r\n\t\t\t<input name=\"$channelParameterGroup.SyncConsistency.key\" type=\"radio\" value=\"BASE\" #if ($channel.parameters.SyncConsistency.isBase()) checked='checked' #end class=\"radio\"/>\r\n\t\t\t基于当前日志变更\r\n\t\t</td>\r\n      </tr>\r\n       <tr> \r\n        <th>同步模式：</th>\r\n        <td>\r\n\t\t\t<input name=\"$channelParameterGroup.syncMode.key\" type=\"radio\" value=\"ROW\" #if ($channel.parameters.SyncMode.isRow()) checked='checked' #end class=\"radio\"/>行记录模式\r\n\t\t\t<input name=\"$channelParameterGroup.syncMode.key\" type=\"radio\" value=\"FIELD\" #if ($channel.parameters.SyncMode.isField()) checked='checked' #end class=\"radio\"/>列记录模式\r\n\t\t</td>\r\n      </tr>\r\n\t  <tr> \r\n        <th>是否开启数据一致性：</th>\r\n        <td>\r\n\t\t\t<input type=\"radio\" name=\"$channelParameterGroup.enableRemedy.key\" value=\"true\" onclick=\"changeDisplay('enableRemedy','table-row')\" #if ($channel.parameters.isEnableRemedy()) checked='checked' #end class=\"radio\"/>是\r\n            <input type=\"radio\" name=\"$channelParameterGroup.enableRemedy.key\" value=\"false\" onclick=\"changeDisplay('enableRemedy','none')\" #if (!$channel.parameters.isEnableRemedy()) checked='checked' #end class=\"radio\"/>否 \r\n\t\t</td>\r\n      </tr>\r\n\t  <tr class=\"enableRemedy\"> \r\n        <th>一致性算法：</th>\r\n        <td>\r\n\t\t\t<input name=\"$channelParameterGroup.remedyAlgorithm.key\" type=\"radio\" value=\"LOOPBACK\" #if ($channel.parameters.remedyAlgorithm.isLoopback()) checked='checked' #end class=\"radio\"/>\r\n\t\t\t单向回环补救&nbsp;&nbsp;&nbsp;&nbsp; \r\n\t\t\t<input name=\"$channelParameterGroup.remedyAlgorithm.key\" type=\"radio\" value=\"INTERSECTION\" #if ($channel.parameters.remedyAlgorithm.isIntersection()) checked='checked' #else disabled #end class=\"radio\"/>\r\n\t\t\t时间交集补救&nbsp;&nbsp;&nbsp;\r\n\t\t</td>\r\n      </tr>\r\n\t  <tr class=\"enableRemedy\"> \r\n        <th>一致性反查数据库延迟阀值(s)：</th>\r\n        <td>\r\n              <input name=\"$channelParameterGroup.remedyDelayThresoldForMedia.key\" value=\"$!channel.parameters.remedyDelayThresoldForMedia\" type=\"text\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t  <br />\r\n\t\t\t  <span class=\"red\">#editChannelMessage ($channelParameterGroup.remedyDelayThresoldForMedia)</span>\r\n        </td>\r\n      </tr>\r\n      <tr>\r\n       <th>描述：</th>\r\n       <td>\r\n\t\t\t<textarea cols=\"45\" rows=\"5\" name=\"$channelGroup.description.key\">$!channel.description</textarea><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#editChannelMessage ($!channelGroup.description)</span>\r\n\t   </td>\r\n      </tr>\r\n    </table>\r\n </div>\r\n   <div class=\"btn\"><a href=\"javascript:document.editChannelForm.submit();\">保存</a></div> \r\n  </form>\r\n</div>\r\n\t\r\n<script language=\"javascript\">\r\n<!--\r\n\t#if(!$channel.parameters.isEnableDetect())\r\n\t\tchangeDisplay('enableDetect','none')\r\n\t#end\r\n\r\n\t#if(!$channel.parameters.isEnableRemedy())\r\n\t\tchangeDisplay('enableRemedy','none')\r\n\t#end\r\n//-->\r\n</script>"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/templates/home/screen/editDataMatrix.vm",
    "content": "$control.setTemplate(\"home:navigation.vm\")\r\n<script language=\"javascript\">\r\n<!--\r\n\tchangeNav(\"datamedia\");\r\n//-->\r\n</script>\r\n#macro (editDataMatrixMessage $field)\r\n    #if (!$field.valid) $field.message #end\r\n#end\t\r\n\r\n<div class=\"main\">\r\n  <div class=\"title\"> \r\n    <h2>添加主备配置</h2>\r\n  </div>\r\n  <div class=\"crumbs\"><a href=\"dataMatrixList.htm\">主备配置</a>&nbsp;&nbsp;>&nbsp;&nbsp;<a href=\"addDataMatrix.htm\">添加主备配置</a></div>\r\n\r\n <form id=\"editDataMatrixForm\" name=\"editDataMatrixForm\" method=\"post\" enctype=\"multipart/form-data\">\r\n\t$csrfToken.hiddenField\r\n\t<input type=\"hidden\" name=\"action\" value=\"data_matrix_action\"/>\r\n\t<input type=\"hidden\" name=\"event_submit_do_edit\" value=\"1\" />\r\n <div class=\"setting_box\">\r\n\t#set ($dataMatrixGroup = $form.dataMatrixInfo.defaultInstance)\r\n    <table cellpadding=\"0\" cellspacing=\"0\" class=\"setting_otter\">\r\n\t\t<tr>\r\n        \t<th width=\"300\"></th>\r\n        \t<td width=\"329\">\r\n        \t\t#foreach($f in ${dataMatrixGroup.getFields()})\r\n        \t\t\t#if (!$f.valid) \r\n        \t\t\t\t<span class=\"red\">$!f.message</span><br/>\r\n        \t\t\t#end\r\n        \t\t#end\r\n        \t</td>\r\n        </tr>\r\n\t\t\r\n\t<input type=\"hidden\" name=\"$dataMatrixGroup.id.key\" value=\"$!dataMatrix.id\" />\r\n\t<input type=\"hidden\" name=\"matrixId\" value=\"$!dataMatrix.id\" />\r\n\t\r\n      <tr> \r\n        <th width=\"300\">group Key：</th>\r\n        <td width=\"329\">\r\n\t\t\t<input id=\"groupKey\" name=\"$dataMatrixGroup.groupKey.key\" value=\"$!dataMatrix.groupKey\" type=\"text\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#editDataMatrixMessage ($dataMatrixGroup.groupKey)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  <tr> \r\n        <th width=\"300\">master：</th>\r\n        <td width=\"329\">\r\n\t\t\t<input id=\"master\" name=\"$dataMatrixGroup.master.key\" value=\"$!dataMatrix.master\" type=\"text\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#editDataMatrixMessage ($dataMatrixGroup.master)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  <tr> \r\n        <th width=\"300\">slaves：</th>\r\n        <td width=\"329\">\r\n\t\t\t<input id=\"slaves\" name=\"$dataMatrixGroup.slave.key\" value=\"$!dataMatrix.slave\" type=\"text\" class=\"setting_input\" />\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#editDataMatrixMessage ($dataMatrixGroup.slave)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t   <tr>\r\n       <th>描述信息：</th>\r\n       <td>\r\n    \t\t<textarea cols=\"45\" rows=\"5\" name=\"$dataMatrixGroup.description.key\">$!dataMatrix.description</textarea><span class=\"red\">*</span>\r\n    \t\t<br />\r\n    \t\t<span class=\"red\">#editDataMatrixMessage ($dataMatrixGroup.description)</span>\r\n\t   </td>\r\n      </tr>   \r\n    </table>\r\n </div>\r\n  <div class=\"btn\"><a href=\"javascript:document.editDataMatrixForm.submit();\">保存</a></div>\r\n  #set ($switchRL = $homeModule.setAction(\"dataMatrixAction\").addQueryData(\"matrixId\", $dataMatrix.id).addQueryData(\"eventSubmitDoSwitch\", \"true\"))\r\n  <div class=\"btn\"><a href=\"javascript:if(confirm('确实要执行主备IP切换吗?'))location='$switchRL'\">切换</a></div>\r\n  </form>\r\n  <br />\r\n  <br />\r\n  <br />\r\n</div>\r\n"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/templates/home/screen/editDataMedia.vm",
    "content": "$control.setTemplate(\"home:navigation.vm\")\r\n<script type='text/javascript' src='dwr/interface/Hello.js'></script>  \r\n<script type='text/javascript' src='dwr/engine.js'></script>  \r\n<script type='text/javascript' src='dwr/util.js'></script>  \r\n<script type='text/javascript' src='js/dbCheck.js'></script>\r\n<script language=\"javascript\">\r\n<!--\r\n\tchangeNav(\"datamedia\");\r\n//-->\r\nfunction changeKeyword(id, name) {\r\n\tif( document.getElementById(\"dataSourceId\") && document.getElementById(\"dataSourceName\")){\r\n    \tdocument.getElementById('dataSourceId').value = id;\r\n    \tdocument.getElementById('dataSourceName').value = name;\r\n\t}\r\n}\r\n</script>\r\n#macro (editDataMediaMessage $field)\r\n    #if (!$field.valid) $field.message #end\r\n#end\t\r\n\r\n<div class=\"main\">\r\n  <div class=\"title\"> \r\n    <h2>编辑数据表</h2>\r\n  </div>\r\n  <div class=\"crumbs\"><a href=\"dataMediaList.htm\">数据表配置</a>&nbsp;&nbsp;>&nbsp;&nbsp;<a href=\"editDataMedia.htm?dataMediaId=$dataMedia.id\">编辑数据表</a></div>\r\n  \r\n  <form name=\"editDataMediaForm\" method=\"post\" enctype=\"multipart/form-data\">\r\n\t$csrfToken.hiddenField\r\n\t<input type=\"hidden\" name=\"action\" value=\"data_media_action\"/>\r\n\t<input type=\"hidden\" name=\"event_submit_do_edit\" value=\"1\" />\r\n\t<input type=\"hidden\" id=\"pageIndex\" name=\"pageIndex\" value=\"$!pageIndex\"/>\r\n\t<input type=\"hidden\" id=\"searchKey\" name=\"searchKey\" value=\"$!searchKey\"/>\r\n <div class=\"setting_box\">\r\n\t#set ($dataMediaGroup = $form.dataMediaInfo.defaultInstance)\r\n\t<input type=\"hidden\" name=\"dataMediaId\" value=\"$dataMedia.id\" />\r\n\t<input type=\"hidden\" name=\"$dataMediaGroup.id.key\" value=\"$dataMedia.id\" />\r\n    <table cellpadding=\"0\" cellspacing=\"0\" class=\"setting_otter\">\r\n\t\t<tr>\r\n            <span class=\"red\">#editDataMediaMessage ($dataMediaGroup.formDataMediaError)</span>\r\n        </tr>\r\n\t  <tr> \r\n        <th width=\"300\">schema name：</th>\r\n        <td width=\"329\">\r\n\t\t\t<input id=\"namespace\" name=\"$dataMediaGroup.namespace.key\" value=\"$!dataMedia.namespace\" type=\"text\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#editDataMediaMessage ($dataMediaGroup.namespace)</span>\r\n\t\t</td>\r\n      </tr>\r\n      <tr>\r\n        <th width=\"300\">table name：</th>\r\n        <td width=\"329\">\r\n\t\t\t<input id=\"name\" name=\"$dataMediaGroup.name.key\" value=\"$!dataMedia.name\" type=\"text\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#editDataMediaMessage ($dataMediaGroup.name)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  \r\n\t  <tr> \r\n        <th>数据源：</th>\r\n        <td>\r\n\t\t\t<input id=\"dataSourceName\" type=\"text\" name=\"$dataMediaGroup.sourceName.key\" value=\"$!dataMedia.source.name\" class=\"setting_input\" readonly  />\r\n\t\t\t<input id=\"dataSourceId\" name=\"$dataMediaGroup.sourceId.key\" value=\"$!dataMedia.source.id\" type=\"hidden\" class=\"setting_input\"  />\r\n\t\t\t<input type=\"button\" value=\"查找数据源\" onclick=\"window.open('selectDataSource.htm', 'selectDataSource')\"><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#editDataMediaMessage ($dataMediaGroup.sourceId)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  <tr> \r\n        <th><input type='button' value='验证连接表' onclick='checkMap();' /><br><hr align=\"right\" style=\"width:100px;\">\r\n            <input type='button' value='查询Schema&Table' onclick='checkNamespaceTables()' />\r\n\t\t\t</th>\r\n        <td>\r\n\t\t\t<span class=\"red\" id=\"result\"></span>\r\n\t\t</td>\r\n      </tr>\r\n\t  <tr> \r\n        <th>table示例说明</th>\r\n        <td>\r\n\t\t\t<br/>\r\n\t\t\t单表配置: alibaba.product<br/>\r\n\t\t\t分表配置: alibaba[1-64].product , alibaba.product[01-32]<br/>\r\n\t\t\t正则配置: (.*).(.*)<br/>\r\n\t\t</td>\r\n      </tr>\r\n    </table>\r\n </div>\r\n  <div class=\"btn\"><a href=\"javascript:document.editDataMediaForm.submit();\">保存</a></div>\r\n  </form>\r\n  <br />\r\n  <br />\r\n  <br />\r\n</div>\r\n"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/templates/home/screen/editDataMediaPair.vm",
    "content": "$control.setTemplate(\"home:navigation.vm\")\r\n<script language=\"javascript\">\r\n<!--\r\n\tchangeNav(\"sync\");\r\n//-->\r\nfunction changeKeyword(id, name, local) {\r\n\tif(local == 'source'){\r\n    \tif( document.getElementById(\"sourceDataMediaId\") && document.getElementById(\"sourceDataMediaName\")){\r\n    \t\tdocument.getElementById('sourceDataMediaId').value = id;\r\n        \tdocument.getElementById('sourceDataMediaName').value = name;\r\n    \t}\r\n\t}else{\r\n\t\tif( document.getElementById(\"targetDataMediaId\") && document.getElementById(\"targetDataMediaName\")){\r\n    \t\tdocument.getElementById('targetDataMediaId').value = id;\r\n        \tdocument.getElementById('targetDataMediaName').value = name;\r\n    \t}\r\n\t}\r\n\t\r\n}\r\n</script>\r\n#macro (editDataMediaPairMessage $field)\r\n    #if (!$field.valid) $field.message #end\r\n#end\t\r\n\r\n<div class=\"main\">\r\n  <div class=\"title\"> \r\n    <h2>编辑映射关系</h2>\r\n  </div> <div class=\"crumbs\"><a href=\"channelList.htm?channelId=$channelId\">Channel管理</a>&nbsp;&nbsp;>&nbsp;&nbsp;<a href=\"pipelineList.htm?channelId=$channelId\">Pipeline管理</a>&nbsp;&nbsp;>&nbsp;&nbsp;<a href=\"dataMediaPairList.htm?pipelineId=$dataMediaPair.pipelineId\">映射关系管理</a>&nbsp;&nbsp;>&nbsp;&nbsp;<a href=\"editDataMediaPair.htm?pipelineId=$dataMediaPair.pipelineId&dataMediaPairId=$dataMediaPair.id\">编辑映射关系</a> </div> \r\n <form name=\"editDataMediaPairForm\" method=\"post\" enctype=\"multipart/form-data\">\r\n\t$csrfToken.hiddenField\r\n\t<input type=\"hidden\" name=\"action\" value=\"data_media_pair_action\"/>\r\n\t<input type=\"hidden\" name=\"event_submit_do_edit\" value=\"1\" />\r\n <div class=\"setting_box\">\r\n\t#set ($dataMediaPairGroup = $form.dataMediaPairInfo.defaultInstance)\r\n\t<input type=\"hidden\" name=\"$dataMediaPairGroup.id.key\" value=\"$dataMediaPair.id\" />\r\n\t<input type=\"hidden\" name=\"dataMediaPairId\" value=\"$dataMediaPair.id\" />\r\n\t<input type=\"hidden\" name=\"pipelineId\" value=\"$dataMediaPair.pipelineId\"/>\r\n\t<input type=\"hidden\" name=\"channelId\" value=\"$channelId\"/>\r\n\t<input type=\"hidden\" name=\"$dataMediaPairGroup.pipelineId.key\" value=\"$dataMediaPair.pipelineId\"/>\r\n    <table cellpadding=\"0\" cellspacing=\"0\" class=\"setting_otter\">\r\n\t\t<span class=\"red\">#editDataMediaPairMessage ($dataMediaPairGroup.formDataMediaPairError)</span>\r\n      <tr> \r\n        <th>源数据表：</th>\r\n        <td>\r\n\t\t\t<input id=\"sourceDataMediaName\" type=\"text\" name=\"$dataMediaPairGroup.sourceDataMediaName.key\" value=\"$!dataMediaPair.source.name\" class=\"setting_input\" readonly />\r\n\t\t\t<input id=\"sourceDataMediaId\" name=\"$dataMediaPairGroup.sourceDataMediaId.key\" value=\"$!dataMediaPair.source.id\" type=\"hidden\" class=\"setting_input\"  />\r\n\t\t\t<input type=\"button\" value=\"查找数据表\" onclick=\"window.open('selectDataMedia.htm?local=source', 'selectSourceDataMedia')\"><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#editDataMediaPairMessage ($dataMediaPairGroup.sourceDataMediaId)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  <tr> \r\n        <th>目标数据表：</th>\r\n        <td>\r\n\t\t\t<input id=\"targetDataMediaName\" type=\"text\" name=\"$dataMediaPairGroup.targetDataMediaName.key\" value=\"$!dataMediaPair.target.name\" class=\"setting_input\" readonly />\r\n\t\t\t<input id=\"targetDataMediaId\" name=\"$dataMediaPairGroup.targetDataMediaId.key\" value=\"$!dataMediaPair.target.id\" type=\"hidden\" class=\"setting_input\"  />\r\n\t\t\t<input type=\"button\" value=\"查找数据表\" onclick=\"window.open('selectDataMedia.htm?local=target', 'selectTargerDataMedia')\"><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#editDataMediaPairMessage ($dataMediaPairGroup.targetDataMediaId)</span>\r\n\t\t</td>\r\n      </tr>\r\n      <tr>\r\n        <th>权重:</th>\r\n        <td>\r\n          <input type=\"text\" name=\"$dataMediaPairGroup.pushWeight.key\" value=\"$!dataMediaPair.pushWeight\" id=\"textfield5\" value=\"0\" /><span class=\"red\">*</span>\r\n\t\t  <br />\r\n\t\t  <span class=\"red\">#editDataMediaPairMessage ($dataMediaPairGroup.pushWeight)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  <tr>\r\n        <th>视图模式:</th>\r\n        <td>\r\n\t\t  <input type=\"radio\" name=\"$dataMediaPairGroup.columnPairMode.key\" value=\"INCLUDE\" id=\"RadioGroup2_0\" #if($!dataMediaPair.columnPairMode.isInclude()) checked=\"checked\" #end class=\"radio\"/>include\r\n          <input type=\"radio\" name=\"$dataMediaPairGroup.columnPairMode.key\" value=\"EXCLUDE\" id=\"RadioGroup2_1\" #if($!dataMediaPair.columnPairMode.isExclude()) checked=\"checked\" #end class=\"radio\"/>exclude\r\n\t\t  <br />\r\n\t\t  <span class=\"red\">#editDataMediaPairMessage ($dataMediaPairGroup.columnPairMode)</span>\r\n        </td>\r\n      </tr>\r\n\t  <tr>\r\n        <th>EventProcessor类型:</th>\r\n\t\t<td>\r\n        <select name=\"$dataMediaPairGroup.filterType.key\" id=\"select\">\r\n\t\t\t\t<option value=\"CLAZZ\" #if ($dataMediaPair.filterData.ExtensionDataType.isClazz()) selected=\"selected\" #end>CLAZZ</option>\r\n\t\t\t\t<option value=\"SOURCE\" #if ($dataMediaPair.filterData.ExtensionDataType.isSource()) selected=\"selected\" #end>SOURCE</option>\r\n            </select><span class=\"red\">*</span>\r\n\t\t\t</td>\r\n      </tr>\r\n      <tr>\r\n        <th>EventProcessor文本:</th>\r\n\t\t<td><textarea cols=\"90\" rows=\"10\" name=\"$dataMediaPairGroup.filterText.key\">#if ($dataMediaPair.filterData.ExtensionDataType.isClazz())$!dataMediaPair.filterData.clazzPath#elseif($dataMediaPair.filterData.ExtensionDataType.isSource())$!dataMediaPair.filterData.sourceText#end</textarea><span class=\"red\">*</span></td>\r\n      </tr>\r\n\t  <tr>\r\n        <th>FileResolver类型:</th>\r\n\t\t<td>\r\n        <select name=\"$dataMediaPairGroup.resolverType.key\" id=\"select\">\r\n\t\t\t\t<option value=\"CLAZZ\" #if ($dataMediaPair.resolverData.ExtensionDataType.isClazz()) selected=\"selected\" #end>CLAZZ</option>\r\n\t\t\t\t<option value=\"SOURCE\" #if ($dataMediaPair.resolverData.ExtensionDataType.isSource()) selected=\"selected\" #end>SOURCE</option>\r\n            </select><span class=\"red\">*</span>\r\n\t\t\t</td>\r\n      </tr>\r\n      <tr>\r\n        <th>FileResolver文本:</th>\r\n\t\t<td><textarea cols=\"90\" rows=\"10\" name=\"$dataMediaPairGroup.resolverText.key\">#if ($dataMediaPair.resolverData.ExtensionDataType.isClazz())$!dataMediaPair.resolverData.clazzPath#elseif($dataMediaPair.resolverData.ExtensionDataType.isSource())$!dataMediaPair.resolverData.sourceText#end</textarea><span class=\"red\">*</span></td>\r\n      </tr>\r\n    </table>\r\n </div>\r\n  <input type=\"submit\" name=\"submitKey\" value=\"保存\" class=\"button\"></input>\r\n  <input type=\"submit\" name=\"submitKey\" value=\"下一步\" class=\"button\"></input>\r\n </form>\r\n</div>\r\n"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/templates/home/screen/editDataSource.vm",
    "content": "$control.setTemplate(\"home:navigation.vm\")\r\n<script type='text/javascript' src='dwr/interface/Hello.js'></script>  \r\n<script type='text/javascript' src='dwr/engine.js'></script>  \r\n<script type='text/javascript' src='dwr/util.js'></script>  \r\n<script type='text/javascript' src='js/dbCheck.js'></script>\r\n<script language=\"javascript\">\r\n<!--\r\n\tchangeNav(\"datamedia\");\r\n//-->\r\n</script>\r\n#macro (editDataSourceMessage $field)\r\n    #if (!$field.valid) $field.message #end\r\n#end\r\n\r\n<div class=\"main\">\r\n  <div class=\"title\"> \r\n    <h2>编辑数据源</h2>\r\n  </div>\r\n  <div class=\"crumbs\"><a href=\"dataSourceList.htm\">数据源配置</a>&nbsp;&nbsp;>&nbsp;&nbsp;<a href=\"editDataSource.htm?dataMediaSourceId=$source.id\">编辑数据源</a></div>\r\n \r\n <form name=\"editSourceForm\" method=\"post\" enctype=\"multipart/form-data\">\r\n $csrfToken.hiddenField\r\n\t<input type=\"hidden\" name=\"action\" value=\"data_media_source_action\"/>\r\n\t<input type=\"hidden\" name=\"event_submit_do_edit\" value=\"1\" />\r\n\t<input type=\"hidden\" id=\"pageIndex\" name=\"pageIndex\" value=\"$!pageIndex\"/>\r\n\t<input type=\"hidden\" id=\"searchKey\" name=\"searchKey\" value=\"$!searchKey\"/>\r\n <div class=\"setting_box\">\r\n\t#set ($dataMediaSourceGroup = $form.dataMediaSourceInfo.defaultInstance)\r\n\t<input type=\"hidden\" name=\"$dataMediaSourceGroup.id.key\" value=\"$source.id\" />\r\n    <input type=\"hidden\" name=\"dataMediaSourceId\" value=\"$source.id\" />\r\n\t<table cellpadding=\"0\" cellspacing=\"0\" class=\"setting_otter\">\r\n        <span class=\"red\">#editDataSourceMessage ($dataMediaSourceGroup.formDataMediaSourceError)</span>\r\n      <tr> \r\n        <th width=\"300\">数据源名字：</th>\r\n        <td width=\"329\">\r\n\t\t\t<input name=\"$dataMediaSourceGroup.name.key\" type=\"text\" value=\"$source.name\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#editDataSourceMessage ($dataMediaSourceGroup.name)</span>\r\n\t\t</td>\r\n      </tr>\r\n      \r\n      <tr> \r\n        <th>类型：</th>\r\n        <td>\r\n            <select id=\"sourceType\" name=\"$dataMediaSourceGroup.type.key\">\t\t\t\r\n\t\t\t\t<option value=\"MYSQL\" #if($source.type.isMysql()) selected=\"selected\" #end>MySQL</option>\r\n\t\t\t\t<option value=\"ORACLE\" #if($source.type.isOracle()) selected=\"selected\" #end>Oracle</option>\r\n\t\t\t</select><span class=\"red\">*</span>\r\n        </td>\r\n\t  </tr>\r\n      <tr> \r\n        <th>用户名：</th>\r\n        <td>\r\n\t\t\t<input id=\"sourceUserName\" name=\"$dataMediaSourceGroup.username.key\" value=\"$!source.username\" type=\"text\" class=\"setting_input\"/>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#editDataSourceMessage ($dataMediaSourceGroup.username)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  <tr> \r\n        <th>密码：</th>\r\n        <td>\r\n\t\t\t<input id=\"sourcePassword\" name=\"$dataMediaSourceGroup.password.key\" value=\"$!source.password\" type=\"password\" class=\"setting_input\"/>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#editDataSourceMessage ($dataMediaSourceGroup.password)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  <tr> \r\n        <th>URL：</th>\r\n        <td>\r\n\t\t\t<input id=\"sourceUrl\" name=\"$dataMediaSourceGroup.url.key\" value=\"$source.url\" type=\"text\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#editDataSourceMessage ($dataMediaSourceGroup.url)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  #**\r\n\t   <tr> \r\n        <th>StorePath：</th>\r\n        <td>\r\n\t\t\t<input id=\"sourceStorePath\" name=\"$dataMediaSourceGroup.storePath.key\" value=\"$!source.storePath\" type=\"text\" class=\"setting_input\"/><span class=\"red\">Napoli</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#editDataSourceMessage ($dataMediaSourceGroup.storePath)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  *#\r\n\t  <tr> \r\n        <th>编码：</th>\r\n        <td>\r\n\t\t\t<select id=\"sourceEncode\" name=\"$dataMediaSourceGroup.encode.key\"  style=\"width:200px;\" > \r\n\t\t\t\t<option value=\"GBK\" #if($!source.encode == 'GBK') selected  #end>GBK</option>\r\n\t\t\t\t<option value=\"UTF8\" #if($!source.encode == 'UTF8') selected  #end>UTF8</option>\r\n\t\t\t\t<option value=\"UTF8MB4\" #if($!source.encode == 'UTF8MB4') selected  #end>UTF8MB4</option>\r\n\t\t\t\t<option value=\"ISO-8859-1\" #if($!source.encode == 'ISO-8859-1') selected  #end>ISO-8859-1</option>\r\n            </select><span class=\"red\">*</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  <tr> \r\n        <th><input type='button' value='验证连接数据源' onclick='check();' /></th>\r\n        <td>\r\n\t\t\t<span class=\"red\" id=\"result\"></span>\r\n\t\t</td>\r\n      </tr>\r\n\t  <tr> \r\n        <th>url示例说明</th>\r\n        <td>\r\n\t\t\tmysql例子: jdbc:mysql://10.20.144.15:3306<br/>\r\n\t\t\toracle例子 : jdbc:oracle:thin:@10.20.144.29:1521:OINTEST<br/>\r\n\t\t\tmedia例子 : jdbc:mysql://groupKey=key (更改 key)\r\n\t\t</td> \r\n      </tr>\r\n      \r\n    </table>\r\n </div>\r\n  <div class=\"btn\"><a href=\"javascript:document.editSourceForm.submit();\">保存</a></div>\r\n </form>\r\n</div>\r\n"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/templates/home/screen/editNode.vm",
    "content": "$control.setTemplate(\"home:navigation.vm\")\r\n<script language=\"javascript\">\r\n<!--\r\n\tchangeNav(\"node\");\r\n//-->\r\n</script>\r\n#macro (editNodeMessage $field)\r\n    #if (!$field.valid) $field.message #end\r\n#end\t\r\n\r\n<div class=\"main\">\r\n  <div class=\"title\"> \r\n    <h2>编辑机器</h2>\r\n  </div>\r\n <div class=\"crumbs\"><a href=\"nodeList.htm\">机器管理</a>&nbsp;&nbsp;>&nbsp;&nbsp;<a href=\"addNode.htm\">编辑机器</a></div>\r\n \r\n \r\n<form name=\"editNodeForm\" method=\"post\" enctype=\"multipart/form-data\">\r\n\t$csrfToken.hiddenField\r\n\t<input type=\"hidden\" name=\"action\" value=\"node_action\"/>\r\n\t<input type=\"hidden\" name=\"event_submit_do_edit\" value=\"1\" />\r\n\t<input type=\"hidden\" id=\"pageIndex\" name=\"pageIndex\" value=\"$!pageIndex\"/>\r\n\t<input type=\"hidden\" id=\"searchKey\" name=\"searchKey\" value=\"$!searchKey\"/>\r\n <div class=\"setting_box\">\r\n \r\n   #set ($nodeGroup = $form.nodeInfo.defaultInstance)\r\n   #set ($nodeParameterGroup = $form.nodeParameterInfo.defaultInstance)\r\n   \r\n\t<input type=\"hidden\" name=\"$nodeGroup.id.key\" value=\"$node.id\" />\r\n\t<input type=\"hidden\" name=\"nodeId\" value=\"$node.id\" />\r\n\t<input type=\"hidden\" name=\"channelId\" value=\"$channel.id\" />\r\n    <table cellpadding=\"0\" cellspacing=\"0\" class=\"setting_otter\">\r\n\t\t<span class=\"red\">#editNodeMessage ($nodeGroup.formNodeError)</span>\r\n      <tr> \r\n        <th width=\"300\">机器名称：</th>\r\n        <td width=\"329\">\r\n            <input type=\"text\" name=\"$nodeGroup.name.key\" value=\"$!node.name\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#editNodeMessage ($nodeGroup.name)</span>\r\n\t\t</td>\r\n      </tr>\r\n      <tr> \r\n        <th>机器IP：</th>\r\n        <td>\r\n\t\t\t<input type=\"text\" name=\"$nodeGroup.ip.key\" value=\"$!node.ip\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#editNodeMessage ($nodeGroup.ip)</span>\r\n\t\t</td>\r\n      </tr>\r\n      <tr> \r\n        <th>机器端口：</th>\r\n        <td>\r\n\t\t\t<input type=\"text\" name=\"$nodeGroup.port.key\" value=\"$!node.port\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#editNodeMessage ($nodeGroup.port)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  #**\r\n\t  <tr> \r\n        <th>ZooKeeper集群：</th>\r\n        <td>\r\n\t\t\t<textarea class=\"service\" name=\"$nodeParameterGroup.zkClusters.key\" cols=\"45\" rows=\"5\" >#foreach($storeAddress in $!node.parameters.zkClusters)$storeAddress;#end</textarea><span class=\"red\">*</span>\r\n\t\t\t  <br />\r\n\t\t\t  <span>格式如 10.20.10.20:8080;（必须以分号结束，可添多个）</span>\r\n\t\t\t  <br />\r\n\t\t\t<span class=\"red\">#editNodeMessage ($nodeParameterGroup.zkClusters)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  *#\r\n\t  <tr> \r\n        <th>下载端口：</th>\r\n        <td>\r\n\t\t\t<input type=\"text\" name=\"$nodeParameterGroup.downloadPort.key\" value=\"$!node.parameters.downloadPort\" class=\"setting_input\"/>\r\n\t\t\t<br />\r\n\t\t\t<span>可为空，不填写默认即为：机器端口 + 1</span>\r\n\t\t    <br />\r\n\t\t\t<span class=\"red\">#editNodeMessage ($nodeParameterGroup.downloadPort)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  <tr> \r\n        <th>Mbean端口：</th>\r\n        <td>\r\n\t\t\t<input type=\"text\" name=\"$nodeParameterGroup.mbeanPort.key\" value=\"$!node.parameters.mbeanPort\" class=\"setting_input\"/>\r\n\t\t\t<br />\r\n\t\t\t<span>可为空，不填写默认即为：机器端口 + 2</span>\r\n\t\t    <br />\r\n\t\t\t<span class=\"red\">#editNodeMessage ($nodeParameterGroup.mbeanPort)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  <tr> \r\n        <th>外部IP：</th>\r\n        <td>\r\n\t\t\t<input type=\"text\" name=\"$nodeParameterGroup.externalIp.key\" value=\"$!node.parameters.externalIp\" class=\"setting_input\" />\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#editNodeMessage ($nodeParameterGroup.externalIp)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  <tr> \r\n        <th>启用外部IP：</th>\r\n        <td width=\"329\">\r\n              <input type=\"radio\" name=\"$nodeParameterGroup.useExternalIp.key\" value=\"true\" #if($!node.parameters.useExternalIp) checked=\"checked\" #end id=\"RadioGroup2_0\" class=\"radio\"/>是\r\n              <input type=\"radio\" name=\"$nodeParameterGroup.useExternalIp.key\" value=\"false\" #if(!$!node.parameters.useExternalIp) checked=\"checked\" #end id=\"RadioGroup2_1\" class=\"radio\"/>否 \r\n        </td>\r\n      </tr>\r\n\t  <tr> \r\n        <th>zookeeper集群：</th>\r\n        <td>\r\n            <select id=\"zkCluster\" name=\"$nodeParameterGroup.autoKeeperClusterId.key\">\r\n\t\t\t#foreach($zkCluster in $zkClusters)\r\n            <option value=\"$zkCluster.id\" #if($!node.parameters.zkCluster.id == $zkCluster.id)selected#end>$zkCluster.clusterName</option>\r\n\t\t\t#end\r\n        </td>\r\n      </tr>\r\n      <tr>\r\n       <th>描述：</th>\r\n       <td>\r\n    \t\t<textarea cols=\"45\" rows=\"5\" name=\"$nodeGroup.description.key\">$!node.description</textarea>\r\n    \t\t<br />\r\n    \t\t<span class=\"red\">#editNodeMessage ($nodeGroup.description)</span>\r\n\t   </td>\r\n      </tr>\r\n    </table>\r\n </div>\r\n   <div class=\"btn\"><a href=\"javascript:document.editNodeForm.submit();\">保存</a></div> \r\n  \r\n  </form>\r\n</div>"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/templates/home/screen/editParameter.vm",
    "content": "$control.setTemplate(\"home:navigation.vm\")\r\n\r\n<div class=\"main\">\r\n  <div class=\"title\"> \r\n    <h2>系统配置</h2>\r\n  </div>\r\n  <div class=\"crumbs\"><a href=\"tableList.htm\">配置管理</a>&nbsp;&nbsp;</div>\r\n <div class=\"setting_box\">\r\n    <table cellpadding=\"0\" cellspacing=\"0\" class=\"setting_otter\">\r\n      <tr> \r\n        <th width=\"300\">DownLoad_Local_Dir:</th>\r\n        <td width=\"329\"><input name=\"Input2\" type=\"text\" class=\"setting_input\"/></td>\r\n      </tr>\r\n      <tr> \r\n        <th>DownLoad_Remote_Root_Inner_Uri</th>\r\n        <td><input name=\"Input32\" type=\"text\" class=\"setting_input\" value=\"1688/download\"/></td>\r\n      </tr>\r\n      <tr> \r\n        <th>Max_Process_Size</th>\r\n        <td><input name=\"Input3\" type=\"text\" class=\"setting_input\" value=\"2\"/></td>\r\n      </tr>\r\n      <tr> \r\n        <th>Img_Root_Path</th>\r\n        <td><input name=\"Input3\" type=\"text\" class=\"setting_input\" value=\"mnt/ets\"/></td>\r\n      </tr>\r\n      <tr> \r\n        <th>文件同步模式</th>\r\n        <td>\r\n            <select name=\"select\" id=\"select\">\r\n            <option value=\"1\">Tmp</option>\r\n            <option value=\"2\">Async</option>\r\n            <option value=\"3\">Sync</option>\r\n            </select>\r\n       </td>\r\n      </tr>\r\n    </table>\r\n </div>\r\n  <div class=\"btn\"><a href=\"#\">保存</a></div>\r\n</div>\r\n"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/templates/home/screen/editPipeline.vm",
    "content": "$control.setTemplate(\"home:navigation.vm\")\r\n<script language=\"javascript\">\r\n<!--\r\n\tchangeNav(\"sync\");\r\n\tfunction changeSuperConfig() {\r\n\t\tvar obj = document.getElementById('superConfig');\r\n\t\tif(obj.checked) {\r\n\t\t\tchangeDisplay('super','table-row');\r\n\t\t}else {\r\n\t\t\tchangeDisplay('super','none');\r\n\t\t}\r\n\t}\r\n\t\r\n\tfunction changeKeyword(id, name) {\r\n    \tif( document.getElementById(\"destinationName\")){\r\n        \tdocument.getElementById('destinationName').value = name;\r\n    \t}\r\n\t}\r\n//-->\r\n</script>\r\n#macro (editPipelineMessage $field)\r\n    #if (!$field.valid) $field.message #end\r\n#end\r\n<div class=\"main\">\r\n  <div class=\"title\"> \r\n    <h2>编辑Pipeline</h2>\r\n  </div>\r\n <div class=\"crumbs\"><a href=\"channelList.htm?channelId=$pipeline.channelId\">Channel管理</a>&nbsp;&nbsp;>&nbsp;&nbsp;<a href=\"pipelineList.htm?channelId=$pipeline.channelId\">Pipeline管理</a>&nbsp;&nbsp;>&nbsp;&nbsp;<a href=\"editPipeline.htm?pipelineId=$pipeline.id\">编辑Pipeline</a></div>   \r\n  <form name=\"editPipelineForm\" method=\"post\" enctype=\"multipart/form-data\">\r\n\t$csrfToken.hiddenField\r\n\t<input type=\"hidden\" name=\"action\" value=\"pipeline_action\"/>\r\n\t<input type=\"hidden\" name=\"event_submit_do_edit\" value=\"1\" />\r\n <div class=\"setting_box\">\r\n\t#set ($pipelineGroup = $form.pipelineInfo.defaultInstance)\r\n    #set ($pipelineParameterGroup = $form.pipelineParameterInfo.defaultInstance)\r\n\t\r\n\t<input type=\"hidden\" name=\"$pipelineGroup.id.key\" value=\"$pipeline.id\" />\r\n\t<input type=\"hidden\" name=\"pipelineId\" value=\"$pipeline.id\" />\r\n\t<input type=\"hidden\" name=\"$pipelineGroup.channelId.key\" value=\"$pipeline.channelId\"/>\r\n    <table cellpadding=\"0\" cellspacing=\"0\" class=\"setting_otter\">\r\n      <tr> \r\n        <th width=\"300\">Pipeline名字：</th>\r\n        <td width=\"329\">\r\n\t\t\t<input name=\"$pipelineGroup.name.key\" value=\"$pipeline.name\" type=\"text\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#editPipelineMessage ($pipelineGroup.name)#editPipelineMessage ($pipelineGroup.formPipelineError)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  \r\n      <tr> \r\n        <th width=\"300\">Select机器：</th>\r\n        <td width=\"329\" class=\"textarea\">\r\n\t\t\t<select name=\"$pipelineGroup.selectNodeIds.key\" multiple=\"multiple\" style=\"height:100px;width:200px\">\r\n                <optgroup label=\"Select\">\r\n\t\t\t\t\t#foreach ($node in $nodes)\r\n\t\t\t\t\t\t<option value=\"$node.id\" \r\n\t\t\t\t\t\t\t#foreach($selectNode in $pipeline.selectNodes)\r\n\t\t\t\t\t\t\t\t#if($node.id == $selectNode.id)\r\n\t\t\t\t\t\t\t\t\tselected \r\n\t\t\t\t\t\t\t\t#end\r\n\t\t\t\t\t\t\t#end\r\n\t\t\t\t\t\t >$node.name</option>\r\n\t\t\t\t\t#end\r\n\t\t\t\t</optgroup>\r\n            </select><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#editPipelineMessage ($pipelineGroup.selectNodeIds)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  #**\r\n\t  <tr> \r\n        <th width=\"300\">Extract机器：</th>\r\n        <td width=\"329\" class=\"textarea_b\">\r\n\t\t\t<select name=\"$pipelineGroup.extractNodeIds.key\" multiple=\"multiple\" style=\"height:100px;width:200px\">\r\n                <optgroup label=\"Extract\">\r\n    \t\t\t\t#foreach ($node in $nodes)\r\n\t\t\t\t\t\t<option value=\"$node.id\"\r\n\t\t\t\t\t\t\t#foreach($extractNode in $pipeline.extractNodes)\r\n\t\t\t\t\t\t\t\t#if($node.id == $extractNode.id)\r\n\t\t\t\t\t\t\t\t\tselected \r\n\t\t\t\t\t\t\t\t#end\r\n\t\t\t\t\t\t\t#end\r\n\t\t\t\t\t\t\t>$node.name</option>\r\n\t\t\t\t\t#end\r\n\t\t\t\t</optgroup>\r\n            </select><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#editPipelineMessage ($pipelineGroup.extractNodeIds)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  *#\r\n      <tr> \r\n        <th width=\"300\">Load机器：</th>\r\n        <td width=\"329\" class=\"textarea_b\">\r\n\t\t\t<select name=\"$pipelineGroup.loadNodeIds.key\" multiple=\"multiple\" style=\"height:100px;width:200px\">\r\n                <optgroup label=\"Load\">\r\n    \t\t\t\t#foreach ($node in $nodes)\r\n\t\t\t\t\t\t<option value=\"$node.id\"\r\n\t\t\t\t\t\t\t#foreach($loadNode in $pipeline.loadNodes)\r\n\t\t\t\t\t\t\t\t#if($node.id == $loadNode.id)\r\n\t\t\t\t\t\t\t\t\tselected \r\n\t\t\t\t\t\t\t\t#end\r\n\t\t\t\t\t\t\t#end\r\n\t\t\t\t\t\t\t>$node.name</option>\r\n\t\t\t\t\t#end\r\n\t\t\t\t</optgroup>\r\n            </select><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#editPipelineMessage ($pipelineGroup.loadNodeIds)</span>\r\n\t\t</td>\r\n      </tr>\r\n      <tr> \r\n        <th width=\"300\">并行度：</th>\r\n        <td width=\"329\">\r\n\t\t\t<input name=\"$pipelineParameterGroup.parallelism.key\" value=\"$pipeline.parameters.parallelism\" type=\"text\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#editPipelineMessage ($pipelineParameterGroup.parallelism)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  <tr> \r\n        <th width=\"300\">数据反查线程数：</th>\r\n        <td width=\"329\">\r\n\t\t\t<input name=\"$pipelineParameterGroup.extractPoolSize.key\" value=\"$pipeline.parameters.extractPoolSize\" type=\"text\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#editPipelineMessage ($pipelineParameterGroup.extractPoolSize)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  <tr> \r\n        <th width=\"300\">数据载入线程数：</th>\r\n        <td width=\"329\">\r\n\t\t\t<input name=\"$pipelineParameterGroup.loadPoolSize.key\" value=\"$pipeline.parameters.loadPoolSize\" type=\"text\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#editPipelineMessage ($pipelineParameterGroup.loadPoolSize)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t   <tr> \r\n        <th width=\"300\">文件载入线程数：</th>\r\n        <td width=\"329\">\r\n\t\t\t<input name=\"$pipelineParameterGroup.fileLoadPoolSize.key\" value=\"$pipeline.parameters.fileLoadPoolSize\" type=\"text\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#editPipelineMessage ($pipelineParameterGroup.fileLoadPoolSize)</span>\r\n\t\t</td>\r\n      </tr>\r\n      <tr> \r\n        <th width=\"300\">主站点：</th>\r\n        <td width=\"329\">\r\n\t\t\t\t<input type=\"radio\" name=\"$pipelineParameterGroup.home.key\" value=\"true\" id=\"RadioGroup1_0\" #if ($pipeline.parameters.home) checked=\"checked\" #end class=\"radio\"/>是\r\n                <input type=\"radio\" name=\"$pipelineParameterGroup.home.key\" value=\"false\" id=\"RadioGroup1_1\" #if (!$pipeline.parameters.home) checked=\"checked\" #end class=\"radio\"/>否\r\n        </td>\r\n      </tr>\r\n\t  <tr> \r\n        <th width=\"300\">同步数据来源：</th>\r\n        <td width=\"329\">\r\n\t\t\t<input type=\"radio\" name=\"$pipelineParameterGroup.selectorMode.key\" value=\"Canal\" onclick=\"changeDisplay('eromanga','none');changeDisplay('canal','table-row');\" #if ($pipeline.parameters.selectorMode.isCanal()) checked=\"checked\" #end class=\"radio\"/>Canal\r\n            <span class=\"red\">*</span>\r\n        </td>\r\n      </tr>\r\n\t  <tr> \r\n        <th width=\"300\">Canal名字：</th>\r\n        <td width=\"329\">\r\n              <input id=\"destinationName\" name=\"$pipelineParameterGroup.destinationName.key\" value=\"$pipeline.parameters.destinationName\" type=\"text\" class=\"setting_input\"/>\r\n\t\t\t  <input type=\"button\" value=\"查找Canal\" onclick=\"window.open('selectCanal.htm', 'selectCanal')\"><span class=\"red\">*</span>\r\n\t\t\t  <br />\r\n\t\t\t  <span class=\"red\">#editPipelineMessage ($pipelineParameterGroup.destinationName)</span>\r\n        </td>\r\n      </tr>\r\n\t  #**\r\n\t  <tr> \r\n        <th width=\"300\">消费端ID：</th>\r\n        <td width=\"329\">\r\n              <input name=\"$pipelineParameterGroup.mainstemClientId.key\" value=\"$pipeline.parameters.mainstemClientId\" type=\"text\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t  <br />\r\n\t\t\t  <span class=\"red\">#editPipelineMessage ($pipelineParameterGroup.mainstemClientId)</span>\r\n        </td>\r\n      </tr>\r\n\t  *#\r\n\t  <tr> \r\n        <th width=\"300\">消费批次大小：</th>\r\n        <td width=\"329\">\r\n              <input name=\"$pipelineParameterGroup.mainstemBatchsize.key\" value=\"$pipeline.parameters.mainstemBatchsize\" type=\"text\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t  <br />\r\n\t\t\t  <span class=\"red\">#editPipelineMessage ($pipelineParameterGroup.mainstemBatchsize)</span>\r\n        </td>\r\n      </tr>\r\n\t  <tr class=\"canal\"> \r\n        <th width=\"300\">获取批次数据超时时间(毫秒): </th>\r\n        <td width=\"329\">\r\n              <input name=\"$pipelineParameterGroup.batchTimeout.key\" value=\"$!pipeline.parameters.batchTimeout\" type=\"text\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t  <br />\r\n\t\t\t  <span>格式: -1不进行控制，0代表永久，>0则按照指定时间控制</span>\r\n\t\t\t  <br />\r\n\t\t\t  <span class=\"red\">#editPipelineMessage ($pipelineParameterGroup.batchTimeout)</span>\r\n        </td>\r\n      </tr>\r\n\t  <tr> \r\n        <th width=\"300\">Load批次大小：</th>\r\n        <td width=\"329\">\r\n              <input name=\"$pipelineParameterGroup.loadBatchsize.key\" value=\"$pipeline.parameters.loadBatchsize\" type=\"text\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t  <br />\r\n\t\t\t  <span class=\"red\">#editPipelineMessage ($pipelineParameterGroup.loadBatchsize)</span>\r\n        </td>\r\n      </tr>\r\n      <tr>\r\n       <th>描述：</th>\r\n       <td>\r\n\t\t\t<textarea name=\"$pipelineGroup.description.key\" cols=\"45\" rows=\"5\">$!pipeline.description</textarea><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#editPipelineMessage ($!pipelineGroup.description)</span>\r\n\t   </td>\r\n      </tr>\r\n\t  <tr>\r\n\t\t<th width=\"300\">高级设置：</th>\r\n        <td width=\"329\">\r\n              <input id=\"superConfig\" type='checkbox' name='super' value=1  class=\"setting_input\" onclick=\"changeSuperConfig()\" />\r\n\t\t\t  <br />\r\n        </td>\r\n      </tr>\r\n\t   <tr class=\"super\"> \r\n        <th width=\"300\">使用batch：</th>\r\n        <td width=\"329\">\r\n\t\t\t<input type=\"radio\" name=\"$pipelineParameterGroup.useBatch.key\" value=\"true\" id=\"RadioGroup2_0\" #if ($pipeline.parameters.useBatch) checked=\"checked\" #end class=\"radio\"/>是\r\n            <input type=\"radio\" name=\"$pipelineParameterGroup.useBatch.key\" value=\"false\" id=\"RadioGroup2_1\" #if (!$pipeline.parameters.useBatch) checked=\"checked\" #end class=\"radio\"/>否\r\n\t\t</td>\r\n      </tr>\r\n\t  <tr class=\"super\"> \r\n        <th width=\"300\">跳过Select异常：</th>\r\n        <td width=\"329\">\r\n              <input type=\"radio\" name=\"$pipelineParameterGroup.skipSelectException.key\" value=\"true\" id=\"RadioGroup2_0\" #if ($pipeline.parameters.skipSelectException) checked=\"checked\" #end class=\"radio\"/>是\r\n              <input type=\"radio\" name=\"$pipelineParameterGroup.skipSelectException.key\" value=\"false\" id=\"RadioGroup2_1\" #if (!$pipeline.parameters.skipSelectException) checked=\"checked\" #end class=\"radio\"/>否 \r\n        </td>\r\n      </tr>\r\n\t   <tr class=\"super\"> \r\n        <th width=\"300\">跳过Load异常：</th>\r\n        <td width=\"329\">\r\n              <input type=\"radio\" name=\"$pipelineParameterGroup.skipLoadException.key\" value=\"true\" id=\"RadioGroup2_0\" #if ($pipeline.parameters.skipLoadException) checked=\"checked\" #end class=\"radio\"/>是\r\n              <input type=\"radio\" name=\"$pipelineParameterGroup.skipLoadException.key\" value=\"false\" id=\"RadioGroup2_1\" #if (!$pipeline.parameters.skipLoadException) checked=\"checked\" #end class=\"radio\"/>否 \r\n        </td>\r\n      </tr>\r\n\t  <tr class=\"super\"> \r\n        <th width=\"300\">仲裁器调度模式：</th>\r\n        <td width=\"329\">\r\n            <select name=\"$pipelineParameterGroup.arbitrateMode.key\" id=\"select\">\r\n            \t<option value=\"AUTOMATIC\" #if ($pipeline.parameters.arbitrateMode.isAutomatic()) selected=\"selected\" #end>自动选择</option>\r\n                <option value=\"RPC\" #if ($pipeline.parameters.arbitrateMode.isRpc()) selected=\"selected\" #end>RPC</option>\r\n\t\t\t\t<option value=\"ZOOKEEPER\" #if ($pipeline.parameters.arbitrateMode.isZookeeper()) selected=\"selected\" #end>ZOOKEEPER</option>\r\n\t\t\t\t<option value=\"MEMORY\" #if ($pipeline.parameters.arbitrateMode.isMemory()) selected=\"selected\" #end>MEMORY</option>\r\n            </select><span class=\"red\">*</span>\r\n        </td>\r\n      </tr>\r\n       <tr class=\"super\"> \r\n        <th width=\"300\">负载均衡算法：</th>\r\n        <td width=\"329\">\r\n            <select name=\"$pipelineParameterGroup.lbAlgorithm.key\" id=\"select\">\r\n\t\t\t\t<option value=\"Stick\" #if ($pipeline.parameters.lbAlgorithm.isStick()) selected=\"selected\" #end>Stick</option>\r\n                <option value=\"RoundRbin\" #if ($pipeline.parameters.lbAlgorithm.isRoundRbin()) selected=\"selected\" #end>RoundRbin</option>\r\n            \t<option value=\"Random\" #if ($pipeline.parameters.lbAlgorithm.isRandom()) selected=\"selected\" #end>Random</option>\r\n            </select><span class=\"red\">*</span>\r\n        </td>\r\n      </tr>\r\n\t  <tr class=\"super\"> \r\n        <th width=\"300\">传输模式：</th>\r\n        <td width=\"329\">\r\n            <select name=\"$pipelineParameterGroup.pipeChooseType.key\" id=\"select\">\r\n            \t<option value=\"AUTOMATIC\" #if ($pipeline.parameters.pipeChooseType.isAutomatic()) selected=\"selected\" #end>自动选择</option>\r\n                <option value=\"RPC\" #if ($pipeline.parameters.pipeChooseType.isRpc()) selected=\"selected\" #end>RPC</option>\r\n\t\t\t\t<option value=\"HTTP\" #if ($pipeline.parameters.pipeChooseType.isHttp()) selected=\"selected\" #end>HTTP</option>\r\n            </select><span class=\"red\">*</span>\r\n        </td>\r\n      </tr>\r\n\t   <tr class=\"super\"> \r\n        <th width=\"300\">记录selector日志：</th>\r\n        <td width=\"329\">\r\n              <input type=\"radio\" name=\"$pipelineParameterGroup.dumpSelector.key\" value=\"true\" id=\"RadioGroup1_2\"  #if ($pipeline.parameters.dumpSelector) checked=\"checked\" #end class=\"radio\"/>是\r\n              <input type=\"radio\" name=\"$pipelineParameterGroup.dumpSelector.key\" value=\"false\" id=\"RadioGroup1_3\" #if (!$pipeline.parameters.dumpSelector) checked=\"checked\" #end class=\"radio\"/>否 \r\n        </td>\r\n      </tr>\r\n\t   <tr class=\"super\"> \r\n        <th width=\"300\">记录selector详细日志：</th>\r\n        <td width=\"329\">\r\n              <input type=\"radio\" name=\"$pipelineParameterGroup.dumpSelectorDetail.key\" value=\"true\" id=\"RadioGroup1_2\"  #if ($pipeline.parameters.dumpSelectorDetail) checked=\"checked\" #end class=\"radio\"/>是\r\n              <input type=\"radio\" name=\"$pipelineParameterGroup.dumpSelectorDetail.key\" value=\"false\" id=\"RadioGroup1_3\" #if (!$pipeline.parameters.dumpSelectorDetail) checked=\"checked\" #end class=\"radio\"/>否 \r\n        </td>\r\n      </tr>\r\n\t   <tr class=\"super\"> \r\n        <th width=\"300\">记录load日志：</th>\r\n        <td width=\"329\">\r\n              <input type=\"radio\" name=\"$pipelineParameterGroup.dumpEvent.key\" value=\"true\" id=\"RadioGroup1_2\"  #if ($pipeline.parameters.dumpEvent) checked=\"checked\" #end class=\"radio\"/>是\r\n              <input type=\"radio\" name=\"$pipelineParameterGroup.dumpEvent.key\" value=\"false\" id=\"RadioGroup1_3\" #if (!$pipeline.parameters.dumpEvent) checked=\"checked\" #end class=\"radio\"/>否 \r\n        </td>\r\n      </tr>\r\n\t   <tr class=\"super\"> \r\n        <th width=\"300\">dryRun模式：</th>\r\n        <td width=\"329\">\r\n              <input type=\"radio\" name=\"$pipelineParameterGroup.dryRun.key\" value=\"true\" id=\"RadioGroup1_2\"  #if ($pipeline.parameters.dryRun) checked=\"checked\" #end class=\"radio\"/>是\r\n              <input type=\"radio\" name=\"$pipelineParameterGroup.dryRun.key\" value=\"false\" id=\"RadioGroup1_3\" #if (!$pipeline.parameters.dryRun) checked=\"checked\" #end class=\"radio\"/>否 \r\n        </td>\r\n      </tr>\r\n\t  <tr class=\"super\"> \r\n        <th width=\"300\">支持ddl同步：</th>\r\n        <td width=\"329\">\r\n              <input type=\"radio\" name=\"$pipelineParameterGroup.ddlSync.key\" value=\"true\" id=\"RadioGroup1_2\" #if ($pipeline.parameters.ddlSync) checked=\"checked\" #end class=\"radio\"/>是\r\n              <input type=\"radio\" name=\"$pipelineParameterGroup.ddlSync.key\" value=\"false\" id=\"RadioGroup1_3\" #if (!$pipeline.parameters.ddlSync) checked=\"checked\" #end class=\"radio\"/>否 \r\n        </td>\r\n      </tr>\r\n\t  <tr class=\"super\"> \r\n        <th width=\"300\">是否跳过ddl异常：</th>\r\n        <td width=\"329\">\r\n              <input type=\"radio\" name=\"$pipelineParameterGroup.skipDdlException.key\" value=\"true\" id=\"RadioGroup2_0\" #if ($pipeline.parameters.skipDdlException) checked=\"checked\" #end class=\"radio\"/>是\r\n              <input type=\"radio\" name=\"$pipelineParameterGroup.skipDdlException.key\" value=\"false\" id=\"RadioGroup2_1\" #if (!$pipeline.parameters.skipDdlException) checked=\"checked\" #end class=\"radio\"/>否 \r\n        </td>\r\n      </tr>\r\n\t  <tr class=\"super\"> \r\n        <th width=\"300\">文件重复同步对比：</th>\r\n        <td width=\"329\">\r\n              <input type=\"radio\" name=\"$pipelineParameterGroup.fileDetect.key\" value=\"true\" id=\"RadioGroup1_2\" #if ($pipeline.parameters.fileDetect) checked=\"checked\" #end class=\"radio\"/>是\r\n              <input type=\"radio\" name=\"$pipelineParameterGroup.fileDetect.key\" value=\"false\" id=\"RadioGroup1_3\" #if (!$pipeline.parameters.fileDetect) checked=\"checked\" #end class=\"radio\"/>否 \r\n        </td>\r\n      </tr>\r\n\t  <tr class=\"super\"> \r\n        <th width=\"300\">文件传输加密：</th>\r\n        <td width=\"329\">\r\n              <input type=\"radio\" name=\"$pipelineParameterGroup.useFileEncrypt.key\" value=\"true\" id=\"RadioGroup1_2\" #if ($pipeline.parameters.useFileEncrypt) checked=\"checked\" #end class=\"radio\"/>是\r\n              <input type=\"radio\" name=\"$pipelineParameterGroup.useFileEncrypt.key\" value=\"false\" id=\"RadioGroup1_3\" #if (!$pipeline.parameters.useFileEncrypt) checked=\"checked\" #end class=\"radio\"/>否 \r\n        </td>\r\n      </tr>\r\n\t  <tr class=\"super\"> \r\n        <th width=\"300\">启用公网同步：</th>\r\n        <td width=\"329\">\r\n              <input type=\"radio\" name=\"$pipelineParameterGroup.useExternalIp.key\" value=\"true\" id=\"RadioGroup1_2\" #if ($pipeline.parameters.useExternalIp) checked=\"checked\" #end class=\"radio\"/>是\r\n              <input type=\"radio\" name=\"$pipelineParameterGroup.useExternalIp.key\" value=\"false\" id=\"RadioGroup1_3\" #if (!$pipeline.parameters.useExternalIp) checked=\"checked\" #end class=\"radio\"/>否 \r\n        </td>\r\n      </tr>\r\n\t  <tr class=\"super\"> \r\n        <th width=\"300\">跳过自由门数据：</th>\r\n        <td width=\"329\">\r\n              <input type=\"radio\" name=\"$pipelineParameterGroup.skipFreedom.key\" value=\"true\" id=\"RadioGroup1_2\" #if ($pipeline.parameters.skipFreedom) checked=\"checked\" #end class=\"radio\"/>是\r\n              <input type=\"radio\" name=\"$pipelineParameterGroup.skipFreedom.key\" value=\"false\" id=\"RadioGroup1_3\" #if (!$pipeline.parameters.skipFreedom) checked=\"checked\" #end class=\"radio\"/>否 \r\n        </td>\r\n      </tr>\r\n\t  <tr class=\"super\"> \r\n        <th width=\"300\">跳过反查无记录数据：</th>\r\n        <td width=\"329\">\r\n              <input type=\"radio\" name=\"$pipelineParameterGroup.skipNoRow.key\" value=\"true\" id=\"RadioGroup1_2\" #if ($pipeline.parameters.skipNoRow) checked=\"checked\" #end class=\"radio\"/>是\r\n              <input type=\"radio\" name=\"$pipelineParameterGroup.skipNoRow.key\" value=\"false\" id=\"RadioGroup1_3\" #if (!$pipeline.parameters.skipNoRow) checked=\"checked\" #end class=\"radio\"/>否 \r\n        </td>\r\n      </tr>\r\n\t  <tr class=\"super\"> \r\n        <th width=\"300\">启用数据表类型转化：</th>\r\n        <td width=\"329\">\r\n              <input type=\"radio\" name=\"$pipelineParameterGroup.useTableTransform.key\" value=\"true\" id=\"RadioGroup1_2\" #if ($pipeline.parameters.useTableTransform) checked=\"checked\" #end class=\"radio\"/>是\r\n              <input type=\"radio\" name=\"$pipelineParameterGroup.useTableTransform.key\" value=\"false\" id=\"RadioGroup1_3\" #if (!$pipeline.parameters.useTableTransform) checked=\"checked\" #end class=\"radio\"/>否 \r\n        </td>\r\n      </tr>\r\n\t  <tr class=\"super\"> \r\n        <th width=\"300\">兼容字段新增同步：</th>\r\n        <td width=\"329\">\r\n              <input type=\"radio\" name=\"$pipelineParameterGroup.enableCompatibleMissColumn.key\" value=\"true\" id=\"RadioGroup1_2\" #if ($pipeline.parameters.enableCompatibleMissColumn) checked=\"checked\" #end class=\"radio\"/>是\r\n              <input type=\"radio\" name=\"$pipelineParameterGroup.enableCompatibleMissColumn.key\" value=\"false\" id=\"RadioGroup1_3\" #if (!$pipeline.parameters.enableCompatibleMissColumn) checked=\"checked\" #end class=\"radio\"/>否 \r\n        </td>\r\n      </tr>\r\n\t  <tr class=\"super\"> \r\n        <th width=\"300\">自定义同步标记：</th>\r\n        <td width=\"329\">\r\n              <input name=\"$pipelineParameterGroup.channelInfo.key\" value=\"$!pipeline.parameters.channelInfo\" type=\"text\" class=\"setting_input\"/>\r\n\t\t\t  <br />\r\n\t\t\t  <span class=\"red\">#editPipelineMessage ($pipelineParameterGroup.channelInfo)</span>\r\n        </td>\r\n    </table>\r\n </div>\r\n  <div class=\"btn\"><a href=\"javascript:document.editPipelineForm.submit();\">保存</a></div>\r\n  </form>\r\n</div>\r\n\r\n<script language=\"javascript\">\r\n<!--\r\n\t#if($pipeline.parameters.selectorMode.isCanal())\r\n\t\tchangeDisplay('canal','table-row')\r\n\t\tchangeDisplay('eromanga','none')\r\n\t#end\r\n\t#if($pipeline.parameters.selectorMode.isEromanga())\r\n\t\tchangeDisplay('eromanga','table-row')\r\n\t\tchangeDisplay('canal','none')\r\n\t#end\r\n\tchangeSuperConfig();\r\n//-->\r\n</script>\r\n"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/templates/home/screen/editUser.vm",
    "content": "$control.setTemplate(\"home:navigation.vm\")\r\n<script language=\"javascript\">\r\n<!--\r\n\tchangeNav(\"user\");\r\n//-->\r\n</script>\r\n#macro (editUserMessage $field)\r\n    #if (!$field.valid) $field.message #end\r\n#end\t\r\n<div class=\"main\">\r\n  <div class=\"title\"> \r\n    <h2>编辑用户信息</h2>\r\n  </div>\r\n <div class=\"crumbs\"><a href=\"userManager.htm\">权限管理</a>&nbsp;&nbsp;>&nbsp;&nbsp;<a href=\"editUser.htm?userId=$user.id\">编辑用户信息</a></div>\r\n <form name=\"editUserForm\" method=\"post\" enctype=\"multipart/form-data\">\r\n\t$csrfToken.hiddenField\r\n\t<input type=\"hidden\" name=\"action\" value=\"user_action\"/>\r\n\t<input type=\"hidden\" name=\"event_submit_do_edit\" value=\"1\" />\r\n\t<input type=\"hidden\" id=\"pageIndex\" name=\"pageIndex\" value=\"$!pageIndex\"/>\r\n\t<input type=\"hidden\" id=\"searchKey\" name=\"searchKey\" value=\"$!searchKey\"/>\r\n <div class=\"setting_box\">\r\n\t\r\n\t#set ($userGroup = $form.editUserInfo.defaultInstance)\r\n\t<input type=\"hidden\" name=\"$userGroup.id.key\" value=\"$user.id\" />\r\n\t<input type=\"hidden\" name=\"userId\" value=\"$user.id\" />\r\n    <table cellpadding=\"0\" cellspacing=\"0\" class=\"setting_otter\">\r\n      <tr> \r\n        <th width=\"300\">用户名：</th>\r\n        <td width=\"329\">\r\n\t\t\t<input value=\"$user.name\" type=\"text\" class=\"setting_input\" disabled />\r\n\t\t</td>\r\n      </tr>\r\n      <tr> \r\n        <th>密码：</th>\r\n        <td>\r\n\t\t\t<input name=\"$userGroup.password.key\" type=\"password\" class=\"setting_input\"/><span>如不修改，请留空</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  <tr> \r\n        <th>重复密码：</th>\r\n        <td>\r\n\t\t\t<input name=\"$userGroup.rePassword.key\" type=\"password\" class=\"setting_input\"/>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#editUserMessage ($userGroup.rePassword)</span>\r\n\t\t</td>\r\n      </tr>\r\n      <tr> \r\n        <th>部门：</th>\r\n        <td>\r\n\t\t\t<input name=\"$userGroup.department.key\" value=\"$user.department\"  type=\"text\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#editUserMessage ($userGroup.department)</span>\r\n\t\t</td>\r\n      </tr>\r\n      <tr> \r\n        <th>真实姓名：</th>\r\n        <td>\r\n\t\t\t<input name=\"$userGroup.realName.key\" value=\"$user.realName\"  type=\"text\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#editUserMessage ($userGroup.realName)</span>\r\n\t\t</td>\r\n      </tr>\r\n      <tr> \r\n        <th>权限选择：</th>\r\n        <td><input name=\"$userGroup.authorizeType.key\" type=\"radio\" value=\"ADMIN\" class=\"radio\" #if ($user.authorizeType.isAdmin()) checked=\"checked\" #end />\r\n          超级管理员 &nbsp;&nbsp;&nbsp;&nbsp;\r\n            <input name=\"$userGroup.authorizeType.key\" type=\"radio\" value=\"OPERATOR\" class=\"radio\" #if ($user.authorizeType.isOperator()) checked=\"checked\" #end />\r\n          普通用户</td>\r\n      </tr>\r\n\t  <tr>\r\n\t\t<span class=\"red\">#editUserMessage ($userGroup.formUserError)</span>\r\n      </tr>\r\n    </table>\r\n </div>\r\n </form>\r\n  <div class=\"btn\"><a href=\"javascript:document.editUserForm.submit();\">保存</a></div>\r\n</div>\r\n"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/templates/home/screen/error.vm",
    "content": "$control.setTemplate(\"home:navigation.vm\")\n\n<div class=\"main\">\n<div class=\"title\">\n对不起，你请求的链接出问题了，可能是超时，没事多刷新一次吧！\n</div>\n</div>"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/templates/home/screen/forbidden.vm",
    "content": "$control.setTemplate(\"home:navigation.vm\")\r\n\r\n<div class=\"main\">\r\n<div class=\"error_forbidden\"></div>\r\n</div>\r\n"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/templates/home/screen/index.vm",
    "content": "$rundata.setLayoutEnabled(false)\r\n#macro (loginMessage $field)\r\n    #if (!$field.valid) $field.message #end\r\n#end\r\n<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\r\n<html xmlns=\"http://www.w3.org/1999/xhtml\">\r\n<head>\r\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\r\n<meta http-equiv=\"Refresh\" content=\"1;URL=channelList.htm\" /> \r\n<title>Otter Manager</title>\r\n\r\n</head>\r\n<body >\n</body>\r\n</html>\r\n"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/templates/home/screen/initSql.vm",
    "content": "$control.setTemplate(\"home:navigation.vm\")\r\n#set($user = $rundata.request.session.getAttribute(\"managerUser\"))\r\n<script type=\"text/javascript\" src=\"js/trcolor.js\"></script>\r\n<script type=\"text/javascript\" src=\"js/jquery-1.4.2.min.js\"></script> \r\n<script type=\"text/javascript\" src=\"js/jquery.simplemodal-1.4.js\"></script> \r\n<script language=\"javascript\">\r\n\tchangeNav(\"manual\");\r\n</script>\r\n\r\n<!--页面主体-->\r\n<div class=\"main\">\r\n   \r\n  <div class=\"title\"> \r\n    <h2>数据库初始化 SQL</h2>\r\n  </div>\r\n   <div class=\"crumbs\">MYSQL</div>\r\n   <br/><br/><br/>\r\n   <h3>说明：该脚本为双A机房数据库同步的初始化SQL，如无该需求请忽略之，注意修改密码</h3>\r\n   \t<pre>\r\n/*\r\n供 otter 使用， otter 需要对 retl.* 的读写权限，以及对业务表的读写权限\r\n1. 创建database retl\r\n*/\r\nCREATE DATABASE retl;\r\n\r\n/* 2. 用户授权 给同步用户授权 */\r\nCREATE USER retl@'%' IDENTIFIED BY 'retl';\r\nGRANT USAGE ON *.* TO `retl`@'%';\r\nGRANT SELECT, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO `retl`@'%';\r\nGRANT SELECT, INSERT, UPDATE, DELETE, EXECUTE ON `retl`.* TO `retl`@'%';\r\n/* 业务表授权，这里可以限定只授权同步业务的表 */\r\nGRANT SELECT, INSERT, UPDATE, DELETE ON *.* TO `retl`@'%';  \r\n\r\n/* 3. 创建系统表 */\r\nUSE retl;\r\nDROP TABLE IF EXISTS retl.retl_buffer;\r\nDROP TABLE IF EXISTS retl.retl_mark;\r\nDROP TABLE IF EXISTS retl.xdual;\r\n\r\nCREATE TABLE retl_buffer\r\n(\t\r\n\tID BIGINT(20) AUTO_INCREMENT,\r\n\tTABLE_ID INT(11) NOT NULL,\r\n\tFULL_NAME varchar(512),\r\n\tTYPE CHAR(1) NOT NULL,\r\n\tPK_DATA VARCHAR(256) NOT NULL,\r\n\tGMT_CREATE TIMESTAMP NOT NULL,\r\n\tGMT_MODIFIED TIMESTAMP NOT NULL,\r\n\tCONSTRAINT RETL_BUFFER_ID PRIMARY KEY (ID) \r\n)  ENGINE=InnoDB DEFAULT CHARSET=utf8;\r\n\r\nCREATE TABLE retl_mark\r\n(\t\r\n\tID BIGINT AUTO_INCREMENT,\r\n\tCHANNEL_ID INT(11),\r\n\tCHANNEL_INFO varchar(128),\r\n\tCONSTRAINT RETL_MARK_ID PRIMARY KEY (ID) \r\n) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;\r\n\r\nCREATE TABLE xdual (\r\n  ID BIGINT(20) NOT NULL AUTO_INCREMENT,\r\n  X timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\r\n  PRIMARY KEY (ID)\r\n) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;\r\n\r\n/* 4. 插入初始化数据 */\r\nINSERT INTO retl.xdual(id, x) VALUES (1,now()) ON DUPLICATE KEY UPDATE x = now();\r\n   \t</pre>\r\n   </div>     \r\n</div>\r\n"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/templates/home/screen/logRecordList.vm",
    "content": "$control.setTemplate(\"home:navigation.vm\")\r\n#set($user = $rundata.request.session.getAttribute(\"managerUser\"))\r\n<script type=\"text/javascript\" src=\"js/trcolor.js\"></script>\r\n<script type=\"text/javascript\" src=\"js/jquery-1.4.2.min.js\"></script> \r\n<script type=\"text/javascript\" src=\"js/jquery.simplemodal-1.4.js\"></script> \r\n<script language=\"javascript\">\r\n\tchangeNav(\"record\");\r\n\t\r\n\tfunction NeatDialog(sHTML, sTitle, bCancel)\r\n    {\r\n      window.neatDialog = null;\r\n      this.elt = null;\r\n      if (document.createElement  &&  document.getElementById)\r\n      {\r\n        var dg = document.createElement(\"div\");\r\n        dg.className = \"neat-dialog\";\r\n        if (sTitle)\r\n          sHTML = '<div class=\"neat-dialog-title\">'+sTitle+\r\n                  ((bCancel)?\r\n                    '<img src=\"x.gif\" alt=\"Cancel\" class=\"nd-cancel\" />':'')+\r\n                    '</div>\\n' + sHTML;\r\n        dg.innerHTML = sHTML;\r\n        var dbg = document.createElement(\"div\");\r\n        dbg.id = \"nd-bdg\";\r\n        dbg.className = \"neat-dialog-bg\";\r\n        var dgc = document.createElement(\"div\");\r\n        dgc.className = \"neat-dialog-cont\";\r\n        dgc.appendChild(dbg);\r\n        dgc.appendChild(dg);\r\n        if (document.body.offsetLeft > 0)\r\n        dgc.style.marginLeft = document.body.offsetLeft + \"px\";\r\n        document.body.appendChild(dgc);\r\n        if (bCancel) document.getElementById(\"nd-cancel\").onclick = function()\r\n        {\r\n          window.neatDialog.close();\r\n        };\r\n        this.elt = dgc;\r\n        window.neatDialog = this;\r\n      }\r\n    }\r\n    NeatDialog.prototype.close = function()\r\n    {\r\n      if (this.elt)\r\n      {\r\n        this.elt.style.display = \"none\";\r\n        this.elt.parentNode.removeChild(this.elt);\r\n      }\r\n      window.neatDialog = null;\r\n    }\r\n\r\n\tfunction openDialog( content )\r\n  \t{\r\n   \t\tvar sHTML = '<p><button onclick=\"window.neatDialog.close()\">关闭</button></p>' + content + '<p><button onclick=\"window.neatDialog.close()\">关闭</button></p>';\r\n        new NeatDialog(sHTML, \"<b>详情</b>\", false);\r\n      \r\n    }\r\n\r\n</script>\r\n<style type=\"text/css\">\r\n<!--\r\n\r\na { text-decoration: none; }\r\n\r\n.showtext { cursor: hand; cursor:pointer;}\r\n.contentid { margin-top: 10px; margin-bottom: 10px; width: 400px; border: 1px solid #CCC; background: #F1F1F1; padding: 15px; display: none; }\r\n-->\r\n</style>\r\n\r\n<!--页面主体-->\r\n<div class=\"main\">\r\n  <div class=\"title\"> \r\n    <h2>日志管理</h2>\r\n  </div>\r\n   \r\n   <!--Node搜索-->\r\n   <div class=\"search_o\"> \r\n\t\t<form name=\"search_node\" action=\"logRecordList.htm\"  method=\"post\">\r\n\t\t\t##$csrfToken.hiddenField\r\n\t\t\t<div class=\"search_input\">\r\n\t\t\t\t<input name=\"searchKey\" type=\"text\" value=\"请输入关键字(目前支持CID,PID搜索)\"  onfocus=\"if(this.value == '请输入关键字(目前支持Node的ID、名字搜索)') {this.value='';}\" onblur=\"if(this.value == '') {this.value = '请输入关键字(目前支持Node的ID、名字搜索)';}\" />\r\n\t\t\t</div>\r\n\t\t\t<div class=\"search_btn\"><a href=\"javascript:document.search_node.submit();\"><img src=\"images/search_btn.png\" width=\"39\" height=\"31\" /></a></div>\r\n        </form>\r\n   </div>\r\n      \r\n  <!--分页表单-->\r\n   <form id=\"pageform\" name=\"pageform\" action=\"$homeModule.setTarget('logRecordList.vm')\" method=\"post\">\r\n    \t<input type=\"hidden\" id=\"pageIndex\" name=\"pageIndex\" value=\"\"/>\r\n\t\t<input type=\"hidden\" id=\"searchKey\" name=\"searchKey\" value=\"$!searchKey\"/>\r\n\t\t<input type=\"hidden\" id=\"pipelineId\" name=\"pipelineId\" value=\"$!pipelineId\"/>\r\n\t\t<input type=\"hidden\" id=\"monitorName\" name=\"monitorName\" value=\"$!monitorName\"/>\r\n   </form>\r\n  \r\n<!--列表-->\r\n \r\n        <table border=\"0\" cellspacing=\"0\" cellpadding=\"0\" class=\"list changecolor_g\" id=\"logrecord-table\">\r\n          <tr> \r\n            <th width=\"5%\">序号</th>\r\n            <th width=\"10%\">Channel信息</th>\r\n\t\t\t<th width=\"10%\">Pipeline信息</th>\r\n\t\t\t<th width=\"7%\">Node信息</th>\r\n\t\t\t<th width=\"10%\">日志标题</th>\r\n\t\t\t<th width=\"40%\">日志内容</th>\r\n\t\t\t<th width=\"20%\">发生时间</th>            \r\n          </tr>\r\n\t\t  #foreach ($logRecord in $logRecords)\r\n\t\t   <tr> \r\n             <td width=\"5%\">$logRecord.id</td>\r\n\t\t\t <td width=\"10%\">\r\n\t\t\t\t#if($logRecord.channel.id > 0)\r\n\t\t\t\t\t#set ($channelURL = $homeModule.setTarget(\"channelList.vm\").addQueryData(\"channelId\", $logRecord.channel.id))\r\n\t\t\t\t\t<a href=\"$channelURL\">$logRecord.channel.name</a>\r\n\t\t\t\t#elseif($logRecord.channel.id == 0)\r\n\t\t\t\t\t<a href=\"#\">UnKnow</a>\r\n\t\t\t\t#else\t\r\n\t\t\t\t\t<a href=\"#\">manager</a>\r\n\t\t\t\t#end\r\n\t\t\t </td>\r\n\t\t\t <td width=\"10%\">\r\n\t\t\t\t#if($logRecord.pipeline.id > 0)\r\n\t\t\t\t\t#set ($pipelineURL = $homeModule.setTarget(\"pipelineList.vm\").addQueryData(\"channelId\", $logRecord.channel.id).addQueryData(\"pipelineId\", $logRecord.pipeline.id))\r\n\t\t\t\t\t<a href=\"$pipelineURL\">$logRecord.pipeline.name</a>\r\n\t\t\t\t#elseif($logRecord.pipeline.id == 0)\r\n\t\t\t\t\t<a href=\"#\">UnKnow</a>\r\n\t\t\t\t#else\r\n\t\t\t\t\t<a href=\"#\">manager</a>\r\n\t\t\t\t#end\r\n\t\t\t </td>\r\n\t\t\t <td width=\"7%\">\r\n\t\t\t\t#if($logRecord.nid && $logRecord.nid > 0)\r\n\t\t\t\t\t#set ($nodeInfoURL = $homeModule.setTarget(\"nodeInfo.vm\").addQueryData(\"nodeId\", $logRecord.nid))\r\n\t\t\t\t\t<a href=\"$nodeInfoURL\">$logRecord.nid</a>\r\n\t\t\t\t#else \r\n\t\t\t\t\t<a href=\"#\">manager</a>\r\n\t\t\t\t#end\r\n\t\t\t </td>\r\n\t\t\t <td width=\"10%\">$logRecord.title</td> \r\n\t\t\t <td width=\"40%\" class=\"message\">\r\n\t\t\t\t<a onclick=\"openDialog('$numberFormat.getHtmlOriginalContent($!logRecord.message)')\">点击查看详细信息</a>\r\n\t\t\t </td> \r\n\t\t\t  <td width=\"20%\">$!numberFormat.format($logRecord.gmtCreate)</td>\r\n\t\t   </tr>\r\n\t\t  #end\r\n        </table>\t\r\n<!--分页-->\r\n     <div class=\"page\">共$paginator.items条数据&nbsp;&nbsp;第$paginator.page页/共$paginator.pages页&nbsp;&nbsp; \r\n       \r\n\t   #if($paginator.page == 1)\r\n            <font color=\"999999\">首页</font>\r\n\t   #else\r\n\t\t\t<a href=\"#\" class=\"prev\" onclick=\"pageNavigation(this,1)\">首页</a>\r\n\t   #end\r\n\t   \r\n\t   #if($paginator.page > 1)\r\n\t\t\t#set($pre_page = $paginator.page - 1)\r\n\t\t\t\t<a href=\"#\" class=\"prev\" onclick=\"pageNavigation(this,$pre_page)\">上一页</a>\r\n\t   #else\r\n            <font color=\"999999\">上一页</font>\r\n\t   #end\r\n\t   ##分页下标\r\n\t   #set($counts_keys = $paginator.getSlider(7))\r\n\t   #foreach( $thisPage in $counts_keys)\r\n\t\t\t#if( $thisPage == $paginator.page)\r\n                <b>$thisPage</b>\r\n\t\t\t#else\r\n\t\t\t\t#if($thisPage != 0)\r\n\t\t\t\t\t<a href=\"#\" class=\"num\" onclick=\"pageNavigation(this,$thisPage)\">$thisPage</a> \r\n\t\t\t\t#end\r\n\t\t\t#end\r\n\t   #end\r\n\t   \r\n\t   #if($paginator.page < $paginator.pages)\r\n\t\t\t#set($next_page = $paginator.page + 1)\r\n\t\t\t\t<a href=\"#\" class=\"prev\" onclick=\"pageNavigation(this,$next_page)\">下一页</a>\r\n\t   #else\r\n            <font color=\"999999\">下一页</font>\r\n\t   #end\r\n\t   \r\n\t   #if($paginator.page == $paginator.pages)\r\n            <font color=\"999999\">末页</font>\r\n\t   #else\r\n\t\t\t<a href=\"#\" class=\"prev\" onclick=\"pageNavigation(this,$paginator.pages)\">末页</a>\r\n\t   #end\r\n     </div>    \r\n\t \r\n</div>\r\n"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/templates/home/screen/logRecordTab.vm",
    "content": "<script type=\"text/javascript\" src=\"js/trcolor.js\"></script>\r\n<script type=\"text/javascript\" src=\"js/jquery-1.4.2.min.js\"></script> \r\n\r\n$control.setTemplate(\"home:navigation.vm\")\r\n#set($user = $rundata.request.session.getAttribute(\"managerUser\"))\r\n<script language=\"javascript\">\r\n<!--\r\n\tchangeNav(\"sync\");\r\n\t\r\n\tfunction setTab(name,cursel,n){\r\n \t\tfor(i=1;i<=n;i++){\r\n  \t\t\tvar menu=document.getElementById(name+i);\r\n  \t\t\tvar con=document.getElementById(\"con_\"+name+\"_\"+i);\r\n  \t\t\tmenu.className=i==cursel?\"tab_active\":\"\";\r\n  \t\t\tcon.style.display=i==cursel?\"block\":\"none\";\r\n \t\t}\r\n\t}\t\r\n\t\r\n\tfunction NeatDialog(sHTML, sTitle, bCancel)\r\n    {\r\n      window.neatDialog = null;\r\n      this.elt = null;\r\n      if (document.createElement  &&  document.getElementById)\r\n      {\r\n        var dg = document.createElement(\"div\");\r\n        dg.className = \"neat-dialog\";\r\n        if (sTitle)\r\n          sHTML = '<div class=\"neat-dialog-title\">'+sTitle+\r\n                  ((bCancel)?\r\n                    '<img src=\"x.gif\" alt=\"Cancel\" class=\"nd-cancel\" />':'')+\r\n                    '</div>\\n' + sHTML;\r\n        dg.innerHTML = sHTML;\r\n        var dbg = document.createElement(\"div\");\r\n        dbg.id = \"nd-bdg\";\r\n        dbg.className = \"neat-dialog-bg\";\r\n        var dgc = document.createElement(\"div\");\r\n        dgc.className = \"neat-dialog-cont\";\r\n        dgc.appendChild(dbg);\r\n        dgc.appendChild(dg);\r\n        if (document.body.offsetLeft > 0)\r\n        \tdgc.style.marginLeft = document.body.offsetLeft + \"px\";\r\n        document.body.appendChild(dgc);\r\n        if (bCancel) document.getElementById(\"nd-cancel\").onclick = function()\r\n        {\r\n          window.neatDialog.close();\r\n        };\r\n        this.elt = dgc;\r\n        window.neatDialog = this;\r\n      }\r\n    }\r\n    NeatDialog.prototype.close = function()\r\n    {\r\n      if (this.elt)\r\n      {\r\n        this.elt.style.display = \"none\";\r\n        this.elt.parentNode.removeChild(this.elt);\r\n      }\r\n      window.neatDialog = null;\r\n    }\r\n\r\n\tfunction openDialog( content )\r\n  \t{\r\n   \t\tvar sHTML = '<p><button onclick=\"window.neatDialog.close()\">关闭</button></p>' + content + '<p><button onclick=\"window.neatDialog.close()\">关闭</button></p>';\r\n        new NeatDialog(sHTML, \"<b>详情</b>\", false);\r\n      \r\n    }\r\n\r\n</script>\r\n<style type=\"text/css\">\r\n<!--\r\na { text-decoration: none; }\r\n\r\n.showtext { cursor: hand; cursor:pointer;}\r\n.contentid { margin-top: 10px; margin-bottom: 10px; width: 400px; border: 1px solid #CCC; background: #F1F1F1; padding: 15px; display: none; }\r\n-->\r\n</style>\r\n<div class=\"main\">\r\n  <div class=\"title\"> \r\n    <h2>日志记录</h2>\r\n  </div>   \r\n  <div class=\"crumbs\"><a href=\"channelList.htm?channelId=$channel.id\">Channel管理</a>&nbsp;&nbsp;>&nbsp;&nbsp;<a href=\"pipelineList.htm?channelId=$channel.id\">Pipeline管理</a>&nbsp;&nbsp;>&nbsp;&nbsp;<a href=\"logRecordTab.htm?pipelineId=$pipelineId\">日志记录</a></div>\r\n  <div class=\"crumbs\"></div> \r\n  <div class=\"tab\" id=\"Tab2\">\r\n        <div class=\"menubox\">\r\n        <ul>\r\n\t\t\r\n        <li id=\"two1\"><a href=\"dataMediaPairList.htm?pipelineId=$pipelineId\">映射关系列表</a></li>\r\n           \r\n        <li id=\"two2\"><a href=\"analysisThroughputStat.htm?pipelineId=$pipelineId\">吞吐量</a></li>\r\n        \r\n        <li id=\"two3\"><a href=\"analysisDelayStat.htm?pipelineId=$pipelineId\">延迟时间</a></li>\r\n           \r\n        <li id=\"two4\"><a href=\"analysisStageStat.htm?pipelineId=$pipelineId\">同步进度</a></li>  \r\n\t\t\r\n\t\t<li id=\"two5\"><a href=\"analysisThroughputHistory.htm?pipelineId=$pipelineId\">历史吞吐量</a></li>\r\n\t\t\r\n\t\t<li id=\"two6\"><a href=\"alarmRuleList.htm?pipelineId=$pipelineId\">监控管理</a></li>\r\n        \r\n\t\t<li id=\"two7\" class=\"tab_active\"><a href=\"logRecordTab.htm?pipelineId=$pipelineId\">日志记录</a></li>\r\n    \r\n        </ul>\r\n        </div>\r\n        <div class=\"contentbox_tab box_tab\">  \r\n           <div id=\"con_two_1\">\r\n        \r\n\t\t\t\r\n\t  <!--分页表单-->\r\n\t\t<form id=\"pageform\" name=\"pageform\" action=\"$homeModule.setTarget('logRecordTab.vm').addQueryData(\"pipelineId\", $pipelineId)\" method=\"post\">\r\n\t\t\t<input type=\"hidden\" id=\"pageIndex\" name=\"pageIndex\" value=\"\"/>\r\n\t\t\t<input type=\"hidden\" id=\"pipelineId\" name=\"pipelineId\" value=\"$!pipelineId\"/>\r\n\t\t\t<input type=\"hidden\" id=\"searchKey\" name=\"searchKey\" value=\"$!searchKey\"/>\r\n\t\t</form>\r\n\t\t\r\n\t  <!--日志搜索-->\r\n\t    <div class=\"search_o\"> \r\n\t\t\t<form name=\"search_log\" action=\"logRecordTab.htm\"  method=\"post\">\r\n\t\t\t\t##$csrfToken.hiddenField\r\n\t\t\t\t<div class=\"search_input\">\r\n\t\t\t\t\t<input name=\"searchKey\" type=\"text\" value=\"请输入关键字(目前支持log内容关键字搜索)\"  onfocus=\"if(this.value == '请输入关键字(目前支持log内容关键字搜索)') {this.value='';}\" onblur=\"if(this.value == '') {this.value = '请输入关键字(目前支持log内容关键字搜索)';}\" />\r\n\t\t\t\t\t<input type=\"hidden\" id=\"pipelineId\" name=\"pipelineId\" value=\"$!pipelineId\"/>\r\n\t\t\t\t</div>\r\n\t\t\t\t<div class=\"search_btn\"><a href=\"javascript:document.search_log.submit();\"><img src=\"images/search_btn.png\" width=\"39\" height=\"31\" /></a></div>\r\n\t\t\t</form>\r\n\t    </div>\r\n       \r\n        <table border=\"0\" cellspacing=\"0\" cellpadding=\"0\" class=\"list2 changecolor_g\">\r\n          <tr> \r\n            <th width=\"5%\">序号</th>\r\n            <th width=\"10%\">Channel信息</th>\r\n            <th width=\"10%\">Pipline信息</th>\r\n\t\t\t<th width=\"7%\">Node信息</th>\r\n            <th width=\"10%\">日志标题</th>\r\n\t\t\t<th width=\"40%\">日志内容</th>\r\n\t\t\t<th width=\"20%\">发生时间</th>\r\n            \r\n          </tr>\r\n\t\t  #foreach ($logRecord in $logRecords)\r\n\t\t   <tr> \r\n             <td width=\"5%\">$logRecord.id</td>\r\n\t\t\t <td width=\"10%\">\r\n\t\t\t\t#if($logRecord.channel.id > 0)\r\n\t\t\t\t\t#set ($channelURL = $homeModule.setTarget(\"channelList.vm\").addQueryData(\"channelId\", $logRecord.channel.id))\r\n\t\t\t\t\t<a href=\"$channelURL\">$logRecord.channel.name</a>\r\n\t\t\t\t#elseif($logRecord.channel.id == 0)\r\n\t\t\t\t\t<a href=\"#\">UnKnow</a>\r\n\t\t\t\t#else\r\n\t\t\t\t\t<a href=\"#\">manager</a>\r\n\t\t\t\t#end\r\n\t\t\t </td>\r\n\t\t\t <td width=\"10%\">\r\n\t\t\t\t#if($logRecord.pipeline.id > 0)\r\n\t\t\t\t\t#set ($pipelineURL = $homeModule.setTarget(\"pipelineList.vm\").addQueryData(\"channelId\", $logRecord.channel.id).addQueryData(\"pipelineId\", $logRecord.pipeline.id))\r\n\t\t\t\t\t<a href=\"$pipelineURL\">$logRecord.pipeline.name</a>\r\n\t\t\t\t#elseif($logRecord.pipeline.id == 0)\r\n\t\t\t\t\t<a href=\"#\">UnKnow</a>\r\n\t\t\t\t#else\r\n\t\t\t\t\t<a href=\"#\">manager</a>\r\n\t\t\t\t#end\r\n\t\t\t </td>\r\n\t\t\t <td width=\"7%\">\r\n\t\t\t\t#if($logRecord.nid && $logRecord.nid > 0)\r\n\t\t\t\t\t#set ($nodeInfoURL = $homeModule.setTarget(\"nodeInfo.vm\").addQueryData(\"nodeId\", $logRecord.nid))\r\n\t\t\t\t\t<a href=\"$nodeInfoURL\">$logRecord.nid</a>\r\n\t\t\t\t#else \r\n\t\t\t\t\t<a href=\"#\">manager</a>\r\n\t\t\t\t#end\r\n\t\t\t </td>\r\n\t\t\t <td width=\"10%\">$logRecord.title</td> \r\n\t\t\t <td width=\"40%\" class=\"message\">\r\n\t\t\t\t<a onclick=\"openDialog('$numberFormat.getHtmlOriginalContent($!logRecord.message)')\">点击查看详细信息</a>\r\n\t\t\t </td>\r\n\t\t\t  <td width=\"20%\">$!numberFormat.format($logRecord.gmtCreate)</td>\r\n\t\t   </tr>\r\n\t\t  #end\r\n\t\t \r\n        </table>\r\n\t\t\r\n\t\t <div class=\"page\">共$paginator.items条数据&nbsp;&nbsp;第$paginator.page页/共$paginator.pages页&nbsp;&nbsp; \r\n       \r\n\t\t\t#if($paginator.page == 1)\r\n\t\t\t\t<font color=\"999999\">首页</font>\r\n\t\t\t#else\r\n\t\t\t\t\t<a href=\"#\" class=\"prev\" onclick=\"pageNavigation(this,1)\">首页</a>\r\n\t\t\t#end\r\n\t   \r\n\t\t\t#if($paginator.page > 1)\r\n\t\t\t\t#set($pre_page = $paginator.page - 1)\r\n\t\t\t\t\t<a href=\"#\" class=\"prev\" onclick=\"pageNavigation(this,$pre_page)\">上一页</a>\r\n\t\t\t#else\r\n            <font color=\"999999\">上一页</font>\r\n\t\t\t#end\r\n\t\t\t##分页下标\r\n\t\t\t#set($counts_keys = $paginator.getSlider(7))\r\n\t\t\t#foreach( $thisPage in $counts_keys)\r\n\t\t\t\t#if( $thisPage == $paginator.page)\r\n\t\t\t\t\t<b>$thisPage</b>\r\n\t\t\t\t#else\r\n\t\t\t\t\t#if($thisPage != 0)\r\n\t\t\t\t\t\t<a href=\"#\" class=\"num\" onclick=\"pageNavigation(this,$thisPage)\">$thisPage</a> \r\n\t\t\t\t\t#end\r\n\t\t\t\t#end\r\n\t\t\t#end\r\n\t   \r\n\t\t\t#if($paginator.page < $paginator.pages)\r\n\t\t\t\t#set($next_page = $paginator.page + 1)\r\n\t\t\t\t\t<a href=\"#\" class=\"prev\" onclick=\"pageNavigation(this,$next_page)\">下一页</a>\r\n\t\t\t#else\r\n\t\t\t\t<font color=\"999999\">下一页</font>\r\n\t\t\t#end\r\n\t   \r\n\t\t\t#if($paginator.page == $paginator.pages)\r\n\t\t\t\t<font color=\"999999\">末页</font>\r\n\t\t\t#else\r\n\t\t\t\t<a href=\"#\" class=\"prev\" onclick=\"pageNavigation(this,$paginator.pages)\">末页</a>\r\n\t\t\t#end\r\n    \t </div>    \r\n\t\t\r\n       <!--</div>-->\r\n    </div>\r\n\t</div>\r\n  </div>\r\n</div>"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/templates/home/screen/login.vm",
    "content": "$rundata.setLayoutEnabled(false)\r\n#macro (loginMessage $field)\r\n    #if (!$field.valid) $field.message #end\r\n#end\r\n<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\r\n<html xmlns=\"http://www.w3.org/1999/xhtml\">\r\n<head>\r\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\r\n<title>Otter Manager</title>\r\n<link type=\"text/css\" href=\"css/commons.css\" rel=\"stylesheet\"/>\r\n<link type=\"text/css\" href=\"css/skin.css\" rel=\"stylesheet\"/>\r\n<link type=\"text/css\" href=\"css/otter.css\" rel=\"stylesheet\"/>\r\n</head>\r\n<body class=\"login\">\r\n   <div class=\"login_container\">\r\n      <div class=\"login_box\">\r\n         <div class=\"logo\"></div>\r\n         <div class=\"user\">\r\n\t\t <form method=\"post\" name=\"login\">\r\n              <input type=\"hidden\" name=\"action\" value=\"user_action\"/>\r\n              <input type=\"hidden\" name=\"event_submit_do_login\" value=\"1\" />\r\n\t\t\t  \r\n\t\t\t<table border=\"0\" cellspacing=\"0\" cellpadding=\"0\">\r\n\t\t\t\t#set ($userGroup = $form.login.defaultInstance)\r\n                <tr>\r\n                   <td align=\"right\">用户名：</td>\r\n                   <td><input name=\"$userGroup.name.key\" value=\"$!userGroup.name.value\" type=\"text\" class=\"login_input\"/></td>\r\n                </tr>\r\n                <tr>\r\n                   <td align=\"right\">密码：</td>\r\n                  <td><input name=\"$userGroup.password.key\" type=\"password\" class=\"login_input\"/></td>\r\n                </tr>\r\n\t\t\t\t<br />\r\n                <span class=\"red\">#loginMessage ($userGroup.loginError)</span>\r\n\t\t\t\t\t\r\n                <tr>\r\n                    <td>&nbsp;</td>\r\n                    <td><div class=\"login_btn right\"><a href=\"javascript:document.login.submit();\">登&nbsp;&nbsp;&nbsp;录</a></div></td>\r\n                </tr>\r\n\t\t\t</table>\r\n         </form>\r\n         </div></div>\r\n   </div>\r\n</body>\r\n</html>\r\n"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/templates/home/screen/monitor/getNodes.vm",
    "content": "{success:true,data:{url:\"http://g.cn/\"}} "
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/templates/home/screen/monitor/monitorTrigger.vm",
    "content": "$!rundata.setLayoutEnabled(false)\n$!result"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/templates/home/screen/monitor/ok.vm",
    "content": "$rundata.setLayoutEnabled(false)\r\n\r\nserver is ok!\r\n\r\n#if($isAllOk == \"Y\")\r\n    server is ok!\r\n#else\r\n\t#foreach($r in $results)\r\n\t\t$r\r\n\t#end\r\n#end"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/templates/home/screen/nodeInfo.vm",
    "content": "$control.setTemplate(\"home:navigation.vm\")\r\n\t<!--Load the AJAX API-->\r\n\t<script type='text/javascript' src='dwr/interface/Node.js'></script>  \r\n    <script type='text/javascript' src='dwr/engine.js'></script>  \r\n    <script type='text/javascript' src='dwr/util.js'></script>  \r\n    <script type='text/javascript' src='js/nodeCheck.js'></script>\r\n    <script type=\"text/javascript\" src=\"js/gchartapi.js\"></script>\r\n    <script type=\"text/javascript\">\r\n      google.load('visualization', '1.0', {'packages':['corechart']});\r\n      google.setOnLoadCallback(drawChart);\r\n      function drawChart() {\r\n\t\tvar obj = eval(#noescape()$!heapMemoryUsage#end);\r\n        var data = new google.visualization.DataTable();\r\n        data.addColumn('string', 'Topping');\r\n        data.addColumn('number', 'Slices');\r\n        data.addRows([\r\n          ['unused', obj.committed/1000/1000-obj.used/1000/1000],\r\n          ['used', obj.used/1000/1000],\r\n        ]);\r\n\r\n        var options = {'title':\"The mem total is \"+obj.committed/1000/1000+\" MB\",\r\n\t\t\t\t\t   'backgroundColor':\"#f0f2f4\",\r\n                       'width':400,\r\n                       'height':300};\r\n\r\n        var chart = new google.visualization.PieChart(document.getElementById('chart_div'));\r\n\t\tchart.draw(data, options);\r\n      }\r\n    </script>\r\n\t\r\n<div class=\"main\">\r\n <div class=\"title\"><h2>机器信息</h2></div>\r\n <div class=\"crumbs\">机器信息</div>\r\n <div class=\"setting_box\">\r\n <table cellpadding=\"0\" cellspacing=\"0\" class=\"setting\">\r\n  <tr>\r\n  <th>机器序号：</th><td>$!node.id</td>\r\n  </tr>\r\n  <tr>\r\n  <th>机器名：</th><td>$!node.name</td>\r\n  </tr>\r\n  <tr>\r\n  <th>地址端口：</th><td>$!node.ip:$!node.port</td>\r\n  </tr>\r\n  <tr>\r\n  <th>下载端口：</th><td>$!node.parameters.downloadPort</td>\r\n  </tr>\r\n  <tr>\r\n  <th>MBean端口：</th><td>$!node.parameters.mbeanPort</td>\r\n  </tr>\r\n  <tr>\r\n  <th>外部IP：</th><td>$!node.parameters.externalIp</td>\r\n  </tr>\r\n  <tr>\r\n  <th>启用外部IP：</th><td>#if($!node.parameters.useExternalIp) 是 #else 否 #end</td>\r\n  </tr>\r\n  <tr>\r\n  <th>Zookeeper集群：</th><td>$node.parameters.zkCluster.clusterName [ $stringUtil.join($node.parameters.zkCluster.serverList,\",\") ]</td>\r\n  </tr>\r\n  <tr>\r\n  <th>运行状态：</th><td>$!node.status</td>\r\n  </tr>\r\n  <tr>\r\n  <th>使用内存：</th><td><div id=\"chart_div\"></div></td>\r\n  </tr>\r\n  <tr>\r\n  <th>线程使用状况：</th><td>$!threadActiveSize/$!threadPoolSize</td>\r\n  </tr>\r\n  <tr>\r\n  <th>实际运行的Pipeline数统计：</th><td>$!runningPipelines</td>\r\n  </tr>\r\n  <tr>\r\n  <th>node版本信息：</th><td>$!versionInfo</td>\r\n  </tr>\r\n  <tr>\r\n  <th>操作系统信息：</th><td>$!systemInfo</td>\r\n  </tr>\r\n  <tr>\r\n  <th>描述信息：</th><td>$!node.description</td>\r\n  </tr>\r\n </table>\r\n </div>\r\n <table border=\"0\" cellspacing=\"0\" cellpadding=\"0\" class=\"list changecolor_w\">\r\n    <tr> \r\n      <th>node</th>\r\n      <th>阶段</th>\r\n\t  <th>所属Pipeline</th>\r\n\t  <th>所属Channel</th>\r\n\t  <th>状态信息</th>\r\n\t  <th>pengding</th>\r\n\t  <th>统计信息</th>\r\n    </tr>\r\n    \r\n    #foreach ($channel in $channels)\r\n\t\t#foreach($pipeline in $channel.pipelines)\r\n\t\t\t#foreach($selectNode in $pipeline.selectNodes)\r\n\t\t\t\t#if($selectNode.id == $!node.id)\r\n\t\t\t\t\t<tr> \r\n    \t\t\t\t<td width=\"16%\">$!node.ip:$!node.port</td>\r\n    \t\t\t\t<td>selectNode</td>\r\n    \t\t\t\t<td>\r\n\t\t\t\t\t\t#set ($pipelineInfoURL = $homeModule.setTarget(\"pipelineList.vm\").addQueryData(\"channelId\", $channel.id).addQueryData(\"pipelineId\", $pipeline.id))\r\n    \t\t\t\t\t<a href=\"$pipelineInfoURL\">$pipeline.name</a>\r\n\t\t\t\t\t</td>\r\n    \t\t\t\t<td>\r\n    \t\t\t\t\t#set ($channelInfoURL = $homeModule.setTarget(\"channelList.vm\").addQueryData(\"channelId\", $channel.id))\r\n    \t\t\t\t\t<a href=\"$channelInfoURL\">$channel.name</a>\r\n    \t\t\t\t</td>\r\n\t\t\t\t\t<td>\r\n    \t\t\t\t\t#if($channel.status.isStart())  <font color=\"green\">运行</font>\r\n\t\t\t\t\t\t\t#elseif($channel.status.isPause())  <font color=\"red\">挂起</font>\r\n\t\t\t\t\t\t\t\t#else 停止\r\n\t\t\t\t\t\t#end\r\n\t\t\t\t\t\t<a href=\"#\" onclick=\"isRunning('$!node.id',$!pipeline.id,'SELECT');return false;\">(状态查询)</a>\r\n    \t\t\t\t</td>\r\n\t\t\t\t\t<td>\r\n    \t\t\t\t\t<a href=\"#\" onclick=\"pending('$!node.id',$!pipeline.id,'SELECT');return false;\">(pending查询)</a>\r\n    \t\t\t\t</td>\r\n\t\t\t\t\t<td>\r\n    \t\t\t\t\t<a href=\"#\" onclick=\"aggregation('$!node.id',$!pipeline.id,'SELECT');return false;\">(统计查询)</a>\r\n    \t\t\t\t</td>\r\n    \t\t\t</tr>\r\n\t\t\t\t#end\r\n\t\t\t#end\r\n\t\t\t\r\n    \t\t#foreach($extractNode in $pipeline.extractNodes)\r\n    \t\t\t#if($extractNode.id == $!node.id)\r\n        \t\t\t<tr> \r\n        \t\t\t\t<td width=\"16%\">$!node.ip:$!node.port</td>\r\n        \t\t\t\t<td>extractNode</td>\r\n        \t\t\t\t<td>\r\n\t\t\t\t\t\t\t#set ($pipelineInfoURL = $homeModule.setTarget(\"pipelineList.vm\").addQueryData(\"channelId\", $channel.id).addQueryData(\"pipelineId\", $pipeline.id))\r\n\t\t\t\t\t\t\t<a href=\"$pipelineInfoURL\">$pipeline.name</a>\r\n\t\t\t\t\t\t</td>\r\n        \t\t\t\t<td>\r\n        \t\t\t\t\t#set ($channelInfoURL = $homeModule.setTarget(\"channelList.vm\").addQueryData(\"channelId\", $channel.id))\r\n        \t\t\t\t\t<a href=\"$channelInfoURL\">$channel.name</a>\r\n        \t\t\t\t</td>\r\n\t\t\t\t\t\t<td>\r\n\t\t\t\t\t\t\t#if($channel.status.isStart())  <font color=\"green\">运行</font>\r\n\t\t\t\t\t\t\t\t#elseif($channel.status.isPause())  <font color=\"red\">挂起</font>\r\n\t\t\t\t\t\t\t\t\t#else 停止\r\n\t\t\t\t\t\t\t#end\r\n\t\t\t\t\t\t<a href=\"#\" onclick=\"isRunning('$!node.id',$!pipeline.id,'EXTRACT');return false;\">(状态查询)</a>\r\n\t\t\t\t\t\t</td>\r\n    \t\t\t\t</td>\r\n\t\t\t\t\t<td>\r\n    \t\t\t\t\t<a href=\"#\" onclick=\"pending('$!node.id',$!pipeline.id,'EXTRACT');return false;\">(pending查询)</a>\r\n    \t\t\t\t</td>\r\n\t\t\t\t\t<td>\r\n    \t\t\t\t\t<a href=\"#\" onclick=\"aggregation('$!node.id',$!pipeline.id,'EXTRACT');return false;\">(统计查询)</a>\r\n    \t\t\t\t</td>\r\n        \t\t\t</tr>\r\n    \t\t\t#end\r\n    \t\t#end\r\n\t\t\r\n    \t\t#foreach($loadNode in $pipeline.loadNodes)\r\n    \t\t\t#if($loadNode.id == $!node.id)\r\n        \t\t\t<tr> \r\n        \t\t\t\t<td width=\"16%\">$!node.ip:$!node.port</td>\r\n        \t\t\t\t<td>transformNode</td>\r\n        \t\t\t\t<td>\r\n\t\t\t\t\t\t\t#set ($pipelineInfoURL = $homeModule.setTarget(\"pipelineList.vm\").addQueryData(\"channelId\", $channel.id).addQueryData(\"pipelineId\", $pipeline.id))\r\n\t\t\t\t\t\t\t<a href=\"$pipelineInfoURL\">$pipeline.name</a>\r\n\t\t\t\t\t\t</td>\r\n        \t\t\t\t<td>\r\n        \t\t\t\t\t#set ($channelInfoURL = $homeModule.setTarget(\"channelList.vm\").addQueryData(\"channelId\", $channel.id))\r\n        \t\t\t\t\t<a href=\"$channelInfoURL\">$channel.name</a>\r\n        \t\t\t\t</td>\r\n\t\t\t\t\t\t<td>\r\n\t\t\t\t\t\t\t#if($channel.status.isStart())  <font color=\"green\">运行</font>\r\n\t\t\t\t\t\t\t\t#elseif($channel.status.isPause())  <font color=\"red\">挂起</font>\r\n\t\t\t\t\t\t\t\t\t#else 停止\r\n\t\t\t\t\t\t\t#end\r\n\t\t\t\t\t\t<a href=\"#\" onclick=\"isRunning('$!node.id',$!pipeline.id,'TRANSFORM');return false;\">(状态查询)</a>\r\n\t\t\t\t\t\t</td>\r\n    \t\t\t\t</td>\r\n\t\t\t\t\t<td>\r\n    \t\t\t\t\t<a href=\"#\" onclick=\"pending('$!node.id',$!pipeline.id,'TRANSFORM');return false;\">(pending查询)</a>\r\n    \t\t\t\t</td>\r\n\t\t\t\t\t<td>\r\n    \t\t\t\t\t<a href=\"#\" onclick=\"aggregation('$!node.id',$!pipeline.id,'TRANSFORM');return false;\">(统计查询)</a>\r\n    \t\t\t\t</td>\r\n        \t\t\t</tr>\r\n    \t\t\t#end\r\n\t\t\t#end\r\n\t\t\t\r\n\t\t\t#foreach($loadNode in $pipeline.loadNodes)\r\n    \t\t\t#if($loadNode.id == $!node.id)\r\n        \t\t\t<tr> \r\n        \t\t\t\t<td width=\"16%\">$!node.ip:$!node.port</td>\r\n        \t\t\t\t<td>loadNode</td>\r\n        \t\t\t\t<td>\r\n\t\t\t\t\t\t\t#set ($pipelineInfoURL = $homeModule.setTarget(\"pipelineList.vm\").addQueryData(\"channelId\", $channel.id).addQueryData(\"pipelineId\", $pipeline.id))\r\n\t\t\t\t\t\t\t<a href=\"$pipelineInfoURL\">$pipeline.name</a>\r\n\t\t\t\t\t\t</td>\r\n        \t\t\t\t<td>\r\n        \t\t\t\t\t#set ($channelInfoURL = $homeModule.setTarget(\"channelList.vm\").addQueryData(\"channelId\", $channel.id))\r\n        \t\t\t\t\t<a href=\"$channelInfoURL\">$channel.name</a>\r\n        \t\t\t\t</td>\r\n\t\t\t\t\t\t<td>\r\n\t\t\t\t\t\t\t#if($channel.status.isStart())  <font color=\"green\">运行</font>\r\n\t\t\t\t\t\t\t\t#elseif($channel.status.isPause())  <font color=\"red\">挂起</font>\r\n\t\t\t\t\t\t\t\t\t#else 停止\r\n\t\t\t\t\t\t\t#end\r\n\t\t\t\t\t\t<a href=\"#\" onclick=\"isRunning('$!node.id',$!pipeline.id,'LOAD');return false;\">(状态查询)</a>\r\n\t\t\t\t\t\t</td>\r\n    \t\t\t\t</td>\r\n\t\t\t\t\t<td>\r\n    \t\t\t\t\t<a href=\"#\" onclick=\"pending('$!node.id',$!pipeline.id,'LOAD');return false;\">(pending查询)</a>\r\n    \t\t\t\t</td>\r\n\t\t\t\t\t<td>\r\n    \t\t\t\t\t<a href=\"#\" onclick=\"aggregation('$!node.id',$!pipeline.id,'LOAD');return false;\">(统计查询)</a>\r\n    \t\t\t\t</td>\r\n        \t\t\t</tr>\r\n    \t\t\t#end\r\n    \t\t#end\r\n\t\t#end\r\n\t#end\r\n  </table>\r\n</div>\r\n"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/templates/home/screen/nodeList.vm",
    "content": "$control.setTemplate(\"home:navigation.vm\")\r\n#set($user = $rundata.request.session.getAttribute(\"managerUser\"))\r\n<script type=\"text/javascript\" src=\"js/trcolor.js\"></script>\r\n<script type=\"text/javascript\" src=\"js/jquery-1.4.2.min.js\"></script> \r\n<script type=\"text/javascript\" src=\"js/jquery.simplemodal-1.4.js\"></script> \r\n<script language=\"javascript\">\r\n\tchangeNav(\"node\");\r\n</script>\r\n\r\n\r\n\r\n<!--页面主体-->\r\n<div class=\"main\">\r\n   \r\n  <div class=\"title\"> \r\n    <h2>机器管理</h2>\r\n  </div>\r\n   <div class=\"crumbs\"><a href=\"nodeList.htm\">机器管理</a> </div> \r\n   <!--分页表单-->\r\n   <form id=\"pageform\" name=\"pageform\" action=\"$homeModule.setTarget('nodeList.vm')\" method=\"post\">\r\n    \t<input type=\"hidden\" id=\"pageIndex\" name=\"pageIndex\" value=\"\"/>\r\n\t\t<input type=\"hidden\" id=\"searchKey\" name=\"searchKey\" value=\"$!searchKey\"/>\r\n   </form>\r\n   <!--Node搜索-->\r\n   <div class=\"search_o\"> \r\n\t\t<form name=\"search_node\" action=\"nodeList.htm\"  method=\"post\">\r\n\t\t\t##$csrfToken.hiddenField\r\n\t\t\t<div class=\"search_input\">\r\n\t\t\t\t<input name=\"searchKey\" type=\"text\" value=\"请输入关键字(目前支持Node的ID、名字搜索)\"  onfocus=\"if(this.value == '请输入关键字(目前支持Node的ID、名字搜索)') {this.value='';}\" onblur=\"if(this.value == '') {this.value = '请输入关键字(目前支持Node的ID、名字搜索)';}\" />\r\n\t\t\t</div>\r\n\t\t\t<div class=\"search_btn\"><a href=\"javascript:document.search_node.submit();\"><img src=\"images/search_btn.png\" width=\"39\" height=\"31\" /></a></div>\r\n        </form>\r\n   </div>\r\n   \r\n   <!--列表-->\r\n     \r\n  <table border=\"0\" cellspacing=\"0\" cellpadding=\"0\" class=\"list changecolor_w\">\r\n    <tr> \r\n      <th>序号</th>\r\n      <th>机器名字</th>\r\n      <th>IP</th>\r\n      <th>端口</th>\r\n      <th>状态</th>\r\n      <th>操作</th>\r\n\t  \r\n    </tr>\r\n\t#foreach($seniorNode in $seniorNodes)\r\n\t\t#set ($nodeInfoURL = $homeModule.setTarget(\"nodeInfo.vm\").addQueryData(\"nodeId\", $seniorNode.id).render())\r\n        <tr> \r\n          <td width=\"4%\">$seniorNode.id</td>\r\n          <td>\r\n          <div class=\"bubbleInfo\">\r\n\t\t\t <a href=\"$nodeInfoURL\" id=\"download\" class=\"trigger\">$seniorNode.name</a>\r\n             <div class=\"popup\">\r\n             <div class=\"pop_tips_top\"></div>\r\n             <div class=\"pop_tips_mid\">\r\n              <div class=\"pop_tips_font\">$seniorNode.description\r\n              </div>\r\n             </div>\r\n             <div class=\"pop_tips_bottom\"></div>\r\n             </div>\r\n          </div>      \r\n          </td>\r\n          <td>#if($seniorNode.parameters.useExternalIp) $seniorNode.parameters.externalIp (外) #else $seniorNode.ip #end</td>\r\n          <td>$seniorNode.port</td>\r\n          <td>#if($seniorNode.status.isStart()) 已启动 #else <font color=\"#FF0000\">未启动</font> #end</td>\r\n          <td>\r\n\t\t\t<a href=\"$nodeInfoURL\"><img src=\"images/ico_edit.png\" width=\"13\" height=\"13\" /><span class=\"ico_font\">查看</span></a>\r\n\t\t\t#if($user.authorizeType.isAdmin())\r\n    \t\t\t#if($seniorNode.status.isStart())\r\n    \t\t\t\t<span class=\"ico_line\">|</span><img src=\"images/ico_edit.png\" width=\"13\" height=\"13\" /><span class=\"ico_font\" title=\"node必须在未启动状态才能编辑\">编辑</span>\r\n    \t\t\t#else\r\n    \t\t\t\t#set ($editURL = $homeModule.setTarget(\"editNode.vm\").addQueryData(\"nodeId\", $seniorNode.id).addQueryData(\"pageIndex\", $!paginator.page).addQueryData(\"searchKey\", $!searchKey))\r\n    \t\t\t\t<span class=\"ico_line\">|</span><a href=\"$editURL\"><img src=\"images/ico_edit.png\" width=\"13\" height=\"13\" /><span class=\"ico_font\">编辑</span></a>\r\n    \t\t\t#end\r\n    \t\t\t\r\n    \t\t\t#if($seniorNode.isUsed())\r\n    \t\t\t\t<span class=\"ico_line\">|</span><img src=\"images/ico_del.png\" width=\"9\" height=\"9\" /><span class=\"ico_font\" title=\"node必须取消与Pipeline关联才能删除\">删除</span>\r\n    \t\t\t#else\r\n    \t\t\t\t#set ($removeURL = $homeModule.setAction(\"nodeAction\").addQueryData(\"nodeId\", $seniorNode.id).addQueryData(\"pageIndex\", $!paginator.page).addQueryData(\"searchKey\", $!searchKey).addQueryData(\"eventSubmitDoDelete\", \"true\"))\r\n    \t\t\t\t<span class=\"ico_line\">|</span><a href=\"javascript:if(confirm('确实要删除吗?'))location='$removeURL'\" class=\"link del\" ><img src=\"images/ico_del.png\" width=\"9\" height=\"9\" /><span class=\"ico_font\">删除</span></a>\r\n    \t\t\t#end\r\n\t\t\t#end\r\n\t\t\t\t\t\t\t\t\r\n\t\t  </td>\r\n        </tr>\r\n\t#end\r\n    \r\n  </table>\r\n  <!--常规按钮-->\r\n  #if($user.authorizeType.isAdmin())\r\n\t<div class=\"btn\"><a href=\"$nodeAddLink\">添加</a></div>\r\n  #end\r\n  <!--分页-->\r\n     <div class=\"page\">共$paginator.items条数据&nbsp;&nbsp;第$paginator.page页/共$paginator.pages页&nbsp;&nbsp; \r\n       \r\n\t   #if($paginator.page == 1)\r\n            <font color=\"999999\">首页</font>\r\n\t   #else\r\n\t\t\t<a href=\"#\" class=\"prev\" onclick=\"pageNavigation(this,1)\">首页</a>\r\n\t   #end\r\n\t   \r\n\t   #if($paginator.page > 1)\r\n\t\t\t#set($pre_page = $paginator.page - 1)\r\n\t\t\t\t<a href=\"#\" class=\"prev\" onclick=\"pageNavigation(this,$pre_page)\">上一页</a>\r\n\t   #else\r\n            <font color=\"999999\">上一页</font>\r\n\t   #end\r\n\t   ##分页下标\r\n\t   #set($counts_keys = $paginator.getSlider(7))\r\n\t   #foreach( $thisPage in $counts_keys)\r\n\t\t\t#if( $thisPage == $paginator.page)\r\n                <b>$thisPage</b>\r\n\t\t\t#else\r\n\t\t\t\t#if($thisPage != 0)\r\n\t\t\t\t\t<a href=\"#\" class=\"num\" onclick=\"pageNavigation(this,$thisPage)\">$thisPage</a> \r\n\t\t\t\t#end\r\n\t\t\t#end\r\n\t   #end\r\n\t   \r\n\t   #if($paginator.page < $paginator.pages)\r\n\t\t\t#set($next_page = $paginator.page + 1)\r\n\t\t\t\t<a href=\"#\" class=\"prev\" onclick=\"pageNavigation(this,$next_page)\">下一页</a>\r\n\t   #else\r\n            <font color=\"999999\">下一页</font>\r\n\t   #end\r\n\t   \r\n\t   #if($paginator.page == $paginator.pages)\r\n            <font color=\"999999\">末页</font>\r\n\t   #else\r\n\t\t\t<a href=\"#\" class=\"prev\" onclick=\"pageNavigation(this,$paginator.pages)\">末页</a>\r\n\t   #end\r\n     </div>     \r\n</div>\r\n"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/templates/home/screen/pipelineInfo.vm",
    "content": "$control.setTemplate(\"home:navigation.vm\")\r\n<script language=\"javascript\">\r\n<!--\r\n\tfunction changeSuperConfig() {\r\n\t\tvar obj = document.getElementById('superConfig');\r\n\t\tif(obj.checked) {\r\n\t\t\tchangeDisplay('super','table-row');\r\n\t\t}else {\r\n\t\t\tchangeDisplay('super','none');\r\n\t\t}\r\n\t}\r\n//-->\r\n</script>\r\n\r\n<div class=\"main\">\r\n <div class=\"title\"><h2>Pipeline信息</h2></div>\r\n <div class=\"crumbs\">Pipeline信息</div>\r\n <div class=\"setting_box\">\r\n <table cellpadding=\"0\" cellspacing=\"0\" class=\"setting\">\r\n  <tr>\r\n  <th>Pipeline序号：</th><td>$!pipeline.id</td>\r\n  </tr>\r\n  <tr>\r\n  <th>Pipeline名字：</th><td>$!pipeline.name</td>\r\n  </tr>\r\n  <tr>\r\n  <th>Select机器：</th><td>#foreach($node in $pipeline.selectNodes) <a href=\"nodeInfo.htm?nodeId=$node.id\">$node.name</a>; #end</td>\r\n  </tr>\r\n  #*\r\n  <tr>\r\n  <th>Extract机器：</th><td>#foreach($node in $pipeline.extractNodes) <a href=\"nodeInfo.htm?nodeId=$node.id\">$node.name</a>; #end</td>\r\n  </tr>\r\n  *#\r\n  <tr>\r\n  <th>Load机器：</th><td>#foreach($node in $pipeline.loadNodes) <a href=\"nodeInfo.htm?nodeId=$node.id\">$node.name</a>; #end</td>\r\n  </tr>\r\n  <tr>\r\n  <th>并行度：</th><td>$!pipeline.parameters.parallelism</td>\r\n  </tr>\r\n  <tr>\r\n  <th>数据反查线程数：</th><td>$!pipeline.parameters.extractPoolSize</td>\r\n  </tr>\r\n  <tr>\r\n  <th>数据载入线程数：</th><td>$!pipeline.parameters.loadPoolSize</td>\r\n  </tr>\r\n  <tr>\r\n  <th>文件载入线程数：</th><td>$!pipeline.parameters.fileLoadPoolSize</td>\r\n  </tr>\r\n  <tr>\r\n  <th>主站点：</th><td>$!pipeline.parameters.home</td>\r\n  </tr>\r\n  <tr>\r\n  <th>同步数据来源：</th><td>$!pipeline.parameters.selectorMode</td>\r\n  </tr>\r\n  <tr>\r\n  #set ($canalInfoURL = $homeModule.setTarget(\"canalList.vm\").addQueryData(\"searchKey\", $!pipeline.parameters.destinationName))\r\n  <th>Canal名字：</th><td><a href=\"$canalInfoURL\">$!pipeline.parameters.destinationName</a></td>\r\n  </tr>\r\n  #**\r\n  <tr>\r\n  <th>主道消费端ID：</th><td>$!pipeline.parameters.mainstemClientId</td>\r\n  </tr>\r\n  *#\r\n  <tr>\r\n  <th>主道消费批次大小：</th><td>$!pipeline.parameters.mainstemBatchsize</td>\r\n  </tr>\r\n  #if($pipeline.parameters.selectorMode.isCanal())\r\n\t<tr>\r\n\t\t<th>获取批次数据超时时间：</th><td>$!pipeline.parameters.batchTimeout</td>\r\n\t</tr>\r\n  #end\r\n  <tr>\r\n  <th>Load批次大小：</th><td>$!pipeline.parameters.loadBatchsize</td>\r\n  </tr>\r\n  <tr>\r\n  <th>描述信息：</th><td>$!pipeline.description</td>\r\n  </tr>\r\n  \r\n  <tr>\r\n  <th>是否显示高级设置：</th>\r\n\t<td>\r\n\t\t<input id=\"superConfig\" type='checkbox' name='super' value=1  class=\"setting_input\" onclick=\"changeSuperConfig()\" />\r\n\t</td>\r\n  </tr>\r\n  <tr class=\"super\"> \r\n  <th>使用batch：</th><td>$!pipeline.parameters.useBatch</td>\r\n  </tr>\r\n  <tr class=\"super\"> \r\n  <th>跳过Select异常：</th><td>#if($!pipeline.parameters.skipSelectException) 开启 #else 关闭 #end</td>\r\n  </tr>\r\n  <tr class=\"super\"> \r\n  <th>跳过Load异常：</th><td>#if($!pipeline.parameters.skipLoadException) 开启 #else 关闭 #end</td>\r\n  </tr>\r\n  <tr class=\"super\"> \r\n  <th>仲裁器调度模式：</th><td>#if($!pipeline.parameters.arbitrateMode.isAutomatic()) 自动选择 #else $!pipeline.parameters.arbitrateMode #end</td>\r\n  </tr>\r\n  <tr class=\"super\"> \r\n  <th>负载均衡算法：</th><td>$!pipeline.parameters.lbAlgorithm</td>\r\n  </tr>\r\n  <tr class=\"super\"> \r\n  <th>传输模式：</th><td>#if($!pipeline.parameters.pipeChooseType.isAutomatic()) 自动选择 #else $!pipeline.parameters.pipeChooseType #end</td>\r\n  </tr>\r\n  <tr class=\"super\"> \r\n  <th>记录selector日志：</th><td>#if($!pipeline.parameters.dumpSelector) 开启 #else 关闭 #end</td>\r\n  </tr>\r\n  <tr class=\"super\"> \r\n  <th>记录selector详细日志：</th><td>#if($!pipeline.parameters.dumpSelectorDetail) 开启 #else 关闭 #end</td>\r\n  </tr>\r\n  <tr class=\"super\"> \r\n  <th>记录load日志：</th><td>#if($!pipeline.parameters.isDumpEvent()) 开启 #else 关闭 #end</td>\r\n  </tr>\r\n  <tr class=\"super\"> \r\n  <th>dryRun模式：</th><td>#if($!pipeline.parameters.isDryRun()) 开启 #else 关闭 #end</td>\r\n  </tr>\r\n  <tr class=\"super\"> \r\n  <th>支持ddl同步：</th><td>#if($!pipeline.parameters.ddlSync) 开启 #else 关闭 #end</td>\r\n  </tr>\r\n  <tr class=\"super\"> \r\n  <th>跳过ddl异常：</th><td>#if($!pipeline.parameters.skipDdlException) 开启 #else 关闭 #end</td>\r\n  </tr>\r\n  <tr class=\"super\"> \r\n  <th>文件重复同步对比：</th><td>#if($!pipeline.parameters.fileDetect) 开启 #else 关闭 #end</td>\r\n  </tr>\r\n  <tr class=\"super\"> \r\n  <th>文件传输加密：</th><td>#if($!pipeline.parameters.useFileEncrypt) 开启 #else 关闭 #end</td>\r\n  </tr>\r\n  <tr class=\"super\"> \r\n  <th>启用公网同步：</th><td>#if($!pipeline.parameters.useExternalIp) 开启 #else 关闭 #end</td>\r\n  </tr>\r\n  <tr class=\"super\"> \r\n  <th>跳过自由门数据：</th><td>#if($!pipeline.parameters.skipFreedom) 开启 #else 关闭 #end</td>\r\n  </tr>\r\n  <tr class=\"super\"> \r\n  <th>跳过反查无记录数据：</th><td>#if($!pipeline.parameters.skipNoRow) 开启 #else 关闭 #end</td>\r\n  </tr>\r\n  <tr class=\"super\"> \r\n  <th>启用数据表类型转化：</th><td>#if($!pipeline.parameters.useTableTransform) 开启 #else 关闭 #end</td>\r\n  </tr>\r\n  <tr class=\"super\"> \r\n  <th>兼容字段新增同步：</th><td>#if($!pipeline.parameters.enableCompatibleMissColumn) 开启 #else 关闭 #end</td>\r\n  </tr>\r\n  <tr class=\"super\"> \r\n  <th>自定义同步标记：</th><td>$!pipeline.parameters.channelInfo</td>\r\n  </tr>\r\n </table>\r\n </div>\r\n</div>\r\n\t\r\n<script language=\"javascript\">\r\n<!--\r\n\tchangeSuperConfig();\r\n//-->\r\n</script>\r\n"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/templates/home/screen/pipelineList.vm",
    "content": "$control.setTemplate(\"home:navigation.vm\")\r\n#set($user = $rundata.request.session.getAttribute(\"managerUser\"))\r\n<script type=\"text/javascript\" src=\"js/trcolor.js\"></script>\r\n<script type=\"text/javascript\" src=\"js/jquery-1.4.2.min.js\"></script> \r\n<script language=\"javascript\">\r\n    <!--\r\n\tchangeNav(\"sync\");\r\n    $(function () {\r\n        $('.bubbleInfo').each(function () {\r\n            var distance = 10;\r\n            var time = 250;\r\n            var hideDelay = 200;\r\n\r\n            var hideDelayTimer = null;\r\n\r\n            var beingShown = false;\r\n            var shown = false;\r\n            var trigger = $('.trigger', this);\r\n            var info = $('.popup', this).css('opacity', 0);\r\n\r\n\r\n            $([trigger.get(0), info.get(0)]).mouseover(function () {\r\n                if (hideDelayTimer) clearTimeout(hideDelayTimer);\r\n                if (beingShown || shown) {\r\n                    // don't trigger the animation again\r\n                    return;\r\n                } else {\r\n                    // reset position of info box\r\n                    beingShown = true;\r\n\r\n                    info.css({\r\n                        top: 10,\r\n                        left: 10,\r\n                        display: 'block'\r\n                    }).animate({\r\n                        top: '+=' + distance + 'px',\r\n                        opacity: 1\r\n                    }, time, 'swing', function() {\r\n                        beingShown = false;\r\n                        shown = true;\r\n                    });\r\n                }\r\n\r\n                return false;\r\n            }).mouseout(function () {\r\n                if (hideDelayTimer) clearTimeout(hideDelayTimer);\r\n                hideDelayTimer = setTimeout(function () {\r\n                    hideDelayTimer = null;\r\n                    info.animate({\r\n                        top: '-=' + distance + 'px',\r\n                        opacity: 0\r\n                    }, time, 'swing', function () {\r\n                        shown = false;\r\n                        info.css('display', 'none');\r\n                    });\r\n\r\n                }, hideDelay);\r\n\r\n                return false;\r\n            });\r\n        });\r\n    });\r\n    \r\n    //-->\r\n</script>\r\n\r\n\r\n<div class=\"main\">\r\n  <div class=\"title\"> \r\n    <h2>Pipeline管理</h2>\r\n  </div>\r\n  <div class=\"crumbs\"><a href=\"channelList.htm?channelId=$channel.id\">channel管理</a>&nbsp;&nbsp;>&nbsp;&nbsp;<a href=\"pipelineList.htm?channelId=$channel.id\">Pipeline管理</a></div>\r\n  <div class=\"crumbs\"></div>     \r\n<!--列表-->\r\n \r\n        <table border=\"0\" cellspacing=\"0\" cellpadding=\"0\" class=\"list changecolor_g\">\r\n          <tr> \r\n            <th width=\"3%\">序号</th>\r\n            <th width=\"6%\">Pipeline名字</th>\r\n\t\t\t<th width=\"3%\">并行度</th>\r\n\t\t\t<th width=\"3%\">主站点</th>\r\n\t\t\t<th width=\"5%\">mainstem状态</th>\r\n\t\t\t<th width=\"5%\">延迟时间</th>\r\n\t\t\t<th width=\"8%\">最后同步时间</th>\r\n\t\t\t<th width=\"8%\">最后位点时间</th>\r\n\t\t\t<th width=\"3%\">监控数</th>\r\n        \t<th width=\"20%\">操作</th>\r\n          </tr>\r\n          <tr> \r\n\t\t\t#foreach ($pipeline in $pipelines)\r\n      <td width=\"3%\">$pipeline.id</td>\r\n      <td width=\"6%\">\r\n      <div class=\"bubbleInfo\">\r\n         <a href=\"dataMediaPairList.htm?pipelineId=$pipeline.id\" id=\"download\" class=\"trigger\">$pipeline.name</a>\r\n         <div class=\"popup\">\r\n         <div class=\"pop_tips_top\"></div>\r\n         <div class=\"pop_tips_mid\">\r\n          <div class=\"pop_tips_font\">$!pipeline.description</div>\r\n         </div>\r\n         <div class=\"pop_tips_bottom\"></div>\r\n         </div>\r\n      </div>      \r\n      </td>\r\n\t  <td width=\"3%\">$pipeline.parameters.parallelism</td>\r\n\t  <td width=\"3%\">$pipeline.parameters.home</td>\r\n\t  <td width=\"5%\">\r\n\t\t#set($mainstemData = $mainstemDatas.get($pipeline.id))\r\n\t\t#if(!$mainstemData)\r\n\t\t\t<font color=\"#FF0000\">未工作</font>\r\n\t\t#else\r\n    \t\t#if($mainstemData.status.isTaking())\r\n    \t\t\t<font color=\"#FF0000\">定位中</font>\r\n    \t\t#else\r\n    \t\t\t工作中\r\n    \t\t#end\r\n\t\t#end\r\n\t  </td>\r\n\t  <td width=\"5%\">$!numberFormat.formatDelay($delayStats.get($pipeline.id).delayTime)</td>\r\n\t  #set($throughputStat = $throughputStats.get($pipeline.id))\r\n\t  <td width=\"8%\">$!numberFormat.format($!throughputStat.gmtModified)</td>\r\n\t  #set($positionData = $positionDatas.get($pipeline.id))\r\n\t  <td width=\"8%\">$!numberFormat.format($!positionData.modifiedTime)</td>\r\n\t  <td width=\"3%\">$alarmRuleStats.get($pipeline.id).size()</td>\r\n\t  <td>\r\n\t\t#set ($pipelineInfoURL = $homeModule.setTarget(\"pipelineInfo.vm\").addQueryData(\"pipelineId\", $pipeline.id))\r\n\t\t<a href=\"$pipelineInfoURL\"><img src=\"images/ico_edit.png\" width=\"13\" height=\"13\" /><span class=\"ico_font\">查看</span></a>\r\n\t\t#set ($monitorURL = $homeModule.setTarget(\"alarmRuleList.vm\").addQueryData(\"pipelineId\", $pipeline.id).addQueryData(\"channelId\", $channel.id))\r\n\t\t<span class=\"ico_line\">|</span><a href=\"$monitorURL\"><img src=\"images/ico_edit.png\" width=\"13\" height=\"13\" /><span class=\"ico_font\">监控</span></a>\r\n\t\t#set ($logRecordURL = $homeModule.setTarget(\"logRecordTab.vm\").addQueryData(\"pipelineId\", $pipeline.id))\r\n\t\t<span class=\"ico_line\">|</span><a href=\"$logRecordURL\"><img src=\"images/ico_edit.png\" width=\"13\" height=\"13\" /><span class=\"ico_font\">日志</span></a>\r\n\t\t#if($user.authorizeType.isAdmin() && $channel.status.isStop())\r\n    \t\t#set($flag = false)\r\n\t\t\t#foreach($pair in $pipeline.pairs)\r\n\t\t\t\t#set($flag = true)\r\n\t\t\t#end\r\n\t\t\t#set ($editURL = $homeModule.setTarget(\"editPipeline.vm\").addQueryData(\"pipelineId\", $pipeline.id))\r\n    \t\t<span class=\"ico_line\">|</span><a href=\"$editURL\"><img src=\"images/ico_edit.png\" width=\"13\" height=\"13\" /><span class=\"ico_font\">编辑</span></a>\r\n\t\t\t#if($flag)\r\n    \t\t\t<span class=\"ico_line\">|</span><img src=\"images/ico_del.png\" width=\"9\" height=\"9\"  /><span class=\"ico_font\" title=\"pipeline必须不包含映射关系才能执行删除\">删除</span>\r\n\t\t\t#elseif($alarmRuleStats.get($pipeline.id).size() > 0)\r\n\t\t\t\t<span class=\"ico_line\">|</span><img src=\"images/ico_del.png\" width=\"9\" height=\"9\"  /><span class=\"ico_font\" title=\"pipeline必须不包含监控信息才能执行删除\">删除</span>\r\n\t\t\t#else\r\n\t\t\t\t#set ($deleteURL = $homeModule.setAction(\"pipelineAction\").addQueryData(\"pipelineId\", $pipeline.id).addQueryData(\"channelId\", $channel.id).addQueryData(\"eventSubmitDoDelete\", \"true\"))\r\n\t\t\t\t<span class=\"ico_line\">|</span><a href=\"javascript:if(confirm('确实要删除吗?'))location='$deleteURL'\"><img src=\"images/ico_del.png\" width=\"13\" height=\"13\" /><span class=\"ico_font\">删除</span></a>\r\n\t\t\t#end\r\n\t\t#end\r\n\t  </td>\r\n      \r\n    </tr>\r\n\t\t  #end\r\n        </table>\r\n\t\t#if($user.authorizeType.isAdmin() && $channel.status.isStop() && $channel.pipelines.size() < 2 )\r\n        \t\t<div class=\"btn\">\r\n        \t\t\t#set ($addURL = $homeModule.setTarget(\"addPipeline.vm\").addQueryData(\"channelId\", $channel.id))\r\n        \t\t\t<a href=\"$addURL\">添加</a>\r\n        \t\t</div>\r\n        #end\r\n        \r\n</div>"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/templates/home/screen/selectCanal.vm",
    "content": "$control.setTemplate(\"home:navigation.vm\")\r\n#set($user = $rundata.request.session.getAttribute(\"managerUser\"))\r\n<script type=\"text/javascript\" src=\"js/trcolor.js\"></script>\r\n<script type=\"text/javascript\" src=\"js/jquery-1.4.2.min.js\"></script> \r\n<script type=\"text/javascript\" src=\"js/jquery.simplemodal-1.4.js\"></script> \r\n<script language=\"javascript\">\r\n\tchangeNav(\"sync\");\r\n\tfunction returnSelected(keywordId, keyword) {\r\n\t\tif(window.opener && !window.opener.closed){\r\n\t\t\twindow.opener.changeKeyword(keywordId, keyword);\r\n\t\t}\r\n\t\twindow.close();\r\n\t}\r\n\t\t\r\n</script>\r\n\r\n<!--页面主体-->\r\n<div class=\"main\">\r\n   \r\n  <div class=\"title\"> \r\n    <h2>Canal管理</h2>\r\n  </div>\r\n   <div class=\"crumbs\"><a href=\"canalList.htm\">Canal列表</a> </div> \r\n   <!--分页表单-->\r\n   <form id=\"pageform\" name=\"pageform\" action=\"$homeModule.setTarget('selectCanal.vm')\" method=\"post\">\r\n    \t<input type=\"hidden\" id=\"pageIndex\" name=\"pageIndex\" value=\"\"/>\r\n\t\t<input type=\"hidden\" id=\"searchKey\" name=\"searchKey\" value=\"$!searchKey\"/>\r\n   </form>\r\n   <!--DataMedia搜索-->\r\n   <div class=\"search_o\"> \r\n\t\t<form name=\"search_canal\" action=\"selectCanal.htm\" method=\"post\">\r\n\t\t\t##$csrfToken.hiddenField\r\n\t\t\t<div class=\"search_input\">\r\n\t\t\t\t<input name=\"searchKey\" type=\"text\" value=\"请输入关键字(目前支持Canal的名字，参数搜索)\"  onfocus=\"if(this.value == '请输入关键字(目前支持Canal的名字，参数搜索)') {this.value='';}\" onblur=\"if(this.value == '') {this.value = '请输入关键字(目前支持Canal的名字，参数搜索)';}\" />\r\n\t\t\t</div>\r\n\t\t\t<div class=\"search_btn\"><a href=\"javascript:document.search_canal.submit();\"><img src=\"images/search_btn.png\" width=\"39\" height=\"31\" /></a></div>\r\n        </form>\r\n   </div>\r\n   \r\n   <!--列表-->\r\n  <table border=\"0\" cellspacing=\"0\" cellpadding=\"0\" class=\"list changecolor_g\">\r\n         <tr> \r\n            <th width=\"5%\">序号</th>\r\n            <th width=\"12%\">Canal名字</th>\r\n\t\t\t<th width=\"8%\">数据源类型</th>\r\n\t\t\t<th width=\"10%\">编码</th>\r\n\t\t\t<th width=\"12%\">链接配置</th>\r\n        \t<th width=\"24%\">操作</th> \r\n         </tr>\r\n           \r\n\t\t#foreach ($seniorCanal in $seniorCanals)\r\n\t\t\t<tr>\r\n\t\t\t\t<td width=\"5%\">$!seniorCanal.id</td>\r\n\t\t\t\t<td width=\"5%\">$!seniorCanal.name</td>\r\n\t\t\t\t<td width=\"5%\">$!seniorCanal.canalParameter.sourcingType</td>\r\n\t\t\t\t<td width=\"5%\">$!seniorCanal.canalParameter.connectionCharset</td>\r\n\t\t\t\t<td width=\"5%\">$!seniorCanal.getUrl()</td>\r\n\t\t\t\t<td>\r\n\t\t\t\t\t<input type=\"button\" value=\"选择\" onclick=\"javascript:returnSelected('$seniorCanal.id','$seniorCanal.name')\">\r\n\t\t\t\t</td>\r\n\t\t    </tr>\r\n\t\t#end\r\n     </table>\r\n\t \r\n\t   <!--常规按钮-->\r\n     #if($user.authorizeType.isAdmin())\r\n\t\t<div class=\"btn\"><a href=\"addCanal.htm\" target=\"_blank\">添加</a></div>\r\n\t #end\r\n\t \r\n     <!--分页-->\r\n     <div class=\"page\">共$paginator.items条数据&nbsp;&nbsp;第$paginator.page页/共$paginator.pages页&nbsp;&nbsp; \r\n       \r\n\t   #if($paginator.page == 1)\r\n            <font color=\"999999\">首页</font>\r\n\t   #else\r\n\t\t\t<a href=\"#\" class=\"prev\" onclick=\"pageNavigation(this,1)\">首页</a>\r\n\t   #end\r\n\t   \r\n\t   #if($paginator.page > 1)\r\n\t\t\t#set($pre_page = $paginator.page - 1)\r\n\t\t\t\t<a href=\"#\" class=\"prev\" onclick=\"pageNavigation(this,$pre_page)\">上一页</a>\r\n\t   #else\r\n            <font color=\"999999\">上一页</font>\r\n\t   #end\r\n\t   ##分页下标\r\n\t   #set($counts_keys = $paginator.getSlider(7))\r\n\t   #foreach( $thisPage in $counts_keys)\r\n\t\t\t#if( $thisPage == $paginator.page)\r\n                <b>$thisPage</b>\r\n\t\t\t#else\r\n\t\t\t\t#if($thisPage != 0)\r\n\t\t\t\t\t<a href=\"#\" class=\"num\" onclick=\"pageNavigation(this,$thisPage)\">$thisPage</a> \r\n\t\t\t\t#end\r\n\t\t\t#end\r\n\t   #end\r\n\t   \r\n\t   #if($paginator.page < $paginator.pages)\r\n\t\t\t#set($next_page = $paginator.page + 1)\r\n\t\t\t\t<a href=\"#\" class=\"prev\" onclick=\"pageNavigation(this,$next_page)\">下一页</a>\r\n\t   #else\r\n            <font color=\"999999\">下一页</font>\r\n\t   #end\r\n\t   \r\n\t   #if($paginator.page == $paginator.pages)\r\n            <font color=\"999999\">末页</font>\r\n\t   #else\r\n\t\t\t<a href=\"#\" class=\"prev\" onclick=\"pageNavigation(this,$paginator.pages)\">末页</a>\r\n\t   #end\r\n     </div>     \r\n</div>\r\n"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/templates/home/screen/selectDataMedia.vm",
    "content": "$control.setTemplate(\"home:navigation.vm\")\r\n#set($user = $rundata.request.session.getAttribute(\"managerUser\"))\r\n<script type=\"text/javascript\" src=\"js/trcolor.js\"></script>\r\n<script type=\"text/javascript\" src=\"js/jquery-1.4.2.min.js\"></script> \r\n<script type=\"text/javascript\" src=\"js/jquery.simplemodal-1.4.js\"></script> \r\n<script language=\"javascript\">\r\n\tchangeNav(\"sync\");\r\n\tfunction returnSelected(keywordId, keyword, local) {\r\n\t\tif(window.opener && !window.opener.closed){\r\n\t\t\twindow.opener.changeKeyword(keywordId, keyword, local);\r\n\t\t}\r\n\t\twindow.close();\r\n\t}\r\n\t\t\r\n</script>\r\n\r\n<!--页面主体-->\r\n<div class=\"main\">\r\n   \r\n  <div class=\"title\"> \r\n    <h2>数据表管理</h2>\r\n  </div>\r\n   <div class=\"crumbs\"><a href=\"dataMediaList.htm\">数据表管理</a> </div> \r\n   <!--分页表单-->\r\n   <form id=\"pageform\" name=\"pageform\" action=\"$homeModule.setTarget('selectDataMedia.vm')\" method=\"post\">\r\n    \t<input type=\"hidden\" id=\"pageIndex\" name=\"pageIndex\" value=\"\"/>\r\n\t\t<input type=\"hidden\" id=\"searchKey\" name=\"searchKey\" value=\"$!searchKey\"/>\r\n\t\t<input type=\"hidden\" name=\"local\" value=\"$!local\"/>\r\n   </form>\r\n   <!--DataMedia搜索-->\r\n   <div class=\"search_o\"> \r\n\t\t<form name=\"search_data_media\" action=\"selectDataMedia.htm\" method=\"post\">\r\n\t\t\t##$csrfToken.hiddenField\r\n\t\t\t<input type=\"hidden\" name=\"local\" value=\"$!local\"/>\r\n\t\t\t<div class=\"search_input\">\r\n\t\t\t\t<input name=\"searchKey\" type=\"text\" value=\"请输入关键字(目前支持DataMedia的ID、名字搜索)\"  onfocus=\"if(this.value == '请输入关键字(目前支持DataMedia的ID、名字搜索)') {this.value='';}\" onblur=\"if(this.value == '') {this.value = '请输入关键字(目前支持DataMedia的ID、名字搜索)';}\" />\r\n\t\t\t</div>\r\n\t\t\t<div class=\"search_btn\"><a href=\"javascript:document.search_data_media.submit();\"><img src=\"images/search_btn.png\" width=\"39\" height=\"31\" /></a></div>\r\n        </form>\r\n   </div>\r\n   \r\n   <!--列表-->\r\n     \r\n  <table border=\"0\" cellspacing=\"0\" cellpadding=\"0\" class=\"list changecolor_w\" >\r\n    <tr> \r\n      <th>序号</th>\r\n\t  <th>Schema Name</th>\r\n\t  <th>Table Name</th>\r\n\t  <th>数据源</th>\r\n\t  <th>操作</th>\r\n    </tr>\r\n\t#foreach ($dataMedia in $dataMedias)\r\n\t\r\n    <tr> \r\n      <td width=\"10%\">$!dataMedia.id</td>\r\n\t  <td width=\"20%\">$!dataMedia.namespace</td>\r\n      <td width=\"20%\">$!dataMedia.name</td>\r\n      <td>\r\n\t\t#set ($dataSourceInfoURL = $homeModule.setTarget(\"dataSourceInfo.vm\").addQueryData(\"dataMediaSourceId\", $!dataMedia.source.id))\r\n\t\t<a href=\"$dataSourceInfoURL\">$!dataMedia.source.name</a>\r\n\t  </td>\r\n\t  <td>\r\n\t\t<input type=\"button\" value=\"选择\" onclick=\"javascript:returnSelected('$dataMedia.id','$dataMedia.name','$local')\">\r\n      </td>\r\n\t  \r\n    </tr>\r\n    #end\r\n  </table>\r\n     \r\n  <!--常规按钮-->\r\n     #if($user.authorizeType.isAdmin())\r\n\t\t<div class=\"btn\"><a href=\"addDataMedia.htm\" target=\"_blank\">添加</a></div>\r\n\t #end\r\n\t \r\n     <!--分页-->\r\n     <div class=\"page\">共$paginator.items条数据&nbsp;&nbsp;第$paginator.page页/共$paginator.pages页&nbsp;&nbsp; \r\n       \r\n\t   #if($paginator.page == 1)\r\n            <font color=\"999999\">首页</font>\r\n\t   #else\r\n\t\t\t<a href=\"#\" class=\"prev\" onclick=\"pageNavigation(this,1)\">首页</a>\r\n\t   #end\r\n\t   \r\n\t   #if($paginator.page > 1)\r\n\t\t\t#set($pre_page = $paginator.page - 1)\r\n\t\t\t\t<a href=\"#\" class=\"prev\" onclick=\"pageNavigation(this,$pre_page)\">上一页</a>\r\n\t   #else\r\n            <font color=\"999999\">上一页</font>\r\n\t   #end\r\n\t   ##分页下标\r\n\t   #set($counts_keys = $paginator.getSlider(7))\r\n\t   #foreach( $thisPage in $counts_keys)\r\n\t\t\t#if( $thisPage == $paginator.page)\r\n                <b>$thisPage</b>\r\n\t\t\t#else\r\n\t\t\t\t#if($thisPage != 0)\r\n\t\t\t\t\t<a href=\"#\" class=\"num\" onclick=\"pageNavigation(this,$thisPage)\">$thisPage</a> \r\n\t\t\t\t#end\r\n\t\t\t#end\r\n\t   #end\r\n\t   \r\n\t   #if($paginator.page < $paginator.pages)\r\n\t\t\t#set($next_page = $paginator.page + 1)\r\n\t\t\t\t<a href=\"#\" class=\"prev\" onclick=\"pageNavigation(this,$next_page)\">下一页</a>\r\n\t   #else\r\n            <font color=\"999999\">下一页</font>\r\n\t   #end\r\n\t   \r\n\t   #if($paginator.page == $paginator.pages)\r\n            <font color=\"999999\">末页</font>\r\n\t   #else\r\n\t\t\t<a href=\"#\" class=\"prev\" onclick=\"pageNavigation(this,$paginator.pages)\">末页</a>\r\n\t   #end\r\n     </div>     \r\n</div>\r\n"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/templates/home/screen/selectDataSource.vm",
    "content": "$control.setTemplate(\"home:navigation.vm\")\r\n#set($user = $rundata.request.session.getAttribute(\"managerUser\"))\r\n<script type=\"text/javascript\" src=\"js/trcolor.js\"></script>\r\n<script type=\"text/javascript\" src=\"js/jquery-1.4.2.min.js\"></script> \r\n<script type=\"text/javascript\" src=\"js/jquery.simplemodal-1.4.js\"></script> \r\n<script language=\"javascript\">\r\n\r\n        <!--\r\n        \tchangeNav(\"datamedia\");\r\n    \t//-->\r\n\t\t$(function() {\r\n\t\t\t\r\n\t\t\tfunction showModal(src, height, width) {\r\n\t\t\t\tjQuery.modal('<iframe src=\"' + src + '\" height=\"' + height + '\" width=\"' + width + '\" frameborder=\"0\" allowTransparency=true>', {\r\n\t\t\t\t\tcloseHTML : \"<input type='button' style='display:none'/>\",\r\n\t\t\t\t\tcloseClass: \"modalClose\",\r\n\t\t\t\t\topacity : 35,\r\n\t\t\t\t\toverlayCss : {\r\n\t\t\t\t\t\tbackgroundColor: \"#000\"\r\n\t\t\t\t\t}\r\n\t\t\t\t});\r\n\t\t\t}\r\n\t\t\t\r\n\t\t\t$(\".del\").click(function() {\r\n\t\t\t\tshowModal(\"tip_message_del.html\", \"350\", \"750\");\r\n\t\t\t});\r\n\r\n\t\t\tfunction megaHoverOver(){\r\n\t\t\t\t$(this).find(\".sub\").stop().fadeTo('fast', 1).show();\r\n\t\t\t\t//Calculate width of all ul's\r\n\t\t\t\t(function($) { \r\n\t\t\t\t\tjQuery.fn.calcSubWidth = function() {\r\n\t\t\t\t\t\trowWidth = 0;\r\n\t\t\t\t\t\t//Calculate row\r\n\t\t\t\t\t\t$(this).find(\"ul\").each(function() {\t\t\t\t\t\r\n\t\t\t\t\t\t\trowWidth += $(this).width(); \r\n\t\t\t\t\t\t});\t\r\n\t\t\t\t\t};\r\n\t\t\t\t})(jQuery); \r\n\t\t\t\t\r\n\t\t\t\tif ( $(this).find(\".row\").length > 0 ) { //If row exists...\r\n\t\t\t\t\tvar biggestRow = 0;\t\r\n\t\t\t\t\t//Calculate each row\r\n\t\t\t\t\t$(this).find(\".row\").each(function() {\t\t\t\t\t\t\t   \r\n\t\t\t\t\t\t$(this).calcSubWidth();\r\n\t\t\t\t\t\t//Find biggest row\r\n\t\t\t\t\t\tif(rowWidth > biggestRow) {\r\n\t\t\t\t\t\t\tbiggestRow = rowWidth;\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t});\r\n\t\t\t\t\t//Set width\r\n\t\t\t\t\t$(this).find(\".sub\").css({'width' :biggestRow});\r\n\t\t\t\t\t$(this).find(\".row:last\").css({'margin':'0'});\r\n\t\t\t\t} else { //If row does not exist...\r\n\t\t\t\t\t$(this).calcSubWidth();\r\n\t\t\t\t\t//Set Width\r\n\t\t\t\t\t$(this).find(\".sub\").css({'width' : rowWidth});\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\tfunction megaHoverOut(){ \r\n\t\t\t  $(this).find(\".sub\").stop().fadeTo('fast', 0, function() {\r\n\t\t\t\t  $(this).hide(); \r\n\t\t\t  });\r\n\t\t\t}\r\n\t\t\tvar config = {    \r\n\t\t\t\t sensitivity: 1, // number = sensitivity threshold (must be 1 or higher)    \r\n\t\t\t\t interval: 100, // number = milliseconds for onMouseOver polling interval    \r\n\t\t\t\t over: megaHoverOver, // function = onMouseOver callback (REQUIRED)    \r\n\t\t\t\t timeout: 200, // number = milliseconds delay before onMouseOut    \r\n\t\t\t\t out: megaHoverOut // function = onMouseOut callback (REQUIRED)    \r\n\t\t\t};\r\n\t\t});\r\n\t\t\r\n\t\tfunction returnSelected(keywordId, keyword) {\r\n \t\t\tif(window.opener && !window.opener.closed){\r\n \t\t\t\twindow.opener.changeKeyword(keywordId, keyword);\r\n \t\t\t}\r\n \t\t\twindow.close();\r\n\t\t}\r\n</script>\r\n\r\n\r\n<!--页面主体-->\r\n<div class=\"main\">\r\n   \r\n  <div class=\"title\"> \r\n    <h2>选择数据源</h2>\r\n  </div>\r\n   <div class=\"crumbs\">选择数据源</div> \r\n   <!--分页表单-->\r\n   <form id=\"pageform\" name=\"pageform\" action=\"$homeModule.setTarget('selectDataSource.vm')\" method=\"post\">\r\n    \t<input type=\"hidden\" id=\"pageIndex\" name=\"pageIndex\" value=\"\"/>\r\n\t\t<input type=\"hidden\" id=\"searchKey\" name=\"searchKey\" value=\"\"/>\r\n   </form>\r\n    <!--DataMedia搜索-->\r\n   <div class=\"search_o\"> \r\n\t\t<form name=\"search_data_source\" action=\"selectDataSource.htm\" method=\"post\">\r\n\t\t\t##$csrfToken.hiddenField\r\n\t\t\t<div class=\"search_input\">\r\n\t\t\t\t<input name=\"searchKey\" type=\"text\" value=\"请输入关键字(目前支持DataSource的ID、名字搜索)\"  onfocus=\"if(this.value == '请输入关键字(目前支持DataSource的ID、名字搜索)') {this.value='';}\" onblur=\"if(this.value == '') {this.value = '请输入关键字(目前支持DataSource的ID、名字搜索)';}\" />\r\n\t\t\t</div>\r\n\t\t\t<div class=\"search_btn\"><a href=\"javascript:document.search_data_source.submit();\"><img src=\"images/search_btn.png\" width=\"39\" height=\"31\" /></a></div>\r\n        </form>\r\n   </div>\r\n   \r\n   <!--列表-->\r\n     \r\n  <table border=\"0\" cellspacing=\"0\" cellpadding=\"0\" class=\"list changecolor_w\">\r\n    <tr> \r\n      <th>序号</th>\r\n      <th>DataSource名字</th>\r\n      <th width=\"12%\">Type</th>\r\n\t  <th width=\"14%\">驱动</th>\r\n\t  <th width=\"14%\">编码</th>\r\n      <th width=\"20%\">URL</th>\r\n      <th width=\"20%\">操作</th>\r\n    </tr>\r\n\t\r\n\t#foreach ($source in $sources)\r\n    <tr> \r\n      <td width=\"5%\">$!source.id</td>\r\n      <td width=\"12%\">$!source.name</td>\r\n      <td>$!source.type</td>\r\n\t  <td>$!source.driver</td>\r\n\t  <td>$!source.encode</td>\r\n      <td>$!source.url</td>\r\n      <td>\r\n\t\t<input type=\"button\" value=\"选择\" onclick=\"javascript:returnSelected('$source.id','$source.name')\">\r\n\t  </td>\r\n    </tr>\r\n    #end\r\n  </table>\r\n  <!--常规按钮-->\r\n  #if($user.authorizeType.isAdmin())\r\n\t<div class=\"btn\"><a href=\"addDataSource.htm\" target=\"_blank\">添加</a></div>\r\n  #end\r\n  \r\n  <!--分页-->\r\n     <div class=\"page\">共$paginator.items条数据&nbsp;&nbsp;第$paginator.page页/共$paginator.pages页&nbsp;&nbsp; \r\n       \r\n\t   #if($paginator.page == 1)\r\n            <font color=\"999999\">首页</font>\r\n\t   #else\r\n\t\t\t<a href=\"#\" class=\"prev\" onclick=\"pageNavigation(this,1,'$!searchKey')\">首页</a>\r\n\t   #end\r\n\t   \r\n\t   #if($paginator.page > 1)\r\n\t\t\t#set($pre_page = $paginator.page - 1)\r\n\t\t\t\t<a href=\"#\" class=\"prev\" onclick=\"pageNavigation(this,$pre_page,'$!searchKey')\">上一页</a>\r\n\t   #else\r\n            <font color=\"999999\">上一页</font>\r\n\t   #end\r\n\t   ##分页下标\r\n\t   #set($counts_keys = $paginator.getSlider(7))\r\n\t   #foreach( $thisPage in $counts_keys)\r\n\t\t\t#if( $thisPage == $paginator.page)\r\n                <b>$thisPage</b>\r\n\t\t\t#else\r\n\t\t\t\t#if($thisPage != 0)\r\n\t\t\t\t\t<a href=\"#\" class=\"num\" onclick=\"pageNavigation(this,$thisPage,'$!searchKey')\">$thisPage</a> \r\n\t\t\t\t#end\r\n\t\t\t#end\r\n\t   #end\r\n\t   \r\n\t   #if($paginator.page < $paginator.pages)\r\n\t\t\t#set($next_page = $paginator.page + 1)\r\n\t\t\t\t<a href=\"#\" class=\"prev\" onclick=\"pageNavigation(this,$next_page,'$!searchKey')\">下一页</a>\r\n\t   #else\r\n            <font color=\"999999\">下一页</font>\r\n\t   #end\r\n\t   \r\n\t   #if($paginator.page == $paginator.pages)\r\n            <font color=\"999999\">末页</font>\r\n\t   #else\r\n\t\t\t<a href=\"#\" class=\"prev\" onclick=\"pageNavigation(this,$paginator.pages,'$!searchKey')\">末页</a>\r\n\t   #end\r\n     </div>\r\n</div>\r\n"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/templates/home/screen/systemParameter.vm",
    "content": "$control.setTemplate(\"home:navigation.vm\")\r\n<script language=\"javascript\">\r\n<!--\r\n\tchangeNav(\"user\");\r\n//-->\r\n</script>\r\n#macro (systemParameterMessage $field)\r\n    #if (!$field.valid) $!field.message #end\r\n#end\t\r\n\r\n<div class=\"main\">\r\n  <div class=\"title\"> \r\n    <h2>系统参数设置</h2>\r\n  </div>\r\n <div class=\"crumbs\"><a href=\"systemParameter.htm\">系统参数设置</a></div>\r\n<form name=\"systemParameterForm\" method=\"post\" enctype=\"multipart/form-data\">\r\n\t$csrfToken.hiddenField\r\n\t<input type=\"hidden\" name=\"action\" value=\"systemParameter_action\"/>\r\n\t<input type=\"hidden\" name=\"event_submit_do_edit\" value=\"1\" />\r\n <div class=\"setting_box\">\r\n   #set ($systemParameterGroup = $form.systemParameterInfo.defaultInstance)\r\n   #set ($systemParameterDetailGroup = $form.systemParameterDetailInfo.defaultInstance)\r\n    <table cellpadding=\"0\" cellspacing=\"0\" class=\"setting_otter\">\r\n      <tr> \r\n        <th>系统schema：</th>\r\n        <td>\r\n\t\t\t<input type=\"text\" name=\"$systemParameterDetailGroup.systemSchema.key\" value=\"$!systemParameter.systemSchema\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#systemParameterMessage ($systemParameterDetailGroup.systemSchema)</span>\r\n\t\t</td>\r\n      </tr>\r\n      <tr> \r\n        <th>双向同步标记表：</th>\r\n        <td>\r\n\t\t\t<input type=\"text\" name=\"$systemParameterDetailGroup.systemMarkTable.key\" value=\"$!systemParameter.systemMarkTable\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#systemParameterMessage ($systemParameterDetailGroup.systemMarkTable)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  <tr> \r\n        <th>双向同步标记id列名：</th>\r\n\t\t<td>\r\n        <input type=\"text\" name=\"$systemParameterDetailGroup.systemMarkTableColumn.key\" value=\"$!systemParameter.systemMarkTableColumn\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#systemParameterMessage ($systemParameterDetailGroup.systemMarkTableColumn)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  <tr> \r\n        <th>双向同步标记info列名：</th>\r\n\t\t<td>\r\n        <input type=\"text\" name=\"$systemParameterDetailGroup.systemMarkTableInfo.key\" value=\"$!systemParameter.systemMarkTableInfo\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#systemParameterMessage ($systemParameterDetailGroup.systemMarkTableInfo)</span>\r\n\t\t</td>\r\n      </tr>\r\n      <tr> \r\n        <th>同步系统buffer表：</th>\r\n\t\t<td>\r\n        <input type=\"text\" name=\"$systemParameterDetailGroup.systemBufferTable.key\" value=\"$!systemParameter.systemBufferTable\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#systemParameterMessage ($systemParameterDetailGroup.systemBufferTable)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  <tr> \r\n        <th>同步系统dual表：</th>\r\n\t\t<td>\r\n        <input type=\"text\" name=\"$systemParameterDetailGroup.systemDualTable.key\" value=\"$!systemParameter.systemDualTable\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#systemParameterMessage ($systemParameterDetailGroup.systemDualTable)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  <tr> \r\n        <th>下载方式：</th>\r\n        <td>\r\n            <select id=\"downloadType\" name=\"$systemParameterDetailGroup.retriever.key\">\r\n            <option value=\"ARIA2C\" #if($!systemParameter.retriever.name() == 'ARIA2C') selected  #end >ARIA2C</option>\r\n            ##<option value=\"MR4J\" #if($!systemParameter.retriever.name() == 'MR4J') selected  #end >MR4J</option>\r\n\t\t\t</select><span class=\"red\">*</span>\r\n        </td>\r\n      </tr>\r\n\t  <tr> \r\n        <th>默认报警联系人：</th>\r\n\t\t<td>\r\n        <input type=\"text\" name=\"$systemParameterDetailGroup.defaultAlarmReceiver.key\" value=\"$!systemParameter.getDefaultAlarmReceiverFormat()\" class=\"setting_input\"/><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#systemParameterMessage ($systemParameterDetailGroup.systemDualTable)</span>\r\n\t\t</td>\r\n      </tr>\r\n\t  <tr> \r\n        <th>自定义报警联系人：</th>\r\n\t\t<td>\r\n\t\t<textarea name=\"$systemParameterDetailGroup.alarmReceiver.key\" cols=\"45\" rows=\"5\">$!systemParameter.getAlarmReceiverFormat()</textarea><span class=\"red\">*</span>\r\n\t\t\t<br />\r\n\t\t\t<span>格式: otter1=xxx@gmail.com;otter2=xxx@gmail.com，多用户请用逗号分隔，分组请用分号分隔.</span>\r\n\t\t\t<br />\r\n\t\t\t<span class=\"red\">#systemParameterMessage ($systemParameterDetailGroup.systemDualTable)</span>\r\n\t\t</td>\r\n      </tr>\r\n    </table> \r\n   </div>\r\n   <div class=\"btn\"><a href=\"javascript:document.systemParameterForm.submit();\">保存</a></div>  #if($edit) <font color=\"red\">保存成功！ </font> #end \r\n  </form>\r\n</div>"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/templates/home/screen/systemReduction.vm",
    "content": "$control.setTemplate(\"home:navigation.vm\")\r\n<script language=\"javascript\">\r\n<!--\r\n\tchangeNav(\"user\");\r\n//-->\r\n</script>\r\n<!--页面主体-->\r\n<div class=\"main\">\r\n   <div class=\"title\">\r\n    <h2>系统初始化</h2>\r\n  </div>\r\n   <div class=\"crumbs\"><a href=\"userManager.htm\">zookeeper节点补全</a></div> \r\n<br/>   \r\n<br/>  \r\n<br/>  \r\n<br/>  \r\n<br/>\r\n解决：zookeeper节点因为人为操作，误删除了channel/pipeline的相关节点，导致manager不可用，可点击进行一键补全\r\n<br/>  \r\n<br/>\r\n  #set ($redoZkLink = $homeModule.setTarget(\"systemReduction.vm\").addQueryData(\"command\", \"true\"))\r\n  <div class=\"btn\"><a href=\"$redoZkLink\">一键补全</a></div>\r\n  \r\n<br/>   \r\n<br/>  \r\n\r\n  <span class=\"red\">$!resultStr</span>\r\n  \r\n</div>\r\n"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/templates/home/screen/userManager.vm",
    "content": "$control.setTemplate(\"home:navigation.vm\")\r\n<script language=\"javascript\">\r\n<!--\r\n\tchangeNav(\"user\");\r\n//-->\r\n</script>\r\n<!--页面主体-->\r\n<div class=\"main\">\r\n   <div class=\"title\">\r\n    <h2>权限管理</h2>\r\n  </div>\r\n   <div class=\"crumbs\"><a href=\"userManager.htm\">权限管理</a></div> \r\n   \r\n   <!--分页表单-->\r\n   <form id=\"pageform\" name=\"pageform\" action=\"$homeModule.setTarget('userManager.vm')\" method=\"post\">\r\n    \t<input type=\"hidden\" id=\"pageIndex\" name=\"pageIndex\" value=\"\"/>\r\n\t\t<input type=\"hidden\" id=\"searchKey\" name=\"searchKey\" value=\"$!searchKey\"/>\r\n   </form>\r\n   <!--全局搜索-->\r\n   <div class=\"search_o\"> \r\n    \t<form name=\"search_user\" action=\"userManager.htm\" method=\"post\">\r\n    \t\t##$csrfToken.hiddenField\r\n\t\t\t<div class=\"search_input\">\r\n\t\t\t\t<input name=\"searchKey\" type=\"text\" value=\"支持ID、用户名、真实姓名、部门搜索\"  onfocus=\"if(this.value == '支持ID、用户名、真实姓名、部门搜索') {this.value='';}\" onblur=\"if(this.value == '') {this.value = '支持ID、用户名、真实姓名、部门搜索';}\" />\r\n\t\t\t</div>\r\n\t\t\t<div class=\"search_btn\"><a href=\"javascript:document.search_user.submit();\"><img src=\"images/search_btn.png\" width=\"39\" height=\"31\" /></a></div>\r\n\t\t</form>\r\n   </div>\r\n   \r\n   <!--列表-->\r\n     \r\n  <table border=\"0\" cellspacing=\"0\" cellpadding=\"0\" class=\"list changecolor_w\">\r\n    <tr> \r\n      <th>序号</th>\r\n      <th>用户名</th>\r\n      <th>部门</th>\r\n      <th>真实姓名</th>\r\n      <th>权限</th>\r\n\t  <th>操作</th>\r\n    </tr>\r\n\t#foreach($user in $users)\r\n    <tr> \r\n      <td width=\"5%\">$user.id</td>\r\n      <td width=\"16%\">$user.name</td>\r\n      <td>$user.department</td>\r\n      <td>$user.realName</td>\r\n      <td>#if($user.authorizeType.isAdmin()) 超级管理员 #else 普通用户 #end</td>\r\n\t  <td><p>\r\n\t\t\t#set ($editURL = $homeModule.setTarget(\"editUser.vm\").addQueryData(\"userId\", $user.id).addQueryData(\"pageIndex\", $!paginator.page).addQueryData(\"searchKey\", $!searchKey))\r\n\t\t\t<a href=\"$editURL\"><img src=\"images/ico_edit.png\" width=\"13\" height=\"13\" /><span class=\"ico_font\">编辑</span></a><span class=\"ico_line\">|</span> \r\n\t\t\t#set ($removeURL = $homeModule.setAction(\"userAction\").addQueryData(\"userId\", $user.id).addQueryData(\"pageIndex\", $!paginator.page).addQueryData(\"searchKey\", $!searchKey).addQueryData(\"eventSubmitDoDelete\", \"true\"))\r\n\t\t\t<a href=\"javascript:if(confirm('确实要删除吗?'))location='$removeURL'\" class=\"link del\"><img src=\"images/ico_del.png\" width=\"9\" height=\"9\" /><span class=\"ico_font\">删除</span></a></p>\r\n\t  </td>\r\n    </tr>\r\n\t#end\r\n  </table>\r\n  <!--常规按钮-->\r\n  <div class=\"btn\"><a href=\"$userAddLink\">添加</a></div>\r\n  <!--分页-->\r\n     <div class=\"page\">共$paginator.items条数据&nbsp;&nbsp;第$paginator.page页/共$paginator.pages页&nbsp;&nbsp; \r\n       \r\n\t   #if($paginator.page == 1)\r\n            <font color=\"999999\">首页</font>\r\n\t   #else\r\n\t\t\t<a href=\"#\" class=\"prev\" onclick=\"pageNavigation(this,1)\">首页</a>\r\n\t   #end\r\n\t   \r\n\t   #if($paginator.page > 1)\r\n\t\t\t#set($pre_page = $paginator.page - 1)\r\n\t\t\t\t<a href=\"#\" class=\"prev\" onclick=\"pageNavigation(this,$pre_page)\">上一页</a>\r\n\t   #else\r\n            <font color=\"999999\">上一页</font>\r\n\t   #end\r\n\t   ##分页下标\r\n\t   #set($counts_keys = $paginator.getSlider(7))\r\n\t   #foreach( $thisPage in $counts_keys)\r\n\t\t\t#if( $thisPage == $paginator.page)\r\n                <b>$thisPage</b>\r\n\t\t\t#else\r\n\t\t\t\t#if($thisPage != 0)\r\n\t\t\t\t\t<a href=\"#\" class=\"num\" onclick=\"pageNavigation(this,$thisPage)\">$thisPage</a> \r\n\t\t\t\t#end\r\n\t\t\t#end\r\n\t   #end\r\n\t   \r\n\t   #if($paginator.page < $paginator.pages)\r\n\t\t\t#set($next_page = $paginator.page + 1)\r\n\t\t\t\t<a href=\"#\" class=\"prev\" onclick=\"pageNavigation(this,$next_page)\">下一页</a>\r\n\t   #else\r\n            <font color=\"999999\">下一页</font>\r\n\t   #end\r\n\t   \r\n\t   #if($paginator.page == $paginator.pages)\r\n            <font color=\"999999\">末页</font>\r\n\t   #else\r\n\t\t\t<a href=\"#\" class=\"prev\" onclick=\"pageNavigation(this,$paginator.pages)\">末页</a>\r\n\t   #end\r\n     </div> \r\n</div>\r\n"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/templates/home/screen/wikiGuide.vm",
    "content": "$control.setTemplate(\"home:navigation.vm\")\n#set($user = $rundata.request.session.getAttribute(\"managerUser\"))\n<script type=\"text/javascript\" src=\"js/trcolor.js\"></script>\n<script type=\"text/javascript\" src=\"js/jquery-1.4.2.min.js\"></script> \n<script type=\"text/javascript\" src=\"js/jquery.simplemodal-1.4.js\"></script> \n<script language=\"javascript\">\n\tchangeNav(\"manual\");\n</script>\n\n<!--页面主体-->\n<div class=\"main\">\n\n  <div class=\"title\">\n    <h2>wiki文档</h2>\n  </div>\n   <div class=\"crumbs\"></div>\n   <div class=\"contentbox_tab changecolor_w\">\n   <font size=\"3px\">\n   <ul>\n    <li><a href=\"https://github.com/alibaba/otter/wiki/Home\">Home</a></li>\n    <li><a href=\"https://github.com/alibaba/otter/wiki/Introduction\">Introduction</a></li>\n\t<li>&nbsp;&nbsp;&nbsp; <a href=\"https://github.com/alibaba/otter/wiki/Otter%E8%B0%83%E5%BA%A6%E6%A8%A1%E5%9E%8B\">Otter调度模型</a></li>\n\t<li>&nbsp;&nbsp;&nbsp; <a href=\"https://github.com/alibaba/otter/wiki/Otter%E6%95%B0%E6%8D%AE%E5%85%A5%E5%BA%93%E7%AE%97%E6%B3%95\">Otter数据入库算法</a></li>\n\t<li>&nbsp;&nbsp;&nbsp; <a href=\"https://github.com/alibaba/otter/wiki/Otter%E5%8F%8C%E5%90%91%E5%9B%9E%E7%8E%AF%E6%8E%A7%E5%88%B6\">Otter双向回环控制</a></li>\n\t<li>&nbsp;&nbsp;&nbsp; <a href=\"https://github.com/alibaba/otter/wiki/Otter%E6%95%B0%E6%8D%AE%E4%B8%80%E8%87%B4%E6%80%A7\">Otter数据一致性</a></li>\n\t<li>&nbsp;&nbsp;&nbsp; <a href=\"https://github.com/alibaba/otter/wiki/Otter%E9%AB%98%E5%8F%AF%E7%94%A8%E6%80%A7\">Otter高可用性</a></li>\n\t<li>&nbsp;&nbsp;&nbsp; <a href=\"https://github.com/alibaba/otter/wiki/Otter%E6%89%A9%E5%B1%95%E6%80%A7\">Otter扩展性</a></li>\n\t<li><a href=\"https://github.com/alibaba/otter/wiki/QuickStart\">QuickStart</a></li>\n\t<li>&nbsp;&nbsp;&nbsp; <a href=\"https://github.com/alibaba/otter/wiki/Manager_Quickstart\">Manager_QuickStart</a></li>\n\t<li>&nbsp;&nbsp;&nbsp; <a href=\"https://github.com/alibaba/otter/wiki/Node_Quickstart\">Node_QuickStar</a></li>\n\t<li><a href=\"https://github.com/alibaba/otter/wiki/Adminguide\">AdminGuide</a></li>\n\t<li>&nbsp;&nbsp;&nbsp; <a href=\"https://github.com/alibaba/otter/wiki/Manager%E4%BD%BF%E7%94%A8%E4%BB%8B%E7%BB%8D\">Manager使用介绍</a></li>\n\t<li>&nbsp;&nbsp;&nbsp; <a href=\"https://github.com/alibaba/otter/wiki/Manager%E9%85%8D%E7%BD%AE%E4%BB%8B%E7%BB%8D\">Manager配置介绍</a></li>\n\t<li><a href=\"https://github.com/alibaba/otter/wiki/%E7%9B%B8%E5%85%B3ppt%26pdf\">相关PPT&amp;PDF</a></li>\n\t<li><a href=\"https://github.com/alibaba/otter/wiki/Faq\">FAQ</a></li>\n   </ul>\n   </font>\n   </div>\n</div>\n"
  },
  {
    "path": "manager/deployer/src/main/resources/webapp/templates/home/screen/zkError.vm",
    "content": "zookeeper出现问题，请联系管理员！"
  },
  {
    "path": "manager/pom.xml",
    "content": "<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\">\n\t<modelVersion>4.0.0</modelVersion>\n\t<parent>\n\t    <groupId>com.alibaba.otter</groupId>\n\t    <artifactId>otter</artifactId>\n\t    <version>4.2.19-SNAPSHOT</version>\n\t</parent>\n\t<groupId>com.alibaba.otter</groupId>\n\t<artifactId>manager</artifactId>\n\t<packaging>pom</packaging>\n\t<name>manager module for otter</name>\n\t<version>4.2.19-SNAPSHOT</version>\n\t<url>http://github.com/alibaba/otter</url>\n\t\n\t<modules>\n\t\t<module>biz</module>\n\t\t<module>web</module>\n\t\t<module>deployer</module>\n\t</modules>\n\t\n\t<properties>\n\t\t<webx_version>3.2.0</webx_version>\n\t</properties>\n\t\n\t<dependencyManagement>\n\t\t<dependencies>\n            <!-- WebX 3 -核心模块 -->\n            <dependency>\n                <groupId>com.alibaba.citrus</groupId>\n                <artifactId>citrus-webx-all</artifactId>\n                <version>${webx_version}</version>\n                <exclusions>\n\t\t\t\t  <exclusion>\n\t\t\t\t\t<groupId>org.codehaus.groovy</groupId>\n\t\t\t\t\t<artifactId>groovy-all</artifactId>\n\t\t\t\t  </exclusion>\n\t\t\t\t  <exclusion>\n\t\t\t\t\t<groupId>org.freemarker</groupId>\n\t\t\t\t\t<artifactId>freemarker</artifactId>\n\t\t\t\t  </exclusion>\n\t\t\t\t</exclusions>\n            </dependency>\n\t\t\t<!-- spring -->\n            <dependency>\n                <groupId>org.springframework</groupId>\n                <artifactId>spring-orm</artifactId>\n                <version>${spring-version}</version>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework</groupId>\n                <artifactId>spring-web</artifactId>\n                <version>${spring-version}</version>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework</groupId>\n                <artifactId>spring-webmvc</artifactId>\n                <version>${spring-version}</version>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework</groupId>\n                <artifactId>spring-test</artifactId>\n                <version>${spring-version}</version>\n            </dependency>\n            <dependency>\n\t\t\t\t<groupId>javax.mail</groupId>\n\t\t\t\t<artifactId>mail</artifactId>\n\t\t\t\t<version>1.4.7</version>\n\t\t\t</dependency>\n            <!-- ibatis -->\n\t\t\t<dependency>\n                <groupId>org.apache.ibatis</groupId>\n                <artifactId>ibatis-sqlmap</artifactId>\n                <version>2.3.4.726</version>\n            </dependency>\n\t\t\t<!-- ajax dwr -->\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.directwebremoting</groupId>\n\t\t\t\t<artifactId>dwr</artifactId>\n\t\t\t\t<version>2.0.10</version>\n\t\t\t</dependency>\n\t\t\t<!-- jetty -->\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.eclipse.jetty</groupId>\n\t\t\t\t<artifactId>jetty-server</artifactId>\n\t\t\t\t<version>${jetty_verion}</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.eclipse.jetty</groupId>\n\t\t\t\t<artifactId>jetty-webapp</artifactId>\n\t\t\t\t<version>${jetty_verion}</version>\n\t\t\t</dependency>\n\t\t</dependencies>\n\t</dependencyManagement>\n</project>\n"
  },
  {
    "path": "manager/web/pom.xml",
    "content": "<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\">\n\t<modelVersion>4.0.0</modelVersion>\n\t<parent>\n\t\t<groupId>com.alibaba.otter</groupId>\n\t\t<artifactId>manager</artifactId>\n\t\t<version>4.2.19-SNAPSHOT</version>\n\t\t<relativePath>../pom.xml</relativePath>\n\t</parent>\n\t<groupId>com.alibaba.otter</groupId>\n\t<artifactId>manager.web</artifactId>\n\t<packaging>jar</packaging>\n\t<name>manager web module for otter</name>\n\t<url>http://github.com/alibaba/otter</url>\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>com.alibaba.otter</groupId>\n\t\t\t<artifactId>manager.biz</artifactId>\n\t\t\t<version>${project.version}</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>javax.servlet</groupId>\n\t\t\t<artifactId>javax.servlet-api</artifactId>\n\t\t\t<scope>provided</scope>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>com.alibaba.citrus</groupId>\n\t\t\t<artifactId>citrus-webx-all</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.directwebremoting</groupId>\n\t\t\t<artifactId>dwr</artifactId>\n\t\t</dependency>\n\t</dependencies>\n</project>\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/common/CanalExtraParamUtil.java",
    "content": "package com.alibaba.otter.manager.web.common;\n\nimport java.util.Map;\n\nimport org.apache.commons.lang.StringUtils;\n\nimport com.alibaba.citrus.service.form.Group;\nimport com.alibaba.fastjson.JSONObject;\nimport com.alibaba.otter.canal.instance.manager.model.CanalParameter;\n\npublic class CanalExtraParamUtil {\n\n    public static Map<String, Object> getExtraParamMap(CanalParameter canalParameter) {\n        CanalParameter.SourcingType sourcingType = canalParameter.getSourcingType();\n        if (sourcingType != null && \"OCEANBASE\".equalsIgnoreCase(sourcingType.name())) {\n            return JSONObject.parseObject(canalParameter.getLocalBinlogDirectory());\n        }\n        return null;\n    }\n\n    public static void setExtraParamString(CanalParameter canalParameter, Group canalParameterInfo) {\n        CanalParameter.SourcingType sourcingType = canalParameter.getSourcingType();\n        String localBinlogDirectory = canalParameterInfo.getField(\"localBinlogDirectory\").getStringValue();\n        if (StringUtils.isNotEmpty(localBinlogDirectory)) {\n            canalParameter.setLocalBinlogDirectory(localBinlogDirectory);\n        } else if (sourcingType != null && \"OCEANBASE\".equalsIgnoreCase(sourcingType.name())) {\n            JSONObject extraParam = new JSONObject();\n            extraParam.put(\"rsList\", canalParameterInfo.getField(\"rsList\").getStringValue());\n            extraParam.put(\"tenant\", canalParameterInfo.getField(\"tenant\").getStringValue());\n            canalParameter.setLocalBinlogDirectory(extraParam.toJSONString());\n        }\n    }\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/common/NumberFormatUtil.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.common;\n\nimport java.math.BigDecimal;\nimport java.math.BigInteger;\nimport java.text.DecimalFormat;\nimport java.text.SimpleDateFormat;\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.List;\n\nimport org.apache.commons.lang.StringEscapeUtils;\nimport org.apache.commons.lang.StringUtils;\n\nimport com.alibaba.otter.canal.instance.manager.model.CanalParameter.DataSourcing;\nimport com.alibaba.otter.canal.instance.manager.model.CanalParameter.SourcingType;\nimport com.alibaba.otter.shared.common.utils.version.VersionInfo;\n\n/**\n * 格式化一下页面输出的数字内容\n * \n * @author jianghang 2011-12-1 下午01:53:15\n * @version 4.0.0\n */\npublic class NumberFormatUtil {\n\n    private static final String PATTERN = \"#,###.###\";\n    private static final Long   KB_SIZE = 1024L;\n    private static final Long   MB_SIZE = 1024 * KB_SIZE;\n    private static final Long   GB_SIZE = 1024 * MB_SIZE;\n    private static final Long   TB_SIZE = 1024 * GB_SIZE;\n\n    public static String format(Double data) {\n        if (data == null) {\n            return null;\n        }\n        DecimalFormat format = new DecimalFormat(PATTERN);\n        return format.format(data);\n    }\n\n    public static String format(Integer data) {\n        if (data == null) {\n            return null;\n        }\n        DecimalFormat format = new DecimalFormat(PATTERN);\n        return format.format(data);\n    }\n\n    public static String format(Long data) {\n        if (data == null) {\n            return null;\n        }\n        DecimalFormat format = new DecimalFormat(PATTERN);\n        return format.format(data);\n    }\n\n    public static String format(BigDecimal data) {\n        if (data == null) {\n            return null;\n        }\n\n        DecimalFormat format = new DecimalFormat(PATTERN);\n        return format.format(data);\n    }\n\n    public static String format(BigInteger data) {\n        if (data == null) {\n            return null;\n        }\n\n        DecimalFormat format = new DecimalFormat(PATTERN);\n        return format.format(data);\n    }\n\n    public static String format(Date date) {\n        if (date == null) {\n            return null;\n        }\n        SimpleDateFormat format = new SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss\");\n        return format.format(date);\n    }\n\n    public static String format(java.sql.Date date) {\n        if (date == null) {\n            return null;\n        }\n        SimpleDateFormat format = new SimpleDateFormat(\"yyyy-MM-dd\");\n        return format.format(date);\n    }\n\n    public static String format(java.sql.Time time) {\n        if (time == null) {\n            return null;\n        }\n        SimpleDateFormat format = new SimpleDateFormat(\"HH:mm:ss\");\n        return format.format(time);\n    }\n\n    public static String format(java.sql.Timestamp timestamp) {\n        if (timestamp == null) {\n            return null;\n        }\n        SimpleDateFormat format = new SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss:fffffffff\");\n        return format.format(timestamp);\n    }\n\n    public static String formatDelay(Number data) {\n        if (data == null) {\n            return StringUtils.EMPTY;\n        }\n        long t = data.longValue();\n        if (t < 0) {\n            return String.valueOf(t);\n        }\n        int hour = 0;\n        int minute = 0;\n\n        while (t >= 60 * 60 * 1000) {\n            hour++;\n            t -= 60 * 60 * 1000;\n        }\n\n        while (t >= 60 * 1000) {\n            minute++;\n            t -= 60 * 1000;\n        }\n\n        List<String> result = new ArrayList<String>();\n        if (hour > 0) {\n            result.add(hour + \" h\");\n        }\n        if (minute > 0) {\n            result.add(minute + \" m\");\n        }\n\n        if (t > 0) {\n            DecimalFormat format = new DecimalFormat(PATTERN);\n            result.add(format.format((t * 1.0) / 1000) + \" s\");\n        }\n\n        if (result.size() == 0) {\n            return \"0\";\n        }\n        return StringUtils.join(result, \" \");\n    }\n\n    public static String formatFileSize(Number data) {\n        if (data == null) {\n            return null;\n        }\n\n        long size = data.longValue();\n        if (size > TB_SIZE) {\n            DecimalFormat format = new DecimalFormat(PATTERN);\n            return format.format((size * 1.0) / TB_SIZE) + \" TB\";\n        } else if (size > GB_SIZE) {\n            DecimalFormat format = new DecimalFormat(PATTERN);\n            return format.format((size * 1.0) / GB_SIZE) + \" GB\";\n        } else if (size > MB_SIZE) {\n            DecimalFormat format = new DecimalFormat(PATTERN);\n            return format.format((size * 1.0) / MB_SIZE) + \" MB\";\n        } else if (size > KB_SIZE) {\n            DecimalFormat format = new DecimalFormat(PATTERN);\n            return format.format((size * 1.0) / KB_SIZE) + \" KB\";\n        } else {\n            DecimalFormat format = new DecimalFormat(PATTERN);\n            return format.format(size) + \" B\";\n        }\n\n    }\n\n    public String formatGroupDbAddress(SourcingType defaultType, List<List<DataSourcing>> groupDbAddresses) {\n        StringBuilder builder = new StringBuilder();\n        for (List<DataSourcing> groupDbAddress : groupDbAddresses) {\n            List<String> address = new ArrayList<String>();\n            for (DataSourcing dbAddress : groupDbAddress) {\n                StringBuilder dbAddressBuilder = new StringBuilder();\n                dbAddressBuilder.append(dbAddress.getDbAddress().getHostString());\n                dbAddressBuilder.append(\":\");\n                dbAddressBuilder.append(String.valueOf(dbAddress.getDbAddress().getPort()));\n                if (!defaultType.equals(dbAddress.getType())) {\n                    dbAddressBuilder.append(\":\").append(dbAddress.getType().name());\n                }\n\n                address.add(dbAddressBuilder.toString());\n            }\n            builder.append(StringUtils.join(address, ',')).append(\";\");\n        }\n\n        return builder.toString();\n    }\n\n    public String getHtmlOriginalContent(String originalContent) {\n        originalContent = StringUtils.replace(originalContent, \"\\n\", \"<br>\");\n        originalContent = StringUtils.replace(originalContent, \"\\t\", \"&nbsp;&nbsp;&nbsp;&nbsp;\");\n        return StringEscapeUtils.escapeJavaScript(originalContent);\n    }\n\n    public String getHtmlOriginalContent(String originalContent, String escape) {\n        originalContent = StringUtils.replace(originalContent, \"\\n\", \"<br>\");\n        originalContent = StringEscapeUtils.escapeJavaScript(originalContent);\n        if (\"HTML\".equalsIgnoreCase(escape)) {\n            originalContent = StringEscapeUtils.escapeHtml(originalContent);\n            originalContent = StringUtils.replace(originalContent, \"\\t\", \"&nbsp;&nbsp;&nbsp;&nbsp;\");\n            originalContent = StringUtils.replace(originalContent, \"\\\\t\", \"&nbsp;&nbsp;&nbsp;&nbsp;\");\n            return originalContent;\n        } else {\n            return originalContent;\n        }\n    }\n\n    public String getManagerVersionInfo() {\n        return VersionInfo.getVersion() + \" [ r\" + VersionInfo.getRevision() + \" ] @ \" + VersionInfo.getDate();\n    }\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/common/WebConstant.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.common;\n\n/**\n * OTTER WEB层的常量。\n * \n * @author simon\n */\npublic interface WebConstant {\n\n    /** 在session中保存user用户对象的key。 */\n    String OTTER_USER_SESSION_KEY           = \"otterUser\";\n\n    /** Login页面返回URL的key。 */\n    String LOGIN_RETURN_KEY                 = \"return\";\n\n    /** 登录URL的名字。 */\n    String OTTER_LOGIN_LINK                 = \"otterLoginLink\";\n\n    /** 登记用户URL的名字。 */\n    String OTTER_REGISTER_LINK              = \"otterRegisterLink\";\n\n    /** 登记用户信息URL的名字。 */\n    String OTTER_REGISTER_ACCOUNT_LINK      = \"otterRegisterAccountLink\";\n\n    /** 查看Channel列表。 */\n    String CHANNEL_LIST_LINK                = \"channelListLink\";\n\n    String CHANNEL_LIST_PAGE_LINK           = \"channelListPageLink\";\n\n    String SELECT_DATA_MEDIA_SOURCE_LINK    = \"selectDataMediaSourceLink\";\n\n    String SEARCH_DELAY_STAT_LINK           = \"searchDelayStatLink\";\n\n    /** 进入编辑Channel页面 */\n    String CHANNEL_EDIT_LINK                = \"channelEditLink\";\n\n    String DATA_MEDIA_LIST_LINK             = \"dataMediaListLink\";\n\n    String NODE_LIST_LINK                   = \"nodeListLink\";\n\n    String AUTO_KEEPER_CLUSTERS_LINK        = \"autoKeeperClustersListLink\";\n\n    String AUTO_KEEPER_CLUSTERS_DETAIL_LINK = \"autoKeeperClustersDetailLink\";\n\n    String DATA_MEDIA_SOURCE_LIST_LINK      = \"dataMediaSourceListLink\";\n\n    String USER_SESSION_KEY                 = \"managerUser\";\n\n    String ERROR_FORBIDDEN_Link             = \"errorForbiddenLink\";\n\n    String USER_MANAGER_LINK                = \"userListLink\";\n\n    String ANALYSIS_DELAY_STAT_LINK         = \"analysisDelayStatLink\";\n\n    String ANALYSIS_THROUGHPUT_HISTORY_LINK = \"analysisThroughputHistoryLink\";\n\n    /** 进入编辑Canal页面 */\n    String CANAL_LIST_LINK                  = \"canalListLink\";\n\n    /** 进入编辑Canal页面 */\n    String MATRIX_LIST_LINK                 = \"dataMatrixListLink\";\n\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/common/api/ApiAuthService.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.common.api;\n\nimport com.alibaba.citrus.turbine.TurbineRunData;\n\n/**\n * @author zebin.xuzb @ 2012-5-20\n */\npublic interface ApiAuthService {\n\n    // 考虑不依赖 webx\n    public boolean auth(TurbineRunData rundata);\n\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/common/api/DefaultApiAuthService.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.common.api;\n\nimport com.alibaba.citrus.turbine.TurbineRunData;\n\n/**\n * @author zebin.xuzb @ 2012-5-20\n */\npublic class DefaultApiAuthService implements ApiAuthService {\n\n    @Override\n    public boolean auth(TurbineRunData rundata) {\n        return true; // TODO 需要增加验证逻辑\n    }\n\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/common/api/JsonResult.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.common.api;\n\nimport java.io.Serializable;\n\n/**\n * @author zebin.xuzb @ 2012-5-18\n */\npublic class JsonResult implements Serializable {\n\n    private static final long serialVersionUID = -1637537013205539672L;\n\n    private boolean           success;\n    private String            errMessage;\n    private Object            data;\n\n    public JsonResult(){\n    }\n\n    public JsonResult(boolean success){\n        this.success = success;\n    }\n\n    // ========= setter & getter ==========\n    public boolean isSuccess() {\n        return success;\n    }\n\n    public void setSuccess(boolean success) {\n        this.success = success;\n    }\n\n    public String getErrMessage() {\n        return errMessage;\n    }\n\n    public void setErrMessage(String errMessage) {\n        this.errMessage = errMessage;\n    }\n\n    public Object getData() {\n        return data;\n    }\n\n    public void setData(Object data) {\n        this.data = data;\n    }\n\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/common/model/SeniorCanal.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.common.model;\n\nimport java.util.List;\n\nimport com.alibaba.otter.canal.instance.manager.model.Canal;\nimport com.alibaba.otter.canal.instance.manager.model.CanalParameter;\nimport com.alibaba.otter.canal.instance.manager.model.CanalParameter.DataSourcing;\nimport com.alibaba.otter.shared.common.model.config.pipeline.Pipeline;\n\n/**\n * @author sarah.lij 2012-7-24 下午01:31:06\n */\npublic class SeniorCanal extends Canal {\n\n    private static final long serialVersionUID = -4121314684324595191L;\n    private boolean           used;\n    private List<Pipeline>    pipelines;\n\n    public boolean isUsed() {\n        return used;\n    }\n\n    public void setUsed(boolean used) {\n        this.used = used;\n    }\n\n    public List<Pipeline> getPipelines() {\n        return pipelines;\n    }\n\n    public void setPipelines(List<Pipeline> pipelines) {\n        this.pipelines = pipelines;\n    }\n\n    public String getUrl() {\n        CanalParameter parameter = getCanalParameter();\n        if (parameter.getHaMode().isMedia()) {\n            return \"media://\" + parameter.getMediaGroup();\n        } else {\n            StringBuilder address = new StringBuilder(\"jdbc://\");\n            for (List<DataSourcing> groupAddress : parameter.getGroupDbAddresses()) {\n                int i = 0;\n                for (DataSourcing dbAddress : groupAddress) {\n                    ++i;\n                    address.append(dbAddress.getDbAddress().getHostString())\n                        .append(\":\")\n                        .append(dbAddress.getDbAddress().getPort());\n\n                    if (i < groupAddress.size()) {\n                        address.append(',');\n                    }\n                }\n\n                address.append(';');\n            }\n\n            return address.toString();\n        }\n    }\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/common/model/SeniorChannel.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.common.model;\n\nimport com.alibaba.otter.shared.common.model.config.channel.Channel;\n\n/**\n * @author simon 2012-1-13 下午03:34:32\n */\npublic class SeniorChannel extends Channel {\n\n    private static final long serialVersionUID = -5864547001482768341L;\n    private boolean           processEmpty;\n\n    public boolean isProcessEmpty() {\n        return processEmpty;\n    }\n\n    public void setProcessEmpty(boolean processEmpty) {\n        this.processEmpty = processEmpty;\n    }\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/common/model/SeniorDataMatrix.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.common.model;\n\nimport com.alibaba.otter.shared.common.model.config.data.DataMatrix;\n\npublic class SeniorDataMatrix extends DataMatrix {\n\n    private static final long serialVersionUID = -4121314684324595191L;\n    private boolean           used;\n\n    public boolean isUsed() {\n        return used;\n    }\n\n    public void setUsed(boolean used) {\n        this.used = used;\n    }\n\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/common/model/SeniorDataMedia.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.common.model;\n\nimport java.util.List;\n\nimport com.alibaba.otter.shared.common.model.config.data.DataMedia;\nimport com.alibaba.otter.shared.common.model.config.data.DataMediaPair;\nimport com.alibaba.otter.shared.common.model.config.data.DataMediaSource;\n\n/**\n * @author simon 2011-12-9 下午03:17:39\n */\npublic class SeniorDataMedia extends DataMedia<DataMediaSource> {\n\n    private static final long   serialVersionUID = 1089669449690478640L;\n\n    private boolean             used;\n\n    private List<DataMediaPair> pairs;\n\n    public boolean isUsed() {\n        return used;\n    }\n\n    public void setUsed(boolean used) {\n        this.used = used;\n    }\n\n    public List<DataMediaPair> getPairs() {\n        return pairs;\n    }\n\n    public void setPairs(List<DataMediaPair> pairs) {\n        this.pairs = pairs;\n    }\n\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/common/model/SeniorDataMediaPair.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.common.model;\n\nimport com.alibaba.otter.shared.common.model.config.channel.Channel;\nimport com.alibaba.otter.shared.common.model.config.data.DataMediaPair;\n\n/**\n * @author simon 2012-3-2 下午04:40:29\n */\npublic class SeniorDataMediaPair {\n\n    private Channel       channel;\n\n    private DataMediaPair dataMediaPair;\n\n    public Channel getChannel() {\n        return channel;\n    }\n\n    public void setChannel(Channel channel) {\n        this.channel = channel;\n    }\n\n    public DataMediaPair getDataMediaPair() {\n        return dataMediaPair;\n    }\n\n    public void setDataMediaPair(DataMediaPair dataMediaPair) {\n        this.dataMediaPair = dataMediaPair;\n    }\n\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/common/model/SeniorDataMediaSource.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.common.model;\n\nimport java.util.List;\n\nimport com.alibaba.otter.shared.common.model.config.data.DataMedia;\nimport com.alibaba.otter.shared.common.model.config.data.db.DbMediaSource;\n\n/**\n * @author simon 2011-12-9 下午03:17:39\n */\npublic class SeniorDataMediaSource extends DbMediaSource {\n\n    private static final long serialVersionUID = 3876613625471584350L;\n    private boolean           used;\n    private String            storePath;\n    private List<DataMedia>   dataMedias;\n\n    public boolean isUsed() {\n        return used;\n    }\n\n    public void setUsed(boolean used) {\n        this.used = used;\n    }\n\n    public String getStorePath() {\n        return storePath;\n    }\n\n    public void setStorePath(String storePath) {\n        this.storePath = storePath;\n    }\n\n    public List<DataMedia> getDataMedias() {\n        return dataMedias;\n    }\n\n    public void setDataMedias(List<DataMedia> dataMedias) {\n        this.dataMedias = dataMedias;\n    }\n\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/common/model/SeniorNode.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.common.model;\n\nimport com.alibaba.otter.shared.common.model.config.node.Node;\n\n/**\n * @author simon 2011-12-8 下午09:26:01\n */\npublic class SeniorNode extends Node {\n\n    private static final long serialVersionUID = -4121314684324595191L;\n    private boolean           used;\n\n    public boolean isUsed() {\n        return used;\n    }\n\n    public void setUsed(boolean used) {\n        this.used = used;\n    }\n\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/home/module/action/AbstractAction.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.home.module.action;\n\nimport java.io.UnsupportedEncodingException;\nimport java.net.URLEncoder;\n\n/**\n * 类AbstractAction.java的实现描述：TODO 类实现描述\n * \n * @author simon 2011-12-1 下午03:58:27\n */\npublic class AbstractAction {\n\n    protected String urlEncode(String character) {\n        String result = null;\n        try {\n            result = (null == character ? \"\" : URLEncoder.encode(character, \"utf-8\"));\n        } catch (UnsupportedEncodingException e) {\n            e.printStackTrace();\n        }\n        return result;\n    }\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/home/module/action/AlarmRuleAction.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.home.module.action;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport javax.annotation.Resource;\n\nimport com.alibaba.citrus.service.form.CustomErrors;\nimport com.alibaba.citrus.service.form.Group;\nimport com.alibaba.citrus.turbine.Navigator;\nimport com.alibaba.citrus.turbine.dataresolver.FormField;\nimport com.alibaba.citrus.turbine.dataresolver.FormGroup;\nimport com.alibaba.citrus.turbine.dataresolver.Param;\nimport com.alibaba.citrus.webx.WebxException;\nimport com.alibaba.otter.manager.biz.common.exceptions.RepeatConfigureException;\nimport com.alibaba.otter.manager.biz.config.alarm.AlarmRuleService;\nimport com.alibaba.otter.manager.biz.config.parameter.SystemParameterService;\nimport com.alibaba.otter.shared.common.model.config.alarm.AlarmRule;\nimport com.alibaba.otter.shared.common.model.config.alarm.AlarmRuleStatus;\nimport com.alibaba.otter.shared.common.model.config.alarm.MonitorName;\nimport com.alibaba.otter.shared.common.model.config.parameter.SystemParameter;\n\npublic class AlarmRuleAction extends AbstractAction {\n\n    @Resource(name = \"alarmRuleService\")\n    private AlarmRuleService       alarmRuleService;\n\n    @Resource(name = \"systemParameterService\")\n    private SystemParameterService systemParameterService;\n\n    public void doAdd(@FormGroup(\"alarmRuleInfo\") Group alarmRuleInfo,\n                      @FormField(name = \"formAlarmRuleError\", group = \"alarmRuleInfo\") CustomErrors err, Navigator nav)\n                                                                                                                       throws Exception {\n        AlarmRule alarmRule = new AlarmRule();\n        alarmRuleInfo.setProperties(alarmRule);\n\n        try {\n            alarmRuleService.create(alarmRule);\n        } catch (RepeatConfigureException rce) {\n            err.setMessage(\"invalidAlarmRule\");\n            return;\n        }\n        nav.redirectToLocation(\"alarmRuleList.htm?pipelineId=\" + alarmRule.getPipelineId());\n    }\n\n    /**\n     * 一键添加监控\n     */\n    public void doOnekeyAddMonitor(@Param(\"pipelineId\") Long pipelineId, Navigator nav) throws Exception {\n        List<AlarmRule> existRules = alarmRuleService.getAlarmRules(pipelineId);\n        if (!existRules.isEmpty()) {\n            nav.redirectToLocation(\"alarmRuleList.htm?pipelineId=\" + pipelineId);\n            return;\n        }\n\n        SystemParameter systemParameter = systemParameterService.find();\n        AlarmRule alarmRule = new AlarmRule();\n        alarmRule.setPipelineId(pipelineId);\n        alarmRule.setDescription(\"one key added!\");\n        alarmRule.setAutoRecovery(Boolean.FALSE);\n        alarmRule.setReceiverKey(systemParameter.getDefaultAlarmReceiveKey());\n        alarmRule.setStatus(AlarmRuleStatus.DISABLE);\n        alarmRule.setRecoveryThresold(3);\n        alarmRule.setIntervalTime(1800L);\n\n        try {\n            alarmRule.setMonitorName(MonitorName.EXCEPTION);\n            alarmRule.setMatchValue(\"ERROR,EXCEPTION\");\n            alarmRule.setIntervalTime(1800L);\n            alarmRule.setAutoRecovery(false);\n            alarmRule.setRecoveryThresold(2);\n            alarmRuleService.create(alarmRule);\n            alarmRule.setMonitorName(MonitorName.POSITIONTIMEOUT);\n            alarmRule.setMatchValue(\"600\");\n            alarmRule.setIntervalTime(1800L);\n            alarmRule.setAutoRecovery(true);\n            alarmRule.setRecoveryThresold(0);\n            alarmRuleService.create(alarmRule);\n            alarmRule.setMonitorName(MonitorName.DELAYTIME);\n            alarmRule.setMatchValue(\"600\");\n            alarmRule.setIntervalTime(1800L);\n            alarmRule.setAutoRecovery(false);\n            alarmRule.setRecoveryThresold(2);\n            alarmRuleService.create(alarmRule);\n            alarmRule.setMonitorName(MonitorName.PROCESSTIMEOUT);\n            alarmRule.setMatchValue(\"60\");\n            alarmRule.setIntervalTime(1800L);\n            alarmRule.setAutoRecovery(true);\n            alarmRule.setRecoveryThresold(2);\n            alarmRuleService.create(alarmRule);\n            // alarmRule.setMonitorName(MonitorName.PIPELINETIMEOUT);\n            // alarmRule.setMatchValue(\"43200\");\n            // alarmRuleService.create(alarmRule);\n        } catch (Exception e) {\n            return;\n        }\n        nav.redirectToLocation(\"alarmRuleList.htm?pipelineId=\" + pipelineId);\n    }\n\n    /**\n     * 修改Node\n     */\n    public void doEdit(@FormGroup(\"alarmRuleInfo\") Group alarmRuleInfo,\n                       @FormField(name = \"formAlarmRuleError\", group = \"alarmRuleInfo\") CustomErrors err, Navigator nav)\n                                                                                                                        throws Exception {\n        AlarmRule alarmRule = new AlarmRule();\n        alarmRuleInfo.setProperties(alarmRule);\n\n        try {\n            alarmRuleService.modify(alarmRule);\n        } catch (RepeatConfigureException rce) {\n            err.setMessage(\"invalidAlarmRule\");\n            return;\n        }\n        nav.redirectToLocation(\"alarmRuleList.htm?pipelineId=\" + alarmRule.getPipelineId());\n    }\n\n    /**\n     * 删除node\n     */\n    public void doDelete(@Param(\"alarmRuleId\") Long alarmRuleId, @Param(\"pipelineId\") Long pipelineId, Navigator nav)\n                                                                                                                     throws WebxException {\n        alarmRuleService.remove(alarmRuleId);\n\n        nav.redirectToLocation(\"alarmRuleList.htm?pipelineId=\" + pipelineId);\n\n    }\n\n    public void doStatus(@Param(\"alarmRuleId\") Long alarmRuleId, @Param(\"pipelineId\") Long pipelineId,\n                         @Param(\"status\") String status, @Param(\"pauseTime\") String pauseTime, Navigator nav)\n                                                                                                             throws WebxException {\n        if (status.equals(\"enable\")) {\n            alarmRuleService.enableMonitor(alarmRuleId);\n        } else if (status.equals(\"disable\")) {\n            if (pauseTime != null) {\n                alarmRuleService.disableMonitor(alarmRuleId, pauseTime);\n            } else {\n                alarmRuleService.disableMonitor(alarmRuleId);\n            }\n        }\n\n        nav.redirectToLocation(\"alarmRuleList.htm?pipelineId=\" + pipelineId);\n    }\n\n    public void doStatusSystem(@Param(\"alarmRuleId\") Long alarmRuleId, @Param(\"pageIndex\") int pageIndex,\n                               @Param(\"status\") String status, @Param(\"pauseTime\") String pauseTime, Navigator nav)\n                                                                                                                   throws WebxException {\n\n        if (status.equals(\"enable\")) {\n            alarmRuleService.enableMonitor(alarmRuleId);\n        } else if (status.equals(\"disable\")) {\n            if (pauseTime != null) {\n                alarmRuleService.disableMonitor(alarmRuleId, pauseTime);\n            } else {\n                alarmRuleService.disableMonitor(alarmRuleId);\n            }\n        }\n        nav.redirectToLocation(\"alarmSystemList.htm?pageIndex=\" + pageIndex);\n    }\n\n    public void doStatusByPipeline(@Param(\"pipelineId\") Long pipelineId, @Param(\"status\") String status,\n                                   @Param(\"pauseTime\") String pauseTime, Navigator nav) throws WebxException {\n\n        List<AlarmRule> alarmRules = alarmRuleService.getAlarmRules(pipelineId);\n        for (AlarmRule alarmRule : alarmRules) {\n            if (status.equals(\"enable\")) {\n                if (alarmRule.getStatus().isDisable()) {\n                    alarmRuleService.enableMonitor(alarmRule.getId());\n                }\n            } else if (status.equals(\"disable\")) {\n                if (alarmRule.getStatus().isEnable()) {\n                    if (pauseTime != null) {\n                        alarmRuleService.disableMonitor(alarmRule.getId(), pauseTime);\n                    } else {\n                        alarmRuleService.disableMonitor(alarmRule.getId());\n                    }\n                }\n            }\n        }\n        nav.redirectToLocation(\"alarmRuleList.htm?pipelineId=\" + pipelineId);\n    }\n\n    public void doStatusByRule(@Param(\"alarmRuleIds\") String alarmRuleIds, @Param(\"pageIndex\") int pageIndex,\n                               @Param(\"status\") String status, @Param(\"pauseTime\") String pauseTime, Navigator nav)\n                                                                                                                   throws WebxException {\n\n        String[] a = alarmRuleIds.split(\",\");\n        List<String> alarmRuleArray = Arrays.asList(a);\n        for (String alarmRuleId : alarmRuleArray) {\n            if (status.equals(\"enable\")) {\n                alarmRuleService.enableMonitor(Long.valueOf(alarmRuleId));\n            } else if (status.equals(\"disable\")) {\n                if (pauseTime != null) {\n                    alarmRuleService.disableMonitor(Long.valueOf(alarmRuleId), pauseTime);\n                } else {\n                    alarmRuleService.disableMonitor(Long.valueOf(alarmRuleId));\n                }\n            }\n        }\n        nav.redirectToLocation(\"alarmSystemList.htm?pageIndex=\" + pageIndex);\n    }\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/home/module/action/AutoKeeperClusterAction.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.home.module.action;\n\nimport java.util.Arrays;\n\nimport javax.annotation.Resource;\n\nimport org.apache.commons.lang.StringUtils;\n\nimport com.alibaba.citrus.service.form.CustomErrors;\nimport com.alibaba.citrus.service.form.Group;\nimport com.alibaba.citrus.turbine.Navigator;\nimport com.alibaba.citrus.turbine.dataresolver.FormField;\nimport com.alibaba.citrus.turbine.dataresolver.FormGroup;\nimport com.alibaba.citrus.turbine.dataresolver.Param;\nimport com.alibaba.otter.manager.biz.autokeeper.impl.AutoKeeperCollector;\nimport com.alibaba.otter.manager.biz.common.exceptions.RepeatConfigureException;\nimport com.alibaba.otter.manager.biz.config.autokeeper.AutoKeeperClusterService;\nimport com.alibaba.otter.manager.web.common.WebConstant;\nimport com.alibaba.otter.shared.common.model.autokeeper.AutoKeeperCluster;\n\npublic class AutoKeeperClusterAction extends AbstractAction {\n\n    @Resource(name = \"autoKeeperClusterService\")\n    private AutoKeeperClusterService autoKeeperClusterService;\n    @Resource(name = \"autoKeeperCollector\")\n    private AutoKeeperCollector      autoKeeperCollector;\n\n    public void doAdd(@FormGroup(\"autokeeperClusterInfo\") Group autokeeperClusterInfo,\n                      @FormField(name = \"formAutokeeperClusterError\", group = \"autokeeperClusterInfo\") CustomErrors err,\n                      Navigator nav) throws Exception {\n        AutoKeeperCluster autoKeeperCluster = new AutoKeeperCluster();\n        autokeeperClusterInfo.setProperties(autoKeeperCluster);\n        String zkClustersString = autokeeperClusterInfo.getField(\"zookeeperClusters\").getStringValue();\n        String[] zkClusters = StringUtils.split(zkClustersString, \";\");\n\n        autoKeeperCluster.setServerList(Arrays.asList(zkClusters));\n\n        try {\n            autoKeeperClusterService.createAutoKeeperCluster(autoKeeperCluster);\n        } catch (RepeatConfigureException rce) {\n            err.setMessage(\"invalidNode\");\n            return;\n        }\n        nav.redirectTo(WebConstant.AUTO_KEEPER_CLUSTERS_LINK);\n    }\n\n    public void doEdit(@FormGroup(\"autokeeperClusterInfo\") Group autokeeperClusterInfo,\n                       @FormField(name = \"formAutokeeperClusterError\", group = \"autokeeperClusterInfo\") CustomErrors err,\n                       Navigator nav) throws Exception {\n        AutoKeeperCluster autoKeeperCluster = new AutoKeeperCluster();\n        autokeeperClusterInfo.setProperties(autoKeeperCluster);\n        String zkClustersString = autokeeperClusterInfo.getField(\"zookeeperClusters\").getStringValue();\n        String[] zkClusters = StringUtils.split(zkClustersString, \";\");\n\n        autoKeeperCluster.setServerList(Arrays.asList(zkClusters));\n        try {\n            autoKeeperClusterService.modifyAutoKeeperCluster(autoKeeperCluster);\n        } catch (RepeatConfigureException rce) {\n            err.setMessage(\"invalidChannelName\");\n            return;\n        }\n        nav.redirectTo(WebConstant.AUTO_KEEPER_CLUSTERS_LINK);\n    }\n\n    public void doRefresh(@Param(\"clusterId\") Long clusterId, Navigator nav) throws Exception {\n        AutoKeeperCluster autoKeeperCluster = autoKeeperClusterService.findAutoKeeperClusterById(clusterId);\n        for (String address : autoKeeperCluster.getServerList()) {\n            autoKeeperCollector.collectorServerStat(address);\n            autoKeeperCollector.collectorConnectionStat(address);\n            autoKeeperCollector.collectorWatchStat(address);\n            autoKeeperCollector.collectorEphemeralStat(address);\n        }\n        nav.redirectToLocation(\"/auto_keeper_clusters_list.htm\");\n    }\n\n    public void doDelete(@Param(\"clusterId\") Long clusterId, Navigator nav) throws Exception {\n\n        autoKeeperClusterService.removeAutoKeeperCluster(clusterId);\n        nav.redirectToLocation(\"/auto_keeper_clusters_list.htm\");\n    }\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/home/module/action/CanalAction.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.home.module.action;\n\nimport java.net.InetSocketAddress;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport javax.annotation.Resource;\n\nimport org.apache.commons.lang.StringUtils;\n\nimport com.alibaba.citrus.service.form.CustomErrors;\nimport com.alibaba.citrus.service.form.Group;\nimport com.alibaba.citrus.turbine.Navigator;\nimport com.alibaba.citrus.turbine.dataresolver.FormField;\nimport com.alibaba.citrus.turbine.dataresolver.FormGroup;\nimport com.alibaba.citrus.turbine.dataresolver.Param;\nimport com.alibaba.citrus.webx.WebxException;\nimport com.alibaba.otter.canal.instance.manager.model.Canal;\nimport com.alibaba.otter.canal.instance.manager.model.CanalParameter;\nimport com.alibaba.otter.canal.instance.manager.model.CanalParameter.DataSourcing;\nimport com.alibaba.otter.canal.instance.manager.model.CanalParameter.SourcingType;\nimport com.alibaba.otter.manager.biz.common.exceptions.RepeatConfigureException;\nimport com.alibaba.otter.manager.biz.config.canal.CanalService;\nimport com.alibaba.otter.manager.biz.config.pipeline.PipelineService;\nimport com.alibaba.otter.manager.web.common.CanalExtraParamUtil;\nimport com.alibaba.otter.manager.web.common.WebConstant;\n\npublic class CanalAction extends AbstractAction {\n\n    @Resource(name = \"canalService\")\n    private CanalService    canalService;\n\n    @Resource(name = \"pipelineService\")\n    private PipelineService pipelineService;\n\n    /**\n     * 添加canal\n     */\n    public void doAdd(@FormGroup(\"canalInfo\") Group canalInfo,\n                      @FormGroup(\"canalParameterInfo\") Group canalParameterInfo,\n                      @FormField(name = \"formCanalError\", group = \"canalInfo\") CustomErrors err,\n                      @FormField(name = \"formHeartBeatError\", group = \"canalParameterInfo\") CustomErrors heartBeatErr,\n                      Navigator nav) throws Exception {\n        Canal canal = new Canal();\n        CanalParameter parameter = new CanalParameter();\n        canalInfo.setProperties(canal);\n        canalParameterInfo.setProperties(parameter);\n\n        String zkClustersString = canalParameterInfo.getField(\"zkClusters\").getStringValue();\n        String[] zkClusters = StringUtils.split(zkClustersString, \";\");\n        parameter.setZkClusters(Arrays.asList(zkClusters));\n\n        Long zkClusterId = canalParameterInfo.getField(\"autoKeeperClusterId\").getLongValue();\n        parameter.setZkClusterId(zkClusterId);\n        canal.setCanalParameter(parameter);\n\n        String dbAddressesString = canalParameterInfo.getField(\"groupDbAddresses\").getStringValue();\n        // 解析格式：\n        // 127.0.0.1:3306:MYSQL,127.0.0.1:3306:ORACLE;127.0.0.1:3306,127.0.0.1:3306;\n        // 第一层的分号代表主备概念，,第二层逗号代表分组概念\n        if (StringUtils.isNotEmpty(dbAddressesString)) {\n            List<List<DataSourcing>> dbSocketAddress = new ArrayList<List<DataSourcing>>();\n            String[] dbAddresses = StringUtils.split(dbAddressesString, \";\");\n            for (String dbAddressString : dbAddresses) {\n                List<DataSourcing> groupDbSocketAddress = new ArrayList<DataSourcing>();\n                String[] groupDbAddresses = StringUtils.split(dbAddressString, \",\");\n                for (String groupDbAddress : groupDbAddresses) {\n                    String strs[] = StringUtils.split(groupDbAddress, \":\");\n                    InetSocketAddress address = new InetSocketAddress(strs[0].trim(), Integer.valueOf(strs[1]));\n                    SourcingType type = parameter.getSourcingType();\n                    if (strs.length > 2) {\n                        type = SourcingType.valueOf(strs[2]);\n                    }\n                    groupDbSocketAddress.add(new DataSourcing(type, address));\n                }\n                dbSocketAddress.add(groupDbSocketAddress);\n            }\n\n            parameter.setGroupDbAddresses(dbSocketAddress);\n        }\n\n        CanalExtraParamUtil.setExtraParamString(parameter, canalParameterInfo);\n\n        String positionsString = canalParameterInfo.getField(\"positions\").getStringValue();\n        if (StringUtils.isNotEmpty(positionsString)) {\n            String positions[] = StringUtils.split(positionsString, \";\");\n            parameter.setPositions(Arrays.asList(positions));\n        }\n\n        if (parameter.getDetectingEnable() && StringUtils.startsWithIgnoreCase(parameter.getDetectingSQL(), \"select\")) {\n            heartBeatErr.setMessage(\"invaliedHeartBeat\");\n            return;\n        }\n\n        try {\n            canalService.create(canal);\n        } catch (RepeatConfigureException rce) {\n            err.setMessage(\"invalidCanal\");\n            return;\n        }\n\n        if (parameter.getSourcingType().isMysql() && parameter.getSlaveId() == null) {\n            parameter.setSlaveId(10000 + canal.getId());\n            // 再次更新一下slaveId\n            try {\n                canalService.modify(canal);\n            } catch (RepeatConfigureException rce) {\n                err.setMessage(\"invalidCanal\");\n                return;\n            }\n        }\n\n        nav.redirectTo(WebConstant.CANAL_LIST_LINK);\n    }\n\n    /**\n     * 修改canal\n     */\n    public void doEdit(@FormGroup(\"canalInfo\") Group canalInfo,\n                       @FormGroup(\"canalParameterInfo\") Group canalParameterInfo,\n                       @FormField(name = \"formCanalError\", group = \"canalInfo\") CustomErrors err,\n                       @FormField(name = \"formHeartBeatError\", group = \"canalParameterInfo\") CustomErrors heartBeatErr,\n                       Navigator nav) throws Exception {\n        Canal canal = new Canal();\n        CanalParameter parameter = new CanalParameter();\n        canalInfo.setProperties(canal);\n        canalParameterInfo.setProperties(parameter);\n\n        String zkClustersString = canalParameterInfo.getField(\"zkClusters\").getStringValue();\n        String[] zkClusters = StringUtils.split(zkClustersString, \";\");\n        parameter.setZkClusters(Arrays.asList(zkClusters));\n\n        Long zkClusterId = canalParameterInfo.getField(\"autoKeeperClusterId\").getLongValue();\n        parameter.setZkClusterId(zkClusterId);\n\n        String dbAddressesString = canalParameterInfo.getField(\"groupDbAddresses\").getStringValue();\n        if (StringUtils.isNotEmpty(dbAddressesString)) {\n            List<List<DataSourcing>> dbSocketAddress = new ArrayList<List<DataSourcing>>();\n            String[] dbAddresses = StringUtils.split(dbAddressesString, \";\");\n            for (String dbAddressString : dbAddresses) {\n                List<DataSourcing> groupDbSocketAddress = new ArrayList<DataSourcing>();\n                String[] groupDbAddresses = StringUtils.split(dbAddressString, \",\");\n                for (String groupDbAddress : groupDbAddresses) {\n                    String strs[] = StringUtils.split(groupDbAddress, \":\");\n                    InetSocketAddress address = new InetSocketAddress(strs[0].trim(), Integer.valueOf(strs[1]));\n                    SourcingType type = parameter.getSourcingType();\n                    if (strs.length > 2) {\n                        type = SourcingType.valueOf(strs[2]);\n                    }\n                    groupDbSocketAddress.add(new DataSourcing(type, address));\n                }\n                dbSocketAddress.add(groupDbSocketAddress);\n            }\n\n            parameter.setGroupDbAddresses(dbSocketAddress);\n        }\n\n        CanalExtraParamUtil.setExtraParamString(parameter, canalParameterInfo);\n\n        String positionsString = canalParameterInfo.getField(\"positions\").getStringValue();\n        if (StringUtils.isNotEmpty(positionsString)) {\n            String positions[] = StringUtils.split(positionsString, \";\");\n            parameter.setPositions(Arrays.asList(positions));\n        }\n\n        if (parameter.getDetectingEnable() && StringUtils.startsWithIgnoreCase(parameter.getDetectingSQL(), \"select\")) {\n            heartBeatErr.setMessage(\"invaliedHeartBeat\");\n            return;\n        }\n\n        canal.setCanalParameter(parameter);\n\n        try {\n            canalService.modify(canal);\n        } catch (RepeatConfigureException rce) {\n            err.setMessage(\"invalidCanal\");\n            return;\n        }\n\n        nav.redirectToLocation(\"canalList.htm\");\n    }\n\n    /**\n     * 删除canal\n     * \n     * @param canalId\n     * @throws WebxException\n     */\n    public void doDelete(@Param(\"canalId\") Long canalId, Navigator nav) throws WebxException {\n\n        canalService.remove(canalId);\n        nav.redirectToLocation(\"canalList.htm\");\n    }\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/home/module/action/ChannelAction.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.home.module.action;\n\nimport javax.annotation.Resource;\n\nimport com.alibaba.citrus.service.form.CustomErrors;\nimport com.alibaba.citrus.service.form.Group;\nimport com.alibaba.citrus.turbine.Navigator;\nimport com.alibaba.citrus.turbine.dataresolver.FormField;\nimport com.alibaba.citrus.turbine.dataresolver.FormGroup;\nimport com.alibaba.citrus.turbine.dataresolver.Param;\nimport com.alibaba.citrus.webx.WebxException;\nimport com.alibaba.otter.manager.biz.common.exceptions.InvalidConfigureException;\nimport com.alibaba.otter.manager.biz.common.exceptions.ManagerException;\nimport com.alibaba.otter.manager.biz.common.exceptions.RepeatConfigureException;\nimport com.alibaba.otter.manager.biz.config.channel.ChannelService;\nimport com.alibaba.otter.manager.biz.config.pipeline.PipelineService;\nimport com.alibaba.otter.manager.web.common.WebConstant;\nimport com.alibaba.otter.shared.common.model.config.channel.Channel;\nimport com.alibaba.otter.shared.common.model.config.channel.ChannelParameter;\nimport com.alibaba.otter.shared.common.model.config.channel.ChannelStatus;\n\n/**\n * 类ChannelAction.java的实现描述：用于Channel管理界面的Action\n * \n * @author simon 2011-10-21 下午02:49:18\n */\npublic class ChannelAction extends AbstractAction {\n\n    @Resource(name = \"channelService\")\n    private ChannelService  channelService;\n\n    @Resource(name = \"pipelineService\")\n    private PipelineService pipelineService;\n\n    /**\n     * 添加Channel\n     * \n     * @param channelInfo\n     * @param channelParameterInfo\n     * @throws Exception\n     */\n    public void doAdd(@FormGroup(\"channelInfo\") Group channelInfo,\n                      @FormGroup(\"channelParameterInfo\") Group channelParameterInfo,\n                      @FormField(name = \"formChannelError\", group = \"channelInfo\") CustomErrors err, Navigator nav)\n                                                                                                                   throws Exception {\n        Channel channel = new Channel();\n        ChannelParameter parameter = new ChannelParameter();\n        channelInfo.setProperties(channel);\n        channelParameterInfo.setProperties(parameter);\n        // 新建Channel默认关闭该状态\n        channel.setStatus(ChannelStatus.STOP);\n        channel.setParameters(parameter);\n        try {\n            channelService.create(channel);\n        } catch (RepeatConfigureException rce) {\n            err.setMessage(\"invalidChannelName\");\n            return;\n        }\n        nav.redirectTo(WebConstant.CHANNEL_LIST_LINK);\n    }\n\n    /**\n     * 修改Channel\n     * \n     * @param channelInfo\n     * @param channelParameterInfo\n     * @throws Exception\n     */\n    public void doEdit(@FormGroup(\"channelInfo\") Group channelInfo, @Param(\"pageIndex\") int pageIndex,\n                       @Param(\"searchKey\") String searchKey,\n                       @FormGroup(\"channelParameterInfo\") Group channelParameterInfo,\n                       @FormField(name = \"formChannelError\", group = \"channelInfo\") CustomErrors err, Navigator nav)\n                                                                                                                    throws Exception {\n        Channel channel = new Channel();\n        ChannelParameter parameter = new ChannelParameter();\n        channelInfo.setProperties(channel);\n        channelParameterInfo.setProperties(parameter);\n        channel.setStatus(channelService.findById(channel.getId()).getStatus());\n        parameter.setChannelId(channel.getId());\n        channel.setParameters(parameter);\n        try {\n            channelService.modify(channel);\n        } catch (RepeatConfigureException rce) {\n            err.setMessage(\"invalidChannelName\");\n            return;\n        }\n\n        nav.redirectToLocation(\"channelList.htm?pageIndex=\" + pageIndex + \"&searchKey=\" + urlEncode(searchKey));\n    }\n\n    /**\n     * 删除Channel\n     * \n     * @param channelId\n     * @throws WebxException\n     */\n    public void doDelete(@Param(\"channelId\") Long channelId, @Param(\"pageIndex\") int pageIndex,\n                         @Param(\"searchKey\") String searchKey, Navigator nav) throws WebxException {\n        if (channelService.findById(channelId).getStatus().isStart()) {\n            nav.redirectTo(WebConstant.ERROR_FORBIDDEN_Link);\n            return;\n        }\n\n        // 如果channel节点下面还有关联的pipeline时，不允许删除\n        if (pipelineService.listByChannelIds(channelId).size() < 1) {\n            channelService.remove(channelId);\n        }\n\n        nav.redirectToLocation(\"channelList.htm?pageIndex=\" + pageIndex + \"&searchKey=\" + urlEncode(searchKey));\n    }\n\n    /**\n     * 用于Channel运行状态的更新操作\n     * \n     * @param channelId\n     * @param status\n     * @throws WebxException\n     */\n    public void doStatus(@Param(\"pageIndex\") int pageIndex, @Param(\"searchKey\") String searchKey,\n                         @Param(\"channelId\") Long channelId, @Param(\"status\") String status, Navigator nav)\n                                                                                                           throws WebxException {\n        String errorType = null;\n        if (status.equals(\"start\")) {\n            try {\n                channelService.startChannel(channelId);\n            } catch (ManagerException e) {\n                if (e.getCause() instanceof InvalidConfigureException) {\n                    errorType = ((InvalidConfigureException) e.getCause()).getType().name();\n                }\n            } catch (InvalidConfigureException rce) {\n                errorType = rce.getType().name();\n            }\n\n        } else if (status.equals(\"stop\")) {\n            channelService.stopChannel(channelId);\n        }\n\n        if (errorType != null) {\n            nav.redirectToLocation(\"channelList.htm?pageIndex=\" + pageIndex + \"&searchKey=\" + urlEncode(searchKey)\n                                   + \"&errorType=\" + errorType);\n        } else {\n            nav.redirectToLocation(\"channelList.htm?pageIndex=\" + pageIndex + \"&searchKey=\" + urlEncode(searchKey));\n        }\n    }\n\n    /**\n     * 用于Channel的配置强制推送\n     * \n     * @param channelId\n     * @param status\n     * @throws WebxException\n     */\n    public void doNotify(@Param(\"pageIndex\") int pageIndex, @Param(\"searchKey\") String searchKey,\n                         @Param(\"channelId\") Long channelId, @Param(\"status\") String status, Navigator nav)\n                                                                                                           throws WebxException {\n\n        channelService.notifyChannel(channelId);\n        nav.redirectToLocation(\"channelList.htm?pageIndex=\" + pageIndex + \"&searchKey=\" + urlEncode(searchKey));\n    }\n\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/home/module/action/ColumnPairAction.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.home.module.action;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport javax.annotation.Resource;\n\nimport com.alibaba.citrus.service.form.CustomErrors;\nimport com.alibaba.citrus.service.form.Group;\nimport com.alibaba.citrus.turbine.Navigator;\nimport com.alibaba.citrus.turbine.dataresolver.FormField;\nimport com.alibaba.citrus.turbine.dataresolver.FormGroup;\nimport com.alibaba.citrus.turbine.dataresolver.Param;\nimport com.alibaba.dubbo.common.utils.StringUtils;\nimport com.alibaba.otter.manager.biz.config.datacolumnpair.DataColumnPairService;\nimport com.alibaba.otter.manager.biz.config.datamediapair.DataMediaPairService;\nimport com.alibaba.otter.shared.common.model.config.data.Column;\nimport com.alibaba.otter.shared.common.model.config.data.ColumnPair;\nimport com.alibaba.otter.shared.common.model.config.data.DataMedia;\n\npublic class ColumnPairAction extends AbstractAction {\n\n    @Resource(name = \"dataColumnPairService\")\n    private DataColumnPairService dataColumnPairService;\n\n    @Resource(name = \"dataMediaPairService\")\n    private DataMediaPairService  dataMediaPairService;\n\n    /**\n     * 添加Channel\n     * \n     * @param channelInfo\n     * @param channelParameterInfo\n     * @throws Exception\n     */\n    public void doSave(@Param(\"dataMediaPairId\") Long dataMediaPairId, @Param(\"submitKey\") String submitKey,\n                       @Param(\"channelId\") Long channelId, @Param(\"pipelineId\") Long pipelineId,\n                       @Param(\"sourceMediaId\") Long sourceMediaId, @Param(\"targetMediaId\") Long targetMediaId,\n                       @FormGroup(\"columnPairInfo\") Group columnPairInfo,\n                       @FormField(name = \"formColumnPairError\", group = \"columnPairInfo\") CustomErrors err,\n                       Navigator nav) throws Exception {\n        String[] sourceColumns = columnPairInfo.getField(\"dltTarget_l\").getStringValues();\n        String[] targetColumns = columnPairInfo.getField(\"dltTarget_r\").getStringValues();\n        List<String> sourceColumnNames = new ArrayList<String>();\n        List<String> targetColumnNames = new ArrayList<String>();\n        for (String sourceColumn : sourceColumns) {\n            sourceColumnNames.add(sourceColumn);\n        }\n\n        for (String targetColumn : targetColumns) {\n            targetColumnNames.add(targetColumn);\n        }\n\n        DataMedia targetMedia = dataMediaPairService.findById(dataMediaPairId).getTarget();\n\n        if (!targetMedia.getSource().getType().isNapoli() && sourceColumnNames.size() != targetColumnNames.size()) {\n            err.setMessage(\"invalidColumnPair\");\n            return;\n        }\n        List<ColumnPair> columnPairsInDb = dataColumnPairService.listByDataMediaPairId(dataMediaPairId);\n        List<ColumnPair> columnPairsTemp = new ArrayList<ColumnPair>();\n        List<String> columnPairsNameSource = new ArrayList<String>();\n        List<String> columnPairsNameTarget = new ArrayList<String>();\n        List<ColumnPair> columnPairs = new ArrayList<ColumnPair>();\n\n        if (targetMedia.getSource().getType().isNapoli()) {\n            for (ColumnPair columnPair : columnPairsInDb) {\n                for (String sourceColumnName : sourceColumnNames) {\n                    if (StringUtils.isEquals(columnPair.getSourceColumn().getName(), sourceColumnName)) {\n                        columnPairsTemp.add(columnPair);\n                        columnPairsNameSource.add(sourceColumnName);\n                    }\n                }\n            }\n            // 要从数据库中删除这些columnPair\n            columnPairsInDb.removeAll(columnPairsTemp);\n            sourceColumnNames.removeAll(columnPairsNameSource);\n\n            for (String columnName : sourceColumnNames) {\n                ColumnPair columnPair = new ColumnPair();\n                columnPair.setSourceColumn(new Column(columnName));\n                columnPair.setDataMediaPairId(dataMediaPairId);\n                columnPairs.add(columnPair);\n            }\n        } else if (targetMedia.getSource().getType().isMysql() || targetMedia.getSource().getType().isOracle()) {\n            for (ColumnPair columnPair : columnPairsInDb) {\n                int i = 0;\n                for (String sourceColumnName : sourceColumnNames) {\n                    if (StringUtils.isEquals(columnPair.getSourceColumn().getName(), sourceColumnName)\n                        && StringUtils.isEquals(columnPair.getTargetColumn().getName(), targetColumnNames.get(i))) {\n                        columnPairsTemp.add(columnPair);\n                        columnPairsNameSource.add(sourceColumnName);\n                        columnPairsNameTarget.add(targetColumnNames.get(i));\n                    }\n                    i++;\n                }\n            }\n            // 要从数据库中删除这些columnPair\n            columnPairsInDb.removeAll(columnPairsTemp);\n            sourceColumnNames.removeAll(columnPairsNameSource);\n            targetColumnNames.removeAll(columnPairsNameTarget);\n\n            int i = 0;\n            for (String columnName : sourceColumnNames) {\n                ColumnPair columnPair = new ColumnPair();\n                columnPair.setSourceColumn(new Column(columnName));\n                columnPair.setTargetColumn(new Column(targetColumnNames.get(i)));\n                columnPair.setDataMediaPairId(dataMediaPairId);\n                columnPairs.add(columnPair);\n                i++;\n            }\n        }\n\n        for (ColumnPair columnPair : columnPairsInDb) {\n            dataColumnPairService.remove(columnPair.getId());\n        }\n\n        dataColumnPairService.createBatch(columnPairs);\n\n        if (submitKey.equals(\"保存\")) {\n            nav.redirectToLocation(\"dataMediaPairList.htm?pipelineId=\" + pipelineId);\n        } else if (submitKey.equals(\"下一步\")) {\n            nav.redirectToLocation(\"addColumnPairGroup.htm?dataMediaPairId=\" + dataMediaPairId + \"&channelId=\"\n                                   + channelId + \"&pipelineId=\" + pipelineId + \"&sourceMediaId=\" + sourceMediaId\n                                   + \"&targetMediaId=\" + targetMediaId);\n        }\n    }\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/home/module/action/ColumnPairGroupAction.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.home.module.action;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport javax.annotation.Resource;\n\nimport com.alibaba.citrus.service.form.CustomErrors;\nimport com.alibaba.citrus.service.form.Group;\nimport com.alibaba.citrus.turbine.Navigator;\nimport com.alibaba.citrus.turbine.dataresolver.FormField;\nimport com.alibaba.citrus.turbine.dataresolver.FormGroup;\nimport com.alibaba.citrus.turbine.dataresolver.Param;\nimport com.alibaba.otter.manager.biz.config.datacolumnpair.DataColumnPairGroupService;\nimport com.alibaba.otter.shared.common.model.config.data.Column;\nimport com.alibaba.otter.shared.common.model.config.data.ColumnGroup;\nimport com.alibaba.otter.shared.common.model.config.data.ColumnPair;\n\n/**\n * 类ColumnPairGroupAction.java的实现描述：TODO 类实现描述\n * \n * @author simon 2012-4-24 下午4:37:18\n */\npublic class ColumnPairGroupAction {\n\n    private static final String        COLON = \":\";\n\n    @Resource(name = \"dataColumnPairGroupService\")\n    private DataColumnPairGroupService dataColumnPairGroupService;\n\n    public void doSave(@Param(\"dataMediaPairId\") Long dataMediaPairId, @Param(\"submitKey\") String submitKey,\n                       @Param(\"pipelineId\") Long pipelineId,\n                       @FormGroup(\"columnPairGroupInfo\") Group columnPairGroupInfo,\n                       @FormField(name = \"formColumnPairGroupError\", group = \"columnPairGroupInfo\") CustomErrors err,\n                       Navigator nav) throws Exception {\n        String[] columnPairStrings = columnPairGroupInfo.getField(\"groupResult\").getStringValues();\n        ColumnGroup columnGroup = new ColumnGroup();\n        List<ColumnPair> columnPairs = new ArrayList<ColumnPair>();\n        for (String columnPairString : columnPairStrings) {\n            ColumnPair columnPair = new ColumnPair();\n            String[] temp = columnPairString.split(COLON);\n            columnPair.setSourceColumn(new Column(temp[0]));\n            columnPair.setTargetColumn(new Column(temp[1]));\n            columnPair.setDataMediaPairId(dataMediaPairId);\n            columnPairs.add(columnPair);\n        }\n\n        columnGroup.setColumnPairs(columnPairs);\n        columnGroup.setDataMediaPairId(dataMediaPairId);\n\n        dataColumnPairGroupService.removeByDataMediaPairId(dataMediaPairId);\n        dataColumnPairGroupService.create(columnGroup);\n\n        nav.redirectToLocation(\"dataMediaPairList.htm?pipelineId=\" + pipelineId);\n    }\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/home/module/action/DataMatrixAction.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.home.module.action;\n\nimport javax.annotation.Resource;\n\nimport org.apache.commons.lang.StringUtils;\n\nimport com.alibaba.citrus.service.form.CustomErrors;\nimport com.alibaba.citrus.service.form.Group;\nimport com.alibaba.citrus.turbine.Navigator;\nimport com.alibaba.citrus.turbine.dataresolver.FormField;\nimport com.alibaba.citrus.turbine.dataresolver.FormGroup;\nimport com.alibaba.citrus.turbine.dataresolver.Param;\nimport com.alibaba.citrus.webx.WebxException;\nimport com.alibaba.otter.manager.biz.common.exceptions.RepeatConfigureException;\nimport com.alibaba.otter.manager.biz.config.datamatrix.DataMatrixService;\nimport com.alibaba.otter.manager.web.common.WebConstant;\nimport com.alibaba.otter.shared.common.model.config.data.DataMatrix;\n\npublic class DataMatrixAction extends AbstractAction {\n\n    @Resource(name = \"dataMatrixService\")\n    private DataMatrixService dataMatrixService;\n\n    public void doAdd(@FormGroup(\"dataMatrixInfo\") Group dataMatrixInfo,\n                      @FormField(name = \"formDataMatrixError\", group = \"dataMatrixInfo\") CustomErrors err, Navigator nav)\n                                                                                                                         throws Exception {\n        DataMatrix matrix = new DataMatrix();\n        dataMatrixInfo.setProperties(matrix);\n\n        try {\n            dataMatrixService.create(matrix);\n        } catch (RepeatConfigureException rce) {\n            err.setMessage(\"invalidDataMatrix\");\n            return;\n        }\n\n        nav.redirectTo(WebConstant.MATRIX_LIST_LINK);\n    }\n\n    public void doEdit(@FormGroup(\"dataMatrixInfo\") Group dataMatrixInfo,\n                       @FormField(name = \"formDataMatrixError\", group = \"dataMatrixInfo\") CustomErrors err,\n                       Navigator nav) throws Exception {\n        DataMatrix matrix = new DataMatrix();\n        dataMatrixInfo.setProperties(matrix);\n\n        try {\n            dataMatrixService.modify(matrix);\n        } catch (RepeatConfigureException rce) {\n            err.setMessage(\"invalidDataMatrix\");\n            return;\n        }\n\n        nav.redirectToLocation(\"dataMatrixList.htm?matrixId=\" + matrix.getId());\n    }\n\n    public void doDelete(@Param(\"matrixId\") Long matrixId, Navigator nav) throws WebxException {\n        dataMatrixService.remove(matrixId);\n        nav.redirectToLocation(\"dataMatrixList.htm\");\n    }\n\n    public void doSwitch(@Param(\"matrixId\") Long matrixId, Navigator nav) throws WebxException {\n        DataMatrix matrix = dataMatrixService.findById(matrixId);\n        String slave = matrix.getMaster();\n        String master = matrix.getSlave();\n        if (StringUtils.isNotEmpty(master) && StringUtils.isNotEmpty(slave)) {\n            matrix.setMaster(master);\n            matrix.setSlave(slave);\n        }\n\n        dataMatrixService.modify(matrix);\n        nav.redirectToLocation(\"dataMatrixList.htm?matrixId=\" + matrixId);\n    }\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/home/module/action/DataMediaAction.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.home.module.action;\n\nimport javax.annotation.Resource;\n\nimport com.alibaba.citrus.service.form.CustomErrors;\nimport com.alibaba.citrus.service.form.Group;\nimport com.alibaba.citrus.turbine.Navigator;\nimport com.alibaba.citrus.turbine.dataresolver.FormField;\nimport com.alibaba.citrus.turbine.dataresolver.FormGroup;\nimport com.alibaba.citrus.turbine.dataresolver.Param;\nimport com.alibaba.citrus.webx.WebxException;\nimport com.alibaba.otter.manager.biz.common.exceptions.RepeatConfigureException;\nimport com.alibaba.otter.manager.biz.config.datamedia.DataMediaService;\nimport com.alibaba.otter.manager.biz.config.datamediapair.DataMediaPairService;\nimport com.alibaba.otter.manager.biz.config.datamediasource.DataMediaSourceService;\nimport com.alibaba.otter.manager.web.common.WebConstant;\nimport com.alibaba.otter.shared.common.model.config.data.DataMedia;\nimport com.alibaba.otter.shared.common.model.config.data.DataMediaSource;\nimport com.alibaba.otter.shared.common.model.config.data.db.DbMediaSource;\nimport com.alibaba.otter.shared.common.model.config.data.mq.MqMediaSource;\n\npublic class DataMediaAction extends AbstractAction {\n\n    @Resource(name = \"dataMediaService\")\n    private DataMediaService       dataMediaService;\n\n    @Resource(name = \"dataMediaPairService\")\n    private DataMediaPairService   dataMediaPairService;\n\n    @Resource(name = \"dataMediaSourceService\")\n    private DataMediaSourceService dataMediaSourceService;\n\n    /**\n     * 添加Channel\n     * \n     * @param channelInfo\n     * @param channelParameterInfo\n     * @throws Exception\n     */\n    public void doAdd(@FormGroup(\"dataMediaInfo\") Group dataMediaInfo,\n                      @FormField(name = \"formDataMediaError\", group = \"dataMediaInfo\") CustomErrors err, Navigator nav)\n                                                                                                                       throws Exception {\n\n        DataMedia dataMedia = new DataMedia();\n        dataMediaInfo.setProperties(dataMedia);\n        DataMediaSource dataMediaSource = dataMediaSourceService.findById(dataMediaInfo.getField(\"sourceId\").getLongValue());\n        if (dataMediaSource.getType().isMysql() || dataMediaSource.getType().isOracle()) {\n            dataMedia.setSource((DbMediaSource) dataMediaSource);\n        } else if (dataMediaSource.getType().isNapoli() || dataMediaSource.getType().isMq()) {\n            dataMedia.setSource((MqMediaSource) dataMediaSource);\n        }\n\n        try {\n            dataMediaService.create(dataMedia);\n        } catch (RepeatConfigureException rce) {\n            err.setMessage(\"invalidDataMedia\");\n            return;\n        }\n\n        nav.redirectTo(WebConstant.DATA_MEDIA_LIST_LINK);\n    }\n\n    /**\n     * @param channelId\n     * @throws WebxException\n     */\n    public void doDelete(@Param(\"dataMediaId\") Long dataMediaId, @Param(\"pageIndex\") int pageIndex,\n                         @Param(\"searchKey\") String searchKey, Navigator nav) throws WebxException {\n        if (dataMediaPairService.listByDataMediaId(dataMediaId).size() < 1) {\n            dataMediaService.remove(dataMediaId);\n        }\n        nav.redirectToLocation(\"dataMediaList.htm?pageIndex=\" + pageIndex + \"&searchKey=\" + urlEncode(searchKey));\n    }\n\n    public void doEdit(@FormGroup(\"dataMediaInfo\") Group dataMediaInfo, @Param(\"pageIndex\") int pageIndex,\n                       @Param(\"searchKey\") String searchKey,\n                       @FormField(name = \"formDataMediaError\", group = \"dataMediaInfo\") CustomErrors err, Navigator nav)\n                                                                                                                        throws Exception {\n        DataMedia dataMedia = new DataMedia();\n        dataMediaInfo.setProperties(dataMedia);\n        DataMediaSource dataMediaSource = dataMediaSourceService.findById(dataMediaInfo.getField(\"sourceId\").getLongValue());\n        if (dataMediaSource.getType().isMysql() || dataMediaSource.getType().isOracle()) {\n            dataMedia.setSource((DbMediaSource) dataMediaSource);\n        } else if (dataMediaSource.getType().isNapoli() || dataMediaSource.getType().isMq()) {\n            dataMedia.setSource((MqMediaSource) dataMediaSource);\n        }\n\n        try {\n            dataMediaService.modify(dataMedia);\n        } catch (RepeatConfigureException rce) {\n            err.setMessage(\"invalidDataMedia\");\n            return;\n        }\n        nav.redirectToLocation(\"dataMediaList.htm?pageIndex=\" + pageIndex + \"&searchKey=\" + urlEncode(searchKey));\n    }\n    /*\n     * private boolean checkDataMedia(DbDataMedia dbDataMedia) { Connection conn\n     * = null; Statement stmt = null; ResultSet rs = null; try { conn =\n     * DriverManager.getConnection(dbDataMedia.getSource().getUrl(),\n     * dbDataMedia.getSource().getUsername(),\n     * dbDataMedia.getSource().getPassword()); if (null == conn) { return false;\n     * } stmt = conn.createStatement(); rs = stmt.executeQuery(\"SELECT * FROM \"\n     * + dbDataMedia.getNamespace() + \".\" + dbDataMedia.getName() +\n     * \" where 0 = 1\"); } catch (SQLException se) { return false; } finally {\n     * try { if (null != rs) { rs.close(); } if (null != stmt) { stmt.close(); }\n     * if (null != conn) { conn.close(); } } catch (SQLException e) {\n     * e.printStackTrace(); } } return true; }\n     */\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/home/module/action/DataMediaPairAction.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.home.module.action;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport javax.annotation.Resource;\n\nimport org.apache.commons.lang.StringUtils;\n\nimport com.alibaba.citrus.service.form.CustomErrors;\nimport com.alibaba.citrus.service.form.Group;\nimport com.alibaba.citrus.turbine.Navigator;\nimport com.alibaba.citrus.turbine.dataresolver.FormField;\nimport com.alibaba.citrus.turbine.dataresolver.FormGroup;\nimport com.alibaba.citrus.turbine.dataresolver.Param;\nimport com.alibaba.citrus.webx.WebxException;\nimport com.alibaba.otter.manager.biz.common.exceptions.ManagerException;\nimport com.alibaba.otter.manager.biz.common.exceptions.RepeatConfigureException;\nimport com.alibaba.otter.manager.biz.config.channel.ChannelService;\nimport com.alibaba.otter.manager.biz.config.datamedia.DataMediaService;\nimport com.alibaba.otter.manager.biz.config.datamediapair.DataMediaPairService;\nimport com.alibaba.otter.manager.biz.config.datamediasource.DataMediaSourceService;\nimport com.alibaba.otter.manager.web.common.WebConstant;\nimport com.alibaba.otter.shared.common.model.config.channel.Channel;\nimport com.alibaba.otter.shared.common.model.config.data.DataMedia;\nimport com.alibaba.otter.shared.common.model.config.data.DataMediaPair;\nimport com.alibaba.otter.shared.common.model.config.data.DataMediaSource;\nimport com.alibaba.otter.shared.common.model.config.data.ExtensionData;\nimport com.alibaba.otter.shared.common.model.config.data.ExtensionDataType;\n\npublic class DataMediaPairAction {\n\n    @Resource(name = \"dataMediaPairService\")\n    private DataMediaPairService   dataMediaPairService;\n\n    @Resource(name = \"dataMediaService\")\n    private DataMediaService       dataMediaService;\n\n    @Resource(name = \"dataMediaSourceService\")\n    private DataMediaSourceService dataMediaSourceService;\n\n    @Resource(name = \"channelService\")\n    private ChannelService         channelService;\n\n    /**\n     * 添加DataMediaPair\n     * \n     * @param channelInfo\n     * @param channelParameterInfo\n     * @throws Exception\n     */\n    public void doAdd(@Param(\"submitKey\") String submitKey, @FormGroup(\"dataMediaPairInfo\") Group dataMediaPairInfo,\n                      @FormField(name = \"formDataMediaPairError\", group = \"dataMediaPairInfo\") CustomErrors err,\n                      Navigator nav) throws Exception {\n        DataMediaPair dataMediaPair = new DataMediaPair();\n        DataMedia sourceDataMedia = new DataMedia();\n        DataMedia targetDataMedia = new DataMedia();\n        dataMediaPairInfo.setProperties(dataMediaPair);\n\n        // filter解析\n        ExtensionDataType filterType = ExtensionDataType.valueOf(dataMediaPairInfo.getField(\"filterType\").getStringValue());\n        ExtensionData filterData = new ExtensionData();\n        filterData.setExtensionDataType(filterType);\n        if (filterType.isClazz()) {\n            filterData.setClazzPath(dataMediaPairInfo.getField(\"filterText\").getStringValue());\n        } else if (filterType.isSource()) {\n            filterData.setSourceText(dataMediaPairInfo.getField(\"filterText\").getStringValue());\n        }\n        dataMediaPair.setFilterData(filterData);\n\n        // fileresovler解析\n        ExtensionDataType resolverType = ExtensionDataType.valueOf(dataMediaPairInfo.getField(\"resolverType\").getStringValue());\n        ExtensionData resolverData = new ExtensionData();\n        resolverData.setExtensionDataType(resolverType);\n        if (resolverType.isClazz()) {\n            resolverData.setClazzPath(dataMediaPairInfo.getField(\"resolverText\").getStringValue());\n        } else if (resolverType.isSource()) {\n            resolverData.setSourceText(dataMediaPairInfo.getField(\"resolverText\").getStringValue());\n        }\n        dataMediaPair.setResolverData(resolverData);\n        sourceDataMedia.setId(dataMediaPairInfo.getField(\"sourceDataMediaId\").getLongValue());\n        dataMediaPair.setSource(sourceDataMedia);\n        targetDataMedia.setId(dataMediaPairInfo.getField(\"targetDataMediaId\").getLongValue());\n        dataMediaPair.setTarget(targetDataMedia);\n        Long id = 0L;\n        try {\n            id = dataMediaPairService.createAndReturnId(dataMediaPair);\n        } catch (RepeatConfigureException rce) {\n            err.setMessage(\"invalidDataMediaPair\");\n            return;\n        }\n        if (submitKey.equals(\"保存\")) {\n            nav.redirectToLocation(\"dataMediaPairList.htm?pipelineId=\" + dataMediaPair.getPipelineId());\n        } else if (submitKey.equals(\"下一步\")) {\n            nav.redirectToLocation(\"addColumnPair.htm?dataMediaPairId=\" + id + \"&pipelineId=\"\n                                   + dataMediaPair.getPipelineId() + \"&dataMediaPairId=\" + id + \"&sourceMediaId=\"\n                                   + sourceDataMedia.getId() + \"&targetMediaId=\" + targetDataMedia.getId());\n        }\n    }\n\n    /**\n     * 批量添加DataMediaPair\n     * \n     * @param dataMediaPairInfo\n     * @throws Exception\n     */\n    public void doBatchAdd(@FormGroup(\"batchDataMediaPairInfo\") Group batchDataMediaPairInfo,\n                           @Param(\"pipelineId\") Long pipelineId,\n                           @FormField(name = \"formBatchDataMediaPairError\", group = \"batchDataMediaPairInfo\") CustomErrors err,\n                           Navigator nav) throws Exception {\n        String batchPairContent = batchDataMediaPairInfo.getField(\"batchPairContent\").getStringValue();\n        List<String> StringPairs = Arrays.asList(batchPairContent.split(\"\\r\\n\"));\n        try {\n            for (String stringPair : StringPairs) {\n                List<String> pairData = Arrays.asList(stringPair.split(\",\"));\n                if (pairData.size() < 4) {\n                    throw new ManagerException(\"[\" + stringPair + \"] the line not all parameters\");\n                }\n                // build the pair source\n                DataMedia sourceDataMedia = new DataMedia();\n                DataMediaSource sourceDataMediaSource = dataMediaSourceService.findById(Long.parseLong(StringUtils.trimToNull(pairData.get(2))));\n                sourceDataMedia.setNamespace(StringUtils.trimToNull(pairData.get(0)));\n                sourceDataMedia.setName(StringUtils.trimToNull(pairData.get(1)));\n                sourceDataMedia.setSource(sourceDataMediaSource);\n                Long sourceMediaId = dataMediaService.createReturnId(sourceDataMedia);\n                sourceDataMedia.setId(sourceMediaId);\n                // build the pair target\n                DataMedia targetDataMedia = new DataMedia();\n                Long weight = 5L;\n                if (StringUtils.isNumeric(pairData.get(3)) && pairData.size() <= 5) {// 如果是纯数字，那说明是简化配置模式\n                    DataMediaSource targetDataMediaSource = dataMediaSourceService.findById(Long.parseLong(StringUtils.trimToNull(pairData.get(3))));\n                    targetDataMedia.setNamespace(StringUtils.trimToNull(pairData.get(0)));\n                    targetDataMedia.setName(StringUtils.trimToNull(pairData.get(1)));\n                    targetDataMedia.setSource(targetDataMediaSource);\n                    Long targetMediaId = dataMediaService.createReturnId(targetDataMedia);\n                    targetDataMedia.setId(targetMediaId);\n\n                    if (pairData.size() >= 5) {\n                        weight = Long.parseLong(StringUtils.trimToNull(pairData.get(4)));\n                    }\n                } else {\n                    DataMediaSource targetDataMediaSource = dataMediaSourceService.findById(Long.parseLong(StringUtils.trimToNull(pairData.get(5))));\n                    targetDataMedia.setNamespace(StringUtils.trimToNull(pairData.get(3)));\n                    targetDataMedia.setName(StringUtils.trimToNull(pairData.get(4)));\n                    targetDataMedia.setSource(targetDataMediaSource);\n                    Long targetMediaId = dataMediaService.createReturnId(targetDataMedia);\n                    targetDataMedia.setId(targetMediaId);\n\n                    if (pairData.size() >= 7) {\n                        weight = Long.parseLong(StringUtils.trimToNull(pairData.get(6)));\n                    }\n                }\n\n                // build the pair\n                DataMediaPair dataMediaPair = new DataMediaPair();\n                dataMediaPair.setSource(sourceDataMedia);\n                dataMediaPair.setTarget(targetDataMedia);\n                dataMediaPair.setPushWeight(weight);\n                dataMediaPair.setPipelineId(pipelineId);\n\n                dataMediaPairService.createIfNotExist(dataMediaPair);\n            }\n        } catch (Exception e) {\n            err.setMessage(\"invalidBatchDataMediaPair\");\n            return;\n        }\n        nav.redirectToLocation(\"dataMediaPairList.htm?pipelineId=\" + pipelineId);\n    }\n\n    public void doEdit(@Param(\"submitKey\") String submitKey, @Param(\"channelId\") Long channelId,\n                       @FormGroup(\"dataMediaPairInfo\") Group dataMediaPairInfo,\n                       @FormField(name = \"formDataMediaPairError\", group = \"dataMediaPairInfo\") CustomErrors err,\n                       Navigator nav) throws Exception {\n        DataMediaPair dataMediaPair = new DataMediaPair();\n        DataMedia sourceDataMedia = new DataMedia();\n        DataMedia targetDataMedia = new DataMedia();\n        dataMediaPairInfo.setProperties(dataMediaPair);\n\n        // filter解析\n        ExtensionDataType filterType = ExtensionDataType.valueOf(dataMediaPairInfo.getField(\"filterType\").getStringValue());\n        ExtensionData filterData = new ExtensionData();\n        filterData.setExtensionDataType(filterType);\n        if (filterType.isClazz()) {\n            filterData.setClazzPath(dataMediaPairInfo.getField(\"filterText\").getStringValue());\n        } else if (filterType.isSource()) {\n            filterData.setSourceText(dataMediaPairInfo.getField(\"filterText\").getStringValue());\n        }\n        dataMediaPair.setFilterData(filterData);\n\n        // fileresovler解析\n        ExtensionDataType resolverType = ExtensionDataType.valueOf(dataMediaPairInfo.getField(\"resolverType\").getStringValue());\n        ExtensionData resolverData = new ExtensionData();\n        resolverData.setExtensionDataType(resolverType);\n        if (resolverType.isClazz()) {\n            resolverData.setClazzPath(dataMediaPairInfo.getField(\"resolverText\").getStringValue());\n        } else if (resolverType.isSource()) {\n            resolverData.setSourceText(dataMediaPairInfo.getField(\"resolverText\").getStringValue());\n        }\n        dataMediaPair.setResolverData(resolverData);\n\n        sourceDataMedia.setId(dataMediaPairInfo.getField(\"sourceDataMediaId\").getLongValue());\n        dataMediaPair.setSource(sourceDataMedia);\n        targetDataMedia.setId(dataMediaPairInfo.getField(\"targetDataMediaId\").getLongValue());\n        dataMediaPair.setTarget(targetDataMedia);\n        try {\n            dataMediaPairService.modify(dataMediaPair);\n\n        } catch (RepeatConfigureException rce) {\n            err.setMessage(\"invalidDataMediaPair\");\n            return;\n        }\n\n        if (submitKey.equals(\"保存\")) {\n            nav.redirectToLocation(\"dataMediaPairList.htm?pipelineId=\" + dataMediaPair.getPipelineId());\n        } else if (submitKey.equals(\"下一步\")) {\n            nav.redirectToLocation(\"addColumnPair.htm?pipelineId=\" + dataMediaPair.getPipelineId() + \"&channelId=\"\n                                   + channelId + \"&dataMediaPairId=\" + dataMediaPair.getId() + \"&sourceMediaId=\"\n                                   + sourceDataMedia.getId() + \"&targetMediaId=\" + targetDataMedia.getId());\n        }\n    }\n\n    /**\n     * 删除映射关系\n     */\n    public void doDelete(@Param(\"dataMediaPairId\") Long dataMediaPairId, @Param(\"pipelineId\") Long pipelineId,\n                         Navigator nav) throws WebxException {\n        Channel channel = channelService.findByPipelineId(pipelineId);\n        if (channel.getStatus().isStart()) {\n            nav.redirectTo(WebConstant.ERROR_FORBIDDEN_Link);\n            return;\n        }\n        dataMediaPairService.remove(dataMediaPairId);\n        nav.redirectToLocation(\"dataMediaPairList.htm?pipelineId=\" + pipelineId);\n    }\n\n    /**\n     * 选择视图同步\n     * \n     * @param channelInfo\n     * @param channelParameterInfo\n     * @throws Exception\n     */\n    public void doNextToView(@FormGroup(\"dataMediaPairInfo\") Group dataMediaPairInfo,\n                             @FormField(name = \"formDataMediaPairError\", group = \"dataMediaPairInfo\") CustomErrors err,\n                             Navigator nav) throws Exception {\n        DataMediaPair dataMediaPair = new DataMediaPair();\n        DataMedia sourceDataMedia = new DataMedia();\n        DataMedia targetDataMedia = new DataMedia();\n        dataMediaPairInfo.setProperties(dataMediaPair);\n        sourceDataMedia.setId(dataMediaPairInfo.getField(\"sourceDataMediaId\").getLongValue());\n        dataMediaPair.setSource(sourceDataMedia);\n        targetDataMedia.setId(dataMediaPairInfo.getField(\"targetDataMediaId\").getLongValue());\n        dataMediaPair.setTarget(targetDataMedia);\n        try {\n            dataMediaPairService.create(dataMediaPair);\n        } catch (RepeatConfigureException rce) {\n            err.setMessage(\"invalidDataMediaPair\");\n            return;\n        }\n\n        nav.redirectToLocation(\"dataMediaPairList.htm?pipelineId=\" + dataMediaPair.getPipelineId());\n    }\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/home/module/action/DataMediaSourceAction.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.home.module.action;\n\nimport javax.annotation.Resource;\n\nimport com.alibaba.citrus.service.form.CustomErrors;\nimport com.alibaba.citrus.service.form.Group;\nimport com.alibaba.citrus.turbine.Navigator;\nimport com.alibaba.citrus.turbine.dataresolver.FormField;\nimport com.alibaba.citrus.turbine.dataresolver.FormGroup;\nimport com.alibaba.citrus.turbine.dataresolver.Param;\nimport com.alibaba.citrus.webx.WebxException;\nimport com.alibaba.otter.manager.biz.common.exceptions.RepeatConfigureException;\nimport com.alibaba.otter.manager.biz.config.datamedia.DataMediaService;\nimport com.alibaba.otter.manager.biz.config.datamediasource.DataMediaSourceService;\nimport com.alibaba.otter.manager.web.common.WebConstant;\nimport com.alibaba.otter.shared.common.model.config.data.DataMediaSource;\nimport com.alibaba.otter.shared.common.model.config.data.db.DbMediaSource;\nimport com.alibaba.otter.shared.common.model.config.data.mq.MqMediaSource;\n\npublic class DataMediaSourceAction extends AbstractAction {\n\n    @Resource(name = \"dataMediaSourceService\")\n    private DataMediaSourceService dataMediaSourceService;\n\n    @Resource(name = \"dataMediaService\")\n    private DataMediaService       dataMediaService;\n\n    /**\n     * 添加Channel\n     * \n     * @param channelInfo\n     * @param channelParameterInfo\n     * @throws Exception\n     */\n    public void doAdd(@FormGroup(\"dataMediaSourceInfo\") Group dataMediaSourceInfo,\n                      @FormField(name = \"formDataMediaSourceError\", group = \"dataMediaSourceInfo\") CustomErrors err,\n                      Navigator nav) throws Exception {\n        DataMediaSource dataMediaSource = new DataMediaSource();\n        dataMediaSourceInfo.setProperties(dataMediaSource);\n\n        if (dataMediaSource.getType().isMysql() || dataMediaSource.getType().isOracle()) {\n            DbMediaSource dbMediaSource = new DbMediaSource();\n            dataMediaSourceInfo.setProperties(dbMediaSource);\n            if (dataMediaSource.getType().isMysql()) {\n                dbMediaSource.setDriver(\"com.mysql.jdbc.Driver\");\n            } else if (dataMediaSource.getType().isOracle()) {\n                dbMediaSource.setDriver(\"oracle.jdbc.driver.OracleDriver\");\n            }\n            try {\n                dataMediaSourceService.create(dbMediaSource);\n            } catch (RepeatConfigureException rce) {\n                err.setMessage(\"invalidDataMediaSource\");\n                return;\n            }\n        } else if (dataMediaSource.getType().isNapoli() || dataMediaSource.getType().isMq()) {\n            MqMediaSource mqMediaSource = new MqMediaSource();\n            dataMediaSourceInfo.setProperties(mqMediaSource);\n\n            try {\n                dataMediaSourceService.create(mqMediaSource);\n            } catch (RepeatConfigureException rce) {\n                err.setMessage(\"invalidDataMediaSource\");\n                return;\n            }\n        }\n\n        nav.redirectTo(WebConstant.DATA_MEDIA_SOURCE_LIST_LINK);\n    }\n\n    /**\n     * @param channelId\n     * @throws WebxException\n     */\n    public void doDelete(@Param(\"dataMediaSourceId\") Long dataMediaSourceId, @Param(\"pageIndex\") int pageIndex,\n                         @Param(\"searchKey\") String searchKey, Navigator nav) throws WebxException {\n        if (dataMediaService.listByDataMediaSourceId(dataMediaSourceId).size() < 1) {\n            dataMediaSourceService.remove(dataMediaSourceId);\n        }\n\n        nav.redirectToLocation(\"dataSourceList.htm?pageIndex=\" + pageIndex + \"&searchKey=\" + urlEncode(searchKey));\n    }\n\n    public void doEdit(@FormGroup(\"dataMediaSourceInfo\") Group dataMediaSourceInfo, @Param(\"pageIndex\") int pageIndex,\n                       @Param(\"searchKey\") String searchKey,\n                       @FormField(name = \"formDataMediaSourceError\", group = \"dataMediaSourceInfo\") CustomErrors err,\n                       Navigator nav) throws Exception {\n        DbMediaSource dbMediaSource = new DbMediaSource();\n        dataMediaSourceInfo.setProperties(dbMediaSource);\n\n        if (dbMediaSource.getType().isMysql()) {\n            dbMediaSource.setDriver(\"com.mysql.jdbc.Driver\");\n        } else if (dbMediaSource.getType().isOracle()) {\n            dbMediaSource.setDriver(\"oracle.jdbc.driver.OracleDriver\");\n        }\n\n        try {\n            dataMediaSourceService.modify(dbMediaSource);\n        } catch (RepeatConfigureException rce) {\n            err.setMessage(\"invalidDataMediaSource\");\n            return;\n        }\n\n        nav.redirectToLocation(\"dataSourceList.htm?pageIndex=\" + pageIndex + \"&searchKey=\" + urlEncode(searchKey));\n    }\n\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/home/module/action/NodeAction.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.home.module.action;\n\nimport javax.annotation.Resource;\n\nimport com.alibaba.citrus.service.form.CustomErrors;\nimport com.alibaba.citrus.service.form.Group;\nimport com.alibaba.citrus.turbine.Navigator;\nimport com.alibaba.citrus.turbine.dataresolver.FormField;\nimport com.alibaba.citrus.turbine.dataresolver.FormGroup;\nimport com.alibaba.citrus.turbine.dataresolver.Param;\nimport com.alibaba.citrus.webx.WebxException;\nimport com.alibaba.otter.manager.biz.common.exceptions.RepeatConfigureException;\nimport com.alibaba.otter.manager.biz.config.autokeeper.AutoKeeperClusterService;\nimport com.alibaba.otter.manager.biz.config.node.NodeService;\nimport com.alibaba.otter.manager.biz.config.pipeline.PipelineService;\nimport com.alibaba.otter.manager.web.common.WebConstant;\nimport com.alibaba.otter.shared.common.model.autokeeper.AutoKeeperCluster;\nimport com.alibaba.otter.shared.common.model.config.node.Node;\nimport com.alibaba.otter.shared.common.model.config.node.NodeParameter;\n\npublic class NodeAction extends AbstractAction {\n\n    @Resource(name = \"nodeService\")\n    private NodeService              nodeService;\n\n    @Resource(name = \"pipelineService\")\n    private PipelineService          pipelineService;\n\n    @Resource(name = \"autoKeeperClusterService\")\n    private AutoKeeperClusterService autoKeeperClusterService;\n\n    public void doAdd(@FormGroup(\"nodeInfo\") Group nodeInfo, @FormGroup(\"nodeParameterInfo\") Group nodeParameterInfo,\n                      @FormField(name = \"formNodeError\", group = \"nodeInfo\") CustomErrors err, Navigator nav)\n                                                                                                             throws Exception {\n        Node node = new Node();\n        NodeParameter parameter = new NodeParameter();\n        nodeInfo.setProperties(node);\n        nodeParameterInfo.setProperties(parameter);\n\n        if (parameter.getDownloadPort() == null || parameter.getDownloadPort() == 0) {\n            parameter.setDownloadPort(node.getPort().intValue() + 1);\n        }\n\n        if (parameter.getMbeanPort() == null || parameter.getMbeanPort() == 0) {\n            parameter.setMbeanPort(node.getPort().intValue() + 2);\n        }\n\n        Long autoKeeperclusterId = nodeParameterInfo.getField(\"autoKeeperclusterId\").getLongValue();\n        if (autoKeeperclusterId != null && autoKeeperclusterId > 0) {\n            AutoKeeperCluster autoKeeperCluster = autoKeeperClusterService.findAutoKeeperClusterById(autoKeeperclusterId);\n            parameter.setZkCluster(autoKeeperCluster);\n        }\n\n        node.setParameters(parameter);\n        try {\n            nodeService.create(node);\n        } catch (RepeatConfigureException rce) {\n            err.setMessage(\"invalidNode\");\n            return;\n        }\n        nav.redirectTo(WebConstant.NODE_LIST_LINK);\n    }\n\n    /**\n     * 修改Node\n     */\n    public void doEdit(@FormGroup(\"nodeInfo\") Group nodeInfo, @FormGroup(\"nodeParameterInfo\") Group nodeParameterInfo,\n                       @Param(\"pageIndex\") int pageIndex, @Param(\"searchKey\") String searchKey,\n                       @FormField(name = \"formNodeError\", group = \"nodeInfo\") CustomErrors err, Navigator nav)\n                                                                                                              throws Exception {\n        Node node = new Node();\n        NodeParameter parameter = new NodeParameter();\n        nodeInfo.setProperties(node);\n        nodeParameterInfo.setProperties(parameter);\n\n        if (parameter.getDownloadPort() == null || parameter.getDownloadPort() == 0) {\n            parameter.setDownloadPort(node.getPort().intValue() + 1);\n        }\n\n        if (parameter.getMbeanPort() == null || parameter.getMbeanPort() == 0) {\n            parameter.setMbeanPort(node.getPort().intValue() + 2);\n        }\n\n        Long autoKeeperclusterId = nodeParameterInfo.getField(\"autoKeeperclusterId\").getLongValue();\n        if (autoKeeperclusterId != null && autoKeeperclusterId > 0) {\n            AutoKeeperCluster autoKeeperCluster = autoKeeperClusterService.findAutoKeeperClusterById(autoKeeperclusterId);\n            parameter.setZkCluster(autoKeeperCluster);\n        }\n\n        node.setParameters(parameter);\n        try {\n            nodeService.modify(node);\n        } catch (RepeatConfigureException rce) {\n            err.setMessage(\"invalidNode\");\n            return;\n        }\n\n        nav.redirectToLocation(\"nodeList.htm?pageIndex=\" + pageIndex + \"&searchKey=\" + urlEncode(searchKey));\n    }\n\n    /**\n     * 删除node\n     * \n     * @param nodeId\n     * @throws WebxException\n     */\n    public void doDelete(@Param(\"nodeId\") Long nodeId, @Param(\"pageIndex\") int pageIndex,\n                         @Param(\"searchKey\") String searchKey, Navigator nav) throws WebxException {\n\n        if (pipelineService.hasRelation(nodeId) || nodeService.findById(nodeId).getStatus().isStart()) {\n            nav.redirectTo(WebConstant.ERROR_FORBIDDEN_Link);\n            return;\n        } else {\n            nodeService.remove(nodeId);\n        }\n\n        nav.redirectToLocation(\"nodeList.htm?pageIndex=\" + pageIndex + \"&searchKey=\" + urlEncode(searchKey));\n    }\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/home/module/action/PipelineAction.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.home.module.action;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport javax.annotation.Resource;\nimport javax.servlet.http.HttpSession;\n\nimport org.apache.commons.lang.ArrayUtils;\n\nimport com.alibaba.citrus.service.form.CustomErrors;\nimport com.alibaba.citrus.service.form.Group;\nimport com.alibaba.citrus.turbine.Navigator;\nimport com.alibaba.citrus.turbine.dataresolver.FormField;\nimport com.alibaba.citrus.turbine.dataresolver.FormGroup;\nimport com.alibaba.citrus.turbine.dataresolver.Param;\nimport com.alibaba.citrus.webx.WebxException;\nimport com.alibaba.otter.manager.biz.common.exceptions.RepeatConfigureException;\nimport com.alibaba.otter.manager.biz.config.channel.ChannelService;\nimport com.alibaba.otter.manager.biz.config.datamediapair.DataMediaPairService;\nimport com.alibaba.otter.manager.biz.config.pipeline.PipelineService;\nimport com.alibaba.otter.manager.web.common.WebConstant;\nimport com.alibaba.otter.shared.common.model.config.channel.Channel;\nimport com.alibaba.otter.shared.common.model.config.node.Node;\nimport com.alibaba.otter.shared.common.model.config.pipeline.Pipeline;\nimport com.alibaba.otter.shared.common.model.config.pipeline.PipelineParameter;\n\npublic class PipelineAction {\n\n    @Resource(name = \"pipelineService\")\n    private PipelineService      pipelineService;\n\n    @Resource(name = \"dataMediaPairService\")\n    private DataMediaPairService dataMediaPairService;\n\n    @Resource(name = \"channelService\")\n    private ChannelService       channelService;\n\n    public void doAdd(@FormGroup(\"pipelineInfo\") Group pipelineInfo,\n                      @FormGroup(\"pipelineParameterInfo\") Group pipelineParameterInfo,\n                      @FormField(name = \"formPipelineError\", group = \"pipelineInfo\") CustomErrors err,\n                      HttpSession session, Navigator nav) throws Exception {\n        Pipeline pipeline = new Pipeline();\n        PipelineParameter parameters = new PipelineParameter();\n        pipelineInfo.setProperties(pipeline);\n        pipelineParameterInfo.setProperties(parameters);\n        // if (parameters.getLoadPoolSize() < 1) {\n        // parameters.setLoadPoolSize(PipelineParameter.DEFAULT_LOAD_POOL_SIZE);\n        // }\n\n        List<Long> selectNodeIds = Arrays.asList(ArrayUtils.toObject(pipelineInfo.getField(\"selectNodeIds\")\n            .getLongValues()));\n        List<Node> selectNodes = new ArrayList<Node>();\n        for (Long selectNodeId : selectNodeIds) {\n            Node node = new Node();\n            node.setId(selectNodeId);\n            selectNodes.add(node);\n        }\n\n        // select/extract节点普遍配置为同一个节点\n        List<Long> extractNodeIds = Arrays.asList(ArrayUtils.toObject(pipelineInfo.getField(\"selectNodeIds\")\n            .getLongValues()));\n        // List<Long> extractNodeIds =\n        // Arrays.asList(ArrayUtils.toObject(pipelineInfo.getField(\"extractNodeIds\").getLongValues()));\n        List<Node> extractNodes = new ArrayList<Node>();\n        for (Long extractNodeId : extractNodeIds) {\n            Node node = new Node();\n            node.setId(extractNodeId);\n            extractNodes.add(node);\n        }\n\n        List<Long> loadNodeIds = Arrays.asList(ArrayUtils.toObject(pipelineInfo.getField(\"loadNodeIds\").getLongValues()));\n        List<Node> loadNodes = new ArrayList<Node>();\n        for (Long loadNodeId : loadNodeIds) {\n            Node node = new Node();\n            node.setId(loadNodeId);\n            loadNodes.add(node);\n        }\n\n        pipeline.setSelectNodes(selectNodes);\n        pipeline.setExtractNodes(extractNodes);\n        pipeline.setLoadNodes(loadNodes);\n        pipeline.setParameters(parameters);\n\n        List<Pipeline> values = pipelineService.listByDestinationWithoutOther(pipeline.getParameters()\n            .getDestinationName());\n        if (!values.isEmpty()) {\n            err.setMessage(\"invalidDestinationName\");\n            return;\n        }\n\n        try {\n            pipelineService.create(pipeline);\n        } catch (RepeatConfigureException rce) {\n            err.setMessage(\"invalidPipelineName\");\n            return;\n        }\n        nav.redirectToLocation(\"pipelineList.htm?channelId=\" + pipeline.getChannelId());\n    }\n\n    public void doDelete(@Param(\"pipelineId\") Long pipelineId, @Param(\"channelId\") Long channelId, Navigator nav)\n                                                                                                                 throws WebxException {\n        Channel channel = channelService.findById(channelId);\n        if (channel.getStatus().isStart()) {\n            nav.redirectTo(WebConstant.ERROR_FORBIDDEN_Link);\n            return;\n        }\n        // 如果pipeline节点下面存在dataMediaPair，则不允许删除\n        if (dataMediaPairService.listByPipelineId(pipelineId).size() < 1) {\n            pipelineService.remove(pipelineId);\n        }\n\n        nav.redirectToLocation(\"pipelineList.htm?channelId=\" + channelId);\n    }\n\n    public void doEdit(@FormGroup(\"pipelineInfo\") Group pipelineInfo,\n                       @FormGroup(\"pipelineParameterInfo\") Group pipelineParameterInfo,\n                       @FormField(name = \"formPipelineError\", group = \"pipelineInfo\") CustomErrors err,\n                       HttpSession session, Navigator nav) {\n        Pipeline pipeline = new Pipeline();\n        PipelineParameter parameters = new PipelineParameter();\n        pipelineInfo.setProperties(pipeline);\n        pipelineParameterInfo.setProperties(parameters);\n        // if (parameters.getLoadPoolSize() < 1) {\n        // parameters.setLoadPoolSize(PipelineParameter.DEFAULT_LOAD_POOL_SIZE);\n        // }\n\n        List<Long> selectNodeIds = Arrays.asList(ArrayUtils.toObject(pipelineInfo.getField(\"selectNodeIds\")\n            .getLongValues()));\n        List<Node> selectNodes = new ArrayList<Node>();\n        for (Long selectNodeId : selectNodeIds) {\n            Node node = new Node();\n            node.setId(selectNodeId);\n            selectNodes.add(node);\n        }\n\n        // select/extract节点普遍配置为同一个节点\n        List<Long> extractNodeIds = Arrays.asList(ArrayUtils.toObject(pipelineInfo.getField(\"selectNodeIds\")\n            .getLongValues()));\n        // List<Long> extractNodeIds =\n        // Arrays.asList(ArrayUtils.toObject(pipelineInfo.getField(\"extractNodeIds\").getLongValues()));\n        List<Node> extractNodes = new ArrayList<Node>();\n        for (Long extractNodeId : extractNodeIds) {\n            Node node = new Node();\n            node.setId(extractNodeId);\n            extractNodes.add(node);\n        }\n\n        List<Long> loadNodeIds = Arrays.asList(ArrayUtils.toObject(pipelineInfo.getField(\"loadNodeIds\").getLongValues()));\n        List<Node> loadNodes = new ArrayList<Node>();\n        for (Long loadNodeId : loadNodeIds) {\n            Node node = new Node();\n            node.setId(loadNodeId);\n            loadNodes.add(node);\n        }\n\n        pipeline.setSelectNodes(selectNodes);\n        pipeline.setExtractNodes(extractNodes);\n        pipeline.setLoadNodes(loadNodes);\n        pipeline.setParameters(parameters);\n\n        List<Pipeline> values = pipelineService.listByDestinationWithoutOther(pipeline.getParameters()\n            .getDestinationName());\n\n        if (!values.isEmpty()) {\n            if (values.size() > 1 || !values.get(0).getId().equals(pipeline.getId())) {\n                err.setMessage(\"invalidDestinationName\");\n                return;\n            }\n        }\n\n        try {\n            pipelineService.modify(pipeline);\n        } catch (RepeatConfigureException rce) {\n            err.setMessage(\"invalidPipelineName\");\n            return;\n        }\n        nav.redirectToLocation(\"pipelineList.htm?channelId=\" + pipeline.getChannelId());\n    }\n\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/home/module/action/PositionAction.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.home.module.action;\n\nimport javax.annotation.Resource;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.alibaba.citrus.turbine.Navigator;\nimport com.alibaba.citrus.turbine.dataresolver.Param;\nimport com.alibaba.otter.manager.biz.config.pipeline.PipelineService;\nimport com.alibaba.otter.shared.arbitrate.ArbitrateViewService;\nimport com.alibaba.otter.shared.arbitrate.model.PositionEventData;\nimport com.alibaba.otter.shared.common.model.config.pipeline.Pipeline;\n\npublic class PositionAction {\n\n    private static final Logger  logger = LoggerFactory.getLogger(PositionAction.class);\n\n    @Resource(name = \"pipelineService\")\n    private PipelineService      pipelineService;\n\n    @Resource(name = \"arbitrateViewService\")\n    private ArbitrateViewService arbitrateViewService;\n\n    public void doRemove(@Param(\"pipelineId\") Long pipelineId, Navigator nav) throws Exception {\n        Pipeline pipeline = pipelineService.findById(pipelineId);\n        String destination = pipeline.getParameters().getDestinationName();\n        short clientId = pipeline.getParameters().getMainstemClientId();\n        PositionEventData position = arbitrateViewService.getCanalCursor(destination, clientId);\n        logger.warn(\"remove pipelineId[{}] position \\n {}\", pipelineId, position); // 记录一下日志\n        arbitrateViewService.removeCanalCursor(destination, clientId);\n        nav.redirectToLocation(\"analysisStageStat.htm?pipelineId=\" + pipelineId);\n    }\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/home/module/action/SwitchWarmupAction.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.home.module.action;\n\nimport javax.annotation.Resource;\n\nimport com.alibaba.citrus.turbine.Navigator;\nimport com.alibaba.citrus.turbine.dataresolver.Param;\nimport com.alibaba.otter.manager.biz.config.channel.ChannelService;\nimport com.alibaba.otter.shared.arbitrate.ArbitrateManageService;\nimport com.alibaba.otter.shared.common.model.config.channel.Channel;\n\npublic class SwitchWarmupAction {\n\n    @Resource(name = \"channelService\")\n    private ChannelService         channelService;\n\n    @Resource(name = \"arbitrateManageService\")\n    private ArbitrateManageService arbitrateManageService;\n\n    public void doSwitch(@Param(\"pipelineId\") Long pipelineId, Navigator nav) throws Exception {\n        Channel channel = channelService.findByPipelineId(pipelineId);\n        arbitrateManageService.channelEvent().restart(channel.getId());// 尝试重新启动\n        arbitrateManageService.systemEvent().switchWarmup(channel.getId(), pipelineId);\n        nav.redirectToLocation(\"analysisStageStat.htm?pipelineId=\" + pipelineId);\n    }\n\n    public void doRestart(@Param(\"pipelineId\") Long pipelineId, Navigator nav) throws Exception {\n        Channel channel = channelService.findByPipelineId(pipelineId);\n        arbitrateManageService.channelEvent().restart(channel.getId());// 尝试重新启动\n        channelService.notifyChannel(channel.getId());// 推送下配置\n        nav.redirectToLocation(\"analysisStageStat.htm?pipelineId=\" + pipelineId);\n    }\n\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/home/module/action/SystemParameterAction.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.home.module.action;\n\nimport java.util.ArrayList;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport javax.annotation.Resource;\n\nimport org.apache.commons.lang.StringUtils;\n\nimport com.alibaba.citrus.service.form.Group;\nimport com.alibaba.citrus.turbine.Navigator;\nimport com.alibaba.citrus.turbine.dataresolver.FormGroup;\nimport com.alibaba.otter.manager.biz.common.exceptions.ManagerException;\nimport com.alibaba.otter.manager.biz.config.parameter.SystemParameterService;\nimport com.alibaba.otter.shared.common.model.config.parameter.SystemParameter;\n\npublic class SystemParameterAction extends AbstractAction {\n\n    @Resource(name = \"systemParameterService\")\n    private SystemParameterService systemParameterService;\n\n    /**\n     * 修改系统参数\n     */\n    public void doEdit(@FormGroup(\"systemParameterDetailInfo\") Group systemParameterDetailInfo, Navigator nav)\n                                                                                                              throws Exception {\n\n        SystemParameter systemParameter = new SystemParameter();\n        systemParameterDetailInfo.setProperties(systemParameter);\n        String defaultAlarmReceiver = systemParameterDetailInfo.getField(\"defaultAlarmReceiver\").getStringValue();\n        String defaultAlarmReceiverStrs[] = StringUtils.split(defaultAlarmReceiver, \"=\");\n        if (defaultAlarmReceiverStrs.length != 2) {\n            throw new ManagerException(\"defaultAlarmReceiver[\" + defaultAlarmReceiver + \"] is not valid!\");\n        }\n        systemParameter.setDefaultAlarmReceiveKey(defaultAlarmReceiverStrs[0]);\n        systemParameter.setDefaultAlarmReceiver(defaultAlarmReceiverStrs[1]);\n\n        String alarmReceiver = systemParameterDetailInfo.getField(\"alarmReceiver\").getStringValue();\n\n        List<String> alarmReceivers = new ArrayList<String>();\n        String alarmReceiver1[] = StringUtils.split(alarmReceiver, \"\\n\");\n        for (String alarmReceiverStr : alarmReceiver1) {\n            String[] alarmReceiver2 = StringUtils.split(alarmReceiverStr, \";\");\n            for (String alarmReceiverStr2 : alarmReceiver2) {\n                alarmReceivers.add(alarmReceiverStr2);\n            }\n        }\n\n        Map<String, String> alarmReceiverMap = new LinkedHashMap<String, String>();\n        for (String alarmReceiverStr : alarmReceivers) {\n            String alarmReceiverStrs[] = StringUtils.split(alarmReceiverStr, \"=\");\n            if (alarmReceiverStrs.length != 2) {\n                throw new ManagerException(\"alarmReceiver[\" + alarmReceiver + \"] is not valid!\");\n            }\n\n            alarmReceiverMap.put(alarmReceiverStrs[0], alarmReceiverStrs[1]);\n        }\n        systemParameter.setAlarmReceiver(alarmReceiverMap);\n        systemParameterService.createOrUpdate(systemParameter);\n        nav.redirectToLocation(\"systemParameter.htm?edit=true\");\n    }\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/home/module/action/UserAction.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.home.module.action;\n\nimport javax.annotation.Resource;\nimport javax.servlet.http.HttpSession;\n\nimport com.alibaba.citrus.service.form.CustomErrors;\nimport com.alibaba.citrus.service.form.Group;\nimport com.alibaba.citrus.service.requestcontext.parser.ParameterParser;\nimport com.alibaba.citrus.turbine.Navigator;\nimport com.alibaba.citrus.turbine.dataresolver.FormField;\nimport com.alibaba.citrus.turbine.dataresolver.FormGroup;\nimport com.alibaba.citrus.turbine.dataresolver.Param;\nimport com.alibaba.citrus.webx.WebxException;\nimport com.alibaba.otter.manager.biz.common.exceptions.RepeatConfigureException;\nimport com.alibaba.otter.manager.biz.user.UserService;\nimport com.alibaba.otter.manager.web.common.WebConstant;\nimport com.alibaba.otter.shared.common.model.user.User;\nimport com.alibaba.otter.shared.common.utils.SecurityUtils;\n\n/**\n * 类UserAction.java的实现描述：TODO 类实现描述\n * \n * @author simon 2011-11-10 下午07:14:50\n */\npublic class UserAction extends AbstractAction {\n\n    @Resource(name = \"userService\")\n    private UserService userService;\n\n    public void doAdd(@FormGroup(\"addUserInfo\") Group userInfo, Navigator nav,\n                      @FormField(name = \"formUserError\", group = \"addUserInfo\") CustomErrors err) {\n        User user = new User();\n        userInfo.setProperties(user);\n        user.setPassword(SecurityUtils.getPassword(user.getPassword()));\n        try {\n            userService.createUser(user);\n        } catch (RepeatConfigureException rce) {\n            err.setMessage(\"invalidUser\");\n            return;\n        }\n        nav.redirectTo(WebConstant.USER_MANAGER_LINK);\n\n    }\n\n    public void doEdit(@FormGroup(\"editUserInfo\") Group userInfo, @Param(\"pageIndex\") int pageIndex,\n                       @Param(\"searchKey\") String searchKey, Navigator nav,\n                       @FormField(name = \"formUserError\", group = \"editUserInfo\") CustomErrors err) {\n        User user = new User();\n        userInfo.setProperties(user);\n        if (null != user.getPassword()) {\n            if (user.getPassword().length() < 6) {\n                err.setMessage(\"passwordTooLess\");\n                return;\n            }\n            user.setPassword(SecurityUtils.getPassword(user.getPassword()));\n        }\n\n        try {\n            userService.updataUser(user);\n        } catch (RepeatConfigureException rce) {\n            err.setMessage(\"invalidUser\");\n            return;\n        }\n        nav.redirectToLocation(\"userManager.htm?pageIndex=\" + pageIndex + \"&searchKey=\" + urlEncode(searchKey));\n    }\n\n    public void doDelete(@Param(\"userId\") Long userId, @Param(\"pageIndex\") int pageIndex,\n                         @Param(\"searchKey\") String searchKey, Navigator nav) throws WebxException {\n        userService.deleteUser(userId);\n        nav.redirectToLocation(\"userManager.htm?pageIndex=\" + pageIndex + \"&searchKey=\" + urlEncode(searchKey));\n    }\n\n    public void doLogin(@FormGroup(\"login\") User user,\n                        @FormField(name = \"loginError\", group = \"login\") CustomErrors err, @Param(\"Done\") String url,\n                        Navigator nav, HttpSession session, ParameterParser params) throws Exception {\n\n        user = userService.login(user.getName(), SecurityUtils.getPassword(user.getPassword()));\n\n        if (user != null) {\n            // 在session中创建User对象\n            session.setAttribute(WebConstant.USER_SESSION_KEY, user);\n\n            // 跳转到return页面\n            if (null == url) {\n                nav.redirectTo(WebConstant.CHANNEL_LIST_LINK);\n            } else {\n                nav.redirectToLocation(url);\n            }\n\n        } else {\n            err.setMessage(\"invalidUserOrPassword\");\n        }\n    }\n\n    public void doLogout(HttpSession session, Navigator nav, ParameterParser params) throws Exception {\n        // 清除session中的user\n        session.removeAttribute(WebConstant.USER_SESSION_KEY);\n\n        // 跳转到return页面\n        nav.redirectTo(WebConstant.OTTER_LOGIN_LINK);\n    }\n\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/home/module/screen/AddAlarmRule.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.home.module.screen;\n\nimport javax.annotation.Resource;\n\nimport com.alibaba.citrus.turbine.Context;\nimport com.alibaba.citrus.turbine.Navigator;\nimport com.alibaba.citrus.turbine.dataresolver.Param;\nimport com.alibaba.otter.manager.biz.config.channel.ChannelService;\n\npublic class AddAlarmRule {\n\n    @Resource(name = \"channelService\")\n    private ChannelService channelService;\n\n    public void execute(@Param(\"pipelineId\") Long pipelineId, Context context, Navigator nav) throws Exception {\n\n        context.put(\"pipelineId\", pipelineId);\n        context.put(\"channelId\", channelService.findByPipelineId(pipelineId).getId());\n    }\n\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/home/module/screen/AddBatchDataMediaPair.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.home.module.screen;\n\nimport javax.annotation.Resource;\n\nimport com.alibaba.citrus.turbine.Context;\nimport com.alibaba.citrus.turbine.Navigator;\nimport com.alibaba.citrus.turbine.dataresolver.Param;\nimport com.alibaba.otter.shared.common.model.config.channel.Channel;\nimport com.alibaba.otter.manager.biz.config.channel.ChannelService;\nimport com.alibaba.otter.manager.web.common.WebConstant;\n\npublic class AddBatchDataMediaPair {\n\n    @Resource(name = \"channelService\")\n    private ChannelService channelService;\n\n    public void execute(@Param(\"pipelineId\") Long pipelineId, Context context, Navigator nav) throws Exception {\n\n        Channel channel = channelService.findByPipelineId(pipelineId);\n        if (channel.getStatus().isStart()) {\n            nav.redirectTo(WebConstant.ERROR_FORBIDDEN_Link);\n            return;\n        }\n\n        context.put(\"pipelineId\", pipelineId);\n        context.put(\"channelId\", channel.getId());\n    }\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/home/module/screen/AddCanal.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.home.module.screen;\n\nimport java.util.List;\n\nimport javax.annotation.Resource;\n\nimport org.springframework.util.CollectionUtils;\n\nimport com.alibaba.citrus.turbine.Context;\nimport com.alibaba.citrus.turbine.Navigator;\nimport com.alibaba.otter.manager.biz.config.autokeeper.AutoKeeperClusterService;\nimport com.alibaba.otter.shared.common.model.autokeeper.AutoKeeperCluster;\n\npublic class AddCanal {\n\n    @Resource(name = \"autoKeeperClusterService\")\n    private AutoKeeperClusterService autoKeeperClusterService;\n\n    public void execute(Context context, Navigator nav) throws Exception {\n        List<AutoKeeperCluster> zkClusters = autoKeeperClusterService.listAutoKeeperClusters();\n        if (CollectionUtils.isEmpty(zkClusters)) {\n            nav.redirectToLocation(\"addZookeeper.htm?message=init\");\n        } else {\n            context.put(\"zkClusters\", zkClusters);\n        }\n    }\n\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/home/module/screen/AddColumnPair.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.home.module.screen;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport javax.annotation.Resource;\n\nimport com.alibaba.citrus.turbine.Context;\nimport com.alibaba.citrus.turbine.dataresolver.Param;\nimport com.alibaba.otter.manager.biz.config.datacolumnpair.DataColumnPairService;\nimport com.alibaba.otter.manager.biz.config.datamedia.DataMediaService;\nimport com.alibaba.otter.shared.common.model.config.data.ColumnPair;\nimport com.alibaba.otter.shared.common.model.config.data.DataMedia;\n\npublic class AddColumnPair {\n\n    @Resource(name = \"dataMediaService\")\n    private DataMediaService      dataMediaService;\n\n    @Resource(name = \"dataColumnPairService\")\n    private DataColumnPairService dataColumnPairService;\n\n    public void execute(@Param(\"dataMediaPairId\") Long dataMediaPairId, @Param(\"channelId\") Long channelId,\n                        @Param(\"pipelineId\") Long pipelineId, @Param(\"sourceMediaId\") Long sourceMediaId,\n                        @Param(\"targetMediaId\") Long targetMediaId, Context context) throws Exception {\n        @SuppressWarnings(\"unchecked\")\n        DataMedia sourcedataMedia = dataMediaService.findById(sourceMediaId);\n        DataMedia targetdataMedia = dataMediaService.findById(targetMediaId);\n\n        List<String> sourceColumns = dataMediaService.queryColumnByMedia(sourcedataMedia);\n        List<String> targetColumns = dataMediaService.queryColumnByMedia(targetdataMedia);\n\n        List<String> underSourceColumns = new ArrayList<String>();\n        List<String> underTargetColumns = new ArrayList<String>();\n\n        List<ColumnPair> columnPairs = dataColumnPairService.listByDataMediaPairId(dataMediaPairId);\n\n        if (columnPairs != null && !columnPairs.isEmpty()) {\n            for (ColumnPair columnPair : columnPairs) {\n                if (columnPair.getSourceColumn() != null) {\n                    underSourceColumns.add(columnPair.getSourceColumn().getName());\n                }\n                if (columnPair.getTargetColumn() != null) {\n                    underTargetColumns.add(columnPair.getTargetColumn().getName());\n                }\n            }\n        }\n\n        sourceColumns.removeAll(underSourceColumns);\n        targetColumns.removeAll(underTargetColumns);\n\n        context.put(\"sourceMediaId\", sourceMediaId);\n        context.put(\"targetMediaId\", targetMediaId);\n        context.put(\"sourceColumns\", sourceColumns);\n        context.put(\"targetColumns\", targetColumns);\n        context.put(\"underSourceColumns\", underSourceColumns);\n        context.put(\"underTargetColumns\", underTargetColumns);\n        context.put(\"dataMediaPairId\", dataMediaPairId);\n        context.put(\"channelId\", channelId);\n        context.put(\"pipelineId\", pipelineId);\n    }\n\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/home/module/screen/AddColumnPairGroup.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.home.module.screen;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport javax.annotation.Resource;\n\nimport org.apache.commons.collections.CollectionUtils;\n\nimport com.alibaba.citrus.turbine.Context;\nimport com.alibaba.citrus.turbine.dataresolver.Param;\nimport com.alibaba.otter.manager.biz.config.datacolumnpair.DataColumnPairGroupService;\nimport com.alibaba.otter.manager.biz.config.datacolumnpair.DataColumnPairService;\nimport com.alibaba.otter.manager.biz.config.datamedia.DataMediaService;\nimport com.alibaba.otter.manager.biz.config.datamediapair.DataMediaPairService;\nimport com.alibaba.otter.shared.common.model.config.data.ColumnGroup;\nimport com.alibaba.otter.shared.common.model.config.data.ColumnPair;\nimport com.alibaba.otter.shared.common.model.config.data.DataMediaPair;\n\npublic class AddColumnPairGroup {\n\n    @Resource(name = \"dataMediaPairService\")\n    private DataMediaPairService       dataMediaPairService;\n\n    @Resource(name = \"dataMediaService\")\n    private DataMediaService           dataMediaService;\n\n    @Resource(name = \"dataColumnPairGroupService\")\n    private DataColumnPairGroupService dataColumnPairGroupService;\n\n    @Resource(name = \"dataColumnPairService\")\n    private DataColumnPairService      dataColumnPairService;\n\n    public void execute(@Param(\"dataMediaPairId\") Long dataMediaPairId, @Param(\"pipelineId\") Long pipelineId,\n                        @Param(\"channelId\") Long channelId, @Param(\"sourceMediaId\") Long sourceMediaId,\n                        @Param(\"targetMediaId\") Long targetMediaId, Context context) throws Exception {\n        List<ColumnPair> columnPairs = dataColumnPairService.listByDataMediaPairId(dataMediaPairId);\n        if (CollectionUtils.isEmpty(columnPairs)) {\n            columnPairs.addAll(buildColumnPairFromDataMedia(dataMediaPairId, sourceMediaId, targetMediaId));\n        } else {\n            DataMediaPair dataMediaPair = dataMediaPairService.findById(dataMediaPairId);\n            if (dataMediaPair.getColumnPairMode().isExclude()) {\n                List<ColumnPair> allColumnPairs = buildColumnPairFromDataMedia(dataMediaPairId, sourceMediaId,\n                                                                               targetMediaId);\n                allColumnPairs.removeAll(columnPairs); // 如果是exclude模式，排除掉\n                columnPairs = allColumnPairs;\n            }\n        }\n\n        List<ColumnGroup> columnGroups = dataColumnPairGroupService.listByDataMediaPairId(dataMediaPairId);\n        List<ColumnPair> columnPairGroup = new ArrayList<ColumnPair>();\n\n        if (CollectionUtils.isNotEmpty(columnGroups)) {\n            for (ColumnGroup columnGroup : columnGroups) {\n                List<ColumnPair> columnPairTemp = new ArrayList<ColumnPair>();\n                columnPairGroup = columnGroup.getColumnPairs();\n                for (ColumnPair columnPair : columnPairGroup) {\n                    for (ColumnPair subColumnPair : columnPairs) {\n                        if (columnPair.equals(subColumnPair)) {\n                            columnPairTemp.add(subColumnPair);\n                        }\n                    }\n                }\n                // 如果是多个Group，需要深度复制columnPair,每个Group需要复制一个\n                columnPairs.removeAll(columnPairTemp);\n            }\n        }\n\n        context.put(\"preColumnPairs\", columnPairs); // 未加入group的columnPair定义\n        context.put(\"columnPairs\", columnPairGroup);// 当前group的columnPair定义\n        context.put(\"dataMediaPairId\", dataMediaPairId);\n        context.put(\"channelId\", channelId);\n        context.put(\"pipelineId\", pipelineId);\n\n    }\n\n    private List<ColumnPair> buildColumnPairFromDataMedia(Long dataMediaPairId, Long sourceMediaId, Long targetMediaId) {\n        List<ColumnPair> columnPairs = new ArrayList<ColumnPair>();\n        List<String> sourceColumns = dataMediaService.queryColumnByMediaId(sourceMediaId);\n        List<String> targetColumns = dataMediaService.queryColumnByMediaId(targetMediaId);\n\n        if (CollectionUtils.isNotEmpty(sourceColumns) && CollectionUtils.isNotEmpty(targetColumns)) {\n            for (String sourceColumn : sourceColumns) {\n                for (String targetColumn : targetColumns) {\n                    if (sourceColumn.equalsIgnoreCase(targetColumn)) {\n                        ColumnPair temp = new ColumnPair(sourceColumn, targetColumn);\n                        temp.setDataMediaPairId(dataMediaPairId);\n                        columnPairs.add(temp);\n                    }\n                }\n            }\n        }\n\n        return columnPairs;\n    }\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/home/module/screen/AddDataMediaPair.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.home.module.screen;\n\nimport javax.annotation.Resource;\n\nimport com.alibaba.citrus.turbine.Context;\nimport com.alibaba.citrus.turbine.Navigator;\nimport com.alibaba.citrus.turbine.dataresolver.Param;\nimport com.alibaba.otter.shared.common.model.config.channel.Channel;\nimport com.alibaba.otter.manager.biz.config.channel.ChannelService;\nimport com.alibaba.otter.manager.web.common.WebConstant;\n\npublic class AddDataMediaPair {\n\n    @Resource(name = \"channelService\")\n    private ChannelService channelService;\n\n    public void execute(@Param(\"pipelineId\") Long pipelineId, Context context, Navigator nav) throws Exception {\n\n        Channel channel = channelService.findByPipelineId(pipelineId);\n        if (channel.getStatus().isStart()) {\n            nav.redirectTo(WebConstant.ERROR_FORBIDDEN_Link);\n            return;\n        }\n\n        context.put(\"pipelineId\", pipelineId);\n        context.put(\"channelId\", channel.getId());\n    }\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/home/module/screen/AddNode.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.home.module.screen;\n\nimport java.util.List;\n\nimport javax.annotation.Resource;\n\nimport org.springframework.util.CollectionUtils;\n\nimport com.alibaba.citrus.turbine.Context;\nimport com.alibaba.citrus.turbine.Navigator;\nimport com.alibaba.otter.manager.biz.config.autokeeper.AutoKeeperClusterService;\nimport com.alibaba.otter.shared.common.model.autokeeper.AutoKeeperCluster;\n\npublic class AddNode {\n\n    @Resource(name = \"autoKeeperClusterService\")\n    private AutoKeeperClusterService autoKeeperClusterService;\n\n    public void execute(Context context, Navigator nav) throws Exception {\n        List<AutoKeeperCluster> zkClusters = autoKeeperClusterService.listAutoKeeperClusters();\n\n        if (CollectionUtils.isEmpty(zkClusters)) {\n            nav.redirectToLocation(\"addZookeeper.htm?message=init\");\n        } else {\n            context.put(\"zkClusters\", zkClusters);\n        }\n    }\n\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/home/module/screen/AddPipeline.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.home.module.screen;\n\nimport javax.annotation.Resource;\n\nimport com.alibaba.citrus.turbine.Context;\nimport com.alibaba.citrus.turbine.Navigator;\nimport com.alibaba.citrus.turbine.dataresolver.Param;\nimport com.alibaba.otter.shared.common.model.config.channel.Channel;\nimport com.alibaba.otter.manager.biz.config.channel.ChannelService;\nimport com.alibaba.otter.manager.biz.config.node.NodeService;\nimport com.alibaba.otter.manager.web.common.WebConstant;\n\npublic class AddPipeline {\n\n    @Resource(name = \"nodeService\")\n    private NodeService    nodeService;\n\n    @Resource(name = \"channelService\")\n    private ChannelService channelService;\n\n    public void execute(@Param(\"channelId\") Long channelId, Context context, Navigator nav) throws Exception {\n        Channel channel = channelService.findById(channelId);\n        if (channel.getStatus().isStart()) {\n            nav.redirectTo(WebConstant.ERROR_FORBIDDEN_Link);\n            return;\n        }\n\n        context.put(\"channelId\", channelId);\n        context.put(\"nodes\", nodeService.listAll());\n    }\n\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/home/module/screen/AddZookeeper.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.home.module.screen;\n\nimport javax.annotation.Resource;\n\nimport com.alibaba.citrus.turbine.Context;\nimport com.alibaba.citrus.turbine.Navigator;\nimport com.alibaba.citrus.turbine.dataresolver.Param;\nimport com.alibaba.otter.manager.biz.config.channel.ChannelService;\n\npublic class AddZookeeper {\n\n    @Resource(name = \"channelService\")\n    private ChannelService channelService;\n\n    public void execute(@Param(\"message\") String message, Context context, Navigator nav) throws Exception {\n        context.put(\"message\", message);\n    }\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/home/module/screen/AlarmRuleList.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.home.module.screen;\n\nimport java.util.List;\n\nimport javax.annotation.Resource;\n\nimport com.alibaba.citrus.turbine.Context;\nimport com.alibaba.citrus.turbine.dataresolver.Param;\nimport com.alibaba.otter.manager.biz.config.alarm.AlarmRuleService;\nimport com.alibaba.otter.manager.biz.config.channel.ChannelService;\nimport com.alibaba.otter.shared.common.model.config.alarm.AlarmRule;\nimport com.alibaba.otter.shared.common.model.config.channel.Channel;\n\npublic class AlarmRuleList {\n\n    @Resource(name = \"alarmRuleService\")\n    private AlarmRuleService alarmRuleService;\n\n    @Resource(name = \"channelService\")\n    private ChannelService   channelService;\n\n    public void execute(@Param(\"pipelineId\") Long pipelineId, Context context) throws Exception {\n\n        List<AlarmRule> alarmRules = alarmRuleService.getAlarmRules(pipelineId);\n        Channel channel = channelService.findByPipelineId(pipelineId);\n        context.put(\"alarmRules\", alarmRules);\n        context.put(\"pipelineId\", pipelineId);\n        context.put(\"channelId\", channel.getId());\n    }\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/home/module/screen/AlarmSystemList.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.home.module.screen;\n\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport javax.annotation.Resource;\n\nimport com.alibaba.citrus.turbine.Context;\nimport com.alibaba.citrus.turbine.dataresolver.Param;\nimport com.alibaba.citrus.util.Paginator;\nimport com.alibaba.otter.manager.biz.config.alarm.AlarmRuleService;\nimport com.alibaba.otter.shared.common.model.config.alarm.AlarmRule;\n\npublic class AlarmSystemList {\n\n    @Resource(name = \"alarmRuleService\")\n    private AlarmRuleService alarmRuleService;\n\n    public void execute(@Param(\"pipelineId\") Long pipelineId, @Param(\"pageIndex\") int pageIndex, Context context)\n                                                                                                                 throws Exception {\n        Map<String, Object> condition = new HashMap<String, Object>();\n        int count = alarmRuleService.getCount();\n        Paginator paginator = new Paginator();\n        paginator.setItems(count);\n        paginator.setPage(pageIndex);\n\n        condition.put(\"offset\", paginator.getOffset());\n        condition.put(\"length\", paginator.getLength());\n\n        List<AlarmRule> alarmRules = alarmRuleService.listAllAlarmRules(condition);\n        StringBuffer buffer = new StringBuffer();\n        for (AlarmRule alarmRule : alarmRules) {\n            buffer.append(alarmRule.getId());\n            buffer.append(\",\");\n        }\n        context.put(\"alarmRules\", alarmRules);\n        context.put(\"alarmRuleIds\", buffer.toString());\n        context.put(\"paginator\", paginator);\n    }\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/home/module/screen/AnalysisDelayStat.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.home.module.screen;\n\nimport java.text.SimpleDateFormat;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport javax.annotation.Resource;\nimport javax.servlet.http.HttpSession;\n\nimport org.apache.commons.lang.StringUtils;\n\nimport com.alibaba.citrus.turbine.Context;\nimport com.alibaba.citrus.turbine.dataresolver.Param;\nimport com.alibaba.otter.manager.biz.config.channel.ChannelService;\nimport com.alibaba.otter.manager.biz.statistics.delay.DelayStatService;\nimport com.alibaba.otter.manager.biz.statistics.delay.param.DelayStatInfo;\nimport com.alibaba.otter.shared.common.model.config.channel.Channel;\n\npublic class AnalysisDelayStat {\n\n    @Resource(name = \"channelService\")\n    private ChannelService   channelService;\n\n    @Resource(name = \"delayStatService\")\n    private DelayStatService delayStatService;\n\n    public void execute(@Param(\"d5221\") String startTime, @Param(\"d5222\") String endTime,\n                        @Param(\"pipelineId\") Long pipelineId, HttpSession session, Context context) throws Exception {\n        Date end = null;\n        Date start = null;\n        SimpleDateFormat sdf = new SimpleDateFormat(\"yyyy-MM-dd HH:mm\");\n        if (StringUtils.isEmpty(startTime) || StringUtils.isEmpty(endTime)) {\n            start = new Date(System.currentTimeMillis() / 60000 * 60000 - 24 * 60 * 60 * 1000);\n            end = new Date(System.currentTimeMillis() / 60000 * 60000);\n        } else {// 当前24小时，时间取整分钟\n            sdf.setLenient(false);\n            if (null != startTime && null != endTime) {\n                start = sdf.parse(startTime);\n                end = sdf.parse(endTime);\n            }\n        }\n\n        Channel channel = channelService.findByPipelineId(pipelineId);\n        Map<Long, DelayStatInfo> delayStatInfos = new HashMap<Long, DelayStatInfo>();\n        if (null != start && null != end) {\n            delayStatInfos = delayStatService.listTimelineDelayStat(pipelineId, start, end);\n        }\n\n        Double delayAvg = 0.0;\n        for (DelayStatInfo info : delayStatInfos.values()) {\n            delayAvg += info.getAvgDelayTime();\n        }\n\n        if (delayStatInfos.size() != 0) {\n            delayAvg = delayAvg / (1.0 * delayStatInfos.size());\n        }\n\n        context.put(\"delayStatInfos\", delayStatInfos);\n        context.put(\"delayAvg\", delayAvg);\n        context.put(\"channel\", channel);\n        context.put(\"pipelineId\", pipelineId);\n        context.put(\"start\", sdf.format(start));\n        context.put(\"end\", sdf.format(end));\n    }\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/home/module/screen/AnalysisStageStat.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.home.module.screen;\n\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport javax.annotation.Resource;\n\nimport com.alibaba.citrus.turbine.Context;\nimport com.alibaba.citrus.turbine.dataresolver.Param;\nimport com.alibaba.otter.manager.biz.config.pipeline.PipelineService;\nimport com.alibaba.otter.manager.biz.statistics.stage.ProcessStatService;\nimport com.alibaba.otter.shared.arbitrate.ArbitrateViewService;\nimport com.alibaba.otter.shared.arbitrate.impl.manage.ChannelArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.model.MainStemEventData;\nimport com.alibaba.otter.shared.arbitrate.model.PositionEventData;\nimport com.alibaba.otter.shared.common.model.config.channel.ChannelStatus;\nimport com.alibaba.otter.shared.common.model.config.pipeline.Pipeline;\nimport com.alibaba.otter.shared.common.model.statistics.stage.ProcessStat;\n\npublic class AnalysisStageStat {\n\n    @Resource(name = \"pipelineService\")\n    private PipelineService       pipelineService;\n\n    @Resource(name = \"processStatService\")\n    private ProcessStatService    processStatService;\n\n    @Resource(name = \"arbitrateViewService\")\n    private ArbitrateViewService  arbitrateViewService;\n\n    @Resource(name = \"channelEvent\")\n    private ChannelArbitrateEvent channelArbitrateEvent;\n\n    public void execute(@Param(\"pipelineId\") Long pipelineId, Context context) throws Exception {\n\n        List<ProcessStat> processStats = new ArrayList<ProcessStat>();\n        Pipeline pipeline = pipelineService.findById(pipelineId);\n        processStats = processStatService.listRealtimeProcessStat(pipelineId);\n        // Map ext = new HashMap<Long, String>();\n        // // ext.put(145456451, \"asdf\");\n        // for (Long i = 1L; i <= 3; i++) {\n        // List<StageStat> stageStats = new ArrayList<StageStat>();\n        // ProcessStat processStat = new ProcessStat();\n        // processStat.setPipelineId(1L);\n        // processStat.setProcessId(i);\n        // StageStat stage = new StageStat();\n        // stage.setStage(StageType.SELECT);\n        // stage.setStartTime(((new Date()).getTime() + i * 10 * 1000));\n        // stage.setEndTime(((new Date()).getTime() + i * 200 * 1000));\n        // stage.setNumber(11231230L);\n        // stage.setSize(14545645640L);\n        // // stage.setExts(ext);\n        // stageStats.add(stage);\n        // stage = new StageStat();\n        // stage.setStage(StageType.EXTRACT);\n        // stage.setStartTime(((new Date()).getTime() + i * 2000 * 1000));\n        // stage.setEndTime(((new Date()).getTime() + i * 3000 * 1000));\n        // stage.setExts(ext);\n        // // stage.setNumber(10L);\n        // // stage.setSize(10L);\n        // stageStats.add(stage);\n        // stage = new StageStat();\n        // stage.setStage(StageType.TRANSFORM);\n        // stage.setStartTime(((new Date()).getTime() + i * 5000 * 1000));\n        // stage.setEndTime(((new Date()).getTime() + i * 6000 * 1000));\n        // stage.setNumber(154640L);\n        // stage.setExts(ext);\n        // // stage.setSize(10L);\n        // stageStats.add(stage);\n        // stage = new StageStat();\n        // stage.setStage(StageType.LOAD);\n        // stage.setStartTime(((new Date()).getTime() + i * 70000 * 1000));\n        // stage.setEndTime(((new Date()).getTime() + i * 80000 * 1000));\n        // // stage.setNumber(10L);\n        // stage.setSize(101445L);\n        // // stage.setExts(ext);\n        // stageStats.add(stage);\n        // processStat.setStageStats(stageStats);\n        // processStats.add(processStat);\n        // }\n\n        Long stageStart = 0L;\n        // Long stageEnd = new Date().getTime() + 3 * 80000 * 1000;\n        Long stageEnd = new Date().getTime();\n        Long interval = 0L;\n        double offset = 0L;\n        // 找出最先开始的process的select阶段的开始时间作为起始时间\n        if (processStats.size() > 0) {\n            if (processStats.get(0).getStageStats().size() > 0) {\n                stageStart = processStats.get(0).getStageStats().get(0).getStartTime();\n            }\n        }\n\n        // 动态计算每个阶段的长度比例\n        if (stageStart > 0) {\n            interval = stageEnd - stageStart;\n        }\n        if (interval > 0) {\n            offset = 800.0 / interval;\n        }\n\n        // 计算每个process当前任务所做的时间总和\n        Map<Long, Long> processTime = new HashMap<Long, Long>();\n        for (ProcessStat processStat : processStats) {\n            Long timeout = 0L;\n            if (processStat.getStageStats().size() > 0) {\n                timeout = stageEnd - processStat.getStageStats().get(0).getStartTime();\n            }\n            processTime.put(processStat.getProcessId(), timeout);\n        }\n\n        // 获取下mainstem状态信息\n        MainStemEventData mainstemData = arbitrateViewService.mainstemData(pipeline.getChannelId(), pipelineId);\n\n        PositionEventData positionData = arbitrateViewService.getCanalCursor(pipeline.getParameters().getDestinationName(),\n                                                                             pipeline.getParameters().getMainstemClientId());\n\n        ChannelStatus status = channelArbitrateEvent.status(pipeline.getChannelId());\n\n        context.put(\"pipeline\", pipeline);\n        context.put(\"pipelineId\", pipelineId);\n        context.put(\"processStats\", processStats);\n        context.put(\"offset\", offset);\n        context.put(\"stageStart\", stageStart);\n        context.put(\"stageEnd\", stageEnd);\n        context.put(\"processTime\", processTime);\n        context.put(\"mainstemData\", mainstemData);\n        context.put(\"positionData\", positionData);\n        context.put(\"channelStatus\", status);\n    }\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/home/module/screen/AnalysisThroughputHistory.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.home.module.screen;\n\nimport java.text.SimpleDateFormat;\nimport java.util.Date;\nimport java.util.LinkedHashMap;\nimport java.util.Map;\n\nimport javax.annotation.Resource;\nimport javax.servlet.http.HttpSession;\n\nimport org.apache.commons.lang.StringUtils;\n\nimport com.alibaba.citrus.turbine.Context;\nimport com.alibaba.citrus.turbine.dataresolver.Param;\nimport com.alibaba.otter.manager.biz.config.channel.ChannelService;\nimport com.alibaba.otter.manager.biz.statistics.throughput.ThroughputStatService;\nimport com.alibaba.otter.manager.biz.statistics.throughput.param.ThroughputInfo;\nimport com.alibaba.otter.manager.biz.statistics.throughput.param.TimelineThroughputCondition;\nimport com.alibaba.otter.shared.common.model.config.channel.Channel;\nimport com.alibaba.otter.shared.common.model.statistics.throughput.ThroughputType;\n\npublic class AnalysisThroughputHistory {\n\n    @Resource(name = \"channelService\")\n    private ChannelService        channelService;\n\n    @Resource(name = \"throughputStatService\")\n    private ThroughputStatService throughputStatService;\n\n    public void execute(@Param(\"d5221\") String startTime, @Param(\"d5222\") String endTime,\n                        @Param(\"pipelineId\") Long pipelineId, HttpSession session, Context context) throws Exception {\n        Date end = null;\n        Date start = null;\n        SimpleDateFormat sdf = new SimpleDateFormat(\"yyyy-MM-dd HH:mm\");\n        if (StringUtils.isEmpty(startTime) || StringUtils.isEmpty(endTime)) {\n            start = new Date(System.currentTimeMillis() / 60000 * 60000 - 24 * 60 * 60 * 1000);\n            end = new Date(System.currentTimeMillis() / 60000 * 60000);\n        } else {// 当前24小时，时间取整分钟\n            sdf.setLenient(false);\n            if (null != startTime && null != endTime) {\n                start = sdf.parse(startTime);\n                end = sdf.parse(endTime);\n            }\n        }\n\n        Channel channel = channelService.findByPipelineId(pipelineId);\n        Map<Long, ThroughputInfo> throughputInfos1 = new LinkedHashMap<Long, ThroughputInfo>();\n        Map<Long, ThroughputInfo> throughputInfos2 = new LinkedHashMap<Long, ThroughputInfo>();\n        TimelineThroughputCondition condition1 = new TimelineThroughputCondition();\n        TimelineThroughputCondition condition2 = new TimelineThroughputCondition();\n        if (null != start && null != end) {\n            condition1.setStart(start);\n            condition1.setEnd(end);\n            condition1.setType(ThroughputType.ROW);\n            condition1.setPipelineId(pipelineId);\n            condition2.setStart(start);\n            condition2.setEnd(end);\n            condition2.setType(ThroughputType.FILE);\n            condition2.setPipelineId(pipelineId);\n            throughputInfos1 = throughputStatService.listTimelineThroughput(condition1);\n            throughputInfos2 = throughputStatService.listTimelineThroughput(condition2);\n        }\n\n        Long totalRecord1 = 0L;\n        Long totalRecord2 = 0L;\n        Long totalSize1 = 0L;\n        Long totalSize2 = 0L;\n        for (ThroughputInfo info : throughputInfos1.values()) {\n            totalRecord1 += info.getNumber();\n            totalSize1 += info.getSize();\n        }\n\n        for (ThroughputInfo info : throughputInfos2.values()) {\n            totalRecord2 += info.getNumber();\n            totalSize2 += info.getSize();\n        }\n\n        context.put(\"throughputInfos1\", throughputInfos1);\n        context.put(\"throughputInfos2\", throughputInfos2);\n        context.put(\"totalRecord1\", totalRecord1);\n        context.put(\"totalRecord2\", totalRecord2);\n        context.put(\"totalSize1\", totalSize1);\n        context.put(\"totalSize2\", totalSize2);\n        context.put(\"channel\", channel);\n        context.put(\"pipelineId\", pipelineId);\n        context.put(\"start\", sdf.format(start));\n        context.put(\"end\", sdf.format(end));\n    }\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/home/module/screen/AnalysisThroughputStat.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.home.module.screen;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\nimport javax.annotation.Resource;\n\nimport com.alibaba.citrus.turbine.Context;\nimport com.alibaba.citrus.turbine.dataresolver.Param;\nimport com.alibaba.otter.shared.common.model.config.channel.Channel;\nimport com.alibaba.otter.shared.common.model.statistics.throughput.ThroughputStat;\nimport com.alibaba.otter.shared.common.model.statistics.throughput.ThroughputType;\nimport com.alibaba.otter.manager.biz.config.channel.ChannelService;\nimport com.alibaba.otter.manager.biz.statistics.throughput.ThroughputStatService;\nimport com.alibaba.otter.manager.biz.statistics.throughput.param.AnalysisType;\nimport com.alibaba.otter.manager.biz.statistics.throughput.param.RealtimeThroughputCondition;\nimport com.alibaba.otter.manager.biz.statistics.throughput.param.ThroughputCondition;\nimport com.alibaba.otter.manager.biz.statistics.throughput.param.ThroughputInfo;\n\npublic class AnalysisThroughputStat {\n\n    @Resource(name = \"throughputStatService\")\n    private ThroughputStatService throughputStatService;\n\n    @Resource(name = \"channelService\")\n    private ChannelService        channelService;\n\n    public void execute(@Param(\"pipelineId\") Long pipelineId, Context context) throws Exception {\n        Channel channel = channelService.findByPipelineId(pipelineId);\n        RealtimeThroughputCondition condition1 = new RealtimeThroughputCondition();\n        RealtimeThroughputCondition condition2 = new RealtimeThroughputCondition();\n        ThroughputCondition condition11 = new ThroughputCondition();\n        ThroughputCondition condition22 = new ThroughputCondition();\n        List<AnalysisType> analysisType = new ArrayList<AnalysisType>();\n        analysisType.add(AnalysisType.ONE_MINUTE);\n        analysisType.add(AnalysisType.FIVE_MINUTE);\n        analysisType.add(AnalysisType.FIFTEEN_MINUTE);\n        condition1.setPipelineId(pipelineId);\n        condition1.setAnalysisType(analysisType);\n        condition1.setType(ThroughputType.FILE);\n        condition2.setPipelineId(pipelineId);\n        condition2.setAnalysisType(analysisType);\n        condition2.setType(ThroughputType.ROW);\n        condition11.setPipelineId(pipelineId);\n        condition11.setType(ThroughputType.FILE);\n        condition22.setPipelineId(pipelineId);\n        condition22.setType(ThroughputType.ROW);\n        Map<AnalysisType, ThroughputInfo> throughputInfos1 = throughputStatService.listRealtimeThroughput(condition1);\n        Map<AnalysisType, ThroughputInfo> throughputInfos2 = throughputStatService.listRealtimeThroughput(condition2);\n        ThroughputStat throughputStat1 = throughputStatService.findThroughputStatByPipelineId(condition11);\n        ThroughputStat throughputStat2 = throughputStatService.findThroughputStatByPipelineId(condition22);\n\n        context.put(\"throughputInfos1\", throughputInfos1);\n        context.put(\"throughputInfos2\", throughputInfos2);\n        context.put(\"channel\", channel);\n        context.put(\"pipelineId\", pipelineId);\n        context.put(\"throughputStat1\", throughputStat1);\n        context.put(\"throughputStat2\", throughputStat2);\n        context.put(\"one\", AnalysisType.ONE_MINUTE);\n        context.put(\"five\", AnalysisType.FIVE_MINUTE);\n        context.put(\"fifteen\", AnalysisType.FIFTEEN_MINUTE);\n    }\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/home/module/screen/AnalysisTopStat.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.home.module.screen;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport javax.annotation.Resource;\n\nimport com.alibaba.citrus.turbine.Context;\nimport com.alibaba.citrus.turbine.dataresolver.Param;\nimport com.alibaba.otter.manager.biz.statistics.delay.DelayStatService;\nimport com.alibaba.otter.manager.biz.statistics.delay.param.TopDelayStat;\nimport com.alibaba.otter.manager.biz.statistics.delay.param.TopDelayStat.DataStat;\nimport com.alibaba.otter.manager.biz.statistics.throughput.ThroughputStatService;\nimport com.alibaba.otter.shared.arbitrate.ArbitrateManageService;\nimport com.alibaba.otter.shared.arbitrate.ArbitrateViewService;\nimport com.alibaba.otter.shared.arbitrate.model.MainStemEventData;\nimport com.alibaba.otter.shared.common.model.config.channel.ChannelStatus;\nimport com.alibaba.otter.shared.common.model.statistics.throughput.ThroughputStat;\nimport com.alibaba.otter.shared.common.model.statistics.throughput.ThroughputType;\n\npublic class AnalysisTopStat {\n\n    @Resource(name = \"throughputStatService\")\n    private ThroughputStatService  throughputStatService;\n\n    @Resource(name = \"delayStatService\")\n    private DelayStatService       delayStatService;\n\n    @Resource(name = \"arbitrateManageService\")\n    private ArbitrateManageService arbitrateManageService;\n\n    @Resource(name = \"arbitrateViewService\")\n    private ArbitrateViewService   arbitrateViewService;\n\n    public void execute(@Param(\"searchKey\") String searchKey, @Param(\"topN\") int topN, @Param(\"statTime\") int minute,\n                        Context context) throws Exception {\n\n        if (topN <= 0) {\n            topN = 15;\n        }\n\n        if (minute <= 0) {\n            minute = 1;\n        }\n\n        List<TopDelayStat> tops = delayStatService.listTopDelayStat(searchKey, topN);\n\n        List<Long> pipelineIds = new ArrayList<Long>();\n        for (TopDelayStat top : tops) {\n            top.setStatTime(Long.valueOf(minute));\n            pipelineIds.add(top.getPipelineId());\n        }\n\n        Map<Long, ChannelStatus> channelStatuses = new HashMap<Long, ChannelStatus>(tops.size(), 1f);\n        Map<Long, MainStemEventData> mainstemStatuses = new HashMap<Long, MainStemEventData>(tops.size(), 1f);\n\n        if (pipelineIds.size() > 0) {\n            List<ThroughputStat> stats = throughputStatService.listRealtimeThroughputByPipelineIds(pipelineIds, minute);\n            for (ThroughputStat stat : stats) {\n                for (TopDelayStat top : tops) {\n                    if (stat.getPipelineId().equals(top.getPipelineId())) {\n                        DataStat s = new DataStat(stat.getNumber(), stat.getSize());\n                        if (ThroughputType.FILE == stat.getType()) {\n                            top.setFileStat(s);\n                        } else if (ThroughputType.ROW == stat.getType()) {\n                            top.setDbStat(s);\n                        }\n                        break;\n                    }\n                }\n            }\n\n            for (TopDelayStat top : tops) {\n                if (!channelStatuses.containsKey(top.getChannelId())) {\n                    channelStatuses.put(top.getChannelId(),\n                                        arbitrateManageService.channelEvent().status(top.getChannelId()));\n                }\n\n                if (!mainstemStatuses.containsKey(top.getPipelineId())) {\n                    mainstemStatuses.put(top.getPipelineId(),\n                                         arbitrateViewService.mainstemData(top.getChannelId(), top.getPipelineId()));\n                }\n            }\n        }\n\n        context.put(\"tops\", tops);\n        context.put(\"statTime\", minute);\n        context.put(\"channelStatuses\", channelStatuses);\n        context.put(\"mainstemStatuses\", mainstemStatuses);\n    }\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/home/module/screen/AutoKeeperClientPath.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.home.module.screen;\n\nimport java.util.HashSet;\nimport java.util.Set;\n\nimport javax.annotation.Resource;\n\nimport com.alibaba.citrus.turbine.Context;\nimport com.alibaba.citrus.turbine.dataresolver.Param;\nimport com.alibaba.otter.manager.biz.autokeeper.AutoKeeperStatService;\nimport com.alibaba.otter.manager.biz.config.autokeeper.AutoKeeperClusterService;\nimport com.alibaba.otter.shared.common.model.autokeeper.AutoKeeperCluster;\nimport com.alibaba.otter.shared.common.model.autokeeper.AutoKeeperConnectionStat;\nimport com.alibaba.otter.shared.common.model.autokeeper.AutoKeeperServerStat;\n\npublic class AutoKeeperClientPath {\n\n    @Resource(name = \"autoKeeperClusterService\")\n    private AutoKeeperClusterService autoKeeperClusterService;\n\n    @Resource(name = \"autoKeeperStatService\")\n    private AutoKeeperStatService    autoKeeperStatService;\n\n    public void execute(@Param(\"clusterId\") String clusterId, @Param(\"address\") String address, Context context)\n                                                                                                                throws Exception {\n\n        AutoKeeperCluster autoKeeperCluster = autoKeeperClusterService.findAutoKeeperClusterById(Long.valueOf(clusterId));\n        Set<AutoKeeperConnectionStat> autoKeeperConnectionStats = new HashSet<AutoKeeperConnectionStat>();\n        for (String ipAddress : autoKeeperCluster.getServerList()) {\n            if (ipAddress.equalsIgnoreCase(address)) {\n                AutoKeeperServerStat autoKeeperServerStat = autoKeeperStatService.findServerStat(ipAddress);\n                if (autoKeeperServerStat != null) {\n                    autoKeeperConnectionStats = autoKeeperServerStat.getConnectionStats();\n                } else {\n                    autoKeeperConnectionStats = new HashSet<AutoKeeperConnectionStat>();\n                }\n            }\n        }\n\n        context.put(\"autoKeeperConnectionStats\", autoKeeperConnectionStats);\n    }\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/home/module/screen/AutoKeeperClustersDetail.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.home.module.screen;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport javax.annotation.Resource;\n\nimport com.alibaba.citrus.turbine.Context;\nimport com.alibaba.citrus.turbine.dataresolver.Param;\nimport com.alibaba.otter.manager.biz.autokeeper.AutoKeeperStatService;\nimport com.alibaba.otter.manager.biz.config.autokeeper.AutoKeeperClusterService;\nimport com.alibaba.otter.shared.common.model.autokeeper.AutoKeeperCluster;\nimport com.alibaba.otter.shared.common.model.autokeeper.AutoKeeperServerStat;\n\npublic class AutoKeeperClustersDetail {\n\n    @Resource(name = \"autoKeeperClusterService\")\n    private AutoKeeperClusterService autoKeeperClusterService;\n\n    @Resource(name = \"autoKeeperStatService\")\n    private AutoKeeperStatService    autoKeeperStatService;\n\n    public void execute(@Param(\"clusterId\") String clusterId, Context context) throws Exception {\n\n        Map<String, AutoKeeperServerStat> statMap = new HashMap<String, AutoKeeperServerStat>();\n        AutoKeeperCluster autoKeeperCluster = autoKeeperClusterService.findAutoKeeperClusterById(Long.valueOf(clusterId));\n        for (String address : autoKeeperCluster.getServerList()) {\n            AutoKeeperServerStat autoKeeperServerStat = autoKeeperStatService.findServerStat(address);\n            statMap.put(address, autoKeeperServerStat);\n        }\n        context.put(\"clusterId\", clusterId);\n        context.put(\"statMap\", statMap);\n    }\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/home/module/screen/AutoKeeperClustersList.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.home.module.screen;\n\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport javax.annotation.Resource;\n\nimport com.alibaba.citrus.turbine.Context;\nimport com.alibaba.citrus.turbine.dataresolver.Param;\nimport com.alibaba.citrus.util.Paginator;\nimport com.alibaba.otter.manager.biz.config.autokeeper.AutoKeeperClusterService;\nimport com.alibaba.otter.shared.common.model.autokeeper.AutoKeeperCluster;\n\npublic class AutoKeeperClustersList {\n\n    @Resource(name = \"autoKeeperClusterService\")\n    private AutoKeeperClusterService autoKeeperClusterService;\n\n    public void execute(@Param(\"pageIndex\") int pageIndex, @Param(\"searchKey\") String searchKey, Context context)\n                                                                                                                 throws Exception {\n        @SuppressWarnings(\"unchecked\")\n        Map<String, Object> condition = new HashMap<String, Object>();\n        if (\"请输入关键字(目前支持Zookeeper的地址搜索)\".equals(searchKey)) {\n            searchKey = \"\";\n        }\n        condition.put(\"searchKey\", searchKey);\n\n        int count = autoKeeperClusterService.getCount();\n        Paginator paginator = new Paginator();\n        paginator.setItems(count);\n        paginator.setPage(pageIndex);\n\n        condition.put(\"offset\", paginator.getOffset());\n        condition.put(\"length\", paginator.getLength());\n\n        List<AutoKeeperCluster> autoKeeperClusters = autoKeeperClusterService.listAutoKeeperClusters();\n\n        context.put(\"paginator\", paginator);\n        context.put(\"autoKeeperClusters\", autoKeeperClusters);\n    }\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/home/module/screen/BehaviorHistoryCurve.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.home.module.screen;\n\nimport java.text.SimpleDateFormat;\nimport java.util.Date;\nimport java.util.LinkedHashMap;\nimport java.util.Map;\n\nimport javax.annotation.Resource;\nimport javax.servlet.http.HttpSession;\n\nimport org.apache.commons.lang.StringUtils;\n\nimport com.alibaba.citrus.turbine.Context;\nimport com.alibaba.citrus.turbine.dataresolver.Param;\nimport com.alibaba.otter.manager.biz.config.channel.ChannelService;\nimport com.alibaba.otter.manager.biz.config.datamediapair.DataMediaPairService;\nimport com.alibaba.otter.manager.biz.statistics.table.TableStatService;\nimport com.alibaba.otter.manager.biz.statistics.table.param.BehaviorHistoryInfo;\nimport com.alibaba.otter.manager.biz.statistics.table.param.TimelineBehaviorHistoryCondition;\nimport com.alibaba.otter.shared.common.model.config.channel.Channel;\nimport com.alibaba.otter.shared.common.model.config.data.DataMediaPair;\n\npublic class BehaviorHistoryCurve {\n\n    @Resource(name = \"channelService\")\n    private ChannelService       channelService;\n\n    @Resource(name = \"dataMediaPairService\")\n    private DataMediaPairService dataMediaPairService;\n\n    @Resource(name = \"tableStatService\")\n    private TableStatService     tableStatService;\n\n    public void execute(@Param(\"d5221\") String startTime, @Param(\"d5222\") String endTime,\n                        @Param(\"dataMediaPairId\") Long dataMediaPairId, HttpSession session, Context context)\n                                                                                                             throws Exception {\n        Date end = null;\n        Date start = null;\n        SimpleDateFormat sdf = new SimpleDateFormat(\"yyyy-MM-dd HH:mm\");\n        if (StringUtils.isEmpty(startTime) || StringUtils.isEmpty(endTime)) {\n            start = new Date(System.currentTimeMillis() / 60000 * 60000 - 24 * 60 * 60 * 1000);\n            end = new Date(System.currentTimeMillis() / 60000 * 60000);\n        } else {// 当前24小时，时间取整分钟\n            sdf.setLenient(false);\n            if (null != startTime && null != endTime) {\n                start = sdf.parse(startTime);\n                end = sdf.parse(endTime);\n            }\n        }\n\n        DataMediaPair dataMediaPair = dataMediaPairService.findById(dataMediaPairId);\n        Channel channel = channelService.findByPipelineId(dataMediaPair.getPipelineId());\n\n        Map<Long, BehaviorHistoryInfo> behaviourHistoryInfos = new LinkedHashMap<Long, BehaviorHistoryInfo>();\n\n        TimelineBehaviorHistoryCondition condition = new TimelineBehaviorHistoryCondition();\n\n        if (null != start && null != end) {\n            condition.setStart(start);\n            condition.setEnd(end);\n            condition.setPairId(dataMediaPairId);\n            behaviourHistoryInfos = tableStatService.listTimelineBehaviorHistory(condition);\n        }\n\n        Long totalInsert = 0L;\n        Long totalUpdate = 0L;\n        Long totalDelete = 0L;\n        Long totalFileCount = 0L;\n        Long totalFileSize = 0L;\n        for (BehaviorHistoryInfo info : behaviourHistoryInfos.values()) {\n            totalInsert += info.getInsertNumber();\n            totalUpdate += info.getUpdateNumber();\n            totalDelete += info.getDeleteNumber();\n            totalFileCount += info.getFileNumber();\n            totalFileSize += info.getFileSize();\n        }\n\n        context.put(\"totalInsert\", totalInsert);\n        context.put(\"totalUpdate\", totalUpdate);\n        context.put(\"totalDelete\", totalDelete);\n        context.put(\"totalFileCount\", totalFileCount);\n        context.put(\"totalFileSize\", totalFileSize);\n        context.put(\"behaviourHistoryInfos\", behaviourHistoryInfos);\n        context.put(\"start\", sdf.format(start));\n        context.put(\"end\", sdf.format(end));\n        context.put(\"dataMediaPair\", dataMediaPair);\n        context.put(\"channel\", channel);\n    }\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/home/module/screen/CanalInfo.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.home.module.screen;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport javax.annotation.Resource;\n\nimport com.alibaba.citrus.turbine.Context;\nimport com.alibaba.citrus.turbine.dataresolver.Param;\nimport com.alibaba.otter.canal.instance.manager.model.Canal;\nimport com.alibaba.otter.manager.biz.config.autokeeper.AutoKeeperClusterService;\nimport com.alibaba.otter.manager.biz.config.canal.CanalService;\nimport com.alibaba.otter.manager.biz.config.channel.ChannelService;\nimport com.alibaba.otter.manager.biz.config.pipeline.PipelineService;\nimport com.alibaba.otter.manager.web.common.CanalExtraParamUtil;\nimport com.alibaba.otter.shared.common.model.autokeeper.AutoKeeperCluster;\nimport com.alibaba.otter.shared.common.model.config.channel.Channel;\nimport com.alibaba.otter.shared.common.model.config.pipeline.Pipeline;\n\npublic class CanalInfo {\n\n    @Resource(name = \"canalService\")\n    private CanalService             canalService;\n\n    @Resource(name = \"autoKeeperClusterService\")\n    private AutoKeeperClusterService autoKeeperClusterService;\n\n    @Resource(name = \"pipelineService\")\n    private PipelineService          pipelineService;\n\n    @Resource(name = \"channelService\")\n    private ChannelService           channelService;\n\n    public void execute(@Param(\"canalId\") Long canalId, Context context) throws Exception {\n        Canal canal = canalService.findById(canalId);\n        AutoKeeperCluster zkCluster = autoKeeperClusterService.findAutoKeeperClusterById(canal.getCanalParameter()\n            .getZkClusterId());\n\n        List<Pipeline> pipelines = pipelineService.listByDestinationWithoutOther(canal.getName());\n        List<Long> channelIds = new ArrayList<Long>();\n        for (Pipeline pipeline : pipelines) {\n            channelIds.add(pipeline.getChannelId());\n        }\n\n        List<Channel> channels = channelService.listOnlyChannels(channelIds.toArray(new Long[channelIds.size()]));\n        Map<Long, Channel> channelMap = new HashMap<Long, Channel>();\n        for (Channel channel : channels) {\n            channelMap.put(channel.getId(), channel);\n        }\n\n        context.put(\"canal\", canal);\n        context.put(\"pipelines\", pipelines);\n        context.put(\"channelMap\", channelMap);\n        context.put(\"zkCluster\", zkCluster);\n\n        context.put(\"canalExtra\", CanalExtraParamUtil.getExtraParamMap(canal.getCanalParameter()));\n    }\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/home/module/screen/CanalList.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.home.module.screen;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport javax.annotation.Resource;\n\nimport com.alibaba.citrus.turbine.Context;\nimport com.alibaba.citrus.turbine.dataresolver.Param;\nimport com.alibaba.citrus.util.Paginator;\nimport com.alibaba.otter.canal.instance.manager.model.Canal;\nimport com.alibaba.otter.manager.biz.config.canal.CanalService;\nimport com.alibaba.otter.manager.biz.config.pipeline.PipelineService;\nimport com.alibaba.otter.manager.web.common.model.SeniorCanal;\nimport com.alibaba.otter.shared.common.model.config.pipeline.Pipeline;\n\n/**\n * @author sarah.lij 2012-7-26 下午04:25:52\n */\npublic class CanalList {\n\n    @Resource(name = \"canalService\")\n    private CanalService    canalService;\n\n    @Resource(name = \"pipelineService\")\n    private PipelineService pipelineService;\n\n    public void execute(@Param(\"pageIndex\") int pageIndex, @Param(\"searchKey\") String searchKey, Context context)\n                                                                                                                 throws Exception {\n        @SuppressWarnings(\"unchecked\")\n        Map<String, Object> condition = new HashMap<String, Object>();\n        if (\"请输入关键字(目前支持Node的ID、名字搜索)\".equals(searchKey)) {\n            searchKey = \"\";\n        }\n        condition.put(\"searchKey\", searchKey);\n\n        int count = canalService.getCount(condition);\n        Paginator paginator = new Paginator();\n        paginator.setItems(count);\n        paginator.setPage(pageIndex);\n\n        condition.put(\"offset\", paginator.getOffset());\n        condition.put(\"length\", paginator.getLength());\n\n        List<Canal> canals = canalService.listByCondition(condition);\n\n        List<SeniorCanal> seniorCanals = new ArrayList<SeniorCanal>();\n\n        for (Canal canal : canals) {\n            SeniorCanal seniorCanal = new SeniorCanal();\n            seniorCanal.setId(canal.getId());\n            seniorCanal.setName(canal.getName());\n            seniorCanal.setStatus(canal.getStatus());\n            seniorCanal.setDesc(canal.getDesc());\n            seniorCanal.setCanalParameter(canal.getCanalParameter());\n            seniorCanal.setGmtCreate(canal.getGmtCreate());\n            seniorCanal.setGmtModified(canal.getGmtModified());\n\n            List<Pipeline> pipelines = pipelineService.listByDestinationWithoutOther(canal.getName());\n            seniorCanal.setPipelines(pipelines);\n            seniorCanal.setUsed(!pipelines.isEmpty());\n            seniorCanals.add(seniorCanal);\n        }\n        context.put(\"seniorCanals\", seniorCanals);\n        context.put(\"paginator\", paginator);\n        context.put(\"searchKey\", searchKey);\n    }\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/home/module/screen/ChannelInfo.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.home.module.screen;\n\nimport javax.annotation.Resource;\n\nimport com.alibaba.citrus.turbine.Context;\nimport com.alibaba.citrus.turbine.dataresolver.Param;\nimport com.alibaba.otter.shared.common.model.config.channel.Channel;\nimport com.alibaba.otter.manager.biz.config.channel.ChannelService;\n\npublic class ChannelInfo {\n\n    @Resource(name = \"channelService\")\n    private ChannelService channelService;\n\n    public void execute(@Param(\"channelId\") Long channelId, Context context) throws Exception {\n        Channel channel = channelService.findById(channelId);\n\n        context.put(\"channel\", channel);\n    }\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/home/module/screen/ChannelList.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.home.module.screen;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport javax.annotation.Resource;\n\nimport com.alibaba.citrus.turbine.Context;\nimport com.alibaba.citrus.turbine.dataresolver.Param;\nimport com.alibaba.citrus.util.Paginator;\nimport com.alibaba.otter.manager.biz.config.channel.ChannelService;\nimport com.alibaba.otter.manager.biz.statistics.stage.ProcessStatService;\nimport com.alibaba.otter.manager.web.common.model.SeniorChannel;\nimport com.alibaba.otter.shared.arbitrate.ArbitrateManageService;\nimport com.alibaba.otter.shared.common.model.config.channel.Channel;\nimport com.alibaba.otter.shared.common.model.config.channel.ChannelStatus;\nimport com.alibaba.otter.shared.common.model.config.pipeline.Pipeline;\n\npublic class ChannelList {\n\n    @Resource(name = \"channelService\")\n    private ChannelService         channelService;\n\n    @Resource(name = \"processStatService\")\n    private ProcessStatService     processStatService;\n\n    @Resource(name = \"arbitrateManageService\")\n    private ArbitrateManageService arbitrateManageService;\n\n    public void execute(@Param(\"pageIndex\") int pageIndex, @Param(\"searchKey\") String searchKey,\n                        @Param(\"channelStatus\") String status, @Param(\"channelId\") Long channelId,\n                        @Param(\"errorType\") String errorType, Context context) throws Exception {\n        @SuppressWarnings(\"unchecked\")\n        Map<String, Object> condition = new HashMap<String, Object>();\n\n        if (\"请输入关键字(目前支持Channel的ID、名字搜索)\".equals(searchKey)) {\n            searchKey = \"\";\n        }\n        condition.put(\"searchKey\", searchKey);\n\n        List<Long> theStatusPks = new ArrayList<Long>();\n        if (null != status) {\n            List<Long> allChannelPks = channelService.listAllChannelId();\n\n            for (Long channelPk : allChannelPks) {\n                ChannelStatus channelStatus = arbitrateManageService.channelEvent().status(channelPk);\n                if (channelStatus.equals(ChannelStatus.valueOf(status))) {\n                    theStatusPks.add(channelPk);\n                }\n            }\n        }\n\n        int count = channelService.getCount(condition);\n        Paginator paginator = new Paginator();\n        paginator.setItems(count);\n        paginator.setPage(pageIndex);\n\n        condition.put(\"offset\", paginator.getOffset());\n        condition.put(\"length\", paginator.getLength());\n        List<Channel> channels = new ArrayList<Channel>();\n\n        if ((null != channelId) && (channelId != 0l)) {\n            channels.add(channelService.findById(channelId));\n            paginator.setItems(1);\n            paginator.setPage(0);\n            searchKey = String.valueOf(channelId); // 定义为新的searchKey\n        } else {\n            channels = channelService.listByConditionWithoutColumn(condition);\n        }\n\n        List<SeniorChannel> seniorChannels = new ArrayList<SeniorChannel>();\n        for (Channel channel : channels) {\n            boolean processEmpty = false;\n            List<Pipeline> pipelines = channel.getPipelines();\n            for (Pipeline pipeline : pipelines) {\n                if (processStatService.listRealtimeProcessStat(channel.getId(), pipeline.getId()).isEmpty()) {\n                    processEmpty = true;\n                }\n            }\n            SeniorChannel seniorChannel = new SeniorChannel();\n            seniorChannel.setId(channel.getId());\n            seniorChannel.setName(channel.getName());\n            seniorChannel.setParameters(channel.getParameters());\n            seniorChannel.setPipelines(channel.getPipelines());\n            seniorChannel.setStatus(channel.getStatus());\n            seniorChannel.setDescription(channel.getDescription());\n            seniorChannel.setGmtCreate(channel.getGmtCreate());\n            seniorChannel.setGmtModified(channel.getGmtModified());\n            seniorChannel.setProcessEmpty(processEmpty);\n            seniorChannels.add(seniorChannel);\n        }\n\n        context.put(\"channels\", seniorChannels);\n        context.put(\"paginator\", paginator);\n        context.put(\"searchKey\", searchKey);\n        context.put(\"errorType\", errorType);\n    }\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/home/module/screen/CheckDelayStat.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.home.module.screen;\n\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.Iterator;\nimport java.util.Map;\nimport java.util.Set;\n\nimport javax.annotation.Resource;\n\nimport org.apache.commons.lang.math.NumberUtils;\nimport org.apache.commons.logging.Log;\nimport org.apache.commons.logging.LogFactory;\n\nimport com.alibaba.citrus.turbine.Context;\nimport com.alibaba.citrus.turbine.dataresolver.Param;\nimport com.alibaba.citrus.webx.WebxException;\nimport com.alibaba.otter.manager.biz.config.channel.ChannelService;\nimport com.alibaba.otter.manager.biz.statistics.delay.DelayStatService;\nimport com.alibaba.otter.manager.biz.statistics.throughput.ThroughputStatService;\nimport com.alibaba.otter.manager.biz.statistics.throughput.param.ThroughputCondition;\nimport com.alibaba.otter.shared.common.model.config.channel.Channel;\nimport com.alibaba.otter.shared.common.model.statistics.delay.DelayStat;\nimport com.alibaba.otter.shared.common.model.statistics.throughput.ThroughputStat;\nimport com.alibaba.otter.shared.common.model.statistics.throughput.ThroughputType;\n\n/**\n * 类CheckQueueSize.java的实现描述：TODO 类实现描述\n * \n * @author simon 2011-12-30 下午04:01:17\n */\npublic class CheckDelayStat {\n\n    private static final Log      logger      = LogFactory.getLog(CheckDelayStat.class);\n\n    @Resource(name = \"delayStatService\")\n    private DelayStatService      delayStatService;\n\n    @Resource(name = \"throughputStatService\")\n    private ThroughputStatService throughputStatService;\n\n    @Resource(name = \"channelService\")\n    private ChannelService        channelService;\n\n    private static final int      MAX_TIMEOUT = 30;                                     // 超时的最大时间，单位分钟\n\n    private static Map<Long, Long> parseAlert(String alert) {\n        if (alert == null) {\n            return null;\n        }\n\n        Map<Long, Long> alertMap = new HashMap<Long, Long>();\n        String[] alerts = alert.split(\",\");\n\n        for (int i = 0; i < alerts.length; i++) {\n            String[] ncidAlert = alerts[i].split(\"-\");\n\n            alertMap.put(NumberUtils.toLong(ncidAlert[0], 0), NumberUtils.toLong(ncidAlert[1], 0));\n\n            if (logger.isInfoEnabled()) {\n                logger.info(ncidAlert[0] + \" : \" + ncidAlert[1]);\n            }\n        }\n\n        return alertMap;\n    }\n\n    public static void main(String[] args) {\n        Map<Long, Long> alertMap = parseAlert(\"20-1000000, 30-20000\");\n\n        for (Long pipelineId : alertMap.keySet()) {\n            System.out.println(pipelineId + \" : \" + alertMap.get(pipelineId));\n        }\n    }\n\n    public void execute(@Param(\"queueSize\") String queueSize, @Param(\"delayTime\") String delayTime,\n                        @Param(\"timeout\") String timeout, Context context) throws WebxException {\n\n        Map<Long, Long> queueSizeMap = parseAlert(queueSize);\n        Map<Long, Long> delayTimeMap = parseAlert(delayTime);\n        Map<Long, Long> timeoutMap = parseAlert(timeout);\n        Boolean result = true;\n\n        if ((queueSizeMap != null) && (false == queueSizeMap.isEmpty())) {\n            Set<Long> key = queueSizeMap.keySet();\n            for (Iterator it = key.iterator(); it.hasNext();) {\n                Long pipelineId = (Long) it.next();\n\n                Channel channel = channelService.findByPipelineId(pipelineId);\n                // 判断channel状态，只有启动状态才进行判断超时时间\n                if (!channel.getStatus().isStop()) {\n\n                    DelayStat delayStat = delayStatService.findRealtimeDelayStat(pipelineId);\n                    logger.info(\"delayStat.getDelayNumber() == \" + delayStat.getDelayNumber());\n\n                    if (null != delayStat.getDelayNumber()\n                        && delayStat.getDelayNumber() >= queueSizeMap.get(pipelineId)) {\n                        result = false;\n                    }\n                }\n            }\n        }\n        if ((delayTimeMap != null) && (false == delayTimeMap.isEmpty())) {\n            Set<Long> key = delayTimeMap.keySet();\n            for (Iterator it = key.iterator(); it.hasNext();) {\n                Long pipelineId = (Long) it.next();\n                Channel channel = channelService.findByPipelineId(pipelineId);\n                // 判断channel状态，只有启动状态才进行判断超时时间\n                if (!channel.getStatus().isStop()) {\n                    DelayStat delayStat = delayStatService.findRealtimeDelayStat(pipelineId);\n                    logger.info(\"delayStat.getDelayTime() == \" + delayStat.getDelayTime());\n\n                    if (null != delayStat.getDelayTime() && delayStat.getDelayTime() >= delayTimeMap.get(pipelineId)) {\n                        result = false;\n                    }\n                }\n            }\n        }\n\n        if ((timeoutMap != null) && (false == timeoutMap.isEmpty())) {\n            Set<Long> key = timeoutMap.keySet();\n            for (Iterator it = key.iterator(); it.hasNext();) {\n                Long pipelineId = (Long) it.next();\n                Channel channel = channelService.findByPipelineId(pipelineId);\n                // 判断channel状态，只有启动状态才进行判断超时时间\n                if (!channel.getStatus().isStop()) {\n                    ThroughputCondition condition = new ThroughputCondition();\n                    condition.setPipelineId(pipelineId);\n                    condition.setType(ThroughputType.ROW);\n                    ThroughputStat throughputStat = throughputStatService.findThroughputStatByPipelineId(condition);\n\n                    if (null != throughputStat.getGmtModified()) {\n                        Date now = new Date();\n                        long time = now.getTime() - throughputStat.getGmtModified().getTime();\n                        logger.info(\"timeout == \" + time + \"(ms)\");\n\n                        long timeout_min = MAX_TIMEOUT; // 单位为分钟\n                        if (timeoutMap.containsKey(pipelineId)) {\n                            timeout_min = timeoutMap.get(pipelineId);\n                        }\n                        if (time / (1000 * 60) > timeout_min) {\n                            result = false;\n                        }\n                    }\n                }\n            }\n        }\n        context.put(\"result\", result);\n\n    }\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/home/module/screen/DataMatrixInfo.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.home.module.screen;\n\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport javax.annotation.Resource;\n\nimport com.alibaba.citrus.turbine.Context;\nimport com.alibaba.citrus.turbine.dataresolver.Param;\nimport com.alibaba.otter.manager.biz.config.datamatrix.DataMatrixService;\nimport com.alibaba.otter.manager.biz.config.datamediasource.DataMediaSourceService;\nimport com.alibaba.otter.shared.common.model.config.data.DataMatrix;\nimport com.alibaba.otter.shared.common.model.config.data.DataMediaSource;\n\npublic class DataMatrixInfo {\n\n    @Resource(name = \"dataMatrixService\")\n    private DataMatrixService      dataMatrixService;\n\n    @Resource(name = \"dataMediaSourceService\")\n    private DataMediaSourceService dataMediaSourceService;\n\n    public void execute(@Param(\"matrixId\") Long matrixId, Context context) throws Exception {\n        DataMatrix matrix = dataMatrixService.findById(matrixId);\n\n        Map condition = new HashMap();\n        condition.put(\"searchKey\", \"jdbc:mysql://groupKey=\" + matrix.getGroupKey());\n        List<DataMediaSource> dataSources = dataMediaSourceService.listByCondition(condition);\n\n        context.put(\"dataMatrix\", matrix);\n        context.put(\"dataSources\", dataSources);\n    }\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/home/module/screen/DataMatrixList.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.home.module.screen;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport javax.annotation.Resource;\n\nimport org.apache.commons.lang.StringUtils;\nimport org.springframework.util.CollectionUtils;\n\nimport com.alibaba.citrus.turbine.Context;\nimport com.alibaba.citrus.turbine.dataresolver.Param;\nimport com.alibaba.citrus.util.Paginator;\nimport com.alibaba.otter.manager.biz.config.datamatrix.DataMatrixService;\nimport com.alibaba.otter.manager.biz.config.datamediasource.DataMediaSourceService;\nimport com.alibaba.otter.manager.web.common.model.SeniorDataMatrix;\nimport com.alibaba.otter.shared.common.model.config.data.DataMatrix;\nimport com.alibaba.otter.shared.common.model.config.data.DataMediaSource;\n\n/**\n * @author sarah.lij 2012-7-26 下午04:25:52\n */\npublic class DataMatrixList {\n\n    @Resource(name = \"dataMatrixService\")\n    private DataMatrixService      dataMatrixService;\n\n    @Resource(name = \"dataMediaSourceService\")\n    private DataMediaSourceService dataMediaSourceService;\n\n    public void execute(@Param(\"pageIndex\") int pageIndex, @Param(\"matrixId\") Long matrixId,\n                        @Param(\"groupKey\") String groupKey, @Param(\"searchKey\") String searchKey, Context context)\n                                                                                                                  throws Exception {\n        Map<String, Object> condition = new HashMap<String, Object>();\n        if (\"请输入关键字(目前支持Node的ID、名字搜索)\".equals(searchKey)) {\n            searchKey = \"\";\n        }\n        condition.put(\"searchKey\", searchKey);\n\n        int count = dataMatrixService.getCount(condition);\n        Paginator paginator = new Paginator();\n        paginator.setItems(count);\n        paginator.setPage(pageIndex);\n\n        condition.put(\"offset\", paginator.getOffset());\n        condition.put(\"length\", paginator.getLength());\n        List<DataMatrix> matrixs = new ArrayList<DataMatrix>();\n        if ((null != matrixId) && (matrixId != 0l)) {\n            DataMatrix matrix = dataMatrixService.findById(matrixId);\n            matrixs.add(matrix);\n            paginator.setItems(1);\n            paginator.setPage(0);\n            searchKey = String.valueOf(matrixId); // 定义为新的searchKey\n        } else if (StringUtils.isNotEmpty(groupKey)) {\n            DataMatrix matrix = dataMatrixService.findByGroupKey(groupKey);\n            matrixs.add(matrix);\n            paginator.setItems(1);\n            paginator.setPage(0);\n            searchKey = String.valueOf(groupKey); // 定义为新的searchKey\n        } else {\n            matrixs = dataMatrixService.listByCondition(condition);\n        }\n\n        List<SeniorDataMatrix> seniorMatrixs = new ArrayList<SeniorDataMatrix>();\n        for (DataMatrix matrix : matrixs) {\n            SeniorDataMatrix seniorMatrix = new SeniorDataMatrix();\n            seniorMatrix.setId(matrix.getId());\n            seniorMatrix.setGroupKey(matrix.getGroupKey());\n            seniorMatrix.setMaster(matrix.getMaster());\n            seniorMatrix.setSlave(matrix.getSlave());\n            seniorMatrix.setGmtCreate(matrix.getGmtCreate());\n            seniorMatrix.setGmtModified(matrix.getGmtModified());\n\n            Map dataSourceCondition = new HashMap();\n            condition.put(\"searchKey\", \"jdbc:mysql://groupKey=\" + matrix.getGroupKey());\n            List<DataMediaSource> dataSources = dataMediaSourceService.listByCondition(dataSourceCondition);\n            seniorMatrix.setUsed(!CollectionUtils.isEmpty(dataSources));\n\n            seniorMatrixs.add(seniorMatrix);\n        }\n\n        context.put(\"dataMatrixs\", seniorMatrixs);\n        context.put(\"paginator\", paginator);\n        context.put(\"searchKey\", searchKey);\n    }\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/home/module/screen/DataMediaInfo.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.home.module.screen;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport javax.annotation.Resource;\n\nimport com.alibaba.citrus.turbine.Context;\nimport com.alibaba.citrus.turbine.dataresolver.Param;\nimport com.alibaba.otter.manager.biz.config.channel.ChannelService;\nimport com.alibaba.otter.manager.biz.config.datamedia.DataMediaService;\nimport com.alibaba.otter.manager.biz.config.datamediapair.DataMediaPairService;\nimport com.alibaba.otter.manager.web.common.model.SeniorDataMediaPair;\nimport com.alibaba.otter.shared.common.model.config.data.DataMedia;\nimport com.alibaba.otter.shared.common.model.config.data.DataMediaPair;\n\npublic class DataMediaInfo {\n\n    @Resource(name = \"dataMediaService\")\n    private DataMediaService     dataMediaService;\n\n    @Resource(name = \"dataMediaPairService\")\n    private DataMediaPairService dataMediaPairService;\n\n    @Resource(name = \"channelService\")\n    private ChannelService       channelService;\n\n    public void execute(@Param(\"dataMediaId\") Long dataMediaId, Context context) throws Exception {\n        DataMedia dataMedia = dataMediaService.findById(dataMediaId);\n\n        List<DataMediaPair> dataMediaPairs = dataMediaPairService.listByDataMediaId(dataMediaId);\n\n        List<SeniorDataMediaPair> seniorDataMediapairs = new ArrayList<SeniorDataMediaPair>();\n        for (DataMediaPair dataMediaPair : dataMediaPairs) {\n            SeniorDataMediaPair seniorDataMediaPair = new SeniorDataMediaPair();\n            seniorDataMediaPair.setChannel(channelService.findByPipelineId(dataMediaPair.getPipelineId()));\n            seniorDataMediaPair.setDataMediaPair(dataMediaPair);\n            seniorDataMediapairs.add(seniorDataMediaPair);\n        }\n\n        context.put(\"dataMedia\", dataMedia);\n        context.put(\"seniorDataMediapairs\", seniorDataMediapairs);\n    }\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/home/module/screen/DataMediaList.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.home.module.screen;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport javax.annotation.Resource;\n\nimport com.alibaba.citrus.turbine.Context;\nimport com.alibaba.citrus.turbine.dataresolver.Param;\nimport com.alibaba.citrus.util.Paginator;\nimport com.alibaba.otter.manager.biz.config.datamedia.DataMediaService;\nimport com.alibaba.otter.manager.biz.config.datamediapair.DataMediaPairService;\nimport com.alibaba.otter.manager.web.common.model.SeniorDataMedia;\nimport com.alibaba.otter.shared.common.model.config.data.DataMedia;\nimport com.alibaba.otter.shared.common.model.config.data.DataMediaPair;\n\npublic class DataMediaList {\n\n    @Resource(name = \"dataMediaService\")\n    private DataMediaService     dataMediaService;\n\n    @Resource(name = \"dataMediaPairService\")\n    private DataMediaPairService dataMediaPairService;\n\n    public void execute(@Param(\"pageIndex\") int pageIndex, @Param(\"searchKey\") String searchKey, Context context)\n                                                                                                                 throws Exception {\n        @SuppressWarnings(\"unchecked\")\n        Map<String, Object> condition = new HashMap<String, Object>();\n        if (\"请输入关键字(目前支持DataMedia的ID、名字搜索)\".equals(searchKey)) {\n            searchKey = \"\";\n        }\n        condition.put(\"searchKey\", searchKey);\n\n        int count = dataMediaService.getCount(condition);\n        Paginator paginator = new Paginator();\n        paginator.setItems(count);\n        paginator.setPage(pageIndex);\n\n        condition.put(\"offset\", paginator.getOffset());\n        condition.put(\"length\", paginator.getLength());\n\n        List<DataMedia> dataMedias = dataMediaService.listByCondition(condition);\n        List<SeniorDataMedia> seniorDataMedias = new ArrayList<SeniorDataMedia>();\n        for (DataMedia dataMedia : dataMedias) {\n            SeniorDataMedia seniorDataMedia = new SeniorDataMedia();\n            seniorDataMedia.setId(dataMedia.getId());\n            seniorDataMedia.setEncode(dataMedia.getEncode());\n            seniorDataMedia.setGmtCreate(dataMedia.getGmtCreate());\n            seniorDataMedia.setGmtModified(dataMedia.getGmtModified());\n            seniorDataMedia.setName(dataMedia.getName());\n            seniorDataMedia.setNamespace(dataMedia.getNamespace());\n            seniorDataMedia.setSource(dataMedia.getSource());\n            List<DataMediaPair> pairs = dataMediaPairService.listByDataMediaId(dataMedia.getId());\n            seniorDataMedia.setPairs(pairs);\n            if (pairs.size() < 1) {\n                seniorDataMedia.setUsed(false);\n            } else {\n                seniorDataMedia.setUsed(true);\n            }\n            seniorDataMedias.add(seniorDataMedia);\n        }\n\n        context.put(\"dataMedias\", seniorDataMedias);\n        context.put(\"paginator\", paginator);\n        context.put(\"searchKey\", searchKey);\n    }\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/home/module/screen/DataMediaPairInfo.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.home.module.screen;\n\nimport java.util.List;\n\nimport javax.annotation.Resource;\n\nimport org.springframework.util.CollectionUtils;\n\nimport com.alibaba.citrus.turbine.Context;\nimport com.alibaba.citrus.turbine.dataresolver.Param;\nimport com.alibaba.otter.manager.biz.config.channel.ChannelService;\nimport com.alibaba.otter.manager.biz.config.datamediapair.DataMediaPairService;\nimport com.alibaba.otter.shared.common.model.config.channel.Channel;\nimport com.alibaba.otter.shared.common.model.config.data.ColumnGroup;\nimport com.alibaba.otter.shared.common.model.config.data.ColumnPair;\nimport com.alibaba.otter.shared.common.model.config.data.DataMediaPair;\n\npublic class DataMediaPairInfo {\n\n    @Resource(name = \"channelService\")\n    private ChannelService       channelService;\n\n    @Resource(name = \"dataMediaPairService\")\n    private DataMediaPairService dataMediaPairService;\n\n    public void execute(@Param(\"dataMediaPairId\") Long dataMediaPairId, Context context) throws Exception {\n        DataMediaPair dataMediaPair = dataMediaPairService.findById(dataMediaPairId);\n        Channel channel = channelService.findByPipelineId(dataMediaPair.getPipelineId());\n\n        List<ColumnPair> columnPairs = dataMediaPair.getColumnPairs();\n        List<ColumnGroup> columnGroups = dataMediaPair.getColumnGroups();\n        // 暂时策略，只拿出list的第一个Group\n        ColumnGroup columnGroup = new ColumnGroup();\n        if (!CollectionUtils.isEmpty(columnGroups)) {\n            columnGroup = columnGroups.get(0);\n        }\n\n        context.put(\"dataMediaPair\", dataMediaPair);\n        context.put(\"columnGroup\", columnGroup);\n        context.put(\"columnPairs\", columnPairs);\n        context.put(\"channelId\", channel.getId());\n    }\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/home/module/screen/DataMediaPairList.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.home.module.screen;\n\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport javax.annotation.Resource;\n\nimport com.alibaba.citrus.turbine.Context;\nimport com.alibaba.citrus.turbine.dataresolver.Param;\nimport com.alibaba.otter.shared.common.model.config.channel.Channel;\nimport com.alibaba.otter.shared.common.model.config.data.DataMediaPair;\nimport com.alibaba.otter.shared.common.model.statistics.table.TableStat;\nimport com.alibaba.otter.manager.biz.config.channel.ChannelService;\nimport com.alibaba.otter.manager.biz.config.datamediapair.DataMediaPairService;\nimport com.alibaba.otter.manager.biz.statistics.table.TableStatService;\n\npublic class DataMediaPairList {\n\n    @Resource(name = \"dataMediaPairService\")\n    private DataMediaPairService dataMediaPairService;\n\n    @Resource(name = \"channelService\")\n    private ChannelService       channelService;\n\n    @Resource(name = \"tableStatService\")\n    private TableStatService     tableStatService;\n\n    public void execute(@Param(\"pipelineId\") Long pipelineId, Context context) throws Exception {\n        // Pipeline pipeline = pipelineService.findById(pipelineId);\n        Channel channel = channelService.findByPipelineId(pipelineId);\n        List<DataMediaPair> dataMediaPairs = dataMediaPairService.listByPipelineId(pipelineId);\n        Map<Long, TableStat> tableStatMap = new HashMap<Long, TableStat>(dataMediaPairs.size(), 1f);\n        List<TableStat> tableStats = tableStatService.listTableStat(pipelineId);\n\n        for (DataMediaPair dataMediaPair : dataMediaPairs) {\n            int flag = 0;\n            for (TableStat tableStat : tableStats) {\n                if (dataMediaPair.getId().equals(tableStat.getDataMediaPairId())) {\n                    tableStatMap.put(dataMediaPair.getId(), tableStat);\n                    flag = 1;\n                    break;\n                }\n            }\n            if (flag == 0) {\n                TableStat tableStat = new TableStat();\n                tableStat.setFileSize(0L);\n                tableStat.setFileCount(0L);\n                tableStat.setDeleteCount(0L);\n                tableStat.setUpdateCount(0L);\n                tableStat.setInsertCount(0L);\n                // tableStat.setGmtModified(dataMediaPair.getGmtModified());\n                tableStatMap.put(dataMediaPair.getId(), tableStat);\n            }\n        }\n\n        context.put(\"dataMediaPairs\", dataMediaPairs);\n\n        // 通过PipelineId不能获取到Channel状态，所以需要传递Channel对象\n\n        context.put(\"channel\", channel);\n        context.put(\"pipelineId\", pipelineId);\n        context.put(\"tableStatMap\", tableStatMap);\n    }\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/home/module/screen/DataSourceInfo.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.home.module.screen;\n\nimport java.util.List;\n\nimport javax.annotation.Resource;\n\nimport com.alibaba.citrus.turbine.Context;\nimport com.alibaba.citrus.turbine.dataresolver.Param;\nimport com.alibaba.otter.manager.biz.config.datamedia.DataMediaService;\nimport com.alibaba.otter.manager.biz.config.datamediasource.DataMediaSourceService;\nimport com.alibaba.otter.shared.common.model.config.data.DataMedia;\nimport com.alibaba.otter.shared.common.model.config.data.DataMediaSource;\n\npublic class DataSourceInfo {\n\n    @Resource(name = \"dataMediaSourceService\")\n    private DataMediaSourceService dataMediaSourceService;\n\n    @Resource(name = \"dataMediaService\")\n    private DataMediaService       dataMediaService;\n\n    public void execute(@Param(\"dataMediaSourceId\") Long dataMediaSourceId, Context context) throws Exception {\n        DataMediaSource dataMediaSource = dataMediaSourceService.findById(dataMediaSourceId);\n\n        // 查询dataSource关联的同步任务\n        List<DataMedia> dataMedias = dataMediaService.listByDataMediaSourceId(dataMediaSource.getId());\n        context.put(\"source\", dataMediaSource);\n        context.put(\"dataMedias\", dataMedias);\n    }\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/home/module/screen/DataSourceList.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.home.module.screen;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport javax.annotation.Resource;\n\nimport com.alibaba.citrus.turbine.Context;\nimport com.alibaba.citrus.turbine.dataresolver.Param;\nimport com.alibaba.citrus.util.Paginator;\nimport com.alibaba.otter.manager.biz.config.datamedia.DataMediaService;\nimport com.alibaba.otter.manager.biz.config.datamediasource.DataMediaSourceService;\nimport com.alibaba.otter.manager.web.common.model.SeniorDataMediaSource;\nimport com.alibaba.otter.shared.common.model.config.data.DataMedia;\nimport com.alibaba.otter.shared.common.model.config.data.DataMediaSource;\nimport com.alibaba.otter.shared.common.model.config.data.db.DbMediaSource;\nimport com.alibaba.otter.shared.common.model.config.data.mq.MqMediaSource;\n\npublic class DataSourceList {\n\n    @Resource(name = \"dataMediaSourceService\")\n    private DataMediaSourceService dataMediaSourceService;\n\n    @Resource(name = \"dataMediaService\")\n    private DataMediaService       dataMediaService;\n\n    public void execute(@Param(\"pageIndex\") int pageIndex, @Param(\"searchKey\") String searchKey, Context context)\n                                                                                                                 throws Exception {\n        @SuppressWarnings(\"unchecked\")\n        Map<String, Object> condition = new HashMap<String, Object>();\n        if (\"请输入关键字(目前支持DataSource的ID、名字搜索)\".equals(searchKey)) {\n            searchKey = \"\";\n        }\n        condition.put(\"searchKey\", searchKey);\n\n        int count = dataMediaSourceService.getCount(condition);\n        Paginator paginator = new Paginator();\n        paginator.setItems(count);\n        paginator.setPage(pageIndex);\n\n        condition.put(\"offset\", paginator.getOffset());\n        condition.put(\"length\", paginator.getLength());\n\n        List<DataMediaSource> dataMediaSources = dataMediaSourceService.listByCondition(condition);\n        List<SeniorDataMediaSource> seniorDataMediaSources = new ArrayList<SeniorDataMediaSource>();\n        for (DataMediaSource dataMediaSource : dataMediaSources) {\n\n            SeniorDataMediaSource seniorDataMediaSource = new SeniorDataMediaSource();\n            seniorDataMediaSource.setEncode(dataMediaSource.getEncode());\n            seniorDataMediaSource.setGmtCreate(dataMediaSource.getGmtCreate());\n            seniorDataMediaSource.setGmtModified(dataMediaSource.getGmtModified());\n            seniorDataMediaSource.setId(dataMediaSource.getId());\n            seniorDataMediaSource.setName(dataMediaSource.getName());\n            seniorDataMediaSource.setType(dataMediaSource.getType());\n            if (dataMediaSource instanceof DbMediaSource) {\n                seniorDataMediaSource.setDriver(((DbMediaSource) dataMediaSource).getDriver());\n                seniorDataMediaSource.setUrl(((DbMediaSource) dataMediaSource).getUrl());\n                seniorDataMediaSource.setUsername(((DbMediaSource) dataMediaSource).getUsername());\n            } else if (dataMediaSource instanceof MqMediaSource) {\n                seniorDataMediaSource.setUrl(((MqMediaSource) dataMediaSource).getUrl());\n                seniorDataMediaSource.setStorePath(((MqMediaSource) dataMediaSource).getStorePath());\n            }\n            List<DataMedia> dataMedia = dataMediaService.listByDataMediaSourceId(dataMediaSource.getId());\n            seniorDataMediaSource.setDataMedias(dataMedia);\n            if (dataMedia.size() < 1) {\n                seniorDataMediaSource.setUsed(false);\n            } else {\n                seniorDataMediaSource.setUsed(true);\n            }\n            seniorDataMediaSources.add(seniorDataMediaSource);\n\n        }\n\n        context.put(\"sources\", seniorDataMediaSources);\n        context.put(\"paginator\", paginator);\n        context.put(\"searchKey\", searchKey);\n    }\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/home/module/screen/EditAlarmRule.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.home.module.screen;\n\nimport javax.annotation.Resource;\n\nimport com.alibaba.citrus.turbine.Context;\nimport com.alibaba.citrus.turbine.Navigator;\nimport com.alibaba.citrus.turbine.dataresolver.Param;\nimport com.alibaba.otter.manager.biz.config.alarm.AlarmRuleService;\nimport com.alibaba.otter.manager.biz.config.channel.ChannelService;\nimport com.alibaba.otter.shared.common.model.config.alarm.AlarmRule;\n\npublic class EditAlarmRule {\n\n    @Resource(name = \"alarmRuleService\")\n    private AlarmRuleService alarmRuleService;\n\n    @Resource(name = \"channelService\")\n    private ChannelService   channelService;\n\n    public void execute(@Param(\"alarmRuleId\") Long alarmRuleId, Context context, Navigator nav) throws Exception {\n        AlarmRule alarmRule = alarmRuleService.getAlarmRuleById(alarmRuleId);\n\n        context.put(\"alarmRule\", alarmRule);\n        context.put(\"channelId\", channelService.findByPipelineId(alarmRule.getPipelineId()).getId());\n    }\n\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/home/module/screen/EditAutoKeeper.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.home.module.screen;\n\nimport javax.annotation.Resource;\n\nimport com.alibaba.citrus.turbine.Context;\nimport com.alibaba.citrus.turbine.Navigator;\nimport com.alibaba.citrus.turbine.dataresolver.Param;\nimport com.alibaba.citrus.webx.WebxException;\nimport com.alibaba.otter.manager.biz.config.autokeeper.AutoKeeperClusterService;\nimport com.alibaba.otter.shared.common.model.autokeeper.AutoKeeperCluster;\n\npublic class EditAutoKeeper {\n\n    @Resource(name = \"autoKeeperClusterService\")\n    private AutoKeeperClusterService autoKeeperClusterService;\n\n    /**\n     * 找到单个Channel，用于编辑Channel信息界面加载信息\n     * \n     * @param channelId\n     * @param context\n     * @throws WebxException\n     */\n    public void execute(@Param(\"clusterId\") Long clusterId, Context context, Navigator nav) throws Exception {\n        AutoKeeperCluster autoKeeperCluster = autoKeeperClusterService.findAutoKeeperClusterById(clusterId);\n\n        context.put(\"autoKeeperCluster\", autoKeeperCluster);\n    }\n\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/home/module/screen/EditCanal.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.home.module.screen;\n\nimport java.util.List;\n\nimport javax.annotation.Resource;\n\nimport com.alibaba.citrus.turbine.Context;\nimport com.alibaba.citrus.turbine.dataresolver.Param;\nimport com.alibaba.citrus.webx.WebxException;\nimport com.alibaba.otter.canal.instance.manager.model.Canal;\nimport com.alibaba.otter.manager.biz.config.autokeeper.AutoKeeperClusterService;\nimport com.alibaba.otter.manager.biz.config.canal.CanalService;\nimport com.alibaba.otter.manager.web.common.CanalExtraParamUtil;\nimport com.alibaba.otter.shared.common.model.autokeeper.AutoKeeperCluster;\n\npublic class EditCanal {\n\n    @Resource(name = \"canalService\")\n    private CanalService             canalService;\n\n    @Resource(name = \"autoKeeperClusterService\")\n    private AutoKeeperClusterService autoKeeperClusterService;\n\n    /**\n     * @param context\n     * @throws WebxException\n     */\n    public void execute(@Param(\"canalId\") Long canalId, Context context) throws Exception {\n        Canal canal = canalService.findById(canalId);\n        List<AutoKeeperCluster> zkClusters = autoKeeperClusterService.listAutoKeeperClusters();\n        context.put(\"zkClusters\", zkClusters);\n        context.put(\"canal\", canal);\n\n        context.put(\"canalExtra\", CanalExtraParamUtil.getExtraParamMap(canal.getCanalParameter()));\n    }\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/home/module/screen/EditChannel.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.home.module.screen;\n\nimport javax.annotation.Resource;\n\nimport com.alibaba.citrus.turbine.Context;\nimport com.alibaba.citrus.turbine.Navigator;\nimport com.alibaba.citrus.turbine.dataresolver.Param;\nimport com.alibaba.citrus.webx.WebxException;\nimport com.alibaba.otter.manager.biz.config.channel.ChannelService;\nimport com.alibaba.otter.manager.web.common.WebConstant;\nimport com.alibaba.otter.shared.common.model.config.channel.Channel;\n\npublic class EditChannel {\n\n    @Resource(name = \"channelService\")\n    private ChannelService channelService;\n\n    /**\n     * 找到单个Channel，用于编辑Channel信息界面加载信息\n     * \n     * @param channelId\n     * @param context\n     * @throws WebxException\n     */\n    public void execute(@Param(\"channelId\") Long channelId, @Param(\"pageIndex\") int pageIndex,\n                        @Param(\"searchKey\") String searchKey, Context context, Navigator nav) throws Exception {\n        Channel channel = channelService.findById(channelId);\n        if (channel.getStatus().isStart()) {\n            nav.redirectTo(WebConstant.ERROR_FORBIDDEN_Link);\n            return;\n        }\n        context.put(\"channel\", channel);\n        context.put(\"pageIndex\", pageIndex);\n        context.put(\"searchKey\", searchKey);\n    }\n\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/home/module/screen/EditDataMatrix.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.home.module.screen;\n\nimport javax.annotation.Resource;\n\nimport com.alibaba.citrus.turbine.Context;\nimport com.alibaba.citrus.turbine.dataresolver.Param;\nimport com.alibaba.citrus.webx.WebxException;\nimport com.alibaba.otter.manager.biz.config.datamatrix.DataMatrixService;\nimport com.alibaba.otter.shared.common.model.config.data.DataMatrix;\n\npublic class EditDataMatrix {\n\n    @Resource(name = \"dataMatrixService\")\n    private DataMatrixService dataMatrixService;\n\n    /**\n     * @param context\n     * @throws WebxException\n     */\n    public void execute(@Param(\"matrixId\") Long matrixId, Context context) throws Exception {\n        DataMatrix matrix = dataMatrixService.findById(matrixId);\n        context.put(\"dataMatrix\", matrix);\n    }\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/home/module/screen/EditDataMedia.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.home.module.screen;\n\nimport javax.annotation.Resource;\n\nimport com.alibaba.citrus.turbine.Context;\nimport com.alibaba.citrus.turbine.dataresolver.Param;\nimport com.alibaba.otter.shared.common.model.config.data.DataMedia;\nimport com.alibaba.otter.manager.biz.config.datamedia.DataMediaService;\n\n/**\n * 类AddDataMedia.java的实现描述：TODO 类实现描述\n * \n * @author simon 2011-10-25 上午10:00:32\n */\npublic class EditDataMedia {\n\n    @Resource(name = \"dataMediaService\")\n    private DataMediaService dataMediaService;\n\n    public void execute(@Param(\"dataMediaId\") Long dataMediaId, @Param(\"pageIndex\") int pageIndex,\n                        @Param(\"searchKey\") String searchKey, Context context) throws Exception {\n        DataMedia dataMedia = dataMediaService.findById(dataMediaId);\n        context.put(\"dataMedia\", dataMedia);\n        context.put(\"pageIndex\", pageIndex);\n        context.put(\"searchKey\", searchKey);\n    }\n\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/home/module/screen/EditDataMediaPair.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.home.module.screen;\n\nimport javax.annotation.Resource;\n\nimport com.alibaba.citrus.turbine.Context;\nimport com.alibaba.citrus.turbine.Navigator;\nimport com.alibaba.citrus.turbine.dataresolver.Param;\nimport com.alibaba.otter.shared.common.model.config.channel.Channel;\nimport com.alibaba.otter.shared.common.model.config.data.DataMediaPair;\nimport com.alibaba.otter.manager.biz.config.channel.ChannelService;\nimport com.alibaba.otter.manager.biz.config.datamediapair.DataMediaPairService;\nimport com.alibaba.otter.manager.web.common.WebConstant;\n\npublic class EditDataMediaPair {\n\n    @Resource(name = \"dataMediaPairService\")\n    private DataMediaPairService dataMediaPairService;\n\n    @Resource(name = \"channelService\")\n    private ChannelService       channelService;\n\n    public void execute(@Param(\"dataMediaPairId\") Long dataMediaPairId, @Param(\"pipelineId\") Long pipelineId,\n                        Context context, Navigator nav) throws Exception {\n        Channel channel = channelService.findByPipelineId(pipelineId);\n        if (channel.getStatus().isStart()) {\n            nav.redirectTo(WebConstant.ERROR_FORBIDDEN_Link);\n            return;\n        }\n\n        DataMediaPair dataMediaPair = dataMediaPairService.findById(dataMediaPairId);\n        context.put(\"channelId\", channel.getId());\n        context.put(\"dataMediaPair\", dataMediaPair);\n    }\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/home/module/screen/EditDataSource.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.home.module.screen;\n\nimport javax.annotation.Resource;\n\nimport com.alibaba.citrus.turbine.Context;\nimport com.alibaba.citrus.turbine.dataresolver.Param;\nimport com.alibaba.otter.shared.common.model.config.data.DataMediaSource;\nimport com.alibaba.otter.manager.biz.config.datamediasource.DataMediaSourceService;\n\n/**\n * 类EditDataSource.java的实现描述：TODO 类实现描述\n * \n * @author simon 2011-10-26 下午04:03:14\n */\npublic class EditDataSource {\n\n    @Resource(name = \"dataMediaSourceService\")\n    private DataMediaSourceService dataMediaSourceService;\n\n    public void execute(@Param(\"dataMediaSourceId\") Long dataMediaSourceId, @Param(\"pageIndex\") int pageIndex,\n                        @Param(\"searchKey\") String searchKey, Context context) throws Exception {\n        DataMediaSource source = dataMediaSourceService.findById(dataMediaSourceId);\n        context.put(\"source\", source);\n        context.put(\"pageIndex\", pageIndex);\n        context.put(\"searchKey\", searchKey);\n    }\n\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/home/module/screen/EditNode.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.home.module.screen;\n\nimport java.util.List;\n\nimport javax.annotation.Resource;\n\nimport com.alibaba.citrus.turbine.Context;\nimport com.alibaba.citrus.turbine.Navigator;\nimport com.alibaba.citrus.turbine.dataresolver.Param;\nimport com.alibaba.citrus.webx.WebxException;\nimport com.alibaba.otter.manager.biz.config.autokeeper.AutoKeeperClusterService;\nimport com.alibaba.otter.manager.biz.config.node.NodeService;\nimport com.alibaba.otter.manager.web.common.WebConstant;\nimport com.alibaba.otter.shared.common.model.autokeeper.AutoKeeperCluster;\nimport com.alibaba.otter.shared.common.model.config.node.Node;\n\npublic class EditNode {\n\n    @Resource(name = \"nodeService\")\n    private NodeService              nodeService;\n\n    @Resource(name = \"autoKeeperClusterService\")\n    private AutoKeeperClusterService autoKeeperClusterService;\n\n    /**\n     * 找到单个Channel，用于编辑Channel信息界面加载信息\n     * \n     * @param channelId\n     * @param context\n     * @throws WebxException\n     */\n    public void execute(@Param(\"nodeId\") Long nodeId, @Param(\"pageIndex\") int pageIndex,\n                        @Param(\"searchKey\") String searchKey, Context context, Navigator nav) throws Exception {\n        Node node = nodeService.findById(nodeId);\n        if (node.getStatus().isStart()) {\n            nav.redirectTo(WebConstant.ERROR_FORBIDDEN_Link);\n            return;\n        }\n\n        List<AutoKeeperCluster> zkClusters = autoKeeperClusterService.listAutoKeeperClusters();\n        context.put(\"zkClusters\", zkClusters);\n        context.put(\"node\", node);\n        context.put(\"pageIndex\", pageIndex);\n        context.put(\"searchKey\", searchKey);\n    }\n\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/home/module/screen/EditPipeline.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.home.module.screen;\n\nimport javax.annotation.Resource;\n\nimport com.alibaba.citrus.turbine.Context;\nimport com.alibaba.citrus.turbine.Navigator;\nimport com.alibaba.citrus.turbine.dataresolver.Param;\nimport com.alibaba.citrus.webx.WebxException;\nimport com.alibaba.otter.manager.biz.config.channel.ChannelService;\nimport com.alibaba.otter.manager.biz.config.node.NodeService;\nimport com.alibaba.otter.manager.biz.config.pipeline.PipelineService;\nimport com.alibaba.otter.manager.web.common.WebConstant;\nimport com.alibaba.otter.shared.common.model.config.channel.Channel;\nimport com.alibaba.otter.shared.common.model.config.pipeline.Pipeline;\n\npublic class EditPipeline {\n\n    @Resource(name = \"pipelineService\")\n    private PipelineService pipelineService;\n    @Resource(name = \"nodeService\")\n    private NodeService     nodeService;\n    @Resource(name = \"channelService\")\n    private ChannelService  channelService;\n\n    /**\n     * 找到单个Channel，用于编辑Channel信息界面加载信息\n     * \n     * @param channelId\n     * @param context\n     * @throws WebxException\n     */\n    public void execute(@Param(\"pipelineId\") Long pipelineId, Context context, Navigator nav) throws Exception {\n        Channel channel = channelService.findByPipelineId(pipelineId);\n        if (channel.getStatus().isStart()) {\n            nav.redirectTo(WebConstant.ERROR_FORBIDDEN_Link);\n            return;\n        }\n\n        Pipeline pipeline = pipelineService.findById(pipelineId);\n        context.put(\"pipeline\", pipeline);\n        context.put(\"nodes\", nodeService.listAll());\n    }\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/home/module/screen/EditUser.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.home.module.screen;\n\nimport javax.annotation.Resource;\n\nimport com.alibaba.citrus.turbine.Context;\nimport com.alibaba.citrus.turbine.Navigator;\nimport com.alibaba.citrus.turbine.dataresolver.Param;\nimport com.alibaba.citrus.webx.WebxException;\nimport com.alibaba.otter.manager.biz.user.UserService;\nimport com.alibaba.otter.shared.common.model.user.User;\n\npublic class EditUser {\n\n    @Resource(name = \"userService\")\n    private UserService userService;\n\n    /**\n     * 找到单个Channel，用于编辑Channel信息界面加载信息\n     * \n     * @param channelId\n     * @param context\n     * @throws WebxException\n     */\n    public void execute(@Param(\"userId\") Long userId, @Param(\"pageIndex\") int pageIndex,\n                        @Param(\"searchKey\") String searchKey, Context context, Navigator nav) throws Exception {\n        User user = userService.findUserById(userId);\n\n        context.put(\"user\", user);\n        context.put(\"pageIndex\", pageIndex);\n        context.put(\"searchKey\", searchKey);\n    }\n\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/home/module/screen/LogRecordList.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.home.module.screen;\n\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport javax.annotation.Resource;\n\nimport com.alibaba.citrus.turbine.Context;\nimport com.alibaba.citrus.turbine.dataresolver.Param;\nimport com.alibaba.citrus.util.Paginator;\nimport com.alibaba.otter.manager.biz.config.record.LogRecordService;\nimport com.alibaba.otter.shared.common.model.config.record.LogRecord;\n\n/**\n * 类NodeList.java的实现描述：TODO 类实现描述\n * \n * @author simon 2011-10-25 上午10:25:27\n */\npublic class LogRecordList {\n\n    @Resource(name = \"logRecordService\")\n    private LogRecordService logRecordService;\n\n    public void execute(@Param(\"pageIndex\") int pageIndex, @Param(\"pipelineId\") String pipelineId,\n                        @Param(\"monitorName\") String monitorName, @Param(\"searchKey\") String searchKey, Context context)\n                                                                                                                        throws Exception {\n        @SuppressWarnings(\"unchecked\")\n        Map<String, Object> condition = new HashMap<String, Object>();\n        if (\"请输入关键字(目前支持CID,PID搜索)\".equals(searchKey)) {\n            searchKey = \"\";\n        }\n        condition.put(\"searchKey\", searchKey);\n        condition.put(\"monitorName\", monitorName);\n        condition.put(\"pipelineId\", pipelineId);\n\n        int count = logRecordService.getCount(condition);\n        Paginator paginator = new Paginator();\n        paginator.setItems(count);\n        paginator.setPage(pageIndex);\n\n        condition.put(\"offset\", paginator.getOffset());\n        condition.put(\"length\", paginator.getLength());\n\n        List<LogRecord> logRecords = logRecordService.listByCondition(condition);\n        // List<LogRecord> logRecords = logRecordService.listAll();\n\n        context.put(\"logRecords\", logRecords);\n        context.put(\"paginator\", paginator);\n        context.put(\"searchKey\", searchKey);\n        context.put(\"pipelineId\", pipelineId);\n        context.put(\"monitorName\", monitorName);\n    }\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/home/module/screen/LogRecordTab.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.home.module.screen;\n\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport javax.annotation.Resource;\n\nimport org.apache.commons.lang.StringUtils;\n\nimport com.alibaba.citrus.turbine.Context;\nimport com.alibaba.citrus.turbine.dataresolver.Param;\nimport com.alibaba.citrus.util.Paginator;\nimport com.alibaba.otter.manager.biz.config.channel.ChannelService;\nimport com.alibaba.otter.manager.biz.config.record.LogRecordService;\nimport com.alibaba.otter.shared.common.model.config.channel.Channel;\nimport com.alibaba.otter.shared.common.model.config.record.LogRecord;\n\npublic class LogRecordTab {\n\n    @Resource(name = \"logRecordService\")\n    private LogRecordService logRecordService;\n    @Resource(name = \"channelService\")\n    private ChannelService   channelService;\n\n    public void execute(@Param(\"pageIndex\") int pageIndex, @Param(\"searchKey\") String searchKey,\n                        @Param(\"pipelineId\") Long pipelineId, Context context) throws Exception {\n\n        @SuppressWarnings(\"unchecked\")\n        Map<String, Object> condition = new HashMap<String, Object>();\n        if (\"请输入关键字(目前支持log内容关键字搜索)\".equals(searchKey)) {\n            searchKey = \"\";\n        }\n\n        condition.put(\"pipelineId\", pipelineId);\n        condition.put(\"searchKey\", searchKey);\n\n        int count = logRecordService.getCount(condition);\n        Paginator paginator = new Paginator();\n        paginator.setItems(count);\n        paginator.setPage(pageIndex);\n\n        condition.put(\"offset\", paginator.getOffset());\n        condition.put(\"length\", paginator.getLength());\n\n        List<LogRecord> logRecords = logRecordService.listByCondition(condition);\n        for (LogRecord logRecord : logRecords) {\n            if (!StringUtils.isEmpty(logRecord.getMessage())) {\n                logRecord.setMessage(logRecord.getMessage().replaceAll(\"\\n\\t\", \"<br/>\"));\n            }\n        }\n\n        context.put(\"logRecords\", logRecords);\n        context.put(\"paginator\", paginator);\n        context.put(\"searchKey\", searchKey);\n        context.put(\"pipelineId\", pipelineId);\n        Channel channel = channelService.findByPipelineId(pipelineId);\n        context.put(\"channel\", channel);\n    }\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/home/module/screen/NodeInfo.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.home.module.screen;\n\nimport java.util.List;\n\nimport javax.annotation.Resource;\n\nimport com.alibaba.citrus.turbine.Context;\nimport com.alibaba.citrus.turbine.dataresolver.Param;\nimport com.alibaba.otter.manager.biz.config.channel.ChannelService;\nimport com.alibaba.otter.manager.biz.config.node.NodeService;\nimport com.alibaba.otter.manager.biz.remote.NodeRemoteService;\nimport com.alibaba.otter.shared.common.model.config.channel.Channel;\nimport com.alibaba.otter.shared.common.model.config.node.Node;\n\npublic class NodeInfo {\n\n    @Resource(name = \"nodeService\")\n    private NodeService       nodeService;\n\n    @Resource(name = \"channelService\")\n    private ChannelService    channelService;\n\n    @Resource(name = \"nodeRemoteService\")\n    private NodeRemoteService nodeRemoteService;\n\n    public void execute(@Param(\"nodeId\") Long nodeId, Context context) throws Exception {\n        Node node = nodeService.findById(nodeId);\n        List<Channel> channels = channelService.listByNodeId(nodeId);\n        if (node.getStatus().isStart()) {\n            context.put(\"heapMemoryUsage\", nodeRemoteService.getHeapMemoryUsage(nodeId));\n            context.put(\"versionInfo\", nodeRemoteService.getNodeVersionInfo(nodeId));\n            context.put(\"systemInfo\", nodeRemoteService.getNodeSystemInfo(nodeId));\n            context.put(\"threadActiveSize\", nodeRemoteService.getThreadActiveSize(nodeId));\n            context.put(\"threadPoolSize\", nodeRemoteService.getThreadPoolSize(nodeId));\n            context.put(\"runningPipelines\", nodeRemoteService.getRunningPipelines(nodeId));\n        } else {\n            context.put(\"heapMemoryUsage\", 0);\n            context.put(\"threadActiveSize\", 0);\n            context.put(\"threadPoolSize\", 0);\n            context.put(\"runningPipelines\", 0);\n            context.put(\"versionInfo\", \"\");\n            context.put(\"systemInfo\", \"\");\n        }\n        context.put(\"node\", node);\n        context.put(\"channels\", channels);\n    }\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/home/module/screen/NodeList.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.home.module.screen;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport javax.annotation.Resource;\n\nimport com.alibaba.citrus.turbine.Context;\nimport com.alibaba.citrus.turbine.dataresolver.Param;\nimport com.alibaba.citrus.util.Paginator;\nimport com.alibaba.otter.manager.biz.config.node.NodeService;\nimport com.alibaba.otter.manager.biz.config.pipeline.PipelineService;\nimport com.alibaba.otter.manager.web.common.model.SeniorNode;\nimport com.alibaba.otter.shared.common.model.config.node.Node;\n\n/**\n * 类NodeList.java的实现描述：TODO 类实现描述\n * \n * @author simon 2011-10-25 上午10:25:27\n */\npublic class NodeList {\n\n    @Resource(name = \"nodeService\")\n    private NodeService     nodeService;\n\n    @Resource(name = \"pipelineService\")\n    private PipelineService pipelineService;\n\n    public void execute(@Param(\"pageIndex\") int pageIndex, @Param(\"searchKey\") String searchKey, Context context)\n                                                                                                                 throws Exception {\n        @SuppressWarnings(\"unchecked\")\n        Map<String, Object> condition = new HashMap<String, Object>();\n        if (\"请输入关键字(目前支持Node的ID、名字搜索)\".equals(searchKey)) {\n            searchKey = \"\";\n        }\n        condition.put(\"searchKey\", searchKey);\n\n        int count = nodeService.getCount(condition);\n        Paginator paginator = new Paginator();\n        paginator.setItems(count);\n        paginator.setPage(pageIndex);\n\n        condition.put(\"offset\", paginator.getOffset());\n        condition.put(\"length\", paginator.getLength());\n\n        List<Node> nodes = nodeService.listByCondition(condition);\n\n        List<SeniorNode> seniorNodes = new ArrayList<SeniorNode>();\n\n        for (Node node : nodes) {\n            SeniorNode seniorNode = new SeniorNode();\n            seniorNode.setId(node.getId());\n            seniorNode.setIp(node.getIp());\n            seniorNode.setName(node.getName());\n            seniorNode.setPort(node.getPort());\n            seniorNode.setDescription(node.getDescription());\n            seniorNode.setStatus(node.getStatus());\n            seniorNode.setParameters(node.getParameters());\n            seniorNode.setGmtCreate(node.getGmtCreate());\n            seniorNode.setGmtModified(node.getGmtModified());\n            seniorNode.setUsed(pipelineService.hasRelation(node.getId()));\n            seniorNodes.add(seniorNode);\n        }\n        context.put(\"seniorNodes\", seniorNodes);\n        context.put(\"paginator\", paginator);\n        context.put(\"searchKey\", searchKey);\n    }\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/home/module/screen/Ok.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.home.module.screen;\n\nimport com.alibaba.citrus.turbine.Context;\nimport com.alibaba.citrus.turbine.dataresolver.Param;\n\n/**\n * @author jianghang 2011-8-31 下午07:00:19\n */\npublic class Ok {\n\n    public void execute(@Param(name = \"param\") String param, Context context) {\n        context.put(\"ok\", param);\n    }\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/home/module/screen/PipelineInfo.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.home.module.screen;\n\nimport javax.annotation.Resource;\n\nimport com.alibaba.citrus.turbine.Context;\nimport com.alibaba.citrus.turbine.dataresolver.Param;\nimport com.alibaba.otter.shared.common.model.config.pipeline.Pipeline;\nimport com.alibaba.otter.manager.biz.config.node.NodeService;\nimport com.alibaba.otter.manager.biz.config.pipeline.PipelineService;\n\npublic class PipelineInfo {\n\n    @Resource(name = \"pipelineService\")\n    private PipelineService pipelineService;\n\n    @Resource(name = \"nodeService\")\n    private NodeService     nodeService;\n\n    public void execute(@Param(\"pipelineId\") Long pipelineId, Context context) throws Exception {\n        Pipeline pipeline = pipelineService.findById(pipelineId);\n\n        context.put(\"pipeline\", pipeline);\n        context.put(\"nodes\", nodeService.listAll());\n    }\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/home/module/screen/PipelineList.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.home.module.screen;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport javax.annotation.Resource;\nimport javax.servlet.http.HttpSession;\n\nimport com.alibaba.citrus.turbine.Context;\nimport com.alibaba.citrus.turbine.dataresolver.Param;\nimport com.alibaba.otter.manager.biz.config.alarm.AlarmRuleService;\nimport com.alibaba.otter.manager.biz.config.channel.ChannelService;\nimport com.alibaba.otter.manager.biz.statistics.delay.DelayStatService;\nimport com.alibaba.otter.manager.biz.statistics.throughput.ThroughputStatService;\nimport com.alibaba.otter.manager.biz.statistics.throughput.param.ThroughputCondition;\nimport com.alibaba.otter.shared.arbitrate.ArbitrateViewService;\nimport com.alibaba.otter.shared.arbitrate.model.MainStemEventData;\nimport com.alibaba.otter.shared.arbitrate.model.PositionEventData;\nimport com.alibaba.otter.shared.common.model.config.alarm.AlarmRule;\nimport com.alibaba.otter.shared.common.model.config.channel.Channel;\nimport com.alibaba.otter.shared.common.model.config.pipeline.Pipeline;\nimport com.alibaba.otter.shared.common.model.statistics.delay.DelayStat;\nimport com.alibaba.otter.shared.common.model.statistics.throughput.ThroughputStat;\nimport com.alibaba.otter.shared.common.model.statistics.throughput.ThroughputType;\n\npublic class PipelineList {\n\n    @Resource(name = \"channelService\")\n    private ChannelService        channelService;\n\n    @Resource(name = \"delayStatService\")\n    private DelayStatService      delayStatService;\n\n    @Resource(name = \"arbitrateViewService\")\n    private ArbitrateViewService  arbitrateViewService;\n\n    @Resource(name = \"throughputStatService\")\n    private ThroughputStatService throughputStatService;\n\n    @Resource(name = \"alarmRuleService\")\n    private AlarmRuleService      alarmRuleService;\n\n    public void execute(@Param(\"channelId\") Long channelId, @Param(\"pipelineId\") Long pipelineId, HttpSession session,\n                        Context context) throws Exception {\n\n        Channel channel = channelService.findByIdWithoutColumn(channelId);\n        List<Pipeline> pipelines = channel.getPipelines();\n        List<Pipeline> tempPipe = new ArrayList<Pipeline>();\n\n        if ((pipelineId != null) && (pipelineId != 0l)) {\n            for (Pipeline pipeline : pipelines) {\n                if (!pipeline.getId().equals(pipelineId)) {\n                    tempPipe.add(pipeline);\n                }\n            }\n            pipelines.removeAll(tempPipe);\n        }\n\n        Map<Long, DelayStat> delayStats = new HashMap<Long, DelayStat>(pipelines.size(), 1f);\n        Map<Long, MainStemEventData> mainstemDatas = new HashMap<Long, MainStemEventData>(pipelines.size(), 1f);\n        Map<Long, ThroughputStat> throughputStats = new HashMap<Long, ThroughputStat>(pipelines.size(), 1f);\n        Map<Long, List<AlarmRule>> alarmRuleStats = new HashMap<Long, List<AlarmRule>>(pipelines.size(), 1f);\n        Map<Long, PositionEventData> positionDatas = new HashMap<Long, PositionEventData>(pipelines.size(), 1f);\n        for (Pipeline pipeline : pipelines) {\n            DelayStat delayStat = delayStatService.findRealtimeDelayStat(pipeline.getId());\n            if (delayStat.getDelayNumber() == null) {\n                delayStat.setDelayNumber(0L);\n                delayStat.setDelayTime(0L);\n                delayStat.setGmtModified(pipeline.getGmtModified());\n            }\n            delayStats.put(pipeline.getId(), delayStat);\n            mainstemDatas.put(pipeline.getId(), arbitrateViewService.mainstemData(channel.getId(), pipeline.getId()));\n            ThroughputCondition condition = new ThroughputCondition();\n            condition.setPipelineId(pipeline.getId());\n            condition.setType(ThroughputType.ROW);\n            ThroughputStat throughputStat = throughputStatService.findThroughputStatByPipelineId(condition);\n            throughputStats.put(pipeline.getId(), throughputStat);\n            List<AlarmRule> alarmRules = alarmRuleService.getAlarmRules(pipeline.getId());\n            alarmRuleStats.put(pipeline.getId(), alarmRules);\n            PositionEventData positionData = arbitrateViewService.getCanalCursor(pipeline.getParameters().getDestinationName(),\n                                                                                 pipeline.getParameters().getMainstemClientId());\n            positionDatas.put(pipeline.getId(), positionData);\n        }\n\n        context.put(\"channel\", channel);\n        context.put(\"pipelines\", pipelines);\n        context.put(\"delayStats\", delayStats);\n        context.put(\"throughputStats\", throughputStats);\n        context.put(\"alarmRuleStats\", alarmRuleStats);\n        context.put(\"mainstemDatas\", mainstemDatas);\n        context.put(\"positionDatas\", positionDatas);\n    }\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/home/module/screen/SelectCanal.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.home.module.screen;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport javax.annotation.Resource;\n\nimport com.alibaba.citrus.turbine.Context;\nimport com.alibaba.citrus.turbine.dataresolver.Param;\nimport com.alibaba.citrus.util.Paginator;\nimport com.alibaba.otter.canal.instance.manager.model.Canal;\nimport com.alibaba.otter.manager.biz.config.canal.CanalService;\nimport com.alibaba.otter.manager.biz.config.pipeline.PipelineService;\nimport com.alibaba.otter.manager.web.common.model.SeniorCanal;\n\n/**\n * @author sarah.lij 2012-7-26 下午04:25:52\n */\npublic class SelectCanal {\n\n    @Resource(name = \"canalService\")\n    private CanalService    canalService;\n\n    @Resource(name = \"pipelineService\")\n    private PipelineService pipelineService;\n\n    public void execute(@Param(\"pageIndex\") int pageIndex, @Param(\"searchKey\") String searchKey, Context context)\n                                                                                                                 throws Exception {\n        @SuppressWarnings(\"unchecked\")\n        Map<String, Object> condition = new HashMap<String, Object>();\n        if (\"请输入关键字(目前支持Canal的名字，参数搜索)\".equals(searchKey)) {\n            searchKey = \"\";\n        }\n        condition.put(\"searchKey\", searchKey);\n\n        int count = canalService.getCount(condition);\n        Paginator paginator = new Paginator();\n        paginator.setItems(count);\n        paginator.setPage(pageIndex);\n\n        condition.put(\"offset\", paginator.getOffset());\n        condition.put(\"length\", paginator.getLength());\n\n        List<Canal> canals = canalService.listByCondition(condition);\n\n        List<SeniorCanal> seniorCanals = new ArrayList<SeniorCanal>();\n\n        for (Canal canal : canals) {\n            SeniorCanal seniorCanal = new SeniorCanal();\n            seniorCanal.setId(canal.getId());\n            seniorCanal.setName(canal.getName());\n            seniorCanal.setStatus(canal.getStatus());\n            seniorCanal.setDesc(canal.getDesc());\n            seniorCanal.setCanalParameter(canal.getCanalParameter());\n            seniorCanal.setUsed(false);\n            seniorCanal.setGmtCreate(canal.getGmtCreate());\n            seniorCanal.setGmtModified(canal.getGmtModified());\n            seniorCanals.add(seniorCanal);\n        }\n        context.put(\"seniorCanals\", seniorCanals);\n        context.put(\"paginator\", paginator);\n        context.put(\"searchKey\", searchKey);\n    }\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/home/module/screen/SelectDataMedia.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.home.module.screen;\n\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport javax.annotation.Resource;\n\nimport com.alibaba.citrus.turbine.Context;\nimport com.alibaba.citrus.turbine.dataresolver.Param;\nimport com.alibaba.citrus.util.Paginator;\nimport com.alibaba.otter.manager.biz.config.datamedia.DataMediaService;\nimport com.alibaba.otter.shared.common.model.config.data.DataMedia;\n\npublic class SelectDataMedia {\n\n    @Resource(name = \"dataMediaService\")\n    private DataMediaService dataMediaService;\n\n    public void execute(@Param(\"pageIndex\") int pageIndex, @Param(\"searchKey\") String searchKey,\n                        @Param(\"local\") String local, Context context) throws Exception {\n        @SuppressWarnings(\"unchecked\")\n        Map<String, Object> condition = new HashMap<String, Object>();\n        if (\"请输入关键字(目前支持DataMedia的ID、名字搜索)\".equals(searchKey)) {\n            searchKey = \"\";\n        }\n        condition.put(\"searchKey\", searchKey);\n\n        int count = dataMediaService.getCount(condition);\n        Paginator paginator = new Paginator();\n        paginator.setItems(count);\n        paginator.setPage(pageIndex);\n\n        condition.put(\"offset\", paginator.getOffset());\n        condition.put(\"length\", paginator.getLength());\n\n        List<DataMedia> dataMedias = dataMediaService.listByCondition(condition);\n        context.put(\"dataMedias\", dataMedias);\n        context.put(\"paginator\", paginator);\n        context.put(\"searchKey\", searchKey);\n        context.put(\"local\", local);\n    }\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/home/module/screen/SelectDataSource.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.home.module.screen;\n\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport javax.annotation.Resource;\n\nimport com.alibaba.citrus.turbine.Context;\nimport com.alibaba.citrus.turbine.dataresolver.Param;\nimport com.alibaba.citrus.util.Paginator;\nimport com.alibaba.otter.manager.biz.config.datamediasource.DataMediaSourceService;\nimport com.alibaba.otter.shared.common.model.config.data.DataMediaSource;\n\npublic class SelectDataSource {\n\n    @Resource(name = \"dataMediaSourceService\")\n    private DataMediaSourceService dataMediaSourceService;\n\n    public void execute(@Param(\"pageIndex\") int pageIndex, @Param(\"searchKey\") String searchKey, Context context)\n                                                                                                                 throws Exception {\n        @SuppressWarnings(\"unchecked\")\n        Map<String, Object> condition = new HashMap<String, Object>();\n        if (\"请输入关键字(目前支持DataSource的ID、名字搜索)\".equals(searchKey)) {\n            searchKey = \"\";\n        }\n        condition.put(\"searchKey\", searchKey);\n\n        int count = dataMediaSourceService.getCount(condition);\n        Paginator paginator = new Paginator();\n        paginator.setItems(count);\n        paginator.setPage(pageIndex);\n\n        condition.put(\"offset\", paginator.getOffset());\n        condition.put(\"length\", paginator.getLength());\n\n        List<DataMediaSource> dataMediaSources = dataMediaSourceService.listByCondition(condition);\n        context.put(\"sources\", dataMediaSources);\n        context.put(\"paginator\", paginator);\n        context.put(\"searchKey\", searchKey);\n    }\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/home/module/screen/SystemParameter.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.home.module.screen;\n\nimport javax.annotation.Resource;\n\nimport com.alibaba.citrus.turbine.Context;\nimport com.alibaba.citrus.turbine.dataresolver.Param;\nimport com.alibaba.otter.manager.biz.config.parameter.SystemParameterService;\n\npublic class SystemParameter {\n\n    @Resource(name = \"systemParameterService\")\n    private SystemParameterService systemParameterService;\n\n    public void execute(@Param(\"edit\") Boolean edit, Context context) throws Exception {\n        com.alibaba.otter.shared.common.model.config.parameter.SystemParameter systemParameter = systemParameterService.find();\n        context.put(\"edit\", edit);\n        context.put(\"systemParameter\", systemParameter);\n    }\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/home/module/screen/SystemReduction.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.home.module.screen;\n\nimport java.util.List;\n\nimport javax.annotation.Resource;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.alibaba.citrus.turbine.Context;\nimport com.alibaba.citrus.turbine.dataresolver.Param;\nimport com.alibaba.otter.shared.arbitrate.ArbitrateManageService;\nimport com.alibaba.otter.shared.arbitrate.exception.ArbitrateException;\nimport com.alibaba.otter.shared.common.model.config.channel.Channel;\nimport com.alibaba.otter.shared.common.model.config.pipeline.Pipeline;\nimport com.alibaba.otter.manager.biz.config.channel.ChannelService;\n\n/**\n * @author simon 2011-10-25 上午10:00:32\n */\npublic class SystemReduction {\n\n    private static final Logger    logger = LoggerFactory.getLogger(SystemReduction.class);\n\n    @Resource(name = \"channelService\")\n    private ChannelService         channelService;\n\n    @Resource(name = \"arbitrateManageService\")\n    private ArbitrateManageService arbitrateManageService;\n\n    public void execute(@Param(\"command\") String command, Context context) throws Exception {\n        @SuppressWarnings(\"unchecked\")\n        String resultStr = \"\";\n\n        if (\"true\".equals(command)) {\n\n            List<Channel> channels = channelService.listAll();\n\n            try {\n                // 初始化根节点\n                arbitrateManageService.systemEvent().init();\n\n                // 遍历所有的Channel节点\n                for (Channel channel : channels) {\n                    // 在ZK中初始化每个channel节点\n                    arbitrateManageService.channelEvent().init(channel.getId());\n\n                    // 在ZK中初始化该channel下的pipeline节点\n                    List<Pipeline> pipelines = channel.getPipelines();\n                    //\n                    for (Pipeline pipeline : pipelines) {\n                        arbitrateManageService.pipelineEvent().init(pipeline.getChannelId(), pipeline.getId());\n                    }\n                }\n\n                resultStr = \"恭喜！Zookeeper节点数据已经补全\";\n            } catch (ArbitrateException ae) {\n                logger.error(\"ERROR ## init zookeeper has a problem \", ae);\n                resultStr = \"出错了！回复zookeeper的时候遇到问题！\";\n            } catch (Exception e) {\n                logger.error(\"ERROR ## init zookeeper has a problem \", e);\n                resultStr = \"出错了！回复zookeeper的时候遇到问题！\";\n            }\n\n        }\n\n        context.put(\"resultStr\", resultStr);\n\n    }\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/home/module/screen/UserManager.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.home.module.screen;\n\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport javax.annotation.Resource;\n\nimport com.alibaba.citrus.turbine.Context;\nimport com.alibaba.citrus.turbine.dataresolver.Param;\nimport com.alibaba.citrus.util.Paginator;\nimport com.alibaba.otter.shared.common.model.user.User;\nimport com.alibaba.otter.manager.biz.user.UserService;\n\n/**\n * 类AddDataMedia.java的实现描述：TODO 类实现描述\n * \n * @author simon 2011-10-25 上午10:00:32\n */\npublic class UserManager {\n\n    @Resource(name = \"userService\")\n    private UserService userService;\n\n    public void execute(@Param(\"pageIndex\") int pageIndex, @Param(\"searchKey\") String searchKey, Context context)\n                                                                                                                 throws Exception {\n        @SuppressWarnings(\"unchecked\")\n        Map<String, Object> condition = new HashMap<String, Object>();\n        if (\"支持ID、用户名、真实姓名、部门搜索\".equals(searchKey)) {\n            searchKey = \"\";\n        }\n        condition.put(\"searchKey\", searchKey);\n\n        int count = userService.getCount(condition);\n        Paginator paginator = new Paginator();\n        paginator.setItems(count);\n        paginator.setPage(pageIndex);\n\n        condition.put(\"offset\", paginator.getOffset());\n        condition.put(\"length\", paginator.getLength());\n\n        List<User> users = userService.listByCondition(condition);\n        context.put(\"users\", users);\n        context.put(\"paginator\", paginator);\n        context.put(\"searchKey\", searchKey);\n    }\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/home/module/screen/api/AbstractJsonScreen.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.home.module.screen.api;\n\nimport java.io.IOException;\n\nimport javax.servlet.http.HttpServletResponse;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\n\nimport com.alibaba.fastjson.JSON;\nimport com.alibaba.otter.manager.web.common.api.JsonResult;\n\n/**\n * @author zebin.xuzb @ 2012-5-18\n */\npublic class AbstractJsonScreen<DATA> {\n\n    protected static final Logger log = LoggerFactory.getLogger(AbstractJsonScreen.class);\n\n    @Autowired\n    private HttpServletResponse   response;\n\n    protected void returnError(String errMessage) {\n        JsonResult result = new JsonResult(false);\n        result.setErrMessage(errMessage);\n        String content = buildJson(result);\n        writeResponse(content);\n    }\n\n    protected void returnSuccess() {\n        JsonResult result = new JsonResult(true);\n        String content = buildJson(result);\n        writeResponse(content);\n\n    }\n\n    protected void returnSuccess(DATA data) {\n        JsonResult result = new JsonResult(true);\n        result.setData(data);\n        String content = buildJson(result);\n        writeResponse(content);\n\n    }\n\n    protected String buildJson(JsonResult result) {\n        return JSON.toJSONString(result);\n    }\n\n    private void writeResponse(String content) {\n        try {\n            response.getWriter().write(content);\n        } catch (IOException e) {\n            throw new IllegalStateException(e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/home/module/screen/api/ChannelCheck.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.home.module.screen.api;\n\nimport javax.annotation.Resource;\n\nimport com.alibaba.citrus.turbine.dataresolver.Param;\nimport com.alibaba.otter.manager.biz.config.channel.ChannelService;\nimport com.alibaba.otter.shared.common.model.config.channel.Channel;\n\n/**\n * @author zebin.xuzb @ 2012-5-18\n */\npublic class ChannelCheck extends AbstractJsonScreen<String> {\n\n    @Resource(name = \"channelService\")\n    private ChannelService channelService;\n\n    public void execute(@Param(\"id\") Long id) {\n\n        try {\n\n            Channel channel = channelService.findById(id);\n\n            returnSuccess(channel.getStatus().toString());\n\n        } catch (Exception e) {\n            String errorMsg = String.format(\"error happens while [check status] channel with id [%d]\", id);\n            log.error(errorMsg, e);\n            returnError(errorMsg);\n        }\n    }\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/home/module/screen/api/ChannelOp.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.home.module.screen.api;\n\nimport javax.annotation.Resource;\n\nimport org.apache.commons.lang.StringUtils;\n\nimport com.alibaba.citrus.turbine.dataresolver.Param;\nimport com.alibaba.otter.manager.biz.config.channel.ChannelService;\n\n/**\n * @author zebin.xuzb @ 2012-5-18\n */\npublic class ChannelOp extends AbstractJsonScreen<String> {\n\n    private final static String START = \"start\";\n    private final static String STOP  = \"stop\";\n\n    @Resource(name = \"channelService\")\n    private ChannelService      channelService;\n\n    public void execute(@Param(\"id\") Long id, @Param(\"command\") String command) {\n\n        try {\n            if (StringUtils.equalsIgnoreCase(command, START)) {\n                channelService.startChannel(id);\n            } else if (StringUtils.equalsIgnoreCase(command, STOP)) {\n                channelService.stopChannel(id);\n            } else {\n                returnError(\"please add specfy the 'command' param.\");\n                return;\n            }\n            returnSuccess();\n        } catch (Exception e) {\n            String errorMsg = String.format(\"error happens while [%s] channel with id [%d]\", command, id);\n            log.error(errorMsg, e);\n            returnError(errorMsg);\n        }\n    }\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/home/module/screen/api/NodeOp.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.home.module.screen.api;\n\nimport java.util.List;\n\nimport javax.annotation.Resource;\n\nimport org.apache.commons.lang.BooleanUtils;\nimport org.apache.commons.lang.StringUtils;\n\nimport com.alibaba.citrus.turbine.dataresolver.Param;\nimport com.alibaba.otter.manager.biz.config.channel.ChannelService;\nimport com.alibaba.otter.manager.biz.remote.NodeRemoteService;\nimport com.alibaba.otter.shared.arbitrate.ArbitrateManageService;\nimport com.alibaba.otter.shared.common.model.config.channel.Channel;\nimport com.alibaba.otter.shared.common.model.config.channel.ChannelStatus;\n\npublic class NodeOp extends AbstractJsonScreen<String> {\n\n    private final static String    ONLINE  = \"online\";\n\n    private final static String    OFFLINE = \"offline\";\n\n    private final static String    THREAD  = \"thread\";\n    private final static String    PROFILE = \"profile\";\n\n    @Resource(name = \"channelService\")\n    private ChannelService         channelService;\n\n    @Resource(name = \"arbitrateManageService\")\n    private ArbitrateManageService arbitrateManageService;\n\n    @Resource(name = \"nodeRemoteService\")\n    private NodeRemoteService      nodeRemoteService;\n\n    public void execute(@Param(\"nid\") Long nid, @Param(\"command\") String command, @Param(\"value\") String value) {\n        try {\n            if (StringUtils.equalsIgnoreCase(command, OFFLINE)) {\n                List<Channel> channels = channelService.listByNodeId(nid, ChannelStatus.START);\n                for (Channel channel : channels) {// 重启一下对应的channel\n                    boolean result = arbitrateManageService.channelEvent().restart(channel.getId());\n                    if (result) {\n                        channelService.notifyChannel(channel.getId());// 推送一下配置\n                    }\n                }\n            } else if (StringUtils.equalsIgnoreCase(command, ONLINE)) {\n                // doNothing，自动会加入服务列表\n            } else if (StringUtils.endsWithIgnoreCase(command, THREAD)) {\n                nodeRemoteService.setThreadPoolSize(nid, Integer.valueOf(value));\n            } else if (StringUtils.endsWithIgnoreCase(command, PROFILE)) {\n                nodeRemoteService.setProfile(nid, BooleanUtils.toBoolean(value));\n            } else {\n                returnError(\"please add specfy the 'command' param.\");\n                return;\n            }\n\n            returnSuccess();\n        } catch (Exception e) {\n            String errorMsg = String.format(\"error happens while [%s] with node id [%d]\", command, nid);\n            log.error(errorMsg, e);\n            returnError(errorMsg);\n        }\n    }\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/home/module/screen/api/TopDelay.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.home.module.screen.api;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport javax.annotation.Resource;\n\nimport com.alibaba.citrus.turbine.dataresolver.Param;\nimport com.alibaba.otter.manager.biz.statistics.delay.DelayStatService;\nimport com.alibaba.otter.manager.biz.statistics.delay.param.TopDelayStat;\nimport com.alibaba.otter.manager.biz.statistics.delay.param.TopDelayStat.DataStat;\nimport com.alibaba.otter.manager.biz.statistics.throughput.ThroughputStatService;\nimport com.alibaba.otter.shared.common.model.statistics.throughput.ThroughputStat;\nimport com.alibaba.otter.shared.common.model.statistics.throughput.ThroughputType;\nimport com.alibaba.otter.shared.common.utils.JsonUtils;\n\npublic class TopDelay extends AbstractJsonScreen<String> {\n\n    @Resource(name = \"throughputStatService\")\n    private ThroughputStatService throughputStatService;\n\n    @Resource(name = \"delayStatService\")\n    private DelayStatService      delayStatService;\n\n    public void execute(@Param(\"searchKey\") String searchKey, @Param(\"topN\") int topN, @Param(\"statTime\") int minute) {\n        try {\n            if (topN <= 0) {\n                topN = 10;\n            }\n\n            if (minute <= 0) {\n                minute = 1;\n            }\n\n            List<TopDelayStat> tops = delayStatService.listTopDelayStat(searchKey, topN);\n\n            List<Long> pipelineIds = new ArrayList<Long>();\n            for (TopDelayStat top : tops) {\n                top.setStatTime(Long.valueOf(minute));\n                pipelineIds.add(top.getPipelineId());\n            }\n\n            List<ThroughputStat> stats = throughputStatService.listRealtimeThroughputByPipelineIds(pipelineIds, minute);\n            for (ThroughputStat stat : stats) {\n                for (TopDelayStat top : tops) {\n                    if (stat.getPipelineId().equals(top.getPipelineId())) {\n                        DataStat s = new DataStat(stat.getNumber(), stat.getSize());\n                        if (ThroughputType.FILE == stat.getType()) {\n                            top.setFileStat(s);\n                        } else if (ThroughputType.ROW == stat.getType()) {\n                            top.setDbStat(s);\n                        }\n                        break;\n                    }\n                }\n            }\n\n            returnSuccess(JsonUtils.marshalToString(tops));\n        } catch (Exception e) {\n            String errorMsg = String.format(\"error happens while searchKey[%s] topN [%d]\", searchKey, topN);\n            log.error(errorMsg, e);\n            returnError(errorMsg);\n        }\n    }\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/home/module/screen/monitor/MonitorTrigger.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.home.module.screen.monitor;\n\nimport javax.annotation.Resource;\n\nimport org.apache.commons.lang.StringUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.alibaba.citrus.turbine.Context;\nimport com.alibaba.citrus.turbine.dataresolver.Param;\nimport com.alibaba.otter.manager.biz.monitor.Monitor;\n\npublic class MonitorTrigger {\n\n    private static final Logger log = LoggerFactory.getLogger(\"monitorTrigger\");\n\n    @Resource(name = \"globalMonitor\")\n    private Monitor             globalMonitor;\n\n    public void execute(@Param(name = \"token\") String token, Context context) throws Exception {\n\n        if (StringUtils.isEmpty(token)) {\n            context.put(\"result\", \"empty token\");\n            return;\n        }\n\n        if (!verify(token)) {\n            context.put(\"result\", \"invalided token\");\n            return;\n        }\n\n        try {\n            globalMonitor.explore();\n        } catch (Throwable e) {\n            log.error(\"monitor trigger happens error\", e);\n            context.put(\"result\", e);\n            return;\n        }\n        context.put(\"result\", true);\n    }\n\n    private boolean verify(String token) {\n        // FIXME 简单实现\n        return \"otter\".equals(token);\n    }\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/webx/valve/AuthContextValve.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.webx.valve;\n\nimport static com.alibaba.citrus.turbine.util.TurbineUtil.getTurbineRunData;\nimport static com.alibaba.otter.shared.common.utils.Assert.assertNotNull;\nimport static com.alibaba.citrus.util.StringUtil.trimToNull;\n\nimport java.util.Enumeration;\nimport java.util.List;\nimport java.util.Map;\n\nimport javax.servlet.http.HttpServletRequest;\n\nimport org.apache.commons.lang.StringUtils;\nimport org.springframework.beans.factory.annotation.Autowired;\n\nimport com.alibaba.citrus.service.pipeline.Pipeline;\nimport com.alibaba.citrus.service.pipeline.PipelineContext;\nimport com.alibaba.citrus.service.pipeline.support.AbstractValve;\nimport com.alibaba.citrus.service.uribroker.URIBrokerService;\nimport com.alibaba.citrus.service.uribroker.uri.URIBroker;\nimport com.alibaba.citrus.turbine.TurbineRunData;\nimport com.alibaba.citrus.util.StringUtil;\nimport com.alibaba.otter.manager.web.common.WebConstant;\nimport com.alibaba.otter.manager.web.common.api.ApiAuthService;\nimport com.alibaba.otter.manager.web.webx.valve.auth.RegExpURLAnalyze;\nimport com.alibaba.otter.shared.common.model.user.AuthorizeType;\nimport com.alibaba.otter.shared.common.model.user.User;\n\n/**\n * 权限控制\n * \n * @author jianghang 2011-11-11 上午11:46:11\n * @version 4.0.0\n */\npublic class AuthContextValve extends AbstractValve {\n\n    private static final String DEFAULT_ACTION_PARAM_NAME = \"action\";\n    private static final String DEFAULT_EVENT_PATTERN     = \"event_submit_do_\";\n    private static final String IMAGE_BUTTON_SUFFIX_1     = \".x\";\n    private static final String IMAGE_BUTTON_SUFFIX_2     = \".y\";\n    private static final String IMAGE_BUTTON_SUFFIX_3     = \".X\";\n    private static final String IMAGE_BUTTON_SUFFIX_4     = \".Y\";\n\n    @Autowired\n    private HttpServletRequest  request;\n    @Autowired\n    private URIBrokerService    uriBrokerService;\n    @Autowired\n    private RegExpURLAnalyze    urlAnalyze;\n    @Autowired\n    private ApiAuthService      apiAuthService;\n\n    private String              loginLink                 = WebConstant.OTTER_LOGIN_LINK;\n    private String              forbiddenLink             = WebConstant.ERROR_FORBIDDEN_Link;\n    private String              redirectParmeter          = \"Done\";\n    private String              actionParam;\n\n    protected void init() throws Exception {\n        if (actionParam == null) {\n            actionParam = DEFAULT_ACTION_PARAM_NAME;\n        }\n    }\n\n    public void invoke(PipelineContext pipelineContext) throws Exception {\n        TurbineRunData rundata = getTurbineRunData(request);\n\n        // TODO 走 api 的验证\n        if (isAPI(rundata)) {\n            if (apiAuthService.auth(rundata)) {\n                pipelineContext.invokeNext();\n            } else {\n                redirect(pipelineContext, rundata, forbiddenLink); // TODO 需要转跳到 json 格式的 link\n            }\n            return;\n        }\n\n        // 得到请求URL相对路径(不包含域名/端口信息)\n        String requestUrl = rundata.getRequest().getRequestURI();\n        List<AuthorizeType> result = urlAnalyze.check(requestUrl);\n        String action = StringUtil.toCamelCase(trimToNull(rundata.getParameters().getString(actionParam)));\n        String eventName = getEventName();\n\n        // 首先判断是否登录\n        User user = (User) rundata.getRequest().getSession().getAttribute(WebConstant.USER_SESSION_KEY);\n\n        if (StringUtils.isNotEmpty(action)) {\n            result.addAll(urlAnalyze.check(action, eventName));\n        }\n\n        if (result.isEmpty()) {\n            // 访问的连接不符合权限匹配规则，跳转到登录页面\n            redirect(pipelineContext, rundata, forbiddenLink);\n        } else {\n            if (null == user) {\n                // 如果用户未登录，则判断访问连接的权限匹配集合：\n                // 1.如果有高于匿名权限，则跳转到登录页面；\n                // 2.如果集合中只包含匿名权限，则通过。\n                if (result.contains(AuthorizeType.OPERATOR) || result.contains(AuthorizeType.ADMIN)) {\n                    redirect(pipelineContext, rundata, loginLink);\n                } else {\n                    pipelineContext.invokeNext();\n                }\n            } else {\n                // 如果用户已经登录，则判断访问连接的权限匹配集合：\n                // 1.如果权限集合有等于（低于）用户权限，则通过；\n                // 2.如果权限集合有高于用户权限，则跳转到登录页面。\n                if (compareAuth(user.getAuthorizeType(), result)) {\n                    pipelineContext.invokeNext();\n                } else {\n                    redirect(pipelineContext, rundata, forbiddenLink);\n                }\n            }\n        }\n    }\n\n    // TODO 目前先简单实现\n    protected boolean isAPI(TurbineRunData rundata) {\n        String requestUrl = rundata.getRequest().getRequestURI();\n        return StringUtils.containsIgnoreCase(requestUrl, \"/api/\");\n    }\n\n    private boolean compareAuth(AuthorizeType sType, List<AuthorizeType> dTypes) {\n        // 如果用户权限为超级管理员，则返回true\n        if (sType.isAdmin()) {\n            return true;\n        }\n        // 用户权限与请求权限集合进行比较，如果集合中有高于用户权限，则返回false\n        for (AuthorizeType dType : dTypes) {\n            if ((sType.isOperator() && dType.isAdmin()) || (sType.isAnonymous() && !dType.isAnonymous())) {\n                return false;\n            }\n        }\n\n        return true;\n    }\n\n    public boolean isAuthenticated(TurbineRunData rundata) {\n        Map<String, String> user = (Map<String, String>) rundata.getRequest().getSession().getAttribute(WebConstant.OTTER_USER_SESSION_KEY);\n        if (user == null) {\n            return false;\n        }\n        return true;\n    }\n\n    private void redirect(PipelineContext pipelineContext, TurbineRunData rundata, String uriBroker) {\n        URIBroker urlBroker = assertNotNull(uriBrokerService.getURIBroker(uriBroker),\n                                            \"uriBroker get from loginLink should not be null\");\n        urlBroker.addQueryData(redirectParmeter, getRequestUrlWithQueryString());\n        rundata.setRedirectLocation(urlBroker.render());\n        pipelineContext.breakPipeline(Pipeline.TOP_LABEL);\n    }\n\n    private String getRequestUrlWithQueryString() {\n        StringBuffer requestUrl = request.getRequestURL();\n        String queryString = StringUtil.trimToNull(request.getQueryString());\n        if (!StringUtil.isBlank(queryString)) {\n            requestUrl.append(\"?\").append(queryString);\n        }\n        return requestUrl.toString();\n    }\n\n    /**\n     * 取得key=eventSubmit_doXyz, value不为空的参数。\n     */\n    private String getEventName() {\n        String event = null;\n\n        @SuppressWarnings(\"unchecked\")\n        Enumeration<String> e = request.getParameterNames();\n\n        while (e.hasMoreElements()) {\n            String originalKey = e.nextElement();\n            String paramKey = StringUtil.toLowerCaseWithUnderscores(originalKey);\n\n            if (paramKey.startsWith(DEFAULT_EVENT_PATTERN) && !StringUtil.isBlank(request.getParameter(originalKey))) {\n                int startIndex = DEFAULT_EVENT_PATTERN.length();\n                int endIndex = paramKey.length();\n\n                // 支持<input type=\"image\">\n                if (paramKey.endsWith(IMAGE_BUTTON_SUFFIX_1)) {\n                    endIndex -= IMAGE_BUTTON_SUFFIX_1.length();\n                } else if (paramKey.endsWith(IMAGE_BUTTON_SUFFIX_2)) {\n                    endIndex -= IMAGE_BUTTON_SUFFIX_2.length();\n                } else if (paramKey.endsWith(IMAGE_BUTTON_SUFFIX_3)) {\n                    endIndex -= IMAGE_BUTTON_SUFFIX_3.length();\n                } else if (paramKey.endsWith(IMAGE_BUTTON_SUFFIX_4)) {\n                    endIndex -= IMAGE_BUTTON_SUFFIX_4.length();\n                }\n\n                event = StringUtil.trimToNull(paramKey.substring(startIndex, endIndex));\n\n                if (event != null) {\n                    break;\n                }\n            }\n        }\n\n        return event;\n    }\n\n    public void setLoginLink(String loginLink) {\n        this.loginLink = loginLink;\n    }\n\n    public void setRedirectParmeter(String redirectParmeter) {\n        this.redirectParmeter = redirectParmeter;\n    }\n\n    public void setActionParam(String actionParam) {\n        this.actionParam = actionParam;\n    }\n\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/webx/valve/PrepareExceptionValve.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.webx.valve;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\n\nimport com.alibaba.citrus.service.pipeline.PipelineContext;\nimport com.alibaba.citrus.service.pipeline.support.AbstractValve;\nimport com.alibaba.citrus.webx.util.ErrorHandlerHelper;\nimport com.alibaba.otter.shared.arbitrate.exception.ArbitrateException;\n\n/**\n * @author jianghang 2011-8-30 下午02:52:04\n */\npublic class PrepareExceptionValve extends AbstractValve {\n\n    private static Logger       log = LoggerFactory.getLogger(PrepareExceptionValve.class);\n\n    @Autowired\n    private HttpServletRequest  request;\n\n    @Autowired\n    private HttpServletResponse response;\n\n    @Override\n    public void invoke(PipelineContext pipelineContext) throws Exception {\n        clearBuffer(response);//\n        Exception e = (Exception) pipelineContext.getAttribute(\"exception\");\n        log.error(e.getMessage(), e);\n\n        Throwable cause = e.getCause();\n        if (cause != null && cause instanceof ArbitrateException) {\n            e = (ArbitrateException) cause;\n        }\n\n        ErrorHandlerHelper errorHandlerHelper = ErrorHandlerHelper.getInstance(request);\n        errorHandlerHelper.setException(e);\n        pipelineContext.invokeNext();\n    }\n\n    private void clearBuffer(HttpServletResponse response) {\n        if (!response.isCommitted()) {\n            response.resetBuffer();\n        }\n    }\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/webx/valve/auth/AuthorizeProtected.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.webx.valve.auth;\n\nimport com.alibaba.otter.manager.web.webx.valve.auth.action.ActionProtected;\nimport com.alibaba.otter.manager.web.webx.valve.auth.url.URLProtected;\n\n/**\n * 一抽象的接口\n * \n * @author jianghang 2011-11-11 下午01:11:17\n * @version 4.0.0\n */\npublic class AuthorizeProtected {\n\n    private URLProtected    urlProtected;\n    private ActionProtected actionProtected;\n\n    public URLProtected getUrlProtected() {\n        return urlProtected;\n    }\n\n    public void setUrlProtected(URLProtected urlProtected) {\n        this.urlProtected = urlProtected;\n    }\n\n    public ActionProtected getActionProtected() {\n        return actionProtected;\n    }\n\n    public void setActionProtected(ActionProtected actionProtected) {\n        this.actionProtected = actionProtected;\n    }\n\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/webx/valve/auth/RegExpURLAnalyze.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.webx.valve.auth;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport com.alibaba.otter.shared.common.model.user.AuthorizeType;\n\n/**\n * 基于正则的URL匹配\n * \n * @author jianghang 2011-11-11 下午12:24:38\n * @version 4.0.0\n */\n\npublic class RegExpURLAnalyze {\n\n    private AuthorizeProtected anonymous;\n    private AuthorizeProtected operator;\n    private AuthorizeProtected admin;\n\n    public List<AuthorizeType> check(String requestUrl) {\n        List<AuthorizeType> result = new ArrayList<AuthorizeType>();\n        if (anonymous != null && anonymous.getUrlProtected() != null && anonymous.getUrlProtected().check(requestUrl)) {\n            result.add(AuthorizeType.ANONYMOUS);\n        }\n\n        if (operator != null && operator.getUrlProtected() != null && operator.getUrlProtected().check(requestUrl)) {\n            result.add(AuthorizeType.OPERATOR);\n        }\n\n        if (admin != null && admin.getUrlProtected() != null && admin.getUrlProtected().check(requestUrl)) {\n            result.add(AuthorizeType.ADMIN);\n        }\n        return result;\n    }\n\n    public List<AuthorizeType> check(String action, String method) {\n        List<AuthorizeType> result = new ArrayList<AuthorizeType>();\n        if (anonymous != null && anonymous.getActionProtected() != null\n            && anonymous.getActionProtected().check(action, method)) {\n            result.add(AuthorizeType.ANONYMOUS);\n        }\n\n        if (operator != null && operator.getActionProtected() != null\n            && operator.getActionProtected().check(action, method)) {\n            result.add(AuthorizeType.OPERATOR);\n        }\n\n        if (admin != null && admin.getActionProtected() != null && admin.getActionProtected().check(action, method)) {\n            result.add(AuthorizeType.ADMIN);\n        }\n        return result;\n    }\n\n    public void setAnonymous(AuthorizeProtected anonymous) {\n        this.anonymous = anonymous;\n    }\n\n    public void setOperator(AuthorizeProtected operator) {\n        this.operator = operator;\n    }\n\n    public void setAdmin(AuthorizeProtected admin) {\n        this.admin = admin;\n    }\n\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/webx/valve/auth/action/ActionPatternHolder.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.webx.valve.auth.action;\n\nimport org.apache.oro.text.regex.Pattern;\n\npublic class ActionPatternHolder {\n\n    private String  actionName;\n    private Pattern actionPattern;\n    private String  methodName;\n    private Pattern methodPattern;\n\n    public String getActionName() {\n        return actionName;\n    }\n\n    public void setActionName(String actionName) {\n        this.actionName = actionName;\n    }\n\n    public Pattern getActionPattern() {\n        return actionPattern;\n    }\n\n    public void setActionPattern(Pattern actionPattern) {\n        this.actionPattern = actionPattern;\n    }\n\n    public String getMethodName() {\n        return methodName;\n    }\n\n    public void setMethodName(String methodName) {\n        this.methodName = methodName;\n    }\n\n    public Pattern getMethodPattern() {\n        return methodPattern;\n    }\n\n    public void setMethodPattern(Pattern methodPattern) {\n        this.methodPattern = methodPattern;\n    }\n\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/webx/valve/auth/action/ActionProtected.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.webx.valve.auth.action;\n\n/**\n * 基于url的匹配\n * \n * @author jianghang 2011-11-11 下午12:30:43\n * @version 4.0.0\n */\npublic interface ActionProtected {\n\n    public boolean check(String action, String method);\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/webx/valve/auth/action/ActionProtectedEditor.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.webx.valve.auth.action;\n\nimport java.beans.PropertyEditorSupport;\nimport java.io.BufferedReader;\nimport java.io.IOException;\nimport java.io.StringReader;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.apache.commons.lang.StringUtils;\nimport org.apache.oro.text.regex.MalformedPatternException;\nimport org.apache.oro.text.regex.Pattern;\nimport org.apache.oro.text.regex.Perl5Compiler;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\npublic class ActionProtectedEditor extends PropertyEditorSupport {\n\n    private static final Logger logger = LoggerFactory.getLogger(ActionProtectedEditor.class);\n\n    public void setAsText(String text) throws IllegalArgumentException {\n        ActionProtected urlProtected = new ActionProtectedImpl(convertTextToPatterns(text));\n        setValue(urlProtected);\n    }\n\n    /**\n     * 把字符串文本转换成一堆Pattern,用于匹配URL\n     */\n    private List<ActionPatternHolder> convertTextToPatterns(String text) {\n        List<ActionPatternHolder> list = new ArrayList<ActionPatternHolder>();\n        if (StringUtils.isNotEmpty(text)) {\n            BufferedReader br = new BufferedReader(new StringReader(text));\n            int counter = 0;\n            String line;\n            while (true) {\n                counter++;\n                try {\n                    line = br.readLine();\n                } catch (IOException ioe) {\n                    throw new IllegalArgumentException(ioe.getMessage());\n                }\n                if (line == null) {\n                    break;\n                }\n                line = StringUtils.trim(line);\n                if (StringUtils.isBlank(line)) {\n                    continue;\n                }\n                if (logger.isDebugEnabled()) {\n                    logger.debug(\"Line \" + counter + \": \" + line);\n                }\n                list.add(convertStringToPattern(line));\n            }\n        }\n        return list;\n    }\n\n    /**\n     * 把字符串转成Pattern和UrlType\n     * \n     * @param perl5RegExp\n     * @return\n     */\n    private ActionPatternHolder convertStringToPattern(String line) {\n        ActionPatternHolder holder = new ActionPatternHolder();\n        String[] strs = org.apache.commons.lang.StringUtils.split(line, \"|\");\n        if (strs.length != 2) {\n            throw new IllegalArgumentException(\"illegal expression: \" + line);\n        }\n        Pattern compiledPattern;\n        Perl5Compiler compiler = new Perl5Compiler();\n        try {\n            holder.setActionName(strs[0]);\n            compiledPattern = compiler.compile(strs[0], Perl5Compiler.READ_ONLY_MASK);\n            holder.setActionPattern(compiledPattern);\n        } catch (MalformedPatternException mpe) {\n            throw new IllegalArgumentException(\"Malformed regular expression: \" + strs[0]);\n        }\n\n        try {\n            holder.setMethodName(strs[1]);\n            compiledPattern = compiler.compile(strs[1], Perl5Compiler.READ_ONLY_MASK);\n            holder.setMethodPattern(compiledPattern);\n        } catch (MalformedPatternException mpe) {\n            throw new IllegalArgumentException(\"Malformed regular expression: \" + strs[1]);\n        }\n\n        return holder;\n    }\n\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/webx/valve/auth/action/ActionProtectedImpl.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.webx.valve.auth.action;\n\nimport static com.alibaba.citrus.util.StringUtil.trimToNull;\n\nimport java.util.Iterator;\nimport java.util.List;\n\nimport org.apache.commons.lang.StringUtils;\nimport org.apache.oro.text.regex.PatternMatcher;\nimport org.apache.oro.text.regex.Perl5Matcher;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.alibaba.citrus.util.StringUtil;\n\npublic class ActionProtectedImpl implements ActionProtected {\n\n    private static final Logger      logger = LoggerFactory.getLogger(ActionProtectedImpl.class);\n    public List<ActionPatternHolder> actionPatternList;\n    private String                   actionParam;\n\n    public ActionProtectedImpl(){\n\n    }\n\n    public ActionProtectedImpl(List<ActionPatternHolder> actionPatternList){\n        this.actionPatternList = actionPatternList;\n    }\n\n    /**\n     * 设置在URL query中代表action的参数名。\n     */\n    public void setActionParam(String actionParam) {\n        this.actionParam = trimToNull(actionParam);\n    }\n\n    public String getActionParam() {\n        return actionParam;\n    }\n\n    public boolean check(String action, String method) {\n        if (!StringUtil.isBlank(action)) {\n            PatternMatcher matcher = new Perl5Matcher();\n            Iterator<ActionPatternHolder> iter = actionPatternList.iterator();\n            while (iter.hasNext()) {\n                ActionPatternHolder holder = (ActionPatternHolder) iter.next();\n                if (StringUtils.isNotEmpty(action) && matcher.matches(action, holder.getActionPattern())\n                    && StringUtils.isNotEmpty(method) && matcher.matches(method, holder.getMethodPattern())) {\n                    if (logger.isDebugEnabled()) {\n                        logger.debug(\"Candidate is: '\" + action + \"|\" + method + \"'; pattern is \"\n                                     + holder.getActionName() + \"|\" + holder.getMethodName() + \"; matched=true\");\n                    }\n                    return true;\n                }\n            }\n        }\n\n        return false;\n    }\n\n    public void setActionPatternList(List<ActionPatternHolder> actionPatternList) {\n        this.actionPatternList = actionPatternList;\n    }\n\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/webx/valve/auth/url/URLPatternHolder.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.webx.valve.auth.url;\n\nimport org.apache.oro.text.regex.Pattern;\n\n/**\n * 封装url pattern匹配\n * \n * @author jianghang 2011-11-11 下午12:38:06\n * @version 4.0.0\n */\npublic class URLPatternHolder {\n\n    private String  url;\n    private Pattern compiledPattern;\n\n    public Pattern getCompiledPattern() {\n        return compiledPattern;\n    }\n\n    public void setCompiledPattern(Pattern compiledPattern) {\n        this.compiledPattern = compiledPattern;\n    }\n\n    public String getUrl() {\n        return url;\n    }\n\n    public void setUrl(String url) {\n        this.url = url;\n    }\n\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/webx/valve/auth/url/URLProtected.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.webx.valve.auth.url;\n\n/**\n * 基于url的匹配\n * \n * @author jianghang 2011-11-11 下午12:30:43\n * @version 4.0.0\n */\npublic interface URLProtected {\n\n    public boolean check(String requestUrl);\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/webx/valve/auth/url/URLProtectedEditor.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.webx.valve.auth.url;\n\nimport java.beans.PropertyEditorSupport;\nimport java.io.BufferedReader;\nimport java.io.IOException;\nimport java.io.StringReader;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.apache.commons.lang.StringUtils;\nimport org.apache.oro.text.regex.MalformedPatternException;\nimport org.apache.oro.text.regex.Pattern;\nimport org.apache.oro.text.regex.Perl5Compiler;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\npublic class URLProtectedEditor extends PropertyEditorSupport {\n\n    private static final Logger logger = LoggerFactory.getLogger(URLProtectedEditor.class);\n\n    @Override\n    public void setAsText(String text) throws IllegalArgumentException {\n        URLProtected urlProtected = new URLProtectedImpl(convertTextToPatterns(text));\n        setValue(urlProtected);\n    }\n\n    /**\n     * 把字符串文本转换成一堆Pattern,用于匹配URL\n     */\n    private List<URLPatternHolder> convertTextToPatterns(String text) {\n        List<URLPatternHolder> list = new ArrayList<URLPatternHolder>();\n        if (StringUtils.isNotEmpty(text)) {\n            BufferedReader br = new BufferedReader(new StringReader(text));\n            int counter = 0;\n            String line;\n            while (true) {\n                counter++;\n                try {\n                    line = br.readLine();\n                } catch (IOException ioe) {\n                    throw new IllegalArgumentException(ioe.getMessage());\n                }\n                if (line == null) {\n                    break;\n                }\n                line = StringUtils.trim(line);\n                if (StringUtils.isBlank(line)) {\n                    continue;\n                }\n                if (logger.isDebugEnabled()) {\n                    logger.debug(\"Line \" + counter + \": \" + line);\n                }\n                list.add(convertStringToPattern(line));\n            }\n        }\n        return list;\n    }\n\n    /**\n     * 把字符串转成Pattern和UrlType\n     * \n     * @param perl5RegExp\n     * @return\n     */\n    private URLPatternHolder convertStringToPattern(String line) {\n        URLPatternHolder holder = new URLPatternHolder();\n        Pattern compiledPattern;\n        Perl5Compiler compiler = new Perl5Compiler();\n        String perl5RegExp = line;\n        try {\n            compiledPattern = compiler.compile(perl5RegExp, Perl5Compiler.READ_ONLY_MASK);\n            holder.setCompiledPattern(compiledPattern);\n        } catch (MalformedPatternException mpe) {\n            throw new IllegalArgumentException(\"Malformed regular expression: \" + perl5RegExp);\n        }\n\n        if (logger.isDebugEnabled()) {\n            logger.debug(\"Added regular expression: \" + compiledPattern.getPattern().toString());\n        }\n        return holder;\n    }\n\n}\n"
  },
  {
    "path": "manager/web/src/main/java/com/alibaba/otter/manager/web/webx/valve/auth/url/URLProtectedImpl.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.manager.web.webx.valve.auth.url;\n\nimport java.util.ArrayList;\nimport java.util.Iterator;\nimport java.util.List;\n\nimport org.apache.commons.lang.StringUtils;\nimport org.apache.oro.text.regex.PatternMatcher;\nimport org.apache.oro.text.regex.Perl5Matcher;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * 基于url匹配的实现\n * \n * @author jianghang 2011-11-11 下午12:41:30\n * @version 4.0.0\n */\npublic class URLProtectedImpl implements URLProtected {\n\n    private static final Logger    logger           = LoggerFactory.getLogger(URLProtectedImpl.class);\n    private List<URLPatternHolder> urlProtectedList = new ArrayList<URLPatternHolder>();\n\n    public URLProtectedImpl(){\n    }\n\n    public URLProtectedImpl(List<URLPatternHolder> urlProtectedList){\n        this.urlProtectedList = urlProtectedList;\n    }\n\n    public boolean check(String requestUrl) {\n        if (StringUtils.isBlank(requestUrl)) {\n            return false;\n        }\n\n        if (logger.isDebugEnabled()) {\n            logger.debug(\"Converted URL to lowercase, from: '\" + requestUrl + \"'; to: '\" + requestUrl + \"'\");\n        }\n\n        PatternMatcher matcher = new Perl5Matcher();\n\n        Iterator<URLPatternHolder> iter = urlProtectedList.iterator();\n\n        while (iter.hasNext()) {\n            URLPatternHolder holder = (URLPatternHolder) iter.next();\n\n            if (matcher.matches(requestUrl, holder.getCompiledPattern())) {\n                if (logger.isDebugEnabled()) {\n                    logger.debug(\"Candidate is: '\" + requestUrl + \"'; pattern is \"\n                                 + holder.getCompiledPattern().getPattern() + \"; matched=true\");\n                }\n                return true;\n            }\n        }\n        return false;\n    }\n\n    public List<URLPatternHolder> getUrlProtectedList() {\n        return urlProtectedList;\n    }\n\n    public void setUrlProtectedList(List<URLPatternHolder> urlProtectedList) {\n        this.urlProtectedList = urlProtectedList;\n    }\n\n    public void addUrlProtected(URLPatternHolder urlProtected) {\n        this.urlProtectedList.add(urlProtected);\n    }\n\n}\n"
  },
  {
    "path": "node/canal/pom.xml",
    "content": "<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\">\n\t<modelVersion>4.0.0</modelVersion>\n\t<parent>\n\t\t<groupId>com.alibaba.otter</groupId>\n\t\t<artifactId>node</artifactId>\n\t\t<version>4.2.19-SNAPSHOT</version>\n\t\t<relativePath>../pom.xml</relativePath>\n\t</parent>\n\t<groupId>com.alibaba.otter</groupId>\n\t<artifactId>node.canal</artifactId>\n\t<packaging>jar</packaging>\n\t<name>canal extend module for otter</name>\n\t<url>http://github.com/alibaba/otter</url>\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>com.alibaba.otter</groupId>\n\t\t\t<artifactId>shared.push</artifactId>\n\t\t\t<version>${project.version}</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>com.alibaba.otter</groupId>\n\t\t\t<artifactId>canal.instance.manager</artifactId>\n\t\t\t<version>${otter_canal_version}</version>\n\t\t\t<exclusions>\n\t\t\t\t<exclusion>\n\t\t\t\t\t<groupId>org.springframework</groupId>\n\t\t\t\t\t<artifactId>spring</artifactId>\n\t\t\t\t</exclusion>\n\t\t\t</exclusions>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>com.alibaba.otter</groupId>\n\t\t\t<artifactId>canal.parse</artifactId>\n\t\t\t<version>${otter_canal_version}</version>\n\t\t\t<exclusions>\n\t\t\t\t<exclusion>\n\t\t\t\t\t<groupId>org.springframework</groupId>\n\t\t\t\t\t<artifactId>spring</artifactId>\n\t\t\t\t</exclusion>\n\t\t\t</exclusions>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>com.alibaba.otter</groupId>\n\t\t\t<artifactId>canal.server</artifactId>\n\t\t\t<version>${otter_canal_version}</version>\n\t\t\t<exclusions>\n\t\t\t\t<exclusion>\n\t\t\t\t\t<groupId>org.springframework</groupId>\n\t\t\t\t\t<artifactId>spring</artifactId>\n\t\t\t\t</exclusion>\n\t\t\t\t<exclusion>\n\t\t\t\t\t<groupId>org.apache.kafka</groupId>\n\t\t\t\t\t<artifactId>kafka_2.11</artifactId>\t\t\t\t\n\t\t\t\t</exclusion>\n\t\t\t</exclusions>\n\t\t</dependency>\n\t\t<!-- test dependency -->\n\t\t<dependency>\n\t\t\t<groupId>org.jtester</groupId>\n\t\t\t<artifactId>jtester</artifactId>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>junit</groupId>\n\t\t\t<artifactId>junit</artifactId>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\t</dependencies>\n</project>\n"
  },
  {
    "path": "node/canal/src/main/java/com/alibaba/otter/canal/extend/communication/CanalCommmunicationClient.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.canal.extend.communication;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.apache.commons.lang.StringUtils;\nimport org.apache.commons.lang.math.RandomUtils;\nimport org.springframework.beans.factory.DisposableBean;\n\nimport com.alibaba.otter.shared.communication.core.CommunicationClient;\nimport com.alibaba.otter.shared.communication.core.exception.CommunicationException;\nimport com.alibaba.otter.shared.communication.core.impl.DefaultCommunicationClientImpl;\nimport com.alibaba.otter.shared.communication.core.model.Callback;\nimport com.alibaba.otter.shared.communication.core.model.Event;\n\n/**\n * 封装了基于communication通讯的工具\n * \n * @author jianghang 2011-10-18 下午02:18:04\n * @version 4.0.0\n */\npublic class CanalCommmunicationClient implements DisposableBean {\n\n    private CommunicationClient delegate;\n    private List<String>        managerAddress;\n    private volatile int        index = 0;\n\n    /**\n     * 指定manager，进行event调用\n     */\n    public Object callManager(final Event event) {\n        CommunicationException ex = null;\n        Object object = null;\n        for (int i = index; i < index + managerAddress.size(); i++) { // 循环一次manager的所有地址\n            String address = managerAddress.get(i % managerAddress.size());\n            try {\n                object = delegate.call(address, event);\n                index = i; // 更新一下上一次成功的地址\n                return object;\n            } catch (CommunicationException e) {\n                // retry next address;\n                ex = e;\n            }\n        }\n\n        throw ex; // 走到这一步，说明肯定有出错了\n    }\n\n    /**\n     * 指定manager，进行event调用\n     * \n     * <pre>\n     * 注意：该方法为异步调用\n     * </pre>\n     */\n    public void callManager(final Event event, final Callback callback) {\n        if (delegate instanceof DefaultCommunicationClientImpl) {\n            ((DefaultCommunicationClientImpl) delegate).submit(new Runnable() {\n\n                public void run() {\n                    Object obj = callManager(event);\n                    callback.call(obj);\n                }\n            });\n        }\n    }\n\n    public void destroy() throws Exception {\n    }\n\n    // ================== setter / getter =====================\n\n    public void setDelegate(CommunicationClient delegate) {\n        this.delegate = delegate;\n    }\n\n    public void setManagerAddress(String managerAddress) {\n        String server = StringUtils.replace(managerAddress, \";\", \",\");\n        String[] servers = StringUtils.split(server, ',');\n        this.managerAddress = Arrays.asList(servers);\n        this.index = RandomUtils.nextInt(this.managerAddress.size()); // 随机选择一台机器\n    }\n\n}\n"
  },
  {
    "path": "node/canal/src/main/java/com/alibaba/otter/canal/extend/communication/CanalConfigClient.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.canal.extend.communication;\n\nimport com.alibaba.otter.canal.common.CanalException;\nimport com.alibaba.otter.canal.instance.manager.model.Canal;\nimport com.alibaba.otter.shared.communication.model.canal.FindCanalEvent;\nimport com.alibaba.otter.shared.communication.model.canal.FindFilterEvent;\n\n/**\n * 对应canal的配置\n * \n * @author jianghang 2012-7-4 下午03:09:17\n * @version 4.1.0\n */\npublic class CanalConfigClient {\n\n    private CanalCommmunicationClient delegate;\n\n    /**\n     * 根据对应的destinantion查询Canal信息\n     */\n    public Canal findCanal(String destination) {\n        FindCanalEvent event = new FindCanalEvent();\n        event.setDestination(destination);\n        try {\n            Object obj = delegate.callManager(event);\n            if (obj != null && obj instanceof Canal) {\n                return (Canal) obj;\n            } else {\n                throw new CanalException(\"No Such Canal by [\" + destination + \"]\");\n            }\n        } catch (Exception e) {\n            throw new CanalException(\"call_manager_error\", e);\n        }\n    }\n\n    /**\n     * 根据对应的destinantion查询filter信息\n     */\n    public String findFilter(String destination) {\n        FindFilterEvent event = new FindFilterEvent();\n        event.setDestination(destination);\n        try {\n            Object obj = delegate.callManager(event);\n            if (obj != null && obj instanceof String) {\n                return (String) obj;\n            } else {\n                throw new CanalException(\"No Such Canal by [\" + destination + \"]\");\n            }\n        } catch (Exception e) {\n            throw new CanalException(\"call_manager_error\", e);\n        }\n    }\n\n    // ================== setter / getter ===============\n\n    public void setDelegate(CanalCommmunicationClient delegate) {\n        this.delegate = delegate;\n    }\n\n}\n"
  },
  {
    "path": "node/canal/src/main/java/com/alibaba/otter/canal/extend/ha/AuthenticationInfoUtils.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.canal.extend.ha;\n\nimport org.apache.commons.beanutils.BeanUtils;\n\nimport com.alibaba.otter.canal.common.CanalException;\nimport com.alibaba.otter.canal.parse.support.AuthenticationInfo;\nimport com.alibaba.otter.common.push.supplier.DatasourceInfo;\n\n/**\n * @author zebin.xuzb 2013-1-23 下午4:54:33\n * @since 4.1.3\n */\npublic abstract class AuthenticationInfoUtils {\n\n    private AuthenticationInfoUtils(){\n    }\n\n    public static AuthenticationInfo createFrom(DatasourceInfo datasourceInfo) {\n        AuthenticationInfo authenticationInfo = new AuthenticationInfo();\n        try {\n            BeanUtils.copyProperties(authenticationInfo, datasourceInfo);\n        } catch (Exception e) {\n            throw new CanalException(e);\n        }\n        return authenticationInfo;\n    }\n\n}\n"
  },
  {
    "path": "node/canal/src/main/java/com/alibaba/otter/canal/extend/ha/MediaHAController.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.canal.extend.ha;\n\nimport org.apache.commons.lang.StringUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.alibaba.otter.canal.common.AbstractCanalLifeCycle;\nimport com.alibaba.otter.canal.parse.CanalHASwitchable;\nimport com.alibaba.otter.canal.parse.exception.CanalHAException;\nimport com.alibaba.otter.canal.parse.ha.CanalHAController;\nimport com.alibaba.otter.canal.parse.support.AuthenticationInfo;\nimport com.alibaba.otter.common.push.supplier.DatasourceChangeCallback;\nimport com.alibaba.otter.common.push.supplier.DatasourceInfo;\nimport com.alibaba.otter.common.push.supplier.DatasourceSupplier;\nimport com.alibaba.otter.common.push.supplier.media.MediaDatasourceSupplier;\n\n/**\n * 基于media的HA控制机制\n * \n * @author jianghang 2012-7-6 下午02:48:21\n * @version 4.1.0\n */\npublic class MediaHAController extends AbstractCanalLifeCycle implements CanalHAController {\n\n    private static Logger               log = LoggerFactory.getLogger(MediaHAController.class);\n\n    private String                      group;\n    private String                      customUser;\n    private String                      customPasswd;\n    private String                      customSchema;\n\n    private DatasourceSupplier          supplier;\n    private CanalHASwitchable           canalHASwitchable;\n    private volatile AuthenticationInfo availableAuthenticationInfo;\n\n    public MediaHAController(String group){\n        this.group = group;\n    }\n\n    public MediaHAController(String group, String customUser, String customPasswd, String customSchema){\n        this.group = group;\n        this.customUser = customUser;\n        this.customPasswd = customPasswd;\n        this.customSchema = customSchema;\n    }\n\n    public void start() throws CanalHAException {\n        super.start();\n\n        if (this.supplier == null) {\n            validate();\n            this.supplier = MediaDatasourceSupplier.newInstance(group);\n        }\n        if (!this.supplier.isStart()) {\n            this.supplier.start();\n        }\n\n        DatasourceInfo fetched = this.supplier.fetchMaster();\n        AuthenticationInfo masterFetched = AuthenticationInfoUtils.createFrom(fetched);\n\n        log.info(String.format(\"medialHAController started for  goup:[%s], and first auth info is : [%s]\", this.group,\n                               masterFetched));\n\n        this.availableAuthenticationInfo = customInfoIfNecessay(masterFetched);\n\n        log.info(String.format(\"medialHAController customed for goup:[%s], and first auth info is : [%s]\", this.group,\n                               this.availableAuthenticationInfo));\n\n        this.supplier.addSwtichCallback(new DatasourceChangeCallback() {\n\n            @Override\n            public void masterChanged(DatasourceInfo newMaster) {\n                AuthenticationInfo newAuthenticationInfo = AuthenticationInfoUtils.createFrom(newMaster);\n                switchEventSource(newAuthenticationInfo);\n            }\n        });\n    }\n\n    private void validate() {\n        if (StringUtils.isEmpty(this.group)) {\n            throw new IllegalStateException(String.format(\"app or group is empty, app is [%s] , group is [%s]\",\n                                                          this.group));\n        }\n    }\n\n    public void stop() throws CanalHAException {\n        super.stop();\n        this.supplier.stop();\n    }\n\n    private void switchEventSource(AuthenticationInfo newMaster) {\n        log.warn(String.format(\"MediaHAController received a datasource swith from [%s] to [%s]\",\n                               availableAuthenticationInfo, newMaster));\n\n        customInfoIfNecessay(newMaster);\n\n        log.warn(String.format(\"MediaHAController customed a datasource swith from [%s] to [%s]\",\n                               availableAuthenticationInfo, newMaster));\n\n        availableAuthenticationInfo = newMaster;\n        this.canalHASwitchable.doSwitch(newMaster);\n    }\n\n    public void setCanalHASwitchable(CanalHASwitchable canalHASwitchable) {\n        this.canalHASwitchable = canalHASwitchable;\n    }\n\n    public CanalHASwitchable getCanalHASwitchable() {\n        return canalHASwitchable;\n    }\n\n    public String getGroup() {\n        return group;\n    }\n\n    public DatasourceSupplier getSupplier() {\n        return supplier;\n    }\n\n    public void setGroup(String group) {\n        this.group = group;\n    }\n\n    public void setSupplier(DatasourceSupplier supplier) {\n        this.supplier = supplier;\n    }\n\n    public AuthenticationInfo getAvailableAuthenticationInfo() {\n        return availableAuthenticationInfo;\n    }\n\n    public void setAvailableAuthenticationInfo(AuthenticationInfo availableAuthenticationInfo) {\n        this.availableAuthenticationInfo = availableAuthenticationInfo;\n    }\n\n    /**\n     * override custom field\n     * \n     * @param authenticationInfo\n     */\n    protected AuthenticationInfo customInfoIfNecessay(AuthenticationInfo authenticationInfo) {\n        if (StringUtils.isNotBlank(customUser)) {\n            authenticationInfo.setUsername(customUser);\n        }\n        if (StringUtils.isNotBlank(customPasswd)) {\n            authenticationInfo.setPassword(customPasswd);\n        }\n        if (StringUtils.isNotBlank(customSchema)) {\n            authenticationInfo.setDefaultDatabaseName(customSchema);\n        }\n\n        return authenticationInfo;\n    }\n\n}\n"
  },
  {
    "path": "node/canal/src/main/resources/spring/otter-canal-communication.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<beans xmlns=\"http://www.springframework.org/schema/beans\"\r\n\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\r\n\txmlns:aop=\"http://www.springframework.org/schema/aop\"\r\n\txmlns:tx=\"http://www.springframework.org/schema/tx\"\r\n\txsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd\t   http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd\t   http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd\"\r\n\tdefault-autowire=\"byName\" default-dependency-check=\"none\">\r\n\t\r\n\t<!-- \r\n\t<bean id=\"canalCommunicationClientDelegate\" class=\"com.alibaba.otter.shared.communication.core.impl.DefaultCommunicationClientImpl\" init-method=\"initial\" destroy-method=\"destory\">\r\n\t\t<property name=\"factory\">\r\n\t\t\t<bean class=\"com.alibaba.otter.shared.communication.core.impl.connection.CommunicationConnectionPoolFactory\" init-method=\"initial\" destroy-method=\"destory\" >\r\n\t\t\t\t<property name=\"factory\">\r\n\t\t\t\t\t<bean class=\"com.alibaba.otter.shared.communication.core.impl.rmi.RmiCommunicationConnectionFactory\" />\r\n\t\t\t\t</property>\r\n\t\t\t\t<property name=\"maxActive\" value=\"${otter.communication.pool.size}\" />\r\n\t\t\t</bean>\r\n\t\t</property>\r\n\t</bean>\r\n\t-->\r\n\t\r\n\t<bean id=\"canalCommunicationClientDelegate\" class=\"com.alibaba.otter.shared.communication.core.impl.DefaultCommunicationClientImpl\" init-method=\"initial\" destroy-method=\"destory\">\r\n\t\t<property name=\"poolSize\" value=\"${otter.communication.pool.size}\" />\r\n\t\t<property name=\"factory\">\r\n\t\t\t<bean class=\"com.alibaba.otter.shared.communication.core.impl.dubbo.DubboCommunicationConnectionFactory\">\r\n\t\t\t\t<property name=\"payload\" value=\"${otter.communication.payload:8388608}\" />\r\n\t\t\t</bean>\r\n\t\t</property>\r\n\t</bean>\r\n\r\n\t<bean id=\"canalCommmunicationClient\" class=\"com.alibaba.otter.canal.extend.communication.CanalCommmunicationClient\">\r\n\t\t<property name=\"managerAddress\" value=\"${otter.manager.address}\" />\r\n\t\t<property name=\"delegate\" ref=\"canalCommunicationClientDelegate\" />\r\n\t</bean>\r\n\t\r\n\t<bean id=\"canalConfigClient\" class=\"com.alibaba.otter.canal.extend.communication.CanalConfigClient\" >\r\n\t\t<property name=\"delegate\" ref=\"canalCommmunicationClient\" />\r\n\t</bean>\r\n</beans>"
  },
  {
    "path": "node/canal/src/main/resources/spring/tsdb/mysql-tsdb.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<beans xmlns=\"http://www.springframework.org/schema/beans\"\n\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:tx=\"http://www.springframework.org/schema/tx\"\n\txmlns:aop=\"http://www.springframework.org/schema/aop\" xmlns:lang=\"http://www.springframework.org/schema/lang\"\n\txmlns:context=\"http://www.springframework.org/schema/context\"\n\txsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd\n           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd\n           http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-2.0.xsd\n           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd\n           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd\"\n\tdefault-autowire=\"byName\">\n\t\n\t<!-- properties -->\n\t<bean class=\"com.alibaba.otter.canal.instance.spring.support.PropertyPlaceholderConfigurer\" lazy-init=\"false\">\n\t\t<property name=\"ignoreResourceNotFound\" value=\"true\" />\n\t\t<property name=\"systemPropertiesModeName\" value=\"SYSTEM_PROPERTIES_MODE_OVERRIDE\"/><!-- 允许system覆盖 -->\n\t\t<property name=\"locationNames\">\n\t\t\t<list>\n\t\t\t\t<value>classpath:canal.properties</value>\n\t\t\t\t<value>classpath:${canal.instance.destination:}/instance.properties</value>\n\t\t\t</list>\n\t\t</property>\n\t</bean>\n\t\n\t<!-- 基于db的实现 -->\n\t<bean id=\"tableMetaTSDB\" class=\"com.alibaba.otter.canal.parse.inbound.mysql.tsdb.DatabaseTableMeta\">\n\t\t<property name=\"metaHistoryDAO\" ref=\"metaHistoryDAO\"/>\n\t\t<property name=\"metaSnapshotDAO\" ref=\"metaSnapshotDAO\"/>\n\t</bean>\n\t\n    <bean id=\"dataSource\" class=\"com.alibaba.druid.pool.DruidDataSource\" destroy-method=\"close\">\n        <property name=\"driverClassName\" value=\"com.mysql.jdbc.Driver\" />\n\t\t<property name=\"url\" value=\"${canal.instance.tsdb.url:}\" />\n\t\t<property name=\"username\" value=\"${canal.instance.tsdb.dbUsername:}\" />\n\t\t<property name=\"password\" value=\"${canal.instance.tsdb.dbPassword:}\" />\n        <property name=\"maxActive\" value=\"30\" />\n        <property name=\"initialSize\" value=\"0\" />\n        <property name=\"minIdle\" value=\"1\" />\n        <property name=\"maxWait\" value=\"10000\" />\n        <property name=\"timeBetweenEvictionRunsMillis\" value=\"60000\" />\n        <property name=\"minEvictableIdleTimeMillis\" value=\"300000\" />\n        <property name=\"validationQuery\" value=\"SELECT 1\" />\n        <property name=\"exceptionSorterClassName\" value=\"com.alibaba.druid.pool.vendor.MySqlExceptionSorter\" />\n        <property name=\"validConnectionCheckerClassName\" value=\"com.alibaba.druid.pool.vendor.MySqlValidConnectionChecker\" />\n        <property name=\"testWhileIdle\" value=\"true\" />\n        <property name=\"testOnBorrow\" value=\"false\" />\n        <property name=\"testOnReturn\" value=\"false\" />\n        <property name=\"useUnfairLock\" value=\"true\" />\n\t</bean>\n\n    <bean id=\"sqlMapClient\" class=\"org.springframework.orm.ibatis.SqlMapClientFactoryBean\">\n        <property name=\"dataSource\" ref=\"dataSource\"/>\n        <property name=\"configLocation\" value=\"classpath:spring/tsdb/sql-map/sqlmap-config.xml\"/>\n    </bean>\n\n    <bean id=\"metaHistoryDAO\" class=\"com.alibaba.otter.canal.parse.inbound.mysql.tsdb.dao.MetaHistoryDAO\">\n        <property name=\"sqlMapClient\" ref=\"sqlMapClient\"/>\n    </bean>\n\n    <bean id=\"metaSnapshotDAO\" class=\"com.alibaba.otter.canal.parse.inbound.mysql.tsdb.dao.MetaSnapshotDAO\">\n        <property name=\"sqlMapClient\" ref=\"sqlMapClient\"/>\n    </bean>\n</beans>\n"
  },
  {
    "path": "node/canal/src/main/resources/spring/tsdb/sql-map/sqlmap-config.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE sqlMapConfig PUBLIC \"-//iBATIS.com//DTD SQL Map Config 2.0//EN\"\n        \"http://www.ibatis.com/dtd/sql-map-config-2.dtd\">\n<sqlMapConfig>\n    <settings useStatementNamespaces=\"true\"/>\n    <sqlMap resource=\"spring/tsdb/sql-map/sqlmap_history.xml\"/>\n    <sqlMap resource=\"spring/tsdb/sql-map/sqlmap_snapshot.xml\"/>\n</sqlMapConfig>\n"
  },
  {
    "path": "node/canal/src/main/resources/spring/tsdb/sql-map/sqlmap_history.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE sqlMap PUBLIC \"-//ibatis.apache.org//DTD SQL Map 2.0//EN\" \"http://ibatis.apache.org/dtd/sql-map-2.dtd\" >\n<sqlMap namespace=\"meta_history\">\n    <typeAlias alias=\"metaHistoryDO\" type=\"com.alibaba.otter.canal.parse.inbound.mysql.tsdb.dao.MetaHistoryDO\"/>\n    <sql id=\"allColumns\">\n        <![CDATA[\n\t\tgmt_create,gmt_modified,destination,binlog_file,binlog_offest,binlog_master_id,binlog_timestamp,use_schema,sql_schema,sql_table,sql_text,sql_type,extra\n        ]]>\n    </sql>\n    <sql id=\"allVOColumns\">\n        <![CDATA[\n\t\ta.id as id,a.gmt_create as gmtCreate,a.gmt_modified as gmtModified,\n\t\ta.destination as destination,a.binlog_file as binlogFile,a.binlog_offest as binlogOffest,a.binlog_master_id as binlogMasterId,a.binlog_timestamp as binlogTimestamp,\n\t\ta.use_schema as useSchema,a.sql_schema as sqlSchema,a.sql_table as sqlTable,a.sql_text as sqlText,a.sql_type as sqlType,a.extra as extra\n        ]]>\n    </sql>\n\n    <select id=\"findByTimestamp\" parameterClass=\"java.util.Map\" resultClass=\"metaHistoryDO\">\n        select\n        <include refid=\"allVOColumns\"/>\n        from meta_history a\n        <![CDATA[\n        where destination = #destination# and binlog_timestamp >= #snapshotTimestamp# and binlog_timestamp <= #timestamp#\n        order by binlog_timestamp asc,id asc\n        ]]>\n    </select>\n\n    <insert id=\"insert\" parameterClass=\"metaHistoryDO\">\n        insert into meta_history (<include refid=\"allColumns\"/>)\n        values(CURRENT_TIMESTAMP,CURRENT_TIMESTAMP,#destination#,#binlogFile#,#binlogOffest#,#binlogMasterId#,#binlogTimestamp#,#useSchema#,#sqlSchema#,#sqlTable#,#sqlText#,#sqlType#,#extra#)\n    </insert>\n    \n    <delete id=\"deleteByName\" parameterClass=\"java.util.Map\">\n        delete from meta_history \n        where destination=#destination#\n    </delete>\n\n\n    <delete id=\"deleteByTimestamp\" parameterClass=\"java.util.Map\">\n        <![CDATA[\n\t\tdelete from meta_history\n\t\twhere destination=#destination# and binlog_timestamp < #timestamp#\n        ]]>\n    </delete>\n</sqlMap>"
  },
  {
    "path": "node/canal/src/main/resources/spring/tsdb/sql-map/sqlmap_snapshot.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE sqlMap PUBLIC \"-//ibatis.apache.org//DTD SQL Map 2.0//EN\" \"http://ibatis.apache.org/dtd/sql-map-2.dtd\" >\n<sqlMap namespace=\"meta_snapshot\">\n    <typeAlias alias=\"metaSnapshotDO\" type=\"com.alibaba.otter.canal.parse.inbound.mysql.tsdb.dao.MetaSnapshotDO\"/>\n    <typeAlias alias=\"tableMetaSnapshotDO\"\n               type=\"com.alibaba.middleware.jingwei.biz.dataobject.CanalTableMetaSnapshotDO\"/>\n    <sql id=\"allColumns\">\n        <![CDATA[\n\t\tgmt_create,gmt_modified,destination,binlog_file,binlog_offest,binlog_master_id,binlog_timestamp,data,extra\n        ]]>\n    </sql>\n    <sql id=\"allVOColumns\">\n        <![CDATA[\n\t\ta.id as id,a.gmt_create as gmtCreate,a.gmt_modified as gmtModified,\n\t\ta.destination as destination,a.binlog_file as binlogFile,a.binlog_offest as binlogOffest,a.binlog_master_id as binlogMasterId,a.binlog_timestamp as binlogTimestamp,a.data as data,a.extra as extra\n        ]]>\n    </sql>\n\n    <select id=\"findByTimestamp\" parameterClass=\"java.util.Map\" resultClass=\"metaSnapshotDO\">\n    \tselect <include refid=\"allVOColumns\"/>\n    \t<![CDATA[\n        from meta_snapshot a\n        where destination = #destination# and binlog_timestamp < #timestamp#\n        order by binlog_timestamp desc,id desc\n        limit 1\n        ]]>\n    </select>\n    \n    <insert id=\"insert\" parameterClass=\"metaSnapshotDO\">\n        insert into meta_snapshot (<include refid=\"allColumns\"/>)\n        values(CURRENT_TIMESTAMP,CURRENT_TIMESTAMP,#destination#,#binlogFile#,#binlogOffest#,#binlogMasterId#,#binlogTimestamp#,#data#,#extra#)\n    </insert>\n\n    <update id=\"update\" parameterClass=\"metaSnapshotDO\">\n        update meta_snapshot set gmt_modified=now(),\n        binlog_file=#binlogFile#,binlog_offest=#binlogOffest#,binlog_master_id=#binlogMasterId#,binlog_timestamp=#binlogTimestamp#,data=#data#,extra=#extra#\n        where destination=#destination# and binlog_timestamp=0\n    </update>\n\n \t<delete id=\"deleteByName\" parameterClass=\"java.util.Map\">\n        delete from meta_snapshot \n        where destination=#destination#\n    </delete>\n\n    <delete id=\"deleteByTimestamp\" parameterClass=\"java.util.Map\">\n        <![CDATA[\n\t\tdelete from meta_snapshot\n\t\twhere destination=#destination# and binlog_timestamp < #timestamp# and binlog_timestamp > 0\n        ]]>\n    </delete>\n</sqlMap>"
  },
  {
    "path": "node/common/pom.xml",
    "content": "<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\">\n\t<modelVersion>4.0.0</modelVersion>\n\t<parent>\n\t\t<groupId>com.alibaba.otter</groupId>\n\t\t<artifactId>node</artifactId>\n\t\t<version>4.2.19-SNAPSHOT</version>\n\t\t<relativePath>../pom.xml</relativePath>\n\t</parent>\n\t<groupId>com.alibaba.otter</groupId>\n\t<artifactId>node.common</artifactId>\n\t<packaging>jar</packaging>\n\t<name>node common module for otter</name>\n\t<url>http://github.com/alibaba/otter</url>\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>com.alibaba.otter</groupId>\n\t\t\t<artifactId>shared.common</artifactId>\n\t\t\t<version>${project.version}</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>com.alibaba.otter</groupId>\n\t\t\t<artifactId>shared.arbitrate</artifactId>\n\t\t\t<version>${project.version}</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>ch.qos.logback</groupId>\n\t\t\t<artifactId>logback-core</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>ch.qos.logback</groupId>\n\t\t\t<artifactId>logback-classic</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.slf4j</groupId>\n\t\t\t<artifactId>jcl-over-slf4j</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.slf4j</groupId>\n\t\t\t<artifactId>slf4j-api</artifactId>\n\t\t</dependency>\n\t\t<!-- spring -->\n\t\t<dependency>\n\t\t\t<groupId>cglib</groupId>\n\t\t\t<artifactId>cglib-nodep</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework</groupId>\n\t\t\t<artifactId>spring-core</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework</groupId>\n\t\t\t<artifactId>spring-beans</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework</groupId>\n\t\t\t<artifactId>spring-aop</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework</groupId>\n\t\t\t<artifactId>spring-context</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework</groupId>\n\t\t\t<artifactId>spring-context-support</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework</groupId>\n\t\t\t<artifactId>spring-tx</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework</groupId>\n\t\t\t<artifactId>spring-jdbc</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework</groupId>\n\t\t\t<artifactId>spring-test</artifactId>\n\t\t</dependency>\n\t\t<!-- commons -->\n\t\t<dependency>\n\t\t\t<groupId>commons-dbcp</groupId>\n\t\t\t<artifactId>commons-dbcp</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>commons-lang</groupId>\n\t\t\t<artifactId>commons-lang</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>commons-pool</groupId>\n\t\t\t<artifactId>commons-pool</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>commons-codec</groupId>\n\t\t\t<artifactId>commons-codec</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>commons-io</groupId>\n\t\t\t<artifactId>commons-io</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>commons-beanutils</groupId>\n\t\t\t<artifactId>commons-beanutils</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.apache.zookeeper</groupId>\n\t\t\t<artifactId>zookeeper</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>com.alibaba</groupId>\n\t\t\t<artifactId>fastjson</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>com.google.guava</groupId>\n\t\t\t<artifactId>guava</artifactId>\n\t\t</dependency>\n\n\t\t<!-- test dependency -->\n\t\t<dependency>\n\t\t\t<groupId>org.jtester</groupId>\n\t\t\t<artifactId>jtester</artifactId>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>junit</groupId>\n\t\t\t<artifactId>junit</artifactId>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\t</dependencies>\n</project>\n"
  },
  {
    "path": "node/common/src/main/java/com/alibaba/otter/node/common/communication/NodeCommmunicationClient.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.common.communication;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.apache.commons.lang.StringUtils;\nimport org.apache.commons.lang.math.RandomUtils;\nimport org.springframework.beans.factory.DisposableBean;\n\nimport com.alibaba.otter.node.common.config.ConfigClientService;\nimport com.alibaba.otter.shared.common.model.config.node.Node;\nimport com.alibaba.otter.shared.communication.core.CommunicationClient;\nimport com.alibaba.otter.shared.communication.core.exception.CommunicationException;\nimport com.alibaba.otter.shared.communication.core.impl.DefaultCommunicationClientImpl;\nimport com.alibaba.otter.shared.communication.core.model.Callback;\nimport com.alibaba.otter.shared.communication.core.model.Event;\n\n/**\n * 封装了基于communication通讯的工具\n * \n * @author jianghang 2011-10-18 下午02:18:04\n * @version 4.0.0\n */\npublic class NodeCommmunicationClient implements DisposableBean {\n\n    private CommunicationClient delegate;\n    private ConfigClientService configClientService;\n    private List<String>        managerAddress;\n    private volatile int        index = 0;\n\n    /**\n     * 指定对应的Node节点，进行event调用\n     */\n    public Object call(Long nid, final Event event) {\n        return delegate.call(convertToAddress(nid), event);\n    }\n\n    /**\n     * 指定对应的Node节点，进行event调用\n     * \n     * <pre>\n     * 注意：该方法为异步调用\n     * </pre>\n     */\n    public void call(Long nid, Event event, final Callback callback) {\n        delegate.call(convertToAddress(nid), event, callback);\n    }\n\n    /**\n     * 指定manager，进行event调用\n     */\n    public Object callManager(final Event event) {\n        CommunicationException ex = null;\n        Object object = null;\n        for (int i = index; i < index + managerAddress.size(); i++) { // 循环一次manager的所有地址\n            String address = managerAddress.get(i % managerAddress.size());\n            try {\n                object = delegate.call(address, event);\n                index = i; // 更新一下上一次成功的地址\n                return object;\n            } catch (CommunicationException e) {\n                // retry next address;\n                ex = e;\n            }\n        }\n\n        throw ex; // 走到这一步，说明肯定有出错了\n    }\n\n    /**\n     * 指定manager，进行event调用\n     * \n     * <pre>\n     * 注意：该方法为异步调用\n     * </pre>\n     */\n    public void callManager(final Event event, final Callback callback) {\n        if (delegate instanceof DefaultCommunicationClientImpl) {\n            ((DefaultCommunicationClientImpl) delegate).submit(new Runnable() {\n\n                public void run() {\n                    Object obj = callManager(event);\n                    callback.call(obj);\n                }\n            });\n        }\n    }\n\n    private String convertToAddress(Long nid) {\n        Node node = configClientService.findNode(nid);\n        if (node.getParameters().getUseExternalIp()) {\n            return node.getParameters().getExternalIp() + \":\" + node.getPort();\n        } else {\n            return node.getIp() + \":\" + node.getPort();\n        }\n    }\n\n    public void destroy() throws Exception {\n    }\n\n    // ================== setter / getter =====================\n\n    public void setDelegate(CommunicationClient delegate) {\n        this.delegate = delegate;\n    }\n\n    public void setManagerAddress(String managerAddress) {\n        String server = StringUtils.replace(managerAddress, \";\", \",\");\n        String[] servers = StringUtils.split(server, ',');\n        this.managerAddress = Arrays.asList(servers);\n        this.index = RandomUtils.nextInt(this.managerAddress.size()); // 随机选择一台机器\n    }\n\n    public void setConfigClientService(ConfigClientService configClientService) {\n        this.configClientService = configClientService;\n    }\n\n    public String getManagerAddress() {\n        return StringUtils.join(managerAddress, \",\");\n    }\n\n}\n"
  },
  {
    "path": "node/common/src/main/java/com/alibaba/otter/node/common/communication/NodeCommunicationEndpoint.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.common.communication;\n\nimport org.springframework.beans.factory.DisposableBean;\nimport org.springframework.beans.factory.InitializingBean;\n\nimport com.alibaba.otter.node.common.config.ConfigClientService;\nimport com.alibaba.otter.shared.communication.core.CommunicationEndpoint;\nimport com.alibaba.otter.shared.communication.core.impl.dubbo.DubboCommunicationEndpoint;\nimport com.alibaba.otter.shared.communication.core.impl.rmi.RmiCommunicationEndpoint;\n\n/**\n * 基于Node节点的endpoint包装\n * \n * @author jianghang 2011-10-18 下午02:28:25\n * @version 4.0.0\n */\npublic class NodeCommunicationEndpoint implements InitializingBean, DisposableBean {\n\n    private CommunicationEndpoint endpoint;\n    private ConfigClientService   configClientService;\n\n    public void afterPropertiesSet() throws Exception {\n        // String ip = config.currentNode().getIp();\n        Long port = configClientService.currentNode().getPort();\n        if (endpoint instanceof RmiCommunicationEndpoint) {\n            RmiCommunicationEndpoint rmiEndpoint = (RmiCommunicationEndpoint) endpoint;\n            // rmiEndpoint.setHost(ip);\n            rmiEndpoint.setPort(port.intValue());\n        }\n\n        if (endpoint instanceof DubboCommunicationEndpoint) {\n            DubboCommunicationEndpoint dubboEndpoint = (DubboCommunicationEndpoint) endpoint;\n            dubboEndpoint.setPort(port.intValue());\n        }\n        endpoint.initial();\n    }\n\n    public void destroy() throws Exception {\n        endpoint.destory();\n    }\n\n    // ================= setter / getter ==============\n\n    public void setEndpoint(CommunicationEndpoint endpoint) {\n        this.endpoint = endpoint;\n    }\n\n    public void setConfigClientService(ConfigClientService configClientService) {\n        this.configClientService = configClientService;\n    }\n\n}\n"
  },
  {
    "path": "node/common/src/main/java/com/alibaba/otter/node/common/config/ConfigClientService.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.common.config;\n\nimport com.alibaba.otter.shared.arbitrate.impl.config.ArbitrateConfig;\nimport com.alibaba.otter.shared.common.model.config.channel.Channel;\nimport com.alibaba.otter.shared.common.model.config.node.Node;\nimport com.alibaba.otter.shared.common.model.config.pipeline.Pipeline;\n\n/**\n * 基于本地内存的config cache实现\n * \n * @author jianghang\n */\npublic interface ConfigClientService extends ArbitrateConfig {\n\n    /**\n     * 查询当前节点的Node信息\n     */\n    public Node currentNode();\n\n    /**\n     * 根据对应的nid查询Node信息\n     */\n    public Node findNode(Long nid);\n\n    /**\n     * 根据channelId获取channel\n     * \n     * @param channelId\n     * @return\n     */\n    public Channel findChannel(Long channelId);\n\n    /**\n     * 根据pipelineId获取pipeline\n     * \n     * @param pipelineId\n     * @return\n     */\n    public Pipeline findPipeline(Long pipelineId);\n\n    /**\n     * 根据pipelineId查询对应的Channel对象\n     * \n     * @param pipelineId\n     * @return\n     */\n    public Channel findChannelByPipelineId(Long pipelineId);\n\n    /**\n     * 根据pipelineId查询相对的Pipeline对象\n     * \n     * @param pipelineId\n     * @return\n     */\n    public Pipeline findOppositePipeline(Long pipelineId);\n\n}\n"
  },
  {
    "path": "node/common/src/main/java/com/alibaba/otter/node/common/config/NodeTaskListener.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.common.config;\n\nimport java.util.List;\n\nimport com.alibaba.otter.node.common.config.model.NodeTask;\n\n/**\n * 在nodeTask发生变化时，广播通知下\n * \n * @author jianghang 2012-4-20 下午10:45:17\n * @version 4.0.2\n */\npublic interface NodeTaskListener {\n\n    boolean process(List<NodeTask> nodeTasks);\n}\n"
  },
  {
    "path": "node/common/src/main/java/com/alibaba/otter/node/common/config/NodeTaskService.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.common.config;\n\nimport java.util.List;\n\nimport com.alibaba.otter.node.common.config.model.NodeTask;\n\n/**\n * Node节点任务分发的服务类\n * \n * @author jianghang\n */\npublic interface NodeTaskService {\n\n    /**\n     * 根据对应的获取任务列表，<strong>注意是所有的任务</strong>\n     */\n    public List<NodeTask> listAllNodeTasks();\n\n    /**\n     * 注册监听器\n     */\n    public void addListener(NodeTaskListener listener);\n\n    /**\n     * 关闭node\n     */\n    public void stopNode();\n\n}\n"
  },
  {
    "path": "node/common/src/main/java/com/alibaba/otter/node/common/config/impl/ConfigClientServiceImpl.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.common.config.impl;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.apache.commons.lang.StringUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.InitializingBean;\n\nimport com.alibaba.otter.node.common.communication.NodeCommmunicationClient;\nimport com.alibaba.otter.node.common.config.ConfigClientService;\nimport com.alibaba.otter.shared.arbitrate.impl.config.ArbitrateConfig;\nimport com.alibaba.otter.shared.arbitrate.impl.config.ArbitrateConfigRegistry;\nimport com.alibaba.otter.shared.common.model.config.ConfigException;\nimport com.alibaba.otter.shared.common.model.config.channel.Channel;\nimport com.alibaba.otter.shared.common.model.config.node.Node;\nimport com.alibaba.otter.shared.common.model.config.pipeline.Pipeline;\nimport com.alibaba.otter.shared.common.utils.cache.RefreshMemoryMirror;\nimport com.alibaba.otter.shared.common.utils.cache.RefreshMemoryMirror.ComputeFunction;\nimport com.alibaba.otter.shared.communication.model.config.FindChannelEvent;\nimport com.alibaba.otter.shared.communication.model.config.FindNodeEvent;\nimport com.google.common.base.Function;\nimport com.google.common.collect.OtterMigrateMap;\n\n/**\n * task节点对应的config对象管理服务\n * \n * @author jianghang\n */\npublic class ConfigClientServiceImpl implements InternalConfigClientService, ArbitrateConfig, InitializingBean {\n\n    private static final String                NID_NAME       = \"nid\";\n    private static final Long                  DEFAULT_PERIOD = 60 * 1000L;\n    private static final Logger                logger         = LoggerFactory.getLogger(ConfigClientService.class);\n\n    private Long                               timeout        = DEFAULT_PERIOD;\n    private Long                               nid;\n    private NodeCommmunicationClient           nodeCommmunicationClient;\n    private RefreshMemoryMirror<Long, Channel> channelCache;\n    private Map<Long, Long>                    channelMapping;                                                     // 将pipelineId映射为channelId\n    private RefreshMemoryMirror<Long, Node>    nodeCache;\n\n    public ConfigClientServiceImpl(){\n        // 注册一下事件处理\n        ArbitrateConfigRegistry.regist(this);\n    }\n\n    public Node currentNode() {\n        Node node = nodeCache.get(nid);\n        if (node == null) {\n            throw new ConfigException(\"nid:\" + nid + \" in manager[\" + nodeCommmunicationClient.getManagerAddress()\n                                      + \"]is not found!\");\n        }\n\n        return node;\n    }\n\n    public Channel findChannel(Long channelId) {\n        return channelCache.get(channelId);\n    }\n\n    public Channel findChannelByPipelineId(Long pipelineId) {\n        Long channelId = channelMapping.get(pipelineId);\n        return channelCache.get(channelId);\n    }\n\n    public Pipeline findOppositePipeline(Long pipelineId) {\n        Long channelId = channelMapping.get(pipelineId);\n        Channel channel = channelCache.get(channelId);\n        List<Pipeline> pipelines = channel.getPipelines();\n        for (Pipeline pipeline : pipelines) {\n            if (pipeline.getId().equals(pipelineId) == false) {// 这里假定pipeline只有两个\n                return pipeline;\n            }\n        }\n\n        return null;\n    }\n\n    public Pipeline findPipeline(Long pipelineId) {\n        Long channelId = channelMapping.get(pipelineId);\n        Channel channel = channelCache.get(channelId);\n        List<Pipeline> pipelines = channel.getPipelines();\n        for (Pipeline pipeline : pipelines) {\n            if (pipeline.getId().equals(pipelineId)) {\n                return pipeline;\n            }\n        }\n\n        throw new ConfigException(\"no pipeline for pipelineId[\" + pipelineId + \"]\");\n    }\n\n    public Node findNode(Long nid) {\n        return nodeCache.get(nid);\n    }\n\n    public void afterPropertiesSet() throws Exception {\n        // 获取一下nid变量\n        String nid = System.getProperty(NID_NAME);\n        if (StringUtils.isEmpty(nid)) {\n            throw new ConfigException(\"nid is not set!\");\n        }\n\n        this.nid = Long.valueOf(nid);\n\n        channelMapping = OtterMigrateMap.makeComputingMap(new Function<Long, Long>() {\n\n            public Long apply(Long pipelineId) {\n                // 处理下pipline -> channel映射关系不存在的情况\n                FindChannelEvent event = new FindChannelEvent();\n                event.setPipelineId(pipelineId);\n                try {\n                    Object obj = nodeCommmunicationClient.callManager(event);\n                    if (obj != null && obj instanceof Channel) {\n                        Channel channel = (Channel) obj;\n                        updateMapping(channel, pipelineId);// 排除下自己\n                        channelCache.put(channel.getId(), channel);// 更新下channelCache\n                        return channel.getId();\n                    }\n                } catch (Exception e) {\n                    logger.error(\"call_manager_error\", event.toString(), e);\n                }\n\n                throw new ConfigException(\"No Such Channel by pipelineId[\" + pipelineId + \"]\");\n            }\n        });\n\n        nodeCache = new RefreshMemoryMirror<Long, Node>(timeout, new ComputeFunction<Long, Node>() {\n\n            public Node apply(Long key, Node oldValue) {\n                FindNodeEvent event = new FindNodeEvent();\n                event.setNid(key);\n                try {\n                    Object obj = nodeCommmunicationClient.callManager(event);\n                    if (obj != null && obj instanceof Node) {\n                        return (Node) obj;\n                    } else {\n                        throw new ConfigException(\"No Such Node by id[\" + key + \"]\");\n                    }\n                } catch (Exception e) {\n                    logger.error(\"call_manager_error\", event.toString(), e);\n                }\n                // 其他情况直接返回内存中的旧值\n                return oldValue;\n            }\n        });\n\n        channelCache = new RefreshMemoryMirror<Long, Channel>(timeout, new ComputeFunction<Long, Channel>() {\n\n            public Channel apply(Long key, Channel oldValue) {\n                FindChannelEvent event = new FindChannelEvent();\n                event.setChannelId(key);\n                try {\n                    Object obj = nodeCommmunicationClient.callManager(event);\n                    if (obj != null && obj instanceof Channel) {\n                        updateMapping((Channel) obj, null);// 排除下自己\n                        return (Channel) obj;\n                    } else {\n                        throw new ConfigException(\"No Such Channel by pipelineId[\" + key + \"]\");\n                    }\n                } catch (Exception e) {\n                    logger.error(\"call_manager_error\", event.toString(), e);\n                }\n                // 其他情况直接返回内存中的旧值\n                return oldValue;\n            }\n        });\n    }\n\n    public void createOrUpdateChannel(Channel channel) {\n        channelCache.put(channel.getId(), channel);\n        updateMapping(channel, null);\n    }\n\n    private void updateMapping(Channel channel, Long excludeId) {\n        Long channelId = channel.getId();\n        List<Pipeline> pipelines = channel.getPipelines();\n        for (Pipeline pipeline : pipelines) {\n            if (excludeId == null || !pipeline.getId().equals(excludeId)) {\n                channelMapping.put(pipeline.getId(), channelId);\n            }\n        }\n    }\n\n    // =================== setter / getter ======================\n\n    public void setNodeCommmunicationClient(NodeCommmunicationClient nodeCommmunicationClient) {\n        this.nodeCommmunicationClient = nodeCommmunicationClient;\n    }\n\n    public void setTimeout(Long timeout) {\n        this.timeout = timeout;\n    }\n\n}\n"
  },
  {
    "path": "node/common/src/main/java/com/alibaba/otter/node/common/config/impl/InternalConfigClientService.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.common.config.impl;\n\nimport com.alibaba.otter.node.common.config.ConfigClientService;\nimport com.alibaba.otter.shared.common.model.config.channel.Channel;\n\n/**\n * 内部config service\n * \n * @author jianghang\n */\npublic interface InternalConfigClientService extends ConfigClientService {\n\n    /**\n     * 创建或者更新本地service的数据\n     */\n    public void createOrUpdateChannel(Channel channel);\n}\n"
  },
  {
    "path": "node/common/src/main/java/com/alibaba/otter/node/common/config/impl/NodeTaskServiceImpl.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.common.config.impl;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.InitializingBean;\nimport org.springframework.util.Assert;\nimport org.springframework.util.CollectionUtils;\n\nimport com.alibaba.otter.node.common.communication.NodeCommmunicationClient;\nimport com.alibaba.otter.node.common.config.NodeTaskListener;\nimport com.alibaba.otter.node.common.config.NodeTaskService;\nimport com.alibaba.otter.node.common.config.model.NodeTask;\nimport com.alibaba.otter.node.common.config.model.NodeTask.TaskEvent;\nimport com.alibaba.otter.shared.common.model.config.channel.Channel;\nimport com.alibaba.otter.shared.common.model.config.enums.StageType;\nimport com.alibaba.otter.shared.common.model.config.node.Node;\nimport com.alibaba.otter.shared.common.model.config.pipeline.Pipeline;\nimport com.alibaba.otter.shared.communication.core.CommunicationRegistry;\nimport com.alibaba.otter.shared.communication.model.arbitrate.StopNodeEvent;\nimport com.alibaba.otter.shared.communication.model.config.ConfigEventType;\nimport com.alibaba.otter.shared.communication.model.config.FindTaskEvent;\nimport com.alibaba.otter.shared.communication.model.config.NotifyChannelEvent;\nimport com.google.common.base.Function;\nimport com.google.common.collect.Lists;\n\n/**\n * task节点对应的任务列表管理器\n * \n * @author jianghang\n */\npublic class NodeTaskServiceImpl implements NodeTaskService, InitializingBean {\n\n    private static final Logger         logger    = LoggerFactory.getLogger(NodeTaskService.class);\n\n    private NodeCommmunicationClient    nodeCommmunicationClient;\n    private InternalConfigClientService configClientService;\n    private List<NodeTask>              allTasks  = Collections.synchronizedList(new ArrayList<NodeTask>());\n    private List<NodeTask>              incTasks  = Collections.synchronizedList(new ArrayList<NodeTask>());\n    private List<NodeTaskListener>      listeners = Collections.synchronizedList(new ArrayList<NodeTaskListener>());\n\n    public NodeTaskServiceImpl(){\n        CommunicationRegistry.regist(ConfigEventType.notifyChannel, this);\n    }\n\n    public synchronized List<NodeTask> listAllNodeTasks() {\n        return allTasks;\n    }\n\n    public void afterPropertiesSet() throws Exception {\n        // 初始化时调用manager获取channel任务\n        initNodeTask();\n        if (notifyListener() == false) {\n            throw new RuntimeException(\"init node task failed.\");\n        }\n    }\n\n    private synchronized List<NodeTask> mergeIncNodeTasks() {\n        List<NodeTask> tasks = new ArrayList<NodeTask>(incTasks);\n        incTasks.clear(); // 清除inc\n        if (logger.isInfoEnabled()) {\n            logger.info(\"##merge all NodeTask {}\", printNodeTasks(tasks));\n        }\n        merge(allTasks, tasks);// inc获取后直接丢到all的队列里\n        if (logger.isInfoEnabled()) {\n            logger.info(\"##now all NodeTask {}\", printNodeTasks(allTasks));\n        }\n\n        return tasks;\n    }\n\n    private void initNodeTask() {\n        // 从manager下获取一下对应的任务列表\n        Node node = configClientService.currentNode();\n        FindTaskEvent event = new FindTaskEvent();\n        event.setNid(node.getId());\n        Object obj = nodeCommmunicationClient.callManager(event);\n        if (obj != null) {\n            List<Channel> channels = (List<Channel>) obj;\n            for (Channel channel : channels) {\n                // 排除已经分配过的task\n                processNodeTask(channel);\n            }\n        }\n    }\n\n    private void processNodeTask(Channel channel) {\n        List<NodeTask> addTasks = parseNodeTask(channel);\n        if (logger.isInfoEnabled()) {\n            logger.info(\"##merge channel[{}] inc NodeTask {}\", channel.getId(), printNodeTasks(addTasks));\n        }\n\n        List<NodeTask> tasks = new ArrayList<NodeTask>(incTasks);\n        merge(tasks, addTasks);// 合并数据到incTasks中\n        merge(incTasks, retain(tasks, allTasks));// 过滤掉allTasks中已有的相同的stage/event类型，比如已经有CREATE动作，不需要重复给出\n        if (logger.isInfoEnabled()) {\n            logger.info(\"##now inc NodeTask {}\", printNodeTasks(incTasks));\n        }\n    }\n\n    private String printNodeTasks(List<NodeTask> tasks) {\n        StringBuilder builder = new StringBuilder();\n        for (NodeTask task : tasks) {\n            builder.append(\"\\n=========================\");\n            builder.append(\"pipeline:\" + task.getPipeline().getId()).append(\"\\n\");\n            builder.append(\"\\t\").append(task.getStage()).append(\"\\n\");\n            builder.append(\"\\t\").append(task.getEvent()).append(\"\\n\");\n            builder.append(\"\\t\").append(\"shutdown:\").append(task.isShutdown()).append(\"\\n\");\n        }\n\n        return builder.toString();\n    }\n\n    // 解析一下tasks为NodeTask对象\n    private List<NodeTask> parseNodeTask(Channel channel) {\n        List<NodeTask> tasks = new ArrayList<NodeTask>();\n        List<Pipeline> pipelines = channel.getPipelines();\n        Long nid = configClientService.currentNode().getId();\n        TaskEvent taksEvent = null;\n        if (channel.getStatus().isStart()) {\n            taksEvent = TaskEvent.CREATE;\n        } else if (channel.getStatus().isStop()) {\n            taksEvent = TaskEvent.DELETE;\n        } else if (channel.getStatus().isPause()) {\n            // modify by ljh at 2013-01-31 , pause状态也需要启动setl线程\n            // 因为在发布的时候，restart指令不会推送指令，导致setl线程没有启动\n            // return tasks;\n            taksEvent = TaskEvent.CREATE;\n        }\n        // 处理当前最新的状态\n        for (Pipeline pipeline : pipelines) {\n            List<Node> sNodes = pipeline.getSelectNodes();\n            for (Node node : sNodes) {\n                if (nid.equals(node.getId())) {// 判断是否为当前的nid\n                    NodeTask task = new NodeTask();\n                    task.setPipeline(pipeline);\n                    NodeTask matchTask = getMatchTask(tasks, task);\n                    if (matchTask == null) {\n                        matchTask = task;\n                        tasks.add(task);\n                    }\n\n                    matchTask.setPipeline(pipeline);\n                    matchTask.getStage().add(StageType.SELECT);\n                    matchTask.getEvent().add(taksEvent);\n                }\n            }\n\n            List<Node> eNodes = pipeline.getExtractNodes();\n            for (Node node : eNodes) {\n                if (nid.equals(node.getId())) {// 判断是否为当前的nid\n                    NodeTask task = new NodeTask();\n                    task.setPipeline(pipeline);\n                    NodeTask matchTask = getMatchTask(tasks, task);\n                    if (matchTask == null) {\n                        matchTask = task;\n                        tasks.add(task);\n                    }\n\n                    matchTask.getStage().add(StageType.EXTRACT);\n                    matchTask.getEvent().add(taksEvent);\n                }\n            }\n\n            List<Node> tlNodes = pipeline.getLoadNodes();\n            for (Node node : tlNodes) {\n                if (nid.equals(node.getId())) {// 判断是否为当前的nid\n                    NodeTask task = new NodeTask();\n                    task.setPipeline(pipeline);\n                    NodeTask matchTask = getMatchTask(tasks, task);\n                    if (matchTask == null) {\n                        matchTask = task;\n                        tasks.add(task);\n                    }\n                    matchTask.getStage().add(StageType.TRANSFORM);\n                    matchTask.getEvent().add(taksEvent);\n                    matchTask.getStage().add(StageType.LOAD);\n                    matchTask.getEvent().add(taksEvent);\n                }\n            }\n        }\n\n        List<Long> pipelineIds = Lists.transform(channel.getPipelines(), new Function<Pipeline, Long>() {\n\n            public Long apply(Pipeline input) {\n                return input.getId();\n            }\n        });\n        // 合并一下target中特有的记录，取一下反操作，表示要关闭\n        for (NodeTask task : allTasks) {\n            Pipeline pipeline = task.getPipeline();\n            if (pipeline.getChannelId().equals(channel.getId()) && !pipelineIds.contains(pipeline.getId())) {\n                // /是同一个channel，但对应的pipeline不在列表里\n                // 处理pipeline删除\n                NodeTask deletePipelineTask = new NodeTask();\n                deletePipelineTask.setPipeline(pipeline);\n\n                List<StageType> stages = task.getStage();\n                List<TaskEvent> events = task.getEvent();\n                for (int i = 0; i < stages.size(); i++) {\n                    StageType stage = stages.get(i);\n                    TaskEvent event = events.get(i);\n                    if (event.isCreate()) {\n                        deletePipelineTask.getStage().add(stage);\n                        deletePipelineTask.getEvent().add(TaskEvent.DELETE);// 添加为关闭\n                    }\n                }\n\n                tasks.add(deletePipelineTask);\n            }\n\n            if (pipelineIds.contains(pipeline.getId())) {// 在当前的channel列表中\n                boolean needAdd = false;\n                NodeTask matchTask = getMatchTask(tasks, task);// 找到对应的匹配\n                if (matchTask == null) {\n                    matchTask = new NodeTask();\n                    matchTask.setPipeline(pipeline);\n                    needAdd = true;\n                }\n                List<StageType> stages = task.getStage();\n                List<TaskEvent> events = task.getEvent();\n                for (int i = 0; i < stages.size(); i++) {\n                    StageType stage = stages.get(i);\n                    TaskEvent event = events.get(i);\n                    TaskEvent matchEvent = getMatchStage(matchTask, stage);\n                    if (matchEvent == null && event.isCreate()) {// 对应的stage已经被移除，触发一个DELETE操作\n                        matchTask.getStage().add(stage);\n                        matchTask.getEvent().add(TaskEvent.DELETE);\n                    }\n                }\n\n                if (needAdd && matchTask.getStage().size() > 0) {\n                    tasks.add(matchTask);\n                }\n\n            }\n        }\n\n        // 判断当前的task是否需要全部关闭\n        for (NodeTask task : tasks) {\n            boolean shutdown = true;\n            for (TaskEvent event : task.getEvent()) {// task已为当前最新节点信息\n                shutdown &= event.isDelete();\n            }\n            task.setShutdown(shutdown);\n        }\n        return tasks;\n    }\n\n    private List<NodeTask> retain(List<NodeTask> targetTasks, List<NodeTask> sourceTasks) {\n        List<NodeTask> result = new ArrayList<NodeTask>();\n        for (NodeTask task : targetTasks) {\n            // 找到有对应的交集task\n            NodeTask sourceTask = getMatchTask(sourceTasks, task);\n            if (sourceTask != null) {\n                // 做一下交集排它处理\n                NodeTask resultTask = retain(task, sourceTask);\n                if (resultTask != null) {\n                    result.add(resultTask);\n                }\n            } else {\n                result.add(task);\n            }\n\n        }\n        return result;\n    }\n\n    // 将target的目标除去source中的信息\n    private NodeTask retain(NodeTask targetTask, NodeTask sourceTask) {\n        List<StageType> stages = targetTask.getStage();\n        List<TaskEvent> events = targetTask.getEvent();\n\n        List<StageType> mergeStates = new ArrayList<StageType>();\n        List<TaskEvent> mergeEvents = new ArrayList<TaskEvent>();\n        // 合并两者的交集的数据\n        for (int i = 0; i < stages.size(); i++) {\n            StageType stage = stages.get(i);\n            TaskEvent event = events.get(i);\n\n            // 找到source节点对应的TaskEvent\n            TaskEvent sourceEvent = getMatchStage(sourceTask, stage);\n            if (sourceEvent != null && sourceEvent != event) {// 存在相同的stage节点，判断event是否相同，不同则则添加\n                mergeStates.add(stage);\n                mergeEvents.add(event);\n            }\n        }\n\n        // 添加targtTask中特有的stage/event\n        for (int i = 0; i < stages.size(); i++) {\n            StageType stage = stages.get(i);\n            TaskEvent event = events.get(i);\n            if (getMatchStage(sourceTask, stage) == null) {\n                mergeStates.add(stage);\n                mergeEvents.add(event);\n            }\n        }\n\n        if (mergeStates.size() > 0) {\n            NodeTask result = new NodeTask();\n            result.setPipeline(targetTask.getPipeline());\n            result.setEvent(mergeEvents);\n            result.setStage(mergeStates);\n            result.setShutdown(targetTask.isShutdown());\n            return result;\n        } else {\n            return null;\n        }\n\n    }\n\n    // 合并两个task列表\n    private void merge(List<NodeTask> targetTasks, List<NodeTask> sourceTasks) {\n        for (NodeTask task : sourceTasks) {\n            // 找到对应的\n            NodeTask targetTask = getMatchTask(targetTasks, task);\n            if (targetTask != null) {\n                // 针对已存在的进行合并\n                merge(targetTask, task);\n            } else {\n                // 添加新的节点\n                targetTasks.add(task);\n            }\n\n        }\n    }\n\n    // 获取pipelineId匹配的的NodeTask\n    private NodeTask getMatchTask(List<NodeTask> tasks, NodeTask match) {\n        for (NodeTask task : tasks) {\n            if (match.getPipeline().getId().equals(task.getPipeline().getId())) {\n                return task;\n            }\n        }\n\n        return null;\n    }\n\n    // 合并两个NodeTask对象\n    private void merge(NodeTask target, NodeTask source) {\n        List<StageType> stages = target.getStage();\n        List<TaskEvent> events = target.getEvent();\n\n        List<StageType> mergeStates = new ArrayList<StageType>();\n        List<TaskEvent> mergeEvents = new ArrayList<TaskEvent>();\n        // 合并两者的交集的数据\n        for (int i = 0; i < stages.size(); i++) {\n            StageType stage = stages.get(i);\n            TaskEvent event = events.get(i);\n            mergeStates.add(stage);\n            // 找到source节点对应的TaskEvent，使用最新值\n            TaskEvent sourceEvent = getMatchStage(source, stage);\n            if (sourceEvent == null) {\n                mergeEvents.add(event);\n            } else {\n                mergeEvents.add(sourceEvent);\n            }\n        }\n        // 添加两者的差集，添加source中特有的节点\n        List<StageType> sourceStages = source.getStage();\n        List<TaskEvent> sourceEvents = source.getEvent();\n        for (int i = 0; i < sourceStages.size(); i++) {\n            StageType stage = sourceStages.get(i);\n            TaskEvent event = sourceEvents.get(i);\n            if (mergeStates.contains(stage)) {\n                continue;\n            }\n\n            mergeStates.add(stage);\n            mergeEvents.add(event);\n        }\n        // 更新一下数据\n        target.setEvent(mergeEvents);\n        target.setStage(mergeStates);\n\n        target.setShutdown(source.isShutdown());// 更新下shutdown变量\n    }\n\n    // 获取匹配stage的TaskEvent对象\n    private TaskEvent getMatchStage(NodeTask nodeTask, StageType stage) {\n        List<StageType> stages = nodeTask.getStage();\n        List<TaskEvent> events = nodeTask.getEvent();\n\n        for (int i = 0; i < stages.size(); i++) {\n            if (stages.get(i) == stage) {\n                return events.get(i);\n            }\n\n        }\n\n        return null;\n    }\n\n    // ===================== 事件处理 =====================\n\n    /**\n     * 接受manager的channel变更事件\n     */\n    protected synchronized boolean onNotifyChannel(NotifyChannelEvent event) {\n        configClientService.createOrUpdateChannel(event.getChannel()); // 更新本地的config数据\n        processNodeTask(event.getChannel());\n        return notifyListener();\n    }\n\n    private synchronized boolean notifyListener() {\n        boolean result = true;\n        List<NodeTask> incNodeTask = new ArrayList<NodeTask>(incTasks);\n        if (CollectionUtils.isEmpty(listeners) == false) {\n            for (NodeTaskListener listener : listeners) {\n                result &= listener.process(incNodeTask);\n            }\n\n            if (result) {\n                mergeIncNodeTasks();\n            } else {\n                incTasks.clear();// 清除本次的增量数据\n                if (logger.isInfoEnabled()) {\n                    logger.info(\"##notify listener error {}\", printNodeTasks(incNodeTask));\n                }\n            }\n        }\n        return result;\n    }\n\n    public void stopNode() {\n        Node node = configClientService.currentNode();\n        StopNodeEvent event = new StopNodeEvent();\n        event.setNid(node.getId());\n        nodeCommmunicationClient.callManager(event);\n    }\n\n    public void addListener(NodeTaskListener listener) {\n        Assert.notNull(listener);\n        this.listeners.add(listener);\n        notifyListener(); // 触发一次listener推送\n    }\n\n    // ===================== setter / getter =========================\n\n    public void setNodeCommmunicationClient(NodeCommmunicationClient nodeCommmunicationClient) {\n        this.nodeCommmunicationClient = nodeCommmunicationClient;\n    }\n\n    public void setConfigClientService(InternalConfigClientService configClientService) {\n        this.configClientService = configClientService;\n    }\n\n    public void setListeners(List<NodeTaskListener> listeners) {\n        this.listeners = listeners;\n    }\n\n}\n"
  },
  {
    "path": "node/common/src/main/java/com/alibaba/otter/node/common/config/model/NodeTask.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.common.config.model;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.apache.commons.lang.builder.ToStringBuilder;\nimport org.apache.commons.lang.builder.ToStringStyle;\n\nimport com.alibaba.otter.shared.common.model.config.enums.StageType;\nimport com.alibaba.otter.shared.common.model.config.pipeline.Pipeline;\n\n/**\n * S.E.T.L阶段模块的任务\n * \n * @author jianghang\n */\npublic class NodeTask implements Serializable {\n\n    private static final long serialVersionUID = 5442938515474956890L;\n    private Pipeline          pipeline;\n    private List<StageType>   stage            = new ArrayList<StageType>(); // 任务类型\n    private List<TaskEvent>   event            = new ArrayList<TaskEvent>(); // 任务事件，新增/修改\n    private boolean           shutdown         = false;\n\n    public boolean isShutdown() {\n        return shutdown;\n    }\n\n    public void setShutdown(boolean shutdown) {\n        this.shutdown = shutdown;\n    }\n\n    /**\n     * 任务事件，新增/删除/修改\n     */\n    public static enum TaskEvent {\n        CREATE, DELETE;\n\n        public boolean isCreate() {\n            return this.equals(TaskEvent.CREATE);\n        }\n\n        public boolean isDelete() {\n            return this.equals(TaskEvent.DELETE);\n        }\n\n    }\n\n    public Pipeline getPipeline() {\n        return pipeline;\n    }\n\n    public void setPipeline(Pipeline pipeline) {\n        this.pipeline = pipeline;\n    }\n\n    public List<StageType> getStage() {\n        return stage;\n    }\n\n    public void setStage(List<StageType> stage) {\n        this.stage = stage;\n    }\n\n    public List<TaskEvent> getEvent() {\n        return event;\n    }\n\n    public void setEvent(List<TaskEvent> event) {\n        this.event = event;\n    }\n\n    @Override\n    public String toString() {\n        return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE);\n    }\n\n}\n"
  },
  {
    "path": "node/common/src/main/java/com/alibaba/otter/node/common/statistics/StatisticsClientService.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.common.statistics;\n\nimport java.util.List;\n\nimport com.alibaba.otter.shared.common.model.statistics.delay.DelayCount;\nimport com.alibaba.otter.shared.common.model.statistics.table.TableStat;\nimport com.alibaba.otter.shared.common.model.statistics.throughput.ThroughputStat;\n\n/**\n * 统计信息的本地service\n * \n * @author jianghang\n */\npublic interface StatisticsClientService {\n\n    /**\n     * 发送增加 delay queue的统计数据\n     */\n    public void sendIncDelayCount(DelayCount delayCount);\n\n    /**\n     * 发送减少 delay queue的统计数据\n     */\n    public void sendDecDelayCount(DelayCount delayCount);\n\n    /**\n     * 发送reset delay queue的统计数据\n     */\n    public void sendResetDelayCount(DelayCount delayCount);\n\n    /**\n     * 发送吞吐量相关统计信息\n     */\n    public void sendThroughputs(List<ThroughputStat> stats);\n\n    /**\n     * 发送table load相关数据信息\n     */\n    public void sendTableStats(List<TableStat> stats);\n\n}\n"
  },
  {
    "path": "node/common/src/main/java/com/alibaba/otter/node/common/statistics/impl/StatisticsClientServiceImpl.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.common.statistics.impl;\n\nimport java.util.List;\nimport java.util.concurrent.BlockingQueue;\nimport java.util.concurrent.LinkedBlockingQueue;\nimport java.util.concurrent.ScheduledThreadPoolExecutor;\nimport java.util.concurrent.ThreadPoolExecutor;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.locks.LockSupport;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.InitializingBean;\n\nimport com.alibaba.otter.node.common.communication.NodeCommmunicationClient;\nimport com.alibaba.otter.node.common.statistics.StatisticsClientService;\nimport com.alibaba.otter.shared.common.model.statistics.delay.DelayCount;\nimport com.alibaba.otter.shared.common.model.statistics.table.TableStat;\nimport com.alibaba.otter.shared.common.model.statistics.throughput.ThroughputStat;\nimport com.alibaba.otter.shared.common.utils.thread.NamedThreadFactory;\nimport com.alibaba.otter.shared.communication.core.model.Callback;\nimport com.alibaba.otter.shared.communication.model.statistics.DelayCountEvent;\nimport com.alibaba.otter.shared.communication.model.statistics.DelayCountEvent.Action;\nimport com.alibaba.otter.shared.communication.model.statistics.TableStatEvent;\nimport com.alibaba.otter.shared.communication.model.statistics.ThroughputStatEvent;\n\n/**\n * 统计信息的本地客户端服务\n * \n * @author jianghang\n */\npublic class StatisticsClientServiceImpl implements StatisticsClientService, InitializingBean {\n\n    private static final Logger                logger                = LoggerFactory.getLogger(StatisticsClientServiceImpl.class);\n    private static final int                   DEFAULT_POOL          = 10;\n    // 使用一个buffer队列，保证inc/desc/reset的发送操作为一个串行过程\n    private BlockingQueue<DelayCountEvent>     delayCountStatsBuffer = new LinkedBlockingQueue<DelayCountEvent>(\n                                                                                                                10 * 1000);\n    private static ScheduledThreadPoolExecutor scheduler;\n    private NodeCommmunicationClient           nodeCommmunicationClient;\n\n    public void sendIncDelayCount(final DelayCount delayCount) {\n        DelayCountEvent event = new DelayCountEvent();\n        event.setCount(delayCount);\n        event.setAction(Action.INC);\n\n        boolean result = delayCountStatsBuffer.offer(event);\n        if (result) {\n            logger.info(\"add IncDelayCount to send with {}\", delayCount);\n        } else {\n            logger.warn(\"add IncDelayCount failed by buffer is full with {}\", delayCount);\n        }\n    }\n\n    public void sendDecDelayCount(final DelayCount delayCount) {\n        DelayCountEvent event = new DelayCountEvent();\n        event.setCount(delayCount);\n        event.setAction(Action.DEC);\n\n        boolean result = delayCountStatsBuffer.offer(event);\n        if (result) {\n            logger.info(\"add sendDecDelayCount to send with {}\", delayCount);\n        } else {\n            logger.warn(\"add sendDecDelayCount failed by buffer is full with {}\", delayCount);\n        }\n    }\n\n    public void sendResetDelayCount(final DelayCount delayCount) {\n        DelayCountEvent event = new DelayCountEvent();\n        event.setCount(delayCount);\n        event.setAction(Action.RESET);\n\n        boolean result = delayCountStatsBuffer.offer(event);\n        if (result) {\n            logger.info(\"add sendResetDelayCount to send with {}\", delayCount);\n        } else {\n            logger.warn(\"add sendResetDelayCount failed by buffer is full with {}\", delayCount);\n        }\n    }\n\n    public void sendThroughputs(final List<ThroughputStat> stats) {\n        ThroughputStatEvent event = new ThroughputStatEvent();\n        event.setStats(stats);\n        nodeCommmunicationClient.callManager(event, new Callback<Object>() {\n\n            public void call(Object event) {\n                logger.info(\"sendThroughput successed for {}\", stats);\n            }\n        });\n\n    }\n\n    public void sendTableStats(final List<TableStat> stats) {\n        TableStatEvent event = new TableStatEvent();\n        event.setStats(stats);\n        nodeCommmunicationClient.callManager(event, new Callback<Object>() {\n\n            public void call(Object event) {\n                logger.info(\"sendTableStats successed for {}\", stats);\n            }\n        });\n    }\n\n    // ================= helper method ==============\n    public void afterPropertiesSet() throws Exception {\n        scheduler = new ScheduledThreadPoolExecutor(DEFAULT_POOL, new NamedThreadFactory(\"Otter-Statistics-Client\"),\n                                                    new ThreadPoolExecutor.CallerRunsPolicy());\n        scheduler.submit(new Runnable() {\n\n            public void run() {\n                doSendDelayCountEvent();\n            }\n        });\n    }\n\n    private void doSendDelayCountEvent() {\n        DelayCountEvent event = null;\n        while (true) { // 尝试从队列里获取一下数据，不阻塞，没有就退出，等下个5秒再检查一次\n            try {\n                event = delayCountStatsBuffer.take();\n                nodeCommmunicationClient.callManager(event);\n                logger.info(\"sendDelayCountEvent successed for {}\", event);\n            } catch (InterruptedException e) {\n                Thread.currentThread().interrupt();\n                return; // 退出\n            } catch (Exception e) {\n                LockSupport.parkNanos(TimeUnit.NANOSECONDS.convert(1, TimeUnit.SECONDS));\n            }\n        }\n    }\n\n    // ================ setter / getter =================\n\n    public void setNodeCommmunicationClient(NodeCommmunicationClient nodeCommmunicationClient) {\n        this.nodeCommmunicationClient = nodeCommmunicationClient;\n    }\n\n}\n"
  },
  {
    "path": "node/common/src/main/resources/spring/otter-node-communication.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<beans xmlns=\"http://www.springframework.org/schema/beans\"\r\n\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\r\n\txmlns:aop=\"http://www.springframework.org/schema/aop\"\r\n\txmlns:tx=\"http://www.springframework.org/schema/tx\"\r\n\txsi:schemaLocation=\"\n\thttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd\n\thttp://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd\n\thttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd\"\r\n\tdefault-autowire=\"byName\" default-dependency-check=\"none\" >\r\n\r\n\t<!--  communication tool -->\r\n\t<!-- \r\n\t<bean id=\"endpoint\" class=\"com.alibaba.otter.shared.communication.core.impl.rmi.RmiCommunicationEndpoint\" />\r\n\t<bean id=\"communicationClient\" class=\"com.alibaba.otter.shared.communication.core.impl.DefaultCommunicationClientImpl\" init-method=\"initial\" destroy-method=\"destory\">\r\n\t\t<property name=\"factory\">\r\n\t\t\t<bean class=\"com.alibaba.otter.shared.communication.core.impl.connection.CommunicationConnectionPoolFactory\" init-method=\"initial\" destroy-method=\"destory\" >\r\n\t\t\t\t<property name=\"factory\">\r\n\t\t\t\t\t<bean class=\"com.alibaba.otter.shared.communication.core.impl.rmi.RmiCommunicationConnectionFactory\" />\r\n\t\t\t\t</property>\r\n\t\t\t\t<property name=\"maxActive\" value=\"${otter.communication.pool.size}\" />\r\n\t\t\t</bean>\r\n\t\t</property>\r\n\t\t<property name=\"discard\" value=\"true\" />\r\n\t</bean>\r\n\t -->\r\n\t<bean id=\"endpoint\" class=\"com.alibaba.otter.shared.communication.core.impl.dubbo.DubboCommunicationEndpoint\">\r\n\t\t<property name=\"payload\" value=\"${otter.communication.payload:8388608}\" />\r\n\t</bean>\r\n\t<bean id=\"communicationClient\" class=\"com.alibaba.otter.shared.communication.core.impl.DefaultCommunicationClientImpl\" init-method=\"initial\" destroy-method=\"destory\">\r\n\t\t<property name=\"poolSize\" value=\"${otter.communication.pool.size:10}\" />\r\n\t\t<property name=\"factory\">\r\n\t\t\t<bean class=\"com.alibaba.otter.shared.communication.core.impl.dubbo.DubboCommunicationConnectionFactory\">\r\n\t\t\t\t<property name=\"payload\" value=\"${otter.communication.payload:8388608}\" />\r\n\t\t\t</bean>\r\n\t\t</property>\r\n\t\t<property name=\"discard\" value=\"true\" />\r\n\t</bean>\r\n\r\n\t<!-- Node communication -->\r\n\t<bean id=\"nodeCommmunicationClient\" class=\"com.alibaba.otter.node.common.communication.NodeCommmunicationClient\" >\r\n\t\t<property name=\"delegate\" ref=\"communicationClient\" />\r\n\t\t<property name=\"managerAddress\" value=\"${otter.manager.address:127.0.0.1:1099}\" />\r\n\t\t<property name=\"configClientService\" ref=\"configClientService\" />\r\n\t</bean>\r\n\t<bean id=\"nodeCommunicationEndpoint\" class=\"com.alibaba.otter.node.common.communication.NodeCommunicationEndpoint\" >\r\n\t\t<property name=\"endpoint\" ref=\"endpoint\" />\r\n\t\t<property name=\"configClientService\" ref=\"configClientService\" />\r\n\t</bean>\r\n</beans>"
  },
  {
    "path": "node/common/src/main/resources/spring/otter-node-config.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<beans xmlns=\"http://www.springframework.org/schema/beans\"\r\n\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\r\n\txmlns:aop=\"http://www.springframework.org/schema/aop\"\r\n\txmlns:tx=\"http://www.springframework.org/schema/tx\"\r\n\txsi:schemaLocation=\"\n\thttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd\n\thttp://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd\n\thttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd\"\r\n\tdefault-autowire=\"byName\" default-dependency-check=\"none\" >\r\n\r\n\t<bean id=\"nodeTaskService\" class=\"com.alibaba.otter.node.common.config.impl.NodeTaskServiceImpl\" depends-on=\"nodeCommunicationEndpoint\">\r\n\t</bean>\r\n\t\r\n\t<bean id=\"configClientService\" class=\"com.alibaba.otter.node.common.config.impl.ConfigClientServiceImpl\" depends-on=\"zookeeperClient\">\r\n\t\t<property name=\"timeout\" value=\"3600000\" /> <!-- 1小时 -->\r\n\t</bean>\r\n</beans>"
  },
  {
    "path": "node/common/src/main/resources/spring/otter-node-statistics.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<beans xmlns=\"http://www.springframework.org/schema/beans\"\r\n\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\r\n\txmlns:aop=\"http://www.springframework.org/schema/aop\"\r\n\txmlns:tx=\"http://www.springframework.org/schema/tx\"\r\n\txsi:schemaLocation=\"\n\thttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd\n\thttp://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd\n\thttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd\"\r\n\tdefault-autowire=\"byName\" default-dependency-check=\"none\" >\r\n\r\n\t<bean id=\"statisticsClientService\" class=\"com.alibaba.otter.node.common.statistics.impl.StatisticsClientServiceImpl\">\r\n\t</bean>\r\n</beans>\r\n"
  },
  {
    "path": "node/common/src/test/java/com/alibaba/otter/node/common/ArbitrateRemoteServiceIntegration.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.common;\n\nimport java.util.concurrent.TimeUnit;\n\nimport org.jtester.annotations.SpringBeanByName;\nimport org.testng.annotations.BeforeClass;\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.node.common.config.ConfigClientService;\nimport com.alibaba.otter.shared.arbitrate.impl.alarm.AlarmClientService;\n\n/**\n * 测试下仲裁器报警处理\n * \n * @author jianghang 2011-11-29 上午09:52:18\n * @version 4.0.0\n */\npublic class ArbitrateRemoteServiceIntegration extends BaseOtterTest {\n\n    @SpringBeanByName\n    private ConfigClientService configClientService;\n\n    @SpringBeanByName\n    private AlarmClientService  alarmClientService;\n\n    @BeforeClass\n    public void initial() {\n        System.setProperty(\"nid\", \"1\");\n    }\n\n    @Test\n    public void test_send() {\n        alarmClientService.sendAlarm(3L, 1L, null, \"load is interrupt!\");\n\n        try {\n            TimeUnit.MILLISECONDS.sleep(10000L);\n        } catch (InterruptedException e) {\n            want.fail();\n        }\n    }\n}\n"
  },
  {
    "path": "node/common/src/test/java/com/alibaba/otter/node/common/BaseOtterTest.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.common;\n\nimport org.jtester.annotations.SpringApplicationContext;\n\n/**\n * @author jianghang 2011-9-16 下午02:58:37\n * @version 4.0.0\n */\n@SpringApplicationContext(\"applicationContext.xml\")\npublic class BaseOtterTest extends org.jtester.testng.JTester {\n\n}\n"
  },
  {
    "path": "node/common/src/test/java/com/alibaba/otter/node/common/config/ConfigClientServiceIntegration.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.common.config;\n\nimport org.jtester.annotations.SpringBeanByName;\nimport org.testng.annotations.BeforeClass;\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.node.common.BaseOtterTest;\nimport com.alibaba.otter.shared.common.model.config.channel.Channel;\nimport com.alibaba.otter.shared.common.model.config.node.Node;\n\npublic class ConfigClientServiceIntegration extends BaseOtterTest {\n\n    @SpringBeanByName\n    private ConfigClientService configClientService;\n\n    @BeforeClass\n    public void initial() {\n        System.setProperty(\"nid\", \"1\");\n    }\n\n    @Test\n    public void test_node() {\n        Node cnode = configClientService.currentNode();\n        System.out.println(cnode);\n        want.bool(cnode.getId() == 1L).is(true);\n        Node fnode = configClientService.findNode(2L);\n        System.out.println(fnode);\n        want.bool(fnode.getId() == 2L);\n\n        fnode = configClientService.findNode(2L);\n        System.out.println(fnode);\n        want.bool(fnode.getId() == 2L);\n    }\n\n    @Test\n    public void test_pipeline() {\n        Long channelId = 11L;\n        Channel channel = configClientService.findChannel(channelId);\n        System.out.println(channel);\n        want.number(channel.getId()).isEqualTo(channelId);\n    }\n\n}\n"
  },
  {
    "path": "node/common/src/test/java/com/alibaba/otter/node/common/config/ConfigClientServiceTest.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.common.config;\n\nimport java.util.Arrays;\n\nimport mockit.Mock;\nimport mockit.Mockit;\n\nimport org.jtester.annotations.SpringBeanByName;\nimport org.testng.annotations.BeforeClass;\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.node.common.BaseOtterTest;\nimport com.alibaba.otter.node.common.communication.NodeCommmunicationClient;\nimport com.alibaba.otter.shared.common.model.config.channel.Channel;\nimport com.alibaba.otter.shared.common.model.config.node.Node;\nimport com.alibaba.otter.shared.common.model.config.pipeline.Pipeline;\nimport com.alibaba.otter.shared.communication.core.model.Event;\nimport com.alibaba.otter.shared.communication.model.config.FindNodeEvent;\n\n/**\n * config client测试\n * \n * @author jianghang 2011-10-21 上午10:33:51\n * @version 4.0.0\n */\npublic class ConfigClientServiceTest extends BaseOtterTest {\n\n    @SpringBeanByName\n    private ConfigClientService configClientService;\n\n    @BeforeClass\n    public void initial() {\n        System.setProperty(\"nid\", \"1\");\n    }\n\n    @Test\n    public void test_node() {\n        final Node node = new Node();\n        Mockit.setUpMock(NodeCommmunicationClient.class, new Object() {\n\n            @Mock\n            public Object callManager(final Event event) {\n                if (event instanceof FindNodeEvent) {\n                    Long nid = ((FindNodeEvent) event).getNid();\n                    node.setId(nid);\n                }\n\n                return node;\n            }\n        });\n\n        Node cnode = configClientService.currentNode();\n        want.bool(cnode.getId() == 1L);\n        Node fnode = configClientService.findNode(2L);\n        want.bool(fnode.getId() == 2L);\n    }\n\n    @Test\n    public void test_pipeline() {\n        Long channelId = 100L;\n        Long pipelineId = 100L;\n        Long oppositePipelineId = 101L;\n        final Channel channel = new Channel();\n        channel.setId(channelId);\n        Pipeline pipeline1 = new Pipeline();\n        pipeline1.setChannelId(channelId);\n        pipeline1.setId(pipelineId);\n\n        Pipeline pipeline2 = new Pipeline();\n        pipeline2.setChannelId(channelId);\n        pipeline2.setId(oppositePipelineId);\n        channel.setPipelines(Arrays.asList(pipeline1, pipeline2));\n\n        Mockit.setUpMock(NodeCommmunicationClient.class, new Object() {\n\n            @Mock\n            public Object callManager(final Event event) {\n                return channel;\n            }\n        });\n\n        Pipeline pipeline = configClientService.findPipeline(pipelineId);\n        want.bool(pipeline.getId() == pipelineId);\n\n        pipeline = configClientService.findOppositePipeline(pipelineId);\n        want.bool(pipeline.getId() == oppositePipelineId);\n\n        Channel channel1 = configClientService.findChannel(channelId);\n        want.bool(channel1.getId() == channelId);\n\n        channel1 = configClientService.findChannelByPipelineId(pipelineId);\n        want.bool(channel1.getId() == channelId);\n    }\n}\n"
  },
  {
    "path": "node/common/src/test/java/com/alibaba/otter/node/common/config/NodeTaskServiceIntegration.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.common.config;\n\nimport java.util.List;\n\nimport org.jtester.annotations.SpringBeanByName;\nimport org.testng.annotations.BeforeClass;\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.node.common.BaseOtterTest;\nimport com.alibaba.otter.node.common.config.model.NodeTask;\n\npublic class NodeTaskServiceIntegration extends BaseOtterTest {\n\n    @SpringBeanByName\n    private NodeTaskService nodeTaskService;\n\n    @BeforeClass\n    public void initial() {\n        System.setProperty(\"nid\", \"1\");\n    }\n\n    @Test\n    public void test_task() {\n        List<NodeTask> tasks = nodeTaskService.listAllNodeTasks();\n        System.out.println(tasks);\n    }\n}\n"
  },
  {
    "path": "node/common/src/test/java/com/alibaba/otter/node/common/config/NodeTaskServiceTest.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.common.config;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport mockit.Mock;\nimport mockit.Mocked;\nimport mockit.Mockit;\n\nimport org.jtester.annotations.SpringBeanFrom;\nimport org.testng.annotations.BeforeClass;\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.node.common.BaseOtterTest;\nimport com.alibaba.otter.node.common.communication.NodeCommmunicationClient;\nimport com.alibaba.otter.node.common.communication.NodeCommunicationEndpoint;\nimport com.alibaba.otter.node.common.config.impl.NodeTaskServiceImpl;\nimport com.alibaba.otter.node.common.config.model.NodeTask;\nimport com.alibaba.otter.shared.common.model.config.channel.Channel;\nimport com.alibaba.otter.shared.common.model.config.channel.ChannelStatus;\nimport com.alibaba.otter.shared.common.model.config.node.Node;\nimport com.alibaba.otter.shared.common.model.config.pipeline.Pipeline;\nimport com.alibaba.otter.shared.common.utils.TestUtils;\nimport com.alibaba.otter.shared.communication.core.model.Event;\nimport com.alibaba.otter.shared.communication.model.config.FindNodeEvent;\nimport com.alibaba.otter.shared.communication.model.config.NotifyChannelEvent;\nimport com.google.common.collect.Lists;\n\npublic class NodeTaskServiceTest extends BaseOtterTest {\n\n    @Mocked\n    @SpringBeanFrom\n    private NodeCommunicationEndpoint nodeCommunicationEndpoint;\n\n    @BeforeClass\n    public void initial() {\n        System.setProperty(\"nid\", \"1\");\n        try {\n            new NonStrictExpectations() {\n\n                {\n                    nodeCommunicationEndpoint.afterPropertiesSet();\n                }\n            };\n        } catch (Exception e) {\n            want.fail();\n        }\n\n    }\n\n    @Test\n    public void test_b_channel() {\n        Long channelId = 100L;\n        Long pipelineId = 101L;\n        Long oppositePipelineId = 102L; // 先加一个pipeline的同步任务\n        final Channel channel = new Channel();\n        channel.setId(channelId);\n        channel.setStatus(ChannelStatus.START);\n        Pipeline pipeline1 = new Pipeline();\n        pipeline1.setChannelId(channelId);\n        pipeline1.setId(pipelineId);\n\n        Pipeline pipeline2 = new Pipeline();\n        pipeline2.setChannelId(channelId);\n        pipeline2.setId(oppositePipelineId);\n        channel.setPipelines(Arrays.asList(pipeline1, pipeline2));\n\n        Node node1 = new Node();\n        node1.setId(1L);\n\n        Node node2 = new Node();\n        node2.setId(2L);\n\n        pipeline1.setSelectNodes(Arrays.asList(node1, node2));\n        pipeline1.setExtractNodes(Arrays.asList(node1, node2));\n        pipeline1.setLoadNodes(Arrays.asList(node2));\n\n        pipeline2.setSelectNodes(Arrays.asList(node1));\n        pipeline2.setExtractNodes(Arrays.asList(node1));\n        pipeline2.setLoadNodes(Arrays.asList(node1, node2));\n        Mockit.setUpMock(NodeCommmunicationClient.class, new Object() {\n\n            @Mock\n            public Object callManager(final Event event) {\n                if (event instanceof FindNodeEvent) {\n                    Node node = new Node();\n                    Long nid = ((FindNodeEvent) event).getNid();\n                    node.setId(nid);\n                    return node;\n                } else {\n                    return Arrays.asList(channel);\n                }\n            }\n        });\n        // 初始化一下数据\n        NodeTaskServiceImpl nodeTaskSerivce = (NodeTaskServiceImpl) spring.getBean(\"nodeTaskService\");\n        List<NodeTask> tasks = null;\n\n        NotifyChannelEvent event = new NotifyChannelEvent();\n        event.setChannel(channel);\n        reflector.invoke(nodeTaskSerivce, \"onNotifyChannel\", event);\n        tasks = reflector.invoke(nodeTaskSerivce, \"mergeIncNodeTasks\");\n        want.bool(tasks.size() == 2).is(true);\n\n        tasks = nodeTaskSerivce.listAllNodeTasks();\n        want.bool(tasks.size() == 2).is(true);\n    }\n\n    @Test\n    public void test_a_reload() throws Exception {\n        Long channelId = 100L;\n        Long pipelineId = 100L;\n        Long oppositePipelineId = 101L;\n        final Channel channel = new Channel();\n        channel.setId(channelId);\n        channel.setStatus(ChannelStatus.START);\n        Pipeline pipeline1 = new Pipeline();\n        pipeline1.setChannelId(channelId);\n        pipeline1.setId(pipelineId);\n\n        Pipeline pipeline2 = new Pipeline();\n        pipeline2.setChannelId(channelId);\n        pipeline2.setId(oppositePipelineId);\n        channel.setPipelines(Arrays.asList(pipeline1, pipeline2));\n\n        Node node1 = new Node();\n        node1.setId(1L);\n\n        Node node2 = new Node();\n        node2.setId(2L);\n\n        pipeline1.setSelectNodes(Arrays.asList(node1, node2));\n        pipeline1.setExtractNodes(Arrays.asList(node1, node2));\n        pipeline1.setLoadNodes(Arrays.asList(node1));\n\n        pipeline2.setSelectNodes(Arrays.asList(node2));\n        pipeline2.setExtractNodes(Arrays.asList(node2));\n        pipeline2.setLoadNodes(Arrays.asList(node1, node2));\n\n        Mockit.setUpMock(NodeCommmunicationClient.class, new Object() {\n\n            @Mock\n            public Object callManager(final Event event) {\n                if (event instanceof FindNodeEvent) {\n                    Node node = new Node();\n                    Long nid = ((FindNodeEvent) event).getNid();\n                    node.setId(nid);\n                    return node;\n                } else {\n                    return Arrays.asList(channel);\n                }\n            }\n        });\n        List<NodeTask> tasks = null;\n        NodeTaskServiceImpl nodeTaskSerivce = (NodeTaskServiceImpl) spring.getBean(\"nodeTaskService\");\n        reflector.invoke(nodeTaskSerivce, \"initNodeTask\");\n        tasks = reflector.invoke(nodeTaskSerivce, \"mergeIncNodeTasks\");\n        want.number(tasks.size()).isEqualTo(2);\n\n        reflector.invoke(nodeTaskSerivce, \"initNodeTask\");\n        tasks = reflector.invoke(nodeTaskSerivce, \"mergeIncNodeTasks\");\n        want.bool(tasks.size() == 0).is(true);\n\n        channel.setStatus(ChannelStatus.STOP);\n        reflector.invoke(nodeTaskSerivce, \"initNodeTask\");\n        tasks = reflector.invoke(nodeTaskSerivce, \"mergeIncNodeTasks\");\n        want.bool(tasks.size() == 2).is(true);\n        tasks = nodeTaskSerivce.listAllNodeTasks();\n        want.bool(tasks.size() == 2).is(true);\n\n        // 清理内存\n        TestUtils.setField(nodeTaskSerivce, \"allTasks\", Lists.newArrayList());\n        TestUtils.setField(nodeTaskSerivce, \"incTasks\", Lists.newArrayList());\n        // 删除某个pipeline的node\n        channel.setStatus(ChannelStatus.START);\n        reflector.invoke(nodeTaskSerivce, \"initNodeTask\");\n        tasks = reflector.invoke(nodeTaskSerivce, \"mergeIncNodeTasks\");\n        want.number(tasks.size()).isEqualTo(2);\n\n        pipeline1.setSelectNodes(Arrays.asList(node2));\n        pipeline1.setExtractNodes(Arrays.asList(node2));\n        pipeline1.setLoadNodes(Arrays.asList(node2));\n        channel.setStatus(ChannelStatus.START);\n        reflector.invoke(nodeTaskSerivce, \"initNodeTask\");\n        tasks = reflector.invoke(nodeTaskSerivce, \"mergeIncNodeTasks\");\n        want.bool(tasks.size() == 1).is(true);\n\n        // 清理内存\n        TestUtils.setField(nodeTaskSerivce, \"allTasks\", Lists.newArrayList());\n        TestUtils.setField(nodeTaskSerivce, \"incTasks\", Lists.newArrayList());\n        channel.setStatus(ChannelStatus.START);\n        reflector.invoke(nodeTaskSerivce, \"initNodeTask\");\n        tasks = reflector.invoke(nodeTaskSerivce, \"mergeIncNodeTasks\");\n        want.number(tasks.size()).isEqualTo(1);\n        // 删除某个pipeline\n        channel.setPipelines(Arrays.asList(pipeline1));\n        reflector.invoke(nodeTaskSerivce, \"initNodeTask\");\n        tasks = reflector.invoke(nodeTaskSerivce, \"mergeIncNodeTasks\");\n        want.number(tasks.size()).isEqualTo(1);\n    }\n}\n"
  },
  {
    "path": "node/common/src/test/java/com/alibaba/otter/node/common/statistics/StatisticsClientServiceIntegration.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.common.statistics;\n\nimport java.util.Arrays;\nimport java.util.Date;\n\nimport org.jtester.annotations.SpringBeanByName;\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.node.common.BaseOtterTest;\nimport com.alibaba.otter.shared.common.model.statistics.delay.DelayCount;\nimport com.alibaba.otter.shared.common.model.statistics.table.TableStat;\nimport com.alibaba.otter.shared.common.model.statistics.throughput.ThroughputStat;\nimport com.alibaba.otter.shared.common.model.statistics.throughput.ThroughputType;\n\npublic class StatisticsClientServiceIntegration extends BaseOtterTest {\n\n    @SpringBeanByName\n    private StatisticsClientService statisticsClientService;\n\n    @Test\n    public void testSend() {\n        sendDelayStat();\n        sendThroughputs();\n        sendTableStats();\n\n        try {\n            Thread.sleep(3000L);\n        } catch (InterruptedException e) {\n            want.fail();\n        }\n    }\n\n    private void sendDelayStat() {\n        DelayCount count = new DelayCount();\n        count.setPipelineId(1L);\n        count.setNumber(100L);\n        count.setTime(5L);\n\n        statisticsClientService.sendIncDelayCount(count);\n        statisticsClientService.sendDecDelayCount(count);\n    }\n\n    private void sendThroughputs() {\n        Date now = new Date();\n\n        ThroughputStat rowStat = new ThroughputStat();\n        rowStat.setType(ThroughputType.ROW);\n        rowStat.setStartTime(new Date(now.getTime() - 600 * 1000L));\n        rowStat.setEndTime(now);\n        rowStat.setPipelineId(1L);\n        rowStat.setNumber(100L);\n        rowStat.setSize(100L);\n\n        ThroughputStat fileStat = new ThroughputStat();\n        fileStat.setType(ThroughputType.FILE);\n        fileStat.setStartTime(new Date(now.getTime() - 800 * 1000L));\n        fileStat.setEndTime(now);\n        fileStat.setPipelineId(1L);\n        fileStat.setNumber(101L);\n        fileStat.setSize(101L);\n\n        statisticsClientService.sendThroughputs(Arrays.asList(rowStat, fileStat));\n    }\n\n    private void sendTableStats() {\n        TableStat stat1 = new TableStat();\n        stat1.setPipelineId(1L);\n        stat1.setDataMediaPairId(1L);\n        stat1.setFileCount(100L);\n        stat1.setFileSize(100L);\n        stat1.setInsertCount(100L);\n        stat1.setUpdateCount(100L);\n        stat1.setDeleteCount(100L);\n\n        TableStat stat2 = new TableStat();\n        stat2.setPipelineId(1L);\n        stat2.setDataMediaPairId(2L);\n        stat2.setFileCount(101L);\n        stat2.setFileSize(101L);\n        stat2.setInsertCount(101L);\n        stat2.setUpdateCount(101L);\n        stat2.setDeleteCount(101L);\n\n        statisticsClientService.sendTableStats(Arrays.asList(stat1, stat2));\n    }\n}\n"
  },
  {
    "path": "node/common/src/test/resources/applicationContext.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<beans xmlns=\"http://www.springframework.org/schema/beans\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n    xmlns:tx=\"http://www.springframework.org/schema/tx\" xmlns:aop=\"http://www.springframework.org/schema/aop\"\n    xmlns:lang=\"http://www.springframework.org/schema/lang\" xmlns:context=\"http://www.springframework.org/schema/context\"\n    xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd\n           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd\n           http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-2.0.xsd\n           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd\n           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd\"\n    default-autowire=\"byName\">\n\t\n\t<!-- properties -->\n\t<bean class=\"com.alibaba.otter.shared.common.utils.spring.PropertyPlaceholderConfigurer\" lazy-init=\"false\">\n\t\t<property name=\"ignoreResourceNotFound\" value=\"true\" />\n\t\t<property name=\"systemPropertiesModeName\" value=\"SYSTEM_PROPERTIES_MODE_OVERRIDE\"/><!-- 允许system覆盖 -->\n\t\t<property name=\"locations\">\n\t\t\t<list>\n\t\t\t\t<value>classpath:otter.properties</value>\n\t\t\t</list>\n\t\t</property>\n\t</bean>\n\t\n\t<import resource=\"classpath:spring/otter-*.xml\"/>\n</beans>"
  },
  {
    "path": "node/deployer/pom.xml",
    "content": "<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\">\n\t<modelVersion>4.0.0</modelVersion>\n\t<parent>\n\t\t<groupId>com.alibaba.otter</groupId>\n\t\t<artifactId>node</artifactId>\n\t\t<version>4.2.19-SNAPSHOT</version>\n\t\t<relativePath>../pom.xml</relativePath>\n\t</parent>\n\t<groupId>com.alibaba.otter</groupId>\n\t<artifactId>node.deployer</artifactId>\n\t<packaging>jar</packaging>\n\t<name>node deployer for otter</name>\n\t<url>http://github.com/alibaba/otter</url>\n\t\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>com.alibaba.otter</groupId>\n\t\t\t<artifactId>node.common</artifactId>\n\t\t\t<version>${project.version}</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>com.alibaba.otter</groupId>\n\t\t\t<artifactId>node.etl</artifactId>\n\t\t\t<version>${project.version}</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>com.alibaba.otter</groupId>\n\t\t\t<artifactId>node.canal</artifactId>\n\t\t\t<version>${project.version}</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>com.alibaba.otter</groupId>\n\t\t\t<artifactId>node.extend</artifactId>\n\t\t\t<version>${project.version}</version>\n\t\t</dependency>\n\t</dependencies>\n\t\n\t<build>\n\t\t<plugins>\n\t\t\t<!-- deploy模块的packaging通常是jar，如果项目中没有java 源代码或资源文件，加上这一段配置使项目能通过构建 -->\n\t\t\t<plugin>\n\t\t\t\t<artifactId>maven-jar-plugin</artifactId>\n\t\t\t\t<configuration>\n\t\t\t\t\t<excludes>\n\t\t\t\t\t\t<exclude>**/logback.xml</exclude>\n\t\t\t\t\t\t<exclude>**/otter.properties</exclude>\n\t\t\t\t\t</excludes>\n\t\t\t\t</configuration>\n\t\t\t</plugin>\n\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.apache.maven.plugins</groupId>\n\t\t\t\t<artifactId>maven-assembly-plugin</artifactId>\n\t\t\t\t<!-- 这是最新版本，推荐使用这个版本 -->\n\t\t\t\t<version>2.2.1</version>\n\t\t\t\t<executions>\n\t\t\t\t\t<execution>\n\t\t\t\t\t\t<id>assemble</id>\n\t\t\t\t\t\t<goals>\n\t\t\t\t\t\t\t<goal>single</goal>\n\t\t\t\t\t\t</goals>\n\t\t\t\t\t\t<phase>package</phase>\n\t\t\t\t\t</execution>\n\t\t\t\t</executions>\n\t\t\t\t<configuration>\n\t\t\t\t\t<appendAssemblyId>false</appendAssemblyId>\n\t\t\t\t\t<attach>false</attach>\n\t\t\t\t</configuration>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\n\t<profiles>\n\t\t<profile>\n\t\t\t<id>dev</id>\n\t\t\t<activation>\n\t\t\t\t<activeByDefault>true</activeByDefault>\n\t\t\t\t<property>\n\t\t\t\t\t<name>env</name>\n\t\t\t\t\t<value>dev</value>\n\t\t\t\t</property>\n\t\t\t</activation>\n\n\t\t\t<build>\n\t\t\t\t<plugins>\n\t\t\t\t\t<plugin>\n\t\t\t\t\t\t<artifactId>maven-assembly-plugin</artifactId>\n\t\t\t\t\t\t<configuration>\n\t\t\t\t\t\t\t<!-- maven assembly插件需要一个描述文件 来告诉插件包的结构以及打包所需的文件来自哪里 -->\n\t\t\t\t\t\t\t<descriptors>\n\t\t\t\t\t\t\t\t<descriptor>${basedir}/src/main/assembly/dev.xml</descriptor>\n\t\t\t\t\t\t\t</descriptors>\n\t\t\t\t\t\t\t<finalName>node</finalName>\n\t\t\t\t\t\t\t<outputDirectory>${project.build.directory}</outputDirectory>\n\t\t\t\t\t\t</configuration>\n\t\t\t\t\t</plugin>\n\t\t\t\t</plugins>\n\t\t\t</build>\n\n\t\t</profile>\n\n\t\t<profile>\n\t\t\t<id>release</id>\n\t\t\t<activation>\n\t\t\t\t<property>\n\t\t\t\t\t<name>env</name>\n\t\t\t\t\t<value>release</value>\n\t\t\t\t</property>\n\t\t\t</activation>\n\t\t\t<build>\n\t\t\t\t<plugins>\n\t\t\t\t\t<plugin>\n\t\t\t\t\t\t<artifactId>maven-assembly-plugin</artifactId>\n\t\t\t\t\t\t<configuration>\n\t\t\t\t\t\t\t<!-- 发布模式使用的maven assembly插件描述文件 -->\n\t\t\t\t\t\t\t<descriptors>\n\t\t\t\t\t\t\t\t<descriptor>${basedir}/src/main/assembly/release.xml</descriptor>\n\t\t\t\t\t\t\t</descriptors>\n\t\t\t\t\t\t\t<!-- 如果一个应用的包含多个deploy模块，如果使用同样的包名， 如果把它们复制的一个目录中可能会失败，所以包名加了 artifactId以示区分 -->\n\t\t\t\t\t\t\t<finalName>${project.artifactId}-${project.version}</finalName>\n\t\t\t\t\t\t\t<!-- scm 要求 release 模式打出的包放到顶级目录下的target子目录中 -->\n\t\t\t\t\t\t\t<outputDirectory>${project.parent.parent.build.directory}</outputDirectory>\n\t\t\t\t\t\t</configuration>\n\t\t\t\t\t</plugin>\n\t\t\t\t</plugins>\n\t\t\t</build>\n\t\t</profile>\n\t\t\n\t\t<profile>\n\t\t\t<id>mvn</id>\n\t\t\t<activation>\n\t\t\t\t<property>\n\t\t\t\t\t<name>env</name>\n\t\t\t\t\t<value>mvn</value>\n\t\t\t\t</property>\n\t\t\t</activation>\n\t\t\t<build>\n\t\t\t\t<plugins>\n\t\t\t\t\t<plugin>\n\t\t\t\t\t\t<artifactId>maven-assembly-plugin</artifactId>\n\t\t\t\t\t\t<configuration>\n\t\t\t\t\t\t\t<!-- 发布模式使用的maven assembly插件描述文件 -->\n\t\t\t\t\t\t\t<descriptors>\n\t\t\t\t\t\t\t\t<descriptor>${basedir}/src/main/assembly/mvn.xml</descriptor>\n\t\t\t\t\t\t\t</descriptors>\n\t\t\t\t\t\t\t<!-- 如果一个应用的包含多个deploy模块，如果使用同样的包名， 如果把它们复制的一个目录中可能会失败，所以包名加了 artifactId以示区分 -->\n\t\t\t\t\t\t\t<finalName>${project.artifactId}-${project.version}</finalName>\n\t\t\t\t\t\t\t<!-- scm 要求 release 模式打出的包放到顶级目录下的target子目录中 -->\n\t\t\t\t\t\t\t<outputDirectory>${project.parent.parent.build.directory}</outputDirectory>\n\t\t\t\t\t\t</configuration>\n\t\t\t\t\t</plugin>\n\t\t\t\t</plugins>\n\t\t\t</build>\n\t\t</profile>\n\t</profiles>\n</project>\n"
  },
  {
    "path": "node/deployer/src/main/assembly/component.xml",
    "content": "<component>\n\t<fileSets>\n\t\t<fileSet>\n\t\t\t<directory>.</directory>\n\t\t\t<outputDirectory>/</outputDirectory>\n\t\t\t<includes>\n\t\t\t\t<include>README*</include>\n\t\t\t</includes>\n\t\t</fileSet>\n\t\t<fileSet>\n\t\t\t<directory>./src/main/bin</directory>\n\t\t\t<outputDirectory>bin</outputDirectory>\n\t\t\t<includes>\n\t\t\t\t<include>**/*</include>\n\t\t\t</includes>\n\t\t\t<fileMode>0755</fileMode>\n\t\t</fileSet>\n\t\t<!-- \n\t\t<fileSet>\n\t\t\t<directory>./src/main/resources</directory>\n\t\t\t<outputDirectory>/conf</outputDirectory>\n\t\t\t<includes>\n\t\t\t\t<include>**/*</include>\n\t\t\t</includes>\n\t\t</fileSet>\n\t\t -->\n\t\t<fileSet>\n\t\t\t<directory>target</directory>\n\t\t\t<outputDirectory>logs</outputDirectory>\n\t\t\t<excludes>\n\t\t\t\t<exclude>**/*</exclude>\n\t\t\t</excludes>\n\t\t</fileSet>\n\t</fileSets>\n\t<files>\n\t\t<file>\n\t\t\t<source>./src/main/resources/logback.xml</source>\n\t\t\t<outputDirectory>conf</outputDirectory>\n\t\t</file>\n\t\t<file>\n\t\t\t<source>./src/main/resources/otter.properties</source>\n\t\t\t<outputDirectory>conf</outputDirectory>\n\t\t</file>\n\t\t<file>\n\t\t\t<source>../../lib/ojdbc6.jar</source>\n\t\t\t<outputDirectory>lib</outputDirectory>\n\t\t</file>\n\t</files>\n\t<dependencySets>\n\t\t<dependencySet>\n\t\t\t<outputDirectory>lib</outputDirectory>\n\t\t\t<excludes>\n\t\t\t\t<exclude>junit:junit</exclude>\n\t\t\t\t<exclude>org.jtester:jtester</exclude>\n\t\t\t</excludes>\n\t\t</dependencySet>\n\t</dependencySets>\n</component>\n"
  },
  {
    "path": "node/deployer/src/main/assembly/dev.xml",
    "content": "<assembly xmlns=\"http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\r\n\txsi:schemaLocation=\"http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd\">\r\n\t<id>dev</id>\r\n\t<formats>\r\n\t\t<format>dir</format>\r\n\t</formats>\r\n\t<includeBaseDirectory>false</includeBaseDirectory>\r\n\t<componentDescriptors>\r\n\t\t<componentDescriptor>src/main/assembly/component.xml</componentDescriptor>\r\n\t</componentDescriptors>\r\n</assembly>\r\n"
  },
  {
    "path": "node/deployer/src/main/assembly/mvn.xml",
    "content": "<assembly xmlns=\"http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\r\n\txsi:schemaLocation=\"http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd\">\r\n\t<id>release</id>\r\n\t<formats>\r\n\t\t<format>tar.gz</format>\r\n\t</formats>\r\n\t<includeBaseDirectory>false</includeBaseDirectory>\r\n\t<componentDescriptors>\r\n\t\t<componentDescriptor>src/main/assembly/component.xml</componentDescriptor>\r\n\t</componentDescriptors>\r\n</assembly>\r\n"
  },
  {
    "path": "node/deployer/src/main/assembly/release.xml",
    "content": "<assembly xmlns=\"http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\r\n\txsi:schemaLocation=\"http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd\">\r\n\t<id>release</id>\r\n\t<formats>\r\n\t\t<format>tar.gz</format>\r\n\t</formats>\r\n\t<includeBaseDirectory>false</includeBaseDirectory>\r\n\t<componentDescriptors>\r\n\t\t<componentDescriptor>src/main/assembly/component.xml</componentDescriptor>\r\n\t</componentDescriptors>\r\n</assembly>\r\n"
  },
  {
    "path": "node/deployer/src/main/bin/startup.bat",
    "content": "@echo off\n@if not \"%ECHO%\" == \"\"  echo %ECHO%\n@if \"%OS%\" == \"Windows_NT\"  setlocal\n\nset ENV_PATH=.\\\nif \"%OS%\" == \"Windows_NT\" set ENV_PATH=%~dp0%\n\nset conf_dir=%ENV_PATH%\\..\\conf\nset nid_file=%conf_dir%\\nid\nset /p nid=<\"%nid_file%\"\nset otter_conf=%conf_dir%\\otter.properties\nset logback_configurationFile=%conf_dir%\\logback.xml\n\nset CLASSPATH=%conf_dir%\nset CLASSPATH=%conf_dir%\\..\\lib\\*;%CLASSPATH%\n\nset JAVA_MEM_OPTS= -Xms128m -Xmx512m -XX:PermSize=128m\nset JAVA_OPTS_EXT= -Djava.awt.headless=true -Djava.net.preferIPv4Stack=true -Dapplication.codeset=UTF-8 -Dfile.encoding=UTF-8\nset JAVA_DEBUG_OPT= -server -Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,address=8099,server=y,suspend=n\nset OTTER_OPTS= -DappName=otter-node -Ddubbo.application.logger=slf4j -Dlogback.configurationFile=\"%logback_configurationFile%\" -Dnid=%nid%\n\nset JAVA_OPTS= %JAVA_MEM_OPTS% %JAVA_OPTS_EXT% %JAVA_DEBUG_OPT% %OTTER_OPTS%\n\nset CMD_STR= java %JAVA_OPTS% -classpath \"%CLASSPATH%\" com.alibaba.otter.node.deployer.OtterLauncher\necho start cmd : %CMD_STR%\n\njava %JAVA_OPTS% -classpath \"%CLASSPATH%\" com.alibaba.otter.node.deployer.OtterLauncher\n"
  },
  {
    "path": "node/deployer/src/main/bin/startup.sh",
    "content": "#!/bin/bash \n\ncurrent_path=`pwd`\ncase \"`uname`\" in\n    Linux)\n\t\tbin_abs_path=$(readlink -f $(dirname $0))\n\t\t;;\n\t*)\n\t\tbin_abs_path=`cd $(dirname $0); pwd`\n\t\t;;\nesac\nbase=${bin_abs_path}/..\notterNodeIdFile=$base/conf/nid\nlogback_configurationFile=$base/conf/logback.xml\nexport LANG=en_US.UTF-8\n\nif [ -f $base/bin/otter.pid ] ; then\n\techo \"found otter.pid , Please run stop.sh first ,then startup.sh\" 2>&2\n    exit 1\nfi\n\nif [ ! -d $base/logs/node ] ; then \n\tmkdir -p $base/logs/node\nfi\n\nif [ -z \"$ARIA2C\" ]; then\n  ARIA2C=$(which aria2c)\nfi\n\nif [ -z \"$ARIA2C\" ]; then\n\tsource $HOME/.bash_profile\n\tARIA2C=$(which aria2c)\n\t\n\tif [ -z \"$ARIA2C\" ]; then\n\t\techo \"Cannot find a aria2c. Please set in your PATH in .bash_profile.\" 2>&2\n\t\t#exit 1;\n\tfi\nfi\n\n## set java path\nif [ -z \"$JAVA\" ] ; then\n  JAVA=$(which java)\nfi\n\nALIBABA_JAVA=\"/usr/alibaba/java/bin/java\"\nTAOBAO_JAVA=\"/opt/taobao/java/bin/java\"\nif [ -z \"$JAVA\" ]; then\n  if [ -f $ALIBABA_JAVA ] ; then\n  \tJAVA=$ALIBABA_JAVA\n  elif [ -f $ALIBABA_JAVA ] ; then\n  \tJAVA=$TAOBAO_JAVA\n  else\n  \techo \"Cannot find a Java JDK. Please set either set JAVA or put java (>=1.5) in your PATH.\" 2>&2\n    exit 1\n  fi\nfi\n\ncase \"$#\" \nin\n0 ) \n\t;;\n1 )\t\n\tvar=$*\n\tif [ -d $var ] \n\tthen \n\t\totterNodeIdFile=$var\n        logback_configurationFile=$base/conf/logback.xml\n\telif [ -f $var ] ; then \n\t\totterNodeIdFile=$base/conf/nid\n        logback_configurationFile=$var\n\telse\n\t\techo \"THE PARAMETER IS NOT CORRECT.PLEASE CHECK AGAIN.\"\n        exit\n\tfi;;\n2 )\t\n\tvar1=$1\n\tvar2=$2\n\tif [ -d $var1 -a -f $var2 ] ; then\n\t\totterNodeIdFile=$var1\n\t\tlogback_configurationFile=$var2\n\telif [ -d $var2 -a -f $var1 ] ; then  \n\t\totterNodeIdFile=$var2\n\t\tlogback_configurationFile=$var1\n\telse \n\t\tif [ \"$1\" = \"debug\" ]; then\n\t\t\tDEBUG_PORT=$2\n\t\t\tDEBUG_SUSPEND=\"n\"\n\t\t\tJAVA_DEBUG_OPT=\"-Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,address=$DEBUG_PORT,server=y,suspend=$DEBUG_SUSPEND\"\n\t\tfi\n     fi;;\n* )\n\techo \"THE PARAMETERS MUST BE TWO OR LESS.PLEASE CHECK AGAIN.\"\n\texit;;\nesac\n\nstr=`file $JAVA | grep 64-bit`\nif [ -n \"$str\" ]; then\n\tJAVA_OPTS=\"-server -Xms2048m -Xmx3072m -Xmn1024m -XX:SurvivorRatio=2 -XX:PermSize=96m -XX:MaxPermSize=256m -Xss256k -XX:-UseAdaptiveSizePolicy -XX:MaxTenuringThreshold=15 -XX:+DisableExplicitGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:+UseFastAccessorMethods -XX:+UseCMSInitiatingOccupancyOnly -XX:+HeapDumpOnOutOfMemoryError\"\nelse\n\tJAVA_OPTS=\"-server -Xms1024m -Xmx1024m -XX:NewSize=256m -XX:MaxNewSize=256m -XX:MaxPermSize=128m \"\nfi\n\nJAVA_OPTS=\" $JAVA_OPTS -Djava.awt.headless=true -Djava.net.preferIPv4Stack=true -Dfile.encoding=UTF-8\"\nOTTER_OPTS=\"-DappName=otter-node -Ddubbo.application.logger=slf4j -Dlogback.configurationFile=$logback_configurationFile -Dnid=$(cat $otterNodeIdFile)\"\n\nif [ -e $otterNodeIdFile -a -e $logback_configurationFile ]\nthen \n\tfor i in $base/lib/*;\n\tdo CLASSPATH=$i:\"$CLASSPATH\";\n\tdone\n\tCLASSPATH=\"$base/conf:$CLASSPATH\";\n \n\techo LOG CONFIGURATION : $logback_configurationFile\n\techo Otter nodeId file : $otterNodeIdFile \n\techo CLASSPATH :$CLASSPATH\n\n  echo \"cd to $bin_abs_path for workaround relative path\"\n  cd $bin_abs_path\n\n\t$JAVA $JAVA_OPTS $JAVA_DEBUG_OPT $OTTER_OPTS -classpath .:$CLASSPATH com.alibaba.otter.node.deployer.OtterLauncher 1>>$base/logs/node/node.log 2>&1 &\n\techo $! > $base/bin/otter.pid \n\n  echo \"cd to $current_path for continue\"\n  cd $current_path\nelse \n\techo \"otterNodeIdFile file(\"$otterNodeIdFile\") OR log configration file($logback_configurationFile) is not exist,please create then first!\"\nfi\n"
  },
  {
    "path": "node/deployer/src/main/bin/stop.sh",
    "content": "#!/bin/bash\n\ncygwin=false;\nlinux=false;\ncase \"`uname`\" in\n    CYGWIN*)\n        cygwin=true\n        ;;\n    Linux*)\n    \tlinux=true\n    \t;;\nesac\n\nget_pid() {\t\n\tSTR=$1\n\tPID=$2\n    if $cygwin; then\n        JAVA_CMD=\"$JAVA_HOME\\bin\\java\"\n        JAVA_CMD=`cygpath --path --unix $JAVA_CMD`\n        JAVA_PID=`ps |grep $JAVA_CMD |awk '{print $1}'`\n    else\n        if $linux; then\n\t        if [ ! -z \"$PID\" ]; then\n\t        \tJAVA_PID=`ps -C java -f --width 1000|grep \"$STR\"|grep \"$PID\"|grep -v grep|awk '{print $2}'`\n\t\t    else \n\t\t        JAVA_PID=`ps -C java -f --width 1000|grep \"$STR\"|grep -v grep|awk '{print $2}'`\n\t        fi\n\t    else\n\t    \tif [ ! -z \"$PID\" ]; then\n\t        \tJAVA_PID=`ps aux |grep \"$STR\"|grep \"$PID\"|grep -v grep|awk '{print $2}'`\n\t\t    else \n\t\t        JAVA_PID=`ps aux |grep \"$STR\"|grep -v grep|awk '{print $2}'`\n\t        fi\n\t    fi\n    fi\n    echo $JAVA_PID;\n}\n\nbase=`dirname $0`/..\npidfile=$base/bin/otter.pid\nif [ ! -f \"$pidfile\" ];then\n\techo \"otter is not running. exists\"\n\texit\nfi\n\npid=`cat $pidfile`\nif [ \"$pid\" == \"\" ] ; then\n\tpid=`get_pid \"appName=otter-node\"`\nfi\n\necho -e \"`hostname`: stopping otter node $pid ... \"\nkill $pid\n\nLOOPS=0\nwhile (true); \ndo \n\tpid=`get_pid \"appName=otter-node\" \"$pid\"`\n    if [ \"$pid\" == \"\" ] ; then\n    \techo \"Oook! cost:$LOOPS\"\n    \t`rm $pidfile`\n    \tbreak;\n    fi\n    let LOOPS=LOOPS+1\n    sleep 1\ndone\n"
  },
  {
    "path": "node/deployer/src/main/java/com/alibaba/otter/node/deployer/OtterLauncher.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.deployer;\n\nimport org.apache.commons.lang.exception.ExceptionUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.alibaba.otter.node.etl.OtterContextLocator;\nimport com.alibaba.otter.node.etl.OtterController;\n\n/**\n * load otter task to sync data with some pipeline.\n * \n * @author xiaoqing.zhouxq 2011-8-29 上午10:02:04\n */\npublic class OtterLauncher {\n\n    private static final Logger logger = LoggerFactory.getLogger(OtterLauncher.class);\n\n    public static void main(String[] args) throws Throwable {\n        // 启动dragoon client\n        // startDragoon();\n        // logger.info(\"INFO ## the dragoon is start now ......\");\n        final OtterController controller = OtterContextLocator.getOtterController();\n        controller.start();\n        try {\n            logger.info(\"INFO ## the otter server is running now ......\");\n            Runtime.getRuntime().addShutdownHook(new Thread() {\n\n                public void run() {\n                    try {\n                        logger.info(\"INFO ## stop the otter server\");\n                        controller.stop();\n                    } catch (Throwable e) {\n                        logger.warn(\"WARN ##something goes wrong when stopping Otter Server:\\n{}\",\n                            ExceptionUtils.getFullStackTrace(e));\n                    } finally {\n                        logger.info(\"INFO ## otter server is down.\");\n                    }\n                }\n\n            });\n        } catch (Throwable e) {\n            logger.error(\"ERROR ## Something goes wrong when starting up the Otter Server:\\n{}\",\n                ExceptionUtils.getFullStackTrace(e));\n            System.exit(0);\n        }\n    }\n\n    // 启动dragoon client\n    // private static void startDragoon() {\n    // do nothing\n    // }\n}\n"
  },
  {
    "path": "node/deployer/src/main/resources/applicationContext.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<beans xmlns=\"http://www.springframework.org/schema/beans\"\r\n\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:tx=\"http://www.springframework.org/schema/tx\"\r\n\txmlns:aop=\"http://www.springframework.org/schema/aop\" xmlns:lang=\"http://www.springframework.org/schema/lang\"\r\n\txmlns:context=\"http://www.springframework.org/schema/context\"\r\n\txsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd\r\n           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd\r\n           http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-2.0.xsd\r\n           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd\r\n           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd\"\r\n\tdefault-autowire=\"byName\">\r\n\t\r\n\t<!-- properties -->\r\n\t<bean class=\"com.alibaba.otter.shared.common.utils.spring.PropertyPlaceholderConfigurer\" lazy-init=\"false\">\r\n\t\t<property name=\"ignoreResourceNotFound\" value=\"true\" />\r\n\t\t<property name=\"systemPropertiesModeName\" value=\"SYSTEM_PROPERTIES_MODE_OVERRIDE\"/><!-- 允许system覆盖 -->\r\n\t\t<property name=\"locations\">\r\n\t\t\t<list>\r\n\t\t\t\t<value>classpath:otter.properties</value>\r\n\t\t\t</list>\r\n\t\t</property>\r\n\t</bean>\r\n\t\r\n\t<import resource=\"classpath*:spring/otter-node-*.xml\"/>\r\n\t<import resource=\"classpath*:spring/otter-canal-*.xml\"/>\r\n\t<import resource=\"classpath*:spring/otter-arbitrate-*.xml\"/>\r\n\t<import resource=\"classpath*:spring/otter-push-*.xml\"/>\r\n</beans>"
  },
  {
    "path": "node/deployer/src/main/resources/logback.xml",
    "content": "<configuration scan=\"true\" scanPeriod=\" 5 seconds\">\r\n\r\n\t<jmxConfigurator />\r\n\t<appender name=\"STDOUT\" class=\"ch.qos.logback.core.ConsoleAppender\">\r\n\t\t<encoder>\r\n\t\t\t<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{56} - %msg%n\r\n\t\t\t</pattern>\r\n\t\t</encoder>\r\n\t</appender>\r\n\t\r\n\t<appender name=\"TASK-ROOT\" class=\"ch.qos.logback.classic.sift.SiftingAppender\">\r\n\t\t<discriminator>\r\n\t\t\t<Key>otter</Key>\r\n\t\t\t<DefaultValue>node</DefaultValue>\r\n\t\t</discriminator>\r\n\t\t<sift>\r\n\t\t\t<appender name=\"FILE-${otter}\"\r\n\t\t\t\tclass=\"ch.qos.logback.core.rolling.RollingFileAppender\">\r\n\t\t\t\t<File>../logs/${otter}/${otter}.log</File>\r\n\t\t\t\t<rollingPolicy\r\n\t\t\t\t\tclass=\"ch.qos.logback.core.rolling.TimeBasedRollingPolicy\">\r\n\t\t\t\t\t<!-- rollover daily -->\r\n\t\t\t\t\t<fileNamePattern>../logs/${otter}/%d{yyyy-MM-dd}/${otter}-%d{yyyy-MM-dd}-%i.log.gz</fileNamePattern>\r\n\t\t\t\t\t<timeBasedFileNamingAndTriggeringPolicy class=\"ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP\">\r\n\t\t\t\t\t\t<!-- or whenever the file size reaches 100MB -->\r\n\t\t\t\t\t\t<maxFileSize>30MB</maxFileSize>\r\n\t\t\t\t\t</timeBasedFileNamingAndTriggeringPolicy>\r\n\t\t\t\t\t<maxHistory>60</maxHistory>\r\n\t\t\t\t</rollingPolicy>\r\n\t\t\t\t<encoder>\r\n\t\t\t\t\t<pattern>\r\n\t\t\t\t\t\t%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{56} - %msg%n\r\n\t\t\t\t\t</pattern>\r\n\t\t\t\t</encoder>\r\n\t\t\t</appender>\r\n\t\t</sift>\r\n\t</appender>\r\n\t\r\n\t<appender name=\"DB_LOG\" class=\"ch.qos.logback.classic.sift.SiftingAppender\">\r\n\t\t<discriminator>\r\n\t\t\t<Key>load</Key>\r\n\t\t\t<DefaultValue>load</DefaultValue>\r\n\t\t</discriminator>\r\n\t\t<sift>\r\n\t\t\t<appender name=\"DB_LOG_${load}\"\r\n\t\t\t\tclass=\"ch.qos.logback.core.rolling.RollingFileAppender\">\r\n\t\t\t\t<File>../logs/${load}/row_load.log</File>\r\n\t\t\t\t<rollingPolicy\r\n\t\t\t\t\tclass=\"ch.qos.logback.core.rolling.TimeBasedRollingPolicy\">\r\n\t\t\t\t\t<!-- rollover daily -->\r\n\t\t\t\t\t<fileNamePattern>../logs/${load}/%d{yyyy-MM-dd}/row_load-%d{yyyy-MM-dd}-%i.log.gz</fileNamePattern>\r\n\t\t\t\t\t<timeBasedFileNamingAndTriggeringPolicy class=\"ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP\">\r\n\t\t\t\t\t\t<maxFileSize>512MB</maxFileSize>\r\n\t\t\t\t\t</timeBasedFileNamingAndTriggeringPolicy>\r\n\t\t\t\t\t<maxHistory>60</maxHistory>\r\n\t\t\t\t</rollingPolicy>\r\n\t\t\t\t<encoder>\r\n\t\t\t\t\t<pattern>%msg</pattern>\r\n\t\t\t\t</encoder>\r\n\t\t\t</appender>\r\n\t\t</sift>\r\n\t</appender>\r\n\t\r\n\t<appender name=\"FILE_LOG\" class=\"ch.qos.logback.classic.sift.SiftingAppender\">\r\n\t\t<discriminator>\r\n\t\t\t<Key>load</Key>\r\n\t\t\t<DefaultValue>load</DefaultValue>\r\n\t\t</discriminator>\r\n\t\t<sift>\r\n\t\t\t<appender name=\"FILE_LOG_${load}\"\r\n\t\t\t\tclass=\"ch.qos.logback.core.rolling.RollingFileAppender\">\r\n\t\t\t\t<File>../logs/${load}/file_load.log</File>\r\n\t\t\t\t<rollingPolicy\r\n\t\t\t\t\tclass=\"ch.qos.logback.core.rolling.TimeBasedRollingPolicy\">\r\n\t\t\t\t\t<!-- rollover daily -->\r\n\t\t\t\t\t<fileNamePattern>../logs/${load}/%d{yyyy-MM-dd}/file_load-%d{yyyy-MM-dd}-%i.log.gz</fileNamePattern>\r\n\t\t\t\t\t<timeBasedFileNamingAndTriggeringPolicy class=\"ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP\">\r\n\t\t\t\t\t\t<maxFileSize>512MB</maxFileSize>\r\n\t\t\t\t\t</timeBasedFileNamingAndTriggeringPolicy>\r\n\t\t\t\t\t<maxHistory>60</maxHistory>\r\n\t\t\t\t</rollingPolicy>\r\n\t\t\t\t<encoder>\r\n\t\t\t\t\t<pattern>%msg</pattern>\r\n\t\t\t\t</encoder>\r\n\t\t\t</appender>\r\n\t\t</sift>\r\n\t</appender>\r\n\t\r\n\t<appender name=\"FILE_MISS_LOG\" class=\"ch.qos.logback.classic.sift.SiftingAppender\">\r\n\t\t<discriminator>\r\n\t\t\t<Key>load</Key>\r\n\t\t\t<DefaultValue>load</DefaultValue>\r\n\t\t</discriminator>\r\n\t\t<sift>\r\n\t\t\t<appender name=\"FILE_MISS_LOG_${load}\"\r\n\t\t\t\tclass=\"ch.qos.logback.core.rolling.RollingFileAppender\">\r\n\t\t\t\t<File>../logs/${load}/file_miss.log</File>\r\n\t\t\t\t<rollingPolicy\r\n\t\t\t\t\tclass=\"ch.qos.logback.core.rolling.TimeBasedRollingPolicy\">\r\n\t\t\t\t\t<!-- rollover daily -->\r\n\t\t\t\t\t<fileNamePattern>../logs/${load}/%d{yyyy-MM-dd}/file_miss-%d{yyyy-MM-dd}-%i.log.gz</fileNamePattern>\r\n\t\t\t\t\t<timeBasedFileNamingAndTriggeringPolicy class=\"ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP\">\r\n\t\t\t\t\t\t<maxFileSize>512MB</maxFileSize>\r\n\t\t\t\t\t</timeBasedFileNamingAndTriggeringPolicy>\r\n\t\t\t\t\t<maxHistory>60</maxHistory>\r\n\t\t\t\t</rollingPolicy>\r\n\t\t\t\t<encoder>\r\n\t\t\t\t\t<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %n %msg %n</pattern>\r\n\t\t\t\t</encoder>\r\n\t\t\t</appender>\r\n\t\t</sift>\r\n\t</appender>\r\n\t\r\n\t<appender name=\"SELECTOR_LOG\" class=\"ch.qos.logback.classic.sift.SiftingAppender\">\r\n\t\t<discriminator>\r\n\t\t\t<Key>select</Key>\r\n\t\t\t<DefaultValue>select</DefaultValue>\r\n\t\t</discriminator>\r\n\t\t<sift>\r\n\t\t\t<appender name=\"SELECTOR_LOG_${select}\"\r\n\t\t\t\tclass=\"ch.qos.logback.core.rolling.RollingFileAppender\">\r\n\t\t\t\t<File>../logs/${select}/row_select.log</File>\r\n\t\t\t\t<rollingPolicy\r\n\t\t\t\t\tclass=\"ch.qos.logback.core.rolling.TimeBasedRollingPolicy\">\r\n\t\t\t\t\t<!-- rollover daily -->\r\n\t\t\t\t\t<fileNamePattern>../logs/${select}/%d{yyyy-MM-dd}/row_select-%d{yyyy-MM-dd}-%i.log.gz</fileNamePattern>\r\n\t\t\t\t\t<timeBasedFileNamingAndTriggeringPolicy class=\"ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP\">\r\n\t\t\t\t\t\t<maxFileSize>512MB</maxFileSize>\r\n\t\t\t\t\t</timeBasedFileNamingAndTriggeringPolicy>\r\n\t\t\t\t\t<maxHistory>60</maxHistory>\r\n\t\t\t\t</rollingPolicy>\r\n\t\t\t\t<encoder>\r\n\t\t\t\t\t<pattern>%msg</pattern>\r\n\t\t\t\t</encoder>\r\n\t\t\t</appender>\r\n\t\t</sift>\r\n\t</appender>\r\n\t\r\n\t<appender name=\"FILE_WAIT\" class=\"ch.qos.logback.classic.sift.SiftingAppender\">\r\n\t\t<discriminator>\r\n\t\t\t<Key>otter</Key>\r\n\t\t\t<DefaultValue>otter</DefaultValue>\r\n\t\t</discriminator>\r\n\t\t<sift>\r\n\t\t\t<appender name=\"FILE_WAIT_LOG_${otter}\"\r\n\t\t\t\tclass=\"ch.qos.logback.core.rolling.RollingFileAppender\">\r\n\t\t\t\t<File>../logs/${otter}/wait.log</File>\r\n\t\t\t\t<rollingPolicy\r\n\t\t\t\t\tclass=\"ch.qos.logback.core.rolling.TimeBasedRollingPolicy\">\r\n\t\t\t\t\t<!-- rollover daily -->\r\n\t\t\t\t\t<fileNamePattern>../logs/${otter}/%d{yyyy-MM-dd}/wait-%d{yyyy-MM-dd}-%i.log.gz</fileNamePattern>\r\n\t\t\t\t\t<timeBasedFileNamingAndTriggeringPolicy class=\"ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP\">\r\n\t\t\t\t\t\t<maxFileSize>512MB</maxFileSize>\r\n\t\t\t\t\t</timeBasedFileNamingAndTriggeringPolicy>\r\n\t\t\t\t\t<maxHistory>60</maxHistory>\r\n\t\t\t\t</rollingPolicy>\r\n\t\t\t\t<encoder>\r\n\t\t\t\t\t<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %msg%n</pattern>\r\n\t\t\t\t</encoder>\r\n\t\t\t</appender>\r\n\t\t</sift>\r\n\t</appender>\r\n\t\r\n\t<logger name=\"com.alibaba.otter.node.etl.load.loader.db.interceptor.log.LogLoadInterceptor\" additivity=\"false\">  \r\n        <level value=\"INFO\" />  \r\n        <appender-ref ref=\"DB_LOG\" />\r\n    </logger>\r\n    <logger name=\"com.alibaba.otter.node.etl.load.loader.db.FileLoadAction\" additivity=\"false\">  \r\n        <level value=\"INFO\" />  \r\n        <appender-ref ref=\"FILE_LOG\" />\r\n    </logger>\r\n    <logger name=\"com.alibaba.otter.node.etl.select.selector.canal.CanalEmbedSelector\" additivity=\"false\">  \r\n        <level value=\"INFO\" />  \r\n        <appender-ref ref=\"SELECTOR_LOG\" />\r\n    </logger>\r\n    <logger name=\"com.alibaba.otter.node.etl.common.pipe.impl.http.AttachmentHttpPipe\" additivity=\"false\">  \r\n        <level value=\"INFO\" />  \r\n        <appender-ref ref=\"FILE_MISS_LOG\" />\r\n    </logger>\r\n    <logger name=\"com.alibaba.otter.node.etl.conflict.impl.FileBatchConflictDetectServiceImpl\" additivity=\"false\">  \r\n        <level value=\"INFO\" />  \r\n        <appender-ref ref=\"FILE_MISS_LOG\" />\r\n    </logger>\r\n    <logger name=\"com.alibaba.otter.node.deployer.OtterLauncher\" additivity=\"false\">  \r\n        <level value=\"INFO\" />  \r\n        <appender-ref ref=\"TASK-ROOT\" />\r\n    </logger>\r\n    <logger name=\"com.alibaba.dubbo.rpc.support.RpcUtils\" additivity=\"false\">  \r\n     \t<level value=\"error\" />  \r\n        <appender-ref ref=\"TASK-ROOT\" />\r\n    </logger>\r\n    <!-- \r\n    <logger name=\"com.alibaba.otter.shared.common.utils.thread.ExecutorTemplate\" additivity=\"false\">  \r\n        <level value=\"INFO\" />  \r\n        <appender-ref ref=\"FILE_WAIT\" />\r\n    </logger>\r\n    <logger name=\"com.alibaba.otter.canal.server.embeded.CanalServerWithEmbeded\" additivity=\"false\">  \r\n        <level value=\"INFO\" />  \r\n        <appender-ref ref=\"TASK-ROOT\" />\r\n    </logger>\r\n    <logger name=\"com.alibaba.otter.node.etl.select.SelectTask\" additivity=\"false\">  \r\n        <level value=\"INFO\" />  \r\n        <appender-ref ref=\"TASK-ROOT\" />\r\n    </logger>\r\n     -->\r\n     \r\n\t<root level=\"WARN\">\r\n\t\t<!--  \r\n\t\t<appender-ref ref=\"STDOUT\"/>\r\n\t\t-->\r\n\t\t<appender-ref ref=\"TASK-ROOT\" />\r\n\t</root>\r\n</configuration>"
  },
  {
    "path": "node/deployer/src/main/resources/otter.properties",
    "content": "# otter node root dir\notter.nodeHome = ${user.dir}/../\n\n## otter node dir\notter.htdocs.dir = ${otter.nodeHome}/htdocs\notter.download.dir = ${otter.nodeHome}/download\notter.extend.dir= ${otter.nodeHome}/extend\n\n## default zookeeper sesstion timeout = 60s\notter.zookeeper.sessionTimeout = 60000\n\n## otter communication payload size (default = 8388608)\notter.communication.payload = 8388608\n\n## otter communication pool size\notter.communication.pool.size = 10\n\n## otter arbitrate & node connect manager config\notter.manager.address = 127.0.0.1:1099\n"
  },
  {
    "path": "node/deployer/src/main/resources/sql/otter-system-ddl-mysql.sql",
    "content": "/*\n供 otter 使用， otter 需要对 retl.* 的读写权限，以及对业务表的读写权限\n1. 创建database retl\n*/\nCREATE DATABASE retl;\n\n/* 2. 用户授权 给同步用户授权 */\nCREATE USER retl@'%' IDENTIFIED BY 'retl';\nGRANT USAGE ON *.* TO `retl`@'%';\nGRANT SELECT, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO `retl`@'%';\nGRANT SELECT, INSERT, UPDATE, DELETE, EXECUTE ON `retl`.* TO `retl`@'%';\n/* 业务表授权，这里可以限定只授权同步业务的表 */\nGRANT SELECT, INSERT, UPDATE, DELETE ON *.* TO `retl`@'%';  \n\n/* 3. 创建系统表 */\nUSE retl;\nDROP TABLE IF EXISTS retl.retl_buffer;\nDROP TABLE IF EXISTS retl.retl_mark;\nDROP TABLE IF EXISTS retl.xdual;\n\nCREATE TABLE retl_buffer\n(\t\n\tID BIGINT(20) AUTO_INCREMENT,\n\tTABLE_ID INT(11) NOT NULL,\n\tFULL_NAME varchar(512),\n\tTYPE CHAR(1) NOT NULL,\n\tPK_DATA VARCHAR(256) NOT NULL,\n\tGMT_CREATE TIMESTAMP NOT NULL,\n\tGMT_MODIFIED TIMESTAMP NOT NULL,\n\tCONSTRAINT RETL_BUFFER_ID PRIMARY KEY (ID) \n)  ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\nCREATE TABLE retl_mark\n(\t\n\tID BIGINT AUTO_INCREMENT,\n\tCHANNEL_ID INT(11),\n\tCHANNEL_INFO varchar(128),\n\tCONSTRAINT RETL_MARK_ID PRIMARY KEY (ID) \n) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;\n\nCREATE TABLE xdual (\n  ID BIGINT(20) NOT NULL AUTO_INCREMENT,\n  X timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  PRIMARY KEY (ID)\n) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;\n\n/* 4. 插入初始化数据 */\nINSERT INTO retl.xdual(id, x) VALUES (1,now()) ON DUPLICATE KEY UPDATE x = now();\n"
  },
  {
    "path": "node/deployer/src/main/resources/sql/otter-system-ddl-oracle.sql",
    "content": "DROP TABLE RETL.RETL_BUFFER;\nCREATE TABLE RETL.RETL_BUFFER \n(\t\n\tID NUMBER(11) NOT NULL,\n\tTABLE_ID NUMBER(11) NOT NULL,\n\tFULL_NAME varchar(512),\n\tTYPE CHAR(1) NOT NULL,\n\tPK_DATA VARCHAR(256) NOT NULL,\n\tGMT_CREATE DATE NOT NULL,\n\tGMT_MODIFIED DATE NOT NULL,\n\tCONSTRAINT RETL_BUFFER_ID PRIMARY KEY (ID) \n);\n\nCREATE SEQUENCE RETL.SEQ_RETL_BUFFER MINVALUE 1 MAXVALUE 1.00000000000000E+27 INCREMENT BY 1 START WITH 1 CACHE 100 NOORDER NOCYCLE; \n\nDROP TABLE RETL.RETL_MARK;\nCREATE TABLE RETL.RETL_MARK\n(\t\n\tID NUMBER(11) NOT NULL,\n\tCHANNEL_INFO varchar(128),\n\tCHANNEL_ID NUMBER(11),\n\tCONSTRAINT RETL_MARK_ID PRIMARY KEY (ID) \n);\nCREATE SEQUENCE RETL.SEQ_RETL_MARK MINVALUE 1 MAXVALUE 1.00000000000000E+27 INCREMENT BY 1 START WITH 1 CACHE 100 NOORDER NOCYCLE;\n\nDROP TABLE RETL.XDUAL;\nCREATE TABLE RETL.XDUAL (\n  ID int(11) NOT NULL,\n  X DATE NOT NULL,\n  CONSTRAINT XDUAL_ID PRIMARY KEY (ID) \n);\nCREATE SEQUENCE RETL.SEQ_XDUAL MINVALUE 1 MAXVALUE 1.00000000000000E+27 INCREMENT BY 1 START WITH 1 CACHE 100 NOORDER NOCYCLE;\n\nCREATE TABLE IF NOT EXIST RETL.RETL_CLIENT;\n( \n\tID NUMBER, \n\tCLIENT_INFO varchar(64), \n\tCLIENT_IDENTIFIER varchar(64), \n\tCONSTRAINT RETL_CLIENT_ID PRIMARY KEY (ID)\n);\nCREATE SEQUENCE RETL.SEQ_RETL_CLIENT MINVALUE 1 MAXVALUE 1.00000000000000E+27 INCREMENT BY 1 START WITH 1 CACHE 100 NOORDER NOCYCLE;\n\n/* 插入初始化数据 */\nmerge /*+ use_nl(a b)*/ into XDUAL a using (select 1 as id , sysdate as x from retl.xdual) b on (a.id=b.id) when matched then update set a.x = b.x when not matched then insert (a.id , a.x) values (b.id , b.x)"
  },
  {
    "path": "node/deployer/src/test/java/com/alibaba/otter/node/deployer/OtterLauncherIntegration.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.deployer;\n\nimport java.util.concurrent.CountDownLatch;\n\nimport com.alibaba.otter.node.etl.OtterContextLocator;\n\n/**\n * 集成测试\n * \n * @author jianghang 2011-10-8 下午06:25:52\n * @version 4.0.0\n */\npublic class OtterLauncherIntegration {\n\n    public static void main(String args[]) throws Throwable {\n        final CountDownLatch latch = new CountDownLatch(1);\n        Thread mainstem = new Thread() {\n\n            @Override\n            public void run() {\n                try {\n                    Thread.sleep(60 * 1000 * 1000);\n                } catch (InterruptedException e) {\n                }\n                System.out.println(\"!!!!!!!!!!!!!!!!!!   start single\");\n                OtterContextLocator.getOtterController();\n                // MainStemArbitrateEvent mainStemEvent =\n                // OtterContextLocator.getArbitrateEventService().mainStemEvent();\n                // // 启动\n                // MainStemEventData eventData = new MainStemEventData();\n                // eventData.setPipelineId(1L);\n                // eventData.setStatus(MainStemEventData.Status.OVERTAKE);\n                // mainStemEvent.single(eventData);\n                // System.out.println(\"!!!!!!!!!!!!!!!!!!  end single\");\n                latch.countDown();\n            }\n\n        };\n        mainstem.start();\n        System.setProperty(\"nid\", \"2\");\n        OtterLauncher.main(null);\n        latch.await();\n    }\n}\n"
  },
  {
    "path": "node/etl/pom.xml",
    "content": "<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\">\n\t<modelVersion>4.0.0</modelVersion>\n\t<parent>\n\t\t<groupId>com.alibaba.otter</groupId>\n\t\t<artifactId>node</artifactId>\n\t\t<version>4.2.19-SNAPSHOT</version>\n\t\t<relativePath>../pom.xml</relativePath>\n\t</parent>\n\t<groupId>com.alibaba.otter</groupId>\n\t<artifactId>node.etl</artifactId>\n\t<packaging>jar</packaging>\n\t<name>etl module for otter</name>\n\t<url>http://github.com/alibaba/otter</url>\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>com.alibaba.otter</groupId>\n\t\t\t<artifactId>shared.push</artifactId>\n\t\t\t<version>${project.version}</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>com.alibaba.otter</groupId>\n\t\t\t<artifactId>shared.arbitrate</artifactId>\n\t\t\t<version>${project.version}</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>com.alibaba.otter</groupId>\n\t\t\t<artifactId>shared.etl</artifactId>\n\t\t\t<version>${project.version}</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>com.alibaba.otter</groupId>\n\t\t\t<artifactId>node.common</artifactId>\n\t\t\t<version>${project.version}</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>com.alibaba.otter</groupId>\n\t\t\t<artifactId>node.canal</artifactId>\n\t\t\t<version>${project.version}</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>com.google.protobuf</groupId>\n\t\t\t<artifactId>protobuf-java</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>com.alibaba</groupId>\n\t\t\t<artifactId>druid</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>com.alibaba.fastsql</groupId>\n\t\t\t<artifactId>fastsql</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>mysql</groupId>\n\t\t\t<artifactId>mysql-connector-java</artifactId>\n\t\t</dependency>\n\t\t<!-- oracle -->\n\t\t<dependency>\n\t\t\t<groupId>com.oracle</groupId>\n\t\t\t<artifactId>ojdbc6</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.apache.commons</groupId>\n\t\t\t<artifactId>commons-compress</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>de.schlichtherle</groupId>\n\t\t\t<artifactId>truezip</artifactId>\n\t\t</dependency>\n\t\t\n\t\t<!-- jetty -->\n\t\t<dependency>\n\t\t  <groupId>org.eclipse.jetty</groupId>\n\t\t  <artifactId>jetty-servlet</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t  <groupId>org.eclipse.jetty</groupId>\n\t\t  <artifactId>jetty-xml</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t  <groupId>org.eclipse.jetty</groupId>\n\t\t  <artifactId>jetty-server</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t  <groupId>org.eclipse.jetty</groupId>\n\t\t  <artifactId>jetty-http</artifactId>\n\t\t</dependency>\n\t\t<!-- test dependency -->\n\t\t<dependency>\n\t\t\t<groupId>org.jtester</groupId>\n\t\t\t<artifactId>jtester</artifactId>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>junit</groupId>\n\t\t\t<artifactId>junit</artifactId>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\t</dependencies>\n</project>\n"
  },
  {
    "path": "node/etl/src/main/java/Batch.proto",
    "content": "package com.alibaba.otter.node.etl.model.protobuf;\r\n\r\noption java_outer_classname = \"BatchProto\";\r\noption optimize_for = SPEED;\r\n\r\n/**同步数据表示对象**/\r\nmessage Identity {\r\n    /**通道标示**/\r\n    optional int64   \t\tchannelId  \t\t\t\t= 1;\r\n    optional int64   \t\tpipelineId\t\t\t\t= 2;\r\n    optional int64   \t\tprocessId \t\t\t\t= 3;\r\n}\r\n\r\n/**数据包**/\r\nmessage RowBatch {\r\n    optional Identity   \tidentity \t\t\t\t= 1;\r\n    /**每个batch里面的所有变更数据**/\r\n    repeated RowData \t\trows\t  \t\t\t\t= 2;\r\n}\r\n\r\nmessage FileBatch {\r\n    optional Identity   \tidentity \t\t\t\t= 1;\r\n    /**每个batch里面变更数据所关联的文件信息**/\r\n    repeated FileData \t\tfiles\t \t\t\t\t= 2;\r\n}\r\n\r\n/**数据**/\r\nmessage RowData {\r\n\toptional int64 \t\t\ttableId\t\t\t\t\t= 1;\r\n\t\r\n\toptional string\t\t\tschemaName\t\t\t\t= 2;\r\n\t\r\n\toptional string\t\t\ttableName\t\t\t\t= 3;\r\n\t\r\n\t/**变更数据的操作类型(I/U/D)**/\r\n    optional string\t\t \teventType\t\t\t\t= 4;\r\n    \r\n    /**变更前的主键，可能是复合主键**/\r\n    repeated Column \t\toldKeys\t\t\t\t\t= 5;\r\n    \r\n    /**变更后的主键，可能是复合主键**/\r\n    repeated Column\t \t\tkeys\t   \t\t\t\t= 6;\r\n    \r\n    /**变更数据的每列变更信息,不包含主键**/\r\n    repeated Column\t \t\tcolumns   \t\t\t\t= 7;\r\n    \r\n    /**变更数据的业务时间**/\r\n    optional int64        \texecuteTime\t\t\t\t= 8;\r\n    /**映射规则id**/\r\n    optional int64 \t\t\tpairId\t\t\t\t\t= 9;\r\n    /**同步模式(R/F)**/\r\n    optional string\t\t\tsyncMode\t\t\t\t= 10;\r\n    /**同步一致性(B/S/M) **/\r\n    optional string\t\t\tsyncConsistency\t\t\t= 11;\r\n    /** eventsize **/\r\n    optional int64\t\t\tsize\t\t\t\t\t= 12;\r\n    /** isRemedy **/\r\n    optional bool\t\t\tremedy\t\t\t\t\t= 13;\r\n    /** dml/ddl sql **/\r\n    optional string\t\t\tsql\t\t\t\t\t\t= 14;\r\n    /** current ddl schemaName **/\r\n    optional string\t\t\tddlSchemaName\t\t\t= 15;\r\n    /** dml/ddl sql **/\r\n    optional string\t\t\thint\t\t\t\t\t= 16;\r\n    /** without schema **/\r\n    optional bool\t\t\twithoutSchema\t\t\t= 17;\r\n}\r\n\r\nmessage Column {\r\n\t/**列下标**/\r\n\toptional int32          index\t\t\t\t\t= 1;\r\n\t/**列名**/\r\n\toptional string\t\t\tname\t\t\t\t\t= 2;\r\n\t/**列值,timestamp,Datetime是一个long型的数字**/\r\n\toptional string\t\t\tvalue\t\t\t\t\t= 3;\r\n\t/**当前列是否是主键**/\r\n\toptional bool\t\t\tisPrimaryKey\t\t\t= 4;\r\n\t/**当前列是否可以为空**/\r\n\toptional bool\t\t\tisNull\t\t\t\t\t= 5;\r\n\t/**当前列的数据类型**/\r\n\toptional int32\t\t\ttype\t\t\t\t\t= 6;\r\n\t/**当前列是否发生真实变更**/\r\n\toptional bool\t\t\tisUpdate\t\t\t\t= 7;\r\n}\r\n/**文件对象**/\r\nmessage FileData {\r\n    /**变更数据的操作类型(I/U/D/C/A/E)**/\r\n    optional string\t\t \teventType\t\t\t\t= 1;\r\n    /**Aranda中特有的**/\r\n    optional string   \t\tnamespace  \t\t\t\t= 2;\r\n    /**文件的路径**/\r\n    optional string   \t\tpath\t  \t\t\t\t= 3;\r\n    /**文件最后一次修改时间**/\r\n    optional int64   \t\tlastModifiedTime\t\t= 4;\r\n    /**文件的大小**/\r\n    optional int64   \t\tsize\t\t\t\t\t= 5;\r\n    \r\n    optional int64\t\t\ttableId\t\t\t\t\t= 6;\r\n    /**映射规则id**/\r\n    optional int64 \t\t\tpairId\t\t\t\t\t= 9;\r\n}\r\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/OtterConstants.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl;\n\n/**\n * otter 常量定义\n * \n * @author jianghang 2012-4-21 下午04:20:18\n * @version 4.0.2\n */\npublic interface OtterConstants {\n\n    public String NID_NAME                      = \"nid\";\n\n    /**\n     * 在logback的配置文件中定义好的按照各个pipeline进行日志文件输出的键值.\n     */\n    public String splitPipelineLogFileKey       = \"otter\";\n\n    /**\n     * 在logback的配置文件中定义好的按照各个pipeline在load时，归档输出的键值.\n     */\n    public String splitPipelineLoadLogFileKey   = \"load\";\n\n    /**\n     * 在logback的配置文件中定义好的按照各个pipeline在select时，归档输出的键值.\n     */\n    public String splitPipelineSelectLogFileKey = \"select\";\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/OtterContextLocator.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl;\n\nimport org.springframework.beans.factory.config.AutowireCapableBeanFactory;\nimport org.springframework.beans.factory.support.DefaultListableBeanFactory;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.support.ClassPathXmlApplicationContext;\n\nimport com.alibaba.otter.shared.common.model.config.ConfigException;\n\n/**\n * Comment of OtterServiceLocator\n * \n * @author xiaoqing.zhouxq\n * @author zebin.xuzb 重写 customizeBeanFactory，防止重复id\n */\npublic class OtterContextLocator {\n\n    private static ClassPathXmlApplicationContext context       = null;\n    private static RuntimeException               initException = null;\n\n    static {\n        try {\n            context = new ClassPathXmlApplicationContext(\"applicationContext.xml\") {\n\n                @Override\n                protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {\n                    super.customizeBeanFactory(beanFactory);\n                    beanFactory.setAllowBeanDefinitionOverriding(false);\n                }\n            };\n        } catch (RuntimeException e) {\n            throw new ConfigException(\"ERROR ## \", e);\n        }\n    }\n\n    private static ApplicationContext getApplicationContext() {\n        if (context == null) {\n            throw initException;\n        }\n\n        return context;\n    }\n\n    public static void close() {\n        ((ClassPathXmlApplicationContext) context).close();\n    }\n\n    public static OtterController getOtterController() {\n        return (OtterController) getApplicationContext().getBean(\"otterController\");\n    }\n\n    public static <T> T getBean(String name) {\n        return (T) getApplicationContext().getBean(name);\n    }\n\n    /**\n     * 根据当前spring容器的bean定义，解析对应的object并完成注入\n     */\n    public static void autowire(Object obj) {\n        // 重新注入一下对象\n        context.getAutowireCapableBeanFactory().autowireBeanProperties(obj,\n                                                                       AutowireCapableBeanFactory.AUTOWIRE_BY_NAME,\n                                                                       false);\n    }\n\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/OtterController.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl;\n\nimport java.lang.management.ManagementFactory;\nimport java.lang.management.MemoryUsage;\nimport java.lang.management.OperatingSystemMXBean;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.ThreadPoolExecutor;\n\nimport org.apache.commons.lang.StringUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.alibaba.otter.node.common.config.ConfigClientService;\nimport com.alibaba.otter.node.common.config.NodeTaskListener;\nimport com.alibaba.otter.node.common.config.NodeTaskService;\nimport com.alibaba.otter.node.common.config.model.NodeTask;\nimport com.alibaba.otter.node.common.config.model.NodeTask.TaskEvent;\nimport com.alibaba.otter.node.etl.common.datasource.DataSourceService;\nimport com.alibaba.otter.node.etl.common.db.dialect.DbDialectFactory;\nimport com.alibaba.otter.node.etl.common.jmx.StageAggregationCollector;\nimport com.alibaba.otter.node.etl.common.task.GlobalTask;\nimport com.alibaba.otter.node.etl.extract.ExtractTask;\nimport com.alibaba.otter.node.etl.load.LoadTask;\nimport com.alibaba.otter.node.etl.select.SelectTask;\nimport com.alibaba.otter.node.etl.transform.TransformTask;\nimport com.alibaba.otter.shared.arbitrate.ArbitrateEventService;\nimport com.alibaba.otter.shared.arbitrate.ArbitrateManageService;\nimport com.alibaba.otter.shared.arbitrate.impl.manage.NodeSessionExpired;\nimport com.alibaba.otter.shared.arbitrate.impl.zookeeper.ZooKeeperClient;\nimport com.alibaba.otter.shared.common.model.config.ConfigException;\nimport com.alibaba.otter.shared.common.model.config.enums.StageType;\nimport com.alibaba.otter.shared.common.model.config.node.Node;\nimport com.alibaba.otter.shared.common.model.config.pipeline.Pipeline;\nimport com.alibaba.otter.shared.common.utils.AddressUtils;\nimport com.alibaba.otter.shared.common.utils.JsonUtils;\nimport com.alibaba.otter.shared.common.utils.version.VersionInfo;\nimport com.google.common.base.Function;\nimport com.google.common.collect.MapMaker;\nimport com.google.common.collect.OtterMigrateMap;\n\n/**\n * 管理和维护对应node机器内的S.E.T.L任务，实时接收manager推送的NodeTask调度信息，可查看 {@linkplain NodeTaskService}\n * \n * @author jianghang 2012-4-21 下午04:48:12\n * @version 4.0.2\n */\npublic class OtterController implements NodeTaskListener, OtterControllerMBean {\n\n    private static final Logger                   logger      = LoggerFactory.getLogger(OtterController.class);\n\n    // 第一层为pipelineId，第二层为S.E.T.L模块\n    private Map<Long, Map<StageType, GlobalTask>> controllers = OtterMigrateMap.makeComputingMap(new Function<Long, Map<StageType, GlobalTask>>() {\n\n                                                                  public Map<StageType, GlobalTask> apply(Long pipelineId) {\n                                                                      return new MapMaker().makeMap();\n                                                                  }\n                                                              });\n    private ConfigClientService                   configClientService;\n    private ArbitrateManageService                arbitrateManageService;\n    private NodeTaskService                       nodeTaskService;\n    // 各种资源管理\n    private DataSourceService                     dataSourceService;                                           // 连接池资源\n    private DbDialectFactory                      dbDialectFactory;                                            // 数据库信息资源\n    private ArbitrateEventService                 arbitrateEventService;                                       // 仲裁器资源\n    private ExecutorService                       executorService;\n\n    private StageAggregationCollector             stageAggregationCollector;\n\n    public void start() throws Throwable {\n        // 初始化节点\n        initNid();\n        nodeTaskService.addListener(this); // 将自己添加为NodeTask响应者\n    }\n\n    public void stop() throws Throwable {\n        for (Map<StageType, GlobalTask> tasks : controllers.values()) {\n            for (GlobalTask task : tasks.values()) {\n                try {\n                    task.shutdown();\n                } catch (Exception e) {\n                    logger.error(\"##shutdown task error!\", e);\n                }\n            }\n        }\n\n        try {\n            Long nid = configClientService.currentNode().getId();\n            arbitrateManageService.nodeEvent().destory(Long.valueOf(nid));\n        } catch (Exception e) {\n            logger.error(\"##destory node error!\", e);\n        }\n\n        try {\n            arbitrateEventService.toolEvent().release();\n        } catch (Exception e) {\n            logger.error(\"##destory arbitrate error!\", e);\n        }\n\n        try {\n            nodeTaskService.stopNode(); // 通知manager停止当前node\n        } catch (Exception e) {\n            logger.error(\"##stop node error!\", e);\n        }\n\n        try {\n            OtterContextLocator.close();\n        } catch (Exception e) {\n            logger.error(\"##cloes spring error!\", e);\n        }\n\n        ZooKeeperClient.destory();// 关闭zookeeper\n    }\n\n    public boolean process(List<NodeTask> nodeTasks) {\n        if (nodeTasks == null || nodeTasks.isEmpty()) {\n            return true;\n        }\n\n        for (NodeTask nodeTask : nodeTasks) {\n            boolean shutdown = nodeTask.isShutdown();\n            Long pipelineId = nodeTask.getPipeline().getId();\n            if (shutdown) {\n                Map<StageType, GlobalTask> tasks = controllers.remove(pipelineId);\n                if (tasks != null) {\n                    logger.info(\"INFO ## shutdown this pipeline sync ,the pipelineId = {} and tasks = {}\", pipelineId,\n                                tasks.keySet());\n                    stopPipeline(pipelineId, tasks);\n                } else {\n                    logger.info(\"INFO ## this pipeline id = {} is not start sync\", pipelineId);\n                }\n            } else {\n                startPipeline(nodeTask);\n            }\n        }\n\n        return true;\n    }\n\n    // ===================== helper method ======================\n\n    public void startPipeline(NodeTask nodeTask) {\n        Long pipelineId = nodeTask.getPipeline().getId();\n        releasePipeline(pipelineId);\n        Map<StageType, GlobalTask> tasks = controllers.get(pipelineId);\n        // 处理具体的任务命令\n        List<StageType> stage = nodeTask.getStage();\n        List<TaskEvent> event = nodeTask.getEvent();\n        for (int i = 0; i < stage.size(); i++) {\n            StageType stageType = stage.get(i);\n            TaskEvent taskEvent = event.get(i);\n            if (taskEvent.isCreate()) {\n                startTask(nodeTask.getPipeline(), tasks, stageType);\n            } else {\n                stopTask(tasks, stageType);\n            }\n        }\n    }\n\n    private void startTask(Pipeline pipeline, Map<StageType, GlobalTask> tasks, StageType taskType) {\n        if (tasks.get(taskType) != null && tasks.get(taskType).isAlive()) {\n            logger.warn(\"WARN ## this task = {} has started\", taskType);\n        }\n\n        GlobalTask task = null;\n        if (taskType.isSelect()) {\n            task = new SelectTask(pipeline.getId());\n        } else if (taskType.isExtract()) {\n            task = new ExtractTask(pipeline.getId());\n        } else if (taskType.isTransform()) {\n            task = new TransformTask(pipeline.getId());\n        } else if (taskType.isLoad()) {\n            task = new LoadTask(pipeline.getId());\n        }\n\n        if (task != null) {\n            OtterContextLocator.autowire(task); // 注入一下spring资源\n            task.start();\n            tasks.put(taskType, task);\n            logger.info(\"INFO ## start this task = {} success\", taskType.toString());\n        }\n    }\n\n    private void stopTask(Map<StageType, GlobalTask> tasks, StageType taskType) {\n        GlobalTask task = tasks.remove(taskType);\n        if (task != null) {\n            task.shutdown();\n            logger.info(\"INFO ## taskName = {} has shutdown\", taskType);\n        } else {\n            logger.info(\"INFo ## taskName = {} is not started\", taskType);\n        }\n\n    }\n\n    private void stopPipeline(Long pipelineId, Map<StageType, GlobalTask> tasks) {\n        for (GlobalTask task : tasks.values()) {\n            try {\n                task.shutdown();\n            } catch (Exception e) {\n                logger.error(\"## stop s/e/t/l task error!\", e);\n            } finally {\n                tasks.remove(task);\n            }\n        }\n        // close other resources.\n        try {\n            Thread.sleep(1 * 1000); // sleep 5s，等待S.E.T.L释放线程\n        } catch (InterruptedException e) {\n            logger.error(\"ERROR ## \", e);\n        }\n\n        // 释放资源\n        releasePipeline(pipelineId);\n        arbitrateEventService.toolEvent().release(pipelineId);\n    }\n\n    private void releasePipeline(Long pipelineId) {\n        dataSourceService.destroy(pipelineId);\n        dbDialectFactory.destory(pipelineId);\n    }\n\n    private void initNid() {\n        // 获取一下nid变量\n        String nid = System.getProperty(OtterConstants.NID_NAME);\n        if (StringUtils.isEmpty(nid)) {\n            throw new ConfigException(\"nid is not set!\");\n        }\n        logger.info(\"INFO ## the nodeId = {}\", nid);\n        checkNidVaild(nid);\n        arbitrateManageService.nodeEvent().init(Long.valueOf(nid));\n        // 添加session expired处理\n        NodeSessionExpired sessionExpired = new NodeSessionExpired();\n        sessionExpired.setNodeEvent(arbitrateManageService.nodeEvent());\n        ZooKeeperClient.registerNotification(sessionExpired);\n    }\n\n    // 判断本机ip是否和node.getIp()相同\n    private void checkNidVaild(String nid) {\n        Node node = configClientService.currentNode();\n        String hostIp = AddressUtils.getHostIp();\n        String nodeIp = node.getIp();\n        int nodePort = node.getPort().intValue();\n        if (!AddressUtils.isHostIp(nodeIp)) {\n            throw new IllegalArgumentException(\n                                               String.format(\"node[%s] ip[%s] port[%s] , but your host ip[%s] is not matched!\",\n                                                             nid, nodeIp, nodePort, hostIp));\n        }\n    }\n\n    // ================ mbean info =======================\n\n    public String getHeapMemoryUsage() {\n        MemoryUsage memoryUsage = ManagementFactory.getMemoryMXBean().getHeapMemoryUsage();\n        return JsonUtils.marshalToString(memoryUsage);\n    }\n\n    public String getNodeSystemInfo() {\n        OperatingSystemMXBean mbean = ManagementFactory.getOperatingSystemMXBean();\n        StringBuilder buf = new StringBuilder();\n        buf.append(\"\").append(mbean.getName()).append(' ').append(mbean.getVersion()).append(' ').append(mbean.getArch());\n        buf.append(\" @ \").append(mbean.getAvailableProcessors()).append(\" cores\");\n        buf.append(\" , 【 load average:\").append(mbean.getSystemLoadAverage()).append(\" 】\");\n        return buf.toString();\n    }\n\n    public String getNodeVersionInfo() {\n        return VersionInfo.getVersion() + \" [ r\" + VersionInfo.getRevision() + \" ] @ \" + VersionInfo.getDate();\n    }\n\n    public int getRunningPipelineCount() {\n        return controllers.size();\n    }\n\n    public List<Long> getRunningPipelines() {\n        return new ArrayList<Long>(controllers.keySet());\n    }\n\n    public int getThreadActiveSize() {\n        if (executorService instanceof ThreadPoolExecutor) {\n            ThreadPoolExecutor pool = (ThreadPoolExecutor) executorService;\n            return pool.getActiveCount();\n        }\n\n        return 0;\n    }\n\n    public int getThreadPoolSize() {\n        if (executorService instanceof ThreadPoolExecutor) {\n            ThreadPoolExecutor pool = (ThreadPoolExecutor) executorService;\n            return pool.getCorePoolSize();\n        }\n\n        return 0;\n    }\n\n    public void setThreadPoolSize(int size) {\n        if (executorService instanceof ThreadPoolExecutor) {\n            ThreadPoolExecutor pool = (ThreadPoolExecutor) executorService;\n            pool.setCorePoolSize(size);\n            pool.setMaximumPoolSize(size);\n        }\n    }\n\n    public void setProfile(boolean profile) {\n        stageAggregationCollector.setProfiling(profile);\n    }\n\n    public boolean isSelectRunning(Long pipelineId) {\n        return controllers.get(pipelineId).containsKey(StageType.SELECT);\n    }\n\n    public boolean isExtractRunning(Long pipelineId) {\n        return controllers.get(pipelineId).containsKey(StageType.EXTRACT);\n    }\n\n    public boolean isTransformRunning(Long pipelineId) {\n        return controllers.get(pipelineId).containsKey(StageType.TRANSFORM);\n    }\n\n    public boolean isLoadRunning(Long pipelineId) {\n        return controllers.get(pipelineId).containsKey(StageType.LOAD);\n    }\n\n    public String selectStageAggregation(Long pipelineId) {\n        return stageAggregationCollector.histogram(pipelineId, StageType.SELECT);\n    }\n\n    public String extractStageAggregation(Long pipelineId) {\n        return stageAggregationCollector.histogram(pipelineId, StageType.EXTRACT);\n    }\n\n    public String transformStageAggregation(Long pipelineId) {\n        return stageAggregationCollector.histogram(pipelineId, StageType.TRANSFORM);\n    }\n\n    public String loadStageAggregation(Long pipelineId) {\n        return stageAggregationCollector.histogram(pipelineId, StageType.LOAD);\n    }\n\n    public String selectPendingProcess(Long pipelineId) {\n        return pendingProcess(pipelineId, StageType.SELECT);\n    }\n\n    public String extractPendingProcess(Long pipelineId) {\n        return pendingProcess(pipelineId, StageType.EXTRACT);\n    }\n\n    public String transformPendingProcess(Long pipelineId) {\n        return pendingProcess(pipelineId, StageType.TRANSFORM);\n    }\n\n    public String loadPendingProcess(Long pipelineId) {\n        return pendingProcess(pipelineId, StageType.LOAD);\n    }\n\n    private String pendingProcess(Long pipelineId, StageType stage) {\n        GlobalTask task = controllers.get(pipelineId).get(stage);\n        if (task != null) {\n            return \"stage:\" + stage + \" , pending:[\" + StringUtils.join(task.getPendingProcess(), ',') + \"]\";\n        } else {\n            return \"node don't running stage:\" + stage;\n        }\n    }\n\n    // ==================== setter / getter =======================\n\n    public void setNodeTaskService(NodeTaskService nodeTaskService) {\n        this.nodeTaskService = nodeTaskService;\n    }\n\n    public void setConfigClientService(ConfigClientService configClientService) {\n        this.configClientService = configClientService;\n    }\n\n    public void setArbitrateManageService(ArbitrateManageService arbitrateManageService) {\n        this.arbitrateManageService = arbitrateManageService;\n    }\n\n    public void setDataSourceService(DataSourceService dataSourceService) {\n        this.dataSourceService = dataSourceService;\n    }\n\n    public void setDbDialectFactory(DbDialectFactory dbDialectFactory) {\n        this.dbDialectFactory = dbDialectFactory;\n    }\n\n    public void setArbitrateEventService(ArbitrateEventService arbitrateEventService) {\n        this.arbitrateEventService = arbitrateEventService;\n    }\n\n    public void setStageAggregationCollector(StageAggregationCollector stageAggregationCollector) {\n        this.stageAggregationCollector = stageAggregationCollector;\n    }\n\n    public void setExecutorService(ExecutorService executorService) {\n        this.executorService = executorService;\n    }\n\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/OtterControllerMBean.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl;\n\nimport java.util.List;\n\npublic interface OtterControllerMBean {\n\n    /**\n     * 返回当前运行pipeline数量，可能只运行S/E/T/L的某个模块\n     */\n    public int getRunningPipelineCount();\n\n    /**\n     * 返回当前运行中的pipeline的id列表\n     */\n    public List<Long> getRunningPipelines();\n\n    /**\n     * 获取当前使用的heap区大小\n     */\n    public String getHeapMemoryUsage();\n\n    /**\n     * 获取node共享线程线程池的线程数\n     */\n    public int getThreadPoolSize();\n\n    /**\n     * 获取当前node共享线程的当前活跃线程数\n     */\n    public int getThreadActiveSize();\n\n    /**\n     * 获取系统对应的load\n     */\n    public String getNodeSystemInfo();\n\n    /**\n     * 获取node节点对应的版本信息\n     */\n    public String getNodeVersionInfo();\n\n    /**\n     * 当前节点是否运行select\n     */\n    public boolean isSelectRunning(Long pipelineId);\n\n    /**\n     * 当前节点是否运行extract\n     */\n    public boolean isExtractRunning(Long pipelineId);\n\n    /**\n     * 当前节点是否运行transform\n     */\n    public boolean isTransformRunning(Long pipelineId);\n\n    /**\n     * 当前节点是否运行load\n     */\n    public boolean isLoadRunning(Long pipelineId);\n\n    /**\n     * 设置是否开启profile统计\n     */\n    public void setProfile(boolean profile);\n\n    /**\n     * 设置对应的s/e/t/l seda模型的线程池大小\n     */\n    public void setThreadPoolSize(int size);\n\n    // ============ 运行信息 =========\n\n    /**\n     * select stage统计信息\n     */\n    public String selectStageAggregation(Long pipelineId);\n\n    /**\n     * extract stage统计信息\n     */\n    public String extractStageAggregation(Long pipelineId);\n\n    /**\n     * transform stage统计信息\n     */\n    public String transformStageAggregation(Long pipelineId);\n\n    /**\n     * load stage统计信息\n     */\n    public String loadStageAggregation(Long pipelineId);\n\n    /**\n     * select pending队列信息\n     */\n    public String selectPendingProcess(Long pipelineId);\n\n    /**\n     * extract pending队列信息\n     */\n    public String extractPendingProcess(Long pipelineId);\n\n    /**\n     * transform pending队列信息\n     */\n    public String transformPendingProcess(Long pipelineId);\n\n    /**\n     * load pending队列信息\n     */\n    public String loadPendingProcess(Long pipelineId);\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/common/datasource/DataSourceService.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.datasource;\n\nimport com.alibaba.otter.shared.common.model.config.data.DataMediaSource;\n\n/**\n * 抽象所有的data source处理service,并且返回DataMedia的meta信息\n * \n * @author xiaoqing.zhouxq\n */\npublic interface DataSourceService {\n\n    /**\n     * 返回操作数据源的句柄\n     * \n     * @param <T>\n     * @param dataMediaId\n     * @return\n     */\n    <T> T getDataSource(long pipelineId, DataMediaSource dataMediaSource);\n\n    /**\n     * 释放当前pipeline的数据源.\n     * \n     * @param pipeline\n     */\n    void destroy(Long pipelineId);\n\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/common/datasource/impl/DBDataSourceService.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.datasource.impl;\n\nimport java.sql.SQLException;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Map;\n\nimport javax.sql.DataSource;\n\nimport org.apache.commons.dbcp.BasicDataSource;\nimport org.apache.commons.lang.StringUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.DisposableBean;\nimport org.springframework.util.Assert;\nimport org.springframework.util.CollectionUtils;\n\nimport com.alibaba.otter.common.push.datasource.DataSourceHanlder;\nimport com.alibaba.otter.node.etl.common.datasource.DataSourceService;\nimport com.alibaba.otter.shared.common.model.config.data.DataMediaSource;\nimport com.alibaba.otter.shared.common.model.config.data.DataMediaType;\nimport com.alibaba.otter.shared.common.model.config.data.db.DbMediaSource;\nimport com.google.common.base.Function;\nimport com.google.common.collect.OtterMigrateMap;\n\n/**\n * Comment of DataSourceServiceImpl\n * \n * @author xiaoqing.zhouxq\n * @author zebinxu, add {@link DataSourceHanlder}\n */\npublic class DBDataSourceService implements DataSourceService, DisposableBean {\n\n    private static final Logger                       logger                        = LoggerFactory.getLogger(DBDataSourceService.class);\n\n    private List<DataSourceHanlder>                   dataSourceHandlers;\n\n    private int                                       maxWait                       = 60 * 1000;\n\n    private int                                       minIdle                       = 0;\n\n    private int                                       initialSize                   = 0;\n\n    private int                                       maxActive                     = 32;\n\n    private int                                       maxIdle                       = 32;\n\n    private int                                       numTestsPerEvictionRun        = -1;\n\n    private int                                       timeBetweenEvictionRunsMillis = 60 * 1000;\n\n    private int                                       removeAbandonedTimeout        = 5 * 60;\n\n    private int                                       minEvictableIdleTimeMillis    = 5 * 60 * 1000;\n\n    /**\n     * 一个pipeline下面有一组DataSource.<br>\n     * key = pipelineId<br>\n     * value = key(dataMediaSourceId)-value(DataSource)<br>\n     */\n    private Map<Long, Map<DbMediaSource, DataSource>> dataSources;\n\n    public DBDataSourceService(){\n        // 构建第一层map\n        dataSources = OtterMigrateMap.makeComputingMap(new Function<Long, Map<DbMediaSource, DataSource>>() {\n\n            public Map<DbMediaSource, DataSource> apply(final Long pipelineId) {\n                // 构建第二层map\n                return OtterMigrateMap.makeComputingMap(new Function<DbMediaSource, DataSource>() {\n\n                    public DataSource apply(DbMediaSource dbMediaSource) {\n\n                        // 扩展功能,可以自定义一些自己实现的 dataSource\n                        DataSource customDataSource = preCreate(pipelineId, dbMediaSource);\n                        if (customDataSource != null) {\n                            return customDataSource;\n                        }\n\n                        return createDataSource(dbMediaSource.getUrl(),\n                            dbMediaSource.getUsername(),\n                            dbMediaSource.getPassword(),\n                            dbMediaSource.getDriver(),\n                            dbMediaSource.getType(),\n                            dbMediaSource.getEncode());\n                    }\n\n                });\n            }\n        });\n\n    }\n\n    public DataSource getDataSource(long pipelineId, DataMediaSource dataMediaSource) {\n        Assert.notNull(dataMediaSource);\n        return dataSources.get(pipelineId).get(dataMediaSource);\n    }\n\n    public void destroy(Long pipelineId) {\n        Map<DbMediaSource, DataSource> sources = dataSources.remove(pipelineId);\n        if (sources != null) {\n            for (DataSource source : sources.values()) {\n                try {\n                    // for filter to destroy custom datasource\n                    if (letHandlerDestroyIfSupport(pipelineId, source)) {\n                        continue;\n                    }\n\n                    // fallback for regular destroy\n                    // TODO need to integrate to handler\n                    BasicDataSource basicDataSource = (BasicDataSource) source;\n                    basicDataSource.close();\n                } catch (SQLException e) {\n                    logger.error(\"ERROR ## close the datasource has an error\", e);\n                }\n            }\n\n            sources.clear();\n        }\n    }\n\n    private boolean letHandlerDestroyIfSupport(Long pipelineId, DataSource dataSource) {\n        boolean destroied = false;\n\n        if (CollectionUtils.isEmpty(this.dataSourceHandlers)) {\n            return destroied;\n        }\n        for (DataSourceHanlder handler : this.dataSourceHandlers) {\n            if (handler.support(dataSource)) {\n                handler.destory(pipelineId);\n                destroied = true;\n                return destroied;\n            }\n        }\n        return destroied;\n\n    }\n\n    public void destroy() throws Exception {\n        for (Long pipelineId : dataSources.keySet()) {\n            destroy(pipelineId);\n        }\n    }\n\n    private DataSource createDataSource(String url, String userName, String password, String driverClassName,\n                                        DataMediaType dataMediaType, String encoding) {\n        BasicDataSource dbcpDs = new BasicDataSource();\n\n        dbcpDs.setInitialSize(initialSize);// 初始化连接池时创建的连接数\n        dbcpDs.setMaxActive(maxActive);// 连接池允许的最大并发连接数，值为非正数时表示不限制\n        dbcpDs.setMaxIdle(maxIdle);// 连接池中的最大空闲连接数，超过时，多余的空闲连接将会被释放，值为负数时表示不限制\n        dbcpDs.setMinIdle(minIdle);// 连接池中的最小空闲连接数，低于此数值时将会创建所欠缺的连接，值为0时表示不创建\n        dbcpDs.setMaxWait(maxWait);// 以毫秒表示的当连接池中没有可用连接时等待可用连接返回的时间，超时则抛出异常，值为-1时表示无限等待\n        dbcpDs.setRemoveAbandoned(true);// 是否清除已经超过removeAbandonedTimeout设置的无效连接\n        dbcpDs.setLogAbandoned(true);// 当清除无效链接时是否在日志中记录清除信息的标志\n        dbcpDs.setRemoveAbandonedTimeout(removeAbandonedTimeout); // 以秒表示清除无效链接的时限\n        dbcpDs.setNumTestsPerEvictionRun(numTestsPerEvictionRun);// 确保连接池中没有已破损的连接\n        dbcpDs.setTestOnBorrow(false);// 指定连接被调用时是否经过校验\n        dbcpDs.setTestOnReturn(false);// 指定连接返回到池中时是否经过校验\n        dbcpDs.setTestWhileIdle(true);// 指定连接进入空闲状态时是否经过空闲对象驱逐进程的校验\n        dbcpDs.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis); // 以毫秒表示空闲对象驱逐进程由运行状态进入休眠状态的时长，值为非正数时表示不运行任何空闲对象驱逐进程\n        dbcpDs.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis); // 以毫秒表示连接被空闲对象驱逐进程驱逐前在池中保持空闲状态的最小时间\n\n        // 动态的参数\n        dbcpDs.setDriverClassName(driverClassName);\n        dbcpDs.setUrl(url);\n        dbcpDs.setUsername(userName);\n        dbcpDs.setPassword(password);\n\n        if (dataMediaType.isOracle()) {\n            dbcpDs.addConnectionProperty(\"restrictGetTables\", \"true\");\n            dbcpDs.setValidationQuery(\"select 1 from dual\");\n        } else if (dataMediaType.isMysql()) {\n            // open the batch mode for mysql since 5.1.8\n            dbcpDs.addConnectionProperty(\"useServerPrepStmts\", \"false\");\n            dbcpDs.addConnectionProperty(\"rewriteBatchedStatements\", \"true\");\n            dbcpDs.addConnectionProperty(\"zeroDateTimeBehavior\", \"convertToNull\");// 将0000-00-00的时间类型返回null\n            dbcpDs.addConnectionProperty(\"yearIsDateType\", \"false\");// 直接返回字符串，不做year转换date处理\n            dbcpDs.addConnectionProperty(\"noDatetimeStringSync\", \"true\");// 返回时间类型的字符串,不做时区处理\n            dbcpDs.addConnectionProperty(\"jdbcCompliantTruncation\", \"false\");// 允许sqlMode为非严格模式\n            if (StringUtils.isNotEmpty(encoding)) {\n                if (StringUtils.equalsIgnoreCase(encoding, \"utf8mb4\")) {\n                    dbcpDs.addConnectionProperty(\"characterEncoding\", \"utf8\");\n                    dbcpDs.setConnectionInitSqls(Arrays.asList(\"set names utf8mb4\"));\n                } else {\n                    dbcpDs.addConnectionProperty(\"characterEncoding\", encoding);\n                }\n            }\n            dbcpDs.setValidationQuery(\"select 1\");\n        } else {\n            logger.error(\"ERROR ## Unknow database type\");\n        }\n\n        return dbcpDs;\n    }\n\n    /**\n     * 扩展功能,可以自定义一些自己实现的 dataSource\n     */\n    private DataSource preCreate(Long pipelineId, DbMediaSource dbMediaSource) {\n\n        if (CollectionUtils.isEmpty(dataSourceHandlers)) {\n            return null;\n        }\n\n        DataSource dataSource = null;\n        for (DataSourceHanlder handler : dataSourceHandlers) {\n            if (handler.support(dbMediaSource)) {\n                dataSource = handler.create(pipelineId, dbMediaSource);\n                if (dataSource != null) {\n                    return dataSource;\n                }\n            }\n        }\n        return null;\n    }\n\n    public void setMaxWait(int maxWait) {\n        this.maxWait = maxWait;\n    }\n\n    public void setMinIdle(int minIdle) {\n        this.minIdle = minIdle;\n    }\n\n    public void setInitialSize(int initialSize) {\n        this.initialSize = initialSize;\n    }\n\n    public void setMaxActive(int maxActive) {\n        this.maxActive = maxActive;\n    }\n\n    public void setMaxIdle(int maxIdle) {\n        this.maxIdle = maxIdle;\n    }\n\n    public void setNumTestsPerEvictionRun(int numTestsPerEvictionRun) {\n        this.numTestsPerEvictionRun = numTestsPerEvictionRun;\n    }\n\n    public void setTimeBetweenEvictionRunsMillis(int timeBetweenEvictionRunsMillis) {\n        this.timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis;\n    }\n\n    public void setRemoveAbandonedTimeout(int removeAbandonedTimeout) {\n        this.removeAbandonedTimeout = removeAbandonedTimeout;\n    }\n\n    public void setMinEvictableIdleTimeMillis(int minEvictableIdleTimeMillis) {\n        this.minEvictableIdleTimeMillis = minEvictableIdleTimeMillis;\n    }\n\n    public void setDataSourceHandlers(List<DataSourceHanlder> dataSourceHandlers) {\n        this.dataSourceHandlers = dataSourceHandlers;\n    }\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/common/db/dialect/AbstractDbDialect.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.db.dialect;\n\nimport java.sql.Connection;\nimport java.sql.DatabaseMetaData;\nimport java.sql.SQLException;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.apache.commons.lang.StringUtils;\nimport org.apache.commons.lang.exception.NestableRuntimeException;\nimport org.apache.ddlutils.model.Table;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.dao.DataAccessException;\nimport org.springframework.jdbc.core.ConnectionCallback;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.jdbc.datasource.DataSourceTransactionManager;\nimport org.springframework.jdbc.support.lob.LobHandler;\nimport org.springframework.transaction.TransactionDefinition;\nimport org.springframework.transaction.support.TransactionTemplate;\nimport org.springframework.util.Assert;\n\nimport com.alibaba.otter.node.etl.common.datasource.DataSourceService;\nimport com.alibaba.otter.shared.common.utils.meta.DdlUtils;\nimport com.alibaba.otter.shared.common.utils.meta.DdlUtilsFilter;\nimport com.google.common.base.Function;\nimport com.google.common.collect.OtterMigrateMap;\n\n/**\n * @author jianghang 2011-10-27 下午01:50:19\n * @version 4.0.0\n */\npublic abstract class AbstractDbDialect implements DbDialect {\n\n    protected static final Logger      logger = LoggerFactory.getLogger(AbstractDbDialect.class);\n    protected int                      databaseMajorVersion;\n    protected int                      databaseMinorVersion;\n    protected String                   databaseName;\n    protected DataSourceService        dataSourceService;\n    protected SqlTemplate              sqlTemplate;\n    protected JdbcTemplate             jdbcTemplate;\n    protected TransactionTemplate      transactionTemplate;\n    protected LobHandler               lobHandler;\n    protected Map<List<String>, Table> tables;\n\n    public AbstractDbDialect(final JdbcTemplate jdbcTemplate, LobHandler lobHandler){\n        this.jdbcTemplate = jdbcTemplate;\n        this.lobHandler = lobHandler;\n        // 初始化transction\n        this.transactionTemplate = new TransactionTemplate();\n        transactionTemplate.setTransactionManager(new DataSourceTransactionManager(jdbcTemplate.getDataSource()));\n        transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);\n\n        // 初始化一些数据\n        jdbcTemplate.execute(new ConnectionCallback() {\n\n            public Object doInConnection(Connection c) throws SQLException, DataAccessException {\n                DatabaseMetaData meta = c.getMetaData();\n                databaseName = meta.getDatabaseProductName();\n                databaseMajorVersion = meta.getDatabaseMajorVersion();\n                databaseMinorVersion = meta.getDatabaseMinorVersion();\n\n                return null;\n            }\n        });\n\n        initTables(jdbcTemplate);\n    }\n\n    public AbstractDbDialect(JdbcTemplate jdbcTemplate, LobHandler lobHandler, String name, int majorVersion,\n                             int minorVersion){\n        this.jdbcTemplate = jdbcTemplate;\n        this.lobHandler = lobHandler;\n        // 初始化transction\n        this.transactionTemplate = new TransactionTemplate();\n        transactionTemplate.setTransactionManager(new DataSourceTransactionManager(jdbcTemplate.getDataSource()));\n        transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);\n\n        this.databaseName = name;\n        this.databaseMajorVersion = majorVersion;\n        this.databaseMinorVersion = minorVersion;\n\n        initTables(jdbcTemplate);\n    }\n\n    public Table findTable(String schema, String table, boolean useCache) {\n        List<String> key = Arrays.asList(schema, table);\n        if (useCache == false) {\n            tables.remove(key);\n        }\n\n        return tables.get(key);\n    }\n\n    public Table findTable(String schema, String table) {\n        return findTable(schema, table, true);\n    }\n\n    public void reloadTable(String schema, String table) {\n        if (StringUtils.isNotEmpty(table)) {\n            tables.remove(Arrays.asList(schema, table));\n        } else {\n            // 如果没有存在表名，则直接清空所有的table，重新加载\n            tables.clear();\n        }\n    }\n\n    public String getName() {\n        return databaseName;\n    }\n\n    public int getMajorVersion() {\n        return databaseMajorVersion;\n    }\n\n    @Override\n    public int getMinorVersion() {\n        return databaseMinorVersion;\n    }\n\n    public String getVersion() {\n        return databaseMajorVersion + \".\" + databaseMinorVersion;\n    }\n\n    public LobHandler getLobHandler() {\n        return lobHandler;\n    }\n\n    public JdbcTemplate getJdbcTemplate() {\n        return jdbcTemplate;\n    }\n\n    public TransactionTemplate getTransactionTemplate() {\n        return transactionTemplate;\n    }\n\n    public SqlTemplate getSqlTemplate() {\n        return sqlTemplate;\n    }\n\n    public boolean isDRDS() {\n        return false;\n    }\n\n    public String getShardColumns(String schema, String table) {\n        return null;\n    }\n\n    public void destory() {\n    }\n\n    // ================================ helper method ==========================\n\n    private void initTables(final JdbcTemplate jdbcTemplate) {\n        this.tables = OtterMigrateMap.makeSoftValueComputingMap(new Function<List<String>, Table>() {\n\n            public Table apply(List<String> names) {\n                Assert.isTrue(names.size() == 2);\n                try {\n                    beforeFindTable(jdbcTemplate, names.get(0), names.get(0), names.get(1));\n                    DdlUtilsFilter filter = getDdlUtilsFilter(jdbcTemplate, names.get(0), names.get(0), names.get(1));\n                    Table table = DdlUtils.findTable(jdbcTemplate, names.get(0), names.get(0), names.get(1), filter);\n                    afterFindTable(table, jdbcTemplate, names.get(0), names.get(0), names.get(1));\n                    if (table == null) {\n                        throw new NestableRuntimeException(\"no found table [\" + names.get(0) + \".\" + names.get(1)\n                                                           + \"] , pls check\");\n                    } else {\n                        return table;\n                    }\n                } catch (Exception e) {\n                    throw new NestableRuntimeException(\"find table [\" + names.get(0) + \".\" + names.get(1) + \"] error\",\n                        e);\n                }\n            }\n        });\n    }\n\n    protected DdlUtilsFilter getDdlUtilsFilter(JdbcTemplate jdbcTemplate, String catalogName, String schemaName,\n                                               String tableName) {\n        // we need to return null for backward compatibility\n        return null;\n    }\n\n    protected void beforeFindTable(JdbcTemplate jdbcTemplate, String catalogName, String schemaName, String tableName) {\n        // for subclass to extend\n    }\n\n    protected void afterFindTable(Table table, JdbcTemplate jdbcTemplate, String catalogName, String schemaName,\n                                  String tableName) {\n        // for subclass to extend\n    }\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/common/db/dialect/AbstractSqlTemplate.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.db.dialect;\n\n/**\n * 默认的基于标准SQL实现的CRUD sql封装\n * \n * @author jianghang 2011-10-27 下午01:37:00\n * @version 4.0.0\n */\npublic abstract class AbstractSqlTemplate implements SqlTemplate {\n\n    private static final String DOT = \".\";\n\n    public String getSelectSql(String schemaName, String tableName, String[] pkNames, String[] columnNames) {\n        StringBuilder sql = new StringBuilder(\"select \");\n        int size = columnNames.length;\n        for (int i = 0; i < size; i++) {\n            sql.append(appendEscape(columnNames[i])).append((i + 1 < size) ? \" , \" : \"\");\n        }\n\n        sql.append(\" from \").append(getFullName(schemaName, tableName)).append(\" where ( \");\n        appendColumnEquals(sql, pkNames, \"and\");\n        sql.append(\" ) \");\n        return sql.toString().intern();// 不使用intern，避免方法区内存消耗过多\n    }\n\n    public String getUpdateSql(String schemaName, String tableName, String[] pkNames, String[] columnNames, boolean updatePks, String shardColumn) {\n        StringBuilder sql = new StringBuilder(\"update \" + getFullName(schemaName, tableName) + \" set \");\n        appendExcludeSingleShardColumnEquals(sql, columnNames, \",\", updatePks, shardColumn);\n        sql.append(\" where (\");\n        appendColumnEquals(sql, pkNames, \"and\");\n        sql.append(\")\");\n        return sql.toString().intern(); // 不使用intern，避免方法区内存消耗过多\n    }\n\n    public String getInsertSql(String schemaName, String tableName, String[] pkNames, String[] columnNames) {\n        StringBuilder sql = new StringBuilder(\"insert into \" + getFullName(schemaName, tableName) + \"(\");\n        String[] allColumns = new String[pkNames.length + columnNames.length];\n        System.arraycopy(columnNames, 0, allColumns, 0, columnNames.length);\n        System.arraycopy(pkNames, 0, allColumns, columnNames.length, pkNames.length);\n\n        int size = allColumns.length;\n        for (int i = 0; i < size; i++) {\n            sql.append(appendEscape(allColumns[i])).append((i + 1 < size) ? \",\" : \"\");\n        }\n\n        sql.append(\") values (\");\n        appendColumnQuestions(sql, allColumns);\n        sql.append(\")\");\n        return sql.toString().intern();// intern优化，避免出现大量相同的字符串\n    }\n\n    public String getDeleteSql(String schemaName, String tableName, String[] pkNames) {\n        StringBuilder sql = new StringBuilder(\"delete from \" + getFullName(schemaName, tableName) + \" where \");\n        appendColumnEquals(sql, pkNames, \"and\");\n        return sql.toString().intern();// intern优化，避免出现大量相同的字符串\n    }\n\n    protected String getFullName(String schemaName, String tableName) {\n        StringBuilder sb = new StringBuilder();\n        if (schemaName != null) {\n            sb.append(appendEscape(schemaName)).append(DOT);\n        }\n        sb.append(appendEscape(tableName));\n        return sb.toString().intern();\n    }\n\n    // ================ helper method ============\n\n    protected String appendEscape(String columnName) {\n        return columnName;\n    }\n\n    protected void appendColumnQuestions(StringBuilder sql, String[] columns) {\n        int size = columns.length;\n        for (int i = 0; i < size; i++) {\n            sql.append(\"?\").append((i + 1 < size) ? \" , \" : \"\");\n        }\n    }\n\n    protected void appendColumnEquals(StringBuilder sql, String[] columns, String separator) {\n        int size = columns.length;\n        for (int i = 0; i < size; i++) {\n            sql.append(\" \").append(appendEscape(columns[i])).append(\" = \").append(\"? \");\n            if (i != size - 1) {\n                sql.append(separator);\n            }\n        }\n    }\n\n    /**\n     * 针对DRDS改造, 在 update set 集合中, 排除 单个拆分键 的赋值操作\n     * @param sql\n     * @param columns\n     * @param separator\n     * @param excludeShardColumn 需要排除的 拆分列\n     */\n    protected void appendExcludeSingleShardColumnEquals(StringBuilder sql, String[] columns, String separator, boolean updatePks, String excludeShardColumn) {\n        int size = columns.length;\n        for (int i = 0; i < size; i++) {\n            // 如果是DRDS数据库, 并且存在拆分键 且 等于当前循环列, 跳过\n            if(!updatePks && excludeShardColumn != null && columns[i].equals(excludeShardColumn)){\n                continue;\n            }\n            sql.append(\" \").append(appendEscape(columns[i])).append(\" = \").append(\"? \");\n            if (i != size - 1) {\n                sql.append(separator);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/common/db/dialect/DbDialect.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.db.dialect;\n\nimport org.apache.ddlutils.model.Table;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.jdbc.support.lob.LobHandler;\nimport org.springframework.transaction.support.TransactionTemplate;\n\n/**\n * 数据库方言定义接口\n * \n * @author jianghang 2011-10-27 上午11:24:15\n * @version 4.0.0\n */\npublic interface DbDialect {\n\n    public String getName();\n\n    public String getVersion();\n\n    public int getMajorVersion();\n\n    public int getMinorVersion();\n\n    public String getDefaultSchema();\n\n    public String getDefaultCatalog();\n\n    public boolean isCharSpacePadded();\n\n    public boolean isCharSpaceTrimmed();\n\n    public boolean isEmptyStringNulled();\n\n    public boolean isSupportMergeSql();\n\n    public boolean isDRDS();\n\n    public LobHandler getLobHandler();\n\n    public JdbcTemplate getJdbcTemplate();\n\n    public TransactionTemplate getTransactionTemplate();\n\n    public SqlTemplate getSqlTemplate();\n\n    public Table findTable(String schema, String table);\n\n    public Table findTable(String schema, String table, boolean useCache);\n\n    public String getShardColumns(String schema, String table);\n\n    public void reloadTable(String schema, String table);\n\n    public void destory();\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/common/db/dialect/DbDialectFactory.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.db.dialect;\n\nimport java.sql.Connection;\nimport java.sql.DatabaseMetaData;\nimport java.sql.SQLException;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\n\nimport javax.sql.DataSource;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.DisposableBean;\nimport org.springframework.dao.DataAccessException;\nimport org.springframework.jdbc.core.ConnectionCallback;\nimport org.springframework.jdbc.core.JdbcTemplate;\n\nimport com.alibaba.otter.node.etl.common.datasource.DataSourceService;\nimport com.alibaba.otter.shared.common.model.config.data.db.DbMediaSource;\nimport com.google.common.base.Function;\nimport com.google.common.collect.MigrateMap;\nimport com.google.common.collect.OtterMigrateMap;\nimport com.google.common.collect.OtterMigrateMap.OtterRemovalListener;\n\n/**\n * @author jianghang 2011-10-27 下午02:12:06\n * @version 4.0.0\n */\npublic class DbDialectFactory implements DisposableBean {\n\n    private static final Logger                      logger = LoggerFactory.getLogger(DbDialectFactory.class);\n    private DataSourceService                        dataSourceService;\n    private DbDialectGenerator                       dbDialectGenerator;\n\n    // 第一层pipelineId , 第二层DbMediaSource id\n    private Map<Long, Map<DbMediaSource, DbDialect>> dialects;\n\n    public DbDialectFactory(){\n        dialects = OtterMigrateMap.makeSoftValueComputingMapWithRemoveListenr(new Function<Long, Map<DbMediaSource, DbDialect>>() {\n\n            public Map<DbMediaSource, DbDialect> apply(final Long pipelineId) {\n                // 构建第二层map\n                return MigrateMap.makeComputingMap(new Function<DbMediaSource, DbDialect>() {\n\n                    public DbDialect apply(final DbMediaSource source) {\n                        DataSource dataSource = dataSourceService.getDataSource(pipelineId, source);\n                        final JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);\n                        return (DbDialect) jdbcTemplate.execute(new ConnectionCallback() {\n\n                            public Object doInConnection(Connection c) throws SQLException, DataAccessException {\n                                DatabaseMetaData meta = c.getMetaData();\n                                String databaseName = meta.getDatabaseProductName();\n                                String databaseVersion = meta.getDatabaseProductVersion();\n                                int databaseMajorVersion = meta.getDatabaseMajorVersion();\n                                int databaseMinorVersion = meta.getDatabaseMinorVersion();\n                                DbDialect dialect = dbDialectGenerator.generate(jdbcTemplate,\n                                    databaseName,\n                                    databaseVersion,\n                                    databaseMajorVersion,\n                                    databaseMinorVersion,\n                                    source.getType());\n                                if (dialect == null) {\n                                    throw new UnsupportedOperationException(\"no dialect for\" + databaseName);\n                                }\n\n                                if (logger.isInfoEnabled()) {\n                                    logger.info(String.format(\"--- DATABASE: %s, SCHEMA: %s ---\",\n                                        databaseName,\n                                        (dialect.getDefaultSchema() == null) ? dialect.getDefaultCatalog() : dialect.getDefaultSchema()));\n                                }\n\n                                return dialect;\n                            }\n                        });\n\n                    }\n                });\n            }\n        },\n            new OtterRemovalListener<Long, Map<DbMediaSource, DbDialect>>() {\n\n                @Override\n                public void onRemoval(Long pipelineId, Map<DbMediaSource, DbDialect> dialect) {\n                    if (dialect == null) {\n                        return;\n                    }\n\n                    for (DbDialect dbDialect : dialect.values()) {\n                        dbDialect.destory();\n                    }\n                }\n\n            });\n\n    }\n\n    public DbDialect getDbDialect(Long pipelineId, DbMediaSource source) {\n        return dialects.get(pipelineId).get(source);\n    }\n\n    public void destory(Long pipelineId) {\n        Map<DbMediaSource, DbDialect> dialect = dialects.remove(pipelineId);\n        if (dialect != null) {\n            for (DbDialect dbDialect : dialect.values()) {\n                dbDialect.destory();\n            }\n        }\n    }\n\n    public void destroy() throws Exception {\n        Set<Long> pipelineIds = new HashSet<Long>(dialects.keySet());\n        for (Long pipelineId : pipelineIds) {\n            destory(pipelineId);\n        }\n    }\n\n    // =============== setter / getter =================\n\n    public void setDataSourceService(DataSourceService dataSourceService) {\n        this.dataSourceService = dataSourceService;\n    }\n\n    public void setDbDialectGenerator(DbDialectGenerator dbDialectGenerator) {\n        this.dbDialectGenerator = dbDialectGenerator;\n    }\n\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/common/db/dialect/DbDialectGenerator.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.db.dialect;\n\nimport org.apache.commons.lang.StringUtils;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.jdbc.support.lob.LobHandler;\n\nimport com.alibaba.otter.node.etl.common.db.dialect.mysql.MysqlDialect;\nimport com.alibaba.otter.node.etl.common.db.dialect.oracle.OracleDialect;\nimport com.alibaba.otter.shared.common.model.config.data.DataMediaType;\n\n/**\n * @author zebin.xuzb @ 2012-8-8\n * @version 4.1.0\n */\npublic class DbDialectGenerator {\n\n    protected static final String ORACLE      = \"oracle\";\n    protected static final String MYSQL       = \"mysql\";\n    protected static final String TDDL_GROUP  = \"TGroupDatabase\";\n    protected static final String TDDL_CLIENT = \"TDDL\";\n\n    protected LobHandler          defaultLobHandler;\n    protected LobHandler          oracleLobHandler;\n\n    protected DbDialect generate(JdbcTemplate jdbcTemplate, String databaseName, String databaseNameVersion,\n                                 int databaseMajorVersion, int databaseMinorVersion, DataMediaType dataMediaType) {\n        DbDialect dialect = null;\n\n        if (StringUtils.startsWithIgnoreCase(databaseName, ORACLE)) { // for\n                                                                      // oracle\n            dialect = new OracleDialect(jdbcTemplate,\n                oracleLobHandler,\n                databaseName,\n                databaseMajorVersion,\n                databaseMinorVersion);\n        } else if (StringUtils.startsWithIgnoreCase(databaseName, MYSQL)) { // for\n                                                                            // mysql\n            dialect = new MysqlDialect(jdbcTemplate,\n                defaultLobHandler,\n                databaseName,\n                databaseNameVersion,\n                databaseMajorVersion,\n                databaseMinorVersion);\n        } else if (StringUtils.startsWithIgnoreCase(databaseName, TDDL_GROUP)) { // for\n                                                                                 // tddl\n                                                                                 // group\n            throw new RuntimeException(databaseName + \" type is not support!\");\n        } else if (StringUtils.startsWithIgnoreCase(databaseName, TDDL_CLIENT)) {\n            throw new RuntimeException(databaseName + \" type is not support!\");\n        }\n\n        // diamond is delegated to mysql/oracle, so don't need to extend here\n\n        return dialect;\n    }\n\n    // ======== setter =========\n    public void setDefaultLobHandler(LobHandler defaultLobHandler) {\n        this.defaultLobHandler = defaultLobHandler;\n    }\n\n    public void setOracleLobHandler(LobHandler oracleLobHandler) {\n        this.oracleLobHandler = oracleLobHandler;\n    }\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/common/db/dialect/SqlTemplate.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.db.dialect;\n\n/**\n * sql构造模板操作\n * \n * @author jianghang 2011-10-27 下午01:31:15\n * @version 4.0.0\n */\npublic interface SqlTemplate {\n\n    public String getSelectSql(String schemaName, String tableName, String[] pkNames, String[] columnNames);\n\n    public String getUpdateSql(String schemaName, String tableName, String[] pkNames, String[] columnNames, boolean updatePks, String shardColumn);\n\n    public String getDeleteSql(String schemaName, String tableName, String[] pkNames);\n\n    public String getInsertSql(String schemaName, String tableName, String[] pkNames, String[] columnNames);\n\n    /**\n     * 获取对应的mergeSql\n     */\n    public String getMergeSql(String schemaName, String tableName, String[] pkNames, String[] columnNames,\n                              String[] viewColumnNames, boolean updatePks, String shardColumn);\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/common/db/dialect/mysql/MysqlDialect.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.db.dialect.mysql;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.apache.commons.lang.StringUtils;\nimport org.apache.commons.lang.exception.NestableRuntimeException;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.jdbc.support.lob.LobHandler;\nimport org.springframework.util.Assert;\n\nimport com.alibaba.otter.node.etl.common.db.dialect.AbstractDbDialect;\nimport com.alibaba.otter.shared.common.utils.meta.DdlUtils;\nimport com.google.common.base.Function;\nimport com.google.common.collect.OtterMigrateMap;\n\n/**\n * 基于mysql的一些特殊处理定义\n * \n * @author jianghang 2011-10-27 下午01:46:57\n * @version 4.0.0\n */\npublic class MysqlDialect extends AbstractDbDialect {\n\n    private boolean                   isDRDS = false;\n    private Map<List<String>, String> shardColumns;\n\n    public MysqlDialect(JdbcTemplate jdbcTemplate, LobHandler lobHandler){\n        super(jdbcTemplate, lobHandler);\n        sqlTemplate = new MysqlSqlTemplate();\n    }\n\n    public MysqlDialect(JdbcTemplate jdbcTemplate, LobHandler lobHandler, String name, String databaseVersion,\n                        int majorVersion, int minorVersion){\n        super(jdbcTemplate, lobHandler, name, majorVersion, minorVersion);\n        sqlTemplate = new MysqlSqlTemplate();\n\n        if (StringUtils.contains(databaseVersion, \"-TDDL-\")) {\n            isDRDS = true;\n            initShardColumns();\n        }\n    }\n\n    private void initShardColumns() {\n        this.shardColumns = OtterMigrateMap.makeSoftValueComputingMap(new Function<List<String>, String>() {\n\n            public String apply(List<String> names) {\n                Assert.isTrue(names.size() == 2);\n                try {\n                    String result = DdlUtils.getShardKeyByDRDS(jdbcTemplate, names.get(0), names.get(0), names.get(1));\n                    if (StringUtils.isEmpty(result)) {\n                        return \"\";\n                    } else {\n                        return result;\n                    }\n                } catch (Exception e) {\n                    throw new NestableRuntimeException(\"find table [\" + names.get(0) + \".\" + names.get(1) + \"] error\",\n                        e);\n                }\n            }\n        });\n    }\n\n    public boolean isCharSpacePadded() {\n        return false;\n    }\n\n    public boolean isCharSpaceTrimmed() {\n        return true;\n    }\n\n    public boolean isEmptyStringNulled() {\n        return false;\n    }\n\n    public boolean isSupportMergeSql() {\n        return true;\n    }\n\n    public String getDefaultSchema() {\n        return null;\n    }\n\n    public boolean isDRDS() {\n        return isDRDS;\n    }\n\n    public String getShardColumns(String schema, String table) {\n        if (isDRDS()) {\n            return shardColumns.get(Arrays.asList(schema, table));\n        } else {\n            return null;\n        }\n    }\n\n    public String getDefaultCatalog() {\n        return (String) jdbcTemplate.queryForObject(\"select database()\", String.class);\n    }\n\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/common/db/dialect/mysql/MysqlSqlTemplate.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.db.dialect.mysql;\n\nimport com.alibaba.otter.node.etl.common.db.dialect.AbstractSqlTemplate;\n\n/**\n * mysql sql生成模板\n * \n * @author jianghang 2011-10-27 下午01:41:20\n * @version 4.0.0\n */\npublic class MysqlSqlTemplate extends AbstractSqlTemplate {\n\n    private static final String ESCAPE = \"`\";\n\n    public String getMergeSql(String schemaName, String tableName, String[] pkNames, String[] columnNames,\n                              String[] viewColumnNames, boolean includePks, String shardColumn) {\n        StringBuilder sql = new StringBuilder(\"insert into \" + getFullName(schemaName, tableName) + \"(\");\n        int size = columnNames.length;\n        for (int i = 0; i < size; i++) {\n            sql.append(appendEscape(columnNames[i])).append(\" , \");\n        }\n        size = pkNames.length;\n        for (int i = 0; i < size; i++) {\n            sql.append(appendEscape(pkNames[i])).append((i + 1 < size) ? \" , \" : \"\");\n        }\n\n        sql.append(\") values (\");\n        size = columnNames.length;\n        for (int i = 0; i < size; i++) {\n            sql.append(\"?\").append(\" , \");\n        }\n        size = pkNames.length;\n        for (int i = 0; i < size; i++) {\n            sql.append(\"?\").append((i + 1 < size) ? \" , \" : \"\");\n        }\n        sql.append(\")\");\n        sql.append(\" on duplicate key update \");\n\n        size = columnNames.length;\n        for (int i = 0; i < size; i++) {\n            // 如果是DRDS数据库, 并且存在拆分键 且 等于当前循环列, 跳过\n            if(!includePks && shardColumn != null && columnNames[i].equals(shardColumn)){\n                continue;\n            }\n\n            sql.append(appendEscape(columnNames[i]))\n                .append(\"=values(\")\n                .append(appendEscape(columnNames[i]))\n                .append(\")\");\n            if (includePks) {\n                sql.append(\" , \");\n            } else {\n                sql.append((i + 1 < size) ? \" , \" : \"\");\n            }\n        }\n\n        if (includePks) {\n            // mysql merge sql匹配了uniqe / primary key时都会执行update，所以需要更新pk信息\n            size = pkNames.length;\n            for (int i = 0; i < size; i++) {\n                sql.append(appendEscape(pkNames[i])).append(\"=values(\").append(appendEscape(pkNames[i])).append(\")\");\n                sql.append((i + 1 < size) ? \" , \" : \"\");\n            }\n        }\n\n        return sql.toString().intern();// intern优化，避免出现大量相同的字符串\n    }\n\n    protected String appendEscape(String columnName) {\n        return ESCAPE + columnName + ESCAPE;\n    }\n\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/common/db/dialect/oracle/OracleDialect.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.db.dialect.oracle;\n\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.jdbc.support.lob.LobHandler;\n\nimport com.alibaba.otter.node.etl.common.db.dialect.AbstractDbDialect;\n\n/**\n * 基于oracle的一些特殊处理定义\n * \n * @author jianghang 2011-10-27 下午01:44:46\n * @version 4.0.0\n */\npublic class OracleDialect extends AbstractDbDialect {\n\n    public OracleDialect(JdbcTemplate jdbcTemplate, LobHandler lobHandler){\n        super(jdbcTemplate, lobHandler);\n        sqlTemplate = new OracleSqlTemplate();\n    }\n\n    public OracleDialect(JdbcTemplate jdbcTemplate, LobHandler lobHandler, String name, int majorVersion,\n                         int minorVersion){\n        super(jdbcTemplate, lobHandler, name, majorVersion, minorVersion);\n        sqlTemplate = new OracleSqlTemplate();\n    }\n\n    public boolean isCharSpacePadded() {\n        return true;\n    }\n\n    public boolean isCharSpaceTrimmed() {\n        return false;\n    }\n\n    public boolean isEmptyStringNulled() {\n        return true;\n    }\n\n    public boolean storesUpperCaseNamesInCatalog() {\n        return true;\n    }\n\n    public boolean isSupportMergeSql() {\n        return true;\n    }\n\n    public String getDefaultCatalog() {\n        return null;\n    }\n\n    public String getDefaultSchema() {\n        return (String) jdbcTemplate.queryForObject(\"SELECT sys_context('USERENV', 'CURRENT_SCHEMA') FROM dual\",\n                                                    String.class);\n    }\n\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/common/db/dialect/oracle/OracleSqlTemplate.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.db.dialect.oracle;\n\nimport com.alibaba.otter.node.etl.common.db.dialect.AbstractSqlTemplate;\n\n/**\n * oracle生成模板\n * \n * @author jianghang 2011-10-27 下午01:41:34\n * @version 4.0.0\n */\npublic class OracleSqlTemplate extends AbstractSqlTemplate {\n\n    private static final String ESCAPE = \"\\\"\";\n\n    /**\n     * http://en.wikipedia.org/wiki/Merge_(SQL)\n     */\n    public String getMergeSql(String schemaName, String tableName, String[] keyNames, String[] columnNames,\n                              String[] viewColumnNames, boolean includePks, String shardColumn) {\n        final String aliasA = \"a\";\n        final String aliasB = \"b\";\n        StringBuilder sql = new StringBuilder();\n\n        sql.append(\"merge /*+ use_nl(a b)*/ into \")\n            .append(getFullName(schemaName, tableName))\n            .append(\" \")\n            .append(aliasA);\n        sql.append(\" using (select \");\n\n        int size = columnNames.length;\n        // 构建 (select ? as col1, ? as col2 from dual)\n        for (int i = 0; i < size; i++) {\n            sql.append(\"? as \" + appendEscape(columnNames[i])).append(\" , \");\n        }\n        size = keyNames.length;\n        for (int i = 0; i < size; i++) {\n            sql.append(\"? as \" + appendEscape(keyNames[i])).append((i + 1 < size) ? \" , \" : \"\");\n        }\n        sql.append(\" from dual) \").append(aliasB);\n        sql.append(\" on (\");\n\n        size = keyNames.length;\n        for (int i = 0; i < size; i++) {\n            sql.append(aliasA + \".\" + appendEscape(keyNames[i]))\n                .append(\"=\")\n                .append(aliasB + \".\" + appendEscape(keyNames[i]));\n            sql.append((i + 1 < size) ? \" and \" : \"\");\n        }\n\n        sql.append(\") when matched then update set \");\n\n        size = columnNames.length;\n        for (int i = 0; i < size; i++) {\n            sql.append(aliasA + \".\" + appendEscape(columnNames[i]))\n                .append(\"=\")\n                .append(aliasB + \".\" + appendEscape(columnNames[i]));\n            sql.append((i + 1 < size) ? \" , \" : \"\");\n        }\n\n        sql.append(\" when not matched then insert (\");\n        size = columnNames.length;\n        for (int i = 0; i < size; i++) {\n            sql.append(aliasA + \".\" + appendEscape(columnNames[i])).append(\" , \");\n        }\n        size = keyNames.length;\n        for (int i = 0; i < size; i++) {\n            sql.append(aliasA + \".\" + appendEscape(keyNames[i])).append((i + 1 < size) ? \" , \" : \"\");\n        }\n\n        sql.append(\" ) values (\");\n        size = columnNames.length;\n        for (int i = 0; i < size; i++) {\n            sql.append(aliasB + \".\" + appendEscape(columnNames[i])).append(\" , \");\n        }\n        size = keyNames.length;\n        for (int i = 0; i < size; i++) {\n            sql.append(aliasB + \".\" + appendEscape(keyNames[i])).append((i + 1 < size) ? \" , \" : \"\");\n        }\n        sql.append(\" )\");\n        return sql.toString().intern(); // intern优化，避免出现大量相同的字符串\n    }\n\n    protected String appendEscape(String columnName) {\n        return columnName;\n    }\n\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/common/db/lob/AutomaticJdbcExtractor.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.db.lob;\n\nimport java.sql.CallableStatement;\nimport java.sql.Connection;\nimport java.sql.PreparedStatement;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.sql.Statement;\nimport java.util.Iterator;\nimport java.util.Map;\n\nimport org.springframework.jdbc.support.nativejdbc.NativeJdbcExtractor;\n\n/**\n * copy from otter3.0，根据不同的数据源自动选择对应的NativeJdbcExtractor\n * \n * @author jianghang 2011-10-27 下午03:35:17\n * @version 4.0.0\n */\npublic class AutomaticJdbcExtractor implements NativeJdbcExtractor {\n\n    private NativeJdbcExtractor              defaultJdbcExtractor;\n    private Map<String, NativeJdbcExtractor> extractors;\n    private NativeJdbcExtractor              jdbcExtractor;\n\n    public AutomaticJdbcExtractor(){\n    }\n\n    public boolean isNativeConnectionNecessaryForNativeStatements() {\n        return true;\n    }\n\n    public boolean isNativeConnectionNecessaryForNativePreparedStatements() {\n        return true;\n    }\n\n    public boolean isNativeConnectionNecessaryForNativeCallableStatements() {\n        return true;\n    }\n\n    public Connection getNativeConnection(Connection con) throws SQLException {\n        return getJdbcExtractor(con).getNativeConnection(con);\n    }\n\n    private synchronized NativeJdbcExtractor getJdbcExtractor(Object o) {\n        if (jdbcExtractor == null) {\n            String objClass = o.getClass().getName();\n            Iterator<String> iterator = extractors.keySet().iterator();\n\n            while (iterator.hasNext()) {\n                String classPrefix = iterator.next();\n\n                if (objClass.indexOf(classPrefix) != -1) {\n                    jdbcExtractor = (NativeJdbcExtractor) extractors.get(classPrefix);\n\n                    break;\n                }\n            }\n\n            if (jdbcExtractor == null) {\n                jdbcExtractor = defaultJdbcExtractor;\n            }\n        }\n\n        return jdbcExtractor;\n    }\n\n    public Connection getNativeConnectionFromStatement(Statement stmt) throws SQLException {\n        return getJdbcExtractor(stmt).getNativeConnectionFromStatement(stmt);\n    }\n\n    public Statement getNativeStatement(Statement stmt) throws SQLException {\n        return getJdbcExtractor(stmt).getNativeStatement(stmt);\n    }\n\n    public PreparedStatement getNativePreparedStatement(PreparedStatement ps) throws SQLException {\n        return getJdbcExtractor(ps).getNativePreparedStatement(ps);\n    }\n\n    public CallableStatement getNativeCallableStatement(CallableStatement cs) throws SQLException {\n        return getJdbcExtractor(cs).getNativeCallableStatement(cs);\n    }\n\n    public ResultSet getNativeResultSet(ResultSet rs) throws SQLException {\n        return getJdbcExtractor(rs).getNativeResultSet(rs);\n    }\n\n    public Map<String, NativeJdbcExtractor> getExtractors() {\n        return extractors;\n    }\n\n    public void setExtractors(Map<String, NativeJdbcExtractor> extractors) {\n        this.extractors = extractors;\n    }\n\n    public NativeJdbcExtractor getDefaultJdbcExtractor() {\n        return defaultJdbcExtractor;\n    }\n\n    public void setDefaultJdbcExtractor(NativeJdbcExtractor defaultJdbcExtractor) {\n        this.defaultJdbcExtractor = defaultJdbcExtractor;\n    }\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/common/db/lob/LazyNativeJdbcExtractor.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.db.lob;\n\nimport java.sql.CallableStatement;\nimport java.sql.Connection;\nimport java.sql.PreparedStatement;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.sql.Statement;\n\nimport org.apache.commons.lang.exception.NestableRuntimeException;\nimport org.springframework.jdbc.support.nativejdbc.NativeJdbcExtractor;\n\n/**\n * A class to lazily instantiate a native JDBC extractor.\n * <p />\n * We need to lazily instantiate it because otherwise Spring will construct it for us, and users might get class not\n * found errors (eg if they're not using Weblogic and Spring tries to load the WeblogicNativeJdbcExtractor, things get\n * ugly).\n */\npublic class LazyNativeJdbcExtractor implements NativeJdbcExtractor {\n\n    private NativeJdbcExtractor delegatedExtractor;\n    private Class               extractorClass;\n\n    public LazyNativeJdbcExtractor(){\n    }\n\n    public void setExtractorClass(Class extractorClass) {\n        this.extractorClass = extractorClass;\n    }\n\n    private synchronized NativeJdbcExtractor getDelegatedExtractor() {\n        try {\n            if (delegatedExtractor == null) {\n                delegatedExtractor = (NativeJdbcExtractor) extractorClass.newInstance();\n            }\n        } catch (IllegalAccessException e) {\n            throw new NestableRuntimeException(\"Error occurred trying to instantiate a native extractor of type: \"\n                                               + extractorClass, e);\n        } catch (InstantiationException e) {\n            throw new NestableRuntimeException(\"Error occurred trying to instantiate a native extractor of type: \"\n                                               + extractorClass, e);\n        }\n\n        if (delegatedExtractor != null) {\n            return delegatedExtractor;\n        } else {\n            throw new NestableRuntimeException(\"Error occurred trying to instantiate a native extractor of type: \"\n                                               + extractorClass);\n        }\n    }\n\n    public boolean isNativeConnectionNecessaryForNativeStatements() {\n        return getDelegatedExtractor().isNativeConnectionNecessaryForNativeStatements();\n    }\n\n    public boolean isNativeConnectionNecessaryForNativePreparedStatements() {\n        return getDelegatedExtractor().isNativeConnectionNecessaryForNativePreparedStatements();\n    }\n\n    public boolean isNativeConnectionNecessaryForNativeCallableStatements() {\n        return getDelegatedExtractor().isNativeConnectionNecessaryForNativeCallableStatements();\n    }\n\n    public Connection getNativeConnection(Connection con) throws SQLException {\n        return getDelegatedExtractor().getNativeConnection(con);\n    }\n\n    public Connection getNativeConnectionFromStatement(Statement stmt) throws SQLException {\n        return getDelegatedExtractor().getNativeConnectionFromStatement(stmt);\n    }\n\n    public Statement getNativeStatement(Statement stmt) throws SQLException {\n        return getDelegatedExtractor().getNativeStatement(stmt);\n    }\n\n    public PreparedStatement getNativePreparedStatement(PreparedStatement ps) throws SQLException {\n        return getDelegatedExtractor().getNativePreparedStatement(ps);\n    }\n\n    public CallableStatement getNativeCallableStatement(CallableStatement cs) throws SQLException {\n        return getDelegatedExtractor().getNativeCallableStatement(cs);\n    }\n\n    public ResultSet getNativeResultSet(ResultSet rs) throws SQLException {\n        return getDelegatedExtractor().getNativeResultSet(rs);\n    }\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/common/db/utils/ByteArrayConverter.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.db.utils;\n\nimport org.apache.commons.beanutils.ConversionException;\nimport org.apache.commons.beanutils.Converter;\nimport org.apache.commons.beanutils.converters.ArrayConverter;\nimport org.apache.commons.beanutils.converters.ByteConverter;\n\n/**\n * 接入eromanga后，需要特殊处理下byte类型数据，先尝试byte[]处理\n * \n * @author jianghang 2011-12-16 下午04:32:08\n * @version 4.0.0\n */\npublic class ByteArrayConverter implements Converter {\n\n    public static final Converter  SQL_BYTES = new ByteArrayConverter(null);\n    private static final Converter converter = new ArrayConverter(byte[].class, new ByteConverter());\n\n    protected final Object         defaultValue;\n    protected final boolean        useDefault;\n\n    public ByteArrayConverter(){\n        this.defaultValue = null;\n        this.useDefault = false;\n    }\n\n    public ByteArrayConverter(Object defaultValue){\n        this.defaultValue = defaultValue;\n        this.useDefault = true;\n    }\n\n    public Object convert(Class type, Object value) {\n        if (value == null) {\n            if (useDefault) {\n                return (defaultValue);\n            } else {\n                throw new ConversionException(\"No value specified\");\n            }\n        }\n\n        if (value instanceof byte[]) {\n            return (value);\n        }\n\n        // BLOB类型，canal直接存储为String(\"ISO-8859-1\")\n        if (value instanceof String) {\n            try {\n                return ((String) value).getBytes(\"ISO-8859-1\");\n            } catch (Exception e) {\n                throw new ConversionException(e);\n            }\n        }\n\n        return converter.convert(type, value); // byteConvertor进行转化\n    }\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/common/db/utils/DdlUtils.java",
    "content": "package com.alibaba.otter.node.etl.common.db.utils;\n\nimport java.util.Map;\n\nimport com.alibaba.druid.sql.ast.SQLCommentHint;\nimport com.alibaba.druid.sql.ast.SQLExpr;\nimport com.alibaba.druid.sql.ast.SQLObject;\nimport com.alibaba.druid.sql.ast.SQLStatement;\nimport com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr;\nimport com.alibaba.druid.sql.ast.expr.SQLPropertyExpr;\nimport com.alibaba.druid.sql.ast.statement.SQLAlterTableItem;\nimport com.alibaba.druid.sql.ast.statement.SQLAlterTableStatement;\nimport com.alibaba.druid.sql.ast.statement.SQLCreateTableStatement;\nimport com.alibaba.druid.sql.ast.statement.SQLExprTableSource;\nimport com.alibaba.druid.sql.ast.statement.SQLAssignItem;\nimport com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlCreateTableStatement;\nimport com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlRenameTableStatement;\nimport com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser;\nimport com.alibaba.druid.sql.dialect.mysql.visitor.MySqlOutputVisitor;\n\n/**\n * 解析一下DDL的完整语法\n * \n * @author agapple 2017年4月6日 下午1:07:53\n * @since 4.2.14\n */\npublic class DdlUtils {\n\n    public static String convert(String sql, String sourceSchema, String sourceTable, String targetSchema,\n                                 String targetTable) {\n        MySqlStatementParser parser = new MySqlStatementParser(sql);\n        SQLStatement stmt = parser.parseStatement();\n\n        StringBuilder out = new StringBuilder();\n        OtterMyqlOutputVisitor visitor = new OtterMyqlOutputVisitor(out,\n            sourceSchema,\n            sourceTable,\n            targetSchema,\n            targetTable);\n        stmt.accept(visitor);\n        return out.toString();\n    }\n\n    public static class OtterMyqlOutputVisitor extends MySqlOutputVisitor {\n\n        private String              targetSchema;\n        private String              targetTable;\n        private String              sourceSchema;\n        private String              sourceTable;\n\n        public OtterMyqlOutputVisitor(Appendable appender, String sourceSchema, String sourceTable,\n                                      String targetSchema, String targetTable){\n            super(appender);\n            this.sourceSchema = sourceSchema;\n            this.sourceTable = sourceTable;\n            this.targetSchema = targetSchema;\n            this.targetTable = targetTable;\n        }\n\n        private void processTableName(SQLExpr sqlName) {\n            if (sqlName instanceof SQLPropertyExpr) {\n                SQLIdentifierExpr owner = (SQLIdentifierExpr) ((SQLPropertyExpr) sqlName).getOwner();\n                String oldSchem = unescapeName(owner.getName());\n                String oldTable = unescapeName(((SQLPropertyExpr) sqlName).getName());\n                if ((sourceSchema == null || oldSchem.equalsIgnoreCase(sourceSchema))\n                    && (sourceTable == null || oldTable.equalsIgnoreCase(sourceTable))) { // rename需要匹配表名\n                    owner.setName(\"`\" + targetSchema + \"`\");\n                    ((SQLPropertyExpr) sqlName).setName(\"`\" + targetTable + \"`\");\n                }\n            } else if (sqlName instanceof SQLIdentifierExpr) {\n                String oldTable = unescapeName(((SQLIdentifierExpr) sqlName).getName());\n                if (sourceTable == null || oldTable.equalsIgnoreCase(sourceTable)) {\n                    // try {\n                    // // 拼上一个schema\n                    // this.appender.append(\"`\" + targetSchema + \"`\");\n                    // } catch (IOException e) {\n                    // throw new RuntimeException(e);\n                    // }\n                    ((SQLIdentifierExpr) sqlName).setName(\"`\" + targetTable + \"`\");\n                }\n            } else {\n                throw new RuntimeException(\"not support SQLName:\" + sqlName);\n            }\n\n            sqlName.accept(this);\n        }\n\n        private String unescapeName(String name) {\n            if (name == null || name.length() <= 0) {\n                return name;\n            }\n            if (name.charAt(0) != '`') {\n                return name;\n            }\n            if (name.charAt(name.length() - 1) != '`') {\n                throw new IllegalArgumentException(\"id start with a '`' must end with a '`', id: \" + name);\n            }\n            StringBuilder sb = new StringBuilder(name.length() - 2);\n            final int endIndex = name.length() - 1;\n            boolean hold = false;\n            for (int i = 1; i < endIndex; ++i) {\n                char c = name.charAt(i);\n                if (c == '`' && !hold) {\n                    hold = true;\n                    continue;\n                }\n                hold = false;\n                if (c >= 'a' && c <= 'z') {\n                    c -= 32;\n                }\n                sb.append(c);\n            }\n            return sb.toString();\n        }\n\n        public boolean visit(MySqlCreateTableStatement x) {\n\n            print0(ucase ? \"CREATE \" : \"create \");\n\n            for (SQLCommentHint hint : x.getHints()) {\n                hint.accept(this);\n                print(' ');\n            }\n\n            if (SQLCreateTableStatement.Type.GLOBAL_TEMPORARY.equals(x.getType())) {\n                print0(ucase ? \"TEMPORARY TABLE \" : \"temporary table \");\n            } else {\n                print0(ucase ? \"TABLE \" : \"table \");\n            }\n\n            if (x.isIfNotExists()) {\n                print0(ucase ? \"IF NOT EXISTS \" : \"if not exists \");\n            }\n\n            processTableName(x.getName());\n\n            if (x.getLike() != null) {\n                print0(ucase ? \" LIKE \" : \" like \");\n                x.getLike().accept(this);\n            }\n\n            int size = x.getTableElementList().size();\n            if (size > 0) {\n                print0(\" (\");\n                incrementIndent();\n                println();\n                for (int i = 0; i < size; ++i) {\n                    if (i != 0) {\n                        print0(\", \");\n                        println();\n                    }\n                    x.getTableElementList().get(i).accept(this);\n                }\n                decrementIndent();\n                println();\n                print(')');\n            }\n\n            for (SQLAssignItem option : x.getTableOptions()) {\n                String key = ((SQLIdentifierExpr) option.getTarget()).getName();\n\n                print(' ');\n                print0(ucase ? key : key.toLowerCase());\n\n                if (\"TABLESPACE\".equals(key)) {\n                    print(' ');\n                    option.getValue().accept(this);\n                    continue;\n                } else if (\"UNION\".equals(key)) {\n                    print0(\" = (\");\n                    option.getValue().accept(this);\n                    print(')');\n                    continue;\n                }\n\n                print0(\" = \");\n\n                option.getValue().accept(this);\n            }\n\n            if (x.getPartitioning() != null) {\n                println();\n                x.getPartitioning().accept(this);\n            }\n\n            if (x.getTableGroup() != null) {\n                println();\n                print0(ucase ? \"TABLEGROUP \" : \"tablegroup \");\n                x.getTableGroup().accept(this);\n            }\n\n            if (x.getSelect() != null) {\n                incrementIndent();\n                println();\n                x.getSelect().accept(this);\n                decrementIndent();\n            }\n\n            for (SQLCommentHint hint : x.getOptionHints()) {\n                print(' ');\n                hint.accept(this);\n            }\n            return false;\n        }\n\n        public boolean visit(SQLAlterTableStatement x) {\n            if (x.isIgnore()) {\n                print0(ucase ? \"ALTER IGNORE TABLE \" : \"alter ignore table \");\n            } else {\n                print0(ucase ? \"ALTER TABLE \" : \"alter table \");\n            }\n            processTableName(x.getName());\n            incrementIndent();\n            for (int i = 0; i < x.getItems().size(); ++i) {\n                SQLAlterTableItem item = x.getItems().get(i);\n                if (i != 0) {\n                    print(',');\n                }\n                println();\n                item.accept(this);\n            }\n\n            if (x.isRemovePatiting()) {\n                println();\n                print0(ucase ? \"REMOVE PARTITIONING\" : \"remove partitioning\");\n            }\n\n            if (x.isUpgradePatiting()) {\n                println();\n                print0(ucase ? \"UPGRADE PARTITIONING\" : \"upgrade partitioning\");\n            }\n\n            if (x.getTableOptions().size() > 0) {\n                println();\n            }\n\n            decrementIndent();\n\n            int i = 0;\n            for (SQLAssignItem option : x.getTableOptions()) {\n                String key = ((SQLIdentifierExpr) option.getTarget()).getName();\n                if (i != 0) {\n                    print(' ');\n                }\n                print0(ucase ? key : key.toLowerCase());\n\n                if (\"TABLESPACE\".equals(key)) {\n                    print(' ');\n                    option.getValue().accept(this);\n                    continue;\n                } else if (\"UNION\".equals(key)) {\n                    print0(\" = (\");\n                    option.getValue().accept(this);\n                    print(')');\n                    continue;\n                }\n\n                print0(\" = \");\n\n                option.getValue().accept(this);\n                i++;\n            }\n\n            return false;\n        }\n\n        @Override\n        public boolean visit(MySqlRenameTableStatement.Item x) {\n            processTableName(x.getName());\n            print0(ucase ? \" TO \" : \" to \");\n            processTableName(x.getTo());\n            return false;\n        }\n\n        public boolean visit(SQLExprTableSource x) {\n            processTableName(x.getExpr());\n            if (x.getAlias() != null) {\n                print(' ');\n                print0(x.getAlias());\n            }\n\n            for (int i = 0; i < x.getHintsSize(); ++i) {\n                print(' ');\n                x.getHints().get(i).accept(this);\n            }\n\n            if (x.getPartitionSize() > 0) {\n                print0(ucase ? \" PARTITION (\" : \" partition (\");\n                printlnAndAccept(x.getPartitions(), \", \");\n                print(')');\n            }\n\n            return false;\n        }\n    }\n\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/common/db/utils/SqlTimestampConverter.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.db.utils;\n\nimport java.sql.Timestamp;\nimport java.text.ParseException;\nimport java.text.ParsePosition;\nimport java.text.SimpleDateFormat;\nimport java.util.Date;\nimport java.util.Locale;\n\nimport org.apache.commons.beanutils.ConversionException;\nimport org.apache.commons.beanutils.Converter;\nimport org.apache.commons.lang.time.DateFormatUtils;\n\npublic class SqlTimestampConverter implements Converter {\n\n    /** Field description */\n    public static final String[]  DATE_FORMATS  = new String[] { \"yyyy-MM-dd\", \"HH:mm:ss\", \"yyyy-MM-dd HH:mm:ss\",\n            \"yyyy-MM-dd hh:mm:ss.fffffffff\", \"EEE MMM dd HH:mm:ss zzz yyyy\",\n            DateFormatUtils.ISO_DATETIME_FORMAT.getPattern(),\n            DateFormatUtils.ISO_DATETIME_TIME_ZONE_FORMAT.getPattern(),\n            DateFormatUtils.SMTP_DATETIME_FORMAT.getPattern(), };\n\n    public static final Converter SQL_TIMESTAMP = new SqlTimestampConverter(null);\n\n    /**\n     * The default value specified to our Constructor, if any.\n     */\n    private final Object          defaultValue;\n\n    /**\n     * Should we return the default value on conversion errors?\n     */\n    private final boolean         useDefault;\n\n    /**\n     * Create a {@link Converter} that will throw a {@link ConversionException} if a conversion error occurs.\n     */\n    public SqlTimestampConverter(){\n        this.defaultValue = null;\n        this.useDefault = false;\n    }\n\n    /**\n     * Create a {@link Converter} that will return the specified default value if a conversion error occurs.\n     * \n     * @param defaultValue The default value to be returned\n     */\n    public SqlTimestampConverter(Object defaultValue){\n        this.defaultValue = defaultValue;\n        this.useDefault = true;\n    }\n\n    /**\n     * Convert the specified input object into an output object of the specified type.\n     * \n     * @param type Data type to which this value should be converted\n     * @param value The input value to be converted\n     * @exception ConversionException if conversion cannot be performed successfully\n     */\n    public Object convert(Class type, Object value) {\n        if (value == null) {\n            if (useDefault) {\n                return (defaultValue);\n            } else {\n                throw new ConversionException(\"No value specified\");\n            }\n        }\n\n        if (value instanceof java.sql.Date && java.sql.Date.class.equals(type)) {\n            return value;\n        } else if (value instanceof java.sql.Time && java.sql.Time.class.equals(type)) {\n            return value;\n        } else if (value instanceof java.sql.Timestamp && java.sql.Timestamp.class.equals(type)) {\n            return value;\n        } else {\n            try {\n                if (java.sql.Date.class.equals(type)) {\n                    return new java.sql.Date(convertTimestamp2TimeMillis(value.toString()));\n                } else if (java.sql.Time.class.equals(type)) {\n                    return new java.sql.Time(convertTimestamp2TimeMillis(value.toString()));\n                } else if (java.sql.Timestamp.class.equals(type)) {\n                    return new java.sql.Timestamp(convertTimestamp2TimeMillis(value.toString()));\n                } else {\n                    return new Timestamp(convertTimestamp2TimeMillis(value.toString()));\n                }\n            } catch (Exception e) {\n                throw new ConversionException(\"Value format invalid: \" + e.getMessage(), e);\n            }\n        }\n\n    }\n\n    private Long convertTimestamp2TimeMillis(String input) {\n        if (input == null) {\n            return null;\n        }\n\n        try {\n            // 先处理Timestamp类型\n            return java.sql.Timestamp.valueOf(input).getTime();\n        } catch (Exception nfe) {\n            try {\n                try {\n                    return parseDate(input, DATE_FORMATS, Locale.ENGLISH).getTime();\n                } catch (Exception err) {\n                    return parseDate(input, DATE_FORMATS, Locale.getDefault()).getTime();\n                }\n            } catch (Exception err) {\n                // 最后处理long time的情况\n                return Long.parseLong(input);\n            }\n        }\n    }\n\n    private Date parseDate(String str, String[] parsePatterns, Locale locale) throws ParseException {\n        if ((str == null) || (parsePatterns == null)) {\n            throw new IllegalArgumentException(\"Date and Patterns must not be null\");\n        }\n\n        SimpleDateFormat parser = null;\n        ParsePosition pos = new ParsePosition(0);\n\n        for (int i = 0; i < parsePatterns.length; i++) {\n            if (i == 0) {\n                parser = new SimpleDateFormat(parsePatterns[0], locale);\n            } else {\n                parser.applyPattern(parsePatterns[i]);\n            }\n            pos.setIndex(0);\n            Date date = parser.parse(str, pos);\n            if ((date != null) && (pos.getIndex() == str.length())) {\n                return date;\n            }\n        }\n\n        throw new ParseException(\"Unable to parse the date: \" + str, -1);\n    }\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/common/db/utils/SqlUtils.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.db.utils;\n\nimport java.io.UnsupportedEncodingException;\nimport java.math.BigDecimal;\nimport java.math.BigInteger;\nimport java.sql.Blob;\nimport java.sql.Clob;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.sql.Types;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.apache.commons.beanutils.ConvertUtilsBean;\nimport org.apache.commons.lang.StringUtils;\n\n/**\n * @author xiaoqing.zhouxq\n */\npublic class SqlUtils {\n\n    public static final String                  REQUIRED_FIELD_NULL_SUBSTITUTE = \" \";\n    public static final String                  SQLDATE_FORMAT                 = \"yyyy-MM-dd\";\n    public static final String                  TIMESTAMP_FORMAT               = \"yyyy-MM-dd HH:mm:ss\";\n    private static final Map<Integer, Class<?>> sqlTypeToJavaTypeMap           = new HashMap<Integer, Class<?>>();\n    private static final ConvertUtilsBean       convertUtilsBean               = new ConvertUtilsBean();\n\n    static {\n        // regist Converter\n        convertUtilsBean.register(SqlTimestampConverter.SQL_TIMESTAMP, java.sql.Date.class);\n        convertUtilsBean.register(SqlTimestampConverter.SQL_TIMESTAMP, java.sql.Time.class);\n        convertUtilsBean.register(SqlTimestampConverter.SQL_TIMESTAMP, java.sql.Timestamp.class);\n        convertUtilsBean.register(ByteArrayConverter.SQL_BYTES, byte[].class);\n\n        // bool\n        sqlTypeToJavaTypeMap.put(Types.BOOLEAN, Boolean.class);\n\n        // int\n        sqlTypeToJavaTypeMap.put(Types.TINYINT, Integer.class);\n        sqlTypeToJavaTypeMap.put(Types.SMALLINT, Integer.class);\n        sqlTypeToJavaTypeMap.put(Types.INTEGER, Integer.class);\n\n        // long\n        sqlTypeToJavaTypeMap.put(Types.BIGINT, Long.class);\n        // mysql bit最多64位，无符号\n        sqlTypeToJavaTypeMap.put(Types.BIT, BigInteger.class);\n\n        // decimal\n        sqlTypeToJavaTypeMap.put(Types.REAL, Float.class);\n        sqlTypeToJavaTypeMap.put(Types.FLOAT, Float.class);\n        sqlTypeToJavaTypeMap.put(Types.DOUBLE, Double.class);\n        sqlTypeToJavaTypeMap.put(Types.NUMERIC, BigDecimal.class);\n        sqlTypeToJavaTypeMap.put(Types.DECIMAL, BigDecimal.class);\n\n        // date\n        sqlTypeToJavaTypeMap.put(Types.DATE, java.sql.Date.class);\n        sqlTypeToJavaTypeMap.put(Types.TIME, java.sql.Time.class);\n        sqlTypeToJavaTypeMap.put(Types.TIMESTAMP, java.sql.Timestamp.class);\n\n        // blob\n        sqlTypeToJavaTypeMap.put(Types.BLOB, byte[].class);\n\n        // byte[]\n        sqlTypeToJavaTypeMap.put(Types.REF, byte[].class);\n        sqlTypeToJavaTypeMap.put(Types.OTHER, byte[].class);\n        sqlTypeToJavaTypeMap.put(Types.ARRAY, byte[].class);\n        sqlTypeToJavaTypeMap.put(Types.STRUCT, byte[].class);\n        sqlTypeToJavaTypeMap.put(Types.SQLXML, byte[].class);\n        sqlTypeToJavaTypeMap.put(Types.BINARY, byte[].class);\n        sqlTypeToJavaTypeMap.put(Types.DATALINK, byte[].class);\n        sqlTypeToJavaTypeMap.put(Types.DISTINCT, byte[].class);\n        sqlTypeToJavaTypeMap.put(Types.VARBINARY, byte[].class);\n        sqlTypeToJavaTypeMap.put(Types.JAVA_OBJECT, byte[].class);\n        sqlTypeToJavaTypeMap.put(Types.LONGVARBINARY, byte[].class);\n\n        // String\n        sqlTypeToJavaTypeMap.put(Types.CHAR, String.class);\n        sqlTypeToJavaTypeMap.put(Types.VARCHAR, String.class);\n        sqlTypeToJavaTypeMap.put(Types.LONGVARCHAR, String.class);\n        sqlTypeToJavaTypeMap.put(Types.LONGNVARCHAR, String.class);\n        sqlTypeToJavaTypeMap.put(Types.NCHAR, String.class);\n        sqlTypeToJavaTypeMap.put(Types.NVARCHAR, String.class);\n        sqlTypeToJavaTypeMap.put(Types.NCLOB, String.class);\n        sqlTypeToJavaTypeMap.put(Types.CLOB, String.class);\n    }\n\n    /**\n     * 将指定java.sql.Types的ResultSet value转换成相应的String\n     * \n     * @param rs\n     * @param index\n     * @param sqlType\n     * @return\n     * @throws SQLException\n     */\n    public static String sqlValueToString(ResultSet rs, int index, int sqlType) throws SQLException {\n        Class<?> requiredType = sqlTypeToJavaTypeMap.get(sqlType);\n        if (requiredType == null) {\n            throw new IllegalArgumentException(\"unknow java.sql.Types - \" + sqlType);\n        }\n\n        return getResultSetValue(rs, index, requiredType);\n    }\n\n    /**\n     * sqlValueToString方法的逆向过程\n     * \n     * @param value\n     * @param sqlType\n     * @param isTextRequired\n     * @param isEmptyStringNulled\n     * @return\n     */\n    public static Object stringToSqlValue(String value, int sqlType, boolean isRequired, boolean isEmptyStringNulled) {\n        // 设置变量\n        String sourceValue = value;\n        if (SqlUtils.isTextType(sqlType)) {\n            if ((sourceValue == null) || (true == StringUtils.isEmpty(sourceValue) && isEmptyStringNulled)) {\n                return isRequired ? REQUIRED_FIELD_NULL_SUBSTITUTE : null;\n            } else {\n                return sourceValue;\n            }\n        } else {\n            if (StringUtils.isEmpty(sourceValue)) {\n                return isEmptyStringNulled ? null : sourceValue;// oracle的返回null，保持兼容\n            } else {\n                Class<?> requiredType = sqlTypeToJavaTypeMap.get(sqlType);\n                if (requiredType == null) {\n                    throw new IllegalArgumentException(\"unknow java.sql.Types - \" + sqlType);\n                } else if (requiredType.equals(String.class)) {\n                    return sourceValue;\n                } else if (true == isNumeric(sqlType)) {\n                    return convertUtilsBean.convert(sourceValue.trim(), requiredType);\n                } else {\n                    return convertUtilsBean.convert(sourceValue, requiredType);\n                }\n            }\n        }\n    }\n\n    public static String encoding(String source, int sqlType, String sourceEncoding, String targetEncoding) {\n        switch (sqlType) {\n            case Types.CHAR:\n            case Types.VARCHAR:\n            case Types.LONGVARCHAR:\n            case Types.NCHAR:\n            case Types.NVARCHAR:\n            case Types.LONGNVARCHAR:\n            case Types.CLOB:\n            case Types.NCLOB:\n                if (false == StringUtils.isEmpty(source)) {\n                    String fromEncoding = StringUtils.isBlank(sourceEncoding) ? \"UTF-8\" : sourceEncoding;\n                    String toEncoding = StringUtils.isBlank(targetEncoding) ? \"UTF-8\" : targetEncoding;\n\n                    // if (false == StringUtils.equalsIgnoreCase(fromEncoding,\n                    // toEncoding)) {\n                    try {\n                        return new String(source.getBytes(fromEncoding), toEncoding);\n                    } catch (UnsupportedEncodingException e) {\n                        throw new IllegalArgumentException(e.getMessage(), e);\n                    }\n                    // }\n                }\n        }\n\n        return source;\n    }\n\n    /**\n     * Retrieve a JDBC column value from a ResultSet, using the specified value\n     * type.\n     * <p>\n     * Uses the specifically typed ResultSet accessor methods, falling back to\n     * {@link #getResultSetValue(java.sql.ResultSet, int)} for unknown types.\n     * <p>\n     * Note that the returned value may not be assignable to the specified\n     * required type, in case of an unknown type. Calling code needs to deal\n     * with this case appropriately, e.g. throwing a corresponding exception.\n     * \n     * @param rs is the ResultSet holding the data\n     * @param index is the column index\n     * @param requiredType the required value type (may be <code>null</code>)\n     * @return the value object\n     * @throws SQLException if thrown by the JDBC API\n     */\n    private static String getResultSetValue(ResultSet rs, int index, Class<?> requiredType) throws SQLException {\n        if (requiredType == null) {\n            return getResultSetValue(rs, index);\n        }\n\n        Object value = null;\n        boolean wasNullCheck = false;\n\n        // Explicitly extract typed value, as far as possible.\n        if (String.class.equals(requiredType)) {\n            value = rs.getString(index);\n        } else if (boolean.class.equals(requiredType) || Boolean.class.equals(requiredType)) {\n            value = Boolean.valueOf(rs.getBoolean(index));\n            wasNullCheck = true;\n        } else if (byte.class.equals(requiredType) || Byte.class.equals(requiredType)) {\n            value = new Byte(rs.getByte(index));\n            wasNullCheck = true;\n        } else if (short.class.equals(requiredType) || Short.class.equals(requiredType)) {\n            value = new Short(rs.getShort(index));\n            wasNullCheck = true;\n        } else if (int.class.equals(requiredType) || Integer.class.equals(requiredType)) {\n            value = new Long(rs.getLong(index));\n            wasNullCheck = true;\n        } else if (long.class.equals(requiredType) || Long.class.equals(requiredType)) {\n            value = rs.getBigDecimal(index);\n            wasNullCheck = true;\n        } else if (float.class.equals(requiredType) || Float.class.equals(requiredType)) {\n            value = new Float(rs.getFloat(index));\n            wasNullCheck = true;\n        } else if (double.class.equals(requiredType) || Double.class.equals(requiredType)\n                   || Number.class.equals(requiredType)) {\n            value = new Double(rs.getDouble(index));\n            wasNullCheck = true;\n        } else if (java.sql.Time.class.equals(requiredType)) {\n            // try {\n            // value = rs.getTime(index);\n            // } catch (SQLException e) {\n            value = rs.getString(index);// 尝试拿为string对象，0000无法用Time表示\n            // if (value == null && !rs.wasNull()) {\n            // value = \"00:00:00\"; //\n            // mysql设置了zeroDateTimeBehavior=convertToNull，出现0值时返回为null\n            // }\n            // }\n        } else if (java.sql.Timestamp.class.equals(requiredType) || java.sql.Date.class.equals(requiredType)) {\n            // try {\n            // value = convertTimestamp(rs.getTimestamp(index));\n            // } catch (SQLException e) {\n            // 尝试拿为string对象，0000-00-00 00:00:00无法用Timestamp 表示\n            value = rs.getString(index);\n            // if (value == null && !rs.wasNull()) {\n            // value = \"0000:00:00 00:00:00\"; //\n            // mysql设置了zeroDateTimeBehavior=convertToNull，出现0值时返回为null\n            // }\n            // }\n        } else if (BigDecimal.class.equals(requiredType)) {\n            value = rs.getBigDecimal(index);\n        } else if (BigInteger.class.equals(requiredType)) {\n            value = rs.getBigDecimal(index);\n        } else if (Blob.class.equals(requiredType)) {\n            value = rs.getBlob(index);\n        } else if (Clob.class.equals(requiredType)) {\n            value = rs.getClob(index);\n        } else if (byte[].class.equals(requiredType)) {\n            try {\n                byte[] bytes = rs.getBytes(index);\n                if (bytes == null) {\n                    value = null;\n                } else {\n                    value = new String(bytes, \"ISO-8859-1\");// 将binary转化为iso-8859-1的字符串\n                }\n            } catch (UnsupportedEncodingException e) {\n                throw new SQLException(e);\n            }\n        } else {\n            // Some unknown type desired -> rely on getObject.\n            value = getResultSetValue(rs, index);\n        }\n\n        // Perform was-null check if demanded (for results that the\n        // JDBC driver returns as primitives).\n        if (wasNullCheck && (value != null) && rs.wasNull()) {\n            value = null;\n        }\n\n        return (value == null) ? null : convertUtilsBean.convert(value);\n    }\n\n    /**\n     * Retrieve a JDBC column value from a ResultSet, using the most appropriate\n     * value type. The returned value should be a detached value object, not\n     * having any ties to the active ResultSet: in particular, it should not be\n     * a Blob or Clob object but rather a byte array respectively String\n     * representation.\n     * <p>\n     * Uses the <code>getObject(index)</code> method, but includes additional\n     * \"hacks\" to get around Oracle 10g returning a non-standard object for its\n     * TIMESTAMP datatype and a <code>java.sql.Date</code> for DATE columns\n     * leaving out the time portion: These columns will explicitly be extracted\n     * as standard <code>java.sql.Timestamp</code> object.\n     * \n     * @param rs is the ResultSet holding the data\n     * @param index is the column index\n     * @return the value object\n     * @throws SQLException if thrown by the JDBC API\n     * @see java.sql.Blob\n     * @see java.sql.Clob\n     * @see java.sql.Timestamp\n     */\n    private static String getResultSetValue(ResultSet rs, int index) throws SQLException {\n        Object obj = rs.getObject(index);\n        return (obj == null) ? null : convertUtilsBean.convert(obj);\n    }\n\n    // private static Object convertTimestamp(Timestamp timestamp) {\n    // return (timestamp == null) ? null : timestamp.getTime();\n    // }\n\n    /**\n     * Check whether the given SQL type is numeric.\n     */\n    public static boolean isNumeric(int sqlType) {\n        return (Types.BIT == sqlType) || (Types.BIGINT == sqlType) || (Types.DECIMAL == sqlType)\n               || (Types.DOUBLE == sqlType) || (Types.FLOAT == sqlType) || (Types.INTEGER == sqlType)\n               || (Types.NUMERIC == sqlType) || (Types.REAL == sqlType) || (Types.SMALLINT == sqlType)\n               || (Types.TINYINT == sqlType);\n    }\n\n    public static boolean isTextType(int sqlType) {\n        if (sqlType == Types.CHAR || sqlType == Types.VARCHAR || sqlType == Types.CLOB || sqlType == Types.LONGVARCHAR\n            || sqlType == Types.NCHAR || sqlType == Types.NVARCHAR || sqlType == Types.NCLOB\n            || sqlType == Types.LONGNVARCHAR) {\n            return true;\n        } else {\n            return false;\n        }\n    }\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/common/io/EncryptUtils.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.io;\n\nimport org.apache.commons.lang.StringUtils;\n\nimport com.alibaba.otter.node.etl.common.io.compress.Compressor;\nimport com.alibaba.otter.node.etl.common.io.compress.impl.gzip.GzipCompressor;\nimport com.alibaba.otter.node.etl.common.io.crypto.AESUtils;\nimport com.alibaba.otter.node.etl.common.io.signature.ChecksumException;\nimport com.alibaba.otter.node.etl.common.io.signature.ChecksumUtils;\n\n/**\n * 数据对象间转换的工具类\n * \n * @author jianghang 2011-10-13 下午07:37:46\n * @version 4.0.0\n */\npublic class EncryptUtils {\n\n    private static final Compressor COMPRESSOR = new GzipCompressor();\n\n    public static EncryptedData encrypt(byte[] input) {\n        // 压缩数据\n        byte[] compData = COMPRESSOR.compress(input);\n\n        // 调用加密工具类\n        AESUtils aes = new AESUtils();\n        aes.generateSecretKey();\n\n        // 加密数据\n        byte[] encryptData = aes.encrypt(compData);\n        return new EncryptedData(encryptData, aes.getSecretyKeyString(), ChecksumUtils.checksum(encryptData));\n    }\n\n    public static byte[] decrypt(EncryptedData encode) {\n        String destCrc = ChecksumUtils.checksum(encode.getData());\n        //验证sign\n        if (false == StringUtils.equals(encode.getCrc(), destCrc)) {\n            throw new ChecksumException(String.format(\"orig: %s, parsed: %s not match\", encode.getCrc(), destCrc));\n        }\n\n        // 调用加密工具类\n        AESUtils aes = new AESUtils();\n        aes.setSecretKeyString(encode.getKey());\n        // 解密并解压数据\n        return COMPRESSOR.decompress(aes.decrypt(encode.getData()));\n    }\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/common/io/EncryptedData.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.io;\n\nimport org.apache.commons.lang.builder.ToStringBuilder;\n\nimport com.alibaba.otter.shared.common.utils.OtterToStringStyle;\n\n/**\n * 加密后的数据对象\n * \n * @author jianghang 2011-10-9 下午06:10:43\n * @version 4.0.0\n */\npublic class EncryptedData {\n\n    private final String crc;\n    private final byte[] data;\n    private final String key;\n\n    public EncryptedData(byte[] data, String key, String crc){\n        this.data = data;\n        this.key = key;\n        this.crc = crc;\n    }\n\n    public String getCrc() {\n        return crc;\n    }\n\n    public byte[] getData() {\n        return data;\n    }\n\n    public String getKey() {\n        return key;\n    }\n\n    public String toString() {\n        return ToStringBuilder.reflectionToString(this, OtterToStringStyle.DEFAULT_STYLE);\n    }\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/common/io/compress/Compressor.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.io.compress;\n\nimport java.io.File;\nimport java.io.InputStream;\nimport java.io.OutputStream;\n\nimport com.alibaba.otter.node.etl.common.io.compress.exception.CompressException;\n\n/**\n * The Compressor Interface defines all operations for the compress/decompress actions.\n */\npublic interface Compressor {\n\n    /**\n     * Compresses this file and returns an InputStream to the compressed File\n     * \n     * @param input File to compress\n     * @return InputStream of the compressed file\n     * @throws CompressException if the Compressor reports an error\n     */\n    public InputStream compress(File input) throws CompressException;\n\n    /**\n     * Compresses this InputStream and returns an InputStream to the compressed file\n     * \n     * @param input Stream to compress\n     * @return Stream to the compressed file\n     * @throws CompressException if the Compressor reports an error\n     */\n    public InputStream compress(InputStream input) throws CompressException;\n\n    /**\n     * Compresses this bytes and returns an bytes\n     * \n     * @param data\n     * @return\n     * @throws CompressException\n     */\n    public byte[] compress(byte[] data) throws CompressException;\n\n    /**\n     * Compresses the file input and creates a file in the same directory with the default file extension in its name.\n     * \n     * @param input the file to compress\n     * @throws CompressException if the Compressor reports an error\n     */\n    public void compressToHere(File input) throws CompressException;\n\n    /**\n     * Creates the file \"output\" with the compressed content of file \"input\"\n     * \n     * @param input the file to compress\n     * @param output the file to create\n     * @throws CompressException if the Compressor reports an error\n     */\n    public void compressTo(File input, File output) throws CompressException;\n\n    /**\n     * Compresses the input stream and writes the compressed bytes to the output stream. This method must be implemented\n     * by all new compressortypes.\n     * \n     * @param input InputStream to compress to\n     * @param output OutputStream to which the byte shall be written\n     * @throws CompressException if the Compressor reports an error\n     */\n    public void compressTo(InputStream input, OutputStream output) throws CompressException;\n\n    /**\n     * Decompresses a file and returns an InputStream\n     * \n     * @param input file to decompress\n     * @return the decompressed file as an inputstream\n     */\n    public InputStream decompress(File input) throws CompressException;\n\n    /**\n     * Decompresses a file and returns an InputStream\n     * \n     * @param input inputstream to decompress\n     * @return the decompressed InputStream\n     */\n    public InputStream decompress(InputStream input) throws CompressException;\n\n    /**\n     * Decompresses this bytes and returns an bytes\n     * \n     * @param data\n     * @return\n     * @throws CompressException\n     */\n    public byte[] decompress(byte[] data) throws CompressException;\n\n    /**\n     * Decompresses this file and writes the decompressed byte to the output file\n     * \n     * @param input File to decompress\n     * @param output File to write the decompressed bytes to\n     * @throws CompressException if the Compressor reports an error\n     */\n    public void decompressTo(File input, File output) throws CompressException;\n\n    /**\n     * Decompresses this file and writes the decompressed file to the output-stream\n     * \n     * @param input Stream to decompress\n     * @param output Stream to write the decompressed bytes to\n     * @throws CompressException if the Compressor reports an error\n     */\n    public void decompressTo(InputStream input, OutputStream output) throws CompressException;\n\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/common/io/compress/exception/CompressException.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.io.compress.exception;\n\nimport org.apache.commons.lang.exception.NestableRuntimeException;\n\n/**\n * 压缩异常类\n */\npublic class CompressException extends NestableRuntimeException {\n\n    private static final long serialVersionUID = -7288830284122672209L;\n\n    private String            errorCode;\n    private String            errorDesc;\n\n    public CompressException(String errorCode){\n        super(errorCode);\n    }\n\n    public CompressException(String errorCode, Throwable cause){\n        super(errorCode, cause);\n    }\n\n    public CompressException(String errorCode, String errorDesc){\n        super(errorCode + \":\" + errorDesc);\n    }\n\n    public CompressException(String errorCode, String errorDesc, Throwable cause){\n        super(errorCode + \":\" + errorDesc, cause);\n    }\n\n    public CompressException(Throwable cause){\n        super(cause);\n    }\n\n    public String getErrorCode() {\n        return errorCode;\n    }\n\n    public String getErrorDesc() {\n        return errorDesc;\n    }\n\n    @Override\n    public Throwable fillInStackTrace() {\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/common/io/compress/impl/AbstractCompressor.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.io.compress.impl;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.ByteArrayOutputStream;\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileNotFoundException;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\n\nimport org.apache.commons.io.IOUtils;\n\nimport com.alibaba.otter.node.etl.common.io.compress.Compressor;\nimport com.alibaba.otter.node.etl.common.io.compress.exception.CompressException;\n\n/**\n * AbstractCompressor handles all compression/decompression actions on an abstract basis.\n */\npublic abstract class AbstractCompressor extends PackableObject implements Compressor {\n\n    public AbstractCompressor(){\n        super();\n    }\n\n    /**\n     * Returns a String with the default file extension for this compressor. For example, a zip-files default file\n     * extension would be \"zip\" (without leading dot).\n     * \n     * @return the default file extension\n     */\n    public abstract String getDefaultFileExtension();\n\n    public InputStream compress(InputStream input) throws CompressException {\n        FileOutputStream output = null;\n        try {\n            File temp = File.createTempFile(\"compress_\", \"jkt\");\n            output = new FileOutputStream(temp);\n            //转化为流进行处理\n            compressTo(input, output);\n            return new FileInputStream(temp);\n        } catch (IOException e) {\n            throw new CompressException(\"An I/O Exception has occured\", e);\n        } finally {\n            IOUtils.closeQuietly(output);\n        }\n    }\n\n    public byte[] compress(byte[] data) throws CompressException {\n        ByteArrayInputStream input = new ByteArrayInputStream(data);\n        ByteArrayOutputStream output = new ByteArrayOutputStream();\n        try {\n            this.compressTo(input, output);\n            return output.toByteArray();\n        } finally {\n            IOUtils.closeQuietly(input);\n            IOUtils.closeQuietly(output);\n        }\n    }\n\n    public void compressToHere(File input) throws CompressException {\n        String pathToFile = input.getAbsolutePath();\n        File output = new File(pathToFile);\n        this.compressTo(input, output);\n    }\n\n    public void compressTo(File input, File output) throws CompressException {\n        FileOutputStream outputStream = null;\n        FileInputStream inputStream = null;\n        try {\n            outputStream = new FileOutputStream(output);\n            inputStream = new FileInputStream(input);\n            this.compressTo(inputStream, outputStream);\n        } catch (FileNotFoundException e) {\n            throw new CompressException(\"File not found\", e);\n        } finally {\n            IOUtils.closeQuietly(inputStream);\n            IOUtils.closeQuietly(outputStream);\n        }\n    }\n\n    public InputStream compress(File input) throws CompressException {\n        FileInputStream inputStream = null;\n        try {\n            inputStream = new FileInputStream(input);\n            return this.compress(inputStream);\n        } catch (FileNotFoundException e) {\n            throw new CompressException(\"File not found\", e);\n        } finally {\n            IOUtils.closeQuietly(inputStream);\n        }\n    }\n\n    public InputStream decompress(File input) throws CompressException {\n        File temp = null;\n        InputStream result = null;\n        try {\n            temp = File.createTempFile(\"compress_\", \"jkt\");\n            this.decompressTo(input, temp);\n            result = new FileInputStream(temp);\n        } catch (IOException e) {\n            throw new CompressException(\"Error while creating a temporary file\", e);\n        }\n\n        return result;\n    }\n\n    public InputStream decompress(InputStream input) throws CompressException {\n        File temp = null;\n        InputStream result = null;\n        FileOutputStream output = null;\n        try {\n            temp = File.createTempFile(\"compress_\", \"jkt\");\n            output = new FileOutputStream(temp);\n            this.decompressTo(input, output);\n            result = new FileInputStream(temp);\n        } catch (IOException e) {\n            throw new CompressException(\"Error while creating a temporary file\", e);\n        } finally {\n            IOUtils.closeQuietly(output);\n        }\n\n        return result;\n    }\n\n    public byte[] decompress(byte[] data) throws CompressException {\n        ByteArrayInputStream input = new ByteArrayInputStream(data);\n        ByteArrayOutputStream output = new ByteArrayOutputStream();\n        try {\n            this.decompressTo(input, output);\n            return output.toByteArray();\n        } finally {\n            IOUtils.closeQuietly(input);\n            IOUtils.closeQuietly(output);\n        }\n    }\n\n    public void decompressTo(File input, File output) throws CompressException {\n        FileInputStream inputStream = null;\n        FileOutputStream outputStream = null;\n        try {\n            outputStream = new FileOutputStream(output);\n            inputStream = new FileInputStream(input);\n            decompressTo(inputStream, outputStream);\n        } catch (FileNotFoundException e) {\n            throw new CompressException(\"File could not be found\", e);\n        } finally {\n            IOUtils.closeQuietly(inputStream);\n            IOUtils.closeQuietly(outputStream);\n        }\n    }\n\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/common/io/compress/impl/PackableObject.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.io.compress.impl;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.util.Iterator;\nimport java.util.List;\n\nimport org.apache.commons.io.IOUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Abstract super object for Compressor and Archiver classes.\n */\npublic abstract class PackableObject {\n\n    /* Type for archive choosing: String */\n    protected static final int CHOOSE_EXTENSION = 1;\n\n    /* Type for archive choosing: Long */\n    protected static final int CHOOSE_NAME      = 2;\n\n    protected final Logger     logger           = LoggerFactory.getLogger(getClass());\n\n    /**\n     * Header byte array for this archive.\n     */\n    public abstract byte[] getHeader();\n\n    /**\n     * Returns the default FileExtension for this archive, for example \"zip\",\n     * \"tar\"...\n     */\n    public abstract String getDefaultFileExtension();\n\n    /**\n     * Returns the ArchiveName for this archive.\n     */\n    public abstract String getName();\n\n    /**\n     * String Chooser.\n     */\n    protected boolean isPackableWith(Object value, int choose) {\n        if (value == null) {\n            return false;\n        }\n\n        if (choose == CHOOSE_EXTENSION) {\n            if (value.equals(getDefaultFileExtension())) {\n                return true;\n            }\n        } else if (choose == CHOOSE_NAME) {\n            if (value.equals(getName())) {\n                return true;\n            }\n        }\n\n        return false;\n    }\n\n    /**\n     * Compares a file to a list of packables and identifies an object by\n     * header. If no matching header is found, it identifies the file by file\n     * extension. If identification was not successfull, null is returned\n     * \n     * @param file the file to identify\n     * @param packables a list of packables\n     * @return a matching packable object, or null\n     * @throws IOException\n     */\n    public static PackableObject identifyByHeader(File file, List packables) throws IOException {\n        FileInputStream fis = null;\n        try {\n            /* Archive result */\n            // PackableObject packable = null;\n            // identify archive by header\n            fis = new FileInputStream(file);\n            byte[] headerBytes = new byte[20];\n            fis.read(headerBytes, 0, 20);\n\n            Iterator iter = packables.iterator();\n            while (iter.hasNext()) {\n                PackableObject p = (PackableObject) iter.next();\n                byte[] fieldHeader = p.getHeader();\n\n                if (fieldHeader != null) {\n                    if (compareByteArrays(headerBytes, fieldHeader)) {\n                        return p;\n                    }\n                }\n            }\n\n            // if we couldn't find an archiver by header bytes, we'll give it a\n            // try\n            // with the default name extension. This is useful, cause some\n            // archives\n            // like tar have no header.\n            String name = file.getName();\n            String extension = null;\n            String[] s = name.split(\"\\\\.\");\n\n            if (s.length > 1) {\n                extension = s[s.length - 1];\n            }\n\n            Iterator it = packables.iterator();\n\n            while (it.hasNext()) {\n                PackableObject p = (PackableObject) it.next();\n                if (p.isPackableWith(extension, PackableObject.CHOOSE_EXTENSION)) {\n                    return p;\n                }\n            }\n            // No implementation found\n            return null;\n        } finally {\n            IOUtils.closeQuietly(fis);\n        }\n    }\n\n    private static boolean compareByteArrays(byte[] source, byte[] match) {\n        int i = 0;\n        while ((source.length < i) || (i < match.length)) {\n            if (source[i] != match[i]) {\n                return false;\n            }\n            i++;\n        }\n        return true;\n    }\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/common/io/compress/impl/bzip2/BZip2Compressor.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.io.compress.impl.bzip2;\n\nimport java.io.InputStream;\nimport java.io.OutputStream;\n\nimport org.apache.commons.compress.compressors.bzip2.BZip2CompressorInputStream;\nimport org.apache.commons.compress.compressors.bzip2.BZip2CompressorOutputStream;\n\nimport com.alibaba.otter.node.etl.common.io.compress.exception.CompressException;\nimport com.alibaba.otter.node.etl.common.io.compress.impl.AbstractCompressor;\nimport com.alibaba.otter.shared.common.utils.NioUtils;\n\n/**\n * Implementation of the Compressor Interface for BZip2.\n * \n * @author brave.taoy\n * @author jianghang 2011-10-9 下午02:33:57\n * @version 4.0.0\n */\npublic class BZip2Compressor extends AbstractCompressor {\n\n    /* Default file extension */\n    private static String       DEFAULT_FILE_EXTENSION = \"bz2\";\n\n    /* Header BZ as byte-Array */\n    private static final byte[] HEADER                 = new byte[] { (byte) 'B', (byte) 'Z', (byte) 'h' };\n\n    /* Name of this implementation */\n    private static final String NAME                   = \"bz2\";\n\n    public BZip2Compressor(){\n        super();\n    }\n\n    public void compressTo(InputStream in, OutputStream out) throws CompressException {\n        BZip2CompressorOutputStream outputBZStream = null;\n        try {\n            outputBZStream = new BZip2CompressorOutputStream(out);\n            NioUtils.copy(in, outputBZStream);\n            outputBZStream.finish();\n        } catch (Exception e) {\n            throw new CompressException(\"bzip_compress_error\", e);\n        }\n    }\n\n    public void decompressTo(InputStream in, OutputStream out) throws CompressException {\n        BZip2CompressorInputStream inputStream = null;\n        try {\n            inputStream = new BZip2CompressorInputStream(in);\n            NioUtils.copy(inputStream, out);\n        } catch (Exception e) {\n            throw new CompressException(\"bzip_decompress_error\", e);\n        }\n    }\n\n    public byte[] getHeader() {\n        return HEADER;\n    }\n\n    public String getName() {\n        return NAME;\n    }\n\n    public String getDefaultFileExtension() {\n        return DEFAULT_FILE_EXTENSION;\n    }\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/common/io/compress/impl/gzip/GzipCompressor.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.io.compress.impl.gzip;\n\nimport java.io.InputStream;\nimport java.io.OutputStream;\nimport java.util.zip.Deflater;\nimport java.util.zip.GZIPInputStream;\nimport java.util.zip.GZIPOutputStream;\n\nimport com.alibaba.otter.node.etl.common.io.compress.exception.CompressException;\nimport com.alibaba.otter.node.etl.common.io.compress.impl.AbstractCompressor;\nimport com.alibaba.otter.shared.common.utils.NioUtils;\n\n/**\n * 基于gzip的压缩实现\n * \n * @author jianghang 2011-10-9 下午03:17:08\n * @version 4.0.0\n */\npublic class GzipCompressor extends AbstractCompressor {\n\n    /* Default file extension */\n    private static String       DEFAULT_FILE_EXTENSION = \"gzip\";\n\n    /* Name of this implementation */\n    private static final String NAME                   = \"gzip\";\n\n    /*\n     * GZIP header magic number.\n     */\n    private final static int    GZIP_MAGIC             = 0x8b1f;\n\n    /* Header BZ as byte-Array */\n    private static final byte[] HEADER                 = new byte[] { (byte) GZIP_MAGIC, // Magic number (short)\n            (byte) (GZIP_MAGIC >> 8), // Magic number (short)\n            Deflater.DEFLATED, // Compression method (CM)\n            0, // Flags (FLG)\n            0, // Modification time MTIME (int)\n            0, // Modification time MTIME (int)\n            0, // Modification time MTIME (int)\n            0, // Modification time MTIME (int)\n            0, // Extra flags (XFLG)\n            0                                         // Operating system (OS)\n                                                       };\n\n    public GzipCompressor(){\n        super();\n    }\n\n    public void compressTo(InputStream in, OutputStream out) throws CompressException {\n        GZIPOutputStream gzipOut = null;\n        try {\n            gzipOut = new GZIPOutputStream(out);\n            NioUtils.copy(in, gzipOut);\n            gzipOut.finish(); //需要使用finish\n        } catch (Exception e) {\n            throw new CompressException(\"gzip_compress_error\", e);\n        }\n    }\n\n    public void decompressTo(InputStream in, OutputStream out) throws CompressException {\n        GZIPInputStream gzipin = null;\n        try {\n            gzipin = new GZIPInputStream(in);\n            NioUtils.copy(gzipin, out);\n            out.flush();\n        } catch (Exception e) {\n            throw new CompressException(\"gzip_decompress_error\", e);\n        }\n    }\n\n    public byte[] getHeader() {\n        return HEADER;\n    }\n\n    public String getName() {\n        return NAME;\n    }\n\n    public String getDefaultFileExtension() {\n        return DEFAULT_FILE_EXTENSION;\n    }\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/common/io/crypto/AESException.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.io.crypto;\n\nimport org.apache.commons.lang.exception.NestableRuntimeException;\n\n/**\n * DES exception\n */\npublic class AESException extends NestableRuntimeException {\n\n    private static final long serialVersionUID = -7288830284122672209L;\n\n    private String            errorCode;\n    private String            errorDesc;\n\n    public AESException(String errorCode){\n        super(errorCode);\n    }\n\n    public AESException(String errorCode, Throwable cause){\n        super(errorCode, cause);\n    }\n\n    public AESException(String errorCode, String errorDesc){\n        super(errorCode + \":\" + errorDesc);\n    }\n\n    public AESException(String errorCode, String errorDesc, Throwable cause){\n        super(errorCode + \":\" + errorDesc, cause);\n    }\n\n    public AESException(Throwable cause){\n        super(cause);\n    }\n\n    public String getErrorCode() {\n        return errorCode;\n    }\n\n    public String getErrorDesc() {\n        return errorDesc;\n    }\n\n    @Override\n    public Throwable fillInStackTrace() {\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/common/io/crypto/AESUtils.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.io.crypto;\n\nimport java.security.InvalidKeyException;\nimport java.security.NoSuchAlgorithmException;\n\nimport javax.crypto.BadPaddingException;\nimport javax.crypto.Cipher;\nimport javax.crypto.IllegalBlockSizeException;\nimport javax.crypto.KeyGenerator;\nimport javax.crypto.NoSuchPaddingException;\nimport javax.crypto.SecretKey;\nimport javax.crypto.spec.SecretKeySpec;\n\nimport com.alibaba.otter.shared.common.utils.ByteUtils;\n\n/**\n * 加密算法的实现\n * \n * @author jianghang 2011-10-9 下午05:31:00\n * @version 4.0.0\n */\npublic class AESUtils {\n\n    private static final String ENCRYPTION_ALGORITHM = \"AES\";\n    private static final int    KEY_SIZE             = 128;\n    private byte[]              secretKey;\n\n    /**\n     * 生成AES密钥\n     * \n     * @return Secret key\n     * @throws AESException AES exception\n     */\n    public byte[] generateSecretKey() throws AESException {\n        try {\n            // Get the KeyGenerator\n            KeyGenerator kgen = KeyGenerator.getInstance(ENCRYPTION_ALGORITHM);\n            kgen.init(KEY_SIZE); // 192 and 256 bits may not be available\n            // Generate the secret key specs.\n            SecretKey skey = kgen.generateKey();\n            secretKey = skey.getEncoded();\n            return secretKey;\n        } catch (NoSuchAlgorithmException e) {\n            throw new AESException(e);\n        }\n    }\n\n    /**\n     * 加密byte数据\n     * \n     * @param plainData\n     * @return\n     * @throws AESException\n     */\n    public byte[] encrypt(byte[] plainData) throws AESException {\n        try {\n            SecretKeySpec skeySpec = new SecretKeySpec(secretKey, ENCRYPTION_ALGORITHM);\n            // Instantiate the cipher\n            Cipher cipher = Cipher.getInstance(ENCRYPTION_ALGORITHM);\n            cipher.init(Cipher.ENCRYPT_MODE, skeySpec);\n            return cipher.doFinal(plainData);\n        } catch (NoSuchAlgorithmException e) {\n            throw new AESException(e);\n        } catch (NoSuchPaddingException e) {\n            throw new AESException(e);\n        } catch (InvalidKeyException e) {\n            throw new AESException(e);\n        } catch (IllegalBlockSizeException e) {\n            throw new AESException(e);\n        } catch (BadPaddingException e) {\n            throw new AESException(e);\n        }\n    }\n\n    /**\n     * 解密byte数据\n     * \n     * @param encrypted\n     * @return\n     * @throws AESException\n     */\n    public byte[] decrypt(byte[] encrypted) throws AESException {\n        try {\n            SecretKeySpec skeySpec = new SecretKeySpec(secretKey, ENCRYPTION_ALGORITHM);\n            Cipher cipher = Cipher.getInstance(ENCRYPTION_ALGORITHM);\n            cipher.init(Cipher.DECRYPT_MODE, skeySpec);\n            return cipher.doFinal(encrypted);\n        } catch (NoSuchAlgorithmException e) {\n            throw new AESException(e);\n        } catch (NoSuchPaddingException e) {\n            throw new AESException(e);\n        } catch (InvalidKeyException e) {\n            throw new AESException(e);\n        } catch (IllegalBlockSizeException e) {\n            throw new AESException(e);\n        } catch (BadPaddingException e) {\n            throw new AESException(e);\n        }\n    }\n\n    /**\n     * 获取生成的加密密钥\n     * \n     * @return\n     */\n    public String getSecretyKeyString() {\n        return ByteUtils.bytesToBase64String(secretKey);\n    }\n\n    public byte[] getSecretKey() {\n        return secretKey;\n    }\n\n    public void setSecretKey(byte[] secretKey) {\n        this.secretKey = secretKey;\n    }\n\n    public void setSecretKeyString(String keyString) {\n        this.secretKey = ByteUtils.base64StringToBytes(keyString);\n    }\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/common/io/download/DataRetriever.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.io.download;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.util.Observer;\n\nimport com.alibaba.otter.node.etl.common.io.download.exception.DataRetrieveException;\n\n/**\n * 数据下载接口\n * \n * @author brave.taoy\n */\npublic interface DataRetriever {\n\n    /**\n     * This type of notification will be send if the progress changes.\n     */\n    public final static Integer NOTIFICATION_PROGRESS = 100;\n\n    // control retriever\n\n    /**\n     * Advise the Retriever to connect to the data source.\n     */\n    public void connect() throws DataRetrieveException;\n\n    /**\n     * Do retrieve data\n     */\n    public void doRetrieve() throws DataRetrieveException;\n\n    /**\n     * Returns true if the data source contains any data to be read.\n     */\n    public boolean isDataAvailable() throws DataRetrieveException;\n\n    /**\n     * Returns an byte array for the retrieved data.\n     */\n    public byte[] getDataAsByteArray() throws DataRetrieveException;\n\n    /**\n     * Returns a file\n     */\n    public File getDataAsFile() throws DataRetrieveException;\n\n    /**\n     * Disconnect from the data source.\n     */\n    public void disconnect() throws DataRetrieveException;\n\n    /**\n     * Aborts the current retrieving.\n     */\n    public void abort();\n\n    /**\n     * Returns the content length in bytes.\n     */\n    public long getContentLength() throws IOException;\n\n    public void addObserver(Observer o);\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/common/io/download/DataRetrieverFactory.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.io.download;\n\nimport org.apache.commons.lang.StringUtils;\nimport org.apache.commons.lang.SystemUtils;\n\nimport com.alibaba.otter.node.etl.common.io.download.exception.DataRetrieveException;\nimport com.alibaba.otter.node.etl.common.io.download.impl.aria2c.Aria2cRetriever;\nimport com.alibaba.otter.shared.common.model.config.parameter.SystemParameter.RetrieverType;\n\n/**\n * DataRetriever工厂，选择合适的下载器\n * \n * @author jianghang 2011-11-3 下午07:42:05\n * @version 4.0.0\n */\npublic class DataRetrieverFactory {\n\n    public DataRetriever createRetriever(RetrieverType type, String url, String targetDir) {\n        if (type.isAria2c()) {\n            return getAria2cRetriever(type.getExe(), url, targetDir);\n        } else {\n            // 其他的类型\n        }\n\n        throw new DataRetrieveException(\"no DataRetriever for[\" + type + \"]\");\n    }\n\n    private DataRetriever getAria2cRetriever(String cmd, String url, String targetDir) {\n        if (StringUtils.isEmpty(cmd)) {\n            cmd = (SystemUtils.IS_OS_WINDOWS ? cmd + \".exe\" : cmd);\n        }\n        return new Aria2cRetriever(cmd, url, targetDir);\n    }\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/common/io/download/Download.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.io.download;\n\n//~--- JDK imports ------------------------------------------------------------\n\nimport java.io.File;\n\n/**\n * @author brave.taoy\n */\npublic interface Download {\n\n    public long getContentLength();\n\n    public File getAssociatedLocalFile();\n\n    public byte[] getAssociatedMemoryData();\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/common/io/download/DownloadStatus.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.io.download;\n\n/**\n * Enum description\n */\npublic enum DownloadStatus {\n\n    RUNNING(\"Downloading\"), EXCEPTION(\"Error\"), COMPLETE(\"Done\"), ABORT(\"Abort\"), CONNECTING(\"Connecting\"),\n    IDLE(\"Idle\"), PAUSED(\"Paused\"), RETRYING(\"Retrying\"), REDIRECTING(\"Redirecting\"), CONNECTED(\"Connected\"),\n    PAUSING(\"Pausing\");\n\n    private String title;\n\n    DownloadStatus(String title){\n        this.title = title;\n    }\n\n    public String title() {\n        return title;\n    }\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/common/io/download/exception/DataRetrieveException.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.io.download.exception;\n\nimport org.apache.commons.lang.exception.NestableRuntimeException;\n\n/**\n * @author jianghang 2011-10-10 下午05:30:17\n * @version 4.0.0\n */\npublic class DataRetrieveException extends NestableRuntimeException {\n\n    private static final long serialVersionUID = -7288830284122672209L;\n\n    private String            errorCode;\n    private String            errorDesc;\n\n    public DataRetrieveException(String errorCode){\n        super(errorCode);\n    }\n\n    public DataRetrieveException(String errorCode, Throwable cause){\n        super(errorCode, cause);\n    }\n\n    public DataRetrieveException(String errorCode, String errorDesc){\n        super(errorCode + \":\" + errorDesc);\n    }\n\n    public DataRetrieveException(String errorCode, String errorDesc, Throwable cause){\n        super(errorCode + \":\" + errorDesc, cause);\n    }\n\n    public DataRetrieveException(Throwable cause){\n        super(cause);\n    }\n\n    public String getErrorCode() {\n        return errorCode;\n    }\n\n    public String getErrorDesc() {\n        return errorDesc;\n    }\n\n    @Override\n    public Throwable fillInStackTrace() {\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/common/io/download/impl/AbstractCommandDownload.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.io.download.impl;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.util.Observable;\nimport java.util.concurrent.atomic.AtomicBoolean;\n\nimport org.apache.commons.lang.SystemUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.alibaba.otter.node.etl.common.io.download.Download;\nimport com.alibaba.otter.node.etl.common.io.download.DownloadStatus;\nimport com.alibaba.otter.node.etl.common.io.download.exception.DataRetrieveException;\nimport com.alibaba.otter.shared.common.utils.NioUtils;\nimport com.alibaba.otter.shared.common.utils.cmd.Exec;\n\npublic abstract class AbstractCommandDownload extends Observable implements Download {\n\n    protected final Logger   logger = LoggerFactory.getLogger(getClass());\n    protected AtomicBoolean  aborted;\n    protected String         cmd;\n    protected AtomicBoolean  completed;\n    protected AtomicBoolean  paused;\n    protected DownloadStatus status;\n    protected String         targetDir;\n    protected File           targetFile;\n    protected String         url;\n\n    public AbstractCommandDownload(String cmdPath, String url, String dir, String[] params){\n        this.url = url;\n        // 下载文件名\n        String fileName = url.substring(url.lastIndexOf(\"/\") + 1).replace(\"%20\", \" \");\n        // 下载到本地目录\n        this.targetDir = dir;\n        // 目标文件\n        this.targetFile = new File(this.targetDir, fileName);\n        this.buildCmd(cmdPath, params);\n        // 下载完成状态\n        completed = new AtomicBoolean(false);\n        // 无法下载\n        aborted = new AtomicBoolean(false);\n        // 下载发生异常, 可断点恢复\n        paused = new AtomicBoolean(false);\n        // idle\n        status = DownloadStatus.IDLE;\n    }\n\n    public void download() throws IOException {\n        // 准备下载\n        notifyStatusChange(DownloadStatus.CONNECTING);\n        Exec.Result result = null;\n        try {\n            result = Exec.execute(cmd);\n            if (false == this.isSuccess(result.getExitCode())) {\n                aborted.set(true);\n                notifyException(new DataRetrieveException(result.toString()));\n                notifyStatusChange(DownloadStatus.EXCEPTION);\n            } else {\n                this.analyzeResult(result);\n                this.notifyMessage(result.toString());\n\n                if (aborted.get()) {\n                    // 中断\n                    notifyStatusChange(DownloadStatus.ABORT);\n                } else if (paused.get()) {\n                    // 暂停\n                    notifyStatusChange(DownloadStatus.PAUSED);\n                } else {\n                    // 下载完成\n                    notifyStatusChange(DownloadStatus.COMPLETE);\n                }\n            }\n        } catch (Exception ex) {\n            aborted.set(true);\n            notifyException(new DataRetrieveException((result != null) ? ex.getMessage() + SystemUtils.LINE_SEPARATOR\n                                                                         + result.toString() : ex.getMessage(), ex));\n        }\n\n    }\n\n    protected abstract void buildCmd(String cmdPath, String[] params);\n\n    protected abstract void analyzeResult(Exec.Result result);\n\n    protected boolean isSuccess(int exitValue) {\n        return exitValue == 0;\n    }\n\n    public DownloadStatus getStatus() {\n        return status;\n    }\n\n    public boolean isPaused() {\n        return paused.get();\n    }\n\n    public boolean isAborted() {\n        return aborted.get();\n    }\n\n    public boolean isCompleted() {\n        return completed.get();\n    }\n\n    public String getUrl() {\n        return url;\n    }\n\n    public byte[] getAssociatedMemoryData() {\n        if (this.targetFile.exists()) {\n            try {\n                return NioUtils.read(this.targetFile);\n            } catch (IOException e) {\n                throw new DataRetrieveException(e);\n            }\n        } else {\n            return new byte[] { (byte) 0 };\n        }\n    }\n\n    public long getContentLength() {\n        if (this.targetFile.exists()) {\n            return this.targetFile.length();\n        }\n        return 0;\n    }\n\n    public File getAssociatedLocalFile() {\n        return this.targetFile;\n    }\n\n    protected void notifyMessage(String msg) {\n        notifyEvent(msg);\n    }\n\n    protected void notifyStatusChange(DownloadStatus status) {\n        this.status = status;\n        notifyEvent(status);\n    }\n\n    protected void notifyException(Exception ex) {\n        notifyEvent(ex);\n    }\n\n    protected void notifyEvent(Object arg) {\n        setChanged();\n        notifyObservers(arg);\n    }\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/common/io/download/impl/AbstractCommandRetriever.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.io.download.impl;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.util.Observer;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.alibaba.otter.node.etl.common.io.download.DataRetriever;\nimport com.alibaba.otter.node.etl.common.io.download.exception.DataRetrieveException;\nimport com.alibaba.otter.node.etl.common.io.download.impl.observer.DefaultExceptionObserver;\nimport com.alibaba.otter.node.etl.common.io.download.impl.observer.DefaultProgressObserver;\nimport com.alibaba.otter.node.etl.common.io.download.impl.observer.DefaultStatusObserver;\nimport com.alibaba.otter.shared.common.utils.NioUtils;\n\n/**\n * 基于命令行工具的下载\n * \n * @author jianghang 2011-10-10 下午05:43:24\n * @version 4.0.0\n */\npublic abstract class AbstractCommandRetriever implements DataRetriever {\n\n    protected final Logger            logger = LoggerFactory.getLogger(getClass());\n    protected AbstractCommandDownload download;\n\n    public AbstractCommandRetriever(String cmdPath, String url, String targetDir){\n        this.buildDownload(cmdPath, url, targetDir, null);\n    }\n\n    public AbstractCommandRetriever(String cmdPath, String url, String targetDir, String[] params){\n        this.buildDownload(cmdPath, url, targetDir, params);\n    }\n\n    protected abstract void buildDownload(String cmdPath, String url, String targetDir, String[] params);\n\n    public void connect() throws DataRetrieveException {\n        this.download.addObserver(new DefaultStatusObserver(logger));\n        this.download.addObserver(new DefaultProgressObserver(logger));\n        this.download.addObserver(new DefaultExceptionObserver(logger));\n    }\n\n    public void doRetrieve() throws DataRetrieveException {\n        try {\n            download.download();\n        } catch (IOException e) {\n            //ignore\n        }\n\n        if (download.isCompleted()) {\n            //\n        } else if (download.isPaused()) {\n            throw new DataRetrieveException(\"retry 3 times still have err, paused.\");\n        } else if (download.isAborted()) {\n            throw new DataRetrieveException(\"aborted for some configration error.\");\n        }\n    }\n\n    public void disconnect() {\n        this.download = null;\n    }\n\n    public void abort() {\n        this.download = null;\n    }\n\n    public void addObserver(Observer o) {\n        this.download.addObserver(o);\n    }\n\n    public long getContentLength() throws IOException {\n        if (download.isCompleted()) {\n            return download.getAssociatedLocalFile().length();\n        }\n\n        return -1;\n    }\n\n    public byte[] getDataAsByteArray() {\n        try {\n            return NioUtils.read(this.getDataAsFile());\n        } catch (IOException e) {\n            throw new DataRetrieveException(e);\n        }\n    }\n\n    public File getDataAsFile() {\n        return download.getAssociatedLocalFile();\n    }\n\n    public boolean isDataAvailable() {\n        return download.isCompleted();\n    }\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/common/io/download/impl/FileRetriever.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.io.download.impl;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.util.Observer;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.alibaba.otter.node.etl.common.io.download.DataRetriever;\nimport com.alibaba.otter.node.etl.common.io.download.exception.DataRetrieveException;\nimport com.alibaba.otter.shared.common.utils.NioUtils;\n\n/**\n * 从文件系统中获取数据\n * \n * @author brave.taoy\n * @author jianghang 2011-10-10 下午06:14:46\n * @version 4.0.0\n */\npublic class FileRetriever implements DataRetriever {\n\n    private static Logger logger = LoggerFactory.getLogger(FileRetriever.class);\n    private boolean       mAbort = false;\n    private long          mByteOffset;\n    private File          mFile;\n\n    public FileRetriever(File pFile){\n        mFile = pFile;\n    }\n\n    public void abort() {\n        mAbort = true;\n    }\n\n    public void connect() {\n        if (mAbort) {\n            throw new IllegalStateException(\"Retriever aborted\");\n        }\n\n        if (logger.isDebugEnabled()) {\n            logger.debug(\"Open file for retrieval: \" + mFile);\n        }\n    }\n\n    public void doRetrieve() {\n    }\n\n    public void disconnect() {\n        if (logger.isDebugEnabled()) {\n            logger.debug(\"Close file \" + mFile.getAbsolutePath());\n        }\n    }\n\n    public boolean isDataAvailable() {\n        return mFile.exists();\n    }\n\n    public byte[] getDataAsByteArray() {\n        try {\n            return NioUtils.read(mFile);\n        } catch (IOException e) {\n            throw new DataRetrieveException(e);\n        }\n    }\n\n    public File getDataAsFile() {\n        return mFile;\n    }\n\n    public void setBytesToSkip(long pBytesToSkip) {\n        mByteOffset = pBytesToSkip;\n    }\n\n    public long getBytesSkipped() {\n        return mByteOffset;\n    }\n\n    public long getContentLength() {\n        return mFile.length();\n    }\n\n    public void addObserver(Observer o) {\n    }\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/common/io/download/impl/aria2c/Aria2cConfig.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.io.download.impl.aria2c;\n\n/**\n * @author brave.taoy\n */\npublic interface Aria2cConfig {\n\n    public static final String[] ARIA2C_PARAM = new String[] {\n                                              // 不加载配置文件\n            \"--no-conf\",\n\n            // 每个url下载线程数 1-16\n            \"-s 16\",\n\n            // 最大并发下载数 1-45\n            \"-j 50\",\n\n            // 单机最大连接数\n            \"-x 16\",\n\n            // 每个链接下载的数据大小\n            \"-k 2M\",\n\n            // 连接超时 单位：秒\n            \"--timeout=600\",\n\n            // 最大重试次数\n            \"--max-tries=5\",\n\n            // 停止下载 单位：秒\n            \"--stop=1800\",\n\n            // 覆盖已存在文件\n            \"--allow-overwrite=true\",\n\n            // Set max download speed\n            // \"--max-overall-download-limit=512K\",\n\n            // http长连接\n            \"--enable-http-keep-alive=true\",\n\n            // 日志级别\n            \"--log-level=warn\",              };\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/common/io/download/impl/aria2c/Aria2cDownload.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.io.download.impl.aria2c;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.apache.commons.lang.StringUtils;\nimport org.apache.commons.lang.SystemUtils;\n\nimport com.alibaba.otter.node.etl.common.io.download.Download;\nimport com.alibaba.otter.node.etl.common.io.download.impl.AbstractCommandDownload;\nimport com.alibaba.otter.shared.common.utils.cmd.Exec;\n\n/**\n * 文档：http://aria2.sourceforge.net/\n * \n * @author jianghang 2011-10-10 下午06:24:11\n * @version 4.0.0\n */\npublic class Aria2cDownload extends AbstractCommandDownload implements Aria2cConfig, Download {\n\n    public Aria2cDownload(String cmdPath, String url, String dir){\n        super(cmdPath, url, dir, null);\n    }\n\n    public Aria2cDownload(String cmdPath, String url, String dir, String[] params){\n        super(cmdPath, url, dir, params);\n    }\n\n    @Override\n    protected void buildCmd(String cmdPath, String[] params) {\n        // 文件存在时，续传\n        boolean retry = targetFile.exists();\n        this.cmd = String.format(\"%s %s-o %s -d %s -l %s/aria2c.log %s %s\", cmdPath, retry ? \"-c \" : \"\",\n                                 targetFile.getName(), this.targetDir, this.targetDir,\n                                 StringUtils.join(((params == null) || (params.length == 0)) ? ARIA2C_PARAM : params,\n                                                  ' '), url);\n    }\n\n    protected void analyzeResult(Exec.Result result) {\n        String[] results = StringUtils.split(result.getStdout(), SystemUtils.LINE_SEPARATOR);\n        List<Aria2cStat> segmentStat = new ArrayList<Aria2cStat>();\n        int pos = 0;\n\n        for (; pos < results.length; pos++) {\n            if (true == results[pos].toLowerCase().startsWith(\"gid|stat\")) {\n                break;\n            }\n        }\n\n        for (pos++; pos < results.length; pos++) {\n            // 下载结束标志\n            // Download Results:\n            // gid|stat|avg speed  |path/URI\n            // ===+====+===========+===========================================================\n            // 1|  OK| 103.7KiB/s|./index.html\n            //\n            // Status Legend:\n            // (OK):download completed.\n            if (true == StringUtils.isNumeric(results[pos].substring(0, 1))) {\n\n                // 分析各个下载块的完成状态\n                String[] status = StringUtils.split(results[pos], \" \\t|\");\n\n                if (status.length > 2) {\n                    if (StringUtils.equalsIgnoreCase(Aria2cStat.OK.name(), status[1])) {\n                        segmentStat.add(Aria2cStat.OK);\n                    } else if (StringUtils.equalsIgnoreCase(Aria2cStat.ERR.name(), status[1])) {\n                        segmentStat.add(Aria2cStat.ERR);\n                    } else if (StringUtils.equalsIgnoreCase(Aria2cStat.INPR.name(), status[1])) {\n                        segmentStat.add(Aria2cStat.INPR);\n                    }\n\n                    logger.warn(results[pos]);\n                } else {\n                    logger.error(\"it seems aria2 changed it's status format: \" + results[pos]);\n                }\n            }\n        }\n\n        int size = segmentStat.size();\n        int errCount = 0;\n        int inprCount = 0;\n        for (int i = 0; i < size; i++) {\n            final Aria2cStat stat = segmentStat.get(i);\n\n            if (Aria2cStat.ERR == stat) {\n                errCount++;\n            } else if (Aria2cStat.INPR == stat) {\n                inprCount++;\n            }\n        }\n\n        // 如果没有异常，则有可能下载模块全部成功或者部分被异常终止\n        if (errCount == 0) {\n            if (inprCount == 0) {\n                // 没有异常，表示下载成功\n                completed.set(true);\n            } else {\n                // 部分被异常终止，可以续传\n                paused.set(true);\n            }\n        } else {\n            if (errCount == size) {\n                // 如果全部下载失败，则存在参数异常\n                aborted.set(true);\n            } else {\n                // 部分成功，可以续传\n                paused.set(true);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/common/io/download/impl/aria2c/Aria2cRetriever.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.io.download.impl.aria2c;\n\nimport com.alibaba.otter.node.etl.common.io.download.impl.AbstractCommandRetriever;\n\npublic class Aria2cRetriever extends AbstractCommandRetriever {\n\n    public static final String NAME = \"aria2c\";\n\n    public Aria2cRetriever(String url, String targetDir){\n        super(NAME, url, targetDir);\n    }\n\n    public Aria2cRetriever(String cmdPath, String url, String targetDir){\n        super(cmdPath, url, targetDir);\n    }\n\n    public Aria2cRetriever(String cmdPath, String url, String targetDir, String[] params){\n        super(cmdPath, url, targetDir, params);\n    }\n\n    @Override\n    protected void buildDownload(String cmdPath, String url, String targetDir, String[] params) {\n        download = new Aria2cDownload(cmdPath, url, targetDir, params);\n    }\n\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/common/io/download/impl/aria2c/Aria2cStat.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.io.download.impl.aria2c;\n\n/**\n * Enum description\n */\npublic enum Aria2cStat {\n    // 下载成功\n    OK,\n\n    // 下载失败\n    ERR,\n\n    // 下载进程异常终止\n    INPR,\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/common/io/download/impl/observer/DefaultExceptionObserver.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.io.download.impl.observer;\n\nimport org.slf4j.Logger;\n\nimport com.alibaba.otter.node.etl.common.io.download.impl.AbstractCommandDownload;\n\n/**\n * @author brave.taoy\n */\npublic class DefaultExceptionObserver extends ExceptionObserver {\n\n    private Logger logger;\n\n    public DefaultExceptionObserver(Logger logger){\n        this.logger = logger;\n    }\n\n    @Override\n    public void exceptionOccured(AbstractCommandDownload download, Exception status) {\n        this.logger.error(status.getMessage(), status);\n    }\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/common/io/download/impl/observer/DefaultProgressObserver.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.io.download.impl.observer;\n\nimport org.slf4j.Logger;\n\nimport com.alibaba.otter.node.etl.common.io.download.impl.AbstractCommandDownload;\n\npublic class DefaultProgressObserver extends ProgressObserver {\n\n    private Logger logger;\n\n    public DefaultProgressObserver(Logger logger){\n        this.logger = logger;\n    }\n\n    @Override\n    public void statusChanged(AbstractCommandDownload download, String msg) {\n        if (logger.isInfoEnabled()) {\n            logger.info(msg);\n        }\n    }\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/common/io/download/impl/observer/DefaultStatusObserver.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.io.download.impl.observer;\n\nimport org.slf4j.Logger;\n\nimport com.alibaba.otter.node.etl.common.io.download.Download;\nimport com.alibaba.otter.node.etl.common.io.download.DownloadStatus;\n\n/**\n * @author brave.taoy\n */\npublic class DefaultStatusObserver extends StatusObserver {\n\n    private Logger logger;\n\n    public DefaultStatusObserver(Logger logger){\n        this.logger = logger;\n    }\n\n    @Override\n    public void statusChanged(Download download, DownloadStatus status) {\n        if (logger.isInfoEnabled()) {\n            logger.info(\"status: \" + status.name());\n        }\n    }\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/common/io/download/impl/observer/ExceptionObserver.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.io.download.impl.observer;\n\n//~--- JDK imports ------------------------------------------------------------\n\nimport java.util.Observable;\nimport java.util.Observer;\n\nimport com.alibaba.otter.node.etl.common.io.download.impl.AbstractCommandDownload;\n\n/**\n * @author brave.taoy\n */\npublic abstract class ExceptionObserver implements Observer {\n\n    public abstract void exceptionOccured(AbstractCommandDownload download, Exception status);\n\n    public void update(Observable o, Object arg) {\n        if (arg instanceof Exception) {\n            exceptionOccured((AbstractCommandDownload) o, (Exception) arg);\n        }\n    }\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/common/io/download/impl/observer/ProgressObserver.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.io.download.impl.observer;\n\nimport java.util.Observable;\nimport java.util.Observer;\n\nimport com.alibaba.otter.node.etl.common.io.download.impl.AbstractCommandDownload;\n\n/**\n * @author brave.taoy\n */\npublic abstract class ProgressObserver implements Observer {\n\n    public abstract void statusChanged(AbstractCommandDownload download, String status);\n\n    public void update(Observable o, Object arg) {\n        if (arg instanceof String) {\n            statusChanged((AbstractCommandDownload) o, (String) arg);\n        }\n    }\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/common/io/download/impl/observer/StatusObserver.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.io.download.impl.observer;\n\nimport java.util.Observable;\nimport java.util.Observer;\n\nimport com.alibaba.otter.node.etl.common.io.download.Download;\nimport com.alibaba.otter.node.etl.common.io.download.DownloadStatus;\n\n/**\n * @author brave.taoy\n */\npublic abstract class StatusObserver implements Observer {\n\n    public abstract void statusChanged(Download download, DownloadStatus status);\n\n    public void update(Observable o, Object arg) {\n        if (arg instanceof DownloadStatus) {\n            statusChanged((Download) o, (DownloadStatus) arg);\n        }\n    }\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/common/io/signature/ChecksumException.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.io.signature;\n\nimport org.apache.commons.lang.exception.NestableRuntimeException;\n\n/**\n * @author jianghang 2011-10-9 下午06:13:29\n * @version 4.0.0\n */\npublic class ChecksumException extends NestableRuntimeException {\n\n    private static final long serialVersionUID = -7288830284122672209L;\n\n    private String            errorCode;\n    private String            errorDesc;\n\n    public ChecksumException(String errorCode){\n        super(errorCode);\n    }\n\n    public ChecksumException(String errorCode, Throwable cause){\n        super(errorCode, cause);\n    }\n\n    public ChecksumException(String errorCode, String errorDesc){\n        super(errorCode + \":\" + errorDesc);\n    }\n\n    public ChecksumException(String errorCode, String errorDesc, Throwable cause){\n        super(errorCode + \":\" + errorDesc, cause);\n    }\n\n    public ChecksumException(Throwable cause){\n        super(cause);\n    }\n\n    public String getErrorCode() {\n        return errorCode;\n    }\n\n    public String getErrorDesc() {\n        return errorDesc;\n    }\n\n    @Override\n    public Throwable fillInStackTrace() {\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/common/io/signature/ChecksumUtils.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.io.signature;\n\n/**\n * 提供基于checksum的签名方式\n * \n * @author jianghang 2011-10-9 下午05:43:04\n * @version 4.0.0\n */\npublic class ChecksumUtils {\n\n    public static String checksum(byte[] bytes) {\n        int sum = Crc32C.maskedCrc32c(bytes, 0, bytes.length);\n        return Integer.toString(sum);\n    }\n\n    public static String checksum(byte[] bytes, int offset, int length) {\n        int sum = Crc32C.maskedCrc32c(bytes, offset, length);\n        return Integer.toString(sum);\n    }\n\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/common/io/signature/Crc32C.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*\n * Copyright (C) 2011 the original author or authors.\n * See the notice.md file distributed with this work for additional\n * information regarding copyright ownership.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.alibaba.otter.node.etl.common.io.signature;\n\nimport java.util.zip.Checksum;\n\n/**\n * A pure-java implementation of the CRC32 checksum that uses the CRC32-C polynomial, the same polynomial used by iSCSI\n * and implemented on many Intel chipsets supporting SSE4.2.\n */\n// this code was taken from Apache Hadoop\npublic class Crc32C implements Checksum {\n\n    private static final int MASK_DELTA = 0xa282ead8;\n\n    public static int maskedCrc32c(byte[] data) {\n        return maskedCrc32c(data, 0, data.length);\n    }\n\n    public static int maskedCrc32c(byte[] data, int offset, int length) {\n        Crc32C crc32c = new Crc32C();\n        crc32c.update(data, offset, length);\n        return crc32c.getMaskedValue();\n    }\n\n    /**\n     * Return a masked representation of crc.\n     * <p/>\n     * Motivation: it is problematic to compute the CRC of a string that contains embedded CRCs. Therefore we recommend\n     * that CRCs stored somewhere (e.g., in files) should be masked before being stored.\n     */\n    public static int mask(int crc) {\n        // Rotate right by 15 bits and add a constant.\n        return ((crc >>> 15) | (crc << 17)) + MASK_DELTA;\n    }\n\n    /**\n     * Return the crc whose masked representation is masked_crc.\n     */\n    public static int unmask(int maskedCrc) {\n        int rot = maskedCrc - MASK_DELTA;\n        return ((rot >>> 17) | (rot << 15));\n    }\n\n    /**\n     * the current CRC value, bit-flipped\n     */\n    private int crc;\n\n    /**\n     * Create a new PureJavaCrc32 object.\n     */\n    public Crc32C(){\n        reset();\n    }\n\n    public int getMaskedValue() {\n        return mask(getIntValue());\n    }\n\n    public int getIntValue() {\n        return ~crc;\n    }\n\n    public long getValue() {\n        long ret = crc;\n        return (~ret) & 0xffffffffL;\n    }\n\n    public void reset() {\n        crc = 0xffffffff;\n    }\n\n    public void update(byte[] b, int off, int len) {\n        int localCrc = crc;\n        while (len > 7) {\n            int c0 = b[off++] ^ localCrc;\n            int c1 = b[off++] ^ (localCrc >>>= 8);\n            int c2 = b[off++] ^ (localCrc >>>= 8);\n            int c3 = b[off++] ^ (localCrc >>>= 8);\n            localCrc = (T8_7[c0 & 0xff] ^ T8_6[c1 & 0xff]) ^ (T8_5[c2 & 0xff] ^ T8_4[c3 & 0xff]);\n\n            localCrc ^= (T8_3[b[off++] & 0xff] ^ T8_2[b[off++] & 0xff])\n                        ^ (T8_1[b[off++] & 0xff] ^ T8_0[b[off++] & 0xff]);\n\n            len -= 8;\n        }\n        while (len > 0) {\n            localCrc = (localCrc >>> 8) ^ T8_0[(localCrc ^ b[off++]) & 0xff];\n            len--;\n        }\n\n        // Publish crc out to object\n        crc = localCrc;\n    }\n\n    public void update(int b) {\n        crc = (crc >>> 8) ^ T8_0[(crc ^ b) & 0xff];\n    }\n\n    // CRC polynomial tables generated by:\n    // java -cp build/test/classes/:build/classes/ \\\n    //   org.apache.hadoop.util.TestPureJavaCrc32\\$Table 82F63B78\n\n    static final int[] T8_0 = new int[] { 0x00000000, 0xF26B8303, 0xE13B70F7, 0x1350F3F4, 0xC79A971F, 0x35F1141C,\n            0x26A1E7E8, 0xD4CA64EB, 0x8AD958CF, 0x78B2DBCC, 0x6BE22838, 0x9989AB3B, 0x4D43CFD0, 0xBF284CD3, 0xAC78BF27,\n            0x5E133C24, 0x105EC76F, 0xE235446C, 0xF165B798, 0x030E349B, 0xD7C45070, 0x25AFD373, 0x36FF2087, 0xC494A384,\n            0x9A879FA0, 0x68EC1CA3, 0x7BBCEF57, 0x89D76C54, 0x5D1D08BF, 0xAF768BBC, 0xBC267848, 0x4E4DFB4B, 0x20BD8EDE,\n            0xD2D60DDD, 0xC186FE29, 0x33ED7D2A, 0xE72719C1, 0x154C9AC2, 0x061C6936, 0xF477EA35, 0xAA64D611, 0x580F5512,\n            0x4B5FA6E6, 0xB93425E5, 0x6DFE410E, 0x9F95C20D, 0x8CC531F9, 0x7EAEB2FA, 0x30E349B1, 0xC288CAB2, 0xD1D83946,\n            0x23B3BA45, 0xF779DEAE, 0x05125DAD, 0x1642AE59, 0xE4292D5A, 0xBA3A117E, 0x4851927D, 0x5B016189, 0xA96AE28A,\n            0x7DA08661, 0x8FCB0562, 0x9C9BF696, 0x6EF07595, 0x417B1DBC, 0xB3109EBF, 0xA0406D4B, 0x522BEE48, 0x86E18AA3,\n            0x748A09A0, 0x67DAFA54, 0x95B17957, 0xCBA24573, 0x39C9C670, 0x2A993584, 0xD8F2B687, 0x0C38D26C, 0xFE53516F,\n            0xED03A29B, 0x1F682198, 0x5125DAD3, 0xA34E59D0, 0xB01EAA24, 0x42752927, 0x96BF4DCC, 0x64D4CECF, 0x77843D3B,\n            0x85EFBE38, 0xDBFC821C, 0x2997011F, 0x3AC7F2EB, 0xC8AC71E8, 0x1C661503, 0xEE0D9600, 0xFD5D65F4, 0x0F36E6F7,\n            0x61C69362, 0x93AD1061, 0x80FDE395, 0x72966096, 0xA65C047D, 0x5437877E, 0x4767748A, 0xB50CF789, 0xEB1FCBAD,\n            0x197448AE, 0x0A24BB5A, 0xF84F3859, 0x2C855CB2, 0xDEEEDFB1, 0xCDBE2C45, 0x3FD5AF46, 0x7198540D, 0x83F3D70E,\n            0x90A324FA, 0x62C8A7F9, 0xB602C312, 0x44694011, 0x5739B3E5, 0xA55230E6, 0xFB410CC2, 0x092A8FC1, 0x1A7A7C35,\n            0xE811FF36, 0x3CDB9BDD, 0xCEB018DE, 0xDDE0EB2A, 0x2F8B6829, 0x82F63B78, 0x709DB87B, 0x63CD4B8F, 0x91A6C88C,\n            0x456CAC67, 0xB7072F64, 0xA457DC90, 0x563C5F93, 0x082F63B7, 0xFA44E0B4, 0xE9141340, 0x1B7F9043, 0xCFB5F4A8,\n            0x3DDE77AB, 0x2E8E845F, 0xDCE5075C, 0x92A8FC17, 0x60C37F14, 0x73938CE0, 0x81F80FE3, 0x55326B08, 0xA759E80B,\n            0xB4091BFF, 0x466298FC, 0x1871A4D8, 0xEA1A27DB, 0xF94AD42F, 0x0B21572C, 0xDFEB33C7, 0x2D80B0C4, 0x3ED04330,\n            0xCCBBC033, 0xA24BB5A6, 0x502036A5, 0x4370C551, 0xB11B4652, 0x65D122B9, 0x97BAA1BA, 0x84EA524E, 0x7681D14D,\n            0x2892ED69, 0xDAF96E6A, 0xC9A99D9E, 0x3BC21E9D, 0xEF087A76, 0x1D63F975, 0x0E330A81, 0xFC588982, 0xB21572C9,\n            0x407EF1CA, 0x532E023E, 0xA145813D, 0x758FE5D6, 0x87E466D5, 0x94B49521, 0x66DF1622, 0x38CC2A06, 0xCAA7A905,\n            0xD9F75AF1, 0x2B9CD9F2, 0xFF56BD19, 0x0D3D3E1A, 0x1E6DCDEE, 0xEC064EED, 0xC38D26C4, 0x31E6A5C7, 0x22B65633,\n            0xD0DDD530, 0x0417B1DB, 0xF67C32D8, 0xE52CC12C, 0x1747422F, 0x49547E0B, 0xBB3FFD08, 0xA86F0EFC, 0x5A048DFF,\n            0x8ECEE914, 0x7CA56A17, 0x6FF599E3, 0x9D9E1AE0, 0xD3D3E1AB, 0x21B862A8, 0x32E8915C, 0xC083125F, 0x144976B4,\n            0xE622F5B7, 0xF5720643, 0x07198540, 0x590AB964, 0xAB613A67, 0xB831C993, 0x4A5A4A90, 0x9E902E7B, 0x6CFBAD78,\n            0x7FAB5E8C, 0x8DC0DD8F, 0xE330A81A, 0x115B2B19, 0x020BD8ED, 0xF0605BEE, 0x24AA3F05, 0xD6C1BC06, 0xC5914FF2,\n            0x37FACCF1, 0x69E9F0D5, 0x9B8273D6, 0x88D28022, 0x7AB90321, 0xAE7367CA, 0x5C18E4C9, 0x4F48173D, 0xBD23943E,\n            0xF36E6F75, 0x0105EC76, 0x12551F82, 0xE03E9C81, 0x34F4F86A, 0xC69F7B69, 0xD5CF889D, 0x27A40B9E, 0x79B737BA,\n            0x8BDCB4B9, 0x988C474D, 0x6AE7C44E, 0xBE2DA0A5, 0x4C4623A6, 0x5F16D052, 0xAD7D5351 };\n    static final int[] T8_1 = new int[] { 0x00000000, 0x13A29877, 0x274530EE, 0x34E7A899, 0x4E8A61DC, 0x5D28F9AB,\n            0x69CF5132, 0x7A6DC945, 0x9D14C3B8, 0x8EB65BCF, 0xBA51F356, 0xA9F36B21, 0xD39EA264, 0xC03C3A13, 0xF4DB928A,\n            0xE7790AFD, 0x3FC5F181, 0x2C6769F6, 0x1880C16F, 0x0B225918, 0x714F905D, 0x62ED082A, 0x560AA0B3, 0x45A838C4,\n            0xA2D13239, 0xB173AA4E, 0x859402D7, 0x96369AA0, 0xEC5B53E5, 0xFFF9CB92, 0xCB1E630B, 0xD8BCFB7C, 0x7F8BE302,\n            0x6C297B75, 0x58CED3EC, 0x4B6C4B9B, 0x310182DE, 0x22A31AA9, 0x1644B230, 0x05E62A47, 0xE29F20BA, 0xF13DB8CD,\n            0xC5DA1054, 0xD6788823, 0xAC154166, 0xBFB7D911, 0x8B507188, 0x98F2E9FF, 0x404E1283, 0x53EC8AF4, 0x670B226D,\n            0x74A9BA1A, 0x0EC4735F, 0x1D66EB28, 0x298143B1, 0x3A23DBC6, 0xDD5AD13B, 0xCEF8494C, 0xFA1FE1D5, 0xE9BD79A2,\n            0x93D0B0E7, 0x80722890, 0xB4958009, 0xA737187E, 0xFF17C604, 0xECB55E73, 0xD852F6EA, 0xCBF06E9D, 0xB19DA7D8,\n            0xA23F3FAF, 0x96D89736, 0x857A0F41, 0x620305BC, 0x71A19DCB, 0x45463552, 0x56E4AD25, 0x2C896460, 0x3F2BFC17,\n            0x0BCC548E, 0x186ECCF9, 0xC0D23785, 0xD370AFF2, 0xE797076B, 0xF4359F1C, 0x8E585659, 0x9DFACE2E, 0xA91D66B7,\n            0xBABFFEC0, 0x5DC6F43D, 0x4E646C4A, 0x7A83C4D3, 0x69215CA4, 0x134C95E1, 0x00EE0D96, 0x3409A50F, 0x27AB3D78,\n            0x809C2506, 0x933EBD71, 0xA7D915E8, 0xB47B8D9F, 0xCE1644DA, 0xDDB4DCAD, 0xE9537434, 0xFAF1EC43, 0x1D88E6BE,\n            0x0E2A7EC9, 0x3ACDD650, 0x296F4E27, 0x53028762, 0x40A01F15, 0x7447B78C, 0x67E52FFB, 0xBF59D487, 0xACFB4CF0,\n            0x981CE469, 0x8BBE7C1E, 0xF1D3B55B, 0xE2712D2C, 0xD69685B5, 0xC5341DC2, 0x224D173F, 0x31EF8F48, 0x050827D1,\n            0x16AABFA6, 0x6CC776E3, 0x7F65EE94, 0x4B82460D, 0x5820DE7A, 0xFBC3FAF9, 0xE861628E, 0xDC86CA17, 0xCF245260,\n            0xB5499B25, 0xA6EB0352, 0x920CABCB, 0x81AE33BC, 0x66D73941, 0x7575A136, 0x419209AF, 0x523091D8, 0x285D589D,\n            0x3BFFC0EA, 0x0F186873, 0x1CBAF004, 0xC4060B78, 0xD7A4930F, 0xE3433B96, 0xF0E1A3E1, 0x8A8C6AA4, 0x992EF2D3,\n            0xADC95A4A, 0xBE6BC23D, 0x5912C8C0, 0x4AB050B7, 0x7E57F82E, 0x6DF56059, 0x1798A91C, 0x043A316B, 0x30DD99F2,\n            0x237F0185, 0x844819FB, 0x97EA818C, 0xA30D2915, 0xB0AFB162, 0xCAC27827, 0xD960E050, 0xED8748C9, 0xFE25D0BE,\n            0x195CDA43, 0x0AFE4234, 0x3E19EAAD, 0x2DBB72DA, 0x57D6BB9F, 0x447423E8, 0x70938B71, 0x63311306, 0xBB8DE87A,\n            0xA82F700D, 0x9CC8D894, 0x8F6A40E3, 0xF50789A6, 0xE6A511D1, 0xD242B948, 0xC1E0213F, 0x26992BC2, 0x353BB3B5,\n            0x01DC1B2C, 0x127E835B, 0x68134A1E, 0x7BB1D269, 0x4F567AF0, 0x5CF4E287, 0x04D43CFD, 0x1776A48A, 0x23910C13,\n            0x30339464, 0x4A5E5D21, 0x59FCC556, 0x6D1B6DCF, 0x7EB9F5B8, 0x99C0FF45, 0x8A626732, 0xBE85CFAB, 0xAD2757DC,\n            0xD74A9E99, 0xC4E806EE, 0xF00FAE77, 0xE3AD3600, 0x3B11CD7C, 0x28B3550B, 0x1C54FD92, 0x0FF665E5, 0x759BACA0,\n            0x663934D7, 0x52DE9C4E, 0x417C0439, 0xA6050EC4, 0xB5A796B3, 0x81403E2A, 0x92E2A65D, 0xE88F6F18, 0xFB2DF76F,\n            0xCFCA5FF6, 0xDC68C781, 0x7B5FDFFF, 0x68FD4788, 0x5C1AEF11, 0x4FB87766, 0x35D5BE23, 0x26772654, 0x12908ECD,\n            0x013216BA, 0xE64B1C47, 0xF5E98430, 0xC10E2CA9, 0xD2ACB4DE, 0xA8C17D9B, 0xBB63E5EC, 0x8F844D75, 0x9C26D502,\n            0x449A2E7E, 0x5738B609, 0x63DF1E90, 0x707D86E7, 0x0A104FA2, 0x19B2D7D5, 0x2D557F4C, 0x3EF7E73B, 0xD98EEDC6,\n            0xCA2C75B1, 0xFECBDD28, 0xED69455F, 0x97048C1A, 0x84A6146D, 0xB041BCF4, 0xA3E32483 };\n    static final int[] T8_2 = new int[] { 0x00000000, 0xA541927E, 0x4F6F520D, 0xEA2EC073, 0x9EDEA41A, 0x3B9F3664,\n            0xD1B1F617, 0x74F06469, 0x38513EC5, 0x9D10ACBB, 0x773E6CC8, 0xD27FFEB6, 0xA68F9ADF, 0x03CE08A1, 0xE9E0C8D2,\n            0x4CA15AAC, 0x70A27D8A, 0xD5E3EFF4, 0x3FCD2F87, 0x9A8CBDF9, 0xEE7CD990, 0x4B3D4BEE, 0xA1138B9D, 0x045219E3,\n            0x48F3434F, 0xEDB2D131, 0x079C1142, 0xA2DD833C, 0xD62DE755, 0x736C752B, 0x9942B558, 0x3C032726, 0xE144FB14,\n            0x4405696A, 0xAE2BA919, 0x0B6A3B67, 0x7F9A5F0E, 0xDADBCD70, 0x30F50D03, 0x95B49F7D, 0xD915C5D1, 0x7C5457AF,\n            0x967A97DC, 0x333B05A2, 0x47CB61CB, 0xE28AF3B5, 0x08A433C6, 0xADE5A1B8, 0x91E6869E, 0x34A714E0, 0xDE89D493,\n            0x7BC846ED, 0x0F382284, 0xAA79B0FA, 0x40577089, 0xE516E2F7, 0xA9B7B85B, 0x0CF62A25, 0xE6D8EA56, 0x43997828,\n            0x37691C41, 0x92288E3F, 0x78064E4C, 0xDD47DC32, 0xC76580D9, 0x622412A7, 0x880AD2D4, 0x2D4B40AA, 0x59BB24C3,\n            0xFCFAB6BD, 0x16D476CE, 0xB395E4B0, 0xFF34BE1C, 0x5A752C62, 0xB05BEC11, 0x151A7E6F, 0x61EA1A06, 0xC4AB8878,\n            0x2E85480B, 0x8BC4DA75, 0xB7C7FD53, 0x12866F2D, 0xF8A8AF5E, 0x5DE93D20, 0x29195949, 0x8C58CB37, 0x66760B44,\n            0xC337993A, 0x8F96C396, 0x2AD751E8, 0xC0F9919B, 0x65B803E5, 0x1148678C, 0xB409F5F2, 0x5E273581, 0xFB66A7FF,\n            0x26217BCD, 0x8360E9B3, 0x694E29C0, 0xCC0FBBBE, 0xB8FFDFD7, 0x1DBE4DA9, 0xF7908DDA, 0x52D11FA4, 0x1E704508,\n            0xBB31D776, 0x511F1705, 0xF45E857B, 0x80AEE112, 0x25EF736C, 0xCFC1B31F, 0x6A802161, 0x56830647, 0xF3C29439,\n            0x19EC544A, 0xBCADC634, 0xC85DA25D, 0x6D1C3023, 0x8732F050, 0x2273622E, 0x6ED23882, 0xCB93AAFC, 0x21BD6A8F,\n            0x84FCF8F1, 0xF00C9C98, 0x554D0EE6, 0xBF63CE95, 0x1A225CEB, 0x8B277743, 0x2E66E53D, 0xC448254E, 0x6109B730,\n            0x15F9D359, 0xB0B84127, 0x5A968154, 0xFFD7132A, 0xB3764986, 0x1637DBF8, 0xFC191B8B, 0x595889F5, 0x2DA8ED9C,\n            0x88E97FE2, 0x62C7BF91, 0xC7862DEF, 0xFB850AC9, 0x5EC498B7, 0xB4EA58C4, 0x11ABCABA, 0x655BAED3, 0xC01A3CAD,\n            0x2A34FCDE, 0x8F756EA0, 0xC3D4340C, 0x6695A672, 0x8CBB6601, 0x29FAF47F, 0x5D0A9016, 0xF84B0268, 0x1265C21B,\n            0xB7245065, 0x6A638C57, 0xCF221E29, 0x250CDE5A, 0x804D4C24, 0xF4BD284D, 0x51FCBA33, 0xBBD27A40, 0x1E93E83E,\n            0x5232B292, 0xF77320EC, 0x1D5DE09F, 0xB81C72E1, 0xCCEC1688, 0x69AD84F6, 0x83834485, 0x26C2D6FB, 0x1AC1F1DD,\n            0xBF8063A3, 0x55AEA3D0, 0xF0EF31AE, 0x841F55C7, 0x215EC7B9, 0xCB7007CA, 0x6E3195B4, 0x2290CF18, 0x87D15D66,\n            0x6DFF9D15, 0xC8BE0F6B, 0xBC4E6B02, 0x190FF97C, 0xF321390F, 0x5660AB71, 0x4C42F79A, 0xE90365E4, 0x032DA597,\n            0xA66C37E9, 0xD29C5380, 0x77DDC1FE, 0x9DF3018D, 0x38B293F3, 0x7413C95F, 0xD1525B21, 0x3B7C9B52, 0x9E3D092C,\n            0xEACD6D45, 0x4F8CFF3B, 0xA5A23F48, 0x00E3AD36, 0x3CE08A10, 0x99A1186E, 0x738FD81D, 0xD6CE4A63, 0xA23E2E0A,\n            0x077FBC74, 0xED517C07, 0x4810EE79, 0x04B1B4D5, 0xA1F026AB, 0x4BDEE6D8, 0xEE9F74A6, 0x9A6F10CF, 0x3F2E82B1,\n            0xD50042C2, 0x7041D0BC, 0xAD060C8E, 0x08479EF0, 0xE2695E83, 0x4728CCFD, 0x33D8A894, 0x96993AEA, 0x7CB7FA99,\n            0xD9F668E7, 0x9557324B, 0x3016A035, 0xDA386046, 0x7F79F238, 0x0B899651, 0xAEC8042F, 0x44E6C45C, 0xE1A75622,\n            0xDDA47104, 0x78E5E37A, 0x92CB2309, 0x378AB177, 0x437AD51E, 0xE63B4760, 0x0C158713, 0xA954156D, 0xE5F54FC1,\n            0x40B4DDBF, 0xAA9A1DCC, 0x0FDB8FB2, 0x7B2BEBDB, 0xDE6A79A5, 0x3444B9D6, 0x91052BA8 };\n    static final int[] T8_3 = new int[] { 0x00000000, 0xDD45AAB8, 0xBF672381, 0x62228939, 0x7B2231F3, 0xA6679B4B,\n            0xC4451272, 0x1900B8CA, 0xF64463E6, 0x2B01C95E, 0x49234067, 0x9466EADF, 0x8D665215, 0x5023F8AD, 0x32017194,\n            0xEF44DB2C, 0xE964B13D, 0x34211B85, 0x560392BC, 0x8B463804, 0x924680CE, 0x4F032A76, 0x2D21A34F, 0xF06409F7,\n            0x1F20D2DB, 0xC2657863, 0xA047F15A, 0x7D025BE2, 0x6402E328, 0xB9474990, 0xDB65C0A9, 0x06206A11, 0xD725148B,\n            0x0A60BE33, 0x6842370A, 0xB5079DB2, 0xAC072578, 0x71428FC0, 0x136006F9, 0xCE25AC41, 0x2161776D, 0xFC24DDD5,\n            0x9E0654EC, 0x4343FE54, 0x5A43469E, 0x8706EC26, 0xE524651F, 0x3861CFA7, 0x3E41A5B6, 0xE3040F0E, 0x81268637,\n            0x5C632C8F, 0x45639445, 0x98263EFD, 0xFA04B7C4, 0x27411D7C, 0xC805C650, 0x15406CE8, 0x7762E5D1, 0xAA274F69,\n            0xB327F7A3, 0x6E625D1B, 0x0C40D422, 0xD1057E9A, 0xABA65FE7, 0x76E3F55F, 0x14C17C66, 0xC984D6DE, 0xD0846E14,\n            0x0DC1C4AC, 0x6FE34D95, 0xB2A6E72D, 0x5DE23C01, 0x80A796B9, 0xE2851F80, 0x3FC0B538, 0x26C00DF2, 0xFB85A74A,\n            0x99A72E73, 0x44E284CB, 0x42C2EEDA, 0x9F874462, 0xFDA5CD5B, 0x20E067E3, 0x39E0DF29, 0xE4A57591, 0x8687FCA8,\n            0x5BC25610, 0xB4868D3C, 0x69C32784, 0x0BE1AEBD, 0xD6A40405, 0xCFA4BCCF, 0x12E11677, 0x70C39F4E, 0xAD8635F6,\n            0x7C834B6C, 0xA1C6E1D4, 0xC3E468ED, 0x1EA1C255, 0x07A17A9F, 0xDAE4D027, 0xB8C6591E, 0x6583F3A6, 0x8AC7288A,\n            0x57828232, 0x35A00B0B, 0xE8E5A1B3, 0xF1E51979, 0x2CA0B3C1, 0x4E823AF8, 0x93C79040, 0x95E7FA51, 0x48A250E9,\n            0x2A80D9D0, 0xF7C57368, 0xEEC5CBA2, 0x3380611A, 0x51A2E823, 0x8CE7429B, 0x63A399B7, 0xBEE6330F, 0xDCC4BA36,\n            0x0181108E, 0x1881A844, 0xC5C402FC, 0xA7E68BC5, 0x7AA3217D, 0x52A0C93F, 0x8FE56387, 0xEDC7EABE, 0x30824006,\n            0x2982F8CC, 0xF4C75274, 0x96E5DB4D, 0x4BA071F5, 0xA4E4AAD9, 0x79A10061, 0x1B838958, 0xC6C623E0, 0xDFC69B2A,\n            0x02833192, 0x60A1B8AB, 0xBDE41213, 0xBBC47802, 0x6681D2BA, 0x04A35B83, 0xD9E6F13B, 0xC0E649F1, 0x1DA3E349,\n            0x7F816A70, 0xA2C4C0C8, 0x4D801BE4, 0x90C5B15C, 0xF2E73865, 0x2FA292DD, 0x36A22A17, 0xEBE780AF, 0x89C50996,\n            0x5480A32E, 0x8585DDB4, 0x58C0770C, 0x3AE2FE35, 0xE7A7548D, 0xFEA7EC47, 0x23E246FF, 0x41C0CFC6, 0x9C85657E,\n            0x73C1BE52, 0xAE8414EA, 0xCCA69DD3, 0x11E3376B, 0x08E38FA1, 0xD5A62519, 0xB784AC20, 0x6AC10698, 0x6CE16C89,\n            0xB1A4C631, 0xD3864F08, 0x0EC3E5B0, 0x17C35D7A, 0xCA86F7C2, 0xA8A47EFB, 0x75E1D443, 0x9AA50F6F, 0x47E0A5D7,\n            0x25C22CEE, 0xF8878656, 0xE1873E9C, 0x3CC29424, 0x5EE01D1D, 0x83A5B7A5, 0xF90696D8, 0x24433C60, 0x4661B559,\n            0x9B241FE1, 0x8224A72B, 0x5F610D93, 0x3D4384AA, 0xE0062E12, 0x0F42F53E, 0xD2075F86, 0xB025D6BF, 0x6D607C07,\n            0x7460C4CD, 0xA9256E75, 0xCB07E74C, 0x16424DF4, 0x106227E5, 0xCD278D5D, 0xAF050464, 0x7240AEDC, 0x6B401616,\n            0xB605BCAE, 0xD4273597, 0x09629F2F, 0xE6264403, 0x3B63EEBB, 0x59416782, 0x8404CD3A, 0x9D0475F0, 0x4041DF48,\n            0x22635671, 0xFF26FCC9, 0x2E238253, 0xF36628EB, 0x9144A1D2, 0x4C010B6A, 0x5501B3A0, 0x88441918, 0xEA669021,\n            0x37233A99, 0xD867E1B5, 0x05224B0D, 0x6700C234, 0xBA45688C, 0xA345D046, 0x7E007AFE, 0x1C22F3C7, 0xC167597F,\n            0xC747336E, 0x1A0299D6, 0x782010EF, 0xA565BA57, 0xBC65029D, 0x6120A825, 0x0302211C, 0xDE478BA4, 0x31035088,\n            0xEC46FA30, 0x8E647309, 0x5321D9B1, 0x4A21617B, 0x9764CBC3, 0xF54642FA, 0x2803E842 };\n    static final int[] T8_4 = new int[] { 0x00000000, 0x38116FAC, 0x7022DF58, 0x4833B0F4, 0xE045BEB0, 0xD854D11C,\n            0x906761E8, 0xA8760E44, 0xC5670B91, 0xFD76643D, 0xB545D4C9, 0x8D54BB65, 0x2522B521, 0x1D33DA8D, 0x55006A79,\n            0x6D1105D5, 0x8F2261D3, 0xB7330E7F, 0xFF00BE8B, 0xC711D127, 0x6F67DF63, 0x5776B0CF, 0x1F45003B, 0x27546F97,\n            0x4A456A42, 0x725405EE, 0x3A67B51A, 0x0276DAB6, 0xAA00D4F2, 0x9211BB5E, 0xDA220BAA, 0xE2336406, 0x1BA8B557,\n            0x23B9DAFB, 0x6B8A6A0F, 0x539B05A3, 0xFBED0BE7, 0xC3FC644B, 0x8BCFD4BF, 0xB3DEBB13, 0xDECFBEC6, 0xE6DED16A,\n            0xAEED619E, 0x96FC0E32, 0x3E8A0076, 0x069B6FDA, 0x4EA8DF2E, 0x76B9B082, 0x948AD484, 0xAC9BBB28, 0xE4A80BDC,\n            0xDCB96470, 0x74CF6A34, 0x4CDE0598, 0x04EDB56C, 0x3CFCDAC0, 0x51EDDF15, 0x69FCB0B9, 0x21CF004D, 0x19DE6FE1,\n            0xB1A861A5, 0x89B90E09, 0xC18ABEFD, 0xF99BD151, 0x37516AAE, 0x0F400502, 0x4773B5F6, 0x7F62DA5A, 0xD714D41E,\n            0xEF05BBB2, 0xA7360B46, 0x9F2764EA, 0xF236613F, 0xCA270E93, 0x8214BE67, 0xBA05D1CB, 0x1273DF8F, 0x2A62B023,\n            0x625100D7, 0x5A406F7B, 0xB8730B7D, 0x806264D1, 0xC851D425, 0xF040BB89, 0x5836B5CD, 0x6027DA61, 0x28146A95,\n            0x10050539, 0x7D1400EC, 0x45056F40, 0x0D36DFB4, 0x3527B018, 0x9D51BE5C, 0xA540D1F0, 0xED736104, 0xD5620EA8,\n            0x2CF9DFF9, 0x14E8B055, 0x5CDB00A1, 0x64CA6F0D, 0xCCBC6149, 0xF4AD0EE5, 0xBC9EBE11, 0x848FD1BD, 0xE99ED468,\n            0xD18FBBC4, 0x99BC0B30, 0xA1AD649C, 0x09DB6AD8, 0x31CA0574, 0x79F9B580, 0x41E8DA2C, 0xA3DBBE2A, 0x9BCAD186,\n            0xD3F96172, 0xEBE80EDE, 0x439E009A, 0x7B8F6F36, 0x33BCDFC2, 0x0BADB06E, 0x66BCB5BB, 0x5EADDA17, 0x169E6AE3,\n            0x2E8F054F, 0x86F90B0B, 0xBEE864A7, 0xF6DBD453, 0xCECABBFF, 0x6EA2D55C, 0x56B3BAF0, 0x1E800A04, 0x269165A8,\n            0x8EE76BEC, 0xB6F60440, 0xFEC5B4B4, 0xC6D4DB18, 0xABC5DECD, 0x93D4B161, 0xDBE70195, 0xE3F66E39, 0x4B80607D,\n            0x73910FD1, 0x3BA2BF25, 0x03B3D089, 0xE180B48F, 0xD991DB23, 0x91A26BD7, 0xA9B3047B, 0x01C50A3F, 0x39D46593,\n            0x71E7D567, 0x49F6BACB, 0x24E7BF1E, 0x1CF6D0B2, 0x54C56046, 0x6CD40FEA, 0xC4A201AE, 0xFCB36E02, 0xB480DEF6,\n            0x8C91B15A, 0x750A600B, 0x4D1B0FA7, 0x0528BF53, 0x3D39D0FF, 0x954FDEBB, 0xAD5EB117, 0xE56D01E3, 0xDD7C6E4F,\n            0xB06D6B9A, 0x887C0436, 0xC04FB4C2, 0xF85EDB6E, 0x5028D52A, 0x6839BA86, 0x200A0A72, 0x181B65DE, 0xFA2801D8,\n            0xC2396E74, 0x8A0ADE80, 0xB21BB12C, 0x1A6DBF68, 0x227CD0C4, 0x6A4F6030, 0x525E0F9C, 0x3F4F0A49, 0x075E65E5,\n            0x4F6DD511, 0x777CBABD, 0xDF0AB4F9, 0xE71BDB55, 0xAF286BA1, 0x9739040D, 0x59F3BFF2, 0x61E2D05E, 0x29D160AA,\n            0x11C00F06, 0xB9B60142, 0x81A76EEE, 0xC994DE1A, 0xF185B1B6, 0x9C94B463, 0xA485DBCF, 0xECB66B3B, 0xD4A70497,\n            0x7CD10AD3, 0x44C0657F, 0x0CF3D58B, 0x34E2BA27, 0xD6D1DE21, 0xEEC0B18D, 0xA6F30179, 0x9EE26ED5, 0x36946091,\n            0x0E850F3D, 0x46B6BFC9, 0x7EA7D065, 0x13B6D5B0, 0x2BA7BA1C, 0x63940AE8, 0x5B856544, 0xF3F36B00, 0xCBE204AC,\n            0x83D1B458, 0xBBC0DBF4, 0x425B0AA5, 0x7A4A6509, 0x3279D5FD, 0x0A68BA51, 0xA21EB415, 0x9A0FDBB9, 0xD23C6B4D,\n            0xEA2D04E1, 0x873C0134, 0xBF2D6E98, 0xF71EDE6C, 0xCF0FB1C0, 0x6779BF84, 0x5F68D028, 0x175B60DC, 0x2F4A0F70,\n            0xCD796B76, 0xF56804DA, 0xBD5BB42E, 0x854ADB82, 0x2D3CD5C6, 0x152DBA6A, 0x5D1E0A9E, 0x650F6532, 0x081E60E7,\n            0x300F0F4B, 0x783CBFBF, 0x402DD013, 0xE85BDE57, 0xD04AB1FB, 0x9879010F, 0xA0686EA3 };\n    static final int[] T8_5 = new int[] { 0x00000000, 0xEF306B19, 0xDB8CA0C3, 0x34BCCBDA, 0xB2F53777, 0x5DC55C6E,\n            0x697997B4, 0x8649FCAD, 0x6006181F, 0x8F367306, 0xBB8AB8DC, 0x54BAD3C5, 0xD2F32F68, 0x3DC34471, 0x097F8FAB,\n            0xE64FE4B2, 0xC00C303E, 0x2F3C5B27, 0x1B8090FD, 0xF4B0FBE4, 0x72F90749, 0x9DC96C50, 0xA975A78A, 0x4645CC93,\n            0xA00A2821, 0x4F3A4338, 0x7B8688E2, 0x94B6E3FB, 0x12FF1F56, 0xFDCF744F, 0xC973BF95, 0x2643D48C, 0x85F4168D,\n            0x6AC47D94, 0x5E78B64E, 0xB148DD57, 0x370121FA, 0xD8314AE3, 0xEC8D8139, 0x03BDEA20, 0xE5F20E92, 0x0AC2658B,\n            0x3E7EAE51, 0xD14EC548, 0x570739E5, 0xB83752FC, 0x8C8B9926, 0x63BBF23F, 0x45F826B3, 0xAAC84DAA, 0x9E748670,\n            0x7144ED69, 0xF70D11C4, 0x183D7ADD, 0x2C81B107, 0xC3B1DA1E, 0x25FE3EAC, 0xCACE55B5, 0xFE729E6F, 0x1142F576,\n            0x970B09DB, 0x783B62C2, 0x4C87A918, 0xA3B7C201, 0x0E045BEB, 0xE13430F2, 0xD588FB28, 0x3AB89031, 0xBCF16C9C,\n            0x53C10785, 0x677DCC5F, 0x884DA746, 0x6E0243F4, 0x813228ED, 0xB58EE337, 0x5ABE882E, 0xDCF77483, 0x33C71F9A,\n            0x077BD440, 0xE84BBF59, 0xCE086BD5, 0x213800CC, 0x1584CB16, 0xFAB4A00F, 0x7CFD5CA2, 0x93CD37BB, 0xA771FC61,\n            0x48419778, 0xAE0E73CA, 0x413E18D3, 0x7582D309, 0x9AB2B810, 0x1CFB44BD, 0xF3CB2FA4, 0xC777E47E, 0x28478F67,\n            0x8BF04D66, 0x64C0267F, 0x507CEDA5, 0xBF4C86BC, 0x39057A11, 0xD6351108, 0xE289DAD2, 0x0DB9B1CB, 0xEBF65579,\n            0x04C63E60, 0x307AF5BA, 0xDF4A9EA3, 0x5903620E, 0xB6330917, 0x828FC2CD, 0x6DBFA9D4, 0x4BFC7D58, 0xA4CC1641,\n            0x9070DD9B, 0x7F40B682, 0xF9094A2F, 0x16392136, 0x2285EAEC, 0xCDB581F5, 0x2BFA6547, 0xC4CA0E5E, 0xF076C584,\n            0x1F46AE9D, 0x990F5230, 0x763F3929, 0x4283F2F3, 0xADB399EA, 0x1C08B7D6, 0xF338DCCF, 0xC7841715, 0x28B47C0C,\n            0xAEFD80A1, 0x41CDEBB8, 0x75712062, 0x9A414B7B, 0x7C0EAFC9, 0x933EC4D0, 0xA7820F0A, 0x48B26413, 0xCEFB98BE,\n            0x21CBF3A7, 0x1577387D, 0xFA475364, 0xDC0487E8, 0x3334ECF1, 0x0788272B, 0xE8B84C32, 0x6EF1B09F, 0x81C1DB86,\n            0xB57D105C, 0x5A4D7B45, 0xBC029FF7, 0x5332F4EE, 0x678E3F34, 0x88BE542D, 0x0EF7A880, 0xE1C7C399, 0xD57B0843,\n            0x3A4B635A, 0x99FCA15B, 0x76CCCA42, 0x42700198, 0xAD406A81, 0x2B09962C, 0xC439FD35, 0xF08536EF, 0x1FB55DF6,\n            0xF9FAB944, 0x16CAD25D, 0x22761987, 0xCD46729E, 0x4B0F8E33, 0xA43FE52A, 0x90832EF0, 0x7FB345E9, 0x59F09165,\n            0xB6C0FA7C, 0x827C31A6, 0x6D4C5ABF, 0xEB05A612, 0x0435CD0B, 0x308906D1, 0xDFB96DC8, 0x39F6897A, 0xD6C6E263,\n            0xE27A29B9, 0x0D4A42A0, 0x8B03BE0D, 0x6433D514, 0x508F1ECE, 0xBFBF75D7, 0x120CEC3D, 0xFD3C8724, 0xC9804CFE,\n            0x26B027E7, 0xA0F9DB4A, 0x4FC9B053, 0x7B757B89, 0x94451090, 0x720AF422, 0x9D3A9F3B, 0xA98654E1, 0x46B63FF8,\n            0xC0FFC355, 0x2FCFA84C, 0x1B736396, 0xF443088F, 0xD200DC03, 0x3D30B71A, 0x098C7CC0, 0xE6BC17D9, 0x60F5EB74,\n            0x8FC5806D, 0xBB794BB7, 0x544920AE, 0xB206C41C, 0x5D36AF05, 0x698A64DF, 0x86BA0FC6, 0x00F3F36B, 0xEFC39872,\n            0xDB7F53A8, 0x344F38B1, 0x97F8FAB0, 0x78C891A9, 0x4C745A73, 0xA344316A, 0x250DCDC7, 0xCA3DA6DE, 0xFE816D04,\n            0x11B1061D, 0xF7FEE2AF, 0x18CE89B6, 0x2C72426C, 0xC3422975, 0x450BD5D8, 0xAA3BBEC1, 0x9E87751B, 0x71B71E02,\n            0x57F4CA8E, 0xB8C4A197, 0x8C786A4D, 0x63480154, 0xE501FDF9, 0x0A3196E0, 0x3E8D5D3A, 0xD1BD3623, 0x37F2D291,\n            0xD8C2B988, 0xEC7E7252, 0x034E194B, 0x8507E5E6, 0x6A378EFF, 0x5E8B4525, 0xB1BB2E3C };\n    static final int[] T8_6 = new int[] { 0x00000000, 0x68032CC8, 0xD0065990, 0xB8057558, 0xA5E0C5D1, 0xCDE3E919,\n            0x75E69C41, 0x1DE5B089, 0x4E2DFD53, 0x262ED19B, 0x9E2BA4C3, 0xF628880B, 0xEBCD3882, 0x83CE144A, 0x3BCB6112,\n            0x53C84DDA, 0x9C5BFAA6, 0xF458D66E, 0x4C5DA336, 0x245E8FFE, 0x39BB3F77, 0x51B813BF, 0xE9BD66E7, 0x81BE4A2F,\n            0xD27607F5, 0xBA752B3D, 0x02705E65, 0x6A7372AD, 0x7796C224, 0x1F95EEEC, 0xA7909BB4, 0xCF93B77C, 0x3D5B83BD,\n            0x5558AF75, 0xED5DDA2D, 0x855EF6E5, 0x98BB466C, 0xF0B86AA4, 0x48BD1FFC, 0x20BE3334, 0x73767EEE, 0x1B755226,\n            0xA370277E, 0xCB730BB6, 0xD696BB3F, 0xBE9597F7, 0x0690E2AF, 0x6E93CE67, 0xA100791B, 0xC90355D3, 0x7106208B,\n            0x19050C43, 0x04E0BCCA, 0x6CE39002, 0xD4E6E55A, 0xBCE5C992, 0xEF2D8448, 0x872EA880, 0x3F2BDDD8, 0x5728F110,\n            0x4ACD4199, 0x22CE6D51, 0x9ACB1809, 0xF2C834C1, 0x7AB7077A, 0x12B42BB2, 0xAAB15EEA, 0xC2B27222, 0xDF57C2AB,\n            0xB754EE63, 0x0F519B3B, 0x6752B7F3, 0x349AFA29, 0x5C99D6E1, 0xE49CA3B9, 0x8C9F8F71, 0x917A3FF8, 0xF9791330,\n            0x417C6668, 0x297F4AA0, 0xE6ECFDDC, 0x8EEFD114, 0x36EAA44C, 0x5EE98884, 0x430C380D, 0x2B0F14C5, 0x930A619D,\n            0xFB094D55, 0xA8C1008F, 0xC0C22C47, 0x78C7591F, 0x10C475D7, 0x0D21C55E, 0x6522E996, 0xDD279CCE, 0xB524B006,\n            0x47EC84C7, 0x2FEFA80F, 0x97EADD57, 0xFFE9F19F, 0xE20C4116, 0x8A0F6DDE, 0x320A1886, 0x5A09344E, 0x09C17994,\n            0x61C2555C, 0xD9C72004, 0xB1C40CCC, 0xAC21BC45, 0xC422908D, 0x7C27E5D5, 0x1424C91D, 0xDBB77E61, 0xB3B452A9,\n            0x0BB127F1, 0x63B20B39, 0x7E57BBB0, 0x16549778, 0xAE51E220, 0xC652CEE8, 0x959A8332, 0xFD99AFFA, 0x459CDAA2,\n            0x2D9FF66A, 0x307A46E3, 0x58796A2B, 0xE07C1F73, 0x887F33BB, 0xF56E0EF4, 0x9D6D223C, 0x25685764, 0x4D6B7BAC,\n            0x508ECB25, 0x388DE7ED, 0x808892B5, 0xE88BBE7D, 0xBB43F3A7, 0xD340DF6F, 0x6B45AA37, 0x034686FF, 0x1EA33676,\n            0x76A01ABE, 0xCEA56FE6, 0xA6A6432E, 0x6935F452, 0x0136D89A, 0xB933ADC2, 0xD130810A, 0xCCD53183, 0xA4D61D4B,\n            0x1CD36813, 0x74D044DB, 0x27180901, 0x4F1B25C9, 0xF71E5091, 0x9F1D7C59, 0x82F8CCD0, 0xEAFBE018, 0x52FE9540,\n            0x3AFDB988, 0xC8358D49, 0xA036A181, 0x1833D4D9, 0x7030F811, 0x6DD54898, 0x05D66450, 0xBDD31108, 0xD5D03DC0,\n            0x8618701A, 0xEE1B5CD2, 0x561E298A, 0x3E1D0542, 0x23F8B5CB, 0x4BFB9903, 0xF3FEEC5B, 0x9BFDC093, 0x546E77EF,\n            0x3C6D5B27, 0x84682E7F, 0xEC6B02B7, 0xF18EB23E, 0x998D9EF6, 0x2188EBAE, 0x498BC766, 0x1A438ABC, 0x7240A674,\n            0xCA45D32C, 0xA246FFE4, 0xBFA34F6D, 0xD7A063A5, 0x6FA516FD, 0x07A63A35, 0x8FD9098E, 0xE7DA2546, 0x5FDF501E,\n            0x37DC7CD6, 0x2A39CC5F, 0x423AE097, 0xFA3F95CF, 0x923CB907, 0xC1F4F4DD, 0xA9F7D815, 0x11F2AD4D, 0x79F18185,\n            0x6414310C, 0x0C171DC4, 0xB412689C, 0xDC114454, 0x1382F328, 0x7B81DFE0, 0xC384AAB8, 0xAB878670, 0xB66236F9,\n            0xDE611A31, 0x66646F69, 0x0E6743A1, 0x5DAF0E7B, 0x35AC22B3, 0x8DA957EB, 0xE5AA7B23, 0xF84FCBAA, 0x904CE762,\n            0x2849923A, 0x404ABEF2, 0xB2828A33, 0xDA81A6FB, 0x6284D3A3, 0x0A87FF6B, 0x17624FE2, 0x7F61632A, 0xC7641672,\n            0xAF673ABA, 0xFCAF7760, 0x94AC5BA8, 0x2CA92EF0, 0x44AA0238, 0x594FB2B1, 0x314C9E79, 0x8949EB21, 0xE14AC7E9,\n            0x2ED97095, 0x46DA5C5D, 0xFEDF2905, 0x96DC05CD, 0x8B39B544, 0xE33A998C, 0x5B3FECD4, 0x333CC01C, 0x60F48DC6,\n            0x08F7A10E, 0xB0F2D456, 0xD8F1F89E, 0xC5144817, 0xAD1764DF, 0x15121187, 0x7D113D4F };\n    static final int[] T8_7 = new int[] { 0x00000000, 0x493C7D27, 0x9278FA4E, 0xDB448769, 0x211D826D, 0x6821FF4A,\n            0xB3657823, 0xFA590504, 0x423B04DA, 0x0B0779FD, 0xD043FE94, 0x997F83B3, 0x632686B7, 0x2A1AFB90, 0xF15E7CF9,\n            0xB86201DE, 0x847609B4, 0xCD4A7493, 0x160EF3FA, 0x5F328EDD, 0xA56B8BD9, 0xEC57F6FE, 0x37137197, 0x7E2F0CB0,\n            0xC64D0D6E, 0x8F717049, 0x5435F720, 0x1D098A07, 0xE7508F03, 0xAE6CF224, 0x7528754D, 0x3C14086A, 0x0D006599,\n            0x443C18BE, 0x9F789FD7, 0xD644E2F0, 0x2C1DE7F4, 0x65219AD3, 0xBE651DBA, 0xF759609D, 0x4F3B6143, 0x06071C64,\n            0xDD439B0D, 0x947FE62A, 0x6E26E32E, 0x271A9E09, 0xFC5E1960, 0xB5626447, 0x89766C2D, 0xC04A110A, 0x1B0E9663,\n            0x5232EB44, 0xA86BEE40, 0xE1579367, 0x3A13140E, 0x732F6929, 0xCB4D68F7, 0x827115D0, 0x593592B9, 0x1009EF9E,\n            0xEA50EA9A, 0xA36C97BD, 0x782810D4, 0x31146DF3, 0x1A00CB32, 0x533CB615, 0x8878317C, 0xC1444C5B, 0x3B1D495F,\n            0x72213478, 0xA965B311, 0xE059CE36, 0x583BCFE8, 0x1107B2CF, 0xCA4335A6, 0x837F4881, 0x79264D85, 0x301A30A2,\n            0xEB5EB7CB, 0xA262CAEC, 0x9E76C286, 0xD74ABFA1, 0x0C0E38C8, 0x453245EF, 0xBF6B40EB, 0xF6573DCC, 0x2D13BAA5,\n            0x642FC782, 0xDC4DC65C, 0x9571BB7B, 0x4E353C12, 0x07094135, 0xFD504431, 0xB46C3916, 0x6F28BE7F, 0x2614C358,\n            0x1700AEAB, 0x5E3CD38C, 0x857854E5, 0xCC4429C2, 0x361D2CC6, 0x7F2151E1, 0xA465D688, 0xED59ABAF, 0x553BAA71,\n            0x1C07D756, 0xC743503F, 0x8E7F2D18, 0x7426281C, 0x3D1A553B, 0xE65ED252, 0xAF62AF75, 0x9376A71F, 0xDA4ADA38,\n            0x010E5D51, 0x48322076, 0xB26B2572, 0xFB575855, 0x2013DF3C, 0x692FA21B, 0xD14DA3C5, 0x9871DEE2, 0x4335598B,\n            0x0A0924AC, 0xF05021A8, 0xB96C5C8F, 0x6228DBE6, 0x2B14A6C1, 0x34019664, 0x7D3DEB43, 0xA6796C2A, 0xEF45110D,\n            0x151C1409, 0x5C20692E, 0x8764EE47, 0xCE589360, 0x763A92BE, 0x3F06EF99, 0xE44268F0, 0xAD7E15D7, 0x572710D3,\n            0x1E1B6DF4, 0xC55FEA9D, 0x8C6397BA, 0xB0779FD0, 0xF94BE2F7, 0x220F659E, 0x6B3318B9, 0x916A1DBD, 0xD856609A,\n            0x0312E7F3, 0x4A2E9AD4, 0xF24C9B0A, 0xBB70E62D, 0x60346144, 0x29081C63, 0xD3511967, 0x9A6D6440, 0x4129E329,\n            0x08159E0E, 0x3901F3FD, 0x703D8EDA, 0xAB7909B3, 0xE2457494, 0x181C7190, 0x51200CB7, 0x8A648BDE, 0xC358F6F9,\n            0x7B3AF727, 0x32068A00, 0xE9420D69, 0xA07E704E, 0x5A27754A, 0x131B086D, 0xC85F8F04, 0x8163F223, 0xBD77FA49,\n            0xF44B876E, 0x2F0F0007, 0x66337D20, 0x9C6A7824, 0xD5560503, 0x0E12826A, 0x472EFF4D, 0xFF4CFE93, 0xB67083B4,\n            0x6D3404DD, 0x240879FA, 0xDE517CFE, 0x976D01D9, 0x4C2986B0, 0x0515FB97, 0x2E015D56, 0x673D2071, 0xBC79A718,\n            0xF545DA3F, 0x0F1CDF3B, 0x4620A21C, 0x9D642575, 0xD4585852, 0x6C3A598C, 0x250624AB, 0xFE42A3C2, 0xB77EDEE5,\n            0x4D27DBE1, 0x041BA6C6, 0xDF5F21AF, 0x96635C88, 0xAA7754E2, 0xE34B29C5, 0x380FAEAC, 0x7133D38B, 0x8B6AD68F,\n            0xC256ABA8, 0x19122CC1, 0x502E51E6, 0xE84C5038, 0xA1702D1F, 0x7A34AA76, 0x3308D751, 0xC951D255, 0x806DAF72,\n            0x5B29281B, 0x1215553C, 0x230138CF, 0x6A3D45E8, 0xB179C281, 0xF845BFA6, 0x021CBAA2, 0x4B20C785, 0x906440EC,\n            0xD9583DCB, 0x613A3C15, 0x28064132, 0xF342C65B, 0xBA7EBB7C, 0x4027BE78, 0x091BC35F, 0xD25F4436, 0x9B633911,\n            0xA777317B, 0xEE4B4C5C, 0x350FCB35, 0x7C33B612, 0x866AB316, 0xCF56CE31, 0x14124958, 0x5D2E347F, 0xE54C35A1,\n            0xAC704886, 0x7734CFEF, 0x3E08B2C8, 0xC451B7CC, 0x8D6DCAEB, 0x56294D82, 0x1F1530A5 };\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/common/jetty/JettyEmbedServer.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.jetty;\n\nimport org.eclipse.jetty.server.Connector;\nimport org.eclipse.jetty.server.Handler;\nimport org.eclipse.jetty.server.Server;\nimport org.eclipse.jetty.servlet.ServletContextHandler;\nimport org.eclipse.jetty.util.resource.Resource;\nimport org.eclipse.jetty.xml.XmlConfiguration;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.DisposableBean;\nimport org.springframework.beans.factory.InitializingBean;\n\nimport com.alibaba.otter.node.common.config.ConfigClientService;\n\n/**\n * jetty的嵌入式启动入口\n * \n * @author jianghang 2011-10-18 下午01:58:33\n * @version 4.0.0\n */\npublic class JettyEmbedServer implements InitializingBean, DisposableBean {\n\n    private static final String DEFAULT_CONFIG = \"jetty/jetty.xml\";\n    private static final Logger logger         = LoggerFactory.getLogger(JettyEmbedServer.class);\n    private Server              server;\n    private String              config         = DEFAULT_CONFIG;\n    private String              htdocsDir;\n    private ConfigClientService configClientService;\n\n    public void afterPropertiesSet() throws Exception {\n        Resource configXml = Resource.newSystemResource(config);\n        XmlConfiguration configuration = new XmlConfiguration(configXml.getInputStream());\n        server = (Server) configuration.configure();\n        Integer port = getPort();\n        if (port != null && port > 0) {\n            Connector[] connectors = server.getConnectors();\n            for (Connector connector : connectors) {\n                connector.setPort(port);\n            }\n        }\n\n        Handler handler = server.getHandler();\n        if (handler != null && handler instanceof ServletContextHandler) {\n            ServletContextHandler servletHandler = (ServletContextHandler) handler;\n            servletHandler.getInitParams().put(\"org.eclipse.jetty.servlet.Default.resourceBase\", htdocsDir);\n        }\n\n        server.start();\n        if (logger.isInfoEnabled()) {\n            logger.info(\"##Jetty Embed Server is startup!\");\n        }\n    }\n\n    private Integer getPort() {\n        return configClientService.currentNode().getParameters().getDownloadPort();\n    }\n\n    public void destroy() throws Exception {\n        server.stop();\n        if (logger.isInfoEnabled()) {\n            logger.info(\"##Jetty Embed Server is stop!\");\n        }\n    }\n\n    // ================ setter / getter ================\n\n    public void setConfig(String config) {\n        this.config = config;\n    }\n\n    public void setConfigClientService(ConfigClientService configClientService) {\n        this.configClientService = configClientService;\n    }\n\n    public void setHtdocsDir(String htdocsDir) {\n        this.htdocsDir = htdocsDir;\n    }\n\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/common/jmx/JmxConnectorServerFactoryBean.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.jmx;\n\nimport java.io.IOException;\nimport java.rmi.RemoteException;\nimport java.rmi.registry.LocateRegistry;\nimport java.rmi.registry.Registry;\nimport java.text.MessageFormat;\n\nimport javax.management.JMException;\n\nimport org.springframework.jmx.support.ConnectorServerFactoryBean;\n\nimport com.alibaba.otter.node.common.config.ConfigClientService;\nimport com.alibaba.otter.shared.common.model.config.node.Node;\n\n/**\n * 扩展实现spring的jmx connector\n * \n * @author jianghang 2012-7-30 下午12:50:05\n */\npublic class JmxConnectorServerFactoryBean extends ConnectorServerFactoryBean {\n\n    private String              SERVER_URL           = \"service:jmx:rmi://127.0.0.1:{0}/jndi/rmi://127.0.0.1:{0}/mbean\";\n    private boolean             alwaysCreateRegistry = false;\n    private ConfigClientService configClientService;\n\n    public void afterPropertiesSet() throws JMException, IOException {\n        Node node = configClientService.currentNode();\n        int port = node.getPort().intValue() + 1;\n        Integer mbeanPort = node.getParameters().getMbeanPort();\n        if (mbeanPort != null && mbeanPort != 0) {// 做个兼容处理，<=4.2.2版本没有mbeanPort设置\n            port = mbeanPort;\n        }\n\n        String serviceUrl = MessageFormat.format(SERVER_URL, String.valueOf(port));\n        super.setServiceUrl(serviceUrl);\n        super.setObjectName(\"connector:name=rmi\");\n        // 直接使用port\n        getRegistry(port);\n        super.afterPropertiesSet();\n    }\n\n    private Registry getRegistry(int registryPort) throws RemoteException {\n        if (this.alwaysCreateRegistry) {\n            logger.info(\"Creating new RMI registry\");\n            return LocateRegistry.createRegistry(registryPort);\n        }\n        if (logger.isInfoEnabled()) {\n            logger.info(\"Looking for RMI registry at port '\" + registryPort + \"'\");\n        }\n        try {\n            // Retrieve existing registry.\n            Registry reg = LocateRegistry.getRegistry(registryPort);\n            testRegistry(reg);\n            return reg;\n        } catch (RemoteException ex) {\n            logger.debug(\"RMI registry access threw exception\", ex);\n            logger.info(\"Could not detect RMI registry - creating new one\");\n            // Assume no registry found -> create new one.\n            return LocateRegistry.createRegistry(registryPort);\n        }\n    }\n\n    private void testRegistry(Registry registry) throws RemoteException {\n        registry.list();\n    }\n\n    public void setServiceUrl(String serviceUrl) {\n        throw new UnsupportedOperationException(\"set serviceUrl is not support!\");\n    }\n\n    public void setAlwaysCreateRegistry(boolean alwaysCreateRegistry) {\n        this.alwaysCreateRegistry = alwaysCreateRegistry;\n    }\n\n    public void setConfigClientService(ConfigClientService configClientService) {\n        this.configClientService = configClientService;\n    }\n\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/common/jmx/StageAggregation.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.jmx;\n\nimport java.util.concurrent.atomic.AtomicLong;\n\n/**\n * 统计每个stage的运行信息\n * \n * @author jianghang 2012-5-29 下午02:32:08\n * @version 4.0.2\n */\npublic class StageAggregation {\n\n    private static final String HISTOGRAM_FORMAT = \"{total:%s,count:%s,maximum:%s,minimum:%s,average:%s,tps:%s,tpm:%s}\";\n    private static final Long   ONE_SECOND       = 1000L;\n    private static final Long   ONE_MINUTE       = 60 * 1000L;\n    private int                 bufferSize       = 1 * 1024;\n    private int                 indexMask;\n    private AggregationItem[]   table;\n    private AtomicLong          sequence         = new AtomicLong(-1);\n\n    public StageAggregation(int bufferSize){\n        if (Integer.bitCount(bufferSize) != 1) {\n            throw new IllegalArgumentException(\"bufferSize must be a power of 2\");\n        }\n        this.bufferSize = bufferSize;\n        indexMask = this.bufferSize - 1;\n        table = new AggregationItem[this.bufferSize];\n    }\n\n    public void push(AggregationItem aggregation) {\n        long seq = sequence.incrementAndGet();\n        table[getIndex(seq)] = aggregation;\n    }\n\n    /**\n     * 返回当前stage处理次数\n     */\n    public Long count() {\n        return sequence.get();\n    }\n\n    /**\n     * 平均处理时间\n     */\n    public String histogram() {\n        Long costs = 0L;\n        Long items = 0L;\n        Long max = 0L;\n        Long min = Long.MAX_VALUE;\n        Long tps = 0L;\n        Long tpm = 0L;\n        Long avg = 0L;\n\n        Long lastTime = 0L;\n        Long tpsCount = 0L;// 记录每秒的请求数，临时变量\n        Long tpsTotal = 0L;// 总tps数多少\n        Long tpsSecond = 0L;// 多少秒中有数据\n\n        Long tpmCount = 0L; // 记录每分钟的请求数，临时变量\n        Long tpmTotal = 0L; // 总tps数多少\n        Long tpmMinute = 0L;// 多少分钟有数据\n        for (int i = 0; i < table.length; i++) {\n            AggregationItem aggregation = table[i];\n            if (aggregation != null) {\n                Long cost = aggregation.getEndTime() - aggregation.getStartTime();\n                items += 1;\n                costs += cost;\n                if (cost > max) {\n                    max = cost;\n                }\n                if (cost < min) {\n                    min = cost;\n                }\n\n                if (lastTime != 0) {\n                    if (lastTime > aggregation.getEndTime() - ONE_SECOND) {// 说明在同一秒\n                        tpsCount++;\n                    } else {\n                        tpsTotal += tpsCount;\n                        tpsSecond++;\n                        tpsCount = 0L;\n                    }\n\n                    if (lastTime > aggregation.getEndTime() - ONE_MINUTE) {// 说明在同一分钟\n                        tpmCount++;\n                    } else {\n                        tpmTotal += tpmCount;\n                        tpmMinute++;\n                        tpmCount = 0L;\n                    }\n\n                }\n\n                lastTime = aggregation.getEndTime();\n            }\n        }\n        // 设置一下最后一批tps/m统计信息\n        tpsTotal += tpsCount;\n        tpsSecond++;\n        tpsCount = 0L;\n        tpmTotal += tpmCount;\n        tpmMinute++;\n        tpmCount = 0L;\n\n        if (items != 0) {\n            avg = costs / items;\n        }\n\n        if (tpsSecond != 0) {\n            tps = tpsTotal / tpsSecond;\n        }\n\n        if (tpmMinute != 0) {\n            tpm = tpmTotal / tpmMinute;\n        }\n\n        if (min == Long.MAX_VALUE) {\n            min = 0L;\n        }\n\n        return String.format(HISTOGRAM_FORMAT, new Object[] { sequence.get() + 1, items, max, min, avg, tps, tpm });\n    }\n\n    private int getIndex(long sequcnce) {\n        return (int) sequcnce & indexMask;\n    }\n\n    public static class AggregationItem {\n\n        private Long startTime; // 一次请求的起始时间\n        private Long endTime;  // 一次请求的结束时间\n        private Long number;   // 一次请求的处理数量\n        private Long size;     // 一次请求的处理大小\n\n        public AggregationItem(Long startTime, Long endTime, Long number, Long size){\n            this.startTime = startTime;\n            this.endTime = endTime;\n            this.number = number;\n            this.size = size;\n        }\n\n        public AggregationItem(Long startTime, Long endTime){\n            this.startTime = startTime;\n            this.endTime = endTime;\n        }\n\n        public Long getStartTime() {\n            return startTime;\n        }\n\n        public void setStartTime(Long startTime) {\n            this.startTime = startTime;\n        }\n\n        public Long getEndTime() {\n            return endTime;\n        }\n\n        public void setEndTime(Long endTime) {\n            this.endTime = endTime;\n        }\n\n        public Long getNumber() {\n            return number;\n        }\n\n        public void setNumber(Long number) {\n            this.number = number;\n        }\n\n        public Long getSize() {\n            return size;\n        }\n\n        public void setSize(Long size) {\n            this.size = size;\n        }\n\n    }\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/common/jmx/StageAggregationCollector.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.jmx;\n\nimport java.util.Map;\nimport java.util.concurrent.atomic.AtomicBoolean;\n\nimport com.alibaba.otter.node.etl.common.jmx.StageAggregation.AggregationItem;\nimport com.alibaba.otter.shared.common.model.config.enums.StageType;\nimport com.google.common.base.Function;\nimport com.google.common.collect.OtterMigrateMap;\n\n/**\n * 统计每个stage的运行信息\n * \n * @author jianghang 2012-5-29 下午02:32:08\n * @version 4.0.2\n */\npublic class StageAggregationCollector {\n\n    private Map<Long, Map<StageType, StageAggregation>> collector;\n    private AtomicBoolean                               profiling = new AtomicBoolean(true);\n\n    public StageAggregationCollector(){\n        this(1024);\n    }\n\n    public StageAggregationCollector(final int bufferSize){\n        collector = OtterMigrateMap.makeComputingMap(new Function<Long, Map<StageType, StageAggregation>>() {\n\n            public Map<StageType, StageAggregation> apply(Long input) {\n                return OtterMigrateMap.makeComputingMap(new Function<StageType, StageAggregation>() {\n\n                    public StageAggregation apply(StageType input) {\n                        return new StageAggregation(bufferSize);\n                    }\n                });\n            }\n        });\n    }\n\n    public void push(Long pipelineId, StageType stage, AggregationItem aggregationItem) {\n        collector.get(pipelineId).get(stage).push(aggregationItem);\n    }\n\n    public String histogram(Long pipelineId, StageType stage) {\n        return collector.get(pipelineId).get(stage).histogram();\n    }\n\n    public boolean isProfiling() {\n        return profiling.get();\n    }\n\n    public void setProfiling(boolean profiling) {\n        this.profiling.set(profiling);\n    }\n\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/common/pipe/Pipe.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.pipe;\n\nimport com.alibaba.otter.node.etl.common.pipe.exception.PipeException;\n\n/**\n * S.E.T.L模块之间的数据交互工具\n * \n * @author jianghang 2011-10-10 下午04:48:44\n * @version 4.0.0\n */\npublic interface Pipe<T, KEY extends PipeKey> {\n\n    /**\n     * 向管道中添加数据\n     * \n     * @param data\n     */\n    public KEY put(T data) throws PipeException;\n\n    /**\n     * 通过key获取管道中的数据\n     * \n     * @param key\n     * @return\n     */\n    public T get(KEY key) throws PipeException;\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/common/pipe/PipeDataType.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.pipe;\n\n/**\n * pipe支持处理的数据类型\n * \n * @author jianghang 2011-11-3 下午06:33:41\n * @version 4.0.0\n */\npublic enum PipeDataType {\n    /** 数据库 */\n    DB_BATCH,\n    /** 附件记录 */\n    FILE_BATCH,\n    /** mq记录 */\n    MQ_BATCH,\n    /** cache记录 */\n    CACHE_BATCH;\n\n    public boolean isDbBatch() {\n        return this == DB_BATCH;\n    }\n\n    public boolean isFileBatch() {\n        return this == FILE_BATCH;\n    }\n\n    public boolean isMqBatch() {\n        return this == MQ_BATCH;\n    }\n\n    public boolean isCacheBatch() {\n        return this == CACHE_BATCH;\n    }\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/common/pipe/PipeKey.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.pipe;\n\nimport java.io.Serializable;\n\nimport org.apache.commons.lang.builder.ToStringBuilder;\n\nimport com.alibaba.otter.shared.common.utils.OtterToStringStyle;\n\n/**\n * pipe中数据的唯一标示\n * \n * @author jianghang 2011-10-13 下午05:26:32\n * @version 4.0.0\n */\npublic class PipeKey implements Serializable {\n\n    private static final long serialVersionUID = 1543055219365681976L;\n\n    private PipeDataType      dataType;                               // 数据对象类型\n\n    public PipeDataType getDataType() {\n        return dataType;\n    }\n\n    public void setDataType(PipeDataType dataType) {\n        this.dataType = dataType;\n    }\n\n    @Override\n    public String toString() {\n        return ToStringBuilder.reflectionToString(this, OtterToStringStyle.DEFAULT_STYLE);\n    }\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/common/pipe/exception/PipeException.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.pipe.exception;\n\nimport org.apache.commons.lang.exception.NestableRuntimeException;\n\n/**\n * @author jianghang 2011-9-16 下午01:59:25\n * @version 4.0.0\n */\npublic class PipeException extends NestableRuntimeException {\n\n    private static final long serialVersionUID = -7288830284122672209L;\n\n    private String            errorCode;\n    private String            errorDesc;\n\n    public PipeException(String errorCode){\n        super(errorCode);\n    }\n\n    public PipeException(String errorCode, Throwable cause){\n        super(errorCode, cause);\n    }\n\n    public PipeException(String errorCode, String errorDesc){\n        super(errorCode + \":\" + errorDesc);\n    }\n\n    public PipeException(String errorCode, String errorDesc, Throwable cause){\n        super(errorCode + \":\" + errorDesc, cause);\n    }\n\n    public PipeException(Throwable cause){\n        super(cause);\n    }\n\n    public String getErrorCode() {\n        return errorCode;\n    }\n\n    public String getErrorDesc() {\n        return errorDesc;\n    }\n\n    @Override\n    public Throwable fillInStackTrace() {\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/common/pipe/impl/RowDataPipeDelegate.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.pipe.impl;\n\nimport java.io.File;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.concurrent.Callable;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Future;\n\nimport org.slf4j.MDC;\nimport org.springframework.util.Assert;\nimport org.springframework.util.CollectionUtils;\n\nimport com.alibaba.otter.node.common.config.ConfigClientService;\nimport com.alibaba.otter.node.etl.OtterConstants;\nimport com.alibaba.otter.node.etl.common.pipe.PipeKey;\nimport com.alibaba.otter.node.etl.common.pipe.exception.PipeException;\nimport com.alibaba.otter.node.etl.common.pipe.impl.http.AttachmentHttpPipe;\nimport com.alibaba.otter.node.etl.common.pipe.impl.http.HttpPipeKey;\nimport com.alibaba.otter.node.etl.common.pipe.impl.http.RowDataHttpPipe;\nimport com.alibaba.otter.node.etl.common.pipe.impl.memory.MemoryPipeKey;\nimport com.alibaba.otter.node.etl.common.pipe.impl.memory.RowDataMemoryPipe;\nimport com.alibaba.otter.node.etl.common.pipe.impl.rpc.RowDataRpcPipe;\nimport com.alibaba.otter.node.etl.common.pipe.impl.rpc.RpcPipeKey;\nimport com.alibaba.otter.shared.common.model.config.pipeline.Pipeline;\nimport com.alibaba.otter.shared.common.model.config.pipeline.PipelineParameter.PipeChooseMode;\nimport com.alibaba.otter.shared.etl.model.DbBatch;\nimport com.alibaba.otter.shared.etl.model.EventData;\n\n/**\n * 管道操作相关工具类\n * \n * @author jianghang 2011-10-17 下午04:02:35\n * @version 4.0.0\n */\npublic class RowDataPipeDelegate {\n\n    private RowDataMemoryPipe   rowDataMemoryPipe;\n    private AttachmentHttpPipe  attachmentHttpPipe;\n    private RowDataHttpPipe     rowDataHttpPipe;\n    private RowDataRpcPipe      rowDataRpcPipe;\n    private ConfigClientService configClientService;\n    private ExecutorService     executorService;\n    private long                sizeThresold = 1024 * 128L; // 默认1MB\n\n    /**\n     * 将对应的数据传递到指定的Node id节点上\n     */\n    public List<PipeKey> put(final DbBatch data, Long nid) throws PipeException {\n        List<PipeKey> keys = new ArrayList<PipeKey>();\n        if (isLocal(nid)) {\n            keys.add(rowDataMemoryPipe.put(data));\n        } else {\n            Future<PipeKey> future = null;\n            Pipeline pipeline = configClientService.findPipeline(data.getRowBatch().getIdentity().getPipelineId());\n            if (data.getFileBatch() != null && !CollectionUtils.isEmpty(data.getFileBatch().getFiles())) {\n                future = executorService.submit(new Callable<PipeKey>() {\n\n                    public PipeKey call() throws Exception {\n                        try {\n                            MDC.put(OtterConstants.splitPipelineLogFileKey,\n                                    String.valueOf(data.getFileBatch().getIdentity().getPipelineId()));\n                            return attachmentHttpPipe.put(data.getFileBatch());\n                        } finally {\n                            MDC.remove(OtterConstants.splitPipelineLogFileKey);\n                        }\n                    }\n                });\n            }\n            try {\n                PipeChooseMode pipeChooseMode = pipeline.getParameters().getPipeChooseType();\n                if (pipeChooseMode.isAutomatic()) {\n                    if (calculateSize(data) <= sizeThresold) {\n                        keys.add(rowDataRpcPipe.put(data));\n                    } else {\n                        keys.add(rowDataHttpPipe.put(data));\n                    }\n                } else if (pipeChooseMode.isRpc()) {\n                    keys.add(rowDataRpcPipe.put(data));\n                } else if (pipeChooseMode.isHttp()) {\n                    keys.add(rowDataHttpPipe.put(data));\n                } else {\n                    throw new PipeException(\"pipeChooseMode is error!\" + pipeChooseMode);\n                }\n\n                // 等待一下附件处理\n                if (future != null) {\n                    keys.add(future.get());\n                }\n            } catch (Exception e) {\n                throw new PipeException(e);\n            }\n        }\n\n        return keys;\n    }\n\n    public DbBatch get(List<PipeKey> keys) {\n        Assert.notNull(keys);\n        DbBatch dbBatch = new DbBatch();\n        Future<File> future = null;\n        for (final PipeKey key : keys) {\n            if (key == null) {\n                continue; // 忽略空的key\n            }\n\n            if (key instanceof MemoryPipeKey) {\n                dbBatch = rowDataMemoryPipe.get((MemoryPipeKey) key);\n                return dbBatch;// 直接返回\n            } else if (key instanceof HttpPipeKey) {\n                if (key.getDataType().isDbBatch()) { // 区分一下数据下载\n                    dbBatch = rowDataHttpPipe.get((HttpPipeKey) key);\n                } else {\n                    future = executorService.submit(new Callable<File>() {\n\n                        public File call() throws Exception {\n                            try {\n                                HttpPipeKey pipeKey = (HttpPipeKey) key;\n                                MDC.put(OtterConstants.splitPipelineLogFileKey,\n                                        String.valueOf(pipeKey.getIdentity().getPipelineId()));\n                                return attachmentHttpPipe.get(pipeKey);\n                            } finally {\n                                MDC.remove(OtterConstants.splitPipelineLogFileKey);\n                            }\n\n                        }\n                    });\n                }\n            } else if (key instanceof RpcPipeKey) {\n                dbBatch = rowDataRpcPipe.get((RpcPipeKey) key);\n            } else {\n                throw new PipeException(\"unknow_PipeKey\", key.toString());\n            }\n        }\n\n        if (future != null && dbBatch != null) {\n            try {\n                dbBatch.setRoot(future.get());\n            } catch (Exception e) {\n                throw new PipeException(e);\n            }\n        }\n        return dbBatch;\n    }\n\n    // 大致估算一下row记录的大小\n    private long calculateSize(DbBatch data) {\n        long size = 0;\n        for (EventData eventData : data.getRowBatch().getDatas()) {\n            size += eventData.getSize();\n        }\n\n        return size;\n\n        // 走序列化的方式快速计算一下大小\n        // SerializeWriter out = new SerializeWriter();\n        // try {\n        // JSONSerializer serializer = new JSONSerializer(out);\n        // serializer.config(SerializerFeature.SortField, false);// 关掉排序\n        // serializer.write(data);\n        // byte[] bytes = out.toBytes(\"UTF-8\");\n        // return bytes.length;\n        // } finally {\n        // out.close();\n        // }\n    }\n\n    private boolean isLocal(Long nid) {\n        return configClientService.currentNode().getId().equals(nid);\n    }\n\n    // ================ setter / getter ===============\n\n    public void setRowDataMemoryPipe(RowDataMemoryPipe rowDataMemoryPipe) {\n        this.rowDataMemoryPipe = rowDataMemoryPipe;\n    }\n\n    public void setAttachmentHttpPipe(AttachmentHttpPipe attachmentHttpPipe) {\n        this.attachmentHttpPipe = attachmentHttpPipe;\n    }\n\n    public void setRowDataRpcPipe(RowDataRpcPipe rowDataRpcPipe) {\n        this.rowDataRpcPipe = rowDataRpcPipe;\n    }\n\n    public void setConfigClientService(ConfigClientService configClientService) {\n        this.configClientService = configClientService;\n    }\n\n    public void setRowDataHttpPipe(RowDataHttpPipe rowDataHttpPipe) {\n        this.rowDataHttpPipe = rowDataHttpPipe;\n    }\n\n    public void setExecutorService(ExecutorService executorService) {\n        this.executorService = executorService;\n    }\n\n    public void setSizeThresold(long sizeThresold) {\n        this.sizeThresold = sizeThresold;\n    }\n\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/common/pipe/impl/http/AbstractHttpPipe.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.pipe.impl.http;\n\nimport java.io.File;\nimport java.io.RandomAccessFile;\nimport java.io.UnsupportedEncodingException;\nimport java.util.Set;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.ScheduledExecutorService;\nimport java.util.concurrent.TimeUnit;\n\nimport org.apache.commons.io.FileUtils;\nimport org.apache.commons.io.IOUtils;\nimport org.apache.commons.lang.StringUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.InitializingBean;\nimport org.springframework.util.Assert;\n\nimport com.alibaba.otter.node.common.config.ConfigClientService;\nimport com.alibaba.otter.node.etl.common.io.EncryptUtils;\nimport com.alibaba.otter.node.etl.common.io.EncryptedData;\nimport com.alibaba.otter.node.etl.common.io.download.DataRetrieverFactory;\nimport com.alibaba.otter.node.etl.common.io.signature.ChecksumException;\nimport com.alibaba.otter.node.etl.common.jetty.JettyEmbedServer;\nimport com.alibaba.otter.node.etl.common.pipe.Pipe;\nimport com.alibaba.otter.node.etl.common.pipe.exception.PipeException;\nimport com.alibaba.otter.shared.common.utils.ByteUtils;\nimport com.alibaba.otter.shared.common.utils.NioUtils;\nimport com.alibaba.otter.shared.common.utils.thread.NamedThreadFactory;\nimport com.google.common.collect.Sets;\n\n/**\n * 基于http下载的pipe实现\n * \n * @author jianghang 2011-10-13 下午06:31:13\n * @version 4.0.0\n */\npublic abstract class AbstractHttpPipe<T, KEY extends HttpPipeKey> implements Pipe<T, KEY>, InitializingBean {\n\n    protected static final Long               DEFAULT_PERIOD = 60 * 1000L;\n    protected static final String             UTF_8          = \"UTF-8\";\n    protected static final String             DATE_FORMAT    = \"yyyy-MM-dd-HH-mm-ss\";\n    protected static ScheduledExecutorService schedulor      = Executors.newScheduledThreadPool(1,\n                                                                                                new NamedThreadFactory(\n                                                                                                                       \"HttpPipe-Cleaner\")); ;\n    protected Logger                          logger         = LoggerFactory.getLogger(this.getClass());\n    protected JettyEmbedServer                jettyEmbedServer;                                                                             // 注入对象，确保server已经启动\n    protected Long                            period         = DEFAULT_PERIOD;\n    protected ConfigClientService             configClientService;\n    protected Long                            timeout        = 24 * 60 * 60 * 1000L;                                                        // 对应的超时时间,24小时\n    protected String                          htdocsDir;                                                                                    // http服务下载路径\n    protected String                          downloadDir;                                                                                  // 下载完成后目标路径\n    protected RemoteUrlBuilder                remoteUrlBuilder;\n    protected DataRetrieverFactory            dataRetrieverFactory;\n\n    public void afterPropertiesSet() throws Exception {\n        Assert.notNull(remoteUrlBuilder);\n        Assert.notNull(htdocsDir);\n        NioUtils.create(new File(htdocsDir), false, 3);\n        if (StringUtils.isEmpty(downloadDir)) {\n            downloadDir = htdocsDir;\n        } else {\n            NioUtils.create(new File(downloadDir), false, 3);\n        }\n        // 启动一下清理任务\n        schedulor.scheduleAtFixedRate(new Runnable() {\n\n            public void run() {\n                try {\n                    long threshold = System.currentTimeMillis() - timeout;\n                    File htdocsDirFile = new File(htdocsDir);\n                    File[] htdocsFiles = htdocsDirFile.listFiles();\n                    Set<File> files = Sets.newHashSet();\n                    for (File htdocsFile : htdocsFiles) {\n                        files.add(htdocsFile);\n                    }\n                    if (downloadDir.equals(htdocsDir) == false) {\n                        File downloadDirFile = new File(downloadDir);\n                        File[] downloadFiles = downloadDirFile.listFiles();\n                        for (File downloadFile : downloadFiles) {\n                            files.add(downloadFile);\n                        }\n                    }\n\n                    for (File file : files) {\n                        boolean isOld = FileUtils.isFileOlder(file, threshold);\n                        if (isOld) {\n                            NioUtils.delete(file, 3);\n                        }\n                    }\n                } catch (Exception e) {\n                    logger.error(\"old_file_clean_error\", e);\n                }\n            }\n        }, DEFAULT_PERIOD, DEFAULT_PERIOD, TimeUnit.MILLISECONDS);\n    }\n\n    protected EncryptedData encryptFile(File file) {\n        // 构造校验对象，这里考虑性能只将file path做为加密源\n        EncryptedData encryptedData = null;\n        try {\n            encryptedData = EncryptUtils.encrypt(file.getPath().getBytes(\"UTF-8\"));\n        } catch (UnsupportedEncodingException e) {\n            // ignore\n        }\n\n        // 写入校验信息\n        RandomAccessFile raf = null;\n        try {\n            raf = new RandomAccessFile(file, \"rw\");\n            long origLength = file.length();\n            int keyLength = ByteUtils.stringToBytes(encryptedData.getKey()).length;\n            int crcLength = ByteUtils.stringToBytes(encryptedData.getCrc()).length;\n            long totalLength = origLength + crcLength + keyLength;\n            raf.setLength(totalLength);\n            raf.seek(origLength);\n            raf.write(ByteUtils.stringToBytes(encryptedData.getKey()), 0, keyLength);\n            raf.seek(origLength + keyLength);\n            raf.write(ByteUtils.stringToBytes(encryptedData.getCrc()), 0, crcLength);\n        } catch (Exception e) {\n            throw new PipeException(\"write_encrypted_error\", e);\n        } finally {\n            IOUtils.closeQuietly(raf);\n        }\n\n        return encryptedData;\n    }\n\n    protected void decodeFile(File file, String key, String crc) {\n        // 读取校验信息\n        RandomAccessFile raf = null;\n        try {\n            raf = new RandomAccessFile(file, \"rw\");\n\n            long totallength = file.length();\n            int keyLength = ByteUtils.stringToBytes(key).length;\n            int crcLength = ByteUtils.stringToBytes(crc).length;\n            // 长度字段起始位\n            long pos = totallength - keyLength - crcLength;\n            // 游标\n            raf.seek(pos);\n            // 读取key内容\n            byte[] keyBytes = new byte[keyLength];\n            raf.read(keyBytes, 0, keyLength);\n            String keystr = ByteUtils.bytesToString(keyBytes);\n            if (!key.equals(keystr)) {\n                throw new ChecksumException(\"unmatch garble key with[\" + key + \"],[\" + keystr + \"]\");\n            }\n\n            // 读取校验码长度\n            raf.seek(pos + keyLength);\n            byte[] crcBytes = new byte[crcLength];\n            raf.read(crcBytes, 0, crcLength);\n            String crcStr = ByteUtils.bytesToString(crcBytes);\n            if (!crc.equals(crcStr)) {\n                throw new ChecksumException(\"unmatch crc with[\" + crc + \"],[\" + crcStr + \"]\");\n            }\n\n            // 设置文件长度\n            raf.setLength(pos);\n        } catch (Exception e) {\n            throw new PipeException(\"read_encrypted_error\", e);\n        } finally {\n            IOUtils.closeQuietly(raf);\n        }\n    }\n\n    // ==================== setter / getter ==================\n\n    public void setConfigClientService(ConfigClientService configClientService) {\n        this.configClientService = configClientService;\n    }\n\n    public void setJettyEmbedServer(JettyEmbedServer jettyEmbedServer) {\n        this.jettyEmbedServer = jettyEmbedServer;\n    }\n\n    public void setRemoteUrlBuilder(RemoteUrlBuilder remoteUrlBuilder) {\n        this.remoteUrlBuilder = remoteUrlBuilder;\n    }\n\n    public void setHtdocsDir(String htdocsDir) {\n        this.htdocsDir = htdocsDir;\n    }\n\n    public void setDownloadDir(String downloadDir) {\n        this.downloadDir = downloadDir;\n    }\n\n    public void setTimeout(Long timeout) {\n        this.timeout = timeout;\n    }\n\n    public void setPeriod(Long period) {\n        this.period = period;\n    }\n\n    public void setDataRetrieverFactory(DataRetrieverFactory dataRetrieverFactory) {\n        this.dataRetrieverFactory = dataRetrieverFactory;\n    }\n\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/common/pipe/impl/http/AttachmentHttpPipe.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.pipe.impl.http;\n\nimport java.io.File;\nimport java.io.InputStream;\nimport java.text.MessageFormat;\nimport java.text.SimpleDateFormat;\nimport java.util.Date;\nimport java.util.List;\n\nimport org.apache.commons.io.FilenameUtils;\nimport org.apache.commons.lang.ClassUtils;\nimport org.apache.commons.lang.StringUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.slf4j.MDC;\nimport org.springframework.beans.BeansException;\nimport org.springframework.beans.factory.BeanFactory;\nimport org.springframework.beans.factory.BeanFactoryAware;\n\nimport com.alibaba.otter.node.etl.OtterConstants;\nimport com.alibaba.otter.node.etl.common.io.EncryptedData;\nimport com.alibaba.otter.node.etl.common.io.download.DataRetriever;\nimport com.alibaba.otter.node.etl.common.pipe.PipeDataType;\nimport com.alibaba.otter.node.etl.common.pipe.exception.PipeException;\nimport com.alibaba.otter.node.etl.common.pipe.impl.http.archive.ArchiveBean;\nimport com.alibaba.otter.node.etl.common.pipe.impl.http.archive.ArchiveRetriverCallback;\nimport com.alibaba.otter.node.etl.common.pipe.impl.http.archive.LazyFileInputStream;\nimport com.alibaba.otter.node.etl.load.loader.db.FileloadDumper;\nimport com.alibaba.otter.shared.common.model.config.pipeline.Pipeline;\nimport com.alibaba.otter.shared.etl.model.FileBatch;\nimport com.alibaba.otter.shared.etl.model.FileData;\nimport com.alibaba.otter.shared.etl.model.Identity;\n\n/**\n * 基于文件附件的http协议的管道\n * \n * @author jianghang 2011-10-17 下午03:11:44\n * @version 4.0.0\n */\npublic class AttachmentHttpPipe extends AbstractHttpPipe<Object, HttpPipeKey> implements BeanFactoryAware {\n\n    private static final Logger logger  = LoggerFactory.getLogger(AttachmentHttpPipe.class);\n    private BeanFactory         beanFactory;\n    private boolean             encrypt = false;\n\n    @Override\n    public HttpPipeKey put(Object data) throws PipeException {\n        if (data instanceof FileBatch) {\n            return archiveFile((FileBatch) data);\n        } else {\n            throw new IllegalArgumentException(\"error argument\");\n        }\n    }\n\n    public File get(HttpPipeKey key) throws PipeException {\n        return unpackFile(key);\n    }\n\n    // 处理对应的附件\n    private HttpPipeKey archiveFile(final FileBatch fileBatch) {\n        // 处理构造对应的文件url\n        String filename = buildFileName(fileBatch.getIdentity(), ClassUtils.getShortClassName(fileBatch.getClass()));\n        File file = new File(htdocsDir, filename);\n        // 压缩对应的文件数据\n        List<FileData> fileDatas = fileBatch.getFiles();\n        Pipeline pipeline = configClientService.findPipeline(fileBatch.getIdentity().getPipelineId());\n        int poolSize = pipeline.getParameters().getFileLoadPoolSize();\n        boolean useLocalFileMutliThread = pipeline.getParameters().getUseLocalFileMutliThread();\n        ArchiveBean archiveBean = getArchiveBean();\n        archiveBean.adjustPoolSize(poolSize);// 调整线程池大小\n        archiveBean.setUseLocalFileMutliThread(useLocalFileMutliThread);// 设置是否启用local多线程同步\n        boolean done = archiveBean.pack(file, fileDatas, new ArchiveRetriverCallback<FileData>() {\n\n            public InputStream retrive(FileData fileData) {\n                boolean miss = false;\n                try {\n                    if (StringUtils.isNotEmpty(fileData.getNameSpace())) {\n                        throw new RuntimeException(fileData + \" is not support!\");\n                    } else {\n                        File source = new File(fileData.getPath());\n                        if (source.exists() && source.isFile()) {\n                            return new LazyFileInputStream(source);\n                        } else {\n                            miss = true;\n                            return null;\n                        }\n                    }\n                } finally {\n                    if (miss && logger.isInfoEnabled()) {\n                        MDC.put(OtterConstants.splitPipelineLoadLogFileKey,\n                                String.valueOf(fileBatch.getIdentity().getPipelineId()));\n                        logger.info(FileloadDumper.dumpMissFileDatas(fileBatch.getIdentity(), fileData));\n                    }\n                }\n\n            }\n        });\n\n        if (done == false) {\n            return null; // 直接返回\n        }\n\n        HttpPipeKey key = new HttpPipeKey();\n        key.setUrl(remoteUrlBuilder.getUrl(fileBatch.getIdentity().getPipelineId(), filename));\n        key.setDataType(PipeDataType.FILE_BATCH);\n        key.setIdentity(fileBatch.getIdentity());\n        if (encrypt || pipeline.getParameters().getUseFileEncrypt()) {\n            // 加密处理\n            EncryptedData encryptedData = encryptFile(file);\n            key.setKey(encryptedData.getKey());\n            key.setCrc(encryptedData.getCrc());\n        }\n        return key;\n    }\n\n    // 处理对应的附件\n    private File unpackFile(HttpPipeKey key) {\n        Pipeline pipeline = configClientService.findPipeline(key.getIdentity().getPipelineId());\n        DataRetriever dataRetriever = dataRetrieverFactory.createRetriever(pipeline.getParameters().getRetriever(),\n                                                                           key.getUrl(), downloadDir);\n        File archiveFile = null;\n        try {\n            dataRetriever.connect();\n            dataRetriever.doRetrieve();\n            archiveFile = dataRetriever.getDataAsFile();\n        } catch (Exception e) {\n            dataRetriever.abort();\n            throw new PipeException(\"download_error\", e);\n        } finally {\n            dataRetriever.disconnect();\n        }\n\n        // 处理下有加密的数据\n        if (StringUtils.isNotEmpty(key.getKey()) && StringUtils.isNotEmpty(key.getCrc())) {\n            decodeFile(archiveFile, key.getKey(), key.getCrc());\n        }\n\n        // 去除末尾的.gzip后缀，做为解压目录\n        String dir = StringUtils.removeEnd(archiveFile.getPath(),\n                                           FilenameUtils.EXTENSION_SEPARATOR_STR\n                                                   + FilenameUtils.getExtension(archiveFile.getPath()));\n        File unpackDir = new File(dir);\n        // 开始解压\n        getArchiveBean().unpack(archiveFile, unpackDir);\n        return unpackDir;\n    }\n\n    // 构造文件名\n    private String buildFileName(Identity identity, String prefix) {\n        Date now = new Date();\n        String time = new SimpleDateFormat(DATE_FORMAT).format(now);\n        return MessageFormat.format(\"{0}-{1}-{2}-{3}-{4}.gzip\", prefix, time, String.valueOf(identity.getChannelId()),\n                                    String.valueOf(identity.getPipelineId()), String.valueOf(identity.getProcessId()));\n    }\n\n    private ArchiveBean getArchiveBean() {\n        // archiveBean做了池化处理，所以每次需要重容器里拿一次\n        return (ArchiveBean) beanFactory.getBean(\"archiveBean\", ArchiveBean.class);\n    }\n\n    // ================== setter / getter ===================\n\n    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {\n        this.beanFactory = beanFactory;\n    }\n\n    public void setEncrypt(boolean encrypt) {\n        this.encrypt = encrypt;\n    }\n\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/common/pipe/impl/http/HttpPipeKey.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.pipe.impl.http;\n\nimport com.alibaba.otter.node.etl.common.pipe.PipeKey;\nimport com.alibaba.otter.shared.etl.model.Identity;\n\n/**\n * 基于Http协议下载的pipe key实现\n * \n * @author jianghang 2011-10-13 下午05:37:24\n * @version 4.0.0\n */\npublic class HttpPipeKey extends PipeKey {\n\n    private static final long serialVersionUID = 2926519897517494101L;\n    private Identity          identity;\n    private String            crc;                                    // checksum数字串\n    private String            key;                                    // 密钥串\n    private String            url;                                    // 数据文件\n\n    public String getCrc() {\n        return crc;\n    }\n\n    public void setCrc(String crc) {\n        this.crc = crc;\n    }\n\n    public String getKey() {\n        return key;\n    }\n\n    public void setKey(String key) {\n        this.key = key;\n    }\n\n    public String getUrl() {\n        return url;\n    }\n\n    public void setUrl(String url) {\n        this.url = url;\n    }\n\n    public Identity getIdentity() {\n        return identity;\n    }\n\n    public void setIdentity(Identity identity) {\n        this.identity = identity;\n    }\n\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/common/pipe/impl/http/LimitedInputStream.java",
    "content": "package com.alibaba.otter.node.etl.common.pipe.impl.http;\n\nimport java.io.FilterInputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\n\n/**\n * copy from google protobuf，支持带limit限制读取数量的功能，将stream可用于流式读取\n * \n * @author jianghang 2013-8-29 下午3:34:32\n * @since 4.2.1\n */\npublic class LimitedInputStream extends FilterInputStream {\n\n    private int limit;\n\n    public LimitedInputStream(InputStream in, int limit){\n        super(in);\n        this.limit = limit;\n    }\n\n    @Override\n    public int available() throws IOException {\n        return Math.min(super.available(), limit);\n    }\n\n    @Override\n    public int read() throws IOException {\n        if (limit <= 0) {\n            return -1;\n        }\n        final int result = super.read();\n        if (result >= 0) {\n            --limit;\n        }\n        return result;\n    }\n\n    @Override\n    public int read(final byte[] b, final int off, int len) throws IOException {\n        if (limit <= 0) {\n            return -1;\n        }\n        len = Math.min(len, limit);\n        final int result = super.read(b, off, len);\n        if (result >= 0) {\n            limit -= result;\n        }\n        return result;\n    }\n\n    @Override\n    public long skip(final long n) throws IOException {\n        final long result = super.skip(Math.min(n, limit));\n        if (result >= 0) {\n            limit -= result;\n        }\n        return result;\n    }\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/common/pipe/impl/http/RemoteUrlBuilder.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.pipe.impl.http;\n\nimport java.text.MessageFormat;\n\nimport org.apache.commons.lang.StringUtils;\nimport org.springframework.beans.factory.InitializingBean;\nimport org.springframework.util.Assert;\n\nimport com.alibaba.otter.node.common.config.ConfigClientService;\nimport com.alibaba.otter.node.etl.common.pipe.impl.http.archive.ArchiveException;\nimport com.alibaba.otter.shared.common.model.config.node.Node;\nimport com.alibaba.otter.shared.common.model.config.pipeline.Pipeline;\n\n/**\n * 远程URL组装实现类\n */\npublic class RemoteUrlBuilder implements InitializingBean {\n\n    private int                 defaultDownloadPort = 8080;\n    private ConfigClientService configClientService;\n    private String              urlFormat;\n\n    public void afterPropertiesSet() throws Exception {\n        Assert.notNull(configClientService);\n        Assert.notNull(urlFormat);\n    }\n\n    public String getUrl(Long pipelineId, String filePath) {\n        Node node = configClientService.currentNode();\n        Pipeline pipeline = configClientService.findPipeline(pipelineId);\n        String ip = node.getIp();\n        if (node.getParameters().getUseExternalIp() || pipeline.getParameters().getUseExternalIp()) {\n            ip = node.getParameters().getExternalIp();\n\n            if (StringUtils.isEmpty(ip)) {\n                throw new ArchiveException(String.format(\"pipelineId:%s useExternalIp by nid[%s] has no external ip\",\n                    String.valueOf(pipelineId),\n                    String.valueOf(node.getId())));\n            }\n        }\n\n        Integer port = node.getParameters().getDownloadPort();// 注意为其下载端口\n        if (port == null || port < 0) {\n            port = defaultDownloadPort;\n        }\n\n        return MessageFormat.format(urlFormat, ip, String.valueOf(port), filePath);\n    }\n\n    // ================= setter / getter =====================\n\n    public void setDefaultDownloadPort(int defaultDownloadPort) {\n        this.defaultDownloadPort = defaultDownloadPort;\n    }\n\n    public void setUrlFormat(String urlFormat) {\n        this.urlFormat = urlFormat;\n    }\n\n    public void setConfigClientService(ConfigClientService configClientService) {\n        this.configClientService = configClientService;\n    }\n\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/common/pipe/impl/http/RowDataHttpPipe.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.pipe.impl.http;\n\nimport java.io.BufferedInputStream;\nimport java.io.BufferedOutputStream;\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.OutputStream;\nimport java.text.MessageFormat;\nimport java.text.SimpleDateFormat;\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.List;\n\nimport org.apache.commons.io.IOUtils;\nimport org.apache.commons.lang.ClassUtils;\nimport org.apache.commons.lang.StringUtils;\nimport org.springframework.util.CollectionUtils;\n\nimport com.alibaba.fastjson.JSONReader;\nimport com.alibaba.otter.node.etl.common.io.EncryptedData;\nimport com.alibaba.otter.node.etl.common.io.download.DataRetriever;\nimport com.alibaba.otter.node.etl.common.pipe.PipeDataType;\nimport com.alibaba.otter.node.etl.common.pipe.exception.PipeException;\nimport com.alibaba.otter.node.etl.model.protobuf.BatchProto;\nimport com.alibaba.otter.shared.common.model.config.channel.ChannelParameter.SyncConsistency;\nimport com.alibaba.otter.shared.common.model.config.channel.ChannelParameter.SyncMode;\nimport com.alibaba.otter.shared.common.model.config.pipeline.Pipeline;\nimport com.alibaba.otter.shared.common.utils.ByteUtils;\nimport com.alibaba.otter.shared.etl.model.DbBatch;\nimport com.alibaba.otter.shared.etl.model.EventColumn;\nimport com.alibaba.otter.shared.etl.model.EventData;\nimport com.alibaba.otter.shared.etl.model.EventType;\nimport com.alibaba.otter.shared.etl.model.FileBatch;\nimport com.alibaba.otter.shared.etl.model.FileData;\nimport com.alibaba.otter.shared.etl.model.Identity;\nimport com.alibaba.otter.shared.etl.model.RowBatch;\n\n/**\n * 基于http下载的pipe实现\n * \n * @author jianghang 2011-10-13 下午06:31:13\n * @version 4.0.0\n */\npublic class RowDataHttpPipe extends AbstractHttpPipe<DbBatch, HttpPipeKey> {\n\n    public HttpPipeKey put(final DbBatch data) throws PipeException {\n        return saveDbBatch(data);\n    }\n\n    public DbBatch get(final HttpPipeKey key) throws PipeException {\n        // 处理dbBatch数据\n        return getDbBatch(key);\n    }\n\n    // ======================== help method ===================\n    // 保存对应的dbBatch\n    private HttpPipeKey saveDbBatch(DbBatch dbBatch) {\n        RowBatch rowBatch = dbBatch.getRowBatch();\n        // 转化为proto对象\n        BatchProto.RowBatch.Builder rowBatchBuilder = BatchProto.RowBatch.newBuilder();\n        rowBatchBuilder.setIdentity(build(rowBatch.getIdentity()));\n        // 处理具体的字段rowData\n        for (EventData eventData : rowBatch.getDatas()) {\n            BatchProto.RowData.Builder rowDataBuilder = BatchProto.RowData.newBuilder();\n            rowDataBuilder.setPairId(eventData.getPairId());\n            rowDataBuilder.setTableId(eventData.getTableId());\n            if (eventData.getSchemaName() != null) {\n                rowDataBuilder.setSchemaName(eventData.getSchemaName());\n            }\n            rowDataBuilder.setTableName(eventData.getTableName());\n            rowDataBuilder.setEventType(eventData.getEventType().getValue());\n            rowDataBuilder.setExecuteTime(eventData.getExecuteTime());\n            // add by ljh at 2012-10-31\n            if (eventData.getSyncMode() != null) {\n                rowDataBuilder.setSyncMode(eventData.getSyncMode().getValue());\n            }\n            if (eventData.getSyncConsistency() != null) {\n                rowDataBuilder.setSyncConsistency(eventData.getSyncConsistency().getValue());\n            }\n\n            // 构造key column\n            for (EventColumn keyColumn : eventData.getKeys()) {\n                rowDataBuilder.addKeys(buildColumn(keyColumn));\n            }\n            // 构造old key column\n            if (CollectionUtils.isEmpty(eventData.getOldKeys()) == false) {\n                for (EventColumn keyColumn : eventData.getOldKeys()) {\n                    rowDataBuilder.addOldKeys(buildColumn(keyColumn));\n                }\n            }\n\n            // 构造其他 column\n            for (EventColumn column : eventData.getColumns()) {\n                rowDataBuilder.addColumns(buildColumn(column));\n            }\n\n            rowDataBuilder.setRemedy(eventData.isRemedy());\n            rowDataBuilder.setSize(eventData.getSize());\n            if (StringUtils.isNotEmpty(eventData.getSql())) {\n                rowDataBuilder.setSql(eventData.getSql());\n            }\n            if (StringUtils.isNotEmpty(eventData.getDdlSchemaName())) {\n                rowDataBuilder.setDdlSchemaName(eventData.getDdlSchemaName());\n            }\n            if (StringUtils.isNotEmpty(eventData.getHint())) {\n                rowDataBuilder.setHint(eventData.getHint());\n            }\n            rowDataBuilder.setWithoutSchema(eventData.isWithoutSchema());\n            rowBatchBuilder.addRows(rowDataBuilder.build());// 添加一条rowData记录\n        }\n\n        // 处理下FileBatch\n        FileBatch fileBatch = dbBatch.getFileBatch();\n        BatchProto.FileBatch.Builder fileBatchBuilder = null;\n        fileBatchBuilder = BatchProto.FileBatch.newBuilder();\n        fileBatchBuilder.setIdentity(build(fileBatch.getIdentity()));\n        // 构造对应的proto对象\n        for (FileData fileData : fileBatch.getFiles()) {\n            BatchProto.FileData.Builder fileDataBuilder = BatchProto.FileData.newBuilder();\n            fileDataBuilder.setPairId(fileData.getPairId());\n            fileDataBuilder.setTableId(fileData.getTableId());\n            if (fileData.getNameSpace() != null) {\n                fileDataBuilder.setNamespace(fileData.getNameSpace());\n            }\n            if (fileData.getPath() != null) {\n                fileDataBuilder.setPath(fileData.getPath());\n            }\n            fileDataBuilder.setEventType(fileData.getEventType().getValue());\n            fileDataBuilder.setSize(fileData.getSize());\n            fileDataBuilder.setLastModifiedTime(fileData.getLastModifiedTime());\n\n            fileBatchBuilder.addFiles(fileDataBuilder.build());// 添加一条fileData记录\n        }\n        // 处理构造对应的文件url\n        String filename = buildFileName(rowBatch.getIdentity(), ClassUtils.getShortClassName(dbBatch.getClass()));\n        // 写入数据\n        File file = new File(htdocsDir, filename);\n        OutputStream output = null;\n        try {\n            output = new BufferedOutputStream(new FileOutputStream(file));\n            com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowBatch rowBatchProto = rowBatchBuilder.build();\n            output.write(ByteUtils.int2bytes(rowBatchProto.getSerializedSize()));// 输出大小\n            rowBatchProto.writeTo(output);// 输出row batch\n\n            com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileBatch fileBatchProto = fileBatchBuilder.build();\n            output.write(ByteUtils.int2bytes(fileBatchProto.getSerializedSize()));// 输出大小\n            fileBatchProto.writeTo(output); // 输出file batch\n            output.flush();\n        } catch (IOException e) {\n            throw new PipeException(\"write_byte_error\", e);\n        } finally {\n            IOUtils.closeQuietly(output);\n        }\n\n        HttpPipeKey key = new HttpPipeKey();\n        key.setUrl(remoteUrlBuilder.getUrl(rowBatch.getIdentity().getPipelineId(), filename));\n        key.setDataType(PipeDataType.DB_BATCH);\n        key.setIdentity(rowBatch.getIdentity());\n        Pipeline pipeline = configClientService.findPipeline(rowBatch.getIdentity().getPipelineId());\n        if (pipeline.getParameters().getUseFileEncrypt()) {\n            // 加密处理\n            EncryptedData encryptedData = encryptFile(file);\n            key.setKey(encryptedData.getKey());\n            key.setCrc(encryptedData.getCrc());\n        }\n\n        return key;\n    }\n\n    // 处理对应的dbBatch\n    private DbBatch getDbBatch(HttpPipeKey key) {\n        String dataUrl = key.getUrl();\n        Pipeline pipeline = configClientService.findPipeline(key.getIdentity().getPipelineId());\n        DataRetriever dataRetriever = dataRetrieverFactory.createRetriever(pipeline.getParameters().getRetriever(),\n            dataUrl,\n            downloadDir);\n        File archiveFile = null;\n        try {\n            dataRetriever.connect();\n            dataRetriever.doRetrieve();\n            archiveFile = dataRetriever.getDataAsFile();\n        } catch (Exception e) {\n            dataRetriever.abort();\n            throw new PipeException(\"download_error\", e);\n        } finally {\n            dataRetriever.disconnect();\n        }\n\n        // 处理下有加密的数据\n        if (StringUtils.isNotEmpty(key.getKey()) && StringUtils.isNotEmpty(key.getCrc())) {\n            decodeFile(archiveFile, key.getKey(), key.getCrc());\n        }\n\n        InputStream input = null;\n        JSONReader reader = null;\n        try {\n            input = new BufferedInputStream(new FileInputStream(archiveFile));\n            DbBatch dbBatch = new DbBatch();\n            byte[] lengthBytes = new byte[4];\n            input.read(lengthBytes);\n            int length = ByteUtils.bytes2int(lengthBytes);\n            BatchProto.RowBatch rowbatchProto = BatchProto.RowBatch.parseFrom(new LimitedInputStream(input, length));\n            // 构造原始的model对象\n            RowBatch rowBatch = new RowBatch();\n            rowBatch.setIdentity(build(rowbatchProto.getIdentity()));\n            for (BatchProto.RowData rowDataProto : rowbatchProto.getRowsList()) {\n                EventData eventData = new EventData();\n                eventData.setPairId(rowDataProto.getPairId());\n                eventData.setTableId(rowDataProto.getTableId());\n                eventData.setTableName(rowDataProto.getTableName());\n                eventData.setSchemaName(rowDataProto.getSchemaName());\n                eventData.setEventType(EventType.valuesOf(rowDataProto.getEventType()));\n                eventData.setExecuteTime(rowDataProto.getExecuteTime());\n                // add by ljh at 2012-10-31\n                if (StringUtils.isNotEmpty(rowDataProto.getSyncMode())) {\n                    eventData.setSyncMode(SyncMode.valuesOf(rowDataProto.getSyncMode()));\n                }\n                if (StringUtils.isNotEmpty(rowDataProto.getSyncConsistency())) {\n                    eventData.setSyncConsistency(SyncConsistency.valuesOf(rowDataProto.getSyncConsistency()));\n                }\n                // 处理主键\n                List<EventColumn> keys = new ArrayList<EventColumn>();\n                for (BatchProto.Column columnProto : rowDataProto.getKeysList()) {\n                    keys.add(buildColumn(columnProto));\n                }\n                eventData.setKeys(keys);\n                // 处理old主键\n                if (CollectionUtils.isEmpty(rowDataProto.getOldKeysList()) == false) {\n                    List<EventColumn> oldKeys = new ArrayList<EventColumn>();\n                    for (BatchProto.Column columnProto : rowDataProto.getOldKeysList()) {\n                        oldKeys.add(buildColumn(columnProto));\n                    }\n                    eventData.setOldKeys(oldKeys);\n                }\n                // 处理具体的column value\n                List<EventColumn> columns = new ArrayList<EventColumn>();\n                for (BatchProto.Column columnProto : rowDataProto.getColumnsList()) {\n                    columns.add(buildColumn(columnProto));\n                }\n                eventData.setColumns(columns);\n\n                eventData.setRemedy(rowDataProto.getRemedy());\n                eventData.setSize(rowDataProto.getSize());\n                eventData.setSql(rowDataProto.getSql());\n                eventData.setDdlSchemaName(rowDataProto.getDdlSchemaName());\n                eventData.setHint(rowDataProto.getHint());\n                eventData.setWithoutSchema(rowDataProto.getWithoutSchema());\n                // 添加到总记录\n                rowBatch.merge(eventData);\n            }\n            dbBatch.setRowBatch(rowBatch);\n\n            input.read(lengthBytes);\n            length = ByteUtils.bytes2int(lengthBytes);\n            BatchProto.FileBatch filebatchProto = BatchProto.FileBatch.parseFrom(new LimitedInputStream(input, length));\n            // 构造原始的model对象\n            FileBatch fileBatch = new FileBatch();\n            fileBatch.setIdentity(build(filebatchProto.getIdentity()));\n            for (BatchProto.FileData fileDataProto : filebatchProto.getFilesList()) {\n                FileData fileData = new FileData();\n                fileData.setPairId(fileDataProto.getPairId());\n                fileData.setTableId(fileDataProto.getTableId());\n                fileData.setEventType(EventType.valuesOf(fileDataProto.getEventType()));\n                fileData.setLastModifiedTime(fileDataProto.getLastModifiedTime());\n                fileData.setNameSpace(fileDataProto.getNamespace());\n                fileData.setPath(fileDataProto.getPath());\n                fileData.setSize(fileDataProto.getSize());\n                // 添加到filebatch中\n                fileBatch.getFiles().add(fileData);\n            }\n            dbBatch.setFileBatch(fileBatch);\n            return dbBatch;\n        } catch (IOException e) {\n            throw new PipeException(\"deserial_error\", e);\n        } finally {\n            IOUtils.closeQuietly(reader);\n        }\n    }\n\n    private EventColumn buildColumn(BatchProto.Column columnProto) {\n        EventColumn column = new EventColumn();\n        column.setColumnName(columnProto.getName());\n        column.setNull(columnProto.getIsNull());\n        column.setColumnType(columnProto.getType());\n        column.setColumnValue(columnProto.getValue());\n        column.setKey(columnProto.getIsPrimaryKey());\n        column.setIndex(columnProto.getIndex());\n        column.setUpdate(columnProto.getIsUpdate());// add by ljh\n                                                    // 2012-08-30，标记变更字段\n        return column;\n    }\n\n    private BatchProto.Column buildColumn(EventColumn keyColumn) {\n        BatchProto.Column.Builder columnBuilder = BatchProto.Column.newBuilder();\n        columnBuilder.setName(keyColumn.getColumnName());\n        columnBuilder.setType(keyColumn.getColumnType());\n        columnBuilder.setIsNull(keyColumn.isNull());\n        columnBuilder.setIsPrimaryKey(keyColumn.isKey());\n        columnBuilder.setIndex(keyColumn.getIndex());\n        if (keyColumn.getColumnValue() != null) {\n            columnBuilder.setValue(keyColumn.getColumnValue());\n        }\n        columnBuilder.setIsUpdate(keyColumn.isUpdate());// add by ljh\n                                                        // 2012-08-30，标记变更字段\n        return columnBuilder.build();\n    }\n\n    // 构造文件名\n    private String buildFileName(Identity identity, String prefix) {\n        Date now = new Date();\n        String time = new SimpleDateFormat(DATE_FORMAT).format(now);\n        return MessageFormat.format(\"{0}-{1}-{2}-{3}-{4}.gzip\",\n            prefix,\n            time,\n            String.valueOf(identity.getChannelId()),\n            String.valueOf(identity.getPipelineId()),\n            String.valueOf(identity.getProcessId()));\n    }\n\n    // 构造proto对象\n    private BatchProto.Identity build(Identity identity) {\n        BatchProto.Identity.Builder identityBuilder = BatchProto.Identity.newBuilder();\n        identityBuilder.setChannelId(identity.getChannelId());\n        identityBuilder.setPipelineId(identity.getPipelineId());\n        identityBuilder.setProcessId(identity.getProcessId());\n        return identityBuilder.build();\n    }\n\n    // 从proto对象构造回object\n    private Identity build(BatchProto.Identity identityProto) {\n        Identity identity = new Identity();\n        identity.setChannelId(identityProto.getChannelId());\n        identity.setPipelineId(identityProto.getPipelineId());\n        identity.setProcessId(identityProto.getProcessId());\n        return identity;\n    }\n\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/common/pipe/impl/http/archive/ArchiveBean.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.pipe.impl.http.archive;\n\nimport java.io.BufferedOutputStream;\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileNotFoundException;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.OutputStream;\nimport java.util.ArrayList;\nimport java.util.Enumeration;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.concurrent.ArrayBlockingQueue;\nimport java.util.concurrent.BlockingQueue;\nimport java.util.concurrent.Callable;\nimport java.util.concurrent.ExecutionException;\nimport java.util.concurrent.ExecutorCompletionService;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Future;\nimport java.util.concurrent.LinkedBlockingQueue;\nimport java.util.concurrent.ThreadPoolExecutor;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.TimeoutException;\nimport java.util.zip.Deflater;\n\nimport org.apache.commons.io.FileUtils;\nimport org.apache.commons.io.FilenameUtils;\nimport org.apache.commons.io.IOUtils;\nimport org.apache.commons.lang.StringUtils;\nimport org.springframework.beans.factory.DisposableBean;\nimport org.springframework.beans.factory.InitializingBean;\n\nimport com.alibaba.otter.shared.common.utils.NioUtils;\nimport com.alibaba.otter.shared.common.utils.thread.NamedThreadFactory;\nimport com.alibaba.otter.shared.etl.model.FileData;\n\nimport de.schlichtherle.util.zip.ZipEntry;\nimport de.schlichtherle.util.zip.ZipFile;\nimport de.schlichtherle.util.zip.ZipOutputStream;\n\n/**\n * 文档归档压缩/解压工具\n * \n * <pre>\n * 优化思路：\n * 1. 针对网络服务文件，考虑使用多线程进行数据获取 (压缩效率 << 获取网络文件数据I/O latency)，提前获取网络文件\n * 2. 针对本地文件，直接进行数据流压缩\n * \n * </pre>\n * \n * @author jianghang 2011-10-11 下午04:44:07\n * @version 4.0.0\n */\npublic class ArchiveBean implements InitializingBean, DisposableBean {\n\n    private static final int    DEFAULT_POOL_SIZE       = 5;\n    private static final String WORKER_NAME             = \"AttachmentHttpPipe\";\n    private int                 poolSize                = DEFAULT_POOL_SIZE;\n    private ExecutorService     executor;\n    private int                 retry                   = 3;\n    private boolean             useLocalFileMutliThread = true;\n\n    public static class ArchiveEntry {\n\n        private String      name;\n        private File        localFile = null;\n        private InputStream stream    = null;\n\n        public ArchiveEntry(String name){\n            this.name = name;\n        }\n\n        public ArchiveEntry(String name, InputStream stream){\n            this.name = name;\n            this.stream = stream;\n        }\n\n        public ArchiveEntry(String name, File localFile){\n            this.name = name;\n            this.localFile = localFile;\n        }\n\n        public String getName() {\n            return name;\n        }\n\n        public void setName(String name) {\n            this.name = name;\n        }\n\n        public InputStream getStream() {\n            if (localFile != null) {\n                try {\n                    return new FileInputStream(localFile);\n                } catch (FileNotFoundException e) {\n                    throw new ArchiveException(e);\n                }\n            } else {\n                return stream;\n            }\n        }\n\n        public void setStream(InputStream stream) {\n            this.stream = stream;\n        }\n\n    }\n\n    /**\n     * 将对应的FileData数据的文件，压缩到指定的目标targetArchiveFile上 <br/>\n     * 需要进行retry处理，解决java.io.IOException: Input/output error\n     */\n    public boolean pack(final File targetArchiveFile, List<FileData> fileDatas,\n                        final ArchiveRetriverCallback<FileData> callback) throws ArchiveException {\n        int count = 0;\n        Exception exception = null;\n        while (++count <= retry) {\n            try {\n                return doPack(targetArchiveFile, fileDatas, callback);\n            } catch (Exception ex) {\n                exception = ex;\n            }\n        }\n\n        throw new ArchiveException(\"pack fileDatas error!\", exception);\n    }\n\n    /**\n     * 执行压缩\n     */\n    @SuppressWarnings(\"resource\")\n    private boolean doPack(final File targetArchiveFile, List<FileData> fileDatas,\n                           final ArchiveRetriverCallback<FileData> callback) {\n        // 首先判断下对应的目标文件是否存在，如存在则执行删除\n        if (true == targetArchiveFile.exists() && false == NioUtils.delete(targetArchiveFile, 3)) {\n            throw new ArchiveException(String.format(\"[%s] exist and delete failed\",\n                targetArchiveFile.getAbsolutePath()));\n        }\n\n        boolean exist = false;\n        ZipOutputStream zipOut = null;\n        Set<String> entryNames = new HashSet<String>();\n        BlockingQueue<Future<ArchiveEntry>> queue = new LinkedBlockingQueue<Future<ArchiveEntry>>(); // 下载成功的任务列表\n        ExecutorCompletionService completionService = new ExecutorCompletionService(executor, queue);\n\n        final File targetDir = new File(targetArchiveFile.getParentFile(),\n            FilenameUtils.getBaseName(targetArchiveFile.getPath()));\n        try {\n            // 创建一个临时目录\n            FileUtils.forceMkdir(targetDir);\n\n            zipOut = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(targetArchiveFile)));\n            zipOut.setLevel(Deflater.BEST_SPEED);\n            // 进行并发压缩处理\n            for (final FileData fileData : fileDatas) {\n                if (fileData.getEventType().isDelete()) {\n                    continue; // 忽略delete类型的数据打包，因为只需直接在目标进行删除\n                }\n\n                String namespace = fileData.getNameSpace();\n                String path = fileData.getPath();\n                boolean isLocal = StringUtils.isBlank(namespace);\n                String entryName = null;\n                if (true == isLocal) {\n                    entryName = FilenameUtils.getPath(path) + FilenameUtils.getName(path);\n                } else {\n                    entryName = namespace + File.separator + path;\n                }\n\n                // 过滤一些重复的文件数据同步\n                if (entryNames.contains(entryName) == false) {\n                    entryNames.add(entryName);\n                } else {\n                    continue;\n                }\n\n                final String name = entryName;\n                if (true == isLocal && !useLocalFileMutliThread) {\n                    // 采用串行处理，不走临时文件\n                    queue.add(new DummyFuture(new ArchiveEntry(name, callback.retrive(fileData))));\n                } else {\n                    completionService.submit(new Callable<ArchiveEntry>() {\n\n                        public ArchiveEntry call() throws Exception {\n                            // 处理下异常，可能失败\n                            InputStream input = null;\n                            OutputStream output = null;\n                            try {\n                                input = callback.retrive(fileData);\n\n                                if (input instanceof LazyFileInputStream) {\n                                    input = ((LazyFileInputStream) input).getInputSteam();// 获取原始的stream\n                                }\n\n                                if (input != null) {\n                                    File tmp = new File(targetDir, name);\n                                    NioUtils.create(tmp.getParentFile(), false, 3);// 尝试创建父路径\n                                    output = new FileOutputStream(tmp);\n                                    NioUtils.copy(input, output);// 拷贝到文件\n                                    return new ArchiveEntry(name, new File(targetDir, name));\n                                } else {\n                                    return new ArchiveEntry(name);\n                                }\n                            } finally {\n                                IOUtils.closeQuietly(input);\n                                IOUtils.closeQuietly(output);\n                            }\n                        }\n                    });\n                }\n            }\n\n            for (int i = 0; i < entryNames.size(); i++) {\n                // 读入流\n                ArchiveEntry input = null;\n                InputStream stream = null;\n                try {\n                    input = queue.take().get();\n                    if (input == null) {\n                        continue;\n                    }\n\n                    stream = input.getStream();\n                    if (stream == null) {\n                        continue;\n                    }\n\n                    if (stream instanceof LazyFileInputStream) {\n                        stream = ((LazyFileInputStream) stream).getInputSteam();// 获取原始的stream\n                    }\n\n                    exist = true;\n                    zipOut.putNextEntry(new ZipEntry(input.getName()));\n                    NioUtils.copy(stream, zipOut);// 输出到压缩流中\n                    zipOut.closeEntry();\n                } finally {\n                    IOUtils.closeQuietly(stream);\n                }\n            }\n\n            if (exist) {\n                zipOut.finish();\n            }\n        } catch (Exception e) {\n            throw new ArchiveException(e);\n        } finally {\n            IOUtils.closeQuietly(zipOut);\n            try {\n                FileUtils.deleteDirectory(targetDir);// 删除临时目录\n            } catch (IOException e) {\n                // ignore\n            }\n        }\n\n        return exist;\n    }\n\n    public List<File> unpack(File archiveFile, File targetDir) throws ArchiveException {\n        // 首先判断下对应的目标文件是否存在，如存在则执行删除\n        if (false == archiveFile.exists()) {\n            throw new ArchiveException(String.format(\"[%s] not exist\", archiveFile.getAbsolutePath()));\n        }\n        if (false == targetDir.exists() && false == NioUtils.create(targetDir, false, 3)) {\n            throw new ArchiveException(String.format(\"[%s] not exist and create failed\", targetDir.getAbsolutePath()));\n        }\n\n        List<File> result = new ArrayList<File>();\n        ZipFile zipFile = null;\n        try {\n            zipFile = new ZipFile(archiveFile);\n            Enumeration entries = zipFile.entries();\n\n            while (entries.hasMoreElements()) {\n                // entry\n                ZipEntry entry = (ZipEntry) entries.nextElement();\n                String entryName = entry.getName();\n                // target\n                File targetFile = new File(targetDir, entryName);\n                NioUtils.create(targetFile.getParentFile(), false, 3);// 尝试创建父路径\n                InputStream input = null;\n                OutputStream output = null;\n                try {\n                    output = new FileOutputStream(targetFile);\n                    input = zipFile.getInputStream(entry);\n                    NioUtils.copy(input, output);\n                } finally {\n                    IOUtils.closeQuietly(input);\n                    IOUtils.closeQuietly(output);\n                }\n                result.add(targetFile);\n            }\n\n        } catch (Exception e) {\n            throw new ArchiveException(e);\n        } finally {\n            if (zipFile != null) {\n                try {\n                    zipFile.close();\n                } catch (IOException ex) {\n                }\n            }\n        }\n\n        return result;\n    }\n\n    private static class DummyFuture implements Future<ArchiveEntry> {\n\n        private ArchiveEntry entry;\n\n        public DummyFuture(ArchiveEntry entry){\n            this.entry = entry;\n        }\n\n        public boolean cancel(boolean mayInterruptIfRunning) {\n            return false;\n        }\n\n        public ArchiveEntry get() throws InterruptedException, ExecutionException {\n            return entry;\n        }\n\n        public ArchiveEntry get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException,\n                                                            TimeoutException {\n            return entry;\n        }\n\n        public boolean isCancelled() {\n            return false;\n        }\n\n        public boolean isDone() {\n            return false;\n        }\n\n    }\n\n    // 调整一下线程池\n    public void adjustPoolSize(int newPoolSize) {\n        if (newPoolSize != poolSize) {\n            poolSize = newPoolSize;\n            if (executor instanceof ThreadPoolExecutor) {\n                ThreadPoolExecutor pool = (ThreadPoolExecutor) executor;\n                pool.setCorePoolSize(newPoolSize);\n                pool.setMaximumPoolSize(newPoolSize);\n            }\n        }\n    }\n\n    public void afterPropertiesSet() throws Exception {\n        executor = new ThreadPoolExecutor(poolSize,\n            poolSize,\n            0L,\n            TimeUnit.MILLISECONDS,\n            new ArrayBlockingQueue(poolSize * 4),\n            new NamedThreadFactory(WORKER_NAME),\n            new ThreadPoolExecutor.CallerRunsPolicy());\n    }\n\n    public void destroy() throws Exception {\n        executor.shutdownNow();\n    }\n\n    public void setPoolSize(int poolSize) {\n        this.poolSize = poolSize;\n    }\n\n    public void setUseLocalFileMutliThread(boolean useLocalFileMutliThread) {\n        this.useLocalFileMutliThread = useLocalFileMutliThread;\n    }\n\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/common/pipe/impl/http/archive/ArchiveException.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.pipe.impl.http.archive;\n\nimport org.apache.commons.lang.exception.NestableRuntimeException;\n\n/**\n * ArchiveException\n */\npublic class ArchiveException extends NestableRuntimeException {\n\n    private static final long serialVersionUID = -7288830284122672209L;\n\n    private String            errorCode;\n    private String            errorDesc;\n\n    public ArchiveException(String errorCode){\n        super(errorCode);\n    }\n\n    public ArchiveException(String errorCode, Throwable cause){\n        super(errorCode, cause);\n    }\n\n    public ArchiveException(String errorCode, String errorDesc){\n        super(errorCode + \":\" + errorDesc);\n    }\n\n    public ArchiveException(String errorCode, String errorDesc, Throwable cause){\n        super(errorCode + \":\" + errorDesc, cause);\n    }\n\n    public ArchiveException(Throwable cause){\n        super(cause);\n    }\n\n    public String getErrorCode() {\n        return errorCode;\n    }\n\n    public String getErrorDesc() {\n        return errorDesc;\n    }\n\n    @Override\n    public Throwable fillInStackTrace() {\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/common/pipe/impl/http/archive/ArchiveRetriverCallback.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.pipe.impl.http.archive;\n\nimport java.io.InputStream;\n\n/**\n * archive数据提取的callback接口\n * \n * @author jianghang 2011-10-11 下午04:49:05\n * @version 4.0.0\n */\npublic interface ArchiveRetriverCallback<SOURCE> {\n\n    /**\n     * 根据source，打开对应的输入流\n     */\n    public InputStream retrive(SOURCE source);\n\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/common/pipe/impl/http/archive/LazyFileInputStream.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.pipe.impl.http.archive;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileNotFoundException;\nimport java.io.IOException;\nimport java.io.InputStream;\n\n/**\n * 基于lazy实现的InputSteam\n * \n * @author jianghang 2013-3-6 下午02:24:30\n * @version 4.1.7\n */\npublic class LazyFileInputStream extends InputStream {\n\n    private InputStream delegate;\n    private File        file;\n\n    public LazyFileInputStream(File file){\n        this.file = file;\n    }\n\n    public InputStream getInputSteam() throws FileNotFoundException {\n        delegate = new FileInputStream(file);\n        return delegate;\n    }\n\n    public void close() throws IOException {\n        if (delegate != null) {\n            delegate.close();\n        }\n    }\n\n    public int read() throws IOException {\n        return 0;\n    }\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/common/pipe/impl/memory/AbstractMemoryPipe.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.pipe.impl.memory;\n\nimport java.util.Map;\nimport java.util.concurrent.TimeUnit;\n\nimport org.springframework.beans.factory.InitializingBean;\n\nimport com.alibaba.otter.node.etl.common.pipe.Pipe;\nimport com.alibaba.otter.shared.etl.model.DbBatch;\nimport com.google.common.collect.OtterMigrateMap;\n\n/**\n * 基于内存版本的pipe实现\n * \n * @author jianghang 2011-10-13 下午05:36:33\n * @version 4.0.0\n */\npublic abstract class AbstractMemoryPipe<T, KEY extends MemoryPipeKey> implements Pipe<T, KEY>, InitializingBean {\n\n    protected Long                        timeout = 60 * 1000L; // 对应的超时时间,1分钟\n\n    protected Map<MemoryPipeKey, DbBatch> cache;\n\n    public void afterPropertiesSet() throws Exception {\n        // 一定要设置过期时间，因为针对rollback操作，不会有后续的节点来获取数据，需要自动过期删除掉\n        cache = OtterMigrateMap.makeSoftValueMapWithTimeout(timeout, TimeUnit.MILLISECONDS);\n    }\n\n    // ============== setter / getter ===============\n\n    public void setTimeout(Long timeout) {\n        this.timeout = timeout;\n    }\n\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/common/pipe/impl/memory/MemoryPipeKey.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.pipe.impl.memory;\n\nimport com.alibaba.otter.node.etl.common.pipe.PipeKey;\nimport com.alibaba.otter.shared.etl.model.Identity;\n\n/**\n * 基于内存的pipekey实现\n * \n * @author jianghang 2011-10-13 下午05:32:04\n * @version 4.0.0\n */\npublic class MemoryPipeKey extends PipeKey {\n\n    private static final long serialVersionUID = -7478539581294846644L;\n\n    private Identity          identity;\n    private Long              time;\n\n    public MemoryPipeKey(){\n        this.time = System.currentTimeMillis();// 随机生成一个值\n    }\n\n    public Identity getIdentity() {\n        return identity;\n    }\n\n    public void setIdentity(Identity identity) {\n        this.identity = identity;\n    }\n\n    public Long getTime() {\n        return time;\n    }\n\n    public void setTime(Long time) {\n        this.time = time;\n    }\n\n    @Override\n    public int hashCode() {\n        final int prime = 31;\n        int result = 1;\n        result = prime * result + ((identity == null) ? 0 : identity.hashCode());\n        result = prime * result + ((time == null) ? 0 : time.hashCode());\n        return result;\n    }\n\n    @Override\n    public boolean equals(Object obj) {\n        if (this == obj) {\n            return true;\n        }\n        if (obj == null) {\n            return false;\n        }\n        if (getClass() != obj.getClass()) {\n            return false;\n        }\n        MemoryPipeKey other = (MemoryPipeKey) obj;\n        if (identity == null) {\n            if (other.identity != null) {\n                return false;\n            }\n        } else if (!identity.equals(other.identity)) {\n            return false;\n        }\n        if (time == null) {\n            if (other.time != null) {\n                return false;\n            }\n        } else if (!time.equals(other.time)) {\n            return false;\n        }\n        return true;\n    }\n\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/common/pipe/impl/memory/RowDataMemoryPipe.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.pipe.impl.memory;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileNotFoundException;\nimport java.io.FileOutputStream;\nimport java.io.InputStream;\nimport java.text.MessageFormat;\nimport java.text.SimpleDateFormat;\nimport java.util.Date;\nimport java.util.List;\n\nimport org.apache.commons.io.FilenameUtils;\nimport org.apache.commons.io.IOUtils;\nimport org.apache.commons.lang.ClassUtils;\nimport org.apache.commons.lang.StringUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.util.Assert;\n\nimport com.alibaba.otter.node.etl.common.pipe.PipeDataType;\nimport com.alibaba.otter.node.etl.common.pipe.exception.PipeException;\nimport com.alibaba.otter.node.etl.load.loader.db.FileloadDumper;\nimport com.alibaba.otter.shared.common.utils.NioUtils;\nimport com.alibaba.otter.shared.etl.model.DbBatch;\nimport com.alibaba.otter.shared.etl.model.FileBatch;\nimport com.alibaba.otter.shared.etl.model.FileData;\nimport com.alibaba.otter.shared.etl.model.Identity;\n\npublic class RowDataMemoryPipe extends AbstractMemoryPipe<DbBatch, MemoryPipeKey> {\n\n    private static final Logger logger      = LoggerFactory.getLogger(RowDataMemoryPipe.class);\n    private static final String DATE_FORMAT = \"yyyy-MM-dd-HH-mm-ss\";\n    private int                 retry       = 3;\n    private String              downloadDir;\n\n    public MemoryPipeKey put(DbBatch data) {\n        MemoryPipeKey key = new MemoryPipeKey();\n        key.setIdentity(data.getRowBatch().getIdentity());\n        // if (data.getRoot() == null && data.getFileBatch() != null\n        // && !CollectionUtils.isEmpty(data.getFileBatch().getFiles())) {\n        // logger.warn(\"Identity[{}] memory pipe exist fileBatch!\",\n        // key.getIdentity());\n        // // data.setRoot(prepareFile(data.getFileBatch()));\n        // }\n        key.setDataType(PipeDataType.DB_BATCH);\n        cache.put(key, data);\n        return key;\n    }\n\n    public DbBatch get(MemoryPipeKey key) {\n        return cache.remove(key);\n    }\n\n    // 处理对应的附件\n    @SuppressWarnings(\"unused\")\n    private File prepareFile(FileBatch fileBatch) {\n        // 处理构造对应的文件url\n        String dirname = buildFileName(fileBatch.getIdentity(), ClassUtils.getShortClassName(fileBatch.getClass()));\n        File dir = new File(downloadDir, dirname);\n        NioUtils.create(dir, false, 3);// 创建父目录\n        // 压缩对应的文件数据\n        List<FileData> fileDatas = fileBatch.getFiles();\n\n        for (FileData fileData : fileDatas) {\n            String namespace = fileData.getNameSpace();\n            String path = fileData.getPath();\n            boolean isLocal = StringUtils.isBlank(namespace);\n            String entryName = null;\n            if (true == isLocal) {\n                entryName = FilenameUtils.getPath(path) + FilenameUtils.getName(path);\n            } else {\n                entryName = namespace + File.separator + path;\n            }\n\n            InputStream input = retrive(fileBatch.getIdentity(), fileData);\n            if (input == null) {\n                continue;\n            }\n            File entry = new File(dir, entryName);\n            NioUtils.create(entry.getParentFile(), false, retry);// 尝试创建父路径\n            FileOutputStream output = null;\n            try {\n                output = new FileOutputStream(entry);\n                NioUtils.copy(input, output);// 输出到压缩流中\n            } catch (Exception e) {\n                throw new PipeException(\"prepareFile error for file[\" + entry.getPath() + \"]\");\n            } finally {\n                IOUtils.closeQuietly(output);\n            }\n        }\n\n        return dir;\n    }\n\n    private InputStream retrive(Identity identity, FileData fileData) {\n        boolean miss = false;\n        try {\n            if (StringUtils.isNotEmpty(fileData.getNameSpace())) {\n                throw new RuntimeException(fileData + \" is not support!\");\n            } else {\n                try {\n                    File source = new File(fileData.getPath());\n                    if (source.exists() && source.isFile()) {\n                        return new FileInputStream(source);\n                    } else {\n                        miss = true;\n                        return null;\n                    }\n                } catch (FileNotFoundException ex) {\n                    miss = true;\n                    return null;\n                }\n            }\n        } finally {\n            if (miss) {\n                logger.error(FileloadDumper.dumpMissFileDatas(identity, fileData));\n            }\n        }\n    }\n\n    // 构造文件名\n    private String buildFileName(Identity identity, String prefix) {\n        Date now = new Date();\n        String time = new SimpleDateFormat(DATE_FORMAT).format(now);\n        return MessageFormat.format(\"{0}-{1}-{2}-{3}-{4}\", prefix, time, String.valueOf(identity.getChannelId()),\n                                    String.valueOf(identity.getPipelineId()), String.valueOf(identity.getProcessId()));\n    }\n\n    public void afterPropertiesSet() throws Exception {\n        super.afterPropertiesSet();\n        Assert.notNull(downloadDir);\n        NioUtils.create(new File(downloadDir), false, 3);\n    }\n\n    public void setDownloadDir(String downloadDir) {\n        this.downloadDir = downloadDir;\n    }\n\n    public void setRetry(int retry) {\n        this.retry = retry;\n    }\n\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/common/pipe/impl/rpc/AbstractRpcPipe.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.pipe.impl.rpc;\n\nimport java.util.Map;\nimport java.util.concurrent.TimeUnit;\n\nimport org.springframework.beans.factory.InitializingBean;\n\nimport com.alibaba.otter.node.etl.common.pipe.Pipe;\nimport com.alibaba.otter.shared.communication.core.model.Event;\nimport com.alibaba.otter.shared.communication.core.model.EventType;\nimport com.alibaba.otter.shared.etl.model.DbBatch;\nimport com.google.common.collect.OtterMigrateMap;\n\n/**\n * 基于rpc通讯的数据传递\n * \n * <pre>\n * PUT：基于内存cache的临时存储\n * GET: 基于远程rpc请求的调用获取\n * </pre>\n * \n * @author jianghang 2011-10-17 下午01:29:49\n * @version 4.0.0\n */\npublic abstract class AbstractRpcPipe<T, KEY extends RpcPipeKey> implements Pipe<T, KEY>, InitializingBean {\n\n    protected Long                     timeout = 60 * 1000L; // 对应的超时时间,1分钟\n\n    protected Map<RpcPipeKey, DbBatch> cache;\n\n    public void afterPropertiesSet() throws Exception {\n        cache = OtterMigrateMap.makeSoftValueMapWithTimeout(timeout, TimeUnit.MILLISECONDS);\n    }\n\n    // rpc get操作事件\n    public static class RpcEvent extends Event {\n\n        private static final long serialVersionUID = 810191575813164952L;\n\n        public RpcEvent(EventType eventType){\n            super(eventType);\n        }\n\n        public RpcPipeKey key;\n\n        public RpcPipeKey getKey() {\n            return key;\n        }\n\n        public void setKey(RpcPipeKey key) {\n            this.key = key;\n        }\n\n    }\n\n    // ============== setter / getter ===============\n\n    public void setTimeout(Long timeout) {\n        this.timeout = timeout;\n    }\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/common/pipe/impl/rpc/RowDataRpcPipe.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.pipe.impl.rpc;\n\nimport com.alibaba.otter.node.common.communication.NodeCommmunicationClient;\nimport com.alibaba.otter.node.common.config.ConfigClientService;\nimport com.alibaba.otter.node.etl.common.pipe.PipeDataType;\nimport com.alibaba.otter.node.etl.common.pipe.exception.PipeException;\nimport com.alibaba.otter.shared.communication.core.CommunicationRegistry;\nimport com.alibaba.otter.shared.communication.core.model.EventType;\nimport com.alibaba.otter.shared.etl.model.DbBatch;\n\n/**\n * 基于rpc调用实现rowData的数据传递\n * \n * @author jianghang 2011-10-18 下午02:56:47\n * @version 4.0.0\n */\npublic class RowDataRpcPipe extends AbstractRpcPipe<DbBatch, RpcPipeKey> {\n\n    private ConfigClientService      configClientService;\n    private NodeCommmunicationClient nodeCommmunicationClient;\n\n    // 基于rowData rpc的eventType\n    public static enum RowDataRpc implements EventType {\n        get\n    }\n\n    public RowDataRpcPipe(){\n        // 注册一下事件处理\n        CommunicationRegistry.regist(RowDataRpc.get, this);\n    }\n\n    public RpcPipeKey put(DbBatch data) throws PipeException {\n        RpcPipeKey key = new RpcPipeKey();\n        key.setIdentity(data.getRowBatch().getIdentity());\n        key.setNid(getNid());\n        key.setDataType(PipeDataType.DB_BATCH);\n        cache.put(key, data);\n        return key;\n    }\n\n    public DbBatch get(RpcPipeKey key) throws PipeException {\n        RpcEvent event = new RpcEvent(RowDataRpc.get);\n        event.setKey(key);\n        return (DbBatch) nodeCommmunicationClient.call(key.getNid(), event);\n    }\n\n    @SuppressWarnings(\"unused\")\n    // 处理rpc调用事件\n    private DbBatch onGet(RpcEvent event) {\n        return cache.remove(event.getKey()); // 不建议使用remove，rpc调用容易有retry请求，导致第二次拿到的数据为null\n    }\n\n    private Long getNid() {\n        return configClientService.currentNode().getId();\n    }\n\n    // ==================== setter / getter =====================\n\n    public void setConfigClientService(ConfigClientService configClientService) {\n        this.configClientService = configClientService;\n    }\n\n    public void setNodeCommmunicationClient(NodeCommmunicationClient nodeCommmunicationClient) {\n        this.nodeCommmunicationClient = nodeCommmunicationClient;\n    }\n\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/common/pipe/impl/rpc/RpcPipeKey.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.pipe.impl.rpc;\n\nimport com.alibaba.otter.node.etl.common.pipe.PipeKey;\nimport com.alibaba.otter.shared.etl.model.Identity;\n\n/**\n * 基于rpc调用的pipe key\n * \n * @author jianghang 2011-10-17 下午01:26:06\n * @version 4.0.0\n */\npublic class RpcPipeKey extends PipeKey {\n\n    private static final long serialVersionUID = -9092948280957762259L;\n    private Long              nid;                                     // 目标机器的唯一标示id\n    private Identity          identity;\n    private Long              time;\n\n    public RpcPipeKey(){\n        this.time = System.currentTimeMillis();// 随机生成一个值\n    }\n\n    public Identity getIdentity() {\n        return identity;\n    }\n\n    public void setIdentity(Identity identity) {\n        this.identity = identity;\n    }\n\n    public Long getNid() {\n        return nid;\n    }\n\n    public void setNid(Long nid) {\n        this.nid = nid;\n    }\n\n    public Long getTime() {\n        return time;\n    }\n\n    public void setTime(Long time) {\n        this.time = time;\n    }\n\n    public int hashCode() {\n        final int prime = 31;\n        int result = 1;\n        result = prime * result + ((identity == null) ? 0 : identity.hashCode());\n        result = prime * result + ((time == null) ? 0 : time.hashCode());\n        return result;\n    }\n\n    public boolean equals(Object obj) {\n        if (this == obj) {\n            return true;\n        }\n        if (obj == null) {\n            return false;\n        }\n        if (getClass() != obj.getClass()) {\n            return false;\n        }\n        RpcPipeKey other = (RpcPipeKey) obj;\n        if (identity == null) {\n            if (other.identity != null) {\n                return false;\n            }\n        } else if (!identity.equals(other.identity)) {\n            return false;\n        }\n        if (time == null) {\n            if (other.time != null) {\n                return false;\n            }\n        } else if (!time.equals(other.time)) {\n            return false;\n        }\n        return true;\n    }\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/common/task/GlobalTask.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.task;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Future;\n\nimport org.I0Itec.zkclient.exception.ZkInterruptedException;\nimport org.apache.commons.lang.ClassUtils;\nimport org.apache.commons.lang.exception.ExceptionUtils;\nimport org.apache.commons.lang.math.RandomUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.alibaba.otter.node.common.config.ConfigClientService;\nimport com.alibaba.otter.node.etl.common.jmx.StageAggregationCollector;\nimport com.alibaba.otter.node.etl.common.pipe.impl.RowDataPipeDelegate;\nimport com.alibaba.otter.shared.arbitrate.ArbitrateEventService;\nimport com.alibaba.otter.shared.arbitrate.model.TerminEventData;\nimport com.alibaba.otter.shared.arbitrate.model.TerminEventData.TerminType;\nimport com.alibaba.otter.shared.common.model.config.pipeline.Pipeline;\n\n/**\n * mainstem,select,extract,transform,load parent Thread.\n * \n * @author xiaoqing.zhouxq 2011-8-23 上午10:38:14\n */\npublic abstract class GlobalTask extends Thread {\n\n    protected final Logger              logger  = LoggerFactory.getLogger(this.getClass());\n    protected volatile boolean          running = true;\n    protected Pipeline                  pipeline;\n    protected Long                      pipelineId;\n    protected ArbitrateEventService     arbitrateEventService;\n    protected RowDataPipeDelegate       rowDataPipeDelegate;\n    protected ExecutorService           executorService;\n    protected ConfigClientService       configClientService;\n    protected StageAggregationCollector stageAggregationCollector;\n    protected Map<Long, Future>         pendingFuture;\n\n    public GlobalTask(Pipeline pipeline){\n        this(pipeline.getId());\n        this.pipeline = pipeline;\n    }\n\n    public GlobalTask(Long pipelineId){\n        this.pipelineId = pipelineId;\n        setName(createTaskName(pipelineId, ClassUtils.getShortClassName(this.getClass())));\n        pendingFuture = new HashMap<Long, Future>();\n    }\n\n    public void shutdown() {\n        running = false;\n        interrupt();\n\n        List<Future> cancelFutures = new ArrayList<Future>();\n        for (Map.Entry<Long, Future> entry : pendingFuture.entrySet()) {\n            if (!entry.getValue().isDone()) {\n                logger.warn(\"WARN ## Task future processId[{}] canceled!\", entry.getKey());\n                cancelFutures.add(entry.getValue());\n            }\n        }\n\n        for (Future future : cancelFutures) {\n            future.cancel(true);\n        }\n        pendingFuture.clear();\n    }\n\n    protected void sendRollbackTermin(long pipelineId, Throwable exception) {\n        sendRollbackTermin(pipelineId, ExceptionUtils.getFullStackTrace(exception));\n    }\n\n    protected void sendRollbackTermin(long pipelineId, String message) {\n        TerminEventData errorEventData = new TerminEventData();\n        errorEventData.setPipelineId(pipelineId);\n        errorEventData.setType(TerminType.ROLLBACK);\n        errorEventData.setCode(\"setl\");\n        errorEventData.setDesc(message);\n        arbitrateEventService.terminEvent().single(errorEventData);\n        // 每次发送完报警后，sleep一段时间，继续做后面的事\n        try {\n            Thread.sleep(3000 + RandomUtils.nextInt(3000));\n        } catch (InterruptedException e) {\n        }\n    }\n\n    /**\n     * 自动处理数据为null的情况，重新发一遍数据\n     */\n    protected void processMissData(long pipelineId, String message) {\n        TerminEventData errorEventData = new TerminEventData();\n        errorEventData.setPipelineId(pipelineId);\n        errorEventData.setType(TerminType.RESTART);\n        errorEventData.setCode(\"setl\");\n        errorEventData.setDesc(message);\n        arbitrateEventService.terminEvent().single(errorEventData);\n    }\n\n    protected String createTaskName(long pipelineId, String taskName) {\n        return new StringBuilder().append(\"pipelineId = \").append(pipelineId).append(\",taskName = \").append(taskName).toString();\n    }\n\n    protected boolean isProfiling() {\n        return stageAggregationCollector.isProfiling();\n    }\n\n    protected boolean isInterrupt(Throwable e) {\n        if (!running) {\n            return true;\n        }\n\n        if (e instanceof InterruptedException || e instanceof ZkInterruptedException) {\n            return true;\n        }\n\n        if (ExceptionUtils.getRootCause(e) instanceof InterruptedException) {\n            return true;\n        }\n\n        return false;\n\n    }\n\n    public Collection<Long> getPendingProcess() {\n        List<Long> result = new ArrayList<Long>(pendingFuture.keySet());\n        Collections.sort(result);\n        return result;\n    }\n\n    // ====================== setter / getter =========================\n\n    public void setArbitrateEventService(ArbitrateEventService arbitrateEventService) {\n        this.arbitrateEventService = arbitrateEventService;\n    }\n\n    public void setRowDataPipeDelegate(RowDataPipeDelegate rowDataPipeDelegate) {\n        this.rowDataPipeDelegate = rowDataPipeDelegate;\n    }\n\n    public void setExecutorService(ExecutorService executorService) {\n        this.executorService = executorService;\n    }\n\n    public void setConfigClientService(ConfigClientService configClientService) {\n        this.configClientService = configClientService;\n    }\n\n    public void setStageAggregationCollector(StageAggregationCollector stageAggregationCollector) {\n        this.stageAggregationCollector = stageAggregationCollector;\n    }\n\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/conflict/FileBatchConflictDetectService.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.conflict;\n\nimport com.alibaba.otter.shared.etl.model.FileBatch;\n\n/**\n * 文件冲突检测service\n * \n * @author jianghang 2011-11-10 上午09:34:44\n * @version 4.0.0\n */\npublic interface FileBatchConflictDetectService {\n\n    /**\n     * 和本地的file进行冲突检测，过滤冲突记录，返回无冲突的记录\n     * \n     * <pre>\n     * <strong>注意：在extract之前调用该方法</strong>\n     * </pre>\n     */\n    public FileBatch detect(FileBatch fileBatch, Long targetNodeId);\n\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/conflict/exception/ConflictException.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.conflict.exception;\n\nimport org.apache.commons.lang.exception.NestableRuntimeException;\n\n/**\n * @author jianghang 2012-4-12 下午02:59:12\n * @version 4.0.2\n */\npublic class ConflictException extends NestableRuntimeException {\n\n    private static final long serialVersionUID = -7288830284122672209L;\n\n    private String            errorCode;\n    private String            errorDesc;\n\n    public ConflictException(String errorCode){\n        super(errorCode);\n    }\n\n    public ConflictException(String errorCode, Throwable cause){\n        super(errorCode, cause);\n    }\n\n    public ConflictException(String errorCode, String errorDesc){\n        super(errorCode + \":\" + errorDesc);\n    }\n\n    public ConflictException(String errorCode, String errorDesc, Throwable cause){\n        super(errorCode + \":\" + errorDesc, cause);\n    }\n\n    public ConflictException(Throwable cause){\n        super(cause);\n    }\n\n    public String getErrorCode() {\n        return errorCode;\n    }\n\n    public String getErrorDesc() {\n        return errorDesc;\n    }\n\n    @Override\n    public Throwable fillInStackTrace() {\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/conflict/impl/FileBatchConflictDetectServiceImpl.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.conflict.impl;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.apache.commons.lang.StringUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.slf4j.MDC;\nimport org.springframework.util.CollectionUtils;\n\nimport com.alibaba.otter.node.common.communication.NodeCommmunicationClient;\nimport com.alibaba.otter.node.common.config.ConfigClientService;\nimport com.alibaba.otter.node.etl.OtterConstants;\nimport com.alibaba.otter.node.etl.conflict.FileBatchConflictDetectService;\nimport com.alibaba.otter.node.etl.conflict.model.ConflictEventType;\nimport com.alibaba.otter.node.etl.conflict.model.FileConflictDetectEvent;\nimport com.alibaba.otter.node.etl.load.loader.db.FileloadDumper;\nimport com.alibaba.otter.shared.common.model.config.pipeline.Pipeline;\nimport com.alibaba.otter.shared.common.utils.thread.ExecutorTemplate;\nimport com.alibaba.otter.shared.common.utils.thread.ExecutorTemplateGetter;\nimport com.alibaba.otter.shared.communication.core.CommunicationRegistry;\nimport com.alibaba.otter.shared.etl.model.EventType;\nimport com.alibaba.otter.shared.etl.model.FileBatch;\nimport com.alibaba.otter.shared.etl.model.FileData;\n\n/**\n * 提供文件的冲突检测服务\n * \n * @author jianghang 2011-11-10 上午09:41:20\n * @version 4.0.0\n */\npublic class FileBatchConflictDetectServiceImpl implements FileBatchConflictDetectService {\n\n    private static final Logger      logger = LoggerFactory.getLogger(FileBatchConflictDetectServiceImpl.class);\n    private int                      retry  = 3;\n    private ConfigClientService      configClientService;\n    private NodeCommmunicationClient nodeCommmunicationClient;\n    private ExecutorTemplateGetter   executorTemplateGetter;\n\n    public FileBatchConflictDetectServiceImpl(){\n        // 将自己注册为远程事件处理\n        CommunicationRegistry.regist(ConflictEventType.fileConflictDetect, this);\n    }\n\n    public FileBatch detect(FileBatch fileBatch, Long targetNodeId) {\n        FileConflictDetectEvent event = new FileConflictDetectEvent();\n        event.setFileBatch(fileBatch);\n        if (isLocal(targetNodeId)) {\n            return onFileConflictDetect(event);\n        } else {\n            // 调用远程\n            return (FileBatch) nodeCommmunicationClient.call(targetNodeId, event);\n        }\n    }\n\n    /**\n     * 具体冲突检测的行为\n     */\n    private FileBatch onFileConflictDetect(FileConflictDetectEvent event) {\n        final FileBatch fileBatch = event.getFileBatch();\n        if (CollectionUtils.isEmpty(fileBatch.getFiles())) {\n            return fileBatch;\n        }\n\n        ExecutorTemplate executorTemplate = executorTemplateGetter.get();\n        try {\n            MDC.put(OtterConstants.splitPipelineLoadLogFileKey, String.valueOf(fileBatch.getIdentity().getPipelineId()));\n            executorTemplate.start();\n            // 重新设置下poolSize\n            Pipeline pipeline = configClientService.findPipeline(fileBatch.getIdentity().getPipelineId());\n            executorTemplate.adjustPoolSize(pipeline.getParameters().getFileLoadPoolSize());\n            // 启动\n            final List<FileData> result = Collections.synchronizedList(new ArrayList<FileData>());\n            final List<FileData> filter = Collections.synchronizedList(new ArrayList<FileData>());\n            for (final FileData source : fileBatch.getFiles()) {\n                EventType type = source.getEventType();\n                if (type.isDelete()) {\n                    result.add(source);\n                } else {\n                    executorTemplate.submit(new Runnable() {\n\n                        public void run() {\n                            MDC.put(OtterConstants.splitPipelineLoadLogFileKey,\n                                    String.valueOf(fileBatch.getIdentity().getPipelineId()));\n                            // 处理更新类型\n                            String namespace = source.getNameSpace();\n                            String path = source.getPath();\n                            FileData target = null;\n\n                            int count = 0;\n                            while (count++ < retry) {// 进行重试处理\n                                try {\n                                    if (true == StringUtils.isBlank(namespace)) {\n                                        // local file\n                                        java.io.File targetFile = new java.io.File(path);\n                                        if (true == targetFile.exists()) {\n                                            // modified time cost\n                                            long lastModified = targetFile.lastModified();\n                                            long size = targetFile.length();\n                                            // 更新数据\n                                            target = new FileData();\n                                            target.setLastModifiedTime(lastModified);\n                                            target.setSize(size);\n                                        }\n                                    } else {\n                                        // remote file\n                                        throw new RuntimeException(source + \" is not support!\");\n                                    }\n\n                                    break; // 不出异常就跳出\n                                } catch (Exception ex) {\n                                    target = null;\n                                }\n                            }\n\n                            boolean shouldSync = false;\n                            if (target != null) {\n                                if (true == accept(target, source)) {\n                                    shouldSync = true;\n                                }\n                            } else {\n                                shouldSync = true;\n                            }\n\n                            if (true == shouldSync) {\n                                result.add(source);\n                            } else {\n                                filter.add(source);\n                            }\n                        }\n                    });\n                }\n            }\n            // 等待所有都处理完成\n            executorTemplate.waitForResult();\n\n            if (pipeline.getParameters().getDumpEvent() && logger.isInfoEnabled()) {\n                logger.info(FileloadDumper.dumpFilterFileDatas(fileBatch.getIdentity(), fileBatch.getFiles().size(),\n                                                               result.size(), filter));\n            }\n\n            // 构造返回结果\n            FileBatch target = new FileBatch();\n            target.setIdentity(fileBatch.getIdentity());\n            target.setFiles(result);\n            return target;\n        } finally {\n            if (executorTemplate != null) {\n                executorTemplateGetter.release(executorTemplate);\n            }\n\n            MDC.remove(OtterConstants.splitPipelineLoadLogFileKey);\n        }\n    }\n\n    /**\n     * <pre>\n     * 判断规则：\n     * 1. 源文件的最后修改时间比目标文件的最后修改时间新\n     * 2. 源文件和目标文件大小不一致\n     * </pre>\n     */\n    private boolean accept(FileData target, FileData source) {\n        return (target.getLastModifiedTime() < source.getLastModifiedTime()) || (target.getSize() != source.getSize());\n    }\n\n    private boolean isLocal(Long targetNodeId) {\n        return configClientService.currentNode().getId().equals(targetNodeId);\n    }\n\n    // ===================== setter / getter ======================\n\n    public void setConfigClientService(ConfigClientService configClientService) {\n        this.configClientService = configClientService;\n    }\n\n    public void setNodeCommmunicationClient(NodeCommmunicationClient nodeCommmunicationClient) {\n        this.nodeCommmunicationClient = nodeCommmunicationClient;\n    }\n\n    public void setRetry(int retry) {\n        this.retry = retry;\n    }\n\n    public void setExecutorTemplateGetter(ExecutorTemplateGetter executorTemplateGetter) {\n        this.executorTemplateGetter = executorTemplateGetter;\n    }\n\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/conflict/model/ConflictEventType.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.conflict.model;\n\nimport com.alibaba.otter.shared.communication.core.model.EventType;\n\n/**\n * config交互的事件类型\n * \n * @author jianghang\n */\npublic enum ConflictEventType implements EventType {\n\n    /** 文件冲突检测 */\n    fileConflictDetect;\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/conflict/model/FileConflictDetectEvent.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.conflict.model;\n\nimport com.alibaba.otter.shared.communication.core.model.Event;\nimport com.alibaba.otter.shared.etl.model.FileBatch;\n\n/**\n * 文件冲突检测事件\n * \n * @author jianghang\n */\npublic class FileConflictDetectEvent extends Event {\n\n    private static final long serialVersionUID = 476657754177940448L;\n\n    private FileBatch         fileBatch;\n\n    public FileConflictDetectEvent(){\n        super(ConflictEventType.fileConflictDetect);\n    }\n\n    public FileBatch getFileBatch() {\n        return fileBatch;\n    }\n\n    public void setFileBatch(FileBatch fileBatch) {\n        this.fileBatch = fileBatch;\n    }\n\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/extract/ExtractTask.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.extract;\n\nimport java.util.List;\n\nimport org.slf4j.MDC;\nimport org.springframework.util.CollectionUtils;\n\nimport com.alibaba.otter.node.etl.OtterConstants;\nimport com.alibaba.otter.node.etl.common.jmx.StageAggregation.AggregationItem;\nimport com.alibaba.otter.node.etl.common.pipe.PipeKey;\nimport com.alibaba.otter.node.etl.common.task.GlobalTask;\nimport com.alibaba.otter.node.etl.conflict.FileBatchConflictDetectService;\nimport com.alibaba.otter.node.etl.extract.extractor.OtterExtractorFactory;\nimport com.alibaba.otter.shared.arbitrate.model.EtlEventData;\nimport com.alibaba.otter.shared.common.model.config.enums.StageType;\nimport com.alibaba.otter.shared.etl.model.DbBatch;\nimport com.alibaba.otter.shared.etl.model.FileBatch;\n\n/**\n * extract工作线程,负责桥接连接仲裁器\n * \n * @author xiaoqing.zhouxq\n */\npublic class ExtractTask extends GlobalTask {\n\n    private OtterExtractorFactory          otterExtractorFactory;\n    private FileBatchConflictDetectService fileBatchConflictDetectService;\n\n    public ExtractTask(Long pipelineId){\n        super(pipelineId);\n    }\n\n    public void run() {\n        MDC.put(OtterConstants.splitPipelineLogFileKey, String.valueOf(pipelineId));\n        while (running) {\n            try {\n                final EtlEventData etlEventData = arbitrateEventService.extractEvent().await(pipelineId);\n                Runnable task = new Runnable() {\n\n                    public void run() {\n                        // 设置profiling信息\n                        boolean profiling = isProfiling();\n                        Long profilingStartTime = null;\n                        if (profiling) {\n                            profilingStartTime = System.currentTimeMillis();\n                        }\n\n                        MDC.put(OtterConstants.splitPipelineLogFileKey, String.valueOf(pipelineId));\n                        String currentName = Thread.currentThread().getName();\n                        Thread.currentThread().setName(createTaskName(pipelineId, \"ExtractWorker\"));\n                        try {\n                            pipeline = configClientService.findPipeline(pipelineId);\n                            List<PipeKey> keys = (List<PipeKey>) etlEventData.getDesc();\n                            long nextNodeId = etlEventData.getNextNid();\n                            DbBatch dbBatch = rowDataPipeDelegate.get(keys);\n\n                            // 可能拿到为null，因为内存不足或者网络异常，长时间阻塞时，导致从pipe拿数据出现异常，数据可能被上一个节点已经删除\n                            if (dbBatch == null) {\n                                processMissData(pipelineId, \"extract miss data with keys:\" + keys.toString());\n                                return;\n                            }\n\n                            otterExtractorFactory.extract(dbBatch);// 重新装配一下数据\n                            if (dbBatch.getFileBatch() != null\n                                && !CollectionUtils.isEmpty(dbBatch.getFileBatch().getFiles())\n                                && pipeline.getParameters().getFileDetect()) { // 判断一下是否有文件同步，并且需要进行文件对比\n                                // 对比一下中美图片是否有变化\n                                FileBatch fileBatch = fileBatchConflictDetectService.detect(dbBatch.getFileBatch(),\n                                                                                            nextNodeId);\n                                dbBatch.setFileBatch(fileBatch);\n                            }\n\n                            List<PipeKey> pipeKeys = rowDataPipeDelegate.put(dbBatch, nextNodeId);\n                            etlEventData.setDesc(pipeKeys);\n\n                            if (profiling) {\n                                Long profilingEndTime = System.currentTimeMillis();\n                                stageAggregationCollector.push(pipelineId,\n                                                               StageType.EXTRACT,\n                                                               new AggregationItem(profilingStartTime, profilingEndTime));\n                            }\n                            arbitrateEventService.extractEvent().single(etlEventData);\n                        } catch (Throwable e) {\n                            if (!isInterrupt(e)) {\n                                logger.error(String.format(\"[%d] extractwork executor is error! data:%s\", pipelineId,\n                                                           etlEventData), e);\n                                sendRollbackTermin(pipelineId, e);\n                            } else {\n                                logger.info(String.format(\"[%d] extractwork executor is interrrupt! data:%s\",\n                                                          pipelineId, etlEventData), e);\n                            }\n                        } finally {\n                            Thread.currentThread().setName(currentName);\n                            MDC.remove(OtterConstants.splitPipelineLogFileKey);\n                        }\n                    }\n                };\n\n                // 构造pending任务，可在关闭线程时退出任务\n                SetlFuture extractFuture = new SetlFuture(StageType.EXTRACT, etlEventData.getProcessId(),\n                                                          pendingFuture, task);\n                executorService.execute(extractFuture);\n            } catch (Throwable e) {\n                if (isInterrupt(e)) {\n                    logger.info(String.format(\"[%s] extractTask is interrupted!\", pipelineId), e);\n                    return;\n                } else {\n                    logger.error(String.format(\"[%s] extractTask is error!\", pipelineId), e);\n                    sendRollbackTermin(pipelineId, e);\n                }\n            }\n        }\n    }\n\n    // =================== setter / getter ======================\n\n    public void setOtterExtractorFactory(OtterExtractorFactory otterExtractorFactory) {\n        this.otterExtractorFactory = otterExtractorFactory;\n    }\n\n    public void setFileBatchConflictDetectService(FileBatchConflictDetectService fileBatchConflictDetectService) {\n        this.fileBatchConflictDetectService = fileBatchConflictDetectService;\n    }\n\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/extract/SetlFuture.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.extract;\n\nimport java.util.Map;\nimport java.util.concurrent.Future;\nimport java.util.concurrent.FutureTask;\n\nimport com.alibaba.otter.shared.common.model.config.enums.StageType;\n\n/**\n * @author simon 2012-12-3 下午5:15:15\n * @version 4.1.0\n */\npublic class SetlFuture<V> extends FutureTask<V> {\n\n    private StageType         stageType;\n    private Long              processId;\n    private Map<Long, Future> pendingFuture;\n\n    public SetlFuture(StageType stageType, Long processId, Map<Long, Future> pendingFuture, Runnable runnable){\n        super(runnable, null);\n        this.stageType = stageType;\n        this.pendingFuture = pendingFuture;\n        this.pendingFuture.put(processId, this);\n        this.processId = processId;\n    }\n\n    protected void done() {\n        pendingFuture.remove(processId); // 完成了，将自己从pendingFuture移除\n    }\n\n    public String toString() {\n        return \"SetlFuture [processId=\" + processId + \", stageType=\" + stageType + \"]\";\n    }\n\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/extract/exceptions/ExtractException.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.extract.exceptions;\n\nimport org.apache.commons.lang.exception.NestableRuntimeException;\n\n/**\n * ExtractException for extract module.\n * \n * @author xiaoqing.zhouxq\n */\npublic class ExtractException extends NestableRuntimeException {\n\n    private static final long serialVersionUID = 2680820522662343759L;\n    private String            errorCode;\n    private String            errorDesc;\n\n    public ExtractException(String errorCode){\n        super(errorCode);\n    }\n\n    public ExtractException(String errorCode, Throwable cause){\n        super(errorCode, cause);\n    }\n\n    public ExtractException(String errorCode, String errorDesc){\n        super(errorCode + \":\" + errorDesc);\n    }\n\n    public ExtractException(String errorCode, String errorDesc, Throwable cause){\n        super(errorCode + \":\" + errorDesc, cause);\n    }\n\n    public ExtractException(Throwable cause){\n        super(cause);\n    }\n\n    public String getErrorCode() {\n        return errorCode;\n    }\n\n    public String getErrorDesc() {\n        return errorDesc;\n    }\n\n    @Override\n    public Throwable fillInStackTrace() {\n        return this;\n    }\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/extract/extractor/AbstractExtractor.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.extract.extractor;\n\nimport com.alibaba.otter.node.common.config.ConfigClientService;\nimport com.alibaba.otter.node.etl.common.db.dialect.DbDialect;\nimport com.alibaba.otter.node.etl.common.db.dialect.DbDialectFactory;\nimport com.alibaba.otter.shared.common.model.config.ConfigHelper;\nimport com.alibaba.otter.shared.common.model.config.data.DataMedia;\nimport com.alibaba.otter.shared.common.model.config.data.db.DbMediaSource;\nimport com.alibaba.otter.shared.common.model.config.pipeline.Pipeline;\n\n/**\n * 单条记录处理的extractor\n * \n * @author jianghang 2012-4-18 下午04:12:39\n * @version 4.0.2\n */\npublic abstract class AbstractExtractor<P> implements OtterExtractor<P> {\n\n    protected ConfigClientService configClientService;\n    protected DbDialectFactory    dbDialectFactory;\n\n    protected DbDialect getDbDialect(Long pipelineId, Long tableId) {\n        DataMedia dataMedia = ConfigHelper.findDataMedia(getPipeline(pipelineId), tableId);\n        return dbDialectFactory.getDbDialect(pipelineId, (DbMediaSource) dataMedia.getSource());\n    }\n\n    protected Pipeline getPipeline(Long pipelineId) {\n        return configClientService.findPipeline(pipelineId);\n    }\n\n    // ==================== setter / getter =====================\n\n    public void setConfigClientService(ConfigClientService configClientService) {\n        this.configClientService = configClientService;\n    }\n\n    public void setDbDialectFactory(DbDialectFactory dbDialectFactory) {\n        this.dbDialectFactory = dbDialectFactory;\n    }\n\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/extract/extractor/DatabaseExtractor.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.extract.extractor;\n\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.text.MessageFormat;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.concurrent.ArrayBlockingQueue;\nimport java.util.concurrent.ExecutionException;\nimport java.util.concurrent.ExecutorCompletionService;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Future;\nimport java.util.concurrent.ThreadPoolExecutor;\nimport java.util.concurrent.TimeUnit;\n\nimport org.apache.commons.lang.StringUtils;\nimport org.apache.commons.lang.SystemUtils;\nimport org.apache.ddlutils.model.Column;\nimport org.apache.ddlutils.model.Table;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.slf4j.MDC;\nimport org.springframework.beans.factory.DisposableBean;\nimport org.springframework.beans.factory.InitializingBean;\nimport org.springframework.jdbc.core.RowMapper;\nimport org.springframework.util.Assert;\nimport org.springframework.util.CollectionUtils;\n\nimport com.alibaba.otter.node.etl.OtterConstants;\nimport com.alibaba.otter.node.etl.common.db.dialect.DbDialect;\nimport com.alibaba.otter.node.etl.common.db.dialect.oracle.OracleDialect;\nimport com.alibaba.otter.node.etl.common.db.utils.SqlUtils;\nimport com.alibaba.otter.node.etl.extract.exceptions.ExtractException;\nimport com.alibaba.otter.shared.common.model.config.ConfigHelper;\nimport com.alibaba.otter.shared.common.model.config.data.ColumnPair;\nimport com.alibaba.otter.shared.common.model.config.data.DataMedia;\nimport com.alibaba.otter.shared.common.model.config.data.DataMediaPair;\nimport com.alibaba.otter.shared.common.model.config.data.db.DbMediaSource;\nimport com.alibaba.otter.shared.common.model.config.pipeline.Pipeline;\nimport com.alibaba.otter.shared.common.utils.thread.NamedThreadFactory;\nimport com.alibaba.otter.shared.etl.model.DbBatch;\nimport com.alibaba.otter.shared.etl.model.EventColumn;\nimport com.alibaba.otter.shared.etl.model.EventColumnIndexComparable;\nimport com.alibaba.otter.shared.etl.model.EventData;\n\n/**\n * 基于数据库的反查 , 使用多线程技术进行加速处理 {@linkplain DatabaseExtractWorker}\n * \n * <pre>\n * 说明：\n *  1. 数据反查的总时间 = ( 数据记录数 / (poolsize + 1) ) * 每条记录查询的时间\n *  2. 当其中的一次并行查询出现异常，会立即中断之前的并行查询请求，同时忽略后续的查询直接退出(在出错时快速响应)\n *  3. 编写{@linkplain DatabaseExtractWorker}代码时需注意，在适合的地方响应Thread.currentThread().isInterrupted(),在dbcp连接池和driver代码中是有支持\n *  4. 反查数据库，只会反查update=true的字段，按需反查，因为通过反查之后字段都会变为update=true，不必要的字段会进行数据同步 (modify by ljh at 2012-11-04)\n * </pre>\n * \n * @author jianghang 2012-4-18 下午04:53:15\n * @version 4.0.2\n */\npublic class DatabaseExtractor extends AbstractExtractor<DbBatch> implements InitializingBean, DisposableBean {\n\n    private static final String WORKER_NAME        = \"DataBaseExtractor\";\n    private static final String WORKER_NAME_FORMAT = \"pipelineId = %s , pipelineName = %s , \" + WORKER_NAME;\n    private static final Logger logger             = LoggerFactory.getLogger(DatabaseExtractor.class);\n    private static final int    DEFAULT_POOL_SIZE  = 5;\n    private static final int    retryTimes         = 3;\n    private int                 poolSize           = DEFAULT_POOL_SIZE;\n    private ExecutorService     executor;\n\n    @Override\n    public void extract(DbBatch dbBatch) throws ExtractException {\n        Assert.notNull(dbBatch);\n        Assert.notNull(dbBatch.getRowBatch());\n        // 读取配置\n        Pipeline pipeline = getPipeline(dbBatch.getRowBatch().getIdentity().getPipelineId());\n        boolean mustDb = pipeline.getParameters().getSyncConsistency().isMedia();\n        boolean isRow = pipeline.getParameters().getSyncMode().isRow();// 如果是行记录是必须进行数据库反查\n        // 读取一次配置\n        adjustPoolSize(pipeline.getParameters().getExtractPoolSize()); // 调整下线程池，Extractor会被池化处理\n        ExecutorCompletionService completionService = new ExecutorCompletionService(executor);\n\n        // 进行并发提交\n        ExtractException exception = null;\n        // 每个表进行处理\n        List<DataItem> items = new ArrayList<DataItem>();\n        List<Future> futures = new ArrayList<Future>();\n        List<EventData> eventDatas = dbBatch.getRowBatch().getDatas();\n        for (EventData eventData : eventDatas) {\n            if (eventData.getEventType().isDdl()) {\n                continue;\n            }\n\n            DataItem item = new DataItem(eventData);\n            // 针对row模式，需要去检查一下当前是否已经包含row记录的所有字段，如果发现字段不足，则执行一次数据库查询\n            boolean flag = mustDb\n                           || (eventData.getSyncConsistency() != null && eventData.getSyncConsistency().isMedia());\n\n            // 增加一种case, 针对oracle erosa有时侯结果记录只有主键，没有变更字段，需要做一次反查\n            if (!flag && CollectionUtils.isEmpty(eventData.getUpdatedColumns())) {\n                DataMedia dataMedia = ConfigHelper.findDataMedia(pipeline, eventData.getTableId());\n                if (dataMedia.getSource().getType().isOracle()) {\n                    flag |= true;\n                    eventData.setRemedy(true);// 针对这类数据，也统一视为补救的操作，可能erosa解析时反查数据库也不存在记录\n                }\n            }\n\n            if (isRow && !flag) {\n                // 提前判断一次，避免进入多线程进行竞争\n                // 针对view视图的情况，会有后续再判断一次\n                flag = checkNeedDbForRowMode(pipeline, eventData);\n            }\n\n            if (flag && (eventData.getEventType().isInsert() || eventData.getEventType().isUpdate())) {// 判断是否需要反查\n                Future future = completionService.submit(new DatabaseExtractWorker(pipeline, item), null); // 提交进行并行查询\n                if (future.isDone()) {\n                    // 立即判断一次，因为使用了CallerRun可能当场跑出结果，针对有异常时快速响应，而不是等跑完所有的才抛异常\n                    try {\n                        future.get();\n                    } catch (InterruptedException e) {\n                        cancel(futures);// 取消完之后立马退出\n                        throw new ExtractException(e);\n                    } catch (ExecutionException e) {\n                        cancel(futures); // 取消完之后立马退出\n                        throw new ExtractException(e);\n                    }\n                }\n\n                futures.add(future);// 记录一下添加的任务\n            }\n\n            items.add(item);// 按顺序添加\n        }\n\n        // 开始处理结果\n        int index = 0;\n        while (index < futures.size()) { // 循环处理发出去的所有任务\n            try {\n                Future future = completionService.take();// 它也可能被打断\n                future.get();\n            } catch (InterruptedException e) {\n                exception = new ExtractException(e);\n                break;// 如何一个future出现了异常，就退出\n            } catch (ExecutionException e) {\n                exception = new ExtractException(e);\n                break;// 如何一个future出现了异常，就退出\n            }\n\n            index++;\n        }\n\n        if (index < futures.size()) {\n            // 小于代表有错误，需要对未完成的记录进行cancel操作，对已完成的结果进行收集，做重复录入过滤记录\n            cancel(futures);\n            throw exception;\n        } else {\n            // 全部成功分支, 构造返回结果也要保证原始的顺序\n            for (int i = 0; i < items.size(); i++) {\n                DataItem item = items.get(i);\n                if (item.filter) { // 忽略需要被过滤的数据，比如数据库反查时记录已经不存在\n                    eventDatas.remove(item.getEventData());\n                }\n            }\n        }\n\n    }\n\n    private boolean checkNeedDbForRowMode(Pipeline pipeline, EventData eventData) {\n        // 获取数据表信息\n        DataMedia dataMedia = ConfigHelper.findDataMedia(pipeline, eventData.getTableId());\n        DbDialect dbDialect = dbDialectFactory.getDbDialect(pipeline.getId(), (DbMediaSource) dataMedia.getSource());\n        Table table = dbDialect.findTable(eventData.getSchemaName(), eventData.getTableName());\n        if (table.getColumnCount() == eventData.getColumns().size() + eventData.getKeys().size()) {\n            return false;\n        } else {\n            return true;\n        }\n    }\n\n    // 取消一下当前正在执行的异步任务\n    private void cancel(List<Future> futures) {\n        for (int i = 0; i < futures.size(); i++) {\n            Future future = futures.get(i);\n            if (future.isDone() == false) {\n                future.cancel(true);// 中断之前的操作\n            }\n        }\n    }\n\n    // 调整一下线程池\n    private void adjustPoolSize(int newPoolSize) {\n        if (newPoolSize != poolSize) {\n            poolSize = newPoolSize;\n            if (executor instanceof ThreadPoolExecutor) {\n                ThreadPoolExecutor pool = (ThreadPoolExecutor) executor;\n                pool.setCorePoolSize(newPoolSize);\n                pool.setMaximumPoolSize(newPoolSize);\n            }\n        }\n    }\n\n    public void afterPropertiesSet() throws Exception {\n        executor = new ThreadPoolExecutor(poolSize,\n            poolSize,\n            0L,\n            TimeUnit.MILLISECONDS,\n            new ArrayBlockingQueue(poolSize * 4),\n            new NamedThreadFactory(WORKER_NAME),\n            new ThreadPoolExecutor.CallerRunsPolicy());\n    }\n\n    public void destroy() throws Exception {\n        executor.shutdownNow();\n    }\n\n    // 异步处理的结构体\n    class DataItem {\n\n        private EventData eventData;\n        private boolean   filter = false;\n\n        public DataItem(EventData eventData){\n            this.eventData = eventData;\n        }\n\n        public EventData getEventData() {\n            return eventData;\n        }\n\n        public void setEventData(EventData eventData) {\n            this.eventData = eventData;\n        }\n\n        public boolean isFilter() {\n            return filter;\n        }\n\n        public void setFilter(boolean filter) {\n            this.filter = filter;\n        }\n\n    }\n\n    /**\n     * 反查数据异步处理单元，反查速度由主方法进行控制\n     * \n     * @author jianghang 2012-4-19 下午05:14:18\n     * @version 4.0.2\n     */\n    class DatabaseExtractWorker implements Runnable {\n\n        private final int    event_default_capacity = 1024;                      // 预设值StringBuilder，减少扩容影响\n        private String       eventData_format       = null;\n        private final String SEP                    = SystemUtils.LINE_SEPARATOR;\n\n        private Pipeline     pipeline;\n        private DataItem     item;\n        private EventData    eventData;\n        {\n            eventData_format = \"-----------------\" + SEP;\n            eventData_format += \"- PairId: {0} , TableId: {1} \" + SEP;\n            eventData_format += \"-----------------\" + SEP;\n            eventData_format += \"---START\" + SEP;\n            eventData_format += \"---Pks\" + SEP;\n            eventData_format += \"{2}\" + SEP;\n            eventData_format += \"---Sql\" + SEP;\n            eventData_format += \"{3}\" + SEP;\n            eventData_format += \"---END\" + SEP;\n        }\n\n        public DatabaseExtractWorker(Pipeline pipeline, DataItem item){\n            this.pipeline = pipeline;\n            this.item = item;\n            this.eventData = item.getEventData();\n        }\n\n        public void run() {\n            try {\n                MDC.put(OtterConstants.splitPipelineLogFileKey, String.valueOf(pipeline.getId()));\n                Thread.currentThread().setName(String.format(WORKER_NAME_FORMAT, pipeline.getId(), pipeline.getName()));\n                // 获取数据表信息\n                DataMedia dataMedia = ConfigHelper.findDataMedia(pipeline, eventData.getTableId());\n                DbDialect dbDialect = dbDialectFactory.getDbDialect(pipeline.getId(),\n                    (DbMediaSource) dataMedia.getSource());\n                Table table = dbDialect.findTable(eventData.getSchemaName(), eventData.getTableName());\n                TableData keyTableData = buildTableData(table, eventData.getKeys());\n\n                // oracle类型特殊处理下\n                if (dbDialect instanceof OracleDialect) {\n                    keyTableData.columnTypes = getOraclePkTypes(table, keyTableData.columnNames);\n                }\n\n                boolean needAll = pipeline.getParameters().getSyncMode().isRow()\n                                  || (eventData.getSyncMode() != null && eventData.getSyncMode().isRow());\n\n                // 增加一种case, 针对oracle erosa有时侯结果记录只有主键，没有变更字段，需要做一次反查，获取所有字段\n                needAll |= CollectionUtils.isEmpty(eventData.getUpdatedColumns())\n                           && dataMedia.getSource().getType().isOracle();\n\n                List<DataMediaPair> mediaParis = ConfigHelper.findDataMediaPairByMediaId(pipeline, dataMedia.getId());\n                List<String> viewColumnNames = buildMaxColumnsFromColumnPairs(mediaParis, eventData.getKeys());\n\n                // TODO 后续版本测试下\n                // if (needAll) {\n                // boolean needDb = checkNeedDbForRowMode(table,\n                // viewColumnNames, eventData);\n                // if (needAll && !needDb) {// 不需要进行反查\n                // item.setFilter(false);\n                // return;\n                // }\n                // }\n\n                // modified by ljh at 2012-11-04\n                // 反查数据时只反查带update=true标识的数据，因为update=false的记录可能只是进行filter需要用到的数据，不需要反查\n                TableData columnTableData = buildTableData(table,\n                    eventData.getUpdatedColumns(),\n                    needAll,\n                    viewColumnNames);\n\n                if (columnTableData.columnNames.length == 0) {\n                    // 全主键，不需要进行反查\n                } else {\n                    List<String> newColumnValues = select(dbDialect,\n                        eventData.getSchemaName(),\n                        eventData.getTableName(),\n                        keyTableData,\n                        columnTableData);\n\n                    if (newColumnValues == null) {\n                        // miss from db\n                        // 设置为filter=true，可能存在丢数据的风险.\n                        // 比如针对源库发生主备切换，otter反查的是备库，查询不到对应的记录\n                        // item.setFilter(true);\n\n                        // 针对需要自定义反查数据库的，允许忽略\n                        // a. 自由门触发的数据，不存在时可以忽略\n                        // b. 回环补救算法触发的数据，不存在时可以忽略\n                        boolean needFilter = eventData.isRemedy() || pipeline.getParameters().getSkipNoRow();\n                        item.setFilter(needFilter);\n\n                        // 判断主键是否有变更，如果变更了，就原样返回item\n                        int index = 0;\n                        for (EventColumn oldKey : eventData.getOldKeys()) {\n                            if (!oldKey.equals(eventData.getKeys().get(index))) {\n                                item.setFilter(false);\n                                break;\n                            }\n                        }\n                    } else {\n                        // 构造反查的返回结果\n                        List<EventColumn> newEventColumns = new ArrayList<EventColumn>();\n                        for (int i = 0; i < newColumnValues.size(); i++) {\n                            EventColumn column = new EventColumn();\n                            column.setIndex(columnTableData.indexs[i]);\n                            column.setColumnName(columnTableData.columnNames[i]);\n                            column.setColumnType(columnTableData.columnTypes[i]);\n                            column.setNull(newColumnValues.get(i) == null);\n                            column.setColumnValue(newColumnValues.get(i));\n                            column.setUpdate(true);\n                            newEventColumns.add(column);\n                        }\n\n                        // 处理下columns中不在反查字段内的字段列表\n                        for (EventColumn column : eventData.getColumns()) {\n                            boolean override = false;\n                            for (EventColumn newEventColumn : newEventColumns) {\n                                if (StringUtils.equalsIgnoreCase(newEventColumn.getColumnName(), column.getColumnName())) {\n                                    override = true;\n                                    break;\n                                }\n                            }\n\n                            if (!override) {// 针对newcolumns不存在的记录进行添加\n                                newEventColumns.add(column);\n                            }\n                        }\n\n                        Collections.sort(newEventColumns, new EventColumnIndexComparable()); // 重新排个序\n                        eventData.setColumns(newEventColumns);\n                    }\n                }\n            } catch (InterruptedException e) {\n                // ignore\n            } finally {\n                Thread.currentThread().setName(WORKER_NAME);\n                MDC.remove(OtterConstants.splitPipelineLogFileKey);\n            }\n        }\n\n        /**\n         * 根据视图同步定义的columnPair，获取需要反查的字段列表，不包括主键\n         */\n        private List<String> buildMaxColumnsFromColumnPairs(List<DataMediaPair> mediaPairs, List<EventColumn> pks) {\n            Set<String> allColumns = new HashSet<String>();\n            Map<String, EventColumn> pkMap = new HashMap<String, EventColumn>(pks.size(), 1f);\n            for (EventColumn pk : pks) {\n                pkMap.put(StringUtils.lowerCase(pk.getColumnName()), pk);\n            }\n\n            for (DataMediaPair mediaPair : mediaPairs) {// 一个源库可以对应多个目标，多路复制\n                List<ColumnPair> columnPairs = mediaPair.getColumnPairs();\n\n                if (CollectionUtils.isEmpty(columnPairs) || mediaPair.getColumnPairMode().isExclude()) {\n                    // 1. 如果有一个没有视图定义，说明需要所有字段\n                    // 2. 如果有一个表存在exclude模式，简单处理，直接反查所有字段，到后面进行过滤\n                    return new ArrayList<String>(); // 返回空集合，代表没有view\n                                                    // filter，需要所有字段\n                } else {\n                    for (ColumnPair columnPair : columnPairs) {\n                        String columnName = columnPair.getSourceColumn().getName();\n                        if (!pkMap.containsKey(StringUtils.lowerCase(columnName))) {\n                            allColumns.add(columnPair.getSourceColumn().getName());// 加入的为非主键\n                        }\n                    }\n                }\n            }\n\n            return new ArrayList<String>(allColumns);\n        }\n\n        private List<String> select(DbDialect dbDialect, String schemaName, String tableName, TableData keyTableData,\n                                    TableData columnTableData) throws InterruptedException {\n            String selectSql = dbDialect.getSqlTemplate().getSelectSql(schemaName,\n                tableName,\n                keyTableData.columnNames,\n                columnTableData.columnNames);\n            Exception exception = null;\n            for (int i = 0; i < retryTimes; i++) {\n                if (Thread.currentThread().isInterrupted()) {\n                    throw new InterruptedException(); // 退出\n                }\n\n                try {\n                    List<List<String>> result = dbDialect.getJdbcTemplate().query(selectSql,\n                        keyTableData.columnValues,\n                        keyTableData.columnTypes,\n                        new RowDataMapper(columnTableData.columnTypes));\n                    if (CollectionUtils.isEmpty(result)) {\n                        logger.warn(\"the mediaName = {}.{} not has rowdate in db \\n {}\", new Object[] { schemaName,\n                                tableName, dumpEventData(eventData, selectSql) });\n                        return null;\n                    } else {\n                        return result.get(0);\n                    }\n\n                } catch (Exception e) {\n                    exception = e;\n                    logger.warn(\"retry [\" + (i + 1) + \"] failed\", e);\n                }\n            }\n\n            throw new RuntimeException(\"db extract failed , data:\\n \" + dumpEventData(eventData, selectSql), exception);\n        }\n\n        /**\n         * oracle的erosa获取的字段类型，没有转换成jdbc的类型，所以需要手工转一下.\n         */\n        private int[] getOraclePkTypes(Table table, String[] pkNames) {\n            Column[] columns = table.getColumns();\n            List<Integer> pkTypes = new ArrayList<Integer>();\n            for (String pkName : pkNames) {\n                for (Column column : columns) {\n                    if (column.getName().equalsIgnoreCase(pkName)) {\n                        pkTypes.add(column.getTypeCode());\n                    }\n                }\n            }\n            int[] types = new int[pkTypes.size()];\n            for (int i = 0; i < types.length; i++) {\n                types[i] = pkTypes.get(i);\n            }\n            return types;\n        }\n\n        @SuppressWarnings(\"unused\")\n        private boolean checkNeedDbForRowMode(Table table, List<String> viewColumns, EventData eventData) {\n            if (viewColumns.size() != 0) {// 说明有视图\n                if (viewColumns.size() != eventData.getColumns().size()) {\n                    return true;\n                }\n\n                // 检查一下当前是否所有字段都在view字段列表里\n                for (EventColumn column : eventData.getColumns()) {\n                    if (!viewColumns.contains(column.getColumnName())) {\n                        return true;\n                    }\n                }\n\n                return false;\n            } else {\n                if (table.getColumnCount() == eventData.getColumns().size() + eventData.getKeys().size()) {\n                    return false;\n                } else {\n                    return true;\n                }\n            }\n        }\n\n        /**\n         * 构建数据库主键字段的信息\n         */\n        private TableData buildTableData(Table table, List<EventColumn> keys) {\n            Column[] tableColumns = table.getColumns();\n\n            TableData data = new TableData();\n            data.indexs = new int[keys.size()];\n            data.columnNames = new String[keys.size()];\n            data.columnTypes = new int[keys.size()];\n            data.columnValues = new Object[keys.size()];\n\n            int i = 0;\n            int index = 0;\n            for (EventColumn keyColumn : keys) {\n                for (Column tableColumn : tableColumns) {\n                    if (StringUtils.equalsIgnoreCase(keyColumn.getColumnName(), tableColumn.getName())) {\n                        data.indexs[i] = index;\n                        data.columnNames[i] = tableColumn.getName();\n                        data.columnTypes[i] = tableColumn.getTypeCode();\n                        data.columnValues[i] = SqlUtils.stringToSqlValue(keyColumn.getColumnValue(),\n                            tableColumn.getTypeCode(),\n                            tableColumn.isRequired(),\n                            false);\n\n                        i++;\n                        break;\n                    }\n                    index++;\n                }\n            }\n\n            if (i != keys.size()) {\n                throw new ExtractException(\"keys is not found in table \" + table.toString() + \" keys : \"\n                                           + dumpEventColumn(keys));\n            }\n            return data;\n        }\n\n        /**\n         * 构建数据库非主键字段的信息\n         */\n        private TableData buildTableData(Table table, List<EventColumn> columns, boolean needAll,\n                                         List<String> viewColumnNames) {\n            Column[] tableColumns = table.getColumns();\n            List<Column> noPkcolumns = new ArrayList<Column>();\n            for (Column tableColumn : tableColumns) {\n                if (!tableColumn.isPrimaryKey()) {\n                    noPkcolumns.add(tableColumn);\n                }\n            }\n\n            TableData data = new TableData();\n            int size = columns.size();\n            if (needAll) {\n                size = viewColumnNames.size() != 0 ? viewColumnNames.size() : noPkcolumns.size();// 如果view不为空就使用view作为反查字段\n            }\n\n            data.indexs = new int[size];\n            data.columnNames = new String[size];\n            data.columnTypes = new int[size];\n            data.columnValues = new Object[size];\n\n            int i = 0;\n            if (needAll) {\n                int index = 0;\n                if (viewColumnNames.size() != 0) { // 存在视图定义\n                    for (Column tableColumn : tableColumns) {\n                        if (viewColumnNames.contains(tableColumn.getName())) {// 只放入在view中定义的\n                            data.indexs[i] = index;// 计算下下标\n                            data.columnNames[i] = tableColumn.getName();\n                            data.columnTypes[i] = tableColumn.getTypeCode();\n                            i++;\n                        }\n\n                        index++;\n                    }\n                } else {\n                    for (Column tableColumn : tableColumns) {\n                        if (!tableColumn.isPrimaryKey()) {\n                            data.indexs[i] = index;// 计算下下标\n                            data.columnNames[i] = tableColumn.getName();\n                            data.columnTypes[i] = tableColumn.getTypeCode();\n                            i++;\n                        }\n                        index++;\n                    }\n                }\n            } else {\n                for (EventColumn column : columns) {\n                    int index = 0;\n                    for (Column tableColumn : tableColumns) {\n                        if (StringUtils.equalsIgnoreCase(column.getColumnName(), tableColumn.getName())) {\n                            data.indexs[i] = index;// 计算下下标\n                            data.columnNames[i] = tableColumn.getName();\n                            data.columnTypes[i] = tableColumn.getTypeCode();\n\n                            i++;\n                            break;\n                        }\n                        index++;\n                    }\n                }\n\n                if (i != columns.size()) {\n                    throw new ExtractException(\"columns is not found in table \" + table.toString() + \" columns : \"\n                                               + dumpEventColumn(columns));\n                }\n            }\n\n            return data;\n        }\n\n        private String dumpEventData(EventData eventData, String selectSql) {\n            return MessageFormat.format(eventData_format,\n                eventData.getPairId(),\n                eventData.getTableId(),\n                dumpEventColumn(eventData.getKeys()),\n                \"\\t\" + selectSql);\n        }\n\n        private String dumpEventColumn(List<EventColumn> columns) {\n            StringBuilder builder = new StringBuilder(event_default_capacity);\n            int size = columns.size();\n            for (int i = 0; i < size; i++) {\n                EventColumn column = columns.get(i);\n                builder.append(\"\\t\").append(column.toString());\n                if (i < columns.size() - 1) {\n                    builder.append(SEP);\n                }\n            }\n            return builder.toString();\n        }\n\n    }\n\n    /**\n     * 数据库处理对象\n     */\n    class TableData {\n\n        int[]    indexs;\n        String[] columnNames;\n        int[]    columnTypes;\n        Object[] columnValues;\n    }\n\n    /**\n     * 数据库反查的结果处理\n     */\n    class RowDataMapper implements RowMapper {\n\n        private int[] columnTypes;\n\n        public RowDataMapper(int[] columnTypes){\n            this.columnTypes = columnTypes;\n        }\n\n        public Object mapRow(ResultSet rs, int rowNum) throws SQLException {\n            List<String> result = new ArrayList<String>();\n            for (int i = 0; i < columnTypes.length; i++) {\n                try {\n                    String value = SqlUtils.sqlValueToString(rs, i + 1, columnTypes[i]);\n                    result.add(value);\n                } catch (Exception e) {\n                    throw new ExtractException(\"ERROR ## get columnName has an error\", e);\n                }\n            }\n            return result;\n        }\n    }\n\n    // ============================ setter / getter =========================\n\n    public void setPoolSize(int poolSize) {\n        this.poolSize = poolSize;\n    }\n\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/extract/extractor/FileExtractor.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.extract.extractor;\n\nimport java.io.File;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.apache.commons.lang.StringUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.alibaba.otter.node.etl.extract.exceptions.ExtractException;\nimport com.alibaba.otter.shared.common.model.config.ConfigHelper;\nimport com.alibaba.otter.shared.common.model.config.data.DataMediaPair;\nimport com.alibaba.otter.shared.common.model.config.pipeline.Pipeline;\nimport com.alibaba.otter.shared.common.utils.extension.ExtensionFactory;\nimport com.alibaba.otter.shared.common.utils.thread.ExecutorTemplate;\nimport com.alibaba.otter.shared.common.utils.thread.ExecutorTemplateGetter;\nimport com.alibaba.otter.shared.etl.extend.fileresolver.FileInfo;\nimport com.alibaba.otter.shared.etl.extend.fileresolver.FileResolver;\nimport com.alibaba.otter.shared.etl.extend.fileresolver.support.RemoteDirectoryFetcher;\nimport com.alibaba.otter.shared.etl.extend.fileresolver.support.RemoteDirectoryFetcherAware;\nimport com.alibaba.otter.shared.etl.model.DbBatch;\nimport com.alibaba.otter.shared.etl.model.EventColumn;\nimport com.alibaba.otter.shared.etl.model.EventData;\nimport com.alibaba.otter.shared.etl.model.EventType;\nimport com.alibaba.otter.shared.etl.model.FileBatch;\nimport com.alibaba.otter.shared.etl.model.FileData;\nimport com.alibaba.otter.shared.etl.model.Identity;\nimport com.alibaba.otter.shared.etl.model.RowBatch;\n\n/**\n * 基于rowBatch数据，返回对应的关联文件\n * \n * @author jianghang 2012-4-18 下午04:52:00\n * @version 4.0.2\n */\npublic class FileExtractor extends AbstractExtractor<DbBatch> {\n\n    private static final Logger    logger = LoggerFactory.getLogger(ExecutorTemplate.class);\n    private ExtensionFactory       extensionFactory;\n\n    private RemoteDirectoryFetcher arandaRemoteDirectoryFetcher;\n    private int                    retry  = 3;\n    private ExecutorTemplateGetter executorTemplateGetter;\n\n    public void extract(DbBatch dbBatch) throws ExtractException {\n        List<FileData> fileDatas = doFileExtract(dbBatch.getRowBatch());\n        FileBatch fileBatch = new FileBatch();\n        fileBatch.setFiles(fileDatas);\n        Identity identity = new Identity();\n        identity.setChannelId(dbBatch.getRowBatch().getIdentity().getChannelId());\n        identity.setPipelineId(dbBatch.getRowBatch().getIdentity().getPipelineId());\n        identity.setProcessId(dbBatch.getRowBatch().getIdentity().getProcessId());\n        fileBatch.setIdentity(identity);\n        dbBatch.setFileBatch(fileBatch);\n    }\n\n    /**\n     * 返回这批变更数据对应的FileInfo.\n     * \n     * @param rowBatch\n     * @return\n     */\n    private List<FileData> doFileExtract(RowBatch rowBatch) {\n        List<FileData> fileDatas = new ArrayList<FileData>();\n        // 处理数据\n        Pipeline pipeline = getPipeline(rowBatch.getIdentity().getPipelineId());\n        List<EventData> eventDatas = rowBatch.getDatas();\n        for (EventData eventData : eventDatas) {\n            if (eventData.getEventType().isDdl()) {\n                continue;\n            }\n\n            List<DataMediaPair> dataMediaPairs = ConfigHelper.findDataMediaPairByMediaId(pipeline,\n                                                                                         eventData.getTableId());\n            if (dataMediaPairs == null) {\n                throw new ExtractException(\"ERROR ## the dataMediaId = \" + eventData.getTableId()\n                                           + \" dataMediaPair is null,please check\");\n            }\n\n            for (DataMediaPair dataMediaPair : dataMediaPairs) {\n                if (dataMediaPair.getResolverData() == null\n                    || dataMediaPair.getResolverData().getExtensionDataType() == null\n                    || (dataMediaPair.getResolverData().getExtensionDataType().isClazz() && StringUtils.isBlank(dataMediaPair.getResolverData().getClazzPath()))\n                    || (dataMediaPair.getResolverData().getExtensionDataType().isSource() && StringUtils.isBlank(dataMediaPair.getResolverData().getSourceText()))) {\n                    continue;\n                }\n\n                FileResolver fileResolver = null;\n\n                if (dataMediaPair.getResolverData() != null) {\n                    fileResolver = extensionFactory.getExtension(FileResolver.class, dataMediaPair.getResolverData());\n                } else {\n                    continue;\n                }\n\n                if (fileResolver == null) {\n                    throw new ExtractException(\"ERROR ## the dataMediaId = \" + eventData.getTableId()\n                                               + \" the fileResolver className  = \"\n                                               + dataMediaPair.getResolverData().getClazzPath()\n                                               + \" is null ,please check the class\");\n                }\n\n                if (fileResolver instanceof RemoteDirectoryFetcherAware) {\n                    RemoteDirectoryFetcherAware remoteDirectoryFetcherAware = (RemoteDirectoryFetcherAware) fileResolver;\n                    remoteDirectoryFetcherAware.setRemoteDirectoryFetcher(arandaRemoteDirectoryFetcher);\n                }\n\n                List<FileData> singleRowFileDatas = getSingleRowFileInfos(dataMediaPair.getId(), fileResolver,\n                                                                          eventData);\n                // 做一下去重处理\n                for (FileData data : singleRowFileDatas) {\n                    if (!fileDatas.contains(data)) {\n                        fileDatas.add(data);\n                    }\n                }\n            }\n        }\n\n        // 判断是否需要进行图片重复同步检查\n        if (pipeline.getParameters().getFileDetect()) {\n            doFileDetectCollector(pipeline, fileDatas);\n        }\n        return fileDatas;\n    }\n\n    private List<FileData> getSingleRowFileInfos(long pairId, FileResolver fileResolver, EventData eventData) {\n        if (eventData.getEventType() == EventType.DELETE && fileResolver.isDeleteRequired() == false) {\n            return new ArrayList<FileData>();\n        }\n\n        Map<String, String> rowMap = new HashMap<String, String>();\n\n        List<EventColumn> keyColumns = eventData.getKeys();\n        List<EventColumn> eventColumns = eventData.getUpdatedColumns();\n        for (EventColumn eventColumn : keyColumns) {\n            rowMap.put(eventColumn.getColumnName().toUpperCase(), eventColumn.getColumnValue());\n        }\n        for (EventColumn eventColumn : eventColumns) {\n            rowMap.put(eventColumn.getColumnName().toUpperCase(), eventColumn.getColumnValue());\n        }\n        FileInfo[] fileInfos = fileResolver.getFileInfo(rowMap);\n        if (fileInfos == null || fileInfos.length == 0) {\n            return new ArrayList<FileData>();\n        } else {\n            List<FileData> fileDatas = new ArrayList<FileData>();\n            for (FileInfo fileInfo : fileInfos) {\n                FileData fileData = new FileData();\n                fileData.setPairId(pairId); // 记录一下具体映射规则的id\n                fileData.setTableId(eventData.getTableId());\n                fileData.setEventType(eventData.getEventType());\n                fileData.setLastModifiedTime(fileInfo.getLastModifiedTime());\n                fileData.setNameSpace(fileInfo.getNamespace());\n                fileData.setPath(fileInfo.getPath());\n                fileData.setSize(fileInfo.getSize());\n                fileDatas.add(fileData);\n            }\n            return fileDatas;\n        }\n    }\n\n    private void doFileDetectCollector(Pipeline pipeline, List<FileData> fileDatas) {\n        ExecutorTemplate executorTemplate = executorTemplateGetter.get();\n        try {\n            executorTemplate.start();\n            // 重新设置下poolSize\n            executorTemplate.adjustPoolSize(pipeline.getParameters().getFileLoadPoolSize());\n            for (final FileData fileData : fileDatas) {\n                // 提交进行多线程处理\n                executorTemplate.submit(new Runnable() {\n\n                    public void run() {\n                        boolean isAranda = StringUtils.isNotEmpty(fileData.getNameSpace());\n                        int count = 0;\n                        Throwable exception = null;\n                        while (count++ < retry) {\n                            try {\n                                if (isAranda) {\n                                    // remote file\n                                    throw new RuntimeException(fileData + \" is not support!\");\n                                } else {\n                                    // 处理本地文件\n                                    File file = new File(fileData.getPath());\n                                    fileData.setLastModifiedTime(file.lastModified());\n                                    fileData.setSize(file.length());\n                                }\n\n                                return;// 没有异常就退出\n                            } catch (Exception e) {\n                                fileData.setLastModifiedTime(Long.MIN_VALUE);\n                                fileData.setSize(Long.MIN_VALUE);\n                                exception = e;\n                            }\n                        }\n\n                        if (count >= retry) {\n                            logger.warn(String.format(\"FileDetectCollector is error! collect failed[%s]\",\n                                                      fileData.getNameSpace() + \"/\" + fileData.getPath()), exception);\n                        }\n                    }\n                });\n            }\n\n            long start = System.currentTimeMillis();\n            logger.info(\"start pipelinep[{}] waitFor FileData Size : {} \", pipeline.getId(), fileDatas.size());\n            // 等待所有都处理完成\n            executorTemplate.waitForResult();\n            logger.info(\"end pipelinep[{}] waitFor FileData cost : {} ms \", pipeline.getId(),\n                        (System.currentTimeMillis() - start));\n        } finally {\n            if (executorTemplate != null) {\n                executorTemplateGetter.release(executorTemplate);\n            }\n        }\n    }\n\n    // ==================== setter / getter =====================\n\n    public void setExtensionFactory(ExtensionFactory extensionFactory) {\n        this.extensionFactory = extensionFactory;\n    }\n\n    public void setRetry(int retry) {\n        this.retry = retry;\n    }\n\n    public void setArandaRemoteDirectoryFetcher(RemoteDirectoryFetcher arandaRemoteDirectoryFetcher) {\n        this.arandaRemoteDirectoryFetcher = arandaRemoteDirectoryFetcher;\n    }\n\n    public void setExecutorTemplateGetter(ExecutorTemplateGetter executorTemplateGetter) {\n        this.executorTemplateGetter = executorTemplateGetter;\n    }\n\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/extract/extractor/FreedomExtractor.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.extract.extractor;\n\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\nimport org.apache.commons.lang.StringUtils;\nimport org.apache.ddlutils.model.Column;\nimport org.apache.ddlutils.model.Table;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.util.Assert;\nimport org.springframework.util.CollectionUtils;\n\nimport com.alibaba.otter.node.etl.common.db.dialect.DbDialect;\nimport com.alibaba.otter.node.etl.extract.exceptions.ExtractException;\nimport com.alibaba.otter.shared.common.model.config.ConfigException;\nimport com.alibaba.otter.shared.common.model.config.ConfigHelper;\nimport com.alibaba.otter.shared.common.model.config.channel.ChannelParameter.SyncConsistency;\nimport com.alibaba.otter.shared.common.model.config.channel.ChannelParameter.SyncMode;\nimport com.alibaba.otter.shared.common.model.config.data.DataMedia;\nimport com.alibaba.otter.shared.common.model.config.data.db.DbMediaSource;\nimport com.alibaba.otter.shared.common.model.config.pipeline.Pipeline;\nimport com.alibaba.otter.shared.etl.model.DbBatch;\nimport com.alibaba.otter.shared.etl.model.EventColumn;\nimport com.alibaba.otter.shared.etl.model.EventData;\nimport com.alibaba.otter.shared.etl.model.EventType;\n\n/**\n * 自由之门，允许手工触发数据订正，解析这些记录\n * \n * <pre>\n * buffer表结构：\n *  id , table_id ,  type , pk_data , gmt_create , gmt_modified\n *  \n * pk_data针对多主键时，使用char(1)进行分隔\n * </pre>\n * \n * @author jianghang 2012-4-25 下午04:41:33\n * @version 4.0.2\n */\npublic class FreedomExtractor extends AbstractExtractor<DbBatch> {\n\n    private static final Logger logger    = LoggerFactory.getLogger(FreedomExtractor.class);\n    private static final char   PK_SPLIT  = (char) 1;\n    // private static final String ID = \"id\";\n    private static final String TABLE_ID  = \"table_id\";\n    private static final String FULL_NAME = \"full_name\";\n    private static final String TYPE      = \"type\";\n    private static final String PK_DATA   = \"pk_data\";\n\n    public void extract(DbBatch dbBatch) throws ExtractException {\n        Assert.notNull(dbBatch);\n\n        // 读取配置\n        Pipeline pipeline = getPipeline(dbBatch.getRowBatch().getIdentity().getPipelineId());\n\n        boolean skipFreedom = pipeline.getParameters().getSkipFreedom();\n        String bufferSchema = pipeline.getParameters().getSystemSchema();\n        String bufferTable = pipeline.getParameters().getSystemBufferTable();\n\n        List<EventData> eventDatas = dbBatch.getRowBatch().getDatas();\n        Set<EventData> removeDatas = new HashSet<EventData>();// 使用set，提升remove时的查找速度\n        for (EventData eventData : eventDatas) {\n            if (StringUtils.equalsIgnoreCase(bufferSchema, eventData.getSchemaName())\n                && StringUtils.equalsIgnoreCase(bufferTable, eventData.getTableName())) {\n                if (eventData.getEventType().isDdl()) {\n                    continue;\n                }\n\n                if (skipFreedom) {// 判断是否需要忽略\n                    removeDatas.add(eventData);\n                    continue;\n                }\n\n                // 只处理insert / update记录\n                if (eventData.getEventType().isInsert() || eventData.getEventType().isUpdate()) {\n                    // 重新改写一下EventData的数据，根据系统表的定义\n                    EventColumn tableIdColumn = getMatchColumn(eventData.getColumns(), TABLE_ID);\n                    // 获取到对应tableId的media信息\n                    try {\n                        DataMedia dataMedia = null;\n                        Long tableId = Long.valueOf(tableIdColumn.getColumnValue());\n                        eventData.setTableId(tableId);\n                        if (tableId <= 0) { // 直接按照full_name进行查找\n                            // 尝试直接根据schema+table name进行查找\n                            EventColumn fullNameColumn = getMatchColumn(eventData.getColumns(), FULL_NAME);\n                            if (fullNameColumn != null) {\n                                String[] names = StringUtils.split(fullNameColumn.getColumnValue(), \".\");\n                                if (names.length >= 2) {\n                                    dataMedia = ConfigHelper.findSourceDataMedia(pipeline, names[0], names[1]);\n                                    eventData.setTableId(dataMedia.getId());\n                                } else {\n                                    throw new ConfigException(\"no such DataMedia \" + names);\n                                }\n                            }\n                        } else {\n                            // 如果指定了tableId，需要按照tableId进行严格查找，如果没找到，那说明不需要进行同步\n                            dataMedia = ConfigHelper.findDataMedia(pipeline,\n                                Long.valueOf(tableIdColumn.getColumnValue()));\n                        }\n\n                        DbDialect dbDialect = dbDialectFactory.getDbDialect(pipeline.getId(),\n                            (DbMediaSource) dataMedia.getSource());\n                        // 考虑offer[1-128]的配置模式\n                        if (!dataMedia.getNameMode().getMode().isSingle()\n                            || !dataMedia.getNamespaceMode().getMode().isSingle()) {\n                            boolean hasError = true;\n                            EventColumn fullNameColumn = getMatchColumn(eventData.getColumns(), FULL_NAME);\n                            if (fullNameColumn != null) {\n                                String[] names = StringUtils.split(fullNameColumn.getColumnValue(), \".\");\n                                if (names.length >= 2) {\n                                    eventData.setSchemaName(names[0]);\n                                    eventData.setTableName(names[1]);\n                                    hasError = false;\n                                }\n                            }\n\n                            if (hasError) {\n                                // 出现异常，需要记录一下\n                                logger.warn(\"dataMedia mode:{} , fullname:{} \",\n                                    dataMedia.getMode(),\n                                    fullNameColumn == null ? null : fullNameColumn.getColumnValue());\n                                removeDatas.add(eventData);\n                                // 跳过这条记录\n                                continue;\n                            }\n                        } else {\n                            eventData.setSchemaName(dataMedia.getNamespace());\n                            eventData.setTableName(dataMedia.getName());\n                        }\n\n                        // 更新业务类型\n                        EventColumn typeColumn = getMatchColumn(eventData.getColumns(), TYPE);\n                        EventType eventType = EventType.valuesOf(typeColumn.getColumnValue());\n                        eventData.setEventType(eventType);\n                        if (eventType.isUpdate()) {// 如果是update强制修改为insert，这样可以在目标端执行merge\n                                                   // sql\n                            eventData.setEventType(EventType.INSERT);\n                        } else if (eventType.isDdl()) {\n                            dbDialect.reloadTable(eventData.getSchemaName(), eventData.getTableName());\n                            removeDatas.add(eventData);// 删除当前记录\n                            continue;\n                        }\n                        // 重新构建新的业务主键字段\n                        EventColumn pkDataColumn = getMatchColumn(eventData.getColumns(), PK_DATA);\n                        String pkData = pkDataColumn.getColumnValue();\n                        String[] pks = StringUtils.split(pkData, PK_SPLIT);\n\n                        Table table = dbDialect.findTable(eventData.getSchemaName(), eventData.getTableName());\n                        List<EventColumn> newColumns = new ArrayList<EventColumn>();\n                        Column[] primaryKeyColumns = table.getPrimaryKeyColumns();\n                        if (primaryKeyColumns.length > pks.length) {\n                            throw new ExtractException(\"data pk column size not match , data:\" + eventData.toString());\n                        }\n                        // 构建字段\n                        Column[] allColumns = table.getColumns();\n                        int pkIndex = 0;\n                        for (int i = 0; i < allColumns.length; i++) {\n                            Column column = allColumns[i];\n                            if (column.isPrimaryKey()) {\n                                EventColumn newColumn = new EventColumn();\n                                newColumn.setIndex(i); // 设置下标\n                                newColumn.setColumnName(column.getName());\n                                newColumn.setColumnType(column.getTypeCode());\n                                newColumn.setColumnValue(pks[pkIndex]);\n                                newColumn.setKey(true);\n                                newColumn.setNull(pks[pkIndex] == null);\n                                newColumn.setUpdate(true);\n                                // 添加到记录\n                                newColumns.add(newColumn);\n                                pkIndex++;\n                            }\n                        }\n                        // 设置数据\n                        eventData.setKeys(newColumns);\n                        eventData.setOldKeys(new ArrayList<EventColumn>());\n                        eventData.setColumns(new ArrayList<EventColumn>());\n                        // 设置为行记录+反查\n                        eventData.setSyncMode(SyncMode.ROW);\n                        eventData.setSyncConsistency(SyncConsistency.MEDIA);\n                        eventData.setRemedy(true);\n                        eventData.setSize(1024);// 默认为1kb，如果还是按照binlog大小计算的话，可能会采用rpc传输，导致内存不够用\n                    } catch (ConfigException e) {\n                        // 忽略掉，因为系统表会被共享，所以这条记录会被不是该同步通道给获取到\n                        logger.info(\"find DataMedia error \" + eventData.toString(), e);\n                        removeDatas.add(eventData);\n                        continue;\n                    } catch (Throwable e) {\n                        // 出现异常时忽略掉\n                        logger.warn(\"process freedom data error \" + eventData.toString(), e);\n                        removeDatas.add(eventData);\n                        continue;\n                    }\n                } else {\n                    removeDatas.add(eventData);// 删除该记录\n                }\n            }\n        }\n\n        if (!CollectionUtils.isEmpty(removeDatas)) {\n            eventDatas.removeAll(removeDatas);\n        }\n    }\n\n    private EventColumn getMatchColumn(List<EventColumn> columns, String columnName) {\n        for (EventColumn column : columns) {\n            if (StringUtils.equalsIgnoreCase(column.getColumnName(), columnName)) {\n                return column;\n            }\n        }\n\n        return null;\n    }\n\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/extract/extractor/GroupExtractor.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.extract.extractor;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.apache.commons.lang.StringUtils;\nimport org.springframework.util.Assert;\nimport org.springframework.util.CollectionUtils;\n\nimport com.alibaba.otter.node.etl.extract.exceptions.ExtractException;\nimport com.alibaba.otter.shared.common.model.config.channel.ChannelParameter.SyncConsistency;\nimport com.alibaba.otter.shared.common.model.config.data.ColumnGroup;\nimport com.alibaba.otter.shared.common.model.config.data.ColumnPair;\nimport com.alibaba.otter.shared.common.model.config.data.DataMediaPair;\nimport com.alibaba.otter.shared.common.model.config.pipeline.Pipeline;\nimport com.alibaba.otter.shared.etl.model.DbBatch;\nimport com.alibaba.otter.shared.etl.model.EventColumn;\nimport com.alibaba.otter.shared.etl.model.EventData;\n\n/**\n * 变更的主键+变更的字段 会和group进行交集处理，发现有交集后会确保当前group的所有字段都可以得到同步\n * \n * @author simon 2012-4-26 下午5:04:51\n */\npublic class GroupExtractor extends AbstractExtractor<DbBatch> {\n\n    @Override\n    public void extract(DbBatch dbBatch) throws ExtractException {\n        Assert.notNull(dbBatch);\n        Assert.notNull(dbBatch.getRowBatch());\n\n        Pipeline pipeline = getPipeline(dbBatch.getRowBatch().getIdentity().getPipelineId());\n        List<DataMediaPair> dataMediaPairs = pipeline.getPairs();\n\n        /**\n         * Key = TableId<br>\n         * Value = a List of this tableId's column need to sync<br>\n         */\n        Map<Long, List<ColumnGroup>> groupColumns = new HashMap<Long, List<ColumnGroup>>();\n\n        for (DataMediaPair dataMediaPair : dataMediaPairs) {\n            List<ColumnGroup> columnGroups = dataMediaPair.getColumnGroups();\n            if (!CollectionUtils.isEmpty(columnGroups)) {\n                groupColumns.put(dataMediaPair.getSource().getId(), columnGroups);\n            }\n        }\n\n        List<EventData> eventDatas = dbBatch.getRowBatch().getDatas();\n        for (EventData eventData : eventDatas) {\n            if (eventData.getEventType().isDdl()) {\n                continue;\n            }\n\n            List<ColumnGroup> columnGroups = groupColumns.get(eventData.getTableId());\n            if (!CollectionUtils.isEmpty(columnGroups)) {\n                for (ColumnGroup columnGroup : columnGroups) {\n                    if (columnGroup != null && !CollectionUtils.isEmpty(columnGroup.getColumnPairs())) {\n                        groupFilter(eventData, columnGroup);\n                    }\n                }\n            }\n        }\n    }\n\n    private void groupFilter(EventData eventData, ColumnGroup columnGroup) {\n        List<EventColumn> addColumns = new ArrayList<EventColumn>();\n\n        // 判断一下是否存在字段组内字段的变更\n        Set<String> updatedColumns = new HashSet<String>();\n        Set<String> pks = new HashSet<String>();\n\n        // 注意，这里只拿实际需要同步变更的字段\n        for (EventColumn column : eventData.getUpdatedColumns()) {\n            updatedColumns.add(column.getColumnName());\n        }\n        for (EventColumn pk : eventData.getKeys()) {\n            pks.add(pk.getColumnName());\n        }\n\n        if (!CollectionUtils.isEmpty(eventData.getOldKeys())) {// 处理变更的主键\n            int i = 0;\n            for (EventColumn pk : eventData.getKeys()) {\n                if (!StringUtils.equals(pk.getColumnValue(), eventData.getOldKeys().get(i).getColumnValue())) {\n                    updatedColumns.add(pk.getColumnName());\n                }\n                i++;\n            }\n        }\n\n        if (containsInGroupColumn(updatedColumns, columnGroup.getColumnPairs())) {// 存在交集\n            // 将变更的字段+变更的主键 去和 group字段进行交集处理\n            for (ColumnPair columnPair : columnGroup.getColumnPairs()) {\n                boolean groupColumnHasInChangedColunms = false;// 原谅我起这么长的变量名…\n\n                // add by ljh at 2012-11-04\n                // 做一个优化：\n                // 1. 在select模块如果发现存在FileResolver，会补充完整行记录过来\n                // 2. 在group判断update=true字段和Group的定义存在交集时，可以直接使用before记录进行处理，可以减少反查数据库的操作\n\n                // 这里直接拿所有columns，而不是拿实际变更过的updateColumns\n                // for (String columnName : updatedColumns) {\n                for (EventColumn column : eventData.getColumns()) {\n                    if (StringUtils.equalsIgnoreCase(columnPair.getSourceColumn().getName(), column.getColumnName())) {\n                        groupColumnHasInChangedColunms = true;\n                        if (!column.isUpdate()) {// 如果为非同步字段，强制修改为update=true进行数据同步\n                            column.setUpdate(true);\n                        }\n                        break;\n                    }\n                }\n\n                if (!groupColumnHasInChangedColunms) {// 不存在对应的变更字段记录\n                    String columnName = columnPair.getSourceColumn().getName();\n                    if (!pks.contains(columnName)) { // 只添加非主键的值到反查column，因为主键不需要反查\n                        EventColumn addColumn = new EventColumn();\n                        addColumn.setColumnName(columnPair.getSourceColumn().getName());\n                        addColumn.setUpdate(true);\n                        addColumns.add(addColumn);\n                    }\n                }\n            }\n\n            if (!CollectionUtils.isEmpty(addColumns)) {\n                // 字段去重\n                eventData.getColumns().addAll(addColumns);// 添加不足的字段\n                eventData.setSyncConsistency(SyncConsistency.MEDIA);\n                return;\n            }\n        }\n    }\n\n    /**\n     * 检查一下是否出现了字段组中定义的字段\n     */\n    private boolean containsInGroupColumn(Set<String> columns, List<ColumnPair> columnPairs) {\n        for (ColumnPair columnPair : columnPairs) {\n            for (String columnName : columns) {\n                if (StringUtils.equalsIgnoreCase(columnPair.getSourceColumn().getName(), columnName)) {\n                    return true;\n                }\n            }\n        }\n\n        return false;\n    }\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/extract/extractor/OtterExtractor.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.extract.extractor;\n\nimport com.alibaba.otter.node.etl.extract.exceptions.ExtractException;\n\n/**\n * 组装数据,有多种来源，mysql,oracle,store,file等.\n */\npublic interface OtterExtractor<P> {\n\n    /**\n     * 数据装配\n     */\n    void extract(P param) throws ExtractException;\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/extract/extractor/OtterExtractorFactory.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.extract.extractor;\n\nimport java.util.List;\n\nimport org.springframework.beans.BeansException;\nimport org.springframework.beans.factory.BeanFactory;\nimport org.springframework.beans.factory.BeanFactoryAware;\nimport org.springframework.util.Assert;\n\nimport com.alibaba.otter.shared.etl.model.DbBatch;\n\n/**\n * otter extractor工厂处理\n * \n * @author jianghang 2012-4-18 下午04:09:15\n * @version 4.0.2\n */\npublic class OtterExtractorFactory implements BeanFactoryAware {\n\n    private List        dbBatchExtractor;\n    private BeanFactory beanFactory;\n\n    public void extract(DbBatch dbBatch) {\n        Assert.notNull(dbBatch);\n        for (Object extractor : dbBatchExtractor) {\n            OtterExtractor otterExtractor = null;\n            if (extractor instanceof java.lang.String) {\n                // 每次从容器中取一次，有做池化处理\n                otterExtractor = (OtterExtractor) beanFactory.getBean((String) extractor, OtterExtractor.class);\n            } else {\n                otterExtractor = (OtterExtractor) extractor;\n            }\n\n            otterExtractor.extract(dbBatch);\n        }\n    }\n\n    // ================== setter / getter ====================\n\n    public void setDbBatchExtractor(List dbBatchExtractor) {\n        this.dbBatchExtractor = dbBatchExtractor;\n    }\n\n    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {\n        this.beanFactory = beanFactory;\n    }\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/extract/extractor/ProcessorExtractor.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.extract.extractor;\n\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\nimport javax.sql.DataSource;\n\nimport org.slf4j.MDC;\nimport org.springframework.util.CollectionUtils;\n\nimport com.alibaba.otter.node.etl.OtterConstants;\nimport com.alibaba.otter.node.etl.common.datasource.DataSourceService;\nimport com.alibaba.otter.node.etl.extract.exceptions.ExtractException;\nimport com.alibaba.otter.shared.common.model.config.ConfigHelper;\nimport com.alibaba.otter.shared.common.model.config.data.DataMedia;\nimport com.alibaba.otter.shared.common.model.config.data.DataMediaPair;\nimport com.alibaba.otter.shared.common.model.config.pipeline.Pipeline;\nimport com.alibaba.otter.shared.common.utils.extension.ExtensionFactory;\nimport com.alibaba.otter.shared.common.utils.thread.ExecutorTemplate;\nimport com.alibaba.otter.shared.common.utils.thread.ExecutorTemplateGetter;\nimport com.alibaba.otter.shared.etl.extend.processor.EventProcessor;\nimport com.alibaba.otter.shared.etl.extend.processor.support.DataSourceFetcher;\nimport com.alibaba.otter.shared.etl.extend.processor.support.DataSourceFetcherAware;\nimport com.alibaba.otter.shared.etl.model.DbBatch;\nimport com.alibaba.otter.shared.etl.model.EventData;\nimport com.alibaba.otter.shared.etl.model.RowBatch;\n\n/**\n * 调用{@linkplain EventProcessor}，进行业务数据处理\n * \n * @author jianghang 2012-7-23 下午03:11:19\n */\npublic class ProcessorExtractor extends AbstractExtractor<DbBatch> {\n\n    private ExtensionFactory       extensionFactory;\n    private DataSourceService      dataSourceService;\n    private ExecutorTemplateGetter executorTemplateGetter;\n\n    public void extract(DbBatch param) throws ExtractException {\n        ExecutorTemplate executorTemplate = null;\n        try {\n            RowBatch rowBatch = param.getRowBatch();\n            final Pipeline pipeline = getPipeline(rowBatch.getIdentity().getPipelineId());\n            List<EventData> eventDatas = rowBatch.getDatas();\n            final Set<EventData> removeDatas = Collections.synchronizedSet(new HashSet<EventData>());// 使用set，提升remove时的查找速度\n            executorTemplate = executorTemplateGetter.get();\n            executorTemplate.start();\n            // 重新设置下poolSize\n            executorTemplate.adjustPoolSize(pipeline.getParameters().getExtractPoolSize());\n            for (final EventData eventData : eventDatas) {\n                List<DataMediaPair> dataMediaPairs = ConfigHelper.findDataMediaPairByMediaId(pipeline,\n                    eventData.getTableId());\n                if (dataMediaPairs == null) {\n                    throw new ExtractException(\"ERROR ## the dataMediaId = \" + eventData.getTableId()\n                                               + \" dataMediaPair is null,please check\");\n                }\n\n                for (DataMediaPair dataMediaPair : dataMediaPairs) {\n                    if (!dataMediaPair.isExistFilter()) {\n                        continue;\n                    }\n\n                    final EventProcessor eventProcessor = extensionFactory.getExtension(EventProcessor.class,\n                        dataMediaPair.getFilterData());\n                    if (eventProcessor instanceof DataSourceFetcherAware) {\n                        ((DataSourceFetcherAware) eventProcessor).setDataSourceFetcher(new DataSourceFetcher() {\n\n                            @Override\n                            public DataSource fetch(Long tableId) {\n                                DataMedia dataMedia = ConfigHelper.findDataMedia(pipeline, tableId);\n                                return dataSourceService.getDataSource(pipeline.getId(), dataMedia.getSource());\n                            }\n                        });\n\n                        executorTemplate.submit(new Runnable() {\n\n                            @Override\n                            public void run() {\n                                MDC.put(OtterConstants.splitPipelineLogFileKey, String.valueOf(pipeline.getId()));\n                                boolean process = eventProcessor.process(eventData);\n                                if (!process) {\n                                    removeDatas.add(eventData);// 添加到删除记录中\n                                }\n                            }\n                        });\n                    } else {\n                        boolean process = eventProcessor.process(eventData);\n                        if (!process) {\n                            removeDatas.add(eventData);// 添加到删除记录中\n                            break;\n                        }\n                    }\n\n                }\n\n            }\n\n            // 等待所有都处理完成\n            executorTemplate.waitForResult();\n\n            if (!CollectionUtils.isEmpty(removeDatas)) {\n                eventDatas.removeAll(removeDatas);\n            }\n        } finally {\n            if (executorTemplate != null) {\n                executorTemplateGetter.release(executorTemplate);\n            }\n        }\n\n    }\n\n    public void setExtensionFactory(ExtensionFactory extensionFactory) {\n        this.extensionFactory = extensionFactory;\n    }\n\n    public void setDataSourceService(DataSourceService dataSourceService) {\n        this.dataSourceService = dataSourceService;\n    }\n\n    public void setExecutorTemplateGetter(ExecutorTemplateGetter executorTemplateGetter) {\n        this.executorTemplateGetter = executorTemplateGetter;\n    }\n\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/extract/extractor/ViewExtractor.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.extract.extractor;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.apache.commons.lang.StringUtils;\nimport org.springframework.util.Assert;\nimport org.springframework.util.CollectionUtils;\n\nimport com.alibaba.otter.node.etl.extract.exceptions.ExtractException;\nimport com.alibaba.otter.shared.common.model.config.data.ColumnPair;\nimport com.alibaba.otter.shared.common.model.config.data.ColumnPairMode;\nimport com.alibaba.otter.shared.common.model.config.data.DataMediaPair;\nimport com.alibaba.otter.shared.common.model.config.pipeline.Pipeline;\nimport com.alibaba.otter.shared.etl.model.DbBatch;\nimport com.alibaba.otter.shared.etl.model.EventColumn;\nimport com.alibaba.otter.shared.etl.model.EventData;\n\n/**\n * @author simon 2012-4-26 下午5:04:51\n */\npublic class ViewExtractor extends AbstractExtractor<DbBatch> {\n\n    @Override\n    public void extract(DbBatch dbBatch) throws ExtractException {\n        Assert.notNull(dbBatch);\n        Assert.notNull(dbBatch.getRowBatch());\n\n        Pipeline pipeline = getPipeline(dbBatch.getRowBatch().getIdentity().getPipelineId());\n        List<DataMediaPair> dataMediaPairs = pipeline.getPairs();\n\n        /**\n         * Key = TableId<br>\n         * Value = a List of this tableId's column need to sync<br>\n         */\n        Map<Long, List<ColumnPair>> viewColumnPairs = new HashMap<Long, List<ColumnPair>>();\n        Map<Long, ColumnPairMode> viewColumnPairModes = new HashMap<Long, ColumnPairMode>();\n\n        for (DataMediaPair dataMediaPair : dataMediaPairs) {\n            List<ColumnPair> columnPairs = dataMediaPair.getColumnPairs();\n            // 设置ColumnPairMode\n            viewColumnPairModes.put(dataMediaPair.getSource().getId(), dataMediaPair.getColumnPairMode());\n            // 如果没有columnPairs，则默认全字段同步，不做处理\n            if (!CollectionUtils.isEmpty(columnPairs)) {\n                viewColumnPairs.put(dataMediaPair.getSource().getId(), columnPairs);\n            }\n        }\n\n        List<EventData> eventDatas = dbBatch.getRowBatch().getDatas();\n        Set<EventData> removeDatas = new HashSet<EventData>();// 使用set，提升remove时的查找速度\n        for (EventData eventData : eventDatas) {\n            if (eventData.getEventType().isDdl()) {\n                continue;\n            }\n\n            List<ColumnPair> columns = viewColumnPairs.get(eventData.getTableId());\n            if (!CollectionUtils.isEmpty(columns)) {\n                // 组装需要同步的Column\n                ColumnPairMode mode = viewColumnPairModes.get(eventData.getTableId());\n                eventData.setColumns(columnFilter(eventData.getColumns(), columns, mode));\n                eventData.setKeys(columnFilter(eventData.getKeys(), columns, mode));\n                if (!CollectionUtils.isEmpty(eventData.getOldKeys())) {\n                    eventData.setOldKeys(columnFilter(eventData.getOldKeys(), columns, mode));\n                }\n\n                if (CollectionUtils.isEmpty(eventData.getKeys())) { // 无主键，报错\n                    throw new ExtractException(\n                                               String.format(\"eventData after viewExtractor has no pks , pls check! identity:%s, new eventData:%s\",\n                                                             dbBatch.getRowBatch().getIdentity().toString(),\n                                                             eventData.toString()));\n                }\n\n                // insert：可能view视图只有主键字段，针对无字段情况需要通过\n                // delete: eventData本身就没有字段信息，针对无字段情况需要通过\n                // update: 过滤后如果无字段(变更需要同步)和主键变更，则可以忽略之，避免sql语法错误\n                if (eventData.getEventType().isUpdate()\n                    && (CollectionUtils.isEmpty(eventData.getColumns()) || CollectionUtils.isEmpty(eventData.getUpdatedColumns()))\n                    && CollectionUtils.isEmpty(eventData.getOldKeys())) {\n                    // 过滤之后无字段需要同步，并且不存在主键变更同步，则忽略该记录\n                    removeDatas.add(eventData);\n                }\n            }\n\n        }\n\n        if (!CollectionUtils.isEmpty(removeDatas)) {\n            eventDatas.removeAll(removeDatas);\n        }\n    }\n\n    private List<EventColumn> columnFilter(List<EventColumn> eventColumns, List<ColumnPair> columnPairs,\n                                           ColumnPairMode mode) {\n        if (mode == null) {\n            mode = ColumnPairMode.INCLUDE;\n        }\n\n        List<EventColumn> tempColumns = new ArrayList<EventColumn>();\n        Map<String, ColumnPair> viewNames = new HashMap<String, ColumnPair>();\n        for (ColumnPair columnPair : columnPairs) {\n            viewNames.put(StringUtils.lowerCase(columnPair.getSourceColumn().getName()), columnPair);\n        }\n\n        for (EventColumn eventColumn : eventColumns) {\n            if (mode.isInclude() && viewNames.containsKey(StringUtils.lowerCase(eventColumn.getColumnName()))) {\n                tempColumns.add(eventColumn); // 正向匹配\n            } else if (mode.isExclude() && !viewNames.containsKey(StringUtils.lowerCase(eventColumn.getColumnName()))) {\n                // 逆向匹配\n                tempColumns.add(eventColumn);\n            }\n\n        }\n        return tempColumns;\n    }\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/load/LoadTask.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.load;\n\nimport java.util.List;\n\nimport org.slf4j.MDC;\n\nimport com.alibaba.otter.node.etl.OtterConstants;\nimport com.alibaba.otter.node.etl.common.jmx.StageAggregation.AggregationItem;\nimport com.alibaba.otter.node.etl.common.pipe.PipeKey;\nimport com.alibaba.otter.node.etl.common.task.GlobalTask;\nimport com.alibaba.otter.node.etl.extract.SetlFuture;\nimport com.alibaba.otter.node.etl.load.loader.LoadContext;\nimport com.alibaba.otter.node.etl.load.loader.OtterLoaderFactory;\nimport com.alibaba.otter.node.etl.load.loader.db.context.DbLoadContext;\nimport com.alibaba.otter.node.etl.load.loader.interceptor.LoadInterceptor;\nimport com.alibaba.otter.shared.arbitrate.model.EtlEventData;\nimport com.alibaba.otter.shared.common.model.config.enums.StageType;\nimport com.alibaba.otter.shared.etl.model.DbBatch;\n\n/**\n * load工作线程,负责桥接连接仲裁器,Config,loader\n * \n * @author jianghang 2011-11-3 下午07:05:20\n * @version 4.0.0\n */\npublic class LoadTask extends GlobalTask {\n\n    private OtterLoaderFactory otterLoaderFactory;\n    private LoadInterceptor    dbLoadInterceptor;\n\n    public LoadTask(Long pipelineId){\n        super(pipelineId);\n    }\n\n    public void run() {\n        MDC.put(OtterConstants.splitPipelineLogFileKey, String.valueOf(pipelineId));\n        while (running) {\n            try {\n                final EtlEventData etlEventData = arbitrateEventService.loadEvent().await(pipelineId);\n                Runnable task = new Runnable() {\n\n                    public void run() {\n                        // 设置profiling信息\n                        boolean profiling = isProfiling();\n                        Long profilingStartTime = null;\n                        if (profiling) {\n                            profilingStartTime = System.currentTimeMillis();\n                        }\n\n                        MDC.put(OtterConstants.splitPipelineLogFileKey, String.valueOf(pipelineId));\n                        String currentName = Thread.currentThread().getName();\n                        Thread.currentThread().setName(createTaskName(pipelineId, \"LoadWorker\"));\n                        List<LoadContext> processedContexts = null;\n                        try {\n                            // 后续可判断同步数据是否为rowData\n                            List<PipeKey> keys = (List<PipeKey>) etlEventData.getDesc();\n                            DbBatch dbBatch = rowDataPipeDelegate.get(keys);\n\n                            // 可能拿到为null，因为内存不足或者网络异常，长时间阻塞时，导致从pipe拿数据出现异常，数据可能被上一个节点已经删除\n                            if (dbBatch == null) {\n                                processMissData(pipelineId, \"load miss data with keys:\" + keys.toString());\n                                return;\n                            }\n\n                            // 进行数据load处理\n                            otterLoaderFactory.setStartTime(dbBatch.getRowBatch().getIdentity(),\n                                                            etlEventData.getStartTime());\n\n                            processedContexts = otterLoaderFactory.load(dbBatch);\n\n                            if (profiling) {\n                                Long profilingEndTime = System.currentTimeMillis();\n                                stageAggregationCollector.push(pipelineId,\n                                                               StageType.LOAD,\n                                                               new AggregationItem(profilingStartTime, profilingEndTime));\n                            }\n                            // 处理完成后通知single已完成\n                            arbitrateEventService.loadEvent().single(etlEventData);\n                        } catch (Throwable e) {\n                            if (!isInterrupt(e)) {\n                                logger.error(String.format(\"[%s] loadWork executor is error! data:%s\", pipelineId,\n                                                           etlEventData), e);\n                            } else {\n                                logger.info(String.format(\"[%s] loadWork executor is interrrupt! data:%s\", pipelineId,\n                                                          etlEventData), e);\n                            }\n\n                            if (processedContexts != null) {// 说明load成功了，但是通知仲裁器失败了，需要记录下记录到store\n                                for (LoadContext context : processedContexts) {\n                                    try {\n                                        if (context instanceof DbLoadContext) {\n                                            dbLoadInterceptor.error((DbLoadContext) context);\n                                        }\n\n                                    } catch (Throwable ie) {\n                                        logger.error(String.format(\"[%s] interceptor process error failed!\", pipelineId),\n                                                     ie);\n                                    }\n                                }\n                            }\n\n                            // try {\n                            // arbitrateEventService.loadEvent().release(pipelineId);\n                            // // 释放锁\n                            // } catch (Throwable ie) {\n                            // logger.error(String.format(\"[%s] load release failed!\",\n                            // pipelineId), ie);\n                            // }\n\n                            if (!isInterrupt(e)) {\n                                sendRollbackTermin(pipelineId, e);\n                            }\n                        } finally {\n                            Thread.currentThread().setName(currentName);\n                            MDC.remove(OtterConstants.splitPipelineLogFileKey);\n                        }\n                    }\n                };\n\n                // 构造pending任务，可在关闭线程时退出任务\n                SetlFuture extractFuture = new SetlFuture(StageType.LOAD, etlEventData.getProcessId(), pendingFuture,\n                                                          task);\n                executorService.execute(extractFuture);\n            } catch (Throwable e) {\n                if (isInterrupt(e)) {\n                    logger.info(String.format(\"[%s] loadTask is interrupted!\", pipelineId), e);\n                    // arbitrateEventService.loadEvent().release(pipelineId); //\n                    // 释放锁\n                    return;\n                } else {\n                    logger.error(String.format(\"[%s] loadTask is error!\", pipelineId), e);\n                    // arbitrateEventService.loadEvent().release(pipelineId); //\n                    // 释放锁\n                    sendRollbackTermin(pipelineId, e); // 先解除lock，后发送rollback信号\n                }\n            }\n        }\n    }\n\n    // =================== setter / getter ======================\n\n    public void setOtterLoaderFactory(OtterLoaderFactory otterLoaderFactory) {\n        this.otterLoaderFactory = otterLoaderFactory;\n    }\n\n    public void setDbLoadInterceptor(LoadInterceptor dbLoadInterceptor) {\n        this.dbLoadInterceptor = dbLoadInterceptor;\n    }\n\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/load/exception/LoadException.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.load.exception;\n\nimport org.apache.commons.lang.exception.NestableRuntimeException;\n\n/**\n * @author jianghang 2011-9-16 下午01:59:25\n * @version 4.0.0\n */\npublic class LoadException extends NestableRuntimeException {\n\n    private static final long serialVersionUID = -7288830284122672209L;\n\n    private String            errorCode;\n    private String            errorDesc;\n\n    public LoadException(String errorCode){\n        super(errorCode);\n    }\n\n    public LoadException(String errorCode, Throwable cause){\n        super(errorCode, cause);\n    }\n\n    public LoadException(String errorCode, String errorDesc){\n        super(errorCode + \":\" + errorDesc);\n    }\n\n    public LoadException(String errorCode, String errorDesc, Throwable cause){\n        super(errorCode + \":\" + errorDesc, cause);\n    }\n\n    public LoadException(Throwable cause){\n        super(cause);\n    }\n\n    public String getErrorCode() {\n        return errorCode;\n    }\n\n    public String getErrorDesc() {\n        return errorDesc;\n    }\n\n    @Override\n    public Throwable fillInStackTrace() {\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/load/loader/AbstractLoadContext.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.load.loader;\n\nimport java.io.Serializable;\nimport java.util.Collections;\nimport java.util.LinkedList;\nimport java.util.List;\n\nimport com.alibaba.otter.shared.common.model.config.channel.Channel;\nimport com.alibaba.otter.shared.common.model.config.pipeline.Pipeline;\nimport com.alibaba.otter.shared.etl.model.Identity;\n\n/**\n * 类LoadContext.java的实现描述：TODO 类实现描述\n * \n * @author simon 2012-7-3 下午2:22:51\n * @version 4.1.0\n */\npublic abstract class AbstractLoadContext<T> implements LoadContext, Serializable {\n\n    private static final long serialVersionUID = -2052280419851872736L;\n    private Identity          identity;\n    private Channel           channel;\n    private Pipeline          pipeline;\n    protected List<T>         prepareDatas;                            // 准备处理的数据\n    protected List<T>         processedDatas;                          // 已处理完成的数据\n    protected List<T>         failedDatas;\n\n    public AbstractLoadContext(){\n        this.prepareDatas = Collections.synchronizedList(new LinkedList<T>());\n        this.processedDatas = Collections.synchronizedList(new LinkedList<T>());\n        this.failedDatas = Collections.synchronizedList(new LinkedList<T>());\n    }\n\n    public Identity getIdentity() {\n        return identity;\n    }\n\n    public void setIdentity(Identity identity) {\n        this.identity = identity;\n    }\n\n    public List<T> getPrepareDatas() {\n        return prepareDatas;\n    }\n\n    public void setPrepareDatas(List<T> prepareDatas) {\n        this.prepareDatas = prepareDatas;\n    }\n\n    public void addProcessData(T processData) {\n        this.processedDatas.add(processData);\n    }\n\n    public List<T> getProcessedDatas() {\n        return processedDatas;\n    }\n\n    public void setProcessedDatas(List<T> processedDatas) {\n        this.processedDatas = processedDatas;\n    }\n\n    public List<T> getFailedDatas() {\n        return failedDatas;\n    }\n\n    public void addFailedData(T failedData) {\n        this.failedDatas.add(failedData);\n    }\n\n    public void setFailedDatas(List<T> failedDatas) {\n        this.failedDatas = failedDatas;\n    }\n\n    public Pipeline getPipeline() {\n        return pipeline;\n    }\n\n    public void setPipeline(Pipeline pipeline) {\n        this.pipeline = pipeline;\n    }\n\n    public Channel getChannel() {\n        return channel;\n    }\n\n    public void setChannel(Channel channel) {\n        this.channel = channel;\n    }\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/load/loader/LoadContext.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.load.loader;\n\n/**\n * @author simon 2012-7-3 下午4:16:38\n * @version 4.1.0\n */\npublic interface LoadContext {\n\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/load/loader/LoadStatsTracker.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.load.loader;\n\nimport java.util.Collection;\nimport java.util.Map;\nimport java.util.concurrent.atomic.AtomicLong;\n\nimport com.alibaba.otter.shared.etl.model.Identity;\nimport com.google.common.base.Function;\nimport com.google.common.collect.MigrateMap;\nimport com.google.common.collect.OtterMigrateMap;\n\n/**\n * 统计跟踪器\n * \n * @author jianghang 2011-11-18 上午11:15:10\n * @version 4.0.0\n */\npublic class LoadStatsTracker {\n\n    private Map<Identity, LoadThroughput> throughputs;\n\n    public LoadStatsTracker(){\n        throughputs = OtterMigrateMap.makeComputingMap(new Function<Identity, LoadThroughput>() {\n\n            public LoadThroughput apply(Identity identity) {\n                return new LoadThroughput(identity);\n            }\n        });\n    }\n\n    public LoadThroughput getStat(Identity identity) {\n        return throughputs.get(identity);\n    }\n\n    public void removeStat(Identity identity) {\n        throughputs.remove(identity);\n    }\n\n    public static class LoadThroughput {\n\n        private Identity               identity;\n        private Long                   startTime;\n        private Map<Long, LoadCounter> counters;\n\n        public LoadThroughput(Identity identity){\n            counters = MigrateMap.makeComputingMap(new Function<Long, LoadCounter>() {\n\n                public LoadCounter apply(Long pairId) {\n                    return new LoadCounter(pairId);\n                }\n            });\n        }\n\n        public LoadCounter getStat(Long pairId) {\n            return counters.get(pairId);\n        }\n\n        public Collection<LoadCounter> getStats() {\n            return counters.values();\n        }\n\n        public Identity getIdentity() {\n            return identity;\n        }\n\n        public void setIdentity(Identity identity) {\n            this.identity = identity;\n        }\n\n        public Long getStartTime() {\n            return startTime;\n        }\n\n        public void setStartTime(Long startTime) {\n            this.startTime = startTime;\n        }\n\n    }\n\n    public static class LoadCounter {\n\n        private Long       pairId;\n        private AtomicLong fileSize    = new AtomicLong(0); // 文件大小\n        private AtomicLong fileCount   = new AtomicLong(0); // 文件数量\n        private AtomicLong rowSize     = new AtomicLong(0);\n        private AtomicLong rowCount    = new AtomicLong(0);\n        private AtomicLong mqCount     = new AtomicLong(0);\n        private AtomicLong mqSize      = new AtomicLong(0);\n        private AtomicLong deleteCount = new AtomicLong(0);\n        private AtomicLong updateCount = new AtomicLong(0);\n        private AtomicLong insertCount = new AtomicLong(0);\n\n        public LoadCounter(Long pairId){\n            this.pairId = pairId;\n        }\n\n        public Long getPairId() {\n            return pairId;\n        }\n\n        public void setPairId(Long pairId) {\n            this.pairId = pairId;\n        }\n\n        public AtomicLong getFileSize() {\n            return fileSize;\n        }\n\n        public void setFileSize(AtomicLong fileSize) {\n            this.fileSize = fileSize;\n        }\n\n        public AtomicLong getFileCount() {\n            return fileCount;\n        }\n\n        public void setFileCount(AtomicLong fileCount) {\n            this.fileCount = fileCount;\n        }\n\n        public AtomicLong getDeleteCount() {\n            return deleteCount;\n        }\n\n        public void setDeleteCount(AtomicLong deleteCount) {\n            this.deleteCount = deleteCount;\n        }\n\n        public AtomicLong getUpdateCount() {\n            return updateCount;\n        }\n\n        public void setUpdateCount(AtomicLong updateCount) {\n            this.updateCount = updateCount;\n        }\n\n        public AtomicLong getInsertCount() {\n            return insertCount;\n        }\n\n        public void setInsertCount(AtomicLong insertCount) {\n            this.insertCount = insertCount;\n        }\n\n        public AtomicLong getRowSize() {\n            return rowSize;\n        }\n\n        public void setRowSize(AtomicLong rowSize) {\n            this.rowSize = rowSize;\n        }\n\n        public AtomicLong getRowCount() {\n            return rowCount;\n        }\n\n        public void setRowCount(AtomicLong rowCount) {\n            this.rowCount = rowCount;\n        }\n\n        public AtomicLong getMqCount() {\n            return mqCount;\n        }\n\n        public void setMqCount(AtomicLong mqCount) {\n            this.mqCount = mqCount;\n        }\n\n        public AtomicLong getMqSize() {\n            return mqSize;\n        }\n\n        public void setMqSize(AtomicLong mqSize) {\n            this.mqSize = mqSize;\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/load/loader/OtterLoader.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.load.loader;\n\n/**\n * otter数据loader接口\n * \n * @author jianghang 2011-10-27 上午11:15:13\n * @version 4.0.0\n */\npublic interface OtterLoader<P, R> {\n\n    public R load(P data);\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/load/loader/OtterLoaderFactory.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.load.loader;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Date;\nimport java.util.List;\n\nimport org.springframework.util.CollectionUtils;\n\nimport com.alibaba.otter.node.common.statistics.StatisticsClientService;\nimport com.alibaba.otter.node.etl.load.loader.LoadStatsTracker.LoadCounter;\nimport com.alibaba.otter.node.etl.load.loader.LoadStatsTracker.LoadThroughput;\nimport com.alibaba.otter.node.etl.load.loader.db.DataBatchLoader;\nimport com.alibaba.otter.shared.common.model.statistics.table.TableStat;\nimport com.alibaba.otter.shared.common.model.statistics.throughput.ThroughputStat;\nimport com.alibaba.otter.shared.common.model.statistics.throughput.ThroughputType;\nimport com.alibaba.otter.shared.etl.model.DbBatch;\nimport com.alibaba.otter.shared.etl.model.Identity;\n\n/**\n * loader执行工厂，可根据不同的数据load进行路由到指定的{@linkplain OtterLoader}进行处理\n * \n * @author simon 2012-7-3 下午4:16:58\n * @version 4.1.0\n */\npublic class OtterLoaderFactory {\n\n    private DataBatchLoader         dataBatchLoader;\n    private LoadStatsTracker        loadStatsTracker;\n    private StatisticsClientService statisticsClientService;\n\n    public List<LoadContext> load(DbBatch dbBatch) {\n        try {\n            return dataBatchLoader.load(dbBatch);\n        } finally {\n            try {\n                sendStat(dbBatch.getRowBatch().getIdentity());\n            } finally {\n                loadStatsTracker.removeStat(dbBatch.getRowBatch().getIdentity());\n            }\n        }\n\n    }\n\n    private void sendStat(Identity identity) {\n        LoadThroughput throughput = loadStatsTracker.getStat(identity);\n        Collection<LoadCounter> counters = throughput.getStats();\n        Date endTime = new Date();\n        // 处理table stat\n        long fileSize = 0L;\n        long fileCount = 0L;\n        long rowSize = 0L;\n        long rowCount = 0L;\n        long mqSize = 0L;\n        long mqCount = 0L;\n        List<TableStat> tableStats = new ArrayList<TableStat>();\n        for (LoadCounter counter : counters) {\n            TableStat stat = new TableStat();\n            stat.setPipelineId(identity.getPipelineId());\n            stat.setDataMediaPairId(counter.getPairId());\n            stat.setFileCount(counter.getFileCount().longValue());\n            stat.setFileSize(counter.getFileSize().longValue());\n            stat.setInsertCount(counter.getInsertCount().longValue());\n            stat.setUpdateCount(counter.getUpdateCount().longValue());\n            stat.setDeleteCount(counter.getDeleteCount().longValue());\n            stat.setStartTime(new Date(throughput.getStartTime()));\n            stat.setEndTime(endTime);\n            // 5项中有一项不为空才通知\n            if (!(stat.getFileCount().equals(0L) && stat.getFileSize().equals(0L) && stat.getInsertCount().equals(0L)\n                  && stat.getDeleteCount().equals(0L) && stat.getUpdateCount().equals(0L))) {\n                tableStats.add(stat);\n            }\n\n            fileSize += counter.getFileSize().longValue();\n            fileCount += counter.getFileCount().longValue();\n            rowSize += counter.getRowSize().longValue();\n            rowCount += counter.getRowCount().longValue();\n\n            mqSize += counter.getMqSize().longValue();\n            mqCount += counter.getMqCount().longValue();\n        }\n        if (!CollectionUtils.isEmpty(tableStats)) {\n            statisticsClientService.sendTableStats(tableStats);\n        }\n\n        List<ThroughputStat> throughputStats = new ArrayList<ThroughputStat>();\n        if (!(rowCount == 0 && rowSize == 0)) {\n            // 处理Throughput stat\n            ThroughputStat rowThroughputStat = new ThroughputStat();\n            rowThroughputStat.setType(ThroughputType.ROW);\n            rowThroughputStat.setPipelineId(identity.getPipelineId());\n            rowThroughputStat.setNumber(rowCount);\n            rowThroughputStat.setSize(rowSize);\n            rowThroughputStat.setStartTime(new Date(throughput.getStartTime()));\n            rowThroughputStat.setEndTime(endTime);\n            throughputStats.add(rowThroughputStat);\n        }\n        if (!(fileCount == 0 && fileSize == 0)) {\n            ThroughputStat fileThroughputStat = new ThroughputStat();\n            fileThroughputStat.setType(ThroughputType.FILE);\n            fileThroughputStat.setPipelineId(identity.getPipelineId());\n            fileThroughputStat.setNumber(fileCount);\n            fileThroughputStat.setSize(fileSize);\n            fileThroughputStat.setStartTime(new Date(throughput.getStartTime()));\n            fileThroughputStat.setEndTime(endTime);\n            throughputStats.add(fileThroughputStat);\n        }\n\n        // add by 2012-07-06 for mq loader\n        if (!(mqCount == 0 && mqSize == 0)) {\n            ThroughputStat mqThroughputStat = new ThroughputStat();\n            mqThroughputStat.setType(ThroughputType.MQ);\n            mqThroughputStat.setPipelineId(identity.getPipelineId());\n            mqThroughputStat.setNumber(mqCount);\n            mqThroughputStat.setSize(mqSize);\n            mqThroughputStat.setStartTime(new Date(throughput.getStartTime()));\n            mqThroughputStat.setEndTime(endTime);\n            throughputStats.add(mqThroughputStat);\n        }\n\n        if (!CollectionUtils.isEmpty(throughputStats)) {\n            statisticsClientService.sendThroughputs(throughputStats);\n        }\n    }\n\n    public void setStartTime(Identity identity, Long startTime) {\n        // 初始一下startTime\n        loadStatsTracker.getStat(identity).setStartTime(startTime);\n    }\n\n    // ================= setter / getter ================\n\n    public void setStatisticsClientService(StatisticsClientService statisticsClientService) {\n        this.statisticsClientService = statisticsClientService;\n    }\n\n    public void setDataBatchLoader(DataBatchLoader dataBatchLoader) {\n        this.dataBatchLoader = dataBatchLoader;\n    }\n\n    public void setLoadStatsTracker(LoadStatsTracker loadStatsTracker) {\n        this.loadStatsTracker = loadStatsTracker;\n    }\n\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/load/loader/db/DataBatchLoader.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.load.loader.db;\n\nimport java.io.File;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.Callable;\nimport java.util.concurrent.ExecutionException;\nimport java.util.concurrent.ExecutorCompletionService;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Future;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.slf4j.MDC;\nimport org.springframework.beans.BeansException;\nimport org.springframework.beans.factory.BeanFactory;\nimport org.springframework.beans.factory.BeanFactoryAware;\nimport org.springframework.util.CollectionUtils;\n\nimport com.alibaba.otter.node.common.config.ConfigClientService;\nimport com.alibaba.otter.node.etl.OtterConstants;\nimport com.alibaba.otter.node.etl.load.exception.LoadException;\nimport com.alibaba.otter.node.etl.load.loader.LoadContext;\nimport com.alibaba.otter.node.etl.load.loader.OtterLoader;\nimport com.alibaba.otter.node.etl.load.loader.db.context.DbLoadContext;\nimport com.alibaba.otter.node.etl.load.loader.db.context.FileLoadContext;\nimport com.alibaba.otter.node.etl.load.loader.interceptor.LoadInterceptor;\nimport com.alibaba.otter.node.etl.load.loader.weight.WeightController;\nimport com.alibaba.otter.shared.common.model.config.ConfigHelper;\nimport com.alibaba.otter.shared.common.model.config.data.DataMedia;\nimport com.alibaba.otter.shared.common.model.config.data.DataMediaSource;\nimport com.alibaba.otter.shared.etl.model.DbBatch;\nimport com.alibaba.otter.shared.etl.model.EventData;\nimport com.alibaba.otter.shared.etl.model.FileBatch;\nimport com.alibaba.otter.shared.etl.model.Identity;\nimport com.alibaba.otter.shared.etl.model.RowBatch;\nimport com.google.common.base.Function;\nimport com.google.common.collect.OtterMigrateMap;\n\n/**\n * 针对RowData的数据载入实现\n * \n * @author jianghang 2011-10-27 上午11:15:48\n * @version 4.0.0\n */\npublic class DataBatchLoader implements OtterLoader<DbBatch, List<LoadContext>>, BeanFactoryAware {\n\n    private static final Logger logger = LoggerFactory.getLogger(DataBatchLoader.class);\n    private ExecutorService     executorService;\n    private BeanFactory         beanFactory;\n    private ConfigClientService configClientService;\n    private LoadInterceptor     dbInterceptor;\n\n    public List<LoadContext> load(DbBatch data) {\n        final RowBatch rowBatch = data.getRowBatch();\n        final FileBatch fileBatch = data.getFileBatch();\n        boolean existFileBatch = (rowBatch != null && !CollectionUtils.isEmpty(fileBatch.getFiles()) && data.getRoot() != null);\n        boolean existRowBatch = (rowBatch != null && !CollectionUtils.isEmpty(rowBatch.getDatas()));\n\n        int count = 0;\n        List<RowBatch> rowBatchs = null;\n        if (existRowBatch) {\n            rowBatchs = split(rowBatch); // 根据介质内容进行分类合并，每个介质一个载入通道\n            count += rowBatchs.size();\n        }\n\n        if (existFileBatch) {\n            count += 1;\n        }\n\n        WeightController controller = new WeightController(count);\n        List<Future> futures = new ArrayList<Future>();\n        ExecutorCompletionService completionService = new ExecutorCompletionService(executorService);\n\n        if (existFileBatch) {\n            submitFileBatch(futures, completionService, fileBatch, data.getRoot(), controller);\n        }\n\n        if (existRowBatch) {\n            submitRowBatch(futures, completionService, rowBatchs, controller);\n        }\n\n        // 先获取一下异步处理的结果，记录一下出错的index\n        List<LoadContext> processedContexts = new ArrayList<LoadContext>();\n        int index = 0;\n        LoadException exception = null;\n        while (index < futures.size()) {\n            try {\n                Future future = completionService.take();// 它也可能被打断\n                future.get();\n            } catch (InterruptedException e) {\n                exception = new LoadException(e);\n                break;\n            } catch (ExecutionException e) {\n                exception = new LoadException(e);\n                break;\n            }\n\n            index++;\n        }\n\n        // 任何一个线程返回，出现了异常，就退出整个调度\n        if (index < futures.size()) {// 小于代表有错误，需要对未完成的记录进行cancel操作，对已完成的结果进行收集，做重复录入过滤记录\n            for (int errorIndex = 0; errorIndex < futures.size(); errorIndex++) {\n                Future future = futures.get(errorIndex);\n                if (future.isDone()) {\n                    try {\n                        LoadContext loadContext = (LoadContext) future.get();\n\n                        if (loadContext instanceof DbLoadContext) {\n                            dbInterceptor.error((DbLoadContext) loadContext);// 做一下出错处理，记录到store中\n                        }\n                    } catch (InterruptedException e) {\n                        // ignore\n                    } catch (ExecutionException e) {\n                        // ignore\n                    } catch (Exception e) {\n                        logger.error(\"interceptor process error failed\", e);\n                    }\n\n                } else {\n                    future.cancel(true); // 对未完成的进行取消\n                }\n            }\n        } else {\n            for (int i = 0; i < futures.size(); i++) {// 收集一下正确处理完成的结果\n                Future future = futures.get(i);\n                try {\n                    LoadContext loadContext = (LoadContext) future.get();\n\n                    if (loadContext instanceof DbLoadContext) {\n                        processedContexts.add((DbLoadContext) loadContext);\n                    }\n                } catch (InterruptedException e) {\n                    // ignore\n                } catch (ExecutionException e) {\n                    // ignore\n                }\n            }\n        }\n\n        if (exception != null) {\n            throw exception;\n        } else {\n            return processedContexts;\n        }\n    }\n\n    private void submitFileBatch(List<Future> futures, ExecutorCompletionService completionService,\n                                 final FileBatch fileBatch, final File rootDir, final WeightController controller) {\n        futures.add(completionService.submit(new Callable<FileLoadContext>() {\n\n            public FileLoadContext call() throws Exception {\n                try {\n                    MDC.put(OtterConstants.splitPipelineLogFileKey,\n                            String.valueOf(fileBatch.getIdentity().getPipelineId()));\n\n                    FileLoadAction fileLoadAction = (FileLoadAction) beanFactory.getBean(\"fileLoadAction\",\n                                                                                         FileLoadAction.class);\n                    return fileLoadAction.load(fileBatch, rootDir, controller);\n                } finally {\n                    MDC.remove(OtterConstants.splitPipelineLogFileKey);\n                }\n            }\n        }));\n    }\n\n    private void submitRowBatch(List<Future> futures, ExecutorCompletionService completionService,\n                                final List<RowBatch> rowBatchs, final WeightController controller) {\n        for (final RowBatch rowBatch : rowBatchs) {\n            // 提交多个并行加载通道\n            futures.add(completionService.submit(new Callable<DbLoadContext>() {\n\n                public DbLoadContext call() throws Exception {\n                    try {\n                        MDC.put(OtterConstants.splitPipelineLogFileKey,\n                                String.valueOf(rowBatch.getIdentity().getPipelineId()));\n                        // dbLoadAction是一个pool池化对象\n                        DbLoadAction dbLoadAction = (DbLoadAction) beanFactory.getBean(\"dbLoadAction\",\n                                                                                       DbLoadAction.class);\n                        return dbLoadAction.load(rowBatch, controller);\n                    } finally {\n                        MDC.remove(OtterConstants.splitPipelineLogFileKey);\n                    }\n                }\n            }));\n        }\n    }\n\n    /**\n     * 将rowBatch中的记录，按找载入的目标数据源进行分类\n     */\n    private List<RowBatch> split(RowBatch rowBatch) {\n        final Identity identity = rowBatch.getIdentity();\n        Map<DataMediaSource, RowBatch> result = OtterMigrateMap.makeComputingMap(new Function<DataMediaSource, RowBatch>() {\n\n            public RowBatch apply(DataMediaSource input) {\n                RowBatch rowBatch = new RowBatch();\n                rowBatch.setIdentity(identity);\n                return rowBatch;\n            }\n        });\n\n        for (EventData eventData : rowBatch.getDatas()) {\n            // 获取介质信息\n            DataMedia media = ConfigHelper.findDataMedia(configClientService.findPipeline(identity.getPipelineId()),\n                                                         eventData.getTableId());\n            result.get(media.getSource()).merge(eventData); // 归类\n        }\n\n        return new ArrayList<RowBatch>(result.values());\n    }\n\n    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {\n        this.beanFactory = beanFactory;\n    }\n\n    public void setExecutorService(ExecutorService executorService) {\n        this.executorService = executorService;\n    }\n\n    public void setConfigClientService(ConfigClientService configClientService) {\n        this.configClientService = configClientService;\n    }\n\n    public void setDbInterceptor(LoadInterceptor dbInterceptor) {\n        this.dbInterceptor = dbInterceptor;\n    }\n\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/load/loader/db/DbLoadAction.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.load.loader.db;\n\nimport java.sql.PreparedStatement;\nimport java.sql.SQLException;\nimport java.sql.Statement;\nimport java.sql.Types;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.ArrayBlockingQueue;\nimport java.util.concurrent.Callable;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Future;\nimport java.util.concurrent.ThreadPoolExecutor;\nimport java.util.concurrent.TimeUnit;\n\nimport org.apache.commons.lang.StringUtils;\nimport org.apache.commons.lang.exception.ExceptionUtils;\nimport org.apache.ddlutils.model.Column;\nimport org.apache.ddlutils.model.Table;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.DisposableBean;\nimport org.springframework.beans.factory.InitializingBean;\nimport org.springframework.dao.DataAccessException;\nimport org.springframework.dao.DataIntegrityViolationException;\nimport org.springframework.dao.DeadlockLoserDataAccessException;\nimport org.springframework.jdbc.core.BatchPreparedStatementSetter;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.jdbc.core.PreparedStatementSetter;\nimport org.springframework.jdbc.core.StatementCallback;\nimport org.springframework.jdbc.core.StatementCreatorUtils;\nimport org.springframework.jdbc.support.lob.LobCreator;\nimport org.springframework.transaction.TransactionStatus;\nimport org.springframework.transaction.support.TransactionCallback;\nimport org.springframework.util.Assert;\nimport org.springframework.util.CollectionUtils;\n\nimport com.alibaba.otter.node.common.config.ConfigClientService;\nimport com.alibaba.otter.node.etl.common.db.dialect.DbDialect;\nimport com.alibaba.otter.node.etl.common.db.dialect.DbDialectFactory;\nimport com.alibaba.otter.node.etl.common.db.dialect.mysql.MysqlDialect;\nimport com.alibaba.otter.node.etl.common.db.utils.SqlUtils;\nimport com.alibaba.otter.node.etl.load.exception.LoadException;\nimport com.alibaba.otter.node.etl.load.loader.LoadStatsTracker;\nimport com.alibaba.otter.node.etl.load.loader.LoadStatsTracker.LoadCounter;\nimport com.alibaba.otter.node.etl.load.loader.LoadStatsTracker.LoadThroughput;\nimport com.alibaba.otter.node.etl.load.loader.db.DbLoadData.TableLoadData;\nimport com.alibaba.otter.node.etl.load.loader.db.context.DbLoadContext;\nimport com.alibaba.otter.node.etl.load.loader.interceptor.LoadInterceptor;\nimport com.alibaba.otter.node.etl.load.loader.weight.WeightBuckets;\nimport com.alibaba.otter.node.etl.load.loader.weight.WeightController;\nimport com.alibaba.otter.shared.common.model.config.ConfigHelper;\nimport com.alibaba.otter.shared.common.model.config.channel.Channel;\nimport com.alibaba.otter.shared.common.model.config.data.DataMedia;\nimport com.alibaba.otter.shared.common.model.config.data.DataMediaPair;\nimport com.alibaba.otter.shared.common.model.config.data.db.DbMediaSource;\nimport com.alibaba.otter.shared.common.model.config.pipeline.Pipeline;\nimport com.alibaba.otter.shared.common.utils.thread.NamedThreadFactory;\nimport com.alibaba.otter.shared.etl.model.EventColumn;\nimport com.alibaba.otter.shared.etl.model.EventData;\nimport com.alibaba.otter.shared.etl.model.EventType;\nimport com.alibaba.otter.shared.etl.model.Identity;\nimport com.alibaba.otter.shared.etl.model.RowBatch;\n\n/**\n * 数据库load的执行入口\n * \n * @author jianghang 2011-10-31 下午03:17:43\n * @version 4.0.0\n */\npublic class DbLoadAction implements InitializingBean, DisposableBean {\n\n    private static final Logger logger             = LoggerFactory.getLogger(DbLoadAction.class);\n    private static final String WORKER_NAME        = \"DbLoadAction\";\n    private static final String WORKER_NAME_FORMAT = \"pipelineId = %s , pipelineName = %s , \" + WORKER_NAME;\n    private static final int    DEFAULT_POOL_SIZE  = 5;\n    private int                 poolSize           = DEFAULT_POOL_SIZE;\n    private int                 retry              = 3;\n    private int                 retryWait          = 3000;\n    private LoadInterceptor     interceptor;\n    private ExecutorService     executor;\n    private DbDialectFactory    dbDialectFactory;\n    private ConfigClientService configClientService;\n    private int                 batchSize          = 50;\n    private boolean             useBatch           = true;\n    private LoadStatsTracker    loadStatsTracker;\n\n    /**\n     * 返回结果为已处理成功的记录\n     */\n    public DbLoadContext load(RowBatch rowBatch, WeightController controller) {\n        Assert.notNull(rowBatch);\n        Identity identity = rowBatch.getIdentity();\n        DbLoadContext context = buildContext(identity);\n\n        try {\n            List<EventData> datas = rowBatch.getDatas();\n            context.setPrepareDatas(datas);\n            // 执行重复录入数据过滤\n            datas = context.getPrepareDatas();\n            if (datas == null || datas.size() == 0) {\n                logger.info(\"##no eventdata for load, return\");\n                return context;\n            }\n\n            // 因为所有的数据在DbBatchLoader已按照DateMediaSource进行归好类，不同数据源介质会有不同的DbLoadAction进行处理\n            // 设置media source时，只需要取第一节点的source即可\n            context.setDataMediaSource(ConfigHelper.findDataMedia(context.getPipeline(), datas.get(0).getTableId())\n                .getSource());\n            interceptor.prepare(context);\n            // 执行重复录入数据过滤\n            datas = context.getPrepareDatas();\n            // 处理下ddl语句，ddl/dml语句不可能是在同一个batch中，由canal进行控制\n            // 主要考虑ddl的幂等性问题，尽可能一个ddl一个batch，失败或者回滚都只针对这条sql\n            if (isDdlDatas(datas)) {\n                doDdl(context, datas);\n            } else {\n                WeightBuckets<EventData> buckets = buildWeightBuckets(context, datas);\n                List<Long> weights = buckets.weights();\n                controller.start(weights);// weights可能为空，也得调用start方法\n                if (CollectionUtils.isEmpty(datas)) {\n                    logger.info(\"##no eventdata for load\");\n                }\n                adjustPoolSize(context); // 根据manager配置调整线程池\n                adjustConfig(context); // 调整一下运行参数\n                // 按权重构建数据对象\n                // 处理数据\n                for (int i = 0; i < weights.size(); i++) {\n                    Long weight = weights.get(i);\n                    controller.await(weight.intValue());\n                    // 处理同一个weight下的数据\n                    List<EventData> items = buckets.getItems(weight);\n                    logger.debug(\"##start load for weight:\" + weight);\n                    // 预处理下数据\n\n                    // 进行一次数据合并，合并相同pk的多次I/U/D操作\n                    items = DbLoadMerger.merge(items);\n                    // 按I/U/D进行归并处理\n                    DbLoadData loadData = new DbLoadData();\n                    doBefore(items, context, loadData);\n                    // 执行load操作\n                    doLoad(context, loadData);\n                    controller.single(weight.intValue());\n                    logger.debug(\"##end load for weight:\" + weight);\n                }\n            }\n            interceptor.commit(context);\n        } catch (InterruptedException e) {\n            Thread.currentThread().interrupt();\n            interceptor.error(context);\n        } catch (Exception e) {\n            interceptor.error(context);\n            throw new LoadException(e);\n        }\n\n        return context;// 返回处理成功的记录\n    }\n\n    private DbLoadContext buildContext(Identity identity) {\n        DbLoadContext context = new DbLoadContext();\n        context.setIdentity(identity);\n        Channel channel = configClientService.findChannel(identity.getChannelId());\n        Pipeline pipeline = configClientService.findPipeline(identity.getPipelineId());\n        context.setChannel(channel);\n        context.setPipeline(pipeline);\n        return context;\n    }\n\n    /**\n     * 分析整个数据，将datas划分为多个批次. ddl sql前的DML并发执行，然后串行执行ddl后，再并发执行DML\n     * \n     * @return\n     */\n    private boolean isDdlDatas(List<EventData> eventDatas) {\n        boolean result = false;\n        for (EventData eventData : eventDatas) {\n            result |= eventData.getEventType().isDdl();\n            if (result && !eventData.getEventType().isDdl()) {\n                throw new LoadException(\"ddl/dml can't be in one batch, it's may be a bug , pls submit issues.\",\n                    DbLoadDumper.dumpEventDatas(eventDatas));\n            }\n        }\n\n        return result;\n    }\n\n    /**\n     * 构建基于weight权重分组的item集合列表\n     */\n    private WeightBuckets<EventData> buildWeightBuckets(DbLoadContext context, List<EventData> datas) {\n        WeightBuckets<EventData> buckets = new WeightBuckets<EventData>();\n        for (EventData data : datas) {\n            // 获取对应的weight\n            DataMediaPair pair = ConfigHelper.findDataMediaPair(context.getPipeline(), data.getPairId());\n            buckets.addItem(pair.getPushWeight(), data);\n        }\n\n        return buckets;\n    }\n\n    /**\n     * 执行数据处理，比如数据冲突检测\n     */\n    private void doBefore(List<EventData> items, final DbLoadContext context, final DbLoadData loadData) {\n        for (final EventData item : items) {\n            boolean filter = interceptor.before(context, item);\n            if (!filter) {\n                loadData.merge(item);// 进行分类\n            }\n        }\n    }\n\n    private void doLoad(final DbLoadContext context, DbLoadData loadData) {\n        // 优先处理delete,可以利用batch优化\n        List<List<EventData>> batchDatas = new ArrayList<List<EventData>>();\n        for (TableLoadData tableData : loadData.getTables()) {\n            if (useBatch) {\n                // 优先执行delete语句，针对uniqe更新，一般会进行delete + insert的处理模式，避免并发更新\n                batchDatas.addAll(split(tableData.getDeleteDatas()));\n            } else {\n                // 如果不可以执行batch，则按照单条数据进行并行提交\n                // 优先执行delete语句，针对uniqe更新，一般会进行delete + insert的处理模式，避免并发更新\n                for (EventData data : tableData.getDeleteDatas()) {\n                    batchDatas.add(Arrays.asList(data));\n                }\n            }\n        }\n\n        if (context.getPipeline().getParameters().isDryRun()) {\n            doDryRun(context, batchDatas, true);\n        } else {\n            doTwoPhase(context, batchDatas, true);\n        }\n        batchDatas.clear();\n\n        // 处理下insert/update\n        for (TableLoadData tableData : loadData.getTables()) {\n            if (useBatch) {\n                // 执行insert + update语句\n                batchDatas.addAll(split(tableData.getInsertDatas()));\n                batchDatas.addAll(split(tableData.getUpadateDatas()));// 每条记录分为一组，并行加载\n            } else {\n                // 执行insert + update语句\n                for (EventData data : tableData.getInsertDatas()) {\n                    batchDatas.add(Arrays.asList(data));\n                }\n                for (EventData data : tableData.getUpadateDatas()) {\n                    batchDatas.add(Arrays.asList(data));\n                }\n            }\n        }\n\n        if (context.getPipeline().getParameters().isDryRun()) {\n            doDryRun(context, batchDatas, true);\n        } else {\n            doTwoPhase(context, batchDatas, true);\n        }\n        batchDatas.clear();\n    }\n\n    /**\n     * 将对应的数据按照sql相同进行batch组合\n     */\n    private List<List<EventData>> split(List<EventData> datas) {\n        List<List<EventData>> result = new ArrayList<List<EventData>>();\n        if (datas == null || datas.size() == 0) {\n            return result;\n        } else {\n            int[] bits = new int[datas.size()];// 初始化一个标记，用于标明对应的记录是否已分入某个batch\n            for (int i = 0; i < bits.length; i++) {\n                // 跳过已经被分入batch的\n                while (i < bits.length && bits[i] == 1) {\n                    i++;\n                }\n\n                if (i >= bits.length) { // 已处理完成，退出\n                    break;\n                }\n\n                // 开始添加batch，最大只加入batchSize个数的对象\n                List<EventData> batch = new ArrayList<EventData>();\n                bits[i] = 1;\n                batch.add(datas.get(i));\n                for (int j = i + 1; j < bits.length && batch.size() < batchSize; j++) {\n                    if (bits[j] == 0 && canBatch(datas.get(i), datas.get(j))) {\n                        batch.add(datas.get(j));\n                        bits[j] = 1;// 修改为已加入\n                    }\n                }\n                result.add(batch);\n            }\n\n            return result;\n        }\n    }\n\n    /**\n     * 判断两条记录是否可以作为一个batch提交，主要判断sql是否相等. 可优先通过schemaName进行判断\n     */\n    private boolean canBatch(EventData source, EventData target) {\n        // return StringUtils.equals(source.getSchemaName(),\n        // target.getSchemaName())\n        // && StringUtils.equals(source.getTableName(), target.getTableName())\n        // && StringUtils.equals(source.getSql(), target.getSql());\n        // return StringUtils.equals(source.getSql(), target.getSql());\n\n        // 因为sqlTemplate构造sql时用了String.intern()的操作，保证相同字符串的引用是同一个，所以可以直接使用==进行判断，提升效率\n        return source.getSql() == target.getSql();\n    }\n\n    private void doDryRun(DbLoadContext context, List<List<EventData>> totalRows, boolean canBatch) {\n        for (List<EventData> rows : totalRows) {\n            if (CollectionUtils.isEmpty(rows)) {\n                continue; // 过滤空记录\n            }\n\n            for (EventData row : rows) {\n                processStat(row, context);// 直接记录成功状态\n            }\n\n            context.getProcessedDatas().addAll(rows);\n        }\n    }\n\n    /**\n     * 执行ddl的调用，处理逻辑比较简单: 串行调用\n     * \n     * @param context\n     * @param eventDatas\n     */\n    private void doDdl(DbLoadContext context, List<EventData> eventDatas) {\n        for (final EventData data : eventDatas) {\n            DataMedia dataMedia = ConfigHelper.findDataMedia(context.getPipeline(), data.getTableId());\n            final DbDialect dbDialect = dbDialectFactory.getDbDialect(context.getIdentity().getPipelineId(),\n                (DbMediaSource) dataMedia.getSource());\n            Boolean skipDdlException = context.getPipeline().getParameters().getSkipDdlException();\n            try {\n                Boolean result = dbDialect.getJdbcTemplate().execute(new StatementCallback<Boolean>() {\n\n                    public Boolean doInStatement(Statement stmt) throws SQLException, DataAccessException {\n                        Boolean result = true;\n                        if (dbDialect instanceof MysqlDialect && StringUtils.isNotEmpty(data.getDdlSchemaName())) {\n                            // 如果mysql，执行ddl时，切换到在源库执行的schema上\n                            // result &= stmt.execute(\"use \" +\n                            // data.getDdlSchemaName());\n\n                            // 解决当数据库名称为关键字如\"Order\"的时候,会报错,无法同步\n                            result &= stmt.execute(\"use `\" + data.getDdlSchemaName() + \"`\");\n                        }\n                        result &= stmt.execute(data.getSql());\n                        return result;\n                    }\n                });\n                if (result) {\n                    context.getProcessedDatas().add(data); // 记录为成功处理的sql\n                } else {\n                    context.getFailedDatas().add(data);\n                }\n\n            } catch (Throwable e) {\n                if (skipDdlException) {\n                    // do skip\n                    logger.warn(\"skip exception for ddl : {} , caused by {}\", data, ExceptionUtils.getFullStackTrace(e));\n                } else {\n                    throw new LoadException(e);\n                }\n            }\n\n        }\n    }\n\n    /**\n     * 首先进行并行执行，出错后转为串行执行\n     */\n    private void doTwoPhase(DbLoadContext context, List<List<EventData>> totalRows, boolean canBatch) {\n        // 预处理下数据\n        List<Future<Exception>> results = new ArrayList<Future<Exception>>();\n        for (List<EventData> rows : totalRows) {\n            if (CollectionUtils.isEmpty(rows)) {\n                continue; // 过滤空记录\n            }\n\n            results.add(executor.submit(new DbLoadWorker(context, rows, canBatch)));\n        }\n\n        boolean partFailed = false;\n        for (int i = 0; i < results.size(); i++) {\n            Future<Exception> result = results.get(i);\n            Exception ex = null;\n            try {\n                ex = result.get();\n                for (EventData data : totalRows.get(i)) {\n                    interceptor.after(context, data);// 通知加载完成\n                }\n            } catch (Exception e) {\n                ex = e;\n            }\n\n            if (ex != null) {\n                logger.warn(\"##load phase one failed!\", ex);\n                partFailed = true;\n            }\n        }\n\n        if (true == partFailed) {\n            // if (CollectionUtils.isEmpty(context.getFailedDatas())) {\n            // logger.error(\"##load phase one failed but failedDatas is empty!\");\n            // return;\n            // }\n\n            // 尝试的内容换成phase one跑的所有数据，避免因failed datas计算错误而导致丢数据\n            List<EventData> retryEventDatas = new ArrayList<EventData>();\n            for (List<EventData> rows : totalRows) {\n                retryEventDatas.addAll(rows);\n            }\n\n            context.getFailedDatas().clear(); // 清理failed data数据\n\n            // 可能为null，manager老版本数据序列化传输时，因为数据库中没有skipLoadException变量配置\n            Boolean skipLoadException = context.getPipeline().getParameters().getSkipLoadException();\n            if (skipLoadException != null && skipLoadException) {// 如果设置为允许跳过单条异常，则一条条执行数据load，准确过滤掉出错的记录，并进行日志记录\n                for (EventData retryEventData : retryEventDatas) {\n                    DbLoadWorker worker = new DbLoadWorker(context, Arrays.asList(retryEventData), false);// 强制设置batch为false\n                    try {\n                        Exception ex = worker.call();\n                        if (ex != null) {\n                            // do skip\n                            logger.warn(\"skip exception for data : {} , caused by {}\",\n                                retryEventData,\n                                ExceptionUtils.getFullStackTrace(ex));\n                        }\n                    } catch (Exception ex) {\n                        // do skip\n                        logger.warn(\"skip exception for data : {} , caused by {}\",\n                            retryEventData,\n                            ExceptionUtils.getFullStackTrace(ex));\n                    }\n                }\n            } else {\n                // 直接一批进行处理，减少线程调度\n                DbLoadWorker worker = new DbLoadWorker(context, retryEventDatas, false);// 强制设置batch为false\n                try {\n                    Exception ex = worker.call();\n                    if (ex != null) {\n                        throw ex; // 自己抛自己接\n                    }\n                } catch (Exception ex) {\n                    logger.error(\"##load phase two failed!\", ex);\n                    throw new LoadException(ex);\n                }\n            }\n\n            // 清理failed data数据\n            for (EventData data : retryEventDatas) {\n                interceptor.after(context, data);// 通知加载完成\n            }\n        }\n\n    }\n\n    // 调整一下线程池\n    private void adjustPoolSize(DbLoadContext context) {\n        Pipeline pipeline = context.getPipeline();\n        int newPoolSize = pipeline.getParameters().getLoadPoolSize();\n        if (newPoolSize != poolSize) {\n            poolSize = newPoolSize;\n            if (executor instanceof ThreadPoolExecutor) {\n                ThreadPoolExecutor pool = (ThreadPoolExecutor) executor;\n                pool.setCorePoolSize(newPoolSize);\n                pool.setMaximumPoolSize(newPoolSize);\n            }\n        }\n    }\n\n    private void adjustConfig(DbLoadContext context) {\n        Pipeline pipeline = context.getPipeline();\n        this.useBatch = pipeline.getParameters().isUseBatch();\n\n        // 旧版本manager配置序列化传输时可能无此配置项，因此只有专门配置过的，才进行调整\n        Integer loadBatchsize = pipeline.getParameters().getLoadBatchsize();\n        if (loadBatchsize != null && loadBatchsize > 0){\n            this.batchSize = loadBatchsize;\n        }\n    }\n\n    public void afterPropertiesSet() throws Exception {\n        executor = new ThreadPoolExecutor(poolSize,\n            poolSize,\n            0L,\n            TimeUnit.MILLISECONDS,\n            new ArrayBlockingQueue(poolSize * 4),\n            new NamedThreadFactory(WORKER_NAME),\n            new ThreadPoolExecutor.CallerRunsPolicy());\n    }\n\n    public void destroy() throws Exception {\n        executor.shutdownNow();\n    }\n\n    enum ExecuteResult {\n        SUCCESS, ERROR, RETRY\n    }\n\n    class DbLoadWorker implements Callable<Exception> {\n\n        private DbLoadContext   context;\n        private DbDialect       dbDialect;\n        private List<EventData> datas;\n        private boolean         canBatch;\n        private List<EventData> allFailedDatas   = new ArrayList<EventData>();\n        private List<EventData> allProcesedDatas = new ArrayList<EventData>();\n        private List<EventData> processedDatas   = new ArrayList<EventData>();\n        private List<EventData> failedDatas      = new ArrayList<EventData>();\n\n        public DbLoadWorker(DbLoadContext context, List<EventData> datas, boolean canBatch){\n            this.context = context;\n            this.datas = datas;\n            this.canBatch = canBatch;\n\n            EventData data = datas.get(0); // eventData为同一数据库的记录，只取第一条即可\n            DataMedia dataMedia = ConfigHelper.findDataMedia(context.getPipeline(), data.getTableId());\n            dbDialect = dbDialectFactory.getDbDialect(context.getIdentity().getPipelineId(),\n                (DbMediaSource) dataMedia.getSource());\n\n        }\n\n        public Exception call() throws Exception {\n            try {\n                Thread.currentThread().setName(String.format(WORKER_NAME_FORMAT,\n                    context.getPipeline().getId(),\n                    context.getPipeline().getName()));\n                return doCall();\n            } finally {\n                Thread.currentThread().setName(WORKER_NAME);\n            }\n        }\n\n        private Exception doCall() {\n            RuntimeException error = null;\n            ExecuteResult exeResult = null;\n            int index = 0;// 记录下处理成功的记录下标\n            for (; index < datas.size();) {\n                // 处理数据切分\n                final List<EventData> splitDatas = new ArrayList<EventData>();\n                if (useBatch && canBatch) {\n                    int end = (index + batchSize > datas.size()) ? datas.size() : (index + batchSize);\n                    splitDatas.addAll(datas.subList(index, end));\n                    index = end;// 移动到下一批次\n                } else {\n                    splitDatas.add(datas.get(index));\n                    index = index + 1;// 移动到下一条\n                }\n\n                int retryCount = 0;\n                while (true) {\n                    try {\n                        if (CollectionUtils.isEmpty(failedDatas) == false) {\n                            splitDatas.clear();\n                            splitDatas.addAll(failedDatas); // 下次重试时，只处理错误的记录\n                        } else {\n                            failedDatas.addAll(splitDatas); // 先添加为出错记录，可能获取lob,datasource会出错\n                        }\n\n                        final LobCreator lobCreator = dbDialect.getLobHandler().getLobCreator();\n                        if (useBatch && canBatch) {\n                            // 处理batch\n                            final String sql = splitDatas.get(0).getSql();\n                            int[] affects = new int[splitDatas.size()];\n                            affects = (int[]) dbDialect.getTransactionTemplate().execute(new TransactionCallback() {\n\n                                public Object doInTransaction(TransactionStatus status) {\n                                    // 初始化一下内容\n                                    try {\n                                        failedDatas.clear(); // 先清理\n                                        processedDatas.clear();\n                                        interceptor.transactionBegin(context, splitDatas, dbDialect);\n                                        JdbcTemplate template = dbDialect.getJdbcTemplate();\n                                        int[] affects = template.batchUpdate(sql, new BatchPreparedStatementSetter() {\n\n                                            public void setValues(PreparedStatement ps, int idx) throws SQLException {\n                                                doPreparedStatement(ps, dbDialect, lobCreator, splitDatas.get(idx));\n                                            }\n\n                                            public int getBatchSize() {\n                                                return splitDatas.size();\n                                            }\n                                        });\n                                        interceptor.transactionEnd(context, splitDatas, dbDialect);\n                                        return affects;\n                                    } finally {\n                                        lobCreator.close();\n                                    }\n                                }\n\n                            });\n\n                            // 更新统计信息\n                            for (int i = 0; i < splitDatas.size(); i++) {\n                                processStat(splitDatas.get(i), affects[i], true);\n                            }\n                        } else {\n                            final EventData data = splitDatas.get(0);// 直接取第一条\n                            int affect = 0;\n                            affect = (Integer) dbDialect.getTransactionTemplate().execute(new TransactionCallback() {\n\n                                public Object doInTransaction(TransactionStatus status) {\n                                    try {\n                                        failedDatas.clear(); // 先清理\n                                        processedDatas.clear();\n                                        interceptor.transactionBegin(context, Arrays.asList(data), dbDialect);\n                                        JdbcTemplate template = dbDialect.getJdbcTemplate();\n                                        int affect = template.update(data.getSql(), new PreparedStatementSetter() {\n\n                                            public void setValues(PreparedStatement ps) throws SQLException {\n                                                doPreparedStatement(ps, dbDialect, lobCreator, data);\n                                            }\n                                        });\n                                        interceptor.transactionEnd(context, Arrays.asList(data), dbDialect);\n                                        return affect;\n                                    } finally {\n                                        lobCreator.close();\n                                    }\n                                }\n                            });\n                            // 更新统计信息\n                            processStat(data, affect, false);\n                        }\n\n                        error = null;\n                        exeResult = ExecuteResult.SUCCESS;\n                    } catch (DeadlockLoserDataAccessException ex) {\n                        error = new LoadException(ExceptionUtils.getFullStackTrace(ex),\n                            DbLoadDumper.dumpEventDatas(splitDatas));\n                        exeResult = ExecuteResult.RETRY;\n                    } catch (DataIntegrityViolationException ex) {\n                        error = new LoadException(ExceptionUtils.getFullStackTrace(ex),\n                            DbLoadDumper.dumpEventDatas(splitDatas));\n                        // if (StringUtils.contains(ex.getMessage(),\n                        // \"ORA-00001\")) {\n                        // exeResult = ExecuteResult.RETRY;\n                        // } else {\n                        // exeResult = ExecuteResult.ERROR;\n                        // }\n                        exeResult = ExecuteResult.ERROR;\n                    } catch (RuntimeException ex) {\n                        error = new LoadException(ExceptionUtils.getFullStackTrace(ex),\n                            DbLoadDumper.dumpEventDatas(splitDatas));\n                        exeResult = ExecuteResult.ERROR;\n                    } catch (Throwable ex) {\n                        error = new LoadException(ExceptionUtils.getFullStackTrace(ex),\n                            DbLoadDumper.dumpEventDatas(splitDatas));\n                        exeResult = ExecuteResult.ERROR;\n                    }\n\n                    if (ExecuteResult.SUCCESS == exeResult) {\n                        allFailedDatas.addAll(failedDatas);// 记录一下异常到all记录中\n                        allProcesedDatas.addAll(processedDatas);\n                        failedDatas.clear();// 清空上一轮的处理\n                        processedDatas.clear();\n                        break; // do next eventData\n                    } else if (ExecuteResult.RETRY == exeResult) {\n                        retryCount = retryCount + 1;// 计数一次\n                        // 出现异常，理论上当前的批次都会失败\n                        processedDatas.clear();\n                        failedDatas.clear();\n                        failedDatas.addAll(splitDatas);\n                        if (retryCount >= retry) {\n                            processFailedDatas(index);// 重试已结束，添加出错记录并退出\n                            throw new LoadException(String.format(\"execute [%s] retry %s times failed\",\n                                context.getIdentity().toString(),\n                                retryCount), error);\n                        } else {\n                            try {\n                                int wait = retryCount * retryWait;\n                                wait = (wait < retryWait) ? retryWait : wait;\n                                Thread.sleep(wait);\n                            } catch (InterruptedException ex) {\n                                Thread.interrupted();\n                                processFailedDatas(index);// 局部处理出错了\n                                throw new LoadException(ex);\n                            }\n                        }\n                    } else {\n                        // 出现异常，理论上当前的批次都会失败\n                        processedDatas.clear();\n                        failedDatas.clear();\n                        failedDatas.addAll(splitDatas);\n                        processFailedDatas(index);// 局部处理出错了\n                        throw error;\n                    }\n                }\n            }\n\n            // 记录一下当前处理过程中失败的记录,affect = 0的记录\n            context.getFailedDatas().addAll(allFailedDatas);\n            context.getProcessedDatas().addAll(allProcesedDatas);\n            return null;\n        }\n\n        private void doPreparedStatement(PreparedStatement ps, DbDialect dbDialect, LobCreator lobCreator,\n                                         EventData data) throws SQLException {\n            EventType type = data.getEventType();\n            // 注意insert/update语句对应的字段数序都是将主键排在后面\n            List<EventColumn> columns = new ArrayList<EventColumn>();\n            if (type.isInsert()) {\n                columns.addAll(data.getColumns()); // insert为所有字段\n                columns.addAll(data.getKeys());\n            } else if (type.isDelete()) {\n                columns.addAll(data.getKeys());\n            } else if (type.isUpdate()) {\n                boolean existOldKeys = !CollectionUtils.isEmpty(data.getOldKeys());\n                columns.addAll(data.getUpdatedColumns());// 只更新带有isUpdate=true的字段\n                if (existOldKeys && dbDialect.isDRDS()) {\n                    // DRDS需要区分主键是否有变更\n                    columns.addAll(data.getUpdatedKeys());\n                } else {\n                    columns.addAll(data.getKeys());\n                }\n                if (existOldKeys) {\n                    columns.addAll(data.getOldKeys());\n                }\n            }\n\n            // 获取一下当前字段名的数据是否必填\n            Table table = dbDialect.findTable(data.getSchemaName(), data.getTableName());\n            Map<String, Boolean> isRequiredMap = new HashMap<String, Boolean>();\n            for (Column tableColumn : table.getColumns()) {\n                isRequiredMap.put(StringUtils.lowerCase(tableColumn.getName()), tableColumn.isRequired());\n            }\n\n            for (int i = 0; i < columns.size(); i++) {\n                int paramIndex = i + 1;\n                EventColumn column = columns.get(i);\n                int sqlType = column.getColumnType();\n\n                Boolean isRequired = isRequiredMap.get(StringUtils.lowerCase(column.getColumnName()));\n                if (isRequired == null) {\n                    // 清理一下目标库的表结构,二次检查一下\n                    table = dbDialect.findTable(data.getSchemaName(), data.getTableName(), false);\n                    isRequiredMap = new HashMap<String, Boolean>();\n                    for (Column tableColumn : table.getColumns()) {\n                        isRequiredMap.put(StringUtils.lowerCase(tableColumn.getName()), tableColumn.isRequired());\n                    }\n\n                    isRequired = isRequiredMap.get(StringUtils.lowerCase(column.getColumnName()));\n                    if (isRequired == null) {\n                        throw new LoadException(String.format(\"column name %s is not found in Table[%s]\",\n                            column.getColumnName(),\n                            table.toString()));\n                    }\n                }\n\n                Object param = null;\n                if (dbDialect instanceof MysqlDialect\n                    && (sqlType == Types.TIME || sqlType == Types.TIMESTAMP || sqlType == Types.DATE)) {\n                    // 解决mysql的0000-00-00 00:00:00问题，直接依赖mysql\n                    // driver进行处理，如果转化为Timestamp会出错\n                    param = column.getColumnValue();\n                } else {\n                    param = SqlUtils.stringToSqlValue(column.getColumnValue(),\n                        sqlType,\n                        isRequired,\n                        dbDialect.isEmptyStringNulled());\n                }\n\n                try {\n                    switch (sqlType) {\n                        case Types.CLOB:\n                            lobCreator.setClobAsString(ps, paramIndex, (String) param);\n                            break;\n\n                        case Types.BLOB:\n                            lobCreator.setBlobAsBytes(ps, paramIndex, (byte[]) param);\n                            break;\n                        case Types.TIME:\n                        case Types.TIMESTAMP:\n                        case Types.DATE:\n                            // 只处理mysql的时间类型，oracle的进行转化处理\n                            if (dbDialect instanceof MysqlDialect) {\n                                // 解决mysql的0000-00-00 00:00:00问题，直接依赖mysql\n                                // driver进行处理，如果转化为Timestamp会出错\n                                ps.setObject(paramIndex, param);\n                            } else {\n                                StatementCreatorUtils.setParameterValue(ps, paramIndex, sqlType, null, param);\n                            }\n                            break;\n                        case Types.BIT:\n                            // 只处理mysql的bit类型，bit最多存储64位，所以需要使用BigInteger进行处理才能不丢精度\n                            // mysql driver将bit按照setInt进行处理，会导致数据越界\n                            if (dbDialect instanceof MysqlDialect) {\n                                StatementCreatorUtils.setParameterValue(ps, paramIndex, Types.DECIMAL, null, param);\n                            } else {\n                                StatementCreatorUtils.setParameterValue(ps, paramIndex, sqlType, null, param);\n                            }\n                            break;\n                        default:\n                            StatementCreatorUtils.setParameterValue(ps, paramIndex, sqlType, null, param);\n                            break;\n                    }\n                } catch (SQLException ex) {\n                    logger.error(\"## SetParam error , [pairId={}, sqltype={}, value={}]\",\n                        new Object[] { data.getPairId(), sqlType, param });\n                    throw ex;\n                }\n            }\n        }\n\n        private void processStat(EventData data, int affect, boolean batch) {\n            if (batch && (affect < 1 && affect != Statement.SUCCESS_NO_INFO)) {\n                failedDatas.add(data); // 记录到错误的临时队列，进行重试处理\n            } else if (!batch && affect < 1) {\n                failedDatas.add(data);// 记录到错误的临时队列，进行重试处理\n            } else {\n                processedDatas.add(data); // 记录到成功的临时队列，commit也可能会失败。所以这记录也可能需要进行重试\n                DbLoadAction.this.processStat(data, context);\n            }\n        }\n\n        // 出现异常回滚了，记录一下异常记录\n        private void processFailedDatas(int index) {\n            allFailedDatas.addAll(failedDatas);// 添加失败记录\n            context.getFailedDatas().addAll(allFailedDatas);// 添加历史出错记录\n            for (; index < datas.size(); index++) { // 记录一下未处理的数据\n                context.getFailedDatas().add(datas.get(index));\n            }\n            // 这里不需要添加当前成功记录，出现异常后会rollback所有的成功记录，比如processDatas有记录，但在commit出现失败\n            // (bugfix)\n            allProcesedDatas.addAll(processedDatas);\n            context.getProcessedDatas().addAll(allProcesedDatas);// 添加历史成功记录\n        }\n\n    }\n\n    private void processStat(EventData data, DbLoadContext context) {\n        LoadThroughput throughput = loadStatsTracker.getStat(context.getIdentity());\n        LoadCounter counter = throughput.getStat(data.getPairId());\n        EventType type = data.getEventType();\n        if (type.isInsert()) {\n            counter.getInsertCount().incrementAndGet();\n        } else if (type.isUpdate()) {\n            counter.getUpdateCount().incrementAndGet();\n        } else if (type.isDelete()) {\n            counter.getDeleteCount().incrementAndGet();\n        }\n\n        counter.getRowCount().incrementAndGet();\n        counter.getRowSize().addAndGet(calculateSize(data));\n    }\n\n    // 大致估算一下row记录的大小\n    private long calculateSize(EventData data) {\n        // long size = 0L;\n        // size += data.getKeys().toString().getBytes().length - 12 -\n        // data.getKeys().size() + 1L;\n        // size += data.getColumns().toString().getBytes().length - 12 -\n        // data.getKeys().size() + 1L;\n        // return size;\n\n        // byte[] bytes = JsonUtils.marshalToByte(data);// 走序列化的方式快速计算一下大小\n        // return bytes.length;\n\n        return data.getSize();// 数据不做计算，避免影响性能\n    }\n\n    // =============== setter / getter ===============\n\n    public void setPoolSize(int poolSize) {\n        this.poolSize = poolSize;\n    }\n\n    public void setRetry(int retry) {\n        this.retry = retry;\n    }\n\n    public void setRetryWait(int retryWait) {\n        this.retryWait = retryWait;\n    }\n\n    public void setInterceptor(LoadInterceptor interceptor) {\n        this.interceptor = interceptor;\n    }\n\n    public void setDbDialectFactory(DbDialectFactory dbDialectFactory) {\n        this.dbDialectFactory = dbDialectFactory;\n    }\n\n    public void setConfigClientService(ConfigClientService configClientService) {\n        this.configClientService = configClientService;\n    }\n\n    public void setLoadStatsTracker(LoadStatsTracker loadStatsTracker) {\n        this.loadStatsTracker = loadStatsTracker;\n    }\n\n    public void setUseBatch(boolean useBatch) {\n        this.useBatch = useBatch;\n    }\n\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/load/loader/db/DbLoadData.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.load.loader.db;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport com.alibaba.otter.shared.etl.model.EventData;\nimport com.alibaba.otter.shared.etl.model.EventType;\n\n/**\n * 将同一个weight下的EventData进行数据归类,按表和insert/update/delete类型进行分类\n * \n * <pre>\n * 归类用途：对insert语句进行batch优化\n * 1. mysql索引的限制，需要避免insert并发执行\n * </pre>\n * \n * @author jianghang 2011-11-9 下午04:28:35\n * @version 4.0.0\n */\npublic class DbLoadData {\n\n    private List<TableLoadData> tables = new ArrayList<TableLoadData>();\n\n    public DbLoadData(){\n        // nothing\n    }\n\n    public DbLoadData(List<EventData> datas){\n        for (EventData data : datas) {\n            merge(data);\n        }\n    }\n\n    public void merge(EventData data) {\n        TableLoadData tableData = findTableData(data.getTableId());\n\n        EventType type = data.getEventType();\n        if (type.isInsert()) {\n            tableData.getInsertDatas().add(data);\n        } else if (type.isUpdate()) {\n            tableData.getUpadateDatas().add(data);\n        } else if (type.isDelete()) {\n            tableData.getDeleteDatas().add(data);\n        }\n    }\n\n    public List<TableLoadData> getTables() {\n        return tables;\n    }\n\n    private synchronized TableLoadData findTableData(Long tableId) {\n        for (TableLoadData table : tables) {\n            if (table.getTableId().equals(tableId)) {\n                return table;\n            }\n        }\n\n        TableLoadData data = new TableLoadData(tableId);\n        tables.add(data);\n        return data;\n    }\n\n    /**\n     * 按table进行分类\n     */\n    public static class TableLoadData {\n\n        private Long            tableId;\n        private List<EventData> insertDatas  = new ArrayList<EventData>();\n        private List<EventData> upadateDatas = new ArrayList<EventData>();\n        private List<EventData> deleteDatas  = new ArrayList<EventData>();\n\n        public TableLoadData(Long tableId){\n            this.tableId = tableId;\n        }\n\n        public List<EventData> getInsertDatas() {\n            return insertDatas;\n        }\n\n        public void setInsertDatas(List<EventData> insertDatas) {\n            this.insertDatas = insertDatas;\n        }\n\n        public List<EventData> getUpadateDatas() {\n            return upadateDatas;\n        }\n\n        public void setUpadateDatas(List<EventData> upadateDatas) {\n            this.upadateDatas = upadateDatas;\n        }\n\n        public List<EventData> getDeleteDatas() {\n            return deleteDatas;\n        }\n\n        public void setDeleteDatas(List<EventData> deleteDatas) {\n            this.deleteDatas = deleteDatas;\n        }\n\n        public Long getTableId() {\n            return tableId;\n        }\n\n        public void setTableId(Long tableId) {\n            this.tableId = tableId;\n        }\n\n    }\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/load/loader/db/DbLoadDumper.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.load.loader.db;\n\nimport java.text.MessageFormat;\nimport java.util.List;\n\nimport org.apache.commons.lang.StringUtils;\nimport org.apache.commons.lang.SystemUtils;\nimport org.springframework.util.CollectionUtils;\n\nimport com.alibaba.otter.node.etl.load.loader.db.context.DbLoadContext;\nimport com.alibaba.otter.shared.etl.model.EventColumn;\nimport com.alibaba.otter.shared.etl.model.EventData;\n\n/**\n * dump记录\n * \n * @author jianghang 2011-11-9 下午03:52:26\n * @version 4.0.0\n */\npublic class DbLoadDumper {\n\n    private static final String SEP                    = SystemUtils.LINE_SEPARATOR;\n\n    private static String       context_format         = null;\n    private static String       eventData_format       = null;\n    private static int          event_default_capacity = 1024;                      // 预设值StringBuilder，减少扩容影响\n\n    static {\n        context_format = SEP + \"****************************************************\" + SEP;\n        context_format += \"* Identity : {0} *\" + SEP;\n        context_format += \"* total Data : [{1}] , success Data : [{2}] , failed Data : [{3}] , Interrupt : [{4}]\" + SEP;\n        context_format += \"****************************************************\" + SEP;\n        context_format += \"* process Data  *\" + SEP;\n        context_format += \"{5}\" + SEP;\n        context_format += \"****************************************************\" + SEP;\n        context_format += \"* failed Data *\" + SEP;\n        context_format += \"{6}\" + SEP;\n        context_format += \"****************************************************\" + SEP;\n\n        eventData_format = \"-----------------\" + SEP;\n        eventData_format += \"- PairId: {0} , TableId: {1} , EventType : {2} , Time : {3} \" + SEP;\n        eventData_format += \"- Consistency : {4} , Mode : {5} \" + SEP;\n        eventData_format += \"-----------------\" + SEP;\n        eventData_format += \"---Pks\" + SEP;\n        eventData_format += \"{6}\" + SEP;\n        eventData_format += \"---oldPks\" + SEP;\n        eventData_format += \"{7}\" + SEP;\n        eventData_format += \"---Columns\" + SEP;\n        eventData_format += \"{8}\" + SEP;\n        eventData_format += \"---Sql\" + SEP;\n        eventData_format += \"{9}\" + SEP;\n    }\n\n    public static String dumpContext(DbLoadContext context) {\n        int successed = context.getProcessedDatas().size();\n        int failed = context.getFailedDatas().size();\n        int all = context.getPrepareDatas().size();\n        boolean isInterrupt = (all != (failed + successed));\n        return MessageFormat.format(context_format, context.getIdentity().toString(), all, successed, failed,\n                                    isInterrupt, dumpEventDatas(context.getProcessedDatas()),\n                                    dumpEventDatas(context.getFailedDatas()));\n    }\n\n    public static String dumpEventDatas(List<EventData> eventDatas) {\n        if (CollectionUtils.isEmpty(eventDatas)) {\n            return StringUtils.EMPTY;\n        }\n\n        // 预先设定容量大小\n        StringBuilder builder = new StringBuilder(event_default_capacity * eventDatas.size());\n        for (EventData data : eventDatas) {\n            builder.append(dumpEventData(data));\n        }\n        return builder.toString();\n    }\n\n    public static String dumpEventData(EventData eventData) {\n        String type = (eventData.getEventType() != null) ? eventData.getEventType().getValue() : \"\";\n        String consistency = (eventData.getSyncConsistency() != null) ? eventData.getSyncConsistency().getValue() : \"\";\n        String mode = (eventData.getSyncMode() != null) ? eventData.getSyncMode().getValue() : \"\";\n        return MessageFormat.format(eventData_format, eventData.getPairId(), eventData.getTableId(), type,\n                                    String.valueOf(eventData.getExecuteTime()), consistency, mode,\n                                    dumpEventColumn(eventData.getKeys()), dumpEventColumn(eventData.getOldKeys()),\n                                    dumpEventColumn(eventData.getColumns()), \"\\t\" + eventData.getSql());\n    }\n\n    private static String dumpEventColumn(List<EventColumn> columns) {\n        StringBuilder builder = new StringBuilder(event_default_capacity);\n        int size = columns.size();\n        for (int i = 0; i < size; i++) {\n            EventColumn column = columns.get(i);\n            builder.append(\"\\t\").append(column.toString());\n            if (i < columns.size() - 1) {\n                builder.append(SEP);\n            }\n        }\n        return builder.toString();\n    }\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/load/loader/db/DbLoadMerger.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.load.loader.db;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.LinkedHashMap;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.util.CollectionUtils;\n\nimport com.alibaba.otter.node.etl.load.exception.LoadException;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.monitor.PermitMonitor;\nimport com.alibaba.otter.shared.etl.model.EventColumn;\nimport com.alibaba.otter.shared.etl.model.EventColumnIndexComparable;\nimport com.alibaba.otter.shared.etl.model.EventData;\nimport com.alibaba.otter.shared.etl.model.EventType;\n\n/**\n * <pre>\n * 合并相同tableId的变更记录.\n * pk相同的多条变更数据合并后的结果是：\n * 1, I \n * 2, U \n * 3, D \n * 如果有一条I，多条U，merge成I;\n * 如果有多条U，取最晚的那条;\n * </pre>\n * \n * @author jianghang 2012-10-31 下午05:23:40\n * @version 4.1.2\n */\npublic class DbLoadMerger {\n\n    private static final Logger logger = LoggerFactory.getLogger(PermitMonitor.class);\n\n    /**\n     * 将一批数据进行根据table+主键信息进行合并，保证一个表的一个pk记录只有一条结果\n     * \n     * @param eventDatas\n     * @return\n     */\n    public static List<EventData> merge(List<EventData> eventDatas) {\n        Map<RowKey, EventData> result = new LinkedHashMap<RowKey, EventData>();\n        for (EventData eventData : eventDatas) {\n            merge(eventData, result);\n        }\n        return new LinkedList<EventData>(result.values());\n    }\n\n    public static void merge(EventData eventData, Map<RowKey, EventData> result) {\n        EventType eventType = eventData.getEventType();\n        switch (eventType) {\n            case INSERT:\n                mergeInsert(eventData, result);\n                break;\n            case UPDATE:\n                mergeUpdate(eventData, result);\n                break;\n            case DELETE:\n                mergeDelete(eventData, result);\n                break;\n            default:\n                break;\n        }\n    }\n\n    private static void mergeInsert(EventData eventData, Map<RowKey, EventData> result) {\n        // insert无主键变更的处理\n        RowKey rowKey = new RowKey(eventData.getTableId(), eventData.getSchemaName(), eventData.getTableName(),\n                                   eventData.getKeys());\n        if (!result.containsKey(rowKey)) {\n            result.put(rowKey, eventData);\n        } else {\n            EventData oldEventData = result.get(rowKey);\n            eventData.setSize(oldEventData.getSize() + eventData.getSize());\n            // 如果上一条变更是delete的，就直接用insert替换\n            if (oldEventData.getEventType() == EventType.DELETE) {\n                result.put(rowKey, eventData);\n            } else if (oldEventData.getEventType() == EventType.UPDATE\n                       || oldEventData.getEventType() == EventType.INSERT) {\n                // insert之前出现了update逻辑上不可能，唯一的可能性主要是Freedom的介入，人为的插入了一条Insert记录\n                // 不过freedom一般不建议Insert操作，只建议执行update/delete操作. update默认会走merge\n                // sql,不存在即插入\n                logger.warn(\"update-insert/insert-insert happend. before[{}] , after[{}]\", oldEventData, eventData);\n                // 如果上一条变更是update的，就用insert替换，并且把上一条存在而这一条不存在的字段值拷贝到这一条中\n                EventData mergeEventData = replaceColumnValue(eventData, oldEventData);\n                mergeEventData.getOldKeys().clear();// 清空oldkeys，insert记录不需要\n                result.put(rowKey, mergeEventData);\n            }\n        }\n    }\n\n    private static void mergeUpdate(EventData eventData, Map<RowKey, EventData> result) {\n        RowKey rowKey = new RowKey(eventData.getTableId(), eventData.getSchemaName(), eventData.getTableName(),\n                                   eventData.getKeys());\n        if (!CollectionUtils.isEmpty(eventData.getOldKeys())) {// 存在主键变更\n            // 需要解决(1->2 , 2->3)级联主键变更的问题\n            RowKey oldKey = new RowKey(eventData.getTableId(), eventData.getSchemaName(), eventData.getTableName(),\n                                       eventData.getOldKeys());\n            if (!result.containsKey(oldKey)) {// 不需要级联\n                result.put(rowKey, eventData);\n            } else {\n                EventData oldEventData = result.get(oldKey);\n                eventData.setSize(oldEventData.getSize() + eventData.getSize());\n                // 如果上一条变更是insert的，就把这一条的eventType改成insert，并且把上一条存在而这一条不存在的字段值拷贝到这一条中\n                if (oldEventData.getEventType() == EventType.INSERT) {\n                    eventData.setEventType(EventType.INSERT);\n                    // 删除当前变更数据老主键的记录.\n                    result.remove(oldKey);\n\n                    EventData mergeEventData = replaceColumnValue(eventData, oldEventData);\n                    mergeEventData.getOldKeys().clear();// 清空oldkeys，insert记录不需要\n                    result.put(rowKey, mergeEventData);\n                } else if (oldEventData.getEventType() == EventType.UPDATE) {\n                    // 删除当前变更数据老主键的记录.\n                    result.remove(oldKey);\n\n                    // 如果上一条变更是update的，把上一条存在而这一条不存在的数据拷贝到这一条中\n                    EventData mergeEventData = replaceColumnValue(eventData, oldEventData);\n                    result.put(rowKey, mergeEventData);\n                } else {\n                    throw new LoadException(\"delete(has old pks) + update impossible happed!\");\n                }\n            }\n        } else {\n            if (!result.containsKey(rowKey)) {// 没有主键变更\n                result.put(rowKey, eventData);\n            } else {\n                EventData oldEventData = result.get(rowKey);\n                // 如果上一条变更是insert的，就把这一条的eventType改成insert，并且把上一条存在而这一条不存在的字段值拷贝到这一条中\n                if (oldEventData.getEventType() == EventType.INSERT) {\n                    eventData.setEventType(EventType.INSERT);\n\n                    EventData mergeEventData = replaceColumnValue(eventData, oldEventData);\n                    result.put(rowKey, mergeEventData);\n                } else if (oldEventData.getEventType() == EventType.UPDATE) {// 可能存在\n                                                                             // 1->2\n                                                                             // ,\n                                                                             // 2update的问题\n\n                    // 如果上一条变更是update的，把上一条存在而这一条不存在的数据拷贝到这一条中\n                    EventData mergeEventData = replaceColumnValue(eventData, oldEventData);\n                    result.put(rowKey, mergeEventData);\n                } else if (oldEventData.getEventType() == EventType.DELETE) {\n                    //异常情况，出现 delete + update，那就直接更新为update\n                    result.put(rowKey, eventData);\n                }\n            }\n        }\n    }\n\n    private static void mergeDelete(EventData eventData, Map<RowKey, EventData> result) {\n        // 只保留pks，把columns去掉. 以后针对数据仓库可以开放delete columns记录\n        RowKey rowKey = new RowKey(eventData.getTableId(), eventData.getSchemaName(), eventData.getTableName(),\n                                   eventData.getKeys());\n        if (!result.containsKey(rowKey)) {\n            result.put(rowKey, eventData);\n        } else {\n            EventData oldEventData = result.get(rowKey);\n            eventData.setSize(oldEventData.getSize() + eventData.getSize());\n            if (!CollectionUtils.isEmpty(oldEventData.getOldKeys())) {// 存在主键变更\n                // insert/update -> delete记录组合时，delete的对应的pk为上一条记录的pk\n                eventData.setKeys(oldEventData.getOldKeys());\n                eventData.getOldKeys().clear();// 清除oldKeys\n\n                result.remove(rowKey);// 删除老的对象\n                result.put(new RowKey(eventData.getTableId(), eventData.getSchemaName(), eventData.getTableName(),\n                                      eventData.getKeys()), eventData); // key发生变化，需要重新构造一个RowKey\n            } else {\n                eventData.getOldKeys().clear();// 清除oldKeys\n                result.put(rowKey, eventData);\n            }\n\n        }\n    }\n\n    /**\n     * 把old中的值存在而new中不存在的值合并到new中,并且把old中的变更前的主键保存到new中的变更前的主键.\n     * \n     * @param newEventData\n     * @param oldEventData\n     * @return\n     */\n    private static EventData replaceColumnValue(EventData newEventData, EventData oldEventData) {\n        List<EventColumn> newColumns = newEventData.getColumns();\n        List<EventColumn> oldColumns = oldEventData.getColumns();\n        List<EventColumn> temp = new ArrayList<EventColumn>();\n        for (EventColumn oldColumn : oldColumns) {\n            boolean contain = false;\n            for (EventColumn newColumn : newColumns) {\n                if (oldColumn.getColumnName().equalsIgnoreCase(newColumn.getColumnName())) {\n                    newColumn.setUpdate(newColumn.isUpdate() || oldColumn.isUpdate());// 合并isUpdate字段\n                    contain = true;\n                }\n            }\n\n            if (!contain) {\n                temp.add(oldColumn);\n            }\n        }\n        newColumns.addAll(temp);\n        Collections.sort(newColumns, new EventColumnIndexComparable()); // 排序\n        // 把上一次变更的旧主键传递到这次变更的旧主键.\n        newEventData.setOldKeys(oldEventData.getOldKeys());\n        if (oldEventData.getSyncConsistency() != null) {\n            newEventData.setSyncConsistency(oldEventData.getSyncConsistency());\n        }\n        if (oldEventData.getSyncMode() != null) {\n            newEventData.setSyncMode(oldEventData.getSyncMode());\n        }\n\n        if (oldEventData.isRemedy()) {\n            newEventData.setRemedy(oldEventData.isRemedy());\n        }\n        newEventData.setSize(oldEventData.getSize() + newEventData.getSize());\n        return newEventData;\n    }\n\n    public static class RowKey implements Serializable {\n\n        private static final long serialVersionUID = -7369951798499581038L;\n        private Long              tableId;\n        private String            schemaName;                              // tableId代表统配符时，需要指定schemaName\n        private String            tableName;                               // tableId代表统配符时，需要指定tableName\n\n        public RowKey(Long tableId, String schemaName, String tableName, List<EventColumn> keys){\n            this.schemaName = schemaName;\n            this.tableName = tableName;\n            this.keys = keys;\n        }\n\n        public RowKey(List<EventColumn> keys){\n            this.keys = keys;\n        }\n\n        private List<EventColumn> keys = new ArrayList<EventColumn>();\n\n        public List<EventColumn> getKeys() {\n            return keys;\n        }\n\n        public void setKeys(List<EventColumn> keys) {\n            this.keys = keys;\n        }\n\n        public String getSchemaName() {\n            return schemaName;\n        }\n\n        public void setSchemaName(String schemaName) {\n            this.schemaName = schemaName;\n        }\n\n        public String getTableName() {\n            return tableName;\n        }\n\n        public void setTableName(String tableName) {\n            this.tableName = tableName;\n        }\n\n        public Long getTableId() {\n            return tableId;\n        }\n\n        public void setTableId(Long tableId) {\n            this.tableId = tableId;\n        }\n\n        @Override\n        public int hashCode() {\n            final int prime = 31;\n            int result = 1;\n            result = prime * result + ((keys == null) ? 0 : keys.hashCode());\n            result = prime * result + ((schemaName == null) ? 0 : schemaName.hashCode());\n            result = prime * result + ((tableId == null) ? 0 : tableId.hashCode());\n            result = prime * result + ((tableName == null) ? 0 : tableName.hashCode());\n            return result;\n        }\n\n        @Override\n        public boolean equals(Object obj) {\n            if (this == obj) {\n                return true;\n            }\n            if (obj == null) {\n                return false;\n            }\n            if (!(obj instanceof RowKey)) {\n                return false;\n            }\n            RowKey other = (RowKey) obj;\n            if (keys == null) {\n                if (other.keys != null) {\n                    return false;\n                }\n            } else if (!keys.equals(other.keys)) {\n                return false;\n            }\n            if (schemaName == null) {\n                if (other.schemaName != null) {\n                    return false;\n                }\n            } else if (!schemaName.equals(other.schemaName)) {\n                return false;\n            }\n            if (tableId == null) {\n                if (other.tableId != null) {\n                    return false;\n                }\n            } else if (!tableId.equals(other.tableId)) {\n                return false;\n            }\n            if (tableName == null) {\n                if (other.tableName != null) {\n                    return false;\n                }\n            } else if (!tableName.equals(other.tableName)) {\n                return false;\n            }\n            return true;\n        }\n\n    }\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/load/loader/db/FileLoadAction.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.load.loader.db;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.concurrent.ArrayBlockingQueue;\nimport java.util.concurrent.Callable;\nimport java.util.concurrent.ExecutorCompletionService;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Future;\nimport java.util.concurrent.ThreadPoolExecutor;\nimport java.util.concurrent.TimeUnit;\n\nimport org.apache.commons.io.FilenameUtils;\nimport org.apache.commons.lang.StringUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.slf4j.MDC;\nimport org.springframework.beans.factory.DisposableBean;\nimport org.springframework.beans.factory.InitializingBean;\n\nimport com.alibaba.otter.node.common.config.ConfigClientService;\nimport com.alibaba.otter.node.etl.OtterConstants;\nimport com.alibaba.otter.node.etl.load.exception.LoadException;\nimport com.alibaba.otter.node.etl.load.loader.LoadStatsTracker;\nimport com.alibaba.otter.node.etl.load.loader.LoadStatsTracker.LoadCounter;\nimport com.alibaba.otter.node.etl.load.loader.db.context.FileLoadContext;\nimport com.alibaba.otter.node.etl.load.loader.weight.WeightBuckets;\nimport com.alibaba.otter.node.etl.load.loader.weight.WeightController;\nimport com.alibaba.otter.shared.common.model.config.ConfigHelper;\nimport com.alibaba.otter.shared.common.model.config.channel.Channel;\nimport com.alibaba.otter.shared.common.model.config.data.DataMediaPair;\nimport com.alibaba.otter.shared.common.model.config.pipeline.Pipeline;\nimport com.alibaba.otter.shared.common.utils.NioUtils;\nimport com.alibaba.otter.shared.common.utils.thread.NamedThreadFactory;\nimport com.alibaba.otter.shared.etl.model.FileBatch;\nimport com.alibaba.otter.shared.etl.model.FileData;\nimport com.alibaba.otter.shared.etl.model.Identity;\n\n/**\n * 处理文件load\n * \n * @author jianghang 2011-10-31 上午11:33:22\n * @author zebinxu 2012-4-28 下午3:39:17 将每个权重的 file load 做成多线程\n * @version 4.0.0\n */\npublic class FileLoadAction implements InitializingBean, DisposableBean {\n\n    private static final Logger logger             = LoggerFactory.getLogger(FileLoadAction.class);\n    private int                 retry              = 5;\n    private ConfigClientService configClientService;\n    private LoadStatsTracker    loadStatsTracker;\n    private boolean             dump               = true;\n\n    // for concurrent file load\n    private static final String WORKER_NAME        = \"FileLoadAction\";\n    private static final String WORKER_NAME_FORMAT = \"pipelineId = %s , pipelineName = %s , \" + WORKER_NAME;\n    private static final int    DEFAULT_POOL_SIZE  = 5;\n    private int                 poolSize           = DEFAULT_POOL_SIZE;\n    private ExecutorService     executor;\n\n    /**\n     * 返回结果为已处理成功的记录\n     */\n    public FileLoadContext load(FileBatch fileBatch, File rootDir, WeightController controller) {\n        if (false == rootDir.exists()) {\n            throw new LoadException(rootDir.getPath() + \" is not exist\");\n        }\n        FileLoadContext context = buildContext(fileBatch.getIdentity());\n        context.setPrepareDatas(fileBatch.getFiles());\n        boolean isDryRun = context.getPipeline().getParameters().isDryRun();\n        try {\n            // 复制成功的文件信息\n            WeightBuckets<FileData> buckets = buildWeightBuckets(fileBatch.getIdentity(), fileBatch.getFiles());\n            List<Long> weights = buckets.weights();\n            controller.start(weights);\n            // 处理数据\n            for (int i = 0; i < weights.size(); i++) {\n                Long weight = weights.get(i);\n                controller.await(weight.intValue());\n                if (logger.isInfoEnabled()) {\n                    logger.debug(\"##start load for weight:{}\\n\", weight);\n                }\n\n                // 处理同一个weight下的数据\n                List<FileData> items = buckets.getItems(weight);\n                if (context.getPipeline().getParameters().isDryRun()) {\n                    dryRun(context, items, rootDir);\n                } else {\n                    moveFiles(context, items, rootDir);\n                }\n\n                controller.single(weight.intValue());\n                if (logger.isInfoEnabled()) {\n                    logger.debug(\"##end load for weight:{}\\n\", weight);\n                }\n            }\n\n            if (dump || isDryRun) {\n                MDC.put(OtterConstants.splitPipelineLoadLogFileKey,\n                        String.valueOf(fileBatch.getIdentity().getPipelineId()));\n                logger.info(FileloadDumper.dumpContext(\"successed\", context));\n                MDC.remove(OtterConstants.splitPipelineLoadLogFileKey);\n            }\n        } catch (InterruptedException e) {\n            Thread.currentThread().interrupt();\n            if (dump || isDryRun) {\n                MDC.put(OtterConstants.splitPipelineLoadLogFileKey,\n                        String.valueOf(fileBatch.getIdentity().getPipelineId()));\n                logger.info(FileloadDumper.dumpContext(\"error\", context));\n                MDC.remove(OtterConstants.splitPipelineLoadLogFileKey);\n            }\n        } catch (Exception e) {\n            if (dump || isDryRun) {\n                MDC.put(OtterConstants.splitPipelineLoadLogFileKey,\n                        String.valueOf(fileBatch.getIdentity().getPipelineId()));\n                logger.info(FileloadDumper.dumpContext(\"error\", context));\n                MDC.remove(OtterConstants.splitPipelineLoadLogFileKey);\n            }\n            throw new LoadException(e);\n        } finally {\n\n            // 不论是否移动成功，删除临时目录\n            NioUtils.delete(rootDir, 3);\n        }\n\n        return context;\n    }\n\n    private void adjustPoolSize(FileLoadContext context) {\n        Pipeline pipeline = context.getPipeline();\n        int newPoolSize = pipeline.getParameters().getFileLoadPoolSize();\n        if (newPoolSize != poolSize) {\n            poolSize = newPoolSize;\n            if (executor instanceof ThreadPoolExecutor) {\n                ThreadPoolExecutor pool = (ThreadPoolExecutor) executor;\n                pool.setCorePoolSize(newPoolSize);\n                pool.setMaximumPoolSize(newPoolSize);\n            }\n        }\n\n    }\n\n    private FileLoadContext buildContext(Identity identity) {\n        FileLoadContext context = new FileLoadContext();\n        context.setIdentity(identity);\n        Channel channel = configClientService.findChannel(identity.getChannelId());\n        Pipeline pipeline = configClientService.findPipeline(identity.getPipelineId());\n        context.setChannel(channel);\n        context.setPipeline(pipeline);\n        return context;\n    }\n\n    /**\n     * 构建基于weight权重分组的item集合列表\n     */\n    private WeightBuckets<FileData> buildWeightBuckets(Identity identity, List<FileData> datas) {\n        WeightBuckets<FileData> buckets = new WeightBuckets<FileData>();\n        for (FileData data : datas) {\n            // 获取对应的weight\n            DataMediaPair pair = ConfigHelper.findDataMediaPair(getPipeline(identity), data.getPairId());\n            buckets.addItem(pair.getPushWeight(), data);\n        }\n\n        return buckets;\n    }\n\n    private Pipeline getPipeline(Identity identity) {\n        return configClientService.findPipeline(identity.getPipelineId());\n    }\n\n    private void dryRun(FileLoadContext context, List<FileData> fileDatas, File rootDir) {\n        for (FileData fileData : fileDatas) {\n            boolean isLocal = StringUtils.isBlank(fileData.getNameSpace());\n            String entryName = null;\n            if (true == isLocal) {\n                entryName = FilenameUtils.getPath(fileData.getPath()) + FilenameUtils.getName(fileData.getPath());\n            } else {\n                entryName = fileData.getNameSpace() + File.separator + fileData.getPath();\n            }\n            File sourceFile = new File(rootDir, entryName);\n            if (true == sourceFile.exists() && false == sourceFile.isDirectory()) {\n                if (false == isLocal) {\n                    throw new LoadException(fileData + \" is not support!\");\n                } else {\n                    // 记录一下文件的meta信息\n                    fileData.setSize(sourceFile.length());\n                    fileData.setLastModifiedTime(sourceFile.lastModified());\n                    context.getProcessedDatas().add(fileData);\n                }\n\n                LoadCounter counter = loadStatsTracker.getStat(context.getIdentity()).getStat(fileData.getPairId());\n                counter.getFileCount().incrementAndGet();\n                counter.getFileSize().addAndGet(fileData.getSize());\n            } else if (fileData.getEventType().isDelete()) {\n                // 删除对应的文件\n                if (false == isLocal) {\n                    throw new LoadException(fileData + \" is not support!\");\n                } else {\n                    context.getProcessedDatas().add(fileData);\n                }\n            } else {\n                context.getFailedDatas().add(fileData);// 失败记录\n            }\n        }\n    }\n\n    /**\n     * 多线程处理文件加载，使用 fast-fail 策略\n     */\n    private void moveFiles(FileLoadContext context, List<FileData> fileDatas, File rootDir) {\n        Exception exception = null;\n        adjustPoolSize(context);\n        ExecutorCompletionService<Exception> executorComplition = new ExecutorCompletionService<Exception>(executor);\n\n        List<Future<Exception>> results = new ArrayList<Future<Exception>>();\n        for (FileData fileData : fileDatas) {\n            Future<Exception> future = executorComplition.submit(new FileLoadWorker(context, rootDir, fileData));\n            results.add(future);\n\n            // fast fail\n            if (future.isDone()) { // 如果是自己执行的任务(线程池采用 CallerRunsPolicy)，则立刻进行检查\n                try {\n                    exception = future.get();\n                } catch (Exception e) {\n                    exception = e;\n                }\n                if (exception != null) {\n                    for (Future<Exception> result : results) {\n                        if (!result.isDone() && !result.isCancelled()) {\n                            result.cancel(true);\n                        }\n                    }\n                    throw exception instanceof LoadException ? (LoadException) exception : new LoadException(exception);\n                }\n            }\n\n        }\n\n        int resultSize = results.size();\n        int cursor = 0;\n        while (cursor < resultSize) {\n            try {\n                Future<Exception> result = executorComplition.take();\n                exception = result.get();\n            } catch (Exception e) {\n                exception = e;\n                break;\n            }\n            cursor++;\n        }\n\n        if (cursor != resultSize) { // 发现任务出错，立刻把正在进行的任务取消\n            for (Future<Exception> future : results) {\n                if (!future.isDone() && !future.isCancelled()) {\n                    future.cancel(true);\n                }\n            }\n\n        }\n\n        if (exception != null) {\n            throw exception instanceof LoadException ? (LoadException) exception : new LoadException(exception);\n        }\n    }\n\n    private class FileLoadWorker implements Callable<Exception> {\n\n        private FileLoadContext context;\n        private File            rootDir;\n        private FileData        fileData;\n\n        public FileLoadWorker(FileLoadContext context, File rootDir, FileData fileData){\n            this.context = context;\n            this.rootDir = rootDir;\n            this.fileData = fileData;\n\n        }\n\n        public Exception call() throws Exception {\n            Thread.currentThread().setName(String.format(WORKER_NAME_FORMAT, context.getPipeline().getId(),\n                                                         context.getPipeline().getName()));\n            try {\n                MDC.put(OtterConstants.splitPipelineLogFileKey, String.valueOf(context.getPipeline().getId()));\n                if (fileData == null) {\n                    return null;\n                }\n                // 进行重试处理\n                int count = 0;\n                Throwable exception = null;\n                while (count++ < retry) {\n                    try {\n                        doMove(context, rootDir, fileData);\n                        return null;\n                    } catch (Exception e) {\n                        exception = e;\n                        if (count < retry) {\n                            Thread.sleep(50);\n                        }\n                    }\n                }\n\n                throw new LoadException(String.format(\"FileLoadWorker is error! createFile failed[%s]\",\n                                                      fileData.getPath()), exception);\n            } finally {\n                MDC.remove(OtterConstants.splitPipelineLogFileKey);\n            }\n        }\n    }\n\n    private void doMove(FileLoadContext context, File rootDir, FileData fileData) throws IOException {\n        boolean isLocal = StringUtils.isBlank(fileData.getNameSpace());\n        String entryName = null;\n        if (true == isLocal) {\n            entryName = FilenameUtils.getPath(fileData.getPath()) + FilenameUtils.getName(fileData.getPath());\n        } else {\n            entryName = fileData.getNameSpace() + File.separator + fileData.getPath();\n        }\n        File sourceFile = new File(rootDir, entryName);\n        if (true == sourceFile.exists() && false == sourceFile.isDirectory()) {\n            if (false == isLocal) {\n                throw new LoadException(fileData + \" is not support!\");\n            } else {\n                File targetFile = new File(fileData.getPath());\n                // copy to product path\n                NioUtils.copy(sourceFile, targetFile, retry);\n                if (true == targetFile.exists()) {\n                    // 记录一下文件的meta信息\n                    fileData.setSize(sourceFile.length());\n                    fileData.setLastModifiedTime(sourceFile.lastModified());\n                    context.getProcessedDatas().add(fileData);\n                } else {\n                    throw new LoadException(String.format(\"copy/rename [%s] to [%s] failed by unknow reason\",\n                                                          sourceFile.getPath(), targetFile.getPath()));\n                }\n\n            }\n\n            LoadCounter counter = loadStatsTracker.getStat(context.getIdentity()).getStat(fileData.getPairId());\n            counter.getFileCount().incrementAndGet();\n            counter.getFileSize().addAndGet(fileData.getSize());\n        } else if (fileData.getEventType().isDelete()) {\n            // 删除对应的文件\n            if (false == isLocal) {\n                throw new LoadException(fileData + \" is not support!\");\n            } else {\n                File targetFile = new File(fileData.getPath());\n                if (NioUtils.delete(targetFile, retry)) {\n                    context.getProcessedDatas().add(fileData);\n                } else {\n                    context.getFailedDatas().add(fileData);\n                }\n            }\n        } else {\n            context.getFailedDatas().add(fileData);// 失败记录\n        }\n\n    }\n\n    public void afterPropertiesSet() throws Exception {\n        executor = new ThreadPoolExecutor(poolSize, poolSize, 0L, TimeUnit.MILLISECONDS,\n                                          new ArrayBlockingQueue<Runnable>(poolSize * 4),\n                                          new NamedThreadFactory(WORKER_NAME),\n                                          new ThreadPoolExecutor.CallerRunsPolicy());\n    }\n\n    public void destroy() throws Exception {\n        executor.shutdownNow();\n    }\n\n    // ====================== setter / getter ===========================\n\n    public void setConfigClientService(ConfigClientService configClientService) {\n        this.configClientService = configClientService;\n    }\n\n    public void setLoadStatsTracker(LoadStatsTracker loadStatsTracker) {\n        this.loadStatsTracker = loadStatsTracker;\n    }\n\n    public void setRetry(int retry) {\n        this.retry = retry;\n    }\n\n    public void setDump(boolean dump) {\n        this.dump = dump;\n    }\n\n    public void setPoolSize(int poolSize) {\n        this.poolSize = poolSize;\n    }\n\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/load/loader/db/FileloadDumper.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.load.loader.db;\n\nimport java.text.MessageFormat;\nimport java.text.SimpleDateFormat;\nimport java.util.Date;\nimport java.util.List;\n\nimport org.apache.commons.lang.SystemUtils;\n\nimport com.alibaba.otter.node.etl.load.loader.db.context.FileLoadContext;\nimport com.alibaba.otter.shared.etl.model.FileData;\nimport com.alibaba.otter.shared.etl.model.Identity;\n\n/**\n * dumper 记录\n * \n * @author jianghang 2011-12-28 上午11:19:10\n * @version 4.0.0\n */\npublic class FileloadDumper {\n\n    private static final String SEP              = SystemUtils.LINE_SEPARATOR;\n\n    private static final String TIMESTAMP_FORMAT = \"yyyy-MM-dd HH:mm:ss:SSS\";\n    private static String       context_format   = null;\n    private static String       miss_format      = null;\n    private static String       filter_format    = null;\n\n    static {\n        context_format = SEP + \"****************************************************\" + SEP;\n        context_format = \"* status : {0}  , time : {1} *\" + SEP;\n        context_format += \"* Identity : {2} *\" + SEP;\n        context_format += \"* total Data : [{3}] , success Data : [{4}] , miss Data : [{5}] , Interrupt : [{6}]\" + SEP;\n        context_format += \"****************************************************\" + SEP;\n        context_format += \"* process file  *\" + SEP;\n        context_format += \"{7}\" + SEP;\n        context_format += \"* miss file *\" + SEP;\n        context_format += \"{8}\" + SEP;\n        context_format += \"****************************************************\" + SEP;\n\n        miss_format = SEP + \"****************************************************\" + SEP;\n        miss_format += \"* Identity : {0} *\" + SEP;\n        miss_format += \"* miss : \" + SEP;\n        miss_format += \"* {1}\" + SEP;\n        miss_format += \"****************************************************\";\n\n        filter_format = SEP + \"****************************************************\" + SEP;\n        filter_format += \"* Identity : {0} *\" + SEP;\n        filter_format += \"* input [{1}] , output [{2}] , filter [{3}] *\" + SEP;\n        filter_format += \"* filters : \" + SEP;\n        filter_format += \"* {4}\" + SEP;\n        filter_format += \"****************************************************\" + SEP;\n\n    }\n\n    public static String dumpContext(String status, FileLoadContext context) {\n        int successed = context.getProcessedDatas().size();\n        int failed = context.getFailedDatas().size();\n        int all = context.getPrepareDatas().size();\n        boolean isInterrupt = (all != failed + successed);\n        Date now = new Date();\n        SimpleDateFormat format = new SimpleDateFormat(TIMESTAMP_FORMAT);\n\n        return MessageFormat.format(context_format, status, format.format(now), context.getIdentity().toString(), all,\n                                    successed, failed, isInterrupt, dumpFileDatas(context.getProcessedDatas()),\n                                    dumpFileDatas(context.getFailedDatas()));\n    }\n\n    public static String dumpFileDatas(List<FileData> fileDatas) {\n        StringBuilder builder = new StringBuilder();\n        synchronized (fileDatas) {\n            for (FileData data : fileDatas) {\n                builder.append(\"\\t\").append(data.toString()).append(SEP);\n            }\n        }\n        return builder.toString();\n    }\n\n    public static String dumpMissFileDatas(Identity identity, FileData fileData) {\n        return MessageFormat.format(miss_format, identity.toString(), fileData.toString());\n    }\n\n    public static String dumpFilterFileDatas(Identity identity, int input, int output, List<FileData> fileDatas) {\n        StringBuilder builder = new StringBuilder();\n        synchronized (fileDatas) {\n            for (FileData data : fileDatas) {\n                builder.append(\"\\t\").append(data.toString()).append(SEP);\n            }\n        }\n        return MessageFormat.format(filter_format, identity.toString(), input, output, fileDatas.size(),\n                                    builder.toString());\n    }\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/load/loader/db/context/DbLoadContext.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.load.loader.db.context;\n\nimport java.util.Collections;\nimport java.util.LinkedList;\nimport java.util.List;\n\nimport com.alibaba.otter.node.etl.load.loader.AbstractLoadContext;\nimport com.alibaba.otter.shared.common.model.config.data.DataMediaSource;\nimport com.alibaba.otter.shared.etl.model.EventData;\n\n/**\n * 数据库load处理上下文\n * \n * @author jianghang 2011-11-1 上午11:22:45\n * @version 4.0.0\n */\npublic class DbLoadContext extends AbstractLoadContext<EventData> {\n\n    private static final long serialVersionUID = -4851977997313104740L;\n    private List<EventData>   lastProcessedDatas;                      // 上一轮的已录入的记录，可能会有多次失败需要合并多次已录入的数据\n    private DataMediaSource   dataMediaSource;\n\n    public DbLoadContext(){\n        lastProcessedDatas = Collections.synchronizedList(new LinkedList<EventData>());\n        prepareDatas = Collections.synchronizedList(new LinkedList<EventData>());\n        processedDatas = Collections.synchronizedList(new LinkedList<EventData>());\n        failedDatas = Collections.synchronizedList(new LinkedList<EventData>());\n    }\n\n    public List<EventData> getLastProcessedDatas() {\n        return lastProcessedDatas;\n    }\n\n    public void setLastProcessedDatas(List<EventData> lastProcessedDatas) {\n        this.lastProcessedDatas = lastProcessedDatas;\n    }\n\n    public DataMediaSource getDataMediaSource() {\n        return dataMediaSource;\n    }\n\n    public void setDataMediaSource(DataMediaSource dataMediaSource) {\n        this.dataMediaSource = dataMediaSource;\n    }\n\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/load/loader/db/context/FileLoadContext.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.load.loader.db.context;\n\nimport com.alibaba.otter.node.etl.load.loader.AbstractLoadContext;\nimport com.alibaba.otter.shared.etl.model.FileData;\n\npublic class FileLoadContext extends AbstractLoadContext<FileData> {\n\n    private static final long serialVersionUID = -6115201462901866481L;\n\n    public FileLoadContext(){\n        super();\n    }\n\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/load/loader/db/interceptor/log/LogLoadInterceptor.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.load.loader.db.interceptor.log;\n\nimport java.text.MessageFormat;\nimport java.text.SimpleDateFormat;\nimport java.util.Date;\nimport java.util.List;\n\nimport org.apache.commons.lang.SystemUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.slf4j.MDC;\n\nimport com.alibaba.otter.node.etl.OtterConstants;\nimport com.alibaba.otter.node.etl.load.loader.db.DbLoadDumper;\nimport com.alibaba.otter.node.etl.load.loader.db.context.DbLoadContext;\nimport com.alibaba.otter.node.etl.load.loader.interceptor.AbstractLoadInterceptor;\nimport com.alibaba.otter.shared.etl.model.EventData;\n\n/**\n * load的日志记录\n * \n * @author jianghang 2011-11-10 上午11:31:05\n * @version 4.0.0\n */\npublic class LogLoadInterceptor extends AbstractLoadInterceptor<DbLoadContext, EventData> {\n\n    private static final Logger logger           = LoggerFactory.getLogger(LogLoadInterceptor.class);\n    private static final String SEP              = SystemUtils.LINE_SEPARATOR;\n    private static final String TIMESTAMP_FORMAT = \"yyyy-MM-dd HH:mm:ss:SSS\";\n    private int                 batchSize        = 50;\n    private static String       context_format   = null;\n    private boolean             dump             = true;\n\n    static {\n        context_format = \"* status : {0}  , time : {1} *\" + SEP;\n        context_format += \"* Identity : {2} *\" + SEP;\n        context_format += \"* total Data : [{3}] , success Data : [{4}] , failed Data : [{5}] , Interrupt : [{6}]\" + SEP;\n    }\n\n    public void commit(DbLoadContext context) {\n        // 成功时记录一下\n        boolean dumpThisEvent = context.getPipeline().getParameters().isDumpEvent()\n                                || context.getPipeline().getParameters().isDryRun();\n        if (dump && dumpThisEvent && logger.isInfoEnabled()) {\n            synchronized (LogLoadInterceptor.class) {\n                try {\n                    MDC.put(OtterConstants.splitPipelineLoadLogFileKey,\n                            String.valueOf(context.getIdentity().getPipelineId()));\n                    logger.info(SEP + \"****************************************************\" + SEP);\n                    logger.info(dumpContextInfo(\"successed\", context));\n                    logger.info(\"****************************************************\" + SEP);\n                    logger.info(\"* process Data  *\" + SEP);\n                    logEventDatas(context.getProcessedDatas());\n                    logger.info(\"-----------------\" + SEP);\n                    logger.info(\"* failed Data *\" + SEP);\n                    logEventDatas(context.getFailedDatas());\n                    logger.info(\"****************************************************\" + SEP);\n                } finally {\n                    MDC.remove(OtterConstants.splitPipelineLoadLogFileKey);\n                }\n            }\n        }\n    }\n\n    public void error(DbLoadContext context) {\n        boolean dumpThisEvent = context.getPipeline().getParameters().isDumpEvent()\n                                || context.getPipeline().getParameters().isDryRun();\n        if (dump && dumpThisEvent && logger.isInfoEnabled()) {\n            synchronized (LogLoadInterceptor.class) {\n                try {\n                    MDC.put(OtterConstants.splitPipelineLoadLogFileKey,\n                            String.valueOf(context.getIdentity().getPipelineId()));\n                    logger.info(dumpContextInfo(\"error\", context));\n                    logger.info(\"* process Data  *\" + SEP);\n                    logEventDatas(context.getProcessedDatas());\n                    logger.info(\"-----------------\" + SEP);\n                    logger.info(\"* failed Data *\" + SEP);\n                    logEventDatas(context.getFailedDatas());\n                    logger.info(\"****************************************************\" + SEP);\n                } finally {\n                    MDC.remove(OtterConstants.splitPipelineLoadLogFileKey);\n                }\n            }\n        }\n    }\n\n    /**\n     * 分批输出多个数据\n     */\n    private void logEventDatas(List<EventData> eventDatas) {\n        int size = eventDatas.size();\n        // 开始输出每条记录\n        int index = 0;\n        do {\n            if (index + batchSize >= size) {\n                logger.info(DbLoadDumper.dumpEventDatas(eventDatas.subList(index, size)));\n            } else {\n                logger.info(DbLoadDumper.dumpEventDatas(eventDatas.subList(index, index + batchSize)));\n            }\n            index += batchSize;\n        } while (index < size);\n    }\n\n    private String dumpContextInfo(String status, DbLoadContext context) {\n        int successed = context.getProcessedDatas().size();\n        int failed = context.getFailedDatas().size();\n        int all = context.getPrepareDatas().size();\n        boolean isInterrupt = (all != (failed + successed));\n        Date now = new Date();\n        SimpleDateFormat format = new SimpleDateFormat(TIMESTAMP_FORMAT);\n        return MessageFormat.format(context_format, status, format.format(now), context.getIdentity().toString(), all,\n                                    successed, failed, isInterrupt);\n    }\n\n    public void setDump(boolean dump) {\n        this.dump = dump;\n    }\n\n    public void setBatchSize(int batchSize) {\n        this.batchSize = batchSize;\n    }\n\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/load/loader/db/interceptor/operation/AbstractOperationInterceptor.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.load.loader.db.interceptor.operation;\n\nimport java.sql.PreparedStatement;\nimport java.sql.SQLException;\nimport java.text.MessageFormat;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.concurrent.atomic.AtomicInteger;\n\nimport org.apache.commons.lang.StringUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.jdbc.core.BatchPreparedStatementSetter;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.jdbc.datasource.DataSourceTransactionManager;\nimport org.springframework.transaction.TransactionDefinition;\nimport org.springframework.transaction.TransactionStatus;\nimport org.springframework.transaction.support.TransactionCallback;\nimport org.springframework.transaction.support.TransactionTemplate;\n\nimport com.alibaba.otter.node.common.config.ConfigClientService;\nimport com.alibaba.otter.node.etl.common.db.dialect.DbDialect;\nimport com.alibaba.otter.node.etl.load.loader.db.context.DbLoadContext;\nimport com.alibaba.otter.node.etl.load.loader.interceptor.AbstractLoadInterceptor;\nimport com.alibaba.otter.shared.common.model.config.channel.Channel;\nimport com.alibaba.otter.shared.etl.model.EventData;\nimport com.alibaba.otter.shared.etl.model.Identity;\n\n/**\n * @author jianghang 2011-10-31 下午02:24:28\n * @version 4.0.0\n */\npublic abstract class AbstractOperationInterceptor extends AbstractLoadInterceptor<DbLoadContext, EventData> {\n\n    protected final Logger         logger              = LoggerFactory.getLogger(getClass());\n    protected static final int     GLOBAL_THREAD_COUNT = 1000;\n    protected static final int     INNER_THREAD_COUNT  = 300;\n    protected static final String  checkDataSql        = \"SELECT COUNT(*) FROM {0} WHERE id BETWEEN 0 AND {1}\";\n    protected static final String  deleteDataSql       = \"DELETE FROM {0}\";\n\n    protected String               updateSql;\n    protected String               updateInfoSql;\n    protected String               clearSql            = \"UPDATE {0} SET {1} = 0 WHERE id = ? and {1} = ?\";\n    protected String               clearInfoSql        = \"UPDATE {0} SET {1} = 0 , {2} = null WHERE id = ? and {1} = ? and {2} = ?\";\n    protected int                  innerIdCount        = INNER_THREAD_COUNT;\n    protected int                  globalIdCount       = GLOBAL_THREAD_COUNT;\n    protected ConfigClientService  configClientService;\n    protected Set<JdbcTemplate>    tableCheckStatus    = Collections.synchronizedSet(new HashSet<JdbcTemplate>());\n    protected AtomicInteger        THREAD_COUNTER      = new AtomicInteger(0);\n    protected ThreadLocal<Integer> threadLocal         = new ThreadLocal<Integer>();\n\n    protected AbstractOperationInterceptor(String updateSql, String updateInfoSql){\n        this.updateSql = updateSql;\n        this.updateInfoSql = updateInfoSql;\n    }\n\n    private void init(final JdbcTemplate jdbcTemplate, final String markTableName, final String markTableColumn) {\n        int count = jdbcTemplate.queryForInt(MessageFormat.format(checkDataSql, markTableName, GLOBAL_THREAD_COUNT - 1));\n        if (count != GLOBAL_THREAD_COUNT) {\n            if (logger.isInfoEnabled()) {\n                logger.info(\"Interceptor: init \" + markTableName + \"'s data.\");\n            }\n            TransactionTemplate transactionTemplate = new TransactionTemplate();\n            transactionTemplate.setTransactionManager(new DataSourceTransactionManager(jdbcTemplate.getDataSource()));\n            transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_NOT_SUPPORTED);// 注意这里强制使用非事务，保证多线程的可见性\n            transactionTemplate.execute(new TransactionCallback() {\n\n                public Object doInTransaction(TransactionStatus status) {\n                    jdbcTemplate.execute(MessageFormat.format(deleteDataSql, markTableName));\n                    String batchSql = MessageFormat.format(updateSql, new Object[] { markTableName, markTableColumn });\n                    jdbcTemplate.batchUpdate(batchSql, new BatchPreparedStatementSetter() {\n\n                        public void setValues(PreparedStatement ps, int idx) throws SQLException {\n                            ps.setInt(1, idx);\n                            ps.setInt(2, 0);\n                            // ps.setNull(3, Types.VARCHAR);\n                        }\n\n                        public int getBatchSize() {\n                            return GLOBAL_THREAD_COUNT;\n                        }\n                    });\n                    return null;\n                }\n            });\n\n            if (logger.isInfoEnabled()) {\n                logger.info(\"Interceptor: Init EROSA Client Data: \" + updateSql);\n            }\n        }\n\n    }\n\n    public void transactionBegin(DbLoadContext context, List<EventData> currentDatas, DbDialect dialect) {\n        boolean needInfo = StringUtils.isNotEmpty(context.getPipeline().getParameters().getChannelInfo());\n        if (context.getChannel().getPipelines().size() > 1 || needInfo) {// 如果是双向同步，需要记录clientId\n            String hint = currentDatas.get(0).getHint();\n            String sql = needInfo ? updateInfoSql : updateSql;\n            threadLocal.remove();// 进入之前先清理\n            int threadId = currentId();\n            updateMark(context, dialect, threadId, sql, needInfo, hint);\n            threadLocal.set(threadId);\n        }\n    }\n\n    public void transactionEnd(DbLoadContext context, List<EventData> currentDatas, DbDialect dialect) {\n        boolean needInfo = StringUtils.isNotEmpty(context.getPipeline().getParameters().getChannelInfo());\n        if (context.getChannel().getPipelines().size() > 1 || needInfo) {// 如果是双向同步，需要记录clientId\n            String hint = currentDatas.get(0).getHint();\n            String sql = needInfo ? clearInfoSql : clearSql;\n            Integer threadId = threadLocal.get();\n            updateMark(context, dialect, threadId, sql, needInfo, hint);\n            threadLocal.remove();\n        }\n    }\n\n    /**\n     * 更新一下事务标记\n     */\n    private void updateMark(DbLoadContext context, DbDialect dialect, int threadId, String sql, boolean needInfo,\n                            String hint) {\n        Identity identity = context.getIdentity();\n        Channel channel = context.getChannel();\n        // 获取dbDialect\n        String markTableName = context.getPipeline().getParameters().getSystemSchema() + \".\"\n                               + context.getPipeline().getParameters().getSystemMarkTable();\n        String markTableColumn = context.getPipeline().getParameters().getSystemMarkTableColumn();\n        synchronized (dialect.getJdbcTemplate()) {\n            if (tableCheckStatus.contains(dialect.getJdbcTemplate()) == false) {\n                init(dialect.getJdbcTemplate(), markTableName, markTableColumn);\n                tableCheckStatus.add(dialect.getJdbcTemplate());\n            }\n        }\n\n        int affectedCount = 0;\n        if (needInfo) {\n            String infoColumn = context.getPipeline().getParameters().getSystemMarkTableInfo();\n            String info = context.getPipeline().getParameters().getChannelInfo();// 记录一下channelInfo\n            String esql = MessageFormat.format(sql, new Object[] { markTableName, markTableColumn, infoColumn });\n            if (hint != null) {\n                esql = hint + esql;\n            }\n            affectedCount = dialect.getJdbcTemplate().update(esql, new Object[] { threadId, channel.getId(), info });\n        } else {\n            String esql = MessageFormat.format(sql, new Object[] { markTableName, markTableColumn });\n            if (hint != null) {\n                esql = hint + esql;\n            }\n            affectedCount = dialect.getJdbcTemplate().update(esql, new Object[] { threadId, channel.getId() });\n        }\n\n        if (affectedCount <= 0) {\n            logger.warn(\"## update {} failed by [{}]\", markTableName, threadId);\n        } else {\n            if (logger.isInfoEnabled()) {\n                logger.debug(\"Interceptor For [{}]\", identity);\n            }\n        }\n    }\n\n    private int currentId() {\n        synchronized (this) {\n            if (THREAD_COUNTER.get() == INNER_THREAD_COUNT) {\n                THREAD_COUNTER.set(0);\n            }\n\n            return THREAD_COUNTER.incrementAndGet();\n        }\n    }\n\n    // ========================= setter / getter ========================\n\n    public void setInnerIdCount(int innerIdCount) {\n        this.innerIdCount = innerIdCount;\n    }\n\n    public void setGlobalIdCount(int globalIdCount) {\n        this.globalIdCount = globalIdCount;\n    }\n\n    public void setConfigClientService(ConfigClientService configClientService) {\n        this.configClientService = configClientService;\n    }\n\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/load/loader/db/interceptor/operation/CanalMysqlInterceptor.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.load.loader.db.interceptor.operation;\n\n/**\n * 基于erosa的日志记录\n * \n * @author jianghang 2011-10-31 下午02:48:22\n * @version 4.0.0\n */\npublic class CanalMysqlInterceptor extends AbstractOperationInterceptor {\n\n    public static final String mergeofMysqlSql     = \"INSERT INTO {0} (id, {1}) VALUES (?, ?) ON DUPLICATE KEY UPDATE {1} = VALUES({1})\";\n\n    public static final String mergeofMysqlInfoSql = \"INSERT INTO {0} (id, {1}, {2}) VALUES (?, ? ,?) ON DUPLICATE KEY UPDATE {1} = VALUES({1}) , {2} = VALUES({2})\";\n\n    public CanalMysqlInterceptor(){\n        super(mergeofMysqlSql, mergeofMysqlInfoSql);\n    }\n\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/load/loader/db/interceptor/operation/CanalOracleInterceptor.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.load.loader.db.interceptor.operation;\n\n/**\n * 基于oracle的数据过滤\n * \n * @author jianghang 2011-10-31 下午02:51:09\n * @version 4.0.0\n */\npublic class CanalOracleInterceptor extends AbstractOperationInterceptor {\n\n    public static final String mergeofOracleSql     = \"merge /*+ use_nl(a b)*/ into {0} a using (select ? as id , ? as {1} from dual) b on (a.id=b.id)\"\n                                                      + \" when matched then update set a.{1}=b.{1}\"\n                                                      + \" when not matched then insert (a.id , a.{1}) values (b.id , b.{1})\";\n\n    public static final String mergeofOracleInfoSql = \"merge /*+ use_nl(a b)*/ into {0} a using (select ? as id , ? as {1} , ? as {2} from dual) b on (a.id=b.id)\"\n                                                      + \" when matched then update set a.{1}=b.{1} , a.{2}=b.{2}\"\n                                                      + \" when not matched then insert (a.id , a.{1} , a.{2}) values (b.id , b.{1} , b.{2})\";\n\n    public CanalOracleInterceptor(){\n        super(mergeofOracleSql, mergeofOracleInfoSql);\n    }\n\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/load/loader/db/interceptor/operation/OperationInterceptorFactory.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.load.loader.db.interceptor.operation;\n\nimport java.util.List;\n\nimport com.alibaba.otter.node.etl.common.db.dialect.DbDialect;\nimport com.alibaba.otter.node.etl.common.db.dialect.DbDialectFactory;\nimport com.alibaba.otter.node.etl.common.db.dialect.mysql.MysqlDialect;\nimport com.alibaba.otter.node.etl.common.db.dialect.oracle.OracleDialect;\nimport com.alibaba.otter.node.etl.load.loader.db.context.DbLoadContext;\nimport com.alibaba.otter.node.etl.load.loader.interceptor.AbstractLoadInterceptor;\nimport com.alibaba.otter.node.etl.load.loader.interceptor.LoadInterceptor;\nimport com.alibaba.otter.shared.common.model.config.ConfigHelper;\nimport com.alibaba.otter.shared.common.model.config.data.DataMedia;\nimport com.alibaba.otter.shared.common.model.config.data.db.DbMediaSource;\nimport com.alibaba.otter.shared.etl.model.EventData;\n\n/**\n * operator的调用工厂\n * \n * @author jianghang 2011-10-31 下午03:20:16\n * @version 4.0.0\n */\npublic class OperationInterceptorFactory extends AbstractLoadInterceptor<DbLoadContext, EventData> {\n\n    private DbDialectFactory  dbDialectFactory;\n    private LoadInterceptor[] mysqlInterceptors;\n    private LoadInterceptor[] oracleInterceptors;\n    private LoadInterceptor[] empty = new LoadInterceptor[0];\n\n    public void transactionBegin(DbLoadContext context, List<EventData> currentDatas, DbDialect dialect) {\n        LoadInterceptor[] interceptors = getIntercetptor(context, currentDatas);\n        for (LoadInterceptor interceptor : interceptors) {\n            interceptor.transactionBegin(context, currentDatas, dialect);\n        }\n    }\n\n    public void transactionEnd(DbLoadContext context, List<EventData> currentDatas, DbDialect dialect) {\n        LoadInterceptor[] interceptors = getIntercetptor(context, currentDatas);\n        for (LoadInterceptor interceptor : interceptors) {\n            interceptor.transactionEnd(context, currentDatas, dialect);\n        }\n    }\n\n    private LoadInterceptor[] getIntercetptor(DbLoadContext context, List<EventData> currentData) {\n        if (currentData == null || currentData.size() == 0) {\n            return empty;\n        }\n        DataMedia dataMedia = ConfigHelper.findDataMedia(context.getPipeline(), currentData.get(0).getTableId());\n        DbDialect dbDialect = dbDialectFactory.getDbDialect(context.getIdentity().getPipelineId(),\n                                                            (DbMediaSource) dataMedia.getSource());\n\n        if (dbDialect instanceof MysqlDialect) {\n            return mysqlInterceptors;\n        } else if (dbDialect instanceof OracleDialect) {\n            return oracleInterceptors;\n        } else {\n            return empty;\n        }\n    }\n\n    // ===================== setter / getter =========================\n\n    public void setMysqlInterceptors(LoadInterceptor[] mysqlInterceptors) {\n        this.mysqlInterceptors = mysqlInterceptors;\n    }\n\n    public void setOracleInterceptors(LoadInterceptor[] oracleInterceptors) {\n        this.oracleInterceptors = oracleInterceptors;\n    }\n\n    public void setDbDialectFactory(DbDialectFactory dbDialectFactory) {\n        this.dbDialectFactory = dbDialectFactory;\n    }\n\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/load/loader/db/interceptor/sql/SqlBuilderLoadInterceptor.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.load.loader.db.interceptor.sql;\n\nimport com.alibaba.otter.node.etl.common.db.dialect.DbDialect;\nimport com.alibaba.otter.node.etl.common.db.dialect.DbDialectFactory;\nimport com.alibaba.otter.node.etl.common.db.dialect.SqlTemplate;\nimport com.alibaba.otter.node.etl.common.db.dialect.oracle.OracleSqlTemplate;\nimport com.alibaba.otter.node.etl.load.loader.db.context.DbLoadContext;\nimport com.alibaba.otter.node.etl.load.loader.interceptor.AbstractLoadInterceptor;\nimport com.alibaba.otter.shared.common.model.config.data.db.DbMediaSource;\nimport com.alibaba.otter.shared.etl.model.EventColumn;\nimport com.alibaba.otter.shared.etl.model.EventData;\nimport com.alibaba.otter.shared.etl.model.EventType;\nimport org.springframework.util.CollectionUtils;\n\nimport java.util.List;\n\n/**\n * 计算下最新的sql语句\n * \n * @author jianghang 2011-12-26 下午12:09:20\n * @version 4.0.0\n */\npublic class SqlBuilderLoadInterceptor extends AbstractLoadInterceptor<DbLoadContext, EventData> {\n\n    private DbDialectFactory dbDialectFactory;\n\n    public boolean before(DbLoadContext context, EventData currentData) {\n        // 初步构建sql\n        DbDialect dbDialect = dbDialectFactory.getDbDialect(context.getIdentity().getPipelineId(),\n            (DbMediaSource) context.getDataMediaSource());\n        SqlTemplate sqlTemplate = dbDialect.getSqlTemplate();\n        EventType type = currentData.getEventType();\n        String sql = null;\n\n        String schemaName = (currentData.isWithoutSchema() ? null : currentData.getSchemaName());\n\n        /**\n         * 针对DRDS数据库\n         */\n        String shardColumns = null;\n        if(dbDialect.isDRDS()){\n            // 获取拆分键\n            shardColumns = dbDialect.getShardColumns(schemaName, currentData.getTableName());\n\n        }\n\n        // 注意insert/update语句对应的字段数序都是将主键排在后面\n        if (type.isInsert()) {\n            if (CollectionUtils.isEmpty(currentData.getColumns())\n                && (dbDialect.isDRDS() || sqlTemplate instanceof OracleSqlTemplate)) { // 如果表为全主键，直接进行insert\n                // sql\n                sql = sqlTemplate.getInsertSql(schemaName,\n                    currentData.getTableName(),\n                    buildColumnNames(currentData.getKeys()),\n                    buildColumnNames(currentData.getColumns()));\n            } else {\n                sql = sqlTemplate.getMergeSql(schemaName,\n                    currentData.getTableName(),\n                    buildColumnNames(currentData.getKeys()),\n                    buildColumnNames(currentData.getColumns()),\n                    new String[] {},\n                    !dbDialect.isDRDS(),\n                    shardColumns);\n            }\n        } else if (type.isUpdate()) {\n            // String[] keyColumns = buildColumnNames(currentData.getKeys());\n            // String[] otherColumns =\n            // buildColumnNames(currentData.getUpdatedColumns());\n            // boolean existOldKeys = false;\n            // for (String key : keyColumns) {\n            // // 找一下otherColumns是否有主键，存在就代表有主键变更\n            // if (ArrayUtils.contains(otherColumns, key)) {\n            // existOldKeys = true;\n            // break;\n            // }\n            // }\n\n            boolean existOldKeys = !CollectionUtils.isEmpty(currentData.getOldKeys());\n            boolean rowMode = context.getPipeline().getParameters().getSyncMode().isRow();\n            String[] keyColumns = null;\n            String[] otherColumns = null;\n            if (existOldKeys) {\n                // 需要考虑主键变更的场景\n                // 构造sql如下：update table xxx set pk = newPK where pk = oldPk\n                keyColumns = buildColumnNames(currentData.getOldKeys());\n                // 这里需要精确获取变更的主键,因为目标为DRDS时主键会包含拆分键,正常的原主键变更只更新对应的单主键列即可\n                if (dbDialect.isDRDS()) {\n                    otherColumns = buildColumnNames(currentData.getUpdatedColumns(), currentData.getUpdatedKeys());\n                } else {\n                    otherColumns = buildColumnNames(currentData.getUpdatedColumns(), currentData.getKeys());\n                }\n            } else {\n                keyColumns = buildColumnNames(currentData.getKeys());\n                otherColumns = buildColumnNames(currentData.getUpdatedColumns());\n            }\n\n            if (rowMode && !existOldKeys) {// 如果是行记录,并且不存在主键变更，考虑merge sql\n                sql = sqlTemplate.getMergeSql(schemaName,\n                    currentData.getTableName(),\n                    keyColumns,\n                    otherColumns,\n                    new String[] {},\n                    !dbDialect.isDRDS(),\n                    shardColumns);\n            } else {// 否则进行update sql\n                sql = sqlTemplate.getUpdateSql(schemaName, currentData.getTableName(), keyColumns, otherColumns, !dbDialect.isDRDS(), shardColumns);\n            }\n        } else if (type.isDelete()) {\n            sql = sqlTemplate.getDeleteSql(schemaName,\n                currentData.getTableName(),\n                buildColumnNames(currentData.getKeys()));\n        }\n\n        // 处理下hint sql\n        if (currentData.getHint() != null) {\n            currentData.setSql(currentData.getHint() + sql);\n        } else {\n            currentData.setSql(sql);\n        }\n        return false;\n    }\n\n    private String[] buildColumnNames(List<EventColumn> columns) {\n        String[] result = new String[columns.size()];\n        for (int i = 0; i < columns.size(); i++) {\n            EventColumn column = columns.get(i);\n            result[i] = column.getColumnName();\n        }\n        return result;\n    }\n\n    private String[] buildColumnNames(List<EventColumn> columns1, List<EventColumn> columns2) {\n        String[] result = new String[columns1.size() + columns2.size()];\n        int i = 0;\n        for (i = 0; i < columns1.size(); i++) {\n            EventColumn column = columns1.get(i);\n            result[i] = column.getColumnName();\n        }\n\n        for (; i < columns1.size() + columns2.size(); i++) {\n            EventColumn column = columns2.get(i - columns1.size());\n            result[i] = column.getColumnName();\n        }\n        return result;\n    }\n\n    // =============== setter / getter =============\n\n    public void setDbDialectFactory(DbDialectFactory dbDialectFactory) {\n        this.dbDialectFactory = dbDialectFactory;\n    }\n\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/load/loader/interceptor/AbstractLoadInterceptor.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.load.loader.interceptor;\n\nimport java.util.List;\n\nimport com.alibaba.otter.node.etl.common.db.dialect.DbDialect;\n\n/**\n * 提供接口的默认实现\n * \n * @author jianghang 2011-11-9 下午06:28:14\n * @version 4.0.0\n */\npublic class AbstractLoadInterceptor<L, D> implements LoadInterceptor<L, D> {\n\n    public void prepare(L context) {\n    }\n\n    public boolean before(L context, D currentData) {\n        return false;\n    }\n\n    public void transactionBegin(L context, List<D> currentDatas, DbDialect dialect) {\n    }\n\n    public void transactionEnd(L context, List<D> currentDatas, DbDialect dialect) {\n    }\n\n    public void after(L context, D currentData) {\n\n    }\n\n    public void commit(L context) {\n    }\n\n    public void error(L context) {\n    }\n\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/load/loader/interceptor/ChainLoadInterceptor.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.load.loader.interceptor;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport com.alibaba.otter.node.etl.common.db.dialect.DbDialect;\nimport com.alibaba.otter.node.etl.load.loader.LoadContext;\nimport com.alibaba.otter.shared.etl.model.ObjectData;\n\npublic class ChainLoadInterceptor extends AbstractLoadInterceptor<LoadContext, ObjectData> {\n\n    private List<LoadInterceptor> interceptors = new ArrayList<LoadInterceptor>();\n\n    public void prepare(LoadContext context) {\n        if (interceptors == null) {\n            return;\n        }\n\n        for (LoadInterceptor interceptor : interceptors) {\n            interceptor.prepare(context);\n        }\n    }\n\n    public boolean before(LoadContext context, ObjectData currentData) {\n        if (interceptors == null) {\n            return false;\n        }\n\n        boolean result = false;\n        for (LoadInterceptor interceptor : interceptors) {\n            result |= interceptor.before(context, currentData);\n            if (result) {// 出现一个true就退出\n                return result;\n            }\n        }\n        return result;\n    }\n\n    public void transactionBegin(LoadContext context, List<ObjectData> currentDatas, DbDialect dialect) {\n        if (interceptors == null) {\n            return;\n        }\n\n        for (LoadInterceptor interceptor : interceptors) {\n            interceptor.transactionBegin(context, currentDatas, dialect);\n        }\n    }\n\n    public void transactionEnd(LoadContext context, List<ObjectData> currentDatas, DbDialect dialect) {\n        if (interceptors == null) {\n            return;\n        }\n\n        for (LoadInterceptor interceptor : interceptors) {\n            interceptor.transactionEnd(context, currentDatas, dialect);\n        }\n    }\n\n    public void after(LoadContext context, ObjectData currentData) {\n        if (interceptors == null) {\n            return;\n        }\n\n        for (LoadInterceptor interceptor : interceptors) {\n            interceptor.after(context, currentData);\n        }\n    }\n\n    public void commit(LoadContext context) {\n        if (interceptors == null) {\n            return;\n        }\n\n        for (LoadInterceptor interceptor : interceptors) {\n            interceptor.commit(context);\n        }\n    }\n\n    public void error(LoadContext context) {\n        if (interceptors == null) {\n            return;\n        }\n\n        for (LoadInterceptor interceptor : interceptors) {\n            interceptor.error(context);\n        }\n    }\n\n    public void setInterceptors(List<LoadInterceptor> interceptors) {\n        this.interceptors = interceptors;\n    }\n\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/load/loader/interceptor/LoadInterceptor.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.load.loader.interceptor;\n\nimport java.util.List;\n\nimport com.alibaba.otter.node.etl.common.db.dialect.DbDialect;\n\npublic interface LoadInterceptor<L, D> {\n\n    public void prepare(L context);\n\n    /**\n     * 返回值代表是否需要过滤该记录,true即为过滤不处理\n     */\n    public boolean before(L context, D currentData);\n\n    public void transactionBegin(L context, List<D> currentDatas, DbDialect dialect);\n\n    public void transactionEnd(L context, List<D> currentDatas, DbDialect dialect);\n\n    public void after(L context, D currentData);\n\n    public void commit(L context);\n\n    public void error(L context);\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/load/loader/weight/WeightBarrier.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.load.loader.weight;\n\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.TimeoutException;\nimport java.util.concurrent.locks.Condition;\nimport java.util.concurrent.locks.ReentrantLock;\n\n/**\n * 构建基于weight的barrier控制\n * \n * <pre>\n * 场景：\n *   多个loader模块会进行并行加载，但每个loader的加载数据的进度统一受到weight的调度，只有当前的weight的所有数据都完成后，不同loader中的下一个weight才允许开始\n * \n * 实现：\n * 1. 使用AQS构建了一个基于weight的barrier处理，使用一个state进行控制(代表当前运行<state以下的weight运行)，\n * 2. 多个任务之间通过single(weight)进行协调控制\n * </pre>\n * \n * @author jianghang 2011-11-1 上午11:24:56\n * @version 4.0.0\n */\npublic class WeightBarrier {\n\n    private ReentrantLock lock      = new ReentrantLock();\n    private Condition     condition = lock.newCondition();\n    private volatile long threshold;\n\n    public WeightBarrier(){\n        this(Long.MAX_VALUE);\n    }\n\n    public WeightBarrier(long weight){\n        this.threshold = weight;\n    }\n\n    /**\n     * 阻塞等待weight允许执行\n     * \n     * <pre>\n     * 阻塞返回条件：\n     *  1. 中断事件\n     *  2. 其他线程single()的weight > 当前阻塞等待的weight\n     * </pre>\n     * \n     * @throws InterruptedException\n     */\n    public void await(long weight) throws InterruptedException {\n        try {\n            lock.lockInterruptibly();\n            while (isPermit(weight) == false) {\n                condition.await();\n            }\n        } finally {\n            lock.unlock();\n        }\n    }\n\n    /**\n     * 阻塞等待当前的weight处理,允许设置超时时间\n     * \n     * <pre>\n     * 阻塞返回条件：\n     *  1. 中断事件\n     *  2. 其他线程single()的weight > 当前阻塞等待的weight\n     *  3. 超时\n     * </pre>\n     * \n     * @param timeout\n     * @param unit\n     * @throws InterruptedException\n     * @throws TimeoutException\n     */\n    public void await(long weight, long timeout, TimeUnit unit) throws InterruptedException, TimeoutException {\n        try {\n            lock.lockInterruptibly();\n            while (isPermit(weight) == false) {\n                condition.await(timeout, unit);\n            }\n        } finally {\n            lock.unlock();\n        }\n    }\n\n    /**\n     * 重新设置weight信息\n     * \n     * @throws InterruptedException\n     */\n    public void single(long weight) throws InterruptedException {\n        try {\n            lock.lockInterruptibly();\n            threshold = weight;\n            condition.signalAll();\n        } finally {\n            lock.unlock();\n        }\n    }\n\n    public long state() {\n        return threshold;\n    }\n\n    private boolean isPermit(long state) {\n        return state <= state();\n    }\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/load/loader/weight/WeightBuckets.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.load.loader.weight;\n\nimport java.util.ArrayList;\nimport java.util.LinkedList;\nimport java.util.List;\n\nimport com.google.common.base.Function;\nimport com.google.common.collect.Lists;\n\n/**\n * buckets的集合操作对象\n * \n * @author jianghang 2011-11-1 上午07:37:05\n * @version 4.0.0\n */\npublic class WeightBuckets<T> {\n\n    private List<WeightBucket<T>> buckets = new ArrayList<WeightBucket<T>>(); // 对应的桶信息\n\n    /**\n     * 获取对应的weight的列表，从小到大的排序结果\n     */\n    public synchronized List<Long> weights() {\n        return Lists.transform(buckets, new Function<WeightBucket<T>, Long>() {\n\n            public Long apply(WeightBucket<T> input) {\n                return input.getWeight();\n            }\n        });\n    }\n\n    /**\n     * 添加一个节点\n     */\n    public synchronized void addItem(long weight, T item) {\n        WeightBucket<T> bucket = new WeightBucket<T>(weight);\n        int index = indexedSearch(buckets, bucket);\n        if (index > buckets.size() - 1) {// 先加一个bucket\n            bucket.addLastItem(item);\n            buckets.add(index, bucket);\n        } else if (buckets.get(index).getWeight() != weight) {// 不匹配的\n            bucket.addLastItem(item);\n            buckets.add(index, bucket);\n        } else {\n            buckets.get(index).addLastItem(item);// 添加到已有的bucket上\n        }\n\n    }\n\n    public synchronized List<T> getItems(long weight) {\n        WeightBucket<T> bucket = new WeightBucket<T>(weight);\n        int index = indexedSearch(buckets, bucket);\n        if (index < buckets.size() && index >= 0) {\n            return buckets.get(index).getBucket();\n        } else {\n            return new LinkedList<T>();\n        }\n    }\n\n    // ========================= helper method =====================\n\n    private int indexedSearch(List<WeightBucket<T>> list, WeightBucket<T> item) {\n        int i = 0;\n        for (; i < list.size(); i++) {\n            Comparable midVal = list.get(i);\n            int cmp = midVal.compareTo(item);\n            if (cmp == 0) {// item等于中间值\n                return i;\n            } else if (cmp > 0) {// item比中间值小\n                return i;\n            } else if (cmp < 0) {// item比中间值大\n                // next\n            }\n        }\n\n        return i;\n    }\n\n}\n\n/**\n * 相同weight的item集合对象\n * \n * @author jianghang 2011-11-1 上午11:09:58\n * @version 4.0.0\n * @param <T>\n */\nclass WeightBucket<T> implements Comparable<WeightBucket> {\n\n    private long          weight = -1;\n    private LinkedList<T> bucket = new LinkedList<T>();\n\n    public WeightBucket(){\n    }\n\n    public WeightBucket(long weight){\n        this.weight = weight;\n    }\n\n    public long getWeight() {\n        return weight;\n    }\n\n    public void setWeight(long weight) {\n        this.weight = weight;\n    }\n\n    public List<T> getBucket() {\n        return bucket;\n    }\n\n    public void setBucket(LinkedList<T> bucket) {\n        this.bucket = bucket;\n    }\n\n    public void addFirstItem(T item) {\n        this.bucket.addFirst(item);\n    }\n\n    public T getFirstItem() {\n        return this.bucket.getFirst();\n    }\n\n    public void addLastItem(T item) {\n        this.bucket.addLast(item);\n    }\n\n    public T getLastItem() {\n        return this.bucket.getLast();\n    }\n\n    public int compareTo(WeightBucket o) {\n        if (this.getWeight() > o.getWeight()) {\n            return 1;\n        } else if (this.getWeight() == o.getWeight()) {\n            return 0;\n        } else {\n            return -1;\n        }\n    }\n\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/load/loader/weight/WeightController.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.load.loader.weight;\n\nimport java.util.List;\nimport java.util.concurrent.BlockingQueue;\nimport java.util.concurrent.PriorityBlockingQueue;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.TimeoutException;\nimport java.util.concurrent.atomic.AtomicInteger;\n\n/**\n * 权重控制器\n * \n * @author jianghang 2011-11-2 上午10:34:19\n * @version 4.0.0\n */\npublic class WeightController {\n\n    private AtomicInteger       latch;\n    private WeightBarrier       barrier;\n    private BlockingQueue<Long> weights = new PriorityBlockingQueue<Long>();\n\n    public WeightController(int load){\n        latch = new AtomicInteger(load);\n        barrier = new WeightBarrier(Integer.MIN_VALUE);\n    }\n\n    /**\n     * 每个loader任务报告启动的第一个任务的weight\n     * \n     * @throws InterruptedException\n     */\n    public synchronized void start(List<Long> weights) throws InterruptedException {\n        for (int i = 0; i < weights.size(); i++) {\n            this.weights.add(weights.get(i));\n        }\n\n        int number = latch.decrementAndGet();\n        if (number == 0) {\n            Long initWeight = this.weights.peek();\n            if (initWeight != null) {\n                barrier.single(initWeight);\n            }\n        }\n    }\n\n    /**\n     * 等待自己当前的weight任务可以被执行\n     * \n     * @throws InterruptedException\n     */\n    public void await(long weight) throws InterruptedException {\n        barrier.await(weight);\n    }\n\n    /**\n     * 等待自己当前的weight任务可以被执行,带超时控制\n     * \n     * @throws InterruptedException\n     * @throws TimeoutException\n     */\n    public void await(long weight, long timeout, TimeUnit unit) throws InterruptedException, TimeoutException {\n        barrier.await(weight, timeout, unit);\n    }\n\n    /**\n     * 通知下一个weight任务可以被执行\n     * \n     * @throws InterruptedException\n     */\n    public synchronized void single(long weight) throws InterruptedException {\n        this.weights.remove(weight);\n        // 触发下一个可运行的weight\n        Long nextWeight = this.weights.peek();\n        if (nextWeight != null) {\n            barrier.single(nextWeight);\n        }\n    }\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/model/protobuf/BatchProto.java",
    "content": "// Generated by the protocol buffer compiler.  DO NOT EDIT!\n// source: Batch.proto\n\npackage com.alibaba.otter.node.etl.model.protobuf;\n\npublic final class BatchProto {\n  private BatchProto() {}\n  public static void registerAllExtensions(\n      com.google.protobuf.ExtensionRegistry registry) {\n  }\n  public interface IdentityOrBuilder extends\n      // @@protoc_insertion_point(interface_extends:com.alibaba.otter.node.etl.model.protobuf.Identity)\n      com.google.protobuf.MessageOrBuilder {\n\n    /**\n     * <code>optional int64 channelId = 1;</code>\n     *\n     * <pre>\n     **通道标示*\n     * </pre>\n     */\n    boolean hasChannelId();\n    /**\n     * <code>optional int64 channelId = 1;</code>\n     *\n     * <pre>\n     **通道标示*\n     * </pre>\n     */\n    long getChannelId();\n\n    /**\n     * <code>optional int64 pipelineId = 2;</code>\n     */\n    boolean hasPipelineId();\n    /**\n     * <code>optional int64 pipelineId = 2;</code>\n     */\n    long getPipelineId();\n\n    /**\n     * <code>optional int64 processId = 3;</code>\n     */\n    boolean hasProcessId();\n    /**\n     * <code>optional int64 processId = 3;</code>\n     */\n    long getProcessId();\n  }\n  /**\n   * Protobuf type {@code com.alibaba.otter.node.etl.model.protobuf.Identity}\n   *\n   * <pre>\n   **同步数据表示对象*\n   * </pre>\n   */\n  public static final class Identity extends\n      com.google.protobuf.GeneratedMessage implements\n      // @@protoc_insertion_point(message_implements:com.alibaba.otter.node.etl.model.protobuf.Identity)\n      IdentityOrBuilder {\n    // Use Identity.newBuilder() to construct.\n    private Identity(com.google.protobuf.GeneratedMessage.Builder<?> builder) {\n      super(builder);\n      this.unknownFields = builder.getUnknownFields();\n    }\n    private Identity(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); }\n\n    private static final Identity defaultInstance;\n    public static Identity getDefaultInstance() {\n      return defaultInstance;\n    }\n\n    public Identity getDefaultInstanceForType() {\n      return defaultInstance;\n    }\n\n    private final com.google.protobuf.UnknownFieldSet unknownFields;\n    @java.lang.Override\n    public final com.google.protobuf.UnknownFieldSet\n        getUnknownFields() {\n      return this.unknownFields;\n    }\n    private Identity(\n        com.google.protobuf.CodedInputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      initFields();\n      int mutable_bitField0_ = 0;\n      com.google.protobuf.UnknownFieldSet.Builder unknownFields =\n          com.google.protobuf.UnknownFieldSet.newBuilder();\n      try {\n        boolean done = false;\n        while (!done) {\n          int tag = input.readTag();\n          switch (tag) {\n            case 0:\n              done = true;\n              break;\n            default: {\n              if (!parseUnknownField(input, unknownFields,\n                                     extensionRegistry, tag)) {\n                done = true;\n              }\n              break;\n            }\n            case 8: {\n              bitField0_ |= 0x00000001;\n              channelId_ = input.readInt64();\n              break;\n            }\n            case 16: {\n              bitField0_ |= 0x00000002;\n              pipelineId_ = input.readInt64();\n              break;\n            }\n            case 24: {\n              bitField0_ |= 0x00000004;\n              processId_ = input.readInt64();\n              break;\n            }\n          }\n        }\n      } catch (com.google.protobuf.InvalidProtocolBufferException e) {\n        throw e.setUnfinishedMessage(this);\n      } catch (java.io.IOException e) {\n        throw new com.google.protobuf.InvalidProtocolBufferException(\n            e.getMessage()).setUnfinishedMessage(this);\n      } finally {\n        this.unknownFields = unknownFields.build();\n        makeExtensionsImmutable();\n      }\n    }\n    public static final com.google.protobuf.Descriptors.Descriptor\n        getDescriptor() {\n      return com.alibaba.otter.node.etl.model.protobuf.BatchProto.internal_static_com_alibaba_otter_node_etl_model_protobuf_Identity_descriptor;\n    }\n\n    protected com.google.protobuf.GeneratedMessage.FieldAccessorTable\n        internalGetFieldAccessorTable() {\n      return com.alibaba.otter.node.etl.model.protobuf.BatchProto.internal_static_com_alibaba_otter_node_etl_model_protobuf_Identity_fieldAccessorTable\n          .ensureFieldAccessorsInitialized(\n              com.alibaba.otter.node.etl.model.protobuf.BatchProto.Identity.class, com.alibaba.otter.node.etl.model.protobuf.BatchProto.Identity.Builder.class);\n    }\n\n    public static com.google.protobuf.Parser<Identity> PARSER =\n        new com.google.protobuf.AbstractParser<Identity>() {\n      public Identity parsePartialFrom(\n          com.google.protobuf.CodedInputStream input,\n          com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n          throws com.google.protobuf.InvalidProtocolBufferException {\n        return new Identity(input, extensionRegistry);\n      }\n    };\n\n    @java.lang.Override\n    public com.google.protobuf.Parser<Identity> getParserForType() {\n      return PARSER;\n    }\n\n    private int bitField0_;\n    public static final int CHANNELID_FIELD_NUMBER = 1;\n    private long channelId_;\n    /**\n     * <code>optional int64 channelId = 1;</code>\n     *\n     * <pre>\n     **通道标示*\n     * </pre>\n     */\n    public boolean hasChannelId() {\n      return ((bitField0_ & 0x00000001) == 0x00000001);\n    }\n    /**\n     * <code>optional int64 channelId = 1;</code>\n     *\n     * <pre>\n     **通道标示*\n     * </pre>\n     */\n    public long getChannelId() {\n      return channelId_;\n    }\n\n    public static final int PIPELINEID_FIELD_NUMBER = 2;\n    private long pipelineId_;\n    /**\n     * <code>optional int64 pipelineId = 2;</code>\n     */\n    public boolean hasPipelineId() {\n      return ((bitField0_ & 0x00000002) == 0x00000002);\n    }\n    /**\n     * <code>optional int64 pipelineId = 2;</code>\n     */\n    public long getPipelineId() {\n      return pipelineId_;\n    }\n\n    public static final int PROCESSID_FIELD_NUMBER = 3;\n    private long processId_;\n    /**\n     * <code>optional int64 processId = 3;</code>\n     */\n    public boolean hasProcessId() {\n      return ((bitField0_ & 0x00000004) == 0x00000004);\n    }\n    /**\n     * <code>optional int64 processId = 3;</code>\n     */\n    public long getProcessId() {\n      return processId_;\n    }\n\n    private void initFields() {\n      channelId_ = 0L;\n      pipelineId_ = 0L;\n      processId_ = 0L;\n    }\n    private byte memoizedIsInitialized = -1;\n    public final boolean isInitialized() {\n      byte isInitialized = memoizedIsInitialized;\n      if (isInitialized == 1) return true;\n      if (isInitialized == 0) return false;\n\n      memoizedIsInitialized = 1;\n      return true;\n    }\n\n    public void writeTo(com.google.protobuf.CodedOutputStream output)\n                        throws java.io.IOException {\n      getSerializedSize();\n      if (((bitField0_ & 0x00000001) == 0x00000001)) {\n        output.writeInt64(1, channelId_);\n      }\n      if (((bitField0_ & 0x00000002) == 0x00000002)) {\n        output.writeInt64(2, pipelineId_);\n      }\n      if (((bitField0_ & 0x00000004) == 0x00000004)) {\n        output.writeInt64(3, processId_);\n      }\n      getUnknownFields().writeTo(output);\n    }\n\n    private int memoizedSerializedSize = -1;\n    public int getSerializedSize() {\n      int size = memoizedSerializedSize;\n      if (size != -1) return size;\n\n      size = 0;\n      if (((bitField0_ & 0x00000001) == 0x00000001)) {\n        size += com.google.protobuf.CodedOutputStream\n          .computeInt64Size(1, channelId_);\n      }\n      if (((bitField0_ & 0x00000002) == 0x00000002)) {\n        size += com.google.protobuf.CodedOutputStream\n          .computeInt64Size(2, pipelineId_);\n      }\n      if (((bitField0_ & 0x00000004) == 0x00000004)) {\n        size += com.google.protobuf.CodedOutputStream\n          .computeInt64Size(3, processId_);\n      }\n      size += getUnknownFields().getSerializedSize();\n      memoizedSerializedSize = size;\n      return size;\n    }\n\n    private static final long serialVersionUID = 0L;\n    @java.lang.Override\n    protected java.lang.Object writeReplace()\n        throws java.io.ObjectStreamException {\n      return super.writeReplace();\n    }\n\n    public static com.alibaba.otter.node.etl.model.protobuf.BatchProto.Identity parseFrom(\n        com.google.protobuf.ByteString data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return PARSER.parseFrom(data);\n    }\n    public static com.alibaba.otter.node.etl.model.protobuf.BatchProto.Identity parseFrom(\n        com.google.protobuf.ByteString data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return PARSER.parseFrom(data, extensionRegistry);\n    }\n    public static com.alibaba.otter.node.etl.model.protobuf.BatchProto.Identity parseFrom(byte[] data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return PARSER.parseFrom(data);\n    }\n    public static com.alibaba.otter.node.etl.model.protobuf.BatchProto.Identity parseFrom(\n        byte[] data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return PARSER.parseFrom(data, extensionRegistry);\n    }\n    public static com.alibaba.otter.node.etl.model.protobuf.BatchProto.Identity parseFrom(java.io.InputStream input)\n        throws java.io.IOException {\n      return PARSER.parseFrom(input);\n    }\n    public static com.alibaba.otter.node.etl.model.protobuf.BatchProto.Identity parseFrom(\n        java.io.InputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return PARSER.parseFrom(input, extensionRegistry);\n    }\n    public static com.alibaba.otter.node.etl.model.protobuf.BatchProto.Identity parseDelimitedFrom(java.io.InputStream input)\n        throws java.io.IOException {\n      return PARSER.parseDelimitedFrom(input);\n    }\n    public static com.alibaba.otter.node.etl.model.protobuf.BatchProto.Identity parseDelimitedFrom(\n        java.io.InputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return PARSER.parseDelimitedFrom(input, extensionRegistry);\n    }\n    public static com.alibaba.otter.node.etl.model.protobuf.BatchProto.Identity parseFrom(\n        com.google.protobuf.CodedInputStream input)\n        throws java.io.IOException {\n      return PARSER.parseFrom(input);\n    }\n    public static com.alibaba.otter.node.etl.model.protobuf.BatchProto.Identity parseFrom(\n        com.google.protobuf.CodedInputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return PARSER.parseFrom(input, extensionRegistry);\n    }\n\n    public static Builder newBuilder() { return Builder.create(); }\n    public Builder newBuilderForType() { return newBuilder(); }\n    public static Builder newBuilder(com.alibaba.otter.node.etl.model.protobuf.BatchProto.Identity prototype) {\n      return newBuilder().mergeFrom(prototype);\n    }\n    public Builder toBuilder() { return newBuilder(this); }\n\n    @java.lang.Override\n    protected Builder newBuilderForType(\n        com.google.protobuf.GeneratedMessage.BuilderParent parent) {\n      Builder builder = new Builder(parent);\n      return builder;\n    }\n    /**\n     * Protobuf type {@code com.alibaba.otter.node.etl.model.protobuf.Identity}\n     *\n     * <pre>\n     **同步数据表示对象*\n     * </pre>\n     */\n    public static final class Builder extends\n        com.google.protobuf.GeneratedMessage.Builder<Builder> implements\n        // @@protoc_insertion_point(builder_implements:com.alibaba.otter.node.etl.model.protobuf.Identity)\n        com.alibaba.otter.node.etl.model.protobuf.BatchProto.IdentityOrBuilder {\n      public static final com.google.protobuf.Descriptors.Descriptor\n          getDescriptor() {\n        return com.alibaba.otter.node.etl.model.protobuf.BatchProto.internal_static_com_alibaba_otter_node_etl_model_protobuf_Identity_descriptor;\n      }\n\n      protected com.google.protobuf.GeneratedMessage.FieldAccessorTable\n          internalGetFieldAccessorTable() {\n        return com.alibaba.otter.node.etl.model.protobuf.BatchProto.internal_static_com_alibaba_otter_node_etl_model_protobuf_Identity_fieldAccessorTable\n            .ensureFieldAccessorsInitialized(\n                com.alibaba.otter.node.etl.model.protobuf.BatchProto.Identity.class, com.alibaba.otter.node.etl.model.protobuf.BatchProto.Identity.Builder.class);\n      }\n\n      // Construct using com.alibaba.otter.node.etl.model.protobuf.BatchProto.Identity.newBuilder()\n      private Builder() {\n        maybeForceBuilderInitialization();\n      }\n\n      private Builder(\n          com.google.protobuf.GeneratedMessage.BuilderParent parent) {\n        super(parent);\n        maybeForceBuilderInitialization();\n      }\n      private void maybeForceBuilderInitialization() {\n        if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) {\n        }\n      }\n      private static Builder create() {\n        return new Builder();\n      }\n\n      public Builder clear() {\n        super.clear();\n        channelId_ = 0L;\n        bitField0_ = (bitField0_ & ~0x00000001);\n        pipelineId_ = 0L;\n        bitField0_ = (bitField0_ & ~0x00000002);\n        processId_ = 0L;\n        bitField0_ = (bitField0_ & ~0x00000004);\n        return this;\n      }\n\n      public Builder clone() {\n        return create().mergeFrom(buildPartial());\n      }\n\n      public com.google.protobuf.Descriptors.Descriptor\n          getDescriptorForType() {\n        return com.alibaba.otter.node.etl.model.protobuf.BatchProto.internal_static_com_alibaba_otter_node_etl_model_protobuf_Identity_descriptor;\n      }\n\n      public com.alibaba.otter.node.etl.model.protobuf.BatchProto.Identity getDefaultInstanceForType() {\n        return com.alibaba.otter.node.etl.model.protobuf.BatchProto.Identity.getDefaultInstance();\n      }\n\n      public com.alibaba.otter.node.etl.model.protobuf.BatchProto.Identity build() {\n        com.alibaba.otter.node.etl.model.protobuf.BatchProto.Identity result = buildPartial();\n        if (!result.isInitialized()) {\n          throw newUninitializedMessageException(result);\n        }\n        return result;\n      }\n\n      public com.alibaba.otter.node.etl.model.protobuf.BatchProto.Identity buildPartial() {\n        com.alibaba.otter.node.etl.model.protobuf.BatchProto.Identity result = new com.alibaba.otter.node.etl.model.protobuf.BatchProto.Identity(this);\n        int from_bitField0_ = bitField0_;\n        int to_bitField0_ = 0;\n        if (((from_bitField0_ & 0x00000001) == 0x00000001)) {\n          to_bitField0_ |= 0x00000001;\n        }\n        result.channelId_ = channelId_;\n        if (((from_bitField0_ & 0x00000002) == 0x00000002)) {\n          to_bitField0_ |= 0x00000002;\n        }\n        result.pipelineId_ = pipelineId_;\n        if (((from_bitField0_ & 0x00000004) == 0x00000004)) {\n          to_bitField0_ |= 0x00000004;\n        }\n        result.processId_ = processId_;\n        result.bitField0_ = to_bitField0_;\n        onBuilt();\n        return result;\n      }\n\n      public Builder mergeFrom(com.google.protobuf.Message other) {\n        if (other instanceof com.alibaba.otter.node.etl.model.protobuf.BatchProto.Identity) {\n          return mergeFrom((com.alibaba.otter.node.etl.model.protobuf.BatchProto.Identity)other);\n        } else {\n          super.mergeFrom(other);\n          return this;\n        }\n      }\n\n      public Builder mergeFrom(com.alibaba.otter.node.etl.model.protobuf.BatchProto.Identity other) {\n        if (other == com.alibaba.otter.node.etl.model.protobuf.BatchProto.Identity.getDefaultInstance()) return this;\n        if (other.hasChannelId()) {\n          setChannelId(other.getChannelId());\n        }\n        if (other.hasPipelineId()) {\n          setPipelineId(other.getPipelineId());\n        }\n        if (other.hasProcessId()) {\n          setProcessId(other.getProcessId());\n        }\n        this.mergeUnknownFields(other.getUnknownFields());\n        return this;\n      }\n\n      public final boolean isInitialized() {\n        return true;\n      }\n\n      public Builder mergeFrom(\n          com.google.protobuf.CodedInputStream input,\n          com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n          throws java.io.IOException {\n        com.alibaba.otter.node.etl.model.protobuf.BatchProto.Identity parsedMessage = null;\n        try {\n          parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry);\n        } catch (com.google.protobuf.InvalidProtocolBufferException e) {\n          parsedMessage = (com.alibaba.otter.node.etl.model.protobuf.BatchProto.Identity) e.getUnfinishedMessage();\n          throw e;\n        } finally {\n          if (parsedMessage != null) {\n            mergeFrom(parsedMessage);\n          }\n        }\n        return this;\n      }\n      private int bitField0_;\n\n      private long channelId_ ;\n      /**\n       * <code>optional int64 channelId = 1;</code>\n       *\n       * <pre>\n       **通道标示*\n       * </pre>\n       */\n      public boolean hasChannelId() {\n        return ((bitField0_ & 0x00000001) == 0x00000001);\n      }\n      /**\n       * <code>optional int64 channelId = 1;</code>\n       *\n       * <pre>\n       **通道标示*\n       * </pre>\n       */\n      public long getChannelId() {\n        return channelId_;\n      }\n      /**\n       * <code>optional int64 channelId = 1;</code>\n       *\n       * <pre>\n       **通道标示*\n       * </pre>\n       */\n      public Builder setChannelId(long value) {\n        bitField0_ |= 0x00000001;\n        channelId_ = value;\n        onChanged();\n        return this;\n      }\n      /**\n       * <code>optional int64 channelId = 1;</code>\n       *\n       * <pre>\n       **通道标示*\n       * </pre>\n       */\n      public Builder clearChannelId() {\n        bitField0_ = (bitField0_ & ~0x00000001);\n        channelId_ = 0L;\n        onChanged();\n        return this;\n      }\n\n      private long pipelineId_ ;\n      /**\n       * <code>optional int64 pipelineId = 2;</code>\n       */\n      public boolean hasPipelineId() {\n        return ((bitField0_ & 0x00000002) == 0x00000002);\n      }\n      /**\n       * <code>optional int64 pipelineId = 2;</code>\n       */\n      public long getPipelineId() {\n        return pipelineId_;\n      }\n      /**\n       * <code>optional int64 pipelineId = 2;</code>\n       */\n      public Builder setPipelineId(long value) {\n        bitField0_ |= 0x00000002;\n        pipelineId_ = value;\n        onChanged();\n        return this;\n      }\n      /**\n       * <code>optional int64 pipelineId = 2;</code>\n       */\n      public Builder clearPipelineId() {\n        bitField0_ = (bitField0_ & ~0x00000002);\n        pipelineId_ = 0L;\n        onChanged();\n        return this;\n      }\n\n      private long processId_ ;\n      /**\n       * <code>optional int64 processId = 3;</code>\n       */\n      public boolean hasProcessId() {\n        return ((bitField0_ & 0x00000004) == 0x00000004);\n      }\n      /**\n       * <code>optional int64 processId = 3;</code>\n       */\n      public long getProcessId() {\n        return processId_;\n      }\n      /**\n       * <code>optional int64 processId = 3;</code>\n       */\n      public Builder setProcessId(long value) {\n        bitField0_ |= 0x00000004;\n        processId_ = value;\n        onChanged();\n        return this;\n      }\n      /**\n       * <code>optional int64 processId = 3;</code>\n       */\n      public Builder clearProcessId() {\n        bitField0_ = (bitField0_ & ~0x00000004);\n        processId_ = 0L;\n        onChanged();\n        return this;\n      }\n\n      // @@protoc_insertion_point(builder_scope:com.alibaba.otter.node.etl.model.protobuf.Identity)\n    }\n\n    static {\n      defaultInstance = new Identity(true);\n      defaultInstance.initFields();\n    }\n\n    // @@protoc_insertion_point(class_scope:com.alibaba.otter.node.etl.model.protobuf.Identity)\n  }\n\n  public interface RowBatchOrBuilder extends\n      // @@protoc_insertion_point(interface_extends:com.alibaba.otter.node.etl.model.protobuf.RowBatch)\n      com.google.protobuf.MessageOrBuilder {\n\n    /**\n     * <code>optional .com.alibaba.otter.node.etl.model.protobuf.Identity identity = 1;</code>\n     */\n    boolean hasIdentity();\n    /**\n     * <code>optional .com.alibaba.otter.node.etl.model.protobuf.Identity identity = 1;</code>\n     */\n    com.alibaba.otter.node.etl.model.protobuf.BatchProto.Identity getIdentity();\n    /**\n     * <code>optional .com.alibaba.otter.node.etl.model.protobuf.Identity identity = 1;</code>\n     */\n    com.alibaba.otter.node.etl.model.protobuf.BatchProto.IdentityOrBuilder getIdentityOrBuilder();\n\n    /**\n     * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.RowData rows = 2;</code>\n     *\n     * <pre>\n     **每个batch里面的所有变更数据*\n     * </pre>\n     */\n    java.util.List<com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowData> \n        getRowsList();\n    /**\n     * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.RowData rows = 2;</code>\n     *\n     * <pre>\n     **每个batch里面的所有变更数据*\n     * </pre>\n     */\n    com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowData getRows(int index);\n    /**\n     * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.RowData rows = 2;</code>\n     *\n     * <pre>\n     **每个batch里面的所有变更数据*\n     * </pre>\n     */\n    int getRowsCount();\n    /**\n     * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.RowData rows = 2;</code>\n     *\n     * <pre>\n     **每个batch里面的所有变更数据*\n     * </pre>\n     */\n    java.util.List<? extends com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowDataOrBuilder> \n        getRowsOrBuilderList();\n    /**\n     * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.RowData rows = 2;</code>\n     *\n     * <pre>\n     **每个batch里面的所有变更数据*\n     * </pre>\n     */\n    com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowDataOrBuilder getRowsOrBuilder(\n        int index);\n  }\n  /**\n   * Protobuf type {@code com.alibaba.otter.node.etl.model.protobuf.RowBatch}\n   *\n   * <pre>\n   **数据包*\n   * </pre>\n   */\n  public static final class RowBatch extends\n      com.google.protobuf.GeneratedMessage implements\n      // @@protoc_insertion_point(message_implements:com.alibaba.otter.node.etl.model.protobuf.RowBatch)\n      RowBatchOrBuilder {\n    // Use RowBatch.newBuilder() to construct.\n    private RowBatch(com.google.protobuf.GeneratedMessage.Builder<?> builder) {\n      super(builder);\n      this.unknownFields = builder.getUnknownFields();\n    }\n    private RowBatch(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); }\n\n    private static final RowBatch defaultInstance;\n    public static RowBatch getDefaultInstance() {\n      return defaultInstance;\n    }\n\n    public RowBatch getDefaultInstanceForType() {\n      return defaultInstance;\n    }\n\n    private final com.google.protobuf.UnknownFieldSet unknownFields;\n    @java.lang.Override\n    public final com.google.protobuf.UnknownFieldSet\n        getUnknownFields() {\n      return this.unknownFields;\n    }\n    private RowBatch(\n        com.google.protobuf.CodedInputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      initFields();\n      int mutable_bitField0_ = 0;\n      com.google.protobuf.UnknownFieldSet.Builder unknownFields =\n          com.google.protobuf.UnknownFieldSet.newBuilder();\n      try {\n        boolean done = false;\n        while (!done) {\n          int tag = input.readTag();\n          switch (tag) {\n            case 0:\n              done = true;\n              break;\n            default: {\n              if (!parseUnknownField(input, unknownFields,\n                                     extensionRegistry, tag)) {\n                done = true;\n              }\n              break;\n            }\n            case 10: {\n              com.alibaba.otter.node.etl.model.protobuf.BatchProto.Identity.Builder subBuilder = null;\n              if (((bitField0_ & 0x00000001) == 0x00000001)) {\n                subBuilder = identity_.toBuilder();\n              }\n              identity_ = input.readMessage(com.alibaba.otter.node.etl.model.protobuf.BatchProto.Identity.PARSER, extensionRegistry);\n              if (subBuilder != null) {\n                subBuilder.mergeFrom(identity_);\n                identity_ = subBuilder.buildPartial();\n              }\n              bitField0_ |= 0x00000001;\n              break;\n            }\n            case 18: {\n              if (!((mutable_bitField0_ & 0x00000002) == 0x00000002)) {\n                rows_ = new java.util.ArrayList<com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowData>();\n                mutable_bitField0_ |= 0x00000002;\n              }\n              rows_.add(input.readMessage(com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowData.PARSER, extensionRegistry));\n              break;\n            }\n          }\n        }\n      } catch (com.google.protobuf.InvalidProtocolBufferException e) {\n        throw e.setUnfinishedMessage(this);\n      } catch (java.io.IOException e) {\n        throw new com.google.protobuf.InvalidProtocolBufferException(\n            e.getMessage()).setUnfinishedMessage(this);\n      } finally {\n        if (((mutable_bitField0_ & 0x00000002) == 0x00000002)) {\n          rows_ = java.util.Collections.unmodifiableList(rows_);\n        }\n        this.unknownFields = unknownFields.build();\n        makeExtensionsImmutable();\n      }\n    }\n    public static final com.google.protobuf.Descriptors.Descriptor\n        getDescriptor() {\n      return com.alibaba.otter.node.etl.model.protobuf.BatchProto.internal_static_com_alibaba_otter_node_etl_model_protobuf_RowBatch_descriptor;\n    }\n\n    protected com.google.protobuf.GeneratedMessage.FieldAccessorTable\n        internalGetFieldAccessorTable() {\n      return com.alibaba.otter.node.etl.model.protobuf.BatchProto.internal_static_com_alibaba_otter_node_etl_model_protobuf_RowBatch_fieldAccessorTable\n          .ensureFieldAccessorsInitialized(\n              com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowBatch.class, com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowBatch.Builder.class);\n    }\n\n    public static com.google.protobuf.Parser<RowBatch> PARSER =\n        new com.google.protobuf.AbstractParser<RowBatch>() {\n      public RowBatch parsePartialFrom(\n          com.google.protobuf.CodedInputStream input,\n          com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n          throws com.google.protobuf.InvalidProtocolBufferException {\n        return new RowBatch(input, extensionRegistry);\n      }\n    };\n\n    @java.lang.Override\n    public com.google.protobuf.Parser<RowBatch> getParserForType() {\n      return PARSER;\n    }\n\n    private int bitField0_;\n    public static final int IDENTITY_FIELD_NUMBER = 1;\n    private com.alibaba.otter.node.etl.model.protobuf.BatchProto.Identity identity_;\n    /**\n     * <code>optional .com.alibaba.otter.node.etl.model.protobuf.Identity identity = 1;</code>\n     */\n    public boolean hasIdentity() {\n      return ((bitField0_ & 0x00000001) == 0x00000001);\n    }\n    /**\n     * <code>optional .com.alibaba.otter.node.etl.model.protobuf.Identity identity = 1;</code>\n     */\n    public com.alibaba.otter.node.etl.model.protobuf.BatchProto.Identity getIdentity() {\n      return identity_;\n    }\n    /**\n     * <code>optional .com.alibaba.otter.node.etl.model.protobuf.Identity identity = 1;</code>\n     */\n    public com.alibaba.otter.node.etl.model.protobuf.BatchProto.IdentityOrBuilder getIdentityOrBuilder() {\n      return identity_;\n    }\n\n    public static final int ROWS_FIELD_NUMBER = 2;\n    private java.util.List<com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowData> rows_;\n    /**\n     * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.RowData rows = 2;</code>\n     *\n     * <pre>\n     **每个batch里面的所有变更数据*\n     * </pre>\n     */\n    public java.util.List<com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowData> getRowsList() {\n      return rows_;\n    }\n    /**\n     * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.RowData rows = 2;</code>\n     *\n     * <pre>\n     **每个batch里面的所有变更数据*\n     * </pre>\n     */\n    public java.util.List<? extends com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowDataOrBuilder> \n        getRowsOrBuilderList() {\n      return rows_;\n    }\n    /**\n     * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.RowData rows = 2;</code>\n     *\n     * <pre>\n     **每个batch里面的所有变更数据*\n     * </pre>\n     */\n    public int getRowsCount() {\n      return rows_.size();\n    }\n    /**\n     * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.RowData rows = 2;</code>\n     *\n     * <pre>\n     **每个batch里面的所有变更数据*\n     * </pre>\n     */\n    public com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowData getRows(int index) {\n      return rows_.get(index);\n    }\n    /**\n     * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.RowData rows = 2;</code>\n     *\n     * <pre>\n     **每个batch里面的所有变更数据*\n     * </pre>\n     */\n    public com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowDataOrBuilder getRowsOrBuilder(\n        int index) {\n      return rows_.get(index);\n    }\n\n    private void initFields() {\n      identity_ = com.alibaba.otter.node.etl.model.protobuf.BatchProto.Identity.getDefaultInstance();\n      rows_ = java.util.Collections.emptyList();\n    }\n    private byte memoizedIsInitialized = -1;\n    public final boolean isInitialized() {\n      byte isInitialized = memoizedIsInitialized;\n      if (isInitialized == 1) return true;\n      if (isInitialized == 0) return false;\n\n      memoizedIsInitialized = 1;\n      return true;\n    }\n\n    public void writeTo(com.google.protobuf.CodedOutputStream output)\n                        throws java.io.IOException {\n      getSerializedSize();\n      if (((bitField0_ & 0x00000001) == 0x00000001)) {\n        output.writeMessage(1, identity_);\n      }\n      for (int i = 0; i < rows_.size(); i++) {\n        output.writeMessage(2, rows_.get(i));\n      }\n      getUnknownFields().writeTo(output);\n    }\n\n    private int memoizedSerializedSize = -1;\n    public int getSerializedSize() {\n      int size = memoizedSerializedSize;\n      if (size != -1) return size;\n\n      size = 0;\n      if (((bitField0_ & 0x00000001) == 0x00000001)) {\n        size += com.google.protobuf.CodedOutputStream\n          .computeMessageSize(1, identity_);\n      }\n      for (int i = 0; i < rows_.size(); i++) {\n        size += com.google.protobuf.CodedOutputStream\n          .computeMessageSize(2, rows_.get(i));\n      }\n      size += getUnknownFields().getSerializedSize();\n      memoizedSerializedSize = size;\n      return size;\n    }\n\n    private static final long serialVersionUID = 0L;\n    @java.lang.Override\n    protected java.lang.Object writeReplace()\n        throws java.io.ObjectStreamException {\n      return super.writeReplace();\n    }\n\n    public static com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowBatch parseFrom(\n        com.google.protobuf.ByteString data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return PARSER.parseFrom(data);\n    }\n    public static com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowBatch parseFrom(\n        com.google.protobuf.ByteString data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return PARSER.parseFrom(data, extensionRegistry);\n    }\n    public static com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowBatch parseFrom(byte[] data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return PARSER.parseFrom(data);\n    }\n    public static com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowBatch parseFrom(\n        byte[] data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return PARSER.parseFrom(data, extensionRegistry);\n    }\n    public static com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowBatch parseFrom(java.io.InputStream input)\n        throws java.io.IOException {\n      return PARSER.parseFrom(input);\n    }\n    public static com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowBatch parseFrom(\n        java.io.InputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return PARSER.parseFrom(input, extensionRegistry);\n    }\n    public static com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowBatch parseDelimitedFrom(java.io.InputStream input)\n        throws java.io.IOException {\n      return PARSER.parseDelimitedFrom(input);\n    }\n    public static com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowBatch parseDelimitedFrom(\n        java.io.InputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return PARSER.parseDelimitedFrom(input, extensionRegistry);\n    }\n    public static com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowBatch parseFrom(\n        com.google.protobuf.CodedInputStream input)\n        throws java.io.IOException {\n      return PARSER.parseFrom(input);\n    }\n    public static com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowBatch parseFrom(\n        com.google.protobuf.CodedInputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return PARSER.parseFrom(input, extensionRegistry);\n    }\n\n    public static Builder newBuilder() { return Builder.create(); }\n    public Builder newBuilderForType() { return newBuilder(); }\n    public static Builder newBuilder(com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowBatch prototype) {\n      return newBuilder().mergeFrom(prototype);\n    }\n    public Builder toBuilder() { return newBuilder(this); }\n\n    @java.lang.Override\n    protected Builder newBuilderForType(\n        com.google.protobuf.GeneratedMessage.BuilderParent parent) {\n      Builder builder = new Builder(parent);\n      return builder;\n    }\n    /**\n     * Protobuf type {@code com.alibaba.otter.node.etl.model.protobuf.RowBatch}\n     *\n     * <pre>\n     **数据包*\n     * </pre>\n     */\n    public static final class Builder extends\n        com.google.protobuf.GeneratedMessage.Builder<Builder> implements\n        // @@protoc_insertion_point(builder_implements:com.alibaba.otter.node.etl.model.protobuf.RowBatch)\n        com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowBatchOrBuilder {\n      public static final com.google.protobuf.Descriptors.Descriptor\n          getDescriptor() {\n        return com.alibaba.otter.node.etl.model.protobuf.BatchProto.internal_static_com_alibaba_otter_node_etl_model_protobuf_RowBatch_descriptor;\n      }\n\n      protected com.google.protobuf.GeneratedMessage.FieldAccessorTable\n          internalGetFieldAccessorTable() {\n        return com.alibaba.otter.node.etl.model.protobuf.BatchProto.internal_static_com_alibaba_otter_node_etl_model_protobuf_RowBatch_fieldAccessorTable\n            .ensureFieldAccessorsInitialized(\n                com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowBatch.class, com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowBatch.Builder.class);\n      }\n\n      // Construct using com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowBatch.newBuilder()\n      private Builder() {\n        maybeForceBuilderInitialization();\n      }\n\n      private Builder(\n          com.google.protobuf.GeneratedMessage.BuilderParent parent) {\n        super(parent);\n        maybeForceBuilderInitialization();\n      }\n      private void maybeForceBuilderInitialization() {\n        if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) {\n          getIdentityFieldBuilder();\n          getRowsFieldBuilder();\n        }\n      }\n      private static Builder create() {\n        return new Builder();\n      }\n\n      public Builder clear() {\n        super.clear();\n        if (identityBuilder_ == null) {\n          identity_ = com.alibaba.otter.node.etl.model.protobuf.BatchProto.Identity.getDefaultInstance();\n        } else {\n          identityBuilder_.clear();\n        }\n        bitField0_ = (bitField0_ & ~0x00000001);\n        if (rowsBuilder_ == null) {\n          rows_ = java.util.Collections.emptyList();\n          bitField0_ = (bitField0_ & ~0x00000002);\n        } else {\n          rowsBuilder_.clear();\n        }\n        return this;\n      }\n\n      public Builder clone() {\n        return create().mergeFrom(buildPartial());\n      }\n\n      public com.google.protobuf.Descriptors.Descriptor\n          getDescriptorForType() {\n        return com.alibaba.otter.node.etl.model.protobuf.BatchProto.internal_static_com_alibaba_otter_node_etl_model_protobuf_RowBatch_descriptor;\n      }\n\n      public com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowBatch getDefaultInstanceForType() {\n        return com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowBatch.getDefaultInstance();\n      }\n\n      public com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowBatch build() {\n        com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowBatch result = buildPartial();\n        if (!result.isInitialized()) {\n          throw newUninitializedMessageException(result);\n        }\n        return result;\n      }\n\n      public com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowBatch buildPartial() {\n        com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowBatch result = new com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowBatch(this);\n        int from_bitField0_ = bitField0_;\n        int to_bitField0_ = 0;\n        if (((from_bitField0_ & 0x00000001) == 0x00000001)) {\n          to_bitField0_ |= 0x00000001;\n        }\n        if (identityBuilder_ == null) {\n          result.identity_ = identity_;\n        } else {\n          result.identity_ = identityBuilder_.build();\n        }\n        if (rowsBuilder_ == null) {\n          if (((bitField0_ & 0x00000002) == 0x00000002)) {\n            rows_ = java.util.Collections.unmodifiableList(rows_);\n            bitField0_ = (bitField0_ & ~0x00000002);\n          }\n          result.rows_ = rows_;\n        } else {\n          result.rows_ = rowsBuilder_.build();\n        }\n        result.bitField0_ = to_bitField0_;\n        onBuilt();\n        return result;\n      }\n\n      public Builder mergeFrom(com.google.protobuf.Message other) {\n        if (other instanceof com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowBatch) {\n          return mergeFrom((com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowBatch)other);\n        } else {\n          super.mergeFrom(other);\n          return this;\n        }\n      }\n\n      public Builder mergeFrom(com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowBatch other) {\n        if (other == com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowBatch.getDefaultInstance()) return this;\n        if (other.hasIdentity()) {\n          mergeIdentity(other.getIdentity());\n        }\n        if (rowsBuilder_ == null) {\n          if (!other.rows_.isEmpty()) {\n            if (rows_.isEmpty()) {\n              rows_ = other.rows_;\n              bitField0_ = (bitField0_ & ~0x00000002);\n            } else {\n              ensureRowsIsMutable();\n              rows_.addAll(other.rows_);\n            }\n            onChanged();\n          }\n        } else {\n          if (!other.rows_.isEmpty()) {\n            if (rowsBuilder_.isEmpty()) {\n              rowsBuilder_.dispose();\n              rowsBuilder_ = null;\n              rows_ = other.rows_;\n              bitField0_ = (bitField0_ & ~0x00000002);\n              rowsBuilder_ = \n                com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ?\n                   getRowsFieldBuilder() : null;\n            } else {\n              rowsBuilder_.addAllMessages(other.rows_);\n            }\n          }\n        }\n        this.mergeUnknownFields(other.getUnknownFields());\n        return this;\n      }\n\n      public final boolean isInitialized() {\n        return true;\n      }\n\n      public Builder mergeFrom(\n          com.google.protobuf.CodedInputStream input,\n          com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n          throws java.io.IOException {\n        com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowBatch parsedMessage = null;\n        try {\n          parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry);\n        } catch (com.google.protobuf.InvalidProtocolBufferException e) {\n          parsedMessage = (com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowBatch) e.getUnfinishedMessage();\n          throw e;\n        } finally {\n          if (parsedMessage != null) {\n            mergeFrom(parsedMessage);\n          }\n        }\n        return this;\n      }\n      private int bitField0_;\n\n      private com.alibaba.otter.node.etl.model.protobuf.BatchProto.Identity identity_ = com.alibaba.otter.node.etl.model.protobuf.BatchProto.Identity.getDefaultInstance();\n      private com.google.protobuf.SingleFieldBuilder<\n          com.alibaba.otter.node.etl.model.protobuf.BatchProto.Identity, com.alibaba.otter.node.etl.model.protobuf.BatchProto.Identity.Builder, com.alibaba.otter.node.etl.model.protobuf.BatchProto.IdentityOrBuilder> identityBuilder_;\n      /**\n       * <code>optional .com.alibaba.otter.node.etl.model.protobuf.Identity identity = 1;</code>\n       */\n      public boolean hasIdentity() {\n        return ((bitField0_ & 0x00000001) == 0x00000001);\n      }\n      /**\n       * <code>optional .com.alibaba.otter.node.etl.model.protobuf.Identity identity = 1;</code>\n       */\n      public com.alibaba.otter.node.etl.model.protobuf.BatchProto.Identity getIdentity() {\n        if (identityBuilder_ == null) {\n          return identity_;\n        } else {\n          return identityBuilder_.getMessage();\n        }\n      }\n      /**\n       * <code>optional .com.alibaba.otter.node.etl.model.protobuf.Identity identity = 1;</code>\n       */\n      public Builder setIdentity(com.alibaba.otter.node.etl.model.protobuf.BatchProto.Identity value) {\n        if (identityBuilder_ == null) {\n          if (value == null) {\n            throw new NullPointerException();\n          }\n          identity_ = value;\n          onChanged();\n        } else {\n          identityBuilder_.setMessage(value);\n        }\n        bitField0_ |= 0x00000001;\n        return this;\n      }\n      /**\n       * <code>optional .com.alibaba.otter.node.etl.model.protobuf.Identity identity = 1;</code>\n       */\n      public Builder setIdentity(\n          com.alibaba.otter.node.etl.model.protobuf.BatchProto.Identity.Builder builderForValue) {\n        if (identityBuilder_ == null) {\n          identity_ = builderForValue.build();\n          onChanged();\n        } else {\n          identityBuilder_.setMessage(builderForValue.build());\n        }\n        bitField0_ |= 0x00000001;\n        return this;\n      }\n      /**\n       * <code>optional .com.alibaba.otter.node.etl.model.protobuf.Identity identity = 1;</code>\n       */\n      public Builder mergeIdentity(com.alibaba.otter.node.etl.model.protobuf.BatchProto.Identity value) {\n        if (identityBuilder_ == null) {\n          if (((bitField0_ & 0x00000001) == 0x00000001) &&\n              identity_ != com.alibaba.otter.node.etl.model.protobuf.BatchProto.Identity.getDefaultInstance()) {\n            identity_ =\n              com.alibaba.otter.node.etl.model.protobuf.BatchProto.Identity.newBuilder(identity_).mergeFrom(value).buildPartial();\n          } else {\n            identity_ = value;\n          }\n          onChanged();\n        } else {\n          identityBuilder_.mergeFrom(value);\n        }\n        bitField0_ |= 0x00000001;\n        return this;\n      }\n      /**\n       * <code>optional .com.alibaba.otter.node.etl.model.protobuf.Identity identity = 1;</code>\n       */\n      public Builder clearIdentity() {\n        if (identityBuilder_ == null) {\n          identity_ = com.alibaba.otter.node.etl.model.protobuf.BatchProto.Identity.getDefaultInstance();\n          onChanged();\n        } else {\n          identityBuilder_.clear();\n        }\n        bitField0_ = (bitField0_ & ~0x00000001);\n        return this;\n      }\n      /**\n       * <code>optional .com.alibaba.otter.node.etl.model.protobuf.Identity identity = 1;</code>\n       */\n      public com.alibaba.otter.node.etl.model.protobuf.BatchProto.Identity.Builder getIdentityBuilder() {\n        bitField0_ |= 0x00000001;\n        onChanged();\n        return getIdentityFieldBuilder().getBuilder();\n      }\n      /**\n       * <code>optional .com.alibaba.otter.node.etl.model.protobuf.Identity identity = 1;</code>\n       */\n      public com.alibaba.otter.node.etl.model.protobuf.BatchProto.IdentityOrBuilder getIdentityOrBuilder() {\n        if (identityBuilder_ != null) {\n          return identityBuilder_.getMessageOrBuilder();\n        } else {\n          return identity_;\n        }\n      }\n      /**\n       * <code>optional .com.alibaba.otter.node.etl.model.protobuf.Identity identity = 1;</code>\n       */\n      private com.google.protobuf.SingleFieldBuilder<\n          com.alibaba.otter.node.etl.model.protobuf.BatchProto.Identity, com.alibaba.otter.node.etl.model.protobuf.BatchProto.Identity.Builder, com.alibaba.otter.node.etl.model.protobuf.BatchProto.IdentityOrBuilder> \n          getIdentityFieldBuilder() {\n        if (identityBuilder_ == null) {\n          identityBuilder_ = new com.google.protobuf.SingleFieldBuilder<\n              com.alibaba.otter.node.etl.model.protobuf.BatchProto.Identity, com.alibaba.otter.node.etl.model.protobuf.BatchProto.Identity.Builder, com.alibaba.otter.node.etl.model.protobuf.BatchProto.IdentityOrBuilder>(\n                  getIdentity(),\n                  getParentForChildren(),\n                  isClean());\n          identity_ = null;\n        }\n        return identityBuilder_;\n      }\n\n      private java.util.List<com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowData> rows_ =\n        java.util.Collections.emptyList();\n      private void ensureRowsIsMutable() {\n        if (!((bitField0_ & 0x00000002) == 0x00000002)) {\n          rows_ = new java.util.ArrayList<com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowData>(rows_);\n          bitField0_ |= 0x00000002;\n         }\n      }\n\n      private com.google.protobuf.RepeatedFieldBuilder<\n          com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowData, com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowData.Builder, com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowDataOrBuilder> rowsBuilder_;\n\n      /**\n       * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.RowData rows = 2;</code>\n       *\n       * <pre>\n       **每个batch里面的所有变更数据*\n       * </pre>\n       */\n      public java.util.List<com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowData> getRowsList() {\n        if (rowsBuilder_ == null) {\n          return java.util.Collections.unmodifiableList(rows_);\n        } else {\n          return rowsBuilder_.getMessageList();\n        }\n      }\n      /**\n       * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.RowData rows = 2;</code>\n       *\n       * <pre>\n       **每个batch里面的所有变更数据*\n       * </pre>\n       */\n      public int getRowsCount() {\n        if (rowsBuilder_ == null) {\n          return rows_.size();\n        } else {\n          return rowsBuilder_.getCount();\n        }\n      }\n      /**\n       * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.RowData rows = 2;</code>\n       *\n       * <pre>\n       **每个batch里面的所有变更数据*\n       * </pre>\n       */\n      public com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowData getRows(int index) {\n        if (rowsBuilder_ == null) {\n          return rows_.get(index);\n        } else {\n          return rowsBuilder_.getMessage(index);\n        }\n      }\n      /**\n       * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.RowData rows = 2;</code>\n       *\n       * <pre>\n       **每个batch里面的所有变更数据*\n       * </pre>\n       */\n      public Builder setRows(\n          int index, com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowData value) {\n        if (rowsBuilder_ == null) {\n          if (value == null) {\n            throw new NullPointerException();\n          }\n          ensureRowsIsMutable();\n          rows_.set(index, value);\n          onChanged();\n        } else {\n          rowsBuilder_.setMessage(index, value);\n        }\n        return this;\n      }\n      /**\n       * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.RowData rows = 2;</code>\n       *\n       * <pre>\n       **每个batch里面的所有变更数据*\n       * </pre>\n       */\n      public Builder setRows(\n          int index, com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowData.Builder builderForValue) {\n        if (rowsBuilder_ == null) {\n          ensureRowsIsMutable();\n          rows_.set(index, builderForValue.build());\n          onChanged();\n        } else {\n          rowsBuilder_.setMessage(index, builderForValue.build());\n        }\n        return this;\n      }\n      /**\n       * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.RowData rows = 2;</code>\n       *\n       * <pre>\n       **每个batch里面的所有变更数据*\n       * </pre>\n       */\n      public Builder addRows(com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowData value) {\n        if (rowsBuilder_ == null) {\n          if (value == null) {\n            throw new NullPointerException();\n          }\n          ensureRowsIsMutable();\n          rows_.add(value);\n          onChanged();\n        } else {\n          rowsBuilder_.addMessage(value);\n        }\n        return this;\n      }\n      /**\n       * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.RowData rows = 2;</code>\n       *\n       * <pre>\n       **每个batch里面的所有变更数据*\n       * </pre>\n       */\n      public Builder addRows(\n          int index, com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowData value) {\n        if (rowsBuilder_ == null) {\n          if (value == null) {\n            throw new NullPointerException();\n          }\n          ensureRowsIsMutable();\n          rows_.add(index, value);\n          onChanged();\n        } else {\n          rowsBuilder_.addMessage(index, value);\n        }\n        return this;\n      }\n      /**\n       * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.RowData rows = 2;</code>\n       *\n       * <pre>\n       **每个batch里面的所有变更数据*\n       * </pre>\n       */\n      public Builder addRows(\n          com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowData.Builder builderForValue) {\n        if (rowsBuilder_ == null) {\n          ensureRowsIsMutable();\n          rows_.add(builderForValue.build());\n          onChanged();\n        } else {\n          rowsBuilder_.addMessage(builderForValue.build());\n        }\n        return this;\n      }\n      /**\n       * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.RowData rows = 2;</code>\n       *\n       * <pre>\n       **每个batch里面的所有变更数据*\n       * </pre>\n       */\n      public Builder addRows(\n          int index, com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowData.Builder builderForValue) {\n        if (rowsBuilder_ == null) {\n          ensureRowsIsMutable();\n          rows_.add(index, builderForValue.build());\n          onChanged();\n        } else {\n          rowsBuilder_.addMessage(index, builderForValue.build());\n        }\n        return this;\n      }\n      /**\n       * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.RowData rows = 2;</code>\n       *\n       * <pre>\n       **每个batch里面的所有变更数据*\n       * </pre>\n       */\n      public Builder addAllRows(\n          java.lang.Iterable<? extends com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowData> values) {\n        if (rowsBuilder_ == null) {\n          ensureRowsIsMutable();\n          com.google.protobuf.AbstractMessageLite.Builder.addAll(\n              values, rows_);\n          onChanged();\n        } else {\n          rowsBuilder_.addAllMessages(values);\n        }\n        return this;\n      }\n      /**\n       * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.RowData rows = 2;</code>\n       *\n       * <pre>\n       **每个batch里面的所有变更数据*\n       * </pre>\n       */\n      public Builder clearRows() {\n        if (rowsBuilder_ == null) {\n          rows_ = java.util.Collections.emptyList();\n          bitField0_ = (bitField0_ & ~0x00000002);\n          onChanged();\n        } else {\n          rowsBuilder_.clear();\n        }\n        return this;\n      }\n      /**\n       * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.RowData rows = 2;</code>\n       *\n       * <pre>\n       **每个batch里面的所有变更数据*\n       * </pre>\n       */\n      public Builder removeRows(int index) {\n        if (rowsBuilder_ == null) {\n          ensureRowsIsMutable();\n          rows_.remove(index);\n          onChanged();\n        } else {\n          rowsBuilder_.remove(index);\n        }\n        return this;\n      }\n      /**\n       * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.RowData rows = 2;</code>\n       *\n       * <pre>\n       **每个batch里面的所有变更数据*\n       * </pre>\n       */\n      public com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowData.Builder getRowsBuilder(\n          int index) {\n        return getRowsFieldBuilder().getBuilder(index);\n      }\n      /**\n       * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.RowData rows = 2;</code>\n       *\n       * <pre>\n       **每个batch里面的所有变更数据*\n       * </pre>\n       */\n      public com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowDataOrBuilder getRowsOrBuilder(\n          int index) {\n        if (rowsBuilder_ == null) {\n          return rows_.get(index);  } else {\n          return rowsBuilder_.getMessageOrBuilder(index);\n        }\n      }\n      /**\n       * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.RowData rows = 2;</code>\n       *\n       * <pre>\n       **每个batch里面的所有变更数据*\n       * </pre>\n       */\n      public java.util.List<? extends com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowDataOrBuilder> \n           getRowsOrBuilderList() {\n        if (rowsBuilder_ != null) {\n          return rowsBuilder_.getMessageOrBuilderList();\n        } else {\n          return java.util.Collections.unmodifiableList(rows_);\n        }\n      }\n      /**\n       * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.RowData rows = 2;</code>\n       *\n       * <pre>\n       **每个batch里面的所有变更数据*\n       * </pre>\n       */\n      public com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowData.Builder addRowsBuilder() {\n        return getRowsFieldBuilder().addBuilder(\n            com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowData.getDefaultInstance());\n      }\n      /**\n       * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.RowData rows = 2;</code>\n       *\n       * <pre>\n       **每个batch里面的所有变更数据*\n       * </pre>\n       */\n      public com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowData.Builder addRowsBuilder(\n          int index) {\n        return getRowsFieldBuilder().addBuilder(\n            index, com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowData.getDefaultInstance());\n      }\n      /**\n       * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.RowData rows = 2;</code>\n       *\n       * <pre>\n       **每个batch里面的所有变更数据*\n       * </pre>\n       */\n      public java.util.List<com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowData.Builder> \n           getRowsBuilderList() {\n        return getRowsFieldBuilder().getBuilderList();\n      }\n      private com.google.protobuf.RepeatedFieldBuilder<\n          com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowData, com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowData.Builder, com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowDataOrBuilder> \n          getRowsFieldBuilder() {\n        if (rowsBuilder_ == null) {\n          rowsBuilder_ = new com.google.protobuf.RepeatedFieldBuilder<\n              com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowData, com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowData.Builder, com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowDataOrBuilder>(\n                  rows_,\n                  ((bitField0_ & 0x00000002) == 0x00000002),\n                  getParentForChildren(),\n                  isClean());\n          rows_ = null;\n        }\n        return rowsBuilder_;\n      }\n\n      // @@protoc_insertion_point(builder_scope:com.alibaba.otter.node.etl.model.protobuf.RowBatch)\n    }\n\n    static {\n      defaultInstance = new RowBatch(true);\n      defaultInstance.initFields();\n    }\n\n    // @@protoc_insertion_point(class_scope:com.alibaba.otter.node.etl.model.protobuf.RowBatch)\n  }\n\n  public interface FileBatchOrBuilder extends\n      // @@protoc_insertion_point(interface_extends:com.alibaba.otter.node.etl.model.protobuf.FileBatch)\n      com.google.protobuf.MessageOrBuilder {\n\n    /**\n     * <code>optional .com.alibaba.otter.node.etl.model.protobuf.Identity identity = 1;</code>\n     */\n    boolean hasIdentity();\n    /**\n     * <code>optional .com.alibaba.otter.node.etl.model.protobuf.Identity identity = 1;</code>\n     */\n    com.alibaba.otter.node.etl.model.protobuf.BatchProto.Identity getIdentity();\n    /**\n     * <code>optional .com.alibaba.otter.node.etl.model.protobuf.Identity identity = 1;</code>\n     */\n    com.alibaba.otter.node.etl.model.protobuf.BatchProto.IdentityOrBuilder getIdentityOrBuilder();\n\n    /**\n     * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.FileData files = 2;</code>\n     *\n     * <pre>\n     **每个batch里面变更数据所关联的文件信息*\n     * </pre>\n     */\n    java.util.List<com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileData> \n        getFilesList();\n    /**\n     * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.FileData files = 2;</code>\n     *\n     * <pre>\n     **每个batch里面变更数据所关联的文件信息*\n     * </pre>\n     */\n    com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileData getFiles(int index);\n    /**\n     * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.FileData files = 2;</code>\n     *\n     * <pre>\n     **每个batch里面变更数据所关联的文件信息*\n     * </pre>\n     */\n    int getFilesCount();\n    /**\n     * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.FileData files = 2;</code>\n     *\n     * <pre>\n     **每个batch里面变更数据所关联的文件信息*\n     * </pre>\n     */\n    java.util.List<? extends com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileDataOrBuilder> \n        getFilesOrBuilderList();\n    /**\n     * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.FileData files = 2;</code>\n     *\n     * <pre>\n     **每个batch里面变更数据所关联的文件信息*\n     * </pre>\n     */\n    com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileDataOrBuilder getFilesOrBuilder(\n        int index);\n  }\n  /**\n   * Protobuf type {@code com.alibaba.otter.node.etl.model.protobuf.FileBatch}\n   */\n  public static final class FileBatch extends\n      com.google.protobuf.GeneratedMessage implements\n      // @@protoc_insertion_point(message_implements:com.alibaba.otter.node.etl.model.protobuf.FileBatch)\n      FileBatchOrBuilder {\n    // Use FileBatch.newBuilder() to construct.\n    private FileBatch(com.google.protobuf.GeneratedMessage.Builder<?> builder) {\n      super(builder);\n      this.unknownFields = builder.getUnknownFields();\n    }\n    private FileBatch(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); }\n\n    private static final FileBatch defaultInstance;\n    public static FileBatch getDefaultInstance() {\n      return defaultInstance;\n    }\n\n    public FileBatch getDefaultInstanceForType() {\n      return defaultInstance;\n    }\n\n    private final com.google.protobuf.UnknownFieldSet unknownFields;\n    @java.lang.Override\n    public final com.google.protobuf.UnknownFieldSet\n        getUnknownFields() {\n      return this.unknownFields;\n    }\n    private FileBatch(\n        com.google.protobuf.CodedInputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      initFields();\n      int mutable_bitField0_ = 0;\n      com.google.protobuf.UnknownFieldSet.Builder unknownFields =\n          com.google.protobuf.UnknownFieldSet.newBuilder();\n      try {\n        boolean done = false;\n        while (!done) {\n          int tag = input.readTag();\n          switch (tag) {\n            case 0:\n              done = true;\n              break;\n            default: {\n              if (!parseUnknownField(input, unknownFields,\n                                     extensionRegistry, tag)) {\n                done = true;\n              }\n              break;\n            }\n            case 10: {\n              com.alibaba.otter.node.etl.model.protobuf.BatchProto.Identity.Builder subBuilder = null;\n              if (((bitField0_ & 0x00000001) == 0x00000001)) {\n                subBuilder = identity_.toBuilder();\n              }\n              identity_ = input.readMessage(com.alibaba.otter.node.etl.model.protobuf.BatchProto.Identity.PARSER, extensionRegistry);\n              if (subBuilder != null) {\n                subBuilder.mergeFrom(identity_);\n                identity_ = subBuilder.buildPartial();\n              }\n              bitField0_ |= 0x00000001;\n              break;\n            }\n            case 18: {\n              if (!((mutable_bitField0_ & 0x00000002) == 0x00000002)) {\n                files_ = new java.util.ArrayList<com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileData>();\n                mutable_bitField0_ |= 0x00000002;\n              }\n              files_.add(input.readMessage(com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileData.PARSER, extensionRegistry));\n              break;\n            }\n          }\n        }\n      } catch (com.google.protobuf.InvalidProtocolBufferException e) {\n        throw e.setUnfinishedMessage(this);\n      } catch (java.io.IOException e) {\n        throw new com.google.protobuf.InvalidProtocolBufferException(\n            e.getMessage()).setUnfinishedMessage(this);\n      } finally {\n        if (((mutable_bitField0_ & 0x00000002) == 0x00000002)) {\n          files_ = java.util.Collections.unmodifiableList(files_);\n        }\n        this.unknownFields = unknownFields.build();\n        makeExtensionsImmutable();\n      }\n    }\n    public static final com.google.protobuf.Descriptors.Descriptor\n        getDescriptor() {\n      return com.alibaba.otter.node.etl.model.protobuf.BatchProto.internal_static_com_alibaba_otter_node_etl_model_protobuf_FileBatch_descriptor;\n    }\n\n    protected com.google.protobuf.GeneratedMessage.FieldAccessorTable\n        internalGetFieldAccessorTable() {\n      return com.alibaba.otter.node.etl.model.protobuf.BatchProto.internal_static_com_alibaba_otter_node_etl_model_protobuf_FileBatch_fieldAccessorTable\n          .ensureFieldAccessorsInitialized(\n              com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileBatch.class, com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileBatch.Builder.class);\n    }\n\n    public static com.google.protobuf.Parser<FileBatch> PARSER =\n        new com.google.protobuf.AbstractParser<FileBatch>() {\n      public FileBatch parsePartialFrom(\n          com.google.protobuf.CodedInputStream input,\n          com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n          throws com.google.protobuf.InvalidProtocolBufferException {\n        return new FileBatch(input, extensionRegistry);\n      }\n    };\n\n    @java.lang.Override\n    public com.google.protobuf.Parser<FileBatch> getParserForType() {\n      return PARSER;\n    }\n\n    private int bitField0_;\n    public static final int IDENTITY_FIELD_NUMBER = 1;\n    private com.alibaba.otter.node.etl.model.protobuf.BatchProto.Identity identity_;\n    /**\n     * <code>optional .com.alibaba.otter.node.etl.model.protobuf.Identity identity = 1;</code>\n     */\n    public boolean hasIdentity() {\n      return ((bitField0_ & 0x00000001) == 0x00000001);\n    }\n    /**\n     * <code>optional .com.alibaba.otter.node.etl.model.protobuf.Identity identity = 1;</code>\n     */\n    public com.alibaba.otter.node.etl.model.protobuf.BatchProto.Identity getIdentity() {\n      return identity_;\n    }\n    /**\n     * <code>optional .com.alibaba.otter.node.etl.model.protobuf.Identity identity = 1;</code>\n     */\n    public com.alibaba.otter.node.etl.model.protobuf.BatchProto.IdentityOrBuilder getIdentityOrBuilder() {\n      return identity_;\n    }\n\n    public static final int FILES_FIELD_NUMBER = 2;\n    private java.util.List<com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileData> files_;\n    /**\n     * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.FileData files = 2;</code>\n     *\n     * <pre>\n     **每个batch里面变更数据所关联的文件信息*\n     * </pre>\n     */\n    public java.util.List<com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileData> getFilesList() {\n      return files_;\n    }\n    /**\n     * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.FileData files = 2;</code>\n     *\n     * <pre>\n     **每个batch里面变更数据所关联的文件信息*\n     * </pre>\n     */\n    public java.util.List<? extends com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileDataOrBuilder> \n        getFilesOrBuilderList() {\n      return files_;\n    }\n    /**\n     * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.FileData files = 2;</code>\n     *\n     * <pre>\n     **每个batch里面变更数据所关联的文件信息*\n     * </pre>\n     */\n    public int getFilesCount() {\n      return files_.size();\n    }\n    /**\n     * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.FileData files = 2;</code>\n     *\n     * <pre>\n     **每个batch里面变更数据所关联的文件信息*\n     * </pre>\n     */\n    public com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileData getFiles(int index) {\n      return files_.get(index);\n    }\n    /**\n     * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.FileData files = 2;</code>\n     *\n     * <pre>\n     **每个batch里面变更数据所关联的文件信息*\n     * </pre>\n     */\n    public com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileDataOrBuilder getFilesOrBuilder(\n        int index) {\n      return files_.get(index);\n    }\n\n    private void initFields() {\n      identity_ = com.alibaba.otter.node.etl.model.protobuf.BatchProto.Identity.getDefaultInstance();\n      files_ = java.util.Collections.emptyList();\n    }\n    private byte memoizedIsInitialized = -1;\n    public final boolean isInitialized() {\n      byte isInitialized = memoizedIsInitialized;\n      if (isInitialized == 1) return true;\n      if (isInitialized == 0) return false;\n\n      memoizedIsInitialized = 1;\n      return true;\n    }\n\n    public void writeTo(com.google.protobuf.CodedOutputStream output)\n                        throws java.io.IOException {\n      getSerializedSize();\n      if (((bitField0_ & 0x00000001) == 0x00000001)) {\n        output.writeMessage(1, identity_);\n      }\n      for (int i = 0; i < files_.size(); i++) {\n        output.writeMessage(2, files_.get(i));\n      }\n      getUnknownFields().writeTo(output);\n    }\n\n    private int memoizedSerializedSize = -1;\n    public int getSerializedSize() {\n      int size = memoizedSerializedSize;\n      if (size != -1) return size;\n\n      size = 0;\n      if (((bitField0_ & 0x00000001) == 0x00000001)) {\n        size += com.google.protobuf.CodedOutputStream\n          .computeMessageSize(1, identity_);\n      }\n      for (int i = 0; i < files_.size(); i++) {\n        size += com.google.protobuf.CodedOutputStream\n          .computeMessageSize(2, files_.get(i));\n      }\n      size += getUnknownFields().getSerializedSize();\n      memoizedSerializedSize = size;\n      return size;\n    }\n\n    private static final long serialVersionUID = 0L;\n    @java.lang.Override\n    protected java.lang.Object writeReplace()\n        throws java.io.ObjectStreamException {\n      return super.writeReplace();\n    }\n\n    public static com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileBatch parseFrom(\n        com.google.protobuf.ByteString data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return PARSER.parseFrom(data);\n    }\n    public static com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileBatch parseFrom(\n        com.google.protobuf.ByteString data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return PARSER.parseFrom(data, extensionRegistry);\n    }\n    public static com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileBatch parseFrom(byte[] data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return PARSER.parseFrom(data);\n    }\n    public static com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileBatch parseFrom(\n        byte[] data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return PARSER.parseFrom(data, extensionRegistry);\n    }\n    public static com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileBatch parseFrom(java.io.InputStream input)\n        throws java.io.IOException {\n      return PARSER.parseFrom(input);\n    }\n    public static com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileBatch parseFrom(\n        java.io.InputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return PARSER.parseFrom(input, extensionRegistry);\n    }\n    public static com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileBatch parseDelimitedFrom(java.io.InputStream input)\n        throws java.io.IOException {\n      return PARSER.parseDelimitedFrom(input);\n    }\n    public static com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileBatch parseDelimitedFrom(\n        java.io.InputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return PARSER.parseDelimitedFrom(input, extensionRegistry);\n    }\n    public static com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileBatch parseFrom(\n        com.google.protobuf.CodedInputStream input)\n        throws java.io.IOException {\n      return PARSER.parseFrom(input);\n    }\n    public static com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileBatch parseFrom(\n        com.google.protobuf.CodedInputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return PARSER.parseFrom(input, extensionRegistry);\n    }\n\n    public static Builder newBuilder() { return Builder.create(); }\n    public Builder newBuilderForType() { return newBuilder(); }\n    public static Builder newBuilder(com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileBatch prototype) {\n      return newBuilder().mergeFrom(prototype);\n    }\n    public Builder toBuilder() { return newBuilder(this); }\n\n    @java.lang.Override\n    protected Builder newBuilderForType(\n        com.google.protobuf.GeneratedMessage.BuilderParent parent) {\n      Builder builder = new Builder(parent);\n      return builder;\n    }\n    /**\n     * Protobuf type {@code com.alibaba.otter.node.etl.model.protobuf.FileBatch}\n     */\n    public static final class Builder extends\n        com.google.protobuf.GeneratedMessage.Builder<Builder> implements\n        // @@protoc_insertion_point(builder_implements:com.alibaba.otter.node.etl.model.protobuf.FileBatch)\n        com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileBatchOrBuilder {\n      public static final com.google.protobuf.Descriptors.Descriptor\n          getDescriptor() {\n        return com.alibaba.otter.node.etl.model.protobuf.BatchProto.internal_static_com_alibaba_otter_node_etl_model_protobuf_FileBatch_descriptor;\n      }\n\n      protected com.google.protobuf.GeneratedMessage.FieldAccessorTable\n          internalGetFieldAccessorTable() {\n        return com.alibaba.otter.node.etl.model.protobuf.BatchProto.internal_static_com_alibaba_otter_node_etl_model_protobuf_FileBatch_fieldAccessorTable\n            .ensureFieldAccessorsInitialized(\n                com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileBatch.class, com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileBatch.Builder.class);\n      }\n\n      // Construct using com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileBatch.newBuilder()\n      private Builder() {\n        maybeForceBuilderInitialization();\n      }\n\n      private Builder(\n          com.google.protobuf.GeneratedMessage.BuilderParent parent) {\n        super(parent);\n        maybeForceBuilderInitialization();\n      }\n      private void maybeForceBuilderInitialization() {\n        if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) {\n          getIdentityFieldBuilder();\n          getFilesFieldBuilder();\n        }\n      }\n      private static Builder create() {\n        return new Builder();\n      }\n\n      public Builder clear() {\n        super.clear();\n        if (identityBuilder_ == null) {\n          identity_ = com.alibaba.otter.node.etl.model.protobuf.BatchProto.Identity.getDefaultInstance();\n        } else {\n          identityBuilder_.clear();\n        }\n        bitField0_ = (bitField0_ & ~0x00000001);\n        if (filesBuilder_ == null) {\n          files_ = java.util.Collections.emptyList();\n          bitField0_ = (bitField0_ & ~0x00000002);\n        } else {\n          filesBuilder_.clear();\n        }\n        return this;\n      }\n\n      public Builder clone() {\n        return create().mergeFrom(buildPartial());\n      }\n\n      public com.google.protobuf.Descriptors.Descriptor\n          getDescriptorForType() {\n        return com.alibaba.otter.node.etl.model.protobuf.BatchProto.internal_static_com_alibaba_otter_node_etl_model_protobuf_FileBatch_descriptor;\n      }\n\n      public com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileBatch getDefaultInstanceForType() {\n        return com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileBatch.getDefaultInstance();\n      }\n\n      public com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileBatch build() {\n        com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileBatch result = buildPartial();\n        if (!result.isInitialized()) {\n          throw newUninitializedMessageException(result);\n        }\n        return result;\n      }\n\n      public com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileBatch buildPartial() {\n        com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileBatch result = new com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileBatch(this);\n        int from_bitField0_ = bitField0_;\n        int to_bitField0_ = 0;\n        if (((from_bitField0_ & 0x00000001) == 0x00000001)) {\n          to_bitField0_ |= 0x00000001;\n        }\n        if (identityBuilder_ == null) {\n          result.identity_ = identity_;\n        } else {\n          result.identity_ = identityBuilder_.build();\n        }\n        if (filesBuilder_ == null) {\n          if (((bitField0_ & 0x00000002) == 0x00000002)) {\n            files_ = java.util.Collections.unmodifiableList(files_);\n            bitField0_ = (bitField0_ & ~0x00000002);\n          }\n          result.files_ = files_;\n        } else {\n          result.files_ = filesBuilder_.build();\n        }\n        result.bitField0_ = to_bitField0_;\n        onBuilt();\n        return result;\n      }\n\n      public Builder mergeFrom(com.google.protobuf.Message other) {\n        if (other instanceof com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileBatch) {\n          return mergeFrom((com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileBatch)other);\n        } else {\n          super.mergeFrom(other);\n          return this;\n        }\n      }\n\n      public Builder mergeFrom(com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileBatch other) {\n        if (other == com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileBatch.getDefaultInstance()) return this;\n        if (other.hasIdentity()) {\n          mergeIdentity(other.getIdentity());\n        }\n        if (filesBuilder_ == null) {\n          if (!other.files_.isEmpty()) {\n            if (files_.isEmpty()) {\n              files_ = other.files_;\n              bitField0_ = (bitField0_ & ~0x00000002);\n            } else {\n              ensureFilesIsMutable();\n              files_.addAll(other.files_);\n            }\n            onChanged();\n          }\n        } else {\n          if (!other.files_.isEmpty()) {\n            if (filesBuilder_.isEmpty()) {\n              filesBuilder_.dispose();\n              filesBuilder_ = null;\n              files_ = other.files_;\n              bitField0_ = (bitField0_ & ~0x00000002);\n              filesBuilder_ = \n                com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ?\n                   getFilesFieldBuilder() : null;\n            } else {\n              filesBuilder_.addAllMessages(other.files_);\n            }\n          }\n        }\n        this.mergeUnknownFields(other.getUnknownFields());\n        return this;\n      }\n\n      public final boolean isInitialized() {\n        return true;\n      }\n\n      public Builder mergeFrom(\n          com.google.protobuf.CodedInputStream input,\n          com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n          throws java.io.IOException {\n        com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileBatch parsedMessage = null;\n        try {\n          parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry);\n        } catch (com.google.protobuf.InvalidProtocolBufferException e) {\n          parsedMessage = (com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileBatch) e.getUnfinishedMessage();\n          throw e;\n        } finally {\n          if (parsedMessage != null) {\n            mergeFrom(parsedMessage);\n          }\n        }\n        return this;\n      }\n      private int bitField0_;\n\n      private com.alibaba.otter.node.etl.model.protobuf.BatchProto.Identity identity_ = com.alibaba.otter.node.etl.model.protobuf.BatchProto.Identity.getDefaultInstance();\n      private com.google.protobuf.SingleFieldBuilder<\n          com.alibaba.otter.node.etl.model.protobuf.BatchProto.Identity, com.alibaba.otter.node.etl.model.protobuf.BatchProto.Identity.Builder, com.alibaba.otter.node.etl.model.protobuf.BatchProto.IdentityOrBuilder> identityBuilder_;\n      /**\n       * <code>optional .com.alibaba.otter.node.etl.model.protobuf.Identity identity = 1;</code>\n       */\n      public boolean hasIdentity() {\n        return ((bitField0_ & 0x00000001) == 0x00000001);\n      }\n      /**\n       * <code>optional .com.alibaba.otter.node.etl.model.protobuf.Identity identity = 1;</code>\n       */\n      public com.alibaba.otter.node.etl.model.protobuf.BatchProto.Identity getIdentity() {\n        if (identityBuilder_ == null) {\n          return identity_;\n        } else {\n          return identityBuilder_.getMessage();\n        }\n      }\n      /**\n       * <code>optional .com.alibaba.otter.node.etl.model.protobuf.Identity identity = 1;</code>\n       */\n      public Builder setIdentity(com.alibaba.otter.node.etl.model.protobuf.BatchProto.Identity value) {\n        if (identityBuilder_ == null) {\n          if (value == null) {\n            throw new NullPointerException();\n          }\n          identity_ = value;\n          onChanged();\n        } else {\n          identityBuilder_.setMessage(value);\n        }\n        bitField0_ |= 0x00000001;\n        return this;\n      }\n      /**\n       * <code>optional .com.alibaba.otter.node.etl.model.protobuf.Identity identity = 1;</code>\n       */\n      public Builder setIdentity(\n          com.alibaba.otter.node.etl.model.protobuf.BatchProto.Identity.Builder builderForValue) {\n        if (identityBuilder_ == null) {\n          identity_ = builderForValue.build();\n          onChanged();\n        } else {\n          identityBuilder_.setMessage(builderForValue.build());\n        }\n        bitField0_ |= 0x00000001;\n        return this;\n      }\n      /**\n       * <code>optional .com.alibaba.otter.node.etl.model.protobuf.Identity identity = 1;</code>\n       */\n      public Builder mergeIdentity(com.alibaba.otter.node.etl.model.protobuf.BatchProto.Identity value) {\n        if (identityBuilder_ == null) {\n          if (((bitField0_ & 0x00000001) == 0x00000001) &&\n              identity_ != com.alibaba.otter.node.etl.model.protobuf.BatchProto.Identity.getDefaultInstance()) {\n            identity_ =\n              com.alibaba.otter.node.etl.model.protobuf.BatchProto.Identity.newBuilder(identity_).mergeFrom(value).buildPartial();\n          } else {\n            identity_ = value;\n          }\n          onChanged();\n        } else {\n          identityBuilder_.mergeFrom(value);\n        }\n        bitField0_ |= 0x00000001;\n        return this;\n      }\n      /**\n       * <code>optional .com.alibaba.otter.node.etl.model.protobuf.Identity identity = 1;</code>\n       */\n      public Builder clearIdentity() {\n        if (identityBuilder_ == null) {\n          identity_ = com.alibaba.otter.node.etl.model.protobuf.BatchProto.Identity.getDefaultInstance();\n          onChanged();\n        } else {\n          identityBuilder_.clear();\n        }\n        bitField0_ = (bitField0_ & ~0x00000001);\n        return this;\n      }\n      /**\n       * <code>optional .com.alibaba.otter.node.etl.model.protobuf.Identity identity = 1;</code>\n       */\n      public com.alibaba.otter.node.etl.model.protobuf.BatchProto.Identity.Builder getIdentityBuilder() {\n        bitField0_ |= 0x00000001;\n        onChanged();\n        return getIdentityFieldBuilder().getBuilder();\n      }\n      /**\n       * <code>optional .com.alibaba.otter.node.etl.model.protobuf.Identity identity = 1;</code>\n       */\n      public com.alibaba.otter.node.etl.model.protobuf.BatchProto.IdentityOrBuilder getIdentityOrBuilder() {\n        if (identityBuilder_ != null) {\n          return identityBuilder_.getMessageOrBuilder();\n        } else {\n          return identity_;\n        }\n      }\n      /**\n       * <code>optional .com.alibaba.otter.node.etl.model.protobuf.Identity identity = 1;</code>\n       */\n      private com.google.protobuf.SingleFieldBuilder<\n          com.alibaba.otter.node.etl.model.protobuf.BatchProto.Identity, com.alibaba.otter.node.etl.model.protobuf.BatchProto.Identity.Builder, com.alibaba.otter.node.etl.model.protobuf.BatchProto.IdentityOrBuilder> \n          getIdentityFieldBuilder() {\n        if (identityBuilder_ == null) {\n          identityBuilder_ = new com.google.protobuf.SingleFieldBuilder<\n              com.alibaba.otter.node.etl.model.protobuf.BatchProto.Identity, com.alibaba.otter.node.etl.model.protobuf.BatchProto.Identity.Builder, com.alibaba.otter.node.etl.model.protobuf.BatchProto.IdentityOrBuilder>(\n                  getIdentity(),\n                  getParentForChildren(),\n                  isClean());\n          identity_ = null;\n        }\n        return identityBuilder_;\n      }\n\n      private java.util.List<com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileData> files_ =\n        java.util.Collections.emptyList();\n      private void ensureFilesIsMutable() {\n        if (!((bitField0_ & 0x00000002) == 0x00000002)) {\n          files_ = new java.util.ArrayList<com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileData>(files_);\n          bitField0_ |= 0x00000002;\n         }\n      }\n\n      private com.google.protobuf.RepeatedFieldBuilder<\n          com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileData, com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileData.Builder, com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileDataOrBuilder> filesBuilder_;\n\n      /**\n       * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.FileData files = 2;</code>\n       *\n       * <pre>\n       **每个batch里面变更数据所关联的文件信息*\n       * </pre>\n       */\n      public java.util.List<com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileData> getFilesList() {\n        if (filesBuilder_ == null) {\n          return java.util.Collections.unmodifiableList(files_);\n        } else {\n          return filesBuilder_.getMessageList();\n        }\n      }\n      /**\n       * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.FileData files = 2;</code>\n       *\n       * <pre>\n       **每个batch里面变更数据所关联的文件信息*\n       * </pre>\n       */\n      public int getFilesCount() {\n        if (filesBuilder_ == null) {\n          return files_.size();\n        } else {\n          return filesBuilder_.getCount();\n        }\n      }\n      /**\n       * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.FileData files = 2;</code>\n       *\n       * <pre>\n       **每个batch里面变更数据所关联的文件信息*\n       * </pre>\n       */\n      public com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileData getFiles(int index) {\n        if (filesBuilder_ == null) {\n          return files_.get(index);\n        } else {\n          return filesBuilder_.getMessage(index);\n        }\n      }\n      /**\n       * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.FileData files = 2;</code>\n       *\n       * <pre>\n       **每个batch里面变更数据所关联的文件信息*\n       * </pre>\n       */\n      public Builder setFiles(\n          int index, com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileData value) {\n        if (filesBuilder_ == null) {\n          if (value == null) {\n            throw new NullPointerException();\n          }\n          ensureFilesIsMutable();\n          files_.set(index, value);\n          onChanged();\n        } else {\n          filesBuilder_.setMessage(index, value);\n        }\n        return this;\n      }\n      /**\n       * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.FileData files = 2;</code>\n       *\n       * <pre>\n       **每个batch里面变更数据所关联的文件信息*\n       * </pre>\n       */\n      public Builder setFiles(\n          int index, com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileData.Builder builderForValue) {\n        if (filesBuilder_ == null) {\n          ensureFilesIsMutable();\n          files_.set(index, builderForValue.build());\n          onChanged();\n        } else {\n          filesBuilder_.setMessage(index, builderForValue.build());\n        }\n        return this;\n      }\n      /**\n       * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.FileData files = 2;</code>\n       *\n       * <pre>\n       **每个batch里面变更数据所关联的文件信息*\n       * </pre>\n       */\n      public Builder addFiles(com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileData value) {\n        if (filesBuilder_ == null) {\n          if (value == null) {\n            throw new NullPointerException();\n          }\n          ensureFilesIsMutable();\n          files_.add(value);\n          onChanged();\n        } else {\n          filesBuilder_.addMessage(value);\n        }\n        return this;\n      }\n      /**\n       * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.FileData files = 2;</code>\n       *\n       * <pre>\n       **每个batch里面变更数据所关联的文件信息*\n       * </pre>\n       */\n      public Builder addFiles(\n          int index, com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileData value) {\n        if (filesBuilder_ == null) {\n          if (value == null) {\n            throw new NullPointerException();\n          }\n          ensureFilesIsMutable();\n          files_.add(index, value);\n          onChanged();\n        } else {\n          filesBuilder_.addMessage(index, value);\n        }\n        return this;\n      }\n      /**\n       * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.FileData files = 2;</code>\n       *\n       * <pre>\n       **每个batch里面变更数据所关联的文件信息*\n       * </pre>\n       */\n      public Builder addFiles(\n          com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileData.Builder builderForValue) {\n        if (filesBuilder_ == null) {\n          ensureFilesIsMutable();\n          files_.add(builderForValue.build());\n          onChanged();\n        } else {\n          filesBuilder_.addMessage(builderForValue.build());\n        }\n        return this;\n      }\n      /**\n       * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.FileData files = 2;</code>\n       *\n       * <pre>\n       **每个batch里面变更数据所关联的文件信息*\n       * </pre>\n       */\n      public Builder addFiles(\n          int index, com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileData.Builder builderForValue) {\n        if (filesBuilder_ == null) {\n          ensureFilesIsMutable();\n          files_.add(index, builderForValue.build());\n          onChanged();\n        } else {\n          filesBuilder_.addMessage(index, builderForValue.build());\n        }\n        return this;\n      }\n      /**\n       * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.FileData files = 2;</code>\n       *\n       * <pre>\n       **每个batch里面变更数据所关联的文件信息*\n       * </pre>\n       */\n      public Builder addAllFiles(\n          java.lang.Iterable<? extends com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileData> values) {\n        if (filesBuilder_ == null) {\n          ensureFilesIsMutable();\n          com.google.protobuf.AbstractMessageLite.Builder.addAll(\n              values, files_);\n          onChanged();\n        } else {\n          filesBuilder_.addAllMessages(values);\n        }\n        return this;\n      }\n      /**\n       * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.FileData files = 2;</code>\n       *\n       * <pre>\n       **每个batch里面变更数据所关联的文件信息*\n       * </pre>\n       */\n      public Builder clearFiles() {\n        if (filesBuilder_ == null) {\n          files_ = java.util.Collections.emptyList();\n          bitField0_ = (bitField0_ & ~0x00000002);\n          onChanged();\n        } else {\n          filesBuilder_.clear();\n        }\n        return this;\n      }\n      /**\n       * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.FileData files = 2;</code>\n       *\n       * <pre>\n       **每个batch里面变更数据所关联的文件信息*\n       * </pre>\n       */\n      public Builder removeFiles(int index) {\n        if (filesBuilder_ == null) {\n          ensureFilesIsMutable();\n          files_.remove(index);\n          onChanged();\n        } else {\n          filesBuilder_.remove(index);\n        }\n        return this;\n      }\n      /**\n       * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.FileData files = 2;</code>\n       *\n       * <pre>\n       **每个batch里面变更数据所关联的文件信息*\n       * </pre>\n       */\n      public com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileData.Builder getFilesBuilder(\n          int index) {\n        return getFilesFieldBuilder().getBuilder(index);\n      }\n      /**\n       * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.FileData files = 2;</code>\n       *\n       * <pre>\n       **每个batch里面变更数据所关联的文件信息*\n       * </pre>\n       */\n      public com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileDataOrBuilder getFilesOrBuilder(\n          int index) {\n        if (filesBuilder_ == null) {\n          return files_.get(index);  } else {\n          return filesBuilder_.getMessageOrBuilder(index);\n        }\n      }\n      /**\n       * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.FileData files = 2;</code>\n       *\n       * <pre>\n       **每个batch里面变更数据所关联的文件信息*\n       * </pre>\n       */\n      public java.util.List<? extends com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileDataOrBuilder> \n           getFilesOrBuilderList() {\n        if (filesBuilder_ != null) {\n          return filesBuilder_.getMessageOrBuilderList();\n        } else {\n          return java.util.Collections.unmodifiableList(files_);\n        }\n      }\n      /**\n       * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.FileData files = 2;</code>\n       *\n       * <pre>\n       **每个batch里面变更数据所关联的文件信息*\n       * </pre>\n       */\n      public com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileData.Builder addFilesBuilder() {\n        return getFilesFieldBuilder().addBuilder(\n            com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileData.getDefaultInstance());\n      }\n      /**\n       * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.FileData files = 2;</code>\n       *\n       * <pre>\n       **每个batch里面变更数据所关联的文件信息*\n       * </pre>\n       */\n      public com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileData.Builder addFilesBuilder(\n          int index) {\n        return getFilesFieldBuilder().addBuilder(\n            index, com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileData.getDefaultInstance());\n      }\n      /**\n       * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.FileData files = 2;</code>\n       *\n       * <pre>\n       **每个batch里面变更数据所关联的文件信息*\n       * </pre>\n       */\n      public java.util.List<com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileData.Builder> \n           getFilesBuilderList() {\n        return getFilesFieldBuilder().getBuilderList();\n      }\n      private com.google.protobuf.RepeatedFieldBuilder<\n          com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileData, com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileData.Builder, com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileDataOrBuilder> \n          getFilesFieldBuilder() {\n        if (filesBuilder_ == null) {\n          filesBuilder_ = new com.google.protobuf.RepeatedFieldBuilder<\n              com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileData, com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileData.Builder, com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileDataOrBuilder>(\n                  files_,\n                  ((bitField0_ & 0x00000002) == 0x00000002),\n                  getParentForChildren(),\n                  isClean());\n          files_ = null;\n        }\n        return filesBuilder_;\n      }\n\n      // @@protoc_insertion_point(builder_scope:com.alibaba.otter.node.etl.model.protobuf.FileBatch)\n    }\n\n    static {\n      defaultInstance = new FileBatch(true);\n      defaultInstance.initFields();\n    }\n\n    // @@protoc_insertion_point(class_scope:com.alibaba.otter.node.etl.model.protobuf.FileBatch)\n  }\n\n  public interface RowDataOrBuilder extends\n      // @@protoc_insertion_point(interface_extends:com.alibaba.otter.node.etl.model.protobuf.RowData)\n      com.google.protobuf.MessageOrBuilder {\n\n    /**\n     * <code>optional int64 tableId = 1;</code>\n     */\n    boolean hasTableId();\n    /**\n     * <code>optional int64 tableId = 1;</code>\n     */\n    long getTableId();\n\n    /**\n     * <code>optional string schemaName = 2;</code>\n     */\n    boolean hasSchemaName();\n    /**\n     * <code>optional string schemaName = 2;</code>\n     */\n    java.lang.String getSchemaName();\n    /**\n     * <code>optional string schemaName = 2;</code>\n     */\n    com.google.protobuf.ByteString\n        getSchemaNameBytes();\n\n    /**\n     * <code>optional string tableName = 3;</code>\n     */\n    boolean hasTableName();\n    /**\n     * <code>optional string tableName = 3;</code>\n     */\n    java.lang.String getTableName();\n    /**\n     * <code>optional string tableName = 3;</code>\n     */\n    com.google.protobuf.ByteString\n        getTableNameBytes();\n\n    /**\n     * <code>optional string eventType = 4;</code>\n     *\n     * <pre>\n     **变更数据的操作类型(I/U/D)*\n     * </pre>\n     */\n    boolean hasEventType();\n    /**\n     * <code>optional string eventType = 4;</code>\n     *\n     * <pre>\n     **变更数据的操作类型(I/U/D)*\n     * </pre>\n     */\n    java.lang.String getEventType();\n    /**\n     * <code>optional string eventType = 4;</code>\n     *\n     * <pre>\n     **变更数据的操作类型(I/U/D)*\n     * </pre>\n     */\n    com.google.protobuf.ByteString\n        getEventTypeBytes();\n\n    /**\n     * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.Column oldKeys = 5;</code>\n     *\n     * <pre>\n     **变更前的主键，可能是复合主键*\n     * </pre>\n     */\n    java.util.List<com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column> \n        getOldKeysList();\n    /**\n     * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.Column oldKeys = 5;</code>\n     *\n     * <pre>\n     **变更前的主键，可能是复合主键*\n     * </pre>\n     */\n    com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column getOldKeys(int index);\n    /**\n     * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.Column oldKeys = 5;</code>\n     *\n     * <pre>\n     **变更前的主键，可能是复合主键*\n     * </pre>\n     */\n    int getOldKeysCount();\n    /**\n     * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.Column oldKeys = 5;</code>\n     *\n     * <pre>\n     **变更前的主键，可能是复合主键*\n     * </pre>\n     */\n    java.util.List<? extends com.alibaba.otter.node.etl.model.protobuf.BatchProto.ColumnOrBuilder> \n        getOldKeysOrBuilderList();\n    /**\n     * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.Column oldKeys = 5;</code>\n     *\n     * <pre>\n     **变更前的主键，可能是复合主键*\n     * </pre>\n     */\n    com.alibaba.otter.node.etl.model.protobuf.BatchProto.ColumnOrBuilder getOldKeysOrBuilder(\n        int index);\n\n    /**\n     * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.Column keys = 6;</code>\n     *\n     * <pre>\n     **变更后的主键，可能是复合主键*\n     * </pre>\n     */\n    java.util.List<com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column> \n        getKeysList();\n    /**\n     * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.Column keys = 6;</code>\n     *\n     * <pre>\n     **变更后的主键，可能是复合主键*\n     * </pre>\n     */\n    com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column getKeys(int index);\n    /**\n     * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.Column keys = 6;</code>\n     *\n     * <pre>\n     **变更后的主键，可能是复合主键*\n     * </pre>\n     */\n    int getKeysCount();\n    /**\n     * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.Column keys = 6;</code>\n     *\n     * <pre>\n     **变更后的主键，可能是复合主键*\n     * </pre>\n     */\n    java.util.List<? extends com.alibaba.otter.node.etl.model.protobuf.BatchProto.ColumnOrBuilder> \n        getKeysOrBuilderList();\n    /**\n     * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.Column keys = 6;</code>\n     *\n     * <pre>\n     **变更后的主键，可能是复合主键*\n     * </pre>\n     */\n    com.alibaba.otter.node.etl.model.protobuf.BatchProto.ColumnOrBuilder getKeysOrBuilder(\n        int index);\n\n    /**\n     * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.Column columns = 7;</code>\n     *\n     * <pre>\n     **变更数据的每列变更信息,不包含主键*\n     * </pre>\n     */\n    java.util.List<com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column> \n        getColumnsList();\n    /**\n     * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.Column columns = 7;</code>\n     *\n     * <pre>\n     **变更数据的每列变更信息,不包含主键*\n     * </pre>\n     */\n    com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column getColumns(int index);\n    /**\n     * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.Column columns = 7;</code>\n     *\n     * <pre>\n     **变更数据的每列变更信息,不包含主键*\n     * </pre>\n     */\n    int getColumnsCount();\n    /**\n     * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.Column columns = 7;</code>\n     *\n     * <pre>\n     **变更数据的每列变更信息,不包含主键*\n     * </pre>\n     */\n    java.util.List<? extends com.alibaba.otter.node.etl.model.protobuf.BatchProto.ColumnOrBuilder> \n        getColumnsOrBuilderList();\n    /**\n     * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.Column columns = 7;</code>\n     *\n     * <pre>\n     **变更数据的每列变更信息,不包含主键*\n     * </pre>\n     */\n    com.alibaba.otter.node.etl.model.protobuf.BatchProto.ColumnOrBuilder getColumnsOrBuilder(\n        int index);\n\n    /**\n     * <code>optional int64 executeTime = 8;</code>\n     *\n     * <pre>\n     **变更数据的业务时间*\n     * </pre>\n     */\n    boolean hasExecuteTime();\n    /**\n     * <code>optional int64 executeTime = 8;</code>\n     *\n     * <pre>\n     **变更数据的业务时间*\n     * </pre>\n     */\n    long getExecuteTime();\n\n    /**\n     * <code>optional int64 pairId = 9;</code>\n     *\n     * <pre>\n     **映射规则id*\n     * </pre>\n     */\n    boolean hasPairId();\n    /**\n     * <code>optional int64 pairId = 9;</code>\n     *\n     * <pre>\n     **映射规则id*\n     * </pre>\n     */\n    long getPairId();\n\n    /**\n     * <code>optional string syncMode = 10;</code>\n     *\n     * <pre>\n     **同步模式(R/F)*\n     * </pre>\n     */\n    boolean hasSyncMode();\n    /**\n     * <code>optional string syncMode = 10;</code>\n     *\n     * <pre>\n     **同步模式(R/F)*\n     * </pre>\n     */\n    java.lang.String getSyncMode();\n    /**\n     * <code>optional string syncMode = 10;</code>\n     *\n     * <pre>\n     **同步模式(R/F)*\n     * </pre>\n     */\n    com.google.protobuf.ByteString\n        getSyncModeBytes();\n\n    /**\n     * <code>optional string syncConsistency = 11;</code>\n     *\n     * <pre>\n     **同步一致性(B/S/M) *\n     * </pre>\n     */\n    boolean hasSyncConsistency();\n    /**\n     * <code>optional string syncConsistency = 11;</code>\n     *\n     * <pre>\n     **同步一致性(B/S/M) *\n     * </pre>\n     */\n    java.lang.String getSyncConsistency();\n    /**\n     * <code>optional string syncConsistency = 11;</code>\n     *\n     * <pre>\n     **同步一致性(B/S/M) *\n     * </pre>\n     */\n    com.google.protobuf.ByteString\n        getSyncConsistencyBytes();\n\n    /**\n     * <code>optional int64 size = 12;</code>\n     *\n     * <pre>\n     ** eventsize *\n     * </pre>\n     */\n    boolean hasSize();\n    /**\n     * <code>optional int64 size = 12;</code>\n     *\n     * <pre>\n     ** eventsize *\n     * </pre>\n     */\n    long getSize();\n\n    /**\n     * <code>optional bool remedy = 13;</code>\n     *\n     * <pre>\n     ** isRemedy *\n     * </pre>\n     */\n    boolean hasRemedy();\n    /**\n     * <code>optional bool remedy = 13;</code>\n     *\n     * <pre>\n     ** isRemedy *\n     * </pre>\n     */\n    boolean getRemedy();\n\n    /**\n     * <code>optional string sql = 14;</code>\n     *\n     * <pre>\n     ** dml/ddl sql *\n     * </pre>\n     */\n    boolean hasSql();\n    /**\n     * <code>optional string sql = 14;</code>\n     *\n     * <pre>\n     ** dml/ddl sql *\n     * </pre>\n     */\n    java.lang.String getSql();\n    /**\n     * <code>optional string sql = 14;</code>\n     *\n     * <pre>\n     ** dml/ddl sql *\n     * </pre>\n     */\n    com.google.protobuf.ByteString\n        getSqlBytes();\n\n    /**\n     * <code>optional string ddlSchemaName = 15;</code>\n     *\n     * <pre>\n     ** current ddl schemaName *\n     * </pre>\n     */\n    boolean hasDdlSchemaName();\n    /**\n     * <code>optional string ddlSchemaName = 15;</code>\n     *\n     * <pre>\n     ** current ddl schemaName *\n     * </pre>\n     */\n    java.lang.String getDdlSchemaName();\n    /**\n     * <code>optional string ddlSchemaName = 15;</code>\n     *\n     * <pre>\n     ** current ddl schemaName *\n     * </pre>\n     */\n    com.google.protobuf.ByteString\n        getDdlSchemaNameBytes();\n\n    /**\n     * <code>optional string hint = 16;</code>\n     *\n     * <pre>\n     ** dml/ddl sql *\n     * </pre>\n     */\n    boolean hasHint();\n    /**\n     * <code>optional string hint = 16;</code>\n     *\n     * <pre>\n     ** dml/ddl sql *\n     * </pre>\n     */\n    java.lang.String getHint();\n    /**\n     * <code>optional string hint = 16;</code>\n     *\n     * <pre>\n     ** dml/ddl sql *\n     * </pre>\n     */\n    com.google.protobuf.ByteString\n        getHintBytes();\n\n    /**\n     * <code>optional bool withoutSchema = 17;</code>\n     *\n     * <pre>\n     ** without schema *\n     * </pre>\n     */\n    boolean hasWithoutSchema();\n    /**\n     * <code>optional bool withoutSchema = 17;</code>\n     *\n     * <pre>\n     ** without schema *\n     * </pre>\n     */\n    boolean getWithoutSchema();\n  }\n  /**\n   * Protobuf type {@code com.alibaba.otter.node.etl.model.protobuf.RowData}\n   *\n   * <pre>\n   **数据*\n   * </pre>\n   */\n  public static final class RowData extends\n      com.google.protobuf.GeneratedMessage implements\n      // @@protoc_insertion_point(message_implements:com.alibaba.otter.node.etl.model.protobuf.RowData)\n      RowDataOrBuilder {\n    // Use RowData.newBuilder() to construct.\n    private RowData(com.google.protobuf.GeneratedMessage.Builder<?> builder) {\n      super(builder);\n      this.unknownFields = builder.getUnknownFields();\n    }\n    private RowData(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); }\n\n    private static final RowData defaultInstance;\n    public static RowData getDefaultInstance() {\n      return defaultInstance;\n    }\n\n    public RowData getDefaultInstanceForType() {\n      return defaultInstance;\n    }\n\n    private final com.google.protobuf.UnknownFieldSet unknownFields;\n    @java.lang.Override\n    public final com.google.protobuf.UnknownFieldSet\n        getUnknownFields() {\n      return this.unknownFields;\n    }\n    private RowData(\n        com.google.protobuf.CodedInputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      initFields();\n      int mutable_bitField0_ = 0;\n      com.google.protobuf.UnknownFieldSet.Builder unknownFields =\n          com.google.protobuf.UnknownFieldSet.newBuilder();\n      try {\n        boolean done = false;\n        while (!done) {\n          int tag = input.readTag();\n          switch (tag) {\n            case 0:\n              done = true;\n              break;\n            default: {\n              if (!parseUnknownField(input, unknownFields,\n                                     extensionRegistry, tag)) {\n                done = true;\n              }\n              break;\n            }\n            case 8: {\n              bitField0_ |= 0x00000001;\n              tableId_ = input.readInt64();\n              break;\n            }\n            case 18: {\n              com.google.protobuf.ByteString bs = input.readBytes();\n              bitField0_ |= 0x00000002;\n              schemaName_ = bs;\n              break;\n            }\n            case 26: {\n              com.google.protobuf.ByteString bs = input.readBytes();\n              bitField0_ |= 0x00000004;\n              tableName_ = bs;\n              break;\n            }\n            case 34: {\n              com.google.protobuf.ByteString bs = input.readBytes();\n              bitField0_ |= 0x00000008;\n              eventType_ = bs;\n              break;\n            }\n            case 42: {\n              if (!((mutable_bitField0_ & 0x00000010) == 0x00000010)) {\n                oldKeys_ = new java.util.ArrayList<com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column>();\n                mutable_bitField0_ |= 0x00000010;\n              }\n              oldKeys_.add(input.readMessage(com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column.PARSER, extensionRegistry));\n              break;\n            }\n            case 50: {\n              if (!((mutable_bitField0_ & 0x00000020) == 0x00000020)) {\n                keys_ = new java.util.ArrayList<com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column>();\n                mutable_bitField0_ |= 0x00000020;\n              }\n              keys_.add(input.readMessage(com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column.PARSER, extensionRegistry));\n              break;\n            }\n            case 58: {\n              if (!((mutable_bitField0_ & 0x00000040) == 0x00000040)) {\n                columns_ = new java.util.ArrayList<com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column>();\n                mutable_bitField0_ |= 0x00000040;\n              }\n              columns_.add(input.readMessage(com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column.PARSER, extensionRegistry));\n              break;\n            }\n            case 64: {\n              bitField0_ |= 0x00000010;\n              executeTime_ = input.readInt64();\n              break;\n            }\n            case 72: {\n              bitField0_ |= 0x00000020;\n              pairId_ = input.readInt64();\n              break;\n            }\n            case 82: {\n              com.google.protobuf.ByteString bs = input.readBytes();\n              bitField0_ |= 0x00000040;\n              syncMode_ = bs;\n              break;\n            }\n            case 90: {\n              com.google.protobuf.ByteString bs = input.readBytes();\n              bitField0_ |= 0x00000080;\n              syncConsistency_ = bs;\n              break;\n            }\n            case 96: {\n              bitField0_ |= 0x00000100;\n              size_ = input.readInt64();\n              break;\n            }\n            case 104: {\n              bitField0_ |= 0x00000200;\n              remedy_ = input.readBool();\n              break;\n            }\n            case 114: {\n              com.google.protobuf.ByteString bs = input.readBytes();\n              bitField0_ |= 0x00000400;\n              sql_ = bs;\n              break;\n            }\n            case 122: {\n              com.google.protobuf.ByteString bs = input.readBytes();\n              bitField0_ |= 0x00000800;\n              ddlSchemaName_ = bs;\n              break;\n            }\n            case 130: {\n              com.google.protobuf.ByteString bs = input.readBytes();\n              bitField0_ |= 0x00001000;\n              hint_ = bs;\n              break;\n            }\n            case 136: {\n              bitField0_ |= 0x00002000;\n              withoutSchema_ = input.readBool();\n              break;\n            }\n          }\n        }\n      } catch (com.google.protobuf.InvalidProtocolBufferException e) {\n        throw e.setUnfinishedMessage(this);\n      } catch (java.io.IOException e) {\n        throw new com.google.protobuf.InvalidProtocolBufferException(\n            e.getMessage()).setUnfinishedMessage(this);\n      } finally {\n        if (((mutable_bitField0_ & 0x00000010) == 0x00000010)) {\n          oldKeys_ = java.util.Collections.unmodifiableList(oldKeys_);\n        }\n        if (((mutable_bitField0_ & 0x00000020) == 0x00000020)) {\n          keys_ = java.util.Collections.unmodifiableList(keys_);\n        }\n        if (((mutable_bitField0_ & 0x00000040) == 0x00000040)) {\n          columns_ = java.util.Collections.unmodifiableList(columns_);\n        }\n        this.unknownFields = unknownFields.build();\n        makeExtensionsImmutable();\n      }\n    }\n    public static final com.google.protobuf.Descriptors.Descriptor\n        getDescriptor() {\n      return com.alibaba.otter.node.etl.model.protobuf.BatchProto.internal_static_com_alibaba_otter_node_etl_model_protobuf_RowData_descriptor;\n    }\n\n    protected com.google.protobuf.GeneratedMessage.FieldAccessorTable\n        internalGetFieldAccessorTable() {\n      return com.alibaba.otter.node.etl.model.protobuf.BatchProto.internal_static_com_alibaba_otter_node_etl_model_protobuf_RowData_fieldAccessorTable\n          .ensureFieldAccessorsInitialized(\n              com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowData.class, com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowData.Builder.class);\n    }\n\n    public static com.google.protobuf.Parser<RowData> PARSER =\n        new com.google.protobuf.AbstractParser<RowData>() {\n      public RowData parsePartialFrom(\n          com.google.protobuf.CodedInputStream input,\n          com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n          throws com.google.protobuf.InvalidProtocolBufferException {\n        return new RowData(input, extensionRegistry);\n      }\n    };\n\n    @java.lang.Override\n    public com.google.protobuf.Parser<RowData> getParserForType() {\n      return PARSER;\n    }\n\n    private int bitField0_;\n    public static final int TABLEID_FIELD_NUMBER = 1;\n    private long tableId_;\n    /**\n     * <code>optional int64 tableId = 1;</code>\n     */\n    public boolean hasTableId() {\n      return ((bitField0_ & 0x00000001) == 0x00000001);\n    }\n    /**\n     * <code>optional int64 tableId = 1;</code>\n     */\n    public long getTableId() {\n      return tableId_;\n    }\n\n    public static final int SCHEMANAME_FIELD_NUMBER = 2;\n    private java.lang.Object schemaName_;\n    /**\n     * <code>optional string schemaName = 2;</code>\n     */\n    public boolean hasSchemaName() {\n      return ((bitField0_ & 0x00000002) == 0x00000002);\n    }\n    /**\n     * <code>optional string schemaName = 2;</code>\n     */\n    public java.lang.String getSchemaName() {\n      java.lang.Object ref = schemaName_;\n      if (ref instanceof java.lang.String) {\n        return (java.lang.String) ref;\n      } else {\n        com.google.protobuf.ByteString bs = \n            (com.google.protobuf.ByteString) ref;\n        java.lang.String s = bs.toStringUtf8();\n        if (bs.isValidUtf8()) {\n          schemaName_ = s;\n        }\n        return s;\n      }\n    }\n    /**\n     * <code>optional string schemaName = 2;</code>\n     */\n    public com.google.protobuf.ByteString\n        getSchemaNameBytes() {\n      java.lang.Object ref = schemaName_;\n      if (ref instanceof java.lang.String) {\n        com.google.protobuf.ByteString b = \n            com.google.protobuf.ByteString.copyFromUtf8(\n                (java.lang.String) ref);\n        schemaName_ = b;\n        return b;\n      } else {\n        return (com.google.protobuf.ByteString) ref;\n      }\n    }\n\n    public static final int TABLENAME_FIELD_NUMBER = 3;\n    private java.lang.Object tableName_;\n    /**\n     * <code>optional string tableName = 3;</code>\n     */\n    public boolean hasTableName() {\n      return ((bitField0_ & 0x00000004) == 0x00000004);\n    }\n    /**\n     * <code>optional string tableName = 3;</code>\n     */\n    public java.lang.String getTableName() {\n      java.lang.Object ref = tableName_;\n      if (ref instanceof java.lang.String) {\n        return (java.lang.String) ref;\n      } else {\n        com.google.protobuf.ByteString bs = \n            (com.google.protobuf.ByteString) ref;\n        java.lang.String s = bs.toStringUtf8();\n        if (bs.isValidUtf8()) {\n          tableName_ = s;\n        }\n        return s;\n      }\n    }\n    /**\n     * <code>optional string tableName = 3;</code>\n     */\n    public com.google.protobuf.ByteString\n        getTableNameBytes() {\n      java.lang.Object ref = tableName_;\n      if (ref instanceof java.lang.String) {\n        com.google.protobuf.ByteString b = \n            com.google.protobuf.ByteString.copyFromUtf8(\n                (java.lang.String) ref);\n        tableName_ = b;\n        return b;\n      } else {\n        return (com.google.protobuf.ByteString) ref;\n      }\n    }\n\n    public static final int EVENTTYPE_FIELD_NUMBER = 4;\n    private java.lang.Object eventType_;\n    /**\n     * <code>optional string eventType = 4;</code>\n     *\n     * <pre>\n     **变更数据的操作类型(I/U/D)*\n     * </pre>\n     */\n    public boolean hasEventType() {\n      return ((bitField0_ & 0x00000008) == 0x00000008);\n    }\n    /**\n     * <code>optional string eventType = 4;</code>\n     *\n     * <pre>\n     **变更数据的操作类型(I/U/D)*\n     * </pre>\n     */\n    public java.lang.String getEventType() {\n      java.lang.Object ref = eventType_;\n      if (ref instanceof java.lang.String) {\n        return (java.lang.String) ref;\n      } else {\n        com.google.protobuf.ByteString bs = \n            (com.google.protobuf.ByteString) ref;\n        java.lang.String s = bs.toStringUtf8();\n        if (bs.isValidUtf8()) {\n          eventType_ = s;\n        }\n        return s;\n      }\n    }\n    /**\n     * <code>optional string eventType = 4;</code>\n     *\n     * <pre>\n     **变更数据的操作类型(I/U/D)*\n     * </pre>\n     */\n    public com.google.protobuf.ByteString\n        getEventTypeBytes() {\n      java.lang.Object ref = eventType_;\n      if (ref instanceof java.lang.String) {\n        com.google.protobuf.ByteString b = \n            com.google.protobuf.ByteString.copyFromUtf8(\n                (java.lang.String) ref);\n        eventType_ = b;\n        return b;\n      } else {\n        return (com.google.protobuf.ByteString) ref;\n      }\n    }\n\n    public static final int OLDKEYS_FIELD_NUMBER = 5;\n    private java.util.List<com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column> oldKeys_;\n    /**\n     * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.Column oldKeys = 5;</code>\n     *\n     * <pre>\n     **变更前的主键，可能是复合主键*\n     * </pre>\n     */\n    public java.util.List<com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column> getOldKeysList() {\n      return oldKeys_;\n    }\n    /**\n     * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.Column oldKeys = 5;</code>\n     *\n     * <pre>\n     **变更前的主键，可能是复合主键*\n     * </pre>\n     */\n    public java.util.List<? extends com.alibaba.otter.node.etl.model.protobuf.BatchProto.ColumnOrBuilder> \n        getOldKeysOrBuilderList() {\n      return oldKeys_;\n    }\n    /**\n     * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.Column oldKeys = 5;</code>\n     *\n     * <pre>\n     **变更前的主键，可能是复合主键*\n     * </pre>\n     */\n    public int getOldKeysCount() {\n      return oldKeys_.size();\n    }\n    /**\n     * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.Column oldKeys = 5;</code>\n     *\n     * <pre>\n     **变更前的主键，可能是复合主键*\n     * </pre>\n     */\n    public com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column getOldKeys(int index) {\n      return oldKeys_.get(index);\n    }\n    /**\n     * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.Column oldKeys = 5;</code>\n     *\n     * <pre>\n     **变更前的主键，可能是复合主键*\n     * </pre>\n     */\n    public com.alibaba.otter.node.etl.model.protobuf.BatchProto.ColumnOrBuilder getOldKeysOrBuilder(\n        int index) {\n      return oldKeys_.get(index);\n    }\n\n    public static final int KEYS_FIELD_NUMBER = 6;\n    private java.util.List<com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column> keys_;\n    /**\n     * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.Column keys = 6;</code>\n     *\n     * <pre>\n     **变更后的主键，可能是复合主键*\n     * </pre>\n     */\n    public java.util.List<com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column> getKeysList() {\n      return keys_;\n    }\n    /**\n     * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.Column keys = 6;</code>\n     *\n     * <pre>\n     **变更后的主键，可能是复合主键*\n     * </pre>\n     */\n    public java.util.List<? extends com.alibaba.otter.node.etl.model.protobuf.BatchProto.ColumnOrBuilder> \n        getKeysOrBuilderList() {\n      return keys_;\n    }\n    /**\n     * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.Column keys = 6;</code>\n     *\n     * <pre>\n     **变更后的主键，可能是复合主键*\n     * </pre>\n     */\n    public int getKeysCount() {\n      return keys_.size();\n    }\n    /**\n     * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.Column keys = 6;</code>\n     *\n     * <pre>\n     **变更后的主键，可能是复合主键*\n     * </pre>\n     */\n    public com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column getKeys(int index) {\n      return keys_.get(index);\n    }\n    /**\n     * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.Column keys = 6;</code>\n     *\n     * <pre>\n     **变更后的主键，可能是复合主键*\n     * </pre>\n     */\n    public com.alibaba.otter.node.etl.model.protobuf.BatchProto.ColumnOrBuilder getKeysOrBuilder(\n        int index) {\n      return keys_.get(index);\n    }\n\n    public static final int COLUMNS_FIELD_NUMBER = 7;\n    private java.util.List<com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column> columns_;\n    /**\n     * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.Column columns = 7;</code>\n     *\n     * <pre>\n     **变更数据的每列变更信息,不包含主键*\n     * </pre>\n     */\n    public java.util.List<com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column> getColumnsList() {\n      return columns_;\n    }\n    /**\n     * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.Column columns = 7;</code>\n     *\n     * <pre>\n     **变更数据的每列变更信息,不包含主键*\n     * </pre>\n     */\n    public java.util.List<? extends com.alibaba.otter.node.etl.model.protobuf.BatchProto.ColumnOrBuilder> \n        getColumnsOrBuilderList() {\n      return columns_;\n    }\n    /**\n     * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.Column columns = 7;</code>\n     *\n     * <pre>\n     **变更数据的每列变更信息,不包含主键*\n     * </pre>\n     */\n    public int getColumnsCount() {\n      return columns_.size();\n    }\n    /**\n     * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.Column columns = 7;</code>\n     *\n     * <pre>\n     **变更数据的每列变更信息,不包含主键*\n     * </pre>\n     */\n    public com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column getColumns(int index) {\n      return columns_.get(index);\n    }\n    /**\n     * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.Column columns = 7;</code>\n     *\n     * <pre>\n     **变更数据的每列变更信息,不包含主键*\n     * </pre>\n     */\n    public com.alibaba.otter.node.etl.model.protobuf.BatchProto.ColumnOrBuilder getColumnsOrBuilder(\n        int index) {\n      return columns_.get(index);\n    }\n\n    public static final int EXECUTETIME_FIELD_NUMBER = 8;\n    private long executeTime_;\n    /**\n     * <code>optional int64 executeTime = 8;</code>\n     *\n     * <pre>\n     **变更数据的业务时间*\n     * </pre>\n     */\n    public boolean hasExecuteTime() {\n      return ((bitField0_ & 0x00000010) == 0x00000010);\n    }\n    /**\n     * <code>optional int64 executeTime = 8;</code>\n     *\n     * <pre>\n     **变更数据的业务时间*\n     * </pre>\n     */\n    public long getExecuteTime() {\n      return executeTime_;\n    }\n\n    public static final int PAIRID_FIELD_NUMBER = 9;\n    private long pairId_;\n    /**\n     * <code>optional int64 pairId = 9;</code>\n     *\n     * <pre>\n     **映射规则id*\n     * </pre>\n     */\n    public boolean hasPairId() {\n      return ((bitField0_ & 0x00000020) == 0x00000020);\n    }\n    /**\n     * <code>optional int64 pairId = 9;</code>\n     *\n     * <pre>\n     **映射规则id*\n     * </pre>\n     */\n    public long getPairId() {\n      return pairId_;\n    }\n\n    public static final int SYNCMODE_FIELD_NUMBER = 10;\n    private java.lang.Object syncMode_;\n    /**\n     * <code>optional string syncMode = 10;</code>\n     *\n     * <pre>\n     **同步模式(R/F)*\n     * </pre>\n     */\n    public boolean hasSyncMode() {\n      return ((bitField0_ & 0x00000040) == 0x00000040);\n    }\n    /**\n     * <code>optional string syncMode = 10;</code>\n     *\n     * <pre>\n     **同步模式(R/F)*\n     * </pre>\n     */\n    public java.lang.String getSyncMode() {\n      java.lang.Object ref = syncMode_;\n      if (ref instanceof java.lang.String) {\n        return (java.lang.String) ref;\n      } else {\n        com.google.protobuf.ByteString bs = \n            (com.google.protobuf.ByteString) ref;\n        java.lang.String s = bs.toStringUtf8();\n        if (bs.isValidUtf8()) {\n          syncMode_ = s;\n        }\n        return s;\n      }\n    }\n    /**\n     * <code>optional string syncMode = 10;</code>\n     *\n     * <pre>\n     **同步模式(R/F)*\n     * </pre>\n     */\n    public com.google.protobuf.ByteString\n        getSyncModeBytes() {\n      java.lang.Object ref = syncMode_;\n      if (ref instanceof java.lang.String) {\n        com.google.protobuf.ByteString b = \n            com.google.protobuf.ByteString.copyFromUtf8(\n                (java.lang.String) ref);\n        syncMode_ = b;\n        return b;\n      } else {\n        return (com.google.protobuf.ByteString) ref;\n      }\n    }\n\n    public static final int SYNCCONSISTENCY_FIELD_NUMBER = 11;\n    private java.lang.Object syncConsistency_;\n    /**\n     * <code>optional string syncConsistency = 11;</code>\n     *\n     * <pre>\n     **同步一致性(B/S/M) *\n     * </pre>\n     */\n    public boolean hasSyncConsistency() {\n      return ((bitField0_ & 0x00000080) == 0x00000080);\n    }\n    /**\n     * <code>optional string syncConsistency = 11;</code>\n     *\n     * <pre>\n     **同步一致性(B/S/M) *\n     * </pre>\n     */\n    public java.lang.String getSyncConsistency() {\n      java.lang.Object ref = syncConsistency_;\n      if (ref instanceof java.lang.String) {\n        return (java.lang.String) ref;\n      } else {\n        com.google.protobuf.ByteString bs = \n            (com.google.protobuf.ByteString) ref;\n        java.lang.String s = bs.toStringUtf8();\n        if (bs.isValidUtf8()) {\n          syncConsistency_ = s;\n        }\n        return s;\n      }\n    }\n    /**\n     * <code>optional string syncConsistency = 11;</code>\n     *\n     * <pre>\n     **同步一致性(B/S/M) *\n     * </pre>\n     */\n    public com.google.protobuf.ByteString\n        getSyncConsistencyBytes() {\n      java.lang.Object ref = syncConsistency_;\n      if (ref instanceof java.lang.String) {\n        com.google.protobuf.ByteString b = \n            com.google.protobuf.ByteString.copyFromUtf8(\n                (java.lang.String) ref);\n        syncConsistency_ = b;\n        return b;\n      } else {\n        return (com.google.protobuf.ByteString) ref;\n      }\n    }\n\n    public static final int SIZE_FIELD_NUMBER = 12;\n    private long size_;\n    /**\n     * <code>optional int64 size = 12;</code>\n     *\n     * <pre>\n     ** eventsize *\n     * </pre>\n     */\n    public boolean hasSize() {\n      return ((bitField0_ & 0x00000100) == 0x00000100);\n    }\n    /**\n     * <code>optional int64 size = 12;</code>\n     *\n     * <pre>\n     ** eventsize *\n     * </pre>\n     */\n    public long getSize() {\n      return size_;\n    }\n\n    public static final int REMEDY_FIELD_NUMBER = 13;\n    private boolean remedy_;\n    /**\n     * <code>optional bool remedy = 13;</code>\n     *\n     * <pre>\n     ** isRemedy *\n     * </pre>\n     */\n    public boolean hasRemedy() {\n      return ((bitField0_ & 0x00000200) == 0x00000200);\n    }\n    /**\n     * <code>optional bool remedy = 13;</code>\n     *\n     * <pre>\n     ** isRemedy *\n     * </pre>\n     */\n    public boolean getRemedy() {\n      return remedy_;\n    }\n\n    public static final int SQL_FIELD_NUMBER = 14;\n    private java.lang.Object sql_;\n    /**\n     * <code>optional string sql = 14;</code>\n     *\n     * <pre>\n     ** dml/ddl sql *\n     * </pre>\n     */\n    public boolean hasSql() {\n      return ((bitField0_ & 0x00000400) == 0x00000400);\n    }\n    /**\n     * <code>optional string sql = 14;</code>\n     *\n     * <pre>\n     ** dml/ddl sql *\n     * </pre>\n     */\n    public java.lang.String getSql() {\n      java.lang.Object ref = sql_;\n      if (ref instanceof java.lang.String) {\n        return (java.lang.String) ref;\n      } else {\n        com.google.protobuf.ByteString bs = \n            (com.google.protobuf.ByteString) ref;\n        java.lang.String s = bs.toStringUtf8();\n        if (bs.isValidUtf8()) {\n          sql_ = s;\n        }\n        return s;\n      }\n    }\n    /**\n     * <code>optional string sql = 14;</code>\n     *\n     * <pre>\n     ** dml/ddl sql *\n     * </pre>\n     */\n    public com.google.protobuf.ByteString\n        getSqlBytes() {\n      java.lang.Object ref = sql_;\n      if (ref instanceof java.lang.String) {\n        com.google.protobuf.ByteString b = \n            com.google.protobuf.ByteString.copyFromUtf8(\n                (java.lang.String) ref);\n        sql_ = b;\n        return b;\n      } else {\n        return (com.google.protobuf.ByteString) ref;\n      }\n    }\n\n    public static final int DDLSCHEMANAME_FIELD_NUMBER = 15;\n    private java.lang.Object ddlSchemaName_;\n    /**\n     * <code>optional string ddlSchemaName = 15;</code>\n     *\n     * <pre>\n     ** current ddl schemaName *\n     * </pre>\n     */\n    public boolean hasDdlSchemaName() {\n      return ((bitField0_ & 0x00000800) == 0x00000800);\n    }\n    /**\n     * <code>optional string ddlSchemaName = 15;</code>\n     *\n     * <pre>\n     ** current ddl schemaName *\n     * </pre>\n     */\n    public java.lang.String getDdlSchemaName() {\n      java.lang.Object ref = ddlSchemaName_;\n      if (ref instanceof java.lang.String) {\n        return (java.lang.String) ref;\n      } else {\n        com.google.protobuf.ByteString bs = \n            (com.google.protobuf.ByteString) ref;\n        java.lang.String s = bs.toStringUtf8();\n        if (bs.isValidUtf8()) {\n          ddlSchemaName_ = s;\n        }\n        return s;\n      }\n    }\n    /**\n     * <code>optional string ddlSchemaName = 15;</code>\n     *\n     * <pre>\n     ** current ddl schemaName *\n     * </pre>\n     */\n    public com.google.protobuf.ByteString\n        getDdlSchemaNameBytes() {\n      java.lang.Object ref = ddlSchemaName_;\n      if (ref instanceof java.lang.String) {\n        com.google.protobuf.ByteString b = \n            com.google.protobuf.ByteString.copyFromUtf8(\n                (java.lang.String) ref);\n        ddlSchemaName_ = b;\n        return b;\n      } else {\n        return (com.google.protobuf.ByteString) ref;\n      }\n    }\n\n    public static final int HINT_FIELD_NUMBER = 16;\n    private java.lang.Object hint_;\n    /**\n     * <code>optional string hint = 16;</code>\n     *\n     * <pre>\n     ** dml/ddl sql *\n     * </pre>\n     */\n    public boolean hasHint() {\n      return ((bitField0_ & 0x00001000) == 0x00001000);\n    }\n    /**\n     * <code>optional string hint = 16;</code>\n     *\n     * <pre>\n     ** dml/ddl sql *\n     * </pre>\n     */\n    public java.lang.String getHint() {\n      java.lang.Object ref = hint_;\n      if (ref instanceof java.lang.String) {\n        return (java.lang.String) ref;\n      } else {\n        com.google.protobuf.ByteString bs = \n            (com.google.protobuf.ByteString) ref;\n        java.lang.String s = bs.toStringUtf8();\n        if (bs.isValidUtf8()) {\n          hint_ = s;\n        }\n        return s;\n      }\n    }\n    /**\n     * <code>optional string hint = 16;</code>\n     *\n     * <pre>\n     ** dml/ddl sql *\n     * </pre>\n     */\n    public com.google.protobuf.ByteString\n        getHintBytes() {\n      java.lang.Object ref = hint_;\n      if (ref instanceof java.lang.String) {\n        com.google.protobuf.ByteString b = \n            com.google.protobuf.ByteString.copyFromUtf8(\n                (java.lang.String) ref);\n        hint_ = b;\n        return b;\n      } else {\n        return (com.google.protobuf.ByteString) ref;\n      }\n    }\n\n    public static final int WITHOUTSCHEMA_FIELD_NUMBER = 17;\n    private boolean withoutSchema_;\n    /**\n     * <code>optional bool withoutSchema = 17;</code>\n     *\n     * <pre>\n     ** without schema *\n     * </pre>\n     */\n    public boolean hasWithoutSchema() {\n      return ((bitField0_ & 0x00002000) == 0x00002000);\n    }\n    /**\n     * <code>optional bool withoutSchema = 17;</code>\n     *\n     * <pre>\n     ** without schema *\n     * </pre>\n     */\n    public boolean getWithoutSchema() {\n      return withoutSchema_;\n    }\n\n    private void initFields() {\n      tableId_ = 0L;\n      schemaName_ = \"\";\n      tableName_ = \"\";\n      eventType_ = \"\";\n      oldKeys_ = java.util.Collections.emptyList();\n      keys_ = java.util.Collections.emptyList();\n      columns_ = java.util.Collections.emptyList();\n      executeTime_ = 0L;\n      pairId_ = 0L;\n      syncMode_ = \"\";\n      syncConsistency_ = \"\";\n      size_ = 0L;\n      remedy_ = false;\n      sql_ = \"\";\n      ddlSchemaName_ = \"\";\n      hint_ = \"\";\n      withoutSchema_ = false;\n    }\n    private byte memoizedIsInitialized = -1;\n    public final boolean isInitialized() {\n      byte isInitialized = memoizedIsInitialized;\n      if (isInitialized == 1) return true;\n      if (isInitialized == 0) return false;\n\n      memoizedIsInitialized = 1;\n      return true;\n    }\n\n    public void writeTo(com.google.protobuf.CodedOutputStream output)\n                        throws java.io.IOException {\n      getSerializedSize();\n      if (((bitField0_ & 0x00000001) == 0x00000001)) {\n        output.writeInt64(1, tableId_);\n      }\n      if (((bitField0_ & 0x00000002) == 0x00000002)) {\n        output.writeBytes(2, getSchemaNameBytes());\n      }\n      if (((bitField0_ & 0x00000004) == 0x00000004)) {\n        output.writeBytes(3, getTableNameBytes());\n      }\n      if (((bitField0_ & 0x00000008) == 0x00000008)) {\n        output.writeBytes(4, getEventTypeBytes());\n      }\n      for (int i = 0; i < oldKeys_.size(); i++) {\n        output.writeMessage(5, oldKeys_.get(i));\n      }\n      for (int i = 0; i < keys_.size(); i++) {\n        output.writeMessage(6, keys_.get(i));\n      }\n      for (int i = 0; i < columns_.size(); i++) {\n        output.writeMessage(7, columns_.get(i));\n      }\n      if (((bitField0_ & 0x00000010) == 0x00000010)) {\n        output.writeInt64(8, executeTime_);\n      }\n      if (((bitField0_ & 0x00000020) == 0x00000020)) {\n        output.writeInt64(9, pairId_);\n      }\n      if (((bitField0_ & 0x00000040) == 0x00000040)) {\n        output.writeBytes(10, getSyncModeBytes());\n      }\n      if (((bitField0_ & 0x00000080) == 0x00000080)) {\n        output.writeBytes(11, getSyncConsistencyBytes());\n      }\n      if (((bitField0_ & 0x00000100) == 0x00000100)) {\n        output.writeInt64(12, size_);\n      }\n      if (((bitField0_ & 0x00000200) == 0x00000200)) {\n        output.writeBool(13, remedy_);\n      }\n      if (((bitField0_ & 0x00000400) == 0x00000400)) {\n        output.writeBytes(14, getSqlBytes());\n      }\n      if (((bitField0_ & 0x00000800) == 0x00000800)) {\n        output.writeBytes(15, getDdlSchemaNameBytes());\n      }\n      if (((bitField0_ & 0x00001000) == 0x00001000)) {\n        output.writeBytes(16, getHintBytes());\n      }\n      if (((bitField0_ & 0x00002000) == 0x00002000)) {\n        output.writeBool(17, withoutSchema_);\n      }\n      getUnknownFields().writeTo(output);\n    }\n\n    private int memoizedSerializedSize = -1;\n    public int getSerializedSize() {\n      int size = memoizedSerializedSize;\n      if (size != -1) return size;\n\n      size = 0;\n      if (((bitField0_ & 0x00000001) == 0x00000001)) {\n        size += com.google.protobuf.CodedOutputStream\n          .computeInt64Size(1, tableId_);\n      }\n      if (((bitField0_ & 0x00000002) == 0x00000002)) {\n        size += com.google.protobuf.CodedOutputStream\n          .computeBytesSize(2, getSchemaNameBytes());\n      }\n      if (((bitField0_ & 0x00000004) == 0x00000004)) {\n        size += com.google.protobuf.CodedOutputStream\n          .computeBytesSize(3, getTableNameBytes());\n      }\n      if (((bitField0_ & 0x00000008) == 0x00000008)) {\n        size += com.google.protobuf.CodedOutputStream\n          .computeBytesSize(4, getEventTypeBytes());\n      }\n      for (int i = 0; i < oldKeys_.size(); i++) {\n        size += com.google.protobuf.CodedOutputStream\n          .computeMessageSize(5, oldKeys_.get(i));\n      }\n      for (int i = 0; i < keys_.size(); i++) {\n        size += com.google.protobuf.CodedOutputStream\n          .computeMessageSize(6, keys_.get(i));\n      }\n      for (int i = 0; i < columns_.size(); i++) {\n        size += com.google.protobuf.CodedOutputStream\n          .computeMessageSize(7, columns_.get(i));\n      }\n      if (((bitField0_ & 0x00000010) == 0x00000010)) {\n        size += com.google.protobuf.CodedOutputStream\n          .computeInt64Size(8, executeTime_);\n      }\n      if (((bitField0_ & 0x00000020) == 0x00000020)) {\n        size += com.google.protobuf.CodedOutputStream\n          .computeInt64Size(9, pairId_);\n      }\n      if (((bitField0_ & 0x00000040) == 0x00000040)) {\n        size += com.google.protobuf.CodedOutputStream\n          .computeBytesSize(10, getSyncModeBytes());\n      }\n      if (((bitField0_ & 0x00000080) == 0x00000080)) {\n        size += com.google.protobuf.CodedOutputStream\n          .computeBytesSize(11, getSyncConsistencyBytes());\n      }\n      if (((bitField0_ & 0x00000100) == 0x00000100)) {\n        size += com.google.protobuf.CodedOutputStream\n          .computeInt64Size(12, size_);\n      }\n      if (((bitField0_ & 0x00000200) == 0x00000200)) {\n        size += com.google.protobuf.CodedOutputStream\n          .computeBoolSize(13, remedy_);\n      }\n      if (((bitField0_ & 0x00000400) == 0x00000400)) {\n        size += com.google.protobuf.CodedOutputStream\n          .computeBytesSize(14, getSqlBytes());\n      }\n      if (((bitField0_ & 0x00000800) == 0x00000800)) {\n        size += com.google.protobuf.CodedOutputStream\n          .computeBytesSize(15, getDdlSchemaNameBytes());\n      }\n      if (((bitField0_ & 0x00001000) == 0x00001000)) {\n        size += com.google.protobuf.CodedOutputStream\n          .computeBytesSize(16, getHintBytes());\n      }\n      if (((bitField0_ & 0x00002000) == 0x00002000)) {\n        size += com.google.protobuf.CodedOutputStream\n          .computeBoolSize(17, withoutSchema_);\n      }\n      size += getUnknownFields().getSerializedSize();\n      memoizedSerializedSize = size;\n      return size;\n    }\n\n    private static final long serialVersionUID = 0L;\n    @java.lang.Override\n    protected java.lang.Object writeReplace()\n        throws java.io.ObjectStreamException {\n      return super.writeReplace();\n    }\n\n    public static com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowData parseFrom(\n        com.google.protobuf.ByteString data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return PARSER.parseFrom(data);\n    }\n    public static com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowData parseFrom(\n        com.google.protobuf.ByteString data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return PARSER.parseFrom(data, extensionRegistry);\n    }\n    public static com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowData parseFrom(byte[] data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return PARSER.parseFrom(data);\n    }\n    public static com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowData parseFrom(\n        byte[] data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return PARSER.parseFrom(data, extensionRegistry);\n    }\n    public static com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowData parseFrom(java.io.InputStream input)\n        throws java.io.IOException {\n      return PARSER.parseFrom(input);\n    }\n    public static com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowData parseFrom(\n        java.io.InputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return PARSER.parseFrom(input, extensionRegistry);\n    }\n    public static com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowData parseDelimitedFrom(java.io.InputStream input)\n        throws java.io.IOException {\n      return PARSER.parseDelimitedFrom(input);\n    }\n    public static com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowData parseDelimitedFrom(\n        java.io.InputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return PARSER.parseDelimitedFrom(input, extensionRegistry);\n    }\n    public static com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowData parseFrom(\n        com.google.protobuf.CodedInputStream input)\n        throws java.io.IOException {\n      return PARSER.parseFrom(input);\n    }\n    public static com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowData parseFrom(\n        com.google.protobuf.CodedInputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return PARSER.parseFrom(input, extensionRegistry);\n    }\n\n    public static Builder newBuilder() { return Builder.create(); }\n    public Builder newBuilderForType() { return newBuilder(); }\n    public static Builder newBuilder(com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowData prototype) {\n      return newBuilder().mergeFrom(prototype);\n    }\n    public Builder toBuilder() { return newBuilder(this); }\n\n    @java.lang.Override\n    protected Builder newBuilderForType(\n        com.google.protobuf.GeneratedMessage.BuilderParent parent) {\n      Builder builder = new Builder(parent);\n      return builder;\n    }\n    /**\n     * Protobuf type {@code com.alibaba.otter.node.etl.model.protobuf.RowData}\n     *\n     * <pre>\n     **数据*\n     * </pre>\n     */\n    public static final class Builder extends\n        com.google.protobuf.GeneratedMessage.Builder<Builder> implements\n        // @@protoc_insertion_point(builder_implements:com.alibaba.otter.node.etl.model.protobuf.RowData)\n        com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowDataOrBuilder {\n      public static final com.google.protobuf.Descriptors.Descriptor\n          getDescriptor() {\n        return com.alibaba.otter.node.etl.model.protobuf.BatchProto.internal_static_com_alibaba_otter_node_etl_model_protobuf_RowData_descriptor;\n      }\n\n      protected com.google.protobuf.GeneratedMessage.FieldAccessorTable\n          internalGetFieldAccessorTable() {\n        return com.alibaba.otter.node.etl.model.protobuf.BatchProto.internal_static_com_alibaba_otter_node_etl_model_protobuf_RowData_fieldAccessorTable\n            .ensureFieldAccessorsInitialized(\n                com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowData.class, com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowData.Builder.class);\n      }\n\n      // Construct using com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowData.newBuilder()\n      private Builder() {\n        maybeForceBuilderInitialization();\n      }\n\n      private Builder(\n          com.google.protobuf.GeneratedMessage.BuilderParent parent) {\n        super(parent);\n        maybeForceBuilderInitialization();\n      }\n      private void maybeForceBuilderInitialization() {\n        if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) {\n          getOldKeysFieldBuilder();\n          getKeysFieldBuilder();\n          getColumnsFieldBuilder();\n        }\n      }\n      private static Builder create() {\n        return new Builder();\n      }\n\n      public Builder clear() {\n        super.clear();\n        tableId_ = 0L;\n        bitField0_ = (bitField0_ & ~0x00000001);\n        schemaName_ = \"\";\n        bitField0_ = (bitField0_ & ~0x00000002);\n        tableName_ = \"\";\n        bitField0_ = (bitField0_ & ~0x00000004);\n        eventType_ = \"\";\n        bitField0_ = (bitField0_ & ~0x00000008);\n        if (oldKeysBuilder_ == null) {\n          oldKeys_ = java.util.Collections.emptyList();\n          bitField0_ = (bitField0_ & ~0x00000010);\n        } else {\n          oldKeysBuilder_.clear();\n        }\n        if (keysBuilder_ == null) {\n          keys_ = java.util.Collections.emptyList();\n          bitField0_ = (bitField0_ & ~0x00000020);\n        } else {\n          keysBuilder_.clear();\n        }\n        if (columnsBuilder_ == null) {\n          columns_ = java.util.Collections.emptyList();\n          bitField0_ = (bitField0_ & ~0x00000040);\n        } else {\n          columnsBuilder_.clear();\n        }\n        executeTime_ = 0L;\n        bitField0_ = (bitField0_ & ~0x00000080);\n        pairId_ = 0L;\n        bitField0_ = (bitField0_ & ~0x00000100);\n        syncMode_ = \"\";\n        bitField0_ = (bitField0_ & ~0x00000200);\n        syncConsistency_ = \"\";\n        bitField0_ = (bitField0_ & ~0x00000400);\n        size_ = 0L;\n        bitField0_ = (bitField0_ & ~0x00000800);\n        remedy_ = false;\n        bitField0_ = (bitField0_ & ~0x00001000);\n        sql_ = \"\";\n        bitField0_ = (bitField0_ & ~0x00002000);\n        ddlSchemaName_ = \"\";\n        bitField0_ = (bitField0_ & ~0x00004000);\n        hint_ = \"\";\n        bitField0_ = (bitField0_ & ~0x00008000);\n        withoutSchema_ = false;\n        bitField0_ = (bitField0_ & ~0x00010000);\n        return this;\n      }\n\n      public Builder clone() {\n        return create().mergeFrom(buildPartial());\n      }\n\n      public com.google.protobuf.Descriptors.Descriptor\n          getDescriptorForType() {\n        return com.alibaba.otter.node.etl.model.protobuf.BatchProto.internal_static_com_alibaba_otter_node_etl_model_protobuf_RowData_descriptor;\n      }\n\n      public com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowData getDefaultInstanceForType() {\n        return com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowData.getDefaultInstance();\n      }\n\n      public com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowData build() {\n        com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowData result = buildPartial();\n        if (!result.isInitialized()) {\n          throw newUninitializedMessageException(result);\n        }\n        return result;\n      }\n\n      public com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowData buildPartial() {\n        com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowData result = new com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowData(this);\n        int from_bitField0_ = bitField0_;\n        int to_bitField0_ = 0;\n        if (((from_bitField0_ & 0x00000001) == 0x00000001)) {\n          to_bitField0_ |= 0x00000001;\n        }\n        result.tableId_ = tableId_;\n        if (((from_bitField0_ & 0x00000002) == 0x00000002)) {\n          to_bitField0_ |= 0x00000002;\n        }\n        result.schemaName_ = schemaName_;\n        if (((from_bitField0_ & 0x00000004) == 0x00000004)) {\n          to_bitField0_ |= 0x00000004;\n        }\n        result.tableName_ = tableName_;\n        if (((from_bitField0_ & 0x00000008) == 0x00000008)) {\n          to_bitField0_ |= 0x00000008;\n        }\n        result.eventType_ = eventType_;\n        if (oldKeysBuilder_ == null) {\n          if (((bitField0_ & 0x00000010) == 0x00000010)) {\n            oldKeys_ = java.util.Collections.unmodifiableList(oldKeys_);\n            bitField0_ = (bitField0_ & ~0x00000010);\n          }\n          result.oldKeys_ = oldKeys_;\n        } else {\n          result.oldKeys_ = oldKeysBuilder_.build();\n        }\n        if (keysBuilder_ == null) {\n          if (((bitField0_ & 0x00000020) == 0x00000020)) {\n            keys_ = java.util.Collections.unmodifiableList(keys_);\n            bitField0_ = (bitField0_ & ~0x00000020);\n          }\n          result.keys_ = keys_;\n        } else {\n          result.keys_ = keysBuilder_.build();\n        }\n        if (columnsBuilder_ == null) {\n          if (((bitField0_ & 0x00000040) == 0x00000040)) {\n            columns_ = java.util.Collections.unmodifiableList(columns_);\n            bitField0_ = (bitField0_ & ~0x00000040);\n          }\n          result.columns_ = columns_;\n        } else {\n          result.columns_ = columnsBuilder_.build();\n        }\n        if (((from_bitField0_ & 0x00000080) == 0x00000080)) {\n          to_bitField0_ |= 0x00000010;\n        }\n        result.executeTime_ = executeTime_;\n        if (((from_bitField0_ & 0x00000100) == 0x00000100)) {\n          to_bitField0_ |= 0x00000020;\n        }\n        result.pairId_ = pairId_;\n        if (((from_bitField0_ & 0x00000200) == 0x00000200)) {\n          to_bitField0_ |= 0x00000040;\n        }\n        result.syncMode_ = syncMode_;\n        if (((from_bitField0_ & 0x00000400) == 0x00000400)) {\n          to_bitField0_ |= 0x00000080;\n        }\n        result.syncConsistency_ = syncConsistency_;\n        if (((from_bitField0_ & 0x00000800) == 0x00000800)) {\n          to_bitField0_ |= 0x00000100;\n        }\n        result.size_ = size_;\n        if (((from_bitField0_ & 0x00001000) == 0x00001000)) {\n          to_bitField0_ |= 0x00000200;\n        }\n        result.remedy_ = remedy_;\n        if (((from_bitField0_ & 0x00002000) == 0x00002000)) {\n          to_bitField0_ |= 0x00000400;\n        }\n        result.sql_ = sql_;\n        if (((from_bitField0_ & 0x00004000) == 0x00004000)) {\n          to_bitField0_ |= 0x00000800;\n        }\n        result.ddlSchemaName_ = ddlSchemaName_;\n        if (((from_bitField0_ & 0x00008000) == 0x00008000)) {\n          to_bitField0_ |= 0x00001000;\n        }\n        result.hint_ = hint_;\n        if (((from_bitField0_ & 0x00010000) == 0x00010000)) {\n          to_bitField0_ |= 0x00002000;\n        }\n        result.withoutSchema_ = withoutSchema_;\n        result.bitField0_ = to_bitField0_;\n        onBuilt();\n        return result;\n      }\n\n      public Builder mergeFrom(com.google.protobuf.Message other) {\n        if (other instanceof com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowData) {\n          return mergeFrom((com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowData)other);\n        } else {\n          super.mergeFrom(other);\n          return this;\n        }\n      }\n\n      public Builder mergeFrom(com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowData other) {\n        if (other == com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowData.getDefaultInstance()) return this;\n        if (other.hasTableId()) {\n          setTableId(other.getTableId());\n        }\n        if (other.hasSchemaName()) {\n          bitField0_ |= 0x00000002;\n          schemaName_ = other.schemaName_;\n          onChanged();\n        }\n        if (other.hasTableName()) {\n          bitField0_ |= 0x00000004;\n          tableName_ = other.tableName_;\n          onChanged();\n        }\n        if (other.hasEventType()) {\n          bitField0_ |= 0x00000008;\n          eventType_ = other.eventType_;\n          onChanged();\n        }\n        if (oldKeysBuilder_ == null) {\n          if (!other.oldKeys_.isEmpty()) {\n            if (oldKeys_.isEmpty()) {\n              oldKeys_ = other.oldKeys_;\n              bitField0_ = (bitField0_ & ~0x00000010);\n            } else {\n              ensureOldKeysIsMutable();\n              oldKeys_.addAll(other.oldKeys_);\n            }\n            onChanged();\n          }\n        } else {\n          if (!other.oldKeys_.isEmpty()) {\n            if (oldKeysBuilder_.isEmpty()) {\n              oldKeysBuilder_.dispose();\n              oldKeysBuilder_ = null;\n              oldKeys_ = other.oldKeys_;\n              bitField0_ = (bitField0_ & ~0x00000010);\n              oldKeysBuilder_ = \n                com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ?\n                   getOldKeysFieldBuilder() : null;\n            } else {\n              oldKeysBuilder_.addAllMessages(other.oldKeys_);\n            }\n          }\n        }\n        if (keysBuilder_ == null) {\n          if (!other.keys_.isEmpty()) {\n            if (keys_.isEmpty()) {\n              keys_ = other.keys_;\n              bitField0_ = (bitField0_ & ~0x00000020);\n            } else {\n              ensureKeysIsMutable();\n              keys_.addAll(other.keys_);\n            }\n            onChanged();\n          }\n        } else {\n          if (!other.keys_.isEmpty()) {\n            if (keysBuilder_.isEmpty()) {\n              keysBuilder_.dispose();\n              keysBuilder_ = null;\n              keys_ = other.keys_;\n              bitField0_ = (bitField0_ & ~0x00000020);\n              keysBuilder_ = \n                com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ?\n                   getKeysFieldBuilder() : null;\n            } else {\n              keysBuilder_.addAllMessages(other.keys_);\n            }\n          }\n        }\n        if (columnsBuilder_ == null) {\n          if (!other.columns_.isEmpty()) {\n            if (columns_.isEmpty()) {\n              columns_ = other.columns_;\n              bitField0_ = (bitField0_ & ~0x00000040);\n            } else {\n              ensureColumnsIsMutable();\n              columns_.addAll(other.columns_);\n            }\n            onChanged();\n          }\n        } else {\n          if (!other.columns_.isEmpty()) {\n            if (columnsBuilder_.isEmpty()) {\n              columnsBuilder_.dispose();\n              columnsBuilder_ = null;\n              columns_ = other.columns_;\n              bitField0_ = (bitField0_ & ~0x00000040);\n              columnsBuilder_ = \n                com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ?\n                   getColumnsFieldBuilder() : null;\n            } else {\n              columnsBuilder_.addAllMessages(other.columns_);\n            }\n          }\n        }\n        if (other.hasExecuteTime()) {\n          setExecuteTime(other.getExecuteTime());\n        }\n        if (other.hasPairId()) {\n          setPairId(other.getPairId());\n        }\n        if (other.hasSyncMode()) {\n          bitField0_ |= 0x00000200;\n          syncMode_ = other.syncMode_;\n          onChanged();\n        }\n        if (other.hasSyncConsistency()) {\n          bitField0_ |= 0x00000400;\n          syncConsistency_ = other.syncConsistency_;\n          onChanged();\n        }\n        if (other.hasSize()) {\n          setSize(other.getSize());\n        }\n        if (other.hasRemedy()) {\n          setRemedy(other.getRemedy());\n        }\n        if (other.hasSql()) {\n          bitField0_ |= 0x00002000;\n          sql_ = other.sql_;\n          onChanged();\n        }\n        if (other.hasDdlSchemaName()) {\n          bitField0_ |= 0x00004000;\n          ddlSchemaName_ = other.ddlSchemaName_;\n          onChanged();\n        }\n        if (other.hasHint()) {\n          bitField0_ |= 0x00008000;\n          hint_ = other.hint_;\n          onChanged();\n        }\n        if (other.hasWithoutSchema()) {\n          setWithoutSchema(other.getWithoutSchema());\n        }\n        this.mergeUnknownFields(other.getUnknownFields());\n        return this;\n      }\n\n      public final boolean isInitialized() {\n        return true;\n      }\n\n      public Builder mergeFrom(\n          com.google.protobuf.CodedInputStream input,\n          com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n          throws java.io.IOException {\n        com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowData parsedMessage = null;\n        try {\n          parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry);\n        } catch (com.google.protobuf.InvalidProtocolBufferException e) {\n          parsedMessage = (com.alibaba.otter.node.etl.model.protobuf.BatchProto.RowData) e.getUnfinishedMessage();\n          throw e;\n        } finally {\n          if (parsedMessage != null) {\n            mergeFrom(parsedMessage);\n          }\n        }\n        return this;\n      }\n      private int bitField0_;\n\n      private long tableId_ ;\n      /**\n       * <code>optional int64 tableId = 1;</code>\n       */\n      public boolean hasTableId() {\n        return ((bitField0_ & 0x00000001) == 0x00000001);\n      }\n      /**\n       * <code>optional int64 tableId = 1;</code>\n       */\n      public long getTableId() {\n        return tableId_;\n      }\n      /**\n       * <code>optional int64 tableId = 1;</code>\n       */\n      public Builder setTableId(long value) {\n        bitField0_ |= 0x00000001;\n        tableId_ = value;\n        onChanged();\n        return this;\n      }\n      /**\n       * <code>optional int64 tableId = 1;</code>\n       */\n      public Builder clearTableId() {\n        bitField0_ = (bitField0_ & ~0x00000001);\n        tableId_ = 0L;\n        onChanged();\n        return this;\n      }\n\n      private java.lang.Object schemaName_ = \"\";\n      /**\n       * <code>optional string schemaName = 2;</code>\n       */\n      public boolean hasSchemaName() {\n        return ((bitField0_ & 0x00000002) == 0x00000002);\n      }\n      /**\n       * <code>optional string schemaName = 2;</code>\n       */\n      public java.lang.String getSchemaName() {\n        java.lang.Object ref = schemaName_;\n        if (!(ref instanceof java.lang.String)) {\n          com.google.protobuf.ByteString bs =\n              (com.google.protobuf.ByteString) ref;\n          java.lang.String s = bs.toStringUtf8();\n          if (bs.isValidUtf8()) {\n            schemaName_ = s;\n          }\n          return s;\n        } else {\n          return (java.lang.String) ref;\n        }\n      }\n      /**\n       * <code>optional string schemaName = 2;</code>\n       */\n      public com.google.protobuf.ByteString\n          getSchemaNameBytes() {\n        java.lang.Object ref = schemaName_;\n        if (ref instanceof String) {\n          com.google.protobuf.ByteString b = \n              com.google.protobuf.ByteString.copyFromUtf8(\n                  (java.lang.String) ref);\n          schemaName_ = b;\n          return b;\n        } else {\n          return (com.google.protobuf.ByteString) ref;\n        }\n      }\n      /**\n       * <code>optional string schemaName = 2;</code>\n       */\n      public Builder setSchemaName(\n          java.lang.String value) {\n        if (value == null) {\n    throw new NullPointerException();\n  }\n  bitField0_ |= 0x00000002;\n        schemaName_ = value;\n        onChanged();\n        return this;\n      }\n      /**\n       * <code>optional string schemaName = 2;</code>\n       */\n      public Builder clearSchemaName() {\n        bitField0_ = (bitField0_ & ~0x00000002);\n        schemaName_ = getDefaultInstance().getSchemaName();\n        onChanged();\n        return this;\n      }\n      /**\n       * <code>optional string schemaName = 2;</code>\n       */\n      public Builder setSchemaNameBytes(\n          com.google.protobuf.ByteString value) {\n        if (value == null) {\n    throw new NullPointerException();\n  }\n  bitField0_ |= 0x00000002;\n        schemaName_ = value;\n        onChanged();\n        return this;\n      }\n\n      private java.lang.Object tableName_ = \"\";\n      /**\n       * <code>optional string tableName = 3;</code>\n       */\n      public boolean hasTableName() {\n        return ((bitField0_ & 0x00000004) == 0x00000004);\n      }\n      /**\n       * <code>optional string tableName = 3;</code>\n       */\n      public java.lang.String getTableName() {\n        java.lang.Object ref = tableName_;\n        if (!(ref instanceof java.lang.String)) {\n          com.google.protobuf.ByteString bs =\n              (com.google.protobuf.ByteString) ref;\n          java.lang.String s = bs.toStringUtf8();\n          if (bs.isValidUtf8()) {\n            tableName_ = s;\n          }\n          return s;\n        } else {\n          return (java.lang.String) ref;\n        }\n      }\n      /**\n       * <code>optional string tableName = 3;</code>\n       */\n      public com.google.protobuf.ByteString\n          getTableNameBytes() {\n        java.lang.Object ref = tableName_;\n        if (ref instanceof String) {\n          com.google.protobuf.ByteString b = \n              com.google.protobuf.ByteString.copyFromUtf8(\n                  (java.lang.String) ref);\n          tableName_ = b;\n          return b;\n        } else {\n          return (com.google.protobuf.ByteString) ref;\n        }\n      }\n      /**\n       * <code>optional string tableName = 3;</code>\n       */\n      public Builder setTableName(\n          java.lang.String value) {\n        if (value == null) {\n    throw new NullPointerException();\n  }\n  bitField0_ |= 0x00000004;\n        tableName_ = value;\n        onChanged();\n        return this;\n      }\n      /**\n       * <code>optional string tableName = 3;</code>\n       */\n      public Builder clearTableName() {\n        bitField0_ = (bitField0_ & ~0x00000004);\n        tableName_ = getDefaultInstance().getTableName();\n        onChanged();\n        return this;\n      }\n      /**\n       * <code>optional string tableName = 3;</code>\n       */\n      public Builder setTableNameBytes(\n          com.google.protobuf.ByteString value) {\n        if (value == null) {\n    throw new NullPointerException();\n  }\n  bitField0_ |= 0x00000004;\n        tableName_ = value;\n        onChanged();\n        return this;\n      }\n\n      private java.lang.Object eventType_ = \"\";\n      /**\n       * <code>optional string eventType = 4;</code>\n       *\n       * <pre>\n       **变更数据的操作类型(I/U/D)*\n       * </pre>\n       */\n      public boolean hasEventType() {\n        return ((bitField0_ & 0x00000008) == 0x00000008);\n      }\n      /**\n       * <code>optional string eventType = 4;</code>\n       *\n       * <pre>\n       **变更数据的操作类型(I/U/D)*\n       * </pre>\n       */\n      public java.lang.String getEventType() {\n        java.lang.Object ref = eventType_;\n        if (!(ref instanceof java.lang.String)) {\n          com.google.protobuf.ByteString bs =\n              (com.google.protobuf.ByteString) ref;\n          java.lang.String s = bs.toStringUtf8();\n          if (bs.isValidUtf8()) {\n            eventType_ = s;\n          }\n          return s;\n        } else {\n          return (java.lang.String) ref;\n        }\n      }\n      /**\n       * <code>optional string eventType = 4;</code>\n       *\n       * <pre>\n       **变更数据的操作类型(I/U/D)*\n       * </pre>\n       */\n      public com.google.protobuf.ByteString\n          getEventTypeBytes() {\n        java.lang.Object ref = eventType_;\n        if (ref instanceof String) {\n          com.google.protobuf.ByteString b = \n              com.google.protobuf.ByteString.copyFromUtf8(\n                  (java.lang.String) ref);\n          eventType_ = b;\n          return b;\n        } else {\n          return (com.google.protobuf.ByteString) ref;\n        }\n      }\n      /**\n       * <code>optional string eventType = 4;</code>\n       *\n       * <pre>\n       **变更数据的操作类型(I/U/D)*\n       * </pre>\n       */\n      public Builder setEventType(\n          java.lang.String value) {\n        if (value == null) {\n    throw new NullPointerException();\n  }\n  bitField0_ |= 0x00000008;\n        eventType_ = value;\n        onChanged();\n        return this;\n      }\n      /**\n       * <code>optional string eventType = 4;</code>\n       *\n       * <pre>\n       **变更数据的操作类型(I/U/D)*\n       * </pre>\n       */\n      public Builder clearEventType() {\n        bitField0_ = (bitField0_ & ~0x00000008);\n        eventType_ = getDefaultInstance().getEventType();\n        onChanged();\n        return this;\n      }\n      /**\n       * <code>optional string eventType = 4;</code>\n       *\n       * <pre>\n       **变更数据的操作类型(I/U/D)*\n       * </pre>\n       */\n      public Builder setEventTypeBytes(\n          com.google.protobuf.ByteString value) {\n        if (value == null) {\n    throw new NullPointerException();\n  }\n  bitField0_ |= 0x00000008;\n        eventType_ = value;\n        onChanged();\n        return this;\n      }\n\n      private java.util.List<com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column> oldKeys_ =\n        java.util.Collections.emptyList();\n      private void ensureOldKeysIsMutable() {\n        if (!((bitField0_ & 0x00000010) == 0x00000010)) {\n          oldKeys_ = new java.util.ArrayList<com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column>(oldKeys_);\n          bitField0_ |= 0x00000010;\n         }\n      }\n\n      private com.google.protobuf.RepeatedFieldBuilder<\n          com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column, com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column.Builder, com.alibaba.otter.node.etl.model.protobuf.BatchProto.ColumnOrBuilder> oldKeysBuilder_;\n\n      /**\n       * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.Column oldKeys = 5;</code>\n       *\n       * <pre>\n       **变更前的主键，可能是复合主键*\n       * </pre>\n       */\n      public java.util.List<com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column> getOldKeysList() {\n        if (oldKeysBuilder_ == null) {\n          return java.util.Collections.unmodifiableList(oldKeys_);\n        } else {\n          return oldKeysBuilder_.getMessageList();\n        }\n      }\n      /**\n       * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.Column oldKeys = 5;</code>\n       *\n       * <pre>\n       **变更前的主键，可能是复合主键*\n       * </pre>\n       */\n      public int getOldKeysCount() {\n        if (oldKeysBuilder_ == null) {\n          return oldKeys_.size();\n        } else {\n          return oldKeysBuilder_.getCount();\n        }\n      }\n      /**\n       * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.Column oldKeys = 5;</code>\n       *\n       * <pre>\n       **变更前的主键，可能是复合主键*\n       * </pre>\n       */\n      public com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column getOldKeys(int index) {\n        if (oldKeysBuilder_ == null) {\n          return oldKeys_.get(index);\n        } else {\n          return oldKeysBuilder_.getMessage(index);\n        }\n      }\n      /**\n       * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.Column oldKeys = 5;</code>\n       *\n       * <pre>\n       **变更前的主键，可能是复合主键*\n       * </pre>\n       */\n      public Builder setOldKeys(\n          int index, com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column value) {\n        if (oldKeysBuilder_ == null) {\n          if (value == null) {\n            throw new NullPointerException();\n          }\n          ensureOldKeysIsMutable();\n          oldKeys_.set(index, value);\n          onChanged();\n        } else {\n          oldKeysBuilder_.setMessage(index, value);\n        }\n        return this;\n      }\n      /**\n       * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.Column oldKeys = 5;</code>\n       *\n       * <pre>\n       **变更前的主键，可能是复合主键*\n       * </pre>\n       */\n      public Builder setOldKeys(\n          int index, com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column.Builder builderForValue) {\n        if (oldKeysBuilder_ == null) {\n          ensureOldKeysIsMutable();\n          oldKeys_.set(index, builderForValue.build());\n          onChanged();\n        } else {\n          oldKeysBuilder_.setMessage(index, builderForValue.build());\n        }\n        return this;\n      }\n      /**\n       * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.Column oldKeys = 5;</code>\n       *\n       * <pre>\n       **变更前的主键，可能是复合主键*\n       * </pre>\n       */\n      public Builder addOldKeys(com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column value) {\n        if (oldKeysBuilder_ == null) {\n          if (value == null) {\n            throw new NullPointerException();\n          }\n          ensureOldKeysIsMutable();\n          oldKeys_.add(value);\n          onChanged();\n        } else {\n          oldKeysBuilder_.addMessage(value);\n        }\n        return this;\n      }\n      /**\n       * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.Column oldKeys = 5;</code>\n       *\n       * <pre>\n       **变更前的主键，可能是复合主键*\n       * </pre>\n       */\n      public Builder addOldKeys(\n          int index, com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column value) {\n        if (oldKeysBuilder_ == null) {\n          if (value == null) {\n            throw new NullPointerException();\n          }\n          ensureOldKeysIsMutable();\n          oldKeys_.add(index, value);\n          onChanged();\n        } else {\n          oldKeysBuilder_.addMessage(index, value);\n        }\n        return this;\n      }\n      /**\n       * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.Column oldKeys = 5;</code>\n       *\n       * <pre>\n       **变更前的主键，可能是复合主键*\n       * </pre>\n       */\n      public Builder addOldKeys(\n          com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column.Builder builderForValue) {\n        if (oldKeysBuilder_ == null) {\n          ensureOldKeysIsMutable();\n          oldKeys_.add(builderForValue.build());\n          onChanged();\n        } else {\n          oldKeysBuilder_.addMessage(builderForValue.build());\n        }\n        return this;\n      }\n      /**\n       * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.Column oldKeys = 5;</code>\n       *\n       * <pre>\n       **变更前的主键，可能是复合主键*\n       * </pre>\n       */\n      public Builder addOldKeys(\n          int index, com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column.Builder builderForValue) {\n        if (oldKeysBuilder_ == null) {\n          ensureOldKeysIsMutable();\n          oldKeys_.add(index, builderForValue.build());\n          onChanged();\n        } else {\n          oldKeysBuilder_.addMessage(index, builderForValue.build());\n        }\n        return this;\n      }\n      /**\n       * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.Column oldKeys = 5;</code>\n       *\n       * <pre>\n       **变更前的主键，可能是复合主键*\n       * </pre>\n       */\n      public Builder addAllOldKeys(\n          java.lang.Iterable<? extends com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column> values) {\n        if (oldKeysBuilder_ == null) {\n          ensureOldKeysIsMutable();\n          com.google.protobuf.AbstractMessageLite.Builder.addAll(\n              values, oldKeys_);\n          onChanged();\n        } else {\n          oldKeysBuilder_.addAllMessages(values);\n        }\n        return this;\n      }\n      /**\n       * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.Column oldKeys = 5;</code>\n       *\n       * <pre>\n       **变更前的主键，可能是复合主键*\n       * </pre>\n       */\n      public Builder clearOldKeys() {\n        if (oldKeysBuilder_ == null) {\n          oldKeys_ = java.util.Collections.emptyList();\n          bitField0_ = (bitField0_ & ~0x00000010);\n          onChanged();\n        } else {\n          oldKeysBuilder_.clear();\n        }\n        return this;\n      }\n      /**\n       * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.Column oldKeys = 5;</code>\n       *\n       * <pre>\n       **变更前的主键，可能是复合主键*\n       * </pre>\n       */\n      public Builder removeOldKeys(int index) {\n        if (oldKeysBuilder_ == null) {\n          ensureOldKeysIsMutable();\n          oldKeys_.remove(index);\n          onChanged();\n        } else {\n          oldKeysBuilder_.remove(index);\n        }\n        return this;\n      }\n      /**\n       * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.Column oldKeys = 5;</code>\n       *\n       * <pre>\n       **变更前的主键，可能是复合主键*\n       * </pre>\n       */\n      public com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column.Builder getOldKeysBuilder(\n          int index) {\n        return getOldKeysFieldBuilder().getBuilder(index);\n      }\n      /**\n       * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.Column oldKeys = 5;</code>\n       *\n       * <pre>\n       **变更前的主键，可能是复合主键*\n       * </pre>\n       */\n      public com.alibaba.otter.node.etl.model.protobuf.BatchProto.ColumnOrBuilder getOldKeysOrBuilder(\n          int index) {\n        if (oldKeysBuilder_ == null) {\n          return oldKeys_.get(index);  } else {\n          return oldKeysBuilder_.getMessageOrBuilder(index);\n        }\n      }\n      /**\n       * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.Column oldKeys = 5;</code>\n       *\n       * <pre>\n       **变更前的主键，可能是复合主键*\n       * </pre>\n       */\n      public java.util.List<? extends com.alibaba.otter.node.etl.model.protobuf.BatchProto.ColumnOrBuilder> \n           getOldKeysOrBuilderList() {\n        if (oldKeysBuilder_ != null) {\n          return oldKeysBuilder_.getMessageOrBuilderList();\n        } else {\n          return java.util.Collections.unmodifiableList(oldKeys_);\n        }\n      }\n      /**\n       * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.Column oldKeys = 5;</code>\n       *\n       * <pre>\n       **变更前的主键，可能是复合主键*\n       * </pre>\n       */\n      public com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column.Builder addOldKeysBuilder() {\n        return getOldKeysFieldBuilder().addBuilder(\n            com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column.getDefaultInstance());\n      }\n      /**\n       * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.Column oldKeys = 5;</code>\n       *\n       * <pre>\n       **变更前的主键，可能是复合主键*\n       * </pre>\n       */\n      public com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column.Builder addOldKeysBuilder(\n          int index) {\n        return getOldKeysFieldBuilder().addBuilder(\n            index, com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column.getDefaultInstance());\n      }\n      /**\n       * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.Column oldKeys = 5;</code>\n       *\n       * <pre>\n       **变更前的主键，可能是复合主键*\n       * </pre>\n       */\n      public java.util.List<com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column.Builder> \n           getOldKeysBuilderList() {\n        return getOldKeysFieldBuilder().getBuilderList();\n      }\n      private com.google.protobuf.RepeatedFieldBuilder<\n          com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column, com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column.Builder, com.alibaba.otter.node.etl.model.protobuf.BatchProto.ColumnOrBuilder> \n          getOldKeysFieldBuilder() {\n        if (oldKeysBuilder_ == null) {\n          oldKeysBuilder_ = new com.google.protobuf.RepeatedFieldBuilder<\n              com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column, com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column.Builder, com.alibaba.otter.node.etl.model.protobuf.BatchProto.ColumnOrBuilder>(\n                  oldKeys_,\n                  ((bitField0_ & 0x00000010) == 0x00000010),\n                  getParentForChildren(),\n                  isClean());\n          oldKeys_ = null;\n        }\n        return oldKeysBuilder_;\n      }\n\n      private java.util.List<com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column> keys_ =\n        java.util.Collections.emptyList();\n      private void ensureKeysIsMutable() {\n        if (!((bitField0_ & 0x00000020) == 0x00000020)) {\n          keys_ = new java.util.ArrayList<com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column>(keys_);\n          bitField0_ |= 0x00000020;\n         }\n      }\n\n      private com.google.protobuf.RepeatedFieldBuilder<\n          com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column, com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column.Builder, com.alibaba.otter.node.etl.model.protobuf.BatchProto.ColumnOrBuilder> keysBuilder_;\n\n      /**\n       * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.Column keys = 6;</code>\n       *\n       * <pre>\n       **变更后的主键，可能是复合主键*\n       * </pre>\n       */\n      public java.util.List<com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column> getKeysList() {\n        if (keysBuilder_ == null) {\n          return java.util.Collections.unmodifiableList(keys_);\n        } else {\n          return keysBuilder_.getMessageList();\n        }\n      }\n      /**\n       * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.Column keys = 6;</code>\n       *\n       * <pre>\n       **变更后的主键，可能是复合主键*\n       * </pre>\n       */\n      public int getKeysCount() {\n        if (keysBuilder_ == null) {\n          return keys_.size();\n        } else {\n          return keysBuilder_.getCount();\n        }\n      }\n      /**\n       * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.Column keys = 6;</code>\n       *\n       * <pre>\n       **变更后的主键，可能是复合主键*\n       * </pre>\n       */\n      public com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column getKeys(int index) {\n        if (keysBuilder_ == null) {\n          return keys_.get(index);\n        } else {\n          return keysBuilder_.getMessage(index);\n        }\n      }\n      /**\n       * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.Column keys = 6;</code>\n       *\n       * <pre>\n       **变更后的主键，可能是复合主键*\n       * </pre>\n       */\n      public Builder setKeys(\n          int index, com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column value) {\n        if (keysBuilder_ == null) {\n          if (value == null) {\n            throw new NullPointerException();\n          }\n          ensureKeysIsMutable();\n          keys_.set(index, value);\n          onChanged();\n        } else {\n          keysBuilder_.setMessage(index, value);\n        }\n        return this;\n      }\n      /**\n       * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.Column keys = 6;</code>\n       *\n       * <pre>\n       **变更后的主键，可能是复合主键*\n       * </pre>\n       */\n      public Builder setKeys(\n          int index, com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column.Builder builderForValue) {\n        if (keysBuilder_ == null) {\n          ensureKeysIsMutable();\n          keys_.set(index, builderForValue.build());\n          onChanged();\n        } else {\n          keysBuilder_.setMessage(index, builderForValue.build());\n        }\n        return this;\n      }\n      /**\n       * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.Column keys = 6;</code>\n       *\n       * <pre>\n       **变更后的主键，可能是复合主键*\n       * </pre>\n       */\n      public Builder addKeys(com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column value) {\n        if (keysBuilder_ == null) {\n          if (value == null) {\n            throw new NullPointerException();\n          }\n          ensureKeysIsMutable();\n          keys_.add(value);\n          onChanged();\n        } else {\n          keysBuilder_.addMessage(value);\n        }\n        return this;\n      }\n      /**\n       * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.Column keys = 6;</code>\n       *\n       * <pre>\n       **变更后的主键，可能是复合主键*\n       * </pre>\n       */\n      public Builder addKeys(\n          int index, com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column value) {\n        if (keysBuilder_ == null) {\n          if (value == null) {\n            throw new NullPointerException();\n          }\n          ensureKeysIsMutable();\n          keys_.add(index, value);\n          onChanged();\n        } else {\n          keysBuilder_.addMessage(index, value);\n        }\n        return this;\n      }\n      /**\n       * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.Column keys = 6;</code>\n       *\n       * <pre>\n       **变更后的主键，可能是复合主键*\n       * </pre>\n       */\n      public Builder addKeys(\n          com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column.Builder builderForValue) {\n        if (keysBuilder_ == null) {\n          ensureKeysIsMutable();\n          keys_.add(builderForValue.build());\n          onChanged();\n        } else {\n          keysBuilder_.addMessage(builderForValue.build());\n        }\n        return this;\n      }\n      /**\n       * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.Column keys = 6;</code>\n       *\n       * <pre>\n       **变更后的主键，可能是复合主键*\n       * </pre>\n       */\n      public Builder addKeys(\n          int index, com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column.Builder builderForValue) {\n        if (keysBuilder_ == null) {\n          ensureKeysIsMutable();\n          keys_.add(index, builderForValue.build());\n          onChanged();\n        } else {\n          keysBuilder_.addMessage(index, builderForValue.build());\n        }\n        return this;\n      }\n      /**\n       * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.Column keys = 6;</code>\n       *\n       * <pre>\n       **变更后的主键，可能是复合主键*\n       * </pre>\n       */\n      public Builder addAllKeys(\n          java.lang.Iterable<? extends com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column> values) {\n        if (keysBuilder_ == null) {\n          ensureKeysIsMutable();\n          com.google.protobuf.AbstractMessageLite.Builder.addAll(\n              values, keys_);\n          onChanged();\n        } else {\n          keysBuilder_.addAllMessages(values);\n        }\n        return this;\n      }\n      /**\n       * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.Column keys = 6;</code>\n       *\n       * <pre>\n       **变更后的主键，可能是复合主键*\n       * </pre>\n       */\n      public Builder clearKeys() {\n        if (keysBuilder_ == null) {\n          keys_ = java.util.Collections.emptyList();\n          bitField0_ = (bitField0_ & ~0x00000020);\n          onChanged();\n        } else {\n          keysBuilder_.clear();\n        }\n        return this;\n      }\n      /**\n       * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.Column keys = 6;</code>\n       *\n       * <pre>\n       **变更后的主键，可能是复合主键*\n       * </pre>\n       */\n      public Builder removeKeys(int index) {\n        if (keysBuilder_ == null) {\n          ensureKeysIsMutable();\n          keys_.remove(index);\n          onChanged();\n        } else {\n          keysBuilder_.remove(index);\n        }\n        return this;\n      }\n      /**\n       * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.Column keys = 6;</code>\n       *\n       * <pre>\n       **变更后的主键，可能是复合主键*\n       * </pre>\n       */\n      public com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column.Builder getKeysBuilder(\n          int index) {\n        return getKeysFieldBuilder().getBuilder(index);\n      }\n      /**\n       * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.Column keys = 6;</code>\n       *\n       * <pre>\n       **变更后的主键，可能是复合主键*\n       * </pre>\n       */\n      public com.alibaba.otter.node.etl.model.protobuf.BatchProto.ColumnOrBuilder getKeysOrBuilder(\n          int index) {\n        if (keysBuilder_ == null) {\n          return keys_.get(index);  } else {\n          return keysBuilder_.getMessageOrBuilder(index);\n        }\n      }\n      /**\n       * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.Column keys = 6;</code>\n       *\n       * <pre>\n       **变更后的主键，可能是复合主键*\n       * </pre>\n       */\n      public java.util.List<? extends com.alibaba.otter.node.etl.model.protobuf.BatchProto.ColumnOrBuilder> \n           getKeysOrBuilderList() {\n        if (keysBuilder_ != null) {\n          return keysBuilder_.getMessageOrBuilderList();\n        } else {\n          return java.util.Collections.unmodifiableList(keys_);\n        }\n      }\n      /**\n       * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.Column keys = 6;</code>\n       *\n       * <pre>\n       **变更后的主键，可能是复合主键*\n       * </pre>\n       */\n      public com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column.Builder addKeysBuilder() {\n        return getKeysFieldBuilder().addBuilder(\n            com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column.getDefaultInstance());\n      }\n      /**\n       * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.Column keys = 6;</code>\n       *\n       * <pre>\n       **变更后的主键，可能是复合主键*\n       * </pre>\n       */\n      public com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column.Builder addKeysBuilder(\n          int index) {\n        return getKeysFieldBuilder().addBuilder(\n            index, com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column.getDefaultInstance());\n      }\n      /**\n       * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.Column keys = 6;</code>\n       *\n       * <pre>\n       **变更后的主键，可能是复合主键*\n       * </pre>\n       */\n      public java.util.List<com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column.Builder> \n           getKeysBuilderList() {\n        return getKeysFieldBuilder().getBuilderList();\n      }\n      private com.google.protobuf.RepeatedFieldBuilder<\n          com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column, com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column.Builder, com.alibaba.otter.node.etl.model.protobuf.BatchProto.ColumnOrBuilder> \n          getKeysFieldBuilder() {\n        if (keysBuilder_ == null) {\n          keysBuilder_ = new com.google.protobuf.RepeatedFieldBuilder<\n              com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column, com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column.Builder, com.alibaba.otter.node.etl.model.protobuf.BatchProto.ColumnOrBuilder>(\n                  keys_,\n                  ((bitField0_ & 0x00000020) == 0x00000020),\n                  getParentForChildren(),\n                  isClean());\n          keys_ = null;\n        }\n        return keysBuilder_;\n      }\n\n      private java.util.List<com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column> columns_ =\n        java.util.Collections.emptyList();\n      private void ensureColumnsIsMutable() {\n        if (!((bitField0_ & 0x00000040) == 0x00000040)) {\n          columns_ = new java.util.ArrayList<com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column>(columns_);\n          bitField0_ |= 0x00000040;\n         }\n      }\n\n      private com.google.protobuf.RepeatedFieldBuilder<\n          com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column, com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column.Builder, com.alibaba.otter.node.etl.model.protobuf.BatchProto.ColumnOrBuilder> columnsBuilder_;\n\n      /**\n       * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.Column columns = 7;</code>\n       *\n       * <pre>\n       **变更数据的每列变更信息,不包含主键*\n       * </pre>\n       */\n      public java.util.List<com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column> getColumnsList() {\n        if (columnsBuilder_ == null) {\n          return java.util.Collections.unmodifiableList(columns_);\n        } else {\n          return columnsBuilder_.getMessageList();\n        }\n      }\n      /**\n       * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.Column columns = 7;</code>\n       *\n       * <pre>\n       **变更数据的每列变更信息,不包含主键*\n       * </pre>\n       */\n      public int getColumnsCount() {\n        if (columnsBuilder_ == null) {\n          return columns_.size();\n        } else {\n          return columnsBuilder_.getCount();\n        }\n      }\n      /**\n       * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.Column columns = 7;</code>\n       *\n       * <pre>\n       **变更数据的每列变更信息,不包含主键*\n       * </pre>\n       */\n      public com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column getColumns(int index) {\n        if (columnsBuilder_ == null) {\n          return columns_.get(index);\n        } else {\n          return columnsBuilder_.getMessage(index);\n        }\n      }\n      /**\n       * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.Column columns = 7;</code>\n       *\n       * <pre>\n       **变更数据的每列变更信息,不包含主键*\n       * </pre>\n       */\n      public Builder setColumns(\n          int index, com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column value) {\n        if (columnsBuilder_ == null) {\n          if (value == null) {\n            throw new NullPointerException();\n          }\n          ensureColumnsIsMutable();\n          columns_.set(index, value);\n          onChanged();\n        } else {\n          columnsBuilder_.setMessage(index, value);\n        }\n        return this;\n      }\n      /**\n       * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.Column columns = 7;</code>\n       *\n       * <pre>\n       **变更数据的每列变更信息,不包含主键*\n       * </pre>\n       */\n      public Builder setColumns(\n          int index, com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column.Builder builderForValue) {\n        if (columnsBuilder_ == null) {\n          ensureColumnsIsMutable();\n          columns_.set(index, builderForValue.build());\n          onChanged();\n        } else {\n          columnsBuilder_.setMessage(index, builderForValue.build());\n        }\n        return this;\n      }\n      /**\n       * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.Column columns = 7;</code>\n       *\n       * <pre>\n       **变更数据的每列变更信息,不包含主键*\n       * </pre>\n       */\n      public Builder addColumns(com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column value) {\n        if (columnsBuilder_ == null) {\n          if (value == null) {\n            throw new NullPointerException();\n          }\n          ensureColumnsIsMutable();\n          columns_.add(value);\n          onChanged();\n        } else {\n          columnsBuilder_.addMessage(value);\n        }\n        return this;\n      }\n      /**\n       * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.Column columns = 7;</code>\n       *\n       * <pre>\n       **变更数据的每列变更信息,不包含主键*\n       * </pre>\n       */\n      public Builder addColumns(\n          int index, com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column value) {\n        if (columnsBuilder_ == null) {\n          if (value == null) {\n            throw new NullPointerException();\n          }\n          ensureColumnsIsMutable();\n          columns_.add(index, value);\n          onChanged();\n        } else {\n          columnsBuilder_.addMessage(index, value);\n        }\n        return this;\n      }\n      /**\n       * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.Column columns = 7;</code>\n       *\n       * <pre>\n       **变更数据的每列变更信息,不包含主键*\n       * </pre>\n       */\n      public Builder addColumns(\n          com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column.Builder builderForValue) {\n        if (columnsBuilder_ == null) {\n          ensureColumnsIsMutable();\n          columns_.add(builderForValue.build());\n          onChanged();\n        } else {\n          columnsBuilder_.addMessage(builderForValue.build());\n        }\n        return this;\n      }\n      /**\n       * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.Column columns = 7;</code>\n       *\n       * <pre>\n       **变更数据的每列变更信息,不包含主键*\n       * </pre>\n       */\n      public Builder addColumns(\n          int index, com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column.Builder builderForValue) {\n        if (columnsBuilder_ == null) {\n          ensureColumnsIsMutable();\n          columns_.add(index, builderForValue.build());\n          onChanged();\n        } else {\n          columnsBuilder_.addMessage(index, builderForValue.build());\n        }\n        return this;\n      }\n      /**\n       * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.Column columns = 7;</code>\n       *\n       * <pre>\n       **变更数据的每列变更信息,不包含主键*\n       * </pre>\n       */\n      public Builder addAllColumns(\n          java.lang.Iterable<? extends com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column> values) {\n        if (columnsBuilder_ == null) {\n          ensureColumnsIsMutable();\n          com.google.protobuf.AbstractMessageLite.Builder.addAll(\n              values, columns_);\n          onChanged();\n        } else {\n          columnsBuilder_.addAllMessages(values);\n        }\n        return this;\n      }\n      /**\n       * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.Column columns = 7;</code>\n       *\n       * <pre>\n       **变更数据的每列变更信息,不包含主键*\n       * </pre>\n       */\n      public Builder clearColumns() {\n        if (columnsBuilder_ == null) {\n          columns_ = java.util.Collections.emptyList();\n          bitField0_ = (bitField0_ & ~0x00000040);\n          onChanged();\n        } else {\n          columnsBuilder_.clear();\n        }\n        return this;\n      }\n      /**\n       * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.Column columns = 7;</code>\n       *\n       * <pre>\n       **变更数据的每列变更信息,不包含主键*\n       * </pre>\n       */\n      public Builder removeColumns(int index) {\n        if (columnsBuilder_ == null) {\n          ensureColumnsIsMutable();\n          columns_.remove(index);\n          onChanged();\n        } else {\n          columnsBuilder_.remove(index);\n        }\n        return this;\n      }\n      /**\n       * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.Column columns = 7;</code>\n       *\n       * <pre>\n       **变更数据的每列变更信息,不包含主键*\n       * </pre>\n       */\n      public com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column.Builder getColumnsBuilder(\n          int index) {\n        return getColumnsFieldBuilder().getBuilder(index);\n      }\n      /**\n       * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.Column columns = 7;</code>\n       *\n       * <pre>\n       **变更数据的每列变更信息,不包含主键*\n       * </pre>\n       */\n      public com.alibaba.otter.node.etl.model.protobuf.BatchProto.ColumnOrBuilder getColumnsOrBuilder(\n          int index) {\n        if (columnsBuilder_ == null) {\n          return columns_.get(index);  } else {\n          return columnsBuilder_.getMessageOrBuilder(index);\n        }\n      }\n      /**\n       * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.Column columns = 7;</code>\n       *\n       * <pre>\n       **变更数据的每列变更信息,不包含主键*\n       * </pre>\n       */\n      public java.util.List<? extends com.alibaba.otter.node.etl.model.protobuf.BatchProto.ColumnOrBuilder> \n           getColumnsOrBuilderList() {\n        if (columnsBuilder_ != null) {\n          return columnsBuilder_.getMessageOrBuilderList();\n        } else {\n          return java.util.Collections.unmodifiableList(columns_);\n        }\n      }\n      /**\n       * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.Column columns = 7;</code>\n       *\n       * <pre>\n       **变更数据的每列变更信息,不包含主键*\n       * </pre>\n       */\n      public com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column.Builder addColumnsBuilder() {\n        return getColumnsFieldBuilder().addBuilder(\n            com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column.getDefaultInstance());\n      }\n      /**\n       * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.Column columns = 7;</code>\n       *\n       * <pre>\n       **变更数据的每列变更信息,不包含主键*\n       * </pre>\n       */\n      public com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column.Builder addColumnsBuilder(\n          int index) {\n        return getColumnsFieldBuilder().addBuilder(\n            index, com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column.getDefaultInstance());\n      }\n      /**\n       * <code>repeated .com.alibaba.otter.node.etl.model.protobuf.Column columns = 7;</code>\n       *\n       * <pre>\n       **变更数据的每列变更信息,不包含主键*\n       * </pre>\n       */\n      public java.util.List<com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column.Builder> \n           getColumnsBuilderList() {\n        return getColumnsFieldBuilder().getBuilderList();\n      }\n      private com.google.protobuf.RepeatedFieldBuilder<\n          com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column, com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column.Builder, com.alibaba.otter.node.etl.model.protobuf.BatchProto.ColumnOrBuilder> \n          getColumnsFieldBuilder() {\n        if (columnsBuilder_ == null) {\n          columnsBuilder_ = new com.google.protobuf.RepeatedFieldBuilder<\n              com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column, com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column.Builder, com.alibaba.otter.node.etl.model.protobuf.BatchProto.ColumnOrBuilder>(\n                  columns_,\n                  ((bitField0_ & 0x00000040) == 0x00000040),\n                  getParentForChildren(),\n                  isClean());\n          columns_ = null;\n        }\n        return columnsBuilder_;\n      }\n\n      private long executeTime_ ;\n      /**\n       * <code>optional int64 executeTime = 8;</code>\n       *\n       * <pre>\n       **变更数据的业务时间*\n       * </pre>\n       */\n      public boolean hasExecuteTime() {\n        return ((bitField0_ & 0x00000080) == 0x00000080);\n      }\n      /**\n       * <code>optional int64 executeTime = 8;</code>\n       *\n       * <pre>\n       **变更数据的业务时间*\n       * </pre>\n       */\n      public long getExecuteTime() {\n        return executeTime_;\n      }\n      /**\n       * <code>optional int64 executeTime = 8;</code>\n       *\n       * <pre>\n       **变更数据的业务时间*\n       * </pre>\n       */\n      public Builder setExecuteTime(long value) {\n        bitField0_ |= 0x00000080;\n        executeTime_ = value;\n        onChanged();\n        return this;\n      }\n      /**\n       * <code>optional int64 executeTime = 8;</code>\n       *\n       * <pre>\n       **变更数据的业务时间*\n       * </pre>\n       */\n      public Builder clearExecuteTime() {\n        bitField0_ = (bitField0_ & ~0x00000080);\n        executeTime_ = 0L;\n        onChanged();\n        return this;\n      }\n\n      private long pairId_ ;\n      /**\n       * <code>optional int64 pairId = 9;</code>\n       *\n       * <pre>\n       **映射规则id*\n       * </pre>\n       */\n      public boolean hasPairId() {\n        return ((bitField0_ & 0x00000100) == 0x00000100);\n      }\n      /**\n       * <code>optional int64 pairId = 9;</code>\n       *\n       * <pre>\n       **映射规则id*\n       * </pre>\n       */\n      public long getPairId() {\n        return pairId_;\n      }\n      /**\n       * <code>optional int64 pairId = 9;</code>\n       *\n       * <pre>\n       **映射规则id*\n       * </pre>\n       */\n      public Builder setPairId(long value) {\n        bitField0_ |= 0x00000100;\n        pairId_ = value;\n        onChanged();\n        return this;\n      }\n      /**\n       * <code>optional int64 pairId = 9;</code>\n       *\n       * <pre>\n       **映射规则id*\n       * </pre>\n       */\n      public Builder clearPairId() {\n        bitField0_ = (bitField0_ & ~0x00000100);\n        pairId_ = 0L;\n        onChanged();\n        return this;\n      }\n\n      private java.lang.Object syncMode_ = \"\";\n      /**\n       * <code>optional string syncMode = 10;</code>\n       *\n       * <pre>\n       **同步模式(R/F)*\n       * </pre>\n       */\n      public boolean hasSyncMode() {\n        return ((bitField0_ & 0x00000200) == 0x00000200);\n      }\n      /**\n       * <code>optional string syncMode = 10;</code>\n       *\n       * <pre>\n       **同步模式(R/F)*\n       * </pre>\n       */\n      public java.lang.String getSyncMode() {\n        java.lang.Object ref = syncMode_;\n        if (!(ref instanceof java.lang.String)) {\n          com.google.protobuf.ByteString bs =\n              (com.google.protobuf.ByteString) ref;\n          java.lang.String s = bs.toStringUtf8();\n          if (bs.isValidUtf8()) {\n            syncMode_ = s;\n          }\n          return s;\n        } else {\n          return (java.lang.String) ref;\n        }\n      }\n      /**\n       * <code>optional string syncMode = 10;</code>\n       *\n       * <pre>\n       **同步模式(R/F)*\n       * </pre>\n       */\n      public com.google.protobuf.ByteString\n          getSyncModeBytes() {\n        java.lang.Object ref = syncMode_;\n        if (ref instanceof String) {\n          com.google.protobuf.ByteString b = \n              com.google.protobuf.ByteString.copyFromUtf8(\n                  (java.lang.String) ref);\n          syncMode_ = b;\n          return b;\n        } else {\n          return (com.google.protobuf.ByteString) ref;\n        }\n      }\n      /**\n       * <code>optional string syncMode = 10;</code>\n       *\n       * <pre>\n       **同步模式(R/F)*\n       * </pre>\n       */\n      public Builder setSyncMode(\n          java.lang.String value) {\n        if (value == null) {\n    throw new NullPointerException();\n  }\n  bitField0_ |= 0x00000200;\n        syncMode_ = value;\n        onChanged();\n        return this;\n      }\n      /**\n       * <code>optional string syncMode = 10;</code>\n       *\n       * <pre>\n       **同步模式(R/F)*\n       * </pre>\n       */\n      public Builder clearSyncMode() {\n        bitField0_ = (bitField0_ & ~0x00000200);\n        syncMode_ = getDefaultInstance().getSyncMode();\n        onChanged();\n        return this;\n      }\n      /**\n       * <code>optional string syncMode = 10;</code>\n       *\n       * <pre>\n       **同步模式(R/F)*\n       * </pre>\n       */\n      public Builder setSyncModeBytes(\n          com.google.protobuf.ByteString value) {\n        if (value == null) {\n    throw new NullPointerException();\n  }\n  bitField0_ |= 0x00000200;\n        syncMode_ = value;\n        onChanged();\n        return this;\n      }\n\n      private java.lang.Object syncConsistency_ = \"\";\n      /**\n       * <code>optional string syncConsistency = 11;</code>\n       *\n       * <pre>\n       **同步一致性(B/S/M) *\n       * </pre>\n       */\n      public boolean hasSyncConsistency() {\n        return ((bitField0_ & 0x00000400) == 0x00000400);\n      }\n      /**\n       * <code>optional string syncConsistency = 11;</code>\n       *\n       * <pre>\n       **同步一致性(B/S/M) *\n       * </pre>\n       */\n      public java.lang.String getSyncConsistency() {\n        java.lang.Object ref = syncConsistency_;\n        if (!(ref instanceof java.lang.String)) {\n          com.google.protobuf.ByteString bs =\n              (com.google.protobuf.ByteString) ref;\n          java.lang.String s = bs.toStringUtf8();\n          if (bs.isValidUtf8()) {\n            syncConsistency_ = s;\n          }\n          return s;\n        } else {\n          return (java.lang.String) ref;\n        }\n      }\n      /**\n       * <code>optional string syncConsistency = 11;</code>\n       *\n       * <pre>\n       **同步一致性(B/S/M) *\n       * </pre>\n       */\n      public com.google.protobuf.ByteString\n          getSyncConsistencyBytes() {\n        java.lang.Object ref = syncConsistency_;\n        if (ref instanceof String) {\n          com.google.protobuf.ByteString b = \n              com.google.protobuf.ByteString.copyFromUtf8(\n                  (java.lang.String) ref);\n          syncConsistency_ = b;\n          return b;\n        } else {\n          return (com.google.protobuf.ByteString) ref;\n        }\n      }\n      /**\n       * <code>optional string syncConsistency = 11;</code>\n       *\n       * <pre>\n       **同步一致性(B/S/M) *\n       * </pre>\n       */\n      public Builder setSyncConsistency(\n          java.lang.String value) {\n        if (value == null) {\n    throw new NullPointerException();\n  }\n  bitField0_ |= 0x00000400;\n        syncConsistency_ = value;\n        onChanged();\n        return this;\n      }\n      /**\n       * <code>optional string syncConsistency = 11;</code>\n       *\n       * <pre>\n       **同步一致性(B/S/M) *\n       * </pre>\n       */\n      public Builder clearSyncConsistency() {\n        bitField0_ = (bitField0_ & ~0x00000400);\n        syncConsistency_ = getDefaultInstance().getSyncConsistency();\n        onChanged();\n        return this;\n      }\n      /**\n       * <code>optional string syncConsistency = 11;</code>\n       *\n       * <pre>\n       **同步一致性(B/S/M) *\n       * </pre>\n       */\n      public Builder setSyncConsistencyBytes(\n          com.google.protobuf.ByteString value) {\n        if (value == null) {\n    throw new NullPointerException();\n  }\n  bitField0_ |= 0x00000400;\n        syncConsistency_ = value;\n        onChanged();\n        return this;\n      }\n\n      private long size_ ;\n      /**\n       * <code>optional int64 size = 12;</code>\n       *\n       * <pre>\n       ** eventsize *\n       * </pre>\n       */\n      public boolean hasSize() {\n        return ((bitField0_ & 0x00000800) == 0x00000800);\n      }\n      /**\n       * <code>optional int64 size = 12;</code>\n       *\n       * <pre>\n       ** eventsize *\n       * </pre>\n       */\n      public long getSize() {\n        return size_;\n      }\n      /**\n       * <code>optional int64 size = 12;</code>\n       *\n       * <pre>\n       ** eventsize *\n       * </pre>\n       */\n      public Builder setSize(long value) {\n        bitField0_ |= 0x00000800;\n        size_ = value;\n        onChanged();\n        return this;\n      }\n      /**\n       * <code>optional int64 size = 12;</code>\n       *\n       * <pre>\n       ** eventsize *\n       * </pre>\n       */\n      public Builder clearSize() {\n        bitField0_ = (bitField0_ & ~0x00000800);\n        size_ = 0L;\n        onChanged();\n        return this;\n      }\n\n      private boolean remedy_ ;\n      /**\n       * <code>optional bool remedy = 13;</code>\n       *\n       * <pre>\n       ** isRemedy *\n       * </pre>\n       */\n      public boolean hasRemedy() {\n        return ((bitField0_ & 0x00001000) == 0x00001000);\n      }\n      /**\n       * <code>optional bool remedy = 13;</code>\n       *\n       * <pre>\n       ** isRemedy *\n       * </pre>\n       */\n      public boolean getRemedy() {\n        return remedy_;\n      }\n      /**\n       * <code>optional bool remedy = 13;</code>\n       *\n       * <pre>\n       ** isRemedy *\n       * </pre>\n       */\n      public Builder setRemedy(boolean value) {\n        bitField0_ |= 0x00001000;\n        remedy_ = value;\n        onChanged();\n        return this;\n      }\n      /**\n       * <code>optional bool remedy = 13;</code>\n       *\n       * <pre>\n       ** isRemedy *\n       * </pre>\n       */\n      public Builder clearRemedy() {\n        bitField0_ = (bitField0_ & ~0x00001000);\n        remedy_ = false;\n        onChanged();\n        return this;\n      }\n\n      private java.lang.Object sql_ = \"\";\n      /**\n       * <code>optional string sql = 14;</code>\n       *\n       * <pre>\n       ** dml/ddl sql *\n       * </pre>\n       */\n      public boolean hasSql() {\n        return ((bitField0_ & 0x00002000) == 0x00002000);\n      }\n      /**\n       * <code>optional string sql = 14;</code>\n       *\n       * <pre>\n       ** dml/ddl sql *\n       * </pre>\n       */\n      public java.lang.String getSql() {\n        java.lang.Object ref = sql_;\n        if (!(ref instanceof java.lang.String)) {\n          com.google.protobuf.ByteString bs =\n              (com.google.protobuf.ByteString) ref;\n          java.lang.String s = bs.toStringUtf8();\n          if (bs.isValidUtf8()) {\n            sql_ = s;\n          }\n          return s;\n        } else {\n          return (java.lang.String) ref;\n        }\n      }\n      /**\n       * <code>optional string sql = 14;</code>\n       *\n       * <pre>\n       ** dml/ddl sql *\n       * </pre>\n       */\n      public com.google.protobuf.ByteString\n          getSqlBytes() {\n        java.lang.Object ref = sql_;\n        if (ref instanceof String) {\n          com.google.protobuf.ByteString b = \n              com.google.protobuf.ByteString.copyFromUtf8(\n                  (java.lang.String) ref);\n          sql_ = b;\n          return b;\n        } else {\n          return (com.google.protobuf.ByteString) ref;\n        }\n      }\n      /**\n       * <code>optional string sql = 14;</code>\n       *\n       * <pre>\n       ** dml/ddl sql *\n       * </pre>\n       */\n      public Builder setSql(\n          java.lang.String value) {\n        if (value == null) {\n    throw new NullPointerException();\n  }\n  bitField0_ |= 0x00002000;\n        sql_ = value;\n        onChanged();\n        return this;\n      }\n      /**\n       * <code>optional string sql = 14;</code>\n       *\n       * <pre>\n       ** dml/ddl sql *\n       * </pre>\n       */\n      public Builder clearSql() {\n        bitField0_ = (bitField0_ & ~0x00002000);\n        sql_ = getDefaultInstance().getSql();\n        onChanged();\n        return this;\n      }\n      /**\n       * <code>optional string sql = 14;</code>\n       *\n       * <pre>\n       ** dml/ddl sql *\n       * </pre>\n       */\n      public Builder setSqlBytes(\n          com.google.protobuf.ByteString value) {\n        if (value == null) {\n    throw new NullPointerException();\n  }\n  bitField0_ |= 0x00002000;\n        sql_ = value;\n        onChanged();\n        return this;\n      }\n\n      private java.lang.Object ddlSchemaName_ = \"\";\n      /**\n       * <code>optional string ddlSchemaName = 15;</code>\n       *\n       * <pre>\n       ** current ddl schemaName *\n       * </pre>\n       */\n      public boolean hasDdlSchemaName() {\n        return ((bitField0_ & 0x00004000) == 0x00004000);\n      }\n      /**\n       * <code>optional string ddlSchemaName = 15;</code>\n       *\n       * <pre>\n       ** current ddl schemaName *\n       * </pre>\n       */\n      public java.lang.String getDdlSchemaName() {\n        java.lang.Object ref = ddlSchemaName_;\n        if (!(ref instanceof java.lang.String)) {\n          com.google.protobuf.ByteString bs =\n              (com.google.protobuf.ByteString) ref;\n          java.lang.String s = bs.toStringUtf8();\n          if (bs.isValidUtf8()) {\n            ddlSchemaName_ = s;\n          }\n          return s;\n        } else {\n          return (java.lang.String) ref;\n        }\n      }\n      /**\n       * <code>optional string ddlSchemaName = 15;</code>\n       *\n       * <pre>\n       ** current ddl schemaName *\n       * </pre>\n       */\n      public com.google.protobuf.ByteString\n          getDdlSchemaNameBytes() {\n        java.lang.Object ref = ddlSchemaName_;\n        if (ref instanceof String) {\n          com.google.protobuf.ByteString b = \n              com.google.protobuf.ByteString.copyFromUtf8(\n                  (java.lang.String) ref);\n          ddlSchemaName_ = b;\n          return b;\n        } else {\n          return (com.google.protobuf.ByteString) ref;\n        }\n      }\n      /**\n       * <code>optional string ddlSchemaName = 15;</code>\n       *\n       * <pre>\n       ** current ddl schemaName *\n       * </pre>\n       */\n      public Builder setDdlSchemaName(\n          java.lang.String value) {\n        if (value == null) {\n    throw new NullPointerException();\n  }\n  bitField0_ |= 0x00004000;\n        ddlSchemaName_ = value;\n        onChanged();\n        return this;\n      }\n      /**\n       * <code>optional string ddlSchemaName = 15;</code>\n       *\n       * <pre>\n       ** current ddl schemaName *\n       * </pre>\n       */\n      public Builder clearDdlSchemaName() {\n        bitField0_ = (bitField0_ & ~0x00004000);\n        ddlSchemaName_ = getDefaultInstance().getDdlSchemaName();\n        onChanged();\n        return this;\n      }\n      /**\n       * <code>optional string ddlSchemaName = 15;</code>\n       *\n       * <pre>\n       ** current ddl schemaName *\n       * </pre>\n       */\n      public Builder setDdlSchemaNameBytes(\n          com.google.protobuf.ByteString value) {\n        if (value == null) {\n    throw new NullPointerException();\n  }\n  bitField0_ |= 0x00004000;\n        ddlSchemaName_ = value;\n        onChanged();\n        return this;\n      }\n\n      private java.lang.Object hint_ = \"\";\n      /**\n       * <code>optional string hint = 16;</code>\n       *\n       * <pre>\n       ** dml/ddl sql *\n       * </pre>\n       */\n      public boolean hasHint() {\n        return ((bitField0_ & 0x00008000) == 0x00008000);\n      }\n      /**\n       * <code>optional string hint = 16;</code>\n       *\n       * <pre>\n       ** dml/ddl sql *\n       * </pre>\n       */\n      public java.lang.String getHint() {\n        java.lang.Object ref = hint_;\n        if (!(ref instanceof java.lang.String)) {\n          com.google.protobuf.ByteString bs =\n              (com.google.protobuf.ByteString) ref;\n          java.lang.String s = bs.toStringUtf8();\n          if (bs.isValidUtf8()) {\n            hint_ = s;\n          }\n          return s;\n        } else {\n          return (java.lang.String) ref;\n        }\n      }\n      /**\n       * <code>optional string hint = 16;</code>\n       *\n       * <pre>\n       ** dml/ddl sql *\n       * </pre>\n       */\n      public com.google.protobuf.ByteString\n          getHintBytes() {\n        java.lang.Object ref = hint_;\n        if (ref instanceof String) {\n          com.google.protobuf.ByteString b = \n              com.google.protobuf.ByteString.copyFromUtf8(\n                  (java.lang.String) ref);\n          hint_ = b;\n          return b;\n        } else {\n          return (com.google.protobuf.ByteString) ref;\n        }\n      }\n      /**\n       * <code>optional string hint = 16;</code>\n       *\n       * <pre>\n       ** dml/ddl sql *\n       * </pre>\n       */\n      public Builder setHint(\n          java.lang.String value) {\n        if (value == null) {\n    throw new NullPointerException();\n  }\n  bitField0_ |= 0x00008000;\n        hint_ = value;\n        onChanged();\n        return this;\n      }\n      /**\n       * <code>optional string hint = 16;</code>\n       *\n       * <pre>\n       ** dml/ddl sql *\n       * </pre>\n       */\n      public Builder clearHint() {\n        bitField0_ = (bitField0_ & ~0x00008000);\n        hint_ = getDefaultInstance().getHint();\n        onChanged();\n        return this;\n      }\n      /**\n       * <code>optional string hint = 16;</code>\n       *\n       * <pre>\n       ** dml/ddl sql *\n       * </pre>\n       */\n      public Builder setHintBytes(\n          com.google.protobuf.ByteString value) {\n        if (value == null) {\n    throw new NullPointerException();\n  }\n  bitField0_ |= 0x00008000;\n        hint_ = value;\n        onChanged();\n        return this;\n      }\n\n      private boolean withoutSchema_ ;\n      /**\n       * <code>optional bool withoutSchema = 17;</code>\n       *\n       * <pre>\n       ** without schema *\n       * </pre>\n       */\n      public boolean hasWithoutSchema() {\n        return ((bitField0_ & 0x00010000) == 0x00010000);\n      }\n      /**\n       * <code>optional bool withoutSchema = 17;</code>\n       *\n       * <pre>\n       ** without schema *\n       * </pre>\n       */\n      public boolean getWithoutSchema() {\n        return withoutSchema_;\n      }\n      /**\n       * <code>optional bool withoutSchema = 17;</code>\n       *\n       * <pre>\n       ** without schema *\n       * </pre>\n       */\n      public Builder setWithoutSchema(boolean value) {\n        bitField0_ |= 0x00010000;\n        withoutSchema_ = value;\n        onChanged();\n        return this;\n      }\n      /**\n       * <code>optional bool withoutSchema = 17;</code>\n       *\n       * <pre>\n       ** without schema *\n       * </pre>\n       */\n      public Builder clearWithoutSchema() {\n        bitField0_ = (bitField0_ & ~0x00010000);\n        withoutSchema_ = false;\n        onChanged();\n        return this;\n      }\n\n      // @@protoc_insertion_point(builder_scope:com.alibaba.otter.node.etl.model.protobuf.RowData)\n    }\n\n    static {\n      defaultInstance = new RowData(true);\n      defaultInstance.initFields();\n    }\n\n    // @@protoc_insertion_point(class_scope:com.alibaba.otter.node.etl.model.protobuf.RowData)\n  }\n\n  public interface ColumnOrBuilder extends\n      // @@protoc_insertion_point(interface_extends:com.alibaba.otter.node.etl.model.protobuf.Column)\n      com.google.protobuf.MessageOrBuilder {\n\n    /**\n     * <code>optional int32 index = 1;</code>\n     *\n     * <pre>\n     **列下标*\n     * </pre>\n     */\n    boolean hasIndex();\n    /**\n     * <code>optional int32 index = 1;</code>\n     *\n     * <pre>\n     **列下标*\n     * </pre>\n     */\n    int getIndex();\n\n    /**\n     * <code>optional string name = 2;</code>\n     *\n     * <pre>\n     **列名*\n     * </pre>\n     */\n    boolean hasName();\n    /**\n     * <code>optional string name = 2;</code>\n     *\n     * <pre>\n     **列名*\n     * </pre>\n     */\n    java.lang.String getName();\n    /**\n     * <code>optional string name = 2;</code>\n     *\n     * <pre>\n     **列名*\n     * </pre>\n     */\n    com.google.protobuf.ByteString\n        getNameBytes();\n\n    /**\n     * <code>optional string value = 3;</code>\n     *\n     * <pre>\n     **列值,timestamp,Datetime是一个long型的数字*\n     * </pre>\n     */\n    boolean hasValue();\n    /**\n     * <code>optional string value = 3;</code>\n     *\n     * <pre>\n     **列值,timestamp,Datetime是一个long型的数字*\n     * </pre>\n     */\n    java.lang.String getValue();\n    /**\n     * <code>optional string value = 3;</code>\n     *\n     * <pre>\n     **列值,timestamp,Datetime是一个long型的数字*\n     * </pre>\n     */\n    com.google.protobuf.ByteString\n        getValueBytes();\n\n    /**\n     * <code>optional bool isPrimaryKey = 4;</code>\n     *\n     * <pre>\n     **当前列是否是主键*\n     * </pre>\n     */\n    boolean hasIsPrimaryKey();\n    /**\n     * <code>optional bool isPrimaryKey = 4;</code>\n     *\n     * <pre>\n     **当前列是否是主键*\n     * </pre>\n     */\n    boolean getIsPrimaryKey();\n\n    /**\n     * <code>optional bool isNull = 5;</code>\n     *\n     * <pre>\n     **当前列是否可以为空*\n     * </pre>\n     */\n    boolean hasIsNull();\n    /**\n     * <code>optional bool isNull = 5;</code>\n     *\n     * <pre>\n     **当前列是否可以为空*\n     * </pre>\n     */\n    boolean getIsNull();\n\n    /**\n     * <code>optional int32 type = 6;</code>\n     *\n     * <pre>\n     **当前列的数据类型*\n     * </pre>\n     */\n    boolean hasType();\n    /**\n     * <code>optional int32 type = 6;</code>\n     *\n     * <pre>\n     **当前列的数据类型*\n     * </pre>\n     */\n    int getType();\n\n    /**\n     * <code>optional bool isUpdate = 7;</code>\n     *\n     * <pre>\n     **当前列是否发生真实变更*\n     * </pre>\n     */\n    boolean hasIsUpdate();\n    /**\n     * <code>optional bool isUpdate = 7;</code>\n     *\n     * <pre>\n     **当前列是否发生真实变更*\n     * </pre>\n     */\n    boolean getIsUpdate();\n  }\n  /**\n   * Protobuf type {@code com.alibaba.otter.node.etl.model.protobuf.Column}\n   */\n  public static final class Column extends\n      com.google.protobuf.GeneratedMessage implements\n      // @@protoc_insertion_point(message_implements:com.alibaba.otter.node.etl.model.protobuf.Column)\n      ColumnOrBuilder {\n    // Use Column.newBuilder() to construct.\n    private Column(com.google.protobuf.GeneratedMessage.Builder<?> builder) {\n      super(builder);\n      this.unknownFields = builder.getUnknownFields();\n    }\n    private Column(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); }\n\n    private static final Column defaultInstance;\n    public static Column getDefaultInstance() {\n      return defaultInstance;\n    }\n\n    public Column getDefaultInstanceForType() {\n      return defaultInstance;\n    }\n\n    private final com.google.protobuf.UnknownFieldSet unknownFields;\n    @java.lang.Override\n    public final com.google.protobuf.UnknownFieldSet\n        getUnknownFields() {\n      return this.unknownFields;\n    }\n    private Column(\n        com.google.protobuf.CodedInputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      initFields();\n      int mutable_bitField0_ = 0;\n      com.google.protobuf.UnknownFieldSet.Builder unknownFields =\n          com.google.protobuf.UnknownFieldSet.newBuilder();\n      try {\n        boolean done = false;\n        while (!done) {\n          int tag = input.readTag();\n          switch (tag) {\n            case 0:\n              done = true;\n              break;\n            default: {\n              if (!parseUnknownField(input, unknownFields,\n                                     extensionRegistry, tag)) {\n                done = true;\n              }\n              break;\n            }\n            case 8: {\n              bitField0_ |= 0x00000001;\n              index_ = input.readInt32();\n              break;\n            }\n            case 18: {\n              com.google.protobuf.ByteString bs = input.readBytes();\n              bitField0_ |= 0x00000002;\n              name_ = bs;\n              break;\n            }\n            case 26: {\n              com.google.protobuf.ByteString bs = input.readBytes();\n              bitField0_ |= 0x00000004;\n              value_ = bs;\n              break;\n            }\n            case 32: {\n              bitField0_ |= 0x00000008;\n              isPrimaryKey_ = input.readBool();\n              break;\n            }\n            case 40: {\n              bitField0_ |= 0x00000010;\n              isNull_ = input.readBool();\n              break;\n            }\n            case 48: {\n              bitField0_ |= 0x00000020;\n              type_ = input.readInt32();\n              break;\n            }\n            case 56: {\n              bitField0_ |= 0x00000040;\n              isUpdate_ = input.readBool();\n              break;\n            }\n          }\n        }\n      } catch (com.google.protobuf.InvalidProtocolBufferException e) {\n        throw e.setUnfinishedMessage(this);\n      } catch (java.io.IOException e) {\n        throw new com.google.protobuf.InvalidProtocolBufferException(\n            e.getMessage()).setUnfinishedMessage(this);\n      } finally {\n        this.unknownFields = unknownFields.build();\n        makeExtensionsImmutable();\n      }\n    }\n    public static final com.google.protobuf.Descriptors.Descriptor\n        getDescriptor() {\n      return com.alibaba.otter.node.etl.model.protobuf.BatchProto.internal_static_com_alibaba_otter_node_etl_model_protobuf_Column_descriptor;\n    }\n\n    protected com.google.protobuf.GeneratedMessage.FieldAccessorTable\n        internalGetFieldAccessorTable() {\n      return com.alibaba.otter.node.etl.model.protobuf.BatchProto.internal_static_com_alibaba_otter_node_etl_model_protobuf_Column_fieldAccessorTable\n          .ensureFieldAccessorsInitialized(\n              com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column.class, com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column.Builder.class);\n    }\n\n    public static com.google.protobuf.Parser<Column> PARSER =\n        new com.google.protobuf.AbstractParser<Column>() {\n      public Column parsePartialFrom(\n          com.google.protobuf.CodedInputStream input,\n          com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n          throws com.google.protobuf.InvalidProtocolBufferException {\n        return new Column(input, extensionRegistry);\n      }\n    };\n\n    @java.lang.Override\n    public com.google.protobuf.Parser<Column> getParserForType() {\n      return PARSER;\n    }\n\n    private int bitField0_;\n    public static final int INDEX_FIELD_NUMBER = 1;\n    private int index_;\n    /**\n     * <code>optional int32 index = 1;</code>\n     *\n     * <pre>\n     **列下标*\n     * </pre>\n     */\n    public boolean hasIndex() {\n      return ((bitField0_ & 0x00000001) == 0x00000001);\n    }\n    /**\n     * <code>optional int32 index = 1;</code>\n     *\n     * <pre>\n     **列下标*\n     * </pre>\n     */\n    public int getIndex() {\n      return index_;\n    }\n\n    public static final int NAME_FIELD_NUMBER = 2;\n    private java.lang.Object name_;\n    /**\n     * <code>optional string name = 2;</code>\n     *\n     * <pre>\n     **列名*\n     * </pre>\n     */\n    public boolean hasName() {\n      return ((bitField0_ & 0x00000002) == 0x00000002);\n    }\n    /**\n     * <code>optional string name = 2;</code>\n     *\n     * <pre>\n     **列名*\n     * </pre>\n     */\n    public java.lang.String getName() {\n      java.lang.Object ref = name_;\n      if (ref instanceof java.lang.String) {\n        return (java.lang.String) ref;\n      } else {\n        com.google.protobuf.ByteString bs = \n            (com.google.protobuf.ByteString) ref;\n        java.lang.String s = bs.toStringUtf8();\n        if (bs.isValidUtf8()) {\n          name_ = s;\n        }\n        return s;\n      }\n    }\n    /**\n     * <code>optional string name = 2;</code>\n     *\n     * <pre>\n     **列名*\n     * </pre>\n     */\n    public com.google.protobuf.ByteString\n        getNameBytes() {\n      java.lang.Object ref = name_;\n      if (ref instanceof java.lang.String) {\n        com.google.protobuf.ByteString b = \n            com.google.protobuf.ByteString.copyFromUtf8(\n                (java.lang.String) ref);\n        name_ = b;\n        return b;\n      } else {\n        return (com.google.protobuf.ByteString) ref;\n      }\n    }\n\n    public static final int VALUE_FIELD_NUMBER = 3;\n    private java.lang.Object value_;\n    /**\n     * <code>optional string value = 3;</code>\n     *\n     * <pre>\n     **列值,timestamp,Datetime是一个long型的数字*\n     * </pre>\n     */\n    public boolean hasValue() {\n      return ((bitField0_ & 0x00000004) == 0x00000004);\n    }\n    /**\n     * <code>optional string value = 3;</code>\n     *\n     * <pre>\n     **列值,timestamp,Datetime是一个long型的数字*\n     * </pre>\n     */\n    public java.lang.String getValue() {\n      java.lang.Object ref = value_;\n      if (ref instanceof java.lang.String) {\n        return (java.lang.String) ref;\n      } else {\n        com.google.protobuf.ByteString bs = \n            (com.google.protobuf.ByteString) ref;\n        java.lang.String s = bs.toStringUtf8();\n        if (bs.isValidUtf8()) {\n          value_ = s;\n        }\n        return s;\n      }\n    }\n    /**\n     * <code>optional string value = 3;</code>\n     *\n     * <pre>\n     **列值,timestamp,Datetime是一个long型的数字*\n     * </pre>\n     */\n    public com.google.protobuf.ByteString\n        getValueBytes() {\n      java.lang.Object ref = value_;\n      if (ref instanceof java.lang.String) {\n        com.google.protobuf.ByteString b = \n            com.google.protobuf.ByteString.copyFromUtf8(\n                (java.lang.String) ref);\n        value_ = b;\n        return b;\n      } else {\n        return (com.google.protobuf.ByteString) ref;\n      }\n    }\n\n    public static final int ISPRIMARYKEY_FIELD_NUMBER = 4;\n    private boolean isPrimaryKey_;\n    /**\n     * <code>optional bool isPrimaryKey = 4;</code>\n     *\n     * <pre>\n     **当前列是否是主键*\n     * </pre>\n     */\n    public boolean hasIsPrimaryKey() {\n      return ((bitField0_ & 0x00000008) == 0x00000008);\n    }\n    /**\n     * <code>optional bool isPrimaryKey = 4;</code>\n     *\n     * <pre>\n     **当前列是否是主键*\n     * </pre>\n     */\n    public boolean getIsPrimaryKey() {\n      return isPrimaryKey_;\n    }\n\n    public static final int ISNULL_FIELD_NUMBER = 5;\n    private boolean isNull_;\n    /**\n     * <code>optional bool isNull = 5;</code>\n     *\n     * <pre>\n     **当前列是否可以为空*\n     * </pre>\n     */\n    public boolean hasIsNull() {\n      return ((bitField0_ & 0x00000010) == 0x00000010);\n    }\n    /**\n     * <code>optional bool isNull = 5;</code>\n     *\n     * <pre>\n     **当前列是否可以为空*\n     * </pre>\n     */\n    public boolean getIsNull() {\n      return isNull_;\n    }\n\n    public static final int TYPE_FIELD_NUMBER = 6;\n    private int type_;\n    /**\n     * <code>optional int32 type = 6;</code>\n     *\n     * <pre>\n     **当前列的数据类型*\n     * </pre>\n     */\n    public boolean hasType() {\n      return ((bitField0_ & 0x00000020) == 0x00000020);\n    }\n    /**\n     * <code>optional int32 type = 6;</code>\n     *\n     * <pre>\n     **当前列的数据类型*\n     * </pre>\n     */\n    public int getType() {\n      return type_;\n    }\n\n    public static final int ISUPDATE_FIELD_NUMBER = 7;\n    private boolean isUpdate_;\n    /**\n     * <code>optional bool isUpdate = 7;</code>\n     *\n     * <pre>\n     **当前列是否发生真实变更*\n     * </pre>\n     */\n    public boolean hasIsUpdate() {\n      return ((bitField0_ & 0x00000040) == 0x00000040);\n    }\n    /**\n     * <code>optional bool isUpdate = 7;</code>\n     *\n     * <pre>\n     **当前列是否发生真实变更*\n     * </pre>\n     */\n    public boolean getIsUpdate() {\n      return isUpdate_;\n    }\n\n    private void initFields() {\n      index_ = 0;\n      name_ = \"\";\n      value_ = \"\";\n      isPrimaryKey_ = false;\n      isNull_ = false;\n      type_ = 0;\n      isUpdate_ = false;\n    }\n    private byte memoizedIsInitialized = -1;\n    public final boolean isInitialized() {\n      byte isInitialized = memoizedIsInitialized;\n      if (isInitialized == 1) return true;\n      if (isInitialized == 0) return false;\n\n      memoizedIsInitialized = 1;\n      return true;\n    }\n\n    public void writeTo(com.google.protobuf.CodedOutputStream output)\n                        throws java.io.IOException {\n      getSerializedSize();\n      if (((bitField0_ & 0x00000001) == 0x00000001)) {\n        output.writeInt32(1, index_);\n      }\n      if (((bitField0_ & 0x00000002) == 0x00000002)) {\n        output.writeBytes(2, getNameBytes());\n      }\n      if (((bitField0_ & 0x00000004) == 0x00000004)) {\n        output.writeBytes(3, getValueBytes());\n      }\n      if (((bitField0_ & 0x00000008) == 0x00000008)) {\n        output.writeBool(4, isPrimaryKey_);\n      }\n      if (((bitField0_ & 0x00000010) == 0x00000010)) {\n        output.writeBool(5, isNull_);\n      }\n      if (((bitField0_ & 0x00000020) == 0x00000020)) {\n        output.writeInt32(6, type_);\n      }\n      if (((bitField0_ & 0x00000040) == 0x00000040)) {\n        output.writeBool(7, isUpdate_);\n      }\n      getUnknownFields().writeTo(output);\n    }\n\n    private int memoizedSerializedSize = -1;\n    public int getSerializedSize() {\n      int size = memoizedSerializedSize;\n      if (size != -1) return size;\n\n      size = 0;\n      if (((bitField0_ & 0x00000001) == 0x00000001)) {\n        size += com.google.protobuf.CodedOutputStream\n          .computeInt32Size(1, index_);\n      }\n      if (((bitField0_ & 0x00000002) == 0x00000002)) {\n        size += com.google.protobuf.CodedOutputStream\n          .computeBytesSize(2, getNameBytes());\n      }\n      if (((bitField0_ & 0x00000004) == 0x00000004)) {\n        size += com.google.protobuf.CodedOutputStream\n          .computeBytesSize(3, getValueBytes());\n      }\n      if (((bitField0_ & 0x00000008) == 0x00000008)) {\n        size += com.google.protobuf.CodedOutputStream\n          .computeBoolSize(4, isPrimaryKey_);\n      }\n      if (((bitField0_ & 0x00000010) == 0x00000010)) {\n        size += com.google.protobuf.CodedOutputStream\n          .computeBoolSize(5, isNull_);\n      }\n      if (((bitField0_ & 0x00000020) == 0x00000020)) {\n        size += com.google.protobuf.CodedOutputStream\n          .computeInt32Size(6, type_);\n      }\n      if (((bitField0_ & 0x00000040) == 0x00000040)) {\n        size += com.google.protobuf.CodedOutputStream\n          .computeBoolSize(7, isUpdate_);\n      }\n      size += getUnknownFields().getSerializedSize();\n      memoizedSerializedSize = size;\n      return size;\n    }\n\n    private static final long serialVersionUID = 0L;\n    @java.lang.Override\n    protected java.lang.Object writeReplace()\n        throws java.io.ObjectStreamException {\n      return super.writeReplace();\n    }\n\n    public static com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column parseFrom(\n        com.google.protobuf.ByteString data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return PARSER.parseFrom(data);\n    }\n    public static com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column parseFrom(\n        com.google.protobuf.ByteString data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return PARSER.parseFrom(data, extensionRegistry);\n    }\n    public static com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column parseFrom(byte[] data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return PARSER.parseFrom(data);\n    }\n    public static com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column parseFrom(\n        byte[] data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return PARSER.parseFrom(data, extensionRegistry);\n    }\n    public static com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column parseFrom(java.io.InputStream input)\n        throws java.io.IOException {\n      return PARSER.parseFrom(input);\n    }\n    public static com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column parseFrom(\n        java.io.InputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return PARSER.parseFrom(input, extensionRegistry);\n    }\n    public static com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column parseDelimitedFrom(java.io.InputStream input)\n        throws java.io.IOException {\n      return PARSER.parseDelimitedFrom(input);\n    }\n    public static com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column parseDelimitedFrom(\n        java.io.InputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return PARSER.parseDelimitedFrom(input, extensionRegistry);\n    }\n    public static com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column parseFrom(\n        com.google.protobuf.CodedInputStream input)\n        throws java.io.IOException {\n      return PARSER.parseFrom(input);\n    }\n    public static com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column parseFrom(\n        com.google.protobuf.CodedInputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return PARSER.parseFrom(input, extensionRegistry);\n    }\n\n    public static Builder newBuilder() { return Builder.create(); }\n    public Builder newBuilderForType() { return newBuilder(); }\n    public static Builder newBuilder(com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column prototype) {\n      return newBuilder().mergeFrom(prototype);\n    }\n    public Builder toBuilder() { return newBuilder(this); }\n\n    @java.lang.Override\n    protected Builder newBuilderForType(\n        com.google.protobuf.GeneratedMessage.BuilderParent parent) {\n      Builder builder = new Builder(parent);\n      return builder;\n    }\n    /**\n     * Protobuf type {@code com.alibaba.otter.node.etl.model.protobuf.Column}\n     */\n    public static final class Builder extends\n        com.google.protobuf.GeneratedMessage.Builder<Builder> implements\n        // @@protoc_insertion_point(builder_implements:com.alibaba.otter.node.etl.model.protobuf.Column)\n        com.alibaba.otter.node.etl.model.protobuf.BatchProto.ColumnOrBuilder {\n      public static final com.google.protobuf.Descriptors.Descriptor\n          getDescriptor() {\n        return com.alibaba.otter.node.etl.model.protobuf.BatchProto.internal_static_com_alibaba_otter_node_etl_model_protobuf_Column_descriptor;\n      }\n\n      protected com.google.protobuf.GeneratedMessage.FieldAccessorTable\n          internalGetFieldAccessorTable() {\n        return com.alibaba.otter.node.etl.model.protobuf.BatchProto.internal_static_com_alibaba_otter_node_etl_model_protobuf_Column_fieldAccessorTable\n            .ensureFieldAccessorsInitialized(\n                com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column.class, com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column.Builder.class);\n      }\n\n      // Construct using com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column.newBuilder()\n      private Builder() {\n        maybeForceBuilderInitialization();\n      }\n\n      private Builder(\n          com.google.protobuf.GeneratedMessage.BuilderParent parent) {\n        super(parent);\n        maybeForceBuilderInitialization();\n      }\n      private void maybeForceBuilderInitialization() {\n        if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) {\n        }\n      }\n      private static Builder create() {\n        return new Builder();\n      }\n\n      public Builder clear() {\n        super.clear();\n        index_ = 0;\n        bitField0_ = (bitField0_ & ~0x00000001);\n        name_ = \"\";\n        bitField0_ = (bitField0_ & ~0x00000002);\n        value_ = \"\";\n        bitField0_ = (bitField0_ & ~0x00000004);\n        isPrimaryKey_ = false;\n        bitField0_ = (bitField0_ & ~0x00000008);\n        isNull_ = false;\n        bitField0_ = (bitField0_ & ~0x00000010);\n        type_ = 0;\n        bitField0_ = (bitField0_ & ~0x00000020);\n        isUpdate_ = false;\n        bitField0_ = (bitField0_ & ~0x00000040);\n        return this;\n      }\n\n      public Builder clone() {\n        return create().mergeFrom(buildPartial());\n      }\n\n      public com.google.protobuf.Descriptors.Descriptor\n          getDescriptorForType() {\n        return com.alibaba.otter.node.etl.model.protobuf.BatchProto.internal_static_com_alibaba_otter_node_etl_model_protobuf_Column_descriptor;\n      }\n\n      public com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column getDefaultInstanceForType() {\n        return com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column.getDefaultInstance();\n      }\n\n      public com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column build() {\n        com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column result = buildPartial();\n        if (!result.isInitialized()) {\n          throw newUninitializedMessageException(result);\n        }\n        return result;\n      }\n\n      public com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column buildPartial() {\n        com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column result = new com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column(this);\n        int from_bitField0_ = bitField0_;\n        int to_bitField0_ = 0;\n        if (((from_bitField0_ & 0x00000001) == 0x00000001)) {\n          to_bitField0_ |= 0x00000001;\n        }\n        result.index_ = index_;\n        if (((from_bitField0_ & 0x00000002) == 0x00000002)) {\n          to_bitField0_ |= 0x00000002;\n        }\n        result.name_ = name_;\n        if (((from_bitField0_ & 0x00000004) == 0x00000004)) {\n          to_bitField0_ |= 0x00000004;\n        }\n        result.value_ = value_;\n        if (((from_bitField0_ & 0x00000008) == 0x00000008)) {\n          to_bitField0_ |= 0x00000008;\n        }\n        result.isPrimaryKey_ = isPrimaryKey_;\n        if (((from_bitField0_ & 0x00000010) == 0x00000010)) {\n          to_bitField0_ |= 0x00000010;\n        }\n        result.isNull_ = isNull_;\n        if (((from_bitField0_ & 0x00000020) == 0x00000020)) {\n          to_bitField0_ |= 0x00000020;\n        }\n        result.type_ = type_;\n        if (((from_bitField0_ & 0x00000040) == 0x00000040)) {\n          to_bitField0_ |= 0x00000040;\n        }\n        result.isUpdate_ = isUpdate_;\n        result.bitField0_ = to_bitField0_;\n        onBuilt();\n        return result;\n      }\n\n      public Builder mergeFrom(com.google.protobuf.Message other) {\n        if (other instanceof com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column) {\n          return mergeFrom((com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column)other);\n        } else {\n          super.mergeFrom(other);\n          return this;\n        }\n      }\n\n      public Builder mergeFrom(com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column other) {\n        if (other == com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column.getDefaultInstance()) return this;\n        if (other.hasIndex()) {\n          setIndex(other.getIndex());\n        }\n        if (other.hasName()) {\n          bitField0_ |= 0x00000002;\n          name_ = other.name_;\n          onChanged();\n        }\n        if (other.hasValue()) {\n          bitField0_ |= 0x00000004;\n          value_ = other.value_;\n          onChanged();\n        }\n        if (other.hasIsPrimaryKey()) {\n          setIsPrimaryKey(other.getIsPrimaryKey());\n        }\n        if (other.hasIsNull()) {\n          setIsNull(other.getIsNull());\n        }\n        if (other.hasType()) {\n          setType(other.getType());\n        }\n        if (other.hasIsUpdate()) {\n          setIsUpdate(other.getIsUpdate());\n        }\n        this.mergeUnknownFields(other.getUnknownFields());\n        return this;\n      }\n\n      public final boolean isInitialized() {\n        return true;\n      }\n\n      public Builder mergeFrom(\n          com.google.protobuf.CodedInputStream input,\n          com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n          throws java.io.IOException {\n        com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column parsedMessage = null;\n        try {\n          parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry);\n        } catch (com.google.protobuf.InvalidProtocolBufferException e) {\n          parsedMessage = (com.alibaba.otter.node.etl.model.protobuf.BatchProto.Column) e.getUnfinishedMessage();\n          throw e;\n        } finally {\n          if (parsedMessage != null) {\n            mergeFrom(parsedMessage);\n          }\n        }\n        return this;\n      }\n      private int bitField0_;\n\n      private int index_ ;\n      /**\n       * <code>optional int32 index = 1;</code>\n       *\n       * <pre>\n       **列下标*\n       * </pre>\n       */\n      public boolean hasIndex() {\n        return ((bitField0_ & 0x00000001) == 0x00000001);\n      }\n      /**\n       * <code>optional int32 index = 1;</code>\n       *\n       * <pre>\n       **列下标*\n       * </pre>\n       */\n      public int getIndex() {\n        return index_;\n      }\n      /**\n       * <code>optional int32 index = 1;</code>\n       *\n       * <pre>\n       **列下标*\n       * </pre>\n       */\n      public Builder setIndex(int value) {\n        bitField0_ |= 0x00000001;\n        index_ = value;\n        onChanged();\n        return this;\n      }\n      /**\n       * <code>optional int32 index = 1;</code>\n       *\n       * <pre>\n       **列下标*\n       * </pre>\n       */\n      public Builder clearIndex() {\n        bitField0_ = (bitField0_ & ~0x00000001);\n        index_ = 0;\n        onChanged();\n        return this;\n      }\n\n      private java.lang.Object name_ = \"\";\n      /**\n       * <code>optional string name = 2;</code>\n       *\n       * <pre>\n       **列名*\n       * </pre>\n       */\n      public boolean hasName() {\n        return ((bitField0_ & 0x00000002) == 0x00000002);\n      }\n      /**\n       * <code>optional string name = 2;</code>\n       *\n       * <pre>\n       **列名*\n       * </pre>\n       */\n      public java.lang.String getName() {\n        java.lang.Object ref = name_;\n        if (!(ref instanceof java.lang.String)) {\n          com.google.protobuf.ByteString bs =\n              (com.google.protobuf.ByteString) ref;\n          java.lang.String s = bs.toStringUtf8();\n          if (bs.isValidUtf8()) {\n            name_ = s;\n          }\n          return s;\n        } else {\n          return (java.lang.String) ref;\n        }\n      }\n      /**\n       * <code>optional string name = 2;</code>\n       *\n       * <pre>\n       **列名*\n       * </pre>\n       */\n      public com.google.protobuf.ByteString\n          getNameBytes() {\n        java.lang.Object ref = name_;\n        if (ref instanceof String) {\n          com.google.protobuf.ByteString b = \n              com.google.protobuf.ByteString.copyFromUtf8(\n                  (java.lang.String) ref);\n          name_ = b;\n          return b;\n        } else {\n          return (com.google.protobuf.ByteString) ref;\n        }\n      }\n      /**\n       * <code>optional string name = 2;</code>\n       *\n       * <pre>\n       **列名*\n       * </pre>\n       */\n      public Builder setName(\n          java.lang.String value) {\n        if (value == null) {\n    throw new NullPointerException();\n  }\n  bitField0_ |= 0x00000002;\n        name_ = value;\n        onChanged();\n        return this;\n      }\n      /**\n       * <code>optional string name = 2;</code>\n       *\n       * <pre>\n       **列名*\n       * </pre>\n       */\n      public Builder clearName() {\n        bitField0_ = (bitField0_ & ~0x00000002);\n        name_ = getDefaultInstance().getName();\n        onChanged();\n        return this;\n      }\n      /**\n       * <code>optional string name = 2;</code>\n       *\n       * <pre>\n       **列名*\n       * </pre>\n       */\n      public Builder setNameBytes(\n          com.google.protobuf.ByteString value) {\n        if (value == null) {\n    throw new NullPointerException();\n  }\n  bitField0_ |= 0x00000002;\n        name_ = value;\n        onChanged();\n        return this;\n      }\n\n      private java.lang.Object value_ = \"\";\n      /**\n       * <code>optional string value = 3;</code>\n       *\n       * <pre>\n       **列值,timestamp,Datetime是一个long型的数字*\n       * </pre>\n       */\n      public boolean hasValue() {\n        return ((bitField0_ & 0x00000004) == 0x00000004);\n      }\n      /**\n       * <code>optional string value = 3;</code>\n       *\n       * <pre>\n       **列值,timestamp,Datetime是一个long型的数字*\n       * </pre>\n       */\n      public java.lang.String getValue() {\n        java.lang.Object ref = value_;\n        if (!(ref instanceof java.lang.String)) {\n          com.google.protobuf.ByteString bs =\n              (com.google.protobuf.ByteString) ref;\n          java.lang.String s = bs.toStringUtf8();\n          if (bs.isValidUtf8()) {\n            value_ = s;\n          }\n          return s;\n        } else {\n          return (java.lang.String) ref;\n        }\n      }\n      /**\n       * <code>optional string value = 3;</code>\n       *\n       * <pre>\n       **列值,timestamp,Datetime是一个long型的数字*\n       * </pre>\n       */\n      public com.google.protobuf.ByteString\n          getValueBytes() {\n        java.lang.Object ref = value_;\n        if (ref instanceof String) {\n          com.google.protobuf.ByteString b = \n              com.google.protobuf.ByteString.copyFromUtf8(\n                  (java.lang.String) ref);\n          value_ = b;\n          return b;\n        } else {\n          return (com.google.protobuf.ByteString) ref;\n        }\n      }\n      /**\n       * <code>optional string value = 3;</code>\n       *\n       * <pre>\n       **列值,timestamp,Datetime是一个long型的数字*\n       * </pre>\n       */\n      public Builder setValue(\n          java.lang.String value) {\n        if (value == null) {\n    throw new NullPointerException();\n  }\n  bitField0_ |= 0x00000004;\n        value_ = value;\n        onChanged();\n        return this;\n      }\n      /**\n       * <code>optional string value = 3;</code>\n       *\n       * <pre>\n       **列值,timestamp,Datetime是一个long型的数字*\n       * </pre>\n       */\n      public Builder clearValue() {\n        bitField0_ = (bitField0_ & ~0x00000004);\n        value_ = getDefaultInstance().getValue();\n        onChanged();\n        return this;\n      }\n      /**\n       * <code>optional string value = 3;</code>\n       *\n       * <pre>\n       **列值,timestamp,Datetime是一个long型的数字*\n       * </pre>\n       */\n      public Builder setValueBytes(\n          com.google.protobuf.ByteString value) {\n        if (value == null) {\n    throw new NullPointerException();\n  }\n  bitField0_ |= 0x00000004;\n        value_ = value;\n        onChanged();\n        return this;\n      }\n\n      private boolean isPrimaryKey_ ;\n      /**\n       * <code>optional bool isPrimaryKey = 4;</code>\n       *\n       * <pre>\n       **当前列是否是主键*\n       * </pre>\n       */\n      public boolean hasIsPrimaryKey() {\n        return ((bitField0_ & 0x00000008) == 0x00000008);\n      }\n      /**\n       * <code>optional bool isPrimaryKey = 4;</code>\n       *\n       * <pre>\n       **当前列是否是主键*\n       * </pre>\n       */\n      public boolean getIsPrimaryKey() {\n        return isPrimaryKey_;\n      }\n      /**\n       * <code>optional bool isPrimaryKey = 4;</code>\n       *\n       * <pre>\n       **当前列是否是主键*\n       * </pre>\n       */\n      public Builder setIsPrimaryKey(boolean value) {\n        bitField0_ |= 0x00000008;\n        isPrimaryKey_ = value;\n        onChanged();\n        return this;\n      }\n      /**\n       * <code>optional bool isPrimaryKey = 4;</code>\n       *\n       * <pre>\n       **当前列是否是主键*\n       * </pre>\n       */\n      public Builder clearIsPrimaryKey() {\n        bitField0_ = (bitField0_ & ~0x00000008);\n        isPrimaryKey_ = false;\n        onChanged();\n        return this;\n      }\n\n      private boolean isNull_ ;\n      /**\n       * <code>optional bool isNull = 5;</code>\n       *\n       * <pre>\n       **当前列是否可以为空*\n       * </pre>\n       */\n      public boolean hasIsNull() {\n        return ((bitField0_ & 0x00000010) == 0x00000010);\n      }\n      /**\n       * <code>optional bool isNull = 5;</code>\n       *\n       * <pre>\n       **当前列是否可以为空*\n       * </pre>\n       */\n      public boolean getIsNull() {\n        return isNull_;\n      }\n      /**\n       * <code>optional bool isNull = 5;</code>\n       *\n       * <pre>\n       **当前列是否可以为空*\n       * </pre>\n       */\n      public Builder setIsNull(boolean value) {\n        bitField0_ |= 0x00000010;\n        isNull_ = value;\n        onChanged();\n        return this;\n      }\n      /**\n       * <code>optional bool isNull = 5;</code>\n       *\n       * <pre>\n       **当前列是否可以为空*\n       * </pre>\n       */\n      public Builder clearIsNull() {\n        bitField0_ = (bitField0_ & ~0x00000010);\n        isNull_ = false;\n        onChanged();\n        return this;\n      }\n\n      private int type_ ;\n      /**\n       * <code>optional int32 type = 6;</code>\n       *\n       * <pre>\n       **当前列的数据类型*\n       * </pre>\n       */\n      public boolean hasType() {\n        return ((bitField0_ & 0x00000020) == 0x00000020);\n      }\n      /**\n       * <code>optional int32 type = 6;</code>\n       *\n       * <pre>\n       **当前列的数据类型*\n       * </pre>\n       */\n      public int getType() {\n        return type_;\n      }\n      /**\n       * <code>optional int32 type = 6;</code>\n       *\n       * <pre>\n       **当前列的数据类型*\n       * </pre>\n       */\n      public Builder setType(int value) {\n        bitField0_ |= 0x00000020;\n        type_ = value;\n        onChanged();\n        return this;\n      }\n      /**\n       * <code>optional int32 type = 6;</code>\n       *\n       * <pre>\n       **当前列的数据类型*\n       * </pre>\n       */\n      public Builder clearType() {\n        bitField0_ = (bitField0_ & ~0x00000020);\n        type_ = 0;\n        onChanged();\n        return this;\n      }\n\n      private boolean isUpdate_ ;\n      /**\n       * <code>optional bool isUpdate = 7;</code>\n       *\n       * <pre>\n       **当前列是否发生真实变更*\n       * </pre>\n       */\n      public boolean hasIsUpdate() {\n        return ((bitField0_ & 0x00000040) == 0x00000040);\n      }\n      /**\n       * <code>optional bool isUpdate = 7;</code>\n       *\n       * <pre>\n       **当前列是否发生真实变更*\n       * </pre>\n       */\n      public boolean getIsUpdate() {\n        return isUpdate_;\n      }\n      /**\n       * <code>optional bool isUpdate = 7;</code>\n       *\n       * <pre>\n       **当前列是否发生真实变更*\n       * </pre>\n       */\n      public Builder setIsUpdate(boolean value) {\n        bitField0_ |= 0x00000040;\n        isUpdate_ = value;\n        onChanged();\n        return this;\n      }\n      /**\n       * <code>optional bool isUpdate = 7;</code>\n       *\n       * <pre>\n       **当前列是否发生真实变更*\n       * </pre>\n       */\n      public Builder clearIsUpdate() {\n        bitField0_ = (bitField0_ & ~0x00000040);\n        isUpdate_ = false;\n        onChanged();\n        return this;\n      }\n\n      // @@protoc_insertion_point(builder_scope:com.alibaba.otter.node.etl.model.protobuf.Column)\n    }\n\n    static {\n      defaultInstance = new Column(true);\n      defaultInstance.initFields();\n    }\n\n    // @@protoc_insertion_point(class_scope:com.alibaba.otter.node.etl.model.protobuf.Column)\n  }\n\n  public interface FileDataOrBuilder extends\n      // @@protoc_insertion_point(interface_extends:com.alibaba.otter.node.etl.model.protobuf.FileData)\n      com.google.protobuf.MessageOrBuilder {\n\n    /**\n     * <code>optional string eventType = 1;</code>\n     *\n     * <pre>\n     **变更数据的操作类型(I/U/D/C/A/E)*\n     * </pre>\n     */\n    boolean hasEventType();\n    /**\n     * <code>optional string eventType = 1;</code>\n     *\n     * <pre>\n     **变更数据的操作类型(I/U/D/C/A/E)*\n     * </pre>\n     */\n    java.lang.String getEventType();\n    /**\n     * <code>optional string eventType = 1;</code>\n     *\n     * <pre>\n     **变更数据的操作类型(I/U/D/C/A/E)*\n     * </pre>\n     */\n    com.google.protobuf.ByteString\n        getEventTypeBytes();\n\n    /**\n     * <code>optional string namespace = 2;</code>\n     *\n     * <pre>\n     **Aranda中特有的*\n     * </pre>\n     */\n    boolean hasNamespace();\n    /**\n     * <code>optional string namespace = 2;</code>\n     *\n     * <pre>\n     **Aranda中特有的*\n     * </pre>\n     */\n    java.lang.String getNamespace();\n    /**\n     * <code>optional string namespace = 2;</code>\n     *\n     * <pre>\n     **Aranda中特有的*\n     * </pre>\n     */\n    com.google.protobuf.ByteString\n        getNamespaceBytes();\n\n    /**\n     * <code>optional string path = 3;</code>\n     *\n     * <pre>\n     **文件的路径*\n     * </pre>\n     */\n    boolean hasPath();\n    /**\n     * <code>optional string path = 3;</code>\n     *\n     * <pre>\n     **文件的路径*\n     * </pre>\n     */\n    java.lang.String getPath();\n    /**\n     * <code>optional string path = 3;</code>\n     *\n     * <pre>\n     **文件的路径*\n     * </pre>\n     */\n    com.google.protobuf.ByteString\n        getPathBytes();\n\n    /**\n     * <code>optional int64 lastModifiedTime = 4;</code>\n     *\n     * <pre>\n     **文件最后一次修改时间*\n     * </pre>\n     */\n    boolean hasLastModifiedTime();\n    /**\n     * <code>optional int64 lastModifiedTime = 4;</code>\n     *\n     * <pre>\n     **文件最后一次修改时间*\n     * </pre>\n     */\n    long getLastModifiedTime();\n\n    /**\n     * <code>optional int64 size = 5;</code>\n     *\n     * <pre>\n     **文件的大小*\n     * </pre>\n     */\n    boolean hasSize();\n    /**\n     * <code>optional int64 size = 5;</code>\n     *\n     * <pre>\n     **文件的大小*\n     * </pre>\n     */\n    long getSize();\n\n    /**\n     * <code>optional int64 tableId = 6;</code>\n     */\n    boolean hasTableId();\n    /**\n     * <code>optional int64 tableId = 6;</code>\n     */\n    long getTableId();\n\n    /**\n     * <code>optional int64 pairId = 9;</code>\n     *\n     * <pre>\n     **映射规则id*\n     * </pre>\n     */\n    boolean hasPairId();\n    /**\n     * <code>optional int64 pairId = 9;</code>\n     *\n     * <pre>\n     **映射规则id*\n     * </pre>\n     */\n    long getPairId();\n  }\n  /**\n   * Protobuf type {@code com.alibaba.otter.node.etl.model.protobuf.FileData}\n   *\n   * <pre>\n   **文件对象*\n   * </pre>\n   */\n  public static final class FileData extends\n      com.google.protobuf.GeneratedMessage implements\n      // @@protoc_insertion_point(message_implements:com.alibaba.otter.node.etl.model.protobuf.FileData)\n      FileDataOrBuilder {\n    // Use FileData.newBuilder() to construct.\n    private FileData(com.google.protobuf.GeneratedMessage.Builder<?> builder) {\n      super(builder);\n      this.unknownFields = builder.getUnknownFields();\n    }\n    private FileData(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); }\n\n    private static final FileData defaultInstance;\n    public static FileData getDefaultInstance() {\n      return defaultInstance;\n    }\n\n    public FileData getDefaultInstanceForType() {\n      return defaultInstance;\n    }\n\n    private final com.google.protobuf.UnknownFieldSet unknownFields;\n    @java.lang.Override\n    public final com.google.protobuf.UnknownFieldSet\n        getUnknownFields() {\n      return this.unknownFields;\n    }\n    private FileData(\n        com.google.protobuf.CodedInputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      initFields();\n      int mutable_bitField0_ = 0;\n      com.google.protobuf.UnknownFieldSet.Builder unknownFields =\n          com.google.protobuf.UnknownFieldSet.newBuilder();\n      try {\n        boolean done = false;\n        while (!done) {\n          int tag = input.readTag();\n          switch (tag) {\n            case 0:\n              done = true;\n              break;\n            default: {\n              if (!parseUnknownField(input, unknownFields,\n                                     extensionRegistry, tag)) {\n                done = true;\n              }\n              break;\n            }\n            case 10: {\n              com.google.protobuf.ByteString bs = input.readBytes();\n              bitField0_ |= 0x00000001;\n              eventType_ = bs;\n              break;\n            }\n            case 18: {\n              com.google.protobuf.ByteString bs = input.readBytes();\n              bitField0_ |= 0x00000002;\n              namespace_ = bs;\n              break;\n            }\n            case 26: {\n              com.google.protobuf.ByteString bs = input.readBytes();\n              bitField0_ |= 0x00000004;\n              path_ = bs;\n              break;\n            }\n            case 32: {\n              bitField0_ |= 0x00000008;\n              lastModifiedTime_ = input.readInt64();\n              break;\n            }\n            case 40: {\n              bitField0_ |= 0x00000010;\n              size_ = input.readInt64();\n              break;\n            }\n            case 48: {\n              bitField0_ |= 0x00000020;\n              tableId_ = input.readInt64();\n              break;\n            }\n            case 72: {\n              bitField0_ |= 0x00000040;\n              pairId_ = input.readInt64();\n              break;\n            }\n          }\n        }\n      } catch (com.google.protobuf.InvalidProtocolBufferException e) {\n        throw e.setUnfinishedMessage(this);\n      } catch (java.io.IOException e) {\n        throw new com.google.protobuf.InvalidProtocolBufferException(\n            e.getMessage()).setUnfinishedMessage(this);\n      } finally {\n        this.unknownFields = unknownFields.build();\n        makeExtensionsImmutable();\n      }\n    }\n    public static final com.google.protobuf.Descriptors.Descriptor\n        getDescriptor() {\n      return com.alibaba.otter.node.etl.model.protobuf.BatchProto.internal_static_com_alibaba_otter_node_etl_model_protobuf_FileData_descriptor;\n    }\n\n    protected com.google.protobuf.GeneratedMessage.FieldAccessorTable\n        internalGetFieldAccessorTable() {\n      return com.alibaba.otter.node.etl.model.protobuf.BatchProto.internal_static_com_alibaba_otter_node_etl_model_protobuf_FileData_fieldAccessorTable\n          .ensureFieldAccessorsInitialized(\n              com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileData.class, com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileData.Builder.class);\n    }\n\n    public static com.google.protobuf.Parser<FileData> PARSER =\n        new com.google.protobuf.AbstractParser<FileData>() {\n      public FileData parsePartialFrom(\n          com.google.protobuf.CodedInputStream input,\n          com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n          throws com.google.protobuf.InvalidProtocolBufferException {\n        return new FileData(input, extensionRegistry);\n      }\n    };\n\n    @java.lang.Override\n    public com.google.protobuf.Parser<FileData> getParserForType() {\n      return PARSER;\n    }\n\n    private int bitField0_;\n    public static final int EVENTTYPE_FIELD_NUMBER = 1;\n    private java.lang.Object eventType_;\n    /**\n     * <code>optional string eventType = 1;</code>\n     *\n     * <pre>\n     **变更数据的操作类型(I/U/D/C/A/E)*\n     * </pre>\n     */\n    public boolean hasEventType() {\n      return ((bitField0_ & 0x00000001) == 0x00000001);\n    }\n    /**\n     * <code>optional string eventType = 1;</code>\n     *\n     * <pre>\n     **变更数据的操作类型(I/U/D/C/A/E)*\n     * </pre>\n     */\n    public java.lang.String getEventType() {\n      java.lang.Object ref = eventType_;\n      if (ref instanceof java.lang.String) {\n        return (java.lang.String) ref;\n      } else {\n        com.google.protobuf.ByteString bs = \n            (com.google.protobuf.ByteString) ref;\n        java.lang.String s = bs.toStringUtf8();\n        if (bs.isValidUtf8()) {\n          eventType_ = s;\n        }\n        return s;\n      }\n    }\n    /**\n     * <code>optional string eventType = 1;</code>\n     *\n     * <pre>\n     **变更数据的操作类型(I/U/D/C/A/E)*\n     * </pre>\n     */\n    public com.google.protobuf.ByteString\n        getEventTypeBytes() {\n      java.lang.Object ref = eventType_;\n      if (ref instanceof java.lang.String) {\n        com.google.protobuf.ByteString b = \n            com.google.protobuf.ByteString.copyFromUtf8(\n                (java.lang.String) ref);\n        eventType_ = b;\n        return b;\n      } else {\n        return (com.google.protobuf.ByteString) ref;\n      }\n    }\n\n    public static final int NAMESPACE_FIELD_NUMBER = 2;\n    private java.lang.Object namespace_;\n    /**\n     * <code>optional string namespace = 2;</code>\n     *\n     * <pre>\n     **Aranda中特有的*\n     * </pre>\n     */\n    public boolean hasNamespace() {\n      return ((bitField0_ & 0x00000002) == 0x00000002);\n    }\n    /**\n     * <code>optional string namespace = 2;</code>\n     *\n     * <pre>\n     **Aranda中特有的*\n     * </pre>\n     */\n    public java.lang.String getNamespace() {\n      java.lang.Object ref = namespace_;\n      if (ref instanceof java.lang.String) {\n        return (java.lang.String) ref;\n      } else {\n        com.google.protobuf.ByteString bs = \n            (com.google.protobuf.ByteString) ref;\n        java.lang.String s = bs.toStringUtf8();\n        if (bs.isValidUtf8()) {\n          namespace_ = s;\n        }\n        return s;\n      }\n    }\n    /**\n     * <code>optional string namespace = 2;</code>\n     *\n     * <pre>\n     **Aranda中特有的*\n     * </pre>\n     */\n    public com.google.protobuf.ByteString\n        getNamespaceBytes() {\n      java.lang.Object ref = namespace_;\n      if (ref instanceof java.lang.String) {\n        com.google.protobuf.ByteString b = \n            com.google.protobuf.ByteString.copyFromUtf8(\n                (java.lang.String) ref);\n        namespace_ = b;\n        return b;\n      } else {\n        return (com.google.protobuf.ByteString) ref;\n      }\n    }\n\n    public static final int PATH_FIELD_NUMBER = 3;\n    private java.lang.Object path_;\n    /**\n     * <code>optional string path = 3;</code>\n     *\n     * <pre>\n     **文件的路径*\n     * </pre>\n     */\n    public boolean hasPath() {\n      return ((bitField0_ & 0x00000004) == 0x00000004);\n    }\n    /**\n     * <code>optional string path = 3;</code>\n     *\n     * <pre>\n     **文件的路径*\n     * </pre>\n     */\n    public java.lang.String getPath() {\n      java.lang.Object ref = path_;\n      if (ref instanceof java.lang.String) {\n        return (java.lang.String) ref;\n      } else {\n        com.google.protobuf.ByteString bs = \n            (com.google.protobuf.ByteString) ref;\n        java.lang.String s = bs.toStringUtf8();\n        if (bs.isValidUtf8()) {\n          path_ = s;\n        }\n        return s;\n      }\n    }\n    /**\n     * <code>optional string path = 3;</code>\n     *\n     * <pre>\n     **文件的路径*\n     * </pre>\n     */\n    public com.google.protobuf.ByteString\n        getPathBytes() {\n      java.lang.Object ref = path_;\n      if (ref instanceof java.lang.String) {\n        com.google.protobuf.ByteString b = \n            com.google.protobuf.ByteString.copyFromUtf8(\n                (java.lang.String) ref);\n        path_ = b;\n        return b;\n      } else {\n        return (com.google.protobuf.ByteString) ref;\n      }\n    }\n\n    public static final int LASTMODIFIEDTIME_FIELD_NUMBER = 4;\n    private long lastModifiedTime_;\n    /**\n     * <code>optional int64 lastModifiedTime = 4;</code>\n     *\n     * <pre>\n     **文件最后一次修改时间*\n     * </pre>\n     */\n    public boolean hasLastModifiedTime() {\n      return ((bitField0_ & 0x00000008) == 0x00000008);\n    }\n    /**\n     * <code>optional int64 lastModifiedTime = 4;</code>\n     *\n     * <pre>\n     **文件最后一次修改时间*\n     * </pre>\n     */\n    public long getLastModifiedTime() {\n      return lastModifiedTime_;\n    }\n\n    public static final int SIZE_FIELD_NUMBER = 5;\n    private long size_;\n    /**\n     * <code>optional int64 size = 5;</code>\n     *\n     * <pre>\n     **文件的大小*\n     * </pre>\n     */\n    public boolean hasSize() {\n      return ((bitField0_ & 0x00000010) == 0x00000010);\n    }\n    /**\n     * <code>optional int64 size = 5;</code>\n     *\n     * <pre>\n     **文件的大小*\n     * </pre>\n     */\n    public long getSize() {\n      return size_;\n    }\n\n    public static final int TABLEID_FIELD_NUMBER = 6;\n    private long tableId_;\n    /**\n     * <code>optional int64 tableId = 6;</code>\n     */\n    public boolean hasTableId() {\n      return ((bitField0_ & 0x00000020) == 0x00000020);\n    }\n    /**\n     * <code>optional int64 tableId = 6;</code>\n     */\n    public long getTableId() {\n      return tableId_;\n    }\n\n    public static final int PAIRID_FIELD_NUMBER = 9;\n    private long pairId_;\n    /**\n     * <code>optional int64 pairId = 9;</code>\n     *\n     * <pre>\n     **映射规则id*\n     * </pre>\n     */\n    public boolean hasPairId() {\n      return ((bitField0_ & 0x00000040) == 0x00000040);\n    }\n    /**\n     * <code>optional int64 pairId = 9;</code>\n     *\n     * <pre>\n     **映射规则id*\n     * </pre>\n     */\n    public long getPairId() {\n      return pairId_;\n    }\n\n    private void initFields() {\n      eventType_ = \"\";\n      namespace_ = \"\";\n      path_ = \"\";\n      lastModifiedTime_ = 0L;\n      size_ = 0L;\n      tableId_ = 0L;\n      pairId_ = 0L;\n    }\n    private byte memoizedIsInitialized = -1;\n    public final boolean isInitialized() {\n      byte isInitialized = memoizedIsInitialized;\n      if (isInitialized == 1) return true;\n      if (isInitialized == 0) return false;\n\n      memoizedIsInitialized = 1;\n      return true;\n    }\n\n    public void writeTo(com.google.protobuf.CodedOutputStream output)\n                        throws java.io.IOException {\n      getSerializedSize();\n      if (((bitField0_ & 0x00000001) == 0x00000001)) {\n        output.writeBytes(1, getEventTypeBytes());\n      }\n      if (((bitField0_ & 0x00000002) == 0x00000002)) {\n        output.writeBytes(2, getNamespaceBytes());\n      }\n      if (((bitField0_ & 0x00000004) == 0x00000004)) {\n        output.writeBytes(3, getPathBytes());\n      }\n      if (((bitField0_ & 0x00000008) == 0x00000008)) {\n        output.writeInt64(4, lastModifiedTime_);\n      }\n      if (((bitField0_ & 0x00000010) == 0x00000010)) {\n        output.writeInt64(5, size_);\n      }\n      if (((bitField0_ & 0x00000020) == 0x00000020)) {\n        output.writeInt64(6, tableId_);\n      }\n      if (((bitField0_ & 0x00000040) == 0x00000040)) {\n        output.writeInt64(9, pairId_);\n      }\n      getUnknownFields().writeTo(output);\n    }\n\n    private int memoizedSerializedSize = -1;\n    public int getSerializedSize() {\n      int size = memoizedSerializedSize;\n      if (size != -1) return size;\n\n      size = 0;\n      if (((bitField0_ & 0x00000001) == 0x00000001)) {\n        size += com.google.protobuf.CodedOutputStream\n          .computeBytesSize(1, getEventTypeBytes());\n      }\n      if (((bitField0_ & 0x00000002) == 0x00000002)) {\n        size += com.google.protobuf.CodedOutputStream\n          .computeBytesSize(2, getNamespaceBytes());\n      }\n      if (((bitField0_ & 0x00000004) == 0x00000004)) {\n        size += com.google.protobuf.CodedOutputStream\n          .computeBytesSize(3, getPathBytes());\n      }\n      if (((bitField0_ & 0x00000008) == 0x00000008)) {\n        size += com.google.protobuf.CodedOutputStream\n          .computeInt64Size(4, lastModifiedTime_);\n      }\n      if (((bitField0_ & 0x00000010) == 0x00000010)) {\n        size += com.google.protobuf.CodedOutputStream\n          .computeInt64Size(5, size_);\n      }\n      if (((bitField0_ & 0x00000020) == 0x00000020)) {\n        size += com.google.protobuf.CodedOutputStream\n          .computeInt64Size(6, tableId_);\n      }\n      if (((bitField0_ & 0x00000040) == 0x00000040)) {\n        size += com.google.protobuf.CodedOutputStream\n          .computeInt64Size(9, pairId_);\n      }\n      size += getUnknownFields().getSerializedSize();\n      memoizedSerializedSize = size;\n      return size;\n    }\n\n    private static final long serialVersionUID = 0L;\n    @java.lang.Override\n    protected java.lang.Object writeReplace()\n        throws java.io.ObjectStreamException {\n      return super.writeReplace();\n    }\n\n    public static com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileData parseFrom(\n        com.google.protobuf.ByteString data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return PARSER.parseFrom(data);\n    }\n    public static com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileData parseFrom(\n        com.google.protobuf.ByteString data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return PARSER.parseFrom(data, extensionRegistry);\n    }\n    public static com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileData parseFrom(byte[] data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return PARSER.parseFrom(data);\n    }\n    public static com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileData parseFrom(\n        byte[] data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return PARSER.parseFrom(data, extensionRegistry);\n    }\n    public static com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileData parseFrom(java.io.InputStream input)\n        throws java.io.IOException {\n      return PARSER.parseFrom(input);\n    }\n    public static com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileData parseFrom(\n        java.io.InputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return PARSER.parseFrom(input, extensionRegistry);\n    }\n    public static com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileData parseDelimitedFrom(java.io.InputStream input)\n        throws java.io.IOException {\n      return PARSER.parseDelimitedFrom(input);\n    }\n    public static com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileData parseDelimitedFrom(\n        java.io.InputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return PARSER.parseDelimitedFrom(input, extensionRegistry);\n    }\n    public static com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileData parseFrom(\n        com.google.protobuf.CodedInputStream input)\n        throws java.io.IOException {\n      return PARSER.parseFrom(input);\n    }\n    public static com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileData parseFrom(\n        com.google.protobuf.CodedInputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return PARSER.parseFrom(input, extensionRegistry);\n    }\n\n    public static Builder newBuilder() { return Builder.create(); }\n    public Builder newBuilderForType() { return newBuilder(); }\n    public static Builder newBuilder(com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileData prototype) {\n      return newBuilder().mergeFrom(prototype);\n    }\n    public Builder toBuilder() { return newBuilder(this); }\n\n    @java.lang.Override\n    protected Builder newBuilderForType(\n        com.google.protobuf.GeneratedMessage.BuilderParent parent) {\n      Builder builder = new Builder(parent);\n      return builder;\n    }\n    /**\n     * Protobuf type {@code com.alibaba.otter.node.etl.model.protobuf.FileData}\n     *\n     * <pre>\n     **文件对象*\n     * </pre>\n     */\n    public static final class Builder extends\n        com.google.protobuf.GeneratedMessage.Builder<Builder> implements\n        // @@protoc_insertion_point(builder_implements:com.alibaba.otter.node.etl.model.protobuf.FileData)\n        com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileDataOrBuilder {\n      public static final com.google.protobuf.Descriptors.Descriptor\n          getDescriptor() {\n        return com.alibaba.otter.node.etl.model.protobuf.BatchProto.internal_static_com_alibaba_otter_node_etl_model_protobuf_FileData_descriptor;\n      }\n\n      protected com.google.protobuf.GeneratedMessage.FieldAccessorTable\n          internalGetFieldAccessorTable() {\n        return com.alibaba.otter.node.etl.model.protobuf.BatchProto.internal_static_com_alibaba_otter_node_etl_model_protobuf_FileData_fieldAccessorTable\n            .ensureFieldAccessorsInitialized(\n                com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileData.class, com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileData.Builder.class);\n      }\n\n      // Construct using com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileData.newBuilder()\n      private Builder() {\n        maybeForceBuilderInitialization();\n      }\n\n      private Builder(\n          com.google.protobuf.GeneratedMessage.BuilderParent parent) {\n        super(parent);\n        maybeForceBuilderInitialization();\n      }\n      private void maybeForceBuilderInitialization() {\n        if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) {\n        }\n      }\n      private static Builder create() {\n        return new Builder();\n      }\n\n      public Builder clear() {\n        super.clear();\n        eventType_ = \"\";\n        bitField0_ = (bitField0_ & ~0x00000001);\n        namespace_ = \"\";\n        bitField0_ = (bitField0_ & ~0x00000002);\n        path_ = \"\";\n        bitField0_ = (bitField0_ & ~0x00000004);\n        lastModifiedTime_ = 0L;\n        bitField0_ = (bitField0_ & ~0x00000008);\n        size_ = 0L;\n        bitField0_ = (bitField0_ & ~0x00000010);\n        tableId_ = 0L;\n        bitField0_ = (bitField0_ & ~0x00000020);\n        pairId_ = 0L;\n        bitField0_ = (bitField0_ & ~0x00000040);\n        return this;\n      }\n\n      public Builder clone() {\n        return create().mergeFrom(buildPartial());\n      }\n\n      public com.google.protobuf.Descriptors.Descriptor\n          getDescriptorForType() {\n        return com.alibaba.otter.node.etl.model.protobuf.BatchProto.internal_static_com_alibaba_otter_node_etl_model_protobuf_FileData_descriptor;\n      }\n\n      public com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileData getDefaultInstanceForType() {\n        return com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileData.getDefaultInstance();\n      }\n\n      public com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileData build() {\n        com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileData result = buildPartial();\n        if (!result.isInitialized()) {\n          throw newUninitializedMessageException(result);\n        }\n        return result;\n      }\n\n      public com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileData buildPartial() {\n        com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileData result = new com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileData(this);\n        int from_bitField0_ = bitField0_;\n        int to_bitField0_ = 0;\n        if (((from_bitField0_ & 0x00000001) == 0x00000001)) {\n          to_bitField0_ |= 0x00000001;\n        }\n        result.eventType_ = eventType_;\n        if (((from_bitField0_ & 0x00000002) == 0x00000002)) {\n          to_bitField0_ |= 0x00000002;\n        }\n        result.namespace_ = namespace_;\n        if (((from_bitField0_ & 0x00000004) == 0x00000004)) {\n          to_bitField0_ |= 0x00000004;\n        }\n        result.path_ = path_;\n        if (((from_bitField0_ & 0x00000008) == 0x00000008)) {\n          to_bitField0_ |= 0x00000008;\n        }\n        result.lastModifiedTime_ = lastModifiedTime_;\n        if (((from_bitField0_ & 0x00000010) == 0x00000010)) {\n          to_bitField0_ |= 0x00000010;\n        }\n        result.size_ = size_;\n        if (((from_bitField0_ & 0x00000020) == 0x00000020)) {\n          to_bitField0_ |= 0x00000020;\n        }\n        result.tableId_ = tableId_;\n        if (((from_bitField0_ & 0x00000040) == 0x00000040)) {\n          to_bitField0_ |= 0x00000040;\n        }\n        result.pairId_ = pairId_;\n        result.bitField0_ = to_bitField0_;\n        onBuilt();\n        return result;\n      }\n\n      public Builder mergeFrom(com.google.protobuf.Message other) {\n        if (other instanceof com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileData) {\n          return mergeFrom((com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileData)other);\n        } else {\n          super.mergeFrom(other);\n          return this;\n        }\n      }\n\n      public Builder mergeFrom(com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileData other) {\n        if (other == com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileData.getDefaultInstance()) return this;\n        if (other.hasEventType()) {\n          bitField0_ |= 0x00000001;\n          eventType_ = other.eventType_;\n          onChanged();\n        }\n        if (other.hasNamespace()) {\n          bitField0_ |= 0x00000002;\n          namespace_ = other.namespace_;\n          onChanged();\n        }\n        if (other.hasPath()) {\n          bitField0_ |= 0x00000004;\n          path_ = other.path_;\n          onChanged();\n        }\n        if (other.hasLastModifiedTime()) {\n          setLastModifiedTime(other.getLastModifiedTime());\n        }\n        if (other.hasSize()) {\n          setSize(other.getSize());\n        }\n        if (other.hasTableId()) {\n          setTableId(other.getTableId());\n        }\n        if (other.hasPairId()) {\n          setPairId(other.getPairId());\n        }\n        this.mergeUnknownFields(other.getUnknownFields());\n        return this;\n      }\n\n      public final boolean isInitialized() {\n        return true;\n      }\n\n      public Builder mergeFrom(\n          com.google.protobuf.CodedInputStream input,\n          com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n          throws java.io.IOException {\n        com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileData parsedMessage = null;\n        try {\n          parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry);\n        } catch (com.google.protobuf.InvalidProtocolBufferException e) {\n          parsedMessage = (com.alibaba.otter.node.etl.model.protobuf.BatchProto.FileData) e.getUnfinishedMessage();\n          throw e;\n        } finally {\n          if (parsedMessage != null) {\n            mergeFrom(parsedMessage);\n          }\n        }\n        return this;\n      }\n      private int bitField0_;\n\n      private java.lang.Object eventType_ = \"\";\n      /**\n       * <code>optional string eventType = 1;</code>\n       *\n       * <pre>\n       **变更数据的操作类型(I/U/D/C/A/E)*\n       * </pre>\n       */\n      public boolean hasEventType() {\n        return ((bitField0_ & 0x00000001) == 0x00000001);\n      }\n      /**\n       * <code>optional string eventType = 1;</code>\n       *\n       * <pre>\n       **变更数据的操作类型(I/U/D/C/A/E)*\n       * </pre>\n       */\n      public java.lang.String getEventType() {\n        java.lang.Object ref = eventType_;\n        if (!(ref instanceof java.lang.String)) {\n          com.google.protobuf.ByteString bs =\n              (com.google.protobuf.ByteString) ref;\n          java.lang.String s = bs.toStringUtf8();\n          if (bs.isValidUtf8()) {\n            eventType_ = s;\n          }\n          return s;\n        } else {\n          return (java.lang.String) ref;\n        }\n      }\n      /**\n       * <code>optional string eventType = 1;</code>\n       *\n       * <pre>\n       **变更数据的操作类型(I/U/D/C/A/E)*\n       * </pre>\n       */\n      public com.google.protobuf.ByteString\n          getEventTypeBytes() {\n        java.lang.Object ref = eventType_;\n        if (ref instanceof String) {\n          com.google.protobuf.ByteString b = \n              com.google.protobuf.ByteString.copyFromUtf8(\n                  (java.lang.String) ref);\n          eventType_ = b;\n          return b;\n        } else {\n          return (com.google.protobuf.ByteString) ref;\n        }\n      }\n      /**\n       * <code>optional string eventType = 1;</code>\n       *\n       * <pre>\n       **变更数据的操作类型(I/U/D/C/A/E)*\n       * </pre>\n       */\n      public Builder setEventType(\n          java.lang.String value) {\n        if (value == null) {\n    throw new NullPointerException();\n  }\n  bitField0_ |= 0x00000001;\n        eventType_ = value;\n        onChanged();\n        return this;\n      }\n      /**\n       * <code>optional string eventType = 1;</code>\n       *\n       * <pre>\n       **变更数据的操作类型(I/U/D/C/A/E)*\n       * </pre>\n       */\n      public Builder clearEventType() {\n        bitField0_ = (bitField0_ & ~0x00000001);\n        eventType_ = getDefaultInstance().getEventType();\n        onChanged();\n        return this;\n      }\n      /**\n       * <code>optional string eventType = 1;</code>\n       *\n       * <pre>\n       **变更数据的操作类型(I/U/D/C/A/E)*\n       * </pre>\n       */\n      public Builder setEventTypeBytes(\n          com.google.protobuf.ByteString value) {\n        if (value == null) {\n    throw new NullPointerException();\n  }\n  bitField0_ |= 0x00000001;\n        eventType_ = value;\n        onChanged();\n        return this;\n      }\n\n      private java.lang.Object namespace_ = \"\";\n      /**\n       * <code>optional string namespace = 2;</code>\n       *\n       * <pre>\n       **Aranda中特有的*\n       * </pre>\n       */\n      public boolean hasNamespace() {\n        return ((bitField0_ & 0x00000002) == 0x00000002);\n      }\n      /**\n       * <code>optional string namespace = 2;</code>\n       *\n       * <pre>\n       **Aranda中特有的*\n       * </pre>\n       */\n      public java.lang.String getNamespace() {\n        java.lang.Object ref = namespace_;\n        if (!(ref instanceof java.lang.String)) {\n          com.google.protobuf.ByteString bs =\n              (com.google.protobuf.ByteString) ref;\n          java.lang.String s = bs.toStringUtf8();\n          if (bs.isValidUtf8()) {\n            namespace_ = s;\n          }\n          return s;\n        } else {\n          return (java.lang.String) ref;\n        }\n      }\n      /**\n       * <code>optional string namespace = 2;</code>\n       *\n       * <pre>\n       **Aranda中特有的*\n       * </pre>\n       */\n      public com.google.protobuf.ByteString\n          getNamespaceBytes() {\n        java.lang.Object ref = namespace_;\n        if (ref instanceof String) {\n          com.google.protobuf.ByteString b = \n              com.google.protobuf.ByteString.copyFromUtf8(\n                  (java.lang.String) ref);\n          namespace_ = b;\n          return b;\n        } else {\n          return (com.google.protobuf.ByteString) ref;\n        }\n      }\n      /**\n       * <code>optional string namespace = 2;</code>\n       *\n       * <pre>\n       **Aranda中特有的*\n       * </pre>\n       */\n      public Builder setNamespace(\n          java.lang.String value) {\n        if (value == null) {\n    throw new NullPointerException();\n  }\n  bitField0_ |= 0x00000002;\n        namespace_ = value;\n        onChanged();\n        return this;\n      }\n      /**\n       * <code>optional string namespace = 2;</code>\n       *\n       * <pre>\n       **Aranda中特有的*\n       * </pre>\n       */\n      public Builder clearNamespace() {\n        bitField0_ = (bitField0_ & ~0x00000002);\n        namespace_ = getDefaultInstance().getNamespace();\n        onChanged();\n        return this;\n      }\n      /**\n       * <code>optional string namespace = 2;</code>\n       *\n       * <pre>\n       **Aranda中特有的*\n       * </pre>\n       */\n      public Builder setNamespaceBytes(\n          com.google.protobuf.ByteString value) {\n        if (value == null) {\n    throw new NullPointerException();\n  }\n  bitField0_ |= 0x00000002;\n        namespace_ = value;\n        onChanged();\n        return this;\n      }\n\n      private java.lang.Object path_ = \"\";\n      /**\n       * <code>optional string path = 3;</code>\n       *\n       * <pre>\n       **文件的路径*\n       * </pre>\n       */\n      public boolean hasPath() {\n        return ((bitField0_ & 0x00000004) == 0x00000004);\n      }\n      /**\n       * <code>optional string path = 3;</code>\n       *\n       * <pre>\n       **文件的路径*\n       * </pre>\n       */\n      public java.lang.String getPath() {\n        java.lang.Object ref = path_;\n        if (!(ref instanceof java.lang.String)) {\n          com.google.protobuf.ByteString bs =\n              (com.google.protobuf.ByteString) ref;\n          java.lang.String s = bs.toStringUtf8();\n          if (bs.isValidUtf8()) {\n            path_ = s;\n          }\n          return s;\n        } else {\n          return (java.lang.String) ref;\n        }\n      }\n      /**\n       * <code>optional string path = 3;</code>\n       *\n       * <pre>\n       **文件的路径*\n       * </pre>\n       */\n      public com.google.protobuf.ByteString\n          getPathBytes() {\n        java.lang.Object ref = path_;\n        if (ref instanceof String) {\n          com.google.protobuf.ByteString b = \n              com.google.protobuf.ByteString.copyFromUtf8(\n                  (java.lang.String) ref);\n          path_ = b;\n          return b;\n        } else {\n          return (com.google.protobuf.ByteString) ref;\n        }\n      }\n      /**\n       * <code>optional string path = 3;</code>\n       *\n       * <pre>\n       **文件的路径*\n       * </pre>\n       */\n      public Builder setPath(\n          java.lang.String value) {\n        if (value == null) {\n    throw new NullPointerException();\n  }\n  bitField0_ |= 0x00000004;\n        path_ = value;\n        onChanged();\n        return this;\n      }\n      /**\n       * <code>optional string path = 3;</code>\n       *\n       * <pre>\n       **文件的路径*\n       * </pre>\n       */\n      public Builder clearPath() {\n        bitField0_ = (bitField0_ & ~0x00000004);\n        path_ = getDefaultInstance().getPath();\n        onChanged();\n        return this;\n      }\n      /**\n       * <code>optional string path = 3;</code>\n       *\n       * <pre>\n       **文件的路径*\n       * </pre>\n       */\n      public Builder setPathBytes(\n          com.google.protobuf.ByteString value) {\n        if (value == null) {\n    throw new NullPointerException();\n  }\n  bitField0_ |= 0x00000004;\n        path_ = value;\n        onChanged();\n        return this;\n      }\n\n      private long lastModifiedTime_ ;\n      /**\n       * <code>optional int64 lastModifiedTime = 4;</code>\n       *\n       * <pre>\n       **文件最后一次修改时间*\n       * </pre>\n       */\n      public boolean hasLastModifiedTime() {\n        return ((bitField0_ & 0x00000008) == 0x00000008);\n      }\n      /**\n       * <code>optional int64 lastModifiedTime = 4;</code>\n       *\n       * <pre>\n       **文件最后一次修改时间*\n       * </pre>\n       */\n      public long getLastModifiedTime() {\n        return lastModifiedTime_;\n      }\n      /**\n       * <code>optional int64 lastModifiedTime = 4;</code>\n       *\n       * <pre>\n       **文件最后一次修改时间*\n       * </pre>\n       */\n      public Builder setLastModifiedTime(long value) {\n        bitField0_ |= 0x00000008;\n        lastModifiedTime_ = value;\n        onChanged();\n        return this;\n      }\n      /**\n       * <code>optional int64 lastModifiedTime = 4;</code>\n       *\n       * <pre>\n       **文件最后一次修改时间*\n       * </pre>\n       */\n      public Builder clearLastModifiedTime() {\n        bitField0_ = (bitField0_ & ~0x00000008);\n        lastModifiedTime_ = 0L;\n        onChanged();\n        return this;\n      }\n\n      private long size_ ;\n      /**\n       * <code>optional int64 size = 5;</code>\n       *\n       * <pre>\n       **文件的大小*\n       * </pre>\n       */\n      public boolean hasSize() {\n        return ((bitField0_ & 0x00000010) == 0x00000010);\n      }\n      /**\n       * <code>optional int64 size = 5;</code>\n       *\n       * <pre>\n       **文件的大小*\n       * </pre>\n       */\n      public long getSize() {\n        return size_;\n      }\n      /**\n       * <code>optional int64 size = 5;</code>\n       *\n       * <pre>\n       **文件的大小*\n       * </pre>\n       */\n      public Builder setSize(long value) {\n        bitField0_ |= 0x00000010;\n        size_ = value;\n        onChanged();\n        return this;\n      }\n      /**\n       * <code>optional int64 size = 5;</code>\n       *\n       * <pre>\n       **文件的大小*\n       * </pre>\n       */\n      public Builder clearSize() {\n        bitField0_ = (bitField0_ & ~0x00000010);\n        size_ = 0L;\n        onChanged();\n        return this;\n      }\n\n      private long tableId_ ;\n      /**\n       * <code>optional int64 tableId = 6;</code>\n       */\n      public boolean hasTableId() {\n        return ((bitField0_ & 0x00000020) == 0x00000020);\n      }\n      /**\n       * <code>optional int64 tableId = 6;</code>\n       */\n      public long getTableId() {\n        return tableId_;\n      }\n      /**\n       * <code>optional int64 tableId = 6;</code>\n       */\n      public Builder setTableId(long value) {\n        bitField0_ |= 0x00000020;\n        tableId_ = value;\n        onChanged();\n        return this;\n      }\n      /**\n       * <code>optional int64 tableId = 6;</code>\n       */\n      public Builder clearTableId() {\n        bitField0_ = (bitField0_ & ~0x00000020);\n        tableId_ = 0L;\n        onChanged();\n        return this;\n      }\n\n      private long pairId_ ;\n      /**\n       * <code>optional int64 pairId = 9;</code>\n       *\n       * <pre>\n       **映射规则id*\n       * </pre>\n       */\n      public boolean hasPairId() {\n        return ((bitField0_ & 0x00000040) == 0x00000040);\n      }\n      /**\n       * <code>optional int64 pairId = 9;</code>\n       *\n       * <pre>\n       **映射规则id*\n       * </pre>\n       */\n      public long getPairId() {\n        return pairId_;\n      }\n      /**\n       * <code>optional int64 pairId = 9;</code>\n       *\n       * <pre>\n       **映射规则id*\n       * </pre>\n       */\n      public Builder setPairId(long value) {\n        bitField0_ |= 0x00000040;\n        pairId_ = value;\n        onChanged();\n        return this;\n      }\n      /**\n       * <code>optional int64 pairId = 9;</code>\n       *\n       * <pre>\n       **映射规则id*\n       * </pre>\n       */\n      public Builder clearPairId() {\n        bitField0_ = (bitField0_ & ~0x00000040);\n        pairId_ = 0L;\n        onChanged();\n        return this;\n      }\n\n      // @@protoc_insertion_point(builder_scope:com.alibaba.otter.node.etl.model.protobuf.FileData)\n    }\n\n    static {\n      defaultInstance = new FileData(true);\n      defaultInstance.initFields();\n    }\n\n    // @@protoc_insertion_point(class_scope:com.alibaba.otter.node.etl.model.protobuf.FileData)\n  }\n\n  private static final com.google.protobuf.Descriptors.Descriptor\n    internal_static_com_alibaba_otter_node_etl_model_protobuf_Identity_descriptor;\n  private static\n    com.google.protobuf.GeneratedMessage.FieldAccessorTable\n      internal_static_com_alibaba_otter_node_etl_model_protobuf_Identity_fieldAccessorTable;\n  private static final com.google.protobuf.Descriptors.Descriptor\n    internal_static_com_alibaba_otter_node_etl_model_protobuf_RowBatch_descriptor;\n  private static\n    com.google.protobuf.GeneratedMessage.FieldAccessorTable\n      internal_static_com_alibaba_otter_node_etl_model_protobuf_RowBatch_fieldAccessorTable;\n  private static final com.google.protobuf.Descriptors.Descriptor\n    internal_static_com_alibaba_otter_node_etl_model_protobuf_FileBatch_descriptor;\n  private static\n    com.google.protobuf.GeneratedMessage.FieldAccessorTable\n      internal_static_com_alibaba_otter_node_etl_model_protobuf_FileBatch_fieldAccessorTable;\n  private static final com.google.protobuf.Descriptors.Descriptor\n    internal_static_com_alibaba_otter_node_etl_model_protobuf_RowData_descriptor;\n  private static\n    com.google.protobuf.GeneratedMessage.FieldAccessorTable\n      internal_static_com_alibaba_otter_node_etl_model_protobuf_RowData_fieldAccessorTable;\n  private static final com.google.protobuf.Descriptors.Descriptor\n    internal_static_com_alibaba_otter_node_etl_model_protobuf_Column_descriptor;\n  private static\n    com.google.protobuf.GeneratedMessage.FieldAccessorTable\n      internal_static_com_alibaba_otter_node_etl_model_protobuf_Column_fieldAccessorTable;\n  private static final com.google.protobuf.Descriptors.Descriptor\n    internal_static_com_alibaba_otter_node_etl_model_protobuf_FileData_descriptor;\n  private static\n    com.google.protobuf.GeneratedMessage.FieldAccessorTable\n      internal_static_com_alibaba_otter_node_etl_model_protobuf_FileData_fieldAccessorTable;\n\n  public static com.google.protobuf.Descriptors.FileDescriptor\n      getDescriptor() {\n    return descriptor;\n  }\n  private static com.google.protobuf.Descriptors.FileDescriptor\n      descriptor;\n  static {\n    java.lang.String[] descriptorData = {\n      \"\\n\\013Batch.proto\\022)com.alibaba.otter.node.et\" +\n      \"l.model.protobuf\\\"D\\n\\010Identity\\022\\021\\n\\tchannelI\" +\n      \"d\\030\\001 \\001(\\003\\022\\022\\n\\npipelineId\\030\\002 \\001(\\003\\022\\021\\n\\tprocessId\" +\n      \"\\030\\003 \\001(\\003\\\"\\223\\001\\n\\010RowBatch\\022E\\n\\010identity\\030\\001 \\001(\\01323.\" +\n      \"com.alibaba.otter.node.etl.model.protobu\" +\n      \"f.Identity\\022@\\n\\004rows\\030\\002 \\003(\\01322.com.alibaba.o\" +\n      \"tter.node.etl.model.protobuf.RowData\\\"\\226\\001\\n\" +\n      \"\\tFileBatch\\022E\\n\\010identity\\030\\001 \\001(\\01323.com.aliba\" +\n      \"ba.otter.node.etl.model.protobuf.Identit\" +\n      \"y\\022B\\n\\005files\\030\\002 \\003(\\01323.com.alibaba.otter.nod\",\n      \"e.etl.model.protobuf.FileData\\\"\\324\\003\\n\\007RowDat\" +\n      \"a\\022\\017\\n\\007tableId\\030\\001 \\001(\\003\\022\\022\\n\\nschemaName\\030\\002 \\001(\\t\\022\\021\" +\n      \"\\n\\ttableName\\030\\003 \\001(\\t\\022\\021\\n\\teventType\\030\\004 \\001(\\t\\022B\\n\\007\" +\n      \"oldKeys\\030\\005 \\003(\\01321.com.alibaba.otter.node.e\" +\n      \"tl.model.protobuf.Column\\022?\\n\\004keys\\030\\006 \\003(\\01321\" +\n      \".com.alibaba.otter.node.etl.model.protob\" +\n      \"uf.Column\\022B\\n\\007columns\\030\\007 \\003(\\01321.com.alibaba\" +\n      \".otter.node.etl.model.protobuf.Column\\022\\023\\n\" +\n      \"\\013executeTime\\030\\010 \\001(\\003\\022\\016\\n\\006pairId\\030\\t \\001(\\003\\022\\020\\n\\010sy\" +\n      \"ncMode\\030\\n \\001(\\t\\022\\027\\n\\017syncConsistency\\030\\013 \\001(\\t\\022\\014\\n\",\n      \"\\004size\\030\\014 \\001(\\003\\022\\016\\n\\006remedy\\030\\r \\001(\\010\\022\\013\\n\\003sql\\030\\016 \\001(\\t\" +\n      \"\\022\\025\\n\\rddlSchemaName\\030\\017 \\001(\\t\\022\\014\\n\\004hint\\030\\020 \\001(\\t\\022\\025\\n\" +\n      \"\\rwithoutSchema\\030\\021 \\001(\\010\\\"z\\n\\006Column\\022\\r\\n\\005index\\030\" +\n      \"\\001 \\001(\\005\\022\\014\\n\\004name\\030\\002 \\001(\\t\\022\\r\\n\\005value\\030\\003 \\001(\\t\\022\\024\\n\\014is\" +\n      \"PrimaryKey\\030\\004 \\001(\\010\\022\\016\\n\\006isNull\\030\\005 \\001(\\010\\022\\014\\n\\004type\" +\n      \"\\030\\006 \\001(\\005\\022\\020\\n\\010isUpdate\\030\\007 \\001(\\010\\\"\\207\\001\\n\\010FileData\\022\\021\\n\" +\n      \"\\teventType\\030\\001 \\001(\\t\\022\\021\\n\\tnamespace\\030\\002 \\001(\\t\\022\\014\\n\\004p\" +\n      \"ath\\030\\003 \\001(\\t\\022\\030\\n\\020lastModifiedTime\\030\\004 \\001(\\003\\022\\014\\n\\004s\" +\n      \"ize\\030\\005 \\001(\\003\\022\\017\\n\\007tableId\\030\\006 \\001(\\003\\022\\016\\n\\006pairId\\030\\t \\001\" +\n      \"(\\003B\\016B\\nBatchProtoH\\001\"\n    };\n    com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner =\n        new com.google.protobuf.Descriptors.FileDescriptor.    InternalDescriptorAssigner() {\n          public com.google.protobuf.ExtensionRegistry assignDescriptors(\n              com.google.protobuf.Descriptors.FileDescriptor root) {\n            descriptor = root;\n            return null;\n          }\n        };\n    com.google.protobuf.Descriptors.FileDescriptor\n      .internalBuildGeneratedFileFrom(descriptorData,\n        new com.google.protobuf.Descriptors.FileDescriptor[] {\n        }, assigner);\n    internal_static_com_alibaba_otter_node_etl_model_protobuf_Identity_descriptor =\n      getDescriptor().getMessageTypes().get(0);\n    internal_static_com_alibaba_otter_node_etl_model_protobuf_Identity_fieldAccessorTable = new\n      com.google.protobuf.GeneratedMessage.FieldAccessorTable(\n        internal_static_com_alibaba_otter_node_etl_model_protobuf_Identity_descriptor,\n        new java.lang.String[] { \"ChannelId\", \"PipelineId\", \"ProcessId\", });\n    internal_static_com_alibaba_otter_node_etl_model_protobuf_RowBatch_descriptor =\n      getDescriptor().getMessageTypes().get(1);\n    internal_static_com_alibaba_otter_node_etl_model_protobuf_RowBatch_fieldAccessorTable = new\n      com.google.protobuf.GeneratedMessage.FieldAccessorTable(\n        internal_static_com_alibaba_otter_node_etl_model_protobuf_RowBatch_descriptor,\n        new java.lang.String[] { \"Identity\", \"Rows\", });\n    internal_static_com_alibaba_otter_node_etl_model_protobuf_FileBatch_descriptor =\n      getDescriptor().getMessageTypes().get(2);\n    internal_static_com_alibaba_otter_node_etl_model_protobuf_FileBatch_fieldAccessorTable = new\n      com.google.protobuf.GeneratedMessage.FieldAccessorTable(\n        internal_static_com_alibaba_otter_node_etl_model_protobuf_FileBatch_descriptor,\n        new java.lang.String[] { \"Identity\", \"Files\", });\n    internal_static_com_alibaba_otter_node_etl_model_protobuf_RowData_descriptor =\n      getDescriptor().getMessageTypes().get(3);\n    internal_static_com_alibaba_otter_node_etl_model_protobuf_RowData_fieldAccessorTable = new\n      com.google.protobuf.GeneratedMessage.FieldAccessorTable(\n        internal_static_com_alibaba_otter_node_etl_model_protobuf_RowData_descriptor,\n        new java.lang.String[] { \"TableId\", \"SchemaName\", \"TableName\", \"EventType\", \"OldKeys\", \"Keys\", \"Columns\", \"ExecuteTime\", \"PairId\", \"SyncMode\", \"SyncConsistency\", \"Size\", \"Remedy\", \"Sql\", \"DdlSchemaName\", \"Hint\", \"WithoutSchema\", });\n    internal_static_com_alibaba_otter_node_etl_model_protobuf_Column_descriptor =\n      getDescriptor().getMessageTypes().get(4);\n    internal_static_com_alibaba_otter_node_etl_model_protobuf_Column_fieldAccessorTable = new\n      com.google.protobuf.GeneratedMessage.FieldAccessorTable(\n        internal_static_com_alibaba_otter_node_etl_model_protobuf_Column_descriptor,\n        new java.lang.String[] { \"Index\", \"Name\", \"Value\", \"IsPrimaryKey\", \"IsNull\", \"Type\", \"IsUpdate\", });\n    internal_static_com_alibaba_otter_node_etl_model_protobuf_FileData_descriptor =\n      getDescriptor().getMessageTypes().get(5);\n    internal_static_com_alibaba_otter_node_etl_model_protobuf_FileData_fieldAccessorTable = new\n      com.google.protobuf.GeneratedMessage.FieldAccessorTable(\n        internal_static_com_alibaba_otter_node_etl_model_protobuf_FileData_descriptor,\n        new java.lang.String[] { \"EventType\", \"Namespace\", \"Path\", \"LastModifiedTime\", \"Size\", \"TableId\", \"PairId\", });\n  }\n\n  // @@protoc_insertion_point(outer_class_scope)\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/select/SelectTask.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.select;\n\nimport java.util.Date;\nimport java.util.List;\nimport java.util.concurrent.BlockingQueue;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.LinkedBlockingQueue;\nimport java.util.concurrent.atomic.AtomicInteger;\nimport java.util.concurrent.locks.LockSupport;\n\nimport org.slf4j.MDC;\nimport org.springframework.util.CollectionUtils;\n\nimport com.alibaba.otter.canal.common.CanalException;\nimport com.alibaba.otter.node.common.statistics.StatisticsClientService;\nimport com.alibaba.otter.node.etl.OtterConstants;\nimport com.alibaba.otter.node.etl.common.jmx.StageAggregation.AggregationItem;\nimport com.alibaba.otter.node.etl.common.pipe.PipeKey;\nimport com.alibaba.otter.node.etl.common.task.GlobalTask;\nimport com.alibaba.otter.node.etl.extract.SetlFuture;\nimport com.alibaba.otter.node.etl.select.exceptions.SelectException;\nimport com.alibaba.otter.node.etl.select.selector.Message;\nimport com.alibaba.otter.node.etl.select.selector.OtterSelector;\nimport com.alibaba.otter.node.etl.select.selector.OtterSelectorFactory;\nimport com.alibaba.otter.shared.arbitrate.model.EtlEventData;\nimport com.alibaba.otter.shared.arbitrate.model.TerminEventData;\nimport com.alibaba.otter.shared.common.model.config.channel.Channel;\nimport com.alibaba.otter.shared.common.model.config.enums.StageType;\nimport com.alibaba.otter.shared.common.model.statistics.delay.DelayCount;\nimport com.alibaba.otter.shared.common.utils.lock.BooleanMutex;\nimport com.alibaba.otter.shared.etl.model.DbBatch;\nimport com.alibaba.otter.shared.etl.model.EventData;\nimport com.alibaba.otter.shared.etl.model.Identity;\nimport com.alibaba.otter.shared.etl.model.RowBatch;\n\n/**\n * select流处理模式的实现版本\n * \n * <pre>\n * 调度模型：\n * 1. 正常运行调度流程 \n * 假如并行度为3\n * ----------------------------------------------------------->时间轴\n * | ProcessSelect\n * --> 1 \n *      --> 2\n *           -->3\n *               --> 1\n * | ProcessTermin\n *     ---> 1 ack\n *          ----> 2ack\n *                 ---> 3ack\n * a. ProcessSelect拿到数据后，丢入pool池进行异步处理，并通知ProcessTermin顺序接收termin信号\n * b. 同一时间在s/e/t/l流水线上的数据受并行度控制，满了就会阻塞ProcessSelect，避免取过多的数据，只会多取一份，等待其中一个s/e/t/l完成\n * c. ProcessTermin接受到termin信号\n *    i. 会严格按照发出去的batchId/processId进行对比，发现不匹配，发起rollback操作.\n *    ii. 会根据terminType判断这一批数据是否处理成功，如果发现不成功，发起rollback操作\n * \n * 2. 异常调度流程\n * 假如并行度为3\n * |-->1 --> 2 -->3(ing)\n * a. 当第1份数据,ProcessTermin发现需要rollback，此时需要回滚2,3份数据的批次. (可能第2,3份数据还未提交到s/e/t/l调度中)\n *    i. 如果2批次数据已经提交，等待2批次termin信号的返回，此时需要阻塞ProcessSelect，避免再取新数据\n *    ii. 如果第2批次数据未提交，直接rollback数据，不再进入s/e/t/l调度流程\n * b. 当所有批次都已经处理完成，再通知ProcessSelect启动 (注意：这里会避免rollback和get并发操作，会造成数据不一致)\n * \n * 3. 热备机制\n * a. Select主线程会一直监听mainstem的信号，一旦抢占成功，则启动ProcessSelect/ProcessTermin线程\n * b. ProcessSelect/ProcessTermin在处理过程中，会检查一下当前节点是否为抢占mainstem成功的节点，如果发现不是，立马停止，继续监听mainstem\n * c. ProcessSelect进行get数据之前，会等到ProcessTermin会读取未被处理过termin信号，对上一次的selector进行ack/rollback处理\n *      i. 注意：ProcessSelect进行get数据时，需要保证batch/termin/get操作状态保持一致，必须都处于同一个数据点上\n * </pre>\n * \n * @author jianghang 2012-7-31 下午05:39:06\n * @version 4.1.0\n */\npublic class SelectTask extends GlobalTask {\n\n    // 运行调度控制\n    private volatile boolean           isStart          = false;\n    // 运行\n    private StatisticsClientService    statisticsClientService;\n    private OtterSelectorFactory       otterSelectorFactory;\n    private OtterSelector<Message>     otterSelector;\n    private ExecutorService            executor;\n    private BlockingQueue<BatchTermin> batchBuffer      = new LinkedBlockingQueue<BatchTermin>(50); // 设置有界队列，避免小batch处理太多\n    private boolean                    needCheck        = false;\n    private BooleanMutex               canStartSelector = new BooleanMutex(false);                 // 非常轻量的一个阻塞式实现，调用成本低\n    private AtomicInteger              rversion         = new AtomicInteger(0);\n    private long                       lastResetTime    = new Date().getTime();\n\n    public SelectTask(Long pipelineId){\n        super(pipelineId);\n    }\n\n    public void run() {\n        MDC.put(OtterConstants.splitPipelineLogFileKey, String.valueOf(pipelineId));\n        try {\n            while (running) {\n                try {\n                    if (isStart) {\n                        boolean working = arbitrateEventService.mainStemEvent().check(pipelineId);\n                        if (!working) {\n                            stopup(false);\n                        }\n\n                        LockSupport.parkNanos(5 * 1000 * 1000L * 1000L); // 5秒钟检查一次\n                    } else {\n                        startup();\n                    }\n                } catch (Throwable e) {\n                    if (isInterrupt(e)) {\n                        logger.info(\"INFO ## select is interrupt\", e);\n                        return;\n                    } else {\n                        logger.warn(\"WARN ## select is failed.\", e);\n                        sendRollbackTermin(pipelineId, e);\n\n                        // sleep 10秒再进行重试\n                        try {\n                            Thread.sleep(10 * 1000);\n                        } catch (InterruptedException e1) {\n                        }\n                    }\n                }\n            }\n        } finally {\n            arbitrateEventService.mainStemEvent().release(pipelineId);\n        }\n    }\n\n    /**\n     * 尝试启动，需要进行mainstem竞争，拿到锁之后才可以进行下一步\n     * \n     * @throws InterruptedException\n     */\n    private void startup() throws InterruptedException {\n        try {\n            arbitrateEventService.mainStemEvent().await(pipelineId);\n        } catch (Throwable e) {\n            if (isInterrupt(e)) {\n                logger.info(\"INFO ## this node is interrupt\", e);\n            } else {\n                logger.warn(\"WARN ## this node is crashed.\", e);\n            }\n            arbitrateEventService.mainStemEvent().release(pipelineId);\n            return;\n        }\n\n        executor = Executors.newFixedThreadPool(2); // 启动两个线程\n        // 启动selector\n        otterSelector = otterSelectorFactory.getSelector(pipelineId); // 获取对应的selector\n        otterSelector.start();\n\n        canStartSelector.set(false);// 初始化为false\n        startProcessTermin();\n        startProcessSelect();\n\n        isStart = true;\n    }\n\n    private synchronized void stopup(boolean needInterrut) throws InterruptedException {\n        if (isStart) {\n            if (executor != null) {\n                executor.shutdownNow();\n            }\n\n            if (otterSelector != null && otterSelector.isStart()) {\n                otterSelector.stop();\n            }\n\n            if (needInterrut) {\n                throw new InterruptedException();// 抛异常，退出自己\n            }\n\n            isStart = false;\n        }\n    }\n\n    /**\n     * 执行数据分发工作\n     */\n    private void startProcessSelect() {\n        executor.submit(new Runnable() {\n\n            public void run() {\n                MDC.put(OtterConstants.splitPipelineLogFileKey, String.valueOf(pipelineId));\n                String currentName = Thread.currentThread().getName();\n                Thread.currentThread().setName(createTaskName(pipelineId, \"ProcessSelect\"));\n                try {\n                    processSelect();\n                } finally {\n                    Thread.currentThread().setName(currentName);\n                    MDC.remove(OtterConstants.splitPipelineLogFileKey);\n                }\n            }\n        });\n\n    }\n\n    private void processSelect() {\n        while (running) {\n            try {\n                // 等待ProcessTermin exhaust，会阻塞\n                // ProcessTermin发现出现rollback，会立即通知暂停，比分布式permit及时性高\n                canStartSelector.get();\n\n                // 判断当前是否为工作节点，S模块不能出现双节点工作，selector容易出现数据错乱\n                if (needCheck) {\n                    checkContinueWork();\n                }\n\n                // 出现阻塞挂起时，等待mananger处理完成，解挂开启同步\n                arbitrateEventService.toolEvent().waitForPermit(pipelineId);// 出现rollback后能及时停住\n\n                // 使用startVersion要解决的一个问题：出现rollback时，尽可能判断取出来的数据是rollback前还是rollback后，想办法丢弃rollback前的数据。\n                // (因为出现rollback，之前取出去的几个批次的数据其实是没有执行成功，get取出来的数据会是其后一批数据，如果不丢弃的话，会出现后面的数据先执行，然后又回到出错的点，再执行一遍)\n                // int startVersion = rversion.get();\n                Message gotMessage = otterSelector.selector();\n\n                // modify by ljh at 2012-09-10，startVersion获取操作应该放在拿到数据之后\n                // 放在前面 : (遇到一个并发bug)\n                // // a.\n                // 先拿startVersion，再获取数据，在拿数据过程中rollback开始并完成了，导致selector返回时数据已经取到了末尾\n                // // b. 在进行version判断时发现已经有变化，导致又触发一次拿数据的过程，此时的get\n                // cursor已经到队列的末尾，拿不出任何数据，所以出现死等情况\n                // 放在后面 : (一点点瑕疵)\n                // // a.\n                // 并发操作rollback和selector时，针对拿到rollback前的老数据，此时startVersion还未初始化，导致判断不出出现过rollback操作，后面的变更数据会提前同步\n                // (概率性会比较高，取决于selector和初始化startVersion的时间间隔)\n                int startVersion = rversion.get();\n\n                if (canStartSelector.state() == false) { // 是否出现异常\n                    // 回滚在出现异常的瞬间，拿出来的数据，因为otterSelector.selector()会循环，可能出现了rollback，其还未感知到\n                    rollback(gotMessage.getId());\n                    continue;\n                }\n\n                if (CollectionUtils.isEmpty(gotMessage.getDatas())) {// 处理下空数据，也得更新下游标，可能是回环数据被过滤掉\n                    // 添加到待响应的buffer列表，不需要await termin信号，因为没启动过s/e/t/l流程\n                    batchBuffer.put(new BatchTermin(gotMessage.getId(), false));\n                    continue;\n                }\n\n                final EtlEventData etlEventData = arbitrateEventService.selectEvent().await(pipelineId);\n                if (rversion.get() != startVersion) {// 说明存在过变化，中间出现过rollback，需要丢弃该数据\n                    logger.warn(\"rollback happend , should skip this data and get new message.\");\n                    canStartSelector.get();// 确认一下rollback是否完成\n                    // 先睡眠一段时间，保证channel有足够的时间变成pause态，即使没有变成PAUSE态，***MemoryArbitrateEvent里面有回滚操作兜底。\n                    Thread.sleep(10 * 1000);\n                    arbitrateEventService.toolEvent().waitForPermit(pipelineId);\n                    gotMessage = otterSelector.selector();// 这时不管有没有数据，都需要执行一次s/e/t/l\n                }\n\n                final Message message = gotMessage;\n                final BatchTermin batchTermin = new BatchTermin(message.getId(), etlEventData.getProcessId());\n                batchBuffer.put(batchTermin); // 添加到待响应的buffer列表\n                Runnable task = new Runnable() {\n\n                    public void run() {\n                        // 设置profiling信息\n                        boolean profiling = isProfiling();\n                        Long profilingStartTime = null;\n                        if (profiling) {\n                            profilingStartTime = System.currentTimeMillis();\n                        }\n\n                        MDC.put(OtterConstants.splitPipelineLogFileKey, String.valueOf(pipelineId));\n                        String currentName = Thread.currentThread().getName();\n                        Thread.currentThread().setName(createTaskName(pipelineId, \"SelectWorker\"));\n                        try {\n                            pipeline = configClientService.findPipeline(pipelineId);\n                            List<EventData> eventData = message.getDatas();\n                            long startTime = etlEventData.getStartTime();\n                            if (!CollectionUtils.isEmpty(eventData)) {\n                                startTime = eventData.get(0).getExecuteTime();\n                            }\n\n                            Channel channel = configClientService.findChannelByPipelineId(pipelineId);\n                            RowBatch rowBatch = new RowBatch();\n                            // 构造唯一标识\n                            Identity identity = new Identity();\n                            identity.setChannelId(channel.getId());\n                            identity.setPipelineId(pipelineId);\n                            identity.setProcessId(etlEventData.getProcessId());\n                            rowBatch.setIdentity(identity);\n                            // 进行数据合并\n                            for (EventData data : eventData) {\n                                rowBatch.merge(data);\n                            }\n\n                            long nextNodeId = etlEventData.getNextNid();\n                            List<PipeKey> pipeKeys = rowDataPipeDelegate.put(new DbBatch(rowBatch), nextNodeId);\n                            etlEventData.setDesc(pipeKeys);\n                            etlEventData.setNumber((long) eventData.size());\n                            etlEventData.setFirstTime(startTime); // 使用原始数据的第一条\n                            etlEventData.setBatchId(message.getId());\n\n                            if (profiling) {\n                                Long profilingEndTime = System.currentTimeMillis();\n                                stageAggregationCollector.push(pipelineId,\n                                    StageType.SELECT,\n                                    new AggregationItem(profilingStartTime, profilingEndTime));\n                            }\n                            arbitrateEventService.selectEvent().single(etlEventData);\n                        } catch (Throwable e) {\n                            if (!isInterrupt(e)) {\n                                logger.error(String.format(\"[%s] selectwork executor is error! data:%s\",\n                                    pipelineId,\n                                    etlEventData), e);\n                                sendRollbackTermin(pipelineId, e);\n                            } else {\n                                logger.info(String.format(\"[%s] selectwork executor is interrrupt! data:%s\",\n                                    pipelineId,\n                                    etlEventData), e);\n                            }\n                        } finally {\n                            Thread.currentThread().setName(currentName);\n                            MDC.remove(OtterConstants.splitPipelineLogFileKey);\n                        }\n                    }\n                };\n\n                // 构造pending任务，可在关闭线程时退出任务\n                SetlFuture extractFuture = new SetlFuture(StageType.SELECT,\n                    etlEventData.getProcessId(),\n                    pendingFuture,\n                    task);\n                executorService.execute(extractFuture);\n\n            } catch (Throwable e) {\n                if (!isInterrupt(e)) {\n                    logger.error(String.format(\"[%s] selectTask is error!\", pipelineId), e);\n                    sendRollbackTermin(pipelineId, e);\n                } else {\n                    logger.info(String.format(\"[%s] selectTask is interrrupt!\", pipelineId), e);\n                    return;\n                }\n            }\n        }\n    }\n\n    private void startProcessTermin() {\n        executor.submit(new Runnable() {\n\n            public void run() {\n                MDC.put(OtterConstants.splitPipelineLogFileKey, String.valueOf(pipelineId));\n                String currentName = Thread.currentThread().getName();\n                Thread.currentThread().setName(createTaskName(pipelineId, \"ProcessTermin\"));\n                try {\n                    boolean lastStatus = true;\n                    while (running) {\n                        try {\n                            // 处理下上一次非正常退出后，未被处理的termin信息\n                            lastStatus = true;\n                            // 消费掉所有的termin，不管成功还是失败，都回滚canal中的数据，会有重复\n                            arbitrateEventService.terminEvent().exhaust(pipelineId);\n\n                            batchBuffer.clear();// 清空上一次的待处理的batch记录，因为所有的batch都会被rollback掉\n\n                            // 开始处理新的termin数据\n                            while (running) {\n                                if (batchBuffer.size() == 0) {\n                                    // termin任务已经处理完成，可以通知selector重新开始拉数据\n                                    if (canStartSelector.state() == false) {\n                                        otterSelector.rollback();// rollback一下所有节点，确保所有的节点都被ack，包括被预取出来的数据\n                                    }\n\n                                    lastStatus = true;\n                                    canStartSelector.set(true);\n                                }\n\n                                BatchTermin batch = batchBuffer.take();\n                                logger.info(\"start process termin : {}\", batch.toString());\n                                if (batch.isNeedWait()) {\n                                    lastStatus = processTermin(lastStatus, batch.getBatchId(), batch.getProcessId());\n                                } else {\n                                    // 不需要wait的批次，直接以上一个batch的结果决定是否ack\n                                    if (lastStatus) {\n                                        ack(batch.getBatchId());\n                                        sendDelayReset(pipelineId);\n                                    } else {\n                                        rollback(batch.getBatchId());// 会阻塞selector等待所有batch的rollback操作完成\n                                    }\n                                }\n\n                                logger.info(\"end process termin : {}  result : {}\", batch.toString(), lastStatus);\n                            }\n                        } catch (CanalException e) {// 捕获可处理的异常，进行retry,基本可自行恢复\n                            logger.info(String.format(\"[%s] ProcessTermin has an error! retry...\", pipelineId), e);\n                            notifyRollback();\n                        } catch (SelectException e) {// 捕获可处理的异常，进行retry,基本可自行恢复\n                            logger.info(String.format(\"[%s] ProcessTermin has an error! retry...\", pipelineId), e);\n                            notifyRollback();\n                        } catch (Throwable e) {\n                            if (isInterrupt(e)) {\n                                logger.info(String.format(\"[%s] ProcessTermin is interrupted!\", pipelineId), e);\n                                return;\n                            } else {\n                                logger.error(String.format(\"[%s] ProcessTermin is error!\", pipelineId), e);\n                                notifyRollback();\n                                sendRollbackTermin(pipelineId, e);\n                            }\n                        }\n\n                        try {\n                            Thread.sleep(30000); // sleep 30，等待termin信号都ready\n                        } catch (InterruptedException e) {\n                        }\n                    }\n                } finally {\n                    Thread.currentThread().setName(currentName);\n                    MDC.remove(OtterConstants.splitPipelineLogFileKey);\n                }\n            }\n\n        });\n    }\n\n    private boolean processTermin(boolean lastStatus, Long batchId, Long processId) throws InterruptedException {\n        int retry = 0;\n        SelectException exception = null;\n        TerminEventData terminData = null;\n        while (retry++ < 30) {\n            // 因为存在网络因素，而且在Load进行termin处理时，因为是异步处理，有一定的概率会出现termin不按顺序过来\n            terminData = arbitrateEventService.terminEvent().await(pipelineId);\n            Long terminBatchId = terminData.getBatchId();\n            Long terminProcessId = terminData.getProcessId();\n\n            if (terminBatchId == null && processId != -1L && !processId.equals(terminProcessId)) {\n                // 针对manager发起rollback，terminBatchId可能为null，需要特殊处理下\n                exception = new SelectException(\"unmatched processId, SelectTask batchId = \" + batchId\n                                                + \" processId = \" + processId + \" and Termin Event: \"\n                                                + terminData.toString());\n                Thread.sleep(1000); // sleep 1秒，等新的数据包\n            } else if (terminBatchId != null && batchId != -1L && !batchId.equals(terminBatchId)) {\n                exception = new SelectException(\"unmatched terminId, SelectTask batchId = \" + batchId + \" processId = \"\n                                                + processId + \" and Termin Event: \" + terminData.toString());\n                Thread.sleep(1000); // sleep 1秒，等新的数据包\n            } else {\n                exception = null; // batchId/processId对上了，退出\n                break;\n            }\n        }\n\n        if (exception != null) {\n            throw exception;\n        }\n\n        if (needCheck) {\n            checkContinueWork();\n        }\n\n        boolean status = terminData.getType().isNormal();\n        if (lastStatus == false && status == true) {\n            // 上一批失败，这一批成功，说明调度有问题\n            throw new SelectException(String.format(\"last status is rollback , but now [batchId:%d , processId:%d] is ack\",\n                batchId,\n                terminData.getProcessId()));\n        }\n\n        if (terminData.getType().isNormal()) {\n            ack(batchId);\n            sendDelayStat(pipelineId, terminData.getEndTime(), terminData.getFirstTime());\n        } else {\n            rollback(batchId);\n        }\n\n        arbitrateEventService.terminEvent().ack(terminData); // 先发送对应的数据\n        return status;\n    }\n\n    private void rollback(Long batchId) {\n        notifyRollback();\n        // otterSelector.rollback(batchId);\n        otterSelector.rollback();// 一旦出错，rollback所有的mark，避免拿出后面的数据进行同步\n    }\n\n    private void ack(Long batchId) {\n        canStartSelector.set(true);\n        otterSelector.ack(batchId);\n    }\n\n    private void notifyRollback() {\n        canStartSelector.set(false);\n        rversion.incrementAndGet();// 变更一下版本\n    }\n\n    /**\n     * 检查一下是否需要继续工作，因为mainstem代表一个节点是否为工作节点，一旦出现断网，另一个节点就会启用。此时会出现双节点同时工作，\n     * 所以需要做一个检查\n     */\n    private void checkContinueWork() throws InterruptedException {\n        boolean working = arbitrateEventService.mainStemEvent().check(pipelineId);\n        if (!working) {\n            logger.warn(\"mainstem is not run in this node\");\n            stopup(true);\n        }\n\n    }\n\n    public void shutdown() {\n        super.shutdown();\n\n        if (executor != null) {\n            executor.shutdownNow();\n        }\n\n        if (otterSelector != null && otterSelector.isStart()) {\n            otterSelector.stop();\n        }\n    }\n\n    public static class BatchTermin {\n\n        private Long    batchId   = -1L;\n        private Long    processId = -1L;\n        private boolean needWait  = true;\n\n        public BatchTermin(Long batchId, Long processId){\n            this(batchId, processId, true);\n        }\n\n        public BatchTermin(Long batchId, boolean needWait){\n            this(batchId, -1L, needWait);\n        }\n\n        public BatchTermin(Long batchId, Long processId, boolean needWait){\n            this.batchId = batchId;\n            this.processId = processId;\n            this.needWait = needWait;\n        }\n\n        public Long getBatchId() {\n            return batchId;\n        }\n\n        public void setBatchId(Long batchId) {\n            this.batchId = batchId;\n        }\n\n        public Long getProcessId() {\n            return processId;\n        }\n\n        public void setProcessId(Long processId) {\n            this.processId = processId;\n        }\n\n        public boolean isNeedWait() {\n            return needWait;\n        }\n\n        public void setNeedWait(boolean needWait) {\n            this.needWait = needWait;\n        }\n\n        @Override\n        public String toString() {\n            return \"BatchTermin [batchId=\" + batchId + \", needWait=\" + needWait + \", processId=\" + processId + \"]\";\n        }\n\n    }\n\n    private void sendDelayStat(long pipelineId, Long endTime, Long startTime) {\n        DelayCount delayCount = new DelayCount();\n        delayCount.setPipelineId(pipelineId);\n        delayCount.setNumber(0L);// 不再统计delayNumber\n        if (startTime != null && endTime != null) {\n            delayCount.setTime(endTime - startTime);// 以后改造成获取数据库的sysdate/now()\n        }\n\n        statisticsClientService.sendResetDelayCount(delayCount);\n    }\n\n    private void sendDelayReset(long pipelineId) {\n        long currentTime = System.currentTimeMillis();\n        if (currentTime - lastResetTime > 60 * 1000) {\n            // 60秒向manager推送一次配置\n            lastResetTime = currentTime;\n            DelayCount delayCount = new DelayCount();\n            delayCount.setPipelineId(pipelineId);\n            delayCount.setNumber(0L);\n            long delayTime = currentTime - otterSelector.lastEntryTime();\n            delayCount.setTime(delayTime);\n            statisticsClientService.sendResetDelayCount(delayCount);\n        }\n    }\n\n    // ======================= setter / getter ===================\n\n    public void setOtterSelectorFactory(OtterSelectorFactory otterSelectorFactory) {\n        this.otterSelectorFactory = otterSelectorFactory;\n    }\n\n    public void setStatisticsClientService(StatisticsClientService statisticsClientService) {\n        this.statisticsClientService = statisticsClientService;\n    }\n\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/select/exceptions/SelectException.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.select.exceptions;\n\n/**\n * SelectException for select module.\n * \n * @author xiaoqing.zhouxq\n */\npublic class SelectException extends RuntimeException {\n\n    /**\n     * \n     */\n    private static final long serialVersionUID = 1L;\n\n    public SelectException(String cause){\n        super(cause);\n    }\n\n    public SelectException(Throwable t){\n        super(t);\n    }\n\n    public SelectException(String cause, Throwable t){\n        super(cause, t);\n    }\n\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/select/selector/Message.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.select.selector;\n\nimport java.util.List;\n\n/**\n * 数据对象\n * \n * @author jianghang 2012-7-31 下午02:43:08\n */\npublic class Message<T> {\n\n    private Long    id;\n    private List<T> datas;\n\n    public Message(Long id, List<T> datas){\n        this.id = id;\n        this.datas = datas;\n    }\n\n    public Long getId() {\n        return id;\n    }\n\n    public void setId(Long id) {\n        this.id = id;\n    }\n\n    public List<T> getDatas() {\n        return datas;\n    }\n\n    public void setDatas(List<T> datas) {\n        this.datas = datas;\n    }\n\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/select/selector/MessageDumper.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.select.selector;\n\nimport java.text.MessageFormat;\nimport java.text.SimpleDateFormat;\nimport java.util.Date;\nimport java.util.List;\n\nimport org.apache.commons.lang.StringUtils;\nimport org.apache.commons.lang.SystemUtils;\nimport org.springframework.util.CollectionUtils;\n\nimport com.alibaba.otter.shared.etl.model.EventColumn;\nimport com.alibaba.otter.shared.etl.model.EventData;\n\n/**\n * dump记录\n * \n * @author jianghang 2011-11-9 下午03:52:26\n * @version 4.0.0\n */\npublic class MessageDumper {\n\n    private static final String SEP                    = SystemUtils.LINE_SEPARATOR;\n    private static final String TIMESTAMP_FORMAT       = \"yyyy-MM-dd HH:mm:ss:SSS\";\n    private static String       context_format         = null;\n    private static String       eventData_format       = null;\n    private static int          event_default_capacity = 1024;                      // 预设值StringBuilder，减少扩容影响\n\n    static {\n        context_format = \"* Batch Id: [{0}] ,total : [{1}] , normal : [{2}] , filter :[{3}] , Time : {4}\" + SEP;\n        context_format += \"* Start : [{5}] \" + SEP;\n        context_format += \"* End : [{6}] \" + SEP;\n\n        eventData_format = \"-----------------\" + SEP;\n        eventData_format += \"- TableId: {0} , Schema: {1} , Table: {2} \" + SEP;\n        eventData_format += \"- Type: {3}  , ExecuteTime: {4} , Remedy: {5}\" + SEP;\n        eventData_format += \"-----------------\" + SEP;\n        eventData_format += \"---START\" + SEP;\n        eventData_format += \"---Pks\" + SEP;\n        eventData_format += \"{6}\" + SEP;\n        eventData_format += \"---oldPks\" + SEP;\n        eventData_format += \"{7}\" + SEP;\n        eventData_format += \"---Columns\" + SEP;\n        eventData_format += \"{8}\" + SEP;\n        eventData_format += \"---END\" + SEP;\n\n    }\n\n    public static String dumpMessageInfo(Message<EventData> message, String startPosition, String endPosition, int total) {\n        Date now = new Date();\n        SimpleDateFormat format = new SimpleDateFormat(TIMESTAMP_FORMAT);\n        int normal = message.getDatas().size();\n        return MessageFormat.format(context_format, String.valueOf(message.getId()), total, normal, total - normal,\n                                    format.format(now), startPosition, endPosition);\n    }\n\n    public static String dumpEventDatas(List<EventData> eventDatas) {\n        if (CollectionUtils.isEmpty(eventDatas)) {\n            return StringUtils.EMPTY;\n        }\n\n        // 预先设定容量大小\n        StringBuilder builder = new StringBuilder(event_default_capacity * eventDatas.size());\n        for (EventData data : eventDatas) {\n            builder.append(dumpEventData(data));\n        }\n        return builder.toString();\n    }\n\n    public static String dumpEventData(EventData eventData) {\n        boolean remedy = eventData.isRemedy();\n        return MessageFormat.format(eventData_format, String.valueOf(eventData.getTableId()),\n                                    eventData.getSchemaName(), eventData.getTableName(),\n                                    eventData.getEventType().getValue(), String.valueOf(eventData.getExecuteTime()),\n                                    remedy, dumpEventColumn(eventData.getKeys()),\n                                    dumpEventColumn(eventData.getOldKeys()), dumpEventColumn(eventData.getColumns()),\n                                    \"\\t\" + eventData.getSql());\n    }\n\n    private static String dumpEventColumn(List<EventColumn> columns) {\n        StringBuilder builder = new StringBuilder(event_default_capacity);\n        int size = columns.size();\n        for (int i = 0; i < size; i++) {\n            EventColumn column = columns.get(i);\n            builder.append(\"\\t\").append(column.toString());\n            if (i < columns.size() - 1) {\n                builder.append(SEP);\n            }\n        }\n        return builder.toString();\n    }\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/select/selector/MessageParser.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.select.selector;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.Date;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.apache.commons.lang.StringUtils;\nimport org.apache.ddlutils.model.Table;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.util.CollectionUtils;\n\nimport com.alibaba.otter.canal.protocol.CanalEntry;\nimport com.alibaba.otter.canal.protocol.CanalEntry.Column;\nimport com.alibaba.otter.canal.protocol.CanalEntry.Entry;\nimport com.alibaba.otter.canal.protocol.CanalEntry.RowChange;\nimport com.alibaba.otter.canal.protocol.CanalEntry.RowData;\nimport com.alibaba.otter.node.common.config.ConfigClientService;\nimport com.alibaba.otter.node.etl.common.db.dialect.DbDialect;\nimport com.alibaba.otter.node.etl.common.db.dialect.DbDialectFactory;\nimport com.alibaba.otter.node.etl.common.db.dialect.mysql.MysqlDialect;\nimport com.alibaba.otter.node.etl.common.db.dialect.oracle.OracleDialect;\nimport com.alibaba.otter.node.etl.select.exceptions.SelectException;\nimport com.alibaba.otter.node.etl.transform.exception.TransformException;\nimport com.alibaba.otter.shared.common.model.config.ConfigHelper;\nimport com.alibaba.otter.shared.common.model.config.channel.ChannelParameter.SyncConsistency;\nimport com.alibaba.otter.shared.common.model.config.data.DataMedia;\nimport com.alibaba.otter.shared.common.model.config.data.DataMedia.ModeValue;\nimport com.alibaba.otter.shared.common.model.config.data.DataMediaPair;\nimport com.alibaba.otter.shared.common.model.config.data.DataMediaSource;\nimport com.alibaba.otter.shared.common.model.config.data.db.DbMediaSource;\nimport com.alibaba.otter.shared.common.model.config.pipeline.Pipeline;\nimport com.alibaba.otter.shared.common.model.config.pipeline.PipelineParameter;\nimport com.alibaba.otter.shared.etl.model.EventColumn;\nimport com.alibaba.otter.shared.etl.model.EventColumnIndexComparable;\nimport com.alibaba.otter.shared.etl.model.EventData;\nimport com.alibaba.otter.shared.etl.model.EventType;\n\n/**\n * 数据对象解析\n * \n * @author jianghang 2012-10-25 下午02:31:06\n * @version 4.1.2\n */\npublic class MessageParser {\n\n    private static final Logger logger                         = LoggerFactory.getLogger(MessageParser.class);\n    private ConfigClientService configClientService;\n    private DbDialectFactory    dbDialectFactory;\n    private static final String RETL_CLIENT_FLAG               = \"_SYNC\";\n    private static final String compatibleMarkTable            = \"retl_client\";\n    private static final String compatibleMarkInfoColumn       = \"client_info\";\n    private static final String compatibleMarkIdentifierColumn = \"client_identifier\";\n\n    /**\n     * 将对应canal送出来的Entry对象解析为otter使用的内部对象\n     * \n     * <pre>\n     * 需要处理数据过滤：\n     * 1. Transaction Begin/End过滤\n     * 2. retl.retl_client/retl.retl_mark 回环标记处理以及后续的回环数据过滤\n     * 3. retl.xdual canal心跳表数据过滤\n     * </pre>\n     */\n    public List<EventData> parse(Long pipelineId, List<Entry> datas) throws SelectException {\n        List<EventData> eventDatas = new ArrayList<EventData>();\n        Pipeline pipeline = configClientService.findPipeline(pipelineId);\n        List<Entry> transactionDataBuffer = new ArrayList<Entry>();\n        // hz为主站点，us->hz的数据，需要回环同步会us。并且需要开启回环补救算法\n        PipelineParameter pipelineParameter = pipeline.getParameters();\n        boolean enableLoopbackRemedy = pipelineParameter.isEnableRemedy() && pipelineParameter.isHome()\n                                       && pipelineParameter.getRemedyAlgorithm().isLoopback();\n        boolean isLoopback = false;\n        boolean needLoopback = false; // 判断是否属于需要loopback处理的类型，只处理正常otter同步产生的回环数据，因为会有业务方手工屏蔽同步的接口，避免回环\n\n        long now = new Date().getTime();\n        try {\n            for (Entry entry : datas) {\n                switch (entry.getEntryType()) {\n                    case TRANSACTIONBEGIN:\n                        isLoopback = false;\n                        break;\n                    case ROWDATA:\n                        String tableName = entry.getHeader().getTableName();\n                        // 判断是否是回环表retl_mark\n                        boolean isMarkTable = tableName.equalsIgnoreCase(pipeline.getParameters().getSystemMarkTable());\n                        if (isMarkTable) {\n                            RowChange rowChange = RowChange.parseFrom(entry.getStoreValue());\n                            if (!rowChange.getIsDdl()) {\n                                int loopback = 0;\n                                if (rowChange.getRowDatasCount() > 0) {\n                                    loopback = checkLoopback(pipeline, rowChange.getRowDatas(0));\n                                }\n                                if (loopback == 2) {\n                                    needLoopback |= true; // 只处理正常同步产生的回环数据\n                                }\n\n                                isLoopback |= loopback > 0;\n                            }\n                        }\n\n                        // 检查下otter3.0的回环表，对应的schmea会比较随意，所以不做比较\n                        boolean isCompatibleLoopback = tableName.equalsIgnoreCase(compatibleMarkTable);\n\n                        if (isCompatibleLoopback) {\n                            RowChange rowChange = RowChange.parseFrom(entry.getStoreValue());\n                            if (!rowChange.getIsDdl()) {\n                                int loopback = 0;\n                                if (rowChange.getRowDatasCount() > 0) {\n                                    loopback = checkCompatibleLoopback(pipeline, rowChange.getRowDatas(0));\n                                }\n                                if (loopback == 2) {\n                                    needLoopback |= true; // 只处理正常同步产生的回环数据\n                                }\n                                isLoopback |= loopback > 0;\n                            }\n                        }\n\n                        if ((!isLoopback || (enableLoopbackRemedy && needLoopback)) && !isMarkTable\n                            && !isCompatibleLoopback) {\n                            transactionDataBuffer.add(entry);\n                        }\n                        break;\n                    case TRANSACTIONEND:\n                        if (!isLoopback || (enableLoopbackRemedy && needLoopback)) {\n                            // 添加数据解析\n                            for (Entry bufferEntry : transactionDataBuffer) {\n                                List<EventData> parseDatas = internParse(pipeline, bufferEntry);\n                                if (CollectionUtils.isEmpty(parseDatas)) {// 可能为空，针对ddl返回时就为null\n                                    continue;\n                                }\n\n                                // 初步计算一下事件大小\n                                long totalSize = bufferEntry.getHeader().getEventLength();\n                                long eachSize = totalSize / parseDatas.size();\n                                for (EventData eventData : parseDatas) {\n                                    if (eventData == null) {\n                                        continue;\n                                    }\n\n                                    eventData.setSize(eachSize);// 记录一下大小\n                                    if (needLoopback) {// 针对需要回环同步的\n                                        // 如果延迟超过指定的阀值，则设置为需要反查db\n                                        if (now - eventData.getExecuteTime() > 1000 * pipeline.getParameters()\n                                            .getRemedyDelayThresoldForMedia()) {\n                                            eventData.setSyncConsistency(SyncConsistency.MEDIA);\n                                        } else {\n                                            eventData.setSyncConsistency(SyncConsistency.BASE);\n                                        }\n                                        eventData.setRemedy(true);\n                                    }\n                                    eventDatas.add(eventData);\n                                }\n                            }\n                        }\n\n                        isLoopback = false;\n                        needLoopback = false;\n                        transactionDataBuffer.clear();\n                        break;\n                    default:\n                        break;\n                }\n            }\n\n            // 添加最后一次的数据，可能没有TRANSACTIONEND\n            if (!isLoopback || (enableLoopbackRemedy && needLoopback)) {\n                // 添加数据解析\n                for (Entry bufferEntry : transactionDataBuffer) {\n                    List<EventData> parseDatas = internParse(pipeline, bufferEntry);\n                    if (CollectionUtils.isEmpty(parseDatas)) {// 可能为空，针对ddl返回时就为null\n                        continue;\n                    }\n\n                    // 初步计算一下事件大小\n                    long totalSize = bufferEntry.getHeader().getEventLength();\n                    long eachSize = totalSize / parseDatas.size();\n                    for (EventData eventData : parseDatas) {\n                        if (eventData == null) {\n                            continue;\n                        }\n\n                        eventData.setSize(eachSize);// 记录一下大小\n                        if (needLoopback) {// 针对需要回环同步的\n                            // 如果延迟超过指定的阀值，则设置为需要反查db\n                            if (now - eventData.getExecuteTime() > 1000 * pipeline.getParameters()\n                                .getRemedyDelayThresoldForMedia()) {\n                                eventData.setSyncConsistency(SyncConsistency.MEDIA);\n                            } else {\n                                eventData.setSyncConsistency(SyncConsistency.BASE);\n                            }\n                        }\n                        eventDatas.add(eventData);\n                    }\n                }\n            }\n        } catch (Exception e) {\n            throw new SelectException(e);\n        }\n\n        return eventDatas;\n    }\n\n    /**\n     * <pre>\n     * the table def: \n     *              channel_info varchar\n     *              channel_id varchar\n     * 每次解析时，每个事务首先获取 retl_mark 下的 channel_info 或 channel_id 字段变更。\n     *  a. 如果存在 channel_info 以 '_SYNC'结尾的字符串 ，则忽略本次事务的数据变更；\n     *  b. 如果不等于，则执行下面的判断。\n     *      i. 如果存在channel_id = \"xx\"，则检查对应的channel_id是否为当前同步的channelId，如果是则忽略。\n     *      ii. 不存在则不处理\n     * </pre>\n     */\n    private int checkLoopback(Pipeline pipeline, RowData rowData) {\n        // 检查channel_info字段\n        // 首先检查下after记录，从无变有的过程，一般出现在事务头\n        Column infokColumn = getColumnIgnoreCase(rowData.getAfterColumnsList(), pipeline.getParameters()\n            .getSystemMarkTableInfo());\n\n        // 匹配对应的channelInfo，如果以_SYNC结尾，则认为需要忽略\n        if (infokColumn != null && StringUtils.endsWithIgnoreCase(infokColumn.getValue(), RETL_CLIENT_FLAG)) {\n            return 1;\n        }\n\n        // 匹配对应的channelInfo，如果相同，则认为需要忽略，并返回2，代表需要进行回环补救check机制，因为这个变更也是otter系统产生的\n        if (infokColumn != null\n            && StringUtils.equalsIgnoreCase(infokColumn.getValue(), pipeline.getParameters().getChannelInfo())) {\n            return 2;\n        }\n\n        infokColumn = getColumnIgnoreCase(rowData.getBeforeColumnsList(), pipeline.getParameters()\n            .getSystemMarkTableInfo());\n        // 匹配对应的channelInfo，如果以_SYNC结尾，则认为需要忽略\n        if (infokColumn != null && StringUtils.endsWithIgnoreCase(infokColumn.getValue(), RETL_CLIENT_FLAG)) {\n            return 1;\n        }\n\n        // 匹配对应的channelInfo，如果相同，则认为需要忽略，并返回2，代表需要进行回环补救check机制，因为这个变更也是otter系统产生的\n        if (infokColumn != null\n            && StringUtils.equalsIgnoreCase(infokColumn.getValue(), pipeline.getParameters().getChannelInfo())) {\n            return 2;\n        }\n\n        // 检查channel_id字段\n        Column markColumn = getColumnIgnoreCase(rowData.getAfterColumnsList(), pipeline.getParameters()\n            .getSystemMarkTableColumn());\n        // 匹配对应的channel id\n        if (markColumn != null && pipeline.getChannelId().equals(Long.parseLong(markColumn.getValue()))) {\n            return 2;\n        }\n\n        markColumn = getColumnIgnoreCase(rowData.getBeforeColumnsList(), pipeline.getParameters()\n            .getSystemMarkTableColumn());\n        if (markColumn != null && pipeline.getChannelId().equals(Long.parseLong(markColumn.getValue()))) {\n            return 2;\n        }\n\n        return 0;\n    }\n\n    /**\n     * 检查otter3.0的兼容表处理，主要部分业务使用了3.0的功能，需要考虑兼容支持，后续可删除\n     */\n    private int checkCompatibleLoopback(Pipeline pipeline, RowData rowData) {\n        // 检查_info字段\n        // 首先检查下after记录，从无变有的过程，一般出现在事务头\n        Column infokColumn = getColumnIgnoreCase(rowData.getAfterColumnsList(), compatibleMarkInfoColumn);\n        // 匹配对应的channel id\n        if (infokColumn != null && infokColumn.getValue().toUpperCase().endsWith(RETL_CLIENT_FLAG)) {\n            return 1;\n        }\n\n        infokColumn = getColumnIgnoreCase(rowData.getBeforeColumnsList(), compatibleMarkInfoColumn);\n        if (infokColumn != null && infokColumn.getValue().toUpperCase().endsWith(RETL_CLIENT_FLAG)) {\n            return 1;\n        }\n\n        // 检查_id字段\n        Column markColumn = getColumnIgnoreCase(rowData.getAfterColumnsList(), compatibleMarkIdentifierColumn);\n        // 匹配对应的channel id\n        if (markColumn != null && pipeline.getChannelId().equals(Long.parseLong(markColumn.getValue()))) {\n            return 2;\n        }\n\n        markColumn = getColumnIgnoreCase(rowData.getBeforeColumnsList(), compatibleMarkIdentifierColumn);\n        if (markColumn != null && pipeline.getChannelId().equals(Long.parseLong(markColumn.getValue()))) {\n            return 2;\n        }\n\n        return 0;\n    }\n\n    private Column getColumnIgnoreCase(List<Column> columns, String columName) {\n        for (Column column : columns) {\n            if (column.getName().equalsIgnoreCase(columName)) {\n                return column;\n            }\n        }\n\n        return null;\n    }\n\n    private List<EventData> internParse(Pipeline pipeline, Entry entry) {\n        RowChange rowChange = null;\n        try {\n            rowChange = RowChange.parseFrom(entry.getStoreValue());\n        } catch (Exception e) {\n            throw new SelectException(\"parser of canal-event has an error , data:\" + entry.toString(), e);\n        }\n\n        if (rowChange == null) {\n            return null;\n        }\n\n        String schemaName = entry.getHeader().getSchemaName();\n        String tableName = entry.getHeader().getTableName();\n        EventType eventType = EventType.fromName(rowChange.getEventType().name());\n\n        if (eventType == null) {\n            logger.warn(\"Discard unsupported event type: {}\", rowChange.getEventType());\n            return null;\n        }\n\n        // 处理下DDL操作\n        if (eventType.isQuery()) {\n            // 直接忽略query事件\n            return null;\n        }\n\n        // 首先判断是否为系统表\n        if (StringUtils.equalsIgnoreCase(pipeline.getParameters().getSystemSchema(), schemaName)) {\n            // do noting\n            if (eventType.isDdl()) {\n                return null;\n            }\n\n            if (StringUtils.equalsIgnoreCase(pipeline.getParameters().getSystemDualTable(), tableName)) {\n                // 心跳表数据直接忽略\n                return null;\n            }\n        } else {\n            if (eventType.isDdl()) {\n                boolean notExistReturnNull = false;\n                if (eventType.isRename()) {\n                    notExistReturnNull = true;\n                }\n\n                DataMedia dataMedia = ConfigHelper.findSourceDataMedia(pipeline,\n                    schemaName,\n                    tableName,\n                    notExistReturnNull);\n                // 如果EventType是CREATE/ALTER，需要reload\n                // DataMediaInfo;并且把CREATE/ALTER类型的事件丢弃掉.\n                if (dataMedia != null && (eventType.isCreate() || eventType.isAlter() || eventType.isRename())) {\n                    DbDialect dbDialect = dbDialectFactory.getDbDialect(pipeline.getId(),\n                        (DbMediaSource) dataMedia.getSource());\n                    dbDialect.reloadTable(schemaName, tableName);// 更新下meta信息\n                }\n\n                boolean ddlSync = pipeline.getParameters().getDdlSync();\n                if (ddlSync) {\n                    // 处理下ddl操作\n                    EventData eventData = new EventData();\n                    eventData.setSchemaName(schemaName);\n                    eventData.setTableName(tableName);\n                    eventData.setEventType(eventType);\n                    eventData.setExecuteTime(entry.getHeader().getExecuteTime());\n                    eventData.setSql(rowChange.getSql());\n                    eventData.setDdlSchemaName(rowChange.getDdlSchemaName());\n                    eventData.setTableId(dataMedia.getId());\n                    return Arrays.asList(eventData);\n                } else {\n                    return null;\n                }\n            }\n        }\n\n        List<EventData> eventDatas = new ArrayList<EventData>();\n        for (RowData rowData : rowChange.getRowDatasList()) {\n            EventData eventData = internParse(pipeline, entry, rowChange, rowData);\n            if (eventData != null) {\n                eventDatas.add(eventData);\n            }\n        }\n\n        return eventDatas;\n    }\n\n    /**\n     * 解析出从canal中获取的Event事件<br>\n     * Oracle:有变更的列值. <br>\n     * <i>insert:从afterColumns中获取所有的变更数据<br>\n     * <i>delete:从beforeColumns中获取所有的变更数据<br>\n     * <i>update:在before中存放所有的主键和变化前的非主键值，在after中存放变化后的主键和非主键值,如果是复合主键，只会存放变化的主键<br>\n     * Mysql:可以得到所有变更前和变更后的数据.<br>\n     * <i>insert:从afterColumns中获取所有的变更数据<br>\n     * <i>delete:从beforeColumns中获取所有的变更数据<br>\n     * <i>update:在beforeColumns中存放变更前的所有数据,在afterColumns中存放变更后的所有数据<br>\n     */\n    private EventData internParse(Pipeline pipeline, Entry entry, RowChange rowChange, RowData rowData) {\n        EventData eventData = new EventData();\n        eventData.setTableName(entry.getHeader().getTableName());\n        eventData.setSchemaName(entry.getHeader().getSchemaName());\n        eventData.setEventType(EventType.valueOf(rowChange.getEventType().name()));\n        eventData.setExecuteTime(entry.getHeader().getExecuteTime());\n        EventType eventType = eventData.getEventType();\n        TableInfoHolder tableHolder = null;\n\n        if (!StringUtils.equalsIgnoreCase(pipeline.getParameters().getSystemSchema(), eventData.getSchemaName())) {\n            boolean useTableTransform = pipeline.getParameters().getUseTableTransform();\n            Table table = null;\n            DataMediaPair dataMediaPair = ConfigHelper.findDataMediaPairBySourceName(pipeline,\n                eventData.getSchemaName(),\n                eventData.getTableName());\n            DataMedia dataMedia = dataMediaPair.getSource();\n            eventData.setTableId(dataMedia.getId());\n            // 获取目标表\n            DataMedia targetDataMedia = dataMediaPair.getTarget();\n            if (useTableTransform || dataMedia.getSource().getType().isOracle()) {// oracle需要反查一次meta\n                // 如果设置了需要进行table meta转化，则反查一下table信息\n                // 比如oracle erosa解析时可能使用了非物理主键，需要直接使用，信任erosa的信息\n                DbDialect dbDialect = dbDialectFactory.getDbDialect(pipeline.getId(),\n                    (DbMediaSource) dataMedia.getSource());\n                table = dbDialect.findTable(eventData.getSchemaName(), eventData.getTableName());// 查询一下meta信息\n                if (table == null) {\n                    logger.warn(\"find table[{}.{}] is null , may be drop table.\",\n                        eventData.getSchemaName(),\n                        eventData.getTableName());\n                }\n                // 获取一下目标库的拆分字段,设置源表为主键\n                // 首先要求源和目标的库名表名是一致的\n                DataMediaSource targetSource = targetDataMedia.getSource();\n                if (targetSource instanceof DbMediaSource\n                    && StringUtils.containsIgnoreCase(((DbMediaSource) targetSource).getUrl(), \"drds\")) {\n                    // 优先判断是否为drds\n                    DbDialect targetDbDialect = dbDialectFactory.getDbDialect(pipeline.getId(),\n                        (DbMediaSource) targetDataMedia.getSource());\n                    if (targetDbDialect.isDRDS()) {\n                        String schemaName = buildName(eventData.getSchemaName(),\n                            dataMedia.getNamespaceMode(),\n                            targetDataMedia.getNamespaceMode());\n                        String tableName = buildName(eventData.getSchemaName(),\n                            dataMedia.getNameMode(),\n                            targetDataMedia.getNameMode());\n                        String shardColumns = targetDbDialect.getShardColumns(schemaName, tableName);\n                        if (StringUtils.isNotEmpty(shardColumns)) {\n                            String columns[] = StringUtils.split(shardColumns, ',');\n                            for (String key : columns) {\n                                org.apache.ddlutils.model.Column col = table.findColumn(key, false);\n                                if (col != null) {\n                                    col.setPrimaryKey(true);\n                                } else {\n                                    logger.warn(String.format(\"shardColumn %s in table[%s.%s] is not found\",\n                                        key,\n                                        eventData.getSchemaName(),\n                                        eventData.getTableName()));\n                                }\n                            }\n                        }\n                    }\n                }\n                tableHolder = new TableInfoHolder(dbDialect, table, useTableTransform);\n            }\n        }\n\n        List<Column> beforeColumns = rowData.getBeforeColumnsList();\n        List<Column> afterColumns = rowData.getAfterColumnsList();\n        String tableName = eventData.getSchemaName() + \".\" + eventData.getTableName();\n\n        // 判断一下是否需要all columns\n        boolean isRowMode = pipeline.getParameters().getSyncMode().isRow(); // 如果是rowMode模式，所有字段都需要标记为updated\n        boolean needAllColumns = isRowMode || checkNeedAllColumns(pipeline);\n\n        // 变更后的主键\n        Map<String, EventColumn> keyColumns = new LinkedHashMap<String, EventColumn>();\n        // 变更前的主键\n        Map<String, EventColumn> oldKeyColumns = new LinkedHashMap<String, EventColumn>();\n        // 有变化的非主键\n        Map<String, EventColumn> notKeyColumns = new LinkedHashMap<String, EventColumn>();\n\n        if (eventType.isInsert()) {\n            for (Column column : afterColumns) {\n                if (isKey(tableHolder, tableName, column)) {\n                    keyColumns.put(column.getName(), copyEventColumn(column, true, tableHolder));\n                } else {\n                    // mysql 有效\n                    notKeyColumns.put(column.getName(), copyEventColumn(column, true, tableHolder));\n                }\n            }\n        } else if (eventType.isDelete()) {\n            for (Column column : beforeColumns) {\n                if (isKey(tableHolder, tableName, column)) {\n                    keyColumns.put(column.getName(), copyEventColumn(column, true, tableHolder));\n                } else {\n                    // mysql 有效\n                    notKeyColumns.put(column.getName(), copyEventColumn(column, true, tableHolder));\n                }\n            }\n        } else if (eventType.isUpdate()) {\n            // 获取变更前的主键.\n            for (Column column : beforeColumns) {\n                if (isKey(tableHolder, tableName, column)) {\n                    oldKeyColumns.put(column.getName(), copyEventColumn(column, true, tableHolder));\n                    // 同时记录一下new\n                    // key,因为mysql5.6之后出现了minimal模式,after里会没有主键信息,需要在before记录中找\n                    keyColumns.put(column.getName(), copyEventColumn(column, true, tableHolder));\n                } else {\n                    if (needAllColumns && entry.getHeader().getSourceType() == CanalEntry.Type.ORACLE) {\n                        // 针对行记录同步时，针对oracle记录一下非主键的字段，因为update时针对未变更的字段在aftercolume里没有\n                        notKeyColumns.put(column.getName(), copyEventColumn(column, isRowMode, tableHolder));\n                    }\n                }\n            }\n            for (Column column : afterColumns) {\n                if (isKey(tableHolder, tableName, column)) {\n                    // 获取变更后的主键\n                    keyColumns.put(column.getName(), copyEventColumn(column, true, tableHolder));\n                } else if (needAllColumns || entry.getHeader().getSourceType() == CanalEntry.Type.ORACLE\n                           || column.getUpdated()) {\n                    // 在update操作时，oracle和mysql存放变更的非主键值的方式不同,oracle只有变更的字段;\n                    // mysql会把变更前和变更后的字段都发出来，只需要取有变更的字段.\n                    // 如果是oracle库，after里一定为对应的变更字段\n\n                    boolean isUpdate = true;\n                    if (entry.getHeader().getSourceType() == CanalEntry.Type.MYSQL) { // mysql的after里部分数据为未变更,oracle里after里为变更字段\n                        isUpdate = column.getUpdated();\n                    }\n\n                    notKeyColumns.put(column.getName(), copyEventColumn(column, isRowMode || isUpdate, tableHolder));// 如果是rowMode，所有字段都为updated\n                }\n            }\n\n            if (entry.getHeader().getSourceType() == CanalEntry.Type.ORACLE) { // 针对oracle进行特殊处理\n                checkUpdateKeyColumns(oldKeyColumns, keyColumns);\n            }\n        }\n\n        List<EventColumn> keys = new ArrayList<EventColumn>(keyColumns.values());\n        List<EventColumn> oldKeys = new ArrayList<EventColumn>(oldKeyColumns.values());\n        List<EventColumn> columns = new ArrayList<EventColumn>(notKeyColumns.values());\n\n        Collections.sort(keys, new EventColumnIndexComparable());\n        Collections.sort(oldKeys, new EventColumnIndexComparable());\n        Collections.sort(columns, new EventColumnIndexComparable());\n        if (!keyColumns.isEmpty()) {\n            eventData.setKeys(keys);\n            if (eventData.getEventType().isUpdate() && !oldKeys.equals(keys)) { // update类型，如果存在主键不同,则记录下old\n                                                                                // keys为变更前的主键\n                eventData.setOldKeys(oldKeys);\n            }\n            eventData.setColumns(columns);\n            // } else if (CanalEntry.Type.MYSQL ==\n            // entry.getHeader().getSourceType()) {\n            // // 只支持mysql无主键同步\n            // if (eventType.isUpdate()) {\n            // List<EventColumn> oldColumns = new ArrayList<EventColumn>();\n            // List<EventColumn> newColumns = new ArrayList<EventColumn>();\n            // for (Column column : beforeColumns) {\n            // oldColumns.add(copyEventColumn(column, true, tableHolder));\n            // }\n            //\n            // for (Column column : afterColumns) {\n            // newColumns.add(copyEventColumn(column, true, tableHolder));\n            // }\n            // Collections.sort(oldColumns, new EventColumnIndexComparable());\n            // Collections.sort(newColumns, new EventColumnIndexComparable());\n            // eventData.setOldKeys(oldColumns);// 做为老主键\n            // eventData.setKeys(newColumns);// 做为新主键，需要保证新老主键字段数量一致\n            // } else {\n            // // 针对无主键，等同为所有都是主键进行处理\n            // eventData.setKeys(columns);\n            // }\n        } else {\n            throw new SelectException(\"this rowdata has no pks , entry: \" + entry.toString() + \" and rowData: \"\n                                      + rowData);\n        }\n\n        return eventData;\n    }\n\n    private boolean checkNeedAllColumns(Pipeline pipeline) {\n        boolean needAllColumns = false;\n        // 只要有filter/resolver逻辑存在，就需要尽可能保留所有字段\n        for (DataMediaPair pair : pipeline.getPairs()) {\n            needAllColumns |= pair.isExistFilter();\n            if (pair.getResolverData() != null && pair.getResolverData().getExtensionDataType() != null) {\n                if (pair.getResolverData().getExtensionDataType().isClazz()) {\n                    needAllColumns |= StringUtils.isNotEmpty(pair.getResolverData().getClazzPath());\n                } else {\n                    needAllColumns |= StringUtils.isNotEmpty(pair.getResolverData().getSourceText());\n                }\n            } else {\n                needAllColumns |= Boolean.FALSE;\n            }\n\n        }\n        return needAllColumns;\n    }\n\n    /**\n     * 在oracle中，补充没有变更的主键<br>\n     * 如果变更后的主键为空，直接从old中拷贝<br>\n     * 如果变更前后的主键数目不相等，把old中存在而new中不存在的主键拷贝到new中.\n     * \n     * @param oldKeys\n     * @param newKeys\n     */\n    private void checkUpdateKeyColumns(Map<String, EventColumn> oldKeyColumns, Map<String, EventColumn> keyColumns) {\n        // 在变更前没有主键的情况\n        if (oldKeyColumns.size() == 0) {\n            return;\n        }\n        // 变更后的主键数据大于变更前的，不符合\n        if (keyColumns.size() > oldKeyColumns.size()) {\n            return;\n        }\n        // 主键没有变更，把所有变更前的主键拷贝到变更后的主键中.\n        if (keyColumns.size() == 0) {\n            keyColumns.putAll(oldKeyColumns);\n            return;\n        }\n\n        // 把old中存在而new中不存在的主键拷贝到new中\n        if (oldKeyColumns.size() != keyColumns.size()) {\n            for (String oldKey : oldKeyColumns.keySet()) {\n                if (keyColumns.get(oldKey) == null) {\n                    keyColumns.put(oldKey, oldKeyColumns.get(oldKey));\n                }\n            }\n        }\n    }\n\n    /**\n     * 把 erosa-protocol's Column 转化成 otter's model EventColumn.\n     * \n     * @param column\n     * @return\n     */\n    private EventColumn copyEventColumn(Column column, boolean isUpdate, TableInfoHolder tableHolder) {\n        EventColumn eventColumn = new EventColumn();\n        eventColumn.setIndex(column.getIndex());\n        eventColumn.setKey(column.getIsKey());\n        eventColumn.setNull(column.getIsNull());\n        eventColumn.setColumnName(column.getName());\n        eventColumn.setColumnValue(column.getValue());\n        eventColumn.setUpdate(isUpdate);\n        eventColumn.setColumnType(column.getSqlType());\n\n        if (tableHolder != null && tableHolder.getTable() != null\n            && (tableHolder.isUseTableTransform() || tableHolder.isOracle())) {\n            org.apache.ddlutils.model.Column dbColumn = tableHolder.getTable().findColumn(column.getName(), false);\n            if (dbColumn == null) {\n                // 可能存在ddl，重新reload一下table\n                tableHolder.reload();\n                dbColumn = tableHolder.getTable().findColumn(column.getName(), false);\n            }\n\n            if (dbColumn != null) {\n                int sqlType = dbColumn.getTypeCode();\n                if (sqlType != column.getSqlType()) {\n                    // 针对oracle的erosa给出的字段为非标准的jdbc，需要做一次类型反查\n                    eventColumn.setColumnType(sqlType);\n                    logger.info(\"table [{}] column [{}] is not match , MeType: {}, EType {}\", new Object[] {\n                            tableHolder.getTable().getName(), column.getName(), sqlType, column.getSqlType() });\n                }\n            }\n        }\n\n        return eventColumn;\n    }\n\n    private boolean isKey(TableInfoHolder tableHolder, String tableName, Column column) {\n        boolean isEKey = column.getIsKey();\n        if (tableHolder == null || tableHolder.getTable() == null || !tableHolder.isUseTableTransform()) {\n            return isEKey;\n        }\n\n        org.apache.ddlutils.model.Column dbColumn = tableHolder.getTable().findColumn(column.getName(), false);\n        if (dbColumn == null) {\n            // 可能存在ddl，重新reload一下table\n            tableHolder.reload();\n            dbColumn = tableHolder.getTable().findColumn(column.getName(), false);\n            if (dbColumn == null) {\n                throw new SelectException(String.format(\"not found column[%s] in table[%s]\",\n                    column.getName(),\n                    tableHolder.getTable().toVerboseString()));\n            }\n        }\n\n        boolean isMKey = dbColumn.isPrimaryKey();\n        if (isMKey != isEKey) {\n            logger.info(\"table [{}] column [{}] is not match , isMeky: {}, isEkey {}\",\n                new Object[] { tableName, column.getName(), isMKey, isEKey });\n        }\n        return isMKey;\n    }\n\n    private String buildName(String name, ModeValue sourceModeValue, ModeValue targetModeValue) {\n        if (targetModeValue.getMode().isWildCard()) {\n            return name; // 通配符，认为源和目标一定是一致的\n        } else if (targetModeValue.getMode().isMulti()) {\n            int index = ConfigHelper.indexIgnoreCase(sourceModeValue.getMultiValue(), name);\n            if (index == -1) {\n                throw new TransformException(\"can not found namespace or name in media:\" + sourceModeValue.toString());\n            }\n\n            return targetModeValue.getMultiValue().get(index);\n        } else {\n            return targetModeValue.getSingleValue();\n        }\n    }\n\n    // ======================== setter / getter =============================\n\n    public void setDbDialectFactory(DbDialectFactory dbDialectFactory) {\n        this.dbDialectFactory = dbDialectFactory;\n    }\n\n    public void setConfigClientService(ConfigClientService configClientService) {\n        this.configClientService = configClientService;\n    }\n\n    /**\n     * 实现可reload的table meta，可替换table属性.\n     * \n     * @author jianghang 2012-5-16 下午04:34:18\n     * @version 4.0.2\n     */\n    static class TableInfoHolder {\n\n        private DbDialect dbDialect;\n        private Table     table;\n        private boolean   useTableTransform;\n\n        public TableInfoHolder(DbDialect dbDialect, Table table, boolean useTableTransform){\n            this.dbDialect = dbDialect;\n            this.table = table;\n            this.useTableTransform = useTableTransform;\n        }\n\n        public Table getTable() {\n            return table;\n        }\n\n        public void setTable(Table table) {\n            this.table = table;\n        }\n\n        public DbDialect getDbDialect() {\n            return dbDialect;\n        }\n\n        public void setDbDialect(DbDialect dbDialect) {\n            this.dbDialect = dbDialect;\n        }\n\n        public boolean isUseTableTransform() {\n            return useTableTransform;\n        }\n\n        public void setUseTableTransform(boolean useTableTransform) {\n            this.useTableTransform = useTableTransform;\n        }\n\n        public boolean isOracle() {\n            return (dbDialect != null && dbDialect instanceof OracleDialect);\n        }\n\n        public boolean isMysql() {\n            return (dbDialect != null && dbDialect instanceof MysqlDialect);\n        }\n\n        public void reload() {\n            if (table != null) {\n                String schemaName = StringUtils.isEmpty(table.getCatalog()) ? table.getSchema() : table.getCatalog();\n                this.table = dbDialect.findTable(schemaName, table.getName(), false);\n            }\n        }\n\n    }\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/select/selector/OtterSelector.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.select.selector;\n\nimport java.util.List;\n\n/**\n * otter同步数据获取\n * \n * @author jianghang 2012-7-31 下午02:30:33\n */\npublic interface OtterSelector<T> {\n\n    /**\n     * 启动\n     */\n    public void start();\n\n    /**\n     * 是否启动\n     */\n    public boolean isStart();\n\n    /**\n     * 关闭\n     */\n    public void stop();\n\n    /**\n     * 获取一批待处理的数据\n     */\n    public Message<T> selector() throws InterruptedException;\n\n    /**\n     * 返回未被ack的数据\n     */\n    public List<Long> unAckBatchs();\n\n    /**\n     * 反馈一批数据处理失败，需要下次重新被处理\n     */\n    public void rollback(Long batchId);\n\n    /**\n     * 反馈所有的batch数据需要被重新处理\n     */\n    public void rollback();\n\n    /**\n     * 反馈一批数据处理完成\n     */\n    public void ack(Long batchId);\n\n    /**\n     * 返回最后一次entry数据的时间戳\n     */\n    public Long lastEntryTime();\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/select/selector/OtterSelectorFactory.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.select.selector;\n\nimport com.alibaba.otter.node.etl.OtterContextLocator;\nimport com.alibaba.otter.node.etl.select.selector.canal.CanalEmbedSelector;\n\n/**\n * 获取对应的selector\n * \n * @author jianghang 2012-8-1 上午10:25:06\n * @version 4.1.0\n */\npublic class OtterSelectorFactory {\n\n    public OtterSelector getSelector(Long pipelineId) {\n        CanalEmbedSelector selector = new CanalEmbedSelector(pipelineId);\n        OtterContextLocator.autowire(selector);\n        return selector;\n    }\n\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/select/selector/canal/CanalEmbedSelector.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.select.selector.canal;\n\nimport java.text.SimpleDateFormat;\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.List;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.locks.LockSupport;\n\nimport org.apache.commons.lang.SystemUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.slf4j.MDC;\nimport org.springframework.util.CollectionUtils;\n\nimport com.alibaba.otter.canal.common.CanalException;\nimport com.alibaba.otter.canal.extend.communication.CanalConfigClient;\nimport com.alibaba.otter.canal.extend.ha.MediaHAController;\nimport com.alibaba.otter.canal.instance.core.CanalInstance;\nimport com.alibaba.otter.canal.instance.core.CanalInstanceGenerator;\nimport com.alibaba.otter.canal.instance.manager.CanalInstanceWithManager;\nimport com.alibaba.otter.canal.instance.manager.model.Canal;\nimport com.alibaba.otter.canal.instance.manager.model.CanalParameter.HAMode;\nimport com.alibaba.otter.canal.parse.CanalEventParser;\nimport com.alibaba.otter.canal.parse.ha.CanalHAController;\nimport com.alibaba.otter.canal.parse.inbound.mysql.MysqlEventParser;\nimport com.alibaba.otter.canal.parse.support.AuthenticationInfo;\nimport com.alibaba.otter.canal.protocol.CanalEntry;\nimport com.alibaba.otter.canal.protocol.CanalEntry.Entry;\nimport com.alibaba.otter.canal.protocol.ClientIdentity;\nimport com.alibaba.otter.canal.server.embedded.CanalServerWithEmbedded;\nimport com.alibaba.otter.canal.sink.AbstractCanalEventSink;\nimport com.alibaba.otter.canal.sink.CanalEventSink;\nimport com.alibaba.otter.node.common.config.ConfigClientService;\nimport com.alibaba.otter.node.etl.OtterConstants;\nimport com.alibaba.otter.node.etl.OtterContextLocator;\nimport com.alibaba.otter.node.etl.select.exceptions.SelectException;\nimport com.alibaba.otter.node.etl.select.selector.Message;\nimport com.alibaba.otter.node.etl.select.selector.MessageDumper;\nimport com.alibaba.otter.node.etl.select.selector.MessageParser;\nimport com.alibaba.otter.node.etl.select.selector.OtterSelector;\nimport com.alibaba.otter.shared.common.model.config.pipeline.Pipeline;\nimport com.alibaba.otter.shared.etl.model.EventData;\nimport com.google.protobuf.ByteString;\nimport com.google.protobuf.InvalidProtocolBufferException;\n\n/**\n * 基于canal embed实现数据获取方式\n * \n * @author jianghang 2012-7-31 下午02:45:15\n * @version 4.1.0\n */\npublic class CanalEmbedSelector implements OtterSelector {\n\n    private static final Logger     logger           = LoggerFactory.getLogger(CanalEmbedSelector.class);\n    private static final String     SEP              = SystemUtils.LINE_SEPARATOR;\n    private static final String     DATE_FORMAT      = \"yyyy-MM-dd HH:mm:ss\";\n    private static final int        maxEmptyTimes    = 10;\n    private int                     logSplitSize     = 50;\n    private boolean                 dump             = true;\n    private boolean                 dumpDetail       = true;\n    private Long                    pipelineId;\n    private CanalServerWithEmbedded canalServer;\n    private ClientIdentity          clientIdentity;\n    private MessageParser           messageParser;\n    private ConfigClientService     configClientService;\n    private OtterDownStreamHandler  handler;\n\n    private String                  destination;\n    private String                  filter;\n    private int                     batchSize        = 10000;\n    private long                    batchTimeout     = -1L;\n    private boolean                 ddlSync          = true;\n    private boolean                 filterTableError = false;\n\n    private CanalConfigClient       canalConfigClient;\n    private volatile boolean        running          = false;                                            // 是否处于运行中\n    private volatile long           lastEntryTime    = 0;\n\n    public CanalEmbedSelector(Long pipelineId){\n        this.pipelineId = pipelineId;\n        canalServer = new CanalServerWithEmbedded();\n    }\n\n    public boolean isStart() {\n        return running;\n    }\n\n    public void start() {\n        if (running) {\n            return;\n        }\n        // 获取destination/filter参数\n        Pipeline pipeline = configClientService.findPipeline(pipelineId);\n        filter = CanalFilterSupport.makeFilterExpression(pipeline);\n        destination = pipeline.getParameters().getDestinationName();\n        batchSize = pipeline.getParameters().getMainstemBatchsize();\n        batchTimeout = pipeline.getParameters().getBatchTimeout();\n        ddlSync = pipeline.getParameters().getDdlSync();\n        final boolean syncFull = pipeline.getParameters().getSyncMode().isRow()\n                                 || pipeline.getParameters().isEnableRemedy();\n        // 暂时使用skip load代替\n        filterTableError = pipeline.getParameters().getSkipSelectException();\n        if (pipeline.getParameters().getDumpSelector() != null) {\n            dump = pipeline.getParameters().getDumpSelector();\n        }\n\n        if (pipeline.getParameters().getDumpSelectorDetail() != null) {\n            dumpDetail = pipeline.getParameters().getDumpSelectorDetail();\n        }\n\n        canalServer.setCanalInstanceGenerator(new CanalInstanceGenerator() {\n\n            public CanalInstance generate(String destination) {\n                Canal canal = canalConfigClient.findCanal(destination);\n                final OtterAlarmHandler otterAlarmHandler = new OtterAlarmHandler();\n                otterAlarmHandler.setPipelineId(pipelineId);\n                OtterContextLocator.autowire(otterAlarmHandler); // 注入一下spring资源\n                // 设置下slaveId，保证多个piplineId下重复引用时不重复\n                long slaveId = 10000;// 默认基数\n                if (canal.getCanalParameter().getSlaveId() != null) {\n                    slaveId = canal.getCanalParameter().getSlaveId();\n                }\n                canal.getCanalParameter().setSlaveId(slaveId + pipelineId);\n                canal.getCanalParameter().setDdlIsolation(ddlSync);\n                canal.getCanalParameter().setFilterTableError(filterTableError);\n                canal.getCanalParameter().setMemoryStorageRawEntry(false);\n\n                CanalInstanceWithManager instance = new CanalInstanceWithManager(canal, filter) {\n\n                    protected CanalHAController initHaController() {\n                        HAMode haMode = parameters.getHaMode();\n                        if (haMode.isMedia()) {\n                            return new MediaHAController(parameters.getMediaGroup(),\n                                parameters.getDbUsername(),\n                                parameters.getDbPassword(),\n                                parameters.getDefaultDatabaseName());\n                        } else {\n                            return super.initHaController();\n                        }\n                    }\n\n                    protected void startEventParserInternal(CanalEventParser parser, boolean isGroup) {\n                        super.startEventParserInternal(parser, isGroup);\n\n                        if (eventParser instanceof MysqlEventParser) {\n                            // 设置支持的类型\n                            ((MysqlEventParser) eventParser).setSupportBinlogFormats(\"ROW\");\n                            if (syncFull) {\n                                ((MysqlEventParser) eventParser).setSupportBinlogImages(\"FULL\");\n                            } else {\n                                ((MysqlEventParser) eventParser).setSupportBinlogImages(\"FULL,MINIMAL\");\n                            }\n\n                            MysqlEventParser mysqlEventParser = (MysqlEventParser) eventParser;\n                            mysqlEventParser.setParallel(false); // otter先使用简单的模式\n                            CanalHAController haController = mysqlEventParser.getHaController();\n                            if (haController instanceof MediaHAController) {\n                                if (isGroup) {\n                                    throw new CanalException(\"not support group database use media HA\");\n                                }\n\n                                ((MediaHAController) haController).setCanalHASwitchable(mysqlEventParser);\n                            }\n\n                            if (!haController.isStart()) {\n                                haController.start();\n                            }\n\n                            // 基于media的Ha，直接从tddl中获取数据库信息\n                            if (haController instanceof MediaHAController) {\n                                AuthenticationInfo authenticationInfo = ((MediaHAController) haController).getAvailableAuthenticationInfo();\n                                ((MysqlEventParser) eventParser).setMasterInfo(authenticationInfo);\n                            }\n                        }\n                    }\n\n                };\n                instance.setAlarmHandler(otterAlarmHandler);\n\n                CanalEventSink eventSink = instance.getEventSink();\n                if (eventSink instanceof AbstractCanalEventSink) {\n                    handler = new OtterDownStreamHandler();\n                    handler.setPipelineId(pipelineId);\n                    handler.setDetectingIntervalInSeconds(canal.getCanalParameter().getDetectingIntervalInSeconds());\n                    OtterContextLocator.autowire(handler); // 注入一下spring资源\n                    ((AbstractCanalEventSink) eventSink).addHandler(handler, 0); // 添加到开头\n                    handler.start();\n                }\n\n                return instance;\n            }\n        });\n        canalServer.start();\n\n        canalServer.start(destination);\n        this.clientIdentity = new ClientIdentity(destination, pipeline.getParameters().getMainstemClientId(), filter);\n        canalServer.subscribe(clientIdentity);// 发起一次订阅\n\n        running = true;\n    }\n\n    public void stop() {\n        if (!running) {\n            return;\n        }\n        running = false;\n        try {\n            handler.stop();\n        } catch (Exception e) {\n            logger.warn(\"failed destory handler\", e);\n        }\n\n        handler = null;\n        canalServer.stop(destination);\n        canalServer.stop();\n    }\n\n    public Message<EventData> selector() throws InterruptedException {\n        int emptyTimes = 0;\n        com.alibaba.otter.canal.protocol.Message message = null;\n        if (batchTimeout < 0) {// 进行轮询处理\n            while (running) {\n                message = canalServer.getWithoutAck(clientIdentity, batchSize);\n                if (message == null || message.getId() == -1L) { // 代表没数据\n                    applyWait(emptyTimes++);\n                } else {\n                    break;\n                }\n            }\n            if (!running) {\n                throw new InterruptedException();\n            }\n        } else { // 进行超时控制\n            while (running) {\n                message = canalServer.getWithoutAck(clientIdentity, batchSize, batchTimeout, TimeUnit.MILLISECONDS);\n                if (message == null || message.getId() == -1L) { // 代表没数据\n                    continue;\n                } else {\n                    break;\n                }\n            }\n            if (!running) {\n                throw new InterruptedException();\n            }\n        }\n\n        List<Entry> entries = null;\n        if (message.isRaw()) {\n            entries = new ArrayList<CanalEntry.Entry>(message.getRawEntries().size());\n            for (ByteString entry : message.getRawEntries()) {\n                try {\n                    entries.add(CanalEntry.Entry.parseFrom(entry));\n                } catch (InvalidProtocolBufferException e) {\n                    throw new SelectException(e);\n                }\n            }\n        } else {\n            entries = message.getEntries();\n        }\n\n        List<EventData> eventDatas = messageParser.parse(pipelineId, entries); // 过滤事务头/尾和回环数据\n        Message<EventData> result = new Message<EventData>(message.getId(), eventDatas);\n        // 更新一下最后的entry时间，包括被过滤的数据\n        if (!CollectionUtils.isEmpty(entries)) {\n            long lastEntryTime = entries.get(entries.size() - 1).getHeader().getExecuteTime();\n            if (lastEntryTime > 0) {// oracle的时间可能为0\n                this.lastEntryTime = lastEntryTime;\n            }\n        }\n\n        if (dump && logger.isInfoEnabled()) {\n            String startPosition = null;\n            String endPosition = null;\n            if (!CollectionUtils.isEmpty(entries)) {\n                startPosition = buildPositionForDump(entries.get(0));\n                endPosition = buildPositionForDump(entries.get(entries.size() - 1));\n            }\n\n            dumpMessages(result, startPosition, endPosition, entries.size());// 记录一下，方便追查问题\n        }\n        return result;\n    }\n\n    public void rollback(Long batchId) {\n        canalServer.rollback(clientIdentity, batchId);\n    }\n\n    public void rollback() {\n        canalServer.rollback(clientIdentity);\n    }\n\n    public void ack(Long batchId) {\n        canalServer.ack(clientIdentity, batchId);\n    }\n\n    public List<Long> unAckBatchs() {\n        return canalServer.listBatchIds(clientIdentity);\n    }\n\n    public Long lastEntryTime() {\n        return lastEntryTime;\n    }\n\n    /**\n     * 记录一下message对象\n     */\n    private synchronized void dumpMessages(Message message, String startPosition, String endPosition, int total) {\n        try {\n            MDC.put(OtterConstants.splitPipelineSelectLogFileKey, String.valueOf(pipelineId));\n            logger.info(SEP + \"****************************************************\" + SEP);\n            logger.info(MessageDumper.dumpMessageInfo(message, startPosition, endPosition, total));\n            logger.info(\"****************************************************\" + SEP);\n            if (dumpDetail) {// 判断一下是否需要打印详细信息\n                dumpEventDatas(message.getDatas());\n                logger.info(\"****************************************************\" + SEP);\n            }\n        } finally {\n            MDC.remove(OtterConstants.splitPipelineSelectLogFileKey);\n        }\n    }\n\n    /**\n     * 分批输出多个数据\n     */\n    private void dumpEventDatas(List<EventData> eventDatas) {\n        int size = eventDatas.size();\n        // 开始输出每条记录\n        int index = 0;\n        do {\n            if (index + logSplitSize >= size) {\n                logger.info(MessageDumper.dumpEventDatas(eventDatas.subList(index, size)));\n            } else {\n                logger.info(MessageDumper.dumpEventDatas(eventDatas.subList(index, index + logSplitSize)));\n            }\n            index += logSplitSize;\n        } while (index < size);\n    }\n\n    // 处理无数据的情况，避免空循环挂死\n    private void applyWait(int emptyTimes) {\n        int newEmptyTimes = emptyTimes > maxEmptyTimes ? maxEmptyTimes : emptyTimes;\n        if (emptyTimes <= 3) { // 3次以内\n            Thread.yield();\n        } else { // 超过3次，最多只sleep 10ms\n            LockSupport.parkNanos(1000 * 1000L * newEmptyTimes);\n        }\n    }\n\n    private String buildPositionForDump(Entry entry) {\n        long time = entry.getHeader().getExecuteTime();\n        Date date = new Date(time);\n        SimpleDateFormat format = new SimpleDateFormat(DATE_FORMAT);\n        return entry.getHeader().getLogfileName() + \":\" + entry.getHeader().getLogfileOffset() + \":\"\n               + entry.getHeader().getExecuteTime() + \"(\" + format.format(date) + \")\";\n    }\n\n    // ================== setter / getter ==================\n    public void setMessageParser(MessageParser messageParser) {\n        this.messageParser = messageParser;\n    }\n\n    public void setConfigClientService(ConfigClientService configClientService) {\n        this.configClientService = configClientService;\n    }\n\n    public void setCanalConfigClient(CanalConfigClient canalConfigClient) {\n        this.canalConfigClient = canalConfigClient;\n    }\n\n    public void setDump(boolean dump) {\n        this.dump = dump;\n    }\n\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/select/selector/canal/CanalFilterSupport.java",
    "content": "package com.alibaba.otter.node.etl.select.selector.canal;\n\nimport java.util.HashSet;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Set;\n\nimport com.alibaba.otter.node.etl.select.exceptions.SelectException;\nimport com.alibaba.otter.shared.common.model.config.data.DataMedia;\nimport com.alibaba.otter.shared.common.model.config.data.DataMediaPair;\nimport com.alibaba.otter.shared.common.model.config.pipeline.Pipeline;\n\n/**\n * Created with Intellij IDEA. Author: yinxiu Date: 2016-01-11 Time: 16:12\n */\npublic class CanalFilterSupport {\n\n    /**\n     * 构建filter 表达式\n     */\n    public static String makeFilterExpression(Pipeline pipeline) {\n        List<DataMediaPair> dataMediaPairs = pipeline.getPairs();\n        if (dataMediaPairs.isEmpty()) {\n            throw new SelectException(\"ERROR ## the pair is empty,the pipeline id = \" + pipeline.getId());\n        }\n\n        Set<String> mediaNames = new HashSet<String>();\n        for (DataMediaPair dataMediaPair : dataMediaPairs) {\n            DataMedia.ModeValue namespaceMode = dataMediaPair.getSource().getNamespaceMode();\n            DataMedia.ModeValue nameMode = dataMediaPair.getSource().getNameMode();\n\n            if (namespaceMode.getMode().isSingle()) {\n                buildFilter(mediaNames, namespaceMode.getSingleValue(), nameMode, false);\n            } else if (namespaceMode.getMode().isMulti()) {\n                for (String namespace : namespaceMode.getMultiValue()) {\n                    buildFilter(mediaNames, namespace, nameMode, false);\n                }\n            } else if (namespaceMode.getMode().isWildCard()) {\n                buildFilter(mediaNames, namespaceMode.getSingleValue(), nameMode, true);\n            }\n        }\n\n        StringBuilder result = new StringBuilder();\n        Iterator<String> iter = mediaNames.iterator();\n        int i = -1;\n        while (iter.hasNext()) {\n            i++;\n            if (i == 0) {\n                result.append(iter.next());\n            } else {\n                result.append(\",\").append(iter.next());\n            }\n        }\n\n        String markTable = pipeline.getParameters().getSystemSchema() + \".\"\n                           + pipeline.getParameters().getSystemMarkTable();\n        String bufferTable = pipeline.getParameters().getSystemSchema() + \".\"\n                             + pipeline.getParameters().getSystemBufferTable();\n        String dualTable = pipeline.getParameters().getSystemSchema() + \".\"\n                           + pipeline.getParameters().getSystemDualTable();\n\n        if (!mediaNames.contains(markTable)) {\n            result.append(\",\").append(markTable);\n        }\n\n        if (!mediaNames.contains(bufferTable)) {\n            result.append(\",\").append(bufferTable);\n        }\n\n        if (!mediaNames.contains(dualTable)) {\n            result.append(\",\").append(dualTable);\n        }\n\n        // String otterTable = pipeline.getParameters().getSystemSchema() +\n        // \"\\\\..*\";\n        // if (!mediaNames.contains(otterTable)) {\n        // result.append(\",\").append(otterTable);\n        // }\n\n        return result.toString();\n    }\n\n    private static void buildFilter(Set<String> mediaNames, String namespace, DataMedia.ModeValue nameMode,\n                                    boolean wildcard) {\n        String splitChar = \".\";\n        if (wildcard) {\n            splitChar = \"\\\\.\";\n        }\n\n        if (nameMode.getMode().isSingle()) {\n            mediaNames.add(namespace + splitChar + nameMode.getSingleValue());\n        } else if (nameMode.getMode().isMulti()) {\n            for (String name : nameMode.getMultiValue()) {\n                mediaNames.add(namespace + splitChar + name);\n            }\n        } else if (nameMode.getMode().isWildCard()) {\n            mediaNames.add(namespace + \"\\\\.\" + nameMode.getSingleValue());\n        }\n    }\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/select/selector/canal/OtterAlarmHandler.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.select.selector.canal;\n\nimport com.alibaba.otter.canal.common.AbstractCanalLifeCycle;\nimport com.alibaba.otter.canal.common.alarm.CanalAlarmHandler;\nimport com.alibaba.otter.shared.arbitrate.ArbitrateEventService;\nimport com.alibaba.otter.shared.arbitrate.model.TerminEventData;\nimport com.alibaba.otter.shared.arbitrate.model.TerminEventData.TerminType;\n\n/**\n * 基于otter manager的报警机制实现\n * \n * @author jianghang 2012-8-23 上午10:59:58\n * @version 4.1.0\n */\npublic class OtterAlarmHandler extends AbstractCanalLifeCycle implements CanalAlarmHandler {\n\n    private Long                  pipelineId;\n    private ArbitrateEventService arbitrateEventService;\n\n    public void sendAlarm(String destination, String msg) {\n        TerminEventData errorEventData = new TerminEventData();\n        errorEventData.setPipelineId(pipelineId);\n        errorEventData.setType(TerminType.WARNING);\n        errorEventData.setCode(\"canal\");\n        errorEventData.setDesc(destination + \":\" + msg);\n        arbitrateEventService.terminEvent().single(errorEventData);\n    }\n\n    public void setPipelineId(Long pipelineId) {\n        this.pipelineId = pipelineId;\n    }\n\n    public void setArbitrateEventService(ArbitrateEventService arbitrateEventService) {\n        this.arbitrateEventService = arbitrateEventService;\n    }\n\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/select/selector/canal/OtterDownStreamHandler.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.select.selector.canal;\n\nimport java.util.List;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.ScheduledExecutorService;\nimport java.util.concurrent.ScheduledFuture;\nimport java.util.concurrent.ScheduledThreadPoolExecutor;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.atomic.AtomicBoolean;\nimport java.util.concurrent.atomic.AtomicLong;\nimport java.util.concurrent.locks.ReentrantLock;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.slf4j.MDC;\n\nimport com.alibaba.otter.canal.sink.AbstractCanalEventDownStreamHandler;\nimport com.alibaba.otter.canal.sink.CanalEventSink;\nimport com.alibaba.otter.canal.store.model.Event;\nimport com.alibaba.otter.node.etl.OtterConstants;\nimport com.alibaba.otter.shared.arbitrate.ArbitrateEventService;\nimport com.alibaba.otter.shared.arbitrate.model.MainStemEventData;\nimport com.alibaba.otter.shared.arbitrate.model.TerminEventData;\nimport com.alibaba.otter.shared.arbitrate.model.TerminEventData.TerminType;\nimport com.alibaba.otter.shared.common.utils.thread.NamedThreadFactory;\n\n/**\n * 在{@linkplain CanalEventSink}消费数据之前，更新到对应的store中\n * \n * @author jianghang 2012-7-31 下午03:27:18\n * @version 4.1.0\n */\npublic class OtterDownStreamHandler extends AbstractCanalEventDownStreamHandler<List<Event>> {\n\n    private static final Logger      logger                   = LoggerFactory.getLogger(OtterDownStreamHandler.class);\n    private static final String      DETECTING_FAILED_MESSAGE = \"pid:%s canal elapsed %s seconds no data\";\n    private Long                     pipelineId;\n    private ArbitrateEventService    arbitrateEventService;\n    // 心跳检查控制mainstem信号\n    private ScheduledExecutorService scheduler                = null;\n    private ScheduledFuture          future                   = null;\n    private AtomicBoolean            working                  = new AtomicBoolean(false);\n    private Integer                  detectingIntervalInSeconds;                                                      // 心跳包发送时间\n    private volatile Long            lastEventExecuteTime     = 0L;\n    // detecting临时数据\n    private int                      detectingThresoldCount   = 10;\n    private int                      detectingExpCount        = 1;                                                    // 增常趋势\n    private AtomicLong               detectingFailedCount     = new AtomicLong(0);                                    // 检测失败的次数\n    private AtomicLong               detectingSuccessedCount  = new AtomicLong(0);\n    private ReentrantLock            lock                     = new ReentrantLock();                                  // 检测成功的次数\n\n    public void stop() {\n        try {\n            lock.lock();\n            super.stop();\n\n            if (working.compareAndSet(true, false)) {\n                stopDetecting();\n            }\n        } finally {\n            lock.unlock();\n        }\n\n    }\n\n    public List<Event> before(List<Event> events) {\n        /**\n         * 1）故障现象channel重启后，pipeline处于定位中，后台日志报超时异常。\n         * 2）故障原因：OtterDownStreamHandler里面添加打印日志发现有2个OtterDownStreamHandler.scheduler在运行\n         * 分析发现旧的scheduler没有被停止掉导致。原因是OtterDownStreamHandler.scheduler关闭后被AbstractEventParser.startHeartBeat拉起\n         */\n        lastEventExecuteTime = System.currentTimeMillis();// 记录最后一条数据时间\n\n        if (super.isStart()) {\n            if (working.compareAndSet(false, true)) {// 第一次有数据时\n                try {\n                    // 和stop()方法竞争锁，防止stop停止调度任务，又被本方法开启调度\n                    lock.lock();\n                    if (super.isStart()) {\n                        // 再次判断是否为isStart()，因为并发的stop()随时可以使状态为停止态\n                        startDetecting();\n                    } else {\n                        // 已经被上面的并发的stop()关闭掉\n                        working.set(false);\n                    }\n                } catch (Exception e) {\n                    e.printStackTrace();\n                } finally {\n                    lock.unlock();\n                }\n\n            }\n\n        }\n\n        return super.before(events);\n    }\n\n    public List<Event> retry(List<Event> events) {\n        lastEventExecuteTime = System.currentTimeMillis();// 记录最后一条数据时间\n        return super.retry(events);\n    }\n\n    public List<Event> after(List<Event> events) {\n        // do nothing\n        return super.after(events);\n    }\n\n    private void startDetecting() {\n        // 直接发送已追上的状态，保持和eromanga兼容处理\n        MainStemEventData mainStemData = new MainStemEventData();\n        mainStemData.setPipelineId(pipelineId);\n        mainStemData.setStatus(MainStemEventData.Status.OVERTAKE);\n        arbitrateEventService.mainStemEvent().single(mainStemData);\n\n        // 启动异步线程定时监控，一定会有数据过来\n        String schedulerName = String.format(\"pipelineId = %s , CanalDetecting\", String.valueOf(pipelineId));\n        scheduler = Executors.newScheduledThreadPool(1, new NamedThreadFactory(schedulerName));\n        future = scheduler.scheduleAtFixedRate(new Runnable() {\n\n            public void run() {\n                try {\n                    MDC.put(OtterConstants.splitPipelineLogFileKey, String.valueOf(pipelineId));\n                    // 检查下和最后收到的数据的时间戳，如果超过一定时间没收到，说明canal解析存在问题\n                    // (因为会有心跳包数据，理论上时间间隔会小于一定值)\n                    if (isDelayed(System.currentTimeMillis(), lastEventExecuteTime)) {\n                        notifyFailed();\n                    } else {\n                        notifySuccessed();\n                    }\n                } catch (Exception e) {\n                    logger.error(\"heartbeat check failed!\", e);\n                } finally {\n                    MDC.remove(OtterConstants.splitPipelineLogFileKey);\n                }\n\n            }\n        }, detectingIntervalInSeconds, detectingIntervalInSeconds, TimeUnit.SECONDS);\n    }\n\n    private void stopDetecting() {\n        ((ScheduledThreadPoolExecutor) scheduler).remove((Runnable) future);\n        scheduler.shutdownNow();\n    }\n\n    private void notifyFailed() {\n        detectingSuccessedCount.set(0);\n        long failedCount = detectingFailedCount.incrementAndGet();\n        if (failedCount == 1) {\n            detectingExpCount = 1;// 系数重置\n\n            notifyMainstemStatus(MainStemEventData.Status.TAKEING);\n        }\n\n        if (failedCount >= detectingThresoldCount * detectingExpCount * detectingExpCount) {\n            notifyMainstemStatus(MainStemEventData.Status.TAKEING);\n            detectingExpCount++; // 系数增大一次\n\n            // 并且发送一次报警信息，系统不太正常了，超过一定时间一次都没有拿到对应的数据\n            // 可能出现的情况：\n            // 1. 主备发生切换，定位position花费了过久的时间\n            // 2. MysqlEventParser工作不正常，一直拿不到数据，比如数据库挂了，但是又没通知其进行主备切换\n            TerminEventData errorEventData = new TerminEventData();\n            errorEventData.setPipelineId(pipelineId);\n            errorEventData.setType(TerminType.WARNING);\n            errorEventData.setCode(\"mainstem\");\n            errorEventData.setDesc(String.format(DETECTING_FAILED_MESSAGE,\n                pipelineId,\n                String.valueOf(detectingIntervalInSeconds * failedCount)));\n            arbitrateEventService.terminEvent().single(errorEventData);\n        }\n    }\n\n    private void notifySuccessed() {\n        detectingFailedCount.set(0);\n        long successedCount = detectingSuccessedCount.incrementAndGet();\n        if (successedCount == 1) {\n            detectingExpCount = 1;// 系数重置\n            notifyMainstemStatus(MainStemEventData.Status.OVERTAKE);\n        }\n\n        if (successedCount >= detectingThresoldCount * detectingExpCount * detectingExpCount) {\n            detectingExpCount++; // 系数增大一次\n            notifyMainstemStatus(MainStemEventData.Status.OVERTAKE);\n        }\n\n    }\n\n    private void notifyMainstemStatus(MainStemEventData.Status status) {\n        MainStemEventData mainStemData = new MainStemEventData();\n        mainStemData.setPipelineId(pipelineId);\n        mainStemData.setStatus(status);\n        arbitrateEventService.mainStemEvent().single(mainStemData);\n    }\n\n    private boolean isDelayed(Long detectingExecuteTime, Long lastExecuteTime) {\n        long delayTime = detectingExecuteTime - lastExecuteTime;\n        return delayTime > detectingIntervalInSeconds * 2 * 1000;\n    }\n\n    public void setPipelineId(Long pipelineId) {\n        this.pipelineId = pipelineId;\n    }\n\n    public void setDetectingIntervalInSeconds(Integer detectingIntervalInSeconds) {\n        this.detectingIntervalInSeconds = detectingIntervalInSeconds;\n    }\n\n    public void setArbitrateEventService(ArbitrateEventService arbitrateEventService) {\n        this.arbitrateEventService = arbitrateEventService;\n    }\n\n    public void setDetectingThresoldCount(int detectingThresoldCount) {\n        this.detectingThresoldCount = detectingThresoldCount;\n    }\n\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/transform/TransformTask.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.transform;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.slf4j.MDC;\n\nimport com.alibaba.otter.node.etl.OtterConstants;\nimport com.alibaba.otter.node.etl.common.jmx.StageAggregation.AggregationItem;\nimport com.alibaba.otter.node.etl.common.pipe.PipeKey;\nimport com.alibaba.otter.node.etl.common.task.GlobalTask;\nimport com.alibaba.otter.node.etl.extract.SetlFuture;\nimport com.alibaba.otter.node.etl.transform.transformer.OtterTransformerFactory;\nimport com.alibaba.otter.shared.arbitrate.model.EtlEventData;\nimport com.alibaba.otter.shared.common.model.config.enums.StageType;\nimport com.alibaba.otter.shared.etl.model.BatchObject;\nimport com.alibaba.otter.shared.etl.model.DbBatch;\nimport com.alibaba.otter.shared.etl.model.EventData;\nimport com.alibaba.otter.shared.etl.model.FileBatch;\nimport com.alibaba.otter.shared.etl.model.FileData;\nimport com.alibaba.otter.shared.etl.model.RowBatch;\n\n/**\n * transform工作线程,负责桥接连接仲裁器,Config,translate\n * \n * @author jianghang 2011-10-11 下午04:14:11\n * @version 4.0.0\n */\npublic class TransformTask extends GlobalTask {\n\n    private OtterTransformerFactory otterTransformerFactory;\n\n    public TransformTask(Long pipelineId){\n        super(pipelineId);\n    }\n\n    public void run() {\n        MDC.put(OtterConstants.splitPipelineLogFileKey, String.valueOf(pipelineId));\n        while (running) {\n            try {\n                final EtlEventData etlEventData = arbitrateEventService.transformEvent().await(pipelineId);\n                Runnable task = new Runnable() {\n\n                    @Override\n                    public void run() {\n                        // 设置profiling信息\n                        boolean profiling = isProfiling();\n                        Long profilingStartTime = null;\n                        if (profiling) {\n                            profilingStartTime = System.currentTimeMillis();\n                        }\n\n                        MDC.put(OtterConstants.splitPipelineLogFileKey, String.valueOf(pipelineId));\n                        String currentName = Thread.currentThread().getName();\n                        Thread.currentThread().setName(createTaskName(pipelineId, \"transformWorker\"));\n\n                        try {\n                            // 后续可判断同步数据是否为rowData\n                            List<PipeKey> keys = (List<PipeKey>) etlEventData.getDesc();\n                            DbBatch dbBatch = rowDataPipeDelegate.get(keys);\n\n                            // 可能拿到为null，因为内存不足或者网络异常，长时间阻塞时，导致从pipe拿数据出现异常，数据可能被上一个节点已经删除\n                            if (dbBatch == null) {\n                                processMissData(pipelineId, \"transform miss data with keys:\" + keys.toString());\n                                return;\n                            }\n\n                            // 根据对应的tid，转化为目标端的tid。后续可进行字段的加工处理\n                            // 暂时认为rowBatchs和fileBatchs不会有异构数据的转化\n                            Map<Class, BatchObject> dataBatchs = otterTransformerFactory.transform(dbBatch.getRowBatch());\n\n                            // 可能存在同一个Pipeline下有Mq和Db两种同步类型\n                            dbBatch.setRowBatch((RowBatch) dataBatchs.get(EventData.class));\n\n                            if (dbBatch.getFileBatch() != null) {\n                                Map<Class, BatchObject> fileBatchs = otterTransformerFactory.transform(dbBatch.getFileBatch());\n                                dbBatch.setFileBatch((FileBatch) fileBatchs.get(FileData.class));\n                            }\n                            // 传递给下一个流程\n                            List<PipeKey> nextKeys = rowDataPipeDelegate.put(dbBatch, etlEventData.getNextNid());\n                            etlEventData.setDesc(nextKeys);\n\n                            if (profiling) {\n                                Long profilingEndTime = System.currentTimeMillis();\n                                stageAggregationCollector.push(pipelineId,\n                                                               StageType.TRANSFORM,\n                                                               new AggregationItem(profilingStartTime, profilingEndTime));\n                            }\n                            // 处理完成后通知single已完成\n                            arbitrateEventService.transformEvent().single(etlEventData);\n                        } catch (Throwable e) {\n                            if (!isInterrupt(e)) {\n                                logger.error(String.format(\"[%s] transformWork executor is error! data:%s\", pipelineId,\n                                                           etlEventData), e);\n                                sendRollbackTermin(pipelineId, e);\n                            } else {\n                                logger.info(String.format(\"[%s] transformWork executor is interrrupt! data:%s\",\n                                                          pipelineId, etlEventData), e);\n                            }\n                        } finally {\n                            Thread.currentThread().setName(currentName);\n                            MDC.remove(OtterConstants.splitPipelineLogFileKey);\n                        }\n                    }\n                };\n\n                // 构造pending任务，可在关闭线程时退出任务\n                SetlFuture extractFuture = new SetlFuture(StageType.TRANSFORM, etlEventData.getProcessId(),\n                                                          pendingFuture, task);\n                executorService.execute(extractFuture);\n\n            } catch (Throwable e) {\n                if (isInterrupt(e)) {\n                    logger.info(String.format(\"[%s] transformTask is interrupted!\", pipelineId), e);\n                    return;\n                } else {\n                    logger.error(String.format(\"[%s] transformTask is error!\", pipelineId), e);\n                    sendRollbackTermin(pipelineId, e);\n                }\n            }\n        }\n    }\n\n    // =================== setter / getter ======================\n\n    public void setOtterTransformerFactory(OtterTransformerFactory otterTransformerFactory) {\n        this.otterTransformerFactory = otterTransformerFactory;\n    }\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/transform/exception/TransformException.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.transform.exception;\n\nimport org.apache.commons.lang.exception.NestableRuntimeException;\n\n/**\n * @author jianghang 2011-9-16 下午01:59:25\n * @version 4.0.0\n */\npublic class TransformException extends NestableRuntimeException {\n\n    private static final long serialVersionUID = -7288830284122672209L;\n\n    private String            errorCode;\n    private String            errorDesc;\n\n    public TransformException(String errorCode){\n        super(errorCode);\n    }\n\n    public TransformException(String errorCode, Throwable cause){\n        super(errorCode, cause);\n    }\n\n    public TransformException(String errorCode, String errorDesc){\n        super(errorCode + \":\" + errorDesc);\n    }\n\n    public TransformException(String errorCode, String errorDesc, Throwable cause){\n        super(errorCode + \":\" + errorDesc, cause);\n    }\n\n    public TransformException(Throwable cause){\n        super(cause);\n    }\n\n    public String getErrorCode() {\n        return errorCode;\n    }\n\n    public String getErrorDesc() {\n        return errorDesc;\n    }\n\n    @Override\n    public Throwable fillInStackTrace() {\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/transform/transformer/AbstractOtterTransformer.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.transform.transformer;\n\n/**\n * @author jianghang 2011-10-27 下午04:09:31\n * @version 4.0.0\n * @param <S>\n * @param <T>\n */\npublic abstract class AbstractOtterTransformer<S, T> implements OtterTransformer<S, T> {\n\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/transform/transformer/FileDataTransformer.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.transform.transformer;\n\nimport com.alibaba.otter.shared.etl.model.FileData;\n\n/**\n * {@linkplain FileData}数据对象转化\n * \n * @author jianghang 2011-10-27 下午06:31:15\n * @version 4.0.0\n */\npublic class FileDataTransformer extends AbstractOtterTransformer<FileData, FileData> {\n\n    public FileData transform(FileData data, OtterTransformerContext context) {\n        // 后续可以针对文件进行目标地的fileResolver解析\n        if (context.getDataMediaPair().getId().equals(data.getPairId())) {\n            return data;\n        } else {\n            return null;\n        }\n        // data.setPairId(context.getDataMediaPair().getId());\n        // return data;\n    }\n\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/transform/transformer/OtterTransformer.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.transform.transformer;\n\n/**\n * 数据提取过程，T\n * \n * @author jianghang 2011-10-27 下午04:04:24\n * @version 4.0.0\n */\npublic interface OtterTransformer<S, T> {\n\n    public S transform(T data, OtterTransformerContext context);\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/transform/transformer/OtterTransformerContext.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.transform.transformer;\n\nimport com.alibaba.otter.shared.common.model.config.data.DataMediaPair;\nimport com.alibaba.otter.shared.common.model.config.pipeline.Pipeline;\nimport com.alibaba.otter.shared.etl.model.Identity;\n\n/**\n * 数据转换过程中的上下文\n * \n * @author jianghang 2011-10-27 下午05:12:53\n * @version 4.0.0\n */\npublic class OtterTransformerContext {\n\n    private Identity      identity;\n    private Pipeline      pipeline;\n    private DataMediaPair dataMediaPair;\n\n    public OtterTransformerContext(Identity identity, DataMediaPair dataMediaPair, Pipeline pipeline){\n        this.identity = identity;\n        this.dataMediaPair = dataMediaPair;\n        this.pipeline = pipeline;\n    }\n\n    public Identity getIdentity() {\n        return identity;\n    }\n\n    public void setIdentity(Identity identity) {\n        this.identity = identity;\n    }\n\n    public DataMediaPair getDataMediaPair() {\n        return dataMediaPair;\n    }\n\n    public void setDataMediaPair(DataMediaPair dataMediaPair) {\n        this.dataMediaPair = dataMediaPair;\n    }\n\n    public Pipeline getPipeline() {\n        return pipeline;\n    }\n\n    public void setPipeline(Pipeline pipeline) {\n        this.pipeline = pipeline;\n    }\n\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/transform/transformer/OtterTransformerFactory.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.transform.transformer;\n\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport com.alibaba.otter.node.common.config.ConfigClientService;\nimport com.alibaba.otter.node.etl.transform.exception.TransformException;\nimport com.alibaba.otter.shared.common.model.config.ConfigHelper;\nimport com.alibaba.otter.shared.common.model.config.data.DataMedia;\nimport com.alibaba.otter.shared.common.model.config.data.DataMediaPair;\nimport com.alibaba.otter.shared.common.model.config.data.db.DbDataMedia;\nimport com.alibaba.otter.shared.common.model.config.pipeline.Pipeline;\nimport com.alibaba.otter.shared.etl.model.BatchObject;\nimport com.alibaba.otter.shared.etl.model.EventData;\nimport com.alibaba.otter.shared.etl.model.FileBatch;\nimport com.alibaba.otter.shared.etl.model.FileData;\nimport com.alibaba.otter.shared.etl.model.Identity;\nimport com.alibaba.otter.shared.etl.model.RowBatch;\n\n/**\n * 数据对象转化工厂\n * \n * @author jianghang 2011-10-27 下午06:29:02\n * @version 4.0.0\n */\npublic class OtterTransformerFactory {\n\n    private ConfigClientService configClientService;\n    private RowDataTransformer  rowDataTransformer;\n    private FileDataTransformer fileDataTransformer;\n\n    /**\n     * 将一种源数据进行转化，最后得到的结果会根据DataMediaPair中定义的目标对象生成不同的数据对象 <br/>\n     * \n     * <pre>\n     * 返回对象格式：Map\n     * key : Class对象，代表生成的目标数据对象\n     * value : 每种目标数据对象的集合数据\n     * </pre>\n     */\n    public Map<Class, BatchObject> transform(RowBatch rowBatch) {\n        final Identity identity = translateIdentity(rowBatch.getIdentity());\n        Map<Class, BatchObject> result = new HashMap<Class, BatchObject>();\n        // 初始化默认值\n        result.put(EventData.class, initBatchObject(identity, EventData.class));\n\n        for (EventData eventData : rowBatch.getDatas()) {\n            // 处理eventData\n            Long tableId = eventData.getTableId();\n            Pipeline pipeline = configClientService.findPipeline(identity.getPipelineId());\n            // 针对每个同步数据，可能会存在多路复制的情况\n            List<DataMediaPair> dataMediaPairs = ConfigHelper.findDataMediaPairByMediaId(pipeline, tableId);\n            for (DataMediaPair pair : dataMediaPairs) {\n                if (!pair.getSource().getId().equals(tableId)) { // 过滤tableID不为源的同步\n                    continue;\n                }\n\n                OtterTransformer translate = lookup(pair.getSource(), pair.getTarget());\n                // 进行转化\n                Object item = translate.transform(eventData, new OtterTransformerContext(identity, pair, pipeline));\n                if (item == null) {\n                    continue;\n                }\n                // 合并结果\n                merge(identity, result, item);\n            }\n\n        }\n\n        return result;\n    }\n\n    /**\n     * 转化FileBatch对象\n     */\n    public Map<Class, BatchObject> transform(FileBatch fileBatch) {\n        final Identity identity = translateIdentity(fileBatch.getIdentity());\n        List<FileData> fileDatas = fileBatch.getFiles();\n        Map<Class, BatchObject> result = new HashMap<Class, BatchObject>();\n        // 初始化默认值\n        result.put(FileData.class, initBatchObject(identity, FileData.class));\n\n        for (FileData fileData : fileDatas) {\n            // 进行转化\n            Long tableId = fileData.getTableId();\n            Pipeline pipeline = configClientService.findPipeline(identity.getPipelineId());\n            // 针对每个同步数据，可能会存在多路复制的情况\n            List<DataMediaPair> dataMediaPairs = ConfigHelper.findDataMediaPairByMediaId(pipeline, tableId);\n            for (DataMediaPair pair : dataMediaPairs) {\n                if (!pair.getSource().getId().equals(tableId)) { // 过滤tableID不为源的同步\n                    continue;\n                }\n\n                Object item = fileDataTransformer.transform(fileData, new OtterTransformerContext(identity, pair,\n                                                                                                  pipeline));\n                if (item == null) {\n                    continue;\n                }\n                // 合并结果\n                merge(identity, result, item);\n            }\n\n        }\n\n        return result;\n    }\n\n    // =============================== helper method\n    // ============================\n\n    // 将生成的item对象合并到结果对象中\n    private synchronized void merge(Identity identity, Map<Class, BatchObject> data, Object item) {\n        Class clazz = item.getClass();\n        BatchObject batchObject = data.get(clazz);\n        // 初始化一下对象\n        if (batchObject == null) {\n            batchObject = initBatchObject(identity, clazz);\n            data.put(clazz, batchObject);\n        }\n\n        // 进行merge处理\n        if (batchObject instanceof RowBatch) {\n            ((RowBatch) batchObject).merge((EventData) item);\n        } else if (batchObject instanceof FileBatch) {\n            ((FileBatch) batchObject).getFiles().add((FileData) item);\n        } else {\n            throw new TransformException(\"no support Data[\" + clazz.getName() + \"]\");\n        }\n    }\n\n    // 根据对应的类型初始化batchObject对象\n    private BatchObject initBatchObject(Identity identity, Class clazz) {\n        if (EventData.class.equals(clazz)) {\n            RowBatch rowbatch = new RowBatch();\n            rowbatch.setIdentity(identity);\n            return rowbatch;\n        } else if (FileData.class.equals(clazz)) {\n            FileBatch fileBatch = new FileBatch();\n            fileBatch.setIdentity(identity);\n            return fileBatch;\n        } else {\n            throw new TransformException(\"no support Data[\" + clazz.getName() + \"]\");\n        }\n    }\n\n    // 查找对应的tranlate转化对象\n    private OtterTransformer lookup(DataMedia sourceDataMedia, DataMedia targetDataMedia) {\n        if (sourceDataMedia instanceof DbDataMedia && targetDataMedia instanceof DbDataMedia) {\n            return rowDataTransformer;\n        }\n\n        throw new TransformException(\"no support translate for source \" + sourceDataMedia.toString() + \" to target \"\n                                     + targetDataMedia);\n    }\n\n    private Identity translateIdentity(Identity identity) {\n        Identity result = new Identity();\n        result.setChannelId(identity.getChannelId());\n        result.setPipelineId(identity.getPipelineId());\n        result.setProcessId(identity.getProcessId());\n        return result;\n    }\n\n    // ==================== setter / getter ==================\n\n    public void setConfigClientService(ConfigClientService configClientService) {\n        this.configClientService = configClientService;\n    }\n\n    public void setRowDataTransformer(RowDataTransformer rowDataTransformer) {\n        this.rowDataTransformer = rowDataTransformer;\n    }\n\n    public void setFileDataTransformer(FileDataTransformer fileDataTransformer) {\n        this.fileDataTransformer = fileDataTransformer;\n    }\n\n}\n"
  },
  {
    "path": "node/etl/src/main/java/com/alibaba/otter/node/etl/transform/transformer/RowDataTransformer.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.transform.transformer;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.List;\n\nimport org.apache.commons.lang.StringUtils;\nimport org.apache.ddlutils.model.Column;\nimport org.apache.ddlutils.model.Table;\nimport org.springframework.util.CollectionUtils;\n\nimport com.alibaba.otter.node.etl.common.db.dialect.DbDialect;\nimport com.alibaba.otter.node.etl.common.db.dialect.DbDialectFactory;\nimport com.alibaba.otter.node.etl.common.db.utils.DdlUtils;\nimport com.alibaba.otter.node.etl.transform.exception.TransformException;\nimport com.alibaba.otter.shared.common.model.config.ConfigHelper;\nimport com.alibaba.otter.shared.common.model.config.data.ColumnPair;\nimport com.alibaba.otter.shared.common.model.config.data.DataMedia;\nimport com.alibaba.otter.shared.common.model.config.data.DataMedia.ModeValue;\nimport com.alibaba.otter.shared.common.model.config.data.DataMediaPair;\nimport com.alibaba.otter.shared.common.model.config.data.db.DbMediaSource;\nimport com.alibaba.otter.shared.etl.model.EventColumn;\nimport com.alibaba.otter.shared.etl.model.EventData;\nimport com.alibaba.otter.shared.etl.model.EventType;\nimport com.google.common.collect.HashMultimap;\nimport com.google.common.collect.Multimap;\n\n/**\n * RowData -> RowData数据的转换\n * \n * @author jianghang 2011-10-27 下午04:11:45\n * @version 4.0.0\n */\npublic class RowDataTransformer extends AbstractOtterTransformer<EventData, EventData> {\n\n    private DbDialectFactory dbDialectFactory;\n\n    public EventData transform(EventData data, OtterTransformerContext context) {\n        EventData result = new EventData();\n        // 处理Table转化\n        DataMedia dataMedia = context.getDataMediaPair().getTarget();\n        result.setPairId(context.getDataMediaPair().getId());\n        result.setTableId(dataMedia.getId());\n        // 需要特殊处理下multi场景\n        buildName(data, result, context.getDataMediaPair());\n        result.setEventType(data.getEventType());\n        result.setExecuteTime(data.getExecuteTime());\n        result.setSyncConsistency(data.getSyncConsistency());\n        result.setRemedy(data.isRemedy());\n        result.setSyncMode(data.getSyncMode());\n        result.setSize(data.getSize());\n        result.setHint(data.getHint());\n        result.setWithoutSchema(data.isWithoutSchema());\n        if (data.getEventType().isDdl()) {\n            // ddl不需要处理字段\n            if (StringUtils.equalsIgnoreCase(result.getSchemaName(), data.getSchemaName())\n                && StringUtils.equalsIgnoreCase(result.getTableName(), data.getTableName())) {\n                // 是否需要对ddl sql进行转化，暂时不支持异构，必须保证源表和目标表的名字相同\n                result.setDdlSchemaName(data.getDdlSchemaName());\n                result.setSql(data.getSql());\n                return result;\n            } else {\n                // 动态转换ddl sql,替换库名和表名\n                String sql = DdlUtils.convert(data.getSql(),\n                    data.getSchemaName(),\n                    data.getTableName(),\n                    result.getSchemaName(),\n                    result.getTableName());\n                result.setDdlSchemaName(result.getSchemaName());\n                result.setSql(sql);\n                return result;\n                // throw new TransformException(\"no support ddl for [\" +\n                // data.getSchemaName() + \".\" + data.getTableName()\n                // + \"] to [\" + result.getSchemaName() + \".\" +\n                // result.getTableName()\n                // + \"] , sql :\" + data.getSql());\n            }\n        }\n\n        Multimap<String, String> translateColumnNames = HashMultimap.create();\n        if (context.getDataMediaPair().getColumnPairMode().isInclude()) { // 只针对正向匹配进行名字映射，exclude不做处理\n            List<ColumnPair> columnPairs = context.getDataMediaPair().getColumnPairs();\n            for (ColumnPair columnPair : columnPairs) {\n                translateColumnNames.put(columnPair.getSourceColumn().getName(), columnPair.getTargetColumn().getName());\n            }\n        }\n        // 准备一下table meta\n        DataMediaPair dataMediaPair = context.getDataMediaPair();\n        boolean useTableTransform = context.getPipeline().getParameters().getUseTableTransform();\n        boolean enableCompatibleMissColumn = context.getPipeline().getParameters().getEnableCompatibleMissColumn();\n        TableInfoHolder tableHolder = null;\n        if (useTableTransform || enableCompatibleMissColumn) {// 控制一下是否需要反查table\n                                                              // meta信息，如果同构数据库，完全没必要反查\n            // 获取目标库的表信息\n            DbDialect dbDialect = dbDialectFactory.getDbDialect(dataMediaPair.getPipelineId(),\n                (DbMediaSource) dataMedia.getSource());\n\n            Table table = dbDialect.findTable(result.getSchemaName(), result.getTableName());\n            tableHolder = new TableInfoHolder(table, useTableTransform, enableCompatibleMissColumn);\n        }\n\n        // 处理column转化\n        List<EventColumn> otherColumns = translateColumns(result,\n            data.getColumns(),\n            context.getDataMediaPair(),\n            translateColumnNames,\n            tableHolder);\n        translatePkColumn(result,\n            data.getKeys(),\n            data.getOldKeys(),\n            otherColumns,\n            context.getDataMediaPair(),\n            translateColumnNames,\n            tableHolder);\n\n        result.setColumns(otherColumns);\n        return result;\n    }\n\n    /**\n     * 设置对应的目标库schema.name，需要考虑mutl配置情况\n     * \n     * <pre>\n     * case:\n     * 1. 源:offer , 目：offer\n     * 2. 源:offer[1-128] , 目：offer\n     * 3. 源:offer[1-128] , 目：offer[1-128]\n     * 4. 源:offer , 目：offer[1-128] 不支持，会报错\n     */\n    private void buildName(EventData data, EventData result, DataMediaPair pair) {\n        DataMedia targetDataMedia = pair.getTarget();\n        DataMedia sourceDataMedia = pair.getSource();\n        String schemaName = buildName(data.getSchemaName(),\n            sourceDataMedia.getNamespaceMode(),\n            targetDataMedia.getNamespaceMode());\n        String tableName = buildName(data.getTableName(), sourceDataMedia.getNameMode(), targetDataMedia.getNameMode());\n        result.setSchemaName(schemaName);\n        result.setTableName(tableName);\n    }\n\n    private String buildName(String name, ModeValue sourceModeValue, ModeValue targetModeValue) {\n        if (targetModeValue.getMode().isWildCard()) {\n            return name; // 通配符，认为源和目标一定是一致的\n        } else if (targetModeValue.getMode().isMulti()) {\n            int index = ConfigHelper.indexIgnoreCase(sourceModeValue.getMultiValue(), name);\n            if (index == -1) {\n                throw new TransformException(\"can not found namespace or name in media:\" + sourceModeValue.toString());\n            }\n\n            return targetModeValue.getMultiValue().get(index);\n        } else {\n            return targetModeValue.getSingleValue();\n        }\n    }\n\n    // 处理字段映射\n    private List<EventColumn> translateColumns(EventData data, List<EventColumn> columns, DataMediaPair dataMediaPair,\n                                               Multimap<String, String> translateColumnNames,\n                                               TableInfoHolder tableHolder) {\n        List<EventColumn> tcolumns = new ArrayList<EventColumn>();\n        for (EventColumn scolumn : columns) {\n            EventColumn tcolumn = translateColumn(data, scolumn, tableHolder, dataMediaPair, translateColumnNames);\n            if (tcolumn != null) {\n                tcolumns.add(tcolumn);\n            }\n        }\n        return tcolumns;\n    }\n\n    private void translatePkColumn(EventData data, List<EventColumn> pks, List<EventColumn> oldPks,\n                                   List<EventColumn> columns, DataMediaPair dataMediaPair,\n                                   Multimap<String, String> translateColumnNames, TableInfoHolder tableHolder) {\n        if (CollectionUtils.isEmpty(oldPks)) { // 如果不存在主键变更\n            List<EventColumn> tpks = new ArrayList<EventColumn>();\n            for (EventColumn scolumn : pks) {\n                EventColumn tcolumn = translateColumn(data, scolumn, tableHolder, dataMediaPair, translateColumnNames);\n                if (tcolumn != null) {\n                    tpks.add(tcolumn);\n                }\n            }\n\n            data.setKeys(tpks);\n        } else { // 存在主键变更\n            // modify by ljh at 2012-11-07 , 只做view视图映射的转化，不再做update table xxx\n            // set pk = newPK where pk = oldPk的处理\n            List<EventColumn> tnewPks = new ArrayList<EventColumn>();\n            List<EventColumn> toldPks = new ArrayList<EventColumn>();\n            for (int i = 0; i < pks.size(); i++) {\n                EventColumn newPk = pks.get(i);\n                EventColumn oldPk = oldPks.get(i);\n                // 转化new pk\n                EventColumn tnewPk = translateColumn(data, newPk, tableHolder, dataMediaPair, translateColumnNames);\n                if (tnewPk != null) {\n                    tnewPks.add(tnewPk);\n                    // 转化old pk，这里不能再用translateColumnNames了，因为转化new\n                    // pk已经remove过一次view name了\n                    EventColumn transEventColumn = translateColumn(tnewPk, oldPk.getColumnValue(), dataMediaPair);\n                    // modify by yuyiding 20180725 主键的isupdate还是oldpk的isupdate\n                    transEventColumn.setUpdate(oldPk.isUpdate());\n                    toldPks.add(transEventColumn);\n                }\n            }\n\n            data.setKeys(tnewPks);\n            data.setOldKeys(toldPks);\n\n            // 主键变更构建的sql规则如下：\n            // update table xxx set pk = newPK where pk = oldPk;\n            // for (int i = 0; i < pks.size(); i++) {\n            // EventColumn scolumn = pks.get(i);\n            // EventColumn oldPk = oldPks.get(i);\n            //\n            // EventColumn updatePk = translateColumn(scolumn, tableHolder,\n            // dataMediaPair, translateColumnNames);\n            // if (scolumn.getColumnValue().equals(oldPk.getColumnValue())) {//\n            // 主键内容没变更\n            // tcolumns.add(updatePk);\n            // } else {\n            // columns.add(updatePk);// 添加到变更字段中, 设置set pk = newPK的内容\n            // // 设置where pk = oldPk的条件\n            // tcolumns.add(translateColumn(updatePk, oldPk.getColumnValue(),\n            // dataMediaPair));\n            // }\n            // }\n        }\n    }\n\n    private EventColumn translateColumn(EventData data, EventColumn scolumn, TableInfoHolder tableHolder,\n                                        DataMediaPair dataMediaPair, Multimap<String, String> translateColumnNames) {\n        EventType type = data.getEventType();\n        EventColumn tcolumn = new EventColumn();\n        tcolumn.setNull(scolumn.getColumnValue() == null);\n        tcolumn.setKey(scolumn.isKey());// 左右两边的主键值必须保持一样，可以不为物理主键\n        tcolumn.setIndex(scolumn.getIndex());\n        tcolumn.setUpdate(scolumn.isUpdate());\n\n        String columnName = translateColumnName(scolumn.getColumnName(), dataMediaPair, translateColumnNames);\n        if (StringUtils.isBlank(columnName)) {\n            throw new TransformException(\"can't translate column name:\" + scolumn.getColumnName() + \"in pair:\"\n                                         + dataMediaPair.toString());\n        }\n\n        // 特殊处理\n        // columnName = StringUtils.remove(columnName, \"`\"); //\n        // 处理下特殊字符，eromanga给了错误的字段名\n        tcolumn.setColumnName(columnName);\n        tcolumn.setColumnType(scolumn.getColumnType());// 不反查，直接使用源库的类型\n        if (tableHolder != null) {\n            // modify by ljh at 2013-01-23\n            // 双向同步新增字段，在一边加了字段后，虽然新的字段没有产生业务变化，但会因为某些原因导致传递了新的字段到T模块\n            // 此时在目标库并不存在这个字段，导致一直挂起。ps. mysql新增字段时间不是一般的长\n            // 所以，做了一个容错处理，针对目标库不存在的字段，如果变更记录在源库不存在变更，并且是null值的，允许丢弃该字段(其实最好还是要判断源库的column的defaultValue和当前值是否一致)\n            boolean canColumnsNotExist = tableHolder.isEnableCompatibleMissColumn();\n            if (type == EventType.UPDATE) {\n                // 非变更字段，且当前值为null\n                canColumnsNotExist &= !scolumn.isUpdate() && scolumn.isNull();\n            } else if (type == EventType.INSERT) {\n                // 当前值为null\n                canColumnsNotExist &= scolumn.isNull();\n            } else if (type == EventType.DELETE) {\n                canColumnsNotExist &= !scolumn.isKey(); // 主键不允许不存在\n            }\n\n            Column matchDbColumn = getMatchColumn(tableHolder.getTable().getColumns(), tcolumn.getColumnName());\n            // 匹配字段为空，可能源库发生过DDL操作，目标库重新载入一下meta信息\n            if (matchDbColumn == null) { // 尝试reload一下table meta\n                // 获取目标库的表信息\n                DbMediaSource dbMediaSource = (DbMediaSource) dataMediaPair.getTarget().getSource();\n                DbDialect dbDialect = dbDialectFactory.getDbDialect(dataMediaPair.getPipelineId(), dbMediaSource);\n                String schemaName = tableHolder.getTable().getSchema();\n                if (StringUtils.isEmpty(schemaName)) {\n                    schemaName = tableHolder.getTable().getCatalog();\n                }\n                Table table = dbDialect.findTable(schemaName, tableHolder.getTable().getName(), false); // 强制反查一次，并放入cache\n\n                tableHolder.setTable(table);\n                matchDbColumn = getMatchColumn(tableHolder.getTable().getColumns(), tcolumn.getColumnName());\n                if (matchDbColumn == null) {\n                    if (canColumnsNotExist) {\n                        return null;\n                    } else {\n                        throw new TransformException(scolumn.getColumnName() + \" is not found in \" + table.toString()\n                                                     + \" and source : \" + dataMediaPair.getTarget().getNamespace()\n                                                     + \".\" + dataMediaPair.getTarget().getName());\n                    }\n                }\n            }\n\n            if (tableHolder.isUseTableTransform()) {\n                int sqlType = matchDbColumn.getTypeCode();\n                tcolumn.setColumnType(sqlType);\n            }\n        }\n\n        // if (dataMediaPair.getTarget().getSource().getType().isOracle()) {\n        // // 特殊处理下oracle编码\n        // String encodeValue = SqlUtils.encoding(scolumn.getColumnValue(),\n        // scolumn.getColumnType(),\n        // dataMediaPair.getSource().getSource().getEncode(),\n        // dataMediaPair.getTarget().getSource().getEncode());\n        // tcolumn.setColumnValue(encodeValue);\n        // } else {\n        // mysql编码转化已经在驱动层面上完成\n        tcolumn.setColumnValue(scolumn.getColumnValue());\n        // }\n        translateColumnNames.remove(scolumn.getColumnName(), columnName);// 删除映射关系，避免下次重复转换\n        return tcolumn;\n    }\n\n    // 根据pk的值 + oldPk的value重新构造一个column对象，用于where pk = oldValue\n    private EventColumn translateColumn(EventColumn scolumn, String newValue, DataMediaPair dataMediaPair) {\n        EventColumn tcolumn = new EventColumn();\n        tcolumn.setNull(newValue == null);\n        tcolumn.setKey(scolumn.isKey());// 左右两边的主键值必须保持一样，可以不为物理主键\n        tcolumn.setIndex(scolumn.getIndex());\n        tcolumn.setColumnName(scolumn.getColumnName());\n        tcolumn.setColumnType(scolumn.getColumnType());\n        tcolumn.setUpdate(scolumn.isUpdate());\n        // if (dataMediaPair.getTarget().getSource().getType().isOracle()) {\n        // // 特殊处理下oracle编码\n        // String encodeValue = SqlUtils.encoding(newValue,\n        // scolumn.getColumnType(), dataMediaPair.getSource()\n        // .getSource()\n        // .getEncode(), dataMediaPair.getTarget().getSource().getEncode());\n        // tcolumn.setColumnValue(encodeValue);\n        // } else {\n        tcolumn.setColumnValue(newValue);\n        // }\n        return tcolumn;\n    }\n\n    // ============ helper method ============\n\n    /**\n     * 根据名字在manager配置的映射关系，转化为目标的字段名字\n     */\n    private String translateColumnName(String srcColumnName, DataMediaPair dataMediaPair,\n                                       Multimap<String, String> translateDict) {\n        if (dataMediaPair.getColumnPairMode().isExclude() || CollectionUtils.isEmpty(dataMediaPair.getColumnPairs())) {\n            return srcColumnName; // 默认同名\n        }\n\n        Collection<String> tColumnNames = translateDict.get(srcColumnName);\n        if (CollectionUtils.isEmpty(tColumnNames)) {\n            throw new TransformException(srcColumnName + \" is not found in column pairs: \" + translateDict.toString());\n        }\n        String columnName = tColumnNames.iterator().next();\n\n        return columnName;\n    }\n\n    private Column getMatchColumn(Column[] columns, String columnName) {\n        // 目标库字段的类型确定\n        for (Column column : columns) {\n            if (column.getName().equalsIgnoreCase(columnName)) {\n                return column;\n            }\n        }\n\n        return null;\n    }\n\n    // =============== setter / getter =============\n\n    public void setDbDialectFactory(DbDialectFactory dbDialectFactory) {\n        this.dbDialectFactory = dbDialectFactory;\n    }\n\n    /**\n     * 实现可reload的table meta，可替换table属性.\n     * \n     * @author jianghang 2012-5-16 下午04:34:18\n     * @version 4.0.2\n     */\n    static class TableInfoHolder {\n\n        private Table   table;\n        private boolean useTableTransform          = true;\n        private boolean enableCompatibleMissColumn = true;\n\n        public TableInfoHolder(Table table, boolean useTableTransform, boolean enableCompatibleMissColumn){\n            this.useTableTransform = useTableTransform;\n            this.enableCompatibleMissColumn = enableCompatibleMissColumn;\n            this.table = table;\n        }\n\n        public Table getTable() {\n            return table;\n        }\n\n        public void setTable(Table table) {\n            this.table = table;\n        }\n\n        public boolean isUseTableTransform() {\n            return useTableTransform;\n        }\n\n        public void setUseTableTransform(boolean useTableTransform) {\n            this.useTableTransform = useTableTransform;\n        }\n\n        public boolean isEnableCompatibleMissColumn() {\n            return enableCompatibleMissColumn;\n        }\n\n        public void setEnableCompatibleMissColumn(boolean enableCompatibleMissColumn) {\n            this.enableCompatibleMissColumn = enableCompatibleMissColumn;\n        }\n\n    }\n}\n"
  },
  {
    "path": "node/etl/src/main/java/protocol.txt",
    "content": "protoc --java_out=. Batch.proto"
  },
  {
    "path": "node/etl/src/main/resources/jetty/jetty.xml",
    "content": "<?xml version=\"1.0\"?>\n<!DOCTYPE Configure PUBLIC \"-//Jetty//Configure//EN\" \"http://www.eclipse.org/jetty/configure.dtd\">\n\n<!-- =============================================================== -->\n<!-- Configure the Jetty Server                                      -->\n<!--                                                                 -->\n<!-- Documentation of this file format can be found at:              -->\n<!-- http://wiki.eclipse.org/Jetty/Reference/jetty.xml_syntax        -->\n<!--                                                                 -->\n<!-- Additional configuration files are available in $JETTY_HOME/etc -->\n<!-- and can be mixed in.  For example:                              -->\n<!--   java -jar start.jar etc/jetty.xml etc/jetty-ssl.xml           -->\n<!--                                                                 -->\n<!-- See start.ini file for the default configuraton files           -->\n<!-- =============================================================== -->\n\n\n<Configure id=\"Server\" class=\"org.eclipse.jetty.server.Server\">\n\n    <!-- =========================================================== -->\n    <!-- Server Thread Pool                                          -->\n    <!-- =========================================================== -->\n    <Set name=\"ThreadPool\">\n      <!-- Default queued blocking threadpool -->\n      <New class=\"org.eclipse.jetty.util.thread.QueuedThreadPool\">\n        <Set name=\"minThreads\">1</Set>\n        <Set name=\"maxThreads\">250</Set>\n      </New>\n    </Set>\n\n    <!-- =========================================================== -->\n    <!-- Set connectors                                              -->\n    <!-- =========================================================== -->\n    <!-- -->\n    <Call name=\"addConnector\">\n      <Arg>\n          <New class=\"org.eclipse.jetty.server.bio.SocketConnector\">\n            <Set name=\"port\"><Property name=\"jetty.port\" default=\"8080\" /></Set>\n            <Set name=\"forwarded\">true</Set>\n            <Set name=\"forwardedHostHeader\">ignore</Set>\n            <Set name=\"forwardedServerHeader\">ignore</Set>\n            <Set name=\"acceptQueueSize\">256</Set>\n            <Set name=\"statsOn\">false</Set>\n            <Set name=\"maxIdleTime\">600000</Set>\n            <Set name=\"lowResourcesMaxIdleTime\">5000</Set>\n            <Set name=\"requestHeaderSize\">8192</Set>\n\t\t\t<Set name=\"responseHeaderSize\">8192</Set>\n          </New>\n      </Arg>\n    </Call>\n    <!-- \n    <Call name=\"addConnector\">\n      <Arg>\n          <New class=\"org.eclipse.jetty.server.nio.SelectChannelConnector\">\n            <Set name=\"host\"><Property name=\"jetty.host\" /></Set>\n            <Set name=\"port\"><Property name=\"jetty.port\" default=\"${otter_download_port}\"/></Set>\n            <Set name=\"forwarded\">true</Set>\n            <Set name=\"forwardedHostHeader\">ignore</Set>\n            <Set name=\"forwardedServerHeader\">ignore</Set>\n            <Set name=\"maxIdleTime\">600000</Set>\n            <Set name=\"Acceptors\">2</Set>\n            <Set name=\"acceptQueueSize\">256</Set>\n            <Set name=\"statsOn\">false</Set>\n            <Set name=\"confidentialPort\">8443</Set>\n            <Set name=\"lowResourcesConnections\">2000</Set>\n            <Set name=\"lowResourcesMaxIdleTime\">5000</Set>\n            <Set name=\"requestHeaderSize\">8192</Set>\n\t\t\t<Set name=\"responseHeaderSize\">8192</Set>\n          </New>\n      </Arg>\n    </Call>\n     -->\n    <!-- =========================================================== -->\n    <!-- Set handler Collection Structure                            -->\n    <!-- =========================================================== -->\n    <Set name=\"handler\">\n\t\t<New id=\"ServletHandler\" class=\"org.eclipse.jetty.servlet.ServletContextHandler\">\n\t\t   \t<Set name=\"contextPath\">/</Set>\n\t\t   \t<Call name=\"addServlet\">\n\t\t   \t\t<Arg>org.eclipse.jetty.servlet.DefaultServlet</Arg>\n\t\t   \t\t<Arg>/download/*</Arg>\n\t\t   \t</Call>\n\t\t   \t<Get name=\"initParams\">\n\t\t   \t\t<Put name=\"org.eclipse.jetty.servlet.Default.resourceBase\"><Property name=\"jetty.htdocs\" default=\"/tmp\"/></Put>\n\t\t   \t\t<Put name=\"org.eclipse.jetty.servlet.Default.pathInfoOnly\">true</Put>\n\t\t   \t\t<Put name=\"org.eclipse.jetty.servlet.Default.gzip\">false</Put>\n\t\t   \t\t<!-- \n\t\t   \t\t<Put name=\"org.eclipse.jetty.servlet.Default.relativeResourceBase\">/tmp/</Put>\n\t\t   \t\t -->\n\t\t   \t</Get>\n\t\t</New>\n\t</Set>\n\n    <!-- =========================================================== -->\n    <!-- extra options                                               -->\n    <!-- =========================================================== -->\n    <Set name=\"stopAtShutdown\">true</Set>\n    <Set name=\"sendServerVersion\">false</Set>\n    <Set name=\"sendDateHeader\">true</Set>\n    <Set name=\"gracefulShutdown\">1000</Set>\n</Configure>\n"
  },
  {
    "path": "node/etl/src/main/resources/spring/otter-node-common.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<beans xmlns=\"http://www.springframework.org/schema/beans\"\r\n\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\r\n\txmlns:aop=\"http://www.springframework.org/schema/aop\"\r\n\txmlns:tx=\"http://www.springframework.org/schema/tx\"\r\n\txsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd\t   http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd\t   http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd\"\r\n\tdefault-autowire=\"byName\" default-dependency-check=\"none\">\r\n\t\r\n\t<bean id=\"executorService\" class=\"com.alibaba.otter.shared.common.utils.thread.ExecutorServiceFactoryBean\">\r\n\t\t<property name=\"poolSize\" value=\"100\" />\r\n\t\t<property name=\"acceptCount\" value=\"200\" />\r\n\t\t<property name=\"name\" value=\"Otter-Seda-Executor\" />\r\n\t</bean>\r\n\t\r\n\t<!-- 池化配置 -->\r\n\t<bean id=\"executorTemplateGetter\" class=\"com.alibaba.otter.shared.common.utils.thread.ExecutorTemplateGetter\" />\r\n\t<bean id=\"executorTemplate\" class=\"org.springframework.aop.framework.ProxyFactoryBean\">\r\n\t\t<property name=\"optimize\" value=\"false\"/>\r\n   \t\t<property name=\"proxyTargetClass\" value=\"true\" />\r\n\t\t<property name=\"targetSource\" ref=\"executorTemplateTargetSource\" />\r\n\t</bean>\r\n\t<bean id=\"executorTemplateTargetSource\" class=\"org.springframework.aop.target.CommonsPoolTargetSource\" >\r\n\t\t<property name=\"minIdle\" value=\"1\" />\r\n\t\t<property name=\"maxSize\" value=\"-1\" />\r\n\t\t<property name=\"timeBetweenEvictionRunsMillis\" value=\"60000\" /><!-- 1分钟进行一次回收 -->\r\n\t\t<property name=\"minEvictableIdleTimeMillis\" value=\"600000\" /><!-- 10分钟回收空闲的 -->\r\n\t\t<property name=\"targetBeanName\" value=\"executorTemplateTarget\" />\r\n\t</bean>\r\n\t<bean id=\"executorTemplateTarget\" class=\"com.alibaba.otter.shared.common.utils.thread.ExecutorTemplate\" scope=\"prototype\" >\r\n\t\t<property name=\"poolSize\" value=\"5\" />\r\n\t</bean>\r\n\t\r\n\t<bean id=\"stageAggregationCollector\" class=\"com.alibaba.otter.node.etl.common.jmx.StageAggregationCollector\">\r\n\t</bean>\r\n\t\r\n\t<bean id=\"otterController\" class=\"com.alibaba.otter.node.etl.OtterController\" depends-on=\"configClientService\">\r\n\t</bean>\r\n</beans>"
  },
  {
    "path": "node/etl/src/main/resources/spring/otter-node-conflict.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<beans xmlns=\"http://www.springframework.org/schema/beans\"\r\n\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\r\n\txmlns:aop=\"http://www.springframework.org/schema/aop\"\r\n\txmlns:tx=\"http://www.springframework.org/schema/tx\"\r\n\txsi:schemaLocation=\"\t   http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd\t   http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd\t   http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd\"\r\n\tdefault-autowire=\"byName\" default-dependency-check=\"none\">\r\n\r\n\t<!-- conflict module -->\r\n\t<bean id=\"fileBatchConflictDetectService\" class=\"com.alibaba.otter.node.etl.conflict.impl.FileBatchConflictDetectServiceImpl\">\r\n\t\t<property name=\"retry\" value=\"3\" />\r\n\t</bean>\t\r\n</beans>\r\n"
  },
  {
    "path": "node/etl/src/main/resources/spring/otter-node-database.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<beans xmlns=\"http://www.springframework.org/schema/beans\"\r\n\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\r\n\txmlns:aop=\"http://www.springframework.org/schema/aop\"\r\n\txmlns:tx=\"http://www.springframework.org/schema/tx\"\r\n\txsi:schemaLocation=\"\t   http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd\t   http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd\t   http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd\"\r\n\tdefault-autowire=\"byName\" default-dependency-check=\"none\">\r\n\t\r\n\t<!-- dbDialect -->\r\n\t<bean id=\"dbDialectFactory\" class=\"com.alibaba.otter.node.etl.common.db.dialect.DbDialectFactory\">\r\n\t\t<property name=\"dataSourceService\" ref=\"dataSourceService\" />\r\n\t\t<property name=\"dbDialectGenerator\" ref=\"dbDialectGenerator\" />\r\n\t</bean>\r\n\t\r\n\t<bean id=\"dbDialectGenerator\" class=\"com.alibaba.otter.node.etl.common.db.dialect.DbDialectGenerator\">\r\n\t\t<property name=\"defaultLobHandler\" ref=\"defaultLobHandler\" />\r\n\t\t<property name=\"oracleLobHandler\" ref=\"oracleLobHandler\" />\r\n\t</bean>\r\n\t\r\n\t<!--  lob bean -->\r\n\t<bean id=\"defaultLobHandler\" class=\"org.springframework.jdbc.support.lob.DefaultLobHandler\">\r\n\t\t<property name=\"streamAsLob\" value=\"true\" />\r\n\t</bean>\r\n\t<bean id=\"oracleLobHandler\" class=\"org.springframework.jdbc.support.lob.OracleLobHandler\">\r\n\t\t<property name=\"nativeJdbcExtractor\" ref=\"automaticJdbcExtractor\"/>\r\n\t</bean>\r\n\t<bean id=\"automaticJdbcExtractor\" class=\"com.alibaba.otter.node.etl.common.db.lob.AutomaticJdbcExtractor\">\r\n\t\t<property name=\"defaultJdbcExtractor\">\r\n\t\t\t<bean class=\"org.springframework.jdbc.support.nativejdbc.SimpleNativeJdbcExtractor\"/>\r\n\t\t</property>\r\n\t\t<property name=\"extractors\">\r\n\t\t\t<map>\r\n\t\t\t\t<entry key=\"org.apache.commons.dbcp\">\r\n\t\t\t\t\t\t<bean class=\"org.springframework.jdbc.support.nativejdbc.CommonsDbcpNativeJdbcExtractor\" />\r\n\t\t\t\t</entry>\r\n\t\t\t</map>\r\n\t\t</property>\r\n\t</bean>\r\n\t\r\n\t<bean id=\"dataSourceService\" class=\"com.alibaba.otter.node.etl.common.datasource.impl.DBDataSourceService\" scope=\"singleton\">\r\n\t\t<property name=\"dataSourceHandlers\">\r\n\t\t\t<list>\r\n\t\t\t\t<ref bean=\"mediaPushDataSourceHandler\" />\r\n\t\t\t</list>\r\n\t\t</property>\r\n\t</bean>\r\n\t\r\n\t<bean id=\"mediaPushDataSourceHandler\" class=\"com.alibaba.otter.common.push.datasource.media.MediaPushDataSourceHandler\" scope=\"singleton\" />\r\n</beans>"
  },
  {
    "path": "node/etl/src/main/resources/spring/otter-node-extension.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<beans xmlns=\"http://www.springframework.org/schema/beans\"\r\n\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\r\n\txmlns:aop=\"http://www.springframework.org/schema/aop\"\r\n\txmlns:tx=\"http://www.springframework.org/schema/tx\"\r\n\txsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd\t   http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd\t   http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd\"\r\n\tdefault-autowire=\"byName\" default-dependency-check=\"none\">\r\n\r\n\t<bean id=\"jdkCompiler\" class=\"com.alibaba.otter.shared.common.utils.compile.impl.JdkCompiler\" scope=\"singleton\"/>\r\n\t\r\n\t<bean id=\"classPathScanner\" class=\"com.alibaba.otter.shared.common.utils.extension.classpath.ClasspathClassScanner\">\r\n\t</bean>\r\n\t\r\n\t<bean id=\"fileSystemScanner\" class=\"com.alibaba.otter.shared.common.utils.extension.classpath.FileSystemClassScanner\">\r\n\t\t<property name=\"extendsDir\" value=\"${otter.extend.dir}\" />\r\n\t</bean>\r\n\t\r\n\t<bean id=\"extensionFactory\" class=\"com.alibaba.otter.shared.common.utils.extension.DefaultExtensionFactory\" scope=\"singleton\" >\r\n\t</bean>\r\n</beans>\r\n"
  },
  {
    "path": "node/etl/src/main/resources/spring/otter-node-extract.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<beans xmlns=\"http://www.springframework.org/schema/beans\"\r\n\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\r\n\txmlns:aop=\"http://www.springframework.org/schema/aop\"\r\n\txmlns:tx=\"http://www.springframework.org/schema/tx\"\r\n\txsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd\t   http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd\t   http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd\"\r\n\tdefault-autowire=\"byName\" default-dependency-check=\"none\">\r\n\r\n\t<bean id=\"otterExtractorFactory\" class=\"com.alibaba.otter.node.etl.extract.extractor.OtterExtractorFactory\" scope=\"singleton\">\r\n\t\t<property name=\"dbBatchExtractor\">\r\n\t\t\t<list>\r\n\t\t\t\t<value>freedomExtractor</value>\r\n\t\t\t\t<value>groupExtractor</value>\r\n\t\t\t\t<value>databaseExtractor</value>\r\n\t\t\t\t<value>processorExtractor</value>\r\n\t\t\t\t<value>fileExtractor</value>\r\n\t\t\t\t<value>viewExtractor</value>\r\n\t\t\t</list>\r\n\t\t</property>\r\n\t</bean> \r\n\t\r\n\t<!-- 池化配置 -->\r\n\t<bean id=\"databaseExtractor\" class=\"org.springframework.aop.framework.ProxyFactoryBean\">\r\n\t\t<property name=\"optimize\" value=\"false\"/>\r\n   \t\t<property name=\"proxyTargetClass\" value=\"true\" />\r\n\t\t<property name=\"targetSource\" ref=\"databaseExtractorTargetSource\" />\r\n\t</bean>\r\n\t<bean id=\"databaseExtractorTargetSource\" class=\"org.springframework.aop.target.CommonsPoolTargetSource\" >\r\n\t\t<property name=\"minIdle\" value=\"1\" />\r\n\t\t<property name=\"maxSize\" value=\"-1\" />\r\n\t\t<property name=\"timeBetweenEvictionRunsMillis\" value=\"60000\" /><!-- 1分钟进行一次回收 -->\r\n\t\t<property name=\"minEvictableIdleTimeMillis\" value=\"600000\" /><!-- 10分钟回收空闲的 -->\r\n\t\t<property name=\"targetBeanName\" value=\"databaseExtractorTarget\" />\r\n\t</bean>\r\n\t<bean id=\"databaseExtractorTarget\" class=\"com.alibaba.otter.node.etl.extract.extractor.DatabaseExtractor\" scope=\"prototype\" >\r\n\t\t<property name=\"poolSize\" value=\"5\" />\r\n\t</bean>\r\n\t\r\n\t<bean id=\"fileExtractor\" class=\"com.alibaba.otter.node.etl.extract.extractor.FileExtractor\" scope=\"singleton\" >\r\n\t</bean>\r\n\t\r\n\t<bean id=\"freedomExtractor\" class=\"com.alibaba.otter.node.etl.extract.extractor.FreedomExtractor\" scope=\"singleton\" >\r\n\t</bean>\r\n\t\r\n\t<bean id=\"viewExtractor\" class=\"com.alibaba.otter.node.etl.extract.extractor.ViewExtractor\" scope=\"singleton\" >\r\n\t</bean>\r\n\t\r\n\t<bean id=\"groupExtractor\" class=\"com.alibaba.otter.node.etl.extract.extractor.GroupExtractor\" scope=\"singleton\" >\r\n\t</bean>\r\n\t\r\n\t<bean id=\"processorExtractor\" class=\"com.alibaba.otter.node.etl.extract.extractor.ProcessorExtractor\" scope=\"singleton\" >\r\n\t</bean>\r\n</beans>\r\n"
  },
  {
    "path": "node/etl/src/main/resources/spring/otter-node-jmx.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<beans xmlns=\"http://www.springframework.org/schema/beans\"\r\n\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:aop=\"http://www.springframework.org/schema/aop\"\r\n\txmlns:tx=\"http://www.springframework.org/schema/tx\"\r\n\txsi:schemaLocation=\"\t   http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd\t   http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd\t   http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd\"\r\n\tdefault-dependency-check=\"none\">\r\n\r\n\t<!-- jmx -->\r\n\t<!--\r\n\t\t<bean id=\"mbeanServer\"\r\n\t\tclass=\"org.springframework.jmx.support.MBeanServerFactoryBean\"/>\r\n\t-->\r\n\t\r\n\t<bean id=\"assembler\"\r\n\t\tclass=\"org.springframework.jmx.export.assembler.InterfaceBasedMBeanInfoAssembler\">\r\n\t\t<property name=\"interfaceMappings\">\r\n\t\t\t<value>\r\n\t\t\t\tbean\\:name\\=otterControllor=com.alibaba.otter.node.etl.OtterControllerMBean\r\n\t\t\t</value>\r\n\t\t</property>\r\n\t</bean>\r\n\t\r\n\t<bean id=\"mbeanServerConnector\" class=\"com.alibaba.otter.node.etl.common.jmx.JmxConnectorServerFactoryBean\">\r\n\t\t<property name=\"configClientService\" ref=\"configClientService\" />\r\n\t</bean>\r\n\t\r\n\t<bean id=\"exporter\" class=\"org.springframework.jmx.export.MBeanExporter\">\r\n\t\t<property name=\"beans\">\r\n\t\t\t<map>\r\n\t\t\t\t<entry key=\"bean:name=otterControllor\" value-ref=\"otterController\" />\r\n\t\t\t\t<entry key=\"connectors:protocol=rmi\" value-ref=\"mbeanServerConnector\" />\r\n\t\t\t</map>\r\n\t\t</property>\r\n\t\t<property name=\"assembler\" ref=\"assembler\" />\r\n\t</bean>\r\n</beans>"
  },
  {
    "path": "node/etl/src/main/resources/spring/otter-node-load.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<beans xmlns=\"http://www.springframework.org/schema/beans\"\r\n\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\r\n\txmlns:aop=\"http://www.springframework.org/schema/aop\"\r\n\txmlns:tx=\"http://www.springframework.org/schema/tx\"\r\n\txsi:schemaLocation=\"\t   http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd\t   http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd\t   http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd\"\r\n\tdefault-autowire=\"byName\" default-dependency-check=\"none\">\r\n\t\r\n\t<bean id=\"loadStatsTracker\" class=\"com.alibaba.otter.node.etl.load.loader.LoadStatsTracker\">\r\n\t</bean>\r\n\t\r\n\t<!-- loader module -->\r\n\t<bean id=\"otterLoaderFactory\" class=\"com.alibaba.otter.node.etl.load.loader.OtterLoaderFactory\" >\r\n\t\t<property name=\"dataBatchLoader\" ref=\"dataBatchLoader\" />\r\n\t</bean>\r\n\t<bean id=\"dataBatchLoader\" class=\"com.alibaba.otter.node.etl.load.loader.db.DataBatchLoader\">\r\n\t\t<property name=\"dbInterceptor\" ref=\"dbLoadInterceptor\" />\r\n\t</bean>\r\n\t<bean id=\"dbLoadAction\" class=\"org.springframework.aop.framework.ProxyFactoryBean\">\r\n\t\t<property name=\"optimize\" value=\"false\"/>\r\n   \t\t<property name=\"proxyTargetClass\" value=\"true\" />\r\n\t\t<property name=\"targetSource\" ref=\"dbLoadActionTargetSource\" />\r\n\t</bean>\r\n\t<bean id=\"dbLoadActionTargetSource\" class=\"org.springframework.aop.target.CommonsPoolTargetSource\" >\r\n\t\t<property name=\"minIdle\" value=\"1\" />\r\n\t\t<property name=\"maxSize\" value=\"-1\" />\r\n\t\t<property name=\"timeBetweenEvictionRunsMillis\" value=\"60000\" /><!-- 1分钟进行一次回收 -->\r\n\t\t<property name=\"minEvictableIdleTimeMillis\" value=\"600000\" /><!-- 10分钟回收空闲的 -->\r\n\t\t<property name=\"targetBeanName\" value=\"dbLoadActionTarget\" />\r\n\t</bean>\r\n\t<bean id=\"dbLoadActionTarget\" class=\"com.alibaba.otter.node.etl.load.loader.db.DbLoadAction\" scope=\"prototype\">\r\n\t\t<property name=\"retry\" value=\"3\" />\r\n\t\t<property name=\"retryWait\" value=\"3000\" />\r\n\t\t<property name=\"poolSize\" value=\"5\" />\r\n\t\t<property name=\"interceptor\" ref=\"dbLoadInterceptor\" />\r\n\t</bean>\r\n\t\r\n\t<bean id=\"fileLoadAction\" class=\"org.springframework.aop.framework.ProxyFactoryBean\">\r\n\t\t<property name=\"optimize\" value=\"false\"/>\r\n   \t\t<property name=\"proxyTargetClass\" value=\"true\" />\r\n\t\t<property name=\"targetSource\" ref=\"fileLoadActionTargetSource\" />\r\n\t</bean>\r\n\t<bean id=\"fileLoadActionTargetSource\" class=\"org.springframework.aop.target.CommonsPoolTargetSource\">\r\n\t\t<property name=\"minIdle\" value=\"1\" />\r\n\t\t<property name=\"maxSize\" value=\"-1\" />\r\n\t\t<property name=\"timeBetweenEvictionRunsMillis\" value=\"60000\" /><!-- 1分钟进行一次回收 -->\r\n\t\t<property name=\"minEvictableIdleTimeMillis\" value=\"600000\" /><!-- 10分钟回收空闲的 -->\r\n\t\t<property name=\"targetBeanName\" value=\"fileLoadActionTarget\" />\r\n\t</bean>\r\n\t<bean id=\"fileLoadActionTarget\" class=\"com.alibaba.otter.node.etl.load.loader.db.FileLoadAction\" scope=\"prototype\">\r\n\t\t<property name=\"retry\" value=\"3\" />\r\n\t\t<property name=\"poolSize\" value=\"5\" />\r\n\t</bean>\r\n\t\r\n\t<!-- interceptor -->\r\n\t<bean id=\"dbLoadInterceptor\" class=\"com.alibaba.otter.node.etl.load.loader.interceptor.ChainLoadInterceptor\" >\r\n\t\t<property name=\"interceptors\">\r\n\t\t\t<list>\r\n\t\t\t\t<ref bean=\"sqlBuilderLoadInterceptor\" />\r\n\t\t\t\t<ref bean=\"operationInterceptorFactory\" />\r\n\t\t\t\t<ref bean=\"dbLogLoadInterceptor\" />\r\n\t\t\t</list>\r\n\t\t</property>\r\n\t</bean>\r\n\t\r\n\t<bean id=\"dbLogLoadInterceptor\" class=\"com.alibaba.otter.node.etl.load.loader.db.interceptor.log.LogLoadInterceptor\">\r\n\t\t<property name=\"dump\" value=\"true\" />\r\n\t</bean>\r\n\t<bean id=\"sqlBuilderLoadInterceptor\" class=\"com.alibaba.otter.node.etl.load.loader.db.interceptor.sql.SqlBuilderLoadInterceptor\" >\r\n\t</bean>\r\n\t<!-- retl_client处理 -->\r\n\t<bean id=\"abstractOperationInterceptor\" abstract=\"true\">\r\n\t\t<property name=\"innerIdCount\" value=\"300\"/>\r\n\t\t<property name=\"globalIdCount\" value=\"1000\"/>\r\n\t</bean>\r\n\t<bean id=\"canalMysqlInterceptor\" class=\"com.alibaba.otter.node.etl.load.loader.db.interceptor.operation.CanalMysqlInterceptor\" parent=\"abstractOperationInterceptor\" />\r\n\t<bean id=\"canalOracleInterceptor\" class=\"com.alibaba.otter.node.etl.load.loader.db.interceptor.operation.CanalOracleInterceptor\" parent=\"abstractOperationInterceptor\" />\r\n\t<bean id=\"operationInterceptorFactory\" class=\"com.alibaba.otter.node.etl.load.loader.db.interceptor.operation.OperationInterceptorFactory\">\r\n\t\t<property name=\"mysqlInterceptors\">\r\n\t\t\t<list>\r\n\t\t\t\t<ref local=\"canalMysqlInterceptor\"/> \r\n\t\t\t</list>\r\n\t\t</property>\r\n\t\t<property name=\"oracleInterceptors\">\r\n\t\t\t<list>\r\n\t\t\t\t<ref local=\"canalOracleInterceptor\"/> \r\n\t\t\t</list>\r\n\t\t</property>\r\n\t</bean>  \r\n</beans>"
  },
  {
    "path": "node/etl/src/main/resources/spring/otter-node-pipe.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<beans xmlns=\"http://www.springframework.org/schema/beans\"\r\n\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\r\n\txmlns:aop=\"http://www.springframework.org/schema/aop\"\r\n\txmlns:tx=\"http://www.springframework.org/schema/tx\"\r\n\txsi:schemaLocation=\"\t   http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd\t   http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd\t   http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd\"\r\n\tdefault-autowire=\"byName\" default-dependency-check=\"none\">\r\n\t\r\n\t<bean id=\"rowDataMemoryPipe\" class=\"com.alibaba.otter.node.etl.common.pipe.impl.memory.RowDataMemoryPipe\"  scope=\"singleton\" >\r\n\t\t<property name=\"timeout\" value=\"600000\" /> <!-- 10分钟过期,by zebin -->\r\n\t\t<property name=\"downloadDir\" value=\"${otter.download.dir}\" />\r\n\t\t<property name=\"retry\" value=\"3\" />\r\n\t</bean>\r\n\t\r\n\t<bean id=\"rowDataPipeDelegate\" class=\"com.alibaba.otter.node.etl.common.pipe.impl.RowDataPipeDelegate\" scope=\"singleton\">\r\n\t</bean>\r\n\t\r\n\t<!-- http服务 -->\r\n\t<bean id=\"rowDataHttpPipe\" class=\"com.alibaba.otter.node.etl.common.pipe.impl.http.RowDataHttpPipe\"  scope=\"singleton\" >\r\n\t\t<property name=\"timeout\" value=\"600000\" /> <!-- 10分钟过期 -->\r\n\t\t<property name=\"period\" value=\"60000\" /> <!-- 1分钟检查一次 -->\r\n\t\t<property name=\"htdocsDir\" value=\"${otter.htdocs.dir}\" />\r\n\t\t<property name=\"downloadDir\" value=\"${otter.download.dir}\" />\r\n\t\t<property name=\"remoteUrlBuilder\" ref=\"remoteUrlBuilder\" />\r\n\t</bean>\r\n\t<bean id=\"attachmentHttpPipe\" class=\"com.alibaba.otter.node.etl.common.pipe.impl.http.AttachmentHttpPipe\"  scope=\"singleton\">\r\n\t\t<property name=\"timeout\" value=\"600000\" /> <!-- 10分钟过期 -->\r\n\t\t<property name=\"period\" value=\"60000\" /> <!-- 1分钟检查一次 -->\r\n\t\t<property name=\"htdocsDir\" value=\"${otter.htdocs.dir}\" />\r\n\t\t<property name=\"downloadDir\" value=\"${otter.download.dir}\" />\r\n\t\t<property name=\"remoteUrlBuilder\" ref=\"remoteUrlBuilder\" />\r\n\t</bean>\r\n\t\r\n\t<!-- rpc服务 -->\r\n\t<bean id=\"rowDataRpcPipe\" class=\"com.alibaba.otter.node.etl.common.pipe.impl.rpc.RowDataRpcPipe\"  scope=\"singleton\">\r\n\t\t<property name=\"timeout\" value=\"600000\" /> <!-- 10分钟过期,by zebin -->\r\n\t</bean>\r\n\t\r\n\t<!-- jetty服务 -->\r\n\t<bean id=\"jettyEmbedServer\" class=\"com.alibaba.otter.node.etl.common.jetty.JettyEmbedServer\" scope=\"singleton\">\r\n\t\t<property name=\"config\" value=\"jetty/jetty.xml\" />\r\n\t\t<property name=\"htdocsDir\" value=\"${otter.htdocs.dir}\" />\r\n\t</bean>\r\n\t<bean id=\"remoteUrlBuilder\" class=\"com.alibaba.otter.node.etl.common.pipe.impl.http.RemoteUrlBuilder\" >\r\n\t\t<property name=\"urlFormat\" value=\"http://{0}:{1}/download/{2}\" /><!-- 0为ip,1为port,2为下载路径  -->\r\n\t</bean>\r\n\t\r\n\t<bean id=\"dataRetrieverFactory\" class=\"com.alibaba.otter.node.etl.common.io.download.DataRetrieverFactory\">\r\n\t</bean>\r\n\t\r\n\t<!-- 池化配置 -->\r\n\t<bean id=\"archiveBean\" class=\"org.springframework.aop.framework.ProxyFactoryBean\">\r\n\t\t<property name=\"optimize\" value=\"false\"/>\r\n   \t\t<property name=\"proxyTargetClass\" value=\"true\" />\r\n\t\t<property name=\"targetSource\" ref=\"archiveBeanTargetSource\" />\r\n\t</bean>\r\n\t<bean id=\"archiveBeanTargetSource\" class=\"org.springframework.aop.target.CommonsPoolTargetSource\" >\r\n\t\t<property name=\"minIdle\" value=\"1\" />\r\n\t\t<property name=\"maxSize\" value=\"-1\" />\r\n\t\t<property name=\"timeBetweenEvictionRunsMillis\" value=\"60000\" /><!-- 1分钟进行一次回收 -->\r\n\t\t<property name=\"minEvictableIdleTimeMillis\" value=\"600000\" /><!-- 10分钟回收空闲的 -->\r\n\t\t<property name=\"targetBeanName\" value=\"archiveBeanTarget\" />\r\n\t</bean>\r\n\t<bean id=\"archiveBeanTarget\" class=\"com.alibaba.otter.node.etl.common.pipe.impl.http.archive.ArchiveBean\" scope=\"prototype\" >\r\n\t\t<property name=\"poolSize\" value=\"5\" />\r\n\t</bean>\r\n</beans>"
  },
  {
    "path": "node/etl/src/main/resources/spring/otter-node-select.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<beans xmlns=\"http://www.springframework.org/schema/beans\"\r\n\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\r\n\txmlns:aop=\"http://www.springframework.org/schema/aop\"\r\n\txmlns:tx=\"http://www.springframework.org/schema/tx\"\r\n\txsi:schemaLocation=\"\t   http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd\t   http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd\t   http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd\"\r\n\tdefault-autowire=\"byName\" default-dependency-check=\"none\">\r\n\r\n\t<bean id=\"messageParser\" class=\"com.alibaba.otter.node.etl.select.selector.MessageParser\" />\r\n\r\n\t<bean id=\"otterSelectorFactory\" class=\"com.alibaba.otter.node.etl.select.selector.OtterSelectorFactory\" />\r\n</beans>"
  },
  {
    "path": "node/etl/src/main/resources/spring/otter-node-transform.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<beans xmlns=\"http://www.springframework.org/schema/beans\"\r\n\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\r\n\txmlns:aop=\"http://www.springframework.org/schema/aop\"\r\n\txmlns:tx=\"http://www.springframework.org/schema/tx\"\r\n\txsi:schemaLocation=\"\t   http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd\t   http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd\t   http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd\"\r\n\tdefault-autowire=\"byName\" default-dependency-check=\"none\">\r\n\r\n\t<!-- transform module -->\r\n\t<bean id=\"otterTransformerFactory\" class=\"com.alibaba.otter.node.etl.transform.transformer.OtterTransformerFactory\">\r\n\t\t<property name=\"rowDataTransformer\" ref=\"rowDataTransformer\" />\r\n\t\t<property name=\"fileDataTransformer\" ref=\"fileDataTransformer\" />\r\n\t</bean>\r\n\t<bean id=\"rowDataTransformer\" class=\"com.alibaba.otter.node.etl.transform.transformer.RowDataTransformer\" />\r\n\t<bean id=\"fileDataTransformer\" class=\"com.alibaba.otter.node.etl.transform.transformer.FileDataTransformer\" />\r\n</beans>"
  },
  {
    "path": "node/etl/src/test/java/com/alibaba/otter/node/etl/ArbitrateRemoteServiceIntegration.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl;\n\nimport java.util.concurrent.TimeUnit;\n\nimport org.jtester.annotations.SpringBeanByName;\nimport org.testng.annotations.BeforeClass;\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.node.common.config.ConfigClientService;\nimport com.alibaba.otter.shared.arbitrate.impl.alarm.AlarmClientService;\n\n/**\n * 测试下仲裁器报警处理\n * \n * @author jianghang 2011-11-29 上午09:52:18\n * @version 4.0.0\n */\npublic class ArbitrateRemoteServiceIntegration extends BaseOtterTest {\n\n    @SpringBeanByName\n    private ConfigClientService configClientService;\n\n    @SpringBeanByName\n    private AlarmClientService  alarmClientService;\n\n    @BeforeClass\n    public void initial() {\n        System.setProperty(\"nid\", \"1\");\n    }\n\n    @Test\n    public void test_send() {\n        alarmClientService.sendAlarm(3L, -1L, null, \"load is interrupt!\");\n\n        try {\n            TimeUnit.MILLISECONDS.sleep(10000L);\n        } catch (InterruptedException e) {\n            want.fail();\n        }\n    }\n}\n"
  },
  {
    "path": "node/etl/src/test/java/com/alibaba/otter/node/etl/BaseDbTest.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl;\n\nimport com.alibaba.otter.shared.common.model.config.data.DataMediaType;\nimport com.alibaba.otter.shared.common.model.config.data.db.DbDataMedia;\nimport com.alibaba.otter.shared.common.model.config.data.db.DbMediaSource;\n\npublic class BaseDbTest extends BaseOtterTest {\n\n    public DbDataMedia getMysqlMedia() {\n        DbMediaSource dbMediaSource = new DbMediaSource();\n        dbMediaSource.setId(10L);\n        dbMediaSource.setDriver(\"com.mysql.jdbc.Driver\");\n        dbMediaSource.setUsername(\"xxxxx\");\n        dbMediaSource.setPassword(\"xxxxx\");\n        dbMediaSource.setUrl(\"jdbc:mysql://127.0.0.1:3306/srf\");\n        dbMediaSource.setEncode(\"UTF-8\");\n        dbMediaSource.setType(DataMediaType.MYSQL);\n\n        DbDataMedia dataMedia = new DbDataMedia();\n        dataMedia.setSource(dbMediaSource);\n        dataMedia.setId(1L);\n        dataMedia.setName(\"columns\");\n        dataMedia.setNamespace(\"srf\");\n        return dataMedia;\n    }\n\n    public DbDataMedia getOracleMedia() {\n        DbMediaSource dbMediaSource = new DbMediaSource();\n        dbMediaSource.setId(11L);\n        dbMediaSource.setDriver(\"oracle.jdbc.OracleDriver\");\n        dbMediaSource.setUsername(\"xxxxx\");\n        dbMediaSource.setPassword(\"xxxxx\");\n        dbMediaSource.setUrl(\"jdbc:oracle:thin:@127.0.0.1:1521:crmgsb\");\n        dbMediaSource.setEncode(\"UTF-8\");\n        dbMediaSource.setType(DataMediaType.ORACLE);\n\n        DbDataMedia dataMedia = new DbDataMedia();\n        dataMedia.setSource(dbMediaSource);\n        dataMedia.setId(2L);\n        dataMedia.setName(\"columns\");\n        dataMedia.setNamespace(\"srf\");\n        return dataMedia;\n    }\n\n}\n"
  },
  {
    "path": "node/etl/src/test/java/com/alibaba/otter/node/etl/BaseOtterTest.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl;\n\nimport org.jtester.annotations.SpringApplicationContext;\n\n/**\n * @author jianghang 2011-9-16 下午02:58:37\n * @version 4.0.0\n */\n@SpringApplicationContext(\"applicationContext.xml\")\npublic class BaseOtterTest extends org.jtester.testng.JTester {\n\n}\n"
  },
  {
    "path": "node/etl/src/test/java/com/alibaba/otter/node/etl/SqlUtilsTest.java",
    "content": "package com.alibaba.otter.node.etl;\n\nimport java.sql.Types;\n\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.node.etl.common.db.utils.SqlUtils;\n\npublic class SqlUtilsTest extends BaseOtterTest {\n\n    @Test\n    public void testClob() {\n        Object result = SqlUtils.stringToSqlValue(\"\", Types.CLOB, true, false);\n        want.object(result).isEqualTo(\"\");\n\n        // requied = true\n        result = SqlUtils.stringToSqlValue(null, Types.CLOB, true, false);\n        want.object(result).isEqualTo(\" \");\n\n        result = SqlUtils.stringToSqlValue(null, Types.CLOB, true, true);\n        want.object(result).isEqualTo(\" \");\n\n        // requied = false\n        result = SqlUtils.stringToSqlValue(null, Types.CLOB, false, false);\n        want.object(result).isEqualTo(null);\n\n        result = SqlUtils.stringToSqlValue(\"\", Types.CLOB, false, true);\n        want.object(result).isEqualTo(null);\n    }\n\n    @Test\n    public void testBlob() {\n        Object result = SqlUtils.stringToSqlValue(\"\", Types.INTEGER, true, false);\n        want.object(result).isEqualTo(\"\");\n\n        // requied = true\n        result = SqlUtils.stringToSqlValue(null, Types.BLOB, true, false);\n        want.object(result).isEqualTo(null);\n\n        result = SqlUtils.stringToSqlValue(null, Types.BLOB, true, true);\n        want.object(result).isEqualTo(null);\n\n        // requied = false\n        result = SqlUtils.stringToSqlValue(null, Types.BLOB, false, false);\n        want.object(result).isEqualTo(null);\n\n        result = SqlUtils.stringToSqlValue(\"\", Types.BLOB, false, true);\n        want.object(result).isEqualTo(\"\");\n    }\n}\n"
  },
  {
    "path": "node/etl/src/test/java/com/alibaba/otter/node/etl/TestUtils.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl;\n\nimport java.lang.management.ManagementFactory;\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Method;\n\nimport org.springframework.util.ReflectionUtils;\n\n/**\n * 提供常见的测试方法\n * \n * @author jianghang 2011-1-30 上午11:15:54\n */\npublic class TestUtils {\n\n    /**\n     * 获取对应属性的值\n     * \n     * @param obj\n     * @param fieldName\n     * @return\n     */\n    public static Object getField(Object obj, String fieldName) {\n        Field field = ReflectionUtils.findField(obj.getClass(), fieldName);\n        ReflectionUtils.makeAccessible(field);\n        return ReflectionUtils.getField(field, obj);\n    }\n\n    /**\n     * 设置对应参数的值\n     * \n     * @param target\n     * @param methodName\n     * @param args\n     * @return\n     * @throws Exception\n     */\n    public static void setField(Object target, String fieldName, Object args) throws Exception {\n        // 查找对应的方法\n        Field field = ReflectionUtils.findField(target.getClass(), fieldName);\n        ReflectionUtils.makeAccessible(field);\n        ReflectionUtils.setField(field, target, args);\n    }\n\n    /**\n     * 调用方法，可以是一些私有方法\n     * \n     * @param target\n     * @param methodName\n     * @param args\n     * @return\n     * @throws Exception\n     */\n    public static Object invokeMethod(Object target, String methodName, Object... args) throws Exception {\n        Method method = null;\n        // 查找对应的方法\n        if (args == null || args.length == 0) {\n            method = ReflectionUtils.findMethod(target.getClass(), methodName);\n        } else {\n            Class[] argsClass = new Class[args.length];\n            for (int i = 0; i < args.length; i++) {\n                argsClass[i] = args[i].getClass();\n            }\n            method = ReflectionUtils.findMethod(target.getClass(), methodName, argsClass);\n        }\n        ReflectionUtils.makeAccessible(method);\n\n        if (args == null || args.length == 0) {\n            return ReflectionUtils.invokeMethod(method, target);\n        } else {\n            return ReflectionUtils.invokeMethod(method, target, args);\n        }\n    }\n\n    public static void restoreJvm() {\n        int maxRestoreJvmLoops = 10;\n        long memUsedPrev = memoryUsed();\n        for (int i = 0; i < maxRestoreJvmLoops; i++) {\n            System.runFinalization();\n            System.gc();\n\n            long memUsedNow = memoryUsed();\n            // break early if have no more finalization and get constant mem used\n            if ((ManagementFactory.getMemoryMXBean().getObjectPendingFinalizationCount() == 0)\n                && (memUsedNow >= memUsedPrev)) {\n                break;\n            } else {\n                memUsedPrev = memUsedNow;\n            }\n        }\n    }\n\n    public static long memoryUsed() {\n        Runtime rt = Runtime.getRuntime();\n        return rt.totalMemory() - rt.freeMemory();\n    }\n}\n"
  },
  {
    "path": "node/etl/src/test/java/com/alibaba/otter/node/etl/common/datasource/AbstractDbDialectTest.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.datasource;\n\nimport javax.sql.DataSource;\n\nimport org.apache.commons.dbcp.BasicDataSource;\nimport org.apache.commons.lang.StringUtils;\nimport org.apache.ddlutils.model.Column;\nimport org.apache.ddlutils.model.Table;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.shared.common.model.config.data.DataMediaType;\nimport com.alibaba.otter.shared.common.utils.meta.DdlUtils;\n\n/**\n * 类TestAbstractDbDialect.java的实现描述：TODO 类实现描述\n * \n * @author xiaoqing.zhouxq 2011-12-9 下午3:03:55\n */\npublic class AbstractDbDialectTest {\n\n    @Test\n    public void testFindTable() throws Exception {\n        DataSource dataSource = createDataSource(\"jdbc:oracle:thin:@127.0.0.1:1521:OINTEST\", \"otter1\", \"jonathan\",\n                                                 \"oracle.jdbc.OracleDriver\", DataMediaType.ORACLE, \"utf-8\");\n        JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);\n        Table table = DdlUtils.findTable(jdbcTemplate, \"otter1\".toUpperCase(), \"otter1\".toUpperCase(),\n                                         \"wytable3\".toUpperCase());\n        System.out.println(\"the tablename = \" + table.getSchema() + \".\" + table.getName());\n        Column[] columns = table.getColumns();\n        for (Column column : columns) {\n            System.out.println(\"columnName = \" + column.getName() + \",columnType = \" + column.getTypeCode()\n                               + \",isPrimary = \" + column.isPrimaryKey() + \",nullable = \" + column.isRequired());\n        }\n\n    }\n\n    private DataSource createDataSource(String url, String userName, String password, String driverClassName,\n                                        DataMediaType dataMediaType, String encoding) {\n        BasicDataSource dbcpDs = new BasicDataSource();\n\n        dbcpDs.setRemoveAbandoned(true);\n        dbcpDs.setLogAbandoned(true);\n        dbcpDs.setTestOnBorrow(true);\n        dbcpDs.setTestWhileIdle(true);\n\n        // 动态的参数\n        dbcpDs.setDriverClassName(driverClassName);\n        dbcpDs.setUrl(url);\n        dbcpDs.setUsername(userName);\n        dbcpDs.setPassword(password);\n\n        if (dataMediaType.isOracle()) {\n            dbcpDs.addConnectionProperty(\"restrictGetTables\", \"true\");\n            dbcpDs.setValidationQuery(\"select 1 from dual\");\n        } else if (dataMediaType.isMysql()) {\n            // open the batch mode for mysql since 5.1.8\n            dbcpDs.addConnectionProperty(\"useServerPrepStmts\", \"true\");\n            dbcpDs.addConnectionProperty(\"rewriteBatchedStatements\", \"true\");\n            if (StringUtils.isNotEmpty(encoding)) {\n                dbcpDs.addConnectionProperty(\"characterEncoding\", encoding);\n            }\n            dbcpDs.setValidationQuery(\"select 1\");\n        }\n\n        return dbcpDs;\n    }\n}\n"
  },
  {
    "path": "node/etl/src/test/java/com/alibaba/otter/node/etl/common/datasource/TestMysqlUnsignedInt.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.datasource;\n\nimport java.math.BigDecimal;\nimport java.sql.Connection;\nimport java.sql.DriverManager;\nimport java.sql.PreparedStatement;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.util.Properties;\n\n/**\n * 类TestMysqlUnsignedInt.java的实现描述\n * \n * @author xiaoqing.zhouxq 2011-12-23 上午10:03:37\n */\npublic class TestMysqlUnsignedInt {\n\n    public static void insertNumeric() throws ClassNotFoundException, SQLException {\n        Class.forName(\"com.mysql.jdbc.Driver\");\n        Properties from = new Properties();\n        from.put(\"user\", \"root\");\n        from.put(\"password\", \"root\");\n        from.put(\"characterEncoding\", \"utf8\");\n        Connection conn = DriverManager.getConnection(\"jdbc:mysql://localhost:3306/erosa\", from);\n        PreparedStatement pst = conn.prepareStatement(\"insert into unsignednumeric(id,id1,id2,id3) values (?,?,?,?)\");\n        pst.setLong(1, Integer.MAX_VALUE * 2L);\n        pst.setLong(2, Integer.MAX_VALUE);\n        pst.setBigDecimal(3, new BigDecimal(\"18446744073709551614\"));\n        pst.setBigDecimal(4, new BigDecimal(\"9223372036854775807\"));\n        pst.executeUpdate();\n\n        pst.close();\n        conn.close();\n    }\n\n    public static void main(String[] args) throws ClassNotFoundException, SQLException, InterruptedException {\n        insertNumeric();\n\n        Thread.sleep(1000L);\n\n        Class.forName(\"com.mysql.jdbc.Driver\");\n        Properties from = new Properties();\n        from.put(\"user\", \"root\");\n        from.put(\"password\", \"root\");\n        from.put(\"characterEncoding\", \"utf8\");\n        Connection conn = DriverManager.getConnection(\"jdbc:mysql://localhost:3306/erosa\", from);\n        PreparedStatement pst = conn.prepareStatement(\"select id,id1,id2,id3 from unsignednumeric\");\n        ResultSet rs = pst.executeQuery();\n        while (rs.next()) {\n            //            try {\n            //                System.out.println(rs.getInt(1));\n            //            } catch (Exception e) {\n            //                System.out.println(rs.getLong(1));\n            //            }\n            System.out.println(rs.getLong(1));\n            System.out.println(rs.getLong(2));\n            System.out.println(rs.getBigDecimal(3));\n            //            System.out.println(rs.getString(3));\n            System.out.println(rs.getBigDecimal(4));\n            System.out.println(\"-----------------------------\");\n        }\n\n        rs.close();\n        pst.close();\n        conn.close();\n    }\n\n}\n"
  },
  {
    "path": "node/etl/src/test/java/com/alibaba/otter/node/etl/common/datasource/TestMysqlUnsignedNumber.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.datasource;\n\nimport java.math.BigDecimal;\nimport java.sql.Connection;\nimport java.sql.DriverManager;\nimport java.sql.PreparedStatement;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.util.Properties;\n\n/**\n * 类TestMysqlUnsignedInt.java的实现描述\n * \n * @author xiaoqing.zhouxq 2011-12-23 上午10:03:37\n */\npublic class TestMysqlUnsignedNumber {\n\n    public static void insertNumeric() throws ClassNotFoundException, SQLException {\n        Class.forName(\"com.mysql.jdbc.Driver\");\n        Properties from = new Properties();\n        from.put(\"user\", \"root\");\n        from.put(\"password\", \"root\");\n        from.put(\"characterEncoding\", \"utf8\");\n        Connection conn = DriverManager.getConnection(\"jdbc:mysql://localhost:3306/erosa\", from);\n        PreparedStatement pst = conn.prepareStatement(\"insert into unsignednumeric(id,id1,id2,id3) values (?,?,?,?)\");\n        pst.setLong(1, Integer.MAX_VALUE * 2L);\n        pst.setLong(2, Integer.MIN_VALUE);\n        pst.setBigDecimal(3, new BigDecimal(\"18446744073709551614\"));\n        pst.setBigDecimal(4, new BigDecimal(Long.MIN_VALUE + \"\"));\n        pst.executeUpdate();\n\n        pst.close();\n        conn.close();\n    }\n\n    public static void main(String[] args) throws ClassNotFoundException, SQLException, InterruptedException {\n        //        insertNumeric();\n        //        \n        //        Thread.sleep(1000L);\n\n        Class.forName(\"com.mysql.jdbc.Driver\");\n        Properties from = new Properties();\n        from.put(\"user\", \"root\");\n        from.put(\"password\", \"root\");\n        from.put(\"characterEncoding\", \"utf8\");\n        Connection conn = DriverManager.getConnection(\"jdbc:mysql://localhost:3306/erosa\", from);\n        PreparedStatement pst = conn.prepareStatement(\"select id,id1,id3 from unsignednumeric\");\n        ResultSet rs = pst.executeQuery();\n        while (rs.next()) {\n            //            try {\n            //                System.out.println(rs.getInt(1));\n            //            } catch (Exception e) {\n            //                System.out.println(rs.getLong(1));\n            //            }\n            System.out.println(rs.getLong(1));\n            System.out.println(rs.getLong(2));\n            System.out.println(rs.getBigDecimal(3));\n            System.out.println(\"-----------------------------\");\n        }\n\n        rs.close();\n        pst.close();\n        conn.close();\n    }\n\n}\n"
  },
  {
    "path": "node/etl/src/test/java/com/alibaba/otter/node/etl/common/db/BitTableIntegration.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.db;\n\nimport java.io.UnsupportedEncodingException;\nimport java.sql.PreparedStatement;\nimport java.sql.SQLException;\nimport java.sql.Types;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.apache.ddlutils.model.Table;\nimport org.jtester.annotations.SpringBeanByName;\nimport org.springframework.dao.DataAccessException;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.jdbc.core.PreparedStatementCallback;\nimport org.springframework.jdbc.core.StatementCreatorUtils;\nimport org.springframework.jdbc.support.lob.LobCreator;\nimport org.springframework.transaction.TransactionStatus;\nimport org.springframework.transaction.support.TransactionCallback;\nimport org.springframework.transaction.support.TransactionTemplate;\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.node.etl.BaseDbTest;\nimport com.alibaba.otter.node.etl.common.db.dialect.DbDialect;\nimport com.alibaba.otter.node.etl.common.db.dialect.DbDialectFactory;\nimport com.alibaba.otter.node.etl.common.db.dialect.SqlTemplate;\nimport com.alibaba.otter.node.etl.common.db.dialect.mysql.MysqlDialect;\nimport com.alibaba.otter.node.etl.common.db.utils.SqlUtils;\nimport com.alibaba.otter.shared.common.model.config.data.DataMediaType;\nimport com.alibaba.otter.shared.common.model.config.data.db.DbMediaSource;\n\npublic class BitTableIntegration extends BaseDbTest {\n\n    private static final String SCHEMA_NAME    = \"test\";\n    private static final String TABLE_NAME     = \"test_bit\";\n    @SpringBeanByName\n    private DbDialectFactory    dbDialectFactory;\n\n    private String[]            pkColumns      = { \"id\" };\n    private String[]            columns        = { \"onebit_value\", \"bits_values\" };\n\n    private String[]            pkColumnValues = { \"3\" };\n\n    private String[]            columnValues   = { \"1\", \"63\" };\n\n    @Test\n    public void test_mysql() throws UnsupportedEncodingException {\n        DbMediaSource dbMediaSource = new DbMediaSource();\n        dbMediaSource.setId(10L);\n        dbMediaSource.setDriver(\"com.mysql.jdbc.Driver\");\n        dbMediaSource.setUsername(\"xxxxx\");\n        dbMediaSource.setPassword(\"xxxxx\");\n        dbMediaSource.setUrl(\"jdbc:mysql://127.0.0.1:3306\");\n        dbMediaSource.setEncode(\"UTF-8\");\n        dbMediaSource.setType(DataMediaType.MYSQL);\n\n        final DbDialect dbDialect = dbDialectFactory.getDbDialect(2L, dbMediaSource);\n        want.object(dbDialect).clazIs(MysqlDialect.class);\n\n        Table table = dbDialect.findTable(SCHEMA_NAME, TABLE_NAME);\n        System.out.println(table);\n\n        final SqlTemplate sqlTemplate = dbDialect.getSqlTemplate();\n        final JdbcTemplate jdbcTemplate = dbDialect.getJdbcTemplate();\n        final TransactionTemplate transactionTemplate = dbDialect.getTransactionTemplate();\n        final int[] pkColumnTypes = { Types.INTEGER };\n        final int[] columnTypes = { Types.BIT, Types.BIT };\n        transactionTemplate.execute(new TransactionCallback() {\n\n            public Object doInTransaction(TransactionStatus status) {\n                int affect = 0;\n                String sql = null;\n                // 执行insert\n                sql = sqlTemplate.getInsertSql(SCHEMA_NAME, TABLE_NAME, pkColumns, columns);\n                System.out.println(sql);\n                affect = (Integer) jdbcTemplate.execute(sql, new PreparedStatementCallback() {\n\n                    public Object doInPreparedStatement(PreparedStatement ps) throws SQLException, DataAccessException {\n                        doPreparedStatement(ps,\n                            dbDialect,\n                            toTypes(columnTypes, pkColumnTypes),\n                            toValues(columnValues, pkColumnValues));\n                        return ps.executeUpdate();\n                    }\n\n                });\n                want.number(affect).isEqualTo(1);\n                return null;\n                // throw new RuntimeException(\"rollback\");\n            }\n        });\n\n    }\n\n    private Integer[] toTypes(int[]... types) {\n        List<Integer> result = new ArrayList<Integer>();\n        for (int[] type : types) {\n            for (int t : type) {\n                result.add(t);\n            }\n        }\n\n        return result.toArray(new Integer[result.size()]);\n    }\n\n    private String[] toValues(String[]... values) {\n        List<String> result = new ArrayList<String>();\n        for (String[] value : values) {\n            for (String v : value) {\n                result.add(v);\n            }\n        }\n\n        return result.toArray(new String[result.size()]);\n    }\n\n    private void doPreparedStatement(PreparedStatement ps, final DbDialect dbDialect, final Integer[] columnTypes,\n                                     final String[] columnValues) throws SQLException {\n        LobCreator lobCreator = null;\n        for (int i = 0; i < columnTypes.length; i++) {\n            int paramIndex = i + 1;\n            String sqlValue = columnValues[i];\n            int sqlType = columnTypes[i];\n            Object param = SqlUtils.stringToSqlValue(sqlValue,\n                sqlType,\n                SqlUtils.isTextType(sqlType),\n                dbDialect.isEmptyStringNulled());\n            switch (sqlType) {\n                case Types.CLOB:\n                    if (lobCreator == null) {\n                        lobCreator = dbDialect.getLobHandler().getLobCreator();\n                    }\n\n                    lobCreator.setClobAsString(ps, paramIndex, (String) param);\n                    break;\n\n                case Types.BLOB:\n                    if (lobCreator == null) {\n                        lobCreator = dbDialect.getLobHandler().getLobCreator();\n                    }\n\n                    lobCreator.setBlobAsBytes(ps, paramIndex, (byte[]) param);\n                    break;\n                case Types.TIME:\n                case Types.TIMESTAMP:\n                case Types.DATE:\n                    ps.setObject(paramIndex, sqlValue);\n                    break;\n                default:\n                    StatementCreatorUtils.setParameterValue(ps, paramIndex, sqlType, null, param);\n                    break;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "node/etl/src/test/java/com/alibaba/otter/node/etl/common/db/DbDialectIntegration.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.db;\n\nimport java.sql.PreparedStatement;\nimport java.sql.SQLException;\nimport java.sql.Types;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.apache.ddlutils.model.Table;\nimport org.jtester.annotations.SpringBeanByName;\nimport org.springframework.dao.DataAccessException;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.jdbc.core.PreparedStatementCallback;\nimport org.springframework.jdbc.core.StatementCreatorUtils;\nimport org.springframework.jdbc.support.lob.LobCreator;\nimport org.springframework.transaction.TransactionStatus;\nimport org.springframework.transaction.support.TransactionCallback;\nimport org.springframework.transaction.support.TransactionTemplate;\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.node.etl.BaseDbTest;\nimport com.alibaba.otter.node.etl.common.db.dialect.DbDialect;\nimport com.alibaba.otter.node.etl.common.db.dialect.DbDialectFactory;\nimport com.alibaba.otter.node.etl.common.db.dialect.SqlTemplate;\nimport com.alibaba.otter.node.etl.common.db.dialect.mysql.MysqlDialect;\nimport com.alibaba.otter.node.etl.common.db.utils.SqlUtils;\nimport com.alibaba.otter.shared.common.model.config.data.DataMediaType;\nimport com.alibaba.otter.shared.common.model.config.data.db.DbMediaSource;\n\npublic class DbDialectIntegration extends BaseDbTest {\n\n    private static final String SCHEMA_NAME    = \"test\";\n    private static final String TABLE_NAME     = \"ljh_demo\";\n    @SpringBeanByName\n    private DbDialectFactory    dbDialectFactory;\n\n    private String[]            pkColumns      = { \"id\" };\n    private String[]            columns        = { \"name\", \"enum_value\", \"bigint_value\", \"int_value\" };\n\n    private String[]            pkColumnValues = { \"10\" };\n\n    private String[]            columnValues   = { \"hello\", \"1\", \"9223372036854775808\", \"2147483648\" };\n\n    @Test(expectedExceptions = RuntimeException.class)\n    public void test_mysql() {\n        DbMediaSource dbMediaSource = new DbMediaSource();\n        dbMediaSource.setId(10L);\n        dbMediaSource.setDriver(\"com.mysql.jdbc.Driver\");\n        dbMediaSource.setUsername(\"xxxxx\");\n        dbMediaSource.setPassword(\"xxxxx\");\n        dbMediaSource.setUrl(\"jdbc:mysql://127.0.0.1:3306\");\n        dbMediaSource.setEncode(\"UTF-8\");\n        dbMediaSource.setType(DataMediaType.MYSQL);\n\n        final DbDialect dbDialect = dbDialectFactory.getDbDialect(2L, dbMediaSource);\n        want.object(dbDialect).clazIs(MysqlDialect.class);\n\n        Table table = dbDialect.findTable(\"test\", \"ljh_demo\");\n        System.out.println(table);\n\n        final SqlTemplate sqlTemplate = dbDialect.getSqlTemplate();\n        final JdbcTemplate jdbcTemplate = dbDialect.getJdbcTemplate();\n        final TransactionTemplate transactionTemplate = dbDialect.getTransactionTemplate();\n        final int[] pkColumnTypes = { Types.INTEGER };\n        final int[] columnTypes = { Types.VARCHAR, Types.INTEGER, Types.DECIMAL, Types.BIGINT };\n        transactionTemplate.execute(new TransactionCallback() {\n\n            public Object doInTransaction(TransactionStatus status) {\n                int affect = 0;\n                String sql = null;\n                // 执行insert\n                sql = sqlTemplate.getInsertSql(SCHEMA_NAME, TABLE_NAME, pkColumns, columns);\n                System.out.println(sql);\n                affect = (Integer) jdbcTemplate.execute(sql, new PreparedStatementCallback() {\n\n                    public Object doInPreparedStatement(PreparedStatement ps) throws SQLException, DataAccessException {\n                        doPreparedStatement(ps, dbDialect, toTypes(columnTypes, pkColumnTypes),\n                                            toValues(columnValues, pkColumnValues));\n                        return ps.executeUpdate();\n                    }\n\n                });\n                want.number(affect).isEqualTo(1);\n                throw new RuntimeException(\"rollback\");\n            }\n        });\n\n    }\n\n    private Integer[] toTypes(int[]... types) {\n        List<Integer> result = new ArrayList<Integer>();\n        for (int[] type : types) {\n            for (int t : type) {\n                result.add(t);\n            }\n        }\n\n        return result.toArray(new Integer[result.size()]);\n    }\n\n    private String[] toValues(String[]... values) {\n        List<String> result = new ArrayList<String>();\n        for (String[] value : values) {\n            for (String v : value) {\n                result.add(v);\n            }\n        }\n\n        return result.toArray(new String[result.size()]);\n    }\n\n    private void doPreparedStatement(PreparedStatement ps, final DbDialect dbDialect, final Integer[] columnTypes,\n                                     final String[] columnValues) throws SQLException {\n        LobCreator lobCreator = null;\n        for (int i = 0; i < columnTypes.length; i++) {\n            int paramIndex = i + 1;\n            String sqlValue = columnValues[i];\n            int sqlType = columnTypes[i];\n            Object param = SqlUtils.stringToSqlValue(sqlValue, sqlType, SqlUtils.isTextType(sqlType),\n                                                     dbDialect.isEmptyStringNulled());\n            switch (sqlType) {\n                case Types.CLOB:\n                    if (lobCreator == null) {\n                        lobCreator = dbDialect.getLobHandler().getLobCreator();\n                    }\n\n                    lobCreator.setClobAsString(ps, paramIndex, (String) param);\n                    break;\n\n                case Types.BLOB:\n                    if (lobCreator == null) {\n                        lobCreator = dbDialect.getLobHandler().getLobCreator();\n                    }\n\n                    lobCreator.setBlobAsBytes(ps, paramIndex, (byte[]) param);\n                    break;\n\n                default:\n                    StatementCreatorUtils.setParameterValue(ps, paramIndex, sqlType, null, param);\n                    break;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "node/etl/src/test/java/com/alibaba/otter/node/etl/common/db/DbDialectTableTest.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.db;\n\nimport org.apache.ddlutils.model.Column;\nimport org.apache.ddlutils.model.Table;\nimport org.jtester.annotations.SpringBeanByName;\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.node.etl.BaseDbTest;\nimport com.alibaba.otter.node.etl.common.db.dialect.DbDialect;\nimport com.alibaba.otter.node.etl.common.db.dialect.DbDialectFactory;\nimport com.alibaba.otter.shared.common.model.config.data.db.DbDataMedia;\n\n/**\n * 测试下table表的获取操作\n * \n * @author jianghang 2012-4-20 下午04:56:43\n * @version 4.0.2\n */\npublic class DbDialectTableTest extends BaseDbTest {\n\n    @SpringBeanByName\n    private DbDialectFactory dbDialectFactory;\n\n    @Test\n    public void testMysqlTable() {\n        DbDataMedia mysqlMedia = getMysqlMedia();\n        DbDialect dbDialect = dbDialectFactory.getDbDialect(1L, mysqlMedia.getSource());\n        Table table = dbDialect.findTable(mysqlMedia.getNamespace(), mysqlMedia.getName());\n        want.object(table).notNull();\n\n        System.out.println(\"tableName = \" + table.getName());\n        Column[] columns = table.getColumns();\n        for (Column column : columns) {\n            System.out.println(\"columnName = \" + column.getName() + \",columnType = \" + column.getTypeCode()\n                               + \",isPrimary = \" + column.isPrimaryKey() + \",nullable = \" + column.isRequired());\n        }\n\n    }\n\n    @Test\n    public void testOracleTable() {\n        DbDataMedia oracleMedia = getOracleMedia();\n        DbDialect dbDialect = dbDialectFactory.getDbDialect(1L, oracleMedia.getSource());\n        Table table = dbDialect.findTable(oracleMedia.getNamespace(), oracleMedia.getName());\n        want.object(table).notNull();\n\n        System.out.println(\"tableName = \" + table.getName());\n        Column[] columns = table.getColumns();\n        for (Column column : columns) {\n            System.out.println(\"columnName = \" + column.getName() + \",columnType = \" + column.getTypeCode()\n                               + \",isPrimary = \" + column.isPrimaryKey() + \",nullable = \" + column.isRequired());\n        }\n    }\n}\n"
  },
  {
    "path": "node/etl/src/test/java/com/alibaba/otter/node/etl/common/db/DbDialectTest.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.db;\n\nimport com.alibaba.otter.node.etl.BaseDbTest;\nimport com.alibaba.otter.node.etl.common.db.dialect.DbDialect;\nimport com.alibaba.otter.node.etl.common.db.dialect.DbDialectFactory;\nimport com.alibaba.otter.node.etl.common.db.dialect.SqlTemplate;\nimport com.alibaba.otter.node.etl.common.db.dialect.mysql.MysqlDialect;\nimport com.alibaba.otter.node.etl.common.db.dialect.oracle.OracleDialect;\nimport com.alibaba.otter.node.etl.common.db.utils.SqlUtils;\nimport com.alibaba.otter.shared.common.model.config.data.db.DbDataMedia;\nimport org.jtester.annotations.SpringBeanByName;\nimport org.springframework.dao.DataAccessException;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.jdbc.core.PreparedStatementCallback;\nimport org.springframework.jdbc.core.StatementCreatorUtils;\nimport org.springframework.jdbc.support.lob.LobCreator;\nimport org.springframework.transaction.TransactionStatus;\nimport org.springframework.transaction.support.TransactionCallback;\nimport org.springframework.transaction.support.TransactionTemplate;\nimport org.testng.annotations.Test;\n\nimport java.sql.PreparedStatement;\nimport java.sql.SQLException;\nimport java.sql.Types;\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class DbDialectTest extends BaseDbTest {\n\n    private static final String MYSQL_SCHEMA_NAME  = \"srf\";\n    private static final String ORACLE_SCHEMA_NAME = \"srf\";\n    private static final String TABLE_NAME         = \"columns\";\n    @SpringBeanByName\n    private DbDialectFactory    dbDialectFactory;\n    // private String[] allColumns = { \"alias_name\", \"amount\", \"text_b\",\n    // \"text_c\", \"curr_date\",\n    // \"gmt_create\", \"gmt_modify\", \"id\", \"name\" };\n\n    private String[]            pkColumns          = { \"id\", \"name\" };\n    private String[]            columns            = { \"alias_name\", \"amount\", \"text_b\", \"text_c\", \"curr_date\",\n            \"gmt_create\", \"gmt_modify\"            };\n\n    private String[]            pkColumnValues     = { \"1\", \"ljh\" };\n\n    // [116,101,120,116,95,98]\n    private String[]            columnValues       = { \"hello\", \"100.01\", \"text_b\", \"text_c\", \"2011-01-01\",\n            \"2011-01-01 11:11:11\", \"2011-01-01 11:11:11\" };\n\n    @Test(expectedExceptions = RuntimeException.class)\n    public void test_mysql() {\n        DbDataMedia media = getMysqlMedia();\n        final DbDialect dbDialect = dbDialectFactory.getDbDialect(2L, media.getSource());\n        want.object(dbDialect).clazIs(MysqlDialect.class);\n\n        final SqlTemplate sqlTemplate = dbDialect.getSqlTemplate();\n        final JdbcTemplate jdbcTemplate = dbDialect.getJdbcTemplate();\n        final TransactionTemplate transactionTemplate = dbDialect.getTransactionTemplate();\n        final int[] pkColumnTypes = { Types.INTEGER, Types.VARCHAR };\n        final int[] columnTypes = { Types.CHAR, Types.DECIMAL, Types.BLOB, Types.CLOB, Types.DATE, Types.TIMESTAMP,\n                Types.TIMESTAMP };\n        transactionTemplate.execute(new TransactionCallback() {\n\n            public Object doInTransaction(TransactionStatus status) {\n                int affect = 0;\n                String sql = null;\n                // 执行insert\n                sql = sqlTemplate.getInsertSql(MYSQL_SCHEMA_NAME, TABLE_NAME, pkColumns, columns);\n                System.out.println(sql);\n                affect = (Integer) jdbcTemplate.execute(sql, new PreparedStatementCallback() {\n\n                    public Object doInPreparedStatement(PreparedStatement ps) throws SQLException, DataAccessException {\n                        doPreparedStatement(ps,\n                            dbDialect,\n                            toTypes(columnTypes, pkColumnTypes),\n                            toValues(columnValues, pkColumnValues));\n                        return ps.executeUpdate();\n                    }\n\n                });\n                want.number(affect).isEqualTo(1);\n                // 执行update\n                sql = sqlTemplate.getUpdateSql(MYSQL_SCHEMA_NAME, TABLE_NAME, pkColumns, columns, true, null);\n                System.out.println(sql);\n                affect = (Integer) jdbcTemplate.execute(sql, new PreparedStatementCallback() {\n\n                    public Object doInPreparedStatement(PreparedStatement ps) throws SQLException, DataAccessException {\n                        doPreparedStatement(ps,\n                            dbDialect,\n                            toTypes(columnTypes, pkColumnTypes),\n                            toValues(columnValues, pkColumnValues));\n                        return ps.executeUpdate();\n                    }\n\n                });\n                want.number(affect).isEqualTo(1);\n                // 执行deleate\n                sql = sqlTemplate.getDeleteSql(MYSQL_SCHEMA_NAME, TABLE_NAME, pkColumns);\n                System.out.println(sql);\n                affect = (Integer) jdbcTemplate.execute(sql, new PreparedStatementCallback() {\n\n                    public Object doInPreparedStatement(PreparedStatement ps) throws SQLException, DataAccessException {\n                        doPreparedStatement(ps, dbDialect, toTypes(pkColumnTypes), toValues(pkColumnValues));\n                        return ps.executeUpdate();\n                    }\n\n                });\n                want.number(affect).isEqualTo(1);\n                // 执行merge\n                sql = sqlTemplate.getMergeSql(MYSQL_SCHEMA_NAME, TABLE_NAME, pkColumns, columns, null, true, null);\n                System.out.println(sql);\n                affect = (Integer) jdbcTemplate.execute(sql, new PreparedStatementCallback() {\n\n                    public Object doInPreparedStatement(PreparedStatement ps) throws SQLException, DataAccessException {\n                        doPreparedStatement(ps,\n                            dbDialect,\n                            toTypes(columnTypes, pkColumnTypes),\n                            toValues(columnValues, pkColumnValues));\n                        return ps.executeUpdate();\n                    }\n\n                });\n                want.number(affect).isEqualTo(1);\n                throw new RuntimeException(\"rollback\");\n            }\n        });\n\n    }\n\n    @Test(expectedExceptions = RuntimeException.class)\n    public void test_oracle() {\n        DbDataMedia media = getOracleMedia();\n        final DbDialect dbDialect = dbDialectFactory.getDbDialect(1L, media.getSource());\n\n        want.object(dbDialect).clazIs(OracleDialect.class);\n        final SqlTemplate sqlTemplate = dbDialect.getSqlTemplate();\n        final JdbcTemplate jdbcTemplate = dbDialect.getJdbcTemplate();\n        final TransactionTemplate transactionTemplate = dbDialect.getTransactionTemplate();\n        final int[] pkColumnTypes = { Types.NUMERIC, Types.VARCHAR };\n        final int[] columnTypes = { Types.CHAR, Types.NUMERIC, Types.BLOB, Types.CLOB, Types.DATE, Types.DATE,\n                Types.DATE };\n        transactionTemplate.execute(new TransactionCallback() {\n\n            public Object doInTransaction(TransactionStatus status) {\n                int affect = 0;\n                String sql = null;\n                // 执行insert\n                sql = sqlTemplate.getInsertSql(ORACLE_SCHEMA_NAME, TABLE_NAME, pkColumns, columns);\n                System.out.println(sql);\n                affect = (Integer) jdbcTemplate.execute(sql, new PreparedStatementCallback() {\n\n                    public Object doInPreparedStatement(PreparedStatement ps) throws SQLException, DataAccessException {\n                        doPreparedStatement(ps,\n                            dbDialect,\n                            toTypes(columnTypes, pkColumnTypes),\n                            toValues(columnValues, pkColumnValues));\n                        return ps.executeUpdate();\n                    }\n\n                });\n                want.number(affect).isEqualTo(1);\n                // 执行update\n                sql = sqlTemplate.getUpdateSql(ORACLE_SCHEMA_NAME, TABLE_NAME, pkColumns, columns, true, null);\n                System.out.println(sql);\n                affect = (Integer) jdbcTemplate.execute(sql, new PreparedStatementCallback() {\n\n                    public Object doInPreparedStatement(PreparedStatement ps) throws SQLException, DataAccessException {\n                        doPreparedStatement(ps,\n                            dbDialect,\n                            toTypes(columnTypes, pkColumnTypes),\n                            toValues(columnValues, pkColumnValues));\n                        return ps.executeUpdate();\n                    }\n\n                });\n                want.number(affect).isEqualTo(1);\n                // 执行deleate\n                sql = sqlTemplate.getDeleteSql(ORACLE_SCHEMA_NAME, TABLE_NAME, pkColumns);\n                System.out.println(sql);\n                affect = (Integer) jdbcTemplate.execute(sql, new PreparedStatementCallback() {\n\n                    public Object doInPreparedStatement(PreparedStatement ps) throws SQLException, DataAccessException {\n                        doPreparedStatement(ps, dbDialect, toTypes(pkColumnTypes), toValues(pkColumnValues));\n                        return ps.executeUpdate();\n                    }\n\n                });\n                want.number(affect).isEqualTo(1);\n                // 执行merge\n                sql = sqlTemplate.getMergeSql(ORACLE_SCHEMA_NAME, TABLE_NAME, pkColumns, columns, null, true, null);\n                System.out.println(sql);\n\n                affect = (Integer) jdbcTemplate.execute(sql, new PreparedStatementCallback() {\n\n                    public Object doInPreparedStatement(PreparedStatement ps) throws SQLException, DataAccessException {\n                        doPreparedStatement(ps,\n                            dbDialect,\n                            toTypes(columnTypes, pkColumnTypes),\n                            toValues(columnValues, pkColumnValues));\n                        return ps.executeUpdate();\n                    }\n\n                });\n                want.number(affect).isEqualTo(1);\n                throw new RuntimeException(\"rollback\");\n            }\n        });\n    }\n\n    private Integer[] toTypes(int[]... types) {\n        List<Integer> result = new ArrayList<Integer>();\n        for (int[] type : types) {\n            for (int t : type) {\n                result.add(t);\n            }\n        }\n\n        return result.toArray(new Integer[result.size()]);\n    }\n\n    private String[] toValues(String[]... values) {\n        List<String> result = new ArrayList<String>();\n        for (String[] value : values) {\n            for (String v : value) {\n                result.add(v);\n            }\n        }\n\n        return result.toArray(new String[result.size()]);\n    }\n\n    private void doPreparedStatement(PreparedStatement ps, final DbDialect dbDialect, final Integer[] columnTypes,\n                                     final String[] columnValues) throws SQLException {\n        LobCreator lobCreator = null;\n        for (int i = 0; i < columnTypes.length; i++) {\n            int paramIndex = i + 1;\n            String sqlValue = columnValues[i];\n            int sqlType = columnTypes[i];\n            Object param = SqlUtils.stringToSqlValue(sqlValue,\n                sqlType,\n                SqlUtils.isTextType(sqlType),\n                dbDialect.isEmptyStringNulled());\n            switch (sqlType) {\n                case Types.CLOB:\n                    if (lobCreator == null) {\n                        lobCreator = dbDialect.getLobHandler().getLobCreator();\n                    }\n\n                    lobCreator.setClobAsString(ps, paramIndex, (String) param);\n                    break;\n\n                case Types.BLOB:\n                    if (lobCreator == null) {\n                        lobCreator = dbDialect.getLobHandler().getLobCreator();\n                    }\n\n                    lobCreator.setBlobAsBytes(ps, paramIndex, (byte[]) param);\n                    break;\n\n                default:\n                    StatementCreatorUtils.setParameterValue(ps, paramIndex, sqlType, null, param);\n                    break;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "node/etl/src/test/java/com/alibaba/otter/node/etl/common/db/DbPerfIntergration.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.db;\n\nimport java.sql.PreparedStatement;\nimport java.sql.SQLException;\nimport java.sql.Timestamp;\nimport java.sql.Types;\nimport java.util.Date;\nimport java.util.concurrent.ArrayBlockingQueue;\nimport java.util.concurrent.CountDownLatch;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.ThreadPoolExecutor;\nimport java.util.concurrent.TimeUnit;\n\nimport org.apache.commons.lang.RandomStringUtils;\nimport org.jtester.annotations.SpringBeanByName;\nimport org.springframework.jdbc.core.BatchPreparedStatementSetter;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.jdbc.core.StatementCreatorUtils;\nimport org.springframework.transaction.TransactionStatus;\nimport org.springframework.transaction.support.TransactionCallback;\nimport org.springframework.transaction.support.TransactionTemplate;\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.node.etl.BaseDbTest;\nimport com.alibaba.otter.node.etl.common.db.dialect.DbDialect;\nimport com.alibaba.otter.node.etl.common.db.dialect.DbDialectFactory;\nimport com.alibaba.otter.node.etl.common.db.dialect.mysql.MysqlDialect;\nimport com.alibaba.otter.shared.common.model.config.data.DataMediaType;\nimport com.alibaba.otter.shared.common.model.config.data.db.DbDataMedia;\nimport com.alibaba.otter.shared.common.model.config.data.db.DbMediaSource;\nimport com.alibaba.otter.shared.common.utils.thread.NamedThreadFactory;\n\npublic class DbPerfIntergration extends BaseDbTest {\n\n    @SpringBeanByName\n    private DbDialectFactory dbDialectFactory;\n\n    @Test\n    public void test_stack() {\n        DbMediaSource dbMediaSource = new DbMediaSource();\n        dbMediaSource.setId(1L);\n        dbMediaSource.setDriver(\"com.mysql.jdbc.Driver\");\n        dbMediaSource.setUsername(\"otter\");\n        dbMediaSource.setPassword(\"otter\");\n        dbMediaSource.setUrl(\"jdbc:mysql://127.0.0.1:3306/retl\");\n        dbMediaSource.setEncode(\"UTF-8\");\n        dbMediaSource.setType(DataMediaType.MYSQL);\n\n        DbDataMedia dataMedia = new DbDataMedia();\n        dataMedia.setSource(dbMediaSource);\n        dataMedia.setId(1L);\n        dataMedia.setName(\"ljhtable1\");\n        dataMedia.setNamespace(\"otter\");\n\n        final DbDialect dbDialect = dbDialectFactory.getDbDialect(2L, dataMedia.getSource());\n        want.object(dbDialect).clazIs(MysqlDialect.class);\n\n        final TransactionTemplate transactionTemplate = dbDialect.getTransactionTemplate();\n\n        // 插入数据准备\n        int minute = 5;\n        int nextId = 1;\n        final int thread = 10;\n        final int batch = 50;\n        final String sql = \"insert into otter.ljhtable1 values(? , ? , ? , ?)\";\n\n        final CountDownLatch latch = new CountDownLatch(thread);\n        ExecutorService executor = new ThreadPoolExecutor(thread,\n            thread,\n            60,\n            TimeUnit.SECONDS,\n            new ArrayBlockingQueue(thread * 2),\n            new NamedThreadFactory(\"load\"),\n            new ThreadPoolExecutor.CallerRunsPolicy());\n\n        for (int sec = 0; sec < minute * 60; sec++) {\n            // 执行秒循环\n            long startTime = System.currentTimeMillis();\n            for (int i = 0; i < thread; i++) {\n                final int start = nextId + i * batch;\n                executor.submit(new Runnable() {\n\n                    public void run() {\n                        try {\n                            transactionTemplate.execute(new TransactionCallback() {\n\n                                public Object doInTransaction(TransactionStatus status) {\n                                    JdbcTemplate jdbcTemplate = dbDialect.getJdbcTemplate();\n                                    return jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() {\n\n                                        public void setValues(PreparedStatement ps, int idx) throws SQLException {\n                                            int id = start + idx;\n                                            StatementCreatorUtils.setParameterValue(ps, 1, Types.INTEGER, null, id);\n                                            StatementCreatorUtils.setParameterValue(ps,\n                                                2,\n                                                Types.VARCHAR,\n                                                null,\n                                                RandomStringUtils.randomAlphabetic(1000));\n                                            // RandomStringUtils.randomAlphabetic()\n                                            long time = new Date().getTime();\n                                            StatementCreatorUtils.setParameterValue(ps,\n                                                3,\n                                                Types.TIMESTAMP,\n                                                new Timestamp(time));\n                                            StatementCreatorUtils.setParameterValue(ps,\n                                                4,\n                                                Types.TIMESTAMP,\n                                                new Timestamp(time));\n                                        }\n\n                                        public int getBatchSize() {\n                                            return batch;\n                                        }\n                                    });\n                                }\n                            });\n                        } finally {\n                            latch.countDown();\n                        }\n                    }\n                });\n\n            }\n\n            long endTime = System.currentTimeMillis();\n            try {\n                latch.await(1000 * 60L - (endTime - startTime), TimeUnit.MILLISECONDS);\n            } catch (InterruptedException e) {\n                e.printStackTrace();\n            }\n\n            if (latch.getCount() != 0) {\n                System.out.println(\"perf is not enough!\");\n                System.exit(-1);\n            }\n            endTime = System.currentTimeMillis();\n            System.out.println(\"Time cost : \" + (System.currentTimeMillis() - startTime));\n            try {\n                TimeUnit.MILLISECONDS.sleep(1000L - (endTime - startTime));\n            } catch (InterruptedException e) {\n                e.printStackTrace();\n            }\n\n            nextId = nextId + thread * batch;\n        }\n        executor.shutdown();\n    }\n}\n"
  },
  {
    "path": "node/etl/src/test/java/com/alibaba/otter/node/etl/common/db/DdlUtilsTest.java",
    "content": "package com.alibaba.otter.node.etl.common.db;\n\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.node.etl.common.db.utils.DdlUtils;\n\n/**\n * @author agapple 2017年4月6日 下午3:23:25\n * @since 3.1.9\n */\npublic class DdlUtilsTest {\n\n    @Test\n    public void testCreateTable() {\n        String sql = \"CREATE TABLE old.`old_table` (`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,`c_varchar2` varchar(32) DEFAULT NULL,`c_nvarchar2` varchar(32) DEFAULT NULL,`c_char` char(32) DEFAULT NULL,`c_nchar` char(32) DEFAULT NULL,`c_number` decimal(11,2) DEFAULT NULL,`c_float` double DEFAULT NULL,`c_long` longtext,`c_date` datetime DEFAULT NULL,`c_binary_float` decimal(65,8) DEFAULT NULL,`c_binary_double` double DEFAULT NULL,`c_timestamp0` datetime DEFAULT NULL,`c_timestamp3` datetime(3) DEFAULT NULL,`c_timestamp6` datetime(6) DEFAULT NULL,`c_clob` longtext,`c_nclob` longtext,`c_blob` longblob,`c_raw` varbinary(2000) DEFAULT NULL,`gmt_create` datetime NOT NULL,`gmt_modified` datetime NOT NULL,PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8 \";\n        String result = DdlUtils.convert(sql, \"old\", \"old_table\", \"new\", \"new_table\");\n        System.out.println(result);\n    }\n\n    @Test\n    public void testAlterTable() {\n        String sql = \"alter table `old`.old_table add column name varchar(32) DEFAULT NULL\";\n        String result = DdlUtils.convert(sql, \"old\", \"old_table\", \"new\", \"new_table\");\n        System.out.println(result);\n    }\n\n    @Test\n    public void testAlterTable2() {\n        String sql = \"alter table ps_users\\n\\tadd column name varchar(32) DEFAULT NULL\";\n        String result = DdlUtils.convert(sql, \"test\", \"ps_users\", \"test\", \"ps_users2\");\n        System.out.println(result);\n    }\n\n    @Test\n    public void testDropTable() {\n        String sql = \"drop table old.`old_table`\";\n        String result = DdlUtils.convert(sql, \"old\", \"old_table\", \"new\", \"new_table\");\n        System.out.println(result);\n    }\n\n    @Test\n    public void testRenameTable() {\n        String sql = \"rename table old.`old_table` to old2\";\n        String result = DdlUtils.convert(sql, \"old\", \"old_table\", \"new\", \"new_table\");\n        System.out.println(result);\n\n        sql = \"rename table old1 to old.`old_table`\";\n        result = DdlUtils.convert(sql, \"old\", \"old_table\", \"new\", \"new_table\");\n        System.out.println(result);\n    }\n\n    @Test\n    public void testCreateIndex() {\n        String sql = \" create index IDX_testNoPK_Name on old.`old_table` (name)\";\n        String result = DdlUtils.convert(sql, \"old\", \"old_table\", \"new\", \"new_table\");\n        System.out.println(result);\n    }\n\n    @Test\n    public void testDropIndex() {\n        String sql = \" drop index IDX_testNoPK_Name on old.`old_table`\";\n        String result = DdlUtils.convert(sql, \"old\", \"old_table\", \"new\", \"new_table\");\n        System.out.println(result);\n    }\n\n    public static void main(String args[]) {\n        DdlUtilsTest tester = new DdlUtilsTest();\n        tester.testAlterTable2();\n        tester.testCreateTable();\n        tester.testAlterTable();\n        tester.testDropTable();\n        tester.testRenameTable();\n        tester.testCreateIndex();\n        tester.testDropIndex();\n    }\n}\n"
  },
  {
    "path": "node/etl/src/test/java/com/alibaba/otter/node/etl/common/db/SqlTemplateTest.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.db;\n\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.node.etl.BaseDbTest;\nimport com.alibaba.otter.node.etl.common.db.dialect.SqlTemplate;\nimport com.alibaba.otter.node.etl.common.db.dialect.mysql.MysqlSqlTemplate;\nimport com.alibaba.otter.node.etl.common.db.dialect.oracle.OracleSqlTemplate;\n\npublic class SqlTemplateTest extends BaseDbTest {\n\n    private static final String SCHEMA_NAME = \"srf\";\n    private static final String TABLE_NAME  = \"columns\";\n\n    private String[]            pkColumns   = { \"id\", \"name\" };\n    private String[]            columns     = { \"alias_name\", \"amount\", \"text_b\", \"text_c\", \"curr_date\", \"gmt_create\",\n            \"gmt_modify\"                   };\n\n    @Test\n    public void test_mysql() {\n        SqlTemplate sqlTemplate = new MysqlSqlTemplate();\n        // 执行insert\n        String sql1 = sqlTemplate.getInsertSql(SCHEMA_NAME, TABLE_NAME, pkColumns, columns);\n        String sql2 = sqlTemplate.getInsertSql(SCHEMA_NAME, TABLE_NAME, pkColumns, columns);\n        want.bool(sql1 == sql2);\n        // 执行update\n        sql1 = sqlTemplate.getUpdateSql(SCHEMA_NAME, TABLE_NAME, pkColumns, columns, true, null);\n        sql2 = sqlTemplate.getUpdateSql(SCHEMA_NAME, TABLE_NAME, pkColumns, columns, true, null);\n        want.bool(sql1 == sql2);\n        // 执行deleate\n        sql1 = sqlTemplate.getDeleteSql(SCHEMA_NAME, TABLE_NAME, pkColumns);\n        sql2 = sqlTemplate.getDeleteSql(SCHEMA_NAME, TABLE_NAME, pkColumns);\n        want.bool(sql1 == sql2);\n        // 执行merge\n        sql1 = sqlTemplate.getMergeSql(SCHEMA_NAME, TABLE_NAME, pkColumns, columns, null, true, null);\n        sql2 = sqlTemplate.getMergeSql(SCHEMA_NAME, TABLE_NAME, pkColumns, columns, null, true, null);\n        want.bool(sql1 == sql2);\n\n    }\n\n    @Test\n    public void test_oracle() {\n        SqlTemplate sqlTemplate = new OracleSqlTemplate();\n        // 执行insert\n        String sql1 = sqlTemplate.getInsertSql(SCHEMA_NAME, TABLE_NAME, pkColumns, columns);\n        String sql2 = sqlTemplate.getInsertSql(SCHEMA_NAME, TABLE_NAME, pkColumns, columns);\n        want.bool(sql1 == sql2);\n        // 执行update\n        sql1 = sqlTemplate.getUpdateSql(SCHEMA_NAME, TABLE_NAME, pkColumns, columns, true, null);\n        sql2 = sqlTemplate.getUpdateSql(SCHEMA_NAME, TABLE_NAME, pkColumns, columns, true, null);\n        want.bool(sql1 == sql2);\n        // 执行deleate\n        sql1 = sqlTemplate.getDeleteSql(SCHEMA_NAME, TABLE_NAME, pkColumns);\n        sql2 = sqlTemplate.getDeleteSql(SCHEMA_NAME, TABLE_NAME, pkColumns);\n        want.bool(sql1 == sql2);\n        // 执行merge\n        sql1 = sqlTemplate.getMergeSql(SCHEMA_NAME, TABLE_NAME, pkColumns, columns, null, true, null);\n        sql2 = sqlTemplate.getMergeSql(SCHEMA_NAME, TABLE_NAME, pkColumns, columns, null, true, null);\n        want.bool(sql1 == sql2);\n    }\n\n}\n"
  },
  {
    "path": "node/etl/src/test/java/com/alibaba/otter/node/etl/common/db/TimeTableIntegration.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.db;\n\nimport java.sql.PreparedStatement;\nimport java.sql.SQLException;\nimport java.sql.Types;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.apache.ddlutils.model.Table;\nimport org.jtester.annotations.SpringBeanByName;\nimport org.springframework.dao.DataAccessException;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.jdbc.core.PreparedStatementCallback;\nimport org.springframework.jdbc.core.StatementCreatorUtils;\nimport org.springframework.jdbc.support.lob.LobCreator;\nimport org.springframework.transaction.TransactionStatus;\nimport org.springframework.transaction.support.TransactionCallback;\nimport org.springframework.transaction.support.TransactionTemplate;\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.node.etl.BaseDbTest;\nimport com.alibaba.otter.node.etl.common.db.dialect.DbDialect;\nimport com.alibaba.otter.node.etl.common.db.dialect.DbDialectFactory;\nimport com.alibaba.otter.node.etl.common.db.dialect.SqlTemplate;\nimport com.alibaba.otter.node.etl.common.db.utils.SqlUtils;\nimport com.alibaba.otter.shared.common.model.config.data.DataMediaType;\nimport com.alibaba.otter.shared.common.model.config.data.db.DbMediaSource;\n\npublic class TimeTableIntegration extends BaseDbTest {\n\n    private static final String SCHEMA_NAME    = \"otter2\";\n    private static final String TABLE_NAME     = \"test_time\";\n    @SpringBeanByName\n    private DbDialectFactory    dbDialectFactory;\n\n    private String[]            pkColumns      = { \"id\" };\n    private String[]            columns        = { \"DATETIME_VALUES\", \"TIMESTAMP_VALUES\", \"DATE_VALUES\", \"TIME_VALUES\",\n            \"YEAR4_VALUES\", \"YEAR2_VALUES\"    };\n\n    private String[]            pkColumnValues = { \"1\" };\n\n    private String[]            columnValues   = { \"0000-00-00 00:00:00.0\", \"0000-00-00 00:00:00.0\", \"0000-00-00\",\n            \"00:00:00\", \"1\", \"1\"              };\n\n    @Test\n    public void test_mysql() {\n        DbMediaSource dbMediaSource = new DbMediaSource();\n        dbMediaSource.setId(10L);\n        dbMediaSource.setDriver(\"oracle.jdbc.OracleDriver\");\n        dbMediaSource.setUsername(\"otter1\");\n        dbMediaSource.setPassword(\"jonathan\");\n        dbMediaSource.setUrl(\"jdbc:oracle:thin:@127.0.0.1:1521:ointest\");\n        dbMediaSource.setEncode(\"UTF-8\");\n        dbMediaSource.setType(DataMediaType.ORACLE);\n\n        final DbDialect dbDialect = dbDialectFactory.getDbDialect(2L, dbMediaSource);\n        // want.object(dbDialect).clazIs(MysqlDialect.class);\n\n        Table table = dbDialect.findTable(\"otter2\", \"test_time\");\n        System.out.println(table);\n\n        final SqlTemplate sqlTemplate = dbDialect.getSqlTemplate();\n        final JdbcTemplate jdbcTemplate = dbDialect.getJdbcTemplate();\n        final TransactionTemplate transactionTemplate = dbDialect.getTransactionTemplate();\n        final int[] pkColumnTypes = { Types.INTEGER };\n        final int[] columnTypes = { Types.TIMESTAMP, Types.TIMESTAMP, Types.DATE, Types.TIME, Types.INTEGER,\n                Types.INTEGER };\n        transactionTemplate.execute(new TransactionCallback() {\n\n            public Object doInTransaction(TransactionStatus status) {\n                int affect = 0;\n                String sql = null;\n                // 执行insert\n                sql = sqlTemplate.getInsertSql(SCHEMA_NAME, TABLE_NAME, pkColumns, columns);\n                System.out.println(sql);\n                affect = (Integer) jdbcTemplate.execute(sql, new PreparedStatementCallback() {\n\n                    public Object doInPreparedStatement(PreparedStatement ps) throws SQLException, DataAccessException {\n                        doPreparedStatement(ps,\n                            dbDialect,\n                            toTypes(columnTypes, pkColumnTypes),\n                            toValues(columnValues, pkColumnValues));\n                        return ps.executeUpdate();\n                    }\n\n                });\n                want.number(affect).isEqualTo(1);\n                return null;\n                // throw new RuntimeException(\"rollback\");\n            }\n        });\n\n    }\n\n    private Integer[] toTypes(int[]... types) {\n        List<Integer> result = new ArrayList<Integer>();\n        for (int[] type : types) {\n            for (int t : type) {\n                result.add(t);\n            }\n        }\n\n        return result.toArray(new Integer[result.size()]);\n    }\n\n    private String[] toValues(String[]... values) {\n        List<String> result = new ArrayList<String>();\n        for (String[] value : values) {\n            for (String v : value) {\n                result.add(v);\n            }\n        }\n\n        return result.toArray(new String[result.size()]);\n    }\n\n    private void doPreparedStatement(PreparedStatement ps, final DbDialect dbDialect, final Integer[] columnTypes,\n                                     final String[] columnValues) throws SQLException {\n        LobCreator lobCreator = null;\n        for (int i = 0; i < columnTypes.length; i++) {\n            int paramIndex = i + 1;\n            String sqlValue = columnValues[i];\n            int sqlType = columnTypes[i];\n            Object param = SqlUtils.stringToSqlValue(sqlValue,\n                sqlType,\n                SqlUtils.isTextType(sqlType),\n                dbDialect.isEmptyStringNulled());\n            switch (sqlType) {\n                case Types.CLOB:\n                    if (lobCreator == null) {\n                        lobCreator = dbDialect.getLobHandler().getLobCreator();\n                    }\n\n                    lobCreator.setClobAsString(ps, paramIndex, (String) param);\n                    break;\n\n                case Types.BLOB:\n                    if (lobCreator == null) {\n                        lobCreator = dbDialect.getLobHandler().getLobCreator();\n                    }\n\n                    lobCreator.setBlobAsBytes(ps, paramIndex, (byte[]) param);\n                    break;\n                case Types.TIME:\n                case Types.TIMESTAMP:\n                case Types.DATE:\n                    ps.setObject(paramIndex, sqlValue);\n                    break;\n                default:\n                    StatementCreatorUtils.setParameterValue(ps, paramIndex, sqlType, null, param);\n                    break;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "node/etl/src/test/java/com/alibaba/otter/node/etl/common/io/AESUtilsTest.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.io;\n\nimport org.apache.commons.lang.math.RandomUtils;\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.node.etl.common.io.crypto.AESUtils;\nimport com.alibaba.otter.node.etl.BaseOtterTest;\n\npublic class AESUtilsTest extends BaseOtterTest {\n\n    @Test\n    public void test_simple() {\n        AESUtils aes = new AESUtils();\n        aes.generateSecretKey();\n        byte[] data = getBlock(10 * 1024);\n        byte[] encrypt = aes.encrypt(data);\n        byte[] decrypt = aes.decrypt(encrypt);\n        System.out.println(\"data length : \" + data.length + \" \" + encrypt.length);\n        check(data, decrypt);\n    }\n\n    private void check(byte[] src, byte[] dest) {\n        want.object(src).notNull();\n        want.object(dest).notNull();\n        want.bool(src.length == dest.length).is(true);\n\n        for (int i = 0; i < src.length; i++) {\n            if (src[i] != dest[i]) {\n                want.fail();\n            }\n        }\n    }\n\n    private byte[] getBlock(int length) {\n        byte[] rawData = new byte[length];\n        for (int i = 0; i < rawData.length; i++) {\n            rawData[i] = (byte) (' ' + RandomUtils.nextInt(95));\n\n        }\n        return rawData;\n    }\n}\n"
  },
  {
    "path": "node/etl/src/test/java/com/alibaba/otter/node/etl/common/io/ArchiveBeanIntegration.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.io;\n\nimport java.io.File;\nimport java.io.InputStream;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.List;\n\nimport org.apache.commons.io.FileUtils;\nimport org.apache.commons.io.FilenameUtils;\nimport org.apache.commons.lang.StringUtils;\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.node.etl.BaseOtterTest;\nimport com.alibaba.otter.node.etl.common.pipe.impl.http.archive.ArchiveBean;\nimport com.alibaba.otter.node.etl.common.pipe.impl.http.archive.ArchiveRetriverCallback;\nimport com.alibaba.otter.node.etl.common.pipe.impl.http.archive.LazyFileInputStream;\nimport com.alibaba.otter.shared.etl.model.FileData;\n\npublic class ArchiveBeanIntegration extends BaseOtterTest {\n\n    // @Test\n    public void test_pack() {\n        ArchiveBean archiveBean = new ArchiveBean();\n        try {\n            archiveBean.afterPropertiesSet();\n            archiveBean.setUseLocalFileMutliThread(false);\n        } catch (Exception e1) {\n            want.fail();\n        }\n\n        File file = new File(\"/tmp/otter/test\");\n        Collection<File> allFiles = FileUtils.listFiles(file, new String[] { \"jpg\" }, true);\n\n        List<FileData> fileDatas = new ArrayList<FileData>();\n        for (File files : allFiles) {\n            FileData data = new FileData();\n            // data.setPath(\"wsproduct_repository/product_sku/76/84/32/84/768432847_10.summ.jpg\");\n            data.setPath(StringUtils.substringAfter(files.getAbsolutePath(), \"/tmp/otter/test\"));\n            fileDatas.add(data);\n        }\n\n        File archiveFile = new File(\"/tmp/otter/test.gzip\");\n        if (archiveFile.exists()) {\n            archiveFile.delete();\n        }\n\n        boolean result = archiveBean.pack(archiveFile, fileDatas, new ArchiveRetriverCallback<FileData>() {\n\n            public InputStream retrive(FileData source) {\n                return new LazyFileInputStream(new File(\"/tmp/otter/test\", source.getPath()));\n            }\n        });\n\n        if (!result) {\n            want.fail();\n        }\n\n    }\n\n    @Test\n    public void test_unpack() {\n        ArchiveBean archiveBean = new ArchiveBean();\n        try {\n            archiveBean.afterPropertiesSet();\n            archiveBean.setUseLocalFileMutliThread(false);\n        } catch (Exception e1) {\n            want.fail();\n        }\n\n        File archiveFile = new File(\"/tmp/otter/test.gzip\");\n        // for (File archiveFile : archiveFiles.listFiles()) {\n        // if (archiveFile.isDirectory()) {\n        // continue;\n        // }\n        //\n        // if (!\"FileBatch-2013-03-07-16-27-05-245-369-3209577.gzip\".equals(archiveFile.getName())) {\n        // continue;\n        // }\n\n        System.out.println(\"start unpack : \" + archiveFile.getName());\n        File targetFile = new File(\"/tmp/otter\", FilenameUtils.removeExtension(archiveFile.getName()));\n        archiveBean.unpack(archiveFile, targetFile);\n        // }\n    }\n\n}\n"
  },
  {
    "path": "node/etl/src/test/java/com/alibaba/otter/node/etl/common/io/ArchiveBeanTest.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.io;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileNotFoundException;\nimport java.io.InputStream;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.node.etl.BaseOtterTest;\nimport com.alibaba.otter.node.etl.common.pipe.impl.http.archive.ArchiveBean;\nimport com.alibaba.otter.node.etl.common.pipe.impl.http.archive.ArchiveRetriverCallback;\nimport com.alibaba.otter.shared.common.utils.NioUtils;\nimport com.alibaba.otter.shared.etl.model.FileData;\n\n/**\n * 测试下压缩和解压缩\n * \n * @author jianghang 2011-10-11 下午05:58:53\n * @version 4.0.0\n */\npublic class ArchiveBeanTest extends BaseOtterTest {\n\n    private static final String tmp = System.getProperty(\"java.io.tmpdir\", \"/tmp\");\n\n    @Test\n    public void test_simple() {\n        File[] files = new File[10];\n\n        List<FileData> fileDatas = new ArrayList<FileData>();\n        File archiveFile = new File(tmp, \"pack.zip\");\n        File unpack = new File(tmp, \"unpack\");\n\n        ArchiveBean archiveBean = new ArchiveBean();\n        try {\n            archiveBean.afterPropertiesSet();\n        } catch (Exception e1) {\n            want.fail();\n        }\n\n        try {\n            // for (int i = 0; i < 10; i++) {\n            // files[i] = new File(tmp, \"archiveTest_\" + i + \".txt\");\n            // datas[i] = getBlock((i + 1) * 1024);\n            // NioUtils.write(datas[i], files[i]);\n            //\n            // FileData filedata = new FileData();\n            // filedata.setPath(files[i].getPath());\n            // fileDatas.add(filedata);\n            // }\n\n            archiveBean.pack(archiveFile, fileDatas, new ArchiveRetriverCallback<FileData>() {\n\n                public InputStream retrive(FileData source) {\n                    try {\n                        return new FileInputStream(new File(source.getPath()));\n                    } catch (FileNotFoundException e) {\n                        e.printStackTrace();\n                        want.fail();\n                    }\n                    return null;\n                }\n            });\n\n            // 开始解压\n            List<File> result = archiveBean.unpack(archiveFile, unpack);\n            want.bool(result.size() == fileDatas.size());\n\n            // File dir = new File(unpack, archiveFile.getParent());\n            // File[] unpackFiles = dir.listFiles();\n            //\n            // List<File> unpackFilesList = Arrays.asList(unpackFiles);\n            // Collections.sort(unpackFilesList); // 排序一下\n            // for (int i = 0; i < unpackFilesList.size(); i++) {\n            // byte[] data = NioUtils.read(unpackFilesList.get(i));\n            // check(data, datas[i]);\n            // }\n\n        } catch (Exception e) {\n            want.fail();\n        } finally {\n            for (int i = 0; i < files.length; i++) {\n                NioUtils.delete(files[i]);\n            }\n            NioUtils.delete(archiveFile);\n            NioUtils.delete(unpack);\n        }\n    }\n\n}\n"
  },
  {
    "path": "node/etl/src/test/java/com/alibaba/otter/node/etl/common/io/Aria2cDownLoadIntegration.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.io;\n\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.node.etl.common.io.download.DataRetriever;\nimport com.alibaba.otter.node.etl.common.io.download.exception.DataRetrieveException;\nimport com.alibaba.otter.node.etl.common.io.download.impl.aria2c.Aria2cRetriever;\nimport com.alibaba.otter.node.etl.BaseOtterTest;\n\n/**\n * @author jianghang 2011-10-10 下午06:23:33\n * @version 4.0.0\n */\npublic class Aria2cDownLoadIntegration extends BaseOtterTest {\n\n    private static final String tmp = System.getProperty(\"java.io.tmpdir\", \"/tmp\");\n\n    @Test\n    public void testDownLoad_ok() {\n        DataRetriever retriever = new Aria2cRetriever(\"http://china.alibaba.com\", tmp);\n        try {\n            retriever.connect();\n            retriever.doRetrieve();\n        } catch (DataRetrieveException ex) {\n            retriever.abort();\n        } finally {\n            retriever.disconnect();\n        }\n    }\n\n    @Test\n    public void testDownLoad_failed() {\n        DataRetriever retriever = new Aria2cRetriever(\"aaaaaaa/sssss\", tmp);\n        try {\n            retriever.connect();\n            retriever.doRetrieve();\n        } catch (DataRetrieveException ex) {\n            retriever.abort();\n        } finally {\n            retriever.disconnect();\n        }\n    }\n}\n"
  },
  {
    "path": "node/etl/src/test/java/com/alibaba/otter/node/etl/common/io/ChecksumUtilsTest.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.io;\n\nimport org.apache.commons.lang.math.RandomUtils;\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.node.etl.common.io.signature.ChecksumUtils;\nimport com.alibaba.otter.node.etl.BaseOtterTest;\n\npublic class ChecksumUtilsTest extends BaseOtterTest {\n\n    @Test\n    public void test_simple() {\n        byte[] data = getBlock(10 * 1024);\n        String c1 = ChecksumUtils.checksum(data);\n        String c2 = ChecksumUtils.checksum(data, 0, data.length);\n        System.out.println(\"checksum : \" + c1 + \" \" + c2);\n        want.string(c1).isEqualTo(c2);\n    }\n\n    private byte[] getBlock(int length) {\n        byte[] rawData = new byte[length];\n        for (int i = 0; i < rawData.length; i++) {\n            rawData[i] = (byte) (' ' + RandomUtils.nextInt(95));\n\n        }\n        return rawData;\n    }\n}\n"
  },
  {
    "path": "node/etl/src/test/java/com/alibaba/otter/node/etl/common/io/CompressorTest.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.io;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.File;\nimport java.io.InputStream;\nimport java.util.Arrays;\n\nimport org.apache.commons.lang.math.RandomUtils;\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.node.etl.BaseOtterTest;\nimport com.alibaba.otter.node.etl.common.io.compress.Compressor;\nimport com.alibaba.otter.node.etl.common.io.compress.impl.PackableObject;\nimport com.alibaba.otter.node.etl.common.io.compress.impl.bzip2.BZip2Compressor;\nimport com.alibaba.otter.node.etl.common.io.compress.impl.gzip.GzipCompressor;\nimport com.alibaba.otter.shared.common.utils.NioUtils;\n\n/**\n * 数据压缩测试类\n * \n * @author jianghang 2011-10-10 上午09:06:32\n * @version 4.0.0\n */\npublic class CompressorTest extends BaseOtterTest {\n\n    private static Compressor[] comps = new Compressor[] { new GzipCompressor(), new BZip2Compressor() };\n\n    @Test\n    public void test_stream() {\n        try {\n            byte[] data = getBlock(20 * 1024);\n            byte[] result;\n            for (int i = 0; i < comps.length; i++) {\n                ByteArrayInputStream input = new ByteArrayInputStream(data);\n                Compressor comp = comps[i];\n                // 基于流的处理\n                InputStream encrypt = comp.compress(input);\n                InputStream decrypt = comp.decompress(encrypt);\n                result = NioUtils.read(decrypt);\n                check(data, result);\n                encrypt.close();\n                decrypt.close();\n            }\n\n        } catch (Exception e) {\n            want.fail();\n        }\n    }\n\n    @Test\n    public void test_block() {\n        try {\n            byte[] data = getBlock(20 * 1024);\n            for (int i = 0; i < comps.length; i++) {\n                Compressor comp = comps[i];\n                // 基于流的处理\n                byte[] encrypt = comp.compress(data);\n                byte[] decrypt = comp.decompress(encrypt);\n                check(data, decrypt);\n            }\n\n        } catch (Exception e) {\n            want.fail();\n        }\n    }\n\n    @Test\n    public void test_identify() {\n        try {\n            byte[] data = getBlock(20 * 1024);\n            File input = File.createTempFile(\"compress_\", \"src\");\n            NioUtils.write(data, input);//写入数据到文件\n\n            for (int i = 0; i < comps.length; i++) {\n                Compressor comp = comps[i];\n                // 基于流的处理\n                File output = File.createTempFile(\"compress_\", \"jkt\");\n                comp.compressTo(input, output);\n\n                Compressor icomp = (Compressor) PackableObject.identifyByHeader(output, Arrays.asList(comps));\n\n                InputStream decrypt = icomp.decompress(output);\n                byte[] result = NioUtils.read(decrypt);\n                check(data, result);\n            }\n\n        } catch (Exception e) {\n            want.fail();\n        }\n    }\n\n    private void check(byte[] src, byte[] dest) {\n        want.object(src).notNull();\n        want.object(dest).notNull();\n        want.bool(src.length == dest.length).is(true);\n\n        for (int i = 0; i < src.length; i++) {\n            if (src[i] != dest[i]) {\n                want.fail();\n            }\n        }\n    }\n\n    private byte[] getBlock(int length) {\n        byte[] rawData = new byte[length];\n        for (int i = 0; i < rawData.length; i++) {\n            rawData[i] = (byte) (' ' + RandomUtils.nextInt(95));\n\n        }\n        return rawData;\n    }\n}\n"
  },
  {
    "path": "node/etl/src/test/java/com/alibaba/otter/node/etl/common/jetty/JettyEmbedIntegration.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.jetty;\n\nimport org.eclipse.jetty.server.Connector;\nimport org.eclipse.jetty.server.Handler;\nimport org.eclipse.jetty.server.Server;\nimport org.eclipse.jetty.servlet.ServletContextHandler;\nimport org.eclipse.jetty.util.resource.Resource;\nimport org.eclipse.jetty.xml.XmlConfiguration;\n\npublic class JettyEmbedIntegration {\n\n    public static void main(String args[]) throws Exception {\n        Resource jetty_xml = Resource.newSystemResource(\"jetty/jetty.xml\");\n        XmlConfiguration configuration = new XmlConfiguration(jetty_xml.getInputStream());\n        Server server = (Server) configuration.configure();\n        int port = 8081;\n        Connector[] connectors = server.getConnectors();\n        for (Connector connector : connectors) {\n            connector.setPort(port);\n        }\n\n        Handler handler = server.getHandler();\n        if (handler != null && handler instanceof ServletContextHandler) {\n            ServletContextHandler servletHandler = (ServletContextHandler) handler;\n            servletHandler.getInitParams().put(\"org.eclipse.jetty.servlet.Default.resourceBase\", \"/tmp/\");\n        }\n\n        server.start();\n        server.join();\n    }\n}\n"
  },
  {
    "path": "node/etl/src/test/java/com/alibaba/otter/node/etl/common/jmx/JmxLoaderIntegration.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.jmx;\n\nimport java.rmi.RemoteException;\nimport java.rmi.registry.LocateRegistry;\nimport java.util.Map;\n\nimport javax.management.MBeanInfo;\nimport javax.management.MBeanServer;\nimport javax.management.MBeanServerConnection;\nimport javax.management.ObjectName;\nimport javax.management.remote.JMXConnector;\nimport javax.management.remote.JMXConnectorFactory;\nimport javax.management.remote.JMXServiceURL;\n\nimport org.jtester.annotations.SpringBeanByName;\nimport org.springframework.jmx.export.MBeanExporter;\nimport org.testng.annotations.BeforeClass;\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.node.etl.BaseOtterTest;\n\npublic class JmxLoaderIntegration extends BaseOtterTest {\n\n    static {\n        try {\n            LocateRegistry.createRegistry(1099);\n        } catch (RemoteException e) {\n            e.printStackTrace();\n        }\n    }\n\n    @SpringBeanByName\n    private MBeanExporter exporter;\n\n    @BeforeClass\n    public void initial() {\n        System.setProperty(\"nid\", \"50\");\n    }\n\n    @Test\n    public void test_simple() {\n        MBeanServer mBeanServer = exporter.getServer();\n\n        try {\n            ObjectName objectName = new ObjectName(\"bean:name=otterControllor\");\n            MBeanInfo nodeInfo = mBeanServer.getMBeanInfo(objectName);\n            System.out.println(nodeInfo);\n            Object result = mBeanServer.getAttribute(objectName, \"HeapMemoryUsage\");\n            System.out.println(result);\n\n            JMXServiceURL address = new JMXServiceURL(\"service:jmx:rmi://127.0.0.1/jndi/rmi://127.0.0.1:1099/mbean\");\n            Map environment = null;\n\n            JMXConnector cntor = JMXConnectorFactory.connect(address, environment);\n            MBeanServerConnection mbsc = cntor.getMBeanServerConnection();\n            String domain = mbsc.getDefaultDomain();\n            System.out.println(domain);\n\n            result = mbsc.getAttribute(objectName, \"HeapMemoryUsage\");\n            System.out.println(result);\n        } catch (Exception e) {\n            want.fail(e.getMessage());\n        }\n    }\n}\n"
  },
  {
    "path": "node/etl/src/test/java/com/alibaba/otter/node/etl/common/jmx/StageAggregationTest.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.jmx;\n\nimport java.util.concurrent.locks.LockSupport;\n\nimport org.apache.commons.lang.math.RandomUtils;\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.node.etl.BaseOtterTest;\nimport com.alibaba.otter.node.etl.common.jmx.StageAggregation.AggregationItem;\n\npublic class StageAggregationTest extends BaseOtterTest {\n\n    @Test\n    public void test_normal() {\n        StageAggregation aggregation = new StageAggregation(256);\n\n        for (int i = 0; i < 128; i++) {\n            long now = System.currentTimeMillis();\n            aggregation.push(new AggregationItem(now - 10 - RandomUtils.nextInt(100), now));\n            LockSupport.parkNanos(1000 * 1000L);\n        }\n\n        LockSupport.parkNanos(2000 * 1000 * 1000L);\n\n        for (int i = 0; i < 200; i++) {\n            long now = System.currentTimeMillis();\n            aggregation.push(new AggregationItem(now - 10 - RandomUtils.nextInt(100), now));\n            LockSupport.parkNanos(1000 * 1000L);\n        }\n\n        String result = aggregation.histogram();\n        System.out.println(result);\n    }\n\n    @Test\n    public void test_zero() {\n        StageAggregation aggregation = new StageAggregation(256);\n        String result = aggregation.histogram();\n        System.out.println(result);\n    }\n}\n"
  },
  {
    "path": "node/etl/src/test/java/com/alibaba/otter/node/etl/common/pipe/HttpPipeIntegration.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.pipe;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.util.Date;\n\nimport mockit.Mock;\nimport mockit.Mocked;\nimport mockit.Mockit;\n\nimport org.apache.commons.lang.math.RandomUtils;\nimport org.jtester.annotations.SpringBeanByName;\nimport org.jtester.annotations.SpringBeanFrom;\nimport org.testng.annotations.BeforeClass;\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.node.common.config.ConfigClientService;\nimport com.alibaba.otter.node.etl.BaseOtterTest;\nimport com.alibaba.otter.node.etl.common.jetty.JettyEmbedServer;\nimport com.alibaba.otter.node.etl.common.pipe.impl.http.AttachmentHttpPipe;\nimport com.alibaba.otter.node.etl.common.pipe.impl.http.HttpPipeKey;\nimport com.alibaba.otter.node.etl.common.pipe.impl.http.RowDataHttpPipe;\nimport com.alibaba.otter.shared.common.model.config.node.Node;\nimport com.alibaba.otter.shared.common.model.config.node.NodeParameter;\nimport com.alibaba.otter.shared.common.model.config.parameter.SystemParameter.RetrieverType;\nimport com.alibaba.otter.shared.common.model.config.pipeline.Pipeline;\nimport com.alibaba.otter.shared.common.utils.NioUtils;\nimport com.alibaba.otter.shared.etl.model.DbBatch;\nimport com.alibaba.otter.shared.etl.model.EventColumn;\nimport com.alibaba.otter.shared.etl.model.EventData;\nimport com.alibaba.otter.shared.etl.model.EventType;\nimport com.alibaba.otter.shared.etl.model.FileBatch;\nimport com.alibaba.otter.shared.etl.model.FileData;\nimport com.alibaba.otter.shared.etl.model.Identity;\nimport com.alibaba.otter.shared.etl.model.RowBatch;\n\npublic class HttpPipeIntegration extends BaseOtterTest {\n\n    private static final String tmp = System.getProperty(\"java.io.tmpdir\", \"/tmp\");\n\n    @SpringBeanByName\n    private AttachmentHttpPipe  attachmentHttpPipe;\n\n    @SpringBeanByName\n    private RowDataHttpPipe     rowDataHttpPipe;\n\n    @SpringBeanFrom\n    @Mocked\n    private ConfigClientService configClientService;\n\n    @BeforeClass\n    public void initial() {\n        Mockit.setUpMock(JettyEmbedServer.class, new Object() {\n\n            @Mock\n            private Integer getPort() {\n                return null;\n            }\n\n        });\n\n    }\n\n    @Test\n    public void test_attachment() {\n        final Node currentNode = new Node();\n        currentNode.setId(1L);\n        currentNode.setIp(\"127.0.0.1\");\n        currentNode.setParameters(new NodeParameter());\n        final Pipeline pipeline = new Pipeline();\n        pipeline.getParameters().setRetriever(RetrieverType.ARIA2C);\n        // mock一下\n        new NonStrictExpectations() {\n\n            {\n                configClientService.currentNode();\n                returns(currentNode);\n\n                configClientService.findPipeline(anyLong);\n                returns(pipeline);\n            }\n        };\n        Identity identity = new Identity();\n        identity.setChannelId(100L);\n        identity.setPipelineId(100L);\n        identity.setProcessId(100L);\n\n        FileBatch fileBatch = new FileBatch();\n        fileBatch.setIdentity(identity);\n        File localFile = new File(tmp, \"httpPipeTest.jpg\");\n        FileData localFileData = new FileData();\n        localFileData.setEventType(EventType.INSERT);\n        localFileData.setPath(localFile.getPath());\n        fileBatch.getFiles().add(localFileData);\n        try {\n            byte[] data = getBlock(10 * 1024);\n            NioUtils.write(data, localFile);\n            HttpPipeKey key = attachmentHttpPipe.put(fileBatch);\n            File target = attachmentHttpPipe.get(key);\n            byte[] getbytes = NioUtils.read(new File(target, localFile.getPath()));\n            check(data, getbytes);\n        } catch (IOException e) {\n            want.fail();\n        } finally {\n            NioUtils.delete(localFile);\n        }\n\n    }\n\n    @Test\n    public void test_rowData() {\n        final Node currentNode = new Node();\n        currentNode.setId(1L);\n        currentNode.setIp(\"127.0.0.1\");\n        currentNode.setParameters(new NodeParameter());\n        final Pipeline pipeline = new Pipeline();\n        pipeline.getParameters().setRetriever(RetrieverType.ARIA2C);\n        // mock一下\n        new NonStrictExpectations() {\n\n            {\n                configClientService.currentNode();\n                returns(currentNode);\n\n                configClientService.findPipeline(anyLong);\n                returns(pipeline);\n            }\n        };\n        Identity identity = new Identity();\n        identity.setChannelId(100L);\n        identity.setPipelineId(100L);\n        identity.setProcessId(100L);\n\n        FileBatch fileBatch = new FileBatch();\n        fileBatch.setIdentity(identity);\n        File localFile = new File(tmp, \"httpPipeTest.jpg\");\n        FileData localFileData = new FileData();\n        localFileData.setPath(localFile.getPath());\n        localFileData.setEventType(EventType.INSERT);\n        localFileData.setLastModifiedTime(new Date().getTime());\n        localFileData.setSize(100L);\n        localFileData.setTableId(1L);\n        fileBatch.getFiles().add(localFileData);\n\n        RowBatch rowBatch = new RowBatch();\n        rowBatch.setIdentity(identity);\n        EventData eventData = new EventData();\n        eventData.setTableId(1L);\n        eventData.setSchemaName(\"otter\");\n        eventData.setTableName(\"test\");\n        eventData.setEventType(EventType.INSERT);\n        eventData.setExecuteTime(100L);\n\n        EventColumn primaryKey = new EventColumn();\n        primaryKey.setColumnName(\"id\");\n        primaryKey.setColumnType(1);\n        primaryKey.setColumnValue(\"1\");\n        primaryKey.setKey(true);\n        primaryKey.setNull(false);\n        eventData.getKeys().add(primaryKey);\n\n        EventColumn column = new EventColumn();\n        column.setColumnName(\"name\");\n        column.setColumnType(1);\n        column.setColumnValue(\"test\");\n        column.setKey(false);\n        column.setNull(false);\n        eventData.getColumns().add(column);\n\n        rowBatch.merge(eventData);\n\n        DbBatch dbBatch = new DbBatch();\n        dbBatch.setRowBatch(rowBatch);\n        dbBatch.setFileBatch(fileBatch);\n\n        HttpPipeKey key = rowDataHttpPipe.put(dbBatch);\n        DbBatch target = rowDataHttpPipe.get(key);\n\n        want.bool(target.getRowBatch().getIdentity().equals(identity));\n        want.object(target).notNull();\n    }\n\n    private void check(byte[] src, byte[] dest) {\n        want.object(src).notNull();\n        want.object(dest).notNull();\n        want.bool(src.length == dest.length).is(true);\n\n        for (int i = 0; i < src.length; i++) {\n            if (src[i] != dest[i]) {\n                want.fail();\n            }\n        }\n    }\n\n    private byte[] getBlock(int length) {\n        byte[] rawData = new byte[length];\n        for (int i = 0; i < rawData.length; i++) {\n            rawData[i] = (byte) (' ' + RandomUtils.nextInt(95));\n\n        }\n        return rawData;\n    }\n}\n"
  },
  {
    "path": "node/etl/src/test/java/com/alibaba/otter/node/etl/common/pipe/MemoryPipeTest.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.pipe;\n\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.node.etl.common.pipe.impl.memory.MemoryPipeKey;\nimport com.alibaba.otter.node.etl.common.pipe.impl.memory.RowDataMemoryPipe;\nimport com.alibaba.otter.node.etl.BaseOtterTest;\nimport com.alibaba.otter.shared.etl.model.DbBatch;\nimport com.alibaba.otter.shared.etl.model.Identity;\nimport com.alibaba.otter.shared.etl.model.RowBatch;\n\npublic class MemoryPipeTest extends BaseOtterTest {\n\n    private static final String tmp = System.getProperty(\"java.io.tmpdir\", \"/tmp\");\n\n    @Test\n    public void test_ok() {\n        RowDataMemoryPipe pipe = new RowDataMemoryPipe();\n        pipe.setDownloadDir(tmp);\n        try {\n            pipe.afterPropertiesSet();\n        } catch (Exception e) {\n            want.fail();\n        }\n\n        DbBatch source = new DbBatch();\n        RowBatch rowBatch = new RowBatch();\n        Identity identity = new Identity();\n        identity.setChannelId(100L);\n        identity.setPipelineId(100L);\n        identity.setProcessId(100L);\n        rowBatch.setIdentity(identity);\n        source.setRowBatch(rowBatch);\n\n        MemoryPipeKey key = pipe.put(source);\n        DbBatch target = pipe.get(key);\n        want.bool(source == target).is(true);// 引用为同一个\n    }\n\n    @Test\n    public void test_timeout() {\n        RowDataMemoryPipe pipe = new RowDataMemoryPipe();\n        pipe.setTimeout(1 * 1000L);// 1s后超时\n        pipe.setDownloadDir(tmp);\n        try {\n            pipe.afterPropertiesSet();\n        } catch (Exception e) {\n            want.fail();\n        }\n\n        DbBatch source = new DbBatch();\n        RowBatch rowBatch = new RowBatch();\n        Identity identity = new Identity();\n        identity.setChannelId(100L);\n        identity.setPipelineId(100L);\n        identity.setProcessId(100L);\n        rowBatch.setIdentity(identity);\n        source.setRowBatch(rowBatch);\n\n        MemoryPipeKey key = pipe.put(source);\n        try {\n            Thread.sleep(1500L);\n        } catch (InterruptedException e) {\n            want.fail();\n        }\n        DbBatch target = pipe.get(key);\n        want.bool(target == null).is(true);// 返回结果为空\n    }\n}\n"
  },
  {
    "path": "node/etl/src/test/java/com/alibaba/otter/node/etl/common/pipe/RpcPipeTest.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.common.pipe;\n\nimport mockit.Mock;\nimport mockit.Mockit;\n\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.node.common.communication.NodeCommmunicationClient;\nimport com.alibaba.otter.node.etl.BaseOtterTest;\nimport com.alibaba.otter.node.etl.TestUtils;\nimport com.alibaba.otter.node.etl.common.pipe.impl.rpc.RowDataRpcPipe;\nimport com.alibaba.otter.node.etl.common.pipe.impl.rpc.RpcPipeKey;\nimport com.alibaba.otter.shared.communication.core.model.Event;\nimport com.alibaba.otter.shared.etl.model.DbBatch;\nimport com.alibaba.otter.shared.etl.model.Identity;\nimport com.alibaba.otter.shared.etl.model.RowBatch;\n\npublic class RpcPipeTest extends BaseOtterTest {\n\n    @Test\n    public void test_ok() {\n        final DbBatch source = new DbBatch();\n        RowBatch rowBatch = new RowBatch();\n        Identity identity = new Identity();\n        identity.setChannelId(100L);\n        identity.setPipelineId(100L);\n        identity.setProcessId(100L);\n        rowBatch.setIdentity(identity);\n        source.setRowBatch(rowBatch);\n\n        final RowDataRpcPipe pipe = new RowDataRpcPipe();\n        try {\n            pipe.afterPropertiesSet();\n        } catch (Exception e) {\n            want.fail();\n        }\n        Mockit.setUpMock(NodeCommmunicationClient.class, new Object() {\n\n            @Mock\n            public Object call(Long nid, final Event event) {\n                try {\n                    return TestUtils.invokeMethod(pipe, \"onGet\", event);\n                } catch (Exception e) {\n                    want.fail();\n                }\n\n                return null;\n            }\n\n        });\n\n        Mockit.setUpMock(RowDataRpcPipe.class, new Object() {\n\n            @Mock\n            private Long getNid() {\n                return 1L;\n            }\n\n        });\n        pipe.setNodeCommmunicationClient(new NodeCommmunicationClient());\n        RpcPipeKey key = pipe.put(source);\n        DbBatch target = pipe.get(key);\n        want.bool(source.getRowBatch().getIdentity().equals(target.getRowBatch().getIdentity())).is(true);// identify相等\n    }\n\n    @Test\n    public void test_timeout() {\n\n        final DbBatch source = new DbBatch();\n        RowBatch rowBatch = new RowBatch();\n        Identity identity = new Identity();\n        identity.setChannelId(100L);\n        identity.setPipelineId(100L);\n        identity.setProcessId(100L);\n        rowBatch.setIdentity(identity);\n        source.setRowBatch(rowBatch);\n\n        final RowDataRpcPipe pipe = new RowDataRpcPipe();\n        pipe.setTimeout(1 * 1000L);// 1s后超时\n        try {\n            pipe.afterPropertiesSet();\n        } catch (Exception e) {\n            want.fail();\n        }\n        Mockit.setUpMock(NodeCommmunicationClient.class, new Object() {\n\n            @Mock\n            public Object call(Long nid, final Event event) {\n                try {\n                    return TestUtils.invokeMethod(pipe, \"onGet\", event);\n                } catch (Exception e) {\n                    want.fail();\n                }\n\n                return null;\n            }\n\n        });\n\n        Mockit.setUpMock(RowDataRpcPipe.class, new Object() {\n\n            @Mock\n            private Long getNid() {\n                return 1L;\n            }\n\n        });\n        pipe.setNodeCommmunicationClient(new NodeCommmunicationClient());\n\n        RpcPipeKey key = pipe.put(source);\n        try {\n            Thread.sleep(1500L);\n        } catch (InterruptedException e) {\n            want.fail();\n        }\n        DbBatch target = pipe.get(key);\n        want.bool(target == null).is(true);// 返回结果为空\n    }\n}\n"
  },
  {
    "path": "node/etl/src/test/java/com/alibaba/otter/node/etl/conflict/FileBatchConflictDetectServiceIntegration.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.conflict;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport mockit.Mocked;\n\nimport org.apache.commons.lang.math.RandomUtils;\nimport org.jtester.annotations.SpringBeanByName;\nimport org.jtester.annotations.SpringBeanFrom;\nimport org.testng.annotations.BeforeClass;\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.node.common.config.ConfigClientService;\nimport com.alibaba.otter.node.etl.BaseOtterTest;\nimport com.alibaba.otter.shared.common.model.config.node.Node;\nimport com.alibaba.otter.shared.common.model.config.pipeline.Pipeline;\nimport com.alibaba.otter.shared.common.utils.NioUtils;\nimport com.alibaba.otter.shared.etl.model.EventType;\nimport com.alibaba.otter.shared.etl.model.FileBatch;\nimport com.alibaba.otter.shared.etl.model.FileData;\nimport com.alibaba.otter.shared.etl.model.Identity;\n\npublic class FileBatchConflictDetectServiceIntegration extends BaseOtterTest {\n\n    private static final String            OTTERLOAD = \"otterload\";\n\n    private static final String            tmp       = System.getProperty(\"java.io.tmpdir\", \"/tmp\");\n\n    @SpringBeanByName\n    private FileBatchConflictDetectService fileBatchConflictDetectService;\n\n    @SpringBeanFrom\n    @Mocked\n    private ConfigClientService            configClientService;\n\n    @BeforeClass\n    public void initial() {\n        System.setProperty(\"nid\", \"1\");\n    }\n\n    @Test\n    public void test_localFile() {\n        final Pipeline pipeline = new Pipeline();\n        pipeline.setId(100L);\n\n        final Node currentNode = new Node();\n        currentNode.setId(1L);\n        new NonStrictExpectations() {\n\n            {\n                configClientService.currentNode();\n                returns(currentNode);\n                configClientService.findPipeline(anyLong);\n                returns(pipeline);\n            }\n        };\n\n        Identity identity = new Identity();\n        identity.setChannelId(100L);\n        identity.setPipelineId(100L);\n        identity.setProcessId(100L);\n\n        FileBatch fileBatch = new FileBatch();\n        fileBatch.setIdentity(identity);\n\n        fileBatch.getFiles().addAll(generatorLocalFileData(\"fileLoad\", 10));\n        FileBatch result = fileBatchConflictDetectService.detect(fileBatch, 1L);\n        want.number(result.getFiles().size()).isEqualTo(0);\n\n        NioUtils.delete(new File(tmp + File.separator + OTTERLOAD));\n    }\n\n    private List<FileData> generatorLocalFileData(String prefix, int count) {\n        List<FileData> result = new ArrayList<FileData>();\n        for (int i = 0; i < count; i++) {\n            String filepath = tmp + File.separator + OTTERLOAD + File.separator;\n            File local = new File(filepath, prefix + \"_\" + i + \".jpg\");\n            FileData localFileData = new FileData();\n            localFileData.setEventType(EventType.UPDATE);\n            localFileData.setPairId(i);\n            localFileData.setTableId(i);\n            localFileData.setNameSpace(null);\n            localFileData.setPath(local.getPath());\n            try {\n                byte[] data = getBlock((i + 1) * 1024);\n                localFileData.setSize(data.length);\n                NioUtils.write(data, local);\n                localFileData.setLastModifiedTime(local.lastModified());\n            } catch (IOException e) {\n                want.fail();\n            }\n            result.add(localFileData);\n        }\n\n        return result;\n    }\n\n    private byte[] getBlock(int length) {\n        byte[] rawData = new byte[length];\n        for (int i = 0; i < rawData.length; i++) {\n            rawData[i] = (byte) (' ' + RandomUtils.nextInt(95));\n\n        }\n        return rawData;\n    }\n}\n"
  },
  {
    "path": "node/etl/src/test/java/com/alibaba/otter/node/etl/extract/DatabaseExtractorTest.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.extract;\n\nimport java.sql.Types;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Date;\nimport java.util.List;\n\nimport mockit.Mocked;\n\nimport org.apache.commons.lang.math.RandomUtils;\nimport org.jtester.annotations.SpringBeanByName;\nimport org.jtester.annotations.SpringBeanFrom;\nimport org.testng.annotations.BeforeMethod;\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.node.common.config.ConfigClientService;\nimport com.alibaba.otter.node.etl.BaseDbTest;\nimport com.alibaba.otter.node.etl.extract.extractor.DatabaseExtractor;\nimport com.alibaba.otter.shared.common.model.config.channel.ChannelParameter.SyncConsistency;\nimport com.alibaba.otter.shared.common.model.config.channel.ChannelParameter.SyncMode;\nimport com.alibaba.otter.shared.common.model.config.data.DataMediaPair;\nimport com.alibaba.otter.shared.common.model.config.data.db.DbDataMedia;\nimport com.alibaba.otter.shared.common.model.config.pipeline.Pipeline;\nimport com.alibaba.otter.shared.etl.model.DbBatch;\nimport com.alibaba.otter.shared.etl.model.EventColumn;\nimport com.alibaba.otter.shared.etl.model.EventData;\nimport com.alibaba.otter.shared.etl.model.EventType;\nimport com.alibaba.otter.shared.etl.model.Identity;\nimport com.alibaba.otter.shared.etl.model.RowBatch;\n\npublic class DatabaseExtractorTest extends BaseDbTest {\n\n    @SpringBeanByName\n    private DatabaseExtractor   databaseExtractor;\n\n    @SpringBeanFrom\n    @Mocked\n    private ConfigClientService configClientService;\n\n    private Identity            identity = null;\n\n    @BeforeMethod\n    public void setUp() {\n        identity = new Identity();\n        identity.setChannelId(100L);\n        identity.setPipelineId(100L);\n        identity.setProcessId(100L);\n    }\n\n    @Test\n    public void test_global_row() {\n        final Pipeline pipeline = new Pipeline();\n        pipeline.setId(100L);\n        pipeline.getParameters().setSyncMode(SyncMode.ROW);\n        pipeline.getParameters().setSyncConsistency(SyncConsistency.MEDIA);// 设置为全局\n\n        int start = RandomUtils.nextInt();\n        int count = 10;\n        List<DataMediaPair> pairs = getDataMediaPairForMysql(start, count);\n        pipeline.setPairs(pairs);\n\n        new NonStrictExpectations() {\n\n            {\n                configClientService.findPipeline(100L);\n                returns(pipeline);\n            }\n        };\n\n        // 构造数据\n        RowBatch rowBatch = new RowBatch();\n        rowBatch.setIdentity(identity);\n        for (int tableId = start; tableId < start + count; tableId++) {\n            for (int i = start; i < start + count; i++) {\n                EventData eventData = getEventData(tableId, i);\n                eventData.setSchemaName(\"srf\");\n                eventData.setTableName(\"columns\");\n                rowBatch.merge(eventData);\n            }\n        }\n\n        databaseExtractor.extract(new DbBatch(rowBatch));\n        want.number(rowBatch.getDatas().size()).isEqualTo(count);\n    }\n\n    public void test_override_field() {\n        final Pipeline pipeline = new Pipeline();\n        pipeline.setId(100L);\n        pipeline.getParameters().setSyncMode(SyncMode.FIELD);\n        pipeline.getParameters().setSyncConsistency(SyncConsistency.BASE);// 设置为全局\n\n        int start = RandomUtils.nextInt();\n        int count = 10;\n        List<DataMediaPair> pairs = getDataMediaPairForOracle(start, count);\n        pipeline.setPairs(pairs);\n\n        new NonStrictExpectations() {\n\n            {\n                configClientService.findPipeline(100L);\n                returns(pipeline);\n            }\n        };\n\n        // 构造数据\n        RowBatch rowBatch = new RowBatch();\n        rowBatch.setIdentity(identity);\n        for (int tableId = start; tableId < start + count; tableId++) {\n            for (int i = start; i < start + count; i++) {\n                EventData eventData = getEventData(tableId, i);\n                eventData.setSchemaName(\"srf\");\n                eventData.setTableName(\"columns\");\n                eventData.setSyncConsistency(SyncConsistency.MEDIA);\n                rowBatch.merge(eventData);\n            }\n        }\n\n        databaseExtractor.extract(new DbBatch(rowBatch));\n\n        want.number(rowBatch.getDatas().size()).isEqualTo(count);\n    }\n\n    private List<DataMediaPair> getDataMediaPairForMysql(long tableId, int count) {\n        List<DataMediaPair> pairs = new ArrayList<DataMediaPair>();\n        for (int i = 0; i < count; i++) {\n            DataMediaPair pair = new DataMediaPair();\n            pair.setId(Long.valueOf(i));\n            pair.setPullWeight(1L);\n            pair.setPushWeight(1L);\n\n            DbDataMedia mysqlMedia = getMysqlMedia();\n            mysqlMedia.setId(tableId + i);\n            pair.setSource(mysqlMedia);\n\n            DbDataMedia oracleMedia = getOracleMedia();\n            oracleMedia.setId(tableId + i + count);\n            pair.setTarget(oracleMedia);\n            pairs.add(pair);\n        }\n        return pairs;\n    }\n\n    private List<DataMediaPair> getDataMediaPairForOracle(long tableId, int count) {\n        List<DataMediaPair> pairs = new ArrayList<DataMediaPair>();\n        for (int i = 0; i < count; i++) {\n            DataMediaPair pair = new DataMediaPair();\n            pair.setId(Long.valueOf(i));\n            pair.setPullWeight(1L);\n            pair.setPushWeight(1L);\n\n            DbDataMedia oracleMedia = getOracleMedia();\n            oracleMedia.setId(tableId + i);\n            pair.setSource(oracleMedia);\n\n            DbDataMedia mysqlMedia = getMysqlMedia();\n            mysqlMedia.setId(tableId + i + count);\n            pair.setTarget(mysqlMedia);\n\n            pairs.add(pair);\n        }\n        return pairs;\n    }\n\n    private EventData getEventData(long tableId, int value) {\n        EventData eventData = new EventData();\n        eventData.setTableId(tableId);\n        eventData.setEventType(EventType.INSERT);\n        eventData.setExecuteTime(new Date().getTime());\n        eventData.setKeys(getPrimary(value));\n        eventData.setColumns(getColumn(value));\n\n        return eventData;\n    }\n\n    private List<EventColumn> getPrimary(int value) {\n        EventColumn pk = new EventColumn();\n        pk.setColumnName(\"id\");\n        pk.setColumnType(java.sql.Types.INTEGER);\n        pk.setColumnValue(String.valueOf(value));\n        pk.setIndex(1);\n        pk.setNull(false);\n        return Arrays.asList(pk);\n    }\n\n    private List<EventColumn> getColumn(int value) {\n        List<EventColumn> result = new ArrayList<EventColumn>();\n        result.add(buildColumn(\"id\", Types.INTEGER, \"\" + value, true, false));\n        result.add(buildColumn(\"name\", Types.VARCHAR, \"ljh_\" + value, true, false));\n\n        result.add(buildColumn(\"alias_name\", Types.CHAR, \"hello_\" + value, false, false));\n        result.add(buildColumn(\"amount\", Types.DECIMAL, \"100.01\", false, false));\n        result.add(buildColumn(\"text_b\", Types.BLOB, \"[116,101,120,116,95,98]\", false, false));\n        result.add(buildColumn(\"text_c\", Types.CLOB, \"中文\", false, false));\n        result.add(buildColumn(\"curr_date\", Types.DATE, \"2011-01-01\", false, false));\n        result.add(buildColumn(\"gmt_create\", Types.TIMESTAMP, \"2011-01-01 11:11:11\", false, false));\n        result.add(buildColumn(\"gmt_modify\", Types.TIMESTAMP, \"2011-01-01 11:11:11\", false, false));\n        return result;\n    }\n\n    private EventColumn buildColumn(String name, int type, String value, boolean isKey, boolean isNull) {\n        EventColumn column = new EventColumn();\n        column.setColumnName(name);\n        column.setColumnType(type);\n        column.setColumnValue(value);\n        column.setKey(isKey);\n        column.setNull(isNull);\n        return column;\n    }\n\n}\n"
  },
  {
    "path": "node/etl/src/test/java/com/alibaba/otter/node/etl/extract/EventProcessorFactoryIntegration.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.extract;\n\npublic class EventProcessorFactoryIntegration {\n\n    // private static final String ROOT_DIR = \"/home/jianghang/work/otter-4.0.3/node/extends/target/classes\";\n\n    // @Test\n    // public void testGetEventProcessor() {\n    // EventProcessorFactory factory = new DefaultEventProcessorFactory(ROOT_DIR);\n    //\n    // String className = \"com.alibaba.otter.node.extend.processor.DefaultTransferProcessor\";\n    // EventProcessor eventProcessor = factory.getEventProcessor(className);\n    // Assert.assertEquals(className, eventProcessor.getClass().getName());\n    // }\n\n    // @Test\n    // public void testGetActiveEventProcessorMap() {\n    // EventProcessorFactory factory = new DefaultEventProcessorFactory(ROOT_DIR);\n    //\n    // String className = \"com.alibaba.otter.node.extend.processor.DefaultTransferProcessor\";\n    // EventProcessor eventProcessor = factory.getEventProcessor(className);\n    // Assert.assertEquals(className, eventProcessor.getClass().getName());\n    //\n    // Map<String, EventProcessor> map = factory.getActivEventProcessorMap();\n    // for (Map.Entry<String, EventProcessor> entry : map.entrySet()) {\n    // Assert.assertEquals(className, entry.getValue().getClass().getName());\n    // }\n    // }\n}\n"
  },
  {
    "path": "node/etl/src/test/java/com/alibaba/otter/node/etl/extract/FreedomExtractorTest.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.extract;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Date;\nimport java.util.List;\n\nimport mockit.Mocked;\n\nimport org.apache.commons.lang.math.RandomUtils;\nimport org.jtester.annotations.SpringBeanByName;\nimport org.jtester.annotations.SpringBeanFrom;\nimport org.testng.annotations.BeforeMethod;\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.node.common.config.ConfigClientService;\nimport com.alibaba.otter.node.etl.BaseDbTest;\nimport com.alibaba.otter.node.etl.extract.extractor.FreedomExtractor;\nimport com.alibaba.otter.shared.common.model.config.data.DataMediaPair;\nimport com.alibaba.otter.shared.common.model.config.data.db.DbDataMedia;\nimport com.alibaba.otter.shared.common.model.config.pipeline.Pipeline;\nimport com.alibaba.otter.shared.etl.model.DbBatch;\nimport com.alibaba.otter.shared.etl.model.EventColumn;\nimport com.alibaba.otter.shared.etl.model.EventData;\nimport com.alibaba.otter.shared.etl.model.EventType;\nimport com.alibaba.otter.shared.etl.model.Identity;\nimport com.alibaba.otter.shared.etl.model.RowBatch;\n\nclass FreedomExtractorTest extends BaseDbTest {\n\n    @SpringBeanByName\n    private FreedomExtractor    freedomExtractor;\n\n    @SpringBeanFrom\n    @Mocked\n    private ConfigClientService configClientService;\n\n    private Identity            identity = null;\n\n    @BeforeMethod\n    public void setUp() {\n        identity = new Identity();\n        identity.setChannelId(100L);\n        identity.setPipelineId(100L);\n        identity.setProcessId(100L);\n    }\n\n    @Test\n    public void test_mysql() {\n        final Pipeline pipeline = new Pipeline();\n        pipeline.setId(100L);\n\n        int start = RandomUtils.nextInt();\n        int count = 10;\n        List<DataMediaPair> pairs = getDataMediaPairForMysql(start, count);\n        pipeline.setPairs(pairs);\n\n        new NonStrictExpectations() {\n\n            {\n                configClientService.findPipeline(100L);\n                returns(pipeline);\n            }\n        };\n\n        // 构造数据\n        RowBatch rowBatch = new RowBatch();\n        rowBatch.setIdentity(identity);\n        for (int tableId = start; tableId < start + count; tableId++) {\n            for (int i = start; i < start + count; i++) {\n                EventData eventData = getEventData(tableId, i);\n                eventData.setSchemaName(\"retl\");\n                eventData.setTableName(\"retl_buffer\");\n                rowBatch.merge(eventData);\n            }\n        }\n\n        DbBatch dbBatch = new DbBatch(rowBatch);\n        freedomExtractor.extract(dbBatch);\n        want.collection(dbBatch.getRowBatch().getDatas()).sizeEq(count * count);\n    }\n\n    @Test\n    public void test_oracle() {\n        final Pipeline pipeline = new Pipeline();\n        pipeline.setId(100L);\n\n        int start = RandomUtils.nextInt();\n        int count = 10;\n        List<DataMediaPair> pairs = getDataMediaPairForOracle(start, count);\n        pipeline.setPairs(pairs);\n\n        new NonStrictExpectations() {\n\n            {\n                configClientService.findPipeline(100L);\n                returns(pipeline);\n            }\n        };\n\n        // 构造数据\n        RowBatch rowBatch = new RowBatch();\n        rowBatch.setIdentity(identity);\n        for (int tableId = start; tableId < start + count; tableId++) {\n            for (int i = start; i < start + count; i++) {\n                EventData eventData = getEventData(tableId, i);\n                eventData.setSchemaName(\"retl\");\n                eventData.setTableName(\"retl_buffer\");\n                rowBatch.merge(eventData);\n            }\n        }\n\n        DbBatch dbBatch = new DbBatch(rowBatch);\n        freedomExtractor.extract(dbBatch);\n        want.collection(dbBatch.getRowBatch().getDatas()).sizeEq(count * count);\n    }\n\n    private EventData getEventData(long tableId, int value) {\n        EventData eventData = new EventData();\n        eventData.setTableId(tableId);\n        eventData.setEventType(EventType.INSERT);\n        eventData.setExecuteTime(new Date().getTime());\n        eventData.setKeys(getPrimary(value));\n        eventData.setColumns(getColumn(value));\n\n        return eventData;\n    }\n\n    private List<EventColumn> getPrimary(int value) {\n        EventColumn pk = new EventColumn();\n        pk.setColumnName(\"id\");\n        pk.setColumnType(java.sql.Types.INTEGER);\n        pk.setColumnValue(String.valueOf(value));\n        pk.setIndex(1);\n        pk.setNull(false);\n        return Arrays.asList(pk);\n    }\n\n    private List<EventColumn> getColumn(int value) {\n        EventColumn c1 = new EventColumn();\n        c1.setColumnName(\"type\");\n        c1.setColumnValue(\"I\");\n\n        EventColumn c2 = new EventColumn();\n        c2.setColumnName(\"table_id\");\n        c2.setColumnValue(String.valueOf(value));\n\n        EventColumn c3 = new EventColumn();\n        c3.setColumnName(\"pk_data\");\n        c3.setColumnValue(String.valueOf(value) + ((char) 1) + \"hello\");\n        return Arrays.asList(c1, c2, c3);\n    }\n\n    private List<DataMediaPair> getDataMediaPairForMysql(long tableId, int count) {\n        List<DataMediaPair> pairs = new ArrayList<DataMediaPair>();\n        for (int i = 0; i < count; i++) {\n            DataMediaPair pair = new DataMediaPair();\n            pair.setId(Long.valueOf(i));\n            pair.setPullWeight(1L);\n            pair.setPushWeight(1L);\n\n            DbDataMedia mysqlMedia = getMysqlMedia();\n            mysqlMedia.setId(tableId + i);\n            pair.setSource(mysqlMedia);\n\n            DbDataMedia oracleMedia = getOracleMedia();\n            oracleMedia.setId(tableId + i + count);\n            pair.setTarget(oracleMedia);\n            pairs.add(pair);\n        }\n        return pairs;\n    }\n\n    private List<DataMediaPair> getDataMediaPairForOracle(long tableId, int count) {\n        List<DataMediaPair> pairs = new ArrayList<DataMediaPair>();\n        for (int i = 0; i < count; i++) {\n            DataMediaPair pair = new DataMediaPair();\n            pair.setId(Long.valueOf(i));\n            pair.setPullWeight(1L);\n            pair.setPushWeight(1L);\n\n            DbDataMedia oracleMedia = getOracleMedia();\n            oracleMedia.setId(tableId + i);\n            pair.setSource(oracleMedia);\n\n            DbDataMedia mysqlMedia = getMysqlMedia();\n            mysqlMedia.setId(tableId + i + count);\n            pair.setTarget(mysqlMedia);\n\n            pairs.add(pair);\n        }\n        return pairs;\n    }\n}\n"
  },
  {
    "path": "node/etl/src/test/java/com/alibaba/otter/node/etl/launcher/ArbitrateInitIntegration.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.launcher;\n\npublic class ArbitrateInitIntegration {\n\n    public static void main(String args[]) {\n        // System.setProperty(\"nid\", \"1\");\n        // OtterContextLocator.getOtterController();\n        //\n        // cleaner(\"/otter/channel/1/1/termin\", \"/otter/channel/1/1/termin\");\n        // cleaner(\"/otter/channel/1/1/process\", \"/otter/channel/1/1/process\");\n        // cleaner(\"/otter/channel/1/1/lock/load\", \"/otter/channel/1/1/lock/load\");\n        // cleaner(\"/otter/channel/2/2/termin\", \"/otter/channel/2/2/termin\");\n        // cleaner(\"/otter/channel/2/2/process\", \"/otter/channel/2/2/process\");\n        // cleaner(\"/otter/channel/2/2/lock/load\", \"/otter/channel/2/2/lock/load\");\n        // cleaner(\"/e3/destinations/mysql\", \"/e3/destinations/mysql\");\n        // cleaner(\"/e3/destinations/oracle\", \"/e3/destinations/oracle\");\n        //\n        // // ChannelArbitrateEvent channelEvent =\n        // OtterArbitrateServiceLocator.getArbitrateManageService().channelEvent();\n        // // channelEvent.init(1L);\n        // // PipelineArbitrateEvent pipelineEvent =\n        // // OtterArbitrateServiceLocator.getArbitrateManageService().pipelineEvent();\n        // // pipelineEvent.init(1L, 1L);\n        //\n        // ZkClientx zookeeper = ZooKeeperClient.getInstance();\n        // try {\n        // zookeeper.setData(\"/otter/channel/1/1\", new byte[0], -1); // 更新记录\n        // zookeeper.setData(\"/otter/channel/2/2\", new byte[0], -1); // 更新记录\n        // } catch (KeeperException e) {\n        // e.printStackTrace();\n        // } catch (InterruptedException e) {\n        // e.printStackTrace();\n        // }\n        System.exit(0);\n        // long[] ids = new long[] { 1L, 2L, 3L, 4L, 5L, 11L, 13L, 14L, 15L, 16L, 17L, 18L, 19L };\n        // for (long id : ids) {\n        // channelEvent.init(id);\n        // }\n        // PipelineArbitrateEvent pipelineEvent =\n        // OtterArbitrateServiceLocator.getArbitrateManageService().pipelineEvent();\n        // System.out.println(pipelineEvent);\n        // pipelineEvent.init(1L, 1L);\n    }\n\n    // private static void cleaner(String root, String path) {\n    // ZkClientx zookeeper = ZooKeeperClient.getInstance();\n    // try {\n    // List<String> nodes = zookeeper.getChildren(path, false);\n    // for (String node : nodes) {\n    // cleaner(root, path + \"/\" + node);\n    // }\n    // if (path.equals(root)) {\n    // return;\n    // } else {\n    // System.out.println(\"clean :\" + path);\n    // zookeeper.delete(path, -1);\n    // return;\n    // }\n    // } catch (NoNodeException e) {\n    // // ignore\n    // } catch (KeeperException e) {\n    // e.printStackTrace();\n    // } catch (InterruptedException e) {\n    // e.printStackTrace();\n    // }\n    // }\n}\n"
  },
  {
    "path": "node/etl/src/test/java/com/alibaba/otter/node/etl/launcher/OtterLoaderFactoryIntegration.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.launcher;\n\nimport java.util.concurrent.CountDownLatch;\nimport java.util.concurrent.ExecutorService;\n\nimport org.jtester.annotations.SpringBeanByName;\nimport org.testng.annotations.BeforeClass;\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.node.etl.BaseDbTest;\nimport com.alibaba.otter.node.etl.load.loader.OtterLoaderFactory;\nimport com.alibaba.otter.shared.etl.model.DbBatch;\nimport com.alibaba.otter.shared.etl.model.FileBatch;\nimport com.alibaba.otter.shared.etl.model.Identity;\nimport com.alibaba.otter.shared.etl.model.RowBatch;\n\npublic class OtterLoaderFactoryIntegration extends BaseDbTest {\n\n    @SpringBeanByName\n    private ExecutorService    executorService;\n\n    @SpringBeanByName\n    private OtterLoaderFactory otterLoaderFactory;\n\n    @BeforeClass\n    public void initial() {\n        System.setProperty(\"nid\", \"1\");\n    }\n\n    @Test\n    public void test_simple() {\n        Identity identity = new Identity();\n        identity.setChannelId(100L);\n        identity.setPipelineId(100L);\n        identity.setProcessId(100L);\n\n        RowBatch rowBatch = new RowBatch();\n        rowBatch.setIdentity(identity);\n\n        FileBatch fileBatch = new FileBatch();\n        fileBatch.setIdentity(identity);\n\n        final DbBatch dbBatch = new DbBatch();\n        dbBatch.setRowBatch(rowBatch);\n        dbBatch.setFileBatch(fileBatch);\n        final CountDownLatch latch = new CountDownLatch(1);\n        executorService.submit(new Runnable() {\n\n            public void run() {\n                System.out.println(\"first run!!!!!!\");\n                otterLoaderFactory.load(dbBatch);\n                latch.countDown();\n            }\n        });\n\n        try {\n            latch.await();\n        } catch (InterruptedException e) {\n        }\n    }\n}\n"
  },
  {
    "path": "node/etl/src/test/java/com/alibaba/otter/node/etl/load/DbLoadActionTest.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.load;\n\nimport java.sql.Types;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport mockit.Mocked;\n\nimport org.jtester.annotations.SpringBeanFrom;\nimport org.jtester.core.TestedObject;\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.node.common.config.ConfigClientService;\nimport com.alibaba.otter.node.etl.BaseDbTest;\nimport com.alibaba.otter.node.etl.load.loader.db.DbLoadAction;\nimport com.alibaba.otter.node.etl.load.loader.weight.WeightController;\nimport com.alibaba.otter.shared.arbitrate.impl.config.ArbitrateConfigRegistry;\nimport com.alibaba.otter.shared.common.model.config.channel.Channel;\nimport com.alibaba.otter.shared.common.model.config.channel.ChannelParameter;\nimport com.alibaba.otter.shared.common.model.config.data.DataMediaPair;\nimport com.alibaba.otter.shared.common.model.config.data.db.DbDataMedia;\nimport com.alibaba.otter.shared.common.model.config.node.Node;\nimport com.alibaba.otter.shared.common.model.config.parameter.SystemParameter;\nimport com.alibaba.otter.shared.common.model.config.pipeline.Pipeline;\nimport com.alibaba.otter.shared.etl.model.EventColumn;\nimport com.alibaba.otter.shared.etl.model.EventData;\nimport com.alibaba.otter.shared.etl.model.EventType;\nimport com.alibaba.otter.shared.etl.model.Identity;\nimport com.alibaba.otter.shared.etl.model.RowBatch;\n\npublic class DbLoadActionTest extends BaseDbTest {\n\n    private DbLoadAction        dbLoadAction;\n\n    @SpringBeanFrom\n    @Mocked\n    private ConfigClientService configClientService;\n\n    @Test\n    public void test_db_load_oracle() {\n        ArbitrateConfigRegistry.regist(configClientService);\n        dbLoadAction = (DbLoadAction) TestedObject.getSpringBeanFactory().getBean(\"dbLoadAction\");\n\n        final Channel channel = new Channel();\n        channel.setId(1L);\n\n        final Pipeline pipeline = new Pipeline();\n        pipeline.setId(100L);\n        List<DataMediaPair> pairs = generatorDataMediaPairForOracle(20);\n        pipeline.setPairs(pairs);\n        pipeline.getParameters().merge(new SystemParameter());\n        pipeline.getParameters().merge(new ChannelParameter());\n\n        // final Pipeline oppositePipeline = new Pipeline();\n        // oppositePipeline.setId(101L);\n        channel.setPipelines(Arrays.asList(pipeline));\n\n        final Node currentNode = new Node();\n        currentNode.setId(1L);\n        new NonStrictExpectations() {\n\n            {\n                configClientService.findChannel(anyLong);\n                returns(channel);\n                configClientService.findPipeline(anyLong);\n                returns(pipeline);\n                configClientService.currentNode();\n                returns(currentNode);\n            }\n        };\n\n        Identity identity = new Identity();\n        identity.setChannelId(100L);\n        identity.setPipelineId(100L);\n        identity.setProcessId(100L);\n\n        RowBatch rowBatch = new RowBatch();\n        rowBatch.setIdentity(identity);\n        List<EventData> eventDatas = generatorEventDataForOracle(0, 20, EventType.INSERT);\n        for (EventData eventData : eventDatas) {\n            rowBatch.merge(eventData);\n        }\n        eventDatas = generatorEventDataForOracle(10, 10, EventType.INSERT);\n        for (EventData eventData : eventDatas) {\n            rowBatch.merge(eventData);\n        }\n        eventDatas = generatorEventDataForOracle(19, 1, EventType.DELETE);\n        for (EventData eventData : eventDatas) {\n            rowBatch.merge(eventData);\n        }\n\n        WeightController controller = new WeightController(1);\n        dbLoadAction.load(rowBatch, controller);\n    }\n\n    @Test\n    public void test_db_load_mysql() {\n        ArbitrateConfigRegistry.regist(configClientService);\n        dbLoadAction = (DbLoadAction) TestedObject.getSpringBeanFactory().getBean(\"dbLoadAction\");\n\n        final Channel channel = new Channel();\n        channel.setId(1L);\n\n        final Pipeline pipeline = new Pipeline();\n        pipeline.setId(100L);\n        List<DataMediaPair> pairs = generatorDataMediaPairForMysql(20);\n        pipeline.setPairs(pairs);\n        pipeline.getParameters().merge(new SystemParameter());\n        pipeline.getParameters().merge(new ChannelParameter());\n        // pipeline.getParameters().setChannelInfo(\"LJH_DEMO\");\n\n        // final Pipeline oppositePipeline = new Pipeline();\n        // oppositePipeline.setId(101L);\n        channel.setPipelines(Arrays.asList(pipeline));\n\n        final Node currentNode = new Node();\n        currentNode.setId(1L);\n        new NonStrictExpectations() {\n\n            {\n                configClientService.findChannel(anyLong);\n                returns(channel);\n                configClientService.findPipeline(anyLong);\n                returns(pipeline);\n                configClientService.currentNode();\n                returns(currentNode);\n            }\n        };\n\n        Identity identity = new Identity();\n        identity.setChannelId(100L);\n        identity.setPipelineId(100L);\n        identity.setProcessId(100L);\n\n        RowBatch rowBatch = new RowBatch();\n        rowBatch.setIdentity(identity);\n        List<EventData> eventDatas = generatorEventDataForMysql(0, 20, EventType.INSERT);\n        for (EventData eventData : eventDatas) {\n            rowBatch.merge(eventData);\n        }\n        eventDatas = generatorEventDataForMysql(10, 10, EventType.INSERT);\n        for (EventData eventData : eventDatas) {\n            rowBatch.merge(eventData);\n        }\n        eventDatas = generatorEventDataForMysql(19, 1, EventType.DELETE);\n        for (EventData eventData : eventDatas) {\n            rowBatch.merge(eventData);\n        }\n\n        WeightController controller = new WeightController(1);\n        dbLoadAction.load(rowBatch, controller);\n    }\n\n    private List<EventData> generatorEventDataForOracle(int start, int count, EventType type) {\n        List<EventData> eventDatas = new ArrayList<EventData>();\n        for (int i = 0; i < count; i++) {\n            int index = i + 1 + start;\n            EventData eventData = new EventData();\n            eventData.setPairId(index);\n            eventData.setTableId(1L);\n            eventData.setSchemaName(\"srf\");\n            eventData.setTableName(\"columns\");\n            eventData.setEventType(type);\n            eventData.setExecuteTime(100L);\n            eventData.getKeys().add(buildColumn(\"id\", Types.NUMERIC, \"\" + index, true, false));\n            eventData.getKeys().add(buildColumn(\"name\", Types.VARCHAR, \"ljh_\" + index, true, false));\n            eventData.getOldKeys().add(buildColumn(\"id\", Types.NUMERIC, \"\" + index, true, false));\n            eventData.getOldKeys().add(buildColumn(\"name\", Types.VARCHAR, \"ljh_\" + index, true, false));\n\n            eventData.getColumns().add(buildColumn(\"alias_name\", Types.CHAR, \"hello_\" + index, false, false));\n            eventData.getColumns().add(buildColumn(\"amount\", Types.NUMERIC, \"100.01\", false, false));\n            eventData.getColumns().add(buildColumn(\"text_b\", Types.BLOB, \"[116,101,120,116,95,98]\", false, false));\n            eventData.getColumns().add(buildColumn(\"text_c\", Types.CLOB, \"中文\", false, false));\n            eventData.getColumns().add(buildColumn(\"curr_date\", Types.DATE, \"2011-01-01\", false, false));\n            eventData.getColumns().add(buildColumn(\"gmt_create\", Types.DATE, \"2011-01-01 11:11:11\", false, false));\n            eventData.getColumns().add(buildColumn(\"gmt_modify\", Types.DATE, \"2011-01-01 11:11:11\", false, false));\n\n            // OracleSqlTemplate sqlTemplate = new OracleSqlTemplate();\n            // String sql = null;\n            // if (type.isInsert()) {\n            // sql = sqlTemplate.getMergeSql(eventData.getSchemaName(),\n            // eventData.getTableName(),\n            // buildColumnNames(eventData.getKeys()),\n            // buildColumnNames(eventData.getColumns()), new String[] {});\n            // } else if (type.isUpdate()) {\n            // sql = sqlTemplate.getUpdateSql(eventData.getSchemaName(),\n            // eventData.getTableName(),\n            // buildColumnNames(eventData.getKeys()),\n            // buildColumnNames(eventData.getColumns()));\n            // } else if (type.isDelete()) {\n            // sql = sqlTemplate.getDeleteSql(eventData.getSchemaName(),\n            // eventData.getTableName(),\n            // buildColumnNames(eventData.getKeys()));\n            // }\n            // eventData.setSql(sql);\n\n            eventDatas.add(eventData);\n        }\n\n        return eventDatas;\n    }\n\n    private List<EventData> generatorEventDataForMysql(int start, int count, EventType type) {\n        List<EventData> eventDatas = new ArrayList<EventData>();\n        for (int i = 0; i < count; i++) {\n            int index = i + 1 + start;\n            EventData eventData = new EventData();\n            eventData.setPairId(index);\n            eventData.setTableId(1L);\n            eventData.setSchemaName(\"srf\");\n            eventData.setTableName(\"columns\");\n            eventData.setEventType(type);\n            eventData.setExecuteTime(100L);\n            eventData.getKeys().add(buildColumn(\"id\", Types.INTEGER, \"\" + index, true, false));\n            eventData.getKeys().add(buildColumn(\"name\", Types.VARCHAR, \"ljh_\" + index, true, false));\n\n            eventData.getOldKeys().add(buildColumn(\"id\", Types.INTEGER, \"\" + index, true, false));\n            eventData.getOldKeys().add(buildColumn(\"name\", Types.VARCHAR, \"ljh_\" + index, true, false));\n\n            eventData.getColumns().add(buildColumn(\"alias_name\", Types.CHAR, \"hello_\" + index, false, false));\n            eventData.getColumns().add(buildColumn(\"amount\", Types.DECIMAL, \"100.01\", false, false));\n            eventData.getColumns().add(buildColumn(\"text_b\", Types.BLOB, \"[116,101,120,116,95,98]\", false, false));\n            eventData.getColumns().add(buildColumn(\"text_c\", Types.CLOB, \"中文\", false, false));\n            eventData.getColumns().add(buildColumn(\"curr_date\", Types.DATE, \"2011-01-01\", false, false));\n            eventData.getColumns().add(buildColumn(\"gmt_create\", Types.TIMESTAMP, \"2011-01-01 11:11:11\", false, false));\n            eventData.getColumns().add(buildColumn(\"gmt_modify\", Types.TIMESTAMP, \"2011-01-01 11:11:11\", false, false));\n\n            // MysqlSqlTemplate sqlTemplate = new MysqlSqlTemplate();\n            // String sql = null;\n            // if (type.isInsert()) {\n            // sql = sqlTemplate.getMergeSql(eventData.getSchemaName(),\n            // eventData.getTableName(),\n            // buildColumnNames(eventData.getKeys()),\n            // buildColumnNames(eventData.getColumns()), new String[] {});\n            // } else if (type.isUpdate()) {\n            // sql = sqlTemplate.getUpdateSql(eventData.getSchemaName(),\n            // eventData.getTableName(),\n            // buildColumnNames(eventData.getKeys()),\n            // buildColumnNames(eventData.getColumns()));\n            // } else if (type.isDelete()) {\n            // sql = sqlTemplate.getDeleteSql(eventData.getSchemaName(),\n            // eventData.getTableName(),\n            // buildColumnNames(eventData.getKeys()));\n            // }\n            // eventData.setSql(sql);\n            eventDatas.add(eventData);\n        }\n\n        return eventDatas;\n    }\n\n    // private String[] buildColumnNames(List<EventColumn> columns) {\n    // String[] result = new String[columns.size()];\n    // for (int i = 0; i < columns.size(); i++) {\n    // EventColumn column = columns.get(i);\n    // result[i] = column.getColumnName();\n    // }\n    // return result;\n    // }\n\n    private EventColumn buildColumn(String name, int type, String value, boolean isKey, boolean isNull) {\n        EventColumn column = new EventColumn();\n        column.setColumnName(name);\n        column.setColumnType(type);\n        column.setColumnValue(value);\n        column.setKey(isKey);\n        column.setNull(isNull);\n        return column;\n    }\n\n    private List<DataMediaPair> generatorDataMediaPairForOracle(int count) {\n        List<DataMediaPair> pairs = new ArrayList<DataMediaPair>();\n        for (int i = 0; i < count; i++) {\n            DataMediaPair pair = new DataMediaPair();\n            int index = i + 1;\n            pair.setId(Long.valueOf(index));\n            pair.setPullWeight(count - Long.valueOf(index));\n            pair.setPushWeight(count - Long.valueOf(index));\n\n            DbDataMedia mysqlMedia = getMysqlMedia();\n            mysqlMedia.setId(2L);\n            pair.setSource(mysqlMedia);\n\n            DbDataMedia oracleMedia = getOracleMedia();\n            oracleMedia.setId(1L);\n            pair.setTarget(oracleMedia);\n            pairs.add(pair);\n        }\n        return pairs;\n    }\n\n    private List<DataMediaPair> generatorDataMediaPairForMysql(int count) {\n        List<DataMediaPair> pairs = new ArrayList<DataMediaPair>();\n        for (int i = 0; i < count; i++) {\n            DataMediaPair pair = new DataMediaPair();\n            int index = i + 1;\n            pair.setId(Long.valueOf(index));\n            pair.setPullWeight(count - Long.valueOf(index));\n            pair.setPushWeight(count - Long.valueOf(index));\n\n            DbDataMedia oracleMedia = getOracleMedia();\n            oracleMedia.setId(2L);\n            pair.setSource(oracleMedia);\n\n            DbDataMedia mysqlMedia = getMysqlMedia();\n            mysqlMedia.setId(1L);\n            pair.setTarget(mysqlMedia);\n            pairs.add(pair);\n        }\n        return pairs;\n    }\n}\n"
  },
  {
    "path": "node/etl/src/test/java/com/alibaba/otter/node/etl/load/DbLoadMergerTest.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.load;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\n\nimport junit.framework.Assert;\n\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.node.etl.BaseDbTest;\nimport com.alibaba.otter.node.etl.load.loader.db.DbLoadMerger;\nimport com.alibaba.otter.node.etl.load.loader.db.DbLoadMerger.RowKey;\nimport com.alibaba.otter.shared.etl.model.EventColumn;\nimport com.alibaba.otter.shared.etl.model.EventData;\nimport com.alibaba.otter.shared.etl.model.EventType;\nimport com.google.common.collect.MapMaker;\n\npublic class DbLoadMergerTest extends BaseDbTest {\n\n    private static final int      COLUMN_TYPE    = 1;\n\n    private static final long     TABLE_ID       = 10;\n\n    private static final String   SCHEMA_NAME    = \"test\";\n\n    private static final String   TABLE_NAME     = \"test\";\n\n    private static final String   KEY_NAME       = \"id\";\n\n    private static final String   KEY_VALUE      = \"100\";\n\n    private static final String   KEY_VALUE_NEW1 = \"1001\";\n    private static final String   KEY_VALUE_NEW2 = \"1002\";\n    private static final String   KEY_VALUE_NEW3 = \"1003\";\n    private static final String   KEY_VALUE_NEW4 = \"1004\";\n\n    private static final String[] COLUMN_NAMES1  = { \"name\", \"password\" };\n\n    private static final String[] COLUMN_NAMES2  = { \"name\", \"age\" };\n\n    /**\n     * 测试insert+update\n     */\n    @Test\n    public void testMergeWithSameKeyOfIU() {\n        Map<RowKey, EventData> mergeMap = new MapMaker().makeMap();\n        DbLoadMerger.merge(makeInsertEventData(), mergeMap);\n        DbLoadMerger.merge(makeUpdateEventData(), mergeMap);\n\n        for (Entry<RowKey, EventData> entry : mergeMap.entrySet()) {\n            RowKey key = entry.getKey();\n            EventColumn keyColumn = key.getKeys().get(0);\n            Assert.assertEquals(KEY_VALUE, keyColumn.getColumnValue());\n            Assert.assertEquals(KEY_NAME, keyColumn.getColumnName());\n\n            EventData eventData = entry.getValue();\n\n            Assert.assertEquals(SCHEMA_NAME, eventData.getSchemaName());\n            Assert.assertEquals(TABLE_NAME, eventData.getTableName());\n            Assert.assertEquals(TABLE_ID, eventData.getTableId());\n            Assert.assertEquals(EventType.INSERT, eventData.getEventType());\n            Assert.assertEquals(eventData.getOldKeys().size(), 0); // 不存在oldKeys\n\n            List<EventColumn> columns = eventData.getColumns();\n            Assert.assertEquals(3, columns.size());\n        }\n    }\n\n    /**\n     * 测试insert+update+delete\n     */\n    @Test\n    public void testMergeWithSameKeyOfIUD() {\n        Map<RowKey, EventData> mergeMap = new MapMaker().makeMap();\n        DbLoadMerger.merge(makeInsertEventData(), mergeMap);\n        DbLoadMerger.merge(makeUpdateEventData(), mergeMap);\n        DbLoadMerger.merge(makeDeleteEventData(), mergeMap);\n\n        for (Entry<RowKey, EventData> entry : mergeMap.entrySet()) {\n            RowKey key = entry.getKey();\n            EventColumn keyColumn = key.getKeys().get(0);\n            Assert.assertEquals(KEY_VALUE, keyColumn.getColumnValue());\n            Assert.assertEquals(KEY_NAME, keyColumn.getColumnName());\n\n            EventData eventData = entry.getValue();\n            Assert.assertEquals(SCHEMA_NAME, eventData.getSchemaName());\n            Assert.assertEquals(TABLE_NAME, eventData.getTableName());\n            Assert.assertEquals(TABLE_ID, eventData.getTableId());\n            Assert.assertEquals(EventType.DELETE, eventData.getEventType());\n            Assert.assertEquals(eventData.getOldKeys().size(), 0); // 不存在oldKeys\n\n            List<EventColumn> columns = eventData.getColumns();\n            Assert.assertEquals(0, columns.size());\n        }\n    }\n\n    /**\n     * 测试insert+update+delete+insert\n     */\n    @Test\n    public void testMergeWithSameKeyOfIUDI() {\n        Map<RowKey, EventData> mergeMap = new MapMaker().makeMap();\n        DbLoadMerger.merge(makeInsertEventData(), mergeMap);\n        DbLoadMerger.merge(makeUpdateEventData(), mergeMap);\n        DbLoadMerger.merge(makeDeleteEventData(), mergeMap);\n        DbLoadMerger.merge(makeInsertEventData(), mergeMap);\n\n        for (Entry<RowKey, EventData> entry : mergeMap.entrySet()) {\n            RowKey key = entry.getKey();\n            EventColumn keyColumn = key.getKeys().get(0);\n            Assert.assertEquals(KEY_VALUE, keyColumn.getColumnValue());\n            Assert.assertEquals(KEY_NAME, keyColumn.getColumnName());\n\n            EventData eventData = entry.getValue();\n            Assert.assertEquals(SCHEMA_NAME, eventData.getSchemaName());\n            Assert.assertEquals(TABLE_NAME, eventData.getTableName());\n            Assert.assertEquals(TABLE_ID, eventData.getTableId());\n            Assert.assertEquals(EventType.INSERT, eventData.getEventType());\n            Assert.assertEquals(eventData.getOldKeys().size(), 0); // 不存在oldKeys\n\n            List<EventColumn> columns = eventData.getColumns();\n            Assert.assertEquals(2, columns.size());\n        }\n    }\n\n    /**\n     * 测试在主键发生变化后的merge操作，Update/Update\n     */\n    @Test\n    public void testMergeWithUpdateKeyOfUU() {\n        Map<RowKey, EventData> mergeMap = new MapMaker().makeMap();\n        DbLoadMerger.merge(makeUpdateEventData(KEY_VALUE, KEY_VALUE_NEW1), mergeMap);\n        DbLoadMerger.merge(makeUpdateEventData(KEY_VALUE_NEW1, KEY_VALUE_NEW2), mergeMap);\n\n        for (Entry<RowKey, EventData> entry : mergeMap.entrySet()) {\n            RowKey key = entry.getKey();\n            EventColumn keyColumn = key.getKeys().get(0);\n            Assert.assertEquals(KEY_VALUE_NEW2, keyColumn.getColumnValue());\n            Assert.assertEquals(KEY_NAME, keyColumn.getColumnName());\n\n            EventData eventData = entry.getValue();\n            Assert.assertEquals(SCHEMA_NAME, eventData.getSchemaName());\n            Assert.assertEquals(TABLE_NAME, eventData.getTableName());\n            Assert.assertEquals(TABLE_ID, eventData.getTableId());\n            Assert.assertEquals(EventType.UPDATE, eventData.getEventType());\n\n            List<EventColumn> oldKeys = eventData.getOldKeys();\n            List<EventColumn> keys = eventData.getKeys();\n            Assert.assertNotSame(oldKeys, keys);\n        }\n    }\n\n    /**\n     * 测试在主键发生变化后的merge操作，Update/Update/delete\n     */\n    @Test\n    public void testMergeWithUpdateKeyOfUUD() {\n        Map<RowKey, EventData> mergeMap = new MapMaker().makeMap();\n        DbLoadMerger.merge(makeUpdateEventData(KEY_VALUE_NEW1, KEY_VALUE_NEW2), mergeMap);\n        DbLoadMerger.merge(makeUpdateEventData(KEY_VALUE_NEW2, KEY_VALUE), mergeMap);\n        DbLoadMerger.merge(makeDeleteEventData(), mergeMap);\n\n        for (Entry<RowKey, EventData> entry : mergeMap.entrySet()) {\n            RowKey key = entry.getKey();\n            EventColumn keyColumn = key.getKeys().get(0);\n            Assert.assertEquals(KEY_VALUE_NEW1, keyColumn.getColumnValue());\n            Assert.assertEquals(KEY_NAME, keyColumn.getColumnName());\n\n            EventData eventData = entry.getValue();\n            Assert.assertEquals(SCHEMA_NAME, eventData.getSchemaName());\n            Assert.assertEquals(TABLE_NAME, eventData.getTableName());\n            Assert.assertEquals(TABLE_ID, eventData.getTableId());\n            Assert.assertEquals(EventType.DELETE, eventData.getEventType());\n            Assert.assertEquals(eventData.getOldKeys().size(), 0); // 不存在oldKeys\n        }\n    }\n\n    /**\n     * 测试在主键发生变化后的merge操作，Insert/Update/Update/Update/Update\n     */\n    @Test\n    public void testMergeWithUpdateKeyOfIUUUU() {\n        Map<RowKey, EventData> mergeMap = new MapMaker().makeMap();\n        DbLoadMerger.merge(makeInsertEventData(), mergeMap);\n        DbLoadMerger.merge(makeUpdateEventData(KEY_VALUE, KEY_VALUE_NEW1), mergeMap);\n        DbLoadMerger.merge(makeUpdateEventData(KEY_VALUE_NEW1, KEY_VALUE_NEW2), mergeMap);\n        DbLoadMerger.merge(makeUpdateEventData(KEY_VALUE_NEW2, KEY_VALUE_NEW3), mergeMap);\n        DbLoadMerger.merge(makeUpdateEventData(KEY_VALUE_NEW3, KEY_VALUE_NEW4), mergeMap);\n\n        for (Entry<RowKey, EventData> entry : mergeMap.entrySet()) {\n            RowKey key = entry.getKey();\n            EventColumn keyColumn = key.getKeys().get(0);\n            Assert.assertEquals(KEY_VALUE_NEW4, keyColumn.getColumnValue());\n            Assert.assertEquals(KEY_NAME, keyColumn.getColumnName());\n\n            EventData eventData = entry.getValue();\n            Assert.assertEquals(SCHEMA_NAME, eventData.getSchemaName());\n            Assert.assertEquals(TABLE_NAME, eventData.getTableName());\n            Assert.assertEquals(TABLE_ID, eventData.getTableId());\n            Assert.assertEquals(EventType.INSERT, eventData.getEventType());\n            Assert.assertEquals(eventData.getOldKeys().size(), 0); // 不存在oldKeys\n        }\n    }\n\n    /**\n     * 测试在主键发生变化后的merge操作，Update/Update/Insert\n     */\n    @Test\n    public void testMergeWithUpdateKeyOfUI() {\n        Map<RowKey, EventData> mergeMap = new MapMaker().makeMap();\n        DbLoadMerger.merge(makeUpdateEventData(KEY_VALUE_NEW1, KEY_VALUE_NEW2), mergeMap);\n        DbLoadMerger.merge(makeUpdateEventData(KEY_VALUE_NEW2, KEY_VALUE), mergeMap);\n        DbLoadMerger.merge(makeInsertEventData(), mergeMap);\n\n        for (Entry<RowKey, EventData> entry : mergeMap.entrySet()) {\n            RowKey key = entry.getKey();\n            EventColumn keyColumn = key.getKeys().get(0);\n            Assert.assertEquals(KEY_VALUE, keyColumn.getColumnValue());\n            Assert.assertEquals(KEY_NAME, keyColumn.getColumnName());\n\n            EventData eventData = entry.getValue();\n            Assert.assertEquals(SCHEMA_NAME, eventData.getSchemaName());\n            Assert.assertEquals(TABLE_NAME, eventData.getTableName());\n            Assert.assertEquals(TABLE_ID, eventData.getTableId());\n            Assert.assertEquals(EventType.INSERT, eventData.getEventType());\n\n            List<EventColumn> oldKeys = eventData.getOldKeys();\n            List<EventColumn> keys = eventData.getKeys();\n\n            Assert.assertNotSame(oldKeys, keys);\n        }\n    }\n\n    /**\n     * 测试在主键发生变化后的merge操作，Insert/Insert\n     */\n    @Test\n    public void testMergeWithUpdateKeyOfII() {\n        Map<RowKey, EventData> mergeMap = new MapMaker().makeMap();\n        DbLoadMerger.merge(makeInsertEventData(), mergeMap);\n        DbLoadMerger.merge(makeInsertEventData(), mergeMap);\n\n        for (Entry<RowKey, EventData> entry : mergeMap.entrySet()) {\n            RowKey key = entry.getKey();\n            EventColumn keyColumn = key.getKeys().get(0);\n            Assert.assertEquals(KEY_VALUE, keyColumn.getColumnValue());\n            Assert.assertEquals(KEY_NAME, keyColumn.getColumnName());\n\n            EventData eventData = entry.getValue();\n            Assert.assertEquals(SCHEMA_NAME, eventData.getSchemaName());\n            Assert.assertEquals(TABLE_NAME, eventData.getTableName());\n            Assert.assertEquals(TABLE_ID, eventData.getTableId());\n            Assert.assertEquals(EventType.INSERT, eventData.getEventType());\n\n            List<EventColumn> oldKeys = eventData.getOldKeys();\n            List<EventColumn> keys = eventData.getKeys();\n\n            Assert.assertNotSame(oldKeys, keys);\n        }\n    }\n\n    private EventData makeInsertEventData() {\n        EventData eventData = new EventData();\n        eventData.setEventType(EventType.INSERT);\n        eventData.setSchemaName(SCHEMA_NAME);\n        eventData.setTableName(TABLE_NAME);\n        eventData.setTableId(TABLE_ID);\n\n        List<EventColumn> keys = new ArrayList<EventColumn>();\n        keys.add(makeEventColumn(KEY_NAME, KEY_VALUE, true));\n        eventData.setKeys(keys);\n\n        List<EventColumn> columns = new ArrayList<EventColumn>();\n        int i = 0;\n        for (String columnName : COLUMN_NAMES1) {\n            columns.add(makeEventColumn(columnName, columnName + i, false));\n        }\n        eventData.setColumns(columns);\n        return eventData;\n    }\n\n    private EventData makeUpdateEventData() {\n        EventData eventData = new EventData();\n        eventData.setEventType(EventType.UPDATE);\n        eventData.setSchemaName(SCHEMA_NAME);\n        eventData.setTableName(TABLE_NAME);\n        eventData.setTableId(TABLE_ID);\n\n        List<EventColumn> keys = new ArrayList<EventColumn>();\n        keys.add(makeEventColumn(KEY_NAME, KEY_VALUE, true));\n        eventData.setKeys(keys);\n\n        List<EventColumn> columns = new ArrayList<EventColumn>();\n        int i = 0;\n        for (String columnName : COLUMN_NAMES2) {\n            columns.add(makeEventColumn(columnName, columnName + i, false));\n            i++;\n        }\n        eventData.setColumns(columns);\n        return eventData;\n    }\n\n    private EventData makeUpdateEventData(String oldKeyValue, String newKeyValue) {\n        EventData eventData = new EventData();\n        eventData.setEventType(EventType.UPDATE);\n        eventData.setSchemaName(SCHEMA_NAME);\n        eventData.setTableName(TABLE_NAME);\n        eventData.setTableId(TABLE_ID);\n\n        List<EventColumn> oldKeys = new ArrayList<EventColumn>();\n        oldKeys.add(makeEventColumn(KEY_NAME, oldKeyValue, true));\n        List<EventColumn> newKeys = new ArrayList<EventColumn>();\n        newKeys.add(makeEventColumn(KEY_NAME, newKeyValue, true));\n        eventData.setKeys(newKeys);\n        eventData.setOldKeys(oldKeys);\n        return eventData;\n    }\n\n    private EventData makeDeleteEventData() {\n        EventData eventData = new EventData();\n        eventData.setEventType(EventType.DELETE);\n        eventData.setSchemaName(SCHEMA_NAME);\n        eventData.setTableName(TABLE_NAME);\n        eventData.setTableId(TABLE_ID);\n\n        List<EventColumn> keys = new ArrayList<EventColumn>();\n        keys.add(makeEventColumn(KEY_NAME, KEY_VALUE, true));\n        eventData.setKeys(keys);\n        return eventData;\n    }\n\n    private EventColumn makeEventColumn(String columnName, String columnValue, boolean key) {\n        EventColumn eventColumn = new EventColumn();\n        eventColumn.setColumnName(columnName);\n        eventColumn.setColumnType(COLUMN_TYPE);\n        eventColumn.setColumnValue(columnValue);\n        eventColumn.setKey(key);\n        return eventColumn;\n    }\n}\n"
  },
  {
    "path": "node/etl/src/test/java/com/alibaba/otter/node/etl/load/FileLoadActionTest.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.load;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.Calendar;\nimport java.util.List;\n\nimport mockit.Mocked;\n\nimport org.apache.commons.io.FileUtils;\nimport org.jtester.annotations.SpringBeanByName;\nimport org.testng.annotations.AfterMethod;\nimport org.testng.annotations.BeforeMethod;\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.node.common.config.ConfigClientService;\nimport com.alibaba.otter.node.etl.BaseDbTest;\nimport com.alibaba.otter.node.etl.load.exception.LoadException;\nimport com.alibaba.otter.node.etl.load.loader.db.FileLoadAction;\nimport com.alibaba.otter.node.etl.load.loader.db.context.FileLoadContext;\nimport com.alibaba.otter.node.etl.load.loader.weight.WeightController;\nimport com.alibaba.otter.shared.common.model.config.channel.Channel;\nimport com.alibaba.otter.shared.common.model.config.data.DataMediaPair;\nimport com.alibaba.otter.shared.common.model.config.pipeline.Pipeline;\nimport com.alibaba.otter.shared.common.model.config.pipeline.PipelineParameter;\nimport com.alibaba.otter.shared.etl.model.EventType;\nimport com.alibaba.otter.shared.etl.model.FileBatch;\nimport com.alibaba.otter.shared.etl.model.FileData;\nimport com.alibaba.otter.shared.etl.model.Identity;\n\n/**\n * @author zebinxu 2012-5-3 上午11:17:22\n */\npublic class FileLoadActionTest extends BaseDbTest {\n\n    private static final int    NUMBER_OF_FILE_DATA_COPIES = 5;\n    private static final long   TABLE_ID                   = 1L;\n    private static final File   ROOT_DIR                   = new File(System.getProperty(\"java.io.tmpdir\")\n                                                                      + \"/file_load_action_test\");\n\n    @SpringBeanByName\n    private FileLoadAction      fileLoadAction;\n\n    @SpringBeanByName\n    @Mocked(realClassName = \"com.alibaba.otter.node.common.config.impl.ConfigClientServiceImpl\")\n    private ConfigClientService configClientService;\n\n    @Test\n    public void testWithOutRootDir() throws Exception {\n        File rootDir = new File(\"/null\");\n        Identity id = buildIdentity(1L, 2L, 3L);\n        FileBatch fileBatch = buildFileBatch(id);\n        fileBatch.getFiles().addAll(buildFileDatas(\"ns_\", EventType.INSERT, 0, 20, false));\n\n        try {\n            fileLoadAction.load(fileBatch, rootDir, null);\n        } catch (Exception e) {\n            // expect for LoadException\n            if (e instanceof LoadException) {\n                return;\n            }\n\n            throw e;\n        }\n\n        want.fail(\"unreachable code.\");\n\n    }\n\n    @Test\n    public void testLoadWithLocal() throws IOException {\n\n        // 构造fileData使用的参数，fileDataStartIndex 决定着 pipeline 与 fileData 对应的关系（通过\n        // dataMediaPair 的 id），\n        // 以及 dataMediaPair 的 pushWeight\n        final int fileDataStartIndex = 0;\n        final int fileDataCount = 50;\n        final Pipeline pipeline = buildPipeline(fileDataStartIndex, fileDataCount);\n\n        final Channel channel = new Channel();\n\n        new NonStrictExpectations() {\n\n            {\n                configClientService.findChannel(anyLong);\n                returns(channel);\n                configClientService.findPipeline(anyLong);\n                returns(pipeline);\n\n            }\n        };\n\n        Identity id = buildIdentity(1L, 2L, 3L);\n        FileBatch fileBatch = buildFileBatch(id);\n        fileBatch.getFiles().addAll(buildFileDatas(null, EventType.INSERT, fileDataStartIndex, fileDataCount, true));\n\n        WeightController controller = new WeightController(1);\n        FileLoadContext context = fileLoadAction.load(fileBatch, ROOT_DIR, controller);\n        want.object(context.getChannel()).isEqualTo(channel);\n        want.object(context.getPipeline()).isEqualTo(pipeline);\n        want.object(context.getPrepareDatas()).isEqualTo(fileBatch.getFiles());\n        want.number(context.getProcessedDatas().size()).isEqualTo(fileBatch.getFiles().size());\n    }\n\n    @BeforeMethod\n    public void setUp() throws IOException {\n        FileUtils.deleteDirectory(ROOT_DIR);\n    }\n\n    @AfterMethod\n    public void cleanUp() throws IOException {\n        FileUtils.deleteDirectory(ROOT_DIR);\n    }\n\n    protected Pipeline buildPipeline(final int fileDataStartIndex, int fileDataCount) {\n        final Pipeline pipeline = new Pipeline();\n        pipeline.setParameters(new PipelineParameter());\n\n        int dataMediaPairCount = fileDataCount / NUMBER_OF_FILE_DATA_COPIES;\n        pipeline.setPairs(new ArrayList<DataMediaPair>(dataMediaPairCount));\n        for (int i = fileDataStartIndex; i < dataMediaPairCount; i++) {\n            DataMediaPair dataMediaPair = buildDataMediaPair(i, i);\n            pipeline.getPairs().add(dataMediaPair);\n        }\n        return pipeline;\n    }\n\n    protected DataMediaPair buildDataMediaPair(long id, long pushWeight) {\n        DataMediaPair result = new DataMediaPair();\n        result.setId(id);\n        result.setPushWeight(pushWeight);\n        return result;\n    }\n\n    protected FileBatch buildFileBatch(Identity identity) {\n        FileBatch fileBatch = new FileBatch();\n        fileBatch.setIdentity(identity);\n        return fileBatch;\n    }\n\n    protected List<FileData> buildFileDatas(String namespace, EventType eventType, int start, int count, boolean create)\n                                                                                                                        throws IOException {\n        List<FileData> files = new ArrayList<FileData>();\n\n        for (int i = start; i < count; i++) {\n            FileData fileData = new FileData();\n            fileData.setNameSpace(namespace); // namespace is null means file is\n                                              // local file\n            fileData.setEventType(eventType);\n            fileData.setPairId(i % NUMBER_OF_FILE_DATA_COPIES);\n            fileData.setPath(ROOT_DIR.getAbsolutePath() + \"/target/\" + eventType.getValue() + i);\n\n            String parentPath = ROOT_DIR.getPath();\n            if (namespace != null) {\n                parentPath = parentPath + \"/\" + namespace;\n            }\n            File file = new File(parentPath, fileData.getPath());\n            if (!file.exists() && create) {\n                FileUtils.touch(file);\n            }\n\n            fileData.setSize(file.exists() ? file.length() : 0);\n            fileData.setLastModifiedTime(file.exists() ? file.lastModified() : Calendar.getInstance().getTimeInMillis());\n            fileData.setTableId(TABLE_ID);\n\n            files.add(fileData);\n        }\n\n        return files;\n    }\n\n    protected Identity buildIdentity(long channelId, long pipelineId, long processId) {\n        Identity identity = new Identity();\n        identity.setChannelId(channelId);\n        identity.setPipelineId(pipelineId);\n        identity.setProcessId(processId);\n        return identity;\n    }\n\n}\n"
  },
  {
    "path": "node/etl/src/test/java/com/alibaba/otter/node/etl/load/LocalFileLoaderActionTest.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.load;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.List;\n\nimport mockit.Mocked;\n\nimport org.apache.commons.lang.math.RandomUtils;\nimport org.jtester.annotations.SpringBeanByName;\nimport org.jtester.annotations.SpringBeanFrom;\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.node.common.config.ConfigClientService;\nimport com.alibaba.otter.node.etl.BaseDbTest;\nimport com.alibaba.otter.node.etl.load.loader.db.FileLoadAction;\nimport com.alibaba.otter.node.etl.load.loader.weight.WeightController;\nimport com.alibaba.otter.shared.common.model.config.data.DataMediaPair;\nimport com.alibaba.otter.shared.common.model.config.data.db.DbDataMedia;\nimport com.alibaba.otter.shared.common.model.config.pipeline.Pipeline;\nimport com.alibaba.otter.shared.common.utils.NioUtils;\nimport com.alibaba.otter.shared.etl.model.FileBatch;\nimport com.alibaba.otter.shared.etl.model.FileData;\nimport com.alibaba.otter.shared.etl.model.Identity;\n\nclass LocalFileLoaderActionTest extends BaseDbTest {\n\n    private static final String OTTERLOAD = \"otterload\";\n\n    private static final String tmp       = System.getProperty(\"java.io.tmpdir\", \"/tmp\");\n\n    @SpringBeanByName\n    private FileLoadAction      fileLoadAction;\n\n    @SpringBeanFrom\n    @Mocked\n    private ConfigClientService configClientService;\n\n    @Test\n    public void test_load_file() {\n        final Pipeline pipeline = new Pipeline();\n        pipeline.setId(100L);\n        List<DataMediaPair> pairs = generatorDataMediaPair(10);\n        pipeline.setPairs(pairs);\n        new NonStrictExpectations() {\n\n            {\n                configClientService.findPipeline(anyLong);\n                returns(pipeline);\n            }\n        };\n\n        Identity identity = new Identity();\n        identity.setChannelId(100L);\n        identity.setPipelineId(100L);\n        identity.setProcessId(100L);\n\n        FileBatch fileBatch = new FileBatch();\n        fileBatch.setIdentity(identity);\n        fileBatch.getFiles().addAll(generatorLocalFileData(\"fileLoad\", 10));\n\n        WeightController controller = new WeightController(1);\n        fileLoadAction.load(fileBatch, new File(tmp + File.separator + OTTERLOAD), controller);\n\n        File target = new File(tmp + File.separator + OTTERLOAD + \"_loaded/\");\n        want.number(target.listFiles().length).isEqualTo(10);\n        NioUtils.delete(target);\n    }\n\n    private List<DataMediaPair> generatorDataMediaPair(int count) {\n        List<DataMediaPair> pairs = new ArrayList<DataMediaPair>();\n        for (int i = 0; i < count; i++) {\n            DataMediaPair pair = new DataMediaPair();\n            pair.setId(Long.valueOf(i));\n            pair.setPullWeight(Long.valueOf(i));\n            pair.setPushWeight(Long.valueOf(i));\n\n            DbDataMedia mysqlMedia = getMysqlMedia();\n            mysqlMedia.setId(Long.valueOf(count + 1));\n            pair.setSource(mysqlMedia);\n\n            DbDataMedia oracleMedia = getOracleMedia();\n            oracleMedia.setId(Long.valueOf(i));\n            pair.setTarget(oracleMedia);\n            pairs.add(pair);\n        }\n        return pairs;\n    }\n\n    private List<FileData> generatorLocalFileData(String prefix, int count) {\n        List<FileData> result = new ArrayList<FileData>();\n        String target = tmp + File.separator + OTTERLOAD + \"_loaded/\";\n        for (int i = 0; i < count; i++) {\n            String filepath = tmp + File.separator + OTTERLOAD + target;\n            File local = new File(filepath, prefix + \"_\" + i + \".jpg\");\n            FileData localFileData = new FileData();\n            localFileData.setPairId(i);\n            localFileData.setTableId(i);\n            localFileData.setPath(target + local.getName());\n            localFileData.setLastModifiedTime(new Date().getTime());\n            try {\n                byte[] data = getBlock((i + 1) * 1024);\n                localFileData.setSize(data.length);\n                NioUtils.write(data, local);\n            } catch (IOException e) {\n                want.fail();\n            }\n            result.add(localFileData);\n        }\n\n        return result;\n    }\n\n    private byte[] getBlock(int length) {\n        byte[] rawData = new byte[length];\n        for (int i = 0; i < rawData.length; i++) {\n            rawData[i] = (byte) (' ' + RandomUtils.nextInt(95));\n\n        }\n        return rawData;\n    }\n}\n"
  },
  {
    "path": "node/etl/src/test/java/com/alibaba/otter/node/etl/load/WeightBarrierTest.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.load;\n\nimport java.util.concurrent.Callable;\nimport java.util.concurrent.CountDownLatch;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\n\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.node.etl.BaseOtterTest;\nimport com.alibaba.otter.node.etl.load.loader.weight.WeightBarrier;\n\npublic class WeightBarrierTest extends BaseOtterTest {\n\n    @Test\n    public void test_simple() {\n        final WeightBarrier barrier = new WeightBarrier(10);\n        try {\n            barrier.await(10);// 可以成功通过\n        } catch (InterruptedException e1) {\n            want.fail();\n        }\n\n        try {\n            final CountDownLatch count = new CountDownLatch(1);\n            ExecutorService executor = Executors.newCachedThreadPool();\n\n            executor.submit(new Callable() {\n\n                public Object call() throws Exception {\n                    Thread.sleep(1000);\n                    barrier.single(11);\n                    count.countDown();\n                    return null;\n                }\n            });\n\n            barrier.await(11);// 会被阻塞\n            count.await();\n            executor.shutdown();\n        } catch (InterruptedException e) {\n            want.fail();\n        }\n    }\n\n    @Test\n    public void test_cocurrent() {\n        final WeightBarrier barrier = new WeightBarrier(-1);\n\n        try {\n            final CountDownLatch count = new CountDownLatch(10);\n            ExecutorService executor = Executors.newCachedThreadPool();\n            for (int i = 0; i < 10; i++) {\n                final long index = i;\n                executor.submit(new Callable() {\n\n                    public Object call() throws Exception {\n                        barrier.await(index);\n                        want.number(index).isLe(barrier.state());\n                        count.countDown();\n                        return null;\n                    }\n                });\n            }\n            Thread.sleep(1000);\n            for (int i = 0; i < 10; i++) {\n                barrier.single(i);\n            }\n            count.await();\n            executor.shutdown();\n        } catch (InterruptedException e) {\n            want.fail();\n        }\n    }\n}\n"
  },
  {
    "path": "node/etl/src/test/java/com/alibaba/otter/node/etl/load/WeightBucketTest.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.load;\n\nimport java.util.List;\n\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.node.etl.load.loader.weight.WeightBuckets;\nimport com.alibaba.otter.node.etl.BaseOtterTest;\n\npublic class WeightBucketTest extends BaseOtterTest {\n\n    @Test\n    public void test_simple() {\n        WeightBuckets<String> bucket = new WeightBuckets();\n        int iCount = 10;\n        int jCount = 100;\n        for (int i = 0; i < iCount; i++) {\n            for (int j = 0; j < jCount; j++) {\n                bucket.addItem(i, \"name:\" + i + \"_\" + j);\n            }\n        }\n\n        for (int i = 0; i < iCount; i++) {\n            List<String> items = bucket.getItems(i);\n            want.collection(items).sizeEq(jCount);\n            for (int j = 0; j < jCount; j++) {\n                want.string(items.get(j)).isEqualTo(\"name:\" + i + \"_\" + j);\n            }\n        }\n    }\n\n    @Test\n    public void test_random() {\n        WeightBuckets<String> bucket = new WeightBuckets();\n        int iCount = 10;\n        int jCount = 100;\n        for (int i = 0; i < iCount; i++) {\n            for (int j = 0; j < jCount; j++) {\n                bucket.addItem(j, \"name:\" + i + \"_\" + j);\n            }\n        }\n\n        for (int j = 0; j < jCount; j++) {\n            List<String> items = bucket.getItems(j);\n            want.collection(items).sizeEq(iCount);\n\n            for (int i = 0; i < iCount; i++) {\n                want.string(items.get(i)).isEqualTo(\"name:\" + i + \"_\" + j);\n            }\n        }\n    }\n\n    @Test\n    public void test_custom() {\n        WeightBuckets<String> bucket = new WeightBuckets();\n        bucket.addItem(6, \"6\");\n        bucket.addItem(1, \"1\");\n        bucket.addItem(5, \"5\");\n        bucket.addItem(3, \"3\");\n        bucket.addItem(5, \"51\");\n        bucket.addItem(2, \"2\");\n        bucket.addItem(4, \"4\");\n        bucket.addItem(6, \"61\");\n\n        List<Long> weights = bucket.weights();\n        want.number(weights.get(0)).isEqualTo(1);\n        want.number(weights.get(1)).isEqualTo(2);\n        want.number(weights.get(2)).isEqualTo(3);\n        want.number(weights.get(3)).isEqualTo(4);\n        want.number(weights.get(4)).isEqualTo(5);\n        want.number(weights.get(5)).isEqualTo(6);\n    }\n}\n"
  },
  {
    "path": "node/etl/src/test/java/com/alibaba/otter/node/etl/load/WeightControllerTest.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.load;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.concurrent.CountDownLatch;\n\nimport org.apache.commons.lang.math.RandomUtils;\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.node.etl.load.loader.weight.WeightController;\nimport com.alibaba.otter.node.etl.BaseOtterTest;\n\npublic class WeightControllerTest extends BaseOtterTest {\n\n    @Test\n    public void test_simple() {\n        int thread = 10;\n        int count = 10;\n        WeightController controller = new WeightController(thread);\n        CountDownLatch latch = new CountDownLatch(thread);\n        WeightWorkerTest[] workers = new WeightWorkerTest[thread];\n        for (int i = 0; i < thread; i++) {\n            int[] weights = new int[count];\n            for (int j = 0; j < count; j++) {\n                weights[j] = RandomUtils.nextInt(count);\n            }\n            workers[i] = new WeightWorkerTest(i, weights, controller, latch);\n        }\n\n        for (int i = 0; i < thread; i++) {\n            workers[i].start();\n        }\n\n        try {\n            latch.await();\n        } catch (InterruptedException e) {\n            want.fail();\n        }\n    }\n\n}\n\nclass WeightWorkerTest extends Thread {\n\n    public long getId() {\n        return id;\n    }\n\n    private List<Long>       weights;\n    private WeightController controller;\n    private CountDownLatch   latch;\n    private int              id;\n\n    public WeightWorkerTest(int id, int[] weights, WeightController controller, CountDownLatch latch){\n        this.id = id;\n        this.controller = controller;\n        this.weights = new ArrayList<Long>();\n        this.latch = latch;\n        for (int i = 0; i < weights.length; i++) {\n            this.weights.add(Long.valueOf(weights[i]));\n        }\n        Collections.sort(this.weights);\n    }\n\n    public void run() {\n        try {\n            controller.start(weights);\n        } catch (InterruptedException e1) {\n            e1.printStackTrace();\n        }\n        for (Long weight : weights) {\n            try {\n                controller.await(weight);\n                System.out.println(id + \" : \" + weight);\n                Thread.sleep(50);\n                controller.single(weight);\n            } catch (InterruptedException e) {\n                e.printStackTrace();\n            }\n        }\n\n        latch.countDown();\n    }\n}\n"
  },
  {
    "path": "node/etl/src/test/java/com/alibaba/otter/node/etl/select/CanalClientIntegration.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.select;\n\nimport org.jtester.annotations.SpringBeanByName;\nimport org.testng.annotations.BeforeClass;\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.canal.instance.manager.CanalConfigClient;\nimport com.alibaba.otter.canal.instance.manager.model.Canal;\nimport com.alibaba.otter.node.etl.BaseOtterTest;\n\npublic class CanalClientIntegration extends BaseOtterTest {\n\n    @SpringBeanByName\n    private CanalConfigClient canalConfigClient;\n\n    @BeforeClass\n    public void setup() {\n        System.setProperty(\"nid\", \"14\");\n    }\n\n    @Test\n    public void test_simple() {\n        Canal canal = canalConfigClient.findCanal(\"ljh_canal_test01\");\n        System.out.println(canal);\n    }\n}\n"
  },
  {
    "path": "node/etl/src/test/java/com/alibaba/otter/node/etl/select/OtterDownStreamHandlerIntergration.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.select;\n\nimport java.util.Arrays;\nimport java.util.Date;\nimport java.util.concurrent.CountDownLatch;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\n\nimport org.jtester.annotations.SpringBeanByName;\nimport org.jtester.core.TestedObject;\nimport org.springframework.beans.factory.config.AutowireCapableBeanFactory;\nimport org.testng.annotations.BeforeClass;\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.canal.protocol.CanalEntry.Entry;\nimport com.alibaba.otter.canal.protocol.CanalEntry.EntryType;\nimport com.alibaba.otter.canal.protocol.CanalEntry.Header;\nimport com.alibaba.otter.canal.protocol.CanalEntry.RowChange;\nimport com.alibaba.otter.canal.protocol.CanalEntry.RowData;\nimport com.alibaba.otter.canal.protocol.position.LogIdentity;\nimport com.alibaba.otter.canal.store.model.Event;\nimport com.alibaba.otter.node.common.config.ConfigClientService;\nimport com.alibaba.otter.node.etl.BaseOtterTest;\nimport com.alibaba.otter.node.etl.select.selector.canal.OtterDownStreamHandler;\nimport com.alibaba.otter.shared.arbitrate.impl.zookeeper.ZooKeeperClient;\n\npublic class OtterDownStreamHandlerIntergration extends BaseOtterTest {\n\n    @SpringBeanByName\n    private ConfigClientService configClientService;\n\n    public OtterDownStreamHandlerIntergration(){\n        ZooKeeperClient client = new ZooKeeperClient();\n        client.setCluster(\"127.0.0.1:2181\");\n    }\n\n    @BeforeClass\n    public void setup() {\n        System.setProperty(\"nid\", \"14\");\n\n    }\n\n    @Test\n    public void testSimple() {\n        final OtterDownStreamHandler handler = new OtterDownStreamHandler();\n        handler.setPipelineId(388L);\n        handler.setDetectingIntervalInSeconds(1);\n\n        ((AutowireCapableBeanFactory) TestedObject.getSpringBeanFactory()).autowireBeanProperties(handler,\n            AutowireCapableBeanFactory.AUTOWIRE_BY_NAME,\n            false);\n\n        final CountDownLatch count = new CountDownLatch(1);\n        ExecutorService executor = Executors.newFixedThreadPool(1);\n        executor.submit(new Runnable() {\n\n            public void run() {\n                int times = 50;\n                handler.before(Arrays.asList(buildEvent()));\n                while (--times > 0) {\n                    try {\n                        Thread.sleep(50000);\n                    } catch (InterruptedException e) {\n                    }\n\n                    handler.before(Arrays.asList(buildEvent()));\n                }\n\n                count.countDown();\n            }\n        });\n\n        try {\n            count.await();\n        } catch (InterruptedException e) {\n        }\n    }\n\n    private Event buildEvent() {\n\n        Header.Builder headBuilder = Header.newBuilder();\n        headBuilder.setEventLength(1000L);\n        headBuilder.setExecuteTime(new Date().getTime());\n        headBuilder.setLogfileName(\"mysql-bin.000001\");\n        headBuilder.setLogfileOffset(1000L);\n        headBuilder.setSchemaName(\"test\");\n        headBuilder.setTableName(\"ljh\");\n\n        Entry.Builder entryBuilder = Entry.newBuilder();\n        entryBuilder.setHeader(headBuilder.build());\n        entryBuilder.setEntryType(EntryType.ROWDATA);\n\n        RowChange.Builder rowChangeBuilder = RowChange.newBuilder();\n        RowData.Builder rowDataBuilder = RowData.newBuilder();\n        rowChangeBuilder.addRowDatas(rowDataBuilder.build());\n\n        entryBuilder.setStoreValue(rowChangeBuilder.build().toByteString());\n        Entry entry = entryBuilder.build();\n        Event event = new Event(new LogIdentity(), entry);\n        return event;\n    }\n}\n"
  },
  {
    "path": "node/etl/src/test/java/com/alibaba/otter/node/etl/transform/OtterTransformerTest.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.etl.transform;\n\nimport java.io.File;\nimport java.sql.Types;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\nimport mockit.Mocked;\n\nimport org.jtester.annotations.SpringBeanByName;\nimport org.jtester.annotations.SpringBeanFrom;\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.node.common.config.ConfigClientService;\nimport com.alibaba.otter.node.etl.BaseDbTest;\nimport com.alibaba.otter.node.etl.transform.transformer.OtterTransformerFactory;\nimport com.alibaba.otter.shared.common.model.config.channel.ChannelParameter.SyncMode;\nimport com.alibaba.otter.shared.common.model.config.data.DataMediaPair;\nimport com.alibaba.otter.shared.common.model.config.data.db.DbDataMedia;\nimport com.alibaba.otter.shared.common.model.config.pipeline.Pipeline;\nimport com.alibaba.otter.shared.common.model.config.pipeline.PipelineParameter;\nimport com.alibaba.otter.shared.etl.model.BatchObject;\nimport com.alibaba.otter.shared.etl.model.EventColumn;\nimport com.alibaba.otter.shared.etl.model.EventData;\nimport com.alibaba.otter.shared.etl.model.EventType;\nimport com.alibaba.otter.shared.etl.model.FileBatch;\nimport com.alibaba.otter.shared.etl.model.FileData;\nimport com.alibaba.otter.shared.etl.model.Identity;\nimport com.alibaba.otter.shared.etl.model.RowBatch;\n\nclass OtterTransformerTest extends BaseDbTest {\n\n    @SpringBeanFrom\n    @Mocked\n    private ConfigClientService     configClientService;\n\n    @SpringBeanByName\n    private OtterTransformerFactory otterTransformFactory;\n\n    @Test\n    public void test_rowData_mysql_oracle() {\n        final Pipeline pipeline = new Pipeline();\n        pipeline.setId(100L);\n\n        List<DataMediaPair> pairs = new ArrayList<DataMediaPair>();\n        DataMediaPair pair1 = new DataMediaPair();\n        pair1.setId(1L);\n        pair1.setPipelineId(pipeline.getId());\n        pair1.setPullWeight(1L);\n        pair1.setPushWeight(1L);\n\n        DbDataMedia mysqlMedia = getMysqlMedia();\n        mysqlMedia.setId(1L);\n        pair1.setSource(mysqlMedia);\n\n        DbDataMedia oracleMedia = getOracleMedia();\n        pair1.setTarget(oracleMedia);\n        pairs.add(pair1);\n        pipeline.setPairs(pairs);\n        PipelineParameter param = new PipelineParameter();\n        param.setSyncMode(SyncMode.ROW);\n\n        pipeline.setParameters(param);\n        new NonStrictExpectations() {\n\n            {\n                configClientService.findPipeline(anyLong);\n                returns(pipeline);\n            }\n        };\n\n        Identity identity = new Identity();\n        identity.setChannelId(100L);\n        identity.setPipelineId(100L);\n        identity.setProcessId(100L);\n\n        RowBatch rowBatch = new RowBatch();\n        rowBatch.setIdentity(identity);\n        EventData eventData = new EventData();\n        eventData.setTableId(1L);\n        eventData.setSchemaName(\"srf\");\n        eventData.setTableName(\"columns\");\n        eventData.setEventType(EventType.UPDATE);\n        eventData.setExecuteTime(100L);\n\n        eventData.getKeys().add(buildColumn(\"id\", Types.INTEGER, \"1\", true, false));\n        eventData.getKeys().add(buildColumn(\"name\", Types.VARCHAR, \"ljh\", true, false));\n\n        eventData.getColumns().add(buildColumn(\"alias_name\", Types.CHAR, \"hello\", false, false));\n        eventData.getColumns().add(buildColumn(\"amount\", Types.DECIMAL, \"100.01\", false, false));\n        eventData.getColumns().add(buildColumn(\"text_b\", Types.BLOB, \"[116,101,120,116,95,98]\", false, false));\n        eventData.getColumns().add(buildColumn(\"text_c\", Types.CLOB, \"text_c\", false, false));\n        eventData.getColumns().add(buildColumn(\"curr_date\", Types.DATE, \"2011-01-01\", false, false));\n        eventData.getColumns().add(buildColumn(\"gmt_create\", Types.TIMESTAMP, \"2011-01-01 11:11:11\", false, false));\n        eventData.getColumns().add(buildColumn(\"gmt_modify\", Types.TIMESTAMP, \"2011-01-01 11:11:11\", false, false));\n\n        rowBatch.merge(eventData);\n\n        Map<Class, BatchObject> batchs = otterTransformFactory.transform(rowBatch);\n        RowBatch result = (RowBatch) batchs.get(EventData.class);\n        want.number(result.getDatas().size()).isEqualTo(1);\n    }\n\n    @Test\n    public void test_rowData_oracle_mysql() {\n        final Pipeline pipeline = new Pipeline();\n        pipeline.setId(100L);\n\n        List<DataMediaPair> pairs = new ArrayList<DataMediaPair>();\n        DataMediaPair pair1 = new DataMediaPair();\n        pair1.setId(1L);\n        pair1.setPipelineId(pipeline.getId());\n        pair1.setPullWeight(1L);\n        pair1.setPushWeight(1L);\n\n        DbDataMedia oracleMedia = getOracleMedia();\n        oracleMedia.setId(1L);\n        pair1.setSource(oracleMedia);\n\n        DbDataMedia mysqlMedia = getMysqlMedia();\n        pair1.setTarget(mysqlMedia);\n\n        pairs.add(pair1);\n        pipeline.setPairs(pairs);\n        PipelineParameter param = new PipelineParameter();\n        param.setSyncMode(SyncMode.ROW);\n\n        pipeline.setParameters(param);\n        new NonStrictExpectations() {\n\n            {\n                configClientService.findPipeline(anyLong);\n                returns(pipeline);\n            }\n        };\n\n        Identity identity = new Identity();\n        identity.setChannelId(100L);\n        identity.setPipelineId(100L);\n        identity.setProcessId(100L);\n\n        RowBatch rowBatch = new RowBatch();\n        rowBatch.setIdentity(identity);\n        EventData eventData = new EventData();\n        eventData.setTableId(1L);\n        eventData.setSchemaName(\"srf\");\n        eventData.setTableName(\"columns\");\n        eventData.setEventType(EventType.UPDATE);\n        eventData.setExecuteTime(100L);\n\n        eventData.getKeys().add(buildColumn(\"id\", Types.NUMERIC, \"1\", true, false));\n        eventData.getKeys().add(buildColumn(\"name\", Types.VARCHAR, \"ljh\", true, false));\n\n        eventData.getColumns().add(buildColumn(\"alias_name\", Types.CHAR, \"hello\", false, false));\n        eventData.getColumns().add(buildColumn(\"amount\", Types.NUMERIC, \"100.01\", false, false));\n        eventData.getColumns().add(buildColumn(\"text_b\", Types.BLOB, \"[116,101,120,116,95,98]\", false, false));\n        eventData.getColumns().add(buildColumn(\"text_c\", Types.CLOB, \"text_c\", false, false));\n        eventData.getColumns().add(buildColumn(\"curr_date\", Types.DATE, \"2011-01-01\", false, false));\n        eventData.getColumns().add(buildColumn(\"gmt_create\", Types.DATE, \"2011-01-01 11:11:11\", false, false));\n        eventData.getColumns().add(buildColumn(\"gmt_modify\", Types.DATE, \"2011-01-01 11:11:11\", false, false));\n\n        rowBatch.merge(eventData);\n\n        Map<Class, BatchObject> batchs = otterTransformFactory.transform(rowBatch);\n        RowBatch result = (RowBatch) batchs.get(EventData.class);\n        want.number(result.getDatas().size()).isEqualTo(1);\n    }\n\n    @Test\n    public void test_fileData() {\n        final Pipeline pipeline = new Pipeline();\n        pipeline.setId(100L);\n\n        List<DataMediaPair> pairs = new ArrayList<DataMediaPair>();\n        DataMediaPair pair1 = new DataMediaPair();\n        pair1.setId(1L);\n        pair1.setPipelineId(pipeline.getId());\n        pair1.setPullWeight(1L);\n        pair1.setPushWeight(1L);\n\n        DbDataMedia oracleMedia = getOracleMedia();\n        oracleMedia.setId(1L);\n        pair1.setSource(oracleMedia);\n\n        DbDataMedia mysqlMedia = getMysqlMedia();\n        pair1.setTarget(mysqlMedia);\n\n        pairs.add(pair1);\n        pipeline.setPairs(pairs);\n        new NonStrictExpectations() {\n\n            {\n                configClientService.findPipeline(anyLong);\n                returns(pipeline);\n            }\n        };\n\n        Identity identity = new Identity();\n        identity.setChannelId(100L);\n        identity.setPipelineId(100L);\n        identity.setProcessId(100L);\n\n        FileBatch fileBatch = new FileBatch();\n        fileBatch.setIdentity(identity);\n        File localFile = new File(\"/tmp\", \"httpPipeTest.jpg\");\n        FileData localFileData = new FileData();\n        localFileData.setTableId(1L);\n        localFileData.setPairId(1L);\n        localFileData.setPath(localFile.getPath());\n        fileBatch.getFiles().add(localFileData);\n\n        Map<Class, BatchObject> batchs = otterTransformFactory.transform(fileBatch);\n        FileBatch result = (FileBatch) batchs.get(FileData.class);\n        want.number(result.getFiles().size()).isEqualTo(1);\n    }\n\n    private EventColumn buildColumn(String name, int type, String value, boolean isKey, boolean isNull) {\n        EventColumn column = new EventColumn();\n        column.setColumnName(name);\n        column.setColumnType(type);\n        column.setColumnValue(value);\n        column.setKey(isKey);\n        column.setNull(isNull);\n        return column;\n    }\n}\n"
  },
  {
    "path": "node/etl/src/test/resources/applicationContext.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<beans xmlns=\"http://www.springframework.org/schema/beans\"\r\n\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:tx=\"http://www.springframework.org/schema/tx\"\r\n\txmlns:aop=\"http://www.springframework.org/schema/aop\" xmlns:lang=\"http://www.springframework.org/schema/lang\"\r\n\txmlns:context=\"http://www.springframework.org/schema/context\"\r\n\txsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd\r\n           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd\r\n           http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-2.0.xsd\r\n           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd\r\n           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd\"\r\n\tdefault-autowire=\"byName\">\r\n\t\r\n\t<!-- properties -->\r\n\t<bean class=\"com.alibaba.otter.shared.common.utils.spring.PropertyPlaceholderConfigurer\" lazy-init=\"false\">\r\n\t\t<property name=\"ignoreResourceNotFound\" value=\"true\" />\r\n\t\t<property name=\"systemPropertiesModeName\" value=\"SYSTEM_PROPERTIES_MODE_OVERRIDE\"/><!-- 允许system覆盖 -->\r\n\t\t<property name=\"locations\">\r\n\t\t\t<list>\r\n\t\t\t\t<value>classpath:otter.properties</value>\r\n\t\t\t</list>\r\n\t\t</property>\r\n\t</bean>\r\n\t\r\n\t<import resource=\"classpath*:spring/otter-node-*.xml\"/>\r\n\t<import resource=\"classpath*:spring/otter-canal-*.xml\"/>\r\n\t<import resource=\"classpath*:spring/otter-arbitrate-*.xml\"/>\r\n\t<import resource=\"classpath*:spring/otter-push-*.xml\"/>\r\n</beans>"
  },
  {
    "path": "node/etl/src/test/resources/sql/mysql_init.sql",
    "content": "drop table columns;\ncreate table columns \n(\t\n\tid INT(11) AUTO_INCREMENT,\n\tname VARCHAR(32) ,\n\talias_name\tchar(32) default ' ' not null,\n\tamount DECIMAL(11,2) , \n\ttext_b blob,\n\ttext_c TEXT,\n\tcurr_date date not null,\n\tgmt_create TIMESTAMP not null,\n\tgmt_modify TIMESTAMP not null,\n\tCONSTRAINT   columns_pk  PRIMARY   KEY   (id,name) \n) ENGINE = InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;\n\ndrop retl_client;\ncreate table retl_client\n(\n\tid INT(11) ,\n\tchannel_id int(11),\n\tCONSTRAINT retl_client_pk PRIMARY KEY (id)\n) ENGINE = InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin ;\n\ndrop `erosa`.`offer`;\nCREATE TABLE  `erosa`.`offer` (\n  `SITE` varchar(64) COLLATE utf8_bin DEFAULT NULL,\n  `id` bigint(20) NOT NULL,\n  `gmt_create` datetime NOT NULL DEFAULT '1999-09-09 00:00:00',\n  `gmt_modified` datetime NOT NULL DEFAULT '1999-09-09 00:00:00',\n  `gmt_approved` datetime DEFAULT NULL,\n  `gmt_post` datetime DEFAULT NULL,\n  `ACTION` varchar(32) COLLATE utf8_bin DEFAULT NULL,\n  `STATUS` varchar(32) COLLATE utf8_bin DEFAULT NULL,\n  `RECOMMENDED` char(1) COLLATE utf8_bin DEFAULT NULL,\n  `category_id_1` bigint(20) DEFAULT NULL,\n  `category_id_2` bigint(20) DEFAULT NULL,\n  `category_id_3` bigint(20) DEFAULT NULL,\n  `category_id_4` bigint(20) DEFAULT NULL,\n  `category_id_5` bigint(20) DEFAULT NULL,\n  `SUBJECT` varchar(512) COLLATE utf8_bin DEFAULT NULL,\n  `PRICE_TERMS` varchar(512) COLLATE utf8_bin DEFAULT NULL,\n  `PACKAGING` varchar(512) COLLATE utf8_bin DEFAULT NULL,\n  `TYPE` varchar(32) COLLATE utf8_bin DEFAULT NULL,\n  `gmt_expire` datetime DEFAULT NULL,\n  `KEYWORDS` varchar(512) COLLATE utf8_bin DEFAULT NULL,\n  `SPECIFICATIONS` varchar(512) COLLATE utf8_bin DEFAULT NULL,\n  `QUANTITY` varchar(512) COLLATE utf8_bin DEFAULT NULL,\n  `FIRST_NAME` varchar(256) COLLATE utf8_bin DEFAULT NULL,\n  `COUNTRY` varchar(128) COLLATE utf8_bin DEFAULT NULL,\n  `LAST_NAME` varchar(256) COLLATE utf8_bin DEFAULT NULL,\n  `PROVINCE` varchar(256) COLLATE utf8_bin DEFAULT NULL,\n  `COMPANY` varchar(256) COLLATE utf8_bin DEFAULT NULL,\n  `CITY` varchar(256) COLLATE utf8_bin DEFAULT NULL,\n  `EMAIL` varchar(256) COLLATE utf8_bin DEFAULT NULL,\n  `ADDRESS` varchar(512) COLLATE utf8_bin DEFAULT NULL,\n  `ZIP` varchar(64) COLLATE utf8_bin DEFAULT NULL,\n  `ALT_EMAIL` varchar(256) COLLATE utf8_bin DEFAULT NULL,\n  `PHONE_COUNTRY` varchar(16) COLLATE utf8_bin DEFAULT NULL,\n  `PHONE_AREA` varchar(16) COLLATE utf8_bin DEFAULT NULL,\n  `PHONE_NUMBER` varchar(256) COLLATE utf8_bin DEFAULT NULL,\n  `HOMEPAGE_URL` varchar(256) COLLATE utf8_bin DEFAULT NULL,\n  `FAX_COUNTRY` varchar(16) COLLATE utf8_bin DEFAULT NULL,\n  `FAX_AREA` varchar(16) COLLATE utf8_bin DEFAULT NULL,\n  `FAX_NUMBER` varchar(256) COLLATE utf8_bin DEFAULT NULL,\n  `CSA_ADD` char(1) COLLATE utf8_bin DEFAULT NULL,\n  `member_id` varchar(32) COLLATE utf8_bin NOT NULL,\n  `WSAD_OFFER` char(1) COLLATE utf8_bin DEFAULT NULL,\n  `REMMIT_STATUS` char(1) COLLATE utf8_bin DEFAULT NULL,\n  `remmit_date` datetime DEFAULT NULL,\n  `remmit_value` decimal(11,2) DEFAULT NULL,\n  `REMMIT_WAY` char(1) COLLATE utf8_bin DEFAULT NULL,\n  `PICWAY` varchar(20) COLLATE utf8_bin DEFAULT NULL,\n  `PICLINK` varchar(256) COLLATE utf8_bin DEFAULT NULL,\n  `PICSAMPLE` varchar(256) COLLATE utf8_bin DEFAULT NULL,\n  `TO_CHINESE` char(1) COLLATE utf8_bin DEFAULT NULL,\n  `JOIN_FROM` varchar(512) COLLATE utf8_bin DEFAULT NULL,\n  `paid_money` bigint(20) DEFAULT NULL,\n  `ordering` bigint(20) DEFAULT NULL,\n  `SAMPLE_URL` varchar(256) COLLATE utf8_bin DEFAULT NULL,\n  `SERVICE_VALUE` varchar(64) COLLATE utf8_bin DEFAULT NULL,\n  `IS_EXPIRED` char(1) COLLATE utf8_bin DEFAULT NULL,\n  `company_id` bigint(20) DEFAULT NULL,\n  `FEEDBACK_FLAG` char(1) COLLATE utf8_bin DEFAULT NULL,\n  `MEMBER_LEVEL` varchar(32) COLLATE utf8_bin DEFAULT NULL,\n  `MOBILE_PHONE_STATUS` varchar(32) COLLATE utf8_bin DEFAULT NULL,\n  `SMS_REV_FEEDBACK` char(1) COLLATE utf8_bin DEFAULT NULL,\n  `SMS_REV_EXPIRED` char(1) COLLATE utf8_bin DEFAULT NULL,\n  `MATURITY` int(11) DEFAULT NULL,\n  `PICSAMPLE_ARRAY` varchar(512) COLLATE utf8_bin DEFAULT NULL,\n  `MEMBER_DEFINE_PROPERTIES` varchar(6000) COLLATE utf8_bin DEFAULT NULL,\n  `OFFER_SPECIAL_SIGN` int(11) DEFAULT NULL,\n  `QUALITY_SCORE` int(11) DEFAULT NULL,\n  `gmt_client_last_operate` datetime DEFAULT NULL,\n  `STATUS_BEFORE_CLIENT_OPERATE` varchar(64) COLLATE utf8_bin DEFAULT NULL,\n  `REASON` varchar(256) COLLATE utf8_bin DEFAULT NULL,\n  `OWNER_ID` varchar(128) COLLATE utf8_bin DEFAULT NULL,\n  `IS_CHECKED` char(1) COLLATE utf8_bin DEFAULT NULL,\n  `TERM_OFFER_PROCESS` int(11) DEFAULT NULL,\n  `BRIEF` varchar(2000) COLLATE utf8_bin DEFAULT NULL,\n  `price` bigint(20) DEFAULT NULL,\n  `quantity_begin` bigint(20) DEFAULT NULL,\n  `UNIT` varchar(160) COLLATE utf8_bin DEFAULT NULL,\n  `BUYER_LEVEL` varchar(64) COLLATE utf8_bin DEFAULT NULL,\n  `buy_sub_type` bigint(20) DEFAULT NULL,\n  `TRADE_TYPE` int(11) DEFAULT NULL,\n  `SIGN` int(11) DEFAULT NULL,\n  `batch_no` bigint(20) DEFAULT NULL,\n  `gmt_last_repost` datetime DEFAULT NULL,\n  `group_id` bigint(20) DEFAULT NULL,\n  `column_int1` int(11) DEFAULT NULL,\n  `column_int2` int(11) DEFAULT NULL,\n  `column_int3` int(11) DEFAULT NULL,\n  `column_int4` int(11) DEFAULT NULL,\n  `column_varchar1` varchar(32) COLLATE utf8_bin DEFAULT NULL,\n  `column_varchar2` varchar(64) COLLATE utf8_bin DEFAULT NULL,\n  `column_varchar3` varchar(128) COLLATE utf8_bin DEFAULT NULL,\n  `column_varchar4` varchar(128) COLLATE utf8_bin DEFAULT NULL,\n  `column_varchar5` varchar(128) COLLATE utf8_bin DEFAULT NULL,\n  `column_varchar6` varchar(4000) COLLATE utf8_bin DEFAULT NULL,\n  PRIMARY KEY (`id`),\n  KEY `idx_offer_mid_st_ge_gid_id_sub` (`member_id`,`STATUS`,`gmt_expire`,`group_id`,`id`,`SUBJECT`(255))\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin\n\n\ncreate table retl.retl_buffer \n(\t\n\tid INT(11) AUTO_INCREMENT,\n\ttable_id INT(11) not null,\n\ttype char(1) not null,\n\tpk_data varchar(256) not null,\n\tgmt_create TIMESTAMP not null,\n\tgmt_modified TIMESTAMP not null,\n\tCONSTRAINT retl_buffer_id PRIMARY KEY (id) \n)  ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin\n\ncreate table retl.retl_mark\n(\t\n\tid INT(11) AUTO_INCREMENT,\n\tchannel_id INT(11) not null,\n\tCONSTRAINT retl_mark_id PRIMARY KEY (id) \n);\n\nuse monitor;\ndrop table `xdual`;\n\nCREATE TABLE `xdual` (\n  `id` int(11) NOT NULL AUTO_INCREMENT,\n  `x` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n  PRIMARY KEY (`id`)\n) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8"
  },
  {
    "path": "node/etl/src/test/resources/sql/oracle_init.sql",
    "content": "drop table columns;\ncreate table columns \n(\t\n\tid NUMBER(11)  ,\n\tname varchar2(32) ,\n\talias_name\tchar(32) default ' ' not null,\n\tamount number(11,2) , \n\ttext_b blob,\n\ttext_c clob,\n\tcurr_date date not null,\n\tgmt_create date not null,\n\tgmt_modify date not null,\n\tCONSTRAINT columns_pk  PRIMARY   KEY   (id,name) \n);\n\n\ndrop retl_client;\ncreate table retl_client\n(\n\tid NUMBER(11) ,\n\tchannel_id NUMBER(11),\n\tCONSTRAINT retl_client_pk PRIMARY KEY (id)\n);\n\ndrop offer;\nCREATE TABLE  OFFER (\n  SITE varchar2(64) DEFAULT NULL,\n  id number(20) NOT NULL,\n  gmt_create date DEFAULT sysdate NOT NULL ,\n  gmt_modified date DEFAULT sysdate NOT NULL ,\n  gmt_approved date DEFAULT NULL,\n  gmt_post date DEFAULT NULL,\n  ACTION varchar2(32) DEFAULT NULL,\n  STATUS varchar2(32) DEFAULT NULL,\n  RECOMMENDED char(1) DEFAULT NULL,\n  category_id_1 number(20) DEFAULT NULL,\n  category_id_2 number(20) DEFAULT NULL,\n  category_id_3 number(20) DEFAULT NULL,\n  category_id_4 number(20) DEFAULT NULL,\n  category_id_5 number(20) DEFAULT NULL,\n  SUBJECT varchar2(512) DEFAULT NULL,\n  PRICE_TERMS varchar2(512) DEFAULT NULL,\n  PACKAGING varchar2(512) DEFAULT NULL,\n  TYPE varchar2(32) DEFAULT NULL,\n  gmt_expire date DEFAULT NULL,\n  KEYWORDS varchar2(512) DEFAULT NULL,\n  SPECIFICATIONS varchar2(512) DEFAULT NULL,\n  QUANTITY varchar2(512) DEFAULT NULL,\n  FIRST_NAME varchar2(256) DEFAULT NULL,\n  COUNTRY varchar2(128) DEFAULT NULL,\n  LAST_NAME varchar2(256) DEFAULT NULL,\n  PROVINCE varchar2(256) DEFAULT NULL,\n  COMPANY varchar2(256) DEFAULT NULL,\n  CITY varchar2(256) DEFAULT NULL,\n  EMAIL varchar2(256) DEFAULT NULL,\n  ADDRESS varchar2(512) DEFAULT NULL,\n  ZIP varchar2(64) DEFAULT NULL,\n  ALT_EMAIL varchar2(256) DEFAULT NULL,\n  PHONE_COUNTRY varchar2(16) DEFAULT NULL,\n  PHONE_AREA varchar2(16) DEFAULT NULL,\n  PHONE_NUMBER varchar2(256) DEFAULT NULL,\n  HOMEPAGE_URL varchar2(256) DEFAULT NULL,\n  FAX_COUNTRY varchar2(16) DEFAULT NULL,\n  FAX_AREA varchar2(16) DEFAULT NULL,\n  FAX_NUMBER varchar2(256) DEFAULT NULL,\n  CSA_ADD char(1) DEFAULT NULL,\n  member_id varchar2(32) NOT NULL,\n  WSAD_OFFER char(1) DEFAULT NULL,\n  REMMIT_STATUS char(1) DEFAULT NULL,\n  remmit_date date DEFAULT NULL,\n  remmit_value number(11,2) DEFAULT NULL,\n  REMMIT_WAY char(1) DEFAULT NULL,\n  PICWAY varchar2(20) DEFAULT NULL,\n  PICLINK varchar2(256) DEFAULT NULL,\n  PICSAMPLE varchar2(256) DEFAULT NULL,\n  TO_CHINESE char(1) DEFAULT NULL,\n  JOIN_FROM varchar2(512) DEFAULT NULL,\n  paid_money number(20) DEFAULT NULL,\n  ordering number(20) DEFAULT NULL,\n  SAMPLE_URL varchar2(256) DEFAULT NULL,\n  SERVICE_VALUE varchar2(64) DEFAULT NULL,\n  IS_EXPIRED char(1) DEFAULT NULL,\n  company_id number(20) DEFAULT NULL,\n  FEEDBACK_FLAG char(1) DEFAULT NULL,\n  MEMBER_LEVEL varchar2(32) DEFAULT NULL,\n  MOBILE_PHONE_STATUS varchar2(32) DEFAULT NULL,\n  SMS_REV_FEEDBACK char(1) DEFAULT NULL,\n  SMS_REV_EXPIRED char(1) DEFAULT NULL,\n  MATURITY number(11) DEFAULT NULL,\n  PICSAMPLE_ARRAY varchar2(512) DEFAULT NULL,\n  MEMBER_DEFINE_PROPERTIES varchar2(4000) DEFAULT NULL,\n  OFFER_SPECIAL_SIGN number(11) DEFAULT NULL,\n  QUALITY_SCORE number(11) DEFAULT NULL,\n  gmt_client_last_operate date DEFAULT NULL,\n  STATUS_BEFORE_CLIENT_OPERATE varchar2(64) DEFAULT NULL,\n  REASON varchar2(256) DEFAULT NULL,\n  OWNER_ID varchar2(128) DEFAULT NULL,\n  IS_CHECKED char(1) DEFAULT NULL,\n  TERM_OFFER_PROCESS number(11) DEFAULT NULL,\n  BRIEF varchar2(2000) DEFAULT NULL,\n  price number(20) DEFAULT NULL,\n  quantity_begin number(20) DEFAULT NULL,\n  UNIT varchar2(160) DEFAULT NULL,\n  BUYER_LEVEL varchar2(64) DEFAULT NULL,\n  buy_sub_type number(20) DEFAULT NULL,\n  TRADE_TYPE number(11) DEFAULT NULL,\n  SIGN number(11) DEFAULT NULL,\n  batch_no number(20) DEFAULT NULL,\n  gmt_last_repost date DEFAULT NULL,\n  group_id number(20) DEFAULT NULL,\n  column_int1 number(11) DEFAULT NULL,\n  column_int2 number(11) DEFAULT NULL,\n  column_int3 number(11) DEFAULT NULL,\n  column_int4  number(11) DEFAULT NULL,\n  column_varchar1 varchar2(32) DEFAULT NULL,\n  column_varchar2 varchar2(64) DEFAULT NULL,\n  column_varchar3 varchar2(128) DEFAULT NULL,\n  column_varchar4 varchar2(128) DEFAULT NULL,\n  column_varchar5 varchar2(128) DEFAULT NULL,\n  column_varchar6 varchar2(4000) DEFAULT NULL,\n  CONSTRAINT offer_pk PRIMARY KEY (id)\n);\n\nCREATE INDEX idx_offer_mid_st_ge_gid_id_sub on OFFER(member_id,STATUS,gmt_expire,group_id,id,SUBJECT)\n\ncreate table retl.retl_buffer \n(\t\n\tid number(11) not null,\n\ttable_id number(11) not null,\n\ttype char(1) not null,\n\tpk_data varchar(256) not null,\n\tgmt_create DATE not null,\n\tgmt_modified DATE not null,\n\tCONSTRAINT retl_buffer_id PRIMARY KEY (id) \n);\n\ncreate table retl.retl_mark\n(\t\n\tid number(11) not null,\n\tchannel_id number(11) not null,\n\tCONSTRAINT retl_mark_id PRIMARY KEY (id) \n);"
  },
  {
    "path": "node/extend/pom.xml",
    "content": "<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\">\n\t<modelVersion>4.0.0</modelVersion>\n\t<parent>\n\t\t<groupId>com.alibaba.otter</groupId>\n\t\t<artifactId>node</artifactId>\n\t\t<version>4.2.19-SNAPSHOT</version>\n\t\t<relativePath>../pom.xml</relativePath>\n\t</parent>\n\t<groupId>com.alibaba.otter</groupId>\n\t<artifactId>node.extend</artifactId>\n\t<packaging>jar</packaging>\n\t<name>otter extend module for otter</name>\n\t<url>http://github.com/alibaba/otter</url>\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>com.alibaba.otter</groupId>\n\t\t\t<artifactId>shared.etl</artifactId>\n\t\t\t<version>${project.version}</version>\n\t\t</dependency>\n\t\t<!-- test dependency -->\n\t\t<dependency>\n\t\t\t<groupId>junit</groupId>\n\t\t\t<artifactId>junit</artifactId>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\t</dependencies>\n</project>\n"
  },
  {
    "path": "node/extend/src/main/java/com/alibaba/otter/node/extend/fileresolver/AbstractFileResolver.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.extend.fileresolver;\n\nimport com.alibaba.otter.shared.etl.extend.fileresolver.FileResolver;\n\n/**\n * @author jianghang 2012-10-23 下午04:11:14\n * @version 4.1.0\n */\npublic abstract class AbstractFileResolver implements FileResolver {\n\n    public boolean isDeleteRequired() {\n        return false;\n    }\n\n    public boolean isDistributed() {\n        return false;\n    }\n}\n"
  },
  {
    "path": "node/extend/src/main/java/com/alibaba/otter/node/extend/fileresolver/TestFileResolver.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.extend.fileresolver;\n\nimport java.util.Map;\n\nimport org.apache.commons.lang.StringUtils;\n\nimport com.alibaba.otter.shared.etl.extend.fileresolver.FileInfo;\n\npublic class TestFileResolver extends AbstractFileResolver {\n\n    public FileInfo[] getFileInfo(Map<String, String> rowMap) {\n        // 基本步骤：\n        // 1. 获取binlog中的变更字段，比如组成文件有多个字段组成version+path\n        // 2. 基于字段内容，构造一个文件路径，目前开源版本只支持本地文件的同步.(如果是网络文件，建议进行NFS mount到ndde机器).\n        // 3. 返回FileInfo数组，(目前不支持目录同步，如果是目录需要展开为多个FileInfo的子文件)，如果不需要同步，则返回null.\n        String path = rowMap.get(\"FIELD\"); //注意为大写\n        FileInfo fileInfo = null;\n        if (StringUtils.isNotEmpty(path)) {\n            fileInfo = new FileInfo(path);\n            return new FileInfo[] { fileInfo };\n        } else {\n            return null;\n        }\n    }\n\n}\n"
  },
  {
    "path": "node/extend/src/main/java/com/alibaba/otter/node/extend/processor/AbstractEventProcessor.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.extend.processor;\n\nimport org.apache.commons.lang.StringUtils;\n\nimport com.alibaba.otter.shared.etl.extend.processor.EventProcessor;\nimport com.alibaba.otter.shared.etl.model.EventColumn;\nimport com.alibaba.otter.shared.etl.model.EventData;\n\n/**\n * 业务自定义处理过程\n * \n * @author jianghang 2012-6-25 下午02:26:36\n * @version 4.1.0\n */\npublic class AbstractEventProcessor implements EventProcessor {\n\n    public boolean process(EventData eventData) {\n        // 默认啥都不处理\n        return true;\n    }\n\n    protected EventColumn getColumn(EventData eventData, String columnName) {\n        for (EventColumn column : eventData.getColumns()) {\n            if (StringUtils.equalsIgnoreCase(column.getColumnName(), columnName)) {\n                return column;\n            }\n        }\n\n        for (EventColumn column : eventData.getKeys()) {\n            if (StringUtils.equalsIgnoreCase(column.getColumnName(), columnName)) {\n                return column;\n            }\n        }\n        return null;\n    }\n\n}\n"
  },
  {
    "path": "node/extend/src/main/java/com/alibaba/otter/node/extend/processor/HintEventProcessor.java",
    "content": "package com.alibaba.otter.node.extend.processor;\n\nimport com.alibaba.otter.shared.etl.model.EventData;\n\n/**\n * 测试下hint\n * \n * @author jianghang 2014-6-11 下午4:20:32\n * @since 5.1.0\n */\npublic class HintEventProcessor extends AbstractEventProcessor {\n\n    public boolean process(EventData eventData) {\n        eventData.setHint(\"/* hint */\");\n        return true;\n    }\n}\n"
  },
  {
    "path": "node/extend/src/main/java/com/alibaba/otter/node/extend/processor/TestEventProcessor.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.node.extend.processor;\n\nimport java.sql.Types;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport com.alibaba.fastjson.JSONArray;\nimport com.alibaba.fastjson.JSONObject;\nimport com.alibaba.otter.shared.etl.model.EventColumn;\nimport com.alibaba.otter.shared.etl.model.EventData;\nimport com.alibaba.otter.shared.etl.model.EventType;\n\npublic class TestEventProcessor extends AbstractEventProcessor {\n\n    public boolean process(EventData eventData) {\n        // 基本步骤：\n        // 1. 获取binlog中的变更字段\n        // 2. 根据业务逻辑进行判断，如果需要忽略本条数据同步，直接返回false，否则返回true\n        // 3. 根据业务逻辑进行逻辑转化，比如可以修改整个EventData数据.  \n\n        // 本文例子：源库的每条binlog变更，记录到一个日志表binlog\n        // create table test.binlog(\n        //        id bigint(20) auto_increment,\n        //        oschema varchar(256),\n        //        otable varchar(256),\n        //        gtime varchar(32)\n        //        ovalue text,\n        //        primary key(id);\n        //    )\n        // 在process处理中，可以修改EventData的任何数据，达到数据转换的效果, just have fun.\n        JSONObject col = new JSONObject();\n        JSONArray array = new JSONArray();\n        for (EventColumn column : eventData.getColumns()) {\n            JSONObject obj = this.doColumn(column);\n            array.add(obj);\n        }\n\n        for (EventColumn key : eventData.getKeys()) {\n            JSONObject obj = this.doColumn(key);\n            array.add(obj);\n        }\n\n        col.put(\"schema\", eventData.getSchemaName());\n        col.put(\"table\", eventData.getTableName());\n        col.put(\"columns\", array);\n        col.put(\"dml\", eventData.getEventType());\n        col.put(\"exectime\", eventData.getExecuteTime());\n\n        // 构造新的主键\n        EventColumn id = new EventColumn();\n        id.setColumnValue(eventData.getSchemaName());\n        id.setColumnType(Types.BIGINT);\n        id.setColumnName(\"id\");\n        // 构造新的字段\n        EventColumn schema = new EventColumn();\n        schema.setColumnValue(eventData.getSchemaName());\n        schema.setColumnType(Types.VARCHAR);\n        schema.setColumnName(\"oschema\");\n\n        EventColumn table = new EventColumn();\n        table.setColumnValue(eventData.getTableName());\n        table.setColumnType(Types.VARCHAR);\n        table.setColumnName(\"otable\");\n\n        EventColumn ovalue = new EventColumn();\n        ovalue.setColumnValue(col.toJSONString());\n        ovalue.setColumnType(Types.VARCHAR);\n        ovalue.setColumnName(\"ovalue\");\n\n        EventColumn gtime = new EventColumn();\n        gtime.setColumnValue(eventData.getExecuteTime() + \"\");\n        gtime.setColumnType(Types.VARCHAR);\n        gtime.setColumnName(\"gtime\");\n\n        // 替换为新的字段和主键信息\n        List<EventColumn> cols = new ArrayList<EventColumn>();\n        cols.add(schema);\n        cols.add(table);\n        cols.add(gtime);\n        cols.add(ovalue);\n        eventData.setColumns(cols);\n\n        List<EventColumn> keys = new ArrayList<EventColumn>();\n        keys.add(id);\n        eventData.setKeys(keys);\n\n        //修改数据meta信息\n        eventData.setEventType(EventType.INSERT);\n        eventData.setSchemaName(\"test\");\n        eventData.setTableName(\"binlog\");\n        return true;\n    }\n\n    private JSONObject doColumn(EventColumn column) {\n        JSONObject obj = new JSONObject();\n        obj.put(\"name\", column.getColumnName());\n        obj.put(\"update\", column.isUpdate());\n        obj.put(\"key\", column.isKey());\n        if (column.getColumnType() != Types.BLOB && column.getColumnType() != Types.CLOB) {\n            obj.put(\"value\", column.getColumnValue());\n        } else {\n            obj.put(\"value\", \"\");\n        }\n        return obj;\n    }\n}\n"
  },
  {
    "path": "node/pom.xml",
    "content": "<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\">\n\t<modelVersion>4.0.0</modelVersion>\n\t<parent>\n\t    <groupId>com.alibaba.otter</groupId>\n\t    <artifactId>otter</artifactId>\n\t    <version>4.2.19-SNAPSHOT</version>\n\t</parent>\n\t<groupId>com.alibaba.otter</groupId>\n\t<artifactId>node</artifactId>\n\t<packaging>pom</packaging>\n\t<name>node module for otter</name>\n\t<version>4.2.19-SNAPSHOT</version>\n\t<url>http://github.com/alibaba/otter</url>\n\t\n\t<modules>\n\t\t<module>common</module>\n\t\t<module>etl</module>\n\t\t<module>canal</module>\n\t\t<module>extend</module>\n\t\t<module>deployer</module>\n\t</modules>\n\t\n\t<dependencyManagement>\n\t\t<dependencies>\n\t\t\t<dependency>\n\t\t\t\t<groupId>de.schlichtherle</groupId>\n\t\t\t\t<artifactId>truezip</artifactId>\n\t\t\t\t<version>6.8.4</version>\n\t\t\t</dependency>\n\t\t\t<!-- jetty -->\n\t\t\t<dependency>\n\t\t\t  <groupId>org.eclipse.jetty</groupId>\n\t\t\t  <artifactId>jetty-servlet</artifactId>\n\t\t\t  <version>${jetty_verion}</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t  <groupId>org.eclipse.jetty</groupId>\n\t\t\t  <artifactId>jetty-xml</artifactId>\n\t\t\t  <version>${jetty_verion}</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t  <groupId>org.eclipse.jetty</groupId>\n\t\t\t  <artifactId>jetty-server</artifactId>\n\t\t\t  <version>${jetty_verion}</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t  <groupId>org.eclipse.jetty</groupId>\n\t\t\t  <artifactId>jetty-http</artifactId>\n\t\t\t  <version>${jetty_verion}</version>\n\t\t\t</dependency>\n\t\t</dependencies>\n\t</dependencyManagement>\n</project>\n"
  },
  {
    "path": "pom.xml",
    "content": "<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\">\n\t<modelVersion>4.0.0</modelVersion>\n\t<parent>\n\t    <groupId>org.sonatype.oss</groupId>\n\t    <artifactId>oss-parent</artifactId>\n\t    <version>7</version>\n\t</parent>\n\t<groupId>com.alibaba.otter</groupId>\n\t<artifactId>otter</artifactId>\n\t<packaging>pom</packaging>\n\t<name>otter</name>\n\t<version>4.2.19-SNAPSHOT</version>\n\t<inceptionYear>2011</inceptionYear>\n\t<url>http://github.com/alibaba/otter</url>\n\t<organization>\n\t\t<name>alibaba</name>\n\t\t<url>http://www.alibaba.com</url>\n\t</organization>\n\n\t<developers>\n\t\t<developer>\n\t\t\t<name>agapple</name>\n\t\t\t<url>http://agapple.iteye.com</url>\n\t\t\t<email>jianghang115@gmail.com</email>\n\t\t\t<timezone>8</timezone>\n\t\t</developer>\n\t\t<developer>\n\t\t\t<name>zavakid</name>\n\t\t\t<url>http://www.zavakid.com</url>\n\t\t\t<email>zava.kid@gmail.com</email>\n\t\t\t<timezone>8</timezone>\n\t\t</developer>\n\t\t<developer>\n\t\t\t<name>hatter</name>\n\t\t\t<url>http://hatter.me</url>\n\t\t\t<email>jht5945@gmail.com</email>\n\t\t\t<timezone>8</timezone>\n\t\t</developer>\n\t</developers>\n\n\t<licenses>\n\t\t<license>\n\t\t\t<name>Apache License, Version 2.0</name>\n\t\t\t<url>http://www.apache.org/licenses/LICENSE-2.0</url>\n\t\t</license>\n\t</licenses>\n\n\t<scm>\n    <url>git@github.com:alibaba/otter.git</url>\n    <connection>scm:git:git@github.com:alibaba/otter.git</connection>\n    <developerConnection>scm:git:git@github.com:alibaba/otter.git</developerConnection>\n\t</scm>\n\n\t<repositories>\n\t\t<repository>\n\t\t\t<id>central</id>\n\t\t\t<url>https://repo1.maven.org/maven2</url>\n\t\t\t<releases>\n\t\t\t\t<enabled>true</enabled>\n\t\t\t</releases>\n\t\t\t<snapshots>\n\t\t\t\t<enabled>false</enabled>\n\t\t\t</snapshots>\n\t\t</repository>\n\t\t<!--<repository>\n\t\t\t<id>java.net</id>\n\t\t\t<url>http://download.java.net/maven/2/</url>\n\t\t\t<releases>\n\t\t\t\t<enabled>true</enabled>\n\t\t\t</releases>\n\t\t\t<snapshots>\n\t\t\t\t<enabled>false</enabled>\n\t\t\t</snapshots>\n\t\t</repository>\n\t\t<repository>\n\t\t\t<id>jtester-maven</id>\n\t\t\t<name>JTester</name>\n\t\t\t<url>http://jtester.googlecode.com/svn/m2/</url>\n\t\t\t<releases>\n\t\t\t\t<enabled>true</enabled>\n\t\t\t</releases>\n\t\t\t<snapshots>\n\t\t\t\t<enabled>false</enabled>\n\t\t\t</snapshots>\n\t\t</repository>-->\n\t\t<repository>\n\t\t\t<id>sonatype</id>\n\t\t\t<name>sonatype</name>\n\t\t\t<url>https://oss.sonatype.org/content/repositories/snapshots</url>\n\t\t\t<releases>\n\t\t\t\t<enabled>false</enabled>\n\t\t\t</releases>\n\t\t\t<snapshots>\n\t\t\t\t<enabled>true</enabled>\n\t\t\t</snapshots>\n\t\t</repository>\n\t\t<repository>\n\t\t\t<id>sonatype-release</id>\n\t\t\t<name>sonatype-release</name>\n\t\t\t<url>https://oss.sonatype.org/service/local/repositories/releases/content</url>\n\t\t\t<releases>\n\t\t\t\t<enabled>false</enabled>\n\t\t\t</releases>\n\t\t\t<snapshots>\n\t\t\t\t<enabled>true</enabled>\n\t\t\t</snapshots>\n\t\t</repository>\n\t</repositories>\n\n\t<properties>\n\t\t<!--maven properties-->\n        <maven.test.skip>true</maven.test.skip>\n        <downloadSources>true</downloadSources>\n        <!-- compiler settings properties -->\n        <java_source_version>1.6</java_source_version>\n        <java_target_version>1.6</java_target_version>\n\t\t<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n\t\t<file_encoding>UTF-8</file_encoding>\n\t\t<spring-version>3.1.2.RELEASE</spring-version>\n\t\t<jetty_verion>8.1.7.v20120910</jetty_verion>\n\t\t<logback_version>1.2.9</logback_version>\n\t\t<slf4j_version>1.7.12</slf4j_version>\n\t\t<otter_canal_version>1.1.5</otter_canal_version>\n\t</properties>\n\n\t<modules>\n\t\t<module>shared</module>\n\t\t<module>manager</module>\n\t\t<module>node</module>\n\t</modules>\n\n\t<dependencyManagement>\n\t\t<dependencies>\n\t\t\t<!-- commons -->\n            <dependency>\n\t\t\t\t<groupId>commons-dbcp</groupId>\n\t\t\t\t<artifactId>commons-dbcp</artifactId>\n\t\t\t\t<version>1.4</version>\n\t\t\t</dependency>\n            <dependency>\n\t\t\t\t<groupId>commons-lang</groupId>\n\t\t\t\t<artifactId>commons-lang</artifactId>\n\t\t\t\t<version>2.6</version>\n\t\t\t</dependency>\n            <dependency>\n\t\t\t\t<groupId>commons-pool</groupId>\n\t\t\t\t<artifactId>commons-pool</artifactId>\n\t\t\t\t<version>1.5.4</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>commons-codec</groupId>\n\t\t\t\t<artifactId>commons-codec</artifactId>\n\t\t\t\t<version>1.15</version>\n\t\t\t</dependency>\n            <dependency>\n                <groupId>org.apache.commons</groupId>\n                <artifactId>commons-compress</artifactId>\n                <version>1.21</version>\n            </dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>commons-io</groupId>\n\t\t\t\t<artifactId>commons-io</artifactId>\n\t\t\t\t<version>2.7</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>commons-beanutils</groupId>\n\t\t\t\t<artifactId>commons-beanutils</artifactId>\n\t\t\t\t<version>1.9.4</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>commons-collections</groupId>\n\t\t\t\t<artifactId>commons-collections</artifactId>\n\t\t\t\t<version>3.2.2</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>com.alibaba</groupId>\n\t\t\t\t<artifactId>fastjson</artifactId>\n\t\t\t\t<version>1.2.70</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>com.google.guava</groupId>\n\t\t\t\t<artifactId>guava</artifactId>\n\t\t\t\t<version>18.0</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>com.google.protobuf</groupId>\n\t\t\t\t<artifactId>protobuf-java</artifactId>\n\t\t\t\t<version>3.16.1</version>\n\t\t\t</dependency>\n            <dependency>\n                <groupId>com.alibaba</groupId>\n                <artifactId>druid</artifactId>\n                <version>1.2.6</version>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.fastsql</groupId>\n                <artifactId>fastsql</artifactId>\n                <version>2.0.0_preview_973</version>\n            </dependency>\n\t\t\t<!-- zookeeper -->\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.apache.zookeeper</groupId>\n\t\t\t\t<artifactId>zookeeper</artifactId>\n\t\t\t\t<version>3.4.14</version>\n\t\t\t\t<exclusions>\n\t\t\t\t  <exclusion>\n\t\t\t\t\t\t<groupId>org.slf4j</groupId>\n\t\t\t\t\t\t<artifactId>slf4j-log4j12</artifactId>\n\t\t\t\t  </exclusion>\n\t\t\t\t  <exclusion>\n\t\t\t\t\t\t<groupId>org.slf4j</groupId>\n\t\t\t\t\t\t<artifactId>slf4j-api</artifactId>\n\t\t\t\t  </exclusion>\n\t\t\t\t  <exclusion>\n\t\t\t\t\t\t<groupId>log4j</groupId>\n\t\t\t\t\t\t<artifactId>log4j</artifactId>\n\t\t\t\t  </exclusion>\n\t\t\t\t  <exclusion>\n\t\t\t\t\t\t<groupId>jline</groupId>\n\t\t\t\t\t\t<artifactId>jline</artifactId>\n\t\t\t\t  </exclusion>\n\t\t\t\t</exclusions>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>com.101tec</groupId>\n\t\t\t\t<artifactId>zkclient</artifactId>\n\t\t\t\t<version>0.10</version>\n\t\t\t</dependency>\n\t\t\t<!-- mysql -->\n\t\t\t<dependency>\n\t\t\t\t<groupId>mysql</groupId>\n\t\t\t\t<artifactId>mysql-connector-java</artifactId>\n\t\t\t\t<version>5.1.47</version>\n\t\t\t</dependency>\n            <!-- oracle -->\n\t\t\t<dependency>\n\t\t\t\t<groupId>com.oracle</groupId>\n\t\t\t\t<artifactId>ojdbc6</artifactId>\n\t\t\t\t<version>11.1.0.7.0</version>\n\t\t\t</dependency>\n\t\t\t<!-- spring -->\n\t\t\t<dependency>\n                <groupId>org.springframework</groupId>\n                <artifactId>spring-core</artifactId>\n                <version>${spring-version}</version>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework</groupId>\n                <artifactId>spring-beans</artifactId>\n                <version>${spring-version}</version>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework</groupId>\n                <artifactId>spring-aop</artifactId>\n                <version>${spring-version}</version>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework</groupId>\n                <artifactId>spring-context</artifactId>\n                <version>${spring-version}</version>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework</groupId>\n                <artifactId>spring-context-support</artifactId>\n                <version>${spring-version}</version>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework</groupId>\n                <artifactId>spring-tx</artifactId>\n                <version>${spring-version}</version>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework</groupId>\n                <artifactId>spring-jdbc</artifactId>\n                <version>${spring-version}</version>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework</groupId>\n                <artifactId>spring-test</artifactId>\n                <version>${spring-version}</version>\n            </dependency>\n            <dependency>\n\t\t\t\t<groupId>cglib</groupId>\n\t\t\t\t<artifactId>cglib-nodep</artifactId>\n\t\t\t\t<version>2.2</version>\n\t\t\t</dependency>\n\t\t\t<!-- log -->\n            <dependency>\n                <groupId>ch.qos.logback</groupId>\n                <artifactId>logback-core</artifactId>\n                <version>${logback_version}</version>\n            </dependency>\n            <dependency>\n                <groupId>ch.qos.logback</groupId>\n                <artifactId>logback-classic</artifactId>\n                <version>${logback_version}</version>\n            </dependency>\n            <dependency>\n                <groupId>org.slf4j</groupId>\n                <artifactId>jcl-over-slf4j</artifactId>\n                <version>${slf4j_version}</version>\n            </dependency>\n            <dependency>\n                <groupId>org.slf4j</groupId>\n                <artifactId>slf4j-api</artifactId>\n                <version>${slf4j_version}</version>\n            </dependency>\n            <dependency>\n                <groupId>javax.servlet</groupId>\n                <artifactId>javax.servlet-api</artifactId>\n                <version>3.0.1</version>\n                <scope>provided</scope>\n            </dependency>\n\t\t\t<!-- test dependency -->\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.jtester</groupId>\n\t\t\t\t<artifactId>jtester</artifactId>\n\t\t\t\t<version>1.1.8</version>\n\t\t\t\t<scope>test</scope>\n\t\t\t</dependency>\n            <dependency>\n\t\t\t\t<groupId>junit</groupId>\n\t\t\t\t<artifactId>junit</artifactId>\n\t\t\t\t<version>4.13.1</version>\n\t\t\t\t<scope>test</scope>\n\t\t\t</dependency>\n\t\t\t<dependency>\n                <groupId>com.google.code.findbugs</groupId>\n                <artifactId>jsr305</artifactId>\n                <version>3.0.2</version>\n            </dependency>\n\t\t</dependencies>\n\t</dependencyManagement>\n\n\t<build>\n\t\t<plugins>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.apache.maven.plugins</groupId>\n\t\t\t\t<artifactId>maven-source-plugin</artifactId>\n\t\t\t\t<executions>\n\t\t\t\t\t<execution>\n\t\t\t\t\t\t<id>attach-sources</id>\n\t\t\t\t\t\t<goals>\n\t\t\t\t\t\t\t<goal>jar</goal>\n\t\t\t\t\t\t</goals>\n\t\t\t\t\t</execution>\n\t\t\t\t</executions>\n\t\t\t</plugin>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.apache.maven.plugins</groupId>\n\t\t\t\t<artifactId>maven-compiler-plugin</artifactId>\n\t\t\t\t<configuration>\n\t\t\t\t\t<source>${java_source_version}</source>\n\t\t\t\t\t<target>${java_target_version}</target>\n\t\t\t\t\t<encoding>${file_encoding}</encoding>\n\t\t\t\t</configuration>\n\t\t\t</plugin>\n\t\t\t<plugin>\n\t\t\t   <groupId>org.apache.maven.plugins</groupId>\n\t\t\t   <artifactId>maven-eclipse-plugin</artifactId>\n\t\t\t   <version>2.5.1</version>\n\t\t\t   <configuration>\n\t\t\t\t  <additionalConfig>\n\t\t\t\t\t <file>\n\t\t\t\t\t\t<name>.settings/org.eclipse.core.resources.prefs</name>\n\t\t\t\t\t\t<content>\n\t\t\t\t\t\t   <![CDATA[eclipse.preferences.version=1${line.separator}encoding/<project>=${file_encoding}${line.separator}]]>\n\t\t\t\t\t\t</content>\n\t\t\t\t\t </file>\n\t\t\t\t  </additionalConfig>\n\t\t\t   </configuration>\n\t\t\t</plugin>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.apache.maven.plugins</groupId>\n\t\t\t\t<artifactId>maven-jar-plugin</artifactId>\n\t\t\t\t<configuration>\n\t\t\t\t\t<excludes>\n\t\t\t\t\t\t<exclude>**/.svn/**</exclude>\n\t\t\t\t\t</excludes>\n\t\t\t\t</configuration>\n\t\t\t</plugin>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.apache.maven.plugins</groupId>\n\t\t\t\t<artifactId>maven-surefire-plugin</artifactId>\n\t\t\t\t<version>2.5</version>\n\t\t\t\t<configuration>\n\t\t\t\t\t<testNGArtifactName>org.testng:testng</testNGArtifactName>\n\t\t\t\t\t<argLine>-javaagent:\"${settings.localRepository}/mockit/jmockit/0.999.10/jmockit-0.999.10.jar\"</argLine>\n\t\t\t\t\t<useSystemClassLoader>true</useSystemClassLoader>\n\t\t\t\t\t<suiteXmlFiles>\n\t\t\t\t\t</suiteXmlFiles>\n\t\t\t\t</configuration>\n\t\t\t</plugin>\n\t\t\t<plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-javadoc-plugin</artifactId>\n                <version>2.9.1</version>\n                <executions>\n                    <execution>\n                            <id>attach-javadocs</id>\n                            <goals>\n                                <goal>jar</goal>\n                            </goals>\n                        </execution>\n                    </executions>\n                <configuration>\n                  <encoding>${file_encoding}</encoding>\n                  <charset>${file_encoding}</charset>\n                  <additionalparam>-Xdoclint:none</additionalparam>\n                </configuration>\n            </plugin>\n\t\t</plugins>\n\n\t\t<sourceDirectory>src/main/java</sourceDirectory>\n        <testSourceDirectory>src/test/java</testSourceDirectory>\n        <resources>\n            <resource>\n                <directory>src/main/resources</directory>\n                <includes>\n                    <include>**/*</include>\n                </includes>\n                <excludes>\n                    <exclude>**/.svn/</exclude>\n                </excludes>\n            </resource>\n        </resources>\n        <testResources>\n            <testResource>\n                <directory>src/test/resources</directory>\n                <includes>\n                    <include>**/*</include>\n                </includes>\n                <excludes>\n                    <exclude>**/.svn/</exclude>\n                </excludes>\n            </testResource>\n        </testResources>\n\t</build>\n\n\t<distributionManagement>\n\t\t<snapshotRepository>\n\t\t\t<id>sonatype-nexus-snapshots</id>\n\t\t\t<name>Sonatype Nexus Snapshots</name>\n\t\t\t<url>https://oss.sonatype.org/content/repositories/snapshots/</url>\n\t\t</snapshotRepository>\n\t\t<repository>\n\t\t\t<id>sonatype-nexus-staging</id>\n\t\t\t<name>Nexus Release Repository</name>\n\t\t\t<url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>\n\t\t</repository>\n\t</distributionManagement>\n</project>\n"
  },
  {
    "path": "shared/arbitrate/pom.xml",
    "content": "<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\">\n\t<modelVersion>4.0.0</modelVersion>\n\t<parent>\n\t\t<groupId>com.alibaba.otter</groupId>\n\t\t<artifactId>shared</artifactId>\n\t\t<version>4.2.19-SNAPSHOT</version>\n\t\t<relativePath>../pom.xml</relativePath>\n\t</parent>\n\t<groupId>com.alibaba.otter</groupId>\n\t<artifactId>shared.arbitrate</artifactId>\n\t<packaging>jar</packaging>\n\t<name>arbitrate module for otter</name>\n\t<url>http://github.com/alibaba/otter</url>\n\n\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>com.alibaba.otter</groupId>\n\t\t\t<artifactId>shared.common</artifactId>\n\t\t\t<version>${project.version}</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>com.alibaba.otter</groupId>\n\t\t\t<artifactId>shared.communication</artifactId>\n\t\t\t<version>${project.version}</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.apache.zookeeper</groupId>\n\t\t\t<artifactId>zookeeper</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>commons-lang</groupId>\n\t\t\t<artifactId>commons-lang</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>com.alibaba</groupId>\n\t\t\t<artifactId>fastjson</artifactId>\n\t\t</dependency>\n\t\t\n\t\t<!-- test dependency -->\n\t\t<dependency>\n\t\t\t<groupId>junit</groupId>\n\t\t\t<artifactId>junit</artifactId>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.jtester</groupId>\n\t\t\t<artifactId>jtester</artifactId>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\t</dependencies>\n</project>\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/ArbitrateEventService.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate;\n\nimport com.alibaba.otter.shared.arbitrate.impl.setl.ExtractArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.LoadArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.MainStemArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.SelectArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.TerminArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.ToolArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.TransformArbitrateEvent;\n\n/**\n * 仲裁器事件处理的service，使用者可关注相应的await/single两个方法\n * \n * @author jianghang 2011-8-9 下午04:39:49\n */\npublic interface ArbitrateEventService {\n\n    public MainStemArbitrateEvent mainStemEvent();\n\n    public SelectArbitrateEvent selectEvent();\n\n    public ExtractArbitrateEvent extractEvent();\n\n    public TransformArbitrateEvent transformEvent();\n\n    public LoadArbitrateEvent loadEvent();\n\n    public TerminArbitrateEvent terminEvent();\n\n    public ToolArbitrateEvent toolEvent();\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/ArbitrateManageService.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate;\n\nimport com.alibaba.otter.shared.arbitrate.impl.manage.ChannelArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.manage.NodeArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.manage.PipelineArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.manage.SystemArbitrateEvent;\n\n/**\n * 仲裁器管理服务，提供给console进行干预仲裁器的行为：比如开始/停止channel同步\n * \n * @author jianghang 2011-8-9 下午04:40:36\n */\npublic interface ArbitrateManageService {\n\n    public SystemArbitrateEvent systemEvent();\n\n    public NodeArbitrateEvent nodeEvent();\n\n    public PipelineArbitrateEvent pipelineEvent();\n\n    public ChannelArbitrateEvent channelEvent();\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/ArbitrateViewService.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate;\n\nimport java.util.List;\n\nimport com.alibaba.otter.shared.arbitrate.model.MainStemEventData;\nimport com.alibaba.otter.shared.arbitrate.model.PositionEventData;\nimport com.alibaba.otter.shared.common.model.statistics.stage.ProcessStat;\n\n/**\n * 仲裁器状态视图服务,允许查看当前的一些process/termin状态信息\n * \n * @author jianghang 2011-9-27 下午05:20:42\n * @version 4.0.0\n */\npublic interface ArbitrateViewService {\n\n    /**\n     * 查询当前的mainstem工作信息\n     */\n    MainStemEventData mainstemData(Long channelId, Long pipelineId);\n\n    /**\n     * 查询当前的process列表\n     */\n    List<ProcessStat> listProcesses(Long channelId, Long pipelineId);\n\n    /**\n     * 查询下一个processId\n     */\n    Long getNextProcessId(Long channelId, Long pipelineId);\n\n    /**\n     * 获取canal cursor\n     */\n    PositionEventData getCanalCursor(String destination, short clientId);\n\n    /**\n     * 删除canal cursor\n     */\n    void removeCanalCursor(String destination, short clientId);\n\n    /**\n     * 删除canal cursor + filter\n     */\n    void removeCanal(String destination, short clientId);\n\n    /**\n     * 删除canal meta信息\n     */\n    void removeCanal(String destination);\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/exception/ArbitrateException.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.exception;\n\nimport org.apache.commons.lang.exception.NestableRuntimeException;\n\n/**\n * @author jianghang 2011-9-16 下午01:59:25\n * @version 4.0.0\n */\npublic class ArbitrateException extends NestableRuntimeException {\n\n    private static final long serialVersionUID = -7288830284122672209L;\n\n    public ArbitrateException(String errorCode){\n        super(errorCode);\n    }\n\n    public ArbitrateException(String errorCode, Throwable cause){\n        super(errorCode, cause);\n    }\n\n    public ArbitrateException(String errorCode, String errorDesc){\n        super(errorCode + \":\" + errorDesc);\n    }\n\n    public ArbitrateException(String errorCode, String errorDesc, Throwable cause){\n        super(errorCode + \":\" + errorDesc, cause);\n    }\n\n    public ArbitrateException(Throwable cause){\n        super(cause);\n    }\n\n    public Throwable fillInStackTrace() {\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/ArbitrateConstants.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl;\n\n/**\n * 仲裁器相关常量定义\n * \n * @author jianghang\n */\npublic interface ArbitrateConstants {\n\n    /**\n     * otter的根节点\n     */\n    public String NODE_OTTER_ROOT         = \"/otter\";\n\n    /**\n     * otter的node机器的根节点\n     */\n    public String NODE_NID_ROOT           = NODE_OTTER_ROOT + \"/node\";\n\n    /**\n     * otter中node节点的format格式,接受nid做为参数\n     */\n    public String NODE_NID_FORMAT         = NODE_NID_ROOT + \"/{0}\";\n\n    /**\n     * otter的channel的根节点\n     */\n    public String NODE_CHANNEL_ROOT       = NODE_OTTER_ROOT + \"/channel\";\n\n    /**\n     * otter中channel节点的format格式,接受channelId做为参数\n     */\n    public String NODE_CHANNEL_FORMAT     = NODE_CHANNEL_ROOT + \"/{0}\";\n\n    /**\n     * otter中pipeline节点的format格式,接受channelId,pipelineId做为参数\n     */\n    public String NODE_PIPELINE_FORMAT    = NODE_CHANNEL_FORMAT + \"/{1}\";\n\n    /**\n     * otter的remedy的根节点\n     */\n    public String NODE_REMEDY_ROOT        = NODE_PIPELINE_FORMAT + \"/remedy\";\n\n    /**\n     * otter的process的根节点\n     */\n    public String NODE_PROCESS_ROOT       = NODE_PIPELINE_FORMAT + \"/process\";\n\n    /**\n     * otter中process节点的format格式,接受channelId,pipelineId,processId做为参数\n     */\n    public String NODE_PROCESS_FORMAT     = NODE_PROCESS_ROOT + \"/{2}\";\n\n    /**\n     * otter的termin信号的根节点\n     */\n    public String NODE_TERMIN_ROOT        = NODE_PIPELINE_FORMAT + \"/termin\";\n\n    /**\n     * otter中termin节点的format格式,接受channelId,pipelineId,processId做为参数\n     */\n    public String NODE_TERMIN_FORMAT      = NODE_TERMIN_ROOT + \"/{2}\";\n\n    /**\n     * otter的lock根节点\n     */\n    public String NODE_LOCK_ROOT          = NODE_PIPELINE_FORMAT + \"/lock\";\n\n    /**\n     * otter的load的lock节点\n     */\n    public String NODE_LOCK_LOAD          = \"load\";\n\n    /**\n     * 主导线程的状态节点,为pipeline的子节点\n     */\n    public String NODE_MAINSTEM           = \"mainstem\";\n\n    /**\n     * select完成状态的节点,为process的子节点\n     */\n    public String NODE_SELECTED           = \"selected\";\n\n    /**\n     * extract完成状态的节点,为process的子节点\n     */\n    public String NODE_EXTRACTED          = \"extracted\";\n\n    /**\n     * transform完成状态的节点,为process的子节点\n     */\n    public String NODE_TRANSFORMED        = \"transformed\";\n\n    /**\n     * load完成状态的节点,为process的子节点\n     */\n    public String NODE_LOADED             = \"loaded\";\n\n    /**\n     * 在logback的配置文件中定义好的按照各个pipeline进行日志文件输出的键值.\n     */\n    public String splitPipelineLogFileKey = \"otter\";\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/ArbitrateEvent.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl;\n\n/**\n * 具体的仲裁行为接口\n * \n * @author jianghang 2011-8-9 下午04:39:20\n */\npublic interface ArbitrateEvent {\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/ArbitrateEventServiceImpl.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl;\n\nimport com.alibaba.otter.shared.arbitrate.ArbitrateEventService;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.ExtractArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.LoadArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.MainStemArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.SelectArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.TerminArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.ToolArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.TransformArbitrateEvent;\n\n/**\n * 仲裁器事件service的默认实现\n * \n * @author jianghang 2011-9-22 下午04:04:00\n * @version 4.0.0\n */\npublic class ArbitrateEventServiceImpl implements ArbitrateEventService {\n\n    private MainStemArbitrateEvent  mainStemEvent;\n    private SelectArbitrateEvent    selectEvent;\n    private ExtractArbitrateEvent   extractEvent;\n    private TransformArbitrateEvent transformEvent;\n    private LoadArbitrateEvent      loadEvent;\n    private TerminArbitrateEvent    terminEvent;\n    private ToolArbitrateEvent      toolEvent;\n\n    public MainStemArbitrateEvent mainStemEvent() {\n        return mainStemEvent;\n    }\n\n    public SelectArbitrateEvent selectEvent() {\n        return selectEvent;\n    }\n\n    public ExtractArbitrateEvent extractEvent() {\n        return extractEvent;\n    }\n\n    public TransformArbitrateEvent transformEvent() {\n        return transformEvent;\n    }\n\n    public LoadArbitrateEvent loadEvent() {\n        return loadEvent;\n    }\n\n    public TerminArbitrateEvent terminEvent() {\n        return terminEvent;\n    }\n\n    public ToolArbitrateEvent toolEvent() {\n        return toolEvent;\n    }\n\n    // ================ setter / getter ====================\n\n    public void setTransformEvent(TransformArbitrateEvent transformEvent) {\n        this.transformEvent = transformEvent;\n    }\n\n    public void setMainStemEvent(MainStemArbitrateEvent mainStemEvent) {\n        this.mainStemEvent = mainStemEvent;\n    }\n\n    public void setSelectEvent(SelectArbitrateEvent selectEvent) {\n        this.selectEvent = selectEvent;\n    }\n\n    public void setExtractEvent(ExtractArbitrateEvent extractEvent) {\n        this.extractEvent = extractEvent;\n    }\n\n    public void setLoadEvent(LoadArbitrateEvent loadEvent) {\n        this.loadEvent = loadEvent;\n    }\n\n    public void setTerminEvent(TerminArbitrateEvent terminEvent) {\n        this.terminEvent = terminEvent;\n    }\n\n    public void setToolEvent(ToolArbitrateEvent toolEvent) {\n        this.toolEvent = toolEvent;\n    }\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/ArbitrateManageServiceImpl.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl;\n\nimport com.alibaba.otter.shared.arbitrate.ArbitrateManageService;\nimport com.alibaba.otter.shared.arbitrate.impl.manage.ChannelArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.manage.NodeArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.manage.PipelineArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.manage.SystemArbitrateEvent;\n\n/**\n * manager的管理信号\n * \n * @author jianghang 2011-9-26 下午07:03:35\n * @version 4.0.0\n */\npublic class ArbitrateManageServiceImpl implements ArbitrateManageService {\n\n    private SystemArbitrateEvent   systemEvent;\n    private ChannelArbitrateEvent  channelEvent;\n    private NodeArbitrateEvent     nodeEvent;\n    private PipelineArbitrateEvent pipelineEvent;\n\n    public SystemArbitrateEvent systemEvent() {\n        return systemEvent;\n    }\n\n    public ChannelArbitrateEvent channelEvent() {\n        return channelEvent;\n    }\n\n    public NodeArbitrateEvent nodeEvent() {\n        return nodeEvent;\n    }\n\n    public PipelineArbitrateEvent pipelineEvent() {\n        return pipelineEvent;\n    }\n\n    // ===================== setter / getter ===================\n\n    public void setChannelEvent(ChannelArbitrateEvent channelEvent) {\n        this.channelEvent = channelEvent;\n    }\n\n    public void setNodeEvent(NodeArbitrateEvent nodeEvent) {\n        this.nodeEvent = nodeEvent;\n    }\n\n    public void setPipelineEvent(PipelineArbitrateEvent pipelineEvent) {\n        this.pipelineEvent = pipelineEvent;\n    }\n\n    public void setSystemEvent(SystemArbitrateEvent systemEvent) {\n        this.systemEvent = systemEvent;\n    }\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/ArbitrateViewServiceImpl.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl;\n\nimport java.io.UnsupportedEncodingException;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.I0Itec.zkclient.IZkConnection;\nimport org.I0Itec.zkclient.exception.ZkException;\nimport org.apache.commons.lang.StringUtils;\nimport org.apache.zookeeper.KeeperException;\nimport org.apache.zookeeper.KeeperException.NoNodeException;\nimport org.apache.zookeeper.ZooKeeper;\nimport org.apache.zookeeper.data.Stat;\nimport org.springframework.util.CollectionUtils;\n\nimport com.alibaba.otter.shared.arbitrate.ArbitrateViewService;\nimport com.alibaba.otter.shared.arbitrate.exception.ArbitrateException;\nimport com.alibaba.otter.shared.arbitrate.impl.manage.helper.ManagePathUtils;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.helper.StageComparator;\nimport com.alibaba.otter.shared.arbitrate.impl.zookeeper.ZooKeeperClient;\nimport com.alibaba.otter.shared.arbitrate.model.EtlEventData;\nimport com.alibaba.otter.shared.arbitrate.model.MainStemEventData;\nimport com.alibaba.otter.shared.arbitrate.model.PositionEventData;\nimport com.alibaba.otter.shared.arbitrate.model.ProcessNodeEventData;\nimport com.alibaba.otter.shared.common.model.config.enums.StageType;\nimport com.alibaba.otter.shared.common.model.statistics.stage.ProcessStat;\nimport com.alibaba.otter.shared.common.model.statistics.stage.StageStat;\nimport com.alibaba.otter.shared.common.utils.JsonUtils;\nimport com.alibaba.otter.shared.common.utils.zookeeper.ZkClientx;\nimport com.alibaba.otter.shared.common.utils.zookeeper.ZooKeeperx;\n\n/**\n * 查询当前的仲裁器的一些运行状态视图\n * \n * @author jianghang 2011-9-27 下午05:27:38\n * @version 4.0.0\n */\npublic class ArbitrateViewServiceImpl implements ArbitrateViewService {\n\n    private static final String CANAL_PATH        = \"/otter/canal/destinations/%s\";\n    private static final String CANAL_DATA_PATH   = CANAL_PATH + \"/%s\";\n    private static final String CANAL_CURSOR_PATH = CANAL_PATH + \"/%s/cursor\";\n    private ZkClientx           zookeeper         = ZooKeeperClient.getInstance();\n\n    public MainStemEventData mainstemData(Long channelId, Long pipelineId) {\n        String path = ManagePathUtils.getMainStem(channelId, pipelineId);\n        try {\n            byte[] bytes = zookeeper.readData(path);\n            return JsonUtils.unmarshalFromByte(bytes, MainStemEventData.class);\n        } catch (ZkException e) {\n            return null;\n        }\n    }\n\n    public Long getNextProcessId(Long channelId, Long pipelineId) {\n        String processRoot = ManagePathUtils.getProcessRoot(channelId, pipelineId);\n        IZkConnection connection = zookeeper.getConnection();\n        // zkclient会将获取stat信息和正常的操作分开，使用原生的zk进行优化\n        ZooKeeper orginZk = ((ZooKeeperx) connection).getZookeeper();\n\n        Stat processParentStat = new Stat();\n        // 获取所有的process列表\n        try {\n            orginZk.getChildren(processRoot, false, processParentStat);\n            return (Long) ((processParentStat.getCversion() + processParentStat.getNumChildren()) / 2L);\n        } catch (Exception e) {\n            return -1L;\n        }\n    }\n\n    public List<ProcessStat> listProcesses(Long channelId, Long pipelineId) {\n        List<ProcessStat> processStats = new ArrayList<ProcessStat>();\n        String processRoot = ManagePathUtils.getProcessRoot(channelId, pipelineId);\n        IZkConnection connection = zookeeper.getConnection();\n        // zkclient会将获取stat信息和正常的操作分开，使用原生的zk进行优化\n        ZooKeeper orginZk = ((ZooKeeperx) connection).getZookeeper();\n\n        // 获取所有的process列表\n        List<String> processNodes = zookeeper.getChildren(processRoot);\n        List<Long> processIds = new ArrayList<Long>();\n        for (String processNode : processNodes) {\n            processIds.add(ManagePathUtils.getProcessId(processNode));\n        }\n\n        Collections.sort(processIds);\n\n        for (int i = 0; i < processIds.size(); i++) {\n            Long processId = processIds.get(i);\n            // 当前的process可能会有变化\n            ProcessStat processStat = new ProcessStat();\n            processStat.setPipelineId(pipelineId);\n            processStat.setProcessId(processId);\n\n            List<StageStat> stageStats = new ArrayList<StageStat>();\n            processStat.setStageStats(stageStats);\n            try {\n                String processPath = ManagePathUtils.getProcess(channelId, pipelineId, processId);\n                Stat zkProcessStat = new Stat();\n                List<String> stages = orginZk.getChildren(processPath, false, zkProcessStat);\n                Collections.sort(stages, new StageComparator());\n\n                StageStat prev = null;\n                for (String stage : stages) {// 循环每个process下的stage\n                    String stagePath = processPath + \"/\" + stage;\n                    Stat zkStat = new Stat();\n\n                    StageStat stageStat = new StageStat();\n                    stageStat.setPipelineId(pipelineId);\n                    stageStat.setProcessId(processId);\n\n                    byte[] bytes = orginZk.getData(stagePath, false, zkStat);\n                    if (bytes != null && bytes.length > 0) {\n                        // 特殊处理zookeeper里的data信息，manager没有对应node中PipeKey的对象，所以导致反序列化会失败，需要特殊处理，删除'@'符号\n                        String json = StringUtils.remove(new String(bytes, \"UTF-8\"), '@');\n                        EtlEventData data = JsonUtils.unmarshalFromString(json, EtlEventData.class);\n                        stageStat.setNumber(data.getNumber());\n                        stageStat.setSize(data.getSize());\n\n                        Map exts = new HashMap();\n                        if (!CollectionUtils.isEmpty(data.getExts())) {\n                            exts.putAll(data.getExts());\n                        }\n                        exts.put(\"currNid\", data.getCurrNid());\n                        exts.put(\"nextNid\", data.getNextNid());\n                        exts.put(\"desc\", data.getDesc());\n                        stageStat.setExts(exts);\n                    }\n                    if (prev != null) {// 对应的start时间为上一个节点的结束时间\n                        stageStat.setStartTime(prev.getEndTime());\n                    } else {\n                        stageStat.setStartTime(zkProcessStat.getMtime()); // process的最后修改时间,select\n                                                                          // await成功后会设置USED标志位\n                    }\n                    stageStat.setEndTime(zkStat.getMtime());\n                    if (ArbitrateConstants.NODE_SELECTED.equals(stage)) {\n                        stageStat.setStage(StageType.SELECT);\n                    } else if (ArbitrateConstants.NODE_EXTRACTED.equals(stage)) {\n                        stageStat.setStage(StageType.EXTRACT);\n                    } else if (ArbitrateConstants.NODE_TRANSFORMED.equals(stage)) {\n                        stageStat.setStage(StageType.TRANSFORM);\n                        // } else if\n                        // (ArbitrateConstants.NODE_LOADED.equals(stage)) {\n                        // stageStat.setStage(StageType.LOAD);\n                    }\n\n                    prev = stageStat;\n                    stageStats.add(stageStat);\n                }\n\n                // 添加一个当前正在处理的\n                StageStat currentStageStat = new StageStat();\n                currentStageStat.setPipelineId(pipelineId);\n                currentStageStat.setProcessId(processId);\n                if (prev == null) {\n                    byte[] bytes = orginZk.getData(processPath, false, zkProcessStat);\n                    if (bytes == null || bytes.length == 0) {\n                        continue; // 直接认为未使用，忽略之\n                    }\n\n                    ProcessNodeEventData nodeData = JsonUtils.unmarshalFromByte(bytes, ProcessNodeEventData.class);\n                    if (nodeData.getStatus().isUnUsed()) {// process未使用,直接忽略\n                        continue; // 跳过该process\n                    } else {\n                        currentStageStat.setStage(StageType.SELECT);// select操作\n                        currentStageStat.setStartTime(zkProcessStat.getMtime());\n                    }\n                } else {\n                    // 判断上一个节点，确定当前的stage\n                    StageType stage = prev.getStage();\n                    if (stage.isSelect()) {\n                        currentStageStat.setStage(StageType.EXTRACT);\n                    } else if (stage.isExtract()) {\n                        currentStageStat.setStage(StageType.TRANSFORM);\n                    } else if (stage.isTransform()) {\n                        currentStageStat.setStage(StageType.LOAD);\n                    } else if (stage.isLoad()) {// 已经是最后一个节点了\n                        continue;\n                    }\n\n                    currentStageStat.setStartTime(prev.getEndTime());// 开始时间为上一个节点的结束时间\n                }\n\n                if (currentStageStat.getStage().isLoad()) {// load必须为第一个process节点\n                    if (i == 0) {\n                        stageStats.add(currentStageStat);\n                    }\n                } else {\n                    stageStats.add(currentStageStat);// 其他情况都添加\n                }\n\n            } catch (NoNodeException e) {\n                // ignore\n            } catch (KeeperException e) {\n                throw new ArbitrateException(e);\n            } catch (InterruptedException e) {\n                // ignore\n            } catch (UnsupportedEncodingException e) {\n                // ignore\n            }\n\n            processStats.add(processStat);\n        }\n\n        return processStats;\n    }\n\n    public PositionEventData getCanalCursor(String destination, short clientId) {\n        String path = String.format(CANAL_CURSOR_PATH, destination, String.valueOf(clientId));\n        try {\n            IZkConnection connection = zookeeper.getConnection();\n            // zkclient会将获取stat信息和正常的操作分开，使用原生的zk进行优化\n            ZooKeeper orginZk = ((ZooKeeperx) connection).getZookeeper();\n            Stat stat = new Stat();\n            byte[] bytes = orginZk.getData(path, false, stat);\n            PositionEventData eventData = new PositionEventData();\n            eventData.setCreateTime(new Date(stat.getCtime()));\n            eventData.setModifiedTime(new Date(stat.getMtime()));\n            eventData.setPosition(new String(bytes, \"UTF-8\"));\n            return eventData;\n        } catch (Exception e) {\n            return null;\n        }\n    }\n\n    public void removeCanalCursor(String destination, short clientId) {\n        String path = String.format(CANAL_CURSOR_PATH, destination, String.valueOf(clientId));\n        zookeeper.delete(path);\n    }\n\n    @Override\n    public void removeCanal(String destination, short clientId) {\n        String path = String.format(CANAL_DATA_PATH, destination, String.valueOf(clientId));\n        zookeeper.deleteRecursive(path);\n    }\n\n    public void removeCanal(String destination) {\n        String path = String.format(CANAL_PATH, destination);\n        zookeeper.deleteRecursive(path);\n    }\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/alarm/AlarmClientService.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.alarm;\n\nimport java.text.MessageFormat;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.alibaba.otter.shared.arbitrate.impl.communication.ArbitrateCommmunicationClient;\nimport com.alibaba.otter.shared.common.model.config.alarm.MonitorName;\nimport com.alibaba.otter.shared.communication.core.model.Callback;\nimport com.alibaba.otter.shared.communication.model.arbitrate.NodeAlarmEvent;\n\n/**\n * 发送报警信息给manager\n * \n * @author jianghang 2011-9-26 下午10:30:16\n * @version 4.0.0\n */\npublic class AlarmClientService {\n\n    private static final Logger           logger         = LoggerFactory.getLogger(AlarmClientService.class);\n    private static final String           MESSAGE_FORMAT = \"{0}:{1}\";\n    private ArbitrateCommmunicationClient arbitrateCommmunicationClient;\n\n    public void sendAlarm(Long currentNid, Long pipelineId, String title, String msg) {\n        final NodeAlarmEvent event = new NodeAlarmEvent();\n        event.setNid(currentNid);\n        event.setTitle(MonitorName.EXCEPTION.name());\n        event.setMessage(MessageFormat.format(MESSAGE_FORMAT, title, msg));\n        event.setPipelineId(pipelineId);\n        arbitrateCommmunicationClient.callManager(event, new Callback<Object>() {\n\n            public void call(Object result) {\n                logger.info(\"##callManager successed! event:[{}]\", event.toString());\n            }\n\n        });\n    }\n\n    // =============== setter / getter ==================\n\n    public void setArbitrateCommmunicationClient(ArbitrateCommmunicationClient arbitrateCommmunicationClient) {\n        this.arbitrateCommmunicationClient = arbitrateCommmunicationClient;\n    }\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/communication/ArbitrateCommmunicationClient.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.communication;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.apache.commons.lang.StringUtils;\nimport org.apache.commons.lang.math.RandomUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.alibaba.otter.shared.arbitrate.impl.config.ArbitrateConfigUtils;\nimport com.alibaba.otter.shared.common.model.config.node.Node;\nimport com.alibaba.otter.shared.communication.core.CommunicationClient;\nimport com.alibaba.otter.shared.communication.core.exception.CommunicationException;\nimport com.alibaba.otter.shared.communication.core.impl.DefaultCommunicationClientImpl;\nimport com.alibaba.otter.shared.communication.core.model.Callback;\nimport com.alibaba.otter.shared.communication.core.model.Event;\n\n/**\n * 封装了基于communication通讯的工具\n * \n * @author jianghang 2011-10-18 下午02:18:04\n * @version 4.0.0\n */\npublic class ArbitrateCommmunicationClient {\n\n    private static final Logger logger = LoggerFactory.getLogger(ArbitrateCommmunicationClient.class);\n    private CommunicationClient delegate;\n    private List<String>        managerAddress;\n    private volatile int        index  = 0;\n\n    /**\n     * 指定对应的Node节点，进行event调用\n     */\n    public Object call(Long nid, final Event event) {\n        return delegate.call(convertToAddress(nid), event);\n    }\n\n    /**\n     * 指定对应的Node节点，进行event调用\n     * \n     * <pre>\n     * 注意：该方法为异步调用\n     * </pre>\n     */\n    public void call(Long nid, Event event, final Callback callback) {\n        delegate.call(convertToAddress(nid), event, callback);\n    }\n\n    /**\n     * 指定manager，进行event调用\n     */\n    public Object callManager(final Event event) {\n        CommunicationException ex = null;\n        for (int i = index; i < index + managerAddress.size(); i++) {\n            String address = managerAddress.get(i % managerAddress.size());\n            try {\n                Object result = delegate.call(address, event);\n                index = i;\n                return result;\n            } catch (CommunicationException e) {\n                ex = e;\n                logger.warn(\"call manager [{}] event [{}] failed, maybe can try another manager.\", address, event);\n            }\n        }\n\n        throw ex;\n    }\n\n    /**\n     * 指定manager，进行event调用\n     * \n     * <pre>\n     * 注意：该方法为异步调用\n     * </pre>\n     */\n    public void callManager(final Event event, final Callback callback) {\n        if (delegate instanceof DefaultCommunicationClientImpl) {\n            DefaultCommunicationClientImpl defaultClient = (DefaultCommunicationClientImpl) delegate;\n            defaultClient.submit(new Runnable() {\n\n                @Override\n                public void run() {\n                    Object result = callManager(event);\n                    callback.call(result);\n                }\n            });\n        }\n    }\n\n    private String convertToAddress(Long nid) {\n        Node node = ArbitrateConfigUtils.findNode(nid);\n        if (node.getParameters().getUseExternalIp()) {\n            return node.getParameters().getExternalIp() + \":\" + node.getPort();\n        } else {\n            return node.getIp() + \":\" + node.getPort();\n        }\n    }\n\n    // ================== setter / getter =====================\n\n    public void setDelegate(CommunicationClient delegate) {\n        this.delegate = delegate;\n    }\n\n    public void setManagerAddress(String managerAddress) {\n        String server = StringUtils.replace(managerAddress, \";\", \",\");\n        String[] servers = StringUtils.split(server, ',');\n        this.managerAddress = Arrays.asList(servers);\n        this.index = RandomUtils.nextInt(this.managerAddress.size()); // 随机选择一台机器\n    }\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/config/ArbitrateConfig.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.config;\n\nimport com.alibaba.otter.shared.common.model.config.channel.Channel;\nimport com.alibaba.otter.shared.common.model.config.node.Node;\nimport com.alibaba.otter.shared.common.model.config.pipeline.Pipeline;\n\n/**\n * 仲裁器相关配置信息\n * \n * @author jianghang 2011-10-9 上午11:31:41\n * @version 4.0.0\n */\npublic interface ArbitrateConfig {\n\n    /**\n     * 获取当前节点信息\n     */\n    public Node currentNode();\n\n    /**\n     * 根据nid查询Node信息\n     */\n    public Node findNode(Long nid);\n\n    /**\n     * 根据channelId获取channel\n     */\n    public Channel findChannel(Long channelId);\n\n    /**\n     * 根据pipelineId获取pipeline\n     * \n     * @param pipelineId\n     * @return\n     */\n    public Pipeline findPipeline(Long pipelineId);\n\n    /**\n     * 根据pipelineId查询对应的Channel对象\n     * \n     * @param pipelineId\n     * @return\n     */\n    public Channel findChannelByPipelineId(Long pipelineId);\n\n    /**\n     * 根据pipelineId查询相对的Pipeline对象\n     * \n     * @param pipelineId\n     * @return\n     */\n    public Pipeline findOppositePipeline(Long pipelineId);\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/config/ArbitrateConfigRegistry.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.config;\n\n/**\n * 配置获取注册接口\n * \n * @author jianghang 2011-11-3 上午10:19:50\n * @version 4.0.0\n */\npublic class ArbitrateConfigRegistry {\n\n    private static ArbitrateConfig config;\n\n    public static void regist(ArbitrateConfig config) {\n        ArbitrateConfigRegistry.config = config;\n    }\n\n    public static void unRegist(ArbitrateConfig config) {\n        ArbitrateConfigRegistry.config = config;\n    }\n\n    public static ArbitrateConfig getConfig() {\n        return config;\n    }\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/config/ArbitrateConfigUtils.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.config;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.apache.commons.lang.StringUtils;\n\nimport com.alibaba.otter.shared.common.model.config.channel.Channel;\nimport com.alibaba.otter.shared.common.model.config.node.Node;\nimport com.alibaba.otter.shared.common.model.config.pipeline.Pipeline;\n\n/**\n * 配置操作聚合类，方便mock\n * \n * @author jianghang 2011-9-27 下午08:27:04\n * @version 4.0.0\n */\npublic class ArbitrateConfigUtils {\n\n    /**\n     * 获取当前节点的nid信息\n     */\n    public static Long getCurrentNid() {\n        Node node = ArbitrateConfigRegistry.getConfig().currentNode();\n        if (node != null) {\n            return node.getId();\n        } else {\n            return null;\n        }\n    }\n\n    /**\n     * 获取对应Node的zk集群列表配置\n     */\n    public static List<String> getServerAddrs() {\n        Node node = ArbitrateConfigRegistry.getConfig().currentNode();\n        if (node != null) {\n            String addr = StringUtils.join(node.getParameters().getZkCluster().getServerList(), ',');\n            return Arrays.asList(addr);\n        } else {\n            return new ArrayList<String>();\n        }\n    }\n\n    /**\n     * 获取task配置中定义的pipeline\n     */\n    public static Pipeline getPipeline(Long pipelineId) {\n        return ArbitrateConfigRegistry.getConfig().findPipeline(pipelineId);\n    }\n\n    /**\n     * 根据pipelineId获取task配置中定义的反向的pipeline\n     */\n    public static Pipeline getOppositePipeline(Long pipelineId) {\n        return ArbitrateConfigRegistry.getConfig().findOppositePipeline(pipelineId);\n    }\n\n    /**\n     * 根据pipelineId获取task配置中定义的channel\n     */\n    public static Channel getChannel(Long pipelineId) {\n        return ArbitrateConfigRegistry.getConfig().findChannelByPipelineId(pipelineId);\n    }\n\n    /**\n     * 根据channelId获取task配置中定义的channel\n     * \n     * @return\n     */\n    public static Channel getChannelByChannelId(Long channelId) {\n        return ArbitrateConfigRegistry.getConfig().findChannel(channelId);\n    }\n\n    /**\n     * 返回并行度\n     */\n    public static int getParallelism(Long pipelineId) {\n        return ArbitrateConfigRegistry.getConfig().findPipeline(pipelineId).getParameters().getParallelism().intValue();\n    }\n\n    /**\n     * 根据nid查询node信息\n     */\n    public static Node findNode(Long nid) {\n        return ArbitrateConfigRegistry.getConfig().findNode(nid);\n    }\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/interceptor/LogInterceptor.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.interceptor;\n\nimport java.text.MessageFormat;\nimport java.text.SimpleDateFormat;\nimport java.util.AbstractCollection;\nimport java.util.AbstractMap;\nimport java.util.Arrays;\nimport java.util.Date;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\n\nimport org.aopalliance.intercept.MethodInterceptor;\nimport org.aopalliance.intercept.MethodInvocation;\nimport org.apache.commons.lang.ClassUtils;\nimport org.apache.commons.lang.ObjectUtils;\nimport org.apache.commons.lang.StringUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * 通过拦截器记录一下调用event事件的相关参数和返回结果\n * \n * @author jianghang 2011-9-28 下午07:13:40\n * @version 4.0.0\n */\npublic class LogInterceptor implements MethodInterceptor {\n\n    private static final String DATA_FORMAT = \"yyyy-MM-dd HH:mm:ss.sss\";\n    private static String       MESSAGE     = \"\\n=======================================\\n[Class:{0} , Method:{1} , time:{2} , take:{3}ms]\\n{4}Result\\r\\t{5}\\n=======================================\";\n\n    public Object invoke(MethodInvocation methodInvocation) throws Throwable {\n        long startTime = System.currentTimeMillis();\n        Object result = null;\n        try {\n            result = methodInvocation.proceed();\n        } catch (Exception e) {\n            dump(methodInvocation, e, System.currentTimeMillis() - startTime);// 记录异常信息\n            throw e;\n        }\n        // 记录异常信息\n        dump(methodInvocation, result, System.currentTimeMillis() - startTime); // 记录正常结果信息\n        return result;\n    }\n\n    /**\n     * 取得对应的logger\n     * \n     * @param obj\n     */\n    protected Logger getLogger(Class obj) {\n        return LoggerFactory.getLogger(obj);\n    }\n\n    /**\n     * 记录请求信息\n     * \n     * @param methodInvocation\n     * @param take\n     */\n    private void dump(MethodInvocation methodInvocation, Object result, long take) {\n        // 取得日志打印对象\n        Logger log = getLogger(methodInvocation.getMethod().getDeclaringClass());\n        Object[] args = methodInvocation.getArguments();\n        StringBuffer buffer = getArgsString(args);\n\n        if (log.isInfoEnabled()) {\n            String className = ClassUtils.getShortClassName(methodInvocation.getMethod().getDeclaringClass());\n            String methodName = methodInvocation.getMethod().getName();\n            String resultStr = getResultString(result);\n\n            String now = new SimpleDateFormat(DATA_FORMAT).format(new Date());\n            log.info(MessageFormat.format(MESSAGE, new Object[] { className, methodName, now, take, buffer.toString(),\n                    resultStr }));\n        }\n    }\n\n    /**\n     * 取得结果字符串\n     * \n     * @param result\n     * @return\n     */\n    protected String getResultString(Object result) {\n        if (result == null) {\n            return StringUtils.EMPTY;\n        }\n\n        if (result instanceof Map) { // 处理map\n            return getMapResultString((Map) result);\n        } else if (result instanceof List) {// 处理list\n            return getListResultString((List) result);\n        } else if (result.getClass().isArray()) {// 处理array\n            return getArrayResultString((Object[]) result);\n        } else {\n            // 直接处理string\n            return ObjectUtils.toString(result, StringUtils.EMPTY).toString();\n            // return ToStringBuilder.reflectionToString(result, ToStringStyle.SIMPLE_STYLE);\n        }\n    }\n\n    /**\n     * 取得map的string，自定义的主要目的：针对value中数组数据的toString处理, copy from {@link AbstractMap}\n     * \n     * @param result\n     * @return\n     */\n    private String getMapResultString(Map result) {\n        StringBuilder sb = new StringBuilder();\n        Iterator<Entry> i = result.entrySet().iterator();\n        if (!i.hasNext()) {\n            return \"{}\";\n        }\n        sb.append('{');\n        for (;;) {\n            Entry e = i.next();\n            Object key = e.getKey();\n            Object value = e.getValue();\n            // 注意: 修改为getResultString(e)进行递归处理\n            sb.append(key == this ? \"(this Map)\" : getResultString(key));\n            sb.append('=');\n            // 注意: 修改为getResultString(e)进行递归处理\n            sb.append(value == this ? \"(this Map)\" : getResultString(value));\n            if (!i.hasNext()) {\n                return sb.append('}').toString();\n            }\n            sb.append(\", \");\n        }\n    }\n\n    /**\n     * 取得list的string，自定义的主要目的：针对value中数组数据的toString处理, copy from {@link AbstractCollection}\n     * \n     * @param result\n     * @return\n     */\n    private String getListResultString(List result) {\n        StringBuilder sb = new StringBuilder();\n        Iterator i = result.iterator();\n        if (!i.hasNext()) {\n            return \"[]\";\n        }\n        sb.append('[');\n        for (;;) {\n            Object e = i.next();\n            // 注意: 修改为getResultString(e)进行递归处理\n            sb.append(e == this ? \"(this Collection)\" : getResultString(e));\n            if (!i.hasNext()) {\n                return sb.append(']').toString();\n            }\n            sb.append(\", \");\n        }\n    }\n\n    /**\n     * 取得array的string，自定义的主要目的：针对value中数组数据的toString处理\n     * \n     * @param result\n     * @return\n     */\n    private String getArrayResultString(Object[] result) {\n        return getListResultString(Arrays.asList(result));\n    }\n\n    /**\n     * 取得参数字符串\n     * \n     * @param args\n     * @return\n     */\n    private StringBuffer getArgsString(Object[] args) {\n        StringBuffer buffer = new StringBuffer();\n        String prefix = \"args \";\n        for (int i = 0; i < args.length; i++) {\n            if (args.length > 1) {\n                buffer.append(prefix + (i + 1));\n            }\n            buffer.append(\"\\r\\t\");\n            buffer.append(getResultString(args[i]));\n            buffer.append(\"\\n\");\n        }\n        return buffer;\n    }\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/manage/ChannelArbitrateEvent.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.manage;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.concurrent.Callable;\nimport java.util.concurrent.ExecutionException;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Future;\n\nimport org.I0Itec.zkclient.exception.ZkException;\nimport org.I0Itec.zkclient.exception.ZkNoNodeException;\nimport org.I0Itec.zkclient.exception.ZkNodeExistsException;\nimport org.apache.commons.lang.StringUtils;\nimport org.apache.commons.lang.exception.ExceptionUtils;\nimport org.apache.commons.lang.math.RandomUtils;\nimport org.apache.zookeeper.CreateMode;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.util.CollectionUtils;\n\nimport com.alibaba.otter.shared.arbitrate.ArbitrateViewService;\nimport com.alibaba.otter.shared.arbitrate.exception.ArbitrateException;\nimport com.alibaba.otter.shared.arbitrate.impl.ArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.config.ArbitrateConfigUtils;\nimport com.alibaba.otter.shared.arbitrate.impl.manage.helper.ManagePathUtils;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.helper.StagePathUtils;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.zookeeper.termin.ErrorTerminProcess;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.zookeeper.termin.WarningTerminProcess;\nimport com.alibaba.otter.shared.arbitrate.impl.zookeeper.ZooKeeperClient;\nimport com.alibaba.otter.shared.arbitrate.model.TerminEventData;\nimport com.alibaba.otter.shared.arbitrate.model.TerminEventData.TerminType;\nimport com.alibaba.otter.shared.common.model.config.channel.Channel;\nimport com.alibaba.otter.shared.common.model.config.channel.ChannelStatus;\nimport com.alibaba.otter.shared.common.model.config.node.Node;\nimport com.alibaba.otter.shared.common.model.config.pipeline.Pipeline;\nimport com.alibaba.otter.shared.common.model.statistics.stage.ProcessStat;\nimport com.alibaba.otter.shared.common.utils.JsonUtils;\nimport com.alibaba.otter.shared.common.utils.zookeeper.ZkClientx;\n\n/**\n * 针对channel管理的相关信号操作\n * \n * @author jianghang 2011-8-31 下午07:39:26\n */\npublic class ChannelArbitrateEvent implements ArbitrateEvent {\n\n    protected static final Logger logger    = LoggerFactory.getLogger(ChannelArbitrateEvent.class);\n    private ZkClientx             zookeeper = ZooKeeperClient.getInstance();\n    private ArbitrateViewService  arbitrateViewService;\n    private NodeArbitrateEvent    nodeEvent;\n    private ErrorTerminProcess    errorTerminProcess;\n    private WarningTerminProcess  warningTerminProcess;\n    private ExecutorService       arbitrateExecutor;\n\n    /**\n     * 初始化对应的channel节点,同步调用\n     */\n    public void init(Long channelId) {\n        String path = ManagePathUtils.getChannelByChannelId(channelId);\n        byte[] data = JsonUtils.marshalToByte(ChannelStatus.STOP);// 初始化的数据对象\n\n        try {\n            zookeeper.create(path, data, CreateMode.PERSISTENT);\n        } catch (ZkNodeExistsException e) {\n            // 如果节点已经存在，则不抛异常\n            // ignore\n        } catch (ZkNoNodeException e) {\n            zookeeper.createPersistent(path, data, true);//创建父节点\n        } catch (ZkException e) {\n            throw new ArbitrateException(\"Channel_init\", channelId.toString(), e);\n        }\n    }\n\n    /**\n     * 启动对应的channel同步,是个同步调用\n     */\n    public void start(Long channelId) {\n        updateStatus(channelId, ChannelStatus.START);\n    }\n\n    /**\n     * 停止对应的channel同步,是个异步调用\n     */\n    public boolean pause(Long channelId) {\n        return pause(channelId, true);\n    }\n\n    /**\n     * 停止对应的channel同步,是个异步调用\n     */\n    public boolean pause(Long channelId, boolean needTermin) {\n        ChannelStatus currstatus = status(channelId);\n        boolean status = false;\n        boolean result = !needTermin;\n        if (currstatus.isStart()) { // stop的优先级高于pause，这里只针对start状态进行状态更新\n            updateStatus(channelId, ChannelStatus.PAUSE);\n            status = true; // 避免stop时发生rollback报警\n        }\n\n        if (needTermin) {\n            try {\n                // 调用termin进行关闭\n                result |= termin(channelId, TerminType.ROLLBACK);\n            } catch (Throwable e) {\n                updateStatus(channelId, ChannelStatus.PAUSE); // 出错了，直接挂起\n                throw new ArbitrateException(e);\n            }\n        }\n\n        return result && status;\n    }\n\n    /**\n     * 停止对应的channel同步,是个异步调用\n     */\n    public boolean stop(Long channelId) {\n        return stop(channelId, true);\n    }\n\n    /**\n     * 停止对应的channel同步,是个异步调用\n     */\n    public boolean stop(Long channelId, boolean needTermin) {\n        // stop优先级高于pause\n        updateStatus(channelId, ChannelStatus.STOP);\n\n        boolean result = !needTermin;\n\n        if (needTermin) {\n            try {\n                result |= termin(channelId, TerminType.SHUTDOWN);\n            } catch (Throwable e) {\n                updateStatus(channelId, ChannelStatus.STOP); // 出错了，直接挂起\n                throw new ArbitrateException(e);\n            }\n        }\n\n        return result;\n    }\n\n    public boolean restart(final Long channelId) {\n        return restart(channelId, true);\n    }\n\n    /**\n     * 停止对应的channel同步,是个异步调用\n     */\n    public boolean restart(final Long channelId, boolean needTermin) {\n        boolean result = !needTermin;\n        boolean status = false;\n        if (status(channelId).isStop() == false) { // stop的优先级高于pause\n            updateStatus(channelId, ChannelStatus.PAUSE);\n            status = true;\n        }\n\n        if (needTermin) {\n            try {\n                result |= termin(channelId, TerminType.RESTART);\n            } catch (Throwable e) {\n                updateStatus(channelId, ChannelStatus.PAUSE); // 出错了，直接挂起\n                throw new ArbitrateException(e);\n            }\n\n        }\n\n        // 处理一下重启操作，只处理pause状态\n        if (status || result) {\n            // 异步启动\n            arbitrateExecutor.submit(new Runnable() {\n\n                public void run() {\n                    // sleep一段时间，保证rollback信息有足够的时间能被处理完成\n                    try {\n                        Thread.sleep(5000L + RandomUtils.nextInt(2000));\n                    } catch (InterruptedException e) {\n                        // ignore\n                    }\n\n                    Channel channel = ArbitrateConfigUtils.getChannelByChannelId(channelId);\n                    ChannelStatus status = status(channel.getId());\n                    if (status.isStop()) {\n                        // stop优先级最高，不允许自动重启\n                        logger.info(\"channel[{}] is already stop , restart is ignored\", channel.getId());\n                    } else if (canStart(channel)) { // 出现stop，就不允许进行自动重启，stop优先级最高\n                        start(channelId);\n                    }\n                }\n            });\n        }\n\n        return result && status;\n    }\n\n    /**\n     * 查询当前channel的运行状态，是否同步调用\n     */\n    public ChannelStatus status(Long channelId) {\n        String path = StagePathUtils.getChannelByChannelId(channelId);\n        byte[] data = null;\n        try {\n            data = zookeeper.readData(path);\n        } catch (ZkNoNodeException e) {\n            // 如果节点已经不存在，则不抛异常\n            // ignore\n            return null;\n        } catch (ZkException e) {\n            throw new ArbitrateException(\"Channel_status\", channelId.toString(), e);\n        }\n\n        return JsonUtils.unmarshalFromByte(data, ChannelStatus.class);\n    }\n\n    /**\n     * 销毁对应的channel节点,同步调用\n     */\n    public void destory(Long channelId) {\n        String path = ManagePathUtils.getChannelByChannelId(channelId);\n        try {\n            zookeeper.delete(path); // 删除节点，不关心版本\n        } catch (ZkNoNodeException e) {\n            // 如果节点已经不存在，则不抛异常\n            // ignore\n        } catch (ZkException e) {\n            throw new ArbitrateException(\"Channel_destory\", channelId.toString(), e);\n        }\n    }\n\n    // ===================== help method =================\n\n    /**\n     * 执行结束同步任务操作\n     */\n    private Boolean termin(Long channelId, final TerminType type) throws Exception {\n        Channel channel = ArbitrateConfigUtils.getChannelByChannelId(channelId);\n        List<Pipeline> pipelines = channel.getPipelines();\n        List<Future<Boolean>> futures = new ArrayList<Future<Boolean>>();\n        for (final Pipeline pipeline : pipelines) {\n            futures.add(arbitrateExecutor.submit(new Callable<Boolean>() {\n\n                public Boolean call() {\n                    TerminEventData data = new TerminEventData();\n                    data.setPipelineId(pipeline.getId());\n                    data.setType(type);\n                    data.setCode(\"channel\");\n                    data.setDesc(type.toString());\n                    return errorTerminProcess.process(data); // 处理关闭\n                }\n            }));\n\n        }\n\n        boolean result = false;\n        Exception exception = null;\n        int index = 0;\n        for (Future<Boolean> future : futures) {\n            try {\n                result |= future.get(); // 进行处理\n            } catch (InterruptedException e) {\n                // ignore\n                Thread.currentThread().interrupt();\n            } catch (ExecutionException e) {\n                sendWarningMessage(pipelines.get(index).getId(), e);\n                exception = e;\n            }\n\n            index++;\n        }\n\n        if (exception != null) {\n            throw exception;\n        } else {\n            return result;\n        }\n    }\n\n    private void sendWarningMessage(Long pipelineId, Exception e) {\n        sendWarningMessage(pipelineId, ExceptionUtils.getFullStackTrace(e));\n    }\n\n    private void sendWarningMessage(Long pipelineId, String message) {\n        TerminEventData eventData = new TerminEventData();\n        eventData.setPipelineId(pipelineId);\n        eventData.setType(TerminType.WARNING);\n        eventData.setCode(\"channel\");\n        eventData.setDesc(message);\n        warningTerminProcess.process(eventData);\n    }\n\n    private boolean canStart(Channel channel) {\n        // 判断机器节点是否有存活的通路\n        // 查询一下最新的存活的node列表，可能channel取出来的数据为cache的结果\n        List<Long> liveNodes = nodeEvent.liveNodes();\n        for (Pipeline pipeline : channel.getPipelines()) {\n            // 判断select\n            List<Long> nids = getNids(pipeline.getSelectNodes());\n            if (!CollectionUtils.containsAny(liveNodes, nids)) {\n                logger.error(\"current live nodes:{} , but select nids:{} , result:{}\", new Object[] { liveNodes, nids,\n                        CollectionUtils.containsAny(liveNodes, nids) });\n                sendWarningMessage(pipeline.getId(), \"can't restart by no select live node\");\n                return false;\n            }\n\n            // 判断extract\n            nids = getNids(pipeline.getExtractNodes());\n            if (!CollectionUtils.containsAny(liveNodes, nids)) {\n                logger.error(\"current live nodes:{} , but extract nids:{} , result:{}\", new Object[] { liveNodes, nids,\n                        CollectionUtils.containsAny(liveNodes, nids) });\n                sendWarningMessage(pipeline.getId(), \"can't restart by no extract live node\");\n                return false;\n            }\n\n            // 判断transform/load\n            nids = getNids(pipeline.getLoadNodes());\n            if (!CollectionUtils.containsAny(liveNodes, nids)) {\n                logger.error(\"current live nodes:{} , but transform nids:{} , result:{}\", new Object[] { liveNodes,\n                        nids, CollectionUtils.containsAny(liveNodes, nids) });\n                sendWarningMessage(pipeline.getId(), \"can't restart by no transform live node\");\n                return false;\n            }\n\n            // 判断当前没有未清理的process\n            List<ProcessStat> stats = arbitrateViewService.listProcesses(channel.getId(), pipeline.getId());\n            if (!stats.isEmpty() && !status(channel.getId()).isStart()) {\n                List<Long> processIds = new ArrayList<Long>();\n                for (ProcessStat stat : stats) {\n                    processIds.add(stat.getProcessId());\n                }\n                sendWarningMessage(pipeline.getId(),\n                                   \"can't restart by exist process[\" + StringUtils.join(processIds, ',') + \"]\");\n                return false;\n            }\n        }\n\n        return true;\n    }\n\n    private List<Long> getNids(List<Node> nodes) {\n        List<Long> nids = new ArrayList<Long>();\n        for (Node node : nodes) {\n            nids.add(node.getId());\n        }\n\n        return nids;\n    }\n\n    private void updateStatus(Long channelId, ChannelStatus status) {\n        String path = ManagePathUtils.getChannelByChannelId(channelId);\n        byte[] data = JsonUtils.marshalToByte(status);// 初始化的数据对象\n        try {\n            zookeeper.writeData(path, data);\n        } catch (ZkException e) {\n            throw new ArbitrateException(\"Channel_init\", channelId.toString(), e);\n        }\n    }\n\n    // ====================== setter / getter =================\n\n    public void setErrorTerminProcess(ErrorTerminProcess errorTerminProcess) {\n        this.errorTerminProcess = errorTerminProcess;\n    }\n\n    public void setWarningTerminProcess(WarningTerminProcess warningTerminProcess) {\n        this.warningTerminProcess = warningTerminProcess;\n    }\n\n    public void setArbitrateExecutor(ExecutorService arbitrateExecutor) {\n        this.arbitrateExecutor = arbitrateExecutor;\n    }\n\n    public void setArbitrateViewService(ArbitrateViewService arbitrateViewService) {\n        this.arbitrateViewService = arbitrateViewService;\n    }\n\n    public void setNodeEvent(NodeArbitrateEvent nodeEvent) {\n        this.nodeEvent = nodeEvent;\n    }\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/manage/NodeArbitrateEvent.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.manage;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.I0Itec.zkclient.exception.ZkException;\nimport org.I0Itec.zkclient.exception.ZkNoNodeException;\nimport org.apache.zookeeper.CreateMode;\n\nimport com.alibaba.otter.shared.arbitrate.exception.ArbitrateException;\nimport com.alibaba.otter.shared.arbitrate.impl.ArbitrateConstants;\nimport com.alibaba.otter.shared.arbitrate.impl.ArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.manage.helper.ManagePathUtils;\nimport com.alibaba.otter.shared.arbitrate.impl.zookeeper.ZooKeeperClient;\nimport com.alibaba.otter.shared.common.utils.zookeeper.ZkClientx;\n\n/**\n * 机器 node 节点的相关信号\n * \n * @author jianghang 2011-8-31 下午 07:26:02\n */\npublic class NodeArbitrateEvent implements ArbitrateEvent {\n\n    private ZkClientx zookeeper = ZooKeeperClient.getInstance();\n\n    /**\n     * 创建相应的 node 节点，说明：node 节点的生命周期为 EPHEMERAL\n     * \n     * <pre>\n     * 1. 是个同步调用\n     * </pre>\n     */\n    public void init(Long nid) {\n        String path = ManagePathUtils.getNode(nid);\n\n        try {\n          if (!zookeeper.exists(path)){\n              zookeeper.create(path, new byte[0], CreateMode.EPHEMERAL);// 创建为临时节点\n            }\n        } catch (ZkException e) {\n            throw new ArbitrateException(\"Node_init\", nid.toString(), e);\n        }\n    }\n\n    /**\n     * 销毁的 node 节点\n     * \n     * <pre>\n     * 1. 是个同步调用\n     * </pre>\n     */\n    public void destory(Long nid) {\n        String path = ManagePathUtils.getNode(nid);\n\n        try {\n            zookeeper.delete(path); // 删除节点，不关心版本\n        } catch (ZkNoNodeException e) {\n            // 如果节点已经不存在，则不抛异常\n            // ignore\n        } catch (ZkException e) {\n            throw new ArbitrateException(\"Node_destory\", nid.toString(), e);\n        }\n    }\n\n    /**\n     * 获取当前存活的节点列表\n     */\n    public List<Long> liveNodes() {\n        String path = ArbitrateConstants.NODE_NID_ROOT;\n        try {\n            List<String> nids = zookeeper.getChildren(path);\n            List<Long> result = new ArrayList<Long>();\n            for (String nid : nids) {\n                result.add(Long.valueOf(nid));\n            }\n\n            return result;\n        } catch (ZkException e) {\n            throw new ArbitrateException(\"liveNodes\", e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/manage/NodeSessionExpired.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.manage;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.alibaba.otter.shared.arbitrate.impl.config.ArbitrateConfigUtils;\nimport com.alibaba.otter.shared.arbitrate.impl.zookeeper.SessionExpiredNotification;\n\n/**\n * node节点重建\n * \n * @author jianghang 2012-1-13 上午11:16:59\n * @version 4.0.0\n */\npublic class NodeSessionExpired implements SessionExpiredNotification {\n\n    private static final Logger logger = LoggerFactory.getLogger(NodeSessionExpired.class);\n    private NodeArbitrateEvent  nodeEvent;\n\n    public void notification() {\n        try {\n            nodeEvent.init(ArbitrateConfigUtils.getCurrentNid());\n        } catch (Exception e) {\n            logger.error(\"after session expired , init node failed. \", e);\n        }\n    }\n\n    public void setNodeEvent(NodeArbitrateEvent nodeEvent) {\n        this.nodeEvent = nodeEvent;\n    }\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/manage/PipelineArbitrateEvent.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.manage;\n\nimport org.I0Itec.zkclient.exception.ZkException;\nimport org.I0Itec.zkclient.exception.ZkNoNodeException;\nimport org.I0Itec.zkclient.exception.ZkNodeExistsException;\nimport org.apache.zookeeper.CreateMode;\n\nimport com.alibaba.otter.shared.arbitrate.exception.ArbitrateException;\nimport com.alibaba.otter.shared.arbitrate.impl.ArbitrateConstants;\nimport com.alibaba.otter.shared.arbitrate.impl.ArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.manage.helper.ManagePathUtils;\nimport com.alibaba.otter.shared.arbitrate.impl.zookeeper.ZooKeeperClient;\nimport com.alibaba.otter.shared.common.utils.zookeeper.ZkClientx;\n\n/**\n * 针对pipeline管理的相关信号操作\n * \n * @author jianghang 2011-8-31 下午07:34:11\n */\npublic class PipelineArbitrateEvent implements ArbitrateEvent {\n\n    private ZkClientx zookeeper = ZooKeeperClient.getInstance();\n\n    /**\n     * 初始化对应的pipeline节点,同步调用\n     */\n    public void init(Long channelId, Long pipelineId) {\n        String path = ManagePathUtils.getPipeline(channelId, pipelineId);\n        String processRootPath = ManagePathUtils.getProcessRoot(channelId, pipelineId);\n        String terminRootPath = ManagePathUtils.getTerminRoot(channelId, pipelineId);\n        String remedyRootPath = ManagePathUtils.getRemedyRoot(channelId, pipelineId);\n        String lockRootPath = ManagePathUtils.getLockRoot(channelId, pipelineId);\n        String loadLockPath = lockRootPath + \"/\" + ArbitrateConstants.NODE_LOCK_LOAD;\n        try {\n            zookeeper.createPersistent(path, true);//创建父节点\n            zookeeper.create(processRootPath, new byte[0], CreateMode.PERSISTENT);\n            zookeeper.create(terminRootPath, new byte[0], CreateMode.PERSISTENT);\n            zookeeper.create(remedyRootPath, new byte[0], CreateMode.PERSISTENT);\n            zookeeper.create(lockRootPath, new byte[0], CreateMode.PERSISTENT);\n            zookeeper.create(loadLockPath, new byte[0], CreateMode.PERSISTENT);\n        } catch (ZkNodeExistsException e) {\n            // 如果节点已经存在，则不抛异常\n            // ignore\n        } catch (ZkException e) {\n            throw new ArbitrateException(\"Pipeline_init\", pipelineId.toString(), e);\n        }\n    }\n\n    /**\n     * 销毁对应的pipeline节点,同步调用\n     */\n    public void destory(Long channelId, Long pipelineId) {\n        String path = ManagePathUtils.getPipeline(channelId, pipelineId);\n        String processRootPath = ManagePathUtils.getProcessRoot(channelId, pipelineId);\n        String terminRootPath = ManagePathUtils.getTerminRoot(channelId, pipelineId);\n        String remedyRootPath = ManagePathUtils.getRemedyRoot(channelId, pipelineId);\n        String lockRootPath = ManagePathUtils.getLockRoot(channelId, pipelineId);\n        String loadLockPath = lockRootPath + \"/\" + ArbitrateConstants.NODE_LOCK_LOAD;\n        try {\n            zookeeper.deleteRecursive(loadLockPath); // 删除节点，不关心版本\n            zookeeper.deleteRecursive(lockRootPath); // 删除节点，不关心版本\n            zookeeper.deleteRecursive(terminRootPath); // 删除节点，不关心版本\n            zookeeper.deleteRecursive(remedyRootPath); // 删除节点，不关心版本\n            zookeeper.deleteRecursive(processRootPath); // 删除节点，不关心版本\n            zookeeper.deleteRecursive(path); // 删除节点，不关心版本\n        } catch (ZkNoNodeException e) {\n            // 如果节点已经不存在，则不抛异常\n            // ignore\n        } catch (ZkException e) {\n            throw new ArbitrateException(\"Pipeline_destory\", pipelineId.toString(), e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/manage/SystemArbitrateEvent.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.manage;\n\nimport org.I0Itec.zkclient.exception.ZkBadVersionException;\nimport org.I0Itec.zkclient.exception.ZkException;\nimport org.I0Itec.zkclient.exception.ZkNoNodeException;\nimport org.I0Itec.zkclient.exception.ZkNodeExistsException;\nimport org.apache.zookeeper.CreateMode;\nimport org.apache.zookeeper.data.Stat;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.alibaba.otter.shared.arbitrate.exception.ArbitrateException;\nimport com.alibaba.otter.shared.arbitrate.impl.ArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.manage.helper.ManagePathUtils;\nimport com.alibaba.otter.shared.arbitrate.impl.zookeeper.ZooKeeperClient;\nimport com.alibaba.otter.shared.arbitrate.model.MainStemEventData;\nimport com.alibaba.otter.shared.common.utils.JsonUtils;\nimport com.alibaba.otter.shared.common.utils.zookeeper.ZkClientx;\n\n/**\n * otter系统节点初始化\n * \n * @author jianghang 2012-2-16 下午04:38:33\n * @version 4.0.0\n */\npublic class SystemArbitrateEvent implements ArbitrateEvent {\n\n    private static final Logger logger    = LoggerFactory.getLogger(SystemArbitrateEvent.class);\n    private ZkClientx           zookeeper = ZooKeeperClient.getInstance();\n\n    /**\n     * 初始化对应的系统节点,同步调用\n     */\n    public void init() {\n        String rootPath = ManagePathUtils.getRoot();\n        String channelRootPath = ManagePathUtils.getChannelRoot();\n        String nodeRootPath = ManagePathUtils.getNodeRoot();\n        try {\n            zookeeper.create(rootPath, new byte[0], CreateMode.PERSISTENT);\n            zookeeper.create(channelRootPath, new byte[0], CreateMode.PERSISTENT);\n            zookeeper.create(nodeRootPath, new byte[0], CreateMode.PERSISTENT);\n        } catch (ZkNodeExistsException e) {\n            // 如果节点已经存在，则不抛异常\n            // ignore\n        } catch (ZkException e) {\n            throw new ArbitrateException(\"system_init\", e);\n        }\n    }\n\n    /**\n     * 销毁对应的系统节点,同步调用\n     */\n    public void destory() {\n        String rootPath = ManagePathUtils.getRoot();\n        String channelRootPath = ManagePathUtils.getChannelRoot();\n        String nodeRootPath = ManagePathUtils.getNodeRoot();\n        try {\n            zookeeper.deleteRecursive(channelRootPath); // 删除节点，不关心版本\n            zookeeper.deleteRecursive(nodeRootPath); // 删除节点，不关心版本\n            zookeeper.deleteRecursive(rootPath); // 删除节点，不关心版本\n        } catch (ZkNoNodeException e) {\n            // 如果节点已经不存在，则不抛异常\n            // ignore\n        } catch (ZkException e) {\n            throw new ArbitrateException(\"system_destory\", e);\n        }\n    }\n\n    /**\n     * 手工触发一次主备切换\n     */\n    public void switchWarmup(Long channelId, Long pipelineId) {\n        String path = ManagePathUtils.getMainStem(channelId, pipelineId);\n        try {\n            while (true) {\n                Stat stat = new Stat();\n                byte[] bytes = zookeeper.readData(path, stat);\n                MainStemEventData mainStemData = JsonUtils.unmarshalFromByte(bytes, MainStemEventData.class);\n                mainStemData.setActive(false);\n                try {\n                    zookeeper.writeData(path, JsonUtils.marshalToByte(mainStemData), stat.getVersion());\n                    logger.warn(\"relase channelId[{}],pipelineId[{}] mainstem successed! \", channelId, pipelineId);\n                    break;\n                } catch (ZkBadVersionException e) {\n                    // ignore , retrying\n                }\n            }\n        } catch (ZkNoNodeException e) {\n            // ignore\n        } catch (ZkException e) {\n            throw new ArbitrateException(\"releaseMainStem\", pipelineId.toString(), e);\n        }\n    }\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/manage/helper/ManagePathUtils.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.manage.helper;\n\nimport java.text.MessageFormat;\n\nimport org.apache.commons.lang.StringUtils;\n\nimport com.alibaba.otter.shared.arbitrate.impl.ArbitrateConstants;\n\n/**\n * 对应zookeeper path构建的helper类\n * \n * @author jianghang\n */\npublic class ManagePathUtils {\n\n    /**\n     * 返回对应的otter root path\n     */\n    public static String getRoot() {\n        return ArbitrateConstants.NODE_OTTER_ROOT;\n    }\n\n    /**\n     * 返回对应的node root path\n     */\n    public static String getNodeRoot() {\n        return ArbitrateConstants.NODE_NID_ROOT;\n    }\n\n    /**\n     * 返回对应的channel root path\n     */\n    public static String getChannelRoot() {\n        return ArbitrateConstants.NODE_CHANNEL_ROOT;\n    }\n\n    /**\n     * 返回对应的node path\n     */\n    public static String getNode(Long nodeId) {\n        // 根据nodeId构造path\n        return MessageFormat.format(ArbitrateConstants.NODE_NID_FORMAT, String.valueOf(nodeId));\n    }\n\n    /**\n     * 返回对应的channel path (不依赖对应的config信息)\n     */\n    public static String getChannelByChannelId(Long channelId) {\n        // 根据channelId 构造path\n        return MessageFormat.format(ArbitrateConstants.NODE_CHANNEL_FORMAT, String.valueOf(channelId));\n    }\n\n    /**\n     * 返回对应的pipeline path (不依赖对应的config信息)\n     */\n    public static String getPipeline(Long channelId, Long pipelineId) {\n        // 根据channelId , pipelineId 构造path\n        return MessageFormat.format(ArbitrateConstants.NODE_PIPELINE_FORMAT,\n            String.valueOf(channelId),\n            String.valueOf(pipelineId));\n    }\n\n    /**\n     * 返回对应的remedy root path\n     */\n    public static String getRemedyRoot(Long channelId, Long pipelineId) {\n        // 根据channelId , pipelineId构造path\n        return MessageFormat.format(ArbitrateConstants.NODE_REMEDY_ROOT,\n            String.valueOf(channelId),\n            String.valueOf(pipelineId));\n    }\n\n    /**\n     * 返回对应的getProcess path (不依赖对应的config信息)\n     */\n    public static String getProcessRoot(Long channelId, Long pipelineId) {\n        // 根据channelId , pipelineId 构造path\n        return MessageFormat.format(ArbitrateConstants.NODE_PROCESS_ROOT,\n            String.valueOf(channelId),\n            String.valueOf(pipelineId));\n    }\n\n    /**\n     * 返回对应的mainStem path\n     */\n    public static String getMainStem(Long channelId, Long pipelineId) {\n        return MessageFormat.format(ArbitrateConstants.NODE_PIPELINE_FORMAT,\n            String.valueOf(channelId),\n            String.valueOf(pipelineId))\n               + \"/\" + ArbitrateConstants.NODE_MAINSTEM;\n    }\n\n    /**\n     * 返回对应的termin root path(不依赖对应的config信息)\n     */\n    public static String getTerminRoot(Long channelId, Long pipelineId) {\n        // 根据channelId , pipelineId构造path\n        return MessageFormat.format(ArbitrateConstants.NODE_TERMIN_ROOT,\n            String.valueOf(channelId),\n            String.valueOf(pipelineId));\n    }\n\n    /**\n     * 返回对应的process path\n     */\n    public static String getProcess(Long channelId, Long pipelineId, Long processId) {\n        // 根据channelId , pipelineId构造path\n        return MessageFormat.format(ArbitrateConstants.NODE_PROCESS_FORMAT,\n            String.valueOf(channelId),\n            String.valueOf(pipelineId),\n            getProcessNode(processId));\n    }\n\n    /**\n     * 返回对应的process path\n     */\n    public static String getProcess(Long channelId, Long pipelineId, String processNode) {\n        // 根据channelId , pipelineId构造path\n        return MessageFormat.format(ArbitrateConstants.NODE_PROCESS_FORMAT,\n            String.valueOf(channelId),\n            String.valueOf(pipelineId),\n            processNode);\n    }\n\n    /**\n     * 返回对应的termin path\n     */\n    public static String getTermin(Long channelId, Long pipelineId, Long processId) {\n        // 根据channelId , pipelineId构造path\n        return MessageFormat.format(ArbitrateConstants.NODE_TERMIN_FORMAT,\n            String.valueOf(channelId),\n            String.valueOf(pipelineId),\n            getProcessNode(processId));\n    }\n\n    /**\n     * 返回对应的lock root path(不依赖对应的config信息)\n     */\n    public static String getLockRoot(Long channelId, Long pipelineId) {\n        // 根据channelId , pipelineId构造path\n        return MessageFormat.format(ArbitrateConstants.NODE_LOCK_ROOT,\n            String.valueOf(channelId),\n            String.valueOf(pipelineId));\n    }\n\n    // ======================== hleper method============================\n\n    /**\n     * zookeeper中的node名称转化为processId\n     */\n    public static Long getProcessId(String processNode) {\n        return Long.valueOf(processNode);\n    }\n\n    /**\n     * 将processId转化为zookeeper中的node名称\n     */\n    public static String getProcessNode(Long processId) {\n        return StringUtils.leftPad(String.valueOf(processId.intValue()), 10, '0');\n    }\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/setl/ArbitrateFactory.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.setl;\n\nimport java.lang.reflect.Constructor;\nimport java.util.Collection;\nimport java.util.Map;\n\nimport org.springframework.beans.BeansException;\nimport org.springframework.beans.factory.config.AutowireCapableBeanFactory;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.ApplicationContextAware;\n\nimport com.alibaba.otter.shared.arbitrate.exception.ArbitrateException;\nimport com.google.common.base.Function;\nimport com.google.common.collect.OtterMigrateMap;\n\n/**\n * 针对arbitrate的对象管理的工厂方法，基于pipelineId需要做对象缓存\n * \n * @author jianghang 2011-9-20 上午11:24:19\n * @version 4.0.0\n */\npublic class ArbitrateFactory implements ApplicationContextAware {\n\n    private static ApplicationContext            context = null;\n    // 两层的Map接口，第一层为pipelineId，第二层为具体的资源类型class\n    private static Map<Long, Map<Class, Object>> cache   = OtterMigrateMap.makeComputingMap(new Function<Long, Map<Class, Object>>() {\n\n                                                             public Map<Class, Object> apply(final Long pipelineId) {\n                                                                 return OtterMigrateMap.makeComputingMap(new Function<Class, Object>() {\n\n                                                                     public Object apply(Class instanceClass) {\n                                                                         return newInstance(pipelineId, instanceClass);\n                                                                     }\n                                                                 });\n                                                             }\n                                                         });\n\n    private static Object newInstance(Long pipelineId, Class instanceClass) {\n        Object obj = newInstance(instanceClass, pipelineId);// 通过反射调用构造函数进行初始化\n        autowire(obj);\n        return obj;\n    }\n\n    /**\n     * 指定对应的pipelineId，获取对应的仲裁资源类型<br/>\n     * 要求对应的instanceClass，都必须支持以pipelineId做为唯一参数的构造函数\n     */\n    public static <T extends ArbitrateLifeCycle> T getInstance(Long pipelineId, Class<T> instanceClass) {\n        // Map<Class, Object> resources = cache.get(pipelineId);\n        // if (resources == null) {\n        // synchronized (cache) {// 锁住一下\n        // if (!cache.containsKey(pipelineId)) {// double check\n        // resources = new ConcurrentHashMap<Class, Object>();\n        // cache.put(pipelineId, resources);\n        // } else {\n        // resources = cache.get(pipelineId);\n        // }\n        // }\n        // }\n        // Object obj = resources.get(instanceClass);\n        // if (obj == null) {\n        // synchronized (instanceClass) {// 锁住class对象\n        // if (!resources.containsKey(instanceClass)) { // double check\n        // obj = newInstance(instanceClass, pipelineId);// 通过反射调用构造函数进行初始化\n        // autowire(obj);\n        // resources.put(instanceClass, obj);\n        // } else {\n        // obj = resources.get(instanceClass);\n        // }\n        // }\n        // }\n        // return (T) obj;\n\n        return (T) cache.get(pipelineId).get(instanceClass);\n    }\n\n    public static void autowire(Object obj) {\n        // 重新注入一下对象\n        context.getAutowireCapableBeanFactory().autowireBeanProperties(obj,\n                                                                       AutowireCapableBeanFactory.AUTOWIRE_BY_NAME,\n                                                                       true);\n    }\n\n    public static void destory() {\n        for (Long pipelineId : cache.keySet()) {\n            destory(pipelineId);\n        }\n    }\n\n    /**\n     * 销毁和释放对应pipelineId的仲裁资源\n     * \n     * @param pipelineId\n     */\n    public static void destory(Long pipelineId) {\n        Map<Class, Object> resources = cache.remove(pipelineId);\n        if (resources != null) {\n            Collection collection = resources.values();\n            for (Object obj : collection) {\n                if (obj instanceof ArbitrateLifeCycle) {\n                    ArbitrateLifeCycle lifeCycle = (ArbitrateLifeCycle) obj;\n                    lifeCycle.destory();// 调用销毁方法\n                }\n            }\n        }\n    }\n\n    /**\n     * 销毁和释放对应pipelineId的仲裁资源\n     * \n     * @param pipelineId\n     */\n    public static <T extends ArbitrateLifeCycle> void destory(Long pipelineId, Class<T> instanceClass) {\n        Map<Class, Object> resources = cache.get(pipelineId);\n        if (resources != null) {\n            Object obj = resources.remove(instanceClass);\n            if (obj instanceof ArbitrateLifeCycle) {\n                ArbitrateLifeCycle lifeCycle = (ArbitrateLifeCycle) obj;\n                lifeCycle.destory();// 调用销毁方法\n            }\n        }\n    }\n\n    // ==================== helper method =======================\n\n    private static Object newInstance(Class type, Long pipelineId) {\n        Constructor _constructor = null;\n        Object[] _constructorArgs = new Object[1];\n        _constructorArgs[0] = pipelineId;\n\n        try {\n            _constructor = type.getConstructor(new Class[] { Long.class });\n        } catch (NoSuchMethodException e) {\n            throw new ArbitrateException(\"Constructor_notFound\");\n        }\n\n        try {\n            return _constructor.newInstance(_constructorArgs);\n        } catch (Exception e) {\n            throw new ArbitrateException(\"Constructor_newInstance_error\", e);\n        }\n\n    }\n\n    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {\n        context = applicationContext;\n    }\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/setl/ArbitrateLifeCycle.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.setl;\n\n/**\n * @author jianghang 2011-9-20 下午01:05:24\n * @version 4.0.0\n */\npublic abstract class ArbitrateLifeCycle {\n\n    private Long             pipelineId;\n    private volatile boolean stop = false; //是否关闭\n\n    public ArbitrateLifeCycle(Long pipelineId){\n        this.pipelineId = pipelineId;\n    }\n\n    public Long getPipelineId() {\n        return pipelineId;\n    }\n\n    public boolean isStop() {\n        return stop;\n    }\n\n    public void setStop(boolean stop) {\n        this.stop = stop;\n    }\n\n    public void destory() {\n        stop = true;\n    }\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/setl/ExtractArbitrateEvent.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.setl;\n\nimport com.alibaba.otter.shared.arbitrate.impl.ArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.model.EtlEventData;\n\n/**\n * 抽象extract模块的调度接口\n * \n * @author jianghang 2012-9-27 下午09:54:53\n * @version 4.1.0\n */\npublic interface ExtractArbitrateEvent extends ArbitrateEvent {\n\n    public EtlEventData await(Long pipelineId) throws InterruptedException;\n\n    public void single(EtlEventData data);\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/setl/LoadArbitrateEvent.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.setl;\n\nimport com.alibaba.otter.shared.arbitrate.impl.ArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.model.EtlEventData;\n\n/**\n * 抽象load模块的调度接口\n * \n * @author jianghang 2012-9-27 下午09:54:53\n * @version 4.1.0\n */\npublic interface LoadArbitrateEvent extends ArbitrateEvent {\n\n    public EtlEventData await(Long pipelineId) throws InterruptedException;\n\n    public void single(EtlEventData data);\n\n    // public void release(Long pipelineId);\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/setl/MainStemArbitrateEvent.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.setl;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.util.Assert;\n\nimport com.alibaba.otter.shared.arbitrate.impl.ArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.monitor.MainstemMonitor;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.monitor.PermitMonitor;\nimport com.alibaba.otter.shared.arbitrate.model.MainStemEventData;\nimport com.alibaba.otter.shared.common.model.config.channel.ChannelStatus;\n\n/**\n * 主导线程信号控制\n * \n * @author jianghang 2011-8-9 下午05:16:16\n */\npublic class MainStemArbitrateEvent implements ArbitrateEvent {\n\n    private static final Logger logger = LoggerFactory.getLogger(MainStemArbitrateEvent.class);\n\n    /**\n     * <pre>\n     * 算法:\n     * 1. 检查当前的Permit，阻塞等待其授权(解决Channel的pause状态处理)\n     * 2. 尝试创建对应的mainStem节点(非持久化节点)\n     *  a. 如果创建成功，则直接构造结果返回\n     *  b. 如果创建失败，则关注该节点的exist信号. 继续执行步骤2\n     * </pre>\n     */\n    public void await(Long pipelineId) throws InterruptedException {\n        Assert.notNull(pipelineId);\n        PermitMonitor permitMonitor = ArbitrateFactory.getInstance(pipelineId, PermitMonitor.class);\n        ChannelStatus status = permitMonitor.getChannelPermit(true);\n        boolean isRuning = check(pipelineId);\n        if (!status.isStart() && isRuning) {\n            // 当前状态不为启动，强制设置为taking，下次授权启动后重新追数据\n            MainStemEventData data = new MainStemEventData();\n            data.setPipelineId(pipelineId);\n            data.setStatus(MainStemEventData.Status.TAKEING);\n            single(data);\n            permitMonitor.waitForChannelPermit(); // 阻塞等待挂起\n            return;\n        } else if (status.isStart() && isRuning) {// 正常状态\n            return;\n        } else if (isRuning == false) {\n            if (!status.isStart()) {\n                permitMonitor.waitForChannelPermit(); // 阻塞等待挂起\n            }\n            MainstemMonitor mainstemMonitor = ArbitrateFactory.getInstance(pipelineId, MainstemMonitor.class);\n            mainstemMonitor.waitForActive();// 等待自己成为active\n\n            status = permitMonitor.getChannelPermit(false);\n            if (status.isStart()) {\n                return;\n            } else {\n                logger.info(\"pipelineId[{}] mainstem ignore by status[{}]\", new Object[] { pipelineId, status });\n                await(pipelineId);// 重新进行check一次\n            }\n        }\n\n    }\n\n    /**\n     * 检查下当前的mainstem是否可以运行(在await获取成功后，每次执行业务之前进行check)\n     */\n    public boolean check(Long pipelineId) {\n        MainstemMonitor mainstemMonitor = ArbitrateFactory.getInstance(pipelineId, MainstemMonitor.class);\n        return mainstemMonitor.check();\n    }\n\n    /**\n     * 更新mainStem的同步状态数据\n     */\n    public void single(MainStemEventData data) {\n        MainstemMonitor mainstemMonitor = ArbitrateFactory.getInstance(data.getPipelineId(), MainstemMonitor.class);\n        mainstemMonitor.single(data);\n    }\n\n    /**\n     * 释放mainStem的节点，重新选择工作节点\n     */\n    public void release(Long pipelineId) {\n        MainstemMonitor mainstemMonitor = ArbitrateFactory.getInstance(pipelineId, MainstemMonitor.class);\n        mainstemMonitor.releaseMainstem();\n    }\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/setl/SelectArbitrateEvent.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.setl;\n\nimport com.alibaba.otter.shared.arbitrate.impl.ArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.model.EtlEventData;\n\n/**\n * 抽象select模块的调度接口\n * \n * @author jianghang 2012-9-27 下午09:54:53\n * @version 4.1.0\n */\npublic interface SelectArbitrateEvent extends ArbitrateEvent {\n\n    public EtlEventData await(Long pipelineId) throws InterruptedException;\n\n    public void single(EtlEventData data);\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/setl/TerminArbitrateEvent.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.setl;\n\nimport com.alibaba.otter.shared.arbitrate.model.TerminEventData;\n\npublic interface TerminArbitrateEvent {\n\n    public TerminEventData await(Long pipelineId) throws InterruptedException;\n\n    public void exhaust(Long pipelineId);\n\n    public void ack(TerminEventData data);\n\n    public int size(Long pipelineId);\n\n    public void single(final TerminEventData data);\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/setl/ToolArbitrateEvent.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.setl;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.I0Itec.zkclient.exception.ZkException;\nimport org.I0Itec.zkclient.exception.ZkNoNodeException;\nimport org.I0Itec.zkclient.exception.ZkNodeExistsException;\nimport org.apache.zookeeper.CreateMode;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.util.Assert;\n\nimport com.alibaba.otter.shared.arbitrate.exception.ArbitrateException;\nimport com.alibaba.otter.shared.arbitrate.impl.ArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.helper.RemedyIndexComparator;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.helper.StagePathUtils;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.monitor.PermitMonitor;\nimport com.alibaba.otter.shared.arbitrate.impl.zookeeper.ZooKeeperClient;\nimport com.alibaba.otter.shared.arbitrate.model.RemedyIndexEventData;\nimport com.alibaba.otter.shared.arbitrate.model.SyncStatusEventData;\nimport com.alibaba.otter.shared.common.utils.JsonUtils;\nimport com.alibaba.otter.shared.common.utils.zookeeper.ZkClientx;\n\n/**\n * 为setl提供辅助工具的event事件\n * \n * @author jianghang 2011-10-18 上午10:17:18\n * @version 4.0.0\n */\npublic class ToolArbitrateEvent implements ArbitrateEvent {\n\n    private static final Logger logger    = LoggerFactory.getLogger(ToolArbitrateEvent.class);\n    private ZkClientx           zookeeper = ZooKeeperClient.getInstance();\n\n    /**\n     * 阻塞等待授权通过\n     * \n     * @param pipelineId\n     * @throws InterruptedException\n     */\n    public void waitForPermit(Long pipelineId) throws InterruptedException {\n        Assert.notNull(pipelineId);\n\n        PermitMonitor permitMonitor = ArbitrateFactory.getInstance(pipelineId, PermitMonitor.class);\n        permitMonitor.waitForPermit();// 阻塞等待授权\n    }\n\n    /**\n     * 提供数据接口获取对应pipeline上的状态\n     */\n    public SyncStatusEventData fetch(Long pipelineId) {\n        String path = StagePathUtils.getPipeline(pipelineId);\n        try {\n            byte[] bytes = zookeeper.readData(path);\n            if (bytes == null || bytes.length == 0) {\n                SyncStatusEventData evnetData = new SyncStatusEventData();\n                evnetData.setPipelineId(pipelineId);\n                return evnetData;\n            } else {\n                return JsonUtils.unmarshalFromByte(bytes, SyncStatusEventData.class);\n            }\n        } catch (ZkException e) {\n            // 没有节点返回空\n            throw new ArbitrateException(\"fetch_SyncStatus\", pipelineId.toString(), e);\n        }\n    }\n\n    /**\n     * 提供数据接口更新对应的pipeline上的状态\n     */\n    public void single(SyncStatusEventData syncStatus) {\n        String path = StagePathUtils.getPipeline(syncStatus.getPipelineId());\n        try {\n            byte[] bytes = JsonUtils.marshalToByte(syncStatus);\n            zookeeper.writeData(path, bytes);\n            logger.info(\"## single status : \" + syncStatus);\n        } catch (ZkException e) {\n            throw new ArbitrateException(\"single_SyncStatus\", syncStatus.getPipelineId().toString(), e);\n        }\n    }\n\n    /**\n     * 添加一个index节点\n     */\n    public void addRemedyIndex(RemedyIndexEventData data) {\n        String path = StagePathUtils.getRemedyRoot(data.getPipelineId());\n        try {\n            zookeeper.create(path + \"/\" + RemedyIndexEventData.formatNodeName(data), new byte[] {},\n                             CreateMode.PERSISTENT);\n        } catch (ZkNodeExistsException e) {\n            // ignore\n        } catch (ZkException e) {\n            throw new ArbitrateException(\"addRemedyIndex\", data.getPipelineId().toString(), e);\n        }\n    }\n\n    /**\n     * 删除一个index节点\n     */\n    public void removeRemedyIndex(RemedyIndexEventData data) {\n        String path = StagePathUtils.getRemedyRoot(data.getPipelineId());\n        try {\n            zookeeper.delete(path + \"/\" + RemedyIndexEventData.formatNodeName(data));\n        } catch (ZkNoNodeException e) {\n            // ignore\n        } catch (ZkException e) {\n            throw new ArbitrateException(\"removeRemedyIndex\", data.getPipelineId().toString(), e);\n        }\n    }\n\n    /**\n     * 查询当前的remedy index记录\n     */\n    public List<RemedyIndexEventData> listRemedyIndexs(Long pipelineId) {\n        String path = StagePathUtils.getRemedyRoot(pipelineId);\n        List<RemedyIndexEventData> datas = new ArrayList<RemedyIndexEventData>();\n        try {\n            List<String> nodes = zookeeper.getChildren(path);\n            for (String node : nodes) {\n                RemedyIndexEventData data = RemedyIndexEventData.parseNodeName(node);\n                data.setPipelineId(pipelineId);\n                datas.add(data);\n            }\n        } catch (ZkException e) {\n            throw new ArbitrateException(\"listRemedyIndexs\", pipelineId.toString(), e);\n        }\n\n        Collections.sort(datas, new RemedyIndexComparator()); // 做一下排序\n        return datas;\n    }\n\n    /**\n     * 释放对应的pipeline资源，是个同步调用\n     */\n    public void release(Long pipelineId) {\n        ArbitrateFactory.destory(pipelineId);\n    }\n\n    public void release() {\n        ArbitrateFactory.destory();\n    }\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/setl/TransformArbitrateEvent.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.setl;\n\nimport com.alibaba.otter.shared.arbitrate.impl.ArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.model.EtlEventData;\n\n/**\n * 抽象transform模块的调度接口\n * \n * @author jianghang 2012-9-27 下午09:54:53\n * @version 4.1.0\n */\npublic interface TransformArbitrateEvent extends ArbitrateEvent {\n\n    public EtlEventData await(Long pipelineId) throws InterruptedException;\n\n    public void single(EtlEventData data);\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/setl/delegate/AbstractDelegateArbitrateEvent.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.setl.delegate;\n\nimport java.util.List;\n\nimport com.alibaba.otter.shared.arbitrate.impl.ArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.config.ArbitrateConfigUtils;\nimport com.alibaba.otter.shared.common.model.config.node.Node;\nimport com.alibaba.otter.shared.common.model.config.pipeline.Pipeline;\nimport com.alibaba.otter.shared.common.model.config.pipeline.PipelineParameter.ArbitrateMode;\n\n/**\n * delegate一些共用的判断摸索\n * \n * @author jianghang 2012-9-28 上午10:07:16\n * @version 4.1.0\n */\npublic class AbstractDelegateArbitrateEvent implements ArbitrateEvent {\n\n    public ArbitrateMode chooseMode(Long pipelineId) {\n        Pipeline pipeline = ArbitrateConfigUtils.getPipeline(pipelineId);\n        ArbitrateMode arbitrateMode = pipeline.getParameters().getArbitrateMode();\n        // 重新计算arbitrateMode\n        ArbitrateMode result = null;\n        switch (arbitrateMode) {\n            case AUTOMATIC:\n                // 1. 如果s/e/t/l都是由1台或者多台机器提供服务，则选择内存模式\n                // 2. 如果s/e由一组机器，t/l由另一组机器提供服务，则选择rpc模式\n                if (containAll(pipeline.getSelectNodes(), pipeline.getExtractNodes())\n                    && containAll(pipeline.getSelectNodes(), pipeline.getLoadNodes())) {\n                    result = ArbitrateMode.MEMORY;\n                } else {\n                    result = ArbitrateMode.RPC;\n                }\n\n                break;\n            default:\n                result = arbitrateMode;\n                break;\n        }\n\n        return result;\n    }\n\n    /**\n     * 判断srcNodes是否在targetNodes中都包含\n     */\n    private boolean containAll(List<Node> srcNodes, List<Node> targetNodes) {\n        boolean result = true;\n        for (Node node : srcNodes) {\n            result &= targetNodes.contains(node);\n        }\n\n        return result;\n    }\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/setl/delegate/ExtractDelegateArbitrateEvent.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.setl.delegate;\n\nimport java.util.Map;\n\nimport com.alibaba.otter.shared.arbitrate.impl.setl.ExtractArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.model.EtlEventData;\nimport com.alibaba.otter.shared.common.model.config.pipeline.PipelineParameter.ArbitrateMode;\n\n/**\n * extract delegate实现\n * \n * @author jianghang 2012-9-28 上午10:36:38\n * @version 4.1.0\n */\npublic class ExtractDelegateArbitrateEvent extends AbstractDelegateArbitrateEvent implements ExtractArbitrateEvent {\n\n    private Map<ArbitrateMode, ExtractArbitrateEvent> delegate;\n\n    public EtlEventData await(Long pipelineId) throws InterruptedException {\n        return delegate.get(chooseMode(pipelineId)).await(pipelineId);\n    }\n\n    public void single(EtlEventData data) {\n        delegate.get(chooseMode(data.getPipelineId())).single(data);\n    }\n\n    public void setDelegate(Map<ArbitrateMode, ExtractArbitrateEvent> delegate) {\n        this.delegate = delegate;\n    }\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/setl/delegate/LoadDelegateArbitrateEvent.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.setl.delegate;\n\nimport java.util.Map;\n\nimport com.alibaba.otter.shared.arbitrate.impl.setl.LoadArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.model.EtlEventData;\nimport com.alibaba.otter.shared.common.model.config.pipeline.PipelineParameter.ArbitrateMode;\n\n/**\n * load delegate实现\n * \n * @author jianghang 2012-9-28 上午10:36:38\n * @version 4.1.0\n */\npublic class LoadDelegateArbitrateEvent extends AbstractDelegateArbitrateEvent implements LoadArbitrateEvent {\n\n    private Map<ArbitrateMode, LoadArbitrateEvent> delegate;\n\n    public EtlEventData await(Long pipelineId) throws InterruptedException {\n        return delegate.get(chooseMode(pipelineId)).await(pipelineId);\n    }\n\n    public void single(EtlEventData data) {\n        delegate.get(chooseMode(data.getPipelineId())).single(data);\n    }\n\n    public void setDelegate(Map<ArbitrateMode, LoadArbitrateEvent> delegate) {\n        this.delegate = delegate;\n    }\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/setl/delegate/SelectDelegateArbitrateEvent.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.setl.delegate;\n\nimport java.util.Map;\n\nimport com.alibaba.otter.shared.arbitrate.impl.setl.SelectArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.model.EtlEventData;\nimport com.alibaba.otter.shared.common.model.config.pipeline.PipelineParameter.ArbitrateMode;\n\n/**\n * select delegate实现\n * \n * @author jianghang 2012-9-28 上午10:36:38\n * @version 4.1.0\n */\npublic class SelectDelegateArbitrateEvent extends AbstractDelegateArbitrateEvent implements SelectArbitrateEvent {\n\n    private Map<ArbitrateMode, SelectArbitrateEvent> delegate;\n\n    public EtlEventData await(Long pipelineId) throws InterruptedException {\n        return delegate.get(chooseMode(pipelineId)).await(pipelineId);\n    }\n\n    public void single(EtlEventData data) {\n        delegate.get(chooseMode(data.getPipelineId())).single(data);\n    }\n\n    public void setDelegate(Map<ArbitrateMode, SelectArbitrateEvent> delegate) {\n        this.delegate = delegate;\n    }\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/setl/delegate/TerminDelegateArbitrateEvent.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.setl.delegate;\n\nimport java.util.Map;\n\nimport com.alibaba.otter.shared.arbitrate.impl.setl.TerminArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.model.TerminEventData;\nimport com.alibaba.otter.shared.common.model.config.pipeline.PipelineParameter.ArbitrateMode;\n\n/**\n * termin delegate实现\n * \n * @author jianghang 2012-9-28 上午10:42:08\n * @version 4.1.0\n */\npublic class TerminDelegateArbitrateEvent extends AbstractDelegateArbitrateEvent implements TerminArbitrateEvent {\n\n    private Map<ArbitrateMode, TerminArbitrateEvent> delegate;\n\n    public TerminEventData await(Long pipelineId) throws InterruptedException {\n        return delegate.get(chooseMode(pipelineId)).await(pipelineId);\n    }\n\n    public void single(TerminEventData data) {\n        delegate.get(chooseMode(data.getPipelineId())).single(data);\n    }\n\n    public void exhaust(Long pipelineId) {\n        delegate.get(chooseMode(pipelineId)).exhaust(pipelineId);\n    }\n\n    public void ack(TerminEventData data) {\n        delegate.get(chooseMode(data.getPipelineId())).ack(data);\n    }\n\n    public int size(Long pipelineId) {\n        return delegate.get(chooseMode(pipelineId)).size(pipelineId);\n    }\n\n    public void setDelegate(Map<ArbitrateMode, TerminArbitrateEvent> delegate) {\n        this.delegate = delegate;\n    }\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/setl/delegate/TransformDelegateArbitrateEvent.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.setl.delegate;\n\nimport java.util.Map;\n\nimport com.alibaba.otter.shared.arbitrate.impl.setl.TransformArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.model.EtlEventData;\nimport com.alibaba.otter.shared.common.model.config.pipeline.PipelineParameter.ArbitrateMode;\n\n/**\n * transform delegate实现\n * \n * @author jianghang 2012-9-28 上午10:36:38\n * @version 4.1.0\n */\npublic class TransformDelegateArbitrateEvent extends AbstractDelegateArbitrateEvent implements TransformArbitrateEvent {\n\n    private Map<ArbitrateMode, TransformArbitrateEvent> delegate;\n\n    public EtlEventData await(Long pipelineId) throws InterruptedException {\n        return delegate.get(chooseMode(pipelineId)).await(pipelineId);\n    }\n\n    public void single(EtlEventData data) {\n        delegate.get(chooseMode(data.getPipelineId())).single(data);\n    }\n\n    public void setDelegate(Map<ArbitrateMode, TransformArbitrateEvent> delegate) {\n        this.delegate = delegate;\n    }\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/setl/fastrpc/FastRpcStageController.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.setl.fastrpc;\n\nimport com.alibaba.otter.shared.arbitrate.impl.setl.ArbitrateLifeCycle;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.monitor.listener.PermitListener;\nimport com.alibaba.otter.shared.arbitrate.model.EtlEventData;\nimport com.alibaba.otter.shared.common.model.config.enums.StageType;\n\n/**\n * 基于memory + rpc的调度模式的一个整合版，不依赖于zookeeper的process创建\n * \n * <pre>\n * 大致算法:\n * 1. 基于内存方式创建一个顺序processId，正常运行过程中不可重复，出现Rollback/Stop操作后可重复\n * 2. 判断最小id的方法：\n *    a. 需要记录上一次成功load的processId，如果当前id为该processId + 1的话，则为最小id. \n *    b. 当上一次load的processId为0时，代表第一次启动，数字id 1为最小id\n *    前提：\n *      i. 每次启动，processId一定是从1开始分配，并且必须是连续的id分配\n * 3. 运行时如何停止：\n *    a. 首先修改channel status状态，通过PermitMonitor通知所有节点，\"尽可能\"hold住所有s/e/t/l调度. 因为zookeepr watcher通知存在延迟，高峰美国有5秒的延迟\n *    b. 每个{@linkplain FastRpcStageController}实例，监听Permit的变化，发现ChannelStatus出现Rollback/Stop情况，立即销毁\n *    前提：\n *      i. zookeeper watcher推送存在不确定性，多台机器推送有先后，其中一台重建状态，另一台可能还处于老状态，\n * </pre>\n * \n * @author jianghang 2013-2-28 下午10:15:20\n * @version 4.1.7\n */\npublic class FastRpcStageController extends ArbitrateLifeCycle implements PermitListener {\n\n    public FastRpcStageController(Long pipelineId){\n        super(pipelineId);\n    }\n\n    public synchronized boolean single(StageType stage, EtlEventData etlEventData) {\n        return true;\n    }\n\n    public void processChanged(boolean isPermit) {\n\n    }\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/setl/fastrpc/FastRpcStageEventDispatcher.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.setl.fastrpc;\n\nimport org.springframework.util.Assert;\n\nimport com.alibaba.otter.shared.arbitrate.impl.communication.ArbitrateCommmunicationClient;\nimport com.alibaba.otter.shared.arbitrate.impl.config.ArbitrateConfigUtils;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.ArbitrateFactory;\nimport com.alibaba.otter.shared.arbitrate.model.EtlEventData;\nimport com.alibaba.otter.shared.common.model.config.enums.StageType;\nimport com.alibaba.otter.shared.communication.core.CommunicationRegistry;\nimport com.alibaba.otter.shared.communication.model.arbitrate.ArbitrateEventType;\nimport com.alibaba.otter.shared.communication.model.arbitrate.StageSingleEvent;\n\n/**\n * 分发rpc的请求，根据不同的pipelineId分发到不同的{@link FastRpcStageController}实例上去\n * \n * @author jianghang 2013-2-28 下午10:04:50\n * @version 4.1.7\n */\npublic class FastRpcStageEventDispatcher {\n\n    private ArbitrateCommmunicationClient arbitrateCommmunicationClient;\n\n    public FastRpcStageEventDispatcher(){\n        CommunicationRegistry.regist(ArbitrateEventType.fastStageSingle, this);\n    }\n\n    /**\n     * 接收rpc请求的调用\n     */\n    protected boolean onStageSingle(StageSingleEvent event) {\n        Assert.notNull(event.getPipelineId());\n        // 根据pipeline找到对应的实例\n        FastRpcStageController controller = ArbitrateFactory.getInstance(event.getPipelineId(),\n                                                                         FastRpcStageController.class);\n        return controller.single(event.getStage(), (EtlEventData) event.getData());\n    }\n\n    /**\n     * 触发通知\n     */\n    public boolean single(StageType stage, EtlEventData eventData) {\n        Assert.notNull(eventData);\n        eventData.setCurrNid(ArbitrateConfigUtils.getCurrentNid());\n\n        StageSingleEvent event = new StageSingleEvent(ArbitrateEventType.fastStageSingle);\n        event.setPipelineId(eventData.getPipelineId());\n        event.setStage(stage);\n        event.setData(eventData);\n\n        if (isLocal(eventData.getNextNid())) {// 判断是否为本地jvm\n            return onStageSingle(event);\n        } else {\n            return (Boolean) arbitrateCommmunicationClient.call(eventData.getNextNid(), event);// rpc通知下一个节点\n        }\n    }\n\n    private boolean isLocal(Long targetNodeId) {\n        return ArbitrateConfigUtils.getCurrentNid().equals(targetNodeId);\n    }\n\n    public void setArbitrateCommmunicationClient(ArbitrateCommmunicationClient arbitrateCommmunicationClient) {\n        this.arbitrateCommmunicationClient = arbitrateCommmunicationClient;\n    }\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/setl/helper/RemedyIndexComparator.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.setl.helper;\n\nimport java.util.Comparator;\n\nimport com.alibaba.otter.shared.arbitrate.model.RemedyIndexEventData;\n\n/**\n * Remedy 排序，根据processId (process和startTime/endTime一定保持一致的排序性）\n * \n * @author jianghang 2012-4-13 下午02:20:35\n * @version 4.0.2\n */\npublic class RemedyIndexComparator implements Comparator<RemedyIndexEventData> {\n\n    public int compare(RemedyIndexEventData index1, RemedyIndexEventData index2) {\n        return index1.getProcessId().compareTo(index2.getProcessId());\n    }\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/setl/helper/ReplyProcessQueue.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.setl.helper;\n\nimport java.util.LinkedHashMap;\nimport java.util.PriorityQueue;\nimport java.util.Map.Entry;\nimport java.util.concurrent.locks.Condition;\nimport java.util.concurrent.locks.ReentrantLock;\n\n/**\n * 自定义queue实现，简单的合并history + list的功能\n * \n * <pre>\n * 修改记录：\n * 1. 2012-09-08 by ljh\n *  将LinkedList换成了PriorityQueue进行存储，保证并发处理时，保证processId小的一定会得到优先处理。\n *  a. 避免在s模块出现processId大的先被分了出去，导致加载顺序会出错\n *  b. 尽可能的优先处理processId小的，因为只有之前的processId的s/e/t/l都处理完了，下一个processId才会进行load\n * </pre>\n * \n * @author jianghang 2012-6-28 上午10:12:25\n * @version 4.1.0\n */\npublic class ReplyProcessQueue {\n\n    private static final Object            PRESENT  = new Object();\n    private LRULinkedHashMap<Long, Object> history;                             // 记录一下最近分配出去的processId，容量必须>当前并行度\n    private PriorityQueue<Long>            tables   = new PriorityQueue<Long>();\n    private ReentrantLock                  lock     = new ReentrantLock();\n    private Condition                      notEmpty = lock.newCondition();\n\n    public ReplyProcessQueue(int historySize){\n        history = new LRULinkedHashMap<Long, Object>(historySize);\n    }\n\n    public Long take() throws InterruptedException {\n        try {\n            lock.lockInterruptibly();\n            Long result = null;\n            do {\n                if (tables.size() == 0) {\n                    notEmpty.await();\n                }\n\n                result = tables.poll();\n            } while (result == null);\n\n            history.put(result, PRESENT);\n            return result;\n        } finally {\n            lock.unlock();\n        }\n    }\n\n    public boolean offer(Long processId) {\n        lock.lock();\n        try {\n            if (contains(processId)) {\n                return false;\n            }\n\n            int size = tables.size();\n            // tables.addLast(processId);\n            tables.add(processId);// 添加记录\n            if (size == 0) {\n                notEmpty.signalAll();\n            }\n            return true;\n        } finally {\n            lock.unlock();\n        }\n    }\n\n    public boolean remove(Long processId) {\n        lock.lock();\n        try {\n            boolean result = tables.remove(processId);\n            if (result) {\n                history.put(processId, PRESENT); // 记录一下到历史记录\n            }\n            return result;\n        } finally {\n            lock.unlock();\n        }\n    }\n\n    public void clear() {\n        lock.lock();\n        try {\n            tables.clear();\n            history.clear();\n        } finally {\n            lock.unlock();\n        }\n    }\n\n    public boolean contains(Long processId) {\n        return tables.contains(processId) || history.containsKey(processId);\n    }\n\n    public Object[] toArray() {\n        return tables.toArray();\n    }\n\n    public int size() {\n        return tables.size();\n    }\n\n}\n\n/**\n * 简单的继承实现LRU算法对象,注意需要控制多线程\n * \n * @author jianghang 2011-9-28 上午10:18:06\n * @version 4.0.0\n */\nclass LRULinkedHashMap<K, V> extends LinkedHashMap<K, V> {\n\n    private static final long  serialVersionUID    = 1827912970480911024L;\n\n    private final int          maxCapacity;\n\n    private static final float DEFAULT_LOAD_FACTOR = 1f;\n\n    public LRULinkedHashMap(int maxCapacity){\n        super(maxCapacity, DEFAULT_LOAD_FACTOR, false);\n        this.maxCapacity = maxCapacity;\n    }\n\n    protected boolean removeEldestEntry(Entry eldest) {\n        return (size() > this.maxCapacity);\n    }\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/setl/helper/StageComparator.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.setl.helper;\n\nimport java.util.Comparator;\nimport java.util.Map;\n\nimport com.alibaba.otter.shared.arbitrate.impl.ArbitrateConstants;\nimport com.google.common.collect.Maps;\n\n/**\n * stage阶段的排序因子\n * \n * @author jianghang 2011-9-22 上午09:57:17\n * @version 4.0.0\n */\npublic class StageComparator implements Comparator<String> {\n\n    private static final Map<String, Integer> stageIndex = Maps.newHashMap();\n    static {\n        stageIndex.put(ArbitrateConstants.NODE_SELECTED, 1);\n        stageIndex.put(ArbitrateConstants.NODE_EXTRACTED, 2);\n        stageIndex.put(ArbitrateConstants.NODE_TRANSFORMED, 3);\n        // stageIndex.put(ArbitrateConstants.NODE_LOADED, 4);\n    }\n\n    @Override\n    public int compare(String o1, String o2) {\n        int i1 = (stageIndex.get(o1) == null ? 0 : stageIndex.get(o1));\n        int i2 = (stageIndex.get(o2) == null ? 0 : stageIndex.get(o2));\n\n        if (i1 > i2) {\n            return 1;\n        } else if (i1 == i2) {\n            return 0;\n        } else {\n            return -1;\n        }\n    }\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/setl/helper/StagePathUtils.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.setl.helper;\n\nimport java.text.MessageFormat;\n\nimport com.alibaba.otter.shared.arbitrate.exception.ArbitrateException;\nimport com.alibaba.otter.shared.arbitrate.impl.ArbitrateConstants;\nimport com.alibaba.otter.shared.arbitrate.impl.config.ArbitrateConfigUtils;\nimport com.alibaba.otter.shared.arbitrate.impl.manage.helper.ManagePathUtils;\nimport com.alibaba.otter.shared.common.model.config.channel.Channel;\nimport com.alibaba.otter.shared.common.model.config.pipeline.Pipeline;\n\n/**\n * 对应zookeeper path构建的helper类\n * \n * @author jianghang\n */\npublic class StagePathUtils extends ManagePathUtils {\n\n    /**\n     * 返回对应的pipeline path\n     */\n    public static String getPipeline(Long pipelineId) {\n        // 根据channelId , pipelineId构造path\n        return MessageFormat.format(ArbitrateConstants.NODE_PIPELINE_FORMAT, getChannelId(pipelineId),\n                                    String.valueOf(pipelineId));\n    }\n\n    /**\n     * 返回对应的opposite pipeline path\n     */\n    public static String getOppositePipeline(Long pipelineId) {\n        // 根据channelId , pipelineId构造path\n        return MessageFormat.format(ArbitrateConstants.NODE_PIPELINE_FORMAT, getChannelId(pipelineId),\n                                    getOppositePipelineId(pipelineId));\n    }\n\n    /**\n     * 返回对应的channel path\n     */\n    public static String getChannel(Long pipelineId) {\n        // 根据channelId , pipelineId构造path\n        return MessageFormat.format(ArbitrateConstants.NODE_CHANNEL_FORMAT, getChannelId(pipelineId));\n    }\n\n    /**\n     * 返回对应的remedy root path\n     */\n    public static String getRemedyRoot(Long pipelineId) {\n        // 根据channelId , pipelineId构造path\n        return MessageFormat.format(ArbitrateConstants.NODE_REMEDY_ROOT, getChannelId(pipelineId),\n                                    String.valueOf(pipelineId));\n    }\n\n    /**\n     * 返回对应的process root path\n     */\n    public static String getProcessRoot(Long pipelineId) {\n        // 根据channelId , pipelineId构造path\n        return MessageFormat.format(ArbitrateConstants.NODE_PROCESS_ROOT, getChannelId(pipelineId),\n                                    String.valueOf(pipelineId));\n    }\n\n    /**\n     * 返回对应的process path\n     */\n    public static String getProcess(Long pipelineId, Long processId) {\n        // 根据channelId , pipelineId构造path\n        return MessageFormat.format(ArbitrateConstants.NODE_PROCESS_FORMAT, getChannelId(pipelineId),\n                                    String.valueOf(pipelineId), getProcessNode(processId));\n    }\n\n    /**\n     * 返回对应的termin root path\n     */\n    public static String getTerminRoot(Long pipelineId) {\n        // 根据channelId , pipelineId构造path\n        return MessageFormat.format(ArbitrateConstants.NODE_TERMIN_ROOT, getChannelId(pipelineId),\n                                    String.valueOf(pipelineId));\n    }\n\n    /**\n     * 返回对应的termin path\n     */\n    public static String getTermin(Long pipelineId, Long processId) {\n        // 根据channelId , pipelineId构造path\n        return MessageFormat.format(ArbitrateConstants.NODE_TERMIN_FORMAT, getChannelId(pipelineId),\n                                    String.valueOf(pipelineId), getProcessNode(processId));\n    }\n\n    /**\n     * 返回对应的process path\n     */\n    public static String getProcess(Long pipelineId, String processNode) {\n        // 根据channelId , pipelineId构造path\n        return MessageFormat.format(ArbitrateConstants.NODE_PROCESS_FORMAT, getChannelId(pipelineId),\n                                    String.valueOf(pipelineId), processNode);\n    }\n\n    /**\n     * 返回对应的mainStem path\n     */\n    public static String getMainStem(Long pipelineId) {\n        return getPipeline(pipelineId) + \"/\" + ArbitrateConstants.NODE_MAINSTEM;\n    }\n\n    /**\n     * 返回对应的opposite mainStem path\n     */\n    public static String getOppositeMainStem(Long pipelineId) {\n        return getOppositePipeline(pipelineId) + \"/\" + ArbitrateConstants.NODE_MAINSTEM;\n    }\n\n    /**\n     * 返回对应的lock root path\n     */\n    public static String getLockRoot(Long pipelineId) {\n        // 根据channelId , pipelineId构造path\n        return MessageFormat.format(ArbitrateConstants.NODE_LOCK_ROOT, getChannelId(pipelineId),\n                                    String.valueOf(pipelineId));\n    }\n\n    /**\n     * 返回对应的load模块的lock路径\n     */\n    public static String getLoadLock(Long pipelineId) {\n        return getLockRoot(pipelineId) + \"/\" + ArbitrateConstants.NODE_LOCK_LOAD;\n    }\n\n    // =========================== stage path =========================\n\n    /**\n     * 返回对应的select stage path\n     */\n    public static String getSelectStage(Long pipelineId, Long processId) {\n        return getProcess(pipelineId, processId) + \"/\" + ArbitrateConstants.NODE_SELECTED;\n    }\n\n    /**\n     * 返回对应的extract stage path\n     */\n    public static String getExtractStage(Long pipelineId, Long processId) {\n        return getProcess(pipelineId, processId) + \"/\" + ArbitrateConstants.NODE_EXTRACTED;\n    }\n\n    /**\n     * 返回对应的transform stage path\n     */\n    public static String getTransformStage(Long pipelineId, Long processId) {\n        return getProcess(pipelineId, processId) + \"/\" + ArbitrateConstants.NODE_TRANSFORMED;\n    }\n\n    // /**\n    // * 返回对应的load stage path\n    // */\n    // public static String getLoadStage(Long pipelineId, Long processId) {\n    // return getProcess(pipelineId, processId) + \"/\" + ArbitrateConstants.NODE_LOADED;\n    // }\n\n    // ================ helper method ================\n\n    private static String getChannelId(Long pipelineId) {\n        Channel channel = ArbitrateConfigUtils.getChannel(pipelineId);\n        return String.valueOf(channel.getId());\n    }\n\n    private static String getOppositePipelineId(Long pipelineId) {\n        Pipeline pipeline = ArbitrateConfigUtils.getOppositePipeline(pipelineId);\n        if (pipeline != null) {\n            Long id = pipeline.getId();\n            return String.valueOf(id);\n        }\n\n        throw new ArbitrateException(\"pipeline[\" + pipelineId + \"] has not opposite pipeline!\");\n    }\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/setl/helper/StageProgress.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.setl.helper;\n\nimport org.apache.commons.lang.builder.ToStringBuilder;\n\nimport com.alibaba.otter.shared.arbitrate.model.EtlEventData;\nimport com.alibaba.otter.shared.common.model.config.enums.StageType;\nimport com.alibaba.otter.shared.common.utils.OtterToStringStyle;\n\n/**\n * stage进度数据\n * \n * @author jianghang 2012-9-29 上午10:34:05\n * @version 4.1.0\n */\npublic class StageProgress {\n\n    private StageType    stage;\n    private EtlEventData data;\n\n    public StageProgress(){\n    }\n\n    public StageProgress(StageType stage, EtlEventData data){\n        this.stage = stage;\n        this.data = data;\n    }\n\n    public StageType getStage() {\n        return stage;\n    }\n\n    public void setStage(StageType stage) {\n        this.stage = stage;\n    }\n\n    public EtlEventData getData() {\n        return data;\n    }\n\n    public void setData(EtlEventData data) {\n        this.data = data;\n    }\n\n    public String toString() {\n        return ToStringBuilder.reflectionToString(this, OtterToStringStyle.DEFAULT_STYLE);\n    }\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/setl/helper/TerminProcessQueue.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.setl.helper;\n\nimport java.util.PriorityQueue;\nimport java.util.concurrent.locks.Condition;\nimport java.util.concurrent.locks.ReentrantLock;\n\n/**\n * 结束信号对应的queue模型, peek/ack组合\n * \n * @author jianghang 2012-7-3 下午03:10:43\n * @version 4.1.0\n */\npublic class TerminProcessQueue {\n\n    private PriorityQueue<Long>            queue    = new PriorityQueue<Long>();\n    private static final Object            PRESENT  = new Object();\n    private LRULinkedHashMap<Long, Object> history;                             // 记录一下最近分配出去的processId，容量必须>当前并行度\n    private ReentrantLock                  lock     = new ReentrantLock();\n    private Condition                      notEmpty = lock.newCondition();\n\n    public TerminProcessQueue(){\n        history = new LRULinkedHashMap<Long, Object>(100);\n    }\n\n    /**\n     * 从queue中获取第一个数据节点，不移除，等待ack信号进行移除\n     * \n     * @return\n     * @throws InterruptedException\n     */\n    public Long peek() throws InterruptedException {\n        lock.lockInterruptibly();\n        try {\n            while (queue.size() == 0) {\n                notEmpty.await();\n            }\n\n            return queue.peek();\n        } finally {\n            lock.unlock();\n        }\n    }\n\n    public boolean offer(Long processId) {\n        lock.lock();\n        try {\n            if (contains(processId)) {\n                return false;\n            }\n\n            int size = queue.size();\n            queue.add(processId);\n            if (size == 0) {\n                notEmpty.signalAll();\n            }\n            return true;\n        } finally {\n            lock.unlock();\n        }\n    }\n\n    public boolean contains(Long processId) {\n        return queue.contains(processId) || history.containsKey(processId);\n    }\n\n    /**\n     * 物理移除queue中第一个数据节点，不移除\n     * \n     * @return\n     */\n    public boolean ack() {\n        lock.lock();\n        try {\n            Long result = queue.poll();\n            if (result != null) {// 添加到历史记录里，避免重复\n                history.put(result, PRESENT);\n            }\n            return result != null;\n        } finally {\n            lock.unlock();\n        }\n    }\n\n    public void clear() {\n        lock.lock();\n        try {\n            queue.clear();\n            history.clear();\n        } finally {\n            lock.unlock();\n        }\n    }\n\n    public int size() {\n        lock.lock();\n        try {\n            return queue.size();\n        } finally {\n            lock.unlock();\n        }\n    }\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/setl/lb/AbstractLoadBalance.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.setl.lb;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport com.alibaba.otter.shared.arbitrate.impl.config.ArbitrateConfigUtils;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.ArbitrateLifeCycle;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.monitor.NodeMonitor;\nimport com.alibaba.otter.shared.common.model.config.node.Node;\nimport com.alibaba.otter.shared.common.model.config.pipeline.Pipeline;\n\n/**\n * 抽象的负载均衡接口\n * \n * @author jianghang 2011-9-20 下午01:31:29\n * @version 4.0.0\n */\npublic abstract class AbstractLoadBalance extends ArbitrateLifeCycle implements LoadBalance {\n\n    protected NodeMonitor nodeMonitor;\n\n    public AbstractLoadBalance(Long pipelineId){\n        super(pipelineId);\n    }\n\n    public abstract List<Node> getAliveNodes();\n\n    public void destory() {\n        super.destory();\n    }\n\n    public void setNodeMonitor(NodeMonitor nodeMonitor) {\n        this.nodeMonitor = nodeMonitor;\n    }\n\n    public List<Node> getExtractAliveNodes() {\n        Pipeline pipeline = ArbitrateConfigUtils.getPipeline(getPipelineId());\n        List<Node> extractNodes = pipeline.getExtractNodes();\n        List<Node> eNodes = new ArrayList<Node>();\n\n        List<Long> aliveNodes = nodeMonitor.getAliveNodes();\n        for (Node sourceNode : extractNodes) {\n            if (aliveNodes.contains(sourceNode.getId())) {\n                eNodes.add(sourceNode);\n            }\n        }\n\n        return eNodes;\n    }\n\n    public List<Node> getTransformAliveNodes() {\n        Pipeline pipeline = ArbitrateConfigUtils.getPipeline(getPipelineId());\n        List<Node> transformNodes = pipeline.getLoadNodes();\n        List<Node> tNodes = new ArrayList<Node>();\n\n        List<Long> aliveNodes = nodeMonitor.getAliveNodes();\n        for (Node sourceNode : transformNodes) {\n            if (aliveNodes.contains(sourceNode.getId())) {\n                tNodes.add(sourceNode);\n            }\n        }\n\n        return tNodes;\n    }\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/setl/lb/ExtractRandomLoadBanlance.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.setl.lb;\n\nimport java.util.List;\n\nimport com.alibaba.otter.shared.common.model.config.node.Node;\n\n/**\n * extract模块的负载均衡实现\n * \n * @author jianghang 2011-9-20 下午01:24:22\n * @version 4.0.0\n */\npublic class ExtractRandomLoadBanlance extends RandomLoadBalance {\n\n    public ExtractRandomLoadBanlance(Long pipelineId){\n        super(pipelineId);\n    }\n\n    @Override\n    public List<Node> getAliveNodes() {\n        return getExtractAliveNodes();\n    }\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/setl/lb/ExtractRoundRobinLoadBalance.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.setl.lb;\n\nimport java.util.List;\n\nimport com.alibaba.otter.shared.common.model.config.node.Node;\n\n/**\n * extract模块的负载均衡实现\n * \n * @author jianghang 2011-9-20 下午01:24:22\n * @version 4.0.0\n */\npublic class ExtractRoundRobinLoadBalance extends RoundRobinLoadBalance {\n\n    public ExtractRoundRobinLoadBalance(Long pipelineId){\n        super(pipelineId);\n    }\n\n    @Override\n    public List<Node> getAliveNodes() {\n        return getExtractAliveNodes();\n    }\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/setl/lb/ExtractStickLoadBalance.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.setl.lb;\n\nimport java.util.List;\n\nimport com.alibaba.otter.shared.common.model.config.node.Node;\n\npublic class ExtractStickLoadBalance extends StickLoadBalance {\n\n    public ExtractStickLoadBalance(Long pipelineId){\n        super(pipelineId);\n    }\n\n    @Override\n    public List<Node> getAliveNodes() {\n        return getExtractAliveNodes();\n    }\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/setl/lb/LoadBalance.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.setl.lb;\n\nimport com.alibaba.otter.shared.common.model.config.node.Node;\n\n/**\n * 负载均衡算法\n * \n * @author jianghang 2011-8-19 上午10:16:45\n */\npublic interface LoadBalance {\n\n    public Node next() throws InterruptedException;\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/setl/lb/LoadBalanceFactory.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.setl.lb;\n\nimport com.alibaba.otter.shared.arbitrate.impl.config.ArbitrateConfigUtils;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.ArbitrateFactory;\nimport com.alibaba.otter.shared.common.model.config.node.Node;\nimport com.alibaba.otter.shared.common.model.config.pipeline.Pipeline;\nimport com.alibaba.otter.shared.common.model.config.pipeline.PipelineParameter.LoadBanlanceAlgorithm;\n\n/**\n * 获取next node\n * \n * @author jianghang 2013-2-28 下午09:55:56\n * @version 4.1.4\n */\npublic class LoadBalanceFactory {\n\n    public static Node getNextExtractNode(Long pipelineId) throws InterruptedException {\n        Pipeline pipeline = ArbitrateConfigUtils.getPipeline(pipelineId);\n        LoadBanlanceAlgorithm loadBanlanceAlgorithm = pipeline.getParameters().getLbAlgorithm();\n        LoadBalance loadbalance = null;\n        if (loadBanlanceAlgorithm.isRandom()) {\n            loadbalance = ArbitrateFactory.getInstance(pipelineId, ExtractRandomLoadBanlance.class);\n        } else if (loadBanlanceAlgorithm.isRoundRbin()) {\n            loadbalance = ArbitrateFactory.getInstance(pipelineId, ExtractRoundRobinLoadBalance.class);\n        } else {\n            loadbalance = ArbitrateFactory.getInstance(pipelineId, ExtractStickLoadBalance.class);\n        }\n        Node node = loadbalance.next();// 获取下一个处理节点信息\n        return node;\n    }\n\n    public static Node getNextTransformNode(Long pipelineId) throws InterruptedException {\n        Pipeline pipeline = ArbitrateConfigUtils.getPipeline(pipelineId);\n        LoadBanlanceAlgorithm loadBanlanceAlgorithm = pipeline.getParameters().getLbAlgorithm();\n        LoadBalance loadbalance = null;\n        if (loadBanlanceAlgorithm.isRandom()) {\n            loadbalance = ArbitrateFactory.getInstance(pipelineId, TransformRandomLoadBanlance.class);\n        } else if (loadBanlanceAlgorithm.isRoundRbin()) {\n            loadbalance = ArbitrateFactory.getInstance(pipelineId, TransformRoundRobinLoadBalance.class);\n        } else {\n            loadbalance = ArbitrateFactory.getInstance(pipelineId, TransformStickLoadBalance.class);\n        }\n        Node node = loadbalance.next();// 获取下一个处理节点信息\n        return node;\n    }\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/setl/lb/RandomLoadBalance.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.setl.lb;\n\nimport java.util.List;\n\nimport org.apache.commons.lang.math.RandomUtils;\n\nimport com.alibaba.otter.shared.arbitrate.impl.config.ArbitrateConfigUtils;\nimport com.alibaba.otter.shared.common.model.config.node.Node;\n\n/**\n * Random的负载均衡实现 <br/>\n * \n * @author jianghang 2011-9-16 下午07:12:27\n * @version 4.0.0\n */\npublic abstract class RandomLoadBalance extends AbstractLoadBalance implements LoadBalance {\n\n    private int localPercent = 90; //local优先返回的权重，百分比\n\n    public RandomLoadBalance(Long pipelineId){\n        super(pipelineId);\n    }\n\n    @Override\n    public Node next() throws InterruptedException {\n        List<Node> nodes = getAliveNodes();\n\n        if (nodes == null || nodes.size() == 0) {\n            return null;\n        }\n\n        Long nid = ArbitrateConfigUtils.getCurrentNid();\n        Node current = new Node();\n        current.setId(nid);\n\n        // 判断一下是否优先返回local\n        boolean existLocal = nodes.remove(current);\n        if (existLocal && nodes.size() == 0) {//如果只有它自己\n            return current;\n        } else if (existLocal && RandomUtils.nextInt(100) < localPercent) {//计算一下百分比\n            return current;\n        } else {\n            int index = RandomUtils.nextInt(nodes.size());\n            return nodes.get(index);\n        }\n\n    }\n\n    public void setLocalPercent(int localPercent) {\n        this.localPercent = localPercent;\n    }\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/setl/lb/RoundRobinLoadBalance.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.setl.lb;\n\nimport java.util.List;\nimport java.util.concurrent.atomic.AtomicInteger;\n\nimport org.apache.commons.lang.math.RandomUtils;\n\nimport com.alibaba.otter.shared.arbitrate.impl.config.ArbitrateConfigUtils;\nimport com.alibaba.otter.shared.common.model.config.node.Node;\n\n/**\n * Round-Robin的负载均衡实现 <br/>\n * \n * @author jianghang 2011-8-19 上午10:25:36\n */\npublic abstract class RoundRobinLoadBalance extends AbstractLoadBalance {\n\n    private static final int MAX_ROUND    = 1000 * 1000;\n    private AtomicInteger    round        = new AtomicInteger(0);\n    private int              localPercent = 90;                  //local优先返回的权重，百分比\n\n    public RoundRobinLoadBalance(Long pipelineId){\n        super(pipelineId);\n    }\n\n    public Node next() throws InterruptedException {\n        List<Node> nodes = getAliveNodes();\n        if (nodes == null || nodes.size() == 0) {\n            return null;\n        }\n\n        Long nid = ArbitrateConfigUtils.getCurrentNid();\n        Node current = new Node();\n        current.setId(nid);\n\n        // 判断一下是否优先返回local\n        boolean existLocal = nodes.remove(current);\n        if (existLocal && nodes.size() == 0) {//如果只有它自己\n            return current;\n        } else if (existLocal && RandomUtils.nextInt(100) < localPercent) {//计算一下百分比\n            return current;\n        } else {\n            int number = round.incrementAndGet();\n            if (number > MAX_ROUND) {\n                number = round.getAndSet(0);\n            }\n            int index = (int) (number % nodes.size());\n            return nodes.get(index);\n        }\n\n    }\n\n    public void setLocalPercent(int localPercent) {\n        this.localPercent = localPercent;\n    }\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/setl/lb/StickLoadBalance.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.setl.lb;\n\nimport java.util.List;\nimport java.util.concurrent.atomic.AtomicLong;\n\nimport org.apache.commons.lang.math.RandomUtils;\n\nimport com.alibaba.otter.shared.arbitrate.impl.config.ArbitrateConfigUtils;\nimport com.alibaba.otter.shared.common.model.config.node.Node;\n\n/**\n * 提供一种固定粘性的{@linkplain LoadBalance}的机制，因为多个load落在同一个jvm，可以减少通过zookeeper的仲裁调度交互\n * \n * @author jianghang 2013-2-25 下午10:51:56\n * @version 4.1.7\n */\npublic abstract class StickLoadBalance extends AbstractLoadBalance implements LoadBalance {\n\n    private int        stickPercent   = 100;              // 固定返回某一个节点的百分比\n    private long       lastNid        = -1;               // 上一次选择的节点\n    private int        randomThresold = 100;              // 超过100个批次后重新选择\n    private AtomicLong stickCount     = new AtomicLong(0);\n\n    public StickLoadBalance(Long pipelineId){\n        super(pipelineId);\n    }\n\n    public Node next() throws InterruptedException {\n        List<Node> nodes = getAliveNodes();\n\n        if (nodes == null || nodes.size() == 0) {\n            return null;\n        }\n\n        Long nid = ArbitrateConfigUtils.getCurrentNid();\n        Node current = new Node();\n        current.setId(nid);\n\n        // 判断一下是否优先返回local\n        boolean existLocal = nodes.remove(current);\n        if (existLocal && nodes.size() == 0) {// 如果只有它自己\n            return current;\n        } else if (existLocal && RandomUtils.nextInt(100) <= stickPercent) {// 计算一下百分比\n            return current;\n        } else {\n            for (Node node : nodes) {\n                if (node.getId().equals(lastNid) && RandomUtils.nextInt(100) <= stickPercent) {\n                    lastNid = node.getId();\n                    long count = stickCount.incrementAndGet();\n                    if (count > randomThresold) {\n                        lastNid = -1; // 进入下一轮的重新选择，避免挂死一个节点\n                        stickCount.set(0);\n                    }\n                    return node;\n                }\n            }\n\n            // 如果没找到对应lastNid对应的信息，可能节点挂了，则随机选一个\n            int index = RandomUtils.nextInt(nodes.size());\n            Node node = nodes.get(index);\n            lastNid = node.getId();\n            return node;\n        }\n\n    }\n\n    public void setStickPercent(int stickPercent) {\n        this.stickPercent = stickPercent;\n    }\n\n    public void setRandomThresold(int randomThresold) {\n        this.randomThresold = randomThresold;\n    }\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/setl/lb/TransformRandomLoadBanlance.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.setl.lb;\n\nimport java.util.List;\n\nimport com.alibaba.otter.shared.common.model.config.node.Node;\n\n/**\n * transform模块的负载均衡实现\n * \n * @author jianghang 2011-9-20 下午01:24:22\n * @version 4.0.0\n */\npublic class TransformRandomLoadBanlance extends RandomLoadBalance {\n\n    public TransformRandomLoadBanlance(Long pipelineId){\n        super(pipelineId);\n    }\n\n    @Override\n    public List<Node> getAliveNodes() {\n        return getTransformAliveNodes();\n    }\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/setl/lb/TransformRoundRobinLoadBalance.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.setl.lb;\n\nimport java.util.List;\n\nimport com.alibaba.otter.shared.common.model.config.node.Node;\n\n/**\n * transform模块的负载均衡实现\n * \n * @author jianghang 2011-9-20 下午01:24:22\n * @version 4.0.0\n */\npublic class TransformRoundRobinLoadBalance extends RoundRobinLoadBalance {\n\n    public TransformRoundRobinLoadBalance(Long pipelineId){\n        super(pipelineId);\n    }\n\n    @Override\n    public List<Node> getAliveNodes() {\n        return getTransformAliveNodes();\n    }\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/setl/lb/TransformStickLoadBalance.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.setl.lb;\n\nimport java.util.List;\n\nimport com.alibaba.otter.shared.common.model.config.node.Node;\n\npublic class TransformStickLoadBalance extends StickLoadBalance {\n\n    public TransformStickLoadBalance(Long pipelineId){\n        super(pipelineId);\n    }\n\n    public List<Node> getAliveNodes() {\n        return getTransformAliveNodes();\n    }\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/setl/memory/ExtractMemoryArbitrateEvent.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.setl.memory;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.util.Assert;\n\nimport com.alibaba.otter.shared.arbitrate.impl.setl.ArbitrateFactory;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.ExtractArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.monitor.PermitMonitor;\nimport com.alibaba.otter.shared.arbitrate.model.EtlEventData;\nimport com.alibaba.otter.shared.arbitrate.model.TerminEventData.TerminType;\nimport com.alibaba.otter.shared.common.model.config.channel.ChannelStatus;\nimport com.alibaba.otter.shared.common.model.config.enums.StageType;\n\n/**\n * 基于memory的仲裁器实现\n * \n * @author jianghang 2012-9-27 下午10:05:08\n * @version 4.1.0\n */\npublic class ExtractMemoryArbitrateEvent implements ExtractArbitrateEvent {\n\n    private static final Logger logger = LoggerFactory.getLogger(ExtractMemoryArbitrateEvent.class);\n\n    public EtlEventData await(Long pipelineId) throws InterruptedException {\n        Assert.notNull(pipelineId);\n\n        PermitMonitor permitMonitor = ArbitrateFactory.getInstance(pipelineId, PermitMonitor.class);\n        permitMonitor.waitForPermit();// 阻塞等待授权\n\n        MemoryStageController stageController = ArbitrateFactory.getInstance(pipelineId, MemoryStageController.class);\n        Long processId = stageController.waitForProcess(StageType.EXTRACT); // 符合条件的processId\n\n        ChannelStatus status = permitMonitor.getChannelPermit();\n        if (status.isStart()) {// 即时查询一下当前的状态，状态随时可能会变\n            return stageController.getLastData(processId);\n        } else {\n            logger.warn(\"pipelineId[{}] extract ignore processId[{}] by status[{}],rollback now\",\n                new Object[] { pipelineId,\n                    processId, status });\n            // 进行ROLLBACK，触发释放下processId，信号量及EventStore里面的读位置点。\n            // 1)因为MemoryStageController的load是等待processId最小值完成Tranform才继续，如果这里不释放，会一直卡死等待\n            // 2)SELECT信号量消耗完selectTask任务会停止3)EventStore里面的读位置点不回置，如果正好队列已经满并且读取了最后，BINLOG新的数据进不来\n            stageController.termin(TerminType.ROLLBACK);\n            return await(pipelineId);// 递归调用\n        }\n    }\n\n    public void single(EtlEventData data) {\n        Assert.notNull(data);\n        MemoryStageController stageController = ArbitrateFactory.getInstance(data.getPipelineId(),\n                                                                             MemoryStageController.class);\n        stageController.single(StageType.EXTRACT, data);// 通知下一个节点\n    }\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/setl/memory/LoadMemoryArbitrateEvent.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.setl.memory;\n\nimport java.util.Date;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.util.Assert;\n\nimport com.alibaba.otter.shared.arbitrate.impl.config.ArbitrateConfigUtils;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.ArbitrateFactory;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.LoadArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.monitor.PermitMonitor;\nimport com.alibaba.otter.shared.arbitrate.model.EtlEventData;\nimport com.alibaba.otter.shared.arbitrate.model.TerminEventData;\nimport com.alibaba.otter.shared.arbitrate.model.TerminEventData.TerminType;\nimport com.alibaba.otter.shared.common.model.config.channel.ChannelStatus;\nimport com.alibaba.otter.shared.common.model.config.enums.StageType;\n\n/**\n * 基于memory的仲裁器实现\n * \n * @author jianghang 2012-9-27 下午10:05:08\n * @version 4.1.0\n */\npublic class LoadMemoryArbitrateEvent implements LoadArbitrateEvent {\n\n    private static final Logger        logger = LoggerFactory.getLogger(LoadMemoryArbitrateEvent.class);\n    private TerminMemoryArbitrateEvent terminEvent;\n\n    public EtlEventData await(Long pipelineId) throws InterruptedException {\n        Assert.notNull(pipelineId);\n\n        PermitMonitor permitMonitor = ArbitrateFactory.getInstance(pipelineId, PermitMonitor.class);\n        permitMonitor.waitForPermit();// 阻塞等待授权\n\n        MemoryStageController stageController = ArbitrateFactory.getInstance(pipelineId, MemoryStageController.class);\n        Long processId = stageController.waitForProcess(StageType.LOAD); // 符合条件的processId\n\n        ChannelStatus status = permitMonitor.getChannelPermit();\n        if (status.isStart()) {// 即时查询一下当前的状态，状态随时可能会变\n            return stageController.getLastData(processId);\n        } else {\n            logger.warn(\"pipelineId[{}] load ignore processId[{}] by status[{}],rollback now\",\n                new Object[] { pipelineId, processId,\n                    status });\n            // 进行ROLLBACK，触发释放下processId，信号量及EventStore里面的读位置点。\n            // 1)因为MemoryStageController的load是等待processId最小值完成Tranform才继续，如果这里不释放，会一直卡死等待\n            // 2)信号量消耗完selectTask任务停止，3)EventStore里面的读位置点不回置，如果正好队列已经满并且读取了最后，BINLOG新的数据进不来\n            stageController.termin(TerminType.ROLLBACK);\n            return await(pipelineId);// 递归调用\n        }\n    }\n\n    public void single(EtlEventData data) {\n        Assert.notNull(data);\n        data.setEndTime(new Date().getTime());// 返回当前时间\n        MemoryStageController stageController = ArbitrateFactory.getInstance(data.getPipelineId(),\n                                                                             MemoryStageController.class);\n        boolean result = stageController.single(StageType.LOAD, data);// 通知下一个节点\n        if (result) {// 可能已经被rollback了，需要直接忽略\n            // 调用Termin信号\n            TerminEventData termin = new TerminEventData();\n            termin.setPipelineId(data.getPipelineId());\n            termin.setProcessId(data.getProcessId());\n            termin.setStartTime(data.getStartTime());\n            termin.setEndTime(data.getEndTime());\n            termin.setFirstTime(data.getFirstTime());\n            termin.setNumber(data.getNumber());\n            termin.setBatchId(data.getBatchId());\n            termin.setSize(data.getSize());\n            termin.setExts(data.getExts());\n            termin.setType(TerminType.NORMAL);\n            termin.setCode(\"setl\");\n            termin.setDesc(\"\");\n            termin.setCurrNid(ArbitrateConfigUtils.getCurrentNid());\n            terminEvent.single(termin);\n        }\n    }\n\n    public void setTerminEvent(TerminMemoryArbitrateEvent terminEvent) {\n        this.terminEvent = terminEvent;\n    }\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/setl/memory/MemoryStageController.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.setl.memory;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.BlockingQueue;\nimport java.util.concurrent.LinkedBlockingQueue;\nimport java.util.concurrent.atomic.AtomicLong;\n\nimport org.springframework.util.CollectionUtils;\n\nimport com.alibaba.otter.shared.arbitrate.impl.config.ArbitrateConfigUtils;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.ArbitrateLifeCycle;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.helper.ReplyProcessQueue;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.helper.StageProgress;\nimport com.alibaba.otter.shared.arbitrate.model.EtlEventData;\nimport com.alibaba.otter.shared.arbitrate.model.TerminEventData;\nimport com.alibaba.otter.shared.arbitrate.model.TerminEventData.TerminType;\nimport com.alibaba.otter.shared.common.model.config.enums.StageType;\nimport com.google.common.base.Function;\nimport com.google.common.collect.MapMaker;\nimport com.google.common.collect.OtterMigrateMap;\n\n/**\n * @author jianghang 2012-9-27 下午10:12:35\n * @version 4.1.0\n */\npublic class MemoryStageController extends ArbitrateLifeCycle {\n\n    private AtomicLong                        atomicMaxProcessId = new AtomicLong(0);\n    private Map<StageType, ReplyProcessQueue> replys;\n    private Map<Long, StageProgress>          progress;\n    private BlockingQueue<TerminEventData>    termins;\n    private StageProgress                     nullProgress       = new StageProgress();\n\n    public MemoryStageController(Long pipelineId){\n        super(pipelineId);\n\n        replys = OtterMigrateMap.makeComputingMap(new Function<StageType, ReplyProcessQueue>() {\n\n            public ReplyProcessQueue apply(StageType input) {\n                int size = ArbitrateConfigUtils.getParallelism(getPipelineId()) * 10;\n                if (size < 100) {\n                    size = 100;\n                }\n                return new ReplyProcessQueue(size);\n            }\n        });\n\n        progress = new MapMaker().makeMap();\n        termins = new LinkedBlockingQueue<TerminEventData>(20);\n    }\n\n    public Long waitForProcess(StageType stage) throws InterruptedException {\n        if (stage.isSelect() && !replys.containsKey(stage)) {\n            initSelect();\n        }\n\n        Long processId = replys.get(stage).take();\n        if (stage.isSelect()) {// select一旦分出processId，就需要在progress中记录一笔，用于判断谁是最小的一个processId\n            progress.put(processId, nullProgress);\n        }\n\n        return processId;\n    }\n\n    public EtlEventData getLastData(Long processId) {\n        return progress.get(processId).getData();\n    }\n\n    public synchronized void destory() {\n        replys.clear();\n        progress.clear();\n    }\n\n    public synchronized void clearProgress(Long processId) {\n        progress.remove(processId);\n    }\n\n    /**\n     * 处理异常termin结束\n     */\n    public synchronized void termin(TerminType type) {\n        // 构建termin信号\n        List<Long> processIds = new ArrayList<Long>(progress.keySet());\n        Collections.sort(processIds);// 做一下排序\n        for (Long processId : processIds) {\n            EtlEventData eventData = progress.get(processId).getData();\n\n            TerminEventData data = new TerminEventData();\n            data.setPipelineId(getPipelineId());\n            data.setType(type);\n            data.setCode(\"channel\");\n            data.setDesc(type.toString());\n            data.setProcessId(processId);\n            if (eventData != null) {\n                data.setBatchId(eventData.getBatchId());\n                data.setCurrNid(eventData.getCurrNid());\n                data.setStartTime(eventData.getStartTime());\n                data.setEndTime(eventData.getEndTime());\n                data.setFirstTime(eventData.getFirstTime());\n                data.setNumber(eventData.getNumber());\n                data.setSize(eventData.getSize());\n                data.setExts(eventData.getExts());\n            }\n            offerTermin(data);\n            progress.remove(processId);\n        }\n\n        // 重新初始化一下select调度\n        initSelect();\n    }\n\n    public synchronized boolean single(StageType stage, EtlEventData etlEventData) {\n        boolean result = false;\n        switch (stage) {\n            case SELECT:\n                if (progress.containsKey(etlEventData.getProcessId())) {// 可能发生了rollback，对应的progress已经被废弃\n                    progress.put(etlEventData.getProcessId(), new StageProgress(stage, etlEventData));\n                    replys.get(StageType.EXTRACT).offer(etlEventData.getProcessId());\n                    result = true;\n                }\n                break;\n            case EXTRACT:\n                if (progress.containsKey(etlEventData.getProcessId())) {\n                    progress.put(etlEventData.getProcessId(), new StageProgress(stage, etlEventData));\n                    replys.get(StageType.TRANSFORM).offer(etlEventData.getProcessId());\n                    result = true;\n                }\n                break;\n            case TRANSFORM:\n                if (progress.containsKey(etlEventData.getProcessId())) {\n                    progress.put(etlEventData.getProcessId(), new StageProgress(stage, etlEventData));\n                    result = true;\n                }\n                // 并不是立即触发，通知最小的一个process启动\n                computeNextLoad();\n                break;\n            case LOAD:\n                Object removed = progress.remove(etlEventData.getProcessId());\n                // 并不是立即触发，通知下一个最小的一个process启动\n                computeNextLoad();\n                // 一个process完成了，自动添加下一个process\n                if (removed != null) {\n                    replys.get(StageType.SELECT).offer(atomicMaxProcessId.incrementAndGet());\n                    result = true;\n                }\n                break;\n            default:\n                break;\n        }\n\n        return result;\n    }\n\n    public void offerTermin(TerminEventData data) {\n        try {\n            termins.put(data);\n        } catch (InterruptedException e) {\n            // ignore\n        }\n    }\n\n    public void ackTermin(TerminEventData data) {\n        // do nothing\n    }\n\n    public int sizeTermin() {\n        return termins.size();\n    }\n\n    public TerminEventData waitTermin() throws InterruptedException {\n        return termins.take();\n    }\n\n    private synchronized void initSelect() {\n        // 第一次/出现ROLLBACK/RESTART事件，删除了所有调度信号后，重新初始化一下select\n        // stage的数据，初始大小为并行度大小\n        // 后续的select的reply队列变化，由load single时直接添加\n        ReplyProcessQueue queue = replys.get(StageType.SELECT);\n        int parallelism = ArbitrateConfigUtils.getParallelism(getPipelineId());\n        while (parallelism-- > 0 && queue.size() <= parallelism) {\n            queue.offer(atomicMaxProcessId.incrementAndGet());\n        }\n    }\n\n    /**\n     * 计算下一个load的processId\n     */\n    private void computeNextLoad() {\n        Long processId = getMinTransformedProcessId();\n        if (processId != null) {\n            replys.get(StageType.LOAD).offer(processId);\n        }\n    }\n\n    /**\n     * 获取最小一个符合条件的processId\n     */\n    private synchronized Long getMinTransformedProcessId() {\n        if (!CollectionUtils.isEmpty(progress)) {\n            Long processId = Collections.min(progress.keySet());\n            StageProgress stage = progress.get(processId);\n            // stage可能为空，针对select未完成时，对应的值就为null\n            if (stage != null && stage != nullProgress && stage.getStage().isTransform()) {\n                return processId;\n            }\n        }\n\n        return null;\n    }\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/setl/memory/SelectMemoryArbitrateEvent.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.setl.memory;\n\nimport java.util.Date;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.util.Assert;\n\nimport com.alibaba.otter.shared.arbitrate.impl.config.ArbitrateConfigUtils;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.ArbitrateFactory;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.SelectArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.monitor.PermitMonitor;\nimport com.alibaba.otter.shared.arbitrate.model.EtlEventData;\nimport com.alibaba.otter.shared.arbitrate.model.TerminEventData.TerminType;\nimport com.alibaba.otter.shared.common.model.config.channel.ChannelStatus;\nimport com.alibaba.otter.shared.common.model.config.enums.StageType;\n\n/**\n * 基于memory的仲裁器实现\n * \n * @author jianghang 2012-9-27 下午10:05:08\n * @version 4.1.0\n */\npublic class SelectMemoryArbitrateEvent implements SelectArbitrateEvent {\n\n    private static final Logger logger = LoggerFactory.getLogger(SelectMemoryArbitrateEvent.class);\n\n    public EtlEventData await(Long pipelineId) throws InterruptedException {\n        Assert.notNull(pipelineId);\n\n        PermitMonitor permitMonitor = ArbitrateFactory.getInstance(pipelineId, PermitMonitor.class);\n        permitMonitor.waitForPermit();// 阻塞等待授权\n\n        MemoryStageController stageController = ArbitrateFactory.getInstance(pipelineId, MemoryStageController.class);\n        Long processId = stageController.waitForProcess(StageType.SELECT); // 符合条件的processId\n\n        ChannelStatus status = permitMonitor.getChannelPermit();\n        if (status.isStart()) {// 即时查询一下当前的状态，状态随时可能会变\n            EtlEventData eventData = new EtlEventData();\n            eventData.setPipelineId(pipelineId);\n            eventData.setProcessId(processId);\n            eventData.setStartTime(new Date().getTime());// 返回当前时间\n            Long nid = ArbitrateConfigUtils.getCurrentNid();\n            eventData.setCurrNid(nid);\n            eventData.setNextNid(nid);\n            return eventData;// 只有这一条路返回\n        } else {\n            logger.warn(\"pipelineId[{}] select ignore processId[{}] by status[{}],rollback now\",\n                new Object[] { pipelineId,\n                    processId, status });\n            // 进行ROLLBACK，触发释放下processId，信号量及EventStore里面的读位置点。\n            // 1)因为MemoryStageController的load是等待processId最小值完成Tranform才继续，如果这里不释放，会一直卡死等待\n            // 2)信号量消耗完selectTask任务停止3)EventStore里面的读位置点不回置，如果正好队列已经满并且读取了最后，BINLOG新的数据进不来\n            stageController.termin(TerminType.ROLLBACK);\n            return await(pipelineId);// 递归调用\n        }\n    }\n\n    public void single(EtlEventData data) {\n        Assert.notNull(data);\n        MemoryStageController stageController = ArbitrateFactory.getInstance(data.getPipelineId(),\n                                                                             MemoryStageController.class);\n        stageController.single(StageType.SELECT, data);// 通知下一个节点\n    }\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/setl/memory/TerminMemoryArbitrateEvent.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.setl.memory;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.util.Assert;\n\nimport com.alibaba.otter.shared.arbitrate.exception.ArbitrateException;\nimport com.alibaba.otter.shared.arbitrate.impl.communication.ArbitrateCommmunicationClient;\nimport com.alibaba.otter.shared.arbitrate.impl.config.ArbitrateConfigUtils;\nimport com.alibaba.otter.shared.arbitrate.impl.manage.ChannelArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.ArbitrateFactory;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.TerminArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.zookeeper.termin.WarningTerminProcess;\nimport com.alibaba.otter.shared.arbitrate.model.TerminEventData;\nimport com.alibaba.otter.shared.arbitrate.model.TerminEventData.TerminType;\nimport com.alibaba.otter.shared.common.model.config.channel.Channel;\nimport com.alibaba.otter.shared.communication.model.arbitrate.StopChannelEvent;\n\n/**\n * 基于内存版本的termin信号处理\n * \n * @author jianghang 2012-9-27 下午11:30:13\n * @version 4.1.0\n */\npublic class TerminMemoryArbitrateEvent implements TerminArbitrateEvent {\n\n    private static final Logger           logger = LoggerFactory.getLogger(TerminMemoryArbitrateEvent.class);\n    private ArbitrateCommmunicationClient arbitrateCommmunicationClient;\n    private WarningTerminProcess          warningTerminProcess;\n    private ChannelArbitrateEvent         channelEvent;\n\n    public TerminEventData await(Long pipelineId) throws InterruptedException {\n        Assert.notNull(pipelineId);\n        MemoryStageController stageController = ArbitrateFactory.getInstance(pipelineId, MemoryStageController.class);\n        TerminEventData eventData = stageController.waitTermin();\n        if (logger.isDebugEnabled()) {\n            logger.debug(\"## await pipeline[{}] processId[{}] is termin\", pipelineId, eventData.getProcessId());\n        }\n\n        return eventData;\n    }\n\n    public void exhaust(Long pipelineId) {\n        Assert.notNull(pipelineId);\n        MemoryStageController stageController = ArbitrateFactory.getInstance(pipelineId, MemoryStageController.class);\n        int size = stageController.sizeTermin();\n        try {\n            for (int i = 0; i < size; i++) {\n                TerminEventData data = stageController.waitTermin();\n                ack(data);\n            }\n        } catch (InterruptedException e) {\n            throw new ArbitrateException(e);\n        }\n    }\n\n    public void single(TerminEventData data) {\n        // 正向处理\n        final TerminType type = data.getType();\n        MemoryStageController stageController = ArbitrateFactory.getInstance(data.getPipelineId(),\n                                                                             MemoryStageController.class);\n        if (type.isNormal()) {\n            Assert.notNull(data.getProcessId());\n            stageController.offerTermin(data);\n        } else if (type.isWarning()) {\n            warningTerminProcess.process(data); // warn单独处理，不需要关闭相关的pipeline\n        } else {\n            // 内存版可以简化处理rollback/restart/shutdown模型，不需要进行process的termin操作处理\n            Channel channel = ArbitrateConfigUtils.getChannel(data.getPipelineId());\n            if (data.getType().isRollback()) {\n                boolean paused = channelEvent.pause(channel.getId(), false);\n                if (paused) {// 如果pause成功，则发送报警信息\n                    warningTerminProcess.process(data);\n                }\n            } else if (data.getType().isShutdown()) {\n                boolean shutdowned = channelEvent.stop(channel.getId(), false);\n                // 发送报警信息\n                if (shutdowned) {\n                    warningTerminProcess.process(data);\n                }\n                // 发送关闭命令给manager\n                StopChannelEvent event = new StopChannelEvent();\n                event.setChannelId(channel.getId());\n                arbitrateCommmunicationClient.callManager(event);\n            } else if (data.getType().isRestart()) {\n                boolean restarted = channelEvent.restart(channel.getId(), false);\n                // 发送报警信息\n                if (restarted) {\n                    warningTerminProcess.process(data);\n                }\n            }\n\n            stageController.termin(data.getType());// 内存中构造异常termin信号返回\n        }\n\n    }\n\n    public void ack(TerminEventData data) {\n        MemoryStageController stageController = ArbitrateFactory.getInstance(data.getPipelineId(),\n                                                                             MemoryStageController.class);\n        stageController.ackTermin(data);\n    }\n\n    public int size(Long pipelineId) {\n        MemoryStageController stageController = ArbitrateFactory.getInstance(pipelineId, MemoryStageController.class);\n        return stageController.sizeTermin();\n    }\n\n    // ================== setter / getter ===================\n    public void setArbitrateCommmunicationClient(ArbitrateCommmunicationClient arbitrateCommmunicationClient) {\n        this.arbitrateCommmunicationClient = arbitrateCommmunicationClient;\n    }\n\n    public void setWarningTerminProcess(WarningTerminProcess warningTerminProcess) {\n        this.warningTerminProcess = warningTerminProcess;\n    }\n\n    public void setChannelEvent(ChannelArbitrateEvent channelEvent) {\n        this.channelEvent = channelEvent;\n    }\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/setl/memory/TransformMemoryArbitrateEvent.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.setl.memory;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.util.Assert;\n\nimport com.alibaba.otter.shared.arbitrate.impl.setl.ArbitrateFactory;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.TransformArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.monitor.PermitMonitor;\nimport com.alibaba.otter.shared.arbitrate.model.EtlEventData;\nimport com.alibaba.otter.shared.arbitrate.model.TerminEventData.TerminType;\nimport com.alibaba.otter.shared.common.model.config.channel.ChannelStatus;\nimport com.alibaba.otter.shared.common.model.config.enums.StageType;\n\n/**\n * 基于memory的仲裁器实现\n * \n * @author jianghang 2012-9-27 下午10:05:08\n * @version 4.1.0\n */\npublic class TransformMemoryArbitrateEvent implements TransformArbitrateEvent {\n\n    private static final Logger logger = LoggerFactory.getLogger(TransformMemoryArbitrateEvent.class);\n\n    public EtlEventData await(Long pipelineId) throws InterruptedException {\n        Assert.notNull(pipelineId);\n\n        PermitMonitor permitMonitor = ArbitrateFactory.getInstance(pipelineId, PermitMonitor.class);\n        permitMonitor.waitForPermit();// 阻塞等待授权\n\n        MemoryStageController stageController = ArbitrateFactory.getInstance(pipelineId, MemoryStageController.class);\n        Long processId = stageController.waitForProcess(StageType.TRANSFORM); // 符合条件的processId\n\n        ChannelStatus status = permitMonitor.getChannelPermit();\n        if (status.isStart()) {// 即时查询一下当前的状态，状态随时可能会变\n            return stageController.getLastData(processId);\n        } else {\n            logger.warn(\"pipelineId[{}] transform ignore processId[{}] by status[{}],rollback now\",\n                new Object[] { pipelineId,\n                    processId, status });\n            // 进行ROLLBACK，触发释放下processId，信号量及EventStore里面的读位置点。\n            // 1)因为MemoryStageController的load是等待processId最小值完成Tranform才继续，如果这里不释放，会一直卡死等待\n            // 2)信号量消耗完selectTask任务停止3)EventStore里面的读位置点不回置，如果正好队列已经满并且读取了最后，BINLOG新的数据进不来\n            stageController.termin(TerminType.ROLLBACK);\n            return await(pipelineId);// 递归调用\n        }\n    }\n\n    public void single(EtlEventData data) {\n        Assert.notNull(data);\n        MemoryStageController stageController = ArbitrateFactory.getInstance(data.getPipelineId(),\n                                                                             MemoryStageController.class);\n        stageController.single(StageType.TRANSFORM, data);// 通知下一个节点\n    }\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/setl/monitor/MainstemMonitor.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.setl.monitor;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.ScheduledExecutorService;\nimport java.util.concurrent.TimeUnit;\n\nimport org.I0Itec.zkclient.IZkDataListener;\nimport org.I0Itec.zkclient.exception.ZkException;\nimport org.I0Itec.zkclient.exception.ZkInterruptedException;\nimport org.I0Itec.zkclient.exception.ZkNoNodeException;\nimport org.I0Itec.zkclient.exception.ZkNodeExistsException;\nimport org.apache.commons.lang.ClassUtils;\nimport org.apache.zookeeper.CreateMode;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.slf4j.MDC;\nimport org.springframework.util.Assert;\n\nimport com.alibaba.otter.shared.arbitrate.exception.ArbitrateException;\nimport com.alibaba.otter.shared.arbitrate.impl.ArbitrateConstants;\nimport com.alibaba.otter.shared.arbitrate.impl.config.ArbitrateConfigUtils;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.ArbitrateFactory;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.ArbitrateLifeCycle;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.helper.StagePathUtils;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.monitor.listener.MainstemListener;\nimport com.alibaba.otter.shared.arbitrate.impl.zookeeper.ZooKeeperClient;\nimport com.alibaba.otter.shared.arbitrate.model.MainStemEventData;\nimport com.alibaba.otter.shared.common.model.config.channel.ChannelStatus;\nimport com.alibaba.otter.shared.common.utils.JsonUtils;\nimport com.alibaba.otter.shared.common.utils.lock.BooleanMutex;\nimport com.alibaba.otter.shared.common.utils.zookeeper.ZkClientx;\nimport com.google.common.collect.Lists;\n\n/**\n * 主备切换控制器，active的只有一位，所有的standy都有平等的选择权\n * \n * <pre>\n * 1. active一旦产生，出现瞬断，在规定的时间内，其享有优先权\n * 2. active一旦产生，如果主动释放其active权利，其他的standby的节点就有机会立即参与选举\n * </pre>\n * \n * @author jianghang 2012-10-1 下午02:19:22\n * @version 4.1.0\n */\npublic class MainstemMonitor extends ArbitrateLifeCycle implements Monitor {\n\n    private static final Logger        logger       = LoggerFactory.getLogger(MainstemMonitor.class);\n    private ZkClientx                  zookeeper    = ZooKeeperClient.getInstance();\n    private ScheduledExecutorService   delayExector = Executors.newScheduledThreadPool(1);\n    private int                        delayTime    = 5;\n    private volatile MainStemEventData activeData;\n    private IZkDataListener            dataListener;\n    private BooleanMutex               mutex        = new BooleanMutex(false);\n    private volatile boolean           release      = false;\n    private List<MainstemListener>     listeners    = Collections.synchronizedList(new ArrayList<MainstemListener>());\n\n    public MainstemMonitor(Long pipelineId){\n        super(pipelineId);\n        // initMainstem();\n        dataListener = new IZkDataListener() {\n\n            public void handleDataChange(String dataPath, Object data) throws Exception {\n                MDC.put(ArbitrateConstants.splitPipelineLogFileKey, String.valueOf(getPipelineId()));\n                MainStemEventData mainStemData = JsonUtils.unmarshalFromByte((byte[]) data, MainStemEventData.class);\n                if (!isMine(mainStemData.getNid())) {\n                    mutex.set(false);\n                }\n\n                if (!mainStemData.isActive() && isMine(mainStemData.getNid())) { // 说明出现了主动释放的操作，并且本机之前是active\n                    release = true;\n                    releaseMainstem();// 彻底释放mainstem\n                }\n\n                activeData = (MainStemEventData) mainStemData;\n            }\n\n            public void handleDataDeleted(String dataPath) throws Exception {\n                MDC.put(ArbitrateConstants.splitPipelineLogFileKey, String.valueOf(getPipelineId()));\n                mutex.set(false);\n                if (!release && isMine(activeData.getNid())) {\n                    // 如果上一次active的状态就是本机，则即时触发一下active抢占\n                    initMainstem();\n                    // } else if (!isMine(activeData.getNid()) && !activeData.isActive()) {\n                    // // 针对其他的节点，如果发现上一次的mainstem状态为非active状态，说明存在手工干预进行mainstem切换，可立马进行抢占mainstem\n                    // initMainstem();\n                } else {\n                    // 否则就是等待delayTime，避免因网络瞬端或者zk异常，导致出现频繁的切换操作\n                    delayExector.schedule(new Runnable() {\n\n                        public void run() {\n                            initMainstem();\n                        }\n                    }, delayTime, TimeUnit.SECONDS);\n                }\n            }\n\n        };\n\n        String path = StagePathUtils.getMainStem(getPipelineId());\n        zookeeper.subscribeDataChanges(path, dataListener);\n        MonitorScheduler.register(this, 5 * 60 * 1000L, 5 * 60 * 1000L); // 5分钟处理一次\n    }\n\n    public void reload() {\n        if (logger.isDebugEnabled()) {\n            logger.debug(\"## reload mainstem pipeline[{}]\", getPipelineId());\n        }\n\n        try {\n            initMainstem();\n        } catch (Exception e) {// 处理下异常\n        }\n    }\n\n    public void initMainstem() {\n        if (isStop()) {\n            return;\n        }\n\n        PermitMonitor permitMonitor = ArbitrateFactory.getInstance(getPipelineId(), PermitMonitor.class);\n        ChannelStatus status = permitMonitor.getChannelPermit(true);\n        if (status.isStop()) {\n            return; // 如果已经关闭则退出\n        }\n\n        Long nid = ArbitrateConfigUtils.getCurrentNid();\n        String path = StagePathUtils.getMainStem(getPipelineId());\n\n        MainStemEventData data = new MainStemEventData();\n        data.setStatus(MainStemEventData.Status.TAKEING);\n        data.setPipelineId(getPipelineId());\n        data.setNid(nid);// 设置当前的nid\n        // 序列化\n        byte[] bytes = JsonUtils.marshalToByte(data);\n        try {\n            mutex.set(false);\n            zookeeper.create(path, bytes, CreateMode.EPHEMERAL);\n            activeData = data;\n            processActiveEnter();// 触发一下事件\n            mutex.set(true);\n        } catch (ZkNodeExistsException e) {\n            bytes = zookeeper.readData(path, true);\n            if (bytes == null) {// 如果不存在节点，立即尝试一次\n                initMainstem();\n            } else {\n                activeData = JsonUtils.unmarshalFromByte(bytes, MainStemEventData.class);\n                if (nid.equals(activeData.getNid())) { // reload时会重复创建，如果是自己就触发一下\n                    mutex.set(true);\n                }\n            }\n        }\n    }\n\n    public void destory() {\n        super.destory();\n\n        String path = StagePathUtils.getMainStem(getPipelineId());\n        zookeeper.unsubscribeDataChanges(path, dataListener);\n\n        delayExector.shutdownNow(); // 关闭调度\n        releaseMainstem();\n        MonitorScheduler.unRegister(this);\n    }\n\n    public boolean releaseMainstem() {\n        if (check()) {\n            String path = StagePathUtils.getMainStem(getPipelineId());\n            zookeeper.delete(path);\n            mutex.set(false);\n            processActiveExit();\n            return true;\n        }\n\n        return false;\n    }\n\n    public MainStemEventData getCurrentActiveData() {\n        return activeData;\n    }\n\n    /**\n     * 阻塞等待自己成为active，如果自己成为active，立马返回\n     * \n     * @throws InterruptedException\n     */\n    public void waitForActive() throws InterruptedException {\n        initMainstem();\n        mutex.get();\n    }\n\n    /**\n     * 检查当前的状态\n     */\n    public boolean check() {\n        String path = StagePathUtils.getMainStem(getPipelineId());\n        try {\n            byte[] bytes = zookeeper.readData(path);\n            Long nid = ArbitrateConfigUtils.getCurrentNid();\n            MainStemEventData eventData = JsonUtils.unmarshalFromByte(bytes, MainStemEventData.class);\n            activeData = eventData;// 更新下为最新值\n            // 检查下nid是否为自己\n            boolean result = nid.equals(eventData.getNid());\n            if (!result) {\n                logger.warn(\"mainstem is running in node[{}] , but not in node[{}]\", eventData.getNid(), nid);\n            }\n            return result;\n        } catch (ZkNoNodeException e) {\n            logger.warn(\"mainstem is not run any in node\");\n            return false;\n        } catch (ZkInterruptedException e) {\n            logger.warn(\"mainstem check is interrupt\");\n            Thread.interrupted();// 清除interrupt标记\n            return check();\n        } catch (ZkException e) {\n            logger.warn(\"mainstem check is failed\");\n            return false;\n        }\n    }\n\n    /**\n     * 更新mainStem的同步状态数据\n     */\n    public void single(MainStemEventData data) {\n        Assert.notNull(data);\n        Long nid = ArbitrateConfigUtils.getCurrentNid();\n        if (!check()) {\n            return;\n        }\n\n        data.setNid(nid);// 设置当前的nid\n        String path = StagePathUtils.getMainStem(data.getPipelineId());\n        byte[] bytes = JsonUtils.marshalToByte(data);// 初始化的数据对象\n        try {\n            zookeeper.writeData(path, bytes);\n        } catch (ZkException e) {\n            throw new ArbitrateException(\"mainStem_single\", data.toString(), e);\n        }\n        activeData = data;\n    }\n\n    // ====================== helper method ======================\n\n    private boolean isMine(Long targetNid) {\n        return targetNid.equals(ArbitrateConfigUtils.getCurrentNid());\n    }\n\n    public void addListener(MainstemListener listener) {\n        if (logger.isDebugEnabled()) {\n            logger.debug(\"## pipeline[{}] add listener [{}]\", ClassUtils.getShortClassName(listener.getClass()));\n        }\n\n        this.listeners.add(listener);\n    }\n\n    public void removeListener(MainstemListener listener) {\n        if (logger.isDebugEnabled()) {\n            logger.debug(\"## remove listener [{}]\", ClassUtils.getShortClassName(listener.getClass()));\n        }\n\n        this.listeners.remove(listener);\n    }\n\n    private void processActiveEnter() {\n        for (final MainstemListener listener : Lists.newArrayList(listeners)) {\n            try {\n                listener.processActiveEnter();\n            } catch (Exception e) {\n                logger.error(\"processSwitchActive failed\", e);\n            }\n        }\n    }\n\n    private void processActiveExit() {\n        // 注意：在处理响应事件时，会调用remove的操作，可能会导致出现java.util.ConcurrentModificationException异常，所以这里拷贝了一份listener\n        for (final MainstemListener listener : Lists.newArrayList(listeners)) {\n            try {\n                listener.processActiveExit();\n            } catch (Exception e) {\n                logger.error(\"processSwitchActive failed\", e);\n            }\n        }\n    }\n\n    public void setDelayTime(int delayTime) {\n        this.delayTime = delayTime;\n    }\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/setl/monitor/Monitor.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.setl.monitor;\n\n/**\n * Arbitrate Monitor的统一接口定义，允许进行数据的reload<br/>\n * 在并发往zookeeper写数据，通过Watcher进行监听时，Watcher响应到重新注册Watcher这段时间的数据不能得到响应， 所以需要定时进行reload，避免死锁\n * \n * @author jianghang 2011-9-19 下午02:51:36\n * @version 4.0.0\n */\npublic interface Monitor {\n\n    public void reload();\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/setl/monitor/MonitorScheduler.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.setl.monitor;\n\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.ScheduledFuture;\nimport java.util.concurrent.ScheduledThreadPoolExecutor;\nimport java.util.concurrent.ThreadPoolExecutor;\nimport java.util.concurrent.TimeUnit;\n\nimport com.alibaba.otter.shared.common.utils.thread.NamedThreadFactory;\n\n/**\n * monitor调度程序,定时调度{@linkplain Monitor}对象\n * \n * @author jianghang 2011-9-19 下午08:55:42\n * @version 4.0.0\n */\npublic class MonitorScheduler {\n\n    private static final Long                    DEFAULT_PERIOD = 60 * 1000L;\n    private static final int                     DEFAULT_POOL   = 10;\n    private static ScheduledThreadPoolExecutor   scheduler      = new ScheduledThreadPoolExecutor(\n                                                                                                  DEFAULT_POOL,\n                                                                                                  new NamedThreadFactory(\n                                                                                                                         \"Arbitrate-Monitor\"),\n                                                                                                  new ThreadPoolExecutor.CallerRunsPolicy());\n\n    private static Map<Monitor, ScheduledFuture> register       = new ConcurrentHashMap<Monitor, ScheduledFuture>(10);\n\n    /**\n     * 注册对应的Monitor对象\n     * \n     * @param monitor\n     */\n    public static void register(final Monitor monitor) {\n        register(monitor, DEFAULT_PERIOD);\n    }\n\n    /**\n     * 注册对应的Monitor对象\n     * \n     * @param monitor\n     */\n    public static void register(final Monitor monitor, Long delay) {\n        register(monitor, delay, DEFAULT_PERIOD);\n    }\n\n    /**\n     * 注册对应的Monitor对象\n     * \n     * @param monitor\n     * @param period 调度周期，单位ms\n     */\n    public static void register(final Monitor monitor, Long delay, Long period) {\n        ScheduledFuture future = scheduler.scheduleAtFixedRate(new Runnable() {\n\n            public void run() {\n                monitor.reload();\n            }\n        }, delay, period, TimeUnit.MILLISECONDS);\n\n        register.put(monitor, future);\n    }\n\n    /**\n     * 取消注册对应的Monitor对象\n     * \n     * @param monitor\n     */\n    public static void unRegister(Monitor monitor) {\n        ScheduledFuture future = register.remove(monitor);\n        if (future != null) {\n            future.cancel(true);// 打断\n        }\n    }\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/setl/monitor/NodeMonitor.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.setl.monitor;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.concurrent.ExecutorService;\n\nimport org.I0Itec.zkclient.IZkChildListener;\nimport org.I0Itec.zkclient.exception.ZkNodeExistsException;\nimport org.apache.commons.lang.ClassUtils;\nimport org.apache.commons.lang.StringUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.alibaba.otter.shared.arbitrate.impl.ArbitrateConstants;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.monitor.listener.NodeListener;\nimport com.alibaba.otter.shared.arbitrate.impl.zookeeper.ZooKeeperClient;\nimport com.alibaba.otter.shared.common.utils.zookeeper.ZkClientx;\n\n/**\n * otter所有node节点监控\n * \n * @author jianghang 2012-8-29 下午01:00:43\n * @version 4.1.0\n */\npublic class NodeMonitor implements Monitor {\n\n    private static final Logger logger     = LoggerFactory.getLogger(NodeMonitor.class);\n\n    private ExecutorService     arbitrateExecutor;\n    private ZkClientx           zookeeper  = ZooKeeperClient.getInstance();\n    private List<NodeListener>  listeners  = Collections.synchronizedList(new ArrayList<NodeListener>());\n    private volatile List<Long> aliveNodes = new ArrayList<Long>();                                      // se模块存活的节点\n    private IZkChildListener    childListener;\n\n    public NodeMonitor(){\n        childListener = new IZkChildListener() {\n\n            public void handleChildChange(String parentPath, List<String> currentChilds) throws Exception {\n                if (currentChilds != null) {\n                    initNodes(currentChilds);\n                }\n            }\n        };\n        List<String> childs = zookeeper.subscribeChildChanges(ArbitrateConstants.NODE_NID_ROOT, childListener);\n        if (childs == null) {//如果为null，代表系统节点为初始化\n            try {\n                zookeeper.createPersistent(ArbitrateConstants.NODE_NID_ROOT, true);\n            } catch (ZkNodeExistsException e) {\n                //ignore \n            }\n\n            childs = zookeeper.getChildren(ArbitrateConstants.NODE_NID_ROOT);\n        }\n\n        initNodes(childs);\n        // syncNodes();// 开始监视node节点的变化\n        MonitorScheduler.register(this);\n    }\n\n    public void reload() {\n        try {\n            initNodes();// 更新数据\n        } catch (Exception e) {\n        }\n\n    }\n\n    public void destory() {\n        listeners.clear();\n\n        zookeeper.unsubscribeChildChanges(ArbitrateConstants.NODE_NID_ROOT, childListener);\n        MonitorScheduler.unRegister(this);\n    }\n\n    /**\n     * 返回当前存活的node列表\n     */\n    public List<Long> getAliveNodes(boolean reload) {\n        if (reload) {\n            initNodes();\n        }\n\n        return new ArrayList<Long>(aliveNodes);\n    }\n\n    /**\n     * 返回当前存活的node列表\n     */\n    public List<Long> getAliveNodes() {\n        return getAliveNodes(false);\n    }\n\n    private void initNodes() {\n        // 获取一下当前存活的所有node节点，再根据对应pipeline关联的node，检查是否在对应的存活列表里\n        List<String> nodes = zookeeper.getChildren(ArbitrateConstants.NODE_NID_ROOT);\n        initNodes(nodes);\n    }\n\n    private synchronized void initNodes(List<String> nodes) {\n        List<Long> nids = new ArrayList<Long>();\n        for (String node : nodes) {\n            if (StringUtils.isNumeric(node)) {\n                nids.add(Long.valueOf(node));\n            }\n        }\n        Collections.sort(nids);\n\n        if (!aliveNodes.equals(nids)) {// 不相同，说明有变化\n            if (logger.isDebugEnabled()) {\n                logger.debug(\"old aliveNodes{} ,current aliveNodes{}\", new Object[] { aliveNodes, nids });\n            }\n\n            aliveNodes = nids; // 切换引用，需设置为volatile保证线程安全&可见性\n            processChanged(nids);// 通知变化\n        }\n    }\n\n    // private void syncNodes() {\n    // try {\n    // List<String> nodes = zookeeper.getChildren(ArbitrateConstants.NODE_NID_ROOT, new AsyncWatcher() {\n    //\n    // public void asyncProcess(WatchedEvent event) {\n    // // 出现session expired/connection losscase下，会触发所有的watcher响应，同时老的watcher会继续保留，所以会导致出现多次watcher响应\n    // boolean dataChanged = event.getType() == EventType.NodeDataChanged\n    // || event.getType() == EventType.NodeDeleted\n    // || event.getType() == EventType.NodeCreated\n    // || event.getType() == EventType.NodeChildrenChanged;\n    // if (dataChanged) {\n    // syncNodes();// 继续关注node节点变化\n    // }\n    // }\n    // });\n    //\n    // initNodes(nodes);\n    // } catch (KeeperException e) {\n    // syncNodes();\n    // logger.error(\"\", e);\n    // } catch (InterruptedException e) {\n    // // ignore\n    // }\n    // }\n\n    // ======================== listener处理 ======================\n\n    public void addListener(NodeListener listener) {\n        if (logger.isDebugEnabled()) {\n            logger.debug(\"## pipeline[{}] add listener [{}]\", ClassUtils.getShortClassName(listener.getClass()));\n        }\n\n        this.listeners.add(listener);\n    }\n\n    public void removeListener(NodeListener listener) {\n        if (logger.isDebugEnabled()) {\n            logger.debug(\"## remove listener [{}]\", ClassUtils.getShortClassName(listener.getClass()));\n        }\n\n        this.listeners.remove(listener);\n    }\n\n    private void processChanged(final List<Long> nodes) {\n        for (final NodeListener listener : listeners) {\n            // 异步处理\n            arbitrateExecutor.submit(new Runnable() {\n\n                public void run() {\n                    listener.processChanged(nodes);\n                }\n            });\n        }\n    }\n\n    // ========= setter ========\n\n    public void setArbitrateExecutor(ExecutorService arbitrateExecutor) {\n        this.arbitrateExecutor = arbitrateExecutor;\n    }\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/setl/monitor/PermitMonitor.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.setl.monitor;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.concurrent.ExecutorService;\n\nimport org.I0Itec.zkclient.IZkDataListener;\nimport org.I0Itec.zkclient.exception.ZkException;\nimport org.I0Itec.zkclient.exception.ZkNoNodeException;\nimport org.apache.commons.lang.ClassUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.slf4j.MDC;\n\nimport com.alibaba.otter.shared.arbitrate.impl.ArbitrateConstants;\nimport com.alibaba.otter.shared.arbitrate.impl.config.ArbitrateConfigUtils;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.ArbitrateLifeCycle;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.helper.StagePathUtils;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.monitor.listener.PermitListener;\nimport com.alibaba.otter.shared.arbitrate.impl.zookeeper.ZooKeeperClient;\nimport com.alibaba.otter.shared.arbitrate.model.MainStemEventData;\nimport com.alibaba.otter.shared.common.model.config.channel.ChannelStatus;\nimport com.alibaba.otter.shared.common.utils.JsonUtils;\nimport com.alibaba.otter.shared.common.utils.lock.BooleanMutex;\nimport com.alibaba.otter.shared.common.utils.zookeeper.ZkClientx;\n\n/**\n * 同步任务状态的监控\n * \n * <pre>\n * 监控数据内容：\n * 1. channel的status状态\n * 2. 当前pipeline的mainStem状态 &　反向同步的pipeline的mainStem状态\n * </pre>\n * \n * @author jianghang\n */\npublic class PermitMonitor extends ArbitrateLifeCycle implements Monitor {\n\n    private static final Logger      logger                 = LoggerFactory.getLogger(PermitMonitor.class);\n\n    private ZkClientx                zookeeper              = ZooKeeperClient.getInstance();\n    private ChannelStatus            channelStatus          = ChannelStatus.STOP;                                           // 标识channel的状态\n    private MainStemEventData.Status mainStemStatus         = MainStemEventData.Status.TAKEING;                             // 当前pipeline的mainStem状态\n    private MainStemEventData.Status oppositeMainStemStatus = MainStemEventData.Status.TAKEING;                             // 反方向的pipeline的mainStem状态\n\n    private ExecutorService          arbitrateExecutor;\n    private BooleanMutex             permitMutex            = new BooleanMutex(false);                                      // 控制器\n    private BooleanMutex             channelMutex           = new BooleanMutex(false);\n    private List<PermitListener>     listeners              = Collections.synchronizedList(new ArrayList<PermitListener>());\n    private volatile boolean         existOpposite          = false;\n    private IZkDataListener          channelDataListener;\n    private IZkDataListener          mainstemDataListener;\n    private IZkDataListener          oppositeMainstemDataListener;\n\n    public PermitMonitor(Long pipelineId){\n        super(pipelineId);\n        existOpposite = (ArbitrateConfigUtils.getOppositePipeline(getPipelineId()) != null);\n        // 开始同步\n        channelDataListener = new IZkDataListener() {\n\n            public void handleDataChange(String dataPath, Object data) throws Exception {\n                initChannelStatus((byte[]) data);\n            }\n\n            public void handleDataDeleted(String dataPath) throws Exception {\n                channelStatus = ChannelStatus.STOP;\n                permitSem();\n            }\n        };\n        String path = StagePathUtils.getChannel(getPipelineId());\n        zookeeper.subscribeDataChanges(path, channelDataListener);\n\n        mainstemDataListener = new IZkDataListener() {\n\n            public void handleDataChange(String dataPath, Object data) throws Exception {\n                initMainStemStatus((byte[]) data);\n            }\n\n            public void handleDataDeleted(String dataPath) throws Exception {\n                // mainstem节点挂了后，状态直接修改为taking\n                mainStemStatus = MainStemEventData.Status.TAKEING;\n                permitSem();\n            }\n        };\n\n        path = StagePathUtils.getMainStem(getPipelineId());\n        zookeeper.subscribeDataChanges(path, mainstemDataListener);\n\n        initChannelStatus();\n        initMainStemStatus();\n        // syncChannelStatus();\n        // syncMainStemStatus();\n        if (existOpposite) {\n            oppositeMainstemDataListener = new IZkDataListener() {\n\n                public void handleDataChange(String dataPath, Object data) throws Exception {\n                    initOppositeMainStemStatus((byte[]) data);\n                }\n\n                public void handleDataDeleted(String dataPath) throws Exception {\n                    // mainstem节点挂了后，状态直接修改为taking\n                    oppositeMainStemStatus = MainStemEventData.Status.TAKEING;\n                    permitSem();\n                }\n            };\n\n            path = StagePathUtils.getOppositeMainStem(getPipelineId());\n            zookeeper.subscribeDataChanges(path, oppositeMainstemDataListener);\n            initOppositeMainStemStatus();\n            // syncOppositeMainStemStatus();\n        }\n        MonitorScheduler.register(this);\n    }\n\n    public void reload() {\n        if (logger.isDebugEnabled()) {\n            logger.debug(\"## reload Permit pipeline[{}]\", getPipelineId());\n        }\n\n        try {\n            initChannelStatus();\n        } catch (Exception e) {// 处理下异常\n        }\n\n        try {\n            initMainStemStatus();\n        } catch (Exception e) {// 处理下异常\n        }\n\n        boolean prev = existOpposite;\n        existOpposite = (ArbitrateConfigUtils.getOppositePipeline(getPipelineId()) != null);\n        if (existOpposite) {\n            if (prev == false) {\n                // syncOppositeMainStemStatus();// 是个变化的过程，开启反向同步\n                String path = StagePathUtils.getOppositeMainStem(getPipelineId());\n                zookeeper.subscribeDataChanges(path, oppositeMainstemDataListener);\n            }\n\n            try {\n                initOppositeMainStemStatus();\n            } catch (Exception e) {// 处理下异常\n            }\n        }\n\n    }\n\n    public void destory() {\n        super.destory();\n\n        if (logger.isDebugEnabled()) {\n            logger.debug(\"## destory Permit pipeline[{}]\", getPipelineId());\n        }\n        String path = StagePathUtils.getChannel(getPipelineId());\n        zookeeper.unsubscribeDataChanges(path, channelDataListener);\n\n        path = StagePathUtils.getMainStem(getPipelineId());\n        zookeeper.unsubscribeDataChanges(path, mainstemDataListener);\n\n        if (existOpposite) {\n            path = StagePathUtils.getOppositeMainStem(getPipelineId());\n            zookeeper.unsubscribeDataChanges(path, oppositeMainstemDataListener);\n        }\n\n        MonitorScheduler.unRegister(this);\n    }\n\n    /**\n     * 查询是否允许授权处理，非阻塞\n     */\n    public boolean isPermit() {\n        return isPermit(false);\n    }\n\n    /**\n     * 查询是否允许授权处理，非阻塞，指定是否强制刷新\n     */\n    public boolean isPermit(boolean reload) {\n        if (reload) {// 判断是否需要重新reload\n            reload();\n        }\n\n        boolean result = channelStatus.isStart() && mainStemStatus.isOverTake();\n        if (existOpposite) {// 判断是否存在反向同步\n            result &= oppositeMainStemStatus.isOverTake();\n        }\n\n        return result;\n    }\n\n    /**\n     * 查询对应的channel授权状态\n     */\n    public ChannelStatus getChannelPermit() {\n        return getChannelPermit(false);\n    }\n\n    /**\n     * 查询对应的mainstem授权状态\n     */\n    public MainStemEventData.Status getMainStemPermit() {\n        return getMainStemPermit(false);\n    }\n\n    /**\n     * 查询对应的channel授权状态，指定是否强制刷新\n     */\n    public ChannelStatus getChannelPermit(boolean reload) {\n        if (reload) {\n            initChannelStatus();\n        }\n\n        return channelStatus;\n    }\n\n    /**\n     * 查询对应的mainstem授权状态，指定是否强制刷新\n     */\n    public MainStemEventData.Status getMainStemPermit(boolean reload) {\n        if (reload) {\n            initMainStemStatus();\n        }\n\n        return mainStemStatus;\n    }\n\n    /**\n     * 阻塞等待允许授权处理, 支持线程中断信号\n     * \n     * @return\n     */\n    public void waitForPermit() throws InterruptedException {\n        permitMutex.get();\n    }\n\n    /**\n     * 阻塞等待允许channel的授权处理, 支持线程中断信号\n     * \n     * @throws InterruptedException\n     */\n    public void waitForChannelPermit() throws InterruptedException {\n        channelMutex.get();\n    }\n\n    // ================ 状态数据同步 ================\n\n    private void initChannelStatus() {\n        String path = null;\n        try {\n            path = StagePathUtils.getChannel(getPipelineId());\n            byte[] bytes = zookeeper.readData(path);\n            initChannelStatus(bytes);\n        } catch (ZkNoNodeException e) {\n            channelStatus = ChannelStatus.STOP;\n            permitSem();\n        } catch (ZkException e) {\n            logger.error(path, e);\n        }\n    }\n\n    private void initChannelStatus(byte[] bytes) {\n        ChannelStatus newChannelStatus = JsonUtils.unmarshalFromByte(bytes, ChannelStatus.class);\n\n        if (logger.isDebugEnabled()) {\n            logger.debug(\"pipeline[{}] newChannelStatus is [{}]\", getPipelineId(), newChannelStatus);\n        }\n\n        synchronized (this) {\n            // 发生变化，才触发权限检查\n            if (!newChannelStatus.equals(channelStatus)) {\n                channelStatus = newChannelStatus;\n                permitSem();\n            }\n        }\n    }\n\n    // private void syncChannelStatus() {\n    // final String path = StagePathUtils.getChannel(getPipelineId());\n    // try {\n    // zookeeper.exists(path, new AsyncWatcher() {\n    //\n    // public void asyncProcess(WatchedEvent event) {\n    // MDC.put(ArbitrateConstants.splitPipelineLogFileKey, String.valueOf(getPipelineId()));\n    // if (isStop()) {// 如果已关闭，停止递归同步\n    // return;\n    // }\n    // // 出现session expired/connection losscase下，会触发所有的watcher响应，同时老的watcher会继续保留，所以会导致出现多次watcher响应\n    // boolean dataChanged = event.getType() == EventType.NodeDataChanged\n    // || event.getType() == EventType.NodeDeleted\n    // || event.getType() == EventType.NodeCreated\n    // || event.getType() == EventType.NodeChildrenChanged;\n    // if (dataChanged) {\n    // syncChannelStatus();// 开始同步channel状态\n    // }\n    // }\n    // });\n    //\n    // // 防止 watcher 错过事件\n    // initChannelStatus();\n    // } catch (KeeperException e) {\n    // syncChannelStatus();// 开始同步channel状态\n    // logger.error(path, e);\n    // } catch (InterruptedException e) {\n    // // ignore\n    // }\n    // }\n\n    private void initMainStemStatus() {\n        String path = null;\n        try {\n            path = StagePathUtils.getMainStem(getPipelineId());\n            byte[] bytes = zookeeper.readData(path);\n            initMainStemStatus(bytes);\n        } catch (ZkNoNodeException e) {\n            // mainstem节点挂了后，状态直接修改为taking\n            mainStemStatus = MainStemEventData.Status.TAKEING;\n            permitSem();\n        } catch (ZkException e) {\n            logger.error(path, e);\n        }\n    }\n\n    private void initMainStemStatus(byte[] bytes) {\n        MainStemEventData eventData = JsonUtils.unmarshalFromByte(bytes, MainStemEventData.class);\n        MainStemEventData.Status newStatus = eventData.getStatus();\n\n        if (logger.isDebugEnabled()) {\n            logger.debug(\"pipeline[{}] new mainStemStatus is [{}]\", getPipelineId(), newStatus);\n        }\n\n        synchronized (this) {\n            if (!mainStemStatus.equals(newStatus)) {\n                mainStemStatus = newStatus;\n                permitSem();\n            }\n        }\n    }\n\n    // private void syncMainStemStatus() {\n    // final String path = StagePathUtils.getMainStem(getPipelineId());\n    // try {\n    // // exists同样在data发生变化时会触发\n    // zookeeper.exists(path, new AsyncWatcher() {\n    //\n    // public void asyncProcess(WatchedEvent event) {\n    // MDC.put(ArbitrateConstants.splitPipelineLogFileKey, String.valueOf(getPipelineId()));\n    // if (isStop()) {// 如果已关闭，停止递归同步\n    // return;\n    // }\n    // // 出现session expired/connection losscase下，会触发所有的watcher响应，同时老的watcher会继续保留，所以会导致出现多次watcher响应\n    // boolean dataChanged = event.getType() == EventType.NodeDataChanged\n    // || event.getType() == EventType.NodeDeleted\n    // || event.getType() == EventType.NodeCreated\n    // || event.getType() == EventType.NodeChildrenChanged;\n    // if (dataChanged) {\n    // syncMainStemStatus();// 开始同步mainStem状态\n    // }\n    // }\n    // });\n    //\n    // initMainStemStatus();\n    // } catch (NoNodeException e) {\n    // // 可能不存在对应的节点,忽略\n    // } catch (KeeperException e) {\n    // syncMainStemStatus();// 开始同步mainStem状态\n    // logger.error(path, e);\n    // } catch (InterruptedException e) {\n    // // ignore\n    // }\n    // }\n\n    private void initOppositeMainStemStatus() {\n        String path = null;\n        try {\n            path = StagePathUtils.getOppositeMainStem(getPipelineId());\n            byte[] bytes = zookeeper.readData(path);\n            initOppositeMainStemStatus(bytes);\n        } catch (ZkNoNodeException e) {\n            // mainstem节点挂了后，状态直接修改为taking\n            oppositeMainStemStatus = MainStemEventData.Status.TAKEING;\n            permitSem();\n        } catch (ZkException e) {\n            logger.error(path, e);\n        }\n    }\n\n    private void initOppositeMainStemStatus(byte[] bytes) {\n        MainStemEventData eventData = JsonUtils.unmarshalFromByte(bytes, MainStemEventData.class);\n        MainStemEventData.Status newStatus = eventData.getStatus();\n\n        if (logger.isDebugEnabled()) {\n            logger.debug(\"pipeline[{}] new oppositeMainStemStatus is [{}]\", getPipelineId(), newStatus);\n        }\n\n        synchronized (this) {\n            if (!oppositeMainStemStatus.equals(newStatus)) {\n                oppositeMainStemStatus = newStatus;\n                permitSem();\n            }\n        }\n    }\n\n    // private void syncOppositeMainStemStatus() {\n    // final String path = StagePathUtils.getOppositeMainStem(getPipelineId());\n    // try {\n    // // exists同样在data发生变化时会触发\n    // zookeeper.exists(path, new AsyncWatcher() {\n    //\n    // public void asyncProcess(WatchedEvent event) {\n    // MDC.put(ArbitrateConstants.splitPipelineLogFileKey, String.valueOf(getPipelineId()));\n    // if (existOpposite == false || isStop()) {// 如果已关闭，停止递归同步\n    // return;\n    // }\n    // // 出现session expired/connection losscase下，会触发所有的watcher响应，同时老的watcher会继续保留，所以会导致出现多次watcher响应\n    // boolean dataChanged = event.getType() == EventType.NodeDataChanged\n    // || event.getType() == EventType.NodeDeleted\n    // || event.getType() == EventType.NodeCreated\n    // || event.getType() == EventType.NodeChildrenChanged;\n    // if (dataChanged) {\n    // syncOppositeMainStemStatus();// 开始同步mainStem状态\n    // }\n    // }\n    // });\n    //\n    // initOppositeMainStemStatus();\n    // } catch (NoNodeException e) {\n    // // 可能不存在对应的opposite节点,忽略\n    // } catch (KeeperException e) {\n    // syncOppositeMainStemStatus();// 开始同步mainStem状态\n    // logger.error(path, e);\n    // } catch (InterruptedException e) {\n    // // ignore\n    // }\n    // }\n\n    /**\n     * permit信号的处理\n     */\n    private void permitSem() {\n        if (channelStatus.isStart()) {\n            channelMutex.set(true);\n            logger.debug(\"channel status is ok!\");\n        } else {\n            channelMutex.set(false);\n            logger.debug(\"channel status is fail!\");\n        }\n\n        boolean permit = isPermit(false);\n        if (permit == false) {\n            if (logger.isDebugEnabled()) {\n                logger.debug(\"Permit is fail!\");\n            }\n            // 如果未授权，则设置信号量为0\n            permitMutex.set(false);\n        } else {\n            // 信号量+1\n            if (logger.isDebugEnabled()) {\n                logger.debug(\"Permit is Ok!\");\n            }\n            permitMutex.set(true);\n        }\n\n        processChanged(permit);// 通知下变化\n    }\n\n    // ======================== listener处理 ======================\n\n    public void addListener(PermitListener listener) {\n        if (logger.isDebugEnabled()) {\n            logger.debug(\"## pipeline[{}] add listener [{}]\", getPipelineId(),\n                         ClassUtils.getShortClassName(listener.getClass()));\n        }\n\n        this.listeners.add(listener);\n    }\n\n    public void removeListener(PermitListener listener) {\n        if (logger.isDebugEnabled()) {\n            logger.debug(\"## pipeline[{}] remove listener [{}]\", getPipelineId(),\n                         ClassUtils.getShortClassName(listener.getClass()));\n        }\n\n        this.listeners.remove(listener);\n    }\n\n    private void processChanged(final boolean isPermit) {\n        for (final PermitListener listener : listeners) {\n            // 异步处理\n            arbitrateExecutor.submit(new Runnable() {\n\n                public void run() {\n                    MDC.put(ArbitrateConstants.splitPipelineLogFileKey, String.valueOf(getPipelineId()));\n                    listener.processChanged(isPermit);\n                }\n            });\n        }\n    }\n\n    public void setArbitrateExecutor(ExecutorService arbitrateExecutor) {\n        this.arbitrateExecutor = arbitrateExecutor;\n    }\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/setl/monitor/TerminMonitor.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.setl.monitor;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.I0Itec.zkclient.IZkChildListener;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.util.CollectionUtils;\n\nimport com.alibaba.otter.shared.arbitrate.impl.setl.ArbitrateLifeCycle;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.helper.StagePathUtils;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.helper.TerminProcessQueue;\nimport com.alibaba.otter.shared.arbitrate.impl.zookeeper.ZooKeeperClient;\nimport com.alibaba.otter.shared.common.utils.zookeeper.ZkClientx;\n\n/**\n * 结束信号的监听\n * \n * @author jianghang 2011-9-26 上午11:31:50\n * @version 4.0.0\n */\npublic class TerminMonitor extends ArbitrateLifeCycle implements Monitor {\n\n    private static final Logger logger         = LoggerFactory.getLogger(TerminMonitor.class);\n\n    private ZkClientx           zookeeper      = ZooKeeperClient.getInstance();\n    private TerminProcessQueue  waitProcessIds = new TerminProcessQueue();                    // 记录对应的终结信号数据，从小到大的排序\n    private IZkChildListener    childListener;\n\n    public TerminMonitor(Long pipelineId){\n        super(pipelineId);\n        childListener = new IZkChildListener() {\n\n            public void handleChildChange(String parentPath, List<String> currentChilds) throws Exception {\n                if (currentChilds != null) {\n                    initTermin(currentChilds);\n                }\n            }\n        };\n\n        String path = StagePathUtils.getTerminRoot(getPipelineId());\n        List<String> childs = zookeeper.subscribeChildChanges(path, childListener);\n        initTermin(childs);\n        MonitorScheduler.register(this);\n    }\n\n    public void reload() {\n        try {\n            if (logger.isDebugEnabled()) {\n                logger.debug(\"## reload termin pipeline[{}]\", getPipelineId());\n            }\n\n            initTermin();\n        } catch (Exception e) {\n            // ignore\n        }\n    }\n\n    public void destory() {\n        super.destory();\n        if (logger.isDebugEnabled()) {\n            logger.debug(\"## destory termin pipeline[{}]\", getPipelineId());\n        }\n\n        String path = StagePathUtils.getTerminRoot(getPipelineId());\n        zookeeper.unsubscribeChildChanges(path, childListener);\n        MonitorScheduler.unRegister(this);\n        waitProcessIds.clear();\n    }\n\n    /**\n     * 阻塞获取对应的process的termin事件\n     */\n    public Long waitForProcess() throws InterruptedException {\n        Long processId = waitProcessIds.peek();\n        if (logger.isDebugEnabled()) {\n            logger.debug(\"## {} get termin id [{}]\", getPipelineId(), processId);\n        }\n        return processId;\n    }\n\n    /**\n     * @return 当前待处理的termin信号的总数\n     */\n    public int size() {\n        return waitProcessIds.size();\n    }\n\n    /**\n     * 提交termin的ack信息，物理移除termin\n     */\n    public boolean ack(Long processId) {\n        boolean result = waitProcessIds.ack();\n        if (logger.isDebugEnabled()) {\n            logger.debug(\"## {} ack termin id [{}]\", getPipelineId(), processId);\n        }\n        return result;\n    }\n\n    // ================ 状态数据同步 ================\n\n    private void initTermin() {\n        String path = StagePathUtils.getTerminRoot(getPipelineId());\n        List<String> termins = zookeeper.getChildren(path);\n        initTermin(termins);\n    }\n\n    private synchronized void initTermin(List<String> termins) {\n        if (CollectionUtils.isEmpty(termins)) {\n            return;\n        }\n\n        List<Long> processIds = new ArrayList<Long>(termins.size());\n        for (String termin : termins) {\n            processIds.add(StagePathUtils.getProcessId(termin));\n        }\n        // 排序一下\n        Collections.sort(processIds);\n        for (Long processId : processIds) {\n            boolean successed = waitProcessIds.offer(processId);\n            if (successed && logger.isDebugEnabled()) {\n                logger.debug(\"## {} add termin id [{}]\", getPipelineId(), processId);\n            }\n        }\n    }\n\n    // private void syncTermin() {\n    // String path = null;\n    // try {\n    // path = StagePathUtils.getTerminRoot(getPipelineId());\n    // List<String> termins = zookeeper.getChildren(path, new AsyncWatcher() {\n    //\n    // public void asyncProcess(WatchedEvent event) {\n    // MDC.put(ArbitrateConstants.splitPipelineLogFileKey, String.valueOf(getPipelineId()));\n    // if (isStop()) {\n    // return;\n    // }\n    //\n    // // 出现session expired/connection losscase下，会触发所有的watcher响应，同时老的watcher会继续保留，所以会导致出现多次watcher响应\n    // boolean dataChanged = event.getType() == EventType.NodeDataChanged\n    // || event.getType() == EventType.NodeDeleted\n    // || event.getType() == EventType.NodeCreated\n    // || event.getType() == EventType.NodeChildrenChanged;\n    // if (dataChanged) {\n    // syncTermin();// 继续关注\n    // }\n    // }\n    // });\n    //\n    // // watcher 挂载需要时间，先检查一遍\n    // initTermin(termins);\n    // } catch (KeeperException e) {\n    // syncTermin();// 挂失败了，重新添加一个\n    // } catch (InterruptedException e) {\n    // // ignore\n    // }\n    // }\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/setl/monitor/listener/MainstemListener.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.setl.monitor.listener;\n\n/**\n * 触发一下mainstem发生切换\n * \n * @author jianghang 2012-9-11 下午02:26:03\n * @version 4.1.0\n */\npublic interface MainstemListener {\n\n    /**\n     * 触发现在轮到自己做为active，需要载入上一个active的上下文数据\n     */\n    public void processActiveEnter();\n\n    /**\n     * 触发一下当前active模式失败\n     */\n    public void processActiveExit();\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/setl/monitor/listener/NodeListener.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.setl.monitor.listener;\n\nimport java.util.List;\n\n/**\n * dead node的监控处理实现，运行在启动了mainStem的单节点上，避免多个节点同时处理，所以抽取了Listener\n * \n * @author jianghang 2011-9-26 下午10:33:42\n * @version 4.0.0\n */\npublic interface NodeListener {\n\n    /**\n     * 触发process变化，传递了变化后最新的processIds列表\n     */\n    public void processChanged(List<Long> aliveNodes);\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/setl/monitor/listener/PermitListener.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.setl.monitor.listener;\n\n/**\n * 监控下permit发生变化，即时处理一些数据(比如select节点的启动)\n * \n * @author jianghang 2011-12-8 下午04:48:14\n * @version 4.0.0\n */\npublic interface PermitListener {\n\n    /**\n     * 触发一下permit变化的值\n     */\n    public void processChanged(boolean isPermit);\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/setl/rpc/ExtractRpcArbitrateEvent.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.setl.rpc;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.util.Assert;\n\nimport com.alibaba.otter.shared.arbitrate.exception.ArbitrateException;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.ArbitrateFactory;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.ExtractArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.helper.StagePathUtils;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.lb.LoadBalanceFactory;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.monitor.PermitMonitor;\nimport com.alibaba.otter.shared.arbitrate.impl.zookeeper.ZooKeeperClient;\nimport com.alibaba.otter.shared.arbitrate.model.EtlEventData;\nimport com.alibaba.otter.shared.common.model.config.channel.ChannelStatus;\nimport com.alibaba.otter.shared.common.model.config.enums.StageType;\nimport com.alibaba.otter.shared.common.model.config.node.Node;\nimport com.alibaba.otter.shared.common.utils.zookeeper.ZkClientx;\n\n/**\n * 基于rpc方式实现的extract调度\n * \n * @author jianghang 2012-9-29 上午10:59:24\n * @version 4.1.0\n */\npublic class ExtractRpcArbitrateEvent implements ExtractArbitrateEvent {\n\n    private static final Logger     logger    = LoggerFactory.getLogger(ExtractRpcArbitrateEvent.class);\n    private ZkClientx               zookeeper = ZooKeeperClient.getInstance();\n    private RpcStageEventDispatcher rpcStageEventDispatcher;\n\n    public EtlEventData await(Long pipelineId) throws InterruptedException {\n        Assert.notNull(pipelineId);\n\n        PermitMonitor permitMonitor = ArbitrateFactory.getInstance(pipelineId, PermitMonitor.class);\n        permitMonitor.waitForPermit();// 阻塞等待授权\n\n        RpcStageController stageController = ArbitrateFactory.getInstance(pipelineId, RpcStageController.class);\n        Long processId = stageController.waitForProcess(StageType.EXTRACT); // 符合条件的processId\n\n        ChannelStatus status = permitMonitor.getChannelPermit();\n        if (status.isStart() || status.isPause()) {// pause状态也让其处理，避免误删除pause状态的processId，导致通道挂起\n            EtlEventData eventData = stageController.getLastData(processId);\n            Node node = LoadBalanceFactory.getNextTransformNode(pipelineId);// 获取下一个处理节点信息\n            if (node == null) {// 没有后端节点\n                throw new ArbitrateException(\"Extract_single\", \"no next node\");\n            } else {\n                eventData.setNextNid(node.getId());\n                return eventData;// 只有这一条路返回\n            }\n        } else {\n            logger.warn(\"pipelineId[{}] extract ignore processId[{}] by status[{}]\", new Object[] { pipelineId,\n                    processId, status });\n            String path = StagePathUtils.getProcess(pipelineId, processId);\n            zookeeper.exists(path);\n\n            return await(pipelineId);// 递归调用\n        }\n    }\n\n    public void single(EtlEventData data) {\n        Assert.notNull(data);\n        rpcStageEventDispatcher.single(StageType.EXTRACT, data);// 通知下一个节点\n    }\n\n    public void setRpcStageEventDispatcher(RpcStageEventDispatcher rpcStageEventDispatcher) {\n        this.rpcStageEventDispatcher = rpcStageEventDispatcher;\n    }\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/setl/rpc/LoadRpcArbitrateEvent.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.setl.rpc;\n\nimport java.util.Date;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.util.Assert;\n\nimport com.alibaba.otter.shared.arbitrate.impl.config.ArbitrateConfigUtils;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.ArbitrateFactory;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.LoadArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.helper.StagePathUtils;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.monitor.PermitMonitor;\nimport com.alibaba.otter.shared.arbitrate.impl.zookeeper.ZooKeeperClient;\nimport com.alibaba.otter.shared.arbitrate.model.EtlEventData;\nimport com.alibaba.otter.shared.arbitrate.model.TerminEventData;\nimport com.alibaba.otter.shared.arbitrate.model.TerminEventData.TerminType;\nimport com.alibaba.otter.shared.common.model.config.channel.ChannelStatus;\nimport com.alibaba.otter.shared.common.model.config.enums.StageType;\nimport com.alibaba.otter.shared.common.utils.zookeeper.ZkClientx;\n\n/**\n * 基于rpc方式实现的load调度\n * \n * @author jianghang 2012-9-29 上午10:59:24\n * @version 4.1.0\n */\npublic class LoadRpcArbitrateEvent implements LoadArbitrateEvent {\n\n    private static final Logger     logger    = LoggerFactory.getLogger(LoadRpcArbitrateEvent.class);\n    private TerminRpcArbitrateEvent terminEvent;\n    private ZkClientx               zookeeper = ZooKeeperClient.getInstance();\n    private RpcStageEventDispatcher rpcStageEventDispatcher;\n\n    public EtlEventData await(Long pipelineId) throws InterruptedException {\n        Assert.notNull(pipelineId);\n\n        PermitMonitor permitMonitor = ArbitrateFactory.getInstance(pipelineId, PermitMonitor.class);\n        permitMonitor.waitForPermit();// 阻塞等待授权\n\n        RpcStageController stageController = ArbitrateFactory.getInstance(pipelineId, RpcStageController.class);\n        Long processId = stageController.waitForProcess(StageType.LOAD); // 符合条件的processId\n\n        ChannelStatus status = permitMonitor.getChannelPermit();\n        if (status.isStart()) {// 即时查询一下当前的状态，状态随时可能会变\n            return stageController.getLastData(processId);\n        } else {\n            // 需要进一步check，避免丢失load信号\n            status = permitMonitor.getChannelPermit(true);\n            if (status.isStart()) {\n                return stageController.getLastData(processId);\n            } else if (status.isPause()) {\n                String path = StagePathUtils.getProcess(pipelineId, processId);\n                if (zookeeper.exists(path)) { // 如果存在process，那说明没有被rollback掉(可能刚好在做rollback)，这种运行进行load处理\n                    return stageController.getLastData(processId);\n                }\n            }\n\n            logger.warn(\"pipelineId[{}] load ignore processId[{}] by status[{}]\", new Object[] { pipelineId, processId,\n                    status });\n            return await(pipelineId);// 递归调用\n        }\n    }\n\n    public void single(final EtlEventData data) {\n        Assert.notNull(data);\n        data.setEndTime(new Date().getTime());// 返回当前时间\n        boolean result = rpcStageEventDispatcher.single(StageType.LOAD, data);// 通知下一个节点，下一个节点也肯定会是自己\n\n        if (result) {\n            // 直接异步处理termin，更快速的返回, modify by ljh at 2013-02-25\n            // 减少Load await/single所占用的时间，尽快返回，因为两个load之间的传递可以尽可能不走zookeeper完成\n            TerminExecutor executor = ArbitrateFactory.getInstance(data.getPipelineId(), TerminExecutor.class);\n            executor.submit(new Runnable() {\n\n                public void run() {\n                    // 调用Termin信号\n                    TerminEventData termin = new TerminEventData();\n                    termin.setPipelineId(data.getPipelineId());\n                    termin.setProcessId(data.getProcessId());\n                    termin.setStartTime(data.getStartTime());\n                    termin.setEndTime(data.getEndTime());\n                    termin.setFirstTime(data.getFirstTime());\n                    termin.setNumber(data.getNumber());\n                    termin.setBatchId(data.getBatchId());\n                    termin.setSize(data.getSize());\n                    termin.setExts(data.getExts());\n                    termin.setType(TerminType.NORMAL);\n                    termin.setCode(\"setl\");\n                    termin.setDesc(\"\");\n                    termin.setCurrNid(ArbitrateConfigUtils.getCurrentNid());\n                    terminEvent.single(termin);\n                }\n            });\n        }\n    }\n\n    public void setRpcStageEventDispatcher(RpcStageEventDispatcher rpcStageEventDispatcher) {\n        this.rpcStageEventDispatcher = rpcStageEventDispatcher;\n    }\n\n    public void setTerminEvent(TerminRpcArbitrateEvent terminEvent) {\n        this.terminEvent = terminEvent;\n    }\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/setl/rpc/RpcStageController.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.setl.rpc;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport org.apache.commons.lang.ClassUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.util.CollectionUtils;\n\nimport com.alibaba.otter.shared.arbitrate.exception.ArbitrateException;\nimport com.alibaba.otter.shared.arbitrate.impl.config.ArbitrateConfigUtils;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.ArbitrateFactory;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.ArbitrateLifeCycle;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.helper.ReplyProcessQueue;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.helper.StageProgress;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.rpc.monitor.ProcessListener;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.rpc.monitor.ProcessMonitor;\nimport com.alibaba.otter.shared.arbitrate.model.EtlEventData;\nimport com.alibaba.otter.shared.common.model.config.enums.StageType;\nimport com.google.common.base.Function;\nimport com.google.common.collect.MapMaker;\nimport com.google.common.collect.OtterMigrateMap;\n\n/**\n * 基于rpc的stage调度控制器，排除select调度,主要控制e/t/l的调度控制\n * \n * @author jianghang 2012-9-28 下午10:05:26\n * @version 4.1.0\n */\npublic class RpcStageController extends ArbitrateLifeCycle implements ProcessListener {\n\n    private static final Logger               logger                 = LoggerFactory.getLogger(RpcStageController.class);\n    private Map<StageType, ReplyProcessQueue> replys;\n    private Map<Long, StageProgress>          progress;\n    private ProcessMonitor                    processMonitor;\n    private volatile Long                     lastestLoadedProcessId = -1L;                                              // 最近一次同步成功的processId\n\n    public RpcStageController(Long pipelineId){\n        super(pipelineId);\n\n        replys = OtterMigrateMap.makeComputingMap(new Function<StageType, ReplyProcessQueue>() {\n\n            public ReplyProcessQueue apply(StageType input) {\n                int size = ArbitrateConfigUtils.getParallelism(getPipelineId()) * 10;\n                if (size < 100) {\n                    size = 100;\n                }\n                return new ReplyProcessQueue(size);\n            }\n        });\n\n        progress = new MapMaker().makeMap();\n        // 注册一下监听事件变化\n        processMonitor = ArbitrateFactory.getInstance(pipelineId, ProcessMonitor.class);\n        processMonitor.addListener(this);\n        processMonitor.reload();\n    }\n\n    public Long waitForProcess(StageType stage) throws InterruptedException {\n        if (stage.isSelect()) {\n            throw new ArbitrateException(\"not support\");\n        }\n\n        return replys.get(stage).take();\n    }\n\n    /**\n     * 获取上一个stage传递的数据信息\n     */\n    public EtlEventData getLastData(Long processId) {\n        return progress.get(processId).getData();\n    }\n\n    public void destory() {\n        processMonitor.removeListener(this);\n        replys.clear();\n        progress.clear();\n    }\n\n    public synchronized boolean single(StageType stage, EtlEventData etlEventData) {\n        boolean result = true;\n        switch (stage) {\n            case SELECT:\n                progress.put(etlEventData.getProcessId(), new StageProgress(StageType.SELECT, etlEventData));\n                replys.get(StageType.EXTRACT).offer(etlEventData.getProcessId());\n                break;\n            case EXTRACT:\n                progress.put(etlEventData.getProcessId(), new StageProgress(StageType.EXTRACT, etlEventData));\n                replys.get(StageType.TRANSFORM).offer(etlEventData.getProcessId());\n                break;\n            case TRANSFORM:\n                progress.put(etlEventData.getProcessId(), new StageProgress(StageType.TRANSFORM, etlEventData));\n                // 并不是立即触发，通知最小的一个process启动\n                computeNextLoad();\n                break;\n            case LOAD:\n                Object removed = progress.remove(etlEventData.getProcessId());\n                // 并不是立即触发，通知下一个最小的一个process启动\n                if (removed == null) {\n                    result = false;\n                } else {\n                    // 如果2个process 1和2, 1先执行完了load,此时2还不符合条件，等2到了Transform时，还需要依赖zookeeper的process列表变化进行判断\n                    // 记录一下上一次同步成功的processId，提升load响应速度，方便在内存中直接判断\n                    lastestLoadedProcessId = etlEventData.getProcessId();\n                    computeNextLoad(); // 通知load processId，触发下一个\n                }\n                break;\n            default:\n                break;\n        }\n\n        return result;\n    }\n\n    /**\n     * 计算下一个load的processId\n     */\n    private void computeNextLoad() {\n        // 针对上一个id为本地load成功的，直接忽略，触发下一个id\n        Long processId = getMinTransformedProcessId(lastestLoadedProcessId);\n        if (processId != null) {\n            replys.get(StageType.LOAD).offer(processId);\n        }\n    }\n\n    /**\n     * 获取最小一个符合条件的processId，排除loadedProcessId\n     */\n    private Long getMinTransformedProcessId(Long loadedProcessId) {\n        ProcessMonitor processMonitor = ArbitrateFactory.getInstance(getPipelineId(), ProcessMonitor.class);\n        List<Long> processIds = processMonitor.getCurrentProcessIds();\n        // 如果需要当前node处理当前process的load时，rpc请求一定会将对应的stage状态发到这机器上，并保存到progress中\n        if (!CollectionUtils.isEmpty(processIds) && !CollectionUtils.isEmpty(progress)) {\n            // 上一次load成功的在当前的processId中不存在，可能有两种情况:\n            // 1. zk还未将数据通知过来，当前current processIds还是为老版本的值\n            // 2. processId已经被删除，比如好久没有数据同步了，定时触发时发觉列表一直为空\n            // if (loadedProcessId != null && !processIds.contains(loadedProcessId)) {\n            // // 强制刷新一次，不过也可能是刷到老版本的值，杭州leader还没同步到美国\n            // processIds = processMonitor.getCurrentProcessIds(true);\n            // }\n\n            Long result = null;\n            // 做的一个优化，如果上一个processId load成功是在本机，直接忽略\n            // 因为存在一个问题：比如两个process，都先完成了T模块，然后逐个触发L模块，此时第二个process需要等zookeeper回调watcher时才会被触发\n            for (Long processId : processIds) {\n                if (loadedProcessId == null || processId > loadedProcessId) {\n                    result = processId;\n                    break;\n                }\n            }\n\n            // 如果不存在符合>loadedProcessId的记录，直接假设下一个processId就是上一个id+1\n            // 因为processId目前的机制永远只会递增\n            if (result == null) {\n                result = loadedProcessId + 1;\n            }\n\n            if (result != null) {\n                StageProgress stage = progress.get(result);\n                if (stage != null && stage.getStage().isTransform()) {\n                    return result;\n                } else {\n                    logger.info(\"rpc compute [{}] but stage [{}]\", result, stage == null ? null : stage.getStage());\n                    return null;\n                }\n            }\n        }\n\n        return null;\n    }\n\n    public void processChanged(List<Long> processIds) {\n        compareProgress(processIds);\n\n        for (ReplyProcessQueue replyProcessIds : replys.values()) {\n            compareReply(processIds, replyProcessIds);\n        }\n\n        // process发生变化，可能是process load完成，需要触发下一个process进行load\n        computeNextLoad();\n    }\n\n    /**\n     * 删除已经被废弃的processId\n     */\n    private synchronized void compareProgress(List<Long> processIds) {\n        if (CollectionUtils.isEmpty(processIds) == false) {\n            Long minProcessId = processIds.get(0);\n            // 对比一下progress中的记录，如果小于当前最小的processId，直接删除内存中的记录\n            // 因为发生跨机器调用或者出现restart指令，对应的process记录不会被删除\n            for (Long processId : progress.keySet()) {\n                if (processId < minProcessId) {\n                    progress.remove(processId);\n                }\n            }\n        }\n    }\n\n    /**\n     * 将当前的符合条件的processIds和当前的reply queue进行校对，剔除不在processIds里的内容\n     */\n    private synchronized void compareReply(List<Long> processIds, ReplyProcessQueue replyProcessIds) {\n        Object[] replyIds = replyProcessIds.toArray();\n        for (Object replyId : replyIds) {\n            if (processIds.contains((Long) replyId) == false) { // 判断reply id是否在当前processId列表中\n                // 因为存在并发问题，如在执行Listener事件的同时，可能触发了process的创建，这时新建的processId会进入到reply队列中\n                // 此时接受到的processIds变量为上一个版本的内容，所以会删除新建的process，导致整个通道被挂住\n                if (CollectionUtils.isEmpty(processIds) == false) {\n                    Long processId = processIds.get(0);\n                    if (processId > (Long) replyId) { // 如果当前最小的processId都大于replyId, processId都是递增创建的\n                        logger.info(\"## {} remove reply id [{}]\", ClassUtils.getShortClassName(this.getClass()),\n                                    (Long) replyId);\n                        replyProcessIds.remove((Long) replyId);\n                        progress.remove((Long) replyId);\n                    }\n                }\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/setl/rpc/RpcStageEventDispatcher.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.setl.rpc;\n\nimport org.springframework.util.Assert;\n\nimport com.alibaba.otter.shared.arbitrate.impl.communication.ArbitrateCommmunicationClient;\nimport com.alibaba.otter.shared.arbitrate.impl.config.ArbitrateConfigUtils;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.ArbitrateFactory;\nimport com.alibaba.otter.shared.arbitrate.model.EtlEventData;\nimport com.alibaba.otter.shared.common.model.config.enums.StageType;\nimport com.alibaba.otter.shared.communication.core.CommunicationRegistry;\nimport com.alibaba.otter.shared.communication.model.arbitrate.ArbitrateEventType;\nimport com.alibaba.otter.shared.communication.model.arbitrate.StageSingleEvent;\n\n/**\n * 分发rpc的请求，根据不同的pipelineId分发到不同的{@link RpcStageController}实例上去\n * \n * @author jianghang 2012-9-29 上午10:26:38\n * @version 4.1.0\n */\npublic class RpcStageEventDispatcher {\n\n    private ArbitrateCommmunicationClient arbitrateCommmunicationClient;\n\n    public RpcStageEventDispatcher(){\n        CommunicationRegistry.regist(ArbitrateEventType.stageSingle, this);\n    }\n\n    /**\n     * 接收rpc请求的调用\n     */\n    protected boolean onStageSingle(StageSingleEvent event) {\n        Assert.notNull(event.getPipelineId());\n        // 根据pipeline找到对应的实例\n        RpcStageController controller = ArbitrateFactory.getInstance(event.getPipelineId(), RpcStageController.class);\n        return controller.single(event.getStage(), (EtlEventData) event.getData());\n    }\n\n    /**\n     * 触发通知\n     */\n    public boolean single(StageType stage, EtlEventData eventData) {\n        Assert.notNull(eventData);\n        eventData.setCurrNid(ArbitrateConfigUtils.getCurrentNid());\n\n        StageSingleEvent event = new StageSingleEvent(ArbitrateEventType.stageSingle);\n        event.setPipelineId(eventData.getPipelineId());\n        event.setStage(stage);\n        event.setData(eventData);\n\n        if (isLocal(eventData.getNextNid())) {// 判断是否为本地jvm\n            return onStageSingle(event);\n        } else {\n            return (Boolean) arbitrateCommmunicationClient.call(eventData.getNextNid(), event);// rpc通知下一个节点\n        }\n    }\n\n    private boolean isLocal(Long targetNodeId) {\n        return ArbitrateConfigUtils.getCurrentNid().equals(targetNodeId);\n    }\n\n    public void setArbitrateCommmunicationClient(ArbitrateCommmunicationClient arbitrateCommmunicationClient) {\n        this.arbitrateCommmunicationClient = arbitrateCommmunicationClient;\n    }\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/setl/rpc/SelectRpcArbitrateEvent.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.setl.rpc;\n\nimport java.util.Date;\n\nimport org.I0Itec.zkclient.exception.ZkException;\nimport org.I0Itec.zkclient.exception.ZkNoNodeException;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.util.Assert;\n\nimport com.alibaba.otter.shared.arbitrate.exception.ArbitrateException;\nimport com.alibaba.otter.shared.arbitrate.impl.config.ArbitrateConfigUtils;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.ArbitrateFactory;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.SelectArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.helper.StagePathUtils;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.lb.LoadBalanceFactory;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.monitor.PermitMonitor;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.rpc.monitor.SelectProcessListener;\nimport com.alibaba.otter.shared.arbitrate.impl.zookeeper.ZooKeeperClient;\nimport com.alibaba.otter.shared.arbitrate.model.EtlEventData;\nimport com.alibaba.otter.shared.arbitrate.model.ProcessNodeEventData;\nimport com.alibaba.otter.shared.common.model.config.channel.ChannelStatus;\nimport com.alibaba.otter.shared.common.model.config.enums.StageType;\nimport com.alibaba.otter.shared.common.model.config.node.Node;\nimport com.alibaba.otter.shared.common.model.config.pipeline.PipelineParameter.ArbitrateMode;\nimport com.alibaba.otter.shared.common.utils.JsonUtils;\nimport com.alibaba.otter.shared.common.utils.zookeeper.ZkClientx;\n\n/**\n * 基于rpc模式的select实现, process的持久化控制还是依赖于zookeeper，只是改变了原先s/e/t/l之间的通讯方式，由zookeeper watcher改为rpc\n * \n * @author jianghang 2012-9-28 下午09:32:04\n * @version 4.1.0\n */\npublic class SelectRpcArbitrateEvent implements SelectArbitrateEvent {\n\n    private static final Logger     logger    = LoggerFactory.getLogger(SelectRpcArbitrateEvent.class);\n    private ZkClientx               zookeeper = ZooKeeperClient.getInstance();\n    private RpcStageEventDispatcher rpcStageEventDispatcher;\n\n    public EtlEventData await(Long pipelineId) throws InterruptedException {\n        Assert.notNull(pipelineId);\n\n        PermitMonitor permitMonitor = ArbitrateFactory.getInstance(pipelineId, PermitMonitor.class);\n        permitMonitor.waitForPermit();// 阻塞等待授权\n\n        SelectProcessListener selectProcessListener = ArbitrateFactory.getInstance(pipelineId,\n                                                                                   SelectProcessListener.class);\n        Long processId = selectProcessListener.waitForProcess(); // 符合条件的processId\n\n        ChannelStatus status = permitMonitor.getChannelPermit();\n        if (status.isStart()) {// 即时查询一下当前的状态，状态随时可能会变\n            try {\n                EtlEventData eventData = new EtlEventData();\n                eventData.setPipelineId(pipelineId);\n                eventData.setProcessId(processId);\n                eventData.setStartTime(new Date().getTime());// 返回当前时间\n\n                Node node = LoadBalanceFactory.getNextExtractNode(pipelineId);// 获取下一个处理节点信息\n                if (node == null) {// 没有后端节点\n                    // TerminEventData termin = new TerminEventData();\n                    // termin.setPipelineId(pipelineId);\n                    // termin.setType(TerminType.ROLLBACK);\n                    // termin.setCode(\"no_node\");\n                    // termin.setDesc(MessageFormat.format(\"pipeline[{}] extract stage has no node!\", pipelineId));\n                    // terminEvent.single(termin);\n                    throw new ArbitrateException(\"Select_single\", \"no next node\");\n                } else {\n                    eventData.setNextNid(node.getId());\n                    markUsed(eventData); // 标记为已使用\n                    return eventData;// 只有这一条路返回\n                }\n            } catch (ZkNoNodeException e) {\n                logger.error(\"pipeline[{}] processId[{}] is invalid , retry again\", pipelineId, processId);\n                return await(pipelineId);// /出现节点不存在，说明出现了error情况,递归调用重新获取一次\n            } catch (ZkException e) {\n                throw new ArbitrateException(\"Select_await\", e.getMessage(), e);\n            }\n        } else {\n            logger.warn(\"pipelineId[{}] select ignore processId[{}] by status[{}]\", new Object[] { pipelineId,\n                    processId, status });\n            // add by ljh 2013-02-01\n            // 遇到一个bug:\n            // a. 某台机器发起了一个RESTART指令，然后开始删除process列表\n            // b. 此时另一个台机器(select工作节点)，并没有收到PAUSE的推送，导致还会再创建一个process节点\n            // c. 后续收到PAUSE指令后，丢弃了processId，就出现了unused的processId\n            // 这里删除了，要考虑一个问题，就是和restart指令在并行删除同一个processId时的并发考虑，目前来看没问题\n            String path = StagePathUtils.getProcess(pipelineId, processId);\n            zookeeper.delete(path); // 忽略删除失败\n            return await(pipelineId);// 递归调用\n        }\n    }\n\n    public void single(EtlEventData data) {\n        Assert.notNull(data);\n        rpcStageEventDispatcher.single(StageType.SELECT, data);\n    }\n\n    /**\n     * 标记一下当前process为已使用\n     */\n    private void markUsed(EtlEventData data) throws ZkNoNodeException, ZkException {\n        String path = StagePathUtils.getProcess(data.getPipelineId(), data.getProcessId());\n        // 序列化\n        ProcessNodeEventData eventData = new ProcessNodeEventData();\n        Long nid = ArbitrateConfigUtils.getCurrentNid();\n        eventData.setNid(nid);\n        eventData.setStatus(ProcessNodeEventData.Status.USED);// 标记为已使用\n        eventData.setMode(ArbitrateMode.RPC);// 直接声明为rpc模式\n        byte[] bytes = JsonUtils.marshalToByte(eventData);\n        zookeeper.writeData(path, bytes);\n    }\n\n    public void setRpcStageEventDispatcher(RpcStageEventDispatcher rpcStageEventDispatcher) {\n        this.rpcStageEventDispatcher = rpcStageEventDispatcher;\n    }\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/setl/rpc/TerminExecutor.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.setl.rpc;\n\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Future;\nimport java.util.concurrent.LinkedBlockingQueue;\nimport java.util.concurrent.ThreadPoolExecutor;\nimport java.util.concurrent.TimeUnit;\n\nimport com.alibaba.otter.shared.arbitrate.impl.setl.ArbitrateLifeCycle;\nimport com.alibaba.otter.shared.common.utils.thread.NamedThreadFactory;\n\n/**\n * 基于pipeline隔离的executor实现，每个pipeline一个线程\n * \n * @author jianghang 2013-2-26 下午09:16:32\n * @version 4.1.7\n */\npublic class TerminExecutor extends ArbitrateLifeCycle {\n\n    // 注意accept数量可以和SelectTask termin的大小一致\n    // 队列必须为1，保证termin的创建是顺序的\n    private ExecutorService executor = new ThreadPoolExecutor(1, 1, 10, TimeUnit.SECONDS, new LinkedBlockingQueue(),\n                                                              new NamedThreadFactory(\"Load-Rpc-Async\"));\n\n    public TerminExecutor(Long pipelineId){\n        super(pipelineId);\n    }\n\n    public Future<?> submit(Runnable task) {\n        return executor.submit(task);\n    }\n\n    public void destory() {\n        super.destory();\n        executor.shutdownNow();\n    }\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/setl/rpc/TerminRpcArbitrateEvent.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.setl.rpc;\n\nimport com.alibaba.otter.shared.arbitrate.impl.setl.TerminArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.zookeeper.TerminZooKeeperArbitrateEvent;\n\n/**\n * 基于rpc的仲裁调度的termin信号处理，直接使用zookeeper的现有处理机制\n * \n * @author jianghang 2012-9-29 上午11:06:11\n * @version 4.1.0\n */\npublic class TerminRpcArbitrateEvent extends TerminZooKeeperArbitrateEvent implements TerminArbitrateEvent {\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/setl/rpc/TransformRpcArbitrateEvent.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.setl.rpc;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.util.Assert;\n\nimport com.alibaba.otter.shared.arbitrate.impl.config.ArbitrateConfigUtils;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.ArbitrateFactory;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.TransformArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.monitor.PermitMonitor;\nimport com.alibaba.otter.shared.arbitrate.model.EtlEventData;\nimport com.alibaba.otter.shared.common.model.config.channel.ChannelStatus;\nimport com.alibaba.otter.shared.common.model.config.enums.StageType;\n\n/**\n * 基于rpc方式实现的transform调度\n * \n * @author jianghang 2012-9-29 上午10:59:24\n * @version 4.1.0\n */\npublic class TransformRpcArbitrateEvent implements TransformArbitrateEvent {\n\n    private static final Logger     logger = LoggerFactory.getLogger(TransformRpcArbitrateEvent.class);\n    private RpcStageEventDispatcher rpcStageEventDispatcher;\n\n    public EtlEventData await(Long pipelineId) throws InterruptedException {\n        Assert.notNull(pipelineId);\n\n        PermitMonitor permitMonitor = ArbitrateFactory.getInstance(pipelineId, PermitMonitor.class);\n        permitMonitor.waitForPermit();// 阻塞等待授权\n\n        RpcStageController stageController = ArbitrateFactory.getInstance(pipelineId, RpcStageController.class);\n        Long processId = stageController.waitForProcess(StageType.TRANSFORM); // 符合条件的processId\n\n        ChannelStatus status = permitMonitor.getChannelPermit();\n        if (status.isStart() || status.isPause()) {// pause状态也让其处理，避免误删除pause状态的processId，导致通道挂起\n            EtlEventData eventData = stageController.getLastData(processId);\n            eventData.setNextNid(ArbitrateConfigUtils.getCurrentNid());// 下一个节点信息即为自己\n            return eventData;\n        } else {\n            logger.warn(\"pipelineId[{}] transform ignore processId[{}] by status[{}]\", new Object[] { pipelineId,\n                    processId, status });\n            return await(pipelineId);// 递归调用\n        }\n    }\n\n    public void single(EtlEventData data) {\n        Assert.notNull(data);\n        rpcStageEventDispatcher.single(StageType.TRANSFORM, data);// 通知下一个节点\n    }\n\n    public void setRpcStageEventDispatcher(RpcStageEventDispatcher rpcStageEventDispatcher) {\n        this.rpcStageEventDispatcher = rpcStageEventDispatcher;\n    }\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/setl/rpc/monitor/AbstractProcessListener.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.setl.rpc.monitor;\n\nimport java.util.List;\nimport java.util.concurrent.locks.ReentrantLock;\n\nimport org.apache.commons.lang.ClassUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.util.CollectionUtils;\n\nimport com.alibaba.otter.shared.arbitrate.impl.config.ArbitrateConfigUtils;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.ArbitrateFactory;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.ArbitrateLifeCycle;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.helper.ReplyProcessQueue;\nimport com.alibaba.otter.shared.arbitrate.impl.zookeeper.ZooKeeperClient;\nimport com.alibaba.otter.shared.common.utils.zookeeper.ZkClientx;\n\n/**\n * 抽取stage处理中一些共性的内容\n * \n * @author jianghang 2011-9-21 下午02:16:17\n * @version 4.0.0\n */\npublic abstract class AbstractProcessListener extends ArbitrateLifeCycle implements ProcessListener {\n\n    protected static final Logger logger    = LoggerFactory.getLogger(AbstractProcessListener.class);\n    protected ZkClientx           zookeeper = ZooKeeperClient.getInstance();\n    protected ReplyProcessQueue   replyProcessIds;                                                   // 有响应的processId列表\n    protected ReentrantLock       lock      = new ReentrantLock();\n    protected ProcessMonitor      processMonitor;\n\n    public AbstractProcessListener(Long pipelineId){\n        super(pipelineId);\n        // 设置容量，必须大于并行度，这里设置为并行度的10倍，避免因并行度的运行时变化引起问题\n        int size = ArbitrateConfigUtils.getParallelism(pipelineId) * 10;\n        if (size < 100) {\n            size = 100;\n        }\n\n        replyProcessIds = new ReplyProcessQueue(size);\n        processMonitor = ArbitrateFactory.getInstance(pipelineId, ProcessMonitor.class);\n        processMonitor.addListener(this);\n        processMonitor.reload(); // 触发一下processChanged\n    }\n\n    public void processChanged(List<Long> processIds) {\n        // 在运行过程中会出现Termin(rollback/restart/shutdown)等信号，仲裁器会删除当前运行的所有process\n        // 因此需要删除之前已满足条件的队列记录，在具体的event处理时还会再对processId再做一次判断，是否已被废弃\n        compareReply(processIds);\n    }\n\n    /**\n     * 阻塞方法，获取对应可以被处理的processId，支持中断处理\n     */\n    public Long waitForProcess() throws InterruptedException {\n        // take和history.put操作非原子，addReply操作时会出现并发问题，同一个processId插入两次\n        Long processId = (Long) replyProcessIds.take();\n        logger.debug(\"## {} get reply id [{}]\", ClassUtils.getShortClassName(this.getClass()), processId);\n        return processId;\n    }\n\n    protected synchronized void addReply(Long processId) {\n        boolean isSuccessed = replyProcessIds.offer(processId);\n\n        if (isSuccessed) {\n            logger.debug(\"## {} add reply id [{}]\", ClassUtils.getShortClassName(this.getClass()), processId);\n        } else {\n            logger.warn(\"## {} dup reply id [{}]\", ClassUtils.getShortClassName(this.getClass()), processId);\n        }\n    }\n\n    /**\n     * 将当前的符合条件的processIds和当前的reply queue进行校对，剔除不在processIds里的内容\n     */\n    protected synchronized void compareReply(List<Long> processIds) {\n        Object[] replyIds = replyProcessIds.toArray();\n        for (Object replyId : replyIds) {\n            if (processIds.contains((Long) replyId) == false) { // 判断reply id是否在当前processId列表中\n                // 因为存在并发问题，如在执行Listener事件的同时，可能触发了process的创建，这时新建的processId会进入到reply队列中\n                // 此时接受到的processIds变量为上一个版本的内容，所以会删除新建的process，导致整个通道被挂住\n                if (CollectionUtils.isEmpty(processIds) == false) {\n                    Long processId = processIds.get(0);\n                    if (processId > (Long) replyId) { // 如果当前最小的processId都大于replyId, processId都是递增创建的\n                        logger.info(\"## {} remove reply id [{}]\", ClassUtils.getShortClassName(this.getClass()),\n                                    (Long) replyId);\n                        replyProcessIds.remove((Long) replyId);\n                    }\n                }\n            }\n        }\n    }\n\n    public void destory() {\n        super.destory();\n        logger.info(\"## destory pipeline[{}] , Listener[{}]\", getPipelineId(),\n                    ClassUtils.getShortClassName(this.getClass()));\n\n        processMonitor.removeListener(this);\n        replyProcessIds.clear();\n    }\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/setl/rpc/monitor/ProcessListener.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.setl.rpc.monitor;\n\nimport java.util.List;\n\n/**\n * <pre>\n * 1. 合并S.E.T.L各类事件的监听，减少和zookeeper的交互\n * 2. 采用观察者事件变化推送的模式\n * </pre>\n * \n * @author jianghang 2012-9-28 下午09:38:12\n * @version 4.1.0\n */\npublic interface ProcessListener {\n\n    /**\n     * 触发process变化，传递了变化后最新的processIds列表\n     */\n    public void processChanged(List<Long> processIds);\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/setl/rpc/monitor/ProcessMonitor.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.setl.rpc.monitor;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.concurrent.ExecutorService;\n\nimport org.I0Itec.zkclient.IZkChildListener;\nimport org.apache.commons.lang.ClassUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.slf4j.MDC;\n\nimport com.alibaba.otter.shared.arbitrate.impl.ArbitrateConstants;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.ArbitrateLifeCycle;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.helper.StagePathUtils;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.monitor.Monitor;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.monitor.MonitorScheduler;\nimport com.alibaba.otter.shared.arbitrate.impl.zookeeper.ZooKeeperClient;\nimport com.alibaba.otter.shared.common.utils.zookeeper.ZkClientx;\n\n/**\n * process节点列表的监控，{@linkplain stageMonitor}的简化版本，只关注了process节点的变化，不再关心process的子节点stage的变化，减少了对zk watcher的依赖\n * \n * <pre>\n * 1. 获取当前的process列表，用于控制并行度\n * 2. 获取当前的最小的processId，用于控制load的按顺序载入\n * </pre>\n * \n * @author jianghang 2012-9-28 下午09:35:00\n * @version 4.1.0\n */\npublic class ProcessMonitor extends ArbitrateLifeCycle implements Monitor {\n\n    private static final Logger   logger            = LoggerFactory.getLogger(ProcessMonitor.class);\n\n    private ExecutorService       arbitrateExecutor;\n    private ZkClientx             zookeeper         = ZooKeeperClient.getInstance();\n    private volatile List<Long>   currentProcessIds = new ArrayList<Long>();                                         // 当前的处于监控中的processId列表\n    private List<ProcessListener> listeners         = Collections.synchronizedList(new ArrayList<ProcessListener>());\n    private IZkChildListener      processListener;\n\n    public ProcessMonitor(Long pipelineId){\n        super(pipelineId);\n        processListener = new IZkChildListener() {\n\n            public void handleChildChange(String parentPath, List<String> currentChilds) throws Exception {\n                if (currentChilds != null) {\n                    initProcess(currentChilds);\n                }\n            }\n        };\n\n        String path = StagePathUtils.getProcessRoot(getPipelineId());\n        List<String> childs = zookeeper.subscribeChildChanges(path, processListener);\n        initProcess(childs);\n        // syncStage();\n        MonitorScheduler.register(this);\n    }\n\n    public void reload() {\n        try {\n            if (logger.isDebugEnabled()) {\n                logger.debug(\"## reload Stage pipeline[{}]\", getPipelineId());\n            }\n\n            initProcess();\n        } catch (Exception cause) {\n        }\n    }\n\n    /**\n     * 获取当前的process id列表\n     */\n    public List<Long> getCurrentProcessIds() {\n        return getCurrentProcessIds(false);\n    }\n\n    /**\n     * 获取当前的process id列表，指定是否强制刷新\n     */\n    public List<Long> getCurrentProcessIds(boolean reload) {\n        if (reload) {\n            reload();\n        }\n\n        return currentProcessIds;\n    }\n\n    public void destory() {\n        super.destory();\n        if (logger.isDebugEnabled()) {\n            logger.debug(\"## destory process pipeline[{}]\", getPipelineId());\n        }\n\n        this.listeners.clear();\n        String path = StagePathUtils.getProcessRoot(getPipelineId());\n        zookeeper.unsubscribeChildChanges(path, processListener);\n        MonitorScheduler.unRegister(this);\n    }\n\n    /**\n     * 获取zk列表数据，无须同步处理\n     */\n    private void initProcess() {\n        // 1. 根据pipelineId构造对应的path\n        String path = StagePathUtils.getProcessRoot(getPipelineId());\n        // 2. 获取当前的所有process列表\n        List<String> currentProcesses = zookeeper.getChildren(path);\n        initProcess(currentProcesses);\n    }\n\n    /**\n     * 重新获取最新的process列表\n     */\n    private void initProcess(List<String> currentProcesses) {\n        // 3. 循环处理每个process\n        List<Long> processIds = new ArrayList<Long>();\n        for (String process : currentProcesses) {\n            processIds.add(StagePathUtils.getProcessId(process));\n        }\n        Collections.sort(processIds); // 排序一下\n        if (logger.isDebugEnabled()) {\n            logger.debug(\"pipeline[{}] old processIds{},current processIds{}\", new Object[] { getPipelineId(),\n                    currentProcessIds, processIds });\n        }\n\n        // if (!processIds.equals(currentProcessIds) || currentProcessIds.isEmpty()) {// 不相同，说明有变化\n        processChanged(processIds);// 通知变化\n        // }\n\n        currentProcessIds = processIds; // 切换引用，需设置为volatile保证线程安全&可见性\n    }\n\n    // ======================== listener处理 ======================\n\n    public void addListener(ProcessListener listener) {\n        if (logger.isDebugEnabled()) {\n            logger.debug(\"## pipeline[{}] add listener [{}]\", getPipelineId(),\n                         ClassUtils.getShortClassName(listener.getClass()));\n        }\n\n        this.listeners.add(listener);\n    }\n\n    public void removeListener(ProcessListener listener) {\n        if (logger.isDebugEnabled()) {\n            logger.debug(\"## pipeline[{}] remove listener [{}]\", getPipelineId(),\n                         ClassUtils.getShortClassName(listener.getClass()));\n        }\n\n        this.listeners.remove(listener);\n    }\n\n    private void processChanged(final List<Long> processIds) {\n        for (final ProcessListener listener : listeners) {\n            // 异步处理\n            arbitrateExecutor.submit(new Runnable() {\n\n                public void run() {\n                    MDC.put(ArbitrateConstants.splitPipelineLogFileKey, String.valueOf(getPipelineId()));\n                    listener.processChanged(processIds);\n                }\n            });\n        }\n    }\n\n    public void setArbitrateExecutor(ExecutorService arbitrateExecutor) {\n        this.arbitrateExecutor = arbitrateExecutor;\n    }\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/setl/rpc/monitor/SelectProcessListener.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.setl.rpc.monitor;\n\nimport java.util.List;\n\nimport org.I0Itec.zkclient.exception.ZkException;\nimport org.apache.commons.lang.StringUtils;\nimport org.apache.zookeeper.CreateMode;\n\nimport com.alibaba.otter.shared.arbitrate.impl.config.ArbitrateConfigUtils;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.ArbitrateFactory;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.helper.StagePathUtils;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.monitor.MainstemMonitor;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.monitor.PermitMonitor;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.monitor.listener.MainstemListener;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.monitor.listener.PermitListener;\nimport com.alibaba.otter.shared.arbitrate.model.MainStemEventData;\nimport com.alibaba.otter.shared.arbitrate.model.ProcessNodeEventData;\nimport com.alibaba.otter.shared.common.model.config.pipeline.PipelineParameter.ArbitrateMode;\nimport com.alibaba.otter.shared.common.utils.JsonUtils;\n\n/**\n * 监听process的变化，发现节点数<并行度，则添加一个可调度的process\n * \n * @author jianghang 2012-9-28 下午10:00:22\n * @version 4.1.0\n */\npublic class SelectProcessListener extends AbstractProcessListener implements ProcessListener, PermitListener, MainstemListener {\n\n    private volatile boolean isPermit = true;\n    private PermitMonitor    permitMonitor;\n    private MainstemMonitor  mainstemMonitor;\n\n    public SelectProcessListener(Long pipelineId){\n        super(pipelineId);\n        permitMonitor = ArbitrateFactory.getInstance(pipelineId, PermitMonitor.class);\n        mainstemMonitor = ArbitrateFactory.getInstance(pipelineId, MainstemMonitor.class);\n        permitMonitor.addListener(this);\n        mainstemMonitor.addListener(this);\n\n        recovery(getPipelineId());// 启动时载入一次\n    }\n\n    public void processChanged(List<Long> processIds) {\n        super.processChanged(processIds);\n        // add by ljh at 2012-09-13,解决zookeeper ConnectionLoss问题\n        for (Long processId : processIds) {\n            if (!replyProcessIds.contains(processId)) {\n                logger.warn(\"process is not in order, please check processId:{}\", processId);\n                addReply(processId);\n            }\n        }\n\n        try {\n            String path = StagePathUtils.getProcessRoot(getPipelineId());\n            // 根据并行度创建任务\n            int size = ArbitrateConfigUtils.getParallelism(getPipelineId()) - processIds.size();\n            if (size > 0) {// 创建一个节点\n                PermitMonitor permit = ArbitrateFactory.getInstance(getPipelineId(), PermitMonitor.class);\n                if (permit.isPermit() == false) { // 如果非授权，则不做任何处理\n                    return;\n                }\n\n                String mainStemPath = StagePathUtils.getMainStem(getPipelineId());\n                byte[] bytes = zookeeper.readData(mainStemPath, true);\n                if (bytes == null) {\n                    return;\n                }\n\n                MainStemEventData eventData = JsonUtils.unmarshalFromByte(bytes, MainStemEventData.class);\n                if (eventData.getNid().equals(ArbitrateConfigUtils.getCurrentNid()) == false) {\n                    return;// 如果非自己设置的mainStem,则不做任何处理\n                }\n\n                // 目前select只会在一个节点上部署，只需要单机版锁即可，后续可采用分布式锁进行并发控制\n                // DistributedLock lock = new DistributedLock(PathUtils.getSelectLock(getPipelineId()));\n                // try {\n                // lock.lock();\n                // //创建process\n                // } finally {\n                // lock.unlock();\n                // }\n\n                synchronized (this) {\n                    // 重新再取一次, dobble-check\n                    List<String> currentProcesses = zookeeper.getChildren(path);\n                    size = ArbitrateConfigUtils.getParallelism(getPipelineId()) - currentProcesses.size();\n                    if (size > 0) {// 创建一个节点\n                        ProcessNodeEventData nodeData = new ProcessNodeEventData();\n                        nodeData.setStatus(ProcessNodeEventData.Status.UNUSED);// 标记为未使用\n                        nodeData.setMode(ArbitrateMode.RPC);\n                        nodeData.setNid(ArbitrateConfigUtils.getCurrentNid());\n                        byte[] nodeBytes = JsonUtils.marshalToByte(nodeData);\n                        String processPath = zookeeper.create(path + \"/\", nodeBytes, CreateMode.PERSISTENT_SEQUENTIAL);\n                        // 创建为顺序的节点\n                        String processNode = StringUtils.substringAfterLast(processPath, \"/\");\n                        Long processId = StagePathUtils.getProcessId(processNode);// 添加到当前的process列表\n                        addReply(processId);\n                    }\n                }\n\n            }\n        } catch (ZkException e) {\n            recovery(getPipelineId());// 出现异常后进行一次recovery，读取一下当前最新值，解决出现ConnectionLoss时create成功问题\n            logger.error(\"add process error!\", e);\n        }\n\n    }\n\n    public void processChanged(boolean isPermit) {\n        if (this.isPermit != isPermit && isPermit == true) { // isPemit从未授权到一个授权的变动\n            processMonitor.reload(); // 触发一下processChanged，快速的创建process\n        }\n\n        this.isPermit = isPermit;\n    }\n\n    /**\n     * 尝试载入一下上一次未使用的processId，可能发生mainstem切换，新的S模块需要感知前S模块已创建但未使用的process，不然就是一个死锁。而针对已经使用的processId会由e/t/l节点进行处理\n     */\n    private void recovery(Long pipelineId) {\n        List<Long> currentProcessIds = processMonitor.getCurrentProcessIds(false);\n        for (Long processId : currentProcessIds) {\n            String path = StagePathUtils.getProcess(pipelineId, processId);\n            try {\n                byte[] bytes = zookeeper.readData(path);\n                ProcessNodeEventData nodeData = JsonUtils.unmarshalFromByte(bytes, ProcessNodeEventData.class);\n                if (nodeData.getStatus().isUnUsed()) {// 加入未使用的processId\n                    addReply(processId);\n                }\n            } catch (ZkException e) {\n                logger.error(\"recovery error!\", e);\n            }\n        }\n    }\n\n    public void processActiveEnter() {\n        recovery(getPipelineId());\n        processMonitor.reload(); // 触发一下processChanged\n    }\n\n    public void processActiveExit() {\n        ArbitrateFactory.destory(getPipelineId(), this.getClass());\n    }\n\n    public void destory() {\n        // 取消注册\n        permitMonitor.removeListener(this);\n        mainstemMonitor.removeListener(this);\n        super.destory();\n    }\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/setl/zookeeper/ExtractZooKeeperArbitrateEvent.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.setl.zookeeper;\n\nimport org.I0Itec.zkclient.exception.ZkException;\nimport org.I0Itec.zkclient.exception.ZkInterruptedException;\nimport org.I0Itec.zkclient.exception.ZkNoNodeException;\nimport org.I0Itec.zkclient.exception.ZkNodeExistsException;\nimport org.apache.zookeeper.CreateMode;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.util.Assert;\n\nimport com.alibaba.fastjson.serializer.SerializerFeature;\nimport com.alibaba.otter.shared.arbitrate.exception.ArbitrateException;\nimport com.alibaba.otter.shared.arbitrate.impl.config.ArbitrateConfigUtils;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.ArbitrateFactory;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.ExtractArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.helper.StagePathUtils;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.lb.LoadBalanceFactory;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.monitor.PermitMonitor;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.zookeeper.monitor.ExtractStageListener;\nimport com.alibaba.otter.shared.arbitrate.impl.zookeeper.ZooKeeperClient;\nimport com.alibaba.otter.shared.arbitrate.model.EtlEventData;\nimport com.alibaba.otter.shared.common.model.config.channel.ChannelStatus;\nimport com.alibaba.otter.shared.common.model.config.node.Node;\nimport com.alibaba.otter.shared.common.utils.JsonUtils;\nimport com.alibaba.otter.shared.common.utils.zookeeper.ZkClientx;\n\n/**\n * 关注selected节点，创建extracted节点\n * \n * @author jianghang 2011-8-9 下午05:10:50\n */\npublic class ExtractZooKeeperArbitrateEvent implements ExtractArbitrateEvent {\n\n    private static final Logger logger    = LoggerFactory.getLogger(ExtractZooKeeperArbitrateEvent.class);\n    private ZkClientx           zookeeper = ZooKeeperClient.getInstance();\n\n    // private TerminArbitrateEvent terminEvent;\n\n    /**\n     * <pre>\n     * 算法:\n     * 1. 检查当前的Permit，阻塞等待其授权(解决Channel的pause状态处理)\n     * 2. 开始阻塞获取符合条件的processId\n     * 3. 检查当前的即时Permit状态 (在阻塞获取processId过程会出现一些error信号,process节点会被删除)\n     * 4. 获取Select传递的EventData数据，添加next node信息后直接返回\n     * </pre>\n     * \n     * @return\n     */\n    public EtlEventData await(Long pipelineId) throws InterruptedException {\n        Assert.notNull(pipelineId);\n        PermitMonitor permitMonitor = ArbitrateFactory.getInstance(pipelineId, PermitMonitor.class);\n        permitMonitor.waitForPermit();// 阻塞等待授权\n\n        ExtractStageListener extractStageListener = ArbitrateFactory.getInstance(pipelineId, ExtractStageListener.class);\n        Long processId = extractStageListener.waitForProcess(); // 符合条件的processId\n\n        ChannelStatus status = permitMonitor.getChannelPermit();\n        if (status.isStart()) {// 即时查询一下当前的状态，状态随时可能会变\n            // 根据pipelineId+processId构造对应的path\n            String path = StagePathUtils.getSelectStage(pipelineId, processId);\n\n            try {\n                byte[] data = zookeeper.readData(path);\n                EtlEventData eventData = JsonUtils.unmarshalFromByte(data, EtlEventData.class);\n\n                Node node = LoadBalanceFactory.getNextTransformNode(pipelineId);// 获取下一个处理节点信息\n                if (node == null) {// 没有后端节点\n                    // TerminEventData termin = new TerminEventData();\n                    // termin.setPipelineId(pipelineId);\n                    // termin.setType(TerminType.ROLLBACK);\n                    // termin.setCode(\"no_node\");\n                    // termin.setDesc(MessageFormat.format(\"pipeline[{}] extract stage has no node!\", pipelineId));\n                    // terminEvent.single(termin);\n                    throw new ArbitrateException(\"Extract_single\", \"no next node\");\n                } else {\n                    eventData.setNextNid(node.getId());\n                    return eventData;// 只有这一条路返回\n                }\n            } catch (ZkNoNodeException e) {\n                logger.error(\"pipeline[{}] processId[{}] is invalid , retry again\", pipelineId, processId);\n                return await(pipelineId);// /出现节点不存在，说明出现了error情况,递归调用重新获取一次\n            } catch (ZkException e) {\n                throw new ArbitrateException(\"Extract_await\", e.getMessage(), e);\n            }\n        } else {\n            logger.warn(\"pipelineId[{}] extract ignore processId[{}] by status[{}]\", new Object[] { pipelineId,\n                    processId, status });\n                    \n            // 释放下processId，因为load是等待processId最小值完成Tranform才继续，如果这里不释放，会一直卡死等待\n            String path = StagePathUtils.getProcess(pipelineId, processId);\n            zookeeper.delete(path);\n            return await(pipelineId);// 递归调用\n        }\n\n    }\n\n    /**\n     * <pre>\n     * 算法:\n     * 1. 创建对应的extracted节点,标志extract已完成\n     * </pre>\n     * \n     * @param pipelineId 同步流id\n     */\n    public void single(EtlEventData data) {\n        Assert.notNull(data);\n        String path = StagePathUtils.getExtractStage(data.getPipelineId(), data.getProcessId());\n        data.setCurrNid(ArbitrateConfigUtils.getCurrentNid());\n        // 序列化\n        byte[] bytes = JsonUtils.marshalToByte(data, SerializerFeature.WriteClassName);\n        try {\n            zookeeper.create(path, bytes, CreateMode.PERSISTENT);\n        } catch (ZkNoNodeException e) {\n            // process节点不存在，出现了rollback/shutdown操作，直接忽略\n            logger.warn(\"pipelineId[{}] extract ignore processId[{}] single by data:{}\",\n                        new Object[] { data.getPipelineId(), data.getProcessId(), data });\n        } catch (ZkNodeExistsException e) {\n            // process节点已存在，出现了ConnectionLoss retry操作\n            logger.warn(\"pipelineId[{}] extract ignore processId[{}] single by data:{}\",\n                        new Object[] { data.getPipelineId(), data.getProcessId(), data });\n        } catch (ZkInterruptedException e) {\n            // ignore\n        } catch (ZkException e) {\n            throw new ArbitrateException(\"Extract_single\", e.getMessage(), e);\n        }\n    }\n\n    // public void setTerminEvent(TerminArbitrateEvent terminEvent) {\n    // this.terminEvent = terminEvent;\n    // }\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/setl/zookeeper/LoadZooKeeperArbitrateEvent.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.setl.zookeeper;\n\nimport java.util.Date;\n\nimport org.I0Itec.zkclient.exception.ZkException;\nimport org.I0Itec.zkclient.exception.ZkNoNodeException;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.util.Assert;\n\nimport com.alibaba.otter.shared.arbitrate.exception.ArbitrateException;\nimport com.alibaba.otter.shared.arbitrate.impl.config.ArbitrateConfigUtils;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.ArbitrateFactory;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.LoadArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.helper.StagePathUtils;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.monitor.PermitMonitor;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.zookeeper.monitor.LoadStageListener;\nimport com.alibaba.otter.shared.arbitrate.impl.zookeeper.ZooKeeperClient;\nimport com.alibaba.otter.shared.arbitrate.model.EtlEventData;\nimport com.alibaba.otter.shared.arbitrate.model.TerminEventData;\nimport com.alibaba.otter.shared.arbitrate.model.TerminEventData.TerminType;\nimport com.alibaba.otter.shared.common.model.config.channel.ChannelStatus;\nimport com.alibaba.otter.shared.common.utils.JsonUtils;\nimport com.alibaba.otter.shared.common.utils.zookeeper.ZkClientx;\n\n/**\n * 关注transformed节点，创建loaded节点\n * \n * @version 4.0.3\n * \n * <pre>\n * 1. 去除对应的DistributedLock的操作，减少中美同步时的latency. 每次DistributedLock操作都会涉及中美zk集群的交互，延迟在300ms\n * \n * </pre>\n * @author jianghang 2011-8-9 下午05:10:50\n */\npublic class LoadZooKeeperArbitrateEvent implements LoadArbitrateEvent {\n\n    private static final Logger           logger    = LoggerFactory.getLogger(LoadZooKeeperArbitrateEvent.class);\n    private ZkClientx                     zookeeper = ZooKeeperClient.getInstance();\n    private TerminZooKeeperArbitrateEvent terminEvent;\n\n    // private Map<Long, DistributedLock> locks = new ConcurrentHashMap<Long,\n    // DistributedLock>();\n\n    /**\n     * <pre>\n     * 算法:\n     * 1. 检查当前的Permit，阻塞等待其授权(解决Channel的pause状态处理)\n     * 2. 开始阻塞获取符合条件的processId\n     * 3. 检查当前的即时Permit状态 (在阻塞获取processId过程会出现一些error信号,process节点会被删除)\n     * 4. 获取Select传递的EventData数据，添加next node信息后直接返回\n     * </pre>\n     */\n    public EtlEventData await(Long pipelineId) throws InterruptedException {\n        Assert.notNull(pipelineId);\n        PermitMonitor permitMonitor = ArbitrateFactory.getInstance(pipelineId, PermitMonitor.class);\n        permitMonitor.waitForPermit();// 阻塞等待授权\n\n        LoadStageListener loadStageListener = ArbitrateFactory.getInstance(pipelineId, LoadStageListener.class);\n        Long processId = loadStageListener.waitForProcess(); // 符合条件的processId\n\n        // DistributedLock lock = getLock(pipelineId);\n        try {\n            // 使用锁的理由：\n            // 1. 针对双向同步时，其中一个方向出现了异常，需要发起另一端的关闭，此时对方正好在执行某个process的load\n            // 2. 单向同步时，如果出现node节点异常，此时正常的节点正在执行某个process的load\n            // 为避免因load无法中端引起的数据重复录入，所以针对load阶段添加分布式锁。在有process\n            // load过程中不允许进行pipeline关闭操作\n            // lock.lock();\n\n            ChannelStatus status = permitMonitor.getChannelPermit();\n            if (status.isStart()) {// 即时查询一下当前的状态，状态随时可能会变\n                // 根据pipelineId+processId构造对应的path\n                String path = StagePathUtils.getTransformStage(pipelineId, processId);\n                try {\n                    byte[] data = zookeeper.readData(path);\n                    return JsonUtils.unmarshalFromByte(data, EtlEventData.class);// 反序列化并返回\n                } catch (ZkNoNodeException e) {\n                    logger.error(\"pipeline[{}] processId[{}] is invalid , retry again\", pipelineId, processId);\n                    // try {\n                    // lock.unlock();// 出现任何异常解除lock\n                    // } catch (KeeperException e1) {\n                    // // ignore\n                    // }\n                    return await(pipelineId);// /出现节点不存在，说明出现了error情况,递归调用重新获取一次\n                } catch (ZkException e) {\n                    throw e;\n                }\n            } else {\n                logger.warn(\"pipelineId[{}] load ignore processId[{}] by status[{}]\", new Object[] { pipelineId,\n                        processId, status });\n                // try {\n                // lock.unlock();// 出现任何异常解除lock\n                // } catch (KeeperException e) {\n                // // ignore\n                // }\n                // 释放下processId，因为load是等待processId最小值完成Tranform才继续，如果这里不释放，会一直卡死等待\n                String path = StagePathUtils.getProcess(pipelineId, processId);\n                zookeeper.delete(path);\n                return await(pipelineId);// 出现rollback情况，递归调用重新获取一次，当前的processId可丢弃\n            }\n        } catch (InterruptedException e) {\n            throw e;\n        } catch (Exception e) {\n            // try {\n            // lock.unlock();// 出现任何异常解除lock\n            // } catch (KeeperException e1) {\n            // // ignore\n            // }\n\n            throw new ArbitrateException(e);\n        }\n    }\n\n    /**\n     * <pre>\n     * 算法:\n     * 1. 创建对应的loaded节点,标志load已完成\n     * </pre>\n     * \n     * @param pipelineId 同步流id\n     */\n    public void single(EtlEventData data) {\n        Assert.notNull(data);\n        try {\n            // String path = StagePathUtils.getLoadStage(data.getPipelineId(),\n            // data.getProcessId());\n            // // 序列化\n            // data.setEndTime(new Date().getTime());// 返回当前时间\n            // byte[] bytes = JsonUtils.marshalToByte(data,\n            // SerializerFeature.WriteClassName);\n            // try {\n            // zookeeper.create(path, bytes, CreateMode.PERSISTENT);\n            // } catch (NodeExistsException e) {\n            // throw new ArbitrateException(\"Load_single\", e.getMessage(), e);\n            // } catch (KeeperException e) {\n            // throw new ArbitrateException(\"Load_single\", e.getMessage(), e);\n            // } catch (InterruptedException e) {\n            // // ignore\n            // }\n\n            data.setEndTime(new Date().getTime());// 返回当前时间\n\n            // 调用Termin信号\n            TerminEventData termin = new TerminEventData();\n            termin.setPipelineId(data.getPipelineId());\n            termin.setProcessId(data.getProcessId());\n            termin.setStartTime(data.getStartTime());\n            termin.setEndTime(data.getEndTime());\n            termin.setFirstTime(data.getFirstTime());\n            termin.setNumber(data.getNumber());\n            termin.setBatchId(data.getBatchId());\n            termin.setSize(data.getSize());\n            termin.setExts(data.getExts());\n            termin.setType(TerminType.NORMAL);\n            termin.setCode(\"setl\");\n            termin.setDesc(\"\");\n            termin.setCurrNid(ArbitrateConfigUtils.getCurrentNid());\n            terminEvent.single(termin);\n        } finally {\n            // // 最后一步释放锁\n            // DistributedLock lock = getLock(data.getPipelineId());\n            // try {\n            // lock.unlock();// 解除lock\n            // } catch (KeeperException e) {\n            // throw new ArbitrateException(\"Load_single\", e.getMessage(), e);\n            // }\n        }\n    }\n\n    /**\n     * <pre>\n     * 算法:\n     * 1. load出现异常，解除load锁定，并发送对应的termin信号\n     * </pre>\n     * \n     * @param pipelineId 同步流id\n     */\n    public void release(Long pipelineId) {\n        // DistributedLock lock = getLock(pipelineId);\n        // try {\n        // lock.unlock();// 解除lock\n        // } catch (KeeperException e) {\n        // throw new ArbitrateException(\"Load_single\", e.getMessage(), e);\n        // }\n    }\n\n    // private DistributedLock getLock(Long pipelineId) {\n    // DistributedLock lock = locks.get(pipelineId);\n    // if (lock == null) {\n    // synchronized (locks) {\n    // if (!locks.containsKey(pipelineId)) {\n    // lock = new DistributedLock(StagePathUtils.getLoadLock(pipelineId));\n    // locks.put(pipelineId, lock);\n    // }\n    // }\n    // }\n    //\n    // return lock;\n    // }\n\n    // ======================== setter / getter ==========================\n\n    public void setTerminEvent(TerminZooKeeperArbitrateEvent terminEvent) {\n        this.terminEvent = terminEvent;\n    }\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/setl/zookeeper/SelectZooKeeperArbitrateEvent.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.setl.zookeeper;\n\nimport java.util.Date;\n\nimport org.I0Itec.zkclient.exception.ZkException;\nimport org.I0Itec.zkclient.exception.ZkNoNodeException;\nimport org.I0Itec.zkclient.exception.ZkNodeExistsException;\nimport org.apache.zookeeper.CreateMode;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.util.Assert;\n\nimport com.alibaba.fastjson.serializer.SerializerFeature;\nimport com.alibaba.otter.shared.arbitrate.exception.ArbitrateException;\nimport com.alibaba.otter.shared.arbitrate.impl.config.ArbitrateConfigUtils;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.ArbitrateFactory;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.SelectArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.helper.StagePathUtils;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.lb.LoadBalanceFactory;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.monitor.PermitMonitor;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.zookeeper.monitor.SelectStageListener;\nimport com.alibaba.otter.shared.arbitrate.impl.zookeeper.ZooKeeperClient;\nimport com.alibaba.otter.shared.arbitrate.model.EtlEventData;\nimport com.alibaba.otter.shared.arbitrate.model.ProcessNodeEventData;\nimport com.alibaba.otter.shared.common.model.config.channel.ChannelStatus;\nimport com.alibaba.otter.shared.common.model.config.node.Node;\nimport com.alibaba.otter.shared.common.model.config.pipeline.PipelineParameter.ArbitrateMode;\nimport com.alibaba.otter.shared.common.utils.JsonUtils;\nimport com.alibaba.otter.shared.common.utils.zookeeper.ZkClientx;\n\n/**\n * 基于zookeeper的仲裁器调度\n * \n * @author jianghang 2011-8-9 下午05:10:50\n */\npublic class SelectZooKeeperArbitrateEvent implements SelectArbitrateEvent {\n\n    private static final Logger logger    = LoggerFactory.getLogger(SelectZooKeeperArbitrateEvent.class);\n    private ZkClientx           zookeeper = ZooKeeperClient.getInstance();\n\n    // private TerminArbitrateEvent terminEvent;\n\n    /**\n     * <pre>\n     * 算法:\n     * 1. 检查当前的Permit，阻塞等待其授权(解决Channel的pause状态处理)\n     * 2. 开始阻塞获取符合条件的processId，创建空的EventData对象,添加next node信息后直接返回\n     * </pre>\n     */\n    public EtlEventData await(Long pipelineId) throws InterruptedException {\n        Assert.notNull(pipelineId);\n\n        PermitMonitor permitMonitor = ArbitrateFactory.getInstance(pipelineId, PermitMonitor.class);\n        permitMonitor.waitForPermit();// 阻塞等待授权\n\n        SelectStageListener selectStageListener = ArbitrateFactory.getInstance(pipelineId, SelectStageListener.class);\n        Long processId = selectStageListener.waitForProcess(); // 符合条件的processId\n\n        ChannelStatus status = permitMonitor.getChannelPermit();\n        if (status.isStart()) {// 即时查询一下当前的状态，状态随时可能会变\n\n            try {\n                EtlEventData eventData = new EtlEventData();\n                eventData.setPipelineId(pipelineId);\n                eventData.setProcessId(processId);\n                eventData.setStartTime(new Date().getTime());// 返回当前时间\n\n                Node node = LoadBalanceFactory.getNextExtractNode(pipelineId);// 获取下一个处理节点信息\n                if (node == null) {// 没有后端节点\n                    // TerminEventData termin = new TerminEventData();\n                    // termin.setPipelineId(pipelineId);\n                    // termin.setType(TerminType.ROLLBACK);\n                    // termin.setCode(\"no_node\");\n                    // termin.setDesc(MessageFormat.format(\"pipeline[{}] extract stage has no node!\", pipelineId));\n                    // terminEvent.single(termin);\n                    throw new ArbitrateException(\"Select_single\", \"no next node\");\n                } else {\n                    eventData.setNextNid(node.getId());\n                    markUsed(eventData); // 标记为已使用\n                    return eventData;// 只有这一条路返回\n                }\n            } catch (ZkNoNodeException e) {\n                logger.error(\"pipeline[{}] processId[{}] is invalid , retry again\", pipelineId, processId);\n                return await(pipelineId);// /出现节点不存在，说明出现了error情况,递归调用重新获取一次\n            } catch (ZkException e) {\n                throw new ArbitrateException(\"Select_await\", e.getMessage(), e);\n            }\n        } else {\n            logger.warn(\"pipelineId[{}] select ignore processId[{}] by status[{}]\", new Object[] { pipelineId,\n                    processId, status });\n            // add by ljh 2013-02-01\n            // 遇到一个bug:\n            // a. 某台机器发起了一个RESTART指令，然后开始删除process列表\n            // b. 此时另一个台机器(select工作节点)，并没有收到PAUSE的推送，导致还会再创建一个process节点\n            // c. 后续收到PAUSE指令后，丢弃了processId，就出现了unused的processId\n            // 这里删除了，要考虑一个问题，就是和restart指令在并行删除同一个processId时的并发考虑，目前来看没问题\n            String path = StagePathUtils.getProcess(pipelineId, processId);\n            zookeeper.delete(path); // 忽略删除失败\n            return await(pipelineId);// 递归调用\n        }\n    }\n\n    /**\n     * <pre>\n     * 算法:\n     * 1. 创建对应的selected节点,标志selected已完成\n     * </pre>\n     * \n     * @param pipelineId 同步流id\n     */\n    public void single(EtlEventData data) {\n        Assert.notNull(data);\n        String path = StagePathUtils.getSelectStage(data.getPipelineId(), data.getProcessId());\n        data.setCurrNid(ArbitrateConfigUtils.getCurrentNid());\n        // 序列化\n        byte[] bytes = JsonUtils.marshalToByte(data, SerializerFeature.WriteClassName);\n        try {\n            zookeeper.create(path, bytes, CreateMode.PERSISTENT);\n        } catch (ZkNoNodeException e) {\n            // process节点不存在，出现了rollback/shutdown操作，直接忽略\n            logger.warn(\"pipelineId[{}] select ignore processId[{}] single by data:{}\",\n                        new Object[] { data.getPipelineId(), data.getProcessId(), data });\n        } catch (ZkNodeExistsException e) {\n            // process节点已存在，出现了ConnectionLoss retry操作\n            logger.warn(\"pipelineId[{}] select ignore processId[{}] single by data:{}\",\n                        new Object[] { data.getPipelineId(), data.getProcessId(), data });\n        } catch (ZkException e) {\n            throw new ArbitrateException(\"Select_single\", e.getMessage(), e);\n        }\n\n    }\n\n    /**\n     * 标记一下当前process为已使用\n     */\n    private void markUsed(EtlEventData data) throws ZkNoNodeException, ZkException {\n        String path = StagePathUtils.getProcess(data.getPipelineId(), data.getProcessId());\n        // 序列化\n        ProcessNodeEventData eventData = new ProcessNodeEventData();\n        Long nid = ArbitrateConfigUtils.getCurrentNid();\n        eventData.setNid(nid);\n        eventData.setStatus(ProcessNodeEventData.Status.USED);// 标记为已使用\n        eventData.setMode(ArbitrateMode.ZOOKEEPER);// 直接声明为zookeeper模式\n        byte[] bytes = JsonUtils.marshalToByte(eventData);\n        zookeeper.writeData(path, bytes);\n    }\n\n    // public void setTerminEvent(TerminArbitrateEvent terminEvent) {\n    // this.terminEvent = terminEvent;\n    // }\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/setl/zookeeper/TerminZooKeeperArbitrateEvent.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.setl.zookeeper;\n\nimport org.I0Itec.zkclient.exception.ZkException;\nimport org.I0Itec.zkclient.exception.ZkNoNodeException;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.util.Assert;\n\nimport com.alibaba.otter.shared.arbitrate.exception.ArbitrateException;\nimport com.alibaba.otter.shared.arbitrate.impl.communication.ArbitrateCommmunicationClient;\nimport com.alibaba.otter.shared.arbitrate.impl.config.ArbitrateConfigUtils;\nimport com.alibaba.otter.shared.arbitrate.impl.manage.ChannelArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.ArbitrateFactory;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.TerminArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.helper.StagePathUtils;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.monitor.TerminMonitor;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.zookeeper.termin.NormalTerminProcess;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.zookeeper.termin.WarningTerminProcess;\nimport com.alibaba.otter.shared.arbitrate.impl.zookeeper.ZooKeeperClient;\nimport com.alibaba.otter.shared.arbitrate.model.TerminEventData;\nimport com.alibaba.otter.shared.arbitrate.model.TerminEventData.TerminType;\nimport com.alibaba.otter.shared.common.model.config.channel.Channel;\nimport com.alibaba.otter.shared.common.utils.JsonUtils;\nimport com.alibaba.otter.shared.common.utils.zookeeper.ZkClientx;\nimport com.alibaba.otter.shared.communication.model.arbitrate.StopChannelEvent;\n\n/**\n * 处理termin信号\n * \n * @author jianghang 2011-8-9 下午04:39:20\n */\npublic class TerminZooKeeperArbitrateEvent implements TerminArbitrateEvent {\n\n    private static final Logger           logger    = LoggerFactory.getLogger(TerminZooKeeperArbitrateEvent.class);\n\n    private ZkClientx                     zookeeper = ZooKeeperClient.getInstance();\n    private ArbitrateCommmunicationClient arbitrateCommmunicationClient;\n    private NormalTerminProcess           normalTerminProcess;\n    private WarningTerminProcess          warningTerminProcess;\n    private ChannelArbitrateEvent         channelEvent;\n\n    /**\n     * <pre>\n     * 算法:\n     * 1. 开始阻塞获取符合条件的processId，获取对应的data数据直接返回\n     * </pre>\n     */\n    public TerminEventData await(Long pipelineId) throws InterruptedException {\n        Assert.notNull(pipelineId);\n        TerminMonitor terminMonitor = ArbitrateFactory.getInstance(pipelineId, TerminMonitor.class);\n        Long processId = terminMonitor.waitForProcess(); // 符合条件的processId\n        if (logger.isDebugEnabled()) {\n            logger.debug(\"## await pipeline[{}] processId[{}] is termin\", pipelineId, processId);\n        }\n\n        // 根据pipelineId+processId构造对应的path\n        String path = StagePathUtils.getTermin(pipelineId, processId);\n\n        try {\n            byte[] data = zookeeper.readData(path);\n            return JsonUtils.unmarshalFromByte(data, TerminEventData.class);\n        } catch (ZkNoNodeException e) {\n            logger.error(\"pipeline[{}] processId[{}] is process\", pipelineId, processId);\n            terminMonitor.ack(processId); // modify for 2012-09-08, 发生主备切换时，await会进入死循环，针对NoNode后直接从内存队列中移除\n            return await(pipelineId); // 再取下一个节点\n        } catch (ZkException e) {\n            throw new ArbitrateException(\"Termin_await\", e);\n        }\n    }\n\n    /**\n     * 消耗掉所有的termin信号\n     */\n    public void exhaust(Long pipelineId) {\n        Assert.notNull(pipelineId);\n        TerminMonitor terminMonitor = ArbitrateFactory.getInstance(pipelineId, TerminMonitor.class);\n        int size = terminMonitor.size();\n        try {\n            for (int i = 0; i < size; i++) {\n                Long processId;\n                processId = terminMonitor.waitForProcess();\n                TerminEventData data = new TerminEventData();\n                data.setPipelineId(pipelineId);\n                data.setProcessId(processId);\n                ack(data);\n            }\n        } catch (InterruptedException e) {\n            throw new ArbitrateException(e);\n        }\n    }\n\n    /**\n     * <pre>\n     * 算法:\n     * 1. 客户端处理完成对应的termin事件后，反馈给仲裁器处理完成。仲裁器根据对应S.E.T.L的反馈情况，判断是否删除对应的termin信号\n     * </pre>\n     */\n    public void ack(TerminEventData data) {\n        Assert.notNull(data);\n        // 目前只有select模块需要发送ack信号，这里一旦收到一个信号后就删除对应的termin节点，后续可扩展\n        // 删除termin节点\n        String path = StagePathUtils.getTermin(data.getPipelineId(), data.getProcessId());\n        try {\n            zookeeper.delete(path);\n        } catch (ZkNoNodeException e) {\n            // ignore,说明节点已经被删除\n        } catch (ZkException e) {\n            throw new ArbitrateException(\"Termin_ack\", e);\n        }\n\n        TerminMonitor terminMonitor = ArbitrateFactory.getInstance(data.getPipelineId(), TerminMonitor.class);\n        terminMonitor.ack(data.getProcessId());\n    }\n\n    /**\n     * 查询当前待处理的termin信号的总数\n     */\n    public int size(Long pipelineId) {\n        Assert.notNull(pipelineId);\n\n        TerminMonitor terminMonitor = ArbitrateFactory.getInstance(pipelineId, TerminMonitor.class);\n        return terminMonitor.size();\n    }\n\n    /**\n     * <pre>\n     * 算法:\n     * 1. 创建对应的termin节点,标志process为终结状态\n     * </pre>\n     */\n    public void single(final TerminEventData data) {\n        // 正向处理\n        final TerminType type = data.getType();\n        if (type.isNormal()) {\n            Assert.notNull(data.getProcessId());\n            normalTerminProcess.process(data); // 单独处理\n        } else if (type.isWarning()) {\n            warningTerminProcess.process(data); // warn单独处理，不需要关闭相关的pipeline\n        } else {\n            Channel channel = ArbitrateConfigUtils.getChannel(data.getPipelineId());\n\n            if (data.getType().isRollback()) {\n                boolean paused = channelEvent.pause(channel.getId());\n                if (paused) {// 如果pause成功，则发送报警信息\n                    warningTerminProcess.process(data);\n                }\n            } else if (data.getType().isShutdown()) {\n                boolean shutdowned = channelEvent.stop(channel.getId());\n                // 发送报警信息\n                if (shutdowned) {\n                    warningTerminProcess.process(data);\n                }\n                // 发送关闭命令给manager\n                StopChannelEvent event = new StopChannelEvent();\n                event.setChannelId(channel.getId());\n                arbitrateCommmunicationClient.callManager(event);\n            } else if (data.getType().isRestart()) {\n                boolean restarted = channelEvent.restart(channel.getId());\n                // 发送报警信息\n                if (restarted) {\n                    warningTerminProcess.process(data);\n                }\n            }\n        }\n    }\n\n    // ================== setter / getter ===================\n\n    public void setArbitrateCommmunicationClient(ArbitrateCommmunicationClient arbitrateCommmunicationClient) {\n        this.arbitrateCommmunicationClient = arbitrateCommmunicationClient;\n    }\n\n    public void setNormalTerminProcess(NormalTerminProcess normalTerminProcess) {\n        this.normalTerminProcess = normalTerminProcess;\n    }\n\n    public void setWarningTerminProcess(WarningTerminProcess warningTerminProcess) {\n        this.warningTerminProcess = warningTerminProcess;\n    }\n\n    public void setChannelEvent(ChannelArbitrateEvent channelEvent) {\n        this.channelEvent = channelEvent;\n    }\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/setl/zookeeper/TransformZooKeeperArbitrateEvent.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.setl.zookeeper;\n\nimport org.I0Itec.zkclient.exception.ZkException;\nimport org.I0Itec.zkclient.exception.ZkNoNodeException;\nimport org.I0Itec.zkclient.exception.ZkNodeExistsException;\nimport org.apache.zookeeper.CreateMode;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.util.Assert;\n\nimport com.alibaba.fastjson.serializer.SerializerFeature;\nimport com.alibaba.otter.shared.arbitrate.exception.ArbitrateException;\nimport com.alibaba.otter.shared.arbitrate.impl.config.ArbitrateConfigUtils;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.ArbitrateFactory;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.TransformArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.helper.StagePathUtils;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.monitor.PermitMonitor;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.zookeeper.monitor.TransformStageListener;\nimport com.alibaba.otter.shared.arbitrate.impl.zookeeper.ZooKeeperClient;\nimport com.alibaba.otter.shared.arbitrate.model.EtlEventData;\nimport com.alibaba.otter.shared.common.model.config.channel.ChannelStatus;\nimport com.alibaba.otter.shared.common.utils.JsonUtils;\nimport com.alibaba.otter.shared.common.utils.zookeeper.ZkClientx;\n\n/**\n * 关注extracted节点，创建transformed节点\n * \n * @author jianghang 2011-8-9 下午05:10:50\n */\npublic class TransformZooKeeperArbitrateEvent implements TransformArbitrateEvent {\n\n    private static final Logger logger    = LoggerFactory.getLogger(TransformZooKeeperArbitrateEvent.class);\n    private ZkClientx           zookeeper = ZooKeeperClient.getInstance();\n\n    /**\n     * <pre>\n     * 算法:\n     * 1. 检查当前的Permit，阻塞等待其授权(解决Channel的pause状态处理)\n     * 2. 开始阻塞获取符合条件的processId\n     * 3. 检查当前的即时Permit状态 (在阻塞获取processId过程会出现一些error信号,process节点会被删除)\n     * 4. 获取Select传递的EventData数据，添加next node信息后直接返回\n     * </pre>\n     * \n     * @return\n     */\n    public EtlEventData await(Long pipelineId) throws InterruptedException {\n        Assert.notNull(pipelineId);\n        PermitMonitor permitMonitor = ArbitrateFactory.getInstance(pipelineId, PermitMonitor.class);\n        permitMonitor.waitForPermit();// 阻塞等待授权\n\n        TransformStageListener transformStageListener = ArbitrateFactory.getInstance(pipelineId,\n                                                                                     TransformStageListener.class);\n        Long processId = transformStageListener.waitForProcess(); // 符合条件的processId\n\n        ChannelStatus status = permitMonitor.getChannelPermit();\n        if (status.isStart()) {// 即时查询一下当前的状态，状态随时可能会变\n            // 根据pipelineId+processId构造对应的path\n            String path = StagePathUtils.getExtractStage(pipelineId, processId);\n\n            try {\n                byte[] data = zookeeper.readData(path);\n                EtlEventData eventData = JsonUtils.unmarshalFromByte(data, EtlEventData.class);\n                eventData.setNextNid(ArbitrateConfigUtils.getCurrentNid());// 下一个节点信息即为自己\n                return eventData;// 只有这一条路返回\n            } catch (ZkNoNodeException e) {\n                logger.error(\"pipeline[{}] processId[{}] is invalid , retry again\", pipelineId, processId);\n                return await(pipelineId);// /出现节点不存在，说明出现了error情况,递归调用重新获取一次\n            } catch (ZkException e) {\n                throw new ArbitrateException(\"transform_await\", e.getMessage(), e);\n            }\n        } else {\n            logger.info(\"pipelineId[{}] transform ignore processId[{}] by status[{}]\", new Object[] { pipelineId,\n                    processId, status });\n                    \n            // 释放下processId，因为load是等待processId最小值完成Tranform才继续，如果这里不释放，会一直卡死等待\n            String path = StagePathUtils.getProcess(pipelineId, processId);\n            zookeeper.delete(path);\n            return await(pipelineId);// 递归调用\n        }\n    }\n\n    /**\n     * <pre>\n     * 算法:\n     * 1. 创建对应的transformed节点,标志transform已完成\n     * </pre>\n     * \n     * @param pipelineId 同步流id\n     */\n    public void single(EtlEventData data) {\n        Assert.notNull(data);\n        String path = StagePathUtils.getTransformStage(data.getPipelineId(), data.getProcessId());\n        data.setCurrNid(ArbitrateConfigUtils.getCurrentNid());\n        // 序列化\n        byte[] bytes = JsonUtils.marshalToByte(data, SerializerFeature.WriteClassName);\n        try {\n            zookeeper.create(path, bytes, CreateMode.PERSISTENT);\n        } catch (ZkNoNodeException e) {\n            // process节点不存在，出现了rollback/shutdown操作，直接忽略\n            logger.warn(\"pipelineId[{}] transform ignore processId[{}] single by data:{}\",\n                        new Object[] { data.getPipelineId(), data.getProcessId(), data });\n        } catch (ZkNodeExistsException e) {\n            // process节点已存在，出现了ConnectionLoss retry操作\n            logger.warn(\"pipelineId[{}] transform ignore processId[{}] single by data:{}\",\n                        new Object[] { data.getPipelineId(), data.getProcessId(), data });\n        } catch (ZkException e) {\n            throw new ArbitrateException(\"transform_single\", e.getMessage(), e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/setl/zookeeper/monitor/AbstractStageListener.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.setl.zookeeper.monitor;\n\nimport java.util.List;\nimport java.util.concurrent.locks.ReentrantLock;\n\nimport org.apache.commons.lang.ClassUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.util.CollectionUtils;\n\nimport com.alibaba.otter.shared.arbitrate.impl.config.ArbitrateConfigUtils;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.ArbitrateFactory;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.ArbitrateLifeCycle;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.helper.ReplyProcessQueue;\nimport com.alibaba.otter.shared.arbitrate.impl.zookeeper.ZooKeeperClient;\nimport com.alibaba.otter.shared.common.utils.zookeeper.ZkClientx;\n\n/**\n * 抽取stage处理中一些共性的内容\n * \n * @author jianghang 2011-9-21 下午02:16:17\n * @version 4.0.0\n */\npublic abstract class AbstractStageListener extends ArbitrateLifeCycle implements StageListener {\n\n    protected static final Logger logger    = LoggerFactory.getLogger(AbstractStageListener.class);\n    protected ZkClientx           zookeeper = ZooKeeperClient.getInstance();\n    protected ReplyProcessQueue   replyProcessIds;                                                 // 有响应的processId列表\n    protected ReentrantLock       lock      = new ReentrantLock();\n    protected StageMonitor        stageMonitor;\n\n    public AbstractStageListener(Long pipelineId){\n        super(pipelineId);\n        // 设置容量，必须大于并行度，这里设置为并行度的10倍，避免因并行度的运行时变化引起问题\n        int size = ArbitrateConfigUtils.getParallelism(pipelineId) * 10;\n        if (size < 100) {\n            size = 100;\n        }\n\n        replyProcessIds = new ReplyProcessQueue(size);\n        stageMonitor = ArbitrateFactory.getInstance(pipelineId, StageMonitor.class);\n        stageMonitor.addListener(this);\n        stageMonitor.reload(); // 触发一下processChanged\n    }\n\n    public void processChanged(List<Long> processIds) {\n        // do nothing\n        compareReply(processIds);\n    }\n\n    public void stageChannged(Long processId, List<String> stageNode) {\n        // do nothing\n    }\n\n    public synchronized void processTermined(Long processId) {\n        // 在运行过程中会出现Termin(rollback/restart/shutdown)等信号，仲裁器会删除当前运行的所有process\n        // 因此需要删除之前已满足条件的队列记录，在具体的event处理时还会再对processId再做一次判断，是否已被废弃\n        logger.info(\"## {} remove reply id [{}]\", ClassUtils.getShortClassName(this.getClass()), processId);\n        replyProcessIds.remove(processId);\n    }\n\n    /**\n     * 阻塞方法，获取对应可以被处理的processId，支持中断处理\n     */\n    public Long waitForProcess() throws InterruptedException {\n        // take和history.put操作非原子，addReply操作时会出现并发问题，同一个processId插入两次\n        Long processId = (Long) replyProcessIds.take();\n        logger.debug(\"## {} get reply id [{}]\", ClassUtils.getShortClassName(this.getClass()), processId);\n        return processId;\n    }\n\n    protected synchronized void addReply(Long processId) {\n        boolean isSuccessed = replyProcessIds.offer(processId);\n\n        if (isSuccessed) {\n            logger.debug(\"## {} add reply id [{}]\", ClassUtils.getShortClassName(this.getClass()), processId);\n        } else {\n            logger.warn(\"## {} dup reply id [{}]\", ClassUtils.getShortClassName(this.getClass()), processId);\n        }\n    }\n\n    /**\n     * 将当前的符合条件的processIds和当前的reply queue进行校对，剔除不在processIds里的内容\n     */\n    protected synchronized void compareReply(List<Long> processIds) {\n        Object[] replyIds = replyProcessIds.toArray();\n        for (Object replyId : replyIds) {\n            if (processIds.contains((Long) replyId) == false) { // 判断reply id是否在当前processId列表中\n                // 因为存在并发问题，如在执行Listener事件的同时，可能触发了process的创建，这时新建的processId会进入到reply队列中\n                // 此时接受到的processIds变量为上一个版本的内容，所以会删除新建的process，导致整个通道被挂住\n                if (CollectionUtils.isEmpty(processIds) == false) {\n                    Long processId = processIds.get(0);\n                    if (processId > (Long) replyId) { // 如果当前最小的processId都大于replyId, processId都是递增创建的\n                        processTermined((Long) replyId); // 调用一下删除操作\n                    }\n                }\n            }\n        }\n    }\n\n    public void destory() {\n        super.destory();\n        logger.info(\"## destory pipeline[{}] , Listener[{}]\", getPipelineId(),\n                    ClassUtils.getShortClassName(this.getClass()));\n\n        replyProcessIds.clear();\n        stageMonitor.removeListener(this);\n    }\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/setl/zookeeper/monitor/ExtractStageListener.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.setl.zookeeper.monitor;\n\nimport java.util.List;\n\nimport org.I0Itec.zkclient.exception.ZkException;\nimport org.I0Itec.zkclient.exception.ZkNoNodeException;\n\nimport com.alibaba.otter.shared.arbitrate.impl.ArbitrateConstants;\nimport com.alibaba.otter.shared.arbitrate.impl.config.ArbitrateConfigUtils;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.helper.StagePathUtils;\nimport com.alibaba.otter.shared.arbitrate.model.EtlEventData;\nimport com.alibaba.otter.shared.common.utils.JsonUtils;\n\n/**\n * 处理extract模块节点的监控\n * \n * <pre>\n * 监控内容：\n *  1. 某个process的stage节点发生变化后，判断selected节点是否已经准备完成\n * </pre>\n * \n * @author jianghang 2011-9-21 下午02:19:19\n * @version 4.0.0\n */\npublic class ExtractStageListener extends AbstractStageListener implements StageListener {\n\n    private static final String currentNode = ArbitrateConstants.NODE_EXTRACTED;\n    private static final String prevNode    = ArbitrateConstants.NODE_SELECTED;\n\n    public ExtractStageListener(Long pipelineId){\n        super(pipelineId);\n    }\n\n    public void processChanged(List<Long> processIds) {\n        // do nothing\n    }\n\n    public void stageChannged(Long processId, List<String> stageNodes) {\n        try {\n            // 1. 根据pipelineId+processId构造对应的path\n            String path = StagePathUtils.getProcess(getPipelineId(), processId);\n\n            // 2.1 判断是否存在了current节点\n            if (stageNodes.contains(currentNode)) {\n                if (replyProcessIds.remove(processId)) {\n                    if (logger.isDebugEnabled()) {\n                        logger.debug(\"## remove reply id [{}]\", processId);\n                    }\n                }\n\n                return;// 不需要监听了\n            }\n\n            if (replyProcessIds.contains(processId)) {\n                return;// 避免重复处理\n            }\n\n            // 2.2 判断是否存在了prev节点\n            if (stageNodes.contains(prevNode)) {\n                // 2.2.1 获取上一个节点的next node节点信息\n                byte[] data = zookeeper.readData(path + \"/\" + prevNode);\n                EtlEventData eventData = JsonUtils.unmarshalFromByte(data, EtlEventData.class);\n                if (eventData.getNextNid().equals(ArbitrateConfigUtils.getCurrentNid())) {\n                    addReply(processId);// 添加到返回队列,唤醒wait阻塞\n                }\n            }\n        } catch (ZkNoNodeException e) {\n            // 出现节点不存在，说明出现了error情况\n        } catch (ZkException e) {\n            logger.error(\"ExtractStageListener\", e);\n        }\n    }\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/setl/zookeeper/monitor/LoadStageListener.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.setl.zookeeper.monitor;\n\nimport java.util.List;\n\nimport org.I0Itec.zkclient.exception.ZkException;\nimport org.I0Itec.zkclient.exception.ZkNoNodeException;\n\nimport com.alibaba.otter.shared.arbitrate.impl.ArbitrateConstants;\nimport com.alibaba.otter.shared.arbitrate.impl.config.ArbitrateConfigUtils;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.helper.StagePathUtils;\nimport com.alibaba.otter.shared.arbitrate.model.EtlEventData;\nimport com.alibaba.otter.shared.common.utils.JsonUtils;\n\n/**\n * 处理load模块节点的监控\n * \n * <pre>\n * 监控内容：\n *  1. 某个process的stage节点发生变化后，判断transform节点是否已经准备完成，并判断当前是否为最小的process\n *  2. process发生变化后，检测当前的最小processId是否有变化，有变化则触发检查是否可以进行load操作\n * </pre>\n * \n * @author jianghang 2011-9-21 下午02:20:52\n * @version 4.0.0\n */\npublic class LoadStageListener extends AbstractStageListener implements StageListener {\n\n    // private static final String currentNode = ArbitrateConstants.NODE_LOADED;\n    private static final String prevNode = ArbitrateConstants.NODE_TRANSFORMED;\n\n    public LoadStageListener(Long pipelineId){\n        super(pipelineId);\n    }\n\n    public void processChanged(List<Long> processIds) {\n        // nothing\n    }\n\n    public void stageChannged(Long processId, List<String> stageNodes) {\n        try {\n            // 1. 根据pipelineId+processId构造对应的path\n            String path = StagePathUtils.getProcess(getPipelineId(), processId);\n            // 2.1 判断是否存在了error节点,end节点或者current节点\n            // if (stageNodes.contains(currentNode)) {\n            // if (replyProcessIds.remove(processId)) {\n            // if (logger.isDebugEnabled()) {\n            // logger.debug(\"## remove reply id [{}]\", processId);\n            // }\n            // }\n            // return;// 不需要监听了\n            // }\n\n            if (replyProcessIds.contains(processId)) {\n                return;// 避免重复处理\n            }\n\n            // 2.2 判断是否存在了prev节点\n            if (stageNodes.contains(prevNode)) {\n                // 2.2.1 获取上一个节点的next node节点信息\n                byte[] data = zookeeper.readData(path + \"/\" + prevNode);\n                EtlEventData eventData = JsonUtils.unmarshalFromByte(data, EtlEventData.class);\n                if (eventData.getNextNid().equals(ArbitrateConfigUtils.getCurrentNid())) {\n                    List<Long> currentProcessIds = stageMonitor.getCurrentProcessIds(false);\n                    if (currentProcessIds.contains(processId) && currentProcessIds.get(0).equals(processId)) {\n                        // 判断是否是当前最小的processId节点，轮到自己处理了\n                        addReply(processId);// 添加到返回队列,唤醒wait阻塞\n                    }\n\n                }\n            }\n        } catch (ZkNoNodeException e) {\n            // 出现节点不存在，说明出现了error情况\n        } catch (ZkException e) {\n            logger.error(\"LoadStageListener\", e);\n        }\n    }\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/setl/zookeeper/monitor/SelectStageListener.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.setl.zookeeper.monitor;\n\nimport java.util.List;\n\nimport org.I0Itec.zkclient.exception.ZkException;\nimport org.apache.commons.lang.StringUtils;\nimport org.apache.zookeeper.CreateMode;\n\nimport com.alibaba.otter.shared.arbitrate.impl.config.ArbitrateConfigUtils;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.ArbitrateFactory;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.helper.StagePathUtils;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.monitor.MainstemMonitor;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.monitor.PermitMonitor;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.monitor.listener.MainstemListener;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.monitor.listener.PermitListener;\nimport com.alibaba.otter.shared.arbitrate.model.MainStemEventData;\nimport com.alibaba.otter.shared.arbitrate.model.ProcessNodeEventData;\nimport com.alibaba.otter.shared.common.utils.JsonUtils;\n\n/**\n * 处理select模块节点的监控\n * \n * <pre>\n * 监控内容：\n *  1. process节点变化后，判断是否小于并行度，创建新的process节点\n * </pre>\n * \n * @author jianghang 2011-9-21 下午02:17:47\n * @version 4.0.0\n */\npublic class SelectStageListener extends AbstractStageListener implements StageListener, PermitListener, MainstemListener {\n\n    private volatile boolean isPermit = true;\n    private PermitMonitor    permitMonitor;\n    private MainstemMonitor  mainstemMonitor;\n\n    public SelectStageListener(Long pipelineId){\n        super(pipelineId);\n        permitMonitor = ArbitrateFactory.getInstance(pipelineId, PermitMonitor.class);\n        mainstemMonitor = ArbitrateFactory.getInstance(pipelineId, MainstemMonitor.class);\n        permitMonitor.addListener(this);\n        mainstemMonitor.addListener(this);\n\n        recovery(getPipelineId());\n    }\n\n    public void processChanged(List<Long> processIds) {\n        super.processChanged(processIds);\n        // add by ljh at 2012-09-13,解决zookeeper ConnectionLoss问题\n        for (Long processId : processIds) {\n            if (!replyProcessIds.contains(processId)) {\n                logger.warn(\"process is not in order, please check processId:{}\", processId);\n                addReply(processId);\n            }\n        }\n\n        try {\n            String path = StagePathUtils.getProcessRoot(getPipelineId());\n            // 根据并行度创建任务\n            int size = ArbitrateConfigUtils.getParallelism(getPipelineId()) - processIds.size();\n            if (size > 0) {// 创建一个节点\n                PermitMonitor permit = ArbitrateFactory.getInstance(getPipelineId(), PermitMonitor.class);\n                if (permit.isPermit() == false) { // 如果非授权，则不做任何处理\n                    return;\n                }\n\n                String mainStemPath = StagePathUtils.getMainStem(getPipelineId());\n                byte[] bytes = zookeeper.readData(mainStemPath, true);\n                if (bytes == null) {\n                    return;\n                }\n\n                MainStemEventData eventData = JsonUtils.unmarshalFromByte(bytes, MainStemEventData.class);\n                if (eventData.getNid().equals(ArbitrateConfigUtils.getCurrentNid()) == false) {\n                    return;// 如果非自己设置的mainStem,则不做任何处理\n                }\n\n                // 目前select只会在一个节点上部署，只需要单机版锁即可，后续可采用分布式锁进行并发控制\n                // DistributedLock lock = new DistributedLock(PathUtils.getSelectLock(getPipelineId()));\n                // try {\n                // lock.lock();\n                // //创建process\n                // } finally {\n                // lock.unlock();\n                // }\n\n                synchronized (this) {\n                    // 重新再取一次, dobble-check\n                    List<String> currentProcesses = zookeeper.getChildren(path);\n                    size = ArbitrateConfigUtils.getParallelism(getPipelineId()) - currentProcesses.size();\n                    if (size > 0) {// 创建一个节点\n                        ProcessNodeEventData nodeData = new ProcessNodeEventData();\n                        nodeData.setStatus(ProcessNodeEventData.Status.UNUSED);// 标记为未使用\n                        nodeData.setNid(ArbitrateConfigUtils.getCurrentNid());\n                        byte[] nodeBytes = JsonUtils.marshalToByte(nodeData);\n                        String processPath = zookeeper.create(path + \"/\", nodeBytes, CreateMode.PERSISTENT_SEQUENTIAL);\n                        // 创建为顺序的节点\n                        String processNode = StringUtils.substringAfterLast(processPath, \"/\");\n                        Long processId = StagePathUtils.getProcessId(processNode);// 添加到当前的process列表\n                        addReply(processId);\n                    }\n                }\n\n            }\n        } catch (ZkException e) {\n            recovery(getPipelineId());// 出现异常后进行一次recovery，读取一下当前最新值，解决出现ConnectionLoss时create成功问题\n            logger.error(\"SelectStageListener\", e);\n        }\n\n    }\n\n    public void processChanged(boolean isPermit) {\n        if (this.isPermit != isPermit && isPermit == true) { // isPemit从未授权到一个授权的变动\n            stageMonitor.reload(); // 触发一下processChanged，快速的创建process\n        }\n\n        this.isPermit = isPermit;\n    }\n\n    /**\n     * 尝试载入一下上一次未使用的processId，可能发生mainstem切换，新的S模块需要感知前S模块已创建但未使用的process，不然就是一个死锁。而针对已经使用的processId会由e/t/l节点进行处理\n     */\n    private void recovery(Long pipelineId) {\n        List<Long> currentProcessIds = stageMonitor.getCurrentProcessIds(false);\n        for (Long processId : currentProcessIds) {\n            String path = StagePathUtils.getProcess(pipelineId, processId);\n            try {\n                byte[] bytes = zookeeper.readData(path);\n                ProcessNodeEventData nodeData = JsonUtils.unmarshalFromByte(bytes, ProcessNodeEventData.class);\n                if (nodeData.getStatus().isUnUsed()) {// 加入未使用的processId\n                    addReply(processId);\n                }\n            } catch (ZkException e) {\n                logger.error(\"SelectStageListener\", e);\n            }\n        }\n    }\n\n    public void processActiveEnter() {\n        recovery(getPipelineId());\n        stageMonitor.reload(); // 触发一下processChanged\n    }\n\n    public void processActiveExit() {\n        ArbitrateFactory.destory(getPipelineId(), this.getClass());\n    }\n\n    public void destory() {\n        // 取消注册\n        permitMonitor.removeListener(this);\n        mainstemMonitor.removeListener(this);\n        super.destory();\n    }\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/setl/zookeeper/monitor/StageListener.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.setl.zookeeper.monitor;\n\nimport java.util.List;\n\n/**\n * S.E.T.L模块监控的实现\n * \n * <pre>\n * 1. 合并S.E.T.L各类事件的监听，减少和zookeeper的交互\n * 2. 采用观察者事件变化推送的模式\n * </pre>\n * \n * @author jianghang 2011-9-21 上午10:58:20\n * @version 4.0.0\n */\npublic interface StageListener {\n\n    /**\n     * 触发process变化，传递了变化后最新的processIds列表\n     */\n    public void processChanged(List<Long> processIds);\n\n    /**\n     * process节点被删除，触发对应的事件\n     */\n    public void processTermined(Long processId);\n\n    /**\n     * 单个process stage节点发生变化，传递了变化后最新的stage列表\n     */\n    public void stageChannged(Long processId, List<String> stageNodes);\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/setl/zookeeper/monitor/StageMonitor.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.setl.zookeeper.monitor;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.ExecutorService;\n\nimport org.I0Itec.zkclient.IZkChildListener;\nimport org.I0Itec.zkclient.IZkConnection;\nimport org.I0Itec.zkclient.exception.ZkException;\nimport org.I0Itec.zkclient.exception.ZkNoNodeException;\nimport org.apache.commons.lang.ClassUtils;\nimport org.apache.zookeeper.KeeperException;\nimport org.apache.zookeeper.WatchedEvent;\nimport org.apache.zookeeper.ZooKeeper;\nimport org.apache.zookeeper.KeeperException.NoNodeException;\nimport org.apache.zookeeper.Watcher.Event.EventType;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.slf4j.MDC;\n\nimport com.alibaba.otter.shared.arbitrate.impl.ArbitrateConstants;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.ArbitrateLifeCycle;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.helper.StageComparator;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.helper.StagePathUtils;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.monitor.Monitor;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.monitor.MonitorScheduler;\nimport com.alibaba.otter.shared.arbitrate.impl.zookeeper.AsyncWatcher;\nimport com.alibaba.otter.shared.arbitrate.impl.zookeeper.ZooKeeperClient;\nimport com.alibaba.otter.shared.common.utils.zookeeper.ZkClientx;\nimport com.alibaba.otter.shared.common.utils.zookeeper.ZooKeeperx;\n\n/**\n * 所有process节点变化的监控\n * \n * <pre>\n * 监控内容：\n *  1. process列表的删除/新增\n *  2. 每个process下的子节点(setl + end + error)节点的变化信息\n *  \n * 数据获取：\n *  1. 定义{@linkplain StageListener}\n * 接口，并注册。即可监听监控内容的数据变化\n * \n * 注意点：\n *  stageListener返回的processId为历史数据，可能出现以下情况：\n *  1. 历史已就绪的process，因为系统的error错误或者人为的关闭同步队列，会导致当前的processId被删除\n *  2. 此时该processId仍会被返回，需要在各个event中最后进行检查。判断Permit,是否出现error节点,process是否被删除等\n * \n * </pre>\n * \n * @author jianghang 2011-9-21 下午01:16:06\n * @version 4.0.0\n */\npublic class StageMonitor extends ArbitrateLifeCycle implements Monitor {\n\n    private static final Logger              logger            = LoggerFactory.getLogger(StageMonitor.class);\n\n    private ExecutorService                  arbitrateExecutor;\n    private ZkClientx                        zookeeper         = ZooKeeperClient.getInstance();\n    private volatile List<Long>              currentProcessIds = new ArrayList<Long>();                                       // 当前的处于监控中的processId列表\n    private volatile Map<Long, List<String>> currentStages     = new ConcurrentHashMap<Long, List<String>>();                 // 记录下stages信息\n\n    private List<StageListener>              listeners         = Collections.synchronizedList(new ArrayList<StageListener>());\n\n    private IZkChildListener                 processListener;\n\n    public StageMonitor(Long pipelineId){\n        super(pipelineId);\n\n        processListener = new IZkChildListener() {\n\n            public void handleChildChange(String parentPath, List<String> currentChilds) throws Exception {\n                if (currentChilds != null) {\n                    initStage(currentChilds);\n                }\n            }\n        };\n\n        String path = StagePathUtils.getProcessRoot(getPipelineId());\n        List<String> childs = zookeeper.subscribeChildChanges(path, processListener);\n        initStage(childs);\n        // syncStage();\n        MonitorScheduler.register(this);\n    }\n\n    public void destory() {\n        super.destory();\n        if (logger.isDebugEnabled()) {\n            logger.debug(\"## destory Stage pipeline[{}]\", getPipelineId());\n        }\n\n        this.listeners.clear();\n        String path = StagePathUtils.getProcessRoot(getPipelineId());\n        zookeeper.unsubscribeChildChanges(path, processListener);\n        MonitorScheduler.unRegister(this);\n    }\n\n    public void reload() {\n        try {\n            if (logger.isDebugEnabled()) {\n                logger.debug(\"## reload Stage pipeline[{}]\", getPipelineId());\n            }\n\n            initStage();\n            for (Long processId : currentProcessIds) {\n                reload(processId);\n            }\n        } catch (Exception cause) {\n        }\n    }\n\n    /**\n     * 只reload process这一级别的数据\n     */\n    public void reloadWithoutStage() {\n        try {\n            if (logger.isDebugEnabled()) {\n                logger.debug(\"## reload Stage pipeline[{}]\", getPipelineId());\n            }\n\n            initStage();\n        } catch (Exception cause) {\n        }\n    }\n\n    /**\n     * 重新加载下processId信息\n     */\n    public void reload(Long processId) {\n        try {\n            if (logger.isDebugEnabled()) {\n                logger.debug(\"## reload Stage pipeline[{}] process[{}]\", getPipelineId(), processId);\n            }\n\n            initProcessStage(processId);\n        } catch (Exception cause) {\n        }\n    }\n\n    /**\n     * 获取当前的process id列表\n     */\n    public List<Long> getCurrentProcessIds() {\n        return getCurrentProcessIds(false);\n    }\n\n    /**\n     * 获取当前的process id列表，指定是否强制刷新\n     */\n    public List<Long> getCurrentProcessIds(boolean reload) {\n        if (reload) {\n            reloadWithoutStage();\n        }\n\n        return currentProcessIds;\n    }\n\n    /**\n     * 获取当前的process id对应的stage节点信息\n     */\n    public List<String> getCurrentStages(Long processId) {\n        return getCurrentStages(processId, false);\n    }\n\n    /**\n     * 获取当前的process id对应的stage节点信息，指定是否强制刷新\n     */\n    public List<String> getCurrentStages(Long processId, boolean reload) {\n        if (reload) {\n            reload(processId);\n        }\n\n        List<String> stages = currentStages.get(processId);\n        if (stages == null) {\n            stages = new ArrayList<String>();\n        }\n        return stages;\n    }\n\n    /**\n     * 获取zk列表数据，无须同步处理\n     */\n    private void initStage() {\n        // 1. 根据pipelineId构造对应的path\n        String path = StagePathUtils.getProcessRoot(getPipelineId());\n        // 2. 获取当前的所有process列表\n        List<String> currentProcesses = zookeeper.getChildren(path);\n        initStage(currentProcesses);\n    }\n\n    /**\n     * 重新获取最新的process列表\n     */\n    private void initStage(List<String> currentProcesses) {\n        // 3. 循环处理每个process\n        List<Long> processIds = new ArrayList<Long>();\n        for (String process : currentProcesses) {\n            processIds.add(StagePathUtils.getProcessId(process));\n        }\n        Collections.sort(processIds); // 排序一下\n        List<Long> needSyncProcessIds = new ArrayList<Long>();\n\n        synchronized (currentProcessIds) { // 需要同步处理\n            for (Long processId : processIds) {\n                if (!currentProcessIds.contains(processId)) {\n                    needSyncProcessIds.add(processId);\n                }\n            }\n\n            for (Long currentProcessId : currentProcessIds) {\n                if (!processIds.contains(currentProcessId)) {\n                    currentStages.remove(currentProcessId);// 如果process已删除,删除本地的stage信息\n                }\n            }\n\n            if (logger.isDebugEnabled()) {\n                logger.debug(\"pipeline[{}] old processIds{},current processIds{}\", new Object[] { getPipelineId(),\n                        currentProcessIds, processIds });\n            }\n\n            currentProcessIds = processIds; // 切换引用，需设置为volatile保证线程安全&可见性\n        }\n\n        for (Long syncProcessId : needSyncProcessIds) {\n            syncStage(syncProcessId);// 开始同步单个的process\n            // boolean reply = initProcessStage(processId);// 通知下节点变化\n            // if (!currentProcessIds.contains(processId) && reply == false) {\n            // syncStage(processId);// 开始同步单个的process\n            // } else {\n            // // 如果上一次循环中已经添加了监控，则忽略掉本次处理, 避免重复监听\n            // }\n        }\n\n        if (processIds.size() > 0) {// 尝试触发一下第一个process进行load\n            initProcessStage(processIds.get(0));\n        }\n        processChanged(currentProcessIds);// 通知变化\n    }\n\n    private boolean initProcessStage(Long processId) {\n        String path = null;\n        try {\n            // 1. 根据pipelineId+processId构造对应的path\n            path = StagePathUtils.getProcess(getPipelineId(), processId);\n            // 2. 获取当前的所有process列表\n            List<String> currentStages = zookeeper.getChildren(path);\n            return initProcessStage(processId, currentStages);\n        } catch (ZkNoNodeException e) {\n            // ignore，已经被termin处理\n            return false;\n        } catch (ZkException e) {\n            return true;// 走到这一步代表已经出异常了，不需要监听\n        }\n\n    }\n\n    /**\n     * 处理指定的processId变化，返回结果true代表需要继续监听,false代表不需要监视\n     */\n    private boolean initProcessStage(Long processId, List<String> currentStages) {\n        Collections.sort(currentStages, new StageComparator());\n        if (logger.isDebugEnabled()) {\n            logger.debug(\"pipeline[{}] processId[{}] with stage{}\", new Object[] { getPipelineId(), processId,\n                    currentStages });\n        }\n\n        this.currentStages.put(processId, currentStages);// 更新下stage数据\n        stageChanged(processId, currentStages);// 通知stage变化\n        // 2.1 判断是否存在了loaded节点，此节点为最后一个节点\n        if (currentStages.contains(ArbitrateConstants.NODE_TRANSFORMED)) {\n            return true;// 不需要监听了\n        } else {\n            return false;// 其他的状态需要监听\n        }\n    }\n\n    // /**\n    // * 监听process的新增/删除变化，触发process列表的更新\n    // */\n    // private void syncStage() {\n    // // 1. 根据pipelineId构造对应的path\n    // String path = null;\n    // try {\n    // path = StagePathUtils.getProcessRoot(getPipelineId());\n    // // 2. 监听当前的process列表的变化\n    // List<String> currentProcesses = zookeeper.getChildren(path, new AsyncWatcher() {\n    //\n    // public void asyncProcess(WatchedEvent event) {\n    // MDC.put(ArbitrateConstants.splitPipelineLogFileKey, String.valueOf(getPipelineId()));\n    // if (isStop()) {\n    // return;\n    // }\n    //\n    // // 出现session expired/connection losscase下，会触发所有的watcher响应，同时老的watcher会继续保留，所以会导致出现多次watcher响应\n    // boolean dataChanged = event.getType() == EventType.NodeDataChanged\n    // || event.getType() == EventType.NodeDeleted\n    // || event.getType() == EventType.NodeCreated\n    // || event.getType() == EventType.NodeChildrenChanged;\n    // if (dataChanged) {\n    // syncStage(); // 继续监听\n    // // initStage(); // 重新更新\n    // }\n    // }\n    // });\n    //\n    // // 3. 循环处理每个process\n    // List<Long> processIds = new ArrayList<Long>();\n    // for (String process : currentProcesses) {\n    // processIds.add(StagePathUtils.getProcessId(process));\n    // }\n    //\n    // Collections.sort(processIds); // 排序一下\n    // // 判断一下当前processIds和当前内存中的记录是否有差异，如果有差异立马触发一下\n    // if (!currentProcessIds.equals(processIds)) {\n    // initStage(); // 立马触发一下\n    // }\n    // } catch (KeeperException e) {\n    // syncStage(); // 继续监听\n    // } catch (InterruptedException e) {\n    // // ignore\n    // }\n    // }\n\n    /**\n     * 监听指定的processId节点的变化\n     */\n    private void syncStage(final Long processId) {\n        // 1. 根据pipelineId + processId构造对应的path\n        String path = null;\n        try {\n            path = StagePathUtils.getProcess(getPipelineId(), processId);\n            // 2. 监听当前的process列表的变化\n            IZkConnection connection = zookeeper.getConnection();\n            // zkclient包装的是一个持久化的zk，分布式lock只需要一次性的watcher，需要调用原始的zk链接进行操作\n            ZooKeeper orginZk = ((ZooKeeperx) connection).getZookeeper();\n            List<String> currentStages = orginZk.getChildren(path, new AsyncWatcher() {\n\n                public void asyncProcess(WatchedEvent event) {\n                    MDC.put(ArbitrateConstants.splitPipelineLogFileKey, String.valueOf(getPipelineId()));\n                    if (isStop()) {\n                        return;\n                    }\n\n                    if (event.getType() == EventType.NodeDeleted) {\n                        processTermined(processId); // 触发下节点删除\n                        return;\n                    }\n\n                    // 出现session expired/connection losscase下，会触发所有的watcher响应，同时老的watcher会继续保留，所以会导致出现多次watcher响应\n                    boolean dataChanged = event.getType() == EventType.NodeDataChanged\n                                          || event.getType() == EventType.NodeDeleted\n                                          || event.getType() == EventType.NodeCreated\n                                          || event.getType() == EventType.NodeChildrenChanged;\n                    if (dataChanged) {\n                        // boolean reply = initStage(processId);\n                        // if (reply == false) {// 出现过load后就不需要再监听变化，剩下的就是节点的删除操作\n                        syncStage(processId);\n                        // }\n                    }\n                }\n            });\n\n            Collections.sort(currentStages, new StageComparator());\n            List<String> lastStages = this.currentStages.get(processId);\n            if (lastStages == null || !lastStages.equals(currentStages)) {\n                initProcessStage(processId); // 存在差异，立马触发一下\n            }\n\n        } catch (NoNodeException e) {\n            processTermined(processId); // 触发下节点删除\n        } catch (KeeperException e) {\n            syncStage(processId);\n        } catch (InterruptedException e) {\n            // ignore\n        }\n    }\n\n    // ======================== listener处理 ======================\n\n    public void addListener(StageListener listener) {\n        if (logger.isDebugEnabled()) {\n            logger.debug(\"## pipeline[{}] add listener [{}]\", getPipelineId(),\n                         ClassUtils.getShortClassName(listener.getClass()));\n        }\n\n        this.listeners.add(listener);\n    }\n\n    public void removeListener(StageListener listener) {\n        if (logger.isDebugEnabled()) {\n            logger.debug(\"## pipeline[{}] remove listener [{}]\", getPipelineId(),\n                         ClassUtils.getShortClassName(listener.getClass()));\n        }\n\n        this.listeners.remove(listener);\n    }\n\n    private void processChanged(final List<Long> processIds) {\n        for (final StageListener listener : listeners) {\n            // 异步处理\n            arbitrateExecutor.submit(new Runnable() {\n\n                public void run() {\n                    MDC.put(ArbitrateConstants.splitPipelineLogFileKey, String.valueOf(getPipelineId()));\n                    listener.processChanged(processIds);\n                }\n            });\n        }\n    }\n\n    private void stageChanged(final Long processId, final List<String> stages) {\n        for (final StageListener listener : listeners) {\n            // 异步处理\n            arbitrateExecutor.submit(new Runnable() {\n\n                public void run() {\n                    MDC.put(ArbitrateConstants.splitPipelineLogFileKey, String.valueOf(getPipelineId()));\n                    listener.stageChannged(processId, stages);\n                }\n            });\n        }\n    }\n\n    private void processTermined(final Long processId) {\n        for (final StageListener listener : listeners) {\n            // 异步处理\n            arbitrateExecutor.submit(new Runnable() {\n\n                public void run() {\n                    MDC.put(ArbitrateConstants.splitPipelineLogFileKey, String.valueOf(getPipelineId()));\n                    listener.processTermined(processId);\n                }\n            });\n        }\n    }\n\n    // ========== setter =========\n    public void setArbitrateExecutor(ExecutorService arbitrateExecutor) {\n        this.arbitrateExecutor = arbitrateExecutor;\n    }\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/setl/zookeeper/monitor/TransformStageListener.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.setl.zookeeper.monitor;\n\nimport java.util.List;\n\nimport org.I0Itec.zkclient.exception.ZkException;\nimport org.I0Itec.zkclient.exception.ZkNoNodeException;\n\nimport com.alibaba.otter.shared.arbitrate.impl.ArbitrateConstants;\nimport com.alibaba.otter.shared.arbitrate.impl.config.ArbitrateConfigUtils;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.helper.StagePathUtils;\nimport com.alibaba.otter.shared.arbitrate.model.EtlEventData;\nimport com.alibaba.otter.shared.common.utils.JsonUtils;\n\n/**\n * 处理transofrm模块节点的监控\n * \n * <pre>\n * 监控内容：\n *  1. 某个process的stage节点发生变化后，判断extracted节点是否已经准备完成\n * </pre>\n * \n * @author jianghang 2011-9-21 下午02:20:30\n * @version 4.0.0\n */\npublic class TransformStageListener extends AbstractStageListener implements StageListener {\n\n    private static final String currentNode = ArbitrateConstants.NODE_TRANSFORMED;\n    private static final String prevNode    = ArbitrateConstants.NODE_EXTRACTED;\n\n    public TransformStageListener(Long pipelineId){\n        super(pipelineId);\n    }\n\n    public void processChanged(List<Long> processIds) {\n        // do nothing\n    }\n\n    public void stageChannged(Long processId, List<String> stageNodes) {\n        try {\n            // 1. 根据pipelineId+processId构造对应的path\n            String path = StagePathUtils.getProcess(getPipelineId(), processId);\n            // 2.1 判断是否存在了error节点,end节点或者current节点\n            if (stageNodes.contains(currentNode)) {\n                if (replyProcessIds.remove(processId)) {\n                    if (logger.isDebugEnabled()) {\n                        logger.debug(\"## remove reply id [{}]\", processId);\n                    }\n                }\n                return;// 不需要监听了\n            }\n\n            if (replyProcessIds.contains(processId)) {\n                return;// 避免重复处理\n            }\n\n            // 2.2 判断是否存在了prev节点\n            if (stageNodes.contains(prevNode)) {\n                // 2.2.1 获取上一个节点的next node节点信息\n                byte[] data = zookeeper.readData(path + \"/\" + prevNode);\n                EtlEventData eventData = JsonUtils.unmarshalFromByte(data, EtlEventData.class);\n                if (eventData.getNextNid().equals(ArbitrateConfigUtils.getCurrentNid())) {\n                    addReply(processId);// 添加到返回队列,唤醒wait阻塞\n                }\n            }\n        } catch (ZkNoNodeException e) {\n            // 出现节点不存在，说明出现了error情况\n        } catch (ZkException e) {\n            logger.error(\"TransformStageListener\", e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/setl/zookeeper/termin/ErrorTerminProcess.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.setl.zookeeper.termin;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.I0Itec.zkclient.exception.ZkException;\nimport org.apache.zookeeper.KeeperException;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.alibaba.otter.shared.arbitrate.ArbitrateManageService;\nimport com.alibaba.otter.shared.arbitrate.exception.ArbitrateException;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.helper.StagePathUtils;\nimport com.alibaba.otter.shared.arbitrate.impl.zookeeper.ZooKeeperClient;\nimport com.alibaba.otter.shared.arbitrate.impl.zookeeper.lock.DistributedLock;\nimport com.alibaba.otter.shared.arbitrate.model.TerminEventData;\nimport com.alibaba.otter.shared.common.utils.zookeeper.ZkClientx;\n\n/**\n * 抽取异常信号的公共处理，termin的chain处理\n * \n * @author jianghang 2011-9-27 下午04:58:06\n * @version 4.0.0\n */\npublic class ErrorTerminProcess implements TerminProcess {\n\n    protected static final Logger    logger    = LoggerFactory.getLogger(ErrorTerminProcess.class);\n    protected ZkClientx              zookeeper = ZooKeeperClient.getInstance();\n    protected ArbitrateManageService arbitrateManageService;\n    protected NormalTerminProcess    normalTerminProcess;\n\n    public boolean process(TerminEventData data) {\n        DistributedLock lock = new DistributedLock(StagePathUtils.getLoadLock(data.getPipelineId()));\n        try {\n            boolean locked = lock.tryLock();// 尝试进行锁定，等待当前的load操作完成\n            if (!locked) {\n                return false;\n            }\n            processChain(data);\n            return true;\n        } catch (KeeperException e) {\n            throw new ArbitrateException(\"Termin_process\", e);\n        } finally {\n            try {\n                lock.unlock();// 马上进行释放\n            } catch (KeeperException e1) {\n                // ignore\n            }\n        }\n    }\n\n    public void processChain(TerminEventData data) {\n        // 关闭对应的服务\n        Long pipelineId = data.getPipelineId();\n\n        // 清理对应的process\n        String processRoot = StagePathUtils.getProcessRoot(pipelineId);\n        try {\n            List<String> processNodes = zookeeper.getChildren(processRoot);\n            // 3. 循环处理每个process\n            List<Long> processIds = new ArrayList<Long>();\n            for (String process : processNodes) {\n                processIds.add(StagePathUtils.getProcessId(process));\n            }\n            Collections.sort(processIds); // 排序一下\n\n            Long processId = data.getProcessId();\n            if (processId != null) {// 可能为空\n                normalTerminProcess.process(data);\n            }\n\n            for (Long currProcessId : processIds) {\n                if (processId != null && currProcessId <= processId) {\n                    continue;\n                }\n\n                // 发送给最小的一个process的termin信号，进行链式的触发\n                data.setProcessId(currProcessId);\n                processChain(data); // 处理异常信息\n                break;\n            }\n\n        } catch (ZkException e) {\n            throw new ArbitrateException(\"Termin_process\", e);\n        }\n    }\n\n    // =================== setter / getter ====================\n\n    public void setArbitrateManageService(ArbitrateManageService arbitrateManageService) {\n        this.arbitrateManageService = arbitrateManageService;\n    }\n\n    public void setNormalTerminProcess(NormalTerminProcess normalTerminProcess) {\n        this.normalTerminProcess = normalTerminProcess;\n    }\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/setl/zookeeper/termin/NormalTerminProcess.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.setl.zookeeper.termin;\n\nimport java.util.Collections;\nimport java.util.List;\n\nimport org.I0Itec.zkclient.exception.ZkException;\nimport org.I0Itec.zkclient.exception.ZkInterruptedException;\nimport org.I0Itec.zkclient.exception.ZkNoNodeException;\nimport org.I0Itec.zkclient.exception.ZkNodeExistsException;\nimport org.apache.commons.lang.math.RandomUtils;\nimport org.apache.zookeeper.CreateMode;\nimport org.springframework.util.CollectionUtils;\n\nimport com.alibaba.otter.shared.arbitrate.exception.ArbitrateException;\nimport com.alibaba.otter.shared.arbitrate.impl.ArbitrateConstants;\nimport com.alibaba.otter.shared.arbitrate.impl.config.ArbitrateConfigUtils;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.helper.StageComparator;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.helper.StagePathUtils;\nimport com.alibaba.otter.shared.arbitrate.impl.zookeeper.ZooKeeperClient;\nimport com.alibaba.otter.shared.arbitrate.model.ProcessNodeEventData;\nimport com.alibaba.otter.shared.arbitrate.model.TerminEventData;\nimport com.alibaba.otter.shared.common.utils.JsonUtils;\nimport com.alibaba.otter.shared.common.utils.zookeeper.ZkClientx;\n\n/**\n * 正常的结束信号处理\n * \n * <pre>\n * 特殊说明： 告知以后所有维护此代码的人，多看看写的一些注释，这个类并发问题太多了，踩了好多坑.\n * 1. normal和shutdown/rollback操作存在并发性，shutdown/rollback有互斥锁，所以不会有并发。 (normal加互斥锁代价高，不合算)\n * 2. 执行shutdown/rollback操作时，虽然是先修改channel状态，但是normal可能还会多跑一个stage，这时执行process删除时，normal又会创建一个stage，导致删除失败，需要重试\n * 3. 需要先创建termin，再删除process (以前遇到并发，删除了一个process，在创建termin的过程，另一个process流程提前完成了这一系列动作，导致termin顺序不对)\n * 4. normal和shutdown/rollback的并发操作，会导致重复创建termin. (两个操作都发现要创建termin，一个先完成create，并且被消费者消费了termin并删除了，另一个再create时也能成功了，导致出现重复) \n * \n * 大致想到了这么几点，没事就别改这个类了，啥并发问题都有，(建议多用用新版的canal，对termin信号顺序/无重复没有这么强要求，客户端多retry几次有点重复数据，事情也就不会那么复杂) \n * write at 2012-09-06 by jianghang.loujh\n * </pre>\n * \n * @author jianghang 2011-9-26 下午01:52:53\n * @version 4.0.0\n */\npublic class NormalTerminProcess implements TerminProcess {\n\n    private ZkClientx zookeeper = ZooKeeperClient.getInstance();\n\n    public boolean process(TerminEventData data) {\n        return doProcess(data, false);\n    }\n\n    private boolean doProcess(TerminEventData data, boolean retry) {\n        Long pipelineId = data.getPipelineId();\n        Long processId = data.getProcessId();\n\n        List<String> currentStages = null;\n        try {\n            currentStages = zookeeper.getChildren(StagePathUtils.getProcess(pipelineId, processId));\n            Collections.sort(currentStages, new StageComparator());\n        } catch (ZkNoNodeException e) {\n            // ignore，说明节点已经被删除了\n            return false;\n        } catch (ZkException e) {\n            throw new ArbitrateException(\"Termin_process\", e);\n        }\n\n        // 按顺序删除对应的S.E.T.L节点\n        // s节点\n        if (currentStages == null || currentStages.contains(ArbitrateConstants.NODE_SELECTED)) {\n            try {\n                boolean successed = zookeeper.delete(StagePathUtils.getSelectStage(pipelineId, processId));\n                if (!successed) {\n                    processDeleteFailed();\n                }\n            } catch (ZkException e) {\n                throw new ArbitrateException(\"Termin_process\", e);\n            }\n        }\n\n        // e节点\n        if (currentStages == null || currentStages.contains(ArbitrateConstants.NODE_EXTRACTED)) {\n            try {\n                boolean successed = zookeeper.delete(StagePathUtils.getExtractStage(pipelineId, processId));\n                if (!successed) {\n                    processDeleteFailed();\n                }\n            } catch (ZkException e) {\n                throw new ArbitrateException(\"Termin_process\", e);\n            }\n        }\n\n        // t节点\n        if (currentStages == null || currentStages.contains(ArbitrateConstants.NODE_TRANSFORMED)) {\n            try {\n                boolean successed = zookeeper.delete(StagePathUtils.getTransformStage(pipelineId, processId));\n                if (!successed) {\n                    processDeleteFailed();\n                }\n            } catch (ZkException e) {\n                throw new ArbitrateException(\"Termin_process\", e);\n            }\n        }\n        // l节点\n        // try {\n        // zookeeper.delete(StagePathUtils.getLoadStage(pipelineId, processId), -1, new VoidCallback() {\n        //\n        // public void processResult(int rc, String path, Object ctx) {\n        // logger.debug(\"delete {} successful. \", path);\n        // }\n        // }, null);\n        // } catch (NoNodeException e) {\n        // // ignore,说明节点已经被删除\n        // } catch (KeeperException e) {\n        // throw new ArbitrateException(\"Termin_process\", e);\n        // } catch (InterruptedException e) {\n        // // ignore\n        // }\n\n        // 针对transform删除成功，s/e有一个删除失败，这说明normal和rollback/shutdown一定有并发\n        // 不过会有遗漏判断，比如并发时都是一个线程全删除成功\n        return processDelete(data, CollectionUtils.isEmpty(currentStages), retry);\n    }\n\n    private boolean processDelete(TerminEventData data, boolean noStage, boolean retry) {\n        Long pipelineId = data.getPipelineId();\n        Long processId = data.getProcessId();\n\n        boolean result = false;\n        // process节点\n        // 最后删除一下process节点\n        String path = StagePathUtils.getProcess(pipelineId, processId);\n        byte[] bytes = null;\n        try {\n            bytes = zookeeper.readData(path);\n        } catch (ZkNoNodeException e) {\n            return false;// 说明节点已经被删除了，直接忽略\n        }\n\n        ProcessNodeEventData nodeData = JsonUtils.unmarshalFromByte(bytes, ProcessNodeEventData.class);\n        if (nodeData.getStatus().isUsed()) {// 如果已使用在标记为true，需要创建termin节点\n            // 只删除已经被使用了的节点\n\n            if (noStage && nodeData.getMode().isZookeeper()) {// 针对rpc mode就是没有stage，不需要进行sleep\n                // 处理一种case:\n                // 针对两个并发操作，一个已经完成了s/e/t/l模块的所有delete，另一个刚好进来发现没有可delete的\n                // 这时两个线程就一起进入createTermin操作，存在一些并发问题，针对这种case，需要错开一下\n                // 不过这种情况可能会有误判，针对s模块没有处理完成，发起了一次rollback/shutdown操作就会碰上，概率比较小，忽略这种误判吧\n                processDeleteFailed();\n                return processDelete(data, false, retry);// 再重新尝试访问一下process，看下是否已经被删除了\n            }\n\n            // at 2012-09-06，备注一下一个问题排查的结果\n            // deleteFailed值判断存在问题，针对非正常结束的process，可能就是没有s/e/t/l节点，就会出现一次sleep+retry操作\n            // 在这段sleep的过程中，process可能还会跑一段，产生新的s/e/t节点，导致process删除失败，从而重复执行了createTermin\n            if (!retry) {\n                // modify at 2012-08-14 , 遇到一个并发bug\n                // 1. 两个process a和b，a先执行完毕删除了process节点，b立马得到触发并在极端的时间内处理完成\n                // 2. 最后的一个结果b创建的termin要早于a创建的termin，导致termin发送顺序不对\n                // 这里修改为，先创建termin节点，再删除对应的process，触发下一个process，保证termin创建为顺序\n                // 同样可以避免删除了process后，termin信号创建失败的问题\n\n                // modify at 2012-09-06 , 遇到一个并发bug\n                // 一个process只完成了s/e模块，然后进行shutdown操作，完成了termin节点创建，但在process delete时，老的process创建了t节点\n                // 这时会出现process删除失败，从而触发进行一次retry操作，此时retry又会再一次创建了termin信号，导致调度出错\n                // 所以这里做了一个控制，只有针对非retry模式下才会创建termin信号\n                result = createTermin(data, pipelineId, processId);// 创建termin节点\n            }\n        }\n\n        try {\n            // 修改为false，已经有另一个线程添加了该节点\n            result = zookeeper.deleteRecursive(StagePathUtils.getProcess(pipelineId, processId));\n            if (!result) {\n                doProcess(data, true);// 做一次重试，可能做manager关闭的时侯，node节点还跑了一段，导致stage节点又创建了一个\n            }\n        } catch (ZkInterruptedException e) {\n            throw e;\n        } catch (ZkException e) {\n            doProcess(data, true);// 做一次重试，可能做manager关闭的时侯，node节点还跑了一段，导致stage节点又创建了一个\n        }\n\n        return result;\n    }\n\n    private boolean createTermin(TerminEventData data, Long pipelineId, Long processId) {\n        // 1. 创建end节点\n        String path = StagePathUtils.getTermin(pipelineId, processId);\n        data.setCurrNid(ArbitrateConfigUtils.getCurrentNid());\n        // 序列化\n        byte[] bytes = JsonUtils.marshalToByte(data);\n        try {\n            zookeeper.create(path, bytes, CreateMode.PERSISTENT);\n        } catch (ZkNodeExistsException e) {\n            // ignore\n            return false;\n        } catch (ZkException e) {\n            throw new ArbitrateException(\"Termin_single\", e);\n        }\n\n        return true;\n    }\n\n    private void processDeleteFailed() {\n        try {\n            // 出现并发删除，其中某个人需要放慢一下步骤，等另一个人完成\n            Thread.sleep(500 + RandomUtils.nextInt(500));\n        } catch (InterruptedException e) {\n            // ignore\n        }\n    }\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/setl/zookeeper/termin/TerminProcess.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.setl.zookeeper.termin;\n\nimport com.alibaba.otter.shared.arbitrate.model.TerminEventData;\n\n/**\n * 终结信号处理的接口\n * \n * @author jianghang 2011-9-26 下午01:37:04\n * @version 4.0.0\n */\npublic interface TerminProcess {\n\n    public boolean process(TerminEventData data);\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/setl/zookeeper/termin/WarningTerminProcess.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.setl.zookeeper.termin;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.alibaba.otter.shared.arbitrate.impl.alarm.AlarmClientService;\nimport com.alibaba.otter.shared.arbitrate.impl.config.ArbitrateConfigUtils;\nimport com.alibaba.otter.shared.arbitrate.model.TerminEventData;\n\n/**\n * 回滚的终结信号处理\n * \n * @author jianghang 2011-9-26 下午02:03:02\n * @version 4.0.0\n */\npublic class WarningTerminProcess implements TerminProcess {\n\n    private static final Logger logger = LoggerFactory.getLogger(WarningTerminProcess.class);\n    private AlarmClientService  alarmClientService;\n\n    public boolean process(TerminEventData data) {\n        logger.warn(\"nid:{}[{}:{}]\",\n                    new Object[] { ArbitrateConfigUtils.getCurrentNid(), data.getPipelineId(),\n                            data.getCode() + \":\" + data.getDesc() });\n        alarmClientService.sendAlarm(ArbitrateConfigUtils.getCurrentNid(), data.getPipelineId(), data.getCode(),\n                                     data.getDesc());\n        return true;\n    }\n\n    // ============= setter / getter =============\n\n    public void setAlarmClientService(AlarmClientService alarmClientService) {\n        this.alarmClientService = alarmClientService;\n    }\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/zookeeper/AsyncWatcher.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.zookeeper;\n\nimport java.util.concurrent.ArrayBlockingQueue;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.ThreadPoolExecutor;\nimport java.util.concurrent.TimeUnit;\n\nimport org.apache.zookeeper.WatchedEvent;\nimport org.apache.zookeeper.Watcher;\n\nimport com.alibaba.otter.shared.common.utils.thread.NamedThreadFactory;\n\n/**\n * 包装ZooKeeper的Watcher接口，支持Async的异步调用处理\n * \n * <pre>\n * 说明：\n *  1. zookeeper针对watcher的调用是以单线程串行的方式进行处理，容易造成堵塞影响，monitor的数据同步及时性\n *  2. AsyncWatcher为采取的一种策略为当不超过acceptCount=60的任务时，会采用异步线程的方式处理。如果超过60任务，会变为原先的单线程串行的模式\n * </pre>\n * \n * @author jianghang 2011-9-21 下午01:00:39\n * @version 4.0.0\n */\npublic abstract class AsyncWatcher implements Watcher {\n\n    private static final int       DEFAULT_POOL_SIZE    = 30;\n    private static final int       DEFAULT_ACCEPT_COUNT = 60;\n\n    private static ExecutorService executor             = new ThreadPoolExecutor(\n                                                                                 DEFAULT_POOL_SIZE,\n                                                                                 DEFAULT_POOL_SIZE,\n                                                                                 0L,\n                                                                                 TimeUnit.MILLISECONDS,\n                                                                                 new ArrayBlockingQueue(\n                                                                                                        DEFAULT_ACCEPT_COUNT),\n                                                                                 new NamedThreadFactory(\n                                                                                                        \"Arbitrate-Async-Watcher\"),\n                                                                                 new ThreadPoolExecutor.CallerRunsPolicy());\n\n    public void process(final WatchedEvent event) {\n        executor.execute(new Runnable() {// 提交异步处理\n\n            public void run() {\n                asyncProcess(event);\n            }\n        });\n\n    }\n\n    public abstract void asyncProcess(WatchedEvent event);\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/zookeeper/SessionExpiredNotification.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.zookeeper;\n\n/**\n * zookeeper 出现session expired异常后的通知，允许业务实现自处理，比如重建ephemeral对象\n * \n * @author jianghang 2012-1-13 上午10:46:34\n * @version 4.0.0\n */\npublic interface SessionExpiredNotification {\n\n    public void notification();\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/zookeeper/ZooKeeperClient.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.zookeeper;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.I0Itec.zkclient.IZkStateListener;\nimport org.apache.commons.lang.StringUtils;\nimport org.apache.zookeeper.Watcher.Event.KeeperState;\n\nimport com.alibaba.otter.shared.arbitrate.impl.config.ArbitrateConfigUtils;\nimport com.alibaba.otter.shared.common.utils.zookeeper.ZkClientx;\nimport com.google.common.base.Function;\nimport com.google.common.collect.OtterMigrateMap;\n\n/**\n * 封装了ZooKeeper，使其支持节点的优先顺序，比如美国机房的节点会优先加载美国对应的zk集群列表，都失败后才会选择加载杭州的zk集群列表\n * \n * @author jianghang 2011-9-8 下午07:55:44\n * @version 4.0.0\n */\npublic class ZooKeeperClient {\n\n    private static String               cluster;\n    private static int                  sessionTimeout = 10 * 1000;\n    private static Map<Long, ZkClientx> clients        = OtterMigrateMap.makeComputingMap(new Function<Long, ZkClientx>() {\n\n                                                           public ZkClientx apply(Long pipelineId) {\n                                                               return createClient();\n                                                           }\n                                                       });\n    private static Long                 defaultId      = 0L;\n\n    /**\n     * 获取对应的zookeeper客户端\n     */\n    public static ZkClientx getInstance() {\n        return getInstance(defaultId);\n    }\n\n    /**\n     * 根据pipelineId获取对应的zookeeper客户端，每个pipelineId可以独立一个zookeeper链接，保证性能\n     */\n    public static ZkClientx getInstance(Long pipelineId) {\n        return clients.get(pipelineId);\n    }\n\n    public static void destory() {\n        for (ZkClientx zkClient : clients.values()) {\n            zkClient.close();\n        }\n    }\n\n    public static void registerNotification(final SessionExpiredNotification notification) {\n        getInstance().subscribeStateChanges(new IZkStateListener() {\n\n            public void handleStateChanged(KeeperState state) throws Exception {\n\n            }\n\n            public void handleNewSession() throws Exception {\n                notification.notification();\n            }\n\n            @Override\n            public void handleSessionEstablishmentError(Throwable error) throws Exception {\n            }\n        });\n\n    }\n\n    private static ZkClientx createClient() {\n        List<String> serveraddrs = getServerAddrs();\n        return new ZkClientx(StringUtils.join(serveraddrs, \",\"), sessionTimeout);\n    }\n\n    /**\n     * 从当前的node信息中获取对应的zk集群信息\n     */\n    private static List<String> getServerAddrs() {\n        List<String> result = ArbitrateConfigUtils.getServerAddrs();\n        if (result == null || result.size() == 0) {\n            result = Arrays.asList(cluster);\n        }\n        return result;\n    }\n\n    public void setCluster(String cluster) {\n        ZooKeeperClient.cluster = cluster;\n    }\n\n    public void setSessionTimeout(int timeout) {\n        ZooKeeperClient.sessionTimeout = timeout;\n    }\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/zookeeper/ZooKeeperHeartBeatWorker.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.zookeeper;\n\nimport java.util.concurrent.ScheduledThreadPoolExecutor;\nimport java.util.concurrent.ThreadPoolExecutor;\nimport java.util.concurrent.TimeUnit;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.DisposableBean;\nimport org.springframework.beans.factory.InitializingBean;\n\nimport com.alibaba.otter.shared.arbitrate.impl.manage.NodeArbitrateEvent;\nimport com.alibaba.otter.shared.common.utils.thread.NamedThreadFactory;\n\n/**\n * zookeeper的心跳机制，保证和zookeeper的连接有请求\n * \n * <pre>\n * 1. 尽量避免SessionExpired异常\n * 2. 出现SessionExpired/ConnectLoss异常时，能触发重连和链接恢复\n * \n * <pre>\n * @author jianghang 2012-8-28 下午10:30:33\n * @version 4.1.0\n */\npublic class ZooKeeperHeartBeatWorker implements InitializingBean, DisposableBean {\n\n    private static final Logger         logger    = LoggerFactory.getLogger(ZooKeeperHeartBeatWorker.class);\n    private int                         period    = 3000;\n    private ScheduledThreadPoolExecutor scheduler = new ScheduledThreadPoolExecutor(\n                                                                                    1,\n                                                                                    new NamedThreadFactory(\n                                                                                                           \"Otter-zookeeper-heartbeat\"),\n                                                                                    new ThreadPoolExecutor.CallerRunsPolicy());\n    private NodeArbitrateEvent          nodeEvent;\n\n    public void start() {\n        scheduler.scheduleAtFixedRate(new Runnable() {\n\n            public void run() {\n                try {\n                    nodeEvent.liveNodes();\n                } catch (Throwable e) {\n                    logger.warn(\"zookeeper heartbeat has failed.\", e);\n                }\n            }\n        }, period, period, TimeUnit.MILLISECONDS);\n\n    }\n\n    public void afterPropertiesSet() throws Exception {\n        start();\n    }\n\n    public void destroy() throws Exception {\n        scheduler.shutdownNow();\n    }\n\n    public void setPeriod(int period) {\n        this.period = period;\n    }\n\n    public void setNodeEvent(NodeArbitrateEvent nodeEvent) {\n        this.nodeEvent = nodeEvent;\n    }\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/zookeeper/ZooKeeperOperation.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.zookeeper;\n\nimport org.apache.zookeeper.KeeperException;\n\n/**\n * {@linkplain ZkClientx}的callback接口\n */\npublic interface ZooKeeperOperation<T> {\n\n    public T execute() throws KeeperException, InterruptedException;\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/zookeeper/ZooKeeperx.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.zookeeper;\n\nimport java.util.List;\nimport java.util.concurrent.atomic.AtomicBoolean;\nimport java.util.concurrent.atomic.AtomicInteger;\n\nimport org.apache.zookeeper.CreateMode;\nimport org.apache.zookeeper.KeeperException;\nimport org.apache.zookeeper.Watcher;\nimport org.apache.zookeeper.ZooDefs;\nimport org.apache.zookeeper.ZooKeeper;\nimport org.apache.zookeeper.AsyncCallback.VoidCallback;\nimport org.apache.zookeeper.data.ACL;\nimport org.apache.zookeeper.data.Stat;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.alibaba.otter.shared.arbitrate.exception.ArbitrateException;\n\n/**\n * 扩展ZooKeeper，主要是统一处理下链接异常和ACL管理 <br/>\n * <p>\n * document : <br/>\n * <a href=\"http://wiki.apache.org/hadoop/ZooKeeper/FAQ#A3\">http://wiki.apache. org/hadoop/ZooKeeper/FAQ#A3</a> <br/>\n * <a href=\"http://wiki.apache.org/hadoop/ZooKeeper/ErrorHandling\"\n * >http://wiki.apache.org/hadoop/ZooKeeper/ErrorHandling</a>\n * \n * @author jianghang 2011-9-23 下午01:38:06\n * @version 4.0.0\n */\n\n@Deprecated\npublic class ZooKeeperx {\n\n    private static final Logger  logger     = LoggerFactory.getLogger(ZooKeeperx.class);\n    // private static final Field zooKeeperField = ReflectionUtils.findField(ZooKeeperClient.class, \"zookeeper\");\n    private static final Integer maxRetry   = 3;                                        // 出现exception，最多重试3次\n\n    private long                 retryDelay = 3000L;\n    private List<ACL>            acl        = ZooDefs.Ids.OPEN_ACL_UNSAFE;\n    private ZooKeeper            zookeeper;\n    private AtomicInteger        cversion   = new AtomicInteger(0);\n    private AtomicBoolean        running    = new AtomicBoolean(true);\n\n    public ZooKeeperx(ZooKeeper zookeeper){\n        this.zookeeper = zookeeper;\n    }\n\n    /**\n     * add by ljh at 2012-09-13\n     * \n     * <pre>\n     * 1. 使用zookeeper过程，针对出现ConnectionLoss异常，比如进行create/setData/delete，操作可能已经在zookeeper server上进行应用\n     * 2. 针对SelectStageListener进行processId创建时，会以最后一次创建的processId做为调度id. 如果进行retry，之前成功的processId就会被遗漏了\n     * </pre>\n     * \n     * @see org.apache.zookeeper.ZooKeeper#create(String path, byte[] path, List acl, CreateMode mode)\n     */\n    public String createNoRetry(final String path, final byte[] data, final CreateMode mode) throws KeeperException,\n                                                                                            InterruptedException {\n        return zookeeper.create(path, data, acl, mode);\n    }\n\n    /**\n     * <pre>\n     * 1. 使用zookeeper过程，针对出现ConnectionLoss异常，比如进行create/setData/delete，操作可能已经在zookeeper server上进行应用\n     * 2. 针对SelectStageListener进行processId创建时，会以最后一次创建的processId做为调度id. 如果进行retry，之前成功的processId就会被遗漏了\n     * </pre>\n     * \n     * @see org.apache.zookeeper.ZooKeeper#create(String path, byte[] path, List acl, CreateMode mode)\n     */\n    public String create(final String path, final byte[] data, final CreateMode mode) throws KeeperException,\n                                                                                     InterruptedException {\n        if (mode.isSequential()) {\n            return zookeeper.create(path, data, acl, mode);\n        } else {\n            return retryOperation(new ZooKeeperOperation<String>() {\n\n                public String execute() throws KeeperException, InterruptedException {\n                    return zookeeper.create(path, data, acl, mode);\n                }\n            });\n        }\n\n    }\n\n    /**\n     * @see org.apache.zookeeper.ZooKeeper#delete(String path, int version)\n     */\n    public void delete(final String path, final int version) throws InterruptedException, KeeperException {\n        retryOperation(new ZooKeeperOperation() {\n\n            public Object execute() throws KeeperException, InterruptedException {\n                zookeeper.delete(path, version);\n                return null;\n            }\n        });\n    }\n\n    /**\n     * @see org.apache.zookeeper.ZooKeeper#delete(String path, int version , VoidCallback cb , Object ctx)\n     */\n    public void delete(final String path, final int version, final VoidCallback cb, final Object ctx)\n                                                                                                     throws InterruptedException,\n                                                                                                     KeeperException {\n        retryOperation(new ZooKeeperOperation() {\n\n            public Object execute() throws KeeperException, InterruptedException {\n                zookeeper.delete(path, version, cb, ctx);\n                return null;\n            }\n        });\n    }\n\n    /**\n     * @see org.apache.zookeeper.ZooKeeper#exists(String path, boolean watch)\n     */\n    public Stat exists(final String path, final boolean watch) throws KeeperException, InterruptedException {\n        return retryOperation(new ZooKeeperOperation<Stat>() {\n\n            public Stat execute() throws KeeperException, InterruptedException {\n                return zookeeper.exists(path, watch);\n            }\n        });\n\n    }\n\n    /**\n     * @see org.apache.zookeeper.ZooKeeper#exists(String path, Watcher watcher)\n     */\n    public Stat exists(final String path, final Watcher watcher) throws KeeperException, InterruptedException {\n        return retryOperation(new ZooKeeperOperation<Stat>() {\n\n            public Stat execute() throws KeeperException, InterruptedException {\n                return zookeeper.exists(path, watcher);\n            }\n        });\n    }\n\n    /**\n     * @see org.apache.zookeeper.ZooKeeper#getChildren(String path, boolean watch)\n     */\n    public List<String> getChildren(final String path, final boolean watch) throws KeeperException,\n                                                                           InterruptedException {\n        return retryOperation(new ZooKeeperOperation<List<String>>() {\n\n            public List<String> execute() throws KeeperException, InterruptedException {\n                return zookeeper.getChildren(path, watch);\n            }\n        });\n\n    }\n\n    /**\n     * @see org.apache.zookeeper.ZooKeeper#getChildren(String path, boolean watch , Stat stat)\n     */\n    public List<String> getChildren(final String path, final boolean watch, final Stat stat) throws KeeperException,\n                                                                                            InterruptedException {\n        return retryOperation(new ZooKeeperOperation<List<String>>() {\n\n            public List<String> execute() throws KeeperException, InterruptedException {\n                return zookeeper.getChildren(path, watch, stat);\n            }\n        });\n\n    }\n\n    /**\n     * @see org.apache.zookeeper.ZooKeeper#getChildren(String path, Watcher watcher)\n     */\n    public List<String> getChildren(final String path, final Watcher watcher) throws KeeperException,\n                                                                             InterruptedException {\n        return retryOperation(new ZooKeeperOperation<List<String>>() {\n\n            public List<String> execute() throws KeeperException, InterruptedException {\n                return zookeeper.getChildren(path, watcher);\n            }\n        });\n    }\n\n    /**\n     * @see org.apache.zookeeper.ZooKeeper#getData(String path, boolean watch, Stat stat)\n     */\n    public byte[] getData(final String path, final boolean watch, final Stat stat) throws KeeperException,\n                                                                                  InterruptedException {\n        return retryOperation(new ZooKeeperOperation<byte[]>() {\n\n            public byte[] execute() throws KeeperException, InterruptedException {\n                return zookeeper.getData(path, watch, stat);\n            }\n        });\n\n    }\n\n    /**\n     * @see org.apache.zookeeper.ZooKeeper#getData(String path, Watcher watcher, Stat stat)\n     */\n    public byte[] getData(final String path, final Watcher watcher, final Stat stat) throws KeeperException,\n                                                                                    InterruptedException {\n        return retryOperation(new ZooKeeperOperation<byte[]>() {\n\n            public byte[] execute() throws KeeperException, InterruptedException {\n                return zookeeper.getData(path, watcher, stat);\n            }\n        });\n    }\n\n    /**\n     * @see org.apache.zookeeper.ZooKeeper#setData(String path, byte[] data, int version)\n     */\n    public Stat setData(final String path, final byte[] data, final int version) throws KeeperException,\n                                                                                InterruptedException {\n        return retryOperation(new ZooKeeperOperation<Stat>() {\n\n            public Stat execute() throws KeeperException, InterruptedException {\n                return zookeeper.setData(path, data, version);\n            }\n        });\n    }\n\n    // =========================== helper method =============================\n\n    /**\n     * 包装重试策略\n     */\n    public <T> T retryOperation(ZooKeeperOperation<T> operation) throws KeeperException, InterruptedException {\n        if (!running.get()) {\n            throw new ArbitrateException(\"Zookeeper is destory ,should never be used ....\");\n        }\n\n        KeeperException exception = null;\n        for (int i = 0; i < maxRetry; i++) {\n            int version = cversion.get(); // 获取版本\n            int retryCount = i + 1;\n            try {\n                if (!zookeeper.getState().isAlive()) {\n                    retryDelay(retryCount);\n                    cleanup(version);\n                } else {\n                    return (T) operation.execute();\n                }\n            } catch (KeeperException.SessionExpiredException e) {\n                logger.warn(\"Session expired for: \" + this + \" so reconnecting \" + (i + 1) + \" times due to: \" + e, e);\n                retryDelay(retryCount);\n                cleanup(version);\n            } catch (KeeperException.ConnectionLossException e) { // 特殊处理Connection Loss\n                if (exception == null) {\n                    exception = e;\n                }\n                logger.warn(\"Attempt \" + retryCount + \" failed with connection loss so \" + \"attempting to reconnect: \"\n                            + e, e);\n                retryDelay(retryCount);\n            }\n        }\n\n        throw exception;\n    }\n\n    private void retryDelay(int attemptCount) {\n        if (attemptCount > 0) {\n            try {\n                Thread.sleep(attemptCount * retryDelay);\n            } catch (InterruptedException e) {\n                logger.warn(\"Failed to sleep: \" + e, e);\n            }\n        }\n    }\n\n    public ZooKeeper getDelegate() {\n        return this.zookeeper;\n    }\n\n    private synchronized void cleanup(int version) { // 加锁操作，阻塞等待上一次的重建完成\n        // if (cversion.compareAndSet(version, version + 1)) {// 看一下version是否有变化，没变化就我来更新\n        // // ReflectionUtils.makeAccessible(zooKeeperField);\n        // // ReflectionUtils.setField(zooKeeperField, new ZooKeeperClient(), null);// 清空\n        // ZooKeeper oldZookeepre = zookeeper;\n        // zookeeper = ZooKeeperClient.createZookeeper();\n        //\n        // try {\n        // oldZookeepre.close(); // 关闭老链接\n        // } catch (InterruptedException e) {\n        // // ignore\n        // }\n        // for (SessionExpiredNotification notification : ZooKeeperClient.getNotifications()) {\n        // notification.notification(this);\n        // }\n        //\n        // cversion.incrementAndGet();// 更新一下版本号\n        // } else {\n        // // 更新失败，忽略。说明已经有别的线程重建过zookeeper，再做一次尝试\n        // }\n    }\n\n    public void destory() {\n        if (running.compareAndSet(true, false)) {\n            try {\n                zookeeper.close();\n            } catch (InterruptedException e) {\n                // ignore\n            }\n        }\n    }\n\n    // ===================== setter / getter =================\n\n    public List<ACL> getAcl() {\n        return acl;\n    }\n\n    public void setAcl(List<ACL> acl) {\n        this.acl = acl;\n    }\n\n    public long getRetryDelay() {\n        return retryDelay;\n    }\n\n    public void setRetryDelay(long retryDelay) {\n        this.retryDelay = retryDelay;\n    }\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/zookeeper/lock/DistributedLock.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.zookeeper.lock;\n\nimport java.util.List;\nimport java.util.SortedSet;\nimport java.util.TreeSet;\nimport java.util.concurrent.locks.ReentrantLock;\n\nimport org.I0Itec.zkclient.IZkConnection;\nimport org.I0Itec.zkclient.exception.ZkException;\nimport org.I0Itec.zkclient.exception.ZkInterruptedException;\nimport org.apache.commons.lang.StringUtils;\nimport org.apache.commons.lang.exception.NestableRuntimeException;\nimport org.apache.zookeeper.CreateMode;\nimport org.apache.zookeeper.KeeperException;\nimport org.apache.zookeeper.WatchedEvent;\nimport org.apache.zookeeper.ZooKeeper;\nimport org.apache.zookeeper.data.Stat;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.alibaba.otter.shared.arbitrate.impl.zookeeper.AsyncWatcher;\nimport com.alibaba.otter.shared.arbitrate.impl.zookeeper.ZooKeeperClient;\nimport com.alibaba.otter.shared.common.utils.lock.BooleanMutex;\nimport com.alibaba.otter.shared.common.utils.zookeeper.ZkClientx;\nimport com.alibaba.otter.shared.common.utils.zookeeper.ZooKeeperx;\n\n/**\n * 实现一个基于zookeeper的分布式锁 <br/>\n * document : <br/>\n * <a href=\"http://zookeeper.apache.org/doc/trunk/recipes.html\">http://zookeeper.apache.org/doc/trunk/recipes.html</a>\n * \n * <pre>\n * 使用注意：\n *  传统的{@linkplain ReentrantLock}使用有所区别，ReentrantLock主要用于空只单进程多线程之间的调度，所以要求每个线程使用同一个ReentrantLock实例\n *  而{@linkplain DistributedLock}主要是用于控制多进程的调度，所以如果需要被用来控制多线程时，需要使用不同的DistributedLock实例对象。\n *  <strong>因此单个DistributedLock实例在多个线程中进行lock/unlock操作时会有线程安全问题!!</strong>\n *  \n * 使用例子：\n * <code>\n *         DistributedLock lock = new DistributedLock(\"/lock/\");\n *         try {\n *             lock.lock();\n *             // do something\n *         } catch (InterruptedException e1) {\n *             // 可中断\n *         } catch (KeeperException e1) {\n *             // zookeeper异常\n *         } finally {\n *             try {\n *                 lock.unlock();\n *             } catch (KeeperException e) {\n *                 // zookeeper异常\n *             }\n *         }\n * </code>\n * </pre>\n * \n * @author jianghang 2011-9-29 上午11:16:07\n * @version 4.0.0\n */\npublic class DistributedLock {\n\n    private static final Logger  logger    = LoggerFactory.getLogger(DistributedLock.class);\n    private static final byte[]  data      = { 0x12, 0x34 };\n    // private static final Long DEFAULT_TIMEOUT_PERIOD = 60 * 1000L;\n    private ZkClientx            zookeeper = ZooKeeperClient.getInstance();\n    private final String         root;                                                      // 根节点路径\n    private String               id;\n    private LockNode             idName;\n    private String               ownerId;\n    private String               lastChildId;\n    private Throwable            other     = null;\n    private KeeperException      exception = null;\n    private InterruptedException interrupt = null;\n\n    public DistributedLock(String root){\n        this.root = root;\n        ensureExists(root);\n    }\n\n    /**\n     * 尝试获取锁操作，阻塞式可被中断\n     */\n    public void lock() throws InterruptedException, KeeperException {\n        // 可能初始化的时候就失败了\n        if (exception != null) {\n            throw exception;\n        }\n\n        if (interrupt != null) {\n            throw interrupt;\n        }\n\n        if (other != null) {\n            throw new NestableRuntimeException(other);\n        }\n\n        if (isOwner()) {// 锁重入\n            return;\n        }\n\n        BooleanMutex mutex = new BooleanMutex();\n        acquireLock(mutex);\n\n        mutex.get();\n        // 避免zookeeper重启后导致watcher丢失，会出现死锁使用了超时进行重试\n        // try {\n        // mutex.get(DEFAULT_TIMEOUT_PERIOD, TimeUnit.MILLISECONDS);// 阻塞等待值为true\n        // } catch (TimeoutException e) {\n        // if (!mutex.state()) {\n        // lock();\n        // }\n        // }\n\n        if (exception != null) {\n            unlock();\n            throw exception;\n        }\n\n        if (interrupt != null) {\n            unlock();\n            throw interrupt;\n        }\n\n        if (other != null) {\n            unlock();\n            throw new NestableRuntimeException(other);\n        }\n    }\n\n    /**\n     * 尝试获取锁对象, 不会阻塞\n     * \n     * @throws InterruptedException\n     * @throws KeeperException\n     */\n    public boolean tryLock() throws KeeperException {\n        // 可能初始化的时候就失败了\n        if (exception != null) {\n            throw exception;\n        }\n\n        if (isOwner()) {// 锁重入\n            return true;\n        }\n\n        acquireLock(null);\n\n        if (exception != null) {\n            unlock();\n            throw exception;\n        }\n\n        if (interrupt != null) {\n            unlock();\n            Thread.currentThread().interrupt();\n        }\n\n        if (other != null) {\n            unlock();\n            throw new NestableRuntimeException(other);\n        }\n\n        return isOwner();\n    }\n\n    /**\n     * 释放锁对象\n     */\n    public void unlock() throws KeeperException {\n        if (id != null) {\n            zookeeper.delete(root + \"/\" + id);\n            id = null;\n            idName = null;\n        } else {\n            // do nothing\n        }\n    }\n\n    private void ensureExists(final String path) {\n        try {\n            if (zookeeper.exists(path)) {\n                return;\n            }\n\n            zookeeper.create(path, data, CreateMode.PERSISTENT);\n        } catch (ZkInterruptedException e) {\n            Thread.currentThread().interrupt();\n            interrupt = (InterruptedException) e.getCause();\n        } catch (ZkException e) {\n            exception = (KeeperException) e.getCause();\n        }\n    }\n\n    /**\n     * 返回锁对象对应的path\n     */\n    public String getRoot() {\n        return root;\n    }\n\n    /**\n     * 判断当前是不是锁的owner\n     */\n    public boolean isOwner() {\n        return id != null && ownerId != null && id.equals(ownerId);\n    }\n\n    /**\n     * 返回当前的节点id\n     */\n    public String getId() {\n        return this.id;\n    }\n\n    // ===================== helper method =============================\n\n    /**\n     * 执行lock操作，允许传递watch变量控制是否需要阻塞lock操作\n     */\n    private Boolean acquireLock(final BooleanMutex mutex) {\n        try {\n            do {\n                if (id == null) {// 构建当前lock的唯一标识\n                    long sessionId = getSessionId();\n                    String prefix = \"x-\" + sessionId + \"-\";\n                    // 如果第一次，则创建一个节点\n                    String path = zookeeper.create(root + \"/\" + prefix, data, CreateMode.EPHEMERAL_SEQUENTIAL);\n                    int index = path.lastIndexOf(\"/\");\n                    id = StringUtils.substring(path, index + 1);\n                    idName = new LockNode(id);\n                }\n\n                if (id != null) {\n                    List<String> names = zookeeper.getChildren(root);\n                    if (names.isEmpty()) {\n                        logger.warn(\"lock lost with scene:empty list, id[] and node[]\", id, idName);\n                        unlock();// 异常情况，退出后重新创建一个\n                    } else {\n                        // 对节点进行排序\n                        SortedSet<LockNode> sortedNames = new TreeSet<LockNode>();\n                        for (String name : names) {\n                            sortedNames.add(new LockNode(name));\n                        }\n\n                        if (sortedNames.contains(idName) == false) {\n                            logger.warn(\"lock lost with scene:not contains ,id[] and node[]\", id, idName);\n                            unlock();// 异常情况，退出后重新创建一个\n                            continue;\n                        }\n\n                        // 将第一个节点做为ownerId\n                        ownerId = sortedNames.first().getName();\n                        if (mutex != null && isOwner()) {\n                            mutex.set(true);// 直接更新状态，返回\n                            return true;\n                        } else if (mutex == null) {\n                            return isOwner();\n                        }\n\n                        SortedSet<LockNode> lessThanMe = sortedNames.headSet(idName);\n                        if (!lessThanMe.isEmpty()) {\n                            // 关注一下排队在自己之前的最近的一个节点\n                            LockNode lastChildName = lessThanMe.last();\n                            lastChildId = lastChildName.getName();\n                            // 异步watcher处理\n                            IZkConnection connection = zookeeper.getConnection();\n                            // zkclient包装的是一个持久化的zk，分布式lock只需要一次性的watcher，需要调用原始的zk链接进行操作\n                            ZooKeeper orginZk = ((ZooKeeperx) connection).getZookeeper();\n                            Stat stat = orginZk.exists(root + \"/\" + lastChildId, new AsyncWatcher() {\n\n                                public void asyncProcess(WatchedEvent event) {\n                                    if (!mutex.state()) { // 避免重复获取lock\n                                        acquireLock(mutex);\n                                    } else {\n                                        logger.warn(\"locked successful.\");\n                                    }\n                                }\n\n                            });\n\n                            if (stat == null) {\n                                acquireLock(mutex);// 如果节点不存在，需要自己重新触发一下，watcher不会被挂上去\n                            }\n                        } else {\n                            if (isOwner()) {\n                                mutex.set(true);\n                            } else {\n                                logger.warn(\"lock lost with scene:no less ,id[] and node[]\", id, idName);\n                                unlock();// 可能自己的节点已超时挂了，所以id和ownerId不相同\n                            }\n                        }\n                    }\n                }\n            } while (id == null);\n        } catch (KeeperException e) {\n            exception = e;\n            if (mutex != null) {\n                mutex.set(true);\n            }\n        } catch (InterruptedException e) {\n            interrupt = e;\n            if (mutex != null) {\n                mutex.set(true);\n            }\n        } catch (Throwable e) {\n            other = e;\n            if (mutex != null) {\n                mutex.set(true);\n            }\n        }\n\n        if (isOwner() && mutex != null) {\n            mutex.set(true);\n        }\n        return Boolean.FALSE;\n    }\n\n    private long getSessionId() {\n        IZkConnection connection = zookeeper.getConnection();\n        return ((ZooKeeperx) connection).getZookeeper().getSessionId();\n    }\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/zookeeper/lock/DistributedReentrantLock.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.zookeeper.lock;\n\nimport java.text.MessageFormat;\nimport java.util.concurrent.locks.ReentrantLock;\n\nimport org.apache.zookeeper.KeeperException;\n\n/**\n * 基于{@linkplain ReentrantLock}和{@linkplain DistributedLock} 的功能组合，实现多进程+多线程全方位的lock控制\n * \n * @author jianghang 2011-9-30 上午09:48:05\n * @version 4.0.0\n */\npublic class DistributedReentrantLock extends DistributedLock {\n\n    private static final String ID_FORMAT     = \"Thread[{0}] Distributed[{1}]\";\n    private ReentrantLock       reentrantLock = new ReentrantLock();\n\n    public DistributedReentrantLock(String root){\n        super(root);\n    }\n\n    public void lock() throws InterruptedException, KeeperException {\n        reentrantLock.lock();//多线程竞争时，先拿到第一层锁\n        super.lock();\n    }\n\n    public boolean tryLock() throws KeeperException {\n        //多线程竞争时，先拿到第一层锁\n        return reentrantLock.tryLock() && super.tryLock();\n    }\n\n    public void unlock() throws KeeperException {\n        super.unlock();\n        reentrantLock.unlock();//多线程竞争时，释放最外层锁\n    }\n\n    @Override\n    public String getId() {\n        return MessageFormat.format(ID_FORMAT, Thread.currentThread().getId(), super.getId());\n    }\n\n    @Override\n    public boolean isOwner() {\n        return reentrantLock.isHeldByCurrentThread() && super.isOwner();\n    }\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/impl/zookeeper/lock/LockNode.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.impl.zookeeper.lock;\n\nimport org.springframework.util.Assert;\n\n/**\n * 支持排序的节点\n * \n * @author jianghang 2011-9-29 下午01:30:36\n * @version 4.0.0\n */\npublic class LockNode implements Comparable<LockNode> {\n\n    private final String name;\n    private String       prefix;\n    private int          sequence = -1;\n\n    public LockNode(String name){\n        Assert.notNull(name, \"id cannot be null\");\n        this.name = name;\n        this.prefix = name;\n        int idx = name.lastIndexOf('-');\n        if (idx >= 0) {\n            this.prefix = name.substring(0, idx);\n            try {\n                this.sequence = Integer.parseInt(name.substring(idx + 1));\n            } catch (Exception e) {\n                // ignore\n            }\n        }\n    }\n\n    public int compareTo(LockNode that) {\n        int s1 = this.sequence;\n        int s2 = that.sequence;\n        if (s1 == -1 && s2 == -1) {\n            return this.name.compareTo(that.name);\n        }\n\n        if (s1 == -1) {\n            return -1;\n        } else if (s2 == -1) {\n            return 1;\n        } else {\n            return s1 - s2;\n        }\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public int getSequence() {\n        return sequence;\n    }\n\n    public String getPrefix() {\n        return prefix;\n    }\n\n    public String toString() {\n        return name.toString();\n    }\n\n    // ==================== hashcode & equals方法=======================\n\n    @Override\n    public int hashCode() {\n        final int prime = 31;\n        int result = 1;\n        result = prime * result + ((name == null) ? 0 : name.hashCode());\n        return result;\n    }\n\n    @Override\n    public boolean equals(Object obj) {\n        if (this == obj) {\n            return true;\n        }\n        if (obj == null) {\n            return false;\n        }\n        if (getClass() != obj.getClass()) {\n            return false;\n        }\n        LockNode other = (LockNode) obj;\n        if (name == null) {\n            if (other.name != null) {\n                return false;\n            }\n        } else if (!name.equals(other.name)) {\n            return false;\n        }\n        return true;\n    }\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/model/EtlEventData.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.model;\n\n/**\n * S.E.T.L的数据传递对象\n * \n * @author jianghang 2011-8-22 下午04:33:56\n */\npublic class EtlEventData extends ProcessEventData {\n\n    private static final long serialVersionUID = -639227151519007664L;\n    private Long              currNid;                                // 当前节点\n    private Long              nextNid;                                // 下一个节点\n    private Object            desc;                                   // 对应的pipe描述信息\n\n    public Long getNextNid() {\n        return nextNid;\n    }\n\n    public void setNextNid(Long nextNid) {\n        this.nextNid = nextNid;\n    }\n\n    public Object getDesc() {\n        return desc;\n    }\n\n    public void setDesc(Object desc) {\n        this.desc = desc;\n    }\n\n    public Long getCurrNid() {\n        return currNid;\n    }\n\n    public void setCurrNid(Long currNid) {\n        this.currNid = currNid;\n    }\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/model/EventData.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.model;\n\nimport java.io.Serializable;\n\nimport org.apache.commons.lang.builder.ToStringBuilder;\n\nimport com.alibaba.otter.shared.common.utils.OtterToStringStyle;\n\n/**\n * 仲裁器信号传递的基类\n * \n * @author jianghang 2011-8-22 下午08:19:54\n */\npublic class EventData implements Serializable {\n\n    private static final long serialVersionUID = -1375302207165601758L;\n\n    @Override\n    public String toString() {\n        return ToStringBuilder.reflectionToString(this, OtterToStringStyle.DEFAULT_STYLE);\n    }\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/model/MainStemEventData.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.model;\n\n/**\n * 主道控制信号\n * \n * @author jianghang 2011-8-16 下午08:29:48\n */\npublic class MainStemEventData extends PipelineEventData {\n\n    private static final long serialVersionUID = 2694861930354206657L;\n\n    public enum Status {\n        /** 已追上 */\n        OVERTAKE,\n        /** 追赶中 */\n        TAKEING;\n\n        public boolean isOverTake() {\n            return this.equals(Status.OVERTAKE);\n        }\n\n        public boolean isTaking() {\n            return this.equals(Status.TAKEING);\n        }\n    }\n\n    private Long    nid;\n    private Status  status;       // 主道控制信号的状态，已追上或者未追上\n    private boolean active = true;\n\n    public Status getStatus() {\n        return status;\n    }\n\n    public void setStatus(Status status) {\n        this.status = status;\n    }\n\n    public Long getNid() {\n        return nid;\n    }\n\n    public void setNid(Long nid) {\n        this.nid = nid;\n    }\n\n    public boolean isActive() {\n        return active;\n    }\n\n    public void setActive(boolean active) {\n        this.active = active;\n    }\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/model/PipelineEventData.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.model;\n\n/**\n * 对应pipeline的event data\n * \n * @author jianghang 2011-8-17 上午10:31:46\n */\npublic class PipelineEventData extends EventData {\n\n    private static final long serialVersionUID = 4223623194547317751L;\n    private Long              pipelineId;                             // 通道id\n\n    public Long getPipelineId() {\n        return pipelineId;\n    }\n\n    public void setPipelineId(Long pipelineId) {\n        this.pipelineId = pipelineId;\n    }\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/model/PositionEventData.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.model;\n\nimport java.util.Date;\n\n/**\n * 位点信息\n * \n * @author jianghang 2012-12-12 上午10:52:12\n * @version 4.1.3\n */\npublic class PositionEventData extends EventData {\n\n    private static final long serialVersionUID = 1L;\n    private Date              createTime;\n    private Date              modifiedTime;\n    private String            position;\n\n    public Date getCreateTime() {\n        return createTime;\n    }\n\n    public void setCreateTime(Date createTime) {\n        this.createTime = createTime;\n    }\n\n    public Date getModifiedTime() {\n        return modifiedTime;\n    }\n\n    public void setModifiedTime(Date modifiedTime) {\n        this.modifiedTime = modifiedTime;\n    }\n\n    public String getPosition() {\n        return position;\n    }\n\n    public void setPosition(String position) {\n        this.position = position;\n    }\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/model/ProcessEventData.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.model;\n\nimport java.util.Map;\n\n/**\n * 基于process数据的信号对象\n * \n * @author jianghang 2011-8-16 下午08:19:16\n */\npublic class ProcessEventData extends PipelineEventData {\n\n    private static final long serialVersionUID = 3384175022262480571L;\n    private Long              processId;                              // 同步进程id\n    private Long              startTime;                              // 同步开始时间\n    private Long              endTime;                                // 同步结束时间\n    private Long              firstTime;                              // 第一条记录的时间\n\n    private Long              batchId;                                // 批处理Id，对应一批处理的数据\n    private Long              number;                                 // 对应调度的记录数\n    private Long              size;                                   // 对应调度的数据大小\n    private Map               exts;                                   // 对应的扩展数据\n\n    public Long getProcessId() {\n        return processId;\n    }\n\n    public void setProcessId(Long processId) {\n        this.processId = processId;\n    }\n\n    public Long getStartTime() {\n        return startTime;\n    }\n\n    public void setStartTime(Long startTime) {\n        this.startTime = startTime;\n    }\n\n    public Long getEndTime() {\n        return endTime;\n    }\n\n    public void setEndTime(Long endTime) {\n        this.endTime = endTime;\n    }\n\n    public Long getFirstTime() {\n        return firstTime;\n    }\n\n    public void setFirstTime(Long firstTime) {\n        this.firstTime = firstTime;\n    }\n\n    public Long getNumber() {\n        return number;\n    }\n\n    public void setNumber(Long number) {\n        this.number = number;\n    }\n\n    public Long getSize() {\n        return size;\n    }\n\n    public void setSize(Long size) {\n        this.size = size;\n    }\n\n    public Map getExts() {\n        return exts;\n    }\n\n    public void setExts(Map exts) {\n        this.exts = exts;\n    }\n\n    public Long getBatchId() {\n        return batchId;\n    }\n\n    public void setBatchId(Long batchId) {\n        this.batchId = batchId;\n    }\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/model/ProcessNodeEventData.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.model;\n\nimport com.alibaba.otter.shared.common.model.config.pipeline.PipelineParameter.ArbitrateMode;\n\n/**\n * process node节点使用的数据对象\n * \n * @author jianghang 2011-12-1 下午06:23:40\n * @version 4.0.0\n */\npublic class ProcessNodeEventData extends EventData {\n\n    private static final long serialVersionUID = -7622558087796345197L;\n\n    public enum Status {\n        /** 已使用 */\n        USED,\n        /** 未使用 */\n        UNUSED;\n\n        public boolean isUsed() {\n            return this == USED;\n        }\n\n        public boolean isUnUsed() {\n            return this == UNUSED;\n        }\n    }\n\n    private Long          nid;\n    private Status        status;\n    private ArbitrateMode mode = ArbitrateMode.ZOOKEEPER;\n\n    public Status getStatus() {\n        return status;\n    }\n\n    public void setStatus(Status status) {\n        this.status = status;\n    }\n\n    public Long getNid() {\n        return nid;\n    }\n\n    public void setNid(Long nid) {\n        this.nid = nid;\n    }\n\n    public ArbitrateMode getMode() {\n        return mode;\n    }\n\n    public void setMode(ArbitrateMode mode) {\n        this.mode = mode;\n    }\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/model/RemedyIndexEventData.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.model;\n\nimport org.apache.commons.lang.StringUtils;\n\nimport com.alibaba.otter.shared.arbitrate.exception.ArbitrateException;\n\n/**\n * 补救数据记录\n * \n * @author jianghang 2012-4-13 上午11:18:11\n * @version 4.0.2\n */\npublic class RemedyIndexEventData extends EventData {\n\n    private static final String SPLIT            = \"_\";\n    private static final long   serialVersionUID = 3125886367323255220L;\n    private Long                pipelineId;                             // 通道id\n    private Long                processId;\n    private Long                startTime;\n    private Long                endTime;\n\n    public Long getPipelineId() {\n        return pipelineId;\n    }\n\n    public void setPipelineId(Long pipelineId) {\n        this.pipelineId = pipelineId;\n    }\n\n    public Long getProcessId() {\n        return processId;\n    }\n\n    public void setProcessId(Long processId) {\n        this.processId = processId;\n    }\n\n    public Long getStartTime() {\n        return startTime;\n    }\n\n    public void setStartTime(Long startTime) {\n        this.startTime = startTime;\n    }\n\n    public Long getEndTime() {\n        return endTime;\n    }\n\n    public void setEndTime(Long endTime) {\n        this.endTime = endTime;\n    }\n\n    // ===================== helper method =================\n\n    public static String formatNodeName(RemedyIndexEventData eventData) {\n        StringBuilder builder = new StringBuilder();\n        builder.append(eventData.getProcessId()).append(SPLIT).append(eventData.getStartTime()).append(SPLIT).append(eventData.getEndTime());\n        return builder.toString();\n    }\n\n    public static RemedyIndexEventData parseNodeName(String node) {\n        String[] datas = StringUtils.split(node, SPLIT);\n        if (datas.length != 3) {\n            throw new ArbitrateException(\"remedy index[\" + node + \"] format is not correctly!\");\n        }\n\n        RemedyIndexEventData eventData = new RemedyIndexEventData();\n        eventData.setProcessId(Long.valueOf(datas[0]));\n        eventData.setStartTime(Long.valueOf(datas[1]));\n        eventData.setEndTime(Long.valueOf(datas[2]));\n        return eventData;\n    }\n\n    @Override\n    public int hashCode() {\n        final int prime = 31;\n        int result = 1;\n        result = prime * result + ((endTime == null) ? 0 : endTime.hashCode());\n        result = prime * result + ((pipelineId == null) ? 0 : pipelineId.hashCode());\n        result = prime * result + ((processId == null) ? 0 : processId.hashCode());\n        result = prime * result + ((startTime == null) ? 0 : startTime.hashCode());\n        return result;\n    }\n\n    @Override\n    public boolean equals(Object obj) {\n        if (this == obj) {\n            return true;\n        }\n        if (obj == null) {\n            return false;\n        }\n        if (!(obj instanceof RemedyIndexEventData)) {\n            return false;\n        }\n        RemedyIndexEventData other = (RemedyIndexEventData) obj;\n        if (endTime == null) {\n            if (other.endTime != null) {\n                return false;\n            }\n        } else if (!endTime.equals(other.endTime)) {\n            return false;\n        }\n        if (pipelineId == null) {\n            if (other.pipelineId != null) {\n                return false;\n            }\n        } else if (!pipelineId.equals(other.pipelineId)) {\n            return false;\n        }\n        if (processId == null) {\n            if (other.processId != null) {\n                return false;\n            }\n        } else if (!processId.equals(other.processId)) {\n            return false;\n        }\n        if (startTime == null) {\n            if (other.startTime != null) {\n                return false;\n            }\n        } else if (!startTime.equals(other.startTime)) {\n            return false;\n        }\n        return true;\n    }\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/model/SyncStatusEventData.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.model;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.apache.commons.lang.builder.ToStringBuilder;\n\nimport com.alibaba.otter.shared.common.utils.OtterToStringStyle;\n\n/**\n * eromanga中取出的一批数据需要划分成多个process进行同步，保存process的数目和每个process同步的数据量,以及这一批数据同步是否成功.\n * \n * @author jianghang 2011-10-18 上午10:16:12\n * @version 4.0.0\n */\npublic class SyncStatusEventData extends PipelineEventData {\n\n    private static final long serialVersionUID = -1755817244279698216L;\n    private List<SyncStatus>  status           = new ArrayList<SyncStatus>();\n    /**\n     * 初始值为并行度,在ProcessEndTask收到一个process的ack以后，就减1，如果=0时，就可以开启SelectConsumerTask去取下一批数据了.\n     */\n    private long              parallelism;\n\n    public void decParallelism() {\n        --parallelism;\n    }\n\n    public void addParallelism() {\n        ++parallelism;\n    }\n\n    public List<SyncStatus> getStatus() {\n        return status;\n    }\n\n    public void setStatus(List<SyncStatus> status) {\n        this.status = status;\n    }\n\n    public void addStatus(SyncStatus status) {\n        this.status.add(status);\n    }\n\n    public long getParallelism() {\n        return parallelism;\n    }\n\n    public void setParallelism(long parallelism) {\n        this.parallelism = parallelism;\n    }\n\n    /**\n     * @author xiaoqing.zhouxq\n     */\n    public static class SyncStatus implements Serializable {\n\n        private static final long serialVersionUID  = 794565950364625433L;\n\n        public static final long  DEFAULT_PROCESSID = -1;\n\n        /**\n         * 划分成的多个process同步是否成功,如果成功，给eromanga发送ack，如果失败，从eromanga中取同一批数据， 并且过滤掉已经同步成功的process的数据.\n         */\n        private boolean           status;\n\n        /**\n         * 每个process需要同步的数据量.\n         */\n        private int               processDataCount;\n\n        private long              processId         = DEFAULT_PROCESSID;\n\n        public SyncStatus(){\n\n        }\n\n        public SyncStatus(boolean status, int processDataCount){\n            this.status = status;\n            this.processDataCount = processDataCount;\n        }\n\n        public boolean isStatus() {\n            return status;\n        }\n\n        public void setStatus(boolean status) {\n            this.status = status;\n        }\n\n        public int getProcessDataCount() {\n            return processDataCount;\n        }\n\n        public void setProcessDataCount(int processDataCount) {\n            this.processDataCount = processDataCount;\n        }\n\n        public long getProcessId() {\n            return processId;\n        }\n\n        public void setProcessId(long processId) {\n            this.processId = processId;\n        }\n\n        @Override\n        public String toString() {\n            return ToStringBuilder.reflectionToString(this, OtterToStringStyle.DEFAULT_STYLE);\n        }\n    }\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/java/com/alibaba/otter/shared/arbitrate/model/TerminEventData.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.model;\n\n/**\n * 结束信号对象\n * \n * @author jianghang 2011-9-26 上午11:34:12\n * @version 4.0.0\n */\npublic class TerminEventData extends ProcessEventData {\n\n    private static final long serialVersionUID = -5108807540865997596L;\n\n    public static enum TerminType {\n        /** 正常结束 */\n        NORMAL,\n        /** 警告信息 */\n        WARNING,\n        /** 回滚对应同步 */\n        ROLLBACK,\n        /** 重新开始同步 */\n        RESTART,\n        /** 关闭同步 */\n        SHUTDOWN;\n\n        public boolean isNormal() {\n            return this.equals(NORMAL);\n        }\n\n        public boolean isWarning() {\n            return this.equals(WARNING);\n        }\n\n        public boolean isRollback() {\n            return this.equals(ROLLBACK);\n        }\n\n        public boolean isRestart() {\n            return this.equals(RESTART);\n        }\n\n        public boolean isShutdown() {\n            return this.equals(SHUTDOWN);\n        }\n    }\n\n    private TerminType type = TerminType.NORMAL;\n    private String     code;\n    private String     desc;\n    private Long       currNid;\n\n    public TerminType getType() {\n        return type;\n    }\n\n    public void setType(TerminType type) {\n        this.type = type;\n    }\n\n    public String getCode() {\n        return code;\n    }\n\n    public void setCode(String code) {\n        this.code = code;\n    }\n\n    public String getDesc() {\n        return desc;\n    }\n\n    public void setDesc(String desc) {\n        this.desc = desc;\n    }\n\n    public Long getCurrNid() {\n        return currNid;\n    }\n\n    public void setCurrNid(Long currNid) {\n        this.currNid = currNid;\n    }\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/main/resources/spring/otter-arbitrate-alarm.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<beans xmlns=\"http://www.springframework.org/schema/beans\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n    xmlns:tx=\"http://www.springframework.org/schema/tx\" xmlns:aop=\"http://www.springframework.org/schema/aop\"\n    xmlns:lang=\"http://www.springframework.org/schema/lang\" xmlns:context=\"http://www.springframework.org/schema/context\"\n    xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd\n           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd\n           http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-2.0.xsd\n           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd\n           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd\"\n    default-autowire=\"byName\" >\n\t\n\t<bean name=\"alarmClientService\" class=\"com.alibaba.otter.shared.arbitrate.impl.alarm.AlarmClientService\" />\n</beans>"
  },
  {
    "path": "shared/arbitrate/src/main/resources/spring/otter-arbitrate-common.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<beans xmlns=\"http://www.springframework.org/schema/beans\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n    xmlns:tx=\"http://www.springframework.org/schema/tx\" xmlns:aop=\"http://www.springframework.org/schema/aop\"\n    xmlns:lang=\"http://www.springframework.org/schema/lang\" xmlns:context=\"http://www.springframework.org/schema/context\"\n    xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd\n           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd\n           http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-2.0.xsd\n           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd\n           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd\"\n    default-autowire=\"byName\" >\n\t\n\t<!-- 初始化一下ArbitrateFactory，注入spring容器 -->\n\t<bean name=\"arbitrateFactory\" class=\"com.alibaba.otter.shared.arbitrate.impl.setl.ArbitrateFactory\" lazy-init=\"false\" scope=\"singleton\"/>\n\t\n\t<!-- zookeeper -->\n\t<bean name=\"zookeeperClient\" class=\"com.alibaba.otter.shared.arbitrate.impl.zookeeper.ZooKeeperClient\" depends-on=\"arbitrateFactory\" lazy-init=\"false\" scope=\"singleton\">\n\t\t<property name=\"cluster\" value=\"${otter.zookeeper.cluster.default:127.0.0.1:2181}\" />\n\t\t<property name=\"sessionTimeout\" value=\"${otter.zookeeper.sessionTimeout:60000}\" />\n\t</bean>\n\t\n\t<bean id=\"arbitrateExecutor\" class=\"com.alibaba.otter.shared.common.utils.thread.ExecutorServiceFactoryBean\">\n\t\t<property name=\"poolSize\" value=\"20\" />\n\t\t<property name=\"acceptCount\" value=\"40\" />\n\t\t<property name=\"name\" value=\"Otter-Arbitrate-Executor\" />\n\t</bean>\n\t\n\t<bean id=\"nodeMonitor\" class=\"com.alibaba.otter.shared.arbitrate.impl.setl.monitor.NodeMonitor\" scope=\"singleton\">\n\t</bean>\n</beans>"
  },
  {
    "path": "shared/arbitrate/src/main/resources/spring/otter-arbitrate-communication.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<beans xmlns=\"http://www.springframework.org/schema/beans\"\r\n\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\r\n\txmlns:aop=\"http://www.springframework.org/schema/aop\"\r\n\txmlns:tx=\"http://www.springframework.org/schema/tx\"\r\n\txsi:schemaLocation=\"\n\thttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd\n\thttp://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd\n\thttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd\"\r\n\tdefault-autowire=\"byName\" default-dependency-check=\"none\">\r\n\r\n\t<bean id=\"arbitrateCommunicationClient\" class=\"com.alibaba.otter.shared.communication.core.impl.DefaultCommunicationClientImpl\" init-method=\"initial\" destroy-method=\"destory\">\r\n\t\t<property name=\"poolSize\" value=\"${otter.communication.pool.size:10}\" />\r\n\t\t<property name=\"factory\">\r\n\t\t\t<bean class=\"com.alibaba.otter.shared.communication.core.impl.dubbo.DubboCommunicationConnectionFactory\">\r\n\t\t\t\t<property name=\"payload\" value=\"${otter.communication.payload:8388608}\" />\r\n\t\t\t</bean>\r\n\t\t</property>\r\n\t</bean>\r\n\t\r\n\t<!-- Node communication -->\r\n\t<bean id=\"arbitrateCommmunicationClient\" class=\"com.alibaba.otter.shared.arbitrate.impl.communication.ArbitrateCommmunicationClient\" scope=\"singleton\" >\r\n\t\t<property name=\"delegate\" ref=\"arbitrateCommunicationClient\" />\r\n\t\t<property name=\"managerAddress\" value=\"${otter.manager.address:127.0.0.1:1099}\" />\r\n\t</bean>\r\n</beans>\r\n"
  },
  {
    "path": "shared/arbitrate/src/main/resources/spring/otter-arbitrate-event.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<beans xmlns=\"http://www.springframework.org/schema/beans\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n    xmlns:tx=\"http://www.springframework.org/schema/tx\" xmlns:aop=\"http://www.springframework.org/schema/aop\"\n    xmlns:lang=\"http://www.springframework.org/schema/lang\" xmlns:context=\"http://www.springframework.org/schema/context\"\n    xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd\n           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd\n           http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-2.0.xsd\n           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd\n           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd\"\n    default-autowire=\"byName\" >\n\t\n\t<!--event service -->\n\t<bean name=\"arbitrateEventService\" class=\"com.alibaba.otter.shared.arbitrate.impl.ArbitrateEventServiceImpl\" scope=\"singleton\" />\n\t<bean name=\"arbitrateViewService\" class=\"com.alibaba.otter.shared.arbitrate.impl.ArbitrateViewServiceImpl\" scope=\"singleton\" />\n\t\n\t<bean name=\"toolEvent\" class=\"com.alibaba.otter.shared.arbitrate.impl.setl.ToolArbitrateEvent\" scope=\"singleton\" />\n\t<bean name=\"mainStemEvent\" class=\"com.alibaba.otter.shared.arbitrate.impl.setl.MainStemArbitrateEvent\" scope=\"singleton\" />\n\t\n\t<!-- delegate bean -->\n\t<bean name=\"selectEvent\" class=\"com.alibaba.otter.shared.arbitrate.impl.setl.delegate.SelectDelegateArbitrateEvent\" scope=\"singleton\">\n\t\t<property name=\"delegate\">\n\t\t\t<map>\n\t\t\t\t<entry key=\"MEMORY\" value-ref=\"selectMemoryEvent\" />\n\t\t\t\t<entry key=\"RPC\" value-ref=\"selectRpcEvent\" />\n\t\t\t\t<entry key=\"ZOOKEEPER\" value-ref=\"selectZooKeeperEvent\" />\n\t\t\t</map>\n\t\t</property>\n\t</bean>\n\t<bean name=\"extractEvent\" class=\"com.alibaba.otter.shared.arbitrate.impl.setl.delegate.ExtractDelegateArbitrateEvent\" scope=\"singleton\" >\n\t\t<property name=\"delegate\">\n\t\t\t<map>\n\t\t\t\t<entry key=\"MEMORY\" value-ref=\"extractMemoryEvent\" />\n\t\t\t\t<entry key=\"RPC\" value-ref=\"extractRpcEvent\" />\n\t\t\t\t<entry key=\"ZOOKEEPER\" value-ref=\"extractZooKeeperEvent\" />\n\t\t\t</map>\n\t\t</property>\n\t</bean>\n\t<bean name=\"transformEvent\" class=\"com.alibaba.otter.shared.arbitrate.impl.setl.delegate.TransformDelegateArbitrateEvent\" scope=\"singleton\">\n\t\t<property name=\"delegate\">\n\t\t\t<map>\n\t\t\t\t<entry key=\"MEMORY\" value-ref=\"transformMemoryEvent\" />\n\t\t\t\t<entry key=\"RPC\" value-ref=\"transformRpcEvent\" />\n\t\t\t\t<entry key=\"ZOOKEEPER\" value-ref=\"transformZooKeeperEvent\" />\n\t\t\t</map>\n\t\t</property>\n\t</bean>\n\t<bean name=\"loadEvent\" class=\"com.alibaba.otter.shared.arbitrate.impl.setl.delegate.LoadDelegateArbitrateEvent\" scope=\"singleton\">\n\t\t<property name=\"delegate\">\n\t\t\t<map>\n\t\t\t\t<entry key=\"MEMORY\" value-ref=\"loadMemoryEvent\" />\n\t\t\t\t<entry key=\"RPC\" value-ref=\"loadRpcEvent\" />\n\t\t\t\t<entry key=\"ZOOKEEPER\" value-ref=\"loadZooKeeperEvent\" />\n\t\t\t</map>\n\t\t</property>\n\t</bean>\n\t<bean name=\"terminEvent\" class=\"com.alibaba.otter.shared.arbitrate.impl.setl.delegate.TerminDelegateArbitrateEvent\" scope=\"singleton\">\n\t\t<property name=\"delegate\">\n\t\t\t<map>\n\t\t\t\t<entry key=\"MEMORY\" value-ref=\"terminMemoryEvent\" />\n\t\t\t\t<entry key=\"RPC\" value-ref=\"terminRpcEvent\" />\n\t\t\t\t<entry key=\"ZOOKEEPER\" value-ref=\"terminZooKeeperEvent\" />\n\t\t\t</map>\n\t\t</property>\n\t</bean>\n\t\n\t<!-- distuributed bean -->\n\t<bean name=\"selectZooKeeperEvent\" class=\"com.alibaba.otter.shared.arbitrate.impl.setl.zookeeper.SelectZooKeeperArbitrateEvent\" scope=\"singleton\" />\n\t<bean name=\"extractZooKeeperEvent\" class=\"com.alibaba.otter.shared.arbitrate.impl.setl.zookeeper.ExtractZooKeeperArbitrateEvent\" scope=\"singleton\" />\n\t<bean name=\"transformZooKeeperEvent\" class=\"com.alibaba.otter.shared.arbitrate.impl.setl.zookeeper.TransformZooKeeperArbitrateEvent\" scope=\"singleton\" />\n\t<bean name=\"loadZooKeeperEvent\" class=\"com.alibaba.otter.shared.arbitrate.impl.setl.zookeeper.LoadZooKeeperArbitrateEvent\" scope=\"singleton\">\n\t\t<property name=\"terminEvent\" ref=\"terminZooKeeperEvent\" />\n\t</bean>\n\t<bean name=\"terminZooKeeperEvent\" class=\"com.alibaba.otter.shared.arbitrate.impl.setl.zookeeper.TerminZooKeeperArbitrateEvent\" scope=\"singleton\" />\n\t<!--  termin process bean -->\n\t<bean name=\"normalTerminProcess\" class=\"com.alibaba.otter.shared.arbitrate.impl.setl.zookeeper.termin.NormalTerminProcess\" scope=\"singleton\" />\n\t<bean name=\"warningTerminProcess\" class=\"com.alibaba.otter.shared.arbitrate.impl.setl.zookeeper.termin.WarningTerminProcess\" scope=\"singleton\" />\n\t<bean name=\"errorTerminProcess\" class=\"com.alibaba.otter.shared.arbitrate.impl.setl.zookeeper.termin.ErrorTerminProcess\" scope=\"singleton\" />\n\t\n\t<!-- rpc bean -->\n\t<bean name=\"selectRpcEvent\" class=\"com.alibaba.otter.shared.arbitrate.impl.setl.rpc.SelectRpcArbitrateEvent\" scope=\"singleton\" />\n\t<bean name=\"extractRpcEvent\" class=\"com.alibaba.otter.shared.arbitrate.impl.setl.rpc.ExtractRpcArbitrateEvent\" scope=\"singleton\" />\n\t<bean name=\"transformRpcEvent\" class=\"com.alibaba.otter.shared.arbitrate.impl.setl.rpc.TransformRpcArbitrateEvent\" scope=\"singleton\" />\n\t<bean name=\"loadRpcEvent\" class=\"com.alibaba.otter.shared.arbitrate.impl.setl.rpc.LoadRpcArbitrateEvent\" scope=\"singleton\">\n\t\t<property name=\"terminEvent\" ref=\"terminRpcEvent\" />\n\t</bean>\n\t<bean name=\"terminRpcEvent\" class=\"com.alibaba.otter.shared.arbitrate.impl.setl.rpc.TerminRpcArbitrateEvent\" scope=\"singleton\" />\n\t<bean name=\"rpcStageEventDispatcher\" class=\"com.alibaba.otter.shared.arbitrate.impl.setl.rpc.RpcStageEventDispatcher\" scope=\"singleton\" />\n\t<!-- memory bean -->\n\t<bean name=\"selectMemoryEvent\" class=\"com.alibaba.otter.shared.arbitrate.impl.setl.memory.SelectMemoryArbitrateEvent\" scope=\"singleton\" />\n\t<bean name=\"extractMemoryEvent\" class=\"com.alibaba.otter.shared.arbitrate.impl.setl.memory.ExtractMemoryArbitrateEvent\" scope=\"singleton\" />\n\t<bean name=\"transformMemoryEvent\" class=\"com.alibaba.otter.shared.arbitrate.impl.setl.memory.TransformMemoryArbitrateEvent\" scope=\"singleton\" />\n\t<bean name=\"loadMemoryEvent\" class=\"com.alibaba.otter.shared.arbitrate.impl.setl.memory.LoadMemoryArbitrateEvent\" scope=\"singleton\">\n\t\t<property name=\"terminEvent\" ref=\"terminMemoryEvent\" />\n\t</bean>\n\t<bean name=\"terminMemoryEvent\" class=\"com.alibaba.otter.shared.arbitrate.impl.setl.memory.TerminMemoryArbitrateEvent\" scope=\"singleton\" />\n</beans>\n"
  },
  {
    "path": "shared/arbitrate/src/main/resources/spring/otter-arbitrate-log.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<beans xmlns=\"http://www.springframework.org/schema/beans\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n    xmlns:tx=\"http://www.springframework.org/schema/tx\" xmlns:aop=\"http://www.springframework.org/schema/aop\"\n    xmlns:lang=\"http://www.springframework.org/schema/lang\" xmlns:context=\"http://www.springframework.org/schema/context\"\n    xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd\n           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd\n           http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-2.0.xsd\n           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd\n           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd\"\n    default-autowire=\"byName\" >\n\t\n\t<bean name=\"logInterceptor\" class=\"com.alibaba.otter.shared.arbitrate.impl.interceptor.LogInterceptor\" scope=\"singleton\"/>\n\t<bean class=\"org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator\">\n\t\t<property name=\"proxyTargetClass\" value=\"true\"/><!-- 强制使用cglib代理 -->\n        <property name=\"beanNames\">\n        \t<list>\n        \t\t<value>channelEvent</value>\n        \t\t<value>pipelineEvent</value>\n        \t\t<value>nodeEvent</value>\n        \t\t\n        \t\t<value>mainStemEvent</value>\n        \t\t<value>selectEvent</value>\n        \t\t<value>extractEvent</value>\n        \t\t<value>transformEvent</value>\n        \t\t<value>loadEvent</value>\n        \t\t\n        \t\t<value>terminEvent</value>\n        \t\t<value>normalTerminProcess</value>\n        \t\t<value>warningTerminProcess</value>\n        \t\t<value>errorTerminProcess</value>\n        \t</list>\n        </property>\n        <property name=\"interceptorNames\">\n        \t<list>\n\t            <value>logInterceptor</value>\n        \t</list>\n        </property>\n     </bean>\n</beans>\n"
  },
  {
    "path": "shared/arbitrate/src/main/resources/spring/otter-arbitrate-manage.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<beans xmlns=\"http://www.springframework.org/schema/beans\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n    xmlns:tx=\"http://www.springframework.org/schema/tx\" xmlns:aop=\"http://www.springframework.org/schema/aop\"\n    xmlns:lang=\"http://www.springframework.org/schema/lang\" xmlns:context=\"http://www.springframework.org/schema/context\"\n    xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd\n           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd\n           http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-2.0.xsd\n           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd\n           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd\"\n    default-autowire=\"byName\" >\n\t\n\t<bean name=\"arbitrateManageService\" class=\"com.alibaba.otter.shared.arbitrate.impl.ArbitrateManageServiceImpl\" scope=\"singleton\" />\n\t\n\t<!-- manage event -->\n\t<bean name=\"channelEvent\" class=\"com.alibaba.otter.shared.arbitrate.impl.manage.ChannelArbitrateEvent\" scope=\"singleton\" />\n\t<bean name=\"pipelineEvent\" class=\"com.alibaba.otter.shared.arbitrate.impl.manage.PipelineArbitrateEvent\" scope=\"singleton\" />\n\t<bean name=\"nodeEvent\" class=\"com.alibaba.otter.shared.arbitrate.impl.manage.NodeArbitrateEvent\" scope=\"singleton\" />\n\t<bean name=\"systemEvent\" class=\"com.alibaba.otter.shared.arbitrate.impl.manage.SystemArbitrateEvent\" scope=\"singleton\" />\n\t\n\t<bean name=\"zooKeeperHeartBeatWorker\" class=\"com.alibaba.otter.shared.arbitrate.impl.zookeeper.ZooKeeperHeartBeatWorker\" scope=\"singleton\">\n\t\t<property name=\"nodeEvent\" ref=\"nodeEvent\" />\n\t</bean>\n</beans>"
  },
  {
    "path": "shared/arbitrate/src/test/java/com/alibaba/otter/shared/arbitrate/BaseEventTest.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport mockit.Mock;\nimport mockit.Mockit;\n\nimport org.testng.annotations.BeforeClass;\n\nimport com.alibaba.otter.shared.arbitrate.impl.ArbitrateConstants;\nimport com.alibaba.otter.shared.arbitrate.impl.zookeeper.ZooKeeperClient;\nimport com.alibaba.otter.shared.common.utils.zookeeper.ZkClientx;\n\npublic class BaseEventTest extends BaseOtterTest {\n\n    private String    cluster1  = \"127.0.0.1:2188\";\n    private String    cluster2  = \"127.0.0.1:2188,127.0.0.1:2188\";\n    private ZkClientx zookeeper = null;\n\n    public ZkClientx getZookeeper() {\n        // ReflectionUtils.setField(zookeeperField, new ZooKeeperClient(),\n        // null);\n        Mockit.setUpMock(ZooKeeperClient.class, new Object() {\n\n            @Mock\n            private List<String> getServerAddrs() {\n                return Arrays.asList(cluster1, cluster2);\n            }\n\n        });\n\n        return ZooKeeperClient.getInstance();\n    }\n\n    @BeforeClass\n    final public void clean() {\n        zookeeper = getZookeeper();\n        cleaner(ArbitrateConstants.NODE_CHANNEL_ROOT);\n        cleaner(ArbitrateConstants.NODE_NID_ROOT);\n    }\n\n    private void cleaner(String path) {\n        List<String> nodes = zookeeper.getChildren(path);\n        for (String node : nodes) {\n            cleaner(path + \"/\" + node);\n        }\n        if (path.equals(ArbitrateConstants.NODE_CHANNEL_ROOT) || path.equals(ArbitrateConstants.NODE_NID_ROOT)) {\n            return;\n        } else {\n            System.out.println(\"clean :\" + path);\n            zookeeper.delete(path);\n            return;\n        }\n    }\n\n    protected void sleep() {\n        try {\n            Thread.sleep(1000);\n        } catch (InterruptedException e) {\n            want.fail();\n        }\n    }\n\n    protected void sleep(Long time) {\n        try {\n            Thread.sleep(time);\n        } catch (InterruptedException e) {\n            want.fail();\n        }\n    }\n}\n"
  },
  {
    "path": "shared/arbitrate/src/test/java/com/alibaba/otter/shared/arbitrate/BaseOtterTest.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate;\n\nimport java.util.Map;\n\nimport org.jtester.annotations.SpringApplicationContext;\nimport org.jtester.core.TestedObject;\nimport org.springframework.beans.factory.BeanFactory;\nimport org.springframework.beans.factory.config.AutowireCapableBeanFactory;\nimport org.testng.annotations.BeforeMethod;\n\nimport com.alibaba.otter.shared.arbitrate.impl.setl.ArbitrateFactory;\nimport com.alibaba.otter.shared.common.utils.TestUtils;\n\n/**\n * @author jianghang 2011-9-16 下午02:58:37\n * @version 4.0.0\n */\n@SpringApplicationContext(\"applicationContext.xml\")\npublic class BaseOtterTest extends org.jtester.testng.JTester {\n\n    @BeforeMethod\n    public void setUp() {\n        try {\n            Map cache = (Map) TestUtils.getField(new ArbitrateFactory(), \"cache\");\n            cache.clear();\n        } catch (Exception e) {\n            want.fail();\n        }\n    }\n\n    protected BeanFactory getBeanFactory() {\n        return (BeanFactory) TestedObject.getSpringBeanFactory();\n    }\n\n    protected void autowire(Object obj) {\n        // 重新注入一下对象\n        ((AutowireCapableBeanFactory) TestedObject.getSpringBeanFactory()).autowireBeanProperties(obj,\n                                                                                                  AutowireCapableBeanFactory.AUTOWIRE_BY_NAME,\n                                                                                                  true);\n    }\n}\n"
  },
  {
    "path": "shared/arbitrate/src/test/java/com/alibaba/otter/shared/arbitrate/demo/ArbitrateAllTest.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.demo;\n\nimport java.util.Arrays;\n\nimport mockit.Mock;\nimport mockit.Mockit;\n\nimport org.testng.annotations.AfterMethod;\nimport org.testng.annotations.BeforeMethod;\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.shared.arbitrate.ArbitrateEventService;\nimport com.alibaba.otter.shared.arbitrate.BaseEventTest;\nimport com.alibaba.otter.shared.arbitrate.demo.servcie.ExtractServiceDemo;\nimport com.alibaba.otter.shared.arbitrate.demo.servcie.LoadServiceDemo;\nimport com.alibaba.otter.shared.arbitrate.demo.servcie.MainStemServiceDemo;\nimport com.alibaba.otter.shared.arbitrate.demo.servcie.ProcessViewDemo;\nimport com.alibaba.otter.shared.arbitrate.demo.servcie.SelectServiceDemo;\nimport com.alibaba.otter.shared.arbitrate.demo.servcie.TerminProcessDemo;\nimport com.alibaba.otter.shared.arbitrate.demo.servcie.TransformServiceDemo;\nimport com.alibaba.otter.shared.arbitrate.impl.ArbitrateConstants;\nimport com.alibaba.otter.shared.arbitrate.impl.communication.ArbitrateCommmunicationClient;\nimport com.alibaba.otter.shared.arbitrate.impl.config.ArbitrateConfigUtils;\nimport com.alibaba.otter.shared.arbitrate.impl.manage.ChannelArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.manage.NodeArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.manage.PipelineArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.ArbitrateFactory;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.helper.StagePathUtils;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.lb.ExtractRandomLoadBanlance;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.lb.ExtractRoundRobinLoadBalance;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.lb.TransformRandomLoadBanlance;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.lb.TransformRoundRobinLoadBalance;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.memory.MemoryStageController;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.monitor.PermitMonitor;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.rpc.RpcStageController;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.rpc.monitor.SelectProcessListener;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.zookeeper.monitor.ExtractStageListener;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.zookeeper.monitor.LoadStageListener;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.zookeeper.monitor.SelectStageListener;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.zookeeper.monitor.TransformStageListener;\nimport com.alibaba.otter.shared.arbitrate.model.TerminEventData;\nimport com.alibaba.otter.shared.arbitrate.model.TerminEventData.TerminType;\nimport com.alibaba.otter.shared.common.model.config.channel.Channel;\nimport com.alibaba.otter.shared.common.model.config.node.Node;\nimport com.alibaba.otter.shared.common.model.config.pipeline.Pipeline;\nimport com.alibaba.otter.shared.common.model.config.pipeline.PipelineParameter.ArbitrateMode;\nimport com.alibaba.otter.shared.common.utils.zookeeper.ZkClientx;\nimport com.alibaba.otter.shared.communication.core.model.Event;\n\n/**\n * demo的启动入口\n * \n * @author jianghang 2011-9-22 下午03:58:53\n * @version 4.0.0\n */\npublic class ArbitrateAllTest extends BaseEventTest {\n\n    private MainStemServiceDemo    mainStem;\n    private SelectServiceDemo      select;\n    private ExtractServiceDemo     extract;\n    private TransformServiceDemo   transform;\n    private LoadServiceDemo        load;\n    private ProcessViewDemo        view;\n    private TerminProcessDemo      termin;\n\n    // 环境数据准备对象\n    private NodeArbitrateEvent     nodeEvent;\n    private ChannelArbitrateEvent  channelEvent;\n    private PipelineArbitrateEvent pipelineEvent;\n    private ArbitrateEventService  arbitrateEventService;\n    private final Node             local         = new Node();\n    private final Long             nid           = 1L;\n    private Long                   channelId     = 100L;\n    private Long                   pipelineId    = 100L;\n    private ZkClientx              zookeeper;\n    private ArbitrateMode          arbitrateMode = ArbitrateMode.RPC;\n\n    @BeforeMethod\n    public void setUp() {\n        // mock 配置信息数据\n        Mockit.setUpMock(ArbitrateConfigUtils.class, new Object() {\n\n            @Mock\n            public Channel getChannelByChannelId(Long channelId) {\n                Channel channel = new Channel();\n                channel.setId(channelId);\n                Pipeline pipeline = new Pipeline();\n                pipeline.setId(pipelineId);\n                pipeline.setSelectNodes(Arrays.asList(local));\n                pipeline.setExtractNodes(Arrays.asList(local));\n                pipeline.setLoadNodes(Arrays.asList(local));\n                pipeline.getParameters().setArbitrateMode(arbitrateMode);\n                channel.setPipelines(Arrays.asList(pipeline));\n                return channel;\n            }\n\n            @Mock\n            public Pipeline getPipeline(Long pipelineId) {\n                Pipeline pipeline = new Pipeline();\n                pipeline.setId(pipelineId);\n                pipeline.setSelectNodes(Arrays.asList(local));\n                pipeline.setExtractNodes(Arrays.asList(local));\n                pipeline.setLoadNodes(Arrays.asList(local));\n                pipeline.getParameters().setArbitrateMode(arbitrateMode);\n                return pipeline;\n            }\n\n            @Mock\n            public Long getCurrentNid() {\n                return nid;\n            }\n\n            @Mock\n            public int getParallelism(Long pipelineId) {\n                return 3;// 并行度\n            }\n\n            @Mock\n            public Pipeline getOppositePipeline(Long pipelineId) {\n                return null;\n            }\n\n            @Mock\n            public Channel getChannel(Long pipelineId) {\n                Channel channel = new Channel();\n                channel.setId(channelId);\n                Pipeline pipeline = new Pipeline();\n                pipeline.setId(pipelineId);\n                pipeline.getParameters().setArbitrateMode(arbitrateMode);\n                channel.setPipelines(Arrays.asList(pipeline));\n                return channel;\n            }\n\n        });\n\n        Mockit.setUpMock(ArbitrateCommmunicationClient.class, new Object() {\n\n            @Mock\n            public Object callManager(final Event event) {\n                // do nothing\n                return null;\n            }\n        });\n\n        zookeeper = getZookeeper();\n\n        local.setId(nid);\n        nodeEvent = new NodeArbitrateEvent();\n        channelEvent = new ChannelArbitrateEvent();\n        pipelineEvent = new PipelineArbitrateEvent();\n\n        // 创建node节点\n        nodeEvent.init(local.getId());\n\n        // 创建pipeline节点\n        channelEvent.init(channelId);\n        pipelineEvent.init(channelId, pipelineId);\n\n        arbitrateEventService = (ArbitrateEventService) getBeanFactory().getBean(\"arbitrateEventService\");\n        mainStem = new MainStemServiceDemo();\n        autowire(mainStem);\n        select = new SelectServiceDemo();\n        autowire(select);\n        extract = new ExtractServiceDemo();\n        autowire(extract);\n        transform = new TransformServiceDemo();\n        autowire(transform);\n        load = new LoadServiceDemo();\n        autowire(load);\n        view = new ProcessViewDemo();\n        autowire(view);\n        termin = new TerminProcessDemo();\n        autowire(termin);\n    }\n\n    @AfterMethod\n    public void tearDown() {\n        // 删除mainStem节点\n        String path = StagePathUtils.getPipeline(pipelineId) + \"/\" + ArbitrateConstants.NODE_MAINSTEM;\n        zookeeper.delete(path);\n\n        nodeEvent.destory(local.getId());\n        // 关闭pipeline\n        pipelineEvent.destory(channelId, pipelineId);\n        channelEvent.destory(channelId);\n    }\n\n    @Test\n    public void testDemo() {\n        // 设置启动标志\n        channelEvent.start(channelId);\n        sleep(); // 停顿一下\n\n        PermitMonitor permit = ArbitrateFactory.getInstance(pipelineId, PermitMonitor.class);\n\n        // 优先启动主导线程\n        mainStem.submit(pipelineId);\n        try {\n            permit.waitForPermit();// 阻塞等待授权\n        } catch (InterruptedException e1) {\n            want.fail();\n        }\n\n        if (arbitrateMode.isZookeeper()) {\n            ArbitrateFactory.getInstance(pipelineId, SelectStageListener.class);\n            ArbitrateFactory.getInstance(pipelineId, ExtractStageListener.class);\n            ArbitrateFactory.getInstance(pipelineId, TransformStageListener.class);\n            ArbitrateFactory.getInstance(pipelineId, LoadStageListener.class);\n        }\n\n        if (arbitrateMode.isMemory()) {\n            ArbitrateFactory.getInstance(pipelineId, MemoryStageController.class);\n        }\n\n        if (arbitrateMode.isRpc()) {\n            ArbitrateFactory.getInstance(pipelineId, RpcStageController.class);\n            ArbitrateFactory.getInstance(pipelineId, SelectProcessListener.class);\n        }\n\n        if (arbitrateMode.isZookeeper() || arbitrateMode.isRpc()) {\n            ArbitrateFactory.getInstance(pipelineId, ExtractRandomLoadBanlance.class);\n            ArbitrateFactory.getInstance(pipelineId, ExtractRoundRobinLoadBalance.class);\n            ArbitrateFactory.getInstance(pipelineId, TransformRandomLoadBanlance.class);\n            ArbitrateFactory.getInstance(pipelineId, TransformRoundRobinLoadBalance.class);\n        }\n\n        // 启动\n        select.submit(pipelineId);\n        extract.submit(pipelineId);\n        transform.submit(pipelineId);\n        load.submit(pipelineId);\n        view.submit(pipelineId);\n        this.termin.submit(pipelineId);\n\n        try {\n            Thread.sleep(20 * 1000);// 运行30s\n        } catch (InterruptedException e) {\n            want.fail();\n        }\n\n        // 发送结束事件\n        TerminEventData termin = new TerminEventData();\n        termin.setPipelineId(pipelineId);\n        termin.setType(TerminType.SHUTDOWN);\n        arbitrateEventService.terminEvent().single(termin);\n        sleep(5 * 1000L);// 等待处理完所有的termin事件\n\n        // 关闭\n        select.destory(pipelineId);\n        extract.destory(pipelineId);\n        transform.destory(pipelineId);\n        load.destory(pipelineId);\n        view.destory(pipelineId);\n        this.termin.destory(pipelineId);\n        ArbitrateFactory.destory(pipelineId);\n    }\n}\n"
  },
  {
    "path": "shared/arbitrate/src/test/java/com/alibaba/otter/shared/arbitrate/demo/multi/ArbitrateForwardIntegration.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.demo.multi;\n\nimport java.io.IOException;\nimport java.util.Arrays;\n\nimport mockit.Mock;\nimport mockit.Mockit;\n\nimport org.testng.annotations.AfterMethod;\nimport org.testng.annotations.BeforeMethod;\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.shared.arbitrate.ArbitrateEventService;\nimport com.alibaba.otter.shared.arbitrate.BaseEventTest;\nimport com.alibaba.otter.shared.arbitrate.demo.servcie.ExtractServiceDemo;\nimport com.alibaba.otter.shared.arbitrate.demo.servcie.LoadServiceDemo;\nimport com.alibaba.otter.shared.arbitrate.demo.servcie.MainStemServiceDemo;\nimport com.alibaba.otter.shared.arbitrate.demo.servcie.ProcessViewDemo;\nimport com.alibaba.otter.shared.arbitrate.demo.servcie.SelectServiceDemo;\nimport com.alibaba.otter.shared.arbitrate.demo.servcie.TerminProcessDemo;\nimport com.alibaba.otter.shared.arbitrate.demo.servcie.TransformServiceDemo;\nimport com.alibaba.otter.shared.arbitrate.impl.ArbitrateConstants;\nimport com.alibaba.otter.shared.arbitrate.impl.communication.ArbitrateCommmunicationClient;\nimport com.alibaba.otter.shared.arbitrate.impl.config.ArbitrateConfigUtils;\nimport com.alibaba.otter.shared.arbitrate.impl.manage.ChannelArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.manage.NodeArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.manage.PipelineArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.ArbitrateFactory;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.helper.StagePathUtils;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.monitor.PermitMonitor;\nimport com.alibaba.otter.shared.arbitrate.model.TerminEventData;\nimport com.alibaba.otter.shared.arbitrate.model.TerminEventData.TerminType;\nimport com.alibaba.otter.shared.common.model.config.channel.Channel;\nimport com.alibaba.otter.shared.common.model.config.node.Node;\nimport com.alibaba.otter.shared.common.model.config.pipeline.Pipeline;\nimport com.alibaba.otter.shared.common.utils.zookeeper.ZkClientx;\nimport com.alibaba.otter.shared.communication.core.model.Event;\n\n/**\n * demo的启动入口\n * \n * @author jianghang 2011-9-22 下午03:58:53\n * @version 4.0.0\n */\npublic class ArbitrateForwardIntegration extends BaseEventTest {\n\n    private MainStemServiceDemo    mainStem;\n    private SelectServiceDemo      select;\n    private ExtractServiceDemo     extract;\n    private TransformServiceDemo   transform;\n    private LoadServiceDemo        load;\n    private ProcessViewDemo        view;\n    private TerminProcessDemo      termin;\n\n    // 环境数据准备对象\n    private NodeArbitrateEvent     nodeEvent;\n    private ChannelArbitrateEvent  channelEvent;\n    private PipelineArbitrateEvent pipelineEvent;\n    private ArbitrateEventService  arbitrateEventService;\n    private final Node             one                = new Node();\n    private final Node             two                = new Node();\n    private final Long             oneNid             = 1L;        // 第一台机器\n    private final Long             twoNid             = 2L;\n    private Long                   channelId          = 100L;\n    private Long                   pipelineId         = 100L;      // 注意是100L\n    private Long                   oppositePipelineId = 101L;\n    private ZkClientx              zookeeper;\n\n    @BeforeMethod\n    public void setUp() {\n        // mock 配置信息数据\n        Mockit.setUpMock(ArbitrateConfigUtils.class, new Object() {\n\n            @Mock\n            public Channel getChannelByChannelId(Long channelId) {\n                Channel channel = new Channel();\n                channel.setId(channelId);\n                Pipeline pipeline = new Pipeline();\n                pipeline.setId(pipelineId);\n                pipeline.setSelectNodes(Arrays.asList(one));\n                pipeline.setExtractNodes(Arrays.asList(one));\n                pipeline.setLoadNodes(Arrays.asList(two));\n                channel.setPipelines(Arrays.asList(pipeline));\n                return channel;\n            }\n\n            @Mock\n            public Pipeline getPipeline(Long pipelineId) {\n                Pipeline pipeline = new Pipeline();\n                pipeline.setId(pipelineId);\n                pipeline.setSelectNodes(Arrays.asList(one));\n                pipeline.setExtractNodes(Arrays.asList(one));\n                pipeline.setLoadNodes(Arrays.asList(two));\n                return pipeline;\n            }\n\n            @Mock\n            public Long getCurrentNid() {\n                return oneNid;\n            }\n\n            @Mock\n            public int getParallelism(Long pipelineId) {\n                return 3;// 并行度\n            }\n\n            @Mock\n            public Pipeline getOppositePipeline(Long pipelineId) {\n                Pipeline pipeline = new Pipeline();\n                pipeline.setId(oppositePipelineId);\n                pipeline.setSelectNodes(Arrays.asList(two));\n                pipeline.setExtractNodes(Arrays.asList(two));\n                pipeline.setLoadNodes(Arrays.asList(one));\n                return pipeline;\n            }\n\n            @Mock\n            public Channel getChannel(Long pipelineId) {\n                Channel channel = new Channel();\n                channel.setId(channelId);\n\n                Pipeline pipeline = new Pipeline();\n                pipeline.setId(pipelineId);\n\n                Pipeline oppositePipeline = new Pipeline();\n                oppositePipeline.setId(oppositePipelineId);\n                channel.setPipelines(Arrays.asList(pipeline, oppositePipeline));\n                return channel;\n            }\n\n        });\n\n        Mockit.setUpMock(ArbitrateCommmunicationClient.class, new Object() {\n\n            @Mock\n            public Object callManager(final Event event) {\n                // do nothing\n                return null;\n            }\n        });\n\n        zookeeper = getZookeeper();\n\n        one.setId(oneNid);\n        two.setId(twoNid);\n        nodeEvent = new NodeArbitrateEvent();\n        channelEvent = new ChannelArbitrateEvent();// 创建channel\n        pipelineEvent = new PipelineArbitrateEvent();\n\n        // 创建node节点\n        nodeEvent.init(oneNid);\n\n        // 创建pipeline节点\n        try {\n            channelEvent.init(channelId);\n        } catch (Exception e) {\n            // ignore\n        }\n\n        try {\n            pipelineEvent.init(channelId, pipelineId);\n        } catch (Exception e) {\n            // ignore\n        }\n\n        arbitrateEventService = (ArbitrateEventService) getBeanFactory().getBean(\"arbitrateEventService\");\n        mainStem = new MainStemServiceDemo();\n        autowire(mainStem);\n        select = new SelectServiceDemo();\n        autowire(select);\n        extract = new ExtractServiceDemo();\n        autowire(extract);\n        transform = new TransformServiceDemo();\n        autowire(transform);\n        load = new LoadServiceDemo();\n        autowire(load);\n        view = new ProcessViewDemo();\n        autowire(view);\n        termin = new TerminProcessDemo();\n        autowire(termin);\n    }\n\n    @AfterMethod\n    public void tearDown() {\n        // 删除mainStem节点\n        String path = StagePathUtils.getPipeline(pipelineId) + \"/\" + ArbitrateConstants.NODE_MAINSTEM;\n        zookeeper.delete(path);\n\n        nodeEvent.destory(one.getId());\n        // 关闭pipeline\n        pipelineEvent.destory(channelId, pipelineId);\n        channelEvent.destory(channelId);\n    }\n\n    @Test\n    public void testDemo() {\n        // 设置启动标志\n        channelEvent.start(channelId);\n        sleep(); // 停顿一下\n\n        // 优先启动主导线程\n        mainStem.submit(pipelineId);\n\n        PermitMonitor permit = ArbitrateFactory.getInstance(pipelineId, PermitMonitor.class);\n        try {\n            permit.waitForPermit();// 阻塞等待授权\n        } catch (InterruptedException e1) {\n            want.fail();\n        }\n\n        // 启动\n        select.submit(pipelineId);\n        extract.submit(pipelineId);\n        view.submit(pipelineId);\n        this.termin.submit(pipelineId);\n\n        transform.submit(oppositePipelineId);// 注意是反方向的\n        load.submit(oppositePipelineId);// 注意是反方向的\n\n        try {\n            System.in.read();\n        } catch (IOException e) {\n            e.printStackTrace();\n        }\n\n        // 发送结束事件\n        TerminEventData termin = new TerminEventData();\n        termin.setPipelineId(pipelineId);\n        termin.setType(TerminType.SHUTDOWN);\n        arbitrateEventService.terminEvent().single(termin);\n        sleep(5 * 1000L);// 等待处理完所有的termin事件\n\n        // 关闭\n        select.destory(pipelineId);\n        extract.destory(pipelineId);\n        view.destory(pipelineId);\n        this.termin.destory(pipelineId);\n\n        transform.destory(oppositePipelineId);\n        load.destory(oppositePipelineId);\n        ArbitrateFactory.destory(pipelineId);\n    }\n}\n"
  },
  {
    "path": "shared/arbitrate/src/test/java/com/alibaba/otter/shared/arbitrate/demo/multi/ArbitrateOppositeIntegration.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.demo.multi;\n\nimport java.io.IOException;\nimport java.util.Arrays;\n\nimport mockit.Mock;\nimport mockit.Mockit;\n\nimport org.testng.annotations.AfterMethod;\nimport org.testng.annotations.BeforeMethod;\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.shared.arbitrate.ArbitrateEventService;\nimport com.alibaba.otter.shared.arbitrate.BaseEventTest;\nimport com.alibaba.otter.shared.arbitrate.demo.servcie.ExtractServiceDemo;\nimport com.alibaba.otter.shared.arbitrate.demo.servcie.LoadServiceDemo;\nimport com.alibaba.otter.shared.arbitrate.demo.servcie.MainStemServiceDemo;\nimport com.alibaba.otter.shared.arbitrate.demo.servcie.ProcessViewDemo;\nimport com.alibaba.otter.shared.arbitrate.demo.servcie.SelectServiceDemo;\nimport com.alibaba.otter.shared.arbitrate.demo.servcie.TerminProcessDemo;\nimport com.alibaba.otter.shared.arbitrate.demo.servcie.TransformServiceDemo;\nimport com.alibaba.otter.shared.arbitrate.impl.ArbitrateConstants;\nimport com.alibaba.otter.shared.arbitrate.impl.communication.ArbitrateCommmunicationClient;\nimport com.alibaba.otter.shared.arbitrate.impl.config.ArbitrateConfigUtils;\nimport com.alibaba.otter.shared.arbitrate.impl.manage.ChannelArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.manage.NodeArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.manage.PipelineArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.ArbitrateFactory;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.helper.StagePathUtils;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.monitor.PermitMonitor;\nimport com.alibaba.otter.shared.arbitrate.model.TerminEventData;\nimport com.alibaba.otter.shared.arbitrate.model.TerminEventData.TerminType;\nimport com.alibaba.otter.shared.common.model.config.channel.Channel;\nimport com.alibaba.otter.shared.common.model.config.node.Node;\nimport com.alibaba.otter.shared.common.model.config.pipeline.Pipeline;\nimport com.alibaba.otter.shared.common.utils.zookeeper.ZkClientx;\nimport com.alibaba.otter.shared.communication.core.model.Event;\n\n/**\n * demo的启动入口\n * \n * @author jianghang 2011-9-22 下午03:58:53\n * @version 4.0.0\n */\npublic class ArbitrateOppositeIntegration extends BaseEventTest {\n\n    private MainStemServiceDemo    mainStem;\n    private SelectServiceDemo      select;\n    private ExtractServiceDemo     extract;\n    private TransformServiceDemo   transform;\n    private LoadServiceDemo        load;\n    private ProcessViewDemo        view;\n    private TerminProcessDemo      termin;\n\n    // 环境数据准备对象\n    private NodeArbitrateEvent     nodeEvent;\n    private ChannelArbitrateEvent  channelEvent;\n    private PipelineArbitrateEvent pipelineEvent;\n    private ArbitrateEventService  arbitrateEventService;\n    private final Node             one                = new Node();\n    private final Node             two                = new Node();\n    private final Long             oneNid             = 2L;\n    private final Long             twoNid             = 1L;        // 第一台机器\n    private Long                   channelId          = 100L;\n    private Long                   pipelineId         = 101L;      // 注意是101L，反方向的同步\n    private Long                   oppositePipelineId = 100L;\n    private ZkClientx              zookeeper;\n\n    @BeforeMethod\n    public void setUp() {\n        // mock 配置信息数据\n        Mockit.setUpMock(ArbitrateConfigUtils.class, new Object() {\n\n            @Mock\n            public Channel getChannelByChannelId(Long channelId) {\n                Channel channel = new Channel();\n                channel.setId(channelId);\n                Pipeline pipeline = new Pipeline();\n                pipeline.setId(pipelineId);\n                pipeline.setSelectNodes(Arrays.asList(one));\n                pipeline.setExtractNodes(Arrays.asList(one));\n                pipeline.setLoadNodes(Arrays.asList(two));\n                channel.setPipelines(Arrays.asList(pipeline));\n                return channel;\n            }\n\n            @Mock\n            public Pipeline getPipeline(Long pipelineId) {\n                Pipeline pipeline = new Pipeline();\n                pipeline.setId(pipelineId);\n                pipeline.setSelectNodes(Arrays.asList(one));\n                pipeline.setExtractNodes(Arrays.asList(one));\n                pipeline.setLoadNodes(Arrays.asList(two));\n                return pipeline;\n            }\n\n            @Mock\n            public Long getCurrentNid() {\n                return oneNid;\n            }\n\n            @Mock\n            public int getParallelism(Long pipelineId) {\n                return 3;// 并行度\n            }\n\n            @Mock\n            public Pipeline getOppositePipeline(Long pipelineId) {\n                Pipeline pipeline = new Pipeline();\n                pipeline.setId(oppositePipelineId);\n                pipeline.setSelectNodes(Arrays.asList(two));\n                pipeline.setExtractNodes(Arrays.asList(two));\n                pipeline.setLoadNodes(Arrays.asList(one));\n                return pipeline;\n            }\n\n            @Mock\n            public Channel getChannel(Long pipelineId) {\n                Channel channel = new Channel();\n                channel.setId(channelId);\n\n                Pipeline pipeline = new Pipeline();\n                pipeline.setId(pipelineId);\n\n                Pipeline oppositePipeline = new Pipeline();\n                oppositePipeline.setId(oppositePipelineId);\n                channel.setPipelines(Arrays.asList(pipeline, oppositePipeline));\n                return channel;\n            }\n\n        });\n\n        Mockit.setUpMock(ArbitrateCommmunicationClient.class, new Object() {\n\n            @Mock\n            public Object callManager(final Event event) {\n                // do nothing\n                return null;\n            }\n        });\n\n        zookeeper = getZookeeper();\n\n        one.setId(oneNid);\n        two.setId(twoNid);\n        nodeEvent = new NodeArbitrateEvent();\n        channelEvent = new ChannelArbitrateEvent();// 创建channel\n        pipelineEvent = new PipelineArbitrateEvent();\n\n        // 创建node节点\n        nodeEvent.init(one.getId());\n\n        // 创建pipeline节点\n        try {\n            channelEvent.init(channelId);\n        } catch (Exception e) {\n            // ignore\n        }\n\n        try {\n            pipelineEvent.init(channelId, pipelineId);\n        } catch (Exception e) {\n            // ignore\n        }\n\n        arbitrateEventService = (ArbitrateEventService) getBeanFactory().getBean(\"arbitrateEventService\");\n        mainStem = new MainStemServiceDemo();\n        autowire(mainStem);\n        select = new SelectServiceDemo();\n        autowire(select);\n        extract = new ExtractServiceDemo();\n        autowire(extract);\n        transform = new TransformServiceDemo();\n        autowire(transform);\n        load = new LoadServiceDemo();\n        autowire(load);\n        view = new ProcessViewDemo();\n        autowire(view);\n        termin = new TerminProcessDemo();\n        autowire(termin);\n    }\n\n    @AfterMethod\n    public void tearDown() {\n        // 删除mainStem节点\n        String path = StagePathUtils.getPipeline(pipelineId) + \"/\" + ArbitrateConstants.NODE_MAINSTEM;\n\n        zookeeper.delete(path);\n        nodeEvent.destory(one.getId());\n        // 关闭pipeline\n        pipelineEvent.destory(channelId, pipelineId);\n        // channelEvent.destory(channelId);\n    }\n\n    @Test\n    public void testDemo() {\n        // 设置启动标志\n        // channelEvent.start(channelId);\n        // sleep(); //停顿一下\n\n        // 优先启动主导线程\n        mainStem.submit(pipelineId);\n\n        PermitMonitor permit = ArbitrateFactory.getInstance(pipelineId, PermitMonitor.class);\n        try {\n            permit.waitForPermit();// 阻塞等待授权\n        } catch (InterruptedException e1) {\n            want.fail();\n        }\n\n        // 启动\n        select.submit(pipelineId);\n        extract.submit(pipelineId);\n        view.submit(pipelineId);\n        this.termin.submit(pipelineId);\n\n        transform.submit(oppositePipelineId);// 注意是反方向的\n        load.submit(oppositePipelineId);// 注意是反方向的\n\n        try {\n            System.in.read();\n        } catch (IOException e) {\n            e.printStackTrace();\n        }\n\n        // 发送结束事件\n        TerminEventData termin = new TerminEventData();\n        termin.setPipelineId(pipelineId);\n        termin.setType(TerminType.SHUTDOWN);\n        arbitrateEventService.terminEvent().single(termin);\n        sleep(5 * 1000L);// 等待处理完所有的termin事件\n\n        // 关闭\n        select.destory(pipelineId);\n        extract.destory(pipelineId);\n        view.destory(pipelineId);\n        this.termin.destory(pipelineId);\n\n        transform.destory(oppositePipelineId);\n        load.destory(oppositePipelineId);\n        ArbitrateFactory.destory(pipelineId);\n    }\n}\n"
  },
  {
    "path": "shared/arbitrate/src/test/java/com/alibaba/otter/shared/arbitrate/demo/multi/ChannelArbitrateEventIntegration.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.demo.multi;\n\nimport java.util.Arrays;\n\nimport mockit.Mock;\nimport mockit.Mockit;\n\nimport org.jtester.annotations.SpringBeanByName;\nimport org.testng.annotations.BeforeClass;\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.shared.arbitrate.BaseEventTest;\nimport com.alibaba.otter.shared.arbitrate.impl.config.ArbitrateConfigUtils;\nimport com.alibaba.otter.shared.arbitrate.impl.manage.ChannelArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.helper.StagePathUtils;\nimport com.alibaba.otter.shared.common.model.config.channel.Channel;\nimport com.alibaba.otter.shared.common.model.config.channel.ChannelStatus;\nimport com.alibaba.otter.shared.common.model.config.pipeline.Pipeline;\nimport com.alibaba.otter.shared.common.utils.JsonUtils;\nimport com.alibaba.otter.shared.common.utils.zookeeper.ZkClientx;\n\n/**\n * 集成测试\n * \n * @author jianghang 2011-10-8 下午06:25:52\n * @version 4.0.0\n */\npublic class ChannelArbitrateEventIntegration extends BaseEventTest {\n\n    @SpringBeanByName\n    private ChannelArbitrateEvent channelEvent;\n\n    private ZkClientx             zookeeper          = null;\n    private Long                  channelId          = 100L;\n    private Long                  pipelineId         = 100L;\n    private Long                  oppositePipelineId = 101L;\n\n    @BeforeClass\n    public void init() {\n        // 初始化节点\n        // mock 配置信息数据\n        Mockit.setUpMock(ArbitrateConfigUtils.class, new Object() {\n\n            @Mock\n            public Pipeline getPipeline(Long pipelineId) {\n                Pipeline pipeline = new Pipeline();\n                pipeline.setId(pipelineId);\n                return pipeline;\n            }\n\n            @Mock\n            public Channel getChannel(Long pipelineId) {\n                Channel channel = new Channel();\n                channel.setId(channelId);\n\n                Pipeline pipeline = new Pipeline();\n                pipeline.setId(pipelineId);\n\n                Pipeline oppositePipeline = new Pipeline();\n                oppositePipeline.setId(oppositePipelineId);\n                channel.setPipelines(Arrays.asList(pipeline, oppositePipeline));\n                return channel;\n            }\n\n            @Mock\n            public Channel getChannelByChannelId(Long channelId) {\n                Channel channel = new Channel();\n                channel.setId(channelId);\n\n                Pipeline pipeline = new Pipeline();\n                pipeline.setId(pipelineId);\n\n                Pipeline oppositePipeline = new Pipeline();\n                oppositePipeline.setId(oppositePipelineId);\n                channel.setPipelines(Arrays.asList(pipeline, oppositePipeline));\n                return channel;\n            }\n\n            @Mock\n            public Pipeline getOppositePipeline(Long pipelineId) {\n                Pipeline pipeline = new Pipeline();\n                pipeline.setId(pipelineId);\n                return pipeline;\n            }\n\n        });\n\n        zookeeper = getZookeeper();\n    }\n\n    @Test\n    public void test_aPause() {\n        // 启动\n        channelEvent.pause(channelId);\n        String path = StagePathUtils.getChannelByChannelId(channelId);\n        byte[] data = zookeeper.readData(path);\n        ChannelStatus status = JsonUtils.unmarshalFromByte(data, ChannelStatus.class);\n        want.bool(status == ChannelStatus.PAUSE).is(true);\n    }\n\n    // @Test\n    // public void test_aStop() {\n    // //启动\n    // channelEvent.stop(channelId);\n    // String path = PathUtils.getChannelByChannelId(channelId);\n    // byte[] data = null;\n    // try {\n    // data = zookeeper.getData(path, false, null);\n    // } catch (KeeperException e) {\n    // want.fail();\n    // } catch (InterruptedException e) {\n    // want.fail();\n    // }\n    // ChannelStatus status = JsonUtils.unmarshalFromByte(data,\n    // ChannelStatus.class);\n    // want.bool(status == ChannelStatus.STOP).is(true);\n    // }\n\n    @Test\n    public void test_bStart() {\n        // 启动\n        channelEvent.start(channelId);\n        String path = StagePathUtils.getChannelByChannelId(channelId);\n        byte[] data = zookeeper.readData(path);\n        ChannelStatus status = JsonUtils.unmarshalFromByte(data, ChannelStatus.class);\n        want.bool(status == ChannelStatus.START).is(true);\n    }\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/test/java/com/alibaba/otter/shared/arbitrate/demo/servcie/ExtractServiceDemo.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.demo.servcie;\n\nimport java.util.Map;\nimport java.util.concurrent.Callable;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.Future;\n\nimport org.apache.commons.lang.math.RandomUtils;\n\nimport com.alibaba.otter.shared.arbitrate.ArbitrateEventService;\nimport com.alibaba.otter.shared.arbitrate.model.EtlEventData;\nimport com.alibaba.otter.shared.common.utils.thread.NamedThreadFactory;\nimport com.google.common.collect.Maps;\n\n/**\n * extract的示例代码\n * \n * @author jianghang 2011-8-22 下午04:28:12\n */\npublic class ExtractServiceDemo implements PipelineLifeCycle {\n\n    private ArbitrateEventService arbitrateEventService;\n    private ExecutorService       executor = Executors.newCachedThreadPool(new NamedThreadFactory(\"ExtractService\"));\n    private Map<Long, Future>     threads  = Maps.newConcurrentMap();\n\n    public void submit(Long pipelineId) {\n        if (threads.containsKey(pipelineId)) {\n            throw new IllegalArgumentException(\"pipeline is dup!\");\n        }\n\n        Future future = executor.submit(new ExtractWorker(pipelineId));\n        threads.put(pipelineId, future);\n    }\n\n    public void destory(Long pipelineId) {\n        if (!threads.containsKey(pipelineId)) {\n            throw new IllegalArgumentException(\"pipeline is not exist!\");\n        }\n\n        Future future = threads.get(pipelineId);\n        future.cancel(true);\n    }\n\n    private class ExtractWorker implements Runnable {\n\n        private ExecutorService executor = Executors.newFixedThreadPool(10, new NamedThreadFactory(\"ExtractWorker\"));\n        private Long            pipelineId;\n\n        public ExtractWorker(Long pipelineId){\n            this.pipelineId = pipelineId;\n        }\n\n        public void run() {\n            while (true) {\n                try {\n                    // 注意single时需传递同一个eventData对象，里面带着对应的nextNid信息\n                    final EtlEventData eventData = arbitrateEventService.extractEvent().await(pipelineId);\n                    // 提交对应的任务工作任务\n                    executor.submit(new Callable() {\n\n                        @Override\n                        public Object call() throws Exception {\n                            Long nextNid = eventData.getNextNid();\n                            // 业务处理\n                            Thread.sleep(500 + RandomUtils.nextInt(2000)); // sleep一下代表处理业务\n\n                            if (isLocal(nextNid)) {// 判断是否本地\n                                // 调用本地的pipe工具进行数据处理\n                                eventData.setDesc(new Object());// 并设置相关信息\n                            } else {\n                                // 调用HTTP的pipe工具进行数据处理\n                                eventData.setDesc(new Object());// 并设置相关信息\n                            }\n\n                            // 处理完成，通知一下\n                            arbitrateEventService.extractEvent().single(eventData);\n                            return true;\n                        }\n\n                    });\n\n                } catch (InterruptedException e) {\n                    System.out.printf(\"Pipeline [%s] Select is Interrupted\", pipelineId);\n                    // 出现中断后就退出\n                    break;\n                } catch (Exception e) {\n                    System.out.printf(\"Pipeline [%s] Select is error\", pipelineId);\n                    e.printStackTrace();\n                }\n            }\n        }\n\n        private boolean isLocal(Long nid) {\n            // Long currentId = OtterClientServicesLocator.getConfigClientService().currentNode()\n            // .getId();\n            // return currentId.equals(nid);\n            return true;\n        }\n\n    }\n\n    public void setArbitrateEventService(ArbitrateEventService arbitrateEventService) {\n        this.arbitrateEventService = arbitrateEventService;\n    }\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/test/java/com/alibaba/otter/shared/arbitrate/demo/servcie/LoadServiceDemo.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.demo.servcie;\n\nimport java.util.Map;\nimport java.util.concurrent.Callable;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.Future;\n\nimport org.apache.commons.lang.math.RandomUtils;\n\nimport com.alibaba.otter.shared.arbitrate.ArbitrateEventService;\nimport com.alibaba.otter.shared.arbitrate.model.EtlEventData;\nimport com.alibaba.otter.shared.common.utils.thread.NamedThreadFactory;\nimport com.google.common.collect.Maps;\n\n/**\n * load的示例代码\n * \n * @author jianghang 2011-8-22 下午04:28:12\n */\npublic class LoadServiceDemo implements PipelineLifeCycle {\n\n    private ArbitrateEventService arbitrateEventService;\n    private ExecutorService       executor = Executors.newCachedThreadPool(new NamedThreadFactory(\"LoadService\"));\n    private Map<Long, Future>     threads  = Maps.newConcurrentMap();\n\n    public void submit(Long pipelineId) {\n        if (threads.containsKey(pipelineId)) {\n            throw new IllegalArgumentException(\"pipeline is dup!\");\n        }\n\n        Future future = executor.submit(new LoadWorker(pipelineId));\n        threads.put(pipelineId, future);\n    }\n\n    public void destory(Long pipelineId) {\n        if (!threads.containsKey(pipelineId)) {\n            throw new IllegalArgumentException(\"pipeline is not exist!\");\n        }\n\n        Future future = threads.get(pipelineId);\n        future.cancel(true);\n    }\n\n    private class LoadWorker implements Runnable {\n\n        private ExecutorService executor = Executors.newFixedThreadPool(10, new NamedThreadFactory(\"LoadWorker\"));\n        private Long            pipelineId;\n\n        public LoadWorker(Long pipelineId){\n            this.pipelineId = pipelineId;\n        }\n\n        public void run() {\n            while (true) {\n                try {\n                    // 注意single时需传递同一个eventData对象，里面带着对应的nextNid信息\n                    final EtlEventData eventData = arbitrateEventService.loadEvent().await(pipelineId);\n                    // 提交对应的任务工作任务\n                    executor.submit(new Callable() {\n\n                        @Override\n                        public Object call() throws Exception {\n                            Long nextNid = eventData.getNextNid();\n                            // 业务处理\n                            Thread.sleep(500 + RandomUtils.nextInt(2000)); // sleep一下代表处理业务\n\n                            if (isLocal(nextNid)) {// 判断是否本地\n                                // 调用本地的pipe工具进行数据处理\n                                eventData.setDesc(new Object());// 并设置相关信息\n                            } else {\n                                // 调用HTTP的pipe工具进行数据处理\n                                eventData.setDesc(new Object());// 并设置相关信息\n                            }\n\n                            // 处理完成，通知一下\n                            arbitrateEventService.loadEvent().single(eventData);\n                            return true;\n                        }\n\n                    });\n\n                } catch (InterruptedException e) {\n                    System.out.printf(\"Pipeline [%s] Select is Interrupted\", pipelineId);\n                    // 出现中断后就退出\n                    break;\n                } catch (Exception e) {\n                    System.out.printf(\"Pipeline [%s] Select is error\", pipelineId);\n                    e.printStackTrace();\n                }\n            }\n        }\n\n        private boolean isLocal(Long nid) {\n            // Long currentId = OtterClientServicesLocator.getConfigClientService().currentNode()\n            // .getId();\n            // return currentId.equals(nid);\n            return true;\n        }\n\n    }\n\n    public void setArbitrateEventService(ArbitrateEventService arbitrateEventService) {\n        this.arbitrateEventService = arbitrateEventService;\n    }\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/test/java/com/alibaba/otter/shared/arbitrate/demo/servcie/MainStemServiceDemo.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.demo.servcie;\n\nimport java.util.Map;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.Future;\n\nimport com.alibaba.otter.shared.arbitrate.ArbitrateEventService;\nimport com.alibaba.otter.shared.arbitrate.model.MainStemEventData;\nimport com.google.common.collect.Maps;\n\n/**\n * @author jianghang 2011-9-22 下午04:33:34\n * @version 4.0.0\n */\npublic class MainStemServiceDemo implements PipelineLifeCycle {\n\n    private ArbitrateEventService arbitrateEventService;\n    private ExecutorService       executor = Executors.newCachedThreadPool();\n    private Map<Long, Future>     threads  = Maps.newConcurrentMap();\n\n    public MainStemServiceDemo(){\n\n    }\n\n    public void submit(Long pipelineId) {\n        if (threads.containsKey(pipelineId)) {\n            throw new IllegalArgumentException(\"pipeline is dup!\");\n        }\n\n        Future future = executor.submit(new MainStemWorker(pipelineId));\n        threads.put(pipelineId, future);\n    }\n\n    public void destory(Long pipelineId) {\n        if (!threads.containsKey(pipelineId)) {\n            throw new IllegalArgumentException(\"pipeline is not exist!\");\n        }\n\n        Future future = threads.get(pipelineId);\n        future.cancel(true);\n    }\n\n    private class MainStemWorker implements Runnable {\n\n        private Long pipelineId;\n\n        public MainStemWorker(Long pipelineId){\n            this.pipelineId = pipelineId;\n        }\n\n        @Override\n        public void run() {\n            try {\n                arbitrateEventService.mainStemEvent().await(pipelineId);\n                Thread.sleep(1000L);\n                MainStemEventData eventData = new MainStemEventData();\n                eventData.setPipelineId(pipelineId);\n                eventData.setStatus(MainStemEventData.Status.OVERTAKE);\n                arbitrateEventService.mainStemEvent().single(eventData);\n                while (true) {\n                    // 处理eromanga的数据，这里通过sleep进行模拟\n                    Thread.sleep(10 * 1000);\n                }\n            } catch (InterruptedException e) {\n                // 退出\n            }\n        }\n    }\n\n    public void setArbitrateEventService(ArbitrateEventService arbitrateEventService) {\n        this.arbitrateEventService = arbitrateEventService;\n    }\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/test/java/com/alibaba/otter/shared/arbitrate/demo/servcie/PipelineLifeCycle.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.demo.servcie;\n\npublic interface PipelineLifeCycle {\n\n    public void submit(Long pipelineId);\n\n    public void destory(Long pipelineId);\n}\n"
  },
  {
    "path": "shared/arbitrate/src/test/java/com/alibaba/otter/shared/arbitrate/demo/servcie/ProcessViewDemo.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.demo.servcie;\n\nimport java.io.File;\nimport java.io.PrintWriter;\nimport java.text.SimpleDateFormat;\nimport java.util.Date;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.Future;\n\nimport org.apache.commons.lang.math.RandomUtils;\n\nimport com.alibaba.otter.shared.arbitrate.ArbitrateViewService;\nimport com.alibaba.otter.shared.common.model.statistics.stage.ProcessStat;\nimport com.alibaba.otter.shared.common.model.statistics.stage.StageStat;\nimport com.alibaba.otter.shared.common.utils.thread.NamedThreadFactory;\nimport com.google.common.collect.Maps;\n\npublic class ProcessViewDemo implements PipelineLifeCycle {\n\n    private ArbitrateViewService arbitrateViewService;\n    private ExecutorService      executor = Executors.newCachedThreadPool(new NamedThreadFactory(\"ProcessView\"));\n    private Map<Long, Future>    threads  = Maps.newConcurrentMap();\n\n    public void submit(Long pipelineId) {\n        if (threads.containsKey(pipelineId)) {\n            throw new IllegalArgumentException(\"pipeline is dup!\");\n        }\n\n        Future future = executor.submit(new ViewWorker(pipelineId));\n        threads.put(pipelineId, future);\n    }\n\n    public void destory(Long pipelineId) {\n        if (!threads.containsKey(pipelineId)) {\n            throw new IllegalArgumentException(\"pipeline is not exist!\");\n        }\n\n        Future future = threads.get(pipelineId);\n        future.cancel(true);\n    }\n\n    private class ViewWorker implements Runnable {\n\n        private Long pipelineId;\n\n        public ViewWorker(Long pipelineId){\n            this.pipelineId = pipelineId;\n        }\n\n        public void run() {\n            PrintWriter print = null;\n            try {\n                String tmp = System.getProperty(\"java.io.tmpdir\", \"/tmp\");\n                File log = new File(tmp + \"/process.log\");\n                if (log.exists() == false) {\n                    log.createNewFile();\n                }\n\n                print = new PrintWriter(log);\n                while (true) {\n                    if (Thread.currentThread().isInterrupted()) {\n                        break;\n                    }\n\n                    List<ProcessStat> processStats = arbitrateViewService.listProcesses(100L, pipelineId);\n                    StringBuilder builder = new StringBuilder();\n                    builder.append(\"=============================== time : \" + printDate(new Date().getTime())).append(\"\\n\");\n\n                    builder.append(\"pipeline : \").append(pipelineId).append(\"\\n\");\n                    for (ProcessStat processStat : processStats) {\n                        builder.append(\"\\t process : \").append(processStat.getProcessId()).append(\"\\n\");\n\n                        for (StageStat stageStat : processStat.getStageStats()) {\n                            builder.append(\"\\t\\t \").append(stageStat.getStage().name());\n                            builder.append(\"    ====> startTime [\").append(printDate(stageStat.getStartTime())).append(\" ]\");\n                            if (stageStat.getEndTime() != null) {\n                                builder.append(\" endTime [\").append(printDate(stageStat.getEndTime())).append(\" ]\");\n                            }\n                            builder.append(\"\\n\");\n                        }\n                    }\n\n                    print.println(builder.toString());\n                    print.flush();\n\n                    try {\n                        Thread.sleep(500 + RandomUtils.nextInt(1000));\n                    } catch (InterruptedException e) {\n                        break;\n                    }\n                }\n            } catch (Exception e1) {\n                e1.printStackTrace(print);\n            } finally {\n                print.flush();\n                print.close();\n            }\n        }\n\n        private String printDate(Long time) {\n            Date date = new Date(time);\n            return new SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss\").format(date);\n        }\n    }\n\n    public void setArbitrateViewService(ArbitrateViewService arbitrateViewService) {\n        this.arbitrateViewService = arbitrateViewService;\n    }\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/test/java/com/alibaba/otter/shared/arbitrate/demo/servcie/SelectServiceDemo.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.demo.servcie;\n\nimport java.util.Map;\nimport java.util.concurrent.Callable;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.Future;\n\nimport org.apache.commons.lang.math.RandomUtils;\n\nimport com.alibaba.otter.shared.arbitrate.ArbitrateEventService;\nimport com.alibaba.otter.shared.arbitrate.model.EtlEventData;\nimport com.alibaba.otter.shared.common.utils.thread.NamedThreadFactory;\nimport com.google.common.collect.Maps;\n\n/**\n * select的示例代码\n * \n * @author jianghang 2011-8-22 下午04:28:12\n */\npublic class SelectServiceDemo implements PipelineLifeCycle {\n\n    private ArbitrateEventService arbitrateEventService;\n\n    private ExecutorService       executor = Executors.newCachedThreadPool(new NamedThreadFactory(\"SelectService\"));\n    private Map<Long, Future>     threads  = Maps.newConcurrentMap();\n\n    public void submit(Long pipelineId) {\n        if (threads.containsKey(pipelineId)) {\n            throw new IllegalArgumentException(\"pipeline is dup!\");\n        }\n\n        Future future = executor.submit(new SelectWorker(pipelineId));\n        threads.put(pipelineId, future);\n    }\n\n    public void destory(Long pipelineId) {\n        if (!threads.containsKey(pipelineId)) {\n            throw new IllegalArgumentException(\"pipeline is not exist!\");\n        }\n\n        Future future = threads.get(pipelineId);\n        future.cancel(true);\n    }\n\n    private class SelectWorker implements Runnable {\n\n        private ExecutorService executor = Executors.newFixedThreadPool(10, new NamedThreadFactory(\"SelectWorker\"));\n\n        private Long            pipelineId;\n\n        public SelectWorker(Long pipelineId){\n            this.pipelineId = pipelineId;\n        }\n\n        public void run() {\n            while (true) {\n                try {\n                    // 注意single时需传递同一个eventData对象，里面带着对应的nextNid信息\n                    final EtlEventData eventData = arbitrateEventService.selectEvent().await(pipelineId);\n                    // 提交对应的任务工作任务\n                    executor.submit(new Callable() {\n\n                        @Override\n                        public Object call() throws Exception {\n                            Long nextNid = eventData.getNextNid();\n                            // 业务处理\n                            Thread.sleep(500 + RandomUtils.nextInt(2000)); // sleep一下代表处理业务\n\n                            if (isLocal(nextNid)) {// 判断是否本地\n                                // 调用本地的pipe工具进行数据处理\n                                eventData.setDesc(new Object());// 并设置相关信息\n                            } else {\n                                // 调用HTTP的pipe工具进行数据处理\n                                eventData.setDesc(new Object());// 并设置相关信息\n                            }\n\n                            // 处理完成，通知一下\n                            arbitrateEventService.selectEvent().single(eventData);\n                            return true;\n                        }\n\n                    });\n\n                } catch (InterruptedException e) {\n                    System.out.printf(\"Pipeline [%s] Select is Interrupted\", pipelineId);\n                    // 出现中断后就退出\n                    break;\n                } catch (Exception e) {\n                    System.out.printf(\"Pipeline [%s] Select is error\", pipelineId);\n                    e.printStackTrace();\n                }\n            }\n        }\n\n        private boolean isLocal(Long nid) {\n            // Long currentId = OtterClientServicesLocator.getConfigClientService().currentNode()\n            // .getId();\n            // return currentId.equals(nid);\n            return true;\n        }\n\n    }\n\n    public void setArbitrateEventService(ArbitrateEventService arbitrateEventService) {\n        this.arbitrateEventService = arbitrateEventService;\n    }\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/test/java/com/alibaba/otter/shared/arbitrate/demo/servcie/TerminProcessDemo.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.demo.servcie;\n\nimport java.io.File;\nimport java.io.PrintWriter;\nimport java.text.SimpleDateFormat;\nimport java.util.Date;\nimport java.util.Map;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.Future;\n\nimport com.alibaba.otter.shared.arbitrate.ArbitrateEventService;\nimport com.alibaba.otter.shared.arbitrate.model.TerminEventData;\nimport com.alibaba.otter.shared.arbitrate.model.TerminEventData.TerminType;\nimport com.alibaba.otter.shared.common.utils.thread.NamedThreadFactory;\nimport com.google.common.collect.Maps;\n\n/**\n * select的示例代码\n * \n * @author jianghang 2011-8-22 下午04:28:12\n */\npublic class TerminProcessDemo implements PipelineLifeCycle {\n\n    private ArbitrateEventService arbitrateEventService;\n    private ExecutorService       executor = Executors.newCachedThreadPool(new NamedThreadFactory(\"TerminProcess\"));\n    private Map<Long, Future>     threads  = Maps.newConcurrentMap();\n\n    public void submit(Long pipelineId) {\n        if (threads.containsKey(pipelineId)) {\n            throw new IllegalArgumentException(\"pipeline is dup!\");\n        }\n\n        Future future = executor.submit(new TermintWorker(pipelineId));\n        threads.put(pipelineId, future);\n    }\n\n    public void destory(Long pipelineId) {\n        if (!threads.containsKey(pipelineId)) {\n            throw new IllegalArgumentException(\"pipeline is not exist!\");\n        }\n\n        Future future = threads.get(pipelineId);\n        future.cancel(true);\n    }\n\n    private class TermintWorker implements Runnable {\n\n        private Long pipelineId;\n\n        public TermintWorker(Long pipelineId){\n            this.pipelineId = pipelineId;\n        }\n\n        public void run() {\n            PrintWriter print = null;\n            try {\n                String tmp = System.getProperty(\"java.io.tmpdir\", \"/tmp\");\n                File log = new File(tmp + \"/termin.log\");\n                if (log.exists() == false) {\n                    log.createNewFile();\n                }\n\n                print = new PrintWriter(log);\n                while (true) {\n                    try {\n                        final TerminEventData eventData = arbitrateEventService.terminEvent().await(pipelineId);\n                        // 可以使用异步处理\n                        TerminType terminType = eventData.getType();\n                        if (terminType.isNormal()) {\n                            // 更新游标，判断是否发送ack给eromanga信息\n                        } else {\n                            // 出现异常了，rollback对应的数据\n                        }\n\n                        arbitrateEventService.terminEvent().ack(eventData);// 处理完了，通知下仲裁器\n                        StringBuilder builder = new StringBuilder();\n                        builder.append(\"=============================== time : \" + printDate(new Date().getTime())).append(\"\\n\");\n\n                        builder.append(\"pipeline : \").append(pipelineId).append(\"\\n\");\n                        builder.append(\"\\t termin : \").append(eventData.getProcessId()).append(\" type : \").append(eventData.getType().name()).append(\"\\n\");\n                        print.println(builder);\n                        print.flush();\n                    } catch (InterruptedException e) {\n                        break;\n                    }\n\n                }\n            } catch (Exception e) {\n                e.printStackTrace(print);\n            } finally {\n                print.flush();\n                print.close();\n            }\n        }\n\n        private String printDate(Long time) {\n            Date date = new Date(time);\n            return new SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss\").format(date);\n        }\n    }\n\n    public void setArbitrateEventService(ArbitrateEventService arbitrateEventService) {\n        this.arbitrateEventService = arbitrateEventService;\n    }\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/test/java/com/alibaba/otter/shared/arbitrate/demo/servcie/TransformServiceDemo.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.demo.servcie;\n\nimport java.util.Map;\nimport java.util.concurrent.Callable;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.Future;\n\nimport org.apache.commons.lang.math.RandomUtils;\n\nimport com.alibaba.otter.shared.arbitrate.ArbitrateEventService;\nimport com.alibaba.otter.shared.arbitrate.model.EtlEventData;\nimport com.alibaba.otter.shared.common.utils.thread.NamedThreadFactory;\nimport com.google.common.collect.Maps;\n\n/**\n * transform的示例代码\n * \n * @author jianghang 2011-8-22 下午04:28:12\n */\npublic class TransformServiceDemo implements PipelineLifeCycle {\n\n    private ArbitrateEventService arbitrateEventService;\n    private ExecutorService       executor = Executors.newCachedThreadPool(new NamedThreadFactory(\"transformService\"));\n    private Map<Long, Future>     threads  = Maps.newConcurrentMap();\n\n    public void submit(Long pipelineId) {\n        if (threads.containsKey(pipelineId)) {\n            throw new IllegalArgumentException(\"pipeline is dup!\");\n        }\n\n        Future future = executor.submit(new transformWorker(pipelineId));\n        threads.put(pipelineId, future);\n    }\n\n    public void destory(Long pipelineId) {\n        if (!threads.containsKey(pipelineId)) {\n            throw new IllegalArgumentException(\"pipeline is not exist!\");\n        }\n\n        Future future = threads.get(pipelineId);\n        future.cancel(true);\n    }\n\n    private class transformWorker implements Runnable {\n\n        private ExecutorService executor = Executors.newFixedThreadPool(10, new NamedThreadFactory(\"transformWorker\"));\n        private Long            pipelineId;\n\n        public transformWorker(Long pipelineId){\n            this.pipelineId = pipelineId;\n        }\n\n        public void run() {\n            while (true) {\n                try {\n                    // 注意single时需传递同一个eventData对象，里面带着对应的nextNid信息\n                    final EtlEventData eventData = arbitrateEventService.transformEvent().await(pipelineId);\n                    // 提交对应的任务工作任务\n                    executor.submit(new Callable() {\n\n                        @Override\n                        public Object call() throws Exception {\n                            Long nextNid = eventData.getNextNid();\n                            // 业务处理\n                            Thread.sleep(500 + RandomUtils.nextInt(2000)); // sleep一下代表处理业务\n\n                            if (isLocal(nextNid)) {// 判断是否本地\n                                // 调用本地的pipe工具进行数据处理\n                                eventData.setDesc(new Object());// 并设置相关信息\n                            } else {\n                                // 调用HTTP的pipe工具进行数据处理\n                                eventData.setDesc(new Object());// 并设置相关信息\n                            }\n\n                            // 处理完成，通知一下\n                            arbitrateEventService.transformEvent().single(eventData);\n                            return true;\n                        }\n\n                    });\n\n                } catch (InterruptedException e) {\n                    System.out.printf(\"Pipeline [%s] transform is Interrupted\", pipelineId);\n                    // 出现中断后就退出\n                    break;\n                } catch (Exception e) {\n                    System.out.printf(\"Pipeline [%s] Select is error\", pipelineId);\n                    e.printStackTrace();\n                }\n            }\n        }\n\n        private boolean isLocal(Long nid) {\n            // Long currentId = OtterClientServicesLocator.getConfigClientService().currentNode()\n            // .getId();\n            // return currentId.equals(nid);\n            return true;\n        }\n\n    }\n\n    public void setArbitrateEventService(ArbitrateEventService arbitrateEventService) {\n        this.arbitrateEventService = arbitrateEventService;\n    }\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/test/java/com/alibaba/otter/shared/arbitrate/manage/ArbitrateViewServiceTest.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.manage;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.jtester.annotations.SpringBeanByName;\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.shared.arbitrate.ArbitrateViewService;\nimport com.alibaba.otter.shared.arbitrate.impl.ArbitrateConstants;\nimport com.alibaba.otter.shared.arbitrate.setl.BaseStageTest;\nimport com.alibaba.otter.shared.common.model.statistics.stage.ProcessStat;\n\n/**\n * @author jianghang 2011-9-28 下午04:43:02\n * @version 4.0.0\n */\npublic class ArbitrateViewServiceTest extends BaseStageTest {\n\n    @SpringBeanByName\n    private ArbitrateViewService arbitrateViewService;\n\n    @Test\n    public void test_listProcesses() {\n        final List<Long> initProcessIds = new ArrayList<Long>();\n        final Map<Long, List<String>> stages = new HashMap<Long, List<String>>();\n        try {\n            Long p1 = initProcess();\n            initStage(p1, ArbitrateConstants.NODE_SELECTED);\n            initStage(p1, ArbitrateConstants.NODE_EXTRACTED);\n            initStage(p1, ArbitrateConstants.NODE_TRANSFORMED);\n\n            Long p2 = initProcess();\n            initStage(p2, ArbitrateConstants.NODE_SELECTED);\n            initStage(p2, ArbitrateConstants.NODE_EXTRACTED);\n\n            Long p3 = initProcess();\n            initStage(p3, ArbitrateConstants.NODE_SELECTED);\n\n            Long p4 = initProcess();\n\n            initProcessIds.add(p1);\n            initProcessIds.add(p2);\n            initProcessIds.add(p3);\n            initProcessIds.add(p4);\n            List<String> p1Stages = Arrays.asList(ArbitrateConstants.NODE_SELECTED, ArbitrateConstants.NODE_EXTRACTED,\n                                                  ArbitrateConstants.NODE_TRANSFORMED);\n            stages.put(p1, p1Stages);\n\n            List<String> p2Stages = Arrays.asList(ArbitrateConstants.NODE_SELECTED, ArbitrateConstants.NODE_EXTRACTED);\n            stages.put(p2, p2Stages);\n\n            List<String> p3Stages = Arrays.asList(ArbitrateConstants.NODE_SELECTED);\n            stages.put(p3, p3Stages);\n\n            List<String> p4Stages = new ArrayList<String>();\n            stages.put(p4, p4Stages);\n\n            List<ProcessStat> processStat = arbitrateViewService.listProcesses(channelId, pipelineId);\n            System.out.println(processStat);\n        } finally {\n            for (Long processId : initProcessIds) {\n                List<String> ss = stages.get(processId);\n                for (String stage : ss) {\n                    destoryStage(processId, stage);\n                }\n                destoryProcess(processId);\n            }\n        }\n\n    }\n}\n"
  },
  {
    "path": "shared/arbitrate/src/test/java/com/alibaba/otter/shared/arbitrate/manage/ChannelArbitrateEventTest.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.manage;\n\nimport mockit.Mock;\nimport mockit.Mockit;\n\nimport org.testng.annotations.BeforeClass;\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.shared.arbitrate.BaseEventTest;\nimport com.alibaba.otter.shared.arbitrate.impl.config.ArbitrateConfigUtils;\nimport com.alibaba.otter.shared.arbitrate.impl.manage.ChannelArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.helper.StagePathUtils;\nimport com.alibaba.otter.shared.common.model.config.channel.Channel;\nimport com.alibaba.otter.shared.common.model.config.channel.ChannelStatus;\nimport com.alibaba.otter.shared.common.model.config.pipeline.Pipeline;\nimport com.alibaba.otter.shared.common.utils.JsonUtils;\nimport com.alibaba.otter.shared.common.utils.zookeeper.ZkClientx;\n\n/**\n * 测试下channel\n * \n * @author jianghang 2011-9-19 下午01:04:15\n * @version 4.0.0\n */\npublic class ChannelArbitrateEventTest extends BaseEventTest {\n\n    private ZkClientx             zookeeper = null;\n    private ChannelArbitrateEvent channelEvent;\n    private Long                  channelId = 100L;\n\n    @BeforeClass\n    public void init() {\n        // 初始化节点\n        // mock 配置信息数据\n        Mockit.setUpMock(ArbitrateConfigUtils.class, new Object() {\n\n            @Mock\n            public Channel getChannel(Long pipelineId) {\n                Channel channel = new Channel();\n                channel.setId(channelId);\n                return channel;\n            }\n\n            @Mock\n            public Pipeline getOppositePipeline(Long pipelineId) {\n                Pipeline pipeline = new Pipeline();\n                pipeline.setId(pipelineId);\n                return pipeline;\n            }\n\n        });\n\n        zookeeper = getZookeeper();\n        channelEvent = new ChannelArbitrateEvent();\n    }\n\n    @Test\n    public void testInit() {\n        channelEvent.init(channelId);\n        String path = StagePathUtils.getChannelByChannelId(channelId);\n        byte[] data = zookeeper.readData(path);\n        ChannelStatus status = JsonUtils.unmarshalFromByte(data, ChannelStatus.class);\n        want.bool(status == ChannelStatus.STOP).is(true);\n        channelEvent.destory(channelId);\n    }\n\n    @Test\n    public void testDestory() {\n        channelEvent.init(channelId);\n        channelEvent.destory(channelId);\n        String path = StagePathUtils.getChannelByChannelId(channelId);\n        want.bool(zookeeper.exists(path)).is(false);\n    }\n\n    @Test\n    public void testStart() {\n        // 初始化\n        channelEvent.init(channelId);\n        // 启动\n        channelEvent.start(channelId);\n        String path = StagePathUtils.getChannelByChannelId(channelId);\n        byte[] data = zookeeper.readData(path);\n        ChannelStatus status = JsonUtils.unmarshalFromByte(data, ChannelStatus.class);\n        want.bool(status == ChannelStatus.START).is(true);\n        channelEvent.destory(channelId);\n    }\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/test/java/com/alibaba/otter/shared/arbitrate/manage/NodeArbitrateEventTest.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.manage;\n\nimport org.testng.annotations.BeforeMethod;\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.shared.arbitrate.BaseEventTest;\nimport com.alibaba.otter.shared.arbitrate.impl.manage.NodeArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.helper.StagePathUtils;\nimport com.alibaba.otter.shared.common.utils.zookeeper.ZkClientx;\n\n/**\n * 测试下Node manage\n * \n * @author jianghang 2011-9-19 上午11:37:12\n * @version 4.0.0\n */\npublic class NodeArbitrateEventTest extends BaseEventTest {\n\n    private ZkClientx          zookeeper = null;\n    private NodeArbitrateEvent nodeEvent;\n\n    @BeforeMethod\n    public void setUp() {\n        zookeeper = getZookeeper();\n        nodeEvent = new NodeArbitrateEvent();\n    }\n\n    @Test\n    public void testDestory() {\n        nodeEvent.init(100L);\n        nodeEvent.destory(100L);\n\n        String path = StagePathUtils.getNode(100L);\n        want.bool(zookeeper.exists(path)).is(false);\n    }\n\n    // @Test\n    // public void testInit_immediate() {\n    // nodeEvent.init(1L);\n    //\n    // final CountDownLatch count = new CountDownLatch(1);\n    // ExecutorService executor = Executors.newCachedThreadPool();\n    //\n    // nodeEvent.setZookeeper(getZookeeper());// 创建新的链接\n    // executor.submit(new Callable() {\n    //\n    // public Object call() throws Exception {\n    // nodeEvent.awaitForNotExist(1L);\n    // count.countDown();\n    // return null;\n    // }\n    // });\n    //\n    // try {\n    // zookeeper.close();//老链接进行关闭, awaitForNotExist就会被唤醒\n    // } catch (InterruptedException e) {\n    // want.fail();\n    // }\n    //\n    // try {\n    // count.await();\n    // executor.shutdown();\n    // } catch (InterruptedException e) {\n    // want.fail();\n    // }\n    //\n    // }\n    //\n    // @Test\n    // public void testInit_sleep() {\n    // nodeEvent.init(1L);\n    //\n    // final CountDownLatch count = new CountDownLatch(1);\n    // ExecutorService executor = Executors.newCachedThreadPool();\n    //\n    // nodeEvent.setZookeeper(getZookeeper());// 创建新的链接\n    // executor.submit(new Callable() {\n    //\n    // public Object call() throws Exception {\n    // nodeEvent.awaitForNotExist(1L);\n    // System.out.println(\"node close\");\n    // count.countDown();\n    // return null;\n    // }\n    // });\n    //\n    // try {\n    // Thread.sleep(1000); // sleep一下，等待awaitForNotExist进入watcher事件\n    // } catch (InterruptedException e) {\n    // want.fail();\n    // }\n    //\n    // try {\n    // zookeeper.close();//老链接进行关闭, awaitForNotExist就会被唤醒\n    // } catch (InterruptedException e) {\n    // want.fail();\n    // }\n    //\n    // try {\n    // count.await();\n    // } catch (InterruptedException e) {\n    // want.fail();\n    // }\n    //\n    // }\n}\n"
  },
  {
    "path": "shared/arbitrate/src/test/java/com/alibaba/otter/shared/arbitrate/manage/PipelineArbitrateEventTest.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.manage;\n\nimport mockit.Mock;\nimport mockit.Mockit;\n\nimport org.testng.annotations.BeforeClass;\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.shared.arbitrate.BaseEventTest;\nimport com.alibaba.otter.shared.arbitrate.impl.config.ArbitrateConfigUtils;\nimport com.alibaba.otter.shared.arbitrate.impl.manage.ChannelArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.manage.PipelineArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.helper.StagePathUtils;\nimport com.alibaba.otter.shared.common.model.config.channel.Channel;\nimport com.alibaba.otter.shared.common.model.config.pipeline.Pipeline;\nimport com.alibaba.otter.shared.common.utils.zookeeper.ZkClientx;\n\n/**\n * 测试下pipeline\n * \n * @author jianghang 2011-9-19 下午01:04:15\n * @version 4.0.0\n */\npublic class PipelineArbitrateEventTest extends BaseEventTest {\n\n    private ZkClientx              zookeeper  = null;\n    private ChannelArbitrateEvent  channelEvent;\n    private PipelineArbitrateEvent pipelineEvent;\n    private Long                   channelId  = 100L;\n    private Long                   pipelineId = 100L;\n\n    @BeforeClass\n    public void init() {\n        // 初始化节点\n        // mock 配置信息数据\n        Mockit.setUpMock(ArbitrateConfigUtils.class, new Object() {\n\n            @Mock\n            public Channel getChannel(Long pipelineId) {\n                Channel channel = new Channel();\n                channel.setId(channelId);\n                return channel;\n            }\n\n            @Mock\n            public Pipeline getOppositePipeline(Long pipelineId) {\n                Pipeline pipeline = new Pipeline();\n                pipeline.setId(pipelineId);\n                return pipeline;\n            }\n\n        });\n\n        zookeeper = getZookeeper();\n        channelEvent = new ChannelArbitrateEvent();\n        pipelineEvent = new PipelineArbitrateEvent();\n    }\n\n    @Test\n    public void testInit() {\n        channelEvent.init(channelId);// 一定要先初始化channel\n        channelEvent.start(channelId);\n        pipelineEvent.init(channelId, pipelineId);// 初始化piepline\n        String path = StagePathUtils.getPipeline(channelId, pipelineId);\n        want.bool(zookeeper.exists(path)).is(true);\n\n        pipelineEvent.destory(channelId, pipelineId);\n        channelEvent.destory(channelId);\n    }\n\n    @Test\n    public void testDestory() {\n        channelEvent.init(channelId);// 一定要先初始化channel\n        pipelineEvent.init(channelId, pipelineId);// 初始化piepline\n        pipelineEvent.destory(channelId, pipelineId);\n        channelEvent.destory(channelId);\n\n        String path = StagePathUtils.getPipeline(channelId, pipelineId);\n        want.bool(zookeeper.exists(path)).is(false);\n    }\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/test/java/com/alibaba/otter/shared/arbitrate/setl/BaseStageTest.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.setl;\n\nimport java.util.Arrays;\nimport java.util.Map;\n\nimport mockit.Mock;\nimport mockit.Mockit;\n\nimport org.apache.commons.lang.StringUtils;\nimport org.apache.zookeeper.CreateMode;\nimport org.testng.annotations.AfterClass;\nimport org.testng.annotations.AfterMethod;\nimport org.testng.annotations.BeforeClass;\nimport org.testng.annotations.BeforeMethod;\n\nimport com.alibaba.otter.shared.arbitrate.BaseEventTest;\nimport com.alibaba.otter.shared.arbitrate.impl.ArbitrateConstants;\nimport com.alibaba.otter.shared.arbitrate.impl.communication.ArbitrateCommmunicationClient;\nimport com.alibaba.otter.shared.arbitrate.impl.config.ArbitrateConfigUtils;\nimport com.alibaba.otter.shared.arbitrate.impl.manage.ChannelArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.manage.NodeArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.manage.PipelineArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.ArbitrateFactory;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.helper.StagePathUtils;\nimport com.alibaba.otter.shared.arbitrate.model.EtlEventData;\nimport com.alibaba.otter.shared.arbitrate.model.MainStemEventData;\nimport com.alibaba.otter.shared.arbitrate.model.TerminEventData;\nimport com.alibaba.otter.shared.arbitrate.model.TerminEventData.TerminType;\nimport com.alibaba.otter.shared.common.model.config.channel.Channel;\nimport com.alibaba.otter.shared.common.model.config.node.Node;\nimport com.alibaba.otter.shared.common.model.config.node.NodeStatus;\nimport com.alibaba.otter.shared.common.model.config.pipeline.Pipeline;\nimport com.alibaba.otter.shared.common.utils.JsonUtils;\nimport com.alibaba.otter.shared.common.utils.TestUtils;\nimport com.alibaba.otter.shared.common.utils.zookeeper.ZkClientx;\nimport com.alibaba.otter.shared.communication.core.model.Callback;\nimport com.alibaba.otter.shared.communication.core.model.Event;\n\n/**\n * 测试基类\n * \n * @author jianghang 2011-9-21 下午08:21:21\n * @version 4.0.0\n */\npublic class BaseStageTest extends BaseEventTest {\n\n    protected ZkClientx              zookeeper  = null;\n    // 环境数据准备对象\n    protected NodeArbitrateEvent     nodeEvent;\n    protected ChannelArbitrateEvent  channelEvent;\n    protected PipelineArbitrateEvent pipelineEvent;\n    protected final Node             local      = new Node();\n    protected final Long             nid        = 1L;\n    protected Long                   channelId  = 100L;\n    protected Long                   pipelineId = 100L;\n    protected String                 pipelinePath;\n    protected String                 processPath;\n\n    @BeforeClass\n    public void init() {\n        // 初始化节点\n        // mock 配置信息数据\n        local.setStatus(NodeStatus.START);\n        Mockit.setUpMock(ArbitrateConfigUtils.class, new Object() {\n\n            @Mock\n            public Channel getChannelByChannelId(Long channelId) {\n                Channel channel = new Channel();\n                channel.setId(channelId);\n                Pipeline pipeline = new Pipeline();\n                pipeline.setId(pipelineId);\n                pipeline.setSelectNodes(Arrays.asList(local));\n                pipeline.setExtractNodes(Arrays.asList(local));\n                pipeline.setLoadNodes(Arrays.asList(local));\n                channel.setPipelines(Arrays.asList(pipeline));\n                return channel;\n            }\n\n            @Mock\n            public Channel getChannel(Long pipelineId) {\n                Channel channel = new Channel();\n                channel.setId(channelId);\n                Pipeline pipeline = new Pipeline();\n                pipeline.setId(pipelineId);\n                pipeline.setSelectNodes(Arrays.asList(local));\n                pipeline.setExtractNodes(Arrays.asList(local));\n                pipeline.setLoadNodes(Arrays.asList(local));\n                channel.setPipelines(Arrays.asList(pipeline));\n                return channel;\n            }\n\n            @Mock\n            public Pipeline getPipeline(Long pipelineId) {\n                Pipeline pipeline = new Pipeline();\n                pipeline.setSelectNodes(Arrays.asList(local));\n                pipeline.setExtractNodes(Arrays.asList(local));\n                pipeline.setLoadNodes(Arrays.asList(local));\n                return pipeline;\n            }\n\n            @Mock\n            public Pipeline getOppositePipeline(Long pipelineId) {\n                return null;// 没有反向同步\n            }\n\n            @Mock\n            public Long getCurrentNid() {\n                return nid;\n            }\n\n            @Mock\n            public int getParallelism(Long pipelineId) {\n                return 3;// 并行度\n            }\n\n        });\n\n        Mockit.setUpMock(ArbitrateCommmunicationClient.class, new Object() {\n\n            @Mock\n            public Object callManager(final Event event) {\n                // do nothing\n                return null;\n            }\n\n            @Mock\n            public void callManager(final Event event, final Callback callback) {\n                // do nothing\n            }\n        });\n\n        zookeeper = getZookeeper();\n        local.setId(nid);\n        nodeEvent = new NodeArbitrateEvent();\n        channelEvent = new ChannelArbitrateEvent();\n        pipelineEvent = new PipelineArbitrateEvent();\n\n        pipelinePath = StagePathUtils.getPipeline(channelId, pipelineId);\n        processPath = StagePathUtils.getProcessRoot(channelId, pipelineId);\n        channelEvent.init(channelId);\n        pipelineEvent.init(channelId, pipelineId);\n        channelEvent.start(channelId);\n\n        String path = pipelinePath + \"/\" + ArbitrateConstants.NODE_MAINSTEM;\n        MainStemEventData eventData = new MainStemEventData();\n        eventData.setStatus(MainStemEventData.Status.OVERTAKE);\n        eventData.setNid(nid);\n        byte[] bytes = JsonUtils.marshalToByte(eventData);// 初始化的数据对象\n\n        zookeeper.create(path, bytes, CreateMode.EPHEMERAL);\n    }\n\n    @AfterClass\n    public void destory() {\n        ArbitrateFactory.destory(pipelineId);\n        // 删除mainStem节点\n        String path = pipelinePath + \"/\" + ArbitrateConstants.NODE_MAINSTEM;\n\n        zookeeper.delete(path);\n\n        pipelineEvent.destory(channelId, pipelineId);\n        channelEvent.destory(channelId);\n    }\n\n    @BeforeMethod\n    public void setUp() {\n        try {\n            Map cache = (Map) TestUtils.getField(new ArbitrateFactory(), \"cache\");\n            cache.clear();\n        } catch (Exception e) {\n            want.fail();\n        }\n    }\n\n    @AfterMethod\n    public void dispose() {\n        ArbitrateFactory.destory(pipelineId);\n    }\n\n    protected EtlEventData getData(Long nid) {\n        EtlEventData data = new EtlEventData();\n        data.setNextNid(nid);\n        return data;\n    }\n\n    // ================================ helper method ================================\n\n    // 模拟创建一个process\n    protected Long initProcess() {\n        String path = zookeeper.create(processPath + \"/\", new byte[0], CreateMode.PERSISTENT_SEQUENTIAL);\n\n        // 创建为顺序的节点\n        String processNode = StringUtils.substringAfterLast(path, \"/\");\n        return StagePathUtils.getProcessId(processNode);// 添加到当前的process列表\n    }\n\n    // 模拟销毁一个process\n    protected void destoryProcess(Long processId) {\n        String path = processPath + \"/\" + StagePathUtils.getProcessNode(processId);\n        zookeeper.delete(path);\n    }\n\n    // 模拟创建一个stage节点\n    protected void initStage(Long processId, String stageNode) {\n        String path = processPath + \"/\" + StagePathUtils.getProcessNode(processId) + \"/\" + stageNode;\n        zookeeper.create(path, new byte[0], CreateMode.PERSISTENT);\n    }\n\n    // 模拟销毁一个stage节点\n    protected void destoryStage(Long processId, String stageNode) {\n        String path = processPath + \"/\" + StagePathUtils.getProcessNode(processId) + \"/\" + stageNode;\n\n        zookeeper.delete(path);\n    }\n\n    // 模拟创建一个带数据的stage节点\n    protected void initStage(Long processId, String stageNode, EtlEventData event) {\n        String path = processPath + \"/\" + StagePathUtils.getProcessNode(processId) + \"/\" + stageNode;\n\n        byte[] datas = JsonUtils.marshalToByte(event);\n        zookeeper.create(path, datas, CreateMode.PERSISTENT);\n    }\n\n    // 模拟创建一个终结节点\n    protected void initTermin(Long processId) {\n        TerminEventData data = new TerminEventData();\n        data.setPipelineId(pipelineId);\n        data.setProcessId(processId);\n        data.setType(TerminType.NORMAL);\n\n        byte[] bytes = JsonUtils.marshalToByte(data);\n        zookeeper.create(StagePathUtils.getTermin(pipelineId, processId), bytes, CreateMode.PERSISTENT);\n\n    }\n\n    // 模拟删除一个终结节点\n    protected void destoryTermin(Long processId) {\n        zookeeper.delete(StagePathUtils.getTermin(pipelineId, processId));\n\n    }\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/test/java/com/alibaba/otter/shared/arbitrate/setl/event/BaseArbitrateEventTest.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.setl.event;\n\nimport org.testng.annotations.AfterMethod;\nimport org.testng.annotations.BeforeMethod;\n\nimport com.alibaba.otter.shared.arbitrate.impl.setl.MainStemArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.setl.BaseStageTest;\n\n/**\n * 提供mainStem信号的初始化\n * \n * @author jianghang 2011-9-22 下午05:30:08\n * @version 4.0.0\n */\npublic class BaseArbitrateEventTest extends BaseStageTest {\n\n    protected MainStemArbitrateEvent mainStemEvent;\n\n    @BeforeMethod\n    public void setUp() {\n        nodeEvent.init(nid);\n        channelEvent.start(channelId);//启动\n    }\n\n    @AfterMethod\n    public void tearDown() {\n        nodeEvent.destory(local.getId());\n    }\n}\n"
  },
  {
    "path": "shared/arbitrate/src/test/java/com/alibaba/otter/shared/arbitrate/setl/event/MainStemArbitrateEventTest.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.setl.event;\n\nimport java.util.Arrays;\n\nimport mockit.Mock;\nimport mockit.Mockit;\n\nimport org.testng.annotations.AfterClass;\nimport org.testng.annotations.BeforeClass;\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.shared.arbitrate.BaseEventTest;\nimport com.alibaba.otter.shared.arbitrate.impl.config.ArbitrateConfigUtils;\nimport com.alibaba.otter.shared.arbitrate.impl.manage.ChannelArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.manage.NodeArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.manage.PipelineArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.ArbitrateFactory;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.MainStemArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.monitor.PermitMonitor;\nimport com.alibaba.otter.shared.arbitrate.model.MainStemEventData;\nimport com.alibaba.otter.shared.common.model.config.channel.Channel;\nimport com.alibaba.otter.shared.common.model.config.node.Node;\nimport com.alibaba.otter.shared.common.model.config.pipeline.Pipeline;\n\n/**\n * 测试下mainstem信号\n * \n * @author jianghang 2011-9-22 下午04:58:45\n * @version 4.0.0\n */\npublic class MainStemArbitrateEventTest extends BaseEventTest {\n\n    private MainStemArbitrateEvent mainStemEvent;\n    private ChannelArbitrateEvent  channelEvent;\n    private PipelineArbitrateEvent pipelineEvent;\n    private final Node             local      = new Node();\n    private final Long             nid        = 1L;\n    private Long                   channelId  = 100L;\n    private Long                   pipelineId = 100L;\n    private NodeArbitrateEvent     nodeEvent;\n\n    @BeforeClass\n    public void init() {\n        // 初始化节点\n        // mock 配置信息数据\n        Mockit.setUpMock(ArbitrateConfigUtils.class, new Object() {\n\n            @Mock\n            public Channel getChannel(Long pipelineId) {\n                Channel channel = new Channel();\n                channel.setId(channelId);\n                return channel;\n            }\n\n            @Mock\n            public Pipeline getOppositePipeline(Long pipelineId) {\n                return null;// 没有反向同步\n            }\n\n            @Mock\n            public int getParallelism(Long pipelineId) {\n                return 3;// 并行度\n            }\n\n            @Mock\n            public Pipeline getPipeline(Long pipelineId) {\n                Pipeline pipeline = new Pipeline();\n                pipeline.setId(pipelineId);\n                pipeline.setSelectNodes(Arrays.asList(local));\n                pipeline.setExtractNodes(Arrays.asList(local));\n                pipeline.setLoadNodes(Arrays.asList(local));\n                return pipeline;\n            }\n\n            @Mock\n            public Long getCurrentNid() {\n                return nid;\n            }\n\n        });\n\n        getZookeeper();\n        local.setId(nid);\n        nodeEvent = new NodeArbitrateEvent();\n        channelEvent = new ChannelArbitrateEvent();\n        pipelineEvent = new PipelineArbitrateEvent();\n\n        nodeEvent.init(nid);\n        channelEvent.init(channelId);\n        pipelineEvent.init(channelId, pipelineId);\n        channelEvent.start(channelId);\n    }\n\n    @Test\n    public void test_mainStem() {\n        channelEvent.start(channelId);// 启动\n        mainStemEvent = new MainStemArbitrateEvent();\n        try {\n            mainStemEvent.await(pipelineId);\n\n            MainStemEventData eventData = new MainStemEventData();\n            eventData.setPipelineId(pipelineId);\n            eventData.setStatus(MainStemEventData.Status.OVERTAKE);\n            mainStemEvent.single(eventData);\n\n            PermitMonitor permit = ArbitrateFactory.getInstance(pipelineId, PermitMonitor.class);\n            permit.waitForPermit();// 阻塞等待授权\n        } catch (InterruptedException e) {\n            want.fail();\n        }\n\n        boolean check = mainStemEvent.check(pipelineId);\n        want.bool(check).is(true);\n\n        // 删除mainStem节点\n        mainStemEvent.release(pipelineId);\n    }\n\n    @AfterClass\n    public void destory() {\n        nodeEvent.destory(nid);\n        pipelineEvent.destory(channelId, pipelineId);\n        channelEvent.destory(channelId);\n    }\n}\n"
  },
  {
    "path": "shared/arbitrate/src/test/java/com/alibaba/otter/shared/arbitrate/setl/event/ToolArbitrateEventTest.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.setl.event;\n\nimport java.util.Date;\nimport java.util.List;\n\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.shared.arbitrate.impl.setl.ToolArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.model.RemedyIndexEventData;\nimport com.alibaba.otter.shared.arbitrate.model.SyncStatusEventData;\nimport com.alibaba.otter.shared.arbitrate.model.SyncStatusEventData.SyncStatus;\n\npublic class ToolArbitrateEventTest extends BaseArbitrateEventTest {\n\n    private ToolArbitrateEvent toolEvent;\n\n    @Test\n    public void test_simple() {\n        toolEvent = new ToolArbitrateEvent();\n        SyncStatusEventData eventData = toolEvent.fetch(pipelineId);\n        eventData.setPipelineId(pipelineId);\n        eventData.addStatus(new SyncStatus(false, 1000));\n        eventData.addStatus(new SyncStatus(true, 1001));\n        toolEvent.single(eventData);\n\n        eventData = toolEvent.fetch(pipelineId);\n        want.object(eventData).notNull();\n    }\n\n    @Test\n    public void test_remedy() {\n        toolEvent = new ToolArbitrateEvent();\n\n        RemedyIndexEventData data = new RemedyIndexEventData();\n        Date now = new Date();\n        Long start = now.getTime() - 24 * 3600 * 60;\n\n        Long startProcessId = 100L;\n        for (int i = 10; i >= 1; i--) {\n            data.setPipelineId(pipelineId);\n            data.setProcessId(startProcessId + i);\n            data.setStartTime(start + startProcessId + i);\n            data.setEndTime(start + startProcessId + 2 * i);\n            toolEvent.addRemedyIndex(data);\n        }\n\n        List<RemedyIndexEventData> indexs = toolEvent.listRemedyIndexs(pipelineId);\n        want.collection(indexs).sizeEq(10);\n\n        for (RemedyIndexEventData index : indexs) {\n            toolEvent.removeRemedyIndex(index);\n        }\n\n        indexs = toolEvent.listRemedyIndexs(pipelineId);\n        want.collection(indexs).sizeEq(0);\n    }\n}\n"
  },
  {
    "path": "shared/arbitrate/src/test/java/com/alibaba/otter/shared/arbitrate/setl/event/memory/MemoryArbitrateEventTest.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.setl.event.memory;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport mockit.Mock;\nimport mockit.Mockit;\n\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.shared.arbitrate.impl.config.ArbitrateConfigUtils;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.ArbitrateFactory;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.memory.ExtractMemoryArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.memory.LoadMemoryArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.memory.SelectMemoryArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.memory.TerminMemoryArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.memory.TransformMemoryArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.model.EtlEventData;\nimport com.alibaba.otter.shared.arbitrate.model.TerminEventData;\nimport com.alibaba.otter.shared.arbitrate.setl.event.BaseArbitrateEventTest;\n\n/**\n * 基于memory的调度测试\n * \n * @author jianghang 2012-9-28 上午11:42:29\n * @version 4.1.0\n */\npublic class MemoryArbitrateEventTest extends BaseArbitrateEventTest {\n\n    private SelectMemoryArbitrateEvent    selectEvent;\n    private ExtractMemoryArbitrateEvent   extractEvent;\n    private TransformMemoryArbitrateEvent transformEvent;\n    private LoadMemoryArbitrateEvent      loadEvent;\n    private TerminMemoryArbitrateEvent    terminEvent;\n\n    @Test\n    public void test_all() {\n        Mockit.setUpMock(ArbitrateConfigUtils.class, new Object() {\n\n            @Mock\n            public int getParallelism(Long pipelineId) {\n                return 2;// 并行度\n            }\n\n        });\n\n        selectEvent = new SelectMemoryArbitrateEvent();\n        extractEvent = new ExtractMemoryArbitrateEvent();\n        transformEvent = new TransformMemoryArbitrateEvent();\n        loadEvent = new LoadMemoryArbitrateEvent();\n        terminEvent = (TerminMemoryArbitrateEvent) this.getBeanFactory().getBean(\"terminMemoryEvent\");\n        loadEvent.setTerminEvent(terminEvent);\n\n        final List<Long> initProcessIds = new ArrayList<Long>();\n        try {\n            // 获取数据\n\n            // select stage\n            EtlEventData sdata1 = selectEvent.await(pipelineId);\n            EtlEventData sdata2 = selectEvent.await(pipelineId);\n\n            initProcessIds.add(sdata1.getProcessId());\n            initProcessIds.add(sdata2.getProcessId());\n\n            selectEvent.single(sdata1);\n            selectEvent.single(sdata2);\n            // extract stage\n            EtlEventData edata1 = extractEvent.await(pipelineId);\n            EtlEventData edata2 = extractEvent.await(pipelineId);\n\n            extractEvent.single(edata1);\n            extractEvent.single(edata2);\n\n            // transform stage\n            EtlEventData tdata1 = transformEvent.await(pipelineId);\n            EtlEventData tdata2 = transformEvent.await(pipelineId);\n\n            transformEvent.single(tdata1);\n            transformEvent.single(tdata2);\n\n            // load stage\n            EtlEventData ldata1 = loadEvent.await(pipelineId);\n            loadEvent.single(ldata1);\n            Long p1 = ldata1.getProcessId();\n\n            TerminEventData terminData1 = new TerminEventData();\n            terminData1.setPipelineId(pipelineId);\n            terminData1.setProcessId(p1);\n            terminEvent.ack(terminData1);// 发送ack信号，删除termin节点\n\n            EtlEventData ldata2 = loadEvent.await(pipelineId);\n            want.bool(ldata1.getProcessId() < ldata2.getProcessId()).is(true);\n            loadEvent.single(ldata2);\n\n            Long p2 = ldata2.getProcessId();\n            TerminEventData terminData2 = new TerminEventData();\n            terminData2.setPipelineId(pipelineId);\n            terminData2.setProcessId(p2);\n            terminEvent.ack(terminData2);// 发送ack信号，删除termin节点\n\n            sleep(2000L);\n            ArbitrateFactory.destory(pipelineId);\n        } catch (InterruptedException e) {\n            want.fail();\n        } finally {\n        }\n    }\n}\n"
  },
  {
    "path": "shared/arbitrate/src/test/java/com/alibaba/otter/shared/arbitrate/setl/event/memory/TerminMemoryArbitrateEventTest.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.setl.event.memory;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport mockit.Mock;\nimport mockit.Mockit;\n\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.shared.arbitrate.impl.config.ArbitrateConfigUtils;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.ArbitrateFactory;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.helper.StagePathUtils;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.memory.ExtractMemoryArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.memory.LoadMemoryArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.memory.SelectMemoryArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.memory.TerminMemoryArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.memory.TransformMemoryArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.monitor.PermitMonitor;\nimport com.alibaba.otter.shared.arbitrate.model.EtlEventData;\nimport com.alibaba.otter.shared.arbitrate.model.TerminEventData;\nimport com.alibaba.otter.shared.arbitrate.model.TerminEventData.TerminType;\nimport com.alibaba.otter.shared.arbitrate.setl.event.BaseArbitrateEventTest;\n\n/**\n * @author jianghang 2011-9-27 下午09:45:23\n * @version 4.0.0\n */\npublic class TerminMemoryArbitrateEventTest extends BaseArbitrateEventTest {\n\n    private SelectMemoryArbitrateEvent    selectEvent;\n    private ExtractMemoryArbitrateEvent   extractEvent;\n    private TransformMemoryArbitrateEvent transformEvent;\n    private LoadMemoryArbitrateEvent      loadEvent;\n    private TerminMemoryArbitrateEvent    terminEvent;\n\n    @Test\n    public void test_Rollback() {\n        normalProcess();\n        // 发送rollback信号\n        TerminEventData rollback = new TerminEventData();\n        rollback.setPipelineId(pipelineId);\n        rollback.setType(TerminType.ROLLBACK);\n        terminEvent.single(rollback);\n\n        PermitMonitor monitor = ArbitrateFactory.getInstance(pipelineId, PermitMonitor.class);\n        want.bool(monitor.getChannelPermit(true).isPause()).is(true);\n\n        destoryTermin();\n        ArbitrateFactory.destory(pipelineId);\n    }\n\n    @Test\n    public void test_Shutdown() {\n        normalProcess();\n        // 发送shutdown信号\n        TerminEventData shutdown = new TerminEventData();\n        shutdown.setPipelineId(pipelineId);\n        shutdown.setType(TerminType.SHUTDOWN);\n        terminEvent.single(shutdown);\n\n        PermitMonitor monitor = ArbitrateFactory.getInstance(pipelineId, PermitMonitor.class);\n        want.bool(monitor.getChannelPermit(true).isStop()).is(true);\n\n        destoryTermin();\n        ArbitrateFactory.destory(pipelineId);\n    }\n\n    @Test\n    public void test_Restart() {\n        normalProcess();\n        // 发送restart信号\n        TerminEventData rollback = new TerminEventData();\n        rollback.setPipelineId(pipelineId);\n        rollback.setType(TerminType.RESTART);\n        terminEvent.single(rollback);\n\n        sleep(4000L);\n        PermitMonitor monitor = ArbitrateFactory.getInstance(pipelineId, PermitMonitor.class);\n        want.bool(monitor.getChannelPermit(true).isStart()).is(true);\n        sleep(4000L);\n\n        // 发送shutdown信号\n        TerminEventData shutdown = new TerminEventData();\n        shutdown.setPipelineId(pipelineId);\n        shutdown.setType(TerminType.SHUTDOWN);\n        terminEvent.single(shutdown);\n\n        want.bool(monitor.getChannelPermit(true).isStop()).is(true);\n\n        // 删除对应的错误节点\n        destoryTermin();\n        ArbitrateFactory.destory(pipelineId);\n    }\n\n    private void destoryTermin() {\n        String path = StagePathUtils.getTerminRoot(pipelineId);\n        List<String> terminNodes = zookeeper.getChildren(path);\n        for (String node : terminNodes) {\n            // 删除对应的错误节点\n            TerminEventData termin = new TerminEventData();\n            termin.setPipelineId(pipelineId);\n            termin.setProcessId(StagePathUtils.getProcessId(node));\n            System.out.println(\"remove termin node: \" + path + \"/\" + node);\n            terminEvent.ack(termin);// 发送ack信号，删除termin节点\n        }\n    }\n\n    private void normalProcess() {\n        Mockit.setUpMock(ArbitrateConfigUtils.class, new Object() {\n\n            @Mock\n            public int getParallelism(Long pipelineId) {\n                return 2;// 并行度\n            }\n\n            @Mock\n            public Long getCurrentNid() {\n                return nid;\n            }\n\n        });\n\n        selectEvent = new SelectMemoryArbitrateEvent();\n        extractEvent = new ExtractMemoryArbitrateEvent();\n        transformEvent = new TransformMemoryArbitrateEvent();\n        loadEvent = new LoadMemoryArbitrateEvent();\n        terminEvent = (TerminMemoryArbitrateEvent) this.getBeanFactory().getBean(\"terminMemoryEvent\");\n        loadEvent.setTerminEvent(terminEvent);\n\n        final List<Long> initProcessIds = new ArrayList<Long>();\n        try {\n            // 获取数据\n\n            // select stage\n            EtlEventData sdata1 = selectEvent.await(pipelineId);\n            EtlEventData sdata2 = selectEvent.await(pipelineId);\n\n            initProcessIds.add(sdata1.getProcessId());\n            initProcessIds.add(sdata2.getProcessId());\n\n            selectEvent.single(sdata1);\n            selectEvent.single(sdata2);\n\n            // extract stage\n            EtlEventData edata1 = extractEvent.await(pipelineId);\n            EtlEventData edata2 = extractEvent.await(pipelineId);\n\n            extractEvent.single(edata1);\n            extractEvent.single(edata2);\n\n            // transform stage\n            EtlEventData tdata1 = transformEvent.await(pipelineId);\n            EtlEventData tdata2 = transformEvent.await(pipelineId);\n\n            transformEvent.single(tdata1);\n            transformEvent.single(tdata2);\n\n            // SelectStageListener selectStageListener =\n            // ArbitrateFactory.getInstance(pipelineId,\n            // SelectStageListener.class);\n            // selectStageListener.destory();\n            // load stage\n            EtlEventData ldata1 = loadEvent.await(pipelineId);\n            loadEvent.single(ldata1);\n            Long p1 = ldata1.getProcessId();\n\n            TerminEventData terminData1 = new TerminEventData();\n            terminData1.setPipelineId(pipelineId);\n            terminData1.setProcessId(p1);\n            terminEvent.ack(terminData1);// 发送ack信号，删除termin节点\n\n        } catch (InterruptedException e) {\n            want.fail();\n        }\n\n    }\n}\n"
  },
  {
    "path": "shared/arbitrate/src/test/java/com/alibaba/otter/shared/arbitrate/setl/event/rpc/RpcArbitrateEventTest.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.setl.event.rpc;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport mockit.Mock;\nimport mockit.Mockit;\n\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.shared.arbitrate.impl.config.ArbitrateConfigUtils;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.ArbitrateFactory;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.rpc.ExtractRpcArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.rpc.LoadRpcArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.rpc.RpcStageEventDispatcher;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.rpc.SelectRpcArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.rpc.TerminRpcArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.rpc.TransformRpcArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.model.EtlEventData;\nimport com.alibaba.otter.shared.arbitrate.model.TerminEventData;\nimport com.alibaba.otter.shared.arbitrate.setl.event.BaseArbitrateEventTest;\n\n/**\n * 基于Rpc的调度测试\n * \n * @author jianghang 2012-9-28 上午11:42:29\n * @version 4.1.0\n */\npublic class RpcArbitrateEventTest extends BaseArbitrateEventTest {\n\n    private SelectRpcArbitrateEvent    selectEvent;\n    private ExtractRpcArbitrateEvent   extractEvent;\n    private TransformRpcArbitrateEvent transformEvent;\n    private LoadRpcArbitrateEvent      loadEvent;\n    private TerminRpcArbitrateEvent    terminEvent;\n\n    @Test\n    public void test_all() {\n        Mockit.setUpMock(ArbitrateConfigUtils.class, new Object() {\n\n            @Mock\n            public int getParallelism(Long pipelineId) {\n                return 2;// 并行度\n            }\n\n        });\n\n        RpcStageEventDispatcher rpcStageEventDispatcher = (RpcStageEventDispatcher) this.getBeanFactory().getBean(\"rpcStageEventDispatcher\");\n        terminEvent = (TerminRpcArbitrateEvent) this.getBeanFactory().getBean(\"terminRpcEvent\");\n\n        selectEvent = new SelectRpcArbitrateEvent();\n        selectEvent.setRpcStageEventDispatcher(rpcStageEventDispatcher);\n\n        extractEvent = new ExtractRpcArbitrateEvent();\n        extractEvent.setRpcStageEventDispatcher(rpcStageEventDispatcher);\n\n        transformEvent = new TransformRpcArbitrateEvent();\n        transformEvent.setRpcStageEventDispatcher(rpcStageEventDispatcher);\n\n        loadEvent = new LoadRpcArbitrateEvent();\n        loadEvent.setRpcStageEventDispatcher(rpcStageEventDispatcher);\n        loadEvent.setTerminEvent(terminEvent);\n\n        final List<Long> initProcessIds = new ArrayList<Long>();\n        try {\n            // 获取数据\n\n            // select stage\n            EtlEventData sdata1 = selectEvent.await(pipelineId);\n            EtlEventData sdata2 = selectEvent.await(pipelineId);\n\n            initProcessIds.add(sdata1.getProcessId());\n            initProcessIds.add(sdata2.getProcessId());\n\n            selectEvent.single(sdata1);\n            selectEvent.single(sdata2);\n            // extract stage\n            EtlEventData edata1 = extractEvent.await(pipelineId);\n            EtlEventData edata2 = extractEvent.await(pipelineId);\n\n            extractEvent.single(edata1);\n            extractEvent.single(edata2);\n\n            // transform stage\n            EtlEventData tdata1 = transformEvent.await(pipelineId);\n            EtlEventData tdata2 = transformEvent.await(pipelineId);\n\n            transformEvent.single(tdata1);\n            transformEvent.single(tdata2);\n\n            // load stage\n            EtlEventData ldata1 = loadEvent.await(pipelineId);\n            loadEvent.single(ldata1);\n            Long p1 = ldata1.getProcessId();\n\n            TerminEventData terminData1 = new TerminEventData();\n            terminData1.setPipelineId(pipelineId);\n            terminData1.setProcessId(p1);\n            terminEvent.ack(terminData1);// 发送ack信号，删除termin节点\n\n            EtlEventData ldata2 = loadEvent.await(pipelineId);\n            want.bool(ldata1.getProcessId() < ldata2.getProcessId()).is(true);\n            loadEvent.single(ldata2);\n\n            Long p2 = ldata2.getProcessId();\n            TerminEventData terminData2 = new TerminEventData();\n            terminData2.setPipelineId(pipelineId);\n            terminData2.setProcessId(p2);\n            terminEvent.ack(terminData2);// 发送ack信号，删除termin节点\n\n            sleep(2000L);\n            ArbitrateFactory.destory(pipelineId);\n        } catch (InterruptedException e) {\n            want.fail();\n        } finally {\n        }\n    }\n}\n"
  },
  {
    "path": "shared/arbitrate/src/test/java/com/alibaba/otter/shared/arbitrate/setl/event/zookeeper/ExtractArbitrateEventTest.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.setl.event.zookeeper;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport mockit.Mock;\nimport mockit.Mockit;\n\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.shared.arbitrate.impl.ArbitrateConstants;\nimport com.alibaba.otter.shared.arbitrate.impl.config.ArbitrateConfigUtils;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.ArbitrateFactory;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.zookeeper.ExtractZooKeeperArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.zookeeper.SelectZooKeeperArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.model.EtlEventData;\nimport com.alibaba.otter.shared.arbitrate.setl.event.BaseArbitrateEventTest;\n\n/**\n * @author jianghang 2011-9-23 下午02:58:13\n * @version 4.0.0\n */\npublic class ExtractArbitrateEventTest extends BaseArbitrateEventTest {\n\n    private SelectZooKeeperArbitrateEvent  selectEvent;\n    private ExtractZooKeeperArbitrateEvent extractEvent;\n\n    @Test\n    public void test_extract() {\n        Mockit.setUpMock(ArbitrateConfigUtils.class, new Object() {\n\n            @Mock\n            public int getParallelism(Long pipelineId) {\n                return 2;//并行度\n            }\n\n        });\n        selectEvent = new SelectZooKeeperArbitrateEvent();\n        extractEvent = new ExtractZooKeeperArbitrateEvent();\n\n        final List<Long> initProcessIds = new ArrayList<Long>();\n        try {\n            //获取数据\n\n            //select stage\n            EtlEventData sdata1 = selectEvent.await(pipelineId);\n            EtlEventData sdata2 = selectEvent.await(pipelineId);\n\n            initProcessIds.add(sdata1.getProcessId());\n            initProcessIds.add(sdata2.getProcessId());\n\n            selectEvent.single(sdata1);\n            selectEvent.single(sdata2);\n            // extract stage\n            EtlEventData edata1 = extractEvent.await(pipelineId);\n            EtlEventData edata2 = extractEvent.await(pipelineId);\n\n            extractEvent.single(edata1);\n            extractEvent.single(edata2);\n            ArbitrateFactory.destory(pipelineId);\n        } catch (InterruptedException e) {\n            want.fail();\n        } finally {\n            for (Long processId : initProcessIds) {\n                destoryStage(processId, ArbitrateConstants.NODE_SELECTED);\n                destoryStage(processId, ArbitrateConstants.NODE_EXTRACTED);\n                destoryProcess(processId);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "shared/arbitrate/src/test/java/com/alibaba/otter/shared/arbitrate/setl/event/zookeeper/LoadArbitrateEventTest.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.setl.event.zookeeper;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport mockit.Mock;\nimport mockit.Mockit;\n\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.shared.arbitrate.impl.config.ArbitrateConfigUtils;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.ArbitrateFactory;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.zookeeper.ExtractZooKeeperArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.zookeeper.LoadZooKeeperArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.zookeeper.SelectZooKeeperArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.zookeeper.TerminZooKeeperArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.zookeeper.TransformZooKeeperArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.zookeeper.monitor.SelectStageListener;\nimport com.alibaba.otter.shared.arbitrate.model.EtlEventData;\nimport com.alibaba.otter.shared.arbitrate.model.TerminEventData;\nimport com.alibaba.otter.shared.arbitrate.setl.event.BaseArbitrateEventTest;\n\n/**\n * @author jianghang 2011-9-23 下午02:58:13\n * @version 4.0.0\n */\npublic class LoadArbitrateEventTest extends BaseArbitrateEventTest {\n\n    private SelectZooKeeperArbitrateEvent    selectEvent;\n    private ExtractZooKeeperArbitrateEvent   extractEvent;\n    private TransformZooKeeperArbitrateEvent transformEvent;\n    private LoadZooKeeperArbitrateEvent      loadEvent;\n    private TerminZooKeeperArbitrateEvent    terminEvent;\n\n    @Test\n    public void test_load() {\n        Mockit.setUpMock(ArbitrateConfigUtils.class, new Object() {\n\n            @Mock\n            public int getParallelism(Long pipelineId) {\n                return 2;// 并行度\n            }\n\n        });\n\n        selectEvent = new SelectZooKeeperArbitrateEvent();\n        extractEvent = new ExtractZooKeeperArbitrateEvent();\n        transformEvent = new TransformZooKeeperArbitrateEvent();\n        loadEvent = new LoadZooKeeperArbitrateEvent();\n        terminEvent = (TerminZooKeeperArbitrateEvent) this.getBeanFactory().getBean(\"terminZooKeeperEvent\");\n        loadEvent.setTerminEvent(terminEvent);\n\n        final List<Long> initProcessIds = new ArrayList<Long>();\n        try {\n            // 获取数据\n\n            // select stage\n            EtlEventData sdata1 = selectEvent.await(pipelineId);\n            EtlEventData sdata2 = selectEvent.await(pipelineId);\n\n            initProcessIds.add(sdata1.getProcessId());\n            initProcessIds.add(sdata2.getProcessId());\n\n            selectEvent.single(sdata1);\n            selectEvent.single(sdata2);\n            // extract stage\n            EtlEventData edata1 = extractEvent.await(pipelineId);\n            EtlEventData edata2 = extractEvent.await(pipelineId);\n\n            extractEvent.single(edata1);\n            extractEvent.single(edata2);\n\n            // transform stage\n            EtlEventData tdata1 = transformEvent.await(pipelineId);\n            EtlEventData tdata2 = transformEvent.await(pipelineId);\n\n            transformEvent.single(tdata1);\n            transformEvent.single(tdata2);\n\n            SelectStageListener selectStageListener = ArbitrateFactory.getInstance(pipelineId,\n                                                                                   SelectStageListener.class);\n            selectStageListener.destory();\n            // load stage\n            EtlEventData ldata1 = loadEvent.await(pipelineId);\n            loadEvent.single(ldata1);\n            Long p1 = ldata1.getProcessId();\n\n            TerminEventData terminData1 = new TerminEventData();\n            terminData1.setPipelineId(pipelineId);\n            terminData1.setProcessId(p1);\n            terminEvent.ack(terminData1);// 发送ack信号，删除termin节点\n\n            EtlEventData ldata2 = loadEvent.await(pipelineId);\n            want.bool(ldata1.getProcessId() < ldata2.getProcessId()).is(true);\n            loadEvent.single(ldata2);\n\n            Long p2 = ldata2.getProcessId();\n            TerminEventData terminData2 = new TerminEventData();\n            terminData2.setPipelineId(pipelineId);\n            terminData2.setProcessId(p2);\n            terminEvent.ack(terminData2);// 发送ack信号，删除termin节点\n\n            sleep(2000L);\n            ArbitrateFactory.destory(pipelineId);\n        } catch (InterruptedException e) {\n            want.fail();\n        } finally {\n        }\n    }\n}\n"
  },
  {
    "path": "shared/arbitrate/src/test/java/com/alibaba/otter/shared/arbitrate/setl/event/zookeeper/SelectArbitrateEventTest.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.setl.event.zookeeper;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport mockit.Mock;\nimport mockit.Mockit;\n\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.shared.arbitrate.impl.ArbitrateConstants;\nimport com.alibaba.otter.shared.arbitrate.impl.config.ArbitrateConfigUtils;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.ArbitrateFactory;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.zookeeper.SelectZooKeeperArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.model.EtlEventData;\nimport com.alibaba.otter.shared.arbitrate.setl.event.BaseArbitrateEventTest;\n\n/**\n * @author jianghang 2011-9-22 下午05:24:26\n * @version 4.0.0\n */\npublic class SelectArbitrateEventTest extends BaseArbitrateEventTest {\n\n    private SelectZooKeeperArbitrateEvent selectEvent;\n\n    @Test\n    public void test_select() {\n        Mockit.setUpMock(ArbitrateConfigUtils.class, new Object() {\n\n            @Mock\n            public int getParallelism(Long pipelineId) {\n                return 2;//并行度\n            }\n\n        });\n\n        selectEvent = new SelectZooKeeperArbitrateEvent();\n        final List<Long> initProcessIds = new ArrayList<Long>();\n        try {\n            //获取数据\n            EtlEventData data1 = selectEvent.await(pipelineId);\n            EtlEventData data2 = selectEvent.await(pipelineId);\n\n            want.bool(data1 != null).is(true);\n            want.bool(data2 != null).is(true);\n            initProcessIds.add(data1.getProcessId());\n            initProcessIds.add(data2.getProcessId());\n\n            selectEvent.single(data1);\n            selectEvent.single(data2);\n\n            ArbitrateFactory.destory(pipelineId);\n        } catch (InterruptedException e) {\n            want.fail();\n        } finally {\n            for (Long processId : initProcessIds) {\n                destoryStage(processId, ArbitrateConstants.NODE_SELECTED);\n                destoryProcess(processId);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "shared/arbitrate/src/test/java/com/alibaba/otter/shared/arbitrate/setl/event/zookeeper/TerminArbitrateEventTest.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.setl.event.zookeeper;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport mockit.Mock;\nimport mockit.Mockit;\n\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.shared.arbitrate.impl.config.ArbitrateConfigUtils;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.ArbitrateFactory;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.helper.StagePathUtils;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.monitor.PermitMonitor;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.zookeeper.ExtractZooKeeperArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.zookeeper.LoadZooKeeperArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.zookeeper.SelectZooKeeperArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.zookeeper.TerminZooKeeperArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.zookeeper.TransformZooKeeperArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.model.EtlEventData;\nimport com.alibaba.otter.shared.arbitrate.model.TerminEventData;\nimport com.alibaba.otter.shared.arbitrate.model.TerminEventData.TerminType;\nimport com.alibaba.otter.shared.arbitrate.setl.event.BaseArbitrateEventTest;\n\n/**\n * @author jianghang 2011-9-27 下午09:45:23\n * @version 4.0.0\n */\npublic class TerminArbitrateEventTest extends BaseArbitrateEventTest {\n\n    private SelectZooKeeperArbitrateEvent    selectEvent;\n    private ExtractZooKeeperArbitrateEvent   extractEvent;\n    private TransformZooKeeperArbitrateEvent transformEvent;\n    private LoadZooKeeperArbitrateEvent      loadEvent;\n    private TerminZooKeeperArbitrateEvent    terminEvent;\n\n    @Test\n    public void test_Rollback() {\n        normalProcess();\n        // 发送rollback信号\n        TerminEventData rollback = new TerminEventData();\n        rollback.setPipelineId(pipelineId);\n        rollback.setType(TerminType.ROLLBACK);\n        terminEvent.single(rollback);\n\n        PermitMonitor monitor = ArbitrateFactory.getInstance(pipelineId, PermitMonitor.class);\n        want.bool(monitor.getChannelPermit().isPause()).is(true);\n\n        destoryTermin();\n        ArbitrateFactory.destory(pipelineId);\n    }\n\n    @Test\n    public void test_Shutdown() {\n        normalProcess();\n        // 发送shutdown信号\n        TerminEventData shutdown = new TerminEventData();\n        shutdown.setPipelineId(pipelineId);\n        shutdown.setType(TerminType.SHUTDOWN);\n        terminEvent.single(shutdown);\n\n        PermitMonitor monitor = ArbitrateFactory.getInstance(pipelineId, PermitMonitor.class);\n        want.bool(monitor.getChannelPermit().isStop()).is(true);\n\n        destoryTermin();\n        ArbitrateFactory.destory(pipelineId);\n    }\n\n    @Test\n    public void test_Restart() {\n        normalProcess();\n        // 发送restart信号\n        TerminEventData rollback = new TerminEventData();\n        rollback.setPipelineId(pipelineId);\n        rollback.setType(TerminType.RESTART);\n        terminEvent.single(rollback);\n\n        PermitMonitor monitor = ArbitrateFactory.getInstance(pipelineId, PermitMonitor.class);\n        sleep(4000L);\n        want.bool(monitor.getChannelPermit().isStart()).is(true);\n\n        // 发送shutdown信号\n        TerminEventData shutdown = new TerminEventData();\n        shutdown.setPipelineId(pipelineId);\n        shutdown.setType(TerminType.SHUTDOWN);\n        terminEvent.single(shutdown);\n\n        want.bool(monitor.getChannelPermit().isStop()).is(true);\n\n        // 删除对应的错误节点\n        destoryTermin();\n        ArbitrateFactory.destory(pipelineId);\n    }\n\n    private void destoryTermin() {\n        String path = StagePathUtils.getTerminRoot(pipelineId);\n        List<String> terminNodes = zookeeper.getChildren(path);\n        for (String node : terminNodes) {\n            // 删除对应的错误节点\n            TerminEventData termin = new TerminEventData();\n            termin.setPipelineId(pipelineId);\n            termin.setProcessId(StagePathUtils.getProcessId(node));\n            System.out.println(\"remove termin node: \" + path + \"/\" + node);\n            terminEvent.ack(termin);// 发送ack信号，删除termin节点\n        }\n    }\n\n    private void normalProcess() {\n        Mockit.setUpMock(ArbitrateConfigUtils.class, new Object() {\n\n            @Mock\n            public int getParallelism(Long pipelineId) {\n                return 2;// 并行度\n            }\n\n            @Mock\n            public Long getCurrentNid() {\n                return nid;\n            }\n\n        });\n\n        selectEvent = new SelectZooKeeperArbitrateEvent();\n        extractEvent = new ExtractZooKeeperArbitrateEvent();\n        transformEvent = new TransformZooKeeperArbitrateEvent();\n        loadEvent = new LoadZooKeeperArbitrateEvent();\n        terminEvent = (TerminZooKeeperArbitrateEvent) this.getBeanFactory().getBean(\"terminZooKeeperEvent\");\n        loadEvent.setTerminEvent(terminEvent);\n\n        final List<Long> initProcessIds = new ArrayList<Long>();\n        try {\n            // 获取数据\n\n            // select stage\n            EtlEventData sdata1 = selectEvent.await(pipelineId);\n            EtlEventData sdata2 = selectEvent.await(pipelineId);\n\n            initProcessIds.add(sdata1.getProcessId());\n            initProcessIds.add(sdata2.getProcessId());\n\n            selectEvent.single(sdata1);\n            selectEvent.single(sdata2);\n\n            // extract stage\n            EtlEventData edata1 = extractEvent.await(pipelineId);\n            EtlEventData edata2 = extractEvent.await(pipelineId);\n\n            extractEvent.single(edata1);\n            extractEvent.single(edata2);\n\n            // transform stage\n            EtlEventData tdata1 = transformEvent.await(pipelineId);\n            EtlEventData tdata2 = transformEvent.await(pipelineId);\n\n            transformEvent.single(tdata1);\n            transformEvent.single(tdata2);\n\n            // SelectStageListener selectStageListener =\n            // ArbitrateFactory.getInstance(pipelineId,\n            // SelectStageListener.class);\n            // selectStageListener.destory();\n            // load stage\n            EtlEventData ldata1 = loadEvent.await(pipelineId);\n            loadEvent.single(ldata1);\n            Long p1 = ldata1.getProcessId();\n\n            TerminEventData terminData1 = new TerminEventData();\n            terminData1.setPipelineId(pipelineId);\n            terminData1.setProcessId(p1);\n            terminEvent.ack(terminData1);// 发送ack信号，删除termin节点\n\n        } catch (InterruptedException e) {\n            want.fail();\n        }\n\n    }\n}\n"
  },
  {
    "path": "shared/arbitrate/src/test/java/com/alibaba/otter/shared/arbitrate/setl/event/zookeeper/TransformArbitrateEventTest.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.setl.event.zookeeper;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport mockit.Mock;\nimport mockit.Mockit;\n\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.shared.arbitrate.impl.ArbitrateConstants;\nimport com.alibaba.otter.shared.arbitrate.impl.config.ArbitrateConfigUtils;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.ArbitrateFactory;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.zookeeper.ExtractZooKeeperArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.zookeeper.SelectZooKeeperArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.zookeeper.TransformZooKeeperArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.model.EtlEventData;\nimport com.alibaba.otter.shared.arbitrate.setl.event.BaseArbitrateEventTest;\n\n/**\n * @author jianghang 2011-9-23 下午02:58:13\n * @version 4.0.0\n */\npublic class TransformArbitrateEventTest extends BaseArbitrateEventTest {\n\n    private SelectZooKeeperArbitrateEvent    selectEvent;\n    private ExtractZooKeeperArbitrateEvent   extractEvent;\n    private TransformZooKeeperArbitrateEvent transformEvent;\n\n    @Test\n    public void test_transform() {\n        Mockit.setUpMock(ArbitrateConfigUtils.class, new Object() {\n\n            @Mock\n            public int getParallelism(Long pipelineId) {\n                return 2;// 并行度\n            }\n\n        });\n\n        selectEvent = new SelectZooKeeperArbitrateEvent();\n        extractEvent = new ExtractZooKeeperArbitrateEvent();\n        transformEvent = new TransformZooKeeperArbitrateEvent();\n\n        final List<Long> initProcessIds = new ArrayList<Long>();\n        try {\n            // 获取数据\n\n            // select stage\n            EtlEventData sdata1 = selectEvent.await(pipelineId);\n            EtlEventData sdata2 = selectEvent.await(pipelineId);\n\n            initProcessIds.add(sdata1.getProcessId());\n            initProcessIds.add(sdata2.getProcessId());\n\n            selectEvent.single(sdata1);\n            selectEvent.single(sdata2);\n            // extract stage\n            EtlEventData edata1 = extractEvent.await(pipelineId);\n            EtlEventData edata2 = extractEvent.await(pipelineId);\n\n            extractEvent.single(edata1);\n            extractEvent.single(edata2);\n\n            // transform stage\n            EtlEventData tdata1 = transformEvent.await(pipelineId);\n            EtlEventData tdata2 = transformEvent.await(pipelineId);\n\n            transformEvent.single(tdata1);\n            transformEvent.single(tdata2);\n            ArbitrateFactory.destory(pipelineId);\n        } catch (InterruptedException e) {\n            want.fail();\n        } finally {\n            for (Long processId : initProcessIds) {\n                destoryStage(processId, ArbitrateConstants.NODE_SELECTED);\n                destoryStage(processId, ArbitrateConstants.NODE_EXTRACTED);\n                destoryStage(processId, ArbitrateConstants.NODE_TRANSFORMED);\n                destoryProcess(processId);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "shared/arbitrate/src/test/java/com/alibaba/otter/shared/arbitrate/setl/lb/BaseLoadBalanceTest.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.setl.lb;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport mockit.Mock;\nimport mockit.Mockit;\n\nimport org.testng.annotations.AfterMethod;\nimport org.testng.annotations.BeforeClass;\nimport org.testng.annotations.BeforeMethod;\n\nimport com.alibaba.otter.shared.arbitrate.BaseEventTest;\nimport com.alibaba.otter.shared.arbitrate.impl.config.ArbitrateConfigUtils;\nimport com.alibaba.otter.shared.arbitrate.impl.manage.NodeArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.monitor.NodeMonitor;\nimport com.alibaba.otter.shared.common.model.config.channel.Channel;\nimport com.alibaba.otter.shared.common.model.config.node.Node;\nimport com.alibaba.otter.shared.common.utils.zookeeper.ZkClientx;\n\n/**\n * lb 测试基础\n * \n * @author jianghang 2011-9-22 上午11:38:21\n * @version 4.0.0\n */\npublic class BaseLoadBalanceTest extends BaseEventTest {\n\n    protected ZkClientx          zookeeper  = null;\n    protected Long               channelId  = 100L;\n    protected Long               pipelineId = 100L;\n    protected NodeArbitrateEvent nodeEvent;\n    protected NodeMonitor        nodeMonitor;\n    protected final Node         node1      = new Node();\n    protected final Node         node2      = new Node();\n    protected final Node         node3      = new Node();\n    protected final Node         node4      = new Node();\n    protected List<Node>         sourceList = Arrays.asList(node1, node3);\n    protected List<Node>         targetList = Arrays.asList(node2, node4);\n\n    @BeforeClass\n    public void init() {\n        // 初始化节点\n        Mockit.setUpMock(ArbitrateConfigUtils.class, new Object() {\n\n            @Mock\n            public Channel getChannel(Long pipelineId) {\n                Channel channel = new Channel();\n                channel.setId(channelId);\n                return channel;\n            }\n\n            @Mock\n            public Long getCurrentNid() {\n                return 1L;\n            }\n\n        });\n\n        node1.setId(1L);\n        node2.setId(2L);\n        node3.setId(3L);\n        node4.setId(4L);\n        zookeeper = getZookeeper();\n        nodeEvent = new NodeArbitrateEvent();\n        nodeMonitor = new NodeMonitor();\n    }\n\n    @BeforeMethod\n    public void setUp() {\n        nodeEvent.init(node1.getId());\n        nodeEvent.init(node2.getId());\n        nodeEvent.init(node3.getId());\n        nodeEvent.init(node4.getId());\n    }\n\n    @AfterMethod\n    public void tearDown() {\n        nodeEvent.destory(node1.getId());\n        nodeEvent.destory(node2.getId());\n        nodeEvent.destory(node3.getId());\n        nodeEvent.destory(node4.getId());\n    }\n}\n"
  },
  {
    "path": "shared/arbitrate/src/test/java/com/alibaba/otter/shared/arbitrate/setl/lb/RandomLoadBalanceTest.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.setl.lb;\n\nimport mockit.Mock;\nimport mockit.Mockit;\n\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.shared.arbitrate.impl.config.ArbitrateConfigUtils;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.lb.ExtractRandomLoadBanlance;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.lb.TransformRandomLoadBanlance;\nimport com.alibaba.otter.shared.common.model.config.node.Node;\nimport com.alibaba.otter.shared.common.model.config.pipeline.Pipeline;\n\n/**\n * Random lb测试\n * \n * @author jianghang 2011-9-22 上午11:36:08\n * @version 4.0.0\n */\npublic class RandomLoadBalanceTest extends BaseLoadBalanceTest {\n\n    @Test\n    public void testExtract() {\n        // 初始化节点\n        Mockit.setUpMock(ArbitrateConfigUtils.class, new Object() {\n\n            @Mock\n            public Pipeline getPipeline(Long pipelineId) {\n                Pipeline pipeline = new Pipeline();\n                pipeline.setId(pipelineId);\n                pipeline.setSelectNodes(sourceList);\n                pipeline.setExtractNodes(sourceList);\n                pipeline.setLoadNodes(targetList);\n\n                return pipeline;\n            }\n\n        });\n\n        ExtractRandomLoadBanlance extract = new ExtractRandomLoadBanlance(pipelineId);\n        extract.setNodeMonitor(nodeMonitor);\n        sleep(500L);\n        try {\n            Node n1 = extract.next();\n            Node n2 = extract.next();\n            Node n3 = extract.next();\n            Node n4 = extract.next();\n            System.out.printf(\"n1[%s] n2[%s] n3[%s] n4[%s]\", n1.getId(), n2.getId(), n3.getId(), n4.getId());\n            want.bool(sourceList.contains(n1)).is(true);\n            want.bool(sourceList.contains(n2)).is(true);\n            want.bool(sourceList.contains(n3)).is(true);\n            want.bool(sourceList.contains(n4)).is(true);\n        } catch (InterruptedException e) {\n            want.fail();\n        }\n\n    }\n\n    @Test\n    public void testTransform() {\n        // 初始化节点\n        Mockit.setUpMock(ArbitrateConfigUtils.class, new Object() {\n\n            @Mock\n            public Pipeline getPipeline(Long pipelineId) {\n                Pipeline pipeline = new Pipeline();\n                pipeline.setId(pipelineId);\n                pipeline.setSelectNodes(sourceList);\n                pipeline.setExtractNodes(sourceList);\n                pipeline.setLoadNodes(targetList);\n                return pipeline;\n            }\n\n        });\n\n        TransformRandomLoadBanlance transform = new TransformRandomLoadBanlance(pipelineId);\n        transform.setNodeMonitor(nodeMonitor);\n        sleep(500L);\n        try {\n            Node n1 = transform.next();\n            Node n2 = transform.next();\n            Node n3 = transform.next();\n            Node n4 = transform.next();\n            System.out.printf(\"n1[%s] n2[%s] n3[%s] n4[%s]\", n1.getId(), n2.getId(), n3.getId(), n4.getId());\n            want.bool(targetList.contains(n1)).is(true);\n            want.bool(targetList.contains(n2)).is(true);\n            want.bool(targetList.contains(n3)).is(true);\n            want.bool(targetList.contains(n4)).is(true);\n        } catch (InterruptedException e) {\n            want.fail();\n        }\n\n    }\n}\n"
  },
  {
    "path": "shared/arbitrate/src/test/java/com/alibaba/otter/shared/arbitrate/setl/lb/RoundRobinBalanceTest.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.setl.lb;\n\nimport mockit.Mock;\nimport mockit.Mockit;\n\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.shared.arbitrate.impl.config.ArbitrateConfigUtils;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.lb.ExtractRoundRobinLoadBalance;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.lb.TransformRoundRobinLoadBalance;\nimport com.alibaba.otter.shared.common.model.config.node.Node;\nimport com.alibaba.otter.shared.common.model.config.pipeline.Pipeline;\n\n/**\n * RoundRobin lb测试\n * \n * @author jianghang 2011-9-22 上午11:36:24\n * @version 4.0.0\n */\npublic class RoundRobinBalanceTest extends BaseLoadBalanceTest {\n\n    @Test\n    public void testExtract() {\n        // 初始化节点\n        Mockit.setUpMock(ArbitrateConfigUtils.class, new Object() {\n\n            @Mock\n            public Pipeline getPipeline(Long pipelineId) {\n                Pipeline pipeline = new Pipeline();\n                pipeline.setId(pipelineId);\n                pipeline.setSelectNodes(sourceList);\n                pipeline.setExtractNodes(sourceList);\n                pipeline.setLoadNodes(targetList);\n                return pipeline;\n            }\n\n        });\n\n        ExtractRoundRobinLoadBalance extract = new ExtractRoundRobinLoadBalance(pipelineId);\n        extract.setNodeMonitor(nodeMonitor);\n        try {\n            Node n1 = extract.next();\n            Node n2 = extract.next();\n            Node n3 = extract.next();\n            Node n4 = extract.next();\n            System.out.printf(\"n1[%s] n2[%s] n3[%s] n4[%s]\", n1.getId(), n2.getId(), n3.getId(), n4.getId());\n            want.bool(sourceList.contains(n1)).is(true);\n            want.bool(sourceList.contains(n2)).is(true);\n            want.bool(sourceList.contains(n3)).is(true);\n            want.bool(sourceList.contains(n4)).is(true);\n        } catch (InterruptedException e) {\n            want.fail();\n        }\n\n    }\n\n    @Test\n    public void testTransform() {\n        // 初始化节点\n        Mockit.setUpMock(ArbitrateConfigUtils.class, new Object() {\n\n            @Mock\n            public Pipeline getPipeline(Long pipelineId) {\n                Pipeline pipeline = new Pipeline();\n                pipeline.setId(pipelineId);\n                pipeline.setSelectNodes(sourceList);\n                pipeline.setExtractNodes(sourceList);\n                pipeline.setLoadNodes(targetList);\n                return pipeline;\n            }\n\n        });\n\n        TransformRoundRobinLoadBalance transform = new TransformRoundRobinLoadBalance(pipelineId);\n        transform.setNodeMonitor(nodeMonitor);\n        try {\n            Node n1 = transform.next();\n            Node n2 = transform.next();\n            Node n3 = transform.next();\n            Node n4 = transform.next();\n            System.out.printf(\"n1[%s] n2[%s] n3[%s] n4[%s]\", n1.getId(), n2.getId(), n3.getId(), n4.getId());\n            want.bool(targetList.contains(n1)).is(true);\n            want.bool(targetList.contains(n2)).is(true);\n            want.bool(targetList.contains(n3)).is(true);\n            want.bool(targetList.contains(n4)).is(true);\n        } catch (InterruptedException e) {\n            want.fail();\n        }\n\n    }\n}\n"
  },
  {
    "path": "shared/arbitrate/src/test/java/com/alibaba/otter/shared/arbitrate/setl/monitor/MainStemMonitorTest.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.setl.monitor;\n\nimport java.util.Arrays;\n\nimport mockit.Mock;\nimport mockit.Mockit;\n\nimport org.I0Itec.zkclient.exception.ZkBadVersionException;\nimport org.I0Itec.zkclient.exception.ZkException;\nimport org.I0Itec.zkclient.exception.ZkNoNodeException;\nimport org.apache.zookeeper.data.Stat;\nimport org.testng.annotations.AfterMethod;\nimport org.testng.annotations.BeforeClass;\nimport org.testng.annotations.BeforeMethod;\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.shared.arbitrate.BaseEventTest;\nimport com.alibaba.otter.shared.arbitrate.exception.ArbitrateException;\nimport com.alibaba.otter.shared.arbitrate.impl.config.ArbitrateConfigUtils;\nimport com.alibaba.otter.shared.arbitrate.impl.manage.ChannelArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.manage.NodeArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.manage.PipelineArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.manage.helper.ManagePathUtils;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.ArbitrateFactory;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.monitor.MainstemMonitor;\nimport com.alibaba.otter.shared.arbitrate.model.MainStemEventData;\nimport com.alibaba.otter.shared.common.model.config.channel.Channel;\nimport com.alibaba.otter.shared.common.model.config.node.Node;\nimport com.alibaba.otter.shared.common.model.config.pipeline.Pipeline;\nimport com.alibaba.otter.shared.common.utils.JsonUtils;\nimport com.alibaba.otter.shared.common.utils.zookeeper.ZkClientx;\n\npublic class MainStemMonitorTest extends BaseEventTest {\n\n    private ZkClientx              zookeeper  = null;\n    private ChannelArbitrateEvent  channelEvent;\n    private PipelineArbitrateEvent pipelineEvent;\n    private NodeArbitrateEvent     nodeEvent;\n    private final Node             local      = new Node();\n    private final Long             nid        = 1L;\n    private Long                   channelId  = 100L;\n    private Long                   pipelineId = 100L;\n\n    @BeforeClass\n    public void init() {\n        // 初始化节点\n        Mockit.setUpMock(ArbitrateConfigUtils.class, new Object() {\n\n            @Mock\n            public Channel getChannel(Long pipelineId) {\n                Channel channel = new Channel();\n                channel.setId(channelId);\n                return channel;\n            }\n\n            @Mock\n            public Pipeline getOppositePipeline(Long pipelineId) {\n                return null;// 没有反向同步\n            }\n\n            @Mock\n            public int getParallelism(Long pipelineId) {\n                return 3;// 并行度\n            }\n\n            @Mock\n            public Pipeline getPipeline(Long pipelineId) {\n                Pipeline pipeline = new Pipeline();\n                pipeline.setId(pipelineId);\n                pipeline.setSelectNodes(Arrays.asList(local));\n                pipeline.setExtractNodes(Arrays.asList(local));\n                pipeline.setLoadNodes(Arrays.asList(local));\n                return pipeline;\n            }\n\n            @Mock\n            public Long getCurrentNid() {\n                return nid;\n            }\n        });\n\n        zookeeper = getZookeeper();\n        local.setId(nid);\n        nodeEvent = new NodeArbitrateEvent();\n        channelEvent = new ChannelArbitrateEvent();\n        pipelineEvent = new PipelineArbitrateEvent();\n    }\n\n    @BeforeMethod\n    public void setUp() {\n        nodeEvent.init(nid);\n        channelEvent.init(channelId);\n        pipelineEvent.init(channelId, pipelineId);\n        channelEvent.start(channelId);\n    }\n\n    @AfterMethod\n    public void tearDown() {\n        nodeEvent.destory(nid);\n        pipelineEvent.destory(channelId, pipelineId);\n        channelEvent.destory(channelId);\n\n    }\n\n    @Test\n    public void testInit() {\n        MainstemMonitor mainstemMonitor = ArbitrateFactory.getInstance(pipelineId, MainstemMonitor.class);\n        boolean running = mainstemMonitor.check();\n        want.bool(running).is(true);\n\n        try {\n            mainstemMonitor.waitForActive();\n        } catch (InterruptedException e) {\n            want.fail();\n        }\n\n        ArbitrateFactory.destory(pipelineId);\n        mainstemMonitor.releaseMainstem();\n    }\n\n    @Test\n    public void testRelease() {\n        MainstemMonitor mainstemMonitor = ArbitrateFactory.getInstance(pipelineId, MainstemMonitor.class);\n        boolean running = mainstemMonitor.check();\n        want.bool(running).is(true);\n\n        boolean release = mainstemMonitor.releaseMainstem();// 模拟一次断网，\n        want.bool(release).is(true);\n\n        long start = System.currentTimeMillis();\n        try {\n            mainstemMonitor.waitForActive();\n        } catch (InterruptedException e) {\n            want.fail();\n        }\n        want.number(System.currentTimeMillis() - start).isLe(1000L);\n        ArbitrateFactory.destory(pipelineId);\n        mainstemMonitor.releaseMainstem();\n    }\n\n    @Test\n    public void testManualRelease() {\n        MainstemMonitor mainstemMonitor = ArbitrateFactory.getInstance(pipelineId, MainstemMonitor.class);\n        boolean running = mainstemMonitor.check();\n        want.bool(running).is(true);\n        mainstemMonitor.setDelayTime(5);\n\n        switchWarmup(channelId, pipelineId);\n        sleep(1000L); // 等manual release被响应\n\n        long start = System.currentTimeMillis();\n        try {\n            mainstemMonitor.waitForActive();\n        } catch (InterruptedException e) {\n            want.fail();\n        }\n        want.number(System.currentTimeMillis() - start).isGe(4000L);\n        ArbitrateFactory.destory(pipelineId);\n        mainstemMonitor.releaseMainstem();\n    }\n\n    /**\n     * 手工触发一次主备切换\n     */\n    private void switchWarmup(Long channelId, Long pipelineId) {\n        String path = ManagePathUtils.getMainStem(channelId, pipelineId);\n        try {\n            while (true) {\n                Stat stat = new Stat();\n                byte[] bytes = zookeeper.readData(path, stat);\n                MainStemEventData mainStemData = JsonUtils.unmarshalFromByte(bytes, MainStemEventData.class);\n                mainStemData.setActive(false);\n                try {\n                    zookeeper.writeData(path, JsonUtils.marshalToByte(mainStemData), stat.getVersion());\n                    break;\n                } catch (ZkBadVersionException e) {\n                    // ignore , retrying\n                }\n\n            }\n        } catch (ZkNoNodeException e) {\n            // ignore\n        } catch (ZkException e) {\n            throw new ArbitrateException(\"releaseMainStem\", pipelineId.toString(), e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/test/java/com/alibaba/otter/shared/arbitrate/setl/monitor/PermitMonitorTest.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.setl.monitor;\n\nimport java.util.concurrent.CountDownLatch;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\n\nimport mockit.Mock;\nimport mockit.Mockit;\n\nimport org.apache.zookeeper.CreateMode;\nimport org.testng.annotations.AfterMethod;\nimport org.testng.annotations.BeforeClass;\nimport org.testng.annotations.BeforeMethod;\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.shared.arbitrate.BaseEventTest;\nimport com.alibaba.otter.shared.arbitrate.impl.ArbitrateConstants;\nimport com.alibaba.otter.shared.arbitrate.impl.config.ArbitrateConfigUtils;\nimport com.alibaba.otter.shared.arbitrate.impl.manage.ChannelArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.manage.PipelineArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.helper.StagePathUtils;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.monitor.MonitorScheduler;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.monitor.PermitMonitor;\nimport com.alibaba.otter.shared.arbitrate.model.MainStemEventData;\nimport com.alibaba.otter.shared.common.model.config.channel.Channel;\nimport com.alibaba.otter.shared.common.model.config.pipeline.Pipeline;\nimport com.alibaba.otter.shared.common.utils.JsonUtils;\nimport com.alibaba.otter.shared.common.utils.zookeeper.ZkClientx;\n\n/**\n * permit测试\n * \n * @author jianghang 2011-9-19 下午02:57:14\n * @version 4.0.0\n */\npublic class PermitMonitorTest extends BaseEventTest {\n\n    private ZkClientx              zookeeper          = null;\n    private ChannelArbitrateEvent  channelEvent;\n    private PipelineArbitrateEvent pipelineEvent;\n\n    private Long                   channelId          = 100L;\n    private Long                   pipelineId         = 100L;\n    private Long                   oppositePipelineId = 101L;\n    private PermitMonitor          permitMonitor      = null;\n\n    @BeforeClass\n    public void init() {\n        // 初始化节点\n        Mockit.setUpMock(ArbitrateConfigUtils.class, new Object() {\n\n            @Mock\n            public Channel getChannel(Long pipelineId) {\n                Channel channel = new Channel();\n                channel.setId(channelId);\n                return channel;\n            }\n\n            @Mock\n            public Pipeline getOppositePipeline(Long pipelineId) {\n                Pipeline pipeline = new Pipeline();\n                pipeline.setId(oppositePipelineId);\n                return pipeline;\n            }\n\n        });\n\n        zookeeper = getZookeeper();\n        channelEvent = new ChannelArbitrateEvent();\n        pipelineEvent = new PipelineArbitrateEvent();\n    }\n\n    @BeforeMethod\n    public void setUp() {\n        channelEvent.init(channelId);\n        // 初始化两个pipeline\n        pipelineEvent.init(channelId, pipelineId);\n        pipelineEvent.init(channelId, oppositePipelineId);\n\n        initMainStem(pipelineId);\n        initMainStem(oppositePipelineId);\n    }\n\n    @AfterMethod\n    public void tearDown() {\n        destoryMainStem(pipelineId);\n        destoryMainStem(oppositePipelineId);\n        permitMonitor.destory();\n        // MonitorScheduler.unRegister(permitMonitor);\n        // 初始化两个pipeline\n        pipelineEvent.destory(channelId, pipelineId);\n        pipelineEvent.destory(channelId, oppositePipelineId);\n        channelEvent.destory(channelId);\n\n    }\n\n    private void initMainStem(Long pipelineId) {\n        String pipelinePath = StagePathUtils.getPipeline(channelId, pipelineId);\n        String path = pipelinePath + \"/\" + ArbitrateConstants.NODE_MAINSTEM;\n\n        MainStemEventData eventData = new MainStemEventData();\n        eventData.setStatus(MainStemEventData.Status.TAKEING);\n        byte[] bytes = JsonUtils.marshalToByte(eventData);// 初始化的数据对象\n\n        zookeeper.create(path, bytes, CreateMode.EPHEMERAL);\n    }\n\n    private void updateMainStem(Long pipelineId, MainStemEventData.Status status) {\n        String pipelinePath = StagePathUtils.getPipeline(channelId, pipelineId);\n        String path = pipelinePath + \"/\" + ArbitrateConstants.NODE_MAINSTEM;\n        MainStemEventData eventData = new MainStemEventData();\n        eventData.setStatus(status);\n        byte[] bytes = JsonUtils.marshalToByte(eventData);// 初始化的数据对象\n\n        zookeeper.writeData(path, bytes);\n    }\n\n    private void destoryMainStem(Long pipelineId) {\n        String pipelinePath = StagePathUtils.getPipeline(channelId, pipelineId);\n        String path = pipelinePath + \"/\" + ArbitrateConstants.NODE_MAINSTEM;\n\n        zookeeper.delete(path);\n    }\n\n    @Test\n    public void testPermit_init_ok() {// 测试下permit的初始化内容\n        channelEvent.start(channelId);\n        updateMainStem(pipelineId, MainStemEventData.Status.OVERTAKE);\n        updateMainStem(oppositePipelineId, MainStemEventData.Status.OVERTAKE);\n\n        permitMonitor = new PermitMonitor(pipelineId);\n        boolean isPermit = permitMonitor.isPermit();\n        want.bool(isPermit).is(true);\n    }\n\n    @Test\n    public void testPermit_init_fail() {// 测试下permit的初始化内容\n        channelEvent.start(channelId);\n        updateMainStem(pipelineId, MainStemEventData.Status.OVERTAKE);\n        updateMainStem(oppositePipelineId, MainStemEventData.Status.TAKEING);// 一个节点挂起\n\n        permitMonitor = new PermitMonitor(pipelineId);\n        boolean isPermit = permitMonitor.isPermit();\n        want.bool(isPermit).is(false);\n    }\n\n    @Test\n    public void testPermit_change() {\n        channelEvent.start(channelId);\n        updateMainStem(pipelineId, MainStemEventData.Status.OVERTAKE);\n        updateMainStem(oppositePipelineId, MainStemEventData.Status.TAKEING);// 一个节点挂起\n\n        permitMonitor = new PermitMonitor(pipelineId);\n        boolean isPermit = permitMonitor.isPermit();\n        want.bool(isPermit).is(false);\n\n        updateMainStem(oppositePipelineId, MainStemEventData.Status.OVERTAKE);\n        updateMainStem(pipelineId, MainStemEventData.Status.TAKEING);// 一个节点挂起\n        sleep();// 需要sleep一下，保证数据已经更新到monitor上\n        isPermit = permitMonitor.isPermit();\n        want.bool(isPermit).is(false);\n\n        updateMainStem(pipelineId, MainStemEventData.Status.OVERTAKE);\n        sleep();// 需要sleep一下，保证数据已经更新到monitor上\n        isPermit = permitMonitor.isPermit();\n        want.bool(isPermit).is(true);\n        MonitorScheduler.unRegister(permitMonitor);\n    }\n\n    @Test\n    public void testPermit_wait() {\n        channelEvent.start(channelId);\n        updateMainStem(pipelineId, MainStemEventData.Status.OVERTAKE);\n        updateMainStem(oppositePipelineId, MainStemEventData.Status.OVERTAKE);\n        permitMonitor = new PermitMonitor(pipelineId);\n        boolean isPermit = permitMonitor.isPermit();\n        want.bool(isPermit).is(true);\n        try {\n            permitMonitor.waitForPermit();// 当前为permit=true,立马返回\n        } catch (InterruptedException e) {\n            want.fail();\n        }\n\n        updateMainStem(pipelineId, MainStemEventData.Status.TAKEING);// 一个节点挂起\n        sleep();\n        isPermit = permitMonitor.isPermit();\n        want.bool(isPermit).is(false);\n        // 提交一个异步更新状态任务\n        final CountDownLatch count = new CountDownLatch(1);\n        ExecutorService executor = Executors.newCachedThreadPool();\n        executor.submit(new Runnable() {\n\n            public void run() {\n                sleep();// sleep一下后再触发\n                updateMainStem(pipelineId, MainStemEventData.Status.OVERTAKE);// 一个节点挂起\n                count.countDown();\n            }\n        });\n\n        try {\n            permitMonitor.waitForPermit();// 当前为permit=false,阻塞，等待信号\n        } catch (InterruptedException e) {\n            want.fail();\n        }\n\n        try {\n            count.await();\n            executor.shutdown();\n        } catch (InterruptedException e) {\n            want.fail();\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/test/java/com/alibaba/otter/shared/arbitrate/setl/monitor/ProcessMonitorTest.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.setl.monitor;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.concurrent.CountDownLatch;\n\nimport org.testng.annotations.AfterMethod;\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.shared.arbitrate.impl.setl.ArbitrateFactory;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.monitor.MonitorScheduler;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.rpc.monitor.ProcessListener;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.rpc.monitor.ProcessMonitor;\nimport com.alibaba.otter.shared.arbitrate.setl.BaseStageTest;\n\npublic class ProcessMonitorTest extends BaseStageTest {\n\n    private ProcessMonitor monitor;\n\n    @AfterMethod\n    public void dispose() {\n        MonitorScheduler.unRegister(monitor);\n    }\n\n    @Test\n    public void testProcess_static() {// 测试静态的process获取\n        final List<Long> initProcessIds = new ArrayList<Long>();\n        try {\n            Long p1 = initProcess();\n            Long p2 = initProcess();\n            initProcessIds.add(p1);\n            initProcessIds.add(p2);\n            monitor = ArbitrateFactory.getInstance(pipelineId, ProcessMonitor.class);\n            List<Long> processIds = monitor.getCurrentProcessIds();\n            want.collection(processIds).isEqualTo(initProcessIds);\n\n            MonitorScheduler.unRegister(monitor);\n        } finally {\n            for (Long processId : initProcessIds) {\n                destoryProcess(processId);\n            }\n\n        }\n    }\n\n    @Test\n    public void testProcess_dynamic() {// 测试动态的process获取\n        final List<Long> initProcessIds = new ArrayList<Long>();\n        try {\n            Long p1 = initProcess();\n            Long p2 = initProcess();\n            initProcessIds.add(p1);\n            initProcessIds.add(p2);\n\n            monitor = ArbitrateFactory.getInstance(pipelineId, ProcessMonitor.class);\n            final CountDownLatch count = new CountDownLatch(1);\n            monitor.addListener(new ProcessListener() {\n\n                public void processChanged(List<Long> processIds) {\n                    want.collection(processIds).isEqualTo(initProcessIds);\n                    count.countDown();\n                }\n\n            });\n            // 产生一些变化\n            Long p3 = initProcess();\n            initProcessIds.add(p3);\n            destoryProcess(p1);\n            initProcessIds.remove(p1);\n\n            sleep();\n            List<Long> processIds = monitor.getCurrentProcessIds();\n            want.collection(processIds).isEqualTo(initProcessIds);\n\n            try {\n                count.await();// 等待listener响应\n            } catch (InterruptedException e) {\n                want.fail();\n            }\n        } finally {\n            for (Long processId : initProcessIds) {\n                destoryProcess(processId);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "shared/arbitrate/src/test/java/com/alibaba/otter/shared/arbitrate/setl/monitor/TerminMonitorTest.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.setl.monitor;\n\nimport mockit.Mock;\nimport mockit.Mockit;\n\nimport org.apache.zookeeper.CreateMode;\nimport org.testng.annotations.AfterMethod;\nimport org.testng.annotations.BeforeClass;\nimport org.testng.annotations.BeforeMethod;\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.shared.arbitrate.BaseEventTest;\nimport com.alibaba.otter.shared.arbitrate.impl.config.ArbitrateConfigUtils;\nimport com.alibaba.otter.shared.arbitrate.impl.manage.ChannelArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.manage.PipelineArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.helper.StagePathUtils;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.monitor.MonitorScheduler;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.monitor.TerminMonitor;\nimport com.alibaba.otter.shared.arbitrate.model.TerminEventData;\nimport com.alibaba.otter.shared.arbitrate.model.TerminEventData.TerminType;\nimport com.alibaba.otter.shared.common.model.config.channel.Channel;\nimport com.alibaba.otter.shared.common.model.config.pipeline.Pipeline;\nimport com.alibaba.otter.shared.common.utils.JsonUtils;\nimport com.alibaba.otter.shared.common.utils.zookeeper.ZkClientx;\n\n/**\n * @author jianghang 2011-9-27 上午11:15:21\n * @version 4.0.0\n */\npublic class TerminMonitorTest extends BaseEventTest {\n\n    private ZkClientx              zookeeper  = null;\n    private ChannelArbitrateEvent  channelEvent;\n    private PipelineArbitrateEvent pipelineEvent;\n\n    private Long                   channelId  = 100L;\n    private Long                   pipelineId = 100L;\n\n    private TerminMonitor          terminMonitor;\n\n    @BeforeClass\n    public void init() {\n        // 初始化节点\n        // mock 配置信息数据\n        Mockit.setUpMock(ArbitrateConfigUtils.class, new Object() {\n\n            @Mock\n            public Channel getChannel(Long pipelineId) {\n                Channel channel = new Channel();\n                channel.setId(channelId);\n                return channel;\n            }\n\n            @Mock\n            public Pipeline getOppositePipeline(Long pipelineId) {\n                Pipeline pipeline = new Pipeline();\n                pipeline.setId(pipelineId);\n                return pipeline;\n            }\n\n        });\n\n        zookeeper = getZookeeper();\n        channelEvent = new ChannelArbitrateEvent();\n        pipelineEvent = new PipelineArbitrateEvent();\n    }\n\n    @BeforeMethod\n    public void setUp() {\n        channelEvent.init(channelId);\n        // 初始化两个pipeline\n        pipelineEvent.init(channelId, pipelineId);\n    }\n\n    @AfterMethod\n    public void tearDown() {\n        MonitorScheduler.unRegister(terminMonitor);\n        // 初始化两个pipeline\n        pipelineEvent.destory(channelId, pipelineId);\n        channelEvent.destory(channelId);\n    }\n\n    @Test\n    public void test_init() {\n        initTermin(1L);\n        initTermin(2L);\n        try {\n            terminMonitor = new TerminMonitor(pipelineId);\n            Long p1 = terminMonitor.waitForProcess();\n            terminMonitor.ack(p1);\n            Long p2 = terminMonitor.waitForProcess();\n            terminMonitor.ack(p2);\n\n            want.bool(p1.equals(1L)).is(true);\n            want.bool(p2.equals(2L)).is(true);\n            terminMonitor.destory();\n        } catch (InterruptedException e) {\n            want.fail();\n        } finally {\n            destoryTermin(1L);\n            destoryTermin(2L);\n        }\n    }\n\n    @Test\n    public void test_dynamic() {\n        initTermin(1L);\n        initTermin(2L);\n        try {\n            terminMonitor = new TerminMonitor(pipelineId);\n            // 开始变化\n            destoryTermin(1L);\n            initTermin(3L);\n            initTermin(1L);\n            sleep();\n            Long p1 = terminMonitor.waitForProcess();\n            terminMonitor.ack(p1);\n            Long p2 = terminMonitor.waitForProcess();\n            terminMonitor.ack(p2);\n            Long p3 = terminMonitor.waitForProcess();\n            terminMonitor.ack(p3);\n            // 一定是按顺序输出\n            want.bool(p1.equals(1L)).is(true);\n            want.bool(p2.equals(2L)).is(true);\n            want.bool(p3.equals(3L)).is(true);\n            terminMonitor.destory();\n        } catch (InterruptedException e) {\n            want.fail();\n        } finally {\n            destoryTermin(1L);\n            destoryTermin(2L);\n            destoryTermin(3L);\n        }\n    }\n\n    private void initTermin(Long processId) {\n        TerminEventData data = new TerminEventData();\n        data.setPipelineId(pipelineId);\n        data.setProcessId(processId);\n        data.setType(TerminType.NORMAL);\n\n        byte[] bytes = JsonUtils.marshalToByte(data);\n        zookeeper.create(StagePathUtils.getTermin(pipelineId, processId), bytes, CreateMode.PERSISTENT);\n\n    }\n\n    private void destoryTermin(Long processId) {\n        zookeeper.delete(StagePathUtils.getTermin(pipelineId, processId));\n    }\n}\n"
  },
  {
    "path": "shared/arbitrate/src/test/java/com/alibaba/otter/shared/arbitrate/setl/monitor/node/NodeMonitorTest.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.setl.monitor.node;\n\nimport java.util.Arrays;\nimport java.util.List;\n\nimport mockit.Mock;\nimport mockit.Mockit;\n\nimport org.testng.annotations.AfterMethod;\nimport org.testng.annotations.BeforeClass;\nimport org.testng.annotations.BeforeMethod;\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.shared.arbitrate.BaseEventTest;\nimport com.alibaba.otter.shared.arbitrate.impl.config.ArbitrateConfigUtils;\nimport com.alibaba.otter.shared.arbitrate.impl.manage.NodeArbitrateEvent;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.monitor.NodeMonitor;\nimport com.alibaba.otter.shared.common.model.config.channel.Channel;\nimport com.alibaba.otter.shared.common.model.config.node.Node;\nimport com.alibaba.otter.shared.common.model.config.pipeline.Pipeline;\n\n/**\n * 测试下Node的监听事件\n * \n * @author jianghang 2011-9-19 下午01:21:08\n * @version 4.0.0\n */\npublic class NodeMonitorTest extends BaseEventTest {\n\n    private Long               channelId  = 100L;\n    // private Long pipelineId = 100L;\n    private NodeArbitrateEvent nodeEvent;\n    private final Node         node1      = new Node();\n    private final Node         node2      = new Node();\n    private final Node         node3      = new Node();\n    private final Node         node4      = new Node();\n    private List<Node>         sourceList = Arrays.asList(node1, node3);\n    private List<Node>         targetList = Arrays.asList(node2, node4);\n\n    @BeforeClass\n    public void init() {\n        // 初始化节点\n        Mockit.setUpMock(ArbitrateConfigUtils.class, new Object() {\n\n            @Mock\n            public Channel getChannel(Long pipelineId) {\n                Channel channel = new Channel();\n                channel.setId(channelId);\n                return channel;\n            }\n\n            @Mock\n            public Pipeline getOppositePipeline(Long pipelineId) {\n                Pipeline pipeline = new Pipeline();\n                pipeline.setId(pipelineId);\n                return pipeline;\n            }\n\n            @Mock\n            public Pipeline getPipeline(Long pipelineId) {\n                Pipeline pipeline = new Pipeline();\n                pipeline.setSelectNodes(sourceList);\n                pipeline.setExtractNodes(sourceList);\n                pipeline.setLoadNodes(targetList);\n                return pipeline;\n            }\n\n        });\n\n        node1.setId(1L);\n        node2.setId(2L);\n        node3.setId(3L);\n        node4.setId(4L);\n\n        getZookeeper();\n        nodeEvent = new NodeArbitrateEvent();\n    }\n\n    @BeforeMethod\n    public void setUp() {\n        nodeEvent.init(node1.getId());\n        nodeEvent.init(node2.getId());\n        nodeEvent.init(node3.getId());\n        nodeEvent.init(node4.getId());\n    }\n\n    @AfterMethod\n    public void tearDown() {\n        nodeEvent.destory(node1.getId());\n        nodeEvent.destory(node2.getId());\n        nodeEvent.destory(node3.getId());\n        nodeEvent.destory(node4.getId());\n    }\n\n    @Test\n    public void testAliveNodes_all() {\n        NodeMonitor nodeMonitor = new NodeMonitor();\n        List<Long> nodes = nodeMonitor.getAliveNodes();\n        want.bool(nodes.size() == 4).is(true);\n        want.number(nodes.get(0)).isEqualTo(node1.getId());\n        want.number(nodes.get(1)).isEqualTo(node2.getId());\n        want.number(nodes.get(2)).isEqualTo(node3.getId());\n        want.number(nodes.get(3)).isEqualTo(node4.getId());\n    }\n\n    @Test\n    public void testAliveNodes_dead() {\n        NodeMonitor nodeMonitor = new NodeMonitor();\n        nodeEvent.destory(node1.getId()); // 关闭一个节点\n        sleep(); // 需要间隔一定的时间，zookeeper需要推送数据到NodeMonitor，时间间隔在10ms以内\n        List<Long> nodes = nodeMonitor.getAliveNodes();\n        want.bool(nodes.size() == 3).is(true);\n        want.number(nodes.get(0)).isEqualTo(node2.getId());\n        want.number(nodes.get(1)).isEqualTo(node3.getId());\n        want.number(nodes.get(2)).isEqualTo(node4.getId());\n\n        nodeEvent.init(node1.getId()); // 开启一个节点\n        nodeEvent.destory(node3.getId()); // 关闭一个节点\n        sleep();\n        nodes = nodeMonitor.getAliveNodes();\n        want.bool(nodes.size() == 3).is(true);\n        want.number(nodes.get(0)).isEqualTo(node1.getId());\n        want.number(nodes.get(1)).isEqualTo(node2.getId());\n        want.number(nodes.get(2)).isEqualTo(node4.getId());\n    }\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/test/java/com/alibaba/otter/shared/arbitrate/setl/monitor/stage/ExtractStageListenerTest.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.setl.monitor.stage;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.shared.arbitrate.impl.ArbitrateConstants;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.ArbitrateFactory;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.zookeeper.monitor.ExtractStageListener;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.zookeeper.monitor.StageMonitor;\nimport com.alibaba.otter.shared.arbitrate.setl.BaseStageTest;\nimport com.google.common.collect.Lists;\n\n/**\n * extract模块的测试\n * \n * @author jianghang 2011-9-22 上午09:16:53\n * @version 4.0.0\n */\npublic class ExtractStageListenerTest extends BaseStageTest {\n\n    @Test\n    public void testProcess_init() {\n        final List<Long> initProcessIds = new ArrayList<Long>();\n        final Map<Long, List<String>> stages = new HashMap<Long, List<String>>();\n        try {\n            Long p1 = initProcess();\n            initStage(p1, ArbitrateConstants.NODE_SELECTED);\n            initStage(p1, ArbitrateConstants.NODE_EXTRACTED);\n\n            Long p2 = initProcess();\n            initStage(p2, ArbitrateConstants.NODE_SELECTED, getData(nid));\n\n            Long p3 = initProcess();\n            initStage(p3, ArbitrateConstants.NODE_SELECTED);\n            initStage(p3, ArbitrateConstants.NODE_EXTRACTED);\n            initStage(p3, ArbitrateConstants.NODE_TRANSFORMED);\n\n            Long p4 = initProcess();\n            initStage(p4, ArbitrateConstants.NODE_SELECTED, getData(nid + 1));\n\n            // 准备清理数据\n            initProcessIds.add(p1);\n            initProcessIds.add(p2);\n            initProcessIds.add(p3);\n            initProcessIds.add(p4);\n\n            List<String> p1Stages = Arrays.asList(ArbitrateConstants.NODE_SELECTED, ArbitrateConstants.NODE_EXTRACTED);\n            stages.put(p1, p1Stages);\n\n            List<String> p2Stages = Arrays.asList(ArbitrateConstants.NODE_SELECTED);\n            stages.put(p2, p2Stages);\n\n            List<String> p3Stages = Arrays.asList(ArbitrateConstants.NODE_SELECTED, ArbitrateConstants.NODE_EXTRACTED,\n                                                  ArbitrateConstants.NODE_TRANSFORMED);\n            stages.put(p3, p3Stages);\n\n            List<String> p4Stages = Arrays.asList(ArbitrateConstants.NODE_SELECTED);\n            stages.put(p4, p4Stages);\n\n            // 进行验证\n            ExtractStageListener extract = new ExtractStageListener(pipelineId);\n            Long processId = extract.waitForProcess();\n            want.number(processId).isEqualTo(p2);\n\n            // 验证下process信息\n            StageMonitor monitor = ArbitrateFactory.getInstance(pipelineId, StageMonitor.class);\n            List<Long> processIds = monitor.getCurrentProcessIds();\n            // 获取下stage信息\n            List<String> currentP1Stages = monitor.getCurrentStages(p1);\n            List<String> currentP2Stages = monitor.getCurrentStages(p2);\n            List<String> currentP3Stages = monitor.getCurrentStages(p3);\n            List<String> currentP4Stages = monitor.getCurrentStages(p4);\n\n            want.collection(processIds).isEqualTo(initProcessIds);\n            want.collection(currentP1Stages).isEqualTo(stages.get(p1));\n            want.collection(currentP2Stages).isEqualTo(stages.get(p2));\n            want.collection(currentP3Stages).isEqualTo(stages.get(p3));\n            want.collection(currentP4Stages).isEqualTo(stages.get(p4));\n            extract.destory();\n            ArbitrateFactory.destory(pipelineId);\n        } catch (InterruptedException e) {\n            want.fail();\n        } finally {\n            for (Long processId : initProcessIds) {\n                List<String> ss = stages.get(processId);\n                for (String stage : ss) {\n                    destoryStage(processId, stage);\n                }\n                destoryProcess(processId);\n            }\n        }\n    }\n\n    @Test\n    public void testProcess_dynamic() {\n        final List<Long> initProcessIds = new ArrayList<Long>();\n        final Map<Long, List<String>> stages = new HashMap<Long, List<String>>();\n        try {\n            Long p1 = initProcess();\n            initStage(p1, ArbitrateConstants.NODE_SELECTED);\n            initStage(p1, ArbitrateConstants.NODE_EXTRACTED);\n            initStage(p1, ArbitrateConstants.NODE_TRANSFORMED);\n\n            Long p2 = initProcess();\n            initStage(p2, ArbitrateConstants.NODE_SELECTED);\n            initStage(p2, ArbitrateConstants.NODE_EXTRACTED);\n\n            Long p3 = initProcess();\n            initStage(p3, ArbitrateConstants.NODE_SELECTED, getData(nid + 1));\n\n            // 初始化信息\n            ExtractStageListener extract = new ExtractStageListener(pipelineId);\n\n            // 开始变化\n            destoryStage(p1, ArbitrateConstants.NODE_SELECTED);\n            destoryStage(p1, ArbitrateConstants.NODE_EXTRACTED);\n            destoryStage(p1, ArbitrateConstants.NODE_TRANSFORMED);\n            destoryProcess(p1);\n            Long p4 = initProcess();\n            initStage(p4, ArbitrateConstants.NODE_SELECTED, getData(nid));\n\n            Long p5 = initProcess();\n            initStage(p5, ArbitrateConstants.NODE_SELECTED, getData(nid + 1));\n\n            Long p6 = initProcess();\n\n            // 准备清理数据\n            initProcessIds.add(p2);\n            initProcessIds.add(p3);\n            initProcessIds.add(p4);\n            initProcessIds.add(p5);\n            initProcessIds.add(p6);\n\n            List<String> p1Stages = Lists.newArrayList();\n            stages.put(p1, p1Stages);\n\n            List<String> p2Stages = Arrays.asList(ArbitrateConstants.NODE_SELECTED, ArbitrateConstants.NODE_EXTRACTED);\n            stages.put(p2, p2Stages);\n\n            List<String> p3Stages = Arrays.asList(ArbitrateConstants.NODE_SELECTED);\n            stages.put(p3, p3Stages);\n\n            List<String> p4Stages = Arrays.asList(ArbitrateConstants.NODE_SELECTED);\n            stages.put(p4, p4Stages);\n\n            List<String> p5Stages = Arrays.asList(ArbitrateConstants.NODE_SELECTED);\n            stages.put(p5, p5Stages);\n\n            List<String> p6Stages = Lists.newArrayList();\n            stages.put(p6, p6Stages);\n\n            sleep();// sleep一下，等待数据同步\n            // 进行验证\n            Long processId = extract.waitForProcess();\n            want.number(processId).isEqualTo(p4);\n\n            // 验证下process信息\n            StageMonitor monitor = ArbitrateFactory.getInstance(pipelineId, StageMonitor.class);\n            List<Long> processIds = monitor.getCurrentProcessIds();\n            // 获取下stage信息\n            List<String> currentP1Stages = monitor.getCurrentStages(p1);\n            List<String> currentP2Stages = monitor.getCurrentStages(p2);\n            List<String> currentP3Stages = monitor.getCurrentStages(p3);\n            List<String> currentP4Stages = monitor.getCurrentStages(p4);\n            List<String> currentP5Stages = monitor.getCurrentStages(p5);\n            List<String> currentP6Stages = monitor.getCurrentStages(p6);\n\n            want.collection(processIds).isEqualTo(initProcessIds);\n            want.collection(currentP1Stages).isEqualTo(stages.get(p1));\n            want.collection(currentP2Stages).isEqualTo(stages.get(p2));\n            want.collection(currentP3Stages).isEqualTo(stages.get(p3));\n            want.collection(currentP4Stages).isEqualTo(stages.get(p4));\n            want.collection(currentP5Stages).isEqualTo(stages.get(p5));\n            want.collection(currentP6Stages).isEqualTo(stages.get(p6));\n            extract.destory();\n            ArbitrateFactory.destory(pipelineId);\n        } catch (InterruptedException e) {\n            want.fail();\n        } finally {\n            for (Long processId : initProcessIds) {\n                List<String> ss = stages.get(processId);\n                for (String stage : ss) {\n                    destoryStage(processId, stage);\n                }\n                destoryProcess(processId);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "shared/arbitrate/src/test/java/com/alibaba/otter/shared/arbitrate/setl/monitor/stage/LoadStageListenerTest.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.setl.monitor.stage;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.shared.arbitrate.impl.ArbitrateConstants;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.ArbitrateFactory;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.zookeeper.monitor.LoadStageListener;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.zookeeper.monitor.StageMonitor;\nimport com.alibaba.otter.shared.arbitrate.setl.BaseStageTest;\nimport com.google.common.collect.Lists;\n\n/**\n * load模块的测试\n * \n * @author jianghang 2011-9-22 上午10:35:18\n * @version 4.0.0\n */\npublic class LoadStageListenerTest extends BaseStageTest {\n\n    @Test\n    public void testProcess_init() {\n        final List<Long> initProcessIds = new ArrayList<Long>();\n        final Map<Long, List<String>> stages = new HashMap<Long, List<String>>();\n        try {\n            // 准备数据\n            Long p1 = initProcess();\n            initStage(p1, ArbitrateConstants.NODE_SELECTED);\n            initStage(p1, ArbitrateConstants.NODE_EXTRACTED);\n            initStage(p1, ArbitrateConstants.NODE_TRANSFORMED, getData(nid));\n\n            Long p2 = initProcess();\n            initStage(p2, ArbitrateConstants.NODE_SELECTED);\n            initStage(p2, ArbitrateConstants.NODE_EXTRACTED);\n            initStage(p2, ArbitrateConstants.NODE_TRANSFORMED, getData(nid + 1));\n\n            // 准备清理数据\n            initProcessIds.add(p1);\n            initProcessIds.add(p2);\n\n            List<String> p1Stages = Arrays.asList(ArbitrateConstants.NODE_SELECTED, ArbitrateConstants.NODE_EXTRACTED,\n                                                  ArbitrateConstants.NODE_TRANSFORMED);\n            stages.put(p1, p1Stages);\n\n            List<String> p2Stages = Arrays.asList(ArbitrateConstants.NODE_SELECTED, ArbitrateConstants.NODE_EXTRACTED,\n                                                  ArbitrateConstants.NODE_TRANSFORMED);\n            stages.put(p2, p2Stages);\n\n            // 进行验证\n            LoadStageListener load = new LoadStageListener(pipelineId);\n            Long processId = load.waitForProcess();\n            want.number(processId).isEqualTo(p1);\n\n            // 验证下process信息\n            StageMonitor monitor = ArbitrateFactory.getInstance(pipelineId, StageMonitor.class);\n            List<Long> processIds = monitor.getCurrentProcessIds();\n            // 获取下stage信息\n            List<String> currentP1Stages = monitor.getCurrentStages(p1);\n            List<String> currentP2Stages = monitor.getCurrentStages(p2);\n\n            want.collection(processIds).isEqualTo(initProcessIds);\n            want.collection(currentP1Stages).isEqualTo(stages.get(p1));\n            want.collection(currentP2Stages).isEqualTo(stages.get(p2));\n            load.destory();\n            ArbitrateFactory.destory(pipelineId);\n        } catch (InterruptedException e) {\n            want.fail();\n        } finally {\n            for (Long processId : initProcessIds) {\n                List<String> ss = stages.get(processId);\n                for (String stage : ss) {\n                    destoryStage(processId, stage);\n                }\n                destoryProcess(processId);\n            }\n        }\n    }\n\n    @Test\n    public void testProcess_dymanic() {\n        final List<Long> initProcessIds = new ArrayList<Long>();\n        final Map<Long, List<String>> stages = new HashMap<Long, List<String>>();\n        try {\n            // 准备数据\n            Long p1 = initProcess();\n            initStage(p1, ArbitrateConstants.NODE_SELECTED);\n            initStage(p1, ArbitrateConstants.NODE_EXTRACTED);\n            initStage(p1, ArbitrateConstants.NODE_TRANSFORMED, getData(nid + 1));\n\n            Long p2 = initProcess();\n            initStage(p2, ArbitrateConstants.NODE_SELECTED);\n            initStage(p2, ArbitrateConstants.NODE_EXTRACTED);\n            initStage(p2, ArbitrateConstants.NODE_TRANSFORMED, getData(nid));\n\n            Long p3 = initProcess();\n            initStage(p3, ArbitrateConstants.NODE_SELECTED);\n            initStage(p3, ArbitrateConstants.NODE_EXTRACTED);\n            initStage(p3, ArbitrateConstants.NODE_TRANSFORMED, getData(nid));\n\n            // 初始化\n            LoadStageListener load = new LoadStageListener(pipelineId);\n\n            // 开始变化\n            // initStage(p1, ArbitrateConstants.NODE_LOADED);\n            destoryStage(p1, ArbitrateConstants.NODE_SELECTED);\n            destoryStage(p1, ArbitrateConstants.NODE_EXTRACTED);\n            destoryStage(p1, ArbitrateConstants.NODE_TRANSFORMED);\n            // destoryStage(p1, ArbitrateConstants.NODE_LOADED);\n            destoryProcess(p1);\n\n            // 准备清理数据\n            initProcessIds.add(p2);\n            initProcessIds.add(p3);\n\n            List<String> p1Stages = Lists.newArrayList();\n            stages.put(p1, p1Stages);\n\n            List<String> p2Stages = Arrays.asList(ArbitrateConstants.NODE_SELECTED, ArbitrateConstants.NODE_EXTRACTED,\n                                                  ArbitrateConstants.NODE_TRANSFORMED);\n            stages.put(p2, p2Stages);\n\n            List<String> p3Stages = Arrays.asList(ArbitrateConstants.NODE_SELECTED, ArbitrateConstants.NODE_EXTRACTED,\n                                                  ArbitrateConstants.NODE_TRANSFORMED);\n            stages.put(p3, p3Stages);\n\n            sleep();\n            // 进行验证\n            Long processId = load.waitForProcess();\n            want.number(processId).isEqualTo(p2);\n\n            // 验证下process信息\n            StageMonitor monitor = ArbitrateFactory.getInstance(pipelineId, StageMonitor.class);\n            List<Long> processIds = monitor.getCurrentProcessIds();\n            // 获取下stage信息\n            List<String> currentP1Stages = monitor.getCurrentStages(p1);\n            List<String> currentP2Stages = monitor.getCurrentStages(p2);\n            List<String> currentP3Stages = monitor.getCurrentStages(p3);\n\n            want.collection(processIds).isEqualTo(initProcessIds);\n            want.collection(currentP1Stages).isEqualTo(stages.get(p1));\n            want.collection(currentP2Stages).isEqualTo(stages.get(p2));\n            want.collection(currentP3Stages).isEqualTo(stages.get(p3));\n\n            // 继续变化\n            // initStage(p2, ArbitrateConstants.NODE_LOADED);\n            destoryStage(p2, ArbitrateConstants.NODE_SELECTED);\n            destoryStage(p2, ArbitrateConstants.NODE_EXTRACTED);\n            destoryStage(p2, ArbitrateConstants.NODE_TRANSFORMED);\n            // destoryStage(p2, ArbitrateConstants.NODE_LOADED);\n            destoryProcess(p2);\n\n            // 准备清理数据\n            initProcessIds.remove(p2);\n\n            p2Stages = Lists.newArrayList();\n            stages.put(p2, p2Stages);\n\n            sleep();\n            // 进行验证\n            processId = load.waitForProcess();\n            want.number(processId).isEqualTo(p3);\n\n            // 验证下process信息\n            processIds = monitor.getCurrentProcessIds();\n            // 获取下stage信息\n            currentP1Stages = monitor.getCurrentStages(p1);\n            currentP2Stages = monitor.getCurrentStages(p2);\n            currentP3Stages = monitor.getCurrentStages(p3);\n\n            want.collection(processIds).isEqualTo(initProcessIds);\n            want.collection(currentP1Stages).isEqualTo(stages.get(p1));\n            want.collection(currentP2Stages).isEqualTo(stages.get(p2));\n            want.collection(currentP3Stages).isEqualTo(stages.get(p3));\n            load.destory();\n            ArbitrateFactory.destory(pipelineId);\n        } catch (InterruptedException e) {\n            want.fail();\n        } finally {\n            for (Long processId : initProcessIds) {\n                List<String> ss = stages.get(processId);\n                for (String stage : ss) {\n                    destoryStage(processId, stage);\n                }\n                destoryProcess(processId);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "shared/arbitrate/src/test/java/com/alibaba/otter/shared/arbitrate/setl/monitor/stage/SelectStageListenerTest.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.setl.monitor.stage;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.concurrent.CountDownLatch;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\n\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.shared.arbitrate.impl.setl.ArbitrateFactory;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.zookeeper.monitor.SelectStageListener;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.zookeeper.monitor.StageMonitor;\nimport com.alibaba.otter.shared.arbitrate.setl.BaseStageTest;\n\n/**\n * 测试下select模块\n * \n * @author jianghang 2011-9-21 下午08:18:10\n * @version 4.0.0\n */\npublic class SelectStageListenerTest extends BaseStageTest {\n\n    @Test\n    public void testProcess_init() {\n        final List<Long> initProcessIds = new ArrayList<Long>();\n        try {\n            initProcess();\n            // initProcessIds.add(p1);\n            SelectStageListener select = new SelectStageListener(pipelineId);\n            sleep();\n\n            Long p2 = select.waitForProcess();\n            Long p3 = select.waitForProcess();\n            Long p4 = select.waitForProcess();\n            initProcessIds.add(p2);\n            initProcessIds.add(p3);\n            initProcessIds.add(p4);\n\n            StageMonitor monitor = ArbitrateFactory.getInstance(pipelineId, StageMonitor.class);\n            List<Long> processIds = monitor.getCurrentProcessIds();\n\n            want.collection(processIds).isEqualTo(initProcessIds);\n            select.destory();\n            ArbitrateFactory.destory(pipelineId);\n        } catch (InterruptedException e) {\n            want.fail();\n        } finally {\n            for (Long processId : initProcessIds) {\n                destoryProcess(processId);\n            }\n\n        }\n    }\n\n    @Test\n    public void testProcess_dymanic() {\n        final List<Long> initProcessIds = new ArrayList<Long>();\n        try {\n            initProcess();\n            SelectStageListener select = new SelectStageListener(pipelineId);\n            final Long p2 = select.waitForProcess();\n            final Long p3 = select.waitForProcess();\n\n            final CountDownLatch count = new CountDownLatch(1);\n            ExecutorService executor = Executors.newCachedThreadPool();\n            executor.submit(new Runnable() {\n\n                public void run() {\n                    sleep();\n                    destoryProcess(p2);\n                    sleep();\n                    destoryProcess(p3);\n                    count.countDown();\n                }\n            });\n\n            Long p4 = select.waitForProcess();\n            Long p5 = select.waitForProcess();\n            initProcessIds.add(p4);\n            initProcessIds.add(p5);\n\n            sleep();\n            StageMonitor monitor = ArbitrateFactory.getInstance(pipelineId, StageMonitor.class);\n            List<Long> processIds = monitor.getCurrentProcessIds();\n            want.collection(processIds).isEqualTo(initProcessIds);\n            count.await();\n            select.destory();\n            ArbitrateFactory.destory(pipelineId);\n        } catch (InterruptedException e) {\n            want.fail();\n        } finally {\n            for (Long processId : initProcessIds) {\n                destoryProcess(processId);\n            }\n\n        }\n    }\n}\n"
  },
  {
    "path": "shared/arbitrate/src/test/java/com/alibaba/otter/shared/arbitrate/setl/monitor/stage/StageMonitorTest.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.setl.monitor.stage;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.CountDownLatch;\n\nimport org.testng.annotations.AfterMethod;\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.shared.arbitrate.impl.ArbitrateConstants;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.ArbitrateFactory;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.monitor.MonitorScheduler;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.zookeeper.monitor.StageListener;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.zookeeper.monitor.StageMonitor;\nimport com.alibaba.otter.shared.arbitrate.setl.BaseStageTest;\n\n/**\n * stage监控测试\n * \n * @author jianghang 2011-9-21 下午02:37:23\n * @version 4.0.0\n */\npublic class StageMonitorTest extends BaseStageTest {\n\n    private StageMonitor monitor;\n\n    @AfterMethod\n    public void dispose() {\n        MonitorScheduler.unRegister(monitor);\n    }\n\n    @Test\n    public void testProcess_static() {// 测试静态的process获取\n        final List<Long> initProcessIds = new ArrayList<Long>();\n        try {\n            Long p1 = initProcess();\n            Long p2 = initProcess();\n            initProcessIds.add(p1);\n            initProcessIds.add(p2);\n            monitor = ArbitrateFactory.getInstance(pipelineId, StageMonitor.class);\n            List<Long> processIds = monitor.getCurrentProcessIds();\n            want.collection(processIds).isEqualTo(initProcessIds);\n\n            MonitorScheduler.unRegister(monitor);\n        } finally {\n            for (Long processId : initProcessIds) {\n                destoryProcess(processId);\n            }\n\n        }\n    }\n\n    @Test\n    public void testProcess_dynamic() {// 测试动态的process获取\n        final List<Long> initProcessIds = new ArrayList<Long>();\n        try {\n            Long p1 = initProcess();\n            Long p2 = initProcess();\n            initProcessIds.add(p1);\n            initProcessIds.add(p2);\n\n            monitor = ArbitrateFactory.getInstance(pipelineId, StageMonitor.class);\n            final CountDownLatch count = new CountDownLatch(1);\n            monitor.addListener(new StageListener() {\n\n                public void stageChannged(Long processId, List<String> stageNodes) {\n\n                }\n\n                public void processChanged(List<Long> processIds) {\n                    count.countDown();\n                    want.collection(processIds).isEqualTo(initProcessIds);\n                }\n\n                @Override\n                public void processTermined(Long processId) {\n\n                }\n            });\n            // 产生一些变化\n            Long p3 = initProcess();\n            initProcessIds.add(p3);\n            destoryProcess(p1);\n            initProcessIds.remove(p1);\n\n            sleep();\n            List<Long> processIds = monitor.getCurrentProcessIds();\n            want.collection(processIds).isEqualTo(initProcessIds);\n\n            try {\n                count.await();// 等待listener响应\n            } catch (InterruptedException e) {\n                want.fail();\n            }\n        } finally {\n            for (Long processId : initProcessIds) {\n                destoryProcess(processId);\n            }\n        }\n    }\n\n    @Test\n    public void testStage_static() {// 测试静态的stage获取\n        final List<Long> initProcessIds = new ArrayList<Long>();\n        final Map<Long, List<String>> stages = new HashMap<Long, List<String>>();\n        try {\n            Long p1 = initProcess();\n            Long p2 = initProcess();\n            initProcessIds.add(p1);\n            initProcessIds.add(p2);\n\n            initStage(p1, ArbitrateConstants.NODE_SELECTED);\n            initStage(p1, ArbitrateConstants.NODE_EXTRACTED);\n            List<String> p1Stages = Arrays.asList(ArbitrateConstants.NODE_SELECTED, ArbitrateConstants.NODE_EXTRACTED);\n            stages.put(p1, p1Stages);\n            initStage(p2, ArbitrateConstants.NODE_SELECTED);\n            List<String> p2Stages = Arrays.asList(ArbitrateConstants.NODE_SELECTED);\n            stages.put(p2, p2Stages);\n\n            StageMonitor monitor = new StageMonitor(pipelineId);\n            List<Long> processIds = monitor.getCurrentProcessIds();\n            // 获取下stage信息\n            List<String> currentP1Stages = monitor.getCurrentStages(p1);\n            List<String> currentP2Stages = monitor.getCurrentStages(p2);\n\n            want.collection(processIds).isEqualTo(initProcessIds);\n            want.collection(currentP1Stages).isEqualTo(stages.get(p1));\n            want.collection(currentP2Stages).isEqualTo(stages.get(p2));\n        } finally {\n            for (Long processId : initProcessIds) {\n                List<String> ss = stages.get(processId);\n                for (String stage : ss) {\n                    destoryStage(processId, stage);\n                }\n                destoryProcess(processId);\n            }\n        }\n    }\n\n    @Test\n    public void testStage_dynamic() {// 测试动态的stage获取\n        final List<Long> initProcessIds = new ArrayList<Long>();\n        final Map<Long, List<String>> stages = new HashMap<Long, List<String>>();\n        try {\n            Long p1 = initProcess();\n            Long p2 = initProcess();\n            initProcessIds.add(p1);\n            initProcessIds.add(p2);\n\n            initStage(p1, ArbitrateConstants.NODE_SELECTED);\n            initStage(p1, ArbitrateConstants.NODE_EXTRACTED);\n            initStage(p2, ArbitrateConstants.NODE_SELECTED);\n\n            monitor = ArbitrateFactory.getInstance(pipelineId, StageMonitor.class);\n            // 产生一些变化\n            Long p3 = initProcess();\n            initProcessIds.add(p3);\n            // p1走完所有的stage后被销毁\n            initStage(p1, ArbitrateConstants.NODE_TRANSFORMED);\n            // initStage(p1, ArbitrateConstants.NODE_LOADED);\n            destoryStage(p1, ArbitrateConstants.NODE_SELECTED);\n            destoryStage(p1, ArbitrateConstants.NODE_EXTRACTED);\n            destoryStage(p1, ArbitrateConstants.NODE_TRANSFORMED);\n            // destoryStage(p1, ArbitrateConstants.NODE_LOADED);\n            destoryProcess(p1);\n            initProcessIds.remove(p1);\n            // p2走了一个stage\n            initStage(p2, ArbitrateConstants.NODE_EXTRACTED);\n\n            List<String> p2Stages = Arrays.asList(ArbitrateConstants.NODE_SELECTED, ArbitrateConstants.NODE_EXTRACTED);\n            stages.put(p1, new ArrayList<String>());\n            stages.put(p2, p2Stages);\n            stages.put(p3, new ArrayList<String>());\n\n            sleep();\n            List<Long> processIds = monitor.getCurrentProcessIds();\n            // 获取下stage信息\n            List<String> currentP1Stages = monitor.getCurrentStages(p1);\n            List<String> currentP2Stages = monitor.getCurrentStages(p2);\n            List<String> currentP3Stages = monitor.getCurrentStages(p3);\n\n            want.collection(processIds).isEqualTo(initProcessIds);\n            want.collection(currentP1Stages).isEqualTo(stages.get(p1));\n            want.collection(currentP2Stages).isEqualTo(stages.get(p2));\n            want.collection(currentP3Stages).isEqualTo(stages.get(p3));\n\n        } finally {\n            for (Long processId : initProcessIds) {\n                List<String> ss = stages.get(processId);\n                for (String stage : ss) {\n                    destoryStage(processId, stage);\n                }\n\n                destoryProcess(processId);\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "shared/arbitrate/src/test/java/com/alibaba/otter/shared/arbitrate/setl/monitor/stage/TransformStageListenerTest.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.setl.monitor.stage;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.shared.arbitrate.impl.ArbitrateConstants;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.ArbitrateFactory;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.zookeeper.monitor.StageMonitor;\nimport com.alibaba.otter.shared.arbitrate.impl.setl.zookeeper.monitor.TransformStageListener;\nimport com.alibaba.otter.shared.arbitrate.setl.BaseStageTest;\nimport com.google.common.collect.Lists;\n\n/**\n * transform模块的测试，和extract测试基本接近\n * \n * @author jianghang 2011-9-22 上午09:16:53\n * @version 4.0.0\n */\npublic class TransformStageListenerTest extends BaseStageTest {\n\n    @Test\n    public void testProcess_init() {\n        final List<Long> initProcessIds = new ArrayList<Long>();\n        final Map<Long, List<String>> stages = new HashMap<Long, List<String>>();\n        try {\n            Long p1 = initProcess();\n            initStage(p1, ArbitrateConstants.NODE_SELECTED);\n            initStage(p1, ArbitrateConstants.NODE_EXTRACTED);\n            initStage(p1, ArbitrateConstants.NODE_TRANSFORMED);\n\n            Long p2 = initProcess();\n            initStage(p2, ArbitrateConstants.NODE_SELECTED);\n            initStage(p2, ArbitrateConstants.NODE_EXTRACTED, getData(nid));\n\n            Long p3 = initProcess();\n            initStage(p3, ArbitrateConstants.NODE_SELECTED);\n            initStage(p3, ArbitrateConstants.NODE_EXTRACTED);\n            initStage(p3, ArbitrateConstants.NODE_TRANSFORMED);\n\n            Long p4 = initProcess();\n            initStage(p4, ArbitrateConstants.NODE_SELECTED);\n            initStage(p4, ArbitrateConstants.NODE_EXTRACTED, getData(nid + 1));\n\n            // 准备清理数据\n            initProcessIds.add(p1);\n            initProcessIds.add(p2);\n            initProcessIds.add(p3);\n            initProcessIds.add(p4);\n\n            List<String> p1Stages = Arrays.asList(ArbitrateConstants.NODE_SELECTED, ArbitrateConstants.NODE_EXTRACTED,\n                                                  ArbitrateConstants.NODE_TRANSFORMED);\n            stages.put(p1, p1Stages);\n\n            List<String> p2Stages = Arrays.asList(ArbitrateConstants.NODE_SELECTED, ArbitrateConstants.NODE_EXTRACTED);\n            stages.put(p2, p2Stages);\n\n            List<String> p3Stages = Arrays.asList(ArbitrateConstants.NODE_SELECTED, ArbitrateConstants.NODE_EXTRACTED,\n                                                  ArbitrateConstants.NODE_TRANSFORMED);\n            stages.put(p3, p3Stages);\n\n            List<String> p4Stages = Arrays.asList(ArbitrateConstants.NODE_SELECTED, ArbitrateConstants.NODE_EXTRACTED);\n            stages.put(p4, p4Stages);\n\n            // 进行验证\n            TransformStageListener transform = new TransformStageListener(pipelineId);\n            Long processId = transform.waitForProcess();\n            want.number(processId).isEqualTo(p2);\n\n            // 验证下process信息\n            StageMonitor monitor = ArbitrateFactory.getInstance(pipelineId, StageMonitor.class);\n            List<Long> processIds = monitor.getCurrentProcessIds();\n            // 获取下stage信息\n            List<String> currentP1Stages = monitor.getCurrentStages(p1);\n            List<String> currentP2Stages = monitor.getCurrentStages(p2);\n            List<String> currentP3Stages = monitor.getCurrentStages(p3);\n            List<String> currentP4Stages = monitor.getCurrentStages(p4);\n\n            want.collection(processIds).isEqualTo(initProcessIds);\n            want.collection(currentP1Stages).isEqualTo(stages.get(p1));\n            want.collection(currentP2Stages).isEqualTo(stages.get(p2));\n            want.collection(currentP3Stages).isEqualTo(stages.get(p3));\n            want.collection(currentP4Stages).isEqualTo(stages.get(p4));\n            transform.destory();\n            ArbitrateFactory.destory(pipelineId);\n        } catch (InterruptedException e) {\n            want.fail();\n        } finally {\n            for (Long processId : initProcessIds) {\n                List<String> ss = stages.get(processId);\n                for (String stage : ss) {\n                    destoryStage(processId, stage);\n                }\n                destoryProcess(processId);\n            }\n        }\n    }\n\n    @Test\n    public void testProcess_dynamic() {\n        final List<Long> initProcessIds = new ArrayList<Long>();\n        final Map<Long, List<String>> stages = new HashMap<Long, List<String>>();\n        try {\n            // 准备数据，准备5个process，一个已经完成extracted，一个满足条件，一个出现error，一个出现end，一个stage满足但不是自己机器处理\n            Long p1 = initProcess();\n            initStage(p1, ArbitrateConstants.NODE_SELECTED);\n            initStage(p1, ArbitrateConstants.NODE_EXTRACTED);\n            initStage(p1, ArbitrateConstants.NODE_TRANSFORMED);\n\n            Long p2 = initProcess();\n            initStage(p2, ArbitrateConstants.NODE_SELECTED);\n            initStage(p2, ArbitrateConstants.NODE_EXTRACTED, getData(nid + 1));\n\n            // 初始化信息\n            TransformStageListener transform = new TransformStageListener(pipelineId);\n\n            // 开始变化\n            destoryStage(p1, ArbitrateConstants.NODE_SELECTED);\n            destoryStage(p1, ArbitrateConstants.NODE_EXTRACTED);\n            destoryStage(p1, ArbitrateConstants.NODE_TRANSFORMED);\n            destoryProcess(p1);\n\n            Long p3 = initProcess();\n            initStage(p3, ArbitrateConstants.NODE_SELECTED);\n            initStage(p3, ArbitrateConstants.NODE_EXTRACTED, getData(nid));\n\n            Long p4 = initProcess();\n\n            // 准备清理数据\n            initProcessIds.add(p2);\n            initProcessIds.add(p3);\n            initProcessIds.add(p4);\n\n            List<String> p1Stages = Lists.newArrayList();\n            stages.put(p1, p1Stages);\n\n            List<String> p2Stages = Arrays.asList(ArbitrateConstants.NODE_SELECTED, ArbitrateConstants.NODE_EXTRACTED);\n            stages.put(p2, p2Stages);\n\n            List<String> p3Stages = Arrays.asList(ArbitrateConstants.NODE_SELECTED, ArbitrateConstants.NODE_EXTRACTED);\n            stages.put(p3, p3Stages);\n\n            List<String> p4Stages = Lists.newArrayList();\n            stages.put(p4, p4Stages);\n\n            sleep();// sleep一下，等待数据同步\n            // 进行验证\n            Long processId = transform.waitForProcess();\n            want.number(processId).isEqualTo(p3);\n\n            // 验证下process信息\n            StageMonitor monitor = ArbitrateFactory.getInstance(pipelineId, StageMonitor.class);\n            List<Long> processIds = monitor.getCurrentProcessIds();\n            // 获取下stage信息\n            List<String> currentP1Stages = monitor.getCurrentStages(p1);\n            List<String> currentP2Stages = monitor.getCurrentStages(p2);\n            List<String> currentP3Stages = monitor.getCurrentStages(p3);\n            List<String> currentP4Stages = monitor.getCurrentStages(p4);\n\n            want.collection(processIds).isEqualTo(initProcessIds);\n            want.collection(currentP1Stages).isEqualTo(stages.get(p1));\n            want.collection(currentP2Stages).isEqualTo(stages.get(p2));\n            want.collection(currentP3Stages).isEqualTo(stages.get(p3));\n            want.collection(currentP4Stages).isEqualTo(stages.get(p4));\n            transform.destory();\n            ArbitrateFactory.destory(pipelineId);\n        } catch (InterruptedException e) {\n            want.fail();\n        } finally {\n            for (Long processId : initProcessIds) {\n                List<String> ss = stages.get(processId);\n                for (String stage : ss) {\n                    destoryStage(processId, stage);\n                }\n                destoryProcess(processId);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "shared/arbitrate/src/test/java/com/alibaba/otter/shared/arbitrate/zookeeper/DistributedLockTest.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.zookeeper;\n\nimport java.util.concurrent.CountDownLatch;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\n\nimport org.apache.commons.lang.math.RandomUtils;\nimport org.apache.zookeeper.KeeperException;\nimport org.testng.annotations.BeforeClass;\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.shared.arbitrate.BaseEventTest;\nimport com.alibaba.otter.shared.arbitrate.impl.zookeeper.lock.DistributedLock;\n\n/**\n * @author jianghang 2011-9-29 下午02:36:19\n * @version 4.0.0\n */\npublic class DistributedLockTest extends BaseEventTest {\n\n    private final String dir = \"/\" + getClass().getSimpleName();\n\n    @BeforeClass\n    public void init() {\n        getZookeeper();\n    }\n\n    @Test\n    protected void test_lock() {\n        ExecutorService exeucotr = Executors.newCachedThreadPool();\n        final int count = 50;\n        final CountDownLatch latch = new CountDownLatch(count);\n        final DistributedLock[] nodes = new DistributedLock[count];\n        for (int i = 0; i < count; i++) {\n            final DistributedLock node = new DistributedLock(dir);\n            nodes[i] = node;\n            exeucotr.submit(new Runnable() {\n\n                public void run() {\n                    try {\n                        node.lock();\n                        Thread.sleep(100 + RandomUtils.nextInt(100));\n                        System.out.println(\"id: \" + node.getId() + \" is leader: \" + node.isOwner());\n                    } catch (InterruptedException e) {\n                        want.fail();\n                    } catch (KeeperException e) {\n                        want.fail();\n                    } finally {\n                        latch.countDown();\n                        try {\n                            node.unlock();\n                        } catch (KeeperException e) {\n                            want.fail();\n                        }\n                    }\n\n                }\n            });\n        }\n\n        try {\n            latch.await();\n        } catch (InterruptedException e) {\n            want.fail();\n        }\n\n        exeucotr.shutdown();\n    }\n\n    @Test\n    protected void test_try_lock() {\n        ExecutorService exeucotr = Executors.newCachedThreadPool();\n        final int count = 50;\n        final CountDownLatch latch = new CountDownLatch(count);\n\n        final DistributedLock[] nodes = new DistributedLock[count];\n        for (int i = 0; i < count; i++) {\n            final DistributedLock node = new DistributedLock(dir);\n            nodes[i] = node;\n            exeucotr.submit(new Runnable() {\n\n                public void run() {\n                    try {\n                        while (node.tryLock() == false) {\n                            Thread.sleep(100 + RandomUtils.nextInt(100));\n                        }\n\n                        System.out.println(\"id: \" + node.getId() + \" is leader: \" + node.isOwner());\n                    } catch (InterruptedException e) {\n                        want.fail();\n                    } catch (KeeperException e) {\n                        want.fail();\n                    } finally {\n                        latch.countDown();\n                        try {\n                            node.unlock();\n                        } catch (KeeperException e) {\n                            want.fail();\n                        }\n                    }\n\n                }\n            });\n        }\n\n        try {\n            latch.await();\n        } catch (InterruptedException e) {\n            want.fail();\n        }\n\n        exeucotr.shutdown();\n    }\n}\n"
  },
  {
    "path": "shared/arbitrate/src/test/java/com/alibaba/otter/shared/arbitrate/zookeeper/DistributedReentrantLockTest.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.zookeeper;\n\nimport java.util.concurrent.CountDownLatch;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\n\nimport org.apache.commons.lang.math.RandomUtils;\nimport org.apache.zookeeper.KeeperException;\nimport org.testng.annotations.BeforeClass;\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.shared.arbitrate.BaseEventTest;\nimport com.alibaba.otter.shared.arbitrate.impl.zookeeper.lock.DistributedReentrantLock;\n\n/**\n * @author jianghang 2011-9-29 下午02:36:19\n * @version 4.0.0\n */\npublic class DistributedReentrantLockTest extends BaseEventTest {\n\n    private final String dir = \"/\" + getClass().getSimpleName();\n\n    @BeforeClass\n    public void init() {\n        getZookeeper();\n    }\n\n    @Test\n    protected void test_lock() {\n        ExecutorService exeucotr = Executors.newCachedThreadPool();\n        final int count = 50;\n        final CountDownLatch latch = new CountDownLatch(count);\n\n        final DistributedReentrantLock lock = new DistributedReentrantLock(dir);\n        for (int i = 0; i < count; i++) {\n            exeucotr.submit(new Runnable() {\n\n                public void run() {\n                    try {\n                        Thread.sleep(1000);\n                        lock.lock();\n                        Thread.sleep(100 + RandomUtils.nextInt(100));\n\n                        System.out.println(\"id: \" + lock.getId() + \" is leader: \" + lock.isOwner());\n                    } catch (InterruptedException e) {\n                        want.fail();\n                    } catch (KeeperException e) {\n                        want.fail();\n                    } finally {\n                        latch.countDown();\n                        try {\n                            lock.unlock();\n                        } catch (KeeperException e) {\n                            want.fail();\n                        }\n                    }\n\n                }\n            });\n        }\n\n        try {\n            latch.await();\n        } catch (InterruptedException e) {\n            want.fail();\n        }\n\n        exeucotr.shutdown();\n    }\n\n    @Test\n    protected void test_try_lock() {\n        ExecutorService exeucotr = Executors.newCachedThreadPool();\n        final int count = 50;\n        final CountDownLatch latch = new CountDownLatch(count);\n\n        final DistributedReentrantLock lock = new DistributedReentrantLock(dir);\n        for (int i = 0; i < count; i++) {\n            exeucotr.submit(new Runnable() {\n\n                public void run() {\n                    try {\n                        while (lock.tryLock() == false) {\n                            Thread.sleep(100 + RandomUtils.nextInt(100));\n                        }\n\n                        System.out.println(\"id: \" + lock.getId() + \" is leader: \" + lock.isOwner());\n                    } catch (InterruptedException e) {\n                        want.fail();\n                    } catch (KeeperException e) {\n                        want.fail();\n                    } finally {\n                        latch.countDown();\n                        try {\n                            lock.unlock();\n                        } catch (KeeperException e) {\n                            want.fail();\n                        }\n                    }\n\n                }\n            });\n        }\n\n        try {\n            latch.await();\n        } catch (InterruptedException e) {\n            want.fail();\n        }\n\n        exeucotr.shutdown();\n    }\n}\n"
  },
  {
    "path": "shared/arbitrate/src/test/java/com/alibaba/otter/shared/arbitrate/zookeeper/ZooKeeperCleanerIntegration.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.zookeeper;\n\nimport java.util.List;\n\nimport org.testng.annotations.BeforeClass;\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.shared.arbitrate.BaseEventTest;\nimport com.alibaba.otter.shared.arbitrate.impl.ArbitrateConstants;\nimport com.alibaba.otter.shared.common.utils.zookeeper.ZkClientx;\n\n/**\n * 递归删除zookeeper下的otter相关的所有节点\n * \n * @author jianghang 2011-9-21 下午03:03:31\n * @version 4.0.0\n */\npublic class ZooKeeperCleanerIntegration extends BaseEventTest {\n\n    private ZkClientx zookeeper = null;\n\n    @BeforeClass\n    public void init() {\n        zookeeper = getZookeeper();\n    }\n\n    @Test\n    public void testCleaner() {\n        cleaner(ArbitrateConstants.NODE_CHANNEL_ROOT);\n        cleaner(ArbitrateConstants.NODE_NID_ROOT);\n\n    }\n\n    private void cleaner(String path) {\n        List<String> nodes = zookeeper.getChildren(path);\n        for (String node : nodes) {\n            cleaner(path + \"/\" + node);\n        }\n        if (path.equals(ArbitrateConstants.NODE_CHANNEL_ROOT) || path.equals(ArbitrateConstants.NODE_NID_ROOT)) {\n            return;\n        } else {\n            System.out.println(\"clean :\" + path);\n            zookeeper.delete(path);\n            return;\n        }\n    }\n}\n"
  },
  {
    "path": "shared/arbitrate/src/test/java/com/alibaba/otter/shared/arbitrate/zookeeper/ZooKeeperClientTest.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.arbitrate.zookeeper;\n\nimport java.io.IOException;\nimport java.lang.reflect.Field;\nimport java.net.InetSocketAddress;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.concurrent.CountDownLatch;\n\nimport mockit.Mock;\nimport mockit.Mockit;\n\nimport org.apache.zookeeper.ClientCnxn;\nimport org.apache.zookeeper.KeeperException;\nimport org.apache.zookeeper.WatchedEvent;\nimport org.apache.zookeeper.Watcher;\nimport org.apache.zookeeper.ZooKeeper;\nimport org.apache.zookeeper.client.HostProvider;\nimport org.apache.zookeeper.client.StaticHostProvider;\nimport org.apache.zookeeper.data.Stat;\nimport org.springframework.util.ReflectionUtils;\nimport org.testng.annotations.BeforeClass;\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.shared.arbitrate.BaseOtterTest;\nimport com.alibaba.otter.shared.arbitrate.impl.manage.NodeSessionExpired;\nimport com.alibaba.otter.shared.arbitrate.impl.zookeeper.ZooKeeperClient;\nimport com.alibaba.otter.shared.common.utils.zookeeper.ZkClientx;\nimport com.alibaba.otter.shared.common.utils.zookeeper.ZooKeeperx;\n\n/**\n * 测试下zookeeper优先集群列表\n * \n * @author jianghang 2011-9-16 下午03:00:01\n * @version 4.0.0\n */\npublic class ZooKeeperClientTest extends BaseOtterTest {\n\n    private static final Field clientCnxnField      = ReflectionUtils.findField(ZooKeeper.class, \"cnxn\");\n    private static final Field hostProviderField    = ReflectionUtils.findField(ClientCnxn.class, \"hostProvider\");\n    private static final Field serverAddressesField = ReflectionUtils.findField(StaticHostProvider.class,\n                                                        \"serverAddresses\");\n    static {\n        ReflectionUtils.makeAccessible(clientCnxnField);\n        ReflectionUtils.makeAccessible(hostProviderField);\n        ReflectionUtils.makeAccessible(serverAddressesField);\n    }\n\n    private String             cluster1             = \"127.0.0.1:2188\";\n    private String             cluster2             = \"127.0.0.1:2188,127.0.0.1:2188\";\n\n    // private String cluster1 = \"127.0.0.1:2181\";\n    // private String cluster2 = \"127.0.0.1:2181,127.0.0.1:2181\";\n\n    @BeforeClass\n    public void initial() {\n        String data = String.format(Locale.ENGLISH, \"%010d\", \"268171150\");\n        System.out.println(data);\n\n        Mockit.setUpMock(ZooKeeperClient.class, new Object() {\n\n            @Mock\n            private List<String> getServerAddrs() {\n                return Arrays.asList(cluster1, cluster2);\n            }\n\n        });\n\n        // 初始化节点\n        Mockit.setUpMock(NodeSessionExpired.class, new Object() {\n\n            @Mock\n            public void notification() {\n                return;\n            }\n\n        });\n    }\n\n    @Test\n    public void testClient() {\n        ZkClientx zk = ZooKeeperClient.getInstance();\n        // 强制获取zk中的地址信息\n        final ZooKeeper zkp = ((ZooKeeperx) zk.getConnection()).getZookeeper();\n        ClientCnxn cnxn = (ClientCnxn) ReflectionUtils.getField(clientCnxnField, zkp);\n        HostProvider hostProvider = (HostProvider) ReflectionUtils.getField(hostProviderField, cnxn);\n        List<InetSocketAddress> serverAddrs = (List<InetSocketAddress>) ReflectionUtils.getField(serverAddressesField,\n            hostProvider);\n        want.number(serverAddrs.size()).isEqualTo(3);\n        String s1 = serverAddrs.get(0).getAddress().getHostAddress() + \":\" + serverAddrs.get(0).getPort();\n        want.string(s1).isEqualTo(cluster1);\n\n        Stat stat = new Stat();\n        try {\n            zkp.getChildren(\"/otter/channel/304/388\", false, stat);\n            System.out.println(stat.getCversion());\n        } catch (KeeperException e2) {\n            // TODO Auto-generated catch block\n            e2.printStackTrace();\n        } catch (InterruptedException e2) {\n            // TODO Auto-generated catch block\n            e2.printStackTrace();\n        }\n\n        // 测试下session timeout\n        final CountDownLatch latch = new CountDownLatch(1);\n        new Thread() {\n\n            public void run() {\n                try {\n                    zkp.getChildren(\"/\", false);\n                } catch (KeeperException e1) {\n                    want.fail();\n                } catch (InterruptedException e1) {\n                    want.fail();\n                }\n                int sessionTimeout = zkp.getSessionTimeout();\n                long sessionId = zkp.getSessionId();\n                byte[] passwd = zkp.getSessionPasswd();\n                try {\n                    ZooKeeper newZk = new ZooKeeper(cluster1, sessionTimeout, new Watcher() {\n\n                        public void process(WatchedEvent event) {\n                            // do nothing\n                        }\n\n                    }, sessionId, passwd);\n\n                    // 用老的sessionId连接上去，进行一次close操作后，让原先正在使用的出现SESSION_EXPIRED\n                    newZk.close();\n                } catch (IOException e) {\n                    want.fail();\n                } catch (InterruptedException e) {\n                    want.fail();\n                }\n\n                latch.countDown();\n            }\n\n        }.start();\n\n        try {\n            latch.await();\n        } catch (InterruptedException e) {\n            want.fail();\n        }\n\n        zk.getChildren(\"/\");\n    }\n}\n"
  },
  {
    "path": "shared/arbitrate/src/test/resources/applicationContext.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<beans xmlns=\"http://www.springframework.org/schema/beans\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n    xmlns:tx=\"http://www.springframework.org/schema/tx\" xmlns:aop=\"http://www.springframework.org/schema/aop\"\n    xmlns:lang=\"http://www.springframework.org/schema/lang\" xmlns:context=\"http://www.springframework.org/schema/context\"\n    xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd\n           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd\n           http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-2.0.xsd\n           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd\n           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd\"\n    default-autowire=\"byName\">\n\t\n\t<!-- properties -->\n\t<bean class=\"com.alibaba.otter.shared.common.utils.spring.PropertyPlaceholderConfigurer\" lazy-init=\"false\">\n\t\t<property name=\"ignoreResourceNotFound\" value=\"true\" />\n\t\t<property name=\"systemPropertiesModeName\" value=\"SYSTEM_PROPERTIES_MODE_OVERRIDE\"/><!-- 允许system覆盖 -->\n\t\t<property name=\"locations\">\n\t\t\t<list>\n\t\t\t\t<value>classpath:otter.properties</value>\n\t\t\t</list>\n\t\t</property>\n\t</bean>\n\t\n\t<import resource=\"classpath:spring/otter-arbitrate-*.xml\"/>\n</beans>"
  },
  {
    "path": "shared/common/pom.xml",
    "content": "<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\">\n\t<modelVersion>4.0.0</modelVersion>\n\t<parent>\n\t\t<groupId>com.alibaba.otter</groupId>\n\t\t<artifactId>shared</artifactId>\n\t\t<version>4.2.19-SNAPSHOT</version>\n\t\t<relativePath>../pom.xml</relativePath>\n\t</parent>\n\t<groupId>com.alibaba.otter</groupId>\n\t<artifactId>shared.common</artifactId>\n\t<packaging>jar</packaging>\n\t<name>common module for otter</name>\n\t<url>http://github.com/alibaba/otter</url>\n\n\t<dependencies>\n\t\t<!-- log -->\n\t\t<dependency>\n\t\t\t<groupId>ch.qos.logback</groupId>\n\t\t\t<artifactId>logback-core</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>ch.qos.logback</groupId>\n\t\t\t<artifactId>logback-classic</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.slf4j</groupId>\n\t\t\t<artifactId>jcl-over-slf4j</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.slf4j</groupId>\n\t\t\t<artifactId>slf4j-api</artifactId>\n\t\t</dependency>\n\t\t<!-- external -->\n\t\t<dependency>\n\t\t\t<groupId>commons-codec</groupId>\n\t\t\t<artifactId>commons-codec</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>commons-io</groupId>\n\t\t\t<artifactId>commons-io</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>commons-beanutils</groupId>\n\t\t\t<artifactId>commons-beanutils</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>commons-lang</groupId>\n\t\t\t<artifactId>commons-lang</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>commons-pool</groupId>\n\t\t\t<artifactId>commons-pool</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>commons-collections</groupId>\n\t\t\t<artifactId>commons-collections</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>oro</groupId>\n\t\t\t<artifactId>oro</artifactId>\n\t\t</dependency>\n\n\t\t<!-- spring -->\n\t\t<dependency>\n\t\t\t<groupId>org.springframework</groupId>\n\t\t\t<artifactId>spring-core</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework</groupId>\n\t\t\t<artifactId>spring-beans</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework</groupId>\n\t\t\t<artifactId>spring-aop</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework</groupId>\n\t\t\t<artifactId>spring-context</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework</groupId>\n\t\t\t<artifactId>spring-context-support</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework</groupId>\n\t\t\t<artifactId>spring-tx</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework</groupId>\n\t\t\t<artifactId>spring-jdbc</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.springframework</groupId>\n\t\t\t<artifactId>spring-test</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>com.alibaba</groupId>\n\t\t\t<artifactId>fastjson</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>com.google.guava</groupId>\n\t\t\t<artifactId>guava</artifactId>\n\t\t</dependency>\n\n\t\t<!-- zk -->\n\t\t<dependency>\n\t\t\t<groupId>org.apache.zookeeper</groupId>\n\t\t\t<artifactId>zookeeper</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>com.101tec</groupId>\n\t\t\t<artifactId>zkclient</artifactId>\n\t\t</dependency>\n\n\t\t<dependency>\n\t\t\t<groupId>org.apache.ddlutils</groupId>\n\t\t\t<artifactId>ddlutils</artifactId>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.jtester</groupId>\n\t\t\t<artifactId>jtester</artifactId>\n\t\t\t<scope>provided</scope>\n\t\t</dependency>\n\t\t<dependency>\n            <groupId>com.google.code.findbugs</groupId>\n            <artifactId>jsr305</artifactId>\n        </dependency>\n\t\t<!-- test dependency -->\n\t\t<dependency>\n\t\t\t<groupId>junit</groupId>\n\t\t\t<artifactId>junit</artifactId>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\t</dependencies>\n\n\t<profiles>\n\t\t<profile>\n\t\t\t<id>Linux</id>\n\t\t\t<activation>\n\t\t\t\t<os>\n\t\t\t\t\t<name>Linux</name>\n\t\t\t\t\t<family>Linux</family>\n\t\t\t\t</os>\n\t\t\t</activation>\n\t\t\t<build>\n\t\t\t\t<plugins>\n\t\t\t\t\t<plugin>\n\t\t\t\t\t\t<artifactId>exec-maven-plugin</artifactId>\n\t\t\t\t\t\t<groupId>org.codehaus.mojo</groupId>\n\t\t\t\t\t\t<executions>\n\t\t\t\t\t\t\t<execution><!-- Run our version calculation script -->\n\t\t\t\t\t\t\t\t<id>Version Calculation</id>\n\t\t\t\t\t\t\t\t<phase>generate-sources</phase>\n\t\t\t\t\t\t\t\t<goals>\n\t\t\t\t\t\t\t\t\t<goal>exec</goal>\n\t\t\t\t\t\t\t\t</goals>\n\t\t\t\t\t\t\t\t<configuration>\n\t\t\t\t\t\t\t\t\t<executable>${basedir}/src/saveVersion.sh</executable>\n\t\t\t\t\t\t\t\t</configuration>\n\t\t\t\t\t\t\t</execution>\n\t\t\t\t\t\t</executions>\n\t\t\t\t\t</plugin>\n\t\t\t\t</plugins>\n\t\t\t</build>\n\t\t</profile>\n\t\t\n\t\t<profile>\n\t\t\t<id>Unix</id>\n\t\t\t<activation>\n\t\t\t\t<os>\n\t\t\t\t\t<name>Unix</name>\n\t\t\t\t\t<family>Unix</family>\n\t\t\t\t</os>\n\t\t\t</activation>\n\t\t\t<build>\n\t\t\t\t<plugins>\n\t\t\t\t\t<plugin>\n\t\t\t\t\t\t<artifactId>exec-maven-plugin</artifactId>\n\t\t\t\t\t\t<groupId>org.codehaus.mojo</groupId>\n\t\t\t\t\t\t<executions>\n\t\t\t\t\t\t\t<execution><!-- Run our version calculation script -->\n\t\t\t\t\t\t\t\t<id>Version Calculation</id>\n\t\t\t\t\t\t\t\t<phase>generate-sources</phase>\n\t\t\t\t\t\t\t\t<goals>\n\t\t\t\t\t\t\t\t\t<goal>exec</goal>\n\t\t\t\t\t\t\t\t</goals>\n\t\t\t\t\t\t\t\t<configuration>\n\t\t\t\t\t\t\t\t\t<executable>${basedir}/src/saveVersion.sh</executable>\n\t\t\t\t\t\t\t\t</configuration>\n\t\t\t\t\t\t\t</execution>\n\t\t\t\t\t\t</executions>\n\t\t\t\t\t</plugin>\n\t\t\t\t</plugins>\n\t\t\t</build>\n\t\t</profile>\n\t</profiles>\n</project>\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/model/autokeeper/AutoKeeperCluster.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.model.autokeeper;\n\nimport java.io.Serializable;\nimport java.util.Date;\nimport java.util.List;\n\nimport org.apache.commons.lang.builder.ToStringBuilder;\n\nimport com.alibaba.otter.shared.common.utils.OtterToStringStyle;\n\n/**\n * zk集群监控对象\n * \n * @author jianghang 2012-9-21 下午01:54:17\n * @version 4.1.0\n */\npublic class AutoKeeperCluster implements Serializable {\n\n    private static final long serialVersionUID = 6065960677054678659L;\n    private Long              id;\n    private String            clusterName;\n    private List<String>      serverList;                             // 机器列表\n    private String            description;                            // 描述\n    private Date              gmtCreate;\n    private Date              gmtModified;\n\n    public Long getId() {\n        return id;\n    }\n\n    public void setId(Long id) {\n        this.id = id;\n    }\n\n    public String getClusterName() {\n        return clusterName;\n    }\n\n    public void setClusterName(String clusterName) {\n        this.clusterName = clusterName;\n    }\n\n    public List<String> getServerList() {\n        return serverList;\n    }\n\n    public void setServerList(List<String> serverList) {\n        this.serverList = serverList;\n    }\n\n    public String getDescription() {\n        return description;\n    }\n\n    public void setDescription(String description) {\n        this.description = description;\n    }\n\n    public Date getGmtCreate() {\n        return gmtCreate;\n    }\n\n    public void setGmtCreate(Date gmtCreate) {\n        this.gmtCreate = gmtCreate;\n    }\n\n    public Date getGmtModified() {\n        return gmtModified;\n    }\n\n    public void setGmtModified(Date gmtModified) {\n        this.gmtModified = gmtModified;\n    }\n\n    public String toString() {\n        return ToStringBuilder.reflectionToString(this, OtterToStringStyle.DEFAULT_STYLE);\n    }\n\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/model/autokeeper/AutoKeeperConnectionStat.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.model.autokeeper;\n\nimport java.util.HashSet;\nimport java.util.Set;\n\n/**\n * 客户端链接统计信息\n * \n * <pre>\n * 命令: echo cons | nc 127.0.0.1 2181\n *  /127.0.0.1:60731[1](queued=0,recved=1506932,sent=2005617,sid=0x6339e69e49350004,lop=GETC,est=1348196892287,to=10000,lcxid=0x16fe71,lzxid=0x1b03dbc434,lresp=1348209252756,llat=0,minlat=0,avglat=0,maxlat=60)\n * </pre>\n * \n * @author jianghang 2012-9-21 下午02:13:19\n * @version 4.1.0\n */\npublic class AutoKeeperConnectionStat extends AutoKeeperStateStat {\n\n    private static final long            serialVersionUID = -786367247388065889L;\n    private String                       sessionId;\n    private String                       serverAddress;\n    private String                       clientAddress;\n    private Set<AutoKeeperWatchStat>     watchStats       = new HashSet<AutoKeeperWatchStat>();    // watcher请求状态\n    private Set<AutoKeeperEphemeralStat> ephemeralStats   = new HashSet<AutoKeeperEphemeralStat>(); // 临时节点状态\n\n    public String getServerAddress() {\n        return serverAddress;\n    }\n\n    public void setServerAddress(String serverAddress) {\n        this.serverAddress = serverAddress;\n    }\n\n    public String getClientAddress() {\n        return clientAddress;\n    }\n\n    public void setClientAddress(String clientAddress) {\n        this.clientAddress = clientAddress;\n    }\n\n    public String getSessionId() {\n        return sessionId;\n    }\n\n    public void setSessionId(String sessionId) {\n        this.sessionId = sessionId;\n    }\n\n    public Set<AutoKeeperWatchStat> getWatchStats() {\n        return watchStats;\n    }\n\n    public void setWatchStats(Set<AutoKeeperWatchStat> watchStats) {\n        this.watchStats = watchStats;\n    }\n\n    public Set<AutoKeeperEphemeralStat> getEphemeralStats() {\n        return ephemeralStats;\n    }\n\n    public void setEphemeralStats(Set<AutoKeeperEphemeralStat> ephemeralStats) {\n        this.ephemeralStats = ephemeralStats;\n    }\n\n    @Override\n    public int hashCode() {\n        final int prime = 31;\n        int result = 1;\n        result = prime * result + ((clientAddress == null) ? 0 : clientAddress.hashCode());\n        result = prime * result + ((serverAddress == null) ? 0 : serverAddress.hashCode());\n        result = prime * result + ((sessionId == null) ? 0 : sessionId.hashCode());\n        return result;\n    }\n\n    @Override\n    public boolean equals(Object obj) {\n        if (this == obj) {\n            return true;\n        }\n        if (obj == null) {\n            return false;\n        }\n        if (!(obj instanceof AutoKeeperConnectionStat)) {\n            return false;\n        }\n        AutoKeeperConnectionStat other = (AutoKeeperConnectionStat) obj;\n        if (clientAddress == null) {\n            if (other.clientAddress != null) {\n                return false;\n            }\n        } else if (!clientAddress.equals(other.clientAddress)) {\n            return false;\n        }\n        if (serverAddress == null) {\n            if (other.serverAddress != null) {\n                return false;\n            }\n        } else if (!serverAddress.equals(other.serverAddress)) {\n            return false;\n        }\n        if (sessionId == null) {\n            if (other.sessionId != null) {\n                return false;\n            }\n        } else if (!sessionId.equals(other.sessionId)) {\n            return false;\n        }\n        return true;\n    }\n\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/model/autokeeper/AutoKeeperEphemeralStat.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.model.autokeeper;\n\nimport java.util.List;\n\n/**\n * ephemeral节点的相关统计\n * \n * <pre>\n * 命令：echo dump | nc 127.0.0.1 2181\n * Sessions with Ephemerals (4):\n * 0x3437a299be280538:\n *     /otter/channel/304/388/mainstem\n *     /otter/node/2\n * </pre>\n * \n * @author jianghang 2012-9-21 下午02:25:00\n * @version 4.1.0\n */\npublic class AutoKeeperEphemeralStat extends AutoKeeperStat {\n\n    private static final long serialVersionUID = 7706173088583901348L;\n    private String            sessionId;\n    private List<String>      paths;\n\n    public String getSessionId() {\n        return sessionId;\n    }\n\n    public void setSessionId(String sessionId) {\n        this.sessionId = sessionId;\n    }\n\n    public List<String> getPaths() {\n        return paths;\n    }\n\n    public void setPaths(List<String> paths) {\n        this.paths = paths;\n    }\n\n    public int hashCode() {\n        final int prime = 31;\n        int result = 1;\n        result = prime * result + ((sessionId == null) ? 0 : sessionId.hashCode());\n        return result;\n    }\n\n    public boolean equals(Object obj) {\n        if (this == obj) {\n            return true;\n        }\n        if (obj == null) {\n            return false;\n        }\n        if (!(obj instanceof AutoKeeperEphemeralStat)) {\n            return false;\n        }\n        AutoKeeperEphemeralStat other = (AutoKeeperEphemeralStat) obj;\n        if (sessionId == null) {\n            if (other.sessionId != null) {\n                return false;\n            }\n        } else if (!sessionId.equals(other.sessionId)) {\n            return false;\n        }\n        return true;\n    }\n\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/model/autokeeper/AutoKeeperQuorumType.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.model.autokeeper;\n\n/**\n * 当前Quorum类型\n * \n * @author jianghang 2012-9-21 下午02:03:44\n * @version 4.1.0\n */\npublic enum AutoKeeperQuorumType {\n\n    LEADER, FOLLOWER, OBSERVER, STANDALONE;\n\n    public boolean isLeader() {\n        return this.equals(AutoKeeperQuorumType.LEADER);\n    }\n\n    public boolean isFollower() {\n        return this.equals(AutoKeeperQuorumType.FOLLOWER);\n    }\n\n    public boolean isObserver() {\n        return this.equals(AutoKeeperQuorumType.OBSERVER);\n    }\n\n    public boolean isStandalone() {\n        return this.equals(AutoKeeperQuorumType.STANDALONE);\n    }\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/model/autokeeper/AutoKeeperServerStat.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.model.autokeeper;\n\nimport java.util.HashSet;\nimport java.util.Set;\n\n/**\n * zk服务对应统计信息\n * \n * <pre>\n * 命令：echo stat | nc 127.0.0.1 2181\n * Zookeeper version: 3.3.6-1366786, built on 07/29/2012 06:22 GMT\n * Clients:\n *  /127.0.0.1:34480[0](queued=0,recved=1,sent=0)\n *  /127.0.0.1:60731[1](queued=0,recved=1853744,sent=2466780)\n * \n * Latency min/avg/max: 0/0/99\n * Received: 1857451\n * Sent: 2470863\n * Outstanding: 0\n * Zxid: 0x1b03de020b\n * Mode: follower\n * Node count: 1758\n * </pre>\n * \n * @author jianghang 2012-9-21 下午02:13:40\n * @version 4.1.0\n */\npublic class AutoKeeperServerStat extends AutoKeeperStateStat {\n\n    private static final long             serialVersionUID = 617926406886982808L;\n    private String                        address;\n    private String                        version;\n    private AutoKeeperQuorumType          quorumType;                                                // 运行类型，leader/follower/observer\n    private long                          nodeCount;                                                 // 总的节点数\n    private Set<AutoKeeperConnectionStat> connectionStats  = new HashSet<AutoKeeperConnectionStat>(); // 客户端链接状态\n\n    public String getAddress() {\n        return address;\n    }\n\n    public void setAddress(String address) {\n        this.address = address;\n    }\n\n    public Set<AutoKeeperConnectionStat> getConnectionStats() {\n        return connectionStats;\n    }\n\n    public void setConnectionStats(Set<AutoKeeperConnectionStat> connectionStats) {\n        this.connectionStats = connectionStats;\n    }\n\n    public AutoKeeperQuorumType getQuorumType() {\n        return quorumType;\n    }\n\n    public void setQuorumType(AutoKeeperQuorumType quorumType) {\n        this.quorumType = quorumType;\n    }\n\n    public long getNodeCount() {\n        return nodeCount;\n    }\n\n    public void setNodeCount(long nodeCount) {\n        this.nodeCount = nodeCount;\n    }\n\n    public String getVersion() {\n        return version;\n    }\n\n    public void setVersion(String version) {\n        this.version = version;\n    }\n\n    public int hashCode() {\n        final int prime = 31;\n        int result = 1;\n        result = prime * result + ((address == null) ? 0 : address.hashCode());\n        return result;\n    }\n\n    public boolean equals(Object obj) {\n        if (this == obj) {\n            return true;\n        }\n        if (obj == null) {\n            return false;\n        }\n        if (!(obj instanceof AutoKeeperServerStat)) {\n            return false;\n        }\n        AutoKeeperServerStat other = (AutoKeeperServerStat) obj;\n        if (address == null) {\n            if (other.address != null) {\n                return false;\n            }\n        } else if (!address.equals(other.address)) {\n            return false;\n        }\n        return true;\n    }\n\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/model/autokeeper/AutoKeeperStat.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.model.autokeeper;\n\nimport java.io.Serializable;\n\nimport org.apache.commons.lang.StringUtils;\nimport org.apache.commons.lang.builder.ToStringBuilder;\n\nimport com.alibaba.otter.shared.common.utils.OtterToStringStyle;\n\npublic class AutoKeeperStat implements Serializable {\n\n    private static final long serialVersionUID = 1593638849202842131L;\n    private String            originalContent;                        // 原始的zk返回的文本信息\n\n    public String getOriginalContent() {\n        return originalContent;\n    }\n\n    public String getHtmlOriginalContent() {\n        return StringUtils.replace(originalContent, \"\\n\", \"<br>\");\n    }\n\n    public void setOriginalContent(String originalContent) {\n        this.originalContent = originalContent;\n    }\n\n    public String toString() {\n        return ToStringBuilder.reflectionToString(this, OtterToStringStyle.DEFAULT_STYLE);\n    }\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/model/autokeeper/AutoKeeperStateStat.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.model.autokeeper;\n\n/**\n * 基本统计信息\n * \n * @author jianghang 2012-9-21 下午02:12:03\n * @version 4.1.0\n */\npublic class AutoKeeperStateStat extends AutoKeeperStat {\n\n    private static final long serialVersionUID = 250466030211280762L;\n    private long              minLatency;\n    private long              maxLatency;\n    private long              avgLatency;\n    private long              queued;                                // 等待队列\n    private long              recved;                                // 接受队列\n    private long              sent;                                  // 发送队列\n\n    public long getMinLatency() {\n        return minLatency;\n    }\n\n    public void setMinLatency(long minLatency) {\n        this.minLatency = minLatency;\n    }\n\n    public long getMaxLatency() {\n        return maxLatency;\n    }\n\n    public void setMaxLatency(long maxLatency) {\n        this.maxLatency = maxLatency;\n    }\n\n    public long getAvgLatency() {\n        return avgLatency;\n    }\n\n    public void setAvgLatency(long avgLatency) {\n        this.avgLatency = avgLatency;\n    }\n\n    public long getQueued() {\n        return queued;\n    }\n\n    public void setQueued(long queued) {\n        this.queued = queued;\n    }\n\n    public long getRecved() {\n        return recved;\n    }\n\n    public void setRecved(long recved) {\n        this.recved = recved;\n    }\n\n    public long getSent() {\n        return sent;\n    }\n\n    public void setSent(long sent) {\n        this.sent = sent;\n    }\n\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/model/autokeeper/AutoKeeperWatchStat.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.model.autokeeper;\n\nimport java.util.List;\n\n/**\n * watch对应的状态 <br/>\n * \n * <pre>\n * 命令1: echo wchc | nc 127.0.0.1 2181\n * ----\n * 0x6339e69e49350004\n *     /otter/channel/304/388/mainstem\n *     /otter/channel/304\n * ----\n * 命令2: echo wchp | nc 127.0.0.1 2181\n * /otter/channel/304/388/mainstem\n *     0x6339e69e49350004\n * /otter/channel/304\n *     0x6339e69e49350004\n * ----\n * 命令3: echo wchs | nc 127.0.0.1 2181\n * 1 connections watching 2 paths\n * Total watches:2\n * </pre>\n * \n * @author jianghang 2012-9-21 下午02:19:30\n * @version 4.1.0\n */\npublic class AutoKeeperWatchStat extends AutoKeeperStat {\n\n    private static final long serialVersionUID = -448913735928277986L;\n    private String            sessionId;\n    private List<String>      paths;\n\n    public String getSessionId() {\n        return sessionId;\n    }\n\n    public void setSessionId(String sessionId) {\n        this.sessionId = sessionId;\n    }\n\n    public List<String> getPaths() {\n        return paths;\n    }\n\n    public void setPaths(List<String> paths) {\n        this.paths = paths;\n    }\n\n    public int hashCode() {\n        final int prime = 31;\n        int result = 1;\n        result = prime * result + ((sessionId == null) ? 0 : sessionId.hashCode());\n        return result;\n    }\n\n    public boolean equals(Object obj) {\n        if (this == obj) {\n            return true;\n        }\n        if (obj == null) {\n            return false;\n        }\n        if (!(obj instanceof AutoKeeperWatchStat)) {\n            return false;\n        }\n        AutoKeeperWatchStat other = (AutoKeeperWatchStat) obj;\n        if (sessionId == null) {\n            if (other.sessionId != null) {\n                return false;\n            }\n        } else if (!sessionId.equals(other.sessionId)) {\n            return false;\n        }\n        return true;\n    }\n\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/model/config/ConfigException.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.model.config;\n\nimport org.apache.commons.lang.exception.NestableRuntimeException;\n\n/**\n * @author jianghang 2011-10-21 下午04:08:37\n * @version 4.0.0\n */\npublic class ConfigException extends NestableRuntimeException {\n\n    private static final long serialVersionUID = -7288830284122672209L;\n\n    private String            errorCode;\n    private String            errorDesc;\n\n    public ConfigException(String errorCode){\n        super(errorCode);\n    }\n\n    public ConfigException(String errorCode, Throwable cause){\n        super(errorCode, cause);\n    }\n\n    public ConfigException(String errorCode, String errorDesc){\n        super(errorCode + \":\" + errorDesc);\n    }\n\n    public ConfigException(String errorCode, String errorDesc, Throwable cause){\n        super(errorCode + \":\" + errorDesc, cause);\n    }\n\n    public ConfigException(Throwable cause){\n        super(cause);\n    }\n\n    public String getErrorCode() {\n        return errorCode;\n    }\n\n    public String getErrorDesc() {\n        return errorDesc;\n    }\n\n    @Override\n    public Throwable fillInStackTrace() {\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/model/config/ConfigHelper.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.model.config;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.apache.commons.lang.StringUtils;\nimport org.apache.oro.text.regex.MalformedPatternException;\nimport org.apache.oro.text.regex.MatchResult;\nimport org.apache.oro.text.regex.Pattern;\nimport org.apache.oro.text.regex.PatternCompiler;\nimport org.apache.oro.text.regex.PatternMatcher;\nimport org.apache.oro.text.regex.Perl5Compiler;\nimport org.apache.oro.text.regex.Perl5Matcher;\nimport org.springframework.util.Assert;\n\nimport com.alibaba.otter.shared.common.model.config.data.DataMedia;\nimport com.alibaba.otter.shared.common.model.config.data.DataMedia.Mode;\nimport com.alibaba.otter.shared.common.model.config.data.DataMedia.ModeValue;\nimport com.alibaba.otter.shared.common.model.config.data.DataMediaPair;\nimport com.alibaba.otter.shared.common.model.config.data.DataMediaSource;\nimport com.alibaba.otter.shared.common.model.config.pipeline.Pipeline;\nimport com.google.common.base.Function;\nimport com.google.common.collect.OtterMigrateMap;\n\n/**\n * 常用的config处理帮助类\n * \n * @author jianghang 2011-10-20 下午05:28:39\n * @version 4.0.0\n */\npublic class ConfigHelper {\n\n    public static final String          MODE_PATTERN = \"(.*)(\\\\[(\\\\d+)\\\\-(\\\\d+)\\\\])(.*)\"; // 匹配offer[1-128]\n    private static Map<String, Pattern> patterns     = OtterMigrateMap.makeComputingMap(new Function<String, Pattern>() {\n\n                                                         public Pattern apply(String input) {\n                                                             PatternCompiler pc = new Perl5Compiler();\n                                                             try {\n                                                                 return pc.compile(input,\n                                                                     Perl5Compiler.CASE_INSENSITIVE_MASK\n                                                                             | Perl5Compiler.READ_ONLY_MASK);\n                                                             } catch (MalformedPatternException e) {\n                                                                 throw new ConfigException(e);\n                                                             }\n                                                         }\n                                                     });\n\n    /**\n     * 根据DataMedia id得到对应的DataMedia\n     */\n    public static DataMedia<? extends DataMediaSource> findDataMedia(Pipeline pipeline, Long id) {\n        Assert.notNull(pipeline);\n        for (DataMediaPair pair : pipeline.getPairs()) {\n            if (pair.getSource().getId().equals(id)) {\n                return pair.getSource();\n            } else if (pair.getTarget().getId().equals(id)) {\n                return pair.getTarget();\n            }\n        }\n\n        throw new ConfigException(\"no such DataMedia , the tableId = \" + id);\n    }\n\n    /**\n     * 根据NameSpace和Name得到对应的DataMedia.\n     */\n    public static DataMedia<? extends DataMediaSource> findSourceDataMedia(Pipeline pipeline, String namespace,\n                                                                           String name) {\n        return findSourceDataMedia(pipeline, namespace, name, false);\n    }\n\n    /**\n     * 根据NameSpace和Name得到对应的DataMedia\n     */\n    public static DataMedia<? extends DataMediaSource> findSourceDataMedia(Pipeline pipeline, String namespace,\n                                                                           String name, boolean notExistReturnNull) {\n        for (DataMediaPair pair : pipeline.getPairs()) {\n            if (isMatch(pair.getSource(), namespace, name)) {\n                return pair.getSource();\n            }\n        }\n\n        if (notExistReturnNull) {\n            return null;\n        } else {\n            throw new ConfigException(\"no such DataMedia , the namespace = \" + namespace + \" name = \" + name);\n        }\n    }\n\n    /**\n     * 根据NameSpace和Name得到对应的DataMediaPair.\n     */\n    public static DataMediaPair findDataMediaPairBySourceName(Pipeline pipeline, String namespace, String name) {\n        return findDataMediaPairBySourceName(pipeline, namespace, name, false);\n    }\n\n    /**\n     * 根据NameSpace和Name得到对应的DataMediaPair\n     */\n    public static DataMediaPair findDataMediaPairBySourceName(Pipeline pipeline, String namespace, String name,\n                                                              boolean notExistReturnNull) {\n        for (DataMediaPair pair : pipeline.getPairs()) {\n            if (isMatch(pair.getSource(), namespace, name)) {\n                return pair;\n            }\n        }\n\n        if (notExistReturnNull) {\n            return null;\n        } else {\n            throw new ConfigException(\"no such DataMedia , the namespace = \" + namespace + \" name = \" + name);\n        }\n    }\n\n    /**\n     * 根据DataMedia id得到对应的DataMediaPair\n     */\n    public static List<DataMediaPair> findDataMediaPairByMediaId(Pipeline pipeline, Long tid) {\n        Assert.notNull(pipeline);\n        List<DataMediaPair> pairs = new ArrayList<DataMediaPair>();\n        for (DataMediaPair pair : pipeline.getPairs()) {\n            if (pair.getSource().getId().equals(tid)) {\n                pairs.add(pair);\n            } else if (pair.getTarget().getId().equals(tid)) {\n                pairs.add(pair);\n            }\n        }\n\n        return pairs;\n    }\n\n    /**\n     * 根据DataMedia id得到对应的DataMediaPair\n     */\n    public static DataMediaPair findDataMediaPair(Pipeline pipeline, Long pairId) {\n        Assert.notNull(pipeline);\n        for (DataMediaPair pair : pipeline.getPairs()) {\n            if (pair.getId().equals(pairId)) {\n                return pair;\n            }\n        }\n\n        throw new ConfigException(\"no such DataMediaPair , the pairId = \" + pairId);\n    }\n\n    /**\n     * 解析DataMedia中的namespace和name，支持offer[1-128]分库的定义\n     */\n    public static ModeValue parseMode(String value) {\n        PatternMatcher matcher = new Perl5Matcher();\n        if (matcher.matches(value, patterns.get(MODE_PATTERN))) {\n            MatchResult matchResult = matcher.getMatch();\n            String prefix = matchResult.group(1);\n            String startStr = matchResult.group(3);\n            String ednStr = matchResult.group(4);\n            int start = Integer.valueOf(startStr);\n            int end = Integer.valueOf(ednStr);\n            String postfix = matchResult.group(5);\n\n            List<String> values = new ArrayList<String>();\n            for (int i = start; i <= end; i++) {\n                StringBuilder builder = new StringBuilder(value.length());\n                String str = String.valueOf(i);\n                // 处理0001类型\n                if (startStr.length() == ednStr.length() && startStr.startsWith(\"0\")) {\n                    str = StringUtils.leftPad(String.valueOf(i), startStr.length(), '0');\n                }\n\n                builder.append(prefix).append(str).append(postfix);\n                values.add(builder.toString());\n            }\n            return new ModeValue(Mode.MULTI, values);\n        } else if (isWildCard(value)) {// 通配符支持\n            return new ModeValue(Mode.WILDCARD, Arrays.asList(value));\n        } else {\n            return new ModeValue(Mode.SINGLE, Arrays.asList(value));\n        }\n    }\n\n    public static String makeSQLPattern(String rawValue) {\n        return makeSQLPattern(parseMode(rawValue), rawValue);\n    }\n\n    public static String makeSQLPattern(ModeValue mode, String rawValue) {\n        Assert.notNull(mode);\n        Assert.notNull(rawValue);\n        if (mode.getMode().isSingle()) {\n            return rawValue;\n        } else if (mode.getMode().isMulti()) {\n            return StringUtils.substringBefore(rawValue, \"[\") + \"%\";\n        } else if (mode.getMode().isWildCard()) {\n            StringBuilder sb = new StringBuilder(rawValue.length());\n            FOR_LOOP: for (int i = 0; i < rawValue.length(); i++) {\n                String charString = String.valueOf(rawValue.charAt(i));\n                if (isWildCard(charString)) {\n                    break FOR_LOOP;\n                } else {\n                    sb.append(rawValue.charAt(i));\n                }\n            }\n            return sb.toString() + \"%\";\n        } else {\n            throw new UnsupportedOperationException(\"unsupport mode:\" + mode.getMode());\n        }\n    }\n\n    public static ModeValueFilter makeModeValueFilter(final ModeValue mode, final String rawValue) {\n        Assert.notNull(mode);\n        Assert.notNull(rawValue);\n        if (mode.getMode().isSingle()) {\n            return new ModeValueFilter() {\n\n                @Override\n                public boolean accept(String value) {\n                    return rawValue.equalsIgnoreCase(value);\n                }\n            };\n        } else if (mode.getMode().isWildCard()) {\n            return new ModeValueFilter() {\n\n                @Override\n                public boolean accept(String value) {\n                    return isWildCardMatch(rawValue, value);\n                }\n            };\n        } else if (mode.getMode().isMulti()) {\n            return new ModeValueFilter() {\n\n                @Override\n                public boolean accept(String value) {\n                    return (indexIgnoreCase(mode.getMultiValue(), value) != -1);\n                }\n            };\n        } else {\n            throw new UnsupportedOperationException(\"unsupport mode:\" + mode.getMode());\n        }\n    }\n\n    // ===================== helper method ================\n\n    private static boolean isMatch(DataMedia dataMedia, String namespace, String name) {\n        boolean isMatch = true;\n        if (StringUtils.isEmpty(namespace)) {\n            isMatch &= StringUtils.isEmpty(dataMedia.getNamespace());\n        } else {\n            if (dataMedia.getNamespaceMode().getMode().isSingle()) {\n                isMatch &= dataMedia.getNamespace().equalsIgnoreCase(namespace);\n            } else if (dataMedia.getNamespaceMode().getMode().isMulti()) {\n                isMatch &= (indexIgnoreCase(dataMedia.getNamespaceMode().getMultiValue(), namespace) != -1);\n            } else if (dataMedia.getNamespaceMode().getMode().isWildCard()) {\n                isMatch &= isWildCardMatch(dataMedia.getNamespace(), namespace);\n            } else {\n                throw new UnsupportedOperationException(\"unsupport mode:\" + dataMedia.getNameMode().getMode());\n            }\n        }\n\n        if (StringUtils.isEmpty(name)) {\n            isMatch &= StringUtils.isEmpty(dataMedia.getName());\n        } else {\n            if (dataMedia.getNameMode().getMode().isSingle()) {\n                isMatch &= dataMedia.getName().equalsIgnoreCase(name);\n            } else if (dataMedia.getNameMode().getMode().isMulti()) {\n                isMatch &= (indexIgnoreCase(dataMedia.getNameMode().getMultiValue(), name) != -1);\n            } else if (dataMedia.getNameMode().getMode().isWildCard()) {\n                isMatch &= isWildCardMatch(dataMedia.getName(), name);\n            } else {\n                throw new UnsupportedOperationException(\"unsupport mode:\" + dataMedia.getNameMode().getMode());\n            }\n        }\n\n        return isMatch;\n    }\n\n    private static boolean isWildCard(String value) {\n        return StringUtils.containsAny(value, new char[] { '*', '?', '+', '|', '(', ')', '{', '}', '[', ']', '\\\\', '$',\n                '^', '.' });\n    }\n\n    private static boolean isWildCardMatch(String matchPattern, String value) {\n        PatternMatcher matcher = new Perl5Matcher();\n        return matcher.matches(value, patterns.get(matchPattern));\n    }\n\n    public static int indexIgnoreCase(List<String> datas, String value) {\n        for (int i = 0; i < datas.size(); i++) {\n            String data = datas.get(i);\n            if (data.equalsIgnoreCase(value)) {\n                return i;\n            }\n\n        }\n\n        return -1;\n    }\n\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/model/config/ModeValueFilter.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.model.config;\n\n/**\n * @author hatterjiang\n */\npublic interface ModeValueFilter {\n\n    boolean accept(final String value);\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/model/config/Transient.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.model.config;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n@Target({ ElementType.FIELD, ElementType.PARAMETER })\n@Retention(RetentionPolicy.RUNTIME)\npublic @interface Transient {\n\n    boolean value() default true;\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/model/config/alarm/AlarmRule.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.model.config.alarm;\n\nimport java.io.Serializable;\nimport java.util.Date;\n\nimport org.apache.commons.lang.builder.ToStringBuilder;\nimport org.apache.commons.lang.builder.ToStringStyle;\n\n/**\n * @author simon 2012-8-23 上午11:04:07\n * @version 4.1.0\n */\npublic class AlarmRule implements Serializable {\n\n    private static final long serialVersionUID = 7500180945297613382L;\n\n    private Long              id;\n    private Long              pipelineId;\n    private AlarmRuleStatus   status;\n    private MonitorName       monitorName;\n    private String            receiverKey;\n    private String            matchValue;\n    private Long              intervalTime;\n    private Date              pauseTime;                              // 标准\n    private Integer           recoveryThresold;                       // 自动恢复阀值\n    private Boolean           autoRecovery;                           // 是否需要自动恢复\n    private String            description;\n    private Date              gmtCreate;\n    private Date              gmtModified;\n\n    public Long getId() {\n        return id;\n    }\n\n    public void setId(Long id) {\n        this.id = id;\n    }\n\n    public Long getPipelineId() {\n        return pipelineId;\n    }\n\n    public void setPipelineId(Long pipelineId) {\n        this.pipelineId = pipelineId;\n    }\n\n    public AlarmRuleStatus getStatus() {\n        return status;\n    }\n\n    public void setStatus(AlarmRuleStatus status) {\n        this.status = status;\n    }\n\n    public MonitorName getMonitorName() {\n        return monitorName;\n    }\n\n    public void setMonitorName(MonitorName monitorName) {\n        this.monitorName = monitorName;\n    }\n\n    public String getReceiverKey() {\n        return receiverKey;\n    }\n\n    public void setReceiverKey(String receiverKey) {\n        this.receiverKey = receiverKey;\n    }\n\n    public Long getIntervalTime() {\n        return intervalTime;\n    }\n\n    public void setIntervalTime(Long intervalTime) {\n        this.intervalTime = intervalTime;\n    }\n\n    public String getMatchValue() {\n        return matchValue;\n    }\n\n    public void setMatchValue(String matchValue) {\n        this.matchValue = matchValue;\n    }\n\n    public String getDescription() {\n        return description;\n    }\n\n    public void setDescription(String description) {\n        this.description = description;\n    }\n\n    public Date getGmtCreate() {\n        return gmtCreate;\n    }\n\n    public void setGmtCreate(Date gmtCreate) {\n        this.gmtCreate = gmtCreate;\n    }\n\n    public Date getGmtModified() {\n        return gmtModified;\n    }\n\n    public void setGmtModified(Date gmtModified) {\n        this.gmtModified = gmtModified;\n    }\n\n    public Boolean getAutoRecovery() {\n        return autoRecovery;\n    }\n\n    public void setAutoRecovery(Boolean autoRecovery) {\n        this.autoRecovery = autoRecovery;\n    }\n\n    public Integer getRecoveryThresold() {\n        return recoveryThresold;\n    }\n\n    public void setRecoveryThresold(Integer recoveryThresold) {\n        this.recoveryThresold = recoveryThresold;\n    }\n\n    public Date getPauseTime() {\n        return pauseTime;\n    }\n\n    public void setPauseTime(Date pauseTime) {\n        this.pauseTime = pauseTime;\n    }\n\n    public boolean isPaused() {\n        return pauseTime != null && new Date().before(pauseTime);\n    }\n\n    @Override\n    public String toString() {\n        return ToStringBuilder.reflectionToString(this, ToStringStyle.DEFAULT_STYLE);\n    }\n\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/model/config/alarm/AlarmRuleStatus.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.model.config.alarm;\n\n/**\n * @author simon 2012-8-29 下午7:34:31\n * @version 4.1.0\n */\n\npublic enum AlarmRuleStatus {\n    ENABLE, PAUSED, DISABLE;\n\n    public boolean isEnable() {\n        return this.equals(AlarmRuleStatus.ENABLE);\n    }\n\n    public boolean isPaused() {\n        return this.equals(AlarmRuleStatus.PAUSED);\n    }\n\n    public boolean isDisable() {\n        return this.equals(AlarmRuleStatus.DISABLE);\n    }\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/model/config/alarm/MonitorName.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.model.config.alarm;\n\n/**\n * @author simon 2012-8-29 下午7:33:53\n * @version 4.1.0\n */\npublic enum MonitorName {\n\n    /** 延迟 */\n    DELAYTIME,\n\n    /** 异常 */\n    EXCEPTION,\n\n    /** Pipeline超时 */\n    PIPELINETIMEOUT,\n\n    /** Process超时 */\n    PROCESSTIMEOUT,\n\n    /** position超时 */\n    POSITIONTIMEOUT;\n\n    public boolean isDelayTime() {\n        return this.equals(MonitorName.DELAYTIME);\n    }\n\n    public boolean isPipelineTimeout() {\n        return this.equals(MonitorName.PIPELINETIMEOUT);\n    }\n\n    public boolean isProcessTimeout() {\n        return this.equals(MonitorName.PROCESSTIMEOUT);\n    }\n\n    public boolean isException() {\n        return this.equals(MonitorName.EXCEPTION);\n    }\n\n    public boolean isPositionTimeout() {\n        return this.equals(MonitorName.POSITIONTIMEOUT);\n    }\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/model/config/channel/Channel.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.model.config.channel;\n\nimport java.io.Serializable;\nimport java.util.Date;\nimport java.util.List;\n\nimport org.apache.commons.lang.builder.ToStringBuilder;\n\nimport com.alibaba.otter.shared.common.model.config.pipeline.Pipeline;\nimport com.alibaba.otter.shared.common.utils.OtterToStringStyle;\n\n/**\n * channel数据对象\n * \n * @author jianghang 2011-8-31 下午07:35:29\n */\npublic class Channel implements Serializable {\n\n    private static final long serialVersionUID = 2345662422309356370L;\n    private Long              id;                                       // 唯一标示id\n    private String            name;\n    private ChannelStatus     status;\n    private String            description;                              // 描述信息\n    private List<Pipeline>    pipelines;\n    private Date              gmtCreate;\n    private Date              gmtModified;\n    private ChannelParameter  parameters       = new ChannelParameter();\n\n    public Long getId() {\n        return id;\n    }\n\n    public void setId(Long id) {\n        this.id = id;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public void setName(String name) {\n        this.name = name;\n    }\n\n    public String getDescription() {\n        return description;\n    }\n\n    public void setDescription(String description) {\n        this.description = description;\n    }\n\n    public List<Pipeline> getPipelines() {\n        return pipelines;\n    }\n\n    public void setPipelines(List<Pipeline> pipelines) {\n        this.pipelines = pipelines;\n    }\n\n    public Date getGmtCreate() {\n        return gmtCreate;\n    }\n\n    public void setGmtCreate(Date gmtCreate) {\n        this.gmtCreate = gmtCreate;\n    }\n\n    public Date getGmtModified() {\n        return gmtModified;\n    }\n\n    public void setGmtModified(Date gmtModified) {\n        this.gmtModified = gmtModified;\n    }\n\n    public ChannelStatus getStatus() {\n        return status;\n    }\n\n    public void setStatus(ChannelStatus status) {\n        this.status = status;\n    }\n\n    public ChannelParameter getParameters() {\n        return parameters;\n    }\n\n    public void setParameters(ChannelParameter parameters) {\n        this.parameters = parameters;\n    }\n\n    @Override\n    public String toString() {\n        return ToStringBuilder.reflectionToString(this, OtterToStringStyle.DEFAULT_STYLE);\n    }\n\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/model/config/channel/ChannelParameter.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.model.config.channel;\n\nimport java.io.Serializable;\n\nimport org.apache.commons.lang.builder.ToStringBuilder;\n\nimport com.alibaba.otter.shared.common.model.config.ConfigException;\nimport com.alibaba.otter.shared.common.utils.OtterToStringStyle;\n\n/**\n * channel相关的参数对象\n * \n * @author jianghang 2011-9-6 下午12:35:05\n */\npublic class ChannelParameter implements Serializable {\n\n    private static final long serialVersionUID            = 298053781205276645L;\n    private Long              channelId;\n    private boolean           enableRemedy                = false;                   // 是否启用冲突补救算法\n    private RemedyAlgorithm   remedyAlgorithm             = RemedyAlgorithm.LOOPBACK; // 冲突补救算法\n    private int               remedyDelayThresoldForMedia = 60;                      // 低于60秒钟的同步延迟，回环补救不反查\n    private SyncMode          syncMode                    = SyncMode.FIELD;          // 同步模式：字段/整条记录\n    private SyncConsistency   syncConsistency             = SyncConsistency.BASE;    // 同步一致性要求\n\n    public boolean isEnableRemedy() {\n        return enableRemedy;\n    }\n\n    public void setEnableRemedy(boolean enableRemedy) {\n        this.enableRemedy = enableRemedy;\n    }\n\n    public int getRemedyDelayThresoldForMedia() {\n        return remedyDelayThresoldForMedia <= 0 ? 10 : remedyDelayThresoldForMedia;\n    }\n\n    public void setRemedyDelayThresoldForMedia(int remedyDelayThresoldForMedia) {\n        this.remedyDelayThresoldForMedia = remedyDelayThresoldForMedia;\n    }\n\n    public static enum RemedyAlgorithm {\n\n        /** 交集覆盖 */\n        INTERSECTION,\n\n        /** 普通模式-全部覆盖 */\n        LOOPBACK;\n\n        public boolean isIntersection() {\n            return this.equals(RemedyAlgorithm.INTERSECTION);\n        }\n\n        public boolean isLoopback() {\n            return this.equals(RemedyAlgorithm.LOOPBACK);\n        }\n\n    }\n\n    public static enum SyncMode {\n        /** 行记录 */\n        ROW(\"R\"),\n        /** 字段记录 */\n        FIELD(\"F\");\n\n        private String value;\n\n        SyncMode(String value){\n            this.value = value;\n        }\n\n        public static SyncMode valuesOf(String value) {\n            SyncMode[] modes = values();\n            for (SyncMode mode : modes) {\n                if (mode.value.equalsIgnoreCase(value)) {\n                    return mode;\n                }\n            }\n\n            throw new ConfigException(\"unknow SyncMode : \" + value);\n        }\n\n        public String getValue() {\n            return value;\n        }\n\n        public void setValue(String value) {\n            this.value = value;\n        }\n\n        public boolean isRow() {\n            return this.equals(SyncMode.ROW);\n        }\n\n        public boolean isField() {\n            return this.equals(SyncMode.FIELD);\n        }\n    }\n\n    public static enum SyncConsistency {\n        /** 基于当前介质最新数据 */\n        MEDIA(\"M\"),\n        /** 基于当前的store记录的数据 */\n        STORE(\"S\"),\n        /** 基于当前的变更value，最终一致性 */\n        BASE(\"B\");\n\n        private String value;\n\n        SyncConsistency(String value){\n            this.value = value;\n        }\n\n        public static SyncConsistency valuesOf(String value) {\n            SyncConsistency[] modes = values();\n            for (SyncConsistency mode : modes) {\n                if (mode.value.equalsIgnoreCase(value)) {\n                    return mode;\n                }\n            }\n\n            throw new ConfigException(\"unknow SyncConsistency : \" + value);\n        }\n\n        public String getValue() {\n            return value;\n        }\n\n        public void setValue(String value) {\n            this.value = value;\n        }\n\n        public boolean isMedia() {\n            return this.equals(SyncConsistency.MEDIA);\n        }\n\n        public boolean isStore() {\n            return this.equals(SyncConsistency.STORE);\n        }\n\n        public boolean isBase() {\n            return this.equals(SyncConsistency.BASE);\n        }\n    }\n\n    public Long getChannelId() {\n        return channelId;\n    }\n\n    public void setChannelId(Long channelId) {\n        this.channelId = channelId;\n    }\n\n    public SyncMode getSyncMode() {\n        return syncMode;\n    }\n\n    public void setSyncMode(SyncMode syncMode) {\n        this.syncMode = syncMode;\n    }\n\n    public SyncConsistency getSyncConsistency() {\n        return syncConsistency;\n    }\n\n    public void setSyncConsistency(SyncConsistency syncConsistency) {\n        this.syncConsistency = syncConsistency;\n    }\n\n    public RemedyAlgorithm getRemedyAlgorithm() {\n        return remedyAlgorithm == null ? RemedyAlgorithm.LOOPBACK : remedyAlgorithm;\n    }\n\n    public void setRemedyAlgorithm(RemedyAlgorithm remedyAlgorithm) {\n        this.remedyAlgorithm = remedyAlgorithm;\n    }\n\n    @Override\n    public String toString() {\n        return ToStringBuilder.reflectionToString(this, OtterToStringStyle.DEFAULT_STYLE);\n    }\n\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/model/config/channel/ChannelStatus.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.model.config.channel;\n\n/**\n * channel的运行状态\n * \n * @author jianghang\n */\npublic enum ChannelStatus {\n    /** 运行中 */\n    START,\n    /** 暂停(临时停止) */\n    PAUSE,\n    /** 停止(长时停止) */\n    STOP;\n\n    public boolean isStart() {\n        return this.equals(ChannelStatus.START);\n    }\n\n    public boolean isPause() {\n        return this.equals(ChannelStatus.PAUSE);\n    }\n\n    public boolean isStop() {\n        return this.equals(ChannelStatus.STOP);\n    }\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/model/config/data/Column.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.model.config.data;\n\nimport java.io.Serializable;\n\nimport org.apache.commons.lang.builder.ToStringBuilder;\n\nimport com.alibaba.otter.shared.common.utils.OtterToStringStyle;\n\n/**\n * 数据介质字段模型，代表某个Key的数据\n * \n * @author simon 2012-4-6 上午10:32:01\n */\npublic class Column implements Serializable {\n\n    private static final long serialVersionUID = -4728005785857916134L;\n    private String            name;                                    // 字段名\n\n    public Column(){\n\n    }\n\n    public Column(String name){\n        this.name = name;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public void setName(String name) {\n        this.name = name;\n    }\n\n    @Override\n    public String toString() {\n        return ToStringBuilder.reflectionToString(this, OtterToStringStyle.DEFAULT_STYLE);\n    }\n\n    @Override\n    public int hashCode() {\n        final int prime = 31;\n        int result = 1;\n        result = prime * result + ((name == null) ? 0 : name.hashCode());\n        return result;\n    }\n\n    @Override\n    public boolean equals(Object obj) {\n        if (this == obj) return true;\n        if (obj == null) return false;\n        if (getClass() != obj.getClass()) return false;\n        Column other = (Column) obj;\n        if (name == null) {\n            if (other.name != null) return false;\n        } else if (!name.equals(other.name)) return false;\n        return true;\n    }\n\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/model/config/data/ColumnGroup.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.model.config.data;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.List;\n\nimport org.apache.commons.lang.builder.ToStringBuilder;\n\nimport com.alibaba.otter.shared.common.utils.OtterToStringStyle;\n\n/**\n * 数据介质字段同步组\n * \n * @author simon 2012-3-31 下午03:54:22\n */\npublic class ColumnGroup implements Serializable {\n\n    private static final long serialVersionUID = 8903835374659632986L;\n    private Long              id;\n    private List<ColumnPair>  columnPairs      = new ArrayList<ColumnPair>();\n    private Long              dataMediaPairId;\n    private Date              gmtCreate;\n    private Date              gmtModified;\n\n    public Long getId() {\n        return id;\n    }\n\n    public void setId(Long id) {\n        this.id = id;\n    }\n\n    public Date getGmtCreate() {\n        return gmtCreate;\n    }\n\n    public void setGmtCreate(Date gmtCreate) {\n        this.gmtCreate = gmtCreate;\n    }\n\n    public Date getGmtModified() {\n        return gmtModified;\n    }\n\n    public void setGmtModified(Date gmtModified) {\n        this.gmtModified = gmtModified;\n    }\n\n    public List<ColumnPair> getColumnPairs() {\n        return columnPairs;\n    }\n\n    public void setColumnPairs(List<ColumnPair> columnPairs) {\n        this.columnPairs = columnPairs;\n    }\n\n    public Long getDataMediaPairId() {\n        return dataMediaPairId;\n    }\n\n    public void setDataMediaPairId(Long dataMediaPairId) {\n        this.dataMediaPairId = dataMediaPairId;\n    }\n\n    @Override\n    public String toString() {\n        return ToStringBuilder.reflectionToString(this, OtterToStringStyle.DEFAULT_STYLE);\n    }\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/model/config/data/ColumnPair.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.model.config.data;\n\nimport java.io.Serializable;\nimport java.util.Date;\n\nimport org.apache.commons.lang.builder.ToStringBuilder;\n\nimport com.alibaba.otter.shared.common.utils.OtterToStringStyle;\n\n/**\n * 字段同步对，描述同步映射时左右两边的字段\n * \n * @author simon 2012-3-31 下午03:50:38\n */\npublic class ColumnPair implements Serializable {\n\n    private static final long serialVersionUID = -7751579969781886333L;\n    private Long              id;\n    private Column            sourceColumn;\n    private Column            targetColumn;\n    private Long              dataMediaPairId;\n    private Date              gmtCreate;\n    private Date              gmtModified;\n\n    public ColumnPair(){\n\n    }\n\n    public ColumnPair(Column sourceColumn, Column targetColumn){\n        this.sourceColumn = sourceColumn;\n        this.targetColumn = targetColumn;\n    }\n\n    public ColumnPair(String sourceColumn, String targetColumn){\n        this.sourceColumn = new Column(sourceColumn);\n        this.targetColumn = new Column(targetColumn);\n    }\n\n    public Long getId() {\n        return id;\n    }\n\n    public void setId(Long id) {\n        this.id = id;\n    }\n\n    public Date getGmtCreate() {\n        return gmtCreate;\n    }\n\n    public void setGmtCreate(Date gmtCreate) {\n        this.gmtCreate = gmtCreate;\n    }\n\n    public Date getGmtModified() {\n        return gmtModified;\n    }\n\n    public void setGmtModified(Date gmtModified) {\n        this.gmtModified = gmtModified;\n    }\n\n    public Column getSourceColumn() {\n        return sourceColumn;\n    }\n\n    public void setSourceColumn(Column sourceColumn) {\n        this.sourceColumn = sourceColumn;\n    }\n\n    public Column getTargetColumn() {\n        return targetColumn;\n    }\n\n    public void setTargetColumn(Column targetColumn) {\n        this.targetColumn = targetColumn;\n    }\n\n    public Long getDataMediaPairId() {\n        return dataMediaPairId;\n    }\n\n    public void setDataMediaPairId(Long dataMediaPairId) {\n        this.dataMediaPairId = dataMediaPairId;\n    }\n\n    @Override\n    public String toString() {\n        return ToStringBuilder.reflectionToString(this, OtterToStringStyle.DEFAULT_STYLE);\n    }\n\n    @Override\n    public int hashCode() {\n        final int prime = 31;\n        int result = 1;\n        result = prime * result + ((dataMediaPairId == null) ? 0 : dataMediaPairId.hashCode());\n        result = prime * result + ((sourceColumn == null) ? 0 : sourceColumn.hashCode());\n        result = prime * result + ((targetColumn == null) ? 0 : targetColumn.hashCode());\n        return result;\n    }\n\n    @Override\n    public boolean equals(Object obj) {\n        if (this == obj) return true;\n        if (obj == null) return false;\n        if (getClass() != obj.getClass()) return false;\n        ColumnPair other = (ColumnPair) obj;\n        if (dataMediaPairId == null) {\n            if (other.dataMediaPairId != null) return false;\n        } else if (!dataMediaPairId.equals(other.dataMediaPairId)) return false;\n        if (sourceColumn == null) {\n            if (other.sourceColumn != null) return false;\n        } else if (!sourceColumn.equals(other.sourceColumn)) return false;\n        if (targetColumn == null) {\n            if (other.targetColumn != null) return false;\n        } else if (!targetColumn.equals(other.targetColumn)) return false;\n        return true;\n    }\n\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/model/config/data/ColumnPairMode.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.model.config.data;\n\n/**\n * @author jianghang 2013-1-6 下午05:32:11\n * @version 4.1.6\n */\npublic enum ColumnPairMode {\n    INCLUDE, EXCLUDE;\n\n    public boolean isInclude() {\n        return this == INCLUDE;\n    }\n\n    public boolean isExclude() {\n        return this == EXCLUDE;\n    }\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/model/config/data/DataMatrix.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.model.config.data;\n\nimport java.io.Serializable;\nimport java.util.Date;\n\nimport org.apache.commons.lang.builder.ToStringBuilder;\n\nimport com.alibaba.otter.shared.common.utils.OtterToStringStyle;\n\n/**\n * media主备配置\n * \n * @author jianghang 2013-4-18 下午12:22:42\n * @version 4.1.8\n */\npublic class DataMatrix implements Serializable {\n\n    private static final long serialVersionUID = 4577662145949358550L;\n    private Long              id;                                     // 唯一标示id\n    private String            groupKey;                               // groupKey\n    private String            master;\n    private String            slave;\n    private String            description;                            // 描述\n    private Date              gmtCreate;                              // 创建时间\n    private Date              gmtModified;                            // 修改时间\n\n    public Long getId() {\n        return id;\n    }\n\n    public void setId(Long id) {\n        this.id = id;\n    }\n\n    public String getGroupKey() {\n        return groupKey;\n    }\n\n    public void setGroupKey(String groupKey) {\n        this.groupKey = groupKey;\n    }\n\n    public String getMaster() {\n        return master;\n    }\n\n    public void setMaster(String master) {\n        this.master = master;\n    }\n\n    public String getSlave() {\n        return slave;\n    }\n\n    public void setSlave(String slave) {\n        this.slave = slave;\n    }\n\n    public String getDescription() {\n        return description;\n    }\n\n    public void setDescription(String description) {\n        this.description = description;\n    }\n\n    public Date getGmtCreate() {\n        return gmtCreate;\n    }\n\n    public void setGmtCreate(Date gmtCreate) {\n        this.gmtCreate = gmtCreate;\n    }\n\n    public Date getGmtModified() {\n        return gmtModified;\n    }\n\n    public void setGmtModified(Date gmtModified) {\n        this.gmtModified = gmtModified;\n    }\n\n    public String toString() {\n        return ToStringBuilder.reflectionToString(this, OtterToStringStyle.DEFAULT_STYLE);\n    }\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/model/config/data/DataMedia.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.model.config.data;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.List;\n\nimport org.apache.commons.lang.builder.ToStringBuilder;\nimport org.springframework.util.Assert;\n\nimport com.alibaba.otter.shared.common.model.config.ConfigHelper;\nimport com.alibaba.otter.shared.common.utils.OtterToStringStyle;\n\n/**\n * 同步数据存储介质\n * \n * @author jianghang 2011-9-2 上午11:25:37\n */\npublic class DataMedia<Source extends DataMediaSource> implements Serializable {\n\n    private static final long   serialVersionUID = -7161158767271516776L;\n    private Long                id;\n    private String              namespace;\n    private String              name;                                    // 介质名称\n    private Source              source;                                  // 介质源地址信息\n    private String              encode;                                  // 编码\n    private Date                gmtCreate;\n    private Date                gmtModified;\n    // 运行时计算出来的属性，避免每次通过ConfigHelper进行正则解析\n    private transient ModeValue nameMode;\n    private transient ModeValue namespaceMode;\n\n    @Deprecated\n    private Mode                mode;                                    // 使用ModeValue进行替代\n\n    public static enum Mode {\n        SINGLE(0), MULTI(1), WILDCARD(3);\n\n        private int value; // datamedia pair定义排序用\n\n        Mode(int value){\n            this.value = value;\n        }\n\n        public int getValue() {\n            return value;\n        }\n\n        public boolean isSingle() {\n            return this == Mode.SINGLE;\n        }\n\n        public boolean isMulti() {\n            return this == Mode.MULTI;\n        }\n\n        public boolean isWildCard() {\n            return this == Mode.WILDCARD;\n        }\n\n    }\n\n    // 模式，比如offer[1-128]代表offer1...offer128，128个配置定义\n    public static class ModeValue implements Serializable {\n\n        private static final long serialVersionUID = 54902778821522113L;\n        private Mode              mode;\n        private List<String>      values           = new ArrayList<String>();\n\n        public ModeValue(Mode mode, List<String> values){\n            this.mode = mode;\n            this.values = values;\n        }\n\n        public String getSingleValue() {\n            Assert.notEmpty(values);\n            return values.get(0);\n        }\n\n        public List<String> getMultiValue() {\n            return values;\n        }\n\n        public Mode getMode() {\n            return mode;\n        }\n\n        public String toString() {\n            return ToStringBuilder.reflectionToString(this, OtterToStringStyle.DEFAULT_STYLE);\n        }\n    }\n\n    public Long getId() {\n        return id;\n    }\n\n    public void setId(Long id) {\n        this.id = id;\n    }\n\n    public String getNamespace() {\n        return namespace;\n    }\n\n    public void setNamespace(String namespace) {\n        this.namespace = namespace;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public void setName(String name) {\n        this.name = name;\n    }\n\n    public Date getGmtCreate() {\n        return gmtCreate;\n    }\n\n    public void setGmtCreate(Date gmtCreate) {\n        this.gmtCreate = gmtCreate;\n    }\n\n    public Date getGmtModified() {\n        return gmtModified;\n    }\n\n    public void setGmtModified(Date gmtModified) {\n        this.gmtModified = gmtModified;\n    }\n\n    public Source getSource() {\n        return source;\n    }\n\n    public void setSource(Source source) {\n        this.source = source;\n    }\n\n    public String getEncode() {\n        return encode;\n    }\n\n    public void setEncode(String encode) {\n        this.encode = encode;\n    }\n\n    public Mode getMode() {\n        if (mode == null) {// 重新计算下\n            mode = ConfigHelper.parseMode(namespace).getMode();\n        }\n\n        return mode;\n    }\n\n    public void setMode(Mode mode) {\n        this.mode = mode;\n    }\n\n    public ModeValue getNameMode() {\n        if (nameMode == null) {\n            nameMode = ConfigHelper.parseMode(name);\n        }\n\n        return nameMode;\n    }\n\n    public void setNameMode(ModeValue nameMode) {\n        this.nameMode = nameMode;\n    }\n\n    public ModeValue getNamespaceMode() {\n        if (namespaceMode == null) {\n            namespaceMode = ConfigHelper.parseMode(namespace);\n        }\n\n        return namespaceMode;\n    }\n\n    public void setNamespaceMode(ModeValue namespaceMode) {\n        this.namespaceMode = namespaceMode;\n    }\n\n    @Override\n    public String toString() {\n        return ToStringBuilder.reflectionToString(this, OtterToStringStyle.DEFAULT_STYLE);\n    }\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/model/config/data/DataMediaPair.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.model.config.data;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.List;\n\nimport org.apache.commons.lang.builder.ToStringBuilder;\n\nimport com.alibaba.otter.shared.common.utils.OtterToStringStyle;\n\n/**\n * 介质A -> 介质B 的同步映射关系对\n * \n * @author jianghang 2011-9-2 上午11:41:33\n */\npublic class DataMediaPair implements Serializable {\n\n    private static final long serialVersionUID = 6173105172139714032L;\n    private Long              id;\n    private Long              pipelineId;                                     // 同步任务id\n    private DataMedia         source;\n    private DataMedia         target;\n    private Long              pullWeight;                                     // 介质A中获取数据的权重\n    private Long              pushWeight;                                     // 介质B中写入数据的权重\n    private ExtensionData     resolverData;                                   // 关联数据解析类\n    private ExtensionData     filterData;                                     // filter解析类\n    private ColumnPairMode    columnPairMode   = ColumnPairMode.INCLUDE;\n    private List<ColumnPair>  columnPairs      = new ArrayList<ColumnPair>();\n    private List<ColumnGroup> columnGroups     = new ArrayList<ColumnGroup>();\n    private Date              gmtCreate;\n    private Date              gmtModified;\n\n    public Long getId() {\n        return id;\n    }\n\n    public void setId(Long id) {\n        this.id = id;\n    }\n\n    public Long getPipelineId() {\n        return pipelineId;\n    }\n\n    public void setPipelineId(Long pipelineId) {\n        this.pipelineId = pipelineId;\n    }\n\n    public DataMedia getSource() {\n        return source;\n    }\n\n    public void setSource(DataMedia source) {\n        this.source = source;\n    }\n\n    public DataMedia getTarget() {\n        return target;\n    }\n\n    public void setTarget(DataMedia target) {\n        this.target = target;\n    }\n\n    public Long getPullWeight() {\n        return pullWeight;\n    }\n\n    public void setPullWeight(Long pullWeight) {\n        this.pullWeight = pullWeight;\n    }\n\n    public Long getPushWeight() {\n        return pushWeight;\n    }\n\n    public ExtensionData getResolverData() {\n        return resolverData;\n    }\n\n    public void setResolverData(ExtensionData resolverData) {\n        this.resolverData = resolverData;\n    }\n\n    public void setPushWeight(Long pushWeight) {\n        this.pushWeight = pushWeight;\n    }\n\n    public Date getGmtCreate() {\n        return gmtCreate;\n    }\n\n    public void setGmtCreate(Date gmtCreate) {\n        this.gmtCreate = gmtCreate;\n    }\n\n    public Date getGmtModified() {\n        return gmtModified;\n    }\n\n    public void setGmtModified(Date gmtModified) {\n        this.gmtModified = gmtModified;\n    }\n\n    public ExtensionData getFilterData() {\n        return filterData;\n    }\n\n    public void setFilterData(ExtensionData filterData) {\n        this.filterData = filterData;\n    }\n\n    public List<ColumnPair> getColumnPairs() {\n        return columnPairs;\n    }\n\n    public void setColumnPairs(List<ColumnPair> columnPairs) {\n        this.columnPairs = columnPairs;\n    }\n\n    public List<ColumnGroup> getColumnGroups() {\n        return columnGroups;\n    }\n\n    public void setColumnGroups(List<ColumnGroup> columnGroups) {\n        this.columnGroups = columnGroups;\n    }\n\n    public boolean isExistFilter() {\n        return (filterData != null && filterData.isNotBlank());\n    }\n\n    public boolean isExistResolver() {\n        return (resolverData != null && resolverData.isNotBlank());\n    }\n\n    public ColumnPairMode getColumnPairMode() {\n        return columnPairMode == null ? ColumnPairMode.INCLUDE : columnPairMode;\n    }\n\n    public void setColumnPairMode(ColumnPairMode columnPairMode) {\n        this.columnPairMode = columnPairMode;\n    }\n\n    public String toString() {\n        return ToStringBuilder.reflectionToString(this, OtterToStringStyle.DEFAULT_STYLE);\n    }\n\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/model/config/data/DataMediaPairComparable.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.model.config.data;\n\nimport java.util.Comparator;\n\n/**\n * 按照源表的dataMeia的mode模式进行排序\n * \n * @author jianghang 2013-9-13 下午2:10:52\n * @since 4.2.3\n */\npublic class DataMediaPairComparable implements Comparator<DataMediaPair> {\n\n    public int compare(DataMediaPair o1, DataMediaPair o2) {\n        int s1 = o1.getSource().getNamespaceMode().getMode().getValue()\n                 + o1.getSource().getNameMode().getMode().getValue();\n\n        int s2 = o2.getSource().getNamespaceMode().getMode().getValue()\n                 + o2.getSource().getNameMode().getMode().getValue();\n\n        if (s1 < s2) {\n            return -1;\n        } else if (s1 > s2) {\n            return 1;\n        } else {\n            return o1.getId().compareTo(o2.getId());\n        }\n    }\n\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/model/config/data/DataMediaSource.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.model.config.data;\n\nimport java.io.Serializable;\nimport java.util.Date;\n\nimport org.apache.commons.lang.builder.ToStringBuilder;\n\nimport com.alibaba.otter.shared.common.utils.OtterToStringStyle;\n\n/**\n * 数据介质源信息描述\n * \n * @author jianghang 2011-9-2 上午11:28:21\n */\npublic class DataMediaSource implements Serializable {\n\n    private static final long serialVersionUID = -7653632703273608373L;\n    private Long              id;\n    private String            name;\n    private DataMediaType     type;\n    private String            encode;\n    private Date              gmtCreate;\n    private Date              gmtModified;\n\n    public Long getId() {\n        return id;\n    }\n\n    public void setId(Long id) {\n        this.id = id;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public void setName(String name) {\n        this.name = name;\n    }\n\n    public DataMediaType getType() {\n        return type;\n    }\n\n    public void setType(DataMediaType type) {\n        this.type = type;\n    }\n\n    public Date getGmtCreate() {\n        return gmtCreate;\n    }\n\n    public void setGmtCreate(Date gmtCreate) {\n        this.gmtCreate = gmtCreate;\n    }\n\n    public Date getGmtModified() {\n        return gmtModified;\n    }\n\n    public void setGmtModified(Date gmtModified) {\n        this.gmtModified = gmtModified;\n    }\n\n    public String getEncode() {\n        return encode;\n    }\n\n    public void setEncode(String encode) {\n        this.encode = encode;\n    }\n\n    @Override\n    public int hashCode() {\n        final int prime = 31;\n        int result = 1;\n        result = prime * result + ((encode == null) ? 0 : encode.hashCode());\n        result = prime * result + ((id == null) ? 0 : id.hashCode());\n        result = prime * result + ((name == null) ? 0 : name.hashCode());\n        result = prime * result + ((type == null) ? 0 : type.hashCode());\n        return result;\n    }\n\n    @Override\n    public boolean equals(Object obj) {\n        if (this == obj) return true;\n        if (obj == null) return false;\n        if (getClass() != obj.getClass()) return false;\n        DataMediaSource other = (DataMediaSource) obj;\n        if (encode == null) {\n            if (other.encode != null) return false;\n        } else if (!encode.equals(other.encode)) return false;\n        if (id == null) {\n            if (other.id != null) return false;\n        } else if (!id.equals(other.id)) return false;\n        if (name == null) {\n            if (other.name != null) return false;\n        } else if (!name.equals(other.name)) return false;\n        if (type != other.type) return false;\n        return true;\n    }\n\n    @Override\n    public String toString() {\n        return ToStringBuilder.reflectionToString(this, OtterToStringStyle.DEFAULT_STYLE);\n    }\n\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/model/config/data/DataMediaType.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.model.config.data;\n\n/**\n * @author jianghang 2011-9-2 上午11:36:21\n */\npublic enum DataMediaType {\n    /** mysql DB */\n    MYSQL,\n    /** oracle DB */\n    ORACLE,\n    /** cobar */\n    COBAR,\n    /** tddl */\n    TDDL,\n    /** cache */\n    MEMCACHE,\n    /** mq */\n    MQ,\n    /** napoli */\n    NAPOLI,\n    /** diamond push for us */\n    DIAMOND_PUSH;\n\n    public boolean isMysql() {\n        return this == DataMediaType.MYSQL;\n    }\n\n    public boolean isOracle() {\n        return this == DataMediaType.ORACLE;\n    }\n\n    public boolean isTddl() {\n        return this == DataMediaType.TDDL;\n    }\n\n    public boolean isCobar() {\n        return this == DataMediaType.COBAR;\n    }\n\n    public boolean isMemcache() {\n        return this == DataMediaType.MEMCACHE;\n    }\n\n    public boolean isMq() {\n        return this == DataMediaType.MQ;\n    }\n\n    public boolean isNapoli() {\n        return this == DataMediaType.NAPOLI;\n    }\n\n    public boolean isDiamondPush() {\n        return this == DataMediaType.DIAMOND_PUSH;\n    }\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/model/config/data/ExtensionData.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.model.config.data;\n\nimport java.io.Serializable;\n\nimport org.apache.commons.lang.StringUtils;\n\n/**\n * @author simon 2012-10-16 下午7:46:42\n * @version 4.1.0\n */\npublic class ExtensionData implements Serializable, Comparable<ExtensionData> {\n\n    private static final long serialVersionUID = 5697237591471377596L;\n    private ExtensionDataType extensionDataType;\n    private String            clazzPath;\n    private String            sourceText;\n    private Long              timestamp;\n\n    public ExtensionData(){\n        this.timestamp = System.currentTimeMillis();\n    }\n\n    public ExtensionDataType getExtensionDataType() {\n        return extensionDataType;\n    }\n\n    public void setExtensionDataType(ExtensionDataType extensionDataType) {\n        this.extensionDataType = extensionDataType;\n    }\n\n    public String getClazzPath() {\n        return clazzPath;\n    }\n\n    public void setClazzPath(String clazzPath) {\n        this.clazzPath = clazzPath;\n    }\n\n    public String getSourceText() {\n        return sourceText;\n    }\n\n    public void setSourceText(String sourceText) {\n        this.sourceText = sourceText;\n    }\n\n    public Long getTimestamp() {\n        return timestamp;\n    }\n\n    public void setTimestamp(Long timestamp) {\n        this.timestamp = timestamp;\n    }\n\n    public boolean isBlank() {\n        return !isNotBlank();\n    }\n\n    public boolean isNotBlank() {\n        return (StringUtils.isNotBlank(clazzPath) || StringUtils.isNotBlank(sourceText));\n    }\n\n    @Override\n    public int compareTo(ExtensionData o) {\n        if (this.timestamp < o.getTimestamp()) {\n            return -1;\n        } else if (this.timestamp > o.getTimestamp()) {\n            return 1;\n        } else {\n            return 0;\n        }\n    }\n\n    @Override\n    public int hashCode() {\n        final int prime = 31;\n        int result = 1;\n        result = prime * result + ((clazzPath == null) ? 0 : clazzPath.hashCode());\n        result = prime * result + ((extensionDataType == null) ? 0 : extensionDataType.hashCode());\n        result = prime * result + ((sourceText == null) ? 0 : sourceText.hashCode());\n        return result;\n    }\n\n    @Override\n    public boolean equals(Object obj) {\n        if (this == obj) return true;\n        if (obj == null) return false;\n        if (getClass() != obj.getClass()) return false;\n        ExtensionData other = (ExtensionData) obj;\n        if (clazzPath == null) {\n            if (other.clazzPath != null) return false;\n        } else if (!clazzPath.equals(other.clazzPath)) return false;\n        if (extensionDataType != other.extensionDataType) return false;\n        if (sourceText == null) {\n            if (other.sourceText != null) return false;\n        } else if (!sourceText.equals(other.sourceText)) return false;\n        return true;\n    }\n\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/model/config/data/ExtensionDataType.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.model.config.data;\n\n/**\n * 类ResolverType.java的实现描述：TODO 类实现描述\n * \n * @author simon 2012-10-16 下午7:47:01\n * @version 4.1.0\n */\npublic enum ExtensionDataType {\n    CLAZZ, SOURCE;\n\n    public boolean isClazz() {\n        return this.equals(ExtensionDataType.CLAZZ);\n    }\n\n    public boolean isSource() {\n        return this.equals(ExtensionDataType.SOURCE);\n    }\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/model/config/data/db/DbDataMedia.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.model.config.data.db;\n\nimport com.alibaba.otter.shared.common.model.config.data.DataMedia;\n\n/**\n * 基于数据库的介质\n * \n * @author jianghang\n */\npublic class DbDataMedia extends DataMedia<DbMediaSource> {\n\n    private static final long serialVersionUID = 4466168734406777366L;\n\n    // TODO: 允许定义同步或者过滤的字段信息\n\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/model/config/data/db/DbMediaSource.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.model.config.data.db;\n\nimport java.util.Properties;\n\nimport com.alibaba.otter.shared.common.model.config.data.DataMediaSource;\n\n/**\n * 基于db的source信息\n * \n * @author jianghang\n */\npublic class DbMediaSource extends DataMediaSource {\n\n    private static final long serialVersionUID = 2840851954936715456L;\n    // private static final String MYSQL_DRIVER = \"com.mysql.jdbc.Driver\";\n    // private static final String ORACLE_DRIVER_PROXY = \"com.alibaba.china.jdbc.SimpleDriver\";\n    // private static final String ORACLE_DRIVER = \"oracle.jdbc.driver.OracleDriver\";\n    private String            url;\n    private String            username;\n    private String            password;\n    private String            driver;\n    private Properties        properties;\n\n    public String getUrl() {\n        return url;\n    }\n\n    public void setUrl(String url) {\n        this.url = url;\n    }\n\n    public String getUsername() {\n        return username;\n    }\n\n    public void setUsername(String username) {\n        this.username = username;\n    }\n\n    public String getPassword() {\n        return password;\n    }\n\n    public void setPassword(String password) {\n        this.password = password;\n    }\n\n    public String getDriver() {\n        // DataMediaType type = getType();\n        // if (type != null) {\n        // if (type.isMysql()) {\n        // return MYSQL_DRIVER;\n        // } else if (type.isOracle()) {\n        // return ORACLE_DRIVER_PROXY;\n        // }\n        // }\n        return driver;\n    }\n\n    public void setDriver(String driver) {\n        this.driver = driver;\n    }\n\n    public Properties getProperties() {\n        return properties;\n    }\n\n    public void setProperties(Properties properties) {\n        this.properties = properties;\n    }\n\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/model/config/data/mq/MqDataMedia.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.model.config.data.mq;\n\nimport com.alibaba.otter.shared.common.model.config.data.DataMedia;\n\n/**\n * NapoliSender对象的实现\n * \n * @author simon 2012-6-19 下午10:49:08\n * @version 4.1.0\n */\npublic class MqDataMedia extends DataMedia<MqMediaSource> {\n\n    private static final long serialVersionUID = 1347886915428919398L;\n\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/model/config/data/mq/MqMediaSource.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.model.config.data.mq;\n\nimport com.alibaba.otter.shared.common.model.config.data.DataMediaSource;\n\n/**\n * NapoliConnector对象的实现\n * \n * @author simon 2012-6-19 下午10:49:25\n * @version 4.1.0\n */\npublic class MqMediaSource extends DataMediaSource {\n\n    private static final long serialVersionUID = -1699317916850638142L;\n    private String            url;\n    private String            storePath;\n\n    public String getUrl() {\n        return url;\n    }\n\n    public void setUrl(String url) {\n        this.url = url;\n    }\n\n    public String getStorePath() {\n        return storePath;\n    }\n\n    public void setStorePath(String storePath) {\n        this.storePath = storePath;\n    }\n\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/model/config/enums/AreaType.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.model.config.enums;\n\n/**\n * 标记一下地区信息，因为不同地区会有不同的配置信息\n * \n * @author jianghang 2013-6-5 下午04:14:05\n * @version 4.1.9\n */\npublic enum AreaType {\n\n    HZ, US;\n\n    public boolean isHzArea() {\n        return this.equals(AreaType.HZ);\n    }\n\n    public boolean isUsArea() {\n        return this.equals(AreaType.US);\n    }\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/model/config/enums/StageType.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.model.config.enums;\n\n/**\n * S.E.T.L的阶段类型\n * \n * @author jianghang 2011-9-8 下午12:40:29\n */\npublic enum StageType {\n\n    SELECT, EXTRACT, TRANSFORM, LOAD;\n\n    public boolean isSelect() {\n        return this.equals(StageType.SELECT);\n    }\n\n    public boolean isExtract() {\n        return this.equals(StageType.EXTRACT);\n    }\n\n    /**\n     * transform和load一定会同时出现\n     */\n    public boolean isTransform() {\n        return this.equals(StageType.TRANSFORM);\n    }\n\n    /**\n     * transform和load一定会同时出现\n     */\n    public boolean isLoad() {\n        return this.equals(StageType.LOAD);\n    }\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/model/config/node/Node.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.model.config.node;\n\nimport java.io.Serializable;\nimport java.util.Date;\n\nimport org.apache.commons.lang.builder.ToStringBuilder;\n\nimport com.alibaba.otter.shared.common.utils.OtterToStringStyle;\n\n/**\n * Node数据对象\n * \n * @author jianghang 2011-8-19 上午10:19:40\n */\npublic class Node implements Serializable {\n\n    private static final long serialVersionUID = 1427704645257914286L;\n    private Long              id;                                     // 唯一标示id\n    private String            name;                                   // 机器名字\n    private String            ip;                                     // 机器ip\n    private Long              port;                                   // 和manager对应的通讯端口\n    private NodeStatus        status;                                 // 对应状态\n    private String            description;                            // 详细描述\n    private NodeParameter     parameters       = new NodeParameter(); // node对应参数信息\n    private Date              gmtCreate;                              // 创建时间\n    private Date              gmtModified;                            // 修改时间\n\n    public Long getId() {\n        return id;\n    }\n\n    public void setId(Long id) {\n        this.id = id;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public void setName(String name) {\n        this.name = name;\n    }\n\n    public String getIp() {\n        return ip;\n    }\n\n    public void setIp(String ip) {\n        this.ip = ip;\n    }\n\n    public Long getPort() {\n        return port;\n    }\n\n    public void setPort(Long port) {\n        this.port = port;\n    }\n\n    public NodeStatus getStatus() {\n        return status;\n    }\n\n    public void setStatus(NodeStatus status) {\n        this.status = status;\n    }\n\n    public String getDescription() {\n        return description;\n    }\n\n    public void setDescription(String description) {\n        this.description = description;\n    }\n\n    public NodeParameter getParameters() {\n        return parameters;\n    }\n\n    public void setParameters(NodeParameter parameters) {\n        this.parameters = parameters;\n    }\n\n    public Date getGmtCreate() {\n        return gmtCreate;\n    }\n\n    public void setGmtCreate(Date gmtCreate) {\n        this.gmtCreate = gmtCreate;\n    }\n\n    public Date getGmtModified() {\n        return gmtModified;\n    }\n\n    public void setGmtModified(Date gmtModified) {\n        this.gmtModified = gmtModified;\n    }\n\n    @Override\n    public String toString() {\n        return ToStringBuilder.reflectionToString(this, OtterToStringStyle.DEFAULT_STYLE);\n    }\n\n    @Override\n    public boolean equals(Object obj) {\n        if (this == obj) {\n            return true;\n        }\n        if (obj == null) {\n            return false;\n        }\n        if (getClass() != obj.getClass()) {\n            return false;\n        }\n        Node other = (Node) obj;\n        if (id == null) {\n            if (other.id != null) {\n                return false;\n            }\n        } else if (!id.equals(other.id)) {\n            return false;\n        }\n        return true;\n    }\n\n    @Override\n    public int hashCode() {\n        final int prime = 31;\n        int result = 1;\n        result = prime * result + ((id == null) ? 0 : id.hashCode());\n        return result;\n    }\n\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/model/config/node/NodeParameter.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.model.config.node;\n\nimport java.io.Serializable;\n\nimport org.apache.commons.lang.builder.ToStringBuilder;\n\nimport com.alibaba.otter.shared.common.model.autokeeper.AutoKeeperCluster;\nimport com.alibaba.otter.shared.common.utils.OtterToStringStyle;\n\n/**\n * Node节点对应的参数信息\n * \n * @author jianghang 2011-9-16 下午03:39:36\n * @version 4.0.0\n */\npublic class NodeParameter implements Serializable {\n\n    private static final long serialVersionUID = -4788966688697451950L;\n    private Integer           mbeanPort;                               // mbean端口\n    private Integer           downloadPort;                            // 下载端口\n    private AutoKeeperCluster zkCluster;                               // zk的集群\n    private String            externalIp;                              // 外部ip\n    private Boolean           useExternalIp    = false;                // 是否使用外部ip，此优先级高于pipeline参数，设置后包括rpc/pipe都将使用外部ip\n\n    public Integer getDownloadPort() {\n        return downloadPort;\n    }\n\n    public void setDownloadPort(Integer downloadPort) {\n        this.downloadPort = downloadPort;\n    }\n\n    public Integer getMbeanPort() {\n        return mbeanPort;\n    }\n\n    public void setMbeanPort(Integer mbeanPort) {\n        this.mbeanPort = mbeanPort;\n    }\n\n    public AutoKeeperCluster getZkCluster() {\n        return zkCluster;\n    }\n\n    public void setZkCluster(AutoKeeperCluster zkCluster) {\n        this.zkCluster = zkCluster;\n    }\n\n    public String getExternalIp() {\n        return externalIp;\n    }\n\n    public void setExternalIp(String externalIp) {\n        this.externalIp = externalIp;\n    }\n\n    public Boolean getUseExternalIp() {\n        return useExternalIp == null ? false : useExternalIp;\n    }\n\n    public void setUseExternalIp(Boolean useExternalIp) {\n        this.useExternalIp = useExternalIp;\n    }\n\n    @Override\n    public String toString() {\n        return ToStringBuilder.reflectionToString(this, OtterToStringStyle.DEFAULT_STYLE);\n    }\n\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/model/config/node/NodeStatus.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.model.config.node;\n\n/**\n * Node节点的运行状态\n * \n * @author jianghang\n */\npublic enum NodeStatus {\n\n    /** 运行中 */\n    START,\n    /** 停止 */\n    STOP;\n\n    public boolean isStart() {\n        return this.equals(NodeStatus.START);\n    }\n\n    public boolean isStop() {\n        return this.equals(NodeStatus.STOP);\n    }\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/model/config/parameter/Parameter.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.model.config.parameter;\n\nimport java.io.Serializable;\nimport java.math.BigDecimal;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\n\n/**\n * 参数信息类\n * \n * @author jianghang 2011-9-2 上午10:35:16\n */\npublic class Parameter implements Serializable {\n\n    private static final long   serialVersionUID      = -8019445081834822960L;\n    private Map<String, String> params                = new ConcurrentHashMap<String, String>(10);\n    private boolean             mergeSystemProperties = true;\n\n    public Object put(String key, String value) {\n        return params.put(key, value);\n    }\n\n    public String getString(String key) {\n        String value = params.get(key);\n        // 是否需要合并system.properties\n        return (mergeSystemProperties && value == null) ? System.getProperty(key) : null;\n    }\n\n    public Integer getInteger(String key) {\n        String value = getString(key);\n        return value == null ? null : Integer.valueOf(value);\n    }\n\n    public Long getLong(String key) {\n        String value = getString(key);\n        return value == null ? null : Long.valueOf(value);\n    }\n\n    public BigDecimal getBigDecimal(String key) {\n        String value = getString(key);\n        return value == null ? null : new BigDecimal(value);\n    }\n\n    public Boolean getBool(String key) {\n        String value = getString(key);\n        return value == null ? null : Boolean.valueOf(value);\n    }\n\n    public <T extends Enum<T>> T getEnum(String key, Class<T> enumType) {\n        String value = getString(key);\n        return (T) Enum.valueOf(enumType, value);\n    }\n\n    /**\n     * 设置对应的参数对象\n     */\n    protected void setParams(Map params) {\n        params.putAll(params);\n    }\n\n    /**\n     * 获取对应的params对象\n     */\n    protected Map getParams() {\n        return params;\n    }\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/model/config/parameter/SystemParameter.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.model.config.parameter;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.apache.commons.lang.StringUtils;\nimport org.apache.commons.lang.builder.ToStringBuilder;\n\nimport com.alibaba.otter.shared.common.utils.OtterToStringStyle;\n\n/**\n * 全局参数定义\n * \n * @author jianghang 2012-4-9 下午01:51:04\n * @version 4.0.2\n */\npublic class SystemParameter implements Serializable {\n\n    private static final long   serialVersionUID       = -1780184554337059839L;\n\n    private String              systemSchema           = \"retl\";                             // 默认为retl，不允许为空\n    private String              systemMarkTable        = \"retl_mark\";                        // 双向同步标记表\n    private String              systemMarkTableColumn  = \"channel_id\";                       // 双向同步标记的列名\n    private String              systemMarkTableInfo    = \"channel_info\";                     // 双向同步标记的info信息\n    private String              systemBufferTable      = \"retl_buffer\";                      // otter同步系统buffer表\n    private String              systemDualTable        = \"xdual\";                            // otter同步心跳表\n    private List<String>        hzZkClusters           = new ArrayList<String>();            // 杭州zk集群列表\n    private List<String>        usZkClusters           = new ArrayList<String>();            // 美国zk集群列表\n    private List<String>        hzStoreClusters        = new ArrayList<String>();            // 杭州store集群列表\n    private List<String>        usStoreClusters        = new ArrayList<String>();            // 美国store集群列表\n    private String              hzArandaCluster        = \"\";                                 // 杭州aranda集群地址\n    private String              usArandaCluster        = \"\";                                 // 美国aranda集群地址\n    private RetrieverType       retriever              = RetrieverType.ARIA2C;               // 下载方式\n    private String              defaultAlarmReceiveKey = \"otterteam\";\n    private String              defaultAlarmReceiver   = \"jianghang115@gmail.com\";\n    private Map<String, String> alarmReceiver          = new LinkedHashMap<String, String>(); // 报警联系人\n\n    public static enum RetrieverType {\n        /** java版多线程下载 */\n        MR4J(\"\"),\n        /** aria2c多线程下载 */\n        ARIA2C(\"aria2c\");\n\n        private String exe; // 代表可执行文件的路径，可以是相对于PATH的路径\n\n        RetrieverType(String exe){\n            this.exe = exe;\n        }\n\n        public boolean isMr4j() {\n            return this.equals(RetrieverType.MR4J);\n        }\n\n        public boolean isAria2c() {\n            return this.equals(RetrieverType.ARIA2C);\n        }\n\n        public String getExe() {\n            return exe;\n        }\n\n    }\n\n    public String getSystemSchema() {\n        return systemSchema;\n    }\n\n    public void setSystemSchema(String systemSchema) {\n        this.systemSchema = systemSchema;\n    }\n\n    public String getSystemMarkTable() {\n        return systemMarkTable;\n    }\n\n    public void setSystemMarkTable(String systemMarkTable) {\n        this.systemMarkTable = systemMarkTable;\n    }\n\n    public String getSystemBufferTable() {\n        return systemBufferTable;\n    }\n\n    public void setSystemBufferTable(String systemBufferTable) {\n        this.systemBufferTable = systemBufferTable;\n    }\n\n    public RetrieverType getRetriever() {\n        return retriever;\n    }\n\n    public void setRetriever(RetrieverType retriever) {\n        this.retriever = retriever;\n    }\n\n    public String getSystemMarkTableColumn() {\n        return systemMarkTableColumn;\n    }\n\n    public void setSystemMarkTableColumn(String systemMarkTableColumn) {\n        this.systemMarkTableColumn = systemMarkTableColumn;\n    }\n\n    public String getSystemDualTable() {\n        return systemDualTable;\n    }\n\n    public void setSystemDualTable(String systemDualTable) {\n        this.systemDualTable = systemDualTable;\n    }\n\n    public String getSystemMarkTableInfo() {\n        return systemMarkTableInfo;\n    }\n\n    public void setSystemMarkTableInfo(String systemMarkTableInfo) {\n        this.systemMarkTableInfo = systemMarkTableInfo;\n    }\n\n    public String getHzArandaCluster() {\n        return hzArandaCluster;\n    }\n\n    public void setHzArandaCluster(String hzArandaCluster) {\n        this.hzArandaCluster = hzArandaCluster;\n    }\n\n    public List<String> getHzZkClusters() {\n        return hzZkClusters;\n    }\n\n    public void setHzZkClusters(List<String> hzZkClusters) {\n        this.hzZkClusters = hzZkClusters;\n    }\n\n    public List<String> getUsZkClusters() {\n        return usZkClusters;\n    }\n\n    public void setUsZkClusters(List<String> usZkClusters) {\n        this.usZkClusters = usZkClusters;\n    }\n\n    public String getUsArandaCluster() {\n        return usArandaCluster;\n    }\n\n    public void setUsArandaCluster(String usArandaCluster) {\n        this.usArandaCluster = usArandaCluster;\n    }\n\n    public List<String> getHzStoreClusters() {\n        return hzStoreClusters;\n    }\n\n    public void setHzStoreClusters(List<String> hzStoreClusters) {\n        this.hzStoreClusters = hzStoreClusters;\n    }\n\n    public List<String> getUsStoreClusters() {\n        return usStoreClusters;\n    }\n\n    public void setUsStoreClusters(List<String> usStoreClusters) {\n        this.usStoreClusters = usStoreClusters;\n    }\n\n    public Map<String, String> getAlarmReceiver() {\n        return alarmReceiver;\n    }\n\n    public void setAlarmReceiver(Map<String, String> alarmReceiver) {\n        this.alarmReceiver = alarmReceiver;\n    }\n\n    public String getDefaultAlarmReceiveKey() {\n        return defaultAlarmReceiveKey;\n    }\n\n    public void setDefaultAlarmReceiveKey(String defaultAlarmReceiveKey) {\n        this.defaultAlarmReceiveKey = defaultAlarmReceiveKey;\n    }\n\n    public String getDefaultAlarmReceiver() {\n        return defaultAlarmReceiver;\n    }\n\n    public void setDefaultAlarmReceiver(String defaultAlarmReceiver) {\n        this.defaultAlarmReceiver = defaultAlarmReceiver;\n    }\n\n    // ================ helper method==================\n\n    public String getDefaultAlarmReceiverFormat() {\n        return defaultAlarmReceiveKey + \"=\" + defaultAlarmReceiver;\n    }\n\n    public String getAlarmReceiverFormat() {\n        List<String> result = new ArrayList<String>();\n        for (Map.Entry<String, String> entry : alarmReceiver.entrySet()) {\n            result.add(entry.getKey() + \"=\" + entry.getValue());\n        }\n\n        return StringUtils.join(result, \"\\n\");\n    }\n\n    @Override\n    public String toString() {\n        return ToStringBuilder.reflectionToString(this, OtterToStringStyle.DEFAULT_STYLE);\n    }\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/model/config/pipeline/Pipeline.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.model.config.pipeline;\n\nimport java.io.Serializable;\nimport java.util.Date;\nimport java.util.List;\n\nimport org.apache.commons.lang.builder.ToStringBuilder;\n\nimport com.alibaba.otter.shared.common.model.config.data.DataMediaPair;\nimport com.alibaba.otter.shared.common.model.config.node.Node;\nimport com.alibaba.otter.shared.common.utils.OtterToStringStyle;\n\n/**\n * 同步任务数据对象\n * \n * @author jianghang 2011-8-31 下午07:35:38\n */\npublic class Pipeline implements Serializable {\n\n    private static final long   serialVersionUID = 5055655233043393285L;\n    private Long                id;\n    private Long                channelId;                                 // 对应关联的channel唯一标示id\n    private String              name;\n    private String              description;                               // 描述信息\n    private List<Node>          selectNodes;\n    private List<Node>          extractNodes;\n    private List<Node>          loadNodes;\n    private List<DataMediaPair> pairs;\n    private Date                gmtCreate;\n    private Date                gmtModified;\n    private PipelineParameter   parameters       = new PipelineParameter();\n\n    public Long getId() {\n        return id;\n    }\n\n    public void setId(Long id) {\n        this.id = id;\n    }\n\n    public Long getChannelId() {\n        return channelId;\n    }\n\n    public void setChannelId(Long channelId) {\n        this.channelId = channelId;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public void setName(String name) {\n        this.name = name;\n    }\n\n    public String getDescription() {\n        return description;\n    }\n\n    public void setDescription(String description) {\n        this.description = description;\n    }\n\n    public List<Node> getSelectNodes() {\n        return selectNodes;\n    }\n\n    public void setSelectNodes(List<Node> selectNodes) {\n        this.selectNodes = selectNodes;\n    }\n\n    public List<Node> getExtractNodes() {\n        return extractNodes;\n    }\n\n    public void setExtractNodes(List<Node> extractNodes) {\n        this.extractNodes = extractNodes;\n    }\n\n    public List<Node> getLoadNodes() {\n        return loadNodes;\n    }\n\n    public void setLoadNodes(List<Node> loadNodes) {\n        this.loadNodes = loadNodes;\n    }\n\n    public List<DataMediaPair> getPairs() {\n        return pairs;\n    }\n\n    public void setPairs(List<DataMediaPair> pairs) {\n        this.pairs = pairs;\n    }\n\n    public Date getGmtCreate() {\n        return gmtCreate;\n    }\n\n    public void setGmtCreate(Date gmtCreate) {\n        this.gmtCreate = gmtCreate;\n    }\n\n    public Date getGmtModified() {\n        return gmtModified;\n    }\n\n    public void setGmtModified(Date gmtModified) {\n        this.gmtModified = gmtModified;\n    }\n\n    public PipelineParameter getParameters() {\n        return parameters;\n    }\n\n    public void setParameters(PipelineParameter parameters) {\n        this.parameters = parameters;\n    }\n\n    @Override\n    public String toString() {\n        return ToStringBuilder.reflectionToString(this, OtterToStringStyle.DEFAULT_STYLE);\n    }\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/model/config/pipeline/PipelineParameter.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.model.config.pipeline;\n\nimport java.io.Serializable;\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Modifier;\n\nimport org.apache.commons.beanutils.BeanUtils;\nimport org.apache.commons.lang.builder.ToStringBuilder;\nimport org.springframework.util.ReflectionUtils;\n\nimport com.alibaba.otter.shared.common.model.config.Transient;\nimport com.alibaba.otter.shared.common.model.config.channel.ChannelParameter;\nimport com.alibaba.otter.shared.common.model.config.channel.ChannelParameter.RemedyAlgorithm;\nimport com.alibaba.otter.shared.common.model.config.channel.ChannelParameter.SyncConsistency;\nimport com.alibaba.otter.shared.common.model.config.channel.ChannelParameter.SyncMode;\nimport com.alibaba.otter.shared.common.model.config.parameter.SystemParameter;\nimport com.alibaba.otter.shared.common.model.config.parameter.SystemParameter.RetrieverType;\nimport com.alibaba.otter.shared.common.utils.OtterToStringStyle;\n\n/**\n * pipeline相关的参数类\n * \n * @author jianghang 2011-9-2 上午10:42:27\n */\npublic class PipelineParameter implements Serializable {\n\n    private static final long     serialVersionUID           = 8112362911827913152L;\n    private Long                  pipelineId;\n    private Long                  parallelism                = 3L;                          // 并行度\n    private LoadBanlanceAlgorithm lbAlgorithm                = LoadBanlanceAlgorithm.Random; // 负载均衡算法\n    private Boolean               home                       = false;                       // 是否为主站点\n    private SelectorMode          selectorMode               = SelectorMode.Canal;          // 数据获取方式\n    private String                destinationName;\n    private Short                 mainstemClientId;                                         // mainstem订阅id\n    private Integer               mainstemBatchsize          = 10000 * 10;                  // mainstem订阅批次大小\n    private Integer               loadBatchsize              = 50;                          // load批次大小\n    private Integer               extractPoolSize            = 5;                           // extract模块载入线程数，针对单个载入通道\n    private Integer               loadPoolSize               = 5;                           // load模块载入线程数，针对单个载入通道\n    private Integer               fileLoadPoolSize           = 5;                           // 文件同步线程数\n\n    private Boolean               dumpEvent                  = true;                        // 是否需要dumpevent对象\n    private Boolean               dumpSelector               = true;                        // 是否需要dumpSelector信息\n    private Boolean               dumpSelectorDetail         = true;                        // 是否需要dumpSelector的详细信息\n    private PipeChooseMode        pipeChooseType             = PipeChooseMode.AUTOMATIC;    // pipe传输模式\n    private Boolean               useBatch                   = true;                        // 是否使用batch模式\n    private Boolean               skipSelectException        = false;                       // 是否跳过select时的执行异常\n    private Boolean               skipLoadException          = false;                       // 是否跳过load时的执行异常\n    private ArbitrateMode         arbitrateMode              = ArbitrateMode.ZOOKEEPER;     // 调度模式，默认进行自动选择\n    private Long                  batchTimeout               = -1L;                         // 获取批量数据的超时时间,-1代表不进行超时控制，0代表永久，>0则表示按照指定的时间进行控制(单位毫秒)\n    private Boolean               fileDetect                 = false;                       // 是否开启文件同步检测\n    private Boolean               skipFreedom                = false;                       // 是否跳过自由门数据\n    private Boolean               useLocalFileMutliThread    = false;                       // 是否启用对local\n                                                                                             // file同步启用多线程\n    private Boolean               useFileEncrypt             = false;                       // 是否针对文件进行加密处理\n    private Boolean               useExternalIp              = false;                       // 是否起用外部Ip\n    private Boolean               useTableTransform          = false;                       // 是否启用转化机制，比如类型不同，默认为true，兼容老逻辑\n    private Boolean               enableCompatibleMissColumn = true;                        // 是否启用兼容字段不匹配处理\n    private Boolean               skipNoRow                  = false;                       // 跳过反查没记录的情况\n    private String                channelInfo;                                              // 同步标记，设置该标记后会在retl_mark中记录，在messageParse时进行check，相同则忽略\n    private Boolean               dryRun                     = false;                       // 是否启用dry\n                                                                                             // run模型，只记录load日志，不同步数据\n    private Boolean               ddlSync                    = true;                        // 是否支持ddl同步\n    private Boolean               skipDdlException           = false;                       // 是否跳过ddl执行异常\n\n    // ================================= channel parameter\n    // ================================\n\n    @Transient\n    private Boolean               enableRemedy;                                             // 是否启用冲突补救算法\n    @Transient\n    private RemedyAlgorithm       remedyAlgorithm;                                          // 冲突补救算法\n    @Transient\n    private Integer               remedyDelayThresoldForMedia;                              // 针对回环补救，如果反查速度过快，容易查到旧版本的数据记录，导致中美不一致，所以设置一个阀值，低于这个阀值的延迟不进行反查\n    @Transient\n    private SyncMode              syncMode;                                                 // 同步模式：字段/整条记录\n    @Transient\n    private SyncConsistency       syncConsistency;                                          // 同步一致性要求\n\n    // ================================= system parameter\n    // ================================\n    @Transient\n    private String                systemSchema;                                             // 默认为retl，不允许为空\n    @Transient\n    private String                systemMarkTable;                                          // 双向同步标记表\n    @Transient\n    private String                systemMarkTableColumn;                                    // 双向同步标记的列名\n    @Transient\n    private String                systemMarkTableInfo;                                      // 双向同步标记的info信息，比如类似BI_SYNC\n    @Transient\n    private String                systemBufferTable;                                        // otter同步buffer表\n    @Transient\n    private String                systemDualTable;                                          // otter同步心跳表\n    @Transient\n    private RetrieverType         retriever;                                                // 下载方式\n\n    /**\n     * 合并pipeline参数设置\n     */\n    public void merge(PipelineParameter pipelineParameter) {\n        try {\n            Field[] fields = this.getClass().getDeclaredFields();\n            for (int i = 0; i < fields.length; i++) {\n                Field field = fields[i];\n                // Skip static and final fields.\n                if (Modifier.isStatic(field.getModifiers()) || Modifier.isFinal(field.getModifiers())) {\n                    continue;\n                }\n\n                ReflectionUtils.makeAccessible(field);\n                Object srcValue = field.get(pipelineParameter);\n                if (srcValue != null) { // 忽略null值\n                    field.set(this, srcValue);\n                }\n            }\n        } catch (Exception e) {\n            // ignore\n        }\n    }\n\n    /**\n     * 合并system参数设置\n     */\n    public void merge(SystemParameter globalParmeter) {\n        try {\n            BeanUtils.copyProperties(this, globalParmeter);\n        } catch (Exception e) {\n            // ignore\n        }\n    }\n\n    /**\n     * 合并channel参数设置\n     */\n    public void merge(ChannelParameter channelParameter) {\n        try {\n            BeanUtils.copyProperties(this, channelParameter);\n        } catch (Exception e) {\n            // ignore\n        }\n    }\n\n    public static enum LoadBanlanceAlgorithm {\n        /** 轮询 */\n        RoundRbin,\n        /** 随机 */\n        Random,\n        /** Stick */\n        Stick;\n\n        public boolean isRoundRbin() {\n            return this.equals(LoadBanlanceAlgorithm.RoundRbin);\n        }\n\n        public boolean isRandom() {\n            return this.equals(LoadBanlanceAlgorithm.Random);\n        }\n\n        public boolean isStick() {\n            return this.equals(LoadBanlanceAlgorithm.Stick);\n        }\n    }\n\n    public static enum ArbitrateMode {\n        /** 内存调度 */\n        MEMORY,\n        /** rpc调度 */\n        RPC,\n        /** zk watcher调度 */\n        ZOOKEEPER,\n        /** 自动选择 */\n        AUTOMATIC;\n\n        public boolean isMemory() {\n            return this.equals(ArbitrateMode.MEMORY);\n        }\n\n        public boolean isRpc() {\n            return this.equals(ArbitrateMode.RPC);\n        }\n\n        public boolean isZookeeper() {\n            return this.equals(ArbitrateMode.ZOOKEEPER);\n        }\n\n        public boolean isAutomatic() {\n            return this.equals(ArbitrateMode.AUTOMATIC);\n        }\n    }\n\n    public static enum PipeChooseMode {\n        /** 自动选择 */\n        AUTOMATIC,\n        /** RPC */\n        RPC,\n        /** HTTP */\n        HTTP;\n\n        public boolean isAutomatic() {\n            return this.equals(PipeChooseMode.AUTOMATIC);\n        }\n\n        public boolean isRpc() {\n            return this.equals(PipeChooseMode.RPC);\n        }\n\n        public boolean isHttp() {\n            return this.equals(PipeChooseMode.HTTP);\n        }\n    }\n\n    public static enum SelectorMode {\n\n        Eromanga, Canal;\n\n        public boolean isEromanga() {\n            return this.equals(SelectorMode.Eromanga);\n        }\n\n        public boolean isCanal() {\n            return this.equals(SelectorMode.Canal);\n        }\n\n    }\n\n    // ======================== setter / getter ===========================\n\n    public Long getPipelineId() {\n        return pipelineId;\n    }\n\n    public void setPipelineId(Long pipelineId) {\n        this.pipelineId = pipelineId;\n    }\n\n    public Long getParallelism() {\n        return parallelism;\n    }\n\n    public void setParallelism(Long parallelism) {\n        this.parallelism = parallelism;\n    }\n\n    public LoadBanlanceAlgorithm getLbAlgorithm() {\n        return lbAlgorithm;\n    }\n\n    public void setLbAlgorithm(LoadBanlanceAlgorithm lbAlgorithm) {\n        this.lbAlgorithm = lbAlgorithm;\n    }\n\n    public Short getMainstemClientId() {\n        return mainstemClientId;\n    }\n\n    public void setMainstemClientId(short mainstemClientId) {\n        this.mainstemClientId = mainstemClientId;\n    }\n\n    public Integer getMainstemBatchsize() {\n        return mainstemBatchsize;\n    }\n\n    public Integer getLoadBatchsize() {\n        return loadBatchsize;\n    }\n\n    public void setLoadBatchsize(Integer loadBatchsize) {\n        this.loadBatchsize = loadBatchsize;\n    }\n\n    public Boolean isHome() {\n        return home;\n    }\n\n    public void setHome(Boolean home) {\n        this.home = home;\n    }\n\n    public Integer getLoadPoolSize() {\n        return loadPoolSize;\n    }\n\n    public void setLoadPoolSize(Integer loadPoolSize) {\n        this.loadPoolSize = loadPoolSize;\n    }\n\n    public Integer getExtractPoolSize() {\n        return extractPoolSize;\n    }\n\n    public void setExtractPoolSize(int extractPoolSize) {\n        this.extractPoolSize = extractPoolSize;\n    }\n\n    public Boolean isDumpEvent() {\n        return dumpEvent;\n    }\n\n    public void setDumpEvent(Boolean dumpEvent) {\n        this.dumpEvent = dumpEvent;\n    }\n\n    public PipeChooseMode getPipeChooseType() {\n        return pipeChooseType;\n    }\n\n    public void setPipeChooseType(PipeChooseMode pipeChooseType) {\n        this.pipeChooseType = pipeChooseType;\n    }\n\n    public Boolean isUseBatch() {\n        return useBatch;\n    }\n\n    public Boolean getSkipLoadException() {\n        // 兼容性处理\n        return skipLoadException == null ? false : skipLoadException;\n    }\n\n    public void setSkipLoadException(Boolean skipLoadException) {\n        this.skipLoadException = skipLoadException;\n    }\n\n    public void setSelectorMode(SelectorMode selectorMode) {\n        this.selectorMode = selectorMode;\n    }\n\n    public Boolean getHome() {\n        return home;\n    }\n\n    public Boolean getDumpEvent() {\n        return dumpEvent;\n    }\n\n    public void setExtractPoolSize(Integer extractPoolSize) {\n        this.extractPoolSize = extractPoolSize;\n    }\n\n    public Boolean getDumpSelector() {\n        // 兼容性处理\n        return dumpSelector == null ? true : dumpSelector;\n    }\n\n    public void setDumpSelector(Boolean dumpSelector) {\n        this.dumpSelector = dumpSelector;\n    }\n\n    public Boolean getDumpSelectorDetail() {\n        // 兼容性处理\n        return dumpSelectorDetail == null ? true : dumpSelectorDetail;\n    }\n\n    public void setDumpSelectorDetail(Boolean dumpSelectorDetail) {\n        this.dumpSelectorDetail = dumpSelectorDetail;\n    }\n\n    public SelectorMode getSelectorMode() {\n        return selectorMode;\n    }\n\n    public ArbitrateMode getArbitrateMode() {\n        return arbitrateMode == null ? ArbitrateMode.ZOOKEEPER : arbitrateMode;\n    }\n\n    public void setArbitrateMode(ArbitrateMode arbitrateMode) {\n        this.arbitrateMode = arbitrateMode;\n    }\n\n    public Long getBatchTimeout() {\n        return batchTimeout == null ? -1 : batchTimeout;\n    }\n\n    public void setBatchTimeout(Long batchTimeout) {\n        this.batchTimeout = batchTimeout;\n    }\n\n    public Boolean getFileDetect() {\n        return fileDetect == null ? false : fileDetect;\n    }\n\n    public void setFileDetect(Boolean fileDetect) {\n        this.fileDetect = fileDetect;\n    }\n\n    public Integer getFileLoadPoolSize() {\n        return fileLoadPoolSize == null ? 5 : fileLoadPoolSize;\n    }\n\n    public void setFileLoadPoolSize(Integer fileLoadPoolSize) {\n        this.fileLoadPoolSize = fileLoadPoolSize;\n    }\n\n    public Boolean getSkipFreedom() {\n        return skipFreedom == null ? false : skipFreedom;\n    }\n\n    public void setSkipFreedom(Boolean skipFreedom) {\n        this.skipFreedom = skipFreedom;\n    }\n\n    public String getDestinationName() {\n        return destinationName;\n    }\n\n    public void setDestinationName(String destinationName) {\n        this.destinationName = destinationName;\n    }\n\n    public Boolean getUseLocalFileMutliThread() {\n        return useLocalFileMutliThread == null ? false : useLocalFileMutliThread;\n    }\n\n    public void setUseLocalFileMutliThread(Boolean useLocalFileMutliThread) {\n        this.useLocalFileMutliThread = useLocalFileMutliThread;\n    }\n\n    public Boolean getUseExternalIp() {\n        return useExternalIp == null ? false : useExternalIp;\n    }\n\n    public void setUseExternalIp(Boolean useExternalIp) {\n        this.useExternalIp = useExternalIp;\n    }\n\n    public Boolean getUseFileEncrypt() {\n        return useFileEncrypt == null ? false : useFileEncrypt;\n    }\n\n    public void setUseFileEncrypt(Boolean useFileEncrypt) {\n        this.useFileEncrypt = useFileEncrypt;\n    }\n\n    public Boolean getUseTableTransform() {\n        return useTableTransform == null ? true : useTableTransform;\n    }\n\n    public void setUseTableTransform(Boolean useTableTransform) {\n        this.useTableTransform = useTableTransform;\n    }\n\n    public Boolean getSkipNoRow() {\n        return skipNoRow == null ? false : skipNoRow;\n    }\n\n    public void setSkipNoRow(Boolean skipNoRow) {\n        this.skipNoRow = skipNoRow;\n    }\n\n    public Boolean getEnableCompatibleMissColumn() {\n        return enableCompatibleMissColumn == null ? true : enableCompatibleMissColumn;\n    }\n\n    public void setEnableCompatibleMissColumn(Boolean enableCompatibleMissColumn) {\n        this.enableCompatibleMissColumn = enableCompatibleMissColumn;\n    }\n\n    public String getChannelInfo() {\n        return channelInfo;\n    }\n\n    public void setChannelInfo(String channelInfo) {\n        this.channelInfo = channelInfo;\n    }\n\n    public Boolean getDryRun() {\n        return dryRun == null ? false : dryRun;\n    }\n\n    public Boolean isDryRun() {\n        return dryRun == null ? false : dryRun;\n    }\n\n    public void setDryRun(Boolean dryRun) {\n        this.dryRun = dryRun;\n    }\n\n    public Boolean getDdlSync() {\n        return ddlSync == null ? true : ddlSync;\n    }\n\n    public void setDdlSync(Boolean ddlSync) {\n        this.ddlSync = ddlSync;\n    }\n\n    public Boolean getSkipDdlException() {\n        return skipDdlException == null ? false : skipDdlException;\n    }\n\n    public void setSkipDdlException(Boolean skipDdlException) {\n        this.skipDdlException = skipDdlException;\n    }\n\n    // =============================channel parameter ==========================\n\n    public Boolean getEnableRemedy() {\n        return enableRemedy;\n    }\n\n    public Boolean isEnableRemedy() {\n        return enableRemedy;\n    }\n\n    public void setEnableRemedy(Boolean enableRemedy) {\n        this.enableRemedy = enableRemedy;\n    }\n\n    public SyncMode getSyncMode() {\n        return syncMode;\n    }\n\n    public void setSyncMode(SyncMode syncMode) {\n        this.syncMode = syncMode;\n    }\n\n    public SyncConsistency getSyncConsistency() {\n        return syncConsistency;\n    }\n\n    public void setSyncConsistency(SyncConsistency syncConsistency) {\n        this.syncConsistency = syncConsistency;\n    }\n\n    public RemedyAlgorithm getRemedyAlgorithm() {\n        return remedyAlgorithm;\n    }\n\n    public void setRemedyAlgorithm(RemedyAlgorithm remedyAlgorithm) {\n        this.remedyAlgorithm = remedyAlgorithm;\n    }\n\n    // ============================= system parameter\n    // ================================\n\n    public Boolean getUseBatch() {\n        return useBatch;\n    }\n\n    public void setUseBatch(Boolean useBatch) {\n        this.useBatch = useBatch;\n    }\n\n    public void setMainstemClientId(Short mainstemClientId) {\n        this.mainstemClientId = mainstemClientId;\n    }\n\n    public void setMainstemBatchsize(Integer mainstemBatchsize) {\n        this.mainstemBatchsize = mainstemBatchsize;\n    }\n\n    public String getSystemSchema() {\n        return systemSchema;\n    }\n\n    public void setSystemSchema(String systemSchema) {\n        this.systemSchema = systemSchema;\n    }\n\n    public String getSystemMarkTable() {\n        return systemMarkTable;\n    }\n\n    public void setSystemMarkTable(String systemMarkTable) {\n        this.systemMarkTable = systemMarkTable;\n    }\n\n    public String getSystemBufferTable() {\n        return systemBufferTable;\n    }\n\n    public void setSystemBufferTable(String systemBufferTable) {\n        this.systemBufferTable = systemBufferTable;\n    }\n\n    public RetrieverType getRetriever() {\n        return retriever;\n    }\n\n    public void setRetriever(RetrieverType retriever) {\n        this.retriever = retriever;\n    }\n\n    public String getSystemMarkTableColumn() {\n        return systemMarkTableColumn;\n    }\n\n    public void setSystemMarkTableColumn(String systemMarkTableColumn) {\n        this.systemMarkTableColumn = systemMarkTableColumn;\n    }\n\n    public String getSystemDualTable() {\n        return systemDualTable;\n    }\n\n    public void setSystemDualTable(String systemDualTable) {\n        this.systemDualTable = systemDualTable;\n    }\n\n    public String getSystemMarkTableInfo() {\n        return systemMarkTableInfo;\n    }\n\n    public void setSystemMarkTableInfo(String systemMarkTableInfo) {\n        this.systemMarkTableInfo = systemMarkTableInfo;\n    }\n\n    public Integer getRemedyDelayThresoldForMedia() {\n        return remedyDelayThresoldForMedia;\n    }\n\n    public void setRemedyDelayThresoldForMedia(Integer remedyDelayThresoldForMedia) {\n        this.remedyDelayThresoldForMedia = remedyDelayThresoldForMedia;\n    }\n\n    public Boolean getSkipSelectException() {\n        return skipSelectException == null ? false : skipSelectException;\n    }\n\n    public void setSkipSelectException(Boolean skipSelectException) {\n        this.skipSelectException = skipSelectException;\n    }\n\n    @Override\n    public String toString() {\n        return ToStringBuilder.reflectionToString(this, OtterToStringStyle.DEFAULT_STYLE);\n    }\n\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/model/config/record/LogRecord.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.model.config.record;\n\nimport java.io.Serializable;\nimport java.util.Date;\n\nimport org.apache.commons.lang.builder.ToStringBuilder;\n\nimport com.alibaba.otter.shared.common.model.config.channel.Channel;\nimport com.alibaba.otter.shared.common.model.config.pipeline.Pipeline;\nimport com.alibaba.otter.shared.common.utils.OtterToStringStyle;\n\n/**\n * @author simon 2012-6-15 下午3:37:35\n */\npublic class LogRecord implements Serializable {\n\n    private static final long serialVersionUID = -6034150366256885260L;\n    private Long              id;\n    private Channel           channel;\n    private Pipeline          pipeline;\n    private Long              nid;\n    private String            title;\n    private String            message;\n    private Date              gmtCreate;\n    private Date              gmtModified;\n\n    public Long getId() {\n        return id;\n    }\n\n    public void setId(Long id) {\n        this.id = id;\n    }\n\n    public Channel getChannel() {\n        return channel;\n    }\n\n    public void setChannel(Channel channel) {\n        this.channel = channel;\n    }\n\n    public Pipeline getPipeline() {\n        return pipeline;\n    }\n\n    public void setPipeline(Pipeline pipeline) {\n        this.pipeline = pipeline;\n    }\n\n    public Long getNid() {\n        return nid;\n    }\n\n    public void setNid(Long nid) {\n        this.nid = nid;\n    }\n\n    public String getTitle() {\n        return title;\n    }\n\n    public void setTitle(String title) {\n        this.title = title;\n    }\n\n    public String getMessage() {\n        return message;\n    }\n\n    public void setMessage(String message) {\n        this.message = message;\n    }\n\n    public Date getGmtCreate() {\n        return gmtCreate;\n    }\n\n    public void setGmtCreate(Date gmtCreate) {\n        this.gmtCreate = gmtCreate;\n    }\n\n    public Date getGmtModified() {\n        return gmtModified;\n    }\n\n    public void setGmtModified(Date gmtModified) {\n        this.gmtModified = gmtModified;\n    }\n\n    @Override\n    public String toString() {\n        return ToStringBuilder.reflectionToString(this, OtterToStringStyle.DEFAULT_STYLE);\n    }\n\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/model/statistics/delay/DelayCount.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.model.statistics.delay;\n\nimport java.io.Serializable;\nimport java.util.Date;\n\nimport org.apache.commons.lang.builder.ToStringBuilder;\n\nimport com.alibaba.otter.shared.common.utils.OtterToStringStyle;\n\n/**\n * delay count的计数器\n * \n * @author jianghang 2011-11-21 下午02:44:59\n * @version 4.0.0\n */\npublic class DelayCount implements Serializable {\n\n    private static final long serialVersionUID = -1350200637109107904L;\n    private Long              id;\n    private Long              pipelineId;\n    private Long              number;                                  // 延迟数量\n    private Long              time;                                    // 延迟时间\n    private Date              gmtCreate;\n    private Date              gmtModified;\n\n    public Long getId() {\n        return id;\n    }\n\n    public void setId(Long id) {\n        this.id = id;\n    }\n\n    public Long getPipelineId() {\n        return pipelineId;\n    }\n\n    public void setPipelineId(Long pipelineId) {\n        this.pipelineId = pipelineId;\n    }\n\n    public Long getNumber() {\n        return number;\n    }\n\n    public void setNumber(Long number) {\n        this.number = number;\n    }\n\n    public Long getTime() {\n        return time;\n    }\n\n    public void setTime(Long time) {\n        this.time = time;\n    }\n\n    public Date getGmtCreate() {\n        return gmtCreate;\n    }\n\n    public void setGmtCreate(Date gmtCreate) {\n        this.gmtCreate = gmtCreate;\n    }\n\n    public Date getGmtModified() {\n        return gmtModified;\n    }\n\n    public void setGmtModified(Date gmtModified) {\n        this.gmtModified = gmtModified;\n    }\n\n    @Override\n    public String toString() {\n        return ToStringBuilder.reflectionToString(this, OtterToStringStyle.DEFAULT_STYLE);\n    }\n\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/model/statistics/delay/DelayStat.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.model.statistics.delay;\n\nimport java.io.Serializable;\nimport java.util.Date;\n\nimport org.apache.commons.lang.builder.ToStringBuilder;\n\nimport com.alibaba.otter.shared.common.utils.OtterToStringStyle;\n\n/**\n * 延迟状态\n * \n * @author jianghang 2011-9-8 下午12:32:10\n */\npublic class DelayStat implements Serializable {\n\n    private static final long serialVersionUID = -1350200637109107904L;\n    private Long              id;\n    private Long              pipelineId;\n    private Long              delayNumber;                             // 延迟数量\n    private Long              delayTime;                               // 延迟时间\n    private Date              gmtCreate;\n    private Date              gmtModified;\n\n    public Long getId() {\n        return id;\n    }\n\n    public void setId(Long id) {\n        this.id = id;\n    }\n\n    public Long getPipelineId() {\n        return pipelineId;\n    }\n\n    public void setPipelineId(Long pipelineId) {\n        this.pipelineId = pipelineId;\n    }\n\n    public Long getDelayNumber() {\n        return delayNumber;\n    }\n\n    public void setDelayNumber(Long delayNumber) {\n        this.delayNumber = delayNumber;\n    }\n\n    public Long getDelayTime() {\n        return delayTime;\n    }\n\n    public void setDelayTime(Long delayTime) {\n        this.delayTime = delayTime;\n    }\n\n    public Date getGmtCreate() {\n        return gmtCreate;\n    }\n\n    public void setGmtCreate(Date gmtCreate) {\n        this.gmtCreate = gmtCreate;\n    }\n\n    public Date getGmtModified() {\n        return gmtModified;\n    }\n\n    public void setGmtModified(Date gmtModified) {\n        this.gmtModified = gmtModified;\n    }\n\n    @Override\n    public String toString() {\n        return ToStringBuilder.reflectionToString(this, OtterToStringStyle.DEFAULT_STYLE);\n    }\n\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/model/statistics/stage/ProcessStat.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.model.statistics.stage;\n\nimport java.io.Serializable;\nimport java.util.List;\n\nimport org.apache.commons.lang.builder.ToStringBuilder;\n\nimport com.alibaba.otter.shared.common.utils.OtterToStringStyle;\n\n/**\n * @author jianghang 2011-9-8 下午12:43:53\n */\npublic class ProcessStat implements Serializable {\n\n    private static final long serialVersionUID = -5625269232233751756L;\n    private Long              pipelineId;\n    private Long              processId;\n    private List<StageStat>   stageStats;                              // 当前process的阶段列表\n\n    public Long getPipelineId() {\n        return pipelineId;\n    }\n\n    public void setPipelineId(Long pipelineId) {\n        this.pipelineId = pipelineId;\n    }\n\n    public Long getProcessId() {\n        return processId;\n    }\n\n    public void setProcessId(Long processId) {\n        this.processId = processId;\n    }\n\n    public List<StageStat> getStageStats() {\n        return stageStats;\n    }\n\n    public void setStageStats(List<StageStat> stageStats) {\n        this.stageStats = stageStats;\n    }\n\n    @Override\n    public String toString() {\n        return ToStringBuilder.reflectionToString(this, OtterToStringStyle.DEFAULT_STYLE);\n    }\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/model/statistics/stage/StageStat.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.model.statistics.stage;\n\nimport java.io.Serializable;\nimport java.util.Map;\n\nimport org.apache.commons.lang.builder.ToStringBuilder;\n\nimport com.alibaba.otter.shared.common.model.config.enums.StageType;\nimport com.alibaba.otter.shared.common.utils.OtterToStringStyle;\n\n/**\n * @author jianghang 2011-9-8 下午12:41:20\n */\npublic class StageStat implements Serializable {\n\n    private static final long serialVersionUID = 8751367492373579459L;\n    private Long              pipelineId;\n    private Long              processId;\n    private StageType         stage;                                  // 对应的阶段\n    private Long              startTime;                              // 开始时间\n    private Long              endTime;                                // 结束时间\n    private Long              number;                                 // 处理的数据量\n    private Long              size;                                   // 处理的数据大小\n    private Map               exts;                                   // 扩展信息字段\n\n    public Long getPipelineId() {\n        return pipelineId;\n    }\n\n    public void setPipelineId(Long pipelineId) {\n        this.pipelineId = pipelineId;\n    }\n\n    public Long getProcessId() {\n        return processId;\n    }\n\n    public void setProcessId(Long processId) {\n        this.processId = processId;\n    }\n\n    public StageType getStage() {\n        return stage;\n    }\n\n    public void setStage(StageType stage) {\n        this.stage = stage;\n    }\n\n    public Long getStartTime() {\n        return startTime;\n    }\n\n    public void setStartTime(Long startTime) {\n        this.startTime = startTime;\n    }\n\n    public Long getEndTime() {\n        return endTime;\n    }\n\n    public void setEndTime(Long endTime) {\n        this.endTime = endTime;\n    }\n\n    public Long getNumber() {\n        return number;\n    }\n\n    public void setNumber(Long number) {\n        this.number = number;\n    }\n\n    public Long getSize() {\n        return size;\n    }\n\n    public void setSize(Long size) {\n        this.size = size;\n    }\n\n    public Map getExts() {\n        return exts;\n    }\n\n    public void setExts(Map exts) {\n        this.exts = exts;\n    }\n\n    @Override\n    public String toString() {\n        return ToStringBuilder.reflectionToString(this, OtterToStringStyle.DEFAULT_STYLE);\n    }\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/model/statistics/table/TableStat.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.model.statistics.table;\n\nimport java.io.Serializable;\nimport java.util.Date;\n\nimport org.apache.commons.lang.builder.ToStringBuilder;\n\nimport com.alibaba.otter.shared.common.utils.OtterToStringStyle;\n\n/**\n * @author jianghang 2011-9-8 下午06:28:18\n */\npublic class TableStat implements Serializable {\n\n    private static final long serialVersionUID = -6463213086225536773L;\n    private Long              id;\n    private Long              pipelineId;                              // 同步任务组\n    private Long              dataMediaPairId;                         // 同步任务表对id\n    private Long              fileSize;                                // 文件大小\n    private Long              fileCount;                               // 文件数量\n    private Long              deleteCount;\n    private Long              updateCount;\n    private Long              insertCount;\n    // add by ljh at 2012-07-13\n    private Date              startTime;                               // 批次开始时间\n    private Date              endTime;                                 // 批次结束时间\n    private Date              gmtCreate;\n    private Date              gmtModified;\n\n    public Long getId() {\n        return id;\n    }\n\n    public void setId(Long id) {\n        this.id = id;\n    }\n\n    public Long getPipelineId() {\n        return pipelineId;\n    }\n\n    public void setPipelineId(Long pipelineId) {\n        this.pipelineId = pipelineId;\n    }\n\n    public Long getDataMediaPairId() {\n        return dataMediaPairId;\n    }\n\n    public void setDataMediaPairId(Long dataMediaPairId) {\n        this.dataMediaPairId = dataMediaPairId;\n    }\n\n    public Long getFileSize() {\n        return fileSize;\n    }\n\n    public void setFileSize(Long fileSize) {\n        this.fileSize = fileSize;\n    }\n\n    public Long getFileCount() {\n        return fileCount;\n    }\n\n    public void setFileCount(Long fileCount) {\n        this.fileCount = fileCount;\n    }\n\n    public Long getDeleteCount() {\n        return deleteCount;\n    }\n\n    public void setDeleteCount(Long deleteCount) {\n        this.deleteCount = deleteCount;\n    }\n\n    public Long getUpdateCount() {\n        return updateCount;\n    }\n\n    public void setUpdateCount(Long updateCount) {\n        this.updateCount = updateCount;\n    }\n\n    public Long getInsertCount() {\n        return insertCount;\n    }\n\n    public void setInsertCount(Long insertCount) {\n        this.insertCount = insertCount;\n    }\n\n    public Date getGmtCreate() {\n        return gmtCreate;\n    }\n\n    public void setGmtCreate(Date gmtCreate) {\n        this.gmtCreate = gmtCreate;\n    }\n\n    public Date getGmtModified() {\n        return gmtModified;\n    }\n\n    public void setGmtModified(Date gmtModified) {\n        this.gmtModified = gmtModified;\n    }\n\n    public Date getStartTime() {\n        return startTime;\n    }\n\n    public void setStartTime(Date startTime) {\n        this.startTime = startTime;\n    }\n\n    public Date getEndTime() {\n        return endTime;\n    }\n\n    public void setEndTime(Date endTime) {\n        this.endTime = endTime;\n    }\n\n    @Override\n    public String toString() {\n        return ToStringBuilder.reflectionToString(this, OtterToStringStyle.DEFAULT_STYLE);\n    }\n\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/model/statistics/throughput/ThroughputStat.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.model.statistics.throughput;\n\nimport java.io.Serializable;\nimport java.util.Date;\n\nimport org.apache.commons.lang.builder.ToStringBuilder;\n\nimport com.alibaba.otter.shared.common.utils.OtterToStringStyle;\n\n/**\n * 吞吐量统计\n * \n * @author jianghang 2011-9-8 下午12:53:59\n */\npublic class ThroughputStat implements Serializable {\n\n    private static final long serialVersionUID = 1953478777704061454L;\n    private Long              id;\n    private Long              pipelineId;\n    private Date              startTime;\n    private Date              endTime;\n    private ThroughputType    type;\n    private Long              number;\n    private Long              size;\n    private Date              gmtCreate;\n    private Date              gmtModified;\n\n    public Long getId() {\n        return id;\n    }\n\n    public void setId(Long id) {\n        this.id = id;\n    }\n\n    public Long getPipelineId() {\n        return pipelineId;\n    }\n\n    public void setPipelineId(Long pipelineId) {\n        this.pipelineId = pipelineId;\n    }\n\n    public Date getStartTime() {\n        return startTime;\n    }\n\n    public void setStartTime(Date startTime) {\n        this.startTime = startTime;\n    }\n\n    public Date getEndTime() {\n        return endTime;\n    }\n\n    public void setEndTime(Date endTime) {\n        this.endTime = endTime;\n    }\n\n    public ThroughputType getType() {\n        return type;\n    }\n\n    public void setType(ThroughputType type) {\n        this.type = type;\n    }\n\n    public Long getNumber() {\n        return number;\n    }\n\n    public void setNumber(Long number) {\n        this.number = number;\n    }\n\n    public Long getSize() {\n        return size;\n    }\n\n    public void setSize(Long size) {\n        this.size = size;\n    }\n\n    public Date getGmtCreate() {\n        return gmtCreate;\n    }\n\n    public void setGmtCreate(Date gmtCreate) {\n        this.gmtCreate = gmtCreate;\n    }\n\n    public Date getGmtModified() {\n        return gmtModified;\n    }\n\n    public void setGmtModified(Date gmtModified) {\n        this.gmtModified = gmtModified;\n    }\n\n    @Override\n    public String toString() {\n        return ToStringBuilder.reflectionToString(this, OtterToStringStyle.DEFAULT_STYLE);\n    }\n\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/model/statistics/throughput/ThroughputType.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.model.statistics.throughput;\n\n/**\n * 吞吐量统计类型\n * \n * @author jianghang 2011-9-8 下午12:57:30\n */\npublic enum ThroughputType {\n    /** 数据库行记录 */\n    ROW,\n    /** 文件数据 */\n    FILE,\n    /** MQ记录数据 */\n    MQ;\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/model/user/AuthorizeType.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.model.user;\n\n/**\n * 用户权限\n * \n * @author simon 2011-11-10 下午07:34:58\n */\npublic enum AuthorizeType {\n    /** 匿名用户 */\n    ANONYMOUS,\n    /** 普通操作员 */\n    OPERATOR,\n    /** 系统管理员 */\n    ADMIN;\n\n    public boolean isAnonymous() {\n        return this.equals(AuthorizeType.ANONYMOUS);\n    }\n\n    public boolean isOperator() {\n        return this.equals(AuthorizeType.OPERATOR);\n    }\n\n    public boolean isAdmin() {\n        return this.equals(AuthorizeType.ADMIN);\n    }\n\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/model/user/User.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.model.user;\n\nimport java.io.Serializable;\nimport java.util.Date;\n\npublic class User implements Serializable {\n\n    private static final long serialVersionUID = -1724299315008190533L;\n    private Long              id;\n    private String            name;\n    private String            password;\n    private String            department;\n    private String            realName;\n    private AuthorizeType     authorizeType;\n    private Date              gmtCreate;\n    private Date              gmtModified;\n\n    public Long getId() {\n        return id;\n    }\n\n    public void setId(Long id) {\n        this.id = id;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public void setName(String name) {\n        this.name = name;\n    }\n\n    public String getPassword() {\n        return password;\n    }\n\n    public void setPassword(String password) {\n        this.password = password;\n    }\n\n    public String getDepartment() {\n        return department;\n    }\n\n    public void setDepartment(String department) {\n        this.department = department;\n    }\n\n    public String getRealName() {\n        return realName;\n    }\n\n    public void setRealName(String realName) {\n        this.realName = realName;\n    }\n\n    public Date getGmtCreate() {\n        return gmtCreate;\n    }\n\n    public void setGmtCreate(Date gmtCreate) {\n        this.gmtCreate = gmtCreate;\n    }\n\n    public Date getGmtModified() {\n        return gmtModified;\n    }\n\n    public void setGmtModified(Date gmtModified) {\n        this.gmtModified = gmtModified;\n    }\n\n    public AuthorizeType getAuthorizeType() {\n        return authorizeType;\n    }\n\n    public void setAuthorizeType(AuthorizeType authorizeType) {\n        this.authorizeType = authorizeType;\n    }\n\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/utils/AddressUtils.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.utils;\n\nimport java.io.IOException;\nimport java.net.InetAddress;\nimport java.net.NetworkInterface;\nimport java.net.ServerSocket;\nimport java.util.Enumeration;\nimport java.util.regex.Pattern;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\npublic class AddressUtils {\n\n    private static final Logger  logger       = LoggerFactory.getLogger(AddressUtils.class);\n    private static final String  LOCALHOST_IP = \"127.0.0.1\";\n    private static final String  EMPTY_IP     = \"0.0.0.0\";\n    private static final Pattern IP_PATTERN   = Pattern.compile(\"[0-9]{1,3}(\\\\.[0-9]{1,3}){3,}\");\n\n    public static boolean isAvailablePort(int port) {\n        ServerSocket ss = null;\n        try {\n            ss = new ServerSocket(port);\n            ss.bind(null);\n            return true;\n        } catch (IOException e) {\n            return false;\n        } finally {\n            if (ss != null) {\n                try {\n                    ss.close();\n                } catch (IOException e) {\n                }\n            }\n        }\n    }\n\n    private static boolean isValidHostAddress(InetAddress address) {\n        if (address == null || address.isLoopbackAddress()) {\n            return false;\n        }\n\n        String name = address.getHostAddress();\n        return (name != null && !EMPTY_IP.equals(name) && !LOCALHOST_IP.equals(name) && IP_PATTERN.matcher(name)\n            .matches());\n    }\n\n    public static String getHostIp() {\n        InetAddress address = getHostAddress();\n        return address == null ? null : address.getHostAddress();\n    }\n\n    public static String getHostName() {\n        InetAddress address = getHostAddress();\n        return address == null ? null : address.getHostName();\n    }\n\n    /**\n     * 判断该ip是否为本机ip，一台机器可能同时有多个IP\n     * \n     * @param ip\n     * @return\n     */\n    public static boolean isHostIp(String ip) {\n        InetAddress localAddress = null;\n        try {\n            localAddress = InetAddress.getLocalHost();\n            if (localAddress.isLoopbackAddress() || isValidHostAddress(localAddress)\n                && (localAddress.getHostAddress().equals(ip) || localAddress.getHostName().equals(ip))) {\n                return true;\n            }\n        } catch (Throwable e) {\n            logger.warn(\"Failed to retriving local host ip address, try scan network card ip address. cause: \"\n                        + e.getMessage());\n        }\n\n        try {\n            Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();\n            if (interfaces != null) {\n                while (interfaces.hasMoreElements()) {\n                    try {\n                        NetworkInterface network = interfaces.nextElement();\n                        Enumeration<InetAddress> addresses = network.getInetAddresses();\n                        if (addresses != null) {\n                            while (addresses.hasMoreElements()) {\n                                try {\n                                    InetAddress address = addresses.nextElement();\n                                    if (address.isLoopbackAddress() || isValidHostAddress(address) && address.getHostAddress().equals(ip)) {\n                                        return true;\n                                    }\n                                } catch (Throwable e) {\n                                    logger.warn(\"Failed to retriving network card ip address. cause:\" + e.getMessage());\n                                }\n                            }\n                        }\n                    } catch (Throwable e) {\n                        logger.warn(\"Failed to retriving network card ip address. cause:\" + e.getMessage());\n                    }\n                }\n            }\n        } catch (Throwable e) {\n            logger.warn(\"Failed to retriving network card ip address. cause:\" + e.getMessage());\n        }\n\n        return false;\n    }\n\n    public static InetAddress getHostAddress() {\n        InetAddress localAddress = null;\n        try {\n            localAddress = InetAddress.getLocalHost();\n            if (isValidHostAddress(localAddress)) {\n                return localAddress;\n            }\n        } catch (Throwable e) {\n            logger.warn(\"Failed to retriving local host ip address, try scan network card ip address. cause: \"\n                        + e.getMessage());\n        }\n        try {\n            Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();\n            if (interfaces != null) {\n                while (interfaces.hasMoreElements()) {\n                    try {\n                        NetworkInterface network = interfaces.nextElement();\n                        Enumeration<InetAddress> addresses = network.getInetAddresses();\n                        if (addresses != null) {\n                            while (addresses.hasMoreElements()) {\n                                try {\n                                    InetAddress address = addresses.nextElement();\n                                    if (isValidHostAddress(address)) {\n                                        return address;\n                                    }\n                                } catch (Throwable e) {\n                                    logger.warn(\"Failed to retriving network card ip address. cause:\" + e.getMessage());\n                                }\n                            }\n                        }\n                    } catch (Throwable e) {\n                        logger.warn(\"Failed to retriving network card ip address. cause:\" + e.getMessage());\n                    }\n                }\n            }\n        } catch (Throwable e) {\n            logger.warn(\"Failed to retriving network card ip address. cause:\" + e.getMessage());\n        }\n        logger.error(\"Could not get local host ip address, will use 127.0.0.1 instead.\");\n        return localAddress;\n    }\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/utils/Assert.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.utils;\n\npublic final class Assert {\n\n    public static <T> T assertNotNull(T object) {\n        return assertNotNull(object, null, null, (Object[]) null);\n    }\n\n    /** 确保对象不为空，否则抛出<code>IllegalArgumentException</code>。 */\n    public static <T> T assertNotNull(T object, String message, Object... args) {\n        return assertNotNull(object, null, message, args);\n    }\n\n    /** 确保对象不为空，否则抛出指定异常，默认为<code>IllegalArgumentException</code>。 */\n    public static <T> T assertNotNull(T object, ExceptionType exceptionType, String message, Object... args) {\n        if (object == null) {\n            if (exceptionType == null) {\n                exceptionType = ExceptionType.ILLEGAL_ARGUMENT;\n            }\n\n            throw exceptionType.newInstance(getMessage(message, args,\n                                                       \"[Assertion failed] - the argument is required; it must not be null\"));\n        }\n\n        return object;\n    }\n\n    /** 确保对象为空，否则抛出<code>IllegalArgumentException</code>。 */\n    public static <T> T assertNull(T object) {\n        return assertNull(object, null, null, (Object[]) null);\n    }\n\n    /** 确保对象为空，否则抛出<code>IllegalArgumentException</code>。 */\n    public static <T> T assertNull(T object, String message, Object... args) {\n        return assertNull(object, null, message, args);\n    }\n\n    /** 确保对象为空，否则抛出指定异常，默认为<code>IllegalArgumentException</code>。 */\n    public static <T> T assertNull(T object, ExceptionType exceptionType, String message, Object... args) {\n        if (object != null) {\n            if (exceptionType == null) {\n                exceptionType = ExceptionType.ILLEGAL_ARGUMENT;\n            }\n\n            throw exceptionType.newInstance(getMessage(message, args,\n                                                       \"[Assertion failed] - the object argument must be null\"));\n        }\n\n        return object;\n    }\n\n    /** 确保表达式为真，否则抛出<code>IllegalArgumentException</code>。 */\n    public static void assertTrue(boolean expression) {\n        assertTrue(expression, null, null, (Object[]) null);\n    }\n\n    /** 确保表达式为真，否则抛出<code>IllegalArgumentException</code>。 */\n    public static void assertTrue(boolean expression, String message, Object... args) {\n        assertTrue(expression, null, message, args);\n    }\n\n    /** 确保表达式为真，否则抛出指定异常，默认为<code>IllegalArgumentException</code>。 */\n    public static void assertTrue(boolean expression, ExceptionType exceptionType, String message, Object... args) {\n        if (!expression) {\n            if (exceptionType == null) {\n                exceptionType = ExceptionType.ILLEGAL_ARGUMENT;\n            }\n\n            throw exceptionType.newInstance(getMessage(message, args,\n                                                       \"[Assertion failed] - the expression must be true\"));\n        }\n    }\n\n    /** 取得带参数的消息。 */\n    private static String getMessage(String message, Object[] args, String defaultMessage) {\n        if (message == null) {\n            message = defaultMessage;\n        }\n\n        if (args == null || args.length == 0) {\n            return message;\n        }\n\n        return String.format(message, args);\n    }\n\n    /** Assertion错误类型。 */\n    public static enum ExceptionType {\n        ILLEGAL_ARGUMENT {\n\n            @Override\n            RuntimeException newInstance(String message) {\n                return new IllegalArgumentException(message);\n            }\n        };\n\n        abstract RuntimeException newInstance(String message);\n    }\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/utils/ByteUtils.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.utils;\n\nimport java.io.UnsupportedEncodingException;\nimport java.math.BigInteger;\nimport java.nio.ByteBuffer;\n\nimport org.apache.commons.codec.binary.Base64;\nimport org.apache.commons.lang.exception.NestableRuntimeException;\n\n/**\n * 提供一些便利的byte处理工具\n * \n * @author jianghang 2011-10-9 下午06:22:13\n * @version 4.0.0\n */\npublic class ByteUtils {\n\n    public static String bigIntToHex(BigInteger big) {\n        return big.toString(16);\n    }\n\n    public static BigInteger hexToBigInt(String hex) {\n        return new BigInteger(hex, 16);\n    }\n\n    public static String bytesToString(byte[] bytes) {\n        try {\n            return new String(bytes, \"UTF-8\");\n        } catch (UnsupportedEncodingException e) {\n            throw new NestableRuntimeException(e);\n        }\n    }\n\n    public static byte[] stringToBytes(String string) {\n        try {\n            return string.getBytes(\"UTF-8\");\n        } catch (UnsupportedEncodingException e) {\n            throw new NestableRuntimeException(e);\n        }\n    }\n\n    public static String bytesToBase64String(byte[] bytes) {\n        try {\n            return new String(Base64.encodeBase64(bytes), \"UTF-8\");\n        } catch (UnsupportedEncodingException e) {\n            throw new NestableRuntimeException(e);\n        }\n    }\n\n    public static byte[] base64StringToBytes(String string) {\n        try {\n            return Base64.decodeBase64(string.getBytes(\"UTF-8\"));\n        } catch (UnsupportedEncodingException e) {\n            throw new NestableRuntimeException(e);\n        }\n    }\n\n    public static byte[] int2bytes(int i) {\n        byte[] b = new byte[4];\n        int2bytes(i, b, 0);\n        return b;\n    }\n\n    public static int bytes2int(byte[] b) {\n        ByteBuffer buf = ByteBuffer.allocate(4);\n        buf.put(b);\n        buf.flip();\n        return buf.getInt();\n    }\n\n    public static byte[] long2bytes(long l) {\n        byte[] b = new byte[8];\n        long2bytes(l, b, 0);\n        return b;\n    }\n\n    public static long bytes2long(byte[] b) {\n        ByteBuffer buf = ByteBuffer.allocate(8);\n        buf.put(b);\n        buf.flip();\n        return buf.getLong();\n    }\n\n    public static int int2bytes(int i, byte[] data, int offset) {\n        data[offset++] = (byte) ((i >> 24) & 0xff);\n        data[offset++] = (byte) ((i >> 16) & 0xff);\n        data[offset++] = (byte) ((i >> 8) & 0xff);\n        data[offset] = (byte) (i & 0xff);\n        return 4;\n    }\n\n    public static int bytes2int(byte[] data, int offset) {\n        ByteBuffer buffer = ByteBuffer.wrap(data, offset, 4);\n        return buffer.getInt();\n    }\n\n    public static int long2bytes(long l, byte[] data, int offset) {\n        data[offset++] = (byte) ((l >> 56) & 0xff);\n        data[offset++] = (byte) ((l >> 48) & 0xff);\n        data[offset++] = (byte) ((l >> 40) & 0xff);\n        data[offset++] = (byte) ((l >> 32) & 0xff);\n        data[offset++] = (byte) ((l >> 24) & 0xff);\n        data[offset++] = (byte) ((l >> 16) & 0xff);\n        data[offset++] = (byte) ((l >> 8) & 0xff);\n        data[offset] = (byte) (l & 0xff);\n        return 8;\n    }\n\n    public static long bytes2long(byte[] data, int offset) {\n        ByteBuffer buffer = ByteBuffer.wrap(data, offset, 8);\n        return buffer.getLong();\n    }\n\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/utils/JsonUtils.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.utils;\n\nimport java.io.IOException;\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Type;\nimport java.net.Inet4Address;\nimport java.net.Inet6Address;\nimport java.net.InetAddress;\nimport java.net.InetSocketAddress;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\nimport org.apache.commons.lang.StringUtils;\n\nimport com.alibaba.fastjson.JSON;\nimport com.alibaba.fastjson.TypeReference;\nimport com.alibaba.fastjson.parser.DefaultJSONParser;\nimport com.alibaba.fastjson.parser.JSONLexer;\nimport com.alibaba.fastjson.parser.JSONToken;\nimport com.alibaba.fastjson.parser.ParserConfig;\nimport com.alibaba.fastjson.parser.deserializer.ObjectDeserializer;\nimport com.alibaba.fastjson.serializer.JSONSerializer;\nimport com.alibaba.fastjson.serializer.ObjectSerializer;\nimport com.alibaba.fastjson.serializer.PropertyFilter;\nimport com.alibaba.fastjson.serializer.SerializeConfig;\nimport com.alibaba.fastjson.serializer.SerializeWriter;\nimport com.alibaba.fastjson.serializer.SerializerFeature;\nimport com.alibaba.otter.shared.common.model.config.Transient;\nimport com.alibaba.otter.shared.common.utils.JsonUtils.InetSocketAddressSerializer.InetAddressDeserializer;\nimport com.alibaba.otter.shared.common.utils.JsonUtils.InetSocketAddressSerializer.InetSocketAddressDeserializer;\n\n/**\n * 字节处理相关工具类\n * \n * @author jianghang\n */\npublic class JsonUtils {\n\n    static {\n        SerializeConfig.getGlobalInstance().put(InetAddress.class, InetAddressSerializer.instance);\n        SerializeConfig.getGlobalInstance().put(Inet4Address.class, InetAddressSerializer.instance);\n        SerializeConfig.getGlobalInstance().put(Inet6Address.class, InetAddressSerializer.instance);\n        SerializeConfig.getGlobalInstance().put(InetSocketAddress.class, InetSocketAddressSerializer.instance);\n\n        ParserConfig.getGlobalInstance().getDeserializers().put(InetAddress.class, InetAddressDeserializer.instance);\n        ParserConfig.getGlobalInstance().getDeserializers().put(Inet4Address.class, InetAddressDeserializer.instance);\n        ParserConfig.getGlobalInstance().getDeserializers().put(Inet6Address.class, InetAddressDeserializer.instance);\n        ParserConfig.getGlobalInstance()\n            .getDeserializers()\n            .put(InetSocketAddress.class, InetSocketAddressDeserializer.instance);\n        SerializeConfig.getGlobalInstance().put(Inet6Address.class, InetAddressSerializer.instance);\n\n        // ParserConfig.getGlobalInstance().setAutoTypeSupport(true);\n        ParserConfig.getGlobalInstance().addAccept(\"com.alibaba.otter.\");\n        ParserConfig.getGlobalInstance().addAccept(\"com.taobao.tddl.dbsync.\");\n    }\n\n    public static <T> T unmarshalFromByte(byte[] bytes, Class<T> targetClass) {\n        return (T) JSON.parseObject(bytes, targetClass);// 默认为UTF-8\n    }\n\n    public static <T> T unmarshalFromByte(byte[] bytes, TypeReference<T> type) {\n        return (T) JSON.parseObject(bytes, type.getType());\n    }\n\n    public static byte[] marshalToByte(Object obj) {\n        return JSON.toJSONBytes(obj); // 默认为UTF-8\n    }\n\n    public static byte[] marshalToByte(Object obj, SerializerFeature... features) {\n        return JSON.toJSONBytes(obj, features); // 默认为UTF-8\n    }\n\n    public static <T> T unmarshalFromString(String json, Class<T> targetClass) {\n        return (T) JSON.parseObject(json, targetClass);// 默认为UTF-8\n    }\n\n    public static <T> T unmarshalFromString(String json, TypeReference<T> type) {\n        return (T) JSON.parseObject(json, type);// 默认为UTF-8\n    }\n\n    public static String marshalToString(Object obj) {\n        return JSON.toJSONString(obj); // 默认为UTF-8\n    }\n\n    public static String marshalToString(Object obj, SerializerFeature... features) {\n        return JSON.toJSONString(obj, features); // 默认为UTF-8\n    }\n\n    public static String marshalToStringWithoutTransient(Object obj) {\n        // 获取忽略字段\n        Class<?> clazz = obj.getClass();\n        Field[] fields = clazz.getDeclaredFields();\n        List<String> transientFileds = new ArrayList<String>();\n        for (Field f : fields) {\n            if (!f.isAccessible()) {\n                f.setAccessible(true);\n            }\n\n            Transient anno = f.getAnnotation(Transient.class);\n            if (anno != null && anno.value()) {\n                transientFileds.add(f.getName());\n            }\n        }\n\n        return marshalToString(obj, transientFileds.toArray(new String[transientFileds.size()]));\n    }\n\n    /**\n     * 可以允许指定一些过滤字段进行生成json对象\n     */\n    public static String marshalToString(Object obj, String... fliterFields) {\n        final List<String> propertyFliters = Arrays.asList(fliterFields);\n        SerializeWriter out = new SerializeWriter();\n        try {\n            JSONSerializer serializer = new JSONSerializer(out);\n            serializer.getPropertyFilters().add(new PropertyFilter() {\n\n                public boolean apply(Object source, String name, Object value) {\n                    return !propertyFliters.contains(name);\n                }\n\n            });\n            serializer.write(obj);\n            return out.toString();\n        } finally {\n            out.close();\n        }\n    }\n\n    public static class InetAddressSerializer implements ObjectSerializer {\n\n        public static InetAddressSerializer instance = new InetAddressSerializer();\n\n        public void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType)\n                                                                                                     throws IOException {\n            if (object == null) {\n                serializer.writeNull();\n                return;\n            }\n\n            InetAddress address = (InetAddress) object;\n            String[] data = StringUtils.split(address.toString(), '/');\n            serializer.write(data[0]);\n        }\n\n        public void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType, int features)\n                                                                                                                   throws IOException {\n            if (object == null) {\n                serializer.writeNull();\n                return;\n            }\n\n            InetAddress address = (InetAddress) object;\n            String[] data = StringUtils.split(address.toString(), '/');\n            serializer.write(data[0]);\n        }\n    }\n\n    public static class InetSocketAddressSerializer implements ObjectSerializer {\n\n        public static InetSocketAddressSerializer instance = new InetSocketAddressSerializer();\n\n        public void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType)\n                                                                                                     throws IOException {\n            if (object == null) {\n                serializer.writeNull();\n                return;\n            }\n\n            SerializeWriter out = serializer.out;\n            InetSocketAddress address = (InetSocketAddress) object;\n            InetAddress inetAddress = address.getAddress();\n            serializer.write('{');\n            out.writeFieldName(\"address\");\n            if (inetAddress != null) {\n                serializer.write(inetAddress);\n            } else {\n                out.writeString(address.getHostString());\n            }\n            out.write(',');\n            out.writeFieldName(\"port\");\n            out.writeInt(address.getPort());\n            serializer.write('}');\n        }\n\n        public void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType, int features)\n                                                                                                                   throws IOException {\n            if (object == null) {\n                serializer.writeNull();\n                return;\n            }\n\n            SerializeWriter out = serializer.out;\n            InetSocketAddress address = (InetSocketAddress) object;\n            address.getHostString();\n            InetAddress inetAddress = address.getAddress();\n            out.write('{');\n            out.writeFieldName(\"address\");\n            if (inetAddress != null) {\n                serializer.write(inetAddress);\n            } else {\n                out.writeString(address.getHostString());\n            }\n            out.write(',');\n            out.writeFieldName(\"port\");\n            out.writeInt(address.getPort());\n            out.write('}');\n        }\n\n        public static class InetAddressDeserializer implements ObjectDeserializer {\n\n            public static InetAddressDeserializer instance = new InetAddressDeserializer();\n\n            @Override\n            public String deserialze(DefaultJSONParser parser, Type type, Object fieldName) {\n                return (String) parser.parse();\n            }\n\n            @Override\n            public int getFastMatchToken() {\n                return 0;\n            }\n\n        }\n\n        public static class InetSocketAddressDeserializer implements ObjectDeserializer {\n\n            public static InetSocketAddressDeserializer instance = new InetSocketAddressDeserializer();\n\n            @Override\n            public InetSocketAddress deserialze(DefaultJSONParser parser, Type type, Object fieldName) {\n                JSONLexer lexer = parser.lexer;\n                if (lexer.token() == JSONToken.NULL) {\n                    lexer.nextToken();\n                    return null;\n                }\n\n                parser.accept(JSONToken.LBRACE);\n\n                Object address = null;\n                int port = 0;\n                for (;;) {\n                    String key = lexer.stringVal();\n                    lexer.nextToken(JSONToken.COLON);\n\n                    if (key.equals(\"address\")) {\n                        parser.accept(JSONToken.COLON);\n                        address = parser.parseObject(InetAddress.class);\n                    } else if (key.equals(\"port\")) {\n                        parser.accept(JSONToken.COLON);\n                        if (lexer.token() != JSONToken.LITERAL_INT) {\n                            throw new RuntimeException(\"port is not int\");\n                        }\n                        port = lexer.intValue();\n                        lexer.nextToken();\n                    } else {\n                        parser.accept(JSONToken.COLON);\n                        parser.parse();\n                    }\n\n                    if (lexer.token() == JSONToken.COMMA) {\n                        lexer.nextToken();\n                        continue;\n                    }\n\n                    break;\n                }\n\n                parser.accept(JSONToken.RBRACE);\n                return new InetSocketAddress(address.toString(), port);\n            }\n\n            @Override\n            public int getFastMatchToken() {\n                return 0;\n            }\n\n        }\n    }\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/utils/NioUtils.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.utils;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.ByteArrayOutputStream;\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileNotFoundException;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.OutputStream;\nimport java.nio.channels.Channels;\nimport java.nio.channels.FileChannel;\nimport java.nio.channels.ReadableByteChannel;\nimport java.nio.channels.WritableByteChannel;\n\nimport org.apache.commons.io.FileUtils;\nimport org.apache.commons.io.IOUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * 基于nio技术，提供一些快速的stream处理\n * \n * @author jianghang 2011-10-9 下午06:28:44\n * @version 4.0.0\n */\npublic class NioUtils {\n\n    private static final Logger logger              = LoggerFactory.getLogger(NioUtils.class);\n    private static final int    DEFAULT_BUFFER_SIZE = 8 * 1024;\n    private static final int    timeWait            = 1000;\n\n    /**\n     * 基于流的数据copy\n     */\n    public static long copy(InputStream input, OutputStream output, long offset) throws IOException {\n        long count = 0;\n        long n = 0;\n        if (input instanceof FileInputStream) {\n            FileChannel inChannel = ((FileInputStream) input).getChannel();\n            WritableByteChannel outChannel = Channels.newChannel(output);\n            count = inChannel.transferTo(offset, inChannel.size() - offset, outChannel);\n        } else if (output instanceof FileOutputStream) {\n            FileChannel outChannel = ((FileOutputStream) output).getChannel();\n            ReadableByteChannel inChannel = Channels.newChannel(input);\n            do {\n                n = outChannel.transferFrom(inChannel, offset + count, DEFAULT_BUFFER_SIZE);\n                count += n;\n            } while (n > 0);\n        } else {\n            byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];\n\n            input.skip(offset);\n            while (-1 != (n = input.read(buffer))) {\n                output.write(buffer, 0, (int) n);\n                count += n;\n            }\n            // ReadableByteChannel inChannel = Channels.newChannel(input);\n            // WritableByteChannel outChannel = Channels.newChannel(output);\n            //            \n            // //ByteBuffer buffer = new ByteBuffer(DEFAULT_BUFFER_SIZE);\n            // ByteBuffer buffer = ByteBuffer.allocateDirect(DEFAULT_BUFFER_SIZE);\n            // while (-1 != (n = inChannel.read(buffer))) {\n            // outChannel.write(buffer);\n            // count += n;\n            // }\n        }\n        return count;\n    }\n\n    /**\n     * 基于流的数据copy\n     */\n    public static long copy(InputStream input, OutputStream output, long offset, long count) throws IOException {\n        long rcount = 0;\n        long n = 0;\n        if (input instanceof FileInputStream) {\n            FileChannel inChannel = ((FileInputStream) input).getChannel();\n            WritableByteChannel outChannel = Channels.newChannel(output);\n            rcount = inChannel.transferTo(offset, count, outChannel);\n        } else if (output instanceof FileOutputStream) {\n            FileChannel outChannel = ((FileOutputStream) output).getChannel();\n            ReadableByteChannel inChannel = Channels.newChannel(input);\n            do {\n                if (count < DEFAULT_BUFFER_SIZE) {\n                    n = outChannel.transferFrom(inChannel, offset + rcount, count);\n                } else {\n                    n = outChannel.transferFrom(inChannel, offset + rcount, DEFAULT_BUFFER_SIZE);\n                }\n                count -= n;\n                rcount += n;\n            } while (n > 0);\n        } else {\n            byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];\n\n            input.skip(offset);\n            while (count > 0) {\n                if (count < DEFAULT_BUFFER_SIZE) {\n                    n = input.read(buffer, 0, (int) count);\n                } else {\n                    n = input.read(buffer, 0, DEFAULT_BUFFER_SIZE);\n                }\n\n                output.write(buffer, 0, (int) n);\n                count -= n;\n                rcount += n;\n            }\n            // ReadableByteChannel inChannel = Channels.newChannel(input);\n            // WritableByteChannel outChannel = Channels.newChannel(output);\n            //            \n            // //ByteBuffer buffer = new ByteBuffer(DEFAULT_BUFFER_SIZE);\n            // ByteBuffer buffer = ByteBuffer.allocateDirect(DEFAULT_BUFFER_SIZE);\n            // while (-1 != (n = inChannel.read(buffer))) {\n            // outChannel.write(buffer);\n            // count += n;\n            // }\n        }\n        return rcount;\n    }\n\n    /**\n     * 基于流的数据copy\n     */\n    public static long copy(InputStream input, OutputStream output) throws IOException {\n        long count = 0;\n        long n = 0;\n        if (input instanceof FileInputStream) {\n            FileChannel inChannel = ((FileInputStream) input).getChannel();\n            WritableByteChannel outChannel = Channels.newChannel(output);\n            count = inChannel.transferTo(0, inChannel.size(), outChannel);\n        } else if (output instanceof FileOutputStream) {\n            FileChannel outChannel = ((FileOutputStream) output).getChannel();\n            ReadableByteChannel inChannel = Channels.newChannel(input);\n            do {\n                n = outChannel.transferFrom(inChannel, count, DEFAULT_BUFFER_SIZE);\n                count += n;\n            } while (n > 0);\n        } else {\n            byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];\n\n            while (-1 != (n = input.read(buffer))) {\n                output.write(buffer, 0, (int) n);\n                count += n;\n            }\n            // ReadableByteChannel inChannel = Channels.newChannel(input);\n            // WritableByteChannel outChannel = Channels.newChannel(output);\n            //            \n            // //ByteBuffer buffer = new ByteBuffer(DEFAULT_BUFFER_SIZE);\n            // ByteBuffer buffer = ByteBuffer.allocateDirect(DEFAULT_BUFFER_SIZE);\n            // while (-1 != (n = inChannel.read(buffer))) {\n            // outChannel.write(buffer);\n            // count += n;\n            // }\n        }\n        return count;\n    }\n\n    /**\n     * 将byte[]数据写入到流中\n     */\n    public static void write(byte[] data, OutputStream output) throws IOException {\n        ByteArrayInputStream input = null;\n        try {\n            input = new ByteArrayInputStream(data);\n            copy(input, output);\n            output.flush();\n        } finally {\n            IOUtils.closeQuietly(output);\n        }\n    }\n\n    /**\n     * 将byte数组写入文件\n     */\n    public static void write(final byte[] srcArray, File targetFile) throws IOException {\n        write(srcArray, targetFile, true);\n    }\n\n    /**\n     * 将byte数组写入文件\n     */\n    public static void write(final byte[] srcArray, File targetFile, final boolean overwrite) throws IOException {\n        if (srcArray == null) {\n            throw new IOException(\"Source must not be null\");\n        }\n\n        if (targetFile == null) {\n            throw new IOException(\"Target must not be null\");\n        }\n\n        if (true == targetFile.exists()) {\n            if (true == targetFile.isDirectory()) {\n                throw new IOException(\"Target '\" + targetFile.getAbsolutePath() + \"' is directory!\");\n            } else if (true == targetFile.isFile()) {\n                if (!overwrite) {\n                    throw new IOException(\"Target file '\" + targetFile.getAbsolutePath() + \"' already exists!\");\n                }\n            } else {\n                throw new IOException(\"Invalid target object '\" + targetFile.getAbsolutePath() + \"'!\");\n            }\n        } else {\n            // create parent dir\n            create(targetFile.getParentFile(), false, 3);\n        }\n\n        // 使用无拷贝的inputStream\n        ByteArrayInputStream input = null;\n        FileOutputStream output = null;\n        try {\n            input = new ByteArrayInputStream(srcArray);\n            output = new FileOutputStream(targetFile);\n            copy(input, output);\n            output.flush();\n        } finally {\n            IOUtils.closeQuietly(input);\n            IOUtils.closeQuietly(output);\n        }\n    }\n\n    /**\n     * 读取文件到数组\n     */\n    public static byte[] read(File sourceFile) throws IOException {\n        if (sourceFile == null) {\n            throw new IOException(\"Source must not be null\");\n        }\n\n        if (false == sourceFile.exists()) {\n            throw new IOException(\"Source '\" + sourceFile + \"' does not exist\");\n        }\n\n        if (true == sourceFile.isDirectory()) {\n            throw new IOException(\"Source '\" + sourceFile + \"' exists but is a directory\");\n        }\n\n        FileInputStream input = null;\n        ByteArrayOutputStream output = null;\n\n        try {\n            input = new FileInputStream(sourceFile);\n            output = new ByteArrayOutputStream();\n            copy(input, output);\n            output.flush();\n            return output.toByteArray();\n        } finally {\n            IOUtils.closeQuietly(input);\n            IOUtils.closeQuietly(output);\n        }\n    }\n\n    /**\n     * 从流中读数据到byte[]\n     */\n    public static byte[] read(InputStream input) throws IOException {\n        ByteArrayOutputStream output = null;\n        try {\n            output = new ByteArrayOutputStream();\n            copy(input, output);\n            output.flush();\n            return output.toByteArray();\n        } finally {\n            IOUtils.closeQuietly(output);\n        }\n    }\n\n    /**\n     * Copy file overwrite\n     */\n    public static void copy(final File src, File dest) throws IOException {\n        copy(src, dest, 1);\n    }\n\n    /**\n     * 尝试多次复制文件，排除网络故障\n     * \n     * @param src\n     * @param dest\n     * @param retryTimes\n     * @throws IOException\n     */\n    public static boolean copy(final File src, File dest, final int retryTimes) throws IOException {\n        int totalRetry = retryTimes;\n\n        if (retryTimes < 1) {\n            totalRetry = 1;\n        }\n\n        int retry = 0;\n        while (retry++ < totalRetry) {\n            try {\n                copy(src, dest, true);\n                return true;\n            } catch (Exception ex) {\n                // 本次等待时间\n                int wait = (int) Math.pow(retry, retry) * timeWait;\n                wait = (wait < timeWait) ? timeWait : wait;\n\n                if (retry == totalRetry) {\n                    if (ex instanceof IOException) {\n                        throw (IOException) ex;\n                    } else {\n                        throw new IOException((ex == null) ? \"unknow error\" : ex.getMessage(), ex);\n                    }\n                } else {\n                    try {\n                        Thread.sleep(wait);\n                    } catch (InterruptedException e) {\n                        // ignore\n                    }\n                }\n            }\n        }\n\n        return false;\n    }\n\n    /**\n     * Copy source file to destination. If destination is a path then source file name is appended. If destination file\n     * exists then: overwrite=true - destination file is replaced; overwite=false - exception is thrown\n     */\n    public static void copy(final File sourceFile, File targetFile, final boolean overwrite) throws IOException {\n        if (sourceFile == null) {\n            throw new IOException(\"Source must not be null\");\n        }\n\n        if (targetFile == null) {\n            throw new IOException(\"Target must not be null\");\n        }\n\n        // checks\n        if ((false == sourceFile.isFile()) || (false == sourceFile.exists())) {\n            throw new IOException(\"Source file '\" + sourceFile.getAbsolutePath() + \"' not found!\");\n        }\n\n        if (true == targetFile.exists()) {\n            if (true == targetFile.isDirectory()) {\n\n                // Directory? -> use source file name\n                targetFile = new File(targetFile, sourceFile.getName());\n            } else if (true == targetFile.isFile()) {\n                if (false == overwrite) {\n                    throw new IOException(\"Target file '\" + targetFile.getAbsolutePath() + \"' already exists!\");\n                }\n            } else {\n                throw new IOException(\"Invalid target object '\" + targetFile.getAbsolutePath() + \"'!\");\n            }\n        } else {\n            // create parent dir\n            FileUtils.forceMkdir(targetFile.getParentFile());\n        }\n\n        FileInputStream input = null;\n        FileOutputStream output = null;\n\n        try {\n            input = new FileInputStream(sourceFile);\n            output = new FileOutputStream(targetFile);\n            copy(input, output);\n            output.flush();\n        } finally {\n            IOUtils.closeQuietly(input);\n            IOUtils.closeQuietly(output);\n        }\n    }\n\n    /**\n     * 正常创建\n     */\n    public static boolean create(File dest) {\n        return create(dest, true, 1);\n    }\n\n    /**\n     * 尝试多次创建\n     */\n    public static boolean create(File dest, final boolean isFile, final int retryTimes) {\n        if (dest == null) {\n            return false;\n        }\n\n        int totalRetry = retryTimes;\n\n        if (retryTimes < 0) {\n            totalRetry = 1;\n        }\n\n        int retry = 0;\n        while (retry++ < totalRetry) {\n            try {\n                if (true == isFile) {\n                    if ((true == dest.exists()) || (true == dest.createNewFile())) {\n                        return true;\n                    }\n                } else {\n                    FileUtils.forceMkdir(dest);\n                    return true;\n                }\n            } catch (Exception ex) {\n                // 本次等待时间\n                int wait = (int) Math.pow(retry, retry) * timeWait;\n                wait = (wait < timeWait) ? timeWait : wait;\n\n                // 尝试等待\n                if (retry == totalRetry) {\n                    return false;\n                } else {\n\n                    // 记录日志\n                    logger.warn(String.format(\"[%s] create() - retry %s failed : wait [%s] ms , caused by %s\",\n                                              dest.getAbsolutePath(), retry, wait, ex.getMessage()));\n                    try {\n                        Thread.sleep(wait);\n                    } catch (InterruptedException e) {\n                        return false;\n                    }\n                }\n            }\n        }\n\n        return false;\n    }\n\n    /**\n     * 正常删除，实在不行就jvm退出时删除\n     * \n     * @param dest\n     * @param retryTimes\n     */\n    public static boolean delete(File dest) {\n        return delete(dest, 1);\n    }\n\n    /**\n     * 尝试多次删除，实在不行就jvm退出时删除\n     * \n     * @param dest\n     * @param retryTimes\n     */\n    public static boolean delete(File dest, final int retryTimes) {\n        if (dest == null) {\n            return false;\n        }\n\n        if (false == dest.exists()) {\n            return true;\n        }\n\n        int totalRetry = retryTimes;\n        if (retryTimes < 1) {\n            totalRetry = 1;\n        }\n\n        int retry = 0;\n        while (retry++ < totalRetry) {\n            try {\n                FileUtils.forceDelete(dest);\n                return true;\n            } catch (FileNotFoundException ex) {\n                return true;\n            } catch (Exception ex) {\n                // 本次等待时间\n                int wait = (int) Math.pow(retry, retry) * timeWait;\n                wait = (wait < timeWait) ? timeWait : wait;\n                if (retry == totalRetry) {\n                    try {\n                        FileUtils.forceDeleteOnExit(dest);\n                        return false;\n                    } catch (Exception e) {\n                        // ignore\n                    }\n                } else {\n                    // 记录日志\n                    logger.warn(String.format(\"[%s] delete() - retry %s failed : wait [%s] ms , caused by %s\",\n                                              dest.getAbsolutePath(), retry, wait, ex.getMessage()));\n                    try {\n                        Thread.sleep(wait);\n                    } catch (InterruptedException e) {\n                        // ignore\n                    }\n                }\n            }\n        }\n\n        return false;\n    }\n\n    /**\n     * Move file without retry\n     */\n    public static void move(final File src, File dest) throws IOException {\n        move(src, dest, 1);\n    }\n\n    /**\n     * Moves the source file to the destination. If the destination cannot be created or is a read-only file, the method\n     * returns <code>false</code>. Otherwise, the contents of the source are copied to the destination, the source is\n     * deleted, and <code>true</code> is returned.\n     * \n     * @param src The source file to move.\n     * @param dest The destination where to move the file.\n     * @param retryTimes Move and delete retry times\n     */\n    public static void move(final File src, File dest, final int retryTimes) throws IOException {\n        copy(src, dest, retryTimes);\n        delete(src, retryTimes);\n    }\n\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/utils/OtterToStringStyle.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.utils;\n\nimport java.text.SimpleDateFormat;\nimport java.util.Date;\n\nimport org.apache.commons.lang.builder.ToStringStyle;\n\n/**\n * Otter项目内部使用的ToStringStyle\n * \n * <pre>\n * 默认Style输出格式：\n * Person[name=John Doe,age=33,smoker=false ,time=2010-04-01 00:00:00]\n * </pre>\n * \n * @author jianghang 2010-6-18 上午11:35:27\n */\npublic class OtterToStringStyle extends ToStringStyle {\n\n    private static final long         serialVersionUID = -6568177374288222145L;\n\n    private static final String       DEFAULT_TIME     = \"yyyy-MM-dd HH:mm:ss\";\n    private static final String       DEFAULT_DAY      = \"yyyy-MM-dd\";\n\n    /**\n     * <pre>\n     * 输出格式：\n     * Person[name=John Doe,age=33,smoker=false ,time=2010-04-01 00:00:00]\n     * </pre>\n     */\n    public static final ToStringStyle TIME_STYLE       = new OtterDateStyle(DEFAULT_TIME);\n\n    /**\n     * <pre>\n     * 输出格式：\n     * Person[name=John Doe,age=33,smoker=false ,day=2010-04-01]\n     * </pre>\n     */\n    public static final ToStringStyle DAY_STYLE        = new OtterDateStyle(DEFAULT_DAY);\n\n    /**\n     * <pre>\n     * 输出格式：\n     * Person[name=John Doe,age=33,smoker=false ,time=2010-04-01 00:00:00]\n     * </pre>\n     */\n    public static final ToStringStyle DEFAULT_STYLE    = OtterToStringStyle.TIME_STYLE;\n\n    // =========================== 自定义style =============================\n\n    /**\n     * 支持日期格式化的ToStringStyle\n     * \n     * @author li.jinl\n     */\n    private static class OtterDateStyle extends ToStringStyle {\n\n        private static final long serialVersionUID = 5208917932254652886L;\n\n        // 日期format格式\n        private String            pattern;\n\n        public OtterDateStyle(String pattern){\n            super();\n            this.setUseShortClassName(true);\n            this.setUseIdentityHashCode(false);\n            // 设置日期format格式\n            this.pattern = pattern;\n        }\n\n        protected void appendDetail(StringBuffer buffer, String fieldName, Object value) {\n            // 增加自定义的date对象处理\n            if (value instanceof Date) {\n                value = new SimpleDateFormat(pattern).format(value);\n            }\n            // 后续可以增加其他自定义对象处理\n            buffer.append(value);\n        }\n    }\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/utils/RegexUtils.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.utils;\n\nimport java.util.Map;\n\nimport org.apache.commons.lang.StringUtils;\nimport org.apache.oro.text.regex.MalformedPatternException;\nimport org.apache.oro.text.regex.Pattern;\nimport org.apache.oro.text.regex.PatternCompiler;\nimport org.apache.oro.text.regex.PatternMatcher;\nimport org.apache.oro.text.regex.Perl5Compiler;\nimport org.apache.oro.text.regex.Perl5Matcher;\n\nimport com.google.common.base.Function;\nimport com.google.common.collect.OtterMigrateMap;\n\n/**\n * @author simon 2012-9-25 下午5:01:48\n * @version 4.1.0\n */\npublic class RegexUtils {\n\n    private static Map<String, Pattern> patterns = null;\n\n    static {\n        patterns = OtterMigrateMap.makeSoftValueComputingMap(new Function<String, Pattern>() {\n\n            public Pattern apply(String pattern) {\n                try {\n                    PatternCompiler pc = new Perl5Compiler();\n                    return pc.compile(pattern, Perl5Compiler.CASE_INSENSITIVE_MASK | Perl5Compiler.READ_ONLY_MASK);\n                } catch (MalformedPatternException e) {\n                    throw new RuntimeException(\"Regex failed!\", e);\n                }\n            }\n        });\n    }\n\n    public static String findFirst(String originalStr, String regex) {\n        if (StringUtils.isBlank(originalStr) || StringUtils.isBlank(regex)) {\n            return StringUtils.EMPTY;\n        }\n\n        PatternMatcher matcher = new Perl5Matcher();\n        if (matcher.contains(originalStr, patterns.get(regex))) {\n            return StringUtils.trimToEmpty(matcher.getMatch().group(0));\n        }\n        return StringUtils.EMPTY;\n    }\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/utils/SecurityUtils.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.utils;\n\nimport java.io.UnsupportedEncodingException;\nimport java.security.MessageDigest;\nimport java.security.NoSuchAlgorithmException;\n\n/**\n * 类SecurityUtils.java的实现描述：TODO 类实现描述\n * \n * @author simon 2011-11-14 上午10:48:03\n */\npublic class SecurityUtils {\n\n    /**\n     * MD5 加密\n     */\n    public static String getMD5Str(String str) {\n        MessageDigest messageDigest = null;\n\n        try {\n            messageDigest = MessageDigest.getInstance(\"MD5\");\n\n            messageDigest.reset();\n\n            messageDigest.update(str.getBytes(\"UTF-8\"));\n        } catch (NoSuchAlgorithmException e) {\n            System.out.println(\"NoSuchAlgorithmException caught!\");\n            System.exit(-1);\n        } catch (UnsupportedEncodingException e) {\n            e.printStackTrace();\n        }\n\n        byte[] byteArray = messageDigest.digest();\n\n        StringBuffer md5StrBuff = new StringBuffer();\n\n        for (int i = 0; i < byteArray.length; i++) {\n            if (Integer.toHexString(0xFF & byteArray[i]).length() == 1) md5StrBuff.append(\"0\").append(Integer.toHexString(0xFF & byteArray[i]));\n            else md5StrBuff.append(Integer.toHexString(0xFF & byteArray[i]));\n        }\n\n        return md5StrBuff.toString();\n    }\n\n    public static String getPassword(String str) {\n        StringBuffer password = new StringBuffer();\n        String md5Str = getMD5Str(str);\n        password.append(md5Str.substring(26, 32));\n        password.append(md5Str.substring(10, 17));\n        password.append(md5Str.substring(15, 22));\n        return password.toString();\n    }\n\n    // public static void main(String[] args) {\n    // System.out.println(getMD5Str(\"operator\"));\n    // System.out.println(getPassword(\"operator\"));\n    // }\n\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/utils/TestUtils.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.utils;\n\nimport java.lang.management.ManagementFactory;\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Method;\n\nimport org.springframework.util.ReflectionUtils;\n\n/**\n * 提供常见的测试方法\n * \n * @author jianghang 2011-1-30 上午11:15:54\n */\npublic class TestUtils {\n\n    /**\n     * 获取对应属性的值\n     * \n     * @param obj\n     * @param fieldName\n     * @return\n     */\n    public static Object getField(Object obj, String fieldName) {\n        Field field = ReflectionUtils.findField(obj.getClass(), fieldName);\n        ReflectionUtils.makeAccessible(field);\n        return ReflectionUtils.getField(field, obj);\n    }\n\n    /**\n     * 设置对应参数的值\n     * \n     * @param target\n     * @param methodName\n     * @param args\n     * @return\n     * @throws Exception\n     */\n    public static void setField(Object target, String fieldName, Object args) throws Exception {\n        // 查找对应的方法\n        Field field = ReflectionUtils.findField(target.getClass(), fieldName);\n        ReflectionUtils.makeAccessible(field);\n        ReflectionUtils.setField(field, target, args);\n    }\n\n    /**\n     * 调用方法，可以是一些私有方法\n     * \n     * @param target\n     * @param methodName\n     * @param args\n     * @return\n     * @throws Exception\n     */\n    public static Object invokeMethod(Object target, String methodName, Object... args) throws Exception {\n        Method method = null;\n        // 查找对应的方法\n        if (args == null || args.length == 0) {\n            method = ReflectionUtils.findMethod(target.getClass(), methodName);\n        } else {\n            Class[] argsClass = new Class[args.length];\n            for (int i = 0; i < args.length; i++) {\n                argsClass[i] = args[i].getClass();\n            }\n            method = ReflectionUtils.findMethod(target.getClass(), methodName, argsClass);\n        }\n        ReflectionUtils.makeAccessible(method);\n\n        if (args == null || args.length == 0) {\n            return ReflectionUtils.invokeMethod(method, target);\n        } else {\n            return ReflectionUtils.invokeMethod(method, target, args);\n        }\n    }\n\n    public static void restoreJvm() {\n        int maxRestoreJvmLoops = 10;\n        long memUsedPrev = memoryUsed();\n        for (int i = 0; i < maxRestoreJvmLoops; i++) {\n            System.runFinalization();\n            System.gc();\n\n            long memUsedNow = memoryUsed();\n            // break early if have no more finalization and get constant mem used\n            if ((ManagementFactory.getMemoryMXBean().getObjectPendingFinalizationCount() == 0)\n                && (memUsedNow >= memUsedPrev)) {\n                break;\n            } else {\n                memUsedPrev = memUsedNow;\n            }\n        }\n    }\n\n    public static long memoryUsed() {\n        Runtime rt = Runtime.getRuntime();\n        return rt.totalMemory() - rt.freeMemory();\n    }\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/utils/cache/ExtensionMemoryMirror.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.utils.cache;\n\nimport java.util.Iterator;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.Map.Entry;\n\nimport com.google.common.collect.MapMaker;\n\n/**\n * 简单内存镜像实现\n * \n * <pre>\n * 1. 使用hashMap做为存储\n * 2. 支持时间戳作为版本号\n * 3. 支持ComputeFunction，在get结果为null允许进行回调处理\n * </pre>\n */\npublic class ExtensionMemoryMirror<KEY, VALUE> {\n\n    private final Map<KEY, VALUE>             store;\n    private final ComputeFunction<KEY, VALUE> function;\n\n    public ExtensionMemoryMirror(ComputeFunction<KEY, VALUE> function){\n        this.function = function;\n        store = new MapMaker().makeMap();\n    }\n\n    public synchronized VALUE get(KEY key) {\n        if (store.containsKey(key)) {\n            KEY oldKey = null;\n            for (KEY k : store.keySet()) {\n                if (k.equals(key)) {\n                    oldKey = k;\n                    break;\n                }\n            }\n\n            if (((Comparable) key).compareTo(oldKey) > 0) {\n                VALUE result = function.apply(key);\n                // 一定要先删除key，否则put时key不会被更新，对应的时间戳一直为老对象\n                remove(key);\n                put(key, result);\n                return result;\n            } else {\n                return store.get(key);\n            }\n        } else {\n            VALUE result = function.apply(key);\n            remove(key);\n            put(key, result);\n            return result;\n        }\n\n    }\n\n    public synchronized void put(KEY key, VALUE value) {\n        store.put(getKey(key), value);\n    }\n\n    public synchronized void remove(Object key) {\n        store.remove(getKey(key));\n    }\n\n    public synchronized void clear() {\n        store.clear();\n    }\n\n    public Iterator getKeys() {\n        return store.keySet().iterator();\n    }\n\n    public Set<Entry<KEY, VALUE>> entrySet() {\n        return store.entrySet();\n    }\n\n    public int size() {\n        return store.size();\n    }\n\n    private KEY getKey(Object key) {\n        if (key == null) {\n            throw new IllegalArgumentException(\"Cache key not be null\");\n        }\n\n        return (KEY) key;\n    }\n\n    public static interface ComputeFunction<KEY, VALUE> {\n\n        VALUE apply(KEY key);\n\n    }\n\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/utils/cache/RefreshMemoryMirror.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.utils.cache;\n\nimport java.util.Calendar;\nimport java.util.Date;\nimport java.util.Iterator;\nimport java.util.Map;\n\nimport org.apache.commons.lang.ObjectUtils;\nimport org.apache.commons.lang.builder.ToStringBuilder;\n\nimport com.alibaba.otter.shared.common.utils.OtterToStringStyle;\nimport com.google.common.collect.MapMaker;\n\n/**\n * 简单内存镜像实现\n * \n * <pre>\n * 1. 使用hashMap做为存储\n * 2. 支持过期时间\n * 3. 支持ComputeFunction，在get结果为null允许进行回调处理\n * \n * 后期改进：优化下并行处理性能，目前比较暴力加了synchronized\n * </pre>\n * \n * @author jianghang 2011-10-9 下午01:08:46\n * @version 4.0.0\n */\npublic class RefreshMemoryMirror<KEY, VALUE> {\n\n    private final Long                              period;\n    private final Map<String, RefreshObject<VALUE>> store;\n    private final ComputeFunction<KEY, VALUE>       function;\n\n    public RefreshMemoryMirror(Long period, ComputeFunction<KEY, VALUE> function){\n        this.period = period;\n        this.function = function;\n        store = new MapMaker().makeMap();\n    }\n\n    public synchronized VALUE get(KEY key) {\n        RefreshObject<VALUE> object = store.get(getKey(key));\n\n        if (object == null) {// 记录为空,直接返回null\n            VALUE result = function.apply(key, null);\n            put(key, result);\n            return result;\n        } else {\n            if (isExpired(object)) { // 判断是否过期,过期清空数据\n                VALUE result = function.apply(key, object.getValue());\n                put(key, result);\n                return result;\n            } else {\n                return object.getValue();\n            }\n        }\n    }\n\n    public synchronized void put(KEY key, VALUE value) {\n        RefreshObject<VALUE> object = new RefreshObject<VALUE>(value);\n        store.put(getKey(key), object);\n    }\n\n    public synchronized void remove(Object key) {\n        store.remove(getKey(key));\n    }\n\n    public synchronized void clear() {\n        store.clear();\n    }\n\n    public Iterator getKeys() {\n        return store.keySet().iterator();\n    }\n\n    /**\n     * 判断对象是否过期\n     * \n     * @param refreshObject\n     * @return\n     */\n    private boolean isExpired(RefreshObject refreshObject) {\n        if (refreshObject == null) {\n            return false;\n        }\n\n        Calendar calendar = Calendar.getInstance();\n        calendar.setTimeInMillis(refreshObject.getTimestamp());\n        // calendar.add(Calendar.SECOND, period.intValue());\n        calendar.add(Calendar.MILLISECOND, period.intValue());\n\n        Date now = new Date();\n        return now.after(calendar.getTime());\n    }\n\n    /**\n     * 取得对应的key String\n     * \n     * @param key\n     * @return\n     */\n    private String getKey(Object key) {\n        if (key == null) {\n            throw new IllegalArgumentException(\"Cache key not be null\");\n        }\n\n        return ObjectUtils.toString(key);\n    }\n\n    public String toString() {\n        return \"RefreshMemoryCache[period=\" + period + \", size=\" + store.size() + \"]\";\n    }\n\n    /**\n     * cache失效后重新计算的函数\n     */\n    public static interface ComputeFunction<KEY, VALUE> {\n\n        VALUE apply(KEY key, VALUE oldValue);\n    }\n\n    /**\n     * cache对象\n     */\n    public static class RefreshObject<VALUE> {\n\n        private long  timestamp; // 记录数据存入时间戳\n        private VALUE value;    // 记录具体的对象值\n\n        public RefreshObject(VALUE value){\n            this.value = value;\n            timestamp = new Date().getTime();\n        }\n\n        public VALUE getValue() {\n            return value;\n        }\n\n        public void setValue(VALUE value) {\n            this.value = value;\n        }\n\n        public long getTimestamp() {\n            return timestamp;\n        }\n\n        public void setTimestamp(long timestamp) {\n            this.timestamp = timestamp;\n        }\n\n        @Override\n        public String toString() {\n            return ToStringBuilder.reflectionToString(this, OtterToStringStyle.SIMPLE_STYLE);\n        }\n    }\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/utils/cmd/Exec.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.utils.cmd;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.File;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.OutputStream;\n\nimport org.apache.commons.io.IOUtils;\nimport org.apache.commons.lang.StringUtils;\nimport org.apache.commons.lang.SystemUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * 外部脚本调用工具\n * \n * <pre>\n * example:\n *  Result result = Exec.executre(\"pwd\");\n *  String dir = result.getStdout();\n * </pre>\n * \n * @author jianghang 2011-9-27 上午10:19:19\n * @version 4.0.0\n */\npublic class Exec {\n\n    private static final Logger logger = LoggerFactory.getLogger(Exec.class);\n\n    public static Result execute(String cmd) throws Exception {\n        return execute(cmd, null, null, null);\n    }\n\n    public static Result execute(String cmd, String outputLog) throws Exception {\n        return execute(cmd, outputLog, null, null);\n    }\n\n    public static Result execute(String cmd, String outputLog, byte[] input) throws Exception {\n        return execute(cmd, outputLog, input, null);\n    }\n\n    public static Result execute(String cmd, String outputLog, byte[] input, File workingDir) throws Exception {\n        // 注意单command命令和多command命令使用方式有所不同，不可完全兼容\n        Process process = Runtime.getRuntime().exec(cmd, null, workingDir);\n        return execute(process, cmd, outputLog, input, workingDir);\n    }\n\n    public static Result execute(String[] cmds) throws Exception {\n        return execute(cmds, null, null, null);\n    }\n\n    public static Result execute(String[] cmds, String outputLog) throws Exception {\n        return execute(cmds, outputLog, null, null);\n    }\n\n    public static Result execute(String[] cmds, String outputLog, byte[] input) throws Exception {\n        return execute(cmds, outputLog, input, null);\n    }\n\n    public static Result execute(String[] cmds, String outputLog, byte[] input, File workingDir) throws Exception {\n        // 注意单command命令和多command命令使用方式有所不同，不可完全兼容\n        Process process = Runtime.getRuntime().exec(cmds, null, workingDir);\n        return execute(process, StringUtils.join(cmds, \" \"), outputLog, input, workingDir);\n    }\n\n    public static Result execute(Process process, String cmd, String outputLog, byte[] input, File workingDir)\n                                                                                                              throws Exception {\n        // 处理输入参数流\n        Thread inputThread = new InputPumper((input == null) ? new byte[] {} : input, process.getOutputStream());\n        StreamCollector stderr = null;\n        StreamCollector stdout = null;\n        FileOutputStream fileOutput = null;\n        StreamAppender outputLogger = null;\n        String errString = null;\n        String outString = null;\n\n        try {\n            if (outputLog == null) {\n                stdout = new StreamCollector(process.getInputStream());\n                stderr = new StreamCollector(process.getErrorStream());\n                stdout.start();\n                stderr.start();\n            } else {\n                errString = \"stderr output redirected to file \" + outputLog;\n                outString = \"stdout output redirected to file \" + outputLog;\n                fileOutput = new FileOutputStream(outputLog);\n                outputLogger = new StreamAppender(fileOutput);\n                outputLogger.writeInput(process.getErrorStream(), process.getInputStream());\n            }\n\n            inputThread.start();\n\n            final int exitCode = process.waitFor();\n\n            inputThread.join();\n\n            if (outputLogger != null) {\n                outputLogger.finish();\n            }\n\n            if (stdout != null) {\n                stdout.join();\n                outString = stdout.toString();\n            }\n\n            if (stderr != null) {\n                stderr.join();\n                errString = stderr.toString();\n            }\n\n            return new Result(cmd.toString(), outString, errString, exitCode);\n        } finally {\n            IOUtils.closeQuietly(fileOutput);\n\n            if (process != null) {\n                // evitons http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6462165\n                process.getInputStream().close();\n                process.getOutputStream().close();\n                process.getErrorStream().close();\n                process.destroy();\n            }\n        }\n    }\n\n    // 参数输入处理\n    private static class InputPumper extends Thread {\n\n        private final InputStream  input;\n        private final OutputStream output;\n\n        InputPumper(byte[] input, OutputStream output){\n            this.output = output;\n            this.input = new ByteArrayInputStream(input);\n            this.setName(\"Input Pumper\");\n            this.setDaemon(true);\n        }\n\n        public void run() {\n            try {\n                IOUtils.copy(input, output);\n            } catch (IOException e) {\n                logger.error(\"\", e);\n            } finally {\n                IOUtils.closeQuietly(output);\n            }\n        }\n    }\n\n    public static class Result {\n\n        private final String cmd;\n        private final int    exitCode;\n        private final String stderr;\n        private final String stdout;\n\n        private Result(String cmd, String stdout, String stderr, int exitCode){\n            this.cmd = cmd;\n            this.stdout = stdout;\n            this.stderr = stderr;\n            this.exitCode = exitCode;\n        }\n\n        public String getStderr() {\n            return stderr;\n        }\n\n        public String getStdout() {\n            return stdout;\n        }\n\n        public int getExitCode() {\n            return exitCode;\n        }\n\n        public String toString() {\n            return String.format(\"Command: %s%sexit code:%s%sstdout:%s%s%sstderr:%s%s%s\", cmd,\n                                 SystemUtils.LINE_SEPARATOR, exitCode, SystemUtils.LINE_SEPARATOR,\n                                 SystemUtils.LINE_SEPARATOR, stdout, SystemUtils.LINE_SEPARATOR,\n                                 SystemUtils.LINE_SEPARATOR, stderr, SystemUtils.LINE_SEPARATOR);\n        }\n    }\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/utils/cmd/StreamAppender.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.utils.cmd;\n\nimport java.io.BufferedReader;\nimport java.io.BufferedWriter;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.InputStreamReader;\nimport java.io.OutputStream;\nimport java.io.OutputStreamWriter;\nimport java.io.PrintWriter;\n\nimport org.apache.commons.io.IOUtils;\n\npublic class StreamAppender {\n\n    private Thread      errWriter;\n    private Thread      outWriter;\n    private PrintWriter output;\n\n    public StreamAppender(OutputStream output){\n        this.output = new PrintWriter(new BufferedWriter(new OutputStreamWriter(output)));\n    }\n\n    public void writeInput(final InputStream err, final InputStream out) {\n        errWriter = new Thread() {\n\n            BufferedReader reader = new BufferedReader(new InputStreamReader(err));\n\n            public void run() {\n                try {\n                    String line = null;\n                    while ((line = reader.readLine()) != null) {\n                        output.println(line);\n                    }\n                } catch (IOException e) {\n                    //ignore\n                } finally {\n                    output.flush();// 关闭之前flush一下\n                    IOUtils.closeQuietly(reader);\n                }\n            }\n        };\n        outWriter = new Thread() {\n\n            BufferedReader reader = new BufferedReader(new InputStreamReader(out));\n\n            public void run() {\n                try {\n                    String line = null;\n                    while ((line = reader.readLine()) != null) {\n                        output.println(line);\n                    }\n                } catch (IOException e) {\n                    //ignore\n                } finally {\n                    output.flush();// 关闭之前flush一下\n                    IOUtils.closeQuietly(reader);\n                }\n            }\n        };\n        errWriter.setDaemon(true);\n        outWriter.setDaemon(true);\n        errWriter.start();\n        outWriter.start();\n    }\n\n    public void finish() throws Exception {\n        outWriter.join();\n        errWriter.join();\n    }\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/utils/cmd/StreamCollector.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.utils.cmd;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.InputStream;\n\n/**\n * An object that reads a stream asynchronously and collects it into a data buffer.\n */\npublic class StreamCollector extends StreamCopier {\n\n    public StreamCollector(InputStream stream){\n        // 使用alibaba common io，避免byte多次拷贝\n        super(stream, new ByteArrayOutputStream());\n    }\n\n    public String toString() {\n        return new String(((ByteArrayOutputStream) this.out).toByteArray());\n    }\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/utils/cmd/StreamCopier.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.utils.cmd;\n\n//~--- non-JDK imports --------------------------------------------------------\n\nimport java.io.BufferedReader;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.InputStreamReader;\nimport java.io.OutputStream;\n\nimport org.apache.commons.io.IOUtils;\nimport org.apache.commons.lang.StringUtils;\nimport org.apache.commons.lang.SystemUtils;\n\n/**\n * A simple thread that copies one stream to another. Useful for copying a process's output/error streams to this\n * process's output/error streams.\n */\npublic class StreamCopier extends Thread {\n\n    protected final String         identifier;\n    protected final OutputStream   out;\n    protected final BufferedReader reader;\n\n    public StreamCopier(InputStream stream, OutputStream out){\n        this(stream, out, null);\n    }\n\n    public StreamCopier(InputStream stream, OutputStream out, String identifier){\n        if ((stream == null) || (out == null)) {\n            throw new AssertionError(\"null streams not allowed\");\n        }\n\n        this.reader = new BufferedReader(new InputStreamReader(stream));\n        this.out = out;\n        this.identifier = identifier;\n        this.setName(\"Stream Copier\");\n        this.setDaemon(true);\n    }\n\n    public void run() {\n        try {\n            StringBuffer buf = new StringBuffer();\n            String line = null;\n\n            while ((line = reader.readLine()) != null) {\n                if (identifier != null) {\n                    line = identifier + line;\n                }\n\n                if (false == StringUtils.isBlank(line)) {\n                    buf.append(line).append(SystemUtils.LINE_SEPARATOR);\n                }\n            }\n\n            out.write(buf.toString().getBytes());\n            out.flush();\n        } catch (IOException ioe) {\n            //ignore\n        } finally {\n            IOUtils.closeQuietly(reader);\n        }\n    }\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/utils/code/Code.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.utils.code;\n\n/**\n * 返回值定义的code对象定义\n * \n * @author jianghang 2011-9-13 下午04:46:42\n */\npublic interface Code {\n\n    public String getCode();\n\n    public String getMessage(String... params);\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/utils/code/ResourceBundleUtil.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.utils.code;\n\nimport java.text.MessageFormat;\nimport java.util.MissingResourceException;\nimport java.util.ResourceBundle;\n\nimport org.apache.commons.lang.StringUtils;\n\n/**\n * <pre>\n * ResourceBundle工具类\n * 不同的资源文件,创建不同的 ResourceBundleUtil,从该资源文件中,得到key对应的描述信息\n * \n * 使用方法:\n * \n * 资源文件 res/BundleName.properties内容如下:\n * key1=value1\n * key2=value2,{0}\n * \n * 代码:\n * ResourceBundleUtil util = new ResourceBundleUtil(\"res/BundleName\");\n * util.getMessage(\"key1\");                   //输出:value1\n * util.getMessage(\"key2\",\"stone\");           //输出:value2,stone\n * </pre>\n * \n * @author jianghang 2011-9-13 下午04:44:05\n */\npublic class ResourceBundleUtil {\n\n    private ResourceBundle bundle; // 资源\n\n    /**\n     * 构建ResourceBundleUtil,初始化bundle\n     * \n     * @param bundleName 资源名\n     * @throws MissingResourceException 资源文件不存在,则抛出运行期异常\n     */\n    public ResourceBundleUtil(String bundleName){\n        this.bundle = ResourceBundle.getBundle(bundleName);\n    }\n\n    /**\n     * <pre>\n     * 从资源文件中,根据key,得到详细描述信息\n     * 资源文件中,key对应的message允许占位符,根据params组成动态的描述信息\n     * \n     * 如果key为null,则返回null\n     * 如果key对应的message为null,则返回null\n     * </pre>\n     * \n     * @param key 详细描述对应的关键词\n     * @param params 占位符对应的内容\n     * @return 详细描述信息\n     */\n    public String getMessage(String key, String... params) {\n        // 参数校验\n        if (key == null) {\n            return null;\n        }\n        // 得到message内容\n        String msg = bundle.getString(key);\n        // 如果不存在动态内容,则直接返回msg\n        if (params == null || params.length == 0) {\n            return msg;\n        }\n        // 存在动态内容,渲染后返回新的message\n        if (StringUtils.isBlank(msg)) {\n            // 如果得到的msg为null或者空字符串,则直接返回msg本身\n            return msg;\n        }\n        return MessageFormat.format(msg, (Object[]) params);\n    }\n\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/utils/compile/JavaSourceCompiler.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.utils.compile;\n\nimport com.alibaba.otter.shared.common.utils.compile.model.JavaSource;\n\npublic interface JavaSourceCompiler {\n\n    Class compile(String sourceString);\n\n    Class compile(JavaSource javaSource);\n\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/utils/compile/exception/CompileExprException.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.utils.compile.exception;\n\n/**\n * 类CompileExprException.java的实现描述：TODO 类实现描述\n * \n * @author simon 2012-10-18 上午10:36:05\n * @version 4.1.0\n */\npublic class CompileExprException extends RuntimeException {\n\n    private static final long serialVersionUID = 1L;\n\n    public CompileExprException(String message){\n        super(message);\n    }\n\n    public CompileExprException(String message, Throwable cause){\n        super(message, cause);\n    }\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/utils/compile/exception/JdkCompileException.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.utils.compile.exception;\n\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.Set;\n\nimport javax.tools.DiagnosticCollector;\nimport javax.tools.JavaFileObject;\n\n/**\n * @author wenshao<szujobs@hotmail.com>\n */\npublic class JdkCompileException extends Exception {\n\n    private static final long                             serialVersionUID = 1L;\n\n    private Set<String>                                   classNames;\n    private transient DiagnosticCollector<JavaFileObject> diagnostics;\n\n    private String                                        source;\n\n    public JdkCompileException(String message, Set<String> qualifiedClassNames, Throwable cause,\n                               DiagnosticCollector<JavaFileObject> diagnostics){\n        super(message, cause);\n        setClassNames(qualifiedClassNames);\n        setDiagnostics(diagnostics);\n    }\n\n    public String getSource() {\n        return source;\n    }\n\n    public void setSource(String source) {\n        this.source = source;\n    }\n\n    public JdkCompileException(String message, Set<String> qualifiedClassNames,\n                               DiagnosticCollector<JavaFileObject> diagnostics){\n        super(message);\n        setClassNames(qualifiedClassNames);\n        setDiagnostics(diagnostics);\n    }\n\n    public JdkCompileException(Set<String> qualifiedClassNames, Throwable cause,\n                               DiagnosticCollector<JavaFileObject> diagnostics){\n        super(cause);\n        setClassNames(qualifiedClassNames);\n        setDiagnostics(diagnostics);\n    }\n\n    private void setClassNames(Set<String> qualifiedClassNames) {\n        // create a new HashSet because the set passed in may not\n        // be Serializable. For example, Map.keySet() returns a non-Serializable\n        // set.\n        classNames = new HashSet<String>(qualifiedClassNames);\n    }\n\n    private void setDiagnostics(DiagnosticCollector<JavaFileObject> diagnostics) {\n        this.diagnostics = diagnostics;\n    }\n\n    /**\n     * Gets the diagnostics collected by this exception.\n     * \n     * @return this exception's diagnostics\n     */\n    public DiagnosticCollector<JavaFileObject> getDiagnostics() {\n        return diagnostics;\n    }\n\n    /**\n     * @return The name of the classes whose compilation caused the compile exception\n     */\n    public Collection<String> getClassNames() {\n        return Collections.unmodifiableSet(classNames);\n    }\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/utils/compile/impl/JdkCompileTask.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.utils.compile.impl;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.net.URI;\nimport java.net.URISyntaxException;\nimport java.net.URL;\nimport java.net.URLClassLoader;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\n\nimport javax.tools.DiagnosticCollector;\nimport javax.tools.JavaCompiler;\nimport javax.tools.JavaCompiler.CompilationTask;\nimport javax.tools.JavaFileObject;\nimport javax.tools.StandardJavaFileManager;\nimport javax.tools.StandardLocation;\nimport javax.tools.ToolProvider;\n\nimport com.alibaba.otter.shared.common.utils.compile.exception.JdkCompileException;\nimport com.alibaba.otter.shared.common.utils.compile.model.JavaFileManagerImpl;\nimport com.alibaba.otter.shared.common.utils.compile.model.JavaFileObjectImpl;\nimport com.alibaba.otter.shared.common.utils.compile.model.JdkCompilerClassLoader;\n\npublic class JdkCompileTask<T> {\n\n    public static final String                  JAVA_EXTENSION = \".java\";\n\n    private final JdkCompilerClassLoader        classLoader;\n\n    private final JavaCompiler                  compiler;\n\n    private final List<String>                  options;\n\n    private DiagnosticCollector<JavaFileObject> diagnostics;\n\n    private JavaFileManagerImpl                 javaFileManager;\n\n    public JdkCompileTask(JdkCompilerClassLoader classLoader, Iterable<String> options){\n        compiler = ToolProvider.getSystemJavaCompiler();\n        if (compiler == null) {\n            throw new IllegalStateException(\"Cannot find the system Java compiler. \"\n                                            + \"Check that your class path includes tools.jar\");\n        }\n\n        this.classLoader = classLoader;\n        ClassLoader loader = classLoader.getParent();\n        diagnostics = new DiagnosticCollector<JavaFileObject>();\n        final StandardJavaFileManager fileManager = compiler.getStandardFileManager(diagnostics, null, null);\n\n        if (loader instanceof URLClassLoader\n            && (!loader.getClass().getName().equalsIgnoreCase(\"sun.misc.Launcher$AppClassLoader\"))) {\n            try {\n                @SuppressWarnings(\"resource\")\n                URLClassLoader urlClassLoader = (URLClassLoader) loader;\n                List<File> path = new ArrayList<File>();\n                for (URL url : urlClassLoader.getURLs()) {\n                    File file = new File(url.getFile());\n                    path.add(file);\n                }\n\n                fileManager.setLocation(StandardLocation.CLASS_PATH, path);\n            } catch (IOException e) {\n                throw new RuntimeException(e);\n            }\n        }\n\n        javaFileManager = new JavaFileManagerImpl(fileManager, classLoader);\n        this.options = new ArrayList<String>();\n        if (options != null) { // make a save copy of input options\n            for (String option : options) {\n                this.options.add(option);\n            }\n        }\n    }\n\n    public synchronized Class compile(final String className, final CharSequence javaSource,\n                                      final DiagnosticCollector<JavaFileObject> diagnosticsList)\n                                                                                                throws JdkCompileException,\n                                                                                                ClassCastException {\n        if (diagnosticsList != null) {\n            diagnostics = diagnosticsList;\n        } else {\n            diagnostics = new DiagnosticCollector<JavaFileObject>();\n        }\n\n        Map<String, CharSequence> classes = new HashMap<String, CharSequence>(1);\n        classes.put(className, javaSource);\n\n        Map<String, Class> compiled = compile(classes, diagnosticsList);\n        Class newClass = compiled.get(className);\n        return newClass;\n    }\n\n    public synchronized Map<String, Class> compile(final Map<String, CharSequence> classes,\n                                                   final DiagnosticCollector<JavaFileObject> diagnosticsList)\n                                                                                                             throws JdkCompileException {\n        Map<String, Class> compiled = new HashMap<String, Class>();\n\n        List<JavaFileObject> sources = new ArrayList<JavaFileObject>();\n        for (Entry<String, CharSequence> entry : classes.entrySet()) {\n            String qualifiedClassName = entry.getKey();\n            CharSequence javaSource = entry.getValue();\n            if (javaSource != null) {\n                final int dotPos = qualifiedClassName.lastIndexOf('.');\n                final String className = dotPos == -1 ? qualifiedClassName : qualifiedClassName.substring(dotPos + 1);\n                final String packageName = dotPos == -1 ? \"\" : qualifiedClassName.substring(0, dotPos);\n                final JavaFileObjectImpl source = new JavaFileObjectImpl(className, javaSource);\n                sources.add(source);\n                javaFileManager.putFileForInput(StandardLocation.SOURCE_PATH,\n                    packageName,\n                    className + JAVA_EXTENSION,\n                    source);\n            }\n        }\n\n        // Get a CompliationTask from the compiler and compile the sources\n        final CompilationTask task = compiler.getTask(null, javaFileManager, diagnostics, options, null, sources);\n        final Boolean result = task.call();\n        if (result == null || !result.booleanValue()) {\n            throw new JdkCompileException(\"Compilation failed.\", classes.keySet(), diagnostics);\n        }\n\n        try {\n            // For each class name in the inpput map, get its compiled class and\n            // put it in the output map\n            for (String qualifiedClassName : classes.keySet()) {\n                final Class<T> newClass = loadClass(qualifiedClassName);\n                compiled.put(qualifiedClassName, (Class<?>) newClass);\n            }\n\n            return compiled;\n        } catch (ClassNotFoundException e) {\n            throw new JdkCompileException(classes.keySet(), e, diagnostics);\n        } catch (IllegalArgumentException e) {\n            throw new JdkCompileException(classes.keySet(), e, diagnostics);\n        } catch (SecurityException e) {\n            throw new JdkCompileException(classes.keySet(), e, diagnostics);\n        }\n    }\n\n    public Class<T> loadClass(final String qualifiedClassName) throws ClassNotFoundException {\n        return (Class<T>) classLoader.loadClass(qualifiedClassName);\n    }\n\n    public static URI toURI(String name) {\n        try {\n            return new URI(name);\n        } catch (URISyntaxException e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    public ClassLoader getClassLoader() {\n        return javaFileManager.getClassLoader();\n    }\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/utils/compile/impl/JdkCompiler.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.utils.compile.impl;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport javax.tools.DiagnosticCollector;\nimport javax.tools.JavaFileObject;\n\nimport com.alibaba.otter.shared.common.utils.compile.JavaSourceCompiler;\nimport com.alibaba.otter.shared.common.utils.compile.exception.CompileExprException;\nimport com.alibaba.otter.shared.common.utils.compile.exception.JdkCompileException;\nimport com.alibaba.otter.shared.common.utils.compile.model.JavaSource;\nimport com.alibaba.otter.shared.common.utils.compile.model.JdkCompilerClassLoader;\n\npublic class JdkCompiler implements JavaSourceCompiler {\n\n    private List<String> options;\n\n    public JdkCompiler(){\n        options = new ArrayList<String>();\n        // options.add(\"-target\");\n        // options.add(\"1.6\");\n    }\n\n    public Class compile(String sourceString) {\n        JavaSource source = new JavaSource(sourceString);\n        return compile(source);\n    }\n\n    public Class compile(JavaSource javaSource) {\n        try {\n\n            final DiagnosticCollector<JavaFileObject> errs = new DiagnosticCollector<JavaFileObject>();\n            JdkCompileTask compileTask = new JdkCompileTask(new JdkCompilerClassLoader(this.getClass().getClassLoader()),\n                options);\n            String fullName = javaSource.getPackageName() + \".\" + javaSource.getClassName();\n            Class newClass = compileTask.compile(fullName, javaSource.getSource(), errs);\n            return newClass;\n        } catch (JdkCompileException ex) {\n            DiagnosticCollector<JavaFileObject> diagnostics = ex.getDiagnostics();\n            throw new CompileExprException(\"compile error, source : \\n\" + javaSource + \", \"\n                                           + diagnostics.getDiagnostics(), ex);\n        } catch (Exception ex) {\n            throw new CompileExprException(\"compile error, source : \\n\" + javaSource, ex);\n        }\n\n    }\n\n    public void setOptions(List<String> options) {\n        this.options = options;\n    }\n\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/utils/compile/model/JavaFileManagerImpl.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.utils.compile.model;\n\nimport java.io.IOException;\nimport java.net.URI;\nimport java.net.URL;\nimport java.util.ArrayList;\nimport java.util.Enumeration;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport javax.tools.FileObject;\nimport javax.tools.ForwardingJavaFileManager;\nimport javax.tools.JavaFileManager;\nimport javax.tools.JavaFileObject;\nimport javax.tools.StandardLocation;\nimport javax.tools.JavaFileObject.Kind;\n\nimport com.alibaba.otter.shared.common.utils.compile.impl.JdkCompileTask;\n\npublic class JavaFileManagerImpl extends ForwardingJavaFileManager<JavaFileManager> {\n\n    private final JdkCompilerClassLoader   classLoader;\n\n    private final Map<URI, JavaFileObject> fileObjects = new HashMap<URI, JavaFileObject>();\n\n    public JavaFileManagerImpl(JavaFileManager fileManager, JdkCompilerClassLoader classLoader){\n        super(fileManager);\n        this.classLoader = classLoader;\n    }\n\n    public ClassLoader getClassLoader() {\n        return classLoader;\n    }\n\n    @Override\n    public FileObject getFileForInput(Location location, String packageName, String relativeName) throws IOException {\n        FileObject o = fileObjects.get(uri(location, packageName, relativeName));\n\n        if (o != null) {\n            return o;\n        }\n\n        return super.getFileForInput(location, packageName, relativeName);\n    }\n\n    public void putFileForInput(StandardLocation location, String packageName, String relativeName, JavaFileObject file) {\n        fileObjects.put(uri(location, packageName, relativeName), file);\n    }\n\n    private URI uri(Location location, String packageName, String relativeName) {\n        return JdkCompileTask.toURI(location.getName() + '/' + packageName + '/' + relativeName);\n    }\n\n    @Override\n    public JavaFileObject getJavaFileForOutput(Location location, String qualifiedName, Kind kind, FileObject outputFile)\n                                                                                                                         throws IOException {\n        JavaFileObject file = new JavaFileObjectImpl(qualifiedName, kind);\n        classLoader.add(qualifiedName, file);\n        return file;\n    }\n\n    @Override\n    public ClassLoader getClassLoader(JavaFileManager.Location location) {\n        return classLoader;\n    }\n\n    @Override\n    public String inferBinaryName(Location loc, JavaFileObject file) {\n        if (file instanceof JavaFileObjectImpl) {\n            return file.getName();\n        }\n\n        return super.inferBinaryName(loc, file);\n    }\n\n    @Override\n    public Iterable<JavaFileObject> list(Location location, String packageName, Set<Kind> kinds, boolean recurse)\n                                                                                                                 throws IOException {\n        Iterable<JavaFileObject> result = super.list(location, packageName, kinds, recurse);\n\n        ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();\n        List<URL> urlList = new ArrayList<URL>();\n        Enumeration<URL> e = contextClassLoader.getResources(\"com\");\n        while (e.hasMoreElements()) {\n            urlList.add(e.nextElement());\n        }\n\n        ArrayList<JavaFileObject> files = new ArrayList<JavaFileObject>();\n\n        if (location == StandardLocation.CLASS_PATH && kinds.contains(JavaFileObject.Kind.CLASS)) {\n            for (JavaFileObject file : fileObjects.values()) {\n                if (file.getKind() == Kind.CLASS && file.getName().startsWith(packageName)) {\n                    files.add(file);\n                }\n            }\n\n            files.addAll(classLoader.files());\n        } else if (location == StandardLocation.SOURCE_PATH && kinds.contains(JavaFileObject.Kind.SOURCE)) {\n            for (JavaFileObject file : fileObjects.values()) {\n                if (file.getKind() == Kind.SOURCE && file.getName().startsWith(packageName)) {\n                    files.add(file);\n                }\n            }\n        }\n\n        for (JavaFileObject file : result) {\n            files.add(file);\n        }\n\n        return files;\n    }\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/utils/compile/model/JavaFileObjectImpl.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.utils.compile.model;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.ByteArrayOutputStream;\nimport java.io.InputStream;\nimport java.io.OutputStream;\nimport java.net.URI;\n\nimport javax.tools.SimpleJavaFileObject;\n\nimport com.alibaba.otter.shared.common.utils.compile.impl.JdkCompileTask;\n\npublic final class JavaFileObjectImpl extends SimpleJavaFileObject {\n\n    // If kind == CLASS, this stores byte code from openOutputStream\n    private ByteArrayOutputStream byteCode = new ByteArrayOutputStream();\n\n    // if kind == SOURCE, this contains the source text\n    private final CharSequence    source;\n\n    public JavaFileObjectImpl(final String baseName, final CharSequence source){\n        super(JdkCompileTask.toURI(baseName + JdkCompileTask.JAVA_EXTENSION), Kind.SOURCE);\n        this.source = source;\n    }\n\n    public JavaFileObjectImpl(final String name, final Kind kind){\n        super(JdkCompileTask.toURI(name), kind);\n        source = null;\n    }\n\n    public JavaFileObjectImpl(URI uri, Kind kind){\n        super(uri, kind);\n        source = null;\n    }\n\n    @Override\n    public CharSequence getCharContent(final boolean ignoreEncodingErrors) throws UnsupportedOperationException {\n        if (source == null) {\n            throw new UnsupportedOperationException();\n        }\n\n        return source;\n    }\n\n    @Override\n    public InputStream openInputStream() {\n        return new ByteArrayInputStream(getByteCode());\n    }\n\n    @Override\n    public OutputStream openOutputStream() {\n        return byteCode;\n    }\n\n    public byte[] getByteCode() {\n        return byteCode.toByteArray();\n    }\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/utils/compile/model/JavaSource.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.utils.compile.model;\n\nimport org.apache.commons.lang.StringUtils;\n\nimport com.alibaba.otter.shared.common.utils.RegexUtils;\n\n/**\n * @author simon 2012-10-16 上午10:33:58\n * @version 4.1.0\n */\npublic class JavaSource {\n\n    private String packageName;\n    private String className;\n    private String source;\n\n    public JavaSource(String sourceString){\n        String className = RegexUtils.findFirst(sourceString, \"public class (?s).*?{\").split(\"extends\")[0].split(\"implements\")[0].replaceAll(\"public class \",\n                                                                                                                                             StringUtils.EMPTY).replace(\"{\",\n                                                                                                                                                                        StringUtils.EMPTY).trim();\n        String packageName = RegexUtils.findFirst(sourceString, \"package (?s).*?;\").replaceAll(\"package \",\n                                                                                               StringUtils.EMPTY).replaceAll(\";\",\n                                                                                                                             StringUtils.EMPTY).trim();\n        this.packageName = packageName;\n        this.className = className;\n        this.source = sourceString;\n    }\n\n    public JavaSource(String packageName, String className, String source){\n        this.packageName = packageName;\n        this.className = className;\n        this.source = source;\n    }\n\n    public String getPackageName() {\n        return packageName;\n    }\n\n    public void setPackageName(String packageName) {\n        this.packageName = packageName;\n    }\n\n    public String getClassName() {\n        return className;\n    }\n\n    public void setClassName(String className) {\n        this.className = className;\n    }\n\n    public String getSource() {\n        return source;\n    }\n\n    public void setSource(String source) {\n        this.source = source;\n    }\n\n    public String toString() {\n        return packageName + \".\" + className;\n    }\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/utils/compile/model/JdkCompilerClassLoader.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.utils.compile.model;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.InputStream;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport javax.tools.JavaFileObject;\n\npublic final class JdkCompilerClassLoader extends ClassLoader {\n\n    private final Map<String, JavaFileObject> classes = new HashMap<String, JavaFileObject>();\n\n    public JdkCompilerClassLoader(ClassLoader parentClassLoader){\n        super(parentClassLoader);\n    }\n\n    public Collection<JavaFileObject> files() {\n        return Collections.unmodifiableCollection(classes.values());\n    }\n\n    public void clearCache() {\n        this.classes.clear();\n    }\n\n    public JavaFileObject getJavaFileObject(String qualifiedClassName) {\n        return classes.get(qualifiedClassName);\n    }\n\n    protected synchronized Class<?> findClass(String qualifiedClassName) throws ClassNotFoundException {\n        JavaFileObject file = classes.get(qualifiedClassName);\n        if (file != null) {\n            byte[] bytes = ((JavaFileObjectImpl) file).getByteCode();\n            return defineClass(qualifiedClassName, bytes, 0, bytes.length);\n        }\n\n        try {\n            return Class.forName(qualifiedClassName);\n        } catch (ClassNotFoundException nf) {\n            // Ignore and fall through\n        }\n\n        try {\n            return Thread.currentThread().getContextClassLoader().loadClass(qualifiedClassName);\n        } catch (ClassNotFoundException nf) {\n            // Ignore and fall through\n        }\n\n        return super.findClass(qualifiedClassName);\n    }\n\n    public void add(String qualifiedClassName, final JavaFileObject javaFile) {\n        classes.put(qualifiedClassName, javaFile);\n    }\n\n    protected synchronized Class<?> loadClass(final String name, final boolean resolve) throws ClassNotFoundException {\n        try {\n            Class c = findClass(name);\n            if (c != null) {\n                if (resolve) {\n                    resolveClass(c);\n                }\n\n                return c;\n            }\n        } catch (ClassNotFoundException e) {\n            // Ignore and fall through\n        }\n\n        return super.loadClass(name, resolve);\n    }\n\n    public InputStream getResourceAsStream(final String name) {\n        if (name.endsWith(\".class\")) {\n            String qualifiedClassName = name.substring(0, name.length() - \".class\".length()).replace('/', '.');\n            JavaFileObjectImpl file = (JavaFileObjectImpl) classes.get(qualifiedClassName);\n\n            if (file != null) {\n                return new ByteArrayInputStream(file.getByteCode());\n            }\n        }\n\n        return super.getResourceAsStream(name);\n    }\n\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/utils/extension/DefaultExtensionFactory.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.utils.extension;\n\nimport org.apache.commons.lang.StringUtils;\n\nimport com.alibaba.otter.shared.common.model.config.data.ExtensionData;\nimport com.alibaba.otter.shared.common.utils.cache.ExtensionMemoryMirror;\nimport com.alibaba.otter.shared.common.utils.cache.ExtensionMemoryMirror.ComputeFunction;\nimport com.alibaba.otter.shared.common.utils.compile.impl.JdkCompiler;\nimport com.alibaba.otter.shared.common.utils.compile.model.JavaSource;\nimport com.alibaba.otter.shared.common.utils.extension.classpath.ClassScanner;\nimport com.alibaba.otter.shared.common.utils.extension.exceptions.ExtensionLoadException;\n\n/**\n * @author jianghang 2012-11-7 下午02:11:46\n * @version 4.1.2\n */\npublic class DefaultExtensionFactory implements ExtensionFactory {\n\n    private ExtensionMemoryMirror<ExtensionData, Object> resolverCache;\n    private ClassScanner                                 classPathScanner;\n    private ClassScanner                                 fileSystemScanner;\n    private JdkCompiler                                  jdkCompiler;\n\n    public DefaultExtensionFactory(){\n        resolverCache = new ExtensionMemoryMirror<ExtensionData, Object>(new ComputeFunction<ExtensionData, Object>() {\n\n            public Object apply(ExtensionData extensionData) {\n                return getExtensionInternal(extensionData);\n            }\n        });\n    }\n\n    public <T> T getExtension(Class<T> type, ExtensionData extensionData) {\n        return (T) resolverCache.get(extensionData);\n    }\n\n    private Object getExtensionInternal(ExtensionData extensionData) {\n        Class<?> clazz = null;\n        String fullname = StringUtils.EMPTY;\n\n        if (extensionData.getExtensionDataType().isClazz() && StringUtils.isNotBlank(extensionData.getClazzPath())) {\n            clazz = scan(extensionData.getClazzPath());\n            fullname = \"[\" + extensionData.getClazzPath() + \"]ClassPath\";\n        } else if (extensionData.getExtensionDataType().isSource()\n                   && StringUtils.isNotBlank(extensionData.getSourceText())) {\n            JavaSource javaSource = new JavaSource(extensionData.getSourceText());\n            clazz = jdkCompiler.compile(javaSource);\n            fullname = \"[\" + javaSource.toString() + \"]SourceText\";\n        }\n\n        if (clazz == null) {\n            throw new ExtensionLoadException(\"ERROR ## classload this fileresolver=\" + fullname + \" has an error\");\n        }\n\n        try {\n            return clazz.newInstance();\n        } catch (Exception e) {\n            throw new ExtensionLoadException(\"ERROR ## classload this fileresolver=\" + fullname + \" has an error\", e);\n        }\n    }\n\n    private Class scan(String fileResolverClassname) {\n        Class<?> clazz = classPathScanner.scan(fileResolverClassname);\n        if (clazz == null) {\n            clazz = fileSystemScanner.scan(fileResolverClassname);\n        }\n\n        return clazz;\n    }\n\n    // =============setter / getter===============\n\n    public void setClassPathScanner(ClassScanner classPathScanner) {\n        this.classPathScanner = classPathScanner;\n    }\n\n    public void setFileSystemScanner(ClassScanner fileSystemScanner) {\n        this.fileSystemScanner = fileSystemScanner;\n    }\n\n    public ClassScanner getFileSystemScanner() {\n        return fileSystemScanner;\n    }\n\n    public void setJdkCompiler(JdkCompiler jdkCompiler) {\n        this.jdkCompiler = jdkCompiler;\n    }\n\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/utils/extension/ExtensionFactory.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.utils.extension;\n\nimport com.alibaba.otter.shared.common.model.config.data.ExtensionData;\n\n/**\n * 扩展类获取接口\n * \n * @author jianghang 2012-10-23 下午04:29:18\n * @version 4.1.0\n */\npublic interface ExtensionFactory {\n\n    /**\n     * Get extension.\n     * \n     * @param type object type.\n     * @param name object name.\n     * @return object instance.\n     */\n    <T> T getExtension(Class<T> type, ExtensionData extensionData);\n\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/utils/extension/classpath/ClassFilter.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.utils.extension.classpath;\n\npublic interface ClassFilter {\n\n    boolean accept(Class<?> clazz);\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/utils/extension/classpath/ClassNameUtils.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.utils.extension.classpath;\n\n/**\n * class名字转换处理类\n * \n * @author jianghang 2012-10-23 下午04:37:48\n * @version 4.1.0\n */\npublic class ClassNameUtils {\n\n    /**\n     * Convert a class name with underscores to the corresponding column name using \"_\". A name like \"CustomerNumber\"\n     * class name would match a \"CUSTOMER_NUMBER\".\n     * \n     * @param name the class name to be converted\n     * @return the name using \"_\"\n     */\n    public static String convertClassNameToUnderscoreName(String name) {\n        StringBuilder result = new StringBuilder();\n\n        if (name != null) {\n            int len = name.length();\n\n            if (len > 0) {\n                result.append(name.charAt(0));\n\n                for (int i = 1; i < len; i++) {\n                    if (true == Character.isUpperCase(name.charAt(i))) {\n                        result.append('_');\n                    }\n\n                    result.append(name.charAt(i));\n                }\n            }\n        }\n\n        return result.toString().toUpperCase();\n    }\n\n    /**\n     * Convert a column name with underscores to the corresponding class name using \"camel case\". A name like\n     * \"customer_number\" would match a \"CustomerNumber\" class name.\n     * \n     * @param name the column name to be converted\n     * @return the name using \"camel case\"\n     */\n    public static String convertUnderscoreNameToClassName(String name) {\n        StringBuffer result = new StringBuffer();\n        boolean nextIsUpper = false;\n\n        if (name != null) {\n            int len = name.length();\n\n            if (len > 0) {\n                String s = String.valueOf(name.charAt(0));\n\n                result.append(s.toUpperCase());\n\n                for (int i = 1; i < len; i++) {\n                    s = String.valueOf(name.charAt(i));\n\n                    if (\"_\".equals(s)) {\n                        nextIsUpper = true;\n                    } else {\n                        if (nextIsUpper) {\n                            result.append(s.toUpperCase());\n                            nextIsUpper = false;\n                        } else {\n                            result.append(s.toLowerCase());\n                        }\n                    }\n                }\n            }\n        }\n\n        return result.toString();\n    }\n\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/utils/extension/classpath/ClassScanner.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.utils.extension.classpath;\n\n/**\n * 用于扫描classpath下面和外部文件系统中类.\n * \n * @author jianghang 2012-10-23 下午04:36:53\n * @version 4.1.0\n */\npublic interface ClassScanner {\n\n    Class<?> scan(String className);\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/utils/extension/classpath/ClasspathClassScanner.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.utils.extension.classpath;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Class path 扫描器\n * \n * @author xiaoqing.zhouxq\n */\npublic class ClasspathClassScanner implements ClassScanner {\n\n    private static final Logger logger = LoggerFactory.getLogger(ClasspathClassScanner.class);\n\n    public Class<?> scan(String className) {\n        try {\n            return Class.forName(className);\n        } catch (ClassNotFoundException e) {\n            logger.error(\"ERROR ## can not found this class ,the name = \" + className);\n        }\n\n        return null;\n    }\n\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/utils/extension/classpath/FileSystemClassLoader.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.utils.extension.classpath;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * 基于文件系统的classloader.\n * \n * @author xiaoqing.zhouxq\n */\npublic class FileSystemClassLoader extends ClassLoader {\n\n    private static final Logger logger = LoggerFactory.getLogger(FileSystemClassLoader.class);\n    private String              rootDir;\n\n    public FileSystemClassLoader(String rootDir, ClassLoader parent){\n        super(parent);\n        this.rootDir = rootDir;\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see java.lang.ClassLoader#findClass(java.lang.String)\n     */\n    protected Class<?> findClass(String name) throws ClassNotFoundException {\n        byte[] classData = getClassData(name);\n        if (classData == null) {\n            throw new ClassNotFoundException();\n        } else {\n            return defineClass(name, classData, 0, classData.length);\n        }\n    }\n\n    private byte[] getClassData(String className) {\n        String path = classNameToPath(className);\n        InputStream ins = null;\n        try {\n            ins = new FileInputStream(path);\n            ByteArrayOutputStream baos = new ByteArrayOutputStream();\n            int bufferSize = 4096;\n            byte[] buffer = new byte[bufferSize];\n            int bytesNumRead = 0;\n            while ((bytesNumRead = ins.read(buffer)) != -1) {\n                baos.write(buffer, 0, bytesNumRead);\n            }\n            return baos.toByteArray();\n        } catch (IOException e) {\n            logger.error(\"ERROR ## get class data has an error\", e);\n        } finally {\n            if (ins != null) {\n                try {\n                    ins.close();\n                } catch (IOException e) {\n                    logger.error(\"ERROR ## close inputstream has an error\", e);\n                }\n            }\n        }\n        return null;\n    }\n\n    private String classNameToPath(String className) {\n        return rootDir + File.separatorChar + className.replace('.', File.separatorChar) + \".class\";\n    }\n\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/utils/extension/classpath/FileSystemClassScanner.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.utils.extension.classpath;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.util.Enumeration;\nimport java.util.zip.ZipEntry;\nimport java.util.zip.ZipFile;\n\nimport org.apache.commons.io.FileUtils;\nimport org.apache.commons.io.FilenameUtils;\nimport org.apache.commons.lang.StringUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.InitializingBean;\n\n/**\n * 外部文件的class扫描器.\n * \n * @author xiaoqing.zhouxq\n */\npublic class FileSystemClassScanner implements InitializingBean, ClassScanner {\n\n    private static final Logger   logger     = LoggerFactory.getLogger(FileSystemClassScanner.class);\n\n    private static final String   CLASS_FILE = \".class\";\n    private static final String   JAR_FILE   = \".jar\";\n    private String                extendsDir;\n    private FileSystemClassLoader fileClassLoader;\n\n    @Override\n    public void afterPropertiesSet() throws Exception {\n        this.fileClassLoader = new FileSystemClassLoader(extendsDir, this.getClass().getClassLoader());\n    }\n\n    public Class<?> scan(String className) {\n        return findInDirectory(extendsDir, className);\n    }\n\n    private Class<?> findInDirectory(String dirStr, String className) {\n        File dir = StrToFile(dirStr);\n        File[] files = dir.listFiles();\n        String rootPath = dir.getPath();\n        for (File file : files) {\n            if (file.isFile()) {\n                String classFileName = file.getPath();\n                if (classFileName.endsWith(CLASS_FILE)) {\n                    String tempClassName = classFileName.substring(rootPath.length() - className.lastIndexOf(\".\"),\n                                                                   classFileName.length() - CLASS_FILE.length());\n                    if (className.equals(pathToDot(tempClassName))) {\n                        try {\n                            return fileClassLoader.loadClass(className);\n                        } catch (Exception ex) {\n                            logger.warn(\"WARN ## load this class has an error,the fileName is = \" + className, ex);\n                        }\n                    }\n                } else if (classFileName.endsWith(JAR_FILE)) {\n                    // logger.info(\"INFO ## cannot scane filesystem's jar file\");\n                    return scanInJar(classFileName, className);\n                }\n            } else if (file.isDirectory()) {\n                Class<?> clz = findInDirectory(file.toString(), className);\n                if (clz != null) {\n                    return clz;\n                }\n            }\n        }\n\n        return null;\n    }\n\n    private Class<?> scanInJar(String jarFileName, String className) {\n        ZipFile zipfile = null;\n\n        try {\n            zipfile = new ZipFile(jarFileName);\n            Enumeration<?> zipenum = zipfile.entries();\n            ZipEntry entry = null;\n            String tempClassName = null;\n\n            while (zipenum.hasMoreElements()) {\n                entry = (ZipEntry) zipenum.nextElement();\n                tempClassName = entry.getName();\n\n                if (tempClassName.endsWith(\".class\")) {\n                    tempClassName = StringUtils.replace(FilenameUtils.removeExtension(tempClassName), \"/\", \".\");\n                    if (tempClassName.equals(className)) {\n                        try {\n                            return fileClassLoader.loadClass(className);\n                        } catch (Exception ex) {\n                            logger.warn(\"WARN ## load this class has an error,the fileName is = \" + className, ex);\n                        }\n                    }\n                }\n            }\n        } catch (IOException ex) {\n            logger.error(ex.getMessage(), ex);\n        } finally {\n            if (zipfile != null) {\n                try {\n                    zipfile.close();\n                } catch (IOException ex) {\n                    logger.warn(ex.getMessage(), ex);\n                }\n            }\n        }\n        return null;\n    }\n\n    private File StrToFile(String dirString) {\n        File file = new File(dirString);\n        if (!file.exists()) {\n            file.mkdir();\n        }\n        return file;\n    }\n\n    private String pathToDot(String s) {\n        return s.replace('/', '.').replace('\\\\', '.');\n    }\n\n    public void setExtendsDir(String extendsDir) {\n        this.extendsDir = extendsDir;\n\n        File dir = new File(extendsDir);\n        if (!dir.exists()) {\n            try {\n                FileUtils.forceMkdir(dir);\n            } catch (IOException e) {\n                logger.error(\"##ERROR\", e);\n            }\n        }\n    }\n\n    public void setFileClassLoader(FileSystemClassLoader fileClassLoader) {\n        this.fileClassLoader = fileClassLoader;\n    }\n\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/utils/extension/exceptions/ExtensionLoadException.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.utils.extension.exceptions;\n\n/**\n * 类ExtensionLoadException.java的实现描述：TODO 类实现描述\n * \n * @author simon 2012-10-23 下午9:19:20\n * @version 4.1.0\n */\npublic class ExtensionLoadException extends RuntimeException {\n\n    private static final long serialVersionUID = 1L;\n\n    public ExtensionLoadException(String cause){\n        super(cause);\n    }\n\n    public ExtensionLoadException(Throwable t){\n        super(t);\n    }\n\n    public ExtensionLoadException(String cause, Throwable t){\n        super(cause, t);\n    }\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/utils/lock/BooleanMutex.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.utils.lock;\n\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.TimeoutException;\nimport java.util.concurrent.locks.AbstractQueuedSynchronizer;\n\n/**\n * 实现一个互斥实现，基于Cocurrent中的{@linkplain AbstractQueuedSynchronizer}实现了自己的sync <br/>\n * 应用场景：系统初始化/授权控制，没权限时阻塞等待。有权限时所有线程都可以快速通过\n * \n * <pre>\n * false : 代表需要被阻塞挂起，等待mutex变为true被唤醒\n * true : 唤醒被阻塞在false状态下的thread\n * \n * BooleanMutex mutex = new BooleanMutex(true);\n * try {\n *     mutex.get(); //当前状态为true, 不会被阻塞\n * } catch (InterruptedException e) {\n *     // do something\n * }\n * \n * mutex.set(false);\n * try {\n *     mutex.get(); //当前状态为false, 会被阻塞直到另一个线程调用mutex.set(true);\n * } catch (InterruptedException e) {\n *     // do something\n * }\n * </pre>\n * \n * @author jianghang 2011-9-23 上午09:58:03\n * @version 4.0.0\n */\npublic class BooleanMutex {\n\n    private Sync sync;\n\n    public BooleanMutex(){\n        sync = new Sync();\n        set(false);\n    }\n\n    public BooleanMutex(Boolean mutex){\n        sync = new Sync();\n        set(mutex);\n    }\n\n    /**\n     * 阻塞等待Boolean为true\n     * \n     * @throws InterruptedException\n     */\n    public void get() throws InterruptedException {\n        sync.innerGet();\n    }\n\n    /**\n     * 阻塞等待Boolean为true,允许设置超时时间\n     * \n     * @param timeout\n     * @param unit\n     * @throws InterruptedException\n     * @throws TimeoutException\n     */\n    public void get(long timeout, TimeUnit unit) throws InterruptedException, TimeoutException {\n        sync.innerGet(unit.toNanos(timeout));\n    }\n\n    /**\n     * 重新设置对应的Boolean mutex\n     * \n     * @param mutex\n     */\n    public void set(Boolean mutex) {\n        if (mutex) {\n            sync.innerSetTrue();\n        } else {\n            sync.innerSetFalse();\n        }\n    }\n\n    public boolean state() {\n        return sync.innerState();\n    }\n\n    /**\n     * Synchronization control for BooleanMutex. Uses AQS sync state to represent run status\n     */\n    private final class Sync extends AbstractQueuedSynchronizer {\n\n        private static final long serialVersionUID = -7828117401763700385L;\n\n        /** State value representing that TRUE */\n        private static final int  TRUE             = 1;\n        /** State value representing that FALSE */\n        private static final int  FALSE            = 2;\n\n        private boolean isTrue(int state) {\n            return (state & TRUE) != 0;\n        }\n\n        /**\n         * 实现AQS的接口，获取共享锁的判断\n         */\n        protected int tryAcquireShared(int state) {\n            // 如果为true，直接允许获取锁对象\n            // 如果为false，进入阻塞队列，等待被唤醒\n            return isTrue(getState()) ? 1 : -1;\n        }\n\n        /**\n         * 实现AQS的接口，释放共享锁的判断\n         */\n        protected boolean tryReleaseShared(int ignore) {\n            // 始终返回true，代表可以release\n            return true;\n        }\n\n        boolean innerState() {\n            return isTrue(getState());\n        }\n\n        void innerGet() throws InterruptedException {\n            acquireSharedInterruptibly(0);\n        }\n\n        void innerGet(long nanosTimeout) throws InterruptedException, TimeoutException {\n            if (!tryAcquireSharedNanos(0, nanosTimeout)) throw new TimeoutException();\n        }\n\n        void innerSetTrue() {\n            for (;;) {\n                int s = getState();\n                if (s == TRUE) {\n                    return; // 直接退出\n                }\n                if (compareAndSetState(s, TRUE)) {// cas更新状态，避免并发更新true操作\n                    releaseShared(0);// 释放一下锁对象，唤醒一下阻塞的Thread\n                    return;\n                }\n            }\n        }\n\n        void innerSetFalse() {\n            for (;;) {\n                int s = getState();\n                if (s == FALSE) {\n                    return; // 直接退出\n                }\n                if (compareAndSetState(s, FALSE)) {// cas更新状态，避免并发更新false操作\n                    return;\n                }\n            }\n        }\n\n    }\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/utils/meta/DdlSchemaFilter.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.utils.meta;\n\n/**\n * @author hatterjiang\n */\npublic interface DdlSchemaFilter {\n\n    boolean accept(String schemaName);\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/utils/meta/DdlTableNameFilter.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.utils.meta;\n\n/**\n * @author hatterjiang\n */\npublic interface DdlTableNameFilter {\n\n    boolean accept(String catalogName, String schemaName, String tableName);\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/utils/meta/DdlUtils.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.utils.meta;\n\nimport java.sql.Connection;\nimport java.sql.DatabaseMetaData;\nimport java.sql.PreparedStatement;\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\nimport java.sql.Types;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.apache.commons.lang.StringUtils;\nimport org.apache.commons.lang.builder.ToStringBuilder;\nimport org.apache.commons.lang.builder.ToStringStyle;\nimport org.apache.commons.lang.math.NumberUtils;\nimport org.apache.ddlutils.model.Column;\nimport org.apache.ddlutils.model.Table;\nimport org.apache.ddlutils.platform.DatabaseMetaDataWrapper;\nimport org.apache.ddlutils.platform.MetaDataColumnDescriptor;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.dao.DataAccessException;\nimport org.springframework.jdbc.core.ConnectionCallback;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.jdbc.core.PreparedStatementCallback;\nimport org.springframework.jdbc.core.SingleColumnRowMapper;\nimport org.springframework.jdbc.support.JdbcUtils;\nimport org.springframework.util.Assert;\n\n/**\n * copy from otter3.0\n * \n * @author xiaoqing.zhouxq 2012-3-30 上午10:43:04\n * @author zebin.xuzb add filter for data\n */\npublic class DdlUtils {\n\n    private static final Logger               logger                = LoggerFactory.getLogger(DdlUtils.class);\n    private static TableType[]                SUPPORTED_TABLE_TYPES = new TableType[] { TableType.view, TableType.table };\n    private final static Map<Integer, String> _defaultSizes         = new HashMap<Integer, String>();\n    static {\n        _defaultSizes.put(new Integer(1), \"254\");\n        _defaultSizes.put(new Integer(12), \"254\");\n        _defaultSizes.put(new Integer(-1), \"254\");\n        _defaultSizes.put(new Integer(-2), \"254\");\n        _defaultSizes.put(new Integer(-3), \"254\");\n        _defaultSizes.put(new Integer(-4), \"254\");\n        _defaultSizes.put(new Integer(4), \"32\");\n        _defaultSizes.put(new Integer(-5), \"64\");\n        _defaultSizes.put(new Integer(7), \"7,0\");\n        _defaultSizes.put(new Integer(6), \"15,0\");\n        _defaultSizes.put(new Integer(8), \"15,0\");\n        _defaultSizes.put(new Integer(3), \"15,15\");\n        _defaultSizes.put(new Integer(2), \"15,15\");\n    }\n\n    /**\n     * !!! Only supports MySQL\n     */\n    @SuppressWarnings(\"unchecked\")\n    public static List<String> findSchemas(JdbcTemplate jdbcTemplate, final String schemaPattern) {\n        try {\n            if (StringUtils.isEmpty(schemaPattern)) {\n                return jdbcTemplate.query(\"show databases\", new SingleColumnRowMapper(String.class));\n            }\n            return jdbcTemplate.query(\"show databases like ?\",\n                new Object[] { schemaPattern },\n                new SingleColumnRowMapper(String.class));\n        } catch (Exception e) {\n            logger.error(e.getMessage(), e);\n            return new ArrayList<String>();\n        }\n    }\n\n    /**\n     * !!! Only supports MySQL\n     */\n    public static List<String> findSchemas(JdbcTemplate jdbcTemplate, final String schemaPattern,\n                                           final DdlSchemaFilter ddlSchemaFilter) {\n        List<String> schemas = findSchemas(jdbcTemplate, schemaPattern);\n        if (ddlSchemaFilter == null) {\n            return schemas;\n        }\n        List<String> filterSchemas = new ArrayList<String>();\n        for (String schema : schemas) {\n            if (ddlSchemaFilter.accept(schema)) {\n                filterSchemas.add(schema);\n            }\n        }\n        return filterSchemas;\n    }\n\n    public static Table findTable(JdbcTemplate jdbcTemplate, final String catalogName, final String schemaName,\n                                  final String tableName) throws Exception {\n        return findTable(jdbcTemplate, catalogName, schemaName, tableName, null);\n    }\n\n    public static Table findTable(final JdbcTemplate jdbcTemplate, final String catalogName, final String schemaName,\n                                  final String tableName, final DdlUtilsFilter filter) throws Exception {\n        return (Table) jdbcTemplate.execute(new ConnectionCallback() {\n\n            public Object doInConnection(Connection con) throws SQLException, DataAccessException {\n                Table table = null;\n                DatabaseMetaDataWrapper metaData = new DatabaseMetaDataWrapper();\n                boolean isDRDS = false;\n                try {\n                    if (filter != null) {\n                        con = filter.filterConnection(con);\n                        Assert.notNull(con);\n                    }\n                    DatabaseMetaData databaseMetaData = con.getMetaData();\n                    if (filter != null) {\n                        databaseMetaData = filter.filterDataBaseMetaData(jdbcTemplate, con, databaseMetaData);\n                        Assert.notNull(databaseMetaData);\n                    }\n\n                    String databaseName = databaseMetaData.getDatabaseProductName();\n                    String version = databaseMetaData.getDatabaseProductVersion();\n                    if (StringUtils.startsWithIgnoreCase(databaseName, \"mysql\")\n                        && StringUtils.contains(version, \"-TDDL-\")) {\n                        isDRDS = true;\n                    }\n\n                    metaData.setMetaData(databaseMetaData);\n                    metaData.setTableTypes(TableType.toStrings(SUPPORTED_TABLE_TYPES));\n                    metaData.setCatalog(catalogName);\n                    metaData.setSchemaPattern(schemaName);\n\n                    String convertTableName = tableName;\n                    if (databaseMetaData.storesUpperCaseIdentifiers()) {\n                        metaData.setCatalog(catalogName.toUpperCase());\n                        metaData.setSchemaPattern(schemaName.toUpperCase());\n                        convertTableName = tableName.toUpperCase();\n                    }\n                    if (databaseMetaData.storesLowerCaseIdentifiers()) {\n                        metaData.setCatalog(catalogName.toLowerCase());\n                        metaData.setSchemaPattern(schemaName.toLowerCase());\n                        convertTableName = tableName.toLowerCase();\n                    }\n\n                    ResultSet tableData = null;\n                    try {\n                        tableData = metaData.getTables(convertTableName);\n\n                        while ((tableData != null) && tableData.next()) {\n                            Map<String, Object> values = readColumns(tableData, initColumnsForTable());\n\n                            table = readTable(metaData, values);\n                            if (table.getName().equalsIgnoreCase(tableName)) {\n                                break;\n                            }\n                        }\n                    } finally {\n                        JdbcUtils.closeResultSet(tableData);\n                    }\n                } catch (Exception e) {\n                    logger.error(e.getMessage(), e);\n                }\n\n                makeAllColumnsPrimaryKeysIfNoPrimaryKeysFound(table);\n                if (isDRDS) {\n                    makeDRDSShardColumnsAsPrimaryKeys(table, jdbcTemplate, catalogName, schemaName, tableName);\n                }\n                return table;\n            }\n        });\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public static List<Table> findTables(final JdbcTemplate jdbcTemplate, final String catalogName,\n                                         final String schemaName, final String tableNamePattern,\n                                         final DdlUtilsFilter filter, final DdlTableNameFilter tableNameFilter)\n                                                                                                               throws Exception {\n        return (List<Table>) jdbcTemplate.execute(new ConnectionCallback() {\n\n            public Object doInConnection(Connection con) throws SQLException, DataAccessException {\n                List<Table> tables = new ArrayList<Table>();\n                DatabaseMetaDataWrapper metaData = new DatabaseMetaDataWrapper();\n                boolean isDRDS = false;\n                try {\n                    if (filter != null) {\n                        con = filter.filterConnection(con);\n                        Assert.notNull(con);\n                    }\n                    DatabaseMetaData databaseMetaData = con.getMetaData();\n                    if (filter != null) {\n                        databaseMetaData = filter.filterDataBaseMetaData(jdbcTemplate, con, databaseMetaData);\n                        Assert.notNull(databaseMetaData);\n                    }\n\n                    String databaseName = databaseMetaData.getDatabaseProductName();\n                    String version = databaseMetaData.getDatabaseProductVersion();\n                    if (StringUtils.startsWithIgnoreCase(databaseName, \"mysql\")\n                        && StringUtils.contains(version, \"-TDDL-\")) {\n                        isDRDS = true;\n                    }\n\n                    metaData.setMetaData(databaseMetaData);\n                    metaData.setTableTypes(TableType.toStrings(SUPPORTED_TABLE_TYPES));\n                    metaData.setCatalog(catalogName);\n                    metaData.setSchemaPattern(schemaName);\n\n                    String convertTableName = tableNamePattern;\n                    if (databaseMetaData.storesUpperCaseIdentifiers()) {\n                        metaData.setCatalog(catalogName.toUpperCase());\n                        metaData.setSchemaPattern(schemaName.toUpperCase());\n                        convertTableName = tableNamePattern.toUpperCase();\n                    }\n                    if (databaseMetaData.storesLowerCaseIdentifiers()) {\n                        metaData.setCatalog(catalogName.toLowerCase());\n                        metaData.setSchemaPattern(schemaName.toLowerCase());\n                        convertTableName = tableNamePattern.toLowerCase();\n                    }\n\n                    ResultSet tableData = null;\n                    try {\n                        tableData = metaData.getTables(convertTableName);\n\n                        while ((tableData != null) && tableData.next()) {\n                            Map<String, Object> values = readColumns(tableData, initColumnsForTable());\n\n                            Table table = readTable(metaData, values);\n                            if ((tableNameFilter == null)\n                                || tableNameFilter.accept(catalogName, schemaName, table.getName())) {\n                                tables.add(table);\n                            }\n                        }\n                    } finally {\n                        JdbcUtils.closeResultSet(tableData);\n                    }\n                } catch (Exception e) {\n                    logger.error(e.getMessage(), e);\n                }\n\n                for (Table table : tables) {\n                    makeAllColumnsPrimaryKeysIfNoPrimaryKeysFound(table);\n                    if (isDRDS) {\n                        makeDRDSShardColumnsAsPrimaryKeys(table, jdbcTemplate, catalogName, schemaName, table.getName());\n                    }\n                }\n\n                return tables;\n            }\n        });\n    }\n\n    /**\n     * Treat tables with no primary keys as a table with all primary keys.\n     */\n    private static void makeAllColumnsPrimaryKeysIfNoPrimaryKeysFound(Table table) {\n        if ((table != null) && (table.getPrimaryKeyColumns() != null) && (table.getPrimaryKeyColumns().length == 0)) {\n            Column[] allCoumns = table.getColumns();\n\n            for (Column column : allCoumns) {\n                column.setPrimaryKey(true);\n            }\n        }\n    }\n\n    private static void makeDRDSShardColumnsAsPrimaryKeys(Table table, final JdbcTemplate jdbcTemplate,\n                                                          final String catalogName, final String schemaName,\n                                                          final String tableName) {\n        String shardColumns = getShardKeyByDRDS(jdbcTemplate, catalogName, schemaName, tableName);\n        if (StringUtils.isNotEmpty(shardColumns)) {\n            String columns[] = StringUtils.split(shardColumns, ',');\n            for (String key : columns) {\n                Column col = table.findColumn(key, false);\n                if (col != null) {\n                    col.setPrimaryKey(true);\n                } else {\n                    throw new NullPointerException(String.format(\"%s pk %s is null\", tableName, key));\n                }\n            }\n        }\n    }\n\n    /**\n     * 获取DRDS下表的拆分字段, 返回格式为 id,name\n     * \n     * @param dataSource\n     * @param schemaName\n     * @param tableName\n     * @return\n     */\n    public static String getShardKeyByDRDS(final JdbcTemplate jdbcTemplate, final String catalogName,\n                                            final String schemaName, final String tableName) {\n        try {\n            return (String) jdbcTemplate.execute(\"show partitions from ?\", new PreparedStatementCallback() {\n\n                public Object doInPreparedStatement(PreparedStatement ps) throws SQLException, DataAccessException {\n                    DatabaseMetaData metaData = ps.getConnection().getMetaData();\n                    // String sName = getIdentifierName(schemaName, metaData);\n                    String convertTableName = tableName;\n                    if (metaData.storesUpperCaseIdentifiers()) {\n                        convertTableName = tableName.toUpperCase();\n                    }\n                    if (metaData.storesLowerCaseIdentifiers()) {\n                        convertTableName = tableName.toLowerCase();\n                    }\n                    String tName = convertTableName;\n                    ps.setString(1, tName);\n                    ResultSet rs = ps.executeQuery();\n                    String log = null;\n                    if (rs.next()) {\n                        log = rs.getString(\"KEYS\");\n                    }\n\n                    rs.close();\n                    return log;\n                }\n            });\n        } catch (DataAccessException e) {\n            // 兼容下oracle源库和目标库DRDS表名不一致的情况,识别一下表名不存在\n            Throwable cause = e.getRootCause();\n            if (cause instanceof SQLException) {\n                // ER_NO_SUCH_TABLE\n                if (((SQLException) cause).getErrorCode() == 1146) {\n                    return null;\n                }\n            }\n\n            throw e;\n        }\n    }\n\n    private static Table readTable(DatabaseMetaDataWrapper metaData, Map<String, Object> values) throws SQLException {\n        String tableName = (String) values.get(\"TABLE_NAME\");\n        Table table = null;\n\n        if ((tableName != null) && (tableName.length() > 0)) {\n            table = new Table();\n            table.setName(tableName);\n            table.setType((String) values.get(\"TABLE_TYPE\"));\n            table.setCatalog((String) values.get(\"TABLE_CAT\"));\n            table.setSchema((String) values.get(\"TABLE_SCHEM\"));\n            table.setDescription((String) values.get(\"REMARKS\"));\n            table.addColumns(readColumns(metaData, tableName));\n\n            Collection<String> primaryKeys = readPrimaryKeyNames(metaData, tableName);\n\n            for (Object key : primaryKeys) {\n                Column col = table.findColumn((String) key, true);\n\n                if (col != null) {\n                    col.setPrimaryKey(true);\n                } else {\n                    throw new NullPointerException(String.format(\"%s pk %s is null - %s %s\",\n                        tableName,\n                        key,\n                        ToStringBuilder.reflectionToString(metaData, ToStringStyle.SIMPLE_STYLE),\n                        ToStringBuilder.reflectionToString(values, ToStringStyle.SIMPLE_STYLE)));\n                }\n            }\n        }\n\n        return table;\n    }\n\n    private static List<MetaDataColumnDescriptor> initColumnsForTable() {\n        List<MetaDataColumnDescriptor> result = new ArrayList<MetaDataColumnDescriptor>();\n\n        result.add(new MetaDataColumnDescriptor(\"TABLE_NAME\", Types.VARCHAR));\n        result.add(new MetaDataColumnDescriptor(\"TABLE_TYPE\", Types.VARCHAR, \"UNKNOWN\"));\n        result.add(new MetaDataColumnDescriptor(\"TABLE_CAT\", Types.VARCHAR));\n        result.add(new MetaDataColumnDescriptor(\"TABLE_SCHEM\", Types.VARCHAR));\n        result.add(new MetaDataColumnDescriptor(\"REMARKS\", Types.VARCHAR));\n\n        return result;\n    }\n\n    private static List<MetaDataColumnDescriptor> initColumnsForColumn() {\n        List<MetaDataColumnDescriptor> result = new ArrayList<MetaDataColumnDescriptor>();\n\n        // As suggested by Alexandre Borgoltz, we're reading the COLUMN_DEF\n        // first because Oracle\n        // has problems otherwise (it seemingly requires a LONG column to be the\n        // first to be read)\n        // See also DDLUTILS-29\n        result.add(new MetaDataColumnDescriptor(\"COLUMN_DEF\", Types.VARCHAR));\n\n        // we're also reading the table name so that a model reader impl can\n        // filter manually\n        result.add(new MetaDataColumnDescriptor(\"TABLE_NAME\", Types.VARCHAR));\n        result.add(new MetaDataColumnDescriptor(\"COLUMN_NAME\", Types.VARCHAR));\n        result.add(new MetaDataColumnDescriptor(\"TYPE_NAME\", Types.VARCHAR));\n        result.add(new MetaDataColumnDescriptor(\"DATA_TYPE\", Types.INTEGER, new Integer(java.sql.Types.OTHER)));\n        result.add(new MetaDataColumnDescriptor(\"NUM_PREC_RADIX\", Types.INTEGER, new Integer(10)));\n        result.add(new MetaDataColumnDescriptor(\"DECIMAL_DIGITS\", Types.INTEGER, new Integer(0)));\n        result.add(new MetaDataColumnDescriptor(\"COLUMN_SIZE\", Types.VARCHAR));\n        result.add(new MetaDataColumnDescriptor(\"IS_NULLABLE\", Types.VARCHAR, \"YES\"));\n        result.add(new MetaDataColumnDescriptor(\"REMARKS\", Types.VARCHAR));\n\n        return result;\n    }\n\n    private static List<MetaDataColumnDescriptor> initColumnsForPK() {\n        List<MetaDataColumnDescriptor> result = new ArrayList<MetaDataColumnDescriptor>();\n\n        result.add(new MetaDataColumnDescriptor(\"COLUMN_NAME\", Types.VARCHAR));\n\n        // we're also reading the table name so that a model reader impl can\n        // filter manually\n        result.add(new MetaDataColumnDescriptor(\"TABLE_NAME\", Types.VARCHAR));\n\n        // the name of the primary key is currently only interesting to the pk\n        // index name resolution\n        result.add(new MetaDataColumnDescriptor(\"PK_NAME\", Types.VARCHAR));\n\n        return result;\n    }\n\n    private static List<Column> readColumns(DatabaseMetaDataWrapper metaData, String tableName) throws SQLException {\n        ResultSet columnData = null;\n\n        try {\n            columnData = metaData.getColumns(tableName, null);\n\n            List<Column> columns = new ArrayList<Column>();\n            Map<String, Object> values = null;\n\n            for (; columnData.next(); columns.add(readColumn(metaData, values))) {\n                Map<String, Object> tmp = readColumns(columnData, initColumnsForColumn());\n                if (tableName.equalsIgnoreCase((String) tmp.get(\"TABLE_NAME\"))) {\n                    values = tmp;\n                } else {\n                    break;\n                }\n            }\n\n            // if (true == columns.isEmpty()) {\n            // Connection con = metaData.getMetaData().getConnection();\n            // String propIncludeSynonyms = \"includeSynonyms\";\n            //\n            // if (PropertyUtils.isWriteable(con, propIncludeSynonyms)) {\n            // try {\n            // String includeSynonymsMethodName = \"setIncludeSynonyms\";\n            // boolean previousSynonyms = (Boolean)\n            // PropertyUtils.getProperty(con, propIncludeSynonyms);\n            //\n            // if (logger.isInfoEnabled()) {\n            // logger.info(\"ORACLE: switch includeSynonyms to \" +\n            // previousSynonyms);\n            // }\n            //\n            // try {\n            // MethodUtils.invokeMethod(con, includeSynonymsMethodName,\n            // !previousSynonyms);\n            // columns = readColumns(metaData, tableName);\n            //\n            // if (false == columns.isEmpty()) {\n            // return columns;\n            // }\n            // } finally {\n            // MethodUtils.invokeMethod(con, includeSynonymsMethodName,\n            // previousSynonyms);\n            // }\n            //\n            // throw new SQLException(\"ORACLE: no column found for \" +\n            // tableName);\n            // } catch (IllegalAccessException e) {\n            // logger.error(e.getMessage(), e);\n            // } catch (InvocationTargetException e) {\n            // logger.error(e.getMessage(), e);\n            // } catch (NoSuchMethodException e) {\n            // logger.error(e.getMessage(), e);\n            // }\n            // }\n            // }\n\n            return columns;\n        } finally {\n            JdbcUtils.closeResultSet(columnData);\n        }\n    }\n\n    private static Column readColumn(DatabaseMetaDataWrapper metaData, Map<String, Object> values) throws SQLException {\n        Column column = new Column();\n\n        column.setName((String) values.get(\"COLUMN_NAME\"));\n        column.setDefaultValue((String) values.get(\"COLUMN_DEF\"));\n        column.setTypeCode(((Integer) values.get(\"DATA_TYPE\")).intValue());\n\n        String typeName = (String) values.get(\"TYPE_NAME\");\n        // column.setType(typeName);\n\n        if ((typeName != null) && typeName.startsWith(\"TIMESTAMP\")) {\n            column.setTypeCode(Types.TIMESTAMP);\n        }\n        // modify 2013-09-25，处理下unsigned\n        if ((typeName != null) && StringUtils.containsIgnoreCase(typeName, \"UNSIGNED\")) {\n            // 如果为unsigned，往上调大一个量级，避免数据溢出\n            switch (column.getTypeCode()) {\n                case Types.TINYINT:\n                    column.setTypeCode(Types.SMALLINT);\n                    break;\n                case Types.SMALLINT:\n                    column.setTypeCode(Types.INTEGER);\n                    break;\n                case Types.INTEGER:\n                    column.setTypeCode(Types.BIGINT);\n                    break;\n                case Types.BIGINT:\n                    column.setTypeCode(Types.DECIMAL);\n                    break;\n                default:\n                    break;\n            }\n        }\n\n        Integer precision = (Integer) values.get(\"NUM_PREC_RADIX\");\n\n        if (precision != null) {\n            column.setPrecisionRadix(precision.intValue());\n        }\n\n        String size = (String) values.get(\"COLUMN_SIZE\");\n\n        if (size == null) {\n            size = (String) _defaultSizes.get(new Integer(column.getTypeCode()));\n        }\n\n        // we're setting the size after the precision and radix in case\n        // the database prefers to return them in the size value\n        column.setSize(size);\n\n        int scale = 0;\n        Object dec_digits = values.get(\"DECIMAL_DIGITS\");\n\n        if (dec_digits instanceof String) {\n            scale = (dec_digits == null) ? 0 : NumberUtils.toInt(dec_digits.toString());\n        } else if (dec_digits instanceof Integer) {\n            scale = (dec_digits == null) ? 0 : (Integer) dec_digits;\n        }\n\n        if (scale != 0) {\n            column.setScale(scale);\n        }\n\n        column.setRequired(\"NO\".equalsIgnoreCase(((String) values.get(\"IS_NULLABLE\")).trim()));\n        column.setDescription((String) values.get(\"REMARKS\"));\n        return column;\n    }\n\n    private static Map<String, Object> readColumns(ResultSet resultSet, List<MetaDataColumnDescriptor> columnDescriptors)\n                                                                                                                         throws SQLException {\n        Map<String, Object> values = new HashMap<String, Object>();\n        MetaDataColumnDescriptor descriptor;\n\n        for (Iterator<MetaDataColumnDescriptor> it = columnDescriptors.iterator(); it.hasNext(); values.put(descriptor.getName(),\n            descriptor.readColumn(resultSet))) {\n            descriptor = (MetaDataColumnDescriptor) it.next();\n        }\n\n        return values;\n    }\n\n    private static Collection<String> readPrimaryKeyNames(DatabaseMetaDataWrapper metaData, String tableName)\n                                                                                                             throws SQLException {\n        ResultSet pkData = null;\n\n        try {\n            List<String> pks = new ArrayList<String>();\n            Map<String, Object> values;\n\n            for (pkData = metaData.getPrimaryKeys(tableName); pkData.next(); pks.add(readPrimaryKeyName(metaData,\n                values))) {\n                values = readColumns(pkData, initColumnsForPK());\n            }\n\n            return pks;\n        } finally {\n            JdbcUtils.closeResultSet(pkData);\n        }\n    }\n\n    private static String readPrimaryKeyName(DatabaseMetaDataWrapper metaData, Map<String, Object> values)\n                                                                                                          throws SQLException {\n        return (String) values.get(\"COLUMN_NAME\");\n    }\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/utils/meta/DdlUtilsFilter.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.utils.meta;\n\nimport java.sql.Connection;\nimport java.sql.DatabaseMetaData;\n\nimport org.springframework.jdbc.core.JdbcTemplate;\n\n/**\n * @author zebin.xuzb @ 2012-8-8\n * @version 4.1.0\n */\npublic abstract class DdlUtilsFilter {\n\n    /**\n     * 返回要获取 {@linkplain DatabaseMetaData} 的 {@linkplain Connection}，不能返回null\n     * \n     * @param con\n     * @return\n     */\n    public Connection filterConnection(Connection con) throws Exception {\n        return con;\n    }\n\n    /**\n     * 对 databaseMetaData 做一些过滤,返回 {@linkplain DatabaseMetaData}，不能为 null\n     * \n     * @param databaseMetaData\n     * @return\n     */\n    public DatabaseMetaData filterDataBaseMetaData(JdbcTemplate jdbcTemplate, Connection con,\n                                                   DatabaseMetaData databaseMetaData) throws Exception {\n        return databaseMetaData;\n    }\n\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/utils/meta/TableType.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.utils.meta;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Locale;\n\n/**\n * An enumeration wrapper around JDBC table types.\n */\npublic enum TableType {\n\n    /**\n     * Unknown\n     */\n    unknown,\n\n    /**\n     * System table\n     */\n    system_table,\n\n    /**\n     * Global temporary\n     */\n    global_temporary,\n\n    /**\n     * Local temporary\n     */\n    local_temporary,\n\n    /**\n     * Table\n     */\n    table,\n\n    /**\n     * View\n     */\n    view,\n\n    /**\n     * Alias\n     */\n    alias,\n\n    /**\n     * Synonym\n     */\n    synonym, ;\n\n    /**\n     * Converts an array of table types to an array of their corresponding string values.\n     * \n     * @param tableTypes Array of table types\n     * @return Array of string table types\n     */\n    public static String[] toStrings(final TableType[] tableTypes) {\n        if ((tableTypes == null) || (tableTypes.length == 0)) {\n            return new String[0];\n        }\n\n        final List<String> tableTypeStrings = new ArrayList<String>(tableTypes.length);\n\n        for (final TableType tableType : tableTypes) {\n            if (tableType != null) {\n                tableTypeStrings.add(tableType.toString().toUpperCase(Locale.ENGLISH));\n            }\n        }\n\n        return tableTypeStrings.toArray(new String[tableTypeStrings.size()]);\n    }\n\n    /**\n     * Converts an array of string table types to an array of their corresponding enumeration values.\n     * \n     * @param tableTypeStrings Array of string table types\n     * @return Array of table types\n     */\n    public static TableType[] valueOf(final String[] tableTypeStrings) {\n        if ((tableTypeStrings == null) || (tableTypeStrings.length == 0)) {\n            return new TableType[0];\n        }\n\n        final List<TableType> tableTypes = new ArrayList<TableType>(tableTypeStrings.length);\n\n        for (final String tableTypeString : tableTypeStrings) {\n            tableTypes.add(valueOf(tableTypeString.toLowerCase(Locale.ENGLISH)));\n        }\n\n        return tableTypes.toArray(new TableType[tableTypes.size()]);\n    }\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/utils/sizeof/NaiveSizeOf.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.utils.sizeof;\n\nimport java.lang.reflect.Array;\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Modifier;\n\n/* http://www.glenmccl.com/tip_038.htm modified 1999-10 */\n/**\n * Sizeof For Java(tm) Java(tm) has no sizeof() operator like C/C++.With uniform sizes for primitive data types, and a\n * different style of memory allocation, the need for sizeof() really isn't there. And it's hard to define what sizeof()\n * would mean anyway, given that an object may not contain other objects, but only references to them. But it's\n * interesting to experiment with the 1.1 reflection feature and see whether a method can be devised that will return\n * useful information about object sizes. The Sizeof class below tries to do this, for a passed-in data structure. It\n * walks the structure and tallies up the total size in bytes. It ignores alignment and packing issues and hidden fields\n * in structures, and assumes a boolean is of size 1 and a reference of size 4 (reference sizes may vary; for example\n * SZ_REF might be 8 on a machine with 64-bit pointers). It does not count static data members of class instances, but\n * does include members inherited/implemented from superclasses and interfaces. It does not follow references in object\n * instances or in arrays, except for the case of a multi-dimensional array, where the reference is to another array.\n */\npublic class NaiveSizeOf {\n\n    private NaiveSizeOf(){\n        super();\n    }\n\n    private static final int SZ_REF = 4;\n\n    public static int sizeof(boolean b) {\n        return 1;\n    }\n\n    public static int sizeof(byte b) {\n        return 1;\n    }\n\n    public static int sizeof(char c) {\n        return 2;\n    }\n\n    public static int sizeof(short s) {\n        return 2;\n    }\n\n    public static int sizeof(int i) {\n        return 4;\n    }\n\n    public static int sizeof(long l) {\n        return 8;\n    }\n\n    public static int sizeof(float f) {\n        return 4;\n    }\n\n    public static int sizeof(double d) {\n        return 8;\n    }\n\n    private static int size_inst(final Class c) {\n        Field flds[] = c.getDeclaredFields();\n        int sz = 0;\n\n        for (int i = 0; i < flds.length; ++i) {\n            Field f = flds[i];\n            if (!c.isInterface() && (f.getModifiers() & Modifier.STATIC) != 0) continue;\n            sz += size_prim(f.getType());\n        }\n\n        if (c.getSuperclass() != null) sz += size_inst(c.getSuperclass());\n\n        Class cv[] = c.getInterfaces();\n        for (int i = 0; i < cv.length; ++i)\n            sz += size_inst(cv[i]);\n\n        return sz;\n    }\n\n    private static int size_prim(final Class t) {\n        if (t == Boolean.TYPE) return 1;\n        else if (t == Byte.TYPE) return 1;\n        else if (t == Character.TYPE) return 2;\n        else if (t == Short.TYPE) return 2;\n        else if (t == Integer.TYPE) return 4;\n        else if (t == Long.TYPE) return 8;\n        else if (t == Float.TYPE) return 4;\n        else if (t == Double.TYPE) return 8;\n        else if (t == Void.TYPE) return 0;\n        else return SZ_REF;\n    }\n\n    private static int size_arr(final Object obj, final Class c) {\n        Class ct = c.getComponentType();\n        int len = Array.getLength(obj);\n\n        if (ct.isPrimitive()) {\n            return len * size_prim(ct);\n        } else {\n            int sz = 0;\n            for (int i = 0; i < len; ++i) {\n                sz += SZ_REF;\n                Object obj2 = Array.get(obj, i);\n                if (obj2 == null) continue;\n                Class c2 = obj2.getClass();\n                if (!c2.isArray()) continue;\n                sz += size_arr(obj2, c2);\n            }\n            return sz;\n        }\n    }\n\n    public static int sizeof(final Object obj) {\n        if (null == obj) return 0;\n\n        Class c = obj.getClass();\n\n        if (c.isArray()) return size_arr(obj, c);\n        else return size_inst(c);\n    }\n\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/utils/sizeof/ObjectProfiler.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.utils.sizeof;\n\nimport java.lang.reflect.Array;\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Modifier;\nimport java.math.BigInteger;\nimport java.security.AccessController;\nimport java.security.PrivilegedActionException;\nimport java.security.PrivilegedExceptionAction;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.IdentityHashMap;\nimport java.util.Iterator;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.Map;\nimport java.util.WeakHashMap;\nimport java.util.jar.JarFile;\n\n// ----------------------------------------------------------------------------\n/**\n * This non-instantiable class presents an API for object sizing as described in the <a\n * href=\"http://www.javaworld.com/javaqa/2003-12/02-qa-1226-sizeof_p.html\">article</a>. See individual methods for\n * details.\n * <P>\n * This implementation is J2SE 1.4+ only. You would need to code your own identity hashmap to port this to earlier Java\n * versions.\n * <P>\n * Security: this implementation uses AccessController.doPrivileged() so it could be granted privileges to access\n * non-public class fields separately from your main application code. The minimum set of permissions necessary for this\n * class to function correctly follows:\n * \n * <pre>\n *    permission java.lang.RuntimePermission &quot;accessDeclaredMembers&quot;;\n *    permission java.lang.reflect.ReflectPermission &quot;suppressAccessChecks&quot;;\n * </pre>\n * \n * @author (C) <a href=\"http://www.javaworld.com/columns/jw-qna-index.shtml\">Vlad Roubtsov</a>, 2003\n */\npublic abstract class ObjectProfiler {\n\n    // public: ................................................................\n\n    // the following constants are physical sizes (in bytes) and are JVM-dependent:\n    // [the current values are Ok for most 32-bit JVMs]\n\n    public static final int OBJECT_SHELL_SIZE  = 8; // java.lang.Object shell\n    // size in bytes\n    public static final int OBJREF_SIZE        = 4;\n    public static final int LONG_FIELD_SIZE    = 8;\n    public static final int INT_FIELD_SIZE     = 4;\n    public static final int SHORT_FIELD_SIZE   = 2;\n    public static final int CHAR_FIELD_SIZE    = 2;\n    public static final int BYTE_FIELD_SIZE    = 1;\n    public static final int BOOLEAN_FIELD_SIZE = 1;\n    public static final int DOUBLE_FIELD_SIZE  = 8;\n    public static final int FLOAT_FIELD_SIZE   = 4;\n\n    /**\n     * Estimates the full size of the object graph rooted at 'obj'. Duplicate data instances are correctly accounted\n     * for. The implementation is not recursive.\n     * \n     * @param obj input object instance to be measured\n     * @return 'obj' size [0 if 'obj' is null']\n     */\n    public static long sizeof(final Object obj) {\n        if (null == obj || isSharedFlyweight(obj)) {\n            return 0;\n        }\n\n        final IdentityHashMap visited = new IdentityHashMap(80000);\n\n        try {\n            return computeSizeof(obj, visited, CLASS_METADATA_CACHE);\n        } catch (RuntimeException re) {\n            // re.printStackTrace();//DEBUG\n            return -1;\n        } catch (NoClassDefFoundError ncdfe) {\n            // BUG: throws \"java.lang.NoClassDefFoundError: org.eclipse.core.resources.IWorkspaceRoot\" when run in WSAD\n            // 5\n            // see\n            // http://www.javaworld.com/javaforums/showflat.php?Cat=&Board=958763&Number=15235&page=0&view=collapsed&sb=5&o=\n            // System.err.println(ncdfe);//DEBUG\n            return -1;\n        }\n    }\n\n    /**\n     * Estimates the full size of the object graph rooted at 'obj' by pre-populating the \"visited\" set with the object\n     * graph rooted at 'base'. The net effect is to compute the size of 'obj' by summing over all instance data\n     * contained in 'obj' but not in 'base'.\n     * \n     * @param base graph boundary [may not be null]\n     * @param obj input object instance to be measured\n     * @return 'obj' size [0 if 'obj' is null']\n     */\n    public static long sizedelta(final Object base, final Object obj) {\n        if (null == obj || isSharedFlyweight(obj)) {\n            return 0;\n        }\n        if (null == base) {\n            throw new IllegalArgumentException(\"null input: base\");\n        }\n\n        final IdentityHashMap visited = new IdentityHashMap(40000);\n\n        try {\n            computeSizeof(base, visited, CLASS_METADATA_CACHE);\n            return visited.containsKey(obj) ? 0 : computeSizeof(obj, visited, CLASS_METADATA_CACHE);\n        } catch (RuntimeException re) {\n            // re.printStackTrace();//DEBUG\n            return -1;\n        } catch (NoClassDefFoundError ncdfe) {\n            // BUG: throws \"java.lang.NoClassDefFoundError: org.eclipse.core.resources.IWorkspaceRoot\" when run in WSAD\n            // 5\n            // see\n            // http://www.javaworld.com/javaforums/showflat.php?Cat=&Board=958763&Number=15235&page=0&view=collapsed&sb=5&o=\n            // System.err.println(ncdfe);//DEBUG\n            return -1;\n        }\n    }\n\n    // protected: .............................................................\n\n    // package: ...............................................................\n\n    // private: ...............................................................\n\n    /*\n     * Internal class used to cache class metadata information.\n     */\n    private static final class ClassMetadata {\n\n        ClassMetadata(final int primitiveFieldCount, final int shellSize, final Field[] refFields){\n            m_primitiveFieldCount = primitiveFieldCount;\n            m_shellSize = shellSize;\n            m_refFields = refFields;\n        }\n\n        // all fields are inclusive of superclasses:\n\n        final int     m_primitiveFieldCount;\n\n        final int     m_shellSize;          // class shell size\n\n        final Field[] m_refFields;          // cached non-static fields (made accessible)\n\n    } // end of nested class\n\n    private static final class ClassAccessPrivilegedAction implements PrivilegedExceptionAction {\n\n        /** {@inheritDoc} */\n        public Object run() throws Exception {\n            return m_cls.getDeclaredFields();\n        }\n\n        void setContext(final Class cls) {\n            m_cls = cls;\n        }\n\n        private Class m_cls;\n\n    } // end of nested class\n\n    private static final class FieldAccessPrivilegedAction implements PrivilegedExceptionAction {\n\n        /** {@inheritDoc} */\n        public Object run() throws Exception {\n            m_field.setAccessible(true);\n            return null;\n        }\n\n        void setContext(final Field field) {\n            m_field = field;\n        }\n\n        private Field m_field;\n\n    } // end of nested class\n\n    private ObjectProfiler(){\n    } // this class is not extendible\n\n    /*\n     * The main worker method for sizeof() and sizedelta().\n     */\n    private static long computeSizeof(Object obj, final IdentityHashMap visited,\n                                      final Map /* <Class,ClassMetadata> */metadataMap) {\n        // this uses depth-first traversal; the exact graph traversal algorithm\n        // does not matter for computing the total size and this method could be\n        // easily adjusted to do breadth-first instead (addLast() instead of\n        // addFirst()),\n        // however, dfs/bfs require max queue length to be the length of the\n        // longest\n        // graph path/width of traversal front correspondingly, so I expect\n        // dfs to use fewer resources than bfs for most Java objects;\n\n        if (null == obj || isSharedFlyweight(obj)) {\n            return 0;\n        }\n\n        final LinkedList queue = new LinkedList();\n\n        visited.put(obj, obj);\n        queue.add(obj);\n\n        long result = 0;\n\n        final ClassAccessPrivilegedAction caAction = new ClassAccessPrivilegedAction();\n        final FieldAccessPrivilegedAction faAction = new FieldAccessPrivilegedAction();\n\n        while (!queue.isEmpty()) {\n            obj = queue.removeFirst();\n            final Class objClass = obj.getClass();\n\n            int skippedBytes = skipClassDueToSunJVMBug(objClass);\n            if (skippedBytes > 0) {\n                result += skippedBytes; // can't do better than that\n                continue;\n            }\n\n            if (objClass.isArray()) {\n                final int arrayLength = Array.getLength(obj);\n                final Class componentType = objClass.getComponentType();\n\n                result += sizeofArrayShell(arrayLength, componentType);\n\n                if (!componentType.isPrimitive()) {\n                    // traverse each array slot:\n                    for (int i = 0; i < arrayLength; ++i) {\n                        final Object ref = Array.get(obj, i);\n\n                        if ((ref != null) && !visited.containsKey(ref)) {\n                            visited.put(ref, ref);\n                            queue.addFirst(ref);\n                        }\n                    }\n                }\n            } else { // the object is of a non-array type\n                final ClassMetadata metadata = getClassMetadata(objClass, metadataMap, caAction, faAction);\n                final Field[] fields = metadata.m_refFields;\n\n                result += metadata.m_shellSize;\n\n                // traverse all non-null ref fields:\n                for (int f = 0, fLimit = fields.length; f < fLimit; ++f) {\n                    final Field field = fields[f];\n\n                    final Object ref;\n                    try { // to get the field value:\n                        ref = field.get(obj);\n                    } catch (Exception e) {\n                        throw new RuntimeException(\"cannot get field [\" + field.getName() + \"] of class [\"\n                                                   + field.getDeclaringClass().getName() + \"]: \" + e.toString());\n                    }\n\n                    if ((ref != null) && !visited.containsKey(ref)) {\n                        visited.put(ref, ref);\n                        queue.addFirst(ref);\n                    }\n                }\n            }\n        }\n\n        return result;\n    }\n\n    /*\n     * A helper method for manipulating a class metadata cache.\n     */\n    private static ClassMetadata getClassMetadata(final Class cls, final Map /* <Class,ClassMetadata> */metadataMap,\n                                                  final ClassAccessPrivilegedAction caAction,\n                                                  final FieldAccessPrivilegedAction faAction) {\n        if (null == cls) {\n            return null;\n        }\n\n        ClassMetadata result;\n        synchronized (metadataMap) {\n            result = (ClassMetadata) metadataMap.get(cls);\n        }\n        if (result != null) {\n            return result;\n        }\n\n        int primitiveFieldCount = 0;\n        int shellSize = OBJECT_SHELL_SIZE; // java.lang.Object shell\n        final List /* Field */refFields = new LinkedList();\n\n        final Field[] declaredFields;\n        try {\n            caAction.setContext(cls);\n            declaredFields = (Field[]) AccessController.doPrivileged(caAction);\n        } catch (PrivilegedActionException pae) {\n            throw new RuntimeException(\"could not access declared fields of class \" + cls.getName() + \": \"\n                                       + pae.getException());\n        }\n\n        for (int f = 0; f < declaredFields.length; ++f) {\n            final Field field = declaredFields[f];\n            if (Modifier.isStatic(field.getModifiers())) {\n                continue;\n            }\n            /*\n             * Can't do that: HashMap data is transient, for example... if (Modifier.isTransient(field.getModifiers()))\n             * { shellSize += OBJREF_SIZE; continue; }\n             */\n\n            final Class fieldType = field.getType();\n            if (fieldType.isPrimitive()) {\n                // memory alignment ignored:\n                shellSize += sizeofPrimitiveType(fieldType);\n                ++primitiveFieldCount;\n            } else {\n                // prepare for graph traversal later:\n                if (!field.isAccessible()) {\n                    try {\n                        faAction.setContext(field);\n                        AccessController.doPrivileged(faAction);\n                    } catch (PrivilegedActionException pae) {\n                        throw new RuntimeException(\"could not make field \" + field + \" accessible: \"\n                                                   + pae.getException());\n                    }\n                }\n\n                // memory alignment ignored:\n                shellSize += OBJREF_SIZE;\n                refFields.add(field);\n            }\n        }\n\n        // recurse into superclass:\n        final ClassMetadata superMetadata = getClassMetadata(cls.getSuperclass(), metadataMap, caAction, faAction);\n        if (superMetadata != null) {\n            primitiveFieldCount += superMetadata.m_primitiveFieldCount;\n            shellSize += superMetadata.m_shellSize - OBJECT_SHELL_SIZE;\n            refFields.addAll(Arrays.asList(superMetadata.m_refFields));\n        }\n\n        final Field[] _refFields = new Field[refFields.size()];\n        refFields.toArray(_refFields);\n\n        result = new ClassMetadata(primitiveFieldCount, shellSize, _refFields);\n        synchronized (metadataMap) {\n            metadataMap.put(cls, result);\n        }\n\n        return result;\n    }\n\n    /*\n     * Computes the \"shallow\" size of an array instance.\n     */\n    private static int sizeofArrayShell(final int length, final Class componentType) {\n        // this ignores memory alignment issues by design:\n\n        final int slotSize = componentType.isPrimitive() ? sizeofPrimitiveType(componentType) : OBJREF_SIZE;\n\n        return OBJECT_SHELL_SIZE + INT_FIELD_SIZE + OBJREF_SIZE + length * slotSize;\n    }\n\n    /*\n     * Returns the JVM-specific size of a primitive type.\n     */\n    private static int sizeofPrimitiveType(final Class type) {\n        if (type == int.class) {\n            return INT_FIELD_SIZE;\n        } else if (type == long.class) {\n            return LONG_FIELD_SIZE;\n        } else if (type == short.class) {\n            return SHORT_FIELD_SIZE;\n        } else if (type == byte.class) {\n            return BYTE_FIELD_SIZE;\n        } else if (type == boolean.class) {\n            return BOOLEAN_FIELD_SIZE;\n        } else if (type == char.class) {\n            return CHAR_FIELD_SIZE;\n        } else if (type == double.class) {\n            return DOUBLE_FIELD_SIZE;\n        } else if (type == float.class) {\n            return FLOAT_FIELD_SIZE;\n        } else {\n            throw new IllegalArgumentException(\"not primitive: \" + type);\n        }\n    }\n\n    // class metadata cache:\n    private static final Map CLASS_METADATA_CACHE = new WeakHashMap(101);\n\n    static final Class[]     sunProblematicClasses;\n    static final Map        /* <Class, Integer> */sunProblematicClassesSizes;\n\n    static {\n        Map classesSizes = new HashMap();\n        classesSizes.put(\"java.lang.Class\", Integers.valueOf(OBJECT_SHELL_SIZE));// not really a pb, but since this is\n        // shared, so there's no point in going\n        // further\n        // 1.3+\n        classesSizes.put(\"java.lang.Throwable\", Integers.valueOf(OBJECT_SHELL_SIZE + 4 * OBJREF_SIZE));\n        // 1.4+\n        classesSizes.put(\"sun.reflect.UnsafeStaticFieldAccessorImpl\", Integers.valueOf(OBJECT_SHELL_SIZE));// unknown\n        classesSizes.put(\"sun.reflect.UnsafeStaticBooleanFieldAccessorImpl\", Integers.valueOf(OBJECT_SHELL_SIZE));// unknown\n        classesSizes.put(\"sun.reflect.UnsafeStaticByteFieldAccessorImpl\", Integers.valueOf(OBJECT_SHELL_SIZE));// unknown\n        classesSizes.put(\"sun.reflect.UnsafeStaticShortFieldAccessorImpl\", Integers.valueOf(OBJECT_SHELL_SIZE));// unknown\n        classesSizes.put(\"sun.reflect.UnsafeStaticIntegerFieldAccessorImpl\", Integers.valueOf(OBJECT_SHELL_SIZE));// unknown\n        classesSizes.put(\"sun.reflect.UnsafeStaticLongFieldAccessorImpl\", Integers.valueOf(OBJECT_SHELL_SIZE));// unknown\n        classesSizes.put(\"sun.reflect.UnsafeStaticCharacterFieldAccessorImpl\", Integers.valueOf(OBJECT_SHELL_SIZE));// unknown\n        classesSizes.put(\"sun.reflect.UnsafeStaticFloatFieldAccessorImpl\", Integers.valueOf(OBJECT_SHELL_SIZE));// unknown\n        classesSizes.put(\"sun.reflect.UnsafeStaticDoubleFieldAccessorImpl\", Integers.valueOf(OBJECT_SHELL_SIZE));// unknown\n        classesSizes.put(\"sun.reflect.UnsafeStaticObjectFieldAccessorImpl\", Integers.valueOf(OBJECT_SHELL_SIZE));// unknown\n        // 1.5+\n        classesSizes.put(\"java.lang.Enum\", Integers.valueOf(OBJECT_SHELL_SIZE));// not really a pb, but since this is\n        // shared, so there's no point in going\n        // further\n        classesSizes.put(\"sun.reflect.ConstantPool\", Integers.valueOf(OBJECT_SHELL_SIZE + OBJECT_SHELL_SIZE));\n        sunProblematicClassesSizes = Collections.unmodifiableMap(classesSizes);\n\n        List classes = new ArrayList(sunProblematicClassesSizes.size());\n        Iterator iter = sunProblematicClassesSizes.entrySet().iterator();\n        while (iter.hasNext()) {\n            Map.Entry entry = (Map.Entry) iter.next();\n            String className = (String) entry.getKey();\n            try {\n                classes.add(Class.forName(className));\n            } catch (ClassNotFoundException cnfe) {\n                // } catch (ExceptionInInitializerError eiie) {\n                // } catch (NoClassDefFoundError ncdfe) {\n                // } catch (UnsatisfiedLinkError le) {\n            } catch (LinkageError le) {\n                // BEA JRockit 1.4 also throws NoClassDefFoundError and UnsatisfiedLinkError\n            }\n        }\n        sunProblematicClasses = (Class[]) classes.toArray(new Class[0]);\n    }\n\n    /**\n     * http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=5012949 Implementation note: we can compare classes with ==\n     * since they will always be loaded from the same ClassLoader (they are \"low\" in the hierarchy)\n     */\n    private static int skipClassDueToSunJVMBug(Class clazz) {\n        for (int i = 0; i < sunProblematicClasses.length; ++i) {\n            Class sunPbClass = sunProblematicClasses[i];\n            if (clazz == sunPbClass) {\n                return ((Integer) sunProblematicClassesSizes.get(clazz.getName())).intValue();\n            }\n        }\n        return 0;\n    }\n\n    /*\n     * Very very incomplete, but better than nothing...\n     */\n    // See http://download.oracle.com/javase/7/docs/api/constant-values.html for JDK's String constants\n    private static boolean isSharedFlyweight(Object obj) {\n        if (obj == null) {\n            return true;\n        }\n        if (obj == Boolean.TRUE || obj == Boolean.FALSE) {\n            return true;\n        }\n        if (/* obj == Locale.ROOT || *//* Java 6 */\n        obj == Locale.ENGLISH || obj == Locale.FRENCH || obj == Locale.GERMAN || obj == Locale.ITALIAN\n                || obj == Locale.JAPANESE || obj == Locale.KOREAN || obj == Locale.CHINESE\n                || obj == Locale.SIMPLIFIED_CHINESE || obj == Locale.TRADITIONAL_CHINESE || obj == Locale.FRANCE\n                || obj == Locale.GERMANY || obj == Locale.ITALY || obj == Locale.JAPAN || obj == Locale.KOREA\n                || obj == Locale.CHINA || obj == Locale.PRC || obj == Locale.TAIWAN || obj == Locale.UK\n                || obj == Locale.US || obj == Locale.CANADA || obj == Locale.CANADA_FRENCH) {\n            return true;\n        }\n        if (obj == Collections.EMPTY_SET || obj == Collections.EMPTY_LIST || obj == Collections.EMPTY_MAP) {\n            return true;\n        }\n        if (obj == BigInteger.ZERO || obj == BigInteger.ONE) {\n            return true;\n        }\n        if (obj == System.in || obj == System.out || obj == System.err) {\n            return true;\n        }\n        if (obj == String.CASE_INSENSITIVE_ORDER) {\n            return true;\n        }\n        if (obj == JarFile.MANIFEST_NAME) {\n            return true;\n        }\n        return false;\n    }\n}\n\nfinal class Integers {\n\n    private Integers(){\n    }\n\n    private static final int     cache_low  = -128;\n    private static final int     cache_high = 127;\n    private static final Integer cache[]    = new Integer[(cache_high - cache_low) + 1];\n\n    static {\n        for (int i = 0; i < cache.length; ++i) {\n            cache[i] = new Integer(i + cache_low);\n        }\n    }\n\n    /**\n     * Returns a <tt>Integer</tt> instance representing the specified <tt>int</tt> value. If a new <tt>Integer</tt>\n     * instance is not required, this method should generally be used in preference to the constructor\n     * {@link #Integer(int)}, as this method is likely to yield significantly better space and time performance by\n     * caching frequently requested values.\n     * \n     * @param i an <code>int</code> value.\n     * @return a <tt>Integer</tt> instance representing <tt>i</tt>.\n     * @since 1.5\n     */\n    public static Integer valueOf(int i) {\n        if (i >= cache_low && i <= cache_high) { // must cache\n            return cache[i - cache_low];\n        } else {\n            return new Integer(i);\n        }\n    }\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/utils/spring/PropertyPlaceholderConfigurer.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.utils.spring;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Properties;\n\nimport org.springframework.beans.factory.InitializingBean;\nimport org.springframework.context.ResourceLoaderAware;\nimport org.springframework.core.io.Resource;\nimport org.springframework.core.io.ResourceLoader;\nimport org.springframework.util.Assert;\n\n/**\n * 扩展Spring的{@linkplain org.springframework.beans.factory.config.PropertyPlaceholderConfigurer} ，增加默认值的功能。\n * 例如：${placeholder:defaultValue}，假如placeholder的值不存在，则默认取得 defaultValue。\n * \n * @author jianghang 2013-1-24 下午03:37:56\n * @version 1.0.0\n */\npublic class PropertyPlaceholderConfigurer extends org.springframework.beans.factory.config.PropertyPlaceholderConfigurer implements ResourceLoaderAware, InitializingBean {\n\n    private static final String PLACEHOLDER_PREFIX = \"${\";\n    private static final String PLACEHOLDER_SUFFIX = \"}\";\n    private ResourceLoader      loader;\n    private String[]            locationNames;\n\n    public PropertyPlaceholderConfigurer(){\n        setIgnoreUnresolvablePlaceholders(true);\n    }\n\n    public void setResourceLoader(ResourceLoader loader) {\n        this.loader = loader;\n    }\n\n    public void setLocationNames(String[] locations) {\n        this.locationNames = locations;\n    }\n\n    public void afterPropertiesSet() throws Exception {\n        Assert.notNull(loader, \"no resourceLoader\");\n\n        if (locationNames != null) {\n            for (int i = 0; i < locationNames.length; i++) {\n                locationNames[i] = resolveSystemPropertyPlaceholders(locationNames[i]);\n            }\n        }\n\n        if (locationNames != null) {\n            List<Resource> resources = new ArrayList<Resource>(locationNames.length);\n\n            for (String location : locationNames) {\n                location = trimToNull(location);\n\n                if (location != null) {\n                    resources.add(loader.getResource(location));\n                }\n            }\n\n            super.setLocations(resources.toArray(new Resource[resources.size()]));\n        }\n    }\n\n    private String resolveSystemPropertyPlaceholders(String text) {\n        StringBuilder buf = new StringBuilder(text);\n\n        for (int startIndex = buf.indexOf(PLACEHOLDER_PREFIX); startIndex >= 0;) {\n            int endIndex = buf.indexOf(PLACEHOLDER_SUFFIX, startIndex + PLACEHOLDER_PREFIX.length());\n\n            if (endIndex != -1) {\n                String placeholder = buf.substring(startIndex + PLACEHOLDER_PREFIX.length(), endIndex);\n                int nextIndex = endIndex + PLACEHOLDER_SUFFIX.length();\n\n                try {\n                    String value = resolveSystemPropertyPlaceholder(placeholder);\n\n                    if (value != null) {\n                        buf.replace(startIndex, endIndex + PLACEHOLDER_SUFFIX.length(), value);\n                        nextIndex = startIndex + value.length();\n                    } else {\n                        System.err.println(\"Could not resolve placeholder '\"\n                                           + placeholder\n                                           + \"' in [\"\n                                           + text\n                                           + \"] as system property: neither system property nor environment variable found\");\n                    }\n                } catch (Throwable ex) {\n                    System.err.println(\"Could not resolve placeholder '\" + placeholder + \"' in [\" + text\n                                       + \"] as system property: \" + ex);\n                }\n\n                startIndex = buf.indexOf(PLACEHOLDER_PREFIX, nextIndex);\n            } else {\n                startIndex = -1;\n            }\n        }\n\n        return buf.toString();\n    }\n\n    private String resolveSystemPropertyPlaceholder(String placeholder) {\n        DefaultablePlaceholder dp = new DefaultablePlaceholder(placeholder);\n        String value = System.getProperty(dp.placeholder);\n\n        if (value == null) {\n            value = System.getenv(dp.placeholder);\n        }\n\n        if (value == null) {\n            value = dp.defaultValue;\n        }\n\n        return value;\n    }\n\n    @Override\n    protected String resolvePlaceholder(String placeholder, Properties props, int systemPropertiesMode) {\n        DefaultablePlaceholder dp = new DefaultablePlaceholder(placeholder);\n        String value = super.resolvePlaceholder(dp.placeholder, props, systemPropertiesMode);\n\n        if (value == null) {\n            value = dp.defaultValue;\n        }\n\n        return trimToEmpty(value);\n    }\n\n    private static class DefaultablePlaceholder {\n\n        private final String defaultValue;\n        private final String placeholder;\n\n        public DefaultablePlaceholder(String placeholder){\n            int commaIndex = placeholder.indexOf(\":\");\n            String defaultValue = null;\n\n            if (commaIndex >= 0) {\n                defaultValue = trimToEmpty(placeholder.substring(commaIndex + 1));\n                placeholder = trimToEmpty(placeholder.substring(0, commaIndex));\n            }\n\n            this.placeholder = placeholder;\n            this.defaultValue = defaultValue;\n        }\n    }\n\n    private String trimToNull(String str) {\n        if (str == null) {\n            return null;\n        }\n\n        String result = str.trim();\n\n        if (result == null || result.length() == 0) {\n            return null;\n        }\n\n        return result;\n    }\n\n    public static String trimToEmpty(String str) {\n        if (str == null) {\n            return \"\";\n        }\n\n        return str.trim();\n    }\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/utils/test/JTesterxClassPathXmlApplicationContext.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.utils.test;\n\nimport java.util.List;\n\nimport org.jtester.module.spring.JTesterSpringContext;\nimport org.jtester.module.tracer.TracerBeanManager;\nimport org.springframework.beans.BeansException;\nimport org.springframework.beans.factory.parsing.DefaultsDefinition;\nimport org.springframework.beans.factory.parsing.EmptyReaderEventListener;\nimport org.springframework.beans.factory.support.DefaultListableBeanFactory;\nimport org.springframework.beans.factory.xml.BeanDefinitionParserDelegate;\nimport org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader;\nimport org.springframework.beans.factory.xml.DocumentDefaultsDefinition;\nimport org.springframework.beans.factory.xml.XmlBeanDefinitionReader;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.support.AbstractRefreshableApplicationContext;\n\n/**\n * 集成jtester，并扩展相关内容\n * \n * <pre>\n * 扩展内容：\n * 1. 复写initBeanDefinitionReader，增加XmlBeanDefinitionReader的自定义属性，插入自定义的\n * {@link JTesterxReaderEventListener}，用于复写lazy-init=true属性\n * 2. 复写{@link AbstractRefreshableApplicationContext}的\n * customizeBeanFactory，修改allowEagerClassLoading属性为false，使得支持lazy-init=true的设置\n * \n * <pre>\n * \n * @author jianghang 2010-6-2 上午10:58:04\n */\npublic class JTesterxClassPathXmlApplicationContext extends org.jtester.module.spring.ClassPathXmlApplicationContextFactory {\n\n    private class JTesterxSpringContext extends org.jtester.module.spring.JTesterSpringContext {\n\n        public JTesterxSpringContext(Object testedObject, String[] configLocations, boolean ignoreNoSuchBean)\n                                                                                                             throws BeansException{\n            super(testedObject, configLocations, ignoreNoSuchBean);\n        }\n\n        public JTesterxSpringContext(String[] configLocations, boolean refresh, ApplicationContext parent,\n                                     boolean ignoreNoSuchBean) throws BeansException{\n            super(configLocations, refresh, parent, ignoreNoSuchBean);\n        }\n\n        /**\n         * 下面这段本来想将spring初始化时所有的bean都置成lazy-init的模式<br>\n         * 但实现中碰到问题,主要是tracer的aop初始化上出错。\n         **/\n        @Override\n        protected void initBeanDefinitionReader(XmlBeanDefinitionReader beanDefinitionReader) {\n            beanDefinitionReader.setEventListener(new JTesterxReaderEventListener());\n        }\n\n        @Override\n        protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {\n            super.customizeBeanFactory(beanFactory);\n            beanFactory.setAllowEagerClassLoading(false);\n        }\n\n    }\n\n    /**\n     * 自定义spring ReaderEventListener<br>\n     * 参见{@link DefaultBeanDefinitionDocumentReader} 和 {@link BeanDefinitionParserDelegate}的initDefaults方法\n     * \n     * <pre>\n     * 复写defaultsRegistered方法，在跑单元测试中，强制设置default-lazy-init=true属性\n     * </pre>\n     **/\n    public static class JTesterxReaderEventListener extends EmptyReaderEventListener {\n\n        @Override\n        public void defaultsRegistered(DefaultsDefinition defaultsDefinition) {\n            if (defaultsDefinition instanceof DocumentDefaultsDefinition) {\n                DocumentDefaultsDefinition docDefault = (DocumentDefaultsDefinition) defaultsDefinition;\n                docDefault.setLazyInit(\"true\");\n            }\n        }\n    }\n\n    @Override\n    public JTesterSpringContext createApplicationContext(List<String> locations, boolean ignoreNoSuchBean) {\n        JTesterSpringContext c = new JTesterxSpringContext(locations.toArray(new String[0]), false, null,\n                                                           ignoreNoSuchBean);\n\n        TracerBeanManager.clear();\n        return c;\n    }\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/utils/thread/ExecutorServiceFactoryBean.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.utils.thread;\n\nimport java.util.concurrent.ArrayBlockingQueue;\nimport java.util.concurrent.ThreadPoolExecutor;\nimport java.util.concurrent.TimeUnit;\n\nimport org.springframework.beans.factory.DisposableBean;\nimport org.springframework.beans.factory.FactoryBean;\nimport org.springframework.beans.factory.InitializingBean;\n\n/**\n * 基于spring 提供一个async的容器\n * \n * @author jianghang 2011-11-14 下午03:48:57\n * @version 4.0.0\n */\npublic class ExecutorServiceFactoryBean implements FactoryBean, InitializingBean, DisposableBean {\n\n    private ThreadPoolExecutor executor;\n    private static final int   DEFAULT_POOL_SIZE    = 50;\n    private static final int   DEFAULT_ACCEPT_COUNT = 100;\n    private int                poolSize             = DEFAULT_POOL_SIZE;\n    private int                acceptCount          = DEFAULT_ACCEPT_COUNT;\n    private String             name                 = \"Otter-Async-Executor\";\n\n    public Object getObject() throws Exception {\n        return executor;\n    }\n\n    public Class getObjectType() {\n        return ThreadPoolExecutor.class;\n    }\n\n    public boolean isSingleton() {\n        return true;\n    }\n\n    public void afterPropertiesSet() throws Exception {\n        if (executor == null) {// 初始化一个默认值\n            executor = new ThreadPoolExecutor(poolSize, poolSize, 60, TimeUnit.SECONDS,\n                                              new ArrayBlockingQueue(acceptCount), new NamedThreadFactory(name),\n                                              new ThreadPoolExecutor.CallerRunsPolicy());\n        }\n    }\n\n    public void destroy() throws Exception {\n        executor.shutdown();\n    }\n\n    public void setExecutor(ThreadPoolExecutor executor) {\n        this.executor = executor;\n    }\n\n    public void setPoolSize(int poolSize) {\n        this.poolSize = poolSize;\n    }\n\n    public void setAcceptCount(int acceptCount) {\n        this.acceptCount = acceptCount;\n    }\n\n    public void setName(String name) {\n        this.name = name;\n    }\n\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/utils/thread/ExecutorTemplate.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.utils.thread;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.concurrent.ArrayBlockingQueue;\nimport java.util.concurrent.Callable;\nimport java.util.concurrent.ExecutorCompletionService;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Future;\nimport java.util.concurrent.ThreadPoolExecutor;\nimport java.util.concurrent.TimeUnit;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.DisposableBean;\nimport org.springframework.beans.factory.InitializingBean;\n\n/**\n * 多线程执行器模板代码，otter中好多地方都写多线程，比较多的都是重复的逻辑代码，抽象一下做个模板把\n * \n * @author jianghang 2013-2-26 下午10:46:43\n * @version 4.1.7\n */\npublic class ExecutorTemplate implements InitializingBean, DisposableBean {\n\n    private static final Logger                logger            = LoggerFactory.getLogger(ExecutorTemplate.class);\n    private String                             name              = \"ExecutorTemplate\";\n    private static final int                   DEFAULT_POOL_SIZE = 5;\n    private int                                poolSize          = DEFAULT_POOL_SIZE;\n    private ExecutorService                    executor;\n\n    private volatile ExecutorCompletionService completionService = null;\n    private volatile List<Future>              futures           = null;\n\n    public void start() {\n        completionService = new ExecutorCompletionService(executor);\n        futures = Collections.synchronizedList(new ArrayList<Future>());\n    }\n\n    public void submit(Callable<Exception> task) {\n        Future future = completionService.submit(task);\n        futures.add(future);\n        check(future);// 立即check下，fast-fail\n    }\n\n    public void submit(Runnable task) {\n        Future future = completionService.submit(task, null);\n        futures.add(future);\n        check(future);// 立即check下，fast-fail\n    }\n\n    private void check(Future future) {\n        if (future.isDone()) {\n            // 立即判断一次，因为使用了CallerRun可能当场跑出结果，针对有异常时快速响应，而不是等跑完所有的才抛异常\n            try {\n                future.get();\n            } catch (InterruptedException e) {\n                cancel();// 取消完之后立马退出\n                throw new RuntimeException(e);\n            } catch (Throwable e) {\n                cancel(); // 取消完之后立马退出\n                throw new RuntimeException(e);\n            }\n        }\n    }\n\n    public synchronized List<?> waitForResult() {\n        List result = new ArrayList();\n        RuntimeException exception = null;\n        // 开始处理结果\n        int index = 0;\n        while (index < futures.size()) { // 循环处理发出去的所有任务\n            try {\n                Future future = completionService.take();// 它也可能被打断\n                result.add(future.get());\n            } catch (InterruptedException e) {\n                exception = new RuntimeException(e);\n                break;// 如何一个future出现了异常，就退出\n            } catch (Throwable e) {\n                exception = new RuntimeException(e);\n                break;// 如何一个future出现了异常，就退出\n            }\n\n            index++;\n        }\n\n        if (index < futures.size()) {\n            // 小于代表有错误，需要对未完成的记录进行cancel操作，对已完成的结果进行收集，做重复录入过滤记录\n            cancel();\n            throw exception;\n        } else {\n            return result;\n        }\n    }\n\n    public void cancel() {\n        logger.info(\"canal Futures[{}]\", futures.size());\n        for (int i = 0; i < futures.size(); i++) {\n            Future future = futures.get(i);\n            if (!future.isDone() && !future.isCancelled()) {\n                future.cancel(true);\n            }\n        }\n    }\n\n    // 调整一下线程池\n    public void adjustPoolSize(int newPoolSize) {\n        if (newPoolSize != poolSize) {\n            poolSize = newPoolSize;\n            if (executor instanceof ThreadPoolExecutor) {\n                ThreadPoolExecutor pool = (ThreadPoolExecutor) executor;\n                pool.setCorePoolSize(newPoolSize);\n                pool.setMaximumPoolSize(newPoolSize);\n            }\n        }\n    }\n\n    public void afterPropertiesSet() throws Exception {\n        executor = new ThreadPoolExecutor(poolSize, poolSize, 0L, TimeUnit.MILLISECONDS,\n                                          new ArrayBlockingQueue(poolSize * 4), new NamedThreadFactory(name),\n                                          new ThreadPoolExecutor.CallerRunsPolicy());\n    }\n\n    public void destroy() throws Exception {\n        if (futures != null) {\n            futures.clear();\n        }\n\n        executor.shutdownNow(); // 立即关闭\n    }\n\n    public void setPoolSize(int poolSize) {\n        this.poolSize = poolSize;\n    }\n\n    public void setName(String name) {\n        this.name = name;\n    }\n\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/utils/thread/ExecutorTemplateGetter.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.utils.thread;\n\nimport org.springframework.aop.TargetSource;\nimport org.springframework.beans.BeansException;\nimport org.springframework.beans.factory.BeanFactory;\nimport org.springframework.beans.factory.BeanFactoryAware;\n\n/**\n * 获取池化的{@linkplain ExecutorTemplate}\n * \n * @author jianghang 2013-3-1 下午08:05:43\n * @version 4.1.7\n */\npublic class ExecutorTemplateGetter implements BeanFactoryAware {\n\n    private BeanFactory beanFactory;\n\n    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {\n        this.beanFactory = beanFactory;\n    }\n\n    public ExecutorTemplate get() {\n        TargetSource targetSource = (TargetSource) this.beanFactory.getBean(\"executorTemplateTargetSource\");\n        try {\n            return (ExecutorTemplate) targetSource.getTarget();\n        } catch (Exception e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    public void release(ExecutorTemplate target) {\n        TargetSource targetSource = (TargetSource) this.beanFactory.getBean(\"executorTemplateTargetSource\");\n        try {\n            targetSource.releaseTarget(target);\n        } catch (Exception e) {\n            throw new RuntimeException(e);\n        }\n    }\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/utils/thread/NamedThreadFactory.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.utils.thread;\n\nimport java.lang.Thread.UncaughtExceptionHandler;\nimport java.util.concurrent.ThreadFactory;\nimport java.util.concurrent.atomic.AtomicInteger;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * 多线程TreahdFactory工厂，允许指定线程名字\n * \n * @author jianghang 2011-9-19 下午09:00:37\n * @version 4.0.0\n */\npublic class NamedThreadFactory implements ThreadFactory {\n\n    private static final Logger           logger                   = LoggerFactory.getLogger(NamedThreadFactory.class);\n    final private static String           DEFAULT_NAME             = \"otter-pool\";\n    final private String                  name;\n    final private boolean                 daemon;\n    final private ThreadGroup             group;\n    final private AtomicInteger           threadNumber             = new AtomicInteger(0);\n    final static UncaughtExceptionHandler uncaughtExceptionHandler = new UncaughtExceptionHandler() {\n\n                                                                       public void uncaughtException(Thread t,\n                                                                                                     Throwable e) {\n                                                                           logger.error(\"from \" + t.getName(), e);\n                                                                       }\n                                                                   };\n\n    public NamedThreadFactory(){\n        this(DEFAULT_NAME, true);\n    }\n\n    public NamedThreadFactory(String name){\n        this(name, true);\n    }\n\n    public NamedThreadFactory(String name, boolean daemon){\n        this.name = name;\n        this.daemon = daemon;\n        SecurityManager s = System.getSecurityManager();\n        group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup();\n    }\n\n    public Thread newThread(Runnable r) {\n        Thread t = new Thread(group, r, name + \"-\" + threadNumber.getAndIncrement(), 0);\n        t.setDaemon(daemon);\n        if (t.getPriority() != Thread.NORM_PRIORITY) {\n            t.setPriority(Thread.NORM_PRIORITY);\n        }\n\n        t.setUncaughtExceptionHandler(uncaughtExceptionHandler);\n        return t;\n    }\n\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/utils/version/OtterVersionAnnotation.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.utils.version;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n/**\n * A package attribute that captures the version of Otter that was compiled.\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Target(ElementType.PACKAGE)\npublic @interface OtterVersionAnnotation {\n\n    /**\n     * Get the Otter version\n     * \n     * @return the version string \"3.0.5-r\"\n     */\n    String version();\n\n    /**\n     * Get the username that compiled Otter.\n     */\n    String user();\n\n    /**\n     * Get the date when Otter was compiled.\n     * \n     * @return the date in unix 'date' format\n     */\n    String date();\n\n    /**\n     * Get the url for the subversion repository.\n     */\n    String url();\n\n    /**\n     * Get the subversion revision.\n     * \n     * @return the revision number as a string (eg. \"168168\")\n     */\n    String revision();\n\n    /**\n     * Get the branch from which this was compiled.\n     * \n     * @return The branch name, e.g. \"trunk\" or \"branches/branch-3.0.5\"\n     */\n    String branch();\n\n    /**\n     * Get a checksum of the source files from which Otter was compiled.\n     * \n     * @return a string that uniquely identifies the source\n     */\n    String srcChecksum();\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/utils/version/VersionInfo.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.utils.version;\n\nimport org.apache.commons.lang.SystemUtils;\n\n/**\n * This class finds the package info for Otter and the OtterVersionAnnotation information.\n */\npublic class VersionInfo {\n\n    private static Package                myPackage;\n    private static OtterVersionAnnotation version;\n\n    static {\n        myPackage = OtterVersionAnnotation.class.getPackage();\n        version = myPackage.getAnnotation(OtterVersionAnnotation.class);\n    }\n\n    /**\n     * Get the meta-data for the Otter package.\n     * \n     * @return\n     */\n    static Package getPackage() {\n        return myPackage;\n    }\n\n    /**\n     * Get the Otter version.\n     * \n     * @return the Otter version string, eg. \"3.0.5-r\"\n     */\n    public static String getVersion() {\n        return (version != null) ? version.version() : \"Unknown\";\n    }\n\n    /**\n     * Get the subversion revision number for the root directory\n     * \n     * @return the revision number, eg. \"168168\"\n     */\n    public static String getRevision() {\n        return (version != null) ? version.revision() : \"Unknown\";\n    }\n\n    /**\n     * Get the branch on which this originated.\n     * \n     * @return The branch name, e.g. \"trunk\" or \"branches/branch-3.0.5\"\n     */\n    public static String getBranch() {\n        return (version != null) ? version.branch() : \"Unknown\";\n    }\n\n    /**\n     * The date that Otter was compiled.\n     * \n     * @return the compilation date in unix date format\n     */\n    public static String getDate() {\n        return (version != null) ? version.date() : \"Unknown\";\n    }\n\n    /**\n     * The user that compiled Otter.\n     * \n     * @return the username of the user\n     */\n    public static String getUser() {\n        return (version != null) ? version.user() : \"Unknown\";\n    }\n\n    /**\n     * Get the subversion URL for the root Otter directory.\n     */\n    public static String getUrl() {\n        return (version != null) ? version.url() : \"Unknown\";\n    }\n\n    /**\n     * Get the checksum of the source files from which Otter was built.\n     */\n    public static String getSrcChecksum() {\n        return (version != null) ? version.srcChecksum() : \"Unknown\";\n    }\n\n    /**\n     * Returns the buildVersion which includes version, revision, user and date.\n     */\n    public static String getBuildVersion() {\n        StringBuilder buf = new StringBuilder();\n\n        buf.append(SystemUtils.LINE_SEPARATOR);\n        buf.append(\"[OTTER Version Info]\").append(SystemUtils.LINE_SEPARATOR);\n        buf.append(\"[version ]\").append(VersionInfo.getVersion()).append(SystemUtils.LINE_SEPARATOR);\n        buf.append(\"[revision]\").append(VersionInfo.getRevision()).append(SystemUtils.LINE_SEPARATOR);\n        buf.append(\"[compiler]\").append(VersionInfo.getUser()).append(SystemUtils.LINE_SEPARATOR);\n        buf.append(\"[date    ]\").append(VersionInfo.getDate()).append(SystemUtils.LINE_SEPARATOR);\n        buf.append(\"[checksum]\").append(VersionInfo.getSrcChecksum()).append(SystemUtils.LINE_SEPARATOR);\n        buf.append(\"[branch  ]\").append(VersionInfo.getBranch()).append(SystemUtils.LINE_SEPARATOR);\n        buf.append(\"[url     ]\").append(VersionInfo.getUrl()).append(SystemUtils.LINE_SEPARATOR);\n\n        return buf.toString();\n    }\n\n    public static void main(String[] args) {\n        System.out.println(getBuildVersion());\n    }\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/utils/zookeeper/ByteSerializer.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.utils.zookeeper;\n\nimport java.io.UnsupportedEncodingException;\n\nimport org.I0Itec.zkclient.exception.ZkMarshallingError;\nimport org.I0Itec.zkclient.serialize.ZkSerializer;\n\n/**\n * 基于string的序列化方式\n * \n * @author jianghang 2012-7-11 下午02:57:09\n * @version 4.1.0\n */\npublic class ByteSerializer implements ZkSerializer {\n\n    public Object deserialize(final byte[] bytes) throws ZkMarshallingError {\n        return bytes;\n    }\n\n    public byte[] serialize(final Object data) throws ZkMarshallingError {\n        try {\n            if (data instanceof byte[]) {\n                return (byte[]) data;\n            } else {\n                return ((String) data).getBytes(\"utf-8\");\n            }\n        } catch (final UnsupportedEncodingException e) {\n            throw new ZkMarshallingError(e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/utils/zookeeper/StringSerializer.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.utils.zookeeper;\n\nimport java.io.UnsupportedEncodingException;\n\nimport org.I0Itec.zkclient.exception.ZkMarshallingError;\nimport org.I0Itec.zkclient.serialize.ZkSerializer;\n\n/**\n * 基于string的序列化方式\n * \n * @author jianghang 2012-7-11 下午02:57:09\n * @version 4.1.0\n */\npublic class StringSerializer implements ZkSerializer {\n\n    public Object deserialize(final byte[] bytes) throws ZkMarshallingError {\n        try {\n            return new String(bytes, \"utf-8\");\n        } catch (final UnsupportedEncodingException e) {\n            throw new ZkMarshallingError(e);\n        }\n    }\n\n    public byte[] serialize(final Object data) throws ZkMarshallingError {\n        try {\n            return ((String) data).getBytes(\"utf-8\");\n        } catch (final UnsupportedEncodingException e) {\n            throw new ZkMarshallingError(e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/utils/zookeeper/ZkClientx.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.utils.zookeeper;\n\nimport java.io.OutputStream;\nimport java.util.Date;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Map.Entry;\nimport java.util.Set;\nimport java.util.concurrent.Callable;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.CopyOnWriteArraySet;\nimport java.util.concurrent.TimeUnit;\n\nimport org.I0Itec.zkclient.DataUpdater;\nimport org.I0Itec.zkclient.ExceptionUtil;\nimport org.I0Itec.zkclient.IZkChildListener;\nimport org.I0Itec.zkclient.IZkConnection;\nimport org.I0Itec.zkclient.IZkDataListener;\nimport org.I0Itec.zkclient.IZkStateListener;\nimport org.I0Itec.zkclient.ZkLock;\nimport org.I0Itec.zkclient.exception.ZkBadVersionException;\nimport org.I0Itec.zkclient.exception.ZkException;\nimport org.I0Itec.zkclient.exception.ZkInterruptedException;\nimport org.I0Itec.zkclient.exception.ZkNoNodeException;\nimport org.I0Itec.zkclient.exception.ZkNodeExistsException;\nimport org.I0Itec.zkclient.exception.ZkTimeoutException;\nimport org.I0Itec.zkclient.serialize.ZkSerializer;\nimport org.apache.zookeeper.CreateMode;\nimport org.apache.zookeeper.KeeperException;\nimport org.apache.zookeeper.KeeperException.ConnectionLossException;\nimport org.apache.zookeeper.KeeperException.SessionExpiredException;\nimport org.apache.zookeeper.WatchedEvent;\nimport org.apache.zookeeper.Watcher;\nimport org.apache.zookeeper.Watcher.Event.EventType;\nimport org.apache.zookeeper.Watcher.Event.KeeperState;\nimport org.apache.zookeeper.ZooKeeper.States;\nimport org.apache.zookeeper.data.Stat;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.alibaba.otter.shared.common.utils.zookeeper.ZkEventThread.ZkEvent;\nimport com.google.common.base.Function;\nimport com.google.common.collect.OtterMigrateMap;\n\n/**\n * 使用自定义的ZooKeeperx for zk connection\n * \n * @author jianghang 2012-7-10 下午02:31:15\n * @version 4.1.0\n */\npublic class ZkClientx implements Watcher {\n\n    // 对于zkclient进行一次缓存，避免一个jvm内部使用多个zk connection\n    private static Map<String, ZkClientx> clients = OtterMigrateMap.makeComputingMap(new Function<String, ZkClientx>() {\n\n                                                      public ZkClientx apply(String servers) {\n                                                          return new ZkClientx(servers);\n                                                      }\n                                                  });\n\n    public static ZkClientx getZkClient(String servers) {\n        return clients.get(servers);\n    }\n\n    public ZkClientx(String serverstring){\n        this(serverstring, Integer.MAX_VALUE);\n    }\n\n    public ZkClientx(String zkServers, int connectionTimeout){\n        this(new ZooKeeperx(zkServers), connectionTimeout);\n    }\n\n    public ZkClientx(String zkServers, int sessionTimeout, int connectionTimeout){\n        this(new ZooKeeperx(zkServers, sessionTimeout), connectionTimeout);\n    }\n\n    public ZkClientx(String zkServers, int sessionTimeout, int connectionTimeout, ZkSerializer zkSerializer){\n        this(new ZooKeeperx(zkServers, sessionTimeout), connectionTimeout, zkSerializer);\n    }\n\n    public ZkClientx(IZkConnection connection, int connectionTimeout){\n        this(connection, connectionTimeout, new ByteSerializer());\n    }\n\n    public ZkClientx(IZkConnection zkConnection, int connectionTimeout, ZkSerializer zkSerializer){\n        _connection = zkConnection;\n        _zkSerializer = zkSerializer;\n        connect(connectionTimeout, this);\n    }\n\n    /**\n     * 获取链接\n     */\n    public IZkConnection getConnection() {\n        return _connection;\n    }\n\n    /**\n     * Create a persistent Sequential node.\n     * \n     * @param path\n     * @param createParents if true all parent dirs are created as well and no {@link ZkNodeExistsException} is thrown\n     * in case the path already exists\n     * @throws ZkInterruptedException if operation was interrupted, or a required reconnection got interrupted\n     * @throws IllegalArgumentException if called from anything except the ZooKeeper event thread\n     * @throws ZkException if any ZooKeeper exception occurred\n     * @throws RuntimeException if any other exception occurs\n     */\n    public String createPersistentSequential(String path, boolean createParents) throws ZkInterruptedException,\n                                                                                IllegalArgumentException, ZkException,\n                                                                                RuntimeException {\n        try {\n            return create(path, null, CreateMode.PERSISTENT_SEQUENTIAL);\n        } catch (ZkNoNodeException e) {\n            if (!createParents) {\n                throw e;\n            }\n            String parentDir = path.substring(0, path.lastIndexOf('/'));\n            createPersistent(parentDir, createParents);\n            return createPersistentSequential(path, createParents);\n        }\n    }\n\n    /**\n     * Create a persistent Sequential node.\n     * \n     * @param path\n     * @param data\n     * @param createParents if true all parent dirs are created as well and no {@link ZkNodeExistsException} is thrown\n     * in case the path already exists\n     * @throws ZkInterruptedException if operation was interrupted, or a required reconnection got interrupted\n     * @throws IllegalArgumentException if called from anything except the ZooKeeper event thread\n     * @throws ZkException if any ZooKeeper exception occurred\n     * @throws RuntimeException if any other exception occurs\n     */\n    public String createPersistentSequential(String path, Object data, boolean createParents)\n                                                                                             throws ZkInterruptedException,\n                                                                                             IllegalArgumentException,\n                                                                                             ZkException,\n                                                                                             RuntimeException {\n        try {\n            return create(path, data, CreateMode.PERSISTENT_SEQUENTIAL);\n        } catch (ZkNoNodeException e) {\n            if (!createParents) {\n                throw e;\n            }\n            String parentDir = path.substring(0, path.lastIndexOf('/'));\n            createPersistent(parentDir, createParents);\n            return createPersistentSequential(path, data, createParents);\n        }\n    }\n\n    /**\n     * Create a persistent Sequential node.\n     * \n     * @param path\n     * @param data\n     * @param createParents if true all parent dirs are created as well and no {@link ZkNodeExistsException} is thrown\n     * in case the path already exists\n     * @throws ZkInterruptedException if operation was interrupted, or a required reconnection got interrupted\n     * @throws IllegalArgumentException if called from anything except the ZooKeeper event thread\n     * @throws ZkException if any ZooKeeper exception occurred\n     * @throws RuntimeException if any other exception occurs\n     */\n    public void createPersistent(String path, Object data, boolean createParents) throws ZkInterruptedException,\n                                                                                 IllegalArgumentException, ZkException,\n                                                                                 RuntimeException {\n        try {\n            create(path, data, CreateMode.PERSISTENT);\n        } catch (ZkNodeExistsException e) {\n            if (!createParents) {\n                throw e;\n            }\n        } catch (ZkNoNodeException e) {\n            if (!createParents) {\n                throw e;\n            }\n            String parentDir = path.substring(0, path.lastIndexOf('/'));\n            createPersistent(parentDir, createParents);\n            createPersistent(path, data, createParents);\n        }\n    }\n\n    /**\n     * copy from zkclient包中的代码，主要为解决ZkEventThread并发执行\n     */\n    private static final Logger                                   LOG            = LoggerFactory.getLogger(ZkClientx.class);\n\n    protected IZkConnection                                       _connection;\n    private final Map<String, Set<IZkChildListener>>              _childListener = new ConcurrentHashMap<String, Set<IZkChildListener>>();\n    private final ConcurrentHashMap<String, Set<IZkDataListener>> _dataListener  = new ConcurrentHashMap<String, Set<IZkDataListener>>();\n    private final Set<IZkStateListener>                           _stateListener = new CopyOnWriteArraySet<IZkStateListener>();\n    private KeeperState                                           _currentState;\n    private final ZkLock                                          _zkEventLock   = new ZkLock();\n    private boolean                                               _shutdownTriggered;\n    private ZkEventThread                                         _eventThread;\n    // TODO PVo remove this later\n    private Thread                                                _zookeeperEventThread;\n    private ZkSerializer                                          _zkSerializer;\n\n    public void setZkSerializer(ZkSerializer zkSerializer) {\n        _zkSerializer = zkSerializer;\n    }\n\n    public List<String> subscribeChildChanges(String path, IZkChildListener listener) {\n        synchronized (_childListener) {\n            Set<IZkChildListener> listeners = _childListener.get(path);\n            if (listeners == null) {\n                listeners = new CopyOnWriteArraySet<IZkChildListener>();\n                _childListener.put(path, listeners);\n            }\n            listeners.add(listener);\n        }\n        return watchForChilds(path);\n    }\n\n    public void unsubscribeChildChanges(String path, IZkChildListener childListener) {\n        synchronized (_childListener) {\n            final Set<IZkChildListener> listeners = _childListener.get(path);\n            if (listeners != null) {\n                listeners.remove(childListener);\n            }\n        }\n    }\n\n    public void subscribeDataChanges(String path, IZkDataListener listener) {\n        Set<IZkDataListener> listeners;\n        synchronized (_dataListener) {\n            listeners = _dataListener.get(path);\n            if (listeners == null) {\n                listeners = new CopyOnWriteArraySet<IZkDataListener>();\n                _dataListener.put(path, listeners);\n            }\n            listeners.add(listener);\n        }\n        watchForData(path);\n        LOG.debug(\"Subscribed data changes for \" + path);\n    }\n\n    public void unsubscribeDataChanges(String path, IZkDataListener dataListener) {\n        synchronized (_dataListener) {\n            final Set<IZkDataListener> listeners = _dataListener.get(path);\n            if (listeners != null) {\n                listeners.remove(dataListener);\n            }\n            if (listeners == null || listeners.isEmpty()) {\n                _dataListener.remove(path);\n            }\n        }\n    }\n\n    public void subscribeStateChanges(final IZkStateListener listener) {\n        synchronized (_stateListener) {\n            _stateListener.add(listener);\n        }\n    }\n\n    public void unsubscribeStateChanges(IZkStateListener stateListener) {\n        synchronized (_stateListener) {\n            _stateListener.remove(stateListener);\n        }\n    }\n\n    public void unsubscribeAll() {\n        synchronized (_childListener) {\n            _childListener.clear();\n        }\n        synchronized (_dataListener) {\n            _dataListener.clear();\n        }\n        synchronized (_stateListener) {\n            _stateListener.clear();\n        }\n    }\n\n    // </listeners>\n\n    /**\n     * Create a persistent node.\n     * \n     * @param path\n     * @throws ZkInterruptedException if operation was interrupted, or a required reconnection got interrupted\n     * @throws IllegalArgumentException if called from anything except the ZooKeeper event thread\n     * @throws ZkException if any ZooKeeper exception occurred\n     * @throws RuntimeException if any other exception occurs\n     */\n    public void createPersistent(String path) throws ZkInterruptedException, IllegalArgumentException, ZkException,\n                                             RuntimeException {\n        createPersistent(path, false);\n    }\n\n    /**\n     * Create a persistent node.\n     * \n     * @param path\n     * @param createParents if true all parent dirs are created as well and no {@link ZkNodeExistsException} is thrown\n     * in case the path already exists\n     * @throws ZkInterruptedException if operation was interrupted, or a required reconnection got interrupted\n     * @throws IllegalArgumentException if called from anything except the ZooKeeper event thread\n     * @throws ZkException if any ZooKeeper exception occurred\n     * @throws RuntimeException if any other exception occurs\n     */\n    public void createPersistent(String path, boolean createParents) throws ZkInterruptedException,\n                                                                    IllegalArgumentException, ZkException,\n                                                                    RuntimeException {\n        try {\n            create(path, null, CreateMode.PERSISTENT);\n        } catch (ZkNodeExistsException e) {\n            if (!createParents) {\n                throw e;\n            }\n        } catch (ZkNoNodeException e) {\n            if (!createParents) {\n                throw e;\n            }\n            String parentDir = path.substring(0, path.lastIndexOf('/'));\n            createPersistent(parentDir, createParents);\n            createPersistent(path, createParents);\n        }\n    }\n\n    /**\n     * Create a persistent node.\n     * \n     * @param path\n     * @param data\n     * @throws ZkInterruptedException if operation was interrupted, or a required reconnection got interrupted\n     * @throws IllegalArgumentException if called from anything except the ZooKeeper event thread\n     * @throws ZkException if any ZooKeeper exception occurred\n     * @throws RuntimeException if any other exception occurs\n     */\n    public void createPersistent(String path, Object data) throws ZkInterruptedException, IllegalArgumentException,\n                                                          ZkException, RuntimeException {\n        create(path, data, CreateMode.PERSISTENT);\n    }\n\n    /**\n     * Create a persistent, sequental node.\n     * \n     * @param path\n     * @param data\n     * @return create node's path\n     * @throws ZkInterruptedException if operation was interrupted, or a required reconnection got interrupted\n     * @throws IllegalArgumentException if called from anything except the ZooKeeper event thread\n     * @throws ZkException if any ZooKeeper exception occurred\n     * @throws RuntimeException if any other exception occurs\n     */\n    public String createPersistentSequential(String path, Object data) throws ZkInterruptedException,\n                                                                      IllegalArgumentException, ZkException,\n                                                                      RuntimeException {\n        return create(path, data, CreateMode.PERSISTENT_SEQUENTIAL);\n    }\n\n    /**\n     * Create an ephemeral node.\n     * \n     * @param path\n     * @throws ZkInterruptedException if operation was interrupted, or a required reconnection got interrupted\n     * @throws IllegalArgumentException if called from anything except the ZooKeeper event thread\n     * @throws ZkException if any ZooKeeper exception occurred\n     * @throws RuntimeException if any other exception occurs\n     */\n    public void createEphemeral(final String path) throws ZkInterruptedException, IllegalArgumentException,\n                                                  ZkException, RuntimeException {\n        create(path, null, CreateMode.EPHEMERAL);\n    }\n\n    /**\n     * Create a node.\n     * \n     * @param path\n     * @param data\n     * @param mode\n     * @return create node's path\n     * @throws ZkInterruptedException if operation was interrupted, or a required reconnection got interrupted\n     * @throws IllegalArgumentException if called from anything except the ZooKeeper event thread\n     * @throws ZkException if any ZooKeeper exception occurred\n     * @throws RuntimeException if any other exception occurs\n     */\n    public String create(final String path, Object data, final CreateMode mode) throws ZkInterruptedException,\n                                                                               IllegalArgumentException, ZkException,\n                                                                               RuntimeException {\n        if (path == null) {\n            throw new NullPointerException(\"path must not be null.\");\n        }\n        final byte[] bytes = data == null ? null : serialize(data);\n\n        return retryUntilConnected(new Callable<String>() {\n\n            @Override\n            public String call() throws Exception {\n                return _connection.create(path, bytes, mode);\n            }\n        });\n    }\n\n    /**\n     * Create an ephemeral node.\n     * \n     * @param path\n     * @param data\n     * @throws ZkInterruptedException if operation was interrupted, or a required reconnection got interrupted\n     * @throws IllegalArgumentException if called from anything except the ZooKeeper event thread\n     * @throws ZkException if any ZooKeeper exception occurred\n     * @throws RuntimeException if any other exception occurs\n     */\n    public void createEphemeral(final String path, final Object data) throws ZkInterruptedException,\n                                                                     IllegalArgumentException, ZkException,\n                                                                     RuntimeException {\n        create(path, data, CreateMode.EPHEMERAL);\n    }\n\n    /**\n     * Create an ephemeral, sequential node.\n     * \n     * @param path\n     * @param data\n     * @return created path\n     * @throws ZkInterruptedException if operation was interrupted, or a required reconnection got interrupted\n     * @throws IllegalArgumentException if called from anything except the ZooKeeper event thread\n     * @throws ZkException if any ZooKeeper exception occurred\n     * @throws RuntimeException if any other exception occurs\n     */\n    public String createEphemeralSequential(final String path, final Object data) throws ZkInterruptedException,\n                                                                                 IllegalArgumentException, ZkException,\n                                                                                 RuntimeException {\n        return create(path, data, CreateMode.EPHEMERAL_SEQUENTIAL);\n    }\n\n    public void process(WatchedEvent event) {\n        LOG.debug(\"Received event: \" + event);\n        _zookeeperEventThread = Thread.currentThread();\n\n        boolean stateChanged = event.getPath() == null;\n        boolean znodeChanged = event.getPath() != null;\n        boolean dataChanged = event.getType() == EventType.NodeDataChanged || event.getType() == EventType.NodeDeleted\n                              || event.getType() == EventType.NodeCreated\n                              || event.getType() == EventType.NodeChildrenChanged;\n\n        getEventLock().lock();\n        try {\n\n            // We might have to install child change event listener if a new node was created\n            if (getShutdownTrigger()) {\n                LOG.debug(\"ignoring event '{\" + event.getType() + \" | \" + event.getPath()\n                          + \"}' since shutdown triggered\");\n                return;\n            }\n            if (stateChanged) {\n                processStateChanged(event);\n            }\n            if (dataChanged) {\n                processDataOrChildChange(event);\n            }\n        } finally {\n            if (stateChanged) {\n                getEventLock().getStateChangedCondition().signalAll();\n\n                // If the session expired we have to signal all conditions, because watches might have been removed and\n                // there is no guarantee that those\n                // conditions will be signaled at all after an Expired event\n                // TODO PVo write a test for this\n                if (event.getState() == KeeperState.Expired) {\n                    getEventLock().getZNodeEventCondition().signalAll();\n                    getEventLock().getDataChangedCondition().signalAll();\n                    // We also have to notify all listeners that something might have changed\n                    fireAllEvents();\n                }\n            }\n            if (znodeChanged) {\n                getEventLock().getZNodeEventCondition().signalAll();\n            }\n            if (dataChanged) {\n                getEventLock().getDataChangedCondition().signalAll();\n            }\n            getEventLock().unlock();\n            LOG.debug(\"Leaving process event\");\n        }\n    }\n\n    private void fireAllEvents() {\n        for (Entry<String, Set<IZkChildListener>> entry : _childListener.entrySet()) {\n            fireChildChangedEvents(entry.getKey(), entry.getValue());\n        }\n        for (Entry<String, Set<IZkDataListener>> entry : _dataListener.entrySet()) {\n            fireDataChangedEvents(entry.getKey(), entry.getValue());\n        }\n    }\n\n    public List<String> getChildren(String path) {\n        return getChildren(path, hasListeners(path));\n    }\n\n    protected List<String> getChildren(final String path, final boolean watch) {\n        return retryUntilConnected(new Callable<List<String>>() {\n\n            @Override\n            public List<String> call() throws Exception {\n                return _connection.getChildren(path, watch);\n            }\n        });\n    }\n\n    /**\n     * Counts number of children for the given path.\n     * \n     * @param path\n     * @return number of children or 0 if path does not exist.\n     */\n    public int countChildren(String path) {\n        try {\n            return getChildren(path).size();\n        } catch (ZkNoNodeException e) {\n            return 0;\n        }\n    }\n\n    protected boolean exists(final String path, final boolean watch) {\n        return retryUntilConnected(new Callable<Boolean>() {\n\n            @Override\n            public Boolean call() throws Exception {\n                return _connection.exists(path, watch);\n            }\n        });\n    }\n\n    public boolean exists(final String path) {\n        return exists(path, hasListeners(path));\n    }\n\n    private void processStateChanged(WatchedEvent event) {\n        LOG.info(\"zookeeper state changed (\" + event.getState() + \")\");\n        setCurrentState(event.getState());\n        if (getShutdownTrigger()) {\n            return;\n        }\n        try {\n            fireStateChangedEvent(event.getState());\n\n            if (event.getState() == KeeperState.Expired) {\n                reconnect();\n                fireNewSessionEvents();\n            }\n        } catch (final Exception e) {\n            throw new RuntimeException(\"Exception while restarting zk client\", e);\n        }\n    }\n\n    private void fireNewSessionEvents() {\n        for (final IZkStateListener stateListener : _stateListener) {\n            _eventThread.send(new ZkEvent(\"New session event sent to \" + stateListener) {\n\n                @Override\n                public void run() throws Exception {\n                    stateListener.handleNewSession();\n                }\n            });\n        }\n    }\n\n    private void fireStateChangedEvent(final KeeperState state) {\n        for (final IZkStateListener stateListener : _stateListener) {\n            _eventThread.send(new ZkEvent(\"State changed to \" + state + \" sent to \" + stateListener) {\n\n                @Override\n                public void run() throws Exception {\n                    stateListener.handleStateChanged(state);\n                }\n            });\n        }\n    }\n\n    private boolean hasListeners(String path) {\n        Set<IZkDataListener> dataListeners = _dataListener.get(path);\n        if (dataListeners != null && dataListeners.size() > 0) {\n            return true;\n        }\n        Set<IZkChildListener> childListeners = _childListener.get(path);\n        if (childListeners != null && childListeners.size() > 0) {\n            return true;\n        }\n        return false;\n    }\n\n    public boolean deleteRecursive(String path) {\n        List<String> children;\n        try {\n            children = getChildren(path, false);\n        } catch (ZkNoNodeException e) {\n            return true;\n        }\n\n        for (String subPath : children) {\n            if (!deleteRecursive(path + \"/\" + subPath)) {\n                return false;\n            }\n        }\n\n        return delete(path);\n    }\n\n    private void processDataOrChildChange(WatchedEvent event) {\n        final String path = event.getPath();\n\n        if (event.getType() == EventType.NodeChildrenChanged || event.getType() == EventType.NodeCreated\n            || event.getType() == EventType.NodeDeleted) {\n            Set<IZkChildListener> childListeners = _childListener.get(path);\n            if (childListeners != null && !childListeners.isEmpty()) {\n                fireChildChangedEvents(path, childListeners);\n            }\n        }\n\n        if (event.getType() == EventType.NodeDataChanged || event.getType() == EventType.NodeDeleted\n            || event.getType() == EventType.NodeCreated) {\n            Set<IZkDataListener> listeners = _dataListener.get(path);\n            if (listeners != null && !listeners.isEmpty()) {\n                fireDataChangedEvents(event.getPath(), listeners);\n            }\n        }\n    }\n\n    private void fireDataChangedEvents(final String path, Set<IZkDataListener> listeners) {\n        for (final IZkDataListener listener : listeners) {\n            _eventThread.send(new ZkEvent(\"Data of \" + path + \" changed sent to \" + listener) {\n\n                @Override\n                public void run() throws Exception {\n                    // reinstall watch\n                    exists(path, true);\n                    try {\n                        Object data = readData(path, null, true);\n                        listener.handleDataChange(path, data);\n                    } catch (ZkNoNodeException e) {\n                        listener.handleDataDeleted(path);\n                    }\n                }\n            });\n        }\n    }\n\n    private void fireChildChangedEvents(final String path, Set<IZkChildListener> childListeners) {\n        try {\n            // reinstall the watch\n            for (final IZkChildListener listener : childListeners) {\n                _eventThread.send(new ZkEvent(\"Children of \" + path + \" changed sent to \" + listener) {\n\n                    @Override\n                    public void run() throws Exception {\n                        try {\n                            // if the node doesn't exist we should listen for the root node to reappear\n                            exists(path);\n                            List<String> children = getChildren(path);\n                            listener.handleChildChange(path, children);\n                        } catch (ZkNoNodeException e) {\n                            listener.handleChildChange(path, null);\n                        }\n                    }\n                });\n            }\n        } catch (Exception e) {\n            LOG.error(\"Failed to fire child changed event. Unable to getChildren.  \", e);\n        }\n    }\n\n    public boolean waitUntilExists(String path, TimeUnit timeUnit, long time) throws ZkInterruptedException {\n        Date timeout = new Date(System.currentTimeMillis() + timeUnit.toMillis(time));\n        LOG.debug(\"Waiting until znode '\" + path + \"' becomes available.\");\n        if (exists(path)) {\n            return true;\n        }\n        acquireEventLock();\n        try {\n            while (!exists(path, true)) {\n                boolean gotSignal = getEventLock().getZNodeEventCondition().awaitUntil(timeout);\n                if (!gotSignal) {\n                    return false;\n                }\n            }\n            return true;\n        } catch (InterruptedException e) {\n            throw new ZkInterruptedException(e);\n        } finally {\n            getEventLock().unlock();\n        }\n    }\n\n    protected Set<IZkDataListener> getDataListener(String path) {\n        return _dataListener.get(path);\n    }\n\n    public void showFolders(OutputStream output) {\n        throw new UnsupportedOperationException();\n        // try {\n        // output.write(ZkPathUtil.toString(this).getBytes());\n        // } catch (final IOException e) {\n        // e.printStackTrace();\n        // }\n    }\n\n    public void waitUntilConnected() throws ZkInterruptedException {\n        waitUntilConnected(Integer.MAX_VALUE, TimeUnit.MILLISECONDS);\n    }\n\n    public boolean waitUntilConnected(long time, TimeUnit timeUnit) throws ZkInterruptedException {\n        return waitForKeeperState(KeeperState.SyncConnected, time, timeUnit);\n    }\n\n    public boolean waitForKeeperState(KeeperState keeperState, long time, TimeUnit timeUnit)\n                                                                                            throws ZkInterruptedException {\n        if (_zookeeperEventThread != null && Thread.currentThread() == _zookeeperEventThread) {\n            throw new IllegalArgumentException(\"Must not be done in the zookeeper event thread.\");\n        }\n        Date timeout = new Date(System.currentTimeMillis() + timeUnit.toMillis(time));\n\n        LOG.debug(\"Waiting for keeper state \" + keeperState);\n        acquireEventLock();\n        try {\n            boolean stillWaiting = true;\n            while (_currentState != keeperState) {\n                if (!stillWaiting) {\n                    return false;\n                }\n                stillWaiting = getEventLock().getStateChangedCondition().awaitUntil(timeout);\n            }\n            LOG.debug(\"State is \" + _currentState);\n            return true;\n        } catch (InterruptedException e) {\n            throw new ZkInterruptedException(e);\n        } finally {\n            getEventLock().unlock();\n        }\n    }\n\n    private void acquireEventLock() {\n        try {\n            getEventLock().lockInterruptibly();\n        } catch (InterruptedException e) {\n            throw new ZkInterruptedException(e);\n        }\n    }\n\n    /**\n     * @param <T>\n     * @param callable\n     * @return result of Callable\n     * @throws ZkInterruptedException if operation was interrupted, or a required reconnection got interrupted\n     * @throws IllegalArgumentException if called from anything except the ZooKeeper event thread\n     * @throws ZkException if any ZooKeeper exception occurred\n     * @throws RuntimeException if any other exception occurs from invoking the Callable\n     */\n    public <T> T retryUntilConnected(Callable<T> callable) throws ZkInterruptedException, IllegalArgumentException,\n                                                          ZkException, RuntimeException {\n        if (_zookeeperEventThread != null && Thread.currentThread() == _zookeeperEventThread) {\n            throw new IllegalArgumentException(\"Must not be done in the zookeeper event thread.\");\n        }\n        while (true) {\n            try {\n                return callable.call();\n            } catch (ConnectionLossException e) {\n                // we give the event thread some time to update the status to 'Disconnected'\n                Thread.yield();\n                waitUntilConnected();\n            } catch (SessionExpiredException e) {\n                // we give the event thread some time to update the status to 'Expired'\n                Thread.yield();\n                waitUntilConnected();\n            } catch (KeeperException e) {\n                throw ZkException.create(e);\n            } catch (InterruptedException e) {\n                throw new ZkInterruptedException(e);\n            } catch (Exception e) {\n                throw ExceptionUtil.convertToRuntimeException(e);\n            }\n        }\n    }\n\n    public void setCurrentState(KeeperState currentState) {\n        getEventLock().lock();\n        try {\n            _currentState = currentState;\n        } finally {\n            getEventLock().unlock();\n        }\n    }\n\n    /**\n     * Returns a mutex all zookeeper events are synchronized aginst. So in case you need to do something without getting\n     * any zookeeper event interruption synchronize against this mutex. Also all threads waiting on this mutex object\n     * will be notified on an event.\n     * \n     * @return the mutex.\n     */\n    public ZkLock getEventLock() {\n        return _zkEventLock;\n    }\n\n    public boolean delete(final String path) {\n        try {\n            retryUntilConnected(new Callable<Object>() {\n\n                @Override\n                public Object call() throws Exception {\n                    _connection.delete(path);\n                    return null;\n                }\n            });\n\n            return true;\n        } catch (ZkNoNodeException e) {\n            return false;\n        }\n    }\n\n    private byte[] serialize(Object data) {\n        return _zkSerializer.serialize(data);\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    private <T extends Object> T derializable(byte[] data) {\n        if (data == null) {\n            return null;\n        }\n        return (T) _zkSerializer.deserialize(data);\n    }\n\n    public <T extends Object> T readData(String path) {\n        return (T) readData(path, false);\n    }\n\n    public <T extends Object> T readData(String path, boolean returnNullIfPathNotExists) {\n        T data = null;\n        try {\n            data = (T) readData(path, null);\n        } catch (ZkNoNodeException e) {\n            if (!returnNullIfPathNotExists) {\n                throw e;\n            }\n        }\n        return data;\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public <T extends Object> T readData(String path, Stat stat) {\n        return (T) readData(path, stat, hasListeners(path));\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    protected <T extends Object> T readData(final String path, final Stat stat, final boolean watch) {\n        byte[] data = retryUntilConnected(new Callable<byte[]>() {\n\n            @Override\n            public byte[] call() throws Exception {\n                return _connection.readData(path, stat, watch);\n            }\n        });\n        return (T) derializable(data);\n    }\n\n    public void writeData(String path, Object object) {\n        writeData(path, object, -1);\n    }\n\n    /**\n     * Updates data of an existing znode. The current content of the znode is passed to the {@link DataUpdater} that is\n     * passed into this method, which returns the new content. The new content is only written back to ZooKeeper if\n     * nobody has modified the given znode in between. If a concurrent change has been detected the new data of the\n     * znode is passed to the updater once again until the new contents can be successfully written back to ZooKeeper.\n     * \n     * @param <T>\n     * @param path The path of the znode.\n     * @param updater Updater that creates the new contents.\n     */\n    public <T extends Object> void updateDataSerialized(String path, DataUpdater<T> updater) {\n        Stat stat = new Stat();\n        boolean retry;\n        do {\n            retry = false;\n            try {\n                T oldData = (T) readData(path, stat);\n                T newData = updater.update(oldData);\n                writeData(path, newData, stat.getVersion());\n            } catch (ZkBadVersionException e) {\n                retry = true;\n            }\n        } while (retry);\n    }\n\n    public void writeData(final String path, Object datat, final int expectedVersion) {\n        final byte[] data = serialize(datat);\n        retryUntilConnected(new Callable<Object>() {\n\n            @Override\n            public Object call() throws Exception {\n                _connection.writeData(path, data, expectedVersion);\n                return null;\n            }\n        });\n    }\n\n    public void watchForData(final String path) {\n        retryUntilConnected(new Callable<Object>() {\n\n            @Override\n            public Object call() throws Exception {\n                _connection.exists(path, true);\n                return null;\n            }\n        });\n    }\n\n    /**\n     * Installs a child watch for the given path.\n     * \n     * @param path\n     * @return the current children of the path or null if the zk node with the given path doesn't exist.\n     */\n    public List<String> watchForChilds(final String path) {\n        if (_zookeeperEventThread != null && Thread.currentThread() == _zookeeperEventThread) {\n            throw new IllegalArgumentException(\"Must not be done in the zookeeper event thread.\");\n        }\n        return retryUntilConnected(new Callable<List<String>>() {\n\n            @Override\n            public List<String> call() throws Exception {\n                exists(path, true);\n                try {\n                    return getChildren(path, true);\n                } catch (ZkNoNodeException e) {\n                    // ignore, the \"exists\" watch will listen for the parent node to appear\n                }\n                return null;\n            }\n        });\n    }\n\n    /**\n     * Connect to ZooKeeper.\n     * \n     * @param maxMsToWaitUntilConnected\n     * @param watcher\n     * @throws ZkInterruptedException if the connection timed out due to thread interruption\n     * @throws ZkTimeoutException if the connection timed out\n     * @throws IllegalStateException if the connection timed out due to thread interruption\n     */\n    public void connect(final long maxMsToWaitUntilConnected, Watcher watcher) throws ZkInterruptedException,\n                                                                              ZkTimeoutException, IllegalStateException {\n        boolean started = false;\n        try {\n            getEventLock().lockInterruptibly();\n            setShutdownTrigger(false);\n            _eventThread = new ZkEventThread(_connection.getServers());\n            _eventThread.start();\n            _connection.connect(watcher);\n\n            LOG.debug(\"Awaiting connection to Zookeeper server\");\n            if (!waitUntilConnected(maxMsToWaitUntilConnected, TimeUnit.MILLISECONDS)) {\n                throw new ZkTimeoutException(\"Unable to connect to zookeeper server within timeout: \"\n                                             + maxMsToWaitUntilConnected);\n            }\n            started = true;\n        } catch (InterruptedException e) {\n            States state = _connection.getZookeeperState();\n            throw new IllegalStateException(\"Not connected with zookeeper server yet. Current state is \" + state);\n        } finally {\n            getEventLock().unlock();\n\n            // we should close the zookeeper instance, otherwise it would keep\n            // on trying to connect\n            if (!started) {\n                close();\n            }\n        }\n    }\n\n    public long getCreationTime(String path) {\n        try {\n            getEventLock().lockInterruptibly();\n            return _connection.getCreateTime(path);\n        } catch (KeeperException e) {\n            throw ZkException.create(e);\n        } catch (InterruptedException e) {\n            throw new ZkInterruptedException(e);\n        } finally {\n            getEventLock().unlock();\n        }\n    }\n\n    /**\n     * Close the client.\n     * \n     * @throws ZkInterruptedException\n     */\n    public void close() throws ZkInterruptedException {\n        if (_connection == null) {\n            return;\n        }\n        LOG.debug(\"Closing ZkClient...\");\n        getEventLock().lock();\n        try {\n            setShutdownTrigger(true);\n            _eventThread.interrupt();\n            _eventThread.join(2000);\n            _connection.close();\n            _connection = null;\n        } catch (InterruptedException e) {\n            throw new ZkInterruptedException(e);\n        } finally {\n            getEventLock().unlock();\n        }\n        LOG.debug(\"Closing ZkClient...done\");\n    }\n\n    private void reconnect() {\n        getEventLock().lock();\n        try {\n            _connection.close();\n            _connection.connect(this);\n        } catch (InterruptedException e) {\n            throw new ZkInterruptedException(e);\n        } finally {\n            getEventLock().unlock();\n        }\n    }\n\n    public void setShutdownTrigger(boolean triggerState) {\n        _shutdownTriggered = triggerState;\n    }\n\n    public boolean getShutdownTrigger() {\n        return _shutdownTriggered;\n    }\n\n    public int numberOfListeners() {\n        int listeners = 0;\n        for (Set<IZkChildListener> childListeners : _childListener.values()) {\n            listeners += childListeners.size();\n        }\n        for (Set<IZkDataListener> dataListeners : _dataListener.values()) {\n            listeners += dataListeners.size();\n        }\n        listeners += _stateListener.size();\n\n        return listeners;\n    }\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/utils/zookeeper/ZkEventThread.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.utils.zookeeper;\n\nimport java.util.concurrent.ArrayBlockingQueue;\nimport java.util.concurrent.BlockingQueue;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.LinkedBlockingQueue;\nimport java.util.concurrent.ThreadPoolExecutor;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.atomic.AtomicInteger;\n\nimport org.I0Itec.zkclient.exception.ZkInterruptedException;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.alibaba.otter.shared.common.utils.thread.NamedThreadFactory;\n\n/**\n * copy from zkclient中的{@linkplain ZkEventThread}，解决串行执行问题\n * \n * @author jianghang 2012-9-27 下午05:48:41\n * @version 4.1.0\n */\npublic class ZkEventThread extends Thread {\n\n    private static final Logger    LOG                  = LoggerFactory.getLogger(ZkEventThread.class);\n    private static AtomicInteger   _eventId             = new AtomicInteger(0);\n    private static final int       DEFAULT_POOL_SIZE    = 30;\n    private static final int       DEFAULT_ACCEPT_COUNT = 60;\n\n    private static ExecutorService executor             = new ThreadPoolExecutor(\n                                                                                 DEFAULT_POOL_SIZE,\n                                                                                 DEFAULT_POOL_SIZE,\n                                                                                 0L,\n                                                                                 TimeUnit.MILLISECONDS,\n                                                                                 new ArrayBlockingQueue(\n                                                                                                        DEFAULT_ACCEPT_COUNT),\n                                                                                 new NamedThreadFactory(\n                                                                                                        \"Arbitrate-Async-Watcher\"),\n                                                                                 new ThreadPoolExecutor.CallerRunsPolicy());\n\n    private BlockingQueue<ZkEvent> _events              = new LinkedBlockingQueue<ZkEvent>();\n\n    public static abstract class ZkEvent {\n\n        private String _description;\n\n        public ZkEvent(String description){\n            _description = description;\n        }\n\n        public abstract void run() throws Exception;\n\n        @Override\n        public String toString() {\n            return \"ZkEvent[\" + _description + \"]\";\n        }\n    }\n\n    ZkEventThread(String name){\n        setDaemon(true);\n        setName(\"ZkClient-EventThread-\" + getId() + \"-\" + name);\n    }\n\n    public void run() {\n        LOG.info(\"Starting ZkClient event thread.\");\n        try {\n            while (!isInterrupted()) {\n                final ZkEvent zkEvent = _events.take();\n                int eventId = _eventId.incrementAndGet();\n                LOG.debug(\"Delivering event #\" + eventId + \" \" + zkEvent);\n                executor.submit(new Runnable() {\n\n                    public void run() {\n                        try {\n                            zkEvent.run();\n                        } catch (InterruptedException e) {\n                            interrupt();\n                        } catch (ZkInterruptedException e) {\n                            interrupt();\n                        } catch (Throwable e) {\n                            LOG.error(\"Error handling event \" + zkEvent, e);\n                        }\n                    }\n                });\n                LOG.debug(\"Delivering event #\" + eventId + \" done\");\n            }\n        } catch (InterruptedException e) {\n            LOG.info(\"Terminate ZkClient event thread.\");\n        }\n    }\n\n    public void send(ZkEvent event) {\n        if (!isInterrupted()) {\n            LOG.debug(\"New event: \" + event);\n            _events.add(event);\n        }\n    }\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/alibaba/otter/shared/common/utils/zookeeper/ZooKeeperx.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.utils.zookeeper;\n\nimport java.io.IOException;\nimport java.lang.reflect.Field;\nimport java.net.InetSocketAddress;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.concurrent.locks.Lock;\nimport java.util.concurrent.locks.ReentrantLock;\n\nimport org.I0Itec.zkclient.ZkConnection;\nimport org.I0Itec.zkclient.exception.ZkException;\nimport org.apache.commons.lang.StringUtils;\nimport org.apache.zookeeper.ClientCnxn;\nimport org.apache.zookeeper.Watcher;\nimport org.apache.zookeeper.ZooKeeper;\nimport org.apache.zookeeper.client.ConnectStringParser;\nimport org.apache.zookeeper.client.HostProvider;\nimport org.apache.zookeeper.client.StaticHostProvider;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.util.ReflectionUtils;\n\n\n/**\n * 封装了ZooKeeper，使其支持节点的优先顺序，比如美国机房的节点会优先加载美国对应的zk集群列表，都失败后才会选择加载杭州的zk集群列表 *\n * \n * @author jianghang 2012-7-10 下午02:31:42\n * @version 4.1.0\n */\npublic class ZooKeeperx extends ZkConnection {\n\n    private static final String SERVER_COMMA            = \";\";\n    private static final Logger logger                  = LoggerFactory.getLogger(ZooKeeperx.class);\n    private static final Field  clientCnxnField         = ReflectionUtils.findField(ZooKeeper.class, \"cnxn\");\n    private static final Field  hostProviderField       = ReflectionUtils.findField(ClientCnxn.class, \"hostProvider\");\n    private static final Field  serverAddressesField    = ReflectionUtils.findField(StaticHostProvider.class,\n                                                            \"serverAddresses\");\n    private static final Field  zookeeperLockField      = ReflectionUtils.findField(ZkConnection.class,\n                                                            \"_zookeeperLock\");\n    private static final Field  zookeeperFiled          = ReflectionUtils.findField(ZkConnection.class, \"_zk\");\n    private static final int    DEFAULT_SESSION_TIMEOUT = 90000;\n\n    private final List<String>  _serversList;\n    private final int           _sessionTimeOut;\n\n    public ZooKeeperx(String zkServers){\n        this(zkServers, DEFAULT_SESSION_TIMEOUT);\n    }\n\n    public ZooKeeperx(String zkServers, int sessionTimeOut){\n        super(zkServers, sessionTimeOut);\n        _serversList = Arrays.asList(StringUtils.split(this.getServers(), SERVER_COMMA));\n        _sessionTimeOut = sessionTimeOut;\n    }\n\n    @Override\n    public void connect(Watcher watcher) {\n        ReflectionUtils.makeAccessible(zookeeperLockField);\n        ReflectionUtils.makeAccessible(zookeeperFiled);\n        Lock _zookeeperLock = (ReentrantLock) ReflectionUtils.getField(zookeeperLockField, this);\n        ZooKeeper _zk = (ZooKeeper) ReflectionUtils.getField(zookeeperFiled, this);\n\n        _zookeeperLock.lock();\n        try {\n            if (_zk != null) {\n                throw new IllegalStateException(\"zk client has already been started\");\n            }\n            String zkServers = _serversList.get(0);\n\n            try {\n                logger.debug(\"Creating new ZookKeeper instance to connect to \" + zkServers + \".\");\n                _zk = new ZooKeeper(zkServers, _sessionTimeOut, watcher);\n                configMutliCluster(_zk);\n                ReflectionUtils.setField(zookeeperFiled, this, _zk);\n            } catch (IOException e) {\n                throw new ZkException(\"Unable to connect to \" + zkServers, e);\n            }\n        } finally {\n            _zookeeperLock.unlock();\n        }\n    }\n\n    // ===============================\n\n    public void configMutliCluster(ZooKeeper zk) {\n        if (_serversList.size() == 1) {\n            return;\n        }\n        String cluster1 = _serversList.get(0);\n        try {\n            if (_serversList.size() > 1) {\n                // 强制的声明accessible\n                ReflectionUtils.makeAccessible(clientCnxnField);\n                ReflectionUtils.makeAccessible(hostProviderField);\n                ReflectionUtils.makeAccessible(serverAddressesField);\n\n                // 添加第二组集群列表\n                for (int i = 1; i < _serversList.size(); i++) {\n                    String cluster = _serversList.get(i);\n                    // 强制获取zk中的地址信息\n                    ClientCnxn cnxn = (ClientCnxn) ReflectionUtils.getField(clientCnxnField, zk);\n                    HostProvider hostProvider = (HostProvider) ReflectionUtils.getField(hostProviderField, cnxn);\n                    List<InetSocketAddress> serverAddrs = (List<InetSocketAddress>) ReflectionUtils.getField(serverAddressesField,\n                        hostProvider);\n                    // 添加第二组集群列表\n                    serverAddrs.addAll(new ConnectStringParser(cluster).getServerAddresses());\n                }\n            }\n        } catch (Exception e) {\n            try {\n                if (zk != null) {\n                    zk.close();\n                }\n            } catch (InterruptedException ie) {\n                // ignore interrupt\n            }\n            throw new ZkException(\"zookeeper_create_error, serveraddrs=\" + cluster1, e);\n        }\n\n    }\n}\n"
  },
  {
    "path": "shared/common/src/main/java/com/google/common/collect/OtterMigrateMap.java",
    "content": "package com.google.common.collect;\n\nimport java.util.concurrent.ConcurrentMap;\nimport java.util.concurrent.TimeUnit;\n\nimport com.google.common.base.Function;\nimport com.google.common.collect.MapMaker.RemovalListener;\nimport com.google.common.collect.MapMaker.RemovalNotification;\n\npublic class OtterMigrateMap {\n\n    @SuppressWarnings(\"deprecation\")\n    public static <K, V> ConcurrentMap<K, V> makeComputingMap(MapMaker maker,\n                                                              Function<? super K, ? extends V> computingFunction) {\n        return maker.makeComputingMap(computingFunction);\n    }\n\n    @SuppressWarnings(\"deprecation\")\n    public static <K, V> ConcurrentMap<K, V> makeComputingMap(Function<? super K, ? extends V> computingFunction) {\n        return new MapMaker().makeComputingMap(computingFunction);\n    }\n\n    @SuppressWarnings(\"deprecation\")\n    public static <K, V> ConcurrentMap<K, V> makeSoftValueComputingMap(MapMaker maker,\n                                                                       Function<? super K, ? extends V> computingFunction) {\n        return maker.softValues().makeComputingMap(computingFunction);\n    }\n\n    @SuppressWarnings(\"deprecation\")\n    public static <K, V> ConcurrentMap<K, V> makeSoftValueComputingMap(Function<? super K, ? extends V> computingFunction) {\n        return new MapMaker().softValues().makeComputingMap(computingFunction);\n    }\n\n    @SuppressWarnings(\"deprecation\")\n    public static <K, V> ConcurrentMap<K, V> makeSoftValueComputingMapWithTimeout(MapMaker maker,\n                                                                                  Function<? super K, ? extends V> computingFunction,\n                                                                                  long timeout, TimeUnit timeUnit) {\n        return maker.expireAfterWrite(timeout, timeUnit).softValues().makeComputingMap(computingFunction);\n    }\n\n    @SuppressWarnings(\"deprecation\")\n    public static <K, V> ConcurrentMap<K, V> makeSoftValueComputingMapWithTimeout(Function<? super K, ? extends V> computingFunction,\n                                                                       long timeout, TimeUnit timeUnit) {\n        return new MapMaker().expireAfterWrite(timeout, timeUnit).softValues().makeComputingMap(computingFunction);\n    }\n\n    @SuppressWarnings(\"deprecation\")\n    public static <K, V> ConcurrentMap<K, V> makeSoftValueMapWithTimeout(MapMaker maker,\n    long timeout, TimeUnit timeUnit) {\n        return maker.expireAfterWrite(timeout, timeUnit).softValues().makeMap();\n    }\n\n    @SuppressWarnings(\"deprecation\")\n    public static <K, V> ConcurrentMap<K, V> makeSoftValueMapWithTimeout(long timeout, TimeUnit timeUnit) {\n        return new MapMaker().expireAfterWrite(timeout, timeUnit).softValues().makeMap();\n    }\n\n    @SuppressWarnings(\"deprecation\")\n    public static <K, V> ConcurrentMap<K, V> makeSoftValueComputingMapWithRemoveListenr(MapMaker maker,\n                                                                                        Function<? super K, ? extends V> computingFunction,\n                                                                                        final OtterRemovalListener listener) {\n        return maker.softValues().removalListener(new RemovalListener<K, V>() {\n\n            @Override\n            public void onRemoval(RemovalNotification<K, V> notification) {\n                if (notification == null) {\n                    return;\n                }\n\n                listener.onRemoval(notification.getKey(), notification.getValue());\n            }\n        }).makeComputingMap(computingFunction);\n    }\n\n    @SuppressWarnings(\"deprecation\")\n    public static <K, V> ConcurrentMap<K, V> makeSoftValueComputingMapWithRemoveListenr(Function<? super K, ? extends V> computingFunction,\n                                                                                        final OtterRemovalListener<K, V> listener) {\n        return new MapMaker().softValues().removalListener(new RemovalListener<K, V>() {\n\n            @Override\n            public void onRemoval(RemovalNotification<K, V> notification) {\n                if (notification == null) {\n                    return;\n                }\n\n                listener.onRemoval(notification.getKey(), notification.getValue());\n            }\n        }).makeComputingMap(computingFunction);\n    }\n\n    public static interface OtterRemovalListener<K, V> {\n\n        void onRemoval(K key, V value);\n    }\n}\n"
  },
  {
    "path": "shared/common/src/main/resources/jtester.properties",
    "content": "SpringModule.ApplicationContextFactory.ImplClassName=com.alibaba.otter.shared.common.utils.test.JTesterxClassPathXmlApplicationContext\n"
  },
  {
    "path": "shared/common/src/saveVersion.sh",
    "content": "#!/bin/sh\n\n# This file is used to generate the package-info.java class that\n# records the version, revision, branch, user, timestamp, and url\n\nunset LANG\nunset LC_CTYPE\nunset LC_TIME\nuser=`whoami`\ndate=`date`\ncurrent_path=`pwd`\nbin_abs_path=$(readlink -f $(dirname $0))\nproject_path=$(readlink -f $(dirname $bin_abs_path/../../../../))\necho \"run saveVerion.sh script\"\necho \"cd to $bin_abs_path for workaround relative path\"\ncd $bin_abs_path\n\nif [ -d ../../../.svn ]; then\n  revision=`svn info | sed -n -e 's/Last Changed Rev: \\(.*\\)/\\1/p'`\n  url=`svn info node | sed -n -e 's/URL: \\(.*\\)/\\1/p'`\n  # Get canonical branch (branches/X, tags/X, or trunk)\n  branch=`echo $url | sed -n -e 's,.*\\(branches/.*\\)$,\\1,p' \\\n                             -e 's,.*\\(tags/.*\\)$,\\1,p' \\\n                             -e 's,.*/trunk$,trunk,p'`\n  version=`echo $url | sed -n -e 's,.*branches/\\(.*\\)$,\\1,p' \\\n                             -e 's,.*tags/\\(.*\\)$,\\1,p' \\\n                             -e 's,.*/trunk$,trunk,p'`\nelif [ -d ../../../.git ]; then\n  revision=`git log -1 --pretty=format:\"%H\"`\n  hostname=`hostname`\n  url=\"git://${hostname}${project_path}\"\n  version=`cat ../pom.xml | grep -oP '<version>4\\.\\d+\\.\\d+(-SNAPSHOT)?' | cut -c 10-`\n  branch=`git branch  | awk '{print $2}'`\nelse\n  revision=\"Unknown\"\n  branch=\"Unknown\"\n  version=\"Unknown\"\n  url=\"file://$bin_abs_path\"\nfi\nsrcChecksum=`find ./ -maxdepth 1 -name '*.sh' | LC_ALL=C sort | xargs md5sum | md5sum | cut -d ' ' -f 1`\n\ncat << EOF | \\\n  sed -e \"s/VERSION/$version/\" -e \"s/USER/$user/\" -e \"s/DATE/$date/\" \\\n      -e \"s|URL|$url|\" -e \"s/REV/$revision/\" \\\n      -e \"s|BRANCH|$branch|\" -e \"s/SRCCHECKSUM/$srcChecksum/\" \\\n      > main/java/com/alibaba/otter/shared/common/utils/version/package-info.java\n/*\n * Generated by saveVersion.sh\n */\n@OtterVersionAnnotation(version=\"VERSION\", revision=\"REV\", branch=\"BRANCH\",\n                         user=\"USER\", date=\"DATE\", url=\"URL\",\n                         srcChecksum=\"SRCCHECKSUM\")\npackage com.alibaba.otter.shared.common.utils.version;\nEOF\n\necho \"cd to $current_path for continue\"\ncd $current_path\n"
  },
  {
    "path": "shared/common/src/test/java/com/alibaba/otter/shared/common/AddressUtilsTest.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common;\n\nimport java.net.InetAddress;\n\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.shared.common.utils.AddressUtils;\n\npublic class AddressUtilsTest extends BaseOtterTest {\n\n    @Test\n    public void testHostIp() {\n        InetAddress address = AddressUtils.getHostAddress();\n        want.bool(address.isLoopbackAddress()).is(false);\n    }\n\n    @Test\n    public void testPort() {\n        want.bool(AddressUtils.isAvailablePort(23)).is(false);\n    }\n\n}\n"
  },
  {
    "path": "shared/common/src/test/java/com/alibaba/otter/shared/common/BaseOtterPerformance.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common;\n\nimport java.text.MessageFormat;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.concurrent.Callable;\nimport java.util.concurrent.CountDownLatch;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.Future;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.alibaba.otter.shared.common.utils.TestUtils;\n\n/**\n * 性能测试基类,在原先PerformanceTesterExt基础上添加了warmup的功能，保证thread和JIT优化\n * \n * <pre>\n * 使用方法:\n * BaseOtterPerformance tester = new BaseOtterPerformance(20, 1000, 1000);\n * try {\n *   tester.test(new BaseOtterPerformance.Job(\"测试\") {\n *     public void execute() {\n *       try {\n *         Thread.sleep(10);\n *       } catch (InterruptedException e) {\n *       }\n *     }\n *   });\n * } catch (Warn e) {\n *   //断言失败\n * }\n * \n * @author jianghang 2011-10-10 上午11:15:40\n * @version 4.0.0\n */\npublic class BaseOtterPerformance {\n\n    private static final Logger log = LoggerFactory.getLogger(BaseOtterPerformance.class);\n\n    // 并发线程数\n    private int                 threads;\n    // 单个线程循环次数\n    private int                 loop;\n    // 性能阀值\n    private double              threshold;\n\n    public BaseOtterPerformance(int threads, int loop, double threshold){\n        this.threads = threads;\n        this.loop = loop;\n        this.threshold = threshold;\n    }\n\n    public void test(Job job) throws Warn {\n        // 准备测试\n        Statistics statistics = new Statistics(job.getName(), threads, loop);\n        CountDownLatch latch = new CountDownLatch(threads);\n        ExecutorService pool = Executors.newFixedThreadPool(threads);\n        List<Callable<Long>> tasks = new ArrayList<Callable<Long>>(threads);\n        List<Future<Long>> results = new ArrayList<Future<Long>>(threads);\n        // 先做一下预热，为JIT优化做准备\n        warmup(pool, job);\n        TestUtils.restoreJvm(); // 进行GC回收\n        // 执行性能测试\n        for (int i = 0; i < threads; i++) {\n            tasks.add(new Runner(latch, job, loop));\n        }\n\n        long start = System.currentTimeMillis();\n        try {\n            results = pool.invokeAll(tasks);\n            latch.await();\n            // 得到结果\n            for (Future<Long> future : results) {\n                statistics.addTime(future.get());\n            }\n        } catch (Exception e) {\n            throw new RuntimeException(\"run test fail.\", e);\n        }\n        long duration = System.currentTimeMillis() - start;\n        statistics.setTotal(duration);\n        // 关闭线程池\n        pool.shutdown();\n\n        // 统计结果处理\n        processStatistics(statistics);\n    }\n\n    private void warmup(ExecutorService pool, Job job) {\n        // 进行预热处理\n        CountDownLatch warmuplatch = new CountDownLatch(threads);\n        List<Callable<Long>> tasks = new ArrayList<Callable<Long>>(threads);\n        int warmup = 10070;\n        int loop = warmup / threads + 1;// 计算出每个thread平均需要执行的次数\n        // 先进行预热，加载一些类，避免影响测试\n        for (int i = 0; i < threads; i++) {\n            tasks.add(new Runner(warmuplatch, job, loop));\n        }\n        try {\n            pool.invokeAll(tasks);\n            warmuplatch.await();\n        } catch (InterruptedException e) {\n            throw new RuntimeException(\"run test fail.\", e);\n        }\n    }\n\n    /*\n     * 处理统计信息\n     */\n    private void processStatistics(Statistics statistics) throws Warn {\n        // 打印统计信息\n        System.out.println(statistics);\n        // 阀值判断,判断性能是否达标\n        if (statistics.average() > threshold) {\n            throw new Warn(threshold, statistics.average());\n        }\n    }\n\n    public class Runner implements Callable<Long> {\n\n        private CountDownLatch latch;\n        private Job            job;\n        private int            loop;\n\n        public Runner(CountDownLatch latch, Job job, int loop){\n            this.latch = latch;\n            this.job = job;\n            this.loop = loop;\n        }\n\n        public Long call() throws Exception {\n            long start = System.currentTimeMillis();\n            for (int i = 0; i < loop; i++) {\n                try {\n                    job.execute();\n                } catch (Exception e) {\n                    // 暂时通过log的方式打印.如果失败场景多的话,需要设置Success&Fail机制\n                    log.error(\"run job fail:\", e);\n                }\n            }\n            long duration = System.currentTimeMillis() - start;\n            latch.countDown();\n            return duration;\n        }\n\n    }\n\n    /**\n     * <pre>\n     * 性能统计信息\n     * </pre>\n     */\n    public static class Statistics {\n\n        /** 一个线程 平均响应时间 */\n        public static final String  AVERAGE_PER_THREAD  = \"AVERAGE_PER_THREAD\";\n        /** 单次请求 平均响应时间 */\n        public static final String  AVERAGE_PER_REQUEST = \"AVERAGE_PER_REQUEST\";\n\n        // toString pattern\n        private static final String TO_STRING_PATTERN   = \"Statistics [NAME:{0}; TPS:{1}; AVERAGE:{2}ms]\";\n\n        // 统计名称\n        private String              name;\n        // 线程数\n        private int                 threads;\n        // 每个线程循环数\n        private int                 loop;\n        // 总共耗时时间,单位毫秒\n        private long                total;\n        // 每个线程消耗时间,单位毫秒\n        private List<Long>          times;\n\n        public Statistics(String name, int threads, int loop){\n            this.name = name;\n            this.threads = threads;\n            this.loop = loop;\n            this.times = new ArrayList<Long>(threads);\n        }\n\n        /**\n         * 得到统计名称\n         * \n         * @return 统计名称\n         */\n        public String name() {\n            return name;\n        }\n\n        /**\n         * 添加线程消耗时间\n         * \n         * @param time 单个线程小时的时间,单位毫秒\n         */\n        public void addTime(long time) {\n            times.add(time);\n        }\n\n        /**\n         * 设置总耗时时间\n         * \n         * @param total 总耗时,单位毫秒\n         */\n        public void setTotal(long total) {\n            this.total = total;\n        }\n\n        /**\n         * 得到总耗时时间\n         * \n         * @return 总耗时,单位毫秒\n         */\n        public long total() {\n            return total;\n        }\n\n        /**\n         * 得到平均响应时间\n         * \n         * @return 平均响应时间,单位毫秒\n         */\n        public double average() {\n            return average(AVERAGE_PER_REQUEST);\n        }\n\n        /**\n         * 得到TPS\n         * \n         * @return TPS\n         */\n        public long tps() {\n            return (threads * loop) * 1000 / total;\n        }\n\n        /**\n         * 不同类型下的平均响应时间\n         * \n         * @param type {AVERAGE,AVERAGE_PER_THREAD,AVERAGE_PER_REQUEST}\n         * @return 平均响应时间,单位毫秒\n         */\n        public double average(String type) {\n            if (AVERAGE_PER_THREAD.equals(type)) {\n                long loopTotal = 0;\n                for (long t : times) {\n                    loopTotal += t;\n                }\n                return ((double) loopTotal) / threads;\n            }\n            if (AVERAGE_PER_REQUEST.equals(type)) {\n                long loopTotal = 0;\n                for (long t : times) {\n                    loopTotal += t;\n                }\n                // System.out.println(loopTotal);\n                return ((double) loopTotal) / (threads * loop);\n            }\n            throw new IllegalArgumentException(\"type error.\");\n        }\n\n        @Override\n        public String toString() {\n            return MessageFormat.format(TO_STRING_PATTERN, name(), tps(), average());\n        }\n\n    }\n\n    /**\n     * 测试单元逻辑\n     */\n    public static abstract class Job {\n\n        // 测试名称\n        private String name;\n\n        public Job(String name){\n            this.name = name;\n        }\n\n        public String getName() {\n            return name;\n        }\n\n        /**\n         * 测试逻辑\n         */\n        public abstract void execute();\n\n    }\n\n    /**\n     * <pre>\n     * 警告\n     * 性能不达标时,抛出此异常\n     * </pre>\n     */\n    public static class Warn extends Exception {\n\n        private static final long serialVersionUID = -5790026554772900047L;\n\n        private double            expected;\n        private double            actual;\n\n        public Warn(double expected, double actual){\n            this.expected = expected;\n            this.actual = actual;\n        }\n\n        public double getExpected() {\n            return expected;\n        }\n\n        public double getActual() {\n            return actual;\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "shared/common/src/test/java/com/alibaba/otter/shared/common/BaseOtterTest.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common;\n\nimport org.jtester.annotations.SpringApplicationContext;\n\n/**\n * @author jianghang 2010-6-2 上午11:48:00\n */\n@SpringApplicationContext(\"applicationContext.xml\")\npublic class BaseOtterTest extends org.jtester.testng.JTester {\n\n}\n"
  },
  {
    "path": "shared/common/src/test/java/com/alibaba/otter/shared/common/InteAddressTest.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common;\n\nimport java.net.InetSocketAddress;\n\nimport com.alibaba.otter.shared.common.utils.JsonUtils;\n\npublic class InteAddressTest {\n\n    public static void main(String args[]) {\n        {\n            InetSocketAddress address = new InetSocketAddress(\"127.0.0.1\", 3306);\n            System.out.println(address.getHostString());\n            String str = JsonUtils.marshalToString(address);\n            System.out.println(str);\n\n            address = JsonUtils.unmarshalFromString(str, InetSocketAddress.class);\n            System.out.println(address);\n        }\n\n        {\n            InetSocketAddress address = new InetSocketAddress(\"localhost\", 3306);\n            System.out.println(address.getHostString());\n            String str = JsonUtils.marshalToString(address);\n            System.out.println(str);\n\n            address = JsonUtils.unmarshalFromString(str, InetSocketAddress.class);\n            System.out.println(address);\n        }\n\n        {\n            InetSocketAddress address = new InetSocketAddress(\"127.0.0.2\", 3306);\n            System.out.println(address.getHostString());\n            String str = JsonUtils.marshalToString(address);\n            System.out.println(str);\n\n            address = JsonUtils.unmarshalFromString(str, InetSocketAddress.class);\n            System.out.println(address);\n        }\n\n        {\n            InetSocketAddress address = new InetSocketAddress(\"notx\", 3306);\n            System.out.println(address.getHostString());\n            String str = JsonUtils.marshalToString(address);\n            System.out.println(str);\n\n            address = JsonUtils.unmarshalFromString(str, InetSocketAddress.class);\n            System.out.println(address);\n        }\n    }\n}\n"
  },
  {
    "path": "shared/common/src/test/java/com/alibaba/otter/shared/common/JsonUtilsTest.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common;\n\nimport java.io.IOException;\nimport java.io.StringReader;\nimport java.io.StringWriter;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.testng.annotations.Test;\n\nimport com.alibaba.fastjson.JSONReader;\nimport com.alibaba.fastjson.JSONWriter;\nimport com.alibaba.fastjson.TypeReference;\nimport com.alibaba.fastjson.annotation.JSONField;\nimport com.alibaba.fastjson.serializer.SerializerFeature;\nimport com.alibaba.otter.shared.common.model.config.Transient;\nimport com.alibaba.otter.shared.common.model.config.data.DataMediaType;\nimport com.alibaba.otter.shared.common.model.config.data.db.DbMediaSource;\nimport com.alibaba.otter.shared.common.utils.ByteUtils;\nimport com.alibaba.otter.shared.common.utils.JsonUtils;\n\npublic class JsonUtilsTest extends BaseOtterTest {\n\n    @Test\n    public void test_filter() {\n        DbMediaSource media = new DbMediaSource();\n        media.setGmtCreate(new Date());\n        media.setGmtModified(new Date());\n        media.setId(1L);\n        media.setName(\"test\");\n        media.setType(DataMediaType.MYSQL);// 这个是枚举值\n        media.setUsername(\"ljh\");\n        media.setPassword(\"ljh\");\n\n        String str = JsonUtils.marshalToString(media, \"gmtCreate\", \"gmtModified\", \"id\", \"name\", \"type\");\n        want.string(str).notContain(\"id\");\n\n        DbMediaSource target = JsonUtils.unmarshalFromString(str, DbMediaSource.class);\n        want.object(target.getUsername()).notNull();\n        want.object(target.getPassword()).notNull();\n    }\n\n    @Test\n    public void test_map_byte() {\n        Map data = new HashMap<String, byte[]>();\n        byte[] one = new byte[] { 1, 2, 3 };\n        byte[] two = new byte[] { 4, 5, 6 };\n        data.put(\"one\", one);\n        data.put(\"two\", two);\n\n        byte[] bytes = JsonUtils.marshalToByte(data);\n        Map target = JsonUtils.unmarshalFromByte(bytes, Map.class);\n        byte[] oneDates = ByteUtils.base64StringToBytes((String) target.get(\"one\"));\n        byte[] twoDates = ByteUtils.base64StringToBytes((String) target.get(\"two\"));\n\n        check(oneDates, one);\n        check(twoDates, two);\n    }\n\n    @Test\n    public void test_bytes() {\n        Map data = new HashMap<String, byte[]>();\n        byte[] one = new byte[] { 1, 2, 3 };\n        byte[] two = new byte[] { 4, 5, 6 };\n        data.put(\"one\", one);\n        data.put(\"two\", two);\n\n        StringWriter jsonStr = new StringWriter();\n        JSONWriter writer = new JSONWriter(jsonStr);// 超大文本写入\n        writer.startArray();\n        writer.writeValue(one);\n        writer.writeValue(two);\n        writer.endArray();\n        try {\n            writer.close();\n        } catch (IOException e) {\n        }\n\n        JSONReader reader = new JSONReader(new StringReader(jsonStr.getBuffer().toString()));\n        byte[] oneDates = null;\n        byte[] twoDates = null;\n        reader.startArray();\n        while (reader.hasNext()) {\n            if (oneDates == null) {\n                oneDates = reader.readObject(byte[].class);\n            } else if (twoDates == null) {\n                twoDates = reader.readObject(byte[].class);\n            } else {\n                want.fail(\"not possible\");\n            }\n\n        }\n        reader.endArray();\n        reader.close();\n        check(oneDates, one);\n        check(twoDates, two);\n    }\n\n    private void check(byte[] src, byte[] dest) {\n        want.object(src).notNull();\n        want.object(dest).notNull();\n        want.bool(src.length == dest.length).is(true);\n\n        for (int i = 0; i < src.length; i++) {\n            if (src[i] != dest[i]) {\n                want.fail();\n            }\n        }\n    }\n\n    @Test\n    public void test_list() {\n        SubPipeKey key1 = new SubPipeKey();\n        key1.setId(1);\n        key1.setUrl(\"test1\");\n        key1.setName(\"key1\");\n\n        SubPipeKey key2 = new SubPipeKey();\n        key2.setId(2);\n        key2.setUrl(\"test2\");\n        key2.setName(\"key1\");\n\n        PipeEventData<List<PipeKey>> data = new PipeEventData<List<PipeKey>>();\n        data.setId(1);\n        data.setKey(key1);\n        String json = JsonUtils.marshalToString(data, SerializerFeature.WriteClassName);\n        System.out.println(json);\n\n        PipeEventData result = JsonUtils.unmarshalFromString(json, new TypeReference<PipeEventData<List<PipeKey>>>() {\n\n        });\n        System.out.println(result);\n        want.bool(result.getKey() instanceof SubPipeKey).is(true);\n    }\n\n    @Test\n    public void testTransTransient() {\n        SubPipeKey key = new SubPipeKey();\n        key.setName(\"hello\");\n        key.setId(1);\n\n        String json = JsonUtils.marshalToStringWithoutTransient(key);\n        System.out.println(json);\n    }\n\n    public static class PipeEventData<T> {\n\n        @JSONField(serialzeFeatures = { SerializerFeature.WriteClassName })\n        private PipeKey key;\n\n        @JSONField(serialize = false, deserialize = true)\n        private int     id;\n\n        public PipeKey getKey() {\n            return key;\n        }\n\n        public void setKey(PipeKey key) {\n            this.key = key;\n        }\n\n        public int getId() {\n            return id;\n        }\n\n        public void setId(int id) {\n            this.id = id;\n        }\n\n    }\n\n    public static class PipeKey {\n\n        private String url;\n        private int    id;\n\n        public String getUrl() {\n            return url;\n        }\n\n        public void setUrl(String url) {\n            this.url = url;\n        }\n\n        public int getId() {\n            return id;\n        }\n\n        public void setId(int id) {\n            this.id = id;\n        }\n\n    }\n\n    public static class SubPipeKey extends PipeKey {\n\n        @Transient\n        private String name = \"l\";\n\n        public String getName() {\n            return name;\n        }\n\n        public void setName(String name) {\n            this.name = name;\n        }\n\n    }\n}\n"
  },
  {
    "path": "shared/common/src/test/java/com/alibaba/otter/shared/common/config/ConfigHelperTest.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.config;\n\nimport org.apache.oro.text.regex.MalformedPatternException;\nimport org.apache.oro.text.regex.Pattern;\nimport org.apache.oro.text.regex.PatternCompiler;\nimport org.apache.oro.text.regex.PatternMatcher;\nimport org.apache.oro.text.regex.Perl5Compiler;\nimport org.apache.oro.text.regex.Perl5Matcher;\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.shared.common.BaseOtterTest;\nimport com.alibaba.otter.shared.common.model.config.ConfigException;\nimport com.alibaba.otter.shared.common.model.config.ConfigHelper;\nimport com.alibaba.otter.shared.common.model.config.data.DataMedia.ModeValue;\n\npublic class ConfigHelperTest extends BaseOtterTest {\n\n    @Test\n    public void testParse() {\n        String v1 = \"offer[001-128]\";\n        ModeValue m1 = ConfigHelper.parseMode(v1);\n        want.bool(m1.getMode().isMulti()).is(true);\n        want.collection(m1.getMultiValue()).sizeEq(128);\n\n        String v2 = \"offer[1-128]test\";\n        ModeValue m2 = ConfigHelper.parseMode(v2);\n        want.bool(m2.getMode().isMulti()).is(true);\n        want.collection(m2.getMultiValue()).sizeEq(128);\n\n        String v3 = \"[1-128]test\";\n        ModeValue m3 = ConfigHelper.parseMode(v3);\n        want.bool(m3.getMode().isMulti()).is(true);\n        want.collection(m3.getMultiValue()).sizeEq(128);\n\n        String v4 = \"offer[1-128\";\n        ModeValue m4 = ConfigHelper.parseMode(v4);\n        want.bool(m4.getMode().isWildCard()).is(true);\n\n        String v5 = \"offer1-128]\";\n        ModeValue m5 = ConfigHelper.parseMode(v5);\n        want.bool(m5.getMode().isWildCard()).is(true);\n\n        String v6 = \"offer1128\";\n        ModeValue m6 = ConfigHelper.parseMode(v6);\n        want.bool(m6.getMode().isSingle()).is(true);\n    }\n\n    @Test\n    public void testWildCard() {\n        PatternMatcher matcher = new Perl5Matcher();\n\n        Pattern pattern = null;\n        PatternCompiler pc = new Perl5Compiler();\n        try {\n            pattern = pc.compile(\"havana_us_.*\", Perl5Compiler.DEFAULT_MASK);\n        } catch (MalformedPatternException e) {\n            throw new ConfigException(e);\n        }\n        boolean ismatch = matcher.matches(\"havana_us_0001\", pattern);\n        System.out.println(ismatch);\n    }\n}\n"
  },
  {
    "path": "shared/common/src/test/java/com/alibaba/otter/shared/common/sample/JtesterxSampleTest.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.sample;\n\nimport mockit.Mocked;\nimport mockit.Verifications;\n\nimport org.jtester.annotations.SpringBeanByName;\nimport org.jtester.annotations.SpringBeanFrom;\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.shared.common.BaseOtterTest;\n\n/**\n * 标准单元测试 示例1\n * \n * @author jianghang 2010-6-2 上午11:45:07\n */\npublic class JtesterxSampleTest extends BaseOtterTest {\n\n    @SpringBeanByName\n    private TestService      testService;\n\n    @SpringBeanByName\n    private TestMockService1 testService1;\n\n    @Mocked\n    @SpringBeanFrom\n    private TestMockService2 testService2;\n\n    @SpringBeanByName\n    private TestMockService3 testService3;\n\n    @Test\n    public void testExpectService2() {\n        final String MOCK_SERVICE2 = \"mock service 2 done!\";\n\n        // new NonStrictExpectations和 Expectations区别： Expectations只允许调用被mock的方法\n        new NonStrictExpectations(TestMockService2.class) {\n\n            {\n                testService2.doTestMockService2();\n                returns(MOCK_SERVICE2);\n            }\n        };\n\n        want.string(testService.doTestService1(\"test 1\")).start(MOCK_SERVICE2);\n\n        new Verifications(1) { // 检查是否执行了两次\n\n            {\n                testService2.doTestMockService2();\n            }\n        };\n    }\n\n    /**\n     * 验证mock行为是否正确\n     */\n    @Test\n    public void testExpect() {\n        final String MOCK_SERVICE1 = \"mock service 1 done!\";\n        final String MOCK_SERVICE2 = \"mock service 2 done!\";\n\n        // new NonStrictExpectations和 Expectations区别： Expectations只允许调用被mock的方法\n        new NonStrictExpectations(TestMockService1.class, TestMockService2.class) {\n\n            {\n                testService1.doTestMockService1();\n                returns(MOCK_SERVICE1);\n\n                testService2.doTestMockService2();\n                returns(MOCK_SERVICE2);\n            }\n        };\n\n        want.string(testService.doTestService1(\"test 1\")).start(MOCK_SERVICE2);\n        want.string(testService.doTestService2()).isEqualTo(MOCK_SERVICE2);\n\n        new Verifications(2) { // 检查是否执行了两次\n\n            {\n                testService2.doTestMockService2();\n            }\n        };\n    }\n\n    /**\n     * 验证执行路径是否正确\n     */\n    @Test\n    public void testVerications() {\n        want.string(testService3.doTestMockService3()).isEqualTo(\"server 3 done!\");\n        new Verifications() {\n\n            {\n                testService3.doTestMockService3();\n            }\n        };\n    }\n}\n"
  },
  {
    "path": "shared/common/src/test/java/com/alibaba/otter/shared/common/sample/TestMockService1.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.sample;\n\nimport org.springframework.beans.factory.InitializingBean;\n\n/**\n * 测试的Mock servie1\n * \n * @author jianghang 2010-6-2 下午12:20:05\n */\npublic class TestMockService1 implements InitializingBean {\n\n    private TestMockService2 testService2;\n\n    public String doTestMockService1() {\n        return \"server 1 done!\";\n    }\n\n    public String doTestMockService1(String param) {\n        return testService2.doTestMockService2() + param;\n    }\n\n    public void setTestService2(TestMockService2 testService2) {\n        this.testService2 = testService2;\n    }\n\n    @Override\n    public void afterPropertiesSet() throws Exception {\n        System.out.println(\"TestMockService1 init!\");\n    }\n\n}\n"
  },
  {
    "path": "shared/common/src/test/java/com/alibaba/otter/shared/common/sample/TestMockService2.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.sample;\n\nimport org.springframework.beans.factory.InitializingBean;\n\n/**\n * 测试的Mock servie2\n * \n * @author jianghang 2010-6-2 下午12:20:49\n */\npublic class TestMockService2 implements InitializingBean {\n\n    public String doTestMockService2() {\n        return \"server 2 done!\";\n    }\n\n    @Override\n    public void afterPropertiesSet() throws Exception {\n        System.out.println(\"TestMockService2 init!\");\n    }\n}\n"
  },
  {
    "path": "shared/common/src/test/java/com/alibaba/otter/shared/common/sample/TestMockService3.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.sample;\n\nimport org.springframework.beans.factory.InitializingBean;\n\n/**\n * 测试的Mock servie2,测试lazy-init=true是否正常\n * \n * @author jianghang 2010-6-2 下午12:20:49\n */\npublic class TestMockService3 implements InitializingBean {\n\n    public String doTestMockService3() {\n        return \"server 3 done!\";\n    }\n\n    @Override\n    public void afterPropertiesSet() throws Exception {\n        System.out.println(\"TestMockService3 init!\");\n    }\n}\n"
  },
  {
    "path": "shared/common/src/test/java/com/alibaba/otter/shared/common/sample/TestService.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.sample;\n\n/**\n * @author jianghang 2010-6-2 下午12:19:41\n */\npublic class TestService {\n\n    private TestMockService1 testService1;\n    private TestMockService2 testService2;\n\n    public String doTestService1(String param) {\n        return testService1.doTestMockService1(param);\n    }\n\n    public String doTestService2() {\n        return testService2.doTestMockService2();\n    }\n\n    public void setTestService1(TestMockService1 testService1) {\n        this.testService1 = testService1;\n    }\n\n    public void setTestService2(TestMockService2 testService2) {\n        this.testService2 = testService2;\n    }\n\n}\n"
  },
  {
    "path": "shared/common/src/test/java/com/alibaba/otter/shared/common/utils/BooleanMutexTest.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.utils;\n\nimport java.util.concurrent.Callable;\nimport java.util.concurrent.CountDownLatch;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\n\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.shared.common.BaseOtterTest;\nimport com.alibaba.otter.shared.common.utils.lock.BooleanMutex;\n\n/**\n * 锁对象测试\n * \n * @author jianghang 2011-9-23 上午10:40:32\n * @version 4.0.0\n */\npublic class BooleanMutexTest extends BaseOtterTest {\n\n    @Test\n    public void test_init_true() {\n        BooleanMutex mutex = new BooleanMutex(true);\n        try {\n            mutex.get(); // 不会被阻塞\n        } catch (InterruptedException e) {\n            want.fail();\n        }\n    }\n\n    @Test\n    public void test_init_false() {\n        final BooleanMutex mutex = new BooleanMutex(false);\n        try {\n            final CountDownLatch count = new CountDownLatch(1);\n            ExecutorService executor = Executors.newCachedThreadPool();\n\n            executor.submit(new Callable() {\n\n                public Object call() throws Exception {\n                    Thread.sleep(1000);\n                    mutex.set(true);\n                    count.countDown();\n                    return null;\n                }\n            });\n\n            mutex.get(); // 会被阻塞，等异步线程释放锁对象\n            count.await();\n            executor.shutdown();\n        } catch (InterruptedException e) {\n            want.fail();\n        }\n    }\n\n    @Test\n    public void test_concurrent_true() {\n        try {\n            final BooleanMutex mutex = new BooleanMutex(true);\n            final CountDownLatch count = new CountDownLatch(10);\n            ExecutorService executor = Executors.newCachedThreadPool();\n\n            for (int i = 0; i < 10; i++) {\n                executor.submit(new Callable() {\n\n                    public Object call() throws Exception {\n                        mutex.get();\n                        count.countDown();\n                        return null;\n                    }\n                });\n            }\n            count.await();\n            executor.shutdown();\n        } catch (InterruptedException e) {\n            want.fail();\n        }\n    }\n\n    @Test\n    public void test_concurrent_false() {\n        try {\n            final BooleanMutex mutex = new BooleanMutex(false);// 初始为false\n            final CountDownLatch count = new CountDownLatch(10);\n            ExecutorService executor = Executors.newCachedThreadPool();\n\n            for (int i = 0; i < 10; i++) {\n                executor.submit(new Callable() {\n\n                    public Object call() throws Exception {\n                        mutex.get();// 被阻塞\n                        count.countDown();\n                        return null;\n                    }\n                });\n            }\n            Thread.sleep(1000);\n            mutex.set(true);\n            count.await();\n            executor.shutdown();\n        } catch (InterruptedException e) {\n            want.fail();\n        }\n    }\n}\n"
  },
  {
    "path": "shared/common/src/test/java/com/alibaba/otter/shared/common/utils/ExecIntegration.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.utils;\n\nimport java.io.File;\n\nimport org.testng.annotations.AfterMethod;\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.shared.common.BaseOtterTest;\nimport com.alibaba.otter.shared.common.utils.cmd.Exec;\nimport com.alibaba.otter.shared.common.utils.cmd.Exec.Result;\n\n/**\n * Exec 单元测试\n * \n * @author jianghang 2011-9-27 上午10:45:14\n * @version 4.0.0\n */\npublic class ExecIntegration extends BaseOtterTest {\n\n    @Test\n    public void testSample() {\n        try {\n            Result result = Exec.execute(\"dir\");// liunx和windows度支持的命令\n            want.object(result).notNull();\n            want.string(result.getStdout()).notBlank();\n        } catch (Exception e) {\n            want.fail();\n        }\n\n    }\n\n    @Test\n    public void testAppender() {\n        String tmp = System.getProperty(\"java.io.tmpdir\", \"/tmp\");\n        try {\n            Result result = Exec.execute(\"dir\", tmp + \"/exec.log\");// liunx和windows度支持的命令\n            want.object(result).notNull();\n            want.string(result.getStdout()).notBlank();\n        } catch (Exception e) {\n            e.printStackTrace();\n            want.fail();\n        }\n\n    }\n\n    @Test\n    public void testInput() {\n        String tmp = System.getProperty(\"java.io.tmpdir\", \"/tmp\");\n        try {\n            Result result = Exec.execute(\"dir\", tmp + \"/exec.log\", tmp.getBytes());// liunx和windows度支持的命令\n            want.object(result).notNull();\n            want.string(result.getStdout()).notBlank();\n        } catch (Exception e) {\n            want.fail();\n        }\n\n    }\n\n    @Test\n    public void testUserDir() {\n        String tmp = System.getProperty(\"java.io.tmpdir\", \"/tmp\");\n        try {\n            Result result = Exec.execute(\"dir\", tmp + \"/exec.log\", tmp.getBytes(), new File(tmp));// liunx和windows度支持的命令\n            want.object(result).notNull();\n            want.string(result.getStdout()).notBlank();\n        } catch (Exception e) {\n            want.fail();\n        }\n\n    }\n\n    @AfterMethod\n    public void tearDown() {\n        String tmp = System.getProperty(\"java.io.tmpdir\", \"/tmp\");\n        new File(tmp + \"/exec.log\").deleteOnExit();\n    }\n}\n"
  },
  {
    "path": "shared/common/src/test/java/com/alibaba/otter/shared/common/utils/ExecutorTemplateTest.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.utils;\n\nimport org.apache.commons.lang.math.RandomUtils;\nimport org.testng.annotations.AfterTest;\nimport org.testng.annotations.BeforeTest;\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.shared.common.BaseOtterTest;\nimport com.alibaba.otter.shared.common.utils.thread.ExecutorTemplate;\n\npublic class ExecutorTemplateTest extends BaseOtterTest {\n\n    private ExecutorTemplate template = new ExecutorTemplate();\n\n    @BeforeTest\n    public void setUp() {\n        try {\n            template.afterPropertiesSet();\n        } catch (Exception e) {\n            want.fail(e.getMessage());\n        }\n    }\n\n    @AfterTest\n    public void tearDown() {\n        try {\n            template.destroy();\n        } catch (Exception e) {\n            want.fail(e.getMessage());\n        }\n    }\n\n    @Test\n    public void testSimple() {\n        template.start();\n        long start = System.currentTimeMillis();\n        for (int i = 0; i < 10; i++) {\n            template.submit(new Runnable() {\n\n                public void run() {\n                    try {\n                        Thread.sleep(RandomUtils.nextInt(1000) + 1000);\n                    } catch (InterruptedException e) {\n                    }\n                }\n            });\n        }\n\n        template.waitForResult();\n        long end = System.currentTimeMillis();\n        System.out.println(\"cost : \" + (end - start));\n    }\n\n    @Test(expectedExceptions = RuntimeException.class)\n    public void testException() {\n        template.start();\n        long start = System.currentTimeMillis();\n        for (int i = 0; i < 10; i++) {\n            template.submit(new Runnable() {\n\n                public void run() {\n                    if (RandomUtils.nextBoolean()) {\n                        try {\n                            Thread.sleep(RandomUtils.nextInt(5000) + 5000);\n                        } catch (InterruptedException e) {\n                            System.out.println(\"i'm cancel\");\n                        }\n                    } else {\n                        throw new RuntimeException(\"i'm error\");\n                    }\n                }\n            });\n        }\n\n        template.waitForResult();\n        long end = System.currentTimeMillis();\n        System.out.println(\"cost : \" + (end - start));\n    }\n}\n"
  },
  {
    "path": "shared/common/src/test/java/com/alibaba/otter/shared/common/utils/JavaSourceCompilerTest.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.utils;\n\nimport java.io.IOException;\nimport java.util.List;\n\nimport org.apache.commons.io.IOUtils;\nimport org.apache.commons.lang.StringUtils;\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.shared.common.BaseOtterTest;\nimport com.alibaba.otter.shared.common.utils.compile.impl.JdkCompiler;\n\npublic class JavaSourceCompilerTest extends BaseOtterTest {\n\n    @Test\n    public void testSimple() {\n\n        String javasource = null;\n        try {\n            List<String> lines = IOUtils.readLines(Thread.currentThread().getContextClassLoader().getResourceAsStream(\"compiler.txt\"));\n            javasource = StringUtils.join(lines, \"\\n\");\n        } catch (IOException e1) {\n            e1.printStackTrace();\n        }\n        JdkCompiler compiler = new JdkCompiler();\n\n        Class<?> clazz = compiler.compile(javasource);\n        System.out.println(clazz.getName());\n    }\n\n}\n"
  },
  {
    "path": "shared/common/src/test/java/com/alibaba/otter/shared/common/utils/NioUtilsPerformance.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.utils;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileOutputStream;\nimport java.lang.reflect.Method;\nimport java.nio.ByteBuffer;\nimport java.nio.MappedByteBuffer;\nimport java.nio.channels.FileChannel;\nimport java.security.AccessController;\nimport java.security.PrivilegedAction;\n\nimport org.apache.commons.io.IOUtils;\n\n/**\n * 测试下nio的一些操作方法性能\n * \n * <pre>\n * jvm args : \n * -server -Xmx2g -Xms2g -Xmn512m -XX:PermSize=196m -Xss256k -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:LargePageSizeInBytes=128m -XX:+UseFastAccessorMethods -XX:+UseCMSInitiatingOccupancyOnly -XX:+UseCompressedOops\n * \n * result :\n * 500Mb : \n * copyTest cost : 12148 , 15924\n * channel cost : 14423 , 13847 \n * mapped cost : 7857 , 7883\n * sendfile cost : 5728 , 9352 \n * \n * 1GB : \n * copyTest cost : 32409 , 33557\n * channel cost : 32856 , 33305\n * mapped cost : 55789 , 52108\n * sendfile cost : 28179 , 30279\n * </pre>\n * \n * @author jianghang 2011-10-10 下午03:31:17\n * @version 4.0.0\n */\npublic class NioUtilsPerformance {\n\n    public static void main(String args[]) throws Exception {\n        long start = System.currentTimeMillis();\n        long end = -1;\n\n        copyTest(new File(\"/tmp/source.tar.gz\"), new File(\"/tmp/target-copy.tar.gz\"));\n        end = System.currentTimeMillis();\n        System.out.printf(\"%s cost : %d \\n\", \"copyTest\", end - start);\n        start = end;\n\n        channelTest(new File(\"/tmp/source.tar.gz\"), new File(\"/tmp/target-channel.tar.gz\"));\n        end = System.currentTimeMillis();\n        System.out.printf(\"%s cost : %d \\n\", \"channel\", end - start);\n        start = end;\n\n        mappedTest(new File(\"/tmp/source.tar.gz\"), new File(\"/tmp/target-mapped.tar.gz\"));\n        end = System.currentTimeMillis();\n        System.out.printf(\"%s cost : %d \\n\", \"mapped\", end - start);\n        start = end;\n\n        sendFileTest(new File(\"/tmp/source.tar.gz\"), new File(\"/tmp/target-sendile.tar.gz\"));\n        end = System.currentTimeMillis();\n        System.out.printf(\"%s cost : %d \\n\", \"sendfile\", end - start);\n    }\n\n    public static void copyTest(File source, File target) throws Exception {\n        FileInputStream fis = null;\n        FileOutputStream fos = null;\n        try {\n            fis = new FileInputStream(source);\n            fos = new FileOutputStream(target);\n            target.createNewFile();\n\n            byte[] bytes = new byte[16 * 1024];\n            int n = -1;\n            while ((n = fis.read(bytes, 0, bytes.length)) > 0) {\n                fos.write(bytes, 0, n);\n            }\n\n            fos.flush();\n        } finally {\n            IOUtils.closeQuietly(fis);\n            IOUtils.closeQuietly(fos);\n        }\n    }\n\n    public static void channelTest(File source, File target) throws Exception {\n        FileInputStream fis = null;\n        FileOutputStream fos = null;\n        try {\n            fis = new FileInputStream(source);\n            fos = new FileOutputStream(target);\n            FileChannel sChannel = fis.getChannel();\n            FileChannel tChannel = fos.getChannel();\n\n            target.createNewFile();\n\n            ByteBuffer buffer = ByteBuffer.allocate(16 * 1024);\n            while (sChannel.read(buffer) > 0) {\n                buffer.flip();\n                tChannel.write(buffer);\n                buffer.clear();\n            }\n\n            tChannel.close();\n            sChannel.close();\n        } finally {\n            IOUtils.closeQuietly(fis);\n            IOUtils.closeQuietly(fos);\n        }\n    }\n\n    public static void mappedTest(File source, File target) throws Exception {\n        FileInputStream fis = null;\n        FileOutputStream fos = null;\n        MappedByteBuffer mapbuffer = null;\n\n        try {\n            long fileSize = source.length();\n            final byte[] outputData = new byte[(int) fileSize];\n            fis = new FileInputStream(source);\n            fos = new FileOutputStream(target);\n            FileChannel sChannel = fis.getChannel();\n\n            target.createNewFile();\n\n            mapbuffer = sChannel.map(FileChannel.MapMode.READ_ONLY, 0, fileSize);\n            for (int i = 0; i < fileSize; i++) {\n                outputData[i] = mapbuffer.get();\n            }\n\n            mapbuffer.clear();\n            fos.write(outputData);\n            fos.flush();\n        } finally {\n            IOUtils.closeQuietly(fis);\n            IOUtils.closeQuietly(fos);\n\n            if (mapbuffer == null) {\n                return;\n            }\n\n            final Object buffer = mapbuffer;\n\n            AccessController.doPrivileged(new PrivilegedAction() {\n\n                public Object run() {\n                    try {\n                        Method clean = buffer.getClass().getMethod(\"cleaner\", new Class[0]);\n\n                        if (clean == null) {\n                            return null;\n                        }\n                        clean.setAccessible(true);\n                        sun.misc.Cleaner cleaner = (sun.misc.Cleaner) clean.invoke(buffer, new Object[0]);\n                        cleaner.clean();\n                    } catch (Throwable ex) {\n                    }\n\n                    return null;\n                }\n            });\n        }\n    }\n\n    public static void sendFileTest(File source, File target) throws Exception {\n        FileInputStream fis = null;\n        FileOutputStream fos = null;\n        try {\n            fis = new FileInputStream(source);\n            fos = new FileOutputStream(target);\n            FileChannel sChannel = fis.getChannel();\n            FileChannel tChannel = fos.getChannel();\n            target.createNewFile();\n            sChannel.transferTo(0, source.length(), tChannel);\n            tChannel.close();\n            sChannel.close();\n        } finally {\n            IOUtils.closeQuietly(fis);\n            IOUtils.closeQuietly(fos);\n        }\n    }\n}\n"
  },
  {
    "path": "shared/common/src/test/java/com/alibaba/otter/shared/common/utils/NioUtilsTest.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.utils;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\n\nimport org.apache.commons.lang.math.RandomUtils;\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.shared.common.BaseOtterTest;\n\n/**\n * NioUtils单元测试\n * \n * @author jianghang 2011-10-10 下午02:25:56\n * @version 4.0.0\n */\npublic class NioUtilsTest extends BaseOtterTest {\n\n    private static final String tmp = System.getProperty(\"java.io.tmpdir\", \"/tmp\");\n\n    @Test\n    public void test_create_delete() {\n        File file = new File(tmp + \"/nioTestFile.txt\");\n        NioUtils.create(file);\n        NioUtils.delete(file);\n\n        File dir = new File(tmp + \"/nioTestDir\");\n        NioUtils.create(dir, false, 1);\n        NioUtils.delete(dir);\n    }\n\n    public void test_read_write_byte() {\n        File file = new File(\"/tmp/nioTestFile.txt\");\n        NioUtils.create(file);\n        byte[] data = getBlock(10 * 1024);\n\n        try {\n            NioUtils.write(data, file);\n            byte[] result = NioUtils.read(file);\n            check(data, result);\n        } catch (IOException e) {\n            e.printStackTrace();\n            want.fail();\n        } finally {\n            NioUtils.delete(file);\n        }\n    }\n\n    public void test_read_write_stream() {\n        File file = new File(tmp, \"nioTestFile.txt\");\n        NioUtils.create(file);\n        byte[] data = getBlock(10 * 1024);\n\n        try {\n            NioUtils.write(data, new FileOutputStream(file));\n            byte[] result = NioUtils.read(new FileInputStream(file));\n            check(data, result);\n        } catch (IOException e) {\n            e.printStackTrace();\n            want.fail();\n        } finally {\n            NioUtils.delete(file);\n        }\n    }\n\n    @Test\n    public void test_move() {\n        File src = new File(tmp, \"nioTestFile1.txt\");\n        File dest = new File(tmp, \"nioTestFile2.txt\");\n        NioUtils.create(src);\n        byte[] data = getBlock(10 * 1024);\n\n        try {\n            NioUtils.write(data, new FileOutputStream(src));\n            NioUtils.move(src, dest);\n            byte[] result = NioUtils.read(new FileInputStream(dest));\n            check(data, result);\n        } catch (IOException e) {\n            e.printStackTrace();\n            want.fail();\n        } finally {\n            NioUtils.delete(src);\n            NioUtils.delete(dest);\n        }\n    }\n\n    @Test\n    public void test_copy_offest() {\n        File file = new File(tmp, \"nioTestFile.txt\");\n        NioUtils.create(file);\n        byte[] data = getBlock(20 * 1024);\n\n        try {\n            NioUtils.write(data, new FileOutputStream(file));\n            ByteArrayOutputStream output = new ByteArrayOutputStream();\n            NioUtils.copy(new FileInputStream(file), output, 10 * 1024, 5 * 1024);\n\n            byte[] result = output.toByteArray();\n            for (int i = 0; i < result.length; i++) {\n                if (result[i] != data[i + 10 * 1024]) {\n                    want.fail();\n                }\n            }\n        } catch (IOException e) {\n            e.printStackTrace();\n            want.fail();\n        } finally {\n            NioUtils.delete(file);\n        }\n    }\n\n    private void check(byte[] src, byte[] dest) {\n        want.object(src).notNull();\n        want.object(dest).notNull();\n        want.bool(src.length == dest.length).is(true);\n\n        for (int i = 0; i < src.length; i++) {\n            if (src[i] != dest[i]) {\n                want.fail();\n            }\n        }\n    }\n\n    private byte[] getBlock(int length) {\n        byte[] rawData = new byte[length];\n        for (int i = 0; i < rawData.length; i++) {\n            rawData[i] = (byte) (' ' + RandomUtils.nextInt(95));\n\n        }\n        return rawData;\n    }\n}\n"
  },
  {
    "path": "shared/common/src/test/java/com/alibaba/otter/shared/common/utils/ResourceBundleUtilTest.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.common.utils;\n\nimport java.util.MissingResourceException;\n\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.shared.common.BaseOtterTest;\nimport com.alibaba.otter.shared.common.utils.code.ResourceBundleUtil;\n\n/**\n * @author jianghang 2011-9-13 下午04:45:23\n */\npublic class ResourceBundleUtilTest extends BaseOtterTest {\n\n    private static final String RESOURCE_LOCATION = \"code/ResourceBundleUtil\";\n\n    // 测试资源文件没有找到\n    @Test\n    public void testResourceBundleNotFound() {\n        try {\n            new ResourceBundleUtil(\"code/NotFound\");\n        } catch (RuntimeException e) {\n            want.object(e).clazIs(MissingResourceException.class);\n        }\n    }\n\n    // 测试不同key下的value情况\n    @Test\n    public void testResourceBundle() {\n        ResourceBundleUtil util = new ResourceBundleUtil(RESOURCE_LOCATION);\n        // key为空\n        want.object(util.getMessage(null)).isNull();\n        // key对应的value为空字符串\n        want.string(util.getMessage(\"key1\")).isEqualTo(\"\");\n        // 静态value的测试\n        want.string(util.getMessage(\"key2\")).isEqualTo(\"value2\");\n        // 动态渲染的测试\n        want.string(util.getMessage(\"key3\", \"stone\")).isEqualTo(\"value3,stone\");\n    }\n\n}\n"
  },
  {
    "path": "shared/common/src/test/resources/applicationContext.xml",
    "content": "<?xml version=\"1.0\" encoding=\"GB2312\"?>\n<beans xmlns=\"http://www.springframework.org/schema/beans\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:tx=\"http://www.springframework.org/schema/tx\" xmlns:aop=\"http://www.springframework.org/schema/aop\" xmlns:lang=\"http://www.springframework.org/schema/lang\" xmlns:context=\"http://www.springframework.org/schema/context\" xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd\n           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd\n           http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-2.0.xsd\n           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd\n           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd\"\n    default-autowire=\"byName\">\n\n    <import resource=\"classpath:spring/otter-test-*.xml\" />\n</beans>"
  },
  {
    "path": "shared/common/src/test/resources/code/ResourceBundleUtil.properties",
    "content": "=value0\r\nkey1=\r\nkey2=value2\r\nkey3=value3,{0}"
  },
  {
    "path": "shared/common/src/test/resources/compiler.txt",
    "content": "package com.alibaba.otter.node.extend.fileresolver;\n\nimport java.util.Map;\n\nimport org.apache.commons.lang.StringUtils;\n\nimport com.alibaba.otter.shared.etl.extend.fileresolver.FileInfo;\n\npublic class TestFileResolver extends AbstractFileResolver {\n\n    public FileInfo[] getFileInfo(Map<String, String> rowMap) {\n        \n        String path = rowMap.get(\"FIELD\"); \n        FileInfo fileInfo = null;\n        if (StringUtils.isNotEmpty(path)) {\n            fileInfo = new FileInfo(path);\n            return new FileInfo[] { fileInfo };\n        } else {\n            return null;\n        }\n    }\n\n}"
  },
  {
    "path": "shared/common/src/test/resources/otter.properties",
    "content": "otter.communication.pool.size=10\notter.manager.address=127.0.0.1:1099\notter.zookeeper.cluster.default=127.0.0.1:2181\notter.zookeeper.sessionTimeout=60000\n\n# otter node root dir\notter.nodeHome = ${java.io.tmpdir}\n\n## otter node dir\notter.htdocs.dir = ${otter.nodeHome}/htdocs\notter.download.dir = ${otter.nodeHome}/download\notter.extend.dir= ${otter.nodeHome}/extend\n\n# manager email user config\notter.manager.monitor.email.host = smtp.gmail.com\notter.manager.monitor.email.username = jianghang115@gmail.com\notter.manager.monitor.email.password = xxxxxxx\notter.manager.monitor.email.stmp.port = 465"
  },
  {
    "path": "shared/common/src/test/resources/spring/otter-test-sample.xml",
    "content": "<?xml version=\"1.0\" encoding=\"GB2312\"?>\n<!DOCTYPE beans PUBLIC \"-//SPRING//DTD BEAN 2.0//EN\" \"http://www.springframework.org/dtd/spring-beans-2.0.dtd\">\n<beans default-autowire=\"byName\">\n\t<bean id=\"testService\" class=\"com.alibaba.otter.shared.common.sample.TestService\" />\n\t<bean id=\"testService1\" class=\"com.alibaba.otter.shared.common.sample.TestMockService1\" />\n\t<bean id=\"testService2\" class=\"com.alibaba.otter.shared.common.sample.TestMockService2\" />\n\t<bean id=\"testService3\" class=\"com.alibaba.otter.shared.common.sample.TestMockService3\" scope=\"singleton\"/>\n</beans>"
  },
  {
    "path": "shared/communication/pom.xml",
    "content": "<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\">\n\t<modelVersion>4.0.0</modelVersion>\n\t<parent>\n\t\t<groupId>com.alibaba.otter</groupId>\n\t\t<artifactId>shared</artifactId>\n\t\t<version>4.2.19-SNAPSHOT</version>\n\t\t<relativePath>../pom.xml</relativePath>\n\t</parent>\n\t<groupId>com.alibaba.otter</groupId>\n\t<artifactId>shared.communication</artifactId>\n\t<packaging>jar</packaging>\n\t<name>communication module for otter</name>\n\t<url>http://github.com/alibaba/otter</url>\n\t\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>com.alibaba.otter</groupId>\n\t\t\t<artifactId>shared.common</artifactId>\n\t\t\t<version>${project.version}</version>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>com.alibaba</groupId>\n\t\t\t<artifactId>dubbo</artifactId>\n\t\t\t<exclusions>\n\t\t\t\t<exclusion>\n\t\t\t\t\t<groupId>org.springframework</groupId>\n\t\t\t\t\t<artifactId>spring</artifactId>\n\t\t\t\t</exclusion>\n\t\t\t</exclusions>\n\t\t</dependency>\n\t\t<!-- test dependency -->\n\t\t<dependency>\n\t\t\t<groupId>junit</groupId>\n\t\t\t<artifactId>junit</artifactId>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.jtester</groupId>\n\t\t\t<artifactId>jtester</artifactId>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\t</dependencies>\n</project>\n"
  },
  {
    "path": "shared/communication/src/main/java/com/alibaba/otter/shared/communication/core/CommunicationClient.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.communication.core;\n\nimport com.alibaba.otter.shared.communication.core.model.Callback;\nimport com.alibaba.otter.shared.communication.core.model.Event;\n\n/**\n * 通讯服务类\n * \n * @author jianghang 2011-9-9 下午04:12:38\n */\npublic interface CommunicationClient {\n\n    /**\n     * 启动communication客户端\n     */\n    public void initial();\n\n    /**\n     * 关闭communication客户端\n     */\n    public void destory();\n\n    /**\n     * 指定对应的地址，进行event调用. 地址格式为：127.0.0.1:1099\n     * \n     * @param nid\n     * @param event\n     */\n    public Object call(final String addr, final Event event);\n\n    /**\n     * 指定对应的地址，进行event调用，并注册对应的callback接口. 地址格式为：127.0.0.1:1099\n     * \n     * <pre>\n     * 注意：该方法为异步调用\n     * </pre>\n     * \n     * @param nid\n     * @param event\n     */\n    public void call(final String addr, Event event, final Callback callback);\n\n    /**\n     * 指定对应的地址列表，进行event调用. 地址格式为：127.0.0.1:1099\n     * \n     * @param nid\n     * @param event\n     */\n    public Object call(final String[] addrs, final Event event);\n\n    /**\n     * 指定对应的地址列表，进行event调用，并注册对应的callback接口. 地址格式为：127.0.0.1:1099\n     * \n     * <pre>\n     * 注意：该方法为异步调用\n     * </pre>\n     * \n     * @param nid\n     * @param event\n     */\n    public void call(final String[] serveraddrs, final Event event, final Callback callback);\n\n}\n"
  },
  {
    "path": "shared/communication/src/main/java/com/alibaba/otter/shared/communication/core/CommunicationEndpoint.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.communication.core;\n\nimport com.alibaba.otter.shared.communication.core.model.Event;\n\n/**\n * 通讯服务端点，需要在每个node上部署后，就可以通过Communication工具进行数据通讯\n * \n * @author jianghang 2011-9-9 下午04:07:51\n */\npublic interface CommunicationEndpoint {\n\n    /**\n     * 初始化endpint\n     */\n    public void initial();\n\n    /**\n     * 销毁endpoint\n     */\n    public void destory();\n\n    /**\n     * 接受一个消息\n     * \n     * @return\n     */\n    public Object acceptEvent(Event event);\n\n}\n"
  },
  {
    "path": "shared/communication/src/main/java/com/alibaba/otter/shared/communication/core/CommunicationRegistry.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.communication.core;\n\nimport java.util.Iterator;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.alibaba.otter.shared.communication.core.model.EventType;\n\n/**\n * 注册中心,针对每个事件允许绑定一个对象处理,类似于webx中的Action事件处理\n * \n * @author jianghang\n */\npublic class CommunicationRegistry {\n\n    private static final Logger           logger = LoggerFactory.getLogger(CommunicationRegistry.class);\n\n    // 定义事件和动作的关联关系\n    private static Map<EventType, Object> table  = new ConcurrentHashMap<EventType, Object>();\n\n    /**\n     * 注册一个事件对应的处理对象\n     * \n     * @param eventType\n     * @param action\n     */\n    public static void regist(EventType eventType, Object action) {\n        if (logger.isInfoEnabled()) {\n            logger.info(\" Regist \" + action + \" For \" + eventType);\n        }\n\n        if (table.containsKey(eventType)) {\n            if (logger.isWarnEnabled()) {\n                logger.warn(\" EventType \" + eventType + \" is already exist!\");\n            }\n        }\n        table.put(eventType, action);\n    }\n\n    /**\n     * 注册一组事件处理对象\n     * \n     * @param eventType\n     * @param action\n     */\n    public static void regist(EventType[] eventTypes, Object action) {\n        if (eventTypes != null) {\n            for (EventType eventType : eventTypes) {\n                regist(eventType, action);\n            }\n        }\n    }\n\n    /**\n     * 注册一个事件处理对象\n     * \n     * @param eventType\n     */\n    public static void unregist(EventType eventType) {\n        if (logger.isInfoEnabled()) {\n            logger.info(\" Un Regist EventType : \" + eventType);\n        }\n\n        table.remove(eventType);\n    }\n\n    /**\n     * 注销调一个Action\n     * \n     * @param action\n     * @return\n     */\n    public static void unregist(Object action) {\n        if (logger.isInfoEnabled()) {\n            logger.info(\" Un Regist Action : \" + action);\n        }\n\n        if (action != null) {\n            for (Iterator<Map.Entry<EventType, Object>> iter = table.entrySet().iterator(); iter.hasNext();) {\n                Map.Entry<EventType, Object> entry = iter.next();\n                if (action == entry.getValue()) {\n                    if (logger.isInfoEnabled()) {\n                        logger.info(\" Find \" + entry.getKey() + \" For : \" + action);\n                    }\n\n                    iter.remove();\n                }\n            }\n        }\n    }\n\n    /**\n     * 获得事件对应的Action\n     * \n     * @param eventType\n     * @return\n     */\n    public static Object getAction(EventType eventType) {\n        return table.get(eventType);\n    }\n}\n"
  },
  {
    "path": "shared/communication/src/main/java/com/alibaba/otter/shared/communication/core/exception/CommunicationException.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.communication.core.exception;\n\nimport org.apache.commons.lang.exception.NestableRuntimeException;\n\n/**\n * @author jianghang 2011-9-9 下午05:05:09\n */\npublic class CommunicationException extends NestableRuntimeException {\n\n    private static final long serialVersionUID = -7288830284122672209L;\n\n    private String            errorCode;\n    private String            errorDesc;\n\n    public CommunicationException(String errorCode){\n        super(errorCode);\n    }\n\n    public CommunicationException(String errorCode, Throwable cause){\n        super(errorCode, cause);\n    }\n\n    public CommunicationException(String errorCode, String errorDesc){\n        super(errorCode + \":\" + errorDesc);\n    }\n\n    public CommunicationException(String errorCode, String errorDesc, Throwable cause){\n        super(errorCode + \":\" + errorDesc, cause);\n    }\n\n    public CommunicationException(Throwable cause){\n        super(cause);\n    }\n\n    public String getErrorCode() {\n        return errorCode;\n    }\n\n    public String getErrorDesc() {\n        return errorDesc;\n    }\n\n    @Override\n    public Throwable fillInStackTrace() {\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "shared/communication/src/main/java/com/alibaba/otter/shared/communication/core/impl/AbstractCommunicationEndpoint.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.communication.core.impl;\n\nimport java.lang.reflect.Method;\n\nimport org.apache.commons.lang.StringUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.util.ReflectionUtils;\n\nimport com.alibaba.otter.shared.communication.core.CommunicationEndpoint;\nimport com.alibaba.otter.shared.communication.core.CommunicationRegistry;\nimport com.alibaba.otter.shared.communication.core.exception.CommunicationException;\nimport com.alibaba.otter.shared.communication.core.model.Event;\nimport com.alibaba.otter.shared.communication.core.model.heart.HeartEvent;\n\n/**\n * 默认的endpoint实现\n * \n * @author jianghang 2011-9-9 下午07:01:49\n */\npublic abstract class AbstractCommunicationEndpoint implements CommunicationEndpoint {\n\n    // 需要禁止输出详细内容的事件\n    private static final Logger logger         = LoggerFactory.getLogger(CommunicationEndpoint.class);\n\n    private static final String DEFAULT_METHOD = \"handleEvent\";\n\n    /**\n     * 处理指定的事件\n     */\n    public Object acceptEvent(Event event) {\n        if (event instanceof HeartEvent) {\n            return event; // 针对心跳请求，返回一个随意结果\n        }\n\n        try {\n            Object action = CommunicationRegistry.getAction(event.getType());\n            if (action != null) {\n\n                // 通过反射获取方法并执行\n                String methodName = \"on\" + StringUtils.capitalize(event.getType().toString());\n                Method method = ReflectionUtils.findMethod(action.getClass(), methodName,\n                                                           new Class[] { event.getClass() });\n                if (method == null) {\n                    methodName = DEFAULT_METHOD; // 尝试一下默认方法\n                    method = ReflectionUtils.findMethod(action.getClass(), methodName, new Class[] { event.getClass() });\n\n                    if (method == null) { // 再尝试一下Event参数\n                        method = ReflectionUtils.findMethod(action.getClass(), methodName, new Class[] { Event.class });\n                    }\n                }\n                // 方法不为空就调用指定的方法,反之调用缺省的处理函数\n                if (method != null) {\n                    try {\n                        ReflectionUtils.makeAccessible(method);\n                        return method.invoke(action, new Object[] { event });\n                    } catch (Throwable e) {\n                        throw new CommunicationException(\"method_invoke_error:\" + methodName, e);\n                    }\n                } else {\n                    throw new CommunicationException(\"no_method_error for[\"\n                                                     + StringUtils.capitalize(event.getType().toString())\n                                                     + \"] in Class[\" + action.getClass().getName() + \"]\");\n                }\n\n            }\n\n            throw new CommunicationException(\"eventType_no_action\", event.getType().name());\n        } catch (RuntimeException e) {\n            logger.error(\"endpoint_error\", e);\n            throw e;\n        } catch (Exception e) {\n            logger.error(\"endpoint_error\", e);\n            throw new CommunicationException(e);\n        }\n    }\n}\n"
  },
  {
    "path": "shared/communication/src/main/java/com/alibaba/otter/shared/communication/core/impl/DefaultCommunicationClientImpl.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.communication.core.impl;\n\nimport java.net.InetAddress;\nimport java.net.UnknownHostException;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.concurrent.Callable;\nimport java.util.concurrent.ExecutionException;\nimport java.util.concurrent.ExecutorCompletionService;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Future;\nimport java.util.concurrent.LinkedBlockingQueue;\nimport java.util.concurrent.RejectedExecutionHandler;\nimport java.util.concurrent.ThreadPoolExecutor;\nimport java.util.concurrent.TimeUnit;\n\nimport org.apache.commons.lang.StringUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.util.Assert;\n\nimport com.alibaba.otter.shared.common.utils.thread.NamedThreadFactory;\nimport com.alibaba.otter.shared.communication.core.CommunicationClient;\nimport com.alibaba.otter.shared.communication.core.exception.CommunicationException;\nimport com.alibaba.otter.shared.communication.core.impl.connection.CommunicationConnection;\nimport com.alibaba.otter.shared.communication.core.impl.connection.CommunicationConnectionFactory;\nimport com.alibaba.otter.shared.communication.core.model.Callback;\nimport com.alibaba.otter.shared.communication.core.model.CommunicationParam;\nimport com.alibaba.otter.shared.communication.core.model.Event;\n\n/**\n * 通讯交互的client的默认实现实现\n * \n * @author jianghang\n */\npublic class DefaultCommunicationClientImpl implements CommunicationClient {\n\n    private static final Logger            logger     = LoggerFactory.getLogger(DefaultCommunicationClientImpl.class);\n\n    private CommunicationConnectionFactory factory    = null;\n    private int                            poolSize   = 10;\n    private ExecutorService                executor   = null;\n    private int                            retry      = 3;\n    private int                            retryDelay = 1000;\n    private boolean                        discard    = false;\n\n    public DefaultCommunicationClientImpl(){\n    }\n\n    public DefaultCommunicationClientImpl(CommunicationConnectionFactory factory){\n        this.factory = factory;\n    }\n\n    public void initial() {\n        RejectedExecutionHandler handler = null;\n        if (discard) {\n            handler = new ThreadPoolExecutor.DiscardPolicy();\n        } else {\n            handler = new ThreadPoolExecutor.AbortPolicy();\n        }\n\n        executor = new ThreadPoolExecutor(poolSize, poolSize, 60 * 1000L, TimeUnit.MILLISECONDS,\n                                          new LinkedBlockingQueue<Runnable>(10 * 1000),\n                                          new NamedThreadFactory(\"communication-async\"), handler);\n    }\n\n    public void destory() {\n        executor.shutdown();\n    }\n\n    public Object call(final String addr, final Event event) {\n        Assert.notNull(this.factory, \"No factory specified\");\n        CommunicationParam params = buildParams(addr);\n        CommunicationConnection connection = null;\n        int count = 0;\n        Throwable ex = null;\n        while (count++ < retry) {\n            try {\n                connection = factory.createConnection(params);\n                return connection.call(event);\n            } catch (Exception e) {\n                logger.error(String.format(\"call[%s] , retry[%s]\", addr, count), e);\n                try {\n                    Thread.sleep(count * retryDelay);\n                } catch (InterruptedException e1) {\n                    // ignore\n                }\n                ex = e;\n            } finally {\n                if (connection != null) {\n                    connection.close();\n                }\n            }\n        }\n\n        logger.error(\"call[{}] failed , event[{}]!\", addr, event.toString());\n        throw new CommunicationException(\"call[\" + addr + \"] , Event[\" + event.toString() + \"]\", ex);\n    }\n\n    public void call(final String addr, final Event event, final Callback callback) {\n        Assert.notNull(this.factory, \"No factory specified\");\n        submit(new Runnable() {\n\n            @Override\n            public void run() {\n                Object obj = call(addr, event);\n                callback.call(obj);\n            }\n        });\n    }\n\n    public Object call(final String[] addrs, final Event event) {\n        Assert.notNull(this.factory, \"No factory specified\");\n        if (addrs == null || addrs.length == 0) {\n            throw new IllegalArgumentException(\"addrs example: 127.0.0.1:1099\");\n        }\n\n        ExecutorCompletionService completionService = new ExecutorCompletionService(executor);\n        List<Future<Object>> futures = new ArrayList<Future<Object>>(addrs.length);\n        List result = new ArrayList(10);\n        for (final String addr : addrs) {\n            futures.add(completionService.submit((new Callable<Object>() {\n\n                @Override\n                public Object call() throws Exception {\n                    return DefaultCommunicationClientImpl.this.call(addr, event);\n                }\n            })));\n        }\n\n        Exception ex = null;\n        int errorIndex = 0;\n        while (errorIndex < futures.size()) {\n            try {\n                Future future = completionService.take();// 它也可能被打断\n                future.get();\n            } catch (InterruptedException e) {\n                Thread.currentThread().interrupt();\n                ex = e;\n                break;\n            } catch (ExecutionException e) {\n                ex = e;\n                break;\n            }\n\n            errorIndex++;\n        }\n\n        if (errorIndex < futures.size()) {\n            for (int index = 0; index < futures.size(); index++) {\n                Future<Object> future = futures.get(index);\n                if (future.isDone() == false) {\n                    future.cancel(true);\n                }\n            }\n        } else {\n            for (int index = 0; index < futures.size(); index++) {\n                Future<Object> future = futures.get(index);\n                try {\n                    result.add(future.get());\n                } catch (InterruptedException e) {\n                    // ignore\n                    Thread.currentThread().interrupt();\n                } catch (ExecutionException e) {\n                    // ignore\n                }\n            }\n        }\n\n        if (ex != null) {\n            throw new CommunicationException(String.format(\"call addr[%s] error by %s\", addrs[errorIndex],\n                                                           ex.getMessage()), ex);\n        } else {\n            return result;\n        }\n    }\n\n    public void call(final String[] addrs, final Event event, final Callback callback) {\n        Assert.notNull(this.factory, \"No factory specified\");\n        if (addrs == null || addrs.length == 0) {\n            throw new IllegalArgumentException(\"addrs example: 127.0.0.1:1099\");\n        }\n        submit(new Runnable() {\n\n            @Override\n            public void run() {\n                Object obj = call(addrs, event);\n                callback.call(obj);\n            }\n        });\n    }\n\n    /**\n     * 直接提交一个异步任务\n     */\n    public Future submit(Runnable call) {\n        Assert.notNull(this.factory, \"No factory specified\");\n        return executor.submit(call);\n    }\n\n    /**\n     * 直接提交一个异步任务\n     */\n    public Future submit(Callable call) {\n        Assert.notNull(this.factory, \"No factory specified\");\n        return executor.submit(call);\n    }\n\n    // ===================== helper method ==================\n\n    private CommunicationParam buildParams(String addr) {\n        CommunicationParam params = new CommunicationParam();\n        String[] strs = StringUtils.split(addr, \":\");\n        if (strs == null || strs.length != 2) {\n            throw new IllegalArgumentException(\"addr example: 127.0.0.1:1099\");\n        }\n        InetAddress address = null;\n        try {\n            address = InetAddress.getByName(strs[0]);\n        } catch (UnknownHostException e) {\n            throw new CommunicationException(\"addr_error\", \"addr[\" + addr + \"] is unknow!\");\n        }\n        params.setIp(address.getHostAddress());\n        params.setPort(Integer.valueOf(strs[1]));\n        return params;\n    }\n\n    // ============================= setter / getter ==========================\n\n    public void setFactory(CommunicationConnectionFactory factory) {\n        this.factory = factory;\n    }\n\n    public void setExecutor(ExecutorService executor) {\n        this.executor = executor;\n    }\n\n    public void setRetry(int retry) {\n        this.retry = retry;\n    }\n\n    public void setRetryDelay(int retryDelay) {\n        this.retryDelay = retryDelay;\n    }\n\n    public void setPoolSize(int poolSize) {\n        this.poolSize = poolSize;\n    }\n\n    public void setDiscard(boolean discard) {\n        this.discard = discard;\n    }\n\n}\n"
  },
  {
    "path": "shared/communication/src/main/java/com/alibaba/otter/shared/communication/core/impl/connection/CommunicationConnection.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.communication.core.impl.connection;\n\nimport com.alibaba.otter.shared.communication.core.exception.CommunicationException;\nimport com.alibaba.otter.shared.communication.core.model.CommunicationParam;\nimport com.alibaba.otter.shared.communication.core.model.Event;\n\n/**\n * 通讯链接\n * \n * @author jianghang 2011-9-9 下午04:55:25\n */\npublic interface CommunicationConnection {\n\n    public Object call(Event event);\n\n    public CommunicationParam getParams();\n\n    public void close() throws CommunicationException;\n}\n"
  },
  {
    "path": "shared/communication/src/main/java/com/alibaba/otter/shared/communication/core/impl/connection/CommunicationConnectionFactory.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.communication.core.impl.connection;\n\nimport com.alibaba.otter.shared.communication.core.model.CommunicationParam;\n\n/**\n * {@linkplain CommunicationConnection}链接创建和关闭工厂\n * \n * @author jianghang 2011-9-9 下午05:24:09\n */\npublic interface CommunicationConnectionFactory {\n\n    CommunicationConnection createConnection(CommunicationParam params);\n\n    void releaseConnection(CommunicationConnection connection);\n}\n"
  },
  {
    "path": "shared/communication/src/main/java/com/alibaba/otter/shared/communication/core/impl/connection/CommunicationConnectionPoolFactory.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.communication.core.impl.connection;\n\nimport org.apache.commons.pool.impl.GenericKeyedObjectPool;\n\nimport com.alibaba.otter.shared.communication.core.exception.CommunicationException;\nimport com.alibaba.otter.shared.communication.core.impl.rmi.RmiCommunicationConnectionFactory;\nimport com.alibaba.otter.shared.communication.core.model.CommunicationParam;\n\n/**\n * @author jianghang 2011-9-9 下午05:48:13\n */\npublic class CommunicationConnectionPoolFactory implements CommunicationConnectionFactory {\n\n    private volatile GenericKeyedObjectPool pool      = null;\n    private CommunicationConnectionFactory  factory   = new RmiCommunicationConnectionFactory();\n    private int                             maxActive = 10;\n\n    public CommunicationConnectionPoolFactory(){\n    }\n\n    public CommunicationConnectionPoolFactory(CommunicationConnectionFactory factory){\n        this.factory = factory;\n        initial();\n    }\n\n    public void initial() {\n        if (factory == null) {\n            throw new IllegalArgumentException(\"factory is null!\");\n        }\n\n        // 创建链接池对象\n        pool = new GenericKeyedObjectPool();\n        pool.setMaxActive(maxActive);\n        pool.setMaxIdle(maxActive);\n        pool.setMinIdle(0);\n        pool.setMaxWait(60 * 1000); // 60s\n        pool.setTestOnBorrow(false);\n        pool.setTestOnReturn(false);\n        pool.setTimeBetweenEvictionRunsMillis(10 * 1000);\n        pool.setNumTestsPerEvictionRun(maxActive * 2);\n        pool.setMinEvictableIdleTimeMillis(30 * 60 * 1000);\n        pool.setTestWhileIdle(true);\n        pool.setFactory(new CommunicationConnectionPoolableFactory(factory)); // 设置连接池管理对象\n    }\n\n    public void destory() {\n        try {\n            pool.close();\n        } catch (Exception e) {\n            throw new CommunicationException(\"Connection_Pool_Close_Error\", e);\n        }\n    }\n\n    public CommunicationConnection createConnection(CommunicationParam params) {\n        try {\n            CommunicationConnectionPoolable poolable = new CommunicationConnectionPoolable(\n                                                                                           (CommunicationConnection) pool.borrowObject(params),\n                                                                                           this);\n            return poolable;\n        } catch (Exception e) {\n            throw new CommunicationException(\"createConnection_error\", e);\n        }\n    }\n\n    public void releaseConnection(CommunicationConnection connection) {\n        try {\n            pool.returnObject(connection.getParams(), connection);\n        } catch (Exception e) {\n            throw new CommunicationException(\"releaseConnection_error\", e);\n        }\n    }\n\n    // ======================= setter / getter =======================\n\n    public void setFactory(CommunicationConnectionFactory factory) {\n        this.factory = factory;\n    }\n\n    public void setMaxActive(int maxActive) {\n        this.maxActive = maxActive;\n    }\n\n}\n"
  },
  {
    "path": "shared/communication/src/main/java/com/alibaba/otter/shared/communication/core/impl/connection/CommunicationConnectionPoolable.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.communication.core.impl.connection;\n\nimport com.alibaba.otter.shared.communication.core.exception.CommunicationException;\nimport com.alibaba.otter.shared.communication.core.model.CommunicationParam;\nimport com.alibaba.otter.shared.communication.core.model.Event;\n\n/**\n * 可被链接池管理的对象, @see {@linkplain CommunicationConnectionPoolableFactory}\n * \n * @author jianghang 2011-9-9 下午05:01:14\n */\npublic class CommunicationConnectionPoolable implements CommunicationConnection {\n\n    private CommunicationConnectionPoolFactory pool;\n    private CommunicationConnection            delegate;\n\n    public CommunicationConnectionPoolable(CommunicationConnection connection, CommunicationConnectionPoolFactory pool){\n        this.delegate = connection;\n        this.pool = pool;\n    }\n\n    public Object call(Event event) {\n        return getDelegate().call(event);\n    }\n\n    public void close() throws CommunicationException {\n        pool.releaseConnection(this);\n    }\n\n    public CommunicationParam getParams() {\n        return getDelegate().getParams();\n    }\n\n    /**\n     * @return 返回原始connection对象\n     */\n    public CommunicationConnection getDelegate() {\n        return this.delegate;\n    }\n\n}\n"
  },
  {
    "path": "shared/communication/src/main/java/com/alibaba/otter/shared/communication/core/impl/connection/CommunicationConnectionPoolableFactory.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.communication.core.impl.connection;\n\nimport org.apache.commons.pool.KeyedPoolableObjectFactory;\nimport org.apache.commons.pool.PoolableObjectFactory;\nimport org.apache.commons.pool.impl.GenericObjectPool;\n\nimport com.alibaba.otter.shared.communication.core.model.CommunicationParam;\nimport com.alibaba.otter.shared.communication.core.model.heart.HeartEvent;\n\n/**\n * 链接池内容管理工厂, @see {@linkplain GenericObjectPool} , {@linkplain PoolableObjectFactory}\n * \n * @author jianghang 2011-9-9 下午05:00:15\n */\npublic class CommunicationConnectionPoolableFactory implements KeyedPoolableObjectFactory {\n\n    private CommunicationConnectionFactory factory;\n\n    public CommunicationConnectionPoolableFactory(CommunicationConnectionFactory factory){\n        this.factory = factory;\n    }\n\n    public void destroyObject(Object key, Object obj) throws Exception {\n        if (obj instanceof CommunicationConnectionPoolable) {\n            factory.releaseConnection(((CommunicationConnectionPoolable) obj).getDelegate());// 关闭原始链接\n        } else {\n            throw new IllegalArgumentException(\"pool object is not CommunicationConnectionPoolable!\");\n        }\n    }\n\n    public Object makeObject(Object key) throws Exception {\n        if (key instanceof CommunicationParam) {\n            return factory.createConnection((CommunicationParam) key);// 创建链接\n        } else {\n            throw new IllegalArgumentException(\"key object is not CommunicationParams!\");\n        }\n    }\n\n    public void passivateObject(Object key, Object obj) throws Exception {\n    }\n\n    public void activateObject(Object key, Object obj) throws Exception {\n    }\n\n    public boolean validateObject(Object key, Object obj) {\n        if (obj instanceof CommunicationConnectionPoolable) {\n            CommunicationConnectionPoolable connection = (CommunicationConnectionPoolable) obj;\n            try {\n                Object value = connection.call(new HeartEvent());//发起一次心跳检查\n                return value != null;\n            } catch (Exception e) {\n                return false;\n            }\n        }\n        return false;\n    }\n\n}\n"
  },
  {
    "path": "shared/communication/src/main/java/com/alibaba/otter/shared/communication/core/impl/dubbo/DubboCommunicationConnection.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.communication.core.impl.dubbo;\n\nimport com.alibaba.otter.shared.communication.core.CommunicationEndpoint;\nimport com.alibaba.otter.shared.communication.core.exception.CommunicationException;\nimport com.alibaba.otter.shared.communication.core.impl.connection.CommunicationConnection;\nimport com.alibaba.otter.shared.communication.core.model.CommunicationParam;\nimport com.alibaba.otter.shared.communication.core.model.Event;\n\n/**\n * @author jianghang 2011-11-29 上午11:10:50\n * @version 4.0.0\n */\npublic class DubboCommunicationConnection implements CommunicationConnection {\n\n    private CommunicationEndpoint endpoint;\n    private CommunicationParam    params;\n\n    public DubboCommunicationConnection(CommunicationParam params, CommunicationEndpoint endpoint){\n        this.params = params;\n        this.endpoint = endpoint;\n    }\n\n    public void close() throws CommunicationException {\n        // do nothing\n    }\n\n    public Object call(Event event) {\n        // 调用rmi传递数据到目标server上\n        return endpoint.acceptEvent(event);\n    }\n\n    @Override\n    public CommunicationParam getParams() {\n        return params;\n    }\n\n}\n"
  },
  {
    "path": "shared/communication/src/main/java/com/alibaba/otter/shared/communication/core/impl/dubbo/DubboCommunicationConnectionFactory.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.communication.core.impl.dubbo;\n\nimport java.text.MessageFormat;\nimport java.util.Map;\n\nimport com.alibaba.dubbo.common.Constants;\nimport com.alibaba.dubbo.common.URL;\nimport com.alibaba.dubbo.common.extension.ExtensionLoader;\nimport com.alibaba.dubbo.rpc.ProxyFactory;\nimport com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol;\nimport com.alibaba.otter.shared.communication.core.CommunicationEndpoint;\nimport com.alibaba.otter.shared.communication.core.impl.connection.CommunicationConnection;\nimport com.alibaba.otter.shared.communication.core.impl.connection.CommunicationConnectionFactory;\nimport com.alibaba.otter.shared.communication.core.model.CommunicationParam;\nimport com.google.common.base.Function;\nimport com.google.common.collect.OtterMigrateMap;\n\n/**\n * dubbo rpc服务链接的factory\n * \n * @author jianghang 2011-11-29 上午11:13:31\n * @version 4.0.0\n */\npublic class DubboCommunicationConnectionFactory implements CommunicationConnectionFactory {\n\n    private final String                       DUBBO_SERVICE_URL = \"dubbo://{0}:{1}/endpoint?client=netty&codec=dubbo&serialization=java&lazy=true&iothreads=4&threads=50&connections=30&acceptEvent.timeout=50000&payload={2}\";\n\n    private DubboProtocol                      protocol          = DubboProtocol.getDubboProtocol();\n    private ProxyFactory                       proxyFactory      = ExtensionLoader.getExtensionLoader(ProxyFactory.class)\n                                                                     .getExtension(\"javassist\");\n\n    private Map<String, CommunicationEndpoint> connections       = null;\n    private int                                payload           = Constants.DEFAULT_PAYLOAD;\n\n    public DubboCommunicationConnectionFactory(){\n        connections = OtterMigrateMap.makeComputingMap(new Function<String, CommunicationEndpoint>() {\n\n            public CommunicationEndpoint apply(String serviceUrl) {\n                return proxyFactory.getProxy(protocol.refer(CommunicationEndpoint.class, URL.valueOf(serviceUrl)));\n            }\n        });\n    }\n\n    public CommunicationConnection createConnection(CommunicationParam params) {\n        if (params == null) {\n            throw new IllegalArgumentException(\"param is null!\");\n        }\n\n        // 构造对应的url， String.valueOf() 为避免数字包含千位符\n        String serviceUrl = MessageFormat.format(DUBBO_SERVICE_URL, params.getIp(), String.valueOf(params.getPort()), String.valueOf(payload));\n        CommunicationEndpoint endpoint = connections.get(serviceUrl);\n        return new DubboCommunicationConnection(params, endpoint);\n\n    }\n\n    public void releaseConnection(CommunicationConnection connection) {\n        // do nothing\n    }\n\n    // =============== setter / gettter ==================\n\n    public void setPayload(int payload) {\n        this.payload = payload;\n    }\n\n}\n"
  },
  {
    "path": "shared/communication/src/main/java/com/alibaba/otter/shared/communication/core/impl/dubbo/DubboCommunicationEndpoint.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.communication.core.impl.dubbo;\n\nimport java.text.MessageFormat;\n\nimport com.alibaba.dubbo.common.Constants;\nimport com.alibaba.dubbo.common.URL;\nimport com.alibaba.dubbo.common.extension.ExtensionLoader;\nimport com.alibaba.dubbo.rpc.Exporter;\nimport com.alibaba.dubbo.rpc.ProxyFactory;\nimport com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol;\nimport com.alibaba.otter.shared.communication.core.CommunicationEndpoint;\nimport com.alibaba.otter.shared.communication.core.impl.AbstractCommunicationEndpoint;\n\n/**\n * 基于dubbo的endpoint实现,仅仅使用了dubb的rpc工具\n * \n * @author jianghang 2011-11-29 上午11:08:29\n * @version 4.0.0\n */\npublic class DubboCommunicationEndpoint extends AbstractCommunicationEndpoint {\n\n    private static final String             DUBBO_SERVICE_URL = \"dubbo://127.0.0.1:{0}/endpoint?server=netty&codec=dubbo&serialization=java&heartbeat=5000&iothreads=4&threads=50&connections=30&payload={1}\";\n    private DubboProtocol                   protocol          = DubboProtocol.getDubboProtocol();\n    private ProxyFactory                    proxyFactory      = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getExtension(\"javassist\");\n\n    private Exporter<CommunicationEndpoint> exporter          = null;\n    private int                             port              = 2088;\n    private int                             payload           = Constants.DEFAULT_PAYLOAD;\n\n    public DubboCommunicationEndpoint(){\n\n    }\n\n    public DubboCommunicationEndpoint(int port){\n        this.port = port;\n    }\n\n    public void initial() {\n        // 构造对应的url， String.valueOf() 为避免数字包含千位符\n        String url = MessageFormat.format(DUBBO_SERVICE_URL, String.valueOf(port), String.valueOf(payload));\n        exporter = protocol.export(proxyFactory.getInvoker(this, CommunicationEndpoint.class, URL.valueOf(url)));\n    }\n\n    public void destory() {\n        exporter.unexport();\n    }\n\n    // =============== setter / gettter ==================\n\n    public void setPort(int port) {\n        this.port = port;\n    }\n\n    public void setPayload(int payload) {\n        this.payload = payload;\n    }\n\n}\n"
  },
  {
    "path": "shared/communication/src/main/java/com/alibaba/otter/shared/communication/core/impl/rmi/RmiCommunicationClientImpl.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.communication.core.impl.rmi;\n\nimport com.alibaba.otter.shared.communication.core.CommunicationClient;\nimport com.alibaba.otter.shared.communication.core.impl.DefaultCommunicationClientImpl;\nimport com.alibaba.otter.shared.communication.core.impl.connection.CommunicationConnectionFactory;\nimport com.alibaba.otter.shared.communication.core.impl.connection.CommunicationConnectionPoolFactory;\n\n/**\n * 基于rmi的通讯实现\n * \n * @author jianghang 2011-9-13 下午04:10:30\n */\npublic class RmiCommunicationClientImpl extends DefaultCommunicationClientImpl implements CommunicationClient {\n\n    // 是否使用链接池\n    private boolean poolable = true;\n\n    public void initial() {\n        CommunicationConnectionFactory factory = null;\n        if (poolable) {\n            factory = new CommunicationConnectionPoolFactory(new RmiCommunicationConnectionFactory());\n            ((CommunicationConnectionPoolFactory) factory).initial();\n        } else {\n            factory = new RmiCommunicationConnectionFactory();\n        }\n\n        super.setFactory(factory);\n    }\n\n    // ============================= setter / getter ==========================\n\n    public void setPoolable(boolean poolable) {\n        this.poolable = poolable;\n    }\n}\n"
  },
  {
    "path": "shared/communication/src/main/java/com/alibaba/otter/shared/communication/core/impl/rmi/RmiCommunicationConnection.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.communication.core.impl.rmi;\n\nimport com.alibaba.otter.shared.communication.core.CommunicationEndpoint;\nimport com.alibaba.otter.shared.communication.core.exception.CommunicationException;\nimport com.alibaba.otter.shared.communication.core.impl.connection.CommunicationConnection;\nimport com.alibaba.otter.shared.communication.core.model.CommunicationParam;\nimport com.alibaba.otter.shared.communication.core.model.Event;\n\n/**\n * 对应rmi的connection实现\n * \n * @author jianghang 2011-9-9 下午05:26:44\n */\npublic class RmiCommunicationConnection implements CommunicationConnection {\n\n    private CommunicationEndpoint endpoint;\n    private CommunicationParam    params;\n\n    public RmiCommunicationConnection(CommunicationParam params, CommunicationEndpoint endpoint){\n        this.params = params;\n        this.endpoint = endpoint;\n    }\n\n    public void close() throws CommunicationException {\n        // do nothing\n    }\n\n    public Object call(Event event) {\n        // 调用rmi传递数据到目标server上\n        return endpoint.acceptEvent(event);\n    }\n\n    @Override\n    public CommunicationParam getParams() {\n        return params;\n    }\n}\n"
  },
  {
    "path": "shared/communication/src/main/java/com/alibaba/otter/shared/communication/core/impl/rmi/RmiCommunicationConnectionFactory.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.communication.core.impl.rmi;\n\nimport java.text.MessageFormat;\n\nimport org.springframework.remoting.rmi.RmiProxyFactoryBean;\n\nimport com.alibaba.otter.shared.communication.core.CommunicationEndpoint;\nimport com.alibaba.otter.shared.communication.core.impl.connection.CommunicationConnection;\nimport com.alibaba.otter.shared.communication.core.impl.connection.CommunicationConnectionFactory;\nimport com.alibaba.otter.shared.communication.core.model.CommunicationParam;\n\n/**\n * 基于rmi的通讯链接实现\n * \n * @author jianghang 2011-9-9 下午04:58:28\n */\npublic class RmiCommunicationConnectionFactory implements CommunicationConnectionFactory {\n\n    static {\n        // 初始化rmi相关的参数\n        System.setProperty(\"sun.rmi.transport.connectTimeout\", \"30000\"); // 连接超时\n    }\n\n    private final String RMI_SERVICE_URL = \"rmi://{0}:{1}/endpoint\";\n\n    @Override\n    public CommunicationConnection createConnection(CommunicationParam params) {\n        if (params == null) {\n            throw new IllegalArgumentException(\"param is null!\");\n        }\n\n        // 构造对应的url\n        String serviceUrl = MessageFormat.format(RMI_SERVICE_URL, params.getIp(), String.valueOf(params.getPort()));\n        // 自己实现的有连接池的Stub\n        RmiProxyFactoryBean proxy = new RmiProxyFactoryBean();\n        proxy.setServiceUrl(serviceUrl);\n        proxy.setServiceInterface(CommunicationEndpoint.class);\n        proxy.afterPropertiesSet();\n        return new RmiCommunicationConnection(params, (CommunicationEndpoint) proxy.getObject());// 创建链接\n    }\n\n    @Override\n    public void releaseConnection(CommunicationConnection connection) {\n        // do nothing\n    }\n\n}\n"
  },
  {
    "path": "shared/communication/src/main/java/com/alibaba/otter/shared/communication/core/impl/rmi/RmiCommunicationEndpoint.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.communication.core.impl.rmi;\n\nimport java.rmi.RemoteException;\n\nimport org.springframework.remoting.rmi.RmiServiceExporter;\n\nimport com.alibaba.otter.shared.communication.core.CommunicationEndpoint;\nimport com.alibaba.otter.shared.communication.core.exception.CommunicationException;\nimport com.alibaba.otter.shared.communication.core.impl.AbstractCommunicationEndpoint;\n\n/**\n * 基于rmi的endpoint的实现，包装了一个rmi remote对象\n * \n * @author jianghang 2011-9-9 下午07:06:25\n */\npublic class RmiCommunicationEndpoint extends AbstractCommunicationEndpoint {\n\n    private String             host;\n    private int                port                 = 1099;\n    private RmiServiceExporter export;\n    private boolean            alwaysCreateRegistry = false;\n\n    public RmiCommunicationEndpoint(){\n    }\n\n    public RmiCommunicationEndpoint(int port){\n        this.port = port;\n        initial();\n    }\n\n    public void initial() {\n        export = new RmiServiceExporter();\n        export.setServiceName(\"endpoint\");\n        export.setService(this);// 暴露自己\n        export.setServiceInterface(CommunicationEndpoint.class);\n        export.setRegistryHost(host);\n        export.setRegistryPort(port);\n        export.setAlwaysCreateRegistry(alwaysCreateRegistry);// 强制创建一个\n\n        try {\n            export.afterPropertiesSet();\n        } catch (RemoteException e) {\n            throw new CommunicationException(\"Rmi_Create_Error\", e);\n        }\n\n    }\n\n    public void destory() {\n        try {\n            export.destroy();\n        } catch (RemoteException e) {\n            throw new CommunicationException(\"Rmi_Destory_Error\", e);\n        }\n    }\n\n    // =============== setter / gettter ==================\n    public void setAlwaysCreateRegistry(boolean alwaysCreateRegistry) {\n        this.alwaysCreateRegistry = alwaysCreateRegistry;\n    }\n\n    public void setPort(int port) {\n        this.port = port;\n    }\n\n    public void setHost(String host) {\n        this.host = host;\n    }\n\n}\n"
  },
  {
    "path": "shared/communication/src/main/java/com/alibaba/otter/shared/communication/core/model/Callback.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.communication.core.model;\n\n/**\n * 通讯的异步callback回调接口\n * \n * @author jianghang 2011-9-9 下午04:16:04\n */\npublic interface Callback<PARAM> {\n\n    public void call(PARAM event);\n}\n"
  },
  {
    "path": "shared/communication/src/main/java/com/alibaba/otter/shared/communication/core/model/CommunicationParam.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.communication.core.model;\n\n/**\n * 连接参数类，<strong>如果参数有变化，需要酌情考虑是否更新相应的hashcode & equals方法</strong>\n * \n * @author jianghang 2011-9-9 下午07:16:59\n */\npublic class CommunicationParam {\n\n    private String              ip;                                 // 通讯ip\n    private int                 port;                               // 通讯端口\n    private CummunicationMethod comMethod = CummunicationMethod.RMI; // 通讯方式\n\n    /**\n     * 远程通讯方式\n     */\n    private static enum CummunicationMethod {\n        RMI;\n    }\n\n    // ================ setter / getter ====================\n\n    public CummunicationMethod getComMethod() {\n        return comMethod;\n    }\n\n    public void setComMethod(String comMethod) {\n        CummunicationMethod.valueOf(comMethod);\n    }\n\n    public void setComMethod(CummunicationMethod comMethod) {\n        this.comMethod = comMethod;\n    }\n\n    public String getIp() {\n        return ip;\n    }\n\n    public void setIp(String ip) {\n        this.ip = ip;\n    }\n\n    public int getPort() {\n        return port;\n    }\n\n    public void setPort(int port) {\n        this.port = port;\n    }\n\n    // ==================== hashcode & equals ===================\n\n    public int hashCode() {\n        final int prime = 31;\n        int result = 1;\n        result = prime * result + ((comMethod == null) ? 0 : comMethod.hashCode());\n        result = prime * result + ((ip == null) ? 0 : ip.hashCode());\n        result = prime * result + port;\n        return result;\n    }\n\n    public boolean equals(Object obj) {\n        if (this == obj) {\n            return true;\n        }\n        if (obj == null) {\n            return false;\n        }\n        if (!(obj instanceof CommunicationParam)) {\n            return false;\n        }\n        CommunicationParam other = (CommunicationParam) obj;\n        if (comMethod == null) {\n            if (other.comMethod != null) {\n                return false;\n            }\n        } else if (!comMethod.equals(other.comMethod)) {\n            return false;\n        }\n        if (ip == null) {\n            if (other.ip != null) {\n                return false;\n            }\n        } else if (!ip.equals(other.ip)) {\n            return false;\n        }\n        if (port != other.port) {\n            return false;\n        }\n        return true;\n    }\n\n}\n"
  },
  {
    "path": "shared/communication/src/main/java/com/alibaba/otter/shared/communication/core/model/Event.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.communication.core.model;\n\nimport java.io.Serializable;\n\nimport org.apache.commons.lang.builder.ToStringBuilder;\n\nimport com.alibaba.otter.shared.common.utils.OtterToStringStyle;\n\n/**\n * 通讯事件对象\n * \n * @author jianghang 2011-9-9 下午04:02:53\n */\npublic abstract class Event implements Serializable {\n\n    private static final long serialVersionUID = 208038167977229245L;\n\n    private EventType         type;\n\n    protected Event(){\n    }\n\n    protected Event(EventType type){\n        this.type = type;\n    }\n\n    public EventType getType() {\n        return type;\n    }\n\n    @Override\n    public String toString() {\n        return ToStringBuilder.reflectionToString(this, OtterToStringStyle.DEFAULT_STYLE);\n    }\n}\n"
  },
  {
    "path": "shared/communication/src/main/java/com/alibaba/otter/shared/communication/core/model/EventType.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.communication.core.model;\n\n/**\n * 通讯事件类型\n * \n * @author jianghang 2011-9-9 下午04:02:13\n */\npublic interface EventType {\n\n    public String name();\n}\n"
  },
  {
    "path": "shared/communication/src/main/java/com/alibaba/otter/shared/communication/core/model/heart/HeartEvent.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.communication.core.model.heart;\n\nimport com.alibaba.otter.shared.communication.core.model.Event;\nimport com.alibaba.otter.shared.communication.core.model.EventType;\n\n/**\n * 心跳检查事件\n * \n * @author jianghang\n */\npublic class HeartEvent extends Event {\n\n    private static final long serialVersionUID = 8690886624112649424L;\n\n    public HeartEvent(){\n        super(HeartEventType.HEARTBEAT);\n    }\n\n    private Byte heart = 1;\n\n    public static enum HeartEventType implements EventType {\n        HEARTBEAT;\n    }\n\n    public Byte getHeart() {\n        return heart;\n    }\n\n    public void setHeart(Byte heart) {\n        this.heart = heart;\n    }\n\n}\n"
  },
  {
    "path": "shared/communication/src/main/java/com/alibaba/otter/shared/communication/model/OtterRemoteException.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.communication.model;\n\nimport org.apache.commons.lang.exception.NestableRuntimeException;\n\n/**\n * otter remote操作的统一传输异常对象\n * \n * @author jianghang 2011-11-28 下午02:17:00\n * @version 4.0.0\n */\npublic class OtterRemoteException extends NestableRuntimeException {\n\n    private static final long serialVersionUID = -7288830284122672209L;\n\n    private String            errorCode;\n    private String            errorDesc;\n\n    public OtterRemoteException(String errorCode){\n        super(errorCode);\n    }\n\n    public OtterRemoteException(String errorCode, Throwable cause){\n        super(errorCode, cause);\n    }\n\n    public OtterRemoteException(String errorCode, String errorDesc){\n        super(errorCode + \":\" + errorDesc);\n    }\n\n    public OtterRemoteException(String errorCode, String errorDesc, Throwable cause){\n        super(errorCode + \":\" + errorDesc, cause);\n    }\n\n    public OtterRemoteException(Throwable cause){\n        super(cause);\n    }\n\n    public String getErrorCode() {\n        return errorCode;\n    }\n\n    public String getErrorDesc() {\n        return errorDesc;\n    }\n\n    @Override\n    public Throwable fillInStackTrace() {\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "shared/communication/src/main/java/com/alibaba/otter/shared/communication/model/arbitrate/ArbitrateEventType.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.communication.model.arbitrate;\n\nimport com.alibaba.otter.shared.communication.core.model.EventType;\n\npublic enum ArbitrateEventType implements EventType {\n\n    /** 通知manager关闭 */\n    stopChannel,\n    /** 报警信息 */\n    nodeAlarm,\n    /** 通知manager node需要关闭 */\n    stopNode,\n    /** stage调度通知 */\n    stageSingle,\n    /** fast stage调度通知 */\n    fastStageSingle;\n}\n"
  },
  {
    "path": "shared/communication/src/main/java/com/alibaba/otter/shared/communication/model/arbitrate/NodeAlarmEvent.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.communication.model.arbitrate;\n\nimport com.alibaba.otter.shared.communication.core.model.Event;\n\npublic class NodeAlarmEvent extends Event {\n\n    private static final long serialVersionUID = 476657754177940448L;\n\n    private Long              nid;                                   // 发送报警的node机器id\n    private Long              pipelineId;                            // 对应出错的pipelineId\n    private String            title;\n    private String            message;\n\n    public NodeAlarmEvent(){\n        super(ArbitrateEventType.nodeAlarm);\n    }\n\n    public Long getNid() {\n        return nid;\n    }\n\n    public void setNid(Long nid) {\n        this.nid = nid;\n    }\n\n    public Long getPipelineId() {\n        return pipelineId;\n    }\n\n    public void setPipelineId(Long pipelineId) {\n        this.pipelineId = pipelineId;\n    }\n\n    public String getTitle() {\n        return title;\n    }\n\n    public void setTitle(String title) {\n        this.title = title;\n    }\n\n    public String getMessage() {\n        return message;\n    }\n\n    public void setMessage(String message) {\n        this.message = message;\n    }\n\n}\n"
  },
  {
    "path": "shared/communication/src/main/java/com/alibaba/otter/shared/communication/model/arbitrate/StageSingleEvent.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.communication.model.arbitrate;\n\nimport com.alibaba.otter.shared.common.model.config.enums.StageType;\nimport com.alibaba.otter.shared.communication.core.model.Event;\nimport com.alibaba.otter.shared.communication.core.model.EventType;\n\n/**\n * 基于rpc实现仲裁器调度的通知信号对象\n * \n * @author jianghang 2012-9-28 下午10:21:03\n * @version 4.1.0\n */\npublic class StageSingleEvent extends Event {\n\n    private static final long serialVersionUID = 476657754177940448L;\n\n    private Long              pipelineId;\n    private StageType         stage;\n    // 对应的对象类型为EtlEventData，因为依赖关系的问题，不能直接使用具体类,由序列化方式保证可以拿到具体的子类\n    private Object            data;\n\n    public StageSingleEvent(EventType type){\n        super(type);\n    }\n\n    public StageType getStage() {\n        return stage;\n    }\n\n    public void setStage(StageType stage) {\n        this.stage = stage;\n    }\n\n    public Object getData() {\n        return data;\n    }\n\n    public void setData(Object data) {\n        this.data = data;\n    }\n\n    public Long getPipelineId() {\n        return pipelineId;\n    }\n\n    public void setPipelineId(Long pipelineId) {\n        this.pipelineId = pipelineId;\n    }\n\n}\n"
  },
  {
    "path": "shared/communication/src/main/java/com/alibaba/otter/shared/communication/model/arbitrate/StopChannelEvent.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.communication.model.arbitrate;\n\nimport com.alibaba.otter.shared.communication.core.model.Event;\n\npublic class StopChannelEvent extends Event {\n\n    private static final long serialVersionUID = 476657754177940448L;\n\n    private Long              channelId;                             // 对应的channelId\n\n    public StopChannelEvent(){\n        super(ArbitrateEventType.stopChannel);\n    }\n\n    public Long getChannelId() {\n        return channelId;\n    }\n\n    public void setChannelId(Long channelId) {\n        this.channelId = channelId;\n    }\n\n}\n"
  },
  {
    "path": "shared/communication/src/main/java/com/alibaba/otter/shared/communication/model/arbitrate/StopNodeEvent.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.communication.model.arbitrate;\n\nimport com.alibaba.otter.shared.communication.core.model.Event;\n\n/**\n * node关闭的信号通知\n * \n * @author jianghang 2012-8-29 下午02:03:15\n * @version 4.1.0\n */\npublic class StopNodeEvent extends Event {\n\n    private static final long serialVersionUID = -8472088519060045661L;\n\n    public StopNodeEvent(){\n        super(ArbitrateEventType.stopNode);\n    }\n\n    private Long nid;\n\n    public Long getNid() {\n        return nid;\n    }\n\n    public void setNid(Long nid) {\n        this.nid = nid;\n    }\n\n}\n"
  },
  {
    "path": "shared/communication/src/main/java/com/alibaba/otter/shared/communication/model/canal/CanalEventType.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.communication.model.canal;\n\nimport com.alibaba.otter.shared.communication.core.model.EventType;\n\n/**\n * config交互的事件类型\n * \n * @author jianghang\n */\npublic enum CanalEventType implements EventType {\n\n    /** 查询对应canal信息 */\n    findCanal,\n    /** 查询对应的过滤条件 */\n    findFilter;\n}\n"
  },
  {
    "path": "shared/communication/src/main/java/com/alibaba/otter/shared/communication/model/canal/FindCanalEvent.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.communication.model.canal;\n\nimport com.alibaba.otter.shared.communication.core.model.Event;\n\n/**\n * 配置查询的事件\n * \n * @author jianghang\n */\npublic class FindCanalEvent extends Event {\n\n    private static final long serialVersionUID = 476657754177940448L;\n\n    private String            destination;\n\n    public FindCanalEvent(){\n        super(CanalEventType.findCanal);\n    }\n\n    public String getDestination() {\n        return destination;\n    }\n\n    public void setDestination(String destination) {\n        this.destination = destination;\n    }\n\n}\n"
  },
  {
    "path": "shared/communication/src/main/java/com/alibaba/otter/shared/communication/model/canal/FindFilterEvent.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.communication.model.canal;\n\nimport com.alibaba.otter.shared.communication.core.model.Event;\n\n/**\n * 查询对应的过滤条件\n * \n * @author jianghang 2012-7-23 下午02:41:52\n */\npublic class FindFilterEvent extends Event {\n\n    private static final long serialVersionUID = 476657754177940448L;\n\n    private String            destination;\n\n    public FindFilterEvent(){\n        super(CanalEventType.findFilter);\n    }\n\n    public String getDestination() {\n        return destination;\n    }\n\n    public void setDestination(String destination) {\n        this.destination = destination;\n    }\n\n}\n"
  },
  {
    "path": "shared/communication/src/main/java/com/alibaba/otter/shared/communication/model/config/ConfigEventType.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.communication.model.config;\n\nimport com.alibaba.otter.shared.communication.core.model.EventType;\n\n/**\n * config交互的事件类型\n * \n * @author jianghang\n */\npublic enum ConfigEventType implements EventType {\n\n    /** 查询nid对应的任务列表 */\n    findTask,\n    /** 根据nid查询Node对象 */\n    findNode,\n    /** 根据id查询对应的channel对象 */\n    findChannel,\n    /** manager通知task channel的变化 */\n    notifyChannel,\n    /** 查询media信息 */\n    findMedia,\n    /** 通知medai信息 */\n    notifyMedia;\n\n}\n"
  },
  {
    "path": "shared/communication/src/main/java/com/alibaba/otter/shared/communication/model/config/FindChannelEvent.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.communication.model.config;\n\nimport com.alibaba.otter.shared.communication.core.model.Event;\n\n/**\n * 配置查询的事件\n * \n * @author jianghang\n */\npublic class FindChannelEvent extends Event {\n\n    private static final long serialVersionUID = 476657754177940448L;\n\n    private Long              channelId;                             // 对应的channelId, 可能为空\n    private Long              pipelineId;                            // 对应的pipelineId, 可能为空\n\n    public FindChannelEvent(){\n        super(ConfigEventType.findChannel);\n    }\n\n    public Long getPipelineId() {\n        return pipelineId;\n    }\n\n    public void setPipelineId(Long pipelineId) {\n        this.pipelineId = pipelineId;\n    }\n\n    public Long getChannelId() {\n        return channelId;\n    }\n\n    public void setChannelId(Long channelId) {\n        this.channelId = channelId;\n    }\n\n}\n"
  },
  {
    "path": "shared/communication/src/main/java/com/alibaba/otter/shared/communication/model/config/FindMediaEvent.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.communication.model.config;\n\nimport com.alibaba.otter.shared.communication.core.model.Event;\n\npublic class FindMediaEvent extends Event {\n\n    private static final long serialVersionUID = 476657754177940448L;\n\n    private String            dataId;\n\n    public FindMediaEvent(){\n        super(ConfigEventType.findMedia);\n    }\n\n    public String getDataId() {\n        return dataId;\n    }\n\n    public void setDataId(String dataId) {\n        this.dataId = dataId;\n    }\n\n}\n"
  },
  {
    "path": "shared/communication/src/main/java/com/alibaba/otter/shared/communication/model/config/FindNodeEvent.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.communication.model.config;\n\nimport com.alibaba.otter.shared.communication.core.model.Event;\n\npublic class FindNodeEvent extends Event {\n\n    private static final long serialVersionUID = 476657754177940448L;\n\n    private Long              nid;                                   // 对应的task机器id\n\n    public FindNodeEvent(){\n        super(ConfigEventType.findNode);\n    }\n\n    public Long getNid() {\n        return nid;\n    }\n\n    public void setNid(Long nid) {\n        this.nid = nid;\n    }\n\n}\n"
  },
  {
    "path": "shared/communication/src/main/java/com/alibaba/otter/shared/communication/model/config/FindTaskEvent.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.communication.model.config;\n\nimport com.alibaba.otter.shared.communication.core.model.Event;\n\n/**\n * 配置查询的事件\n * \n * @author jianghang\n */\npublic class FindTaskEvent extends Event {\n\n    private static final long serialVersionUID = 476657754177940448L;\n\n    private Long              nid;                                   // 对应的task机器id\n\n    public FindTaskEvent(){\n        super(ConfigEventType.findTask);\n    }\n\n    public Long getNid() {\n        return nid;\n    }\n\n    public void setNid(Long nid) {\n        this.nid = nid;\n    }\n\n}\n"
  },
  {
    "path": "shared/communication/src/main/java/com/alibaba/otter/shared/communication/model/config/NotifyChannelEvent.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.communication.model.config;\n\nimport com.alibaba.otter.shared.communication.core.model.Event;\nimport com.alibaba.otter.shared.common.model.config.channel.Channel;\n\n/**\n * config变更通知的事件\n * \n * @author jianghang\n */\npublic class NotifyChannelEvent extends Event {\n\n    private static final long serialVersionUID = -8472088519060045661L;\n\n    public NotifyChannelEvent(){\n        super(ConfigEventType.notifyChannel);\n    }\n\n    private Channel channel;\n\n    public Channel getChannel() {\n        return channel;\n    }\n\n    public void setChannel(Channel channel) {\n        this.channel = channel;\n    }\n\n}\n"
  },
  {
    "path": "shared/communication/src/main/java/com/alibaba/otter/shared/communication/model/config/NotifyMediaEvent.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.communication.model.config;\n\nimport com.alibaba.otter.shared.communication.core.model.Event;\n\npublic class NotifyMediaEvent extends Event {\n\n    private static final long serialVersionUID = -8472088519060045661L;\n\n    public NotifyMediaEvent(){\n        super(ConfigEventType.notifyMedia);\n    }\n\n    private String mediaInfo;\n\n    public String getMediaInfo() {\n        return mediaInfo;\n    }\n\n    public void setMediaInfo(String mediaInfo) {\n        this.mediaInfo = mediaInfo;\n    }\n\n}\n"
  },
  {
    "path": "shared/communication/src/main/java/com/alibaba/otter/shared/communication/model/statistics/DelayCountEvent.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.communication.model.statistics;\n\nimport com.alibaba.otter.shared.common.model.statistics.delay.DelayCount;\nimport com.alibaba.otter.shared.communication.core.model.Event;\n\n/**\n * delay queue事件\n * \n * @author jianghang\n */\npublic class DelayCountEvent extends Event {\n\n    private static final long serialVersionUID = -5925977847006864387L;\n\n    public DelayCountEvent(){\n        super(StatisticsEventType.delayCount);\n    }\n\n    public static enum Action {\n        INC, DEC, RESET;\n\n        public boolean isInc() {\n            return this == INC;\n        }\n\n        public boolean isDec() {\n            return this == DEC;\n        }\n\n        public boolean isReset() {\n            return this == RESET;\n        }\n    }\n\n    private DelayCount count;\n\n    private Action     action;\n\n    public DelayCount getCount() {\n        return count;\n    }\n\n    public void setCount(DelayCount count) {\n        this.count = count;\n    }\n\n    public Action getAction() {\n        return action;\n    }\n\n    public void setAction(Action action) {\n        this.action = action;\n    }\n\n}\n"
  },
  {
    "path": "shared/communication/src/main/java/com/alibaba/otter/shared/communication/model/statistics/StatisticsEventType.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.communication.model.statistics;\n\nimport com.alibaba.otter.shared.communication.core.model.EventType;\n\n/**\n * 统计数据的事件类型\n * \n * @author jianghang\n */\npublic enum StatisticsEventType implements EventType {\n    /** delayCount */\n    delayCount,\n    /** tableStat */\n    tableStat,\n    /** throughputStat */\n    throughputStat;\n}\n"
  },
  {
    "path": "shared/communication/src/main/java/com/alibaba/otter/shared/communication/model/statistics/TableStatEvent.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.communication.model.statistics;\n\nimport java.util.List;\n\nimport com.alibaba.otter.shared.common.model.statistics.table.TableStat;\nimport com.alibaba.otter.shared.communication.core.model.Event;\n\n/**\n * table stat事件\n * \n * @author jianghang\n */\npublic class TableStatEvent extends Event {\n\n    private static final long serialVersionUID = -5925977847006864387L;\n\n    public TableStatEvent(){\n        super(StatisticsEventType.tableStat);\n\n    }\n\n    private List<TableStat> stats;\n\n    public List<TableStat> getStats() {\n        return stats;\n    }\n\n    public void setStats(List<TableStat> stats) {\n        this.stats = stats;\n    }\n\n}\n"
  },
  {
    "path": "shared/communication/src/main/java/com/alibaba/otter/shared/communication/model/statistics/ThroughputStatEvent.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.communication.model.statistics;\n\nimport java.util.List;\n\nimport com.alibaba.otter.shared.common.model.statistics.throughput.ThroughputStat;\nimport com.alibaba.otter.shared.communication.core.model.Event;\n\n/**\n * 吞吐量事件\n * \n * @author jianghang\n */\npublic class ThroughputStatEvent extends Event {\n\n    private static final long serialVersionUID = 3626191138534384067L;\n\n    public ThroughputStatEvent(){\n        super(StatisticsEventType.throughputStat);\n    }\n\n    private List<ThroughputStat> stats;\n\n    public List<ThroughputStat> getStats() {\n        return stats;\n    }\n\n    public void setStats(List<ThroughputStat> stats) {\n        this.stats = stats;\n    }\n\n}\n"
  },
  {
    "path": "shared/communication/src/test/java/com/alibaba/otter/shared/communication/BaseOtterTest.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.communication;\n\nimport org.jtester.annotations.SpringApplicationContext;\n\n/**\n * @author jianghang 2010-6-2 上午11:48:00\n */\n@SpringApplicationContext(\"applicationContext.xml\")\npublic class BaseOtterTest extends org.jtester.testng.JTester {\n\n}\n"
  },
  {
    "path": "shared/communication/src/test/java/com/alibaba/otter/shared/communication/CommunicationSpringIntegrateTest.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.communication;\n\nimport org.jtester.annotations.SpringBeanByName;\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.shared.communication.core.CommunicationClient;\nimport com.alibaba.otter.shared.communication.core.model.heart.HeartEvent;\n\n/**\n * @author jianghang\n */\npublic class CommunicationSpringIntegrateTest extends BaseOtterTest {\n\n    @SpringBeanByName\n    private CommunicationClient dubboCommunicationClient;\n\n    @SpringBeanByName\n    private CommunicationClient rmiPoolCommunicationClient;\n\n    @SpringBeanByName\n    private CommunicationClient rmiCommunicationClient;\n\n    @Test\n    public void testRmiSingle() {\n        Object result = rmiCommunicationClient.call(\"127.0.0.1:1099\", new HeartEvent());\n        want.object(result).notNull();\n    }\n\n    @Test\n    public void testRmiPool() {\n        Object result = rmiPoolCommunicationClient.call(\"127.0.0.1:1099\", new HeartEvent());\n        want.object(result).notNull();\n    }\n\n    @Test\n    public void testDubboSingle() {\n        Object result = dubboCommunicationClient.call(\"127.0.0.1:2088\", new HeartEvent());\n        want.object(result).notNull();\n    }\n\n}\n"
  },
  {
    "path": "shared/communication/src/test/java/com/alibaba/otter/shared/communication/app/CommunicationAppService.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.communication.app;\n\nimport com.alibaba.otter.shared.communication.app.event.AppCreateEvent;\nimport com.alibaba.otter.shared.communication.app.event.AppDeleteEvent;\nimport com.alibaba.otter.shared.communication.app.event.AppUpdateEvent;\nimport com.alibaba.otter.shared.communication.core.model.Event;\n\n/**\n * @author jianghang 2011-9-13 下午08:30:48\n */\npublic interface CommunicationAppService {\n\n    public boolean onCreate(AppCreateEvent event);\n\n    public boolean onUpdate(AppUpdateEvent event);\n\n    public boolean onDelete(AppDeleteEvent event);\n\n    public Event handleEvent(Event event);\n}\n"
  },
  {
    "path": "shared/communication/src/test/java/com/alibaba/otter/shared/communication/app/CommunicationAppServiceImpl.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.communication.app;\n\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\n\nimport com.alibaba.otter.shared.communication.app.event.AppCreateEvent;\nimport com.alibaba.otter.shared.communication.app.event.AppDeleteEvent;\nimport com.alibaba.otter.shared.communication.app.event.AppUpdateEvent;\nimport com.alibaba.otter.shared.communication.core.model.Event;\n\n/**\n * @author jianghang 2011-9-13 下午08:36:01\n */\npublic class CommunicationAppServiceImpl implements CommunicationAppService {\n\n    private Map<String, Event> events = new ConcurrentHashMap<String, Event>();\n\n    public Event handleEvent(Event event) {\n        throw new IllegalArgumentException();\n    }\n\n    public boolean onCreate(AppCreateEvent event) {\n        events.put(event.getType().name(), event);\n        return true;\n    }\n\n    public boolean onDelete(AppDeleteEvent event) {\n        events.remove(event.getType().name());\n        return true;\n    }\n\n    public boolean onUpdate(AppUpdateEvent event) {\n        events.put(event.getType().name(), event);\n        return true;\n    }\n\n}\n"
  },
  {
    "path": "shared/communication/src/test/java/com/alibaba/otter/shared/communication/app/event/AppCreateEvent.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.communication.app.event;\n\nimport com.alibaba.otter.shared.communication.core.model.Event;\n\n/**\n * @author jianghang 2011-9-13 下午08:31:36\n */\npublic class AppCreateEvent extends Event {\n\n    private static final long serialVersionUID = 810191575813164952L;\n\n    public AppCreateEvent(){\n        super(AppEventType.create);\n    }\n\n    private String    name;\n    private int       intValue;\n    private boolean   boolValue;\n    private float     floatValue;\n    private double    doubleValue;\n    private long      longValue;\n    private char      charValue;\n    private byte      byteValue;\n    private short     shortValue;\n    private Integer   integerValue;\n    private Boolean   boolObjValue;\n    private Float     floatObjValue;\n    private Double    doubleObjValue;\n    private Long      longObjValue;\n    private Character characterValue;\n    private Short     shortObjValue;\n    private Byte      byteObjValue;\n\n    public String getName() {\n        return name;\n    }\n\n    public void setName(String name) {\n        this.name = name;\n    }\n\n    public int getIntValue() {\n        return intValue;\n    }\n\n    public void setIntValue(int intValue) {\n        this.intValue = intValue;\n    }\n\n    public boolean isBoolValue() {\n        return boolValue;\n    }\n\n    public void setBoolValue(boolean boolValue) {\n        this.boolValue = boolValue;\n    }\n\n    public float getFloatValue() {\n        return floatValue;\n    }\n\n    public void setFloatValue(float floatValue) {\n        this.floatValue = floatValue;\n    }\n\n    public double getDoubleValue() {\n        return doubleValue;\n    }\n\n    public void setDoubleValue(double doubleValue) {\n        this.doubleValue = doubleValue;\n    }\n\n    public long getLongValue() {\n        return longValue;\n    }\n\n    public void setLongValue(long longValue) {\n        this.longValue = longValue;\n    }\n\n    public char getCharValue() {\n        return charValue;\n    }\n\n    public void setCharValue(char charValue) {\n        this.charValue = charValue;\n    }\n\n    public byte getByteValue() {\n        return byteValue;\n    }\n\n    public void setByteValue(byte byteValue) {\n        this.byteValue = byteValue;\n    }\n\n    public short getShortValue() {\n        return shortValue;\n    }\n\n    public void setShortValue(short shortValue) {\n        this.shortValue = shortValue;\n    }\n\n    public Integer getIntegerValue() {\n        return integerValue;\n    }\n\n    public void setIntegerValue(Integer integerValue) {\n        this.integerValue = integerValue;\n    }\n\n    public Boolean getBoolObjValue() {\n        return boolObjValue;\n    }\n\n    public void setBoolObjValue(Boolean boolObjValue) {\n        this.boolObjValue = boolObjValue;\n    }\n\n    public Float getFloatObjValue() {\n        return floatObjValue;\n    }\n\n    public void setFloatObjValue(Float floatObjValue) {\n        this.floatObjValue = floatObjValue;\n    }\n\n    public Double getDoubleObjValue() {\n        return doubleObjValue;\n    }\n\n    public void setDoubleObjValue(Double doubleObjValue) {\n        this.doubleObjValue = doubleObjValue;\n    }\n\n    public Long getLongObjValue() {\n        return longObjValue;\n    }\n\n    public void setLongObjValue(Long longObjValue) {\n        this.longObjValue = longObjValue;\n    }\n\n    public Character getCharacterValue() {\n        return characterValue;\n    }\n\n    public void setCharacterValue(Character characterValue) {\n        this.characterValue = characterValue;\n    }\n\n    public Short getShortObjValue() {\n        return shortObjValue;\n    }\n\n    public void setShortObjValue(Short shortObjValue) {\n        this.shortObjValue = shortObjValue;\n    }\n\n    public Byte getByteObjValue() {\n        return byteObjValue;\n    }\n\n    public void setByteObjValue(Byte byteObjValue) {\n        this.byteObjValue = byteObjValue;\n    }\n\n}\n"
  },
  {
    "path": "shared/communication/src/test/java/com/alibaba/otter/shared/communication/app/event/AppDeleteEvent.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.communication.app.event;\n\nimport com.alibaba.otter.shared.communication.core.model.Event;\n\n/**\n * @author jianghang 2011-9-13 下午08:31:36\n */\npublic class AppDeleteEvent extends Event {\n\n    private static final long serialVersionUID = 810191575813164952L;\n\n    public AppDeleteEvent(){\n        super(AppEventType.create);\n    }\n\n    public String name;\n\n    public String getName() {\n        return name;\n    }\n\n    public void setName(String name) {\n        this.name = name;\n    }\n\n}\n"
  },
  {
    "path": "shared/communication/src/test/java/com/alibaba/otter/shared/communication/app/event/AppEventType.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.communication.app.event;\n\nimport com.alibaba.otter.shared.communication.core.model.EventType;\n\n/**\n * @author jianghang 2011-9-13 下午08:28:50\n */\npublic enum AppEventType implements EventType {\n    create, update, delete, find;\n}\n"
  },
  {
    "path": "shared/communication/src/test/java/com/alibaba/otter/shared/communication/app/event/AppFindEvent.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.communication.app.event;\n\nimport com.alibaba.otter.shared.communication.core.model.Event;\n\n/**\n * @author jianghang 2011-9-13 下午08:31:36\n */\npublic class AppFindEvent extends Event {\n\n    private static final long serialVersionUID = 810191575813164952L;\n\n    public AppFindEvent(){\n        super(AppEventType.find);\n    }\n\n    public String name;\n\n    public String getName() {\n        return name;\n    }\n\n    public void setName(String name) {\n        this.name = name;\n    }\n\n}\n"
  },
  {
    "path": "shared/communication/src/test/java/com/alibaba/otter/shared/communication/app/event/AppUpdateEvent.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.communication.app.event;\n\nimport java.io.Serializable;\nimport java.math.BigDecimal;\nimport java.math.BigInteger;\n\nimport com.alibaba.otter.shared.communication.core.model.Event;\n\n/**\n * @author jianghang 2011-9-13 下午08:31:36\n */\npublic class AppUpdateEvent extends Event {\n\n    private static final long serialVersionUID = 810191575813164952L;\n\n    public AppUpdateEvent(){\n        super(AppEventType.update);\n    }\n\n    private String     name;\n    private BigInteger bigIntegerValue;\n    private BigDecimal bigDecimalValue;\n    private UpdateData data;\n\n    public static class UpdateData implements Serializable {\n\n        private static final long serialVersionUID = -2591770066519646446L;\n        private String            name;\n        private BigInteger        bigIntegerValue;\n        private BigDecimal        bigDecimalValue;\n\n        public String getName() {\n            return name;\n        }\n\n        public void setName(String name) {\n            this.name = name;\n        }\n\n        public BigInteger getBigIntegerValue() {\n            return bigIntegerValue;\n        }\n\n        public void setBigIntegerValue(BigInteger bigIntegerValue) {\n            this.bigIntegerValue = bigIntegerValue;\n        }\n\n        public BigDecimal getBigDecimalValue() {\n            return bigDecimalValue;\n        }\n\n        public void setBigDecimalValue(BigDecimal bigDecimalValue) {\n            this.bigDecimalValue = bigDecimalValue;\n        }\n\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public void setName(String name) {\n        this.name = name;\n    }\n\n    public BigInteger getBigIntegerValue() {\n        return bigIntegerValue;\n    }\n\n    public void setBigIntegerValue(BigInteger bigIntegerValue) {\n        this.bigIntegerValue = bigIntegerValue;\n    }\n\n    public BigDecimal getBigDecimalValue() {\n        return bigDecimalValue;\n    }\n\n    public void setBigDecimalValue(BigDecimal bigDecimalValue) {\n        this.bigDecimalValue = bigDecimalValue;\n    }\n\n    public UpdateData getData() {\n        return data;\n    }\n\n    public void setData(UpdateData data) {\n        this.data = data;\n    }\n\n}\n"
  },
  {
    "path": "shared/communication/src/test/java/com/alibaba/otter/shared/communication/dubbo/DubboCommunicationTest.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.communication.dubbo;\n\nimport java.math.BigDecimal;\nimport java.math.BigInteger;\nimport java.util.List;\n\nimport org.testng.annotations.BeforeClass;\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.shared.communication.app.CommunicationAppService;\nimport com.alibaba.otter.shared.communication.app.CommunicationAppServiceImpl;\nimport com.alibaba.otter.shared.communication.app.event.AppCreateEvent;\nimport com.alibaba.otter.shared.communication.app.event.AppDeleteEvent;\nimport com.alibaba.otter.shared.communication.app.event.AppEventType;\nimport com.alibaba.otter.shared.communication.app.event.AppFindEvent;\nimport com.alibaba.otter.shared.communication.app.event.AppUpdateEvent;\nimport com.alibaba.otter.shared.communication.app.event.AppUpdateEvent.UpdateData;\nimport com.alibaba.otter.shared.communication.core.CommunicationClient;\nimport com.alibaba.otter.shared.communication.core.CommunicationRegistry;\nimport com.alibaba.otter.shared.communication.core.exception.CommunicationException;\nimport com.alibaba.otter.shared.communication.core.impl.DefaultCommunicationClientImpl;\nimport com.alibaba.otter.shared.communication.core.impl.connection.CommunicationConnectionFactory;\nimport com.alibaba.otter.shared.communication.core.impl.dubbo.DubboCommunicationConnectionFactory;\nimport com.alibaba.otter.shared.communication.core.impl.dubbo.DubboCommunicationEndpoint;\nimport com.alibaba.otter.shared.communication.core.model.Callback;\n\n/**\n * 测试下基于rmi通讯的功能\n * \n * @author jianghang 2011-9-13 下午04:03:38\n */\npublic class DubboCommunicationTest extends org.jtester.testng.JTester {\n\n    private CommunicationClient     client  = null;\n    private CommunicationAppService service = new CommunicationAppServiceImpl();\n\n    @BeforeClass\n    public void initial() {\n        DubboCommunicationEndpoint endpoint2088 = new DubboCommunicationEndpoint(2088);\n        endpoint2088.initial();\n\n        DubboCommunicationEndpoint endpoint2089 = new DubboCommunicationEndpoint(2089);\n        endpoint2089.initial();\n\n        CommunicationConnectionFactory factory = new DubboCommunicationConnectionFactory();\n        client = new DefaultCommunicationClientImpl(factory);\n        client.initial();\n    }\n\n    @Test\n    public void testRmi_createEvent() {// 测试基本类型的序列化\n        CommunicationRegistry.regist(AppEventType.create, service);\n        AppCreateEvent event = new AppCreateEvent();\n        event.setName(\"rmiEvent\");\n        event.setIntValue(1);\n        event.setBoolValue(false);\n        event.setFloatValue(1.0f);\n        event.setDoubleValue(1.0d);\n        event.setLongValue(1l);\n        event.setCharValue('a');\n        event.setShortValue((short) 1);\n        event.setByteValue((byte) 1);\n        event.setIntegerValue(new Integer(\"1\"));\n        event.setBoolObjValue(new Boolean(\"false\"));\n        event.setFloatObjValue(new Float(\"1.0\"));\n        event.setDoubleObjValue(new Double(\"1.0\"));\n        event.setLongObjValue(new Long(\"1\"));\n        event.setCharacterValue('a');\n        event.setShortObjValue(new Short(\"1\"));\n        event.setByteObjValue(new Byte(\"1\"));\n\n        Object result = client.call(\"127.0.0.1:2088\", event);// 同步调用\n        want.bool((Boolean) result).is(true);\n    }\n\n    @Test\n    public void testRmi_updateEvent() { // 测试复合对象的序列化\n        CommunicationRegistry.regist(AppEventType.update, service);\n        AppUpdateEvent event = new AppUpdateEvent();\n        event.setName(\"rmiEvent\");\n        event.setBigDecimalValue(BigDecimal.TEN);\n        event.setBigIntegerValue(BigInteger.TEN);\n        UpdateData data = new UpdateData();\n        data.setName(\"data\");\n        data.setBigDecimalValue(BigDecimal.TEN);\n        data.setBigIntegerValue(BigInteger.TEN);\n        event.setData(data);\n        List<Boolean> result = (List<Boolean>) client.call(new String[] { \"127.0.0.1:2088\", \"127.0.0.1:2089\" }, event);// 同步调用\n\n        want.number(result.size()).isEqualTo(2);\n        want.bool(result.get(0)).is(true);\n        want.bool(result.get(1)).is(true);\n\n    }\n\n    @Test\n    public void testRmi_deleteEvent() { // 测试异步调用+多节点\n        CommunicationRegistry.regist(AppEventType.update, service);\n        AppDeleteEvent event = new AppDeleteEvent();\n        event.setName(\"rmiEvent\");\n\n        client.call(new String[] { \"127.0.0.1:2088\", \"127.0.0.1:2089\" }, event, new Callback<List<Boolean>>() {\n\n            public void call(List<Boolean> event) {\n                want.number(event.size()).isEqualTo(2);\n                want.bool(event.get(0)).is(true);\n                want.bool(event.get(1)).is(true);\n            }\n        });// 异步调用\n    }\n\n    @Test(expectedExceptions = CommunicationException.class)\n    public void testRmi_FindEvent_Exception() { // 返回异常结果\n        CommunicationRegistry.regist(AppEventType.find, service);\n        AppFindEvent event = new AppFindEvent();\n        event.setName(\"rmiEvent-Not-Found\");\n\n        client.call(\"127.0.0.1:2088\", event);// 同步调用\n    }\n}\n"
  },
  {
    "path": "shared/communication/src/test/java/com/alibaba/otter/shared/communication/dubbo/DubboConnectionTest.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.communication.dubbo;\n\nimport org.testng.annotations.BeforeClass;\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.shared.communication.core.impl.connection.CommunicationConnection;\nimport com.alibaba.otter.shared.communication.core.impl.connection.CommunicationConnectionFactory;\nimport com.alibaba.otter.shared.communication.core.impl.dubbo.DubboCommunicationConnectionFactory;\nimport com.alibaba.otter.shared.communication.core.impl.dubbo.DubboCommunicationEndpoint;\nimport com.alibaba.otter.shared.communication.core.model.CommunicationParam;\nimport com.alibaba.otter.shared.communication.core.model.heart.HeartEvent;\n\npublic class DubboConnectionTest extends org.jtester.testng.JTester {\n\n    @BeforeClass\n    public void initial() {\n        // 创建endpoint\n        DubboCommunicationEndpoint endpoint = new DubboCommunicationEndpoint(2088);\n        endpoint.initial();\n    }\n\n    @Test\n    public void testSingle() {\n        CommunicationConnectionFactory factory = new DubboCommunicationConnectionFactory();\n        CommunicationParam param = new CommunicationParam();\n        param.setIp(\"127.0.0.1\");\n        param.setPort(2088);\n        CommunicationConnection connection = factory.createConnection(param);\n        Object result = connection.call(new HeartEvent());\n        want.object(result).notNull();\n    }\n\n}\n"
  },
  {
    "path": "shared/communication/src/test/java/com/alibaba/otter/shared/communication/rmi/RmiCommunicationTest.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.communication.rmi;\n\nimport java.math.BigDecimal;\nimport java.math.BigInteger;\nimport java.util.List;\n\nimport org.testng.annotations.BeforeClass;\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.shared.communication.app.CommunicationAppService;\nimport com.alibaba.otter.shared.communication.app.CommunicationAppServiceImpl;\nimport com.alibaba.otter.shared.communication.app.event.AppCreateEvent;\nimport com.alibaba.otter.shared.communication.app.event.AppDeleteEvent;\nimport com.alibaba.otter.shared.communication.app.event.AppEventType;\nimport com.alibaba.otter.shared.communication.app.event.AppFindEvent;\nimport com.alibaba.otter.shared.communication.app.event.AppUpdateEvent;\nimport com.alibaba.otter.shared.communication.app.event.AppUpdateEvent.UpdateData;\nimport com.alibaba.otter.shared.communication.core.CommunicationClient;\nimport com.alibaba.otter.shared.communication.core.CommunicationRegistry;\nimport com.alibaba.otter.shared.communication.core.exception.CommunicationException;\nimport com.alibaba.otter.shared.communication.core.impl.DefaultCommunicationClientImpl;\nimport com.alibaba.otter.shared.communication.core.impl.connection.CommunicationConnectionPoolFactory;\nimport com.alibaba.otter.shared.communication.core.impl.rmi.RmiCommunicationConnectionFactory;\nimport com.alibaba.otter.shared.communication.core.impl.rmi.RmiCommunicationEndpoint;\nimport com.alibaba.otter.shared.communication.core.model.Callback;\n\n/**\n * 测试下基于rmi通讯的功能\n * \n * @author jianghang 2011-9-13 下午04:03:38\n */\npublic class RmiCommunicationTest extends org.jtester.testng.JTester {\n\n    private CommunicationClient     client  = null;\n    private CommunicationAppService service = new CommunicationAppServiceImpl();\n\n    @BeforeClass\n    public void initial() {\n        RmiCommunicationEndpoint endpoint1099 = new RmiCommunicationEndpoint(1099);\n        endpoint1099.setAlwaysCreateRegistry(false);\n        endpoint1099.initial();\n\n        RmiCommunicationEndpoint endpoint1098 = new RmiCommunicationEndpoint(1098);\n        endpoint1098.setAlwaysCreateRegistry(false);\n        endpoint1098.initial();\n\n        CommunicationConnectionPoolFactory factory = new CommunicationConnectionPoolFactory(\n                                                                                            new RmiCommunicationConnectionFactory());\n        factory.initial();\n        client = new DefaultCommunicationClientImpl(factory);\n        client.initial();\n    }\n\n    @Test\n    public void testRmi_createEvent() {// 测试基本类型的序列化\n        CommunicationRegistry.regist(AppEventType.create, service);\n        AppCreateEvent event = new AppCreateEvent();\n        event.setName(\"rmiEvent\");\n        event.setIntValue(1);\n        event.setBoolValue(false);\n        event.setFloatValue(1.0f);\n        event.setDoubleValue(1.0d);\n        event.setLongValue(1l);\n        event.setCharValue('a');\n        event.setShortValue((short) 1);\n        event.setByteValue((byte) 1);\n        event.setIntegerValue(new Integer(\"1\"));\n        event.setBoolObjValue(new Boolean(\"false\"));\n        event.setFloatObjValue(new Float(\"1.0\"));\n        event.setDoubleObjValue(new Double(\"1.0\"));\n        event.setLongObjValue(new Long(\"1\"));\n        event.setCharacterValue('a');\n        event.setShortObjValue(new Short(\"1\"));\n        event.setByteObjValue(new Byte(\"1\"));\n\n        Object result = client.call(\"127.0.0.1:1099\", event);// 同步调用\n        want.bool((Boolean) result).is(true);\n    }\n\n    @Test\n    public void testRmi_updateEvent() { // 测试复合对象的序列化\n        CommunicationRegistry.regist(AppEventType.update, service);\n        AppUpdateEvent event = new AppUpdateEvent();\n        event.setName(\"rmiEvent\");\n        event.setBigDecimalValue(BigDecimal.TEN);\n        event.setBigIntegerValue(BigInteger.TEN);\n        UpdateData data = new UpdateData();\n        data.setName(\"data\");\n        data.setBigDecimalValue(BigDecimal.TEN);\n        data.setBigIntegerValue(BigInteger.TEN);\n        event.setData(data);\n        List<Boolean> result = (List<Boolean>) client.call(new String[] { \"127.0.0.1:1099\", \"127.0.0.1:1098\" }, event);// 同步调用\n\n        want.number(result.size()).isEqualTo(2);\n        want.bool(result.get(0)).is(true);\n        want.bool(result.get(1)).is(true);\n\n    }\n\n    @Test\n    public void testRmi_deleteEvent() { // 测试异步调用+多节点\n        CommunicationRegistry.regist(AppEventType.update, service);\n        AppDeleteEvent event = new AppDeleteEvent();\n        event.setName(\"rmiEvent\");\n\n        client.call(new String[] { \"127.0.0.1:1099\", \"127.0.0.1:1098\" }, event, new Callback<List<Boolean>>() {\n\n            public void call(List<Boolean> event) {\n                want.number(event.size()).isEqualTo(2);\n                want.bool(event.get(0)).is(true);\n                want.bool(event.get(1)).is(true);\n            }\n        });// 异步调用\n    }\n\n    @Test(expectedExceptions = CommunicationException.class)\n    public void testRmi_FindEvent_Exception() { // 返回异常结果\n        CommunicationRegistry.regist(AppEventType.find, service);\n        AppFindEvent event = new AppFindEvent();\n        event.setName(\"rmiEvent-Not-Found\");\n\n        client.call(\"127.0.0.1:1099\", event);// 同步调用\n    }\n}\n"
  },
  {
    "path": "shared/communication/src/test/java/com/alibaba/otter/shared/communication/rmi/RmiConnectionTest.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.communication.rmi;\n\nimport java.util.concurrent.Callable;\nimport java.util.concurrent.CountDownLatch;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\n\nimport org.testng.annotations.BeforeClass;\nimport org.testng.annotations.Test;\n\nimport com.alibaba.otter.shared.communication.core.CommunicationRegistry;\nimport com.alibaba.otter.shared.communication.core.impl.connection.CommunicationConnection;\nimport com.alibaba.otter.shared.communication.core.impl.connection.CommunicationConnectionFactory;\nimport com.alibaba.otter.shared.communication.core.impl.connection.CommunicationConnectionPoolFactory;\nimport com.alibaba.otter.shared.communication.core.impl.rmi.RmiCommunicationConnectionFactory;\nimport com.alibaba.otter.shared.communication.core.impl.rmi.RmiCommunicationEndpoint;\nimport com.alibaba.otter.shared.communication.core.model.CommunicationParam;\nimport com.alibaba.otter.shared.communication.core.model.Event;\nimport com.alibaba.otter.shared.communication.core.model.EventType;\nimport com.alibaba.otter.shared.communication.core.model.heart.HeartEvent;\n\n/**\n * 测试下rmi的连接池\n * \n * @author jianghang\n */\npublic class RmiConnectionTest extends org.jtester.testng.JTester {\n\n    @BeforeClass\n    public void initial() {\n        // 创建endpoint\n        RmiCommunicationEndpoint endpoint = new RmiCommunicationEndpoint(1099);\n        endpoint.setAlwaysCreateRegistry(false);\n        endpoint.initial();\n    }\n\n    @Test\n    public void testSingle() {\n        CommunicationConnectionFactory factory = new RmiCommunicationConnectionFactory();\n        CommunicationParam param = new CommunicationParam();\n        param.setIp(\"127.0.0.1\");\n        param.setPort(1099);\n        CommunicationConnection connection = factory.createConnection(param);\n        Object result = connection.call(new HeartEvent());\n        want.object(result).notNull();\n    }\n\n    @Test\n    public void testPool() {\n        CommunicationConnectionFactory factory = new RmiCommunicationConnectionFactory();\n        CommunicationConnectionFactory poolFactory = new CommunicationConnectionPoolFactory(factory);\n        ((CommunicationConnectionPoolFactory) poolFactory).initial();\n        CommunicationParam param = new CommunicationParam();\n        param.setIp(\"127.0.0.1\");\n        param.setPort(1099);\n        CommunicationRegistry.regist(PoolEventType.pool, new TestPoolService());\n\n        CommunicationConnection last = null;\n        for (int i = 0; i < 11; i++) {\n            CommunicationConnection connection = null;\n            try {\n                connection = poolFactory.createConnection(param);\n                connection.call(new PoolEvent(PoolEventType.pool));\n                last = connection;\n                if (last != null) { // 检查链接是否是重用\n                    want.object(last).isEqualTo(connection);\n                }\n            } finally {\n                connection.close();\n            }\n        }\n    }\n\n    @Test\n    public void testPool_exhaust() {\n        CommunicationConnectionFactory factory = new RmiCommunicationConnectionFactory();\n        CommunicationConnectionFactory poolFactory = new CommunicationConnectionPoolFactory(factory);\n        ((CommunicationConnectionPoolFactory) poolFactory).initial();\n        CommunicationParam param = new CommunicationParam();\n        param.setIp(\"127.0.0.1\");\n        param.setPort(1099);\n        CommunicationRegistry.regist(PoolEventType.exhaust, new TestPoolService());\n\n        ExecutorService executor = Executors.newCachedThreadPool();\n        long start = System.currentTimeMillis();\n        final CountDownLatch count = new CountDownLatch(11);\n        for (int i = 0; i < 11; i++) {\n            final CommunicationConnection connection = poolFactory.createConnection(param);\n            final PoolEvent event = new PoolEvent(PoolEventType.exhaust);\n            event.setSleep(1000);\n            executor.submit(new Callable() {\n\n                public Object call() throws Exception {\n                    try {\n                        Object obj = connection.call(event);\n                        count.countDown();\n                        return obj;\n                    } finally {\n                        connection.close();\n                    }\n                }\n            });\n        }\n        try {\n            count.await();\n        } catch (InterruptedException e) {\n            want.fail();\n        }\n        long end = System.currentTimeMillis();\n        want.number(end - start).isGe(1500L).isLe(2500L);\n    }\n\n    public static class PoolEvent extends Event {\n\n        private static final long serialVersionUID = -7387998054696636166L;\n\n        public PoolEvent(PoolEventType event){\n            super(event);\n        }\n\n        private long sleep;\n\n        public long getSleep() {\n            return sleep;\n        }\n\n        public void setSleep(long sleep) {\n            this.sleep = sleep;\n        }\n\n    }\n\n    public static enum PoolEventType implements EventType {\n        pool, exhaust;\n    }\n\n    public static class TestPoolService {\n\n        public void onPool(PoolEvent event) {\n        }\n\n        public void onExhaust(PoolEvent event) {\n            try {\n                Thread.sleep(event.getSleep());\n            } catch (InterruptedException e) {\n                want.fail();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "shared/communication/src/test/resources/applicationContext.xml",
    "content": "<?xml version=\"1.0\" encoding=\"GB2312\"?>\n<beans xmlns=\"http://www.springframework.org/schema/beans\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:tx=\"http://www.springframework.org/schema/tx\" xmlns:aop=\"http://www.springframework.org/schema/aop\" xmlns:lang=\"http://www.springframework.org/schema/lang\" xmlns:context=\"http://www.springframework.org/schema/context\" xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd\n           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd\n           http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-2.0.xsd\n           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd\n           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd\"\n    default-autowire=\"byName\">\n\n    <import resource=\"classpath:spring/otter-test-*.xml\" />\n</beans>"
  },
  {
    "path": "shared/communication/src/test/resources/spring/otter-test-communication.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<beans xmlns=\"http://www.springframework.org/schema/beans\"\r\n\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\r\n\txmlns:aop=\"http://www.springframework.org/schema/aop\"\r\n\txmlns:tx=\"http://www.springframework.org/schema/tx\"\r\n\txsi:schemaLocation=\"\n\thttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd\n\thttp://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd\n\thttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd\"\r\n\tdefault-autowire=\"byName\" default-dependency-check=\"none\">\r\n\r\n\t<bean id=\"rmiEndpoint\" class=\"com.alibaba.otter.shared.communication.core.impl.rmi.RmiCommunicationEndpoint\" init-method=\"initial\" destroy-method=\"destory\" >\r\n\t\t<property name=\"port\" value=\"1099\" />\r\n\t\t<property name=\"alwaysCreateRegistry\" value=\"false\" />\r\n\t</bean>\r\n\t<bean id=\"dubboEndpoint\" class=\"com.alibaba.otter.shared.communication.core.impl.dubbo.DubboCommunicationEndpoint\" init-method=\"initial\" destroy-method=\"destory\" >\r\n\t\t<property name=\"port\" value=\"2088\" />\r\n\t</bean>\r\n\t\r\n\t<bean id=\"rmiPoolCommunicationClient\" class=\"com.alibaba.otter.shared.communication.core.impl.DefaultCommunicationClientImpl\" init-method=\"initial\" destroy-method=\"destory\">\r\n\t\t<property name=\"factory\">\r\n\t\t\t<bean class=\"com.alibaba.otter.shared.communication.core.impl.connection.CommunicationConnectionPoolFactory\" init-method=\"initial\" destroy-method=\"destory\" >\r\n\t\t\t\t<property name=\"factory\">\r\n\t\t\t\t\t<bean class=\"com.alibaba.otter.shared.communication.core.impl.rmi.RmiCommunicationConnectionFactory\" />\r\n\t\t\t\t</property>\r\n\t\t\t\t<property name=\"maxActive\" value=\"10\" />\r\n\t\t\t</bean>\r\n\t\t</property>\r\n\t\t<property name=\"discard\" value=\"true\" />\r\n\t</bean>\r\n\t\r\n\t<bean id=\"rmiCommunicationClient\" class=\"com.alibaba.otter.shared.communication.core.impl.DefaultCommunicationClientImpl\" init-method=\"initial\" destroy-method=\"destory\">\r\n\t\t<property name=\"factory\">\r\n\t\t\t<bean class=\"com.alibaba.otter.shared.communication.core.impl.rmi.RmiCommunicationConnectionFactory\" />\r\n\t\t</property>\r\n\t</bean>\r\n\t\r\n\t<bean id=\"dubboCommunicationClient\" class=\"com.alibaba.otter.shared.communication.core.impl.DefaultCommunicationClientImpl\" init-method=\"initial\" destroy-method=\"destory\">\r\n\t\t<property name=\"factory\">\r\n\t\t\t<bean class=\"com.alibaba.otter.shared.communication.core.impl.dubbo.DubboCommunicationConnectionFactory\" />\r\n\t\t</property>\r\n\t</bean>\r\n</beans>\r\n"
  },
  {
    "path": "shared/etl/pom.xml",
    "content": "<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\">\n\t<modelVersion>4.0.0</modelVersion>\n\t<parent>\n\t\t<groupId>com.alibaba.otter</groupId>\n\t\t<artifactId>shared</artifactId>\n\t\t<version>4.2.19-SNAPSHOT</version>\n\t\t<relativePath>../pom.xml</relativePath>\n\t</parent>\n\t<groupId>com.alibaba.otter</groupId>\n\t<artifactId>shared.etl</artifactId>\n\t<packaging>jar</packaging>\n\t<name>etl shared module for otter</name>\n\t<url>http://github.com/alibaba/otter</url>\n\t\n\t<dependencies>\n\t\t<dependency>\n\t\t\t<groupId>com.alibaba.otter</groupId>\n\t\t\t<artifactId>shared.common</artifactId>\n\t\t\t<version>${project.version}</version>\n\t\t</dependency>\n\t\t<!-- test dependency -->\n\t\t<dependency>\n\t\t\t<groupId>junit</groupId>\n\t\t\t<artifactId>junit</artifactId>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.jtester</groupId>\n\t\t\t<artifactId>jtester</artifactId>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\t</dependencies>\n</project>\n"
  },
  {
    "path": "shared/etl/src/main/java/com/alibaba/otter/shared/etl/extend/fileresolver/FileInfo.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.etl.extend.fileresolver;\n\n/**\n * 文件描述信息\n * \n * @version 4.1.0\n */\npublic class FileInfo {\n\n    // private File file;\n    private long   lastModifiedTime;\n    private String namespace;\n    private String path;\n    private long   size;\n\n    public FileInfo(String path){\n        this.namespace = \"\";\n        // this.file = new File(path);\n        // this.size = file.length();\n        // this.lastModifiedTime = file.lastModified();\n        this.path = path;\n    }\n\n    public FileInfo(String namespace, String path){\n        this.namespace = namespace;\n        this.path = path;\n    }\n\n    public String getNamespace() {\n        return namespace;\n    }\n\n    public String getPath() {\n        return path;\n    }\n\n    public long getLastModifiedTime() {\n        return lastModifiedTime;\n    }\n\n    public long getSize() {\n        return size;\n    }\n\n    public String toString() {\n        return \"FileInfo [namespace=\" + namespace + \", path=\" + path + \", size=\" + size + \", modifyTime=\"\n               + lastModifiedTime + \"]\";\n    }\n}\n"
  },
  {
    "path": "shared/etl/src/main/java/com/alibaba/otter/shared/etl/extend/fileresolver/FileResolver.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.etl.extend.fileresolver;\n\nimport java.util.Map;\n\n/**\n * 文件提取接口类，包含附件的业务表需要实现该接口获取记录对应的附件信息.\n * \n * @author xiaoqing.zhouxq\n */\npublic interface FileResolver {\n\n    /**\n     * Get attachment file, the logic:\n     * \n     * @param rowMap key=column_name(UpCase) value=column_value\n     * @return FileInfo[] or null\n     */\n    public FileInfo[] getFileInfo(Map<String, String> rowMap);\n\n    /**\n     * 针对数据delete类型是否删除对应文件\n     * \n     * @return\n     */\n    public boolean isDeleteRequired();\n\n    public boolean isDistributed();\n\n}\n"
  },
  {
    "path": "shared/etl/src/main/java/com/alibaba/otter/shared/etl/extend/fileresolver/support/RemoteDirectory.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.etl.extend.fileresolver.support;\n\nimport java.io.IOException;\n\n/**\n * @author zebin.xuzb 2013-2-25 上午10:51:14\n * @since 4.1.7\n */\npublic interface RemoteDirectory {\n\n    public String getPath();\n\n    /**\n     * 删除目录\n     * \n     * @return\n     * @throws IOException\n     */\n    public boolean delete() throws IOException;\n\n    /**\n     * 判断目录是否存在。\n     * \n     * @return 如果目录存在返回true，否则false\n     */\n    public boolean exists();\n\n    /**\n     * 返回当前目录下的文件列表\n     * \n     * @return\n     * @throws IOException\n     */\n    public String[] listFiles() throws IOException;\n}\n"
  },
  {
    "path": "shared/etl/src/main/java/com/alibaba/otter/shared/etl/extend/fileresolver/support/RemoteDirectoryFetcher.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.etl.extend.fileresolver.support;\n\n/**\n * @author zebin.xuzb 2013-2-25 上午10:49:50\n * @since 4.1.7\n */\npublic interface RemoteDirectoryFetcher {\n\n    /**\n     * 获取 RemoteDirectory， 可能为 null!\n     * \n     * @param namespace\n     * @param path\n     * @return\n     */\n    RemoteDirectory fetch(String namespace, String path);\n}\n"
  },
  {
    "path": "shared/etl/src/main/java/com/alibaba/otter/shared/etl/extend/fileresolver/support/RemoteDirectoryFetcherAware.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.etl.extend.fileresolver.support;\n\n/**\n * @author zebin.xuzb 2013-2-25 上午10:53:55\n * @since 4.1.7\n */\npublic interface RemoteDirectoryFetcherAware {\n\n    void setRemoteDirectoryFetcher(RemoteDirectoryFetcher remoteDirectoryFetcher);\n}\n"
  },
  {
    "path": "shared/etl/src/main/java/com/alibaba/otter/shared/etl/extend/processor/EventProcessor.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.etl.extend.processor;\n\nimport com.alibaba.otter.shared.etl.model.EventData;\n\n/**\n * 业务自定义处理过程\n * \n * @author jianghang 2012-6-25 下午02:26:36\n * @version 4.1.0\n */\npublic interface EventProcessor {\n\n    /**\n     * 自定义处理单条EventData对象，如果要改变数据内容，请直接修改原对象而非new一个新的对象\n     * \n     * <pre>\n     * EventData数据格式: \n     *    a. schema name / table name\n     *    b. eventType : insert/update/delete\n     *    c. executeTime : 执行时间\n     *    d. keys / oldKeys : 主键字段 (如果是有主键变更，需要带上老主键的信息在oldKeys中)\n     *    e. columns :  非主键字段\n     * \n     * EventColumn数据格式：\n     *    a. index : 字段在数据表中的顺序下标\n     *    b. columnType : 对应于sqlType\n     *    c. columnName : 字段名字\n     *    d. columnValue : 字段类型\n     *    e. isKey : 是否为主键\n     *    f. isNull : 是否为空值\n     * </pre>\n     * \n     * @return false需要忽略该条数据，true代表继续处理\n     */\n    public boolean process(EventData eventData);\n}\n"
  },
  {
    "path": "shared/etl/src/main/java/com/alibaba/otter/shared/etl/extend/processor/support/DataSourceFetcher.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.etl.extend.processor.support;\n\nimport javax.sql.DataSource;\n\n/**\n * 获取数据库\n * \n * @author jianghang 2014-6-11 下午3:15:57\n * @since 4.2.10\n */\npublic interface DataSourceFetcher {\n\n    /**\n     * 获取 DataSource\n     */\n    DataSource fetch(Long tableId);\n\n}\n"
  },
  {
    "path": "shared/etl/src/main/java/com/alibaba/otter/shared/etl/extend/processor/support/DataSourceFetcherAware.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.etl.extend.processor.support;\n\n/**\n * @author jianghang 2014-6-11 下午3:15:57\n * @since 4.2.10\n */\npublic interface DataSourceFetcherAware {\n\n    void setDataSourceFetcher(DataSourceFetcher dataSourceFetcher);\n}\n"
  },
  {
    "path": "shared/etl/src/main/java/com/alibaba/otter/shared/etl/model/BatchObject.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.etl.model;\n\nimport java.io.Serializable;\n\nimport org.apache.commons.lang.builder.ToStringBuilder;\n\nimport com.alibaba.otter.shared.common.utils.OtterToStringStyle;\n\n/**\n * @author xiaoqing.zhouxq 2011-8-15 上午09:10:42\n */\npublic abstract class BatchObject<T> implements Serializable {\n\n    private static final long serialVersionUID = 3211077130963551303L;\n    private Identity          identity;\n\n    public Identity getIdentity() {\n        return identity;\n    }\n\n    public void setIdentity(Identity identity) {\n        this.identity = identity;\n    }\n\n    public abstract void merge(T data);\n\n    @Override\n    public String toString() {\n        return ToStringBuilder.reflectionToString(this, OtterToStringStyle.DEFAULT_STYLE);\n    }\n\n    @Override\n    public int hashCode() {\n        final int prime = 31;\n        int result = 1;\n        result = prime * result + ((identity == null) ? 0 : identity.hashCode());\n        return result;\n    }\n\n    @Override\n    public boolean equals(Object obj) {\n        if (this == obj) return true;\n        if (obj == null) return false;\n        if (getClass() != obj.getClass()) return false;\n        BatchObject other = (BatchObject) obj;\n        if (identity == null) {\n            if (other.identity != null) return false;\n        } else if (!identity.equals(other.identity)) return false;\n        return true;\n    }\n\n}\n"
  },
  {
    "path": "shared/etl/src/main/java/com/alibaba/otter/shared/etl/model/DbBatch.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.etl.model;\n\nimport java.io.File;\nimport java.io.Serializable;\n\nimport org.apache.commons.lang.builder.ToStringBuilder;\n\nimport com.alibaba.otter.shared.common.utils.OtterToStringStyle;\n\n/**\n * 基于数据库的同步记录对象\n * \n * @author xiaoqing.zhouxq\n */\npublic class DbBatch implements Serializable {\n\n    private static final long serialVersionUID = 1716704802567430638L;\n\n    private RowBatch          rowBatch;                               // 如果目标端是db，则一定不为空\n\n    private FileBatch         fileBatch;                              // 可能没有附件\n\n    private File              root;                                   // attachment的根路径，如果存在fileBatch一定存在attachment\n\n    public DbBatch(){\n\n    }\n\n    public DbBatch(RowBatch rowBatch){\n        this.rowBatch = rowBatch;\n        this.fileBatch = new FileBatch();\n        this.fileBatch.setIdentity(rowBatch.getIdentity());\n    }\n\n    public DbBatch(RowBatch rowBatch, FileBatch fileBatch, File root){\n        this.rowBatch = rowBatch;\n        this.fileBatch = fileBatch;\n        this.root = root;\n    }\n\n    public RowBatch getRowBatch() {\n        return rowBatch;\n    }\n\n    public void setRowBatch(RowBatch rowBatch) {\n        this.rowBatch = rowBatch;\n    }\n\n    public FileBatch getFileBatch() {\n        return fileBatch;\n    }\n\n    public void setFileBatch(FileBatch fileBatch) {\n        this.fileBatch = fileBatch;\n    }\n\n    public File getRoot() {\n        return root;\n    }\n\n    public void setRoot(File root) {\n        this.root = root;\n    }\n\n    @Override\n    public String toString() {\n        return ToStringBuilder.reflectionToString(this, OtterToStringStyle.DEFAULT_STYLE);\n    }\n\n}\n"
  },
  {
    "path": "shared/etl/src/main/java/com/alibaba/otter/shared/etl/model/EventColumn.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.etl.model;\n\nimport java.io.Serializable;\n\nimport org.apache.commons.lang.builder.ToStringBuilder;\n\nimport com.alibaba.otter.shared.common.utils.OtterToStringStyle;\n\n/**\n * @author xiaoqing.zhouxq 2011-8-10 上午10:42:27\n */\npublic class EventColumn implements Serializable {\n\n    private static final long serialVersionUID = 8881024631437131042L;\n\n    private int               index;\n\n    private int               columnType;\n\n    private String            columnName;\n\n    /**\n     * timestamp,Datetime是一个long型的数字.\n     */\n    private String            columnValue;\n\n    private boolean           isNull;\n\n    private boolean           isKey;\n\n    /**\n     * 2012.08.09 add by ljh , 新加字段，用于表明是否为真实变更字段，只针对非主键字段有效<br>\n     * 因为FileResolver/EventProcessor会需要所有字段数据做分析，但又想保留按需字段同步模式\n     * \n     * <pre>\n     * 可以简单理解isUpdate代表是否需要在目标库执行数据变更，针对update有效，默认insert/delete为true\n     * 1. row模式，所有字段均为updated\n     * 2. field模式，通过db反查得到的结果，均为updated\n     * 3. 其余场景，根据判断是否变更过，设置updated数据\n     * </pre>\n     */\n    private boolean           isUpdate         = true;\n\n    public int getColumnType() {\n        return columnType;\n    }\n\n    public void setColumnType(int columnType) {\n        this.columnType = columnType;\n    }\n\n    public String getColumnName() {\n        return columnName;\n    }\n\n    public void setColumnName(String columnName) {\n        this.columnName = columnName;\n    }\n\n    public String getColumnValue() {\n        if (isNull) {\n            // 如果为null值，强制设置为null, canal主要是走protobuf协议，String值默认为空字符，无法标示为null对象\n            columnValue = null;\n            return null;\n        } else {\n            return columnValue;\n        }\n    }\n\n    public void setColumnValue(String columnValue) {\n        this.columnValue = columnValue;\n    }\n\n    public boolean isNull() {\n        return isNull;\n    }\n\n    public void setNull(boolean isNull) {\n        this.isNull = isNull;\n    }\n\n    public boolean isKey() {\n        return isKey;\n    }\n\n    public void setKey(boolean isKey) {\n        this.isKey = isKey;\n    }\n\n    public int getIndex() {\n        return index;\n    }\n\n    public void setIndex(int index) {\n        this.index = index;\n    }\n\n    public boolean isUpdate() {\n        return isUpdate;\n    }\n\n    public void setUpdate(boolean isUpdate) {\n        this.isUpdate = isUpdate;\n    }\n\n    public EventColumn clone() {\n        EventColumn column = new EventColumn();\n        column.setIndex(index);\n        column.setColumnName(columnName);\n        column.setColumnType(columnType);\n        column.setColumnValue(columnValue);\n        column.setKey(isKey);\n        column.setNull(isNull);\n        column.setUpdate(isUpdate);\n        return column;\n    }\n\n    @Override\n    public int hashCode() {\n        final int prime = 31;\n        int result = 1;\n        result = prime * result + ((columnName == null) ? 0 : columnName.hashCode());\n        result = prime * result + columnType;\n        result = prime * result + ((columnValue == null) ? 0 : columnValue.hashCode());\n        result = prime * result + index;\n        result = prime * result + (isKey ? 1231 : 1237);\n        result = prime * result + (isNull ? 1231 : 1237);\n        result = prime * result + (isUpdate ? 1231 : 1237);\n        return result;\n    }\n\n    @Override\n    public boolean equals(Object obj) {\n        if (this == obj) return true;\n        if (obj == null) return false;\n        if (getClass() != obj.getClass()) return false;\n        EventColumn other = (EventColumn) obj;\n        if (columnName == null) {\n            if (other.columnName != null) return false;\n        } else if (!columnName.equals(other.columnName)) return false;\n        if (columnType != other.columnType) return false;\n        if (columnValue == null) {\n            if (other.columnValue != null) return false;\n        } else if (!columnValue.equals(other.columnValue)) return false;\n        if (index != other.index) return false;\n        if (isKey != other.isKey) return false;\n        if (isNull != other.isNull) return false;\n        if (isUpdate != other.isUpdate) return false;\n        return true;\n    }\n\n    public String toString() {\n        return ToStringBuilder.reflectionToString(this, OtterToStringStyle.DEFAULT_STYLE);\n    }\n\n}\n"
  },
  {
    "path": "shared/etl/src/main/java/com/alibaba/otter/shared/etl/model/EventColumnIndexComparable.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.etl.model;\n\nimport java.util.Comparator;\n\n/**\n * 按照EventColumn的index进行排序.\n * \n * @author xiaoqing.zhouxq 2012-3-8 上午11:38:25\n */\npublic class EventColumnIndexComparable implements Comparator<EventColumn> {\n\n    public int compare(EventColumn o1, EventColumn o2) {\n        if (o1.getIndex() < o2.getIndex()) {\n            return -1;\n        } else if (o1.getIndex() == o2.getIndex()) {\n            return 0;\n        } else {\n            return 1;\n        }\n    }\n\n}\n"
  },
  {
    "path": "shared/etl/src/main/java/com/alibaba/otter/shared/etl/model/EventData.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.etl.model;\n\nimport java.io.Serializable;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.apache.commons.lang.builder.ToStringBuilder;\n\nimport com.alibaba.otter.shared.common.model.config.channel.ChannelParameter.SyncConsistency;\nimport com.alibaba.otter.shared.common.model.config.channel.ChannelParameter.SyncMode;\nimport com.alibaba.otter.shared.common.utils.OtterToStringStyle;\n\n/**\n * 每条变更数据.\n * \n * @author xiaoqing.zhouxq 2011-8-9 下午03:42:20\n */\npublic class EventData implements ObjectData, Serializable {\n\n    private static final long serialVersionUID = -7071677425383765372L;\n\n    /**\n     * otter内部维护的一套tableId，与manager中得到的table Id对应\n     */\n    private long              tableId          = -1;\n\n    private String            tableName;\n\n    private String            schemaName;\n\n    /**\n     * 变更数据的业务类型(I/U/D/C/A/E),与canal中的EntryProtocol中定义的EventType一致.\n     */\n    private EventType         eventType;\n\n    /**\n     * 变更数据的业务时间.\n     */\n    private long              executeTime;\n\n    /**\n     * 变更前的主键值,如果是insert/delete变更前和变更后的主键值是一样的.\n     */\n    private List<EventColumn> oldKeys          = new ArrayList<EventColumn>();\n\n    /**\n     * 变更后的主键值,如果是insert/delete变更前和变更后的主键值是一样的.\n     */\n    private List<EventColumn> keys             = new ArrayList<EventColumn>();\n\n    /**\n     * 非主键的其他字段\n     */\n    private List<EventColumn> columns          = new ArrayList<EventColumn>();\n\n    // ====================== 运行过程中对数据的附加属性 =============================\n    /**\n     * 预计的size大小，基于binlog event的推算\n     */\n    private long              size             = 1024;\n\n    /**\n     * 同步映射关系的id\n     */\n    private long              pairId           = -1;\n\n    /**\n     * 当eventType =\n     * CREATE/ALTER/ERASE时，就是对应的sql语句，其他情况为动态生成的INSERT/UPDATE/DELETE sql\n     */\n    private String            sql;\n\n    /**\n     * ddl/query的schemaName，会存在跨库ddl，需要保留执行ddl的当前schemaName\n     */\n    private String            ddlSchemaName;\n\n    /**\n     * 自定义的同步模式, 允许覆盖默认的pipeline parameter，比如针对补救数据同步\n     */\n    private SyncMode          syncMode;\n\n    /**\n     * 自定义的同步一致性，允许覆盖默认的pipeline parameter，比如针对字段组强制反查数据库\n     */\n    private SyncConsistency   syncConsistency;\n\n    /**\n     * 是否为remedy补救数据，比如回环补救自动产生的数据，或者是freedom产生的手工订正数据\n     */\n    private boolean           remedy           = false;\n\n    /**\n     * 生成对应的hint内容\n     */\n    private String            hint;\n\n    /**\n     * 生成sql是否忽略schema,比如针对tddl/drds,需要忽略schema\n     */\n    private boolean           withoutSchema    = false;\n\n    public long getTableId() {\n        return tableId;\n    }\n\n    public void setTableId(long tableId) {\n        this.tableId = tableId;\n    }\n\n    public String getTableName() {\n        return tableName;\n    }\n\n    public void setTableName(String tableName) {\n        this.tableName = tableName;\n    }\n\n    public String getSchemaName() {\n        return schemaName;\n    }\n\n    public void setSchemaName(String schemaName) {\n        this.schemaName = schemaName;\n    }\n\n    public EventType getEventType() {\n        return eventType;\n    }\n\n    public void setEventType(EventType eventType) {\n        this.eventType = eventType;\n    }\n\n    public String getSql() {\n        return sql;\n    }\n\n    public void setSql(String sql) {\n        this.sql = sql;\n    }\n\n    public long getExecuteTime() {\n        return executeTime;\n    }\n\n    public void setExecuteTime(long executeTime) {\n        this.executeTime = executeTime;\n    }\n\n    public List<EventColumn> getKeys() {\n        return keys;\n    }\n\n    public void setKeys(List<EventColumn> keys) {\n        this.keys = keys;\n    }\n\n    public List<EventColumn> getColumns() {\n        return columns;\n    }\n\n    public void setColumns(List<EventColumn> columns) {\n        this.columns = columns;\n    }\n\n    public long getPairId() {\n        return pairId;\n    }\n\n    public void setPairId(long pairId) {\n        this.pairId = pairId;\n    }\n\n    public List<EventColumn> getOldKeys() {\n        return oldKeys;\n    }\n\n    public void setOldKeys(List<EventColumn> oldKeys) {\n        this.oldKeys = oldKeys;\n    }\n\n    public SyncMode getSyncMode() {\n        return syncMode;\n    }\n\n    public void setSyncMode(SyncMode syncMode) {\n        this.syncMode = syncMode;\n    }\n\n    public SyncConsistency getSyncConsistency() {\n        return syncConsistency;\n    }\n\n    public void setSyncConsistency(SyncConsistency syncConsistency) {\n        this.syncConsistency = syncConsistency;\n    }\n\n    public long getSize() {\n        return size;\n    }\n\n    public void setSize(long size) {\n        this.size = size;\n    }\n\n    public String getDdlSchemaName() {\n        return ddlSchemaName;\n    }\n\n    public void setDdlSchemaName(String ddlSchemaName) {\n        this.ddlSchemaName = ddlSchemaName;\n    }\n\n    public boolean isRemedy() {\n        return remedy;\n    }\n\n    public void setRemedy(boolean remedy) {\n        this.remedy = remedy;\n    }\n\n    public String getHint() {\n        return hint;\n    }\n\n    public void setHint(String hint) {\n        this.hint = hint;\n    }\n\n    public boolean isWithoutSchema() {\n        return withoutSchema;\n    }\n\n    public void setWithoutSchema(boolean withoutSchema) {\n        this.withoutSchema = withoutSchema;\n    }\n\n    // ======================== helper method =================\n\n    /**\n     * 返回所有待变更的字段\n     */\n    public List<EventColumn> getUpdatedColumns() {\n        List<EventColumn> columns = new ArrayList<EventColumn>();\n        for (EventColumn column : this.columns) {\n            if (column.isUpdate()) {\n                columns.add(column);\n            }\n        }\n\n        return columns;\n    }\n\n    /**\n     * 返回所有变更的主键字段\n     */\n    public List<EventColumn> getUpdatedKeys() {\n        List<EventColumn> columns = new ArrayList<EventColumn>();\n        for (EventColumn column : this.keys) {\n            if (column.isUpdate()) {\n                columns.add(column);\n            }\n        }\n\n        return columns;\n    }\n\n    private List<EventColumn> cloneColumn(List<EventColumn> columns) {\n        if (columns == null) {\n            return null;\n        }\n\n        List<EventColumn> cloneColumns = new ArrayList<EventColumn>();\n        for (EventColumn column : columns) {\n            cloneColumns.add(column.clone());\n        }\n\n        return cloneColumns;\n    }\n\n    public EventData clone() {\n        EventData data = new EventData();\n        data.setTableId(tableId);\n        data.setTableName(tableName);\n        data.setSchemaName(schemaName);\n        data.setDdlSchemaName(ddlSchemaName);\n        data.setEventType(eventType);\n        data.setExecuteTime(executeTime);\n        data.setKeys(cloneColumn(keys));\n        data.setColumns(cloneColumn(columns));\n        data.setOldKeys(cloneColumn(oldKeys));\n        data.setSize(size);\n        data.setPairId(pairId);\n        data.setSql(sql);\n        data.setSyncMode(syncMode);\n        data.setSyncConsistency(syncConsistency);\n        data.setRemedy(remedy);\n        data.setHint(hint);\n        data.setWithoutSchema(withoutSchema);\n        return data;\n    }\n\n    public String toString() {\n        return ToStringBuilder.reflectionToString(this, OtterToStringStyle.DEFAULT_STYLE);\n    }\n\n    @Override\n    public int hashCode() {\n        final int prime = 31;\n        int result = 1;\n        result = prime * result + ((columns == null) ? 0 : columns.hashCode());\n        result = prime * result + ((eventType == null) ? 0 : eventType.hashCode());\n        result = prime * result + (int) (executeTime ^ (executeTime >>> 32));\n        result = prime * result + ((keys == null) ? 0 : keys.hashCode());\n        result = prime * result + ((oldKeys == null) ? 0 : oldKeys.hashCode());\n        result = prime * result + (int) (pairId ^ (pairId >>> 32));\n        result = prime * result + ((schemaName == null) ? 0 : schemaName.hashCode());\n        result = prime * result + (int) (tableId ^ (tableId >>> 32));\n        result = prime * result + ((tableName == null) ? 0 : tableName.hashCode());\n        return result;\n    }\n\n    @Override\n    public boolean equals(Object obj) {\n        if (this == obj) return true;\n        if (obj == null) return false;\n        if (getClass() != obj.getClass()) return false;\n        EventData other = (EventData) obj;\n        if (columns == null) {\n            if (other.columns != null) return false;\n        } else if (!columns.equals(other.columns)) return false;\n        if (eventType != other.eventType) return false;\n        if (executeTime != other.executeTime) return false;\n        if (keys == null) {\n            if (other.keys != null) return false;\n        } else if (!keys.equals(other.keys)) return false;\n        if (oldKeys == null) {\n            if (other.oldKeys != null) return false;\n        } else if (!oldKeys.equals(other.oldKeys)) return false;\n        if (pairId != other.pairId) return false;\n        if (schemaName == null) {\n            if (other.schemaName != null) return false;\n        } else if (!schemaName.equals(other.schemaName)) return false;\n        if (tableId != other.tableId) return false;\n        if (tableName == null) {\n            if (other.tableName != null) return false;\n        } else if (!tableName.equals(other.tableName)) return false;\n        return true;\n    }\n\n}\n"
  },
  {
    "path": "shared/etl/src/main/java/com/alibaba/otter/shared/etl/model/EventType.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.etl.model;\n\n/**\n * chang the eventtype num to I/U/D/C/A/E.\n * \n * @author xiaoqing.zhouxq 2010-7-22 下午02:47:44\n */\npublic enum EventType {\n\n    /**\n     * Insert row.\n     */\n    INSERT(\"I\"),\n\n    /**\n     * Update row.\n     */\n    UPDATE(\"U\"),\n\n    /**\n     * Delete row.\n     */\n    DELETE(\"D\"),\n\n    /**\n     * Create table.\n     */\n    CREATE(\"C\"),\n\n    /**\n     * Alter table.\n     */\n    ALTER(\"A\"),\n\n    /**\n     * Erase table.\n     */\n    ERASE(\"E\"),\n\n    /**\n     * Query.\n     */\n    QUERY(\"Q\"),\n\n    /**\n     * Truncate.\n     */\n    TRUNCATE(\"T\"),\n\n    /**\n     * rename.\n     */\n    RENAME(\"R\"),\n\n    /**\n     * create index.\n     */\n    CINDEX(\"CI\"),\n\n    /**\n     * drop index.\n     */\n    DINDEX(\"DI\");\n\n    private String value;\n\n    private EventType(String value){\n        this.value = value;\n    }\n\n    public boolean isInsert() {\n        return this.equals(EventType.INSERT);\n    }\n\n    public boolean isUpdate() {\n        return this.equals(EventType.UPDATE);\n    }\n\n    public boolean isDelete() {\n        return this.equals(EventType.DELETE);\n    }\n\n    public boolean isCreate() {\n        return this.equals(EventType.CREATE);\n    }\n\n    public boolean isAlter() {\n        return this.equals(EventType.ALTER);\n    }\n\n    public boolean isErase() {\n        return this.equals(EventType.ERASE);\n    }\n\n    public boolean isQuery() {\n        return this.equals(EventType.QUERY);\n    }\n\n    public boolean isTruncate() {\n        return this.equals(EventType.TRUNCATE);\n    }\n\n    public boolean isRename() {\n        return this.equals(EventType.RENAME);\n    }\n\n    public boolean isCindex() {\n        return this.equals(EventType.CINDEX);\n    }\n\n    public boolean isDindex() {\n        return this.equals(EventType.DINDEX);\n    }\n\n    public boolean isDdl() {\n        return isCreate() || isAlter() || isErase() || isTruncate() || isRename() || isCindex() || isDindex();\n    }\n\n    public boolean isDml() {\n        return isInsert() || isUpdate() || isDelete();\n    }\n\n    public static EventType valuesOf(String value) {\n        EventType[] eventTypes = values();\n        for (EventType eventType : eventTypes) {\n            if (eventType.value.equalsIgnoreCase(value)) {\n                return eventType;\n            }\n        }\n        return null;\n    }\n\n    public static EventType fromName(String name) {\n        for (EventType eventType : values()) {\n            if (eventType.name().equalsIgnoreCase(name)) {\n                return eventType;\n            }\n        }\n        return null;\n    }\n\n    public String getValue() {\n        return value;\n    }\n\n    public void setValue(String value) {\n        this.value = value;\n    }\n\n}\n"
  },
  {
    "path": "shared/etl/src/main/java/com/alibaba/otter/shared/etl/model/FileBatch.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.etl.model;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * 文件数据集合对象\n * \n * @author jianghang 2012-10-31 下午05:56:01\n * @version 4.1.2\n */\npublic class FileBatch extends BatchObject<FileData> {\n\n    private static final long serialVersionUID = -520456006652566067L;\n    private List<FileData>    files            = new ArrayList<FileData>();\n\n    public List<FileData> getFiles() {\n        return files;\n    }\n\n    public void setFiles(List<FileData> files) {\n        this.files = files;\n    }\n\n    public void merge(FileData data) {\n        this.files.add(data);\n    }\n\n}\n"
  },
  {
    "path": "shared/etl/src/main/java/com/alibaba/otter/shared/etl/model/FileData.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.etl.model;\n\nimport java.io.Serializable;\n\nimport org.apache.commons.lang.builder.ToStringBuilder;\n\nimport com.alibaba.otter.shared.common.utils.OtterToStringStyle;\n\n/**\n * 每条变更数据关联的文件信息.\n * \n * @author xiaoqing.zhouxq 2011-8-9 下午04:52:10\n */\npublic class FileData implements ObjectData, Serializable {\n\n    private static final long serialVersionUID = -8758520351171874888L;\n\n    /**\n     * Aranda use.\n     */\n    private String            nameSpace;\n\n    private String            path;\n\n    private long              size;\n\n    private long              lastModifiedTime;\n\n    private long              tableId;\n\n    private long              pairId;\n\n    private EventType         eventType;\n\n    public String getPath() {\n        return path;\n    }\n\n    public void setPath(String path) {\n        this.path = path;\n    }\n\n    public long getSize() {\n        return size;\n    }\n\n    public void setSize(long size) {\n        this.size = size;\n    }\n\n    public long getTableId() {\n        return tableId;\n    }\n\n    public void setTableId(long tableId) {\n        this.tableId = tableId;\n    }\n\n    public long getLastModifiedTime() {\n        return lastModifiedTime;\n    }\n\n    public void setLastModifiedTime(long lastModifiedTime) {\n        this.lastModifiedTime = lastModifiedTime;\n    }\n\n    public EventType getEventType() {\n        return eventType;\n    }\n\n    public void setEventType(EventType eventType) {\n        this.eventType = eventType;\n    }\n\n    public String getNameSpace() {\n        return nameSpace;\n    }\n\n    public void setNameSpace(String nameSpace) {\n        this.nameSpace = nameSpace;\n    }\n\n    public long getPairId() {\n        return pairId;\n    }\n\n    public void setPairId(long pairId) {\n        this.pairId = pairId;\n    }\n\n    public int hashCode() {\n        final int prime = 31;\n        int result = 1;\n        result = prime * result + ((nameSpace == null) ? 0 : nameSpace.hashCode());\n        result = prime * result + ((path == null) ? 0 : path.hashCode());\n        return result;\n    }\n\n    public boolean equals(Object obj) {\n        if (this == obj) {\n            return true;\n        }\n        if (obj == null) {\n            return false;\n        }\n        if (!(obj instanceof FileData)) {\n            return false;\n        }\n        FileData other = (FileData) obj;\n        if (nameSpace == null) {\n            if (other.nameSpace != null) {\n                return false;\n            }\n        } else if (!nameSpace.equals(other.nameSpace)) {\n            return false;\n        }\n        if (path == null) {\n            if (other.path != null) {\n                return false;\n            }\n        } else if (!path.equals(other.path)) {\n            return false;\n        }\n        return true;\n    }\n\n    public String toString() {\n        return ToStringBuilder.reflectionToString(this, OtterToStringStyle.DEFAULT_STYLE);\n    }\n}\n"
  },
  {
    "path": "shared/etl/src/main/java/com/alibaba/otter/shared/etl/model/Identity.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.etl.model;\n\nimport java.io.Serializable;\n\nimport org.apache.commons.lang.builder.ToStringBuilder;\n\nimport com.alibaba.otter.shared.common.utils.OtterToStringStyle;\n\n/**\n * process标识，用于区分每个process.\n * \n * @author xiaoqing.zhouxq 2011-8-9 上午10:49:14\n */\npublic class Identity implements Serializable {\n\n    private static final long serialVersionUID = -4551215214079451994L;\n\n    private long              channelId;\n\n    private long              pipelineId;\n\n    private long              processId;\n\n    public Identity(){\n\n    }\n\n    public Identity(Long channelId, Long pipelineId, Long processId){\n        this.channelId = channelId;\n        this.pipelineId = pipelineId;\n        this.processId = processId;\n    }\n\n    public long getChannelId() {\n        return channelId;\n    }\n\n    public void setChannelId(long channelId) {\n        this.channelId = channelId;\n    }\n\n    public long getPipelineId() {\n        return pipelineId;\n    }\n\n    public void setPipelineId(long pipelineId) {\n        this.pipelineId = pipelineId;\n    }\n\n    public long getProcessId() {\n        return processId;\n    }\n\n    public void setProcessId(long processId) {\n        this.processId = processId;\n    }\n\n    @Override\n    public int hashCode() {\n        final int prime = 31;\n        int result = 1;\n        result = prime * result + (int) (channelId ^ (channelId >>> 32));\n        result = prime * result + (int) (pipelineId ^ (pipelineId >>> 32));\n        result = prime * result + (int) (processId ^ (processId >>> 32));\n        return result;\n    }\n\n    @Override\n    public boolean equals(Object obj) {\n        if (this == obj) {\n            return true;\n        }\n        if (obj == null) {\n            return false;\n        }\n        if (!(obj instanceof Identity)) {\n            return false;\n        }\n        Identity other = (Identity) obj;\n        if (channelId != other.channelId) {\n            return false;\n        }\n        if (pipelineId != other.pipelineId) {\n            return false;\n        }\n        if (processId != other.processId) {\n            return false;\n        }\n        return true;\n    }\n\n    public String toString() {\n        return ToStringBuilder.reflectionToString(this, OtterToStringStyle.DEFAULT_STYLE);\n    }\n}\n"
  },
  {
    "path": "shared/etl/src/main/java/com/alibaba/otter/shared/etl/model/ObjectData.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.etl.model;\n\n/**\n * @author simon 2012-7-6 下午6:43:23\n * @version 4.1.0\n */\npublic interface ObjectData {\n\n}\n"
  },
  {
    "path": "shared/etl/src/main/java/com/alibaba/otter/shared/etl/model/RowBatch.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.shared.etl.model;\n\nimport java.util.LinkedList;\nimport java.util.List;\n\n/**\n * 数据记录集合对象\n * \n * @author jianghang 2012-10-31 下午05:51:42\n * @version 4.1.2\n */\npublic class RowBatch extends BatchObject<EventData> {\n\n    private static final long serialVersionUID = -6117067964148581257L;\n\n    private List<EventData>   datas            = new LinkedList<EventData>();\n\n    public List<EventData> getDatas() {\n        return datas;\n    }\n\n    public void setDatas(List<EventData> datas) {\n        this.datas = datas;\n    }\n\n    public void merge(EventData data) {\n        this.datas.add(data);\n    }\n\n}\n"
  },
  {
    "path": "shared/pom.xml",
    "content": "<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\">\n\t<modelVersion>4.0.0</modelVersion>\n\t<parent>\n\t    <groupId>com.alibaba.otter</groupId>\n\t    <artifactId>otter</artifactId>\n\t    <version>4.2.19-SNAPSHOT</version>\n\t</parent>\n\t<groupId>com.alibaba.otter</groupId>\n\t<artifactId>shared</artifactId>\n\t<version>4.2.19-SNAPSHOT</version>\n\t<packaging>pom</packaging>\n\t<name>shared module for otter</name>\n\t<url>http://github.com/alibaba/otter</url>\n\t\n\t<modules>\t\n\t\t<module>arbitrate</module>\n\t\t<module>common</module>\n\t\t<module>communication</module>\n\t\t<module>etl</module>\n\t\t<module>push</module>\n\t</modules>\n\t\n\t<dependencyManagement>\n\t\t<dependencies>\n\t\t\t<dependency>\n\t\t\t\t<groupId>oro</groupId>\n\t\t\t\t<artifactId>oro</artifactId>\n\t\t\t\t<version>2.0.8</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.apache.ddlutils</groupId>\n\t\t\t\t<artifactId>ddlutils</artifactId>\n\t\t\t\t<version>1.0</version>\n\t\t\t\t<exclusions>\n\t\t\t\t  <exclusion>\n\t\t\t\t\t<groupId>commons-beanutils</groupId>\n\t\t\t\t\t<artifactId>commons-beanutils-core</artifactId>\n\t\t\t\t  </exclusion>\n\t\t\t\t  <exclusion>\n\t\t\t\t\t<groupId>commons-logging</groupId>\n\t\t\t\t\t<artifactId>commons-logging-api</artifactId>\n\t\t\t\t  </exclusion>\n\t\t\t\t  <exclusion>\n\t\t\t\t\t<groupId>dom4j</groupId>\n\t\t\t\t\t<artifactId>dom4j</artifactId>\n\t\t\t\t  </exclusion>\n\t\t\t\t  <exclusion>\n\t\t\t\t\t<groupId>stax</groupId>\n\t\t\t\t\t<artifactId>stax-api</artifactId>\n\t\t\t\t  </exclusion>\n\t\t\t\t  <exclusion>\n\t\t\t\t\t<groupId>commons-collections</groupId>\n\t\t\t\t\t<artifactId>commons-collections</artifactId>\n\t\t\t\t  </exclusion>\n\t\t\t\t  <exclusion>\n\t\t\t\t\t<groupId>commons-digester</groupId>\n\t\t\t\t\t<artifactId>commons-digester</artifactId>\n\t\t\t\t  </exclusion>\n\t\t\t\t  <exclusion>\n\t\t\t\t\t<groupId>commons-betwixt</groupId>\n\t\t\t\t\t<artifactId>commons-betwixt</artifactId>\n\t\t\t\t  </exclusion>\n\t\t\t\t</exclusions>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>com.alibaba</groupId>\n\t\t\t\t<artifactId>dubbo</artifactId>\n\t\t\t\t<version>2.6.9</version>\n\t\t\t\t<exclusions>\n\t\t\t\t  <exclusion>\n\t\t\t\t\t<groupId>org.springframework</groupId>\n\t\t\t\t\t<artifactId>spring</artifactId>\n\t\t\t\t  </exclusion>\n\t\t\t\t</exclusions>\n\t\t\t</dependency>\n\t\t</dependencies>\n\t</dependencyManagement>\n</project>\n"
  },
  {
    "path": "shared/push/pom.xml",
    "content": "<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\">\n  <modelVersion>4.0.0</modelVersion>\n  <parent>\n    <groupId>com.alibaba.otter</groupId>\n    <artifactId>shared</artifactId>\n    <version>4.2.19-SNAPSHOT</version>\n    <relativePath>../pom.xml</relativePath>\n  </parent>\n  <groupId>com.alibaba.otter</groupId>\n  <artifactId>shared.push</artifactId>\n  <packaging>jar</packaging>\n  <name>common push module for otter</name>\n  <url>http://github.com/alibaba/otter</url>\n\n  <dependencies>\n  \t<dependency>\n\t\t<groupId>com.alibaba.otter</groupId>\n\t\t<artifactId>shared.common</artifactId>\n\t\t<version>${project.version}</version>\n\t</dependency>\n\t<dependency>\n\t\t<groupId>com.alibaba.otter</groupId>\n\t\t<artifactId>shared.arbitrate</artifactId>\n\t\t<version>${project.version}</version>\n\t</dependency>\n\t\t<!-- test dependency -->\n\t\t<dependency>\n\t\t\t<groupId>junit</groupId>\n\t\t\t<artifactId>junit</artifactId>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.jtester</groupId>\n\t\t\t<artifactId>jtester</artifactId>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n  </dependencies>\n</project>\n"
  },
  {
    "path": "shared/push/src/main/java/com/alibaba/otter/common/push/AbstractSubscribeManager.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.common.push;\n\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.ConcurrentMap;\n\nimport org.apache.commons.lang.StringUtils;\n\nimport com.google.common.collect.HashMultimap;\nimport com.google.common.collect.SetMultimap;\n\n/**\n * 帮助子类管理callback的manager\n * \n * @author zebin.xuzb 2012-9-19 下午4:10:55\n * @version 4.1.0\n */\npublic abstract class AbstractSubscribeManager implements SubscribeManager {\n\n    private boolean                                inited      = false;\n    private ConcurrentMap<String, Object>          mutexes     = new ConcurrentHashMap<String, Object>();\n\n    private SetMultimap<String, SubscribeCallback> callBackMap = HashMultimap.create();\n\n    @Override\n    public synchronized void init() {\n        if (inited) {\n            return;\n        }\n        doInit();\n        inited = true;\n    }\n\n    protected abstract void doInit();\n\n    @Override\n    public synchronized void shutdown() {\n        if (!inited) {\n            return;\n        }\n        doShutdown();\n        inited = false;\n    }\n\n    protected abstract void doShutdown();\n\n    @Override\n    public void registerCallback(String dataId, SubscribeCallback callback) {\n        registerCallback(dataId, null, callback);\n    }\n\n    @Override\n    public void registerCallback(String dataId, String groupId, SubscribeCallback callback) {\n        String key = generateKey(dataId, groupId);\n        doRegisterCallback(dataId, groupId, callback, key);\n    }\n\n    private void doRegisterCallback(String dataId, String groupId, SubscribeCallback callback, String key) {\n        Object lock = getLock(dataId, groupId);\n        synchronized (lock) {\n            callBackMap.put(key, callback);\n            postRegisterCallback(dataId, groupId, callback);\n        }\n    }\n\n    @Override\n    public void unRegisterCallback(String dataId, SubscribeCallback callback) {\n        unRegisterCallback(dataId, null, callback);\n    }\n\n    @Override\n    public void unRegisterCallback(String dataId, String groupId, SubscribeCallback callback) {\n        String key = generateKey(dataId, groupId);\n        doUnregister(dataId, groupId, callback, key);\n    }\n\n    private void doUnregister(String dataId, String groupId, SubscribeCallback callback, String key) {\n        Object lock = getLock(key);\n        synchronized (lock) {\n            Set<SubscribeCallback> callbacks = callBackMap.get(key);\n            boolean effectRemoved = callbacks.remove(callback);\n            if (!effectRemoved) {\n                return;\n            }\n            if (callbacks.isEmpty()) {\n                doWhenCallbackEmpty(dataId, groupId, callback);\n            }\n\n        }\n    }\n\n    /**\n     * 当同一个 dataId-groupId 的callback 被清楚完成之后的动作。同于同一个 dataId-groupId，此方法不会并发\n     * \n     * @param dataId\n     * @param groupId\n     * @param callback\n     */\n    protected void doWhenCallbackEmpty(String dataId, String groupId, SubscribeCallback callback) {\n        // for subclass to close some resources\n    }\n\n    /**\n     * 注册监听器之后的方法，同于同一个 dataId-groupId，此方法不会并发\n     * \n     * @param dataId\n     * @param groupId\n     * @param callback\n     */\n    protected void postRegisterCallback(String dataId, String groupId, SubscribeCallback callback) {\n        // for subclass to extend\n    }\n\n    protected Set<SubscribeCallback> getCallbacks(String dataId) {\n        return getCallbacks(dataId, null);\n    }\n\n    protected Set<SubscribeCallback> getCallbacks(String dataId, String groupId) {\n        String key = generateKey(dataId, groupId);\n        return callBackMap.get(key);\n    }\n\n    protected static String generateKey(String dataId, String groupId) {\n        return StringUtils.trimToEmpty(dataId) + \"_\" + StringUtils.trimToEmpty(groupId);\n    }\n\n    protected Object getLock(String dataId, String groupId) {\n        String key = generateKey(dataId, groupId);\n        return getLock(key);\n    }\n\n    protected Object getLock(String key) {\n        Object lock = new Object();\n        Object expectLock = mutexes.putIfAbsent(key, lock);\n        if (expectLock == null) {\n            expectLock = lock;\n        }\n        return expectLock;\n    }\n\n}\n"
  },
  {
    "path": "shared/push/src/main/java/com/alibaba/otter/common/push/PushException.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.common.push;\n\nimport org.apache.commons.lang.exception.NestableRuntimeException;\n\n/**\n * @author zebin.xuzb 2013-1-23 下午2:07:28\n * @since 4.1.3\n */\npublic class PushException extends NestableRuntimeException {\n\n    private static final long serialVersionUID = -1223749329887228066L;\n\n    public PushException(){\n        super();\n    }\n\n    public PushException(String msg, Throwable cause){\n        super(msg, cause);\n    }\n\n    public PushException(String msg){\n        super(msg);\n    }\n\n    public PushException(Throwable cause){\n        super(cause);\n    }\n\n}\n"
  },
  {
    "path": "shared/push/src/main/java/com/alibaba/otter/common/push/SubscribeCallback.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.common.push;\n\n/**\n * @author zebin.xuzb 2012-9-19 下午3:29:16\n * @version 4.1.0\n */\npublic interface SubscribeCallback {\n\n    void callback(String changedInfo);\n}\n"
  },
  {
    "path": "shared/push/src/main/java/com/alibaba/otter/common/push/SubscribeManager.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.common.push;\n\n/**\n * @author zebin.xuzb 2013-1-23 下午2:01:06\n * @since 4.1.3\n */\npublic interface SubscribeManager {\n\n    void init();\n\n    void shutdown();\n\n    /**\n     * 注册监听dataId的callback，如果对应dataId的配置发生了变化，则回调callback。<br/>\n     * callback 的处理逻辑应该尽可能的快，如果callback处理的时间需要很长，建议采取异步的方式进行处理 <br/>\n     * 或者实现可以起一个线程池，帮助客户端吧callback做异步处理。\n     * \n     * @param dataId\n     * @param callback\n     */\n    void registerCallback(String dataId, SubscribeCallback callback);\n\n    /**\n     * 移除监听事件\n     * \n     * @param dataId\n     * @param callback\n     */\n    void unRegisterCallback(String dataId, SubscribeCallback callback);\n\n    /**\n     * 同 {@linkplain SubscribeManager#registerCallback}。<br/>\n     * groupId 也是组成配置key的部分，可以为<code>null</code>。\n     * \n     * @param dataId\n     * @param groupId\n     * @param callback\n     */\n    void registerCallback(String dataId, String groupId, SubscribeCallback callback);\n\n    /**\n     * 移除监听事件\n     * \n     * @param dataId\n     * @param groupId\n     * @param callback\n     */\n    void unRegisterCallback(String dataId, String groupId, SubscribeCallback callback);\n\n    /**\n     * 主动获取配置信息\n     * \n     * @param dataId\n     * @return 配置信息\n     */\n    String fetchConfig(String dataId);\n\n    /**\n     * 主动获取配置信息\n     * \n     * @param dataId\n     * @param timeout 超时时间，非正数代表无限长，单位毫秒\n     * @return 配置信息\n     */\n    String fetchConfig(String dataId, long timeout);\n\n    /**\n     * 同 {@linkplain SubscribeManager#fetchConfig(String)}。<br/>\n     * groupId 也是组成配置key的部分，可以为<code>null</code>。\n     * \n     * @param dataId\n     * @param groupId\n     * @return 配置信息\n     */\n    String fetchConfig(String dataId, String groupId);\n\n    /**\n     * 同 {@linkplain SubscribeManager#fetchConfig(String)}。<br/>\n     * groupId 也是组成配置key的部分，可以为<code>null</code>。\n     * \n     * @param dataId\n     * @param groupId\n     * @param timeout 超时时间，非正数代表无限长，单位毫秒\n     * @return 配置信息\n     */\n    String fetchConfig(String dataId, String groupId, long timeout);\n}\n"
  },
  {
    "path": "shared/push/src/main/java/com/alibaba/otter/common/push/SubscribeManagerFactory.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.common.push;\n\nimport java.util.Map;\n\nimport org.springframework.beans.BeansException;\nimport org.springframework.beans.factory.config.AutowireCapableBeanFactory;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.ApplicationContextAware;\n\nimport com.alibaba.otter.common.push.media.MediaSubscribeManager;\nimport com.google.common.base.Function;\nimport com.google.common.collect.OtterMigrateMap;\n\n/**\n * @author zebin.xuzb 2012-9-19 上午10:15:22\n * @version 4.1.0\n */\npublic class SubscribeManagerFactory implements ApplicationContextAware {\n\n    private static ApplicationContext                         context        = null;\n\n    private static final Map<SubscribeType, SubscribeManager> innerContainer = OtterMigrateMap.makeComputingMap(new Function<SubscribeType, SubscribeManager>() {\n\n                                                                                 @Override\n                                                                                 public SubscribeManager apply(SubscribeType input) {\n                                                                                     return createSubsrcibeManager(input);\n                                                                                 }\n                                                                             });\n\n    private static SubscribeManager createSubsrcibeManager(SubscribeType type) {\n        SubscribeManager manager = null;\n        if (SubscribeType.MEDIA.equals(type)) {\n            manager = new MediaSubscribeManager();\n            autowire(manager);\n        } else {\n            throw new PushException(\"can't createSubsrcibeManager, type : \" + type + \" is not supported yet\");\n        }\n\n        manager.init();\n        addShutdownForManager(manager);\n        return manager;\n    }\n\n    private static void addShutdownForManager(final SubscribeManager manager) {\n        Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {\n\n            @Override\n            public void run() {\n                manager.shutdown();\n            }\n        }));\n    }\n\n    /**\n     * 根据订阅类型获取相应的订阅管理器。每个订阅类型对应一个单例的订阅管理器。<br/>\n     * \n     * @param type\n     * @return 订阅管理器\n     * @see SubscribeManager\n     * @see SubscribeType\n     */\n    public static SubscribeManager getSubscribeManager(SubscribeType type) {\n        if (type == null) {\n            return null;\n        }\n        SubscribeManager manager = (SubscribeManager) innerContainer.get(type);\n        return manager;\n    }\n\n    public static void autowire(Object obj) {\n        // 重新注入一下对象\n        context.getAutowireCapableBeanFactory().autowireBeanProperties(obj,\n                                                                       AutowireCapableBeanFactory.AUTOWIRE_BY_NAME,\n                                                                       true);\n    }\n\n    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {\n        context = applicationContext;\n    }\n}\n"
  },
  {
    "path": "shared/push/src/main/java/com/alibaba/otter/common/push/SubscribeType.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.common.push;\n\n/**\n * @author zebin.xuzb 2012-9-19 下午3:31:07\n * @version 4.1.0\n */\npublic enum SubscribeType {\n    DIAMOND, MEDIA;\n}\n"
  },
  {
    "path": "shared/push/src/main/java/com/alibaba/otter/common/push/datasource/DataSourceHanlder.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.common.push.datasource;\n\nimport javax.sql.DataSource;\n\nimport com.alibaba.otter.shared.common.model.config.data.db.DbMediaSource;\n\n/**\n * 针对 {@link DataSourceService} 操作 {@link DataSource} 的扩展机制\n * \n * @author zebin.xuzb @ 2012-7-26\n * @version 4.1.0\n */\npublic interface DataSourceHanlder {\n\n    /**\n     * Handler 是否支持此配置的 dataSource\n     * \n     * @return\n     */\n    boolean support(DbMediaSource dbMediaSource);\n\n    /**\n     * Handler 是否支持此 dataSource\n     * \n     * @param dataSource\n     * @return\n     */\n    boolean support(DataSource dataSource);\n\n    /**\n     * 扩展功能,可以自定义一些自己实现的 {@link DataSource} <br/>\n     * 如果返回的 {@link DataSourceService} 不为空，则 {@link DataSourceService} 会采用此 {@link DataSourceService}\n     * \n     * @return\n     */\n    DataSource create(Long pipelineId, DbMediaSource dbMediaSource);\n\n    /**\n     * 扩展功能,可以在 {@link DataSource} 被 destroy 之前做一些事情<br/>\n     * 如果返回 <code>true</code>，则暗示此 dataSource 不会被后续流程 destroy. 通常 filter 可以自己 destroy 自己在 preFilter 产生的 dataSource.\n     * \n     * @param dataSource\n     * @return\n     */\n    boolean destory(Long pipelineId);\n\n}\n"
  },
  {
    "path": "shared/push/src/main/java/com/alibaba/otter/common/push/datasource/DataSourceKey.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.common.push.datasource;\n\nimport com.alibaba.otter.shared.common.model.config.data.DataMediaType;\n\n/**\n * @author zebin.xuzb 2013-1-23 下午6:46:26\n * @since 4.1.3\n */\npublic class DataSourceKey {\n\n    private String        url;\n    private String        userName;\n    private String        password;\n    private String        driverClassName;\n    private DataMediaType dataMediaType;\n    private String        encoding;\n\n    public static DataSourceKey getInstance(String url, String userName, String password, String driverClassName,\n                                            DataMediaType dataMediaType, String encoding) {\n        return new DataSourceKey(url, userName, password, driverClassName, dataMediaType, encoding);\n    }\n\n    public DataSourceKey(String url, String userName, String password, String driverClassName,\n                         DataMediaType dataMediaType, String encoding){\n        this.url = url;\n        this.userName = userName;\n        this.password = password;\n        this.driverClassName = driverClassName;\n        this.dataMediaType = dataMediaType;\n        this.encoding = encoding;\n    }\n\n    @Override\n    public int hashCode() {\n        final int prime = 31;\n        int result = 1;\n        result = prime * result + ((dataMediaType == null) ? 0 : dataMediaType.hashCode());\n        result = prime * result + ((driverClassName == null) ? 0 : driverClassName.hashCode());\n        result = prime * result + ((encoding == null) ? 0 : encoding.hashCode());\n        result = prime * result + ((password == null) ? 0 : password.hashCode());\n        result = prime * result + ((url == null) ? 0 : url.hashCode());\n        result = prime * result + ((userName == null) ? 0 : userName.hashCode());\n        return result;\n    }\n\n    @Override\n    public boolean equals(Object obj) {\n        if (this == obj) return true;\n        if (obj == null) return false;\n        if (getClass() != obj.getClass()) return false;\n        DataSourceKey other = (DataSourceKey) obj;\n        if (dataMediaType != other.dataMediaType) return false;\n        if (driverClassName == null) {\n            if (other.driverClassName != null) return false;\n        } else if (!driverClassName.equals(other.driverClassName)) return false;\n        if (encoding == null) {\n            if (other.encoding != null) return false;\n        } else if (!encoding.equals(other.encoding)) return false;\n        if (password == null) {\n            if (other.password != null) return false;\n        } else if (!password.equals(other.password)) return false;\n        if (url == null) {\n            if (other.url != null) return false;\n        } else if (!url.equals(other.url)) return false;\n        if (userName == null) {\n            if (other.userName != null) return false;\n        } else if (!userName.equals(other.userName)) return false;\n        return true;\n    }\n\n    public String getUrl() {\n        return url;\n    }\n\n    public String getUserName() {\n        return userName;\n    }\n\n    public String getPassword() {\n        return password;\n    }\n\n    public String getDriverClassName() {\n        return driverClassName;\n    }\n\n    public DataMediaType getDataMediaType() {\n        return dataMediaType;\n    }\n\n    public String getEncoding() {\n        return encoding;\n    }\n\n}\n"
  },
  {
    "path": "shared/push/src/main/java/com/alibaba/otter/common/push/datasource/media/MediaPushDataSource.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.common.push.datasource.media;\n\nimport java.io.PrintWriter;\nimport java.lang.reflect.InvocationTargetException;\nimport java.lang.reflect.Method;\nimport java.sql.Connection;\nimport java.sql.SQLException;\nimport java.sql.SQLFeatureNotSupportedException;\nimport java.util.Arrays;\n\nimport javax.sql.CommonDataSource;\nimport javax.sql.DataSource;\n\nimport org.apache.commons.dbcp.BasicDataSource;\nimport org.apache.commons.lang.StringUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.alibaba.otter.common.push.supplier.DatasourceChangeCallback;\nimport com.alibaba.otter.common.push.supplier.DatasourceInfo;\nimport com.alibaba.otter.common.push.supplier.DatasourceSupplier;\nimport com.alibaba.otter.common.push.supplier.media.MediaDatasourceSupplier;\nimport com.alibaba.otter.shared.common.model.config.data.DataMediaType;\n\n/**\n * media datasource support\n * \n * @author jianghang 2013-4-18 下午03:45:44\n * @version 4.1.8\n */\npublic class MediaPushDataSource implements DataSource {\n\n    private static final Logger logger                        = LoggerFactory.getLogger(MediaPushDataSource.class);\n\n    private volatile DataSource delegate;\n    private String              dbGroupKey;\n    private DatasourceSupplier  dataSourceSupplier;\n\n    private int                 maxWait                       = 60 * 1000;\n\n    private int                 minIdle                       = 0;\n\n    private int                 initialSize                   = 0;\n\n    private int                 maxActive                     = 32;\n\n    private int                 maxIdle                       = 32;\n\n    private int                 numTestsPerEvictionRun        = -1;\n\n    private int                 timeBetweenEvictionRunsMillis = 60 * 1000;\n\n    private int                 removeAbandonedTimeout        = 5 * 60;\n\n    private int                 minEvictableIdleTimeMillis    = 5 * 60 * 1000;\n\n    private String              originalUrl;\n    private String              userName;\n    private String              password;\n    private String              driverClassName;\n    private DataMediaType       dataMediaType;\n    private String              encoding;\n\n    public MediaPushDataSource(String originalUrl, String userName, String password, String driverClassName,\n                               DataMediaType dataMediaType, String encoding){\n        this.originalUrl = originalUrl;\n        this.userName = userName;\n        this.password = password;\n        this.driverClassName = driverClassName;\n        this.dataMediaType = dataMediaType;\n        this.encoding = encoding;\n    }\n\n    public synchronized void init() {\n        if (!dataMediaType.isMysql()) {\n            throw new UnsupportedOperationException(\"currently only support mysql type\");\n        }\n\n        if (delegate != null) {\n            return;\n        }\n        if (dataSourceSupplier == null) {\n            dataSourceSupplier = MediaDatasourceSupplier.newInstance(dbGroupKey);\n            dataSourceSupplier.start();\n            dataSourceSupplier.addSwtichCallback(new DatasourceChangeCallback() {\n\n                public void masterChanged(DatasourceInfo newMaster) {\n                    String newUrl = buildMysqlUrl(newMaster.getAddress().getAddress().getHostAddress(),\n                                                  newMaster.getAddress().getPort());\n                    try {\n                        ((BasicDataSource) delegate).close();\n                        DataSource newDelegate = doCreateDataSource(newUrl);\n                        delegate = newDelegate;\n                    } catch (SQLException e) {\n                        logger.error(\"switch master error with url : \" + originalUrl, e);\n                    }\n\n                }\n            });\n        }\n\n        DatasourceInfo datasourceInfo = dataSourceSupplier.fetchMaster();\n        String url = buildMysqlUrl(datasourceInfo.getAddress().getAddress().getHostAddress(),\n                                   datasourceInfo.getAddress().getPort());\n\n        delegate = doCreateDataSource(url);\n    }\n\n    private String buildMysqlUrl(String hostIp, int port) {\n        StringBuilder sb = new StringBuilder(\"jdbc:mysql://\");\n        sb.append(hostIp).append(\":\").append(port);\n        return sb.toString();\n    }\n\n    protected DataSource doCreateDataSource(String url) {\n        BasicDataSource dbcpDs = new BasicDataSource();\n\n        dbcpDs.setInitialSize(initialSize);// 初始化连接池时创建的连接数\n        dbcpDs.setMaxActive(maxActive);// 连接池允许的最大并发连接数，值为非正数时表示不限制\n        dbcpDs.setMaxIdle(maxIdle);// 连接池中的最大空闲连接数，超过时，多余的空闲连接将会被释放，值为负数时表示不限制\n        dbcpDs.setMinIdle(minIdle);// 连接池中的最小空闲连接数，低于此数值时将会创建所欠缺的连接，值为0时表示不创建\n        dbcpDs.setMaxWait(maxWait);// 以毫秒表示的当连接池中没有可用连接时等待可用连接返回的时间，超时则抛出异常，值为-1时表示无限等待\n        dbcpDs.setRemoveAbandoned(true);// 是否清除已经超过removeAbandonedTimeout设置的无效连接\n        dbcpDs.setLogAbandoned(true);// 当清除无效链接时是否在日志中记录清除信息的标志\n        dbcpDs.setRemoveAbandonedTimeout(removeAbandonedTimeout); // 以秒表示清除无效链接的时限\n        dbcpDs.setNumTestsPerEvictionRun(numTestsPerEvictionRun);// 确保连接池中没有已破损的连接\n        dbcpDs.setTestOnBorrow(false);// 指定连接被调用时是否经过校验\n        dbcpDs.setTestOnReturn(false);// 指定连接返回到池中时是否经过校验\n        dbcpDs.setTestWhileIdle(true);// 指定连接进入空闲状态时是否经过空闲对象驱逐进程的校验\n        dbcpDs.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis); // 以毫秒表示空闲对象驱逐进程由运行状态进入休眠状态的时长，值为非正数时表示不运行任何空闲对象驱逐进程\n        dbcpDs.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis); // 以毫秒表示连接被空闲对象驱逐进程驱逐前在池中保持空闲状态的最小时间\n\n        // 动态的参数\n        dbcpDs.setDriverClassName(driverClassName);\n        dbcpDs.setUrl(url);\n        dbcpDs.setUsername(userName);\n        dbcpDs.setPassword(password);\n\n        if (dataMediaType.isOracle()) {\n            dbcpDs.addConnectionProperty(\"restrictGetTables\", \"true\");\n            dbcpDs.setValidationQuery(\"select 1 from dual\");\n        } else if (dataMediaType.isMysql()) {\n            // open the batch mode for mysql since 5.1.8\n            dbcpDs.addConnectionProperty(\"useServerPrepStmts\", \"false\");\n            dbcpDs.addConnectionProperty(\"rewriteBatchedStatements\", \"true\");\n            dbcpDs.addConnectionProperty(\"zeroDateTimeBehavior\", \"convertToNull\");// 将0000-00-00的时间类型返回null\n            dbcpDs.addConnectionProperty(\"yearIsDateType\", \"false\");// 直接返回字符串，不做year转换date处理\n            dbcpDs.addConnectionProperty(\"noDatetimeStringSync\", \"true\");// 返回时间类型的字符串,不做时区处理\n            dbcpDs.addConnectionProperty(\"jdbcCompliantTruncation\", \"false\");// 允许sqlMode为非严格模式\n            if (StringUtils.isNotEmpty(encoding)) {\n                if (StringUtils.equalsIgnoreCase(encoding, \"utf8mb4\")) {\n                    dbcpDs.addConnectionProperty(\"characterEncoding\", \"utf8\");\n                    dbcpDs.setConnectionInitSqls(Arrays.asList(\"set names utf8mb4\"));\n                } else {\n                    dbcpDs.addConnectionProperty(\"characterEncoding\", encoding);\n                }\n            }\n            dbcpDs.setValidationQuery(\"select 1\");\n        } else {\n            logger.error(\"ERROR ## Unknow database type\");\n        }\n\n        return dbcpDs;\n    }\n\n    public synchronized void destory() throws SQLException {\n        if (delegate != null) {\n            BasicDataSource basicDataSource = (BasicDataSource) delegate;\n            basicDataSource.close();\n            delegate = null;\n        }\n        if (dataSourceSupplier != null) {\n            dataSourceSupplier.stop();\n            dataSourceSupplier = null;\n        }\n    }\n\n    @Override\n    public PrintWriter getLogWriter() throws SQLException {\n        return delegate.getLogWriter();\n    }\n\n    @Override\n    public void setLogWriter(PrintWriter out) throws SQLException {\n        delegate.setLogWriter(out);\n    }\n\n    @Override\n    public void setLoginTimeout(int seconds) throws SQLException {\n        delegate.setLoginTimeout(seconds);\n\n    }\n\n    @Override\n    public int getLoginTimeout() throws SQLException {\n        return delegate.getLoginTimeout();\n    }\n\n    @Override\n    public <T> T unwrap(Class<T> iface) throws SQLException {\n        return delegate.unwrap(iface);\n    }\n\n    @Override\n    public boolean isWrapperFor(Class<?> iface) throws SQLException {\n        return delegate.isWrapperFor(iface);\n    }\n\n    @Override\n    public Connection getConnection() throws SQLException {\n        return delegate.getConnection();\n    }\n\n    @Override\n    public Connection getConnection(String username, String password) throws SQLException {\n        return delegate.getConnection(username, password);\n    }\n\n    // implemented from JDK7 @see http://docs.oracle.com/javase/7/docs/api/javax/sql/CommonDataSource.html#getParentLogger()\n    public java.util.logging.Logger getParentLogger() throws SQLFeatureNotSupportedException {\n        try {\n            Method getParentLoggerMethod = CommonDataSource.class.getDeclaredMethod(\"getParentLogger\", new Class<?>[0]);\n            return (java.util.logging.Logger) getParentLoggerMethod.invoke(delegate, new Object[0]);\n        } catch (NoSuchMethodException e) {\n            throw new SQLFeatureNotSupportedException(e);\n        } catch (InvocationTargetException e2) {\n            throw new SQLFeatureNotSupportedException(e2);\n        } catch (IllegalArgumentException e2) {\n            throw new SQLFeatureNotSupportedException(e2);\n        } catch (IllegalAccessException e2) {\n            throw new SQLFeatureNotSupportedException(e2);\n        }\n    }\n\n    // =============== setter & getter ================\n    public DataSource getDelegate() {\n        return delegate;\n    }\n\n    public String getDbGroupKey() {\n        return dbGroupKey;\n    }\n\n    public int getMaxWait() {\n        return maxWait;\n    }\n\n    public int getMinIdle() {\n        return minIdle;\n    }\n\n    public int getInitialSize() {\n        return initialSize;\n    }\n\n    public int getMaxActive() {\n        return maxActive;\n    }\n\n    public int getMaxIdle() {\n        return maxIdle;\n    }\n\n    public int getNumTestsPerEvictionRun() {\n        return numTestsPerEvictionRun;\n    }\n\n    public int getTimeBetweenEvictionRunsMillis() {\n        return timeBetweenEvictionRunsMillis;\n    }\n\n    public int getRemoveAbandonedTimeout() {\n        return removeAbandonedTimeout;\n    }\n\n    public int getMinEvictableIdleTimeMillis() {\n        return minEvictableIdleTimeMillis;\n    }\n\n    public String getOriginalUrl() {\n        return originalUrl;\n    }\n\n    public String getUserName() {\n        return userName;\n    }\n\n    public String getPassword() {\n        return password;\n    }\n\n    public String getDriverClassName() {\n        return driverClassName;\n    }\n\n    public DataMediaType getDataMediaType() {\n        return dataMediaType;\n    }\n\n    public String getEncoding() {\n        return encoding;\n    }\n\n    public void setDelegate(DataSource delegate) {\n        this.delegate = delegate;\n    }\n\n    public void setDbGroupKey(String dbGroupKey) {\n        this.dbGroupKey = dbGroupKey;\n    }\n\n    public void setMaxWait(int maxWait) {\n        this.maxWait = maxWait;\n    }\n\n    public void setMinIdle(int minIdle) {\n        this.minIdle = minIdle;\n    }\n\n    public void setInitialSize(int initialSize) {\n        this.initialSize = initialSize;\n    }\n\n    public void setMaxActive(int maxActive) {\n        this.maxActive = maxActive;\n    }\n\n    public void setMaxIdle(int maxIdle) {\n        this.maxIdle = maxIdle;\n    }\n\n    public void setNumTestsPerEvictionRun(int numTestsPerEvictionRun) {\n        this.numTestsPerEvictionRun = numTestsPerEvictionRun;\n    }\n\n    public void setTimeBetweenEvictionRunsMillis(int timeBetweenEvictionRunsMillis) {\n        this.timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis;\n    }\n\n    public void setRemoveAbandonedTimeout(int removeAbandonedTimeout) {\n        this.removeAbandonedTimeout = removeAbandonedTimeout;\n    }\n\n    public void setMinEvictableIdleTimeMillis(int minEvictableIdleTimeMillis) {\n        this.minEvictableIdleTimeMillis = minEvictableIdleTimeMillis;\n    }\n\n    public void setOriginalUrl(String originalUrl) {\n        this.originalUrl = originalUrl;\n    }\n\n    public void setUserName(String userName) {\n        this.userName = userName;\n    }\n\n    public void setPassword(String password) {\n        this.password = password;\n    }\n\n    public void setDriverClassName(String driverClassName) {\n        this.driverClassName = driverClassName;\n    }\n\n    public void setDataMediaType(DataMediaType dataMediaType) {\n        this.dataMediaType = dataMediaType;\n    }\n\n    public void setEncoding(String encoding) {\n        this.encoding = encoding;\n    }\n\n}\n"
  },
  {
    "path": "shared/push/src/main/java/com/alibaba/otter/common/push/datasource/media/MediaPushDataSourceHandler.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.common.push.datasource.media;\n\nimport java.sql.SQLException;\nimport java.util.Map;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\nimport javax.sql.DataSource;\n\nimport org.apache.commons.lang.StringUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.alibaba.otter.common.push.datasource.DataSourceHanlder;\nimport com.alibaba.otter.shared.common.model.config.data.DataMediaType;\nimport com.alibaba.otter.shared.common.model.config.data.db.DbMediaSource;\nimport com.google.common.base.Function;\nimport com.google.common.collect.OtterMigrateMap;\n\n/**\n * media group 的 url 为： jdbc:mysql://groupKey=xxx\n * \n * @author jianghang 2013-4-18 下午03:33:28\n * @version 4.1.8\n */\npublic class MediaPushDataSourceHandler implements DataSourceHanlder {\n\n    private static final Logger                       log     = LoggerFactory.getLogger(MediaPushDataSourceHandler.class);\n\n    private static final Pattern                      PATTERN = Pattern.compile(\"jdbc:mysql://groupKey=([^&/]+).*\",\n                                                                                Pattern.CASE_INSENSITIVE);\n\n    /**\n     * 一个pipeline下面有一组DataSource.<br>\n     * key = pipelineId<br>\n     * value = key(dataMediaSourceId)-value(DataSource)<br>\n     */\n    private Map<Long, Map<DbMediaSource, DataSource>> dataSources;\n\n    public MediaPushDataSourceHandler(){\n        // 构建第一层map\n        dataSources = OtterMigrateMap.makeComputingMap(new Function<Long, Map<DbMediaSource, DataSource>>() {\n\n            public Map<DbMediaSource, DataSource> apply(Long pipelineId) {\n                // 构建第二层map\n                return OtterMigrateMap.makeComputingMap(new Function<DbMediaSource, DataSource>() {\n\n                    public DataSource apply(DbMediaSource dbMediaSource) {\n                        return createDataSource(dbMediaSource.getUrl(), dbMediaSource.getUsername(),\n                                                dbMediaSource.getPassword(), dbMediaSource.getDriver(),\n                                                dbMediaSource.getType(), dbMediaSource.getEncode());\n                    }\n\n                });\n            }\n        });\n    }\n\n    public boolean support(DbMediaSource dbMediaSource) {\n        return isMediaPushDataSource(dbMediaSource.getUrl());\n    }\n\n    public boolean support(DataSource dataSource) {\n        if (dataSource == null) {\n            return false;\n        }\n        return dataSource instanceof MediaPushDataSource;\n    }\n\n    public DataSource create(Long pipelineId, DbMediaSource dbMediaSource) {\n        return dataSources.get(pipelineId).get(dbMediaSource);\n    }\n\n    protected DataSource createDataSource(String url, String userName, String password, String driverClassName,\n                                          DataMediaType dataMediaType, String encoding) {\n        MediaInfo media = parseMediaInfo(url);\n        if (media == null) {\n            if (isMediaPushDataSource(url)) {\n                log.error(\"{} can't parse as an media groupdatasource, please check!\", url);\n            } else {\n                log.info(\"{} is not a media datasource\", url);\n            }\n            return null;\n        }\n\n        String groupKey = media.getGroupKey();\n        MediaPushDataSource mediaDataSource = new MediaPushDataSource(url, userName, password, driverClassName,\n                                                                      dataMediaType, encoding);\n        mediaDataSource.setDbGroupKey(groupKey);\n        mediaDataSource.init();\n        return mediaDataSource;\n    }\n\n    @Override\n    public boolean destory(Long pipelineId) {\n        Map<DbMediaSource, DataSource> sources = dataSources.remove(pipelineId);\n        if (sources != null) {\n            for (DataSource dataSource : sources.values()) {\n                try {\n                    MediaPushDataSource mediaPushDataSource = (MediaPushDataSource) dataSource;\n                    mediaPushDataSource.destory();\n                } catch (SQLException e) {\n                    log.error(\"ERROR ## close the datasource has an error\", e);\n                }\n            }\n\n            sources.clear();\n        }\n\n        return true;\n    }\n\n    public static boolean isMediaPushDataSource(String url) {\n        return StringUtils.startsWithIgnoreCase(url, \"jdbc:\") && StringUtils.containsIgnoreCase(url, \"groupKey\");\n    }\n\n    // 解析 url\n    public static MediaInfo parseMediaInfo(String url) {\n        if (StringUtils.isEmpty(url)) {\n            return null;\n        }\n        Matcher matcher = PATTERN.matcher(url.trim());\n        if (!matcher.matches()) {\n            return null;\n        }\n\n        if (matcher.groupCount() < 1) {\n            throw new IllegalArgumentException(url\n                                               + \" is a media push datasource but have no enough info for groupKey.\");\n        }\n        return new MediaInfo(matcher.group(1));\n    }\n\n    public static class MediaInfo {\n\n        String groupKey;\n\n        public MediaInfo(String groupKey){\n            this.groupKey = groupKey;\n        }\n\n        public String getGroupKey() {\n            return groupKey;\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "shared/push/src/main/java/com/alibaba/otter/common/push/media/MediaSubscribeManager.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.common.push.media;\n\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.ConcurrentMap;\nimport java.util.concurrent.ScheduledThreadPoolExecutor;\nimport java.util.concurrent.ThreadPoolExecutor;\nimport java.util.concurrent.TimeUnit;\n\nimport org.apache.commons.lang.StringUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.alibaba.otter.common.push.AbstractSubscribeManager;\nimport com.alibaba.otter.common.push.SubscribeCallback;\nimport com.alibaba.otter.shared.arbitrate.impl.communication.ArbitrateCommmunicationClient;\nimport com.alibaba.otter.shared.common.model.config.ConfigException;\nimport com.alibaba.otter.shared.common.utils.cache.RefreshMemoryMirror;\nimport com.alibaba.otter.shared.common.utils.cache.RefreshMemoryMirror.ComputeFunction;\nimport com.alibaba.otter.shared.common.utils.thread.NamedThreadFactory;\nimport com.alibaba.otter.shared.communication.model.config.FindMediaEvent;\n\n/**\n * 基于otter manager media的管理\n * \n * @author jianghang 2013-4-18 下午12:11:53\n * @version 4.1.8\n */\npublic class MediaSubscribeManager extends AbstractSubscribeManager {\n\n    private static final Long                   DEFAULT_PERIOD = 60 * 1000L;\n    private static final Logger                 logger         = LoggerFactory.getLogger(MediaSubscribeManager.class);\n    private ConcurrentMap<String, Object>       mutexes        = new ConcurrentHashMap<String, Object>();\n    private Map<String, Runnable>               runnableMap    = new ConcurrentHashMap<String, Runnable>();\n    private Long                                timeout        = DEFAULT_PERIOD;\n    private RefreshMemoryMirror<String, String> matrixCache;\n    private ArbitrateCommmunicationClient       arbitrateCommmunicationClient;\n\n    private int                                 poolSize       = 8;\n    private ScheduledThreadPoolExecutor         executor;\n\n    public MediaSubscribeManager(){\n        // 注册一下事件处理\n        ComputeFunction function = new ComputeFunction<String, String>() {\n\n            public String apply(final String key, String oldValue) {\n                FindMediaEvent event = new FindMediaEvent();\n                event.setDataId(key);\n                try {\n                    Object obj = arbitrateCommmunicationClient.callManager(event);\n                    if (obj != null && obj instanceof String) {\n                        final String value = (String) obj;\n                        if (!StringUtils.equalsIgnoreCase(oldValue, value)) {\n                            // 触发一下变化\n                            executor.submit(new Runnable() {\n\n                                public void run() {\n                                    Set<SubscribeCallback> callbacks = getCallbacks(key, null);\n                                    for (SubscribeCallback callback : callbacks) {\n                                        callback.callback(value);\n                                    }\n                                }\n                            });\n                        }\n                        return value;\n                    } else {\n                        throw new ConfigException(\"No Such dataId[\" + key + \"]\");\n                    }\n                } catch (Exception e) {\n                    logger.error(\"call_manager_error\", event.toString(), e);\n                }\n                // 其他情况直接返回内存中的旧值\n                return oldValue;\n            }\n\n        };\n        matrixCache = new RefreshMemoryMirror<String, String>(timeout, function);\n    }\n\n    protected void doInit() {\n        if (executor != null) {\n            return;\n        }\n\n        executor = new ScheduledThreadPoolExecutor(poolSize, new NamedThreadFactory(\"canal-media-callback-worker\"),\n                                                   new ThreadPoolExecutor.CallerRunsPolicy());\n    }\n\n    protected void doShutdown() {\n        if (executor != null) {\n            return;\n        }\n        executor.shutdown();\n    }\n\n    public String fetchConfig(String dataId) {\n        return fetchConfig(dataId, 0);\n    }\n\n    public String fetchConfig(String dataId, long timeout) {\n        return matrixCache.get(dataId);\n    }\n\n    public String fetchConfig(String dataId, String groupId) {\n        return fetchConfig(dataId);\n    }\n\n    public String fetchConfig(String dataId, String groupId, long timeout) {\n        return fetchConfig(dataId, 0);\n    }\n\n    protected void postRegisterCallback(String dataId, String groupId, SubscribeCallback callback) {\n        String key = generateKey(dataId, groupId);\n        Object lock = new Object();\n\n        // 原子性的获取一个锁\n        lock = mutexes.putIfAbsent(key, lock);\n        if (lock == null) {\n            lock = mutexes.get(key);\n\n            synchronized (lock) {\n                submitSchedule(dataId, groupId);\n            }\n        }\n    }\n\n    protected void doWhenCallbackEmpty(String dataId, String groupId, SubscribeCallback callback) {\n        String key = generateKey(dataId, groupId);\n        Object lock = new Object();\n\n        // 原子性的获取一个锁\n        lock = mutexes.putIfAbsent(key, lock);\n        if (lock == null) {\n            lock = mutexes.get(key);\n        }\n\n        synchronized (lock) {\n            colseSchedule(dataId, groupId);\n            matrixCache.remove(dataId);\n        }\n    }\n\n    private void submitSchedule(final String dataId, final String groupId) {\n        String key = generateKey(dataId, groupId);\n        Runnable runnable = runnableMap.get(key);\n        if (runnable == null) {\n            runnable = new Runnable() {\n\n                public void run() {\n                    try {\n                        matrixCache.get(dataId);\n                    } catch (Throwable e) {\n                        logger.error(\"reload failed\", e);\n                    }\n                }\n            };\n\n            runnableMap.put(key, runnable);\n            executor.scheduleAtFixedRate(runnable, timeout, timeout, TimeUnit.MILLISECONDS);\n        }\n    }\n\n    private void colseSchedule(final String dataId, final String groupId) {\n        String key = generateKey(dataId, groupId);\n        Runnable runnable = runnableMap.remove(key);\n        if (runnable != null) {\n            executor.remove(runnable);\n        }\n    }\n\n    public void setArbitrateCommmunicationClient(ArbitrateCommmunicationClient arbitrateCommmunicationClient) {\n        this.arbitrateCommmunicationClient = arbitrateCommmunicationClient;\n    }\n\n}\n"
  },
  {
    "path": "shared/push/src/main/java/com/alibaba/otter/common/push/supplier/AbstractDatasourceSupplier.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.common.push.supplier;\n\n/**\n * @author zebin.xuzb 2013-1-23 下午5:08:51\n * @since 4.1.3\n */\npublic abstract class AbstractDatasourceSupplier implements DatasourceSupplier {\n\n    private Object             lock    = new Object();\n    protected volatile boolean running = false;\n\n    @Override\n    public void start() {\n        synchronized (lock) {\n            if (isStart()) {\n                return;\n            }\n            doStart();\n            running = true;\n        }\n    }\n\n    @Override\n    public void stop() {\n        synchronized (lock) {\n            if (!isStart()) {\n                return;\n            }\n            doStop();\n            running = false;\n        }\n    }\n\n    @Override\n    public boolean isStart() {\n        return running;\n    }\n\n    protected abstract void doStart();\n\n    protected abstract void doStop();\n}\n"
  },
  {
    "path": "shared/push/src/main/java/com/alibaba/otter/common/push/supplier/DatasourceChangeCallback.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.common.push.supplier;\n\n/**\n * callback 处理的时间请尽量短，如果时间太长，请使用异步\n * \n * @author zebin.xuzb 2012-9-25 下午12:14:28\n * @version 4.1.0\n */\npublic interface DatasourceChangeCallback {\n\n    void masterChanged(DatasourceInfo newMaster);\n\n}\n"
  },
  {
    "path": "shared/push/src/main/java/com/alibaba/otter/common/push/supplier/DatasourceInfo.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.common.push.supplier;\n\nimport java.net.InetSocketAddress;\n\nimport org.apache.commons.lang.builder.ToStringBuilder;\nimport org.apache.commons.lang.builder.ToStringStyle;\n\n/**\n * @author zebin.xuzb 2013-1-23 下午4:42:48\n * @since 4.1.3\n */\npublic class DatasourceInfo {\n\n    private InetSocketAddress address;            // 主库信息\n    private String            username;           // 帐号\n    private String            password;           // 密码\n    private String            defaultDatabaseName; // 默认链接的数据库\n\n    public DatasourceInfo(){\n        super();\n    }\n\n    public DatasourceInfo(InetSocketAddress address, String username, String password){\n        this(address, username, password, \"\");\n    }\n\n    public DatasourceInfo(InetSocketAddress address, String username, String password, String defaultDatabaseName){\n        this.address = address;\n        this.username = username;\n        this.password = password;\n        this.defaultDatabaseName = defaultDatabaseName;\n    }\n\n    public InetSocketAddress getAddress() {\n        return address;\n    }\n\n    public void setAddress(InetSocketAddress address) {\n        this.address = address;\n    }\n\n    public String getUsername() {\n        return username;\n    }\n\n    public void setUsername(String username) {\n        this.username = username;\n    }\n\n    public String getPassword() {\n        return password;\n    }\n\n    public void setPassword(String password) {\n        this.password = password;\n    }\n\n    public String getDefaultDatabaseName() {\n        return defaultDatabaseName;\n    }\n\n    public void setDefaultDatabaseName(String defaultDatabaseName) {\n        this.defaultDatabaseName = defaultDatabaseName;\n    }\n\n    @Override\n    public String toString() {\n        return ToStringBuilder.reflectionToString(this, ToStringStyle.DEFAULT_STYLE);\n    }\n}\n"
  },
  {
    "path": "shared/push/src/main/java/com/alibaba/otter/common/push/supplier/DatasourceSupplier.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.common.push.supplier;\n\n/**\n * @author zebin.xuzb 2012-9-24 上午11:29:01\n * @version 4.1.0\n */\npublic interface DatasourceSupplier {\n\n    public void start();\n\n    public void stop();\n\n    public boolean isStart();\n\n    /**\n     * 客户端可以主动获取 master 的 {@linkplain AuthenticationInfo}\n     * \n     * @return\n     */\n    DatasourceInfo fetchMaster();\n\n    /**\n     * 客户端可以注册数据库连接发生变更的callback\n     * \n     * @param callback\n     */\n    void addSwtichCallback(DatasourceChangeCallback callback);\n\n}\n"
  },
  {
    "path": "shared/push/src/main/java/com/alibaba/otter/common/push/supplier/HaDatasourceInfo.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.common.push.supplier;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.List;\n\n/**\n * @author zebin.xuzb 2013-1-23 下午4:45:44\n * @since 4.1.3\n */\npublic class HaDatasourceInfo {\n\n    private DatasourceInfo       master;\n    private List<DatasourceInfo> slavers = new ArrayList<DatasourceInfo>();\n\n    public DatasourceInfo getMaster() {\n        return master;\n    }\n\n    public void setMaster(DatasourceInfo master) {\n        this.master = master;\n    }\n\n    public List<DatasourceInfo> getSlavers() {\n        return slavers;\n    }\n\n    public void addSlaver(DatasourceInfo slaver) {\n        this.slavers.add(slaver);\n    }\n\n    public void addSlavers(Collection<DatasourceInfo> slavers) {\n        this.slavers.addAll(slavers);\n    }\n}\n"
  },
  {
    "path": "shared/push/src/main/java/com/alibaba/otter/common/push/supplier/media/MediaDatasourceSupplier.java",
    "content": "/*\n * Copyright (C) 2010-2101 Alibaba Group Holding Limited.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.alibaba.otter.common.push.supplier.media;\n\nimport java.net.InetSocketAddress;\nimport java.util.HashMap;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.atomic.AtomicInteger;\n\nimport org.apache.commons.lang.StringUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.alibaba.otter.common.push.PushException;\nimport com.alibaba.otter.common.push.SubscribeCallback;\nimport com.alibaba.otter.common.push.SubscribeManager;\nimport com.alibaba.otter.common.push.SubscribeManagerFactory;\nimport com.alibaba.otter.common.push.SubscribeType;\nimport com.alibaba.otter.common.push.supplier.AbstractDatasourceSupplier;\nimport com.alibaba.otter.common.push.supplier.DatasourceChangeCallback;\nimport com.alibaba.otter.common.push.supplier.DatasourceInfo;\nimport com.alibaba.otter.common.push.supplier.HaDatasourceInfo;\nimport com.alibaba.otter.shared.common.utils.JsonUtils;\n\n/**\n * 基于data media机制提供的数据源信息\n * \n * @author jianghang 2013-4-18 下午03:03:26\n * @version 4.1.8\n */\npublic class MediaDatasourceSupplier extends AbstractDatasourceSupplier {\n\n    private static final Logger            log          = LoggerFactory.getLogger(MediaDatasourceSupplier.class);\n    private static AtomicInteger           CHANGED_TIME = new AtomicInteger(0);\n\n    private SubscribeManager               mediaSubscribeManager;\n\n    private String                         groupKey;\n    private HaDatasourceInfo               haInfo;\n\n    private List<DatasourceChangeCallback> callbacks    = new LinkedList<DatasourceChangeCallback>();\n\n    private Object                         lock         = new Object();\n    private SubscribeCallback              subscribeCallback;\n\n    private MediaDatasourceSupplier(){\n    }\n\n    private MediaDatasourceSupplier(String groupKey){\n        this.groupKey = groupKey;\n    }\n\n    public static MediaDatasourceSupplier newInstance(String groupKey) {\n        MediaDatasourceSupplier supplier = new MediaDatasourceSupplier(groupKey);\n        return supplier;\n    }\n\n    public void doStart() {\n        subscribeCallback = new SubscribeCallback() {\n\n            @Override\n            public void callback(String matrixInfo) {\n\n                log.warn(\"has received changed ds [{}] for [{}] times\", matrixInfo, CHANGED_TIME.addAndGet(1));\n                synchronized (MediaDatasourceSupplier.this.lock) {\n                    MediaDatasourceSupplier.this.haInfo = parse(matrixInfo);\n                    MediaDatasourceSupplier.this.callback();\n                }\n            }\n        };\n        this.init();\n    }\n\n    @Override\n    public void doStop() {\n        this.mediaSubscribeManager.unRegisterCallback(this.groupKey, subscribeCallback);\n        this.callbacks.clear();\n    }\n\n    public synchronized void init() {\n        this.mediaSubscribeManager = SubscribeManagerFactory.getSubscribeManager(SubscribeType.MEDIA);\n        if (this.mediaSubscribeManager == null) {\n            throw new PushException(\"MediaDatasourceSupplier : mediaSubscribeManager is null, check the spring config\");\n        }\n\n        String matrixStr = mediaSubscribeManager.fetchConfig(groupKey);\n        this.haInfo = parse(matrixStr);\n        this.mediaSubscribeManager.registerCallback(this.groupKey, subscribeCallback);\n    }\n\n    public DatasourceInfo fetchMaster() {\n        synchronized (lock) {\n            if (this.haInfo == null) {\n                throw new PushException(\"haInfo is null, check the init phase\");\n            }\n            return this.haInfo.getMaster();\n        }\n    }\n\n    public void addSwtichCallback(DatasourceChangeCallback callback) {\n        callbacks.add(callback);\n    }\n\n    private void callback() {\n        if (callbacks == null || callbacks.size() == 0) {\n            return;\n        }\n\n        for (DatasourceChangeCallback callback : callbacks) {\n            callback.masterChanged(MediaDatasourceSupplier.this.fetchMaster());\n        }\n\n    }\n\n    private HaDatasourceInfo parse(String matrixStr) {\n        HaDatasourceInfo haInfo = new HaDatasourceInfo();\n\n        Map jsonMap = JsonUtils.unmarshalFromString(matrixStr, HashMap.class);\n        String masterAddress = (String) jsonMap.get(\"master\");\n        if (masterAddress != null) {\n            DatasourceInfo master = new DatasourceInfo();\n            master.setAddress(parseAddress(masterAddress));\n            haInfo.setMaster(master);\n        }\n\n        String slaveAddress = (String) jsonMap.get(\"slave\");\n        if (slaveAddress != null) {\n            DatasourceInfo slave = new DatasourceInfo();\n            slave.setAddress(parseAddress(slaveAddress));\n            haInfo.getSlavers().add(slave);\n        }\n\n        return haInfo;\n    }\n\n    private InetSocketAddress parseAddress(String address) {\n        String[] strs = StringUtils.split(address, \":\");\n        if (strs.length != 2) {\n            throw new IllegalArgumentException(\"illegal address format:\" + address);\n        }\n\n        return new InetSocketAddress(strs[0], Integer.valueOf(strs[1]));\n    }\n\n}\n"
  },
  {
    "path": "shared/push/src/main/resources/spring/otter-push-common.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<beans xmlns=\"http://www.springframework.org/schema/beans\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n    xmlns:tx=\"http://www.springframework.org/schema/tx\" xmlns:aop=\"http://www.springframework.org/schema/aop\"\n    xmlns:lang=\"http://www.springframework.org/schema/lang\" xmlns:context=\"http://www.springframework.org/schema/context\"\n    xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd\n           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd\n           http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-2.0.xsd\n           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd\n           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd\"\n    default-autowire=\"byName\" >\n\t\n\t<!-- 初始化一下ArbitrateFactory，注入spring容器 -->\n\t<bean name=\"subscribeManagerFactory\" class=\"com.alibaba.otter.common.push.SubscribeManagerFactory\" lazy-init=\"false\" scope=\"singleton\"/>\n</beans>"
  }
]