├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── docs ├── 任务流回滚.drawio ├── 任务流回滚.jpg ├── 任务流回滚生命周期.drawio ├── 任务流回滚生命周期.jpg ├── 任务流执行.drawio ├── 任务流执行.jpg ├── 任务流执行生命周期.drawio ├── 任务流执行生命周期.jpg ├── 架构图.drawio ├── 架构图.jpg ├── 集群模式.drawio └── 集群模式.jpg ├── pom.xml ├── wolf-flow-core ├── pom.xml └── src │ ├── main │ ├── java │ │ └── me │ │ │ └── kpali │ │ │ └── wolfflow │ │ │ └── core │ │ │ ├── WolfFlowCoreApplication.java │ │ │ ├── cluster │ │ │ ├── IClusterController.java │ │ │ └── impl │ │ │ │ └── DefaultClusterController.java │ │ │ ├── config │ │ │ ├── ClusterConfig.java │ │ │ ├── ExecutorConfig.java │ │ │ └── SchedulerConfig.java │ │ │ ├── enums │ │ │ ├── TaskFlowScheduleStatusEnum.java │ │ │ ├── TaskFlowStatusEnum.java │ │ │ └── TaskStatusEnum.java │ │ │ ├── event │ │ │ ├── ScheduleStatusChangeEvent.java │ │ │ ├── ScheduleStatusEventPublisher.java │ │ │ ├── TaskFlowStatusChangeEvent.java │ │ │ ├── TaskFlowStatusEventPublisher.java │ │ │ ├── TaskStatusChangeEvent.java │ │ │ └── TaskStatusEventPublisher.java │ │ │ ├── exception │ │ │ ├── GenerateNodeIdException.java │ │ │ ├── InvalidCronExpressionException.java │ │ │ ├── InvalidTaskFlowException.java │ │ │ ├── TaskExecuteException.java │ │ │ ├── TaskFlowExecuteException.java │ │ │ ├── TaskFlowInterruptedException.java │ │ │ ├── TaskFlowLogException.java │ │ │ ├── TaskFlowQueryException.java │ │ │ ├── TaskFlowRollbackException.java │ │ │ ├── TaskFlowStopException.java │ │ │ ├── TaskFlowTriggerException.java │ │ │ ├── TaskInterruptedException.java │ │ │ ├── TaskLogException.java │ │ │ ├── TaskRollbackException.java │ │ │ ├── TaskStopException.java │ │ │ └── TryLockException.java │ │ │ ├── executor │ │ │ ├── ITaskFlowExecutor.java │ │ │ └── impl │ │ │ │ └── DefaultTaskFlowExecutor.java │ │ │ ├── launcher │ │ │ └── Launcher.java │ │ │ ├── logger │ │ │ ├── ITaskFlowLogger.java │ │ │ ├── ITaskLogger.java │ │ │ └── impl │ │ │ │ ├── DefaultTaskFlowLogger.java │ │ │ │ └── DefaultTaskLogger.java │ │ │ ├── model │ │ │ ├── ClusterConstants.java │ │ │ ├── DeliveryContextKey.java │ │ │ ├── Link.java │ │ │ ├── ManualConfirmed.java │ │ │ ├── Task.java │ │ │ ├── TaskContextKey.java │ │ │ ├── TaskFlow.java │ │ │ ├── TaskFlowContextKey.java │ │ │ ├── TaskFlowExecRequest.java │ │ │ ├── TaskFlowLog.java │ │ │ ├── TaskFlowStatus.java │ │ │ ├── TaskLog.java │ │ │ ├── TaskLogLine.java │ │ │ ├── TaskLogResult.java │ │ │ └── TaskStatus.java │ │ │ ├── monitor │ │ │ ├── IMonitor.java │ │ │ └── impl │ │ │ │ └── DefaultMonitor.java │ │ │ ├── querier │ │ │ ├── ITaskFlowQuerier.java │ │ │ └── impl │ │ │ │ └── DefaultTaskFlowQuerier.java │ │ │ ├── scheduler │ │ │ ├── ITaskFlowScheduler.java │ │ │ └── impl │ │ │ │ ├── DefaultTaskFlowScheduler.java │ │ │ │ └── quartz │ │ │ │ ├── MyDynamicScheduler.java │ │ │ │ ├── MyDynamicSchedulerConfig.java │ │ │ │ ├── MyJobFactory.java │ │ │ │ └── MyQuartzJobBean.java │ │ │ └── util │ │ │ ├── IdGenerator.java │ │ │ ├── SnowFlake.java │ │ │ ├── TaskFlowUtils.java │ │ │ └── context │ │ │ ├── ContextWrapper.java │ │ │ ├── DeliveryContextWrapper.java │ │ │ ├── ParamsWrapper.java │ │ │ ├── TaskContextWrapper.java │ │ │ └── TaskFlowContextWrapper.java │ └── resources │ │ └── quartz.properties │ └── test │ └── java │ └── me │ └── kpali │ └── wolfflow │ └── core │ ├── BaseTest.java │ ├── cluster │ └── impl │ │ └── DefaultClusterControllerTest.java │ ├── listener │ └── TaskFlowEventListener.java │ ├── model │ ├── AutoTask.java │ └── ManualTask.java │ ├── scheduler │ └── impl │ │ └── DefaultTaskFlowSchedulerTest.java │ └── util │ └── SpringContextUtil.java ├── wolf-flow-sample-cluster ├── pom.xml └── src │ ├── main │ ├── java │ │ └── me │ │ │ └── kpali │ │ │ └── wolfflow │ │ │ └── sample │ │ │ └── cluster │ │ │ ├── WolfFlowSampleClusterApplication.java │ │ │ ├── config │ │ │ └── RedissonSpringDataConfig.java │ │ │ ├── controller │ │ │ ├── MetricsController.java │ │ │ └── TriggerController.java │ │ │ ├── listener │ │ │ ├── ApplicationReadyEventListener.java │ │ │ └── TaskFlowEventListener.java │ │ │ ├── taskflow │ │ │ ├── MyClusterController.java │ │ │ ├── MyTask.java │ │ │ ├── MyTaskFlowExecutor.java │ │ │ ├── MyTaskFlowLogger.java │ │ │ ├── MyTaskFlowQuerier.java │ │ │ ├── MyTaskFlowScheduler.java │ │ │ └── MyTaskLogger.java │ │ │ └── util │ │ │ └── SpringContextUtil.java │ └── resources │ │ ├── application.properties │ │ ├── quartz.properties │ │ └── redisson.yaml │ └── test │ └── java │ └── me │ └── kpali │ └── wolfflow │ └── sample │ └── cluster │ └── WolfFlowSampleClusterApplicationTests.java ├── wolf-flow-sample ├── pom.xml └── src │ ├── main │ ├── java │ │ └── me │ │ │ └── kpali │ │ │ └── wolfflow │ │ │ └── sample │ │ │ ├── WolfFlowSampleApplication.java │ │ │ ├── controller │ │ │ ├── MetricsController.java │ │ │ └── TriggerController.java │ │ │ ├── listener │ │ │ ├── ApplicationReadyEventListener.java │ │ │ └── TaskFlowEventListener.java │ │ │ ├── taskflow │ │ │ ├── MyTask.java │ │ │ └── MyTaskFlowQuerier.java │ │ │ └── util │ │ │ └── SpringContextUtil.java │ └── resources │ │ ├── application.properties │ │ └── quartz.properties │ └── test │ └── java │ └── me │ └── kpali │ └── wolfflow │ └── sample │ └── WolfFlowSampleApplicationTests.java └── wolf-flow-spring-boot-starter ├── pom.xml └── src └── main ├── java └── me │ └── kpali │ └── wolfflow │ └── autoconfigure │ ├── WolfFlowAutoConfiguration.java │ ├── config │ ├── ClusterConfiguration.java │ ├── EventPublisherConfiguration.java │ ├── ExecutorConfiguration.java │ ├── LauncherConfiguration.java │ ├── LoggerConfiguration.java │ ├── MonitorConfiguration.java │ ├── QuerierConfiguration.java │ ├── SchedulerConfiguration.java │ └── UtilConfiguration.java │ └── properties │ ├── ClusterProperties.java │ ├── ExecutorProperties.java │ └── SchedulerProperties.java └── resources └── META-INF └── spring.factories /.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/** 5 | !**/src/test/** 6 | .flattened-pom.xml 7 | 8 | ### STS ### 9 | .apt_generated 10 | .classpath 11 | .factorypath 12 | .project 13 | .settings 14 | .springBeans 15 | .sts4-cache 16 | 17 | ### IntelliJ IDEA ### 18 | .idea 19 | *.iws 20 | *.iml 21 | *.ipr 22 | 23 | ### NetBeans ### 24 | /nbproject/private/ 25 | /nbbuild/ 26 | /dist/ 27 | /nbdist/ 28 | /.nb-gradle/ 29 | build/ 30 | 31 | ### VS Code ### 32 | .vscode/ 33 | 34 | ### MacOS ### 35 | .DS_Store -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | 3 | jdk: 4 | - openjdk8 5 | 6 | install: 7 | - mvn clean install -DskipTests=true -Dmaven.javadoc.skip=true -B -V 8 | 9 | script: 10 | - mvn test -B 11 | 12 | after_success: 13 | - bash <(curl -s https://codecov.io/bash) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 kpali 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /docs/任务流回滚.drawio: -------------------------------------------------------------------------------- 1 | 7VjRbtowFP2aPDKROAR4BErbVdtUjWlt9zK5iUm8OTE1DoR9/RxyTeIkZUilpesmIZF7ru1cn3OPIbHQJM4uBF5EH3lAmOV0g8xCZ5bj2G53oL5yZFMgAw2EggYwqARm9BcBsAtoSgOyNAZKzpmkCxP0eZIQXxoYFoKvzWFzzsy7LnBIGsDMx6yJ3tBARrALp1/il4SGkb6z7Q2LTIz1YNjJMsIBX1cgNLXQRHAui6s4mxCWk6d5KeadP5LdFSZIIg+Z8PXy+nP/y8y+ev+QYiTOyZDbHVhlhVkKG7amrjUe559pzxqMrJENY5Zyo0kRPE0Ckq9rW2i8jqgkswX28+xatYHCIhkzSM8pYxPOuNjORfP53PF9hS+l4D9JJRN4917PU5nmznSZREiSVSDY6QXhMZFio4ZAFrnAOrRdH8J1qaGthYkq+nmAYWibcLdyyay6AHLbiZ6EV7F3/+0Tjq5v2febq5Xf+dVxmkTXWSWB6jwIuZARD3mC2bRExyXvXRWVYz5wvgC2fxApN2AjnEpuaqEIFZvbfP67ng7vqrmzDBYvog1EfipWO72LwvNqH+1E3TI8FT7Z037gIolFSOSf2rQptiAMS7oy62iTDqZec6oq3DVJr2s2iT2sqV/UBbNqDbAr46Ce2Lf7veZDf6H53Jr5Tu8+9xjuM0xwMise0X3OSd2H7NO6r+VEbriv2Sav333qWHtl7uud3mwZlRWvqehOm0tdl07LA220V2BQ90CDOk806KHq7ityr4/cN+Aj9yV91Mr0sOVXzGMyJ4hvT9aSX+8h5TrRWW5tMVIDFCVZmVRX4fZ76lhD2xp19XKqumJFyNfFUwxKUyFTiYQnpCYbQJjRMFGhr3QhCh/nelD1uDWCREyDYHsAtLWEeQ4cQWM0qGk8bGrstkjsPJfE+jexRePlAidP19ipaFys+NY13vkWND65xC3P3EeV2P7nJEZ9ZP7leUGNW//xeP9P6qbEDT1bVH9U4l7Nxc8osQrLt3LF00b5bhNNfwM= -------------------------------------------------------------------------------- /docs/任务流回滚.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kpali/wolf-flow/2bac995f37f20c26c36fede6e9f65c0a013b1dd3/docs/任务流回滚.jpg -------------------------------------------------------------------------------- /docs/任务流回滚生命周期.drawio: -------------------------------------------------------------------------------- 1 | 7Vttb6M4EP41lu4+bAXYvH2ElHSr7W2rZF8/VRRMwpbGWYdskvv1Z4MJIXhbqiYENidVKh7jgGeeZzyeMQAOntZX1J9P/yEhToCmhGsAL4GmqUix2D8u2eQSqxBMaBzmIqUUjON/sRhZSJdxiBdClotSQpI0nleFAZnNcJBWZD6lZFW9LSJJWBHM/QmuCcaBn9SlX+MwnYpZaGYpf4/jybR4smrYec+TX9wsZrKY+iFZ7YigB+CAEpLmV0/rAU648qp6Gf6md/tiFM/SJgPe6ZFyq72/+/j480vwgX6bzn98eSd+5ZefLMWErz/5i8dhQlbjYIrDZYLpBSVJ8uAHj3/9LWaSbgr1ULKchZg/QQXQXU3jFI/nfsB7VwwQTDZNnxLRHZFZKiysItZesDvj2WQk1KcwUX1SxRtimuL1jkhM8gqTJ5zSDbtF9OqKULhAnGqK9qq0n4aEbLpjO0PIfAGZyfanS62yC6HYVyjZrKkNhwxkokloOiUTMvMTr5S6pWK5Wsp7bgiZC3X+wGm6Efr0lympKhuv4/QbH36hi9b3nZ7LtfjlrLEpGjM23XyQqRft77ud5bisVQx8i2UXZEkD/Iz6kKC9Tyc4fRnLXLfP4oTixE/jX1WCH9zoqMYs4OnA8YDtAM8AtglcA3gWcEzgGLzLGgJbrSGlioNOEMwwqgQz6vxStTb5ZfSQX1p3+KX3kl+6hF8GsGxguRmbHOBY/eQX6hq/VK2u654w7qjEKeLRF4lzcOaIoXckZu9cOmatChwI9xCRz0iM2gPF9jXegBPJmncCnBzX5o2NDjvlLlVJpD9IlosU0wHTFw/xWaSP1zgY4Z9LvEhvowjTfkb82skjftU4AybApkzQu8UE+Eom3DFJP4kAT06EIk/yRxOhaQCtGd0iQj2E3iZ/PAZ/pkV68YAjQvGo1xkgdHoa2H8+DQp0v0wDq1M00OprdZ0Gh0qBchDHgZ84STyZMVnKLXciWuhNaYGUo/HCaqB7P2JLc789kKqoJ3dBkk08T5gMga0ADwHXAQ7KLlz+J1IoKr/HZdcql9gusL1M4mZpTAs4Q+CamUQFVt2nvS67EifJgCSEZmNhFEVaEHATsZjsEe/0hMaDoRvNjOkLoiU4Sg9jW9NUK7aV7TbUdk1bj2i5bSwvs02e+TL+N+3rTSuLn1WktmlaWRmhltR8zrTMigNgDbJ7iuQoYzwDx3OjTC7hBQrmIRQBEf4sK6tdsFG5q7C428gfYTtfnetP98Pb0f3o9ubGdQYfzhIzsnIjUuw2MSNLjR/Y1tzE1x+v7jMzVzDW3MXoWbb+kr+Ae8krY7w+hoCFuIRDtF7XOQcAyXYrSG1zPSnSxk0BlJVdmEvijknNcLB1Mb83bYuOqfBH9+PPg4E3Hp8lrKQhKDLaXMxgPQf9OpdxjnbTdUkMAttcT6Bk57BvicXUn/PLYEmTjUvZRo2nAF4ySXXn1lJ1VW+QHJJ4W90+mn5l4fuWFpkzY66OkcBhrlQHHvO1+V5NtqRyn5qdNOEu0Cq9rGYk3CAPlF1NMqhywlnAto/nbofO9c3nkXcWtFWRVYGVCfU6rIqIph3ayrYOdVPXNxPPRHPd2xaEPrYiKR6MwMIP0enc+N4hDq2OB4hadeNnUIqBTUsxsFulGFjfsvE0aD+rL3unUExVu5D4QqXVHc0Z1F9g0/oL7Fb9BdbrLxn2e3bqfA/1liILK1vFfL22kum1j+WUfeXap1YuapYiOWpq7eQZtb7EXmbxzdFu7GVbbcKlWebjqAmP3ljLNmvWave8M5IkPBry78Bpz85lO/sCItkKgVrNmiF0igizc2fkUdNTorBbp0SRvKbOcyUwI59XnJt4sfDaoFz7NoqeJAaTJTQOFYOxZvldbP51Qvl1MfT+Aw== -------------------------------------------------------------------------------- /docs/任务流回滚生命周期.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kpali/wolf-flow/2bac995f37f20c26c36fede6e9f65c0a013b1dd3/docs/任务流回滚生命周期.jpg -------------------------------------------------------------------------------- /docs/任务流执行.drawio: -------------------------------------------------------------------------------- 1 | 7Zhdb5swFIZ/DZeVgk0guUwyum7apGmZtmsXHPBmONSYfOzXz4AJONAoUxvRdZUiBb/n+Ot9zAnBwqtk/16QLP4MIeUWmoR7C7+zELInHlJfpXKoFW+uhUiwUCe1wpr9pk1PrRYspLmRKAG4ZJkpBpCmNJCGRoSAnZm2AW7OmpGI9oR1QHhf/cFCGdfqDHmtfkdZFDcz2+68jiSkSdY7yWMSwq4jYd/CKwEg66tkv6K8NK/xpe53+0j0uDBBU3lJh+93X75639b2xw8PBcHils7BvsH1KFvCC71hvVh5aBygoTJEN0HIGCJICfdbdSmgSENaTjNRrTbnE0CmRFuJP6mUB02XFBKUFMuE62hQiG3Vv2zUCyhnfXSjWsqhEAE9s7vmwBARUXkmDx1xqHNMIaFSHFQ/QTmRbGuug+gDFR3zWs/Vhbb9LxDM3hCoPG9MBHYfge9Yy2X58afWbGEtbLtHpfW89GwXM0nXGanc2KliaPq7YZyvgIOo+uLNZoOCQOm5FPCLdiKhe+9O3SOELRWS7s9j6NumO2BH156m+Ormrq1kdlOe4k4VcydXMtq7wGj8DxrtnBg9vtPTV11V0IVVxRmzqqALDnufyss/7Gj60g67c4HTzitw2hnd6flAWXG5LA0Ctaeuv+5DAU3gJq8qxEIlqP3v26C6iqpvH1lz21pMmuHU6uoRdfwUnjJVmoRMEimk9ASblghnUaqagYJAlb4sETH1xL/QgYSFYVX+ho6EWRKf46fDMxnP+4idAcLoWoSbP14DiPOMpE9HjDqI6xFfO2LsYfM2Hp3xwAPvszK2/3vG49/HA48fb4yfxHh6UqqvyFg12zdDVazzfg37fwA= -------------------------------------------------------------------------------- /docs/任务流执行.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kpali/wolf-flow/2bac995f37f20c26c36fede6e9f65c0a013b1dd3/docs/任务流执行.jpg -------------------------------------------------------------------------------- /docs/任务流执行生命周期.drawio: -------------------------------------------------------------------------------- 1 | 7Vttc5s4EP41mrn70Awg8fYROzjNXK/JxEmbfsoQEDYNsVwsN3Z//UkggrHUhExsDPXNZCZoJRm0+zzLalcAOHxcnWXBfPoviXAKDC1aAXgKDENHmsP+ccm6kDilYJIlUSHSKsE4+YXFzFK6TCK8ELJCRAlJaTKvC0Mym+GQ1mRBlpGn+rCYpFFNMA8mWBKMwyCVpV+TiE7FKgy7kn/EyWRa3lm33KLnMSgHi5UspkFEnjZE0AdwmBFCi6vH1RCnXHl1vYx+0/v8YBme0SYTPpixdmF8vPz88ONL+E92O51///JB/MrPIF2KBZ9fB4uHUUqexuEUR8sUZyd4hcMlxX/9LRZC16V2MrKcRZjfQAdw8DRNKB7Pg5D3PjE8MNmUPqaiOyYzKgysI9ZesJHJbHIltKcxkbym8gFxRvFqQyTWeIbJI6bZmg0RvaYm9C0Ap9ui/VSZz0BCNt0wnSVkgUDM5PmnK6WyC6HXN+jYltSGI4Yx0SQZnZIJmQWpX0kHlWK5WqoxnwiZC3V+x5SuhT6DJSV1ZeNVQm/59BNTtL5t9JyuxC/njXXZmLHlFpNss2x/2+ys5uWtcuJ7LLsgyyzEL6gPCdYH2QTT16HMdfsiTjKcBjT5Wef3zo2OJGIB3wSeD1wP+BZwbTCwgO8AzwaexbucEXB1CSl1HHSCYJZVJ5gl80s32uSX1UN+Gd3hl9lLfpkKflnAcYEzyNnkAc/pJ79Q1/ilG7Kue8K4vRKnDEdfJc7OmSOmXpKEPXPlmI06cCDcQkSxIjFrCxTPj/EOnCjeeQfAyX5t3tjosFPuUlcE+sN0uaA4GzJ9ZSQtA/0r/GOJF/QijnHWz4jfOHjEr1tHwATYlAlmt5gA38iESybpJxHgwYlQpkn+aCI0DaANq1tEkEPo59yPn2d8SHZyj2OSYb/PCSB0eBa4fz4LSnC/zgKnUyww5Fe1zIIdJUA5hpMwSL00mcyYjHLDHYgVZlNWIG1vtHAaqD6I2Yu51/5H1/SDOyDFDp5nS0bA1YCPwMADHsovBvxP5E90PmbArnWRWvHsPKOiA2eYX4zAwM7HMIns0d6WWknSdEhSkuVzYRzHRhhyE7GA7AFv9ETWvWVazYwZCJ6lOKa7sa1t6zXbqrYaerumlcNZbhvHz21TpL2s/037dtOqgmcd6W2aVlVDkDKaL5mWWXGYW5SNKTOjjPEMHC/NsrmEVycYMrRcUtzLyQsXbFbhKhzuNopbuN5X7/z6bnRxdeff+sOba/8oIaMqNSLNbRMyqrT4jk1dWPj881kdX83di5mn6U/53QenvCTGC2MIOIhLODzlgs4xoEe1T0F6m++SMl/cFD0q0wr38nvTtuiUhC+6G98Mh/54fJSoUkafyGrzPQbl3PPbYHWMdjNNRfgB23yXQMWmYdsSi2kw55fhMkvXgywIH/je/zWT1DdtLVVVzQZZIYWzNd296VcVuT/TIvdlzNMxNnjMk5rAZ5wotmkqt8tdan7ChHtAp3KyhpVyg9xn7GqSQ5UTzgGuuzdvO/LOP91cHUcEqCOnhiobmjKqynimHdaqNg2ypeVtxAuxXOc2BFGAnVgJByt08H18OCe+dXTDkOEAUatO/AgKMLBpAQZ2qwAD5c0aT3/2suaydfTE1o0ThSfUWt3NHEHVBTatusBuVV2gXHXJod+vk+ZboHc0VUjZKuTlikqu1h4WUbZ16x5at6hZcmSfGbUDJ9L6EnXZ5SdGm1GX67SJlaYZjz0mOnpjLdeWrNXu+WakSHQ05N+Os51dS3L2BUOqtwNqNVmG0CFiy84diUdND4XCbh0KReoqOs+RwJx7fnlS4tVSa4MC7fsoepD4S5XJ2FX8xZrVV7DFxwjVt8TQ/w8= -------------------------------------------------------------------------------- /docs/任务流执行生命周期.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kpali/wolf-flow/2bac995f37f20c26c36fede6e9f65c0a013b1dd3/docs/任务流执行生命周期.jpg -------------------------------------------------------------------------------- /docs/架构图.drawio: -------------------------------------------------------------------------------- 1 | 1Vtbd5s4EP41ekwOEqDLI8Y47e5JL5uT7eaphxrFpsXGxfjWX7+SANuA7PiKCfFxpBEI+L6ZkWYkA9MdLR8SfzJ8jAMeAWQES2B2AULQIEj8k5JVJiEsFwySMMhExkbwFP7hxZW5dBYGfJrLMlEax1EaTsrCfjwe835akvlJEi/Kp73GUVASTPwBrwme+n5Ul34Lg3SYSyFmm4YPPBwM81tTRLKGkV+cnL/JdOgH8WJLZHrAdJM4TrPSaOnySIJXxqW3o3X9YAkfp4dc8Ezm0+iZfIbRKFl8Y+nrbPz1DuKsm7kfzfI3zp82XRUQJPFsHHDZiwHMzmIYpvxp4vdl60KQLmTDdBSJGhTF1zCK3DiKE3Wt2et52HWlPB6nObvQEvX60+cvNOdJypdbovxtHng84mmyEqfkrQiZ2SW5alk5JYsNT8TKex1uUUQMmutHrhuDddcb+EQhR1CPpvtz/mRYq3n0Yjy5n+LZb/o8u7PqYHoW6HTkx7MBdYADgYdBR5RVgTLgEOBRKaeuPIcJCd1DATyaAtftiUPIJbKh0GwnCgdj0ZbGkysRY6MyMaTOjMWMOjMQ2VdihmiYYYB1AMXAI6DjAcdShHiKEMEPBR18IUJ2AF+lybMVTQ0QQmiND0yIhg/LOJ8Pvd85zlIEGQagpmzqiKZLEaOxFOWspmkS/+JbLRT9MDFu1obgvV0iDZkHWtF6xLk4a0jDGpVkUSStSHx3mOKop8gSrAnTMs/k6ArYMlpCFqK6PUCkQRZfC1jz3IFDFHoSbXkOlES0DXNsWBV9vj3qRw7X6pu5UuJ0gYPeAeoE45Zhbh+FOcKReKDOj0SUBrJ0kO7LfnqAQTnEU0P10zJeUDHaFr6dmoV13IwZTSRwHDO2nE8xT50jCs67ZMYya+NuC7jRTV9t4HgKZQwYUZPVE+woK/Tkpz7J2rpqiyL8eyZDVgX+3VSh74gToDVZbho39xEK4IrZ9NeZn6R/iiprHe9VizTx7VmnTfjKmh60jRlhka1jhh07X2M94Ng5/A5qMq6Uwb8+qBFAMjO40sTDrk73dOELpLrwBV4gPaMPOnXxS52bPSbWMtMgtIZy4cZuZhpQF8voDODw0StLx7DCj50d9TdBRMNeSpv00s21FdySCRWlM9KsK3LdayWDiV3GX5dz1LubIjd2eUs4Oh1coab9Wm5b1q3dzaEB5bvDFlv2zec5UBd47HXUlRCwbTBbJq2qMLo5yMfN8+t5qLaBjA2jZRAXN6sE0GJWQdVaQocWga+rQmHR1C3HxFgiLoRrPnaDfvwqqYfln2YYHYVBIG9wDZbom6un0NBwZFrXmp6vX6GS5bAAwyphZMppiqeWFKRlEOVjeiekKVR+opapwGqWeUraY1d/zJbqIzvuyulullhh+xZEztCdK6iIUIDTlITAq1myJryowjkd+hNZfI340pF7QAQafBzkxW4/8qfTsK9CYj9J6+ItsPkyTP+TvNwLLrPqi6oiyvJ6d5nzpiqrrcoXnoTirXmSyw5miAelTSl1frbwtzXwF7KER34aznmpcx0l+R2+xKF4kg39pJx5gRa5pyZh64OUe5zGs6TP8042PNf6rS5Hw6pLEbQMeFrrSCnMGoUzdEgzMW9Ah2BJgeRLn6tAQm+SVaaeBirqWfeWDQvBpn9VW23Xqndou0oyVF66NS18L8K6zYFPU0lowEr8LnQdiklKccBmNVQT1VxTQws9uqsrEsFv6JFWKd+dXpnl9L9NrJMVCVZ7Eoq08ZmsWUU6MoCrrRi0LbaortM0Glt8W/DBw/fv5veXH4+f3Mce+WD81G5l2hNaMMCsNuaSrdqGo0Y3Cyz//fNXvOr+ejSihLkPI2S+LI7cJaZdkn5zy0b7l6Sr4XTDC9If+6PX7mzwD/H/puTjcrX8/XmuYeZLIrse8tl0D4DvNDRGh8bG6AKxsRZvXSqZqGQbLG1bvfymSK6O3Sn+Cvq7Vxkv4fztsiXgoovSlki7Tgw02JWI0a+xiIGTKh/jGKqw9jHFeKDxVflGiaJpOvHHReMo7Cvr4sldwgehwHh1N9myt+wK8fzbF+k62tp/seOalvk+ZJc3sGG77vmEjjXo+XQTqjrhTH5nhWyT+W7CH9fktnd3jF3Yzz4akGYH+dVo0OTMmwiQ1uFPFhzZR8ZGO6loSRi0/llTwXMxEh6d4rHe6OjkuEdUN7+Zyk7f/PLM9P4H -------------------------------------------------------------------------------- /docs/架构图.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kpali/wolf-flow/2bac995f37f20c26c36fede6e9f65c0a013b1dd3/docs/架构图.jpg -------------------------------------------------------------------------------- /docs/集群模式.drawio: -------------------------------------------------------------------------------- 1 | 7VtNc6M4EP01OiaFAPFxBNvsHmarZmsOs3vaIkax2cGWg+XE3l+/EiBAEjg4NiQzFSflQi0QSP3oft0tA2u2Of6Wx7v1HyTBGTCN5AisOTBNaJsm4P9GciolnhCs8jQpRUYj+Jb+h6srhfSQJnhfyUoRJSSj6U4WLsl2i5dUksV5Tl7k0x5JlkiCXbzCmuDbMs506fc0oetKCh2/6fgdp6s1FfNzy45NLE6uZrJfxwl5aYmsBbBmOSG0PNocZzjjiyevS9TTWz9Yjrd0yAXPm+PX7GQSY2c8/bmc7777T/s7WE3jOc4O1Yyrp6UnsQQ5OWwTzEcxgBW+rFOKv+3iJe99YUpnsjXdZKwF2eFjmmUzkpG8uNZ6LD5cTra00i60WXtPc/IDt840ig/rqR4I5xQfe6cK6wVkyMNkg2l+YqeIC4QSBOhQ2XxpNOj5lWzdUh7yBHIq1KzqoZuFZQfV2l6wzpbRsc5Oxu4bPrCDFS0mXgr4UkkacJ4ORHTc7YtFDNgJ0Nkdm856lIUP/BB4Dli4IFyAwBbjsgcvh5Zvx8StR7hK94qOuQ5T9ioFWbraMuEDoZRsdJDg4tMFCsuxfCu5ESgcUwKF5Xs6KkxHRwU7dSRUQGc6VCDgeQUqEAjZgVUczIAXFV3z4sADoVF0OcCfcSHvCkAAp4NQDzJUKFGyO4sXFYo3wQ+U8APFEG38iPe8jR8IvZHwI4z1OeuNE+bPqibJ6ZqsyDbOFo00lBXUnPOF8EUu1PIvpvRUrWd8oERWGlvA/PQXv/4eiebf7b75sRq8bJ2q1vKQPxf37TIemm4dJ4pCq+4R3tistctnel63bGHIIV/iM0taOQ4a5ytMzxn0HrDkOItp+iw/SJfiq0u/krR4ryuQma5ipGxTHqJ8sOoqBT71Y1zhpwbwgdcRJSn2DYDqIAduFEWduldx0wKjeREa3wFEpv9LgsiGGojMe9Yu3EkEfMg5isc8DeSexvNB4HLfw9yMNysOIhC6vCtkEh1/zIRTGS85Zv4vfihO4Krc8ZkVc0UhQHMmiSu/sWS6xHmHQ9mkSVJgN4sfcBbGyx+rwi4O4bK83QXWXqdTBSvVEzchQhtrZ17PXhd1xzDv2Jak/Dvhfd4KKnEKeXzc41Hw4uo2h5OTAPg2J7HsO/A4LhgVYXBgEvYd+lADRqF2nC+ecan9wpSIqIvjIon369rR9dqbLdlinYogg/8JgHwl+5SmpBNSX5QTatrbTWLIgWbplt1dBLBGF17ZPHZ8mpvjigfa90wb6RLf7zGztPn+Pt7tMjY0v+M/pYwPst+VIz6mRzwSK2L990gCXB3yt3gRNGAHLzLNfhBexYu8NwFKtzSfgJoeUMhyFTi57w0nuytMe39SFEVVxuQiUuReRIoU11bf8oZkyR/KuM1RyJJvyP7SEtqeiizp3s/pI0uIf7PInFGjYA4C8xcgSwqibkmWSkWeJ0uGIylf3PPjcqWO+GyAa7M+XdsHcG0eMjWu5Ax1bmPlIE37Fs7tM4f0Fo82Ug5JMFkBMjX8LyegeTRtIBsZsnVEyrOM7BrNLmM3IGHAKyAe8KMq6e27GqAbuMKLc9Kz2QIV4f0IFsKCMhmBjl65EtNpmwdVw7erW+mpHN27FB4oKgoHHldEWBwEC15N4F1zIIptN1NBFM1mY8Uf/gdTQUeSfwgTHPctiCKffcZRgeugD6aCIT5ymwR8vwEnIlm836fLoTltjSHLK71M8IP3oLq6axzYq47J7VZPa/lRR51LyK50X0g2gbanMJ+h3gsasvey3WkDOwtpoIFlYGeDMOT/dXWVv7PFG83fYhcEThP8/Wzx3JjJb/us1WDxnOMJY/lWDNZJcwV04wV0dtfOkPdPLl1ScTumtJ1bYs2aubPjhrjzxgjVNnMo30Yj8W2RdhGGRoDwUotl2XLq04TupBZL+M8WFK3SYjHSvQABKjaGMOvlfNbtLjNdttGNvMZ0oXqXSKV8hG7iTe+giimRgpjAtFkj0KY6YfjxaFNPcXYa2gQhVImTsu1oMHHSa3qWP23kb+uEG9VJcSXw6WdTP5sBGjMXbo3PnXoszh20JzM4HYXfy7nU1LW5mj6Zw+mTZNT6cqdNtvTdinsCU69zM3MUbuarRtFSgsChRtGzZfc8OTfTc6FuHzf7LBNeZBq910zjzcuEYmiRVxKW0lQwNZ6lRDeJOj+rPvqmo/eq+iBPNlC2Cqahlg55SsQwMfdD3dsB1b0QA4oQP5uRq3F6eyOHzgegjP7ZSKb8t0mkiZ/xTWDR9EqVXTtIJVVxTcjw6v6HSjQcOF2FF9my1nsxuszdxVmM4YUYW3FQtlu9VNJ2hY4f0plGP1CuqsQgPUXljREYfnQtv86HhmvZ0Qol42mZNZtfvJavfvO7YWvxPw== -------------------------------------------------------------------------------- /docs/集群模式.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kpali/wolf-flow/2bac995f37f20c26c36fede6e9f65c0a013b1dd3/docs/集群模式.jpg -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 2.2.4.RELEASE 9 | 10 | 11 | me.kpali 12 | wolf-flow 13 | 2.1.1 14 | pom 15 | wolf-flow ${project.version} 16 | Wolf flow is a simple, lightweight job scheduling engine 17 | 18 | 19 | 1.8 20 | ${java.version} 21 | ${java.version} 22 | UTF-8 23 | 24 | 3.6.1 25 | 0.8.6 26 | 2.22.2 27 | 1.6 28 | 29 | 30 | 31 | wolf-flow-core 32 | wolf-flow-spring-boot-starter 33 | 34 | 35 | 36 | 37 | The MIT License (MIT) 38 | https://www.mit-license.org/ 39 | 40 | 41 | 42 | 43 | kpali 44 | kpali@qq.com 45 | 46 | 47 | 48 | https://github.com/kpali/wolf-flow 49 | https://github.com/kpali/wolf-flow.git 50 | https://github.com/kpali/wolf-flow.git 51 | 52 | 53 | 54 | release 55 | 56 | 57 | 58 | 59 | org.apache.maven.plugins 60 | maven-source-plugin 61 | 62 | 63 | package 64 | 65 | jar-no-fork 66 | 67 | 68 | 69 | 70 | 71 | 72 | org.apache.maven.plugins 73 | maven-javadoc-plugin 74 | 75 | 76 | package 77 | 78 | jar 79 | 80 | 81 | 82 | 83 | 84 | 85 | org.apache.maven.plugins 86 | maven-gpg-plugin 87 | ${maven-gpg-plugin.version} 88 | 89 | 90 | verify 91 | 92 | sign 93 | 94 | 95 | 96 | 97 | 98 | 99 | maven-compiler-plugin 100 | ${maven-compiler-plugin.version} 101 | 102 | true 103 | ${maven.compiler.source} 104 | ${maven.compiler.target} 105 | ${project.build.sourceEncoding} 106 | 107 | 108 | 109 | 110 | 111 | 112 | sonatype-nexus-snapshots 113 | https://oss.sonatype.org/content/repositories/snapshots/ 114 | 115 | 116 | sonatype-nexus-staging 117 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 118 | 119 | 120 | 121 | 122 | 123 | 124 | -------------------------------------------------------------------------------- /wolf-flow-core/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | me.kpali 7 | wolf-flow 8 | 2.1.1 9 | 10 | wolf-flow-core 11 | wolf-flow-core ${project.version} 12 | 13 | 14 | 2.10.2 15 | 2.3.0 16 | 20.0 17 | 1.6.0 18 | 19 | 20 | 21 | 22 | org.springframework.boot 23 | spring-boot-starter 24 | provided 25 | 26 | 27 | 28 | 29 | org.springframework 30 | spring-context-support 31 | 32 | 33 | org.springframework 34 | spring-jdbc 35 | 36 | 37 | org.quartz-scheduler 38 | quartz 39 | ${quartz.version} 40 | 41 | 42 | 43 | com.fasterxml.jackson.core 44 | jackson-databind 45 | ${jackson.version} 46 | 47 | 48 | 49 | com.google.guava 50 | guava 51 | ${guava.version} 52 | 53 | 54 | 55 | 56 | io.micrometer 57 | micrometer-registry-prometheus 58 | ${micrometer.version} 59 | 60 | 61 | 62 | 63 | org.springframework.boot 64 | spring-boot-starter-test 65 | test 66 | 67 | 68 | org.junit.vintage 69 | junit-vintage-engine 70 | 71 | 72 | 73 | 74 | org.jacoco 75 | jacoco-maven-plugin 76 | ${maven-jacoco-plugin.version} 77 | maven-plugin 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | maven-compiler-plugin 86 | ${maven-compiler-plugin.version} 87 | 88 | true 89 | ${maven.compiler.source} 90 | ${maven.compiler.target} 91 | ${project.build.sourceEncoding} 92 | 93 | 94 | 95 | 96 | org.jacoco 97 | jacoco-maven-plugin 98 | ${maven-jacoco-plugin.version} 99 | 100 | 101 | 102 | prepare-agent 103 | 104 | 105 | jacocoArgLine 106 | 107 | 108 | 109 | report 110 | test 111 | 112 | report 113 | 114 | 115 | 116 | 117 | 118 | 119 | maven-surefire-plugin 120 | ${maven-surefire-plugin.version} 121 | 122 | 123 | ${jacocoArgLine} 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | -------------------------------------------------------------------------------- /wolf-flow-core/src/main/java/me/kpali/wolfflow/core/WolfFlowCoreApplication.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.core; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class WolfFlowCoreApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(WolfFlowCoreApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /wolf-flow-core/src/main/java/me/kpali/wolfflow/core/cluster/IClusterController.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.core.cluster; 2 | 3 | import me.kpali.wolfflow.core.exception.GenerateNodeIdException; 4 | import me.kpali.wolfflow.core.model.ManualConfirmed; 5 | import me.kpali.wolfflow.core.model.TaskFlowExecRequest; 6 | 7 | import java.util.concurrent.TimeUnit; 8 | 9 | /** 10 | * 集群控制器 11 | * 12 | * @author kpali 13 | */ 14 | public interface IClusterController { 15 | /** 16 | * 启动集群控制器 17 | */ 18 | void startup(); 19 | 20 | /** 21 | * 生成节点ID 22 | */ 23 | void generateNodeId() throws GenerateNodeIdException; 24 | 25 | /** 26 | * 获取当前节点ID 27 | * 28 | * @return 节点ID 29 | */ 30 | Long getNodeId(); 31 | 32 | /** 33 | * 发送当前节点心跳 34 | */ 35 | void heartbeat(); 36 | 37 | /** 38 | * 查询其他节点是否存活 39 | * 40 | * @param nodeId 41 | * @return 42 | */ 43 | boolean isNodeAlive(Long nodeId); 44 | 45 | /** 46 | * 加锁,如果暂时无法加锁,则当前线程休眠,直到加锁成功 47 | * 加锁成功后,解锁需要调用unlock方法 48 | * 49 | * @param name 50 | */ 51 | void lock(String name); 52 | 53 | /** 54 | * 加锁,如果暂时无法加锁,则当前线程休眠,直到加锁成功 55 | * 加锁成功后,解锁需要调用unlock方法或在到达指定时间后自动解锁 56 | * 57 | * @param name 锁名称 58 | * @param leaseTime 租赁时间,即上锁后多久自动解锁 59 | * @param unit 时间单位 60 | */ 61 | void lock(String name, long leaseTime, TimeUnit unit); 62 | 63 | /** 64 | * 尝试加锁 65 | * 66 | * @param name 锁名称 67 | * @param waitTime 等待时间,即尝试加锁的最多等待时间 68 | * @param leaseTime 租赁时间,即上锁后多久自动解锁 69 | * @param unit 时间单位 70 | * @return 成功则返回true 71 | */ 72 | boolean tryLock(String name, long waitTime, long leaseTime, TimeUnit unit); 73 | 74 | /** 75 | * 解锁 76 | * 77 | * @param name 锁名称 78 | */ 79 | void unlock(String name); 80 | 81 | /** 82 | * 插入任务流执行请求到队列中 83 | * 84 | * @param request 85 | * @return 成功返回true 86 | */ 87 | boolean execRequestOffer(TaskFlowExecRequest request); 88 | 89 | /** 90 | * 移除并返回任务流执行队列的首个元素 91 | * 92 | * @return 若队列为空则返回null 93 | */ 94 | TaskFlowExecRequest execRequestPoll(); 95 | 96 | /** 97 | * 新增任务流停止请求 98 | * 99 | * @param taskFlowLogId 100 | */ 101 | void stopRequestAdd(Long taskFlowLogId); 102 | 103 | /** 104 | * 查询是否包含任务流停止请求 105 | * 106 | * @param taskFlowLogId 107 | * @return 108 | */ 109 | Boolean stopRequestContains(Long taskFlowLogId); 110 | 111 | /** 112 | * 删除任务流停止请求 113 | * 114 | * @param taskFlowLogId 115 | */ 116 | void stopRequestRemove(Long taskFlowLogId); 117 | 118 | /** 119 | * 新增手工确认信息 120 | * 121 | * @param manualConfirmed 122 | */ 123 | void manualConfirmedAdd(ManualConfirmed manualConfirmed); 124 | 125 | /** 126 | * 获取手工确认信息 127 | * 128 | * @param taskLogId 129 | * @return 130 | */ 131 | ManualConfirmed manualConfirmedGet(Long taskLogId); 132 | 133 | /** 134 | * 删除手工确认信息 135 | * 136 | * @param taskLogId 137 | */ 138 | void manualConfirmedRemove(Long taskLogId); 139 | } 140 | -------------------------------------------------------------------------------- /wolf-flow-core/src/main/java/me/kpali/wolfflow/core/config/ClusterConfig.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.core.config; 2 | 3 | import org.springframework.stereotype.Component; 4 | 5 | /** 6 | * 集群控制器配置 7 | * 8 | * @author kpali 9 | */ 10 | @Component 11 | public class ClusterConfig { 12 | /** 13 | * 节点发送心跳间隔时间,单位秒 14 | */ 15 | private Integer nodeHeartbeatInterval; 16 | /** 17 | * 节点心跳有效期,单位秒 18 | */ 19 | private Integer nodeHeartbeatDuration; 20 | /** 21 | * 生成节点ID获取锁后自动解锁时间,单位秒 22 | */ 23 | private Integer generateNodeIdLockLeaseTime; 24 | /** 25 | * 任务流日志记录获取锁最大等待时间,单位秒 26 | */ 27 | private Integer taskFlowLogLockWaitTime; 28 | /** 29 | * 任务流日志记录获取锁后自动解锁时间,单位秒 30 | */ 31 | private Integer taskFlowLogLockLeaseTime; 32 | /** 33 | * 任务日志记录获取锁最大等待时间,单位秒 34 | */ 35 | private Integer taskLogLockWaitTime; 36 | /** 37 | * 任务日志记录获取锁后自动解锁时间,单位秒 38 | */ 39 | private Integer taskLogLockLeaseTime; 40 | 41 | public Integer getNodeHeartbeatInterval() { 42 | return nodeHeartbeatInterval; 43 | } 44 | 45 | public void setNodeHeartbeatInterval(Integer nodeHeartbeatInterval) { 46 | this.nodeHeartbeatInterval = nodeHeartbeatInterval; 47 | } 48 | 49 | public Integer getNodeHeartbeatDuration() { 50 | return nodeHeartbeatDuration; 51 | } 52 | 53 | public void setNodeHeartbeatDuration(Integer nodeHeartbeatDuration) { 54 | this.nodeHeartbeatDuration = nodeHeartbeatDuration; 55 | } 56 | 57 | public Integer getGenerateNodeIdLockLeaseTime() { 58 | return generateNodeIdLockLeaseTime; 59 | } 60 | 61 | public void setGenerateNodeIdLockLeaseTime(Integer generateNodeIdLockLeaseTime) { 62 | this.generateNodeIdLockLeaseTime = generateNodeIdLockLeaseTime; 63 | } 64 | 65 | public Integer getTaskFlowLogLockWaitTime() { 66 | return taskFlowLogLockWaitTime; 67 | } 68 | 69 | public void setTaskFlowLogLockWaitTime(Integer taskFlowLogLockWaitTime) { 70 | this.taskFlowLogLockWaitTime = taskFlowLogLockWaitTime; 71 | } 72 | 73 | public Integer getTaskFlowLogLockLeaseTime() { 74 | return taskFlowLogLockLeaseTime; 75 | } 76 | 77 | public void setTaskFlowLogLockLeaseTime(Integer taskFlowLogLockLeaseTime) { 78 | this.taskFlowLogLockLeaseTime = taskFlowLogLockLeaseTime; 79 | } 80 | 81 | public Integer getTaskLogLockWaitTime() { 82 | return taskLogLockWaitTime; 83 | } 84 | 85 | public void setTaskLogLockWaitTime(Integer taskLogLockWaitTime) { 86 | this.taskLogLockWaitTime = taskLogLockWaitTime; 87 | } 88 | 89 | public Integer getTaskLogLockLeaseTime() { 90 | return taskLogLockLeaseTime; 91 | } 92 | 93 | public void setTaskLogLockLeaseTime(Integer taskLogLockLeaseTime) { 94 | this.taskLogLockLeaseTime = taskLogLockLeaseTime; 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /wolf-flow-core/src/main/java/me/kpali/wolfflow/core/config/ExecutorConfig.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.core.config; 2 | 3 | import org.springframework.stereotype.Component; 4 | 5 | /** 6 | * 任务流执行器配置 7 | * 8 | * @author kpali 9 | */ 10 | @Component 11 | public class ExecutorConfig { 12 | /** 13 | * 任务流执行器线程池核心线程数 14 | */ 15 | private Integer corePoolSize; 16 | /** 17 | * 任务流执行器线程池最大线程数 18 | */ 19 | private Integer maximumPoolSize; 20 | 21 | public Integer getCorePoolSize() { 22 | return corePoolSize; 23 | } 24 | 25 | public void setCorePoolSize(Integer corePoolSize) { 26 | this.corePoolSize = corePoolSize; 27 | } 28 | 29 | public Integer getMaximumPoolSize() { 30 | return maximumPoolSize; 31 | } 32 | 33 | public void setMaximumPoolSize(Integer maximumPoolSize) { 34 | this.maximumPoolSize = maximumPoolSize; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /wolf-flow-core/src/main/java/me/kpali/wolfflow/core/config/SchedulerConfig.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.core.config; 2 | 3 | import org.springframework.stereotype.Component; 4 | 5 | /** 6 | * 任务流调度器配置 7 | * 8 | * @author kpali 9 | */ 10 | @Component 11 | public class SchedulerConfig { 12 | /** 13 | * 任务流执行请求扫描间隔,单位秒 14 | */ 15 | private Integer execRequestScanInterval; 16 | /** 17 | * 定时任务流扫描间隔,单位秒 18 | */ 19 | private Integer cronScanInterval; 20 | /** 21 | * 定时任务流扫描尝试获取锁最大等待时间,单位秒 22 | */ 23 | private Integer cronScanLockWaitTime; 24 | /** 25 | * 定时任务流扫描获取锁后自动解锁时间,单位秒 26 | */ 27 | private Integer cronScanLockLeaseTime; 28 | /** 29 | * 任务流调度器线程池核心线程数 30 | */ 31 | private Integer corePoolSize; 32 | /** 33 | * 任务流调度器线程池最大线程数 34 | */ 35 | private Integer maximumPoolSize; 36 | 37 | public Integer getExecRequestScanInterval() { 38 | return execRequestScanInterval; 39 | } 40 | 41 | public void setExecRequestScanInterval(Integer execRequestScanInterval) { 42 | this.execRequestScanInterval = execRequestScanInterval; 43 | } 44 | 45 | public Integer getCronScanInterval() { 46 | return cronScanInterval; 47 | } 48 | 49 | public void setCronScanInterval(Integer cronScanInterval) { 50 | this.cronScanInterval = cronScanInterval; 51 | } 52 | 53 | public Integer getCronScanLockWaitTime() { 54 | return cronScanLockWaitTime; 55 | } 56 | 57 | public void setCronScanLockWaitTime(Integer cronScanLockWaitTime) { 58 | this.cronScanLockWaitTime = cronScanLockWaitTime; 59 | } 60 | 61 | public Integer getCronScanLockLeaseTime() { 62 | return cronScanLockLeaseTime; 63 | } 64 | 65 | public void setCronScanLockLeaseTime(Integer cronScanLockLeaseTime) { 66 | this.cronScanLockLeaseTime = cronScanLockLeaseTime; 67 | } 68 | 69 | public Integer getCorePoolSize() { 70 | return corePoolSize; 71 | } 72 | 73 | public void setCorePoolSize(Integer corePoolSize) { 74 | this.corePoolSize = corePoolSize; 75 | } 76 | 77 | public Integer getMaximumPoolSize() { 78 | return maximumPoolSize; 79 | } 80 | 81 | public void setMaximumPoolSize(Integer maximumPoolSize) { 82 | this.maximumPoolSize = maximumPoolSize; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /wolf-flow-core/src/main/java/me/kpali/wolfflow/core/enums/TaskFlowScheduleStatusEnum.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.core.enums; 2 | 3 | /** 4 | * 任务流调度状态枚举 5 | * 6 | * @author kpali 7 | */ 8 | public enum TaskFlowScheduleStatusEnum { 9 | 10 | JOIN("JOIN", "Join"), 11 | UPDATE("UPDATE", "Update"), 12 | FAIL("FAIL", "Fail"); 13 | 14 | private String code; 15 | private String name; 16 | 17 | TaskFlowScheduleStatusEnum(String code, String name) { 18 | this.code = code; 19 | this.name = name; 20 | } 21 | 22 | public String getCode() { 23 | return code; 24 | } 25 | 26 | public String getName() { 27 | return name; 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /wolf-flow-core/src/main/java/me/kpali/wolfflow/core/enums/TaskFlowStatusEnum.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.core.enums; 2 | 3 | /** 4 | * 任务流状态枚举 5 | * 6 | * @author kpali 7 | */ 8 | public enum TaskFlowStatusEnum { 9 | 10 | WAIT_FOR_EXECUTE("WAIT_FOR_EXECUTE", "Wait for execute"), 11 | EXECUTING("EXECUTING", "Executing"), 12 | EXECUTE_SUCCESS("EXECUTE_SUCCESS", "Execute success"), 13 | EXECUTE_FAILURE("EXECUTE_FAILURE", "Execute failure"), 14 | 15 | STOPPING("STOPPING", "Stopping"), 16 | 17 | WAIT_FOR_ROLLBACK("WAIT_FOR_ROLLBACK", "Wait for rollback"), 18 | ROLLING_BACK("ROLLING_BACK", "Rolling back"), 19 | ROLLBACK_SUCCESS("ROLLBACK_SUCCESS", "Rollback success"), 20 | ROLLBACK_FAILURE("ROLLBACK_FAILURE", "Rollback failure"); 21 | 22 | private String code; 23 | private String name; 24 | 25 | TaskFlowStatusEnum(String code, String name) { 26 | this.code = code; 27 | this.name = name; 28 | } 29 | 30 | public String getCode() { 31 | return code; 32 | } 33 | 34 | public String getName() { 35 | return name; 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /wolf-flow-core/src/main/java/me/kpali/wolfflow/core/enums/TaskStatusEnum.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.core.enums; 2 | 3 | /** 4 | * 任务状态枚举 5 | * 6 | * @author kpali 7 | */ 8 | public enum TaskStatusEnum { 9 | 10 | WAIT_FOR_EXECUTE("WAIT_FOR_EXECUTE", "Wait for execute"), 11 | EXECUTING("EXECUTING", "Executing"), 12 | EXECUTE_SUCCESS("EXECUTE_SUCCESS", "Execute success"), 13 | EXECUTE_FAILURE("EXECUTE_FAILURE", "Execute failure"), 14 | 15 | STOPPING("STOPPING", "Stopping"), 16 | SKIPPED("SKIPPED", "Skipped"), 17 | MANUAL_CONFIRM("MANUAL_CONFIRM", "Manual Confirm"), 18 | 19 | WAIT_FOR_ROLLBACK("WAIT_FOR_ROLLBACK", "Wait for rollback"), 20 | ROLLING_BACK("ROLLING_BACK", "Rolling back"), 21 | ROLLBACK_SUCCESS("ROLLBACK_SUCCESS", "Rollback success"), 22 | ROLLBACK_FAILURE("ROLLBACK_FAILURE", "Rollback failure"); 23 | 24 | private String code; 25 | private String name; 26 | 27 | TaskStatusEnum(String code, String name) { 28 | this.code = code; 29 | this.name = name; 30 | } 31 | 32 | public String getCode() { 33 | return code; 34 | } 35 | 36 | public String getName() { 37 | return name; 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /wolf-flow-core/src/main/java/me/kpali/wolfflow/core/event/ScheduleStatusChangeEvent.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.core.event; 2 | 3 | import org.springframework.context.ApplicationEvent; 4 | 5 | /** 6 | * 任务流调度状态变更事件 7 | * 8 | * @author kpali 9 | */ 10 | public class ScheduleStatusChangeEvent extends ApplicationEvent { 11 | public ScheduleStatusChangeEvent(Object source, Long taskFlowId, String cronExpression, String scheduleStatus) { 12 | super(source); 13 | this.taskFlowId = taskFlowId; 14 | this.cronExpression = cronExpression; 15 | this.scheduleStatus = scheduleStatus; 16 | } 17 | 18 | private Long taskFlowId; 19 | private String cronExpression; 20 | private String scheduleStatus; 21 | 22 | public Long getTaskFlowId() { 23 | return taskFlowId; 24 | } 25 | 26 | public String getCronExpression() { 27 | return cronExpression; 28 | } 29 | 30 | public String getScheduleStatus() { 31 | return scheduleStatus; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /wolf-flow-core/src/main/java/me/kpali/wolfflow/core/event/ScheduleStatusEventPublisher.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.core.event; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.context.ApplicationEventPublisher; 5 | import org.springframework.stereotype.Component; 6 | 7 | /** 8 | * 任务流调度状态事件发布器 9 | * 10 | * @author kpali 11 | */ 12 | @Component 13 | public class ScheduleStatusEventPublisher { 14 | @Autowired 15 | private ApplicationEventPublisher eventPublisher; 16 | 17 | public void publishEvent(ScheduleStatusChangeEvent scheduleStatusChangeEvent) { 18 | this.eventPublisher.publishEvent(scheduleStatusChangeEvent); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /wolf-flow-core/src/main/java/me/kpali/wolfflow/core/event/TaskFlowStatusChangeEvent.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.core.event; 2 | 3 | import me.kpali.wolfflow.core.model.TaskFlowStatus; 4 | import org.springframework.context.ApplicationEvent; 5 | 6 | /** 7 | * 任务流状态变更事件 8 | * 9 | * @author kpali 10 | */ 11 | public class TaskFlowStatusChangeEvent extends ApplicationEvent { 12 | public TaskFlowStatusChangeEvent(Object source, TaskFlowStatus taskFlowStatus) { 13 | super(source); 14 | this.taskFlowStatus = taskFlowStatus; 15 | } 16 | 17 | private TaskFlowStatus taskFlowStatus; 18 | 19 | public TaskFlowStatus getTaskFlowStatus() { 20 | return taskFlowStatus; 21 | } 22 | 23 | public void setTaskFlowStatus(TaskFlowStatus taskFlowStatus) { 24 | this.taskFlowStatus = taskFlowStatus; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /wolf-flow-core/src/main/java/me/kpali/wolfflow/core/event/TaskFlowStatusEventPublisher.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.core.event; 2 | 3 | import me.kpali.wolfflow.core.cluster.IClusterController; 4 | import me.kpali.wolfflow.core.config.ClusterConfig; 5 | import me.kpali.wolfflow.core.exception.TaskFlowLogException; 6 | import me.kpali.wolfflow.core.exception.TryLockException; 7 | import me.kpali.wolfflow.core.logger.ITaskFlowLogger; 8 | import me.kpali.wolfflow.core.model.*; 9 | import me.kpali.wolfflow.core.util.context.TaskFlowContextWrapper; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.context.ApplicationEventPublisher; 12 | import org.springframework.stereotype.Component; 13 | 14 | import java.util.Date; 15 | import java.util.concurrent.ConcurrentHashMap; 16 | import java.util.concurrent.TimeUnit; 17 | 18 | /** 19 | * 任务流状态事件发布器 20 | * 21 | * @author kpali 22 | */ 23 | @Component 24 | public class TaskFlowStatusEventPublisher { 25 | @Autowired 26 | private ApplicationEventPublisher eventPublisher; 27 | 28 | @Autowired 29 | private ClusterConfig clusterConfig; 30 | 31 | @Autowired 32 | private IClusterController clusterController; 33 | 34 | @Autowired 35 | private ITaskFlowLogger taskFlowLogger; 36 | 37 | /** 38 | * 发布任务流状态变更事件 39 | * 40 | * @param taskFlow 41 | * @param context 42 | * @param status 43 | * @param message 44 | * @param record 45 | */ 46 | public void publishEvent(TaskFlow taskFlow, ConcurrentHashMap context, String status, String message, boolean record) throws TryLockException, TaskFlowLogException { 47 | TaskFlowContextWrapper taskFlowContextWrapper = new TaskFlowContextWrapper(context); 48 | 49 | TaskFlowStatus taskFlowStatus = new TaskFlowStatus(); 50 | taskFlowStatus.setTaskFlow(taskFlow); 51 | taskFlowStatus.setContext(taskFlowContextWrapper.getTaskFlowContext()); 52 | taskFlowStatus.setStatus(status); 53 | taskFlowStatus.setMessage(message); 54 | if (record) { 55 | String taskFlowLogLock = ClusterConstants.getTaskFlowLogLock(taskFlow.getId()); 56 | boolean locked = false; 57 | try { 58 | locked = this.clusterController.tryLock( 59 | taskFlowLogLock, 60 | clusterConfig.getTaskFlowLogLockWaitTime(), 61 | clusterConfig.getTaskFlowLogLockLeaseTime(), 62 | TimeUnit.SECONDS); 63 | if (!locked) { 64 | throw new TryLockException("Acquire the task flow log lock failed!"); 65 | } 66 | Long taskFlowLogId = taskFlowContextWrapper.getValue(TaskFlowContextKey.LOG_ID, Long.class); 67 | boolean isRollback = taskFlowContextWrapper.getValue(TaskFlowContextKey.IS_ROLLBACK, Boolean.class); 68 | TaskFlowLog taskFlowLog = this.taskFlowLogger.get(taskFlowLogId); 69 | boolean isNewLog = false; 70 | Date now = new Date(); 71 | if (taskFlowLog == null) { 72 | isNewLog = true; 73 | taskFlowLog = new TaskFlowLog(); 74 | taskFlowLog.setCreationTime(now); 75 | } 76 | taskFlowLog.setLogId(taskFlowLogId); 77 | taskFlowLog.setTaskFlowId(taskFlow.getId()); 78 | taskFlowLog.setTaskFlow(taskFlow); 79 | taskFlowLog.setContext(taskFlowContextWrapper.getTaskFlowContext()); 80 | taskFlowLog.setStatus(status); 81 | taskFlowLog.setMessage(message); 82 | taskFlowLog.setRollback(isRollback); 83 | taskFlowLog.setUpdateTime(now); 84 | if (isNewLog) { 85 | this.taskFlowLogger.add(taskFlowLog); 86 | } else { 87 | this.taskFlowLogger.update(taskFlowLog); 88 | } 89 | } finally { 90 | if (locked) { 91 | this.clusterController.unlock(taskFlowLogLock); 92 | } 93 | } 94 | } 95 | TaskFlowStatusChangeEvent taskFlowStatusChangeEvent = new TaskFlowStatusChangeEvent(this, taskFlowStatus); 96 | this.eventPublisher.publishEvent(taskFlowStatusChangeEvent); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /wolf-flow-core/src/main/java/me/kpali/wolfflow/core/event/TaskStatusChangeEvent.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.core.event; 2 | 3 | import me.kpali.wolfflow.core.model.TaskStatus; 4 | import org.springframework.context.ApplicationEvent; 5 | 6 | /** 7 | * 任务状态变更事件 8 | * 9 | * @author kpali 10 | */ 11 | public class TaskStatusChangeEvent extends ApplicationEvent { 12 | public TaskStatusChangeEvent(Object source, TaskStatus taskStatus) { 13 | super(source); 14 | this.taskStatus = taskStatus; 15 | } 16 | 17 | private TaskStatus taskStatus; 18 | 19 | public TaskStatus getTaskStatus() { 20 | return taskStatus; 21 | } 22 | 23 | public void setTaskStatus(TaskStatus taskStatus) { 24 | this.taskStatus = taskStatus; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /wolf-flow-core/src/main/java/me/kpali/wolfflow/core/event/TaskStatusEventPublisher.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.core.event; 2 | 3 | import me.kpali.wolfflow.core.cluster.IClusterController; 4 | import me.kpali.wolfflow.core.config.ClusterConfig; 5 | import me.kpali.wolfflow.core.exception.TaskLogException; 6 | import me.kpali.wolfflow.core.exception.TryLockException; 7 | import me.kpali.wolfflow.core.logger.ITaskLogger; 8 | import me.kpali.wolfflow.core.model.*; 9 | import me.kpali.wolfflow.core.util.context.TaskContextWrapper; 10 | import me.kpali.wolfflow.core.util.context.TaskFlowContextWrapper; 11 | import org.springframework.beans.factory.annotation.Autowired; 12 | import org.springframework.context.ApplicationEventPublisher; 13 | import org.springframework.stereotype.Component; 14 | 15 | import java.util.Date; 16 | import java.util.concurrent.ConcurrentHashMap; 17 | import java.util.concurrent.TimeUnit; 18 | 19 | /** 20 | * 任务状态事件发布器 21 | * 22 | * @author kpali 23 | */ 24 | @Component 25 | public class TaskStatusEventPublisher { 26 | @Autowired 27 | private ApplicationEventPublisher eventPublisher; 28 | 29 | @Autowired 30 | private ClusterConfig clusterConfig; 31 | 32 | @Autowired 33 | private IClusterController clusterController; 34 | 35 | @Autowired 36 | private ITaskLogger taskLogger; 37 | 38 | /** 39 | * 发布任务状态变更事件 40 | * 41 | * @param task 42 | * @param taskFlowId 43 | * @param context 44 | * @param status 45 | * @param message 46 | * @param record 47 | */ 48 | public void publishEvent(Task task, Long taskFlowId, ConcurrentHashMap context, String status, String message, boolean record) throws TryLockException, TaskLogException { 49 | TaskFlowContextWrapper taskFlowContextWrapper = new TaskFlowContextWrapper(context); 50 | TaskContextWrapper taskContextWrapper = taskFlowContextWrapper.getTaskContextWrapper(task.getId().toString()); 51 | 52 | TaskStatus taskStatus = new TaskStatus(); 53 | taskStatus.setTask(task); 54 | taskStatus.setTaskFlowId(taskFlowId); 55 | taskStatus.setContext(taskContextWrapper.getContext()); 56 | taskStatus.setStatus(status); 57 | taskStatus.setMessage(message); 58 | if (record) { 59 | String taskLogLock = ClusterConstants.getTaskLogLock(task.getId()); 60 | boolean locked = false; 61 | try { 62 | locked = this.clusterController.tryLock( 63 | taskLogLock, 64 | clusterConfig.getTaskLogLockWaitTime(), 65 | clusterConfig.getTaskLogLockLeaseTime(), 66 | TimeUnit.SECONDS); 67 | if (!locked) { 68 | throw new TryLockException("Acquire the task log lock failed!"); 69 | } 70 | Long taskFlowLogId = taskFlowContextWrapper.getValue(TaskFlowContextKey.LOG_ID, Long.class); 71 | boolean isRollback = taskFlowContextWrapper.getValue(TaskFlowContextKey.IS_ROLLBACK, Boolean.class); 72 | Long taskLogId = taskContextWrapper.getValue(TaskContextKey.TASK_LOG_ID, Long.class); 73 | String logFileId = taskContextWrapper.getValue(TaskContextKey.TASK_LOG_FILE_ID, String.class); 74 | TaskLog taskLog = this.taskLogger.get(taskFlowLogId, task.getId()); 75 | boolean isNewLog = false; 76 | Date now = new Date(); 77 | if (taskLog == null) { 78 | isNewLog = true; 79 | taskLog = new TaskLog(); 80 | taskLog.setCreationTime(now); 81 | } 82 | taskLog.setLogId(taskLogId); 83 | taskLog.setTaskId(task.getId()); 84 | taskLog.setTask(task); 85 | taskLog.setTaskFlowLogId(taskFlowLogId); 86 | taskLog.setTaskFlowId(taskFlowId); 87 | taskLog.setContext(taskContextWrapper.getContext()); 88 | taskLog.setStatus(status); 89 | taskLog.setMessage(message); 90 | taskLog.setLogFileId(logFileId); 91 | taskLog.setRollback(isRollback); 92 | taskLog.setUpdateTime(now); 93 | if (isNewLog) { 94 | this.taskLogger.add(taskLog); 95 | } else { 96 | this.taskLogger.update(taskLog); 97 | } 98 | } finally { 99 | if (locked) { 100 | this.clusterController.unlock(taskLogLock); 101 | } 102 | } 103 | } 104 | TaskStatusChangeEvent taskStatusChangeEvent = new TaskStatusChangeEvent(this, taskStatus); 105 | this.eventPublisher.publishEvent(taskStatusChangeEvent); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /wolf-flow-core/src/main/java/me/kpali/wolfflow/core/exception/GenerateNodeIdException.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.core.exception; 2 | 3 | /** 4 | * 生成节点ID异常 5 | * 6 | * @author kpali 7 | */ 8 | public class GenerateNodeIdException extends Exception { 9 | public GenerateNodeIdException() { 10 | super(); 11 | } 12 | 13 | public GenerateNodeIdException(String message) { 14 | super(message); 15 | } 16 | 17 | public GenerateNodeIdException(Throwable cause) { 18 | super(cause); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /wolf-flow-core/src/main/java/me/kpali/wolfflow/core/exception/InvalidCronExpressionException.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.core.exception; 2 | 3 | /** 4 | * 无效的cron表达式异常 5 | * 6 | * @author kpali 7 | */ 8 | public class InvalidCronExpressionException extends Exception { 9 | public InvalidCronExpressionException() { 10 | super(); 11 | } 12 | 13 | public InvalidCronExpressionException(String message) { 14 | super(message); 15 | } 16 | 17 | public InvalidCronExpressionException(Throwable cause) { 18 | super(cause); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /wolf-flow-core/src/main/java/me/kpali/wolfflow/core/exception/InvalidTaskFlowException.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.core.exception; 2 | 3 | /** 4 | * 无效的任务流异常 5 | * 6 | * @author kpali 7 | */ 8 | public class InvalidTaskFlowException extends Exception { 9 | public InvalidTaskFlowException() { 10 | super(); 11 | } 12 | 13 | public InvalidTaskFlowException(String message) { 14 | super(message); 15 | } 16 | 17 | public InvalidTaskFlowException(Throwable cause) { 18 | super(cause); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /wolf-flow-core/src/main/java/me/kpali/wolfflow/core/exception/TaskExecuteException.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.core.exception; 2 | 3 | /** 4 | * 任务执行异常 5 | * 6 | * @author kpali 7 | */ 8 | public class TaskExecuteException extends Exception { 9 | public TaskExecuteException() { 10 | super(); 11 | } 12 | 13 | public TaskExecuteException(String message) { 14 | super(message); 15 | } 16 | 17 | public TaskExecuteException(Throwable cause) { 18 | super(cause); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /wolf-flow-core/src/main/java/me/kpali/wolfflow/core/exception/TaskFlowExecuteException.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.core.exception; 2 | 3 | /** 4 | * 任务流执行异常 5 | * 6 | * @author kpali 7 | */ 8 | public class TaskFlowExecuteException extends Exception { 9 | public TaskFlowExecuteException() { 10 | super(); 11 | } 12 | 13 | public TaskFlowExecuteException(String message) { 14 | super(message); 15 | } 16 | 17 | public TaskFlowExecuteException(Throwable cause) { 18 | super(cause); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /wolf-flow-core/src/main/java/me/kpali/wolfflow/core/exception/TaskFlowInterruptedException.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.core.exception; 2 | 3 | /** 4 | * 任务流中断异常 5 | * 6 | * @author kpali 7 | */ 8 | public class TaskFlowInterruptedException extends Exception { 9 | public TaskFlowInterruptedException() { 10 | super(); 11 | } 12 | 13 | public TaskFlowInterruptedException(String message) { 14 | super(message); 15 | } 16 | 17 | public TaskFlowInterruptedException(Throwable cause) { 18 | super(cause); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /wolf-flow-core/src/main/java/me/kpali/wolfflow/core/exception/TaskFlowLogException.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.core.exception; 2 | 3 | /** 4 | * 任务流日志异常 5 | * 6 | * @author kpali 7 | */ 8 | public class TaskFlowLogException extends Exception { 9 | public TaskFlowLogException() { 10 | super(); 11 | } 12 | 13 | public TaskFlowLogException(String message) { 14 | super(message); 15 | } 16 | 17 | public TaskFlowLogException(Throwable cause) { 18 | super(cause); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /wolf-flow-core/src/main/java/me/kpali/wolfflow/core/exception/TaskFlowQueryException.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.core.exception; 2 | 3 | /** 4 | * 任务流查询异常 5 | * 6 | * @author kpali 7 | */ 8 | public class TaskFlowQueryException extends Exception { 9 | public TaskFlowQueryException() { 10 | super(); 11 | } 12 | 13 | public TaskFlowQueryException(String message) { 14 | super(message); 15 | } 16 | 17 | public TaskFlowQueryException(Throwable cause) { 18 | super(cause); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /wolf-flow-core/src/main/java/me/kpali/wolfflow/core/exception/TaskFlowRollbackException.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.core.exception; 2 | 3 | /** 4 | * 任务流回滚异常 5 | * 6 | * @author kpali 7 | */ 8 | public class TaskFlowRollbackException extends Exception { 9 | public TaskFlowRollbackException() { 10 | super(); 11 | } 12 | 13 | public TaskFlowRollbackException(String message) { 14 | super(message); 15 | } 16 | 17 | public TaskFlowRollbackException(Throwable cause) { 18 | super(cause); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /wolf-flow-core/src/main/java/me/kpali/wolfflow/core/exception/TaskFlowStopException.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.core.exception; 2 | 3 | /** 4 | * 任务流终止异常 5 | * 6 | * @author kpali 7 | */ 8 | public class TaskFlowStopException extends Exception { 9 | public TaskFlowStopException() { 10 | super(); 11 | } 12 | 13 | public TaskFlowStopException(String message) { 14 | super(message); 15 | } 16 | 17 | public TaskFlowStopException(Throwable cause) { 18 | super(cause); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /wolf-flow-core/src/main/java/me/kpali/wolfflow/core/exception/TaskFlowTriggerException.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.core.exception; 2 | 3 | /** 4 | * 任务流触发异常 5 | * 6 | * @author kpali 7 | */ 8 | public class TaskFlowTriggerException extends Exception { 9 | public TaskFlowTriggerException() { 10 | super(); 11 | } 12 | 13 | public TaskFlowTriggerException(String message) { 14 | super(message); 15 | } 16 | 17 | public TaskFlowTriggerException(Throwable cause) { 18 | super(cause); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /wolf-flow-core/src/main/java/me/kpali/wolfflow/core/exception/TaskInterruptedException.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.core.exception; 2 | 3 | /** 4 | * 任务中断异常 5 | * 6 | * @author kpali 7 | */ 8 | public class TaskInterruptedException extends Exception { 9 | public TaskInterruptedException() { 10 | super(); 11 | } 12 | 13 | public TaskInterruptedException(String message) { 14 | super(message); 15 | } 16 | 17 | public TaskInterruptedException(Throwable cause) { 18 | super(cause); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /wolf-flow-core/src/main/java/me/kpali/wolfflow/core/exception/TaskLogException.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.core.exception; 2 | 3 | /** 4 | * 任务日志异常 5 | * 6 | * @author kpali 7 | */ 8 | public class TaskLogException extends Exception { 9 | public TaskLogException() { 10 | super(); 11 | } 12 | 13 | public TaskLogException(String message) { 14 | super(message); 15 | } 16 | 17 | public TaskLogException(Throwable cause) { 18 | super(cause); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /wolf-flow-core/src/main/java/me/kpali/wolfflow/core/exception/TaskRollbackException.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.core.exception; 2 | 3 | /** 4 | * 任务回滚异常 5 | * 6 | * @author kpali 7 | */ 8 | public class TaskRollbackException extends Exception { 9 | public TaskRollbackException() { 10 | super(); 11 | } 12 | 13 | public TaskRollbackException(String message) { 14 | super(message); 15 | } 16 | 17 | public TaskRollbackException(Throwable cause) { 18 | super(cause); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /wolf-flow-core/src/main/java/me/kpali/wolfflow/core/exception/TaskStopException.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.core.exception; 2 | 3 | /** 4 | * 任务终止异常 5 | * 6 | * @author kpali 7 | */ 8 | public class TaskStopException extends Exception { 9 | public TaskStopException() { 10 | super(); 11 | } 12 | 13 | public TaskStopException(String message) { 14 | super(message); 15 | } 16 | 17 | public TaskStopException(Throwable cause) { 18 | super(cause); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /wolf-flow-core/src/main/java/me/kpali/wolfflow/core/exception/TryLockException.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.core.exception; 2 | 3 | /** 4 | * 尝试加锁异常 5 | * 6 | * @author kpali 7 | */ 8 | public class TryLockException extends Exception { 9 | public TryLockException() { 10 | super(); 11 | } 12 | 13 | public TryLockException(String message) { 14 | super(message); 15 | } 16 | 17 | public TryLockException(Throwable cause) { 18 | super(cause); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /wolf-flow-core/src/main/java/me/kpali/wolfflow/core/executor/ITaskFlowExecutor.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.core.executor; 2 | 3 | import me.kpali.wolfflow.core.exception.TaskFlowExecuteException; 4 | import me.kpali.wolfflow.core.exception.TaskFlowInterruptedException; 5 | import me.kpali.wolfflow.core.exception.TaskFlowRollbackException; 6 | import me.kpali.wolfflow.core.model.TaskFlow; 7 | 8 | import java.util.concurrent.ConcurrentHashMap; 9 | 10 | /** 11 | * 任务流执行器接口 12 | * 13 | * @author kpali 14 | */ 15 | public interface ITaskFlowExecutor { 16 | /** 17 | * 任务流执行前置处理 18 | * 19 | * @param taskFlow 20 | * @param context 21 | * @throws TaskFlowExecuteException 22 | */ 23 | void beforeExecute(TaskFlow taskFlow, ConcurrentHashMap context) throws TaskFlowExecuteException; 24 | 25 | /** 26 | * 任务流执行 27 | * 28 | * @param taskFlow 29 | * @param context 30 | * @throws TaskFlowExecuteException 31 | * @throws TaskFlowInterruptedException 32 | */ 33 | void execute(TaskFlow taskFlow, ConcurrentHashMap context) throws TaskFlowExecuteException, TaskFlowInterruptedException; 34 | 35 | /** 36 | * 任务流执行后置处理 37 | * 38 | * @param taskFlow 39 | * @param context 40 | * @throws TaskFlowExecuteException 41 | */ 42 | void afterExecute(TaskFlow taskFlow, ConcurrentHashMap context) throws TaskFlowExecuteException; 43 | 44 | /** 45 | * 任务流回滚前置处理 46 | * 47 | * @param taskFlow 48 | * @param context 49 | * @throws TaskFlowRollbackException 50 | */ 51 | void beforeRollback(TaskFlow taskFlow, ConcurrentHashMap context) throws TaskFlowRollbackException; 52 | 53 | /** 54 | * 任务流回滚 55 | * 56 | * @param taskFlow 57 | * @param context 58 | * @throws TaskFlowRollbackException 59 | * @throws TaskFlowInterruptedException 60 | */ 61 | void rollback(TaskFlow taskFlow, ConcurrentHashMap context) throws TaskFlowRollbackException, TaskFlowInterruptedException; 62 | 63 | /** 64 | * 任务流执行后置处理 65 | * 66 | * @param taskFlow 67 | * @param context 68 | * @throws TaskFlowRollbackException 69 | */ 70 | void afterRollback(TaskFlow taskFlow, ConcurrentHashMap context) throws TaskFlowRollbackException; 71 | } 72 | -------------------------------------------------------------------------------- /wolf-flow-core/src/main/java/me/kpali/wolfflow/core/launcher/Launcher.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.core.launcher; 2 | 3 | import me.kpali.wolfflow.core.cluster.IClusterController; 4 | import me.kpali.wolfflow.core.monitor.IMonitor; 5 | import me.kpali.wolfflow.core.scheduler.ITaskFlowScheduler; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.stereotype.Component; 8 | 9 | /** 10 | * 启动器 11 | * 12 | * @author kpali 13 | */ 14 | @Component 15 | public class Launcher { 16 | @Autowired 17 | IMonitor monitor; 18 | @Autowired 19 | ITaskFlowScheduler taskFlowScheduler; 20 | @Autowired 21 | IClusterController clusterController; 22 | 23 | public void startup() { 24 | this.monitor.init(); 25 | this.taskFlowScheduler.startup(); 26 | this.clusterController.startup(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /wolf-flow-core/src/main/java/me/kpali/wolfflow/core/logger/ITaskFlowLogger.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.core.logger; 2 | 3 | import me.kpali.wolfflow.core.exception.TaskFlowLogException; 4 | import me.kpali.wolfflow.core.model.TaskFlowLog; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * 任务流日志器 10 | * 11 | * @author kpali 12 | */ 13 | public interface ITaskFlowLogger { 14 | /** 15 | * 根据任务流ID获取日志列表 16 | * 17 | * @param taskFlowId 18 | * @return 19 | * @throws TaskFlowLogException 20 | */ 21 | List list(Long taskFlowId) throws TaskFlowLogException; 22 | 23 | /** 24 | * 根据任务流ID获取最后的任务流日志 25 | * 26 | * @param taskFlowId 27 | * @return 28 | * @throws TaskFlowLogException 29 | */ 30 | TaskFlowLog last(Long taskFlowId) throws TaskFlowLogException; 31 | 32 | /** 33 | * 根据日志ID获取任务流日志 34 | * 35 | * @param taskFlowLogId 36 | * @return 37 | * @throws TaskFlowLogException 38 | */ 39 | TaskFlowLog get(Long taskFlowLogId) throws TaskFlowLogException; 40 | 41 | /** 42 | * 新增任务流日志 43 | * 44 | * @param taskFlowLog 45 | * @throws TaskFlowLogException 46 | */ 47 | void add(TaskFlowLog taskFlowLog) throws TaskFlowLogException; 48 | 49 | /** 50 | * 更新任务流日志 51 | * 52 | * @param taskFlowLog 53 | * @throws TaskFlowLogException 54 | */ 55 | void update(TaskFlowLog taskFlowLog) throws TaskFlowLogException; 56 | 57 | /** 58 | * 根据日志ID删除任务流日志 59 | * 60 | * @param taskFlowLogId 61 | * @throws TaskFlowLogException 62 | */ 63 | void delete(Long taskFlowLogId) throws TaskFlowLogException; 64 | 65 | /** 66 | * 根据任务流ID删除所有任务流日志 67 | * 68 | * @param taskFlowId 69 | * @throws TaskFlowLogException 70 | */ 71 | void deleteByTaskFlowId(Long taskFlowId) throws TaskFlowLogException; 72 | 73 | /** 74 | * 查询是否正在处理中 75 | * 76 | * @param taskFlowLogId 77 | * @return 78 | * @throws TaskFlowLogException 79 | */ 80 | boolean isInProgress(Long taskFlowLogId) throws TaskFlowLogException; 81 | 82 | /** 83 | * 查询是否正在处理中 84 | * 85 | * @param taskFlowLog 86 | * @return 87 | * @throws TaskFlowLogException 88 | */ 89 | boolean isInProgress(TaskFlowLog taskFlowLog) throws TaskFlowLogException; 90 | } 91 | -------------------------------------------------------------------------------- /wolf-flow-core/src/main/java/me/kpali/wolfflow/core/logger/ITaskLogger.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.core.logger; 2 | 3 | import me.kpali.wolfflow.core.exception.TaskLogException; 4 | import me.kpali.wolfflow.core.model.TaskLog; 5 | import me.kpali.wolfflow.core.model.TaskLogResult; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * 任务日志器 11 | * 12 | * @author kpali 13 | */ 14 | public interface ITaskLogger { 15 | /** 16 | * 根据任务流日志ID获取任务日志列表 17 | * 18 | * @param taskFlowLogId 19 | * @return 20 | * @throws TaskLogException 21 | */ 22 | List list(Long taskFlowLogId) throws TaskLogException; 23 | 24 | /** 25 | * 根据任务流日志ID和任务ID获取任务日志 26 | * 27 | * @param taskFlowLogId 28 | * @param taskId 29 | * @return 30 | * @throws TaskLogException 31 | */ 32 | TaskLog get(Long taskFlowLogId, Long taskId) throws TaskLogException; 33 | 34 | /** 35 | * 新增任务日志 36 | * 37 | * @param taskLog 38 | * @throws TaskLogException 39 | */ 40 | void add(TaskLog taskLog) throws TaskLogException; 41 | 42 | /** 43 | * 更新任务日志 44 | * 45 | * @param taskLog 46 | * @throws TaskLogException 47 | */ 48 | void update(TaskLog taskLog) throws TaskLogException; 49 | 50 | /** 51 | * 根据任务ID获取最后一次执行的日志 52 | * 53 | * @param taskId 54 | * @throws TaskLogException 55 | */ 56 | TaskLog getLastExecuteLog(Long taskId) throws TaskLogException; 57 | 58 | /** 59 | * 根据任务流日志ID删除任务日志 60 | * 61 | * @param taskFlowLogId 62 | * @throws TaskLogException 63 | */ 64 | void deleteByTaskFlowLogId(Long taskFlowLogId) throws TaskLogException; 65 | 66 | /** 67 | * 记录日志内容,如果日志内容不存在则新增,存在则追加 68 | * 69 | * @param logFileId 70 | * @param logContent 71 | * @param end 72 | * @return 当前日志总行数 73 | * @throws TaskLogException 74 | */ 75 | int log(String logFileId, String logContent, Boolean end) throws TaskLogException; 76 | 77 | /** 78 | * 查询日志内容 79 | * 80 | * @param logFileId 81 | * @param fromLineNum 82 | * @return 83 | * @throws TaskLogException 84 | */ 85 | TaskLogResult query(String logFileId, Integer fromLineNum) throws TaskLogException; 86 | 87 | /** 88 | * 新增或更新任务状态 89 | * 90 | * @param taskStatus 91 | * @throws TaskLogException 92 | */ 93 | void putTaskStatus(TaskLog taskStatus) throws TaskLogException; 94 | 95 | /** 96 | * 根据任务ID获取任务状态 97 | * 98 | * @param taskId 99 | * @throws TaskLogException 100 | */ 101 | TaskLog getTaskStatus(Long taskId) throws TaskLogException; 102 | 103 | /** 104 | * 根据任务流ID获取任务状态列表 105 | * 106 | * @param taskFlowId 107 | * @return 108 | * @throws TaskLogException 109 | */ 110 | List listTaskStatus(Long taskFlowId) throws TaskLogException; 111 | 112 | /** 113 | * 根据任务ID删除任务状态 114 | * 115 | * @param taskId 116 | * @throws TaskLogException 117 | */ 118 | void deleteTaskStatus(Long taskId) throws TaskLogException; 119 | 120 | /** 121 | * 查询是否正在处理中 122 | * 123 | * @param taskLog 124 | * @return 125 | * @throws TaskLogException 126 | */ 127 | boolean isInProgress(TaskLog taskLog) throws TaskLogException; 128 | 129 | /** 130 | * 是否可以回滚 131 | * 132 | * @param taskLog 133 | * @return 134 | * @throws TaskLogException 135 | */ 136 | boolean canRollback(TaskLog taskLog) throws TaskLogException; 137 | } 138 | -------------------------------------------------------------------------------- /wolf-flow-core/src/main/java/me/kpali/wolfflow/core/model/ClusterConstants.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.core.model; 2 | 3 | /** 4 | * 集群常量 5 | * 6 | * @author kpali 7 | */ 8 | public class ClusterConstants { 9 | public static final String GENERATE_NODE_ID_LOCK = "GenerateNodeIdLock"; 10 | public static final String TASK_FLOW_LOG_LOCK_PREFIX = "TaskFlowLogLock_"; 11 | public static final String TASK_LOG_LOCK_PREFIX = "TaskLogLock_"; 12 | public static final String CRON_TASK_FLOW_SCAN_LOCK = "CronTaskFlowScanLock"; 13 | 14 | public static String getTaskFlowLogLock(Long taskFlowId) { 15 | return TASK_FLOW_LOG_LOCK_PREFIX + String.valueOf(taskFlowId); 16 | } 17 | 18 | public static String getTaskLogLock(Long taskId) { 19 | return TASK_LOG_LOCK_PREFIX + String.valueOf(taskId); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /wolf-flow-core/src/main/java/me/kpali/wolfflow/core/model/DeliveryContextKey.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.core.model; 2 | 3 | /** 4 | * 投递上下文 5 | * 6 | * @author kpali 7 | */ 8 | public class DeliveryContextKey { 9 | } 10 | -------------------------------------------------------------------------------- /wolf-flow-core/src/main/java/me/kpali/wolfflow/core/model/Link.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.core.model; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * 任务之间的连接 7 | * 8 | * @author kpali 9 | */ 10 | public class Link implements Serializable { 11 | public Link() { 12 | } 13 | 14 | public Link(Long source, Long target) { 15 | this.source = source; 16 | this.target = target; 17 | } 18 | 19 | private static final long serialVersionUID = -4190148088560825591L; 20 | 21 | private Long source; 22 | private Long target; 23 | 24 | public Long getSource() { 25 | return source; 26 | } 27 | 28 | public void setSource(Long source) { 29 | this.source = source; 30 | } 31 | 32 | public Long getTarget() { 33 | return target; 34 | } 35 | 36 | public void setTarget(Long target) { 37 | this.target = target; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /wolf-flow-core/src/main/java/me/kpali/wolfflow/core/model/ManualConfirmed.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.core.model; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * 手工确认信息 7 | * 8 | * @author kpali 9 | */ 10 | public class ManualConfirmed implements Serializable { 11 | private static final long serialVersionUID = 1048105386454641255L; 12 | 13 | public ManualConfirmed() { 14 | } 15 | 16 | public ManualConfirmed(Long taskLogId, Boolean success, String message) { 17 | this.taskLogId = taskLogId; 18 | this.success = success; 19 | this.message = message; 20 | } 21 | 22 | private Long taskLogId; 23 | private Boolean success; 24 | private String message; 25 | 26 | public Long getTaskLogId() { 27 | return taskLogId; 28 | } 29 | 30 | public void setTaskLogId(Long taskLogId) { 31 | this.taskLogId = taskLogId; 32 | } 33 | 34 | public Boolean getSuccess() { 35 | return success; 36 | } 37 | 38 | public void setSuccess(Boolean success) { 39 | this.success = success; 40 | } 41 | 42 | public String getMessage() { 43 | return message; 44 | } 45 | 46 | public void setMessage(String message) { 47 | this.message = message; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /wolf-flow-core/src/main/java/me/kpali/wolfflow/core/model/Task.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.core.model; 2 | 3 | import me.kpali.wolfflow.core.exception.TaskExecuteException; 4 | import me.kpali.wolfflow.core.exception.TaskInterruptedException; 5 | import me.kpali.wolfflow.core.exception.TaskRollbackException; 6 | import me.kpali.wolfflow.core.exception.TaskStopException; 7 | 8 | import java.io.Serializable; 9 | import java.util.concurrent.ConcurrentHashMap; 10 | 11 | /** 12 | * 任务 13 | * 14 | * @author kpali 15 | */ 16 | public class Task implements Serializable { 17 | private static final long serialVersionUID = 1097164523753393528L; 18 | 19 | private Long id; 20 | /** 21 | * 是否手工任务 22 | */ 23 | private boolean manual = false; 24 | 25 | public Long getId() { 26 | return id; 27 | } 28 | 29 | public void setId(Long id) { 30 | this.id = id; 31 | } 32 | 33 | public boolean getManual() { 34 | return this.manual; 35 | } 36 | 37 | public void setManual(boolean manual) { 38 | this.manual = manual; 39 | } 40 | 41 | public void executePreCheck(ConcurrentHashMap context) throws TaskExecuteException { 42 | // 不作任何操作 43 | } 44 | 45 | public void beforeExecute(ConcurrentHashMap context) throws TaskExecuteException { 46 | // 不做任何操作 47 | } 48 | 49 | public void execute(ConcurrentHashMap context) throws TaskExecuteException, TaskInterruptedException { 50 | // 不做任何操作 51 | } 52 | 53 | public void afterExecute(ConcurrentHashMap context) throws TaskExecuteException { 54 | // 不做任何操作 55 | } 56 | 57 | public void beforeRollback(ConcurrentHashMap context) throws TaskRollbackException { 58 | // 不做任何操作 59 | } 60 | 61 | public void rollbackPreCheck(ConcurrentHashMap context) throws TaskRollbackException { 62 | // 不作任何操作 63 | } 64 | 65 | public void rollback(ConcurrentHashMap context) throws TaskRollbackException, TaskInterruptedException { 66 | // 不做任何操作 67 | } 68 | 69 | public void afterRollback(ConcurrentHashMap context) throws TaskRollbackException { 70 | // 不做任何操作 71 | } 72 | 73 | public void stop(ConcurrentHashMap context) throws TaskStopException { 74 | // 不做任何操作 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /wolf-flow-core/src/main/java/me/kpali/wolfflow/core/model/TaskContextKey.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.core.model; 2 | 3 | /** 4 | * 任务上下文 5 | * 6 | * @author kpali 7 | */ 8 | public class TaskContextKey { 9 | public static final String LOG_ID = "logId"; 10 | public static final String TASK_LOG_ID = "logId"; 11 | public static final String TASK_LOG_FILE_ID = "logFileId"; 12 | public static final String TASK_LAST_EXECUTE_LOG_ID = "lastExecuteLogId"; 13 | public static final String PARENT_TASK_ID_LIST = "parentTaskIdList"; 14 | } 15 | -------------------------------------------------------------------------------- /wolf-flow-core/src/main/java/me/kpali/wolfflow/core/model/TaskFlow.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.core.model; 2 | 3 | import java.io.Serializable; 4 | import java.util.List; 5 | 6 | /** 7 | * 任务流 8 | * 9 | * @author kpali 10 | */ 11 | public class TaskFlow implements Serializable { 12 | private static final long serialVersionUID = 5077291498959687573L; 13 | 14 | private Long id; 15 | private List taskList; 16 | private List linkList; 17 | private String cron; 18 | private Long fromTaskId; 19 | private Long toTaskId; 20 | 21 | public Long getId() { 22 | return id; 23 | } 24 | 25 | public void setId(Long id) { 26 | this.id = id; 27 | } 28 | 29 | public List getTaskList() { 30 | return taskList; 31 | } 32 | 33 | public void setTaskList(List taskList) { 34 | this.taskList = taskList; 35 | } 36 | 37 | public List getLinkList() { 38 | return linkList; 39 | } 40 | 41 | public void setLinkList(List linkList) { 42 | this.linkList = linkList; 43 | } 44 | 45 | public String getCron() { 46 | return cron; 47 | } 48 | 49 | public void setCron(String cron) { 50 | this.cron = cron; 51 | } 52 | 53 | public Long getFromTaskId() { 54 | return fromTaskId; 55 | } 56 | 57 | public void setFromTaskId(Long fromTaskId) { 58 | this.fromTaskId = fromTaskId; 59 | } 60 | 61 | public Long getToTaskId() { 62 | return toTaskId; 63 | } 64 | 65 | public void setToTaskId(Long toTaskId) { 66 | this.toTaskId = toTaskId; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /wolf-flow-core/src/main/java/me/kpali/wolfflow/core/model/TaskFlowContextKey.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.core.model; 2 | 3 | /** 4 | * 任务流上下文 5 | * 6 | * @author kpali 7 | */ 8 | public class TaskFlowContextKey { 9 | public static final String TASK_FLOW_ID = "taskFlowId"; 10 | public static final String IS_ROLLBACK = "isRollback"; 11 | public static final String FROM_TASK_ID = "fromTaskId"; 12 | public static final String TO_TASK_ID = "toTaskId"; 13 | public static final String LOG_ID = "logId"; 14 | public static final String PARAMS = "params"; 15 | public static final String DELIVERY_CONTEXT = "deliveryContext"; 16 | public static final String EXECUTE_TASK_FLOW = "executeTaskFlow"; 17 | public static final String ROLLBACK_TASK_FLOW = "rollbackTaskFlow"; 18 | public static final String TASK_CONTEXTS = "taskContexts"; 19 | public static final String EXECUTED_BY_NODE = "executedByNode"; 20 | } 21 | -------------------------------------------------------------------------------- /wolf-flow-core/src/main/java/me/kpali/wolfflow/core/model/TaskFlowExecRequest.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.core.model; 2 | 3 | import java.io.Serializable; 4 | import java.util.concurrent.ConcurrentHashMap; 5 | 6 | /** 7 | * 任务流执行请求 8 | * 9 | * @author kpali 10 | */ 11 | public class TaskFlowExecRequest implements Serializable { 12 | private static final long serialVersionUID = 8484106840325360892L; 13 | 14 | public TaskFlowExecRequest() { 15 | } 16 | 17 | public TaskFlowExecRequest(Long taskFlowId, ConcurrentHashMap context) { 18 | this.taskFlowId = taskFlowId; 19 | this.context = context; 20 | } 21 | 22 | private Long taskFlowId; 23 | private ConcurrentHashMap context; 24 | 25 | public Long getTaskFlowId() { 26 | return taskFlowId; 27 | } 28 | 29 | public void setTaskFlowId(Long taskFlowId) { 30 | this.taskFlowId = taskFlowId; 31 | } 32 | 33 | public ConcurrentHashMap getContext() { 34 | return context; 35 | } 36 | 37 | public void setContext(ConcurrentHashMap context) { 38 | this.context = context; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /wolf-flow-core/src/main/java/me/kpali/wolfflow/core/model/TaskFlowLog.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.core.model; 2 | 3 | import java.io.Serializable; 4 | import java.util.Date; 5 | import java.util.concurrent.ConcurrentHashMap; 6 | 7 | /** 8 | * 任务流日志 9 | * 10 | * @author kpali 11 | */ 12 | public class TaskFlowLog implements Serializable { 13 | private static final long serialVersionUID = -875754396936412243L; 14 | 15 | private Long logId; 16 | private Long taskFlowId; 17 | private TaskFlow taskFlow; 18 | private ConcurrentHashMap context; 19 | private String status; 20 | private String message; 21 | private boolean rollback; 22 | private Date creationTime; 23 | private Date updateTime; 24 | 25 | public Long getLogId() { 26 | return logId; 27 | } 28 | 29 | public void setLogId(Long logId) { 30 | this.logId = logId; 31 | } 32 | 33 | public Long getTaskFlowId() { 34 | return taskFlowId; 35 | } 36 | 37 | public void setTaskFlowId(Long taskFlowId) { 38 | this.taskFlowId = taskFlowId; 39 | } 40 | 41 | public TaskFlow getTaskFlow() { 42 | return taskFlow; 43 | } 44 | 45 | public void setTaskFlow(TaskFlow taskFlow) { 46 | this.taskFlow = taskFlow; 47 | } 48 | 49 | public ConcurrentHashMap getContext() { 50 | return context; 51 | } 52 | 53 | public void setContext(ConcurrentHashMap context) { 54 | this.context = context; 55 | } 56 | 57 | public String getStatus() { 58 | return status; 59 | } 60 | 61 | public void setStatus(String status) { 62 | this.status = status; 63 | } 64 | 65 | public String getMessage() { 66 | return message; 67 | } 68 | 69 | public void setMessage(String message) { 70 | this.message = message; 71 | } 72 | 73 | public boolean getRollback() { 74 | return rollback; 75 | } 76 | 77 | public void setRollback(boolean rollback) { 78 | this.rollback = rollback; 79 | } 80 | 81 | public Date getCreationTime() { 82 | return creationTime; 83 | } 84 | 85 | public void setCreationTime(Date creationTime) { 86 | this.creationTime = creationTime; 87 | } 88 | 89 | public Date getUpdateTime() { 90 | return updateTime; 91 | } 92 | 93 | public void setUpdateTime(Date updateTime) { 94 | this.updateTime = updateTime; 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /wolf-flow-core/src/main/java/me/kpali/wolfflow/core/model/TaskFlowStatus.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.core.model; 2 | 3 | import java.io.Serializable; 4 | import java.util.concurrent.ConcurrentHashMap; 5 | 6 | /** 7 | * 任务流状态 8 | * 9 | * @author kpali 10 | */ 11 | public class TaskFlowStatus implements Serializable { 12 | private static final long serialVersionUID = 4373673109768512258L; 13 | 14 | private TaskFlow taskFlow; 15 | private ConcurrentHashMap context; 16 | private String status; 17 | private String message; 18 | 19 | public TaskFlow getTaskFlow() { 20 | return taskFlow; 21 | } 22 | 23 | public void setTaskFlow(TaskFlow taskFlow) { 24 | this.taskFlow = taskFlow; 25 | } 26 | 27 | public ConcurrentHashMap getContext() { 28 | return context; 29 | } 30 | 31 | public void setContext(ConcurrentHashMap context) { 32 | this.context = context; 33 | } 34 | 35 | public String getStatus() { 36 | return status; 37 | } 38 | 39 | public void setStatus(String status) { 40 | this.status = status; 41 | } 42 | 43 | public String getMessage() { 44 | return message; 45 | } 46 | 47 | public void setMessage(String message) { 48 | this.message = message; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /wolf-flow-core/src/main/java/me/kpali/wolfflow/core/model/TaskLog.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.core.model; 2 | 3 | import java.io.Serializable; 4 | import java.util.Date; 5 | import java.util.concurrent.ConcurrentHashMap; 6 | 7 | /** 8 | * 任务日志 9 | * 10 | * @author kpali 11 | */ 12 | public class TaskLog implements Serializable { 13 | private static final long serialVersionUID = 14370329912885306L; 14 | 15 | private Long logId; 16 | private Long taskFlowLogId; 17 | private Long taskId; 18 | private Task task; 19 | private Long taskFlowId; 20 | private ConcurrentHashMap context; 21 | private String status; 22 | private String message; 23 | private String logFileId; 24 | private boolean rollback; 25 | private Date creationTime; 26 | private Date updateTime; 27 | 28 | public Long getLogId() { 29 | return logId; 30 | } 31 | 32 | public void setLogId(Long logId) { 33 | this.logId = logId; 34 | } 35 | 36 | public Long getTaskFlowLogId() { 37 | return taskFlowLogId; 38 | } 39 | 40 | public void setTaskFlowLogId(Long taskFlowLogId) { 41 | this.taskFlowLogId = taskFlowLogId; 42 | } 43 | 44 | public Long getTaskId() { 45 | return taskId; 46 | } 47 | 48 | public void setTaskId(Long taskId) { 49 | this.taskId = taskId; 50 | } 51 | 52 | public Task getTask() { 53 | return task; 54 | } 55 | 56 | public void setTask(Task task) { 57 | this.task = task; 58 | } 59 | 60 | public Long getTaskFlowId() { 61 | return taskFlowId; 62 | } 63 | 64 | public void setTaskFlowId(Long taskFlowId) { 65 | this.taskFlowId = taskFlowId; 66 | } 67 | 68 | public ConcurrentHashMap getContext() { 69 | return context; 70 | } 71 | 72 | public void setContext(ConcurrentHashMap context) { 73 | this.context = context; 74 | } 75 | 76 | public String getStatus() { 77 | return status; 78 | } 79 | 80 | public void setStatus(String status) { 81 | this.status = status; 82 | } 83 | 84 | public String getMessage() { 85 | return message; 86 | } 87 | 88 | public void setMessage(String message) { 89 | this.message = message; 90 | } 91 | 92 | public String getLogFileId() { 93 | return logFileId; 94 | } 95 | 96 | public void setLogFileId(String logFileId) { 97 | this.logFileId = logFileId; 98 | } 99 | 100 | public boolean getRollback() { 101 | return rollback; 102 | } 103 | 104 | public void setRollback(boolean rollback) { 105 | this.rollback = rollback; 106 | } 107 | 108 | public Date getCreationTime() { 109 | return creationTime; 110 | } 111 | 112 | public void setCreationTime(Date creationTime) { 113 | this.creationTime = creationTime; 114 | } 115 | 116 | public Date getUpdateTime() { 117 | return updateTime; 118 | } 119 | 120 | public void setUpdateTime(Date updateTime) { 121 | this.updateTime = updateTime; 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /wolf-flow-core/src/main/java/me/kpali/wolfflow/core/model/TaskLogLine.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.core.model; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * 任务日志行 7 | * 8 | * @author kpali 9 | */ 10 | public class TaskLogLine implements Serializable { 11 | private static final long serialVersionUID = -5469833248990193615L; 12 | 13 | private Integer lineNum; 14 | private String line; 15 | private Boolean end; 16 | 17 | public Integer getLineNum() { 18 | return lineNum; 19 | } 20 | 21 | public void setLineNum(Integer lineNum) { 22 | this.lineNum = lineNum; 23 | } 24 | 25 | public String getLine() { 26 | return line; 27 | } 28 | 29 | public void setLine(String line) { 30 | this.line = line; 31 | } 32 | 33 | public Boolean getEnd() { 34 | return end; 35 | } 36 | 37 | public void setEnd(Boolean end) { 38 | this.end = end; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /wolf-flow-core/src/main/java/me/kpali/wolfflow/core/model/TaskLogResult.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.core.model; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * 任务日志结果 7 | * 8 | * @author kpali 9 | */ 10 | public class TaskLogResult implements Serializable { 11 | private static final long serialVersionUID = -237328970035347440L; 12 | 13 | private Integer fromLineNum; 14 | private Integer toLineNum; 15 | private String logContent; 16 | private Boolean end; 17 | 18 | public Integer getFromLineNum() { 19 | return fromLineNum; 20 | } 21 | 22 | public void setFromLineNum(Integer fromLineNum) { 23 | this.fromLineNum = fromLineNum; 24 | } 25 | 26 | public Integer getToLineNum() { 27 | return toLineNum; 28 | } 29 | 30 | public void setToLineNum(Integer toLineNum) { 31 | this.toLineNum = toLineNum; 32 | } 33 | 34 | public String getLogContent() { 35 | return logContent; 36 | } 37 | 38 | public void setLogContent(String logContent) { 39 | this.logContent = logContent; 40 | } 41 | 42 | public Boolean getEnd() { 43 | return end; 44 | } 45 | 46 | public void setEnd(Boolean end) { 47 | this.end = end; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /wolf-flow-core/src/main/java/me/kpali/wolfflow/core/model/TaskStatus.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.core.model; 2 | 3 | import java.io.Serializable; 4 | import java.util.concurrent.ConcurrentHashMap; 5 | 6 | /** 7 | * 任务状态 8 | * 9 | * @author kpali 10 | */ 11 | public class TaskStatus implements Serializable { 12 | private static final long serialVersionUID = -933124446911559604L; 13 | 14 | private Task task; 15 | private Long taskFlowId; 16 | private ConcurrentHashMap context; 17 | private String status; 18 | private String message; 19 | 20 | public Task getTask() { 21 | return task; 22 | } 23 | 24 | public void setTask(Task task) { 25 | this.task = task; 26 | } 27 | 28 | public Long getTaskFlowId() { 29 | return taskFlowId; 30 | } 31 | 32 | public void setTaskFlowId(Long taskFlowId) { 33 | this.taskFlowId = taskFlowId; 34 | } 35 | 36 | public ConcurrentHashMap getContext() { 37 | return context; 38 | } 39 | 40 | public void setContext(ConcurrentHashMap context) { 41 | this.context = context; 42 | } 43 | 44 | public String getStatus() { 45 | return status; 46 | } 47 | 48 | public void setStatus(String status) { 49 | this.status = status; 50 | } 51 | 52 | public String getMessage() { 53 | return message; 54 | } 55 | 56 | public void setMessage(String message) { 57 | this.message = message; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /wolf-flow-core/src/main/java/me/kpali/wolfflow/core/monitor/IMonitor.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.core.monitor; 2 | 3 | import java.util.concurrent.ExecutorService; 4 | 5 | /** 6 | * 监控器 7 | * 8 | * @author kpali 9 | */ 10 | public interface IMonitor { 11 | /** 12 | * 监控器初始化 13 | */ 14 | void init(); 15 | 16 | /** 17 | * 监控线程池 18 | * 19 | * @param executor 20 | * @param executorName 21 | */ 22 | void monitor(ExecutorService executor, String executorName); 23 | 24 | /** 25 | * 获取监控指标 26 | * 27 | * @return 28 | */ 29 | String scrape(); 30 | } 31 | -------------------------------------------------------------------------------- /wolf-flow-core/src/main/java/me/kpali/wolfflow/core/monitor/impl/DefaultMonitor.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.core.monitor.impl; 2 | 3 | import io.micrometer.core.instrument.MeterRegistry; 4 | import io.micrometer.core.instrument.Metrics; 5 | import io.micrometer.core.instrument.binder.jvm.*; 6 | import io.micrometer.core.instrument.binder.system.ProcessorMetrics; 7 | import io.micrometer.prometheus.PrometheusConfig; 8 | import io.micrometer.prometheus.PrometheusMeterRegistry; 9 | import me.kpali.wolfflow.core.monitor.IMonitor; 10 | import org.springframework.beans.factory.annotation.Value; 11 | import org.springframework.stereotype.Component; 12 | 13 | import java.util.Collections; 14 | import java.util.List; 15 | import java.util.concurrent.ExecutorService; 16 | import java.util.stream.Collectors; 17 | 18 | /** 19 | * 监控器的默认实现 20 | * 21 | * @author kpali 22 | */ 23 | @Component 24 | public class DefaultMonitor implements IMonitor { 25 | @Value("${spring.application.name:wolf-flow}") 26 | String applicationName; 27 | 28 | @Override 29 | public void init() { 30 | PrometheusMeterRegistry prometheusRegistry = new PrometheusMeterRegistry(PrometheusConfig.DEFAULT); 31 | prometheusRegistry.config().commonTags("application", applicationName); 32 | Metrics.addRegistry(prometheusRegistry); 33 | 34 | new ClassLoaderMetrics().bindTo(Metrics.globalRegistry); 35 | new JvmMemoryMetrics().bindTo(Metrics.globalRegistry); 36 | new JvmGcMetrics().bindTo(Metrics.globalRegistry); 37 | new ProcessorMetrics().bindTo(Metrics.globalRegistry); 38 | new JvmThreadMetrics().bindTo(Metrics.globalRegistry); 39 | } 40 | 41 | @Override 42 | public void monitor(ExecutorService executor, String executorName) { 43 | new ExecutorServiceMetrics(executor, executorName, Collections.emptyList()).bindTo(Metrics.globalRegistry); 44 | } 45 | 46 | @Override 47 | public String scrape() { 48 | List meterRegistries = Metrics.globalRegistry.getRegistries() 49 | .stream() 50 | .filter(meterRegistry -> meterRegistry instanceof PrometheusMeterRegistry) 51 | .collect(Collectors.toList()); 52 | for (MeterRegistry registry : meterRegistries) { 53 | if (registry instanceof PrometheusMeterRegistry) { 54 | return ((PrometheusMeterRegistry) registry).scrape(); 55 | } 56 | } 57 | return ""; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /wolf-flow-core/src/main/java/me/kpali/wolfflow/core/querier/ITaskFlowQuerier.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.core.querier; 2 | 3 | import me.kpali.wolfflow.core.exception.TaskFlowQueryException; 4 | import me.kpali.wolfflow.core.model.TaskFlow; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * 任务流查询器 10 | * 11 | * @author kpali 12 | */ 13 | public interface ITaskFlowQuerier { 14 | 15 | /** 16 | * 根据任务流ID获取任务流 17 | * 18 | * @param taskFlowId 19 | * @return 20 | * @throws TaskFlowQueryException 21 | */ 22 | TaskFlow getTaskFlow(Long taskFlowId) throws TaskFlowQueryException; 23 | 24 | /** 25 | * 获取定时任务流列表 26 | * 27 | * @return 28 | * @throws TaskFlowQueryException 29 | */ 30 | List listCronTaskFlow() throws TaskFlowQueryException; 31 | 32 | } 33 | -------------------------------------------------------------------------------- /wolf-flow-core/src/main/java/me/kpali/wolfflow/core/querier/impl/DefaultTaskFlowQuerier.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.core.querier.impl; 2 | 3 | import me.kpali.wolfflow.core.querier.ITaskFlowQuerier; 4 | import me.kpali.wolfflow.core.exception.TaskFlowQueryException; 5 | import me.kpali.wolfflow.core.model.TaskFlow; 6 | import org.springframework.stereotype.Component; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * 任务流查询器的默认实现 12 | * 13 | * @author kpali 14 | */ 15 | @Component 16 | public class DefaultTaskFlowQuerier implements ITaskFlowQuerier { 17 | @Override 18 | public TaskFlow getTaskFlow(Long taskFlowId) throws TaskFlowQueryException { 19 | return null; 20 | } 21 | 22 | @Override 23 | public List listCronTaskFlow() throws TaskFlowQueryException { 24 | return null; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /wolf-flow-core/src/main/java/me/kpali/wolfflow/core/scheduler/ITaskFlowScheduler.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.core.scheduler; 2 | 3 | import me.kpali.wolfflow.core.exception.TaskFlowStopException; 4 | import me.kpali.wolfflow.core.exception.TaskFlowTriggerException; 5 | 6 | import java.util.concurrent.ConcurrentHashMap; 7 | 8 | /** 9 | * 任务流调度器 10 | * 11 | * @author kpali 12 | */ 13 | public interface ITaskFlowScheduler { 14 | /** 15 | * 启动任务流调度器 16 | */ 17 | void startup(); 18 | 19 | /** 20 | * 执行任务流 21 | * 22 | * @param taskFlowId 23 | * @param params 24 | * @return taskFlowLogId 25 | * @throws TaskFlowTriggerException 26 | */ 27 | long execute(Long taskFlowId, ConcurrentHashMap params) throws TaskFlowTriggerException; 28 | 29 | /** 30 | * 执行任务流,执行指定任务 31 | * 32 | * @param taskFlowId 33 | * @param taskId 34 | * @param params 35 | * @return taskFlowLogId 36 | * @throws TaskFlowTriggerException 37 | */ 38 | long execute(Long taskFlowId, Long taskId, ConcurrentHashMap params) throws TaskFlowTriggerException; 39 | 40 | /** 41 | * 执行任务流,从指定任务开始 42 | * 43 | * @param taskFlowId 44 | * @param fromTaskId 45 | * @param params 46 | * @return taskFlowLogId 47 | * @throws TaskFlowTriggerException 48 | */ 49 | long executeFrom(Long taskFlowId, Long fromTaskId, ConcurrentHashMap params) throws TaskFlowTriggerException; 50 | 51 | /** 52 | * 执行任务流,到指定任务结束 53 | * 54 | * @param taskFlowId 55 | * @param toTaskId 56 | * @param params 57 | * @return taskFlowLogId 58 | * @throws TaskFlowTriggerException 59 | */ 60 | long executeTo(Long taskFlowId, Long toTaskId, ConcurrentHashMap params) throws TaskFlowTriggerException; 61 | 62 | /** 63 | * 回滚任务流 64 | * 65 | * @param taskFlowId 66 | * @param params 67 | * @return taskFlowLogId 68 | * @throws TaskFlowTriggerException 69 | */ 70 | long rollback(Long taskFlowId, ConcurrentHashMap params) throws TaskFlowTriggerException; 71 | 72 | /** 73 | * 停止任务流 74 | * 75 | * @param taskFlowLogId 76 | * @throws TaskFlowStopException 77 | */ 78 | void stop(Long taskFlowLogId) throws TaskFlowStopException; 79 | } 80 | -------------------------------------------------------------------------------- /wolf-flow-core/src/main/java/me/kpali/wolfflow/core/scheduler/impl/quartz/MyDynamicScheduler.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.core.scheduler.impl.quartz; 2 | 3 | import org.quartz.*; 4 | import org.quartz.impl.matchers.GroupMatcher; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import org.springframework.util.Assert; 8 | 9 | import java.util.Date; 10 | import java.util.List; 11 | import java.util.Map; 12 | import java.util.Set; 13 | 14 | /** 15 | * 动态调度器 16 | * 17 | * @author kpali 18 | */ 19 | public class MyDynamicScheduler { 20 | 21 | public MyDynamicScheduler() { 22 | } 23 | 24 | private static final Logger logger = LoggerFactory.getLogger(MyDynamicScheduler.class); 25 | private static Scheduler scheduler; 26 | 27 | public void setScheduler(Scheduler scheduler) { 28 | MyDynamicScheduler.scheduler = scheduler; 29 | } 30 | 31 | public void start() throws Exception { 32 | Assert.notNull(scheduler, "quartz scheduler is null"); 33 | logger.info("Quartz scheduler initialization completed."); 34 | } 35 | 36 | public void destroy() throws Exception { 37 | } 38 | 39 | public static boolean checkExists(String jobName, String jobGroup) throws SchedulerException { 40 | TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroup); 41 | return scheduler.checkExists(triggerKey); 42 | } 43 | 44 | public static void addJob(String jobName, String jobGroup, String cronExpression, Map jobDataMap) throws SchedulerException { 45 | TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroup); 46 | JobKey jobKey = new JobKey(jobName, jobGroup); 47 | if (!scheduler.checkExists(triggerKey)) { 48 | CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression).withMisfireHandlingInstructionDoNothing(); 49 | CronTrigger cronTrigger = (CronTrigger)TriggerBuilder.newTrigger().withIdentity(triggerKey).withSchedule(cronScheduleBuilder).build(); 50 | Class jobClass_ = MyQuartzJobBean.class; 51 | JobDetail jobDetail = JobBuilder.newJob(jobClass_).withIdentity(jobKey).usingJobData(new JobDataMap(jobDataMap)).build(); 52 | Date date = scheduler.scheduleJob(jobDetail, cronTrigger); 53 | logger.info("Quartz schedule job success -> jobName:{}, jobGroup:{}, cronExpression:{}", jobName, jobGroup, cronExpression); 54 | } 55 | } 56 | 57 | public static void removeJob(String jobName, String jobGroup) throws SchedulerException { 58 | JobKey jobKey = new JobKey(jobName, jobGroup); 59 | scheduler.deleteJob(jobKey); 60 | logger.info("Quartz delete job success -> jobName:{}, jobGroup:{}", jobName, jobGroup); 61 | } 62 | 63 | public static void updateJobCron(String jobName, String jobGroup, String cronExpression, Map jobDataMap) throws SchedulerException { 64 | TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroup); 65 | if (scheduler.checkExists(triggerKey)) { 66 | CronTrigger oldTrigger = (CronTrigger) scheduler.getTrigger(triggerKey); 67 | String oldCron = oldTrigger.getCronExpression(); 68 | if (!oldCron.equals(cronExpression)) { 69 | CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression).withMisfireHandlingInstructionDoNothing(); 70 | oldTrigger = (CronTrigger) oldTrigger.getTriggerBuilder().withIdentity(triggerKey).usingJobData(new JobDataMap(jobDataMap)).withSchedule(cronScheduleBuilder).build(); 71 | scheduler.rescheduleJob(triggerKey, oldTrigger); 72 | logger.info("Quartz reschedule job success -> jobName:{}, jobGroup:{}, cronExpression:{}", jobName, jobGroup, cronExpression); 73 | } 74 | } 75 | } 76 | 77 | public static void clear() throws SchedulerException { 78 | scheduler.clear(); 79 | } 80 | 81 | public static Set getTriggerKeys() throws SchedulerException { 82 | return scheduler.getTriggerKeys(GroupMatcher.anyTriggerGroup()); 83 | } 84 | 85 | public static Set getTriggerKeysGroupEquals(String group) throws SchedulerException { 86 | return scheduler.getTriggerKeys(GroupMatcher.groupEquals(group)); 87 | } 88 | 89 | public static Set getJobKeys() throws SchedulerException { 90 | return scheduler.getJobKeys(GroupMatcher.anyJobGroup()); 91 | } 92 | 93 | public static Set getJobKeysGroupEquals(String group) throws SchedulerException { 94 | return scheduler.getJobKeys(GroupMatcher.groupEquals(group)); 95 | } 96 | 97 | public static List getJobGroupNames() throws SchedulerException { 98 | return scheduler.getJobGroupNames(); 99 | } 100 | 101 | public static List getTriggerGroupNames() throws SchedulerException { 102 | return scheduler.getTriggerGroupNames(); 103 | } 104 | 105 | public static List getCurrentlyExecutingJobs() throws SchedulerException { 106 | return scheduler.getCurrentlyExecutingJobs(); 107 | } 108 | 109 | } 110 | -------------------------------------------------------------------------------- /wolf-flow-core/src/main/java/me/kpali/wolfflow/core/scheduler/impl/quartz/MyDynamicSchedulerConfig.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.core.scheduler.impl.quartz; 2 | 3 | import org.quartz.Scheduler; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | import org.springframework.core.io.ClassPathResource; 8 | import org.springframework.scheduling.quartz.SchedulerFactoryBean; 9 | 10 | /** 11 | * 动态调度器配置 12 | * 13 | * @author kpali 14 | */ 15 | @Configuration 16 | public class MyDynamicSchedulerConfig { 17 | 18 | public MyDynamicSchedulerConfig() { 19 | } 20 | 21 | @Autowired 22 | MyJobFactory myJobFactory; 23 | 24 | @Bean 25 | public SchedulerFactoryBean getSchedulerFactoryBean() { 26 | SchedulerFactoryBean schedulerFactory = new SchedulerFactoryBean(); 27 | schedulerFactory.setAutoStartup(true); 28 | schedulerFactory.setOverwriteExistingJobs(true); 29 | schedulerFactory.setApplicationContextSchedulerContextKey("applicationContext"); 30 | schedulerFactory.setConfigLocation(new ClassPathResource("quartz.properties")); 31 | schedulerFactory.setJobFactory(myJobFactory); 32 | return schedulerFactory; 33 | } 34 | 35 | @Bean( 36 | initMethod = "start", 37 | destroyMethod = "destroy" 38 | ) 39 | public MyDynamicScheduler getMyDynamicScheduler(SchedulerFactoryBean schedulerFactory) { 40 | Scheduler scheduler = schedulerFactory.getScheduler(); 41 | MyDynamicScheduler myDynamicScheduler = new MyDynamicScheduler(); 42 | myDynamicScheduler.setScheduler(scheduler); 43 | return myDynamicScheduler; 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /wolf-flow-core/src/main/java/me/kpali/wolfflow/core/scheduler/impl/quartz/MyJobFactory.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.core.scheduler.impl.quartz; 2 | 3 | import org.quartz.spi.TriggerFiredBundle; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.beans.factory.config.AutowireCapableBeanFactory; 6 | import org.springframework.scheduling.quartz.AdaptableJobFactory; 7 | import org.springframework.stereotype.Component; 8 | 9 | /** 10 | * 作业工厂类,主要为了实现Spring Bean注入到Job 11 | * 12 | * @author kpali 13 | */ 14 | @Component 15 | public class MyJobFactory extends AdaptableJobFactory { 16 | 17 | @Autowired 18 | private AutowireCapableBeanFactory capableBeanFactory; 19 | 20 | @Override 21 | protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception { 22 | Object jobInstance = super.createJobInstance(bundle); 23 | capableBeanFactory.autowireBean(jobInstance); 24 | return jobInstance; 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /wolf-flow-core/src/main/java/me/kpali/wolfflow/core/scheduler/impl/quartz/MyQuartzJobBean.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.core.scheduler.impl.quartz; 2 | 3 | import me.kpali.wolfflow.core.exception.TaskFlowTriggerException; 4 | import me.kpali.wolfflow.core.model.TaskFlowContextKey; 5 | import me.kpali.wolfflow.core.scheduler.ITaskFlowScheduler; 6 | import org.quartz.JobDataMap; 7 | import org.quartz.JobExecutionContext; 8 | import org.quartz.JobExecutionException; 9 | import org.quartz.JobKey; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.scheduling.quartz.QuartzJobBean; 12 | 13 | /** 14 | * Quartz作业 15 | * 16 | * @author kpali 17 | */ 18 | public class MyQuartzJobBean extends QuartzJobBean { 19 | public MyQuartzJobBean() { 20 | } 21 | 22 | @Autowired 23 | ITaskFlowScheduler taskFlowScheduler; 24 | 25 | @Override 26 | protected void executeInternal(JobExecutionContext context) throws JobExecutionException { 27 | JobKey jobKey = context.getTrigger().getJobKey(); 28 | Long taskFlowId = Long.valueOf(jobKey.getName()); 29 | Long fromTaskId = null; 30 | Long toTaskId = null; 31 | JobDataMap jobDataMap = context.getMergedJobDataMap(); 32 | if (jobDataMap != null) { 33 | fromTaskId = (jobDataMap.get(TaskFlowContextKey.FROM_TASK_ID) == null ? null : jobDataMap.getLong(TaskFlowContextKey.FROM_TASK_ID)); 34 | toTaskId = (jobDataMap.get(TaskFlowContextKey.TO_TASK_ID) == null ? null : jobDataMap.getLong(TaskFlowContextKey.TO_TASK_ID)); 35 | } 36 | try { 37 | if (fromTaskId != null && fromTaskId.equals(toTaskId)) { 38 | taskFlowScheduler.execute(taskFlowId, fromTaskId, null); 39 | } else if (fromTaskId != null) { 40 | taskFlowScheduler.executeFrom(taskFlowId, fromTaskId, null); 41 | } else if (toTaskId != null) { 42 | taskFlowScheduler.executeTo(taskFlowId, toTaskId, null); 43 | } else { 44 | taskFlowScheduler.execute(taskFlowId, null); 45 | } 46 | } catch (TaskFlowTriggerException e) { 47 | throw new JobExecutionException(e); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /wolf-flow-core/src/main/java/me/kpali/wolfflow/core/util/IdGenerator.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.core.util; 2 | 3 | import me.kpali.wolfflow.core.cluster.IClusterController; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.stereotype.Component; 6 | 7 | /** 8 | * ID生成器 9 | * 10 | * @author kpali 11 | */ 12 | @Component 13 | public class IdGenerator { 14 | @Autowired 15 | private IClusterController clusterController; 16 | 17 | private SnowFlake snowFlake; 18 | 19 | public synchronized long nextId() { 20 | if (snowFlake == null) { 21 | snowFlake = new SnowFlake(this.clusterController.getNodeId()); 22 | } 23 | return snowFlake.nextId(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /wolf-flow-core/src/main/java/me/kpali/wolfflow/core/util/SnowFlake.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.core.util; 2 | 3 | /** 4 | * snowflake算法 5 | * 6 | * @author kpali 7 | */ 8 | public class SnowFlake { 9 | /** 10 | * 起始的时间戳 11 | */ 12 | private final static long START_STMP = 1480166465631L; 13 | 14 | /** 15 | * 每一部分占用的位数 16 | */ 17 | private final static long SEQUENCE_BIT = 8; //序列号占用的位数 18 | private final static long MACHINE_BIT = 5; //机器标识占用的位数 19 | 20 | /** 21 | * 每一部分的最大值 22 | */ 23 | private final static long MAX_MACHINE_NUM = -1L ^ (-1L << MACHINE_BIT); 24 | private final static long MAX_SEQUENCE = -1L ^ (-1L << SEQUENCE_BIT); 25 | 26 | /** 27 | * 每一部分向左的位移 28 | */ 29 | private final static long MACHINE_LEFT = SEQUENCE_BIT; 30 | private final static long TIMESTMP_LEFT = MACHINE_LEFT + MACHINE_BIT; 31 | 32 | private long machineId; //机器标识 33 | private long sequence = 0L; //序列号 34 | private long lastStmp = -1L;//上一次时间戳 35 | 36 | public SnowFlake(long machineId) { 37 | if (machineId > MAX_MACHINE_NUM || machineId < 0) { 38 | throw new IllegalArgumentException("machineId can't be greater than MAX_MACHINE_NUM or less than 0"); 39 | } 40 | this.machineId = machineId; 41 | } 42 | 43 | /** 44 | * 产生下一个ID 45 | * 46 | * @return 47 | */ 48 | public synchronized long nextId() { 49 | long currStmp = getNewstmp(); 50 | if (currStmp < lastStmp) { 51 | throw new RuntimeException("Clock moved backwards. Refusing to generate id"); 52 | } 53 | 54 | if (currStmp == lastStmp) { 55 | //相同毫秒内,序列号自增 56 | sequence = (sequence + 1) & MAX_SEQUENCE; 57 | //同一毫秒的序列数已经达到最大 58 | if (sequence == 0L) { 59 | currStmp = getNextMill(); 60 | } 61 | } else { 62 | //不同毫秒内,序列号置为0 63 | sequence = 0L; 64 | } 65 | 66 | lastStmp = currStmp; 67 | 68 | return (currStmp - START_STMP) << TIMESTMP_LEFT //时间戳部分 69 | | machineId << MACHINE_LEFT //机器标识部分 70 | | sequence; //序列号部分 71 | } 72 | 73 | private long getNextMill() { 74 | long mill = getNewstmp(); 75 | while (mill <= lastStmp) { 76 | mill = getNewstmp(); 77 | } 78 | return mill; 79 | } 80 | 81 | private long getNewstmp() { 82 | return System.currentTimeMillis(); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /wolf-flow-core/src/main/java/me/kpali/wolfflow/core/util/TaskFlowUtils.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.core.util; 2 | 3 | import me.kpali.wolfflow.core.model.Link; 4 | import me.kpali.wolfflow.core.model.Task; 5 | import me.kpali.wolfflow.core.model.TaskFlow; 6 | 7 | import java.util.*; 8 | 9 | /** 10 | * 任务流工具类 11 | * 12 | * @author kpali 13 | */ 14 | public class TaskFlowUtils { 15 | 16 | /** 17 | * 拓扑排序算法 18 | * 19 | * @param taskFlow 20 | * @return 21 | */ 22 | public static List topologicalSort(TaskFlow taskFlow) { 23 | List sortedTaskList = new ArrayList<>(); 24 | int taskCount = taskFlow.getTaskList().size(); 25 | 26 | Map id_mapto_task = new HashMap<>(); 27 | for (Task task : taskFlow.getTaskList()) { 28 | id_mapto_task.put(task.getId(), task); 29 | } 30 | 31 | // 计算节点入度 32 | Map taskId_mapto_inDegree = new HashMap<>(); 33 | for (Task task : taskFlow.getTaskList()) { 34 | int inDegree = 0; 35 | for (Link link : taskFlow.getLinkList()) { 36 | if (link.getTarget().equals(task.getId())) { 37 | inDegree++; 38 | } 39 | } 40 | taskId_mapto_inDegree.put(task.getId(), inDegree); 41 | } 42 | 43 | // 从入度为0的节点开始遍历,对遍历过的节点将其子节点的入度减1,重复此步骤,直到遍历完所有节点 44 | Deque deque = new ArrayDeque<>(); 45 | for (Long taskId : taskId_mapto_inDegree.keySet()) { 46 | int inDegree = taskId_mapto_inDegree.get(taskId); 47 | if (inDegree == 0) { 48 | deque.offer(id_mapto_task.get(taskId)); 49 | } 50 | } 51 | while (!deque.isEmpty()) { 52 | Task task = deque.poll(); 53 | sortedTaskList.add(task); 54 | 55 | for (Link link : taskFlow.getLinkList()) { 56 | if (link.getSource().equals(task.getId())) { 57 | Long childTaskId = link.getTarget(); 58 | int inDegree = taskId_mapto_inDegree.get(childTaskId); 59 | inDegree--; 60 | taskId_mapto_inDegree.put(childTaskId, inDegree); 61 | if (inDegree == 0) { 62 | deque.offer(id_mapto_task.get(childTaskId)); 63 | } 64 | } 65 | } 66 | } 67 | 68 | // 如果无法遍历完所有节点,则说明当前拓扑不是有向无环图,不存在拓扑排序。 69 | return sortedTaskList.size() == taskCount ? sortedTaskList : null; 70 | } 71 | 72 | /** 73 | * 根据从指定任务开始或到指定任务结束,对任务流进行剪裁 74 | * 75 | * @param taskFlow 76 | * @param fromTaskId 77 | * @param toTaskId 78 | * @return 79 | */ 80 | public static TaskFlow prune(TaskFlow taskFlow, Long fromTaskId, Long toTaskId) { 81 | TaskFlow prunedTaskFlow = new TaskFlow(); 82 | prunedTaskFlow.setId(taskFlow.getId()); 83 | prunedTaskFlow.setCron(taskFlow.getCron()); 84 | prunedTaskFlow.setTaskList(new ArrayList<>()); 85 | prunedTaskFlow.setLinkList(new ArrayList<>()); 86 | 87 | Map id_mapto_task = new HashMap<>(); 88 | for (Task task : taskFlow.getTaskList()) { 89 | id_mapto_task.put(task.getId(), task); 90 | } 91 | Deque deque = new ArrayDeque<>(); 92 | 93 | if (fromTaskId == null && toTaskId == null) { 94 | // 所有任务 95 | prunedTaskFlow = taskFlow; 96 | } else if (fromTaskId != null && fromTaskId.equals(toTaskId)) { 97 | // 指定任务 98 | Task fromTask = id_mapto_task.get(fromTaskId); 99 | prunedTaskFlow.getTaskList().add(fromTask); 100 | } else if (fromTaskId != null) { 101 | // 从指定任务开始 102 | Task fromTask = id_mapto_task.get(fromTaskId); 103 | prunedTaskFlow.getTaskList().add(fromTask); 104 | deque.offer(fromTask); 105 | while (!deque.isEmpty()) { 106 | Task task = deque.poll(); 107 | for (Link link : taskFlow.getLinkList()) { 108 | if (link.getSource().equals(task.getId())) { 109 | Task childTask = id_mapto_task.get(link.getTarget()); 110 | if (!prunedTaskFlow.getTaskList().contains(childTask)) { 111 | prunedTaskFlow.getTaskList().add(childTask); 112 | } 113 | prunedTaskFlow.getLinkList().add(link); 114 | deque.offer(childTask); 115 | } 116 | } 117 | } 118 | } else { 119 | // 到指定任务结束 120 | Task toTask = id_mapto_task.get(toTaskId); 121 | prunedTaskFlow.getTaskList().add(toTask); 122 | deque.offer(toTask); 123 | while (!deque.isEmpty()) { 124 | Task task = deque.poll(); 125 | for (Link link : taskFlow.getLinkList()) { 126 | if (link.getTarget().equals(task.getId())) { 127 | Task parentTask = id_mapto_task.get(link.getSource()); 128 | if (!prunedTaskFlow.getTaskList().contains(parentTask)) { 129 | prunedTaskFlow.getTaskList().add(parentTask); 130 | } 131 | prunedTaskFlow.getLinkList().add(link); 132 | deque.offer(parentTask); 133 | } 134 | } 135 | } 136 | } 137 | return prunedTaskFlow; 138 | } 139 | 140 | } 141 | -------------------------------------------------------------------------------- /wolf-flow-core/src/main/java/me/kpali/wolfflow/core/util/context/ContextWrapper.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.core.util.context; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.concurrent.ConcurrentHashMap; 6 | 7 | /** 8 | * 上下文包装类 9 | * 10 | * @author kpali 11 | */ 12 | public class ContextWrapper { 13 | public ContextWrapper() { 14 | this.context = new ConcurrentHashMap<>(); 15 | } 16 | 17 | public ContextWrapper(ConcurrentHashMap context) { 18 | this.context = (context == null ? new ConcurrentHashMap<>() : context); 19 | } 20 | 21 | protected ConcurrentHashMap context; 22 | 23 | public ConcurrentHashMap getContext() { 24 | return context; 25 | } 26 | 27 | public void setContext(ConcurrentHashMap context) { 28 | this.context = context; 29 | } 30 | 31 | public Object get(String key) { 32 | return this.context.get(key); 33 | } 34 | 35 | public Object put(String key, Object value) { 36 | return this.context.put(key, value); 37 | } 38 | 39 | public boolean containsKey(String key) { 40 | return this.context.containsKey(key); 41 | } 42 | 43 | public T getValue(String key, Class clazz) { 44 | Object value = this.context.get(key); 45 | return this.cast(value, clazz); 46 | } 47 | 48 | public List getList(String key, Class clazz) { 49 | Object value = this.context.get(key); 50 | if (value != null) { 51 | List result = new ArrayList(); 52 | if (value instanceof List) { 53 | for (Object o : (List) value) { 54 | result.add(this.cast(o, clazz)); 55 | } 56 | return result; 57 | } 58 | } 59 | return null; 60 | } 61 | 62 | private T cast(Object obj, Class clazz) { 63 | if (obj != null) { 64 | if (!clazz.isInstance(obj) && obj instanceof Integer) { 65 | // Integer类型 转 Long类型 66 | Long longObj = ((Integer) obj).longValue(); 67 | if (clazz.isInstance(longObj)) { 68 | return clazz.cast(longObj); 69 | } 70 | } 71 | return clazz.cast(obj); 72 | } 73 | return null; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /wolf-flow-core/src/main/java/me/kpali/wolfflow/core/util/context/DeliveryContextWrapper.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.core.util.context; 2 | 3 | import java.util.concurrent.ConcurrentHashMap; 4 | 5 | /** 6 | * 传递上下文包装类 7 | * 8 | * @author kpali 9 | */ 10 | public class DeliveryContextWrapper extends ContextWrapper { 11 | public DeliveryContextWrapper() { 12 | super(); 13 | } 14 | 15 | public DeliveryContextWrapper(ConcurrentHashMap context) { 16 | super(context); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /wolf-flow-core/src/main/java/me/kpali/wolfflow/core/util/context/ParamsWrapper.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.core.util.context; 2 | 3 | import java.util.concurrent.ConcurrentHashMap; 4 | 5 | /** 6 | * 任务流上下文参数包装类 7 | * 8 | * @author kpali 9 | */ 10 | public class ParamsWrapper extends ContextWrapper { 11 | public ParamsWrapper() { 12 | super(); 13 | } 14 | 15 | public ParamsWrapper(ConcurrentHashMap context) { 16 | super(context); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /wolf-flow-core/src/main/java/me/kpali/wolfflow/core/util/context/TaskContextWrapper.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.core.util.context; 2 | 3 | import java.util.concurrent.ConcurrentHashMap; 4 | 5 | /** 6 | * 任务上下文包装类 7 | * 8 | * @author kpali 9 | */ 10 | public class TaskContextWrapper extends ContextWrapper { 11 | public TaskContextWrapper() { 12 | super(); 13 | } 14 | 15 | public TaskContextWrapper(ConcurrentHashMap context) { 16 | super(context); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /wolf-flow-core/src/main/java/me/kpali/wolfflow/core/util/context/TaskFlowContextWrapper.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.core.util.context; 2 | 3 | import me.kpali.wolfflow.core.model.TaskFlowContextKey; 4 | 5 | import java.util.Map; 6 | import java.util.concurrent.ConcurrentHashMap; 7 | 8 | /** 9 | * 任务流上下文包装类 10 | * 11 | * @author kpali 12 | */ 13 | public class TaskFlowContextWrapper extends ContextWrapper { 14 | public TaskFlowContextWrapper() { 15 | super(); 16 | } 17 | 18 | public TaskFlowContextWrapper(ConcurrentHashMap context) { 19 | super(context); 20 | } 21 | 22 | public ConcurrentHashMap getTaskFlowContext() { 23 | ConcurrentHashMap taskFlowContext = null; 24 | if (this.getContext() != null) { 25 | taskFlowContext = new ConcurrentHashMap<>(); 26 | for (String key : this.getContext().keySet()) { 27 | if (TaskFlowContextKey.TASK_CONTEXTS.equals(key)) { 28 | continue; 29 | } 30 | taskFlowContext.put(key, this.getContext().get(key)); 31 | } 32 | } 33 | return taskFlowContext; 34 | } 35 | 36 | public ConcurrentHashMap getParams() { 37 | Object paramsObj = this.context.get(TaskFlowContextKey.PARAMS); 38 | if (paramsObj == null) { 39 | return null; 40 | } 41 | return (ConcurrentHashMap) paramsObj; 42 | } 43 | 44 | public synchronized void setParams(ConcurrentHashMap params) { 45 | Object paramsObj = this.context.get(TaskFlowContextKey.PARAMS); 46 | if (paramsObj == null) { 47 | this.context.put(TaskFlowContextKey.PARAMS, params); 48 | } else { 49 | paramsObj = params; 50 | } 51 | } 52 | 53 | public Object getParam(String key) { 54 | ConcurrentHashMap params = this.getParams(); 55 | if (params == null) { 56 | return null; 57 | } 58 | return params.get(key); 59 | } 60 | 61 | public synchronized void putParam(String key, Object value) { 62 | ConcurrentHashMap params = this.getParams(); 63 | if (params == null) { 64 | params = new ConcurrentHashMap<>(); 65 | this.context.put(TaskFlowContextKey.PARAMS, params); 66 | } 67 | params.put(key, value); 68 | } 69 | 70 | public ParamsWrapper getParamsWrapper() { 71 | ConcurrentHashMap params = this.getParams(); 72 | if (params == null) { 73 | return null; 74 | } 75 | return new ParamsWrapper(params); 76 | } 77 | 78 | public ConcurrentHashMap getDeliveryContext() { 79 | Object deliveryContextObj = this.context.get(TaskFlowContextKey.DELIVERY_CONTEXT); 80 | if (deliveryContextObj == null) { 81 | return null; 82 | } 83 | return (ConcurrentHashMap) deliveryContextObj; 84 | } 85 | 86 | public synchronized void setDeliveryContext(ConcurrentHashMap deliveryContext) { 87 | Object deliveryContextObj = this.context.get(TaskFlowContextKey.DELIVERY_CONTEXT); 88 | if (deliveryContextObj == null) { 89 | this.context.put(TaskFlowContextKey.DELIVERY_CONTEXT, deliveryContext); 90 | } else { 91 | deliveryContextObj = deliveryContext; 92 | } 93 | } 94 | 95 | public Object getDeliveryContext(String key) { 96 | ConcurrentHashMap deliveryContext = this.getDeliveryContext(); 97 | if (deliveryContext == null) { 98 | return null; 99 | } 100 | return deliveryContext.get(key); 101 | } 102 | 103 | public synchronized void putDeliveryContext(String key, Object value) { 104 | ConcurrentHashMap deliveryContext = this.getDeliveryContext(); 105 | if (deliveryContext == null) { 106 | deliveryContext = new ConcurrentHashMap<>(); 107 | this.context.put(TaskFlowContextKey.DELIVERY_CONTEXT, deliveryContext); 108 | } 109 | deliveryContext.put(key, value); 110 | } 111 | 112 | public DeliveryContextWrapper getDeliveryContextWrapper() { 113 | ConcurrentHashMap deliveryContext = this.getDeliveryContext(); 114 | if (deliveryContext == null) { 115 | return null; 116 | } 117 | return new DeliveryContextWrapper(deliveryContext); 118 | } 119 | 120 | public Map> getTaskContexts() { 121 | Object taskContextObj = this.context.get(TaskFlowContextKey.TASK_CONTEXTS); 122 | if (taskContextObj == null) { 123 | return null; 124 | } 125 | return (Map>) taskContextObj; 126 | } 127 | 128 | public synchronized void setTaskContexts(Map> taskContexts) { 129 | Object taskContextObj = this.context.get(TaskFlowContextKey.TASK_CONTEXTS); 130 | if (taskContextObj == null) { 131 | this.context.put(TaskFlowContextKey.TASK_CONTEXTS, taskContexts); 132 | } else { 133 | taskContextObj = taskContexts; 134 | } 135 | } 136 | 137 | public TaskContextWrapper getTaskContextWrapper(String taskId) { 138 | Map> taskContexts = this.getTaskContexts(); 139 | if (taskContexts == null) { 140 | return null; 141 | } 142 | ConcurrentHashMap taskContext = taskContexts.get(taskId); 143 | if (taskContext == null) { 144 | return null; 145 | } 146 | return new TaskContextWrapper(taskContext); 147 | } 148 | 149 | public ConcurrentHashMap getTaskContext(String taskId) { 150 | Map> taskContexts = this.getTaskContexts(); 151 | if (taskContexts == null) { 152 | return null; 153 | } 154 | return taskContexts.get(taskId); 155 | } 156 | 157 | public synchronized void putTaskContext(String taskId, ConcurrentHashMap taskContext) { 158 | Map> taskContexts = this.getTaskContexts(); 159 | if (taskContexts == null) { 160 | taskContexts = new ConcurrentHashMap<>(); 161 | this.context.put(TaskFlowContextKey.TASK_CONTEXTS, taskContexts); 162 | } 163 | taskContexts.put(taskId, taskContext); 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /wolf-flow-core/src/main/resources/quartz.properties: -------------------------------------------------------------------------------- 1 | org.quartz.scheduler.instanceName = MyScheduler 2 | org.quartz.threadPool.threadCount = 10 3 | org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore 4 | org.quartz.jobStore.misfireThreshold = 60000 -------------------------------------------------------------------------------- /wolf-flow-core/src/test/java/me/kpali/wolfflow/core/BaseTest.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.core; 2 | 3 | import me.kpali.wolfflow.core.config.ClusterConfig; 4 | import me.kpali.wolfflow.core.config.ExecutorConfig; 5 | import me.kpali.wolfflow.core.config.SchedulerConfig; 6 | import me.kpali.wolfflow.core.launcher.Launcher; 7 | import org.junit.jupiter.api.MethodOrderer; 8 | import org.junit.jupiter.api.Order; 9 | import org.junit.jupiter.api.Test; 10 | import org.junit.jupiter.api.TestMethodOrder; 11 | import org.springframework.beans.factory.annotation.Autowired; 12 | import org.springframework.boot.test.context.SpringBootTest; 13 | 14 | @TestMethodOrder(MethodOrderer.OrderAnnotation.class) 15 | @SpringBootTest 16 | public class BaseTest { 17 | @Autowired 18 | ClusterConfig clusterConfig; 19 | 20 | @Autowired 21 | SchedulerConfig schedulerConfig; 22 | 23 | @Autowired 24 | ExecutorConfig executorConfig; 25 | 26 | @Autowired 27 | Launcher launcher; 28 | 29 | @Order(0) 30 | @Test 31 | public void init() { 32 | this.clusterConfig.setNodeHeartbeatInterval(30); 33 | this.clusterConfig.setNodeHeartbeatDuration(90); 34 | this.clusterConfig.setGenerateNodeIdLockLeaseTime(60); 35 | this.clusterConfig.setTaskFlowLogLockWaitTime(10); 36 | this.clusterConfig.setTaskFlowLogLockLeaseTime(15); 37 | this.clusterConfig.setTaskLogLockWaitTime(10); 38 | this.clusterConfig.setTaskLogLockLeaseTime(15); 39 | 40 | this.schedulerConfig.setExecRequestScanInterval(1); 41 | this.schedulerConfig.setCronScanInterval(10); 42 | this.schedulerConfig.setCronScanLockWaitTime(10); 43 | this.schedulerConfig.setCronScanLockLeaseTime(60); 44 | this.schedulerConfig.setCorePoolSize(10); 45 | this.schedulerConfig.setMaximumPoolSize(10); 46 | 47 | this.executorConfig.setCorePoolSize(30); 48 | this.executorConfig.setMaximumPoolSize(30); 49 | 50 | this.launcher.startup(); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /wolf-flow-core/src/test/java/me/kpali/wolfflow/core/cluster/impl/DefaultClusterControllerTest.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.core.cluster.impl; 2 | 3 | import me.kpali.wolfflow.core.BaseTest; 4 | import me.kpali.wolfflow.core.cluster.IClusterController; 5 | import me.kpali.wolfflow.core.exception.GenerateNodeIdException; 6 | import me.kpali.wolfflow.core.model.ManualConfirmed; 7 | import me.kpali.wolfflow.core.model.TaskFlowExecRequest; 8 | import org.junit.jupiter.api.*; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | 11 | import java.util.concurrent.TimeUnit; 12 | 13 | import static org.junit.jupiter.api.Assertions.*; 14 | 15 | @TestMethodOrder(MethodOrderer.OrderAnnotation.class) 16 | public class DefaultClusterControllerTest extends BaseTest { 17 | 18 | @Autowired 19 | IClusterController clusterController; 20 | 21 | @Test 22 | public void testGetNodeId() throws GenerateNodeIdException { 23 | this.clusterController.generateNodeId(); 24 | assertNotNull(this.clusterController.getNodeId()); 25 | } 26 | 27 | @Order(1) 28 | @Test 29 | public void testHeartbeat() { 30 | this.clusterController.heartbeat(); 31 | } 32 | 33 | @Order(2) 34 | @Test 35 | public void testIsNodeAlive() { 36 | assertTrue(this.clusterController.isNodeAlive(this.clusterController.getNodeId())); 37 | } 38 | 39 | @Test 40 | public void testLock() { 41 | this.clusterController.lock("testLock"); 42 | } 43 | 44 | @Test 45 | public void testLockWithLeaseTime() { 46 | this.clusterController.lock("testLockWithLeaseTime", 15, TimeUnit.SECONDS); 47 | } 48 | 49 | @Test 50 | public void testTryLock() { 51 | this.clusterController.tryLock("testTryLock", 10, 15, TimeUnit.SECONDS); 52 | } 53 | 54 | @Test 55 | public void testUnlock() { 56 | String lockName = "testUnlock"; 57 | this.clusterController.lock(lockName); 58 | this.clusterController.unlock(lockName); 59 | } 60 | 61 | @Test 62 | public void testExecRequest() { 63 | this.clusterController.execRequestOffer(new TaskFlowExecRequest()); 64 | assertNotNull(this.clusterController.execRequestPoll()); 65 | } 66 | 67 | @Test 68 | public void testStopRequest() { 69 | Long taskFlowLogId = 1L; 70 | this.clusterController.stopRequestAdd(taskFlowLogId); 71 | boolean isContains = this.clusterController.stopRequestContains(taskFlowLogId); 72 | assertEquals(isContains, true); 73 | this.clusterController.stopRequestRemove(taskFlowLogId); 74 | isContains = this.clusterController.stopRequestContains(taskFlowLogId); 75 | assertFalse(isContains); 76 | } 77 | 78 | @Test 79 | public void testManualConfirmed() { 80 | Long taskLogId = 1L; 81 | this.clusterController.manualConfirmedAdd(new ManualConfirmed(taskLogId, true, null)); 82 | ManualConfirmed manualConfirmed = this.clusterController.manualConfirmedGet(taskLogId); 83 | assertNotNull(manualConfirmed); 84 | this.clusterController.manualConfirmedRemove(taskLogId); 85 | manualConfirmed = this.clusterController.manualConfirmedGet(taskLogId); 86 | assertNull(manualConfirmed); 87 | } 88 | } -------------------------------------------------------------------------------- /wolf-flow-core/src/test/java/me/kpali/wolfflow/core/listener/TaskFlowEventListener.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.core.listener; 2 | 3 | import me.kpali.wolfflow.core.event.ScheduleStatusChangeEvent; 4 | import me.kpali.wolfflow.core.event.TaskFlowStatusChangeEvent; 5 | import me.kpali.wolfflow.core.event.TaskStatusChangeEvent; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | import org.springframework.context.event.EventListener; 9 | import org.springframework.stereotype.Component; 10 | 11 | @Component 12 | public class TaskFlowEventListener { 13 | private static final Logger logger = LoggerFactory.getLogger(TaskFlowEventListener.class); 14 | 15 | @EventListener 16 | public void taskFlowScheduleStatusChange(ScheduleStatusChangeEvent event) { 17 | // 任务流调度状态变更监听,主要为定时任务流,状态有[加入调度、更新调度、调度失败]等 18 | } 19 | 20 | @EventListener 21 | public void taskFlowStatusChange(TaskFlowStatusChangeEvent event) { 22 | // 任务流状态变更监听,状态有[等待执行、执行中、执行成功、执行失败、停止中]等 23 | logger.info(">>>>>>>>>> Task flow [{}] status changed to: {}", event.getTaskFlowStatus().getTaskFlow().getId(), event.getTaskFlowStatus().getStatus()); 24 | } 25 | 26 | @EventListener 27 | public void taskStatusChange(TaskStatusChangeEvent event) { 28 | // 任务状态变更监听,状态有[等待执行、执行中、执行成功、执行失败、停止中、跳过]等 29 | logger.info(">>>>>>>>>> Task [{}] status changed to: {}", event.getTaskStatus().getTask().getId(), event.getTaskStatus().getStatus()); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /wolf-flow-core/src/test/java/me/kpali/wolfflow/core/model/AutoTask.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.core.model; 2 | 3 | import me.kpali.wolfflow.core.exception.TaskExecuteException; 4 | import me.kpali.wolfflow.core.exception.TaskInterruptedException; 5 | import me.kpali.wolfflow.core.exception.TaskRollbackException; 6 | import me.kpali.wolfflow.core.exception.TaskStopException; 7 | import me.kpali.wolfflow.core.logger.ITaskLogger; 8 | import me.kpali.wolfflow.core.util.SpringContextUtil; 9 | import me.kpali.wolfflow.core.util.context.TaskContextWrapper; 10 | import me.kpali.wolfflow.core.util.context.TaskFlowContextWrapper; 11 | 12 | import java.util.concurrent.ConcurrentHashMap; 13 | 14 | public class AutoTask extends Task { 15 | private boolean requiredToStop = false; 16 | 17 | @Override 18 | public void execute(ConcurrentHashMap context) throws TaskExecuteException, TaskInterruptedException { 19 | if (context == null) { 20 | throw new IllegalArgumentException(); 21 | } 22 | try { 23 | ITaskLogger taskLogger = SpringContextUtil.getBean(ITaskLogger.class); 24 | TaskFlowContextWrapper taskFlowContextWrapper = new TaskFlowContextWrapper(context); 25 | ConcurrentHashMap taskContext = taskFlowContextWrapper.getTaskContext(this.getId().toString()); 26 | TaskContextWrapper taskContextWrapper = new TaskContextWrapper(taskContext); 27 | Long taskLogId = taskContextWrapper.getValue(TaskContextKey.TASK_LOG_ID, Long.class); 28 | String taskLogFileId = taskContextWrapper.getValue(TaskContextKey.TASK_LOG_FILE_ID, String.class); 29 | taskLogger.log(taskLogFileId, "Task executing...", false); 30 | taskLogger.log(taskLogFileId, "Second line...\rThird line...\nFourth line...\r\nFifth line...", false); 31 | if (requiredToStop) { 32 | taskLogger.log(taskLogFileId, "Task execution is terminated", true); 33 | throw new TaskInterruptedException("Task execution is terminated"); 34 | } 35 | taskLogger.log(taskLogFileId, "Task execution finished", true); 36 | } catch (TaskInterruptedException e) { 37 | throw e; 38 | } catch (Exception e) { 39 | throw new TaskExecuteException(e); 40 | } 41 | } 42 | 43 | @Override 44 | public void rollback(ConcurrentHashMap context) throws TaskRollbackException, TaskInterruptedException { 45 | try { 46 | ITaskLogger taskLogger = SpringContextUtil.getBean(ITaskLogger.class); 47 | TaskFlowContextWrapper taskFlowContextWrapper = new TaskFlowContextWrapper(context); 48 | ConcurrentHashMap taskContext = taskFlowContextWrapper.getTaskContext(this.getId().toString()); 49 | TaskContextWrapper taskContextWrapper = new TaskContextWrapper(taskContext); 50 | Long taskLogId = taskContextWrapper.getValue(TaskContextKey.TASK_LOG_ID, Long.class); 51 | String taskLogFileId = taskContextWrapper.getValue(TaskContextKey.TASK_LOG_FILE_ID, String.class); 52 | taskLogger.log(taskLogFileId, "Task rolling back...", false); 53 | taskLogger.log(taskLogFileId, "Second line...\rThird line...\nFourth line...\r\nFifth line...", false); 54 | if (requiredToStop) { 55 | taskLogger.log(taskLogFileId, "Task execution is terminated", true); 56 | throw new TaskInterruptedException("Task execution is terminated"); 57 | } 58 | taskLogger.log(taskLogFileId, "Task rollback finished", true); 59 | } catch (TaskInterruptedException e) { 60 | throw e; 61 | } catch (Exception e) { 62 | throw new TaskRollbackException(e); 63 | } 64 | } 65 | 66 | @Override 67 | public void stop(ConcurrentHashMap context) throws TaskStopException { 68 | this.requiredToStop = true; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /wolf-flow-core/src/test/java/me/kpali/wolfflow/core/model/ManualTask.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.core.model; 2 | 3 | import me.kpali.wolfflow.core.cluster.IClusterController; 4 | import me.kpali.wolfflow.core.exception.TaskExecuteException; 5 | import me.kpali.wolfflow.core.exception.TaskInterruptedException; 6 | import me.kpali.wolfflow.core.exception.TaskRollbackException; 7 | import me.kpali.wolfflow.core.exception.TaskStopException; 8 | import me.kpali.wolfflow.core.util.SpringContextUtil; 9 | import me.kpali.wolfflow.core.util.context.TaskContextWrapper; 10 | import me.kpali.wolfflow.core.util.context.TaskFlowContextWrapper; 11 | 12 | import java.util.concurrent.ConcurrentHashMap; 13 | 14 | public class ManualTask extends Task { 15 | public ManualTask() { 16 | this.setManual(true); 17 | } 18 | 19 | private boolean requiredToStop = false; 20 | 21 | @Override 22 | public void execute(ConcurrentHashMap context) throws TaskExecuteException, TaskInterruptedException { 23 | try { 24 | IClusterController clusterController = SpringContextUtil.getBean(IClusterController.class); 25 | TaskFlowContextWrapper taskFlowContextWrapper = new TaskFlowContextWrapper(context); 26 | ConcurrentHashMap taskContext = taskFlowContextWrapper.getTaskContext(this.getId().toString()); 27 | TaskContextWrapper taskContextWrapper = new TaskContextWrapper(taskContext); 28 | Long taskLogId = taskContextWrapper.getValue(TaskContextKey.TASK_LOG_ID, Long.class); 29 | 30 | while (true) { 31 | try { 32 | Thread.sleep(10); 33 | } catch (InterruptedException e) { 34 | Thread.currentThread().interrupt(); 35 | } 36 | // 检查是否收到停止任务指令 37 | if (requiredToStop) { 38 | throw new TaskInterruptedException("Task execution is terminated"); 39 | } 40 | // 检查手工确认结果 41 | ManualConfirmed manualConfirmed = clusterController.manualConfirmedGet(taskLogId); 42 | if (manualConfirmed != null) { 43 | clusterController.manualConfirmedRemove(taskLogId); 44 | if (manualConfirmed.getSuccess()) { 45 | break; 46 | } else { 47 | throw new TaskExecuteException(manualConfirmed.getMessage()); 48 | } 49 | } 50 | } 51 | } catch (TaskInterruptedException e) { 52 | throw e; 53 | } catch (Exception e) { 54 | throw new TaskExecuteException(e); 55 | } 56 | } 57 | 58 | @Override 59 | public void rollback(ConcurrentHashMap context) throws TaskRollbackException, TaskInterruptedException { 60 | try { 61 | IClusterController clusterController = SpringContextUtil.getBean(IClusterController.class); 62 | TaskFlowContextWrapper taskFlowContextWrapper = new TaskFlowContextWrapper(context); 63 | ConcurrentHashMap taskContext = taskFlowContextWrapper.getTaskContext(this.getId().toString()); 64 | TaskContextWrapper taskContextWrapper = new TaskContextWrapper(taskContext); 65 | Long taskLogId = taskContextWrapper.getValue(TaskContextKey.TASK_LOG_ID, Long.class); 66 | 67 | while (true) { 68 | try { 69 | Thread.sleep(10); 70 | } catch (InterruptedException e) { 71 | Thread.currentThread().interrupt(); 72 | } 73 | // 检查是否收到停止任务指令 74 | if (requiredToStop) { 75 | throw new TaskInterruptedException("Task rollback is terminated"); 76 | } 77 | // 检查手工确认结果 78 | ManualConfirmed manualConfirmed = clusterController.manualConfirmedGet(taskLogId); 79 | if (manualConfirmed != null) { 80 | clusterController.manualConfirmedRemove(taskLogId); 81 | if (manualConfirmed.getSuccess()) { 82 | break; 83 | } else { 84 | throw new TaskRollbackException(manualConfirmed.getMessage()); 85 | } 86 | } 87 | } 88 | } catch (TaskInterruptedException e) { 89 | throw e; 90 | } catch (Exception e) { 91 | throw new TaskRollbackException(e); 92 | } 93 | } 94 | 95 | @Override 96 | public void stop(ConcurrentHashMap context) throws TaskStopException { 97 | this.requiredToStop = true; 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /wolf-flow-core/src/test/java/me/kpali/wolfflow/core/util/SpringContextUtil.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.core.util; 2 | 3 | import org.springframework.beans.BeansException; 4 | import org.springframework.context.ApplicationContext; 5 | import org.springframework.context.ApplicationContextAware; 6 | import org.springframework.stereotype.Component; 7 | 8 | @Component 9 | public class SpringContextUtil implements ApplicationContextAware { 10 | private static ApplicationContext applicationContext = null; 11 | 12 | @Override 13 | public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { 14 | SpringContextUtil.applicationContext = applicationContext; 15 | } 16 | 17 | public static ApplicationContext getApplicationContext() { 18 | return applicationContext; 19 | } 20 | 21 | public static Object getBean(String beanId) { 22 | return applicationContext.getBean(beanId); 23 | } 24 | 25 | public static T getBean(Class requiredType) { 26 | return applicationContext.getBean(requiredType); 27 | } 28 | } 29 | 30 | -------------------------------------------------------------------------------- /wolf-flow-sample-cluster/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 2.2.4.RELEASE 9 | 10 | 11 | me.kpali 12 | wolf-flow-sample-cluster 13 | 1.0.0 14 | wolf-flow-sample-cluster ${project.version} 15 | Wolf flow Sample Cluster 16 | 17 | 18 | 19 | org.springframework.boot 20 | spring-boot-starter 21 | 22 | 23 | org.springframework.boot 24 | spring-boot-starter-web 25 | 26 | 27 | 28 | org.springframework.boot 29 | spring-boot-devtools 30 | runtime 31 | true 32 | 33 | 34 | org.springframework.boot 35 | spring-boot-starter-test 36 | test 37 | 38 | 39 | org.junit.vintage 40 | junit-vintage-engine 41 | 42 | 43 | 44 | 45 | 46 | org.redisson 47 | redisson-spring-data-22 48 | 3.12.0 49 | 50 | 51 | org.redisson 52 | redisson-spring-boot-starter 53 | 3.12.0 54 | 55 | 56 | 57 | me.kpali 58 | wolf-flow-spring-boot-starter 59 | 2.1.1 60 | 61 | 62 | 63 | 64 | 65 | 66 | org.springframework.boot 67 | spring-boot-maven-plugin 68 | 69 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /wolf-flow-sample-cluster/src/main/java/me/kpali/wolfflow/sample/cluster/WolfFlowSampleClusterApplication.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.sample.cluster; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class WolfFlowSampleClusterApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(WolfFlowSampleClusterApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /wolf-flow-sample-cluster/src/main/java/me/kpali/wolfflow/sample/cluster/config/RedissonSpringDataConfig.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.sample.cluster.config; 2 | 3 | import org.redisson.Redisson; 4 | import org.redisson.api.RedissonClient; 5 | import org.redisson.config.Config; 6 | import org.redisson.spring.data.connection.RedissonConnectionFactory; 7 | import org.springframework.beans.factory.annotation.Value; 8 | import org.springframework.context.annotation.Bean; 9 | import org.springframework.context.annotation.Configuration; 10 | import org.springframework.core.io.Resource; 11 | 12 | import java.io.IOException; 13 | 14 | @Configuration 15 | public class RedissonSpringDataConfig { 16 | @Bean 17 | public RedissonConnectionFactory redissonConnectionFactory(RedissonClient redisson) { 18 | return new RedissonConnectionFactory(redisson); 19 | } 20 | 21 | @Bean(destroyMethod = "shutdown") 22 | public RedissonClient redisson(@Value("classpath:/redisson.yaml") Resource configFile) throws IOException { 23 | Config config = Config.fromYAML(configFile.getInputStream()); 24 | return Redisson.create(config); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /wolf-flow-sample-cluster/src/main/java/me/kpali/wolfflow/sample/cluster/controller/MetricsController.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.sample.cluster.controller; 2 | 3 | import me.kpali.wolfflow.core.monitor.IMonitor; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.web.bind.annotation.GetMapping; 6 | import org.springframework.web.bind.annotation.RestController; 7 | 8 | @RestController 9 | public class MetricsController { 10 | @Autowired 11 | IMonitor monitor; 12 | 13 | @GetMapping(value = "/prometheus", produces = {"text/plain; version=0.0.4; charset=utf-8"}) 14 | public String getMetrics() { 15 | String response = monitor.scrape(); 16 | return response; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /wolf-flow-sample-cluster/src/main/java/me/kpali/wolfflow/sample/cluster/controller/TriggerController.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.sample.cluster.controller; 2 | 3 | import me.kpali.wolfflow.core.exception.TaskFlowTriggerException; 4 | import me.kpali.wolfflow.core.scheduler.ITaskFlowScheduler; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.web.bind.annotation.GetMapping; 7 | import org.springframework.web.bind.annotation.PathVariable; 8 | import org.springframework.web.bind.annotation.RestController; 9 | 10 | @RestController 11 | public class TriggerController { 12 | @Autowired 13 | ITaskFlowScheduler taskFlowScheduler; 14 | 15 | @GetMapping(value = "/trigger/{taskFlowId}") 16 | public void trigger(@PathVariable Long taskFlowId) throws TaskFlowTriggerException { 17 | taskFlowScheduler.execute(taskFlowId, null); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /wolf-flow-sample-cluster/src/main/java/me/kpali/wolfflow/sample/cluster/listener/ApplicationReadyEventListener.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.sample.cluster.listener; 2 | 3 | import me.kpali.wolfflow.core.launcher.Launcher; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.boot.context.event.ApplicationReadyEvent; 6 | import org.springframework.context.ApplicationListener; 7 | import org.springframework.stereotype.Component; 8 | 9 | /** 10 | * 程序启动完成事件监听,在程序启动后启动任务流相关的后台线程 11 | * (必要) 12 | * 13 | * @author kpali 14 | */ 15 | @Component 16 | public class ApplicationReadyEventListener implements ApplicationListener { 17 | @Autowired 18 | private Launcher launcher; 19 | 20 | @Override 21 | public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) { 22 | this.launcher.startup(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /wolf-flow-sample-cluster/src/main/java/me/kpali/wolfflow/sample/cluster/listener/TaskFlowEventListener.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.sample.cluster.listener; 2 | 3 | import me.kpali.wolfflow.core.event.ScheduleStatusChangeEvent; 4 | import me.kpali.wolfflow.core.event.TaskFlowStatusChangeEvent; 5 | import me.kpali.wolfflow.core.event.TaskStatusChangeEvent; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | import org.springframework.context.event.EventListener; 9 | import org.springframework.stereotype.Component; 10 | 11 | /** 12 | * 任务流事件监听 13 | * (可选) 14 | * 15 | * @author kpali 16 | */ 17 | @Component 18 | public class TaskFlowEventListener { 19 | private static final Logger logger = LoggerFactory.getLogger(TaskFlowEventListener.class); 20 | 21 | @EventListener 22 | public void taskFlowScheduleStatusChange(ScheduleStatusChangeEvent event) { 23 | // 任务流调度状态变更监听,主要为定时任务流,状态有[加入调度、更新调度、调度失败]等 24 | } 25 | 26 | @EventListener 27 | public void taskFlowStatusChange(TaskFlowStatusChangeEvent event) { 28 | // 任务流状态变更监听,状态有[等待执行、执行中、执行成功、执行失败、停止中]等 29 | logger.info(">>>>>>>>>> Task flow [{}] status changed to: {}", event.getTaskFlowStatus().getTaskFlow().getId(), event.getTaskFlowStatus().getStatus()); 30 | } 31 | 32 | @EventListener 33 | public void taskStatusChange(TaskStatusChangeEvent event) { 34 | // 任务状态变更监听,状态有[等待执行、执行中、执行成功、执行失败、停止中、跳过]等 35 | logger.info(">>>>>>>>>> Task [{}] status changed to: {}", event.getTaskStatus().getTask().getId(), event.getTaskStatus().getStatus()); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /wolf-flow-sample-cluster/src/main/java/me/kpali/wolfflow/sample/cluster/taskflow/MyClusterController.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.sample.cluster.taskflow; 2 | 3 | import me.kpali.wolfflow.core.cluster.impl.DefaultClusterController; 4 | import me.kpali.wolfflow.core.config.ClusterConfig; 5 | import me.kpali.wolfflow.core.exception.GenerateNodeIdException; 6 | import me.kpali.wolfflow.core.model.ClusterConstants; 7 | import me.kpali.wolfflow.core.model.ManualConfirmed; 8 | import me.kpali.wolfflow.core.model.TaskFlowExecRequest; 9 | import org.redisson.api.*; 10 | import org.slf4j.Logger; 11 | import org.slf4j.LoggerFactory; 12 | import org.springframework.beans.factory.annotation.Autowired; 13 | import org.springframework.context.annotation.Primary; 14 | import org.springframework.stereotype.Component; 15 | 16 | import java.util.concurrent.TimeUnit; 17 | 18 | /** 19 | * 可以覆写默认集群控制器的方法实现自定义,本示例使用Redis实现集群控制器来支持集群 20 | * (可选) 21 | * 22 | * @author kpali 23 | */ 24 | @Primary 25 | @Component 26 | public class MyClusterController extends DefaultClusterController { 27 | private static final Logger logger = LoggerFactory.getLogger(MyClusterController.class); 28 | 29 | @Autowired 30 | private RedissonClient redisson; 31 | 32 | @Autowired 33 | private ClusterConfig clusterConfig; 34 | 35 | private static final String NODE_ID = "nodeId"; 36 | private static final String NODE_HEARTBEAT = "nodeHeartbeat"; 37 | private static final String TASK_FLOW_EXEC_REQUEST = "taskFlowExecRequest"; 38 | private static final String TASK_FLOW_STOP_REQUEST = "taskFlowStopRequest"; 39 | private static final String MANUAL_CONFIRMED = "manualConfirmed"; 40 | 41 | @Override 42 | public void generateNodeId() throws GenerateNodeIdException { 43 | try { 44 | this.lock(ClusterConstants.GENERATE_NODE_ID_LOCK, 45 | clusterConfig.getGenerateNodeIdLockLeaseTime(), TimeUnit.SECONDS); 46 | RAtomicLong atomicLong = redisson.getAtomicLong(NODE_ID); 47 | // 当前分布式ID生成算法默认最大支持32个节点 48 | int maxNodeNum = 32; 49 | for (int i = 0; i < maxNodeNum; i++) { 50 | long id = atomicLong.incrementAndGet() % maxNodeNum; 51 | if (!this.isNodeAlive(id)) { 52 | // ID没有被占用 53 | this.nodeId = id; 54 | return; 55 | } 56 | } 57 | throw new GenerateNodeIdException("Failed to generate node id, maximum node numbers has been reached!"); 58 | } finally { 59 | try { 60 | this.unlock(ClusterConstants.GENERATE_NODE_ID_LOCK); 61 | } catch (Exception e) { 62 | logger.warn(e.getMessage(), e); 63 | } 64 | } 65 | } 66 | 67 | @Override 68 | public void heartbeat() { 69 | RLock lock = redisson.getLock(NODE_HEARTBEAT + ":" + this.getNodeId()); 70 | lock.lock(this.clusterConfig.getNodeHeartbeatDuration(), TimeUnit.SECONDS); 71 | } 72 | 73 | @Override 74 | public boolean isNodeAlive(Long nodeId) { 75 | RLock lock = redisson.getLock(NODE_HEARTBEAT + ":" + nodeId); 76 | return lock.isLocked(); 77 | } 78 | 79 | @Override 80 | public void lock(String name) { 81 | RLock lock = redisson.getLock(name); 82 | lock.lock(); 83 | } 84 | 85 | @Override 86 | public void lock(String name, long leaseTime, TimeUnit unit) { 87 | RLock lock = redisson.getLock(name); 88 | lock.lock(leaseTime, unit); 89 | } 90 | 91 | @Override 92 | public boolean tryLock(String name, long waitTime, long leaseTime, TimeUnit unit) { 93 | RLock lock = redisson.getLock(name); 94 | boolean res = false; 95 | try { 96 | res = lock.tryLock(waitTime, leaseTime, unit); 97 | } catch (Exception e) { 98 | logger.error("Try lock [" + name + "] failed: " + e.getMessage(), e); 99 | } 100 | return res; 101 | } 102 | 103 | @Override 104 | public void unlock(String name) { 105 | RLock lock = redisson.getLock(name); 106 | lock.unlock(); 107 | } 108 | 109 | @Override 110 | public boolean execRequestOffer(TaskFlowExecRequest request) { 111 | RQueue queue = redisson.getQueue(TASK_FLOW_EXEC_REQUEST); 112 | return queue.offer(request); 113 | } 114 | 115 | @Override 116 | public TaskFlowExecRequest execRequestPoll() { 117 | RQueue queue = redisson.getQueue(TASK_FLOW_EXEC_REQUEST); 118 | return queue.poll(); 119 | } 120 | 121 | @Override 122 | public void stopRequestAdd(Long taskFlowLogId) { 123 | RSet set = redisson.getSet(TASK_FLOW_STOP_REQUEST); 124 | set.add(taskFlowLogId); 125 | } 126 | 127 | @Override 128 | public Boolean stopRequestContains(Long taskFlowLogId) { 129 | RSet set = redisson.getSet(TASK_FLOW_STOP_REQUEST); 130 | return set.contains(taskFlowLogId); 131 | } 132 | 133 | @Override 134 | public void stopRequestRemove(Long taskFlowLogId) { 135 | RSet set = redisson.getSet(TASK_FLOW_STOP_REQUEST); 136 | set.remove(taskFlowLogId); 137 | } 138 | 139 | @Override 140 | public void manualConfirmedAdd(ManualConfirmed manualConfirmed) { 141 | RMap map = redisson.getMap(MANUAL_CONFIRMED); 142 | map.put(manualConfirmed.getTaskLogId(), manualConfirmed); 143 | } 144 | 145 | @Override 146 | public ManualConfirmed manualConfirmedGet(Long taskLogId) { 147 | RMap map = redisson.getMap(MANUAL_CONFIRMED); 148 | return map.get(taskLogId); 149 | } 150 | 151 | @Override 152 | public void manualConfirmedRemove(Long taskLogId) { 153 | RMap map = redisson.getMap(MANUAL_CONFIRMED); 154 | map.remove(taskLogId); 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /wolf-flow-sample-cluster/src/main/java/me/kpali/wolfflow/sample/cluster/taskflow/MyTask.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.sample.cluster.taskflow; 2 | 3 | import me.kpali.wolfflow.core.exception.TaskExecuteException; 4 | import me.kpali.wolfflow.core.exception.TaskInterruptedException; 5 | import me.kpali.wolfflow.core.exception.TaskStopException; 6 | import me.kpali.wolfflow.core.logger.ITaskLogger; 7 | import me.kpali.wolfflow.core.model.Task; 8 | import me.kpali.wolfflow.core.model.TaskContextKey; 9 | import me.kpali.wolfflow.core.util.context.TaskContextWrapper; 10 | import me.kpali.wolfflow.core.util.context.TaskFlowContextWrapper; 11 | import me.kpali.wolfflow.sample.cluster.util.SpringContextUtil; 12 | 13 | import java.util.concurrent.ConcurrentHashMap; 14 | 15 | /** 16 | * 自定义任务,覆写父类的方法,实现自定义任务的执行内容 17 | * (必要) 18 | * 19 | * @author kpali 20 | */ 21 | public class MyTask extends Task { 22 | private boolean requiredToStop = false; 23 | 24 | @Override 25 | public void execute(ConcurrentHashMap context) throws TaskExecuteException, TaskInterruptedException { 26 | try { 27 | ITaskLogger taskLogger = SpringContextUtil.getBean(ITaskLogger.class); 28 | TaskFlowContextWrapper taskFlowContextWrapper = new TaskFlowContextWrapper(context); 29 | ConcurrentHashMap taskContext = taskFlowContextWrapper.getTaskContext(this.getId().toString()); 30 | TaskContextWrapper taskContextWrapper = new TaskContextWrapper(taskContext); 31 | Long taskLogId = taskContextWrapper.getValue(TaskContextKey.TASK_LOG_ID, Long.class); 32 | String taskLogFileId = taskContextWrapper.getValue(TaskContextKey.TASK_LOG_FILE_ID, String.class); 33 | taskLogger.log(taskLogFileId, "Task executing...", false); 34 | taskLogger.log(taskLogFileId, "Second line...\rThird line...\nFourth line...\r\nFifth line...", false); 35 | if (requiredToStop) { 36 | taskLogger.log(taskLogFileId, "Task execution is terminated", true); 37 | throw new TaskInterruptedException("Task execution is terminated"); 38 | } 39 | taskLogger.log(taskLogFileId, "Task execution finished", true); 40 | } catch (TaskInterruptedException e) { 41 | throw e; 42 | } catch (Exception e) { 43 | throw new TaskExecuteException(e); 44 | } 45 | } 46 | 47 | @Override 48 | public void stop(ConcurrentHashMap context) throws TaskStopException { 49 | this.requiredToStop = true; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /wolf-flow-sample-cluster/src/main/java/me/kpali/wolfflow/sample/cluster/taskflow/MyTaskFlowExecutor.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.sample.cluster.taskflow; 2 | 3 | import me.kpali.wolfflow.core.executor.impl.DefaultTaskFlowExecutor; 4 | import org.springframework.context.annotation.Primary; 5 | import org.springframework.stereotype.Component; 6 | 7 | /** 8 | * 可以覆写默认任务流执行器的方法实现自定义,但一般情况下直接使用默认任务流执行器即可 9 | * (可选) 10 | * 11 | * @author kpali 12 | */ 13 | @Primary 14 | @Component 15 | public class MyTaskFlowExecutor extends DefaultTaskFlowExecutor { 16 | } 17 | -------------------------------------------------------------------------------- /wolf-flow-sample-cluster/src/main/java/me/kpali/wolfflow/sample/cluster/taskflow/MyTaskFlowLogger.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.sample.cluster.taskflow; 2 | 3 | import me.kpali.wolfflow.core.logger.impl.DefaultTaskFlowLogger; 4 | import org.springframework.context.annotation.Primary; 5 | import org.springframework.stereotype.Component; 6 | 7 | /** 8 | * 可以覆写默认任务流日志器的方法实现自定义,推荐使用数据库DAO实现日志持久化到硬盘。本示例为简单起见仍然使用默认实现存储到内存 9 | * (可选) 10 | * 11 | * @author kpali 12 | */ 13 | @Primary 14 | @Component 15 | public class MyTaskFlowLogger extends DefaultTaskFlowLogger { 16 | } 17 | -------------------------------------------------------------------------------- /wolf-flow-sample-cluster/src/main/java/me/kpali/wolfflow/sample/cluster/taskflow/MyTaskFlowQuerier.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.sample.cluster.taskflow; 2 | 3 | import me.kpali.wolfflow.core.exception.TaskFlowQueryException; 4 | import me.kpali.wolfflow.core.model.Link; 5 | import me.kpali.wolfflow.core.model.TaskFlow; 6 | import me.kpali.wolfflow.core.querier.impl.DefaultTaskFlowQuerier; 7 | import org.springframework.context.annotation.Primary; 8 | import org.springframework.stereotype.Component; 9 | 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | import java.util.stream.Collectors; 13 | 14 | /** 15 | * 自定义任务流查询器,覆写父类的方法,实现自定义任务流查询器 16 | * (必要) 17 | * 18 | * @author kpali 19 | */ 20 | @Primary 21 | @Component 22 | public class MyTaskFlowQuerier extends DefaultTaskFlowQuerier { 23 | @Override 24 | public TaskFlow getTaskFlow(Long taskFlowId) throws TaskFlowQueryException { 25 | List taskFlowList = this.listTaskFlow(); 26 | return taskFlowList.stream().filter(taskFlow -> { 27 | return taskFlow.getId().equals(taskFlowId); 28 | }).findFirst().get(); 29 | } 30 | 31 | @Override 32 | public List listCronTaskFlow() throws TaskFlowQueryException { 33 | List taskFlowList = this.listTaskFlow(); 34 | return taskFlowList.stream().filter(taskFlow -> { 35 | return (taskFlow.getCron() != null && !taskFlow.getCron().trim().isEmpty()); 36 | }).collect(Collectors.toList()); 37 | } 38 | 39 | private List listTaskFlow() { 40 | /** 41 | * 示例拓扑图: 42 | * --> 9 43 | * 1 --> 2 --> 4 | ^ 44 | * \ \ ^ | | 45 | * \ v / / --> 8 46 | * --> 3 --> 5 --> 6 47 | * \ | 48 | * \ v 49 | * --> 7 50 | * 10 --> 11 51 | */ 52 | 53 | List taskFlowList = new ArrayList<>(); 54 | 55 | TaskFlow taskFlow = new TaskFlow(); 56 | taskFlow.setId(100L); 57 | taskFlow.setTaskList(new ArrayList<>()); 58 | taskFlow.setLinkList(new ArrayList<>()); 59 | 60 | MyTask myTask1 = new MyTask(); 61 | myTask1.setId(1L); 62 | MyTask myTask2 = new MyTask(); 63 | myTask2.setId(2L); 64 | MyTask myTask3 = new MyTask(); 65 | myTask3.setId(3L); 66 | MyTask myTask4 = new MyTask(); 67 | myTask4.setId(4L); 68 | MyTask myTask5 = new MyTask(); 69 | myTask5.setId(5L); 70 | MyTask myTask6 = new MyTask(); 71 | myTask6.setId(6L); 72 | MyTask myTask7 = new MyTask(); 73 | myTask7.setId(7L); 74 | MyTask myTask8 = new MyTask(); 75 | myTask8.setId(8L); 76 | MyTask myTask9 = new MyTask(); 77 | myTask9.setId(9L); 78 | MyTask myTask10 = new MyTask(); 79 | myTask10.setId(10L); 80 | MyTask myTask11 = new MyTask(); 81 | myTask11.setId(11L); 82 | taskFlow.getTaskList().add(myTask3); 83 | taskFlow.getTaskList().add(myTask5); 84 | taskFlow.getTaskList().add(myTask1); 85 | taskFlow.getTaskList().add(myTask10); 86 | taskFlow.getTaskList().add(myTask9); 87 | taskFlow.getTaskList().add(myTask6); 88 | taskFlow.getTaskList().add(myTask4); 89 | taskFlow.getTaskList().add(myTask2); 90 | taskFlow.getTaskList().add(myTask7); 91 | taskFlow.getTaskList().add(myTask11); 92 | taskFlow.getTaskList().add(myTask8); 93 | 94 | taskFlow.getLinkList().add(new Link(1L, 2L)); 95 | taskFlow.getLinkList().add(new Link(1L, 3L)); 96 | taskFlow.getLinkList().add(new Link(2L, 3L)); 97 | taskFlow.getLinkList().add(new Link(2L, 4L)); 98 | taskFlow.getLinkList().add(new Link(3L, 4L)); 99 | taskFlow.getLinkList().add(new Link(3L, 5L)); 100 | taskFlow.getLinkList().add(new Link(5L, 6L)); 101 | taskFlow.getLinkList().add(new Link(5L, 7L)); 102 | taskFlow.getLinkList().add(new Link(6L, 7L)); 103 | taskFlow.getLinkList().add(new Link(5L, 8L)); 104 | taskFlow.getLinkList().add(new Link(5L, 9L)); 105 | taskFlow.getLinkList().add(new Link(8L, 9L)); 106 | taskFlow.getLinkList().add(new Link(10L, 11L)); 107 | 108 | //taskFlow.setCron("0 * * * * ?"); 109 | //taskFlow.setFromTaskId(1L); 110 | 111 | taskFlowList.add(taskFlow); 112 | 113 | return taskFlowList; 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /wolf-flow-sample-cluster/src/main/java/me/kpali/wolfflow/sample/cluster/taskflow/MyTaskFlowScheduler.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.sample.cluster.taskflow; 2 | 3 | import me.kpali.wolfflow.core.scheduler.impl.DefaultTaskFlowScheduler; 4 | import org.springframework.context.annotation.Primary; 5 | import org.springframework.stereotype.Component; 6 | 7 | /** 8 | * 可以覆写默认任务流调度器的方法实现自定义,但一般情况下直接使用默认任务流调度器即可 9 | * (可选) 10 | * 11 | * @author kpali 12 | */ 13 | @Primary 14 | @Component 15 | public class MyTaskFlowScheduler extends DefaultTaskFlowScheduler { 16 | } 17 | -------------------------------------------------------------------------------- /wolf-flow-sample-cluster/src/main/java/me/kpali/wolfflow/sample/cluster/taskflow/MyTaskLogger.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.sample.cluster.taskflow; 2 | 3 | import me.kpali.wolfflow.core.logger.impl.DefaultTaskLogger; 4 | import org.springframework.context.annotation.Primary; 5 | import org.springframework.stereotype.Component; 6 | 7 | /** 8 | * 可以覆写默认任务日志器的方法实现自定义,推荐使用数据库DAO实现日志持久化到硬盘。本示例为简单起见仍然使用默认实现存储到内存 9 | * (可选) 10 | * 11 | * @author kpali 12 | */ 13 | @Primary 14 | @Component 15 | public class MyTaskLogger extends DefaultTaskLogger { 16 | } 17 | -------------------------------------------------------------------------------- /wolf-flow-sample-cluster/src/main/java/me/kpali/wolfflow/sample/cluster/util/SpringContextUtil.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.sample.cluster.util; 2 | 3 | import org.springframework.beans.BeansException; 4 | import org.springframework.context.ApplicationContext; 5 | import org.springframework.context.ApplicationContextAware; 6 | import org.springframework.stereotype.Component; 7 | 8 | @Component 9 | public class SpringContextUtil implements ApplicationContextAware { 10 | private static ApplicationContext applicationContext = null; 11 | 12 | @Override 13 | public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { 14 | SpringContextUtil.applicationContext = applicationContext; 15 | } 16 | 17 | public static ApplicationContext getApplicationContext() { 18 | return applicationContext; 19 | } 20 | 21 | public static Object getBean(String beanId) { 22 | return applicationContext.getBean(beanId); 23 | } 24 | 25 | public static T getBean(Class requiredType) { 26 | return applicationContext.getBean(requiredType); 27 | } 28 | } 29 | 30 | -------------------------------------------------------------------------------- /wolf-flow-sample-cluster/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.application.name=wolf-flow-sample-cluster -------------------------------------------------------------------------------- /wolf-flow-sample-cluster/src/main/resources/quartz.properties: -------------------------------------------------------------------------------- 1 | org.quartz.scheduler.instanceName = MyScheduler 2 | org.quartz.threadPool.threadCount = 10 3 | org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore 4 | org.quartz.jobStore.misfireThreshold = 60000 -------------------------------------------------------------------------------- /wolf-flow-sample-cluster/src/main/resources/redisson.yaml: -------------------------------------------------------------------------------- 1 | singleServerConfig: 2 | address: "redis://192.168.232.171:6379" -------------------------------------------------------------------------------- /wolf-flow-sample-cluster/src/test/java/me/kpali/wolfflow/sample/cluster/WolfFlowSampleClusterApplicationTests.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.sample.cluster; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import me.kpali.wolfflow.core.exception.TaskFlowLogException; 5 | import me.kpali.wolfflow.core.exception.TaskLogException; 6 | import me.kpali.wolfflow.core.logger.ITaskFlowLogger; 7 | import me.kpali.wolfflow.core.logger.ITaskLogger; 8 | import me.kpali.wolfflow.core.model.TaskFlowLog; 9 | import me.kpali.wolfflow.core.model.TaskLog; 10 | import me.kpali.wolfflow.core.model.TaskLogResult; 11 | import me.kpali.wolfflow.core.scheduler.ITaskFlowScheduler; 12 | import org.junit.jupiter.api.Test; 13 | import org.slf4j.Logger; 14 | import org.slf4j.LoggerFactory; 15 | import org.springframework.beans.factory.annotation.Autowired; 16 | import org.springframework.boot.test.context.SpringBootTest; 17 | 18 | import java.util.List; 19 | 20 | @SpringBootTest 21 | public class WolfFlowSampleClusterApplicationTests { 22 | private static final Logger logger = LoggerFactory.getLogger(WolfFlowSampleClusterApplicationTests.class); 23 | 24 | @Autowired 25 | ITaskFlowScheduler taskFlowScheduler; 26 | @Autowired 27 | ITaskFlowLogger taskFlowLogger; 28 | @Autowired 29 | ITaskLogger taskLogger; 30 | 31 | @Test 32 | public void taskFlowExecuteTest() { 33 | ObjectMapper objectMapper = new ObjectMapper(); 34 | try { 35 | long taskFlowLogId1 = taskFlowScheduler.executeTo(100L, 5L, null); 36 | logger.info(">>>>>>>>>> Task flow log id: " + taskFlowLogId1); 37 | //Thread.sleep(1000); 38 | //taskFlowScheduler.stop(taskFlowLogId1); 39 | this.waitDoneAndPrintLog(taskFlowLogId1); 40 | List taskStatusList1 = taskLogger.listTaskStatus(100L); 41 | logger.info(">>>>>>>>>> Finished, status of tasks: "); 42 | logger.info(objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(taskStatusList1)); 43 | 44 | long taskFlowLogId2 = taskFlowScheduler.executeTo(100L, 6L, null); 45 | logger.info(">>>>>>>>>> Task flow log id: " + taskFlowLogId2); 46 | this.waitDoneAndPrintLog(taskFlowLogId2); 47 | List taskStatusList2 = taskLogger.listTaskStatus(100L); 48 | logger.info(">>>>>>>>>> Finished, status of tasks: "); 49 | logger.info(objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(taskStatusList2)); 50 | } catch (Exception e) { 51 | e.printStackTrace(); 52 | } 53 | } 54 | 55 | private void waitDoneAndPrintLog(long taskFLowLogId) throws TaskFlowLogException, TaskLogException { 56 | while (true) { 57 | TaskFlowLog taskFlowLog = taskFlowLogger.get(taskFLowLogId); 58 | if (!taskFlowLogger.isInProgress(taskFlowLog)) { 59 | List taskLogList = taskLogger.list(taskFLowLogId); 60 | for (TaskLog taskLog : taskLogList) { 61 | TaskLogResult taskLogResult = taskLogger.query(taskLog.getLogFileId(), 1); 62 | if (taskLogResult != null) { 63 | logger.info(">>>>>>>>>> Task [" + taskLog.getTaskId() + "] log contents: "); 64 | logger.info(taskLogResult.getLogContent()); 65 | } 66 | } 67 | break; 68 | } 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /wolf-flow-sample/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 2.2.4.RELEASE 9 | 10 | 11 | me.kpali 12 | wolf-flow-sample 13 | 1.0.0 14 | wolf-flow-sample ${project.version} 15 | Wolf flow Sample 16 | 17 | 18 | 19 | org.springframework.boot 20 | spring-boot-starter 21 | 22 | 23 | org.springframework.boot 24 | spring-boot-starter-web 25 | 26 | 27 | 28 | org.springframework.boot 29 | spring-boot-devtools 30 | runtime 31 | true 32 | 33 | 34 | org.springframework.boot 35 | spring-boot-starter-test 36 | test 37 | 38 | 39 | org.junit.vintage 40 | junit-vintage-engine 41 | 42 | 43 | 44 | 45 | 46 | me.kpali 47 | wolf-flow-spring-boot-starter 48 | 2.1.1 49 | 50 | 51 | 52 | 53 | 54 | 55 | org.springframework.boot 56 | spring-boot-maven-plugin 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /wolf-flow-sample/src/main/java/me/kpali/wolfflow/sample/WolfFlowSampleApplication.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.sample; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class WolfFlowSampleApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(WolfFlowSampleApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /wolf-flow-sample/src/main/java/me/kpali/wolfflow/sample/controller/MetricsController.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.sample.controller; 2 | 3 | import me.kpali.wolfflow.core.monitor.IMonitor; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.web.bind.annotation.GetMapping; 6 | import org.springframework.web.bind.annotation.RestController; 7 | 8 | @RestController 9 | public class MetricsController { 10 | @Autowired 11 | IMonitor monitor; 12 | 13 | @GetMapping(value = "/prometheus", produces = {"text/plain; version=0.0.4; charset=utf-8"}) 14 | public String getMetrics() { 15 | String response = monitor.scrape(); 16 | return response; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /wolf-flow-sample/src/main/java/me/kpali/wolfflow/sample/controller/TriggerController.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.sample.controller; 2 | 3 | import me.kpali.wolfflow.core.exception.TaskFlowTriggerException; 4 | import me.kpali.wolfflow.core.scheduler.ITaskFlowScheduler; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.web.bind.annotation.GetMapping; 7 | import org.springframework.web.bind.annotation.PathVariable; 8 | import org.springframework.web.bind.annotation.RestController; 9 | 10 | @RestController 11 | public class TriggerController { 12 | @Autowired 13 | ITaskFlowScheduler taskFlowScheduler; 14 | 15 | @GetMapping(value = "/trigger/{taskFlowId}") 16 | public void trigger(@PathVariable Long taskFlowId) throws TaskFlowTriggerException { 17 | taskFlowScheduler.execute(taskFlowId, null); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /wolf-flow-sample/src/main/java/me/kpali/wolfflow/sample/listener/ApplicationReadyEventListener.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.sample.listener; 2 | 3 | import me.kpali.wolfflow.core.launcher.Launcher; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.boot.context.event.ApplicationReadyEvent; 6 | import org.springframework.context.ApplicationListener; 7 | import org.springframework.stereotype.Component; 8 | 9 | /** 10 | * 程序启动完成事件监听,在程序启动后启动任务流相关的后台线程 11 | * (必要) 12 | * 13 | * @author kpali 14 | */ 15 | @Component 16 | public class ApplicationReadyEventListener implements ApplicationListener { 17 | @Autowired 18 | private Launcher launcher; 19 | 20 | @Override 21 | public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) { 22 | this.launcher.startup(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /wolf-flow-sample/src/main/java/me/kpali/wolfflow/sample/listener/TaskFlowEventListener.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.sample.listener; 2 | 3 | import me.kpali.wolfflow.core.event.ScheduleStatusChangeEvent; 4 | import me.kpali.wolfflow.core.event.TaskFlowStatusChangeEvent; 5 | import me.kpali.wolfflow.core.event.TaskStatusChangeEvent; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | import org.springframework.context.event.EventListener; 9 | import org.springframework.stereotype.Component; 10 | 11 | /** 12 | * 任务流事件监听 13 | * (可选) 14 | * 15 | * @author kpali 16 | */ 17 | @Component 18 | public class TaskFlowEventListener { 19 | private static final Logger logger = LoggerFactory.getLogger(TaskFlowEventListener.class); 20 | 21 | @EventListener 22 | public void taskFlowScheduleStatusChange(ScheduleStatusChangeEvent event) { 23 | // 任务流调度状态变更监听,主要为定时任务流,状态有[加入调度、更新调度、调度失败]等 24 | } 25 | 26 | @EventListener 27 | public void taskFlowStatusChange(TaskFlowStatusChangeEvent event) { 28 | // 任务流状态变更监听,状态有[等待执行、执行中、执行成功、执行失败、停止中]等 29 | logger.info(">>>>>>>>>> Task flow [{}] status changed to: {}", event.getTaskFlowStatus().getTaskFlow().getId(), event.getTaskFlowStatus().getStatus()); 30 | } 31 | 32 | @EventListener 33 | public void taskStatusChange(TaskStatusChangeEvent event) { 34 | // 任务状态变更监听,状态有[等待执行、执行中、执行成功、执行失败、停止中、跳过]等 35 | logger.info(">>>>>>>>>> Task [{}] status changed to: {}", event.getTaskStatus().getTask().getId(), event.getTaskStatus().getStatus()); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /wolf-flow-sample/src/main/java/me/kpali/wolfflow/sample/taskflow/MyTask.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.sample.taskflow; 2 | 3 | import me.kpali.wolfflow.core.exception.TaskExecuteException; 4 | import me.kpali.wolfflow.core.exception.TaskInterruptedException; 5 | import me.kpali.wolfflow.core.exception.TaskStopException; 6 | import me.kpali.wolfflow.core.logger.ITaskLogger; 7 | import me.kpali.wolfflow.core.model.Task; 8 | import me.kpali.wolfflow.core.model.TaskContextKey; 9 | import me.kpali.wolfflow.core.util.context.TaskContextWrapper; 10 | import me.kpali.wolfflow.core.util.context.TaskFlowContextWrapper; 11 | import me.kpali.wolfflow.sample.util.SpringContextUtil; 12 | 13 | import java.util.concurrent.ConcurrentHashMap; 14 | 15 | /** 16 | * 自定义任务,覆写父类的方法,实现自定义任务的执行内容 17 | * (必要) 18 | * 19 | * @author kpali 20 | */ 21 | public class MyTask extends Task { 22 | private boolean requiredToStop = false; 23 | 24 | @Override 25 | public void execute(ConcurrentHashMap context) throws TaskExecuteException, TaskInterruptedException { 26 | try { 27 | ITaskLogger taskLogger = SpringContextUtil.getBean(ITaskLogger.class); 28 | TaskFlowContextWrapper taskFlowContextWrapper = new TaskFlowContextWrapper(context); 29 | ConcurrentHashMap taskContext = taskFlowContextWrapper.getTaskContext(this.getId().toString()); 30 | TaskContextWrapper taskContextWrapper = new TaskContextWrapper(taskContext); 31 | Long taskLogId = taskContextWrapper.getValue(TaskContextKey.TASK_LOG_ID, Long.class); 32 | String taskLogFileId = taskContextWrapper.getValue(TaskContextKey.TASK_LOG_FILE_ID, String.class); 33 | taskLogger.log(taskLogFileId, "Task executing...", false); 34 | taskLogger.log(taskLogFileId, "Second line...\rThird line...\nFourth line...\r\nFifth line...", false); 35 | if (requiredToStop) { 36 | taskLogger.log(taskLogFileId, "Task execution is terminated", true); 37 | throw new TaskInterruptedException("Task execution is terminated"); 38 | } 39 | taskLogger.log(taskLogFileId, "Task execution finished", true); 40 | } catch (TaskInterruptedException e) { 41 | throw e; 42 | } catch (Exception e) { 43 | throw new TaskExecuteException(e); 44 | } 45 | } 46 | 47 | @Override 48 | public void stop(ConcurrentHashMap context) throws TaskStopException { 49 | this.requiredToStop = true; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /wolf-flow-sample/src/main/java/me/kpali/wolfflow/sample/taskflow/MyTaskFlowQuerier.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.sample.taskflow; 2 | 3 | import me.kpali.wolfflow.core.exception.TaskFlowQueryException; 4 | import me.kpali.wolfflow.core.model.Link; 5 | import me.kpali.wolfflow.core.model.TaskFlow; 6 | import me.kpali.wolfflow.core.querier.impl.DefaultTaskFlowQuerier; 7 | import org.springframework.context.annotation.Primary; 8 | import org.springframework.stereotype.Component; 9 | 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | import java.util.stream.Collectors; 13 | 14 | /** 15 | * 自定义任务流查询器,覆写父类的方法,实现自定义任务流查询器 16 | * (必要) 17 | * 18 | * @author kpali 19 | */ 20 | @Primary 21 | @Component 22 | public class MyTaskFlowQuerier extends DefaultTaskFlowQuerier { 23 | @Override 24 | public TaskFlow getTaskFlow(Long taskFlowId) throws TaskFlowQueryException { 25 | List taskFlowList = this.listTaskFlow(); 26 | return taskFlowList.stream().filter(taskFlow -> { 27 | return taskFlow.getId().equals(taskFlowId); 28 | }).findFirst().get(); 29 | } 30 | 31 | @Override 32 | public List listCronTaskFlow() throws TaskFlowQueryException { 33 | List taskFlowList = this.listTaskFlow(); 34 | return taskFlowList.stream().filter(taskFlow -> { 35 | return (taskFlow.getCron() != null && !taskFlow.getCron().trim().isEmpty()); 36 | }).collect(Collectors.toList()); 37 | } 38 | 39 | private List listTaskFlow() { 40 | /** 41 | * 示例拓扑图: 42 | * --> 9 43 | * 1 --> 2 --> 4 | ^ 44 | * \ \ ^ | | 45 | * \ v / / --> 8 46 | * --> 3 --> 5 --> 6 47 | * \ | 48 | * \ v 49 | * --> 7 50 | * 10 --> 11 51 | */ 52 | 53 | List taskFlowList = new ArrayList<>(); 54 | 55 | TaskFlow taskFlow = new TaskFlow(); 56 | taskFlow.setId(100L); 57 | taskFlow.setTaskList(new ArrayList<>()); 58 | taskFlow.setLinkList(new ArrayList<>()); 59 | 60 | MyTask myTask1 = new MyTask(); 61 | myTask1.setId(1L); 62 | MyTask myTask2 = new MyTask(); 63 | myTask2.setId(2L); 64 | MyTask myTask3 = new MyTask(); 65 | myTask3.setId(3L); 66 | MyTask myTask4 = new MyTask(); 67 | myTask4.setId(4L); 68 | MyTask myTask5 = new MyTask(); 69 | myTask5.setId(5L); 70 | MyTask myTask6 = new MyTask(); 71 | myTask6.setId(6L); 72 | MyTask myTask7 = new MyTask(); 73 | myTask7.setId(7L); 74 | MyTask myTask8 = new MyTask(); 75 | myTask8.setId(8L); 76 | MyTask myTask9 = new MyTask(); 77 | myTask9.setId(9L); 78 | MyTask myTask10 = new MyTask(); 79 | myTask10.setId(10L); 80 | MyTask myTask11 = new MyTask(); 81 | myTask11.setId(11L); 82 | taskFlow.getTaskList().add(myTask3); 83 | taskFlow.getTaskList().add(myTask5); 84 | taskFlow.getTaskList().add(myTask1); 85 | taskFlow.getTaskList().add(myTask10); 86 | taskFlow.getTaskList().add(myTask9); 87 | taskFlow.getTaskList().add(myTask6); 88 | taskFlow.getTaskList().add(myTask4); 89 | taskFlow.getTaskList().add(myTask2); 90 | taskFlow.getTaskList().add(myTask7); 91 | taskFlow.getTaskList().add(myTask11); 92 | taskFlow.getTaskList().add(myTask8); 93 | 94 | taskFlow.getLinkList().add(new Link(1L, 2L)); 95 | taskFlow.getLinkList().add(new Link(1L, 3L)); 96 | taskFlow.getLinkList().add(new Link(2L, 3L)); 97 | taskFlow.getLinkList().add(new Link(2L, 4L)); 98 | taskFlow.getLinkList().add(new Link(3L, 4L)); 99 | taskFlow.getLinkList().add(new Link(3L, 5L)); 100 | taskFlow.getLinkList().add(new Link(5L, 6L)); 101 | taskFlow.getLinkList().add(new Link(5L, 7L)); 102 | taskFlow.getLinkList().add(new Link(6L, 7L)); 103 | taskFlow.getLinkList().add(new Link(5L, 8L)); 104 | taskFlow.getLinkList().add(new Link(5L, 9L)); 105 | taskFlow.getLinkList().add(new Link(8L, 9L)); 106 | taskFlow.getLinkList().add(new Link(10L, 11L)); 107 | 108 | //taskFlow.setCron("0 * * * * ?"); 109 | //taskFlow.setFromTaskId(1L); 110 | 111 | taskFlowList.add(taskFlow); 112 | 113 | return taskFlowList; 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /wolf-flow-sample/src/main/java/me/kpali/wolfflow/sample/util/SpringContextUtil.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.sample.util; 2 | 3 | import org.springframework.beans.BeansException; 4 | import org.springframework.context.ApplicationContext; 5 | import org.springframework.context.ApplicationContextAware; 6 | import org.springframework.stereotype.Component; 7 | 8 | @Component 9 | public class SpringContextUtil implements ApplicationContextAware { 10 | private static ApplicationContext applicationContext = null; 11 | 12 | @Override 13 | public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { 14 | SpringContextUtil.applicationContext = applicationContext; 15 | } 16 | 17 | public static ApplicationContext getApplicationContext() { 18 | return applicationContext; 19 | } 20 | 21 | public static Object getBean(String beanId) { 22 | return applicationContext.getBean(beanId); 23 | } 24 | 25 | public static T getBean(Class requiredType) { 26 | return applicationContext.getBean(requiredType); 27 | } 28 | } 29 | 30 | -------------------------------------------------------------------------------- /wolf-flow-sample/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.application.name=wolf-flow-sample -------------------------------------------------------------------------------- /wolf-flow-sample/src/main/resources/quartz.properties: -------------------------------------------------------------------------------- 1 | org.quartz.scheduler.instanceName = MyScheduler 2 | org.quartz.threadPool.threadCount = 10 3 | org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore 4 | org.quartz.jobStore.misfireThreshold = 60000 -------------------------------------------------------------------------------- /wolf-flow-sample/src/test/java/me/kpali/wolfflow/sample/WolfFlowSampleApplicationTests.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.sample; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import me.kpali.wolfflow.core.exception.TaskFlowLogException; 5 | import me.kpali.wolfflow.core.exception.TaskLogException; 6 | import me.kpali.wolfflow.core.logger.ITaskFlowLogger; 7 | import me.kpali.wolfflow.core.logger.ITaskLogger; 8 | import me.kpali.wolfflow.core.model.TaskFlowLog; 9 | import me.kpali.wolfflow.core.model.TaskLog; 10 | import me.kpali.wolfflow.core.model.TaskLogResult; 11 | import me.kpali.wolfflow.core.scheduler.ITaskFlowScheduler; 12 | import org.junit.jupiter.api.Test; 13 | import org.slf4j.Logger; 14 | import org.slf4j.LoggerFactory; 15 | import org.springframework.beans.factory.annotation.Autowired; 16 | import org.springframework.boot.test.context.SpringBootTest; 17 | 18 | import java.util.List; 19 | 20 | @SpringBootTest 21 | public class WolfFlowSampleApplicationTests { 22 | private static final Logger logger = LoggerFactory.getLogger(WolfFlowSampleApplicationTests.class); 23 | 24 | @Autowired 25 | ITaskFlowScheduler taskFlowScheduler; 26 | @Autowired 27 | ITaskFlowLogger taskFlowLogger; 28 | @Autowired 29 | ITaskLogger taskLogger; 30 | 31 | @Test 32 | public void taskFlowExecuteTest() { 33 | ObjectMapper objectMapper = new ObjectMapper(); 34 | try { 35 | long taskFlowLogId1 = taskFlowScheduler.execute(100L, 10L, null); 36 | logger.info(">>>>>>>>>> Task flow log id: " + taskFlowLogId1); 37 | //Thread.sleep(1000); 38 | //taskFlowScheduler.stop(taskFlowLogId1); 39 | this.waitDoneAndPrintLog(taskFlowLogId1); 40 | List taskStatusList1 = taskLogger.listTaskStatus(100L); 41 | logger.info(">>>>>>>>>> Finished, status of tasks: "); 42 | logger.info(objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(taskStatusList1)); 43 | 44 | long taskFlowLogId2 = taskFlowScheduler.executeFrom(100L, 11L, null); 45 | logger.info(">>>>>>>>>> Task flow log id: " + taskFlowLogId2); 46 | this.waitDoneAndPrintLog(taskFlowLogId2); 47 | List taskStatusList2 = taskLogger.listTaskStatus(100L); 48 | logger.info(">>>>>>>>>> Finished, status of tasks: "); 49 | logger.info(objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(taskStatusList2)); 50 | } catch (Exception e) { 51 | e.printStackTrace(); 52 | } 53 | } 54 | 55 | private void waitDoneAndPrintLog(long taskFlowLogId) throws TaskFlowLogException, TaskLogException { 56 | while (true) { 57 | TaskFlowLog taskFlowLog = taskFlowLogger.get(taskFlowLogId); 58 | if (!taskFlowLogger.isInProgress(taskFlowLog)) { 59 | List taskLogList = taskLogger.list(taskFlowLogId); 60 | for (TaskLog taskLog : taskLogList) { 61 | TaskLogResult taskLogResult = taskLogger.query(taskLog.getLogFileId(), 1); 62 | if (taskLogResult != null) { 63 | logger.info(">>>>>>>>>> Task [" + taskLog.getTaskId() + "] log contents: "); 64 | logger.info(taskLogResult.getLogContent()); 65 | } 66 | } 67 | break; 68 | } 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /wolf-flow-spring-boot-starter/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | me.kpali 7 | wolf-flow 8 | 2.1.1 9 | 10 | wolf-flow-spring-boot-starter 11 | wolf-flow-spring-boot-starter ${project.version} 12 | 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter 17 | provided 18 | 19 | 20 | 21 | me.kpali 22 | wolf-flow-core 23 | ${project.version} 24 | 25 | 26 | 27 | org.springframework.boot 28 | spring-boot-autoconfigure 29 | provided 30 | 31 | 32 | org.springframework.boot 33 | spring-boot-configuration-processor 34 | true 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /wolf-flow-spring-boot-starter/src/main/java/me/kpali/wolfflow/autoconfigure/WolfFlowAutoConfiguration.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.autoconfigure; 2 | 3 | import me.kpali.wolfflow.autoconfigure.config.*; 4 | import me.kpali.wolfflow.autoconfigure.properties.ClusterProperties; 5 | import me.kpali.wolfflow.autoconfigure.properties.ExecutorProperties; 6 | import me.kpali.wolfflow.autoconfigure.properties.SchedulerProperties; 7 | import org.springframework.boot.context.properties.EnableConfigurationProperties; 8 | import org.springframework.context.annotation.Configuration; 9 | import org.springframework.context.annotation.Import; 10 | 11 | /** 12 | * 自动配置 13 | * 14 | * @author kpali 15 | */ 16 | @EnableConfigurationProperties({SchedulerProperties.class, 17 | ExecutorProperties.class, 18 | ClusterProperties.class}) 19 | @Import({UtilConfiguration.class, 20 | EventPublisherConfiguration.class, 21 | SchedulerConfiguration.class, 22 | ExecutorConfiguration.class, 23 | QuerierConfiguration.class, 24 | LoggerConfiguration.class, 25 | ClusterConfiguration.class, 26 | MonitorConfiguration.class, 27 | LauncherConfiguration.class}) 28 | @Configuration 29 | public class WolfFlowAutoConfiguration { 30 | } 31 | -------------------------------------------------------------------------------- /wolf-flow-spring-boot-starter/src/main/java/me/kpali/wolfflow/autoconfigure/config/ClusterConfiguration.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.autoconfigure.config; 2 | 3 | import me.kpali.wolfflow.autoconfigure.properties.ClusterProperties; 4 | import me.kpali.wolfflow.core.config.ClusterConfig; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.ComponentScan; 7 | 8 | /** 9 | * 集群控制器配置 10 | * 11 | * @author kpali 12 | */ 13 | @ComponentScan(basePackages = {"me.kpali.wolfflow.core.cluster"}) 14 | public class ClusterConfiguration { 15 | @Bean 16 | public ClusterConfig getClusterConfig(ClusterProperties clusterProperties) { 17 | ClusterConfig clusterConfig = new ClusterConfig(); 18 | clusterConfig.setNodeHeartbeatInterval(clusterProperties.getNodeHeartbeatInterval()); 19 | clusterConfig.setNodeHeartbeatDuration(clusterProperties.getNodeHeartbeatDuration()); 20 | clusterConfig.setGenerateNodeIdLockLeaseTime(clusterProperties.getGenerateNodeIdLockLeaseTime()); 21 | clusterConfig.setTaskFlowLogLockWaitTime(clusterProperties.getTaskFlowLogLockWaitTime()); 22 | clusterConfig.setTaskFlowLogLockLeaseTime(clusterProperties.getTaskFlowLogLockLeaseTime()); 23 | clusterConfig.setTaskLogLockWaitTime(clusterProperties.getTaskLogLockWaitTime()); 24 | clusterConfig.setTaskLogLockLeaseTime(clusterProperties.getTaskLogLockLeaseTime()); 25 | return clusterConfig; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /wolf-flow-spring-boot-starter/src/main/java/me/kpali/wolfflow/autoconfigure/config/EventPublisherConfiguration.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.autoconfigure.config; 2 | 3 | import org.springframework.context.annotation.ComponentScan; 4 | 5 | /** 6 | * 事件发布器配置 7 | * 8 | * @author kpali 9 | */ 10 | @ComponentScan(basePackages = {"me.kpali.wolfflow.core.event"}) 11 | public class EventPublisherConfiguration { 12 | } 13 | -------------------------------------------------------------------------------- /wolf-flow-spring-boot-starter/src/main/java/me/kpali/wolfflow/autoconfigure/config/ExecutorConfiguration.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.autoconfigure.config; 2 | 3 | import me.kpali.wolfflow.autoconfigure.properties.ExecutorProperties; 4 | import me.kpali.wolfflow.core.config.ExecutorConfig; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.ComponentScan; 7 | 8 | /** 9 | * 执行器配置 10 | * 11 | * @author kpali 12 | */ 13 | @ComponentScan(basePackages = {"me.kpali.wolfflow.core.executor"}) 14 | public class ExecutorConfiguration { 15 | @Bean 16 | public ExecutorConfig getExecutorConfig(ExecutorProperties executorProperties) { 17 | ExecutorConfig executorConfig = new ExecutorConfig(); 18 | executorConfig.setCorePoolSize(executorProperties.getCorePoolSize()); 19 | executorConfig.setMaximumPoolSize(executorProperties.getMaximumPoolSize()); 20 | return executorConfig; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /wolf-flow-spring-boot-starter/src/main/java/me/kpali/wolfflow/autoconfigure/config/LauncherConfiguration.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.autoconfigure.config; 2 | 3 | import org.springframework.context.annotation.ComponentScan; 4 | 5 | /** 6 | * 启动器配置 7 | * 8 | * @author kpali 9 | */ 10 | @ComponentScan(basePackages = {"me.kpali.wolfflow.core.launcher"}) 11 | public class LauncherConfiguration { 12 | } 13 | -------------------------------------------------------------------------------- /wolf-flow-spring-boot-starter/src/main/java/me/kpali/wolfflow/autoconfigure/config/LoggerConfiguration.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.autoconfigure.config; 2 | 3 | import org.springframework.context.annotation.ComponentScan; 4 | 5 | /** 6 | * 日志器配置 7 | * 8 | * @author kpali 9 | */ 10 | @ComponentScan(basePackages = {"me.kpali.wolfflow.core.logger"}) 11 | public class LoggerConfiguration { 12 | } 13 | -------------------------------------------------------------------------------- /wolf-flow-spring-boot-starter/src/main/java/me/kpali/wolfflow/autoconfigure/config/MonitorConfiguration.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.autoconfigure.config; 2 | 3 | import org.springframework.context.annotation.ComponentScan; 4 | 5 | /** 6 | * 监控配置 7 | * 8 | * @author kpali 9 | */ 10 | @ComponentScan(basePackages = {"me.kpali.wolfflow.core.monitor"}) 11 | public class MonitorConfiguration { 12 | } 13 | -------------------------------------------------------------------------------- /wolf-flow-spring-boot-starter/src/main/java/me/kpali/wolfflow/autoconfigure/config/QuerierConfiguration.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.autoconfigure.config; 2 | 3 | import org.springframework.context.annotation.ComponentScan; 4 | 5 | /** 6 | * 查询器配置 7 | * 8 | * @author kpali 9 | */ 10 | @ComponentScan(basePackages = {"me.kpali.wolfflow.core.querier"}) 11 | public class QuerierConfiguration { 12 | } 13 | -------------------------------------------------------------------------------- /wolf-flow-spring-boot-starter/src/main/java/me/kpali/wolfflow/autoconfigure/config/SchedulerConfiguration.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.autoconfigure.config; 2 | 3 | import me.kpali.wolfflow.autoconfigure.properties.SchedulerProperties; 4 | import me.kpali.wolfflow.core.config.SchedulerConfig; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.ComponentScan; 7 | 8 | /** 9 | * 调度器配置 10 | * 11 | * @author kpali 12 | */ 13 | @ComponentScan(basePackages = {"me.kpali.wolfflow.core.scheduler"}) 14 | public class SchedulerConfiguration { 15 | @Bean 16 | public SchedulerConfig getSchedulerConfig(SchedulerProperties schedulerProperties) { 17 | SchedulerConfig schedulerConfig = new SchedulerConfig(); 18 | schedulerConfig.setExecRequestScanInterval(schedulerProperties.getExecRequestScanInterval()); 19 | schedulerConfig.setCronScanInterval(schedulerProperties.getCronScanInterval()); 20 | schedulerConfig.setCronScanLockWaitTime(schedulerProperties.getCronScanLockWaitTime()); 21 | schedulerConfig.setCronScanLockLeaseTime(schedulerProperties.getCronScanLockLeaseTime()); 22 | schedulerConfig.setCorePoolSize(schedulerProperties.getCorePoolSize()); 23 | schedulerConfig.setMaximumPoolSize(schedulerProperties.getMaximumPoolSize()); 24 | return schedulerConfig; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /wolf-flow-spring-boot-starter/src/main/java/me/kpali/wolfflow/autoconfigure/config/UtilConfiguration.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.autoconfigure.config; 2 | 3 | import org.springframework.context.annotation.ComponentScan; 4 | 5 | /** 6 | * 工具配置 7 | * 8 | * @author kpali 9 | */ 10 | @ComponentScan(basePackages = {"me.kpali.wolfflow.core.util"}) 11 | public class UtilConfiguration { 12 | } 13 | -------------------------------------------------------------------------------- /wolf-flow-spring-boot-starter/src/main/java/me/kpali/wolfflow/autoconfigure/properties/ClusterProperties.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.autoconfigure.properties; 2 | 3 | import org.springframework.boot.context.properties.ConfigurationProperties; 4 | 5 | /** 6 | * 集群控制器配置 7 | * 8 | * @author kpali 9 | */ 10 | @ConfigurationProperties(prefix = "wolf-flow.cluster") 11 | public class ClusterProperties { 12 | private Integer nodeHeartbeatInterval = 30; 13 | private Integer nodeHeartbeatDuration = 90; 14 | private Integer generateNodeIdLockLeaseTime = 60; 15 | private Integer taskFlowLogLockWaitTime = 10; 16 | private Integer taskFlowLogLockLeaseTime = 15; 17 | private Integer taskLogLockWaitTime = 10; 18 | private Integer taskLogLockLeaseTime = 15; 19 | 20 | public Integer getNodeHeartbeatInterval() { 21 | return nodeHeartbeatInterval; 22 | } 23 | 24 | public void setNodeHeartbeatInterval(Integer nodeHeartbeatInterval) { 25 | this.nodeHeartbeatInterval = nodeHeartbeatInterval; 26 | } 27 | 28 | public Integer getNodeHeartbeatDuration() { 29 | return nodeHeartbeatDuration; 30 | } 31 | 32 | public void setNodeHeartbeatDuration(Integer nodeHeartbeatDuration) { 33 | this.nodeHeartbeatDuration = nodeHeartbeatDuration; 34 | } 35 | 36 | public Integer getGenerateNodeIdLockLeaseTime() { 37 | return generateNodeIdLockLeaseTime; 38 | } 39 | 40 | public void setGenerateNodeIdLockLeaseTime(Integer generateNodeIdLockLeaseTime) { 41 | this.generateNodeIdLockLeaseTime = generateNodeIdLockLeaseTime; 42 | } 43 | 44 | public Integer getTaskFlowLogLockWaitTime() { 45 | return taskFlowLogLockWaitTime; 46 | } 47 | 48 | public void setTaskFlowLogLockWaitTime(Integer taskFlowLogLockWaitTime) { 49 | this.taskFlowLogLockWaitTime = taskFlowLogLockWaitTime; 50 | } 51 | 52 | public Integer getTaskFlowLogLockLeaseTime() { 53 | return taskFlowLogLockLeaseTime; 54 | } 55 | 56 | public void setTaskFlowLogLockLeaseTime(Integer taskFlowLogLockLeaseTime) { 57 | this.taskFlowLogLockLeaseTime = taskFlowLogLockLeaseTime; 58 | } 59 | 60 | public Integer getTaskLogLockWaitTime() { 61 | return taskLogLockWaitTime; 62 | } 63 | 64 | public void setTaskLogLockWaitTime(Integer taskLogLockWaitTime) { 65 | this.taskLogLockWaitTime = taskLogLockWaitTime; 66 | } 67 | 68 | public Integer getTaskLogLockLeaseTime() { 69 | return taskLogLockLeaseTime; 70 | } 71 | 72 | public void setTaskLogLockLeaseTime(Integer taskLogLockLeaseTime) { 73 | this.taskLogLockLeaseTime = taskLogLockLeaseTime; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /wolf-flow-spring-boot-starter/src/main/java/me/kpali/wolfflow/autoconfigure/properties/ExecutorProperties.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.autoconfigure.properties; 2 | 3 | import org.springframework.boot.context.properties.ConfigurationProperties; 4 | 5 | /** 6 | * 执行器配置 7 | * 8 | * @author kpali 9 | */ 10 | @ConfigurationProperties(prefix = "wolf-flow.executor") 11 | public class ExecutorProperties { 12 | private Integer corePoolSize = 30; 13 | private Integer maximumPoolSize = 30; 14 | 15 | public Integer getCorePoolSize() { 16 | return corePoolSize; 17 | } 18 | 19 | public void setCorePoolSize(Integer corePoolSize) { 20 | this.corePoolSize = corePoolSize; 21 | } 22 | 23 | public Integer getMaximumPoolSize() { 24 | return maximumPoolSize; 25 | } 26 | 27 | public void setMaximumPoolSize(Integer maximumPoolSize) { 28 | this.maximumPoolSize = maximumPoolSize; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /wolf-flow-spring-boot-starter/src/main/java/me/kpali/wolfflow/autoconfigure/properties/SchedulerProperties.java: -------------------------------------------------------------------------------- 1 | package me.kpali.wolfflow.autoconfigure.properties; 2 | 3 | import org.springframework.boot.context.properties.ConfigurationProperties; 4 | 5 | /** 6 | * 调度器配置 7 | * 8 | * @author kpali 9 | */ 10 | @ConfigurationProperties(prefix = "wolf-flow.scheduler") 11 | public class SchedulerProperties { 12 | private Integer execRequestScanInterval = 1; 13 | private Integer cronScanInterval = 10; 14 | private Integer cronScanLockWaitTime = 10; 15 | private Integer cronScanLockLeaseTime = 60; 16 | private Integer corePoolSize = 10; 17 | private Integer maximumPoolSize = 10; 18 | 19 | public Integer getExecRequestScanInterval() { 20 | return execRequestScanInterval; 21 | } 22 | 23 | public void setExecRequestScanInterval(Integer execRequestScanInterval) { 24 | this.execRequestScanInterval = execRequestScanInterval; 25 | } 26 | 27 | public Integer getCronScanInterval() { 28 | return cronScanInterval; 29 | } 30 | 31 | public void setCronScanInterval(Integer cronScanInterval) { 32 | this.cronScanInterval = cronScanInterval; 33 | } 34 | 35 | public Integer getCronScanLockWaitTime() { 36 | return cronScanLockWaitTime; 37 | } 38 | 39 | public void setCronScanLockWaitTime(Integer cronScanLockWaitTime) { 40 | this.cronScanLockWaitTime = cronScanLockWaitTime; 41 | } 42 | 43 | public Integer getCronScanLockLeaseTime() { 44 | return cronScanLockLeaseTime; 45 | } 46 | 47 | public void setCronScanLockLeaseTime(Integer cronScanLockLeaseTime) { 48 | this.cronScanLockLeaseTime = cronScanLockLeaseTime; 49 | } 50 | 51 | public Integer getCorePoolSize() { 52 | return corePoolSize; 53 | } 54 | 55 | public void setCorePoolSize(Integer corePoolSize) { 56 | this.corePoolSize = corePoolSize; 57 | } 58 | 59 | public Integer getMaximumPoolSize() { 60 | return maximumPoolSize; 61 | } 62 | 63 | public void setMaximumPoolSize(Integer maximumPoolSize) { 64 | this.maximumPoolSize = maximumPoolSize; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /wolf-flow-spring-boot-starter/src/main/resources/META-INF/spring.factories: -------------------------------------------------------------------------------- 1 | org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ 2 | me.kpali.wolfflow.autoconfigure.WolfFlowAutoConfiguration --------------------------------------------------------------------------------