You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@dolphinscheduler.apache.org by ga...@apache.org on 2021/01/22 03:02:01 UTC

[incubator-dolphinscheduler] branch dev updated: [Feature-#3049] Alert Plugin Design(merge alert_plugin_design to dev) (#4495)

This is an automated email from the ASF dual-hosted git repository.

gaojun2048 pushed a commit to branch dev
in repository https://gitbox.apache.org/repos/asf/incubator-dolphinscheduler.git


The following commit(s) were added to refs/heads/dev by this push:
     new 0c8d08c  [Feature-#3049] Alert Plugin Design(merge alert_plugin_design to dev) (#4495)
0c8d08c is described below

commit 0c8d08cbaa0636ef93cbcb01c43a34f002bb7fdb
Author: Kirs <ac...@163.com>
AuthorDate: Fri Jan 22 11:01:46 2021 +0800

    [Feature-#3049] Alert Plugin Design(merge alert_plugin_design to dev) (#4495)
    
    * [feature-3665][ui]Add element-ui (#3666)
    
    * [feature-3665][ui]Add element-ui
    
    * add license
    
    * Add form-create plug-in and alarm group management add sample demo
    
    * Modify node version
    
    * fix
    
    * fix
    
    * [feature][ui]Alert plugin design (#3734)
    
    * [feature-3665][ui]Add element-ui (#3666)
    
    * [feature-3665][ui]Add element-ui
    
    * add license
    
    * Add form-create plug-in and alarm group management add sample demo
    
    * Modify node version
    
    * fix
    
    * fix
    
    * [Feature-3682][ui]Add form-create plug-in and alarm group management add sample demo (#3683)
    
    * Add form-create plug-in and alarm group management add sample demo
    
    * Modify node version
    
    * fix
    
    * fix
    
    * [feature][ui] Add alarm instance page
    
    * [feature-3665][ui]Add element-ui (#3666)
    
    * [feature-3665][ui]Add element-ui
    
    * add license
    
    * Add form-create plug-in and alarm group management add sample demo
    
    * Modify node version
    
    * fix
    
    * fix
    
    * [Feature-3189][alert,spi,dao,plugin-api] base code of dolphinscheduler spi and alert plugin implement (#3601)
    
    * DS SPI
    
    * Add DolphinScheduler SPI , and rebuilt the code of the Alert plug-in based on SPI
    
    * Add DolphinScheduler SPI , and rebuilt the code of the Alert plug-in based on SPI
    
    * add TODO
    
    * delete
    
    * compile
    
    * spi commit
    
    * Plugin Alert
    
    * fix some bug
    
    * add todo
    
    * change web ui from alpacajs to form-create
    
    * remove module
    
    * add plugin schema
    
    * add license header
    
    * update alert and spi module version
    
    * update the alert plugin sub module version
    
    * comment the maven.local.repository param
    
    * move utils from spi to common module
    
    * add license header
    
    * add license header and delete some chinese comment
    
    * update spi packages
    
    * delete no use alert_xx.properties
    
    * update mysql.connector.version back to 5.1.34
    
    * delete no use comment in pom.xml
    
    * update email stmp password
    
    * add license
    
    * add semicolon to sql/upgrade/1.4.0_schema/mysql/dolphinscheduler_ddl.sql file
    
    * format the code style
    
    * format new clase file with checkstyle
    
    * update plugin params to Builder model
    
    * move JSONUtils to SPI because plugin can not dependency common module
    
    * move JSONUtils to SPI because plugin can not dependency common module
    
    * delete collection dependency
    
    * replace PluginParamsTransfer to spi PluginParamsTransfer
    
    * update dolphinscheduler-maven-plugin to 1.0.0
    
    * update license
    
    * update apache-rat-plugin add exclude '.iml' file
    
    * check license
    
    * ArtifactResolver only use in development and configPlugins is not empty
    
    * ArtifactResolver only use in development and configPlugins is not empty
    
    * ArtifactResolver only use in development and configPlugins is not empty
    
    * default datasource should be postgresql
    
    * add license files
    
    * add license files
    
    * postgresql port should be 5432
    
    * postgresql test
    
    * mv show_type to spi
    
    add license header to AlertConstants
    
    * check style fix
    
    * copy check style file from branch dev
    
    * alert show_type set by plugin
    
    * alert show_type set by plugin
    
    * add PluginDefineMapper to dolphinscheduler-server/src/test/java/org/apache/dolphinscheduler/server/registry/DependencyConfig.java
    
    * add Bean to TaskCallbackServiceTestConfig
    
    * add Bean to TaskCallbackServiceTestConfig
    
    * fix check style
    
    * check style fix
    
    * [feature-3665][ui]Add element-ui (#3666)
    
    * [feature-3665][ui]Add element-ui
    
    * add license
    
    * fix check style
    
    * [Feature-3682][ui]Add form-create plug-in and alarm group management add sample demo (#3683)
    
    * Add form-create plug-in and alarm group management add sample demo
    
    * Modify node version
    
    * fix
    
    * fix
    
    * check style fix
    
    * rollback test change
    
    * rollback test change
    
    * rollback dao pom change
    
    * [feature-3665][ui]Add element-ui (#3666)
    
    * [feature-3665][ui]Add element-ui
    
    * add license
    
    * Add form-create plug-in and alarm group management add sample demo
    
    * Modify node version
    
    * fix
    
    * fix
    
    * add ut to pom.xml
    
    * add upgrade schema to global schema
    
    * fix ut failed
    
    * fix ut failed
    
    * fix ut failed
    
    * fix ut failed
    
    * add test EmailAlertPluginTest to pom.xml
    
    * fix ut failed
    
    * fix ut failed
    
    * fix check style
    
    * update license header to presto license header
    
    * presto license header not check
    
    * fix ut coverage
    
    * fix ut coverage
    
    * fix ut
    
    * fix ut
    
    * fix ut
    
    * fix ut coverage
    
    * fix ut coverage
    
    * fix ut coverage
    
    * fix ut coverage
    
    * fix ut coverage
    
    * fix ut coverage
    
    Co-authored-by: break60 <79...@qq.com>
    
    * Replace the tenant management page ui with element-ui (#3891)
    
    * [feature-3665][ui]Add element-ui (#3666)
    
    * [feature-3665][ui]Add element-ui
    
    * add license
    
    * Add form-create plug-in and alarm group management add sample demo
    
    * Modify node version
    
    * fix
    
    * fix
    
    * [Feature-3682][ui]Add form-create plug-in and alarm group management add sample demo (#3683)
    
    * Add form-create plug-in and alarm group management add sample demo
    
    * Modify node version
    
    * fix
    
    * fix
    
    * [feature][ui] Add alarm instance page
    
    * [feature-3665][ui]Add element-ui (#3666)
    
    * [feature-3665][ui]Add element-ui
    
    * add license
    
    * Add form-create plug-in and alarm group management add sample demo
    
    * Modify node version
    
    * fix
    
    * fix
    
    * [feature-3665][ui]Add element-ui (#3666)
    
    * [feature-3665][ui]Add element-ui
    
    * add license
    
    * Add form-create plug-in and alarm group management add sample demo
    
    * Modify node version
    
    * fix
    
    * fix
    
    * [feature][ui]Alert plugin design (#3734)
    
    * [feature-3665][ui]Add element-ui (#3666)
    
    * [feature-3665][ui]Add element-ui
    
    * add license
    
    * Add form-create plug-in and alarm group management add sample demo
    
    * Modify node version
    
    * fix
    
    * fix
    
    * [Feature-3682][ui]Add form-create plug-in and alarm group management add sample demo (#3683)
    
    * Add form-create plug-in and alarm group management add sample demo
    
    * Modify node version
    
    * fix
    
    * fix
    
    * [feature][ui] Add alarm instance page
    
    * [feature-3665][ui]Add element-ui (#3666)
    
    * [feature-3665][ui]Add element-ui
    
    * add license
    
    * Add form-create plug-in and alarm group management add sample demo
    
    * Modify node version
    
    * fix
    
    * fix
    
    * [Feature-3189][alert,spi,dao,plugin-api] base code of dolphinscheduler spi and alert plugin implement (#3601)
    
    * DS SPI
    
    * Add DolphinScheduler SPI , and rebuilt the code of the Alert plug-in based on SPI
    
    * Add DolphinScheduler SPI , and rebuilt the code of the Alert plug-in based on SPI
    
    * add TODO
    
    * delete
    
    * compile
    
    * spi commit
    
    * Plugin Alert
    
    * fix some bug
    
    * add todo
    
    * change web ui from alpacajs to form-create
    
    * remove module
    
    * add plugin schema
    
    * add license header
    
    * update alert and spi module version
    
    * update the alert plugin sub module version
    
    * comment the maven.local.repository param
    
    * move utils from spi to common module
    
    * add license header
    
    * add license header and delete some chinese comment
    
    * update spi packages
    
    * delete no use alert_xx.properties
    
    * update mysql.connector.version back to 5.1.34
    
    * delete no use comment in pom.xml
    
    * update email stmp password
    
    * add license
    
    * add semicolon to sql/upgrade/1.4.0_schema/mysql/dolphinscheduler_ddl.sql file
    
    * format the code style
    
    * format new clase file with checkstyle
    
    * update plugin params to Builder model
    
    * move JSONUtils to SPI because plugin can not dependency common module
    
    * move JSONUtils to SPI because plugin can not dependency common module
    
    * delete collection dependency
    
    * replace PluginParamsTransfer to spi PluginParamsTransfer
    
    * update dolphinscheduler-maven-plugin to 1.0.0
    
    * update license
    
    * update apache-rat-plugin add exclude '.iml' file
    
    * check license
    
    * ArtifactResolver only use in development and configPlugins is not empty
    
    * ArtifactResolver only use in development and configPlugins is not empty
    
    * ArtifactResolver only use in development and configPlugins is not empty
    
    * default datasource should be postgresql
    
    * add license files
    
    * add license files
    
    * postgresql port should be 5432
    
    * postgresql test
    
    * mv show_type to spi
    
    add license header to AlertConstants
    
    * check style fix
    
    * copy check style file from branch dev
    
    * alert show_type set by plugin
    
    * alert show_type set by plugin
    
    * add PluginDefineMapper to dolphinscheduler-server/src/test/java/org/apache/dolphinscheduler/server/registry/DependencyConfig.java
    
    * add Bean to TaskCallbackServiceTestConfig
    
    * add Bean to TaskCallbackServiceTestConfig
    
    * fix check style
    
    * check style fix
    
    * [feature-3665][ui]Add element-ui (#3666)
    
    * [feature-3665][ui]Add element-ui
    
    * add license
    
    * fix check style
    
    * [Feature-3682][ui]Add form-create plug-in and alarm group management add sample demo (#3683)
    
    * Add form-create plug-in and alarm group management add sample demo
    
    * Modify node version
    
    * fix
    
    * fix
    
    * check style fix
    
    * rollback test change
    
    * rollback test change
    
    * rollback dao pom change
    
    * [feature-3665][ui]Add element-ui (#3666)
    
    * [feature-3665][ui]Add element-ui
    
    * add license
    
    * Add form-create plug-in and alarm group management add sample demo
    
    * Modify node version
    
    * fix
    
    * fix
    
    * add ut to pom.xml
    
    * add upgrade schema to global schema
    
    * fix ut failed
    
    * fix ut failed
    
    * fix ut failed
    
    * fix ut failed
    
    * add test EmailAlertPluginTest to pom.xml
    
    * fix ut failed
    
    * fix ut failed
    
    * fix check style
    
    * update license header to presto license header
    
    * presto license header not check
    
    * fix ut coverage
    
    * fix ut coverage
    
    * fix ut
    
    * fix ut
    
    * fix ut
    
    * fix ut coverage
    
    * fix ut coverage
    
    * fix ut coverage
    
    * fix ut coverage
    
    * fix ut coverage
    
    * fix ut coverage
    
    Co-authored-by: break60 <79...@qq.com>
    
    * The tenant management module ui is replaced with element-ui
    
    * fix
    
    * fix
    
    * fix
    
    * fix
    
    * fix
    
    * modify
    
    * fix
    
    Co-authored-by: gaojun2048 <32...@users.noreply.github.com>
    
    * the alert module support service.
    
    * the alert module support service.
    
    * Delete blank lines.
    
    * add code checkstyle.
    
    * Solve the sonar bug.
    
    * Re-execute build.
    
    * add test.
    
    * update ut.
    
    * add test class.
    
    * update test.
    
    * [feature#3356] alert-spi support DingTalk&WeChat (#3869)
    
    * [feature-3665][ui]Add element-ui (#3666)
    
    * [feature-3665][ui]Add element-ui
    
    * add license
    
    * Add form-create plug-in and alarm group management add sample demo
    
    * Modify node version
    
    * fix
    
    * fix
    
    * [feature][ui]Alert plugin design (#3734)
    
    * [feature-3665][ui]Add element-ui (#3666)
    
    * [feature-3665][ui]Add element-ui
    
    * add license
    
    * Add form-create plug-in and alarm group management add sample demo
    
    * Modify node version
    
    * fix
    
    * fix
    
    * [Feature-3682][ui]Add form-create plug-in and alarm group management add sample demo (#3683)
    
    * Add form-create plug-in and alarm group management add sample demo
    
    * Modify node version
    
    * fix
    
    * fix
    
    * [feature][ui] Add alarm instance page
    
    * [feature-3665][ui]Add element-ui (#3666)
    
    * [feature-3665][ui]Add element-ui
    
    * add license
    
    * Add form-create plug-in and alarm group management add sample demo
    
    * Modify node version
    
    * fix
    
    * fix
    
    * [Feature-3189][alert,spi,dao,plugin-api] base code of dolphinscheduler spi and alert plugin implement (#3601)
    
    * DS SPI
    
    * Add DolphinScheduler SPI , and rebuilt the code of the Alert plug-in based on SPI
    
    * Add DolphinScheduler SPI , and rebuilt the code of the Alert plug-in based on SPI
    
    * add TODO
    
    * delete
    
    * compile
    
    * spi commit
    
    * Plugin Alert
    
    * fix some bug
    
    * add todo
    
    * change web ui from alpacajs to form-create
    
    * remove module
    
    * add plugin schema
    
    * add license header
    
    * update alert and spi module version
    
    * update the alert plugin sub module version
    
    * comment the maven.local.repository param
    
    * move utils from spi to common module
    
    * add license header
    
    * add license header and delete some chinese comment
    
    * update spi packages
    
    * delete no use alert_xx.properties
    
    * update mysql.connector.version back to 5.1.34
    
    * delete no use comment in pom.xml
    
    * update email stmp password
    
    * add license
    
    * add semicolon to sql/upgrade/1.4.0_schema/mysql/dolphinscheduler_ddl.sql file
    
    * format the code style
    
    * format new clase file with checkstyle
    
    * update plugin params to Builder model
    
    * move JSONUtils to SPI because plugin can not dependency common module
    
    * move JSONUtils to SPI because plugin can not dependency common module
    
    * delete collection dependency
    
    * replace PluginParamsTransfer to spi PluginParamsTransfer
    
    * update dolphinscheduler-maven-plugin to 1.0.0
    
    * update license
    
    * update apache-rat-plugin add exclude '.iml' file
    
    * check license
    
    * ArtifactResolver only use in development and configPlugins is not empty
    
    * ArtifactResolver only use in development and configPlugins is not empty
    
    * ArtifactResolver only use in development and configPlugins is not empty
    
    * default datasource should be postgresql
    
    * add license files
    
    * add license files
    
    * postgresql port should be 5432
    
    * postgresql test
    
    * mv show_type to spi
    
    add license header to AlertConstants
    
    * check style fix
    
    * copy check style file from branch dev
    
    * alert show_type set by plugin
    
    * alert show_type set by plugin
    
    * add PluginDefineMapper to dolphinscheduler-server/src/test/java/org/apache/dolphinscheduler/server/registry/DependencyConfig.java
    
    * add Bean to TaskCallbackServiceTestConfig
    
    * add Bean to TaskCallbackServiceTestConfig
    
    * fix check style
    
    * check style fix
    
    * [feature-3665][ui]Add element-ui (#3666)
    
    * [feature-3665][ui]Add element-ui
    
    * add license
    
    * fix check style
    
    * [Feature-3682][ui]Add form-create plug-in and alarm group management add sample demo (#3683)
    
    * Add form-create plug-in and alarm group management add sample demo
    
    * Modify node version
    
    * fix
    
    * fix
    
    * check style fix
    
    * rollback test change
    
    * rollback test change
    
    * rollback dao pom change
    
    * [feature-3665][ui]Add element-ui (#3666)
    
    * [feature-3665][ui]Add element-ui
    
    * add license
    
    * Add form-create plug-in and alarm group management add sample demo
    
    * Modify node version
    
    * fix
    
    * fix
    
    * add ut to pom.xml
    
    * add upgrade schema to global schema
    
    * fix ut failed
    
    * fix ut failed
    
    * fix ut failed
    
    * fix ut failed
    
    * add test EmailAlertPluginTest to pom.xml
    
    * fix ut failed
    
    * fix ut failed
    
    * fix check style
    
    * update license header to presto license header
    
    * presto license header not check
    
    * fix ut coverage
    
    * fix ut coverage
    
    * fix ut
    
    * fix ut
    
    * fix ut
    
    * fix ut coverage
    
    * fix ut coverage
    
    * fix ut coverage
    
    * fix ut coverage
    
    * fix ut coverage
    
    * fix ut coverage
    
    Co-authored-by: break60 <79...@qq.com>
    
    * [feature#3356] alert-spi support DingTalk
    
    this closes # 3356
    
    * add test
    
    * code style
    
    * we chat alert
    
    * support we chat alert
    
    * support we chat alert
    
    * support we chat alert,update ding talk alert
    
    * code style
    
    * add test
    
    * code style
    
    * clean old code
    
    * clean old code
    
    * code smell
    
    * code style
    
    * add test
    
    * simple config
    
    * code style
    
    * code style
    
    * code style
    
    * delete old file
    
    * fix log content error
    
    Co-authored-by: break60 <79...@qq.com>
    Co-authored-by: gaojun2048 <32...@users.noreply.github.com>
    
    * [FEATURE#4029] alert spi support script alert
    
    this closes #4029
    
    * code style
    
    * code smell
    
    * add test
    
    * code style
    
    * code smell
    
    * code style
    
    * fix name error
    
    * init sms plugin
    
    * modify http type
    
    * delete dolphinscheduler-alert-sms plugin
    
    * dolphinscheduler plugin need add <package>dolphinscheduler-plugin</package> in the pom.xml file
    
    * delete alert http plugin (#4152)
    
    delete alert http plugin (#4152)
    This part of the code was merged by mistake, so I need to delete it
    
    * [Feature-3749][Alert-SPI] SqlTask should send notifications by alert server api (#4080)
    
    * add sqltask send sync alert server.
    
    * update alert-sms license.
    
    * update AlertServer test.
    
    * remote EmailAlertPluginTest.
    
    * update sqltask.
    
    * update test class.
    
    * [Alert plugin design] add http alert plugin (#4165)
    
    * add http alert plugin
    
    * [Feature-3311] Add alert plugin instance function (#4038)
    
    * [Feature-#3252][build]plugins need put in plugins dir when install (#4259)
    
    Update maven build, put the alert plugins in the ${install_dir}/lib/plugin/alert/${plugin_module_name} directory
    (assembly build and rpm build).
    Note: When adding a new plug-in, it must be reflected in provisio/dolphinscheduler.xml.
    
    this closes #3252
    
    * [Feature#4310][Alert-SPI] Plug-ins containing UI components provide display pages (#4311)
    
    * [Feature#4310][Alert-SPI] Plug-ins containing UI components provide display pages
    
    *Some plugins (such as alert plugin) need to provide UI interfaces to users.
    *We use from-creat to dynamically generate UI interfaces. Related parameters are mainly provided by pluginParams.
    *From-create can generate dynamic ui based on this parameter.
    
    this closes #4310
    
    * add license head
    
    * rename
    
    * add ut
    
    * add license
    
    * add query plugin detail interface
    
    * fix error
    
    * [Feature-#3828][server] Delete recipients and CCs in the process defi… (#4303)
    
    * [Feature-#3828][server] Delete recipients and CCs in the process definition and select the associated alert group instead
    
    * The specific alarm instance associated with the alert group
    * The process definition only associates the alert group, and does not care about the specific alarm instance.
    
    this closes #3828
    
    * remove test cc
    
    * remove test cc
    
    * fix test error
    
    * add sql upgrade script
    delete alert column(show_type、alert_type、re、cc)
    
    * code style
    
    * update test
    Note the statement to delete the column, and provide related instructions
    
    * delete AlertGroup field(alert_type)
    
    * fix ut error
    
    * fix ut error
    
    * fix ut error
    
    * code style
    
    * delete api-doc receiverCC
    
    * delete UserAlertGroupMapper
    
    * delete UserAlertGroup
    
    * delete UserAlertGroupApi
    
    * alert group add coulmn user_id
    
    * alter user_id to create_user_id
    
    * userinfo remove alert group info
    
    * Replace the front-end code of the plugin branch with the front-end code of the dev branch (#4353)
    
    * Replace the front-end code of the plugin branch with the front-end code of the dev branch
    
    * Merge dev to alert_plugin_design (#4351)
    
    * fixed queryProcessInstanceListPaging query slow (#3893)
    
    because queryProcessInstanceListPaging has contain longtext type field by instance.*,so queryProcessInstanceListPaging query slow , but longtext field not used.
    
    * fixed  queryTaskInstanceListPaging query slow (#3892)
    
    because queryTaskInstanceListPaging  has contain longtext  type field  by instance.*,so  queryTaskInstanceListPaging query slow , but longtext  field not used.
    
    * add verify tenant name cannot contain special characters. (#3865)
    
    * [Fix][Docker] fix startup.sh variables cannot reference in single quotes. (#3895)
    
    * [Fix][Docker] fix startup.sh variables cannot reference in single quotes.(#3894)
    
    Signed-off-by: YoungMan <bu...@gmail.com>
    
    * Update Dockerfile
    
    update the  comment
    and delete the maridb client
    
    Co-authored-by: dailidong <da...@gmail.com>
    
    * [ambari-plugin]change version 1.3.2 to 1.3.3 (#3935)
    
    * [CodeClean][DAO]Remove redundant comments (#3939)
    
    * fix bug
    
    Delete invalid field: executorcores
    
    Modify verification prompt
    
    * fix bug
    
    Delete invalid field: executorcores
    
    Modify verification prompt
    
    * fix bug
    
    Delete invalid field: executorcores
    
    Modify verification prompt
    
    * dag  add close button
    
    * reset last version
    
    * reset last version
    
    * dag add close buttion
    
    dag add close buttion
    
    * update  CLICK_SAVE_WORKFLOW_BUTTON  xpath
    
    * updae CLICK_SAVE_WORKFLOW_BUTTON xpath
    
    * updae CLICK_SAVE_WORKFLOW_BUTTON xpath
    
    * updae CLICK_SAVE_WORKFLOW_BUTTON xpath
    
    * Update CreateWorkflowLocator.java
    
    modify submit workflow button
    
    * Update CreateWorkflowLocator.java
    
    * Update CreateWorkflowLocator.java
    
    modify CLICK_ADD_BUTTON
    
    * Update CreateWorkflowLocator.java
    
    delete print
    
    * Update CreateWorkflowLocator.java
    
    1
    
    * Update CreateWorkflowLocator.java
    
    1
    
    * Setting '-XX:+DisableExplicitGC ' causes netty memory leaks
    
    in addition
    
    update '- XX: largepagesizeinbytes = 128M' to '- XX: largepagesizeinbytes = 10M'
    
    * Update dag.vue
    
    * Update dag.vue
    
    * Update dag.vue
    
    * Update CreateWorkflowLocator.java
    
    * Revert "Setting '-XX:+DisableExplicitGC ' causes netty memory leaks"
    
    This reverts commit 3a2cba7a
    
    * Setting '-XX:+DisableExplicitGC ' causes netty memory leaks
    
    in addition
    
    update '- XX: largepagesizeinbytes = 128M' to '- XX: largepagesizeinbytes = 10M'
    
    * Update dolphinscheduler-daemon.sh
    
    * Remove redundant comments
    ProcessInstanceMapper# queryProcessInstanceListPaging
    
    * Remove redundant comments
    ProcessInstanceMapper# queryProcessInstanceListPaging
    
    Co-authored-by: dailidong <da...@gmail.com>
    Co-authored-by: xingchun-chen <55...@users.noreply.github.com>
    
    * fix bug:3615 After the task is executed successfully, but the next task has not been submitted, stop the master
    
    * When running a task, the resource file is lost, which results in an error
    
    * When running a task, the resource file is lost, which results in an error
    
    * When running a task, the resource file is lost, which results in an error
    
    * [Fix][api] Add queryProjectCreatedAndAuthorizedByUser function (#3658)
    
    * fix getLoginUserCreated bug
    
    * fix getLoginUserCreated bug 1
    
    * fix checkstyle
    
    * modify function name
    
    * modify comment
    
    * fix unit test error
    
    * rollback sqoop task hive target file
    
    Co-authored-by: sunchaohe <su...@linklogis.com>
    Co-authored-by: dailidong <da...@gmail.com>
    
    * When running a task, the resource file is lost, which results in an error
    
    * When running a task, the resource file is lost, which results in an error
    
    * [fixBug-3964][ui]Switch back and forth over timeout alarm, the selected value is empty
    
    * solve too many files, close logClientService (#3971)
    
    * fix #3966 sub process doesnot send alert mail after process instance ending. (#3972)
    
    Co-authored-by: baoliang <ba...@analysys.com.cn>
    
    * [Fix-#3618][server] resolve task executed finished but not release the file handle (#3975)
    
    * [Fix-#3618][server] resolve task executed finished but not release the file handle
    
    * [Fix-#3618][server] resolve task executed finished but not release the file handle
    
    * [Fix-#3958][api] files should not be created successfully in the directory of the authorized file
    
    * [FIX-3966] The timeout warning does not take effect in sub_process (#3982)
    
    * fix #3966 sub process doesnot send alert mail after process instance ending.
    
    * fix bug 3964: sub_process The timeout warning does not take effect
    add timeout warning for sub_process/dependent task.
    
    * fix code smell
    
    * fix code smell
    
    * fix code smell
    
    * update worker group inherit from parent
    
    Co-authored-by: baoliang <ba...@analysys.com.cn>
    
    * [fix-#3962][api] Avoid ClassCastException for LoggerService.queryLog(). (#3963)
    
    * Enhance user experience, add close button to file details page
    
    * [Test-3557][API] full cover DataAnalysisServiceImpl (#3605)
    
    * generate equals and hashCode method
    
    * make up for the state of oblivion
    
    * simplify countQueueState method
    
    * delete try catch, because this never throw exception
    
    * full cover DataAnalysisServiceImpl
    
    * improve test coverage
    
    * adjust code style
    
    * simplify countCommandState method
    
    * reduce duplication
    
    * simplify countTaskDtos method
    
    * Update DataAnalysisServiceImpl.java
    
    Co-authored-by: dailidong <da...@gmail.com>
    
    * fix import dolphinscheduler_mysql.sql insert admin user data
    
    * [FIX-3929] condition task would post wrong tasks when failover. (#3999)
    
    * fix #3966 sub process doesnot send alert mail after process instance ending.
    
    * fix bug 3964: sub_process The timeout warning does not take effect
    add timeout warning for sub_process/dependent task.
    
    * fix code smell
    
    * fix code smell
    
    * fix code smell
    
    * update worker group inherit from parent
    
    * remove stdout in logback configuration
    
    * fix bug #3929 condition task would post error when failover.
    
    * remove unused test
    
    * add comments
    
    * add skip node judge
    
    Co-authored-by: baoliang <ba...@analysys.com.cn>
    
    * [FIX-3929]  because of no lock, start up failover would dispatch two same tasks. (#4004)
    
    * fix #3966 sub process doesnot send alert mail after process instance ending.
    
    * fix bug 3964: sub_process The timeout warning does not take effect
    add timeout warning for sub_process/dependent task.
    
    * fix code smell
    
    * fix code smell
    
    * fix code smell
    
    * update worker group inherit from parent
    
    * remove stdout in logback configuration
    
    * fix bug #3929 condition task would post error when failover.
    
    * remove unused test
    
    * add comments
    
    * add skip node judge
    
    * fix bug 3929: because of no lock, start up failover would dispatch two same tasks.
    
    Co-authored-by: baoliang <ba...@analysys.com.cn>
    
    * revert pom version to 1.3.3-release
    
    * [maven-release-plugin] prepare release 1.3.3
    
    * [maven-release-plugin] prepare for next development iteration
    
    * the resources API document does not match the interface parameters
    
    * [release]revert pom version to 1.3.3-release
    
    * fix bug 4010: remove failed condition tasks from error-task-list. (#4011)
    
    Co-authored-by: baoliang <ba...@analysys.com.cn>
    
    * [maven-release-plugin] prepare release 1.3.3
    
    * [maven-release-plugin] prepare for next development iteration
    
    * fix bug
    
    * reset code
    
    * Update udfs.vue
    
    * [Feature-3941][ui] Remove tenant name  (#3949)
    
    * FeatureUI-3941: Removed Tenant
    
    * FeatureUI-3941: Removed tenantName in the suggested pages
    
    * FeatureUI-3941: Updated the suggested files
    
    * FeatureUI-3941: Updated the TenantController
    
    * FeatureUI-3941: Updated the TenantController and the pom.xml
    
    * FeatureUI-3941: Updated the TenantControllerTest
    
    * [Improvement][script] daemon script usage (#3968)
    
    * Improvement daemon script usage
    
    * don't remove status command
    
    don't remove status command
    
    Co-authored-by: dailidong <da...@gmail.com>
    
    * fix #3688, Optimization of table building statements (#3689)
    
    * merge from 1.3.3-release
    
    * merge from 1.3.3-release
    
    * [Improvement-4012][common/remote] Json util code integration,remove the remote module json util (#4013)
    
    * Json util code integration,remove the remote module json util.
    
    * update code checkstyle.
    
    * update code checkstyle.
    
    * add code checkstyle.
    
    * add test class.
    
    * update JSONUtils class.
    
    * update JSONUtils class.
    
    * merge from 1.3.3-release
    
    * merge from 1.3.3-release
    
    * merge from 1.3.3-release
    
    * merge from 1.3.3-release
    
    * merge from 1.3.3-release
    
    * merge from 1.3.3-release
    
    * merge from 1.3.3-release
    
    * refactor code style
    
    * refactor code style
    
    * refactor code style
    
    * merge from 1.3.3-release
    
    * merge from 1.3.3-release
    
    * merge from 1.3.3-release
    
    * merge from 1.3.3-release
    
    * merge from 1.3.3-release
    
    * refactor ut test
    
    * refactor ut test
    
    * merge from 1.3.3-release
    
    * merge from 1.3.3-release
    
    * merge from 1.3.3-release
    
    * fix #3900 kill multi yarn app in one job
    
    * refactor ut
    
    * merge from 1.3.3-release
    
    * refactor ut
    
    * refactor ut
    
    * refactor
    
    * refactor
    
    * refactor code style
    
    * refactor code style
    
    * refactor code style
    
    * refactor code style
    
    * merge from 1.3.3-release
    
    * merge from 1.3.3-release
    
    * merge from 1.3.3-release
    
    * merge from 1.3.3-release
    
    * merge from 1.3.3-release
    
    * add ProcessUtils UT
    
    * refactor code style
    
    * refactor code style
    
    * refactor code style
    
    * refactor code style
    
    * [Draft][Merge][133-dev]133 merge dev (#4031)
    
    * [Feture-3327][ui]Add the function of re-uploading files in the resource center
    
    * [Feture-3327][ui]Add the function of re-uploading files in the resource center (#3394)
    
    * Before creating a workflow, clear the canvas
    
    * [Fix-3256][ui] herry pick commit from dev for Fix admin user info update error (#3306)
    
    * [Feture-3327][ui]Add the function of re-uploading files in the resource center
    
    Co-authored-by: wuchunfu <31...@qq.com>
    
    * [Improvement-3327][api]support re-upload the resource file (#3395)
    
    * [Fix-3390][server]Running hive sql task need find the hdfs path correctly (#3396)
    
    * [Fix-3390][api]Running hive sql task need find the hdfs path correctly
    
    * [Fix-3390][api]Running hive sql task need find the hdfs path correctly
    
    * update soft version
    
    * hive UDF function to modify the background color
    
    * fix
    
    * fix bug: Fix master task dependency check bug
    
    * cancel spark task version check (#3406)
    
    Co-authored-by: Eights-Li <ye...@gmail.com>
    
    * [Bug][ui]Fix front-end bug #3413
    
    * [Feature][ambari_plugin]support one worker can belongs different worker groups when execute install script (#3410)
    
    * Optimize dag
    
    * Update actions.js (#3401)
    
    * [Fix-3256][ui] Fix admin user info update error (#3425) (#3428)
    
    * [PROPOSAL-3139] Datasource selection changes from radio to select
    
    * [PROPOSAL-3139] Datasource selection changes from radio to select
    
    * [BUG FIX] issues #3256
    
    * [BUG FIX] issues #3256
    
    * [BUG FIX] issues #3256
    
    * [Fix-3256][ui] Fix admin user info update error
    
    * [Fix-3256][ui] Fix admin user info update error
    
    * [Fix-3256][ui] Fix admin user info update error
    
    * [Fix-3256][ui] Fix admin user info update error
    
    * reset createUser.vue
    
    * [Fix-3256][ui] Fix admin user info update error
    
    Co-authored-by: dailidong <da...@gmail.com>
    
    Co-authored-by: wuchunfu <31...@qq.com>
    Co-authored-by: dailidong <da...@gmail.com>
    
    * [Fix-3433][api]Fixed that release the imported process definition which version is below 1.3.0 will be failure
    
    * dag connection add check
    
    * fix
    
    * [Fix-3423][dao][sql]Fixed that the resource file of the task node can't be found when upgrade from 1.2.0 to 1.3.x (#3454)
    
    * Remove node deep monitoring
    
    * If worker group id is null,don't need to set the value of the worker group (#3460)
    
    * [Fix-3423][dao][sql]Fixed that the resource file of the task node can't be found when upgrade from 1.2.0 to 1.3.x
    
    * [Fix-3423][dao]If worker group id is null,don't need to set the value of the worker group
    
    * [ui]Code optimization
    
    * fix
    
    * fix
    
    * [fix-3058][ui]Move rtTargetArr to jsPlumbHandle.js
    
    * [optimization][ui]Prevent the shell script input box from being empty
    
    * [Fix-3462][api]If login user is admin,need list all udfs (#3465)
    
    * [Fix-3462][api]If login user is admin,need list all udfs
    
    * [Fix-3462][api]add the test on the method of QueryUdfFuncList
    
    * [Fix-3462][api]fix the code smell
    
    * [Fix-3462][api]fix the code smell
    
    * [Fix-3462][api]fix the code smell
    
    * [Fix-3463][api]Fixed that run the sql task will be failure after rename the udf resource (#3482)
    
    * [fixBug-3058][ui]Fix connection abnormalities in historical workflow instance data
    
    * [Feture-3327][ui]Add the function of re-uploading files in the udf subdirectory
    
    * fix bug: Fix master task dependency check bug (#3473)
    
    Co-authored-by: lenboo <ba...@analysys.com.cn>
    
    * [maven-release-plugin] prepare release 1.3.2
    
    * [maven-release-plugin] prepare for next development iteration
    
    * fix ci_e2e fail (#3497)
    
    * [Fix-3469][api]Should filter the resource by the different program type (#3498)
    
    * [Fix-3463][api]Fixed that run the sql task will be failure after rename the udf resource
    
    * [Fix-3469][api]Should list python file and jar file
    
    * [Fix-3469][api]Should filter the resource by the different program type
    
    * [Fix-3469][api]fix the code smell
    
    * test release 1.3.2 version rollback
    
    * test release 1.3.2 version rollback
    
    * test release 1.3.2 version rollback (#3499)
    
    * [Feature] JVM parameter optimization , related issue #3370
    
    * [Feature] JVM parameter optimization , related issue #3370
    
    * test release 1.3.2 version rollback
    
    * test release 1.3.2 version rollback
    
    Co-authored-by: qiaozhanwei <qi...@analysys.com.cn>
    
    * [maven-release-plugin] prepare release 1.3.2
    
    * [maven-release-plugin] prepare for next development iteration
    
    * [Fix-3469][ui]The value of maintenance resources and the filtering of resources according to different program types
    
    * fix
    
    * Revert "fix ci_e2e fail (#3497)"
    
    This reverts commit e367f90bb73c9682739308a0a98887a1c0f407ef.
    
    * test
    
    * test release 1.3.2 version rollback
    
    * test release 1.3.2 version rollback (#3503)
    
    * [Feature] JVM parameter optimization , related issue #3370
    
    * [Feature] JVM parameter optimization , related issue #3370
    
    * test release 1.3.2 version rollback
    
    * test release 1.3.2 version rollback
    
    * test
    
    * test release 1.3.2 version rollback
    
    Co-authored-by: qiaozhanwei <qi...@analysys.com.cn>
    
    * [maven-release-plugin] prepare release 1.3.2
    
    * [maven-release-plugin] prepare for next development iteration
    
    * test release 1.3.2 version rollback (#3504)
    
    * [Feature] JVM parameter optimization , related issue #3370
    
    * [Feature] JVM parameter optimization , related issue #3370
    
    * test release 1.3.2 version rollback
    
    * test release 1.3.2 version rollback
    
    * test
    
    * test release 1.3.2 version rollback
    
    Co-authored-by: qiaozhanwei <qi...@analysys.com.cn>
    
    * [maven-release-plugin] prepare release 1.3.2
    
    * [maven-release-plugin] prepare for next development iteration
    
    * fix ds muti-level directory in zk, which lead to fail to assign work
    
    * add login user check some actions in api
    
    * [Hotfix][ci] Fix e2e ci docker image build error
    
    * modify tag 1.3.0 to HEAD
    
    * modify tag 1.3.0 to HEAD (#3525)
    
    * [Feature] JVM parameter optimization , related issue #3370
    
    * [Feature] JVM parameter optimization , related issue #3370
    
    * test release 1.3.2 version rollback
    
    * test release 1.3.2 version rollback
    
    * test
    
    * test release 1.3.2 version rollback
    
    * modify tag 1.3.0 to HEAD
    
    Co-authored-by: qiaozhanwei <qi...@analysys.com.cn>
    
    * remove OGNL part of the mybaits notice (#3526)
    
    * [maven-release-plugin] prepare release 1.3.2
    
    * [maven-release-plugin] prepare for next development iteration
    
    * release 1.3.2 version rollback (#3527)
    
    * [Feature] JVM parameter optimization , related issue #3370
    
    * [Feature] JVM parameter optimization , related issue #3370
    
    * test release 1.3.2 version rollback
    
    * test release 1.3.2 version rollback
    
    * test
    
    * test release 1.3.2 version rollback
    
    * modify tag 1.3.0 to HEAD
    
    Co-authored-by: qiaozhanwei <qi...@analysys.com.cn>
    
    * [ui]Script input box to modify the delay loading time
    
    * fix
    
    * fix
    
    * fix
    
    * fix
    
    * modify general user can't create token
    
    * [ui]It is forbidden to select non-existent resources and modify the tree display data format
    
    * modify general user can't create token (#3533)
    
    * [Feature] JVM parameter optimization , related issue #3370
    
    * [Feature] JVM parameter optimization , related issue #3370
    
    * test release 1.3.2 version rollback
    
    * test release 1.3.2 version rollback
    
    * test
    
    * test release 1.3.2 version rollback
    
    * modify tag 1.3.0 to HEAD
    
    * modify general user can't create token
    
    Co-authored-by: qiaozhanwei <qi...@analysys.com.cn>
    
    * if task is null , set task type is null instead of "null"
    
    * [Fix-3536][api]If user didn't have tenant,create resource directory will NPE (#3537)
    
    * [Fix-3536][api]If user didn't have tenant,create resource will NPE
    
    * [Fix-3536][api]If user didn't have tenant,create resource directory will NPE
    
    * modify general user can't create,delete,update token (#3538)
    
    Co-authored-by: qiaozhanwei <qi...@analysys.com.cn>
    
    * [ui]Resource delete OK button to increase loading, change the number of homepage display cursor
    
    * fix
    
    * [Fix-3616][Server] when worker akc/response master exception , async retry (#3748)
    
    * [fixbug][ui]Repair the master and worker management instrument display
    
    * [Fix-3238][docker]Fix that can not create folder in docker with standalone mode (#3741)
    
    * [fixbug][ui]Remove non-existent or deleted resources disabled
    
    * [fixBug-3621][ui]If the workflow instance status is executing status, it is forbidden to select
    
    * [fix-3553][ui]Repair click workflow connection, select the entire path
    
    * fix
    
    * fix
    
    * [Fix-3238][docker]Fix that can not create folder in docker with standalone mode
    
    * [Fix-3616][Server] when worker akc/response master exception , async retry (#3776)
    
    * [Fix-3616][Server] when worker akc/response master exception , async retry
    
    * [Fix-3616][Server] when worker akc/response master exception , async retry
    
    * [Fix-3616][Server] when worker akc/response master exception , async retry
    
    * [Fix-3616][Server] when worker akc/response master exception , async retry
    
    * [Fix-3616][Server] when worker akc/response master exception , async retry
    
    * [Fix-3616][Server] when worker akc/response master exception , async retry
    
    * [Fix-3616][Server] when worker akc/response master exception , async retry
    
    * [Fix-3616][Server] when worker akc/response master exception , async retry
    
    * [Fix-3616][Server] when worker akc/response master exception , async retry
    
    * [Fix-3616][Server] when worker akc/response master exception , async retry
    
    * [Fix-3616][Server] when worker akc/response master exception , async retry
    
    Co-authored-by: qiaozhanwei <qi...@analysys.com.cn>
    
    * The batch delete function in the workflow definition and workflow instance pages cannot be canceled if selected.
    
    * [Improvement-3720][ui] js mailbox verification fix
    
    * [Fix-3549] [Server][sqlTask]The alias column in the query SQL does not take effect (#3784)
    
    * [Fix-3616][Server] when worker akc/response master exception , async retry
    
    * [Fix-3616][Server] when worker akc/response master exception , async retry
    
    * [Fix-3616][Server] when worker akc/response master exception , async retry
    
    * [Fix-3616][Server] when worker akc/response master exception , async retry
    
    * [Fix-3616][Server] when worker akc/response master exception , async retry
    
    * [Fix-3616][Server] when worker akc/response master exception , async retry
    
    * [Fix-3616][Server] when worker akc/response master exception , async retry
    
    * [Fix-3616][Server] when worker akc/response master exception , async retry
    
    * [Fix-3616][Server] when worker akc/response master exception , async retry
    
    * [Fix-3616][Server] when worker akc/response master exception , async retry
    
    * [Fix-3616][Server] when worker akc/response master exception , async retry
    
    * The batch delete function in the workflow definition and workflow instance pages cannot be canceled if selected.
    
    * [Fix-3549] [Server][sqlTask]The alias column in the query SQL does not take effect
    
    * [Fix-3549] [Server][sqlTask]The alias column in the query SQL does not take effect
    
    Co-authored-by: qiaozhanwei <qi...@analysys.com.cn>
    Co-authored-by: zhuangchong <zh...@163.com>
    Co-authored-by: JinyLeeChina <42...@users.noreply.github.com>
    
    * [Fix-3124][docker]Fix that can not build a docker image on windows (#3765)
    
    * [Fix-3549] [Server][sqlTask]The alias column in the query SQL does not take effect (#3786)
    
    * [Fix-3616][Server] when worker akc/response master exception , async retry
    
    * [Fix-3616][Server] when worker akc/response master exception , async retry
    
    * [Fix-3616][Server] when worker akc/response master exception , async retry
    
    * [Fix-3616][Server] when worker akc/response master exception , async retry
    
    * [Fix-3616][Server] when worker akc/response master exception , async retry
    
    * [Fix-3616][Server] when worker akc/response master exception , async retry
    
    * [Fix-3616][Server] when worker akc/response master exception , async retry
    
    * [Fix-3616][Server] when worker akc/response master exception , async retry
    
    * [Fix-3616][Server] when worker akc/response master exception , async retry
    
    * [Fix-3616][Server] when worker akc/response master exception , async retry
    
    * [Fix-3616][Server] when worker akc/response master exception , async retry
    
    * The batch delete function in the workflow definition and workflow instance pages cannot be canceled if selected.
    
    * [Fix-3549] [Server][sqlTask]The alias column in the query SQL does not take effect
    
    * [Fix-3549] [Server][sqlTask]The alias column in the query SQL does not take effect
    
    * [Fix-3549] [Server][sqlTask]The alias column in the query SQL does not take effect
    
    Co-authored-by: qiaozhanwei <qi...@analysys.com.cn>
    Co-authored-by: zhuangchong <zh...@163.com>
    Co-authored-by: JinyLeeChina <42...@users.noreply.github.com>
    
    * [Fix-3258][Security][Worker group manage] Connot get create time and update time,report DateTimeParseException (#3787)
    
    * [Fix-3616][Server] when worker akc/response master exception , async retry
    
    * [Fix-3616][Server] when worker akc/response master exception , async retry
    
    * [Fix-3616][Server] when worker akc/response master exception , async retry
    
    * [Fix-3616][Server] when worker akc/response master exception , async retry
    
    * [Fix-3616][Server] when worker akc/response master exception , async retry
    
    * [Fix-3616][Server] when worker akc/response master exception , async retry
    
    * [Fix-3616][Server] when worker akc/response master exception , async retry
    
    * [Fix-3616][Server] when worker akc/response master exception , async retry
    
    * [Fix-3616][Server] when worker akc/response master exception , async retry
    
    * [Fix-3616][Server] when worker akc/response master exception , async retry
    
    * [Fix-3616][Server] when worker akc/response master exception , async retry
    
    * The batch delete function in the workflow definition and workflow instance pages cannot be canceled if selected.
    
    * [Fix-3549] [Server][sqlTask]The alias column in the query SQL does not take effect
    
    * [Fix-3549] [Server][sqlTask]The alias column in the query SQL does not take effect
    
    * [Fix-3549] [Server][sqlTask]The alias column in the query SQL does not take effect
    
    * [BugFixed] issue #3258 (#3265)
    
    * 'ExecutionStatus'
    
    * '3258'
    
    * Update WorkerGroupServiceTest.java
    
    * Delete UserState.java
    
    * Delete ResourceSyncService.java
    
    * Delete core-site.xml
    
    * Delete hdfs-site.xml
    
    Co-authored-by: dailidong <da...@gmail.com>
    Co-authored-by: qiaozhanwei <qi...@outlook.com>
    
    Co-authored-by: qiaozhanwei <qi...@analysys.com.cn>
    Co-authored-by: zhuangchong <zh...@163.com>
    Co-authored-by: JinyLeeChina <42...@users.noreply.github.com>
    Co-authored-by: dailidong <da...@gmail.com>
    
    * [fixBug-3792][ui]Click on the sidebar to adapt the width of the pie chart on the project homepage
    
    * [Bug-3713][HadoopUtils] catfile method Stream not closed  (#3715)
    
    * fix bug
    
    Delete invalid field: executorcores
    
    Modify verification prompt
    
    * fix bug
    
    Delete invalid field: executorcores
    
    Modify verification prompt
    
    * fix bug
    
    Delete invalid field: executorcores
    
    Modify verification prompt
    
    * dag  add close button
    
    * reset last version
    
    * reset last version
    
    * dag add close buttion
    
    dag add close buttion
    
    * update  CLICK_SAVE_WORKFLOW_BUTTON  xpath
    
    * updae CLICK_SAVE_WORKFLOW_BUTTON xpath
    
    * updae CLICK_SAVE_WORKFLOW_BUTTON xpath
    
    * updae CLICK_SAVE_WORKFLOW_BUTTON xpath
    
    * Update CreateWorkflowLocator.java
    
    modify submit workflow button
    
    * Update CreateWorkflowLocator.java
    
    * Update CreateWorkflowLocator.java
    
    modify CLICK_ADD_BUTTON
    
    * Update CreateWorkflowLocator.java
    
    delete print
    
    * Update CreateWorkflowLocator.java
    
    1
    
    * Update CreateWorkflowLocator.java
    
    1
    
    * Setting '-XX:+DisableExplicitGC ' causes netty memory leaks
    
    in addition
    
    update '- XX: largepagesizeinbytes = 128M' to '- XX: largepagesizeinbytes = 10M'
    
    * Update dag.vue
    
    * Update dag.vue
    
    * Update dag.vue
    
    * Update CreateWorkflowLocator.java
    
    * Revert "Setting '-XX:+DisableExplicitGC ' causes netty memory leaks"
    
    This reverts commit 3a2cba7a
    
    * Setting '-XX:+DisableExplicitGC ' causes netty memory leaks
    
    in addition
    
    update '- XX: largepagesizeinbytes = 128M' to '- XX: largepagesizeinbytes = 10M'
    
    * Update dolphinscheduler-daemon.sh
    
    * catfile method Stream not closed
    
    * catfile method Stream not closed
    
    Co-authored-by: dailidong <da...@gmail.com>
    Co-authored-by: xingchun-chen <55...@users.noreply.github.com>
    
    * [Fix-#3713][common]Fix that catfile method Stream not closed
    
    * [Fix-#3713][common]Fix that catfile method Stream not closed (#3810)
    
    * [Bug-3713][HadoopUtils] catfile method Stream not closed  (#3715)
    
    * fix bug
    
    Delete invalid field: executorcores
    
    Modify verification prompt
    
    * fix bug
    
    Delete invalid field: executorcores
    
    Modify verification prompt
    
    * fix bug
    
    Delete invalid field: executorcores
    
    Modify verification prompt
    
    * dag  add close button
    
    * reset last version
    
    * reset last version
    
    * dag add close buttion
    
    dag add close buttion
    
    * update  CLICK_SAVE_WORKFLOW_BUTTON  xpath
    
    * updae CLICK_SAVE_WORKFLOW_BUTTON xpath
    
    * updae CLICK_SAVE_WORKFLOW_BUTTON xpath
    
    * updae CLICK_SAVE_WORKFLOW_BUTTON xpath
    
    * Update CreateWorkflowLocator.java
    
    modify submit workflow button
    
    * Update CreateWorkflowLocator.java
    
    * Update CreateWorkflowLocator.java
    
    modify CLICK_ADD_BUTTON
    
    * Update CreateWorkflowLocator.java
    
    delete print
    
    * Update CreateWorkflowLocator.java
    
    1
    
    * Update CreateWorkflowLocator.java
    
    1
    
    * Setting '-XX:+DisableExplicitGC ' causes netty memory leaks
    
    in addition
    
    update '- XX: largepagesizeinbytes = 128M' to '- XX: largepagesizeinbytes = 10M'
    
    * Update dag.vue
    
    * Update dag.vue
    
    * Update dag.vue
    
    * Update CreateWorkflowLocator.java
    
    * Revert "Setting '-XX:+DisableExplicitGC ' causes netty memory leaks"
    
    This reverts commit 3a2cba7a
    
    * Setting '-XX:+DisableExplicitGC ' causes netty memory leaks
    
    in addition
    
    update '- XX: largepagesizeinbytes = 128M' to '- XX: largepagesizeinbytes = 10M'
    
    * Update dolphinscheduler-daemon.sh
    
    * catfile method Stream not closed
    
    * catfile method Stream not closed
    
    Co-authored-by: dailidong <da...@gmail.com>
    Co-authored-by: xingchun-chen <55...@users.noreply.github.com>
    
    * [Fix-#3713][common]Fix that catfile method Stream not closed
    
    Co-authored-by: BoYiZhang <39...@users.noreply.github.com>
    Co-authored-by: dailidong <da...@gmail.com>
    Co-authored-by: xingchun-chen <55...@users.noreply.github.com>
    
    * [Fix-#3487][api、dao] cherry pick from dev to fix that create folder duplicate name under multithreading
    
    * [Hotfix-3131][api] Fix the new tenant already exists prompt (#3132)
    
    * Bugfix: Fix the new tenant already exists prompt
    
    * Feature: Add test cases
    
    * Update TenantServiceTest.java
    
    Co-authored-by: dailidong <da...@gmail.com>
    Co-authored-by: qiaozhanwei <qi...@outlook.com>
    
    * Set up JDK 11 for SonarCloud in github action. (#3052)
    
    * Set up JDK 11 for SonarCloud in github action.
    
    * Fix javadoc error with JDK 11.
    
    * Prevent Javadoc from stopping if it finds any html errors.
    
    * [fixBug-3621][ui]Select the batch checkbox to unfilter the instances in the executing state
    
    * add verify tenant name cannot contain special characters.
    
    * [fixBug-3840][ui]The tenant code only allows letters or a combination of letters and numbers
    
    * fix
    
    * fix
    
    * fix
    
    * [Fix-#3702][api] When re-upload the resource file but don't change the name or desc,it need replace the origin resource file. (#3862)
    
    * [Fix-#3702][api] When re-upload the resource file but don't change the name or desc,it will not replace the origin resource file.
    
    * [Fix-#3702][api] When re-upload the resource file but don't change the name or desc,it will not replace the origin resource file.
    
    * [fixbug-3621][ui]Workflow instance ready to stop and ready to suspend state prohibits checking
    
    * [fixbug-3887][ui]Fix missing English translation of re-upload files
    
    * add process define name verify. (#3879)
    
    * Revert "[1.3.3-release][fix-3835][ui] When the tenantName contains "<", the tenant drop-down list is blankadd verify tenant name cannot contain special characters."
    
    * revert pr 3872
    
    * [FIX-3617][Service]after subtask fault tolerance, 2 task instances are generated (#3830)
    
    * fix bug(#3617): after subtask fault tolerance, 2 task instances are generated.
    
    * delete unused code
    
    * update code smell
    
    * refactor sub work command process
    
    * add process service ut
    
    * add license header
    
    * fix some code smell
    
    * chang ut java8 to java11
    
    * update sonar to java11
    
    * copy ut config from dev
    
    * remove checkstyle
    
    * revert to 1.3.3
    
    * change proess service test to executor service
    
    * add process service test
    
    * add process service test
    
    * revert
    
    * revert
    
    * add comments
    
    * change dev to 1.3.3-release
    
    * revert
    
    Co-authored-by: baoliang <ba...@analysys.com.cn>
    
    * [Fix-#3487][sql] add dolphinscheduler_dml.sql under 1.3.3_schema (#3907)
    
    * [FIX-3836][1.3.3-release-API] process definition validation name interface prompt information error  (#3899)
    
    * fix bug : error message
    
    * fix code smell
    
    * fix code smell
    
    * [FIX_#3789][remote]cherry pick from dev to support netty heart beat
    
    * [FIX_#3789][remote]cherry pick from dev to support netty heart beat
    
    * [FIX_#3789][remote]cherry pick from dev to support netty heart beat (#3913)
    
    * [FIX_#3789][remote]cherry pick from dev to support netty heart beat
    
    * [FIX_#3789][remote]cherry pick from dev to support netty heart beat
    
    Co-authored-by: Kirs <ac...@163.com>
    
    * Repair check box cannot be canceled
    
    * [fix-3843][api] When update workflow definition,if name already exists, the prompt is not friendly
    
    * [fix-3843][api] When update workflow definition,if name already exists, the prompt is not friendly
    
    * [fix-#3843][api]When update workflow definition,if name already exists, the prompt is not friendly (#3918)
    
    * [FIX_#3789][remote]cherry pick from dev to support netty heart beat
    
    * [FIX_#3789][remote]cherry pick from dev to support netty heart beat
    
    * [fix-3843][api] When update workflow definition,if name already exists, the prompt is not friendly
    
    * [fix-3843][api] When update workflow definition,if name already exists, the prompt is not friendly
    
    Co-authored-by: Kirs <ac...@163.com>
    
    * [Fix-#3487][sql] update uc_dolphin_T_t_ds_resources_un
    
    * Workflow definition name re-modified and added check
    
    * [fix-#3843][api]When update workflow definition,if name already exists, the prompt is not friendly.
    
    * update code.
    
    * [#3931][ui]Field name optimization for spark, flink, and mr
    
    * change version from 1.3.2-SNAPSHOT to 1.3.3-SNAPSHOT (#3934)
    
    * [maven-release-plugin] prepare release 1.3.3
    
    * [maven-release-plugin] prepare for next development iteration
    
    * [ambari-plugin]change version 1.3.2 to 1.3.3 (#3935)
    
    * fix bug:3615 After the task is executed successfully, but the next task has not been submitted, stop the master
    
    * [fixBug-3964][ui]Switch back and forth over timeout alarm, the selected value is empty
    
    * solve too many files, close logClientService (#3971)
    
    * fix #3966 sub process doesnot send alert mail after process instance ending. (#3972)
    
    Co-authored-by: baoliang <ba...@analysys.com.cn>
    
    * [Fix-#3618][server] resolve task executed finished but not release the file handle (#3975)
    
    * [Fix-#3618][server] resolve task executed finished but not release the file handle
    
    * [Fix-#3618][server] resolve task executed finished but not release the file handle
    
    * [Fix-#3958][api] files should not be created successfully in the directory of the authorized file
    
    * [FIX-3966] The timeout warning does not take effect in sub_process (#3982)
    
    * fix #3966 sub process doesnot send alert mail after process instance ending.
    
    * fix bug 3964: sub_process The timeout warning does not take effect
    add timeout warning for sub_process/dependent task.
    
    * fix code smell
    
    * fix code smell
    
    * fix code smell
    
    * update worker group inherit from parent
    
    Co-authored-by: baoliang <ba...@analysys.com.cn>
    
    * fix import dolphinscheduler_mysql.sql insert admin user data
    
    * [FIX-3929] condition task would post wrong tasks when failover. (#3999)
    
    * fix #3966 sub process doesnot send alert mail after process instance ending.
    
    * fix bug 3964: sub_process The timeout warning does not take effect
    add timeout warning for sub_process/dependent task.
    
    * fix code smell
    
    * fix code smell
    
    * fix code smell
    
    * update worker group inherit from parent
    
    * remove stdout in logback configuration
    
    * fix bug #3929 condition task would post error when failover.
    
    * remove unused test
    
    * add comments
    
    * add skip node judge
    
    Co-authored-by: baoliang <ba...@analysys.com.cn>
    
    * [FIX-3929]  because of no lock, start up failover would dispatch two same tasks. (#4004)
    
    * fix #3966 sub process doesnot send alert mail after process instance ending.
    
    * fix bug 3964: sub_process The timeout warning does not take effect
    add timeout warning for sub_process/dependent task.
    
    * fix code smell
    
    * fix code smell
    
    * fix code smell
    
    * update worker group inherit from parent
    
    * remove stdout in logback configuration
    
    * fix bug #3929 condition task would post error when failover.
    
    * remove unused test
    
    * add comments
    
    * add skip node judge
    
    * fix bug 3929: because of no lock, start up failover would dispatch two same tasks.
    
    Co-authored-by: baoliang <ba...@analysys.com.cn>
    
    * revert pom version to 1.3.3-release
    
    * [maven-release-plugin] prepare release 1.3.3
    
    * [maven-release-plugin] prepare for next development iteration
    
    * [release]revert pom version to 1.3.3-release
    
    * fix bug 4010: remove failed condition tasks from error-task-list. (#4011)
    
    Co-authored-by: baoliang <ba...@analysys.com.cn>
    
    * [maven-release-plugin] prepare release 1.3.3
    
    * [maven-release-plugin] prepare for next development iteration
    
    * merge from 1.3.3-release
    
    * merge from 1.3.3-release
    
    * merge from 1.3.3-release
    
    * merge from 1.3.3-release
    
    * merge from 1.3.3-release
    
    * merge from 1.3.3-release
    
    * merge from 1.3.3-release
    
    * merge from 1.3.3-release
    
    * merge from 1.3.3-release
    
    * refactor code style
    
    * refactor code style
    
    * refactor code style
    
    * merge from 1.3.3-release
    
    * merge from 1.3.3-release
    
    * merge from 1.3.3-release
    
    * merge from 1.3.3-release
    
    * merge from 1.3.3-release
    
    * refactor ut test
    
    * refactor ut test
    
    * merge from 1.3.3-release
    
    * merge from 1.3.3-release
    
    * merge from 1.3.3-release
    
    * refactor ut
    
    * merge from 1.3.3-release
    
    * refactor ut
    
    * refactor ut
    
    * refactor
    
    * refactor
    
    * refactor code style
    
    * refactor code style
    
    * refactor code style
    
    * refactor code style
    
    * merge from 1.3.3-release
    
    * merge from 1.3.3-release
    
    * merge from 1.3.3-release
    
    * merge from 1.3.3-release
    
    * merge from 1.3.3-release
    
    * refactor code style
    
    Co-authored-by: break60 <79...@qq.com>
    Co-authored-by: wuchunfu <31...@qq.com>
    Co-authored-by: lgcareer <18...@163.com>
    Co-authored-by: xingchun-chen <55...@users.noreply.github.com>
    Co-authored-by: lenboo <ba...@analysys.com.cn>
    Co-authored-by: qiaozhanwei <qi...@analysys.com.cn>
    Co-authored-by: Yelli <am...@my.com>
    Co-authored-by: Eights-Li <ye...@gmail.com>
    Co-authored-by: JinyLeeChina <42...@users.noreply.github.com>
    Co-authored-by: dailidong <da...@gmail.com>
    Co-authored-by: qiaozhanwei <qi...@outlook.com>
    Co-authored-by: XiaotaoYi <v-...@hotmail.com>
    Co-authored-by: Yichao Yang <10...@qq.com>
    Co-authored-by: zhuangchong <zh...@163.com>
    Co-authored-by: BoYiZhang <39...@users.noreply.github.com>
    Co-authored-by: muzhongjiang <mu...@163.com>
    Co-authored-by: Jave-Chen <ba...@gmail.com>
    Co-authored-by: zhuangchong <zh...@163.com>
    Co-authored-by: zhuangchong <37...@users.noreply.github.com>
    Co-authored-by: Kirs <ac...@163.com>
    Co-authored-by: lgcareer <lg...@apache.org>
    Co-authored-by: wulingqi <wu...@baijiahulian.com>
    
    * Revert "[Draft][Merge][133-dev]133 merge dev (#4031)" (#4057)
    
    This reverts commit ad2d9f99d01f3baef8641e0a7e741c08a39f1f24.
    
    * [Fix][API] Condition task null pointer exception (#4056)
    
    * add ProcessUtils UT
    
    * modify ProcessUtils & ProcessUtilsTest
    
    * Enhance user experience, add close button, return to the previous page (#4006)
    
    Co-authored-by: zhanglong <zh...@ysstech.com>
    
    * [FIX_BUG][server-test] dismiss of server warm-up time in RoundRobinSelectorTest (#4067)
    
    * [Fix][api] Fix build parameter error of sqlserver when create. (#4015)
    
    * [fix-#3962][api] Avoid ClassCastException for LoggerService.queryLog().
    
    * [Fix][api] Fix build parameter error of sqlserver when create.
    
    * [Feature-3985][Datax] Datax supports setting up running memory (#3986)
    
    * Datax supports setting up running memory
    
    * Datax supports setting up running memory
    
    * Datax supports setting up running memory
    
    * When running a task, the resource file is lost, which results in an error
    
    * add unit test
    
    * add unit test
    
    * add unit test
    
    * add test unit
    
    * add test unit
    
    * add test unit
    
    * fix code smell
    
    * add test unit
    
    * add test unit
    
    * [Improvement-3767][api] Task instance supports query by process instance name (#3825)
    
    * Task instance supports query by process instance name.
    
    * add test code checkstyle.
    
    * add test param.
    
    * resolve the sonar duplication check.
    
    * solve logger single-line string exceeds 200 characters.
    
    * resolve the sonar check.
    
    * Resolve code conflicts.
    
    Co-authored-by: zhuangchong <zh...@163.com>
    
    * deal with magic value
    
    * [Feature-4050][server] Spark task support udv inject (#4061)
    
    * #4050 spark task support udv inject
    
    * modify spark task UT
    
    * modify sparkTaskExecutionCtx
    
    * add exp for spark task get main jar method
    
    * deal with magic value
    
    * [Improvement-3471][common] JSONUtils.toMap It is not necessary to check whether the JSON method is empty again. #3471 (#3481)
    
    * JSONUtils.toMap call improvement.
    
    * [Fix-4054][Api] Fix The last week of the month for adding/editing timing, preview and save timing will report an error
    
    * 解决单独执行子节点空指针的问题
    
    * using OSUtils.execCmd when kill yarn app
    
    * 解决单独执行子节点空指针的问题(增加checkstyle)
    
    * 解决单独执行子节点空指针的问题(增加checkstyle)
    
    * [FIX-#4083][server]fix taskInstance state change error
    Concurrent processing of ack message and result message causes the execution sequence to be wrong
    
    # this close # 4083
    
    * code style
    
    * fix replaceNRTtoUnderline NullPointerException #4098 (#4100)
    
    * fix replaceNRTtoUnderline NullPointerException
    
    * add  unit Test
    
    * add taskResponseTest
    
    * add taskResponseTest
    
    * code smell
    
    * Time is too small and the task is not finished
    
    * Time is too small and the task is not finished
    
    * [FIX-4034][server] fix sqoop import fail (#4036)
    
    * fix #4043, sqoop import query fail
    
    * fix #4043, sqoop task hard code & code style
    
    * add license for SqoopConstants
    
    * add private constructor for SqoopConstants
    
    * fixed sqoop mysql pwd have special character
    
    * fix checkstyle
    
    * fix sqoop task log
    
    * remove unused constants
    
    * Time is too small and the task is not finished
    
    * Time is too small and the task is not finished
    
    * test
    
    * remove assert
    
    * test
    
    * test
    
    * fix task instance status judgment error
    
    * fix: security page disappear delay problem when force refresh under GENERAL_USER
    
    * improvement: resovle download url with resolveURL to prevent change of apiPrefix
    
    * fix sqoop task jdbc string contains special char (#4105)
    
    * [Bug][Common] read file garbled (#3479)
    
    * fix bug : Random code problem
    
    Co-authored-by: zhanglong <zh...@ysstech.com>
    
    * [Improvement-3933][db operation] Improve the performance of sql query (#3940)
    
    * optimize select * case
    
    * emove redundancy
    
    * bug fixed
    
    * Update en_US.js
    
    * Update startup.sh
    
    * optimize
    
    * optimize code
    
    * optimize
    
    * bug fixed
    
    * add ut
    
    * bug fixed
    
    * bug fixed
    
    * bug fixed
    
    * bug fixed
    
    * Delete WorkFlowLineageMapper.xml
    
    * Delete createTenement.vue
    
    * recove wrongly deleted file
    
    * Update WorkFlowLineageMapper.xml
    
    * Update createTenement.vue
    
    * [Feature][API]enable response resources gzip compression (#4121)
    
    * enable response compression
    
    * add server.compression.mime-types with default value explicitly
    
    * [Improvement][API] ignore noNodeException when get worker groups (#4120)
    
    * ignore noNodeException when get worker groups
    
    * add ut
    
    * [Improvement][Code style] FIX SPELL WAITTING TO WAITING , etc. (#4118)
    
    * FIX SPELL
    
    * FIX SPELL AND  Optimizing code conventions
    
    * add ut  cannot construct process instance, return null;
    
    * add ut testExportProcessMetaData
    
    * add ut testExportProcessMetaData
    
    * add ut testImportProcessSchedule
    
    * add ut MasterExecThreadTest
    
    * add ut MasterExecThreadTest
    
    * add ut testSubProcessViewTree
    
    * add ut testComplementWithStartNodeList
    
    * add ut testRecurseFindSubProcessId
    
    * add ut testRecurseFindSubProcessId
    
    * add ut testRecurseFindSubProcessId
    
    * [FIX#4033] $[] conflicts with mysql keywords (#4111)
    
    * [FIX#4033] $[] conflicts with mysql keywords
    We currently only use this symbol for dates, so I filtered out the number type.
    this close #4033
    
    * test
    
    * fix error
    
    * split sqoop import hive database and table (#4141)
    
    * upgrade quartz version to 2.3.0
    
    * add HikariCP-java6,c3p0,mchange-commons-java license
    
    * upgrade jackson version to 2.9.10
    
    * remove c3p0,mchange-commons-java license
    
    * update Check code style
    
    * update  code style
    
    * Update pom.xml
    
    * Update pom.xml
    
    * Update LICENSE
    
    not need to add HikariCP-java6
    
    * Delete LICENSE-HikariCP-java6.txt
    
    * Update known-dependencies.txt
    
    * Update TaskInstanceMapper.xml
    
    optimize page of [TaskInstance] load data slow
    
    * [FIX-PR-4097][server-master]task ack miss (#4189)
    
    When the message of successful execution arrives earlier than
    the message of ack,
    the message of ack will be discarded,
    resulting in some information missing
    
    * fix bug #4125 (#4127)
    
    * fix bug #4125
    
    * code style!
    
    * code style.
    
    Co-authored-by: chengp <ch...@chengp.net>
    
    * [FIX-4190][DAO] When the amount of json data is large, the process list page display slowly. (#4201)
    
    * fix 4190: When the amount of json data is large, process list page display slowly
    
    * fix 4190: When the amount of json data is large, process list page display slowly
    
    * fix 4190: When the amount of json data is large, process list page display slowly
    
    Co-authored-by: baoliang <ba...@analysys.com.cn>
    
    * [Fix-3457][flink] fix flink args build problem (#4166)
    
    * [Fix][Flink] fix flink args build problem
    
    * [Fix][Flink] fix FlinkArgsUtilsTest
    
    * [Improvement][UI] hide version and cluster input when deployMode is local
    
    * [common]del windows file( not support windows ) (#4204)
    
    * del support win
    
    * del support win
    
    * [Feature-4138][Master] dispatch workgroup error add sleep time (#4139)
    
    * When there are tasks with assignment failure and the number of tasks in the current task queue is less than 10, sleep for 1 second
    
    * When there are tasks with assignment failure and the number of tasks in the current task queue is less than 10, sleep for 1 second
    
    * fix code smell & code style
    
    * fix code smell & code style
    
    Co-authored-by: zhanglong <zh...@ysstech.com>
    
    * [FIX-#4172][server-worker] kill task NPE (#4182)
    
    * [FIX-#4172][server-worker] kill task NPE
    
    The cache task will be sent when the Process is generated. Before that, if a kill task appears, then NPE will appear
    Modification method: write into the cache when the task is received, and mark it as preData
    If the task is killed before the Process is generated, delete the cache directly at this time
    It will be judged before the process is generated. If the task has been killed, it will not be executed.
    After the new process is created, write it into the cache, and judge again, if kill, then kill the process.
    
    this closes #4172
    
    * Delete the commented out code
    Add spring beans
    
    * code smell
    
    * add test
    
    * add test
    
    * fix error
    
    * test
    
    * test
    
    * revert
    
    * fix error
    
    * [Feature-3878]Replace the page with element-ui (#4065)
    
    * [Feature-3878]Introduce elment-ui and replace the security center module page with elment-ui
    
    * Change node version
    
    * fix
    
    * fix
    
    * fix
    
    * fix
    
    * fix
    
    * fix
    
    * fix
    
    * fix
    
    * fix
    
    * fix
    
    * Replace the page with element-ui
    
    * Replace user management with elment-ui
    
    * fix
    
    * fix
    
    * fix
    
    * fix
    
    * fix
    
    * fix
    
    * fix
    
    * fix
    
    * fix
    
    * fix
    
    * fix
    
    * fix
    
    * fix
    
    * fix
    
    * fix
    
    * fix
    
    * fix
    
    * fix
    
    * fix
    
    * fix
    
    * Repair the last Sunday of each month
    
    * support auto eslint for .js, .vue file on save
    
    * update .eslintrc.yml and license check exclude .eslintignore
    
    * fix eslint: fix syntax by npm run lint automatically
    
    * fix eslint: fatal syntax errors
    
    * fix eslint: expected '!==' but instead saw '!=', expected '===' but instead saw '=='
    
    * fix eslint: Unexpected side effect in 'cacheParams' computed property
    
    * fix eslint: assigned a value but never used
    
    * fix eslint: component has been registered but not used
    
    * fix eslint: unexpected mutation of prop
    
    * fix bug: start from the setting nodes with NODE_PRE would be NPE. (#4219)
    
    * deleted invalid code as assigned in issue 4215 (#4221)
    
    deleted the code at following two places:
    org.apache.dolphinscheduler.service.process.ProcessService#checkTaskExistsInTaskQueue
    org.apache.dolphinscheduler.service.process.ProcessService#taskZkInfo
    
    * [FIX-#3177]Task time parameter parsing error (#4224)
    
    * [FIX-#3177]Task time parameter parsing error
    
    rerun schedule time error
    
    this closes #3177
    
    * [FIX-#3177]Task time parameter parsing error
    
    rerun schedule time error
    
    this closes #3177
    
    * fix sql error (#4227)
    
    * [FIX-3177]Task time parameter parsing error (#4228)
    
    * [FIX-3177]Task time parameter parsing error
    when system.datetime !=null $[datetime] = system.datetime
    else $[datetime] = current time
    
    * remove unused import
    
    * fix date_convert null
    
    * fix time cover
    
    * fix time cover
    
    * reformat
    
    * add ut
    
    * [Fix][UI]: fix re-login problem in new tab and state synchronization problem in multiple tabs (#4162)
    
    * fix(ui): re-login problem in new tab
    
    * refresh page in new tab automatically
    
    * support to reload router view in lightweight
    
    * optimize visibility code
    
    * [Improvement][UI] Add no-var rule for eslint and add 'npm run lint:fix' command (#4225)
    
    Add no-var rule for eslint, and fix related files
    Add npm run lint:fix command, which distinguished from the default command npm run lint
    The behavior of command npm run lint and command npm run lint:fix should be distinguished
    The first command is used to lint only, and the second one is used to lint and fix problems automatically
    
    * [FIX][UI ]  security user state  tenantCode User Type Display abnormal (#4255)
    
    * fix user state error
    
    * fix userType  error
    
    * fix tenantCode  error
    
    * [Fix-4222][Master]Add the priority queue to ensure that tasks are submitted according to priority. (#4250)
    
    * [Fix-4222][Master]Add the priority queue to ensure that tasks are submitted according to priority.
    
    * [Fix-4222][Master]Add the priority queue to ensure that tasks are submitted according to priority.
    
    * [Fix-4222][Master]Remove useless import
    
    * [Fix-4222][Master]Reformat code style
    
    * [Fix-4222][Master]Reformat code style
    
    * [Fix-4222][Master]Reformat code style
    
    * [Fix-4222][Master]add PeerTaskInstancePriorityQueueTest
    
    * [Fix-4222][Master]Fix code smell
    
    * [Fix-4222][Master]Reformat code style
    
    * [Fix-4222][Master]Fix code smell
    
    Co-authored-by: xingchun-chen <55...@users.noreply.github.com>
    
    * update bug template (#4260)
    
    * [Improvement-4069][server] When the tenant does not exist, the task execution should throw an exception (#4108)
    
    * when  the tenant does not exist, the task execution should throw an exception
    
    * remote method createWorkDirAndUserIfAbsent
    
    * set the task status failed when the tenant code does not exist.
    
    * add taskLog.
    
    * update check os user exists
    
    * update TaskExecuteThreadTest test method.
    
    * solving sonar fail.
    
    * remove AbstractTask.getCurTaskParamsClass()and replace with TaskParametersUtils.getParameters() (#4262)
    
    * remove getCurTaskParamsClass() in AbstractTask.java and replace it with TaskParametersUtils.getParameters()
    
    * remove unused imports in AbstractTask.java
    
    * reformat
    
    * [Fix-4271][server] Fix IOException or NoSuchFileException in logger server (#4272)
    
    * fix version funcation delete. (#4265)
    
    * [improvement][config] Update datasource.properties,add mysql meta data template (#4266)
    
    * Update datasource.properties
    
    [新增]元数据mysql连接模版
    
    * Update datasource.properties
    
    Co-authored-by: dailidong <da...@gmail.com>
    
    * fix taskInstance submitTime is empty. (#4274)
    
    * [Improvement-3878]Tenant list delete user name (#4278)
    
    * [Fix-4268] Fix NumberFormatException when visiting a doc.html or swagger-ui.html (#4269)
    
    * [Fix-4268] Fix NumberFormatException when visiting a doc.html or swagger-ui.html
    
    * Fix checkstyle error
    
    * Update swagger-models in known-dependencies.txt
    
    * [Improvement-3878][ui]Fix the list style (#4280)
    
    * [Improvement-3878]Tenant list delete user name
    
    * [Improvement-3878][ui]Fix the list style
    
    * [FIX] [UI] fix create project cancel button invalid (#4282)
    
    * fix create project cancel button invalid
    
    * [Improvement] Refactor code to support distributed tracing (#4270)
    
    * Refactor code to support tracing
    
    * Extension network protocol, support context and version
    * Extension master asynchronous queue support context
    * Extract scan task method from MasterSchedulerService for tracing
    
    * fix
    
    * fix
    
    * add test case
    
    * fix
    
    * fix
    
    Co-authored-by: hailin0 <ha...@yeah.net>
    
    * [Improvement] Add Flink job name (#4285)
    
    * [Improvement] Add Flink job name
    
    * fix typo
    
    * [Improvement][ui]List vacancy optimization and icon icon repair (#4286)
    
    * [Improvement][ui] List vacancy optimization and icon icon repair
    
    * [FEATURE-736] integrate ldap authentication (#3743)
    
    * [FEATURE-736] integrate ldap authentication
    
    * add ldap authentication type
    * refactor authentication with ldap and password
    * add  createUser for ldap in user service
    * remove duplicate password authenticator
    
    * [Fix-4289][*] Flink name with disappeared and unescaped problem (#4290)
    
    * fix flink name not display in process definition editor
    
    * fix flink name not escape problem
    
    * simplify escape method
    
    * fix database is the mysql DB keyword. (#4295)
    
    * fix the token management list does not display the user name. (#4302)
    
    * Dev imp server process utils (#4263)
    
    
    * Adds the comment on the WINDOWSATTERN
    
    Co-authored-by: Kirs <ac...@163.com>
    Co-authored-by: 0002939 <li...@mininglamp.com>
    
    * fix version close function does not take effect. (#4307)
    
    * fix user management authorization operation exceptions do not prompt exception information. (#4292)
    
    * [Fix][UI] Fix ui style problem and refactor form style (#4329)
    
    * rename from-model to form-model
    
    * [UI] remove duplicated css style code
    
    * [UI] refactor css style of form model
    
    * fix form model overflow problem in ie
    
    * Align each item one by one in locale
    
    * fix el-dialog width is too wide
    
    * fix locale
    
    * [Improvement][UI] Improve script box and dialog css style (#4331)
    
    * [Fix-4335][UI] Fix IE 9-11 not supported
    
    * [FIx-4338][UI] Fix invalid date problem in IE
    
    * [Improvement] Use environment variable $DOLPHINSCHEDULE_OPTS as daemon startup arguments (#4341)
    
    Co-authored-by: hailin0 <ha...@yeah.net>
    
    * [Fix][common] only two yarns can be selected(#4314) (#4344)
    
    * Handling conflicts manually
    
    * code style
    
    * ut skip ui
    
    * fix ut error
    
    * fix ut error
    
    * delete SchedulerController Unused field
    
    * delete SchedulerController Unused field
    
    * delete SchedulerController Unused field
    
    * ci skip ui
    
    * ignore AlertPluginManagerTest.java and DolphinPluginLoaderTest.java
    
    * fix ut error
    
    * fix ut error
    
    * test
    
    * code style
    
    * replace ui
    
    * replace ui
    
    * replace ui
    
    * replace ui
    
    * fix license
    
    * fix license
    
    Co-authored-by: 小清 <62...@users.noreply.github.com>
    Co-authored-by: zhuangchong <37...@users.noreply.github.com>
    Co-authored-by: spring-bu <37...@users.noreply.github.com>
    Co-authored-by: dailidong <da...@gmail.com>
    Co-authored-by: lgcareer <18...@163.com>
    Co-authored-by: BoYiZhang <39...@users.noreply.github.com>
    Co-authored-by: xingchun-chen <55...@users.noreply.github.com>
    Co-authored-by: baoliang <ba...@analysys.com.cn>
    Co-authored-by: BoYiZhang <zh...@163.com>
    Co-authored-by: zixi0825 <64...@qq.com>
    Co-authored-by: sunchaohe <su...@linklogis.com>
    Co-authored-by: break60 <79...@qq.com>
    Co-authored-by: bao liang <29...@users.noreply.github.com>
    Co-authored-by: t1mon <17...@qq.com>
    Co-authored-by: zhanglong <zh...@ysstech.com>
    Co-authored-by: 孙继峰 <su...@outlook.com>
    Co-authored-by: wulingqi <wu...@baijiahulian.com>
    Co-authored-by: lgcareer <lg...@apache.org>
    Co-authored-by: IamMujuziMoses <49...@users.noreply.github.com>
    Co-authored-by: huzekang <10...@qq.com>
    Co-authored-by: JinyLeeChina <42...@users.noreply.github.com>
    Co-authored-by: Eights-LI <ye...@gmail.com>
    Co-authored-by: wuchunfu <31...@qq.com>
    Co-authored-by: qiaozhanwei <qi...@analysys.com.cn>
    Co-authored-by: Yelli <am...@my.com>
    Co-authored-by: Eights-Li <ye...@gmail.com>
    Co-authored-by: qiaozhanwei <qi...@outlook.com>
    Co-authored-by: XiaotaoYi <v-...@hotmail.com>
    Co-authored-by: Yichao Yang <10...@qq.com>
    Co-authored-by: zhuangchong <zh...@163.com>
    Co-authored-by: muzhongjiang <mu...@163.com>
    Co-authored-by: Jave-Chen <ba...@gmail.com>
    Co-authored-by: zhuangchong <zh...@163.com>
    Co-authored-by: hailin0 <ha...@yeah.net>
    Co-authored-by: yangquan <iy...@qq.com>
    Co-authored-by: liliang1991 <li...@126.com>
    Co-authored-by: liang.li.c <li...@17zuoye.com>
    Co-authored-by: felix.wang <59...@users.noreply.github.com>
    Co-authored-by: chengshiwen <ch...@gmail.com>
    Co-authored-by: Yarlung <tu...@163.com>
    Co-authored-by: karlsun <ka...@tencent.com>
    Co-authored-by: wangxj3 <85...@qq.com>
    Co-authored-by: chengp <50...@qq.com>
    Co-authored-by: chengp <ch...@chengp.net>
    Co-authored-by: Saksham Gupta <sa...@gmail.com>
    Co-authored-by: Tq <36...@users.noreply.github.com>
    Co-authored-by: Jatham <59...@users.noreply.github.com>
    Co-authored-by: hailin0 <ha...@foxmail.com>
    Co-authored-by: zh0122 <zh...@gmail.com>
    Co-authored-by: geosmart <ge...@hotmail.com>
    Co-authored-by: 李丛阳 <li...@163.com>
    Co-authored-by: 0002939 <li...@mininglamp.com>
    Co-authored-by: kamisamak <10...@qq.com>
    
    * fix alert-spi error
    todo sql script
    
    * add page query plugin instance
    add plugin instance check name
    
    * add page query plugin instance
    add plugin instance check name
    
    * add props field
    
    * ding talk some filed is not required
    
    * fix bug
    
    * fix error
    
    * revert
    
    * add name field provide to ui
    
    * fix send error
    
    * add ui
    
    * delete useless constant definitions
    code style
    
    * code style
    
    * fix license head error
    
    * fix ut error
    
    * fix ut error
    
    * del unused jar
    
    * import alert-plugin
    
    * upgrade jboss-logging
    
    * upgrade jboss-logging
    
    * fix ut error
    
    * fix Vulnerabilities
    
    * fix Vulnerabilities
    
    * fix Vulnerabilities
    
    * code smell
    
    * fix Vulnerabilities
    
    * add  public constructors
    
    * fix  Vulnerabilities
    
    * resolve conflicts
    
    Co-authored-by: break60 <79...@qq.com>
    Co-authored-by: gaojun2048 <32...@users.noreply.github.com>
    Co-authored-by: zhuangchong <zh...@163.com>
    Co-authored-by: dailidong <da...@gmail.com>
    Co-authored-by: samz406 <sa...@foxmail.com>
    Co-authored-by: gaojun2048 <54...@qq.com>
    Co-authored-by: zhuangchong <37...@users.noreply.github.com>
    Co-authored-by: Yichao Yang <10...@qq.com>
    Co-authored-by: 小清 <62...@users.noreply.github.com>
    Co-authored-by: spring-bu <37...@users.noreply.github.com>
    Co-authored-by: lgcareer <18...@163.com>
    Co-authored-by: BoYiZhang <39...@users.noreply.github.com>
    Co-authored-by: xingchun-chen <55...@users.noreply.github.com>
    Co-authored-by: baoliang <ba...@analysys.com.cn>
    Co-authored-by: BoYiZhang <zh...@163.com>
    Co-authored-by: zixi0825 <64...@qq.com>
    Co-authored-by: sunchaohe <su...@linklogis.com>
    Co-authored-by: bao liang <29...@users.noreply.github.com>
    Co-authored-by: t1mon <17...@qq.com>
    Co-authored-by: zhanglong <zh...@ysstech.com>
    Co-authored-by: 孙继峰 <su...@outlook.com>
    Co-authored-by: wulingqi <wu...@baijiahulian.com>
    Co-authored-by: lgcareer <lg...@apache.org>
    Co-authored-by: IamMujuziMoses <49...@users.noreply.github.com>
    Co-authored-by: huzekang <10...@qq.com>
    Co-authored-by: JinyLeeChina <42...@users.noreply.github.com>
    Co-authored-by: Eights-LI <ye...@gmail.com>
    Co-authored-by: wuchunfu <31...@qq.com>
    Co-authored-by: qiaozhanwei <qi...@analysys.com.cn>
    Co-authored-by: Yelli <am...@my.com>
    Co-authored-by: Eights-Li <ye...@gmail.com>
    Co-authored-by: qiaozhanwei <qi...@outlook.com>
    Co-authored-by: XiaotaoYi <v-...@hotmail.com>
    Co-authored-by: zhuangchong <zh...@163.com>
    Co-authored-by: muzhongjiang <mu...@163.com>
    Co-authored-by: Jave-Chen <ba...@gmail.com>
    Co-authored-by: hailin0 <ha...@yeah.net>
    Co-authored-by: yangquan <iy...@qq.com>
    Co-authored-by: liliang1991 <li...@126.com>
    Co-authored-by: liang.li.c <li...@17zuoye.com>
    Co-authored-by: felix.wang <59...@users.noreply.github.com>
    Co-authored-by: chengshiwen <ch...@gmail.com>
    Co-authored-by: Yarlung <tu...@163.com>
    Co-authored-by: karlsun <ka...@tencent.com>
    Co-authored-by: wangxj3 <85...@qq.com>
    Co-authored-by: chengp <50...@qq.com>
    Co-authored-by: chengp <ch...@chengp.net>
    Co-authored-by: Saksham Gupta <sa...@gmail.com>
    Co-authored-by: Tq <36...@users.noreply.github.com>
    Co-authored-by: Jatham <59...@users.noreply.github.com>
    Co-authored-by: hailin0 <ha...@foxmail.com>
    Co-authored-by: zh0122 <zh...@gmail.com>
    Co-authored-by: geosmart <ge...@hotmail.com>
    Co-authored-by: 李丛阳 <li...@163.com>
    Co-authored-by: 0002939 <li...@mininglamp.com>
    Co-authored-by: kamisamak <10...@qq.com>
---
 LICENSE                                            |   4 +
 .../dolphinscheduler/requirements.yaml~HEAD        |  15 +-
 .../dolphinscheduler/requirements.yaml~dev         |  15 +-
 .../dolphinscheduler-alert-dingtalk}/pom.xml       |  97 +--
 .../alert/dingtalk/DingTalkAlertChannel.java       |  24 +-
 .../dingtalk/DingTalkAlertChannelFactory.java      |  89 +++
 .../plugin/alert/dingtalk/DingTalkAlertPlugin.java |  24 +-
 .../alert/dingtalk/DingTalkParamsConstants.java    |  57 ++
 .../plugin/alert/dingtalk/DingTalkSender.java      | 204 ++++++
 .../dingtalk/DingTalkAlertChannelFactoryTest.java  |  48 ++
 .../plugin/alert/dingtalk/DingTalkSenderTest.java  |  57 ++
 .../dolphinscheduler-alert-email}/pom.xml          | 111 +--
 .../plugin/alert/email/EmailAlertChannel.java      |  69 ++
 .../alert/email/EmailAlertChannelFactory.java      | 137 ++++
 .../plugin/alert/email/EmailAlertPlugin.java       |  25 +-
 .../plugin/alert/email/EmailConstants.java         |  75 ++
 .../plugin/alert/email}/ExcelUtils.java            |  24 +-
 .../plugin/alert/email/MailParamsConstants.java    |  65 ++
 .../plugin/alert/email/MailSender.java             | 429 ++++++++++++
 .../alert/email}/template/AlertTemplate.java       |  21 +-
 .../alert/email/template}/DefaultHTMLTemplate.java |  53 +-
 .../alert/email/EmailAlertChannelFactoryTest.java  |  75 ++
 .../plugin/alert/email/EmailAlertChannelTest.java  | 161 +++++
 .../plugin/alert/email}/ExcelUtilsTest.java        |  10 +-
 .../plugin/alert/email/MailUtilsTest.java          | 136 ++++
 .../email/template/DefaultHTMLTemplateTest.java    | 105 +++
 .../dolphinscheduler-alert-http}/pom.xml           |  95 +--
 .../plugin/alert/http/HttpAlertChannel.java        |  24 +-
 .../plugin/alert/http/HttpAlertChannelFactory.java |  78 +++
 .../plugin/alert/http/HttpAlertConstants.java      |  22 +-
 .../plugin/alert/http/HttpAlertPlugin.java         |  24 +-
 .../plugin/alert/http/HttpSender.java              | 166 +++++
 .../alert/http/HttpAlertChannelFactoryTest.java    |  46 +-
 .../plugin/alert/http/HttpAlertChannelTest.java    | 104 +++
 .../plugin/alert/http/HttpAlertPluginTest.java     |  33 +-
 .../plugin/alert/http/HttpSenderTest.java          |  50 +-
 .../dolphinscheduler-alert-script}/pom.xml         |  96 +--
 .../plugin/alert/script/OSUtils.java               |  19 +-
 .../plugin/alert/script/ProcessUtils.java          |  62 ++
 .../plugin/alert/script/ScriptAlertChannel.java    |  37 +-
 .../alert/script/ScriptAlertChannelFactory.java    |  71 ++
 .../plugin/alert/script/ScriptAlertPlugin.java     |  23 +-
 .../plugin/alert/script/ScriptParamsConstants.java |  33 +-
 .../plugin/alert/script/ScriptSender.java          |  74 ++
 .../plugin/alert/script/ScriptType.java            |  49 +-
 .../plugin/alert/script/StreamGobbler.java         |  53 +-
 .../plugin/alert/script/ProcessUtilsTest.java      |  32 +-
 .../script/ScriptAlertChannelFactoryTest.java      |  48 ++
 .../plugin/alert/script/ScriptSenderTest.java      |  58 ++
 .../src/test/script/shell/example.sh               |  13 +-
 .../src/test/script/shell/scriptTest.sh            |  13 +-
 .../dolphinscheduler-alert-wechat/pom.xml          |  78 +++
 .../plugin/alert/wechat/WeChatAlertChannel.java    |  24 +-
 .../alert/wechat/WeChatAlertChannelFactory.java    |  94 +++
 .../plugin/alert/wechat/WeChatAlertConstants.java  |  29 +-
 .../alert/wechat/WeChatAlertParamsConstants.java   |  56 ++
 .../plugin/alert/wechat/WeChatAlertPlugin.java     |  24 +-
 .../plugin/alert/wechat/WeChatSender.java          | 335 +++++++++
 .../wechat/WeChatAlertChannelFactoryTest.java      |  48 ++
 .../plugin/alert/wechat/WeChatSenderTest.java      |  89 +++
 .../pom.xml                                        |  47 +-
 dolphinscheduler-alert/pom.xml                     |  79 ++-
 .../apache/dolphinscheduler/alert/AlertServer.java | 126 +++-
 .../alert/manager/EmailManager.java                | 110 +--
 .../alert/manager/EnterpriseWeChatManager.java     |  59 --
 .../dolphinscheduler/alert/manager/MsgManager.java |  10 +-
 .../alert/plugin/AbstractDolphinPluginManager.java |  18 +-
 .../alert/plugin/AlertPluginManager.java           |  99 +++
 .../alert/plugin/DolphinPluginClassLoader.java     | 139 ++++
 .../alert/plugin/DolphinPluginDiscovery.java       | 139 ++++
 .../alert/plugin/DolphinPluginLoader.java          | 194 ++++++
 .../alert/plugin/DolphinPluginManagerConfig.java   | 121 ++++
 .../alert/plugin/EmailAlertPlugin.java             | 147 ----
 .../alert/processor/AlertRequestProcessor.java     |  67 ++
 .../dolphinscheduler/alert/runner/AlertSender.java | 174 +++--
 .../dolphinscheduler/alert/utils/Constants.java    | 172 +----
 .../alert/utils/DingTalkUtils.java                 | 141 ----
 .../alert/utils/EnterpriseWeChatUtils.java         | 286 --------
 .../dolphinscheduler/alert/utils/MailUtils.java    | 375 ----------
 .../alert/utils/PropertyUtils.java                 |   2 +-
 .../src/main/resources/alert.properties            |  49 +-
 .../dolphinscheduler/alert/AlertServerTest.java    |  91 +++
 .../alert/plugin/AlertPluginManagerTest.java       |  65 ++
 .../alert/plugin/DolphinPluginLoaderTest.java      |  60 ++
 .../alert/plugin/EmailAlertPluginTest.java         | 253 +++++--
 .../alert/processor/AlertRequestProcessorTest.java |  61 ++
 .../alert/runner/AlertSenderTest.java              | 181 +++++
 .../alert/template/AlertTemplateFactoryTest.java   |  65 --
 .../template/impl/DefaultHTMLTemplateTest.java     |  96 ---
 .../alert/utils/DingTalkUtilsTest.java             | 120 ----
 .../alert/utils/EnterpriseWeChatUtilsTest.java     | 283 --------
 .../alert/utils/FuncUtilsTest.java                 |  10 +-
 .../alert/utils/MailUtilsTest.java                 | 190 ------
 .../api/controller/AlertGroupController.java       | 113 ++-
 .../controller/AlertPluginInstanceController.java  | 240 +++++++
 .../api/controller/ExecutorController.java         | 113 ++-
 .../api/controller/SchedulerController.java        | 132 ++--
 .../api/controller/UiPluginController.java         |  93 +++
 .../apache/dolphinscheduler/api/enums/Status.java  |  17 +-
 .../api/service/AlertGroupService.java             |  84 +--
 .../api/service/AlertPluginInstanceService.java    |  89 +++
 .../api/service/ExecutorService.java               |  47 +-
 .../api/service/ProcessInstanceService.java        |   3 +-
 .../api/service/SchedulerService.java              |  97 ++-
 .../api/service/UiPluginService.java               |  14 +-
 .../dolphinscheduler/api/service/UsersService.java |  10 +-
 .../impl/AlertPluginInstanceServiceImpl.java       | 208 ++++++
 .../service/impl/ProcessDefinitionServiceImpl.java |   7 +-
 .../impl/ProcessDefinitionVersionServiceImpl.java  |  23 +-
 .../api/service/impl/UiPluginServiceImpl.java      |  85 +++
 .../api/vo/AlertPluginInstanceVO.java              | 117 ++++
 .../src/main/resources/i18n/messages.properties    |   5 +
 .../main/resources/i18n/messages_en_US.properties  |   5 +
 .../api/controller/ExecutorControllerTest.java     | 100 ++-
 .../api/controller/TaskInstanceControllerTest.java |   1 +
 .../api/security/SecurityConfigLDAPTest.java~HEAD} |  35 +-
 .../api/security/SecurityConfigLDAPTest.java~dev}  |  35 +-
 .../api/service/AlertGroupServiceTest.java         | 129 ++--
 .../api/service/ExecutorService2Test.java          | 147 ++--
 .../ProcessDefinitionVersionServiceTest.java       |  23 +-
 .../api/service/ProcessInstanceServiceTest.java    |   2 -
 .../api/service/UiPluginServiceTest.java           |  86 +++
 .../api/service/UserAlertGroupServiceTest.java     |  53 --
 dolphinscheduler-common/pom.xml                    |   5 -
 .../apache/dolphinscheduler/common/Constants.java  |   8 +-
 .../enums/{ShowType.java => PluginType.java}       |  55 +-
 .../common/plugin/FilePluginManager.java           | 107 ---
 .../common/plugin/PluginClassLoader.java           | 154 -----
 .../common/task/sql/SqlParameters.java             |  67 +-
 .../common/plugin/FilePluginManagerTest.java       |  72 --
 .../common/plugin/PluginClassLoaderTest.java       |  61 --
 .../org/apache/dolphinscheduler/dao/AlertDao.java  | 102 +--
 .../org/apache/dolphinscheduler/dao/PluginDao.java |  96 +++
 .../apache/dolphinscheduler/dao/entity/Alert.java  | 121 +---
 .../dolphinscheduler/dao/entity/AlertGroup.java    |  71 +-
 ...serAlertGroup.java => AlertPluginInstance.java} | 104 ++-
 .../{UserAlertGroup.java => PluginDefine.java}     | 107 ++-
 .../dao/entity/ProcessDefinition.java              |  93 ++-
 .../dao/entity/ProcessDefinitionVersion.java       |  88 +--
 .../dao/entity/ProcessInstance.java                |  36 +-
 .../dao/mapper/AlertGroupMapper.java               |  13 +-
 ...pMapper.java => AlertPluginInstanceMapper.java} |  32 +-
 ...ertGroupMapper.java => PluginDefineMapper.java} |  46 +-
 .../src/main/resources/datasource.properties       |   8 +-
 .../dao/mapper/AlertGroupMapper.xml                |  19 +-
 .../dolphinscheduler/dao/mapper/AlertMapper.xml    |   5 +-
 ...oupMapper.xml => AlertPluginInstanceMapper.xml} |  44 +-
 .../dao/mapper/PluginDefineMapper.xml              |  45 ++
 .../dao/mapper/ProcessDefinitionMapper.xml         |  13 +-
 .../dao/mapper/ProcessDefinitionVersionMapper.xml  |   5 +-
 .../dolphinscheduler/dao/mapper/ProjectMapper.xml  |   1 +
 .../apache/dolphinscheduler/dao/AlertDaoTest.java  |  21 +-
 .../dao/mapper/AlertGroupMapperTest.java           | 189 ++---
 .../dao/mapper/AlertMapperTest.java                |  67 +-
 .../dao/mapper/AlertPluginInstanceMapperTest.java  | 112 +++
 .../dao/mapper/PluginDefineTest.java               |  77 +++
 .../dao/mapper/ProcessDefinitionMapperTest.java    |   4 -
 .../mapper/ProcessDefinitionVersionMapperTest.java |   4 -
 .../dao/mapper/UserAlertGroupMapperTest.java       | 196 ------
 .../dao/mapper/UserMapperTest.java                 | 122 +---
 dolphinscheduler-dist/pom.xml                      |   2 +-
 dolphinscheduler-dist/release-docs/LICENSE         |  12 +-
 dolphinscheduler-dist/release-docs/NOTICE          |  23 -
 .../release-docs/licenses/LICENSE-aether-api.txt   |  86 +++
 .../LICENSE-animal-sniffer-annotations.txt         |  21 +
 .../licenses/LICENSE-checker-compat-qual.txt       |  22 +
 .../licenses/LICENSE-j2objc-annotations.txt        |  21 +-
 .../release-docs/licenses/LICENSE-javax.mail.txt   | 759 ---------------------
 .../release-docs/licenses/LICENSE-resolver.txt     |  18 +-
 .../ui-licenses/LICENSE-@form-create-element-ui    |  21 +
 .../src/main/provisio/dolphinscheduler.xml         |  67 ++
 .../plugin/utils/PropertyUtils.java                | 190 ------
 .../plugin/model/AlertDataTest.java                |  80 ---
 .../plugin/utils/PropertyUtilsTest.java            |  83 ---
 .../remote/command/CommandType.java                |  12 +-
 .../command/alert/AlertSendRequestCommand.java     |  80 +++
 .../command/alert/AlertSendResponseCommand.java    |  75 ++
 .../command/alert/AlertSendResponseResult.java     |  41 +-
 .../remote/utils/JsonSerializer.java               |  96 +++
 .../command/alert/AlertSendRequestCommandTest.java |  48 +-
 .../alert/AlertSendResponseCommandTest.java        |  48 ++
 dolphinscheduler-server/pom.xml                    | 325 ++++-----
 .../server/log/TaskLogAppender.java                |   8 +-
 .../master/runner/MasterBaseTaskExecThread.java    |  64 +-
 .../server/master/runner/MasterTaskExecThread.java |  59 +-
 .../server/utils/AlertManager.java                 |  14 +-
 .../server/worker/WorkerServer.java                |  23 +-
 .../server/worker/config/WorkerConfig.java         |  19 +-
 .../worker/processor/TaskExecuteProcessor.java     |  18 +-
 .../server/worker/runner/TaskExecuteThread.java    |  11 +-
 .../server/worker/task/TaskManager.java            |   6 +-
 .../server/worker/task/sql/SqlTask.java            | 103 ++-
 .../src/main/resources/worker.properties           |   3 +
 .../server/registry/DependencyConfig.java          |  84 ++-
 .../processor/TaskCallbackServiceTestConfig.java   |  71 +-
 .../worker/runner/TaskExecuteThreadTest.java       |  17 +-
 .../server/worker/task/TaskManagerTest.java        |  27 +-
 .../server/worker/task/sql/SqlTaskTest.java        | 111 +++
 .../service/alert/AlertClientService.java          | 120 ++++
 .../service/process/ProcessService.java            |   5 +-
 .../service/alert/AlertClientServiceTest.java      | 152 +++++
 .../pom.xml                                        |  36 +-
 .../spi/DolphinSchedulerPlugin.java                |  38 ++
 .../dolphinscheduler/spi/alert/AlertChannel.java   |  17 +-
 .../spi/alert/AlertChannelFactory.java             |  37 +-
 .../dolphinscheduler/spi/alert/AlertConstants.java |  17 +-
 .../dolphinscheduler/spi/alert}/AlertData.java     |  56 +-
 .../dolphinscheduler/spi/alert/AlertInfo.java      |  37 +-
 .../dolphinscheduler/spi/alert/AlertResult.java    |  30 +-
 .../dolphinscheduler/spi/alert}/ShowType.java      |   8 +-
 .../spi/classloader/ThreadContextClassLoader.java  |  27 +-
 .../dolphinscheduler/spi/params/InputParam.java    |  94 +++
 .../dolphinscheduler/spi/params/PasswordParam.java |  87 +++
 .../spi/params/PluginParamsTransfer.java           |  51 ++
 .../dolphinscheduler/spi/params/RadioParam.java    | 106 +++
 .../dolphinscheduler/spi/params/base/DataType.java |  27 +-
 .../dolphinscheduler/spi/params/base/FormType.java |  25 +-
 .../spi/params/base/ParamsOptions.java             |  72 ++
 .../spi/params/base/ParamsProps.java               |  33 +-
 .../spi/params/base/PluginParams.java              | 170 +++++
 .../spi/params/base/PropsType.java                 |  24 +-
 .../spi/params/base/TriggerType.java               |  23 +-
 .../dolphinscheduler/spi/params/base/Validate.java | 137 ++++
 .../dolphinscheduler/spi/utils/JSONUtils.java      | 156 +++++
 .../dolphinscheduler/spi/utils/StringUtils.java    |  32 +-
 .../spi/params/PluginParamsTransferTest.java       | 201 ++++++
 install.sh                                         |   2 +
 pom.xml                                            | 124 +++-
 sql/dolphinscheduler-postgre.sql                   |  95 +--
 sql/dolphinscheduler_mysql.sql                     |  91 +--
 .../1.4.0_schema/mysql/dolphinscheduler_ddl.sql    |  69 ++
 .../1.4.0_schema/mysql/dolphinscheduler_dml.sql    |  21 +-
 .../postgresql/dolphinscheduler_ddl.sql            |  65 ++
 .../postgresql/dolphinscheduler_dml.sql            |  19 +-
 style/checkstyle.xml                               |   8 +-
 style/intellij-java-code-style.xml                 |   2 -
 tools/dependencies/known-dependencies.txt          |  13 +-
 237 files changed, 10874 insertions(+), 6963 deletions(-)

diff --git a/LICENSE b/LICENSE
index 9a0c6aa..5d7c9f4 100644
--- a/LICENSE
+++ b/LICENSE
@@ -216,3 +216,7 @@ The text of each license is the standard Apache 2.0 license.
     ScriptRunner from https://github.com/mybatis/mybatis-3 Apache 2.0
     mvnw files from https://github.com/takari/maven-wrapper Apache 2.0
     PropertyPlaceholderHelper from https://github.com/spring-projects/spring-framework Apache 2.0
+    DolphinPluginClassLoader from https://github.com/prestosql/presto Apache 2.0
+    DolphinPluginDiscovery from https://github.com/prestosql/presto Apache 2.0
+    DolphinPluginLoader from https://github.com/prestosql/presto Apache 2.0
+
diff --git a/dolphinscheduler-plugin-api/src/test/resources/plugin.properties b/docker/kubernetes/dolphinscheduler/requirements.yaml~HEAD
similarity index 76%
copy from dolphinscheduler-plugin-api/src/test/resources/plugin.properties
copy to docker/kubernetes/dolphinscheduler/requirements.yaml~HEAD
index d2ea383..e219975 100644
--- a/dolphinscheduler-plugin-api/src/test/resources/plugin.properties
+++ b/docker/kubernetes/dolphinscheduler/requirements.yaml~HEAD
@@ -14,9 +14,12 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 #
-
-test.string=teststring
-test.false=false
-test.true=true
-cba=3.1
-test.long=100
\ No newline at end of file
+dependencies:
+- name: postgresql
+  version: 8.x.x
+  repository: https://charts.bitnami.com/bitnami
+  condition: postgresql.enabled
+- name: zookeeper
+  version: 5.x.x
+  repository: https://charts.bitnami.com/bitnami
+  condition: redis.enabled
\ No newline at end of file
diff --git a/dolphinscheduler-plugin-api/src/test/resources/plugin.properties b/docker/kubernetes/dolphinscheduler/requirements.yaml~dev
similarity index 76%
copy from dolphinscheduler-plugin-api/src/test/resources/plugin.properties
copy to docker/kubernetes/dolphinscheduler/requirements.yaml~dev
index d2ea383..e219975 100644
--- a/dolphinscheduler-plugin-api/src/test/resources/plugin.properties
+++ b/docker/kubernetes/dolphinscheduler/requirements.yaml~dev
@@ -14,9 +14,12 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 #
-
-test.string=teststring
-test.false=false
-test.true=true
-cba=3.1
-test.long=100
\ No newline at end of file
+dependencies:
+- name: postgresql
+  version: 8.x.x
+  repository: https://charts.bitnami.com/bitnami
+  condition: postgresql.enabled
+- name: zookeeper
+  version: 5.x.x
+  repository: https://charts.bitnami.com/bitnami
+  condition: redis.enabled
\ No newline at end of file
diff --git a/dolphinscheduler-alert/pom.xml b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-dingtalk/pom.xml
similarity index 53%
copy from dolphinscheduler-alert/pom.xml
copy to dolphinscheduler-alert-plugin/dolphinscheduler-alert-dingtalk/pom.xml
index a44d101..930b92f 100644
--- a/dolphinscheduler-alert/pom.xml
+++ b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-dingtalk/pom.xml
@@ -15,65 +15,39 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-    <modelVersion>4.0.0</modelVersion>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     <parent>
+        <artifactId>dolphinscheduler-alert-plugin</artifactId>
         <groupId>org.apache.dolphinscheduler</groupId>
-        <artifactId>dolphinscheduler</artifactId>
         <version>1.3.4-SNAPSHOT</version>
     </parent>
-    <artifactId>dolphinscheduler-alert</artifactId>
-    <name>${project.artifactId}</name>
-    <packaging>jar</packaging>
+    <modelVersion>4.0.0</modelVersion>
 
-    <properties>
-        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
-    </properties>
-    <dependencies>
-        <dependency>
-            <groupId>junit</groupId>
-            <artifactId>junit</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.mockito</groupId>
-            <artifactId>mockito-core</artifactId>
-            <type>jar</type>
-            <scope>test</scope>
-        </dependency>
+    <groupId>org.apache.dolphinscheduler</groupId>
+    <artifactId>dolphinscheduler-alert-dingtalk</artifactId>
+    <packaging>dolphinscheduler-plugin</packaging>
 
-        <dependency>
-            <groupId>org.powermock</groupId>
-            <artifactId>powermock-module-junit4</artifactId>
-            <scope>test</scope>
-        </dependency>
+    <dependencies>
 
         <dependency>
-            <groupId>org.powermock</groupId>
-            <artifactId>powermock-api-mockito2</artifactId>
-            <scope>test</scope>
-            <exclusions>
-                <exclusion>
-                    <groupId>org.mockito</groupId>
-                    <artifactId>mockito-core</artifactId>
-                </exclusion>
-            </exclusions>
+            <groupId>org.apache.dolphinscheduler</groupId>
+            <artifactId>dolphinscheduler-spi</artifactId>
+            <scope>provided</scope>
         </dependency>
-
         <dependency>
-            <groupId>org.apache.commons</groupId>
-            <artifactId>commons-email</artifactId>
+            <groupId>org.apache.httpcomponents</groupId>
+            <artifactId>httpclient</artifactId>
         </dependency>
-
         <dependency>
-            <groupId>com.fasterxml.jackson.core</groupId>
-            <artifactId>jackson-core</artifactId>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
         </dependency>
 
         <dependency>
-            <groupId>com.fasterxml.jackson.core</groupId>
-            <artifactId>jackson-databind</artifactId>
+            <groupId>ch.qos.logback</groupId>
+            <artifactId>logback-classic</artifactId>
         </dependency>
 
         <dependency>
@@ -82,32 +56,27 @@
         </dependency>
 
         <dependency>
-            <groupId>com.google.guava</groupId>
-            <artifactId>guava</artifactId>
-        </dependency>
-
-        <dependency>
-            <groupId>ch.qos.logback</groupId>
-            <artifactId>logback-classic</artifactId>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-annotations</artifactId>
+            <scope>provided</scope>
         </dependency>
 
-        <!--excel poi-->
         <dependency>
-            <groupId>org.apache.poi</groupId>
-            <artifactId>poi</artifactId>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
         </dependency>
 
         <dependency>
-            <groupId>org.apache.dolphinscheduler</groupId>
-            <artifactId>dolphinscheduler-dao</artifactId>
-            <exclusions>
-                <exclusion>
-                    <artifactId>log4j-api</artifactId>
-                    <groupId>org.apache.logging.log4j</groupId>
-                </exclusion>
-            </exclusions>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-core</artifactId>
+            <type>jar</type>
+            <scope>test</scope>
         </dependency>
-
     </dependencies>
 
-</project>
+    <build>
+        <finalName>dolphinscheduler-alert-dingtalk-${project.version}</finalName>
+    </build>
+
+</project>
\ No newline at end of file
diff --git a/dolphinscheduler-common/src/main/java/org/apache/dolphinscheduler/common/plugin/PluginManager.java b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-dingtalk/src/main/java/org/apache/dolphinscheduler/plugin/alert/dingtalk/DingTalkAlertChannel.java
similarity index 50%
copy from dolphinscheduler-common/src/main/java/org/apache/dolphinscheduler/common/plugin/PluginManager.java
copy to dolphinscheduler-alert-plugin/dolphinscheduler-alert-dingtalk/src/main/java/org/apache/dolphinscheduler/plugin/alert/dingtalk/DingTalkAlertChannel.java
index f807884..7adfacc 100644
--- a/dolphinscheduler-common/src/main/java/org/apache/dolphinscheduler/common/plugin/PluginManager.java
+++ b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-dingtalk/src/main/java/org/apache/dolphinscheduler/plugin/alert/dingtalk/DingTalkAlertChannel.java
@@ -14,20 +14,28 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dolphinscheduler.common.plugin;
 
-import org.apache.dolphinscheduler.plugin.api.AlertPlugin;
+package org.apache.dolphinscheduler.plugin.alert.dingtalk;
+
+import org.apache.dolphinscheduler.spi.alert.AlertChannel;
+import org.apache.dolphinscheduler.spi.alert.AlertData;
+import org.apache.dolphinscheduler.spi.alert.AlertInfo;
+import org.apache.dolphinscheduler.spi.alert.AlertResult;
+import org.apache.dolphinscheduler.spi.params.PluginParamsTransfer;
 
 import java.util.Map;
 
 /**
- * PluginManager
+ * DingTalkAlertChannel
  */
-public interface PluginManager {
-
-    AlertPlugin findOne(String name);
+public class DingTalkAlertChannel implements AlertChannel {
 
-    Map<String, AlertPlugin> findAll();
+    @Override
+    public AlertResult process(AlertInfo alertInfo) {
 
-    void addPlugin(AlertPlugin plugin);
+        AlertData alertData = alertInfo.getAlertData();
+        String alertParams = alertInfo.getAlertParams();
+        Map<String, String> paramsMap = PluginParamsTransfer.getPluginParamsMap(alertParams);
+        return new DingTalkSender(paramsMap).sendDingTalkMsg(alertData.getTitle(), alertData.getContent());
+    }
 }
diff --git a/dolphinscheduler-alert-plugin/dolphinscheduler-alert-dingtalk/src/main/java/org/apache/dolphinscheduler/plugin/alert/dingtalk/DingTalkAlertChannelFactory.java b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-dingtalk/src/main/java/org/apache/dolphinscheduler/plugin/alert/dingtalk/DingTalkAlertChannelFactory.java
new file mode 100644
index 0000000..0a6851a
--- /dev/null
+++ b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-dingtalk/src/main/java/org/apache/dolphinscheduler/plugin/alert/dingtalk/DingTalkAlertChannelFactory.java
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.dolphinscheduler.plugin.alert.dingtalk;
+
+import org.apache.dolphinscheduler.spi.alert.AlertChannel;
+import org.apache.dolphinscheduler.spi.alert.AlertChannelFactory;
+import org.apache.dolphinscheduler.spi.params.InputParam;
+import org.apache.dolphinscheduler.spi.params.PasswordParam;
+import org.apache.dolphinscheduler.spi.params.RadioParam;
+import org.apache.dolphinscheduler.spi.params.base.ParamsOptions;
+import org.apache.dolphinscheduler.spi.params.base.PluginParams;
+import org.apache.dolphinscheduler.spi.params.base.Validate;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * DingTalkAlertChannelFactory
+ */
+public class DingTalkAlertChannelFactory implements AlertChannelFactory {
+    @Override
+    public String getName() {
+        return "DingTalk";
+    }
+
+    @Override
+    public List<PluginParams> getParams() {
+        InputParam webHookParam = InputParam.newBuilder(DingTalkParamsConstants.NAME_DING_TALK_WEB_HOOK, DingTalkParamsConstants.DING_TALK_WEB_HOOK)
+            .addValidate(Validate.newBuilder()
+                .setRequired(true)
+                .build())
+            .build();
+        InputParam keywordParam = InputParam.newBuilder(DingTalkParamsConstants.NAME_DING_TALK_KEYWORD, DingTalkParamsConstants.DING_TALK_KEYWORD)
+            .addValidate(Validate.newBuilder()
+                .setRequired(true)
+                .build())
+            .build();
+        RadioParam isEnableProxy =
+            RadioParam.newBuilder(DingTalkParamsConstants.NAME_DING_TALK_PROXY_ENABLE, DingTalkParamsConstants.NAME_DING_TALK_PROXY_ENABLE)
+                    .addParamsOptions(new ParamsOptions("YES", true, false))
+                    .addParamsOptions(new ParamsOptions("NO", false, false))
+                .setValue(true)
+                .addValidate(Validate.newBuilder()
+                    .setRequired(false)
+                    .build())
+                .build();
+        InputParam proxyParam =
+            InputParam.newBuilder(DingTalkParamsConstants.NAME_DING_TALK_PROXY, DingTalkParamsConstants.DING_TALK_PROXY)
+                .addValidate(Validate.newBuilder()
+                    .setRequired(false).build())
+                .build();
+
+        InputParam portParam = InputParam.newBuilder(DingTalkParamsConstants.NAME_DING_TALK_PORT, DingTalkParamsConstants.DING_TALK_PORT)
+            .addValidate(Validate.newBuilder()
+                .setRequired(false).build())
+            .build();
+
+        InputParam userParam =
+            InputParam.newBuilder(DingTalkParamsConstants.NAME_DING_TALK_USER, DingTalkParamsConstants.DING_TALK_USER)
+                .addValidate(Validate.newBuilder()
+                    .setRequired(false).build())
+                .build();
+        PasswordParam passwordParam = PasswordParam.newBuilder(DingTalkParamsConstants.NAME_DING_TALK_PASSWORD, DingTalkParamsConstants.DING_TALK_PASSWORD)
+            .setPlaceholder("if enable use authentication, you need input password")
+            .build();
+
+        return Arrays.asList(webHookParam, keywordParam, isEnableProxy, proxyParam, portParam, userParam, passwordParam);
+    }
+
+    @Override
+    public AlertChannel create() {
+        return new DingTalkAlertChannel();
+    }
+}
diff --git a/dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/template/AlertTemplateFactory.java b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-dingtalk/src/main/java/org/apache/dolphinscheduler/plugin/alert/dingtalk/DingTalkAlertPlugin.java
similarity index 63%
copy from dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/template/AlertTemplateFactory.java
copy to dolphinscheduler-alert-plugin/dolphinscheduler-alert-dingtalk/src/main/java/org/apache/dolphinscheduler/plugin/alert/dingtalk/DingTalkAlertPlugin.java
index d384631..2d15f49 100644
--- a/dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/template/AlertTemplateFactory.java
+++ b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-dingtalk/src/main/java/org/apache/dolphinscheduler/plugin/alert/dingtalk/DingTalkAlertPlugin.java
@@ -15,24 +15,20 @@
  * limitations under the License.
  */
 
-package org.apache.dolphinscheduler.alert.template;
+package org.apache.dolphinscheduler.plugin.alert.dingtalk;
 
-import org.apache.dolphinscheduler.alert.template.impl.DefaultHTMLTemplate;
+import org.apache.dolphinscheduler.spi.DolphinSchedulerPlugin;
+import org.apache.dolphinscheduler.spi.alert.AlertChannelFactory;
+
+import com.google.common.collect.ImmutableList;
 
 /**
- * the alert template factory
+ * DingTalkAlertPlugin
  */
-public class AlertTemplateFactory {
-
-    private AlertTemplateFactory() {
-    }
+public class DingTalkAlertPlugin implements DolphinSchedulerPlugin {
 
-    /**
-     * get a template from alert.properties conf file
-     *
-     * @return a template, default is DefaultHTMLTemplate
-     */
-    public static AlertTemplate getMessageTemplate() {
-        return new DefaultHTMLTemplate();
+    @Override
+    public Iterable<AlertChannelFactory> getAlertChannelFactorys() {
+        return ImmutableList.of(new DingTalkAlertChannelFactory());
     }
 }
diff --git a/dolphinscheduler-alert-plugin/dolphinscheduler-alert-dingtalk/src/main/java/org/apache/dolphinscheduler/plugin/alert/dingtalk/DingTalkParamsConstants.java b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-dingtalk/src/main/java/org/apache/dolphinscheduler/plugin/alert/dingtalk/DingTalkParamsConstants.java
new file mode 100644
index 0000000..fb9f5f9
--- /dev/null
+++ b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-dingtalk/src/main/java/org/apache/dolphinscheduler/plugin/alert/dingtalk/DingTalkParamsConstants.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.dolphinscheduler.plugin.alert.dingtalk;
+
+/**
+ * DingTalkParamsConstants
+ */
+public class DingTalkParamsConstants {
+
+    public DingTalkParamsConstants() {
+        throw new IllegalStateException("Utility class");
+    }
+
+    static final String DING_TALK_WEB_HOOK = "dingtalk.webhook";
+
+    static final String NAME_DING_TALK_WEB_HOOK = "dingTalkWebHook";
+
+    static final String DING_TALK_KEYWORD = "dingtalk.keyword";
+
+    static final String NAME_DING_TALK_KEYWORD = "dingTalkKeyword";
+
+    public static final String DING_TALK_PROXY_ENABLE = "dingtalk.isEnableProxy";
+
+    static final String NAME_DING_TALK_PROXY_ENABLE = "dingTalkIsEnableProxy";
+
+    static final String DING_TALK_PROXY = "dingtalk.proxy";
+
+    static final String NAME_DING_TALK_PROXY = "dingTalkProxy";
+
+    static final String DING_TALK_PORT = "dingtalk.port";
+
+    static final String NAME_DING_TALK_PORT = "dingTalkPort";
+
+    static final String DING_TALK_USER = "dingtalk.user";
+
+    static final String NAME_DING_TALK_USER = "dingTalkUser";
+
+    static final String DING_TALK_PASSWORD = "dingtalk.password";
+
+    static final String NAME_DING_TALK_PASSWORD = "dingTalkPassword";
+
+}
diff --git a/dolphinscheduler-alert-plugin/dolphinscheduler-alert-dingtalk/src/main/java/org/apache/dolphinscheduler/plugin/alert/dingtalk/DingTalkSender.java b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-dingtalk/src/main/java/org/apache/dolphinscheduler/plugin/alert/dingtalk/DingTalkSender.java
new file mode 100644
index 0000000..4500f3e
--- /dev/null
+++ b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-dingtalk/src/main/java/org/apache/dolphinscheduler/plugin/alert/dingtalk/DingTalkSender.java
@@ -0,0 +1,204 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.dolphinscheduler.plugin.alert.dingtalk;
+
+import org.apache.dolphinscheduler.spi.alert.AlertResult;
+import org.apache.dolphinscheduler.spi.utils.JSONUtils;
+
+import org.apache.commons.codec.binary.StringUtils;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpHost;
+import org.apache.http.auth.AuthScope;
+import org.apache.http.auth.UsernamePasswordCredentials;
+import org.apache.http.client.CredentialsProvider;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.BasicCredentialsProvider;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.util.EntityUtils;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Ding Talk Sender
+ */
+public class DingTalkSender {
+    private static final Logger logger = LoggerFactory.getLogger(DingTalkSender.class);
+
+    private String url;
+
+    private String keyword;
+
+    private Boolean enableProxy;
+
+    private String proxy;
+
+    private Integer port;
+
+    private String user;
+
+    private String password;
+
+    DingTalkSender(Map<String, String> config) {
+        url = config.get(DingTalkParamsConstants.NAME_DING_TALK_WEB_HOOK);
+        keyword = config.get(DingTalkParamsConstants.NAME_DING_TALK_KEYWORD);
+        enableProxy = Boolean.valueOf(config.get(DingTalkParamsConstants.NAME_DING_TALK_PROXY_ENABLE));
+        if (Boolean.TRUE.equals(enableProxy)) {
+            port = Integer.parseInt(config.get(DingTalkParamsConstants.NAME_DING_TALK_PORT));
+            proxy = config.get(DingTalkParamsConstants.NAME_DING_TALK_PROXY);
+            user = config.get(DingTalkParamsConstants.DING_TALK_USER);
+            password = config.get(DingTalkParamsConstants.NAME_DING_TALK_PASSWORD);
+        }
+
+    }
+
+    public AlertResult sendDingTalkMsg(String msg, String charset) {
+        AlertResult alertResult;
+        try {
+            String resp = sendMsg(msg, charset);
+            return checkSendDingTalkSendMsgResult(resp);
+        } catch (Exception e) {
+            logger.info("send ding talk alert msg  exception : {}", e.getMessage());
+            alertResult = new AlertResult();
+            alertResult.setStatus("false");
+            alertResult.setMessage("send ding talk alert fail.");
+        }
+        return alertResult;
+    }
+
+    private String sendMsg(String msg, String charset) throws IOException {
+
+        String msgToJson = textToJsonString(msg + "#" + keyword);
+        HttpPost httpPost = constructHttpPost(url, msgToJson, charset);
+
+        CloseableHttpClient httpClient;
+        if (Boolean.TRUE.equals(enableProxy)) {
+            httpClient = getProxyClient(proxy, port, user, password);
+            RequestConfig rcf = getProxyConfig(proxy, port);
+            httpPost.setConfig(rcf);
+        } else {
+            httpClient = getDefaultClient();
+        }
+
+        try {
+            CloseableHttpResponse response = httpClient.execute(httpPost);
+            String resp;
+            try {
+                HttpEntity entity = response.getEntity();
+                resp = EntityUtils.toString(entity, charset);
+                EntityUtils.consume(entity);
+            } finally {
+                response.close();
+            }
+            logger.info("Ding Talk send [ %s ], resp:{%s}", msg, resp);
+            return resp;
+        } finally {
+            httpClient.close();
+        }
+    }
+
+    private static HttpPost constructHttpPost(String url, String msg, String charset) {
+        HttpPost post = new HttpPost(url);
+        StringEntity entity = new StringEntity(msg, charset);
+        post.setEntity(entity);
+        post.addHeader("Content-Type", "application/json; charset=utf-8");
+        return post;
+    }
+
+    private static CloseableHttpClient getProxyClient(String proxy, int port, String user, String password) {
+        HttpHost httpProxy = new HttpHost(proxy, port);
+        CredentialsProvider provider = new BasicCredentialsProvider();
+        provider.setCredentials(new AuthScope(httpProxy), new UsernamePasswordCredentials(user, password));
+        return HttpClients.custom().setDefaultCredentialsProvider(provider).build();
+    }
+
+    private static CloseableHttpClient getDefaultClient() {
+        return HttpClients.createDefault();
+    }
+
+    private static RequestConfig getProxyConfig(String proxy, int port) {
+        HttpHost httpProxy = new HttpHost(proxy, port);
+        return RequestConfig.custom().setProxy(httpProxy).build();
+    }
+
+    private static String textToJsonString(String text) {
+        Map<String, Object> items = new HashMap<>();
+        items.put("msgtype", "text");
+        Map<String, String> textContent = new HashMap<>();
+        byte[] byt = StringUtils.getBytesUtf8(text);
+        String txt = StringUtils.newStringUtf8(byt);
+        textContent.put("content", txt);
+        items.put("text", textContent);
+        return JSONUtils.toJsonString(items);
+    }
+
+    public static class DingTalkSendMsgResponse {
+        private Integer errcode;
+        private String errmsg;
+
+        public Integer getErrcode() {
+            return errcode;
+        }
+
+        public void setErrcode(Integer errcode) {
+            this.errcode = errcode;
+        }
+
+        public String getErrmsg() {
+            return errmsg;
+        }
+
+        public void setErrmsg(String errmsg) {
+            this.errmsg = errmsg;
+        }
+    }
+
+    private static AlertResult checkSendDingTalkSendMsgResult(String result) {
+        AlertResult alertResult = new AlertResult();
+        alertResult.setStatus("false");
+
+        if (null == result) {
+            alertResult.setMessage("send ding talk msg error");
+            logger.info("send ding talk msg error,ding talk server resp is null");
+            return alertResult;
+        }
+        DingTalkSendMsgResponse sendMsgResponse = JSONUtils.parseObject(result, DingTalkSendMsgResponse.class);
+        if (null == sendMsgResponse) {
+            alertResult.setMessage("send ding talk msg fail");
+            logger.info("send ding talk msg error,resp error");
+            return alertResult;
+        }
+        if (sendMsgResponse.errcode == 0) {
+            alertResult.setStatus("true");
+            alertResult.setMessage("send ding talk msg success");
+            return alertResult;
+        }
+        alertResult.setMessage(String.format("alert send ding talk msg error : %s", sendMsgResponse.getErrmsg()));
+        logger.info("alert send ding talk msg error : {}", sendMsgResponse.getErrmsg());
+        return alertResult;
+    }
+
+}
diff --git a/dolphinscheduler-alert-plugin/dolphinscheduler-alert-dingtalk/src/test/java/org/apache/dolphinscheduler/plugin/alert/dingtalk/DingTalkAlertChannelFactoryTest.java b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-dingtalk/src/test/java/org/apache/dolphinscheduler/plugin/alert/dingtalk/DingTalkAlertChannelFactoryTest.java
new file mode 100644
index 0000000..2a26daa
--- /dev/null
+++ b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-dingtalk/src/test/java/org/apache/dolphinscheduler/plugin/alert/dingtalk/DingTalkAlertChannelFactoryTest.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.dolphinscheduler.plugin.alert.dingtalk;
+
+import org.apache.dolphinscheduler.spi.alert.AlertChannel;
+import org.apache.dolphinscheduler.spi.params.base.PluginParams;
+import org.apache.dolphinscheduler.spi.utils.JSONUtils;
+
+import java.util.List;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * DingTalkAlertChannelFactoryTest
+ */
+public class DingTalkAlertChannelFactoryTest {
+
+    @Test
+    public void testGetParams() {
+        DingTalkAlertChannelFactory dingTalkAlertChannelFactory = new DingTalkAlertChannelFactory();
+        List<PluginParams> params = dingTalkAlertChannelFactory.getParams();
+        JSONUtils.toJsonString(params);
+        Assert.assertEquals(7, params.size());
+    }
+
+    @Test
+    public void testCreate() {
+        DingTalkAlertChannelFactory dingTalkAlertChannelFactory = new DingTalkAlertChannelFactory();
+        AlertChannel alertChannel = dingTalkAlertChannelFactory.create();
+        Assert.assertNotNull(alertChannel);
+    }
+}
diff --git a/dolphinscheduler-alert-plugin/dolphinscheduler-alert-dingtalk/src/test/java/org/apache/dolphinscheduler/plugin/alert/dingtalk/DingTalkSenderTest.java b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-dingtalk/src/test/java/org/apache/dolphinscheduler/plugin/alert/dingtalk/DingTalkSenderTest.java
new file mode 100644
index 0000000..4512402
--- /dev/null
+++ b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-dingtalk/src/test/java/org/apache/dolphinscheduler/plugin/alert/dingtalk/DingTalkSenderTest.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.dolphinscheduler.plugin.alert.dingtalk;
+
+import org.apache.dolphinscheduler.spi.alert.AlertResult;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * DingTalkSenderTest
+ */
+public class DingTalkSenderTest {
+
+    private static Map<String, String> dingTalkConfig = new HashMap<>();
+
+    @Before
+    public void initDingTalkConfig() {
+
+        dingTalkConfig.put(DingTalkParamsConstants.NAME_DING_TALK_KEYWORD, "keyWord");
+        dingTalkConfig.put(DingTalkParamsConstants.NAME_DING_TALK_WEB_HOOK, "url");
+        dingTalkConfig.put(DingTalkParamsConstants.NAME_DING_TALK_PROXY_ENABLE, "false");
+        dingTalkConfig.put(DingTalkParamsConstants.NAME_DING_TALK_PASSWORD, "password");
+        dingTalkConfig.put(DingTalkParamsConstants.NAME_DING_TALK_PORT, "9988");
+        dingTalkConfig.put(DingTalkParamsConstants.NAME_DING_TALK_USER, "user1,user2");
+    }
+
+    @Test
+    public void testSend() {
+        DingTalkSender dingTalkSender = new DingTalkSender(dingTalkConfig);
+        dingTalkSender.sendDingTalkMsg("keyWord+Welcome", "UTF-8");
+        dingTalkConfig.put(DingTalkParamsConstants.NAME_DING_TALK_PROXY_ENABLE, "true");
+        dingTalkSender = new DingTalkSender(dingTalkConfig);
+        AlertResult alertResult = dingTalkSender.sendDingTalkMsg("keyWord+Welcome", "UTF-8");
+        Assert.assertEquals("false",alertResult.getStatus());
+    }
+
+}
diff --git a/dolphinscheduler-alert/pom.xml b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-email/pom.xml
similarity index 68%
copy from dolphinscheduler-alert/pom.xml
copy to dolphinscheduler-alert-plugin/dolphinscheduler-alert-email/pom.xml
index a44d101..f4524b2 100644
--- a/dolphinscheduler-alert/pom.xml
+++ b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-email/pom.xml
@@ -15,50 +15,61 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-    <modelVersion>4.0.0</modelVersion>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     <parent>
+        <artifactId>dolphinscheduler-alert-plugin</artifactId>
         <groupId>org.apache.dolphinscheduler</groupId>
-        <artifactId>dolphinscheduler</artifactId>
         <version>1.3.4-SNAPSHOT</version>
     </parent>
-    <artifactId>dolphinscheduler-alert</artifactId>
-    <name>${project.artifactId}</name>
-    <packaging>jar</packaging>
+    <modelVersion>4.0.0</modelVersion>
+
+    <groupId>org.apache.dolphinscheduler</groupId>
+    <artifactId>dolphinscheduler-alert-email</artifactId>
+    <!-- can be load as a Alert Plugin when development and run server in IDE -->
+    <packaging>dolphinscheduler-plugin</packaging>
 
-    <properties>
-        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
-    </properties>
     <dependencies>
         <dependency>
-            <groupId>junit</groupId>
-            <artifactId>junit</artifactId>
-            <scope>test</scope>
+            <groupId>org.apache.dolphinscheduler</groupId>
+            <artifactId>dolphinscheduler-spi</artifactId>
+            <scope>provided</scope>
         </dependency>
+
         <dependency>
-            <groupId>org.mockito</groupId>
-            <artifactId>mockito-core</artifactId>
-            <type>jar</type>
-            <scope>test</scope>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-annotations</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-databind</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-collections4</artifactId>
         </dependency>
 
         <dependency>
-            <groupId>org.powermock</groupId>
-            <artifactId>powermock-module-junit4</artifactId>
-            <scope>test</scope>
+            <groupId>org.apache.poi</groupId>
+            <artifactId>poi</artifactId>
         </dependency>
 
         <dependency>
-            <groupId>org.powermock</groupId>
-            <artifactId>powermock-api-mockito2</artifactId>
-            <scope>test</scope>
-            <exclusions>
-                <exclusion>
-                    <groupId>org.mockito</groupId>
-                    <artifactId>mockito-core</artifactId>
-                </exclusion>
-            </exclusions>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>ch.qos.logback</groupId>
+            <artifactId>logback-classic</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
         </dependency>
 
         <dependency>
@@ -68,46 +79,50 @@
 
         <dependency>
             <groupId>com.fasterxml.jackson.core</groupId>
-            <artifactId>jackson-core</artifactId>
+            <artifactId>jackson-annotations</artifactId>
+            <scope>provided</scope>
         </dependency>
 
         <dependency>
             <groupId>com.fasterxml.jackson.core</groupId>
             <artifactId>jackson-databind</artifactId>
+            <scope>provided</scope>
         </dependency>
 
         <dependency>
-            <groupId>org.slf4j</groupId>
-            <artifactId>slf4j-api</artifactId>
-        </dependency>
-
-        <dependency>
-            <groupId>com.google.guava</groupId>
-            <artifactId>guava</artifactId>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
         </dependency>
 
         <dependency>
-            <groupId>ch.qos.logback</groupId>
-            <artifactId>logback-classic</artifactId>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-core</artifactId>
+            <type>jar</type>
+            <scope>test</scope>
         </dependency>
 
-        <!--excel poi-->
         <dependency>
-            <groupId>org.apache.poi</groupId>
-            <artifactId>poi</artifactId>
+            <groupId>org.powermock</groupId>
+            <artifactId>powermock-module-junit4</artifactId>
+            <scope>test</scope>
         </dependency>
 
         <dependency>
-            <groupId>org.apache.dolphinscheduler</groupId>
-            <artifactId>dolphinscheduler-dao</artifactId>
+            <groupId>org.powermock</groupId>
+            <artifactId>powermock-api-mockito2</artifactId>
+            <scope>test</scope>
             <exclusions>
                 <exclusion>
-                    <artifactId>log4j-api</artifactId>
-                    <groupId>org.apache.logging.log4j</groupId>
+                    <groupId>org.mockito</groupId>
+                    <artifactId>mockito-core</artifactId>
                 </exclusion>
             </exclusions>
         </dependency>
-
     </dependencies>
 
-</project>
+    <build>
+        <finalName>dolphinscheduler-alert-email-${project.version}</finalName>
+    </build>
+
+</project>
\ No newline at end of file
diff --git a/dolphinscheduler-alert-plugin/dolphinscheduler-alert-email/src/main/java/org/apache/dolphinscheduler/plugin/alert/email/EmailAlertChannel.java b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-email/src/main/java/org/apache/dolphinscheduler/plugin/alert/email/EmailAlertChannel.java
new file mode 100644
index 0000000..c793af5
--- /dev/null
+++ b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-email/src/main/java/org/apache/dolphinscheduler/plugin/alert/email/EmailAlertChannel.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.dolphinscheduler.plugin.alert.email;
+
+import org.apache.dolphinscheduler.spi.alert.AlertChannel;
+import org.apache.dolphinscheduler.spi.alert.AlertData;
+import org.apache.dolphinscheduler.spi.alert.AlertInfo;
+import org.apache.dolphinscheduler.spi.alert.AlertResult;
+import org.apache.dolphinscheduler.spi.params.PluginParamsTransfer;
+
+import java.util.Map;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * email alert channel . use email to seed the alertInfo
+ */
+public class EmailAlertChannel implements AlertChannel {
+    private static final Logger logger = LoggerFactory.getLogger(EmailAlertChannel.class);
+
+    @Override
+    public AlertResult process(AlertInfo info) {
+
+        AlertData alert = info.getAlertData();
+        String alertParams = info.getAlertParams();
+        Map<String, String> paramsMap = PluginParamsTransfer.getPluginParamsMap(alertParams);
+        MailSender mailSender = new MailSender(paramsMap);
+        AlertResult alertResult = mailSender.sendMails(alert.getTitle(), alert.getContent());
+
+        //send flag
+        boolean flag = false;
+
+        if (alertResult == null) {
+            alertResult = new AlertResult();
+            alertResult.setStatus("false");
+            alertResult.setMessage("alert send error.");
+            logger.info("alert send error : {}", alertResult.getMessage());
+            return alertResult;
+        }
+
+        flag = Boolean.parseBoolean(String.valueOf(alertResult.getStatus()));
+
+        if (flag) {
+            logger.info("alert send success");
+            alertResult.setMessage("email send success.");
+        } else {
+            alertResult.setMessage("alert send error.");
+            logger.info("alert send error : {}", alertResult.getMessage());
+        }
+
+        return alertResult;
+    }
+}
diff --git a/dolphinscheduler-alert-plugin/dolphinscheduler-alert-email/src/main/java/org/apache/dolphinscheduler/plugin/alert/email/EmailAlertChannelFactory.java b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-email/src/main/java/org/apache/dolphinscheduler/plugin/alert/email/EmailAlertChannelFactory.java
new file mode 100644
index 0000000..f8aa17f
--- /dev/null
+++ b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-email/src/main/java/org/apache/dolphinscheduler/plugin/alert/email/EmailAlertChannelFactory.java
@@ -0,0 +1,137 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.dolphinscheduler.plugin.alert.email;
+
+import org.apache.dolphinscheduler.spi.alert.AlertChannel;
+import org.apache.dolphinscheduler.spi.alert.AlertChannelFactory;
+import org.apache.dolphinscheduler.spi.alert.AlertConstants;
+import org.apache.dolphinscheduler.spi.alert.ShowType;
+import org.apache.dolphinscheduler.spi.params.InputParam;
+import org.apache.dolphinscheduler.spi.params.PasswordParam;
+import org.apache.dolphinscheduler.spi.params.RadioParam;
+import org.apache.dolphinscheduler.spi.params.base.DataType;
+import org.apache.dolphinscheduler.spi.params.base.ParamsOptions;
+import org.apache.dolphinscheduler.spi.params.base.PluginParams;
+import org.apache.dolphinscheduler.spi.params.base.Validate;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * email alert factory
+ */
+public class EmailAlertChannelFactory implements AlertChannelFactory {
+    @Override
+    public String getName() {
+        return "Email";
+    }
+
+    @Override
+    public List<PluginParams> getParams() {
+
+        List<PluginParams> paramsList = new ArrayList<>();
+        InputParam receivesParam = InputParam.newBuilder(MailParamsConstants.NAME_PLUGIN_DEFAULT_EMAIL_RECEIVERS, MailParamsConstants.PLUGIN_DEFAULT_EMAIL_RECEIVERS)
+                .setPlaceholder("please input receives")
+                .addValidate(Validate.newBuilder()
+                        .setRequired(true)
+                        .build())
+                .build();
+
+        InputParam receiveCcsParam = InputParam.newBuilder(MailParamsConstants.NAME_PLUGIN_DEFAULT_EMAIL_RECEIVERCCS, MailParamsConstants.PLUGIN_DEFAULT_EMAIL_RECEIVERCCS)
+                .build();
+
+        InputParam mailSmtpHost = InputParam.newBuilder(MailParamsConstants.NAME_MAIL_SMTP_HOST, MailParamsConstants.MAIL_SMTP_HOST)
+                .addValidate(Validate.newBuilder().setRequired(true).build())
+                .build();
+
+        InputParam mailSmtpPort = InputParam.newBuilder(MailParamsConstants.NAME_MAIL_SMTP_PORT, MailParamsConstants.MAIL_SMTP_PORT)
+                .setValue(25)
+                .addValidate(Validate.newBuilder()
+                        .setRequired(true)
+                        .setType(DataType.NUMBER.getDataType())
+                        .build())
+                .build();
+
+        InputParam mailSender = InputParam.newBuilder(MailParamsConstants.NAME_MAIL_SENDER, MailParamsConstants.MAIL_SENDER)
+                .addValidate(Validate.newBuilder().setRequired(true).build())
+                .build();
+
+        RadioParam enableSmtpAuth = RadioParam.newBuilder(MailParamsConstants.NAME_MAIL_SMTP_AUTH, MailParamsConstants.MAIL_SMTP_AUTH)
+                .addParamsOptions(new ParamsOptions("YES", true, false))
+                .addParamsOptions(new ParamsOptions("NO", false, false))
+                .setValue(true)
+                .addValidate(Validate.newBuilder().setRequired(true).build())
+                .build();
+
+        InputParam mailUser = InputParam.newBuilder(MailParamsConstants.NAME_MAIL_USER, MailParamsConstants.MAIL_USER)
+                .setPlaceholder("if enable use authentication, you need input user")
+                .build();
+
+        PasswordParam mailPassword = PasswordParam.newBuilder(MailParamsConstants.NAME_MAIL_PASSWD, MailParamsConstants.MAIL_PASSWD)
+                .setPlaceholder("if enable use authentication, you need input password")
+                .build();
+
+        RadioParam enableTls = RadioParam.newBuilder(MailParamsConstants.NAME_MAIL_SMTP_STARTTLS_ENABLE, MailParamsConstants.MAIL_SMTP_STARTTLS_ENABLE)
+                .addParamsOptions(new ParamsOptions("YES", true, false))
+                .addParamsOptions(new ParamsOptions("NO", false, false))
+                .setValue(false)
+                .addValidate(Validate.newBuilder().setRequired(true).build())
+                .build();
+
+        RadioParam enableSsl = RadioParam.newBuilder(MailParamsConstants.NAME_MAIL_SMTP_SSL_ENABLE, MailParamsConstants.MAIL_SMTP_SSL_ENABLE)
+                .addParamsOptions(new ParamsOptions("YES", true, false))
+                .addParamsOptions(new ParamsOptions("NO", false, false))
+                .setValue(false)
+                .addValidate(Validate.newBuilder().setRequired(true).build())
+                .build();
+
+        InputParam sslTrust = InputParam.newBuilder(MailParamsConstants.NAME_MAIL_SMTP_SSL_TRUST, MailParamsConstants.MAIL_SMTP_SSL_TRUST)
+                .setValue("*")
+                .addValidate(Validate.newBuilder().setRequired(true).build())
+                .build();
+
+        RadioParam showType = RadioParam.newBuilder(AlertConstants.SHOW_TYPE, AlertConstants.SHOW_TYPE)
+                .addParamsOptions(new ParamsOptions(ShowType.TABLE.getDescp(), ShowType.TABLE.getDescp(), false))
+                .addParamsOptions(new ParamsOptions(ShowType.TEXT.getDescp(), ShowType.TEXT.getDescp(), false))
+                .addParamsOptions(new ParamsOptions(ShowType.ATTACHMENT.getDescp(), ShowType.ATTACHMENT.getDescp(), false))
+                .addParamsOptions(new ParamsOptions(ShowType.TABLEATTACHMENT.getDescp(), ShowType.TABLEATTACHMENT.getDescp(), false))
+                .setValue(ShowType.TABLE.getDescp())
+                .addValidate(Validate.newBuilder().setRequired(true).build())
+                .build();
+
+        paramsList.add(receivesParam);
+        paramsList.add(receiveCcsParam);
+        paramsList.add(mailSmtpHost);
+        paramsList.add(mailSmtpPort);
+        paramsList.add(mailSender);
+        paramsList.add(enableSmtpAuth);
+        paramsList.add(mailUser);
+        paramsList.add(mailPassword);
+        paramsList.add(enableTls);
+        paramsList.add(enableSsl);
+        paramsList.add(sslTrust);
+        paramsList.add(showType);
+
+        return paramsList;
+    }
+
+    @Override
+    public AlertChannel create() {
+        return new EmailAlertChannel();
+    }
+}
diff --git a/dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/template/AlertTemplateFactory.java b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-email/src/main/java/org/apache/dolphinscheduler/plugin/alert/email/EmailAlertPlugin.java
similarity index 63%
copy from dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/template/AlertTemplateFactory.java
copy to dolphinscheduler-alert-plugin/dolphinscheduler-alert-email/src/main/java/org/apache/dolphinscheduler/plugin/alert/email/EmailAlertPlugin.java
index d384631..175b518 100644
--- a/dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/template/AlertTemplateFactory.java
+++ b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-email/src/main/java/org/apache/dolphinscheduler/plugin/alert/email/EmailAlertPlugin.java
@@ -15,24 +15,19 @@
  * limitations under the License.
  */
 
-package org.apache.dolphinscheduler.alert.template;
+package org.apache.dolphinscheduler.plugin.alert.email;
 
-import org.apache.dolphinscheduler.alert.template.impl.DefaultHTMLTemplate;
+import org.apache.dolphinscheduler.spi.DolphinSchedulerPlugin;
+import org.apache.dolphinscheduler.spi.alert.AlertChannelFactory;
+
+import com.google.common.collect.ImmutableList;
 
 /**
- * the alert template factory
+ * email alert plugin
  */
-public class AlertTemplateFactory {
-
-    private AlertTemplateFactory() {
-    }
-
-    /**
-     * get a template from alert.properties conf file
-     *
-     * @return a template, default is DefaultHTMLTemplate
-     */
-    public static AlertTemplate getMessageTemplate() {
-        return new DefaultHTMLTemplate();
+public class EmailAlertPlugin implements DolphinSchedulerPlugin {
+    @Override
+    public Iterable<AlertChannelFactory> getAlertChannelFactorys() {
+        return ImmutableList.of(new EmailAlertChannelFactory());
     }
 }
diff --git a/dolphinscheduler-alert-plugin/dolphinscheduler-alert-email/src/main/java/org/apache/dolphinscheduler/plugin/alert/email/EmailConstants.java b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-email/src/main/java/org/apache/dolphinscheduler/plugin/alert/email/EmailConstants.java
new file mode 100644
index 0000000..d0e85ff
--- /dev/null
+++ b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-email/src/main/java/org/apache/dolphinscheduler/plugin/alert/email/EmailConstants.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.dolphinscheduler.plugin.alert.email;
+
+public class EmailConstants {
+
+
+    public static final String XLS_FILE_PATH = "xls.file.path";
+
+    public static final String MAIL_TRANSPORT_PROTOCOL = "mail.transport.protocol";
+
+    public static final String DEFAULT_SMTP_PORT = "25";
+
+    public static final String TEXT_HTML_CHARSET_UTF_8 = "text/html;charset=utf-8";
+
+    public static final int NUMBER_1000 = 1000;
+
+    public static final String TR = "<tr>";
+
+    public static final String TD = "<td>";
+
+    public static final String TD_END = "</td>";
+
+    public static final String TR_END = "</tr>";
+
+    public static final String TITLE = "title";
+
+    public static final String CONTENT = "content";
+
+    public static final String TH = "<th>";
+
+    public static final String TH_END = "</th>";
+
+    public static final String MARKDOWN_QUOTE = ">";
+
+    public static final String MARKDOWN_ENTER = "\n";
+
+    public static final String HTML_HEADER_PREFIX = new StringBuilder("<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01 Transitional//EN' 'http://www.w3.org/TR/html4/loose.dtd'>")
+            .append("<html>")
+            .append("<head>")
+            .append("<title>dolphinscheduler</title>")
+            .append("<meta name='Keywords' content=''>")
+            .append("<meta name='Description' content=''>")
+            .append("<style type=\"text/css\">")
+            .append("table {margin-top:0px;padding-top:0px;border:1px solid;font-size: 14px;color: #333333;border-width: 1px;border-color: #666666;border-collapse: collapse;}")
+            .append("table th {border-width: 1px;padding: 8px;border-style: solid;border-color: #666666;background-color: #dedede;text-align: left;}")
+            .append("table td {border-width: 1px;padding: 8px;border-style: solid;border-color: #666666;background-color: #ffffff;text-align: left;}")
+            .append("</style>")
+            .append("</head>")
+            .append("<body style=\"margin:0;padding:0\"><table border=\"1px\" cellpadding=\"5px\" cellspacing=\"-10px\"> ")
+            .toString();
+
+    public static final String TABLE_BODY_HTML_TAIL = "</table></body></html>";
+
+    public static final String UTF_8 = "UTF-8";
+
+    public static final String EXCEL_SUFFIX_XLS = ".xls";
+
+    public static final String SINGLE_SLASH = "/";
+}
diff --git a/dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/utils/ExcelUtils.java b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-email/src/main/java/org/apache/dolphinscheduler/plugin/alert/email/ExcelUtils.java
similarity index 87%
rename from dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/utils/ExcelUtils.java
rename to dolphinscheduler-alert-plugin/dolphinscheduler-alert-email/src/main/java/org/apache/dolphinscheduler/plugin/alert/email/ExcelUtils.java
index 76ce66a..1579f95 100644
--- a/dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/utils/ExcelUtils.java
+++ b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-email/src/main/java/org/apache/dolphinscheduler/plugin/alert/email/ExcelUtils.java
@@ -15,11 +15,11 @@
  * limitations under the License.
  */
 
-package org.apache.dolphinscheduler.alert.utils;
+package org.apache.dolphinscheduler.plugin.alert.email;
 
-import org.apache.dolphinscheduler.common.utils.CollectionUtils;
-import org.apache.dolphinscheduler.common.utils.JSONUtils;
+import org.apache.dolphinscheduler.spi.utils.JSONUtils;
 
+import org.apache.commons.collections4.CollectionUtils;
 import org.apache.poi.hssf.usermodel.HSSFCell;
 import org.apache.poi.hssf.usermodel.HSSFRow;
 import org.apache.poi.hssf.usermodel.HSSFSheet;
@@ -31,6 +31,7 @@ import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
@@ -43,12 +44,12 @@ import org.slf4j.LoggerFactory;
  */
 public class ExcelUtils {
 
-    private static final Logger logger = LoggerFactory.getLogger(ExcelUtils.class);
-
-    private ExcelUtils() {
-        throw new IllegalStateException(ExcelUtils.class.getName());
+    public ExcelUtils() {
+        throw new IllegalStateException("Utility class");
     }
 
+    private static final Logger logger = LoggerFactory.getLogger(ExcelUtils.class);
+
     /**
      * generate excel file
      *
@@ -71,7 +72,9 @@ public class ExcelUtils {
 
         List<String> headerList = new ArrayList<>();
 
-        for (Map.Entry<String, Object> en : headerMap.entrySet()) {
+        Iterator<Map.Entry<String, Object>> iter = headerMap.entrySet().iterator();
+        while (iter.hasNext()) {
+            Map.Entry<String, Object> en = iter.next();
             headerList.add(en.getKey());
         }
 
@@ -122,11 +125,12 @@ public class ExcelUtils {
             }
 
             //setting file output
-            fos = new FileOutputStream(xlsFilePath + Constants.SINGLE_SLASH + title + Constants.EXCEL_SUFFIX_XLS);
+            fos = new FileOutputStream(xlsFilePath + EmailConstants.SINGLE_SLASH + title + EmailConstants.EXCEL_SUFFIX_XLS);
 
             wb.write(fos);
 
         } catch (Exception e) {
+            logger.error("generate excel error", e);
             throw new RuntimeException("generate excel error", e);
         } finally {
             if (wb != null) {
@@ -146,4 +150,4 @@ public class ExcelUtils {
         }
     }
 
-}
+}
\ No newline at end of file
diff --git a/dolphinscheduler-alert-plugin/dolphinscheduler-alert-email/src/main/java/org/apache/dolphinscheduler/plugin/alert/email/MailParamsConstants.java b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-email/src/main/java/org/apache/dolphinscheduler/plugin/alert/email/MailParamsConstants.java
new file mode 100644
index 0000000..3eaa420
--- /dev/null
+++ b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-email/src/main/java/org/apache/dolphinscheduler/plugin/alert/email/MailParamsConstants.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.dolphinscheduler.plugin.alert.email;
+
+/**
+ * mail plugin params json use
+ */
+public class MailParamsConstants {
+
+    public MailParamsConstants() {
+        throw new IllegalStateException("Utility class");
+    }
+
+    public static final String PLUGIN_DEFAULT_EMAIL_RECEIVERS = "$t('receivers')";
+    public static final String NAME_PLUGIN_DEFAULT_EMAIL_RECEIVERS = "receivers";
+
+    public static final String PLUGIN_DEFAULT_EMAIL_RECEIVERCCS = "$t('receiverCcs')";
+    public static final String NAME_PLUGIN_DEFAULT_EMAIL_RECEIVERCCS = "receiverCcs";
+
+    public static final String MAIL_PROTOCOL = "transport.protocol";
+    public static final String NAME_MAIL_PROTOCOL = "protocol";
+
+    public static final String MAIL_SMTP_HOST = "smtp.host";
+    public static final String NAME_MAIL_SMTP_HOST = "serverHost";
+
+    public static final String MAIL_SMTP_PORT = "smtp.port";
+    public static final String NAME_MAIL_SMTP_PORT = "serverPort";
+
+    public static final String MAIL_SENDER = "sender";
+    public static final String NAME_MAIL_SENDER = "sender";
+
+    public static final String MAIL_SMTP_AUTH = "smtp.auth";
+    public static final String NAME_MAIL_SMTP_AUTH = "enableSmtpAuth";
+
+    public static final String MAIL_USER = "user";
+    public static final String NAME_MAIL_USER = "user";
+
+    public static final String MAIL_PASSWD = "passwd";
+    public static final String NAME_MAIL_PASSWD = "passwd";
+
+    public static final String MAIL_SMTP_STARTTLS_ENABLE = "smtp.starttls.enable";
+    public static final String NAME_MAIL_SMTP_STARTTLS_ENABLE = "starttlsEnable";
+
+    public static final String MAIL_SMTP_SSL_ENABLE = "smtp.ssl.enable";
+    public static final String NAME_MAIL_SMTP_SSL_ENABLE = "sslEnable";
+
+    public static final String MAIL_SMTP_SSL_TRUST = "smtp.ssl.trust";
+    public static final String NAME_MAIL_SMTP_SSL_TRUST = "smtpSslTrust";
+
+}
diff --git a/dolphinscheduler-alert-plugin/dolphinscheduler-alert-email/src/main/java/org/apache/dolphinscheduler/plugin/alert/email/MailSender.java b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-email/src/main/java/org/apache/dolphinscheduler/plugin/alert/email/MailSender.java
new file mode 100644
index 0000000..03765b2
--- /dev/null
+++ b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-email/src/main/java/org/apache/dolphinscheduler/plugin/alert/email/MailSender.java
@@ -0,0 +1,429 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.dolphinscheduler.plugin.alert.email;
+
+import static java.util.Objects.requireNonNull;
+
+import org.apache.dolphinscheduler.plugin.alert.email.template.AlertTemplate;
+import org.apache.dolphinscheduler.plugin.alert.email.template.DefaultHTMLTemplate;
+import org.apache.dolphinscheduler.spi.alert.AlertConstants;
+import org.apache.dolphinscheduler.spi.alert.AlertResult;
+import org.apache.dolphinscheduler.spi.alert.ShowType;
+import org.apache.dolphinscheduler.spi.utils.StringUtils;
+
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.mail.EmailException;
+import org.apache.commons.mail.HtmlEmail;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import javax.mail.Authenticator;
+import javax.mail.Message;
+import javax.mail.MessagingException;
+import javax.mail.PasswordAuthentication;
+import javax.mail.Session;
+import javax.mail.Transport;
+import javax.mail.internet.InternetAddress;
+import javax.mail.internet.MimeBodyPart;
+import javax.mail.internet.MimeMessage;
+import javax.mail.internet.MimeMultipart;
+import javax.mail.internet.MimeUtility;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.sun.mail.smtp.SMTPProvider;
+
+/**
+ * mail utils
+ */
+public class MailSender {
+
+    public static final Logger logger = LoggerFactory.getLogger(MailSender.class);
+
+    private List<String> receivers;
+    private List<String> receiverCcs;
+    private String mailProtocol = "SMTP";
+    private String mailSmtpHost;
+    private String mailSmtpPort;
+    private String mailSender;
+    private String enableSmtpAuth;
+    private String mailUser;
+    private String mailPasswd;
+    private String mailUseStartTLS;
+    private String mailUseSSL;
+    private String xlsFilePath;
+    private String sslTrust;
+    private String showType;
+    private AlertTemplate alertTemplate;
+
+    public MailSender(Map<String, String> config) {
+
+        String receiversConfig = config.get(MailParamsConstants.NAME_PLUGIN_DEFAULT_EMAIL_RECEIVERS);
+        if (receiversConfig == null || "".equals(receiversConfig)) {
+            throw new RuntimeException(MailParamsConstants.PLUGIN_DEFAULT_EMAIL_RECEIVERS + "must not be null");
+        }
+
+        receivers = Arrays.asList(receiversConfig.split(","));
+
+        String receiverCcsConfig = config.get(MailParamsConstants.NAME_PLUGIN_DEFAULT_EMAIL_RECEIVERCCS);
+
+        receiverCcs = new ArrayList<>();
+        if (receiverCcsConfig != null && !"".equals(receiverCcsConfig)) {
+            receiverCcs = Arrays.asList(receiverCcsConfig.split(","));
+        }
+
+        mailSmtpHost = config.get(MailParamsConstants.NAME_MAIL_SMTP_HOST);
+        requireNonNull(mailSmtpHost, MailParamsConstants.MAIL_SMTP_HOST + " must not null");
+
+        mailSmtpPort = config.get(MailParamsConstants.NAME_MAIL_SMTP_PORT);
+        requireNonNull(mailSmtpPort, MailParamsConstants.MAIL_SMTP_PORT + " must not null");
+
+        mailSender = config.get(MailParamsConstants.NAME_MAIL_SENDER);
+        requireNonNull(mailSender, MailParamsConstants.MAIL_SENDER + " must not null");
+
+        enableSmtpAuth = config.get(MailParamsConstants.NAME_MAIL_SMTP_AUTH);
+
+        mailUser = config.get(MailParamsConstants.NAME_MAIL_USER);
+        requireNonNull(mailUser, MailParamsConstants.MAIL_USER + " must not null");
+
+        mailPasswd = config.get(MailParamsConstants.NAME_MAIL_PASSWD);
+        requireNonNull(mailPasswd, MailParamsConstants.MAIL_PASSWD + " must not null");
+
+        mailUseStartTLS = config.get(MailParamsConstants.NAME_MAIL_SMTP_STARTTLS_ENABLE);
+        requireNonNull(mailUseStartTLS, MailParamsConstants.MAIL_SMTP_STARTTLS_ENABLE + " must not null");
+
+        mailUseSSL = config.get(MailParamsConstants.NAME_MAIL_SMTP_SSL_ENABLE);
+        requireNonNull(mailUseSSL, MailParamsConstants.MAIL_SMTP_SSL_ENABLE + " must not null");
+
+        sslTrust = config.get(MailParamsConstants.NAME_MAIL_SMTP_SSL_TRUST);
+        requireNonNull(sslTrust, MailParamsConstants.MAIL_SMTP_SSL_TRUST + " must not null");
+
+        showType = config.get(AlertConstants.SHOW_TYPE);
+        requireNonNull(showType, AlertConstants.SHOW_TYPE + " must not null");
+
+        xlsFilePath = config.get(EmailConstants.XLS_FILE_PATH);
+        if (StringUtils.isBlank(xlsFilePath)) {
+            xlsFilePath = "/tmp/xls";
+        }
+
+        alertTemplate = new DefaultHTMLTemplate();
+    }
+
+    /**
+     * send mail to receivers
+     *
+     * @param title   title
+     * @param content content
+     * @return
+     */
+    public AlertResult sendMails(String title, String content) {
+        return sendMails(this.receivers, this.receiverCcs, title, content);
+    }
+
+    /**
+     * send mail to receivers
+     *
+     * @param title   email title
+     * @param content email content
+     * @return
+     */
+    public AlertResult sendMailsToReceiverOnly(String title, String content) {
+        return sendMails(this.receivers, null, title, content);
+    }
+
+    /**
+     * send mail
+     *
+     * @param receivers   receivers
+     * @param receiverCcs receiverCcs
+     * @param title       title
+     * @param content     content
+     * @return
+     */
+    public AlertResult sendMails(List<String> receivers, List<String> receiverCcs, String title, String content) {
+        AlertResult alertResult = new AlertResult();
+        alertResult.setStatus("false");
+
+        // if there is no receivers && no receiversCc, no need to process
+        if (CollectionUtils.isEmpty(receivers) && CollectionUtils.isEmpty(receiverCcs)) {
+            return alertResult;
+        }
+
+        receivers.removeIf(StringUtils::isEmpty);
+
+        if (showType.equals(ShowType.TABLE.getDescp()) || showType.equals(ShowType.TEXT.getDescp())) {
+            // send email
+            HtmlEmail email = new HtmlEmail();
+
+            try {
+                Session session = getSession();
+                email.setMailSession(session);
+                email.setFrom(mailSender);
+                email.setCharset(EmailConstants.UTF_8);
+                if (CollectionUtils.isNotEmpty(receivers)) {
+                    // receivers mail
+                    for (String receiver : receivers) {
+                        email.addTo(receiver);
+                    }
+                }
+
+                if (CollectionUtils.isNotEmpty(receiverCcs)) {
+                    //cc
+                    for (String receiverCc : receiverCcs) {
+                        email.addCc(receiverCc);
+                    }
+                }
+                // sender mail
+                return getStringObjectMap(title, content, alertResult, email);
+            } catch (Exception e) {
+                handleException(alertResult, e);
+            }
+        } else if (showType.equals(ShowType.ATTACHMENT.getDescp()) || showType.equals(ShowType.TABLEATTACHMENT.getDescp())) {
+            try {
+
+                String partContent = (showType.equals(ShowType.ATTACHMENT.getDescp()) ? "Please see the attachment " + title + EmailConstants.EXCEL_SUFFIX_XLS : htmlTable(content, false));
+
+                attachment(title, content, partContent);
+
+                alertResult.setStatus("true");
+                return alertResult;
+            } catch (Exception e) {
+                handleException(alertResult, e);
+                return alertResult;
+            }
+        }
+        return alertResult;
+
+    }
+
+    /**
+     * html table content
+     *
+     * @param content the content
+     * @param showAll if show the whole content
+     * @return the html table form
+     */
+    private String htmlTable(String content, boolean showAll) {
+        return alertTemplate.getMessageFromTemplate(content, ShowType.TABLE, showAll);
+    }
+
+    /**
+     * html table content
+     *
+     * @param content the content
+     * @return the html table form
+     */
+    private String htmlTable(String content) {
+        return htmlTable(content, true);
+    }
+
+    /**
+     * html text content
+     *
+     * @param content the content
+     * @return text in html form
+     */
+    private String htmlText(String content) {
+        return alertTemplate.getMessageFromTemplate(content, ShowType.TEXT);
+    }
+
+    /**
+     * send mail as Excel attachment
+     *
+     * @param title
+     * @param content
+     * @param partContent
+     * @throws Exception
+     */
+    private void attachment(String title, String content, String partContent) throws Exception {
+        MimeMessage msg = getMimeMessage();
+
+        attachContent(title, content, partContent, msg);
+    }
+
+    /**
+     * get MimeMessage
+     *
+     * @return
+     * @throws MessagingException
+     */
+    private MimeMessage getMimeMessage() throws MessagingException {
+
+        // 1. The first step in creating mail: creating session
+        Session session = getSession();
+        // Setting debug mode, can be turned off
+        session.setDebug(false);
+
+        // 2. creating mail: Creating a MimeMessage
+        MimeMessage msg = new MimeMessage(session);
+        // 3. set sender
+        msg.setFrom(new InternetAddress(mailSender));
+        // 4. set receivers
+        for (String receiver : receivers) {
+            msg.addRecipients(Message.RecipientType.TO, InternetAddress.parse(receiver));
+        }
+        return msg;
+    }
+
+    /**
+     * get session
+     *
+     * @return the new Session
+     */
+    private Session getSession() {
+        Properties props = new Properties();
+        props.setProperty(MailParamsConstants.MAIL_SMTP_HOST, mailSmtpHost);
+        props.setProperty(MailParamsConstants.MAIL_SMTP_PORT, mailSmtpPort);
+        props.setProperty(MailParamsConstants.MAIL_SMTP_AUTH, enableSmtpAuth);
+        props.setProperty(EmailConstants.MAIL_TRANSPORT_PROTOCOL, mailProtocol);
+        props.setProperty(MailParamsConstants.MAIL_SMTP_STARTTLS_ENABLE, mailUseStartTLS);
+        props.setProperty(MailParamsConstants.MAIL_SMTP_SSL_ENABLE, mailUseSSL);
+        props.setProperty(MailParamsConstants.MAIL_SMTP_SSL_TRUST, sslTrust);
+
+        Authenticator auth = new Authenticator() {
+            @Override
+            protected PasswordAuthentication getPasswordAuthentication() {
+                // mail username and password
+                return new PasswordAuthentication(mailUser, mailPasswd);
+            }
+        };
+
+        Session session = Session.getInstance(props, auth);
+        session.addProvider(new SMTPProvider());
+        return session;
+    }
+
+    /**
+     * attach content
+     *
+     * @param title
+     * @param content
+     * @param partContent
+     * @param msg
+     * @throws MessagingException
+     * @throws IOException
+     */
+    private void attachContent(String title, String content, String partContent, MimeMessage msg) throws MessagingException, IOException {
+        /**
+         * set receiverCc
+         */
+        if (CollectionUtils.isNotEmpty(receiverCcs)) {
+            for (String receiverCc : receiverCcs) {
+                msg.addRecipients(Message.RecipientType.CC, InternetAddress.parse(receiverCc));
+            }
+        }
+
+        // set subject
+        msg.setSubject(title);
+        MimeMultipart partList = new MimeMultipart();
+        // set signature
+        MimeBodyPart part1 = new MimeBodyPart();
+        part1.setContent(partContent, EmailConstants.TEXT_HTML_CHARSET_UTF_8);
+        // set attach file
+        MimeBodyPart part2 = new MimeBodyPart();
+        File file = new File(xlsFilePath + EmailConstants.SINGLE_SLASH + title + EmailConstants.EXCEL_SUFFIX_XLS);
+        if (!file.getParentFile().exists()) {
+            file.getParentFile().mkdirs();
+        }
+        // make excel file
+
+        ExcelUtils.genExcelFile(content, title, xlsFilePath);
+
+        part2.attachFile(file);
+        part2.setFileName(MimeUtility.encodeText(title + EmailConstants.EXCEL_SUFFIX_XLS, EmailConstants.UTF_8, "B"));
+        // add components to collection
+        partList.addBodyPart(part1);
+        partList.addBodyPart(part2);
+        msg.setContent(partList);
+        // 5. send Transport
+        Transport.send(msg);
+        // 6. delete saved file
+        deleteFile(file);
+    }
+
+    /**
+     * the string object map
+     *
+     * @param title
+     * @param content
+     * @param alertResult
+     * @param email
+     * @return
+     * @throws EmailException
+     */
+    private AlertResult getStringObjectMap(String title, String content, AlertResult alertResult, HtmlEmail email) throws EmailException {
+
+        /**
+         * the subject of the message to be sent
+         */
+        email.setSubject(title);
+        /**
+         * to send information, you can use HTML tags in mail content because of the use of HtmlEmail
+         */
+        if (showType.equals(ShowType.TABLE.getDescp())) {
+            email.setMsg(htmlTable(content));
+        } else if (showType.equals(ShowType.TEXT.getDescp())) {
+            email.setMsg(htmlText(content));
+        }
+
+        // send
+        email.setDebug(true);
+        Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
+        email.send();
+
+        alertResult.setStatus("true");
+
+        return alertResult;
+    }
+
+    /**
+     * file delete
+     *
+     * @param file the file to delete
+     */
+    public void deleteFile(File file) {
+        if (file.exists()) {
+            if (file.delete()) {
+                logger.info("delete success: {}", file.getAbsolutePath() + file.getName());
+            } else {
+                logger.info("delete fail: {}", file.getAbsolutePath() + file.getName());
+            }
+        } else {
+            logger.info("file not exists: {}", file.getAbsolutePath() + file.getName());
+        }
+    }
+
+    /**
+     * handle exception
+     *
+     * @param alertResult
+     * @param e
+     */
+    private void handleException(AlertResult alertResult, Exception e) {
+        logger.error("Send email to {} failed", receivers, e);
+        alertResult.setMessage("Send email to {" + String.join(",", receivers) + "} failed," + e.toString());
+    }
+
+}
diff --git a/dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/template/AlertTemplate.java b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-email/src/main/java/org/apache/dolphinscheduler/plugin/alert/email/template/AlertTemplate.java
similarity index 72%
rename from dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/template/AlertTemplate.java
rename to dolphinscheduler-alert-plugin/dolphinscheduler-alert-email/src/main/java/org/apache/dolphinscheduler/plugin/alert/email/template/AlertTemplate.java
index 81b5e65..dec993d 100644
--- a/dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/template/AlertTemplate.java
+++ b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-email/src/main/java/org/apache/dolphinscheduler/plugin/alert/email/template/AlertTemplate.java
@@ -14,9 +14,10 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dolphinscheduler.alert.template;
 
-import org.apache.dolphinscheduler.common.enums.ShowType;
+package org.apache.dolphinscheduler.plugin.alert.email.template;
+
+import org.apache.dolphinscheduler.spi.alert.ShowType;
 
 /**
  * alert message template
@@ -25,20 +26,22 @@ public interface AlertTemplate {
 
     /**
      * get a message from a specified alert template
-     * @param content     alert message content
-     * @param showType    show type
-     * @param showAll    whether to show all
+     *
+     * @param content  alert message content
+     * @param showType show type
+     * @param showAll  whether to show all
      * @return a message from a specified alert template
      */
-    String getMessageFromTemplate(String content, ShowType showType,boolean showAll);
+    String getMessageFromTemplate(String content, ShowType showType, boolean showAll);
 
     /**
      * default showAll is true
-     * @param content alert message content
+     *
+     * @param content  alert message content
      * @param showType show type
      * @return a message from a specified alert template
      */
-    default String getMessageFromTemplate(String content,ShowType showType){
-        return getMessageFromTemplate(content,showType,true);
+    default String getMessageFromTemplate(String content, ShowType showType) {
+        return getMessageFromTemplate(content, showType, true);
     }
 }
diff --git a/dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/template/impl/DefaultHTMLTemplate.java b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-email/src/main/java/org/apache/dolphinscheduler/plugin/alert/email/template/DefaultHTMLTemplate.java
similarity index 66%
rename from dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/template/impl/DefaultHTMLTemplate.java
rename to dolphinscheduler-alert-plugin/dolphinscheduler-alert-email/src/main/java/org/apache/dolphinscheduler/plugin/alert/email/template/DefaultHTMLTemplate.java
index f590849..06decd6 100644
--- a/dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/template/impl/DefaultHTMLTemplate.java
+++ b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-email/src/main/java/org/apache/dolphinscheduler/plugin/alert/email/template/DefaultHTMLTemplate.java
@@ -15,21 +15,19 @@
  * limitations under the License.
  */
 
-package org.apache.dolphinscheduler.alert.template.impl;
+package org.apache.dolphinscheduler.plugin.alert.email.template;
 
-import static org.apache.dolphinscheduler.common.utils.Preconditions.checkNotNull;
+import static java.util.Objects.requireNonNull;
 
-import org.apache.dolphinscheduler.alert.template.AlertTemplate;
-import org.apache.dolphinscheduler.alert.utils.Constants;
-import org.apache.dolphinscheduler.common.enums.ShowType;
-import org.apache.dolphinscheduler.common.utils.JSONUtils;
-import org.apache.dolphinscheduler.common.utils.StringUtils;
+import org.apache.dolphinscheduler.plugin.alert.email.EmailConstants;
+import org.apache.dolphinscheduler.spi.alert.ShowType;
+import org.apache.dolphinscheduler.spi.utils.JSONUtils;
+import org.apache.dolphinscheduler.spi.utils.StringUtils;
 
 import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.Map.Entry;
 import java.util.Set;
 
 import org.slf4j.Logger;
@@ -52,7 +50,7 @@ public class DefaultHTMLTemplate implements AlertTemplate {
             case TABLE:
                 return getTableTypeMessage(content, showAll);
             case TEXT:
-                return getTextTypeMessage(content);
+                return getTextTypeMessage(content, showAll);
             default:
                 throw new IllegalArgumentException(String.format("not support showType: %s in DefaultHTMLTemplate", showType));
         }
@@ -70,8 +68,8 @@ public class DefaultHTMLTemplate implements AlertTemplate {
         if (StringUtils.isNotEmpty(content)) {
             List<LinkedHashMap> mapItemsList = JSONUtils.toList(content, LinkedHashMap.class);
 
-            if (!showAll && mapItemsList.size() > Constants.NUMBER_1000) {
-                mapItemsList = mapItemsList.subList(0, Constants.NUMBER_1000);
+            if (!showAll && mapItemsList.size() > EmailConstants.NUMBER_1000) {
+                mapItemsList = mapItemsList.subList(0, EmailConstants.NUMBER_1000);
             }
 
             StringBuilder contents = new StringBuilder(200);
@@ -81,21 +79,21 @@ public class DefaultHTMLTemplate implements AlertTemplate {
             String title = "";
             for (LinkedHashMap mapItems : mapItemsList) {
 
-                Set<Entry<String, Object>> entries = mapItems.entrySet();
+                Set<Map.Entry<String, Object>> entries = mapItems.entrySet();
 
-                Iterator<Entry<String, Object>> iterator = entries.iterator();
+                Iterator<Map.Entry<String, Object>> iterator = entries.iterator();
 
-                StringBuilder t = new StringBuilder(Constants.TR);
-                StringBuilder cs = new StringBuilder(Constants.TR);
+                StringBuilder t = new StringBuilder(EmailConstants.TR);
+                StringBuilder cs = new StringBuilder(EmailConstants.TR);
                 while (iterator.hasNext()) {
 
                     Map.Entry<String, Object> entry = iterator.next();
-                    t.append(Constants.TH).append(entry.getKey()).append(Constants.TH_END);
-                    cs.append(Constants.TD).append(String.valueOf(entry.getValue())).append(Constants.TD_END);
+                    t.append(EmailConstants.TH).append(entry.getKey()).append(EmailConstants.TH_END);
+                    cs.append(EmailConstants.TD).append(String.valueOf(entry.getValue())).append(EmailConstants.TD_END);
 
                 }
-                t.append(Constants.TR_END);
-                cs.append(Constants.TR_END);
+                t.append(EmailConstants.TR_END);
+                cs.append(EmailConstants.TR_END);
                 if (flag) {
                     title = t.toString();
                 }
@@ -113,17 +111,18 @@ public class DefaultHTMLTemplate implements AlertTemplate {
      * get alert message which type is TEXT
      *
      * @param content message content
+     * @param showAll weather to show all
      * @return alert message
      */
-    private String getTextTypeMessage(String content) {
+    private String getTextTypeMessage(String content, boolean showAll) {
 
         if (StringUtils.isNotEmpty(content)) {
             ArrayNode list = JSONUtils.parseArray(content);
             StringBuilder contents = new StringBuilder(100);
             for (JsonNode jsonNode : list) {
-                contents.append(Constants.TR);
-                contents.append(Constants.TD).append(jsonNode.toString()).append(Constants.TD_END);
-                contents.append(Constants.TR_END);
+                contents.append(EmailConstants.TR);
+                contents.append(EmailConstants.TD).append(jsonNode.toString()).append(EmailConstants.TD_END);
+                contents.append(EmailConstants.TR_END);
             }
 
             return getMessageFromHtmlTemplate(null, contents.toString());
@@ -136,16 +135,16 @@ public class DefaultHTMLTemplate implements AlertTemplate {
     /**
      * get alert message from a html template
      *
-     * @param title message title
+     * @param title   message title
      * @param content message content
      * @return alert message which use html template
      */
     private String getMessageFromHtmlTemplate(String title, String content) {
 
-        checkNotNull(content);
-        String htmlTableThead = StringUtils.isEmpty(title) ? "" : String.format("<thead>%s</thead>%n", title);
+        requireNonNull(content, "content must not null");
+        String htmlTableThead = StringUtils.isEmpty(title) ? "" : String.format("<thead>%s</thead>\n", title);
 
-        return Constants.HTML_HEADER_PREFIX + htmlTableThead + content + Constants.TABLE_BODY_HTML_TAIL;
+        return EmailConstants.HTML_HEADER_PREFIX + htmlTableThead + content + EmailConstants.TABLE_BODY_HTML_TAIL;
     }
 
 }
diff --git a/dolphinscheduler-alert-plugin/dolphinscheduler-alert-email/src/test/java/org/apache/dolphinscheduler/plugin/alert/email/EmailAlertChannelFactoryTest.java b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-email/src/test/java/org/apache/dolphinscheduler/plugin/alert/email/EmailAlertChannelFactoryTest.java
new file mode 100644
index 0000000..977cd8f
--- /dev/null
+++ b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-email/src/test/java/org/apache/dolphinscheduler/plugin/alert/email/EmailAlertChannelFactoryTest.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.dolphinscheduler.plugin.alert.email;
+
+import org.apache.dolphinscheduler.spi.alert.AlertChannel;
+import org.apache.dolphinscheduler.spi.params.base.PluginParams;
+import org.apache.dolphinscheduler.spi.utils.JSONUtils;
+
+import java.util.List;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * EmailAlertChannelFactory Tester.
+ *
+ * @version 1.0
+ * @since <pre>Aug 20, 2020</pre>
+ */
+public class EmailAlertChannelFactoryTest {
+
+    @Before
+    public void before() throws Exception {
+    }
+
+    @After
+    public void after() throws Exception {
+    }
+
+    /**
+     * Method: getName()
+     */
+    @Test
+    public void testGetName() throws Exception {
+    }
+
+    /**
+     * Method: getParams()
+     */
+    @Test
+    public void testGetParams() throws Exception {
+        EmailAlertChannelFactory emailAlertChannelFactory = new EmailAlertChannelFactory();
+        List<PluginParams> params = emailAlertChannelFactory.getParams();
+        System.out.println(JSONUtils.toJsonString(params));
+        Assert.assertEquals(12, params.size());
+    }
+
+    /**
+     * Method: create()
+     */
+    @Test
+    public void testCreate() throws Exception {
+        EmailAlertChannelFactory emailAlertChannelFactory = new EmailAlertChannelFactory();
+        AlertChannel alertChannel = emailAlertChannelFactory.create();
+        Assert.assertNotNull(alertChannel);
+    }
+
+}
diff --git a/dolphinscheduler-alert-plugin/dolphinscheduler-alert-email/src/test/java/org/apache/dolphinscheduler/plugin/alert/email/EmailAlertChannelTest.java b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-email/src/test/java/org/apache/dolphinscheduler/plugin/alert/email/EmailAlertChannelTest.java
new file mode 100644
index 0000000..97a1013
--- /dev/null
+++ b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-email/src/test/java/org/apache/dolphinscheduler/plugin/alert/email/EmailAlertChannelTest.java
@@ -0,0 +1,161 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.dolphinscheduler.plugin.alert.email;
+
+import org.apache.dolphinscheduler.spi.alert.AlertConstants;
+import org.apache.dolphinscheduler.spi.alert.AlertData;
+import org.apache.dolphinscheduler.spi.alert.AlertInfo;
+import org.apache.dolphinscheduler.spi.alert.AlertResult;
+import org.apache.dolphinscheduler.spi.alert.ShowType;
+import org.apache.dolphinscheduler.spi.params.InputParam;
+import org.apache.dolphinscheduler.spi.params.PasswordParam;
+import org.apache.dolphinscheduler.spi.params.RadioParam;
+import org.apache.dolphinscheduler.spi.params.base.DataType;
+import org.apache.dolphinscheduler.spi.params.base.ParamsOptions;
+import org.apache.dolphinscheduler.spi.params.base.PluginParams;
+import org.apache.dolphinscheduler.spi.params.base.Validate;
+import org.apache.dolphinscheduler.spi.utils.JSONUtils;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * EmailAlertChannel Tester.
+ */
+public class EmailAlertChannelTest {
+
+    /**
+     * Method: process(AlertInfo info)
+     */
+    @Test
+    public void testProcess() {
+        EmailAlertChannel emailAlertChannel = new EmailAlertChannel();
+        AlertData alertData = new AlertData();
+        LinkedHashMap<String, Object> map1 = new LinkedHashMap<>();
+        map1.put("mysql service name", "mysql200");
+        map1.put("mysql address", "192.168.xx.xx");
+        map1.put("port", "3306");
+        map1.put("no index of number", "80");
+        map1.put("database client connections", "190");
+        List<LinkedHashMap<String, Object>> maps = new ArrayList<>();
+        maps.add(0, map1);
+        String mapjson = JSONUtils.toJsonString(maps);
+
+        alertData.setId(10)
+                .setContent(mapjson)
+                .setLog("10")
+                .setTitle("test");
+        AlertInfo alertInfo = new AlertInfo();
+        alertInfo.setAlertData(alertData);
+        alertInfo.setAlertParams(getEmailAlertParams());
+        AlertResult alertResult = emailAlertChannel.process(alertInfo);
+        Assert.assertNotNull(alertResult);
+        Assert.assertEquals("false", alertResult.getStatus());
+    }
+
+    public String getEmailAlertParams() {
+        List<PluginParams> paramsList = new ArrayList<>();
+        InputParam receivesParam = InputParam.newBuilder("receivers", "receivers")
+                .setValue("540957506@qq.com")
+                .addValidate(Validate.newBuilder().setRequired(true).build())
+                .build();
+
+        InputParam mailSmtpHost = InputParam.newBuilder("serverHost", "smtp.host")
+                .addValidate(Validate.newBuilder().setRequired(true).build())
+                .setValue("smtp.126.com")
+                .build();
+
+        InputParam mailSmtpPort = InputParam.newBuilder("serverPort", "smtp.port")
+                .addValidate(Validate.newBuilder()
+                        .setRequired(true)
+                        .setType(DataType.NUMBER.getDataType())
+                        .build())
+                .setValue(25)
+                .build();
+
+        InputParam mailSender = InputParam.newBuilder("sender", "sender")
+                .addValidate(Validate.newBuilder().setRequired(true).build())
+                .setValue("dolphinscheduler@126.com")
+                .build();
+
+        RadioParam enableSmtpAuth = RadioParam.newBuilder("enableSmtpAuth", "smtp.auth")
+                .addParamsOptions(new ParamsOptions("YES", true, false))
+                .addParamsOptions(new ParamsOptions("NO", false, false))
+                .addValidate(Validate.newBuilder().setRequired(true).build())
+                .setValue(false)
+                .build();
+
+        InputParam mailUser = InputParam.newBuilder("user", "user")
+                .setPlaceholder("if enable use authentication, you need input user")
+                .setValue("dolphinscheduler@126.com")
+                .build();
+
+        PasswordParam mailPassword = PasswordParam.newBuilder("passwd", "passwd")
+                .setPlaceholder("if enable use authentication, you need input password")
+                .setValue("escheduler123")
+                .build();
+
+        RadioParam enableTls = RadioParam.newBuilder("starttlsEnable", "starttls.enable")
+                .addParamsOptions(new ParamsOptions("YES", true, false))
+                .addParamsOptions(new ParamsOptions("NO", false, false))
+                .addValidate(Validate.newBuilder().setRequired(true).build())
+                .setValue(true)
+                .build();
+
+        RadioParam enableSsl = RadioParam.newBuilder("sslEnable", "smtp.ssl.enable")
+                .addParamsOptions(new ParamsOptions("YES", true, false))
+                .addParamsOptions(new ParamsOptions("NO", false, false))
+                .addValidate(Validate.newBuilder().setRequired(true).build())
+                .setValue(true)
+                .build();
+
+        InputParam sslTrust = InputParam.newBuilder("smtpSslTrust", "smtp.ssl.trust")
+                .addValidate(Validate.newBuilder().setRequired(true).build())
+                .setValue("smtp.126.com")
+                .build();
+
+        List<ParamsOptions> emailShowTypeList = new ArrayList<>();
+        emailShowTypeList.add(new ParamsOptions(ShowType.TABLE.getDescp(), ShowType.TABLE.getDescp(), false));
+        emailShowTypeList.add(new ParamsOptions(ShowType.TEXT.getDescp(), ShowType.TEXT.getDescp(), false));
+        emailShowTypeList.add(new ParamsOptions(ShowType.ATTACHMENT.getDescp(), ShowType.ATTACHMENT.getDescp(), false));
+        emailShowTypeList.add(new ParamsOptions(ShowType.TABLEATTACHMENT.getDescp(), ShowType.TABLEATTACHMENT.getDescp(), false));
+        RadioParam showType = RadioParam.newBuilder(AlertConstants.SHOW_TYPE, "showType")
+                .setParamsOptionsList(emailShowTypeList)
+                .setValue(ShowType.TABLE.getDescp())
+                .addValidate(Validate.newBuilder().setRequired(true).build())
+                .build();
+
+        paramsList.add(receivesParam);
+        paramsList.add(mailSmtpHost);
+        paramsList.add(mailSmtpPort);
+        paramsList.add(mailSender);
+        paramsList.add(enableSmtpAuth);
+        paramsList.add(mailUser);
+        paramsList.add(mailPassword);
+        paramsList.add(enableTls);
+        paramsList.add(enableSsl);
+        paramsList.add(sslTrust);
+        paramsList.add(showType);
+
+        return JSONUtils.toJsonString(paramsList);
+    }
+}
diff --git a/dolphinscheduler-alert/src/test/java/org/apache/dolphinscheduler/alert/utils/ExcelUtilsTest.java b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-email/src/test/java/org/apache/dolphinscheduler/plugin/alert/email/ExcelUtilsTest.java
similarity index 89%
rename from dolphinscheduler-alert/src/test/java/org/apache/dolphinscheduler/alert/utils/ExcelUtilsTest.java
rename to dolphinscheduler-alert-plugin/dolphinscheduler-alert-email/src/test/java/org/apache/dolphinscheduler/plugin/alert/email/ExcelUtilsTest.java
index c483325..5c8b195 100644
--- a/dolphinscheduler-alert/src/test/java/org/apache/dolphinscheduler/alert/utils/ExcelUtilsTest.java
+++ b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-email/src/test/java/org/apache/dolphinscheduler/plugin/alert/email/ExcelUtilsTest.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package org.apache.dolphinscheduler.alert.utils;
+package org.apache.dolphinscheduler.plugin.alert.email;
 
 import static org.junit.Assert.assertTrue;
 
@@ -63,7 +63,7 @@ public class ExcelUtilsTest {
 
         //Define dest file path
         String xlsFilePath = rootPath + System.getProperty("file.separator");
-        logger.info("XLS_FILE_PATH: " + xlsFilePath);
+        logger.info("xlsFilePath: " + xlsFilePath);
 
         //Define correctContent
         String correctContent = "[{\"name\":\"ds name\",\"value\":\"ds value\"}]";
@@ -78,7 +78,7 @@ public class ExcelUtilsTest {
         ExcelUtils.genExcelFile(correctContent, title, xlsFilePath);
 
         //Test file exists
-        File xlsFile = new File(xlsFilePath + Constants.SINGLE_SLASH + title + Constants.EXCEL_SUFFIX_XLS);
+        File xlsFile = new File(xlsFilePath + EmailConstants.SINGLE_SLASH + title + EmailConstants.EXCEL_SUFFIX_XLS);
         assertTrue(xlsFile.exists());
 
         //Expected RuntimeException
@@ -98,7 +98,7 @@ public class ExcelUtilsTest {
     @Test
     public void testGenExcelFileByCheckDir() {
         ExcelUtils.genExcelFile("[{\"a\": \"a\"},{\"a\": \"a\"}]", "t", "/tmp/xls");
-        File file = new File("/tmp/xls" + Constants.SINGLE_SLASH + "t" + Constants.EXCEL_SUFFIX_XLS);
+        File file = new File("/tmp/xls" + EmailConstants.SINGLE_SLASH + "t" + EmailConstants.EXCEL_SUFFIX_XLS);
         file.delete();
     }
-}
+}
\ No newline at end of file
diff --git a/dolphinscheduler-alert-plugin/dolphinscheduler-alert-email/src/test/java/org/apache/dolphinscheduler/plugin/alert/email/MailUtilsTest.java b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-email/src/test/java/org/apache/dolphinscheduler/plugin/alert/email/MailUtilsTest.java
new file mode 100644
index 0000000..e19c819
--- /dev/null
+++ b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-email/src/test/java/org/apache/dolphinscheduler/plugin/alert/email/MailUtilsTest.java
@@ -0,0 +1,136 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.dolphinscheduler.plugin.alert.email;
+
+import org.apache.dolphinscheduler.plugin.alert.email.template.AlertTemplate;
+import org.apache.dolphinscheduler.plugin.alert.email.template.DefaultHTMLTemplate;
+import org.apache.dolphinscheduler.spi.alert.AlertConstants;
+import org.apache.dolphinscheduler.spi.alert.ShowType;
+import org.apache.dolphinscheduler.spi.utils.JSONUtils;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ */
+public class MailUtilsTest {
+    private static final Logger logger = LoggerFactory.getLogger(MailUtilsTest.class);
+
+    private static Map<String, String> emailConfig = new HashMap<>();
+
+    private static AlertTemplate alertTemplate;
+
+    static MailSender mailSender;
+
+    @BeforeClass
+    public static void initEmailConfig() {
+        emailConfig.put(MailParamsConstants.NAME_MAIL_PROTOCOL, "smtp");
+        emailConfig.put(MailParamsConstants.NAME_MAIL_SMTP_HOST, "xxx.xxx.com");
+        emailConfig.put(MailParamsConstants.NAME_MAIL_SMTP_PORT, "25");
+        emailConfig.put(MailParamsConstants.NAME_MAIL_SENDER, "xxx1.xxx.com");
+        emailConfig.put(MailParamsConstants.NAME_MAIL_USER, "xxx2.xxx.com");
+        emailConfig.put(MailParamsConstants.NAME_MAIL_PASSWD, "111111");
+        emailConfig.put(MailParamsConstants.NAME_MAIL_SMTP_STARTTLS_ENABLE, "true");
+        emailConfig.put(MailParamsConstants.NAME_MAIL_SMTP_SSL_ENABLE, "false");
+        emailConfig.put(MailParamsConstants.NAME_MAIL_SMTP_SSL_TRUST, "false");
+        emailConfig.put(MailParamsConstants.NAME_PLUGIN_DEFAULT_EMAIL_RECEIVERS, "347801120@qq.com");
+        emailConfig.put(MailParamsConstants.NAME_PLUGIN_DEFAULT_EMAIL_RECEIVERCCS, "347801120@qq.com");
+        emailConfig.put(AlertConstants.SHOW_TYPE, ShowType.TEXT.getDescp());
+        alertTemplate = new DefaultHTMLTemplate();
+        mailSender = new MailSender(emailConfig);
+    }
+
+    @Test
+    public void testSendMails() {
+
+        String content = "[\"id:69\","
+                + "\"name:UserBehavior-0--1193959466\","
+                + "\"Job name: Start workflow\","
+                + "\"State: SUCCESS\","
+                + "\"Recovery:NO\","
+                + "\"Run time: 1\","
+                + "\"Start time: 2018-08-06 10:31:34.0\","
+                + "\"End time: 2018-08-06 10:31:49.0\","
+                + "\"Host: 192.168.xx.xx\","
+                + "\"Notify group :4\"]";
+
+        mailSender.sendMails(
+                "Mysql Exception",
+                content);
+    }
+
+    public String list2String() {
+
+        LinkedHashMap<String, Object> map1 = new LinkedHashMap<>();
+        map1.put("mysql service name", "mysql200");
+        map1.put("mysql address", "192.168.xx.xx");
+        map1.put("port", "3306");
+        map1.put("no index of number", "80");
+        map1.put("database client connections", "190");
+
+        LinkedHashMap<String, Object> map2 = new LinkedHashMap<>();
+        map2.put("mysql service name", "mysql210");
+        map2.put("mysql address", "192.168.xx.xx");
+        map2.put("port", "3306");
+        map2.put("no index of number", "10");
+        map2.put("database client connections", "90");
+
+        List<LinkedHashMap<String, Object>> maps = new ArrayList<>();
+        maps.add(0, map1);
+        maps.add(1, map2);
+        String mapjson = JSONUtils.toJsonString(maps);
+        logger.info(mapjson);
+
+        return mapjson;
+
+    }
+
+    @Test
+    public void testSendTableMail() {
+        String title = "Mysql Exception";
+        String content = list2String();
+        emailConfig.put(AlertConstants.SHOW_TYPE, ShowType.TABLE.getDescp());
+        mailSender = new MailSender(emailConfig);
+        mailSender.sendMails(title, content);
+    }
+
+    @Test
+    public void testAttachmentFile() throws Exception {
+        String content = list2String();
+        emailConfig.put(AlertConstants.SHOW_TYPE, ShowType.ATTACHMENT.getDescp());
+        mailSender = new MailSender(emailConfig);
+        mailSender.sendMails("gaojing", content);
+    }
+
+    @Test
+    public void testTableAttachmentFile() throws Exception {
+        String content = list2String();
+        emailConfig.put(AlertConstants.SHOW_TYPE, ShowType.TABLEATTACHMENT.getDescp());
+        mailSender = new MailSender(emailConfig);
+        mailSender.sendMails("gaojing", content);
+    }
+
+}
diff --git a/dolphinscheduler-alert-plugin/dolphinscheduler-alert-email/src/test/java/org/apache/dolphinscheduler/plugin/alert/email/template/DefaultHTMLTemplateTest.java b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-email/src/test/java/org/apache/dolphinscheduler/plugin/alert/email/template/DefaultHTMLTemplateTest.java
new file mode 100644
index 0000000..3d94196
--- /dev/null
+++ b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-email/src/test/java/org/apache/dolphinscheduler/plugin/alert/email/template/DefaultHTMLTemplateTest.java
@@ -0,0 +1,105 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.dolphinscheduler.plugin.alert.email.template;
+
+import static org.junit.Assert.assertEquals;
+
+import org.apache.dolphinscheduler.plugin.alert.email.EmailConstants;
+import org.apache.dolphinscheduler.spi.alert.ShowType;
+import org.apache.dolphinscheduler.spi.utils.JSONUtils;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * test class for DefaultHTMLTemplate
+ */
+public class DefaultHTMLTemplateTest {
+
+    private static final Logger logger = LoggerFactory.getLogger(DefaultHTMLTemplateTest.class);
+
+    /**
+     * only need test method GetMessageFromTemplate
+     */
+    @Test
+    public void testGetMessageFromTemplate() {
+
+        DefaultHTMLTemplate template = new DefaultHTMLTemplate();
+
+        String tableTypeMessage = template.getMessageFromTemplate(list2String(), ShowType.TABLE, true);
+
+        assertEquals(tableTypeMessage, generateMockTableTypeResultByHand());
+
+        String textTypeMessage = template.getMessageFromTemplate(list2String(), ShowType.TEXT, true);
+
+        assertEquals(textTypeMessage, generateMockTextTypeResultByHand());
+    }
+
+    /**
+     * generate some simulation data
+     */
+    private String list2String() {
+
+        LinkedHashMap<String, Object> map1 = new LinkedHashMap<>();
+        map1.put("mysql service name", "mysql200");
+        map1.put("mysql address", "192.168.xx.xx");
+        map1.put("database client connections", "190");
+        map1.put("port", "3306");
+        map1.put("no index of number", "80");
+
+        LinkedHashMap<String, Object> map2 = new LinkedHashMap<>();
+        map2.put("mysql service name", "mysql210");
+        map2.put("mysql address", "192.168.xx.xx");
+        map2.put("database client connections", "90");
+        map2.put("port", "3306");
+        map2.put("no index of number", "10");
+
+        List<LinkedHashMap<String, Object>> maps = new ArrayList<>();
+        maps.add(0, map1);
+        maps.add(1, map2);
+        String mapjson = JSONUtils.toJsonString(maps);
+        logger.info(mapjson);
+
+        return mapjson;
+    }
+
+    private String generateMockTableTypeResultByHand() {
+
+        return EmailConstants.HTML_HEADER_PREFIX
+                + "<thead>"
+                + "<tr><th>mysql service name</th><th>mysql address</th><th>database client connections</th><th>port</th><th>no index of number</th></tr>"
+                + "</thead>\n"
+                + "<tr><td>mysql200</td><td>192.168.xx.xx</td><td>190</td><td>3306</td><td>80</td></tr>"
+                + "<tr><td>mysql210</td><td>192.168.xx.xx</td><td>90</td><td>3306</td><td>10</td></tr>"
+                + EmailConstants.TABLE_BODY_HTML_TAIL;
+
+    }
+
+    private String generateMockTextTypeResultByHand() {
+
+        return EmailConstants.HTML_HEADER_PREFIX
+                + "<tr><td>{\"mysql service name\":\"mysql200\",\"mysql address\":\"192.168.xx.xx\",\"database client connections\":\"190\",\"port\":\"3306\",\"no index of number\":\"80\"}</td></tr>"
+                + "<tr><td>{\"mysql service name\":\"mysql210\",\"mysql address\":\"192.168.xx.xx\",\"database client connections\":\"90\",\"port\":\"3306\",\"no index of number\":\"10\"}</td></tr>"
+                + EmailConstants.TABLE_BODY_HTML_TAIL;
+    }
+}
diff --git a/dolphinscheduler-alert/pom.xml b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-http/pom.xml
similarity index 51%
copy from dolphinscheduler-alert/pom.xml
copy to dolphinscheduler-alert-plugin/dolphinscheduler-alert-http/pom.xml
index a44d101..5223730 100644
--- a/dolphinscheduler-alert/pom.xml
+++ b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-http/pom.xml
@@ -15,99 +15,62 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-    <modelVersion>4.0.0</modelVersion>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     <parent>
+        <artifactId>dolphinscheduler-alert-plugin</artifactId>
         <groupId>org.apache.dolphinscheduler</groupId>
-        <artifactId>dolphinscheduler</artifactId>
         <version>1.3.4-SNAPSHOT</version>
     </parent>
-    <artifactId>dolphinscheduler-alert</artifactId>
-    <name>${project.artifactId}</name>
-    <packaging>jar</packaging>
+    <modelVersion>4.0.0</modelVersion>
 
-    <properties>
-        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
-    </properties>
+    <artifactId>dolphinscheduler-alert-http</artifactId>
+    <packaging>dolphinscheduler-plugin</packaging>
     <dependencies>
         <dependency>
-            <groupId>junit</groupId>
-            <artifactId>junit</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.mockito</groupId>
-            <artifactId>mockito-core</artifactId>
-            <type>jar</type>
-            <scope>test</scope>
-        </dependency>
-
-        <dependency>
-            <groupId>org.powermock</groupId>
-            <artifactId>powermock-module-junit4</artifactId>
-            <scope>test</scope>
+            <groupId>org.apache.dolphinscheduler</groupId>
+            <artifactId>dolphinscheduler-spi</artifactId>
+            <scope>provided</scope>
         </dependency>
 
         <dependency>
-            <groupId>org.powermock</groupId>
-            <artifactId>powermock-api-mockito2</artifactId>
-            <scope>test</scope>
-            <exclusions>
-                <exclusion>
-                    <groupId>org.mockito</groupId>
-                    <artifactId>mockito-core</artifactId>
-                </exclusion>
-            </exclusions>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
         </dependency>
 
         <dependency>
-            <groupId>org.apache.commons</groupId>
-            <artifactId>commons-email</artifactId>
+            <groupId>ch.qos.logback</groupId>
+            <artifactId>logback-classic</artifactId>
         </dependency>
 
         <dependency>
-            <groupId>com.fasterxml.jackson.core</groupId>
-            <artifactId>jackson-core</artifactId>
+            <groupId>org.apache.httpcomponents</groupId>
+            <artifactId>httpclient</artifactId>
         </dependency>
 
         <dependency>
             <groupId>com.fasterxml.jackson.core</groupId>
             <artifactId>jackson-databind</artifactId>
+            <scope>provided</scope>
         </dependency>
 
         <dependency>
-            <groupId>org.slf4j</groupId>
-            <artifactId>slf4j-api</artifactId>
-        </dependency>
-
-        <dependency>
-            <groupId>com.google.guava</groupId>
-            <artifactId>guava</artifactId>
-        </dependency>
-
-        <dependency>
-            <groupId>ch.qos.logback</groupId>
-            <artifactId>logback-classic</artifactId>
-        </dependency>
-
-        <!--excel poi-->
-        <dependency>
-            <groupId>org.apache.poi</groupId>
-            <artifactId>poi</artifactId>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
         </dependency>
 
         <dependency>
-            <groupId>org.apache.dolphinscheduler</groupId>
-            <artifactId>dolphinscheduler-dao</artifactId>
-            <exclusions>
-                <exclusion>
-                    <artifactId>log4j-api</artifactId>
-                    <groupId>org.apache.logging.log4j</groupId>
-                </exclusion>
-            </exclusions>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-core</artifactId>
+            <type>jar</type>
+            <scope>test</scope>
         </dependency>
-
     </dependencies>
 
-</project>
+    <build>
+        <finalName>dolphinscheduler-alert-http-${project.version}</finalName>
+    </build>
+
+</project>
\ No newline at end of file
diff --git a/dolphinscheduler-common/src/main/java/org/apache/dolphinscheduler/common/plugin/PluginManager.java b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-http/src/main/java/org/apache/dolphinscheduler/plugin/alert/http/HttpAlertChannel.java
similarity index 50%
copy from dolphinscheduler-common/src/main/java/org/apache/dolphinscheduler/common/plugin/PluginManager.java
copy to dolphinscheduler-alert-plugin/dolphinscheduler-alert-http/src/main/java/org/apache/dolphinscheduler/plugin/alert/http/HttpAlertChannel.java
index f807884..27bc190 100644
--- a/dolphinscheduler-common/src/main/java/org/apache/dolphinscheduler/common/plugin/PluginManager.java
+++ b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-http/src/main/java/org/apache/dolphinscheduler/plugin/alert/http/HttpAlertChannel.java
@@ -14,20 +14,28 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dolphinscheduler.common.plugin;
 
-import org.apache.dolphinscheduler.plugin.api.AlertPlugin;
+package org.apache.dolphinscheduler.plugin.alert.http;
+
+import org.apache.dolphinscheduler.spi.alert.AlertChannel;
+import org.apache.dolphinscheduler.spi.alert.AlertData;
+import org.apache.dolphinscheduler.spi.alert.AlertInfo;
+import org.apache.dolphinscheduler.spi.alert.AlertResult;
+import org.apache.dolphinscheduler.spi.params.PluginParamsTransfer;
 
 import java.util.Map;
 
 /**
- * PluginManager
+ * http alert channel,use sms message to seed the alertInfo
  */
-public interface PluginManager {
-
-    AlertPlugin findOne(String name);
+public class HttpAlertChannel implements AlertChannel {
+    @Override
+    public AlertResult process(AlertInfo alertInfo) {
 
-    Map<String, AlertPlugin> findAll();
+        AlertData alertData = alertInfo.getAlertData();
+        String alertParams = alertInfo.getAlertParams();
+        Map<String, String> paramsMap = PluginParamsTransfer.getPluginParamsMap(alertParams);
 
-    void addPlugin(AlertPlugin plugin);
+        return new HttpSender(paramsMap).send(alertData.getContent());
+    }
 }
diff --git a/dolphinscheduler-alert-plugin/dolphinscheduler-alert-http/src/main/java/org/apache/dolphinscheduler/plugin/alert/http/HttpAlertChannelFactory.java b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-http/src/main/java/org/apache/dolphinscheduler/plugin/alert/http/HttpAlertChannelFactory.java
new file mode 100644
index 0000000..6b8dd30
--- /dev/null
+++ b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-http/src/main/java/org/apache/dolphinscheduler/plugin/alert/http/HttpAlertChannelFactory.java
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.dolphinscheduler.plugin.alert.http;
+
+import org.apache.dolphinscheduler.spi.alert.AlertChannel;
+import org.apache.dolphinscheduler.spi.alert.AlertChannelFactory;
+import org.apache.dolphinscheduler.spi.params.InputParam;
+import org.apache.dolphinscheduler.spi.params.base.PluginParams;
+import org.apache.dolphinscheduler.spi.params.base.Validate;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * http alert factory
+ */
+public class HttpAlertChannelFactory implements AlertChannelFactory {
+    @Override
+    public String getName() {
+        return "Http";
+    }
+
+    @Override
+    public List<PluginParams> getParams() {
+
+        InputParam url = InputParam.newBuilder(HttpAlertConstants.URL, HttpAlertConstants.URL)
+                .addValidate(Validate.newBuilder()
+                        .setRequired(true)
+                        .build())
+                .build();
+
+        InputParam headerParams = InputParam.newBuilder(HttpAlertConstants.HEADER_PARAMS, HttpAlertConstants.HEADER_PARAMS)
+                .addValidate(Validate.newBuilder()
+                        .setRequired(true)
+                        .build())
+                .build();
+
+        InputParam bodyParams = InputParam.newBuilder(HttpAlertConstants.BODY_PARAMS, HttpAlertConstants.BODY_PARAMS)
+                .addValidate(Validate.newBuilder()
+                        .setRequired(true)
+                        .build())
+                .build();
+
+        InputParam contentField = InputParam.newBuilder(HttpAlertConstants.CONTENT_FIELD, HttpAlertConstants.CONTENT_FIELD)
+                .addValidate(Validate.newBuilder()
+                        .setRequired(true)
+                        .build())
+                .build();
+
+        InputParam requestType = InputParam.newBuilder(HttpAlertConstants.REQUEST_TYPE, HttpAlertConstants.REQUEST_TYPE)
+                .addValidate(Validate.newBuilder()
+                        .setRequired(true)
+                        .build())
+                .build();
+
+        return Arrays.asList(url, requestType, headerParams, bodyParams, contentField);
+    }
+
+    @Override
+    public AlertChannel create() {
+        return new HttpAlertChannel();
+    }
+}
diff --git a/dolphinscheduler-common/src/main/java/org/apache/dolphinscheduler/common/plugin/PluginManager.java b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-http/src/main/java/org/apache/dolphinscheduler/plugin/alert/http/HttpAlertConstants.java
similarity index 65%
copy from dolphinscheduler-common/src/main/java/org/apache/dolphinscheduler/common/plugin/PluginManager.java
copy to dolphinscheduler-alert-plugin/dolphinscheduler-alert-http/src/main/java/org/apache/dolphinscheduler/plugin/alert/http/HttpAlertConstants.java
index f807884..965860d 100644
--- a/dolphinscheduler-common/src/main/java/org/apache/dolphinscheduler/common/plugin/PluginManager.java
+++ b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-http/src/main/java/org/apache/dolphinscheduler/plugin/alert/http/HttpAlertConstants.java
@@ -14,20 +14,22 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dolphinscheduler.common.plugin;
 
-import org.apache.dolphinscheduler.plugin.api.AlertPlugin;
+package org.apache.dolphinscheduler.plugin.alert.http;
 
-import java.util.Map;
+public class HttpAlertConstants {
 
-/**
- * PluginManager
- */
-public interface PluginManager {
+    private HttpAlertConstants() {
+    }
+
+    public static final String URL = "url";
+
+    public static final String HEADER_PARAMS = "headerParams";
+
+    public static final String BODY_PARAMS = "bodyParams";
 
-    AlertPlugin findOne(String name);
+    public static final String CONTENT_FIELD = "contentField";
 
-    Map<String, AlertPlugin> findAll();
+    public static final String REQUEST_TYPE = "requestType";
 
-    void addPlugin(AlertPlugin plugin);
 }
diff --git a/dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/template/AlertTemplateFactory.java b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-http/src/main/java/org/apache/dolphinscheduler/plugin/alert/http/HttpAlertPlugin.java
similarity index 63%
copy from dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/template/AlertTemplateFactory.java
copy to dolphinscheduler-alert-plugin/dolphinscheduler-alert-http/src/main/java/org/apache/dolphinscheduler/plugin/alert/http/HttpAlertPlugin.java
index d384631..973f161 100644
--- a/dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/template/AlertTemplateFactory.java
+++ b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-http/src/main/java/org/apache/dolphinscheduler/plugin/alert/http/HttpAlertPlugin.java
@@ -15,24 +15,20 @@
  * limitations under the License.
  */
 
-package org.apache.dolphinscheduler.alert.template;
+package org.apache.dolphinscheduler.plugin.alert.http;
 
-import org.apache.dolphinscheduler.alert.template.impl.DefaultHTMLTemplate;
+import org.apache.dolphinscheduler.spi.DolphinSchedulerPlugin;
+import org.apache.dolphinscheduler.spi.alert.AlertChannelFactory;
+
+import com.google.common.collect.ImmutableList;
 
 /**
- * the alert template factory
+ * http  alertPlugins
  */
-public class AlertTemplateFactory {
-
-    private AlertTemplateFactory() {
-    }
+public class HttpAlertPlugin implements DolphinSchedulerPlugin {
 
-    /**
-     * get a template from alert.properties conf file
-     *
-     * @return a template, default is DefaultHTMLTemplate
-     */
-    public static AlertTemplate getMessageTemplate() {
-        return new DefaultHTMLTemplate();
+    @Override
+    public Iterable<AlertChannelFactory> getAlertChannelFactorys() {
+        return ImmutableList.of(new HttpAlertChannelFactory());
     }
 }
diff --git a/dolphinscheduler-alert-plugin/dolphinscheduler-alert-http/src/main/java/org/apache/dolphinscheduler/plugin/alert/http/HttpSender.java b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-http/src/main/java/org/apache/dolphinscheduler/plugin/alert/http/HttpSender.java
new file mode 100644
index 0000000..32d3cdb
--- /dev/null
+++ b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-http/src/main/java/org/apache/dolphinscheduler/plugin/alert/http/HttpSender.java
@@ -0,0 +1,166 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.dolphinscheduler.plugin.alert.http;
+
+import org.apache.dolphinscheduler.spi.alert.AlertResult;
+import org.apache.dolphinscheduler.spi.utils.JSONUtils;
+import org.apache.dolphinscheduler.spi.utils.StringUtils;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.client.methods.HttpRequestBase;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.apache.http.util.EntityUtils;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+/**
+ * http  send message
+ */
+public class HttpSender {
+
+    public static final Logger logger = LoggerFactory.getLogger(HttpSender.class);
+
+    private String url;
+
+    private final String headerParams;
+
+    private final String bodyParams;
+
+    private final String contentField;
+
+    private final String requestType;
+
+    private HttpRequestBase httpRequest;
+
+
+    private static final String URL_SPLICE_CHAR = "?";
+
+    /**
+     * request type post
+     */
+    private static final String REQUEST_TYPE_POST = "POST";
+
+    /**
+     * request type get
+     */
+    private static final String REQUEST_TYPE_GET = "GET";
+
+    private static final String DEFAULT_CHARSET = "utf-8";
+
+    public HttpSender(Map<String, String> paramsMap) {
+
+        url = paramsMap.get(HttpAlertConstants.URL);
+        headerParams = paramsMap.get(HttpAlertConstants.HEADER_PARAMS);
+        bodyParams = paramsMap.get(HttpAlertConstants.BODY_PARAMS);
+        contentField = paramsMap.get(HttpAlertConstants.CONTENT_FIELD);
+        requestType = paramsMap.get(HttpAlertConstants.REQUEST_TYPE);
+    }
+
+    public AlertResult send(String msg) {
+
+        AlertResult alertResult = new AlertResult();
+
+        createHttpRequest(msg);
+
+        if (httpRequest == null) {
+            alertResult.setStatus("false");
+            alertResult.setMessage("Request types are not supported");
+            return alertResult;
+        }
+
+        try {
+            CloseableHttpClient httpClient = HttpClientBuilder.create().build();
+            CloseableHttpResponse response = httpClient.execute(httpRequest);
+            HttpEntity entity = response.getEntity();
+            String resp = EntityUtils.toString(entity, DEFAULT_CHARSET);
+            alertResult.setStatus("true");
+            alertResult.setMessage(resp);
+        } catch (Exception e) {
+            logger.error("send http alert msg  exception : {}", e.getMessage());
+            alertResult.setStatus("false");
+            alertResult.setMessage("send http request  alert fail.");
+        }
+
+        return alertResult;
+    }
+
+    private void createHttpRequest(String msg) {
+
+        if (REQUEST_TYPE_POST.equals(requestType)) {
+            httpRequest = new HttpPost(url);
+            //POST request add param in request body
+            setMsgInRequestBody(msg);
+        } else if (REQUEST_TYPE_GET.equals(requestType)) {
+            //GET request add param in url
+            setMsgInUrl(msg);
+            httpRequest = new HttpGet(url);
+        }
+        setHeader();
+    }
+
+    /**
+     * add msg param in url
+     */
+    private void setMsgInUrl(String msg) {
+
+        if (StringUtils.isNotBlank(contentField)) {
+            String type = "&";
+            //check splice char is & or ?
+            if (!url.contains(URL_SPLICE_CHAR)) {
+                type = URL_SPLICE_CHAR;
+            }
+            url = String.format("%s%s%s=%s", url, type, contentField, msg);
+        }
+    }
+
+    /**
+     * set header params
+     */
+    private void setHeader() {
+
+        if (httpRequest == null) {
+            return;
+        }
+
+        HashMap<String, Object> map = JSONUtils.parseObject(headerParams, HashMap.class);
+        for (Map.Entry<String, Object> entry : map.entrySet()) {
+            httpRequest.setHeader(entry.getKey(), String.valueOf(entry.getValue()));
+        }
+    }
+
+    /**
+     * set body params
+     */
+    private String setMsgInRequestBody(String msg) {
+        ObjectNode objectNode = JSONUtils.parseObject(bodyParams);
+        //set msg content field
+        objectNode.put(contentField, msg);
+        return objectNode.toString();
+    }
+
+}
diff --git a/dolphinscheduler-plugin-api/src/test/java/org/apache/dolphinscheduler/plugin/model/AlertInfoTest.java b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-http/src/test/java/org/apache/dolphinscheduler/plugin/alert/http/HttpAlertChannelFactoryTest.java
similarity index 53%
copy from dolphinscheduler-plugin-api/src/test/java/org/apache/dolphinscheduler/plugin/model/AlertInfoTest.java
copy to dolphinscheduler-alert-plugin/dolphinscheduler-alert-http/src/test/java/org/apache/dolphinscheduler/plugin/alert/http/HttpAlertChannelFactoryTest.java
index 13eb595..25181eb 100644
--- a/dolphinscheduler-plugin-api/src/test/java/org/apache/dolphinscheduler/plugin/model/AlertInfoTest.java
+++ b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-http/src/test/java/org/apache/dolphinscheduler/plugin/alert/http/HttpAlertChannelFactoryTest.java
@@ -14,41 +14,41 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dolphinscheduler.plugin.model;
 
-import org.junit.Before;
-import org.junit.Test;
+package org.apache.dolphinscheduler.plugin.alert.http;
 
-import java.util.HashMap;
-import java.util.Map;
+import org.apache.dolphinscheduler.spi.alert.AlertChannel;
+import org.apache.dolphinscheduler.spi.params.base.PluginParams;
 
-import static org.junit.Assert.*;
+import java.util.List;
 
-public class AlertInfoTest {
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
 
-    private AlertInfo alertInfo;
+/**
+ * HttpAlertChannelFactory UT
+ */
+public class HttpAlertChannelFactoryTest {
+
+    private HttpAlertChannelFactory httpAlertChannelFactory;
 
     @Before
-    public void before() {
-        alertInfo = new AlertInfo();
+    public void init() {
+        httpAlertChannelFactory = new HttpAlertChannelFactory();
     }
 
     @Test
-    public void getAlertProps() {
-        Map<String, Object> map = new HashMap<>();
-        alertInfo.setAlertProps(map);
-        assertNotNull(alertInfo.getAlertProps());
-    }
+    public void getParamsTest() {
 
-    @Test
-    public void getProp() {
-        alertInfo.addProp("k", "v");
-        assertEquals("v", alertInfo.getProp("k"));
+        List<PluginParams> pluginParamsList = httpAlertChannelFactory.getParams();
+        Assert.assertEquals(5, pluginParamsList.size());
     }
 
     @Test
-    public void getAlertData() {
-        alertInfo.setAlertData(new AlertData());
-        assertNotNull(alertInfo.getAlertData());
+    public void createTest() {
+        AlertChannel alertChannel = httpAlertChannelFactory.create();
+        Assert.assertNotNull(alertChannel);
     }
-}
\ No newline at end of file
+
+}
diff --git a/dolphinscheduler-alert-plugin/dolphinscheduler-alert-http/src/test/java/org/apache/dolphinscheduler/plugin/alert/http/HttpAlertChannelTest.java b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-http/src/test/java/org/apache/dolphinscheduler/plugin/alert/http/HttpAlertChannelTest.java
new file mode 100644
index 0000000..31a438b
--- /dev/null
+++ b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-http/src/test/java/org/apache/dolphinscheduler/plugin/alert/http/HttpAlertChannelTest.java
@@ -0,0 +1,104 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.dolphinscheduler.plugin.alert.http;
+
+import org.apache.dolphinscheduler.spi.alert.AlertData;
+import org.apache.dolphinscheduler.spi.alert.AlertInfo;
+import org.apache.dolphinscheduler.spi.alert.AlertResult;
+import org.apache.dolphinscheduler.spi.params.InputParam;
+import org.apache.dolphinscheduler.spi.params.base.PluginParams;
+import org.apache.dolphinscheduler.spi.params.base.Validate;
+import org.apache.dolphinscheduler.spi.utils.JSONUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * HttpAlertChannel UT
+ */
+public class HttpAlertChannelTest {
+
+    @Test
+    public void processTest() {
+
+        HttpAlertChannel alertChannel = new HttpAlertChannel();
+        AlertInfo alertInfo = new AlertInfo();
+        AlertData alertData = new AlertData();
+        alertData.setContent("Fault tolerance warning");
+        alertInfo.setAlertData(alertData);
+        AlertResult alertResult = alertChannel.process(alertInfo);
+        Assert.assertEquals("Request types are not supported", alertResult.getMessage());
+    }
+
+    @Test
+    public void processTest2() {
+
+        HttpAlertChannel alertChannel = new HttpAlertChannel();
+        AlertInfo alertInfo = new AlertInfo();
+        AlertData alertData = new AlertData();
+        alertData.setContent("Fault tolerance warning");
+        alertInfo.setAlertData(alertData);
+        alertInfo.setAlertParams(getParams());
+        AlertResult alertResult = alertChannel.process(alertInfo);
+        Assert.assertEquals("true", alertResult.getStatus());
+    }
+
+    /**
+     * create params
+     */
+    private String getParams() {
+
+        List<PluginParams> paramsList = new ArrayList<>();
+        InputParam urlParam = InputParam.newBuilder("url", "url")
+                .setValue("http://www.baidu.com")
+                .addValidate(Validate.newBuilder().setRequired(true).build())
+                .build();
+
+        InputParam headerParams = InputParam.newBuilder("headerParams", "headerParams")
+                .addValidate(Validate.newBuilder().setRequired(true).build())
+                .setValue("{\"Content-Type\":\"application/json\"}")
+                .build();
+
+        InputParam bodyParams = InputParam.newBuilder("bodyParams", "bodyParams")
+                .addValidate(Validate.newBuilder().setRequired(true).build())
+                .setValue("{\"number\":\"13457654323\"}")
+                .build();
+
+        InputParam content = InputParam.newBuilder("contentField", "contentField")
+                .setValue("content")
+                .addValidate(Validate.newBuilder().setRequired(true).build())
+                .build();
+
+        InputParam requestType = InputParam.newBuilder("requestType", "requestType")
+                .setValue("POST")
+                .addValidate(Validate.newBuilder().setRequired(true).build())
+                .build();
+
+        paramsList.add(urlParam);
+        paramsList.add(headerParams);
+        paramsList.add(bodyParams);
+        paramsList.add(content);
+        paramsList.add(requestType);
+
+        return JSONUtils.toJsonString(paramsList);
+    }
+
+}
diff --git a/dolphinscheduler-plugin-api/src/main/java/org/apache/dolphinscheduler/plugin/model/PluginName.java b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-http/src/test/java/org/apache/dolphinscheduler/plugin/alert/http/HttpAlertPluginTest.java
similarity index 62%
copy from dolphinscheduler-plugin-api/src/main/java/org/apache/dolphinscheduler/plugin/model/PluginName.java
copy to dolphinscheduler-alert-plugin/dolphinscheduler-alert-http/src/test/java/org/apache/dolphinscheduler/plugin/alert/http/HttpAlertPluginTest.java
index 8066e45..7dac686 100644
--- a/dolphinscheduler-plugin-api/src/main/java/org/apache/dolphinscheduler/plugin/model/PluginName.java
+++ b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-http/src/test/java/org/apache/dolphinscheduler/plugin/alert/http/HttpAlertPluginTest.java
@@ -14,32 +14,25 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dolphinscheduler.plugin.model;
 
-/**
- * PluginName
- */
-public class PluginName {
+package org.apache.dolphinscheduler.plugin.alert.http;
 
-    private String chinese;
+import org.apache.dolphinscheduler.spi.alert.AlertChannelFactory;
 
-    private String english;
+import org.junit.Assert;
+import org.junit.Test;
 
-    public String getChinese() {
-        return chinese;
-    }
+/**
+ * HttpAlertPlugin UT
+ */
+public class HttpAlertPluginTest {
 
-    public PluginName setChinese(String chinese) {
-        this.chinese = chinese;
-        return this;
-    }
+    @Test
+    public void getAlertChannelFactorysTest() {
 
-    public String getEnglish() {
-        return english;
-    }
+        HttpAlertPlugin httpAlertPlugin = new HttpAlertPlugin();
+        Iterable<AlertChannelFactory> alertChannelFactorys = httpAlertPlugin.getAlertChannelFactorys();
+        Assert.assertNotNull(alertChannelFactorys);
 
-    public PluginName setEnglish(String english) {
-        this.english = english;
-        return this;
     }
 }
diff --git a/dolphinscheduler-plugin-api/src/test/java/org/apache/dolphinscheduler/plugin/model/AlertInfoTest.java b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-http/src/test/java/org/apache/dolphinscheduler/plugin/alert/http/HttpSenderTest.java
similarity index 50%
copy from dolphinscheduler-plugin-api/src/test/java/org/apache/dolphinscheduler/plugin/model/AlertInfoTest.java
copy to dolphinscheduler-alert-plugin/dolphinscheduler-alert-http/src/test/java/org/apache/dolphinscheduler/plugin/alert/http/HttpSenderTest.java
index 13eb595..d59c4d4 100644
--- a/dolphinscheduler-plugin-api/src/test/java/org/apache/dolphinscheduler/plugin/model/AlertInfoTest.java
+++ b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-http/src/test/java/org/apache/dolphinscheduler/plugin/alert/http/HttpSenderTest.java
@@ -14,41 +14,33 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dolphinscheduler.plugin.model;
 
-import org.junit.Before;
-import org.junit.Test;
+package org.apache.dolphinscheduler.plugin.alert.http;
+
+import org.apache.dolphinscheduler.spi.alert.AlertResult;
 
 import java.util.HashMap;
 import java.util.Map;
 
-import static org.junit.Assert.*;
-
-public class AlertInfoTest {
-
-    private AlertInfo alertInfo;
-
-    @Before
-    public void before() {
-        alertInfo = new AlertInfo();
-    }
-
-    @Test
-    public void getAlertProps() {
-        Map<String, Object> map = new HashMap<>();
-        alertInfo.setAlertProps(map);
-        assertNotNull(alertInfo.getAlertProps());
-    }
+import org.junit.Assert;
+import org.junit.Test;
 
-    @Test
-    public void getProp() {
-        alertInfo.addProp("k", "v");
-        assertEquals("v", alertInfo.getProp("k"));
-    }
+/**
+ * HttpSender UT
+ */
+public class HttpSenderTest {
 
     @Test
-    public void getAlertData() {
-        alertInfo.setAlertData(new AlertData());
-        assertNotNull(alertInfo.getAlertData());
+    public void sendTest() {
+
+        Map<String, String> paramsMap = new HashMap<>();
+        paramsMap.put(HttpAlertConstants.URL, "http://www.baidu.com");
+        paramsMap.put(HttpAlertConstants.REQUEST_TYPE, "POST");
+        paramsMap.put(HttpAlertConstants.HEADER_PARAMS, "{\"Content-Type\":\"application/json\"}");
+        paramsMap.put(HttpAlertConstants.BODY_PARAMS, "{\"number\":\"13457654323\"}");
+        paramsMap.put(HttpAlertConstants.CONTENT_FIELD, "content");
+        HttpSender httpSender = new HttpSender(paramsMap);
+        AlertResult alertResult = httpSender.send("Fault tolerance warning");
+        Assert.assertEquals("true", alertResult.getStatus());
     }
-}
\ No newline at end of file
+}
diff --git a/dolphinscheduler-alert/pom.xml b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-script/pom.xml
similarity index 52%
copy from dolphinscheduler-alert/pom.xml
copy to dolphinscheduler-alert-plugin/dolphinscheduler-alert-script/pom.xml
index a44d101..8c35b3c 100644
--- a/dolphinscheduler-alert/pom.xml
+++ b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-script/pom.xml
@@ -15,65 +15,36 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-    <modelVersion>4.0.0</modelVersion>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     <parent>
+        <artifactId>dolphinscheduler-alert-plugin</artifactId>
         <groupId>org.apache.dolphinscheduler</groupId>
-        <artifactId>dolphinscheduler</artifactId>
         <version>1.3.4-SNAPSHOT</version>
     </parent>
-    <artifactId>dolphinscheduler-alert</artifactId>
-    <name>${project.artifactId}</name>
-    <packaging>jar</packaging>
-
-    <properties>
-        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
-    </properties>
-    <dependencies>
-        <dependency>
-            <groupId>junit</groupId>
-            <artifactId>junit</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.mockito</groupId>
-            <artifactId>mockito-core</artifactId>
-            <type>jar</type>
-            <scope>test</scope>
-        </dependency>
+    <modelVersion>4.0.0</modelVersion>
 
-        <dependency>
-            <groupId>org.powermock</groupId>
-            <artifactId>powermock-module-junit4</artifactId>
-            <scope>test</scope>
-        </dependency>
+    <groupId>org.apache.dolphinscheduler</groupId>
+    <artifactId>dolphinscheduler-alert-script</artifactId>
+    <packaging>dolphinscheduler-plugin</packaging>
 
-        <dependency>
-            <groupId>org.powermock</groupId>
-            <artifactId>powermock-api-mockito2</artifactId>
-            <scope>test</scope>
-            <exclusions>
-                <exclusion>
-                    <groupId>org.mockito</groupId>
-                    <artifactId>mockito-core</artifactId>
-                </exclusion>
-            </exclusions>
-        </dependency>
+    <dependencies>
 
         <dependency>
-            <groupId>org.apache.commons</groupId>
-            <artifactId>commons-email</artifactId>
+            <groupId>org.apache.dolphinscheduler</groupId>
+            <artifactId>dolphinscheduler-spi</artifactId>
+            <scope>provided</scope>
         </dependency>
 
         <dependency>
-            <groupId>com.fasterxml.jackson.core</groupId>
-            <artifactId>jackson-core</artifactId>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
         </dependency>
 
         <dependency>
-            <groupId>com.fasterxml.jackson.core</groupId>
-            <artifactId>jackson-databind</artifactId>
+            <groupId>ch.qos.logback</groupId>
+            <artifactId>logback-classic</artifactId>
         </dependency>
 
         <dependency>
@@ -82,32 +53,27 @@
         </dependency>
 
         <dependency>
-            <groupId>com.google.guava</groupId>
-            <artifactId>guava</artifactId>
-        </dependency>
-
-        <dependency>
-            <groupId>ch.qos.logback</groupId>
-            <artifactId>logback-classic</artifactId>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-annotations</artifactId>
+            <scope>provided</scope>
         </dependency>
 
-        <!--excel poi-->
         <dependency>
-            <groupId>org.apache.poi</groupId>
-            <artifactId>poi</artifactId>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
         </dependency>
 
         <dependency>
-            <groupId>org.apache.dolphinscheduler</groupId>
-            <artifactId>dolphinscheduler-dao</artifactId>
-            <exclusions>
-                <exclusion>
-                    <artifactId>log4j-api</artifactId>
-                    <groupId>org.apache.logging.log4j</groupId>
-                </exclusion>
-            </exclusions>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-core</artifactId>
+            <type>jar</type>
+            <scope>test</scope>
         </dependency>
-
     </dependencies>
 
-</project>
+    <build>
+        <finalName>dolphinscheduler-alert-script-${project.version}</finalName>
+    </build>
+
+</project>
\ No newline at end of file
diff --git a/dolphinscheduler-plugin-api/src/main/java/org/apache/dolphinscheduler/plugin/spi/AlertPluginProvider.java b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-script/src/main/java/org/apache/dolphinscheduler/plugin/alert/script/OSUtils.java
similarity index 73%
copy from dolphinscheduler-plugin-api/src/main/java/org/apache/dolphinscheduler/plugin/spi/AlertPluginProvider.java
copy to dolphinscheduler-alert-plugin/dolphinscheduler-alert-script/src/main/java/org/apache/dolphinscheduler/plugin/alert/script/OSUtils.java
index 594636f..acf3e31 100644
--- a/dolphinscheduler-plugin-api/src/main/java/org/apache/dolphinscheduler/plugin/spi/AlertPluginProvider.java
+++ b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-script/src/main/java/org/apache/dolphinscheduler/plugin/alert/script/OSUtils.java
@@ -14,20 +14,19 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dolphinscheduler.plugin.spi;
 
-import org.apache.dolphinscheduler.plugin.api.AlertPlugin;
+package org.apache.dolphinscheduler.plugin.alert.script;
 
 /**
- * PluginProvider
+ * OSUtils
  */
-public interface AlertPluginProvider {
+public class OSUtils {
 
-    /**
-     * create an alert plugin
-     *
-     * @return an alert plugin
-     */
-    AlertPlugin createPlugin();
+    public OSUtils() {
+        throw new UnsupportedOperationException("Construct OSUtils");
+    }
 
+    static Boolean isWindows() {
+        return System.getProperty("os.name").startsWith("Windows");
+    }
 }
diff --git a/dolphinscheduler-alert-plugin/dolphinscheduler-alert-script/src/main/java/org/apache/dolphinscheduler/plugin/alert/script/ProcessUtils.java b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-script/src/main/java/org/apache/dolphinscheduler/plugin/alert/script/ProcessUtils.java
new file mode 100644
index 0000000..d63a350
--- /dev/null
+++ b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-script/src/main/java/org/apache/dolphinscheduler/plugin/alert/script/ProcessUtils.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.dolphinscheduler.plugin.alert.script;
+
+import java.io.IOException;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * ProcessUtils
+ */
+public class ProcessUtils {
+
+    private static final Logger logger = LoggerFactory.getLogger(ProcessUtils.class);
+
+    private ProcessUtils() {
+        throw new IllegalStateException("Utility class");
+    }
+
+    /**
+     * executeScript
+     *
+     * @param cmd cmd params
+     * @return exit code
+     */
+    static Integer executeScript(String... cmd) {
+
+        int exitCode = -1;
+
+        ProcessBuilder processBuilder = new ProcessBuilder(cmd);
+        try {
+            Process process = processBuilder.start();
+            StreamGobbler inputStreamGobbler = new StreamGobbler(process.getInputStream());
+            StreamGobbler errorStreamGobbler = new StreamGobbler(process.getErrorStream());
+
+            inputStreamGobbler.start();
+            errorStreamGobbler.start();
+            return process.waitFor();
+        } catch (IOException | InterruptedException e) {
+            logger.error("execute alert script error {}", e.getMessage());
+            Thread.currentThread().interrupt();
+        }
+
+        return exitCode;
+    }
+}
diff --git a/dolphinscheduler-plugin-api/src/main/java/org/apache/dolphinscheduler/plugin/api/AlertPlugin.java b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-script/src/main/java/org/apache/dolphinscheduler/plugin/alert/script/ScriptAlertChannel.java
similarity index 51%
copy from dolphinscheduler-plugin-api/src/main/java/org/apache/dolphinscheduler/plugin/api/AlertPlugin.java
copy to dolphinscheduler-alert-plugin/dolphinscheduler-alert-script/src/main/java/org/apache/dolphinscheduler/plugin/alert/script/ScriptAlertChannel.java
index deb7ff6..2a00212 100644
--- a/dolphinscheduler-plugin-api/src/main/java/org/apache/dolphinscheduler/plugin/api/AlertPlugin.java
+++ b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-script/src/main/java/org/apache/dolphinscheduler/plugin/alert/script/ScriptAlertChannel.java
@@ -14,32 +14,27 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dolphinscheduler.plugin.api;
 
-import org.apache.dolphinscheduler.plugin.model.AlertInfo;
-import org.apache.dolphinscheduler.plugin.model.PluginName;
+package org.apache.dolphinscheduler.plugin.alert.script;
+
+import org.apache.dolphinscheduler.spi.alert.AlertChannel;
+import org.apache.dolphinscheduler.spi.alert.AlertData;
+import org.apache.dolphinscheduler.spi.alert.AlertInfo;
+import org.apache.dolphinscheduler.spi.alert.AlertResult;
+import org.apache.dolphinscheduler.spi.params.PluginParamsTransfer;
 
 import java.util.Map;
 
 /**
- * Plugin
+ * ScriptAlertChannel
  */
-public interface AlertPlugin {
-
-    /**
-     * Get alert plugin id
-     *
-     * @return alert plugin id, which should be unique
-     */
-    String getId();
-
-    /**
-     * Get alert plugin name, which will show in front end portal
-     *
-     * @return plugin name
-     */
-    PluginName getName();
-
-    Map<String, Object> process(AlertInfo info);
+public class ScriptAlertChannel implements AlertChannel {
 
+    @Override
+    public AlertResult process(AlertInfo alertinfo) {
+        AlertData alertData = alertinfo.getAlertData();
+        String alertParams = alertinfo.getAlertParams();
+        Map<String, String> paramsMap = PluginParamsTransfer.getPluginParamsMap(alertParams);
+        return new ScriptSender(paramsMap).sendScriptAlert(alertData.getTitle());
+    }
 }
diff --git a/dolphinscheduler-alert-plugin/dolphinscheduler-alert-script/src/main/java/org/apache/dolphinscheduler/plugin/alert/script/ScriptAlertChannelFactory.java b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-script/src/main/java/org/apache/dolphinscheduler/plugin/alert/script/ScriptAlertChannelFactory.java
new file mode 100644
index 0000000..70ab763
--- /dev/null
+++ b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-script/src/main/java/org/apache/dolphinscheduler/plugin/alert/script/ScriptAlertChannelFactory.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.dolphinscheduler.plugin.alert.script;
+
+import org.apache.dolphinscheduler.spi.alert.AlertChannel;
+import org.apache.dolphinscheduler.spi.alert.AlertChannelFactory;
+import org.apache.dolphinscheduler.spi.params.InputParam;
+import org.apache.dolphinscheduler.spi.params.RadioParam;
+import org.apache.dolphinscheduler.spi.params.base.ParamsOptions;
+import org.apache.dolphinscheduler.spi.params.base.PluginParams;
+import org.apache.dolphinscheduler.spi.params.base.Validate;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * ScriptAlertChannelFactory
+ */
+public class ScriptAlertChannelFactory implements AlertChannelFactory {
+
+    @Override
+    public String getName() {
+        return "Script";
+    }
+
+    @Override
+    public List<PluginParams> getParams() {
+
+        InputParam scriptUserParam = InputParam.newBuilder(ScriptParamsConstants.NAME_SCRIPT_USER_PARAMS, ScriptParamsConstants.SCRIPT_USER_PARAMS)
+                .addValidate(Validate.newBuilder()
+                        .setRequired(false)
+                        .build())
+                .setPlaceholder("please enter your custom parameters, which will be passed to you when calling your script")
+                .build();
+        // need check file type and file exist
+        InputParam scriptPathParam = InputParam.newBuilder(ScriptParamsConstants.NAME_SCRIPT_PATH, ScriptParamsConstants.SCRIPT_PATH)
+                .addValidate(Validate.newBuilder()
+                        .setRequired(true)
+                        .build())
+                .setPlaceholder("please upload the file to the disk directory of the alert server, and ensure that the path is absolute and has the corresponding access rights")
+                .build();
+
+        RadioParam scriptTypeParams = RadioParam.newBuilder(ScriptParamsConstants.NAME_SCRIPT_TYPE, ScriptParamsConstants.SCRIPT_TYPE)
+                .addParamsOptions(new ParamsOptions(ScriptType.SHELL.getDescp(), ScriptType.SHELL.getCode(), false))
+                .setValue(ScriptType.SHELL.getCode())
+                .addValidate(Validate.newBuilder().setRequired(true).build())
+                .build();
+
+        return Arrays.asList(scriptUserParam, scriptPathParam, scriptTypeParams);
+    }
+
+    @Override
+    public AlertChannel create() {
+        return new ScriptAlertChannel();
+    }
+}
diff --git a/dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/template/AlertTemplateFactory.java b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-script/src/main/java/org/apache/dolphinscheduler/plugin/alert/script/ScriptAlertPlugin.java
similarity index 63%
copy from dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/template/AlertTemplateFactory.java
copy to dolphinscheduler-alert-plugin/dolphinscheduler-alert-script/src/main/java/org/apache/dolphinscheduler/plugin/alert/script/ScriptAlertPlugin.java
index d384631..f2ba0e8 100644
--- a/dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/template/AlertTemplateFactory.java
+++ b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-script/src/main/java/org/apache/dolphinscheduler/plugin/alert/script/ScriptAlertPlugin.java
@@ -15,24 +15,21 @@
  * limitations under the License.
  */
 
-package org.apache.dolphinscheduler.alert.template;
+package org.apache.dolphinscheduler.plugin.alert.script;
 
-import org.apache.dolphinscheduler.alert.template.impl.DefaultHTMLTemplate;
+import org.apache.dolphinscheduler.spi.DolphinSchedulerPlugin;
+import org.apache.dolphinscheduler.spi.alert.AlertChannelFactory;
+
+import com.google.common.collect.ImmutableList;
 
 /**
- * the alert template factory
+ * ScriptAlertPlugin
  */
-public class AlertTemplateFactory {
+public class ScriptAlertPlugin implements DolphinSchedulerPlugin {
 
-    private AlertTemplateFactory() {
+    @Override
+    public Iterable<AlertChannelFactory> getAlertChannelFactorys() {
+        return ImmutableList.of(new ScriptAlertChannelFactory());
     }
 
-    /**
-     * get a template from alert.properties conf file
-     *
-     * @return a template, default is DefaultHTMLTemplate
-     */
-    public static AlertTemplate getMessageTemplate() {
-        return new DefaultHTMLTemplate();
-    }
 }
diff --git a/dolphinscheduler-plugin-api/src/main/java/org/apache/dolphinscheduler/plugin/model/PluginName.java b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-script/src/main/java/org/apache/dolphinscheduler/plugin/alert/script/ScriptParamsConstants.java
similarity index 60%
copy from dolphinscheduler-plugin-api/src/main/java/org/apache/dolphinscheduler/plugin/model/PluginName.java
copy to dolphinscheduler-alert-plugin/dolphinscheduler-alert-script/src/main/java/org/apache/dolphinscheduler/plugin/alert/script/ScriptParamsConstants.java
index 8066e45..98e4eee 100644
--- a/dolphinscheduler-plugin-api/src/main/java/org/apache/dolphinscheduler/plugin/model/PluginName.java
+++ b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-script/src/main/java/org/apache/dolphinscheduler/plugin/alert/script/ScriptParamsConstants.java
@@ -14,32 +14,27 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dolphinscheduler.plugin.model;
+
+package org.apache.dolphinscheduler.plugin.alert.script;
 
 /**
- * PluginName
+ * ScriptParamsConstants
  */
-public class PluginName {
+public class ScriptParamsConstants {
 
-    private String chinese;
+    public ScriptParamsConstants() {
+        throw new IllegalStateException("Utility class");
+    }
 
-    private String english;
+    static final String SCRIPT_TYPE = "type";
 
-    public String getChinese() {
-        return chinese;
-    }
+    static final String NAME_SCRIPT_TYPE = "type";
 
-    public PluginName setChinese(String chinese) {
-        this.chinese = chinese;
-        return this;
-    }
+    static final String SCRIPT_PATH = "path";
 
-    public String getEnglish() {
-        return english;
-    }
+    static final String NAME_SCRIPT_PATH = "path";
 
-    public PluginName setEnglish(String english) {
-        this.english = english;
-        return this;
-    }
+    static final String SCRIPT_USER_PARAMS = "user.params";
+
+    static final String NAME_SCRIPT_USER_PARAMS = "userParams";
 }
diff --git a/dolphinscheduler-alert-plugin/dolphinscheduler-alert-script/src/main/java/org/apache/dolphinscheduler/plugin/alert/script/ScriptSender.java b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-script/src/main/java/org/apache/dolphinscheduler/plugin/alert/script/ScriptSender.java
new file mode 100644
index 0000000..377c318
--- /dev/null
+++ b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-script/src/main/java/org/apache/dolphinscheduler/plugin/alert/script/ScriptSender.java
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.dolphinscheduler.plugin.alert.script;
+
+import org.apache.dolphinscheduler.spi.alert.AlertResult;
+
+import java.util.Map;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * ScriptSender
+ */
+public class ScriptSender {
+
+    private static final Logger logger = LoggerFactory.getLogger(ScriptSender.class);
+
+    private String scriptPath;
+
+    private Integer scriptType;
+
+    private String userParams;
+
+    ScriptSender(Map<String, String> config) {
+        scriptPath = config.get(ScriptParamsConstants.NAME_SCRIPT_PATH);
+        scriptType = Integer.parseInt(config.get(ScriptParamsConstants.NAME_SCRIPT_TYPE));
+        userParams = config.get(ScriptParamsConstants.NAME_SCRIPT_USER_PARAMS);
+    }
+
+    AlertResult sendScriptAlert(String msg) {
+        AlertResult alertResult = new AlertResult();
+        if (ScriptType.of(scriptType).equals(ScriptType.SHELL)) {
+            return executeShellScript(msg);
+        }
+        return alertResult;
+    }
+
+    private AlertResult executeShellScript(String msg) {
+        AlertResult alertResult = new AlertResult();
+        alertResult.setStatus("false");
+        if (Boolean.TRUE.equals(OSUtils.isWindows())) {
+            alertResult.setMessage("shell script not support windows os");
+            return alertResult;
+        }
+        String[] cmd = {"/bin/sh", "-c", scriptPath + " " + msg + " " + userParams};
+        int exitCode = ProcessUtils.executeScript(cmd);
+
+        if (exitCode == 0) {
+            alertResult.setStatus("true");
+            alertResult.setMessage("send script alert msg success");
+            return alertResult;
+        }
+        alertResult.setMessage("send script alert msg error,exitCode is " + exitCode);
+        logger.info("send script alert msg error,exitCode is {}", exitCode);
+        return alertResult;
+    }
+
+}
diff --git a/dolphinscheduler-plugin-api/src/main/java/org/apache/dolphinscheduler/plugin/model/AlertInfo.java b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-script/src/main/java/org/apache/dolphinscheduler/plugin/alert/script/ScriptType.java
similarity index 51%
rename from dolphinscheduler-plugin-api/src/main/java/org/apache/dolphinscheduler/plugin/model/AlertInfo.java
rename to dolphinscheduler-alert-plugin/dolphinscheduler-alert-script/src/main/java/org/apache/dolphinscheduler/plugin/alert/script/ScriptType.java
index 1d71ed7..59b17d0 100644
--- a/dolphinscheduler-plugin-api/src/main/java/org/apache/dolphinscheduler/plugin/model/AlertInfo.java
+++ b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-script/src/main/java/org/apache/dolphinscheduler/plugin/alert/script/ScriptType.java
@@ -14,48 +14,49 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dolphinscheduler.plugin.model;
+
+package org.apache.dolphinscheduler.plugin.alert.script;
 
 import java.util.HashMap;
 import java.util.Map;
 
 /**
- * AlertInfo
+ * ScriptType
  */
-public class AlertInfo {
+public enum ScriptType {
 
-    private Map<String, Object> alertProps;
 
-    private AlertData alertData;
+    SHELL(0, "SHELL"),
+    ;
 
-    public AlertInfo() {
-        this.alertProps = new HashMap<>();
+    ScriptType(int code, String descp) {
+        this.code = code;
+        this.descp = descp;
     }
 
-    public Map<String, Object> getAlertProps() {
-        return alertProps;
-    }
+    private final int code;
+    private final String descp;
 
-    public AlertInfo setAlertProps(Map<String, Object> alertProps) {
-        this.alertProps = alertProps;
-        return this;
+    public int getCode() {
+        return code;
     }
 
-    public AlertInfo addProp(String key, Object value) {
-        this.alertProps.put(key, value);
-        return this;
+    public String getDescp() {
+        return descp;
     }
 
-    public Object getProp(String key) {
-        return this.alertProps.get(key);
-    }
+    private static final Map<Integer, ScriptType> SCRIPT_TYPE_MAP = new HashMap<>();
 
-    public AlertData getAlertData() {
-        return alertData;
+    static {
+        for (ScriptType scriptType : ScriptType.values()) {
+            SCRIPT_TYPE_MAP.put(scriptType.code, scriptType);
+        }
     }
 
-    public AlertInfo setAlertData(AlertData alertData) {
-        this.alertData = alertData;
-        return this;
+    public static ScriptType of(Integer code) {
+        if (SCRIPT_TYPE_MAP.containsKey(code)) {
+            return SCRIPT_TYPE_MAP.get(code);
+        }
+        throw new IllegalArgumentException("invalid code : " + code);
     }
 }
diff --git a/dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/manager/DingTalkManager.java b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-script/src/main/java/org/apache/dolphinscheduler/plugin/alert/script/StreamGobbler.java
similarity index 50%
rename from dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/manager/DingTalkManager.java
rename to dolphinscheduler-alert-plugin/dolphinscheduler-alert-script/src/main/java/org/apache/dolphinscheduler/plugin/alert/script/StreamGobbler.java
index 871ad95..2ec2528 100644
--- a/dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/manager/DingTalkManager.java
+++ b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-script/src/main/java/org/apache/dolphinscheduler/plugin/alert/script/StreamGobbler.java
@@ -15,40 +15,47 @@
  * limitations under the License.
  */
 
-package org.apache.dolphinscheduler.alert.manager;
-
-import org.apache.dolphinscheduler.alert.utils.Constants;
-import org.apache.dolphinscheduler.alert.utils.DingTalkUtils;
-import org.apache.dolphinscheduler.plugin.model.AlertInfo;
+package org.apache.dolphinscheduler.plugin.alert.script;
 
+import java.io.BufferedReader;
 import java.io.IOException;
-import java.util.HashMap;
-import java.util.Map;
+import java.io.InputStream;
+import java.io.InputStreamReader;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 /**
- * Ding Talk Manager
+ * StreamGobbler
  */
-public class DingTalkManager {
-    private static final Logger logger = LoggerFactory.getLogger(DingTalkManager.class);
+public class StreamGobbler extends Thread {
+
+    private static final Logger logger = LoggerFactory.getLogger(StreamGobbler.class);
+
+    private InputStream inputStream;
+
+    StreamGobbler(InputStream inputStream) {
+        this.inputStream = inputStream;
+    }
+
+    @Override
+    public void run() {
+        InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
+        BufferedReader inputBufferReader = new BufferedReader(inputStreamReader);
 
-    public Map<String, Object> send(AlertInfo alert) {
-        Map<String, Object> retMap = new HashMap<>();
-        retMap.put(Constants.STATUS, false);
-        logger.info("send message {}", alert.getAlertData().getTitle());
         try {
-            String msg = buildMessage(alert);
-            DingTalkUtils.sendDingTalkMsg(msg, Constants.UTF_8);
+            String line;
+            StringBuilder output = new StringBuilder();
+            while ((line = inputBufferReader.readLine()) != null) {
+                output.append(line);
+                output.append(System.getProperty("line.separator"));
+            }
+            if (output.length() > 0) {
+                logger.info("out put msg is{}",output.toString());
+            }
         } catch (IOException e) {
-            logger.error(e.getMessage(), e);
+            logger.error("I/O error occurs {}", e.getMessage());
         }
-        retMap.put(Constants.STATUS, true);
-        return retMap;
     }
 
-    private String buildMessage(AlertInfo alert) {
-        return alert.getAlertData().getContent();
-    }
-}
+}  
\ No newline at end of file
diff --git a/dolphinscheduler-plugin-api/src/main/java/org/apache/dolphinscheduler/plugin/model/PluginName.java b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-script/src/test/java/org/apache/dolphinscheduler/plugin/alert/script/ProcessUtilsTest.java
similarity index 59%
copy from dolphinscheduler-plugin-api/src/main/java/org/apache/dolphinscheduler/plugin/model/PluginName.java
copy to dolphinscheduler-alert-plugin/dolphinscheduler-alert-script/src/test/java/org/apache/dolphinscheduler/plugin/alert/script/ProcessUtilsTest.java
index 8066e45..1bf98d2 100644
--- a/dolphinscheduler-plugin-api/src/main/java/org/apache/dolphinscheduler/plugin/model/PluginName.java
+++ b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-script/src/test/java/org/apache/dolphinscheduler/plugin/alert/script/ProcessUtilsTest.java
@@ -14,32 +14,24 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dolphinscheduler.plugin.model;
+
+package org.apache.dolphinscheduler.plugin.alert.script;
+
+import org.junit.Test;
 
 /**
- * PluginName
+ * ProcessUtilsTest
  */
-public class PluginName {
+public class ProcessUtilsTest {
 
-    private String chinese;
+    private static final String rootPath = System.getProperty("user.dir");
 
-    private String english;
-
-    public String getChinese() {
-        return chinese;
-    }
+    private static final String shellFilPath = rootPath + "/dolphinscheduler-alert-plugin/dolphinscheduler-alert-script/src/test/script/shell/example.sh";
 
-    public PluginName setChinese(String chinese) {
-        this.chinese = chinese;
-        return this;
-    }
-
-    public String getEnglish() {
-        return english;
-    }
+    private String[] cmd = {"/bin/sh", "-c", shellFilPath + " " + "testMsg" + " " + "userParams"};
 
-    public PluginName setEnglish(String english) {
-        this.english = english;
-        return this;
+    @Test
+    public void testExecuteScript() {
+        ProcessUtils.executeScript(cmd);
     }
 }
diff --git a/dolphinscheduler-alert-plugin/dolphinscheduler-alert-script/src/test/java/org/apache/dolphinscheduler/plugin/alert/script/ScriptAlertChannelFactoryTest.java b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-script/src/test/java/org/apache/dolphinscheduler/plugin/alert/script/ScriptAlertChannelFactoryTest.java
new file mode 100644
index 0000000..72f2197
--- /dev/null
+++ b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-script/src/test/java/org/apache/dolphinscheduler/plugin/alert/script/ScriptAlertChannelFactoryTest.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.dolphinscheduler.plugin.alert.script;
+
+import org.apache.dolphinscheduler.spi.alert.AlertChannel;
+import org.apache.dolphinscheduler.spi.params.base.PluginParams;
+import org.apache.dolphinscheduler.spi.utils.JSONUtils;
+
+import java.util.List;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * ScriptAlertChannelFactoryTest
+ */
+public class ScriptAlertChannelFactoryTest {
+
+    @Test
+    public void testGetParams() {
+        ScriptAlertChannelFactory scriptAlertChannelFactory = new ScriptAlertChannelFactory();
+        List<PluginParams> params = scriptAlertChannelFactory.getParams();
+        JSONUtils.toJsonString(params);
+        Assert.assertEquals(3, params.size());
+    }
+
+    @Test
+    public void testCreate() {
+        ScriptAlertChannelFactory scriptAlertChannelFactory = new ScriptAlertChannelFactory();
+        AlertChannel alertChannel = scriptAlertChannelFactory.create();
+        Assert.assertNotNull(alertChannel);
+    }
+}
diff --git a/dolphinscheduler-alert-plugin/dolphinscheduler-alert-script/src/test/java/org/apache/dolphinscheduler/plugin/alert/script/ScriptSenderTest.java b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-script/src/test/java/org/apache/dolphinscheduler/plugin/alert/script/ScriptSenderTest.java
new file mode 100644
index 0000000..1cd74cf
--- /dev/null
+++ b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-script/src/test/java/org/apache/dolphinscheduler/plugin/alert/script/ScriptSenderTest.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.dolphinscheduler.plugin.alert.script;
+
+import org.apache.dolphinscheduler.spi.alert.AlertResult;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * ScriptSenderTest
+ */
+public class ScriptSenderTest {
+
+    private static Map<String, String> scriptConfig = new HashMap<>();
+
+    private static final String rootPath = System.getProperty("user.dir");
+
+    private static final String shellFilPath = rootPath + "/src/test/script/shell/scriptTest.sh";
+
+    @Before
+    public void initScriptConfig() {
+
+        scriptConfig.put(ScriptParamsConstants.NAME_SCRIPT_TYPE, String.valueOf(ScriptType.SHELL.getCode()));
+        scriptConfig.put(ScriptParamsConstants.NAME_SCRIPT_USER_PARAMS, "userParams");
+        scriptConfig.put(ScriptParamsConstants.NAME_SCRIPT_PATH, shellFilPath);
+    }
+
+    @Test
+    public void testScriptSenderTest() {
+        ScriptSender scriptSender = new ScriptSender(scriptConfig);
+        AlertResult alertResult;
+        alertResult = scriptSender.sendScriptAlert("success");
+        Assert.assertEquals("true", alertResult.getStatus());
+        alertResult = scriptSender.sendScriptAlert("errorMsg");
+        Assert.assertEquals("false", alertResult.getStatus());
+    }
+
+}
diff --git a/dolphinscheduler-plugin-api/src/test/resources/plugin.properties b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-script/src/test/script/shell/example.sh
old mode 100644
new mode 100755
similarity index 81%
copy from dolphinscheduler-plugin-api/src/test/resources/plugin.properties
copy to dolphinscheduler-alert-plugin/dolphinscheduler-alert-script/src/test/script/shell/example.sh
index d2ea383..708dcd0
--- a/dolphinscheduler-plugin-api/src/test/resources/plugin.properties
+++ b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-script/src/test/script/shell/example.sh
@@ -15,8 +15,11 @@
 # limitations under the License.
 #
 
-test.string=teststring
-test.false=false
-test.true=true
-cba=3.1
-test.long=100
\ No newline at end of file
+
+msg=$1
+content=$2
+
+# Write your specific logic here
+
+# Set the exit code according to your execution result, and alert needs to use it to judge the status of this alarm result
+exit 0
diff --git a/dolphinscheduler-plugin-api/src/test/resources/plugin.properties b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-script/src/test/script/shell/scriptTest.sh
old mode 100644
new mode 100755
similarity index 91%
rename from dolphinscheduler-plugin-api/src/test/resources/plugin.properties
rename to dolphinscheduler-alert-plugin/dolphinscheduler-alert-script/src/test/script/shell/scriptTest.sh
index d2ea383..02eba48
--- a/dolphinscheduler-plugin-api/src/test/resources/plugin.properties
+++ b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-script/src/test/script/shell/scriptTest.sh
@@ -15,8 +15,11 @@
 # limitations under the License.
 #
 
-test.string=teststring
-test.false=false
-test.true=true
-cba=3.1
-test.long=100
\ No newline at end of file
+msg=$1
+content=$2
+
+if  [ $msg = errorMsg ]
+   then
+     exit 12
+fi
+exit 0
\ No newline at end of file
diff --git a/dolphinscheduler-alert-plugin/dolphinscheduler-alert-wechat/pom.xml b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-wechat/pom.xml
new file mode 100644
index 0000000..123cebc
--- /dev/null
+++ b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-wechat/pom.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one or more
+  ~ contributor license agreements.  See the NOTICE file distributed with
+  ~ this work for additional information regarding copyright ownership.
+  ~ The ASF licenses this file to You 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.
+  -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>dolphinscheduler-alert-plugin</artifactId>
+        <groupId>org.apache.dolphinscheduler</groupId>
+        <version>1.3.4-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <groupId>org.apache.dolphinscheduler</groupId>
+    <artifactId>dolphinscheduler-alert-wechat</artifactId>
+    <packaging>dolphinscheduler-plugin</packaging>
+     <dependencies>
+
+         <dependency>
+             <groupId>org.apache.dolphinscheduler</groupId>
+             <artifactId>dolphinscheduler-spi</artifactId>
+             <scope>provided</scope>
+         </dependency>
+
+         <dependency>
+             <groupId>com.google.guava</groupId>
+             <artifactId>guava</artifactId>
+         </dependency>
+
+         <dependency>
+             <groupId>ch.qos.logback</groupId>
+             <artifactId>logback-classic</artifactId>
+         </dependency>
+
+         <dependency>
+             <groupId>org.apache.httpcomponents</groupId>
+             <artifactId>httpclient</artifactId>
+         </dependency>
+
+         <dependency>
+             <groupId>com.fasterxml.jackson.core</groupId>
+             <artifactId>jackson-databind</artifactId>
+             <scope>provided</scope>
+         </dependency>
+
+         <dependency>
+             <groupId>junit</groupId>
+             <artifactId>junit</artifactId>
+             <scope>test</scope>
+         </dependency>
+
+         <dependency>
+             <groupId>org.mockito</groupId>
+             <artifactId>mockito-core</artifactId>
+             <type>jar</type>
+             <scope>test</scope>
+         </dependency>
+
+     </dependencies>
+
+    <build>
+        <finalName>dolphinscheduler-alert-wechat-${project.version}</finalName>
+    </build>
+</project>
\ No newline at end of file
diff --git a/dolphinscheduler-common/src/main/java/org/apache/dolphinscheduler/common/plugin/PluginManager.java b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-wechat/src/main/java/org/apache/dolphinscheduler/plugin/alert/wechat/WeChatAlertChannel.java
similarity index 50%
copy from dolphinscheduler-common/src/main/java/org/apache/dolphinscheduler/common/plugin/PluginManager.java
copy to dolphinscheduler-alert-plugin/dolphinscheduler-alert-wechat/src/main/java/org/apache/dolphinscheduler/plugin/alert/wechat/WeChatAlertChannel.java
index f807884..4cdd4d3 100644
--- a/dolphinscheduler-common/src/main/java/org/apache/dolphinscheduler/common/plugin/PluginManager.java
+++ b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-wechat/src/main/java/org/apache/dolphinscheduler/plugin/alert/wechat/WeChatAlertChannel.java
@@ -14,20 +14,28 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dolphinscheduler.common.plugin;
 
-import org.apache.dolphinscheduler.plugin.api.AlertPlugin;
+package org.apache.dolphinscheduler.plugin.alert.wechat;
+
+import org.apache.dolphinscheduler.spi.alert.AlertChannel;
+import org.apache.dolphinscheduler.spi.alert.AlertData;
+import org.apache.dolphinscheduler.spi.alert.AlertInfo;
+import org.apache.dolphinscheduler.spi.alert.AlertResult;
+import org.apache.dolphinscheduler.spi.params.PluginParamsTransfer;
 
 import java.util.Map;
 
 /**
- * PluginManager
+ * WeChatAlertChannel
  */
-public interface PluginManager {
-
-    AlertPlugin findOne(String name);
+public class WeChatAlertChannel implements AlertChannel {
 
-    Map<String, AlertPlugin> findAll();
+    @Override
+    public AlertResult process(AlertInfo info) {
+        AlertData alertData = info.getAlertData();
+        String alertParams = info.getAlertParams();
+        Map<String, String> paramsMap = PluginParamsTransfer.getPluginParamsMap(alertParams);
+        return new WeChatSender(paramsMap).sendEnterpriseWeChat(alertData.getTitle(), alertData.getContent());
 
-    void addPlugin(AlertPlugin plugin);
+    }
 }
diff --git a/dolphinscheduler-alert-plugin/dolphinscheduler-alert-wechat/src/main/java/org/apache/dolphinscheduler/plugin/alert/wechat/WeChatAlertChannelFactory.java b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-wechat/src/main/java/org/apache/dolphinscheduler/plugin/alert/wechat/WeChatAlertChannelFactory.java
new file mode 100644
index 0000000..636c571
--- /dev/null
+++ b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-wechat/src/main/java/org/apache/dolphinscheduler/plugin/alert/wechat/WeChatAlertChannelFactory.java
@@ -0,0 +1,94 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.dolphinscheduler.plugin.alert.wechat;
+
+import org.apache.dolphinscheduler.spi.alert.AlertChannel;
+import org.apache.dolphinscheduler.spi.alert.AlertChannelFactory;
+import org.apache.dolphinscheduler.spi.alert.AlertConstants;
+import org.apache.dolphinscheduler.spi.alert.ShowType;
+import org.apache.dolphinscheduler.spi.params.InputParam;
+import org.apache.dolphinscheduler.spi.params.RadioParam;
+import org.apache.dolphinscheduler.spi.params.base.ParamsOptions;
+import org.apache.dolphinscheduler.spi.params.base.PluginParams;
+import org.apache.dolphinscheduler.spi.params.base.Validate;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * WeChatAlertChannelFactory
+ */
+public class WeChatAlertChannelFactory implements AlertChannelFactory {
+
+    @Override
+    public String getName() {
+        return "WeChat";
+    }
+
+    @Override
+    public List<PluginParams> getParams() {
+        InputParam corpIdParam = InputParam.newBuilder(WeChatAlertParamsConstants.NAME_ENTERPRISE_WE_CHAT_CORP_ID, WeChatAlertParamsConstants.ENTERPRISE_WE_CHAT_CORP_ID)
+                .setPlaceholder("please input corp id ")
+                .addValidate(Validate.newBuilder()
+                        .setRequired(true)
+                        .build())
+                .build();
+
+        InputParam secretParam = InputParam.newBuilder(WeChatAlertParamsConstants.NAME_ENTERPRISE_WE_CHAT_SECRET, WeChatAlertParamsConstants.ENTERPRISE_WE_CHAT_SECRET)
+                .setPlaceholder("please input secret ")
+                .addValidate(Validate.newBuilder()
+                        .setRequired(true)
+                        .build())
+                .build();
+
+        InputParam usersParam = InputParam.newBuilder(WeChatAlertParamsConstants.NAME_ENTERPRISE_WE_CHAT_USERS, WeChatAlertParamsConstants.ENTERPRISE_WE_CHAT_USERS)
+                .setPlaceholder("please input users ")
+                .addValidate(Validate.newBuilder()
+                        .setRequired(true)
+                        .build())
+                .build();
+
+        InputParam userSendMsgParam = InputParam.newBuilder(WeChatAlertParamsConstants.NAME_ENTERPRISE_WE_CHAT_USER_SEND_MSG, WeChatAlertParamsConstants.ENTERPRISE_WE_CHAT_USER_SEND_MSG)
+                .setPlaceholder("please input corp id ")
+                .addValidate(Validate.newBuilder()
+                        .setRequired(true)
+                        .build())
+                .build();
+
+        InputParam agentIdParam = InputParam.newBuilder(WeChatAlertParamsConstants.NAME_ENTERPRISE_WE_CHAT_AGENT_ID, WeChatAlertParamsConstants.ENTERPRISE_WE_CHAT_AGENT_ID)
+                .setPlaceholder("please input agent id ")
+                .addValidate(Validate.newBuilder()
+                        .setRequired(true)
+                        .build())
+                .build();
+
+        RadioParam showType = RadioParam.newBuilder(AlertConstants.SHOW_TYPE, AlertConstants.SHOW_TYPE)
+                .addParamsOptions(new ParamsOptions(ShowType.TABLE.getDescp(), ShowType.TABLE.getDescp(), false))
+                .addParamsOptions(new ParamsOptions(ShowType.TEXT.getDescp(), ShowType.TEXT.getDescp(), false))
+                .setValue(ShowType.TABLE.getDescp())
+                .addValidate(Validate.newBuilder().setRequired(true).build())
+                .build();
+
+        return Arrays.asList(corpIdParam, secretParam, usersParam, userSendMsgParam, agentIdParam, showType);
+    }
+
+    @Override
+    public AlertChannel create() {
+        return new WeChatAlertChannel();
+    }
+}
diff --git a/dolphinscheduler-plugin-api/src/main/java/org/apache/dolphinscheduler/plugin/model/PluginName.java b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-wechat/src/main/java/org/apache/dolphinscheduler/plugin/alert/wechat/WeChatAlertConstants.java
similarity index 62%
copy from dolphinscheduler-plugin-api/src/main/java/org/apache/dolphinscheduler/plugin/model/PluginName.java
copy to dolphinscheduler-alert-plugin/dolphinscheduler-alert-wechat/src/main/java/org/apache/dolphinscheduler/plugin/alert/wechat/WeChatAlertConstants.java
index 8066e45..904060c 100644
--- a/dolphinscheduler-plugin-api/src/main/java/org/apache/dolphinscheduler/plugin/model/PluginName.java
+++ b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-wechat/src/main/java/org/apache/dolphinscheduler/plugin/alert/wechat/WeChatAlertConstants.java
@@ -14,32 +14,21 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dolphinscheduler.plugin.model;
+
+package org.apache.dolphinscheduler.plugin.alert.wechat;
 
 /**
- * PluginName
+ * WeChatAlertConstants
  */
-public class PluginName {
-
-    private String chinese;
+public class WeChatAlertConstants {
 
-    private String english;
+    static final String MARKDOWN_QUOTE = ">";
 
-    public String getChinese() {
-        return chinese;
-    }
+    static final String MARKDOWN_ENTER = "\n";
 
-    public PluginName setChinese(String chinese) {
-        this.chinese = chinese;
-        return this;
-    }
+    static final String CHARSET = "UTF-8";
 
-    public String getEnglish() {
-        return english;
-    }
+    static final String WE_CHAT_PUSH_URL = "https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token={token}";
 
-    public PluginName setEnglish(String english) {
-        this.english = english;
-        return this;
-    }
+    static final String WE_CHAT_TOKEN_URL = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid={corpId}&corpsecret={secret}";
 }
diff --git a/dolphinscheduler-alert-plugin/dolphinscheduler-alert-wechat/src/main/java/org/apache/dolphinscheduler/plugin/alert/wechat/WeChatAlertParamsConstants.java b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-wechat/src/main/java/org/apache/dolphinscheduler/plugin/alert/wechat/WeChatAlertParamsConstants.java
new file mode 100644
index 0000000..138bd83
--- /dev/null
+++ b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-wechat/src/main/java/org/apache/dolphinscheduler/plugin/alert/wechat/WeChatAlertParamsConstants.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.dolphinscheduler.plugin.alert.wechat;
+
+/**
+ * WeChatAlertParamsConstants
+ */
+public class WeChatAlertParamsConstants {
+
+
+    static final String ENTERPRISE_WE_CHAT_CORP_ID = "corp.id";
+
+    static final String NAME_ENTERPRISE_WE_CHAT_CORP_ID = "corpId";
+
+
+    static final String ENTERPRISE_WE_CHAT_SECRET = "secret";
+
+    static final String NAME_ENTERPRISE_WE_CHAT_SECRET = "secret";
+
+    static final String ENTERPRISE_WE_CHAT_TEAM_SEND_MSG = "team.send.msg";
+
+    static final String NAME_ENTERPRISE_WE_CHAT_TEAM_SEND_MSG = "teamSendMsg";
+
+
+    static final String ENTERPRISE_WE_CHAT_USER_SEND_MSG = "user.send.msg";
+
+    static final String NAME_ENTERPRISE_WE_CHAT_USER_SEND_MSG = "userSendMsg";
+
+
+    static final String ENTERPRISE_WE_CHAT_AGENT_ID = "agent.id";
+
+    static final String NAME_ENTERPRISE_WE_CHAT_AGENT_ID = "agentId";
+
+
+    static final String ENTERPRISE_WE_CHAT_USERS = "users";
+
+
+    static final String NAME_ENTERPRISE_WE_CHAT_USERS = "users";
+
+
+}
diff --git a/dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/template/AlertTemplateFactory.java b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-wechat/src/main/java/org/apache/dolphinscheduler/plugin/alert/wechat/WeChatAlertPlugin.java
similarity index 63%
copy from dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/template/AlertTemplateFactory.java
copy to dolphinscheduler-alert-plugin/dolphinscheduler-alert-wechat/src/main/java/org/apache/dolphinscheduler/plugin/alert/wechat/WeChatAlertPlugin.java
index d384631..56c3c01 100644
--- a/dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/template/AlertTemplateFactory.java
+++ b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-wechat/src/main/java/org/apache/dolphinscheduler/plugin/alert/wechat/WeChatAlertPlugin.java
@@ -15,24 +15,20 @@
  * limitations under the License.
  */
 
-package org.apache.dolphinscheduler.alert.template;
+package org.apache.dolphinscheduler.plugin.alert.wechat;
 
-import org.apache.dolphinscheduler.alert.template.impl.DefaultHTMLTemplate;
+import org.apache.dolphinscheduler.spi.DolphinSchedulerPlugin;
+import org.apache.dolphinscheduler.spi.alert.AlertChannelFactory;
+
+import com.google.common.collect.ImmutableList;
 
 /**
- * the alert template factory
+ * WeChatAlertPlugin
  */
-public class AlertTemplateFactory {
-
-    private AlertTemplateFactory() {
-    }
+public class WeChatAlertPlugin implements DolphinSchedulerPlugin {
 
-    /**
-     * get a template from alert.properties conf file
-     *
-     * @return a template, default is DefaultHTMLTemplate
-     */
-    public static AlertTemplate getMessageTemplate() {
-        return new DefaultHTMLTemplate();
+    @Override
+    public Iterable<AlertChannelFactory> getAlertChannelFactorys() {
+        return ImmutableList.of(new WeChatAlertChannelFactory());
     }
 }
diff --git a/dolphinscheduler-alert-plugin/dolphinscheduler-alert-wechat/src/main/java/org/apache/dolphinscheduler/plugin/alert/wechat/WeChatSender.java b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-wechat/src/main/java/org/apache/dolphinscheduler/plugin/alert/wechat/WeChatSender.java
new file mode 100644
index 0000000..f4fd090
--- /dev/null
+++ b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-wechat/src/main/java/org/apache/dolphinscheduler/plugin/alert/wechat/WeChatSender.java
@@ -0,0 +1,335 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.dolphinscheduler.plugin.alert.wechat;
+
+import static java.util.Objects.requireNonNull;
+
+import org.apache.dolphinscheduler.spi.alert.AlertConstants;
+import org.apache.dolphinscheduler.spi.alert.AlertResult;
+import org.apache.dolphinscheduler.spi.alert.ShowType;
+import org.apache.dolphinscheduler.spi.utils.JSONUtils;
+import org.apache.dolphinscheduler.spi.utils.StringUtils;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.util.EntityUtils;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * WeChatSender
+ */
+public class WeChatSender {
+
+    private static Logger logger = LoggerFactory.getLogger(WeChatSender.class);
+
+    private String weChatAgentId;
+
+    private String weChatUsers;
+
+    private String weChatTeamSendMsg;
+
+    private String weChatUserSendMsg;
+
+    private String weChatTokenUrlReplace;
+
+    private String weChatToken;
+
+    private String showType;
+
+
+    private static final String agentIdRegExp = "{agentId}";
+    private static final String msgRegExp = "{msg}";
+    private static final String userRegExp = "{toUser}";
+    private static final String corpIdRegex = "{corpId}";
+    private static final String secretRegex = "{secret}";
+    private static final String toPartyRegex = "{toParty}";
+    private static final String toUserRegex = "{toUser}";
+    private static final String tokenRegex = "{token}";
+
+    WeChatSender(Map<String, String> config) {
+        weChatAgentId = config.get(WeChatAlertParamsConstants.NAME_ENTERPRISE_WE_CHAT_AGENT_ID);
+        weChatUsers = config.get(WeChatAlertParamsConstants.NAME_ENTERPRISE_WE_CHAT_USERS);
+        String weChatCorpId = config.get(WeChatAlertParamsConstants.NAME_ENTERPRISE_WE_CHAT_CORP_ID);
+        String weChatSecret = config.get(WeChatAlertParamsConstants.NAME_ENTERPRISE_WE_CHAT_SECRET);
+        String weChatTokenUrl = WeChatAlertConstants.WE_CHAT_TOKEN_URL;
+        weChatTeamSendMsg = config.get(WeChatAlertParamsConstants.NAME_ENTERPRISE_WE_CHAT_TEAM_SEND_MSG);
+        weChatUserSendMsg = config.get(WeChatAlertParamsConstants.NAME_ENTERPRISE_WE_CHAT_USER_SEND_MSG);
+        showType = config.get(AlertConstants.SHOW_TYPE);
+        requireNonNull(showType, AlertConstants.SHOW_TYPE + " must not null");
+        weChatTokenUrlReplace = weChatTokenUrl
+            .replace(corpIdRegex, weChatCorpId)
+            .replace(secretRegex, weChatSecret);
+        weChatToken = getToken();
+    }
+
+    /**
+     * make user multi user message
+     *
+     * @param toUser the toUser
+     * @param agentId the agentId
+     * @param msg the msg
+     * @return Enterprise WeChat send message
+     */
+    private String makeUserSendMsg(Collection<String> toUser, String agentId, String msg) {
+        String listUser = mkString(toUser);
+        return weChatUserSendMsg.replace(userRegExp, listUser)
+            .replace(agentIdRegExp, agentId)
+            .replace(msgRegExp, msg);
+    }
+
+    /**
+     * send Enterprise WeChat
+     *
+     * @return Enterprise WeChat resp, demo: {"errcode":0,"errmsg":"ok","invaliduser":""}
+     * @throws Exception the Exception
+     */
+    public AlertResult sendEnterpriseWeChat(String title, String content) {
+        AlertResult alertResult;
+        List<String> userList = Arrays.asList(weChatUsers.split(","));
+        String data = markdownByAlert(title, content);
+        String msg = makeUserSendMsg(userList, weChatAgentId, data);
+        if (null == weChatToken) {
+            alertResult = new AlertResult();
+            alertResult.setMessage("send we chat alert fail,get weChat token error");
+            alertResult.setStatus("false");
+            return alertResult;
+        }
+        String enterpriseWeChatPushUrlReplace = WeChatAlertConstants.WE_CHAT_PUSH_URL.replace(tokenRegex, weChatToken);
+
+        try {
+            return checkWeChatSendMsgResult(post(enterpriseWeChatPushUrlReplace, msg));
+        } catch (Exception e) {
+            logger.info("send we chat alert msg  exception : {}", e.getMessage());
+            alertResult = new AlertResult();
+            alertResult.setMessage("send we chat alert fail");
+            alertResult.setStatus("false");
+        }
+        return alertResult;
+    }
+
+    private static String post(String url, String data) throws IOException {
+        try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
+            HttpPost httpPost = new HttpPost(url);
+            httpPost.setEntity(new StringEntity(data, WeChatAlertConstants.CHARSET));
+            CloseableHttpResponse response = httpClient.execute(httpPost);
+            String resp;
+            try {
+                HttpEntity entity = response.getEntity();
+                resp = EntityUtils.toString(entity, WeChatAlertConstants.CHARSET);
+                EntityUtils.consume(entity);
+            } finally {
+                response.close();
+            }
+            logger.info("Enterprise WeChat send [{}], param:{}, resp:{}",
+                url, data, resp);
+            return resp;
+        }
+    }
+
+    /**
+     * convert table to markdown style
+     *
+     * @param title the title
+     * @param content the content
+     * @return markdown table content
+     */
+    private static String markdownTable(String title, String content) {
+        List<LinkedHashMap> mapItemsList = JSONUtils.toList(content, LinkedHashMap.class);
+        if (null == mapItemsList || mapItemsList.isEmpty()) {
+            logger.error("itemsList is null");
+            throw new RuntimeException("itemsList is null");
+        }
+        StringBuilder contents = new StringBuilder(200);
+        for (LinkedHashMap mapItems : mapItemsList) {
+            Set<Entry<String, Object>> entries = mapItems.entrySet();
+            Iterator<Entry<String, Object>> iterator = entries.iterator();
+            StringBuilder t = new StringBuilder(String.format("`%s`%s", title, WeChatAlertConstants.MARKDOWN_ENTER));
+
+            while (iterator.hasNext()) {
+
+                Map.Entry<String, Object> entry = iterator.next();
+                t.append(WeChatAlertConstants.MARKDOWN_QUOTE);
+                t.append(entry.getKey()).append(":").append(entry.getValue());
+                t.append(WeChatAlertConstants.MARKDOWN_ENTER);
+            }
+            contents.append(t);
+        }
+
+        return contents.toString();
+    }
+
+    /**
+     * convert text to markdown style
+     *
+     * @param title the title
+     * @param content the content
+     * @return markdown text
+     */
+    private static String markdownText(String title, String content) {
+        if (StringUtils.isNotEmpty(content)) {
+            List<LinkedHashMap> mapItemsList = JSONUtils.toList(content, LinkedHashMap.class);
+            if (null == mapItemsList || mapItemsList.isEmpty()) {
+                logger.error("itemsList is null");
+                throw new RuntimeException("itemsList is null");
+            }
+
+            StringBuilder contents = new StringBuilder(100);
+            contents.append(String.format("`%s`%n", title));
+            for (LinkedHashMap mapItems : mapItemsList) {
+
+                Set<Map.Entry<String, Object>> entries = mapItems.entrySet();
+                for (Entry<String, Object> entry : entries) {
+                    contents.append(WeChatAlertConstants.MARKDOWN_QUOTE);
+                    contents.append(entry.getKey()).append(":").append(entry.getValue());
+                    contents.append(WeChatAlertConstants.MARKDOWN_ENTER);
+                }
+
+            }
+            return contents.toString();
+        }
+        return null;
+    }
+
+    /**
+     * Determine the mardown style based on the show type of the alert
+     *
+     * @return the markdown alert table/text
+     */
+    private String markdownByAlert(String title, String content) {
+        String result = "";
+        if (showType.equals(ShowType.TABLE.getDescp())) {
+            result = markdownTable(title, content);
+        } else if (showType.equals(ShowType.TEXT.getDescp())) {
+            result = markdownText(title, content);
+        }
+        return result;
+
+    }
+
+    private String getToken() {
+        try {
+            return get(weChatTokenUrlReplace);
+        } catch (IOException e) {
+            logger.info("we chat alert get token error{}", e.getMessage());
+        }
+        return null;
+    }
+
+    private static String get(String url) throws IOException {
+        String resp;
+
+        try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
+            HttpGet httpGet = new HttpGet(url);
+            try (CloseableHttpResponse response = httpClient.execute(httpGet)) {
+                HttpEntity entity = response.getEntity();
+                resp = EntityUtils.toString(entity, WeChatAlertConstants.CHARSET);
+                EntityUtils.consume(entity);
+            }
+
+            HashMap map = JSONUtils.parseObject(resp, HashMap.class);
+            if (map != null && null != map.get("access_token")) {
+                return map.get("access_token").toString();
+            } else {
+                return null;
+            }
+        }
+    }
+
+    private static String mkString(Iterable<String> list) {
+
+        if (null == list || StringUtils.isEmpty("|")) {
+            return null;
+        }
+
+        StringBuilder sb = new StringBuilder();
+        boolean first = true;
+        for (String item : list) {
+            if (first) {
+                first = false;
+            } else {
+                sb.append("|");
+            }
+            sb.append(item);
+        }
+        return sb.toString();
+    }
+
+    public static class WeChatSendMsgResponse {
+        private Integer errcode;
+        private String errmsg;
+
+        public Integer getErrcode() {
+            return errcode;
+        }
+
+        public void setErrcode(Integer errcode) {
+            this.errcode = errcode;
+        }
+
+        public String getErrmsg() {
+            return errmsg;
+        }
+
+        public void setErrmsg(String errmsg) {
+            this.errmsg = errmsg;
+        }
+    }
+
+    private static AlertResult checkWeChatSendMsgResult(String result) {
+        AlertResult alertResult = new AlertResult();
+        alertResult.setStatus("false");
+
+        if (null == result) {
+            alertResult.setMessage("we chat send fail");
+            logger.info("send we chat msg error,resp is null");
+            return alertResult;
+        }
+        WeChatSendMsgResponse sendMsgResponse = JSONUtils.parseObject(result, WeChatSendMsgResponse.class);
+        if (null == sendMsgResponse) {
+            alertResult.setMessage("we chat send fail");
+            logger.info("send we chat msg error,resp error");
+            return alertResult;
+        }
+        if (sendMsgResponse.errcode == 0) {
+            alertResult.setStatus("true");
+            alertResult.setMessage("we chat alert send success");
+            return alertResult;
+        }
+        alertResult.setStatus("false");
+        alertResult.setMessage(sendMsgResponse.getErrmsg());
+        return alertResult;
+    }
+}
diff --git a/dolphinscheduler-alert-plugin/dolphinscheduler-alert-wechat/src/test/java/org/apache/dolphinscheduler/plugin/alert/wechat/WeChatAlertChannelFactoryTest.java b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-wechat/src/test/java/org/apache/dolphinscheduler/plugin/alert/wechat/WeChatAlertChannelFactoryTest.java
new file mode 100644
index 0000000..cc62d5a
--- /dev/null
+++ b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-wechat/src/test/java/org/apache/dolphinscheduler/plugin/alert/wechat/WeChatAlertChannelFactoryTest.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.dolphinscheduler.plugin.alert.wechat;
+
+import org.apache.dolphinscheduler.spi.alert.AlertChannel;
+import org.apache.dolphinscheduler.spi.params.base.PluginParams;
+import org.apache.dolphinscheduler.spi.utils.JSONUtils;
+
+import java.util.List;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * WeChatAlertChannelFactoryTest
+ */
+public class WeChatAlertChannelFactoryTest {
+
+    @Test
+    public void testGetParams() {
+        WeChatAlertChannelFactory weChatAlertChannelFactory = new WeChatAlertChannelFactory();
+        List<PluginParams> params = weChatAlertChannelFactory.getParams();
+        JSONUtils.toJsonString(params);
+        Assert.assertEquals(6, params.size());
+    }
+
+    @Test
+    public void testCreate() {
+        WeChatAlertChannelFactory dingTalkAlertChannelFactory = new WeChatAlertChannelFactory();
+        AlertChannel alertChannel = dingTalkAlertChannelFactory.create();
+        Assert.assertNotNull(alertChannel);
+    }
+}
diff --git a/dolphinscheduler-alert-plugin/dolphinscheduler-alert-wechat/src/test/java/org/apache/dolphinscheduler/plugin/alert/wechat/WeChatSenderTest.java b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-wechat/src/test/java/org/apache/dolphinscheduler/plugin/alert/wechat/WeChatSenderTest.java
new file mode 100644
index 0000000..61010eb
--- /dev/null
+++ b/dolphinscheduler-alert-plugin/dolphinscheduler-alert-wechat/src/test/java/org/apache/dolphinscheduler/plugin/alert/wechat/WeChatSenderTest.java
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.dolphinscheduler.plugin.alert.wechat;
+
+import org.apache.dolphinscheduler.spi.alert.AlertConstants;
+import org.apache.dolphinscheduler.spi.alert.AlertResult;
+import org.apache.dolphinscheduler.spi.alert.ShowType;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * WeChatSenderTest
+ */
+public class WeChatSenderTest {
+
+    private static Map<String, String> weChatConfig = new HashMap<>();
+
+    private String content = "[{\"id\":\"69\","
+            +
+            "\"name\":\"UserBehavior-0--1193959466\","
+            +
+            "\"Job name\":\"Start workflow\","
+            +
+            "\"State\":\"SUCCESS\","
+            +
+            "\"Recovery\":\"NO\","
+            +
+            "\"Run time\":\"1\","
+            +
+            "\"Start time\": \"2018-08-06 10:31:34.0\","
+            +
+            "\"End time\": \"2018-08-06 10:31:49.0\","
+            +
+            "\"Host\": \"192.168.xx.xx\","
+            +
+            "\"Notify group\" :\"4\"}]";
+
+    @Before
+    public void initDingTalkConfig() {
+        // Just for this test, I will delete these configurations before this PR is merged
+        weChatConfig.put(WeChatAlertParamsConstants.NAME_ENTERPRISE_WE_CHAT_AGENT_ID, "100000");
+        weChatConfig.put(WeChatAlertParamsConstants.NAME_ENTERPRISE_WE_CHAT_CORP_ID, "NAME_ENTERPRISE_WE_CHAT_CORP_ID");
+        weChatConfig.put(WeChatAlertParamsConstants.NAME_ENTERPRISE_WE_CHAT_SECRET, "NAME_ENTERPRISE_WE_CHAT_SECRET");
+        weChatConfig.put(WeChatAlertParamsConstants.NAME_ENTERPRISE_WE_CHAT_USER_SEND_MSG, "{\"touser\":\"{toUser}\",\"agentid\":{agentId}"
+                +
+                ",\"msgtype\":\"markdown\",\"markdown\":{\"content\":\"{msg}\"}}"
+        );
+        weChatConfig.put(WeChatAlertParamsConstants.NAME_ENTERPRISE_WE_CHAT_USERS, "Kris");
+        weChatConfig.put(WeChatAlertParamsConstants.NAME_ENTERPRISE_WE_CHAT_TEAM_SEND_MSG, "msg");
+        weChatConfig.put(AlertConstants.SHOW_TYPE, ShowType.TABLE.getDescp());
+    }
+
+    @Test
+    public void testSendWeChatTableMsg() {
+        WeChatSender weChatSender = new WeChatSender(weChatConfig);
+
+        AlertResult alertResult = weChatSender.sendEnterpriseWeChat("test", content);
+        Assert.assertEquals("false", alertResult.getStatus());
+    }
+
+    @Test
+    public void testSendWeChatTextMsg() {
+        weChatConfig.put(AlertConstants.SHOW_TYPE, ShowType.TEXT.getDescp());
+        WeChatSender weChatSender = new WeChatSender(weChatConfig);
+        AlertResult alertResult = weChatSender.sendEnterpriseWeChat("test", content);
+        Assert.assertEquals("false", alertResult.getStatus());
+    }
+
+}
diff --git a/dolphinscheduler-plugin-api/pom.xml b/dolphinscheduler-alert-plugin/pom.xml
similarity index 52%
copy from dolphinscheduler-plugin-api/pom.xml
copy to dolphinscheduler-alert-plugin/pom.xml
index 0c2547d..ede5051 100644
--- a/dolphinscheduler-plugin-api/pom.xml
+++ b/dolphinscheduler-alert-plugin/pom.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0"?>
+<?xml version="1.0" encoding="UTF-8"?>
 <!--
   ~ Licensed to the Apache Software Foundation (ASF) under one or more
   ~ contributor license agreements.  See the NOTICE file distributed with
@@ -15,38 +15,27 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-
-<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
-         xmlns="http://maven.apache.org/POM/4.0.0"
-         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
-    <modelVersion>4.0.0</modelVersion>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     <parent>
-        <groupId>org.apache.dolphinscheduler</groupId>
         <artifactId>dolphinscheduler</artifactId>
+        <groupId>org.apache.dolphinscheduler</groupId>
         <version>1.3.4-SNAPSHOT</version>
     </parent>
-    <artifactId>dolphinscheduler-plugin-api</artifactId>
-    <name>${project.artifactId}</name>
-    <packaging>jar</packaging>
+    <modelVersion>4.0.0</modelVersion>
+
+    <groupId>org.apache.dolphinscheduler</groupId>
+    <artifactId>dolphinscheduler-alert-plugin</artifactId>
+    <packaging>pom</packaging>
 
-    <properties>
-        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
-    </properties>
+    <modules>
+        <module>dolphinscheduler-alert-email</module>
+        <module>dolphinscheduler-alert-wechat</module>
+        <module>dolphinscheduler-alert-dingtalk</module>
+        <module>dolphinscheduler-alert-script</module>
+        <module>dolphinscheduler-alert-http</module>
+    </modules>
 
-    <dependencies>
-        <dependency>
-            <groupId>org.slf4j</groupId>
-            <artifactId>slf4j-api</artifactId>
-        </dependency>
-        <dependency>
-            <groupId>junit</groupId>
-            <artifactId>junit</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>commons-io</groupId>
-            <artifactId>commons-io</artifactId>
-        </dependency>
-    </dependencies>
 
-</project>
+</project>
\ No newline at end of file
diff --git a/dolphinscheduler-alert/pom.xml b/dolphinscheduler-alert/pom.xml
index a44d101..cea763c 100644
--- a/dolphinscheduler-alert/pom.xml
+++ b/dolphinscheduler-alert/pom.xml
@@ -32,38 +32,21 @@
     </properties>
     <dependencies>
         <dependency>
-            <groupId>junit</groupId>
-            <artifactId>junit</artifactId>
-            <scope>test</scope>
+            <groupId>org.apache.dolphinscheduler</groupId>
+            <artifactId>dolphinscheduler-spi</artifactId>
         </dependency>
         <dependency>
-            <groupId>org.mockito</groupId>
-            <artifactId>mockito-core</artifactId>
-            <type>jar</type>
-            <scope>test</scope>
+            <groupId>org.apache.dolphinscheduler</groupId>
+            <artifactId>dolphinscheduler-remote</artifactId>
         </dependency>
-
         <dependency>
-            <groupId>org.powermock</groupId>
-            <artifactId>powermock-module-junit4</artifactId>
-            <scope>test</scope>
+            <groupId>org.apache.dolphinscheduler</groupId>
+            <artifactId>dolphinscheduler-common</artifactId>
         </dependency>
-
         <dependency>
-            <groupId>org.powermock</groupId>
-            <artifactId>powermock-api-mockito2</artifactId>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
             <scope>test</scope>
-            <exclusions>
-                <exclusion>
-                    <groupId>org.mockito</groupId>
-                    <artifactId>mockito-core</artifactId>
-                </exclusion>
-            </exclusions>
-        </dependency>
-
-        <dependency>
-            <groupId>org.apache.commons</groupId>
-            <artifactId>commons-email</artifactId>
         </dependency>
 
         <dependency>
@@ -108,6 +91,52 @@
             </exclusions>
         </dependency>
 
+        <dependency>
+            <groupId>org.sonatype.aether</groupId>
+            <artifactId>aether-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>io.airlift.resolver</groupId>
+            <artifactId>resolver</artifactId>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.ow2.asm</groupId>
+            <artifactId>asm</artifactId>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.powermock</groupId>
+            <artifactId>powermock-module-junit4</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-core</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-java</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.powermock</groupId>
+            <artifactId>powermock-api-mockito2</artifactId>
+            <scope>test</scope>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.mockito</groupId>
+                    <artifactId>mockito-core</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
     </dependencies>
 
 </project>
diff --git a/dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/AlertServer.java b/dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/AlertServer.java
index bf791ac..54afc93 100644
--- a/dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/AlertServer.java
+++ b/dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/AlertServer.java
@@ -17,21 +17,32 @@
 
 package org.apache.dolphinscheduler.alert;
 
-import org.apache.dolphinscheduler.alert.plugin.EmailAlertPlugin;
+import static org.apache.dolphinscheduler.common.Constants.ALERT_RPC_PORT;
+
+import org.apache.dolphinscheduler.alert.plugin.AlertPluginManager;
+import org.apache.dolphinscheduler.alert.plugin.DolphinPluginLoader;
+import org.apache.dolphinscheduler.alert.plugin.DolphinPluginManagerConfig;
+import org.apache.dolphinscheduler.alert.processor.AlertRequestProcessor;
 import org.apache.dolphinscheduler.alert.runner.AlertSender;
 import org.apache.dolphinscheduler.alert.utils.Constants;
 import org.apache.dolphinscheduler.alert.utils.PropertyUtils;
-import org.apache.dolphinscheduler.common.plugin.FilePluginManager;
 import org.apache.dolphinscheduler.common.thread.Stopper;
 import org.apache.dolphinscheduler.dao.AlertDao;
 import org.apache.dolphinscheduler.dao.DaoFactory;
+import org.apache.dolphinscheduler.dao.PluginDao;
 import org.apache.dolphinscheduler.dao.entity.Alert;
+import org.apache.dolphinscheduler.remote.NettyRemotingServer;
+import org.apache.dolphinscheduler.remote.command.CommandType;
+import org.apache.dolphinscheduler.remote.config.NettyServerConfig;
+import org.apache.dolphinscheduler.spi.utils.StringUtils;
 
 import java.util.List;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.collect.ImmutableList;
+
 /**
  * alert of start
  */
@@ -42,34 +53,75 @@ public class AlertServer {
      */
     private AlertDao alertDao = DaoFactory.getDaoInstance(AlertDao.class);
 
+    private PluginDao pluginDao = DaoFactory.getDaoInstance(PluginDao.class);
+
+    private AlertSender alertSender;
+
     private static AlertServer instance;
 
-    private FilePluginManager alertPluginManager;
+    private AlertPluginManager alertPluginManager;
+
+    private DolphinPluginManagerConfig alertPluginManagerConfig;
+
+    public static final String ALERT_PLUGIN_BINDING = "alert.plugin.binding";
+
+    public static final String ALERT_PLUGIN_DIR = "alert.plugin.dir";
+
+    public static final String MAVEN_LOCAL_REPOSITORY = "maven.local.repository";
+
+    /**
+     * netty server
+     */
+    private NettyRemotingServer server;
+
+    private static class AlertServerHolder {
+        private static final AlertServer INSTANCE = new AlertServer();
+    }
 
-    private static final String[] whitePrefixes = new String[]{"org.apache.dolphinscheduler.plugin.utils."};
+    public static final AlertServer getInstance() {
+        return AlertServerHolder.INSTANCE;
+
+    }
 
-    private static final String[] excludePrefixes = new String[]{
-            "org.apache.dolphinscheduler.plugin.",
-            "ch.qos.logback.",
-            "org.slf4j."
-    };
+    private AlertServer() {
 
-    public AlertServer() {
-        alertPluginManager =
-                new FilePluginManager(PropertyUtils.getString(Constants.PLUGIN_DIR), whitePrefixes, excludePrefixes);
-        // add default alert plugins
-        alertPluginManager.addPlugin(new EmailAlertPlugin());
     }
 
-    public static synchronized AlertServer getInstance() {
-        if (null == instance) {
-            instance = new AlertServer();
+    private void initPlugin() {
+        alertPluginManager = new AlertPluginManager();
+        alertPluginManagerConfig = new DolphinPluginManagerConfig();
+        alertPluginManagerConfig.setPlugins(PropertyUtils.getString(ALERT_PLUGIN_BINDING));
+        if (StringUtils.isNotBlank(PropertyUtils.getString(ALERT_PLUGIN_DIR))) {
+            alertPluginManagerConfig.setInstalledPluginsDir(PropertyUtils.getString(ALERT_PLUGIN_DIR, Constants.ALERT_PLUGIN_PATH).trim());
+        }
+
+        if (StringUtils.isNotBlank(PropertyUtils.getString(MAVEN_LOCAL_REPOSITORY))) {
+            alertPluginManagerConfig.setMavenLocalRepository(PropertyUtils.getString(MAVEN_LOCAL_REPOSITORY).trim());
+        }
+
+        DolphinPluginLoader alertPluginLoader = new DolphinPluginLoader(alertPluginManagerConfig, ImmutableList.of(alertPluginManager));
+        try {
+            alertPluginLoader.loadPlugins();
+        } catch (Exception e) {
+            throw new RuntimeException("load Alert Plugin Failed !", e);
         }
-        return instance;
     }
 
-    public void start() {
-        logger.info("alert server ready start ");
+    /**
+     * init netty remoting server
+     */
+    private void initRemoteServer() {
+        NettyServerConfig serverConfig = new NettyServerConfig();
+        serverConfig.setListenPort(ALERT_RPC_PORT);
+        this.server = new NettyRemotingServer(serverConfig);
+        this.server.registerProcessor(CommandType.ALERT_SEND_REQUEST, new AlertRequestProcessor(alertDao, alertPluginManager, pluginDao));
+        this.server.start();
+    }
+
+    /**
+     * Cyclic alert info sending alert
+     */
+    private void runSender() {
         while (Stopper.isRunning()) {
             try {
                 Thread.sleep(Constants.ALERT_SCAN_INTERVAL);
@@ -77,15 +129,43 @@ public class AlertServer {
                 logger.error(e.getMessage(), e);
                 Thread.currentThread().interrupt();
             }
-            List<Alert> alerts = alertDao.listWaitExecutionAlert();
-            AlertSender alertSender = new AlertSender(alerts, alertDao, alertPluginManager);
-            alertSender.run();
+            if (alertPluginManager == null || alertPluginManager.getAlertChannelMap().size() == 0) {
+                logger.warn("No Alert Plugin . Can not send alert info. ");
+            } else {
+                List<Alert> alerts = alertDao.listWaitExecutionAlert();
+                alertSender = new AlertSender(alerts, alertDao, alertPluginManager, pluginDao);
+                alertSender.run();
+            }
         }
     }
 
+    /**
+     * start
+     */
+    public void start() {
+        initPlugin();
+        initRemoteServer();
+        logger.info("alert server ready start ");
+        runSender();
+    }
+
+    /**
+     * stop
+     */
+    public void stop() {
+        this.server.close();
+        logger.info("alert server shut down");
+    }
+
     public static void main(String[] args) {
         AlertServer alertServer = AlertServer.getInstance();
         alertServer.start();
+        Runtime.getRuntime().addShutdownHook(new Thread() {
+            @Override
+            public void run() {
+                alertServer.stop();
+            }
+        });
     }
 
 }
diff --git a/dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/manager/EmailManager.java b/dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/manager/EmailManager.java
index 22f4b7a..874b866 100644
--- a/dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/manager/EmailManager.java
+++ b/dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/manager/EmailManager.java
@@ -1,55 +1,55 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.dolphinscheduler.alert.manager;
-
-import org.apache.dolphinscheduler.alert.utils.MailUtils;
-
-import java.util.List;
-import java.util.Map;
-
-/**
- * email send manager
- */
-public class EmailManager {
-    /**
-     * email send
-     * @param receiversList the receiver list
-     * @param receiversCcList the cc List
-     * @param title the title
-     * @param content the content
-     * @param showType the showType
-     * @return the send result
-     */
-    public Map<String,Object> send(List<String> receiversList,List<String> receiversCcList,String title,String content,String showType){
-
-        return MailUtils.sendMails(receiversList, receiversCcList, title, content, showType);
-    }
-
-    /**
-     * msg send
-     * @param receiversList the receiver list
-     * @param title the title
-     * @param content the content
-     * @param showType the showType
-     * @return the send result
-     */
-    public Map<String,Object> send(List<String> receiversList,String title,String content,String showType){
-
-        return MailUtils.sendMails(receiversList,title, content, showType);
-    }
-    
-}
+///*
+// * Licensed to the Apache Software Foundation (ASF) under one or more
+// * contributor license agreements.  See the NOTICE file distributed with
+// * this work for additional information regarding copyright ownership.
+// * The ASF licenses this file to You 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 org.apache.dolphinscheduler.alert.manager;
+//
+//import org.apache.dolphinscheduler.alert.utils.MailUtils;
+//
+//import java.util.List;
+//import java.util.Map;
+//
+///**
+// * email send manager
+// */
+//public class EmailManager {
+//    /**
+//     * email send
+//     * @param receiversList the receiver list
+//     * @param receiversCcList the cc List
+//     * @param title the title
+//     * @param content the content
+//     * @param showType the showType
+//     * @return the send result
+//     */
+//    public Map<String,Object> send(List<String> receiversList,List<String> receiversCcList,String title,String content,String showType){
+//
+//        return MailUtils.sendMails(receiversList, receiversCcList, title, content, showType);
+//    }
+//
+//    /**
+//     * msg send
+//     * @param receiversList the receiver list
+//     * @param title the title
+//     * @param content the content
+//     * @param showType the showType
+//     * @return the send result
+//     */
+//    public Map<String,Object> send(List<String> receiversList,String title,String content,String showType){
+//
+//        return MailUtils.sendMails(receiversList,title, content, showType);
+//    }
+//
+//}
diff --git a/dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/manager/EnterpriseWeChatManager.java b/dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/manager/EnterpriseWeChatManager.java
deleted file mode 100644
index 43649d6..0000000
--- a/dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/manager/EnterpriseWeChatManager.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 org.apache.dolphinscheduler.alert.manager;
-
-import org.apache.dolphinscheduler.alert.utils.Constants;
-import org.apache.dolphinscheduler.alert.utils.EnterpriseWeChatUtils;
-import org.apache.dolphinscheduler.plugin.model.AlertInfo;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.IOException;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * Enterprise WeChat Manager
- */
-public class EnterpriseWeChatManager {
-    private static final Logger logger = LoggerFactory.getLogger(EnterpriseWeChatManager.class);
-    /**
-     * Enterprise We Chat send
-     * @param alertInfo the alert info
-     * @param token the token
-     * @return the send result
-     */
-    public Map<String,Object> send(AlertInfo alertInfo, String token){
-        Map<String,Object> retMap = new HashMap<>();
-        retMap.put(Constants.STATUS, false);
-        String agentId = EnterpriseWeChatUtils.ENTERPRISE_WE_CHAT_AGENT_ID;
-        String users = EnterpriseWeChatUtils.ENTERPRISE_WE_CHAT_USERS;
-        List<String> userList = Arrays.asList(users.split(","));
-        logger.info("send message {}", alertInfo.getAlertData().getTitle());
-        String msg = EnterpriseWeChatUtils.makeUserSendMsg(userList, agentId,EnterpriseWeChatUtils.markdownByAlert(alertInfo.getAlertData()));
-        try {
-            EnterpriseWeChatUtils.sendEnterpriseWeChat(Constants.UTF_8, msg, token);
-        } catch (IOException e) {
-            logger.error(e.getMessage(),e);
-        }
-        retMap.put(Constants.STATUS, true);
-        return retMap;
-    }
-
-}
diff --git a/dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/manager/MsgManager.java b/dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/manager/MsgManager.java
index 3594926..e7fb161 100644
--- a/dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/manager/MsgManager.java
+++ b/dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/manager/MsgManager.java
@@ -14,23 +14,27 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package org.apache.dolphinscheduler.alert.manager;
 
 import org.apache.dolphinscheduler.dao.entity.Alert;
+
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 /**
  * SMS send manager
  */
-public class MsgManager  {
+public class MsgManager {
 
     private static final Logger logger = LoggerFactory.getLogger(MsgManager.class);
+
     /**
      * SMS send
+     *
      * @param alert the alert
      */
-    public void send(Alert alert){
-        logger.info("send message {}",alert);
+    public void send(Alert alert) {
+        logger.info("send message {}", alert);
     }
 }
diff --git a/dolphinscheduler-common/src/main/java/org/apache/dolphinscheduler/common/plugin/PluginManager.java b/dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/plugin/AbstractDolphinPluginManager.java
similarity index 65%
copy from dolphinscheduler-common/src/main/java/org/apache/dolphinscheduler/common/plugin/PluginManager.java
copy to dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/plugin/AbstractDolphinPluginManager.java
index f807884..9fd847d 100644
--- a/dolphinscheduler-common/src/main/java/org/apache/dolphinscheduler/common/plugin/PluginManager.java
+++ b/dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/plugin/AbstractDolphinPluginManager.java
@@ -14,20 +14,16 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dolphinscheduler.common.plugin;
 
-import org.apache.dolphinscheduler.plugin.api.AlertPlugin;
+package org.apache.dolphinscheduler.alert.plugin;
 
-import java.util.Map;
+import org.apache.dolphinscheduler.dao.DaoFactory;
+import org.apache.dolphinscheduler.dao.PluginDao;
+import org.apache.dolphinscheduler.spi.DolphinSchedulerPlugin;
 
-/**
- * PluginManager
- */
-public interface PluginManager {
-
-    AlertPlugin findOne(String name);
+public abstract class AbstractDolphinPluginManager {
 
-    Map<String, AlertPlugin> findAll();
+    protected PluginDao pluginDao = DaoFactory.getDaoInstance(PluginDao.class);
 
-    void addPlugin(AlertPlugin plugin);
+    public abstract void installPlugin(DolphinSchedulerPlugin dolphinSchedulerPlugin);
 }
diff --git a/dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/plugin/AlertPluginManager.java b/dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/plugin/AlertPluginManager.java
new file mode 100644
index 0000000..a660087
--- /dev/null
+++ b/dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/plugin/AlertPluginManager.java
@@ -0,0 +1,99 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.dolphinscheduler.alert.plugin;
+
+import static java.lang.String.format;
+import static java.util.Objects.requireNonNull;
+
+import static com.google.common.base.Preconditions.checkState;
+
+import org.apache.dolphinscheduler.common.enums.PluginType;
+import org.apache.dolphinscheduler.dao.entity.PluginDefine;
+import org.apache.dolphinscheduler.spi.DolphinSchedulerPlugin;
+import org.apache.dolphinscheduler.spi.alert.AlertChannel;
+import org.apache.dolphinscheduler.spi.alert.AlertChannelFactory;
+import org.apache.dolphinscheduler.spi.classloader.ThreadContextClassLoader;
+import org.apache.dolphinscheduler.spi.params.PluginParamsTransfer;
+import org.apache.dolphinscheduler.spi.params.base.PluginParams;
+
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * load the configured alert plugin and manager them
+ */
+public class AlertPluginManager extends AbstractDolphinPluginManager {
+    private static final Logger logger = LoggerFactory.getLogger(AlertPluginManager.class);
+
+    private final Map<String, AlertChannelFactory> alertChannelFactoryMap = new ConcurrentHashMap<>();
+    private final Map<String, AlertChannel> alertChannelMap = new ConcurrentHashMap<>();
+
+    public void addAlertChannelFactory(AlertChannelFactory alertChannelFactory) {
+        requireNonNull(alertChannelFactory, "alertChannelFactory is null");
+
+        if (alertChannelFactoryMap.putIfAbsent(alertChannelFactory.getName(), alertChannelFactory) != null) {
+            throw new IllegalArgumentException(format("Alert Plugin '{}' is already registered", alertChannelFactory.getName()));
+        }
+
+        try {
+            loadAlertChannel(alertChannelFactory.getName());
+        } catch (Exception e) {
+            throw new IllegalArgumentException(format("Alert Plugin '{}' is can not load .", alertChannelFactory.getName()));
+        }
+    }
+
+    protected void loadAlertChannel(String name) {
+        requireNonNull(name, "name is null");
+
+        AlertChannelFactory alertChannelFactory = alertChannelFactoryMap.get(name);
+        checkState(alertChannelFactory != null, "Alert Plugin {} is not registered", name);
+
+        try (ThreadContextClassLoader ignored = new ThreadContextClassLoader(alertChannelFactory.getClass().getClassLoader())) {
+            AlertChannel alertChannel = alertChannelFactory.create();
+            this.alertChannelMap.put(name, alertChannel);
+        }
+
+        logger.info("-- Loaded Alert Plugin {} --", name);
+    }
+
+    public Map<String, AlertChannelFactory> getAlertChannelFactoryMap() {
+        return alertChannelFactoryMap;
+    }
+
+    public Map<String, AlertChannel> getAlertChannelMap() {
+        return alertChannelMap;
+    }
+
+    @Override
+    public void installPlugin(DolphinSchedulerPlugin dolphinSchedulerPlugin) {
+        for (AlertChannelFactory alertChannelFactory : dolphinSchedulerPlugin.getAlertChannelFactorys()) {
+            logger.info("Registering Alert Plugin '{}'", alertChannelFactory.getName());
+            this.addAlertChannelFactory(alertChannelFactory);
+            List<PluginParams> params = alertChannelFactory.getParams();
+            String nameEn = alertChannelFactory.getName();
+            String paramsJson = PluginParamsTransfer.transferParamsToJson(params);
+
+            PluginDefine pluginDefine = new PluginDefine(nameEn, PluginType.ALERT.getDesc(), paramsJson);
+            pluginDao.addOrUpdatePluginDefine(pluginDefine);
+        }
+    }
+}
diff --git a/dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/plugin/DolphinPluginClassLoader.java b/dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/plugin/DolphinPluginClassLoader.java
new file mode 100644
index 0000000..81dc4f3
--- /dev/null
+++ b/dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/plugin/DolphinPluginClassLoader.java
@@ -0,0 +1,139 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.dolphinscheduler.alert.plugin;
+
+import static java.util.Objects.requireNonNull;
+
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.Enumeration;
+import java.util.List;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+
+class DolphinPluginClassLoader
+        extends URLClassLoader {
+    private static final ClassLoader PLATFORM_CLASS_LOADER = findPlatformClassLoader();
+
+    private final ClassLoader spiClassLoader;
+    private final List<String> spiPackages;
+    private final List<String> spiResources;
+
+    public DolphinPluginClassLoader(
+            List<URL> urls,
+            ClassLoader spiClassLoader,
+            Iterable<String> spiPackages) {
+        this(urls,
+                spiClassLoader,
+                spiPackages,
+                Iterables.transform(spiPackages, DolphinPluginClassLoader::classNameToResource));
+    }
+
+    private DolphinPluginClassLoader(
+            List<URL> urls,
+            ClassLoader spiClassLoader,
+            Iterable<String> spiPackages,
+            Iterable<String> spiResources) {
+        // plugins should not have access to the system (application) class loader
+        super(urls.toArray(new URL[urls.size()]), PLATFORM_CLASS_LOADER);
+        this.spiClassLoader = requireNonNull(spiClassLoader, "spiClassLoader is null");
+        this.spiPackages = ImmutableList.copyOf(spiPackages);
+        this.spiResources = ImmutableList.copyOf(spiResources);
+    }
+
+    @Override
+    protected Class<?> loadClass(String name, boolean resolve)
+            throws ClassNotFoundException {
+        // grab the magic lock
+        synchronized (getClassLoadingLock(name)) {
+            // Check if class is in the loaded classes cache
+            Class<?> cachedClass = findLoadedClass(name);
+            if (cachedClass != null) {
+                return resolveClass(cachedClass, resolve);
+            }
+
+            // If this is an SPI class, only check SPI class loader
+            if (isSpiClass(name)) {
+                return resolveClass(spiClassLoader.loadClass(name), resolve);
+            }
+
+            // Look for class locally
+            return super.loadClass(name, resolve);
+        }
+    }
+
+    private Class<?> resolveClass(Class<?> clazz, boolean resolve) {
+        if (resolve) {
+            resolveClass(clazz);
+        }
+        return clazz;
+    }
+
+    @Override
+    public URL getResource(String name) {
+        // If this is an SPI resource, only check SPI class loader
+        if (isSpiResource(name)) {
+            return spiClassLoader.getResource(name);
+        }
+
+        // Look for resource locally
+        return super.getResource(name);
+    }
+
+    @Override
+    public Enumeration<URL> getResources(String name)
+            throws IOException {
+        // If this is an SPI resource, use SPI resources
+        if (isSpiClass(name)) {
+            return spiClassLoader.getResources(name);
+        }
+
+        // Use local resources
+        return super.getResources(name);
+    }
+
+    private boolean isSpiClass(String name) {
+        return spiPackages.stream().anyMatch(name::startsWith);
+    }
+
+    private boolean isSpiResource(String name) {
+        return spiResources.stream().anyMatch(name::startsWith);
+    }
+
+    private static String classNameToResource(String className) {
+        return className.replace('.', '/');
+    }
+
+    @SuppressWarnings("JavaReflectionMemberAccess")
+    private static ClassLoader findPlatformClassLoader() {
+        try {
+            // use platform class loader on Java 9
+            Method method = ClassLoader.class.getMethod("getPlatformClassLoader");
+            return (ClassLoader) method.invoke(null);
+        } catch (NoSuchMethodException ignored) {
+            // use null class loader on Java 8
+            return null;
+        } catch (IllegalAccessException | InvocationTargetException e) {
+            throw new AssertionError(e);
+        }
+    }
+}
diff --git a/dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/plugin/DolphinPluginDiscovery.java b/dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/plugin/DolphinPluginDiscovery.java
new file mode 100644
index 0000000..7e5cb54
--- /dev/null
+++ b/dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/plugin/DolphinPluginDiscovery.java
@@ -0,0 +1,139 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.dolphinscheduler.alert.plugin;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static java.nio.file.Files.createDirectories;
+import static java.nio.file.Files.walkFileTree;
+
+import static com.google.common.io.ByteStreams.toByteArray;
+
+import org.apache.dolphinscheduler.spi.DolphinSchedulerPlugin;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStreamWriter;
+import java.io.UncheckedIOException;
+import java.io.Writer;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Path;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import org.objectweb.asm.ClassReader;
+import org.sonatype.aether.artifact.Artifact;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+
+/**
+ * The role of this class is to load the plugin class during development
+ */
+final class DolphinPluginDiscovery {
+    private static final String JAVA_CLASS_FILE_SUFFIX = ".class";
+    private static final String PLUGIN_SERVICES_FILE = "META-INF/services/" + DolphinSchedulerPlugin.class.getName();
+
+    private DolphinPluginDiscovery() {
+    }
+
+    public static Set<String> discoverPluginsFromArtifact(Artifact artifact, ClassLoader classLoader)
+            throws IOException {
+        if (!artifact.getExtension().equals("dolphinscheduler-plugin")) {
+            throw new RuntimeException("Unexpected extension for main artifact: " + artifact);
+        }
+
+        File file = artifact.getFile();
+        if (!file.getPath().endsWith("/target/classes")) {
+            throw new RuntimeException("Unexpected file for main artifact: " + file);
+        }
+        if (!file.isDirectory()) {
+            throw new RuntimeException("Main artifact file is not a directory: " + file);
+        }
+
+        if (new File(file, PLUGIN_SERVICES_FILE).exists()) {
+            return ImmutableSet.of();
+        }
+
+        return listClasses(file.toPath()).stream()
+                .filter(name -> classInterfaces(name, classLoader).contains(DolphinSchedulerPlugin.class.getName()))
+                .collect(Collectors.toSet());
+    }
+
+    public static void writePluginServices(Iterable<String> plugins, File root)
+            throws IOException {
+        Path path = root.toPath().resolve(PLUGIN_SERVICES_FILE);
+        createDirectories(path.getParent());
+        try (Writer out = new OutputStreamWriter(new FileOutputStream(path.toFile()), UTF_8)) {
+            for (String plugin : plugins) {
+                out.write(plugin + "\n");
+            }
+        }
+    }
+
+    private static List<String> listClasses(Path base)
+            throws IOException {
+        ImmutableList.Builder<String> list = ImmutableList.builder();
+        walkFileTree(base, new SimpleFileVisitor<Path>() {
+            @Override
+            public FileVisitResult visitFile(Path file, BasicFileAttributes attributes) {
+                if (file.getFileName().toString().endsWith(JAVA_CLASS_FILE_SUFFIX)) {
+                    String name = file.subpath(base.getNameCount(), file.getNameCount()).toString();
+                    list.add(javaName(name.substring(0, name.length() - JAVA_CLASS_FILE_SUFFIX.length())));
+                }
+                return FileVisitResult.CONTINUE;
+            }
+        });
+        return list.build();
+    }
+
+    private static List<String> classInterfaces(String name, ClassLoader classLoader) {
+        ImmutableList.Builder<String> list = ImmutableList.builder();
+        ClassReader reader = readClass(name, classLoader);
+        for (String binaryName : reader.getInterfaces()) {
+            list.add(javaName(binaryName));
+        }
+        if (reader.getSuperName() != null) {
+            list.addAll(classInterfaces(javaName(reader.getSuperName()), classLoader));
+        }
+        return list.build();
+    }
+
+    private static ClassReader readClass(String name, ClassLoader classLoader) {
+        try (InputStream in = classLoader.getResourceAsStream(binaryName(name) + JAVA_CLASS_FILE_SUFFIX)) {
+            if (in == null) {
+                throw new RuntimeException("Failed to read class: " + name);
+            }
+            return new ClassReader(toByteArray(in));
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
+
+    private static String binaryName(String javaName) {
+        return javaName.replace('.', '/');
+    }
+
+    private static String javaName(String binaryName) {
+        return binaryName.replace('/', '.');
+    }
+}
diff --git a/dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/plugin/DolphinPluginLoader.java b/dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/plugin/DolphinPluginLoader.java
new file mode 100644
index 0000000..7dffe1d
--- /dev/null
+++ b/dolphinscheduler-alert/src/main/java/org/apache/dolphinscheduler/alert/plugin/DolphinPluginLoader.java
@@ -0,0 +1,194 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.dolphinscheduler.alert.plugin;
+
+import static java.lang.String.format;
+import static java.util.Objects.requireNonNull;
+
+import static com.google.common.base.Preconditions.checkState;
+
+import org.apache.dolphinscheduler.spi.DolphinSchedulerPlugin;
+import org.apache.dolphinscheduler.spi.classloader.ThreadContextClassLoader;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.ServiceLoader;
+import java.util.Set;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.sonatype.aether.artifact.Artifact;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Ordering;
+
+import io.airlift.resolver.ArtifactResolver;
+
+/**
+ * Plugin Loader
+ * Load Plugin from pom when development and run server in IDE
+ * Load Plugin from the plugin directory when running on the server
+ */
+public class DolphinPluginLoader {
+    private static final Logger logger = LoggerFactory.getLogger(DolphinPluginLoader.class);
+
+    /**
+     * All third-party jar packages used in the classes which in spi package need to be add
+     */
+    private static final ImmutableList<String> DOLPHIN_SPI_PACKAGES = ImmutableList.<String>builder()
+            .add("org.apache.dolphinscheduler.spi.")
+            .add("com.fasterxml.jackson.")
+            .build();
+
+    private final File installedPluginsDir;
+    private final List<String> configPlugins;
+    private ArtifactResolver resolver = null;
+    private final List<AbstractDolphinPluginManager> dolphinPluginManagerList;
+
+    public DolphinPluginLoader(DolphinPluginManagerConfig config, List<AbstractDolphinPluginManager> dolphinPluginManagerList) {
+        installedPluginsDir = config.getInstalledPluginsDir();
+        if (config.getPlugins() == null) {
+            this.configPlugins = ImmutableList.of();
+        } else {
+            this.configPlugins = ImmutableList.copyOf(config.getPlugins());
+        }
+
+        this.dolphinPluginManagerList = requireNonNull(dolphinPluginManagerList, "dolphinPluginManagerList is null");
+        if (configPlugins != null && configPlugins.size() > 0) {
+            this.resolver = new ArtifactResolver(config.getMavenLocalRepository(), config.getMavenRemoteRepository());
+        }
+    }
+
+    public void loadPlugins()
+            throws Exception {
+        for (File file : listPluginDirs(installedPluginsDir)) {
+            if (file.isDirectory()) {
+                loadPlugin(file.getAbsolutePath());
+            }
+        }
+
+        for (String plugin : configPlugins) {
... 17420 lines suppressed ...