diff --git a/.gitignore b/.gitignore
new file mode 100644
index 00000000..04a5ecc2
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,16 @@
+.svn/
+target/
+test-output/
+*.class
+.classpath
+.project
+.settings/
+tmp
+temp
+*.log
+antx.properties
+.idea/
+*.iml
+htdocs
+download
+package-info.java
diff --git a/HEADER.txt b/HEADER.txt
new file mode 100644
index 00000000..9adc0d59
--- /dev/null
+++ b/HEADER.txt
@@ -0,0 +1,15 @@
+/*
+ * Copyright (C) 2010-2101 Alibaba Group Holding Limited.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644
index 00000000..75b52484
--- /dev/null
+++ b/LICENSE.txt
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/README.md b/README.md
new file mode 100644
index 00000000..d37d90c2
--- /dev/null
+++ b/README.md
@@ -0,0 +1,72 @@
+
+ 项目背景
+
+ 阿里巴巴B2B公司,因为业务的特性,卖家主要集中在国内,买家主要集中在国外,所以衍生出了杭州和美国异地机房的需求,同时为了提升用户体验,整个机房的架构为双A,两边均可写,由此诞生了otter这样一个产品。
+
+ otter第一版本可追溯到04~05年,此次外部开源的版本为第4版,开发时间从2011年7月份一直持续到现在,目前阿里巴巴B2B内部的本地/异地机房的同步需求基本全上了otte4。
+
+目前同步规模:
+
+同步数据量6亿
+文件同步1.5TB(2000w张图片)
+涉及200+个数据库实例之间的同步
+80+台机器的集群规模
+
+
+ 项目介绍
+名称:otter ['ɒtə(r)]
+译意: 水獭,数据搬运工
+语言: 纯java开发
+定位: 基于数据库增量日志解析,准实时同步到本机房或异地机房的mysql/oracle数据库. 一个分布式数据库同步系统
+
+
+ 工作原理
+
+原理描述:
+1. 基于Canal开源产品,获取数据库增量日志数据。 什么是Canal, 请点击
+2. 典型管理系统架构,manager(web管理)+node(工作节点)
+ a. manager运行时推送同步配置到node节点
+ b. node节点将同步状态反馈到manager上
+3. 基于zookeeper,解决分布式状态调度的,允许多node节点之间协同工作.
+
+ 什么是canal?
+otter之前开源的一个子项目,开源链接地址:http://github.com/alibaba/canal
+
+
+ Introduction
+See the page for quick start: Introduction .
+
+ QuickStart
+See the page for quick start: QuickStart .
+
+
+ AdminGuide
+See the page for admin deploy guide : AdminGuide
+
+
+ 相关文档
+See the page for 文档: 相关PPT&PDF
+
+
+ 常见问题
+See the page for FAQ: FAQ
+
+
+
+ 版本相关:
+1. 建议版本:4.2.10 (otter开源版本从内部演变而来,所以初始版本直接从4.x开始)
+2. 下载发布包:download
+3. maven依赖 : 暂无
+
+
+
+
+ 问题反馈
+
+ 注意:canal&otter QQ讨论群已经建立,群号:161559791 ,欢迎加入进行技术讨论。
+
+1. qq交流群: 161559791
+2. 邮件交流: jianghang115@gmail.com
+3. 新浪微博: agapple0002
+4. 报告issue: issues
+
diff --git a/codeformat.xml b/codeformat.xml
new file mode 100644
index 00000000..769a6866
--- /dev/null
+++ b/codeformat.xml
@@ -0,0 +1,291 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/codetemplates.xml b/codetemplates.xml
new file mode 100644
index 00000000..1a91c371
--- /dev/null
+++ b/codetemplates.xml
@@ -0,0 +1,41 @@
+${filecomment}
+${package_declaration}
+${typecomment}
+${type_declaration}
+
+
+
+ // ${todo} Auto-generated catch block
+${exception_var}.printStackTrace(); // ${todo} Auto-generated method stub
+${body_statement} ${body_statement}
+// ${todo} Auto-generated constructor stub return ${field}; ${field} = ${param};
diff --git a/lib/install.bat b/lib/install.bat
new file mode 100644
index 00000000..6b9dbe1b
--- /dev/null
+++ b/lib/install.bat
@@ -0,0 +1,3 @@
+call mvn install:install-file -DgroupId=com.oracle -DartifactId=ojdbc14 -Dversion=10.2.0.3.0 -Dpackaging=jar -Dfile=ojdbc14-10.2.0.3.0.jar -DgeneratePom=true
+call 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
+call 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
diff --git a/lib/install.sh b/lib/install.sh
new file mode 100644
index 00000000..1acf094c
--- /dev/null
+++ b/lib/install.sh
@@ -0,0 +1,3 @@
+mvn install:install-file -DgroupId=com.oracle -DartifactId=ojdbc14 -Dversion=10.2.0.3.0 -Dpackaging=jar -Dfile=ojdbc14-10.2.0.3.0.jar -DgeneratePom=true
+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
+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
diff --git a/lib/jmockit-0.999.10-sources.jar b/lib/jmockit-0.999.10-sources.jar
new file mode 100644
index 00000000..1fb02bcc
Binary files /dev/null and b/lib/jmockit-0.999.10-sources.jar differ
diff --git a/lib/jmockit-0.999.10.jar b/lib/jmockit-0.999.10.jar
new file mode 100644
index 00000000..de2bc5bc
Binary files /dev/null and b/lib/jmockit-0.999.10.jar differ
diff --git a/lib/jmockit-0.999.10.pom b/lib/jmockit-0.999.10.pom
new file mode 100644
index 00000000..b35d9a0b
--- /dev/null
+++ b/lib/jmockit-0.999.10.pom
@@ -0,0 +1,93 @@
+
+ 4.0.0
+
+ mockit mockit 0.999.10
+ jmockit 0.999.10
+ Main
+
+
+
+
+ src
+ jmockit.properties
+
+
+
+
+ maven-jar-plugin 2.2
+
+
+ compile
+ jar
+
+
+
+
+ false
+ META-INF/MANIFEST.MF
+
+ Temp.class
+
+
+
+ maven-source-plugin 2.1.2
+
+
+ attach-sources
+ jar
+
+
+
+
+ maven-surefire-plugin 2.5
+
+ none:none
+ -javaagent:"${project.build.directory}"/jmockit-${project.version}.jar
+
+ **/*$*
+ **/Base*Test.class
+ **/testng/*Test.class
+ **/MockStateBetweenTestMethodsNGTest.class
+
+
+
+
+
+
+
+
+ junit junit ${junit.version}
+ true
+
+
+ org.testng testng 6.0.1
+ true
+
+ org.beanshell bsh
+ com.google.inject guice
+ com.beust jcommander
+ org.yaml snakeyaml
+
+
+
+ commons-logging commons-logging 1.1.1
+ true
+
+
+ org.slf4j slf4j-api 1.5.8
+ true
+
+
+ log4j log4j 1.2.15
+ true
+
+ javax.jms jms
+ javax.mail mail
+ com.sun.jmx jmxri
+ com.sun.jdmk jmxtools
+
+
+
+
\ No newline at end of file
diff --git a/lib/jtester-1.1.8-sources.jar b/lib/jtester-1.1.8-sources.jar
new file mode 100644
index 00000000..853301af
Binary files /dev/null and b/lib/jtester-1.1.8-sources.jar differ
diff --git a/lib/jtester-1.1.8.jar b/lib/jtester-1.1.8.jar
new file mode 100644
index 00000000..38948e8f
Binary files /dev/null and b/lib/jtester-1.1.8.jar differ
diff --git a/lib/jtester-1.1.8.pom b/lib/jtester-1.1.8.pom
new file mode 100644
index 00000000..183a64ed
--- /dev/null
+++ b/lib/jtester-1.1.8.pom
@@ -0,0 +1,146 @@
+
+ 4.0.0
+ org.jtester
+ jtester
+ 1.1.8
+ jar
+ jtester core
+
+
+
+ org.testng
+ testng
+ 6.1.1
+
+
+ mockit
+ jmockit
+ 0.999.10
+
+
+ commons-dbcp
+ commons-dbcp
+ 1.2.2
+
+
+ cglib
+ cglib-nodep
+ 2.1_3
+
+
+ org.fitnesse
+ fitnesse
+ 20100211
+
+
+ ognl
+ ognl
+ 2.7.2
+
+
+
+ org.springframework
+ spring
+ 2.5.4
+ provided
+
+
+ org.springframework
+ spring-aspects
+ 2.5.4
+ provided
+
+
+ javax.transaction
+ jta
+ 1.1
+ provided
+
+
+ org.apache.ibatis
+ ibatis-sqlmap
+ 2.3.4.726
+ provided
+
+
+ org.springframework
+ spring-aop
+ 2.5.4
+ provided
+
+
+ javax.annotation
+ jsr250-api
+ 1.0
+ provided
+
+
+ mysql
+ mysql-connector-java
+ 5.1.6
+ provided
+
+
+ com.h2database
+ h2
+ 1.1.107
+ provided
+
+
+ com.oracle
+ ojdbc14
+ 10.2.0.3.0
+ provided
+
+
+ javax.servlet
+ servlet-api
+ 2.4
+ provided
+
+
+
+
+ src/main/java
+ target/classes
+
+
+ src/main/resources
+
+
+
+
+
+ maven-compiler-plugin
+ 2.0.2
+
+ 1.5
+ 1.5
+ utf-8
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+ 2.2
+
+
+
+
+
+
+
+ maven-source-plugin
+
+
+ attach-sources
+
+ jar
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/lib/ojdbc14-10.2.0.3.0.jar b/lib/ojdbc14-10.2.0.3.0.jar
new file mode 100644
index 00000000..2bcaa018
Binary files /dev/null and b/lib/ojdbc14-10.2.0.3.0.jar differ
diff --git a/manager/biz/pom.xml b/manager/biz/pom.xml
new file mode 100644
index 00000000..ca45a44b
--- /dev/null
+++ b/manager/biz/pom.xml
@@ -0,0 +1,135 @@
+
+ 4.0.0
+
+ com.alibaba.otter
+ manager
+ 4.2.11-SNAPSHOT
+ ../pom.xml
+
+ com.alibaba.otter
+ manager.biz
+ jar
+ manager biz module for otter
+ http://github.com/alibaba/otter
+
+
+
+ com.alibaba.otter
+ shared.common
+ ${project.version}
+
+
+ com.alibaba.otter
+ shared.common
+ ${project.version}
+
+
+ com.alibaba.otter
+ shared.push
+ ${project.version}
+
+
+ com.alibaba.otter
+ canal.instance.manager
+ ${otter_canal_version}
+
+
+ org.springframework
+ spring
+
+
+
+
+ cglib
+ cglib-nodep
+
+
+ org.apache.ibatis
+ ibatis-sqlmap
+
+
+ commons-dbcp
+ commons-dbcp
+
+
+
+ mysql
+ mysql-connector-java
+
+
+
+ com.oracle
+ ojdbc14
+
+
+
+
+ org.springframework
+ spring-core
+
+
+ org.springframework
+ spring-beans
+
+
+ org.springframework
+ spring-aop
+
+
+ org.springframework
+ spring-context
+
+
+ org.springframework
+ spring-context-support
+
+
+ org.springframework
+ spring-tx
+
+
+ org.springframework
+ spring-jdbc
+
+
+ org.springframework
+ spring-orm
+
+
+ org.springframework
+ spring-web
+
+
+ org.springframework
+ spring-webmvc
+
+
+ org.springframework
+ spring-test
+
+
+ javax.mail
+ mail
+
+
+ com.alibaba
+ fastjson
+
+
+ com.google.guava
+ guava
+
+
+
+
+ org.jtester
+ jtester
+ test
+
+
+ junit
+ junit
+ test
+
+
+
diff --git a/manager/biz/src/main/java/com/alibaba/otter/manager/biz/autokeeper/AutoKeeperStatService.java b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/autokeeper/AutoKeeperStatService.java
new file mode 100644
index 00000000..09897d9f
--- /dev/null
+++ b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/autokeeper/AutoKeeperStatService.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2010-2101 Alibaba Group Holding Limited.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.otter.manager.biz.autokeeper;
+
+import java.util.List;
+
+import com.alibaba.otter.shared.common.model.autokeeper.AutoKeeperConnectionStat;
+import com.alibaba.otter.shared.common.model.autokeeper.AutoKeeperServerStat;
+
+/**
+ * zookeeper状态查询接口
+ *
+ * @author jianghang 2012-9-21 下午02:42:16
+ * @version 4.1.0
+ */
+public interface AutoKeeperStatService {
+
+ /**
+ * 根据serverIp查询对应的统计信息,包括Connection/Watch/Ephemeral等统计信息
+ *
+ * @param serverIp
+ * @return
+ */
+ public AutoKeeperServerStat findServerStat(String serverIp);
+
+ /**
+ * 根据sessionId查询对应的统计信息,包括详细的Connection/Watch/Ephemeral等统计信息
+ *
+ * @param sessionId
+ * @return
+ */
+ public AutoKeeperServerStat findServerStatBySessionId(String sessionId);
+
+ /**
+ * 根据sessionId查询对应的connction链接
+ *
+ * @param sessionId
+ * @return
+ */
+ public AutoKeeperConnectionStat findConnectionBySessionId(String sessionId);
+
+ /**
+ * 根据临时节点路径查询对应的connection统计信息
+ *
+ * @param path
+ * @return
+ */
+ public AutoKeeperConnectionStat findConnectionByEphemeralPath(String path);
+
+ /**
+ * 根据watcher路径查询对应的connection统计信息
+ *
+ * @param path
+ * @return
+ */
+ public List findConnectionByWatcherPath(String path);
+}
diff --git a/manager/biz/src/main/java/com/alibaba/otter/manager/biz/autokeeper/impl/AutoKeeperCollector.java b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/autokeeper/impl/AutoKeeperCollector.java
new file mode 100644
index 00000000..63b370ec
--- /dev/null
+++ b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/autokeeper/impl/AutoKeeperCollector.java
@@ -0,0 +1,348 @@
+/*
+ * Copyright (C) 2010-2101 Alibaba Group Holding Limited.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.otter.manager.biz.autokeeper.impl;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+import javax.annotation.Resource;
+
+import org.apache.commons.lang.StringUtils;
+import org.springframework.beans.factory.InitializingBean;
+
+import com.alibaba.otter.manager.biz.common.exceptions.ManagerException;
+import com.alibaba.otter.manager.biz.config.autokeeper.AutoKeeperClusterService;
+import com.alibaba.otter.manager.biz.utils.RegexUtils;
+import com.alibaba.otter.shared.common.model.autokeeper.AutoKeeperCluster;
+import com.alibaba.otter.shared.common.model.autokeeper.AutoKeeperConnectionStat;
+import com.alibaba.otter.shared.common.model.autokeeper.AutoKeeperEphemeralStat;
+import com.alibaba.otter.shared.common.model.autokeeper.AutoKeeperQuorumType;
+import com.alibaba.otter.shared.common.model.autokeeper.AutoKeeperServerStat;
+import com.alibaba.otter.shared.common.model.autokeeper.AutoKeeperWatchStat;
+import com.alibaba.otter.shared.common.utils.cmd.Exec;
+import com.alibaba.otter.shared.common.utils.cmd.Exec.Result;
+import com.alibaba.otter.shared.common.utils.thread.NamedThreadFactory;
+
+/**
+ * 对应的数据采集器
+ *
+ * @author jianghang 2012-9-21 下午03:05:28
+ * @version 4.1.0
+ */
+public class AutoKeeperCollector implements InitializingBean {
+
+ @Resource(name = "autoKeeperClusterService")
+ private AutoKeeperClusterService autoKeeperClusterService;
+
+ private static final String MODE_FOLLOWER = "Mode: follower";
+ private static final String MODE_LEADERER = "Mode: leader";
+ private static final String MODE_OBSERVER = "Mode: observer";
+ private static final String MODE_STANDALONE = "Mode: standalone";
+ private static final String NODE_COUNT = "Node count:";
+ private static final String STRING_LATENCY = "Latency min/avg/max:";
+ private static final String STRING_SENT = "Sent:";
+ private static final String STRING_RECEIVED = "Received:";
+ private static final String STRING_OUTSTANDING = "Outstanding:";
+ private static final String COMMA = ",";
+ private static final String BRACKETS = ")";
+ private static final String COLON = ":";
+ private static final String WRAP = "\n";
+ private static final String CMD_STAT = "echo stat | nc %s %s";
+ private static final String CMD_CONS = "echo cons | nc %s %s";
+ private static final String CMD_DUMP = "echo dump | nc %s %s";
+ private static final String CMD_WCHC = "echo wchc | nc %s %s";
+ private static final long DEFAULT_COLLECT_INTERVAL = 300;
+ private long delay = 1;
+ private int singleSize = 1;
+ private long collectInterval = DEFAULT_COLLECT_INTERVAL;
+
+ private AutoKeeperData autoKeeperData;
+ private ScheduledExecutorService collectorExecutor;
+
+ public void collectorConnectionStat(String address) {
+ List netAddress = splitAddress(address);
+ if (netAddress.isEmpty()) {
+ return;
+ }
+ String ip = netAddress.get(0);
+ String port = netAddress.get(1);
+ String[] cmd = { "/bin/bash", "-c", String.format(CMD_CONS, ip, port) };
+ String cmdresult = collector(cmd);
+ String[] result = cmdresult.split(WRAP);
+ List summary = new ArrayList();
+
+ for (String line : result) {
+
+ if (StringUtils.isBlank(line)) {
+ continue;
+ }
+
+ String[] lineArray = line.split(":");
+ if (2 != lineArray.length) {
+ continue;
+ }
+
+ AutoKeeperConnectionStat autoKeeperConnectionStat = new AutoKeeperConnectionStat();
+ autoKeeperConnectionStat.setOriginalContent(line);
+ String clientIp = StringUtils.trimToEmpty(line.split(":")[0].replace("/", ""));
+ String sessionId = StringUtils.trimToEmpty(RegexUtils.findFirst(line.split(":")[1], "sid=(?s).*?[,)]")).replace("sid=",
+ StringUtils.EMPTY).replace(COMMA,
+ StringUtils.EMPTY).replace(BRACKETS,
+ StringUtils.EMPTY);
+ String queued = StringUtils.trimToEmpty(RegexUtils.findFirst(line.split(":")[1], "queued=(?s).*?[,)]")).replace("queued=",
+ StringUtils.EMPTY).replace(COMMA,
+ StringUtils.EMPTY).replace(BRACKETS,
+ StringUtils.EMPTY);
+ String receive = StringUtils.trimToEmpty(RegexUtils.findFirst(line.split(":")[1], "recved=(?s).*?[,)]")).replace("recved=",
+ StringUtils.EMPTY).replace(COMMA,
+ StringUtils.EMPTY).replace(BRACKETS,
+ StringUtils.EMPTY);
+ String sent = StringUtils.trimToEmpty(RegexUtils.findFirst(line.split(":")[1], "sent=(?s).*?[,)]")).replace("sent=",
+ StringUtils.EMPTY).replace(COMMA,
+ StringUtils.EMPTY).replace(BRACKETS,
+ StringUtils.EMPTY);
+ String minlat = StringUtils.trimToEmpty(RegexUtils.findFirst(line.split(":")[1], "minlat=(?s).*?[,)]")).replace("minlat=",
+ StringUtils.EMPTY).replace(COMMA,
+ StringUtils.EMPTY).replace(BRACKETS,
+ StringUtils.EMPTY);
+ String avglat = StringUtils.trimToEmpty(RegexUtils.findFirst(line.split(":")[1], "avglat=(?s).*?[,)]")).replace("avglat=",
+ StringUtils.EMPTY).replace(COMMA,
+ StringUtils.EMPTY).replace(BRACKETS,
+ StringUtils.EMPTY);
+ String maxlat = StringUtils.trimToEmpty(RegexUtils.findFirst(line.split(":")[1], "maxlat=(?s).*?[,)]")).replace("maxlat=",
+ StringUtils.EMPTY).replace(COMMA,
+ StringUtils.EMPTY).replace(BRACKETS,
+ StringUtils.EMPTY);
+ autoKeeperConnectionStat.setServerAddress(ip);
+ autoKeeperConnectionStat.setClientAddress(clientIp);
+ autoKeeperConnectionStat.setSessionId(sessionId);
+ if (StringUtils.isNotEmpty(queued)) {
+ autoKeeperConnectionStat.setQueued(Long.parseLong(queued));
+ }
+ if (StringUtils.isNotEmpty(receive)) {
+ autoKeeperConnectionStat.setRecved(Long.parseLong(receive));
+ }
+ if (StringUtils.isNotEmpty(sent)) {
+ autoKeeperConnectionStat.setSent(Long.parseLong(sent));
+ }
+ if (StringUtils.isNotEmpty(minlat)) {
+ autoKeeperConnectionStat.setMinLatency(Long.parseLong(minlat));
+ }
+ if (StringUtils.isNotEmpty(avglat)) {
+ autoKeeperConnectionStat.setAvgLatency(Long.parseLong(avglat));
+ }
+ if (StringUtils.isNotEmpty(maxlat)) {
+ autoKeeperConnectionStat.setMaxLatency(Long.parseLong(maxlat));
+ }
+
+ summary.add(autoKeeperConnectionStat);
+ }
+ autoKeeperData.joinConnection(address, summary);
+ }
+
+ public void collectorServerStat(String address) {
+ List netAddress = splitAddress(address);
+ if (netAddress.isEmpty()) {
+ return;
+ }
+ String ip = netAddress.get(0);
+ String port = netAddress.get(1);
+ String[] cmd = { "/bin/bash", "-c", String.format(CMD_STAT, ip, port) };
+ String cmdresult = collector(cmd);
+ String[] result = cmdresult.split(WRAP);
+ AutoKeeperServerStat summary = new AutoKeeperServerStat();
+ summary.setOriginalContent(cmdresult);
+ for (String line : result) {
+
+ if (line.contains(MODE_FOLLOWER)) {
+ summary.setQuorumType(AutoKeeperQuorumType.FOLLOWER);
+ } else if (line.contains(MODE_LEADERER)) {
+ summary.setQuorumType(AutoKeeperQuorumType.LEADER);
+ } else if (line.contains(MODE_STANDALONE)) {
+ summary.setQuorumType(AutoKeeperQuorumType.STANDALONE);
+ } else if (line.contains(MODE_OBSERVER)) {
+ summary.setQuorumType(AutoKeeperQuorumType.OBSERVER);
+ } else if (line.contains(STRING_LATENCY)) {
+ List latency = Arrays.asList(StringUtils.trimToEmpty(line.replace(STRING_LATENCY,
+ StringUtils.EMPTY)).split("/"));
+ summary.setMinLatency(Long.parseLong(latency.get(0)));
+ summary.setAvgLatency(Long.parseLong(latency.get(1)));
+ summary.setMaxLatency(Long.parseLong(latency.get(2)));
+ } else if (line.contains(STRING_OUTSTANDING)) {
+ summary.setQueued(Long.parseLong(StringUtils.trimToEmpty(line.replace(STRING_OUTSTANDING,
+ StringUtils.EMPTY))));
+ } else if (line.contains(NODE_COUNT)) {
+ summary.setNodeCount(Long.parseLong(StringUtils.trimToEmpty(line.replace(NODE_COUNT, StringUtils.EMPTY))));
+ } else if (line.contains(STRING_SENT)) {
+ summary.setSent(Long.parseLong(StringUtils.trimToEmpty(line.replace(STRING_SENT, StringUtils.EMPTY))));
+ } else if (line.contains(STRING_RECEIVED)) {
+ summary.setRecved(Long.parseLong(StringUtils.trimToEmpty(line.replace(STRING_RECEIVED,
+ StringUtils.EMPTY))));
+ }
+ }
+
+ autoKeeperData.joinServer(address, summary);
+ }
+
+ public void collectorEphemeralStat(String address) {
+ List netAddress = splitAddress(address);
+ if (netAddress.isEmpty()) {
+ return;
+ }
+ String ip = netAddress.get(0);
+ String port = netAddress.get(1);
+ String[] cmd = { "/bin/bash", "-c", String.format(CMD_DUMP, ip, port) };
+ String cmdresult = collector(cmd);
+
+ Map> pathMap = groupSessionPath(cmdresult);
+
+ List autoKeeperEphemeralStats = new ArrayList();
+ for (Map.Entry> entry : pathMap.entrySet()) {
+ AutoKeeperEphemeralStat autoKeeperEphemeralStat = new AutoKeeperEphemeralStat();
+ autoKeeperEphemeralStat.setSessionId(entry.getKey());
+ autoKeeperEphemeralStat.setPaths(entry.getValue());
+ autoKeeperEphemeralStats.add(autoKeeperEphemeralStat);
+ }
+
+ autoKeeperData.joinEphemeral(address, autoKeeperEphemeralStats);
+
+ }
+
+ public void collectorWatchStat(String address) {
+ List netAddress = splitAddress(address);
+ if (netAddress.isEmpty()) {
+ return;
+ }
+ String ip = netAddress.get(0);
+ String port = netAddress.get(1);
+ String[] cmd = { "/bin/bash", "-c", String.format(CMD_WCHC, ip, port) };
+ String cmdresult = collector(cmd);
+
+ Map> pathMap = groupSessionPath(cmdresult);
+
+ List autoKeeperWatchStats = new ArrayList();
+ for (Map.Entry> entry : pathMap.entrySet()) {
+ AutoKeeperWatchStat autoKeeperWatchStat = new AutoKeeperWatchStat();
+ autoKeeperWatchStat.setSessionId(entry.getKey());
+ autoKeeperWatchStat.setPaths(entry.getValue());
+ autoKeeperWatchStats.add(autoKeeperWatchStat);
+ }
+
+ autoKeeperData.joinWatch(address, autoKeeperWatchStats);
+
+ }
+
+ public static String collector(String[] command) {
+ Result result = null;
+ try {
+ result = Exec.execute(command);
+ if (result.getExitCode() == 0) {
+ return result.getStdout();
+ } else {
+ return result.getStderr();
+ }
+ } catch (Exception e) {
+ throw new ManagerException(e);
+ }
+ }
+
+ private List splitAddress(String address) {
+ List ipPort = Arrays.asList(address.split(":"));
+ if (ipPort.size() != 2) {
+ return new ArrayList();
+ }
+ return ipPort;
+ }
+
+ /**
+ *
+ * key=sessionId
+ * value=pathList
+ *
+ */
+ private Map> groupSessionPath(String cmdresult) {
+ String[] result = cmdresult.split(WRAP);
+
+ Map> pathMap = new HashMap>();
+ String sessionId = StringUtils.EMPTY;
+ for (String line : result) {
+ line = StringUtils.trimToEmpty(line);
+ if (StringUtils.isBlank(line)) {
+ continue;
+ }
+ if (line.startsWith("0x")) {
+ sessionId = line.replace(COLON, StringUtils.EMPTY);
+ pathMap.put(sessionId, new ArrayList());
+ } else if (line.startsWith("/")) {
+ List paths = pathMap.get(sessionId);
+ paths.add(line);
+ }
+ }
+ return pathMap;
+ }
+
+ @Override
+ public void afterPropertiesSet() throws Exception {
+ collectorExecutor = Executors.newScheduledThreadPool(singleSize, new NamedThreadFactory("collector-thread",
+ true));
+ startCollect();
+ }
+
+ private void startCollect() {
+
+ // 启动定时工作任务
+ collectorExecutor.scheduleAtFixedRate(new Runnable() {
+
+ @Override
+ public void run() {
+ List autoKeeperClusters = autoKeeperClusterService.listAutoKeeperClusters();
+ if (!autoKeeperClusters.isEmpty()) {
+ autoKeeperData.persist();
+ for (AutoKeeperCluster autoKeeperCluster : autoKeeperClusters) {
+ List servers = autoKeeperCluster.getServerList();
+ for (String address : servers) {
+ collectorServerStat(address);
+ collectorConnectionStat(address);
+ collectorWatchStat(address);
+ collectorEphemeralStat(address);
+ }
+ }
+ }
+ }
+ }, delay, collectInterval, TimeUnit.SECONDS);
+
+ }
+
+ public void setAutoKeeperClusterService(AutoKeeperClusterService autoKeeperClusterService) {
+ this.autoKeeperClusterService = autoKeeperClusterService;
+ }
+
+ public void setAutoKeeperData(AutoKeeperData autoKeeperData) {
+ this.autoKeeperData = autoKeeperData;
+ }
+
+ public void setCollectInterval(long collectInterval) {
+ this.collectInterval = collectInterval;
+ }
+
+}
diff --git a/manager/biz/src/main/java/com/alibaba/otter/manager/biz/autokeeper/impl/AutoKeeperData.java b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/autokeeper/impl/AutoKeeperData.java
new file mode 100644
index 00000000..bc216248
--- /dev/null
+++ b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/autokeeper/impl/AutoKeeperData.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2010-2101 Alibaba Group Holding Limited.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.otter.manager.biz.autokeeper.impl;
+
+import java.util.List;
+import java.util.Map;
+
+import com.alibaba.otter.shared.common.model.autokeeper.AutoKeeperConnectionStat;
+import com.alibaba.otter.shared.common.model.autokeeper.AutoKeeperEphemeralStat;
+import com.alibaba.otter.shared.common.model.autokeeper.AutoKeeperServerStat;
+import com.alibaba.otter.shared.common.model.autokeeper.AutoKeeperWatchStat;
+import com.google.common.collect.MapMaker;
+
+/**
+ * join server之间的统计数据,提供当前最新和完整的数据结构方便数据查询
+ *
+ *
+ * server ->
+ * connection ->
+ * -> watcher
+ * -> ephemeral
+ *
+ *
+ * @author jianghang 2012-9-21 下午03:02:00
+ * @version 4.1.0
+ */
+public class AutoKeeperData implements AutoKeeperPersist {
+
+ private Map serverStats = new MapMaker().makeMap(); // serverIp和server的对应关系
+ private Map connectionStats = new MapMaker().makeMap(); // sessionId和connection的对应关系
+
+ public void joinServer(String address, AutoKeeperServerStat summary) {
+ serverStats.put(address, summary);
+ }
+
+ public void joinConnection(String address, List connections) {
+ // 记录connection引用
+ for (AutoKeeperConnectionStat connection : connections) {
+ connectionStats.put(connection.getSessionId(), connection);
+ }
+
+ // 添加引用到server中
+ if (serverStats.containsKey(address)) {
+ serverStats.get(address).getConnectionStats().addAll(connections);
+ }
+ }
+
+ public void joinEphemeral(String address, List ephemerals) {
+ for (AutoKeeperEphemeralStat ephemeral : ephemerals) {
+ if (connectionStats.containsKey(ephemeral.getSessionId())) {
+ // 找到对应的connection进行关联,填充数据
+ connectionStats.get(ephemeral.getSessionId()).getEphemeralStats().add(ephemeral);
+ }
+ }
+ }
+
+ public void joinWatch(String address, List watches) {
+ for (AutoKeeperWatchStat watch : watches) {
+ if (connectionStats.containsKey(watch.getSessionId())) {
+ // 找到对应的connection进行关联,填充数据
+ connectionStats.get(watch.getSessionId()).getWatchStats().add(watch);
+ }
+ }
+ }
+
+ public void persist() {
+ serverStats.clear();
+ connectionStats.clear();
+ }
+
+ public Map getServerStats() {
+ return serverStats;
+ }
+
+ public Map getConnectionStats() {
+ return connectionStats;
+ }
+
+}
diff --git a/manager/biz/src/main/java/com/alibaba/otter/manager/biz/autokeeper/impl/AutoKeeperPersist.java b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/autokeeper/impl/AutoKeeperPersist.java
new file mode 100644
index 00000000..e9355492
--- /dev/null
+++ b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/autokeeper/impl/AutoKeeperPersist.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2010-2101 Alibaba Group Holding Limited.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.otter.manager.biz.autokeeper.impl;
+
+/**
+ * 数据持久化接口,会有持久化调度器定时触发
+ *
+ * @author jianghang 2012-9-21 下午03:04:37
+ * @version 4.1.0
+ */
+public interface AutoKeeperPersist {
+
+ /**
+ * 会有
+ */
+ public void persist();
+}
diff --git a/manager/biz/src/main/java/com/alibaba/otter/manager/biz/autokeeper/impl/AutoKeeperStatServiceImpl.java b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/autokeeper/impl/AutoKeeperStatServiceImpl.java
new file mode 100644
index 00000000..22bbac4d
--- /dev/null
+++ b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/autokeeper/impl/AutoKeeperStatServiceImpl.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2010-2101 Alibaba Group Holding Limited.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.otter.manager.biz.autokeeper.impl;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.alibaba.otter.manager.biz.autokeeper.AutoKeeperStatService;
+import com.alibaba.otter.shared.common.model.autokeeper.AutoKeeperConnectionStat;
+import com.alibaba.otter.shared.common.model.autokeeper.AutoKeeperEphemeralStat;
+import com.alibaba.otter.shared.common.model.autokeeper.AutoKeeperServerStat;
+import com.alibaba.otter.shared.common.model.autokeeper.AutoKeeperWatchStat;
+
+/**
+ * 提供autokeeper相关的数据查询接口
+ *
+ * @author simon 2012-9-28 上午11:00:07
+ * @version 4.1.0
+ */
+public class AutoKeeperStatServiceImpl implements AutoKeeperStatService {
+
+ private AutoKeeperData autoKeeperData;
+
+ public AutoKeeperServerStat findServerStat(String serverAddress) {
+ return autoKeeperData.getServerStats().get(serverAddress);
+ }
+
+ public AutoKeeperServerStat findServerStatBySessionId(String sessionId) {
+ String serverAddress = autoKeeperData.getConnectionStats().get(sessionId).getServerAddress();
+ return findServerStat(serverAddress);
+ }
+
+ public AutoKeeperConnectionStat findConnectionBySessionId(String sessionId) {
+ return autoKeeperData.getConnectionStats().get(sessionId);
+ }
+
+ public AutoKeeperConnectionStat findConnectionByEphemeralPath(String path) {
+ for (AutoKeeperConnectionStat connection : autoKeeperData.getConnectionStats().values()) {
+ for (AutoKeeperEphemeralStat ephemeral : connection.getEphemeralStats()) {
+ if (ephemeral.getPaths().contains(path)) {
+ return connection;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ public List findConnectionByWatcherPath(String path) {
+ List connections = new ArrayList();
+ for (AutoKeeperConnectionStat connection : autoKeeperData.getConnectionStats().values()) {
+ for (AutoKeeperWatchStat watch : connection.getWatchStats()) {
+ if (watch.getPaths().contains(path)) {
+ connections.add(connection);
+ }
+ }
+ }
+
+ return connections;
+ }
+
+ public void setAutoKeeperData(AutoKeeperData autoKeeperData) {
+ this.autoKeeperData = autoKeeperData;
+ }
+
+}
diff --git a/manager/biz/src/main/java/com/alibaba/otter/manager/biz/common/DataSourceCreator.java b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/common/DataSourceCreator.java
new file mode 100644
index 00000000..aecf976f
--- /dev/null
+++ b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/common/DataSourceCreator.java
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2010-2101 Alibaba Group Holding Limited.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.otter.manager.biz.common;
+
+import java.sql.SQLException;
+import java.util.List;
+
+import javax.sql.DataSource;
+
+import org.apache.commons.dbcp.BasicDataSource;
+import org.apache.commons.lang.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.DisposableBean;
+import org.springframework.util.Assert;
+import org.springframework.util.CollectionUtils;
+
+import com.alibaba.otter.common.push.datasource.DataSourceHanlder;
+import com.alibaba.otter.shared.common.model.config.data.DataMediaSource;
+import com.alibaba.otter.shared.common.model.config.data.DataMediaType;
+import com.alibaba.otter.shared.common.model.config.data.db.DbMediaSource;
+
+public class DataSourceCreator implements DisposableBean {
+
+ private static final Logger logger = LoggerFactory.getLogger(DataSourceCreator.class);
+
+ private int maxWait = 60 * 1000;
+
+ private int minIdle = 0;
+
+ private int initialSize = 1;
+
+ private int maxActive = 5;
+
+ private int maxIdle = 1;
+
+ private int numTestsPerEvictionRun = -1;
+
+ private int timeBetweenEvictionRunsMillis = 60 * 1000;
+
+ private int removeAbandonedTimeout = 10 * 60;
+
+ private int minEvictableIdleTimeMillis = 30 * 60 * 1000;
+
+ private List dataSourceHandlers;
+
+ /**
+ * 直接创建数据源,不做cache
+ */
+ public DataSource createDataSource(DataMediaSource dataMediaSource) {
+ Assert.notNull(dataMediaSource);
+ DbMediaSource dbMediaSource = (DbMediaSource) dataMediaSource;
+
+ // 扩展功能,可以自定义一些自己实现的 dataSource
+ DataSource customDataSource = preCreate(0L, dbMediaSource);
+ if (customDataSource != null) {
+ return customDataSource;
+ }
+
+ return createDataSource(dbMediaSource.getUrl(), dbMediaSource.getUsername(), dbMediaSource.getPassword(),
+ dbMediaSource.getDriver(), dbMediaSource.getType(), dbMediaSource.getEncode());
+ }
+
+ public void destroyDataSource(DataSource dataSource) {
+ try {
+ // for filter to destroy custom datasource
+ if (letHandlerDestroyIfSupport(0L, dataSource)) {
+ return;
+ }
+
+ if (dataSource == null) {
+ return;
+ }
+
+ BasicDataSource basicDataSource = (BasicDataSource) dataSource;
+ basicDataSource.close();
+ } catch (SQLException e) {
+ logger.error("ERROR ## close the datasource has an error", e);
+ }
+ }
+
+ public void destroy() throws Exception {
+ }
+
+ /**
+ * 扩展功能,可以自定义一些自己实现的 dataSource
+ */
+ private DataSource preCreate(Long pipelineId, DbMediaSource dbMediaSource) {
+
+ if (CollectionUtils.isEmpty(dataSourceHandlers)) {
+ return null;
+ }
+
+ DataSource dataSource = null;
+ for (DataSourceHanlder handler : dataSourceHandlers) {
+ if (handler.support(dbMediaSource)) {
+ dataSource = handler.create(pipelineId, dbMediaSource);
+ if (dataSource != null) {
+ return dataSource;
+ }
+ }
+ }
+ return null;
+ }
+
+ public boolean letHandlerDestroyIfSupport(Long pipelineId, DataSource source) {
+ boolean destroied = false;
+
+ if (CollectionUtils.isEmpty(this.dataSourceHandlers)) {
+ return destroied;
+ }
+
+ for (DataSourceHanlder handler : this.dataSourceHandlers) {
+ if (handler.support(source)) {
+ handler.destory(pipelineId);
+ destroied = true;
+ return destroied;
+ }
+ }
+ return destroied;
+
+ }
+
+ private DataSource createDataSource(String url, String userName, String password, String driverClassName,
+ DataMediaType dataMediaType, String encoding) {
+ BasicDataSource dbcpDs = new BasicDataSource();
+
+ dbcpDs.setInitialSize(initialSize);// 初始化连接池时创建的连接数
+ dbcpDs.setMaxActive(maxActive);// 连接池允许的最大并发连接数,值为非正数时表示不限制
+ dbcpDs.setMaxIdle(maxIdle);// 连接池中的最大空闲连接数,超过时,多余的空闲连接将会被释放,值为负数时表示不限制
+ dbcpDs.setMinIdle(minIdle);// 连接池中的最小空闲连接数,低于此数值时将会创建所欠缺的连接,值为0时表示不创建
+ dbcpDs.setMaxWait(maxWait);// 以毫秒表示的当连接池中没有可用连接时等待可用连接返回的时间,超时则抛出异常,值为-1时表示无限等待
+ dbcpDs.setRemoveAbandoned(true);// 是否清除已经超过removeAbandonedTimeout设置的无效连接
+ dbcpDs.setLogAbandoned(true);// 当清除无效链接时是否在日志中记录清除信息的标志
+ dbcpDs.setRemoveAbandonedTimeout(removeAbandonedTimeout); // 以秒表示清除无效链接的时限
+ dbcpDs.setNumTestsPerEvictionRun(numTestsPerEvictionRun);// 确保连接池中没有已破损的连接
+ dbcpDs.setTestOnBorrow(false);// 指定连接被调用时是否经过校验
+ dbcpDs.setTestOnReturn(false);// 指定连接返回到池中时是否经过校验
+ dbcpDs.setTestWhileIdle(true);// 指定连接进入空闲状态时是否经过空闲对象驱逐进程的校验
+ dbcpDs.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis); // 以毫秒表示空闲对象驱逐进程由运行状态进入休眠状态的时长,值为非正数时表示不运行任何空闲对象驱逐进程
+ dbcpDs.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis); // 以毫秒表示连接被空闲对象驱逐进程驱逐前在池中保持空闲状态的最小时间
+
+ // 动态的参数
+ dbcpDs.setDriverClassName(driverClassName);
+ dbcpDs.setUrl(url);
+ dbcpDs.setUsername(userName);
+ dbcpDs.setPassword(password);
+
+ if (dataMediaType.isOracle()) {
+ dbcpDs.addConnectionProperty("restrictGetTables", "true");
+ // dbcpDs.setValidationQuery("select 1 from dual");
+ } else if (dataMediaType.isMysql()) {
+ // open the batch mode for mysql since 5.1.8
+ dbcpDs.addConnectionProperty("useServerPrepStmts", "false");
+ dbcpDs.addConnectionProperty("rewriteBatchedStatements", "false");
+ if (StringUtils.isNotEmpty(encoding)) {
+ dbcpDs.addConnectionProperty("characterEncoding", encoding);
+ }
+ // dbcpDs.setValidationQuery("select 1");
+ } else {
+ logger.error("ERROR ## Unknow database type");
+ }
+
+ return dbcpDs;
+ }
+
+ public void setMaxWait(int maxWait) {
+ this.maxWait = maxWait;
+ }
+
+ public void setMinIdle(int minIdle) {
+ this.minIdle = minIdle;
+ }
+
+ public void setInitialSize(int initialSize) {
+ this.initialSize = initialSize;
+ }
+
+ public void setMaxActive(int maxActive) {
+ this.maxActive = maxActive;
+ }
+
+ public void setMaxIdle(int maxIdle) {
+ this.maxIdle = maxIdle;
+ }
+
+ public void setNumTestsPerEvictionRun(int numTestsPerEvictionRun) {
+ this.numTestsPerEvictionRun = numTestsPerEvictionRun;
+ }
+
+ public void setTimeBetweenEvictionRunsMillis(int timeBetweenEvictionRunsMillis) {
+ this.timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis;
+ }
+
+ public void setRemoveAbandonedTimeout(int removeAbandonedTimeout) {
+ this.removeAbandonedTimeout = removeAbandonedTimeout;
+ }
+
+ public void setMinEvictableIdleTimeMillis(int minEvictableIdleTimeMillis) {
+ this.minEvictableIdleTimeMillis = minEvictableIdleTimeMillis;
+ }
+
+ public void setDataSourceHandlers(List dataSourceHandlers) {
+ this.dataSourceHandlers = dataSourceHandlers;
+ }
+
+}
diff --git a/manager/biz/src/main/java/com/alibaba/otter/manager/biz/common/alarm/AbstractAlarmService.java b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/common/alarm/AbstractAlarmService.java
new file mode 100644
index 00000000..57072ebc
--- /dev/null
+++ b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/common/alarm/AbstractAlarmService.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2010-2101 Alibaba Group Holding Limited.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.otter.manager.biz.common.alarm;
+
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.LockSupport;
+
+import org.apache.commons.lang.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.DisposableBean;
+import org.springframework.beans.factory.InitializingBean;
+
+/**
+ * 报警服务实现
+ *
+ * @author jianghang 2011-11-3 上午11:12:16
+ * @version 4.0.0
+ */
+public abstract class AbstractAlarmService implements AlarmService, InitializingBean, DisposableBean {
+
+ private static final Logger logger = LoggerFactory.getLogger(AbstractAlarmService.class);
+
+ private BlockingQueue queue = new LinkedBlockingQueue(3 * 3 * 3600);
+ private ExecutorService executor;
+ private int period = 150; // milliseconds
+
+ public void sendAlarm(AlarmMessage data) {
+ try {
+ if (!queue.offer(data, 2, TimeUnit.SECONDS)) {
+ logger.error(String.format("alarm sent to queue error : [%s]", data.toString()));
+ }
+ } catch (Exception e) {
+ logger.error(String.format("send alarm [%s] to drgoon agent error!", data.toString()), e);
+ }
+ }
+
+ private void sendAlarmInternal() {
+ AlarmMessage data = null;
+ try {
+ data = queue.take();
+ doSend(data);
+ logger.info(String.format("has sent alarm [%s] to drgoon agent.", data.toString()));
+ } catch (InterruptedException e) {
+ logger.warn("otter-sendAlarm-worker was interrupted", e);
+ } catch (Exception e) {
+ logger.error(String.format("send alarm [%s] to drgoon agent error!", data.toString()), e);
+ }
+ }
+
+ protected abstract void doSend(AlarmMessage data) throws Exception;
+
+ public void afterPropertiesSet() throws Exception {
+ executor = Executors.newFixedThreadPool(1);
+ executor.submit(new Runnable() {
+
+ public void run() {
+ while (!Thread.currentThread().isInterrupted()) {
+ sendAlarmInternal();
+ LockSupport.parkNanos(period * 1000L * 1000L);
+ }
+ }
+ });
+ }
+
+ @Override
+ public void destroy() throws Exception {
+ if (executor != null && !executor.isShutdown()) {
+ executor.shutdown();
+ executor.awaitTermination(2, TimeUnit.SECONDS);
+ }
+ if (!queue.isEmpty()) {
+ int size = queue.size();
+ logger.warn(String.format("there are %d alarms wait to be sent \n %s", size, dumpQueue()));
+ }
+ }
+
+ protected String dumpQueue() {
+ if (queue.isEmpty()) {
+ return StringUtils.EMPTY;
+ }
+ StringBuilder sb = new StringBuilder();
+ for (AlarmMessage data : queue) {
+ sb.append(data.toString()).append("\n");
+ }
+
+ return sb.toString();
+ }
+
+ // ============= setter ===============
+
+ public void setPeriod(int period) {
+ this.period = period;
+ }
+
+}
diff --git a/manager/biz/src/main/java/com/alibaba/otter/manager/biz/common/alarm/AlarmMessage.java b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/common/alarm/AlarmMessage.java
new file mode 100644
index 00000000..ba7c9293
--- /dev/null
+++ b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/common/alarm/AlarmMessage.java
@@ -0,0 +1,45 @@
+package com.alibaba.otter.manager.biz.common.alarm;
+
+import java.io.Serializable;
+
+import org.apache.commons.lang.builder.ToStringBuilder;
+
+import com.alibaba.otter.shared.common.utils.OtterToStringStyle;
+
+public class AlarmMessage implements Serializable {
+
+ private static final long serialVersionUID = 6110474591366995515L;
+ private String message;
+ private String receiveKey;
+
+ public AlarmMessage(){
+
+ }
+
+ public AlarmMessage(String message, String receiveKey){
+ this.message = message;
+ this.receiveKey = receiveKey;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public void setMessage(String message) {
+ this.message = message;
+ }
+
+ public String getReceiveKey() {
+ return receiveKey;
+ }
+
+ public void setReceiveKey(String receiveKey) {
+ this.receiveKey = receiveKey;
+ }
+
+ @Override
+ public String toString() {
+ return ToStringBuilder.reflectionToString(this, OtterToStringStyle.DEFAULT_STYLE);
+ }
+
+}
diff --git a/manager/biz/src/main/java/com/alibaba/otter/manager/biz/common/alarm/AlarmService.java b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/common/alarm/AlarmService.java
new file mode 100644
index 00000000..0c37d36f
--- /dev/null
+++ b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/common/alarm/AlarmService.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2010-2101 Alibaba Group Holding Limited.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.otter.manager.biz.common.alarm;
+
+
+/**
+ * 报警服务service定义,暂时先简单实现:利用dragoon的报警推送机制进行短信,邮件,旺旺信息等报警
+ *
+ * @author jianghang 2011-9-26 下午10:27:44
+ * @version 4.0.0
+ */
+public interface AlarmService {
+
+ /**
+ * 发送基于kv的报警信息
+ *
+ *
+ * Map内容;
+ * 1. message : 报警内容
+ * 2. receiveKey : 报警接收者信息
+ *
+ *
+ * @param data
+ */
+ public void sendAlarm(AlarmMessage data);
+
+}
diff --git a/manager/biz/src/main/java/com/alibaba/otter/manager/biz/common/alarm/DefaultAlarmService.java b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/common/alarm/DefaultAlarmService.java
new file mode 100644
index 00000000..9102e1c6
--- /dev/null
+++ b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/common/alarm/DefaultAlarmService.java
@@ -0,0 +1,95 @@
+package com.alibaba.otter.manager.biz.common.alarm;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.lang.StringUtils;
+import org.springframework.mail.SimpleMailMessage;
+import org.springframework.mail.javamail.JavaMailSender;
+import org.springframework.mail.javamail.JavaMailSenderImpl;
+
+import com.alibaba.otter.manager.biz.config.parameter.SystemParameterService;
+import com.alibaba.otter.shared.common.model.config.parameter.SystemParameter;
+
+/**
+ * 发送邮件进行报警
+ *
+ * @author jianghang 2013-9-6 上午11:42:04
+ * @since 4.2.2
+ */
+public class DefaultAlarmService extends AbstractAlarmService {
+
+ private static final String TITLE = "alarm_from_otter";
+ private String username;
+ private JavaMailSender mailSender;
+ private SystemParameterService systemParameterService;
+
+ public void doSend(AlarmMessage data) throws Exception {
+ SimpleMailMessage mail = new SimpleMailMessage(); // 只发送纯文本
+ mail.setFrom(username);
+ mail.setSubject(TITLE);// 主题
+ mail.setText(data.getMessage());// 邮件内容
+ String receiveKeys[] = StringUtils.split(StringUtils.replace(data.getReceiveKey(), ";", ","), ",");
+
+ SystemParameter systemParameter = systemParameterService.find();
+ List mailAddress = new ArrayList();
+ for (String receiveKey : receiveKeys) {
+ String receiver = convertToReceiver(systemParameter, receiveKey);
+ String strs[] = StringUtils.split(StringUtils.replace(receiver, ";", ","), ",");
+ for (String str : strs) {
+ if (isMail(str)) {
+ if (str != null) {
+ mailAddress.add(str);
+ }
+ } else if (isSms(str)) {
+ // do nothing
+ }
+ }
+ }
+
+ if (!mailAddress.isEmpty()) {
+ mail.setTo(mailAddress.toArray(new String[mailAddress.size()]));
+ doSendMail(mail);
+ }
+ }
+
+ private void doSendMail(SimpleMailMessage mail) {
+ if (mailSender instanceof JavaMailSenderImpl) {
+ JavaMailSenderImpl mailSenderImpl = (JavaMailSenderImpl) mailSender;
+ if (StringUtils.isNotEmpty(mailSenderImpl.getUsername())
+ && StringUtils.isNotEmpty(mailSenderImpl.getPassword())) {
+ // 正确设置了账户/密码,才尝试发送邮件
+ mailSender.send(mail);
+ }
+ }
+ }
+
+ private boolean isMail(String receiveKey) {
+ return StringUtils.contains(receiveKey, '@');
+ }
+
+ private boolean isSms(String receiveKey) {
+ return false;
+ }
+
+ private String convertToReceiver(SystemParameter systemParameter, String receiveKey) {
+ if (StringUtils.equalsIgnoreCase(systemParameter.getDefaultAlarmReceiveKey(), receiveKey)) {
+ return systemParameter.getDefaultAlarmReceiver();
+ } else {
+ return systemParameter.getAlarmReceiver().get(receiveKey);
+ }
+ }
+
+ public void setMailSender(JavaMailSender mailSender) {
+ this.mailSender = mailSender;
+ }
+
+ public void setUsername(String username) {
+ this.username = username;
+ }
+
+ public void setSystemParameterService(SystemParameterService systemParameterService) {
+ this.systemParameterService = systemParameterService;
+ }
+
+}
diff --git a/manager/biz/src/main/java/com/alibaba/otter/manager/biz/common/arbitrate/ArbitrateConfigImpl.java b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/common/arbitrate/ArbitrateConfigImpl.java
new file mode 100644
index 00000000..34a72cd6
--- /dev/null
+++ b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/common/arbitrate/ArbitrateConfigImpl.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2010-2101 Alibaba Group Holding Limited.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.otter.manager.biz.common.arbitrate;
+
+import java.util.List;
+import java.util.Map;
+
+import org.springframework.beans.factory.InitializingBean;
+
+import com.alibaba.otter.manager.biz.config.channel.ChannelService;
+import com.alibaba.otter.manager.biz.config.node.NodeService;
+import com.alibaba.otter.shared.arbitrate.impl.config.ArbitrateConfig;
+import com.alibaba.otter.shared.arbitrate.impl.config.ArbitrateConfigRegistry;
+import com.alibaba.otter.shared.common.model.config.ConfigException;
+import com.alibaba.otter.shared.common.model.config.channel.Channel;
+import com.alibaba.otter.shared.common.model.config.node.Node;
+import com.alibaba.otter.shared.common.model.config.pipeline.Pipeline;
+import com.alibaba.otter.shared.common.utils.cache.RefreshMemoryMirror;
+import com.alibaba.otter.shared.common.utils.cache.RefreshMemoryMirror.ComputeFunction;
+import com.google.common.base.Function;
+import com.google.common.collect.MapMaker;
+
+/**
+ * manager下的基于db查询的{@linkplain ArbitrateConfig}实现
+ *
+ * @author jianghang 2011-11-3 上午11:09:24
+ * @version 4.0.0
+ */
+public class ArbitrateConfigImpl implements ArbitrateConfig, InitializingBean {
+
+ private static final Long DEFAULT_PERIOD = 60 * 1000L;
+ private Long timeout = DEFAULT_PERIOD;
+ private RefreshMemoryMirror channelCache;
+ private Map channelMapping;
+ private ChannelService channelService;
+ private NodeService nodeService;
+ private RefreshMemoryMirror nodeCache;
+
+ public ArbitrateConfigImpl(){
+ // 注册自己到arbitrate模块
+ ArbitrateConfigRegistry.regist(this);
+ }
+
+ public Node currentNode() {
+ return null;
+ }
+
+ public Node findNode(Long nid) {
+ return nodeCache.get(nid);
+ }
+
+ public Channel findChannel(Long channelId) {
+ return channelCache.get(channelId);
+ }
+
+ public Channel findChannelByPipelineId(Long pipelineId) {
+ Long channelId = channelMapping.get(pipelineId);
+ return channelCache.get(channelId);
+ }
+
+ public Pipeline findOppositePipeline(Long pipelineId) {
+ Long channelId = channelMapping.get(pipelineId);
+ Channel channel = channelCache.get(channelId);
+ List pipelines = channel.getPipelines();
+ for (Pipeline pipeline : pipelines) {
+ if (pipeline.getId().equals(pipelineId) == false) {// 这里假定pipeline只有两个
+ return pipeline;
+ }
+ }
+
+ return null;
+ }
+
+ public Pipeline findPipeline(Long pipelineId) {
+ Long channelId = channelMapping.get(pipelineId);
+ Channel channel = channelCache.get(channelId);
+ List pipelines = channel.getPipelines();
+ for (Pipeline pipeline : pipelines) {
+ if (pipeline.getId().equals(pipelineId)) {
+ return pipeline;
+ }
+ }
+
+ throw new ConfigException("no pipeline for pipelineId[" + pipelineId + "]");
+ }
+
+ public void afterPropertiesSet() throws Exception {
+ // 获取一下nid变量
+ channelMapping = new MapMaker().makeComputingMap(new Function() {
+
+ public Long apply(Long pipelineId) {
+ // 处理下pipline -> channel映射关系不存在的情况
+ Channel channel = channelService.findByPipelineId(pipelineId);
+ if (channel == null) {
+ throw new ConfigException("No Such Channel by pipelineId[" + pipelineId + "]");
+ }
+
+ updateMapping(channel, pipelineId);// 排除下自己
+ channelCache.put(channel.getId(), channel);// 更新下channelCache
+ return channel.getId();
+
+ }
+ });
+
+ channelCache = new RefreshMemoryMirror(timeout, new ComputeFunction() {
+
+ public Channel apply(Long key, Channel oldValue) {
+ Channel channel = channelService.findById(key);
+ if (channel == null) {
+ // 其他情况直接返回内存中的旧值
+ return oldValue;
+ } else {
+ updateMapping(channel, null);// 排除下自己
+ return channel;
+ }
+ }
+ });
+
+ nodeCache = new RefreshMemoryMirror(timeout, new ComputeFunction() {
+
+ public Node apply(Long key, Node oldValue) {
+ Node node = nodeService.findById(key);
+ if (node == null) {
+ return oldValue;
+ } else {
+ return node;
+ }
+ }
+ });
+ }
+
+ private void updateMapping(Channel channel, Long excludeId) {
+ Long channelId = channel.getId();
+ List pipelines = channel.getPipelines();
+ for (Pipeline pipeline : pipelines) {
+ if (excludeId == null || !pipeline.getId().equals(excludeId)) {
+ channelMapping.put(pipeline.getId(), channelId);
+ }
+ }
+ }
+
+ public void setChannelService(ChannelService channelService) {
+ this.channelService = channelService;
+ }
+
+ public void setNodeService(NodeService nodeService) {
+ this.nodeService = nodeService;
+ }
+
+}
diff --git a/manager/biz/src/main/java/com/alibaba/otter/manager/biz/common/arbitrate/DeadNodeListener.java b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/common/arbitrate/DeadNodeListener.java
new file mode 100644
index 00000000..c4c677fb
--- /dev/null
+++ b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/common/arbitrate/DeadNodeListener.java
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) 2010-2101 Alibaba Group Holding Limited.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.otter.manager.biz.common.arbitrate;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.DelayQueue;
+import java.util.concurrent.Delayed;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.DisposableBean;
+import org.springframework.beans.factory.InitializingBean;
+
+import com.alibaba.otter.manager.biz.config.channel.ChannelService;
+import com.alibaba.otter.manager.biz.monitor.PassiveMonitor;
+import com.alibaba.otter.shared.arbitrate.ArbitrateManageService;
+import com.alibaba.otter.shared.arbitrate.impl.setl.monitor.NodeMonitor;
+import com.alibaba.otter.shared.arbitrate.impl.setl.monitor.listener.NodeListener;
+import com.alibaba.otter.shared.common.model.config.alarm.MonitorName;
+import com.alibaba.otter.shared.common.model.config.channel.Channel;
+import com.alibaba.otter.shared.common.model.config.channel.ChannelStatus;
+import com.alibaba.otter.shared.communication.model.arbitrate.NodeAlarmEvent;
+import com.google.common.collect.Lists;
+
+/**
+ * 默认的死亡节点处理
+ *
+ * @author jianghang 2011-9-26 下午10:37:40
+ * @version 4.0.0
+ */
+public class DeadNodeListener implements NodeListener, InitializingBean, DisposableBean {
+
+ private static final Logger logger = LoggerFactory.getLogger(DeadNodeListener.class);
+ private volatile DelayQueue queue = new DelayQueue();
+ private NodeMonitor nodeMonitor;
+ private ArbitrateManageService arbitrateManageService;
+ private PassiveMonitor exceptionRuleMonitor;
+ private ChannelService channelService;
+ private ExecutorService executor;
+ private long checkTime = 60 * 1000L; // 30秒
+ private volatile List currentNodes = new ArrayList(); // 当前存活节点
+
+ public DeadNodeListener(){
+ }
+
+ public void afterPropertiesSet() throws Exception {
+ nodeMonitor.addListener(this);
+ executor = Executors.newFixedThreadPool(1);
+ executor.submit(new Runnable() {
+
+ public void run() {
+ while (true) {
+ DeadNodeDelayed delay = null;
+ try {
+ delay = queue.take();
+ processDead(delay.getNid());
+ } catch (Throwable e) {
+ logger.error(String.format("error happened with [%s]", delay.toString()), e);
+ }
+ }
+ }
+ });
+ }
+
+ public synchronized void processChanged(List nodes) {
+ Set deadNodes = new HashSet();
+ for (Long node : currentNodes) {
+ if (!nodes.contains(node)) {
+ deadNodes.add(node);
+ }
+ }
+
+ currentNodes = nodes;// 切换引用,需设置为volatile保证线程安全&可见性
+ // 处理下dead node
+ if (deadNodes.size() > 0) {
+ for (Long nid : deadNodes) {
+ Delayed delayed = new DeadNodeDelayed(nid, checkTime);
+ if (!queue.contains(delayed)) {// 不重复添加
+ queue.add(new DeadNodeDelayed(nid, checkTime));
+ }
+ }
+ }
+ }
+
+ private void processDead(Long deadNode) {
+ List aliveNodes = nodeMonitor.getAliveNodes(true);
+ // 需要考虑一种网络瞬断的情况,会导致node所有出现重连,导致出现restart风暴,执行restart时需要重新check下是否存活
+ if (aliveNodes.contains(deadNode)) {
+ logger.warn("dead node[{}] happend just one moment , check it's alived", deadNode);
+ return;
+ }
+
+ // 发送一条报警信息
+ List channelIds = Lists.newArrayList();
+ List channels = channelService.listByNodeId(deadNode, ChannelStatus.START);
+ for (Channel channel : channels) {
+ channelIds.add(channel.getId());
+ }
+ Collections.sort(channelIds);
+
+ NodeAlarmEvent alarm = new NodeAlarmEvent();
+ alarm.setPipelineId(-1L);
+ alarm.setTitle(MonitorName.EXCEPTION.name());
+ alarm.setMessage(String.format("nid:%s is dead and restart cids:%s", String.valueOf(deadNode),
+ channelIds.toString()));
+ try {
+ exceptionRuleMonitor.feed(alarm, alarm.getPipelineId());
+ } catch (Exception e) {
+ logger.error(String.format("ERROR # exceptionRuleMonitor error for %s", alarm.toString()), e);
+ }
+
+ for (Long channelId : channelIds) {// 重启一下对应的channel
+ boolean result = arbitrateManageService.channelEvent().restart(channelId);
+ if (result) {
+ channelService.notifyChannel(channelId);// 推送一下配置
+ }
+ }
+
+ }
+
+ public void destroy() throws Exception {
+ nodeMonitor.removeListener(this);
+ executor.shutdownNow();
+ }
+
+ /**
+ * 构建一个基于时间的delay queue,解决一个问题:node出现网络闪段,其jvm并不是真实关闭,可以间隔几秒后check一下是否存活再判断是否进行RESTART操作
+ *
+ * @author jianghang 2012-8-29 下午01:42:47
+ * @version 4.1.0
+ */
+ public static class DeadNodeDelayed implements Delayed {
+
+ // init time for nano
+ private static final long MILL_ORIGIN = System.currentTimeMillis();
+ private long nid;
+ private long now; // 记录具体的now的偏移时间点,单位ms
+ private long timeout; // 记录具体需要被delayed处理的偏移时间点,单位ms
+
+ public DeadNodeDelayed(long nid, long timeout){
+ this.nid = nid;
+ this.timeout = timeout;
+ this.now = System.currentTimeMillis() - MILL_ORIGIN;
+ }
+
+ public long getNid() {
+ return nid;
+ }
+
+ public long getNow() {
+ return now;
+ }
+
+ public long getDelay(TimeUnit unit) {
+ long currNow = System.currentTimeMillis() - MILL_ORIGIN;
+ long d = unit.convert(now + timeout - currNow, TimeUnit.MILLISECONDS);
+ return d;
+ }
+
+ public int compareTo(Delayed other) {
+ if (other == this) { // compare zero ONLY if same object
+ return 0;
+ } else if (other instanceof DeadNodeDelayed) {
+ DeadNodeDelayed x = (DeadNodeDelayed) other;
+ long diff = now + timeout - (x.now + x.timeout);
+ return (diff == 0) ? 0 : ((diff < 0) ? 1 : -1); // 时间越小的,越应该排在前面
+ } else {
+ long d = (getDelay(TimeUnit.MILLISECONDS) - other.getDelay(TimeUnit.MILLISECONDS));
+ return (d == 0) ? 0 : ((d < 0) ? 1 : -1);
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + (int) (nid ^ (nid >>> 32));
+ return result;
+ }
+
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (!(obj instanceof DeadNodeDelayed)) {
+ return false;
+ }
+ DeadNodeDelayed other = (DeadNodeDelayed) obj;
+ if (nid != other.nid) {
+ return false;
+ }
+ return true;
+ }
+
+ }
+
+ public void setNodeMonitor(NodeMonitor nodeMonitor) {
+ this.nodeMonitor = nodeMonitor;
+ }
+
+ public void setArbitrateManageService(ArbitrateManageService arbitrateManageService) {
+ this.arbitrateManageService = arbitrateManageService;
+ }
+
+ public void setChannelService(ChannelService channelService) {
+ this.channelService = channelService;
+ }
+
+ public void setExceptionRuleMonitor(PassiveMonitor exceptionRuleMonitor) {
+ this.exceptionRuleMonitor = exceptionRuleMonitor;
+ }
+
+}
diff --git a/manager/biz/src/main/java/com/alibaba/otter/manager/biz/common/basedao/GenericDAO.java b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/common/basedao/GenericDAO.java
new file mode 100644
index 00000000..dce9d1a5
--- /dev/null
+++ b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/common/basedao/GenericDAO.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2010-2101 Alibaba Group Holding Limited.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.otter.manager.biz.common.basedao;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author simon 2011-10-31 上午09:40:47
+ */
+public interface GenericDAO {
+
+ public T insert(T entityObj);
+
+ public void delete(Long identity);
+
+ public void update(T entityObj);
+
+ public List listAll();
+
+ public List listByCondition(Map condition);
+
+ public List listByMultiId(Long... identities);
+
+ public T findById(Long identity);
+
+ public int getCount();
+
+ public int getCount(Map condition);
+
+ public boolean checkUnique(T entityObj);
+
+}
diff --git a/manager/biz/src/main/java/com/alibaba/otter/manager/biz/common/baseservice/GenericService.java b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/common/baseservice/GenericService.java
new file mode 100644
index 00000000..a1248a07
--- /dev/null
+++ b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/common/baseservice/GenericService.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2010-2101 Alibaba Group Holding Limited.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.otter.manager.biz.common.baseservice;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author simon 2011-10-31 上午10:34:17
+ */
+public interface GenericService {
+
+ public void create(T entityObj);
+
+ public void remove(Long identity);
+
+ public void modify(T entityObj);
+
+ public T findById(Long identity);
+
+ public List listByIds(Long... identities);
+
+ public List listAll();
+
+ public List listByCondition(Map condition);
+
+ public int getCount();
+
+ public int getCount(Map condition);
+}
diff --git a/manager/biz/src/main/java/com/alibaba/otter/manager/biz/common/exceptions/InvalidConfigureException.java b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/common/exceptions/InvalidConfigureException.java
new file mode 100644
index 00000000..a45fa455
--- /dev/null
+++ b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/common/exceptions/InvalidConfigureException.java
@@ -0,0 +1,22 @@
+package com.alibaba.otter.manager.biz.common.exceptions;
+
+public class InvalidConfigureException extends RuntimeException {
+
+ private static final long serialVersionUID = 1L;
+
+ public static enum INVALID_TYPE {
+ DDL, HOME
+ }
+
+ private INVALID_TYPE type;
+
+ public InvalidConfigureException(INVALID_TYPE type){
+ super(type.name());
+ this.type = type;
+ }
+
+ public INVALID_TYPE getType() {
+ return type;
+ }
+
+}
diff --git a/manager/biz/src/main/java/com/alibaba/otter/manager/biz/common/exceptions/ManagerException.java b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/common/exceptions/ManagerException.java
new file mode 100644
index 00000000..96797d8b
--- /dev/null
+++ b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/common/exceptions/ManagerException.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2010-2101 Alibaba Group Holding Limited.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.otter.manager.biz.common.exceptions;
+
+/**
+ * ManagerException for Manager Model
+ *
+ * @author simon 2011-11-13 下午07:38:47
+ */
+public class ManagerException extends RuntimeException {
+
+ private static final long serialVersionUID = 1L;
+
+ public ManagerException(String cause){
+ super(cause);
+ }
+
+ public ManagerException(Throwable t){
+ super(t);
+ }
+
+ public ManagerException(String cause, Throwable t){
+ super(cause, t);
+ }
+
+}
diff --git a/manager/biz/src/main/java/com/alibaba/otter/manager/biz/common/exceptions/RepeatConfigureException.java b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/common/exceptions/RepeatConfigureException.java
new file mode 100644
index 00000000..0495de70
--- /dev/null
+++ b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/common/exceptions/RepeatConfigureException.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2010-2101 Alibaba Group Holding Limited.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.otter.manager.biz.common.exceptions;
+
+/**
+ * @author simon 2011-11-14 下午11:04:32
+ */
+public class RepeatConfigureException extends RuntimeException {
+
+ private static final long serialVersionUID = 1L;
+
+ public RepeatConfigureException(String cause){
+ super(cause);
+ }
+
+ public RepeatConfigureException(Throwable t){
+ super(t);
+ }
+
+ public RepeatConfigureException(String cause, Throwable t){
+ super(cause, t);
+ }
+
+}
diff --git a/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/alarm/AlarmRuleService.java b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/alarm/AlarmRuleService.java
new file mode 100644
index 00000000..6b9948ab
--- /dev/null
+++ b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/alarm/AlarmRuleService.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2010-2101 Alibaba Group Holding Limited.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.otter.manager.biz.config.alarm;
+
+import java.util.List;
+import java.util.Map;
+
+import com.alibaba.otter.shared.common.model.config.alarm.AlarmRule;
+import com.alibaba.otter.shared.common.model.config.alarm.AlarmRuleStatus;
+
+/**
+ * @author simon 2012-8-24 上午5:27:35
+ * @author zebin.xuzb
+ * @version 4.1.0
+ */
+public interface AlarmRuleService {
+
+ void create(AlarmRule alarmRule);
+
+ void modify(AlarmRule alarmRule);
+
+ void remove(Long alarmRuleId);
+
+ public void enableMonitor(Long alarmRuleId);
+
+ public void disableMonitor(Long alarmRuleId);
+
+ public void disableMonitor(Long alarmRuleId, String pauseTime);
+
+ List getAllAlarmRules(AlarmRuleStatus status);
+
+ AlarmRule getAlarmRuleById(Long AlarmRuleId);
+
+ /**
+ * 获取所有状态为 status 的 {@linkplain AlarmRule},并且按照pipelineId分区
+ *
+ * @param status
+ * @return
+ */
+ Map> getAlarmRules(AlarmRuleStatus status);
+
+ List getAlarmRules(Long pipelineId);
+
+ List getAlarmRules(Long pipelineId, AlarmRuleStatus status);
+
+ List listAllAlarmRules(Map condition);
+
+ public int getCount();
+
+}
diff --git a/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/alarm/dal/AlarmRuleDAO.java b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/alarm/dal/AlarmRuleDAO.java
new file mode 100644
index 00000000..128d106b
--- /dev/null
+++ b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/alarm/dal/AlarmRuleDAO.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2010-2101 Alibaba Group Holding Limited.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.otter.manager.biz.config.alarm.dal;
+
+import java.util.List;
+import java.util.Map;
+
+import com.alibaba.otter.manager.biz.config.alarm.dal.dataobject.AlarmRuleDO;
+import com.alibaba.otter.shared.common.model.config.alarm.AlarmRuleStatus;
+
+/**
+ * @author simon 2012-8-24 上午5:17:00
+ * @version 4.1.0
+ */
+public interface AlarmRuleDAO {
+
+ public AlarmRuleDO insert(AlarmRuleDO entityObj);
+
+ public void update(AlarmRuleDO entityObj);
+
+ public void delete(Long id);
+
+ public AlarmRuleDO findById(Long alarmRuleId);
+
+ public List listByPipelineId(Long pipelineId);
+
+ public List listByPipelineId(Long pipelineId, AlarmRuleStatus status);
+
+ public List listAll();
+
+ public List listAllByPipeline(Map condition);
+
+ public List listByStatus(AlarmRuleStatus status);
+
+ public int getCount();
+
+}
diff --git a/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/alarm/dal/dataobject/AlarmRuleDO.java b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/alarm/dal/dataobject/AlarmRuleDO.java
new file mode 100644
index 00000000..2339df92
--- /dev/null
+++ b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/alarm/dal/dataobject/AlarmRuleDO.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2010-2101 Alibaba Group Holding Limited.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.otter.manager.biz.config.alarm.dal.dataobject;
+
+import java.io.Serializable;
+import java.util.Date;
+
+import com.alibaba.otter.shared.common.model.config.alarm.AlarmRuleStatus;
+import com.alibaba.otter.shared.common.model.config.alarm.MonitorName;
+
+/**
+ * @author simon 2012-8-22 下午3:42:34
+ * @version 4.1.0
+ */
+public class AlarmRuleDO implements Serializable {
+
+ private static final long serialVersionUID = -1284784362325347636L;
+ private Long id;
+ private Long pipelineId;
+ private AlarmRuleStatus status;
+ private MonitorName monitorName;
+ private String receiverKey;
+ private String matchValue;
+ private AlarmRuleParameter alarmRuleParameter = new AlarmRuleParameter();
+ private String description;
+ private Date gmtCreate;
+ private Date gmtModified;
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public Long getPipelineId() {
+ return pipelineId;
+ }
+
+ public void setPipelineId(Long pipelineId) {
+ this.pipelineId = pipelineId;
+ }
+
+ public AlarmRuleStatus getStatus() {
+ return status;
+ }
+
+ public void setStatus(AlarmRuleStatus status) {
+ this.status = status;
+ }
+
+ public MonitorName getMonitorName() {
+ return monitorName;
+ }
+
+ public void setMonitorName(MonitorName monitorName) {
+ this.monitorName = monitorName;
+ }
+
+ public String getReceiverKey() {
+ return receiverKey;
+ }
+
+ public void setReceiverKey(String receiverKey) {
+ this.receiverKey = receiverKey;
+ }
+
+ public String getMatchValue() {
+ return matchValue;
+ }
+
+ public void setMatchValue(String matchValue) {
+ this.matchValue = matchValue;
+ }
+
+ public AlarmRuleParameter getAlarmRuleParameter() {
+ return alarmRuleParameter;
+ }
+
+ public void setAlarmRuleParameter(AlarmRuleParameter alarmRuleParameter) {
+ this.alarmRuleParameter = alarmRuleParameter;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public Date getGmtCreate() {
+ return gmtCreate;
+ }
+
+ public void setGmtCreate(Date gmtCreate) {
+ this.gmtCreate = gmtCreate;
+ }
+
+ public Date getGmtModified() {
+ return gmtModified;
+ }
+
+ public void setGmtModified(Date gmtModified) {
+ this.gmtModified = gmtModified;
+ }
+
+}
diff --git a/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/alarm/dal/dataobject/AlarmRuleParameter.java b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/alarm/dal/dataobject/AlarmRuleParameter.java
new file mode 100644
index 00000000..2da5a2c0
--- /dev/null
+++ b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/alarm/dal/dataobject/AlarmRuleParameter.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2010-2101 Alibaba Group Holding Limited.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.otter.manager.biz.config.alarm.dal.dataobject;
+
+import java.io.Serializable;
+
+/**
+ * @author simon 2012-9-12 上午10:35:13
+ * @version 4.1.0
+ */
+public class AlarmRuleParameter implements Serializable {
+
+ private static final long serialVersionUID = 1570395344191530689L;
+ private Long intervalTime = 1800L;
+ private String pauseTime;
+ private Integer recoveryThresold = 3;
+ private Boolean autoRecovery = false;
+
+ public Long getIntervalTime() {
+ return intervalTime;
+ }
+
+ public void setIntervalTime(Long intervalTime) {
+ this.intervalTime = intervalTime;
+ }
+
+ public String getPauseTime() {
+ return pauseTime;
+ }
+
+ public void setPauseTime(String pauseTime) {
+ this.pauseTime = pauseTime;
+ }
+
+ public Boolean getAutoRecovery() {
+ return autoRecovery;
+ }
+
+ public void setAutoRecovery(Boolean autoRecovery) {
+ this.autoRecovery = autoRecovery;
+ }
+
+ public Integer getRecoveryThresold() {
+ return recoveryThresold;
+ }
+
+ public void setRecoveryThresold(Integer recoveryThresold) {
+ this.recoveryThresold = recoveryThresold;
+ }
+
+}
diff --git a/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/alarm/dal/ibatis/AlarmRuleParameterTypeHandler.java b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/alarm/dal/ibatis/AlarmRuleParameterTypeHandler.java
new file mode 100644
index 00000000..d81f072c
--- /dev/null
+++ b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/alarm/dal/ibatis/AlarmRuleParameterTypeHandler.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2010-2101 Alibaba Group Holding Limited.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.otter.manager.biz.config.alarm.dal.ibatis;
+
+import java.sql.SQLException;
+
+import com.alibaba.otter.manager.biz.config.alarm.dal.dataobject.AlarmRuleParameter;
+import com.alibaba.otter.shared.common.utils.JsonUtils;
+import com.ibatis.sqlmap.client.extensions.ParameterSetter;
+import com.ibatis.sqlmap.client.extensions.ResultGetter;
+import com.ibatis.sqlmap.client.extensions.TypeHandlerCallback;
+
+/**
+ * 用于AlarmRuleParameter的解析
+ *
+ * @author simon
+ */
+public class AlarmRuleParameterTypeHandler implements TypeHandlerCallback {
+
+ @Override
+ public void setParameter(ParameterSetter setter, Object parameter) throws SQLException {
+ setter.setString(JsonUtils.marshalToString(parameter));
+ }
+
+ @Override
+ public Object getResult(ResultGetter getter) throws SQLException {
+ return JsonUtils.unmarshalFromString(getter.getString(), AlarmRuleParameter.class);
+ }
+
+ public Object valueOf(String s) {
+ return JsonUtils.unmarshalFromString(s, AlarmRuleParameter.class);
+ }
+
+}
diff --git a/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/alarm/dal/ibatis/IbatisAlarmRuleDAO.java b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/alarm/dal/ibatis/IbatisAlarmRuleDAO.java
new file mode 100644
index 00000000..355e493b
--- /dev/null
+++ b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/alarm/dal/ibatis/IbatisAlarmRuleDAO.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2010-2101 Alibaba Group Holding Limited.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.otter.manager.biz.config.alarm.dal.ibatis;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import org.springframework.orm.ibatis.support.SqlMapClientDaoSupport;
+
+import com.alibaba.otter.shared.common.utils.Assert;
+import com.alibaba.otter.manager.biz.config.alarm.dal.AlarmRuleDAO;
+import com.alibaba.otter.manager.biz.config.alarm.dal.dataobject.AlarmRuleDO;
+import com.alibaba.otter.shared.common.model.config.alarm.AlarmRuleStatus;
+
+/**
+ * @author simon
+ */
+public class IbatisAlarmRuleDAO extends SqlMapClientDaoSupport implements AlarmRuleDAO {
+
+ public AlarmRuleDO insert(AlarmRuleDO entityObj) {
+ Assert.assertNotNull(entityObj);
+ getSqlMapClientTemplate().insert("insertAlarmRule", entityObj);
+ return entityObj;
+ }
+
+ public void update(AlarmRuleDO entityObj) {
+ Assert.assertNotNull(entityObj);
+ getSqlMapClientTemplate().update("updateAlarmRule", entityObj);
+ }
+
+ public void delete(Long id) {
+ Assert.assertNotNull(id);
+ getSqlMapClientTemplate().update("deleteAlarmRuleById", id);
+ }
+
+ public AlarmRuleDO findById(Long alarmRuleId) {
+ Assert.assertNotNull(alarmRuleId);
+ AlarmRuleDO alarmRuleDo = (AlarmRuleDO) getSqlMapClientTemplate().queryForObject("findByRuleId", alarmRuleId);
+ return alarmRuleDo;
+ }
+
+ public List listByPipelineId(Long pipelineId) {
+ Assert.assertNotNull(pipelineId);
+ List alarmRuleDos = getSqlMapClientTemplate().queryForList("listAlarmByPipelineId", pipelineId);
+ return alarmRuleDos;
+ }
+
+ public List listByPipelineId(Long pipelineId, AlarmRuleStatus status) {
+ List alarmRuleDos = listByPipelineId(pipelineId);
+ List result = new ArrayList();
+ for (AlarmRuleDO alarmRuleDo : alarmRuleDos) {
+ if (alarmRuleDo.getStatus().equals(status)) {
+ result.add(alarmRuleDo);
+ }
+ }
+ return result;
+ }
+
+ public List listAll() {
+ List alarmRuleDos = getSqlMapClientTemplate().queryForList("listAllAlarmRule");
+ return alarmRuleDos;
+ }
+
+ public List listAllByPipeline(Map condition) {
+ List alarmRuleDos = getSqlMapClientTemplate().queryForList("listAllAlarmOrderByPipeline",
+ condition);
+ return alarmRuleDos;
+ }
+
+ public List listByStatus(AlarmRuleStatus status) {
+ List alarmRuleDos = getSqlMapClientTemplate().queryForList("listAlarmByStatus", status);
+ return alarmRuleDos;
+ }
+
+ public int getCount() {
+ Integer count = (Integer) getSqlMapClientTemplate().queryForObject("getAlarmRuleCount");
+ return count.intValue();
+ }
+
+}
diff --git a/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/alarm/impl/AlarmRuleServiceImpl.java b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/alarm/impl/AlarmRuleServiceImpl.java
new file mode 100644
index 00000000..10c2dccd
--- /dev/null
+++ b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/alarm/impl/AlarmRuleServiceImpl.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2010-2101 Alibaba Group Holding Limited.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.otter.manager.biz.config.alarm.impl;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.lang.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.alibaba.otter.manager.biz.common.exceptions.ManagerException;
+import com.alibaba.otter.manager.biz.config.alarm.AlarmRuleService;
+import com.alibaba.otter.manager.biz.config.alarm.dal.AlarmRuleDAO;
+import com.alibaba.otter.manager.biz.config.alarm.dal.dataobject.AlarmRuleDO;
+import com.alibaba.otter.manager.biz.config.alarm.dal.dataobject.AlarmRuleParameter;
+import com.alibaba.otter.shared.common.model.config.alarm.AlarmRule;
+import com.alibaba.otter.shared.common.model.config.alarm.AlarmRuleStatus;
+import com.alibaba.otter.shared.common.utils.Assert;
+
+/**
+ * @author simon 2012-8-24 上午5:29:55
+ * @version 4.1.0
+ */
+public class AlarmRuleServiceImpl implements AlarmRuleService {
+
+ private static final Logger logger = LoggerFactory.getLogger(AlarmRuleServiceImpl.class);
+ public static final String TIMESTAMP_FORMAT = "yyyy-MM-dd HH:mm:ss";
+ private AlarmRuleDAO alarmRuleDao;
+
+ public void create(AlarmRule alarmRule) {
+ Assert.assertNotNull(alarmRule);
+ alarmRuleDao.insert(modelToDo(alarmRule));
+ }
+
+ public void modify(AlarmRule alarmRule) {
+ AlarmRuleDO alarmRuleDo = modelToDo(alarmRule);
+ alarmRuleDao.update(alarmRuleDo);
+
+ }
+
+ public void remove(Long alarmRuleId) {
+ alarmRuleDao.delete(alarmRuleId);
+ }
+
+ private void switchAlarmRuleStatus(Long alarmRuleId, AlarmRuleStatus alarmRuleStatus, String pauseTime) {
+ AlarmRuleDO alarmRuleDo = alarmRuleDao.findById(alarmRuleId);
+
+ if (null == alarmRuleDo) {
+ String exceptionCause = "query alarmRule:" + alarmRuleId + " return null.";
+ logger.error("ERROR ## " + exceptionCause);
+ throw new ManagerException(exceptionCause);
+ }
+
+ alarmRuleDo.setStatus(alarmRuleStatus);
+ if (alarmRuleDo.getAlarmRuleParameter() != null) {
+ alarmRuleDo.getAlarmRuleParameter().setPauseTime(pauseTime);
+ } else if (StringUtils.isNotEmpty(pauseTime)) {
+ alarmRuleDo.setAlarmRuleParameter(new AlarmRuleParameter());
+ alarmRuleDo.getAlarmRuleParameter().setPauseTime(pauseTime);
+ }
+ alarmRuleDao.update(alarmRuleDo);
+ }
+
+ public void enableMonitor(Long alarmRuleId) {
+ switchAlarmRuleStatus(alarmRuleId, AlarmRuleStatus.ENABLE, null);
+ }
+
+ public void disableMonitor(Long alarmRuleId) {
+ switchAlarmRuleStatus(alarmRuleId, AlarmRuleStatus.DISABLE, null);
+ }
+
+ public void disableMonitor(Long alarmRuleId, String pauseTime) {
+ switchAlarmRuleStatus(alarmRuleId, AlarmRuleStatus.ENABLE, pauseTime);
+ }
+
+ public AlarmRule getAlarmRuleById(Long AlarmRuleId) {
+ Assert.assertNotNull(AlarmRuleId);
+ return doToModel(alarmRuleDao.findById(AlarmRuleId));
+ }
+
+ public List getAllAlarmRules(AlarmRuleStatus status) {
+ Assert.assertNotNull(status);
+ List alarmRuleDos = alarmRuleDao.listByStatus(status);
+ return doToModel(alarmRuleDos);
+ }
+
+ public Map> getAlarmRules(AlarmRuleStatus status) {
+ Assert.assertNotNull(status);
+ List alarmRules = getAllAlarmRules(status);
+ Map> result = new HashMap>();
+ for (AlarmRule alarmRule : alarmRules) {
+ List rules = result.get(alarmRule.getPipelineId());
+ if (rules == null) {
+ rules = new ArrayList();
+ }
+ if (!rules.contains(alarmRule)) {
+ rules.add(alarmRule);
+ }
+ result.put(alarmRule.getPipelineId(), rules);
+ }
+ return result;
+ }
+
+ public List getAlarmRules(Long pipelineId) {
+ Assert.assertNotNull(pipelineId);
+ List alarmRuleDos = alarmRuleDao.listByPipelineId(pipelineId);
+ return doToModel(alarmRuleDos);
+ }
+
+ public List getAlarmRules(Long pipelineId, AlarmRuleStatus status) {
+ Assert.assertNotNull(pipelineId);
+ Assert.assertNotNull(status);
+ List alarmRuleDos = alarmRuleDao.listByPipelineId(pipelineId, status);
+
+ return doToModel(alarmRuleDos);
+ }
+
+ public List listAllAlarmRules(Map condition) {
+ List alarmRules = doToModel(alarmRuleDao.listAllByPipeline(condition));
+ return alarmRules;
+ }
+
+ public int getCount() {
+ return alarmRuleDao.getCount();
+ }
+
+ private AlarmRule doToModel(AlarmRuleDO alarmRuleDo) {
+ AlarmRule alarmRule = new AlarmRule();
+ alarmRule.setId(alarmRuleDo.getId());
+ alarmRule.setMatchValue(alarmRuleDo.getMatchValue());
+ alarmRule.setMonitorName(alarmRuleDo.getMonitorName());
+ alarmRule.setReceiverKey(alarmRuleDo.getReceiverKey());
+ // 如果数据库里面的数据为空,则返回默认值
+ alarmRule.setIntervalTime(alarmRuleDo.getAlarmRuleParameter() == null ? 1800L : alarmRuleDo.getAlarmRuleParameter().getIntervalTime());
+ String pauseTime = alarmRuleDo.getAlarmRuleParameter() == null ? null : alarmRuleDo.getAlarmRuleParameter().getPauseTime();
+ if (StringUtils.isNotEmpty(pauseTime)) {
+ SimpleDateFormat format = new SimpleDateFormat(TIMESTAMP_FORMAT);
+ try {
+ alarmRule.setPauseTime(format.parse(pauseTime));
+ } catch (ParseException e) {
+ throw new ManagerException(e);
+ }
+ }
+
+ alarmRule.setAutoRecovery(alarmRuleDo.getAlarmRuleParameter() == null ? false : alarmRuleDo.getAlarmRuleParameter().getAutoRecovery());
+ alarmRule.setRecoveryThresold(alarmRuleDo.getAlarmRuleParameter() == null ? 3 : alarmRuleDo.getAlarmRuleParameter().getRecoveryThresold());
+ alarmRule.setPipelineId(alarmRuleDo.getPipelineId());
+ alarmRule.setStatus(alarmRuleDo.getStatus());
+ alarmRule.setDescription(alarmRuleDo.getDescription());
+ alarmRule.setGmtCreate(alarmRuleDo.getGmtCreate());
+ alarmRule.setGmtModified(alarmRuleDo.getGmtModified());
+ return alarmRule;
+ }
+
+ private List doToModel(List alarmRuleDos) {
+ List alarmRules = new ArrayList();
+ for (AlarmRuleDO alarmRuleDo : alarmRuleDos) {
+ alarmRules.add(doToModel(alarmRuleDo));
+ }
+ return alarmRules;
+ }
+
+ private AlarmRuleDO modelToDo(AlarmRule alarmRule) {
+ AlarmRuleDO alarmRuleDo = new AlarmRuleDO();
+ alarmRuleDo.setId(alarmRule.getId());
+ alarmRuleDo.setMatchValue(alarmRule.getMatchValue());
+ alarmRuleDo.setMonitorName(alarmRule.getMonitorName());
+ alarmRuleDo.setReceiverKey(alarmRule.getReceiverKey());
+ alarmRuleDo.setPipelineId(alarmRule.getPipelineId());
+ alarmRuleDo.setStatus(alarmRule.getStatus());
+ alarmRuleDo.setDescription(alarmRule.getDescription());
+ alarmRuleDo.setGmtCreate(alarmRule.getGmtCreate());
+ alarmRuleDo.setGmtModified(alarmRule.getGmtModified());
+ AlarmRuleParameter alarmRuleParameter = new AlarmRuleParameter();
+ alarmRuleParameter.setIntervalTime(alarmRule.getIntervalTime());
+ if (alarmRule.getPauseTime() != null) {
+ SimpleDateFormat format = new SimpleDateFormat(TIMESTAMP_FORMAT);
+ alarmRuleParameter.setPauseTime(format.format(alarmRule.getPauseTime()));
+ }
+ alarmRuleParameter.setAutoRecovery(alarmRule.getAutoRecovery());
+ alarmRuleParameter.setRecoveryThresold(alarmRule.getRecoveryThresold());
+ alarmRuleDo.setAlarmRuleParameter(alarmRuleParameter);
+
+ return alarmRuleDo;
+ }
+
+ public void setAlarmRuleDao(AlarmRuleDAO alarmRuleDao) {
+ this.alarmRuleDao = alarmRuleDao;
+ }
+
+}
diff --git a/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/autokeeper/AutoKeeperClusterService.java b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/autokeeper/AutoKeeperClusterService.java
new file mode 100644
index 00000000..8a0a80f9
--- /dev/null
+++ b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/autokeeper/AutoKeeperClusterService.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2010-2101 Alibaba Group Holding Limited.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.otter.manager.biz.config.autokeeper;
+
+import java.util.List;
+
+import com.alibaba.otter.shared.common.model.autokeeper.AutoKeeperCluster;
+
+/**
+ * @author simon 2012-9-24 下午5:35:01
+ * @version 4.1.0
+ */
+public interface AutoKeeperClusterService {
+
+ public AutoKeeperCluster findAutoKeeperClusterById(Long id);
+
+ public List listAutoKeeperClusters();
+
+ public void modifyAutoKeeperCluster(AutoKeeperCluster autoKeeperCluster);
+
+ public void createAutoKeeperCluster(AutoKeeperCluster autoKeeperCluster);
+
+ public void removeAutoKeeperCluster(Long id);
+
+ public Integer getCount();
+}
diff --git a/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/autokeeper/dal/AutoKeeperClusterDAO.java b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/autokeeper/dal/AutoKeeperClusterDAO.java
new file mode 100644
index 00000000..68fbc0f1
--- /dev/null
+++ b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/autokeeper/dal/AutoKeeperClusterDAO.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2010-2101 Alibaba Group Holding Limited.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.otter.manager.biz.config.autokeeper.dal;
+
+import java.util.List;
+
+import com.alibaba.otter.manager.biz.config.autokeeper.dal.dataobject.AutoKeeperClusterDO;
+
+public interface AutoKeeperClusterDAO {
+
+ public AutoKeeperClusterDO findAutoKeeperClusterById(Long id);
+
+ public List listAutoKeeperClusters();
+
+ public void updateAutoKeeperCluster(AutoKeeperClusterDO autoKeeperClusterDo);
+
+ public void insertAutoKeeperClusterDO(AutoKeeperClusterDO autoKeeperClusterDo);
+
+ public void delete(Long clusterId);
+
+ public Integer getCount();
+
+}
diff --git a/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/autokeeper/dal/dataobject/AutoKeeperClusterDO.java b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/autokeeper/dal/dataobject/AutoKeeperClusterDO.java
new file mode 100644
index 00000000..e097ac73
--- /dev/null
+++ b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/autokeeper/dal/dataobject/AutoKeeperClusterDO.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2010-2101 Alibaba Group Holding Limited.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.otter.manager.biz.config.autokeeper.dal.dataobject;
+
+import java.util.Date;
+
+import org.apache.commons.lang.builder.ToStringBuilder;
+
+import com.alibaba.otter.shared.common.utils.OtterToStringStyle;
+
+/**
+ * zk集群监控对象
+ *
+ * @author jianghang 2012-9-21 下午01:54:17
+ * @version 4.1.0
+ */
+public class AutoKeeperClusterDO {
+
+ private Long id;
+ private String clusterName;
+ private String serverList; // 机器列表
+ private String description; // 描述
+ private Date gmtCreate;
+ private Date gmtModified;
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getClusterName() {
+ return clusterName;
+ }
+
+ public void setClusterName(String clusterName) {
+ this.clusterName = clusterName;
+ }
+
+ public String getServerList() {
+ return serverList;
+ }
+
+ public void setServerList(String serverList) {
+ this.serverList = serverList;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public Date getGmtCreate() {
+ return gmtCreate;
+ }
+
+ public void setGmtCreate(Date gmtCreate) {
+ this.gmtCreate = gmtCreate;
+ }
+
+ public Date getGmtModified() {
+ return gmtModified;
+ }
+
+ public void setGmtModified(Date gmtModified) {
+ this.gmtModified = gmtModified;
+ }
+
+ public String toString() {
+ return ToStringBuilder.reflectionToString(this, OtterToStringStyle.DEFAULT_STYLE);
+ }
+
+}
diff --git a/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/autokeeper/dal/ibatis/IbatisAutoKeeperClusterDAO.java b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/autokeeper/dal/ibatis/IbatisAutoKeeperClusterDAO.java
new file mode 100644
index 00000000..a66b668c
--- /dev/null
+++ b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/autokeeper/dal/ibatis/IbatisAutoKeeperClusterDAO.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2010-2101 Alibaba Group Holding Limited.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.otter.manager.biz.config.autokeeper.dal.ibatis;
+
+import java.util.List;
+
+import org.springframework.orm.ibatis.support.SqlMapClientDaoSupport;
+
+import com.alibaba.otter.shared.common.utils.Assert;
+import com.alibaba.otter.manager.biz.config.autokeeper.dal.AutoKeeperClusterDAO;
+import com.alibaba.otter.manager.biz.config.autokeeper.dal.dataobject.AutoKeeperClusterDO;
+
+/**
+ * 类IbatisAutoKeeperClusterDAO.java的实现描述:TODO 类实现描述
+ *
+ * @author simon 2012-9-24 下午5:17:17
+ * @version 4.1.0
+ */
+public class IbatisAutoKeeperClusterDAO extends SqlMapClientDaoSupport implements AutoKeeperClusterDAO {
+
+ @Override
+ public AutoKeeperClusterDO findAutoKeeperClusterById(Long id) {
+ Assert.assertNotNull(id);
+ return (AutoKeeperClusterDO) getSqlMapClientTemplate().queryForObject("findAutoKeeperClusterById", id);
+ }
+
+ @Override
+ public List listAutoKeeperClusters() {
+ List autoKeeperClusterDOs = getSqlMapClientTemplate().queryForList("listAutoKeeperClusters");
+ return autoKeeperClusterDOs;
+ }
+
+ @Override
+ public void updateAutoKeeperCluster(AutoKeeperClusterDO autoKeeperClusterDo) {
+ Assert.assertNotNull(autoKeeperClusterDo);
+ getSqlMapClientTemplate().update("updateAutoKeeperCluster", autoKeeperClusterDo);
+ }
+
+ @Override
+ public void insertAutoKeeperClusterDO(AutoKeeperClusterDO autoKeeperClusterDo) {
+ Assert.assertNotNull(autoKeeperClusterDo);
+ getSqlMapClientTemplate().insert("insertAutoKeeperCluster", autoKeeperClusterDo);
+ }
+
+ @Override
+ public void delete(Long clusterId) {
+ Assert.assertNotNull(clusterId);
+ getSqlMapClientTemplate().insert("deleteAutoKeeperClusterById", clusterId);
+ }
+
+ @Override
+ public Integer getCount() {
+ Integer count = (Integer) getSqlMapClientTemplate().queryForObject("getAutoKeeperClusterCount");
+ return count.intValue();
+ }
+
+}
diff --git a/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/autokeeper/impl/AutoKeeperClusterServiceImpl.java b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/autokeeper/impl/AutoKeeperClusterServiceImpl.java
new file mode 100644
index 00000000..e402bf71
--- /dev/null
+++ b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/autokeeper/impl/AutoKeeperClusterServiceImpl.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2010-2101 Alibaba Group Holding Limited.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.otter.manager.biz.config.autokeeper.impl;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.alibaba.fastjson.TypeReference;
+import com.alibaba.otter.manager.biz.config.autokeeper.AutoKeeperClusterService;
+import com.alibaba.otter.manager.biz.config.autokeeper.dal.AutoKeeperClusterDAO;
+import com.alibaba.otter.manager.biz.config.autokeeper.dal.dataobject.AutoKeeperClusterDO;
+import com.alibaba.otter.shared.common.model.autokeeper.AutoKeeperCluster;
+import com.alibaba.otter.shared.common.utils.JsonUtils;
+
+/**
+ * 类AutoKeeperClusterServiceImpl.java的实现描述:TODO 类实现描述
+ *
+ * @author simon 2012-9-24 下午5:44:43
+ * @version 4.1.0
+ */
+public class AutoKeeperClusterServiceImpl implements AutoKeeperClusterService {
+
+ private AutoKeeperClusterDAO autoKeeperClusterDao;
+
+ @Override
+ public AutoKeeperCluster findAutoKeeperClusterById(Long id) {
+ AutoKeeperClusterDO autoKeeperClusterDO = autoKeeperClusterDao.findAutoKeeperClusterById(id);
+ return autoKeeperClusterDO == null ? null : doToModel(autoKeeperClusterDO);
+ }
+
+ @Override
+ public List listAutoKeeperClusters() {
+ return doToModel(autoKeeperClusterDao.listAutoKeeperClusters());
+ }
+
+ @Override
+ public void modifyAutoKeeperCluster(AutoKeeperCluster autoKeeperCluster) {
+ autoKeeperClusterDao.updateAutoKeeperCluster(modelToDo(autoKeeperCluster));
+ }
+
+ @Override
+ public void createAutoKeeperCluster(AutoKeeperCluster autoKeeperCluster) {
+ autoKeeperClusterDao.insertAutoKeeperClusterDO(modelToDo(autoKeeperCluster));
+ }
+
+ @Override
+ public void removeAutoKeeperCluster(Long id) {
+ autoKeeperClusterDao.delete(id);
+ }
+
+ private AutoKeeperCluster doToModel(AutoKeeperClusterDO autoKeeperClusterDo) {
+ AutoKeeperCluster autoKeeperCluster = new AutoKeeperCluster();
+ autoKeeperCluster.setId(autoKeeperClusterDo.getId());
+ autoKeeperCluster.setClusterName(autoKeeperClusterDo.getClusterName());
+ autoKeeperCluster.setDescription(autoKeeperClusterDo.getDescription());
+ autoKeeperCluster.setServerList(JsonUtils.unmarshalFromString(autoKeeperClusterDo.getServerList(),
+ new TypeReference>() {
+ }));
+ autoKeeperCluster.setGmtCreate(autoKeeperClusterDo.getGmtCreate());
+ autoKeeperCluster.setGmtModified(autoKeeperClusterDo.getGmtModified());
+ return autoKeeperCluster;
+ }
+
+ public Integer getCount() {
+ return autoKeeperClusterDao.getCount();
+ }
+
+ private List doToModel(List autoKeeperClusterDos) {
+ List autoKeeperClusters = new ArrayList();
+ for (AutoKeeperClusterDO autoKeeperClusterDo : autoKeeperClusterDos) {
+ autoKeeperClusters.add(doToModel(autoKeeperClusterDo));
+ }
+ return autoKeeperClusters;
+ }
+
+ private AutoKeeperClusterDO modelToDo(AutoKeeperCluster autoKeeperCluster) {
+ AutoKeeperClusterDO autokeeperClusterDo = new AutoKeeperClusterDO();
+ autokeeperClusterDo.setId(autoKeeperCluster.getId());
+ autokeeperClusterDo.setClusterName(autoKeeperCluster.getClusterName());
+ autokeeperClusterDo.setDescription(autoKeeperCluster.getDescription());
+ autokeeperClusterDo.setServerList(JsonUtils.marshalToString(autoKeeperCluster.getServerList()));
+ autokeeperClusterDo.setGmtCreate(autoKeeperCluster.getGmtCreate());
+ autokeeperClusterDo.setGmtModified(autoKeeperCluster.getGmtModified());
+ return autokeeperClusterDo;
+ }
+
+ public void setAutoKeeperClusterDao(AutoKeeperClusterDAO autoKeeperClusterDao) {
+ this.autoKeeperClusterDao = autoKeeperClusterDao;
+ }
+
+}
diff --git a/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/canal/CanalService.java b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/canal/CanalService.java
new file mode 100644
index 00000000..12451347
--- /dev/null
+++ b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/canal/CanalService.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2010-2101 Alibaba Group Holding Limited.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.otter.manager.biz.config.canal;
+
+import java.util.List;
+import java.util.Map;
+
+import com.alibaba.otter.canal.instance.manager.model.Canal;
+
+/**
+ * @author sarah.lij 2012-7-25 下午04:02:20
+ */
+public interface CanalService {
+
+ public void create(Canal canal);
+
+ public void remove(Long canalId);
+
+ public void modify(Canal canal);
+
+ public List listByIds(Long... identities);
+
+ public List listAll();
+
+ public Canal findById(Long canalId);
+
+ public Canal findByName(String name);
+
+ public int getCount(Map condition);
+
+ public List listByCondition(Map condition);
+
+}
diff --git a/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/canal/dal/CanalDAO.java b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/canal/dal/CanalDAO.java
new file mode 100644
index 00000000..a73cf183
--- /dev/null
+++ b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/canal/dal/CanalDAO.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2010-2101 Alibaba Group Holding Limited.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.otter.manager.biz.config.canal.dal;
+
+import com.alibaba.otter.manager.biz.common.basedao.GenericDAO;
+import com.alibaba.otter.manager.biz.config.canal.dal.dataobject.CanalDO;
+
+/**
+ * @author sarah.lij 2012-7-25 下午05:05:37
+ */
+public interface CanalDAO extends GenericDAO {
+
+ public CanalDO findByName(String name);
+}
diff --git a/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/canal/dal/dataobject/CanalDO.java b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/canal/dal/dataobject/CanalDO.java
new file mode 100644
index 00000000..b5070627
--- /dev/null
+++ b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/canal/dal/dataobject/CanalDO.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2010-2101 Alibaba Group Holding Limited.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.otter.manager.biz.config.canal.dal.dataobject;
+
+import java.io.Serializable;
+import java.util.Date;
+
+import com.alibaba.otter.canal.instance.manager.model.CanalParameter;
+import com.alibaba.otter.canal.instance.manager.model.CanalStatus;
+
+/**
+ * 类CanalDO.java的实现描述:TODO 类实现描述
+ *
+ * @author sarah.lij 2012-7-25 下午05:11:18
+ */
+public class CanalDO implements Serializable {
+
+ private static final long serialVersionUID = 9148286590254926037L;
+
+ private Long id; // 唯一标示id
+ private String name; // canal名字
+ private String description; // 描述
+ private CanalStatus status;
+ private CanalParameter parameters; // 参数定义
+ private Date gmtCreate; // 创建时间
+ private Date gmtModified; // 修改时间
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public CanalStatus getStatus() {
+ return status;
+ }
+
+ public void setStatus(CanalStatus status) {
+ this.status = status;
+ }
+
+ public CanalParameter getParameters() {
+ return parameters;
+ }
+
+ public void setParameters(CanalParameter parameters) {
+ this.parameters = parameters;
+ }
+
+ public Date getGmtCreate() {
+ return gmtCreate;
+ }
+
+ public void setGmtCreate(Date gmtCreate) {
+ this.gmtCreate = gmtCreate;
+ }
+
+ public Date getGmtModified() {
+ return gmtModified;
+ }
+
+ public void setGmtModified(Date gmtModified) {
+ this.gmtModified = gmtModified;
+ }
+
+}
diff --git a/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/canal/dal/ibatis/CanalParameterTypeHandler.java b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/canal/dal/ibatis/CanalParameterTypeHandler.java
new file mode 100644
index 00000000..cd00c37c
--- /dev/null
+++ b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/canal/dal/ibatis/CanalParameterTypeHandler.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2010-2101 Alibaba Group Holding Limited.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.otter.manager.biz.config.canal.dal.ibatis;
+
+import java.sql.SQLException;
+
+import com.alibaba.otter.canal.instance.manager.model.CanalParameter;
+import com.alibaba.otter.shared.common.utils.JsonUtils;
+import com.ibatis.sqlmap.client.extensions.ParameterSetter;
+import com.ibatis.sqlmap.client.extensions.ResultGetter;
+import com.ibatis.sqlmap.client.extensions.TypeHandlerCallback;
+
+/**
+ * @author sarah.lij 2012-7-25 下午05:12:52
+ */
+public class CanalParameterTypeHandler implements TypeHandlerCallback {
+
+ @Override
+ public void setParameter(ParameterSetter setter, Object parameter) throws SQLException {
+ setter.setString(JsonUtils.marshalToString(parameter));
+ }
+
+ @Override
+ public Object getResult(ResultGetter getter) throws SQLException {
+ return JsonUtils.unmarshalFromString(getter.getString(), CanalParameter.class);
+ }
+
+ public Object valueOf(String s) {
+ return JsonUtils.unmarshalFromString(s, CanalParameter.class);
+ }
+}
diff --git a/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/canal/dal/ibatis/IbatisCanalDAO.java b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/canal/dal/ibatis/IbatisCanalDAO.java
new file mode 100644
index 00000000..a1385f63
--- /dev/null
+++ b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/canal/dal/ibatis/IbatisCanalDAO.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2010-2101 Alibaba Group Holding Limited.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.otter.manager.biz.config.canal.dal.ibatis;
+
+import java.util.List;
+import java.util.Map;
+
+import org.springframework.orm.ibatis.support.SqlMapClientDaoSupport;
+
+import com.alibaba.otter.shared.common.utils.Assert;
+import com.alibaba.otter.manager.biz.config.canal.dal.CanalDAO;
+import com.alibaba.otter.manager.biz.config.canal.dal.dataobject.CanalDO;
+
+/**
+ * @author sarah.lij 2012-7-25 下午05:12:29
+ */
+public class IbatisCanalDAO extends SqlMapClientDaoSupport implements CanalDAO {
+
+ public CanalDO insert(CanalDO canal) {
+ Assert.assertNotNull(canal);
+ getSqlMapClientTemplate().insert("insertCanal", canal);
+ return canal;
+ }
+
+ public void delete(Long canalId) {
+ Assert.assertNotNull(canalId);
+ getSqlMapClientTemplate().delete("deleteCanalById", canalId);
+ }
+
+ public void update(CanalDO canal) {
+ Assert.assertNotNull(canal);
+ getSqlMapClientTemplate().update("updateCanal", canal);
+ }
+
+ public List listAll() {
+ return (List) getSqlMapClientTemplate().queryForList("listCanals");
+ }
+
+ public List listByMultiId(Long... identities) {
+ List canalDos = getSqlMapClientTemplate().queryForList("listCanalByIds", identities);
+ return canalDos;
+ }
+
+ public boolean checkUnique(CanalDO canal) {
+ int count = (Integer) getSqlMapClientTemplate().queryForObject("checkCanalUnique", canal);
+ return count == 0 ? true : false;
+ }
+
+ public CanalDO findByName(String name) {
+ Assert.assertNotNull(name);
+ return (CanalDO) getSqlMapClientTemplate().queryForObject("findCanalByName", name);
+ }
+
+ public CanalDO findById(Long identity) {
+ throw new UnsupportedOperationException();
+ }
+
+ public int getCount() {
+ return 0;
+ }
+
+ public int getCount(Map condition) {
+ Integer count = (Integer) getSqlMapClientTemplate().queryForObject("getCanalCount", condition);
+ return count.intValue();
+ }
+
+ public List listByCondition(Map condition) {
+ List canalDos = getSqlMapClientTemplate().queryForList("listCanals", condition);
+ return canalDos;
+ }
+}
diff --git a/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/canal/impl/CanalServiceImpl.java b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/canal/impl/CanalServiceImpl.java
new file mode 100644
index 00000000..ccf89233
--- /dev/null
+++ b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/canal/impl/CanalServiceImpl.java
@@ -0,0 +1,287 @@
+/*
+ * Copyright (C) 2010-2101 Alibaba Group Holding Limited.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.otter.manager.biz.config.canal.impl;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.lang.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.transaction.TransactionStatus;
+import org.springframework.transaction.support.TransactionCallbackWithoutResult;
+import org.springframework.transaction.support.TransactionTemplate;
+
+import com.alibaba.otter.canal.instance.manager.model.Canal;
+import com.alibaba.otter.canal.instance.manager.model.CanalParameter;
+import com.alibaba.otter.manager.biz.common.exceptions.ManagerException;
+import com.alibaba.otter.manager.biz.common.exceptions.RepeatConfigureException;
+import com.alibaba.otter.manager.biz.config.autokeeper.AutoKeeperClusterService;
+import com.alibaba.otter.manager.biz.config.canal.CanalService;
+import com.alibaba.otter.manager.biz.config.canal.dal.CanalDAO;
+import com.alibaba.otter.manager.biz.config.canal.dal.dataobject.CanalDO;
+import com.alibaba.otter.manager.biz.config.node.impl.NodeServiceImpl;
+import com.alibaba.otter.shared.arbitrate.ArbitrateViewService;
+import com.alibaba.otter.shared.common.model.autokeeper.AutoKeeperCluster;
+import com.alibaba.otter.shared.common.utils.Assert;
+import com.alibaba.otter.shared.common.utils.JsonUtils;
+
+/**
+ * @author sarah.lij 2012-7-25 下午04:04:43
+ */
+public class CanalServiceImpl implements CanalService {
+
+ private static final Logger logger = LoggerFactory.getLogger(NodeServiceImpl.class);
+
+ private CanalDAO canalDao;
+ private TransactionTemplate transactionTemplate;
+ private AutoKeeperClusterService autoKeeperClusterService;
+ private ArbitrateViewService arbitrateViewService;
+
+ /**
+ * 添加
+ */
+ public void create(final Canal canal) {
+ Assert.assertNotNull(canal);
+ transactionTemplate.execute(new TransactionCallbackWithoutResult() {
+
+ protected void doInTransactionWithoutResult(TransactionStatus status) {
+
+ try {
+ CanalDO canalDO = modelToDo(canal);
+ canalDO.setId(0L);
+ if (!canalDao.checkUnique(canalDO)) {
+ String exceptionCause = "exist the same repeat canal in the database.";
+ logger.warn("WARN ## " + exceptionCause);
+ throw new RepeatConfigureException(exceptionCause);
+ }
+ canalDao.insert(canalDO);
+ canal.setId(canalDO.getId());
+ } catch (RepeatConfigureException rce) {
+ throw rce;
+ } catch (Exception e) {
+ logger.error("ERROR ## create canal has an exception!");
+ throw new ManagerException(e);
+ }
+ }
+ });
+ }
+
+ /**
+ * 删除
+ */
+ public void remove(final Long canalId) {
+ Assert.assertNotNull(canalId);
+ transactionTemplate.execute(new TransactionCallbackWithoutResult() {
+
+ protected void doInTransactionWithoutResult(TransactionStatus status) {
+
+ try {
+ Canal canal = findById(canalId);
+ canalDao.delete(canalId);
+ arbitrateViewService.removeCanal(canal.getName()); // 删除canal节点信息
+ } catch (Exception e) {
+ logger.error("ERROR ## remove canal(" + canalId + ") has an exception!");
+ throw new ManagerException(e);
+ }
+ }
+ });
+
+ }
+
+ /**
+ * 修改
+ */
+ public void modify(final Canal canal) {
+ Assert.assertNotNull(canal);
+ transactionTemplate.execute(new TransactionCallbackWithoutResult() {
+
+ protected void doInTransactionWithoutResult(TransactionStatus status) {
+
+ try {
+ CanalDO canalDo = modelToDo(canal);
+ if (canalDao.checkUnique(canalDo)) {
+ canalDao.update(canalDo);
+ } else {
+ String exceptionCause = "exist the same repeat canal in the database.";
+ logger.warn("WARN ## " + exceptionCause);
+ throw new RepeatConfigureException(exceptionCause);
+ }
+ } catch (RepeatConfigureException rce) {
+ throw rce;
+ } catch (Exception e) {
+ logger.error("ERROR ## modify canal(" + canal.getId() + ") has an exception!");
+ throw new ManagerException(e);
+ }
+ }
+ });
+
+ }
+
+ public List listByIds(Long... identities) {
+
+ List canals = new ArrayList();
+ try {
+ List canalDos = null;
+ if (identities.length < 1) {
+ canalDos = canalDao.listAll();
+ if (canalDos.isEmpty()) {
+ logger.debug("DEBUG ## couldn't query any canal, maybe hasn't create any canal.");
+ return canals;
+ }
+ } else {
+ canalDos = canalDao.listByMultiId(identities);
+ if (canalDos.isEmpty()) {
+ String exceptionCause = "couldn't query any canal by canalIds:" + Arrays.toString(identities);
+ logger.error("ERROR ## " + exceptionCause);
+ throw new ManagerException(exceptionCause);
+ }
+ }
+ canals = doToModel(canalDos);
+ } catch (Exception e) {
+ logger.error("ERROR ## query channels has an exception!");
+ throw new ManagerException(e);
+ }
+
+ return canals;
+ }
+
+ public List listAll() {
+ return listByIds();
+ }
+
+ public Canal findById(Long canalId) {
+ Assert.assertNotNull(canalId);
+ List canals = listByIds(canalId);
+ if (canals.size() != 1) {
+ String exceptionCause = "query canalId:" + canalId + " return null.";
+ logger.error("ERROR ## " + exceptionCause);
+ throw new ManagerException(exceptionCause);
+ }
+
+ return canals.get(0);
+ }
+
+ public Canal findByName(String name) {
+ Assert.assertNotNull(name);
+ CanalDO canalDo = canalDao.findByName(name);
+ if (canalDo == null) {
+ String exceptionCause = "query name:" + name + " return null.";
+ logger.error("ERROR ## " + exceptionCause);
+ throw new ManagerException(exceptionCause);
+ }
+
+ return doToModel(canalDo);
+ }
+
+ @Override
+ public int getCount(Map condition) {
+ return canalDao.getCount(condition);
+ }
+
+ @Override
+ public List listByCondition(Map condition) {
+ List canalDos = canalDao.listByCondition(condition);
+ if (canalDos.isEmpty()) {
+ logger.debug("DEBUG ## couldn't query any canal by the condition:" + JsonUtils.marshalToString(condition));
+ return new ArrayList();
+ }
+
+ return doToModel(canalDos);
+ }
+
+ /**
+ * 用于Model对象转化为DO对象
+ *
+ * @param canal
+ * @return CanalDO
+ */
+ private CanalDO modelToDo(Canal canal) {
+ CanalDO canalDo = new CanalDO();
+ try {
+ canalDo.setId(canal.getId());
+ canalDo.setName(canal.getName());
+ canalDo.setStatus(canal.getStatus());
+ canalDo.setDescription(canal.getDesc());
+ canalDo.setParameters(canal.getCanalParameter());
+ canalDo.setGmtCreate(canal.getGmtCreate());
+ canalDo.setGmtModified(canal.getGmtModified());
+ } catch (Exception e) {
+ logger.error("ERROR ## change the canal Model to Do has an exception");
+ throw new ManagerException(e);
+ }
+ return canalDo;
+ }
+
+ /**
+ * 用于DO对象转化为Model对象
+ *
+ * @param canalDo
+ * @return Canal
+ */
+ private Canal doToModel(CanalDO canalDo) {
+ Canal canal = new Canal();
+ try {
+ canal.setId(canalDo.getId());
+ canal.setName(canalDo.getName());
+ canal.setStatus(canalDo.getStatus());
+ canal.setDesc(canalDo.getDescription());
+ CanalParameter parameter = canalDo.getParameters();
+ AutoKeeperCluster zkCluster = autoKeeperClusterService.findAutoKeeperClusterById(parameter.getZkClusterId());
+ if (zkCluster != null) {
+ parameter.setZkClusters(Arrays.asList(StringUtils.join(zkCluster.getServerList(), ',')));
+ }
+ canal.setCanalParameter(canalDo.getParameters());
+ canal.setGmtCreate(canalDo.getGmtCreate());
+ canal.setGmtModified(canalDo.getGmtModified());
+ } catch (Exception e) {
+ logger.error("ERROR ## change the canal Do to Model has an exception");
+ throw new ManagerException(e);
+ }
+
+ return canal;
+ }
+
+ private List doToModel(List canalDos) {
+ List canals = new ArrayList();
+ for (CanalDO canalDo : canalDos) {
+ canals.add(doToModel(canalDo));
+ }
+ return canals;
+ }
+
+ /* ------------------------setter / getter--------------------------- */
+
+ public void setCanalDao(CanalDAO canalDao) {
+ this.canalDao = canalDao;
+ }
+
+ public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
+ this.transactionTemplate = transactionTemplate;
+ }
+
+ public void setAutoKeeperClusterService(AutoKeeperClusterService autoKeeperClusterService) {
+ this.autoKeeperClusterService = autoKeeperClusterService;
+ }
+
+ public void setArbitrateViewService(ArbitrateViewService arbitrateViewService) {
+ this.arbitrateViewService = arbitrateViewService;
+ }
+
+}
diff --git a/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/channel/ChannelService.java b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/channel/ChannelService.java
new file mode 100644
index 00000000..8aba723f
--- /dev/null
+++ b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/channel/ChannelService.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2010-2101 Alibaba Group Holding Limited.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.otter.manager.biz.config.channel;
+
+import java.util.List;
+import java.util.Map;
+
+import com.alibaba.otter.manager.biz.common.baseservice.GenericService;
+import com.alibaba.otter.shared.common.model.config.channel.Channel;
+import com.alibaba.otter.shared.common.model.config.channel.ChannelStatus;
+
+/**
+ * @author simon
+ */
+public interface ChannelService extends GenericService {
+
+ public Channel findByPipelineId(Long pipelineId);
+
+ public Channel findByIdWithoutColumn(Long pipelineId);
+
+ public List listByPipelineIds(Long... pipelineIds);
+
+ public List listByNodeId(Long nodeId);
+
+ public List listOnlyChannels(Long... identities);
+
+ public List listAllChannelId();
+
+ public List listByNodeId(Long nodeId, ChannelStatus... status);
+
+ public List listByConditionWithoutColumn(Map condition);
+
+ public void stopChannel(Long channelId);
+
+ public void notifyChannel(Long channelId);
+
+ public void startChannel(Long channelId);
+
+}
diff --git a/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/channel/dal/ChannelDAO.java b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/channel/dal/ChannelDAO.java
new file mode 100644
index 00000000..26c81238
--- /dev/null
+++ b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/channel/dal/ChannelDAO.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2010-2101 Alibaba Group Holding Limited.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.otter.manager.biz.config.channel.dal;
+
+import java.util.List;
+
+import com.alibaba.otter.manager.biz.common.basedao.GenericDAO;
+import com.alibaba.otter.manager.biz.config.channel.dal.dataobject.ChannelDO;
+
+/**
+ * @author simon
+ */
+public interface ChannelDAO extends GenericDAO {
+
+ public List listChannelPks();
+}
diff --git a/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/channel/dal/dataobject/ChannelDO.java b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/channel/dal/dataobject/ChannelDO.java
new file mode 100644
index 00000000..b059be3f
--- /dev/null
+++ b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/channel/dal/dataobject/ChannelDO.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2010-2101 Alibaba Group Holding Limited.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.otter.manager.biz.config.channel.dal.dataobject;
+
+import java.io.Serializable;
+import java.util.Date;
+
+import com.alibaba.otter.shared.common.model.config.channel.ChannelParameter;
+import com.alibaba.otter.shared.common.model.config.channel.ChannelStatus;
+
+/**
+ * @author simon
+ */
+public class ChannelDO implements Serializable {
+
+ private static final long serialVersionUID = 3708730560311969117L;
+ private Long id; // 唯一标示id
+ private String name; // channel命名
+ private ChannelStatus status; // 运行状态
+ private String description; // 描述信息
+ private ChannelParameter parameters; // 配置参数
+ private Date gmtCreate;
+ private Date gmtModified;
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public ChannelStatus getStatus() {
+ return status;
+ }
+
+ public void setStatus(ChannelStatus status) {
+ this.status = status;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public ChannelParameter getParameters() {
+ return parameters;
+ }
+
+ public void setParameters(ChannelParameter parameters) {
+ this.parameters = parameters;
+ }
+
+ public Date getGmtCreate() {
+ return gmtCreate;
+ }
+
+ public void setGmtCreate(Date gmtCreate) {
+ this.gmtCreate = gmtCreate;
+ }
+
+ public Date getGmtModified() {
+ return gmtModified;
+ }
+
+ public void setGmtModified(Date gmtModified) {
+ this.gmtModified = gmtModified;
+ }
+
+}
diff --git a/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/channel/dal/ibatis/ChannelParameterTypeHandler.java b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/channel/dal/ibatis/ChannelParameterTypeHandler.java
new file mode 100644
index 00000000..b8607610
--- /dev/null
+++ b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/channel/dal/ibatis/ChannelParameterTypeHandler.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2010-2101 Alibaba Group Holding Limited.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.otter.manager.biz.config.channel.dal.ibatis;
+
+import java.sql.SQLException;
+
+import com.alibaba.otter.shared.common.model.config.channel.ChannelParameter;
+import com.alibaba.otter.shared.common.utils.JsonUtils;
+import com.ibatis.sqlmap.client.extensions.ParameterSetter;
+import com.ibatis.sqlmap.client.extensions.ResultGetter;
+import com.ibatis.sqlmap.client.extensions.TypeHandlerCallback;
+
+/**
+ * 用于ChannelParameter的解析
+ *
+ * @author simon
+ */
+public class ChannelParameterTypeHandler implements TypeHandlerCallback {
+
+ @Override
+ public void setParameter(ParameterSetter setter, Object parameter) throws SQLException {
+ setter.setString(JsonUtils.marshalToString(parameter));
+ }
+
+ @Override
+ public Object getResult(ResultGetter getter) throws SQLException {
+ return JsonUtils.unmarshalFromString(getter.getString(), ChannelParameter.class);
+ }
+
+ public Object valueOf(String s) {
+ return JsonUtils.unmarshalFromString(s, ChannelParameter.class);
+ }
+
+}
diff --git a/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/channel/dal/ibatis/IbatisChannelDAO.java b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/channel/dal/ibatis/IbatisChannelDAO.java
new file mode 100644
index 00000000..d1bfa284
--- /dev/null
+++ b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/channel/dal/ibatis/IbatisChannelDAO.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2010-2101 Alibaba Group Holding Limited.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.otter.manager.biz.config.channel.dal.ibatis;
+
+import java.util.List;
+import java.util.Map;
+
+import org.springframework.orm.ibatis.support.SqlMapClientDaoSupport;
+
+import com.alibaba.otter.shared.common.utils.Assert;
+import com.alibaba.otter.manager.biz.config.channel.dal.ChannelDAO;
+import com.alibaba.otter.manager.biz.config.channel.dal.dataobject.ChannelDO;
+
+/**
+ * Channel的DAO层,ibatis的实现,主要是CRUD操作。
+ *
+ * @author simon
+ */
+public class IbatisChannelDAO extends SqlMapClientDaoSupport implements ChannelDAO {
+
+ public ChannelDO insert(ChannelDO entityObj) {
+ Assert.assertNotNull(entityObj);
+ getSqlMapClientTemplate().insert("insertChannel", entityObj);
+ return entityObj;
+ }
+
+ public void delete(Long identity) {
+ Assert.assertNotNull(identity);
+ getSqlMapClientTemplate().delete("deleteChannelById", identity);
+ }
+
+ public void update(ChannelDO entityObj) {
+ Assert.assertNotNull(entityObj);
+ getSqlMapClientTemplate().update("updateChannel", entityObj);
+ }
+
+ public boolean checkUnique(ChannelDO entityObj) {
+ Assert.assertNotNull(entityObj);
+ int count = (Integer) getSqlMapClientTemplate().queryForObject("checkChannelUnique", entityObj);
+ return count == 0 ? true : false;
+ }
+
+ public List listAll() {
+ List channels = getSqlMapClientTemplate().queryForList("listChannels");
+ return channels;
+ }
+
+ public List listChannelPks() {
+ List channels = getSqlMapClientTemplate().queryForList("listChannelPks");
+ return channels;
+ }
+
+ public List listByCondition(Map condition) {
+
+ List channelDos = getSqlMapClientTemplate().queryForList("listChannels", condition);
+ return channelDos;
+ }
+
+ public int getCount() {
+ Integer count = (Integer) getSqlMapClientTemplate().queryForObject("getChannelCount");
+ return count.intValue();
+ }
+
+ public int getCount(Map condition) {
+ Integer count = (Integer) getSqlMapClientTemplate().queryForObject("getChannelCount", condition);
+ return count.intValue();
+ }
+
+ public List listByMultiId(Long... identities) {
+ List channelDos = getSqlMapClientTemplate().queryForList("listChannelByIds", identities);
+ return channelDos;
+ }
+
+ public ChannelDO findById(Long identity) {
+ Assert.assertNotNull(identity);
+ return (ChannelDO) getSqlMapClientTemplate().queryForObject("findChannelById", identity);
+ }
+
+}
diff --git a/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/channel/impl/ChannelServiceImpl.java b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/channel/impl/ChannelServiceImpl.java
new file mode 100644
index 00000000..91ff83f1
--- /dev/null
+++ b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/channel/impl/ChannelServiceImpl.java
@@ -0,0 +1,712 @@
+/*
+ * Copyright (C) 2010-2101 Alibaba Group Holding Limited.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.otter.manager.biz.config.channel.impl;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.transaction.TransactionStatus;
+import org.springframework.transaction.support.TransactionCallbackWithoutResult;
+import org.springframework.transaction.support.TransactionTemplate;
+
+import com.alibaba.otter.manager.biz.common.exceptions.InvalidConfigureException;
+import com.alibaba.otter.manager.biz.common.exceptions.InvalidConfigureException.INVALID_TYPE;
+import com.alibaba.otter.manager.biz.common.exceptions.ManagerException;
+import com.alibaba.otter.manager.biz.common.exceptions.RepeatConfigureException;
+import com.alibaba.otter.manager.biz.config.channel.ChannelService;
+import com.alibaba.otter.manager.biz.config.channel.dal.ChannelDAO;
+import com.alibaba.otter.manager.biz.config.channel.dal.dataobject.ChannelDO;
+import com.alibaba.otter.manager.biz.config.parameter.SystemParameterService;
+import com.alibaba.otter.manager.biz.config.pipeline.PipelineService;
+import com.alibaba.otter.manager.biz.remote.ConfigRemoteService;
+import com.alibaba.otter.shared.arbitrate.ArbitrateManageService;
+import com.alibaba.otter.shared.common.model.config.channel.Channel;
+import com.alibaba.otter.shared.common.model.config.channel.ChannelStatus;
+import com.alibaba.otter.shared.common.model.config.parameter.SystemParameter;
+import com.alibaba.otter.shared.common.model.config.pipeline.Pipeline;
+import com.alibaba.otter.shared.common.model.config.pipeline.PipelineParameter;
+import com.alibaba.otter.shared.common.utils.Assert;
+import com.alibaba.otter.shared.common.utils.JsonUtils;
+
+/**
+ * 主要提供增加、删除、修改、列表功能; 提供开启和停止channel方法,需要调用仲裁器方法
+ *
+ * @author simon
+ */
+public class ChannelServiceImpl implements ChannelService {
+
+ private static final Logger logger = LoggerFactory.getLogger(ChannelServiceImpl.class);
+
+ private SystemParameterService systemParameterService;
+ private ArbitrateManageService arbitrateManageService;
+ private TransactionTemplate transactionTemplate;
+ private ConfigRemoteService configRemoteService;
+ private PipelineService pipelineService;
+ private ChannelDAO channelDao;
+
+ /**
+ * 添加Channel
+ */
+ public void create(final Channel channel) {
+ Assert.assertNotNull(channel);
+
+ transactionTemplate.execute(new TransactionCallbackWithoutResult() {
+
+ @Override
+ protected void doInTransactionWithoutResult(TransactionStatus status) {
+ try {
+
+ ChannelDO channelDo = modelToDo(channel);
+ channelDo.setId(0L);
+
+ if (!channelDao.checkUnique(channelDo)) {
+ String exceptionCause = "exist the same name channel in the database.";
+ logger.warn("WARN ## " + exceptionCause);
+ throw new RepeatConfigureException(exceptionCause);
+ }
+ channelDao.insert(channelDo);
+ arbitrateManageService.channelEvent().init(channelDo.getId());
+
+ } catch (RepeatConfigureException rce) {
+ throw rce;
+ } catch (Exception e) {
+ logger.error("ERROR ## create channel has an exception ", e);
+ throw new ManagerException(e);
+ }
+ }
+ });
+ }
+
+ /**
+ * 修改Channel
+ */
+ public void modify(Channel channel) {
+
+ Assert.assertNotNull(channel);
+
+ try {
+ ChannelDO channelDo = modelToDo(channel);
+ if (channelDao.checkUnique(channelDo)) {
+ channelDao.update(channelDo);
+ } else {
+ String exceptionCause = "exist the same name channel in the database.";
+ logger.warn("WARN ## " + exceptionCause);
+ throw new RepeatConfigureException(exceptionCause);
+ }
+
+ } catch (RepeatConfigureException rce) {
+ throw rce;
+ } catch (Exception e) {
+ logger.error("ERROR ## modify channel has an exception ", e);
+ throw new ManagerException(e);
+ }
+
+ }
+
+ /**
+ * 删除Channel
+ */
+ public void remove(final Long channelId) {
+ Assert.assertNotNull(channelId);
+
+ transactionTemplate.execute(new TransactionCallbackWithoutResult() {
+
+ @Override
+ protected void doInTransactionWithoutResult(TransactionStatus status) {
+ try {
+ arbitrateManageService.channelEvent().destory(channelId);
+ channelDao.delete(channelId);
+ } catch (Exception e) {
+ logger.error("ERROR ## remove channel has an exception ", e);
+ throw new ManagerException(e);
+ }
+ }
+ });
+
+ }
+
+ /*--------------------优化内容:listAll、listByIds、findById合并-------------------------------*/
+
+ public List listByIds(Long... identities) {
+
+ List channels = new ArrayList();
+ try {
+ List channelDos = null;
+ if (identities.length < 1) {
+ channelDos = channelDao.listAll();
+ if (channelDos.isEmpty()) {
+ logger.debug("DEBUG ## couldn't query any channel, maybe hasn't create any channel.");
+ return channels;
+ }
+ } else {
+ channelDos = channelDao.listByMultiId(identities);
+ if (channelDos.isEmpty()) {
+ String exceptionCause = "couldn't query any channel by channelIds:" + Arrays.toString(identities);
+ logger.error("ERROR ## " + exceptionCause);
+ throw new ManagerException(exceptionCause);
+ }
+ }
+ channels = doToModel(channelDos);
+ } catch (Exception e) {
+ logger.error("ERROR ## query channels has an exception!");
+ throw new ManagerException(e);
+ }
+
+ return channels;
+ }
+
+ /**
+ * 列出所有的Channel对象
+ */
+ public List listAll() {
+ return listByIds();
+ }
+
+ public List listOnlyChannels(Long... identities) {
+
+ List channels = new ArrayList();
+ try {
+ List channelDos = null;
+ if (identities.length < 1) {
+ channelDos = channelDao.listAll();
+ if (channelDos.isEmpty()) {
+ logger.debug("DEBUG ## couldn't query any channel, maybe hasn't create any channel.");
+ return channels;
+ }
+ } else {
+ channelDos = channelDao.listByMultiId(identities);
+ if (channelDos.isEmpty()) {
+ String exceptionCause = "couldn't query any channel by channelIds:" + Arrays.toString(identities);
+ logger.error("ERROR ## " + exceptionCause);
+ throw new ManagerException(exceptionCause);
+ }
+ }
+ channels = doToModelOnlyChannels(channelDos);
+ } catch (Exception e) {
+ logger.error("ERROR ## query channels has an exception!");
+ throw new ManagerException(e);
+ }
+
+ return channels;
+ }
+
+ public List listByCondition(Map condition) {
+ List channelDos = channelDao.listByCondition(condition);
+ if (channelDos.isEmpty()) {
+ logger.debug("DEBUG ## couldn't query any channel by the condition:" + JsonUtils.marshalToString(condition));
+ return new ArrayList();
+ }
+ return doToModel(channelDos);
+ }
+
+ public List listByConditionWithoutColumn(Map condition) {
+ List channelDos = channelDao.listByCondition(condition);
+ if (channelDos.isEmpty()) {
+ logger.debug("DEBUG ## couldn't query any channel by the condition:" + JsonUtils.marshalToString(condition));
+ return new ArrayList();
+ }
+ return doToModelWithColumn(channelDos);
+ }
+
+ public List listAllChannelId() {
+ List channelDos = channelDao.listChannelPks();
+ List channelPks = new ArrayList();
+ if (channelDos.isEmpty()) {
+ logger.debug("DEBUG ## couldn't query any channel");
+ }
+ for (ChannelDO channelDo : channelDos) {
+ channelPks.add(channelDo.getId());
+ }
+ return channelPks;
+ }
+
+ /**
+ *
+ * 通过ChannelId找到对应的Channel对象
+ * 并且根据ChannelId找到对应的所有Pipeline。
+ *
+ */
+ public Channel findById(Long channelId) {
+ Assert.assertNotNull(channelId);
+ List channels = listByIds(channelId);
+ if (channels.size() != 1) {
+ String exceptionCause = "query channelId:" + channelId + " return null.";
+ logger.error("ERROR ## " + exceptionCause);
+ throw new ManagerException(exceptionCause);
+ }
+ return channels.get(0);
+ }
+
+ public Channel findByIdWithoutColumn(Long channelId) {
+ List channelDos = channelDao.listByMultiId(channelId);
+ if (channelDos.size() != 1) {
+ String exceptionCause = "query channelId:" + channelId + " return null.";
+ logger.error("ERROR ## " + exceptionCause);
+ throw new ManagerException(exceptionCause);
+ }
+
+ List channels = doToModelWithColumn(channelDos);
+ return channels.get(0);
+ }
+
+ /*--------------------外部关联查询Channel-----------------------*/
+
+ /**
+ *
+ * 根据PipelineID找到对应的Channel
+ * 优化设想:
+ * 应该通过变长参数达到后期扩展的方便性
+ *
+ */
+ public Channel findByPipelineId(Long pipelineId) {
+ Pipeline pipeline = pipelineService.findById(pipelineId);
+ Channel channel = findById(pipeline.getChannelId());
+ return channel;
+ }
+
+ /**
+ *
+ * 根据PipelineID找到对应的Channel
+ * 优化设想:
+ * 应该通过变长参数达到后期扩展的方便性
+ *
+ */
+ public List listByPipelineIds(Long... pipelineIds) {
+ List channels = new ArrayList();
+ try {
+ List pipelines = pipelineService.listByIds(pipelineIds);
+
+ List channelIds = new ArrayList();
+
+ for (Pipeline pipeline : pipelines) {
+ if (!channelIds.contains(pipeline.getChannelId())) {
+ channelIds.add(pipeline.getChannelId());
+ }
+ }
+ channels = listByIds(channelIds.toArray(new Long[channelIds.size()]));
+ } catch (Exception e) {
+ logger.error("ERROR ## list query channel by pipelineIds:" + pipelineIds.toString() + " has an exception!");
+ throw new ManagerException(e);
+ }
+ return channels;
+ }
+
+ /**
+ * pipelineService 根据NodeId找到对应已启动的Channel列表。
+ */
+ public List listByNodeId(Long nodeId) {
+ return listByNodeId(nodeId, new ChannelStatus[] {});
+ }
+
+ /**
+ * 根据NodeId和Channel状态找到对应的Channel列表。
+ */
+ public List listByNodeId(Long nodeId, ChannelStatus... statuses) {
+ List channels = new ArrayList();
+ List results = new ArrayList();
+ try {
+ List pipelines = pipelineService.listByNodeId(nodeId);
+ List pipelineIds = new ArrayList();
+ for (Pipeline pipeline : pipelines) {
+ pipelineIds.add(pipeline.getId());
+ }
+
+ if (pipelineIds.isEmpty()) { // 没有关联任务直接返回
+ return channels;
+ }
+
+ // 反查对应的channel
+ channels = listByPipelineIds(pipelineIds.toArray(new Long[pipelineIds.size()]));
+ if (null == statuses || statuses.length == 0) {
+ return channels;
+ }
+
+ for (Channel channel : channels) {
+ for (ChannelStatus status : statuses) {
+ if (channel.getStatus().equals(status)) {
+ results.add(channel);
+ }
+ }
+ }
+ } catch (Exception e) {
+ logger.error("ERROR ## list query channel by nodeId:" + nodeId + " has an exception!");
+ throw new ManagerException(e);
+ }
+ return results;
+ }
+
+ /**
+ * 拿到channel总数进行分页
+ */
+ public int getCount() {
+ return channelDao.getCount();
+ }
+
+ public int getCount(Map condition) {
+ return channelDao.getCount(condition);
+ }
+
+ /*----------------------Start/Stop Channel 短期优化:增加异常和条件判断--------------------------*/
+ /**
+ *
+ * 切换Channel状态
+ * 1.首先判断Channel是否为空或状态位是否正确
+ * 2.通知总裁器,更新节点
+ * 3.数据库数据库更新状态
+ * 4.调用远程方法,推送Channel到node节点
+ *
+ */
+ private void switchChannelStatus(final Long channelId, final ChannelStatus channelStatus) {
+ transactionTemplate.execute(new TransactionCallbackWithoutResult() {
+
+ @Override
+ protected void doInTransactionWithoutResult(TransactionStatus status) {
+ try {
+ final ChannelDO channelDo = channelDao.findById(channelId);
+
+ if (null == channelDo) {
+ String exceptionCause = "query channelId:" + channelId + " return null.";
+ logger.error("ERROR ## " + exceptionCause);
+ throw new ManagerException(exceptionCause);
+ }
+
+ ChannelStatus oldStatus = arbitrateManageService.channelEvent().status(channelDo.getId());
+ Channel channel = doToModel(channelDo);
+ // 检查下ddl/home配置
+ List pipelines = channel.getPipelines();
+ if (pipelines.size() > 1) {
+ boolean ddlSync = true;
+ boolean homeSync = true;
+ for (Pipeline pipeline : pipelines) {
+ homeSync &= pipeline.getParameters().isHome();
+ ddlSync &= pipeline.getParameters().getDdlSync();
+ }
+
+ if (ddlSync) {
+ throw new InvalidConfigureException(INVALID_TYPE.DDL);
+ }
+
+ if (homeSync) {
+ throw new InvalidConfigureException(INVALID_TYPE.HOME);
+ }
+ }
+
+ channel.setStatus(oldStatus);
+ ChannelStatus newStatus = channelStatus;
+ if (newStatus != null) {
+ if (newStatus.equals(oldStatus)) {
+ // String exceptionCause = "switch the channel(" +
+ // channelId + ") status to " +
+ // channelStatus
+ // + " but it had the status:" + oldStatus;
+ // logger.error("ERROR ## " + exceptionCause);
+ // throw new ManagerException(exceptionCause);
+ // ignored
+ return;
+ } else {
+ channel.setStatus(newStatus);// 强制修改为当前变更状态
+ }
+ } else {
+ newStatus = oldStatus;
+ }
+
+ // 针对关闭操作,要优先更改对应的status,避免node工作线程继续往下跑
+ if (newStatus.isStop()) {
+ arbitrateManageService.channelEvent().stop(channelId);
+ } else if (newStatus.isPause()) {
+ arbitrateManageService.channelEvent().pause(channelId);
+ }
+
+ // 通知变更内容
+ boolean result = configRemoteService.notifyChannel(channel);// 客户端响应成功,才更改对应的状态
+
+ if (result) {
+ // 针对启动的话,需要先通知到客户端,客户端启动线程后,再更改channel状态
+ if (newStatus.isStart()) {
+ arbitrateManageService.channelEvent().start(channelId);
+ }
+ }
+
+ } catch (Exception e) {
+ logger.error("ERROR ## switch the channel(" + channelId + ") status has an exception.");
+ throw new ManagerException(e);
+ }
+ }
+ });
+
+ }
+
+ public void stopChannel(Long channelId) {
+ switchChannelStatus(channelId, ChannelStatus.STOP);
+ }
+
+ public void startChannel(Long channelId) {
+ switchChannelStatus(channelId, ChannelStatus.START);
+ }
+
+ public void notifyChannel(Long channelId) {
+ switchChannelStatus(channelId, null);
+ }
+
+ /*----------------------DO <-> MODEL 组装方法--------------------------*/
+ /**
+ *
+ * 用于Model对象转化为DO对象
+ * 优化:
+ * 无SQL交互,只是简单进行字段组装,暂时无须优化
+ *
+ *
+ * @param channel
+ * @return ChannelDO
+ */
+ private ChannelDO modelToDo(Channel channel) {
+
+ ChannelDO channelDO = new ChannelDO();
+ try {
+ channelDO.setId(channel.getId());
+ channelDO.setName(channel.getName());
+ channelDO.setDescription(channel.getDescription());
+ channelDO.setStatus(channel.getStatus());
+ channelDO.setParameters(channel.getParameters());
+ channelDO.setGmtCreate(channel.getGmtCreate());
+ channelDO.setGmtModified(channel.getGmtModified());
+ } catch (Exception e) {
+ logger.error("ERROR ## change the channel Model to Do has an exception");
+ throw new ManagerException(e);
+ }
+ return channelDO;
+ }
+
+ /**
+ *
+ * 用于DO对象转化为Model对象
+ * 现阶段优化:
+ * 需要五次SQL交互:pipeline\node\dataMediaPair\dataMedia\dataMediaSource(五个层面)
+ * 目前优化方案为单层只执行一次SQL,避免重复循环造成IO及数据库查询开销
+ * 长期优化:
+ * 对SQL进行改造,尽量减小SQL调用次数
+ *
+ *
+ * @param channelDO
+ * @return Channel
+ */
+
+ private Channel doToModel(ChannelDO channelDo) {
+ Channel channel = new Channel();
+ try {
+ channel.setId(channelDo.getId());
+ channel.setName(channelDo.getName());
+ channel.setDescription(channelDo.getDescription());
+ channel.setStatus(arbitrateManageService.channelEvent().status(channelDo.getId()));
+ channel.setParameters(channelDo.getParameters());
+ channel.setGmtCreate(channelDo.getGmtCreate());
+ channel.setGmtModified(channelDo.getGmtModified());
+ List pipelines = pipelineService.listByChannelIds(channelDo.getId());
+ // 合并PipelineParameter和ChannelParameter
+ SystemParameter systemParameter = systemParameterService.find();
+ for (Pipeline pipeline : pipelines) {
+ PipelineParameter parameter = new PipelineParameter();
+ parameter.merge(systemParameter);
+ parameter.merge(channel.getParameters());
+ // 最后复制pipelineId参数
+ parameter.merge(pipeline.getParameters());
+ pipeline.setParameters(parameter);
+ // pipeline.getParameters().merge(channel.getParameters());
+ }
+ channel.setPipelines(pipelines);
+ } catch (Exception e) {
+ logger.error("ERROR ## change the channel DO to Model has an exception");
+ throw new ManagerException(e);
+ }
+
+ return channel;
+ }
+
+ /**
+ *
+ * 用于DO对象数组转化为Model对象数组
+ * 现阶段优化:
+ * 需要五次SQL交互:pipeline\node\dataMediaPair\dataMedia\dataMediaSource(五个层面)
+ * 目前优化方案为单层只执行一次SQL,避免重复循环造成IO及数据库查询开销
+ * 长期优化:
+ * 对SQL进行改造,尽量减小SQL调用次数
+ *
+ *
+ * @param channelDO
+ * @return Channel
+ */
+ private List doToModel(List channelDos) {
+ List channels = new ArrayList();
+ try {
+ // 1.将ChannelID单独拿出来
+ List channelIds = new ArrayList();
+ for (ChannelDO channelDo : channelDos) {
+ channelIds.add(channelDo.getId());
+ }
+ Long[] idArray = new Long[channelIds.size()];
+
+ // 拿到所有的Pipeline进行ChannelID过滤,避免重复查询。
+ List pipelines = pipelineService.listByChannelIds(channelIds.toArray(idArray));
+ SystemParameter systemParameter = systemParameterService.find();
+ for (ChannelDO channelDo : channelDos) {
+ Channel channel = new Channel();
+ channel.setId(channelDo.getId());
+ channel.setName(channelDo.getName());
+ channel.setDescription(channelDo.getDescription());
+ ChannelStatus channelStatus = arbitrateManageService.channelEvent().status(channelDo.getId());
+ channel.setStatus(null == channelStatus ? ChannelStatus.STOP : channelStatus);
+ channel.setParameters(channelDo.getParameters());
+ channel.setGmtCreate(channelDo.getGmtCreate());
+ channel.setGmtModified(channelDo.getGmtModified());
+ // 遍历,将该Channel节点下的Pipeline提取出来。
+ List subPipelines = new ArrayList();
+ for (Pipeline pipeline : pipelines) {
+ if (pipeline.getChannelId().equals(channelDo.getId())) {
+ // 合并PipelineParameter和ChannelParameter
+ PipelineParameter parameter = new PipelineParameter();
+ parameter.merge(systemParameter);
+ parameter.merge(channel.getParameters());
+ // 最后复制pipelineId参数
+ parameter.merge(pipeline.getParameters());
+ pipeline.setParameters(parameter);
+ subPipelines.add(pipeline);
+ }
+ }
+
+ channel.setPipelines(subPipelines);
+ channels.add(channel);
+ }
+ } catch (Exception e) {
+ logger.error("ERROR ## change the channels DO to Model has an exception");
+ throw new ManagerException(e);
+ }
+
+ return channels;
+ }
+
+ private List doToModelWithColumn(List channelDos) {
+ List channels = new ArrayList();
+ try {
+ // 1.将ChannelID单独拿出来
+ List channelIds = new ArrayList();
+ for (ChannelDO channelDo : channelDos) {
+ channelIds.add(channelDo.getId());
+ }
+ Long[] idArray = new Long[channelIds.size()];
+
+ // 拿到所有的Pipeline进行ChannelID过滤,避免重复查询。
+ List pipelines = pipelineService.listByChannelIdsWithoutColumn(channelIds.toArray(idArray));
+ SystemParameter systemParameter = systemParameterService.find();
+ for (ChannelDO channelDo : channelDos) {
+ Channel channel = new Channel();
+ channel.setId(channelDo.getId());
+ channel.setName(channelDo.getName());
+ channel.setDescription(channelDo.getDescription());
+ ChannelStatus channelStatus = arbitrateManageService.channelEvent().status(channelDo.getId());
+ channel.setStatus(null == channelStatus ? ChannelStatus.STOP : channelStatus);
+ channel.setParameters(channelDo.getParameters());
+ channel.setGmtCreate(channelDo.getGmtCreate());
+ channel.setGmtModified(channelDo.getGmtModified());
+ // 遍历,将该Channel节点下的Pipeline提取出来。
+ List subPipelines = new ArrayList();
+ for (Pipeline pipeline : pipelines) {
+ if (pipeline.getChannelId().equals(channelDo.getId())) {
+ // 合并PipelineParameter和ChannelParameter
+ PipelineParameter parameter = new PipelineParameter();
+ parameter.merge(systemParameter);
+ parameter.merge(channel.getParameters());
+ // 最后复制pipelineId参数
+ parameter.merge(pipeline.getParameters());
+ pipeline.setParameters(parameter);
+ subPipelines.add(pipeline);
+ }
+ }
+
+ channel.setPipelines(subPipelines);
+ channels.add(channel);
+ }
+ } catch (Exception e) {
+ logger.error("ERROR ## change the channels DO to Model has an exception");
+ throw new ManagerException(e);
+ }
+
+ return channels;
+ }
+
+ private List doToModelOnlyChannels(List channelDos) {
+ List channels = new ArrayList();
+ try {
+ // 1.将ChannelID单独拿出来
+ List channelIds = new ArrayList();
+ for (ChannelDO channelDo : channelDos) {
+ channelIds.add(channelDo.getId());
+ }
+
+ for (ChannelDO channelDo : channelDos) {
+ Channel channel = new Channel();
+ channel.setId(channelDo.getId());
+ channel.setName(channelDo.getName());
+ channel.setDescription(channelDo.getDescription());
+ ChannelStatus channelStatus = arbitrateManageService.channelEvent().status(channelDo.getId());
+ channel.setStatus(null == channelStatus ? ChannelStatus.STOP : channelStatus);
+ channel.setParameters(channelDo.getParameters());
+ channel.setGmtCreate(channelDo.getGmtCreate());
+ channel.setGmtModified(channelDo.getGmtModified());
+ // 遍历,将该Channel节点下的Pipeline提取出来。
+ List subPipelines = new ArrayList();
+ channel.setPipelines(subPipelines);
+ channels.add(channel);
+ }
+ } catch (Exception e) {
+ logger.error("ERROR ## change the channels doToModelOnlyChannels has an exception");
+ throw new ManagerException(e);
+ }
+
+ return channels;
+ }
+
+ /* ------------------------setter / getter--------------------------- */
+
+ public void setPipelineService(PipelineService pipelineService) {
+ this.pipelineService = pipelineService;
+ }
+
+ public void setChannelDao(ChannelDAO channelDao) {
+ this.channelDao = channelDao;
+ }
+
+ public void setArbitrateManageService(ArbitrateManageService arbitrateManageService) {
+ this.arbitrateManageService = arbitrateManageService;
+ }
+
+ public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
+ this.transactionTemplate = transactionTemplate;
+ }
+
+ public void setConfigRemoteService(ConfigRemoteService configRemoteService) {
+ this.configRemoteService = configRemoteService;
+ }
+
+ public void setSystemParameterService(SystemParameterService systemParameterService) {
+ this.systemParameterService = systemParameterService;
+ }
+
+}
diff --git a/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/datacolumnpair/DataColumnPairGroupService.java b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/datacolumnpair/DataColumnPairGroupService.java
new file mode 100644
index 00000000..2be9343d
--- /dev/null
+++ b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/datacolumnpair/DataColumnPairGroupService.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2010-2101 Alibaba Group Holding Limited.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.otter.manager.biz.config.datacolumnpair;
+
+import java.util.List;
+import java.util.Map;
+
+import com.alibaba.otter.manager.biz.common.baseservice.GenericService;
+import com.alibaba.otter.shared.common.model.config.data.ColumnGroup;
+
+/**
+ * 类DataColumnPairGroupService.java的实现描述:TODO 类实现描述
+ *
+ * @author simon 2012-4-20 下午4:08:24
+ */
+public interface DataColumnPairGroupService extends GenericService {
+
+ public void removeByDataMediaPairId(Long dataMediaPairId);
+
+ public List listByDataMediaPairId(Long dataMediaPairId);
+
+ public Map> listByDataMediaPairIds(Long... dataMediaPairId);
+}
diff --git a/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/datacolumnpair/DataColumnPairService.java b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/datacolumnpair/DataColumnPairService.java
new file mode 100644
index 00000000..aa7d4971
--- /dev/null
+++ b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/datacolumnpair/DataColumnPairService.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2010-2101 Alibaba Group Holding Limited.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.otter.manager.biz.config.datacolumnpair;
+
+import java.util.List;
+import java.util.Map;
+
+import com.alibaba.otter.manager.biz.common.baseservice.GenericService;
+import com.alibaba.otter.shared.common.model.config.data.ColumnPair;
+
+/**
+ * 类DataColumnPairService.java的实现描述:TODO 类实现描述
+ *
+ * @author simon 2012-4-20 下午4:07:47
+ */
+public interface DataColumnPairService extends GenericService {
+
+ public List listByDataMediaPairId(Long dataMediaPairId);
+
+ public Map> listByDataMediaPairIds(Long... dataMediaPairIds);
+
+ public void createBatch(List dataColumnPairs);
+
+ public void removeByDataMediaPairId(Long dataMediaPairId);
+}
diff --git a/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/datacolumnpair/dal/DataColumnPairDAO.java b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/datacolumnpair/dal/DataColumnPairDAO.java
new file mode 100644
index 00000000..afe3d9b4
--- /dev/null
+++ b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/datacolumnpair/dal/DataColumnPairDAO.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2010-2101 Alibaba Group Holding Limited.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.otter.manager.biz.config.datacolumnpair.dal;
+
+import java.util.List;
+
+import com.alibaba.otter.manager.biz.common.basedao.GenericDAO;
+import com.alibaba.otter.manager.biz.config.datacolumnpair.dal.dataobject.DataColumnPairDO;
+
+/**
+ * 类DataColumnPairDAO.java的实现描述:TODO 类实现描述
+ *
+ * @author simon 2012-4-20 下午4:08:55
+ */
+public interface DataColumnPairDAO extends GenericDAO {
+
+ public List listByDataMediaPairId(Long dataMediaPairId);
+
+ public List listByDataMediaPairIds(Long... dataMediaPairIds);
+
+ public void insertBatch(List dataColumnPairDos);
+
+ public void deleteByDataMediaPairId(Long dataMediaPairId);
+}
diff --git a/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/datacolumnpair/dal/DataColumnPairGroupDAO.java b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/datacolumnpair/dal/DataColumnPairGroupDAO.java
new file mode 100644
index 00000000..0c0e6e7f
--- /dev/null
+++ b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/datacolumnpair/dal/DataColumnPairGroupDAO.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2010-2101 Alibaba Group Holding Limited.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.otter.manager.biz.config.datacolumnpair.dal;
+
+import java.util.List;
+
+import com.alibaba.otter.manager.biz.common.basedao.GenericDAO;
+import com.alibaba.otter.manager.biz.config.datacolumnpair.dal.dataobject.DataColumnPairGroupDO;
+
+/**
+ * 类DataColumnPairGroupDAO.java的实现描述:TODO 类实现描述
+ *
+ * @author simon 2012-4-20 下午4:09:09
+ */
+public interface DataColumnPairGroupDAO extends GenericDAO {
+
+ public void deleteByDataMediaPairId(Long dataMediaPairId);
+
+ public List ListByDataMediaPairId(Long dataMediaPairId);
+
+ public List ListByDataMediaPairIds(Long... dataMediaPairIds);
+}
diff --git a/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/datacolumnpair/dal/dataobject/DataColumnPairDO.java b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/datacolumnpair/dal/dataobject/DataColumnPairDO.java
new file mode 100644
index 00000000..de2b0d2d
--- /dev/null
+++ b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/datacolumnpair/dal/dataobject/DataColumnPairDO.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2010-2101 Alibaba Group Holding Limited.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.otter.manager.biz.config.datacolumnpair.dal.dataobject;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 类DataColumnPairDO.java的实现描述:TODO 类实现描述
+ *
+ * @author simon 2012-4-20 下午4:09:38
+ */
+public class DataColumnPairDO implements Serializable {
+
+ private static final long serialVersionUID = 194553152360180533L;
+ private Long id;
+ private String sourceColumnName; // 源字段
+ private String targetColumnName; // 目标字段
+ private Long dataMediaPairId;
+ private Date gmtCreate;
+ private Date gmtModified;
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getSourceColumnName() {
+ return sourceColumnName;
+ }
+
+ public void setSourceColumnName(String sourceColumnName) {
+ this.sourceColumnName = sourceColumnName;
+ }
+
+ public String getTargetColumnName() {
+ return targetColumnName;
+ }
+
+ public void setTargetColumnName(String targetColumnName) {
+ this.targetColumnName = targetColumnName;
+ }
+
+ public Long getDataMediaPairId() {
+ return dataMediaPairId;
+ }
+
+ public void setDataMediaPairId(Long dataMediaPairId) {
+ this.dataMediaPairId = dataMediaPairId;
+ }
+
+ public Date getGmtCreate() {
+ return gmtCreate;
+ }
+
+ public void setGmtCreate(Date gmtCreate) {
+ this.gmtCreate = gmtCreate;
+ }
+
+ public Date getGmtModified() {
+ return gmtModified;
+ }
+
+ public void setGmtModified(Date gmtModified) {
+ this.gmtModified = gmtModified;
+ }
+
+}
diff --git a/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/datacolumnpair/dal/dataobject/DataColumnPairGroupDO.java b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/datacolumnpair/dal/dataobject/DataColumnPairGroupDO.java
new file mode 100644
index 00000000..f0066450
--- /dev/null
+++ b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/datacolumnpair/dal/dataobject/DataColumnPairGroupDO.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2010-2101 Alibaba Group Holding Limited.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.otter.manager.biz.config.datacolumnpair.dal.dataobject;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 类DataColumnPairGroupDO.java的实现描述:TODO 类实现描述
+ *
+ * @author simon 2012-4-20 下午4:09:50
+ */
+public class DataColumnPairGroupDO implements Serializable {
+
+ private static final long serialVersionUID = 7205447225855754450L;
+ private Long id;
+ private String columnPairContent;
+ private Long dataMediaPairId;
+ private Date gmtCreate;
+ private Date gmtModified;
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getColumnPairContent() {
+ return columnPairContent;
+ }
+
+ public void setColumnPairContent(String columnPairContent) {
+ this.columnPairContent = columnPairContent;
+ }
+
+ public Long getDataMediaPairId() {
+ return dataMediaPairId;
+ }
+
+ public void setDataMediaPairId(Long dataMediaPairId) {
+ this.dataMediaPairId = dataMediaPairId;
+ }
+
+ public Date getGmtCreate() {
+ return gmtCreate;
+ }
+
+ public void setGmtCreate(Date gmtCreate) {
+ this.gmtCreate = gmtCreate;
+ }
+
+ public Date getGmtModified() {
+ return gmtModified;
+ }
+
+ public void setGmtModified(Date gmtModified) {
+ this.gmtModified = gmtModified;
+ }
+
+}
diff --git a/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/datacolumnpair/dal/ibatis/IbatisDataColumnPairDAO.java b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/datacolumnpair/dal/ibatis/IbatisDataColumnPairDAO.java
new file mode 100644
index 00000000..038e0332
--- /dev/null
+++ b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/datacolumnpair/dal/ibatis/IbatisDataColumnPairDAO.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2010-2101 Alibaba Group Holding Limited.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.otter.manager.biz.config.datacolumnpair.dal.ibatis;
+
+import java.sql.SQLException;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.springframework.orm.ibatis.support.SqlMapClientDaoSupport;
+
+import com.alibaba.otter.shared.common.utils.Assert;
+import com.alibaba.otter.manager.biz.config.datacolumnpair.dal.DataColumnPairDAO;
+import com.alibaba.otter.manager.biz.config.datacolumnpair.dal.dataobject.DataColumnPairDO;
+import com.alibaba.otter.manager.biz.config.datacolumnpair.dal.dataobject.DataColumnPairGroupDO;
+
+/**
+ * 类IbatisDataColumnPairDAO.java的实现描述:TODO 类实现描述
+ *
+ * @author simon 2012-4-20 下午4:10:48
+ */
+public class IbatisDataColumnPairDAO extends SqlMapClientDaoSupport implements DataColumnPairDAO {
+
+ public DataColumnPairGroupDO insert(DataColumnPairGroupDO dataColumnPairDo) {
+ Assert.assertNotNull(dataColumnPairDo);
+ getSqlMapClientTemplate().insert("insertDataColumnPair", dataColumnPairDo);
+ return dataColumnPairDo;
+ }
+
+ public void insertBatch(List dataColumnPairDos) {
+ try {
+ getSqlMapClientTemplate().getSqlMapClient().startBatch();
+
+ Iterator it = dataColumnPairDos.iterator();
+ while (it.hasNext()) {
+ DataColumnPairDO dataColumnPairDo = (DataColumnPairDO) it.next();
+ getSqlMapClientTemplate().getSqlMapClient().insert("insertDataColumnPair", dataColumnPairDo);
+ }
+
+ getSqlMapClientTemplate().getSqlMapClient().executeBatch();
+
+ } catch (SQLException ex1) {
+ ex1.printStackTrace();
+ }
+
+ }
+
+ public void delete(Long dataColumnPairId) {
+ Assert.assertNotNull(dataColumnPairId);
+ getSqlMapClientTemplate().delete("deleteDataColumnPairById", dataColumnPairId);
+ }
+
+ public void deleteByDataMediaPairId(Long dataMediaPairId) {
+ Assert.assertNotNull(dataMediaPairId);
+ getSqlMapClientTemplate().delete("deleteDataColumnPairByDataMediaPairId", dataMediaPairId);
+ }
+
+ public void update(DataColumnPairGroupDO dataColumnPairDo) {
+ Assert.assertNotNull(dataColumnPairDo);
+ getSqlMapClientTemplate().update("updateDataColumnPair", dataColumnPairDo);
+
+ }
+
+ public List listAll() {
+ List dataColumnPairGroupDos = getSqlMapClientTemplate().queryForList("listDataColumnPairs");
+ return dataColumnPairGroupDos;
+ }
+
+ public List listByCondition(Map condition) {
+ return null;
+ }
+
+ public List listByMultiId(Long... identities) {
+ return null;
+ }
+
+ public DataColumnPairDO findById(Long identity) {
+ Assert.assertNotNull(identity);
+ return (DataColumnPairDO) getSqlMapClientTemplate().queryForObject("findDataColumnPairById", identity);
+ }
+
+ public int getCount() {
+ return 0;
+ }
+
+ public int getCount(Map condition) {
+ return 0;
+ }
+
+ public boolean checkUnique(DataColumnPairGroupDO entityObj) {
+ return false;
+ }
+
+ public List listByDataMediaPairId(Long dataMediaPairId) {
+ List dataColumnPairDos = getSqlMapClientTemplate().queryForList("listDataColumnPairByDataMediaPairId",
+ dataMediaPairId);
+ return dataColumnPairDos;
+ }
+
+ public List listByDataMediaPairIds(Long... dataMediaPairIds) {
+ List dataColumnPairDos = getSqlMapClientTemplate().queryForList("listDataColumnPairByDataMediaPairIds",
+ dataMediaPairIds);
+ return dataColumnPairDos;
+ }
+
+ public DataColumnPairDO insert(DataColumnPairDO entityObj) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public void update(DataColumnPairDO entityObj) {
+ // TODO Auto-generated method stub
+
+ }
+
+ public boolean checkUnique(DataColumnPairDO entityObj) {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+}
diff --git a/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/datacolumnpair/dal/ibatis/IbatisDataColumnPairGroupDAO.java b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/datacolumnpair/dal/ibatis/IbatisDataColumnPairGroupDAO.java
new file mode 100644
index 00000000..f17ba0ff
--- /dev/null
+++ b/manager/biz/src/main/java/com/alibaba/otter/manager/biz/config/datacolumnpair/dal/ibatis/IbatisDataColumnPairGroupDAO.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2010-2101 Alibaba Group Holding Limited.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.alibaba.otter.manager.biz.config.datacolumnpair.dal.ibatis;
+
+import java.util.List;
+import java.util.Map;
+
+import org.springframework.orm.ibatis.support.SqlMapClientDaoSupport;
+
+import com.alibaba.otter.shared.common.utils.Assert;
+import com.alibaba.otter.manager.biz.config.datacolumnpair.dal.DataColumnPairGroupDAO;
+import com.alibaba.otter.manager.biz.config.datacolumnpair.dal.dataobject.DataColumnPairGroupDO;
+
+/**
+ * 类IbatisDataColumnPairGroupDAO.java的实现描述:TODO 类实现描述
+ *
+ * @author simon 2012-4-20 下午4:11:06
+ */
+public class IbatisDataColumnPairGroupDAO extends SqlMapClientDaoSupport implements DataColumnPairGroupDAO {
+
+ public DataColumnPairGroupDO insert(DataColumnPairGroupDO entityObj) {
+ Assert.assertNotNull(entityObj);
+ getSqlMapClientTemplate().insert("insertDataColumnPairGroup", entityObj);
+ return entityObj;
+ }
+
+ public void delete(Long identity) {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void deleteByDataMediaPairId(Long dataMediaPairId) {
+ Assert.assertNotNull(dataMediaPairId);
+ getSqlMapClientTemplate().delete("deleteDataColumnPairGroupByDataMediaPairId", dataMediaPairId);
+
+ }
+
+ public void update(DataColumnPairGroupDO entityObj) {
+ // TODO Auto-generated method stub
+
+ }
+
+ public List listAll() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public List ListByDataMediaPairId(Long dataMediaPairId) {
+ Assert.assertNotNull(dataMediaPairId);
+ List dataColumnPairGroupDos = getSqlMapClientTemplate().queryForList("listDataColumnPairGroupByDataMediaPairId",
+ dataMediaPairId);
+ return dataColumnPairGroupDos;
+ }
+
+ public List ListByDataMediaPairIds(Long... dataMediaPairIds) {
+ Assert.assertNotNull(dataMediaPairIds);
+ List